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