LibreOffice Module svx (master)  1
fmsrcimp.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 
21 #include <rtl/strbuf.hxx>
22 #include <sal/log.hxx>
23 #include <svx/fmtools.hxx>
24 #include <svx/fmsrccfg.hxx>
25 #include <tools/debug.hxx>
26 #include <tools/diagnose_ex.h>
27 #include <tools/wldcrd.hxx>
28 #include <vcl/svapp.hxx>
29 #include <unotools/textsearch.hxx>
30 #include <com/sun/star/awt/XTextComponent.hpp>
31 #include <com/sun/star/awt/XListBox.hpp>
32 #include <com/sun/star/awt/XCheckBox.hpp>
33 #include <com/sun/star/container/XIndexAccess.hpp>
34 #include <com/sun/star/util/SearchAlgorithms2.hpp>
35 #include <com/sun/star/util/SearchResult.hpp>
36 #include <com/sun/star/util/SearchFlags.hpp>
37 #include <com/sun/star/lang/Locale.hpp>
38 #include <com/sun/star/i18n/CollatorOptions.hpp>
39 
40 #include <com/sun/star/sdb/XColumn.hpp>
41 #include <com/sun/star/sdbc/SQLException.hpp>
42 #include <com/sun/star/sdbc/XConnection.hpp>
43 #include <com/sun/star/sdbc/XDatabaseMetaData.hpp>
44 #include <com/sun/star/sdbcx/XColumnsSupplier.hpp>
45 #include <com/sun/star/util/NumberFormatter.hpp>
46 #include <com/sun/star/util/NumberFormat.hpp>
47 #include <com/sun/star/util/XNumberFormatsSupplier.hpp>
48 #include <com/sun/star/util/XNumberFormats.hpp>
49 
50 #include <fmprop.hxx>
51 #include <fmservs.hxx>
52 #include <svx/fmsrcimp.hxx>
53 #include <svx/fmsearch.hxx>
54 
55 #include <comphelper/types.hxx>
56 #include <unotools/syslocale.hxx>
57 #include <i18nutil/searchopt.hxx>
58 
59 #define EQUAL_BOOKMARKS(a, b) a == b
60 
61 #define IFACECAST(c) static_cast<const Reference< XInterface >&>(c)
62 
63 using namespace ::com::sun::star::uno;
64 using namespace ::com::sun::star::util;
65 using namespace ::com::sun::star::lang;
66 using namespace ::com::sun::star::sdbc;
67 using namespace ::com::sun::star::i18n;
68 using namespace ::com::sun::star::beans;
69 using namespace ::svxform;
70 
71 
72 // = FmRecordCountListener
73 
74 // SMART_UNO_IMPLEMENTATION(FmRecordCountListener, UsrObject);
75 
76 
77 FmRecordCountListener::FmRecordCountListener(const Reference< css::sdbc::XResultSet > & dbcCursor)
78 {
79 
80  m_xListening.set(dbcCursor, UNO_QUERY);
81  if (!m_xListening.is())
82  return;
83 
84  if (::comphelper::getBOOL(m_xListening->getPropertyValue(FM_PROP_ROWCOUNTFINAL)))
85  {
86  m_xListening = nullptr;
87  // there's nothing to do as the record count is already known
88  return;
89  }
90 
91  m_xListening->addPropertyChangeListener(FM_PROP_ROWCOUNT, static_cast<css::beans::XPropertyChangeListener*>(this));
92 }
93 
94 
96 {
97  m_lnkWhoWantsToKnow = lnk;
98 
99  if (m_xListening.is())
101 }
102 
103 
105 {
106 
107 }
108 
109 
111 {
112  if(m_xListening.is())
113  m_xListening->removePropertyChangeListener(FM_PROP_ROWCOUNT, static_cast<css::beans::XPropertyChangeListener*>(this));
114  m_xListening = nullptr;
115 }
116 
117 
118 void SAL_CALL FmRecordCountListener::disposing(const css::lang::EventObject& /*Source*/)
119 {
120  DBG_ASSERT(m_xListening.is(), "FmRecordCountListener::disposing should never have been called without a propset !");
121  DisConnect();
122 }
123 
124 
126 {
127  if (m_lnkWhoWantsToKnow.IsSet())
128  {
129  DBG_ASSERT(m_xListening.is(), "FmRecordCountListener::NotifyCurrentCount : I have no propset ... !?");
130  sal_Int32 theCount = ::comphelper::getINT32(m_xListening->getPropertyValue(FM_PROP_ROWCOUNT));
131  m_lnkWhoWantsToKnow.Call(theCount);
132  }
133 }
134 
135 
136 void FmRecordCountListener::propertyChange(const css::beans::PropertyChangeEvent& /*evt*/)
137 {
139 }
140 
141 
142 // FmSearchEngine - local classes
143 
144 SimpleTextWrapper::SimpleTextWrapper(const Reference< css::awt::XTextComponent > & _xText)
145  :ControlTextWrapper(_xText.get())
146  ,m_xText(_xText)
147 {
148  DBG_ASSERT(m_xText.is(), "FmSearchEngine::SimpleTextWrapper::SimpleTextWrapper : invalid argument !");
149 }
150 
151 
153 {
154  return m_xText->getText();
155 }
156 
157 
158 ListBoxWrapper::ListBoxWrapper(const Reference< css::awt::XListBox > & _xBox)
159  :ControlTextWrapper(_xBox.get())
160  ,m_xBox(_xBox)
161 {
162  DBG_ASSERT(m_xBox.is(), "FmSearchEngine::ListBoxWrapper::ListBoxWrapper : invalid argument !");
163 }
164 
165 
167 {
168  return m_xBox->getSelectedItem();
169 }
170 
171 
172 CheckBoxWrapper::CheckBoxWrapper(const Reference< css::awt::XCheckBox > & _xBox)
173  :ControlTextWrapper(_xBox.get())
174  ,m_xBox(_xBox)
175 {
176  DBG_ASSERT(m_xBox.is(), "FmSearchEngine::CheckBoxWrapper::CheckBoxWrapper : invalid argument !");
177 }
178 
179 
181 {
182  switch (static_cast<TriState>(m_xBox->getState()))
183  {
184  case TRISTATE_FALSE: return "0";
185  case TRISTATE_TRUE: return "1";
186  default: break;
187  }
188  return OUString();
189 }
190 
191 
192 // = FmSearchEngine
193 
195 {
196  bool bSuccess = true;
197  try
198  {
199  if (m_bForward)
200  if (m_xSearchCursor.isLast())
201  m_xSearchCursor.first();
202  else
203  m_xSearchCursor.next();
204  else
205  if (m_xSearchCursor.isFirst())
206  {
207  rtl::Reference<FmRecordCountListener> prclListener = new FmRecordCountListener(m_xSearchCursor);
208  prclListener->SetPropChangeHandler(LINK(this, FmSearchEngine, OnNewRecordCount));
209 
210  m_xSearchCursor.last();
211 
212  prclListener->DisConnect();
213  }
214  else
215  m_xSearchCursor.previous();
216  }
217  catch(Exception const&)
218  {
219  TOOLS_WARN_EXCEPTION( "svx", "FmSearchEngine::MoveCursor");
220  bSuccess = false;
221  }
222  catch(...)
223  {
224  OSL_FAIL("FmSearchEngine::MoveCursor : caught an unknown Exception !");
225  bSuccess = false;
226  }
227 
228  return bSuccess;
229 }
230 
231 
232 bool FmSearchEngine::MoveField(sal_Int32& nPos, FieldCollection::iterator& iter, const FieldCollection::iterator& iterBegin, const FieldCollection::iterator& iterEnd)
233 {
234  bool bSuccess(true);
235  if (m_bForward)
236  {
237  ++iter;
238  ++nPos;
239  if (iter == iterEnd)
240  {
241  bSuccess = MoveCursor();
242  iter = iterBegin;
243  nPos = 0;
244  }
245  } else
246  {
247  if (iter == iterBegin)
248  {
249  bSuccess = MoveCursor();
250  iter = iterEnd;
251  nPos = iter-iterBegin;
252  }
253  --iter;
254  --nPos;
255  }
256  return bSuccess;
257 }
258 
259 
260 void FmSearchEngine::BuildAndInsertFieldInfo(const Reference< css::container::XIndexAccess > & xAllFields, sal_Int32 nField)
261 {
262  DBG_ASSERT( xAllFields.is() && ( nField >= 0 ) && ( nField < xAllFields->getCount() ),
263  "FmSearchEngine::BuildAndInsertFieldInfo: invalid field descriptor!" );
264 
265  // the field itself
266  Reference< XInterface > xCurrentField;
267  xAllFields->getByIndex(nField) >>= xCurrentField;
268 
269  // From this I now know that it supports the DatabaseRecord service (I hope).
270  // For the FormatKey and the type I need the PropertySet.
271  Reference< css::beans::XPropertySet > xProperties(xCurrentField, UNO_QUERY);
272 
273  // build the FieldInfo for that
274  FieldInfo fiCurrent;
275  fiCurrent.xContents.set(xCurrentField, UNO_QUERY);
276 
277  // and memorize
278  m_arrUsedFields.insert(m_arrUsedFields.end(), fiCurrent);
279 
280 }
281 
282 OUString FmSearchEngine::FormatField(sal_Int32 nWhich)
283 {
284  DBG_ASSERT(static_cast<sal_uInt32>(nWhich) < m_aControlTexts.size(), "FmSearchEngine::FormatField(sal_Int32) : invalid position !");
285  DBG_ASSERT(m_aControlTexts[nWhich], "FmSearchEngine::FormatField(sal_Int32) : invalid object in array !");
286  DBG_ASSERT(m_aControlTexts[nWhich]->getControl().is(), "FmSearchEngine::FormatField : invalid control !");
287 
288  if (m_nCurrentFieldIndex != -1)
289  {
290  DBG_ASSERT((nWhich == 0) || (nWhich == m_nCurrentFieldIndex), "FmSearchEngine::FormatField : parameter nWhich is invalid");
291  // analogous situation as below
292  nWhich = m_nCurrentFieldIndex;
293  }
294 
295  DBG_ASSERT((nWhich >= 0) && (static_cast<sal_uInt32>(nWhich) < m_aControlTexts.size()),
296  "FmSearchEngine::FormatField : invalid argument nWhich !");
297  return m_aControlTexts[m_nCurrentFieldIndex == -1 ? nWhich : m_nCurrentFieldIndex]->getCurrentText();
298 }
299 
300 
301 FmSearchEngine::SearchResult FmSearchEngine::SearchSpecial(bool _bSearchForNull, sal_Int32& nFieldPos,
302  FieldCollection::iterator& iterFieldLoop, const FieldCollection::iterator& iterBegin, const FieldCollection::iterator& iterEnd)
303 {
304  // memorize the start position
305  Any aStartMark;
306  try { aStartMark = m_xSearchCursor.getBookmark(); }
307  catch ( const Exception& ) { DBG_UNHANDLED_EXCEPTION("svx"); return SearchResult::Error; }
308  FieldCollection::const_iterator iterInitialField = iterFieldLoop;
309 
310 
311  bool bFound(false);
312  bool bMovedAround(false);
313  do
314  {
315  Application::Reschedule( true );
316 
317  // the content to be compared currently
318  iterFieldLoop->xContents->getString(); // needed for wasNull
319  bFound = _bSearchForNull == bool(iterFieldLoop->xContents->wasNull());
320  if (bFound)
321  break;
322 
323  // next field (implicitly next record, if necessary)
324  if (!MoveField(nFieldPos, iterFieldLoop, iterBegin, iterEnd))
325  { // When moving to the next field, something went wrong...
326  // Continuing is not possible, since the next time exactly the same
327  // will definitely go wrong again, thus abort.
328  // Before, however, so that the search continues at the current position:
329  try { m_aPreviousLocBookmark = m_xSearchCursor.getBookmark(); }
330  catch ( const Exception& ) { DBG_UNHANDLED_EXCEPTION("svx"); }
331  m_iterPreviousLocField = iterFieldLoop;
332  // and leave
333  return SearchResult::Error;
334  }
335 
336  Any aCurrentBookmark;
337  try { aCurrentBookmark = m_xSearchCursor.getBookmark(); }
338  catch ( const Exception& ) { DBG_UNHANDLED_EXCEPTION("svx"); return SearchResult::Error; }
339 
340  bMovedAround = EQUAL_BOOKMARKS(aStartMark, aCurrentBookmark) && (iterFieldLoop == iterInitialField);
341 
342  if (nFieldPos == 0)
343  // that is, I've moved to a new record
344  PropagateProgress(bMovedAround);
345  // if we moved to the starting position we don't have to propagate an 'overflow' message
346  // FS - 07.12.99 - 68530
347 
348  // cancel requested?
349  if (CancelRequested())
350  return SearchResult::Cancelled;
351 
352  } while (!bMovedAround);
353 
354  return bFound ? SearchResult::Found : SearchResult::NotFound;
355 }
356 
357 
358 FmSearchEngine::SearchResult FmSearchEngine::SearchWildcard(const OUString& strExpression, sal_Int32& nFieldPos,
359  FieldCollection::iterator& iterFieldLoop, const FieldCollection::iterator& iterBegin, const FieldCollection::iterator& iterEnd)
360 {
361  // memorize the start position
362  Any aStartMark;
363  try { aStartMark = m_xSearchCursor.getBookmark(); }
364  catch ( const Exception& ) { DBG_UNHANDLED_EXCEPTION("svx"); return SearchResult::Error; }
365  FieldCollection::const_iterator iterInitialField = iterFieldLoop;
366 
367  WildCard aSearchExpression(strExpression);
368 
369 
370  bool bFound(false);
371  bool bMovedAround(false);
372  do
373  {
374  Application::Reschedule( true );
375 
376  // the content to be compared currently
377  OUString sCurrentCheck;
378  if (m_bFormatter)
379  sCurrentCheck = FormatField(nFieldPos);
380  else
381  sCurrentCheck = iterFieldLoop->xContents->getString();
382 
383  if (!GetCaseSensitive())
384  // norm the string
385  sCurrentCheck = m_aCharacterClassficator.lowercase(sCurrentCheck);
386 
387  // now the test is easy...
388  bFound = aSearchExpression.Matches(sCurrentCheck);
389 
390  if (bFound)
391  break;
392 
393  // next field (implicitly next record, if necessary)
394  if (!MoveField(nFieldPos, iterFieldLoop, iterBegin, iterEnd))
395  { // When moving to the next field, something went wrong...
396  // Continuing is not possible, since the next time exactly the same
397  // will definitely go wrong again, thus abort.
398  // Before, however, so that the search continues at the current position:
399  try { m_aPreviousLocBookmark = m_xSearchCursor.getBookmark(); }
400  catch ( const Exception& ) { DBG_UNHANDLED_EXCEPTION("svx"); }
401  m_iterPreviousLocField = iterFieldLoop;
402  // and leave
403  return SearchResult::Error;
404  }
405 
406  Any aCurrentBookmark;
407  try { aCurrentBookmark = m_xSearchCursor.getBookmark(); }
408  catch ( const Exception& ) { DBG_UNHANDLED_EXCEPTION("svx"); return SearchResult::Error; }
409 
410  bMovedAround = EQUAL_BOOKMARKS(aStartMark, aCurrentBookmark) && (iterFieldLoop == iterInitialField);
411 
412  if (nFieldPos == 0)
413  // that is, I've moved to a new record
414  PropagateProgress(bMovedAround);
415  // if we moved to the starting position we don't have to propagate an 'overflow' message
416  // FS - 07.12.99 - 68530
417 
418  // cancel requested?
419  if (CancelRequested())
420  return SearchResult::Cancelled;
421 
422  } while (!bMovedAround);
423 
424  return bFound ? SearchResult::Found : SearchResult::NotFound;
425 }
426 
427 
428 FmSearchEngine::SearchResult FmSearchEngine::SearchRegularApprox(const OUString& strExpression, sal_Int32& nFieldPos,
429  FieldCollection::iterator& iterFieldLoop, const FieldCollection::iterator& iterBegin, const FieldCollection::iterator& iterEnd)
430 {
431  DBG_ASSERT(m_bLevenshtein || m_bRegular,
432  "FmSearchEngine::SearchRegularApprox : invalid search mode!");
433  DBG_ASSERT(!m_bLevenshtein || !m_bRegular,
434  "FmSearchEngine::SearchRegularApprox : cannot search for regular expressions and similarities at the same time!");
435 
436  // memorize start position
437  Any aStartMark;
438  try { aStartMark = m_xSearchCursor.getBookmark(); }
439  catch ( const Exception& ) { DBG_UNHANDLED_EXCEPTION("svx"); return SearchResult::Error; }
440  FieldCollection::const_iterator iterInitialField = iterFieldLoop;
441 
442  // collect parameters
444  aParam.AlgorithmType2 = m_bRegular ? SearchAlgorithms2::REGEXP : SearchAlgorithms2::APPROXIMATE;
445  aParam.searchFlag = 0;
446  aParam.transliterateFlags = GetTransliterationFlags();
447  if ( !GetTransliteration() )
448  { // if transliteration is not enabled, the only flags which matter are IGNORE_CASE and IGNORE_WIDTH
449  aParam.transliterateFlags &= TransliterationFlags::IGNORE_CASE | TransliterationFlags::IGNORE_WIDTH;
450  }
451  if (m_bLevenshtein)
452  {
453  if (m_bLevRelaxed)
454  aParam.searchFlag |= SearchFlags::LEV_RELAXED;
455  aParam.changedChars = m_nLevOther;
456  aParam.deletedChars = m_nLevShorter;
457  aParam.insertedChars = m_nLevLonger;
458  }
459  aParam.searchString = strExpression;
461  ::utl::TextSearch aLocalEngine( aParam);
462 
463 
464  bool bFound = false;
465  bool bMovedAround(false);
466  do
467  {
468  Application::Reschedule( true );
469 
470  // the content to be compared currently
471  OUString sCurrentCheck;
472  if (m_bFormatter)
473  sCurrentCheck = FormatField(nFieldPos);
474  else
475  sCurrentCheck = iterFieldLoop->xContents->getString();
476 
477  // (don't care about case here, this is done by the TextSearch object, 'cause we passed our case parameter to it)
478 
479  sal_Int32 nStart = 0, nEnd = sCurrentCheck.getLength();
480  bFound = aLocalEngine.SearchForward(sCurrentCheck, &nStart, &nEnd);
481  // it says 'forward' here, but that only refers to the search within
482  // sCurrentCheck, so it has nothing to do with the direction of my
483  // record migration (MoveField takes care of that)
484 
485  // check if the position is correct
486  if (bFound)
487  {
488  switch (m_nPosition)
489  {
490  case MATCHING_WHOLETEXT :
491  if (nEnd != sCurrentCheck.getLength())
492  {
493  bFound = false;
494  break;
495  }
496  [[fallthrough]];
497  case MATCHING_BEGINNING :
498  if (nStart != 0)
499  bFound = false;
500  break;
501  case MATCHING_END :
502  if (nEnd != sCurrentCheck.getLength())
503  bFound = false;
504  break;
505  }
506  }
507 
508  if (bFound) // still?
509  break;
510 
511  // next field (implicitly next record, if necessary)
512  if (!MoveField(nFieldPos, iterFieldLoop, iterBegin, iterEnd))
513  { // When moving to the next field, something went wrong...
514  // Continuing is not possible, since the next time exactly the same
515  // will definitely go wrong again, thus abort (without error
516  // notification, I expect it to be displayed in the Move).
517  // Before, however, so that the search continues at the current position:
518  try { m_aPreviousLocBookmark = m_xSearchCursor.getBookmark(); }
519  catch ( const Exception& ) { DBG_UNHANDLED_EXCEPTION("svx"); }
520  m_iterPreviousLocField = iterFieldLoop;
521  // and leave
522  return SearchResult::Error;
523  }
524 
525  Any aCurrentBookmark;
526  try { aCurrentBookmark = m_xSearchCursor.getBookmark(); }
527  catch ( const Exception& ) { DBG_UNHANDLED_EXCEPTION("svx"); return SearchResult::Error; }
528  bMovedAround = EQUAL_BOOKMARKS(aStartMark, aCurrentBookmark) && (iterFieldLoop == iterInitialField);
529 
530  if (nFieldPos == 0)
531  // that is, I've moved to a new record
532  PropagateProgress(bMovedAround);
533  // if we moved to the starting position we don't have to propagate an 'overflow' message
534  // FS - 07.12.99 - 68530
535 
536  // cancel requested?
537  if (CancelRequested())
538  return SearchResult::Cancelled;
539 
540  } while (!bMovedAround);
541 
542  return bFound ? SearchResult::Found : SearchResult::NotFound;
543 }
544 
545 
547  const Reference< XResultSet > & xCursor, const OUString& sVisibleFields,
548  const InterfaceArray& arrFields)
549  :m_xSearchCursor(xCursor)
550  ,m_aCharacterClassficator( _rxContext, SvtSysLocale().GetLanguageTag() )
551  ,m_aStringCompare( _rxContext )
552  ,m_nCurrentFieldIndex(-2) // -1 already has a meaning, so I take -2 for 'invalid'
553  ,m_xOriginalIterator(xCursor)
554  ,m_xClonedIterator(m_xOriginalIterator, true)
555  ,m_eSearchForType(SearchFor::String)
556  ,m_srResult(SearchResult::Found)
557  ,m_bSearchingCurrently(false)
558  ,m_bCancelAsynchRequest(false)
559  ,m_bFormatter(true) // this must be consistent with m_xSearchCursor, which is generally == m_xOriginalIterator
560  ,m_bForward(false)
561  ,m_bWildcard(false)
562  ,m_bRegular(false)
563  ,m_bLevenshtein(false)
564  ,m_bTransliteration(false)
565  ,m_bLevRelaxed(false)
566  ,m_nLevOther(0)
567  ,m_nLevShorter(0)
568  ,m_nLevLonger(0)
570  ,m_nTransliterationFlags(TransliterationFlags::NONE)
571 {
572 
573  fillControlTexts(arrFields);
574  Init(sVisibleFields);
575 }
576 
577 
579 {
580  if (bSet)
581  m_nTransliterationFlags |= TransliterationFlags::IGNORE_WIDTH;
582  else
584 }
585 
586 
588 {
589  return bool(m_nTransliterationFlags & TransliterationFlags::IGNORE_WIDTH);
590 }
591 
592 
594 {
595  if (bSet)
597  else
598  m_nTransliterationFlags |= TransliterationFlags::IGNORE_CASE;
599 }
600 
601 
603 {
604  return !(m_nTransliterationFlags & TransliterationFlags::IGNORE_CASE);
605 }
606 
607 
609 {
610  m_aControlTexts.clear();
611 }
612 
613 
615 {
617  Reference< XInterface > xCurrent;
618  for (const auto & rField : arrFields)
619  {
620  xCurrent = rField;
621  DBG_ASSERT(xCurrent.is(), "FmSearchEngine::fillControlTexts : invalid field interface !");
622  // check which type of control this is
623  Reference< css::awt::XTextComponent > xAsText(xCurrent, UNO_QUERY);
624  if (xAsText.is())
625  {
626  m_aControlTexts.emplace_back(new SimpleTextWrapper(xAsText));
627  continue;
628  }
629 
630  Reference< css::awt::XListBox > xAsListBox(xCurrent, UNO_QUERY);
631  if (xAsListBox.is())
632  {
633  m_aControlTexts.emplace_back(new ListBoxWrapper(xAsListBox));
634  continue;
635  }
636 
637  Reference< css::awt::XCheckBox > xAsCheckBox(xCurrent, UNO_QUERY);
638  DBG_ASSERT(xAsCheckBox.is(), "FmSearchEngine::fillControlTexts : invalid field interface (no supported type) !");
639  // we don't have any more options ...
640  m_aControlTexts.emplace_back(new CheckBoxWrapper(xAsCheckBox));
641  }
642 }
643 
644 
645 void FmSearchEngine::Init(const OUString& sVisibleFields)
646 {
647  // analyze the fields
648  // additionally, create the mapping: because the list of used columns can be shorter than the list
649  // of columns of the cursor, we need a mapping: "used column number n" -> "cursor column m"
650  m_arrFieldMapping.clear();
651 
652  // important: The case of the columns does not need to be exact - for instance:
653  // - a user created a form which works on a table, for which the driver returns a column name "COLUMN"
654  // - the driver itself works case-insensitive with column names
655  // - a control in the form is bound to "column" - not the different case
656  // In such a scenario, the form and the field would work okay, but we here need to case for the different case
657  // explicitly
658  // #i8755#
659 
660  // so first of all, check if the database handles identifiers case sensitive
661  Reference< XConnection > xConn;
662  Reference< XDatabaseMetaData > xMeta;
663  Reference< XPropertySet > xCursorProps( IFACECAST( m_xSearchCursor ), UNO_QUERY );
664  if ( xCursorProps.is() )
665  {
666  try
667  {
668  xCursorProps->getPropertyValue( FM_PROP_ACTIVE_CONNECTION ) >>= xConn;
669  }
670  catch( const Exception& ) { /* silent this - will be asserted below */ }
671  }
672  if ( xConn.is() )
673  xMeta = xConn->getMetaData();
674  OSL_ENSURE( xMeta.is(), "FmSearchEngine::Init: very strange cursor (could not derive connection meta data from it)!" );
675 
676  bool bCaseSensitiveIdentifiers = true; // assume case sensitivity
677  if ( xMeta.is() )
678  bCaseSensitiveIdentifiers = xMeta->supportsMixedCaseQuotedIdentifiers();
679 
680  // now that we have this information, we need a collator which is able to case (in)sensitivity compare strings
681  m_aStringCompare.loadDefaultCollator( SvtSysLocale().GetLanguageTag().getLocale(),
682  bCaseSensitiveIdentifiers ? 0 : css::i18n::CollatorOptions::CollatorOptions_IGNORE_CASE );
683 
684  try
685  {
686  // the cursor can give me a record (as PropertySet), which supports the DatabaseRecord service
687  Reference< css::sdbcx::XColumnsSupplier > xSupplyCols(IFACECAST(m_xSearchCursor), UNO_QUERY);
688  DBG_ASSERT(xSupplyCols.is(), "FmSearchEngine::Init : invalid cursor (no columns supplier) !");
689  Reference< css::container::XNameAccess > xAllFieldNames = xSupplyCols->getColumns();
690  Sequence< OUString > seqFieldNames = xAllFieldNames->getElementNames();
691 
692  OUString sCurrentField;
693  sal_Int32 nIndex = 0;
694  do
695  {
696  sCurrentField = sVisibleFields.getToken(0, ';' , nIndex);
697 
698  // search in the field collection
699  sal_Int32 nFoundIndex = -1;
700  auto pFieldName = std::find_if(seqFieldNames.begin(), seqFieldNames.end(),
701  [this, &sCurrentField](const OUString& rFieldName) {
702  return 0 == m_aStringCompare.compareString( rFieldName, sCurrentField ); });
703  if (pFieldName != seqFieldNames.end())
704  nFoundIndex = static_cast<sal_Int32>(std::distance(seqFieldNames.begin(), pFieldName));
705  DBG_ASSERT(nFoundIndex != -1, "FmSearchEngine::Init : Invalid field name were given !");
706  m_arrFieldMapping.push_back(nFoundIndex);
707  }
708  while ( nIndex >= 0 );
709  }
710  catch (const Exception&)
711  {
712  OSL_FAIL("Exception occurred!");
713  }
714 
715 }
716 
717 
719 {
720  if (m_bFormatter == bSet)
721  return;
722  m_bFormatter = bSet;
723 
724  // I did not use a formatter, but TextComponents -> the SearchIterator needs to be adjusted
725  try
726  {
727  if (m_bFormatter)
728  {
729  DBG_ASSERT(m_xSearchCursor == m_xClonedIterator, "FmSearchEngine::SetFormatterUsing : inconsistent state !");
732  // so that I continue with the new iterator at the actual place where I previously stopped
733  }
734  else
735  {
736  DBG_ASSERT(m_xSearchCursor == m_xOriginalIterator, "FmSearchEngine::SetFormatterUsing : inconsistent state !");
739  }
740  }
741  catch( const Exception& )
742  {
744  }
745 
746  // I have to re-bind the fields, because the text exchange might take
747  // place over these fields and the underlying cursor has changed
749 }
750 
751 
752 void FmSearchEngine::PropagateProgress(bool _bDontPropagateOverflow)
753 {
755  {
756  FmSearchProgress aProgress;
757  try
758  {
760  aProgress.nCurrentRecord = m_xSearchCursor.getRow() - 1;
761  if (m_bForward)
762  aProgress.bOverflow = !_bDontPropagateOverflow && m_xSearchCursor.isFirst();
763  else
764  aProgress.bOverflow = !_bDontPropagateOverflow && m_xSearchCursor.isLast();
765  }
766  catch( const Exception& )
767  {
769  }
770 
771  m_aProgressHandler.Call(&aProgress);
772  }
773 }
774 
775 
777 {
779  "FmSearchEngine::SearchNextImpl : search parameters are mutually exclusive!");
780 
781  DBG_ASSERT(m_xSearchCursor.is(), "FmSearchEngine::SearchNextImpl : have invalid iterator!");
782 
783  // the parameters of the search
784  OUString strSearchExpression(m_strSearchExpression); // I need non-const
785  if (!GetCaseSensitive())
786  // norm the string
787  strSearchExpression = m_aCharacterClassficator.lowercase(strSearchExpression);
788 
789  if (!m_bRegular && !m_bLevenshtein)
790  { // 'normal' search I run through WildCards in any case, but must before adjust the OUString depending on the mode
791 
792  if (!m_bWildcard)
793  { // since in all other cases * and ? in the search string are of course
794  // also allowed, but should not count as WildCards, I need to normalize
795  OUString aTmp(strSearchExpression);
796  const OUString s_sStar("\\*");
797  const OUString s_sQuotation("\\?");
798  aTmp = aTmp.replaceAll("*", s_sStar);
799  aTmp = aTmp.replaceAll("?", s_sQuotation);
800  strSearchExpression = aTmp;
801 
802  switch (m_nPosition)
803  {
804  case MATCHING_ANYWHERE :
805  strSearchExpression = "*" + strSearchExpression + "*";
806  break;
807  case MATCHING_BEGINNING :
808  strSearchExpression += "*";
809  break;
810  case MATCHING_END :
811  strSearchExpression = "*" + strSearchExpression;
812  break;
813  case MATCHING_WHOLETEXT :
814  break;
815  default :
816  OSL_FAIL("FmSearchEngine::SearchNextImpl() : the methods listbox may contain only 4 entries ...");
817  }
818  }
819  }
820 
821  // for work on field list
822  FieldCollection::iterator iterBegin = m_arrUsedFields.begin();
823  FieldCollection::iterator iterEnd = m_arrUsedFields.end();
824  FieldCollection::iterator iterFieldCheck;
825 
826  sal_Int32 nFieldPos;
827 
828  if (m_aPreviousLocBookmark.hasValue())
829  {
831  "FmSearchEngine::SearchNextImpl : invalid position!");
832  iterFieldCheck = m_iterPreviousLocField;
833  // continue in the field after (or before) the last discovery
834  nFieldPos = iterFieldCheck - iterBegin;
835  MoveField(nFieldPos, iterFieldCheck, iterBegin, iterEnd);
836  }
837  else
838  {
839  if (m_bForward)
840  iterFieldCheck = iterBegin;
841  else
842  {
843  iterFieldCheck = iterEnd;
844  --iterFieldCheck;
845  }
846  nFieldPos = iterFieldCheck - iterBegin;
847  }
848 
849  PropagateProgress(true);
850  SearchResult srResult;
852  srResult = SearchSpecial(m_eSearchForType == SearchFor::Null, nFieldPos, iterFieldCheck, iterBegin, iterEnd);
853  else if (!m_bRegular && !m_bLevenshtein)
854  srResult = SearchWildcard(strSearchExpression, nFieldPos, iterFieldCheck, iterBegin, iterEnd);
855  else
856  srResult = SearchRegularApprox(strSearchExpression, nFieldPos, iterFieldCheck, iterBegin, iterEnd);
857 
858  m_srResult = srResult;
859 
861  return;
862 
863  // found?
865  {
866  // memorize the position
868  catch ( const Exception& ) { DBG_UNHANDLED_EXCEPTION("svx"); }
869  m_iterPreviousLocField = iterFieldCheck;
870  }
871  else
872  // invalidate the "last discovery"
874 }
875 
876 
878 {
879  if (!m_aProgressHandler.IsSet())
880  return;
881 
882  FmSearchProgress aProgress;
883  try
884  {
885  switch (m_srResult)
886  {
887  case SearchResult::Error :
889  break;
890  case SearchResult::Found :
892  aProgress.aBookmark = m_aPreviousLocBookmark;
893  aProgress.nFieldIndex = m_iterPreviousLocField - m_arrUsedFields.begin();
894  break;
897  aProgress.aBookmark = m_xSearchCursor.getBookmark();
898  break;
901  aProgress.aBookmark = m_xSearchCursor.getBookmark();
902  break;
903  }
904  aProgress.nCurrentRecord = m_xSearchCursor.getRow() - 1;
905  }
906  catch( const Exception& )
907  {
909  }
910 
911  // by definition, the link must be thread-safe (I just require that),
912  // so that I do not have to worry about such things here
913  m_aProgressHandler.Call(&aProgress);
914 
915  m_bSearchingCurrently = false;
916 }
917 
918 
919 IMPL_LINK(FmSearchEngine, OnNewRecordCount, sal_Int32, theCounter, void)
920 {
921  if (!m_aProgressHandler.IsSet())
922  return;
923 
924  FmSearchProgress aProgress;
925  aProgress.nCurrentRecord = theCounter;
927  m_aProgressHandler.Call(&aProgress);
928 }
929 
930 
932 {
933  m_aCancelAsynchAccess.acquire();
934  bool bReturn = m_bCancelAsynchRequest;
935  m_aCancelAsynchAccess.release();
936  return bReturn;
937 }
938 
939 
941 {
942  m_aCancelAsynchAccess.acquire();
943  m_bCancelAsynchRequest = true;
944  m_aCancelAsynchAccess.release();
945 }
946 
947 
948 void FmSearchEngine::SwitchToContext(const Reference< css::sdbc::XResultSet > & xCursor, const OUString& sVisibleFields, const InterfaceArray& arrFields,
949  sal_Int32 nFieldIndex)
950 {
951  DBG_ASSERT(!m_bSearchingCurrently, "FmSearchEngine::SwitchToContext : please do not call while I'm searching !");
953  return;
954 
955  m_xSearchCursor = xCursor;
956  m_xOriginalIterator = xCursor;
958 
959  fillControlTexts(arrFields);
960 
961  Init(sVisibleFields);
962  RebuildUsedFields(nFieldIndex, true);
963 }
964 
965 
967 {
968  m_bCancelAsynchRequest = false;
969  m_bSearchingCurrently = true;
970 
971  SearchNextImpl();
973 }
974 
975 
976 void FmSearchEngine::SearchNext(const OUString& strExpression)
977 {
978  m_strSearchExpression = strExpression;
981 }
982 
983 
984 void FmSearchEngine::SearchNextSpecial(bool _bSearchForNull)
985 {
988 }
989 
990 
991 void FmSearchEngine::StartOver(const OUString& strExpression)
992 {
993  try
994  {
995  if (m_bForward)
997  else
999  }
1000  catch( const Exception& )
1001  {
1002  DBG_UNHANDLED_EXCEPTION("svx");
1003  return;
1004  }
1005 
1007  SearchNext(strExpression);
1008 }
1009 
1010 
1011 void FmSearchEngine::StartOverSpecial(bool _bSearchForNull)
1012 {
1013  try
1014  {
1015  if (m_bForward)
1017  else
1019  }
1020  catch( const Exception& )
1021  {
1022  DBG_UNHANDLED_EXCEPTION("svx");
1023  return;
1024  }
1025 
1027  SearchNextSpecial(_bSearchForNull);
1028 }
1029 
1030 
1032 {
1033  m_aPreviousLocBookmark.clear();
1035 }
1036 
1037 
1038 void FmSearchEngine::RebuildUsedFields(sal_Int32 nFieldIndex, bool bForce)
1039 {
1040  if (!bForce && (nFieldIndex == m_nCurrentFieldIndex))
1041  return;
1042  // (since I allow no change of the iterator from the outside, the same css::sdbcx::Index
1043  // also always means the same column, so I have nothing to do)
1044 
1045  DBG_ASSERT((nFieldIndex == -1) ||
1046  ((nFieldIndex >= 0) &&
1047  (static_cast<size_t>(nFieldIndex) < m_arrFieldMapping.size())),
1048  "FmSearchEngine::RebuildUsedFields : nFieldIndex is invalid!");
1049  // collect all fields I need to search through
1050  m_arrUsedFields.clear();
1051  if (nFieldIndex == -1)
1052  {
1053  Reference< css::container::XIndexAccess > xFields;
1054  for (sal_Int32 i : m_arrFieldMapping)
1055  {
1056  Reference< css::sdbcx::XColumnsSupplier > xSupplyCols(IFACECAST(m_xSearchCursor), UNO_QUERY);
1057  DBG_ASSERT(xSupplyCols.is(), "FmSearchEngine::RebuildUsedFields : invalid cursor (no columns supplier) !");
1058  xFields.set(xSupplyCols->getColumns(), UNO_QUERY);
1059  BuildAndInsertFieldInfo(xFields, i);
1060  }
1061  }
1062  else
1063  {
1064  Reference< css::container::XIndexAccess > xFields;
1065  Reference< css::sdbcx::XColumnsSupplier > xSupplyCols(IFACECAST(m_xSearchCursor), UNO_QUERY);
1066  DBG_ASSERT(xSupplyCols.is(), "FmSearchEngine::RebuildUsedFields : invalid cursor (no columns supplier) !");
1067  xFields.set (xSupplyCols->getColumns(), UNO_QUERY);
1068  BuildAndInsertFieldInfo(xFields, m_arrFieldMapping[static_cast< size_t >(nFieldIndex)]);
1069  }
1070 
1071  m_nCurrentFieldIndex = nFieldIndex;
1072  // and of course I start the next search in a virgin state again
1074 }
1075 
1076 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
css::uno::Reference< css::linguistic2::XProofreadingIterator > get(css::uno::Reference< css::uno::XComponentContext > const &context)
#define MATCHING_END
Definition: fmsrccfg.hxx:31
sal_Int32 nIndex
TransliterationFlags m_nTransliterationFlags
Definition: fmsrcimp.hxx:213
ListBoxWrapper(const css::uno::Reference< css::awt::XListBox > &_xBox)
Definition: fmsrcimp.cxx:158
sal_Int32 compareString(const OUString &s1, const OUString &s2) const
sal_Int32 nFieldIndex
Definition: fmsrcimp.hxx:64
Link< const FmSearchProgress *, void > m_aProgressHandler
Definition: fmsrcimp.hxx:192
sal_Int32 getRow() const
Definition: fmtools.hxx:114
SVX_DLLPRIVATE bool MoveField(sal_Int32 &nPos, FieldCollection::iterator &iter, const FieldCollection::iterator &iterBegin, const FieldCollection::iterator &iterEnd)
Definition: fmsrcimp.cxx:232
void InvalidatePreviousLoc()
invalidate previous search reference
Definition: fmsrcimp.cxx:1031
FieldCollection::iterator m_iterPreviousLocField
Definition: fmsrcimp.hxx:184
css::uno::Any aBookmark
Definition: fmsrcimp.hxx:62
virtual OUString getCurrentText() const override
Definition: fmsrcimp.cxx:180
void Init(const OUString &strVisibleFields)
Definition: fmsrcimp.cxx:645
bool getBOOL(const Any &_rAny)
SVX_DLLPRIVATE void clearControlTexts()
Definition: fmsrcimp.cxx:608
#define FM_PROP_ROWCOUNTFINAL
Definition: fmprop.hxx:33
SVX_DLLPRIVATE SearchResult SearchRegularApprox(const OUString &strExpression, sal_Int32 &nFieldPos, FieldCollection::iterator &iterFieldLoop, const FieldCollection::iterator &iterBegin, const FieldCollection::iterator &iterEnd)
Definition: fmsrcimp.cxx:428
bool m_bSearchingCurrently
Definition: fmsrcimp.hxx:193
void StartOver(const OUString &strExpression)
search for the next appearance, dependent on nDirection from the start or end
Definition: fmsrcimp.cxx:991
void SwitchToContext(const css::uno::Reference< css::sdbc::XResultSet > &xCursor, const OUString &strVisibleFields, const InterfaceArray &arrFields, sal_Int32 nFieldIndex)
only valid, if not an (asynchronous) search is running, the next search will then be executed on top ...
Definition: fmsrcimp.cxx:948
CursorWrapper m_xOriginalIterator
Definition: fmsrcimp.hxx:179
bool isFirst() const
Definition: fmtools.hxx:109
bool isLast() const
Definition: fmtools.hxx:110
class FmRecordCountListener - utility class for FmSearchEngine, listens at a certain cursor and provi...
Definition: fmsrcimp.hxx:71
TRISTATE_TRUE
css::uno::Any m_aPreviousLocBookmark
Definition: fmsrcimp.hxx:183
SVX_DLLPRIVATE bool MoveCursor()
Definition: fmsrcimp.cxx:194
const css::lang::Locale & getLocale(bool bResolveSystem=true) const
NONE
void SearchNextSpecial(bool _bSearchForNull)
analogous, search for "NULL" (_bSearchForNull==sal_True) or "not NULL"
Definition: fmsrcimp.cxx:984
std::vector< css::uno::Reference< css::uno::XInterface > > InterfaceArray
Definition: fmsrcimp.hxx:149
CollatorWrapper m_aStringCompare
Definition: fmsrcimp.hxx:164
sal_uInt32 nCurrentRecord
Definition: fmsrcimp.hxx:57
State aSearchState
Definition: fmsrcimp.hxx:54
FieldCollection m_arrUsedFields
Definition: fmsrcimp.hxx:173
css::uno::Reference< css::awt::XCheckBox > m_xBox
Definition: fmsrcimp.hxx:142
OUString FormatField(sal_Int32 nWhich)
Definition: fmsrcimp.cxx:282
void SetCaseSensitive(bool bSet)
Definition: fmsrcimp.cxx:593
virtual void SAL_CALL propertyChange(const css::beans::PropertyChangeEvent &evt) override
Definition: fmsrcimp.cxx:136
SVX_DLLPRIVATE SearchResult SearchWildcard(const OUString &strExpression, sal_Int32 &nFieldPos, FieldCollection::iterator &iterFieldLoop, const FieldCollection::iterator &iterBegin, const FieldCollection::iterator &iterEnd)
Definition: fmsrcimp.cxx:358
#define EQUAL_BOOKMARKS(a, b)
Definition: fmsrcimp.cxx:59
IMPL_LINK(FmSearchEngine, OnNewRecordCount, sal_Int32, theCounter, void)
Definition: fmsrcimp.cxx:919
void CancelSearch()
returns directly; once it was really aborted, ProgressHandler is called with STATE_CANCELED ...
Definition: fmsrcimp.cxx:940
void SetFormatterUsing(bool bSet)
Definition: fmsrcimp.cxx:718
TransliterationFlags
bool moveToBookmark(const css::uno::Any &bookmark)
Definition: fmtools.hxx:104
css::uno::Reference< css::beans::XPropertySet > m_xListening
Definition: fmsrcimp.hxx:75
css::lang::Locale Locale
bool m_bCancelAsynchRequest
Definition: fmsrcimp.hxx:194
bool SearchForward(const OUString &rStr, sal_Int32 *pStart, sal_Int32 *pEnd, css::util::SearchResult *pRes=nullptr)
bool Matches(const OUString &rStr) const
#define DBG_UNHANDLED_EXCEPTION(...)
#define TOOLS_WARN_EXCEPTION(area, stream)
css::uno::Reference< css::awt::XListBox > m_xBox
Definition: fmsrcimp.hxx:134
static bool Reschedule(bool bHandleAllCurrentEvents=false)
#define DBG_ASSERT(sCon, aError)
std::deque< sal_Int32 > m_arrFieldMapping
Definition: fmsrcimp.hxx:159
bool last()
Definition: fmtools.hxx:113
SVX_DLLPRIVATE void PropagateProgress(bool _bDontPropagateOverflow)
Definition: fmsrcimp.cxx:752
TransliterationFlags transliterateFlags
SVX_DLLPRIVATE SearchResult SearchSpecial(bool _bSearchForNull, sal_Int32 &nFieldPos, FieldCollection::iterator &iterFieldLoop, const FieldCollection::iterator &iterBegin, const FieldCollection::iterator &iterEnd)
Definition: fmsrcimp.cxx:301
int i
TRISTATE_FALSE
sal_Int32 loadDefaultCollator(const css::lang::Locale &rLocale, sal_Int32 nOption)
std::vector< std::unique_ptr< svxform::ControlTextWrapper > > m_aControlTexts
Definition: fmsrcimp.hxx:177
SVX_DLLPRIVATE void fillControlTexts(const InterfaceArray &arrFields)
Definition: fmsrcimp.cxx:614
#define FM_PROP_ROWCOUNT
Definition: fmprop.hxx:32
void RebuildUsedFields(sal_Int32 nFieldIndex, bool bForce=false)
rebuilds m_arrUsedFields (nFieldIndex==-1 means all fields, otherwise it specifies the field index) i...
Definition: fmsrcimp.cxx:1038
struct FmSearchProgress - the owner of SearchEngine receives this structure for status updates (at th...
Definition: fmsrcimp.hxx:49
OUString lowercase(const OUString &rStr, sal_Int32 nPos, sal_Int32 nCount) const
bool GetCaseSensitive() const
Definition: fmsrcimp.cxx:602
sal_Int32 m_nCurrentFieldIndex
Definition: fmsrcimp.hxx:174
#define MATCHING_ANYWHERE
Definition: fmsrccfg.hxx:29
void OnSearchTerminated()
Definition: fmsrcimp.cxx:877
SearchFor m_eSearchForType
Definition: fmsrcimp.hxx:188
bool m_bLevenshtein
Definition: fmsrcimp.hxx:203
OUString m_strSearchExpression
Definition: fmsrcimp.hxx:187
void SearchNext(const OUString &strExpression)
search for the next appearance (for nDirection values check DIRECTION_*-defines)
Definition: fmsrcimp.cxx:976
virtual ~FmRecordCountListener() override
Definition: fmsrcimp.cxx:104
bool GetIgnoreWidthCJK() const
Definition: fmsrcimp.cxx:587
sal_uInt16 m_nPosition
Definition: fmsrcimp.hxx:211
void SearchNextImpl()
Definition: fmsrcimp.cxx:776
void ImplStartNextSearch()
Definition: fmsrcimp.cxx:966
::osl::Mutex m_aCancelAsynchAccess
Definition: fmsrcimp.hxx:195
sal_Int32 m_nPosition
FmSearchEngine(const css::uno::Reference< css::uno::XComponentContext > &_rxContext, const css::uno::Reference< css::sdbc::XResultSet > &xCursor, const OUString &strVisibleFields, const InterfaceArray &arrFields)
two constructs, both analogical to FmSearchDialog, therefore look this up for explanations...
Definition: fmsrcimp.cxx:546
CursorWrapper m_xSearchCursor
Definition: fmsrcimp.hxx:158
#define FM_PROP_ACTIVE_CONNECTION
Definition: fmprop.hxx:128
css::uno::Reference< css::sdb::XColumn > xContents
Definition: fmsrcimp.hxx:169
#define MATCHING_BEGINNING
Definition: fmsrccfg.hxx:30
#define IFACECAST(c)
Definition: fmsrcimp.cxx:61
SVX_DLLPRIVATE void BuildAndInsertFieldInfo(const css::uno::Reference< css::container::XIndexAccess > &xAllFields, sal_Int32 nField)
Definition: fmsrcimp.cxx:260
const LanguageTag & GetLanguageTag() const
class FmSearchEngine - Impl class for FmSearchDialog
FmRecordCountListener(const css::uno::Reference< css::sdbc::XResultSet > &dbcCursor)
Definition: fmsrcimp.cxx:77
Link< sal_Int32, void > m_lnkWhoWantsToKnow
Definition: fmsrcimp.hxx:74
virtual OUString getCurrentText() const override
Definition: fmsrcimp.cxx:166
CheckBoxWrapper(const css::uno::Reference< css::awt::XCheckBox > &_xBox)
Definition: fmsrcimp.cxx:172
SearchResult m_srResult
Definition: fmsrcimp.hxx:189
void SetIgnoreWidthCJK(bool bSet)
Definition: fmsrcimp.cxx:578
css::uno::Any getBookmark()
Definition: fmtools.hxx:100
#define MATCHING_WHOLETEXT
Definition: fmsrccfg.hxx:32
virtual OUString getCurrentText() const override
Definition: fmsrcimp.cxx:152
CursorWrapper m_xClonedIterator
Definition: fmsrcimp.hxx:180
Found
SVX_DLLPRIVATE bool CancelRequested()
Definition: fmsrcimp.cxx:931
void StartOverSpecial(bool _bSearchForNull)
analogous, search for "NULL" (_bSearchForNull==sal_True) or "not NULL"
Definition: fmsrcimp.cxx:1011
CharClass const m_aCharacterClassficator
Definition: fmsrcimp.hxx:163
bool is() const
Definition: fmtools.hxx:86
virtual void SAL_CALL disposing(const css::lang::EventObject &Source) override
Definition: fmsrcimp.cxx:118
bool first()
Definition: fmtools.hxx:112
css::uno::Reference< css::awt::XTextComponent > m_xText
Definition: fmsrcimp.hxx:126
void SetPropChangeHandler(const Link< sal_Int32, void > &lnk)
Definition: fmsrcimp.cxx:95