1 // Copyright (C) 2007-2015 CEA/DEN, EDF R&D, OPEN CASCADE
3 // This library is free software; you can redistribute it and/or
4 // modify it under the terms of the GNU Lesser General Public
5 // License as published by the Free Software Foundation; either
6 // version 2.1 of the License, or (at your option) any later version.
8 // This library is distributed in the hope that it will be useful,
9 // but WITHOUT ANY WARRANTY; without even the implied warranty of
10 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 // Lesser General Public License for more details.
13 // You should have received a copy of the GNU Lesser General Public
14 // License along with this library; if not, write to the Free Software
15 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17 // See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
20 // File : QtxSearchTool.cxx
21 // Author : Vadim SANDLER, Open CASCADE S.A.S. (vadim.sandler@opencascade.com)
23 #include "QtxSearchTool.h"
25 #include <QApplication>
28 #include <QHBoxLayout>
31 #include <QPersistentModelIndex>
34 #include <QToolButton>
37 const char* const first_xpm[] = {
70 const char* const last_xpm[] = {
103 const char* const prev_xpm[] = {
134 const char* const next_xpm[] = {
165 const char* const close_xpm[] = {
192 const char* highlightColor = "#FF6666";
193 const int DefaultAutoHideDelay = 10000;
196 \brief Wrap specified widget by another dumb widget.
198 \param parent widget to be used as parent for the dumb widget
199 \param w widget to be wrapped
200 \return wrapper widget
202 static QWidget* wrapWidget( QWidget* parent, QWidget* w )
204 QWidget* wrapper = new QWidget( parent );
205 w->setParent( wrapper );
206 QHBoxLayout* l = new QHBoxLayout( wrapper );
215 \brief Context search tool.
217 The QtxSearchTool class implements a specific context search tool widget
218 which can be embedded into any GUI element.
219 It represents the usual dialog panel with the line edit box used to enter
220 text to be searched and set of buttons, like "Find Next", "Find Previous", etc.
221 In addition, the search modifiers like "Case sensitive search", "Wrap search"
224 Actually the class QtxSearchTool does not perform a serach itself - it is only
225 the generic widget. To use this widget, you have to install a searcher depending
226 on your needs. This should be a successor of the class QtxSearchTool::Searcher -
227 it is the class which will perform actual search of the data in your widget
228 according to the widget type.
230 For the current moment, only one standard searcher is implemented: it is the
231 class QtxTreeViewSearcher, which can be used to search the text data in the
232 tree view widget (QTreeView). See this class for more details.
234 The usual usage of the searcher widget is the following:
236 QTreeView* tree = new QTreeView( this );
237 QtxSearchTool* st = new QtxSearchTool( this, tree, QtxSearchTool::Standard );
238 st->setActivators( QtxSearchTool::SlashKey | QtxSearchTool::StandardKey );
239 st->setSearcher( new QtxTreeViewSearcher( tree ) );
242 Note, that controls to be displayed by the search tool widget are passed as
243 ORed flags to the widget's constructor. At any time, the available controls
244 can be set/get with setControls() and controls() methods.
245 By default, all widgets are displayed (see also QtxSearchTool::Controls
248 The class QtxSearchTool also provides a way to add custom widgets -
249 these widgets are displayed at the bottom area of the tool box. Use
250 method addCustomWidget() to add custom widget to the search tool box.
251 Your searcher class can use custom widgets to perform advanced search.
253 The class supports different ways of the activation, all of them can be
254 switched on/off with setActivators() method. See QtxSearchTool::Activator
255 enumeration for more details.
256 By default, all methods are switched on: default hot key is <Ctrl><S> and
257 standard key bindings are the platform dependent keyboard shortcuts.
258 Shortcuts can be assigned with the setShortcuts() methods.
264 Creates a search tool widget with parent widget \a parent
265 and watched widget \a watched. The controls to be displayed can be passed
266 via \a controls parameter. By default, all controls are displayed.
268 \param parent parent widget
269 \param watched watched widget
270 \param controls ORed controls flags (QtxSearchTool::Controls)
271 \sa setWatchedWidget(), setControls()
273 QtxSearchTool::QtxSearchTool( QWidget* parent, QWidget* watched, int controls, Qt::Orientation orientation )
275 myWatched( watched ? watched : parent ),
277 myControls( controls ),
278 myActivators( None ),
279 myAutoHideTimer( 0 ),
280 myAutoHideEnabled( true )
288 Creates a search tool widget with parent widget \a parent.
289 Parameter \a parent is also used to set watched widget.
290 The controls to be displayed can be passed via \a controls parameter.
291 By default, all controls are displayed.
293 \param parent parent widget
294 \param controls ORed controls flags (QtxSearchTool::Controls)
295 \sa setWatchedWidget(), setControls()
297 QtxSearchTool::QtxSearchTool( QWidget* parent, int controls, Qt::Orientation orientation )
301 myControls( controls ),
302 myActivators( None ),
303 myAutoHideTimer( 0 ),
304 myAutoHideEnabled( true )
312 QtxSearchTool::~QtxSearchTool()
320 \brief Get watched widget.
321 \return currently used watched widget
322 \sa setWatchedWidget(), activators(), setActivators()
324 QWidget* QtxSearchTool::watchedWidget() const
330 \brief Set watched widget.
332 Watched widget is that one for which shortcut bindings are set.
333 When this widget has focus and any hot key binbding is pressed by the user,
334 the search tool box is activated. The same occurs if slash key is pressed and
335 QtxSearchTool::SlashKey activator is enabled. If the QtxSearchTool::PrintKey
336 activator is enabled, the tool box is activated if any printed key is pressed
339 \param watched a widget to be watched by the search tool
340 \sa watchedWidget(), activators(), setActivators()
342 void QtxSearchTool::setWatchedWidget( QWidget* watched )
346 myWatched->removeEventFilter( this );
351 initShortcuts( shortcuts() );
355 myWatched->installEventFilter( this );
360 \brief Get current searcher.
361 \return currently set searcher (QtxSearchTool::Searcher)
364 QtxSearchTool::Searcher* QtxSearchTool::searcher() const
370 \brief Assign searcher.
372 Note: the search tool takes ownership to the searcher
373 and destroys it when deleted.
375 \param s searcher to be used (QtxSearchTool::Searcher)
378 void QtxSearchTool::setSearcher( QtxSearchTool::Searcher* s )
386 \brief Get activators.
387 \return activators currently enabled (ORed QtxSearchTool::Activator flags)
390 int QtxSearchTool::activators() const
396 \brief Set activators.
397 \param flags set activators to be used (ORed QtxSearchTool::Activator flags)
400 void QtxSearchTool::setActivators( const int flags )
402 myActivators = flags;
408 \return controls currently enabled (ORed QtxSearchTool::Controls flags)
411 int QtxSearchTool::controls() const
418 \param ctrls controls to be displayed (ORed QtxSearchTool::Controls flags)
421 void QtxSearchTool::setControls( const int ctrls )
423 if ( myControls == ctrls )
430 \brief Get shortcuts.
432 Note: the standard bindings are not include to the resulting list.
434 \return list of shortcuts bindings currently set
437 QList<QKeySequence> QtxSearchTool::shortcuts() const
439 QList<QKeySequence> ks;
441 ShortcutList::ConstIterator it;
443 for ( it = myShortcuts.begin(), i = 0; it != myShortcuts.end(); ++it, i++ )
445 if ( i > 2 ) ks.append( (*it)->key() );
452 \brief Set shortcuts.
453 \param accel shortcut binding(s) to be used
456 void QtxSearchTool::setShortcuts( const QKeySequence& accel )
458 QList<QKeySequence> ks;
464 \brief Set shortcuts.
465 \param accel shortcut bindings to be used
468 void QtxSearchTool::setShortcuts( const QList<QKeySequence>& accels )
470 initShortcuts( accels );
474 \brief Add custom widget.
475 \param w custom widget to be added
476 \param id widget unique ID to be used (if < 0, automatically assigned)
477 \return widget unique ID
478 \sa customWidget(), customWidgetId()
480 int QtxSearchTool::addCustomWidget( QWidget* w, int id )
484 static int _wid = -1;
487 QMap<int, QWidget*>::ConstIterator it;
488 for ( it = myWidgets.begin(); it != myWidgets.end() && wid == -1; ++it )
490 if ( it.value() == w )
497 wid = id < 0 ? --_wid : id;
499 QBoxLayout* vbox = qobject_cast<QBoxLayout*>( layout() );
500 w->setParent( this );
501 vbox->addWidget( w );
502 myWidgets.insert( wid, w );
508 \brief Get custom widget by ID.
510 \return custom widget or 0 if not found
511 \sa addCustomWidget(), customWidgetId()
513 QWidget* QtxSearchTool::customWidget( int id ) const
516 if ( myWidgets.contains( id ) )
522 \brief Get custom widget ID.
523 \param w custom widget
524 \return custom widget ID or -1 if widget does not belong to the search tool
525 \sa addCustomWidget(), customWidget()
527 int QtxSearchTool::customWidgetId( QWidget* w ) const
530 QMap<int, QWidget*>::ConstIterator it;
531 for ( it = myWidgets.begin(); it != myWidgets.end() && wid == -1; ++it )
533 if ( it.value() == w )
540 \brief Check if auto-hide of the tool widget is enabled.
542 By default, the search tool widget is automatically hidden
543 after 10 seconds of idle (only if watched widget has input focus).
545 \return \c true if auto-hide option is set
548 bool QtxSearchTool::isAutoHideEnabled() const
550 return myAutoHideEnabled;
554 \brief Set/clear auto-hide option.
556 By default, the search tool widget is automatically hidden
557 after 10 seconds of idle (only if watched widget has input focus).
559 \param enable new option state
560 \sa isAutoHideEnabled()
562 void QtxSearchTool::enableAutoHide( bool enable )
564 if ( myAutoHideEnabled == enable ) return;
566 myAutoHideEnabled = enable;
568 if ( myAutoHideEnabled )
570 if ( isVisible() && !focused() )
571 myAutoHideTimer->start();
575 myAutoHideTimer->stop();
580 \brief Get 'case sensitive search' option value.
582 This method returns \c true if 'case sensitive search' control
583 is enabled and switched on.
585 \return \c true if case sensitive search is performed
586 \sa isRegExpSearch(), isSearchWrapped(), setControls()
587 \sa setCaseSensitive(), setRegExpSearch(), setSearchWrapped()
589 bool QtxSearchTool::isCaseSensitive() const
591 return myControls & Case && myIsCaseSens->isChecked();
595 \brief Get 'regular expression search' option value.
597 This method returns \c true if 'regular expression search' control
598 is enabled and switched on.
600 \return \c true if regular expression search is performed
601 \sa isCaseSensitive(), isSearchWrapped(), setControls()
602 \sa setCaseSensitive(), setRegExpSearch(), setSearchWrapped()
604 bool QtxSearchTool::isRegExpSearch() const
606 return myControls & RegExp && myIsRegExp->isChecked();
610 \brief Get 'search wrapping' option value.
612 This method returns \c true if 'wrap search' control
613 is enabled and switched on.
615 \return \c true if search wrapping is enabled
616 \sa isCaseSensitive(), isRegExpSearch(), setControls()
617 \sa setCaseSensitive(), setRegExpSearch(), setSearchWrapped()
619 bool QtxSearchTool::isSearchWrapped() const
621 return myControls & Wrap && myWrap->isChecked();
625 \brief Set 'case sensitive search' option value.
626 \param on new option state
627 \sa setRegExpSearch(), setSearchWrapped(), setControls()
628 \sa isCaseSensitive(), isRegExpSearch(), isSearchWrapped()
630 void QtxSearchTool::setCaseSensitive( bool on )
632 if ( myControls & Case )
633 myIsCaseSens->setChecked( on );
637 \brief Set 'regular expression search' option value.
638 \param on new option state
639 \sa setCaseSensitive(), setSearchWrapped(), setControls()
640 \sa isCaseSensitive(), isRegExpSearch(), isSearchWrapped()
642 void QtxSearchTool::setRegExpSearch( bool on )
644 if ( myControls & RegExp )
645 myIsRegExp->setChecked( on );
649 \brief Set 'search wrapping' option value.
650 \param on new option state
651 \sa setCaseSensitive(), setRegExpSearch(), setControls()
652 \sa isCaseSensitive(), isRegExpSearch(), isSearchWrapped()
654 void QtxSearchTool::setSearchWrapped( bool on )
656 if ( myControls & Wrap )
657 myWrap->setChecked( on );
661 \brief Customize event handling.
663 \return \c true if event has been handled
665 bool QtxSearchTool::event( QEvent* e )
667 if ( e->type() == QEvent::EnabledChange )
671 else if ( e->type() == QEvent::KeyPress )
673 QKeyEvent* ke = (QKeyEvent*)e;
674 if ( ke->key() == Qt::Key_Escape )
677 else if ( e->type() == QEvent::Hide && myWatched )
679 myWatched->setFocus();
681 return QFrame::event( e );
685 \brief Filter events from the watched widget.
688 \return \c true if further event processing should be stopped
690 bool QtxSearchTool::eventFilter( QObject* o, QEvent* e )
694 case QEvent::KeyPress:
695 if ( myWatched && o == myWatched )
697 QKeyEvent* ke = (QKeyEvent*)e;
699 QString ttf = myData->text();
700 QString text = ke->text();
709 case Qt::Key_Backspace:
717 if ( text.isEmpty() || !text[0].isPrint() )
718 return QFrame::eventFilter( o, e );
724 if ( text.isEmpty() || ! isEnabled() || !text[0].isPrint() )
725 return QFrame::eventFilter( o, e );
727 if ( text.startsWith( '/' ) && myActivators & SlashKey )
733 else if ( !( myActivators & PrintKey ) )
735 return QFrame::eventFilter( o, e );
741 myData->setText( ttf );
744 break; // case QEvent::KeyPress
745 case QEvent::FocusIn:
746 case QEvent::FocusOut:
749 myAutoHideTimer->stop();
751 else if ( isVisible() && isAutoHideEnabled() )
753 myAutoHideTimer->start();
759 return QFrame::eventFilter( o, e );
763 \brief Activate search tool.
765 Call this method to start new search.
767 void QtxSearchTool::find()
771 myData->setFocus( Qt::ShortcutFocusReason );
773 myAutoHideTimer->stop();
777 \brief Find next appropriate data.
779 Call this method to repeat the search in the forward direction.
781 void QtxSearchTool::findNext()
783 find( myData->text(), fNext );
787 \brief Find previous appropriate data.
789 Call this method to repeat the search in the backward direction.
791 void QtxSearchTool::findPrevious()
793 find( myData->text(), fPrevious );
797 \brief Find first appropriate data.
799 Call this method to find the very first appropriate data.
801 void QtxSearchTool::findFirst()
803 find( myData->text(), fFirst );
807 \brief Find last appropriate data.
809 Call this method to find the very last appropriate data.
811 void QtxSearchTool::findLast()
813 find( myData->text(), fLast );
817 \brief Perform search.
819 \param what text to be searched
820 \param where search flags
822 void QtxSearchTool::find( const QString& what, int where )
827 QPalette p = myData->palette();
828 p.setColor( QPalette::Active,
830 QApplication::palette( myData ).color( QPalette::Active,
834 if ( mySearcher && !what.isEmpty() )
839 found = mySearcher->findNext( what, this ); break;
841 found = mySearcher->findPrevious( what, this ); break;
843 found = mySearcher->findFirst( what, this ); break;
845 found = mySearcher->findLast( what, this ); break;
848 found = mySearcher->find( what, this ); break;
853 p.setColor( QPalette::Active, QPalette::Base, QColor( highlightColor ) );
855 if ( !focused() && myAutoHideEnabled )
856 myAutoHideTimer->start();
858 myData->setPalette( p );
862 \brief Called when any search modifier is switched.
865 void QtxSearchTool::modifierSwitched()
867 find( myData->text() );
871 \brief Initialize the search tool widget.
874 void QtxSearchTool::init( Qt::Orientation orientation )
876 setFrameStyle( QFrame::StyledPanel | QFrame::Plain );
878 myBtnWidget = new QWidget( this );
879 QHBoxLayout* myBtnWidget_layout = new QHBoxLayout( myBtnWidget );
880 myBtnWidget_layout->setSpacing( 0 );
881 myBtnWidget_layout->setMargin( 0 );
883 myModWidget = new QWidget( this );
884 QHBoxLayout* myModWidget_layout = new QHBoxLayout( myModWidget );
885 myModWidget_layout->setSpacing( 0 );
886 myModWidget_layout->setMargin( 0 );
888 myClose = new QToolButton( myBtnWidget );
889 myClose->setIcon( QIcon( close_xpm ) );
890 myClose->setAutoRaise( true );
891 myBtnWidget_layout->addWidget( wrapWidget( myBtnWidget, myClose ) );
892 connect( myClose, SIGNAL( clicked() ), this, SLOT( hide() ) );
894 myData = new QLineEdit( myBtnWidget );
895 myData->setMinimumWidth( 50 );
896 myBtnWidget_layout->addWidget( wrapWidget( myBtnWidget, myData ), 1 );
897 connect( myData, SIGNAL( textChanged( const QString& ) ), this, SLOT( find( const QString& ) ) );
898 connect( myData, SIGNAL( returnPressed() ), this, SLOT( findNext() ) );
899 myData->installEventFilter( this );
901 myToFirst = new QToolButton( myBtnWidget );
902 myToFirst->setIcon( QIcon( first_xpm ) );
903 myToFirst->setAutoRaise( true );
904 myBtnWidget_layout->addWidget( wrapWidget( myBtnWidget, myToFirst ), 0 );
905 connect( myToFirst, SIGNAL( clicked() ), this, SLOT( findFirst() ) );
906 myToFirst->installEventFilter( this );
908 myPrev = new QToolButton( myBtnWidget );
909 myPrev->setIcon( QIcon( prev_xpm ) );
910 myPrev->setAutoRaise( true );
911 myBtnWidget_layout->addWidget( wrapWidget( myBtnWidget, myPrev ), 0 );
912 connect( myPrev, SIGNAL( clicked() ), this, SLOT( findPrevious() ) );
913 myPrev->installEventFilter( this );
915 myNext = new QToolButton( myBtnWidget );
916 myNext->setIcon( QIcon( next_xpm ) );
917 myNext->setAutoRaise( true );
918 myBtnWidget_layout->addWidget( wrapWidget( myBtnWidget, myNext ), 0 );
919 connect( myNext, SIGNAL( clicked() ), this, SLOT( findNext() ) );
920 myNext->installEventFilter( this );
922 myToLast = new QToolButton( myBtnWidget );
923 myToLast->setIcon( QIcon( last_xpm ) );
924 myToLast->setAutoRaise( true );
925 myBtnWidget_layout->addWidget( wrapWidget( myBtnWidget, myToLast ), 0 );
926 connect( myToLast, SIGNAL( clicked() ), this, SLOT( findLast() ) );
927 myToLast->installEventFilter( this );
929 myIsCaseSens = new QCheckBox( tr( "Case sensitive" ), myModWidget );
930 myModWidget_layout->addWidget( wrapWidget( myBtnWidget, myIsCaseSens ) );
931 connect( myIsCaseSens, SIGNAL( stateChanged( int ) ), this, SLOT( modifierSwitched() ) );
932 myIsCaseSens->installEventFilter( this );
934 myIsRegExp = new QCheckBox( tr( "Regular expression" ), myModWidget );
935 myModWidget_layout->addWidget( wrapWidget( myBtnWidget, myIsRegExp ) );
936 connect( myIsRegExp, SIGNAL( stateChanged( int ) ), this, SLOT( modifierSwitched() ) );
937 myIsRegExp->installEventFilter( this );
939 myWrap = new QCheckBox( tr( "Wrap search" ), myModWidget );
940 myModWidget_layout->addWidget( wrapWidget( myBtnWidget, myWrap ) );
941 connect( myWrap, SIGNAL( stateChanged( int ) ), this, SLOT( modifierSwitched() ) );
942 myWrap->installEventFilter( this );
944 setWatchedWidget( myWatched );
946 setShortcuts( QKeySequence( "Ctrl+S" ) );
947 setActivators( Any );
949 QBoxLayout* box = orientation == Qt::Vertical ? (QBoxLayout*)( new QVBoxLayout ) : (QBoxLayout*)( new QHBoxLayout );
950 box->setSpacing( 0 );
952 box->addWidget( myBtnWidget );
953 box->addWidget( myModWidget );
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 );