From 7cd6bbf1399f9263c55efa4b9be2a5cbfac23507 Mon Sep 17 00:00:00 2001 From: Enrico Ros Date: Thu, 16 Sep 2004 21:27:34 +0000 Subject: [PATCH] Added "search-as-you-tipe" feature. Note:takes lots of ram for big documents but you can try it for 200 pages pdfs. It's pretty fast. You type in some words and only pages containing that sting are displayed. svn path=/branches/kpdf_experiments/kdegraphics/kpdf/; revision=347060 --- kpdf/Makefile.am | 2 +- kpdf/TODO | 3 +- kpdf/document.cpp | 39 +++++++++++++-- kpdf/document.h | 2 +- kpdf/kpdf_pagewidget.cc | 3 +- kpdf/kpdf_pagewidget.h | 3 +- kpdf/kpdf_part.cpp | 26 +++++----- kpdf/kpdf_part.h | 2 + kpdf/kpdf_part.rc | 1 + kpdf/searchwidget.cpp | 103 ++++++++++++++++++++++++++++++++++++++++ kpdf/searchwidget.h | 50 +++++++++++++++++++ 11 files changed, 213 insertions(+), 21 deletions(-) create mode 100644 kpdf/searchwidget.cpp create mode 100644 kpdf/searchwidget.h diff --git a/kpdf/Makefile.am b/kpdf/Makefile.am index 9b4e7e3a0..0775f32a0 100644 --- a/kpdf/Makefile.am +++ b/kpdf/Makefile.am @@ -38,7 +38,7 @@ shellrc_DATA = kpdf_shell.rc kde_module_LTLIBRARIES = libkpdfpart.la # the Part's source, library search path, and link libraries -libkpdfpart_la_SOURCES = QOutputDev.cpp QOutputDevKPrinter.cpp kpdf_part.cpp kpdf_pagewidget.cc thumbnail.cpp thumbnaillist.cpp kpdf_error.cpp xpdf_errors.cpp thumbnailgenerator.cpp document.cpp page.cpp +libkpdfpart_la_SOURCES = QOutputDev.cpp QOutputDevKPrinter.cpp kpdf_part.cpp kpdf_pagewidget.cc thumbnail.cpp thumbnaillist.cpp kpdf_error.cpp xpdf_errors.cpp thumbnailgenerator.cpp document.cpp page.cpp searchwidget.cpp libkpdfpart_la_LDFLAGS = -module $(KDE_PLUGIN) $(all_libraries) libkpdfpart_la_LIBADD = ../xpdf/libxpdf.la $(LIB_KPARTS) $(LIB_KFILE) $(LIB_KDEPRINT) $(LIB_KUTILS) -lm diff --git a/kpdf/TODO b/kpdf/TODO index a17b16861..3205d9896 100644 --- a/kpdf/TODO +++ b/kpdf/TODO @@ -13,11 +13,12 @@ More items Porting / In progress on the branch (first item comes first): -> porting Albert's link following --> porting Albert's search -> implementing async document generator using Albert's thread as the generation thread -> better zoom handling Done (sorted by inv.time) +-> added a 'search bar' with prune-as-you-type feature +-> ported Albert's search -> smart handling of pixmap using an Observer ID (thumbnails are gone, only pixmaps now) -> some toolbar/menu changes -> outline bottom and right edges (of pages) diff --git a/kpdf/document.cpp b/kpdf/document.cpp index da0081d73..014886688 100644 --- a/kpdf/document.cpp +++ b/kpdf/document.cpp @@ -50,6 +50,8 @@ public: // filtering related QString filterString; + bool filterCaseSensitive; + int filterLastCount; // observers related (note: won't delete oservers) QMap< int, KPDFDocumentObserver* > observers; @@ -70,6 +72,7 @@ KPDFDocument::KPDFDocument() d->currentPage = -1; d->currentPosition = 0; d->lastSearchPage = 0; + d->filterLastCount = 0; SplashColor paperColor; paperColor.rgb8 = splashMakeRGB8( 0xff, 0xff, 0xff ); d->kpdfOutputDev = new KPDFOutputDev( paperColor ); @@ -216,11 +219,11 @@ void KPDFDocument::slotSetCurrentPagePosition( int page, float position ) pageChanged(); } -void KPDFDocument::slotSetFilter( const QString & pattern ) +void KPDFDocument::slotSetFilter( const QString & pattern, bool caseSensitive ) { + d->filterCaseSensitive = caseSensitive; d->filterString = pattern; - if ( pattern.length() > 3 ) - sendFilteredPageList(); + sendFilteredPageList(); } void KPDFDocument::slotFind( const QString & t, long opt ) @@ -301,10 +304,37 @@ void KPDFDocument::sendFilteredPageList( bool forceEmpty ) { // make up a value list of the pages [1,2,3..] uint pageCount = d->pages.count(); + //d->filterLastCount QValueList pagesList; if ( !forceEmpty ) + { for ( uint i = 0; i < pageCount ; i++ ) - pagesList.push_back( i ); + { + KPDFPage * page = d->pages[ i ]; + if ( d->filterString.length() < 3 ) + { + pagesList.push_back( i ); + page->hilightLastSearch( false ); + } + else + { + if ( !page->hasSearchPage() ) + { + // build a TextPage using the lightweight KPDFTextDev generator.. + KPDFTextDev td; + d->docLock.lock(); + d->pdfdoc->displayPage( &td, page->number()+1, 72, 72, 0, true, false ); + d->docLock.unlock(); + // ..and attach it to the page + page->setSearchPage( td.takeTextPage() ); + } + bool ok = page->hasText( d->filterString, d->filterCaseSensitive, true ); + if ( ok ) + pagesList.push_back( i ); + page->hilightLastSearch( ok ); + } + } + } // send the list to observers foreachObserver( pageSetup( pagesList ) ); @@ -324,6 +354,7 @@ void KPDFDocument::deletePages() d->pages.clear(); d->currentPage = -1; d->lastSearchPage = 0; + d->filterLastCount = 0; } /** TO BE IMPORTED: diff --git a/kpdf/document.h b/kpdf/document.h index 98d19510e..5dfebe3df 100644 --- a/kpdf/document.h +++ b/kpdf/document.h @@ -71,7 +71,7 @@ public slots: // document commands via slots void slotSetCurrentPage( int page ); void slotSetCurrentPagePosition( int page, float position ); - void slotSetFilter( const QString & pattern ); + void slotSetFilter( const QString & pattern, bool caseSensitive ); void slotFind( const QString & text = "", long options = 0 ); void slotGoToLink( /* QString anchor */ ); diff --git a/kpdf/kpdf_pagewidget.cc b/kpdf/kpdf_pagewidget.cc index f294c32a6..759e8873e 100644 --- a/kpdf/kpdf_pagewidget.cc +++ b/kpdf/kpdf_pagewidget.cc @@ -29,8 +29,9 @@ #include "kpdf_pagewidget.h" #include "page.h" + PageWidget::PageWidget( QWidget *parent, KPDFDocument *document ) - : QScrollView( parent, "KPDF::pageWidget", WRepaintNoErase ), + : QScrollView( parent, "KPDF::pageWidget", WRepaintNoErase | WStaticContents ), m_document( document ), m_page( 0 ), m_mouseMode( MouseNormal ), m_mouseOnLink( false ), m_zoomMode( FixedFactor ), m_zoomFactor( 1.0 ), diff --git a/kpdf/kpdf_pagewidget.h b/kpdf/kpdf_pagewidget.h index f9815af9d..643360d6e 100644 --- a/kpdf/kpdf_pagewidget.h +++ b/kpdf/kpdf_pagewidget.h @@ -17,6 +17,7 @@ #include #include +#include #include "CharTypes.h" #include "document.h" @@ -28,7 +29,7 @@ class KConfigGroup; class PageWidget : public QScrollView, public KPDFDocumentObserver { - Q_OBJECT +Q_OBJECT public: PageWidget( QWidget *parent, KPDFDocument *document ); diff --git a/kpdf/kpdf_part.cpp b/kpdf/kpdf_part.cpp index 8b3f906db..2606a78bc 100644 --- a/kpdf/kpdf_part.cpp +++ b/kpdf/kpdf_part.cpp @@ -21,14 +21,11 @@ * (at your option) any later version. * ***************************************************************************/ -#include "kpdf_part.moc" - -#include - #include #include #include #include +#include #include #include @@ -39,19 +36,19 @@ #include #include #include -#include #include #include +#include #include "kpdf_error.h" -#include "GString.h" - #include "GlobalParams.h" #include "QOutputDevKPrinter.h" -#include "thumbnaillist.h" +#include "kpdf_part.h" #include "kpdf_pagewidget.h" +#include "searchwidget.h" +#include "thumbnaillist.h" #include "document.h" typedef KParts::GenericFactory KPDFPartFactory; @@ -93,7 +90,11 @@ Part::Part(QWidget *parentWidget, const char *widgetName, m_thumbnailList->setMinimumWidth( 50 ); document->addObserver( m_thumbnailList ); - m_pageWidget = new PageWidget( m_splitter, document ); + QVBox * rightVBox = new QVBox( m_splitter ); + + m_searchWidget = new SearchWidget( rightVBox, document ); + + m_pageWidget = new PageWidget( rightVBox, document ); connect( m_pageWidget, SIGNAL( urlDropped( const KURL& ) ), SLOT( openURL( const KURL & ))); //connect(m _pageWidget, SIGNAL( rightClick() ), this, SIGNAL( rightClick() )); document->addObserver( m_pageWidget ); @@ -131,6 +132,7 @@ Part::Part(QWidget *parentWidget, const char *widgetName, // attach the actions of the 2 children widgets too KConfigGroup settings( KPDFPartFactory::instance()->config(), "General" ); m_pageWidget->setupActions( ac, &settings ); + m_searchWidget->setupActions( ac, &settings ); m_thumbnailList->setupActions( ac, &settings ); // local settings @@ -145,6 +147,7 @@ Part::~Part() { KConfigGroup settings( KPDFPartFactory::instance()->config(), "General" ); m_pageWidget->saveSettings( &settings ); + m_searchWidget->saveSettings( &settings ); m_thumbnailList->saveSettings( &settings ); settings.writeEntry( "SplitterSizes", m_splitter->sizes() ); settings.sync(); @@ -279,8 +282,7 @@ void Part::slotSaveFileAs() QString::null, widget(), QString::null ); - if( !KIO::NetAccess::upload( url().path(), - saveURL, static_cast( 0 ) ) ) + if( !KIO::NetAccess::upload( url().path(), saveURL, static_cast( 0 ) ) ) ; // TODO: Proper error dialog } @@ -527,4 +529,4 @@ void BrowserExtension::print() static_cast(parent())->slotPrint(); } -// vim:ts=2:sw=2:tw=78:et +#include "kpdf_part.moc" diff --git a/kpdf/kpdf_part.h b/kpdf/kpdf_part.h index 9bce12274..56d7b1cf1 100644 --- a/kpdf/kpdf_part.h +++ b/kpdf/kpdf_part.h @@ -34,6 +34,7 @@ class LinkDest; class ThumbnailList; class PageWidget; +class SearchWidget; class KPDFDocument; namespace KPDF @@ -104,6 +105,7 @@ namespace KPDF QSplitter *m_splitter; ThumbnailList *m_thumbnailList; PageWidget *m_pageWidget; + SearchWidget *m_searchWidget; // static instances counter static unsigned int m_count; diff --git a/kpdf/kpdf_part.rc b/kpdf/kpdf_part.rc index a761e5a72..1078de3fe 100644 --- a/kpdf/kpdf_part.rc +++ b/kpdf/kpdf_part.rc @@ -28,6 +28,7 @@ &Settings + Main Toolbar diff --git a/kpdf/searchwidget.cpp b/kpdf/searchwidget.cpp new file mode 100644 index 000000000..8b09f893a --- /dev/null +++ b/kpdf/searchwidget.cpp @@ -0,0 +1,103 @@ +/*************************************************************************** + * Copyright (C) 2004 by Enrico Ros * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + ***************************************************************************/ + +// qt/kde includes +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// local includes +#include "searchwidget.h" +#include "document.h" + +//#include +SearchWidget::SearchWidget( QWidget * parent, KPDFDocument * document ) + : QHBox( parent ), m_document( document ), m_caseSensitive( false ) +{ + setMargin( 4 ); + + // clear button + KToolBarButton * clear = new KToolBarButton( SmallIcon("locationbar_erase"), 1, this ); + QToolTip::add( clear, i18n( "Clear filter" ) ); + + // line edit + m_lineEdit = new KLineEdit( this ); + m_lineEdit->setFrame( QFrame::Sunken ); + connect( m_lineEdit, SIGNAL(textChanged(const QString &)), SLOT(slotTextChanged(const QString &)) ); + connect( clear, SIGNAL(clicked()), m_lineEdit, SLOT(clear()) ); + QToolTip::add( m_lineEdit, i18n( "Enter at least 3 letters to filter pages" ) ); + + // change case button and menu + KToolBarButton * search = new KToolBarButton( SmallIcon("find"), 2, this ); + m_caseMenu = new KPopupMenu( search ); + m_caseMenu->insertItem( i18n("Case Insensitive"), 1 ); + m_caseMenu->insertItem( i18n("Case Sensitive"), 2 ); + m_caseMenu->setItemChecked( 1, true ); + connect( m_caseMenu, SIGNAL( activated(int) ), SLOT( slotChangeCase(int) ) ); + search->setPopup( m_caseMenu ); + + clear->setMinimumHeight( m_lineEdit->sizeHint().height() ); + search->setMinimumHeight( m_lineEdit->sizeHint().height() ); +} + +void SearchWidget::setupActions( KActionCollection * ac, KConfigGroup * config ) +{ + KToggleAction * ss = new KToggleAction( i18n( "Show Search Bar" ), 0, ac, "show_searchbar" ); + ss->setCheckedState(i18n("Hide Search Bar")); + connect( ss, SIGNAL( toggled( bool ) ), SLOT( slotToggleSearchBar( bool ) ) ); + + ss->setChecked( config->readBoolEntry( "ShowSearchBar", false ) ); + slotToggleSearchBar( ss->isChecked() ); +} + +void SearchWidget::saveSettings( KConfigGroup * config ) +{ + config->writeEntry( "ShowSearchBar", isShown() ); +} + +void SearchWidget::slotTextChanged( const QString & text ) +{ + if ( text.length() > 2 || text.isEmpty() ) + { + m_lineEdit->setPaletteForegroundColor( palette().active().text() ); + m_document->slotSetFilter( text, m_caseSensitive ); + } + else + { + m_lineEdit->setPaletteForegroundColor( Qt::red ); + m_document->slotSetFilter( QString::null, m_caseSensitive ); + } +} + +void SearchWidget::slotChangeCase( int index ) +{ + bool newState = (index == 2); + if ( newState != m_caseSensitive ) + { + m_caseSensitive = newState; + m_caseMenu->setItemChecked( 1, !m_caseSensitive ); + m_caseMenu->setItemChecked( 2, m_caseSensitive ); + slotTextChanged( m_lineEdit->text() ); + } +} + +void SearchWidget::slotToggleSearchBar( bool visible ) +{ + setShown( visible ); + if ( !visible ) + m_document->slotSetFilter( QString::null, m_caseSensitive ); +} + +#include "searchwidget.moc" diff --git a/kpdf/searchwidget.h b/kpdf/searchwidget.h new file mode 100644 index 000000000..d530cb46b --- /dev/null +++ b/kpdf/searchwidget.h @@ -0,0 +1,50 @@ +/*************************************************************************** + * Copyright (C) 2004 by Enrico Ros * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + ***************************************************************************/ + +#ifndef _KPDF_SEARCHWIDGET_H_ +#define _KPDF_SEARCHWIDGET_H_ + +#include + +class KActionCollection; +class KConfigGroup; +class KPopupMenu; +class KLineEdit; + +class KPDFDocument; + +/** + * @short A search widget for find-as-you-type search. + * + * ... + */ +class SearchWidget : public QHBox +{ +Q_OBJECT + +public: + SearchWidget( QWidget *parent, KPDFDocument *document ); + + // create actions that interact with this widget + void setupActions( KActionCollection * collection, KConfigGroup * config ); + void saveSettings( KConfigGroup * config ); + +private slots: + void slotTextChanged( const QString & text ); + void slotChangeCase( int index ); + void slotToggleSearchBar( bool visible ); + +private: + KPDFDocument * m_document; + KLineEdit * m_lineEdit; + KPopupMenu * m_caseMenu; + bool m_caseSensitive; +}; + +#endif