00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022 #include "ktreeviewsearchline.h"
00023
00024 #include <QtCore/QList>
00025 #include <QtCore/QTimer>
00026 #include <QtGui/QApplication>
00027 #include <QtGui/QContextMenuEvent>
00028 #include <QtGui/QHBoxLayout>
00029 #include <QtGui/QHeaderView>
00030 #include <QtGui/QLabel>
00031 #include <QtGui/QMenu>
00032 #include <QtGui/QToolButton>
00033 #include <QtGui/QTreeView>
00034
00035 #include <kdebug.h>
00036 #include <kiconloader.h>
00037 #include <klocale.h>
00038 #include <ktoolbar.h>
00039
00040 class KTreeViewSearchLine::Private
00041 {
00042 public:
00043 Private( KTreeViewSearchLine *_parent )
00044 : parent( _parent ),
00045 caseSensitive( Qt::CaseInsensitive ),
00046 activeSearch( false ),
00047 keepParentsVisible( true ),
00048 canChooseColumns( true ),
00049 queuedSearches( 0 )
00050 {
00051 }
00052
00053 KTreeViewSearchLine *parent;
00054 QList<QTreeView *> treeViews;
00055 Qt::CaseSensitivity caseSensitive;
00056 bool activeSearch;
00057 bool keepParentsVisible;
00058 bool canChooseColumns;
00059 QString search;
00060 int queuedSearches;
00061 QList<int> searchColumns;
00062
00063 void rowsInserted(const QModelIndex & parent, int start, int end) const;
00064 void treeViewDeleted( QObject *treeView );
00065 void slotColumnActivated(QAction* action);
00066 void slotAllVisibleColumns();
00067
00068 void checkColumns();
00069 void checkItemParentsNotVisible(QTreeView *treeView);
00070 bool checkItemParentsVisible(QTreeView *treeView, const QModelIndex &index);
00071 };
00072
00074
00076
00077 void KTreeViewSearchLine::Private::rowsInserted( const QModelIndex & parentIndex, int start, int end ) const
00078 {
00079 QAbstractItemModel* model = qobject_cast<QAbstractItemModel*>( parent->sender() );
00080 if ( !model )
00081 return;
00082
00083 QTreeView* widget = 0L;
00084 foreach ( QTreeView* tree, treeViews )
00085 if ( tree->model() == model ) {
00086 widget = tree;
00087 break;
00088 }
00089
00090 if ( !widget )
00091 return;
00092
00093 for ( int i = start; i <= end; ++i ) {
00094 widget->setRowHidden( i, parentIndex, !parent->itemMatches( parentIndex, i, parent->text() ) );
00095 }
00096 }
00097
00098 void KTreeViewSearchLine::Private::treeViewDeleted( QObject *object )
00099 {
00100 treeViews.removeAll( static_cast<QTreeView *>( object ) );
00101 parent->setEnabled( treeViews.isEmpty() );
00102 }
00103
00104 void KTreeViewSearchLine::Private::slotColumnActivated( QAction *action )
00105 {
00106 if ( !action )
00107 return;
00108
00109 bool ok;
00110 int column = action->data().toInt( &ok );
00111
00112 if ( !ok )
00113 return;
00114
00115 if ( action->isChecked() ) {
00116 if ( !searchColumns.isEmpty() ) {
00117 if ( !searchColumns.contains( column ) )
00118 searchColumns.append( column );
00119
00120 if ( searchColumns.count() == treeViews.first()->header()->count() - treeViews.first()->header()->hiddenSectionCount() )
00121 searchColumns.clear();
00122
00123 } else {
00124 searchColumns.append( column );
00125 }
00126 } else {
00127 if ( searchColumns.isEmpty() ) {
00128 QHeaderView* const header = treeViews.first()->header();
00129
00130 for ( int i = 0; i < header->count(); i++ ) {
00131 if ( i != column && !header->isSectionHidden( i ) )
00132 searchColumns.append( i );
00133 }
00134
00135 } else if ( searchColumns.contains( column ) ) {
00136 searchColumns.removeAll( column );
00137 }
00138 }
00139
00140 parent->updateSearch();
00141 }
00142
00143 void KTreeViewSearchLine::Private::slotAllVisibleColumns()
00144 {
00145 if ( searchColumns.isEmpty() )
00146 searchColumns.append( 0 );
00147 else
00148 searchColumns.clear();
00149
00150 parent->updateSearch();
00151 }
00152
00154
00156
00157
00158 void KTreeViewSearchLine::Private::checkColumns()
00159 {
00160 canChooseColumns = parent->canChooseColumnsCheck();
00161 }
00162
00163 void KTreeViewSearchLine::Private::checkItemParentsNotVisible( QTreeView *treeView )
00164 {
00165 Q_UNUSED(treeView)
00166
00167
00168 #if 0
00169 QTreeWidgetItemIterator it( treeWidget );
00170
00171 for ( ; *it; ++it ) {
00172 QTreeWidgetItem *item = *it;
00173 item->treeWidget()->setItemHidden( item, !parent->itemMatches( item, search ) );
00174 }
00175 #endif
00176 }
00177
00178 #include <kvbox.h>
00179
00187 bool KTreeViewSearchLine::Private::checkItemParentsVisible( QTreeView *treeView, const QModelIndex &index )
00188 {
00189 bool childMatch = false;
00190 const int rowcount = treeView->model()->rowCount( index );
00191 for ( int i = 0; i < rowcount; ++i )
00192 childMatch |= checkItemParentsVisible( treeView, treeView->model()->index( i, 0, index ) );
00193
00194
00195 const QModelIndex parentindex = index.parent();
00196 if ( childMatch || parent->itemMatches( parentindex, index.row(), search ) ) {
00197 treeView->setRowHidden( index.row(), parentindex, false );
00198 return true;
00199 }
00200
00201 treeView->setRowHidden( index.row(), parentindex, true );
00202
00203 return false;
00204 }
00205
00206
00208
00210
00211 KTreeViewSearchLine::KTreeViewSearchLine( QWidget *parent, QTreeView *treeView )
00212 : KLineEdit( parent ), d( new Private( this ) )
00213 {
00214 connect( this, SIGNAL( textChanged( const QString& ) ),
00215 this, SLOT( queueSearch( const QString& ) ) );
00216
00217 setClearButtonShown( true );
00218 setTreeView( treeView );
00219
00220 if ( !treeView ) {
00221 setEnabled( false );
00222 }
00223 }
00224
00225 KTreeViewSearchLine::KTreeViewSearchLine( QWidget *parent,
00226 const QList<QTreeView *> &treeViews )
00227 : KLineEdit( parent ), d( new Private( this ) )
00228 {
00229 connect( this, SIGNAL( textChanged( const QString& ) ),
00230 this, SLOT( queueSearch( const QString& ) ) );
00231
00232 setClearButtonShown( true );
00233 setTreeViews( treeViews );
00234 }
00235
00236 KTreeViewSearchLine::~KTreeViewSearchLine()
00237 {
00238 delete d;
00239 }
00240
00241 Qt::CaseSensitivity KTreeViewSearchLine::caseSensitivity() const
00242 {
00243 return d->caseSensitive;
00244 }
00245
00246 QList<int> KTreeViewSearchLine::searchColumns() const
00247 {
00248 if ( d->canChooseColumns )
00249 return d->searchColumns;
00250 else
00251 return QList<int>();
00252 }
00253
00254 bool KTreeViewSearchLine::keepParentsVisible() const
00255 {
00256 return d->keepParentsVisible;
00257 }
00258
00259 QTreeView *KTreeViewSearchLine::treeView() const
00260 {
00261 if ( d->treeViews.count() == 1 )
00262 return d->treeViews.first();
00263 else
00264 return 0;
00265 }
00266
00267 QList<QTreeView *> KTreeViewSearchLine::treeViews() const
00268 {
00269 return d->treeViews;
00270 }
00271
00272
00274
00276
00277 void KTreeViewSearchLine::addTreeView( QTreeView *treeView )
00278 {
00279 if ( treeView ) {
00280 connectTreeView( treeView );
00281
00282 d->treeViews.append( treeView );
00283 setEnabled( !d->treeViews.isEmpty() );
00284
00285 d->checkColumns();
00286 }
00287 }
00288
00289 void KTreeViewSearchLine::removeTreeView( QTreeView *treeView )
00290 {
00291 if ( treeView ) {
00292 int index = d->treeViews.indexOf( treeView );
00293
00294 if ( index != -1 ) {
00295 d->treeViews.removeAt( index );
00296 d->checkColumns();
00297
00298 disconnectTreeView( treeView );
00299
00300 setEnabled( !d->treeViews.isEmpty() );
00301 }
00302 }
00303 }
00304
00305 void KTreeViewSearchLine::updateSearch( const QString &pattern )
00306 {
00307 d->search = pattern.isNull() ? text() : pattern;
00308
00309 foreach ( QTreeView* treeView, d->treeViews )
00310 updateSearch( treeView );
00311 }
00312
00313 void KTreeViewSearchLine::updateSearch( QTreeView *treeView )
00314 {
00315 if ( !treeView || !treeView->model()->rowCount() )
00316 return;
00317
00318
00319
00320
00321
00322 QModelIndex currentIndex = treeView->currentIndex();
00323
00324 bool wasUpdateEnabled = treeView->updatesEnabled();
00325 treeView->setUpdatesEnabled( false );
00326 if ( d->keepParentsVisible )
00327 for ( int i = 0; i < treeView->model()->rowCount(); ++i )
00328 d->checkItemParentsVisible( treeView, treeView->rootIndex() );
00329 else
00330 d->checkItemParentsNotVisible( treeView );
00331 treeView->setUpdatesEnabled( wasUpdateEnabled );
00332
00333 if ( currentIndex.isValid() )
00334 treeView->scrollTo( currentIndex );
00335 }
00336
00337 void KTreeViewSearchLine::setCaseSensitivity( Qt::CaseSensitivity caseSensitive )
00338 {
00339 if ( d->caseSensitive != caseSensitive ) {
00340 d->caseSensitive = caseSensitive;
00341 updateSearch();
00342 }
00343 }
00344
00345 void KTreeViewSearchLine::setKeepParentsVisible( bool visible )
00346 {
00347 if ( d->keepParentsVisible != visible ) {
00348 d->keepParentsVisible = visible;
00349 updateSearch();
00350 }
00351 }
00352
00353 void KTreeViewSearchLine::setSearchColumns( const QList<int> &columns )
00354 {
00355 if ( d->canChooseColumns )
00356 d->searchColumns = columns;
00357 }
00358
00359 void KTreeViewSearchLine::setTreeView( QTreeView *treeView )
00360 {
00361 setTreeViews( QList<QTreeView *>() );
00362 addTreeView( treeView );
00363 }
00364
00365 void KTreeViewSearchLine::setTreeViews( const QList<QTreeView *> &treeViews )
00366 {
00367 foreach ( QTreeView* treeView, d->treeViews )
00368 disconnectTreeView( treeView );
00369
00370 d->treeViews = treeViews;
00371
00372 foreach ( QTreeView* treeView, d->treeViews )
00373 connectTreeView( treeView );
00374
00375 d->checkColumns();
00376
00377 setEnabled( !d->treeViews.isEmpty() );
00378 }
00379
00381
00383
00384 bool KTreeViewSearchLine::itemMatches( const QModelIndex &index, int row, const QString &pattern ) const
00385 {
00386 if ( pattern.isEmpty() )
00387 return true;
00388
00389 if ( !index.isValid() )
00390 return false;
00391
00392
00393
00394
00395 const int columncount = index.model()->columnCount( index );
00396 if ( !d->searchColumns.isEmpty() ) {
00397 QList<int>::ConstIterator it = d->searchColumns.constBegin();
00398 for ( ; it != d->searchColumns.constEnd(); ++it ) {
00399 if ( *it < columncount &&
00400 index.child( row, *it ).data( Qt::DisplayRole ).toString().indexOf( pattern, 0, d->caseSensitive ) >= 0 )
00401 return true;
00402 }
00403 } else {
00404 for ( int i = 0; i < columncount; ++i) {
00405 if ( index.child( row, i ).data( Qt::DisplayRole ).toString().indexOf( pattern, 0, d->caseSensitive ) >= 0 )
00406 return true;
00407 }
00408 }
00409
00410 return false;
00411 }
00412
00413 void KTreeViewSearchLine::contextMenuEvent( QContextMenuEvent *event )
00414 {
00415 QMenu *popup = KLineEdit::createStandardContextMenu();
00416
00417 if ( d->canChooseColumns ) {
00418 popup->addSeparator();
00419 QMenu *subMenu = popup->addMenu( i18n("Search Columns") );
00420
00421 QAction* allVisibleColumnsAction = subMenu->addAction( i18n("All Visible Columns"),
00422 this, SLOT( slotAllVisibleColumns() ) );
00423 allVisibleColumnsAction->setCheckable( true );
00424 allVisibleColumnsAction->setChecked( !d->searchColumns.count() );
00425 subMenu->addSeparator();
00426
00427 bool allColumnsAreSearchColumns = true;
00428
00429 QActionGroup* group = new QActionGroup( popup );
00430 group->setExclusive( false );
00431 connect( group, SIGNAL( triggered( QAction* ) ), SLOT( slotColumnActivated( QAction* ) ) );
00432
00433 QHeaderView* const header = d->treeViews.first()->header();
00434 for ( int j = 0; j < header->count(); j++ ) {
00435 int i = header->logicalIndex( j );
00436
00437 if ( header->isSectionHidden( i ) )
00438 continue;
00439
00440 QString columnText = header->model()->headerData( i, Qt::Horizontal, Qt::DisplayRole ).toString();
00441 QAction* columnAction = subMenu->addAction( qvariant_cast<QIcon>( header->model()->headerData( i, Qt::Horizontal, Qt::DecorationRole ) ), columnText );
00442 columnAction->setCheckable( true );
00443 columnAction->setChecked( d->searchColumns.isEmpty() || d->searchColumns.contains( i ) );
00444 columnAction->setData( i );
00445 columnAction->setActionGroup( group );
00446
00447 if ( d->searchColumns.isEmpty() || d->searchColumns.indexOf( i ) != -1 )
00448 columnAction->setChecked( true );
00449 else
00450 allColumnsAreSearchColumns = false;
00451 }
00452
00453 allVisibleColumnsAction->setChecked( allColumnsAreSearchColumns );
00454
00455
00456 if ( allColumnsAreSearchColumns && !d->searchColumns.isEmpty() )
00457 d->searchColumns.clear();
00458 }
00459
00460 popup->exec( event->globalPos() );
00461 delete popup;
00462 }
00463
00464 void KTreeViewSearchLine::connectTreeView( QTreeView *treeView )
00465 {
00466 connect( treeView, SIGNAL( destroyed( QObject* ) ),
00467 this, SLOT( treeViewDeleted( QObject* ) ) );
00468
00469 connect( treeView->model(), SIGNAL( rowsInserted( const QModelIndex&, int, int) ),
00470 this, SLOT( rowsInserted( const QModelIndex&, int, int ) ) );
00471 }
00472
00473 void KTreeViewSearchLine::disconnectTreeView( QTreeView *treeView )
00474 {
00475 disconnect( treeView, SIGNAL( destroyed( QObject* ) ),
00476 this, SLOT( treeViewDeleted( QObject* ) ) );
00477
00478 disconnect( treeView->model(), SIGNAL( rowsInserted( const QModelIndex&, int, int) ),
00479 this, SLOT( rowsInserted( const QModelIndex&, int, int ) ) );
00480 }
00481
00482 bool KTreeViewSearchLine::canChooseColumnsCheck()
00483 {
00484
00485
00486
00487 if ( d->treeViews.isEmpty() )
00488 return false;
00489
00490 const QTreeView *first = d->treeViews.first();
00491
00492 const int numcols = first->model()->columnCount();
00493
00494 if ( numcols < 2 )
00495 return false;
00496
00497 QStringList headers;
00498 for ( int i = 0; i < numcols; ++i )
00499 headers.append( first->header()->model()->headerData( i, Qt::Horizontal, Qt::DisplayRole ).toString() );
00500
00501 QList<QTreeView *>::ConstIterator it = d->treeViews.constBegin();
00502 for ( ++it ; it != d->treeViews.constEnd(); ++it ) {
00503
00504 if ( (*it)->model()->columnCount() != numcols )
00505 return false;
00506
00507
00508 QStringList::ConstIterator jt;
00509 int i;
00510 for ( i = 0, jt = headers.constBegin(); i < numcols; ++i, ++jt ) {
00511 Q_ASSERT( jt != headers.constEnd() );
00512
00513 if ( (*it)->header()->model()->headerData( i, Qt::Horizontal, Qt::DisplayRole ).toString() != *jt )
00514 return false;
00515 }
00516 }
00517
00518 return true;
00519 }
00520
00522
00524
00525 void KTreeViewSearchLine::queueSearch( const QString &search )
00526 {
00527 d->queuedSearches++;
00528 d->search = search;
00529
00530 QTimer::singleShot( 200, this, SLOT( activateSearch() ) );
00531 }
00532
00533 void KTreeViewSearchLine::activateSearch()
00534 {
00535 --(d->queuedSearches);
00536
00537 if ( d->queuedSearches == 0 )
00538 updateSearch( d->search );
00539 }
00540
00542
00544
00545 class KTreeViewSearchLineWidget::Private
00546 {
00547 public:
00548 Private()
00549 : treeView( 0 ),
00550 searchLine( 0 )
00551 {
00552 }
00553
00554 QTreeView *treeView;
00555 KTreeViewSearchLine *searchLine;
00556 };
00557
00558 KTreeViewSearchLineWidget::KTreeViewSearchLineWidget( QWidget *parent, QTreeView *treeView )
00559 : QWidget( parent ), d( new Private )
00560 {
00561 d->treeView = treeView;
00562
00563 QTimer::singleShot( 0, this, SLOT( createWidgets() ) );
00564 }
00565
00566 KTreeViewSearchLineWidget::~KTreeViewSearchLineWidget()
00567 {
00568 delete d;
00569 }
00570
00571 KTreeViewSearchLine *KTreeViewSearchLineWidget::createSearchLine( QTreeView *treeView ) const
00572 {
00573 return new KTreeViewSearchLine( const_cast<KTreeViewSearchLineWidget*>(this), treeView );
00574 }
00575
00576 void KTreeViewSearchLineWidget::createWidgets()
00577 {
00578 QLabel *label = new QLabel( i18n("S&earch:"), this );
00579 label->setObjectName( QLatin1String("kde toolbar widget") );
00580
00581 searchLine()->show();
00582
00583 label->setBuddy( d->searchLine );
00584 label->show();
00585
00586 QHBoxLayout* layout = new QHBoxLayout( this );
00587 layout->setSpacing( 5 );
00588 layout->setMargin( 0 );
00589 layout->addWidget( label );
00590 layout->addWidget( d->searchLine );
00591 }
00592
00593 KTreeViewSearchLine *KTreeViewSearchLineWidget::searchLine() const
00594 {
00595 if ( !d->searchLine )
00596 d->searchLine = createSearchLine( d->treeView );
00597
00598 return d->searchLine;
00599 }
00600
00601 #include "ktreeviewsearchline.moc"