1 // Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE
3 // Copyright (C) 2003-2007 OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN,
4 // CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS
6 // This library is free software; you can redistribute it and/or
7 // modify it under the terms of the GNU Lesser General Public
8 // License as published by the Free Software Foundation; either
9 // version 2.1 of the License.
11 // This library is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 // Lesser General Public License for more details.
16 // You should have received a copy of the GNU Lesser General Public
17 // License along with this library; if not, write to the Free Software
18 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 // See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
22 // File : QtxSearchTool.cxx
23 // Author : Vadim SANDLER, Open CASCADE S.A.S. (vadim.sandler@opencascade.com)
25 #include "QtxSearchTool.h"
27 #include <QApplication>
30 #include <QHBoxLayout>
33 #include <QPersistentModelIndex>
36 #include <QToolButton>
39 const char* const first_xpm[] = {
72 const char* const last_xpm[] = {
105 const char* const prev_xpm[] = {
136 const char* const next_xpm[] = {
167 const char* const close_xpm[] = {
194 const char* highlightColor = "#FF6666";
195 const int DefaultAutoHideDelay = 10000;
198 \brief Wrap specified widget by another dumb widget.
200 \param parent widget to be used as parent for the dumb widget
201 \param w widget to be wrapped
202 \return wrapper widget
204 static QWidget* wrapWidget( QWidget* parent, QWidget* w )
206 QWidget* wrapper = new QWidget( parent );
207 w->setParent( wrapper );
208 QHBoxLayout* l = new QHBoxLayout( wrapper );
217 \brief Context search tool.
219 The QtxSearchTool class implements a specific context search tool widget
220 which can be embedded into any GUI element.
221 It represents the usual dialog panel with the line edit box used to enter
222 text to be searched and set of buttons, like "Find Next", "Find Previous", etc.
223 In addition, the search modifiers like "Case sensitive search", "Wrap search"
226 Actually the class QtxSearchTool does not perform a serach itself - it is only
227 the generic widget. To use this widget, you have to install a searcher depending
228 on your needs. This should be a successor of the class QtxSearchTool::Searcher -
229 it is the class which will perform actual search of the data in your widget
230 according to the widget type.
232 For the current moment, only one standard searcher is implemented: it is the
233 class QtxTreeViewSearcher, which can be used to search the text data in the
234 tree view widget (QTreeView). See this class for more details.
236 The usual usage of the searcher widget is the following:
238 QTreeView* tree = new QTreeView( this );
239 QtxSearchTool* st = new QtxSearchTool( this, tree, QtxSearchTool::Standard );
240 st->setActivators( QtxSearchTool::SlashKey | QtxSearchTool::StandardKey );
241 st->setSearcher( new QtxTreeViewSearcher( tree ) );
244 Note, that controls to be displayed by the search tool widget are passed as
245 ORed flags to the widget's constructor. At any time, the available controls
246 can be set/get with setControls() and controls() methods.
247 By default, all widgets are displayed (see also QtxSearchTool::Controls
250 The class QtxSearchTool also provides a way to add custom widgets -
251 these widgets are displayed at the bottom area of the tool box. Use
252 method addCustomWidget() to add custom widget to the search tool box.
253 Your searcher class can use custom widgets to perform advanced search.
255 The class supports different ways of the activation, all of them can be
256 switched on/off with setActivators() method. See QtxSearchTool::Activator
257 enumeration for more details.
258 By default, all methods are switched on: default hot key is <Ctrl><S> and
259 standard key bindings are the platform dependent keyboard shortcuts.
260 Shortcuts can be assigned with the setShortcuts() methods.
266 Creates a search tool widget with parent widget \a parent
267 and watched widget \a watched. The controls to be displayed can be passed
268 via \a controls parameter. By default, all controls are displayed.
270 \param parent parent widget
271 \param watched watched widget
272 \param controls ORed controls flags (QtxSearchTool::Controls)
273 \sa setWatchedWidget(), setControls()
275 QtxSearchTool::QtxSearchTool( QWidget* parent, QWidget* watched, int controls )
277 myWatched( watched ? watched : parent ),
279 myControls( controls ),
280 myActivators( None ),
281 myAutoHideTimer( 0 ),
282 myAutoHideEnabled( true )
290 Creates a search tool widget with parent widget \a parent.
291 Parameter \a parent is also used to set watched widget.
292 The controls to be displayed can be passed via \a controls parameter.
293 By default, all controls are displayed.
295 \param parent parent widget
296 \param controls ORed controls flags (QtxSearchTool::Controls)
297 \sa setWatchedWidget(), setControls()
299 QtxSearchTool::QtxSearchTool( QWidget* parent, int controls )
303 myControls( controls ),
304 myActivators( None ),
305 myAutoHideTimer( 0 ),
306 myAutoHideEnabled( true )
314 QtxSearchTool::~QtxSearchTool()
322 \brief Get watched widget.
323 \return currently used watched widget
324 \sa setWatchedWidget(), activators(), setActivators()
326 QWidget* QtxSearchTool::watchedWidget() const
332 \brief Set watched widget.
334 Watched widget is that one for which shortcut bindings are set.
335 When this widget has focus and any hot key binbding is pressed by the user,
336 the search tool box is activated. The same occurs if slash key is pressed and
337 QtxSearchTool::SlashKey activator is enabled. If the QtxSearchTool::PrintKey
338 activator is enabled, the tool box is activated if any printed key is pressed
341 \param watched a widget to be watched by the search tool
342 \sa watchedWidget(), activators(), setActivators()
344 void QtxSearchTool::setWatchedWidget( QWidget* watched )
348 myWatched->removeEventFilter( this );
353 initShortcuts( shortcuts() );
357 myWatched->installEventFilter( this );
362 \brief Get current searcher.
363 \return currently set searcher (QtxSearchTool::Searcher)
366 QtxSearchTool::Searcher* QtxSearchTool::searcher() const
372 \brief Assign searcher.
374 Note: the search tool takes ownership to the searcher
375 and destroys it when deleted.
377 \param s searcher to be used (QtxSearchTool::Searcher)
380 void QtxSearchTool::setSearcher( QtxSearchTool::Searcher* s )
388 \brief Get activators.
389 \return activators currently enabled (ORed QtxSearchTool::Activator flags)
392 int QtxSearchTool::activators() const
398 \brief Set activators.
399 \param flags set activators to be used (ORed QtxSearchTool::Activator flags)
402 void QtxSearchTool::setActivators( const int flags )
404 myActivators = flags;
410 \return controls currently enabled (ORed QtxSearchTool::Controls flags)
413 int QtxSearchTool::controls() const
420 \param ctrls controls to be displayed (ORed QtxSearchTool::Controls flags)
423 void QtxSearchTool::setControls( const int ctrls )
425 if ( myControls == ctrls )
432 \brief Get shortcuts.
434 Note: the standard bindings are not include to the resulting list.
436 \return list of shortcuts bindings currently set
439 QList<QKeySequence> QtxSearchTool::shortcuts() const
441 QList<QKeySequence> ks;
443 ShortcutList::ConstIterator it;
445 for ( it = myShortcuts.begin(), i = 0; it != myShortcuts.end(); ++it, i++ )
447 if ( i > 2 ) ks.append( (*it)->key() );
454 \brief Set shortcuts.
455 \param accel shortcut binding(s) to be used
458 void QtxSearchTool::setShortcuts( const QKeySequence& accel )
460 QList<QKeySequence> ks;
466 \brief Set shortcuts.
467 \param accel shortcut bindings to be used
470 void QtxSearchTool::setShortcuts( const QList<QKeySequence>& accels )
472 initShortcuts( accels );
476 \brief Add custom widget.
477 \param w custom widget to be added
478 \param id widget unique ID to be used (if < 0, automatically assigned)
479 \return widget unique ID
480 \sa customWidget(), customWidgetId()
482 int QtxSearchTool::addCustomWidget( QWidget* w, int id )
486 static int _wid = -1;
489 QMap<int, QWidget*>::ConstIterator it;
490 for ( it = myWidgets.begin(); it != myWidgets.end() && wid == -1; ++it )
492 if ( it.value() == w )
499 wid = id < 0 ? --_wid : id;
501 QVBoxLayout* vbox = qobject_cast<QVBoxLayout*>( layout() );
502 w->setParent( this );
503 vbox->addWidget( w );
504 myWidgets.insert( wid, w );
510 \brief Get custom widget by ID.
512 \return custom widget or 0 if not found
513 \sa addCustomWidget(), customWidgetId()
515 QWidget* QtxSearchTool::customWidget( int id ) const
518 if ( myWidgets.contains( id ) )
524 \brief Get custom widget ID.
525 \param w custom widget
526 \return custom widget ID or -1 if widget does not belong to the search tool
527 \sa addCustomWidget(), customWidget()
529 int QtxSearchTool::customWidgetId( QWidget* w ) const
532 QMap<int, QWidget*>::ConstIterator it;
533 for ( it = myWidgets.begin(); it != myWidgets.end() && wid == -1; ++it )
535 if ( it.value() == w )
542 \brief Check if auto-hide of the tool widget is enabled.
544 By default, the search tool widget is automatically hidden
545 after 10 seconds of idle (only if watched widget has input focus).
547 \return \c true if auto-hide option is set
550 bool QtxSearchTool::isAutoHideEnabled() const
552 return myAutoHideEnabled;
556 \brief Set/clear auto-hide option.
558 By default, the search tool widget is automatically hidden
559 after 10 seconds of idle (only if watched widget has input focus).
561 \param enable new option state
562 \sa isAutoHideEnabled()
564 void QtxSearchTool::enableAutoHide( bool enable )
566 if ( myAutoHideEnabled == enable ) return;
568 myAutoHideEnabled = enable;
570 if ( myAutoHideEnabled )
572 if ( isVisible() && !focused() )
573 myAutoHideTimer->start();
577 myAutoHideTimer->stop();
582 \brief Get 'case sensitive search' option value.
584 This method returns \c true if 'case sensitive search' control
585 is enabled and switched on.
587 \return \c true if case sensitive search is performed
588 \sa isRegExpSearch(), isSearchWrapped(), setControls()
589 \sa setCaseSensitive(), setRegExpSearch(), setSearchWrapped()
591 bool QtxSearchTool::isCaseSensitive() const
593 return myControls & Case && myIsCaseSens->isChecked();
597 \brief Get 'regular expression search' option value.
599 This method returns \c true if 'regular expression search' control
600 is enabled and switched on.
602 \return \c true if regular expression search is performed
603 \sa isCaseSensitive(), isSearchWrapped(), setControls()
604 \sa setCaseSensitive(), setRegExpSearch(), setSearchWrapped()
606 bool QtxSearchTool::isRegExpSearch() const
608 return myControls & RegExp && myIsRegExp->isChecked();
612 \brief Get 'search wrapping' option value.
614 This method returns \c true if 'wrap search' control
615 is enabled and switched on.
617 \return \c true if search wrapping is enabled
618 \sa isCaseSensitive(), isRegExpSearch(), setControls()
619 \sa setCaseSensitive(), setRegExpSearch(), setSearchWrapped()
621 bool QtxSearchTool::isSearchWrapped() const
623 return myControls & Wrap && myWrap->isChecked();
627 \brief Set 'case sensitive search' option value.
628 \param on new option state
629 \sa setRegExpSearch(), setSearchWrapped(), setControls()
630 \sa isCaseSensitive(), isRegExpSearch(), isSearchWrapped()
632 void QtxSearchTool::setCaseSensitive( bool on )
634 if ( myControls & Case )
635 myIsCaseSens->setChecked( on );
639 \brief Set 'regular expression search' option value.
640 \param on new option state
641 \sa setCaseSensitive(), setSearchWrapped(), setControls()
642 \sa isCaseSensitive(), isRegExpSearch(), isSearchWrapped()
644 void QtxSearchTool::setRegExpSearch( bool on )
646 if ( myControls & RegExp )
647 myIsRegExp->setChecked( on );
651 \brief Set 'search wrapping' option value.
652 \param on new option state
653 \sa setCaseSensitive(), setRegExpSearch(), setControls()
654 \sa isCaseSensitive(), isRegExpSearch(), isSearchWrapped()
656 void QtxSearchTool::setSearchWrapped( bool on )
658 if ( myControls & Wrap )
659 myWrap->setChecked( on );
663 \brief Customize event handling.
665 \return \c true if event has been handled
667 bool QtxSearchTool::event( QEvent* e )
669 if ( e->type() == QEvent::EnabledChange )
673 else if ( e->type() == QEvent::KeyPress )
675 QKeyEvent* ke = (QKeyEvent*)e;
676 if ( ke->key() == Qt::Key_Escape )
679 else if ( e->type() == QEvent::Hide && myWatched )
681 myWatched->setFocus();
683 return QFrame::event( e );
687 \brief Filter events from the watched widget.
690 \return \c true if further event processing should be stopped
692 bool QtxSearchTool::eventFilter( QObject* o, QEvent* e )
696 case QEvent::KeyPress:
697 if ( myWatched && o == myWatched )
699 QKeyEvent* ke = (QKeyEvent*)e;
701 QString ttf = myData->text();
702 QString text = ke->text();
711 case Qt::Key_Backspace:
719 if ( text.isEmpty() || !text[0].isPrint() )
720 return QFrame::eventFilter( o, e );
726 if ( text.isEmpty() || ! isEnabled() || !text[0].isPrint() )
727 return QFrame::eventFilter( o, e );
729 if ( text.startsWith( '/' ) && myActivators & SlashKey )
735 else if ( !( myActivators & PrintKey ) )
737 return QFrame::eventFilter( o, e );
743 myData->setText( ttf );
746 break; // case QEvent::KeyPress
747 case QEvent::FocusIn:
748 case QEvent::FocusOut:
751 myAutoHideTimer->stop();
753 else if ( isVisible() && isAutoHideEnabled() )
755 myAutoHideTimer->start();
761 return QFrame::eventFilter( o, e );
765 \brief Activate search tool.
767 Call this method to start new search.
769 void QtxSearchTool::find()
773 myData->setFocus( Qt::ShortcutFocusReason );
775 myAutoHideTimer->stop();
779 \brief Find next appropriate data.
781 Call this method to repeat the search in the forward direction.
783 void QtxSearchTool::findNext()
785 find( myData->text(), fNext );
789 \brief Find previous appropriate data.
791 Call this method to repeat the search in the backward direction.
793 void QtxSearchTool::findPrevious()
795 find( myData->text(), fPrevious );
799 \brief Find first appropriate data.
801 Call this method to find the very first appropriate data.
803 void QtxSearchTool::findFirst()
805 find( myData->text(), fFirst );
809 \brief Find last appropriate data.
811 Call this method to find the very last appropriate data.
813 void QtxSearchTool::findLast()
815 find( myData->text(), fLast );
819 \brief Perform search.
821 \param what text to be searched
822 \param where search flags
824 void QtxSearchTool::find( const QString& what, int where )
829 QPalette p = myData->palette();
830 p.setColor( QPalette::Active,
832 QApplication::palette( myData ).color( QPalette::Active,
836 if ( mySearcher && !what.isEmpty() )
841 found = mySearcher->findNext( what, this ); break;
843 found = mySearcher->findPrevious( what, this ); break;
845 found = mySearcher->findFirst( what, this ); break;
847 found = mySearcher->findLast( what, this ); break;
850 found = mySearcher->find( what, this ); break;
855 p.setColor( QPalette::Active, QPalette::Base, QColor( highlightColor ) );
857 if ( !focused() && myAutoHideEnabled )
858 myAutoHideTimer->start();
860 myData->setPalette( p );
864 \brief Called when any search modifier is switched.
867 void QtxSearchTool::modifierSwitched()
869 find( myData->text() );
873 \brief Initialize the search tool widget.
876 void QtxSearchTool::init()
878 setFrameStyle( QFrame::StyledPanel | QFrame::Plain );
879 QVBoxLayout* vbox = new QVBoxLayout();
880 vbox->setSpacing( 0 );
881 vbox->setMargin( 5 );
884 myBtnWidget = new QWidget( this );
885 QHBoxLayout* myBtnWidget_layout = new QHBoxLayout( myBtnWidget );
886 myBtnWidget_layout->setSpacing( 0 );
887 myBtnWidget_layout->setMargin( 0 );
888 vbox->addWidget( myBtnWidget );
890 myModWidget = new QWidget( this );
891 QHBoxLayout* myModWidget_layout = new QHBoxLayout( myModWidget );
892 myModWidget_layout->setSpacing( 0 );
893 myModWidget_layout->setMargin( 0 );
894 vbox->addWidget( myModWidget );
896 myClose = new QToolButton( myBtnWidget );
897 myClose->setIcon( QIcon( close_xpm ) );
898 myClose->setAutoRaise( true );
899 myBtnWidget_layout->addWidget( wrapWidget( myBtnWidget, myClose ) );
900 connect( myClose, SIGNAL( clicked() ), this, SLOT( hide() ) );
902 myData = new QLineEdit( myBtnWidget );
903 myData->setMinimumWidth( 50 );
904 myBtnWidget_layout->addWidget( wrapWidget( myBtnWidget, myData ), 1 );
905 connect( myData, SIGNAL( textChanged( const QString& ) ), this, SLOT( find( const QString& ) ) );
906 connect( myData, SIGNAL( returnPressed() ), this, SLOT( findNext() ) );
907 myData->installEventFilter( this );
909 myToFirst = new QToolButton( myBtnWidget );
910 myToFirst->setIcon( QIcon( first_xpm ) );
911 myToFirst->setAutoRaise( true );
912 myBtnWidget_layout->addWidget( wrapWidget( myBtnWidget, myToFirst ), 0 );
913 connect( myToFirst, SIGNAL( clicked() ), this, SLOT( findFirst() ) );
914 myToFirst->installEventFilter( this );
916 myPrev = new QToolButton( myBtnWidget );
917 myPrev->setIcon( QIcon( prev_xpm ) );
918 myPrev->setAutoRaise( true );
919 myBtnWidget_layout->addWidget( wrapWidget( myBtnWidget, myPrev ), 0 );
920 connect( myPrev, SIGNAL( clicked() ), this, SLOT( findPrevious() ) );
921 myPrev->installEventFilter( this );
923 myNext = new QToolButton( myBtnWidget );
924 myNext->setIcon( QIcon( next_xpm ) );
925 myNext->setAutoRaise( true );
926 myBtnWidget_layout->addWidget( wrapWidget( myBtnWidget, myNext ), 0 );
927 connect( myNext, SIGNAL( clicked() ), this, SLOT( findNext() ) );
928 myNext->installEventFilter( this );
930 myToLast = new QToolButton( myBtnWidget );
931 myToLast->setIcon( QIcon( last_xpm ) );
932 myToLast->setAutoRaise( true );
933 myBtnWidget_layout->addWidget( wrapWidget( myBtnWidget, myToLast ), 0 );
934 connect( myToLast, SIGNAL( clicked() ), this, SLOT( findLast() ) );
935 myToLast->installEventFilter( this );
937 myIsCaseSens = new QCheckBox( tr( "Case sensitive" ), myModWidget );
938 myModWidget_layout->addWidget( wrapWidget( myBtnWidget, myIsCaseSens ) );
939 connect( myIsCaseSens, SIGNAL( stateChanged( int ) ), this, SLOT( modifierSwitched() ) );
940 myIsCaseSens->installEventFilter( this );
942 myIsRegExp = new QCheckBox( tr( "Regular expression" ), myModWidget );
943 myModWidget_layout->addWidget( wrapWidget( myBtnWidget, myIsRegExp ) );
944 connect( myIsRegExp, SIGNAL( stateChanged( int ) ), this, SLOT( modifierSwitched() ) );
945 myIsRegExp->installEventFilter( this );
947 myWrap = new QCheckBox( tr( "Wrap search" ), myModWidget );
948 myModWidget_layout->addWidget( wrapWidget( myBtnWidget, myWrap ) );
949 connect( myWrap, SIGNAL( stateChanged( int ) ), this, SLOT( modifierSwitched() ) );
950 myWrap->installEventFilter( this );
952 setWatchedWidget( myWatched );
954 setShortcuts( QKeySequence( "Ctrl+S" ) );
955 setActivators( Any );
960 \brief Check if any child widget has input focus.
962 \return \c true if any child widget has input focus
964 bool QtxSearchTool::focused() const
966 return isVisible() && isAncestorOf( QApplication::focusWidget() );
970 \brief Clear shortcuts.
973 void QtxSearchTool::clearShortcuts()
975 ShortcutList::Iterator it;
976 for ( it = myShortcuts.begin(); it != myShortcuts.end(); ++it )
978 if ( !(*it).isNull() )
980 QShortcut* sc = (*it);
988 \brief Install shortcuts.
990 \param accels shortcuts list
992 void QtxSearchTool::initShortcuts( const QList<QKeySequence>& accels )
996 QWidget* p = myWatched ? myWatched : ( parentWidget() ? parentWidget() : this );
999 sc = new QShortcut( QKeySequence::Find, p );
1000 connect( sc, SIGNAL( activated() ), this, SLOT( find() ) );
1001 sc->setContext( Qt::WidgetShortcut );
1002 myShortcuts.append( sc );
1004 sc = new QShortcut( QKeySequence::FindNext, p );
1005 sc->setContext( Qt::WidgetShortcut );
1006 connect( sc, SIGNAL( activated() ), this, SLOT( findNext() ) );
1007 myShortcuts.append( sc );
1009 sc = new QShortcut( QKeySequence::FindPrevious, p );
1010 sc->setContext( Qt::WidgetShortcut );
1011 connect( sc, SIGNAL( activated() ), this, SLOT( findPrevious() ) );
1012 myShortcuts.append( sc );
1014 QList<QKeySequence>::ConstIterator it;
1015 for ( it = accels.begin(); it != accels.end(); ++it )
1017 sc = new QShortcut( *it, p );
1018 sc->setContext( Qt::WidgetShortcut );
1019 connect( sc, SIGNAL( activated() ), this, SLOT( find() ) );
1020 myShortcuts.append( sc );
1023 myAutoHideTimer = new QTimer( this );
1024 myAutoHideTimer->setInterval( DefaultAutoHideDelay );
1025 myAutoHideTimer->setSingleShot( true );
1026 connect( myAutoHideTimer, SIGNAL( timeout() ), this, SLOT( hide() ) );
1034 \brief Update shortcuts state.
1037 void QtxSearchTool::updateShortcuts()
1040 ShortcutList::Iterator it;
1041 for ( it = myShortcuts.begin(), i = 0; it != myShortcuts.end(); ++it, i++ )
1043 (*it)->setEnabled( isEnabled() && ( i < 3 && myActivators & StandardKey ||
1044 i > 2 && myActivators & HotKey ) );
1049 \brief Update controls state.
1052 void QtxSearchTool::updateControls()
1054 myData->parentWidget()->setVisible( myControls & Search );
1055 myNext->parentWidget()->setVisible( myControls & Next );
1056 myPrev->parentWidget()->setVisible( myControls & Prev );
1057 myToFirst->parentWidget()->setVisible( myControls & First );
1058 myToLast->parentWidget()->setVisible( myControls & Last );
1059 myClose->parentWidget()->setVisible( myControls & Close );
1060 myIsCaseSens->parentWidget()->setVisible( myControls & Case );
1061 myIsRegExp->parentWidget()->setVisible( myControls & RegExp );
1062 myWrap->parentWidget()->setVisible( myControls & Wrap );
1064 myBtnWidget->setVisible( myControls & Standard );
1065 myModWidget->setVisible( myControls & Modifiers );
1069 \class QtxSearchTool::Searcher
1070 \brief Generic searcher class.
1072 Searcher is generic class which is used by the search tool to perform
1073 widget-dependant search.
1075 To implement a searcher for some widget, just inherit from QtxSearchTool::Searcher
1076 and override pure virtual methods find(), findNext(), findPrevious(),
1077 findFirst() and findLast()
1083 QtxSearchTool::Searcher::Searcher()
1090 QtxSearchTool::Searcher::~Searcher()
1095 \fn QtxSearchTool::Searcher::find(const QString& text, QtxSearchTool* st)
1096 \brief Start new search.
1097 \param text text to be found
1098 \param st search tool widget
1099 \sa findNext(), findPrevious(), findFirst(), findLast()
1103 \fn QtxSearchTool::Searcher::findNext(const QString& text, QtxSearchTool* st)
1104 \brief Search next appropriate item.
1105 \param text text to be found
1106 \param st search tool widget
1107 \sa find(), findPrevious(), findFirst(), findLast()
1111 \fn QtxSearchTool::Searcher::findPrevious(const QString& text, QtxSearchTool* st)
1112 \brief Search previous appropriate item.
1113 \param text text to be found
1114 \param st search tool widget
1115 \sa find(), findNext(), findFirst(), findLast()
1119 \fn QtxSearchTool::Searcher::findFirst(const QString& text, QtxSearchTool* st)
1120 \brief Search first appropriate item.
1121 \param text text to be found
1122 \param st search tool widget
1123 \sa find(), findNext(), findPrevious(), findLast()
1127 \fn QtxSearchTool::Searcher::findLast(const QString& text, QtxSearchTool* st)
1128 \brief Search last appropriate item.
1129 \param text text to be found
1130 \param st search tool widget
1131 \sa find(), findNext(), findPrevious(), findFirst()
1135 \class QtxTreeViewSearcher
1136 \brief A QTreeView class based searcher.
1138 The class QtxTreeViewSearcher can be used to find the items in the
1141 The column for which data should be searched can be get/set with the
1142 searchColumn(), setSearchColumn() methods.
1143 By default, column 0 is used.
1148 \param view tree view widget
1149 \param col column for which search to be performed (0 by default)
1150 \sa setSearchColumn()
1152 QtxTreeViewSearcher::QtxTreeViewSearcher( QTreeView* view, int col )
1153 : myView( view ), myColumn( col )
1160 QtxTreeViewSearcher::~QtxTreeViewSearcher()
1165 \brief Get column for which search is performed.
1166 \return column number
1167 \sa setSearchColumn()
1169 int QtxTreeViewSearcher::searchColumn() const
1175 \brief Set column for which search should be performed.
1176 \param column column number
1179 void QtxTreeViewSearcher::setSearchColumn( int column )
1185 \brief Start new search.
1186 \param text text to be found
1187 \param st search tool widget
1188 \sa findNext(), findPrevious(), findFirst(), findLast()
1190 bool QtxTreeViewSearcher::find( const QString& text, QtxSearchTool* st )
1195 const QModelIndexList& l = myView->selectionModel() ?
1196 myView->selectionModel()->selectedIndexes() : QModelIndexList();
1198 QModelIndex current;
1199 if ( l.count() > 0 )
1200 current = l.first();
1202 bool wrapSearch = st->isSearchWrapped();
1204 QModelIndexList found = findItems( text, st );
1206 if ( found.count() > 0 )
1208 if ( !current.isValid() )
1210 showItem( found.first() );
1214 if ( found.contains( current ) )
1216 showItem( current );
1220 QModelIndex next = findNearest( current, found, true );
1221 if ( next.isValid() )
1229 showItem( found.first() );
1238 \brief Search next appropriate item.
1239 \param text text to be found
1240 \param st search tool widget
1241 \sa find(), findPrevious(), findFirst(), findLast()
1243 bool QtxTreeViewSearcher::findNext( const QString& text, QtxSearchTool* st )
1248 const QModelIndexList& l = myView->selectionModel() ?
1249 myView->selectionModel()->selectedIndexes() : QModelIndexList();
1251 QModelIndex current;
1252 if ( l.count() > 0 )
1253 current = l.first();
1254 else if ( myIndex.isValid() )
1257 bool wrapSearch = st->isSearchWrapped();
1259 QModelIndexList found = findItems( text, st );
1261 if ( found.count() > 0 )
1263 if ( !current.isValid() )
1265 showItem( found.first() );
1269 QModelIndex next = findNearest( current, found, true );
1270 if ( next.isValid() )
1278 showItem( found.first() );
1287 \brief Search previous appropriate item.
1288 \param text text to be found
1289 \param st search tool widget
1290 \sa find(), findNext(), findFirst(), findLast()
1292 bool QtxTreeViewSearcher::findPrevious( const QString& text, QtxSearchTool* st )
1297 const QModelIndexList& l = myView->selectionModel() ?
1298 myView->selectionModel()->selectedIndexes() : QModelIndexList();
1300 QModelIndex current;
1301 if ( l.count() > 0 )
1302 current = l.first();
1303 else if ( myIndex.isValid() )
1306 bool wrapSearch = st->isSearchWrapped();
1308 QModelIndexList found = findItems( text, st );
1310 if ( found.count() > 0 )
1312 if ( !current.isValid() )
1314 showItem( found.first() );
1318 QModelIndex next = findNearest( current, found, false );
1319 if ( next.isValid() )
1327 showItem( found.last() );
1336 \brief Search first appropriate item.
1337 \param text text to be found
1338 \param st search tool widget
1339 \sa find(), findNext(), findPrevious(), findLast()
1341 bool QtxTreeViewSearcher::findFirst( const QString& text, QtxSearchTool* st )
1343 QModelIndexList found = findItems( text, st );
1345 if ( found.count() > 0 )
1347 showItem( found.first() );
1355 \brief Search last appropriate item.
1356 \param text text to be found
1357 \param st search tool widget
1358 \sa find(), findNext(), findPrevious(), findFirst()
1360 bool QtxTreeViewSearcher::findLast( const QString& text, QtxSearchTool* st )
1362 QModelIndexList found = findItems( text, st );
1364 if ( found.count() > 0 )
1366 showItem( found.last() );
1374 \brief Get match flags to be used by the searcher.
1375 \param st search tool widget
1377 Qt::MatchFlags QtxTreeViewSearcher::matchFlags( QtxSearchTool* st ) const
1379 Qt::MatchFlags fl = Qt::MatchRecursive;
1381 if ( st->isCaseSensitive() )
1382 fl = fl | Qt::MatchCaseSensitive;
1383 if ( st->isRegExpSearch() )
1384 fl = fl | Qt::MatchRegExp;
1386 fl = fl | Qt::MatchContains;
1392 \brief Find all appropriate items.
1394 \param text text to be found
1395 \param st search tool widget
1397 QModelIndexList QtxTreeViewSearcher::findItems( const QString& text, QtxSearchTool* st )
1401 Qt::MatchFlags fl = matchFlags( st );
1402 if ( fl & Qt::MatchRegExp ) {
1403 if ( !s.startsWith( "^" ) && !s.startsWith( ".*" ) )
1405 if ( !s.endsWith( "$" ) && !s.endsWith( ".*" ) )
1409 if ( myView->model() )
1410 return myView->model()->match( myView->model()->index( 0, myColumn ),
1413 return QModelIndexList();
1417 \brief Find model index from the list nearest to the specified index.
1419 \param index model index for which a nearest item is searched
1420 \param lst list of model indices
1421 \param direction if \c true find next appropriate item, otherwise find privious
1424 QModelIndex QtxTreeViewSearcher::findNearest( const QModelIndex& index,
1425 const QModelIndexList& lst,
1430 QListIterator<QModelIndex> it( lst );
1431 while ( it.hasNext() )
1433 QModelIndex found = it.next();
1434 if ( compareIndices( found, index ) > 0 )
1440 QListIterator<QModelIndex> it( lst );
1442 while ( it.hasPrevious() )
1444 QModelIndex found = it.previous();
1445 if ( compareIndices( found, index ) < 0 )
1449 return QModelIndex();
1453 \brief Ensure the found item to become visible and selected.
1455 \param index item to be shown
1457 void QtxTreeViewSearcher::showItem( const QModelIndex& index )
1459 if ( myView && index.isValid() && myView->selectionModel() )
1461 QItemSelectionModel::SelectionFlags f =
1462 QItemSelectionModel::Select | QItemSelectionModel::Rows | QItemSelectionModel::Clear;
1463 myView->selectionModel()->select( index, f );
1464 myView->scrollTo( index );
1470 \brief Get unique item ID.
1472 \param index model index
1475 QString QtxTreeViewSearcher::getId( const QModelIndex& index )
1478 QModelIndex p = index;
1479 while ( p.isValid() )
1481 ids.prepend( QString::number( p.row() ) );
1485 return ids.join( ":" );
1489 \brief Compare items.
1491 \param left first model index to be compared
1492 \param right last model index to be compared
1493 \return 0 if items are equal, negative value if left item is less than right one
1494 and positive value otherwise
1496 int QtxTreeViewSearcher::compareIndices( const QModelIndex& left,
1497 const QModelIndex& right )
1499 QString leftId = getId( left );
1500 QString rightId = getId( right );
1502 QStringList idsLeft = leftId.split( ":", QString::SkipEmptyParts );
1503 QStringList idsRight = rightId.split( ":", QString::SkipEmptyParts );
1505 for ( int i = 0; i < idsLeft.count() && i < idsRight.count(); i++ )
1507 int lid = idsLeft[i].toInt();
1508 int rid = idsRight[i].toInt();
1512 return idsLeft.count() < idsRight.count() ? -1 :
1513 ( idsLeft.count() == idsRight.count() ? 0 : 1 );