| /************************************************************** |
| * |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| * |
| *************************************************************/ |
| |
| |
| |
| // MARKER(update_precomp.py): autogen include statement, do not remove |
| #include "precompiled_svx.hxx" |
| |
| #ifndef _SVX_FMRESIDS_HRC |
| #include "svx/fmresids.hrc" |
| #endif |
| #include "svx/fmtools.hxx" |
| #include "svx/fmsrccfg.hxx" |
| #include <tools/debug.hxx> |
| #include <tools/diagnose_ex.h> |
| #include <tools/wldcrd.hxx> |
| #include <vcl/msgbox.hxx> |
| #include <tools/shl.hxx> |
| #include <svx/dialmgr.hxx> |
| #include <cppuhelper/servicefactory.hxx> |
| #include <vcl/svapp.hxx> |
| #include <unotools/textsearch.hxx> |
| #include <com/sun/star/util/SearchOptions.hpp> |
| #include <com/sun/star/util/SearchAlgorithms.hpp> |
| #include <com/sun/star/util/SearchResult.hpp> |
| #include <com/sun/star/util/SearchFlags.hpp> |
| #include <com/sun/star/lang/Locale.hpp> |
| #include <com/sun/star/i18n/TransliterationModules.hpp> |
| #include <com/sun/star/i18n/CollatorOptions.hpp> |
| |
| #ifndef _COM_SUN_STAR_SDDB_XCOLUMNSSUPPLIER_HPP_ |
| #include <com/sun/star/sdbcx/XColumnsSupplier.hpp> |
| #endif |
| #include <com/sun/star/util/XNumberFormatter.hpp> |
| #include <com/sun/star/util/NumberFormat.hpp> |
| #include <com/sun/star/util/XNumberFormatsSupplier.hpp> |
| #include <com/sun/star/util/XNumberFormats.hpp> |
| #include <comphelper/processfactory.hxx> |
| |
| #ifndef _SVX_FMPROP_HRC |
| #include "fmprop.hrc" |
| #endif |
| #include "fmservs.hxx" |
| #include "svx/fmsrcimp.hxx" |
| #include <svx/fmsearch.hxx> |
| |
| #include <comphelper/numbers.hxx> |
| #include <unotools/syslocale.hxx> |
| |
| #define EQUAL_BOOKMARKS(a, b) a == b |
| |
| #define IFACECAST(c) ((const Reference< XInterface >&)c) |
| // SUN C52 has some ambiguities without this cast .... |
| |
| using namespace ::com::sun::star::uno; |
| using namespace ::com::sun::star::util; |
| using namespace ::com::sun::star::lang; |
| using namespace ::com::sun::star::sdbc; |
| using namespace ::com::sun::star::i18n; |
| using namespace ::com::sun::star::beans; |
| using namespace ::svxform; |
| |
| |
| //======================================================================== |
| // = FmSearchThread |
| //------------------------------------------------------------------------ |
| void FmSearchThread::run() |
| { |
| m_pEngine->SearchNextImpl(); |
| }; |
| |
| //------------------------------------------------------------------------ |
| void FmSearchThread::onTerminated() |
| { |
| if (m_aTerminationHdl.IsSet()) |
| m_aTerminationHdl.Call(this); |
| delete this; |
| } |
| |
| //======================================================================== |
| // = FmRecordCountListener |
| |
| // SMART_UNO_IMPLEMENTATION(FmRecordCountListener, UsrObject); |
| |
| DBG_NAME(FmRecordCountListener); |
| //------------------------------------------------------------------------ |
| FmRecordCountListener::FmRecordCountListener(const Reference< ::com::sun::star::sdbc::XResultSet > & dbcCursor) |
| { |
| DBG_CTOR(FmRecordCountListener,NULL); |
| |
| m_xListening = Reference< ::com::sun::star::beans::XPropertySet > (dbcCursor, UNO_QUERY); |
| if (!m_xListening.is()) |
| return; |
| |
| if (::comphelper::getBOOL(m_xListening->getPropertyValue(FM_PROP_ROWCOUNTFINAL))) |
| { |
| m_xListening = NULL; |
| // there's nothing to do as the record count is already known |
| return; |
| } |
| |
| m_xListening->addPropertyChangeListener(FM_PROP_ROWCOUNT, (::com::sun::star::beans::XPropertyChangeListener*)this); |
| } |
| |
| //------------------------------------------------------------------------ |
| Link FmRecordCountListener::SetPropChangeHandler(const Link& lnk) |
| { |
| Link lnkReturn = m_lnkWhoWantsToKnow; |
| m_lnkWhoWantsToKnow = lnk; |
| |
| if (m_xListening.is()) |
| NotifyCurrentCount(); |
| |
| return lnkReturn; |
| } |
| |
| //------------------------------------------------------------------------ |
| FmRecordCountListener::~FmRecordCountListener() |
| { |
| |
| DBG_DTOR(FmRecordCountListener,NULL); |
| } |
| |
| //------------------------------------------------------------------------ |
| void FmRecordCountListener::DisConnect() |
| { |
| if(m_xListening.is()) |
| m_xListening->removePropertyChangeListener(FM_PROP_ROWCOUNT, (::com::sun::star::beans::XPropertyChangeListener*)this); |
| m_xListening = NULL; |
| } |
| |
| //------------------------------------------------------------------------ |
| void SAL_CALL FmRecordCountListener::disposing(const ::com::sun::star::lang::EventObject& /*Source*/) throw( RuntimeException ) |
| { |
| DBG_ASSERT(m_xListening.is(), "FmRecordCountListener::disposing should never have been called without a propset !"); |
| DisConnect(); |
| } |
| |
| //------------------------------------------------------------------------ |
| void FmRecordCountListener::NotifyCurrentCount() |
| { |
| if (m_lnkWhoWantsToKnow.IsSet()) |
| { |
| DBG_ASSERT(m_xListening.is(), "FmRecordCountListener::NotifyCurrentCount : I have no propset ... !?"); |
| void* pTheCount = (void*)::comphelper::getINT32(m_xListening->getPropertyValue(FM_PROP_ROWCOUNT)); |
| m_lnkWhoWantsToKnow.Call(pTheCount); |
| } |
| } |
| |
| //------------------------------------------------------------------------ |
| void FmRecordCountListener::propertyChange(const ::com::sun::star::beans::PropertyChangeEvent& /*evt*/) throw(::com::sun::star::uno::RuntimeException) |
| { |
| NotifyCurrentCount(); |
| } |
| |
| //======================================================================== |
| // FmSearchEngine - local classes |
| //------------------------------------------------------------------------ |
| SimpleTextWrapper::SimpleTextWrapper(const Reference< ::com::sun::star::awt::XTextComponent > & _xText) |
| :ControlTextWrapper(_xText.get()) |
| ,m_xText(_xText) |
| { |
| DBG_ASSERT(m_xText.is(), "FmSearchEngine::SimpleTextWrapper::SimpleTextWrapper : invalid argument !"); |
| } |
| |
| //------------------------------------------------------------------------ |
| ::rtl::OUString SimpleTextWrapper::getCurrentText() const |
| { |
| return m_xText->getText(); |
| } |
| |
| //------------------------------------------------------------------------ |
| ListBoxWrapper::ListBoxWrapper(const Reference< ::com::sun::star::awt::XListBox > & _xBox) |
| :ControlTextWrapper(_xBox.get()) |
| ,m_xBox(_xBox) |
| { |
| DBG_ASSERT(m_xBox.is(), "FmSearchEngine::ListBoxWrapper::ListBoxWrapper : invalid argument !"); |
| } |
| |
| //------------------------------------------------------------------------ |
| ::rtl::OUString ListBoxWrapper::getCurrentText() const |
| { |
| return m_xBox->getSelectedItem(); |
| } |
| |
| //------------------------------------------------------------------------ |
| CheckBoxWrapper::CheckBoxWrapper(const Reference< ::com::sun::star::awt::XCheckBox > & _xBox) |
| :ControlTextWrapper(_xBox.get()) |
| ,m_xBox(_xBox) |
| { |
| DBG_ASSERT(m_xBox.is(), "FmSearchEngine::CheckBoxWrapper::CheckBoxWrapper : invalid argument !"); |
| } |
| |
| //------------------------------------------------------------------------ |
| ::rtl::OUString CheckBoxWrapper::getCurrentText() const |
| { |
| switch ((TriState)m_xBox->getState()) |
| { |
| case STATE_NOCHECK: return rtl::OUString::createFromAscii("0"); |
| case STATE_CHECK: return rtl::OUString::createFromAscii("1"); |
| default: break; |
| } |
| return rtl::OUString(); |
| } |
| |
| //======================================================================== |
| // = FmSearchEngine |
| //------------------------------------------------------------------------ |
| sal_Bool FmSearchEngine::MoveCursor() |
| { |
| sal_Bool bSuccess = sal_True; |
| try |
| { |
| if (m_bForward) |
| if (m_xSearchCursor.isLast()) |
| m_xSearchCursor.first(); |
| else |
| m_xSearchCursor.next(); |
| else |
| if (m_xSearchCursor.isFirst()) |
| { |
| FmRecordCountListener* prclListener = new FmRecordCountListener(m_xSearchCursor); |
| prclListener->acquire(); |
| prclListener->SetPropChangeHandler(LINK(this, FmSearchEngine, OnNewRecordCount)); |
| |
| m_xSearchCursor.last(); |
| |
| prclListener->DisConnect(); |
| prclListener->release(); |
| } |
| else |
| m_xSearchCursor.previous(); |
| } |
| catch(::com::sun::star::sdbc::SQLException e) |
| { |
| #if OSL_DEBUG_LEVEL > 0 |
| String sDebugMessage; |
| sDebugMessage.AssignAscii("FmSearchEngine::MoveCursor : catched a DatabaseException ("); |
| sDebugMessage += e.SQLState.getStr(); |
| sDebugMessage.AppendAscii(") !"); |
| DBG_ERROR(ByteString(sDebugMessage, RTL_TEXTENCODING_ASCII_US).GetBuffer()); |
| #endif |
| bSuccess = sal_False; |
| } |
| catch(Exception e) |
| { |
| #if OSL_DEBUG_LEVEL > 0 |
| UniString sDebugMessage; |
| sDebugMessage.AssignAscii("FmSearchEngine::MoveCursor : catched an Exception ("); |
| sDebugMessage += e.Message.getStr(); |
| sDebugMessage.AppendAscii(") !"); |
| DBG_ERROR(ByteString(sDebugMessage, RTL_TEXTENCODING_ASCII_US).GetBuffer()); |
| #endif |
| bSuccess = sal_False; |
| } |
| catch(...) |
| { |
| DBG_ERROR("FmSearchEngine::MoveCursor : catched an unknown Exception !"); |
| bSuccess = sal_False; |
| } |
| |
| return bSuccess; |
| } |
| |
| //------------------------------------------------------------------------ |
| sal_Bool FmSearchEngine::MoveField(sal_Int32& nPos, FieldCollectionIterator& iter, const FieldCollectionIterator& iterBegin, const FieldCollectionIterator& iterEnd) |
| { |
| sal_Bool bSuccess(sal_True); |
| if (m_bForward) |
| { |
| ++iter; |
| ++nPos; |
| if (iter == iterEnd) |
| { |
| bSuccess = MoveCursor(); |
| iter = iterBegin; |
| nPos = 0; |
| } |
| } else |
| { |
| if (iter == iterBegin) |
| { |
| bSuccess = MoveCursor(); |
| iter = iterEnd; |
| nPos = iter-iterBegin; |
| } |
| --iter; |
| --nPos; |
| } |
| return bSuccess; |
| } |
| |
| //------------------------------------------------------------------------ |
| void FmSearchEngine::BuildAndInsertFieldInfo(const Reference< ::com::sun::star::container::XIndexAccess > & xAllFields, sal_Int32 nField) |
| { |
| DBG_ASSERT( xAllFields.is() && ( nField >= 0 ) && ( nField < xAllFields->getCount() ), |
| "FmSearchEngine::BuildAndInsertFieldInfo: invalid field descriptor!" ); |
| |
| // das Feld selber |
| Reference< XInterface > xCurrentField; |
| xAllFields->getByIndex(nField) >>= xCurrentField; |
| |
| // von dem weiss ich jetzt, dass es den DatabaseRecord-Service unterstuetzt (hoffe ich) |
| // fuer den FormatKey und den Typ brauche ich das PropertySet |
| Reference< ::com::sun::star::beans::XPropertySet > xProperties(xCurrentField, UNO_QUERY); |
| |
| // die FieldInfo dazu aufbauen |
| FieldInfo fiCurrent; |
| fiCurrent.xContents = Reference< ::com::sun::star::sdb::XColumn > (xCurrentField, UNO_QUERY); |
| fiCurrent.nFormatKey = ::comphelper::getINT32(xProperties->getPropertyValue(FM_PROP_FORMATKEY)); |
| fiCurrent.bDoubleHandling = sal_False; |
| if (m_xFormatSupplier.is()) |
| { |
| Reference< ::com::sun::star::util::XNumberFormats > xNumberFormats(m_xFormatSupplier->getNumberFormats()); |
| |
| sal_Int16 nFormatType = ::comphelper::getNumberFormatType(xNumberFormats, fiCurrent.nFormatKey) & ~((sal_Int16)::com::sun::star::util::NumberFormat::DEFINED); |
| fiCurrent.bDoubleHandling = (nFormatType != ::com::sun::star::util::NumberFormat::TEXT); |
| } |
| |
| // und merken |
| m_arrUsedFields.insert(m_arrUsedFields.end(), fiCurrent); |
| |
| } |
| //------------------------------------------------------------------------ |
| ::rtl::OUString FmSearchEngine::FormatField(const FieldInfo& rField) |
| { |
| DBG_ASSERT(!m_bUsingTextComponents, "FmSearchEngine::FormatField : im UsingTextComponents-Mode bitte FormatField(sal_Int32) benutzen !"); |
| |
| if (!m_xFormatter.is()) |
| return ::rtl::OUString(); |
| // sonst werden Datumsflder zum Beispiel zu irgendeinem Default-Wert formatiert |
| |
| ::rtl::OUString sReturn; |
| try |
| { |
| if (rField.bDoubleHandling) |
| { |
| double fValue = rField.xContents->getDouble(); |
| if (!rField.xContents->wasNull()) |
| sReturn = m_xFormatter->convertNumberToString(rField.nFormatKey, fValue); |
| } |
| else |
| { |
| ::rtl::OUString sValue = rField.xContents->getString(); |
| if (!rField.xContents->wasNull()) |
| sReturn = m_xFormatter->formatString(rField.nFormatKey, sValue); |
| } |
| } |
| catch(...) |
| { |
| } |
| |
| |
| return sReturn; |
| } |
| |
| //------------------------------------------------------------------------ |
| ::rtl::OUString FmSearchEngine::FormatField(sal_Int32 nWhich) |
| { |
| if (m_bUsingTextComponents) |
| { |
| DBG_ASSERT((sal_uInt32)nWhich < m_aControlTexts.size(), "FmSearchEngine::FormatField(sal_Int32) : invalid position !"); |
| DBG_ASSERT(m_aControlTexts[nWhich] != NULL, "FmSearchEngine::FormatField(sal_Int32) : invalid object in array !"); |
| DBG_ASSERT(m_aControlTexts[nWhich]->getControl().is(), "FmSearchEngine::FormatField : invalid control !"); |
| |
| if (m_nCurrentFieldIndex != -1) |
| { |
| DBG_ASSERT((nWhich == 0) || (nWhich == m_nCurrentFieldIndex), "FmSearchEngine::FormatField : Parameter nWhich ist ungueltig"); |
| // analoge Situation wie unten |
| nWhich = m_nCurrentFieldIndex; |
| } |
| |
| DBG_ASSERT((nWhich >= 0) && ((sal_uInt32)nWhich < m_aControlTexts.size()), |
| "FmSearchEngine::FormatField : invalid argument nWhich !"); |
| return m_aControlTexts[m_nCurrentFieldIndex == -1 ? nWhich : m_nCurrentFieldIndex]->getCurrentText(); |
| } |
| else |
| { |
| if (m_nCurrentFieldIndex != -1) |
| { |
| DBG_ASSERT((nWhich == 0) || (nWhich == m_nCurrentFieldIndex), "FmSearchEngine::FormatField : Parameter nWhich ist ungueltig"); |
| // ich bin im single-field-modus, da ist auch die richtige Feld-Nummer erlaubt, obwohl dann der richtige ::com::sun::star::sdbcx::Index |
| // fuer meinen Array-Zugriff natuerlich 0 ist |
| nWhich = 0; |
| } |
| |
| DBG_ASSERT((nWhich>=0) && (nWhich < (m_arrUsedFields.end() - m_arrUsedFields.begin())), |
| "FmSearchEngine::FormatField : Parameter nWhich ist ungueltig"); |
| return FormatField(m_arrUsedFields[nWhich]); |
| } |
| } |
| |
| //------------------------------------------------------------------------ |
| FmSearchEngine::SEARCH_RESULT FmSearchEngine::SearchSpecial(sal_Bool _bSearchForNull, sal_Int32& nFieldPos, |
| FieldCollectionIterator& iterFieldLoop, const FieldCollectionIterator& iterBegin, const FieldCollectionIterator& iterEnd) |
| { |
| // die Startposition merken |
| Any aStartMark; |
| try { aStartMark = m_xSearchCursor.getBookmark(); } |
| catch ( const Exception& ) { DBG_UNHANDLED_EXCEPTION(); return SR_ERROR; } |
| FieldCollectionIterator iterInitialField = iterFieldLoop; |
| |
| // -------------------------------------------------------------- |
| sal_Bool bFound(sal_False); |
| sal_Bool bMovedAround(sal_False); |
| do |
| { |
| if (m_eMode == SM_ALLOWSCHEDULE) //CHINA001 if (m_eMode == FmSearchDialog::SM_ALLOWSCHEDULE) |
| { |
| Application::Reschedule(); |
| Application::Reschedule(); |
| // do 2 reschedules because of #70226# : some things done within this loop's body may cause an user event |
| // to be posted (deep within vcl), and these user events will be handled before any keyinput or paintings |
| // or anything like that. So within each loop we create one user event and handle one user event (and no |
| // paintings and these), so the office seems to be frozen while searching. |
| // FS - 70226 - 02.12.99 |
| } |
| |
| // der aktuell zu vergleichende Inhalt |
| iterFieldLoop->xContents->getString(); // needed for wasNull |
| bFound = _bSearchForNull == iterFieldLoop->xContents->wasNull(); |
| if (bFound) |
| break; |
| |
| // naechstes Feld (implizit naechster Datensatz, wenn noetig) |
| if (!MoveField(nFieldPos, iterFieldLoop, iterBegin, iterEnd)) |
| { // beim Bewegen auf das naechste Feld ging was schief ... weitermachen ist nicht drin, da das naechste Mal genau |
| // das selbe bestimmt wieder schief geht, also Abbruch |
| // vorher aber noch, damit das Weitersuchen an der aktuellen Position weitermacht : |
| try { m_aPreviousLocBookmark = m_xSearchCursor.getBookmark(); } |
| catch ( const Exception& ) { DBG_UNHANDLED_EXCEPTION(); } |
| m_iterPreviousLocField = iterFieldLoop; |
| // und wech |
| return SR_ERROR; |
| } |
| |
| Any aCurrentBookmark; |
| try { aCurrentBookmark = m_xSearchCursor.getBookmark(); } |
| catch ( const Exception& ) { DBG_UNHANDLED_EXCEPTION(); return SR_ERROR; } |
| |
| bMovedAround = EQUAL_BOOKMARKS(aStartMark, aCurrentBookmark) && (iterFieldLoop == iterInitialField); |
| |
| if (nFieldPos == 0) |
| // das heisst, ich habe mich auf einen neuen Datensatz bewegt |
| PropagateProgress(bMovedAround); |
| // if we moved to the starting position we don't have to propagate an 'overflow' message |
| // FS - 07.12.99 - 68530 |
| |
| // abbrechen gefordert ? |
| if (CancelRequested()) |
| return SR_CANCELED; |
| |
| } while (!bMovedAround); |
| |
| return bFound ? SR_FOUND : SR_NOTFOUND; |
| } |
| |
| //------------------------------------------------------------------------ |
| FmSearchEngine::SEARCH_RESULT FmSearchEngine::SearchWildcard(const ::rtl::OUString& strExpression, sal_Int32& nFieldPos, |
| FieldCollectionIterator& iterFieldLoop, const FieldCollectionIterator& iterBegin, const FieldCollectionIterator& iterEnd) |
| { |
| // die Startposition merken |
| Any aStartMark; |
| try { aStartMark = m_xSearchCursor.getBookmark(); } |
| catch ( const Exception& ) { DBG_UNHANDLED_EXCEPTION(); return SR_ERROR; } |
| FieldCollectionIterator iterInitialField = iterFieldLoop; |
| |
| WildCard aSearchExpression(strExpression); |
| |
| // -------------------------------------------------------------- |
| sal_Bool bFound(sal_False); |
| sal_Bool bMovedAround(sal_False); |
| do |
| { |
| if (m_eMode == SM_ALLOWSCHEDULE) //CHINA001 if (m_eMode == FmSearchDialog::SM_ALLOWSCHEDULE) |
| { |
| Application::Reschedule(); |
| Application::Reschedule(); |
| // do 2 reschedules because of #70226# : some things done within this loop's body may cause an user event |
| // to be posted (deep within vcl), and these user events will be handled before any keyinput or paintings |
| // or anything like that. So within each loop we create one user event and hanel one user event (and no |
| // paintings and these), so the office seems to be frozen while searching. |
| // FS - 70226 - 02.12.99 |
| } |
| |
| // der aktuell zu vergleichende Inhalt |
| ::rtl::OUString sCurrentCheck; |
| if (m_bFormatter) |
| sCurrentCheck = FormatField(nFieldPos); |
| else |
| sCurrentCheck = iterFieldLoop->xContents->getString(); |
| |
| if (!GetCaseSensitive()) |
| // norm the string |
| m_aCharacterClassficator.toLower_rtl(sCurrentCheck); |
| |
| // jetzt ist der Test einfach ... |
| bFound = aSearchExpression.Matches(sCurrentCheck); |
| |
| if (bFound) |
| break; |
| |
| // naechstes Feld (implizit naechster Datensatz, wenn noetig) |
| if (!MoveField(nFieldPos, iterFieldLoop, iterBegin, iterEnd)) |
| { // beim Bewegen auf das naechste Feld ging was schief ... weitermachen ist nicht drin, da das naechste Mal genau |
| // das selbe bestimmt wieder schief geht, also Abbruch |
| // vorher aber noch, damit das Weitersuchen an der aktuellen Position weitermacht : |
| try { m_aPreviousLocBookmark = m_xSearchCursor.getBookmark(); } |
| catch ( const Exception& ) { DBG_UNHANDLED_EXCEPTION(); } |
| m_iterPreviousLocField = iterFieldLoop; |
| // und wech |
| return SR_ERROR; |
| } |
| |
| Any aCurrentBookmark; |
| try { aCurrentBookmark = m_xSearchCursor.getBookmark(); } |
| catch ( const Exception& ) { DBG_UNHANDLED_EXCEPTION(); return SR_ERROR; } |
| |
| bMovedAround = EQUAL_BOOKMARKS(aStartMark, aCurrentBookmark) && (iterFieldLoop == iterInitialField); |
| |
| if (nFieldPos == 0) |
| // das heisst, ich habe mich auf einen neuen Datensatz bewegt |
| PropagateProgress(bMovedAround); |
| // if we moved to the starting position we don't have to propagate an 'overflow' message |
| // FS - 07.12.99 - 68530 |
| |
| // abbrechen gefordert ? |
| if (CancelRequested()) |
| return SR_CANCELED; |
| |
| } while (!bMovedAround); |
| |
| return bFound ? SR_FOUND : SR_NOTFOUND; |
| } |
| |
| //------------------------------------------------------------------------ |
| FmSearchEngine::SEARCH_RESULT FmSearchEngine::SearchRegularApprox(const ::rtl::OUString& strExpression, sal_Int32& nFieldPos, |
| FieldCollectionIterator& iterFieldLoop, const FieldCollectionIterator& iterBegin, const FieldCollectionIterator& iterEnd) |
| { |
| DBG_ASSERT(m_bLevenshtein || m_bRegular, |
| "FmSearchEngine::SearchRegularApprox : ungueltiger Suchmodus !"); |
| DBG_ASSERT(!m_bLevenshtein || !m_bRegular, |
| "FmSearchEngine::SearchRegularApprox : kann nicht nach regulaeren Ausdruecken und nach Aehnlichkeiten gleichzeitig suchen !"); |
| |
| // Startposition merken |
| Any aStartMark; |
| try { aStartMark = m_xSearchCursor.getBookmark(); } |
| catch ( const Exception& ) { DBG_UNHANDLED_EXCEPTION(); return SR_ERROR; } |
| FieldCollectionIterator iterInitialField = iterFieldLoop; |
| |
| // Parameter sammeln |
| SearchOptions aParam; |
| aParam.algorithmType = m_bRegular ? SearchAlgorithms_REGEXP : SearchAlgorithms_APPROXIMATE; |
| aParam.searchFlag = 0; |
| aParam.transliterateFlags = GetTransliterationFlags(); |
| if ( !GetTransliteration() ) |
| { // if transliteration is not enabled, the only flags which matter are IGNORE_CASE and IGNORE_WIDTH |
| aParam.transliterateFlags &= TransliterationModules_IGNORE_CASE | TransliterationModules_IGNORE_WIDTH; |
| } |
| if (m_bLevenshtein) |
| { |
| if (m_bLevRelaxed) |
| aParam.searchFlag |= SearchFlags::LEV_RELAXED; |
| aParam.changedChars = m_nLevOther; |
| aParam.deletedChars = m_nLevShorter; |
| aParam.insertedChars = m_nLevLonger; |
| } |
| aParam.searchString = strExpression; |
| aParam.Locale = SvtSysLocale().GetLocaleData().getLocale(); |
| ::utl::TextSearch aLocalEngine(aParam); |
| |
| // -------------------------------------------------------------- |
| bool bFound = false; |
| sal_Bool bMovedAround(sal_False); |
| do |
| { |
| if (m_eMode == SM_ALLOWSCHEDULE) //CHINA001 if (m_eMode == FmSearchDialog::SM_ALLOWSCHEDULE) |
| { |
| Application::Reschedule(); |
| Application::Reschedule(); |
| // do 2 reschedules because of #70226# : some things done within this loop's body may cause an user event |
| // to be posted (deep within vcl), and these user events will be handled before any keyinput or paintings |
| // or anything like that. So within each loop we create one user event and handle one user event (and no |
| // paintings and these), so the office seems to be frozen while searching. |
| // FS - 70226 - 02.12.99 |
| } |
| |
| // der aktuell zu vergleichende Inhalt |
| ::rtl::OUString sCurrentCheck; |
| if (m_bFormatter) |
| sCurrentCheck = FormatField(nFieldPos); |
| else |
| sCurrentCheck = iterFieldLoop->xContents->getString(); |
| |
| // (don't care about case here, this is done by the TextSearch object, 'cause we passed our case parameter to it) |
| |
| xub_StrLen nStart = 0, nEnd = (xub_StrLen)sCurrentCheck.getLength(); |
| bFound = aLocalEngine.SearchFrwrd(sCurrentCheck, &nStart, &nEnd); |
| // das heisst hier 'forward' aber das bezieht sich nur auf die Suche innerhalb von sCurrentCheck, hat also mit |
| // der Richtung meines Datensatz-Durchwanderns nix zu tun (darum kuemmert sich MoveField) |
| |
| // checken, ob die Position stimmt |
| if (bFound) |
| { |
| switch (m_nPosition) |
| { |
| case MATCHING_WHOLETEXT : |
| if (nEnd != sCurrentCheck.getLength()) |
| { |
| bFound = false; |
| break; |
| } |
| // laeuft in den naechsten Case rein ! |
| case MATCHING_BEGINNING : |
| if (nStart != 0) |
| bFound = false; |
| break; |
| case MATCHING_END : |
| if (nEnd != sCurrentCheck.getLength()) |
| bFound = false; |
| break; |
| } |
| } |
| |
| if (bFound) // immer noch ? |
| break; |
| |
| // naechstes Feld (implizit naechster Datensatz, wenn noetig) |
| if (!MoveField(nFieldPos, iterFieldLoop, iterBegin, iterEnd)) |
| { // beim Bewegen auf das naechste Feld ging was schief ... weitermachen ist nicht drin, da das naechste Mal genau |
| // das selbe bestimmt wieder schief geht, also Abbruch (ohne Fehlermeldung, von der erwarte ich, dass sie im Move |
| // angezeigt wurde) |
| // vorher aber noch, damit das Weitersuchen an der aktuellen Position weitermacht : |
| try { m_aPreviousLocBookmark = m_xSearchCursor.getBookmark(); } |
| catch ( const Exception& ) { DBG_UNHANDLED_EXCEPTION(); } |
| m_iterPreviousLocField = iterFieldLoop; |
| // und wech |
| return SR_ERROR; |
| } |
| |
| Any aCurrentBookmark; |
| try { aCurrentBookmark = m_xSearchCursor.getBookmark(); } |
| catch ( const Exception& ) { DBG_UNHANDLED_EXCEPTION(); return SR_ERROR; } |
| bMovedAround = EQUAL_BOOKMARKS(aStartMark, aCurrentBookmark) && (iterFieldLoop == iterInitialField); |
| |
| if (nFieldPos == 0) |
| // das heisst, ich habe mich auf einen neuen Datensatz bewegt |
| PropagateProgress(bMovedAround); |
| // if we moved to the starting position we don't have to propagate an 'overflow' message |
| // FS - 07.12.99 - 68530 |
| |
| // abbrechen gefordert ? |
| if (CancelRequested()) |
| return SR_CANCELED; |
| |
| } while (!bMovedAround); |
| |
| return bFound ? SR_FOUND : SR_NOTFOUND; |
| } |
| |
| |
| DBG_NAME(FmSearchEngine); |
| //------------------------------------------------------------------------ |
| FmSearchEngine::FmSearchEngine(const Reference< XMultiServiceFactory >& _rxORB, |
| const Reference< XResultSet > & xCursor, const ::rtl::OUString& sVisibleFields, |
| const Reference< XNumberFormatsSupplier > & xFormatSupplier, FMSEARCH_MODE eMode)//CHINA001 const Reference< XNumberFormatsSupplier > & xFormatSupplier, FmSearchDialog::SEARCH_MODE eMode) |
| :m_xSearchCursor(xCursor) |
| ,m_xFormatSupplier(xFormatSupplier) |
| ,m_aCharacterClassficator( _rxORB, SvtSysLocale().GetLocaleData().getLocale() ) |
| ,m_aStringCompare( _rxORB ) |
| ,m_nCurrentFieldIndex(-2) // -1 hat schon eine Bedeutung, also nehme ich -2 fuer 'ungueltig' |
| ,m_bUsingTextComponents(sal_False) |
| ,m_eSearchForType(SEARCHFOR_STRING) |
| ,m_srResult(SR_FOUND) |
| ,m_bSearchingCurrently(sal_False) |
| ,m_bCancelAsynchRequest(sal_False) |
| ,m_eMode(eMode) |
| ,m_bFormatter(sal_False) |
| ,m_bForward(sal_False) |
| ,m_bWildcard(sal_False) |
| ,m_bRegular(sal_False) |
| ,m_bLevenshtein(sal_False) |
| ,m_bTransliteration(sal_False) |
| ,m_bLevRelaxed(sal_False) |
| ,m_nLevOther(0) |
| ,m_nLevShorter(0) |
| ,m_nLevLonger(0) |
| ,m_nPosition(MATCHING_ANYWHERE) |
| ,m_nTransliterationFlags(0) |
| { |
| DBG_CTOR(FmSearchEngine,NULL); |
| |
| m_xFormatter = Reference< ::com::sun::star::util::XNumberFormatter > (::comphelper::getProcessServiceFactory() |
| ->createInstance(FM_NUMBER_FORMATTER), UNO_QUERY); |
| if (m_xFormatter.is()) |
| m_xFormatter->attachNumberFormatsSupplier(m_xFormatSupplier); |
| |
| Init(sVisibleFields); |
| } |
| |
| //------------------------------------------------------------------------ |
| FmSearchEngine::FmSearchEngine(const Reference< XMultiServiceFactory >& _rxORB, |
| const Reference< XResultSet > & xCursor, const ::rtl::OUString& sVisibleFields, |
| const InterfaceArray& arrFields, FMSEARCH_MODE eMode)//CHINA001 const InterfaceArray& arrFields, FmSearchDialog::SEARCH_MODE eMode) |
| :m_xSearchCursor(xCursor) |
| ,m_aCharacterClassficator( _rxORB, SvtSysLocale().GetLocaleData().getLocale() ) |
| ,m_aStringCompare( _rxORB ) |
| ,m_nCurrentFieldIndex(-2) // -1 hat schon eine Bedeutung, also nehme ich -2 fuer 'ungueltig' |
| ,m_bUsingTextComponents(sal_True) |
| ,m_xOriginalIterator(xCursor) |
| ,m_xClonedIterator(m_xOriginalIterator, sal_True) |
| ,m_eSearchForType(SEARCHFOR_STRING) |
| ,m_srResult(SR_FOUND) |
| ,m_bSearchingCurrently(sal_False) |
| ,m_bCancelAsynchRequest(sal_False) |
| ,m_eMode(eMode) |
| ,m_bFormatter(sal_True) // das muss konsistent sein mit m_xSearchCursor, der i.A. == m_xOriginalIterator ist |
| ,m_bForward(sal_False) |
| ,m_bWildcard(sal_False) |
| ,m_bRegular(sal_False) |
| ,m_bLevenshtein(sal_False) |
| ,m_bTransliteration(sal_False) |
| ,m_bLevRelaxed(sal_False) |
| ,m_nLevOther(0) |
| ,m_nLevShorter(0) |
| ,m_nLevLonger(0) |
| ,m_nPosition(MATCHING_ANYWHERE) |
| ,m_nTransliterationFlags(0) |
| { |
| DBG_CTOR(FmSearchEngine,NULL); |
| |
| fillControlTexts(arrFields); |
| Init(sVisibleFields); |
| } |
| |
| //------------------------------------------------------------------------ |
| FmSearchEngine::~FmSearchEngine() |
| { |
| clearControlTexts(); |
| |
| DBG_DTOR(FmSearchEngine,NULL); |
| } |
| |
| //------------------------------------------------------------------------ |
| void FmSearchEngine::SetIgnoreWidthCJK(sal_Bool bSet) |
| { |
| if (bSet) |
| m_nTransliterationFlags |= TransliterationModules_IGNORE_WIDTH; |
| else |
| m_nTransliterationFlags &= ~TransliterationModules_IGNORE_WIDTH; |
| } |
| |
| //------------------------------------------------------------------------ |
| sal_Bool FmSearchEngine::GetIgnoreWidthCJK() const |
| { |
| return 0 != (m_nTransliterationFlags & TransliterationModules_IGNORE_WIDTH); |
| } |
| |
| //------------------------------------------------------------------------ |
| void FmSearchEngine::SetCaseSensitive(sal_Bool bSet) |
| { |
| if (bSet) |
| m_nTransliterationFlags &= ~TransliterationModules_IGNORE_CASE; |
| else |
| m_nTransliterationFlags |= TransliterationModules_IGNORE_CASE; |
| } |
| |
| //------------------------------------------------------------------------ |
| sal_Bool FmSearchEngine::GetCaseSensitive() const |
| { |
| return 0 == (m_nTransliterationFlags & TransliterationModules_IGNORE_CASE); |
| } |
| |
| //------------------------------------------------------------------------ |
| void FmSearchEngine::clearControlTexts() |
| { |
| for ( ControlTextSuppliersIterator aIter = m_aControlTexts.begin(); |
| aIter < m_aControlTexts.end(); |
| ++aIter |
| ) |
| { |
| delete *aIter; |
| } |
| m_aControlTexts.clear(); |
| } |
| |
| //------------------------------------------------------------------------ |
| void FmSearchEngine::fillControlTexts(const InterfaceArray& arrFields) |
| { |
| clearControlTexts(); |
| Reference< XInterface > xCurrent; |
| for (sal_uInt32 i=0; i<arrFields.size(); ++i) |
| { |
| xCurrent = arrFields.at(i); |
| DBG_ASSERT(xCurrent.is(), "FmSearchEngine::fillControlTexts : invalid field interface !"); |
| // check which type of control this is |
| Reference< ::com::sun::star::awt::XTextComponent > xAsText(xCurrent, UNO_QUERY); |
| if (xAsText.is()) |
| { |
| m_aControlTexts.insert(m_aControlTexts.end(), new SimpleTextWrapper(xAsText)); |
| continue; |
| } |
| |
| Reference< ::com::sun::star::awt::XListBox > xAsListBox(xCurrent, UNO_QUERY); |
| if (xAsListBox.is()) |
| { |
| m_aControlTexts.insert(m_aControlTexts.end(), new ListBoxWrapper(xAsListBox)); |
| continue; |
| } |
| |
| Reference< ::com::sun::star::awt::XCheckBox > xAsCheckBox(xCurrent, UNO_QUERY); |
| DBG_ASSERT(xAsCheckBox.is(), "FmSearchEngine::fillControlTexts : invalid field interface (no supported type) !"); |
| // we don't have any more options ... |
| m_aControlTexts.insert(m_aControlTexts.end(), new CheckBoxWrapper(xAsCheckBox)); |
| } |
| } |
| |
| //------------------------------------------------------------------------ |
| void FmSearchEngine::Init(const ::rtl::OUString& sVisibleFields) |
| { |
| // analyze the fields |
| // additionally, create the mapping: because the list of used columns can be shorter than the list |
| // of columns of the cursor, we need a mapping: "used column numer n" -> "cursor column m" |
| m_arrFieldMapping.clear(); |
| |
| // important: The case of the columns does not need to be exact - for instance: |
| // - a user created a form which works on a table, for which the driver returns a column name "COLUMN" |
| // - the driver itself works case-insensitve with column names |
| // - a control in the form is bound to "column" - not the different case |
| // In such a scenario, the form and the field would work okay, but we here need to case for the different case |
| // explicitly |
| // 2003-01-09 - #i8755# - fs@openoffice.org |
| |
| // so first of all, check if the database handles identifiers case sensitive |
| Reference< XConnection > xConn; |
| Reference< XDatabaseMetaData > xMeta; |
| Reference< XPropertySet > xCursorProps( IFACECAST( m_xSearchCursor ), UNO_QUERY ); |
| if ( xCursorProps.is() ) |
| { |
| try |
| { |
| xCursorProps->getPropertyValue( FM_PROP_ACTIVE_CONNECTION ) >>= xConn; |
| } |
| catch( Exception& ) { /* silent this - will be asserted below */ } |
| } |
| if ( xConn.is() ) |
| xMeta = xConn->getMetaData(); |
| OSL_ENSURE( xMeta.is(), "FmSearchEngine::Init: very strange cursor (could not derive connection meta data from it)!" ); |
| |
| sal_Bool bCaseSensitiveIdentifiers = sal_True; // assume case sensivity |
| if ( xMeta.is() ) |
| bCaseSensitiveIdentifiers = xMeta->supportsMixedCaseQuotedIdentifiers(); |
| |
| // now that we have this information, we need a collator which is able to case (in)sentively compare strings |
| m_aStringCompare.loadDefaultCollator( SvtSysLocale().GetLocaleData().getLocale(), |
| bCaseSensitiveIdentifiers ? 0 : ::com::sun::star::i18n::CollatorOptions::CollatorOptions_IGNORE_CASE ); |
| |
| try |
| { |
| // der Cursor kann mir einen Record (als PropertySet) liefern, dieser unterstuetzt den DatabaseRecord-Service |
| Reference< ::com::sun::star::sdbcx::XColumnsSupplier > xSupplyCols(IFACECAST(m_xSearchCursor), UNO_QUERY); |
| DBG_ASSERT(xSupplyCols.is(), "FmSearchEngine::Init : invalid cursor (no columns supplier) !"); |
| Reference< ::com::sun::star::container::XNameAccess > xAllFieldNames = xSupplyCols->getColumns(); |
| Sequence< ::rtl::OUString > seqFieldNames = xAllFieldNames->getElementNames(); |
| ::rtl::OUString* pFieldNames = seqFieldNames.getArray(); |
| |
| |
| ::rtl::OUString sCurrentField; |
| UniString sVis(sVisibleFields.getStr()); |
| xub_StrLen nLen = sVis.GetTokenCount(); |
| for (xub_StrLen i=0; i<nLen; ++i) |
| { |
| sCurrentField = sVis.GetToken(i); |
| |
| // in der Feld-Sammlung suchen |
| sal_Int32 nFoundIndex = -1; |
| for (sal_Int32 j=0; j<seqFieldNames.getLength(); ++j, ++pFieldNames) |
| { |
| if ( 0 == m_aStringCompare.compareString( *pFieldNames, sCurrentField ) ) |
| { |
| nFoundIndex = j; |
| break; |
| } |
| } |
| // set the field selection back to the first |
| pFieldNames = seqFieldNames.getArray();; |
| DBG_ASSERT(nFoundIndex != -1, "FmSearchEngine::Init : Invalid field name were given !"); |
| m_arrFieldMapping.push_back(nFoundIndex); |
| } |
| } |
| catch(Exception&) |
| { |
| DBG_ERROR("Exception occured!"); |
| } |
| |
| } |
| |
| //------------------------------------------------------------------------ |
| void FmSearchEngine::SetFormatterUsing(sal_Bool bSet) |
| { |
| if (m_bFormatter == bSet) |
| return; |
| m_bFormatter = bSet; |
| |
| if (m_bUsingTextComponents) |
| { |
| // ich benutzte keinen Formatter, sondern TextComponents -> der SearchIterator muss angepasst werden |
| try |
| { |
| if (m_bFormatter) |
| { |
| DBG_ASSERT(m_xSearchCursor == m_xClonedIterator, "FmSearchEngine::SetFormatterUsing : inkonsistenter Zustand !"); |
| m_xSearchCursor = m_xOriginalIterator; |
| m_xSearchCursor.moveToBookmark(m_xClonedIterator.getBookmark()); |
| // damit ich mit dem neuen Iterator wirklich dort weitermache, wo ich vorher aufgehoert habe |
| } |
| else |
| { |
| DBG_ASSERT(m_xSearchCursor == m_xOriginalIterator, "FmSearchEngine::SetFormatterUsing : inkonsistenter Zustand !"); |
| m_xSearchCursor = m_xClonedIterator; |
| m_xSearchCursor.moveToBookmark(m_xOriginalIterator.getBookmark()); |
| } |
| } |
| catch( const Exception& ) |
| { |
| DBG_UNHANDLED_EXCEPTION(); |
| } |
| |
| // ich muss die Fields neu binden, da der Textaustausch eventuell ueber diese Fields erfolgt und sich der unterliegende Cursor |
| // geaendert hat |
| RebuildUsedFields(m_nCurrentFieldIndex, sal_True); |
| } |
| else |
| InvalidatePreviousLoc(); |
| } |
| |
| //------------------------------------------------------------------------ |
| void FmSearchEngine::PropagateProgress(sal_Bool _bDontPropagateOverflow) |
| { |
| if (m_aProgressHandler.IsSet()) |
| { |
| FmSearchProgress aProgress; |
| try |
| { |
| aProgress.aSearchState = FmSearchProgress::STATE_PROGRESS; |
| aProgress.nCurrentRecord = m_xSearchCursor.getRow() - 1; |
| if (m_bForward) |
| aProgress.bOverflow = !_bDontPropagateOverflow && m_xSearchCursor.isFirst(); |
| else |
| aProgress.bOverflow = !_bDontPropagateOverflow && m_xSearchCursor.isLast(); |
| } |
| catch( const Exception& ) |
| { |
| DBG_UNHANDLED_EXCEPTION(); |
| } |
| |
| m_aProgressHandler.Call(&aProgress); |
| } |
| } |
| |
| //------------------------------------------------------------------------ |
| void FmSearchEngine::SearchNextImpl() |
| { |
| DBG_ASSERT(!(m_bWildcard && m_bRegular) && !(m_bRegular && m_bLevenshtein) && !(m_bLevenshtein && m_bWildcard), |
| "FmSearchEngine::SearchNextImpl : Suchparameter schliessen sich gegenseitig aus !"); |
| |
| DBG_ASSERT(m_xSearchCursor.is(), "FmSearchEngine::SearchNextImpl : habe ungueltigen Iterator !"); |
| |
| // die Parameter der Suche |
| ::rtl::OUString strSearchExpression(m_strSearchExpression); // brauche ich non-const |
| if (!GetCaseSensitive()) |
| // norm the string |
| m_aCharacterClassficator.toLower_rtl(strSearchExpression); |
| |
| if (!m_bRegular && !m_bLevenshtein) |
| { // 'normale' Suche fuehre ich auf jeden Fall ueber WildCards durch, muss aber vorher je nach Modus den ::rtl::OUString anpassen |
| |
| if (!m_bWildcard) |
| { // da natuerlich in allen anderen Faellen auch * und ? im Suchstring erlaubt sind, aber nicht als WildCards zaehlen |
| // sollen, muss ich normieren |
| UniString aTmp(strSearchExpression.getStr()); |
| static const UniString s_sStar = UniString::CreateFromAscii("\\*"); |
| static const UniString s_sQuotation = UniString::CreateFromAscii("\\?"); |
| aTmp.SearchAndReplaceAll('*', s_sStar); |
| aTmp.SearchAndReplaceAll('?', s_sQuotation); |
| strSearchExpression = aTmp; |
| |
| switch (m_nPosition) |
| { |
| case MATCHING_ANYWHERE : |
| strSearchExpression = ::rtl::OUString::createFromAscii("*") + strSearchExpression |
| + ::rtl::OUString::createFromAscii("*"); |
| break; |
| case MATCHING_BEGINNING : |
| strSearchExpression = strSearchExpression + ::rtl::OUString::createFromAscii("*"); |
| break; |
| case MATCHING_END : |
| strSearchExpression = ::rtl::OUString::createFromAscii("*") + strSearchExpression; |
| break; |
| case MATCHING_WHOLETEXT : |
| break; |
| default : |
| DBG_ERROR("FmSearchEngine::SearchNextImpl() : die Methoden-Listbox duerfte nur 4 Eintraege enthalten ..."); |
| } |
| } |
| } |
| |
| // fuer Arbeit auf Feldliste |
| FieldCollectionIterator iterBegin = m_arrUsedFields.begin(); |
| FieldCollectionIterator iterEnd = m_arrUsedFields.end(); |
| FieldCollectionIterator iterFieldCheck; |
| |
| sal_Int32 nFieldPos; |
| |
| if (HasPreviousLoc()) |
| { |
| DBG_ASSERT(EQUAL_BOOKMARKS(m_aPreviousLocBookmark, m_xSearchCursor.getBookmark()), |
| "FmSearchEngine::SearchNextImpl : ungueltige Position !"); |
| iterFieldCheck = m_iterPreviousLocField; |
| // im Feld nach (oder vor) der letzten Fundstelle weitermachen |
| nFieldPos = iterFieldCheck - iterBegin; |
| MoveField(nFieldPos, iterFieldCheck, iterBegin, iterEnd); |
| } |
| else |
| { |
| if (m_bForward) |
| iterFieldCheck = iterBegin; |
| else |
| { |
| iterFieldCheck = iterEnd; |
| --iterFieldCheck; |
| } |
| nFieldPos = iterFieldCheck - iterBegin; |
| } |
| |
| PropagateProgress(sal_True); |
| SEARCH_RESULT srResult; |
| if (m_eSearchForType != SEARCHFOR_STRING) |
| srResult = SearchSpecial(m_eSearchForType == SEARCHFOR_NULL, nFieldPos, iterFieldCheck, iterBegin, iterEnd); |
| else if (!m_bRegular && !m_bLevenshtein) |
| srResult = SearchWildcard(strSearchExpression, nFieldPos, iterFieldCheck, iterBegin, iterEnd); |
| else |
| srResult = SearchRegularApprox(strSearchExpression, nFieldPos, iterFieldCheck, iterBegin, iterEnd); |
| |
| m_srResult = srResult; |
| |
| if (SR_ERROR == m_srResult) |
| return; |
| |
| // gefunden ? |
| if (SR_FOUND == m_srResult) |
| { |
| // die Pos merken |
| try { m_aPreviousLocBookmark = m_xSearchCursor.getBookmark(); } |
| catch ( const Exception& ) { DBG_UNHANDLED_EXCEPTION(); } |
| m_iterPreviousLocField = iterFieldCheck; |
| } |
| else |
| // die "letzte Fundstelle" invalidieren |
| InvalidatePreviousLoc(); |
| } |
| |
| //------------------------------------------------------------------------ |
| IMPL_LINK(FmSearchEngine, OnSearchTerminated, FmSearchThread*, /*pThread*/) |
| { |
| if (!m_aProgressHandler.IsSet()) |
| return 0L; |
| |
| FmSearchProgress aProgress; |
| try |
| { |
| switch (m_srResult) |
| { |
| case SR_ERROR : |
| aProgress.aSearchState = FmSearchProgress::STATE_ERROR; |
| break; |
| case SR_FOUND : |
| aProgress.aSearchState = FmSearchProgress::STATE_SUCCESSFULL; |
| aProgress.aBookmark = m_aPreviousLocBookmark; |
| aProgress.nFieldIndex = m_iterPreviousLocField - m_arrUsedFields.begin(); |
| break; |
| case SR_NOTFOUND : |
| aProgress.aSearchState = FmSearchProgress::STATE_NOTHINGFOUND; |
| aProgress.aBookmark = m_xSearchCursor.getBookmark(); |
| break; |
| case SR_CANCELED : |
| aProgress.aSearchState = FmSearchProgress::STATE_CANCELED; |
| aProgress.aBookmark = m_xSearchCursor.getBookmark(); |
| break; |
| } |
| aProgress.nCurrentRecord = m_xSearchCursor.getRow() - 1; |
| } |
| catch( const Exception& ) |
| { |
| DBG_UNHANDLED_EXCEPTION(); |
| } |
| |
| // per definitionem muss der Link Thread-sicher sein (das verlange ich einfach), so dass ich mich um so etwas hier nicht kuemmern muss |
| m_aProgressHandler.Call(&aProgress); |
| |
| m_bSearchingCurrently = sal_False; |
| return 0L; |
| } |
| |
| //------------------------------------------------------------------------ |
| IMPL_LINK(FmSearchEngine, OnNewRecordCount, void*, pCounterAsVoid) |
| { |
| if (!m_aProgressHandler.IsSet()) |
| return 0L; |
| |
| FmSearchProgress aProgress; |
| aProgress.nCurrentRecord = (sal_uIntPtr)pCounterAsVoid; |
| aProgress.aSearchState = FmSearchProgress::STATE_PROGRESS_COUNTING; |
| m_aProgressHandler.Call(&aProgress); |
| |
| return 0L; |
| } |
| |
| //------------------------------------------------------------------------ |
| sal_Bool FmSearchEngine::CancelRequested() |
| { |
| m_aCancelAsynchAccess.acquire(); |
| sal_Bool bReturn = m_bCancelAsynchRequest; |
| m_aCancelAsynchAccess.release(); |
| return bReturn; |
| } |
| |
| //------------------------------------------------------------------------ |
| void FmSearchEngine::CancelSearch() |
| { |
| m_aCancelAsynchAccess.acquire(); |
| m_bCancelAsynchRequest = sal_True; |
| m_aCancelAsynchAccess.release(); |
| } |
| |
| //------------------------------------------------------------------------ |
| sal_Bool FmSearchEngine::SwitchToContext(const Reference< ::com::sun::star::sdbc::XResultSet > & xCursor, const ::rtl::OUString& sVisibleFields, const InterfaceArray& arrFields, |
| sal_Int32 nFieldIndex) |
| { |
| DBG_ASSERT(!m_bSearchingCurrently, "FmSearchEngine::SwitchToContext : please do not call while I'm searching !"); |
| if (m_bSearchingCurrently) |
| return sal_False; |
| |
| m_xSearchCursor = xCursor; |
| m_xOriginalIterator = xCursor; |
| m_xClonedIterator = CursorWrapper(m_xOriginalIterator, sal_True); |
| m_bUsingTextComponents = sal_True; |
| |
| fillControlTexts(arrFields); |
| |
| Init(sVisibleFields); |
| RebuildUsedFields(nFieldIndex, sal_True); |
| |
| return sal_True; |
| } |
| |
| //------------------------------------------------------------------------ |
| void FmSearchEngine::ImplStartNextSearch() |
| { |
| m_bCancelAsynchRequest = sal_False; |
| m_bSearchingCurrently = sal_True; |
| |
| if (m_eMode == SM_USETHREAD)//CHINA001 if (m_eMode == FmSearchDialog::SM_USETHREAD) |
| { |
| FmSearchThread* pSearcher = new FmSearchThread(this); |
| // der loescht sich nach Beendigung selber ... |
| pSearcher->setTerminationHandler(LINK(this, FmSearchEngine, OnSearchTerminated)); |
| |
| pSearcher->createSuspended(); |
| pSearcher->setPriority(::vos::OThread::TPriority_Lowest); |
| pSearcher->resume(); |
| } |
| else |
| { |
| SearchNextImpl(); |
| LINK(this, FmSearchEngine, OnSearchTerminated).Call(NULL); |
| } |
| } |
| |
| //------------------------------------------------------------------------ |
| void FmSearchEngine::SearchNext(const ::rtl::OUString& strExpression) |
| { |
| m_strSearchExpression = strExpression; |
| m_eSearchForType = SEARCHFOR_STRING; |
| ImplStartNextSearch(); |
| } |
| |
| //------------------------------------------------------------------------ |
| void FmSearchEngine::SearchNextSpecial(sal_Bool _bSearchForNull) |
| { |
| m_eSearchForType = _bSearchForNull ? SEARCHFOR_NULL : SEARCHFOR_NOTNULL; |
| ImplStartNextSearch(); |
| } |
| |
| //------------------------------------------------------------------------ |
| void FmSearchEngine::StartOver(const ::rtl::OUString& strExpression) |
| { |
| try |
| { |
| if (m_bForward) |
| m_xSearchCursor.first(); |
| else |
| m_xSearchCursor.last(); |
| } |
| catch( const Exception& ) |
| { |
| DBG_UNHANDLED_EXCEPTION(); |
| return; |
| } |
| |
| InvalidatePreviousLoc(); |
| SearchNext(strExpression); |
| } |
| |
| //------------------------------------------------------------------------ |
| void FmSearchEngine::StartOverSpecial(sal_Bool _bSearchForNull) |
| { |
| try |
| { |
| if (m_bForward) |
| m_xSearchCursor.first(); |
| else |
| m_xSearchCursor.last(); |
| } |
| catch( const Exception& ) |
| { |
| DBG_UNHANDLED_EXCEPTION(); |
| return; |
| } |
| |
| InvalidatePreviousLoc(); |
| SearchNextSpecial(_bSearchForNull); |
| } |
| |
| //------------------------------------------------------------------------ |
| void FmSearchEngine::InvalidatePreviousLoc() |
| { |
| m_aPreviousLocBookmark.setValue(0,getVoidCppuType()); |
| m_iterPreviousLocField = m_arrUsedFields.end(); |
| } |
| |
| //------------------------------------------------------------------------ |
| void FmSearchEngine::RebuildUsedFields(sal_Int32 nFieldIndex, sal_Bool bForce) |
| { |
| if (!bForce && (nFieldIndex == m_nCurrentFieldIndex)) |
| return; |
| // (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) |
| |
| DBG_ASSERT((nFieldIndex == -1) || |
| ((nFieldIndex >= 0) && |
| (static_cast<size_t>(nFieldIndex) < m_arrFieldMapping.size())), |
| "FmSearchEngine::RebuildUsedFields : nFieldIndex is invalid!"); |
| // alle Felder, die ich durchsuchen muss, einsammeln |
| m_arrUsedFields.clear(); |
| if (nFieldIndex == -1) |
| { |
| Reference< ::com::sun::star::container::XIndexAccess > xFields; |
| for (size_t i=0; i<m_arrFieldMapping.size(); ++i) |
| { |
| Reference< ::com::sun::star::sdbcx::XColumnsSupplier > xSupplyCols(IFACECAST(m_xSearchCursor), UNO_QUERY); |
| DBG_ASSERT(xSupplyCols.is(), "FmSearchEngine::RebuildUsedFields : invalid cursor (no columns supplier) !"); |
| xFields = Reference< ::com::sun::star::container::XIndexAccess > (xSupplyCols->getColumns(), UNO_QUERY); |
| BuildAndInsertFieldInfo(xFields, m_arrFieldMapping[i]); |
| } |
| } |
| else |
| { |
| Reference< ::com::sun::star::container::XIndexAccess > xFields; |
| Reference< ::com::sun::star::sdbcx::XColumnsSupplier > xSupplyCols(IFACECAST(m_xSearchCursor), UNO_QUERY); |
| DBG_ASSERT(xSupplyCols.is(), "FmSearchEngine::RebuildUsedFields : invalid cursor (no columns supplier) !"); |
| xFields = Reference< ::com::sun::star::container::XIndexAccess > (xSupplyCols->getColumns(), UNO_QUERY); |
| BuildAndInsertFieldInfo(xFields, m_arrFieldMapping[static_cast< size_t >(nFieldIndex)]); |
| } |
| |
| m_nCurrentFieldIndex = nFieldIndex; |
| // und natuerlich beginne ich die naechste Suche wieder jungfraeulich |
| InvalidatePreviousLoc(); |
| } |
| |