/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * This file is part of the LibreOffice project. * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * This file incorporates work covered by the following license notice: * * 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 .
*/
m_xListening.set(dbcCursor, UNO_QUERY); if (!m_xListening.is()) return;
if (::comphelper::getBOOL(m_xListening->getPropertyValue(FM_PROP_ROWCOUNTFINAL)))
{
m_xListening = nullptr; // there's nothing to do as the record count is already known return;
}
void SAL_CALL FmRecordCountListener::disposing(const css::lang::EventObject& /*Source*/)
{
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 ... !?");
sal_Int32 theCount = ::comphelper::getINT32(m_xListening->getPropertyValue(FM_PROP_ROWCOUNT));
m_lnkWhoWantsToKnow.Call(theCount);
}
}
// the field itself
Reference< XInterface > xCurrentField;
xAllFields->getByIndex(nField) >>= xCurrentField;
// From this I now know that it supports the DatabaseRecord service (I hope). // For the FormatKey and the type I need the PropertySet.
Reference< css::beans::XPropertySet > xProperties(xCurrentField, UNO_QUERY_THROW);
// build the FieldInfo for that
FieldInfo fiCurrent;
fiCurrent.xContents.set(xCurrentField, UNO_QUERY);
// and memorize
m_arrUsedFields.insert(m_arrUsedFields.end(), fiCurrent);
}
OUString FmSearchEngine::FormatField(sal_Int32 nWhich)
{
DBG_ASSERT(o3tl::make_unsigned(nWhich) < m_aControlTexts.size(), "FmSearchEngine::FormatField(sal_Int32) : invalid position !");
DBG_ASSERT(m_aControlTexts[nWhich], "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 is invalid"); // analogous situation as below
nWhich = m_nCurrentFieldIndex;
}
bool bFound(false); bool bMovedAround(false); do
{
Application::Reschedule( true );
// the content to be compared currently
iterFieldLoop->xContents->getString(); // needed for wasNull
bFound = _bSearchForNull == bool(iterFieldLoop->xContents->wasNull()); if (bFound) break;
// next field (implicitly next record, if necessary) if (!MoveField(nFieldPos, iterFieldLoop, iterBegin, iterEnd))
{ // When moving to the next field, something went wrong... // Continuing is not possible, since the next time exactly the same // will definitely go wrong again, thus abort. // Before, however, so that the search continues at the current position: try { m_aPreviousLocBookmark = m_xSearchCursor.getBookmark(); } catch ( const Exception& ) { DBG_UNHANDLED_EXCEPTION("svx"); }
m_iterPreviousLocField = iterFieldLoop; // and leave return SearchResult::Error;
}
if (nFieldPos == 0) // that is, I've moved to a new record
PropagateProgress(bMovedAround); // if we moved to the starting position we don't have to propagate an 'overflow' message // FS - 07.12.99 - 68530
// cancel requested? if (CancelRequested()) return SearchResult::Cancelled;
bool bFound(false); bool bMovedAround(false); do
{
Application::Reschedule( true );
// the content to be compared currently
OUString sCurrentCheck; if (m_bFormatter)
sCurrentCheck = FormatField(nFieldPos); else
sCurrentCheck = iterFieldLoop->xContents->getString();
if (!GetCaseSensitive()) // norm the string
sCurrentCheck = m_aCharacterClassficator.lowercase(sCurrentCheck);
// now the test is easy...
bFound = aSearchExpression.Matches(sCurrentCheck);
if (bFound) break;
// next field (implicitly next record, if necessary) if (!MoveField(nFieldPos, iterFieldLoop, iterBegin, iterEnd))
{ // When moving to the next field, something went wrong... // Continuing is not possible, since the next time exactly the same // will definitely go wrong again, thus abort. // Before, however, so that the search continues at the current position: try { m_aPreviousLocBookmark = m_xSearchCursor.getBookmark(); } catch ( const Exception& ) { DBG_UNHANDLED_EXCEPTION("svx"); }
m_iterPreviousLocField = iterFieldLoop; // and leave return SearchResult::Error;
}
if (nFieldPos == 0) // that is, I've moved to a new record
PropagateProgress(bMovedAround); // if we moved to the starting position we don't have to propagate an 'overflow' message // FS - 07.12.99 - 68530
// cancel requested? if (CancelRequested()) return SearchResult::Cancelled;
// the content to be compared currently
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)
sal_Int32 nStart = 0, nEnd = sCurrentCheck.getLength();
bFound = aLocalEngine.SearchForward(sCurrentCheck, &nStart, &nEnd); // it says 'forward' here, but that only refers to the search within // sCurrentCheck, so it has nothing to do with the direction of my // record migration (MoveField takes care of that)
// check if the position is correct if (bFound)
{ switch (m_nPosition)
{ case MATCHING_WHOLETEXT : if (nEnd != sCurrentCheck.getLength())
{
bFound = false; break;
}
[[fallthrough]]; case MATCHING_BEGINNING : if (nStart != 0)
bFound = false; break; case MATCHING_END : if (nEnd != sCurrentCheck.getLength())
bFound = false; break;
}
}
if (bFound) // still? break;
// next field (implicitly next record, if necessary) if (!MoveField(nFieldPos, iterFieldLoop, iterBegin, iterEnd))
{ // When moving to the next field, something went wrong... // Continuing is not possible, since the next time exactly the same // will definitely go wrong again, thus abort (without error // notification, I expect it to be displayed in the Move). // Before, however, so that the search continues at the current position: try { m_aPreviousLocBookmark = m_xSearchCursor.getBookmark(); } catch ( const Exception& ) { DBG_UNHANDLED_EXCEPTION("svx"); }
m_iterPreviousLocField = iterFieldLoop; // and leave return SearchResult::Error;
}
if (nFieldPos == 0) // that is, I've moved to a new record
PropagateProgress(bMovedAround); // if we moved to the starting position we don't have to propagate an 'overflow' message // FS - 07.12.99 - 68530
// cancel requested? if (CancelRequested()) return SearchResult::Cancelled;
Reference< css::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.emplace_back(new CheckBoxWrapper(xAsCheckBox));
}
}
void FmSearchEngine::Init(std::u16string_view 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 number 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-insensitive 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 // #i8755#
// 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( const 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)!" );
bool bCaseSensitiveIdentifiers = true; // assume case sensitivity if ( xMeta.is() )
bCaseSensitiveIdentifiers = xMeta->supportsMixedCaseQuotedIdentifiers();
// now that we have this information, we need a collator which is able to case (in)sensitivity compare strings
m_aStringCompare.loadDefaultCollator( SvtSysLocale().GetLanguageTag().getLocale(),
bCaseSensitiveIdentifiers ? 0 : css::i18n::CollatorOptions::CollatorOptions_IGNORE_CASE );
try
{ // the cursor can give me a record (as PropertySet), which supports the DatabaseRecord service
Reference< css::sdbcx::XColumnsSupplier > xSupplyCols(IFACECAST(m_xSearchCursor), UNO_QUERY);
DBG_ASSERT(xSupplyCols.is(), "FmSearchEngine::Init : invalid cursor (no columns supplier) !");
Reference< css::container::XNameAccess > xAllFieldNames = xSupplyCols->getColumns(); const Sequence< OUString > seqFieldNames = xAllFieldNames->getElementNames();
// I did not use a formatter, but TextComponents -> the SearchIterator needs to be adjusted try
{ if (m_bFormatter)
{
DBG_ASSERT(m_xSearchCursor == m_xClonedIterator, "FmSearchEngine::SetFormatterUsing : inconsistent state !");
m_xSearchCursor = m_xOriginalIterator;
m_xSearchCursor.moveToBookmark(m_xClonedIterator.getBookmark()); // so that I continue with the new iterator at the actual place where I previously stopped
} else
{
DBG_ASSERT(m_xSearchCursor == m_xOriginalIterator, "FmSearchEngine::SetFormatterUsing : inconsistent state !");
m_xSearchCursor = m_xClonedIterator;
m_xSearchCursor.moveToBookmark(m_xOriginalIterator.getBookmark());
}
} catch( const Exception& )
{
DBG_UNHANDLED_EXCEPTION("svx");
}
// I have to re-bind the fields, because the text exchange might take // place over these fields and the underlying cursor has changed
RebuildUsedFields(m_nCurrentFieldIndex, true);
}
void FmSearchEngine::PropagateProgress(bool _bDontPropagateOverflow)
{ if (!m_aProgressHandler.IsSet()) return;
DBG_ASSERT(m_xSearchCursor.is(), "FmSearchEngine::SearchNextImpl : have invalid iterator!");
// the parameters of the search
OUString strSearchExpression(m_strSearchExpression); // I need non-const if (!GetCaseSensitive()) // norm the string
strSearchExpression = m_aCharacterClassficator.lowercase(strSearchExpression);
if (!m_bRegular && !m_bLevenshtein)
{ // 'normal' search I run through WildCards in any case, but must before adjust the OUString depending on the mode
if (!m_bWildcard)
{ // since in all other cases * and ? in the search string are of course // also allowed, but should not count as WildCards, I need to normalize
OUString aTmp(strSearchExpression);
aTmp = aTmp.replaceAll("*", "\\*");
aTmp = aTmp.replaceAll("?", "\\?");
strSearchExpression = aTmp;
switch (m_nPosition)
{ case MATCHING_ANYWHERE :
strSearchExpression = "*" + strSearchExpression + "*"; break; case MATCHING_BEGINNING :
strSearchExpression += "*"; break; case MATCHING_END :
strSearchExpression = "*" + strSearchExpression; break; case MATCHING_WHOLETEXT : break; default :
OSL_FAIL("FmSearchEngine::SearchNextImpl() : the methods listbox may contain only 4 entries ...");
}
}
}
// for work on field list
FieldCollection::iterator iterBegin = m_arrUsedFields.begin();
FieldCollection::iterator iterEnd = m_arrUsedFields.end();
FieldCollection::iterator iterFieldCheck;
sal_Int32 nFieldPos;
if (m_aPreviousLocBookmark.hasValue())
{
DBG_ASSERT(EQUAL_BOOKMARKS(m_aPreviousLocBookmark, m_xSearchCursor.getBookmark()), "FmSearchEngine::SearchNextImpl : invalid position!");
iterFieldCheck = m_iterPreviousLocField; // continue in the field after (or before) the last discovery
nFieldPos = iterFieldCheck - iterBegin;
MoveField(nFieldPos, iterFieldCheck, iterBegin, iterEnd);
} else
{ if (m_bForward)
iterFieldCheck = iterBegin; else
{
iterFieldCheck = iterEnd;
--iterFieldCheck;
}
nFieldPos = iterFieldCheck - iterBegin;
}
// by definition, the link must be thread-safe (I just require that), // so that I do not have to worry about such things here
m_aProgressHandler.Call(&aProgress);
m_bSearchingCurrently = false;
}
IMPL_LINK(FmSearchEngine, OnNewRecordCount, sal_Int32, theCounter, void)
{ if (!m_aProgressHandler.IsSet()) return;
void FmSearchEngine::RebuildUsedFields(sal_Int32 nFieldIndex, bool bForce)
{ if (!bForce && (nFieldIndex == m_nCurrentFieldIndex)) return; // (since I allow no change of the iterator from the outside, the same css::sdbcx::Index // also always means the same column, so I have nothing to do)
DBG_ASSERT((nFieldIndex == -1) ||
((nFieldIndex >= 0) &&
(o3tl::make_unsigned(nFieldIndex) < m_arrFieldMapping.size())), "FmSearchEngine::RebuildUsedFields : nFieldIndex is invalid!"); // collect all fields I need to search through
m_arrUsedFields.clear(); if (nFieldIndex == -1)
{
Reference< css::container::XIndexAccess > xFields; for (sal_Int32 i : m_arrFieldMapping)
{
Reference< css::sdbcx::XColumnsSupplier > xSupplyCols(IFACECAST(m_xSearchCursor), UNO_QUERY);
DBG_ASSERT(xSupplyCols.is(), "FmSearchEngine::RebuildUsedFields : invalid cursor (no columns supplier) !");
xFields.set(xSupplyCols->getColumns(), UNO_QUERY);
BuildAndInsertFieldInfo(xFields, i);
}
} else
{
Reference< css::container::XIndexAccess > xFields;
Reference< css::sdbcx::XColumnsSupplier > xSupplyCols(IFACECAST(m_xSearchCursor), UNO_QUERY);
DBG_ASSERT(xSupplyCols.is(), "FmSearchEngine::RebuildUsedFields : invalid cursor (no columns supplier) !");
xFields.set (xSupplyCols->getColumns(), UNO_QUERY);
BuildAndInsertFieldInfo(xFields, m_arrFieldMapping[static_cast< size_t >(nFieldIndex)]);
}
m_nCurrentFieldIndex = nFieldIndex; // and of course I start the next search in a virgin state again
InvalidatePreviousLoc();
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.