Commit 8a858a94 authored by Pino Toscano's avatar Pino Toscano
Browse files

Core implementation of the backward text search, to be used now in the search bar.

Works pretty well -- the only problem left is that when changing the search direction, the first match is the current match.

svn path=/trunk/KDE/kdegraphics/okular/; revision=785629
parent 8b6a259b
......@@ -831,6 +831,107 @@ void DocumentPrivate::doContinueNextMatchSearch(void *pagesToNotifySet, void * t
delete pagesToNotify;
}
void DocumentPrivate::doContinuePrevMatchSearch(void *pagesToNotifySet, void * theMatch, int currentPage, int searchID, const QString & text, int theCaseSensitivity, bool moveViewport, const QColor & color, bool noDialogs, int donePages)
{
RegularAreaRect * match = static_cast<RegularAreaRect *>(theMatch);
Qt::CaseSensitivity caseSensitivity = static_cast<Qt::CaseSensitivity>(theCaseSensitivity);
QSet< int > *pagesToNotify = static_cast< QSet< int > * >( pagesToNotifySet );
if (m_searchCancelled && !match)
{
// if the user cancelled but he just got a match, give him the match!
QApplication::restoreOverrideCursor();
emit m_parent->searchFinished( searchID, Document::SearchCancelled );
delete pagesToNotify;
return;
}
// if no match found, loop through the whole doc, starting from currentPage
if ( !match )
{
int pageCount = m_pagesVector.count();
if (donePages < pageCount)
{
bool doContinue = true;
if ( currentPage < 0 )
{
if ( noDialogs || KMessageBox::questionYesNo(m_parent->widget(), i18n("Beginning of document reached.\nContinue from the bottom?"), QString(), KStandardGuiItem::cont(), KStandardGuiItem::cancel()) == KMessageBox::Yes )
currentPage = pageCount - 1;
else
doContinue = false;
}
if (doContinue)
{
// get page
Page * page = m_pagesVector[ currentPage ];
// request search page if needed
if ( !page->hasTextPage() )
m_parent->requestTextPage( page->number() );
// if found a match on the current page, end the loop
match = page->findText( searchID, text, FromBottom, caseSensitivity );
if ( !match )
{
currentPage--;
donePages++;
}
else
{
donePages = 1;
}
QMetaObject::invokeMethod(m_parent, "doContinuePrevMatchSearch", Qt::QueuedConnection, Q_ARG(void *, pagesToNotifySet), Q_ARG(void *, match), Q_ARG(int, currentPage), Q_ARG(int, searchID), Q_ARG(QString, text), Q_ARG(int, caseSensitivity), Q_ARG(bool, moveViewport), Q_ARG(QColor, color), Q_ARG(bool, noDialogs), Q_ARG(int, donePages));
return;
}
}
}
// reset cursor to previous shape
QApplication::restoreOverrideCursor();
bool foundAMatch = false;
// if a match has been found..
if ( match )
{
// update the RunningSearch structure adding this match..
RunningSearch * s = m_searches[searchID];
foundAMatch = true;
s->continueOnPage = currentPage;
s->continueOnMatch = *match;
s->highlightedPages.insert( currentPage );
// ..add highlight to the page..
m_pagesVector[ currentPage ]->d->setHighlight( searchID, match, color );
// ..queue page for notifying changes..
pagesToNotify->insert( currentPage );
// ..move the viewport to show the first of the searched word sequence centered
if ( moveViewport )
{
DocumentViewport searchViewport( currentPage );
searchViewport.rePos.enabled = true;
searchViewport.rePos.normalizedX = (match->first().left + match->first().right) / 2.0;
searchViewport.rePos.normalizedY = (match->first().top + match->first().bottom) / 2.0;
m_parent->setViewport( searchViewport, -1, true );
}
delete match;
}
else if ( !noDialogs )
KMessageBox::information( m_parent->widget(), i18n( "No matches found for '%1'.", text ) );
// notify observers about highlights changes
foreach(int pageNumber, *pagesToNotify)
foreach(DocumentObserver *observer, m_observers)
observer->notifyPageChanged( pageNumber, DocumentObserver::Highlights );
if (foundAMatch) emit m_parent->searchFinished( searchID, Document::MatchFound );
else emit m_parent->searchFinished( searchID, Document::NoMatchFound );
delete pagesToNotify;
}
void DocumentPrivate::doContinueAllDocumentSearch(void *pagesToNotifySet, void *pageMatchesMap, int currentPage, int searchID, const QString & text, int theCaseSensitivity, const QColor & color)
{
QMap< Page *, QVector<RegularAreaRect *> > *pageMatches = static_cast< QMap< Page *, QVector<RegularAreaRect *> > * >(pageMatchesMap);
......@@ -2207,9 +2308,27 @@ void Document::searchText( int searchID, const QString & text, bool fromStart, Q
QMetaObject::invokeMethod(this, "doContinueNextMatchSearch", Qt::QueuedConnection, Q_ARG(void *, pagesToNotify), Q_ARG(void *, match), Q_ARG(int, currentPage), Q_ARG(int, searchID), Q_ARG(QString, text), Q_ARG(int, caseSensitivity), Q_ARG(bool, moveViewport), Q_ARG(QColor, color), Q_ARG(bool, noDialogs), Q_ARG(int, 1));
}
// 3. PREVMATCH //TODO
// 3. PREVMATCH - find previous matching item (or start from bottom)
else if ( type == PreviousMatch )
{
// find out from where to start/resume search from
int viewportPage = (*d->m_viewportIterator).pageNumber;
int currentPage = fromStart ? d->m_pagesVector.count() - 1 : ((s->continueOnPage != -1) ? s->continueOnPage : viewportPage);
Page * lastPage = fromStart ? 0 : d->m_pagesVector[ currentPage ];
// continue checking last TextPage first (if it is the current page)
RegularAreaRect * match = 0;
if ( lastPage && lastPage->number() == s->continueOnPage )
{
if ( newText )
match = lastPage->findText( searchID, text, FromBottom, caseSensitivity );
else
match = lastPage->findText( searchID, text, PreviousResult, caseSensitivity, &s->continueOnMatch );
if ( !match )
currentPage--;
}
QMetaObject::invokeMethod(this, "doContinuePrevMatchSearch", Qt::QueuedConnection, Q_ARG(void *, pagesToNotify), Q_ARG(void *, match), Q_ARG(int, currentPage), Q_ARG(int, searchID), Q_ARG(QString, text), Q_ARG(int, caseSensitivity), Q_ARG(bool, moveViewport), Q_ARG(QColor, color), Q_ARG(bool, noDialogs), Q_ARG(int, 1));
}
// 4. GOOGLE* - process all document marking pages
else if ( type == GoogleAll || type == GoogleAny )
......
......@@ -109,6 +109,7 @@ class DocumentPrivate
void slotGeneratorConfigChanged( const QString& );
void refreshPixmaps( int );
void doContinueNextMatchSearch(void *pagesToNotifySet, void * match, int currentPage, int searchID, const QString & text, int caseSensitivity, bool moveViewport, const QColor & color, bool noDialogs, int donePages);
void doContinuePrevMatchSearch(void *pagesToNotifySet, void * theMatch, int currentPage, int searchID, const QString & text, int theCaseSensitivity, bool moveViewport, const QColor & color, bool noDialogs, int donePages);
void doContinueAllDocumentSearch(void *pagesToNotifySet, void *pageMatchesMap, int currentPage, int searchID, const QString & text, int caseSensitivity, const QColor & color);
void doContinueGooglesDocumentSearch(void *pagesToNotifySet, void *pageMatchesMap, int currentPage, int searchID, const QString & text, int caseSensitivity, const QColor & color, bool matchAll);
......
......@@ -269,10 +269,10 @@ RegularAreaRect* TextPage::findText( int searchID, const QString &query, SearchD
case FromBottom:
start = d->m_words.end();
end = d->m_words.begin();
if ( !d->m_words.isEmpty() )
{
--start;
}
Q_ASSERT( start != end );
// we can safely go one step back, as we already checked
// that the list is not empty
--start;
forward = false;
break;
case NextResult:
......@@ -290,13 +290,10 @@ RegularAreaRect* TextPage::findText( int searchID, const QString &query, SearchD
{
ret = d->findTextInternalForward( searchID, query, caseSensitivity, start, end );
}
// TODO implement backward search
#if 0
else
{
ret = findTextInternalBackward( searchID, query, caseSensitivity, start, end );
ret = d->findTextInternalBackward( searchID, query, caseSensitivity, start, end );
}
#endif
return ret;
}
......@@ -420,6 +417,130 @@ RegularAreaRect* TextPagePrivate::findTextInternalForward( int searchID, const Q
return 0;
}
RegularAreaRect* TextPagePrivate::findTextInternalBackward( int searchID, const QString &_query,
Qt::CaseSensitivity caseSensitivity,
const TextEntity::List::ConstIterator &start,
const TextEntity::List::ConstIterator &end )
{
QMatrix matrix = m_page ? m_page->rotationMatrix() : QMatrix();
RegularAreaRect* ret=new RegularAreaRect;
const QString query = (caseSensitivity == Qt::CaseSensitive) ? _query : _query.toLower();
// j is the current position in our query
// len is the length of the string in TextEntity
// queryLeft is the length of the query we have left
const TextEntity* curEntity = 0;
int j=query.length() - 1, len=0, queryLeft=query.length();
int offset = 0;
bool haveMatch=false;
bool dontIncrement=false;
bool offsetMoved = false;
TextEntity::List::ConstIterator it = start;
while ( true )
{
curEntity = *it;
const QString &str = curEntity->text();
if ( !offsetMoved && ( it == start ) )
{
if ( m_searchPoints.contains( searchID ) )
{
offset = qMax( m_searchPoints[ searchID ]->offset_begin, 0 );
}
offsetMoved = true;
}
if ( query.at(j).isSpace() )
{
// lets match newline as a space
#ifdef DEBUG_TEXTPAGE
kDebug(OkularDebug) << "newline or space";
#endif
j--;
queryLeft--;
// since we do not really need to increment this after this
// run of the loop finishes because we are not comparing it
// to any entity, rather we are deducing a situation in a document
dontIncrement=true;
}
else
{
dontIncrement=false;
len=str.length();
int min=qMin(queryLeft,len);
#ifdef DEBUG_TEXTPAGE
kDebug(OkularDebug) << str.right(min) << " : " << _query.mid(j-min+1,min);
#endif
// we have equal (or less then) area of the query left as the lengt of the current
// entity
if ((caseSensitivity == Qt::CaseSensitive)
? (str.right(min) != query.mid(j-min+1,min))
: (str.right(min).toLower() != query.mid(j-min+1,min))
)
{
// we not have matched
// this means we do not have a complete match
// we need to get back to query start
// and continue the search from this place
haveMatch=false;
ret->clear();
#ifdef DEBUG_TEXTPAGE
kDebug(OkularDebug) << "\tnot matched";
#endif
j=query.length() - 1;
offset = 0;
queryLeft=query.length();
}
else
{
// we have a match
// move the current position in the query
// to the position after the length of this string
// we matched
// substract the length of the current entity from
// the left length of the query
#ifdef DEBUG_TEXTPAGE
kDebug(OkularDebug) << "\tmatched";
#endif
haveMatch=true;
ret->append( curEntity->transformedArea( matrix ) );
j-=min;
queryLeft-=min;
}
}
if (haveMatch && queryLeft==0 && j<0)
{
// save or update the search point for the current searchID
QMap< int, SearchPoint* >::iterator sIt = m_searchPoints.find( searchID );
if ( sIt == m_searchPoints.end() )
{
sIt = m_searchPoints.insert( searchID, new SearchPoint );
}
SearchPoint* sp = *sIt;
sp->theIt = it;
sp->offset_begin = j;
sp->offset_end = j + qMin( queryLeft, len );
ret->simplify();
return ret;
}
if ( it == end )
break;
else
--it;
}
// end of loop - it means that we've ended the textentities
QMap< int, SearchPoint* >::iterator sIt = m_searchPoints.find( searchID );
if ( sIt != m_searchPoints.end() )
{
SearchPoint* sp = *sIt;
m_searchPoints.erase( sIt );
delete sp;
}
delete ret;
return 0;
}
QString TextPage::text(const RegularAreaRect *area) const
{
if ( area && area->isNull() )
......
......@@ -33,6 +33,10 @@ class TextPagePrivate
Qt::CaseSensitivity caseSensitivity,
const TextEntity::List::ConstIterator &start,
const TextEntity::List::ConstIterator &end );
RegularAreaRect * findTextInternalBackward( int searchID, const QString &query,
Qt::CaseSensitivity caseSensitivity,
const TextEntity::List::ConstIterator &start,
const TextEntity::List::ConstIterator &end );
TextEntity::List m_words;
QMap< int, SearchPoint* > m_searchPoints;
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment