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