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 <tools/debug.hxx>
21 #include <vcl/stdtext.hxx>
22 #include <vcl/svapp.hxx>
23 #include <vcl/weld.hxx>
24 #include <dialmgr.hxx>
25 #include <sfx2/app.hxx>
26 #include <svx/fmsrccfg.hxx>
27 #include <svx/fmsrcimp.hxx>
28 #include <strings.hrc>
29 #include <cuifmsearch.hxx>
30 #include <svl/cjkoptions.hxx>
31 #include <comphelper/processfactory.hxx>
32 #include <comphelper/string.hxx>
33 #include <svx/svxdlg.hxx>
34 
35 using namespace css::uno;
36 using namespace css::i18n;
37 using namespace ::svxform;
38 using namespace css::sdbc;
39 using namespace css::util;
40 
41 #define MAX_HISTORY_ENTRIES     50
42 
initCommon(const Reference<XResultSet> & _rxCursor)43 void FmSearchDialog::initCommon( const Reference< XResultSet >& _rxCursor )
44 {
45     // init the engine
46     DBG_ASSERT( m_pSearchEngine, "FmSearchDialog::initCommon: have no engine!" );
47     m_pSearchEngine->SetProgressHandler(LINK(this, FmSearchDialog, OnSearchProgress));
48 
49     // some layout changes according to available CJK options
50     SvtCJKOptions aCJKOptions;
51     if (!aCJKOptions.IsJapaneseFindEnabled())
52     {
53         // hide the options for the japanese search
54         m_pSoundsLikeCJK->hide();
55         m_pSoundsLikeCJKSettings->hide();
56     }
57 
58     if (!aCJKOptions.IsCJKFontEnabled())
59     {
60         m_pHalfFullFormsCJK->hide();
61 
62         // never ignore the width (ignoring is expensive) if the option is not available at all
63         m_pSearchEngine->SetIgnoreWidthCJK( false );
64     }
65 
66     // some initial record texts
67     m_pftRecord->set_label( OUString::number(_rxCursor->getRow()) );
68     m_pbClose->set_tooltip_text(OUString());
69 }
70 
FmSearchDialog(weld::Window * pParent,const OUString & sInitialText,const std::vector<OUString> & _rContexts,sal_Int16 nInitialContext,const Link<FmSearchContext &,sal_uInt32> & lnkContextSupplier)71 FmSearchDialog::FmSearchDialog(weld::Window* pParent, const OUString& sInitialText, const std::vector< OUString >& _rContexts, sal_Int16 nInitialContext,
72     const Link<FmSearchContext&,sal_uInt32>& lnkContextSupplier)
73     : GenericDialogController(pParent, "cui/ui/fmsearchdialog.ui", "RecordSearchDialog")
74     , m_sCancel( GetStandardText( StandardButtonType::Cancel ) )
75     , m_lnkContextSupplier(lnkContextSupplier)
76     , m_prbSearchForText(m_xBuilder->weld_radio_button("rbSearchForText"))
77     , m_prbSearchForNull(m_xBuilder->weld_radio_button("rbSearchForNull"))
78     , m_prbSearchForNotNull(m_xBuilder->weld_radio_button("rbSearchForNotNull"))
79     , m_pcmbSearchText(m_xBuilder->weld_combo_box("cmbSearchText"))
80     , m_pftForm(m_xBuilder->weld_label("ftForm"))
81     , m_plbForm(m_xBuilder->weld_combo_box("lbForm"))
82     , m_prbAllFields(m_xBuilder->weld_radio_button("rbAllFields"))
83     , m_prbSingleField(m_xBuilder->weld_radio_button("rbSingleField"))
84     , m_plbField(m_xBuilder->weld_combo_box("lbField"))
85     , m_pftPosition(m_xBuilder->weld_label("ftPosition"))
86     , m_plbPosition(m_xBuilder->weld_combo_box("lbPosition"))
87     , m_pcbUseFormat(m_xBuilder->weld_check_button("cbUseFormat"))
88     , m_pcbCase(m_xBuilder->weld_check_button("cbCase"))
89     , m_pcbBackwards(m_xBuilder->weld_check_button("cbBackwards"))
90     , m_pcbStartOver(m_xBuilder->weld_check_button("cbStartOver"))
91     , m_pcbWildCard(m_xBuilder->weld_check_button("cbWildCard"))
92     , m_pcbRegular(m_xBuilder->weld_check_button("cbRegular"))
93     , m_pcbApprox(m_xBuilder->weld_check_button("cbApprox"))
94     , m_ppbApproxSettings(m_xBuilder->weld_button("pbApproxSettings"))
95     , m_pHalfFullFormsCJK(m_xBuilder->weld_check_button("HalfFullFormsCJK"))
96     , m_pSoundsLikeCJK(m_xBuilder->weld_check_button("SoundsLikeCJK"))
97     , m_pSoundsLikeCJKSettings(m_xBuilder->weld_button("SoundsLikeCJKSettings"))
98     , m_pftRecord(m_xBuilder->weld_label("ftRecord"))
99     , m_pftHint(m_xBuilder->weld_label("ftHint"))
100     , m_pbSearchAgain(m_xBuilder->weld_button("pbSearchAgain"))
101     , m_pbClose(m_xBuilder->weld_button("close"))
102 {
103     m_pcmbSearchText->set_size_request(m_pcmbSearchText->get_approximate_digit_width() * 38, -1);
104     m_plbForm->set_size_request(m_plbForm->get_approximate_digit_width() * 38, -1);
105     m_sSearch = m_pbSearchAgain->get_label();
106 
107     DBG_ASSERT(m_lnkContextSupplier.IsSet(), "FmSearchDialog::FmSearchDialog : have no ContextSupplier !");
108 
109     FmSearchContext fmscInitial;
110     fmscInitial.nContext = nInitialContext;
111     m_lnkContextSupplier.Call(fmscInitial);
112     DBG_ASSERT(fmscInitial.xCursor.is(), "FmSearchDialog::FmSearchDialog : invalid data supplied by ContextSupplier !");
113     DBG_ASSERT(comphelper::string::getTokenCount(fmscInitial.strUsedFields, ';') == static_cast<sal_Int32>(fmscInitial.arrFields.size()),
114         "FmSearchDialog::FmSearchDialog : invalid data supplied by ContextSupplied !");
115 #if (OSL_DEBUG_LEVEL > 1) || defined DBG_UTIL
116     for (const Reference<XInterface> & arrField : fmscInitial.arrFields)
117     {
118         DBG_ASSERT(arrField.is(), "FmSearchDialog::FmSearchDialog : invalid data supplied by ContextSupplier !");
119     }
120 #endif // (OSL_DEBUG_LEVEL > 1) || DBG_UTIL
121 
122     for (   std::vector< OUString >::const_iterator context = _rContexts.begin();
123             context != _rContexts.end();
124             ++context
125         )
126     {
127         m_arrContextFields.emplace_back();
128         m_plbForm->append_text(*context);
129     }
130     m_plbForm->set_active(nInitialContext);
131 
132     m_plbForm->connect_changed(LINK(this, FmSearchDialog, OnContextSelection));
133 
134     if (m_arrContextFields.size() == 1)
135     {
136         // hide dispensable controls
137         m_pftForm->hide();
138         m_plbForm->hide();
139     }
140 
141     m_pSearchEngine.reset( new FmSearchEngine(
142         ::comphelper::getProcessComponentContext(), fmscInitial.xCursor, fmscInitial.strUsedFields, fmscInitial.arrFields ) );
143     initCommon( fmscInitial.xCursor );
144 
145     if ( !fmscInitial.sFieldDisplayNames.isEmpty() )
146     {   // use the display names if supplied
147         DBG_ASSERT(comphelper::string::getTokenCount(fmscInitial.sFieldDisplayNames, ';') == comphelper::string::getTokenCount(fmscInitial.strUsedFields, ';'),
148             "FmSearchDialog::FmSearchDialog : invalid initial context description !");
149         Init(fmscInitial.sFieldDisplayNames, sInitialText);
150     }
151     else
152         Init(fmscInitial.strUsedFields, sInitialText);
153 }
154 
~FmSearchDialog()155 FmSearchDialog::~FmSearchDialog()
156 {
157     SaveParams();
158 
159     m_pConfig.reset();
160     m_pSearchEngine.reset();
161 }
162 
Init(const OUString & strVisibleFields,const OUString & sInitialText)163 void FmSearchDialog::Init(const OUString& strVisibleFields, const OUString& sInitialText)
164 {
165     //the initialization of all the Controls
166     m_prbSearchForText->connect_clicked(LINK(this, FmSearchDialog, OnClickedFieldRadios));
167     m_prbSearchForNull->connect_clicked(LINK(this, FmSearchDialog, OnClickedFieldRadios));
168     m_prbSearchForNotNull->connect_clicked(LINK(this, FmSearchDialog, OnClickedFieldRadios));
169 
170     m_prbAllFields->connect_clicked(LINK(this, FmSearchDialog, OnClickedFieldRadios));
171     m_prbSingleField->connect_clicked(LINK(this, FmSearchDialog, OnClickedFieldRadios));
172 
173     m_pbSearchAgain->connect_clicked(LINK(this, FmSearchDialog, OnClickedSearchAgain));
174     m_ppbApproxSettings->connect_clicked(LINK(this, FmSearchDialog, OnClickedSpecialSettings));
175     m_pSoundsLikeCJKSettings->connect_clicked(LINK(this, FmSearchDialog, OnClickedSpecialSettings));
176 
177     m_plbPosition->connect_changed(LINK(this, FmSearchDialog, OnPositionSelected));
178     m_plbField->connect_changed(LINK(this, FmSearchDialog, OnFieldSelected));
179 
180     m_pcmbSearchText->connect_changed(LINK(this, FmSearchDialog, OnSearchTextModified));
181     m_pcmbSearchText->set_entry_completion(false);
182     m_pcmbSearchText->connect_focus_in(LINK(this, FmSearchDialog, OnFocusGrabbed));
183 
184     m_pcbUseFormat->connect_toggled(LINK(this, FmSearchDialog, OnCheckBoxToggled));
185     m_pcbBackwards->connect_toggled(LINK(this, FmSearchDialog, OnCheckBoxToggled));
186     m_pcbStartOver->connect_toggled(LINK(this, FmSearchDialog, OnCheckBoxToggled));
187     m_pcbCase->connect_toggled(LINK(this, FmSearchDialog, OnCheckBoxToggled));
188     m_pcbWildCard->connect_toggled(LINK(this, FmSearchDialog, OnCheckBoxToggled));
189     m_pcbRegular->connect_toggled(LINK(this, FmSearchDialog, OnCheckBoxToggled));
190     m_pcbApprox->connect_toggled(LINK(this, FmSearchDialog, OnCheckBoxToggled));
191     m_pHalfFullFormsCJK->connect_toggled(LINK(this, FmSearchDialog, OnCheckBoxToggled));
192     m_pSoundsLikeCJK->connect_toggled(LINK(this, FmSearchDialog, OnCheckBoxToggled));
193 
194     // fill the listboxes
195     // method of field comparison
196     const char* const aResIds[] = {
197         RID_STR_SEARCH_ANYWHERE,
198         RID_STR_SEARCH_BEGINNING,
199         RID_STR_SEARCH_END,
200         RID_STR_SEARCH_WHOLE
201     };
202     for (auto pResId : aResIds)
203         m_plbPosition->append_text(CuiResId(pResId));
204     m_plbPosition->set_active(MATCHING_ANYWHERE);
205 
206     // the field listbox
207     if (!strVisibleFields.isEmpty())
208     {
209         sal_Int32 nPos {0};
210         do {
211             m_plbField->append_text(strVisibleFields.getToken(0, ';', nPos));
212         } while (nPos>=0);
213     }
214 
215 
216     m_pConfig.reset( new FmSearchConfigItem );
217     LoadParams();
218 
219     m_pcmbSearchText->set_entry_text(sInitialText);
220     // if the Edit-line has changed the text (e.g. because it contains
221     // control characters, as can be the case with memo fields), I use
222     // an empty OUString.
223     OUString sRealSetText = m_pcmbSearchText->get_active_text();
224     if (sRealSetText != sInitialText)
225         m_pcmbSearchText->set_entry_text(OUString());
226     OnSearchTextModified(*m_pcmbSearchText);
227 
228     // initial
229     EnableSearchUI(true);
230 
231     if ( m_prbSearchForText->get_active() )
232         m_pcmbSearchText->grab_focus();
233 
234 }
235 
run()236 short FmSearchDialog::run()
237 {
238     short nRet = weld::GenericDialogController::run();
239     m_pSearchEngine->CancelSearch();
240     return nRet;
241 }
242 
IMPL_LINK(FmSearchDialog,OnClickedFieldRadios,weld::Button &,rButton,void)243 IMPL_LINK(FmSearchDialog, OnClickedFieldRadios, weld::Button&, rButton, void)
244 {
245     if ((&rButton == m_prbSearchForText.get()) || (&rButton == m_prbSearchForNull.get()) || (&rButton == m_prbSearchForNotNull.get()))
246     {
247         EnableSearchForDependees(true);
248     }
249     else
250         // en- or disable field list box accordingly
251         if (&rButton == m_prbSingleField.get())
252         {
253             m_plbField->set_sensitive(true);
254             m_pSearchEngine->RebuildUsedFields(m_plbField->get_active());
255         }
256         else
257         {
258             m_plbField->set_sensitive(false);
259             m_pSearchEngine->RebuildUsedFields(-1);
260         }
261 }
262 
IMPL_LINK_NOARG(FmSearchDialog,OnClickedSearchAgain,weld::Button &,void)263 IMPL_LINK_NOARG(FmSearchDialog, OnClickedSearchAgain, weld::Button&, void)
264 {
265     if (m_pbClose->get_sensitive())
266     {   // the button has the function 'search'
267         OUString strThisRoundText = m_pcmbSearchText->get_active_text();
268         // to history
269         m_pcmbSearchText->remove_text(strThisRoundText);
270         m_pcmbSearchText->insert_text(0, strThisRoundText);
271         // the remove/insert makes sure that a) the OUString does not appear twice and
272         // that b) the last searched strings are at the beginning and limit the list length
273         while (m_pcmbSearchText->get_count() > MAX_HISTORY_ENTRIES)
274             m_pcmbSearchText->remove(m_pcmbSearchText->get_count()-1);
275 
276         // take out the 'overflow' hint
277         m_pftHint->set_label(OUString());
278 
279         if (m_pcbStartOver->get_active())
280         {
281             m_pcbStartOver->set_active(false);
282             EnableSearchUI(false);
283             if (m_prbSearchForText->get_active())
284                 m_pSearchEngine->StartOver(strThisRoundText);
285             else
286                 m_pSearchEngine->StartOverSpecial(m_prbSearchForNull->get_active());
287         }
288         else
289         {
290             EnableSearchUI(false);
291             if (m_prbSearchForText->get_active())
292                 m_pSearchEngine->SearchNext(strThisRoundText);
293             else
294                 m_pSearchEngine->SearchNextSpecial(m_prbSearchForNull->get_active());
295         }
296     }
297     else
298     {   // the button has the function 'cancel'
299             // the CancelButton is usually only disabled, when working in a thread or with reschedule
300         m_pSearchEngine->CancelSearch();
301             // the ProgressHandler is called when it's really finished, here it's only a demand
302     }
303 }
304 
IMPL_LINK(FmSearchDialog,OnClickedSpecialSettings,weld::Button &,rButton,void)305 IMPL_LINK(FmSearchDialog, OnClickedSpecialSettings, weld::Button&, rButton, void)
306 {
307     if (m_ppbApproxSettings.get() == &rButton)
308     {
309         SvxAbstractDialogFactory* pFact = SvxAbstractDialogFactory::Create();
310         ScopedVclPtr<AbstractSvxSearchSimilarityDialog> pDlg(pFact->CreateSvxSearchSimilarityDialog(m_xDialog.get(), m_pSearchEngine->GetLevRelaxed(), m_pSearchEngine->GetLevOther(),
311                     m_pSearchEngine->GetLevShorter(), m_pSearchEngine->GetLevLonger() ));
312         if (pDlg->Execute() == RET_OK)
313         {
314             m_pSearchEngine->SetLevRelaxed( pDlg->IsRelaxed() );
315             m_pSearchEngine->SetLevOther( pDlg->GetOther() );
316             m_pSearchEngine->SetLevShorter(pDlg->GetShorter() );
317             m_pSearchEngine->SetLevLonger( pDlg->GetLonger() );
318         }
319     }
320     else if (m_pSoundsLikeCJKSettings.get() == &rButton)
321     {
322         SfxItemSet aSet( SfxGetpApp()->GetPool() );
323         SvxAbstractDialogFactory* pFact = SvxAbstractDialogFactory::Create();
324         ScopedVclPtr<AbstractSvxJSearchOptionsDialog> aDlg(pFact->CreateSvxJSearchOptionsDialog(m_xDialog.get(), aSet, m_pSearchEngine->GetTransliterationFlags() ));
325         aDlg->Execute();
326 
327         TransliterationFlags nFlags = aDlg->GetTransliterationFlags();
328         m_pSearchEngine->SetTransliterationFlags(nFlags);
329 
330         m_pcbCase->set_active(m_pSearchEngine->GetCaseSensitive());
331         OnCheckBoxToggled( *m_pcbCase );
332         m_pHalfFullFormsCJK->set_active( !m_pSearchEngine->GetIgnoreWidthCJK() );
333         OnCheckBoxToggled( *m_pHalfFullFormsCJK );
334     }
335 }
336 
IMPL_LINK_NOARG(FmSearchDialog,OnSearchTextModified,weld::ComboBox &,void)337 IMPL_LINK_NOARG(FmSearchDialog, OnSearchTextModified, weld::ComboBox&, void)
338 {
339     if ((!m_pcmbSearchText->get_active_text().isEmpty()) || !m_prbSearchForText->get_active())
340         m_pbSearchAgain->set_sensitive(true);
341     else
342         m_pbSearchAgain->set_sensitive(false);
343 
344     m_pSearchEngine->InvalidatePreviousLoc();
345 }
346 
IMPL_LINK_NOARG(FmSearchDialog,OnFocusGrabbed,weld::Widget &,void)347 IMPL_LINK_NOARG(FmSearchDialog, OnFocusGrabbed, weld::Widget&, void)
348 {
349     m_pcmbSearchText->select_entry_region(0, -1);
350 }
351 
IMPL_LINK_NOARG(FmSearchDialog,OnPositionSelected,weld::ComboBox &,void)352 IMPL_LINK_NOARG(FmSearchDialog, OnPositionSelected, weld::ComboBox&, void)
353 {
354     m_pSearchEngine->SetPosition(m_plbPosition->get_active());
355 }
356 
IMPL_LINK_NOARG(FmSearchDialog,OnFieldSelected,weld::ComboBox &,void)357 IMPL_LINK_NOARG(FmSearchDialog, OnFieldSelected, weld::ComboBox&, void)
358 {
359     m_pSearchEngine->RebuildUsedFields(m_prbAllFields->get_active() ? -1 : m_plbField->get_active());
360     // calls m_pSearchEngine->InvalidatePreviousLoc too
361 
362     int nCurrentContext = m_plbForm->get_active();
363     if (nCurrentContext != -1)
364         m_arrContextFields[nCurrentContext] = m_plbField->get_active_text();
365 }
366 
IMPL_LINK(FmSearchDialog,OnCheckBoxToggled,weld::ToggleButton &,rBox,void)367 IMPL_LINK(FmSearchDialog, OnCheckBoxToggled, weld::ToggleButton&, rBox, void)
368 {
369     bool bChecked = rBox.get_active();
370 
371     // formatter or case -> pass on to the engine
372     if (&rBox == m_pcbUseFormat.get())
373         m_pSearchEngine->SetFormatterUsing(bChecked);
374     else if (&rBox == m_pcbCase.get())
375         m_pSearchEngine->SetCaseSensitive(bChecked);
376     // direction -> pass on and reset the checkbox-text for StartOver
377     else if (&rBox == m_pcbBackwards.get())
378     {
379         m_pcbStartOver->set_label( CuiResId( bChecked ? RID_STR_FROM_BOTTOM : RID_STR_FROM_TOP ) );
380         m_pSearchEngine->SetDirection(!bChecked);
381     }
382     // similarity-search or regular expression
383     else if ((&rBox == m_pcbApprox.get()) || (&rBox == m_pcbRegular.get()) || (&rBox == m_pcbWildCard.get()))
384     {
385         weld::CheckButton* pBoxes[] = { m_pcbWildCard.get(), m_pcbRegular.get(), m_pcbApprox.get() };
386         for (weld::CheckButton* pBoxe : pBoxes)
387         {
388             if (pBoxe != &rBox)
389             {
390                 if (bChecked)
391                     pBoxe->set_sensitive(false);
392                 else
393                     pBoxe->set_sensitive(true);
394             }
395         }
396 
397         // pass on to the engine
398         m_pSearchEngine->SetWildcard(m_pcbWildCard->get_sensitive() && m_pcbWildCard->get_active());
399         m_pSearchEngine->SetRegular(m_pcbRegular->get_sensitive() && m_pcbRegular->get_active());
400         m_pSearchEngine->SetLevenshtein(m_pcbApprox->get_sensitive() && m_pcbApprox->get_active());
401             // (disabled boxes have to be passed to the engine as sal_False)
402 
403         // adjust the Position-Listbox (which is not allowed during Wildcard-search)
404         if (&rBox == m_pcbWildCard.get())
405         {
406             if (bChecked)
407             {
408                 m_pftPosition->set_sensitive(false);
409                 m_plbPosition->set_sensitive(false);
410             }
411             else
412             {
413                 m_pftPosition->set_sensitive(true);
414                 m_plbPosition->set_sensitive(true);
415             }
416         }
417 
418         // and the button for similarity-search
419         if (&rBox == m_pcbApprox.get())
420         {
421             if (bChecked)
422                 m_ppbApproxSettings->set_sensitive(true);
423             else
424                 m_ppbApproxSettings->set_sensitive(false);
425         }
426     }
427     else if (&rBox == m_pHalfFullFormsCJK.get())
428     {
429         // forward to the search engine
430         m_pSearchEngine->SetIgnoreWidthCJK( !bChecked );
431     }
432     else if (&rBox == m_pSoundsLikeCJK.get())
433     {
434         m_pSoundsLikeCJKSettings->set_sensitive(bChecked);
435 
436         // two other buttons which depend on this one
437         bool bEnable =  (   m_prbSearchForText->get_active()
438                             &&  !m_pSoundsLikeCJK->get_active()
439                             )
440                          || !SvtCJKOptions().IsJapaneseFindEnabled();
441         m_pcbCase->set_sensitive(bEnable);
442         m_pHalfFullFormsCJK->set_sensitive(bEnable);
443 
444         // forward to the search engine
445         m_pSearchEngine->SetTransliteration( bChecked );
446     }
447 }
448 
InitContext(sal_Int16 nContext)449 void FmSearchDialog::InitContext(sal_Int16 nContext)
450 {
451     FmSearchContext fmscContext;
452     fmscContext.nContext = nContext;
453 
454     sal_uInt32 nResult = m_lnkContextSupplier.Call(fmscContext);
455     DBG_ASSERT(nResult > 0, "FmSearchDialog::InitContext : ContextSupplier didn't give me any controls !");
456 
457     // put the field names into the respective listbox
458     m_plbField->clear();
459 
460     if (!fmscContext.sFieldDisplayNames.isEmpty())
461     {
462         // use the display names if supplied
463         DBG_ASSERT(comphelper::string::getTokenCount(fmscContext.sFieldDisplayNames, ';') == comphelper::string::getTokenCount(fmscContext.strUsedFields, ';'),
464             "FmSearchDialog::InitContext : invalid context description supplied !");
465         sal_Int32 nPos {0};
466         do {
467             m_plbField->append_text(fmscContext.sFieldDisplayNames.getToken(0, ';', nPos));
468         } while (nPos>=0);
469     }
470     else if (!fmscContext.strUsedFields.isEmpty())
471     {
472         // else use the field names
473         sal_Int32 nPos {0};
474         do {
475             m_plbField->append_text(fmscContext.strUsedFields.getToken(0, ';', nPos));
476         } while (nPos>=0);
477     }
478 
479     if (nContext < static_cast<sal_Int32>(m_arrContextFields.size()) && !m_arrContextFields[nContext].isEmpty())
480     {
481         m_plbField->set_active_text(m_arrContextFields[nContext]);
482     }
483     else
484     {
485         m_plbField->set_active(0);
486         if (m_prbSingleField->get_active() && (m_plbField->get_count() > 1))
487             m_plbField->grab_focus();
488     }
489 
490     m_pSearchEngine->SwitchToContext(fmscContext.xCursor, fmscContext.strUsedFields, fmscContext.arrFields,
491         m_prbAllFields->get_active() ? -1 : 0);
492 
493     m_pftRecord->set_label(OUString::number(fmscContext.xCursor->getRow()));
494 }
495 
IMPL_LINK(FmSearchDialog,OnContextSelection,weld::ComboBox &,rBox,void)496 IMPL_LINK(FmSearchDialog, OnContextSelection, weld::ComboBox&, rBox, void)
497 {
498     InitContext(rBox.get_active());
499 }
500 
EnableSearchUI(bool bEnable)501 void FmSearchDialog::EnableSearchUI(bool bEnable)
502 {
503     // the search button has two functions -> adjust its text accordingly
504     OUString sButtonText( bEnable ? m_sSearch : m_sCancel );
505     m_pbSearchAgain->set_label(sButtonText);
506 
507     m_prbSearchForText->set_sensitive(bEnable);
508     m_prbSearchForNull->set_sensitive(bEnable);
509     m_prbSearchForNotNull->set_sensitive(bEnable);
510     m_plbForm->set_sensitive(bEnable);
511     m_prbAllFields->set_sensitive(bEnable);
512     m_prbSingleField->set_sensitive(bEnable);
513     m_plbField->set_sensitive(bEnable && m_prbSingleField->get_active());
514     m_pcbBackwards->set_sensitive(bEnable);
515     m_pcbStartOver->set_sensitive(bEnable);
516     m_pbClose->set_sensitive(bEnable);
517     EnableSearchForDependees(bEnable);
518 
519     if ( !bEnable )
520     {   // this means we're preparing for starting a search
521         // In this case, EnableSearchForDependees disabled the search button
522         // But as we're about to use it for cancelling the search, we really need to enable it, again
523         m_pbSearchAgain->set_sensitive(true);
524     }
525 }
526 
EnableSearchForDependees(bool bEnable)527 void FmSearchDialog::EnableSearchForDependees(bool bEnable)
528 {
529     bool bSearchingForText = m_prbSearchForText->get_active();
530     m_pbSearchAgain->set_sensitive(bEnable && (!bSearchingForText || (!m_pcmbSearchText->get_active_text().isEmpty())));
531 
532     bEnable = bEnable && bSearchingForText;
533 
534     bool bEnableRedundants = !m_pSoundsLikeCJK->get_active() || !SvtCJKOptions().IsJapaneseFindEnabled();
535 
536     m_pcmbSearchText->set_sensitive(bEnable);
537     m_pftPosition->set_sensitive(bEnable && !m_pcbWildCard->get_active());
538     m_pcbWildCard->set_sensitive(bEnable && !m_pcbRegular->get_active() && !m_pcbApprox->get_active());
539     m_pcbRegular->set_sensitive(bEnable && !m_pcbWildCard->get_active() && !m_pcbApprox->get_active());
540     m_pcbApprox->set_sensitive(bEnable && !m_pcbWildCard->get_active() && !m_pcbRegular->get_active());
541     m_ppbApproxSettings->set_sensitive(bEnable && m_pcbApprox->get_active());
542     m_pHalfFullFormsCJK->set_sensitive(bEnable && bEnableRedundants);
543     m_pSoundsLikeCJK->set_sensitive(bEnable);
544     m_pSoundsLikeCJKSettings->set_sensitive(bEnable && m_pSoundsLikeCJK->get_active());
545     m_plbPosition->set_sensitive(bEnable && !m_pcbWildCard->get_active());
546     m_pcbUseFormat->set_sensitive(bEnable);
547     m_pcbCase->set_sensitive(bEnable && bEnableRedundants);
548 }
549 
OnFound(const css::uno::Any & aCursorPos,sal_Int16 nFieldPos)550 void FmSearchDialog::OnFound(const css::uno::Any& aCursorPos, sal_Int16 nFieldPos)
551 {
552     FmFoundRecordInformation friInfo;
553     friInfo.nContext = m_plbForm->get_active();
554     // if I don't do a search in a context, this has an invalid value - but then it doesn't matter anyway
555     friInfo.aPosition = aCursorPos;
556     if (m_prbAllFields->get_active())
557         friInfo.nFieldPos = nFieldPos;
558     else
559         friInfo.nFieldPos = m_plbField->get_active();
560         // this of course implies that I have really searched in the field that is selected in the listbox,
561         // which is made sure in RebuildUsedFields
562 
563     m_lnkFoundHandler.Call(friInfo);
564 
565     m_pcmbSearchText->grab_focus();
566 }
567 
IMPL_LINK(FmSearchDialog,OnSearchProgress,const FmSearchProgress *,pProgress,void)568 IMPL_LINK(FmSearchDialog, OnSearchProgress, const FmSearchProgress*, pProgress, void)
569 {
570     SolarMutexGuard aGuard;
571         // make this single method thread-safe (it's an overkill to block the whole application for this,
572         // but we don't have another safety concept at the moment)
573 
574     switch (pProgress->aSearchState)
575     {
576         case FmSearchProgress::State::Progress:
577             if (pProgress->bOverflow)
578             {
579                 OUString sHint( CuiResId( m_pcbBackwards->get_active() ? RID_STR_OVERFLOW_BACKWARD : RID_STR_OVERFLOW_FORWARD ) );
580                 m_pftHint->set_label( sHint );
581             }
582 
583             m_pftRecord->set_label(OUString::number(1 + pProgress->nCurrentRecord));
584             break;
585 
586         case FmSearchProgress::State::ProgressCounting:
587             m_pftHint->set_label(CuiResId(RID_STR_SEARCH_COUNTING));
588             m_pftRecord->set_label(OUString::number(pProgress->nCurrentRecord));
589             break;
590 
591         case FmSearchProgress::State::Successful:
592             OnFound(pProgress->aBookmark, static_cast<sal_Int16>(pProgress->nFieldIndex));
593             EnableSearchUI(true);
594             break;
595 
596         case FmSearchProgress::State::Error:
597         case FmSearchProgress::State::NothingFound:
598         {
599             const char* pErrorId = (FmSearchProgress::State::Error == pProgress->aSearchState)
600                 ? RID_STR_SEARCH_GENERAL_ERROR
601                 : RID_STR_SEARCH_NORECORD;
602             std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(m_xDialog.get(),
603                                                       VclMessageType::Warning, VclButtonsType::Ok, CuiResId(pErrorId)));
604             xBox->run();
605             [[fallthrough]];
606         }
607         case FmSearchProgress::State::Canceled:
608             EnableSearchUI(true);
609             if (m_lnkCanceledNotFoundHdl.IsSet())
610             {
611                 FmFoundRecordInformation friInfo;
612                 friInfo.nContext = m_plbForm->get_active();
613                 // if I don't do a search in a context, this has an invalid value - but then it doesn't matter anyway
614                 friInfo.aPosition = pProgress->aBookmark;
615                 m_lnkCanceledNotFoundHdl.Call(friInfo);
616             }
617             break;
618     }
619 
620     m_pftRecord->set_label(OUString::number(1 + pProgress->nCurrentRecord));
621 }
622 
LoadParams()623 void FmSearchDialog::LoadParams()
624 {
625     FmSearchParams aParams(m_pConfig->getParams());
626 
627     const OUString* pHistory     =                   aParams.aHistory.getConstArray();
628     const OUString* pHistoryEnd  =   pHistory    +   aParams.aHistory.getLength();
629     for (; pHistory != pHistoryEnd; ++pHistory)
630         m_pcmbSearchText->append_text( *pHistory );
631 
632     // I do the settings at my UI-elements and then I simply call the respective change-handler,
633     // that way the data is handed on to the SearchEngine and all dependent settings are done
634 
635     // current field
636     int nInitialField = m_plbField->find_text( aParams.sSingleSearchField );
637     if (nInitialField == -1)
638         nInitialField = 0;
639     m_plbField->set_active(nInitialField);
640     OnFieldSelected(*m_plbField);
641     // all fields/single field (AFTER selecting the field because OnClickedFieldRadios expects a valid value there)
642     if (aParams.bAllFields)
643     {
644         m_prbSingleField->set_active(false);
645         m_prbAllFields->set_active(true);
646         OnClickedFieldRadios(*m_prbAllFields);
647         // OnClickedFieldRadios also calls to RebuildUsedFields
648     }
649     else
650     {
651         m_prbAllFields->set_active(false);
652         m_prbSingleField->set_active(true);
653         OnClickedFieldRadios(*m_prbSingleField);
654     }
655 
656     m_plbPosition->set_active(aParams.nPosition);
657     OnPositionSelected(*m_plbPosition);
658 
659     // field formatting/case sensitivity/direction
660     m_pcbUseFormat->set_active(aParams.bUseFormatter);
661     m_pcbCase->set_active( aParams.isCaseSensitive() );
662     m_pcbBackwards->set_active(aParams.bBackwards);
663     OnCheckBoxToggled(*m_pcbUseFormat);
664     OnCheckBoxToggled(*m_pcbCase);
665     OnCheckBoxToggled(*m_pcbBackwards);
666 
667     m_pHalfFullFormsCJK->set_active( !aParams.isIgnoreWidthCJK( ) );  // BEWARE: this checkbox has an inverse semantics!
668     m_pSoundsLikeCJK->set_active( aParams.bSoundsLikeCJK );
669     OnCheckBoxToggled(*m_pHalfFullFormsCJK);
670     OnCheckBoxToggled(*m_pSoundsLikeCJK);
671 
672     m_pcbWildCard->set_active(false);
673     m_pcbRegular->set_active(false);
674     m_pcbApprox->set_active(false);
675     OnCheckBoxToggled(*m_pcbWildCard);
676     OnCheckBoxToggled(*m_pcbRegular);
677     OnCheckBoxToggled(*m_pcbApprox);
678 
679     weld::CheckButton* pToCheck = nullptr;
680     if (aParams.bWildcard)
681         pToCheck = m_pcbWildCard.get();
682     if (aParams.bRegular)
683         pToCheck = m_pcbRegular.get();
684     if (aParams.bApproxSearch)
685         pToCheck = m_pcbApprox.get();
686     if (aParams.bSoundsLikeCJK)
687         pToCheck = m_pSoundsLikeCJK.get();
688     if (pToCheck)
689     {
690         pToCheck->set_active(true);
691         OnCheckBoxToggled(*pToCheck);
692     }
693 
694     // set Levenshtein-parameters directly at the SearchEngine
695     m_pSearchEngine->SetLevRelaxed(aParams.bLevRelaxed);
696     m_pSearchEngine->SetLevOther(aParams.nLevOther);
697     m_pSearchEngine->SetLevShorter(aParams.nLevShorter);
698     m_pSearchEngine->SetLevLonger(aParams.nLevLonger);
699 
700     m_pSearchEngine->SetTransliterationFlags( aParams.getTransliterationFlags( ) );
701 
702     m_prbSearchForText->set_active(false);
703     m_prbSearchForNull->set_active(false);
704     m_prbSearchForNotNull->set_active(false);
705     switch (aParams.nSearchForType)
706     {
707         case 1: m_prbSearchForNull->set_active(true); break;
708         case 2: m_prbSearchForNotNull->set_active(true); break;
709         default: m_prbSearchForText->set_active(true); break;
710     }
711     OnClickedFieldRadios(*m_prbSearchForText);
712 }
713 
SaveParams() const714 void FmSearchDialog::SaveParams() const
715 {
716     if (!m_pConfig)
717         return;
718 
719     FmSearchParams aCurrentSettings;
720 
721     int nCount = m_pcmbSearchText->get_count();
722     aCurrentSettings.aHistory.realloc(nCount);
723     OUString* pHistory = aCurrentSettings.aHistory.getArray();
724     for (int i = 0; i < nCount; ++i, ++pHistory)
725         *pHistory = m_pcmbSearchText->get_text(i);
726 
727     aCurrentSettings.sSingleSearchField         = m_plbField->get_active_text();
728     aCurrentSettings.bAllFields                 = m_prbAllFields->get_active();
729     aCurrentSettings.nPosition                  = m_pSearchEngine->GetPosition();
730     aCurrentSettings.bUseFormatter              = m_pSearchEngine->GetFormatterUsing();
731     aCurrentSettings.setCaseSensitive           ( m_pSearchEngine->GetCaseSensitive() );
732     aCurrentSettings.bBackwards                 = !m_pSearchEngine->GetDirection();
733     aCurrentSettings.bWildcard                  = m_pSearchEngine->GetWildcard();
734     aCurrentSettings.bRegular                   = m_pSearchEngine->GetRegular();
735     aCurrentSettings.bApproxSearch              = m_pSearchEngine->GetLevenshtein();
736     aCurrentSettings.bLevRelaxed                = m_pSearchEngine->GetLevRelaxed();
737     aCurrentSettings.nLevOther                  = m_pSearchEngine->GetLevOther();
738     aCurrentSettings.nLevShorter                = m_pSearchEngine->GetLevShorter();
739     aCurrentSettings.nLevLonger                 = m_pSearchEngine->GetLevLonger();
740 
741     aCurrentSettings.bSoundsLikeCJK             = m_pSearchEngine->GetTransliteration();
742     aCurrentSettings.setTransliterationFlags    ( m_pSearchEngine->GetTransliterationFlags() );
743 
744     if (m_prbSearchForNull->get_active())
745         aCurrentSettings.nSearchForType = 1;
746     else if (m_prbSearchForNotNull->get_active())
747         aCurrentSettings.nSearchForType = 2;
748     else
749         aCurrentSettings.nSearchForType = 0;
750 
751     m_pConfig->setParams( aCurrentSettings );
752 }
753 
754 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
755