Members of the KDE Community are recommended to subscribe to the kde-community mailing list at https://mail.kde.org/mailman/listinfo/kde-community to allow them to participate in important discussions and receive other important announcements

Commit f55c401b authored by Pino Toscano's avatar Pino Toscano

Refactor the document search interfaces, as discussed on the mailing list.

Merge the search in normal mode and the type-ahead in a search bar that appears on the bottom of the page view. This should work nicely.
In presentation mode, add a small floating search toolbar that takes care of searching during the presentation mode, on document request. This is not working yet, but basically most of the work is done.

Please test and report any problems you find.

CCMAIL: okular-devel@kde.org

svn path=/trunk/playground/graphics/okular/; revision=643607
parent af1fd0c1
......@@ -101,6 +101,7 @@ set(okularpart_SRCS
ui/annotationtools.cpp
ui/annotationwidgets.cpp
ui/bookmarklist.cpp
ui/findbar.cpp
ui/formwidgets.cpp
ui/minibar.cpp
ui/newstuff.cpp
......@@ -110,6 +111,7 @@ set(okularpart_SRCS
ui/pageviewannotator.cpp
ui/pageview.cpp
ui/pageviewutils.cpp
ui/presentationsearchbar.cpp
ui/presentationwidget.cpp
ui/propertiesdialog.cpp
ui/searchlineedit.cpp
......
......@@ -54,6 +54,7 @@ class VisiblePageRect;
#define PART_SEARCH_ID 1
#define PAGEVIEW_SEARCH_ID 2
#define SW_SEARCH_ID 3
#define PRESENTATION_SEARCH_ID 4
/**
......
......@@ -36,9 +36,7 @@
#include <kstandardaction.h>
#include <kparts/genericfactory.h>
#include <kfiledialog.h>
#include <kfind.h>
#include <kmessagebox.h>
#include <kfinddialog.h>
#include <knuminput.h>
#include <kio/netaccess.h>
#include <kmenu.h>
......@@ -69,6 +67,7 @@
#include "ui/presentationwidget.h"
#include "ui/pagesizelabel.h"
#include "ui/bookmarklist.h"
#include "ui/findbar.h"
#include "conf/preferencesdialog.h"
#include "settings.h"
#include "core/bookmarkmanager.h"
......@@ -221,6 +220,8 @@ m_searchStarted(false), m_cliPresentation(false)
connect( m_document, SIGNAL( warning( const QString&, int ) ), m_pageView, SLOT( warningMessage( const QString&, int ) ) );
connect( m_document, SIGNAL( notice( const QString&, int ) ), m_pageView, SLOT( noticeMessage( const QString&, int ) ) );
rightLayout->addWidget( m_pageView );
m_findBar = new FindBar( m_document, rightContainer );
rightLayout->addWidget( m_findBar );
QWidget * bottomBar = new QWidget( rightContainer );
QHBoxLayout * bottomBarLayout = new QHBoxLayout( bottomBar );
m_pageSizeLabel = new PageSizeLabel( bottomBar, m_document );
......@@ -309,8 +310,11 @@ m_searchStarted(false), m_cliPresentation(false)
ac->addAction("edit_copy",m_copy);
// Find and other actions
m_find = KStandardAction::find( this, SLOT( slotFind() ), ac );
m_find = KStandardAction::find( this, SLOT( slotShowFindBar() ), ac );
ac->addAction("find", m_find);
QList<QKeySequence> s = m_find->shortcuts();
s.append( QKeySequence( Qt::Key_Slash ) );
m_find->setShortcuts( s );
m_find->setEnabled( false );
m_findNext = KStandardAction::findNext( this, SLOT( slotFindNext() ), ac);
......@@ -396,6 +400,12 @@ m_searchStarted(false), m_cliPresentation(false)
m_aboutBackend->setText(i18n("About backend..."));
connect(m_aboutBackend, SIGNAL(triggered()), this, SLOT(slotAboutBackend()));
KAction *closeFindBar = new KAction( i18n( "Close &Find Bar" ), ac );
ac->addAction("close_find_bar", closeFindBar);
connect(closeFindBar, SIGNAL(triggered()), this, SLOT(slotHideFindBar()));
closeFindBar->setShortcut( QKeySequence( Qt::Key_Escape ) );
widget()->addAction(closeFindBar);
// attach the actions of the children widgets too
m_pageView->setupActions( ac );
m_formsMessage->setActionButton( m_pageView->toggleFormsAction() );
......@@ -928,6 +938,18 @@ void Part::enableTOC(bool enable)
}
void Part::slotShowFindBar()
{
m_findBar->show();
m_findBar->focusAndSetCursor();
}
void Part::slotHideFindBar()
{
m_findBar->hide();
m_pageView->setFocus();
}
//BEGIN go to page dialog
class GotoPageDialog : public KDialog
{
......@@ -1055,29 +1077,21 @@ void Part::slotNextBookmark()
void Part::slotFind()
{
KFindDialog dlg( widget() );
dlg.setHasCursor( false );
if ( !m_searchHistory.empty() )
dlg.setFindHistory( m_searchHistory );
dlg.setSupportsBackwardsFind( false );
dlg.setSupportsWholeWordsFind( false );
dlg.setSupportsRegularExpressionFind( false );
if ( dlg.exec() == QDialog::Accepted )
{
m_searchHistory = dlg.findHistory();
m_searchStarted = true;
m_document->resetSearch( PART_SEARCH_ID );
m_document->searchText( PART_SEARCH_ID, dlg.pattern(), false,
dlg.options() & KFind::CaseSensitive ? Qt::CaseSensitive : Qt::CaseInsensitive,
Okular::Document::NextMatch, true, qRgb( 255, 255, 64 ) );
}
// when in presentation mode, there's already a search bar, taking care of
// the 'find' requests
if ( (PresentationWidget*)m_presentationWidget != 0 )
return;
slotShowFindBar();
}
void Part::slotFindNext()
{
if (!m_document->continueLastSearch())
slotFind();
if (m_findBar->isHidden())
slotShowFindBar();
else
m_findBar->findNext();
}
......
......@@ -41,6 +41,7 @@ class KAboutData;
class KPrinter;
class KTemporaryFile;
class FindBar;
class ThumbnailList;
class ThumbnailController;
class PageSizeLabel;
......@@ -139,6 +140,8 @@ class Part : public KParts::ReadOnlyPart, public Okular::DocumentObserver, publi
void close();
void cannotQuit();
void splitterMoved( int pos, int index );
void slotShowFindBar();
void slotHideFindBar();
void setMimeTypes(KIO::Job *job);
void readMimeType(KIO::Job *job, const QString &mime);
void saveSplitterSize();
......@@ -171,6 +174,7 @@ class Part : public KParts::ReadOnlyPart, public Okular::DocumentObserver, publi
QWidget *m_leftPanel;
QToolBox *m_toolBox;
SearchWidget *m_searchWidget;
FindBar * m_findBar;
PageViewTopMessage * m_topMessage;
PageViewTopMessage * m_formsMessage;
QPointer<ThumbnailList> m_thumbnailList;
......
/***************************************************************************
* Copyright (C) 2007 by Pino Toscano <pino@kde.org> *
* *
* 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 <qlayout.h>
#include <qmenu.h>
#include <qtoolbutton.h>
#include <kicon.h>
#include <klocale.h>
#include <kpushbutton.h>
// local includes
#include "findbar.h"
#include "searchlineedit.h"
#include "core/document.h"
FindBar::FindBar( Okular::Document * document, QWidget * parent )
: QWidget( parent )
{
QHBoxLayout * lay = new QHBoxLayout( this );
lay->setMargin( 2 );
QToolButton * closeBtn = new QToolButton( this );
closeBtn->setIcon( KIcon( "fileclose" ) );
closeBtn->setIconSize( QSize( 24, 24 ) );
closeBtn->setToolTip( i18n( "Close" ) );
closeBtn->setAutoRaise( true );
lay->addWidget( closeBtn );
m_text = new SearchLineEdit( this, document );
m_text->setSearchCaseSensitivity( Qt::CaseInsensitive );
m_text->setSearchMinimumLength( 0 );
m_text->setSearchType( Okular::Document::NextMatch );
m_text->setSearchId( PART_SEARCH_ID );
m_text->setSearchColor( qRgb( 255, 255, 64 ) );
m_text->setSearchMoveViewport( true );
lay->addWidget( m_text );
KPushButton * findNextBtn = new KPushButton( KIcon( "find-next" ), i18n( "Find Next" ), this );
lay->addWidget( findNextBtn );
QPushButton * optionsBtn = new QPushButton( this );
optionsBtn->setText( i18n( "Options" ) );
QMenu * optionsMenu = new QMenu();
m_caseSensitiveAct = optionsMenu->addAction( i18n( "Case sensitive" ) );
m_caseSensitiveAct->setCheckable( true );
optionsBtn->setMenu( optionsMenu );
lay->addWidget( optionsBtn );
connect( closeBtn, SIGNAL( clicked() ), this, SLOT( close() ) );
connect( findNextBtn, SIGNAL( clicked() ), this, SLOT( findNext() ) );
connect( m_caseSensitiveAct, SIGNAL( toggled( bool ) ), this, SLOT( caseSensitivityChanged() ) );
hide();
}
FindBar::~FindBar()
{
}
QString FindBar::text() const
{
return m_text->text();
}
Qt::CaseSensitivity FindBar::caseSensitivity() const
{
return m_caseSensitiveAct->isChecked() ? Qt::CaseSensitive : Qt::CaseInsensitive;
}
void FindBar::focusAndSetCursor()
{
setFocus();
m_text->setFocus();
}
void FindBar::findNext()
{
m_text->findNext();
}
void FindBar::caseSensitivityChanged()
{
m_text->setSearchCaseSensitivity( m_caseSensitiveAct->isChecked() ? Qt::CaseSensitive : Qt::CaseInsensitive );
m_text->restartSearch();
}
#include "findbar.moc"
/***************************************************************************
* Copyright (C) 2007 by Pino Toscano <pino@kde.org> *
* *
* 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 _FINDBAR_H_
#define _FINDBAR_H_
#include <qwidget.h>
class QAction;
class SearchLineEdit;
namespace Okular {
class Document;
}
class FindBar
: public QWidget
{
Q_OBJECT
public:
FindBar( Okular::Document * document, QWidget * parent = 0 );
virtual ~FindBar();
QString text() const;
Qt::CaseSensitivity caseSensitivity() const;
void focusAndSetCursor();
public slots:
void findNext();
private slots:
void caseSensitivityChanged();
private:
SearchLineEdit * m_text;
QAction * m_caseSensitiveAct;
};
#endif
......@@ -108,10 +108,6 @@ public:
QSet< int > pagesWithTextSelection;
bool mouseOnRect;
// type ahead find
bool typeAheadActive;
QString typeAheadString;
QTimer * findTimeoutTimer;
// viewport move
bool viewportMoveActive;
QTime viewportMoveTime;
......@@ -269,8 +265,6 @@ PageView::PageView( QWidget *parent, Okular::Document *document )
d->mouseSelecting = false;
d->mouseTextSelecting = false;
d->mouseOnRect = false;
d->typeAheadActive = false;
d->findTimeoutTimer = 0;
d->viewportMoveActive = false;
d->viewportMoveTimer = 0;
d->scrollIncrement = 0;
......@@ -1020,90 +1014,6 @@ void PageView::keyPressEvent( QKeyEvent * e )
if ( ( d->mouseSelecting && e->key() != Qt::Key_Escape ) || d->mouseMidZooming )
return;
// handle 'find as you type' (based on khtml/khtmlview.cpp)
if( d->typeAheadActive )
{
// backspace: remove a char and search or terminates search
if( e->key() == Qt::Key_Backspace )
{
if( d->typeAheadString.length() > 1 )
{
d->typeAheadString = d->typeAheadString.left( d->typeAheadString.length() - 1 );
bool found = d->document->searchText( PAGEVIEW_SEARCH_ID, d->typeAheadString,
true, Qt::CaseInsensitive,
Okular::Document::NextMatch, true, qRgb( 128, 255, 128 ), true );
KLocalizedString status = found ? ki18n("Text found: \"%1\".") : ki18n("Text not found: \"%1\".");
d->messageWindow->display( status.subs(d->typeAheadString.toLower()).toString(),
found ? PageViewMessage::Find : PageViewMessage::Warning, 4000 );
d->findTimeoutTimer->start( 3000 );
}
else
{
slotStopFindAhead();
d->document->resetSearch( PAGEVIEW_SEARCH_ID );
}
}
// go to next occurrency
#ifdef __GNUC__
#warning FIX the find next shortcut
#endif
else if( e->key() == Qt::Key_F3 /*d->actionCollection->action( "find_next" )->shortcut().keyQt()*/ )
{
// part doesn't get this key event because of the keyboard grab
d->findTimeoutTimer->stop(); // restore normal operation during possible messagebox is displayed
// (1/4) it is needed to grab the keyboard becase people may have Space assigned
// to a accel and without grabbing the keyboard you can not vim-search for space
// because it activates the accel
releaseKeyboard();
if ( d->document->continueSearch( PAGEVIEW_SEARCH_ID ) )
d->messageWindow->display( i18n("Text found: \"%1\".", d->typeAheadString.toLower()),
PageViewMessage::Find, 3000 );
d->findTimeoutTimer->start( 3000 );
// (2/4) it is needed to grab the keyboard becase people may have Space assigned
// to a accel and without grabbing the keyboard you can not vim-search for space
// because it activates the accel
grabKeyboard();
}
// esc and return: end search
else if( e->key() == Qt::Key_Escape || e->key() == Qt::Key_Return )
{
slotStopFindAhead();
}
// other key: add to text and search
else if( !e->text().isEmpty() )
{
d->typeAheadString += e->text();
doTypeAheadSearch();
}
return;
}
else if( e->key() == '/' && d->document->isOpened() && d->document->supportsSearching() )
{
// stop scrolling the page (if doing it)
if ( d->autoScrollTimer )
{
d->scrollIncrement = 0;
d->autoScrollTimer->stop();
}
// start type-adeas search
d->typeAheadString = QString();
d->messageWindow->display( i18n("Starting -- find text as you type"), PageViewMessage::Find, 3000 );
d->typeAheadActive = true;
if ( !d->findTimeoutTimer )
{
// create the timer on demand
d->findTimeoutTimer = new QTimer( this );
d->findTimeoutTimer->setSingleShot( true );
connect( d->findTimeoutTimer, SIGNAL( timeout() ), this, SLOT( slotStopFindAhead() ) );
}
d->findTimeoutTimer->start( 3000 );
// (3/4) it is needed to grab the keyboard becase people may have Space assigned
// to a accel and without grabbing the keyboard you can not vim-search for space
// because it activates the accel
grabKeyboard();
return;
}
// if viewport is moving, disable keys handling
if ( d->viewportMoveActive )
return;
......@@ -1197,15 +1107,7 @@ void PageView::keyPressEvent( QKeyEvent * e )
void PageView::inputMethodEvent( QInputMethodEvent * e )
{
if( d->typeAheadActive )
{
if( !e->commitString().isEmpty() )
{
d->typeAheadString += e->commitString();
doTypeAheadSearch();
e->accept();
}
}
Q_UNUSED(e)
}
void PageView::contentsMouseMoveEvent( QMouseEvent * e )
......@@ -2411,17 +2313,6 @@ void PageView::toggleFormWidgets( bool on )
}
}
void PageView::doTypeAheadSearch()
{
bool found = d->document->searchText( PAGEVIEW_SEARCH_ID, d->typeAheadString,
false, Qt::CaseInsensitive,
Okular::Document::NextMatch, true, qRgb( 128, 255, 128 ), true );
KLocalizedString status = found ? ki18n("Text found: \"%1\".") : ki18n("Text not found: \"%1\".");
d->messageWindow->display( status.subs(d->typeAheadString.toLower()).toString(),
found ? PageViewMessage::Find : PageViewMessage::Warning, 4000 );
d->findTimeoutTimer->start( 3000 );
}
//BEGIN private SLOTS
void PageView::slotRelayoutPages()
// called by: notifySetup, viewportResizeEvent, slotRenderMode, slotContinuousToggled, updateZoom
......@@ -2887,17 +2778,6 @@ void PageView::slotDragScroll()
selectionEndPoint( p );
}
void PageView::slotStopFindAhead()
{
d->typeAheadActive = false;
d->typeAheadString = "";
d->messageWindow->display( i18n("Find stopped."), PageViewMessage::Find, 1000 );
// (4/4) it is needed to grab the keyboard becase people may have Space assigned
// to a accel and without grabbing the keyboard you can not vim-search for space
// because it activates the accel
releaseKeyboard();
}
void PageView::slotShowWelcome()
{
// show initial welcome text
......
......@@ -143,8 +143,6 @@ Q_OBJECT
void textSelectionClear();
// updates cursor
void updateCursor( const QPoint &p );
// does the type ahead search
void doTypeAheadSearch();
int viewColumns() const;
int viewRows() const;
......@@ -167,8 +165,6 @@ Q_OBJECT
void slotAutoScoll();
// activated by the dragScroll timer
void slotDragScroll();
// type-ahead find timeout
void slotStopFindAhead();
// show the welcome message
void slotShowWelcome();
......
/***************************************************************************
* Copyright (C) 2007 by Pino Toscano <pino@kde.org> *
* *
* 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. *
***************************************************************************/
#include <qevent.h>
#include <qlayout.h>
#include <qstyle.h>
#include <qstyleoption.h>
#include <qstylepainter.h>
#include <qtoolbutton.h>
#include <kicon.h>
#include <klocale.h>
#include "presentationsearchbar.h"
#include "searchlineedit.h"
#define SNAP_DELTA 15
class HandleDrag
: public QWidget
{
public:
HandleDrag( QWidget *parent = 0 )
: QWidget( parent )
{
setCursor( Qt::SizeAllCursor );
setFixedWidth( style()->pixelMetric( QStyle::PM_ToolBarHandleExtent ) );
installEventFilter( parent );
}
void paintEvent( QPaintEvent * )
{
QStyleOption opt;
opt.initFrom( this );
opt.state |= QStyle::State_Horizontal;
QStylePainter p( this );
p.drawPrimitive( QStyle::PE_IndicatorToolBarHandle, opt );
}
};
PresentationSearchBar::PresentationSearchBar( Okular::Document *document, QWidget *anchor, QWidget *parent )
: QWidget( parent ), m_anchor( anchor ), m_snapped( true )
{
setAutoFillBackground( true );
QHBoxLayout * lay = new QHBoxLayout( this );
lay->setMargin( 0 );
m_handle = new HandleDrag( this );
lay->addWidget( m_handle );
QToolButton * closeBtn = new QToolButton( this );
closeBtn->setIcon( KIcon( "fileclose" ) );
closeBtn->setIconSize( QSize( 24, 24 ) );
closeBtn->setToolTip( i18n( "Close" ) );
closeBtn->setAutoRaise( true );
lay->addWidget( closeBtn );
m_search = new SearchLineEdit( this, document );
m_search->setClearButtonShown( true );
m_search->setSearchCaseSensitivity( Qt::CaseInsensitive );
m_search->setSearchMinimumLength( 0 );
m_search->setSearchType( Okular::Document::NextMatch );
m_search->setSearchId( PRESENTATION_SEARCH_ID );
m_search->setSearchColor( qRgb( 255, 255, 64 ) );
m_search->setSearchMoveViewport( true );
lay->addWidget( m_search );
m_anchor->installEventFilter( this );
connect( closeBtn, SIGNAL( clicked() ), this, SLOT( close() ) );
// force the initial snap
forceSnap();
}
PresentationSearchBar::~PresentationSearchBar()
{
}
void PresentationSearchBar::forceSnap()
{
m_point = QPoint( m_anchor->width() / 2, m_anchor->height() );
m_snapped = true;
move( m_point.x() - width() / 2, m_point.y() - height() );
}
bool PresentationSearchBar::eventFilter( QObject *obj, QEvent *e )
{
if ( obj == m_handle &&
( e->type() == QEvent::MouseButtonPress || e->type() == QEvent::MouseButtonRelease || e->type() == QEvent::MouseMove ) )
{
QMouseEvent *me = (QMouseEvent*)e;
if ( e->type() == QEvent::MouseButtonPress )
{
m_drag = m_handle->mapTo( this, me->pos() );
}
else if ( e->type() == QEvent::MouseButtonRelease )
{
m_drag = QPoint();
}
else if ( e->type() == QEvent::MouseMove )
{
QPoint snapdelta( width() / 2, height() );
QPoint delta = m_handle->mapTo( this, me->pos() ) - m_drag;
QPoint newpostemp = pos() + delta;
QPoint tmp = newpostemp + snapdelta - m_point;
QPoint newpos = abs( tmp.x() ) < SNAP_DELTA && abs( tmp.y() ) < SNAP_DELTA ? m_point - snapdelta : newpostemp;
m_snapped = newpos == ( m_point - snapdelta );
move( newpos );
}
return true;
}
if ( obj == m_anchor && e->type() == QEvent::Resize )
{
m_point = QPoint( m_anchor->width() / 2, m_anchor->height() );
if ( m_snapped )
move( m_point.x() - width() / 2, m_point.y() - height() );
}
return false;
}
#include "presentationsearchbar.moc"
/***************************************************************************
* Copyright (C) 2007 by Pino Toscano <pino@kde.org> *
* *
* 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 _OKULAR_PRESENTATIONSEARCHBAR_H_
#define _OKULAR_PRESENTATIONSEARCHBAR_H_
#include <qwidget.h>
class SearchLineEdit;
namespace Okular {
class Document;
}
class PresentationSearchBar
: public QWidget
{
Q_OBJECT
public:
PresentationSearchBar( Okular::Document *document, QWidget *anchor, QWidget *parent = 0 );
virtual ~PresentationSearchBar();
void forceSnap();
protected:
bool eventFilter( QObject *, QEvent * );
private:
QWidget *m_handle;
QWidget *m_anchor;
QPoint m_point;
bool m_snapped;
QPoint m_drag;
SearchLineEdit *m_search;
};
#endif
......@@ -39,6 +39,7 @@
#include "presentationwidget.h"
#include "annotationtools.h"
#include "pagepainter.h"
#include "presentationsearchbar.h"
#include "core/audioplayer.h"
#include "core/document.h"
#include "core/generator.h"
......@@ -87,7 +88,7 @@ class PresentationToolBar : public QToolBar
PresentationWidget::PresentationWidget( QWidget * parent, Okular::Document * doc )
: QDialog( parent, Qt::FramelessWindowHint ),
m_pressedLink( 0 ), m_handCursor( false ), m_drawingEngine( 0 ), m_document( doc ),
m_frameIndex( -1 ), m_topBar( 0 ), m_pagesEdit( 0 )
m_frameIndex( -1 ), m_topBar( 0 ), m_pagesEdit( 0 ), m_searchBar( 0 )
{
setModal( true );
setAttribute( Qt::WA_DeleteOnClose );
......@@ -135,6 +136,12 @@ PresentationWidget::~PresentationWidget()
// stop the audio playbacks
Okular::AudioPlayer::instance()->stopPlaybacks();
// remove our highlights
if ( m_searchBar )
{
m_document->resetSearch( PRESENTATION_SEARCH_ID );
}
// remove this widget from document observer
m_document->removeObserver( this );
......@@ -444,6 +451,8 @@ void PresentationWidget::paintEvent( QPaintEvent * pe )
p.setColor( QPalette::Active, QPalette::Background, Qt::darkGray );