not quite so much needs to be delayed to the init() function
[personal-kdebase.git] / workspace / plasma / tools / engineexplorer / ktreeviewsearchline.cpp
blob391c19c18ef09062f4fc3a39e4bdd3336f86b790
1 /*
2 Copyright (c) 2003 Scott Wheeler <wheeler@kde.org>
3 Copyright (c) 2005 Rafal Rzepecki <divide@users.sourceforge.net>
4 Copyright (c) 2006 Hamish Rodda <rodda@kde.org>
5 Copyright 2007 Pino Toscano <pino@kde.org>
7 This library is free software; you can redistribute it and/or
8 modify it under the terms of the GNU Library General Public
9 License version 2 as published by the Free Software Foundation.
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 Library General Public License for more details.
16 You should have received a copy of the GNU Library General Public License
17 along with this library; see the file COPYING.LIB. If not, write to
18 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19 Boston, MA 02110-1301, USA.
22 #include "ktreeviewsearchline.h"
24 #include <QtCore/QList>
25 #include <QtCore/QTimer>
26 #include <QtGui/QApplication>
27 #include <QtGui/QContextMenuEvent>
28 #include <QtGui/QHBoxLayout>
29 #include <QtGui/QHeaderView>
30 #include <QtGui/QLabel>
31 #include <QtGui/QMenu>
32 #include <QtGui/QToolButton>
33 #include <QtGui/QTreeView>
35 #include <kdebug.h>
36 #include <kiconloader.h>
37 #include <klocale.h>
38 #include <ktoolbar.h>
40 class KTreeViewSearchLine::Private
42 public:
43 Private( KTreeViewSearchLine *_parent )
44 : parent( _parent ),
45 caseSensitive( Qt::CaseInsensitive ),
46 activeSearch( false ),
47 keepParentsVisible( true ),
48 canChooseColumns( true ),
49 queuedSearches( 0 )
53 KTreeViewSearchLine *parent;
54 QList<QTreeView *> treeViews;
55 Qt::CaseSensitivity caseSensitive;
56 bool activeSearch;
57 bool keepParentsVisible;
58 bool canChooseColumns;
59 QString search;
60 int queuedSearches;
61 QList<int> searchColumns;
63 void rowsInserted(const QModelIndex & parent, int start, int end) const;
64 void treeViewDeleted( QObject *treeView );
65 void slotColumnActivated(QAction* action);
66 void slotAllVisibleColumns();
68 void checkColumns();
69 void checkItemParentsNotVisible(QTreeView *treeView);
70 bool checkItemParentsVisible(QTreeView *treeView, const QModelIndex &index);
73 ////////////////////////////////////////////////////////////////////////////////
74 // private slots
75 ////////////////////////////////////////////////////////////////////////////////
77 void KTreeViewSearchLine::Private::rowsInserted( const QModelIndex & parentIndex, int start, int end ) const
79 QAbstractItemModel* model = qobject_cast<QAbstractItemModel*>( parent->sender() );
80 if ( !model )
81 return;
83 QTreeView* widget = 0L;
84 foreach ( QTreeView* tree, treeViews )
85 if ( tree->model() == model ) {
86 widget = tree;
87 break;
90 if ( !widget )
91 return;
93 for ( int i = start; i <= end; ++i ) {
94 widget->setRowHidden( i, parentIndex, !parent->itemMatches( parentIndex, i, parent->text() ) );
98 void KTreeViewSearchLine::Private::treeViewDeleted( QObject *object )
100 treeViews.removeAll( static_cast<QTreeView *>( object ) );
101 parent->setEnabled( treeViews.isEmpty() );
104 void KTreeViewSearchLine::Private::slotColumnActivated( QAction *action )
106 if ( !action )
107 return;
109 bool ok;
110 int column = action->data().toInt( &ok );
112 if ( !ok )
113 return;
115 if ( action->isChecked() ) {
116 if ( !searchColumns.isEmpty() ) {
117 if ( !searchColumns.contains( column ) )
118 searchColumns.append( column );
120 if ( searchColumns.count() == treeViews.first()->header()->count() - treeViews.first()->header()->hiddenSectionCount() )
121 searchColumns.clear();
123 } else {
124 searchColumns.append( column );
126 } else {
127 if ( searchColumns.isEmpty() ) {
128 QHeaderView* const header = treeViews.first()->header();
130 for ( int i = 0; i < header->count(); i++ ) {
131 if ( i != column && !header->isSectionHidden( i ) )
132 searchColumns.append( i );
135 } else if ( searchColumns.contains( column ) ) {
136 searchColumns.removeAll( column );
140 parent->updateSearch();
143 void KTreeViewSearchLine::Private::slotAllVisibleColumns()
145 if ( searchColumns.isEmpty() )
146 searchColumns.append( 0 );
147 else
148 searchColumns.clear();
150 parent->updateSearch();
153 ////////////////////////////////////////////////////////////////////////////////
154 // private methods
155 ////////////////////////////////////////////////////////////////////////////////
158 void KTreeViewSearchLine::Private::checkColumns()
160 canChooseColumns = parent->canChooseColumnsCheck();
163 void KTreeViewSearchLine::Private::checkItemParentsNotVisible( QTreeView *treeView )
165 Q_UNUSED(treeView)
167 // TODO: PORT ME
168 #if 0
169 QTreeWidgetItemIterator it( treeWidget );
171 for ( ; *it; ++it ) {
172 QTreeWidgetItem *item = *it;
173 item->treeWidget()->setItemHidden( item, !parent->itemMatches( item, search ) );
175 #endif
178 #include <kvbox.h>
180 /** Check whether \p item, its siblings and their descendents should be shown. Show or hide the items as necessary.
182 * \p item The list view item to start showing / hiding items at. Typically, this is the first child of another item, or the
183 * the first child of the list view.
184 * \return \c true if an item which should be visible is found, \c false if all items found should be hidden. If this function
185 * returns true and \p highestHiddenParent was not 0, highestHiddenParent will have been shown.
187 bool KTreeViewSearchLine::Private::checkItemParentsVisible( QTreeView *treeView, const QModelIndex &index )
189 bool childMatch = false;
190 const int rowcount = treeView->model()->rowCount( index );
191 for ( int i = 0; i < rowcount; ++i )
192 childMatch |= checkItemParentsVisible( treeView, treeView->model()->index( i, 0, index ) );
194 // Should this item be shown? It should if any children should be, or if it matches.
195 const QModelIndex parentindex = index.parent();
196 if ( childMatch || parent->itemMatches( parentindex, index.row(), search ) ) {
197 treeView->setRowHidden( index.row(), parentindex, false );
198 return true;
201 treeView->setRowHidden( index.row(), parentindex, true );
203 return false;
207 ////////////////////////////////////////////////////////////////////////////////
208 // public methods
209 ////////////////////////////////////////////////////////////////////////////////
211 KTreeViewSearchLine::KTreeViewSearchLine( QWidget *parent, QTreeView *treeView )
212 : KLineEdit( parent ), d( new Private( this ) )
214 connect( this, SIGNAL( textChanged( const QString& ) ),
215 this, SLOT( queueSearch( const QString& ) ) );
217 setClearButtonShown( true );
218 setTreeView( treeView );
220 if ( !treeView ) {
221 setEnabled( false );
225 KTreeViewSearchLine::KTreeViewSearchLine( QWidget *parent,
226 const QList<QTreeView *> &treeViews )
227 : KLineEdit( parent ), d( new Private( this ) )
229 connect( this, SIGNAL( textChanged( const QString& ) ),
230 this, SLOT( queueSearch( const QString& ) ) );
232 setClearButtonShown( true );
233 setTreeViews( treeViews );
236 KTreeViewSearchLine::~KTreeViewSearchLine()
238 delete d;
241 Qt::CaseSensitivity KTreeViewSearchLine::caseSensitivity() const
243 return d->caseSensitive;
246 QList<int> KTreeViewSearchLine::searchColumns() const
248 if ( d->canChooseColumns )
249 return d->searchColumns;
250 else
251 return QList<int>();
254 bool KTreeViewSearchLine::keepParentsVisible() const
256 return d->keepParentsVisible;
259 QTreeView *KTreeViewSearchLine::treeView() const
261 if ( d->treeViews.count() == 1 )
262 return d->treeViews.first();
263 else
264 return 0;
267 QList<QTreeView *> KTreeViewSearchLine::treeViews() const
269 return d->treeViews;
273 ////////////////////////////////////////////////////////////////////////////////
274 // public slots
275 ////////////////////////////////////////////////////////////////////////////////
277 void KTreeViewSearchLine::addTreeView( QTreeView *treeView )
279 if ( treeView ) {
280 connectTreeView( treeView );
282 d->treeViews.append( treeView );
283 setEnabled( !d->treeViews.isEmpty() );
285 d->checkColumns();
289 void KTreeViewSearchLine::removeTreeView( QTreeView *treeView )
291 if ( treeView ) {
292 int index = d->treeViews.indexOf( treeView );
294 if ( index != -1 ) {
295 d->treeViews.removeAt( index );
296 d->checkColumns();
298 disconnectTreeView( treeView );
300 setEnabled( !d->treeViews.isEmpty() );
305 void KTreeViewSearchLine::updateSearch( const QString &pattern )
307 d->search = pattern.isNull() ? text() : pattern;
309 foreach ( QTreeView* treeView, d->treeViews )
310 updateSearch( treeView );
313 void KTreeViewSearchLine::updateSearch( QTreeView *treeView )
315 if ( !treeView || !treeView->model()->rowCount() )
316 return;
319 // If there's a selected item that is visible, make sure that it's visible
320 // when the search changes too (assuming that it still matches).
322 QModelIndex currentIndex = treeView->currentIndex();
324 bool wasUpdateEnabled = treeView->updatesEnabled();
325 treeView->setUpdatesEnabled( false );
326 if ( d->keepParentsVisible )
327 for ( int i = 0; i < treeView->model()->rowCount(); ++i )
328 d->checkItemParentsVisible( treeView, treeView->rootIndex() );
329 else
330 d->checkItemParentsNotVisible( treeView );
331 treeView->setUpdatesEnabled( wasUpdateEnabled );
333 if ( currentIndex.isValid() )
334 treeView->scrollTo( currentIndex );
337 void KTreeViewSearchLine::setCaseSensitivity( Qt::CaseSensitivity caseSensitive )
339 if ( d->caseSensitive != caseSensitive ) {
340 d->caseSensitive = caseSensitive;
341 updateSearch();
345 void KTreeViewSearchLine::setKeepParentsVisible( bool visible )
347 if ( d->keepParentsVisible != visible ) {
348 d->keepParentsVisible = visible;
349 updateSearch();
353 void KTreeViewSearchLine::setSearchColumns( const QList<int> &columns )
355 if ( d->canChooseColumns )
356 d->searchColumns = columns;
359 void KTreeViewSearchLine::setTreeView( QTreeView *treeView )
361 setTreeViews( QList<QTreeView *>() );
362 addTreeView( treeView );
365 void KTreeViewSearchLine::setTreeViews( const QList<QTreeView *> &treeViews )
367 foreach ( QTreeView* treeView, d->treeViews )
368 disconnectTreeView( treeView );
370 d->treeViews = treeViews;
372 foreach ( QTreeView* treeView, d->treeViews )
373 connectTreeView( treeView );
375 d->checkColumns();
377 setEnabled( !d->treeViews.isEmpty() );
380 ////////////////////////////////////////////////////////////////////////////////
381 // protected members
382 ////////////////////////////////////////////////////////////////////////////////
384 bool KTreeViewSearchLine::itemMatches( const QModelIndex &index, int row, const QString &pattern ) const
386 if ( pattern.isEmpty() )
387 return true;
389 if ( !index.isValid() )
390 return false;
392 // If the search column list is populated, search just the columns
393 // specifified. If it is empty default to searching all of the columns.
395 const int columncount = index.model()->columnCount( index );
396 if ( !d->searchColumns.isEmpty() ) {
397 QList<int>::ConstIterator it = d->searchColumns.constBegin();
398 for ( ; it != d->searchColumns.constEnd(); ++it ) {
399 if ( *it < columncount &&
400 index.child( row, *it ).data( Qt::DisplayRole ).toString().indexOf( pattern, 0, d->caseSensitive ) >= 0 )
401 return true;
403 } else {
404 for ( int i = 0; i < columncount; ++i) {
405 if ( index.child( row, i ).data( Qt::DisplayRole ).toString().indexOf( pattern, 0, d->caseSensitive ) >= 0 )
406 return true;
410 return false;
413 void KTreeViewSearchLine::contextMenuEvent( QContextMenuEvent *event )
415 QMenu *popup = KLineEdit::createStandardContextMenu();
417 if ( d->canChooseColumns ) {
418 popup->addSeparator();
419 QMenu *subMenu = popup->addMenu( i18n("Search Columns") );
421 QAction* allVisibleColumnsAction = subMenu->addAction( i18n("All Visible Columns"),
422 this, SLOT( slotAllVisibleColumns() ) );
423 allVisibleColumnsAction->setCheckable( true );
424 allVisibleColumnsAction->setChecked( !d->searchColumns.count() );
425 subMenu->addSeparator();
427 bool allColumnsAreSearchColumns = true;
429 QActionGroup* group = new QActionGroup( popup );
430 group->setExclusive( false );
431 connect( group, SIGNAL( triggered( QAction* ) ), SLOT( slotColumnActivated( QAction* ) ) );
433 QHeaderView* const header = d->treeViews.first()->header();
434 for ( int j = 0; j < header->count(); j++ ) {
435 int i = header->logicalIndex( j );
437 if ( header->isSectionHidden( i ) )
438 continue;
440 QString columnText = header->model()->headerData( i, Qt::Horizontal, Qt::DisplayRole ).toString();
441 QAction* columnAction = subMenu->addAction( qvariant_cast<QIcon>( header->model()->headerData( i, Qt::Horizontal, Qt::DecorationRole ) ), columnText );
442 columnAction->setCheckable( true );
443 columnAction->setChecked( d->searchColumns.isEmpty() || d->searchColumns.contains( i ) );
444 columnAction->setData( i );
445 columnAction->setActionGroup( group );
447 if ( d->searchColumns.isEmpty() || d->searchColumns.indexOf( i ) != -1 )
448 columnAction->setChecked( true );
449 else
450 allColumnsAreSearchColumns = false;
453 allVisibleColumnsAction->setChecked( allColumnsAreSearchColumns );
455 // searchColumnsMenuActivated() relies on one possible "all" representation
456 if ( allColumnsAreSearchColumns && !d->searchColumns.isEmpty() )
457 d->searchColumns.clear();
460 popup->exec( event->globalPos() );
461 delete popup;
464 void KTreeViewSearchLine::connectTreeView( QTreeView *treeView )
466 connect( treeView, SIGNAL( destroyed( QObject* ) ),
467 this, SLOT( treeViewDeleted( QObject* ) ) );
469 connect( treeView->model(), SIGNAL( rowsInserted( const QModelIndex&, int, int) ),
470 this, SLOT( rowsInserted( const QModelIndex&, int, int ) ) );
473 void KTreeViewSearchLine::disconnectTreeView( QTreeView *treeView )
475 disconnect( treeView, SIGNAL( destroyed( QObject* ) ),
476 this, SLOT( treeViewDeleted( QObject* ) ) );
478 disconnect( treeView->model(), SIGNAL( rowsInserted( const QModelIndex&, int, int) ),
479 this, SLOT( rowsInserted( const QModelIndex&, int, int ) ) );
482 bool KTreeViewSearchLine::canChooseColumnsCheck()
484 // This is true if either of the following is true:
486 // there are no listviews connected
487 if ( d->treeViews.isEmpty() )
488 return false;
490 const QTreeView *first = d->treeViews.first();
492 const int numcols = first->model()->columnCount();
493 // the listviews have only one column,
494 if ( numcols < 2 )
495 return false;
497 QStringList headers;
498 for ( int i = 0; i < numcols; ++i )
499 headers.append( first->header()->model()->headerData( i, Qt::Horizontal, Qt::DisplayRole ).toString() );
501 QList<QTreeView *>::ConstIterator it = d->treeViews.constBegin();
502 for ( ++it /* skip the first one */; it != d->treeViews.constEnd(); ++it ) {
503 // the listviews have different numbers of columns,
504 if ( (*it)->model()->columnCount() != numcols )
505 return false;
507 // the listviews differ in column labels.
508 QStringList::ConstIterator jt;
509 int i;
510 for ( i = 0, jt = headers.constBegin(); i < numcols; ++i, ++jt ) {
511 Q_ASSERT( jt != headers.constEnd() );
513 if ( (*it)->header()->model()->headerData( i, Qt::Horizontal, Qt::DisplayRole ).toString() != *jt )
514 return false;
518 return true;
521 ////////////////////////////////////////////////////////////////////////////////
522 // protected slots
523 ////////////////////////////////////////////////////////////////////////////////
525 void KTreeViewSearchLine::queueSearch( const QString &search )
527 d->queuedSearches++;
528 d->search = search;
530 QTimer::singleShot( 200, this, SLOT( activateSearch() ) );
533 void KTreeViewSearchLine::activateSearch()
535 --(d->queuedSearches);
537 if ( d->queuedSearches == 0 )
538 updateSearch( d->search );
541 ////////////////////////////////////////////////////////////////////////////////
542 // KTreeViewSearchLineWidget
543 ////////////////////////////////////////////////////////////////////////////////
545 class KTreeViewSearchLineWidget::Private
547 public:
548 Private()
549 : treeView( 0 ),
550 searchLine( 0 )
554 QTreeView *treeView;
555 KTreeViewSearchLine *searchLine;
558 KTreeViewSearchLineWidget::KTreeViewSearchLineWidget( QWidget *parent, QTreeView *treeView )
559 : QWidget( parent ), d( new Private )
561 d->treeView = treeView;
563 QTimer::singleShot( 0, this, SLOT( createWidgets() ) );
566 KTreeViewSearchLineWidget::~KTreeViewSearchLineWidget()
568 delete d;
571 KTreeViewSearchLine *KTreeViewSearchLineWidget::createSearchLine( QTreeView *treeView ) const
573 return new KTreeViewSearchLine( const_cast<KTreeViewSearchLineWidget*>(this), treeView );
576 void KTreeViewSearchLineWidget::createWidgets()
578 QLabel *label = new QLabel( i18n("S&earch:"), this );
579 label->setObjectName( QLatin1String("kde toolbar widget") );
581 searchLine()->show();
583 label->setBuddy( d->searchLine );
584 label->show();
586 QHBoxLayout* layout = new QHBoxLayout( this );
587 layout->setSpacing( 5 );
588 layout->setMargin( 0 );
589 layout->addWidget( label );
590 layout->addWidget( d->searchLine );
593 KTreeViewSearchLine *KTreeViewSearchLineWidget::searchLine() const
595 if ( !d->searchLine )
596 d->searchLine = createSearchLine( d->treeView );
598 return d->searchLine;
601 #include "ktreeviewsearchline.moc"