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