bump product version to 5.0.4.1
[LibreOffice.git] / svx / source / form / fmsrcimp.cxx
bloba3ed1a3428435177e26af0d16418823f614e06ee
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
21 #include <rtl/strbuf.hxx>
22 #include "svx/fmresids.hrc"
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/msgbox.hxx>
29 #include <svx/dialmgr.hxx>
30 #include <vcl/svapp.hxx>
31 #include <unotools/textsearch.hxx>
32 #include <com/sun/star/util/SearchOptions.hpp>
33 #include <com/sun/star/util/SearchAlgorithms.hpp>
34 #include <com/sun/star/util/SearchResult.hpp>
35 #include <com/sun/star/util/SearchFlags.hpp>
36 #include <com/sun/star/lang/Locale.hpp>
37 #include <com/sun/star/i18n/TransliterationModules.hpp>
38 #include <com/sun/star/i18n/CollatorOptions.hpp>
40 #include <com/sun/star/sdbcx/XColumnsSupplier.hpp>
41 #include <com/sun/star/util/NumberFormatter.hpp>
42 #include <com/sun/star/util/NumberFormat.hpp>
43 #include <com/sun/star/util/XNumberFormatsSupplier.hpp>
44 #include <com/sun/star/util/XNumberFormats.hpp>
45 #include <comphelper/processfactory.hxx>
47 #include "fmprop.hrc"
48 #include "fmservs.hxx"
49 #include "svx/fmsrcimp.hxx"
50 #include <svx/fmsearch.hxx>
52 #include <comphelper/numbers.hxx>
53 #include <unotools/syslocale.hxx>
55 #define EQUAL_BOOKMARKS(a, b) a == b
57 #define IFACECAST(c) ((const Reference< XInterface >&)c)
59 using namespace ::com::sun::star::uno;
60 using namespace ::com::sun::star::util;
61 using namespace ::com::sun::star::lang;
62 using namespace ::com::sun::star::sdbc;
63 using namespace ::com::sun::star::i18n;
64 using namespace ::com::sun::star::beans;
65 using namespace ::svxform;
69 // = FmSearchThread
71 void FmSearchThread::run()
73 osl_setThreadName("FmSearchThread");
75 m_pEngine->SearchNextImpl();
79 void FmSearchThread::onTerminated()
81 if (m_aTerminationHdl.IsSet())
82 m_aTerminationHdl.Call(this);
83 delete this;
87 // = FmRecordCountListener
89 // SMART_UNO_IMPLEMENTATION(FmRecordCountListener, UsrObject);
92 FmRecordCountListener::FmRecordCountListener(const Reference< ::com::sun::star::sdbc::XResultSet > & dbcCursor)
95 m_xListening = Reference< ::com::sun::star::beans::XPropertySet > (dbcCursor, UNO_QUERY);
96 if (!m_xListening.is())
97 return;
99 if (::comphelper::getBOOL(m_xListening->getPropertyValue(FM_PROP_ROWCOUNTFINAL)))
101 m_xListening = NULL;
102 // there's nothing to do as the record count is already known
103 return;
106 m_xListening->addPropertyChangeListener(FM_PROP_ROWCOUNT, (::com::sun::star::beans::XPropertyChangeListener*)this);
110 Link<> FmRecordCountListener::SetPropChangeHandler(const Link<>& lnk)
112 Link<> lnkReturn = m_lnkWhoWantsToKnow;
113 m_lnkWhoWantsToKnow = lnk;
115 if (m_xListening.is())
116 NotifyCurrentCount();
118 return lnkReturn;
122 FmRecordCountListener::~FmRecordCountListener()
128 void FmRecordCountListener::DisConnect()
130 if(m_xListening.is())
131 m_xListening->removePropertyChangeListener(FM_PROP_ROWCOUNT, (::com::sun::star::beans::XPropertyChangeListener*)this);
132 m_xListening = NULL;
136 void SAL_CALL FmRecordCountListener::disposing(const ::com::sun::star::lang::EventObject& /*Source*/) throw( RuntimeException, std::exception )
138 DBG_ASSERT(m_xListening.is(), "FmRecordCountListener::disposing should never have been called without a propset !");
139 DisConnect();
143 void FmRecordCountListener::NotifyCurrentCount()
145 if (m_lnkWhoWantsToKnow.IsSet())
147 DBG_ASSERT(m_xListening.is(), "FmRecordCountListener::NotifyCurrentCount : I have no propset ... !?");
148 void* pTheCount = reinterpret_cast<void*>(::comphelper::getINT32(m_xListening->getPropertyValue(FM_PROP_ROWCOUNT)));
149 m_lnkWhoWantsToKnow.Call(pTheCount);
154 void FmRecordCountListener::propertyChange(const ::com::sun::star::beans::PropertyChangeEvent& /*evt*/) throw(::com::sun::star::uno::RuntimeException, std::exception)
156 NotifyCurrentCount();
160 // FmSearchEngine - local classes
162 SimpleTextWrapper::SimpleTextWrapper(const Reference< ::com::sun::star::awt::XTextComponent > & _xText)
163 :ControlTextWrapper(_xText.get())
164 ,m_xText(_xText)
166 DBG_ASSERT(m_xText.is(), "FmSearchEngine::SimpleTextWrapper::SimpleTextWrapper : invalid argument !");
170 OUString SimpleTextWrapper::getCurrentText() const
172 return m_xText->getText();
176 ListBoxWrapper::ListBoxWrapper(const Reference< ::com::sun::star::awt::XListBox > & _xBox)
177 :ControlTextWrapper(_xBox.get())
178 ,m_xBox(_xBox)
180 DBG_ASSERT(m_xBox.is(), "FmSearchEngine::ListBoxWrapper::ListBoxWrapper : invalid argument !");
184 OUString ListBoxWrapper::getCurrentText() const
186 return m_xBox->getSelectedItem();
190 CheckBoxWrapper::CheckBoxWrapper(const Reference< ::com::sun::star::awt::XCheckBox > & _xBox)
191 :ControlTextWrapper(_xBox.get())
192 ,m_xBox(_xBox)
194 DBG_ASSERT(m_xBox.is(), "FmSearchEngine::CheckBoxWrapper::CheckBoxWrapper : invalid argument !");
198 OUString CheckBoxWrapper::getCurrentText() const
200 switch ((TriState)m_xBox->getState())
202 case TRISTATE_FALSE: return OUString("0");
203 case TRISTATE_TRUE: return OUString("1");
204 default: break;
206 return OUString();
210 // = FmSearchEngine
212 bool FmSearchEngine::MoveCursor()
214 bool bSuccess = true;
217 if (m_bForward)
218 if (m_xSearchCursor.isLast())
219 m_xSearchCursor.first();
220 else
221 m_xSearchCursor.next();
222 else
223 if (m_xSearchCursor.isFirst())
225 FmRecordCountListener* prclListener = new FmRecordCountListener(m_xSearchCursor);
226 prclListener->acquire();
227 prclListener->SetPropChangeHandler(LINK(this, FmSearchEngine, OnNewRecordCount));
229 m_xSearchCursor.last();
231 prclListener->DisConnect();
232 prclListener->release();
234 else
235 m_xSearchCursor.previous();
237 catch(::com::sun::star::sdbc::SQLException const& e)
239 #if OSL_DEBUG_LEVEL > 0
240 OStringBuffer sDebugMessage("FmSearchEngine::MoveCursor : catched a DatabaseException (");
241 sDebugMessage.append(OUStringToOString(e.SQLState, RTL_TEXTENCODING_ASCII_US));
242 sDebugMessage.append(") !");
243 OSL_FAIL(sDebugMessage.getStr());
244 #else
245 (void)e;
246 #endif
247 bSuccess = false;
249 catch(Exception const& e)
251 #if OSL_DEBUG_LEVEL > 0
252 OStringBuffer sDebugMessage("FmSearchEngine::MoveCursor : catched an Exception (");
253 sDebugMessage.append(OUStringToOString(e.Message, RTL_TEXTENCODING_ASCII_US));
254 sDebugMessage.append(") !");
255 OSL_FAIL(sDebugMessage.getStr());
256 #else
257 (void)e;
258 #endif
259 bSuccess = false;
261 catch(...)
263 OSL_FAIL("FmSearchEngine::MoveCursor : catched an unknown Exception !");
264 bSuccess = false;
267 return bSuccess;
271 bool FmSearchEngine::MoveField(sal_Int32& nPos, FieldCollection::iterator& iter, const FieldCollection::iterator& iterBegin, const FieldCollection::iterator& iterEnd)
273 bool bSuccess(true);
274 if (m_bForward)
276 ++iter;
277 ++nPos;
278 if (iter == iterEnd)
280 bSuccess = MoveCursor();
281 iter = iterBegin;
282 nPos = 0;
284 } else
286 if (iter == iterBegin)
288 bSuccess = MoveCursor();
289 iter = iterEnd;
290 nPos = iter-iterBegin;
292 --iter;
293 --nPos;
295 return bSuccess;
299 void FmSearchEngine::BuildAndInsertFieldInfo(const Reference< ::com::sun::star::container::XIndexAccess > & xAllFields, sal_Int32 nField)
301 DBG_ASSERT( xAllFields.is() && ( nField >= 0 ) && ( nField < xAllFields->getCount() ),
302 "FmSearchEngine::BuildAndInsertFieldInfo: invalid field descriptor!" );
304 // das Feld selber
305 Reference< XInterface > xCurrentField;
306 xAllFields->getByIndex(nField) >>= xCurrentField;
308 // von dem weiss ich jetzt, dass es den DatabaseRecord-Service unterstuetzt (hoffe ich)
309 // fuer den FormatKey und den Typ brauche ich das PropertySet
310 Reference< ::com::sun::star::beans::XPropertySet > xProperties(xCurrentField, UNO_QUERY);
312 // die FieldInfo dazu aufbauen
313 FieldInfo fiCurrent;
314 fiCurrent.xContents = Reference< ::com::sun::star::sdb::XColumn > (xCurrentField, UNO_QUERY);
315 fiCurrent.nFormatKey = ::comphelper::getINT32(xProperties->getPropertyValue(FM_PROP_FORMATKEY));
316 fiCurrent.bDoubleHandling = false;
317 if (m_xFormatSupplier.is())
319 Reference< ::com::sun::star::util::XNumberFormats > xNumberFormats(m_xFormatSupplier->getNumberFormats());
321 sal_Int16 nFormatType = ::comphelper::getNumberFormatType(xNumberFormats, fiCurrent.nFormatKey) & ~((sal_Int16)::com::sun::star::util::NumberFormat::DEFINED);
322 fiCurrent.bDoubleHandling = (nFormatType != ::com::sun::star::util::NumberFormat::TEXT);
325 // und merken
326 m_arrUsedFields.insert(m_arrUsedFields.end(), fiCurrent);
330 OUString FmSearchEngine::FormatField(const FieldInfo& rField)
332 DBG_ASSERT(!m_bUsingTextComponents, "FmSearchEngine::FormatField : im UsingTextComponents-Mode bitte FormatField(sal_Int32) benutzen !");
334 if (!m_xFormatter.is())
335 return OUString();
336 // sonst werden Datumsflder zum Beispiel zu irgendeinem Default-Wert formatiert
338 OUString sReturn;
341 if (rField.bDoubleHandling)
343 double fValue = rField.xContents->getDouble();
344 if (!rField.xContents->wasNull())
345 sReturn = m_xFormatter->convertNumberToString(rField.nFormatKey, fValue);
347 else
349 OUString sValue = rField.xContents->getString();
350 if (!rField.xContents->wasNull())
351 sReturn = m_xFormatter->formatString(rField.nFormatKey, sValue);
354 catch(...)
359 return sReturn;
363 OUString FmSearchEngine::FormatField(sal_Int32 nWhich)
365 if (m_bUsingTextComponents)
367 DBG_ASSERT((sal_uInt32)nWhich < m_aControlTexts.size(), "FmSearchEngine::FormatField(sal_Int32) : invalid position !");
368 DBG_ASSERT(m_aControlTexts[nWhich] != NULL, "FmSearchEngine::FormatField(sal_Int32) : invalid object in array !");
369 DBG_ASSERT(m_aControlTexts[nWhich]->getControl().is(), "FmSearchEngine::FormatField : invalid control !");
371 if (m_nCurrentFieldIndex != -1)
373 DBG_ASSERT((nWhich == 0) || (nWhich == m_nCurrentFieldIndex), "FmSearchEngine::FormatField : Parameter nWhich ist ungueltig");
374 // analoge Situation wie unten
375 nWhich = m_nCurrentFieldIndex;
378 DBG_ASSERT((nWhich >= 0) && ((sal_uInt32)nWhich < m_aControlTexts.size()),
379 "FmSearchEngine::FormatField : invalid argument nWhich !");
380 return m_aControlTexts[m_nCurrentFieldIndex == -1 ? nWhich : m_nCurrentFieldIndex]->getCurrentText();
382 else
384 if (m_nCurrentFieldIndex != -1)
386 DBG_ASSERT((nWhich == 0) || (nWhich == m_nCurrentFieldIndex), "FmSearchEngine::FormatField : Parameter nWhich ist ungueltig");
387 // ich bin im single-field-modus, da ist auch die richtige Feld-Nummer erlaubt, obwohl dann der richtige ::com::sun::star::sdbcx::Index
388 // fuer meinen Array-Zugriff natuerlich 0 ist
389 nWhich = 0;
392 DBG_ASSERT((nWhich>=0) && (nWhich < (m_arrUsedFields.end() - m_arrUsedFields.begin())),
393 "FmSearchEngine::FormatField : Parameter nWhich ist ungueltig");
394 return FormatField(m_arrUsedFields[nWhich]);
399 FmSearchEngine::SEARCH_RESULT FmSearchEngine::SearchSpecial(bool _bSearchForNull, sal_Int32& nFieldPos,
400 FieldCollection::iterator& iterFieldLoop, const FieldCollection::iterator& iterBegin, const FieldCollection::iterator& iterEnd)
402 // die Startposition merken
403 Any aStartMark;
404 try { aStartMark = m_xSearchCursor.getBookmark(); }
405 catch ( const Exception& ) { DBG_UNHANDLED_EXCEPTION(); return SR_ERROR; }
406 FieldCollection::iterator iterInitialField = iterFieldLoop;
409 bool bFound(false);
410 bool bMovedAround(false);
413 if (m_eMode == SM_ALLOWSCHEDULE)
415 Application::Reschedule();
416 Application::Reschedule();
417 // do 2 reschedules because of #70226# : some things done within this loop's body may cause an user event
418 // to be posted (deep within vcl), and these user events will be handled before any keyinput or paintings
419 // or anything like that. So within each loop we create one user event and handle one user event (and no
420 // paintings and these), so the office seems to be frozen while searching.
421 // FS - 70226 - 02.12.99
424 // der aktuell zu vergleichende Inhalt
425 iterFieldLoop->xContents->getString(); // needed for wasNull
426 bFound = _bSearchForNull == bool(iterFieldLoop->xContents->wasNull());
427 if (bFound)
428 break;
430 // naechstes Feld (implizit naechster Datensatz, wenn noetig)
431 if (!MoveField(nFieldPos, iterFieldLoop, iterBegin, iterEnd))
432 { // beim Bewegen auf das naechste Feld ging was schief ... weitermachen ist nicht drin, da das naechste Mal genau
433 // das selbe bestimmt wieder schief geht, also Abbruch
434 // vorher aber noch, damit das Weitersuchen an der aktuellen Position weitermacht :
435 try { m_aPreviousLocBookmark = m_xSearchCursor.getBookmark(); }
436 catch ( const Exception& ) { DBG_UNHANDLED_EXCEPTION(); }
437 m_iterPreviousLocField = iterFieldLoop;
438 // und wech
439 return SR_ERROR;
442 Any aCurrentBookmark;
443 try { aCurrentBookmark = m_xSearchCursor.getBookmark(); }
444 catch ( const Exception& ) { DBG_UNHANDLED_EXCEPTION(); return SR_ERROR; }
446 bMovedAround = EQUAL_BOOKMARKS(aStartMark, aCurrentBookmark) && (iterFieldLoop == iterInitialField);
448 if (nFieldPos == 0)
449 // das heisst, ich habe mich auf einen neuen Datensatz bewegt
450 PropagateProgress(bMovedAround);
451 // if we moved to the starting position we don't have to propagate an 'overflow' message
452 // FS - 07.12.99 - 68530
454 // abbrechen gefordert ?
455 if (CancelRequested())
456 return SR_CANCELED;
458 } while (!bMovedAround);
460 return bFound ? SR_FOUND : SR_NOTFOUND;
464 FmSearchEngine::SEARCH_RESULT FmSearchEngine::SearchWildcard(const OUString& strExpression, sal_Int32& nFieldPos,
465 FieldCollection::iterator& iterFieldLoop, const FieldCollection::iterator& iterBegin, const FieldCollection::iterator& iterEnd)
467 // die Startposition merken
468 Any aStartMark;
469 try { aStartMark = m_xSearchCursor.getBookmark(); }
470 catch ( const Exception& ) { DBG_UNHANDLED_EXCEPTION(); return SR_ERROR; }
471 FieldCollection::iterator iterInitialField = iterFieldLoop;
473 WildCard aSearchExpression(strExpression);
476 bool bFound(false);
477 bool bMovedAround(false);
480 if (m_eMode == SM_ALLOWSCHEDULE)
482 Application::Reschedule();
483 Application::Reschedule();
484 // do 2 reschedules because of #70226# : some things done within this loop's body may cause an user event
485 // to be posted (deep within vcl), and these user events will be handled before any keyinput or paintings
486 // or anything like that. So within each loop we create one user event and hanel one user event (and no
487 // paintings and these), so the office seems to be frozen while searching.
488 // FS - 70226 - 02.12.99
491 // der aktuell zu vergleichende Inhalt
492 OUString sCurrentCheck;
493 if (m_bFormatter)
494 sCurrentCheck = FormatField(nFieldPos);
495 else
496 sCurrentCheck = iterFieldLoop->xContents->getString();
498 if (!GetCaseSensitive())
499 // norm the string
500 sCurrentCheck = m_aCharacterClassficator.lowercase(sCurrentCheck);
502 // jetzt ist der Test einfach ...
503 bFound = aSearchExpression.Matches(sCurrentCheck);
505 if (bFound)
506 break;
508 // naechstes Feld (implizit naechster Datensatz, wenn noetig)
509 if (!MoveField(nFieldPos, iterFieldLoop, iterBegin, iterEnd))
510 { // beim Bewegen auf das naechste Feld ging was schief ... weitermachen ist nicht drin, da das naechste Mal genau
511 // das selbe bestimmt wieder schief geht, also Abbruch
512 // vorher aber noch, damit das Weitersuchen an der aktuellen Position weitermacht :
513 try { m_aPreviousLocBookmark = m_xSearchCursor.getBookmark(); }
514 catch ( const Exception& ) { DBG_UNHANDLED_EXCEPTION(); }
515 m_iterPreviousLocField = iterFieldLoop;
516 // und wech
517 return SR_ERROR;
520 Any aCurrentBookmark;
521 try { aCurrentBookmark = m_xSearchCursor.getBookmark(); }
522 catch ( const Exception& ) { DBG_UNHANDLED_EXCEPTION(); return SR_ERROR; }
524 bMovedAround = EQUAL_BOOKMARKS(aStartMark, aCurrentBookmark) && (iterFieldLoop == iterInitialField);
526 if (nFieldPos == 0)
527 // das heisst, ich habe mich auf einen neuen Datensatz bewegt
528 PropagateProgress(bMovedAround);
529 // if we moved to the starting position we don't have to propagate an 'overflow' message
530 // FS - 07.12.99 - 68530
532 // abbrechen gefordert ?
533 if (CancelRequested())
534 return SR_CANCELED;
536 } while (!bMovedAround);
538 return bFound ? SR_FOUND : SR_NOTFOUND;
542 FmSearchEngine::SEARCH_RESULT FmSearchEngine::SearchRegularApprox(const OUString& strExpression, sal_Int32& nFieldPos,
543 FieldCollection::iterator& iterFieldLoop, const FieldCollection::iterator& iterBegin, const FieldCollection::iterator& iterEnd)
545 DBG_ASSERT(m_bLevenshtein || m_bRegular,
546 "FmSearchEngine::SearchRegularApprox : ungueltiger Suchmodus !");
547 DBG_ASSERT(!m_bLevenshtein || !m_bRegular,
548 "FmSearchEngine::SearchRegularApprox : kann nicht nach regulaeren Ausdruecken und nach Aehnlichkeiten gleichzeitig suchen !");
550 // Startposition merken
551 Any aStartMark;
552 try { aStartMark = m_xSearchCursor.getBookmark(); }
553 catch ( const Exception& ) { DBG_UNHANDLED_EXCEPTION(); return SR_ERROR; }
554 FieldCollection::iterator iterInitialField = iterFieldLoop;
556 // Parameter sammeln
557 SearchOptions aParam;
558 aParam.algorithmType = m_bRegular ? SearchAlgorithms_REGEXP : SearchAlgorithms_APPROXIMATE;
559 aParam.searchFlag = 0;
560 aParam.transliterateFlags = GetTransliterationFlags();
561 if ( !GetTransliteration() )
562 { // if transliteration is not enabled, the only flags which matter are IGNORE_CASE and IGNORE_WIDTH
563 aParam.transliterateFlags &= TransliterationModules_IGNORE_CASE | TransliterationModules_IGNORE_WIDTH;
565 if (m_bLevenshtein)
567 if (m_bLevRelaxed)
568 aParam.searchFlag |= SearchFlags::LEV_RELAXED;
569 aParam.changedChars = m_nLevOther;
570 aParam.deletedChars = m_nLevShorter;
571 aParam.insertedChars = m_nLevLonger;
573 aParam.searchString = strExpression;
574 aParam.Locale = SvtSysLocale().GetLanguageTag().getLocale();
575 ::utl::TextSearch aLocalEngine(aParam);
578 bool bFound = false;
579 bool bMovedAround(false);
582 if (m_eMode == SM_ALLOWSCHEDULE)
584 Application::Reschedule();
585 Application::Reschedule();
586 // do 2 reschedules because of #70226# : some things done within this loop's body may cause an user event
587 // to be posted (deep within vcl), and these user events will be handled before any keyinput or paintings
588 // or anything like that. So within each loop we create one user event and handle one user event (and no
589 // paintings and these), so the office seems to be frozen while searching.
590 // FS - 70226 - 02.12.99
593 // der aktuell zu vergleichende Inhalt
594 OUString sCurrentCheck;
595 if (m_bFormatter)
596 sCurrentCheck = FormatField(nFieldPos);
597 else
598 sCurrentCheck = iterFieldLoop->xContents->getString();
600 // (don't care about case here, this is done by the TextSearch object, 'cause we passed our case parameter to it)
602 sal_Int32 nStart = 0, nEnd = sCurrentCheck.getLength();
603 bFound = aLocalEngine.SearchForward(sCurrentCheck, &nStart, &nEnd);
604 // das heisst hier 'forward' aber das bezieht sich nur auf die Suche innerhalb von sCurrentCheck, hat also mit
605 // der Richtung meines Datensatz-Durchwanderns nix zu tun (darum kuemmert sich MoveField)
607 // checken, ob die Position stimmt
608 if (bFound)
610 switch (m_nPosition)
612 case MATCHING_WHOLETEXT :
613 if (nEnd != sCurrentCheck.getLength())
615 bFound = false;
616 break;
618 // laeuft in den naechsten Case rein !
619 case MATCHING_BEGINNING :
620 if (nStart != 0)
621 bFound = false;
622 break;
623 case MATCHING_END :
624 if (nEnd != sCurrentCheck.getLength())
625 bFound = false;
626 break;
630 if (bFound) // immer noch ?
631 break;
633 // naechstes Feld (implizit naechster Datensatz, wenn noetig)
634 if (!MoveField(nFieldPos, iterFieldLoop, iterBegin, iterEnd))
635 { // beim Bewegen auf das naechste Feld ging was schief ... weitermachen ist nicht drin, da das naechste Mal genau
636 // das selbe bestimmt wieder schief geht, also Abbruch (ohne Fehlermeldung, von der erwarte ich, dass sie im Move
637 // angezeigt wurde)
638 // vorher aber noch, damit das Weitersuchen an der aktuellen Position weitermacht :
639 try { m_aPreviousLocBookmark = m_xSearchCursor.getBookmark(); }
640 catch ( const Exception& ) { DBG_UNHANDLED_EXCEPTION(); }
641 m_iterPreviousLocField = iterFieldLoop;
642 // und wech
643 return SR_ERROR;
646 Any aCurrentBookmark;
647 try { aCurrentBookmark = m_xSearchCursor.getBookmark(); }
648 catch ( const Exception& ) { DBG_UNHANDLED_EXCEPTION(); return SR_ERROR; }
649 bMovedAround = EQUAL_BOOKMARKS(aStartMark, aCurrentBookmark) && (iterFieldLoop == iterInitialField);
651 if (nFieldPos == 0)
652 // das heisst, ich habe mich auf einen neuen Datensatz bewegt
653 PropagateProgress(bMovedAround);
654 // if we moved to the starting position we don't have to propagate an 'overflow' message
655 // FS - 07.12.99 - 68530
657 // abbrechen gefordert ?
658 if (CancelRequested())
659 return SR_CANCELED;
661 } while (!bMovedAround);
663 return bFound ? SR_FOUND : SR_NOTFOUND;
668 FmSearchEngine::FmSearchEngine(const Reference< XComponentContext >& _rxContext,
669 const Reference< XResultSet > & xCursor, const OUString& sVisibleFields,
670 const Reference< XNumberFormatsSupplier > & xFormatSupplier, FMSEARCH_MODE eMode)
672 :m_xSearchCursor(xCursor)
673 ,m_xFormatSupplier(xFormatSupplier)
674 ,m_aCharacterClassficator( _rxContext, SvtSysLocale().GetLanguageTag() )
675 ,m_aStringCompare( _rxContext )
676 ,m_nCurrentFieldIndex(-2) // -1 hat schon eine Bedeutung, also nehme ich -2 fuer 'ungueltig'
677 ,m_bUsingTextComponents(false)
678 ,m_eSearchForType(SEARCHFOR_STRING)
679 ,m_srResult(SR_FOUND)
680 ,m_bSearchingCurrently(false)
681 ,m_bCancelAsynchRequest(false)
682 ,m_eMode(eMode)
683 ,m_bFormatter(false)
684 ,m_bForward(false)
685 ,m_bWildcard(false)
686 ,m_bRegular(false)
687 ,m_bLevenshtein(false)
688 ,m_bTransliteration(false)
689 ,m_bLevRelaxed(false)
690 ,m_nLevOther(0)
691 ,m_nLevShorter(0)
692 ,m_nLevLonger(0)
693 ,m_nPosition(MATCHING_ANYWHERE)
694 ,m_nTransliterationFlags(0)
697 m_xFormatter = Reference< ::com::sun::star::util::XNumberFormatter >(
698 ::com::sun::star::util::NumberFormatter::create( ::comphelper::getProcessComponentContext() ),
699 UNO_QUERY_THROW);
700 m_xFormatter->attachNumberFormatsSupplier(m_xFormatSupplier);
702 Init(sVisibleFields);
706 FmSearchEngine::FmSearchEngine(const Reference< XComponentContext >& _rxContext,
707 const Reference< XResultSet > & xCursor, const OUString& sVisibleFields,
708 const InterfaceArray& arrFields, FMSEARCH_MODE eMode)
709 :m_xSearchCursor(xCursor)
710 ,m_aCharacterClassficator( _rxContext, SvtSysLocale().GetLanguageTag() )
711 ,m_aStringCompare( _rxContext )
712 ,m_nCurrentFieldIndex(-2) // -1 hat schon eine Bedeutung, also nehme ich -2 fuer 'ungueltig'
713 ,m_bUsingTextComponents(true)
714 ,m_xOriginalIterator(xCursor)
715 ,m_xClonedIterator(m_xOriginalIterator, true)
716 ,m_eSearchForType(SEARCHFOR_STRING)
717 ,m_srResult(SR_FOUND)
718 ,m_bSearchingCurrently(false)
719 ,m_bCancelAsynchRequest(false)
720 ,m_eMode(eMode)
721 ,m_bFormatter(true) // das muss konsistent sein mit m_xSearchCursor, der i.A. == m_xOriginalIterator ist
722 ,m_bForward(false)
723 ,m_bWildcard(false)
724 ,m_bRegular(false)
725 ,m_bLevenshtein(false)
726 ,m_bTransliteration(false)
727 ,m_bLevRelaxed(false)
728 ,m_nLevOther(0)
729 ,m_nLevShorter(0)
730 ,m_nLevLonger(0)
731 ,m_nPosition(MATCHING_ANYWHERE)
732 ,m_nTransliterationFlags(0)
735 fillControlTexts(arrFields);
736 Init(sVisibleFields);
740 FmSearchEngine::~FmSearchEngine()
742 clearControlTexts();
747 void FmSearchEngine::SetIgnoreWidthCJK(bool bSet)
749 if (bSet)
750 m_nTransliterationFlags |= TransliterationModules_IGNORE_WIDTH;
751 else
752 m_nTransliterationFlags &= ~TransliterationModules_IGNORE_WIDTH;
756 bool FmSearchEngine::GetIgnoreWidthCJK() const
758 return 0 != (m_nTransliterationFlags & TransliterationModules_IGNORE_WIDTH);
762 void FmSearchEngine::SetCaseSensitive(bool bSet)
764 if (bSet)
765 m_nTransliterationFlags &= ~TransliterationModules_IGNORE_CASE;
766 else
767 m_nTransliterationFlags |= TransliterationModules_IGNORE_CASE;
771 bool FmSearchEngine::GetCaseSensitive() const
773 return 0 == (m_nTransliterationFlags & TransliterationModules_IGNORE_CASE);
777 void FmSearchEngine::clearControlTexts()
779 for ( ControlTextSuppliers::iterator aIter = m_aControlTexts.begin();
780 aIter < m_aControlTexts.end();
781 ++aIter
784 delete *aIter;
786 m_aControlTexts.clear();
790 void FmSearchEngine::fillControlTexts(const InterfaceArray& arrFields)
792 clearControlTexts();
793 Reference< XInterface > xCurrent;
794 for (sal_uInt32 i=0; i<arrFields.size(); ++i)
796 xCurrent = arrFields.at(i);
797 DBG_ASSERT(xCurrent.is(), "FmSearchEngine::fillControlTexts : invalid field interface !");
798 // check which type of control this is
799 Reference< ::com::sun::star::awt::XTextComponent > xAsText(xCurrent, UNO_QUERY);
800 if (xAsText.is())
802 m_aControlTexts.insert(m_aControlTexts.end(), new SimpleTextWrapper(xAsText));
803 continue;
806 Reference< ::com::sun::star::awt::XListBox > xAsListBox(xCurrent, UNO_QUERY);
807 if (xAsListBox.is())
809 m_aControlTexts.insert(m_aControlTexts.end(), new ListBoxWrapper(xAsListBox));
810 continue;
813 Reference< ::com::sun::star::awt::XCheckBox > xAsCheckBox(xCurrent, UNO_QUERY);
814 DBG_ASSERT(xAsCheckBox.is(), "FmSearchEngine::fillControlTexts : invalid field interface (no supported type) !");
815 // we don't have any more options ...
816 m_aControlTexts.insert(m_aControlTexts.end(), new CheckBoxWrapper(xAsCheckBox));
821 void FmSearchEngine::Init(const OUString& sVisibleFields)
823 // analyze the fields
824 // additionally, create the mapping: because the list of used columns can be shorter than the list
825 // of columns of the cursor, we need a mapping: "used column numer n" -> "cursor column m"
826 m_arrFieldMapping.clear();
828 // important: The case of the columns does not need to be exact - for instance:
829 // - a user created a form which works on a table, for which the driver returns a column name "COLUMN"
830 // - the driver itself works case-insensitve with column names
831 // - a control in the form is bound to "column" - not the different case
832 // In such a scenario, the form and the field would work okay, but we here need to case for the different case
833 // explicitly
834 // #i8755#
836 // so first of all, check if the database handles identifiers case sensitive
837 Reference< XConnection > xConn;
838 Reference< XDatabaseMetaData > xMeta;
839 Reference< XPropertySet > xCursorProps( IFACECAST( m_xSearchCursor ), UNO_QUERY );
840 if ( xCursorProps.is() )
844 xCursorProps->getPropertyValue( FM_PROP_ACTIVE_CONNECTION ) >>= xConn;
846 catch( const Exception& ) { /* silent this - will be asserted below */ }
848 if ( xConn.is() )
849 xMeta = xConn->getMetaData();
850 OSL_ENSURE( xMeta.is(), "FmSearchEngine::Init: very strange cursor (could not derive connection meta data from it)!" );
852 bool bCaseSensitiveIdentifiers = true; // assume case sensivity
853 if ( xMeta.is() )
854 bCaseSensitiveIdentifiers = xMeta->supportsMixedCaseQuotedIdentifiers();
856 // now that we have this information, we need a collator which is able to case (in)sentively compare strings
857 m_aStringCompare.loadDefaultCollator( SvtSysLocale().GetLanguageTag().getLocale(),
858 bCaseSensitiveIdentifiers ? 0 : ::com::sun::star::i18n::CollatorOptions::CollatorOptions_IGNORE_CASE );
862 // der Cursor kann mir einen Record (als PropertySet) liefern, dieser unterstuetzt den DatabaseRecord-Service
863 Reference< ::com::sun::star::sdbcx::XColumnsSupplier > xSupplyCols(IFACECAST(m_xSearchCursor), UNO_QUERY);
864 DBG_ASSERT(xSupplyCols.is(), "FmSearchEngine::Init : invalid cursor (no columns supplier) !");
865 Reference< ::com::sun::star::container::XNameAccess > xAllFieldNames = xSupplyCols->getColumns();
866 Sequence< OUString > seqFieldNames = xAllFieldNames->getElementNames();
867 OUString* pFieldNames = seqFieldNames.getArray();
870 OUString sCurrentField;
871 OUString sVis(sVisibleFields.getStr());
872 sal_Int32 nIndex = 0;
875 sCurrentField = sVis.getToken(0, ';' , nIndex);
877 // in der Feld-Sammlung suchen
878 sal_Int32 nFoundIndex = -1;
879 for (sal_Int32 j=0; j<seqFieldNames.getLength(); ++j, ++pFieldNames)
881 if ( 0 == m_aStringCompare.compareString( *pFieldNames, sCurrentField ) )
883 nFoundIndex = j;
884 break;
887 // set the field selection back to the first
888 pFieldNames = seqFieldNames.getArray();
889 DBG_ASSERT(nFoundIndex != -1, "FmSearchEngine::Init : Invalid field name were given !");
890 m_arrFieldMapping.push_back(nFoundIndex);
892 while ( nIndex >= 0 );
894 catch (const Exception&)
896 OSL_FAIL("Exception occurred!");
902 void FmSearchEngine::SetFormatterUsing(bool bSet)
904 if (m_bFormatter == bSet)
905 return;
906 m_bFormatter = bSet;
908 if (m_bUsingTextComponents)
910 // ich benutzte keinen Formatter, sondern TextComponents -> der SearchIterator muss angepasst werden
913 if (m_bFormatter)
915 DBG_ASSERT(m_xSearchCursor == m_xClonedIterator, "FmSearchEngine::SetFormatterUsing : inkonsistenter Zustand !");
916 m_xSearchCursor = m_xOriginalIterator;
917 m_xSearchCursor.moveToBookmark(m_xClonedIterator.getBookmark());
918 // damit ich mit dem neuen Iterator wirklich dort weitermache, wo ich vorher aufgehoert habe
920 else
922 DBG_ASSERT(m_xSearchCursor == m_xOriginalIterator, "FmSearchEngine::SetFormatterUsing : inkonsistenter Zustand !");
923 m_xSearchCursor = m_xClonedIterator;
924 m_xSearchCursor.moveToBookmark(m_xOriginalIterator.getBookmark());
927 catch( const Exception& )
929 DBG_UNHANDLED_EXCEPTION();
932 // ich muss die Fields neu binden, da der Textaustausch eventuell ueber diese Fields erfolgt und sich der unterliegende Cursor
933 // geaendert hat
934 RebuildUsedFields(m_nCurrentFieldIndex, true);
936 else
937 InvalidatePreviousLoc();
941 void FmSearchEngine::PropagateProgress(bool _bDontPropagateOverflow)
943 if (m_aProgressHandler.IsSet())
945 FmSearchProgress aProgress;
948 aProgress.aSearchState = FmSearchProgress::STATE_PROGRESS;
949 aProgress.nCurrentRecord = m_xSearchCursor.getRow() - 1;
950 if (m_bForward)
951 aProgress.bOverflow = !_bDontPropagateOverflow && m_xSearchCursor.isFirst();
952 else
953 aProgress.bOverflow = !_bDontPropagateOverflow && m_xSearchCursor.isLast();
955 catch( const Exception& )
957 DBG_UNHANDLED_EXCEPTION();
960 m_aProgressHandler.Call(&aProgress);
965 void FmSearchEngine::SearchNextImpl()
967 DBG_ASSERT(!(m_bWildcard && m_bRegular) && !(m_bRegular && m_bLevenshtein) && !(m_bLevenshtein && m_bWildcard),
968 "FmSearchEngine::SearchNextImpl : Suchparameter schliessen sich gegenseitig aus !");
970 DBG_ASSERT(m_xSearchCursor.is(), "FmSearchEngine::SearchNextImpl : habe ungueltigen Iterator !");
972 // die Parameter der Suche
973 OUString strSearchExpression(m_strSearchExpression); // brauche ich non-const
974 if (!GetCaseSensitive())
975 // norm the string
976 strSearchExpression = m_aCharacterClassficator.lowercase(strSearchExpression);
978 if (!m_bRegular && !m_bLevenshtein)
979 { // 'normale' Suche fuehre ich auf jeden Fall ueber WildCards durch, muss aber vorher je nach Modus den OUString anpassen
981 if (!m_bWildcard)
982 { // da natuerlich in allen anderen Faellen auch * und ? im Suchstring erlaubt sind, aber nicht als WildCards zaehlen
983 // sollen, muss ich normieren
984 OUString aTmp(strSearchExpression);
985 const OUString s_sStar("\\*");
986 const OUString s_sQuotation("\\?");
987 aTmp = aTmp.replaceAll("*", s_sStar);
988 aTmp = aTmp.replaceAll("?", s_sQuotation);
989 strSearchExpression = aTmp;
991 switch (m_nPosition)
993 case MATCHING_ANYWHERE :
994 strSearchExpression = "*" + strSearchExpression + "*";
995 break;
996 case MATCHING_BEGINNING :
997 strSearchExpression = strSearchExpression + "*";
998 break;
999 case MATCHING_END :
1000 strSearchExpression = "*" + strSearchExpression;
1001 break;
1002 case MATCHING_WHOLETEXT :
1003 break;
1004 default :
1005 OSL_FAIL("FmSearchEngine::SearchNextImpl() : die Methoden-Listbox duerfte nur 4 Eintraege enthalten ...");
1010 // fuer Arbeit auf Feldliste
1011 FieldCollection::iterator iterBegin = m_arrUsedFields.begin();
1012 FieldCollection::iterator iterEnd = m_arrUsedFields.end();
1013 FieldCollection::iterator iterFieldCheck;
1015 sal_Int32 nFieldPos;
1017 if (HasPreviousLoc())
1019 DBG_ASSERT(EQUAL_BOOKMARKS(m_aPreviousLocBookmark, m_xSearchCursor.getBookmark()),
1020 "FmSearchEngine::SearchNextImpl : ungueltige Position !");
1021 iterFieldCheck = m_iterPreviousLocField;
1022 // im Feld nach (oder vor) der letzten Fundstelle weitermachen
1023 nFieldPos = iterFieldCheck - iterBegin;
1024 MoveField(nFieldPos, iterFieldCheck, iterBegin, iterEnd);
1026 else
1028 if (m_bForward)
1029 iterFieldCheck = iterBegin;
1030 else
1032 iterFieldCheck = iterEnd;
1033 --iterFieldCheck;
1035 nFieldPos = iterFieldCheck - iterBegin;
1038 PropagateProgress(true);
1039 SEARCH_RESULT srResult;
1040 if (m_eSearchForType != SEARCHFOR_STRING)
1041 srResult = SearchSpecial(m_eSearchForType == SEARCHFOR_NULL, nFieldPos, iterFieldCheck, iterBegin, iterEnd);
1042 else if (!m_bRegular && !m_bLevenshtein)
1043 srResult = SearchWildcard(strSearchExpression, nFieldPos, iterFieldCheck, iterBegin, iterEnd);
1044 else
1045 srResult = SearchRegularApprox(strSearchExpression, nFieldPos, iterFieldCheck, iterBegin, iterEnd);
1047 m_srResult = srResult;
1049 if (SR_ERROR == m_srResult)
1050 return;
1052 // gefunden ?
1053 if (SR_FOUND == m_srResult)
1055 // die Pos merken
1056 try { m_aPreviousLocBookmark = m_xSearchCursor.getBookmark(); }
1057 catch ( const Exception& ) { DBG_UNHANDLED_EXCEPTION(); }
1058 m_iterPreviousLocField = iterFieldCheck;
1060 else
1061 // die "letzte Fundstelle" invalidieren
1062 InvalidatePreviousLoc();
1066 IMPL_LINK(FmSearchEngine, OnSearchTerminated, FmSearchThread*, /*pThread*/)
1068 if (!m_aProgressHandler.IsSet())
1069 return 0L;
1071 FmSearchProgress aProgress;
1074 switch (m_srResult)
1076 case SR_ERROR :
1077 aProgress.aSearchState = FmSearchProgress::STATE_ERROR;
1078 break;
1079 case SR_FOUND :
1080 aProgress.aSearchState = FmSearchProgress::STATE_SUCCESSFULL;
1081 aProgress.aBookmark = m_aPreviousLocBookmark;
1082 aProgress.nFieldIndex = m_iterPreviousLocField - m_arrUsedFields.begin();
1083 break;
1084 case SR_NOTFOUND :
1085 aProgress.aSearchState = FmSearchProgress::STATE_NOTHINGFOUND;
1086 aProgress.aBookmark = m_xSearchCursor.getBookmark();
1087 break;
1088 case SR_CANCELED :
1089 aProgress.aSearchState = FmSearchProgress::STATE_CANCELED;
1090 aProgress.aBookmark = m_xSearchCursor.getBookmark();
1091 break;
1093 aProgress.nCurrentRecord = m_xSearchCursor.getRow() - 1;
1095 catch( const Exception& )
1097 DBG_UNHANDLED_EXCEPTION();
1100 // per definitionem muss der Link Thread-sicher sein (das verlange ich einfach), so dass ich mich um so etwas hier nicht kuemmern muss
1101 m_aProgressHandler.Call(&aProgress);
1103 m_bSearchingCurrently = false;
1104 return 0L;
1108 IMPL_LINK(FmSearchEngine, OnNewRecordCount, void*, pCounterAsVoid)
1110 if (!m_aProgressHandler.IsSet())
1111 return 0L;
1113 FmSearchProgress aProgress;
1114 aProgress.nCurrentRecord = reinterpret_cast<sal_uIntPtr>(pCounterAsVoid);
1115 aProgress.aSearchState = FmSearchProgress::STATE_PROGRESS_COUNTING;
1116 m_aProgressHandler.Call(&aProgress);
1118 return 0L;
1122 bool FmSearchEngine::CancelRequested()
1124 m_aCancelAsynchAccess.acquire();
1125 bool bReturn = m_bCancelAsynchRequest;
1126 m_aCancelAsynchAccess.release();
1127 return bReturn;
1131 void FmSearchEngine::CancelSearch()
1133 m_aCancelAsynchAccess.acquire();
1134 m_bCancelAsynchRequest = true;
1135 m_aCancelAsynchAccess.release();
1139 bool FmSearchEngine::SwitchToContext(const Reference< ::com::sun::star::sdbc::XResultSet > & xCursor, const OUString& sVisibleFields, const InterfaceArray& arrFields,
1140 sal_Int32 nFieldIndex)
1142 DBG_ASSERT(!m_bSearchingCurrently, "FmSearchEngine::SwitchToContext : please do not call while I'm searching !");
1143 if (m_bSearchingCurrently)
1144 return false;
1146 m_xSearchCursor = xCursor;
1147 m_xOriginalIterator = xCursor;
1148 m_xClonedIterator = CursorWrapper(m_xOriginalIterator, true);
1149 m_bUsingTextComponents = true;
1151 fillControlTexts(arrFields);
1153 Init(sVisibleFields);
1154 RebuildUsedFields(nFieldIndex, true);
1156 return true;
1160 void FmSearchEngine::ImplStartNextSearch()
1162 m_bCancelAsynchRequest = false;
1163 m_bSearchingCurrently = true;
1165 if (m_eMode == SM_USETHREAD)
1167 FmSearchThread* pSearcher = new FmSearchThread(this);
1168 // der loescht sich nach Beendigung selber ...
1169 pSearcher->setTerminationHandler(LINK(this, FmSearchEngine, OnSearchTerminated));
1171 pSearcher->createSuspended();
1172 pSearcher->setPriority(osl_Thread_PriorityLowest);
1173 pSearcher->resume();
1175 else
1177 SearchNextImpl();
1178 LINK(this, FmSearchEngine, OnSearchTerminated).Call(NULL);
1183 void FmSearchEngine::SearchNext(const OUString& strExpression)
1185 m_strSearchExpression = strExpression;
1186 m_eSearchForType = SEARCHFOR_STRING;
1187 ImplStartNextSearch();
1191 void FmSearchEngine::SearchNextSpecial(bool _bSearchForNull)
1193 m_eSearchForType = _bSearchForNull ? SEARCHFOR_NULL : SEARCHFOR_NOTNULL;
1194 ImplStartNextSearch();
1198 void FmSearchEngine::StartOver(const OUString& strExpression)
1202 if (m_bForward)
1203 m_xSearchCursor.first();
1204 else
1205 m_xSearchCursor.last();
1207 catch( const Exception& )
1209 DBG_UNHANDLED_EXCEPTION();
1210 return;
1213 InvalidatePreviousLoc();
1214 SearchNext(strExpression);
1218 void FmSearchEngine::StartOverSpecial(bool _bSearchForNull)
1222 if (m_bForward)
1223 m_xSearchCursor.first();
1224 else
1225 m_xSearchCursor.last();
1227 catch( const Exception& )
1229 DBG_UNHANDLED_EXCEPTION();
1230 return;
1233 InvalidatePreviousLoc();
1234 SearchNextSpecial(_bSearchForNull);
1238 void FmSearchEngine::InvalidatePreviousLoc()
1240 m_aPreviousLocBookmark.setValue(0,cppu::UnoType<void>::get());
1241 m_iterPreviousLocField = m_arrUsedFields.end();
1245 void FmSearchEngine::RebuildUsedFields(sal_Int32 nFieldIndex, bool bForce)
1247 if (!bForce && (nFieldIndex == m_nCurrentFieldIndex))
1248 return;
1249 // (da ich keinen Wechsel des Iterators von aussen zulasse, heisst selber ::com::sun::star::sdbcx::Index auch immer selbe Spalte, also habe ich nix zu tun)
1251 DBG_ASSERT((nFieldIndex == -1) ||
1252 ((nFieldIndex >= 0) &&
1253 (static_cast<size_t>(nFieldIndex) < m_arrFieldMapping.size())),
1254 "FmSearchEngine::RebuildUsedFields : nFieldIndex is invalid!");
1255 // alle Felder, die ich durchsuchen muss, einsammeln
1256 m_arrUsedFields.clear();
1257 if (nFieldIndex == -1)
1259 Reference< ::com::sun::star::container::XIndexAccess > xFields;
1260 for (size_t i=0; i<m_arrFieldMapping.size(); ++i)
1262 Reference< ::com::sun::star::sdbcx::XColumnsSupplier > xSupplyCols(IFACECAST(m_xSearchCursor), UNO_QUERY);
1263 DBG_ASSERT(xSupplyCols.is(), "FmSearchEngine::RebuildUsedFields : invalid cursor (no columns supplier) !");
1264 xFields = Reference< ::com::sun::star::container::XIndexAccess > (xSupplyCols->getColumns(), UNO_QUERY);
1265 BuildAndInsertFieldInfo(xFields, m_arrFieldMapping[i]);
1268 else
1270 Reference< ::com::sun::star::container::XIndexAccess > xFields;
1271 Reference< ::com::sun::star::sdbcx::XColumnsSupplier > xSupplyCols(IFACECAST(m_xSearchCursor), UNO_QUERY);
1272 DBG_ASSERT(xSupplyCols.is(), "FmSearchEngine::RebuildUsedFields : invalid cursor (no columns supplier) !");
1273 xFields = Reference< ::com::sun::star::container::XIndexAccess > (xSupplyCols->getColumns(), UNO_QUERY);
1274 BuildAndInsertFieldInfo(xFields, m_arrFieldMapping[static_cast< size_t >(nFieldIndex)]);
1277 m_nCurrentFieldIndex = nFieldIndex;
1278 // und natuerlich beginne ich die naechste Suche wieder jungfraeulich
1279 InvalidatePreviousLoc();
1282 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */