LibreOffice Module cui (master)  1
SpellDialog.cxx
Go to the documentation of this file.
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 <memory>
21 #include "SpellAttrib.hxx"
22 #include <sfx2/bindings.hxx>
23 #include <sfx2/sfxsids.hrc>
24 #include <sfx2/viewfrm.hxx>
25 #include <svl/grabbagitem.hxx>
26 #include <svl/undo.hxx>
27 #include <tools/debug.hxx>
28 #include <unotools/lingucfg.hxx>
29 #include <editeng/colritem.hxx>
30 #include <editeng/eeitem.hxx>
31 #include <editeng/langitem.hxx>
32 #include <editeng/splwrap.hxx>
33 #include <editeng/unolingu.hxx>
34 #include <editeng/wghtitem.hxx>
35 #include <linguistic/misc.hxx>
36 #include <com/sun/star/lang/XServiceInfo.hpp>
37 #include <com/sun/star/frame/XStorable.hpp>
38 #include <com/sun/star/linguistic2/XDictionary.hpp>
39 #include <com/sun/star/linguistic2/XSpellAlternatives.hpp>
40 #include <com/sun/star/linguistic2/XSearchableDictionaryList.hpp>
41 #include <com/sun/star/linguistic2/XSpellChecker1.hpp>
42 #include <sfx2/app.hxx>
43 #include <vcl/event.hxx>
44 #include <vcl/svapp.hxx>
45 #include <vcl/texteng.hxx>
46 #include <vcl/weld.hxx>
48 #include <SpellDialog.hxx>
49 #include <optlingu.hxx>
50 #include <treeopt.hxx>
51 #include <svtools/langtab.hxx>
52 #include <sal/log.hxx>
54 
55 using namespace ::com::sun::star;
56 using namespace ::com::sun::star::uno;
57 using namespace ::com::sun::star::beans;
58 using namespace ::com::sun::star::linguistic2;
59 using namespace linguistic;
60 
61 
62 // struct SpellDialog_Impl ---------------------------------------------
63 
65 {
66  Sequence< Reference< XDictionary > > aDics;
67 };
68 
69 
70 #define SPELLUNDO_START 200
71 
72 #define SPELLUNDO_CHANGE_LANGUAGE (SPELLUNDO_START + 1)
73 #define SPELLUNDO_CHANGE_TEXTENGINE (SPELLUNDO_START + 2)
74 #define SPELLUNDO_CHANGE_NEXTERROR (SPELLUNDO_START + 3)
75 #define SPELLUNDO_CHANGE_ADD_TO_DICTIONARY (SPELLUNDO_START + 4)
76 #define SPELLUNDO_CHANGE_GROUP (SPELLUNDO_START + 5) //undo list
77 #define SPELLUNDO_MOVE_ERROREND (SPELLUNDO_START + 6)
78 #define SPELLUNDO_UNDO_EDIT_MODE (SPELLUNDO_START + 7)
79 #define SPELLUNDO_ADD_IGNORE_RULE (SPELLUNDO_START + 8)
80 
81 namespace svx{
83 {
84  sal_uInt16 m_nId;
86  //undo of button enabling
89  //undo of MarkNextError - used in change and change all, ignore and ignore all
93  //undo of AddToDictionary
94  Reference<XDictionary> m_xDictionary;
95  OUString m_sAddedWord;
96  //move end of error - ::ChangeMarkedWord()
97  long m_nOffset;
98 
99 public:
100  SpellUndoAction_Impl(sal_uInt16 nId, const Link<SpellUndoAction_Impl&,void>& rActionLink) :
101  m_nId(nId),
102  m_rActionLink( rActionLink),
103  m_bEnableChangePB(false),
104  m_bEnableChangeAllPB(false),
105  m_nOldErrorStart(-1),
106  m_nOldErrorEnd(-1),
107  m_bIsErrorLanguageSelected(false),
108  m_nOffset(0)
109  {}
110 
111  virtual void Undo() override;
112  sal_uInt16 GetId() const;
113 
114  void SetEnableChangePB(){m_bEnableChangePB = true;}
115  bool IsEnableChangePB(){return m_bEnableChangePB;}
116 
117  void SetEnableChangeAllPB(){m_bEnableChangeAllPB = true;}
118  bool IsEnableChangeAllPB(){return m_bEnableChangeAllPB;}
119 
120  void SetErrorMove(long nOldStart, long nOldEnd)
121  {
122  m_nOldErrorStart = nOldStart;
123  m_nOldErrorEnd = nOldEnd;
124  }
125  long GetOldErrorStart() { return m_nOldErrorStart;}
126  long GetOldErrorEnd() { return m_nOldErrorEnd;}
127 
128  void SetErrorLanguageSelected(bool bSet){ m_bIsErrorLanguageSelected = bSet;}
129  bool IsErrorLanguageSelected() const {return m_bIsErrorLanguageSelected;}
130 
131  void SetDictionary(const Reference<XDictionary>& xDict) { m_xDictionary = xDict; }
132  const Reference<XDictionary>& GetDictionary() const { return m_xDictionary; }
133  void SetAddedWord(const OUString& rWord) {m_sAddedWord = rWord;}
134  const OUString& GetAddedWord() const { return m_sAddedWord;}
135 
136  void SetOffset(long nSet) {m_nOffset = nSet;}
137  long GetOffset() const {return m_nOffset;}
138 };
139 }//namespace svx
140 using namespace ::svx;
141 
142 void SpellUndoAction_Impl::Undo()
143 {
144  m_rActionLink.Call(*this);
145 }
146 
147 
148 sal_uInt16 SpellUndoAction_Impl::GetId()const
149 {
150  return m_nId;
151 }
152 
153 // class SvxSpellCheckDialog ---------------------------------------------
154 
155 SpellDialog::SpellDialog(SpellDialogChildWindow* pChildWindow,
156  weld::Window * pParent, SfxBindings* _pBindings)
157  : SfxModelessDialogController (_pBindings, pChildWindow,
158  pParent, "cui/ui/spellingdialog.ui", "SpellingDialog")
159  , aDialogUndoLink(LINK (this, SpellDialog, DialogUndoHdl))
160  , bFocusLocked(true)
161  , rParent(*pChildWindow)
162  , pImpl( new SpellDialog_Impl )
163  , m_xAltTitle(m_xBuilder->weld_label("alttitleft"))
164  , m_xResumeFT(m_xBuilder->weld_label("resumeft"))
165  , m_xNoSuggestionsFT(m_xBuilder->weld_label("nosuggestionsft"))
166  , m_xLanguageFT(m_xBuilder->weld_label("languageft"))
167  , m_xLanguageLB(new LanguageBox(m_xBuilder->weld_combo_box("languagelb")))
168  , m_xExplainFT(m_xBuilder->weld_label("explain"))
169  , m_xExplainLink(m_xBuilder->weld_link_button("explainlink"))
170  , m_xNotInDictFT(m_xBuilder->weld_label("notindictft"))
171  , m_xSentenceED(new SentenceEditWindow_Impl)
172  , m_xSuggestionFT(m_xBuilder->weld_label("suggestionsft"))
173  , m_xSuggestionLB(m_xBuilder->weld_tree_view("suggestionslb"))
174  , m_xIgnorePB(m_xBuilder->weld_button("ignore"))
175  , m_xIgnoreAllPB(m_xBuilder->weld_button("ignoreall"))
176  , m_xIgnoreRulePB(m_xBuilder->weld_button("ignorerule"))
177  , m_xAddToDictPB(m_xBuilder->weld_button("add"))
178  , m_xAddToDictMB(m_xBuilder->weld_menu_button("addmb"))
179  , m_xChangePB(m_xBuilder->weld_button("change"))
180  , m_xChangeAllPB(m_xBuilder->weld_button("changeall"))
181  , m_xAutoCorrPB(m_xBuilder->weld_button("autocorrect"))
182  , m_xCheckGrammarCB(m_xBuilder->weld_check_button("checkgrammar"))
183  , m_xOptionsPB(m_xBuilder->weld_button("options"))
184  , m_xUndoPB(m_xBuilder->weld_button("undo"))
185  , m_xClosePB(m_xBuilder->weld_button("close"))
186  , m_xToolbar(m_xBuilder->weld_toolbar("toolbar"))
187  , m_xSentenceEDWeld(new weld::CustomWeld(*m_xBuilder, "sentence", *m_xSentenceED))
188 {
189  m_xSentenceED->SetSpellDialog(this);
190  m_xSentenceED->Init(m_xToolbar.get());
191 
192  m_sTitleSpellingGrammar = m_xDialog->get_title();
193  m_sTitleSpelling = m_xAltTitle->get_label();
194 
195  // fdo#68794 set initial title for cases where no text has been processed
196  // yet to show its language attributes
198  m_xDialog->set_title(m_xDialog->strip_mnemonic(sTitle.replaceFirst("$LANGUAGE ($LOCATION)", "")));
199 
200  m_sResumeST = m_xResumeFT->get_label();
201  m_sNoSuggestionsST = m_xNoSuggestionsFT->strip_mnemonic(m_xNoSuggestionsFT->get_label());
202 
203  Size aEdSize(m_xSuggestionLB->get_approximate_digit_width() * 60,
204  m_xSuggestionLB->get_height_rows(6));
205  m_xSuggestionLB->set_size_request(aEdSize.Width(), -1);
206  m_sIgnoreOnceST = m_xIgnorePB->get_label();
207  m_xAddToDictMB->set_help_id(m_xAddToDictPB->get_help_id());
209 
210  Init_Impl();
211 
212  // disable controls if service is missing
213  m_xDialog->set_sensitive(xSpell.is());
214 
215  //InitHdl wants to use virtual methods, so it
216  //can't be called during the ctor, so init
217  //it on next event cycle post-ctor
219 }
220 
222 {
223  if (pImpl)
224  {
225  // save possibly modified user-dictionaries
226  Reference< XSearchableDictionaryList > xDicList( LinguMgr::GetDictionaryList() );
227  if (xDicList.is())
228  SaveDictionaries( xDicList );
229 
230  pImpl.reset();
231  }
232 }
233 
235 {
236  // initialize handler
237  m_xClosePB->connect_clicked(LINK( this, SpellDialog, CancelHdl ) );
238  m_xChangePB->connect_clicked(LINK( this, SpellDialog, ChangeHdl ) );
239  m_xChangeAllPB->connect_clicked(LINK( this, SpellDialog, ChangeAllHdl ) );
240  m_xIgnorePB->connect_clicked(LINK( this, SpellDialog, IgnoreHdl ) );
241  m_xIgnoreAllPB->connect_clicked(LINK( this, SpellDialog, IgnoreAllHdl ) );
242  m_xIgnoreRulePB->connect_clicked(LINK( this, SpellDialog, IgnoreAllHdl ) );
243  m_xUndoPB->connect_clicked(LINK( this, SpellDialog, UndoHdl ) );
244 
245  m_xAutoCorrPB->connect_clicked( LINK( this, SpellDialog, ExtClickHdl ) );
246  m_xCheckGrammarCB->connect_clicked( LINK( this, SpellDialog, CheckGrammarHdl ));
247  m_xOptionsPB->connect_clicked( LINK( this, SpellDialog, ExtClickHdl ) );
248 
249  m_xSuggestionLB->connect_row_activated( LINK( this, SpellDialog, DoubleClickChangeHdl ) );
250 
251  m_xSentenceED->SetModifyHdl(LINK ( this, SpellDialog, ModifyHdl) );
252 
253  m_xAddToDictMB->connect_selected(LINK ( this, SpellDialog, AddToDictSelectHdl ) );
254  m_xAddToDictPB->connect_clicked(LINK ( this, SpellDialog, AddToDictClickHdl ) );
255 
256  m_xLanguageLB->connect_changed(LINK( this, SpellDialog, LanguageSelectHdl ) );
257 
258  // initialize language ListBox
259  m_xLanguageLB->SetLanguageList(SvxLanguageListFlags::SPELL_USED, false, false, true);
260 
261  m_xSentenceED->ClearModifyFlag();
262  LinguMgr::GetChangeAllList()->clear();
263 }
264 
265 void SpellDialog::UpdateBoxes_Impl(bool bCallFromSelectHdl)
266 {
267  sal_Int32 i;
268  m_xSuggestionLB->clear();
269 
270  SpellErrorDescription aSpellErrorDescription;
271  bool bSpellErrorDescription = m_xSentenceED->GetAlternatives(aSpellErrorDescription);
272 
273  LanguageType nAltLanguage = LANGUAGE_NONE;
274  Sequence< OUString > aNewWords;
275  bool bIsGrammarError = false;
276  if( bSpellErrorDescription )
277  {
278  nAltLanguage = LanguageTag::convertToLanguageType( aSpellErrorDescription.aLocale );
279  aNewWords = aSpellErrorDescription.aSuggestions;
280  bIsGrammarError = aSpellErrorDescription.bIsGrammarError;
281  m_xExplainLink->set_uri( aSpellErrorDescription.sExplanationURL );
282  m_xExplainFT->set_label( aSpellErrorDescription.sExplanation );
283  }
284  if( bSpellErrorDescription && !aSpellErrorDescription.sDialogTitle.isEmpty() )
285  {
286  // use this function to apply the correct image to be used...
287  SetTitle_Impl( nAltLanguage );
288  // then change the title to the one to be actually used
289  m_xDialog->set_title(m_xDialog->strip_mnemonic(aSpellErrorDescription.sDialogTitle));
290  }
291  else
292  SetTitle_Impl( nAltLanguage );
293  if( !bCallFromSelectHdl )
294  m_xLanguageLB->set_active_id( nAltLanguage );
295  int nDicts = InitUserDicts();
296 
297  // enter alternatives
298  const OUString *pNewWords = aNewWords.getConstArray();
299  const sal_Int32 nSize = aNewWords.getLength();
300  for ( i = 0; i < nSize; ++i )
301  {
302  OUString aTmp( pNewWords[i] );
303  if (m_xSuggestionLB->find_text(aTmp) == -1)
304  m_xSuggestionLB->append_text(aTmp);
305  }
306  if(!nSize)
307  m_xSuggestionLB->append_text(m_sNoSuggestionsST);
308  m_xAutoCorrPB->set_sensitive( nSize > 0 );
309 
310  m_xSuggestionFT->set_sensitive(nSize > 0);
311  m_xSuggestionLB->set_sensitive(nSize > 0);
312  if( nSize )
313  {
314  m_xSuggestionLB->select(0);
315  }
316  m_xChangePB->set_sensitive( nSize > 0);
317  m_xChangeAllPB->set_sensitive(nSize > 0);
318  bool bShowChangeAll = !bIsGrammarError;
319  m_xChangeAllPB->set_visible( bShowChangeAll );
320  m_xExplainFT->set_visible( !bShowChangeAll );
321  m_xLanguageLB->set_sensitive( bShowChangeAll );
322  m_xIgnoreAllPB->set_visible( bShowChangeAll );
323 
324  m_xAddToDictMB->set_visible( bShowChangeAll && nDicts > 1);
325  m_xAddToDictPB->set_visible( bShowChangeAll && nDicts <= 1);
326  m_xIgnoreRulePB->set_visible( !bShowChangeAll );
327  m_xIgnoreRulePB->set_sensitive(bSpellErrorDescription && !aSpellErrorDescription.sRuleId.isEmpty());
328  m_xAutoCorrPB->set_visible( bShowChangeAll && rParent.HasAutoCorrection() );
329 
330  bool bOldShowGrammar = m_xCheckGrammarCB->get_visible();
331  bool bOldShowExplain = m_xExplainLink->get_visible();
332 
334  m_xExplainLink->set_visible(!m_xExplainLink->get_uri().isEmpty());
335  if (m_xExplainFT->get_label().isEmpty())
336  {
337  m_xExplainFT->hide();
338  m_xExplainLink->hide();
339  }
340 
341  if (bOldShowExplain != m_xExplainLink->get_visible() || bOldShowGrammar != m_xCheckGrammarCB->get_visible())
342  m_xDialog->resize_to_request();
343 }
344 
345 void SpellDialog::SpellContinue_Impl(bool bUseSavedSentence, bool bIgnoreCurrentError )
346 {
347  //initially or after the last error of a sentence MarkNextError will fail
348  //then GetNextSentence() has to be called followed again by MarkNextError()
349  //MarkNextError is not initially called if the UndoEdit mode is active
350  bool bNextSentence = false;
351  if((!m_xSentenceED->IsUndoEditMode() && m_xSentenceED->MarkNextError( bIgnoreCurrentError, xSpell )) ||
352  ( bNextSentence = GetNextSentence_Impl(bUseSavedSentence, m_xSentenceED->IsUndoEditMode()) && m_xSentenceED->MarkNextError( false, xSpell )))
353  {
354  SpellErrorDescription aSpellErrorDescription;
355  bool bSpellErrorDescription = m_xSentenceED->GetAlternatives(aSpellErrorDescription);
356  if (bSpellErrorDescription)
357  {
359  weld::Widget* aControls[] =
360  {
361  m_xNotInDictFT.get(),
362  m_xSentenceED->GetDrawingArea(),
363  m_xLanguageFT.get(),
364  nullptr
365  };
366  sal_Int32 nIdx = 0;
367  do
368  {
369  aControls[nIdx]->set_sensitive(true);
370  }
371  while(aControls[++nIdx]);
372 
373  }
374  if( bNextSentence )
375  {
376  //remove undo if a new sentence is active
377  m_xSentenceED->ResetUndo();
378  m_xUndoPB->set_sensitive(false);
379  }
380  }
381 }
382 /* Initialize, asynchronous to prevent virtual calls
383  from a constructor
384  */
385 IMPL_LINK_NOARG( SpellDialog, InitHdl, void*, void)
386 {
387  m_xDialog->freeze();
388  //show or hide AutoCorrect depending on the modules abilities
389  m_xAutoCorrPB->set_visible(rParent.HasAutoCorrection());
390  SpellContinue_Impl();
391  m_xSentenceED->ResetUndo();
392  m_xUndoPB->set_sensitive(false);
393 
394  // get current language
395  UpdateBoxes_Impl();
396 
397  // fill dictionary PopupMenu
398  InitUserDicts();
399 
400  LockFocusChanges(true);
401  if( m_xChangePB->get_sensitive() )
402  m_xChangePB->grab_focus();
403  else if( m_xIgnorePB->get_sensitive() )
404  m_xIgnorePB->grab_focus();
405  else if( m_xClosePB->get_sensitive() )
406  m_xClosePB->grab_focus();
407  LockFocusChanges(false);
408  //show grammar CheckBox depending on the modules abilities
409  m_xCheckGrammarCB->set_active(rParent.IsGrammarChecking());
410  m_xDialog->thaw();
411 };
412 
413 IMPL_LINK( SpellDialog, ExtClickHdl, weld::Button&, rBtn, void )
414 {
415  if (m_xOptionsPB.get() == &rBtn)
416  StartSpellOptDlg_Impl();
417  else if (m_xAutoCorrPB.get() == &rBtn)
418  {
419  //get the currently selected wrong word
420  OUString sCurrentErrorText = m_xSentenceED->GetErrorText();
421  //get the wrong word from the XSpellAlternative
422  SpellErrorDescription aSpellErrorDescription;
423  bool bSpellErrorDescription = m_xSentenceED->GetAlternatives(aSpellErrorDescription);
424  if (bSpellErrorDescription)
425  {
426  OUString sWrong(aSpellErrorDescription.sErrorText);
427  //if the word has not been edited in the MultiLineEdit then
428  //the current suggestion should be used
429  //if it's not the 'no suggestions' entry
430  if(sWrong == sCurrentErrorText &&
431  m_xSuggestionLB->get_sensitive() && m_xSuggestionLB->get_selected_index() != -1 &&
432  m_sNoSuggestionsST != m_xSuggestionLB->get_selected_text())
433  {
434  sCurrentErrorText = m_xSuggestionLB->get_selected_text();
435  }
436  if(sWrong != sCurrentErrorText)
437  {
438  SvxPrepareAutoCorrect( sWrong, sCurrentErrorText );
439  LanguageType eLang = GetSelectedLang_Impl();
440  rParent.AddAutoCorrection( sWrong, sCurrentErrorText, eLang );
441  }
442  }
443  }
444 }
445 
446 IMPL_LINK_NOARG(SpellDialog, CheckGrammarHdl, weld::Button&, void)
447 {
448  rParent.SetGrammarChecking(m_xCheckGrammarCB->get_active());
449  Impl_Restore(true);
450 }
451 
453 {
455  SfxSingleTabDialogController aDlg(m_xDialog.get(), &aSet, "cui/ui/spelloptionsdialog.ui", "SpellOptionsDialog");
456 
457  TabPageParent aParent(aDlg.get_content_area(), &aDlg);
458  VclPtr<SfxTabPage> xPage = SvxLinguTabPage::Create(aParent, &aSet);
459  static_cast<SvxLinguTabPage*>(xPage.get())->HideGroups( GROUP_MODULES );
460  aDlg.SetTabPage(xPage);
461  if (RET_OK == aDlg.run())
462  {
463  InitUserDicts();
464  const SfxItemSet* pOutSet = aDlg.GetOutputItemSet();
465  if(pOutSet)
467  }
468 }
469 
470 namespace
471 {
472  OUString getDotReplacementString(const OUString &rErrorText, const OUString &rSuggestedReplacement)
473  {
474  OUString aString = rErrorText;
475 
476  //dots are sometimes part of the spelled word but they are not necessarily part of the replacement
477  bool bDot = aString.endsWith(".");
478 
479  aString = rSuggestedReplacement;
480 
481  if(bDot && (aString.isEmpty() || !aString.endsWith(".")))
482  aString += ".";
483 
484  return aString;
485  }
486 }
487 
489 {
490  OUString sOrigString = m_xSentenceED->GetErrorText();
491 
492  OUString sReplacement(sOrigString);
493 
494  if(m_xSuggestionLB->get_sensitive() &&
495  m_xSuggestionLB->get_selected_index() != -1 &&
496  m_sNoSuggestionsST != m_xSuggestionLB->get_selected_text())
497  sReplacement = m_xSuggestionLB->get_selected_text();
498 
499  return getDotReplacementString(sOrigString, sReplacement);
500 }
501 
502 IMPL_LINK_NOARG(SpellDialog, DoubleClickChangeHdl, weld::TreeView&, void)
503 {
504  ChangeHdl(*m_xChangePB);
505 }
506 
507 IMPL_LINK_NOARG(SpellDialog, ChangeHdl, weld::Button&, void)
508 {
509  if (m_xSentenceED->IsUndoEditMode())
510  {
511  SpellContinue_Impl();
512  }
513  else
514  {
515  m_xSentenceED->UndoActionStart( SPELLUNDO_CHANGE_GROUP );
516  OUString aString = getReplacementString();
517  m_xSentenceED->ChangeMarkedWord(aString, GetSelectedLang_Impl());
518  SpellContinue_Impl();
519  m_xSentenceED->UndoActionEnd();
520  }
521  if(!m_xChangePB->get_sensitive())
522  m_xIgnorePB->grab_focus();
523 }
524 
525 IMPL_LINK_NOARG(SpellDialog, ChangeAllHdl, weld::Button&, void)
526 {
527  m_xSentenceED->UndoActionStart( SPELLUNDO_CHANGE_GROUP );
528  OUString aString = getReplacementString();
529  LanguageType eLang = GetSelectedLang_Impl();
530 
531  // add new word to ChangeAll list
532  OUString aOldWord( m_xSentenceED->GetErrorText() );
533  SvxPrepareAutoCorrect( aOldWord, aString );
534  Reference<XDictionary> aXDictionary = LinguMgr::GetChangeAllList();
535  DictionaryError nAdded = AddEntryToDic( aXDictionary,
536  aOldWord, true,
537  aString );
538 
539  if(nAdded == DictionaryError::NONE)
540  {
541  std::unique_ptr<SpellUndoAction_Impl> pAction(new SpellUndoAction_Impl(
542  SPELLUNDO_CHANGE_ADD_TO_DICTIONARY, aDialogUndoLink));
543  pAction->SetDictionary(aXDictionary);
544  pAction->SetAddedWord(aOldWord);
545  m_xSentenceED->AddUndoAction(std::move(pAction));
546  }
547 
548  m_xSentenceED->ChangeMarkedWord(aString, eLang);
549  SpellContinue_Impl();
550  m_xSentenceED->UndoActionEnd();
551 }
552 
553 IMPL_LINK( SpellDialog, IgnoreAllHdl, weld::Button&, rButton, void )
554 {
555  m_xSentenceED->UndoActionStart( SPELLUNDO_CHANGE_GROUP );
556  // add word to IgnoreAll list
557  Reference< XDictionary > aXDictionary = LinguMgr::GetIgnoreAllList();
558  //in case the error has been changed manually it has to be restored
559  m_xSentenceED->RestoreCurrentError();
560  if (&rButton == m_xIgnoreRulePB.get())
561  {
562  SpellErrorDescription aSpellErrorDescription;
563  bool bSpellErrorDescription = m_xSentenceED->GetAlternatives(aSpellErrorDescription);
564  try
565  {
566  if( bSpellErrorDescription && aSpellErrorDescription.xGrammarChecker.is() )
567  {
568  aSpellErrorDescription.xGrammarChecker->ignoreRule(aSpellErrorDescription.sRuleId,
569  aSpellErrorDescription.aLocale);
570  // refresh the layout (workaround to launch a dictionary event)
571  aXDictionary->setActive(false);
572  aXDictionary->setActive(true);
573  }
574  }
575  catch( const uno::Exception& )
576  {
577  }
578  }
579  else
580  {
581  OUString sErrorText(m_xSentenceED->GetErrorText());
582  DictionaryError nAdded = AddEntryToDic( aXDictionary,
583  sErrorText, false,
584  OUString() );
585  if (nAdded == DictionaryError::NONE)
586  {
587  std::unique_ptr<SpellUndoAction_Impl> pAction(new SpellUndoAction_Impl(
588  SPELLUNDO_CHANGE_ADD_TO_DICTIONARY, aDialogUndoLink));
589  pAction->SetDictionary(aXDictionary);
590  pAction->SetAddedWord(sErrorText);
591  m_xSentenceED->AddUndoAction(std::move(pAction));
592  }
593  }
594 
595  SpellContinue_Impl();
596  m_xSentenceED->UndoActionEnd();
597 }
598 
599 IMPL_LINK_NOARG(SpellDialog, UndoHdl, weld::Button&, void)
600 {
601  m_xSentenceED->Undo();
602  if(!m_xSentenceED->GetUndoActionCount())
603  m_xUndoPB->set_sensitive(false);
604 }
605 
606 
607 IMPL_LINK( SpellDialog, DialogUndoHdl, SpellUndoAction_Impl&, rAction, void )
608 {
609  switch(rAction.GetId())
610  {
612  {
613  if(rAction.IsEnableChangePB())
614  m_xChangePB->set_sensitive(false);
615  if(rAction.IsEnableChangeAllPB())
616  m_xChangeAllPB->set_sensitive(false);
617  }
618  break;
620  {
621  m_xSentenceED->MoveErrorMarkTo(static_cast<sal_Int32>(rAction.GetOldErrorStart()),
622  static_cast<sal_Int32>(rAction.GetOldErrorEnd()),
623  false);
624  if(rAction.IsErrorLanguageSelected())
625  {
626  UpdateBoxes_Impl();
627  }
628  }
629  break;
631  {
632  if(rAction.GetDictionary().is())
633  rAction.GetDictionary()->remove(rAction.GetAddedWord());
634  }
635  break;
637  {
638  if(rAction.GetOffset() != 0)
639  m_xSentenceED->MoveErrorEnd(rAction.GetOffset());
640  }
641  break;
643  {
644  //refill the dialog with the currently spelled sentence - throw away all changes
645  SpellContinue_Impl(true);
646  }
647  break;
649  //undo of ignored rules is not supported
650  break;
651  }
652 }
653 
654 void SpellDialog::Impl_Restore(bool bUseSavedSentence)
655 {
656  //clear the "ChangeAllList"
657  LinguMgr::GetChangeAllList()->clear();
658  //get a new sentence
659  m_xSentenceED->SetText(OUString());
660  m_xSentenceED->ResetModified();
661  //Resolves: fdo#39348 refill the dialog with the currently spelled sentence
662  SpellContinue_Impl(bUseSavedSentence);
663  m_xIgnorePB->set_label(m_sIgnoreOnceST);
664 }
665 
666 IMPL_LINK_NOARG(SpellDialog, IgnoreHdl, weld::Button&, void)
667 {
668  if (m_sResumeST == m_xIgnorePB->get_label())
669  {
670  Impl_Restore(false);
671  }
672  else
673  {
674  //in case the error has been changed manually it has to be restored,
675  // since the users choice now was to ignore the error
676  m_xSentenceED->RestoreCurrentError();
677 
678  // the word is being ignored
679  SpellContinue_Impl( false, true );
680  }
681 }
682 
684 {
685  if (IsClosing())
686  return;
687 
688  // We have to call ToggleChildWindow directly; calling SfxDispatcher's
689  // Execute() does not work here when we are in a document with protected
690  // section - in that case, the cursor can move from the editable field to
691  // the protected area, and the slots get disabled because of
692  // SfxDisableFlags::SwOnProtectedCursor (see FN_SPELL_GRAMMAR_DIALOG in .sdi).
693  SfxViewFrame* pViewFrame = SfxViewFrame::Current();
694  if (pViewFrame)
695  pViewFrame->ToggleChildWindow(rParent.GetType());
696 }
697 
699 {
700  LanguageType nLang = m_xLanguageLB->get_active_id();
701  return nLang;
702 }
703 
704 IMPL_LINK_NOARG(SpellDialog, LanguageSelectHdl, weld::ComboBox&, void)
705 {
706  //If selected language changes, then add->list should be regenerated to
707  //match
708  InitUserDicts();
709 
710  //if currently an error is selected then search for alternatives for
711  //this word and fill the alternatives ListBox accordingly
712  OUString sError = m_xSentenceED->GetErrorText();
713  m_xSuggestionLB->clear();
714  if (!sError.isEmpty())
715  {
716  LanguageType eLanguage = m_xLanguageLB->get_active_id();
717  Reference <XSpellAlternatives> xAlt = xSpell->spell( sError, static_cast<sal_uInt16>(eLanguage),
719  if( xAlt.is() )
720  m_xSentenceED->SetAlternatives( xAlt );
721  else
722  {
723  m_xSentenceED->ChangeMarkedWord( sError, eLanguage );
724  SpellContinue_Impl();
725  }
726 
727  m_xSentenceED->AddUndoAction(std::make_unique<SpellUndoAction_Impl>(SPELLUNDO_CHANGE_LANGUAGE, aDialogUndoLink));
728  }
730 }
731 
733 {
735  sTitle = sTitle.replaceFirst( "$LANGUAGE ($LOCATION)", SvtLanguageTable::GetLanguageString(nLang) );
736  m_xDialog->set_title(m_xDialog->strip_mnemonic(sTitle));
737 }
738 
740 {
741  const LanguageType nLang = m_xLanguageLB->get_active_id();
742 
743  const Reference< XDictionary > *pDic = nullptr;
744 
745  // get list of dictionaries
746  Reference< XSearchableDictionaryList > xDicList( LinguMgr::GetDictionaryList() );
747  if (xDicList.is())
748  {
749  // add active, positive dictionary to dic-list (if not already done).
750  // This is to ensure that there is at least on dictionary to which
751  // words could be added.
752  Reference< XDictionary > xDic( LinguMgr::GetStandardDic() );
753  if (xDic.is())
754  xDic->setActive( true );
755 
756  pImpl->aDics = xDicList->getDictionaries();
757  }
758 
759  SvtLinguConfig aCfg;
760 
761  // list suitable dictionaries
762  bool bEnable = false;
763  const sal_Int32 nSize = pImpl->aDics.getLength();
764  pDic = pImpl->aDics.getConstArray();
765  m_xAddToDictMB->clear();
766  sal_uInt16 nItemId = 1; // menu items should be enumerated from 1 and not 0
767  for (sal_Int32 i = 0; i < nSize; ++i)
768  {
769  uno::Reference< linguistic2::XDictionary > xDicTmp = pDic[i];
770  if (!xDicTmp.is() || LinguMgr::GetIgnoreAllList() == xDicTmp)
771  continue;
772 
773  uno::Reference< frame::XStorable > xStor( xDicTmp, uno::UNO_QUERY );
774  LanguageType nActLanguage = LanguageTag( xDicTmp->getLocale() ).getLanguageType();
775  if( xDicTmp->isActive()
776  && xDicTmp->getDictionaryType() != linguistic2::DictionaryType_NEGATIVE
777  && (nLang == nActLanguage || LANGUAGE_NONE == nActLanguage )
778  && (!xStor.is() || !xStor->isReadonly()) )
779  {
780  bEnable = true;
781 
782  OUString aDictionaryImageUrl;
783  uno::Reference< lang::XServiceInfo > xSvcInfo( xDicTmp, uno::UNO_QUERY );
784  if (xSvcInfo.is())
785  {
786  aDictionaryImageUrl = aCfg.GetSpellAndGrammarContextDictionaryImage(
787  xSvcInfo->getImplementationName());
788  }
789 
790  m_xAddToDictMB->append_item(OUString::number(nItemId), xDicTmp->getName(), aDictionaryImageUrl);
791 
792  ++nItemId;
793  }
794  }
795  m_xAddToDictMB->set_sensitive( bEnable );
796  m_xAddToDictPB->set_sensitive( bEnable );
797 
798  int nDicts = nItemId-1;
799 
800  m_xAddToDictMB->set_visible( nDicts > 1 );
801  m_xAddToDictPB->set_visible( nDicts <= 1 );
802 
803  return nDicts;
804 }
805 
806 IMPL_LINK_NOARG(SpellDialog, AddToDictClickHdl, weld::Button&, void)
807 {
808  AddToDictionaryExecute(OString::number(1));
809 }
810 
811 IMPL_LINK(SpellDialog, AddToDictSelectHdl, const OString&, rIdent, void)
812 {
813  AddToDictionaryExecute(rIdent);
814 }
815 
816 void SpellDialog::AddToDictionaryExecute(const OString& rItemId)
817 {
818  m_xSentenceED->UndoActionStart( SPELLUNDO_CHANGE_GROUP );
819 
820  //GetErrorText() returns the current error even if the text is already
821  //manually changed
822  const OUString aNewWord = m_xSentenceED->GetErrorText();
823 
824  OUString aDicName(m_xAddToDictMB->get_item_label(rItemId));
825 
826  uno::Reference< linguistic2::XDictionary > xDic;
827  uno::Reference< linguistic2::XSearchableDictionaryList > xDicList( LinguMgr::GetDictionaryList() );
828  if (xDicList.is())
829  xDic = xDicList->getDictionaryByName( aDicName );
830 
831  DictionaryError nAddRes = DictionaryError::UNKNOWN;
832  if (xDic.is())
833  {
834  nAddRes = AddEntryToDic( xDic, aNewWord, false, OUString() );
835  // save modified user-dictionary if it is persistent
836  uno::Reference< frame::XStorable > xSavDic( xDic, uno::UNO_QUERY );
837  if (xSavDic.is())
838  xSavDic->store();
839 
840  if (nAddRes == DictionaryError::NONE)
841  {
842  std::unique_ptr<SpellUndoAction_Impl> pAction(new SpellUndoAction_Impl(
844  pAction->SetDictionary( xDic );
845  pAction->SetAddedWord( aNewWord );
846  m_xSentenceED->AddUndoAction( std::move(pAction) );
847  }
848  // failed because there is already an entry?
849  if (DictionaryError::NONE != nAddRes && xDic->getEntry( aNewWord ).is())
850  nAddRes = DictionaryError::NONE;
851  }
852  if (DictionaryError::NONE != nAddRes)
853  {
854  SvxDicError(m_xDialog.get(), nAddRes);
855  return; // don't continue
856  }
857 
858  // go on
860  m_xSentenceED->UndoActionEnd();
861 }
862 
863 IMPL_LINK_NOARG(SpellDialog, ModifyHdl, LinkParamNone*, void)
864 {
865  m_xSuggestionLB->unselect_all();
866  m_xSuggestionLB->set_sensitive(false);
867  m_xAutoCorrPB->set_sensitive(false);
868  std::unique_ptr<SpellUndoAction_Impl> pSpellAction(new SpellUndoAction_Impl(SPELLUNDO_CHANGE_TEXTENGINE, aDialogUndoLink));
869  if(!m_xChangeAllPB->get_sensitive())
870  {
871  m_xChangeAllPB->set_sensitive(true);
872  pSpellAction->SetEnableChangeAllPB();
873  }
874  if(!m_xChangePB->get_sensitive())
875  {
876  m_xChangePB->set_sensitive(true);
877  pSpellAction->SetEnableChangePB();
878  }
879  m_xSentenceED->AddUndoAction(std::move(pSpellAction));
880 }
881 
882 IMPL_LINK_NOARG(SpellDialog, CancelHdl, weld::Button&, void)
883 {
884  //apply changes and ignored text parts first - if there are any
885  rParent.ApplyChangedSentence(m_xSentenceED->CreateSpellPortions(), false);
886  Close();
887 }
888 
890 {
891  /* #i38338#
892  * FIXME: LoseFocus and GetFocus are signals from vcl that
893  * a window actually got/lost the focus, it never should be
894  * forwarded from another window, that is simply wrong.
895  * FIXME: overriding the virtual methods GetFocus and LoseFocus
896  * in SpellDialogChildWindow by making them pure is at least questionable.
897  * The only sensible thing would be to call the new Method differently,
898  * e.g. DialogGot/LostFocus or so.
899  */
900  if (m_xDialog->get_visible() && !bFocusLocked)
901  {
902  if (m_xDialog->has_toplevel_focus())
903  {
904  //notify the child window of the focus change
905  rParent.GetFocus();
906  }
907  else
908  {
909  //notify the child window of the focus change
910  rParent.LoseFocus();
911  }
912  }
913 }
914 
916 {
919 }
920 
922 {
925 }
926 
928 {
929  if( bFocusLocked )
930  return;
931  m_xIgnorePB->set_label(m_sResumeST);
932  weld::Widget* aDisableArr[] =
933  {
934  m_xNotInDictFT.get(),
935  m_xSentenceED->GetDrawingArea(),
936  m_xSuggestionFT.get(),
937  m_xSuggestionLB.get(),
938  m_xLanguageFT.get(),
939  m_xLanguageLB->get_widget(),
940  m_xIgnoreAllPB.get(),
941  m_xIgnoreRulePB.get(),
942  m_xAddToDictMB.get(),
943  m_xAddToDictPB.get(),
944  m_xChangePB.get(),
945  m_xChangeAllPB.get(),
946  m_xAutoCorrPB.get(),
947  m_xUndoPB.get(),
948  nullptr
949  };
950  sal_Int16 i = 0;
951  while(aDisableArr[i])
952  {
953  aDisableArr[i]->set_sensitive(false);
954  i++;
955  }
957 }
958 
959 bool SpellDialog::GetNextSentence_Impl(bool bUseSavedSentence, bool bRecheck)
960 {
961  bool bRet = false;
962  if(!bUseSavedSentence)
963  {
964  //apply changes and ignored text parts
965  rParent.ApplyChangedSentence(m_xSentenceED->CreateSpellPortions(), bRecheck);
966  }
967  m_xSentenceED->ResetIgnoreErrorsAt();
968  m_xSentenceED->ResetModified();
969  SpellPortions aSentence = bUseSavedSentence ? m_aSavedSentence : rParent.GetNextWrongSentence( bRecheck );
970  if(!bUseSavedSentence)
971  m_aSavedSentence = aSentence;
972  bool bHasReplaced = false;
973  while(!aSentence.empty())
974  {
975  //apply all changes that are already part of the "ChangeAllList"
976  //returns true if the list still contains errors after the changes have been applied
977 
978  if(!ApplyChangeAllList_Impl(aSentence, bHasReplaced))
979  {
980  rParent.ApplyChangedSentence(aSentence, bRecheck);
981  aSentence = rParent.GetNextWrongSentence( bRecheck );
982  }
983  else
984  break;
985  }
986 
987  if(!aSentence.empty())
988  {
989  OUStringBuffer sText;
990  for (auto const& elem : aSentence)
991  {
992  // hidden text has to be ignored
993  if(!elem.bIsHidden)
994  sText.append(elem.sText);
995  }
996  m_xSentenceED->SetText(sText.makeStringAndClear());
997  sal_Int32 nStartPosition = 0;
998  sal_Int32 nEndPosition = 0;
999 
1000  for (auto const& elem : aSentence)
1001  {
1002  // hidden text has to be ignored
1003  if(!elem.bIsHidden)
1004  {
1005  nEndPosition += elem.sText.getLength();
1006  if(elem.xAlternatives.is())
1007  {
1008  uno::Reference< container::XNamed > xNamed( elem.xAlternatives, uno::UNO_QUERY );
1009  OUString sServiceName;
1010  if( xNamed.is() )
1011  sServiceName = xNamed->getName();
1012  SpellErrorDescription aDesc( false, elem.xAlternatives->getWord(),
1013  elem.xAlternatives->getLocale(), elem.xAlternatives->getAlternatives(), nullptr);
1014  SfxGrabBagItem aSpellErrorDescription(EE_CHAR_GRABBAG);
1015  aSpellErrorDescription.GetGrabBag()["SpellErrorDescription"] <<= aDesc.toSequence();
1016  m_xSentenceED->SetAttrib(aSpellErrorDescription, nStartPosition, nEndPosition);
1017  }
1018  else if(elem.bIsGrammarError )
1019  {
1020  beans::PropertyValues aProperties = elem.aGrammarError.aProperties;
1021  OUString sFullCommentURL;
1022  sal_Int32 i = 0;
1023  while ( sFullCommentURL.isEmpty() && i < aProperties.getLength() )
1024  {
1025  if ( aProperties[i].Name == "FullCommentURL" )
1026  {
1027  uno::Any aValue = aProperties[i].Value;
1028  aValue >>= sFullCommentURL;
1029  }
1030  ++i;
1031  }
1032 
1033  SpellErrorDescription aDesc( true,
1034  elem.sText,
1035  LanguageTag::convertToLocale( elem.eLanguage ),
1036  elem.aGrammarError.aSuggestions,
1037  elem.xGrammarChecker,
1038  &elem.sDialogTitle,
1039  &elem.aGrammarError.aFullComment,
1040  &elem.aGrammarError.aRuleIdentifier,
1041  &sFullCommentURL );
1042 
1043  SfxGrabBagItem aSpellErrorDescriptionItem(EE_CHAR_GRABBAG);
1044  aSpellErrorDescriptionItem.GetGrabBag()["SpellErrorDescription"] <<= aDesc.toSequence();
1045  m_xSentenceED->SetAttrib(aSpellErrorDescriptionItem, nStartPosition, nEndPosition);
1046  }
1047 
1048  if (elem.bIsField)
1049  m_xSentenceED->SetAttrib(SvxBackgroundColorItem(COL_LIGHTGRAY, EE_CHAR_BKGCOLOR), nStartPosition, nEndPosition);
1050  m_xSentenceED->SetAttrib(SvxLanguageItem(elem.eLanguage, EE_CHAR_LANGUAGE), nStartPosition, nEndPosition);
1051  nStartPosition = nEndPosition;
1052  }
1053  }
1054  //the edit field needs to be modified to apply the change from the ApplyChangeAllList
1055  if(!bHasReplaced)
1056  m_xSentenceED->ClearModifyFlag();
1057  m_xSentenceED->ResetUndo();
1058  m_xUndoPB->set_sensitive(false);
1059  bRet = nStartPosition > 0;
1060  }
1061  return bRet;
1062 }
1063 /*-------------------------------------------------------------------------
1064  replace errors that have a replacement in the ChangeAllList
1065  returns false if the result doesn't contain errors after the replacement
1066  -----------------------------------------------------------------------*/
1067 bool SpellDialog::ApplyChangeAllList_Impl(SpellPortions& rSentence, bool &bHasReplaced)
1068 {
1069  bHasReplaced = false;
1070  bool bRet = true;
1071  Reference<XDictionary> xChangeAll = LinguMgr::GetChangeAllList();
1072  if(!xChangeAll->getCount())
1073  return bRet;
1074  bRet = false;
1075  for (auto & elem : rSentence)
1076  {
1077  if(elem.xAlternatives.is())
1078  {
1079  const OUString &rString = elem.sText;
1080 
1081  Reference<XDictionaryEntry> xEntry = xChangeAll->getEntry(rString);
1082 
1083  if(xEntry.is())
1084  {
1085  elem.sText = getDotReplacementString(rString, xEntry->getReplacementText());
1086  elem.xAlternatives = nullptr;
1087  bHasReplaced = true;
1088  }
1089  else
1090  bRet = true;
1091  }
1092  else if( elem.bIsGrammarError )
1093  bRet = true;
1094  }
1095  return bRet;
1096 }
1097 
1099  : m_pSpellDialog(nullptr)
1100  , m_pToolbar(nullptr)
1101  , m_nErrorStart(0)
1102  , m_nErrorEnd(0)
1103  , m_bIsUndoEditMode(false)
1104 {
1105 }
1106 
1108 {
1109  Size aSize(pDrawingArea->get_approximate_digit_width() * 60,
1110  pDrawingArea->get_text_height() * 6);
1111  pDrawingArea->set_size_request(aSize.Width(), aSize.Height());
1112  WeldEditView::SetDrawingArea(pDrawingArea);
1113 }
1114 
1116 {
1117 }
1118 
1119 namespace
1120 {
1121  const EECharAttrib* FindCharAttrib(int nStartPosition, int nEndPosition, sal_uInt16 nWhich, std::vector<EECharAttrib>& rAttribList)
1122  {
1123  for (const auto& rTextAtr : rAttribList)
1124  {
1125  if (rTextAtr.pAttr->Which() != nWhich)
1126  continue;
1127  if (rTextAtr.nStart <= nStartPosition && rTextAtr.nEnd >= nEndPosition)
1128  {
1129  return &rTextAtr;
1130  }
1131  }
1132 
1133  return nullptr;
1134  }
1135 
1136  void ExtractErrorDescription(const EECharAttrib& rEECharAttrib, SpellErrorDescription& rSpellErrorDescription)
1137  {
1138  css::uno::Sequence<css::uno::Any> aSequence;
1139  static_cast<const SfxGrabBagItem*>(rEECharAttrib.pAttr)->GetGrabBag().find("SpellErrorDescription")->second >>= aSequence;
1140  rSpellErrorDescription.fromSequence(aSequence);
1141  }
1142 }
1143 
1144 /*-------------------------------------------------------------------------
1145  The selection before inputting a key may have a range or not
1146  and it may be inside or outside of field or error attributes.
1147  A range may include the attribute partially, completely or together
1148  with surrounding text. It may also contain more than one attribute
1149  or no attribute at all.
1150  Depending on this starting conditions some actions are necessary:
1151  Attempts to delete a field are only allowed if the selection is the same
1152  as the field's selection. Otherwise the field has to be selected and the key
1153  input action has to be skipped.
1154  Input of text at the start of the field requires the field attribute to be
1155  corrected - it is not allowed to grow.
1156 
1157  In case of errors the appending of text should grow the error attribute because
1158  that is what the user usually wants to do.
1159 
1160  Backspace at the start of the attribute requires to find out if a field ends
1161  directly in front of the cursor position. In case of a field this attribute has to be
1162  selected otherwise the key input method is allowed.
1163 
1164  All changes outside of the error attributes switch the dialog mode to a "Undo edit" state that
1165  removes all visible attributes and switches off further attribute checks.
1166  Undo in this restarts the dialog with a current sentence newly presented.
1167  All changes to the sentence are undone including the ones before the "Undo edit state" has been reached
1168 
1169  We end up with 9 types of selection
1170  1 (LEFT_NO) - no range, start of attribute - can also be 3 at the same time
1171  2 (INSIDE_NO) - no range, inside of attribute
1172  3 (RIGHT_NO) - no range, end of attribute - can also be 1 at the same time
1173  4 (FULL) - range, same as attribute
1174  5 (INSIDE_YES) - range, inside of the attribute
1175  6 (BRACE)- range, from outside of the attribute to the inside or
1176  including the complete attribute and something outside,
1177  maybe more than one attribute
1178  7 (OUTSIDE_NO) - no range, not at an attribute
1179  8 (OUTSIDE_YES) - range, completely outside of all attributes
1180 
1181  What has to be done depending on the attribute type involved
1182  possible actions: UE - Undo edit mode
1183  CO - Continue, no additional action is required
1184  FS - Field has to be completely selected
1185  EX - The attribute has to be expanded to include the added text
1186 
1187  1 - backspace delete any other
1188  UE on field FS on error CO on field FS on error CO
1189 
1190  2 - on field FS on error C
1191  3 - backspace delete any other
1192  on field FS on error CO UE on field UE on error EX
1193 
1194  if 1 and 3 happen to apply both then backspace and other handling is 1 delete is 3
1195 
1196  4 - on field UE and on error CO
1197  5 - on field FS and on error CO
1198  6 - on field FS and on error UE
1199  7 - UE
1200  8 - UE
1201  -----------------------------------------------------------------------*/
1202 #define INVALID 0
1203 #define LEFT_NO 1
1204 #define INSIDE_NO 2
1205 #define RIGHT_NO 3
1206 #define FULL 4
1207 #define INSIDE_YES 5
1208 #define BRACE 6
1209 #define OUTSIDE_NO 7
1210 #define OUTSIDE_YES 8
1211 
1212 #define ACTION_UNDOEDIT 0
1213 #define ACTION_CONTINUE 1
1214 #define ACTION_SELECTFIELD 2
1215 #define ACTION_EXPAND 3
1216 
1218 {
1219  if (rKeyEvt.GetKeyCode().GetCode() == KEY_TAB)
1220  return false;
1221 
1222  bool bConsumed = false;
1223 
1224  bool bChange = TextEngine::DoesKeyChangeText( rKeyEvt );
1225  if (bChange && !IsUndoEditMode())
1226  {
1227  bConsumed = true;
1228 
1229  ESelection aCurrentSelection(m_xEditView->GetSelection());
1230  aCurrentSelection.Adjust();
1231 
1232  //determine if the selection contains a field
1233  bool bHasFieldLeft = false;
1234  bool bHasErrorLeft = false;
1235 
1236  bool bHasRange = aCurrentSelection.HasRange();
1237  sal_uInt8 nSelectionType = 0; // invalid type!
1238 
1239  std::vector<EECharAttrib> aAttribList;
1240  m_xEditEngine->GetCharAttribs(0, aAttribList);
1241 
1242  auto nCursor = aCurrentSelection.nStartPos;
1243  const EECharAttrib* pBackAttr = FindCharAttrib(nCursor, nCursor, EE_CHAR_BKGCOLOR, aAttribList);
1244  const EECharAttrib* pErrorAttr = FindCharAttrib(nCursor, nCursor, EE_CHAR_GRABBAG, aAttribList);
1245  const EECharAttrib* pBackAttrLeft = nullptr;
1246  const EECharAttrib* pErrorAttrLeft = nullptr;
1247 
1248  bool bHasField = pBackAttr != nullptr && (bHasRange || pBackAttr->nEnd > nCursor);
1249  bool bHasError = pErrorAttr != nullptr && (bHasRange || pErrorAttr->nEnd > nCursor);
1250  if (bHasRange)
1251  {
1252  if (pBackAttr &&
1253  pBackAttr->nStart == aCurrentSelection.nStartPos &&
1254  pBackAttr->nEnd == aCurrentSelection.nEndPos)
1255  {
1256  nSelectionType = FULL;
1257  }
1258  else if (pErrorAttr &&
1259  pErrorAttr->nStart <= aCurrentSelection.nStartPos &&
1260  pErrorAttr->nEnd >= aCurrentSelection.nEndPos)
1261  {
1262  nSelectionType = INSIDE_YES;
1263  }
1264  else
1265  {
1266  nSelectionType = bHasField||bHasError ? BRACE : OUTSIDE_NO;
1267  while (nCursor < aCurrentSelection.nEndPos)
1268  {
1269  ++nCursor;
1270  const EECharAttrib* pIntBackAttr = FindCharAttrib(nCursor, nCursor, EE_CHAR_BKGCOLOR, aAttribList);
1271  const EECharAttrib* pIntErrorAttr = FindCharAttrib(nCursor, nCursor, EE_CHAR_GRABBAG, aAttribList);
1272  //if any attr has been found then BRACE
1273  if (pIntBackAttr || pIntErrorAttr)
1274  nSelectionType = BRACE;
1275  //the field has to be selected
1276  if (pIntBackAttr && !pBackAttr)
1277  pBackAttr = pIntBackAttr;
1278  bHasField |= pIntBackAttr != nullptr;
1279  }
1280  }
1281  }
1282  else
1283  {
1284  //no range selection: then 1 2 3 and 8 are possible
1285  const EECharAttrib* pCurAttr = pBackAttr ? pBackAttr : pErrorAttr;
1286  if (pCurAttr)
1287  {
1288  nSelectionType = pCurAttr->nStart == aCurrentSelection.nStartPos ?
1289  LEFT_NO : pCurAttr->nEnd == aCurrentSelection.nEndPos ? RIGHT_NO : INSIDE_NO;
1290  }
1291  else
1292  nSelectionType = OUTSIDE_NO;
1293 
1294  bHasFieldLeft = pBackAttr && pBackAttr->nEnd == nCursor;
1295  if(bHasFieldLeft)
1296  {
1297  pBackAttrLeft = pBackAttr;
1298  pBackAttr = nullptr;
1299  }
1300  bHasErrorLeft = pErrorAttr && pErrorAttr->nEnd == nCursor;
1301  if(bHasErrorLeft)
1302  {
1303  pErrorAttrLeft = pErrorAttr;
1304  pErrorAttr = nullptr;
1305  }
1306 
1307  //check previous position if this exists
1308  //that is a redundant in the case the attribute found above already is on the left cursor side
1309  //but it's o.k. for two errors/fields side by side
1310  if (nCursor)
1311  {
1312  --nCursor;
1313  pBackAttrLeft = FindCharAttrib(nCursor, nCursor, EE_CHAR_BKGCOLOR, aAttribList);
1314  pErrorAttrLeft = FindCharAttrib(nCursor, nCursor, EE_CHAR_GRABBAG, aAttribList);
1315  bHasFieldLeft = pBackAttrLeft !=nullptr;
1316  bHasErrorLeft = pErrorAttrLeft != nullptr;
1317  ++nCursor;
1318  }
1319  }
1320  //Here we have to determine if the error found is the one currently active
1321  bool bIsErrorActive = (pErrorAttr && pErrorAttr->nStart == m_nErrorStart) ||
1322  (pErrorAttrLeft && pErrorAttrLeft->nStart == m_nErrorStart);
1323 
1324  SAL_WARN_IF(
1325  nSelectionType == INVALID, "cui.dialogs",
1326  "selection type not set");
1327 
1328  const vcl::KeyCode& rKeyCode = rKeyEvt.GetKeyCode();
1329  bool bDelete = rKeyCode.GetCode() == KEY_DELETE;
1330  bool bBackspace = rKeyCode.GetCode() == KEY_BACKSPACE;
1331 
1332  sal_Int8 nAction = ACTION_CONTINUE;
1333  switch(nSelectionType)
1334  {
1335 // 1 - backspace delete any other
1336 // UE on field FS on error CO on field FS on error CO
1337  case LEFT_NO :
1338  if(bBackspace)
1339  {
1340  nAction = bHasFieldLeft ? ACTION_SELECTFIELD : ACTION_UNDOEDIT;
1341  //to force the use of pBackAttrLeft
1342  pBackAttr = nullptr;
1343  }
1344  else if(bDelete)
1345  nAction = bHasField ? ACTION_SELECTFIELD : ACTION_CONTINUE;
1346  else
1347  nAction = bHasError && !nCursor ? ACTION_CONTINUE :
1348  bHasError ? ACTION_EXPAND : bHasErrorLeft ? ACTION_CONTINUE : ACTION_UNDOEDIT;
1349  break;
1350 // 2 - on field FS on error C
1351  case INSIDE_NO :
1352  nAction = bHasField ? ACTION_SELECTFIELD :
1353  bIsErrorActive ? ACTION_CONTINUE : ACTION_UNDOEDIT;
1354  break;
1355 // 3 - backspace delete any other
1356 // on field FS on error CO UE on field UE on error EX
1357  case RIGHT_NO :
1358  if(bBackspace)
1359  nAction = bHasFieldLeft ? ACTION_SELECTFIELD : ACTION_CONTINUE;
1360  else if(bDelete)
1361  nAction = bHasFieldLeft && bHasError ? ACTION_CONTINUE : ACTION_UNDOEDIT;
1362  else
1363  nAction = bHasFieldLeft && bHasError ? ACTION_EXPAND :
1364  bHasError ? ACTION_CONTINUE : bHasErrorLeft ? ACTION_EXPAND :ACTION_UNDOEDIT;
1365  break;
1366 // 4 - on field UE and on error CO
1367  case FULL :
1368  nAction = ACTION_UNDOEDIT;
1369  break;
1370 // 5 - on field FS and on error CO
1371  case INSIDE_YES :
1372  nAction = bHasField ? ACTION_SELECTFIELD : ACTION_CONTINUE;
1373  break;
1374 // 6 - on field FS and on error UE
1375  case BRACE :
1376  nAction = bHasField ? ACTION_SELECTFIELD : ACTION_UNDOEDIT;
1377  break;
1378 // 7 - UE
1379 // 8 - UE
1380  case OUTSIDE_NO :
1381  case OUTSIDE_YES:
1382  nAction = ACTION_UNDOEDIT;
1383  break;
1384  }
1385  //save the current paragraph
1386  sal_Int32 nCurrentLen = m_xEditEngine->GetText().getLength();
1387  if (nAction != ACTION_SELECTFIELD)
1388  {
1389  m_xEditView->PostKeyEvent(rKeyEvt);
1390  }
1391  else
1392  {
1393  const EECharAttrib* pCharAttr = pBackAttr ? pBackAttr : pBackAttrLeft;
1394  if (pCharAttr)
1395  m_xEditView->SetSelection(ESelection(0, pCharAttr->nStart, 0, pCharAttr->nEnd));
1396  }
1397  if(nAction == ACTION_EXPAND)
1398  {
1399  DBG_ASSERT(pErrorAttrLeft || pErrorAttr, "where is the error");
1400  //text has been added on the right and only the 'error attribute has to be corrected
1401  if (pErrorAttrLeft)
1402  {
1403  SpellErrorDescription aSpellErrorDescription;
1404  ExtractErrorDescription(*pErrorAttrLeft, aSpellErrorDescription);
1405 
1406  std::unique_ptr<SfxPoolItem> xNewError(pErrorAttrLeft->pAttr->Clone());
1407  sal_Int32 nStart = pErrorAttrLeft->nStart;
1408  sal_Int32 nEnd = pErrorAttrLeft->nEnd + 1;
1409  m_xEditEngine->RemoveAttribs(ESelection(0, nStart, 0, nEnd), false, EE_CHAR_GRABBAG);
1410  SetAttrib(*xNewError, nStart, nEnd);
1411  //only active errors move the mark
1412  if (bIsErrorActive)
1413  {
1414  bool bGrammar = aSpellErrorDescription.bIsGrammarError;
1415  MoveErrorMarkTo(nStart, nEnd, bGrammar);
1416  }
1417  }
1418  //text has been added on the left then the error attribute has to be expanded and the
1419  //field attribute on the right - if any - has to be contracted
1420  else if (pErrorAttr)
1421  {
1422  SpellErrorDescription aSpellErrorDescription;
1423  ExtractErrorDescription(*pErrorAttr, aSpellErrorDescription);
1424 
1425  //determine the change
1426  sal_Int32 nAddedChars = m_xEditEngine->GetText().getLength() - nCurrentLen;
1427 
1428  std::unique_ptr<SfxPoolItem> xNewError(pErrorAttr->pAttr->Clone());
1429  sal_Int32 nStart = pErrorAttr->nStart + nAddedChars;
1430  sal_Int32 nEnd = pErrorAttr->nEnd + nAddedChars;
1431  m_xEditEngine->RemoveAttribs(ESelection(0, nStart, 0, nEnd), false, EE_CHAR_GRABBAG);
1432  nStart = pErrorAttr->nStart;
1433  SetAttrib(*xNewError, nStart, nEnd);
1434  //only if the error is active the mark is moved here
1435  if (bIsErrorActive)
1436  {
1437  bool bGrammar = aSpellErrorDescription.bIsGrammarError;
1438  MoveErrorMarkTo(nStart, nEnd, bGrammar);
1439  }
1440  xNewError.reset();
1441 
1442  if (pBackAttrLeft)
1443  {
1444  std::unique_ptr<SfxPoolItem> xNewBack(pBackAttrLeft->pAttr->Clone());
1445  sal_Int32 _nStart = pBackAttrLeft->nStart + nAddedChars;
1446  sal_Int32 _nEnd = pBackAttrLeft->nEnd + nAddedChars;
1447  m_xEditEngine->RemoveAttribs(ESelection(0, _nStart, 0, _nEnd), false, EE_CHAR_BKGCOLOR);
1448  _nStart = pBackAttrLeft->nStart;
1449  SetAttrib(*xNewBack, _nStart, _nEnd);
1450  }
1451  }
1452  }
1453  else if(nAction == ACTION_UNDOEDIT)
1454  {
1455  SetUndoEditMode(true);
1456  }
1457  //make sure the error positions are correct after text changes
1458  //the old attribute may have been deleted
1459  //all changes inside of the current error leave the error attribute at the current
1460  //start position
1461  if (!IsUndoEditMode() && bIsErrorActive)
1462  {
1463  const EECharAttrib* pFontColor = FindCharAttrib(nCursor, nCursor, EE_CHAR_COLOR, aAttribList);
1464  const EECharAttrib* pErrorAttrib = FindCharAttrib(m_nErrorStart, m_nErrorStart, EE_CHAR_GRABBAG, aAttribList);
1465  if (pFontColor && pErrorAttrib)
1466  {
1467  m_nErrorStart = pFontColor->nStart;
1468  m_nErrorEnd = pFontColor->nEnd;
1469  if (pErrorAttrib->nStart != m_nErrorStart || pErrorAttrib->nEnd != m_nErrorEnd)
1470  {
1471  std::unique_ptr<SfxPoolItem> xNewError(pErrorAttrib->pAttr->Clone());
1472  assert(pErrorAttr);
1473  m_xEditEngine->RemoveAttribs(ESelection(0, pErrorAttr->nStart, 0, pErrorAttr->nEnd), false, EE_CHAR_GRABBAG);
1474  SetAttrib(*xNewError, m_nErrorStart, m_nErrorEnd);
1475  }
1476  }
1477  }
1478  //this is not a modification anymore
1479  if(nAction != ACTION_SELECTFIELD && !m_bIsUndoEditMode)
1480  CallModifyLink();
1481  }
1482  else
1483  bConsumed = m_xEditView->PostKeyEvent(rKeyEvt);
1484 
1485  return bConsumed;
1486 }
1487 
1489 {
1490  m_pToolbar = pToolbar;
1492 }
1493 
1494 IMPL_LINK(SentenceEditWindow_Impl, ToolbarHdl, const OString&, rCurItemId, void)
1495 {
1496  if (rCurItemId == "paste")
1497  {
1498  m_xEditView->Paste();
1499  CallModifyLink();
1500  }
1501  else if (rCurItemId == "insert")
1502  {
1504  {
1505  OUString aChars = Edit::GetGetSpecialCharsFunction()(GetDrawingArea(), m_xEditEngine->GetStandardFont(0));
1506  if (!aChars.isEmpty())
1507  {
1508  ESelection aCurrentSelection(m_xEditView->GetSelection());
1509  m_xEditEngine->QuickInsertText(aChars, aCurrentSelection);
1510  CallModifyLink();
1511  }
1512  }
1513  }
1514 }
1515 
1516 bool SentenceEditWindow_Impl::MarkNextError( bool bIgnoreCurrentError, const css::uno::Reference<css::linguistic2::XSpellChecker1>& xSpell )
1517 {
1518  if (bIgnoreCurrentError)
1519  m_aIgnoreErrorsAt.insert( m_nErrorStart );
1520 
1521  const sal_Int32 nTextLen = m_xEditEngine->GetTextLen(0);
1522 
1523  if (m_nErrorEnd >= nTextLen - 1)
1524  return false;
1525  //if it's not already modified the modified flag has to be reset at the end of the marking
1526  bool bModified = IsModified();
1527  bool bRet = false;
1528  const sal_Int32 nOldErrorStart = m_nErrorStart;
1529  const sal_Int32 nOldErrorEnd = m_nErrorEnd;
1530 
1531  //create a cursor behind the end of the last error
1532  //- or at 0 at the start of the sentence
1533  int nCursor(m_nErrorEnd ? m_nErrorEnd + 1 : 0);
1534 
1535  //search for SpellErrorDescription
1536  SpellErrorDescription aSpellErrorDescription;
1537 
1538  std::vector<EECharAttrib> aAttribList;
1539  m_xEditEngine->GetCharAttribs(0, aAttribList);
1540 
1541  //iterate over the text and search for the next error that maybe has
1542  //to be replace by a ChangeAllList replacement
1543  bool bGrammarError = false;
1544  while (nCursor < nTextLen)
1545  {
1546  const SpellErrorDescription* pSpellErrorDescription = nullptr;
1547  const EECharAttrib* pEECharAttrib = nullptr;
1548 
1549  sal_Int32 nMinPos = nTextLen + 1;
1550  for (const auto& rTextAtr : aAttribList)
1551  {
1552  if (rTextAtr.pAttr->Which() != EE_CHAR_GRABBAG)
1553  continue;
1554  if (rTextAtr.nEnd > nCursor && rTextAtr.nStart < nMinPos)
1555  {
1556  nMinPos = rTextAtr.nStart;
1557  pEECharAttrib = &rTextAtr;
1558  }
1559  }
1560 
1561  if (pEECharAttrib)
1562  {
1563  ExtractErrorDescription(*pEECharAttrib, aSpellErrorDescription);
1564 
1565  bGrammarError = aSpellErrorDescription.bIsGrammarError;
1566  m_nErrorStart = pEECharAttrib->nStart;
1567  m_nErrorEnd = pEECharAttrib->nEnd;
1568 
1569  pSpellErrorDescription = &aSpellErrorDescription;
1570  }
1571 
1572  nCursor = nMinPos;
1573 
1574  // maybe the error found here is already in the ChangeAllList and has to be replaced
1575  Reference<XDictionary> xChangeAll = LinguMgr::GetChangeAllList();
1576  Reference<XDictionaryEntry> xEntry;
1577 
1578  if (xChangeAll->getCount() && pSpellErrorDescription &&
1579  (xEntry = xChangeAll->getEntry( pSpellErrorDescription->sErrorText )).is())
1580  {
1581  OUString sReplacement(getDotReplacementString(GetErrorText(), xEntry->getReplacementText()));
1582 
1583  int nLenChange = ChangeMarkedWord(sReplacement, LanguageTag::convertToLanguageType(pSpellErrorDescription->aLocale));
1584 
1585  nCursor += sReplacement.getLength();
1586 
1587  if (nLenChange)
1588  m_xEditEngine->GetCharAttribs(0, aAttribList);
1589  // maybe the error found here is already added to the dictionary and has to be ignored
1590  }
1591  else if(pSpellErrorDescription && !bGrammarError &&
1592  xSpell->isValid(GetErrorText(),
1593  static_cast<sal_uInt16>(LanguageTag::convertToLanguageType( pSpellErrorDescription->aLocale )),
1595  {
1596  ++nCursor;
1597  }
1598  else
1599  break;
1600  }
1601 
1602  //if an attrib has been found search for the end of the error string
1603  if (nCursor < nTextLen)
1604  {
1605  MoveErrorMarkTo(nCursor, m_nErrorEnd, bGrammarError);
1606  bRet = true;
1607  //add an undo action
1608  std::unique_ptr<SpellUndoAction_Impl> pAction(new SpellUndoAction_Impl(
1609  SPELLUNDO_CHANGE_NEXTERROR, GetSpellDialog()->aDialogUndoLink));
1610  pAction->SetErrorMove(nOldErrorStart, nOldErrorEnd);
1611 
1612  if (GetErrorDescription(aSpellErrorDescription, nOldErrorStart))
1613  {
1614  pAction->SetErrorLanguageSelected(aSpellErrorDescription.aSuggestions.hasElements() &&
1615  LanguageTag(aSpellErrorDescription.aLocale).getLanguageType() == GetSpellDialog()->m_xLanguageLB->get_active_id());
1616  }
1617  else
1618  pAction->SetErrorLanguageSelected(false);
1619 
1620  AddUndoAction(std::move(pAction));
1621  }
1622  else
1623  m_nErrorStart = m_nErrorEnd = nTextLen;
1624  if( !bModified )
1625  ClearModifyFlag();
1626  SpellDialog* pSpellDialog = GetSpellDialog();
1627  pSpellDialog->m_xIgnorePB->set_sensitive(bRet);
1628  pSpellDialog->m_xIgnoreAllPB->set_sensitive(bRet);
1629  pSpellDialog->m_xAutoCorrPB->set_sensitive(bRet);
1630  pSpellDialog->m_xAddToDictMB->set_sensitive(bRet);
1631  pSpellDialog->m_xAddToDictPB->set_sensitive(bRet);
1632  return bRet;
1633 }
1634 
1635 void SentenceEditWindow_Impl::MoveErrorMarkTo(sal_Int32 nStart, sal_Int32 nEnd, bool bGrammarError)
1636 {
1637  ESelection aAll(0, 0, 0, EE_TEXTPOS_ALL);
1638  m_xEditEngine->RemoveAttribs(aAll, false, EE_CHAR_COLOR);
1639  m_xEditEngine->RemoveAttribs(aAll, false, EE_CHAR_WEIGHT);
1640  m_xEditEngine->RemoveAttribs(aAll, false, EE_CHAR_WEIGHT_CJK);
1641  m_xEditEngine->RemoveAttribs(aAll, false, EE_CHAR_WEIGHT_CTL);
1642 
1643  SfxItemSet aSet(m_xEditEngine->GetEmptyItemSet());
1644  aSet.Put(SvxColorItem(bGrammarError ? COL_LIGHTBLUE : COL_LIGHTRED, EE_CHAR_COLOR));
1648 
1649  m_xEditEngine->QuickSetAttribs(aSet, ESelection(0, nStart, 0, nEnd));
1650  // so the editview will autoscroll to make this visible
1651  m_xEditView->SetSelection(ESelection(0, nStart));
1652  Invalidate();
1653 
1654  m_nErrorStart = nStart;
1655  m_nErrorEnd = nEnd;
1656 }
1657 
1658 int SentenceEditWindow_Impl::ChangeMarkedWord(const OUString& rNewWord, LanguageType eLanguage)
1659 {
1660  std::vector<EECharAttrib> aAttribList;
1661  m_xEditEngine->GetCharAttribs(0, aAttribList);
1662 
1663  //calculate length changes
1664  auto nDiffLen = rNewWord.getLength() - m_nErrorEnd + m_nErrorStart;
1665  //Remove spell error attribute
1666  m_xEditEngine->UndoActionStart(SPELLUNDO_MOVE_ERROREND);
1667  const EECharAttrib* pErrorAttrib = FindCharAttrib(m_nErrorStart, m_nErrorStart, EE_CHAR_GRABBAG, aAttribList);
1668  DBG_ASSERT(pErrorAttrib, "no error attribute found");
1669  bool bSpellErrorDescription = false;
1670  SpellErrorDescription aSpellErrorDescription;
1671  if (pErrorAttrib)
1672  {
1673  ExtractErrorDescription(*pErrorAttrib, aSpellErrorDescription);
1674  m_xEditEngine->RemoveAttribs(ESelection(0, pErrorAttrib->nStart, 0, pErrorAttrib->nEnd), false, EE_CHAR_GRABBAG);
1675  bSpellErrorDescription = true;
1676  }
1677 
1678  const EECharAttrib* pBackAttrib = FindCharAttrib(m_nErrorStart, m_nErrorStart, EE_CHAR_BKGCOLOR, aAttribList);
1679 
1680  ESelection aSel(0, m_nErrorStart, 0, m_nErrorEnd);
1681  m_xEditEngine->QuickInsertText(rNewWord, aSel);
1682 
1683  const sal_Int32 nTextLen = m_xEditEngine->GetTextLen(0);
1684 
1685  if (nDiffLen)
1686  m_xEditEngine->GetCharAttribs(0, aAttribList);
1687 
1688  if (!m_nErrorStart)
1689  {
1690  //attributes following an error at the start of the text are not moved but expanded from the
1691  //text engine - this is done to keep full-paragraph-attributes
1692  //in the current case that handling is not desired
1693  const EECharAttrib* pLangAttrib = FindCharAttrib(m_nErrorEnd, m_nErrorEnd, EE_CHAR_LANGUAGE, aAttribList);
1694 
1695  if (pLangAttrib && !pLangAttrib->nStart && pLangAttrib->nEnd == nTextLen)
1696  {
1697  LanguageType eNewLanguage = static_cast<const SvxLanguageItem*>(pLangAttrib->pAttr)->GetLanguage();
1698  m_xEditEngine->RemoveAttribs(ESelection(0, pLangAttrib->nStart, 0, pLangAttrib->nEnd), false, EE_CHAR_LANGUAGE);
1699  SetAttrib(SvxLanguageItem(eNewLanguage, EE_CHAR_LANGUAGE), m_nErrorEnd + nDiffLen, nTextLen);
1700  }
1701  }
1702 
1703  // undo expanded attributes!
1704  if (pBackAttrib && pBackAttrib->nStart < m_nErrorStart && pBackAttrib->nEnd == m_nErrorEnd + nDiffLen)
1705  {
1706  std::unique_ptr<SfxPoolItem> xNewBackground(pBackAttrib->pAttr->Clone());
1707  const sal_Int32 nStart = pBackAttrib->nStart;
1708 
1709  m_xEditEngine->RemoveAttribs(ESelection(0, pBackAttrib->nStart, 0, pBackAttrib->nEnd), false, EE_CHAR_BKGCOLOR);
1710 
1711  SetAttrib(*xNewBackground, nStart, m_nErrorStart);
1712  }
1713  m_xEditEngine->SetModified();
1714 
1715  //adjust end position
1716  long nEndTemp = m_nErrorEnd;
1717  nEndTemp += nDiffLen;
1718  m_nErrorEnd = static_cast<sal_Int32>(nEndTemp);
1719 
1720  std::unique_ptr<SpellUndoAction_Impl> pAction(new SpellUndoAction_Impl(
1721  SPELLUNDO_MOVE_ERROREND, GetSpellDialog()->aDialogUndoLink));
1722  pAction->SetOffset(nDiffLen);
1723  AddUndoAction(std::move(pAction));
1724  if (bSpellErrorDescription)
1725  {
1726  SfxGrabBagItem aSpellErrorDescriptionItem(EE_CHAR_GRABBAG);
1727  aSpellErrorDescriptionItem.GetGrabBag()["SpellErrorDescription"] <<= aSpellErrorDescription.toSequence();
1728  SetAttrib(aSpellErrorDescriptionItem, m_nErrorStart, m_nErrorEnd);
1729  }
1730  SetAttrib(SvxLanguageItem(eLanguage, EE_CHAR_LANGUAGE), m_nErrorStart, m_nErrorEnd);
1731  m_xEditEngine->UndoActionEnd();
1732 
1733  Invalidate();
1734 
1735  return nDiffLen;
1736 }
1737 
1739 {
1740  return m_xEditEngine->GetText(ESelection(0, m_nErrorStart, 0, m_nErrorEnd));
1741 }
1742 
1743 bool SentenceEditWindow_Impl::GetErrorDescription(SpellErrorDescription& rSpellErrorDescription, sal_Int32 nPosition)
1744 {
1745  std::vector<EECharAttrib> aAttribList;
1746  m_xEditEngine->GetCharAttribs(0, aAttribList);
1747 
1748  if (const EECharAttrib* pEECharAttrib = FindCharAttrib(nPosition, nPosition, EE_CHAR_GRABBAG, aAttribList))
1749  {
1750  ExtractErrorDescription(*pEECharAttrib, rSpellErrorDescription);
1751  return true;
1752  }
1753 
1754  return false;
1755 }
1756 
1758 {
1759  return GetErrorDescription(rSpellErrorDescription, m_nErrorStart);
1760 }
1761 
1763 {
1764  SpellErrorDescription aSpellErrorDescription;
1765  if (GetErrorDescription(aSpellErrorDescription, m_nErrorStart))
1766  {
1767  if (aSpellErrorDescription.sErrorText != GetErrorText() )
1768  ChangeMarkedWord(aSpellErrorDescription.sErrorText, LanguageTag::convertToLanguageType(aSpellErrorDescription.aLocale));
1769  }
1770 }
1771 
1772 void SentenceEditWindow_Impl::SetAlternatives( const Reference< XSpellAlternatives>& xAlt )
1773 {
1774  OUString aWord;
1775  lang::Locale aLocale;
1776  uno::Sequence< OUString > aAlts;
1777  OUString sServiceName;
1778  if (xAlt.is())
1779  {
1780  aWord = xAlt->getWord();
1781  aLocale = xAlt->getLocale();
1782  aAlts = xAlt->getAlternatives();
1783  uno::Reference< container::XNamed > xNamed( xAlt, uno::UNO_QUERY );
1784  if (xNamed.is())
1785  sServiceName = xNamed->getName();
1786  }
1787  SpellErrorDescription aDesc( false, aWord, aLocale, aAlts, nullptr);
1788  SfxGrabBagItem aSpellErrorDescription(EE_CHAR_GRABBAG);
1789  aSpellErrorDescription.GetGrabBag()["SpellErrorDescription"] <<= aDesc.toSequence();
1790  SetAttrib(aSpellErrorDescription, m_nErrorStart, m_nErrorEnd);
1791 }
1792 
1793 void SentenceEditWindow_Impl::SetAttrib(const SfxPoolItem& rItem, sal_Int32 nStart, sal_Int32 nEnd)
1794 {
1795  SfxItemSet aSet(m_xEditEngine->GetEmptyItemSet());
1796  aSet.Put(rItem);
1797  m_xEditEngine->QuickSetAttribs(aSet, ESelection(0, nStart, 0, nEnd));
1798  Invalidate();
1799 }
1800 
1801 void SentenceEditWindow_Impl::SetText( const OUString& rStr )
1802 {
1803  m_nErrorStart = m_nErrorEnd = 0;
1804  m_xEditEngine->SetText(rStr);
1805 }
1806 
1808 {
1809  sal_Int32 nPosition;
1811 
1812  LanguagePosition_Impl(sal_Int32 nPos, LanguageType eLang) :
1813  nPosition(nPos),
1814  eLanguage(eLang)
1815  {}
1816 };
1817 typedef std::vector<LanguagePosition_Impl> LanguagePositions_Impl;
1818 
1820  LanguagePositions_Impl& rBreakPositions, sal_Int32 nInsert, LanguageType eLanguage)
1821 {
1822  LanguagePositions_Impl::iterator aStart = rBreakPositions.begin();
1823  while(aStart != rBreakPositions.end())
1824  {
1825  if(aStart->nPosition == nInsert)
1826  {
1827  //the language of following starts has to overwrite
1828  //the one of previous ends
1829  aStart->eLanguage = eLanguage;
1830  return;
1831  }
1832  else if(aStart->nPosition > nInsert)
1833  {
1834 
1835  rBreakPositions.insert(aStart, LanguagePosition_Impl(nInsert, eLanguage));
1836  return;
1837  }
1838  else
1839  ++aStart;
1840  }
1841  rBreakPositions.emplace_back(nInsert, eLanguage);
1842 }
1843 
1844 /*-------------------------------------------------------------------------
1845  Returns the text in spell portions. Each portion contains text with an
1846  equal language and attribute. The spell alternatives are empty.
1847  -----------------------------------------------------------------------*/
1849 {
1850  svx::SpellPortions aRet;
1851 
1852  const sal_Int32 nTextLen = m_xEditEngine->GetTextLen(0);
1853 
1854  std::vector<EECharAttrib> aAttribList;
1855  m_xEditEngine->GetCharAttribs(0, aAttribList);
1856 
1857  if (nTextLen)
1858  {
1859  int nCursor(0);
1860  LanguagePositions_Impl aBreakPositions;
1861  const EECharAttrib* pLastLang = nullptr;
1862  const EECharAttrib* pLastError = nullptr;
1864  const EECharAttrib* pError = nullptr;
1865  while (nCursor < nTextLen)
1866  {
1867  const EECharAttrib* pLang = FindCharAttrib(nCursor, nCursor, EE_CHAR_LANGUAGE, aAttribList);
1868  if(pLang && pLang != pLastLang)
1869  {
1870  eLang = static_cast<const SvxLanguageItem*>(pLang->pAttr)->GetLanguage();
1871  lcl_InsertBreakPosition_Impl(aBreakPositions, pLang->nStart, eLang);
1872  lcl_InsertBreakPosition_Impl(aBreakPositions, pLang->nEnd, eLang);
1873  pLastLang = pLang;
1874  }
1875  pError = FindCharAttrib(nCursor, nCursor, EE_CHAR_GRABBAG, aAttribList);
1876  if (pError && pLastError != pError)
1877  {
1878  lcl_InsertBreakPosition_Impl(aBreakPositions, pError->nStart, eLang);
1879  lcl_InsertBreakPosition_Impl(aBreakPositions, pError->nEnd, eLang);
1880  pLastError = pError;
1881 
1882  }
1883  ++nCursor;
1884  }
1885 
1886  if (aBreakPositions.empty())
1887  {
1888  //if all content has been overwritten the attributes may have been removed, too
1889  svx::SpellPortion aPortion1;
1891 
1892  aPortion1.sText = m_xEditEngine->GetText(ESelection(0, 0, 0, nTextLen));
1893 
1894  aRet.push_back(aPortion1);
1895  }
1896  else
1897  {
1898  LanguagePositions_Impl::iterator aStart = aBreakPositions.begin();
1899  //start should always be Null
1900  eLang = aStart->eLanguage;
1901  sal_Int32 nStart = aStart->nPosition;
1902  DBG_ASSERT(!nStart, "invalid start position - language attribute missing?");
1903  ++aStart;
1904 
1905  while(aStart != aBreakPositions.end())
1906  {
1907  svx::SpellPortion aPortion1;
1908  aPortion1.eLanguage = eLang;
1909 
1910  aPortion1.sText = m_xEditEngine->GetText(ESelection(0, nStart, 0, aStart->nPosition));
1911 
1912  bool bIsIgnoreError = m_aIgnoreErrorsAt.find( nStart ) != m_aIgnoreErrorsAt.end();
1913  if( bIsIgnoreError )
1914  {
1915  aPortion1.bIgnoreThisError = true;
1916  }
1917  aRet.push_back(aPortion1);
1918  nStart = aStart->nPosition;
1919  eLang = aStart->eLanguage;
1920  ++aStart;
1921  }
1922  }
1923 
1924  // quick partly fix of #i71318. Correct fix needs to patch the EditEngine itself...
1925  // this one will only prevent text from disappearing. It may to not have the
1926  // correct language and will probably not spell checked...
1927  const sal_uInt32 nPara = m_xEditEngine->GetParagraphCount();
1928  if (nPara > 1)
1929  {
1930  OUStringBuffer aLeftOverText;
1931  for (sal_uInt32 i = 1; i < nPara; ++i)
1932  {
1933  aLeftOverText.append("\x0a"); // the manual line break...
1934  aLeftOverText.append(m_xEditEngine->GetText(i));
1935  }
1936  if (pError)
1937  { // we need to add a new portion containing the left-over text
1938  svx::SpellPortion aPortion2;
1939  aPortion2.eLanguage = eLang;
1940  aPortion2.sText = aLeftOverText.makeStringAndClear();
1941  aRet.push_back( aPortion2 );
1942  }
1943  else
1944  { // we just need to append the left-over text to the last portion (which had no errors)
1945  aRet[ aRet.size() - 1 ].sText += aLeftOverText;
1946  }
1947  }
1948  }
1949 
1950  return aRet;
1951 }
1952 
1954 {
1955  SfxUndoManager& rUndoMgr = m_xEditEngine->GetUndoManager();
1956  DBG_ASSERT(GetUndoActionCount(), "no undo actions available" );
1957  if(!GetUndoActionCount())
1958  return;
1959  bool bSaveUndoEdit = IsUndoEditMode();
1960  SpellUndoAction_Impl* pUndoAction;
1961  //if the undo edit mode is active then undo all changes until the UNDO_EDIT_MODE action has been found
1962  do
1963  {
1964  pUndoAction = static_cast<SpellUndoAction_Impl*>(rUndoMgr.GetUndoAction());
1965  rUndoMgr.Undo();
1966  }while(bSaveUndoEdit && SPELLUNDO_UNDO_EDIT_MODE != pUndoAction->GetId() && GetUndoActionCount());
1967 
1968  if(bSaveUndoEdit || SPELLUNDO_CHANGE_GROUP == pUndoAction->GetId())
1970 }
1971 
1973 {
1974  SfxUndoManager& rUndo = m_xEditEngine->GetUndoManager();
1975  rUndo.Clear();
1976 }
1977 
1978 void SentenceEditWindow_Impl::AddUndoAction( std::unique_ptr<SfxUndoAction> pAction )
1979 {
1980  SfxUndoManager& rUndoMgr = m_xEditEngine->GetUndoManager();
1981  rUndoMgr.AddUndoAction(std::move(pAction));
1982  GetSpellDialog()->m_xUndoPB->set_sensitive(true);
1983 }
1984 
1986 {
1987  return m_xEditEngine->GetUndoManager().GetUndoActionCount();
1988 }
1989 
1991 {
1992  m_xEditEngine->UndoActionStart(nId);
1993 }
1994 
1996 {
1997  m_xEditEngine->UndoActionEnd();
1998 }
1999 
2001 {
2002  // Shouldn't we always add the real signed value instead???
2003  if(nOffset > 0)
2004  m_nErrorEnd = m_nErrorEnd - static_cast<sal_Int32>(nOffset);
2005  else
2006  m_nErrorEnd = m_nErrorEnd - static_cast<sal_Int32>(-nOffset);
2007 }
2008 
2009 
2011 {
2012  DBG_ASSERT(!bSet || m_bIsUndoEditMode != bSet, "SetUndoEditMode with equal values?");
2013  m_bIsUndoEditMode = bSet;
2014  //disable all buttons except the Change
2015  SpellDialog* pSpellDialog = GetSpellDialog();
2016  weld::Widget* aControls[] =
2017  {
2018  pSpellDialog->m_xChangeAllPB.get(),
2019  pSpellDialog->m_xExplainFT.get(),
2020  pSpellDialog->m_xIgnoreAllPB.get(),
2021  pSpellDialog->m_xIgnoreRulePB.get(),
2022  pSpellDialog->m_xIgnorePB.get(),
2023  pSpellDialog->m_xSuggestionLB.get(),
2024  pSpellDialog->m_xSuggestionFT.get(),
2025  pSpellDialog->m_xLanguageFT.get(),
2026  pSpellDialog->m_xLanguageLB->get_widget(),
2027  pSpellDialog->m_xAddToDictMB.get(),
2028  pSpellDialog->m_xAddToDictPB.get(),
2029  pSpellDialog->m_xAutoCorrPB.get(),
2030  nullptr
2031  };
2032  sal_Int32 nIdx = 0;
2033  do
2034  {
2035  aControls[nIdx]->set_sensitive(false);
2036  }
2037  while(aControls[++nIdx]);
2038 
2039  //remove error marks
2040  ESelection aAll(0, 0, 0, EE_TEXTPOS_ALL);
2041  m_xEditEngine->RemoveAttribs(aAll, false, EE_CHAR_COLOR);
2042  m_xEditEngine->RemoveAttribs(aAll, false, EE_CHAR_WEIGHT);
2043  m_xEditEngine->RemoveAttribs(aAll, false, EE_CHAR_WEIGHT_CJK);
2044  m_xEditEngine->RemoveAttribs(aAll, false, EE_CHAR_WEIGHT_CTL);
2045  Invalidate();
2046 
2047  //put the appropriate action on the Undo-stack
2048  AddUndoAction( std::make_unique<SpellUndoAction_Impl>(
2049  SPELLUNDO_UNDO_EDIT_MODE, GetSpellDialog()->aDialogUndoLink) );
2050  pSpellDialog->m_xChangePB->set_sensitive(true);
2051 }
2052 
2053 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
sal_Int32 nEnd
SpellUndoAction_Impl(sal_uInt16 nId, const Link< SpellUndoAction_Impl &, void > &rActionLink)
SfxUndoAction * GetUndoAction(size_t nNo=0) const
css::uno::Sequence< css::uno::Any > toSequence() const
Definition: SpellAttrib.hxx:88
#define ACTION_CONTINUE
void Adjust()
std::unique_ptr< weld::Label > m_xResumeFT
#define LANGUAGE_NONE
#define BRACE
virtual bool HasGrammarChecking()
#define ACTION_EXPAND
#define KEY_BACKSPACE
std::unique_ptr< weld::Label > m_xExplainFT
virtual void GetFocus()=0
std::unique_ptr< weld::TreeView > m_xSuggestionLB
#define KEY_DELETE
#define SPELLUNDO_CHANGE_ADD_TO_DICTIONARY
Definition: SpellDialog.cxx:75
virtual void SetDrawingArea(weld::DrawingArea *pDrawingArea) override
sal_Int32 nStart
OUString m_sResumeST
#define FULL
#define KEY_TAB
#define EE_CHAR_LANGUAGE
std::unique_ptr< weld::Label > m_xNotInDictFT
signed char sal_Int8
virtual SfxPoolItem * Clone(SfxItemPool *pPool=nullptr) const =0
LanguageType getLanguageType(bool bResolveSystem=true) const
#define SPELLUNDO_MOVE_ERROREND
Definition: SpellDialog.cxx:77
std::unique_ptr< SpellDialog_Impl > pImpl
static LanguageType convertToLanguageType(const css::lang::Locale &rLocale, bool bResolveSystem=true)
void AddToDictionaryExecute(const OString &rItemId)
IMPL_LINK_NOARG(ClassificationDialog, OnAsyncExpandHdl, void *, void)
std::unique_ptr< weld::Label > m_xSuggestionFT
css::uno::Sequence< OUString > aSuggestions
Definition: SpellAttrib.hxx:37
#define SPELLUNDO_CHANGE_LANGUAGE
Definition: SpellDialog.cxx:72
#define EE_CHAR_WEIGHT_CTL
void SetTitle_Impl(LanguageType nLang)
void AddUndoAction(std::unique_ptr< SfxUndoAction > pAction)
std::set< sal_Int32 > m_aIgnoreErrorsAt
Definition: SpellDialog.hxx:52
std::unique_ptr< weld::Button > m_xOptionsPB
virtual void Deactivate() override
const SfxPoolItem * pAttr
bool GetErrorDescription(SpellErrorDescription &rSpellErrorDescription, sal_Int32 nPosition)
OUString getReplacementString() const
static css::uno::Reference< css::linguistic2::XDictionary > GetStandardDic()
static ImplSVEvent * PostUserEvent(const Link< void *, void > &rLink, void *pCaller=nullptr, bool bReferenceLink=false)
bool GetNextSentence_Impl(bool bUseSavedSentence, bool bRechek)
Retrieves the next sentence.
sal_uInt16 GetCode() const
void ToplevelFocusChanged()
void Init(weld::Toolbar *pToolbar)
constexpr::Color COL_LIGHTRED(0xFF, 0x00, 0x00)
OUString GetErrorText() const
OUString Name
std::unique_ptr< LanguageBox > m_xLanguageLB
constexpr::Color COL_LIGHTGRAY(0xC0, 0xC0, 0xC0)
bool SaveDictionaries(const uno::Reference< XSearchableDictionaryList > &xDicList)
const Link< SpellUndoAction_Impl &, void > & m_rActionLink
Definition: SpellDialog.cxx:85
virtual void SetDrawingArea(weld::DrawingArea *pDrawingArea) override
virtual bool Undo()
OUString m_sIgnoreOnceST
void SetText(const OUString &rStr)
SfxApplication * SfxGetpApp()
std::unique_ptr< weld::Button > m_xIgnoreAllPB
virtual bool HasAutoCorrection()
WEIGHT_BOLD
void ToggleChildWindow(sal_uInt16)
std::unique_ptr< weld::Label > m_xNoSuggestionsFT
svx::SpellPortions CreateSpellPortions() const
std::unique_ptr< weld::Button > m_xIgnoreRulePB
std::unique_ptr< weld::Button > m_xAutoCorrPB
virtual void Deactivate() override
std::unique_ptr< weld::Button > m_xAddToDictPB
#define EE_CHAR_WEIGHT_CJK
svx::SpellDialogChildWindow & rParent
static bool DoesKeyChangeText(const KeyEvent &rKeyEvent)
static css::uno::Reference< css::linguistic2::XSearchableDictionaryList > GetDictionaryList()
std::unique_ptr< weld::Label > m_xLanguageFT
OUString GetSpellAndGrammarContextDictionaryImage(const OUString &rServiceImplName) const
#define EE_CHAR_BKGCOLOR
static css::uno::Reference< css::linguistic2::XDictionary > GetIgnoreAllList()
void UndoActionStart(sal_uInt16 nId)
virtual bool KeyInput(const KeyEvent &rKEvt) override
std::unique_ptr< weld::Button > m_xUndoPB
std::vector< SpellPortion > SpellPortions
static css::uno::Reference< css::linguistic2::XDictionary > GetChangeAllList()
sal_uInt16 GetId() const
#define INVALID
short SvxDicError(weld::Window *pParent, linguistic::DictionaryError nError)
virtual void Close() override
virtual void LoseFocus()=0
std::unique_ptr< weld::CheckButton > m_xCheckGrammarCB
std::unique_ptr< weld::Toolbar > m_xToolbar
virtual void AddUndoAction(std::unique_ptr< SfxUndoAction > pAction, bool bTryMerg=false)
std::unique_ptr< weld::Button > m_xIgnorePB
OUString m_sTitleSpellingGrammar
#define GROUP_MODULES
Definition: optlingu.hxx:42
std::unique_ptr< weld::LinkButton > m_xExplainLink
static css::uno::Reference< css::linguistic2::XSpellChecker1 > GetSpellChecker()
void MoveErrorEnd(long nOffset)
const Reference< XDictionary > & GetDictionary() const
#define DBG_ASSERT(sCon, aError)
OUString m_sTitleSpelling
LanguageType GetLanguage(SwWrtShell &rSh, sal_uInt16 nLangWhichId)
std::unique_ptr< weld::Button > m_xClosePB
Link< SpellUndoAction_Impl &, void > aDialogUndoLink
static void ApplyLanguageOptions(const SfxItemSet &rSet)
Definition: treeopt.cxx:1333
LanguagePosition_Impl(sal_Int32 nPos, LanguageType eLang)
virtual void Activate() override
int i
virtual void Clear()
std::vector< LanguagePosition_Impl > LanguagePositions_Impl
std::unique_ptr< SentenceEditWindow_Impl > m_xSentenceED
static VclPtr< SfxTabPage > Create(TabPageParent pParent, const SfxItemSet *rSet)
Definition: optlingu.cxx:919
#define INSIDE_YES
virtual SpellPortions GetNextWrongSentence(bool bRecheck)=0
std::unique_ptr< weld::Button > m_xChangeAllPB
Object Value
void Impl_Restore(bool bUseSavedSentence)
const std::map< OUString, css::uno::Any > & GetGrabBag() const
#define EE_CHAR_COLOR
#define LANGUAGE_DONTKNOW
std::unique_ptr< EditView > m_xEditView
IMPL_LINK(ClassificationDialog, SelectClassificationHdl, weld::ComboBox &, rBox, void)
void UpdateBoxes_Impl(bool bCallFromSelectHdl=false)
static bool ApplyChangeAllList_Impl(SpellPortions &rSentence, bool &bHasReplaced)
Corrects all errors that have been selected to be changed always.
virtual ~SentenceEditWindow_Impl() override
void SvxPrepareAutoCorrect(OUString &rOldText, const OUString &rNewText)
Sequence< Reference< XDictionary > > aDics
Definition: SpellDialog.cxx:66
#define SPELLUNDO_ADD_IGNORE_RULE
Definition: SpellDialog.cxx:79
const OUString & GetAddedWord() const
#define ACTION_UNDOEDIT
#define SPELLUNDO_UNDO_EDIT_MODE
Definition: SpellDialog.cxx:78
void SetDictionary(const Reference< XDictionary > &xDict)
LanguageType GetSelectedLang_Impl() const
#define SPELLUNDO_CHANGE_GROUP
Definition: SpellDialog.cxx:76
const vcl::KeyCode & GetKeyCode() const
virtual void Activate() override
OUString m_sNoSuggestionsST
void SetAlternatives(const css::uno::Reference< css::linguistic2::XSpellAlternatives > &)
static SfxViewFrame * Current()
bool GetAlternatives(SpellErrorDescription &rDesc)
#define OUTSIDE_NO
int ChangeMarkedWord(const OUString &rNewWord, LanguageType eLanguage)
#define SAL_WARN_IF(condition, area, stream)
DictionaryError AddEntryToDic(uno::Reference< XDictionary > const &rxDic, const OUString &rWord, bool bIsNeg, const OUString &rRplcTxt, bool bStripDot)
virtual void ApplyChangedSentence(const SpellPortions &rChanged, bool bRecheck)=0
unsigned char sal_uInt8
void SetOffset(long nSet)
#define EE_CHAR_WEIGHT
void SpellContinue_Impl(bool UseSavedSentence=false, bool bIgnoreCurrentError=false)
std::unique_ptr< weld::MenuButton > m_xAddToDictMB
#define SPELLUNDO_CHANGE_NEXTERROR
Definition: SpellDialog.cxx:74
bool MarkNextError(bool bIgnoreCurrentError, const css::uno::Reference< css::linguistic2::XSpellChecker1 > &)
css::lang::Locale aLocale
Definition: SpellAttrib.hxx:35
std::unique_ptr< weld::Label > m_xAltTitle
static FncGetSpecialChars GetGetSpecialCharsFunction()
void SetAttrib(const SfxPoolItem &rItem, sal_Int32 nStart, sal_Int32 nEnd)
RET_OK
virtual int get_text_height() const =0
#define SPELLUNDO_CHANGE_TEXTENGINE
Definition: SpellDialog.cxx:73
void MoveErrorMarkTo(sal_Int32 nErrorStart, sal_Int32 nErrorEnd, bool bGrammar)
static void lcl_InsertBreakPosition_Impl(LanguagePositions_Impl &rBreakPositions, sal_Int32 nInsert, LanguageType eLanguage)
void SetAddedWord(const OUString &rWord)
DictionaryError
Reference< XDictionary > m_xDictionary
Definition: SpellDialog.cxx:94
void SetErrorMove(long nOldStart, long nOldEnd)
#define OUTSIDE_YES
static OUString GetLanguageString(const LanguageType eType)
constexpr::Color COL_LIGHTBLUE(0x00, 0x00, 0xFF)
#define ACTION_SELECTFIELD
svx::SpellPortions m_aSavedSentence
void connect_clicked(const Link< const OString &, void > &rLink)
std::unique_ptr< EditEngine > m_xEditEngine
SpellDialog * GetSpellDialog() const
Definition: SpellDialog.hxx:63
#define EE_CHAR_GRABBAG
LanguageType eLanguage
#define INSIDE_NO
void SetErrorLanguageSelected(bool bSet)
void StartSpellOptDlg_Impl()
std::unique_ptr< weld::Button > m_xChangePB
virtual void set_size_request(int nWidth, int nHeight)=0
bool IsErrorLanguageSelected() const
#define RIGHT_NO
virtual ~SpellDialog() override
#define EE_TEXTPOS_ALL
#define LEFT_NO
css::uno::Reference< css::linguistic2::XSpellChecker1 > xSpell
virtual float get_approximate_digit_width() const =0
const sal_Char sServiceName[]
virtual void set_sensitive(bool sensitive)=0
static css::lang::Locale convertToLocale(LanguageType nLangID, bool bResolveSystem=true)