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 //------------------------------------------------------------------------
run()85 void FmSearchThread::run()
86 {
87 m_pEngine->SearchNextImpl();
88 };
89
90 //------------------------------------------------------------------------
onTerminated()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 //------------------------------------------------------------------------
FmRecordCountListener(const Reference<::com::sun::star::sdbc::XResultSet> & dbcCursor)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 //------------------------------------------------------------------------
SetPropChangeHandler(const Link & lnk)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 //------------------------------------------------------------------------
~FmRecordCountListener()136 FmRecordCountListener::~FmRecordCountListener()
137 {
138
139 DBG_DTOR(FmRecordCountListener,NULL);
140 }
141
142 //------------------------------------------------------------------------
DisConnect()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 //------------------------------------------------------------------------
disposing(const::com::sun::star::lang::EventObject &)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 //------------------------------------------------------------------------
NotifyCurrentCount()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 //------------------------------------------------------------------------
propertyChange(const::com::sun::star::beans::PropertyChangeEvent &)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 //------------------------------------------------------------------------
SimpleTextWrapper(const Reference<::com::sun::star::awt::XTextComponent> & _xText)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 //------------------------------------------------------------------------
getCurrentText() const185 ::rtl::OUString SimpleTextWrapper::getCurrentText() const
186 {
187 return m_xText->getText();
188 }
189
190 //------------------------------------------------------------------------
ListBoxWrapper(const Reference<::com::sun::star::awt::XListBox> & _xBox)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 //------------------------------------------------------------------------
getCurrentText() const199 ::rtl::OUString ListBoxWrapper::getCurrentText() const
200 {
201 return m_xBox->getSelectedItem();
202 }
203
204 //------------------------------------------------------------------------
CheckBoxWrapper(const Reference<::com::sun::star::awt::XCheckBox> & _xBox)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 //------------------------------------------------------------------------
getCurrentText() const213 ::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 //------------------------------------------------------------------------
MoveCursor()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 += e.SQLState.getStr();
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 += e.Message.getStr();
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 //------------------------------------------------------------------------
MoveField(sal_Int32 & nPos,FieldCollectionIterator & iter,const FieldCollectionIterator & iterBegin,const FieldCollectionIterator & iterEnd)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 //------------------------------------------------------------------------
BuildAndInsertFieldInfo(const Reference<::com::sun::star::container::XIndexAccess> & xAllFields,sal_Int32 nField)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 //------------------------------------------------------------------------
FormatField(const FieldInfo & rField)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 //------------------------------------------------------------------------
FormatField(sal_Int32 nWhich)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 //------------------------------------------------------------------------
SearchSpecial(sal_Bool _bSearchForNull,sal_Int32 & nFieldPos,FieldCollectionIterator & iterFieldLoop,const FieldCollectionIterator & iterBegin,const FieldCollectionIterator & iterEnd)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 //------------------------------------------------------------------------
SearchWildcard(const::rtl::OUString & strExpression,sal_Int32 & nFieldPos,FieldCollectionIterator & iterFieldLoop,const FieldCollectionIterator & iterBegin,const FieldCollectionIterator & iterEnd)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 //------------------------------------------------------------------------
SearchRegularApprox(const::rtl::OUString & strExpression,sal_Int32 & nFieldPos,FieldCollectionIterator & iterFieldLoop,const FieldCollectionIterator & iterBegin,const FieldCollectionIterator & iterEnd)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 //------------------------------------------------------------------------
FmSearchEngine(const Reference<XMultiServiceFactory> & _rxORB,const Reference<XResultSet> & xCursor,const::rtl::OUString & sVisibleFields,const Reference<XNumberFormatsSupplier> & xFormatSupplier,FMSEARCH_MODE eMode)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 //------------------------------------------------------------------------
FmSearchEngine(const Reference<XMultiServiceFactory> & _rxORB,const Reference<XResultSet> & xCursor,const::rtl::OUString & sVisibleFields,const InterfaceArray & arrFields,FMSEARCH_MODE eMode)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 //------------------------------------------------------------------------
~FmSearchEngine()755 FmSearchEngine::~FmSearchEngine()
756 {
757 clearControlTexts();
758
759 DBG_DTOR(FmSearchEngine,NULL);
760 }
761
762 //------------------------------------------------------------------------
SetIgnoreWidthCJK(sal_Bool bSet)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 //------------------------------------------------------------------------
GetIgnoreWidthCJK() const772 sal_Bool FmSearchEngine::GetIgnoreWidthCJK() const
773 {
774 return 0 != (m_nTransliterationFlags & TransliterationModules_IGNORE_WIDTH);
775 }
776
777 //------------------------------------------------------------------------
SetCaseSensitive(sal_Bool bSet)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 //------------------------------------------------------------------------
GetCaseSensitive() const787 sal_Bool FmSearchEngine::GetCaseSensitive() const
788 {
789 return 0 == (m_nTransliterationFlags & TransliterationModules_IGNORE_CASE);
790 }
791
792 //------------------------------------------------------------------------
clearControlTexts()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 //------------------------------------------------------------------------
fillControlTexts(const InterfaceArray & arrFields)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 //------------------------------------------------------------------------
Init(const::rtl::OUString & sVisibleFields)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 //------------------------------------------------------------------------
SetFormatterUsing(sal_Bool bSet)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 //------------------------------------------------------------------------
PropagateProgress(sal_Bool _bDontPropagateOverflow)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 //------------------------------------------------------------------------
SearchNextImpl()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 //------------------------------------------------------------------------
IMPL_LINK(FmSearchEngine,OnNewRecordCount,void *,pCounterAsVoid)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 //------------------------------------------------------------------------
CancelRequested()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 //------------------------------------------------------------------------
CancelSearch()1147 void FmSearchEngine::CancelSearch()
1148 {
1149 m_aCancelAsynchAccess.acquire();
1150 m_bCancelAsynchRequest = sal_True;
1151 m_aCancelAsynchAccess.release();
1152 }
1153
1154 //------------------------------------------------------------------------
SwitchToContext(const Reference<::com::sun::star::sdbc::XResultSet> & xCursor,const::rtl::OUString & sVisibleFields,const InterfaceArray & arrFields,sal_Int32 nFieldIndex)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 //------------------------------------------------------------------------
ImplStartNextSearch()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 //------------------------------------------------------------------------
SearchNext(const::rtl::OUString & strExpression)1199 void FmSearchEngine::SearchNext(const ::rtl::OUString& strExpression)
1200 {
1201 m_strSearchExpression = strExpression;
1202 m_eSearchForType = SEARCHFOR_STRING;
1203 ImplStartNextSearch();
1204 }
1205
1206 //------------------------------------------------------------------------
SearchNextSpecial(sal_Bool _bSearchForNull)1207 void FmSearchEngine::SearchNextSpecial(sal_Bool _bSearchForNull)
1208 {
1209 m_eSearchForType = _bSearchForNull ? SEARCHFOR_NULL : SEARCHFOR_NOTNULL;
1210 ImplStartNextSearch();
1211 }
1212
1213 //------------------------------------------------------------------------
StartOver(const::rtl::OUString & strExpression)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 //------------------------------------------------------------------------
StartOverSpecial(sal_Bool _bSearchForNull)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 //------------------------------------------------------------------------
InvalidatePreviousLoc()1254 void FmSearchEngine::InvalidatePreviousLoc()
1255 {
1256 m_aPreviousLocBookmark.setValue(0,getVoidCppuType());
1257 m_iterPreviousLocField = m_arrUsedFields.end();
1258 }
1259
1260 //------------------------------------------------------------------------
RebuildUsedFields(sal_Int32 nFieldIndex,sal_Bool bForce)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