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