xref: /AOO41X/main/svx/source/form/fmsrcimp.cxx (revision 1ecadb572e7010ff3b3382ad9bf179dbc6efadbb)
1 /*************************************************************************
2  *
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * Copyright 2000, 2010 Oracle and/or its affiliates.
6  *
7  * OpenOffice.org - a multi-platform office productivity suite
8  *
9  * This file is part of OpenOffice.org.
10  *
11  * OpenOffice.org is free software: you can redistribute it and/or modify
12  * it under the terms of the GNU Lesser General Public License version 3
13  * only, as published by the Free Software Foundation.
14  *
15  * OpenOffice.org is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU Lesser General Public License version 3 for more details
19  * (a copy is included in the LICENSE file that accompanied this code).
20  *
21  * You should have received a copy of the GNU Lesser General Public License
22  * version 3 along with OpenOffice.org.  If not, see
23  * <http://www.openoffice.org/license.html>
24  * for a copy of the LGPLv3 License.
25  *
26  ************************************************************************/
27 
28 // MARKER(update_precomp.py): autogen include statement, do not remove
29 #include "precompiled_svx.hxx"
30 
31 #ifndef _SVX_FMRESIDS_HRC
32 #include "svx/fmresids.hrc"
33 #endif
34 #include "svx/fmtools.hxx"
35 #include "svx/fmsrccfg.hxx"
36 #include <tools/debug.hxx>
37 #include <tools/diagnose_ex.h>
38 #include <tools/wldcrd.hxx>
39 #include <vcl/msgbox.hxx>
40 #include <tools/shl.hxx>
41 #include <svx/dialmgr.hxx>
42 #include <cppuhelper/servicefactory.hxx>
43 #include <vcl/svapp.hxx>
44 #include <unotools/textsearch.hxx>
45 #include <com/sun/star/util/SearchOptions.hpp>
46 #include <com/sun/star/util/SearchAlgorithms.hpp>
47 #include <com/sun/star/util/SearchResult.hpp>
48 #include <com/sun/star/util/SearchFlags.hpp>
49 #include <com/sun/star/lang/Locale.hpp>
50 #include <com/sun/star/i18n/TransliterationModules.hpp>
51 #include <com/sun/star/i18n/CollatorOptions.hpp>
52 
53 #ifndef _COM_SUN_STAR_SDDB_XCOLUMNSSUPPLIER_HPP_
54 #include <com/sun/star/sdbcx/XColumnsSupplier.hpp>
55 #endif
56 #include <com/sun/star/util/XNumberFormatter.hpp>
57 #include <com/sun/star/util/NumberFormat.hpp>
58 #include <com/sun/star/util/XNumberFormatsSupplier.hpp>
59 #include <com/sun/star/util/XNumberFormats.hpp>
60 #include <comphelper/processfactory.hxx>
61 
62 #ifndef _SVX_FMPROP_HRC
63 #include "fmprop.hrc"
64 #endif
65 #include "fmservs.hxx"
66 #include "svx/fmsrcimp.hxx"
67 #include <svx/fmsearch.hxx>
68 
69 #include <comphelper/numbers.hxx>
70 #include <unotools/syslocale.hxx>
71 
72 #define EQUAL_BOOKMARKS(a, b) a == b
73 
74 #define IFACECAST(c)          ((const Reference< XInterface >&)c)
75  // SUN C52 has some ambiguities without this cast ....
76 
77 using namespace ::com::sun::star::uno;
78 using namespace ::com::sun::star::util;
79 using namespace ::com::sun::star::lang;
80 using namespace ::com::sun::star::sdbc;
81 using namespace ::com::sun::star::i18n;
82 using namespace ::com::sun::star::beans;
83 using namespace ::svxform;
84 
85 
86 //========================================================================
87 // = FmSearchThread
88 //------------------------------------------------------------------------
89 void FmSearchThread::run()
90 {
91     m_pEngine->SearchNextImpl();
92 };
93 
94 //------------------------------------------------------------------------
95 void FmSearchThread::onTerminated()
96 {
97     if (m_aTerminationHdl.IsSet())
98         m_aTerminationHdl.Call(this);
99     delete this;
100 }
101 
102 //========================================================================
103 // = FmRecordCountListener
104 
105 //  SMART_UNO_IMPLEMENTATION(FmRecordCountListener, UsrObject);
106 
107 DBG_NAME(FmRecordCountListener);
108 //------------------------------------------------------------------------
109 FmRecordCountListener::FmRecordCountListener(const Reference< ::com::sun::star::sdbc::XResultSet > & dbcCursor)
110 {
111     DBG_CTOR(FmRecordCountListener,NULL);
112 
113     m_xListening = Reference< ::com::sun::star::beans::XPropertySet > (dbcCursor, UNO_QUERY);
114     if (!m_xListening.is())
115         return;
116 
117     if (::comphelper::getBOOL(m_xListening->getPropertyValue(FM_PROP_ROWCOUNTFINAL)))
118     {
119         m_xListening = NULL;
120         // there's nothing to do as the record count is already known
121         return;
122     }
123 
124     m_xListening->addPropertyChangeListener(FM_PROP_ROWCOUNT, (::com::sun::star::beans::XPropertyChangeListener*)this);
125 }
126 
127 //------------------------------------------------------------------------
128 Link FmRecordCountListener::SetPropChangeHandler(const Link& lnk)
129 {
130     Link lnkReturn = m_lnkWhoWantsToKnow;
131     m_lnkWhoWantsToKnow = lnk;
132 
133     if (m_xListening.is())
134         NotifyCurrentCount();
135 
136     return lnkReturn;
137 }
138 
139 //------------------------------------------------------------------------
140 FmRecordCountListener::~FmRecordCountListener()
141 {
142 
143     DBG_DTOR(FmRecordCountListener,NULL);
144 }
145 
146 //------------------------------------------------------------------------
147 void FmRecordCountListener::DisConnect()
148 {
149     if(m_xListening.is())
150         m_xListening->removePropertyChangeListener(FM_PROP_ROWCOUNT, (::com::sun::star::beans::XPropertyChangeListener*)this);
151     m_xListening = NULL;
152 }
153 
154 //------------------------------------------------------------------------
155 void SAL_CALL FmRecordCountListener::disposing(const ::com::sun::star::lang::EventObject& /*Source*/) throw( RuntimeException )
156 {
157     DBG_ASSERT(m_xListening.is(), "FmRecordCountListener::disposing should never have been called without a propset !");
158     DisConnect();
159 }
160 
161 //------------------------------------------------------------------------
162 void FmRecordCountListener::NotifyCurrentCount()
163 {
164     if (m_lnkWhoWantsToKnow.IsSet())
165     {
166         DBG_ASSERT(m_xListening.is(), "FmRecordCountListener::NotifyCurrentCount : I have no propset ... !?");
167         void* pTheCount = (void*)::comphelper::getINT32(m_xListening->getPropertyValue(FM_PROP_ROWCOUNT));
168         m_lnkWhoWantsToKnow.Call(pTheCount);
169     }
170 }
171 
172 //------------------------------------------------------------------------
173 void FmRecordCountListener::propertyChange(const  ::com::sun::star::beans::PropertyChangeEvent& /*evt*/) throw(::com::sun::star::uno::RuntimeException)
174 {
175     NotifyCurrentCount();
176 }
177 
178 //========================================================================
179 // FmSearchEngine - local classes
180 //------------------------------------------------------------------------
181 SimpleTextWrapper::SimpleTextWrapper(const Reference< ::com::sun::star::awt::XTextComponent > & _xText)
182     :ControlTextWrapper(_xText.get())
183     ,m_xText(_xText)
184 {
185     DBG_ASSERT(m_xText.is(), "FmSearchEngine::SimpleTextWrapper::SimpleTextWrapper : invalid argument !");
186 }
187 
188 //------------------------------------------------------------------------
189 ::rtl::OUString SimpleTextWrapper::getCurrentText() const
190 {
191     return m_xText->getText();
192 }
193 
194 //------------------------------------------------------------------------
195 ListBoxWrapper::ListBoxWrapper(const Reference< ::com::sun::star::awt::XListBox > & _xBox)
196     :ControlTextWrapper(_xBox.get())
197     ,m_xBox(_xBox)
198 {
199     DBG_ASSERT(m_xBox.is(), "FmSearchEngine::ListBoxWrapper::ListBoxWrapper : invalid argument !");
200 }
201 
202 //------------------------------------------------------------------------
203 ::rtl::OUString ListBoxWrapper::getCurrentText() const
204 {
205     return m_xBox->getSelectedItem();
206 }
207 
208 //------------------------------------------------------------------------
209 CheckBoxWrapper::CheckBoxWrapper(const Reference< ::com::sun::star::awt::XCheckBox > & _xBox)
210     :ControlTextWrapper(_xBox.get())
211     ,m_xBox(_xBox)
212 {
213     DBG_ASSERT(m_xBox.is(), "FmSearchEngine::CheckBoxWrapper::CheckBoxWrapper : invalid argument !");
214 }
215 
216 //------------------------------------------------------------------------
217 ::rtl::OUString CheckBoxWrapper::getCurrentText() const
218 {
219     switch ((TriState)m_xBox->getState())
220     {
221         case STATE_NOCHECK: return rtl::OUString::createFromAscii("0");
222         case STATE_CHECK: return rtl::OUString::createFromAscii("1");
223         default: break;
224     }
225     return rtl::OUString();
226 }
227 
228 //========================================================================
229 // = FmSearchEngine
230 //------------------------------------------------------------------------
231 sal_Bool FmSearchEngine::MoveCursor()
232 {
233     sal_Bool bSuccess = sal_True;
234     try
235     {
236         if (m_bForward)
237             if (m_xSearchCursor.isLast())
238                 m_xSearchCursor.first();
239             else
240                 m_xSearchCursor.next();
241         else
242             if (m_xSearchCursor.isFirst())
243             {
244                 FmRecordCountListener* prclListener = new FmRecordCountListener(m_xSearchCursor);
245                 prclListener->acquire();
246                 prclListener->SetPropChangeHandler(LINK(this, FmSearchEngine, OnNewRecordCount));
247 
248                 m_xSearchCursor.last();
249 
250                 prclListener->DisConnect();
251                 prclListener->release();
252             }
253             else
254                 m_xSearchCursor.previous();
255     }
256     catch(::com::sun::star::sdbc::SQLException  e)
257     {
258 #if OSL_DEBUG_LEVEL > 0
259         String sDebugMessage;
260         sDebugMessage.AssignAscii("FmSearchEngine::MoveCursor : catched a DatabaseException (");
261         sDebugMessage += (const sal_Unicode*)e.SQLState;
262         sDebugMessage.AppendAscii(") !");
263         DBG_ERROR(ByteString(sDebugMessage, RTL_TEXTENCODING_ASCII_US).GetBuffer());
264 #endif
265         bSuccess = sal_False;
266     }
267     catch(Exception  e)
268     {
269 #if OSL_DEBUG_LEVEL > 0
270         UniString sDebugMessage;
271         sDebugMessage.AssignAscii("FmSearchEngine::MoveCursor : catched an Exception (");
272         sDebugMessage += (const sal_Unicode*)e.Message;
273         sDebugMessage.AppendAscii(") !");
274         DBG_ERROR(ByteString(sDebugMessage, RTL_TEXTENCODING_ASCII_US).GetBuffer());
275 #endif
276         bSuccess = sal_False;
277     }
278     catch(...)
279     {
280         DBG_ERROR("FmSearchEngine::MoveCursor : catched an unknown Exception !");
281         bSuccess = sal_False;
282     }
283 
284     return bSuccess;
285 }
286 
287 //------------------------------------------------------------------------
288 sal_Bool FmSearchEngine::MoveField(sal_Int32& nPos, FieldCollectionIterator& iter, const FieldCollectionIterator& iterBegin, const FieldCollectionIterator& iterEnd)
289 {
290     sal_Bool bSuccess(sal_True);
291     if (m_bForward)
292     {
293         ++iter;
294         ++nPos;
295         if (iter == iterEnd)
296         {
297             bSuccess = MoveCursor();
298             iter = iterBegin;
299             nPos = 0;
300         }
301     } else
302     {
303         if (iter == iterBegin)
304         {
305             bSuccess = MoveCursor();
306             iter = iterEnd;
307             nPos = iter-iterBegin;
308         }
309         --iter;
310         --nPos;
311     }
312     return bSuccess;
313 }
314 
315 //------------------------------------------------------------------------
316 void FmSearchEngine::BuildAndInsertFieldInfo(const Reference< ::com::sun::star::container::XIndexAccess > & xAllFields, sal_Int32 nField)
317 {
318 	DBG_ASSERT( xAllFields.is() && ( nField >= 0 ) && ( nField < xAllFields->getCount() ),
319 		"FmSearchEngine::BuildAndInsertFieldInfo: invalid field descriptor!" );
320 
321 	// das Feld selber
322     Reference< XInterface > xCurrentField;
323     xAllFields->getByIndex(nField) >>= xCurrentField;
324 
325     // von dem weiss ich jetzt, dass es den DatabaseRecord-Service unterstuetzt (hoffe ich)
326     // fuer den FormatKey und den Typ brauche ich das PropertySet
327     Reference< ::com::sun::star::beans::XPropertySet >  xProperties(xCurrentField, UNO_QUERY);
328 
329     // die FieldInfo dazu aufbauen
330     FieldInfo fiCurrent;
331     fiCurrent.xContents = Reference< ::com::sun::star::sdb::XColumn > (xCurrentField, UNO_QUERY);
332     fiCurrent.nFormatKey = ::comphelper::getINT32(xProperties->getPropertyValue(FM_PROP_FORMATKEY));
333     fiCurrent.bDoubleHandling = sal_False;
334     if (m_xFormatSupplier.is())
335     {
336         Reference< ::com::sun::star::util::XNumberFormats >  xNumberFormats(m_xFormatSupplier->getNumberFormats());
337 
338         sal_Int16 nFormatType = ::comphelper::getNumberFormatType(xNumberFormats, fiCurrent.nFormatKey) & ~((sal_Int16)::com::sun::star::util::NumberFormat::DEFINED);
339         fiCurrent.bDoubleHandling = (nFormatType != ::com::sun::star::util::NumberFormat::TEXT);
340     }
341 
342     // und merken
343     m_arrUsedFields.insert(m_arrUsedFields.end(), fiCurrent);
344 
345 }
346 //------------------------------------------------------------------------
347 ::rtl::OUString FmSearchEngine::FormatField(const FieldInfo& rField)
348 {
349     DBG_ASSERT(!m_bUsingTextComponents, "FmSearchEngine::FormatField : im UsingTextComponents-Mode bitte FormatField(sal_Int32) benutzen !");
350 
351     if (!m_xFormatter.is())
352         return ::rtl::OUString();
353     // sonst werden Datumsflder zum Beispiel zu irgendeinem Default-Wert formatiert
354 
355     ::rtl::OUString sReturn;
356     try
357     {
358         if (rField.bDoubleHandling)
359         {
360             double fValue = rField.xContents->getDouble();
361             if (!rField.xContents->wasNull())
362                 sReturn = m_xFormatter->convertNumberToString(rField.nFormatKey, fValue);
363         }
364         else
365         {
366             ::rtl::OUString sValue = rField.xContents->getString();
367             if (!rField.xContents->wasNull())
368                 sReturn = m_xFormatter->formatString(rField.nFormatKey, sValue);
369         }
370     }
371     catch(...)
372     {
373     }
374 
375 
376     return sReturn;
377 }
378 
379 //------------------------------------------------------------------------
380 ::rtl::OUString FmSearchEngine::FormatField(sal_Int32 nWhich)
381 {
382     if (m_bUsingTextComponents)
383     {
384         DBG_ASSERT((sal_uInt32)nWhich < m_aControlTexts.size(), "FmSearchEngine::FormatField(sal_Int32) : invalid position !");
385         DBG_ASSERT(m_aControlTexts[nWhich] != NULL, "FmSearchEngine::FormatField(sal_Int32) : invalid object in array !");
386         DBG_ASSERT(m_aControlTexts[nWhich]->getControl().is(), "FmSearchEngine::FormatField : invalid control !");
387 
388         if (m_nCurrentFieldIndex != -1)
389         {
390             DBG_ASSERT((nWhich == 0) || (nWhich == m_nCurrentFieldIndex), "FmSearchEngine::FormatField : Parameter nWhich ist ungueltig");
391             // analoge Situation wie unten
392             nWhich = m_nCurrentFieldIndex;
393         }
394 
395         DBG_ASSERT((nWhich >= 0) && ((sal_uInt32)nWhich < m_aControlTexts.size()),
396             "FmSearchEngine::FormatField : invalid argument nWhich !");
397         return m_aControlTexts[m_nCurrentFieldIndex == -1 ? nWhich : m_nCurrentFieldIndex]->getCurrentText();
398     }
399     else
400     {
401         if (m_nCurrentFieldIndex != -1)
402         {
403             DBG_ASSERT((nWhich == 0) || (nWhich == m_nCurrentFieldIndex), "FmSearchEngine::FormatField : Parameter nWhich ist ungueltig");
404             // ich bin im single-field-modus, da ist auch die richtige Feld-Nummer erlaubt, obwohl dann der richtige ::com::sun::star::sdbcx::Index
405             // fuer meinen Array-Zugriff natuerlich 0 ist
406             nWhich = 0;
407         }
408 
409         DBG_ASSERT((nWhich>=0) && (nWhich < (m_arrUsedFields.end() - m_arrUsedFields.begin())),
410             "FmSearchEngine::FormatField : Parameter nWhich ist ungueltig");
411         return FormatField(m_arrUsedFields[nWhich]);
412     }
413 }
414 
415 //------------------------------------------------------------------------
416 FmSearchEngine::SEARCH_RESULT FmSearchEngine::SearchSpecial(sal_Bool _bSearchForNull, sal_Int32& nFieldPos,
417     FieldCollectionIterator& iterFieldLoop, const FieldCollectionIterator& iterBegin, const FieldCollectionIterator& iterEnd)
418 {
419     // die Startposition merken
420     Any aStartMark;
421     try { aStartMark = m_xSearchCursor.getBookmark(); }
422     catch ( const Exception& ) { DBG_UNHANDLED_EXCEPTION(); return SR_ERROR; }
423     FieldCollectionIterator iterInitialField = iterFieldLoop;
424 
425     // --------------------------------------------------------------
426     sal_Bool bFound(sal_False);
427     sal_Bool bMovedAround(sal_False);
428     do
429     {
430         if (m_eMode == SM_ALLOWSCHEDULE) //CHINA001  if (m_eMode == FmSearchDialog::SM_ALLOWSCHEDULE)
431         {
432             Application::Reschedule();
433             Application::Reschedule();
434             // do 2 reschedules because of #70226# : some things done within this loop's body may cause an user event
435             // to be posted (deep within vcl), and these user events will be handled before any keyinput or paintings
436             // or anything like that. So within each loop we create one user event and handle one user event (and no
437             // paintings and these), so the office seems to be frozen while searching.
438             // FS - 70226 - 02.12.99
439         }
440 
441         // der aktuell zu vergleichende Inhalt
442         iterFieldLoop->xContents->getString();  // needed for wasNull
443         bFound = _bSearchForNull == iterFieldLoop->xContents->wasNull();
444         if (bFound)
445             break;
446 
447         // naechstes Feld (implizit naechster Datensatz, wenn noetig)
448         if (!MoveField(nFieldPos, iterFieldLoop, iterBegin, iterEnd))
449         {   // beim Bewegen auf das naechste Feld ging was schief ... weitermachen ist nicht drin, da das naechste Mal genau
450             // das selbe bestimmt wieder schief geht, also Abbruch
451             // vorher aber noch, damit das Weitersuchen an der aktuellen Position weitermacht :
452             try { m_aPreviousLocBookmark = m_xSearchCursor.getBookmark(); }
453             catch ( const Exception& ) { DBG_UNHANDLED_EXCEPTION(); }
454             m_iterPreviousLocField = iterFieldLoop;
455             // und wech
456             return SR_ERROR;
457         }
458 
459         Any aCurrentBookmark;
460         try { aCurrentBookmark = m_xSearchCursor.getBookmark(); }
461         catch ( const Exception& ) { DBG_UNHANDLED_EXCEPTION(); return SR_ERROR; }
462 
463         bMovedAround = EQUAL_BOOKMARKS(aStartMark, aCurrentBookmark) && (iterFieldLoop == iterInitialField);
464 
465         if (nFieldPos == 0)
466             // das heisst, ich habe mich auf einen neuen Datensatz bewegt
467             PropagateProgress(bMovedAround);
468                 // if we moved to the starting position we don't have to propagate an 'overflow' message
469                 // FS - 07.12.99 - 68530
470 
471         // abbrechen gefordert ?
472         if (CancelRequested())
473             return SR_CANCELED;
474 
475     } while (!bMovedAround);
476 
477     return bFound ? SR_FOUND : SR_NOTFOUND;
478 }
479 
480 //------------------------------------------------------------------------
481 FmSearchEngine::SEARCH_RESULT FmSearchEngine::SearchWildcard(const ::rtl::OUString& strExpression, sal_Int32& nFieldPos,
482     FieldCollectionIterator& iterFieldLoop, const FieldCollectionIterator& iterBegin, const FieldCollectionIterator& iterEnd)
483 {
484     // die Startposition merken
485     Any aStartMark;
486     try { aStartMark = m_xSearchCursor.getBookmark(); }
487     catch ( const Exception& ) { DBG_UNHANDLED_EXCEPTION(); return SR_ERROR; }
488     FieldCollectionIterator iterInitialField = iterFieldLoop;
489 
490     WildCard aSearchExpression(strExpression);
491 
492     // --------------------------------------------------------------
493     sal_Bool bFound(sal_False);
494     sal_Bool bMovedAround(sal_False);
495     do
496     {
497         if (m_eMode == SM_ALLOWSCHEDULE) //CHINA001  if (m_eMode == FmSearchDialog::SM_ALLOWSCHEDULE)
498         {
499             Application::Reschedule();
500             Application::Reschedule();
501             // do 2 reschedules because of #70226# : some things done within this loop's body may cause an user event
502             // to be posted (deep within vcl), and these user events will be handled before any keyinput or paintings
503             // or anything like that. So within each loop we create one user event and hanel one user event (and no
504             // paintings and these), so the office seems to be frozen while searching.
505             // FS - 70226 - 02.12.99
506         }
507 
508         // der aktuell zu vergleichende Inhalt
509         ::rtl::OUString sCurrentCheck;
510         if (m_bFormatter)
511             sCurrentCheck = FormatField(nFieldPos);
512         else
513             sCurrentCheck = iterFieldLoop->xContents->getString();
514 
515         if (!GetCaseSensitive())
516 			// norm the string
517 			m_aCharacterClassficator.toLower_rtl(sCurrentCheck);
518 
519         // jetzt ist der Test einfach ...
520         bFound = aSearchExpression.Matches(sCurrentCheck);
521 
522         if (bFound)
523             break;
524 
525         // naechstes Feld (implizit naechster Datensatz, wenn noetig)
526         if (!MoveField(nFieldPos, iterFieldLoop, iterBegin, iterEnd))
527         {   // beim Bewegen auf das naechste Feld ging was schief ... weitermachen ist nicht drin, da das naechste Mal genau
528             // das selbe bestimmt wieder schief geht, also Abbruch
529             // vorher aber noch, damit das Weitersuchen an der aktuellen Position weitermacht :
530             try { m_aPreviousLocBookmark = m_xSearchCursor.getBookmark(); }
531             catch ( const Exception& ) { DBG_UNHANDLED_EXCEPTION(); }
532             m_iterPreviousLocField = iterFieldLoop;
533             // und wech
534             return SR_ERROR;
535         }
536 
537         Any aCurrentBookmark;
538         try { aCurrentBookmark = m_xSearchCursor.getBookmark(); }
539         catch ( const Exception& ) { DBG_UNHANDLED_EXCEPTION(); return SR_ERROR; }
540 
541         bMovedAround = EQUAL_BOOKMARKS(aStartMark, aCurrentBookmark) && (iterFieldLoop == iterInitialField);
542 
543         if (nFieldPos == 0)
544             // das heisst, ich habe mich auf einen neuen Datensatz bewegt
545             PropagateProgress(bMovedAround);
546                 // if we moved to the starting position we don't have to propagate an 'overflow' message
547                 // FS - 07.12.99 - 68530
548 
549         // abbrechen gefordert ?
550         if (CancelRequested())
551             return SR_CANCELED;
552 
553     } while (!bMovedAround);
554 
555     return bFound ? SR_FOUND : SR_NOTFOUND;
556 }
557 
558 //------------------------------------------------------------------------
559 FmSearchEngine::SEARCH_RESULT FmSearchEngine::SearchRegularApprox(const ::rtl::OUString& strExpression, sal_Int32& nFieldPos,
560     FieldCollectionIterator& iterFieldLoop, const FieldCollectionIterator& iterBegin, const FieldCollectionIterator& iterEnd)
561 {
562     DBG_ASSERT(m_bLevenshtein || m_bRegular,
563         "FmSearchEngine::SearchRegularApprox : ungueltiger Suchmodus !");
564     DBG_ASSERT(!m_bLevenshtein || !m_bRegular,
565         "FmSearchEngine::SearchRegularApprox : kann nicht nach regulaeren Ausdruecken und nach Aehnlichkeiten gleichzeitig suchen !");
566 
567     // Startposition merken
568     Any aStartMark;
569     try { aStartMark = m_xSearchCursor.getBookmark(); }
570     catch ( const Exception& ) { DBG_UNHANDLED_EXCEPTION(); return SR_ERROR; }
571     FieldCollectionIterator iterInitialField = iterFieldLoop;
572 
573     // Parameter sammeln
574     SearchOptions aParam;
575     aParam.algorithmType = m_bRegular ? SearchAlgorithms_REGEXP : SearchAlgorithms_APPROXIMATE;
576     aParam.searchFlag = 0;
577     aParam.transliterateFlags = GetTransliterationFlags();
578 	if ( !GetTransliteration() )
579 	{	// if transliteration is not enabled, the only flags which matter are IGNORE_CASE and IGNORE_WIDTH
580 		aParam.transliterateFlags &= TransliterationModules_IGNORE_CASE | TransliterationModules_IGNORE_WIDTH;
581 	}
582     if (m_bLevenshtein)
583     {
584         if (m_bLevRelaxed)
585             aParam.searchFlag |= SearchFlags::LEV_RELAXED;
586         aParam.changedChars = m_nLevOther;
587         aParam.deletedChars = m_nLevShorter;
588         aParam.insertedChars = m_nLevLonger;
589     }
590     aParam.searchString = strExpression;
591 	aParam.Locale = SvtSysLocale().GetLocaleData().getLocale();
592     ::utl::TextSearch aLocalEngine(aParam);
593 
594     // --------------------------------------------------------------
595     bool bFound = false;
596     sal_Bool bMovedAround(sal_False);
597     do
598     {
599         if (m_eMode == SM_ALLOWSCHEDULE) //CHINA001   if (m_eMode == FmSearchDialog::SM_ALLOWSCHEDULE)
600         {
601             Application::Reschedule();
602             Application::Reschedule();
603             // do 2 reschedules because of #70226# : some things done within this loop's body may cause an user event
604             // to be posted (deep within vcl), and these user events will be handled before any keyinput or paintings
605             // or anything like that. So within each loop we create one user event and handle one user event (and no
606             // paintings and these), so the office seems to be frozen while searching.
607             // FS - 70226 - 02.12.99
608         }
609 
610         // der aktuell zu vergleichende Inhalt
611         ::rtl::OUString sCurrentCheck;
612         if (m_bFormatter)
613             sCurrentCheck = FormatField(nFieldPos);
614         else
615             sCurrentCheck = iterFieldLoop->xContents->getString();
616 
617         // (don't care about case here, this is done by the TextSearch object, 'cause we passed our case parameter to it)
618 
619         xub_StrLen nStart = 0, nEnd = (xub_StrLen)sCurrentCheck.getLength();
620         bFound = aLocalEngine.SearchFrwrd(sCurrentCheck, &nStart, &nEnd);
621             // das heisst hier 'forward' aber das bezieht sich nur auf die Suche innerhalb von sCurrentCheck, hat also mit
622             // der Richtung meines Datensatz-Durchwanderns nix zu tun (darum kuemmert sich MoveField)
623 
624         // checken, ob die Position stimmt
625         if (bFound)
626         {
627             switch (m_nPosition)
628             {
629                 case MATCHING_WHOLETEXT :
630                     if (nEnd != sCurrentCheck.getLength())
631                     {
632                         bFound = false;
633                         break;
634                     }
635                     // laeuft in den naechsten Case rein !
636                 case MATCHING_BEGINNING :
637                     if (nStart != 0)
638                         bFound = false;
639                     break;
640                 case MATCHING_END :
641                     if (nEnd != sCurrentCheck.getLength())
642                         bFound = false;
643                     break;
644             }
645         }
646 
647         if (bFound) // immer noch ?
648             break;
649 
650         // naechstes Feld (implizit naechster Datensatz, wenn noetig)
651         if (!MoveField(nFieldPos, iterFieldLoop, iterBegin, iterEnd))
652         {   // beim Bewegen auf das naechste Feld ging was schief ... weitermachen ist nicht drin, da das naechste Mal genau
653             // das selbe bestimmt wieder schief geht, also Abbruch (ohne Fehlermeldung, von der erwarte ich, dass sie im Move
654             // angezeigt wurde)
655             // vorher aber noch, damit das Weitersuchen an der aktuellen Position weitermacht :
656             try { m_aPreviousLocBookmark = m_xSearchCursor.getBookmark(); }
657             catch ( const Exception& ) { DBG_UNHANDLED_EXCEPTION(); }
658             m_iterPreviousLocField = iterFieldLoop;
659             // und wech
660             return SR_ERROR;
661         }
662 
663         Any aCurrentBookmark;
664         try { aCurrentBookmark = m_xSearchCursor.getBookmark(); }
665         catch ( const Exception& ) { DBG_UNHANDLED_EXCEPTION(); return SR_ERROR; }
666         bMovedAround = EQUAL_BOOKMARKS(aStartMark, aCurrentBookmark) && (iterFieldLoop == iterInitialField);
667 
668         if (nFieldPos == 0)
669             // das heisst, ich habe mich auf einen neuen Datensatz bewegt
670             PropagateProgress(bMovedAround);
671                 // if we moved to the starting position we don't have to propagate an 'overflow' message
672                 // FS - 07.12.99 - 68530
673 
674         // abbrechen gefordert ?
675         if (CancelRequested())
676             return SR_CANCELED;
677 
678     } while (!bMovedAround);
679 
680     return bFound ? SR_FOUND : SR_NOTFOUND;
681 }
682 
683 
684 DBG_NAME(FmSearchEngine);
685 //------------------------------------------------------------------------
686 FmSearchEngine::FmSearchEngine(const Reference< XMultiServiceFactory >& _rxORB,
687 			const Reference< XResultSet > & xCursor, const ::rtl::OUString& sVisibleFields,
688 			const Reference< XNumberFormatsSupplier > & xFormatSupplier, FMSEARCH_MODE eMode)//CHINA001 const Reference< XNumberFormatsSupplier > & xFormatSupplier, FmSearchDialog::SEARCH_MODE eMode)
689     :m_xSearchCursor(xCursor)
690     ,m_xFormatSupplier(xFormatSupplier)
691 	,m_aCharacterClassficator( _rxORB, SvtSysLocale().GetLocaleData().getLocale() )
692 	,m_aStringCompare( _rxORB )
693     ,m_nCurrentFieldIndex(-2)   // -1 hat schon eine Bedeutung, also nehme ich -2 fuer 'ungueltig'
694     ,m_bUsingTextComponents(sal_False)
695     ,m_eSearchForType(SEARCHFOR_STRING)
696     ,m_srResult(SR_FOUND)
697     ,m_bSearchingCurrently(sal_False)
698     ,m_bCancelAsynchRequest(sal_False)
699     ,m_eMode(eMode)
700     ,m_bFormatter(sal_False)
701     ,m_bForward(sal_False)
702     ,m_bWildcard(sal_False)
703     ,m_bRegular(sal_False)
704     ,m_bLevenshtein(sal_False)
705     ,m_bTransliteration(sal_False)
706     ,m_bLevRelaxed(sal_False)
707     ,m_nLevOther(0)
708     ,m_nLevShorter(0)
709     ,m_nLevLonger(0)
710     ,m_nPosition(MATCHING_ANYWHERE)
711     ,m_nTransliterationFlags(0)
712 {
713     DBG_CTOR(FmSearchEngine,NULL);
714 
715     m_xFormatter = Reference< ::com::sun::star::util::XNumberFormatter > (::comphelper::getProcessServiceFactory()
716                     ->createInstance(FM_NUMBER_FORMATTER), UNO_QUERY);
717     if (m_xFormatter.is())
718         m_xFormatter->attachNumberFormatsSupplier(m_xFormatSupplier);
719 
720     Init(sVisibleFields);
721 }
722 
723 //------------------------------------------------------------------------
724 FmSearchEngine::FmSearchEngine(const Reference< XMultiServiceFactory >& _rxORB,
725 		const Reference< XResultSet > & xCursor, const ::rtl::OUString& sVisibleFields,
726 		const InterfaceArray& arrFields, FMSEARCH_MODE eMode)//CHINA001 const InterfaceArray& arrFields, FmSearchDialog::SEARCH_MODE eMode)
727     :m_xSearchCursor(xCursor)
728 	,m_aCharacterClassficator( _rxORB, SvtSysLocale().GetLocaleData().getLocale() )
729 	,m_aStringCompare( _rxORB )
730     ,m_nCurrentFieldIndex(-2)   // -1 hat schon eine Bedeutung, also nehme ich -2 fuer 'ungueltig'
731     ,m_bUsingTextComponents(sal_True)
732     ,m_xOriginalIterator(xCursor)
733     ,m_xClonedIterator(m_xOriginalIterator, sal_True)
734     ,m_eSearchForType(SEARCHFOR_STRING)
735     ,m_srResult(SR_FOUND)
736     ,m_bSearchingCurrently(sal_False)
737     ,m_bCancelAsynchRequest(sal_False)
738     ,m_eMode(eMode)
739     ,m_bFormatter(sal_True)     // das muss konsistent sein mit m_xSearchCursor, der i.A. == m_xOriginalIterator ist
740     ,m_bForward(sal_False)
741     ,m_bWildcard(sal_False)
742     ,m_bRegular(sal_False)
743     ,m_bLevenshtein(sal_False)
744     ,m_bTransliteration(sal_False)
745     ,m_bLevRelaxed(sal_False)
746     ,m_nLevOther(0)
747     ,m_nLevShorter(0)
748     ,m_nLevLonger(0)
749     ,m_nPosition(MATCHING_ANYWHERE)
750     ,m_nTransliterationFlags(0)
751 {
752     DBG_CTOR(FmSearchEngine,NULL);
753 
754     fillControlTexts(arrFields);
755     Init(sVisibleFields);
756 }
757 
758 //------------------------------------------------------------------------
759 FmSearchEngine::~FmSearchEngine()
760 {
761     clearControlTexts();
762 
763     DBG_DTOR(FmSearchEngine,NULL);
764 }
765 
766 //------------------------------------------------------------------------
767 void FmSearchEngine::SetIgnoreWidthCJK(sal_Bool bSet)
768 {
769     if (bSet)
770         m_nTransliterationFlags |= TransliterationModules_IGNORE_WIDTH;
771     else
772         m_nTransliterationFlags &= ~TransliterationModules_IGNORE_WIDTH;
773 }
774 
775 //------------------------------------------------------------------------
776 sal_Bool FmSearchEngine::GetIgnoreWidthCJK() const
777 {
778     return 0 != (m_nTransliterationFlags & TransliterationModules_IGNORE_WIDTH);
779 }
780 
781 //------------------------------------------------------------------------
782 void FmSearchEngine::SetCaseSensitive(sal_Bool bSet)
783 {
784     if (bSet)
785         m_nTransliterationFlags &= ~TransliterationModules_IGNORE_CASE;
786     else
787         m_nTransliterationFlags |= TransliterationModules_IGNORE_CASE;
788 }
789 
790 //------------------------------------------------------------------------
791 sal_Bool FmSearchEngine::GetCaseSensitive() const
792 {
793     return 0 == (m_nTransliterationFlags & TransliterationModules_IGNORE_CASE);
794 }
795 
796 //------------------------------------------------------------------------
797 void FmSearchEngine::clearControlTexts()
798 {
799     for (   ControlTextSuppliersIterator aIter = m_aControlTexts.begin();
800             aIter < m_aControlTexts.end();
801             ++aIter
802         )
803     {
804         delete *aIter;
805     }
806     m_aControlTexts.clear();
807 }
808 
809 //------------------------------------------------------------------------
810 void FmSearchEngine::fillControlTexts(const InterfaceArray& arrFields)
811 {
812     clearControlTexts();
813     Reference< XInterface >  xCurrent;
814     for (sal_uInt32 i=0; i<arrFields.size(); ++i)
815     {
816         xCurrent = arrFields.at(i);
817         DBG_ASSERT(xCurrent.is(), "FmSearchEngine::fillControlTexts : invalid field interface !");
818         // check which type of control this is
819         Reference< ::com::sun::star::awt::XTextComponent >  xAsText(xCurrent, UNO_QUERY);
820         if (xAsText.is())
821         {
822             m_aControlTexts.insert(m_aControlTexts.end(), new SimpleTextWrapper(xAsText));
823             continue;
824         }
825 
826         Reference< ::com::sun::star::awt::XListBox >  xAsListBox(xCurrent, UNO_QUERY);
827         if (xAsListBox.is())
828         {
829             m_aControlTexts.insert(m_aControlTexts.end(), new ListBoxWrapper(xAsListBox));
830             continue;
831         }
832 
833         Reference< ::com::sun::star::awt::XCheckBox >  xAsCheckBox(xCurrent, UNO_QUERY);
834         DBG_ASSERT(xAsCheckBox.is(), "FmSearchEngine::fillControlTexts : invalid field interface (no supported type) !");
835             // we don't have any more options ...
836         m_aControlTexts.insert(m_aControlTexts.end(), new CheckBoxWrapper(xAsCheckBox));
837     }
838 }
839 
840 //------------------------------------------------------------------------
841 void FmSearchEngine::Init(const ::rtl::OUString& sVisibleFields)
842 {
843 	// analyze the fields
844 	// additionally, create the mapping: because the list of used columns can be shorter than the list
845 	// of columns of the cursor, we need a mapping: "used column numer n" -> "cursor column m"
846     m_arrFieldMapping.clear();
847 
848 	// important: The case of the columns does not need to be exact - for instance:
849 	// - a user created a form which works on a table, for which the driver returns a column name "COLUMN"
850 	// - the driver itself works case-insensitve with column names
851 	// - a control in the form is bound to "column" - not the different case
852 	// In such a scenario, the form and the field would work okay, but we here need to case for the different case
853 	// explicitly
854 	// 2003-01-09 - #i8755# - fs@openoffice.org
855 
856 	// so first of all, check if the database handles identifiers case sensitive
857 	Reference< XConnection > xConn;
858 	Reference< XDatabaseMetaData > xMeta;
859 	Reference< XPropertySet > xCursorProps( IFACECAST( m_xSearchCursor ), UNO_QUERY );
860 	if ( xCursorProps.is() )
861 	{
862 		try
863 		{
864 			xCursorProps->getPropertyValue( FM_PROP_ACTIVE_CONNECTION ) >>= xConn;
865 		}
866 		catch( Exception& ) { /* silent this - will be asserted below */ }
867 	}
868 	if ( xConn.is() )
869 		xMeta = xConn->getMetaData();
870 	OSL_ENSURE( xMeta.is(), "FmSearchEngine::Init: very strange cursor (could not derive connection meta data from it)!" );
871 
872 	sal_Bool bCaseSensitiveIdentifiers = sal_True;	// assume case sensivity
873 	if ( xMeta.is() )
874 		bCaseSensitiveIdentifiers = xMeta->supportsMixedCaseQuotedIdentifiers();
875 
876 	// now that we have this information, we need a collator which is able to case (in)sentively compare strings
877 	m_aStringCompare.loadDefaultCollator( SvtSysLocale().GetLocaleData().getLocale(),
878 		bCaseSensitiveIdentifiers ? 0 : ::com::sun::star::i18n::CollatorOptions::CollatorOptions_IGNORE_CASE );
879 
880     try
881     {
882         // der Cursor kann mir einen Record (als PropertySet) liefern, dieser unterstuetzt den DatabaseRecord-Service
883         Reference< ::com::sun::star::sdbcx::XColumnsSupplier >  xSupplyCols(IFACECAST(m_xSearchCursor), UNO_QUERY);
884         DBG_ASSERT(xSupplyCols.is(), "FmSearchEngine::Init : invalid cursor (no columns supplier) !");
885         Reference< ::com::sun::star::container::XNameAccess >       xAllFieldNames = xSupplyCols->getColumns();
886         Sequence< ::rtl::OUString > seqFieldNames = xAllFieldNames->getElementNames();
887         ::rtl::OUString*            pFieldNames = seqFieldNames.getArray();
888 
889 
890         ::rtl::OUString sCurrentField;
891         UniString sVis(sVisibleFields.getStr());
892         xub_StrLen nLen = sVis.GetTokenCount();
893         for (xub_StrLen i=0; i<nLen; ++i)
894         {
895             sCurrentField = sVis.GetToken(i);
896 
897             // in der Feld-Sammlung suchen
898             sal_Int32 nFoundIndex = -1;
899             for (sal_Int32 j=0; j<seqFieldNames.getLength(); ++j, ++pFieldNames)
900             {
901 				if ( 0 == m_aStringCompare.compareString( *pFieldNames, sCurrentField ) )
902                 {
903                     nFoundIndex = j;
904                     break;
905                 }
906             }
907             // set the field selection back to the first
908             pFieldNames = seqFieldNames.getArray();;
909             DBG_ASSERT(nFoundIndex != -1, "FmSearchEngine::Init : Invalid field name were given !");
910             m_arrFieldMapping.push_back(nFoundIndex);
911         }
912     }
913     catch(Exception&)
914     {
915         DBG_ERROR("Exception occured!");
916     }
917 
918 }
919 
920 //------------------------------------------------------------------------
921 void FmSearchEngine::SetFormatterUsing(sal_Bool bSet)
922 {
923     if (m_bFormatter == bSet)
924         return;
925     m_bFormatter = bSet;
926 
927     if (m_bUsingTextComponents)
928     {
929         // ich benutzte keinen Formatter, sondern TextComponents -> der SearchIterator muss angepasst werden
930         try
931         {
932             if (m_bFormatter)
933             {
934                 DBG_ASSERT(m_xSearchCursor == m_xClonedIterator, "FmSearchEngine::SetFormatterUsing : inkonsistenter Zustand !");
935                 m_xSearchCursor = m_xOriginalIterator;
936                 m_xSearchCursor.moveToBookmark(m_xClonedIterator.getBookmark());
937                     // damit ich mit dem neuen Iterator wirklich dort weitermache, wo ich vorher aufgehoert habe
938             }
939             else
940             {
941                 DBG_ASSERT(m_xSearchCursor == m_xOriginalIterator, "FmSearchEngine::SetFormatterUsing : inkonsistenter Zustand !");
942                 m_xSearchCursor = m_xClonedIterator;
943                 m_xSearchCursor.moveToBookmark(m_xOriginalIterator.getBookmark());
944             }
945         }
946         catch( const Exception& )
947         {
948             DBG_UNHANDLED_EXCEPTION();
949         }
950 
951         // ich muss die Fields neu binden, da der Textaustausch eventuell ueber diese Fields erfolgt und sich der unterliegende Cursor
952         // geaendert hat
953         RebuildUsedFields(m_nCurrentFieldIndex, sal_True);
954     }
955     else
956         InvalidatePreviousLoc();
957 }
958 
959 //------------------------------------------------------------------------
960 void FmSearchEngine::PropagateProgress(sal_Bool _bDontPropagateOverflow)
961 {
962     if (m_aProgressHandler.IsSet())
963     {
964         FmSearchProgress aProgress;
965         try
966         {
967             aProgress.aSearchState = FmSearchProgress::STATE_PROGRESS;
968             aProgress.nCurrentRecord = m_xSearchCursor.getRow() - 1;
969             if (m_bForward)
970                 aProgress.bOverflow = !_bDontPropagateOverflow && m_xSearchCursor.isFirst();
971             else
972                 aProgress.bOverflow = !_bDontPropagateOverflow && m_xSearchCursor.isLast();
973         }
974         catch( const Exception& )
975         {
976             DBG_UNHANDLED_EXCEPTION();
977         }
978 
979         m_aProgressHandler.Call(&aProgress);
980     }
981 }
982 
983 //------------------------------------------------------------------------
984 void FmSearchEngine::SearchNextImpl()
985 {
986     DBG_ASSERT(!(m_bWildcard && m_bRegular) && !(m_bRegular && m_bLevenshtein) && !(m_bLevenshtein && m_bWildcard),
987         "FmSearchEngine::SearchNextImpl : Suchparameter schliessen sich gegenseitig aus !");
988 
989     DBG_ASSERT(m_xSearchCursor.is(), "FmSearchEngine::SearchNextImpl : habe ungueltigen Iterator !");
990 
991     // die Parameter der Suche
992     ::rtl::OUString strSearchExpression(m_strSearchExpression); // brauche ich non-const
993     if (!GetCaseSensitive())
994 		// norm the string
995 		m_aCharacterClassficator.toLower_rtl(strSearchExpression);
996 
997     if (!m_bRegular && !m_bLevenshtein)
998     {   // 'normale' Suche fuehre ich auf jeden Fall ueber WildCards durch, muss aber vorher je nach Modus den ::rtl::OUString anpassen
999 
1000         if (!m_bWildcard)
1001         {   // da natuerlich in allen anderen Faellen auch * und ? im Suchstring erlaubt sind, aber nicht als WildCards zaehlen
1002             // sollen, muss ich normieren
1003             UniString aTmp(strSearchExpression.getStr());
1004             static const UniString s_sStar = UniString::CreateFromAscii("\\*");
1005             static const UniString s_sQuotation = UniString::CreateFromAscii("\\?");
1006             aTmp.SearchAndReplaceAll('*', s_sStar);
1007             aTmp.SearchAndReplaceAll('?', s_sQuotation);
1008             strSearchExpression = aTmp;
1009 
1010             switch (m_nPosition)
1011             {
1012                 case MATCHING_ANYWHERE :
1013                     strSearchExpression = ::rtl::OUString::createFromAscii("*") + strSearchExpression
1014                     + ::rtl::OUString::createFromAscii("*");
1015                     break;
1016                 case MATCHING_BEGINNING :
1017                     strSearchExpression = strSearchExpression + ::rtl::OUString::createFromAscii("*");
1018                     break;
1019                 case MATCHING_END :
1020                     strSearchExpression = ::rtl::OUString::createFromAscii("*") + strSearchExpression;
1021                     break;
1022                 case MATCHING_WHOLETEXT :
1023                     break;
1024                 default :
1025                     DBG_ERROR("FmSearchEngine::SearchNextImpl() : die Methoden-Listbox duerfte nur 4 Eintraege enthalten ...");
1026             }
1027         }
1028     }
1029 
1030     // fuer Arbeit auf Feldliste
1031     FieldCollectionIterator iterBegin = m_arrUsedFields.begin();
1032     FieldCollectionIterator iterEnd = m_arrUsedFields.end();
1033     FieldCollectionIterator iterFieldCheck;
1034 
1035     sal_Int32 nFieldPos;
1036 
1037     if (HasPreviousLoc())
1038     {
1039         DBG_ASSERT(EQUAL_BOOKMARKS(m_aPreviousLocBookmark, m_xSearchCursor.getBookmark()),
1040             "FmSearchEngine::SearchNextImpl : ungueltige Position !");
1041         iterFieldCheck = m_iterPreviousLocField;
1042         // im Feld nach (oder vor) der letzten Fundstelle weitermachen
1043         nFieldPos = iterFieldCheck - iterBegin;
1044         MoveField(nFieldPos, iterFieldCheck, iterBegin, iterEnd);
1045     }
1046     else
1047     {
1048         if (m_bForward)
1049             iterFieldCheck = iterBegin;
1050         else
1051         {
1052             iterFieldCheck = iterEnd;
1053             --iterFieldCheck;
1054         }
1055         nFieldPos = iterFieldCheck - iterBegin;
1056     }
1057 
1058     PropagateProgress(sal_True);
1059     SEARCH_RESULT srResult;
1060     if (m_eSearchForType != SEARCHFOR_STRING)
1061         srResult = SearchSpecial(m_eSearchForType == SEARCHFOR_NULL, nFieldPos, iterFieldCheck, iterBegin, iterEnd);
1062     else if (!m_bRegular && !m_bLevenshtein)
1063         srResult = SearchWildcard(strSearchExpression, nFieldPos, iterFieldCheck, iterBegin, iterEnd);
1064     else
1065         srResult = SearchRegularApprox(strSearchExpression, nFieldPos, iterFieldCheck, iterBegin, iterEnd);
1066 
1067     m_srResult = srResult;
1068 
1069     if (SR_ERROR == m_srResult)
1070         return;
1071 
1072     // gefunden ?
1073     if (SR_FOUND == m_srResult)
1074     {
1075         // die Pos merken
1076         try { m_aPreviousLocBookmark = m_xSearchCursor.getBookmark(); }
1077         catch ( const Exception& ) { DBG_UNHANDLED_EXCEPTION(); }
1078         m_iterPreviousLocField = iterFieldCheck;
1079     }
1080     else
1081         // die "letzte Fundstelle" invalidieren
1082         InvalidatePreviousLoc();
1083 }
1084 
1085 //------------------------------------------------------------------------
1086 IMPL_LINK(FmSearchEngine, OnSearchTerminated, FmSearchThread*, /*pThread*/)
1087 {
1088     if (!m_aProgressHandler.IsSet())
1089         return 0L;
1090 
1091     FmSearchProgress aProgress;
1092     try
1093     {
1094         switch (m_srResult)
1095         {
1096             case SR_ERROR :
1097                 aProgress.aSearchState = FmSearchProgress::STATE_ERROR;
1098                 break;
1099             case SR_FOUND :
1100                 aProgress.aSearchState = FmSearchProgress::STATE_SUCCESSFULL;
1101                 aProgress.aBookmark = m_aPreviousLocBookmark;
1102                 aProgress.nFieldIndex = m_iterPreviousLocField - m_arrUsedFields.begin();
1103                 break;
1104             case SR_NOTFOUND :
1105                 aProgress.aSearchState = FmSearchProgress::STATE_NOTHINGFOUND;
1106                 aProgress.aBookmark = m_xSearchCursor.getBookmark();
1107                 break;
1108             case SR_CANCELED :
1109                 aProgress.aSearchState = FmSearchProgress::STATE_CANCELED;
1110                 aProgress.aBookmark = m_xSearchCursor.getBookmark();
1111                 break;
1112         }
1113         aProgress.nCurrentRecord = m_xSearchCursor.getRow() - 1;
1114     }
1115     catch( const Exception& )
1116     {
1117         DBG_UNHANDLED_EXCEPTION();
1118     }
1119 
1120     // per definitionem muss der Link Thread-sicher sein (das verlange ich einfach), so dass ich mich um so etwas hier nicht kuemmern muss
1121     m_aProgressHandler.Call(&aProgress);
1122 
1123     m_bSearchingCurrently = sal_False;
1124     return 0L;
1125 }
1126 
1127 //------------------------------------------------------------------------
1128 IMPL_LINK(FmSearchEngine, OnNewRecordCount, void*, pCounterAsVoid)
1129 {
1130     if (!m_aProgressHandler.IsSet())
1131         return 0L;
1132 
1133     FmSearchProgress aProgress;
1134     aProgress.nCurrentRecord = (sal_uIntPtr)pCounterAsVoid;
1135     aProgress.aSearchState = FmSearchProgress::STATE_PROGRESS_COUNTING;
1136     m_aProgressHandler.Call(&aProgress);
1137 
1138     return 0L;
1139 }
1140 
1141 //------------------------------------------------------------------------
1142 sal_Bool FmSearchEngine::CancelRequested()
1143 {
1144     m_aCancelAsynchAccess.acquire();
1145     sal_Bool bReturn = m_bCancelAsynchRequest;
1146     m_aCancelAsynchAccess.release();
1147     return bReturn;
1148 }
1149 
1150 //------------------------------------------------------------------------
1151 void FmSearchEngine::CancelSearch()
1152 {
1153     m_aCancelAsynchAccess.acquire();
1154     m_bCancelAsynchRequest = sal_True;
1155     m_aCancelAsynchAccess.release();
1156 }
1157 
1158 //------------------------------------------------------------------------
1159 sal_Bool FmSearchEngine::SwitchToContext(const Reference< ::com::sun::star::sdbc::XResultSet > & xCursor, const ::rtl::OUString& sVisibleFields, const InterfaceArray& arrFields,
1160     sal_Int32 nFieldIndex)
1161 {
1162     DBG_ASSERT(!m_bSearchingCurrently, "FmSearchEngine::SwitchToContext : please do not call while I'm searching !");
1163     if (m_bSearchingCurrently)
1164         return sal_False;
1165 
1166     m_xSearchCursor = xCursor;
1167     m_xOriginalIterator = xCursor;
1168     m_xClonedIterator = CursorWrapper(m_xOriginalIterator, sal_True);
1169     m_bUsingTextComponents = sal_True;
1170 
1171     fillControlTexts(arrFields);
1172 
1173     Init(sVisibleFields);
1174     RebuildUsedFields(nFieldIndex, sal_True);
1175 
1176     return sal_True;
1177 }
1178 
1179 //------------------------------------------------------------------------
1180 void FmSearchEngine::ImplStartNextSearch()
1181 {
1182     m_bCancelAsynchRequest = sal_False;
1183     m_bSearchingCurrently = sal_True;
1184 
1185     if (m_eMode == SM_USETHREAD)//CHINA001 if (m_eMode == FmSearchDialog::SM_USETHREAD)
1186     {
1187         FmSearchThread* pSearcher = new FmSearchThread(this);
1188             // der loescht sich nach Beendigung selber ...
1189         pSearcher->setTerminationHandler(LINK(this, FmSearchEngine, OnSearchTerminated));
1190 
1191         pSearcher->createSuspended();
1192         pSearcher->setPriority(::vos::OThread::TPriority_Lowest);
1193         pSearcher->resume();
1194     }
1195     else
1196     {
1197         SearchNextImpl();
1198         LINK(this, FmSearchEngine, OnSearchTerminated).Call(NULL);
1199     }
1200 }
1201 
1202 //------------------------------------------------------------------------
1203 void FmSearchEngine::SearchNext(const ::rtl::OUString& strExpression)
1204 {
1205     m_strSearchExpression = strExpression;
1206     m_eSearchForType = SEARCHFOR_STRING;
1207     ImplStartNextSearch();
1208 }
1209 
1210 //------------------------------------------------------------------------
1211 void FmSearchEngine::SearchNextSpecial(sal_Bool _bSearchForNull)
1212 {
1213     m_eSearchForType = _bSearchForNull ? SEARCHFOR_NULL : SEARCHFOR_NOTNULL;
1214     ImplStartNextSearch();
1215 }
1216 
1217 //------------------------------------------------------------------------
1218 void FmSearchEngine::StartOver(const ::rtl::OUString& strExpression)
1219 {
1220     try
1221     {
1222         if (m_bForward)
1223             m_xSearchCursor.first();
1224         else
1225             m_xSearchCursor.last();
1226     }
1227     catch( const Exception& )
1228     {
1229         DBG_UNHANDLED_EXCEPTION();
1230         return;
1231     }
1232 
1233     InvalidatePreviousLoc();
1234     SearchNext(strExpression);
1235 }
1236 
1237 //------------------------------------------------------------------------
1238 void FmSearchEngine::StartOverSpecial(sal_Bool _bSearchForNull)
1239 {
1240     try
1241     {
1242         if (m_bForward)
1243             m_xSearchCursor.first();
1244         else
1245             m_xSearchCursor.last();
1246     }
1247     catch( const Exception& )
1248     {
1249         DBG_UNHANDLED_EXCEPTION();
1250         return;
1251     }
1252 
1253     InvalidatePreviousLoc();
1254     SearchNextSpecial(_bSearchForNull);
1255 }
1256 
1257 //------------------------------------------------------------------------
1258 void FmSearchEngine::InvalidatePreviousLoc()
1259 {
1260     m_aPreviousLocBookmark.setValue(0,getVoidCppuType());
1261     m_iterPreviousLocField = m_arrUsedFields.end();
1262 }
1263 
1264 //------------------------------------------------------------------------
1265 void FmSearchEngine::RebuildUsedFields(sal_Int32 nFieldIndex, sal_Bool bForce)
1266 {
1267     if (!bForce && (nFieldIndex == m_nCurrentFieldIndex))
1268         return;
1269     // (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)
1270 
1271     DBG_ASSERT((nFieldIndex == -1) ||
1272                ((nFieldIndex >= 0) &&
1273                 (static_cast<size_t>(nFieldIndex) < m_arrFieldMapping.size())),
1274             "FmSearchEngine::RebuildUsedFields : nFieldIndex is invalid!");
1275     // alle Felder, die ich durchsuchen muss, einsammeln
1276     m_arrUsedFields.clear();
1277     if (nFieldIndex == -1)
1278     {
1279         Reference< ::com::sun::star::container::XIndexAccess >  xFields;
1280         for (size_t i=0; i<m_arrFieldMapping.size(); ++i)
1281         {
1282             Reference< ::com::sun::star::sdbcx::XColumnsSupplier >  xSupplyCols(IFACECAST(m_xSearchCursor), UNO_QUERY);
1283             DBG_ASSERT(xSupplyCols.is(), "FmSearchEngine::RebuildUsedFields : invalid cursor (no columns supplier) !");
1284             xFields = Reference< ::com::sun::star::container::XIndexAccess > (xSupplyCols->getColumns(), UNO_QUERY);
1285             BuildAndInsertFieldInfo(xFields, m_arrFieldMapping[i]);
1286         }
1287     }
1288     else
1289     {
1290         Reference< ::com::sun::star::container::XIndexAccess >  xFields;
1291         Reference< ::com::sun::star::sdbcx::XColumnsSupplier >  xSupplyCols(IFACECAST(m_xSearchCursor), UNO_QUERY);
1292         DBG_ASSERT(xSupplyCols.is(), "FmSearchEngine::RebuildUsedFields : invalid cursor (no columns supplier) !");
1293         xFields = Reference< ::com::sun::star::container::XIndexAccess > (xSupplyCols->getColumns(), UNO_QUERY);
1294         BuildAndInsertFieldInfo(xFields, m_arrFieldMapping[static_cast< size_t >(nFieldIndex)]);
1295     }
1296 
1297     m_nCurrentFieldIndex = nFieldIndex;
1298     // und natuerlich beginne ich die naechste Suche wieder jungfraeulich
1299     InvalidatePreviousLoc();
1300 }
1301 
1302