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>
28#include <tools/wldcrd.hxx>
29#include <vcl/svapp.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>
52
53#define EQUAL_BOOKMARKS(a, b) a == b
54
55#define IFACECAST(c) static_cast<const Reference< XInterface >&>(c)
56
57using namespace ::com::sun::star::uno;
58using namespace ::com::sun::star::util;
59using namespace ::com::sun::star::lang;
60using namespace ::com::sun::star::sdbc;
61using namespace ::com::sun::star::i18n;
62using namespace ::com::sun::star::beans;
63using namespace ::svxform;
64
65
66// = FmRecordCountListener
67
68// SMART_UNO_IMPLEMENTATION(FmRecordCountListener, UsrObject);
69
70
71FmRecordCountListener::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{
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
112void 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{
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
130void FmRecordCountListener::propertyChange(const css::beans::PropertyChangeEvent& /*evt*/)
131{
133}
134
135
136// FmSearchEngine - local classes
137
138SimpleTextWrapper::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
152ListBoxWrapper::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
166CheckBoxWrapper::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
226bool 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
254void 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
276OUString 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
295FmSearchEngine::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 {
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
352FmSearchEngine::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 {
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
422FmSearchEngine::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 {
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
540FmSearchEngine::FmSearchEngine(const Reference< XComponentContext >& _rxContext,
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
633void 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
649 Reference< XConnection > xConn;
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
740void FmSearchEngine::PropagateProgress(bool _bDontPropagateOverflow)
741{
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{
866 return;
867
868 FmSearchProgress aProgress;
869 try
870 {
871 switch (m_srResult)
872 {
875 break;
880 break;
884 break;
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
905IMPL_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{
927}
928
929
930void 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{
952
955}
956
957
958void FmSearchEngine::SearchNext(const OUString& strExpression)
959{
960 m_strSearchExpression = strExpression;
963}
964
965
966void FmSearchEngine::SearchNextSpecial(bool _bSearchForNull)
967{
970}
971
972
973void 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
993void FmSearchEngine::StartOverSpecial(bool _bSearchForNull)
994{
995 try
996 {
997 if (m_bForward)
999 else
1001 }
1002 catch( const Exception& )
1003 {
1005 return;
1006 }
1007
1009 SearchNextSpecial(_bSearchForNull);
1010}
1011
1012
1014{
1015 m_aPreviousLocBookmark.clear();
1017}
1018
1019
1020void 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: */
sal_Int32 m_nPosition
static bool Reschedule(bool bHandleAllCurrentEvents=false)
OUString lowercase(const OUString &rStr, sal_Int32 nPos, sal_Int32 nCount) const
sal_Int32 loadDefaultCollator(const css::lang::Locale &rLocale, sal_Int32 nOption)
sal_Int32 getRow() const
Definition: fmtools.hxx:115
bool isLast() const
Definition: fmtools.hxx:111
css::uno::Any getBookmark()
Definition: fmtools.hxx:101
bool is() const
Definition: fmtools.hxx:87
bool moveToBookmark(const css::uno::Any &bookmark)
Definition: fmtools.hxx:105
bool first()
Definition: fmtools.hxx:113
bool isFirst() const
Definition: fmtools.hxx:110
bool last()
Definition: fmtools.hxx:114
virtual void SAL_CALL propertyChange(const css::beans::PropertyChangeEvent &evt) override
Definition: fmsrcimp.cxx:130
FmRecordCountListener(const css::uno::Reference< css::sdbc::XResultSet > &dbcCursor)
Definition: fmsrcimp.cxx:71
css::uno::Reference< css::beans::XPropertySet > m_xListening
Definition: fmsrcimp.hxx:78
Link< sal_Int32, void > m_lnkWhoWantsToKnow
Definition: fmsrcimp.hxx:77
virtual ~FmRecordCountListener() override
Definition: fmsrcimp.cxx:98
virtual void SAL_CALL disposing(const css::lang::EventObject &Source) override
Definition: fmsrcimp.cxx:112
void SetPropChangeHandler(const Link< sal_Int32, void > &lnk)
Definition: fmsrcimp.cxx:89
bool GetIgnoreWidthCJK() const
Definition: fmsrcimp.cxx:581
SVX_DLLPRIVATE bool CancelRequested()
Definition: fmsrcimp.cxx:917
SVX_DLLPRIVATE void fillControlTexts(const InterfaceArray &arrFields)
Definition: fmsrcimp.cxx:602
std::vector< std::unique_ptr< svxform::ControlTextWrapper > > m_aControlTexts
Definition: fmsrcimp.hxx:180
void SetIgnoreWidthCJK(bool bSet)
Definition: fmsrcimp.cxx:572
Link< const FmSearchProgress *, void > m_aProgressHandler
Definition: fmsrcimp.hxx:195
SearchResult m_srResult
Definition: fmsrcimp.hxx:192
FieldCollection m_arrUsedFields
Definition: fmsrcimp.hxx:176
OUString m_strSearchExpression
Definition: fmsrcimp.hxx:190
SearchFor m_eSearchForType
Definition: fmsrcimp.hxx:191
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 GetCaseSensitive() const
Definition: fmsrcimp.cxx:596
std::deque< sal_Int32 > m_arrFieldMapping
Definition: fmsrcimp.hxx:162
bool m_bLevenshtein
Definition: fmsrcimp.hxx:204
sal_Int32 m_nCurrentFieldIndex
Definition: fmsrcimp.hxx:177
css::uno::Any m_aPreviousLocBookmark
Definition: fmsrcimp.hxx:186
FieldCollection::iterator m_iterPreviousLocField
Definition: fmsrcimp.hxx:187
void SearchNextImpl()
Definition: fmsrcimp.cxx:764
void CancelSearch()
returns directly; once it was really aborted, ProgressHandler is called with STATE_CANCELED
Definition: fmsrcimp.cxx:924
void InvalidatePreviousLoc()
invalidate previous search reference
Definition: fmsrcimp.cxx:1013
void StartOverSpecial(bool _bSearchForNull)
analogous, search for "NULL" (_bSearchForNull==sal_True) or "not NULL"
Definition: fmsrcimp.cxx:993
SVX_DLLPRIVATE SearchResult SearchSpecial(bool _bSearchForNull, sal_Int32 &nFieldPos, FieldCollection::iterator &iterFieldLoop, const FieldCollection::iterator &iterBegin, const FieldCollection::iterator &iterEnd)
Definition: fmsrcimp.cxx:295
CollatorWrapper m_aStringCompare
Definition: fmsrcimp.hxx:167
sal_uInt16 m_nPosition
Definition: fmsrcimp.hxx:212
CursorWrapper m_xClonedIterator
Definition: fmsrcimp.hxx:183
SVX_DLLPRIVATE void PropagateProgress(bool _bDontPropagateOverflow)
Definition: fmsrcimp.cxx:740
void Init(std::u16string_view strVisibleFields)
Definition: fmsrcimp.cxx:633
bool m_bSearchingCurrently
Definition: fmsrcimp.hxx:199
void SearchNext(const OUString &strExpression)
search for the next appearance (for nDirection values check DIRECTION_*-defines)
Definition: fmsrcimp.cxx:958
OUString FormatField(sal_Int32 nWhich)
Definition: fmsrcimp.cxx:276
SVX_DLLPRIVATE bool MoveCursor()
Definition: fmsrcimp.cxx:188
void SetCaseSensitive(bool bSet)
Definition: fmsrcimp.cxx:587
SVX_DLLPRIVATE void BuildAndInsertFieldInfo(const css::uno::Reference< css::container::XIndexAccess > &xAllFields, sal_Int32 nField)
Definition: fmsrcimp.cxx:254
CharClass m_aCharacterClassficator
Definition: fmsrcimp.hxx:166
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
void OnSearchTerminated()
Definition: fmsrcimp.cxx:863
CursorWrapper m_xOriginalIterator
Definition: fmsrcimp.hxx:182
void SetFormatterUsing(bool bSet)
Definition: fmsrcimp.cxx:706
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
void ImplStartNextSearch()
Definition: fmsrcimp.cxx:948
SVX_DLLPRIVATE bool MoveField(sal_Int32 &nPos, FieldCollection::iterator &iter, const FieldCollection::iterator &iterBegin, const FieldCollection::iterator &iterEnd)
Definition: fmsrcimp.cxx:226
std::atomic< bool > m_bCancelAsynchRequest
Definition: fmsrcimp.hxx:196
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
TransliterationFlags m_nTransliterationFlags
Definition: fmsrcimp.hxx:214
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
CursorWrapper m_xSearchCursor
Definition: fmsrcimp.hxx:161
void SearchNextSpecial(bool _bSearchForNull)
analogous, search for "NULL" (_bSearchForNull==sal_True) or "not NULL"
Definition: fmsrcimp.cxx:966
void StartOver(const OUString &strExpression)
search for the next appearance, dependent on nDirection from the start or end
Definition: fmsrcimp.cxx:973
const css::lang::Locale & getLocale(bool bResolveSystem=true) const
const LanguageTag & GetLanguageTag() const
bool Matches(std::u16string_view rStr) const
CheckBoxWrapper(const css::uno::Reference< css::awt::XCheckBox > &_xBox)
Definition: fmsrcimp.cxx:166
css::uno::Reference< css::awt::XCheckBox > m_xBox
Definition: fmsrcimp.hxx:145
virtual OUString getCurrentText() const override
Definition: fmsrcimp.cxx:174
ListBoxWrapper(const css::uno::Reference< css::awt::XListBox > &_xBox)
Definition: fmsrcimp.cxx:152
virtual OUString getCurrentText() const override
Definition: fmsrcimp.cxx:160
css::uno::Reference< css::awt::XListBox > m_xBox
Definition: fmsrcimp.hxx:137
css::uno::Reference< css::awt::XTextComponent > m_xText
Definition: fmsrcimp.hxx:129
virtual OUString getCurrentText() const override
Definition: fmsrcimp.cxx:146
bool SearchForward(const OUString &rStr, sal_Int32 *pStart, sal_Int32 *pEnd, css::util::SearchResult *pRes=nullptr)
#define DBG_ASSERT(sCon, aError)
#define TOOLS_WARN_EXCEPTION(area, stream)
#define DBG_UNHANDLED_EXCEPTION(...)
virtual TransliterationFlags GetTransliterationFlags() const override
constexpr OUStringLiteral FM_PROP_ROWCOUNT
Definition: fmprop.hxx:34
constexpr OUStringLiteral FM_PROP_ACTIVE_CONNECTION
Definition: fmprop.hxx:130
constexpr OUStringLiteral FM_PROP_ROWCOUNTFINAL
Definition: fmprop.hxx:35
#define MATCHING_BEGINNING
Definition: fmsrccfg.hxx:30
#define MATCHING_ANYWHERE
Definition: fmsrccfg.hxx:29
#define MATCHING_WHOLETEXT
Definition: fmsrccfg.hxx:32
#define MATCHING_END
Definition: fmsrccfg.hxx:31
#define EQUAL_BOOKMARKS(a, b)
Definition: fmsrcimp.cxx:53
#define IFACECAST(c)
Definition: fmsrcimp.cxx:55
IMPL_LINK(FmSearchEngine, OnNewRecordCount, sal_Int32, theCounter, void)
Definition: fmsrcimp.cxx:905
std::vector< css::uno::Reference< css::uno::XInterface > > InterfaceArray
Definition: fmsrcimp.hxx:152
TriState
TRISTATE_FALSE
TRISTATE_TRUE
sal_Int32 nIndex
sal_uInt16 nPos
NONE
@ Exception
const LanguageTag & getLocale()
int i
constexpr std::enable_if_t< std::is_signed_v< T >, std::make_unsigned_t< T > > make_unsigned(T value)
std::basic_string_view< charT, traits > getToken(std::basic_string_view< charT, traits > sv, charT delimiter, std::size_t &position)
class FmSearchEngine - Impl class for FmSearchDialog
css::uno::Reference< css::sdb::XColumn > xContents
Definition: fmsrcimp.hxx:172
struct FmSearchProgress - the owner of SearchEngine receives this structure for status updates (at th...
Definition: fmsrcimp.hxx:51
sal_uInt32 nCurrentRecord
Definition: fmsrcimp.hxx:58
css::uno::Any aBookmark
Definition: fmsrcimp.hxx:63
State aSearchState
Definition: fmsrcimp.hxx:55
sal_Int32 nFieldIndex
Definition: fmsrcimp.hxx:65
TransliterationFlags transliterateFlags
css::lang::Locale Locale
TransliterationFlags