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