From 39f8e14d238158bfd7ebf9ef9365fa898ee7badb Mon Sep 17 00:00:00 2001 From: Albert Astals Cid Date: Sat, 5 Mar 2005 17:20:41 +0000 Subject: [PATCH] Branchport fuzzy search (google like) and kwallet support for storing passwords from kpdf_annotations branch to HEAD. Enrico can you check that i have ported all the necessary bits? svn path=/trunk/kdegraphics/kpdf/; revision=395089 --- TODO | 4 +- core/document.cpp | 72 ++++++++++++++++++++++++++- core/document.h | 3 +- core/generator_pdf/generator_pdf.cpp | 57 ++++++++++++++++----- ui/searchwidget.cpp | 74 +++++++++++++++++----------- ui/searchwidget.h | 5 +- 6 files changed, 167 insertions(+), 48 deletions(-) diff --git a/TODO b/TODO index 84f73c977..91e367a5a 100644 --- a/TODO +++ b/TODO @@ -9,7 +9,6 @@ In progress: -- 2005-Feb-26: Merge from 'kpdf_annotations' branch -- More items (first items will enter 'In progress list' first): --> search: google search in the page -> pageview: add scrollbar marks for bookmarks (like kate) -> screen editing (annotations): framework (BR67300,BR62793) -> screen editing (annotations): tools (BR67300), yellow notes 'post-it' like @@ -65,7 +64,6 @@ More items (first items will enter 'In progress list' first): -> investigate 'Splash' lack of smoothness at low resolutions (see lines in thumbnails) -> add search on the toc widget (a 'prune on type' lineedit like in thumbnails widget) -> goto 'logical' page (usually differs from pdf's page) (req. by Luca Burrelli) --> use wallet for storing passwords of encrypted files -> use shortcuts for next and prev page even in presenatation mode (by Tobias Koenig) -> move some document related features from part to the document (see find, goto dialog, ...) -> Albert: Read pdf specification and see if paths with length = 1 are allowed, in case they are allowed see how to fix 97131 without skipping paths with length = 1 @@ -75,6 +73,8 @@ More items (first items will enter 'In progress list' first): Done (newest features come first): -- merging from kdpf_annotations branch -- +-> ADD: google-like search on thumbnails +-> ADD: use kde wallet for storing passwords of protected files -> ADD: Obey DRM is now a configuration option -> FIX: leakfix when closing document while thread was running (no more leaks now) -> FIX: direct hi-performance pixels manipulation for highlighting (instead of the obsoleted setRasterOp) diff --git a/core/document.cpp b/core/document.cpp index e07bbb96c..57ae13da9 100644 --- a/core/document.cpp +++ b/core/document.cpp @@ -735,9 +735,77 @@ bool KPDFDocument::searchText( int searchID, const QString & text, bool fromStar else if ( type == PrevMatch ) { } - // 4. GOOGLELIKE //TODO - else if ( type == GoogleLike ) + // 4. GOOGLE* - process all document marking pages + else if ( type == GoogleAll || type == GoogleAny ) { + // search and highlight every word in 'text' on all pages + bool matchAll = type == GoogleAll; + QStringList words = QStringList::split( " ", text ); + int wordsCount = words.count(), + hueStep = (wordsCount > 1) ? (60 / (wordsCount - 1)) : 60, + baseHue, baseSat, baseVal; + color.getHsv( &baseHue, &baseSat, &baseVal ); + QValueVector< KPDFPage * >::iterator it = pages_vector.begin(), end = pages_vector.end(); + for ( ; it != end; ++it ) + { + // get page (from the first to the last) + KPDFPage * page = *it; + int pageNumber = page->number(); + + // request search page if needed + if ( !page->hasSearchPage() ) + requestTextPage( pageNumber ); + + // loop on a page adding highlights for all found items + bool allMatched = wordsCount > 0, + anyMatched = false; + for ( int w = 0; w < wordsCount; w++ ) + { + QString word = words[ w ]; + int newHue = baseHue - w * hueStep; + if ( newHue < 0 ) + newHue += 360; + QColor wordColor = QColor( newHue, baseSat, baseVal, QColor::Hsv ); + NormalizedRect * lastMatch = 0; + // add all highlights for current word + bool wordMatched = false; + while ( 1 ) + { + if ( lastMatch ) + lastMatch = page->findText( word, caseSensitive, lastMatch ); + else + lastMatch = page->findText( word, caseSensitive ); + + if ( !lastMatch ) + break; + + // add highligh rect to the page + page->setHighlight( searchID, lastMatch, wordColor ); + wordMatched = true; + } + allMatched = allMatched && wordMatched; + anyMatched = anyMatched || wordMatched; + } + + // if not all words are present in page, remove partial highlights + if ( !allMatched && matchAll ) + page->deleteHighlights( searchID ); + + // if page contains all words, udpate internals and queue page for notify + if ( (allMatched && matchAll) || (anyMatched && !matchAll) ) + { + foundAMatch = true; + s->highlightedPages.append( pageNumber ); + if ( !pagesToNotify.contains( pageNumber ) ) + pagesToNotify.append( pageNumber ); + } + } + + // reset cursor to previous shape + QApplication::restoreOverrideCursor(); + + // send page lists to update observers (since some filter on bookmarks) + foreachObserver( notifySetup( pages_vector, false ) ); } // notify observers about highlights changes diff --git a/core/document.h b/core/document.h index 00a6db380..574437516 100644 --- a/core/document.h +++ b/core/document.h @@ -62,7 +62,6 @@ class KPDFDocument : public QObject // enum definitions enum Permission { AllowModify = 1, AllowCopy = 2, AllowPrint = 4, AllowNotes = 8 }; - enum SearchType { NextMatch, PrevMatch, AllDoc, GoogleLike }; // query methods (const ones) bool isOpened() const; @@ -84,6 +83,8 @@ class KPDFDocument : public QObject void setNextViewport(); void requestPixmaps( const QValueList< PixmapRequest * > & requests ); void requestTextPage( uint page ); + + enum SearchType { NextMatch, PrevMatch, AllDoc, GoogleAll, GoogleAny }; bool searchText( int searchID, const QString & text, bool fromStart, bool caseSensitive, SearchType type, bool moveViewport, const QColor & color, bool noDialogs = false ); bool continueSearch( int searchID ); diff --git a/core/generator_pdf/generator_pdf.cpp b/core/generator_pdf/generator_pdf.cpp index 1f8016289..330e6b557 100644 --- a/core/generator_pdf/generator_pdf.cpp +++ b/core/generator_pdf/generator_pdf.cpp @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include @@ -87,7 +88,7 @@ PDFGenerator::~PDFGenerator() //BEGIN Generator inherited functions -bool PDFGenerator::loadDocument( const QString & fileName, QValueVector & pagesVector ) +bool PDFGenerator::loadDocument( const QString & filePath, QValueVector & pagesVector ) { #ifndef NDEBUG if ( pdfdoc ) @@ -97,28 +98,62 @@ bool PDFGenerator::loadDocument( const QString & fileName, QValueVectorisOk() && pdfdoc->getErrorCode() == errEncrypted ) { + QCString password; + + // 1.A. try to retrieve the first password from the kde wallet system + if ( !triedWallet ) + { + QString walletName = KWallet::Wallet::NetworkWallet(); + wallet = KWallet::Wallet::openWallet( walletName ); + if ( wallet ) + { + // use the KPdf folder (and create if missing) + if ( !wallet->hasFolder( "KPdf" ) ) + wallet->createFolder( "KPdf" ); + wallet->setFolder( "KPdf" ); + + // look for the pass in that folder + QString retrievedPass; + if ( !wallet->readPassword( filePath.section('/', -1, -1), retrievedPass ) ) + password = retrievedPass.local8Bit(); + } + triedWallet = true; + } + + // 1.B. if not retrieved, ask the password using the kde password dialog + if ( password.isNull() ) + { QString prompt; - if ( firstTry ) + if ( firstInput ) prompt = i18n( "Please insert the password to read the document:" ); else prompt = i18n( "Incorrect password. Try again:" ); - firstTry = false; + firstInput = false; - QCString pwd; - if ( KPasswordDialog::getPassword( pwd, prompt ) != KPasswordDialog::Accepted ) + // if the user presses cancel, abort opening + if ( KPasswordDialog::getPassword( password, prompt ) != KPasswordDialog::Accepted ) break; - else - { - GString * pwd2 = new GString( pwd.data() ); + } + + // 2. reopen the document using the password + GString * pwd2 = new GString( password.data() ); delete pdfdoc; - pdfdoc = new PDFDoc( new GString( QFile::encodeName( fileName ) ), pwd2, pwd2 ); + pdfdoc = new PDFDoc( new GString( QFile::encodeName( filePath ) ), pwd2, pwd2 ); delete pwd2; + + // 3. if the password is correct, store it to the wallet + if ( pdfdoc->isOk() && wallet && /*safety check*/ wallet->isOpen() ) + { + QString goodPass = QString::fromLocal8Bit( password.data() ); + wallet->writePassword( filePath.section('/', -1, -1), goodPass ); } } if ( !pdfdoc->isOk() ) diff --git a/ui/searchwidget.cpp b/ui/searchwidget.cpp index d510b8778..90ebcd990 100644 --- a/ui/searchwidget.cpp +++ b/ui/searchwidget.cpp @@ -24,14 +24,13 @@ #include "core/document.h" #include "conf/settings.h" -// uncomment following to enable the case switching button -//#define SW_ENABLE_CASE_BUTTON #define CLEAR_ID 1 #define LEDIT_ID 2 #define FIND_ID 3 SearchWidget::SearchWidget( QWidget * parent, KPDFDocument * document ) - : KToolBar( parent, "iSearchBar" ), m_document( document ), m_caseSensitive( false ) + : KToolBar( parent, "iSearchBar" ), m_document( document ), + m_searchType( 0 ), m_caseSensitive( false ) { // change toolbar appearance setMargin( 3 ); @@ -44,31 +43,31 @@ SearchWidget::SearchWidget( QWidget * parent, KPDFDocument * document ) connect( m_inputDelayTimer, SIGNAL( timeout() ), this, SLOT( startSearch() ) ); - // line edit + // 1. text line insertLined( QString::null, LEDIT_ID, SIGNAL( textChanged(const QString &) ), this, SLOT( slotTextChanged(const QString &) ), true, i18n( "Enter at least 3 letters to filter pages" ), 0/*size*/, 1 ); - // clear button (uses a lineEdit slot, so it must be created after) + // 2. clear button (uses a lineEdit slot, so it must be created after) insertButton( QApplication::reverseLayout() ? "clear_left" : "locationbar_erase", CLEAR_ID, SIGNAL( clicked() ), getLined( LEDIT_ID ), SLOT( clear() ), true, i18n( "Clear filter" ), 0/*index*/ ); -#ifdef SW_ENABLE_CASE_BUTTON - // create popup menu for change case button - m_caseMenu = new KPopupMenu( this ); - 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( slotCaseChanged(int) ) ); + // 3.1. create the popup menu for changing filtering features + m_menu = new KPopupMenu( this ); + m_menu->insertItem( i18n("Case Sensitive"), 1 ); + m_menu->insertSeparator( 2 ); + m_menu->insertItem( i18n("Match Phrase"), 3 ); + m_menu->insertItem( i18n("Match All Words"), 4 ); + m_menu->insertItem( i18n("Match Any Word"), 5 ); + m_menu->setItemChecked( 3, true ); + connect( m_menu, SIGNAL( activated(int) ), SLOT( slotMenuChaged(int) ) ); - // create the change case button - insertButton( "find", FIND_ID, m_caseMenu, true, - i18n( "Change Case" ), 2/*index*/ ); -#endif + // 3.2. create the toolbar button that spawns the popup menu + insertButton( "kpdf", FIND_ID, m_menu, true, i18n( "Filter Options" ), 2/*index*/ ); - // setStretchableWidget( lineEditWidget ); + // always maximize the text line setItemAutoSized( LEDIT_ID ); } @@ -88,13 +87,40 @@ void SearchWidget::slotTextChanged( const QString & text ) m_inputDelayTimer->start(333, true); } +void SearchWidget::slotMenuChaged( int index ) +{ + // update internal variables and checked state + if ( index == 1 ) + { + m_caseSensitive = !m_caseSensitive; + m_menu->setItemChecked( 1, m_caseSensitive ); + } + else if ( index >= 3 && index <= 5 ) + { + m_searchType = index - 3; + for ( int i = 0; i < 3; i++ ) + m_menu->setItemChecked( i + 3, m_searchType == i ); + } + else + return; + + // update search + slotTextChanged( getLined( LEDIT_ID )->text() ); +} + void SearchWidget::startSearch() { // search text if have more than 3 chars or else clear search QString text = getLined( LEDIT_ID )->text(); bool ok = true; if ( text.length() >= 3 ) - ok = m_document->searchText( SW_SEARCH_ID, text, true, m_caseSensitive, KPDFDocument::AllDoc, false, qRgb( 0, 183, 255 ) ); + { + KPDFDocument::SearchType type = !m_searchType ? KPDFDocument::AllDoc : + ( (m_searchType > 1) ? KPDFDocument::GoogleAny : + KPDFDocument::GoogleAll ); + ok = m_document->searchText( SW_SEARCH_ID, text, true, m_caseSensitive, + type, false, qRgb( 0, 183, 255 ) ); + } else m_document->resetSearch( SW_SEARCH_ID ); // if not found, use warning colors @@ -106,16 +132,4 @@ void SearchWidget::startSearch() } } -void SearchWidget::slotCaseChanged( 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( getLined( LEDIT_ID )->text() ); - } -} - #include "searchwidget.moc" diff --git a/ui/searchwidget.h b/ui/searchwidget.h index 3bc192a3b..9f2a1e443 100644 --- a/ui/searchwidget.h +++ b/ui/searchwidget.h @@ -36,13 +36,14 @@ class SearchWidget : public KToolBar private: KPDFDocument * m_document; - KPopupMenu * m_caseMenu; + KPopupMenu * m_menu; QTimer * m_inputDelayTimer; + int m_searchType; bool m_caseSensitive; private slots: void slotTextChanged( const QString & text ); - void slotCaseChanged( int index ); + void slotMenuChaged( int index ); void startSearch(); };