cid#1636690 Dereference after null check
[LibreOffice.git] / cui / source / options / optdict.cxx
blob04dbd2ff801b3b890334dd458cc42a0ff587b74e
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
20 #include <editeng/unolingu.hxx>
21 #include <o3tl/safeint.hxx>
22 #include <svx/dialmgr.hxx>
23 #include <com/sun/star/frame/XStorable.hpp>
24 #include <com/sun/star/linguistic2/XDictionary.hpp>
25 #include <com/sun/star/linguistic2/XSearchableDictionaryList.hpp>
26 #include <comphelper/string.hxx>
27 #include <tools/debug.hxx>
28 #include <unotools/collatorwrapper.hxx>
29 #include <unotools/intlwrapper.hxx>
30 #include <unotools/syslocale.hxx>
31 #include <vcl/svapp.hxx>
32 #include <vcl/weld.hxx>
34 #include <linguistic/misc.hxx>
35 #include <strings.hrc>
36 #include <optdict.hxx>
37 #include <dialmgr.hxx>
38 #include <svx/svxerr.hxx>
40 using namespace ::com::sun::star;
41 using namespace ::com::sun::star::uno;
42 using namespace ::com::sun::star::linguistic2;
43 using namespace linguistic;
45 // static function -------------------------------------------------------
47 static OUString getNormDicEntry_Impl(std::u16string_view rText)
49 OUString aTmp(comphelper::string::stripEnd(rText, '.'));
50 // non-standard hyphenation
51 if (aTmp.indexOf('[') > -1)
53 OUStringBuffer aTmp2 ( aTmp.getLength() );
54 bool bSkip = false;
55 for (sal_Int32 i = 0; i < aTmp.getLength(); i++)
57 sal_Unicode cTmp = aTmp[i];
58 if (cTmp == '[')
59 bSkip = true;
60 else if (!bSkip)
61 aTmp2.append( cTmp );
62 else if (cTmp == ']')
63 bSkip = false;
65 aTmp = aTmp2.makeStringAndClear();
67 return aTmp.replaceAll("=", "");
70 // tdf#154499 separate words of a phrase only by a single space,
71 // i.e. trim terminating spaces and replace space sequences with single spaces
72 static OUString fixSpace(OUString sText)
74 sText = sText.trim();
76 sal_Int32 nLen;
79 nLen = sText.getLength();
80 sText = sText.replaceAll(" ", " ");
82 while ( sText.getLength() < nLen );
84 return sText;
87 namespace {
89 // Compare Dictionary Entry result
90 enum CDE_RESULT { CDE_EQUAL, CDE_SIMILAR, CDE_DIFFERENT };
94 static CDE_RESULT cmpDicEntry_Impl( std::u16string_view rText1, std::u16string_view rText2 )
96 CDE_RESULT eRes = CDE_DIFFERENT;
98 if (rText1 == rText2)
99 eRes = CDE_EQUAL;
100 else
101 { // similar = equal up to trailing '.' and hyphenation positions
102 // marked with '=' and '[' + alternative spelling pattern + ']'
103 if (getNormDicEntry_Impl( rText1 ) == getNormDicEntry_Impl( rText2 ))
104 eRes = CDE_SIMILAR;
107 return eRes;
110 // class SvxNewDictionaryDialog -------------------------------------------
112 SvxNewDictionaryDialog::SvxNewDictionaryDialog(weld::Window* pParent)
113 : GenericDialogController(pParent, u"cui/ui/optnewdictionarydialog.ui"_ustr, u"OptNewDictionaryDialog"_ustr)
114 , m_xNameEdit(m_xBuilder->weld_entry(u"nameedit"_ustr))
115 , m_xLanguageLB(new SvxLanguageBox(m_xBuilder->weld_combo_box(u"language"_ustr)))
116 , m_xExceptBtn(m_xBuilder->weld_check_button(u"except"_ustr))
117 , m_xOKBtn(m_xBuilder->weld_button(u"ok"_ustr))
119 // Prevent creation of dictionary without a name.
120 m_xOKBtn->set_sensitive(false);
122 // install handler
123 m_xNameEdit->connect_changed(LINK(this, SvxNewDictionaryDialog, ModifyHdl_Impl));
124 m_xOKBtn->connect_clicked(LINK(this, SvxNewDictionaryDialog, OKHdl_Impl));
126 // display languages
127 m_xLanguageLB->SetLanguageList(SvxLanguageListFlags::ALL, true, true);
128 m_xLanguageLB->set_active(0);
131 IMPL_LINK_NOARG(SvxNewDictionaryDialog, OKHdl_Impl, weld::Button&, void)
134 // add extension for personal dictionaries
135 OUString sDict = comphelper::string::stripEnd(m_xNameEdit->get_text(), ' ') + ".dic";
137 Reference< XSearchableDictionaryList > xDicList( LinguMgr::GetDictionaryList() );
139 if ( sDict.indexOf("/") != -1 || sDict.indexOf("\\") != -1 )
141 // Detected an invalid character.
142 std::unique_ptr<weld::MessageDialog> xInfoBox(Application::CreateMessageDialog(m_xDialog.get(),
143 VclMessageType::Info, VclButtonsType::Ok,
144 CuiResId(RID_CUISTR_OPT_INVALID_DICT_NAME)));
145 xInfoBox->run();
146 m_xNameEdit->grab_focus();
147 return;
150 Sequence< Reference< XDictionary > > aDics;
151 if (xDicList.is())
152 aDics = xDicList->getDictionaries();
154 if (std::any_of(aDics.begin(), aDics.end(),
155 [&sDict](auto& d) { return sDict.equalsIgnoreAsciiCase(d->getName()); }))
157 // duplicate names?
158 std::unique_ptr<weld::MessageDialog> xInfoBox(Application::CreateMessageDialog(m_xDialog.get(),
159 VclMessageType::Info, VclButtonsType::Ok,
160 CuiResId(RID_CUISTR_OPT_DOUBLE_DICTS)));
161 xInfoBox->run();
162 m_xNameEdit->grab_focus();
163 return;
166 // create and add
167 LanguageType nLang = m_xLanguageLB->get_active_id();
170 // create new dictionary
171 DictionaryType eType = m_xExceptBtn->get_active() ?
172 DictionaryType_NEGATIVE : DictionaryType_POSITIVE;
173 if (xDicList.is())
175 lang::Locale aLocale( LanguageTag::convertToLocale(nLang) );
176 OUString aURL( linguistic::GetWritableDictionaryURL( sDict ) );
177 m_xNewDic = xDicList->createDictionary(sDict, aLocale, eType, aURL);
178 m_xNewDic->setActive(true);
180 DBG_ASSERT(m_xNewDic.is(), "NULL pointer");
182 catch(...)
184 m_xNewDic = nullptr;
185 // error: couldn't create new dictionary
186 SfxErrorContext aContext( ERRCTX_SVX_LINGU_DICTIONARY, OUString(),
187 m_xDialog.get(), RID_SVXERRCTX, SvxResLocale() );
188 ErrorHandler::HandleError( ErrCodeMsg(
189 ERRCODE_SVX_LINGU_DICT_NOTWRITEABLE, sDict ) );
190 m_xDialog->response(RET_CANCEL);
193 if (xDicList.is() && m_xNewDic.is())
195 xDicList->addDictionary(m_xNewDic);
197 // refresh list of dictionaries
198 //! dictionaries may have been added/removed elsewhere too.
199 aDics = xDicList->getDictionaries();
202 m_xDialog->response(RET_OK);
205 IMPL_LINK_NOARG(SvxNewDictionaryDialog, ModifyHdl_Impl, weld::Entry&, void)
207 m_xOKBtn->set_sensitive(!m_xNameEdit->get_text().isEmpty());
210 // class SvxEditDictionaryDialog -------------------------------------------
212 SvxEditDictionaryDialog::SvxEditDictionaryDialog(weld::Window* pParent, std::u16string_view rName)
213 : GenericDialogController(pParent, u"cui/ui/editdictionarydialog.ui"_ustr, u"EditDictionaryDialog"_ustr)
214 , sModify(CuiResId(STR_MODIFY))
215 , bFirstSelect(false)
216 , bDoNothing(false)
217 , bDicIsReadonly(false)
218 , m_xAllDictsLB(m_xBuilder->weld_combo_box(u"book"_ustr))
219 , m_xLangFT(m_xBuilder->weld_label(u"lang_label"_ustr))
220 , m_xLangLB(new SvxLanguageBox(m_xBuilder->weld_combo_box(u"lang"_ustr)))
221 , m_xWordED(m_xBuilder->weld_entry(u"word"_ustr))
222 , m_xReplaceFT(m_xBuilder->weld_label(u"replace_label"_ustr))
223 , m_xReplaceED(m_xBuilder->weld_entry(u"replace"_ustr))
224 , m_xSingleColumnLB(m_xBuilder->weld_tree_view(u"words"_ustr))
225 , m_xDoubleColumnLB(m_xBuilder->weld_tree_view(u"replaces"_ustr))
226 , m_xNewReplacePB(m_xBuilder->weld_button(u"newreplace"_ustr))
227 , m_xDeletePB(m_xBuilder->weld_button(u"delete"_ustr))
229 sReplaceFT_Text = m_xReplaceFT->get_label();
230 m_xSingleColumnLB->set_size_request(-1, m_xSingleColumnLB->get_height_rows(8));
231 m_xDoubleColumnLB->set_size_request(-1, m_xDoubleColumnLB->get_height_rows(8));
232 m_pWordsLB = m_xDoubleColumnLB.get();
233 m_xSingleColumnLB->hide();
235 //set to max of both sizes to avoid resizes
236 sNew = m_xNewReplacePB->get_label();
237 auto nNewWidth = m_xNewReplacePB->get_preferred_size().Width();
238 m_xNewReplacePB->set_label(sModify);
239 auto nReplaceWidth = m_xNewReplacePB->get_preferred_size().Width();
240 m_xNewReplacePB->set_label(sNew);
241 m_xNewReplacePB->set_size_request(std::max(nNewWidth, nReplaceWidth), -1);
243 if (LinguMgr::GetDictionaryList().is())
244 aDics = LinguMgr::GetDictionaryList()->getDictionaries();
246 m_xSingleColumnLB->connect_selection_changed(LINK(this, SvxEditDictionaryDialog, SelectHdl));
247 m_xDoubleColumnLB->connect_selection_changed(LINK(this, SvxEditDictionaryDialog, SelectHdl));
249 std::vector<int> aWidths
251 o3tl::narrowing<int>(m_xDoubleColumnLB->get_approximate_digit_width() * 22)
253 m_xDoubleColumnLB->set_column_fixed_widths(aWidths);
255 // install handler
256 m_xNewReplacePB->connect_clicked(
257 LINK( this, SvxEditDictionaryDialog, NewDelButtonHdl));
258 m_xDeletePB->connect_clicked(
259 LINK( this, SvxEditDictionaryDialog, NewDelButtonHdl));
261 m_xLangLB->connect_changed(
262 LINK( this, SvxEditDictionaryDialog, SelectLangHdl_Impl ) );
263 m_xAllDictsLB->connect_changed(
264 LINK( this, SvxEditDictionaryDialog, SelectBookHdl_Impl ) );
266 m_xWordED->connect_changed(LINK(this, SvxEditDictionaryDialog, ModifyHdl));
267 m_xReplaceED->connect_changed(LINK(this, SvxEditDictionaryDialog, ModifyHdl));
268 m_xWordED->connect_activate(LINK(this, SvxEditDictionaryDialog, NewDelActionHdl));
269 m_xReplaceED->connect_activate(LINK(this, SvxEditDictionaryDialog, NewDelActionHdl));
271 // fill listbox with all available WB's
272 OUString aLookUpEntry;
273 for (auto& xDic : aDics)
275 if (xDic.is())
277 bool bNegative = xDic->getDictionaryType() == DictionaryType_NEGATIVE;
278 OUString aDicName( xDic->getName() );
279 const OUString aTxt( ::GetDicInfoStr( aDicName,
280 LanguageTag( xDic->getLocale() ).getLanguageType(), bNegative ) );
281 m_xAllDictsLB->append_text(aTxt);
283 if (rName == aDicName)
284 aLookUpEntry = aTxt;
288 m_xLangLB->SetLanguageList( SvxLanguageListFlags::ALL, true, true );
290 if (aDics.hasElements())
292 m_xAllDictsLB->set_active_text(aLookUpEntry);
293 int nPos = m_xAllDictsLB->get_active();
295 if (nPos == -1)
297 nPos = 0;
298 m_xAllDictsLB->set_active(nPos);
300 Reference< XDictionary > xDic;
301 if (nPos != -1)
302 xDic = aDics[ nPos ];
303 if (xDic.is())
304 SetLanguage_Impl( LanguageTag( xDic->getLocale() ).getLanguageType() );
306 // check if dictionary is read-only
307 SetDicReadonly_Impl(xDic);
308 bool bEnable = !IsDicReadonly_Impl();
309 m_xNewReplacePB->set_sensitive( false );
310 m_xDeletePB->set_sensitive( false );
311 m_xLangFT->set_sensitive( bEnable );
312 m_xLangLB->set_sensitive( bEnable );
313 ShowWords_Impl( nPos );
315 else
317 m_xNewReplacePB->set_sensitive(false);
318 m_xDeletePB->set_sensitive(false);
321 m_xWordED->connect_size_allocate(LINK(this, SvxEditDictionaryDialog, EntrySizeAllocHdl));
322 m_xReplaceED->connect_size_allocate(LINK(this, SvxEditDictionaryDialog, EntrySizeAllocHdl));
325 IMPL_LINK_NOARG(SvxEditDictionaryDialog, EntrySizeAllocHdl, const Size&, void)
327 std::vector<int> aWidths;
328 int x, y, width, height;
329 if (m_xReplaceED->get_extents_relative_to(*m_pWordsLB, x, y, width, height))
331 aWidths.push_back(x);
332 m_xDoubleColumnLB->set_column_fixed_widths(aWidths);
336 SvxEditDictionaryDialog::~SvxEditDictionaryDialog()
340 void SvxEditDictionaryDialog::SetDicReadonly_Impl(
341 Reference< XDictionary > const &xDic )
343 // enable or disable new and delete button according to file attributes
344 bDicIsReadonly = true;
345 if (xDic.is())
347 Reference< frame::XStorable > xStor( xDic, UNO_QUERY );
348 if ( !xStor.is() // non persistent dictionary
349 || !xStor->hasLocation() // not yet persistent
350 || !xStor->isReadonly() )
352 bDicIsReadonly = false;
357 void SvxEditDictionaryDialog::SetLanguage_Impl(LanguageType nLanguage)
359 // select language
360 m_xLangLB->set_active_id(nLanguage);
363 int SvxEditDictionaryDialog::GetLBInsertPos(std::u16string_view rDicWord)
365 IntlWrapper aIntlWrapper(SvtSysLocale().GetUILanguageTag());
366 const CollatorWrapper* pCollator = aIntlWrapper.getCollator();
367 int j;
368 int nCount = m_pWordsLB->n_children();
369 for (j = 0; j < nCount; ++j)
371 OUString aNormEntry( getNormDicEntry_Impl( rDicWord ) );
372 sal_Int32 nCmpRes = pCollator->
373 compareString( aNormEntry, getNormDicEntry_Impl( m_pWordsLB->get_text(j, 0) ) );
374 if (nCmpRes < 0)
375 break;
378 return j;
381 void SvxEditDictionaryDialog::RemoveDictEntry(int nEntry)
383 int nLBPos = m_xAllDictsLB->get_active();
384 if (nEntry != -1 && nLBPos != -1)
386 OUString sTmpShort(m_pWordsLB->get_text(nEntry, 0));
388 Reference<XDictionary> xDic = aDics[nLBPos];
389 if (xDic->remove(sTmpShort)) // sal_True on success
391 m_pWordsLB->remove(nEntry);
392 SelectHdl(*m_pWordsLB);
397 IMPL_LINK_NOARG(SvxEditDictionaryDialog, SelectBookHdl_Impl, weld::ComboBox&, void)
399 int nPos = m_xAllDictsLB->get_active();
400 if (nPos == -1)
401 return;
403 m_xNewReplacePB->set_sensitive( false );
404 m_xDeletePB->set_sensitive( false );
405 // display dictionary
406 ShowWords_Impl( nPos );
407 // enable or disable new and delete button according to file attributes
408 Reference< XDictionary > const & xDic = aDics[ nPos ];
409 if (xDic.is())
410 SetLanguage_Impl( LanguageTag( xDic->getLocale() ).getLanguageType() );
412 SetDicReadonly_Impl(xDic);
413 bool bEnable = !IsDicReadonly_Impl();
414 m_xLangFT->set_sensitive( bEnable );
415 m_xLangLB->set_sensitive( bEnable );
418 IMPL_LINK_NOARG(SvxEditDictionaryDialog, SelectLangHdl_Impl, weld::ComboBox&, void)
420 int nDicPos = m_xAllDictsLB->get_active();
421 LanguageType nLang = m_xLangLB->get_active_id();
422 Reference< XDictionary > const & xDic = aDics[ nDicPos ];
423 LanguageType nOldLang = LanguageTag( xDic->getLocale() ).getLanguageType();
425 if ( nLang == nOldLang )
426 return;
428 std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(m_xDialog.get(),
429 VclMessageType::Question, VclButtonsType::YesNo,
430 CuiResId(RID_CUISTR_CONFIRM_SET_LANGUAGE)));
431 OUString sTxt(xBox->get_primary_text());
432 sTxt = sTxt.replaceFirst("%1", m_xAllDictsLB->get_active_text());
433 xBox->set_primary_text(sTxt);
435 if (xBox->run() == RET_YES)
437 xDic->setLocale( LanguageTag::convertToLocale( nLang ) );
438 bool bNegativ = xDic->getDictionaryType() == DictionaryType_NEGATIVE;
440 const OUString sName(
441 ::GetDicInfoStr( xDic->getName(),
442 LanguageTag( xDic->getLocale() ).getLanguageType(),
443 bNegativ ) );
444 m_xAllDictsLB->remove(nDicPos);
445 m_xAllDictsLB->insert_text(nDicPos, sName);
446 m_xAllDictsLB->set_active(nDicPos);
448 else
449 SetLanguage_Impl( nOldLang );
452 void SvxEditDictionaryDialog::ShowWords_Impl( sal_uInt16 nId )
454 Reference<XDictionary> xDic = aDics[nId];
456 weld::WaitObject aWait(m_xDialog.get());
458 m_xWordED->set_text(OUString());
459 m_xReplaceED->set_text(OUString());
461 bool bIsNegative = xDic->getDictionaryType() != DictionaryType_POSITIVE;
462 bool bLangNone = LanguageTag(
463 xDic->getLocale() ).getLanguageType() == LANGUAGE_NONE;
465 // The label is "Replace By" only in negative dictionaries (forbidden
466 // words), otherwise "Grammar By" in language-specific dictionaries
467 // (where the optional second word is the sample word for
468 // the Hunspell based affixation/compounding of the new dictionary word)
469 if (bIsNegative)
471 m_xReplaceFT->set_label(sReplaceFT_Text);
472 } else if (!bLangNone) {
473 m_xReplaceFT->set_label(CuiResId(RID_CUISTR_OPT_GRAMMAR_BY));
476 if(bIsNegative || !bLangNone)
478 // make controls for replacement text active
479 if (!m_xReplaceFT->get_visible())
481 m_xReplaceFT->show();
482 m_xReplaceED->show();
483 m_xSingleColumnLB->hide();
484 m_xDoubleColumnLB->show();
485 m_pWordsLB = m_xDoubleColumnLB.get();
488 else
490 // deactivate controls for replacement text
491 if (m_xReplaceFT->get_visible())
493 m_xReplaceFT->hide();
494 m_xReplaceED->hide();
495 m_xDoubleColumnLB->hide();
496 m_xSingleColumnLB->show();
497 m_pWordsLB = m_xSingleColumnLB.get();
501 m_pWordsLB->clear();
503 Sequence< Reference< XDictionaryEntry > > aEntries( xDic->getEntries() );
504 std::vector<OUString> aSortedDicEntries;
505 aSortedDicEntries.reserve(aEntries.getLength());
506 for (auto& xDictionaryEntry : aEntries)
508 OUString aStr = xDictionaryEntry->getDictionaryWord();
509 if (!xDictionaryEntry->getReplacementText().isEmpty())
511 aStr += "\t" + xDictionaryEntry->getReplacementText();
513 aSortedDicEntries.push_back(aStr);
516 IntlWrapper aIntlWrapper(SvtSysLocale().GetUILanguageTag());
517 const CollatorWrapper* pCollator = aIntlWrapper.getCollator();
518 std::sort(aSortedDicEntries.begin(), aSortedDicEntries.end(),
519 [&] (OUString const & lhs, OUString const & rhs)
521 sal_Int32 nCmpRes = pCollator->
522 compareString( getNormDicEntry_Impl(lhs), getNormDicEntry_Impl( rhs ) );
523 return nCmpRes < 0;
526 m_pWordsLB->freeze(); // speed up insert
527 int nRow = 0;
528 for (OUString const & rStr : aSortedDicEntries)
530 sal_Int32 index = 0;
531 m_pWordsLB->append_text(rStr.getToken(0, '\t', index));
532 if (index != -1 && m_pWordsLB == m_xDoubleColumnLB.get())
534 OUString sReplace = rStr.getToken(0, '\t', index);
535 m_pWordsLB->set_text(nRow, sReplace, 1);
536 ++nRow;
539 m_pWordsLB->thaw();
541 if (m_pWordsLB->n_children())
543 m_pWordsLB->select(0);
544 m_pWordsLB->set_cursor(0);
545 SelectHdl(*m_pWordsLB);
549 IMPL_LINK(SvxEditDictionaryDialog, SelectHdl, weld::TreeView&, rBox, void)
551 if (bDoNothing)
552 return;
554 int nEntry = rBox.get_selected_index();
556 if(!bFirstSelect)
558 if (nEntry != -1)
560 OUString sTmpShort(rBox.get_text(nEntry, 0));
561 // without this the cursor is always at the beginning of a word, if the text
562 // is set over the ModifyHdl, although you're editing there at the moment
563 if (m_xWordED->get_text() != sTmpShort)
564 m_xWordED->set_text(sTmpShort);
565 if (&rBox == m_xDoubleColumnLB.get())
566 m_xReplaceED->set_text(rBox.get_text(nEntry, 1));
569 else
570 bFirstSelect = false;
572 // entries in the list box should exactly correspond to those from the
573 // dictionary. Thus:
574 m_xNewReplacePB->set_sensitive(false);
575 m_xDeletePB->set_sensitive(nEntry != -1 && !IsDicReadonly_Impl());
578 IMPL_LINK(SvxEditDictionaryDialog, NewDelButtonHdl, weld::Button&, rBtn, void)
580 NewDelHdl(&rBtn);
583 IMPL_LINK(SvxEditDictionaryDialog, NewDelActionHdl, weld::Entry&, rDictEdit, bool)
585 return NewDelHdl(&rDictEdit);
588 bool SvxEditDictionaryDialog::NewDelHdl(const weld::Widget* pBtn)
590 if (pBtn == m_xDeletePB.get())
592 m_xWordED->set_text(u""_ustr);
593 m_xReplaceED->set_text(u""_ustr);
594 m_xDeletePB->set_sensitive(false);
596 int nEntry = m_pWordsLB->get_selected_index();
597 RemoveDictEntry(nEntry); // remove entry from dic and list-box
599 if (pBtn == m_xNewReplacePB.get() || m_xNewReplacePB->get_sensitive())
601 int nEntry = m_pWordsLB->get_selected_index();
602 OUString aNewWord(fixSpace(m_xWordED->get_text()));
603 OUString aReplaceStr(fixSpace(m_xReplaceED->get_text()));
605 DictionaryError nAddRes = DictionaryError::UNKNOWN;
606 int nPos = m_xAllDictsLB->get_active();
607 if (nPos != -1 && !aNewWord.isEmpty())
609 DBG_ASSERT(nPos < aDics.getLength(), "invalid dictionary index");
610 Reference< XDictionary > const & xDic = aDics[ nPos ];
611 if (xDic.is())
613 // make changes in dic
615 bool bIsNegEntry = xDic->getDictionaryType() == DictionaryType_NEGATIVE;
617 OUString aRplcText;
618 if(!aReplaceStr.isEmpty())
619 aRplcText = aReplaceStr;
621 if (nEntry != -1) // entry selected in m_pWordsLB ie action = modify entry
622 xDic->remove(m_pWordsLB->get_text(nEntry, 0));
623 // if remove has failed the following add should fail too
624 // and thus a warning message should be triggered...
626 nAddRes = linguistic::AddEntryToDic( xDic,
627 aNewWord, bIsNegEntry,
628 aRplcText, false );
631 if (DictionaryError::NONE != nAddRes)
632 SvxDicError(m_xDialog.get(), nAddRes);
634 if (DictionaryError::NONE == nAddRes && !aNewWord.isEmpty())
636 // insert new entry in list-box etc...
637 m_pWordsLB->freeze();
639 if (nEntry != -1) // entry selected in m_pWordsLB ie action = modify entry
641 m_pWordsLB->set_text(nEntry, aNewWord);
642 if (!aReplaceStr.isEmpty())
643 m_pWordsLB->set_text(nEntry, aReplaceStr, 1);
645 else
647 nEntry = GetLBInsertPos(aNewWord);
648 m_pWordsLB->insert_text(nEntry, aNewWord);
649 if(!aReplaceStr.isEmpty())
650 m_pWordsLB->set_text(nEntry, aReplaceStr, 1);
653 m_pWordsLB->thaw();
654 m_pWordsLB->scroll_to_row(nEntry);
656 // if the request came from the ReplaceEdit, give focus to the ShortEdit
657 if (m_xReplaceED->has_focus())
658 m_xWordED->grab_focus();
661 else
663 // this can only be an enter in one of the two edit fields
664 // which means EndDialog() - has to be evaluated in KeyInput
665 return false;
667 ModifyHdl(*m_xWordED);
668 return true;
671 IMPL_LINK(SvxEditDictionaryDialog, ModifyHdl, weld::Entry&, rEdt, void)
673 OUString rEntry = rEdt.get_text();
675 sal_Int32 nWordLen = rEntry.getLength();
676 const OUString aRepString = fixSpace(m_xReplaceED->get_text());
678 bool bEnableNewReplace = false;
679 bool bEnableDelete = false;
680 OUString aNewReplaceText = sNew;
682 if (&rEdt == m_xWordED.get())
684 if(nWordLen>0)
686 bool bFound = false;
687 bool bTmpSelEntry=false;
688 CDE_RESULT eCmpRes = CDE_DIFFERENT;
690 bool bDoubleColumn = m_pWordsLB == m_xDoubleColumnLB.get();
692 for (int i = 0, nCount = m_pWordsLB->n_children(); i < nCount; ++i)
694 OUString aTestStr(m_pWordsLB->get_text(i, 0));
695 eCmpRes = cmpDicEntry_Impl( rEntry, aTestStr );
696 if(CDE_DIFFERENT != eCmpRes)
698 if(!aRepString.isEmpty())
699 bFirstSelect = true;
700 bDoNothing=true;
701 m_pWordsLB->set_cursor(i);
702 bDoNothing=false;
703 if (bDoubleColumn)
704 m_xReplaceED->set_text(m_pWordsLB->get_text(i, 1));
706 if (CDE_SIMILAR == eCmpRes)
708 aNewReplaceText = sModify;
709 bEnableNewReplace = true;
711 bFound= true;
712 break;
714 else if(getNormDicEntry_Impl(aTestStr).indexOf(
715 getNormDicEntry_Impl( rEntry ) ) == 0
716 && !bTmpSelEntry)
718 bDoNothing=true;
719 m_pWordsLB->scroll_to_row(i);
720 bDoNothing=false;
721 bTmpSelEntry=true;
723 aNewReplaceText = sNew;
724 bEnableNewReplace = true;
728 if(!bFound)
730 m_pWordsLB->unselect_all();
731 aNewReplaceText = sNew;
732 bEnableNewReplace = true;
734 bEnableDelete = CDE_DIFFERENT != eCmpRes;
736 else if (m_pWordsLB->n_children() > 0)
738 bDoNothing=true;
739 m_pWordsLB->scroll_to_row(0);
740 bDoNothing=false;
743 else if(&rEdt == m_xReplaceED.get())
745 OUString aReplaceText;
746 OUString aWordText;
747 int nFirstSel = m_pWordsLB->get_selected_index();
748 if (nFirstSel != -1) // a m_pWordsLB entry is selected
750 aWordText = m_pWordsLB->get_text(nFirstSel, 0);
751 aReplaceText = m_pWordsLB->get_text(nFirstSel, 1);
753 aNewReplaceText = sModify;
754 bEnableDelete = true;
756 bool bIsChange =
757 CDE_EQUAL != cmpDicEntry_Impl(fixSpace(m_xWordED->get_text()), aWordText)
758 || CDE_EQUAL != cmpDicEntry_Impl(fixSpace(m_xReplaceED->get_text()), aReplaceText);
759 if (!fixSpace(m_xWordED->get_text()).isEmpty() && bIsChange)
760 bEnableNewReplace = true;
763 m_xNewReplacePB->set_label(aNewReplaceText);
764 m_xNewReplacePB->set_sensitive(bEnableNewReplace && !IsDicReadonly_Impl());
765 m_xDeletePB->set_sensitive(bEnableDelete && !IsDicReadonly_Impl());
768 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */