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