/src/libreoffice/sc/source/ui/view/spelleng.cxx
Line | Count | Source |
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 <utility> |
30 | | #include <vcl/settings.hxx> |
31 | | #include <vcl/svapp.hxx> |
32 | | #include <vcl/weld/MessageDialog.hxx> |
33 | | #include <vcl/weld/weld.hxx> |
34 | | #include <osl/diagnose.h> |
35 | | |
36 | | #include <spelldialog.hxx> |
37 | | #include <tabvwsh.hxx> |
38 | | #include <docsh.hxx> |
39 | | #include <cellvalue.hxx> |
40 | | #include <cellform.hxx> |
41 | | #include <patattr.hxx> |
42 | | #include <globstr.hrc> |
43 | | #include <scresid.hxx> |
44 | | #include <markdata.hxx> |
45 | | |
46 | | #include <memory> |
47 | | |
48 | | using namespace ::com::sun::star; |
49 | | |
50 | | ScConversionEngineBase::ScConversionEngineBase( |
51 | | SfxItemPool* pEnginePoolP, ScViewData& rViewData, |
52 | | ScDocument* pUndoDoc, ScDocument* pRedoDoc ) : |
53 | 0 | ScEditEngineDefaulter( pEnginePoolP ), |
54 | 0 | mrViewData( rViewData ), |
55 | 0 | mrDocShell( *rViewData.GetDocShell() ), |
56 | 0 | mrDoc( rViewData.GetDocShell()->GetDocument() ), |
57 | 0 | maSelState( rViewData ), |
58 | 0 | mpUndoDoc( pUndoDoc ), |
59 | 0 | mpRedoDoc( pRedoDoc ), |
60 | 0 | meCurrLang( LANGUAGE_ENGLISH_US ), |
61 | 0 | mbIsAnyModified( false ), |
62 | 0 | mbInitialState( true ), |
63 | 0 | mbWrappedInTable( false ), |
64 | 0 | mbFinished( false ) |
65 | 0 | { |
66 | 0 | maSelState.GetCellCursor().GetVars( mnStartCol, mnStartRow, mnStartTab ); |
67 | | // start with cell A1 in cell/range/multi-selection, will seek to first selected |
68 | 0 | if( maSelState.GetSelectionType() == SC_SELECTTYPE_SHEET ) |
69 | 0 | { |
70 | 0 | mnStartCol = 0; |
71 | 0 | mnStartRow = 0; |
72 | 0 | } |
73 | 0 | mnCurrCol = mnStartCol; |
74 | 0 | mnCurrRow = mnStartRow; |
75 | 0 | } |
76 | | |
77 | | ScConversionEngineBase::~ScConversionEngineBase() |
78 | 0 | { |
79 | 0 | } |
80 | | |
81 | | bool ScConversionEngineBase::FindNextConversionCell() |
82 | 0 | { |
83 | 0 | ScMarkData& rMark = mrViewData.GetMarkData(); |
84 | 0 | ScTabViewShell* pViewShell = mrViewData.GetViewShell(); |
85 | 0 | const ScPatternAttr* pPattern = nullptr; |
86 | 0 | const ScPatternAttr* pLastPattern = nullptr; |
87 | |
|
88 | 0 | SfxItemSet aEditDefaults(GetEmptyItemSet()); |
89 | |
|
90 | 0 | if( IsModified() ) |
91 | 0 | { |
92 | 0 | mbIsAnyModified = true; |
93 | |
|
94 | 0 | OUString aNewStr = GetText(); |
95 | | |
96 | | // Check if the user has changed the language. If the new language is |
97 | | // applied to the entire string length, we will set the language as cell |
98 | | // attribute. Otherwise we will commit this as an edit-engine string. |
99 | 0 | editeng::LanguageSpan aLang = GetLanguage(0, 0); |
100 | |
|
101 | 0 | bool bSimpleString = GetParagraphCount() == 1 && |
102 | 0 | aLang.nLang != LANGUAGE_DONTKNOW && |
103 | 0 | aLang.nStart == 0 && |
104 | 0 | aLang.nEnd == aNewStr.getLength(); |
105 | |
|
106 | 0 | bool bMultiTab = (rMark.GetSelectCount() > 1); |
107 | |
|
108 | 0 | OUString aVisibleStr; |
109 | 0 | if( bMultiTab ) |
110 | 0 | aVisibleStr = mrDoc.GetString(mnCurrCol, mnCurrRow, mnStartTab); |
111 | |
|
112 | 0 | for( SCTAB nTab = 0, nTabCount = mrDoc.GetTableCount(); nTab < nTabCount; ++nTab ) |
113 | 0 | { |
114 | | // always change the cell on the visible tab, |
115 | | // on the other selected tabs only if they contain the same text |
116 | |
|
117 | 0 | if ((nTab == mnStartTab) || |
118 | 0 | (bMultiTab && rMark.GetTableSelect(nTab) && mrDoc.GetString(mnCurrCol, mnCurrRow, nTab) == aVisibleStr)) |
119 | 0 | { |
120 | 0 | ScAddress aPos( mnCurrCol, mnCurrRow, nTab ); |
121 | 0 | CellType eCellType = mrDoc.GetCellType( aPos ); |
122 | 0 | bool bEmptyCell = eCellType == CELLTYPE_NONE; |
123 | |
|
124 | 0 | if (mpUndoDoc && !bEmptyCell) |
125 | 0 | mrDoc.CopyCellToDocument(aPos, aPos, *mpUndoDoc); |
126 | |
|
127 | 0 | if (!bSimpleString || eCellType == CELLTYPE_EDIT) |
128 | 0 | { |
129 | 0 | std::unique_ptr<EditTextObject> pEditObj(CreateTextObject()); |
130 | 0 | mrDoc.SetEditText(aPos, *pEditObj, GetItemPool()); |
131 | 0 | } |
132 | 0 | else |
133 | 0 | { |
134 | | // Set the new string and update the language with the cell. |
135 | 0 | mrDoc.SetString(aPos, aNewStr); |
136 | |
|
137 | 0 | const ScPatternAttr* pAttr(mrDoc.GetPattern(aPos)); |
138 | 0 | ScPatternAttr* pNewAttr(nullptr); |
139 | |
|
140 | 0 | if (nullptr != pAttr) |
141 | 0 | pNewAttr = new ScPatternAttr(*pAttr); |
142 | 0 | else |
143 | 0 | pNewAttr = new ScPatternAttr(mrDoc.getCellAttributeHelper()); |
144 | |
|
145 | 0 | pNewAttr->ItemSetPut(SvxLanguageItem(aLang.nLang, ATTR_FONT_LANGUAGE)); |
146 | 0 | mrDoc.SetPattern(aPos, CellAttributeHolder(pNewAttr, true)); |
147 | 0 | } |
148 | |
|
149 | 0 | if (mpRedoDoc && !bEmptyCell) |
150 | 0 | mrDoc.CopyCellToDocument(aPos, aPos, *mpRedoDoc); |
151 | |
|
152 | 0 | mrDocShell.PostPaintCell(aPos); |
153 | 0 | } |
154 | 0 | } |
155 | 0 | } |
156 | |
|
157 | 0 | SCCOL nNewCol = mnCurrCol; |
158 | 0 | SCROW nNewRow = mnCurrRow; |
159 | |
|
160 | 0 | if( mbInitialState ) |
161 | 0 | { |
162 | | /* On very first call, decrement row to let GetNextSpellingCell() find |
163 | | the first cell of current range. */ |
164 | 0 | mbInitialState = false; |
165 | 0 | --nNewRow; |
166 | 0 | } |
167 | |
|
168 | 0 | bool bSheetSel = maSelState.GetSelectionType() == SC_SELECTTYPE_SHEET; |
169 | 0 | bool bLoop = true; |
170 | 0 | bool bFound = false; |
171 | 0 | while( bLoop && !bFound ) |
172 | 0 | { |
173 | 0 | bLoop = mrDoc.GetNextSpellingCell( nNewCol, nNewRow, mnStartTab, bSheetSel, rMark ); |
174 | 0 | if( bLoop ) |
175 | 0 | { |
176 | 0 | FillFromCell( mnCurrCol, mnCurrRow, mnStartTab ); |
177 | |
|
178 | 0 | if( mbWrappedInTable && ((nNewCol > mnStartCol) || ((nNewCol == mnStartCol) && (nNewRow >= mnStartRow))) ) |
179 | 0 | { |
180 | 0 | ShowFinishDialog(); |
181 | 0 | bLoop = false; |
182 | 0 | mbFinished = true; |
183 | 0 | } |
184 | 0 | else if( nNewCol >= mrDoc.GetAllocatedColumnsCount(mnStartTab) ) |
185 | 0 | { |
186 | | // no more cells in the sheet - try to restart at top of sheet |
187 | |
|
188 | 0 | if( bSheetSel || ((mnStartCol == 0) && (mnStartRow == 0)) ) |
189 | 0 | { |
190 | | // conversion started at cell A1 or in selection, do not query to restart at top |
191 | 0 | ShowFinishDialog(); |
192 | 0 | bLoop = false; |
193 | 0 | mbFinished = true; |
194 | 0 | } |
195 | 0 | else if( ShowTableWrapDialog() ) |
196 | 0 | { |
197 | | // conversion started anywhere but in cell A1, user wants to restart |
198 | 0 | nNewRow = mrDoc.MaxRow() + 2; |
199 | 0 | mbWrappedInTable = true; |
200 | 0 | } |
201 | 0 | else |
202 | 0 | { |
203 | 0 | bLoop = false; |
204 | 0 | mbFinished = true; |
205 | 0 | } |
206 | 0 | } |
207 | 0 | else |
208 | 0 | { |
209 | | // GetPattern may implicitly allocates the column if not exists, |
210 | 0 | pPattern = mrDoc.GetPattern( nNewCol, nNewRow, mnStartTab ); |
211 | 0 | if( pPattern && !ScPatternAttr::areSame(pPattern, pLastPattern) ) |
212 | 0 | { |
213 | 0 | pPattern->FillEditItemSet( &aEditDefaults ); |
214 | 0 | SetDefaults( aEditDefaults ); |
215 | 0 | pLastPattern = pPattern; |
216 | 0 | } |
217 | | |
218 | | // language changed? |
219 | 0 | const SfxPoolItem& rItem = mrDoc.GetAttr( nNewCol, nNewRow, mnStartTab, ATTR_FONT_LANGUAGE ); |
220 | 0 | if (const SvxLanguageItem* pLangItem = dynamic_cast<const SvxLanguageItem*>(&rItem)) |
221 | 0 | { |
222 | 0 | LanguageType eLang = pLangItem->GetValue(); |
223 | 0 | if( eLang == LANGUAGE_SYSTEM ) |
224 | 0 | eLang = Application::GetSettings().GetLanguageTag().getLanguageType(); // never use SYSTEM for spelling |
225 | 0 | if( eLang != meCurrLang ) |
226 | 0 | { |
227 | 0 | meCurrLang = eLang; |
228 | 0 | SetDefaultLanguage( eLang ); |
229 | 0 | } |
230 | 0 | } |
231 | |
|
232 | 0 | FillFromCell( nNewCol, nNewRow, mnStartTab ); |
233 | |
|
234 | 0 | bFound = bLoop && NeedsConversion(); |
235 | 0 | } |
236 | 0 | } |
237 | 0 | } |
238 | |
|
239 | 0 | if( bFound ) |
240 | 0 | { |
241 | 0 | pViewShell->AlignToCursor( nNewCol, nNewRow, SC_FOLLOW_JUMP ); |
242 | 0 | pViewShell->SetCursor( nNewCol, nNewRow, true ); |
243 | 0 | mrViewData.GetView()->MakeEditView(*this, nNewCol, nNewRow); |
244 | 0 | EditView* pEditView = mrViewData.GetSpellingView(); |
245 | | // maSelState.GetEditSelection() returns (0,0) if not in edit mode -> ok |
246 | 0 | pEditView->SetSelection( maSelState.GetEditSelection() ); |
247 | |
|
248 | 0 | ClearModifyFlag(); |
249 | 0 | mnCurrCol = nNewCol; |
250 | 0 | mnCurrRow = nNewRow; |
251 | 0 | } |
252 | |
|
253 | 0 | return bFound; |
254 | 0 | } |
255 | | |
256 | | void ScConversionEngineBase::RestoreCursorPos() |
257 | 0 | { |
258 | 0 | const ScAddress& rPos = maSelState.GetCellCursor(); |
259 | 0 | mrViewData.GetViewShell()->SetCursor( rPos.Col(), rPos.Row() ); |
260 | 0 | } |
261 | | |
262 | | bool ScConversionEngineBase::ShowTableWrapDialog() |
263 | 0 | { |
264 | | // default: no dialog, always restart at top |
265 | 0 | return true; |
266 | 0 | } |
267 | | |
268 | | void ScConversionEngineBase::ShowFinishDialog() |
269 | 0 | { |
270 | | // default: no dialog |
271 | 0 | } |
272 | | |
273 | | // private -------------------------------------------------------------------- |
274 | | |
275 | | void ScConversionEngineBase::FillFromCell( SCCOL nCol, SCROW nRow, SCTAB nTab ) |
276 | 0 | { |
277 | 0 | ScAddress aPos(nCol, nRow, nTab); |
278 | |
|
279 | 0 | ScRefCellValue aCell(mrDoc, aPos); |
280 | 0 | switch (aCell.getType()) |
281 | 0 | { |
282 | 0 | case CELLTYPE_STRING: |
283 | 0 | { |
284 | 0 | sal_uInt32 nNumFmt = mrDoc.GetNumberFormat(ScRange(aPos)); |
285 | 0 | const Color* pColor; |
286 | 0 | OUString aText = ScCellFormat::GetString(aCell, nNumFmt, &pColor, nullptr, mrDoc); |
287 | |
|
288 | 0 | SetTextCurrentDefaults(aText); |
289 | 0 | } |
290 | 0 | break; |
291 | 0 | case CELLTYPE_EDIT: |
292 | 0 | { |
293 | 0 | const EditTextObject* pNewEditObj = aCell.getEditText(); |
294 | 0 | SetTextCurrentDefaults(*pNewEditObj); |
295 | 0 | } |
296 | 0 | break; |
297 | 0 | default: |
298 | 0 | SetTextCurrentDefaults(OUString()); |
299 | 0 | } |
300 | 0 | } |
301 | | |
302 | | ScSpellingEngine::ScSpellingEngine( |
303 | | SfxItemPool* pEnginePoolP, ScViewData& rViewData, |
304 | | ScDocument* pUndoDoc, ScDocument* pRedoDoc, |
305 | | css::uno::Reference< css::linguistic2::XSpellChecker1 > const & xSpeller ) : |
306 | 0 | ScConversionEngineBase( pEnginePoolP, rViewData, pUndoDoc, pRedoDoc ) |
307 | 0 | { |
308 | 0 | SetSpeller( xSpeller ); |
309 | 0 | } |
310 | | |
311 | | void ScSpellingEngine::ConvertAll(weld::Widget* pDialogParent, EditView& rEditView) |
312 | 0 | { |
313 | 0 | EESpellState eState = EESpellState::Ok; |
314 | 0 | if( FindNextConversionCell() ) |
315 | 0 | eState = rEditView.StartSpeller(pDialogParent, true); |
316 | |
|
317 | 0 | OSL_ENSURE( eState != EESpellState::NoSpeller, "ScSpellingEngine::Convert - no spell checker" ); |
318 | 0 | } |
319 | | |
320 | | bool ScSpellingEngine::SpellNextDocument() |
321 | 0 | { |
322 | 0 | return FindNextConversionCell(); |
323 | 0 | } |
324 | | |
325 | | bool ScSpellingEngine::NeedsConversion() |
326 | 0 | { |
327 | 0 | return HasSpellErrors() != EESpellState::Ok; |
328 | 0 | } |
329 | | |
330 | | bool ScSpellingEngine::ShowTableWrapDialog() |
331 | 0 | { |
332 | 0 | weld::Widget* pParent = GetDialogParent(); |
333 | 0 | weld::WaitObject aWaitOff(pParent); |
334 | |
|
335 | 0 | std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(pParent, |
336 | 0 | VclMessageType::Question, VclButtonsType::YesNo, |
337 | 0 | ScResId(STR_SPELLING_BEGIN_TAB))); // "delete data?" |
338 | 0 | xBox->set_title(ScResId(STR_MSSG_DOSUBTOTALS_0)); |
339 | 0 | xBox->set_default_response(RET_YES); |
340 | 0 | return xBox->run() == RET_YES; |
341 | 0 | } |
342 | | |
343 | | void ScSpellingEngine::ShowFinishDialog() |
344 | 0 | { |
345 | 0 | weld::Widget* pParent = GetDialogParent(); |
346 | 0 | weld::WaitObject aWaitOff(pParent); |
347 | 0 | std::unique_ptr<weld::MessageDialog> xInfoBox(Application::CreateMessageDialog(pParent, |
348 | 0 | VclMessageType::Info, VclButtonsType::Ok, |
349 | 0 | ScResId(STR_SPELLING_STOP_OK))); |
350 | 0 | xInfoBox->run(); |
351 | 0 | } |
352 | | |
353 | | weld::Widget* ScSpellingEngine::GetDialogParent() |
354 | 0 | { |
355 | 0 | sal_uInt16 nWinId = ScSpellDialogChildWindow::GetChildWindowId(); |
356 | 0 | SfxViewFrame& rViewFrm = mrViewData.GetViewShell()->GetViewFrame(); |
357 | 0 | if( rViewFrm.HasChildWindow( nWinId ) ) |
358 | 0 | { |
359 | 0 | if( SfxChildWindow* pChild = rViewFrm.GetChildWindow( nWinId ) ) |
360 | 0 | { |
361 | 0 | auto xController = pChild->GetController(); |
362 | 0 | if (xController) |
363 | 0 | { |
364 | 0 | if (weld::Window *pRet = xController->getDialog()) |
365 | 0 | { |
366 | 0 | if (pRet->get_visible()) |
367 | 0 | return pRet; |
368 | 0 | } |
369 | 0 | } |
370 | 0 | } |
371 | 0 | } |
372 | | |
373 | | // fall back to standard dialog parent |
374 | 0 | return ScDocShell::GetActiveDialogParent(); |
375 | 0 | } |
376 | | |
377 | | ScConversionParam::ScConversionParam( ScConversionType eConvType ) : |
378 | 0 | meConvType( eConvType ), |
379 | 0 | meSourceLang( LANGUAGE_NONE ), |
380 | 0 | meTargetLang( LANGUAGE_NONE ), |
381 | 0 | mnOptions( 0 ), |
382 | 0 | mbUseTargetFont( false ), |
383 | 0 | mbIsInteractive( false ) |
384 | 0 | { |
385 | 0 | } |
386 | | |
387 | | ScConversionParam::ScConversionParam( ScConversionType eConvType, |
388 | | LanguageType eLang, sal_Int32 nOptions, bool bIsInteractive ) : |
389 | 0 | meConvType( eConvType ), |
390 | 0 | meSourceLang( eLang ), |
391 | 0 | meTargetLang( eLang ), |
392 | 0 | mnOptions( nOptions ), |
393 | 0 | mbUseTargetFont( false ), |
394 | 0 | mbIsInteractive( bIsInteractive ) |
395 | 0 | { |
396 | 0 | if (LANGUAGE_KOREAN == eLang) |
397 | 0 | mnOptions = i18n::TextConversionOption::CHARACTER_BY_CHARACTER; |
398 | 0 | } |
399 | | |
400 | | ScConversionParam::ScConversionParam( ScConversionType eConvType, |
401 | | LanguageType eSourceLang, LanguageType eTargetLang, vcl::Font aTargetFont, |
402 | | sal_Int32 nOptions, bool bIsInteractive ) : |
403 | 0 | meConvType( eConvType ), |
404 | 0 | meSourceLang( eSourceLang ), |
405 | 0 | meTargetLang( eTargetLang ), |
406 | 0 | maTargetFont(std::move( aTargetFont )), |
407 | 0 | mnOptions( nOptions ), |
408 | 0 | mbUseTargetFont( true ), |
409 | 0 | mbIsInteractive( bIsInteractive ) |
410 | 0 | { |
411 | 0 | if (LANGUAGE_KOREAN == meSourceLang && LANGUAGE_KOREAN == meTargetLang) |
412 | 0 | mnOptions = i18n::TextConversionOption::CHARACTER_BY_CHARACTER; |
413 | 0 | } |
414 | | |
415 | | ScTextConversionEngine::ScTextConversionEngine( |
416 | | SfxItemPool* pEnginePoolP, ScViewData& rViewData, |
417 | | ScConversionParam aConvParam, |
418 | | ScDocument* pUndoDoc, ScDocument* pRedoDoc ) : |
419 | 0 | ScConversionEngineBase( pEnginePoolP, rViewData, pUndoDoc, pRedoDoc ), |
420 | 0 | maConvParam(std::move( aConvParam )) |
421 | 0 | { |
422 | 0 | } |
423 | | |
424 | | void ScTextConversionEngine::ConvertAll(weld::Widget* pDialogParent, EditView& rEditView) |
425 | 0 | { |
426 | 0 | if( FindNextConversionCell() ) |
427 | 0 | { |
428 | 0 | rEditView.StartTextConversion(pDialogParent, |
429 | 0 | maConvParam.GetSourceLang(), maConvParam.GetTargetLang(), maConvParam.GetTargetFont(), |
430 | 0 | maConvParam.GetOptions(), maConvParam.IsInteractive(), true ); |
431 | | // #i34769# restore initial cursor position |
432 | 0 | RestoreCursorPos(); |
433 | 0 | } |
434 | 0 | } |
435 | | |
436 | | bool ScTextConversionEngine::ConvertNextDocument() |
437 | 0 | { |
438 | 0 | return FindNextConversionCell(); |
439 | 0 | } |
440 | | |
441 | | bool ScTextConversionEngine::NeedsConversion() |
442 | 0 | { |
443 | 0 | return HasConvertibleTextPortion( maConvParam.GetSourceLang() ); |
444 | 0 | } |
445 | | |
446 | | /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |