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