Back to index

salome-gui  6.5.0
QtxSearchTool.cxx
Go to the documentation of this file.
00001 // Copyright (C) 2007-2012  CEA/DEN, EDF R&D, OPEN CASCADE
00002 //
00003 // This library is free software; you can redistribute it and/or
00004 // modify it under the terms of the GNU Lesser General Public
00005 // License as published by the Free Software Foundation; either
00006 // version 2.1 of the License.
00007 //
00008 // This library is distributed in the hope that it will be useful,
00009 // but WITHOUT ANY WARRANTY; without even the implied warranty of
00010 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00011 // Lesser General Public License for more details.
00012 //
00013 // You should have received a copy of the GNU Lesser General Public
00014 // License along with this library; if not, write to the Free Software
00015 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
00016 //
00017 // See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
00018 //
00019 
00020 // File   : QtxSearchTool.cxx
00021 // Author : Vadim SANDLER, Open CASCADE S.A.S. (vadim.sandler@opencascade.com)
00022 //
00023 #include "QtxSearchTool.h"
00024 
00025 #include <QApplication>
00026 #include <QCheckBox>
00027 #include <QEvent>
00028 #include <QHBoxLayout>
00029 #include <QKeyEvent>
00030 #include <QLineEdit>
00031 #include <QPersistentModelIndex>
00032 #include <QShortcut>
00033 #include <QTimer>
00034 #include <QToolButton>
00035 #include <QTreeView>
00036 
00037 const char* const first_xpm[] = {
00038 "16 16 14 1",
00039 "       c None",
00040 ".      c #111111",
00041 "+      c #0A0A0A",
00042 "@      c #161616",
00043 "#      c #ACACAC",
00044 "$      c #FC6D6E",
00045 "%      c #FB6364",
00046 "&      c #F25B5C",
00047 "*      c #EA5859",
00048 "=      c #C1494A",
00049 "-      c #B64545",
00050 ";      c #AB4040",
00051 ">      c #A03C3C",
00052 ",      c #99393A",
00053 "           .    ",
00054 " +@+      ..#   ",
00055 " +$+#    .$.... ",
00056 " +$+#   .$$$$$.#",
00057 " +%+#  .%%%%%%.#",
00058 " +&+# .&&&&&&&.#",
00059 " +*+#.********.#",
00060 " +=+.=========.#",
00061 " +-+#.--------.#",
00062 " +;+##.;;;;;;;.#",
00063 " +>+# #.>>>>>>.#",
00064 " +,+#  #.,,,,,.#",
00065 " +,+#   #.,....#",
00066 " +,+#    #..####",
00067 " +@+#     #.#   ",
00068 "  ###      ##   "};
00069 
00070 const char* const last_xpm[] = {
00071 "16 16 14 1",
00072 "       c None",
00073 ".      c #111111",
00074 "+      c #0A0A0A",
00075 "@      c #161616",
00076 "#      c #FC6D6E",
00077 "$      c #ACACAC",
00078 "%      c #FB6364",
00079 "&      c #F25B5C",
00080 "*      c #EA5859",
00081 "=      c #C1494A",
00082 "-      c #B64545",
00083 ";      c #AB4040",
00084 ">      c #A03C3C",
00085 ",      c #99393A",
00086 "    .           ",
00087 "    ..      +@+ ",
00088 " ....#.     +#+$",
00089 " .#####.    +#+$",
00090 " .%%%%%%.   +%+$",
00091 " .&&&&&&&.  +&+$",
00092 " .********. +*+$",
00093 " .=========.+=+$",
00094 " .--------.$+-+$",
00095 " .;;;;;;;.$$+;+$",
00096 " .>>>>>>.$$ +>+$",
00097 " .,,,,,.$$  +,+$",
00098 " ....,.$$   +,+$",
00099 " $$$..$$    +,+$",
00100 "    .$$     +@+$",
00101 "    $$       $$$"};
00102 
00103 const char* const prev_xpm[] = {
00104 "16 16 12 1",
00105 "       c None",
00106 ".      c #111111",
00107 "+      c #ACACAC",
00108 "@      c #FC6D6E",
00109 "#      c #FB6364",
00110 "$      c #F25B5C",
00111 "%      c #EA5859",
00112 "&      c #C1494A",
00113 "*      c #B64545",
00114 "=      c #AB4040",
00115 "-      c #A03C3C",
00116 ";      c #99393A",
00117 "        .       ",
00118 "       ..+      ",
00119 "      .@......  ",
00120 "     .@@@@@@@.+ ",
00121 "    .########.+ ",
00122 "   .$$$$$$$$$.+ ",
00123 "  .%%%%%%%%%%.+ ",
00124 " .&&&&&&&&&&&.+ ",
00125 "  .**********.+ ",
00126 "  +.=========.+ ",
00127 "   +.--------.+ ",
00128 "    +.;;;;;;;.+ ",
00129 "     +.;......+ ",
00130 "      +..++++++ ",
00131 "       +.+      ",
00132 "        ++      "};
00133 
00134 const char* const next_xpm[] = {
00135 "16 16 12 1",
00136 "       c None",
00137 ".      c #111111",
00138 "+      c #FC6D6E",
00139 "@      c #FB6364",
00140 "#      c #F25B5C",
00141 "$      c #EA5859",
00142 "%      c #C1494A",
00143 "&      c #B64545",
00144 "*      c #ACACAC",
00145 "=      c #AB4040",
00146 "-      c #A03C3C",
00147 ";      c #99393A",
00148 "       .        ",
00149 "       ..       ",
00150 "  ......+.      ",
00151 "  .+++++++.     ",
00152 "  .@@@@@@@@.    ",
00153 "  .#########.   ",
00154 "  .$$$$$$$$$$.  ",
00155 "  .%%%%%%%%%%%. ",
00156 "  .&&&&&&&&&&.**",
00157 "  .=========.** ",
00158 "  .--------.**  ",
00159 "  .;;;;;;;.**   ",
00160 "  ......;.**    ",
00161 "   ****..**     ",
00162 "       .**      ",
00163 "       **       "};
00164 
00165 const char* const close_xpm[] = {
00166 "16 16 8 1",
00167 "       c None",
00168 ".      c #D73727",
00169 "+      c #E17765",
00170 "@      c #E7957F",
00171 "#      c #DE6F48",
00172 "$      c #DF7B4F",
00173 "%      c #FAE9E4",
00174 "&      c #FFFFFF",
00175 "                ",
00176 "  ............  ",
00177 " .+@@@@@@@@@@+. ",
00178 " .@#$$$$$$$$#@. ",
00179 " .@$$%$$$$%$$@. ",
00180 " .@$%&%$$%&%$@. ",
00181 " .@$$%&%%&%$$@. ",
00182 " .@$$$%&&%$$$@. ",
00183 " .@$$$%&&%$$$@. ",
00184 " .@$$%&%%&%$$@. ",
00185 " .@$%&%$$%&%$@. ",
00186 " .@$$%$$$$%$$@. ",
00187 " .@#$$$$$$$$#@. ",
00188 " .+@@@@@@@@@@+. ",
00189 "  ............  ",
00190 "                "};
00191 
00192 const char* highlightColor = "#FF6666";
00193 const int DefaultAutoHideDelay = 10000;
00194 
00202 static QWidget* wrapWidget( QWidget* parent, QWidget* w )
00203 {
00204   QWidget* wrapper = new QWidget( parent );
00205   w->setParent( wrapper );
00206   QHBoxLayout* l = new QHBoxLayout( wrapper );
00207   l->setMargin( 1 );
00208   l->setSpacing( 0 );
00209   l->addWidget( w );
00210   return wrapper;
00211 }
00212 
00273 QtxSearchTool::QtxSearchTool( QWidget* parent, QWidget* watched, int controls, Qt::Orientation orientation )
00274 : QFrame( parent ),
00275   myWatched( watched ? watched : parent ),
00276   mySearcher( 0 ),
00277   myControls( controls ),
00278   myActivators( None ),
00279   myAutoHideTimer( 0 ),
00280   myAutoHideEnabled( true )
00281 {
00282   init( orientation );
00283 }
00284 
00297 QtxSearchTool::QtxSearchTool( QWidget* parent, int controls, Qt::Orientation orientation )
00298 : QFrame( parent ),
00299   myWatched( parent ),
00300   mySearcher( 0 ),
00301   myControls( controls ),
00302   myActivators( None ),
00303   myAutoHideTimer( 0 ),
00304   myAutoHideEnabled( true )
00305 {
00306   init( orientation );
00307 }
00308 
00312 QtxSearchTool::~QtxSearchTool()
00313 {
00314   clearShortcuts();
00315   if ( mySearcher )
00316     delete mySearcher;
00317 }
00318 
00324 QWidget* QtxSearchTool::watchedWidget() const
00325 {
00326   return myWatched;
00327 }
00328 
00342 void QtxSearchTool::setWatchedWidget( QWidget* watched )
00343 {
00344   if ( myWatched )
00345   {
00346     myWatched->removeEventFilter( this );
00347   }
00348 
00349   myWatched = watched;
00350 
00351   initShortcuts( shortcuts() );
00352 
00353   if ( myWatched )
00354   {
00355     myWatched->installEventFilter( this );
00356   }
00357 }
00358 
00364 QtxSearchTool::Searcher* QtxSearchTool::searcher() const
00365 {
00366   return mySearcher;
00367 }
00368 
00378 void QtxSearchTool::setSearcher( QtxSearchTool::Searcher* s )
00379 {
00380   if ( mySearcher )
00381     delete mySearcher;
00382   mySearcher = s;
00383 }
00384 
00390 int QtxSearchTool::activators() const
00391 {
00392   return myActivators;
00393 }
00394 
00400 void QtxSearchTool::setActivators( const int flags )
00401 {
00402   myActivators = flags;
00403   updateShortcuts();
00404 }
00405 
00411 int QtxSearchTool::controls() const
00412 {
00413   return myControls;
00414 }
00415 
00421 void QtxSearchTool::setControls( const int ctrls )
00422 {
00423   if ( myControls == ctrls )
00424     return;
00425   myControls = ctrls;
00426   updateControls();
00427 }
00428 
00437 QList<QKeySequence> QtxSearchTool::shortcuts() const
00438 {
00439   QList<QKeySequence> ks;
00440 
00441   ShortcutList::ConstIterator it;
00442   int i;
00443   for ( it = myShortcuts.begin(), i = 0; it != myShortcuts.end(); ++it, i++ )
00444   {
00445     if ( i > 2 ) ks.append( (*it)->key() );
00446   }
00447 
00448   return ks;
00449 }
00450 
00456 void QtxSearchTool::setShortcuts( const QKeySequence& accel )
00457 {
00458   QList<QKeySequence> ks;
00459   ks << accel;
00460   setShortcuts( ks );
00461 }
00462 
00468 void QtxSearchTool::setShortcuts( const QList<QKeySequence>& accels )
00469 {
00470   initShortcuts( accels );
00471 }
00472 
00480 int QtxSearchTool::addCustomWidget( QWidget* w, int id )
00481 {
00482   if ( !w ) return -1;
00483 
00484   static int _wid = -1;
00485 
00486   int wid = -1;
00487   QMap<int, QWidget*>::ConstIterator it;
00488   for ( it = myWidgets.begin(); it != myWidgets.end() && wid == -1; ++it )
00489   {
00490     if ( it.value() == w )
00491       wid = it.key();
00492   }
00493 
00494   if ( wid != -1 )
00495     return wid;
00496 
00497   wid = id < 0 ? --_wid : id;
00498 
00499   QBoxLayout* vbox = qobject_cast<QBoxLayout*>( layout() );
00500   w->setParent( this );
00501   vbox->addWidget( w );
00502   myWidgets.insert( wid, w );
00503 
00504   return wid;
00505 }
00506 
00513 QWidget* QtxSearchTool::customWidget( int id ) const
00514 {
00515   QWidget* w = 0;
00516   if ( myWidgets.contains( id ) )
00517     w = myWidgets[ id ];
00518   return w;
00519 }
00520 
00527 int QtxSearchTool::customWidgetId( QWidget* w ) const
00528 {
00529   int wid = -1;
00530   QMap<int, QWidget*>::ConstIterator it;
00531   for ( it = myWidgets.begin(); it != myWidgets.end() && wid == -1; ++it )
00532   {
00533     if ( it.value() == w )
00534       wid = it.key();
00535   }
00536   return wid;
00537 }
00538 
00548 bool QtxSearchTool::isAutoHideEnabled() const
00549 {
00550   return myAutoHideEnabled;
00551 }
00552 
00562 void QtxSearchTool::enableAutoHide( bool enable )
00563 {
00564   if ( myAutoHideEnabled == enable ) return;
00565 
00566   myAutoHideEnabled = enable;
00567 
00568   if ( myAutoHideEnabled )
00569   {
00570     if ( isVisible() && !focused() )
00571       myAutoHideTimer->start();
00572   }
00573   else
00574   {
00575     myAutoHideTimer->stop();
00576   }
00577 }
00578 
00589 bool QtxSearchTool::isCaseSensitive() const
00590 {
00591   return myControls & Case && myIsCaseSens->isChecked();
00592 }
00593 
00604 bool QtxSearchTool::isRegExpSearch() const
00605 {
00606   return myControls & RegExp && myIsRegExp->isChecked();
00607 }
00608 
00619 bool QtxSearchTool::isSearchWrapped() const
00620 {
00621   return myControls & Wrap && myWrap->isChecked();
00622 }
00623 
00630 void QtxSearchTool::setCaseSensitive( bool on )
00631 {
00632   if ( myControls & Case )
00633     myIsCaseSens->setChecked( on );
00634 }
00635 
00642 void QtxSearchTool::setRegExpSearch( bool on )
00643 {
00644   if ( myControls & RegExp )
00645     myIsRegExp->setChecked( on );
00646 }
00647 
00654 void QtxSearchTool::setSearchWrapped( bool on )
00655 {
00656   if ( myControls & Wrap )
00657     myWrap->setChecked( on );
00658 }
00659 
00665 bool QtxSearchTool::event( QEvent* e )
00666 {
00667   if ( e->type() == QEvent::EnabledChange )
00668   {
00669     updateShortcuts();
00670   }
00671   else if ( e->type() == QEvent::KeyPress )
00672   {
00673     QKeyEvent* ke = (QKeyEvent*)e;
00674     if ( ke->key() == Qt::Key_Escape )
00675       hide();
00676   }
00677   else if ( e->type() == QEvent::Hide && myWatched )
00678   {
00679     myWatched->setFocus();
00680   }
00681   return QFrame::event( e );
00682 }
00683 
00690 bool QtxSearchTool::eventFilter( QObject* o, QEvent* e )
00691 {
00692   switch ( e->type() ) 
00693   {
00694   case QEvent::KeyPress:
00695     if ( myWatched && o == myWatched )
00696     {
00697       QKeyEvent* ke = (QKeyEvent*)e;
00698       int key = ke->key();
00699       QString ttf = myData->text();
00700       QString text = ke->text();
00701       
00702       if ( isVisible() )
00703       {
00704         switch ( key )
00705         {
00706         case Qt::Key_Escape:
00707           hide();
00708           return true;
00709         case Qt::Key_Backspace:
00710           ttf.chop( 1 );
00711           break;
00712         case Qt::Key_Return:
00713         case Qt::Key_Enter:
00714           findNext();
00715           return true;
00716         default:
00717           if ( text.isEmpty() || !text[0].isPrint() )
00718             return QFrame::eventFilter( o, e );
00719           ttf += text;
00720         }
00721       }
00722       else
00723       {
00724         if ( text.isEmpty() || ! isEnabled() || !text[0].isPrint() )
00725           return QFrame::eventFilter( o, e );
00726 
00727         if ( text.startsWith( '/' ) && myActivators & SlashKey )
00728         {
00729           myData->clear();
00730           find();
00731           return true;
00732         }
00733         else if ( !( myActivators & PrintKey ) )
00734         {
00735           return QFrame::eventFilter( o, e );
00736         }
00737         
00738         ttf = text;
00739         show();
00740       }
00741       myData->setText( ttf );
00742       find( ttf );
00743     }
00744     break; // case QEvent::KeyPress
00745   case QEvent::FocusIn:
00746   case QEvent::FocusOut:
00747     if ( focused() )
00748     {
00749       myAutoHideTimer->stop();
00750     }
00751     else if ( isVisible() && isAutoHideEnabled() )
00752     {
00753       myAutoHideTimer->start();
00754     }
00755     break;
00756   default:
00757     break;
00758   }
00759   return QFrame::eventFilter( o, e );
00760 }
00761 
00767 void QtxSearchTool::find()
00768 {
00769   show();
00770 
00771   myData->setFocus( Qt::ShortcutFocusReason );
00772   myData->selectAll();
00773   myAutoHideTimer->stop();
00774 }
00775 
00781 void QtxSearchTool::findNext()
00782 {
00783   find( myData->text(), fNext );
00784 }
00785 
00791 void QtxSearchTool::findPrevious()
00792 {
00793   find( myData->text(), fPrevious );
00794 }
00795 
00801 void QtxSearchTool::findFirst()
00802 {
00803   find( myData->text(), fFirst );
00804 }
00805 
00811 void QtxSearchTool::findLast()
00812 {
00813   find( myData->text(), fLast );
00814 }
00815 
00822 void QtxSearchTool::find( const QString& what, int where )
00823 {
00824   if ( !isVisible() )
00825     show();
00826 
00827   QPalette p = myData->palette();
00828   p.setColor( QPalette::Active,
00829               QPalette::Base,
00830               QApplication::palette( myData ).color( QPalette::Active,
00831                                                      QPalette::Base ) );
00832 
00833   bool found = true;
00834   if ( mySearcher && !what.isEmpty() )
00835   {
00836     switch( where )
00837     {
00838     case fNext:
00839       found = mySearcher->findNext( what, this ); break;
00840     case fPrevious:
00841       found = mySearcher->findPrevious( what, this ); break;
00842     case fFirst:
00843       found = mySearcher->findFirst( what, this ); break;
00844     case fLast:
00845       found = mySearcher->findLast( what, this ); break;
00846     case fAny:
00847     default:
00848       found = mySearcher->find( what, this ); break;
00849     }
00850   }
00851 
00852   if ( !found )
00853     p.setColor( QPalette::Active, QPalette::Base, QColor( highlightColor ) );
00854 
00855   if ( !focused() && myAutoHideEnabled )
00856     myAutoHideTimer->start();
00857 
00858   myData->setPalette( p );
00859 }
00860 
00865 void QtxSearchTool::modifierSwitched()
00866 {
00867   find( myData->text() );
00868 }
00869 
00874 void QtxSearchTool::init( Qt::Orientation orientation )
00875 {
00876   setFrameStyle( QFrame::StyledPanel | QFrame::Plain );
00877 
00878   myBtnWidget = new QWidget( this );
00879   QHBoxLayout* myBtnWidget_layout = new QHBoxLayout( myBtnWidget );
00880   myBtnWidget_layout->setSpacing( 0 );
00881   myBtnWidget_layout->setMargin( 0 );
00882 
00883   myModWidget = new QWidget( this );
00884   QHBoxLayout* myModWidget_layout = new QHBoxLayout( myModWidget );
00885   myModWidget_layout->setSpacing( 0 );
00886   myModWidget_layout->setMargin( 0 );
00887 
00888   myClose = new QToolButton( myBtnWidget );
00889   myClose->setIcon( QIcon( close_xpm ) );
00890   myClose->setAutoRaise( true );
00891   myBtnWidget_layout->addWidget( wrapWidget( myBtnWidget, myClose ) );
00892   connect( myClose, SIGNAL( clicked() ), this, SLOT( hide() ) );
00893 
00894   myData = new QLineEdit( myBtnWidget );
00895   myData->setMinimumWidth( 50 );
00896   myBtnWidget_layout->addWidget( wrapWidget( myBtnWidget, myData ), 1 );
00897   connect( myData, SIGNAL( textChanged( const QString& ) ), this, SLOT( find( const QString& ) ) );
00898   connect( myData, SIGNAL( returnPressed() ), this, SLOT( findNext() ) );
00899   myData->installEventFilter( this );
00900 
00901   myToFirst = new QToolButton( myBtnWidget );
00902   myToFirst->setIcon( QIcon( first_xpm ) );
00903   myToFirst->setAutoRaise( true );
00904   myBtnWidget_layout->addWidget( wrapWidget( myBtnWidget, myToFirst ), 0 );
00905   connect( myToFirst, SIGNAL( clicked() ), this, SLOT( findFirst() ) );
00906   myToFirst->installEventFilter( this );
00907 
00908   myPrev = new QToolButton( myBtnWidget );
00909   myPrev->setIcon( QIcon( prev_xpm ) );
00910   myPrev->setAutoRaise( true );
00911   myBtnWidget_layout->addWidget( wrapWidget( myBtnWidget, myPrev ), 0 );
00912   connect( myPrev, SIGNAL( clicked() ), this, SLOT( findPrevious() ) );
00913   myPrev->installEventFilter( this );
00914 
00915   myNext = new QToolButton( myBtnWidget );
00916   myNext->setIcon( QIcon( next_xpm ) );
00917   myNext->setAutoRaise( true );
00918   myBtnWidget_layout->addWidget( wrapWidget( myBtnWidget, myNext ), 0 );
00919   connect( myNext, SIGNAL( clicked() ), this, SLOT( findNext() ) );
00920   myNext->installEventFilter( this );
00921 
00922   myToLast = new QToolButton( myBtnWidget );
00923   myToLast->setIcon( QIcon( last_xpm ) );
00924   myToLast->setAutoRaise( true );
00925   myBtnWidget_layout->addWidget( wrapWidget( myBtnWidget, myToLast ), 0 );
00926   connect( myToLast, SIGNAL( clicked() ), this, SLOT( findLast() ) );
00927   myToLast->installEventFilter( this );
00928 
00929   myIsCaseSens = new QCheckBox( tr( "Case sensitive" ), myModWidget );
00930   myModWidget_layout->addWidget( wrapWidget( myBtnWidget, myIsCaseSens ) );
00931   connect( myIsCaseSens, SIGNAL( stateChanged( int ) ), this, SLOT( modifierSwitched() ) );
00932   myIsCaseSens->installEventFilter( this );
00933 
00934   myIsRegExp = new QCheckBox( tr( "Regular expression" ), myModWidget );
00935   myModWidget_layout->addWidget( wrapWidget( myBtnWidget, myIsRegExp ) );
00936   connect( myIsRegExp, SIGNAL( stateChanged( int ) ), this, SLOT( modifierSwitched() ) );
00937   myIsRegExp->installEventFilter( this );
00938 
00939   myWrap = new QCheckBox( tr( "Wrap search" ), myModWidget );
00940   myModWidget_layout->addWidget( wrapWidget( myBtnWidget, myWrap ) );
00941   connect( myWrap, SIGNAL( stateChanged( int ) ), this, SLOT( modifierSwitched() ) );
00942   myWrap->installEventFilter( this );
00943 
00944   setWatchedWidget( myWatched );
00945 
00946   setShortcuts( QKeySequence( "Ctrl+S" ) );
00947   setActivators( Any );
00948   
00949   QBoxLayout* box = orientation == Qt::Vertical ? (QBoxLayout*)( new QVBoxLayout ) : (QBoxLayout*)( new QHBoxLayout );
00950   box->setSpacing( 0 );
00951   box->setMargin( 5 );
00952   box->addWidget( myBtnWidget );
00953   box->addWidget( myModWidget );
00954   setLayout( box );
00955 
00956   updateControls();
00957 }
00958 
00964 bool QtxSearchTool::focused() const
00965 {
00966   return isVisible() && isAncestorOf( QApplication::focusWidget() );
00967 }
00968 
00973 void QtxSearchTool::clearShortcuts()
00974 {
00975   ShortcutList::Iterator it;
00976   for ( it = myShortcuts.begin(); it != myShortcuts.end(); ++it )
00977   {
00978     if ( !(*it).isNull() )
00979     {
00980       QShortcut* sc = (*it);
00981       delete sc;
00982     }
00983   }
00984   myShortcuts.clear();
00985 }
00986 
00992 void QtxSearchTool::initShortcuts( const QList<QKeySequence>& accels )
00993 {
00994   clearShortcuts();
00995 
00996   QWidget* p = myWatched ? myWatched : ( parentWidget() ? parentWidget() : this );
00997   QShortcut* sc;
00998 
00999   sc = new QShortcut( QKeySequence::Find, p );
01000   connect( sc, SIGNAL( activated() ), this, SLOT( find() ) );
01001   sc->setContext( Qt::WidgetShortcut );
01002   myShortcuts.append( sc );
01003 
01004   sc = new QShortcut( QKeySequence::FindNext, p );
01005   sc->setContext( Qt::WidgetShortcut );
01006   connect( sc, SIGNAL( activated() ), this, SLOT( findNext() ) );
01007   myShortcuts.append( sc );
01008 
01009   sc = new QShortcut( QKeySequence::FindPrevious, p );
01010   sc->setContext( Qt::WidgetShortcut );
01011   connect( sc, SIGNAL( activated() ), this, SLOT( findPrevious() ) );
01012   myShortcuts.append( sc );
01013 
01014   QList<QKeySequence>::ConstIterator it;
01015   for ( it = accels.begin(); it != accels.end(); ++it )
01016   {
01017     sc = new QShortcut( *it, p );
01018     sc->setContext( Qt::WidgetShortcut );
01019     connect( sc, SIGNAL( activated() ), this, SLOT( find() ) );
01020     myShortcuts.append( sc );
01021   }
01022 
01023   myAutoHideTimer = new QTimer( this );
01024   myAutoHideTimer->setInterval( DefaultAutoHideDelay );
01025   myAutoHideTimer->setSingleShot( true );
01026   connect( myAutoHideTimer, SIGNAL( timeout() ), this, SLOT( hide() ) );
01027 
01028   updateShortcuts();
01029 
01030   hide();
01031 }
01032 
01037 void QtxSearchTool::updateShortcuts()
01038 {
01039   int i;
01040   ShortcutList::Iterator it;
01041   for ( it = myShortcuts.begin(), i = 0; it != myShortcuts.end(); ++it, i++ )
01042   {
01043     (*it)->setEnabled( isEnabled() && ( ( i < 3 && myActivators & StandardKey ) ||
01044                                         ( i > 2 && myActivators & HotKey ) ) );
01045   }
01046 }
01047 
01052 void QtxSearchTool::updateControls()
01053 {
01054   myData->parentWidget()->setVisible( myControls & Search );
01055   myNext->parentWidget()->setVisible( myControls & Next );
01056   myPrev->parentWidget()->setVisible( myControls & Prev );
01057   myToFirst->parentWidget()->setVisible( myControls & First );
01058   myToLast->parentWidget()->setVisible( myControls & Last );
01059   myClose->parentWidget()->setVisible( myControls & Close );
01060   myIsCaseSens->parentWidget()->setVisible( myControls & Case );
01061   myIsRegExp->parentWidget()->setVisible( myControls & RegExp );
01062   myWrap->parentWidget()->setVisible( myControls & Wrap );
01063 
01064   myBtnWidget->setVisible( myControls & Standard );
01065   myModWidget->setVisible( myControls & Modifiers );
01066 }
01067 
01083 QtxSearchTool::Searcher::Searcher()
01084 {
01085 }
01086 
01090 QtxSearchTool::Searcher::~Searcher()
01091 {
01092 }
01093 
01152 QtxTreeViewSearcher::QtxTreeViewSearcher( QTreeView* view, int col )
01153   : myView( view ), myColumn( col )
01154 {
01155 }
01156 
01160 QtxTreeViewSearcher::~QtxTreeViewSearcher()
01161 {
01162 }
01163 
01169 int QtxTreeViewSearcher::searchColumn() const
01170 {
01171   return myColumn;
01172 }
01173 
01179 void QtxTreeViewSearcher::setSearchColumn( int column )
01180 {
01181   myColumn = column;
01182 }
01183 
01190 bool QtxTreeViewSearcher::find( const QString& text, QtxSearchTool* st )
01191 {
01192   if ( !myView )
01193     return false;
01194 
01195   const QModelIndexList& l = myView->selectionModel() ?
01196     myView->selectionModel()->selectedIndexes() : QModelIndexList();
01197 
01198   QModelIndex current;
01199   if ( l.count() > 0 )
01200     current = l.first();
01201 
01202   bool wrapSearch = st->isSearchWrapped();
01203 
01204   QModelIndexList found = findItems( text, st );
01205 
01206   if ( found.count() > 0 )
01207   {
01208     if ( !current.isValid() )
01209     {
01210       showItem( found.first() );
01211       return true;
01212     }
01213 
01214     if ( found.contains( current ) )
01215     {
01216       showItem( current );
01217       return true;
01218     }
01219 
01220     QModelIndex next = findNearest( current, found, true );
01221     if ( next.isValid() )
01222     {
01223       showItem( next );
01224       return true;
01225     }
01226 
01227     if ( wrapSearch )
01228     {
01229       showItem( found.first() );
01230       return true;
01231     }
01232   }
01233 
01234   return false;
01235 }
01236 
01243 bool QtxTreeViewSearcher::findNext( const QString& text, QtxSearchTool* st )
01244 {
01245   if ( !myView )
01246     return false;
01247 
01248   const QModelIndexList& l = myView->selectionModel() ?
01249     myView->selectionModel()->selectedIndexes() : QModelIndexList();
01250 
01251   QModelIndex current;
01252   if ( l.count() > 0 )
01253     current = l.first();
01254   else if ( myIndex.isValid() )
01255     current = myIndex;
01256 
01257   bool wrapSearch = st->isSearchWrapped();
01258 
01259   QModelIndexList found = findItems( text, st );
01260 
01261   if ( found.count() > 0 )
01262   {
01263     if ( !current.isValid() )
01264     {
01265       showItem( found.first() );
01266       return true;
01267     }
01268 
01269     QModelIndex next = findNearest( current, found, true );
01270     if ( next.isValid() )
01271     {
01272       showItem( next );
01273       return true;
01274     }
01275 
01276     if ( wrapSearch )
01277     {
01278       showItem( found.first() );
01279       return true;
01280     }
01281   }
01282 
01283   return false;
01284 }
01285 
01292 bool QtxTreeViewSearcher::findPrevious( const QString& text, QtxSearchTool* st )
01293 {
01294   if ( !myView )
01295     return false;
01296 
01297   const QModelIndexList& l = myView->selectionModel() ?
01298     myView->selectionModel()->selectedIndexes() : QModelIndexList();
01299 
01300   QModelIndex current;
01301   if ( l.count() > 0 )
01302     current = l.first();
01303   else if ( myIndex.isValid() )
01304     current = myIndex;
01305 
01306   bool wrapSearch = st->isSearchWrapped();
01307 
01308   QModelIndexList found = findItems( text, st );
01309 
01310   if ( found.count() > 0 )
01311   {
01312     if ( !current.isValid() )
01313     {
01314       showItem( found.first() );
01315       return true;
01316     }
01317 
01318     QModelIndex next = findNearest( current, found, false );
01319     if ( next.isValid() )
01320     {
01321       showItem( next );
01322       return true;
01323     }
01324 
01325     if ( wrapSearch )
01326     {
01327       showItem( found.last() );
01328       return true;
01329     }
01330   }
01331 
01332   return false;
01333 }
01334 
01341 bool QtxTreeViewSearcher::findFirst( const QString& text, QtxSearchTool* st )
01342 {
01343   QModelIndexList found = findItems( text, st );
01344 
01345   if ( found.count() > 0 )
01346   {
01347     showItem( found.first() );
01348     return true;
01349   }
01350 
01351   return false;
01352 }
01353 
01360 bool QtxTreeViewSearcher::findLast( const QString& text, QtxSearchTool* st )
01361 {
01362   QModelIndexList found = findItems( text, st );
01363 
01364   if ( found.count() > 0 )
01365   {
01366     showItem( found.last() );
01367     return true;
01368   }
01369 
01370   return false;
01371 }
01372 
01377 Qt::MatchFlags QtxTreeViewSearcher::matchFlags( QtxSearchTool* st ) const
01378 {
01379   Qt::MatchFlags fl = Qt::MatchRecursive;
01380 
01381   if ( st->isCaseSensitive() )
01382     fl = fl | Qt::MatchCaseSensitive;
01383   if ( st->isRegExpSearch() )
01384     fl = fl | Qt::MatchRegExp;
01385   else
01386     fl = fl | Qt::MatchContains;
01387 
01388   return fl;
01389 }
01390 
01397 QModelIndexList QtxTreeViewSearcher::findItems( const QString& text, QtxSearchTool* st )
01398 {
01399   QString s = text;
01400 
01401   Qt::MatchFlags fl = matchFlags( st );
01402   if ( fl & Qt::MatchRegExp ) {
01403     if ( !s.startsWith( "^" ) && !s.startsWith( ".*" ) )
01404       s.prepend( ".*" );
01405     if ( !s.endsWith( "$" ) && !s.endsWith( ".*" ) )
01406       s.append( ".*" );
01407   }
01408 
01409   if ( myView->model() )
01410     return myView->model()->match( myView->model()->index( 0, myColumn ),
01411                                    Qt::DisplayRole,
01412                                    s, -1, fl );
01413   return QModelIndexList();
01414 }
01415 
01424 QModelIndex QtxTreeViewSearcher::findNearest( const QModelIndex& index,
01425                                               const QModelIndexList& lst,
01426                                               bool direction )
01427 {
01428   if ( direction )
01429   {
01430     QListIterator<QModelIndex> it( lst );
01431     while ( it.hasNext() )
01432     {
01433       QModelIndex found = it.next();
01434       if ( compareIndices( found, index ) > 0 )
01435         return found;
01436     }
01437   }
01438   else
01439   {
01440     QListIterator<QModelIndex> it( lst );
01441     it.toBack();
01442     while ( it.hasPrevious() )
01443     {
01444       QModelIndex found = it.previous();
01445       if ( compareIndices( found, index ) < 0 )
01446         return found;
01447     }
01448   }
01449   return QModelIndex();
01450 }
01451 
01457 void QtxTreeViewSearcher::showItem( const QModelIndex& index )
01458 {
01459   if ( myView && index.isValid() && myView->selectionModel() )
01460   {
01461     QItemSelectionModel::SelectionFlags f =
01462       QItemSelectionModel::Select | QItemSelectionModel::Rows | QItemSelectionModel::Clear;
01463     myView->selectionModel()->select( index, f );
01464     myView->scrollTo( index );
01465     myIndex = index;
01466   }
01467 }
01468 
01475 QString QtxTreeViewSearcher::getId( const QModelIndex& index )
01476 {
01477   QStringList ids;
01478   QModelIndex p = index;
01479   while ( p.isValid() )
01480   {
01481     ids.prepend( QString::number( p.row() ) );
01482     p = p.parent();
01483   }
01484   ids.prepend( "0" );
01485   return ids.join( ":" );
01486 }
01487 
01496 int QtxTreeViewSearcher::compareIndices( const QModelIndex& left,
01497                                          const QModelIndex& right )
01498 {
01499   QString leftId = getId( left );
01500   QString rightId = getId( right );
01501 
01502   QStringList idsLeft  = leftId.split( ":", QString::SkipEmptyParts );
01503   QStringList idsRight = rightId.split( ":", QString::SkipEmptyParts );
01504 
01505   for ( int i = 0; i < idsLeft.count() && i < idsRight.count(); i++ )
01506   {
01507     int lid = idsLeft[i].toInt();
01508     int rid = idsRight[i].toInt();
01509     if ( lid != rid )
01510       return lid - rid;
01511   }
01512   return idsLeft.count() < idsRight.count() ? -1 :
01513     ( idsLeft.count() == idsRight.count() ? 0 : 1 );
01514 }