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