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