compile
[kdegraphics.git] / okular / ui / ktreeviewsearchline.cpp
blob441254f67a870d8e98171dc19ed3379ed2b74e34
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 // TODO: PORT ME
166 #if 0
167 QTreeWidgetItemIterator it( treeWidget );
169 for ( ; *it; ++it ) {
170 QTreeWidgetItem *item = *it;
171 item->treeWidget()->setItemHidden( item, !parent->itemMatches( item, search ) );
173 #endif
176 #include <kvbox.h>
178 /** Check whether \p item, its siblings and their descendents should be shown. Show or hide the items as necessary.
180 * \p item The list view item to start showing / hiding items at. Typically, this is the first child of another item, or the
181 * the first child of the list view.
182 * \return \c true if an item which should be visible is found, \c false if all items found should be hidden. If this function
183 * returns true and \p highestHiddenParent was not 0, highestHiddenParent will have been shown.
185 bool KTreeViewSearchLine::Private::checkItemParentsVisible( QTreeView *treeView, const QModelIndex &index )
187 bool childMatch = false;
188 const int rowcount = treeView->model()->rowCount( index );
189 for ( int i = 0; i < rowcount; ++i )
190 childMatch |= checkItemParentsVisible( treeView, treeView->model()->index( i, 0, index ) );
192 // Should this item be shown? It should if any children should be, or if it matches.
193 const QModelIndex parentindex = index.parent();
194 if ( childMatch || parent->itemMatches( parentindex, index.row(), search ) ) {
195 treeView->setRowHidden( index.row(), parentindex, false );
196 return true;
199 treeView->setRowHidden( index.row(), parentindex, true );
201 return false;
205 ////////////////////////////////////////////////////////////////////////////////
206 // public methods
207 ////////////////////////////////////////////////////////////////////////////////
209 KTreeViewSearchLine::KTreeViewSearchLine( QWidget *parent, QTreeView *treeView )
210 : KLineEdit( parent ), d( new Private( this ) )
212 connect( this, SIGNAL( textChanged( const QString& ) ),
213 this, SLOT( queueSearch( const QString& ) ) );
215 setClearButtonShown( true );
216 setTreeView( treeView );
218 if ( !treeView ) {
219 setEnabled( false );
223 KTreeViewSearchLine::KTreeViewSearchLine( QWidget *parent,
224 const QList<QTreeView *> &treeViews )
225 : KLineEdit( parent ), d( new Private( this ) )
227 connect( this, SIGNAL( textChanged( const QString& ) ),
228 this, SLOT( queueSearch( const QString& ) ) );
230 setClearButtonShown( true );
231 setTreeViews( treeViews );
234 KTreeViewSearchLine::~KTreeViewSearchLine()
236 delete d;
239 Qt::CaseSensitivity KTreeViewSearchLine::caseSensitivity() const
241 return d->caseSensitive;
244 QList<int> KTreeViewSearchLine::searchColumns() const
246 if ( d->canChooseColumns )
247 return d->searchColumns;
248 else
249 return QList<int>();
252 bool KTreeViewSearchLine::keepParentsVisible() const
254 return d->keepParentsVisible;
257 QTreeView *KTreeViewSearchLine::treeView() const
259 if ( d->treeViews.count() == 1 )
260 return d->treeViews.first();
261 else
262 return 0;
265 QList<QTreeView *> KTreeViewSearchLine::treeViews() const
267 return d->treeViews;
271 ////////////////////////////////////////////////////////////////////////////////
272 // public slots
273 ////////////////////////////////////////////////////////////////////////////////
275 void KTreeViewSearchLine::addTreeView( QTreeView *treeView )
277 if ( treeView ) {
278 connectTreeView( treeView );
280 d->treeViews.append( treeView );
281 setEnabled( !d->treeViews.isEmpty() );
283 d->checkColumns();
287 void KTreeViewSearchLine::removeTreeView( QTreeView *treeView )
289 if ( treeView ) {
290 int index = d->treeViews.indexOf( treeView );
292 if ( index != -1 ) {
293 d->treeViews.removeAt( index );
294 d->checkColumns();
296 disconnectTreeView( treeView );
298 setEnabled( !d->treeViews.isEmpty() );
303 void KTreeViewSearchLine::updateSearch( const QString &pattern )
305 d->search = pattern.isNull() ? text() : pattern;
307 foreach ( QTreeView* treeView, d->treeViews )
308 updateSearch( treeView );
311 void KTreeViewSearchLine::updateSearch( QTreeView *treeView )
313 if ( !treeView || !treeView->model()->rowCount() )
314 return;
317 // If there's a selected item that is visible, make sure that it's visible
318 // when the search changes too (assuming that it still matches).
320 QModelIndex currentIndex = treeView->currentIndex();
322 bool wasUpdateEnabled = treeView->updatesEnabled();
323 treeView->setUpdatesEnabled( false );
324 if ( d->keepParentsVisible )
325 for ( int i = 0; i < treeView->model()->rowCount(); ++i )
326 d->checkItemParentsVisible( treeView, treeView->rootIndex() );
327 else
328 d->checkItemParentsNotVisible( treeView );
329 treeView->setUpdatesEnabled( wasUpdateEnabled );
331 if ( currentIndex.isValid() )
332 treeView->scrollTo( currentIndex );
335 void KTreeViewSearchLine::setCaseSensitivity( Qt::CaseSensitivity caseSensitive )
337 if ( d->caseSensitive != caseSensitive ) {
338 d->caseSensitive = caseSensitive;
339 updateSearch();
343 void KTreeViewSearchLine::setKeepParentsVisible( bool visible )
345 if ( d->keepParentsVisible != visible ) {
346 d->keepParentsVisible = visible;
347 updateSearch();
351 void KTreeViewSearchLine::setSearchColumns( const QList<int> &columns )
353 if ( d->canChooseColumns )
354 d->searchColumns = columns;
357 void KTreeViewSearchLine::setTreeView( QTreeView *treeView )
359 setTreeViews( QList<QTreeView *>() );
360 addTreeView( treeView );
363 void KTreeViewSearchLine::setTreeViews( const QList<QTreeView *> &treeViews )
365 foreach ( QTreeView* treeView, d->treeViews )
366 disconnectTreeView( treeView );
368 d->treeViews = treeViews;
370 foreach ( QTreeView* treeView, d->treeViews )
371 connectTreeView( treeView );
373 d->checkColumns();
375 setEnabled( !d->treeViews.isEmpty() );
378 ////////////////////////////////////////////////////////////////////////////////
379 // protected members
380 ////////////////////////////////////////////////////////////////////////////////
382 bool KTreeViewSearchLine::itemMatches( const QModelIndex &index, int row, const QString &pattern ) const
384 if ( pattern.isEmpty() )
385 return true;
387 if ( !index.isValid() )
388 return false;
390 // If the search column list is populated, search just the columns
391 // specifified. If it is empty default to searching all of the columns.
393 const int columncount = index.model()->columnCount( index );
394 if ( !d->searchColumns.isEmpty() ) {
395 QList<int>::ConstIterator it = d->searchColumns.begin();
396 for ( ; it != d->searchColumns.end(); ++it ) {
397 if ( *it < columncount &&
398 index.child( row, *it ).data( Qt::DisplayRole ).toString().indexOf( pattern, 0, d->caseSensitive ) >= 0 )
399 return true;
401 } else {
402 for ( int i = 0; i < columncount; ++i) {
403 if ( index.child( row, i ).data( Qt::DisplayRole ).toString().indexOf( pattern, 0, d->caseSensitive ) >= 0 )
404 return true;
408 return false;
411 void KTreeViewSearchLine::contextMenuEvent( QContextMenuEvent *event )
413 QMenu *popup = KLineEdit::createStandardContextMenu();
415 if ( d->canChooseColumns ) {
416 popup->addSeparator();
417 QMenu *subMenu = popup->addMenu( i18n("Search Columns") );
419 QAction* allVisibleColumnsAction = subMenu->addAction( i18n("All Visible Columns"),
420 this, SLOT( slotAllVisibleColumns() ) );
421 allVisibleColumnsAction->setCheckable( true );
422 allVisibleColumnsAction->setChecked( !d->searchColumns.count() );
423 subMenu->addSeparator();
425 bool allColumnsAreSearchColumns = true;
427 QActionGroup* group = new QActionGroup( popup );
428 group->setExclusive( false );
429 connect( group, SIGNAL( triggered( QAction* ) ), SLOT( slotColumnActivated( QAction* ) ) );
431 QHeaderView* const header = d->treeViews.first()->header();
432 for ( int j = 0; j < header->count(); j++ ) {
433 int i = header->logicalIndex( j );
435 if ( header->isSectionHidden( i ) )
436 continue;
438 QString columnText = header->model()->headerData( i, Qt::Horizontal, Qt::DisplayRole ).toString();
439 QAction* columnAction = subMenu->addAction( qvariant_cast<QIcon>( header->model()->headerData( i, Qt::Horizontal, Qt::DecorationRole ) ), columnText );
440 columnAction->setCheckable( true );
441 columnAction->setChecked( d->searchColumns.isEmpty() || d->searchColumns.contains( i ) );
442 columnAction->setData( i );
443 columnAction->setActionGroup( group );
445 if ( d->searchColumns.isEmpty() || d->searchColumns.indexOf( i ) != -1 )
446 columnAction->setChecked( true );
447 else
448 allColumnsAreSearchColumns = false;
451 allVisibleColumnsAction->setChecked( allColumnsAreSearchColumns );
453 // searchColumnsMenuActivated() relies on one possible "all" representation
454 if ( allColumnsAreSearchColumns && !d->searchColumns.isEmpty() )
455 d->searchColumns.clear();
458 popup->exec( event->globalPos() );
459 delete popup;
462 void KTreeViewSearchLine::connectTreeView( QTreeView *treeView )
464 connect( treeView, SIGNAL( destroyed( QObject* ) ),
465 this, SLOT( treeViewDeleted( QObject* ) ) );
467 connect( treeView->model(), SIGNAL( rowsInserted( const QModelIndex&, int, int) ),
468 this, SLOT( rowsInserted( const QModelIndex&, int, int ) ) );
471 void KTreeViewSearchLine::disconnectTreeView( QTreeView *treeView )
473 disconnect( treeView, SIGNAL( destroyed( QObject* ) ),
474 this, SLOT( treeViewDeleted( QObject* ) ) );
476 disconnect( treeView->model(), SIGNAL( rowsInserted( const QModelIndex&, int, int) ),
477 this, SLOT( rowsInserted( const QModelIndex&, int, int ) ) );
480 bool KTreeViewSearchLine::canChooseColumnsCheck()
482 // This is true if either of the following is true:
484 // there are no listviews connected
485 if ( d->treeViews.isEmpty() )
486 return false;
488 const QTreeView *first = d->treeViews.first();
490 const int numcols = first->model()->columnCount();
491 // the listviews have only one column,
492 if ( numcols < 2 )
493 return false;
495 QStringList headers;
496 for ( int i = 0; i < numcols; ++i )
497 headers.append( first->header()->model()->headerData( i, Qt::Horizontal, Qt::DisplayRole ).toString() );
499 QList<QTreeView *>::ConstIterator it = d->treeViews.constBegin();
500 for ( ++it /* skip the first one */; it != d->treeViews.constEnd(); ++it ) {
501 // the listviews have different numbers of columns,
502 if ( (*it)->model()->columnCount() != numcols )
503 return false;
505 // the listviews differ in column labels.
506 QStringList::ConstIterator jt;
507 int i;
508 for ( i = 0, jt = headers.constBegin(); i < numcols; ++i, ++jt ) {
509 Q_ASSERT( jt != headers.constEnd() );
511 if ( (*it)->header()->model()->headerData( i, Qt::Horizontal, Qt::DisplayRole ).toString() != *jt )
512 return false;
516 return true;
519 ////////////////////////////////////////////////////////////////////////////////
520 // protected slots
521 ////////////////////////////////////////////////////////////////////////////////
523 void KTreeViewSearchLine::queueSearch( const QString &search )
525 d->queuedSearches++;
526 d->search = search;
528 QTimer::singleShot( 200, this, SLOT( activateSearch() ) );
531 void KTreeViewSearchLine::activateSearch()
533 --(d->queuedSearches);
535 if ( d->queuedSearches == 0 )
536 updateSearch( d->search );
539 ////////////////////////////////////////////////////////////////////////////////
540 // KTreeViewSearchLineWidget
541 ////////////////////////////////////////////////////////////////////////////////
543 class KTreeViewSearchLineWidget::Private
545 public:
546 Private()
547 : treeView( 0 ),
548 searchLine( 0 )
552 QTreeView *treeView;
553 KTreeViewSearchLine *searchLine;
556 KTreeViewSearchLineWidget::KTreeViewSearchLineWidget( QWidget *parent, QTreeView *treeView )
557 : QWidget( parent ), d( new Private )
559 d->treeView = treeView;
561 QTimer::singleShot( 0, this, SLOT( createWidgets() ) );
564 KTreeViewSearchLineWidget::~KTreeViewSearchLineWidget()
566 delete d;
569 KTreeViewSearchLine *KTreeViewSearchLineWidget::createSearchLine( QTreeView *treeView ) const
571 return new KTreeViewSearchLine( const_cast<KTreeViewSearchLineWidget*>(this), treeView );
574 void KTreeViewSearchLineWidget::createWidgets()
576 QLabel *label = new QLabel( i18n("S&earch:"), this );
577 label->setObjectName( QLatin1String("kde toolbar widget") );
579 searchLine()->show();
581 label->setBuddy( d->searchLine );
582 label->show();
584 QHBoxLayout* layout = new QHBoxLayout( this );
585 layout->setSpacing( 5 );
586 layout->setMargin( 0 );
587 layout->addWidget( label );
588 layout->addWidget( d->searchLine );
591 KTreeViewSearchLine *KTreeViewSearchLineWidget::searchLine() const
593 if ( !d->searchLine )
594 d->searchLine = createSearchLine( d->treeView );
596 return d->searchLine;
599 #include "ktreeviewsearchline.moc"