1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
5  * This Source Code Form is subject to the terms of the Mozilla Public
6  * License, v. 2.0. If a copy of the MPL was not distributed with this
7  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
11  *   Licensed to the Apache Software Foundation (ASF) under one or more
12  *   contributor license agreements. See the NOTICE file distributed
13  *   with this work for additional information regarding copyright
14  *   ownership. The ASF licenses this file to you under the Apache
15  *   License, Version 2.0 (the "License"); you may not use this file
16  *   except in compliance with the License. You may obtain a copy of
17  *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
18  */
19 
20 #include <spelleng.hxx>
21 #include <com/sun/star/i18n/TextConversionOption.hpp>
22 
23 #include <scitems.hxx>
24 
25 #include <editeng/langitem.hxx>
26 #include <editeng/editobj.hxx>
27 #include <editeng/editview.hxx>
28 #include <sfx2/viewfrm.hxx>
29 #include <vcl/settings.hxx>
30 #include <vcl/svapp.hxx>
31 #include <vcl/weld.hxx>
32 
33 #include <spelldialog.hxx>
34 #include <tabvwsh.hxx>
35 #include <docsh.hxx>
36 #include <cellvalue.hxx>
37 #include <cellform.hxx>
38 #include <patattr.hxx>
39 #include <globstr.hrc>
40 #include <scresid.hxx>
41 #include <markdata.hxx>
42 
43 #include <memory>
44 
45 using namespace ::com::sun::star;
46 
ScConversionEngineBase(SfxItemPool * pEnginePoolP,ScViewData & rViewData,ScDocument * pUndoDoc,ScDocument * pRedoDoc)47 ScConversionEngineBase::ScConversionEngineBase(
48         SfxItemPool* pEnginePoolP, ScViewData& rViewData,
49         ScDocument* pUndoDoc, ScDocument* pRedoDoc ) :
50     ScEditEngineDefaulter( pEnginePoolP ),
51     mrViewData( rViewData ),
52     mrDocShell( *rViewData.GetDocShell() ),
53     mrDoc( rViewData.GetDocShell()->GetDocument() ),
54     maSelState( rViewData ),
55     mpUndoDoc( pUndoDoc ),
56     mpRedoDoc( pRedoDoc ),
57     meCurrLang( LANGUAGE_ENGLISH_US ),
58     mbIsAnyModified( false ),
59     mbInitialState( true ),
60     mbWrappedInTable( false ),
61     mbFinished( false )
62 {
63     maSelState.GetCellCursor().GetVars( mnStartCol, mnStartRow, mnStartTab );
64     // start with cell A1 in cell/range/multi-selection, will seek to first selected
65     if( maSelState.GetSelectionType() == SC_SELECTTYPE_SHEET )
66     {
67         mnStartCol = 0;
68         mnStartRow = 0;
69     }
70     mnCurrCol = mnStartCol;
71     mnCurrRow = mnStartRow;
72 }
73 
~ScConversionEngineBase()74 ScConversionEngineBase::~ScConversionEngineBase()
75 {
76 }
77 
FindNextConversionCell()78 bool ScConversionEngineBase::FindNextConversionCell()
79 {
80     ScMarkData& rMark = mrViewData.GetMarkData();
81     ScTabViewShell* pViewShell = mrViewData.GetViewShell();
82     const ScPatternAttr* pPattern = nullptr;
83     const ScPatternAttr* pLastPattern = nullptr;
84 
85     std::unique_ptr<SfxItemSet> pEditDefaults(new SfxItemSet(GetEmptyItemSet()));
86 
87     if( IsModified() )
88     {
89         mbIsAnyModified = true;
90 
91         OUString aNewStr = GetText();
92 
93         bool bMultiTab = (rMark.GetSelectCount() > 1);
94         OUString aVisibleStr;
95         if( bMultiTab )
96             aVisibleStr = mrDoc.GetString(mnCurrCol, mnCurrRow, mnStartTab);
97 
98         for( SCTAB nTab = 0, nTabCount = mrDoc.GetTableCount(); nTab < nTabCount; ++nTab )
99         {
100             //  always change the cell on the visible tab,
101             //  on the other selected tabs only if they contain the same text
102 
103             if ((nTab == mnStartTab) ||
104                 (bMultiTab && rMark.GetTableSelect(nTab) && mrDoc.GetString(mnCurrCol, mnCurrRow, nTab) == aVisibleStr))
105             {
106                 ScAddress aPos( mnCurrCol, mnCurrRow, nTab );
107                 CellType eCellType = mrDoc.GetCellType( aPos );
108                 bool bEmptyCell = eCellType == CELLTYPE_NONE;
109 
110                 if (mpUndoDoc && !bEmptyCell)
111                     mrDoc.CopyCellToDocument(aPos, aPos, *mpUndoDoc);
112 
113                 if (eCellType == CELLTYPE_EDIT)
114                 {
115                     std::unique_ptr<EditTextObject> pEditObj(CreateTextObject());
116                     mrDoc.SetEditText(aPos, *pEditObj, GetEditTextObjectPool());
117                 }
118                 else
119                     mrDoc.SetString(aPos, aNewStr);
120 
121                 if (mpRedoDoc && !bEmptyCell)
122                     mrDoc.CopyCellToDocument(aPos, aPos, *mpRedoDoc);
123 
124                 mrDocShell.PostPaintCell(aPos);
125             }
126         }
127     }
128 
129     SCCOL nNewCol = mnCurrCol;
130     SCROW nNewRow = mnCurrRow;
131 
132     if( mbInitialState )
133     {
134         /*  On very first call, decrement row to let GetNextSpellingCell() find
135             the first cell of current range. */
136         mbInitialState = false;
137         --nNewRow;
138     }
139 
140     bool bSheetSel = maSelState.GetSelectionType() == SC_SELECTTYPE_SHEET;
141     bool bLoop = true;
142     bool bFound = false;
143     while( bLoop && !bFound )
144     {
145         bLoop = mrDoc.GetNextSpellingCell( nNewCol, nNewRow, mnStartTab, bSheetSel, rMark );
146         if( bLoop )
147         {
148             FillFromCell( mnCurrCol, mnCurrRow, mnStartTab );
149 
150             if( mbWrappedInTable && ((nNewCol > mnStartCol) || ((nNewCol == mnStartCol) && (nNewRow >= mnStartRow))) )
151             {
152                 ShowFinishDialog();
153                 bLoop = false;
154                 mbFinished = true;
155             }
156             else if( nNewCol > mrDoc.MaxCol() )
157             {
158                 // no more cells in the sheet - try to restart at top of sheet
159 
160                 if( bSheetSel || ((mnStartCol == 0) && (mnStartRow == 0)) )
161                 {
162                     // conversion started at cell A1 or in selection, do not query to restart at top
163                     ShowFinishDialog();
164                     bLoop = false;
165                     mbFinished = true;
166                 }
167                 else if( ShowTableWrapDialog() )
168                 {
169                     // conversion started anywhere but in cell A1, user wants to restart
170                     nNewRow = mrDoc.MaxRow() + 2;
171                     mbWrappedInTable = true;
172                 }
173                 else
174                 {
175                     bLoop = false;
176                     mbFinished = true;
177                 }
178             }
179             else
180             {
181                 pPattern = mrDoc.GetPattern( nNewCol, nNewRow, mnStartTab );
182                 if( pPattern && (pPattern != pLastPattern) )
183                 {
184                     pPattern->FillEditItemSet( pEditDefaults.get() );
185                     SetDefaults( *pEditDefaults );
186                     pLastPattern = pPattern;
187                 }
188 
189                 // language changed?
190                 const SfxPoolItem* pItem = mrDoc.GetAttr( nNewCol, nNewRow, mnStartTab, ATTR_FONT_LANGUAGE );
191                 if( const SvxLanguageItem* pLangItem = dynamic_cast<const SvxLanguageItem*>( pItem )  )
192                 {
193                     LanguageType eLang = pLangItem->GetValue();
194                     if( eLang == LANGUAGE_SYSTEM )
195                         eLang = Application::GetSettings().GetLanguageTag().getLanguageType();   // never use SYSTEM for spelling
196                     if( eLang != meCurrLang )
197                     {
198                         meCurrLang = eLang;
199                         SetDefaultLanguage( eLang );
200                     }
201                 }
202 
203                 FillFromCell( nNewCol, nNewRow, mnStartTab );
204 
205                 bFound = bLoop && NeedsConversion();
206             }
207         }
208     }
209 
210     if( bFound )
211     {
212         pViewShell->AlignToCursor( nNewCol, nNewRow, SC_FOLLOW_JUMP );
213         pViewShell->SetCursor( nNewCol, nNewRow, true );
214         mrViewData.GetView()->MakeEditView( this, nNewCol, nNewRow );
215         EditView* pEditView = mrViewData.GetSpellingView();
216         // maSelState.GetEditSelection() returns (0,0) if not in edit mode -> ok
217         pEditView->SetSelection( maSelState.GetEditSelection() );
218 
219         ClearModifyFlag();
220         mnCurrCol = nNewCol;
221         mnCurrRow = nNewRow;
222     }
223 
224     return bFound;
225 }
226 
RestoreCursorPos()227 void ScConversionEngineBase::RestoreCursorPos()
228 {
229     const ScAddress& rPos = maSelState.GetCellCursor();
230     mrViewData.GetViewShell()->SetCursor( rPos.Col(), rPos.Row() );
231 }
232 
ShowTableWrapDialog()233 bool ScConversionEngineBase::ShowTableWrapDialog()
234 {
235     // default: no dialog, always restart at top
236     return true;
237 }
238 
ShowFinishDialog()239 void ScConversionEngineBase::ShowFinishDialog()
240 {
241     // default: no dialog
242 }
243 
244 // private --------------------------------------------------------------------
245 
FillFromCell(SCCOL nCol,SCROW nRow,SCTAB nTab)246 void ScConversionEngineBase::FillFromCell( SCCOL nCol, SCROW nRow, SCTAB nTab )
247 {
248     ScAddress aPos(nCol, nRow, nTab);
249 
250     ScRefCellValue aCell(mrDoc, aPos);
251     switch (aCell.meType)
252     {
253         case CELLTYPE_STRING:
254         {
255             SvNumberFormatter* pFormatter = mrDoc.GetFormatTable();
256             sal_uInt32 nNumFmt = mrDoc.GetNumberFormat(aPos);
257             OUString aText;
258             Color* pColor;
259             ScCellFormat::GetString(aCell, nNumFmt, aText, &pColor, *pFormatter, &mrDoc);
260 
261             SetText(aText);
262         }
263         break;
264         case CELLTYPE_EDIT:
265         {
266             const EditTextObject* pNewEditObj = aCell.mpEditText;
267             SetText(*pNewEditObj);
268         }
269         break;
270         default:
271             SetText(EMPTY_OUSTRING);
272     }
273 }
274 
ScSpellingEngine(SfxItemPool * pEnginePoolP,ScViewData & rViewData,ScDocument * pUndoDoc,ScDocument * pRedoDoc,css::uno::Reference<css::linguistic2::XSpellChecker1> const & xSpeller)275 ScSpellingEngine::ScSpellingEngine(
276         SfxItemPool* pEnginePoolP, ScViewData& rViewData,
277         ScDocument* pUndoDoc, ScDocument* pRedoDoc,
278         css::uno::Reference< css::linguistic2::XSpellChecker1 > const & xSpeller ) :
279     ScConversionEngineBase( pEnginePoolP, rViewData, pUndoDoc, pRedoDoc )
280 {
281     SetSpeller( xSpeller );
282 }
283 
ConvertAll(EditView & rEditView)284 void ScSpellingEngine::ConvertAll( EditView& rEditView )
285 {
286     EESpellState eState = EESpellState::Ok;
287     if( FindNextConversionCell() )
288         eState = rEditView.StartSpeller( true );
289 
290     OSL_ENSURE( eState != EESpellState::NoSpeller, "ScSpellingEngine::Convert - no spell checker" );
291 }
292 
SpellNextDocument()293 bool ScSpellingEngine::SpellNextDocument()
294 {
295     return FindNextConversionCell();
296 }
297 
NeedsConversion()298 bool ScSpellingEngine::NeedsConversion()
299 {
300     return HasSpellErrors() != EESpellState::Ok;
301 }
302 
ShowTableWrapDialog()303 bool ScSpellingEngine::ShowTableWrapDialog()
304 {
305     weld::Window* pParent = GetDialogParent();
306     weld::WaitObject aWaitOff(pParent);
307 
308     std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(pParent,
309                                               VclMessageType::Question, VclButtonsType::YesNo,
310                                               ScResId(STR_SPELLING_BEGIN_TAB))); // "delete data?"
311     xBox->set_title(ScResId(STR_MSSG_DOSUBTOTALS_0));
312     xBox->set_default_response(RET_YES);
313     return xBox->run() == RET_YES;
314 }
315 
ShowFinishDialog()316 void ScSpellingEngine::ShowFinishDialog()
317 {
318     weld::Window* pParent = GetDialogParent();
319     weld::WaitObject aWaitOff(pParent);
320     std::unique_ptr<weld::MessageDialog> xInfoBox(Application::CreateMessageDialog(pParent,
321                                                   VclMessageType::Info, VclButtonsType::Ok,
322                                                   ScResId(STR_SPELLING_STOP_OK)));
323     xInfoBox->run();
324 }
325 
GetDialogParent()326 weld::Window* ScSpellingEngine::GetDialogParent()
327 {
328     sal_uInt16 nWinId = ScSpellDialogChildWindow::GetChildWindowId();
329     SfxViewFrame* pViewFrm = mrViewData.GetViewShell()->GetViewFrame();
330     if( pViewFrm->HasChildWindow( nWinId ) )
331     {
332         if( SfxChildWindow* pChild = pViewFrm->GetChildWindow( nWinId ) )
333         {
334             auto xController = pChild->GetController();
335             if (xController)
336             {
337                 if (weld::Window *pRet = xController->getDialog())
338                 {
339                     if (pRet->get_visible())
340                         return pRet;
341                 }
342             }
343         }
344     }
345 
346     // fall back to standard dialog parent
347     return ScDocShell::GetActiveDialogParent();
348 }
349 
ScConversionParam(ScConversionType eConvType)350 ScConversionParam::ScConversionParam( ScConversionType eConvType ) :
351     meConvType( eConvType ),
352     meSourceLang( LANGUAGE_NONE ),
353     meTargetLang( LANGUAGE_NONE ),
354     mnOptions( 0 ),
355     mbUseTargetFont( false ),
356     mbIsInteractive( false )
357 {
358 }
359 
ScConversionParam(ScConversionType eConvType,LanguageType eLang,sal_Int32 nOptions,bool bIsInteractive)360 ScConversionParam::ScConversionParam( ScConversionType eConvType,
361         LanguageType eLang, sal_Int32 nOptions, bool bIsInteractive ) :
362     meConvType( eConvType ),
363     meSourceLang( eLang ),
364     meTargetLang( eLang ),
365     mnOptions( nOptions ),
366     mbUseTargetFont( false ),
367     mbIsInteractive( bIsInteractive )
368 {
369     if (LANGUAGE_KOREAN == eLang)
370         mnOptions = i18n::TextConversionOption::CHARACTER_BY_CHARACTER;
371 }
372 
ScConversionParam(ScConversionType eConvType,LanguageType eSourceLang,LanguageType eTargetLang,const vcl::Font & rTargetFont,sal_Int32 nOptions,bool bIsInteractive)373 ScConversionParam::ScConversionParam( ScConversionType eConvType,
374         LanguageType eSourceLang, LanguageType eTargetLang, const vcl::Font& rTargetFont,
375         sal_Int32 nOptions, bool bIsInteractive ) :
376     meConvType( eConvType ),
377     meSourceLang( eSourceLang ),
378     meTargetLang( eTargetLang ),
379     maTargetFont( rTargetFont ),
380     mnOptions( nOptions ),
381     mbUseTargetFont( true ),
382     mbIsInteractive( bIsInteractive )
383 {
384     if (LANGUAGE_KOREAN == meSourceLang && LANGUAGE_KOREAN == meTargetLang)
385         mnOptions = i18n::TextConversionOption::CHARACTER_BY_CHARACTER;
386 }
387 
ScTextConversionEngine(SfxItemPool * pEnginePoolP,ScViewData & rViewData,const ScConversionParam & rConvParam,ScDocument * pUndoDoc,ScDocument * pRedoDoc)388 ScTextConversionEngine::ScTextConversionEngine(
389         SfxItemPool* pEnginePoolP, ScViewData& rViewData,
390         const ScConversionParam& rConvParam,
391         ScDocument* pUndoDoc, ScDocument* pRedoDoc ) :
392     ScConversionEngineBase( pEnginePoolP, rViewData, pUndoDoc, pRedoDoc ),
393     maConvParam( rConvParam )
394 {
395 }
396 
ConvertAll(EditView & rEditView)397 void ScTextConversionEngine::ConvertAll( EditView& rEditView )
398 {
399     if( FindNextConversionCell() )
400     {
401         rEditView.StartTextConversion(
402             maConvParam.GetSourceLang(), maConvParam.GetTargetLang(), maConvParam.GetTargetFont(),
403             maConvParam.GetOptions(), maConvParam.IsInteractive(), true );
404         // #i34769# restore initial cursor position
405         RestoreCursorPos();
406     }
407 }
408 
ConvertNextDocument()409 bool ScTextConversionEngine::ConvertNextDocument()
410 {
411     return FindNextConversionCell();
412 }
413 
NeedsConversion()414 bool ScTextConversionEngine::NeedsConversion()
415 {
416     return HasConvertibleTextPortion( maConvParam.GetSourceLang() );
417 }
418 
419 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
420