From d58a36d40423f30ff4b74c0434cca008ec4c6590 Mon Sep 17 00:00:00 2001 From: nds Date: Fri, 29 Feb 2008 07:21:30 +0000 Subject: [PATCH] Search tool --- src/Qtx/QtxSearchTool.cxx | 1420 +++++++++++++++++++++++++++++++++++++ src/Qtx/QtxSearchTool.h | 201 ++++++ 2 files changed, 1621 insertions(+) create mode 100644 src/Qtx/QtxSearchTool.cxx create mode 100644 src/Qtx/QtxSearchTool.h diff --git a/src/Qtx/QtxSearchTool.cxx b/src/Qtx/QtxSearchTool.cxx new file mode 100644 index 000000000..b825d0338 --- /dev/null +++ b/src/Qtx/QtxSearchTool.cxx @@ -0,0 +1,1420 @@ +// Copyright (C) 2005 OPEN CASCADE, CEA/DEN, EDF R&D, PRINCIPIA R&D +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License. +// +// This library is distributed in the hope that it will be useful +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com +// +// File : QtxSearchTool.cxx +// Author : Vadim SANDLER, Open CASCADE S.A.S. (vadim.sandler@opencascade.com) +// + +#include "QtxSearchTool.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +const char* const first_xpm[] = { +"16 16 14 1", +" c None", +". c #111111", +"+ c #0A0A0A", +"@ c #161616", +"# c #ACACAC", +"$ c #FC6D6E", +"% c #FB6364", +"& c #F25B5C", +"* c #EA5859", +"= c #C1494A", +"- c #B64545", +"; c #AB4040", +"> c #A03C3C", +", c #99393A", +" . ", +" +@+ ..# ", +" +$+# .$.... ", +" +$+# .$$$$$.#", +" +%+# .%%%%%%.#", +" +&+# .&&&&&&&.#", +" +*+#.********.#", +" +=+.=========.#", +" +-+#.--------.#", +" +;+##.;;;;;;;.#", +" +>+# #.>>>>>>.#", +" +,+# #.,,,,,.#", +" +,+# #.,....#", +" +,+# #..####", +" +@+# #.# ", +" ### ## "}; + +const char* const last_xpm[] = { +"16 16 14 1", +" c None", +". c #111111", +"+ c #0A0A0A", +"@ c #161616", +"# c #FC6D6E", +"$ c #ACACAC", +"% c #FB6364", +"& c #F25B5C", +"* c #EA5859", +"= c #C1494A", +"- c #B64545", +"; c #AB4040", +"> c #A03C3C", +", c #99393A", +" . ", +" .. +@+ ", +" ....#. +#+$", +" .#####. +#+$", +" .%%%%%%. +%+$", +" .&&&&&&&. +&+$", +" .********. +*+$", +" .=========.+=+$", +" .--------.$+-+$", +" .;;;;;;;.$$+;+$", +" .>>>>>>.$$ +>+$", +" .,,,,,.$$ +,+$", +" ....,.$$ +,+$", +" $$$..$$ +,+$", +" .$$ +@+$", +" $$ $$$"}; + +const char* const prev_xpm[] = { +"16 16 12 1", +" c None", +". c #111111", +"+ c #ACACAC", +"@ c #FC6D6E", +"# c #FB6364", +"$ c #F25B5C", +"% c #EA5859", +"& c #C1494A", +"* c #B64545", +"= c #AB4040", +"- c #A03C3C", +"; c #99393A", +" . ", +" ..+ ", +" .@...... ", +" .@@@@@@@.+ ", +" .########.+ ", +" .$$$$$$$$$.+ ", +" .%%%%%%%%%%.+ ", +" .&&&&&&&&&&&.+ ", +" .**********.+ ", +" +.=========.+ ", +" +.--------.+ ", +" +.;;;;;;;.+ ", +" +.;......+ ", +" +..++++++ ", +" +.+ ", +" ++ "}; + +const char* const next_xpm[] = { +"16 16 12 1", +" c None", +". c #111111", +"+ c #FC6D6E", +"@ c #FB6364", +"# c #F25B5C", +"$ c #EA5859", +"% c #C1494A", +"& c #B64545", +"* c #ACACAC", +"= c #AB4040", +"- c #A03C3C", +"; c #99393A", +" . ", +" .. ", +" ......+. ", +" .+++++++. ", +" .@@@@@@@@. ", +" .#########. ", +" .$$$$$$$$$$. ", +" .%%%%%%%%%%%. ", +" .&&&&&&&&&&.**", +" .=========.** ", +" .--------.** ", +" .;;;;;;;.** ", +" ......;.** ", +" ****..** ", +" .** ", +" ** "}; + +const char* const close_xpm[] = { +"16 16 8 1", +" c None", +". c #D73727", +"+ c #E17765", +"@ c #E7957F", +"# c #DE6F48", +"$ c #DF7B4F", +"% c #FAE9E4", +"& c #FFFFFF", +" ", +" ............ ", +" .+@@@@@@@@@@+. ", +" .@#$$$$$$$$#@. ", +" .@$$%$$$$%$$@. ", +" .@$%&%$$%&%$@. ", +" .@$$%&%%&%$$@. ", +" .@$$$%&&%$$$@. ", +" .@$$$%&&%$$$@. ", +" .@$$%&%%&%$$@. ", +" .@$%&%$$%&%$@. ", +" .@$$%$$$$%$$@. ", +" .@#$$$$$$$$#@. ", +" .+@@@@@@@@@@+. ", +" ............ ", +" "}; + +const char* highlightColor = "#FF6666"; + +/*! + \brief Wrap specified widget by another dumb widget. + \internal + \param parent widget to be used as parent for the dumb widget + \param w widget to be wrapped + \return wrapper widget +*/ +static QWidget* wrapWidget( QWidget* parent, QWidget* w ) +{ + QWidget* wrapper = new QWidget( parent ); + w->setParent( wrapper ); + QHBoxLayout* l = new QHBoxLayout( wrapper ); + l->setMargin( 1 ); + l->setSpacing( 0 ); + l->addWidget( w ); + return wrapper; +} + +/*! + \class QtxSearchTool + \brief Context search tool. + + The QtxSearchTool class implements a specific context search tool widget + which can be embedded into any GUI element. + It represents the usual dialog panel with the line edit box used to enter + text to be searched and set of buttons, like "Find Next", "Find Previous", etc. + In addition, the search modifiers like "Case sensitive search", "Wrap search" + are provided. + + Actually the class QtxSearchTool does not perform a serach itself - it is only + the generic widget. To use this widget, you have to install a searcher depending + on your needs. This should be a successor of the class QtxSearchTool::Searcher - + it is the class which will perform actual search of the data in your widget + according to the widget type. + + For the current moment, only one standard searcher is implemented: it is the + class QtxTreeViewSearcher, which can be used to search the text data in the + tree view widget (QTreeView). See this class for more details. + + The usual usage of the searcher widget is the following: + \code + QTreeView* tree = new QTreeView( this ); + QtxSearchTool* st = new QtxSearchTool( this, tree, QtxSearchTool::Standard ); + st->setActivators( QtxSearchTool::SlashKey | QtxSearchTool::StandardKey ); + st->setSearcher( new QtxTreeViewSearcher( tree ) ); + \endcode + + Note, that controls to be displayed by the search tool widget are passed as + ORed flags to the widget's constructor. At any time, the available controls + can be set/get with setControls() and controls() methods. + By default, all widgets are displayed (see also QtxSearchTool::Controls + enumeration). + + The class QtxSearchTool also provides a way to add custom widgets - + these widgets are displayed at the bottom area of the tool box. Use + method addCustomWidget() to add custom widget to the search tool box. + Your searcher class can use custom widgets to perform advanced search. + + The class supports different ways of the activation, all of them can be + switched on/off with setActivators() method. See QtxSearchTool::Activator + enumeration for more details. + By default, all methods are switched on: default hot key is and + standard key bindings are the platform dependent keyboard shortcuts. + Shortcuts can be assigned with the setShortcuts() methods. +*/ + +/*! + \brief Constructor. + + Creates a search tool widget with parent widget \a parent + and watched widget \a watched. The controls to be displayed can be passed + via \a controls parameter. By default, all controls are displayed. + + \param parent parent widget + \param watched watched widget + \param controls ORed controls flags (QtxSearchTool::Controls) + \sa setWatchedWidget(), setControls() +*/ +QtxSearchTool::QtxSearchTool( QWidget* parent, QWidget* watched, int controls ) +: QFrame( parent ), + myWatched( watched ? watched : parent ), + mySearcher( 0 ), + myControls( controls ), + myActivators( None ), + myAutoHideTimer( 0 ), + myAutoHideEnabled( true ) +{ + init(); +} + +/*! + \brief Constructor. + + Creates a search tool widget with parent widget \a parent. + Parameter \a parent is also used to set watched widget. + The controls to be displayed can be passed via \a controls parameter. + By default, all controls are displayed. + + \param parent parent widget + \param controls ORed controls flags (QtxSearchTool::Controls) + \sa setWatchedWidget(), setControls() +*/ +QtxSearchTool::QtxSearchTool( QWidget* parent, int controls ) +: QFrame( parent ), + myWatched( parent ), + mySearcher( 0 ), + myControls( controls ), + myActivators( None ), + myAutoHideTimer( 0 ), + myAutoHideEnabled( true ) +{ + init(); +} + +/*! + \brief Destructor. +*/ +QtxSearchTool::~QtxSearchTool() +{ + clearShortcuts(); + if ( mySearcher ) + delete mySearcher; +} + +/*! + \brief Get watched widget. + \return currently used watched widget + \sa setWatchedWidget(), activators(), setActivators() +*/ +QWidget* QtxSearchTool::watchedWidget() const +{ + return myWatched; +} + +/*! + \brief Set watched widget. + + Watched widget is that one for which shortcut bindings are set. + When this widget has focus and any hot key binbding is pressed by the user, + the search tool box is activated. The same occurs if slash key is pressed and + QtxSearchTool::SlashKey activator is enabled. If the QtxSearchTool::PrintKey + activator is enabled, the tool box is activated if any printed key is pressed + by the user. + + \param watched a widget to be watched by the search tool + \sa watchedWidget(), activators(), setActivators() +*/ +void QtxSearchTool::setWatchedWidget( QWidget* watched ) +{ + if ( myWatched ) + { + myWatched->removeEventFilter( this ); + } + + myWatched = watched; + + initShortcuts( shortcuts() ); + + if ( myWatched ) + { + myWatched->installEventFilter( this ); + } +} + +/*! + \brief Get current searcher. + \return currently set searcher (QtxSearchTool::Searcher) + \sa setSearcher() +*/ +QtxSearchTool::Searcher* QtxSearchTool::searcher() const +{ + return mySearcher; +} + +/*! + \brief Assign searcher. + + Note: the search tool takes ownership to the searcher + and destroys it when deleted. + + \param s searcher to be used (QtxSearchTool::Searcher) + \sa searcher() +*/ +void QtxSearchTool::setSearcher( QtxSearchTool::Searcher* s ) +{ + if ( mySearcher ) + delete mySearcher; + mySearcher = s; +} + +/*! + \brief Get activators. + \return activators currently enabled (ORed QtxSearchTool::Activator flags) + \sa setActivators() +*/ +int QtxSearchTool::activators() const +{ + return myActivators; +} + +/*! + \brief Set activators. + \param flags set activators to be used (ORed QtxSearchTool::Activator flags) + \sa activators() +*/ +void QtxSearchTool::setActivators( const int flags ) +{ + myActivators = flags; + updateShortcuts(); +} + +/*! + \brief Get controls. + \return controls currently enabled (ORed QtxSearchTool::Controls flags) + \sa setControls() +*/ +int QtxSearchTool::controls() const +{ + return myControls; +} + +/*! + \brief Set controls. + \param ctrls controls to be displayed (ORed QtxSearchTool::Controls flags) + \sa controls() +*/ +void QtxSearchTool::setControls( const int ctrls ) +{ + if ( myControls == ctrls ) + return; + myControls = ctrls; + updateControls(); +} + +/*! + \brief Get shortcuts. + + Note: the standard bindings are not include to the resulting list. + + \return list of shortcuts bindings currently set + \sa setShortcuts() +*/ +QList QtxSearchTool::shortcuts() const +{ + QList ks; + + ShortcutList::ConstIterator it; + int i; + for ( it = myShortcuts.begin(), i = 0; it != myShortcuts.end(); ++it, i++ ) + { + if ( i > 2 ) ks.append( (*it)->key() ); + } + + return ks; +} + +/*! + \brief Set shortcuts. + \param accel shortcut binding(s) to be used + \sa shortcuts() +*/ +void QtxSearchTool::setShortcuts( const QKeySequence& accel ) +{ + QList ks; + ks << accel; + setShortcuts( ks ); +} + +/*! + \brief Set shortcuts. + \param accel shortcut bindings to be used + \sa shortcuts() +*/ +void QtxSearchTool::setShortcuts( const QList& accels ) +{ + initShortcuts( accels ); +} + +/*! + \brief Add custom widget. + \param w custom widget to be added + \param id widget unique ID to be used (if < 0, automatically assigned) + \return widget unique ID + \sa customWidget(), customWidgetId() +*/ +int QtxSearchTool::addCustomWidget( QWidget* w, int id ) +{ + if ( !w ) return -1; + + static int _wid = -1; + + int wid = -1; + QMap::ConstIterator it; + for ( it = myWidgets.begin(); it != myWidgets.end() && wid == -1; ++it ) + { + if ( it.value() == w ) + wid = it.key(); + } + + if ( wid != -1 ) + return wid; + + wid = id < 0 ? --_wid : id; + + QVBoxLayout* vbox = qobject_cast( layout() ); + w->setParent( this ); + vbox->addWidget( w ); + myWidgets.insert( wid, w ); + + return wid; +} + +/*! + \brief Get custom widget by ID. + \param id widget ID + \return custom widget or 0 if not found + \sa addCustomWidget(), customWidgetId() +*/ +QWidget* QtxSearchTool::customWidget( int id ) const +{ + QWidget* w = 0; + if ( myWidgets.contains( id ) ) + w = myWidgets[ id ]; + return w; +} + +/*! + \brief Get custom widget ID. + \param w custom widget + \return custom widget ID or -1 if widget does not belong to the search tool + \sa addCustomWidget(), customWidget() +*/ +int QtxSearchTool::customWidgetId( QWidget* w ) const +{ + int wid = -1; + QMap::ConstIterator it; + for ( it = myWidgets.begin(); it != myWidgets.end() && wid == -1; ++it ) + { + if ( it.value() == w ) + wid = it.key(); + } + return wid; +} + +/*! + \brief Check if auto-hide of the tool widget is enabled. + + By default, the search tool widget is automatically hidden + after 10 seconds of idle (only if watched widget has input focus). + + \return \c true if auto-hide option is set + \sa enableAutoHide() +*/ +bool QtxSearchTool::isAutoHideEnabled() const +{ + return myAutoHideEnabled; +} + +/*! + \brief Set/clear auto-hide option. + + By default, the search tool widget is automatically hidden + after 10 seconds of idle (only if watched widget has input focus). + + \param enable new option state + \sa isAutoHideEnabled() +*/ +void QtxSearchTool::enableAutoHide( bool enable ) +{ + if ( myAutoHideEnabled == enable ) return; + + myAutoHideEnabled = enable; + + if ( myAutoHideEnabled ) + { + if ( isVisible() && !myData->hasFocus() ) + myAutoHideTimer->start(); + } + else + { + myAutoHideTimer->stop(); + } +} + +/*! + \brief Get 'case sensitive search' option value. + + This method returns \c true if 'case sensitive search' control + is enabled and switched on. + + \return \c true if case sensitive search is performed + \sa isRegExpSearch(), isSearchWrapped(), setControls() +*/ +bool QtxSearchTool::isCaseSensitive() const +{ + return myControls & Case && myIsCaseSens->isChecked(); +} + +/*! + \brief Get 'regular expression search' option value. + + This method returns \c true if 'regular expression search' control + is enabled and switched on. + + \return \c true if regular expression search is performed + \sa isCaseSensitive(), isSearchWrapped(), setControls() +*/ +bool QtxSearchTool::isRegExpSearch() const +{ + return myControls & RegExp && myIsRegExp->isChecked(); +} + +/*! + \brief Get 'search wrapping' option value. + + This method returns \c true if 'wrap search' control + is enabled and switched on. + + \return \c true if search wrapping is enabled + \sa isCaseSensitive(), isRegExpSearch(), setControls() +*/ +bool QtxSearchTool::isSearchWrapped() const +{ + return myControls & Wrap && myWrap->isChecked(); +} + +/*! + \brief Customize event handling. + \param e event + \return \c true if event has been handled +*/ +bool QtxSearchTool::event( QEvent* e ) +{ + if ( e->type() == QEvent::EnabledChange ) + { + updateShortcuts(); + } + else if ( e->type() == QEvent::KeyPress ) + { + QKeyEvent* ke = (QKeyEvent*)e; + if ( ke->key() == Qt::Key_Escape ) + hide(); + } + else if ( e->type() == QEvent::Hide && myWatched ) + { + myWatched->setFocus(); + } + return QFrame::event( e ); +} + +/*! + \brief Filter events from the watched widget. + \param o object + \param e event + \return \c true if further event processing should be stopped +*/ +bool QtxSearchTool::eventFilter( QObject* o, QEvent* e ) +{ + if ( myWatched && o == myWatched && e->type() == QEvent::KeyPress ) + { + QKeyEvent* ke = (QKeyEvent*)e; + int key = ke->key(); + QString ttf = myData->text(); + QString text = ke->text(); + + if ( isVisible() ) + { + switch ( key ) + { + case Qt::Key_Escape: + hide(); + return true; + case Qt::Key_Backspace: + ttf.chop( 1 ); + break; + case Qt::Key_Return: + case Qt::Key_Enter: + findNext(); + return true; + default: + if ( text.isEmpty() || !text[0].isPrint() ) + return QFrame::eventFilter( o, e ); + ttf += text; + } + } + else + { + if ( text.isEmpty() || ! isEnabled() || !text[0].isPrint() ) + return QFrame::eventFilter( o, e ); + + if ( text.startsWith( '/' ) && myActivators & SlashKey ) + { + myData->clear(); + find(); + return true; + } + else if ( !( myActivators & PrintKey ) ) + { + return QFrame::eventFilter( o, e ); + } + + ttf = text; + show(); + } + myData->setText( ttf ); + find( ttf ); + } + if ( o == myData ) + { + if ( e->type() == QEvent::FocusIn && myAutoHideTimer->isActive() ) + myAutoHideTimer->stop(); + } + return QFrame::eventFilter( o, e ); +} + +/*! + \brief Activate search tool. + + Call this method to start new search. +*/ +void QtxSearchTool::find() +{ + show(); + + myData->setFocus( Qt::ShortcutFocusReason ); + myData->selectAll(); + myAutoHideTimer->stop(); +} + +/*! + \brief Find next appropriate data. + + Call this method to repeat the search in the forward direction. +*/ +void QtxSearchTool::findNext() +{ + find( myData->text(), fNext ); +} + +/*! + \brief Find previous appropriate data. + + Call this method to repeat the search in the backward direction. +*/ +void QtxSearchTool::findPrevious() +{ + find( myData->text(), fPrevious ); +} + +/*! + \brief Find first appropriate data. + + Call this method to find the very first appropriate data. +*/ +void QtxSearchTool::findFirst() +{ + find( myData->text(), fFirst ); +} + +/*! + \brief Find last appropriate data. + + Call this method to find the very last appropriate data. +*/ +void QtxSearchTool::findLast() +{ + find( myData->text(), fLast ); +} + +/*! + \brief Perform search. + \internal + \param what text to be searched + \param where search flags +*/ +void QtxSearchTool::find( const QString& what, int where ) +{ + if ( !isVisible() ) + show(); + + QPalette p = myData->palette(); + p.setColor( QPalette::Active, + QPalette::Base, + QApplication::palette( myData ).color( QPalette::Active, + QPalette::Base ) ); + + bool found = true; + if ( mySearcher && !what.isEmpty() ) + { + switch( where ) + { + case fNext: + found = mySearcher->findNext( what, this ); break; + case fPrevious: + found = mySearcher->findPrevious( what, this ); break; + case fFirst: + found = mySearcher->findFirst( what, this ); break; + case fLast: + found = mySearcher->findLast( what, this ); break; + case fAny: + default: + found = mySearcher->find( what, this ); break; + } + } + + if ( !found ) + p.setColor( QPalette::Active, QPalette::Base, QColor( highlightColor ) ); + + if ( !myData->hasFocus() && myAutoHideEnabled ) + myAutoHideTimer->start(); + + myData->setPalette( p ); +} + +/*! + \brief Called when any search modifier is switched. + \internal +*/ +void QtxSearchTool::modifierSwitched() +{ + find( myData->text() ); +} + +/*! + \brief Initialize the search tool widget. + \internal +*/ +void QtxSearchTool::init() +{ + setFrameStyle( QFrame::StyledPanel | QFrame::Plain ); + QVBoxLayout* vbox = new QVBoxLayout(); + vbox->setSpacing( 0 ); + vbox->setMargin( 5 ); + setLayout( vbox ); + + myBtnWidget = new QWidget( this ); + QHBoxLayout* myBtnWidget_layout = new QHBoxLayout( myBtnWidget ); + myBtnWidget_layout->setSpacing( 0 ); + myBtnWidget_layout->setMargin( 0 ); + vbox->addWidget( myBtnWidget ); + + myModWidget = new QWidget( this ); + QHBoxLayout* myModWidget_layout = new QHBoxLayout( myModWidget ); + myModWidget_layout->setSpacing( 0 ); + myModWidget_layout->setMargin( 0 ); + vbox->addWidget( myModWidget ); + + myClose = new QToolButton( myBtnWidget ); + myClose->setIcon( QIcon( close_xpm ) ); + myClose->setAutoRaise( true ); + myBtnWidget_layout->addWidget( wrapWidget( myBtnWidget, myClose ) ); + connect( myClose, SIGNAL( clicked() ), this, SLOT( hide() ) ); + + myData = new QLineEdit( myBtnWidget ); + myData->setMinimumWidth( 50 ); + myBtnWidget_layout->addWidget( wrapWidget( myBtnWidget, myData ), 1 ); + connect( myData, SIGNAL( textChanged( const QString& ) ), this, SLOT( find( const QString& ) ) ); + connect( myData, SIGNAL( returnPressed() ), this, SLOT( findNext() ) ); + myData->installEventFilter( this ); + + myToFirst = new QToolButton( myBtnWidget ); + myToFirst->setIcon( QIcon( first_xpm ) ); + myToFirst->setAutoRaise( true ); + myBtnWidget_layout->addWidget( wrapWidget( myBtnWidget, myToFirst ), 0 ); + connect( myToFirst, SIGNAL( clicked() ), this, SLOT( findFirst() ) ); + + myPrev = new QToolButton( myBtnWidget ); + myPrev->setIcon( QIcon( prev_xpm ) ); + myPrev->setAutoRaise( true ); + myBtnWidget_layout->addWidget( wrapWidget( myBtnWidget, myPrev ), 0 ); + connect( myPrev, SIGNAL( clicked() ), this, SLOT( findPrevious() ) ); + + myNext = new QToolButton( myBtnWidget ); + myNext->setIcon( QIcon( next_xpm ) ); + myNext->setAutoRaise( true ); + myBtnWidget_layout->addWidget( wrapWidget( myBtnWidget, myNext ), 0 ); + connect( myNext, SIGNAL( clicked() ), this, SLOT( findNext() ) ); + + myToLast = new QToolButton( myBtnWidget ); + myToLast->setIcon( QIcon( last_xpm ) ); + myToLast->setAutoRaise( true ); + myBtnWidget_layout->addWidget( wrapWidget( myBtnWidget, myToLast ), 0 ); + connect( myToLast, SIGNAL( clicked() ), this, SLOT( findLast() ) ); + + myIsCaseSens = new QCheckBox( tr( "Case sensitive" ), myModWidget ); + myModWidget_layout->addWidget( wrapWidget( myBtnWidget, myIsCaseSens ) ); + connect( myIsCaseSens, SIGNAL( stateChanged( int ) ), this, SLOT( modifierSwitched() ) ); + + myIsRegExp = new QCheckBox( tr( "Regular expression" ), myModWidget ); + myModWidget_layout->addWidget( wrapWidget( myBtnWidget, myIsRegExp ) ); + connect( myIsRegExp, SIGNAL( stateChanged( int ) ), this, SLOT( modifierSwitched() ) ); + + myWrap = new QCheckBox( tr( "Wrap search" ), myModWidget ); + myModWidget_layout->addWidget( wrapWidget( myBtnWidget, myWrap ) ); + connect( myWrap, SIGNAL( stateChanged( int ) ), this, SLOT( modifierSwitched() ) ); + + setWatchedWidget( myWatched ); + + setShortcuts( QKeySequence( "Ctrl+S" ) ); + setActivators( Any ); + updateControls(); +} + +/*! + \brief Clear shortcuts. + \internal +*/ +void QtxSearchTool::clearShortcuts() +{ + ShortcutList::Iterator it; + for ( it = myShortcuts.begin(); it != myShortcuts.end(); ++it ) + { + if ( !(*it).isNull() ) + { + QShortcut* sc = (*it); + delete sc; + } + } + myShortcuts.clear(); +} + +/*! + \brief Install shortcuts. + \internal + \param accels shortcuts list +*/ +void QtxSearchTool::initShortcuts( const QList& accels ) +{ + clearShortcuts(); + + QWidget* p = myWatched ? myWatched : ( parentWidget() ? parentWidget() : this ); + QShortcut* sc; + + sc = new QShortcut( QKeySequence::Find, p ); + connect( sc, SIGNAL( activated() ), this, SLOT( find() ) ); + sc->setContext( Qt::WidgetShortcut ); + myShortcuts.append( sc ); + + sc = new QShortcut( QKeySequence::FindNext, p ); + sc->setContext( Qt::WidgetShortcut ); + connect( sc, SIGNAL( activated() ), this, SLOT( findNext() ) ); + myShortcuts.append( sc ); + + sc = new QShortcut( QKeySequence::FindPrevious, p ); + sc->setContext( Qt::WidgetShortcut ); + connect( sc, SIGNAL( activated() ), this, SLOT( findPrevious() ) ); + myShortcuts.append( sc ); + + QList::ConstIterator it; + for ( it = accels.begin(); it != accels.end(); ++it ) + { + sc = new QShortcut( *it, p ); + sc->setContext( Qt::WidgetShortcut ); + connect( sc, SIGNAL( activated() ), this, SLOT( find() ) ); + myShortcuts.append( sc ); + } + + myAutoHideTimer = new QTimer( this ); + myAutoHideTimer->setInterval( 10000 ); + myAutoHideTimer->setSingleShot( true ); + connect( myAutoHideTimer, SIGNAL( timeout() ), this, SLOT( hide() ) ); + + updateShortcuts(); + + hide(); +} + +/*! + \brief Update shortcuts state. + \internal +*/ +void QtxSearchTool::updateShortcuts() +{ + int i; + ShortcutList::Iterator it; + for ( it = myShortcuts.begin(), i = 0; it != myShortcuts.end(); ++it, i++ ) + { + (*it)->setEnabled( isEnabled() && ( i < 3 && myActivators & StandardKey || + i > 2 && myActivators & HotKey ) ); + } +} + +/*! + \brief Update controls state. + \internal +*/ +void QtxSearchTool::updateControls() +{ + myData->parentWidget()->setVisible( myControls & Search ); + myNext->parentWidget()->setVisible( myControls & Next ); + myPrev->parentWidget()->setVisible( myControls & Prev ); + myToFirst->parentWidget()->setVisible( myControls & First ); + myToLast->parentWidget()->setVisible( myControls & Last ); + myClose->parentWidget()->setVisible( myControls & Close ); + myIsCaseSens->parentWidget()->setVisible( myControls & Case ); + myIsRegExp->parentWidget()->setVisible( myControls & RegExp ); + myWrap->parentWidget()->setVisible( myControls & Wrap ); + + myBtnWidget->setVisible( myControls & Standard ); + myModWidget->setVisible( myControls & Modifiers ); +} + +/*! + \class QtxSearchTool::Searcher + \brief Generic searcher class. + + Searcher is generic class which is used by the search tool to perform + widget-dependant search. + + To implement a searcher for some widget, just inherit from QtxSearchTool::Searcher + and override pure virtual methods find(), findNext(), findPrevious(), + findFirst() and findLast() +*/ + +/*! + \brief Constructor. +*/ +QtxSearchTool::Searcher::Searcher() +{ +} + +/*! + \brief Destructor. +*/ +QtxSearchTool::Searcher::~Searcher() +{ +} + +/*! + \fn QtxSearchTool::Searcher::find(const QString& text, QtxSearchTool* st) + \brief Start new search. + \param text text to be found + \param st search tool widget + \sa findNext(), findPrevious(), findFirst(), findLast() +*/ + +/*! + \fn QtxSearchTool::Searcher::findNext(const QString& text, QtxSearchTool* st) + \brief Search next appropriate item. + \param text text to be found + \param st search tool widget + \sa find(), findPrevious(), findFirst(), findLast() +*/ + +/*! + \fn QtxSearchTool::Searcher::findPrevious(const QString& text, QtxSearchTool* st) + \brief Search previous appropriate item. + \param text text to be found + \param st search tool widget + \sa find(), findNext(), findFirst(), findLast() +*/ + +/*! + \fn QtxSearchTool::Searcher::findFirst(const QString& text, QtxSearchTool* st) + \brief Search first appropriate item. + \param text text to be found + \param st search tool widget + \sa find(), findNext(), findPrevious(), findLast() +*/ + +/*! + \fn QtxSearchTool::Searcher::findLast(const QString& text, QtxSearchTool* st) + \brief Search last appropriate item. + \param text text to be found + \param st search tool widget + \sa find(), findNext(), findPrevious(), findFirst() +*/ + +/*! + \class QtxTreeViewSearcher + \brief A QTreeView class based searcher. + + The class QtxTreeViewSearcher can be used to find the items in the + QTreeView widget. + + The column for which data should be searched can be get/set with the + searchColumn(), setSearchColumn() methods. + By default, column 0 is used. +*/ + +/*! + \brief Constructor. + \param view tree view widget + \param col column for which search to be performed (0 by default) + \sa setSearchColumn() +*/ +QtxTreeViewSearcher::QtxTreeViewSearcher( QTreeView* view, int col ) + : myView( view ), myColumn( col ) +{ +} + +/*! + \brief Destructor. +*/ +QtxTreeViewSearcher::~QtxTreeViewSearcher() +{ +} + +/*! + \brief Get column for which search is performed. + \return column number + \sa setSearchColumn() +*/ +int QtxTreeViewSearcher::searchColumn() const +{ + return myColumn; +} + +/*! + \brief Set column for which search should be performed. + \param column column number + \sa searchColumn() +*/ +void QtxTreeViewSearcher::setSearchColumn( int column ) +{ + myColumn = column; +} + +/*! + \brief Start new search. + \param text text to be found + \param st search tool widget + \sa findNext(), findPrevious(), findFirst(), findLast() +*/ +bool QtxTreeViewSearcher::find( const QString& text, QtxSearchTool* st ) +{ + if ( !myView ) + return false; + + const QModelIndexList& l = myView->selectionModel() ? + myView->selectionModel()->selectedIndexes() : QModelIndexList(); + + QModelIndex current; + if ( l.count() > 0 ) + current = l.first(); + + bool wrapSearch = st->isSearchWrapped(); + + QModelIndexList found = findItems( text, st ); + + if ( found.count() > 0 ) + { + if ( !current.isValid() ) + { + showItem( found.first() ); + return true; + } + + if ( found.contains( current ) ) + { + showItem( current ); + return true; + } + + QModelIndex next = findNearest( current, found, true ); + if ( next.isValid() ) + { + showItem( next ); + return true; + } + + if ( wrapSearch ) + { + showItem( found.first() ); + return true; + } + } + + return false; +} + +/*! + \brief Search next appropriate item. + \param text text to be found + \param st search tool widget + \sa find(), findPrevious(), findFirst(), findLast() +*/ +bool QtxTreeViewSearcher::findNext( const QString& text, QtxSearchTool* st ) +{ + if ( !myView ) + return false; + + const QModelIndexList& l = myView->selectionModel() ? + myView->selectionModel()->selectedIndexes() : QModelIndexList(); + + QModelIndex current; + if ( l.count() > 0 ) + current = l.first(); + else if ( myIndex.isValid() ) + current = myIndex; + + bool wrapSearch = st->isSearchWrapped(); + + QModelIndexList found = findItems( text, st ); + + if ( found.count() > 0 ) + { + if ( !current.isValid() ) + { + showItem( found.first() ); + return true; + } + + QModelIndex next = findNearest( current, found, true ); + if ( next.isValid() ) + { + showItem( next ); + return true; + } + + if ( wrapSearch ) + { + showItem( found.first() ); + return true; + } + } + + return false; +} + +/*! + \brief Search previous appropriate item. + \param text text to be found + \param st search tool widget + \sa find(), findNext(), findFirst(), findLast() +*/ +bool QtxTreeViewSearcher::findPrevious( const QString& text, QtxSearchTool* st ) +{ + if ( !myView ) + return false; + + const QModelIndexList& l = myView->selectionModel() ? + myView->selectionModel()->selectedIndexes() : QModelIndexList(); + + QModelIndex current; + if ( l.count() > 0 ) + current = l.first(); + else if ( myIndex.isValid() ) + current = myIndex; + + bool wrapSearch = st->isSearchWrapped(); + + QModelIndexList found = findItems( text, st ); + + if ( found.count() > 0 ) + { + if ( !current.isValid() ) + { + showItem( found.first() ); + return true; + } + + QModelIndex next = findNearest( current, found, false ); + if ( next.isValid() ) + { + showItem( next ); + return true; + } + + if ( wrapSearch ) + { + showItem( found.last() ); + return true; + } + } + + return false; +} + +/*! + \brief Search first appropriate item. + \param text text to be found + \param st search tool widget + \sa find(), findNext(), findPrevious(), findLast() +*/ +bool QtxTreeViewSearcher::findFirst( const QString& text, QtxSearchTool* st ) +{ + QModelIndexList found = findItems( text, st ); + + if ( found.count() > 0 ) + { + showItem( found.first() ); + return true; + } + + return false; +} + +/*! + \brief Search last appropriate item. + \param text text to be found + \param st search tool widget + \sa find(), findNext(), findPrevious(), findFirst() +*/ +bool QtxTreeViewSearcher::findLast( const QString& text, QtxSearchTool* st ) +{ + QModelIndexList found = findItems( text, st ); + + if ( found.count() > 0 ) + { + showItem( found.last() ); + return true; + } + + return false; +} + +/*! + \brief Find all appropriate items. + \internal + \param text text to be found + \param st search tool widget +*/ +QModelIndexList QtxTreeViewSearcher::findItems( const QString& text, QtxSearchTool* st ) +{ + Qt::MatchFlags fl = Qt::MatchRecursive; + + if ( st->isCaseSensitive() ) + fl = fl | Qt::MatchCaseSensitive; + fl = fl | ( st->isRegExpSearch() ? Qt::MatchRegExp : Qt::MatchContains ); + + if ( myView->model() ) + return myView->model()->match( myView->model()->index( 0, myColumn ), + Qt::DisplayRole, + text, -1, fl ); + return QModelIndexList(); +} + +/*! + \brief Find model index from the list nearest to the specified index. + \internal + \param index model index for which a nearest item is searched + \param lst list of model indices + \param direction if \c true find next appropriate item, otherwise find privious + appropriate item +*/ +QModelIndex QtxTreeViewSearcher::findNearest( const QModelIndex& index, + const QModelIndexList& lst, + bool direction ) +{ + if ( direction ) + { + QListIterator it( lst ); + while ( it.hasNext() ) + { + QModelIndex found = it.next(); + if ( compareIndices( found, index ) > 0 ) + return found; + } + } + else + { + QListIterator it( lst ); + it.toBack(); + while ( it.hasPrevious() ) + { + QModelIndex found = it.previous(); + if ( compareIndices( found, index ) < 0 ) + return found; + } + } + return QModelIndex(); +} + +/*! + \brief Ensure the found item to become visible and selected. + \internal + \param index item to be shown +*/ +void QtxTreeViewSearcher::showItem( const QModelIndex& index ) +{ + if ( myView && index.isValid() && myView->selectionModel() ) + { + QItemSelectionModel::SelectionFlags f = + QItemSelectionModel::Select | QItemSelectionModel::Rows | QItemSelectionModel::Clear; + myView->selectionModel()->select( index, f ); + myView->scrollTo( index ); + myIndex = index; + } +} + +/*! + \brief Get unique item ID. + \internal + \param index model index + \return item ID +*/ +QString QtxTreeViewSearcher::getId( const QModelIndex& index ) +{ + QStringList ids; + QModelIndex p = index; + while ( p.isValid() ) + { + ids.prepend( QString::number( p.row() ) ); + p = p.parent(); + } + ids.prepend( "0" ); + return ids.join( ":" ); +} + +/*! + \brief Compare items. + \internal + \param left first model index to be compared + \param right last model index to be compared + \return 0 if items are equal, negative value if left item is less than right one + and positive value otherwise +*/ +int QtxTreeViewSearcher::compareIndices( const QModelIndex& left, + const QModelIndex& right ) +{ + QString leftId = getId( left ); + QString rightId = getId( right ); + + QStringList idsLeft = leftId.split( ":", QString::SkipEmptyParts ); + QStringList idsRight = rightId.split( ":", QString::SkipEmptyParts ); + + for ( int i = 0; i < idsLeft.count() && i < idsRight.count(); i++ ) + { + int lid = idsLeft[i].toInt(); + int rid = idsRight[i].toInt(); + if ( lid != rid ) + return lid - rid; + } + return idsLeft.count() < idsRight.count() ? -1 : + ( idsLeft.count() == idsRight.count() ? 0 : 1 ); +} diff --git a/src/Qtx/QtxSearchTool.h b/src/Qtx/QtxSearchTool.h new file mode 100644 index 000000000..c10562589 --- /dev/null +++ b/src/Qtx/QtxSearchTool.h @@ -0,0 +1,201 @@ +// Copyright (C) 2005 OPEN CASCADE, CEA/DEN, EDF R&D, PRINCIPIA R&D +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License. +// +// This library is distributed in the hope that it will be useful +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com +// +// File : QtxSearchTool.h +// Author : Vadim SANDLER, Open CASCADE S.A.S. (vadim.sandler@opencascade.com) +// + +#ifndef QTXSEARCHTOOL_H +#define QTXSEARCHTOOL_H + +#include "Qtx.h" +#include +#include +#include +#include +#include + +class QCheckBox; +class QLineEdit; +class QShortcut; +class QTimer; +class QToolButton; +class QTreeView; + +class QTX_EXPORT QtxSearchTool : public QFrame +{ + Q_OBJECT + + //! Find operation type + enum { + fAny, //!< find any appropriate + fNext, //!< find next appropriate + fPrevious, //!< find previous appropriate + fFirst, //!< find first appropriate + fLast //!< find last appropriate + }; + +public: + class Searcher; + + //! Search tool controls + typedef enum { + Search = 0x00000001, //!< line edit field for searched text + Next = 0x00000002, //!< 'find next' button + Prev = 0x00000004, //!< 'find previous' button + First = 0x00000008, //!< 'find first' button + Last = 0x00000010, //!< 'find last' button + Close = 0x00000020, //!< 'close' button + Case = 0x00000040, //!< 'case sensitive search' check box + RegExp = 0x00000080, //!< 'regular expression' check box + Wrap = 0x00000100, //!< 'wrap search' check box + Basic = Search | Next | Prev | Close, //!< basic controls: text field, "next", "prev" and "close" buttons + Standard = Basic | First | Last, //!< standard controls: same as Basic plus "first" and "last" buttons + Modifiers = Case | RegExp | Wrap, //!< search modifiers check boxes + All = Standard | Modifiers //!< all controls + } Controls; + + typedef enum { + None = 0x00000000, //!< search tool can be activated programmatically only + HotKey = 0x00000001, //!< search tool is activated by hot key + SlashKey = 0x00000002, //!< search tool is activated by splash key ( / ) + StandardKey = 0x00000004, //!< search tool is activated by standard key combination (e.g. F3) + PrintKey = 0x00000008, //!< search tool is activated when user types any print key + Any = HotKey | SlashKey | StandardKey | PrintKey //!< search tool is activated by any of above mentioned ways + } Activator; + + QtxSearchTool( QWidget*, QWidget* = 0, int = All ); + QtxSearchTool( QWidget*, int = All ); + virtual ~QtxSearchTool(); + + QWidget* watchedWidget() const; + void setWatchedWidget( QWidget* ); + + Searcher* searcher() const; + void setSearcher( Searcher* ); + + int activators() const; + void setActivators( const int ); + + int controls() const; + void setControls( const int ); + + QList shortcuts() const; + void setShortcuts( const QKeySequence& ); + void setShortcuts( const QList& ); + + int addCustomWidget( QWidget*, int = -1 ); + QWidget* customWidget( int ) const; + int customWidgetId( QWidget* ) const; + + bool isAutoHideEnabled() const; + void enableAutoHide( bool ); + + bool isCaseSensitive() const; + bool isRegExpSearch() const; + bool isSearchWrapped() const; + + virtual bool event( QEvent* ); + virtual bool eventFilter( QObject*, QEvent* ); + +public slots: + virtual void find(); + virtual void findNext(); + virtual void findPrevious(); + virtual void findFirst(); + virtual void findLast(); + +private slots: + void find( const QString&, int = fAny ); + void modifierSwitched(); + +private: + void init(); + void clearShortcuts(); + void initShortcuts( const QList& ); + void updateShortcuts(); + void updateControls(); + +private: + typedef QPointer ShortcutPtr; + typedef QList ShortcutList; + +private: + QWidget* myBtnWidget; + QWidget* myModWidget; + QLineEdit* myData; + QToolButton* myToFirst; + QToolButton* myToLast; + QToolButton* myNext; + QToolButton* myPrev; + QToolButton* myClose; + QCheckBox* myIsCaseSens; + QCheckBox* myIsRegExp; + QCheckBox* myWrap; + QWidget* myWatched; + Searcher* mySearcher; + int myControls; + int myActivators; + ShortcutList myShortcuts; + QTimer* myAutoHideTimer; + bool myAutoHideEnabled; + QMap myWidgets; +}; + +class QTX_EXPORT QtxSearchTool::Searcher +{ +public: + Searcher(); + virtual ~Searcher(); + + virtual bool find( const QString&, QtxSearchTool* ) = 0; + virtual bool findNext( const QString&, QtxSearchTool* ) = 0; + virtual bool findPrevious( const QString&, QtxSearchTool* ) = 0; + virtual bool findFirst( const QString&, QtxSearchTool* ) = 0; + virtual bool findLast( const QString&, QtxSearchTool* ) = 0; +}; + +class QTX_EXPORT QtxTreeViewSearcher : public QtxSearchTool::Searcher +{ +public: + QtxTreeViewSearcher( QTreeView*, int = 0 ); + virtual ~QtxTreeViewSearcher(); + + int searchColumn() const; + void setSearchColumn( int ); + + virtual bool find( const QString&, QtxSearchTool* ); + virtual bool findNext( const QString&, QtxSearchTool* ); + virtual bool findPrevious( const QString&, QtxSearchTool* ); + virtual bool findFirst( const QString&, QtxSearchTool* ); + virtual bool findLast( const QString&, QtxSearchTool* ); + +private: + QModelIndexList findItems( const QString&, QtxSearchTool* ); + QModelIndex findNearest( const QModelIndex&, const QModelIndexList&, bool ); + void showItem( const QModelIndex& ); + QString getId( const QModelIndex& ); + int compareIndices( const QModelIndex&, const QModelIndex& ); + +private: + QTreeView* myView; + int myColumn; + QPersistentModelIndex myIndex; +}; + +#endif // QTXSEARCHTOOL_H -- 2.39.2