Commit 9e73b423 authored by Enrico Ros's avatar Enrico Ros

Fixed type-ahead searches. Reworked search code. Found alternative way to

setRasterOp. Cleaned up RenningSearch class. Performance fix on actions.

svn path=/trunk/kdegraphics/kpdf/; revision=385447
parent 6fb78405
...@@ -6,22 +6,20 @@ Legend: ...@@ -6,22 +6,20 @@ Legend:
MRG - MeRGed (code from a branch or a patch) MRG - MeRGed (code from a branch or a patch)
Status: Status:
-> 2005-02-02: Features for 3.4 done. Ready for bugfixes.
-> 2005-01-27: Stable and leakchecked. Usability: needs testing. -> 2005-01-27: Stable and leakchecked. Usability: needs testing.
-> 2005-01-20: Stable. Apart from a bad (but still safe) memory deallocation -> 2005-01-20: Stable. Apart from a bad (but still safe) memory deallocation
mechanism the core is ready for a public release. mechanism the core is ready for a public release.
In progress 3.4 features (deadline is 2k5-Feb-2):
-> new word highlighting for searches / other highlights
More items (first items will enter 'In progress list' first): More items (first items will enter 'In progress list' first):
-> go to next/previous bookmark actions (showing in thumbnailslist rmb popup too) -> go to next/previous bookmark actions (showing in thumbnailslist rmb popup too)
-> viewport restoring: sometimes it seems to restore the viewport a bit under where it was -> viewport restoring: sometimes it seems to restore the viewport a bit under where it was
-> viewport restoring: save the page width setting between runs (save/restore zoom factor) -> viewport restoring: save the page width setting between runs (save/restore zoom factor)
-> presentation: provide a pageX/totalPages indicator in addition to the circle one -> presentation: provide a pageX/totalPages indicator in addition to the circle one
-> add scrollbar marks for bookmarks (like kate) -> add scrollbar marks for bookmarks (like kate)
-> google search in the page -> search: google search in the page
-> cleanup code and update README.png -> cleanup code and update README.png
-> find-as-you-type: use shortcut for 'find next' action (not the default one) -> search: use shortcut for 'find next' action (not the default one) in find-ahead
-> show Viewport in ThumbnailsList (blended/contour) -> show Viewport in ThumbnailsList (blended/contour)
-> Delay TOC (DocumentSynapsis) generation (and move it on thread) -> Delay TOC (DocumentSynapsis) generation (and move it on thread)
-> refactor ThumbnailsList to do internal rendering as pageview does (way faster -> refactor ThumbnailsList to do internal rendering as pageview does (way faster
...@@ -79,6 +77,7 @@ More items (first items will enter 'In progress list' first): ...@@ -79,6 +77,7 @@ More items (first items will enter 'In progress list' first):
-> export: export to other formats keeping formatting (a dream.. except for PNG :-) (PS is easy, we just have PSOutputDev that does it :-D) -> export: export to other formats keeping formatting (a dream.. except for PNG :-) (PS is easy, we just have PSOutputDev that does it :-D)
Done (newest features come first): Done (newest features come first):
-> FIX: memleaks, bugfixes (1,..)
-> CHG: changes and cleanups in pageView's mouse handling functions -> CHG: changes and cleanups in pageView's mouse handling functions
-> ADD: KTTSD simple support: speech selection using kspeech api via pure dcop (don't break compatibiltiy) -> ADD: KTTSD simple support: speech selection using kspeech api via pure dcop (don't break compatibiltiy)
-> CHG: right click and drag while in 'normal' mode changes to 'selection' mode and selects -> CHG: right click and drag while in 'normal' mode changes to 'selection' mode and selects
......
...@@ -35,11 +35,13 @@ ...@@ -35,11 +35,13 @@
#include "conf/settings.h" #include "conf/settings.h"
// structures used internally by KPDFDocument for local variables storage // structures used internally by KPDFDocument for local variables storage
class AllocatedPixmap;
class RunningSearch;
class KPDFDocumentPrivate class KPDFDocumentPrivate
{ {
public: public:
// find descriptors (can handle multiple searches) // find descriptors, mapped by ID (we handle multiple searches)
QMap< int, class RunningSearch * > searches; QMap< int, RunningSearch * > searches;
// cached stuff // cached stuff
QString docFileName; QString docFileName;
...@@ -52,7 +54,7 @@ class KPDFDocumentPrivate ...@@ -52,7 +54,7 @@ class KPDFDocumentPrivate
// observers / requests / allocator stuff // observers / requests / allocator stuff
QMap< int, DocumentObserver * > observers; QMap< int, DocumentObserver * > observers;
QValueList< PixmapRequest * > pixmapRequestsStack; QValueList< PixmapRequest * > pixmapRequestsStack;
QValueList< class AllocatedPixmap * > allocatedPixmapsFifo; QValueList< AllocatedPixmap * > allocatedPixmapsFifo;
int allocatedPixmapsTotalMemory; int allocatedPixmapsTotalMemory;
// timers (memory checking / info saver) // timers (memory checking / info saver)
...@@ -72,19 +74,18 @@ struct AllocatedPixmap ...@@ -72,19 +74,18 @@ struct AllocatedPixmap
struct RunningSearch struct RunningSearch
{ {
// publicly accessible fields // store search properties
QString text; int continueOnPage;
bool caseSensitive; HighlightRect continueOnMatch;
bool moveViewport; QValueList< int > highlightedPages;
KPDFDocument::SearchType type;
QColor highlightColor; // fields related to previous searches (used for 'continueSearch')
QString cachedString;
int lastPage; KPDFDocument::SearchType cachedType;
HighlightRect lastMatch; bool cachedCaseSensitive;
QValueList< int > matchedPages; bool cachedViewportMove;
bool cachedNoDialogs;
// public constructor: initialize data QColor cachedColor;
}; };
#define foreachObserver( cmd ) {\ #define foreachObserver( cmd ) {\
...@@ -415,7 +416,7 @@ void KPDFDocument::requestPixmaps( const QValueList< PixmapRequest * > & request ...@@ -415,7 +416,7 @@ void KPDFDocument::requestPixmaps( const QValueList< PixmapRequest * > & request
// add request to the 'stack' at the right place // add request to the 'stack' at the right place
if ( !request->priority ) if ( !request->priority )
// add priority zero requests to the top of the stack // add priority zero requests to the top of the stack
d->pixmapRequestsStack.push_back( request ); d->pixmapRequestsStack.append( request );
else else
{ {
// insert in stack sorted by priority // insert in stack sorted by priority
...@@ -550,57 +551,58 @@ void KPDFDocument::setNextViewport() ...@@ -550,57 +551,58 @@ void KPDFDocument::setNextViewport()
} }
bool KPDFDocument::searchText( int searchID, const QString & text, bool fromStart, bool KPDFDocument::searchText( int searchID, const QString & text, bool fromStart, bool caseSensitive,
bool caseSensitive, SearchType type, bool moveViewport, const QColor & color ) SearchType type, bool moveViewport, const QColor & color, bool noDialogs )
{ {
// add a new search descriptor to runningSearches // if searchID search not recorded, create new descriptor and init params
if ( !d->searches.contains( searchID ) ) if ( !d->searches.contains( searchID ) )
{ {
RunningSearch * search = new RunningSearch(); RunningSearch * search = new RunningSearch();
search->text = text; search->continueOnPage = -1;
search->caseSensitive = caseSensitive;
search->moveViewport = moveViewport;
search->type = type;
search->highlightColor = color;
search->lastPage = -1;
d->searches[ searchID ] = search; d->searches[ searchID ] = search;
} }
// get search descriptor and update stucture
RunningSearch * s = d->searches[ searchID ]; RunningSearch * s = d->searches[ searchID ];
if ( text != s->text )
s->text = text;
if ( type != s->type )
s->type = type;
// set hourglass cursor // update search stucture
QApplication::setOverrideCursor( waitCursor ); bool newText = text != s->cachedString;
s->cachedString = text;
s->cachedType = type;
s->cachedCaseSensitive = caseSensitive;
s->cachedViewportMove = moveViewport;
s->cachedNoDialogs = noDialogs;
s->cachedColor = color;
// unhighlight pages and inform observers about that // global data for search
QValueList< int >::iterator it = s->matchedPages.begin(), end = s->matchedPages.end(); bool foundAMatch = false;
QValueList< int > pagesToNotify;
// remove highlights from pages and queue them for notifying changes
pagesToNotify += s->highlightedPages;
QValueList< int >::iterator it = s->highlightedPages.begin(), end = s->highlightedPages.end();
for ( ; it != end; ++it ) for ( ; it != end; ++it )
{ pages_vector[ *it ]->deleteHighlights( searchID );
int pageNumber = *it; s->highlightedPages.clear();
pages_vector[ pageNumber ]->deleteHighlights( searchID );
foreachObserver( notifyPageChanged( pageNumber, DocumentObserver::Highlights ) );
}
s->matchedPages.clear();
// // set hourglass cursor
bool foundAMatch = false; QApplication::setOverrideCursor( waitCursor );
// [1] ALLDOC - proces all document marking pages // 1. ALLDOC - proces all document marking pages
if ( type == AllDoc ) if ( type == AllDoc )
{ {
// perform a linear search/mark // search and highlight text on all pages
QValueVector< KPDFPage * >::iterator it = pages_vector.begin(), end = pages_vector.end(); QValueVector< KPDFPage * >::iterator it = pages_vector.begin(), end = pages_vector.end();
for ( ; it != end; ++it ) for ( ; it != end; ++it )
{ {
// get page (from the first to the last)
KPDFPage * page = *it; KPDFPage * page = *it;
int pageNumber = page->number();
// request search page if needed
if ( !page->hasSearchPage() ) if ( !page->hasSearchPage() )
requestTextPage( page->number() ); requestTextPage( pageNumber );
bool found = false; // loop on a page adding highlights for all found items
bool addedHighlights = false;
HighlightRect * lastMatch = 0; HighlightRect * lastMatch = 0;
while ( 1 ) while ( 1 )
{ {
...@@ -612,66 +614,64 @@ bool KPDFDocument::searchText( int searchID, const QString & text, bool fromStar ...@@ -612,66 +614,64 @@ bool KPDFDocument::searchText( int searchID, const QString & text, bool fromStar
if ( !lastMatch ) if ( !lastMatch )
break; break;
found = true; // add highligh rect to the page
lastMatch->id = searchID; lastMatch->id = searchID;
lastMatch->color = color; lastMatch->color = color;
page->setHighlight( lastMatch ); page->setHighlight( lastMatch );
s->lastMatch = *lastMatch; addedHighlights = true;
} }
if ( found ) // if added highlights, udpate internals and queue page for notify
if ( addedHighlights )
{ {
foundAMatch = true; foundAMatch = true;
s->matchedPages.append( page->number() ); s->highlightedPages.append( pageNumber );
if ( !pagesToNotify.contains( pageNumber ) )
pagesToNotify.append( pageNumber );
} }
} }
// send page lists
foreachObserver( notifySetup( pages_vector, false ) ); // reset cursor to previous shape
QApplication::restoreOverrideCursor();
// send page lists if found anything new
//if ( foundAMatch ) ?maybe?
foreachObserver( notifySetup( pages_vector, false ) );
} }
// [2] NEXTMATCH - find next matching item (or start from top) // 2. NEXTMATCH - find next matching item (or start from top)
else if ( s->type == NextMatch ) else if ( type == NextMatch )
{ {
// find out from where to start/resume search from // find out from where to start/resume search from
int viewportPage = (*d->viewportIterator).pageNumber; int viewportPage = (*d->viewportIterator).pageNumber;
int currentPage = 0; int currentPage = fromStart ? 0 : ((s->continueOnPage != -1) ? s->continueOnPage : viewportPage);
KPDFPage * lastPage = 0; KPDFPage * lastPage = fromStart ? 0 : pages_vector[ currentPage ];
if ( !fromStart )
{
currentPage = s->lastPage != -1 ? s->lastPage : viewportPage;
lastPage = pages_vector[ currentPage ];
}
else
{
s->lastPage = 0;
}
HighlightRect * r = 0;
// continue checking last SearchPage first (if it is the current page) // continue checking last SearchPage first (if it is the current page)
if ( lastPage && lastPage->number() == s->lastPage ) HighlightRect * r = 0;
if ( lastPage && lastPage->number() == s->continueOnPage )
{ {
r = lastPage->searchText( text, caseSensitive, &s->lastMatch ); if ( newText )
if ( !r ) r = lastPage->searchText( text, caseSensitive );
{
lastPage->deleteHighlights( searchID );
currentPage++;
}
else else
r = lastPage->searchText( text, caseSensitive, &s->continueOnMatch );
if ( r )
{ {
s->lastMatch = *r; s->continueOnMatch = *r;
s->matchedPages.push_back( currentPage ); s->highlightedPages.append( currentPage );
} }
else
currentPage++;
} }
// if no match found, loop through the whole doc, starting from currentPage
if ( !r ) if ( !r )
{ {
// loop through the whole document
const int pageCount = pages_vector.count(); const int pageCount = pages_vector.count();
for ( int i = 0; i < pageCount; i++ ) for ( int i = 0; i < pageCount; i++ )
{ {
if ( currentPage >= pageCount ) if ( currentPage >= pageCount )
{ {
if ( /*FIXME !findAhead &&*/ KMessageBox::questionYesNo(0, i18n("End of document reached.\nContinue from the beginning?")) == KMessageBox::Yes ) if ( !noDialogs && KMessageBox::questionYesNo(0, i18n("End of document reached.\nContinue from the beginning?")) == KMessageBox::Yes )
currentPage = 0; currentPage = 0;
else else
break; break;
...@@ -681,62 +681,75 @@ bool KPDFDocument::searchText( int searchID, const QString & text, bool fromStar ...@@ -681,62 +681,75 @@ bool KPDFDocument::searchText( int searchID, const QString & text, bool fromStar
requestTextPage( page->number() ); requestTextPage( page->number() );
if ( (r = page->searchText( text, caseSensitive )) ) if ( (r = page->searchText( text, caseSensitive )) )
{ {
s->lastPage = currentPage; s->continueOnPage = currentPage;
s->lastMatch = *r; s->continueOnMatch = *r;
s->matchedPages.push_back( currentPage ); s->highlightedPages.append( currentPage );
break; break;
} }
currentPage++; currentPage++;
} }
} }
// reset cursor to previous shape
QApplication::restoreOverrideCursor();
// if a match has been found..
if ( r ) if ( r )
{ {
// add highlight to the page foundAMatch = true;
// ..add highlight to the page..
int pageNumber = currentPage; int pageNumber = currentPage;
KPDFPage * foundPage = pages_vector[ currentPage ]; KPDFPage * foundPage = pages_vector[ currentPage ];
r->id = searchID; r->id = searchID;
r->color = color; r->color = color;
foundPage->setHighlight( r ); foundPage->setHighlight( r );
// move the viewport to show the searched word centered // ..queue page for notifying changes..
DocumentViewport searchViewport( pageNumber ); if ( !pagesToNotify.contains( pageNumber ) )
searchViewport.reCenter.enabled = true; pagesToNotify.append( pageNumber );
searchViewport.reCenter.normalizedCenterX = (r->left + r->right) / 2.0;
searchViewport.reCenter.normalizedCenterY = (r->top + r->bottom) / 2.0;
setViewport( searchViewport, -1, true );
// notify all observers about hilighting chages // ..move the viewport to show the searched word centered
foreachObserver( notifyPageChanged( pageNumber, DocumentObserver::Highlights ) ); if ( moveViewport )
{
DocumentViewport searchViewport( pageNumber );
searchViewport.reCenter.enabled = true;
searchViewport.reCenter.normalizedCenterX = (r->left + r->right) / 2.0;
searchViewport.reCenter.normalizedCenterY = (r->top + r->bottom) / 2.0;
setViewport( searchViewport, -1, true );
}
} }
else if ( true /*FIXME !findAhead*/ ) else if ( !noDialogs )
KMessageBox::information( 0, i18n("No matches found for '%1'.").arg( text ) ); KMessageBox::information( 0, i18n("No matches found for '%1'.").arg( text ) );
} }
// 3. PREVMATCH //TODO
else if ( type == PrevMatch ) else if ( type == PrevMatch )
{ {
//TODO
} }
// 4. GOOGLELIKE //TODO
else if ( type == GoogleLike ) else if ( type == GoogleLike )
{ {
//TODO
} }
// reset cursor to previous shape // notify observers about highlights changes
QApplication::restoreOverrideCursor(); QValueList< int >::iterator nIt = pagesToNotify.begin(), nEnd = pagesToNotify.end();
for ( ; nIt != nEnd; ++nIt )
foreachObserver( notifyPageChanged( *nIt, DocumentObserver::Highlights ) );
// return if search has found one or more matches
return foundAMatch; return foundAMatch;
} }
bool KPDFDocument::continueSearch( int searchID ) bool KPDFDocument::continueSearch( int searchID )
{ {
// check if searchID is present in runningSearches // check if searchID is present in runningSearches
if ( !d->searches.contains( searchID ) ) if ( !d->searches.contains( searchID ) )
return false; return false;
// get previous parameters for search // start search with cached parameters from last search by searchID
RunningSearch * p = d->searches[ searchID ]; RunningSearch * p = d->searches[ searchID ];
return searchText( searchID, p->text, false, p->caseSensitive, p->type, p->moveViewport, p->highlightColor ); return searchText( searchID, p->cachedString, false, p->cachedCaseSensitive,
p->cachedType, p->cachedViewportMove, p->cachedColor,
p->cachedNoDialogs );
} }
void KPDFDocument::resetSearch( int searchID ) void KPDFDocument::resetSearch( int searchID )
...@@ -749,14 +762,16 @@ void KPDFDocument::resetSearch( int searchID ) ...@@ -749,14 +762,16 @@ void KPDFDocument::resetSearch( int searchID )
RunningSearch * s = d->searches[ searchID ]; RunningSearch * s = d->searches[ searchID ];
// unhighlight pages and inform observers about that // unhighlight pages and inform observers about that
QValueList< int >::iterator it = s->matchedPages.begin(), end = s->matchedPages.end(); QValueList< int >::iterator it = s->highlightedPages.begin(), end = s->highlightedPages.end();
for ( ; it != end; ++it ) for ( ; it != end; ++it )
{ {
int pageNumber = *it; int pageNumber = *it;
pages_vector[ pageNumber ]->deleteHighlights( searchID ); pages_vector[ pageNumber ]->deleteHighlights( searchID );
foreachObserver( notifyPageChanged( pageNumber, DocumentObserver::Highlights ) ); foreachObserver( notifyPageChanged( pageNumber, DocumentObserver::Highlights ) );
} }
s->matchedPages.clear();
// send the setup signal too (to update views that filter on matches)
foreachObserver( notifySetup( pages_vector, false ) );
// remove serch from the runningSearches list and delete it // remove serch from the runningSearches list and delete it
d->searches.remove( searchID ); d->searches.remove( searchID );
......
...@@ -82,10 +82,10 @@ class KPDFDocument : public QObject ...@@ -82,10 +82,10 @@ class KPDFDocument : public QObject
enum SearchType { NextMatch, PrevMatch, AllDoc, GoogleLike }; enum SearchType { NextMatch, PrevMatch, AllDoc, GoogleLike };
bool searchText( int searchID, const QString & text, bool fromStart, bool caseSensitive, bool searchText( int searchID, const QString & text, bool fromStart, bool caseSensitive,
SearchType type, bool moveViewport, const QColor & color ); SearchType type, bool moveViewport, const QColor & color, bool noDialogs = false );
bool continueSearch( int searchID ); bool continueSearch( int searchID );
void resetSearch( int searchID ); void resetSearch( int searchID );
void toggleBookmark( int page ); void toggleBookmark( int page );
void processLink( const KPDFLink * link ); void processLink( const KPDFLink * link );
bool print( KPrinter &printer ); bool print( KPrinter &printer );
......
...@@ -65,6 +65,9 @@ ...@@ -65,6 +65,9 @@
#include "core/document.h" #include "core/document.h"
#include "core/page.h" #include "core/page.h"
// definition of searchID for this class
#define PART_SEARCH_ID 1
typedef KParts::GenericFactory<KPDF::Part> KPDFPartFactory; typedef KParts::GenericFactory<KPDF::Part> KPDFPartFactory;
K_EXPORT_COMPONENT_FACTORY(libkpdfpart, KPDFPartFactory) K_EXPORT_COMPONENT_FACTORY(libkpdfpart, KPDFPartFactory)
...@@ -75,7 +78,8 @@ unsigned int Part::m_count = 0; ...@@ -75,7 +78,8 @@ unsigned int Part::m_count = 0;
Part::Part(QWidget *parentWidget, const char *widgetName, Part::Part(QWidget *parentWidget, const char *widgetName,
QObject *parent, const char *name, QObject *parent, const char *name,
const QStringList & /*args*/ ) const QStringList & /*args*/ )
: DCOPObject("kpdf"), KParts::ReadOnlyPart(parent, name), m_showMenuBarAction(0), m_actionsSearched(false) : DCOPObject("kpdf"), KParts::ReadOnlyPart(parent, name), m_showMenuBarAction(0),
m_actionsSearched(false), m_searchStarted(false)
{ {
// load catalog for translation // load catalog for translation
KGlobal::locale()->insertCatalogue("kpdf"); KGlobal::locale()->insertCatalogue("kpdf");
...@@ -255,7 +259,7 @@ Part::Part(QWidget *parentWidget, const char *widgetName, ...@@ -255,7 +259,7 @@ Part::Part(QWidget *parentWidget, const char *widgetName,
// set our XML-UI resource file // set our XML-UI resource file
setXMLFile("part.rc"); setXMLFile("part.rc");
updateActions(); updateViewActions();
slotWatchFile(); slotWatchFile();
} }
...@@ -278,7 +282,7 @@ void Part::notifyViewportChanged( bool /*smoothMove*/ ) ...@@ -278,7 +282,7 @@ void Part::notifyViewportChanged( bool /*smoothMove*/ )
int viewportPage = m_document->viewport().pageNumber; int viewportPage = m_document->viewport().pageNumber;
if ( viewportPage != lastPage ) if ( viewportPage != lastPage )
{ {
updateActions(); updateViewActions();
lastPage = viewportPage; lastPage = viewportPage;
} }
} }
...@@ -315,11 +319,15 @@ bool Part::openFile() ...@@ -315,11 +319,15 @@ bool Part::openFile()
bool ok = m_document->openDocument( m_file ); bool ok = m_document->openDocument( m_file );
// update one-time actions // update one-time actions
m_find->setEnabled( ok );
m_findNext->setEnabled( ok );
m_saveAs->setEnabled( ok ); m_saveAs->setEnabled( ok );
m_printPreview->setEnabled( ok ); m_printPreview->setEnabled( ok );
m_showProperties->setEnabled( ok );
m_showPresentation->setEnabled( ok );
// update viewing actions // update viewing actions
updateActions(); updateViewActions();
if ( !ok ) if ( !ok )
{ {
...@@ -353,6 +361,21 @@ bool Part::openURL(const KURL &url) ...@@ -353,6 +361,21 @@ bool Part::openURL(const KURL &url)
return b; return b;
} }
bool Part::closeURL()
{
m_find->setEnabled( false );
m_findNext->setEnabled( false );
m_saveAs->setEnabled( false );
m_printPreview->setEnabled( false );
m_showProperties->setEnabled( false );
m_showPresentation->setEnabled( false );
updateViewActions();
m_searchStarted = false;
if (!m_file.isEmpty()) m_watcher->removeFile(m_file);
m_document->closeDocument();
return KParts::ReadOnlyPart::closeURL();
}
void Part::slotWatchFile() void Part::slotWatchFile()
{ {
Settings::setWatchFile(m_watchFile->isChecked()); Settings::setWatchFile(m_watchFile->isChecked());
...@@ -388,19 +411,10 @@ void Part::slotDoFileDirty() ...@@ -388,19 +411,10 @@ void Part::slotDoFileDirty()
} }
} }