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>
36
#include <kiconloader.h>
42
class KTreeViewSearchLine::Private
45
Private( KTreeViewSearchLine *_parent )
47
caseSensitive( Qt::CaseInsensitive ),
48
activeSearch( false ),
49
keepParentsVisible( true ),
50
canChooseColumns( true ),
55
KTreeViewSearchLine *parent;
56
QList<QTreeView *> treeViews;
57
Qt::CaseSensitivity caseSensitive;
59
bool keepParentsVisible;
60
bool canChooseColumns;
63
QList<int> searchColumns;
65
void rowsInserted(const QModelIndex & parent, int start, int end) const;
66
void treeViewDeleted( QObject *treeView );
67
void slotColumnActivated(QAction* action);
68
void slotAllVisibleColumns();
71
void checkItemParentsNotVisible(QTreeView *treeView);
72
bool checkItemParentsVisible(QTreeView *treeView, const QModelIndex &index);
75
////////////////////////////////////////////////////////////////////////////////
77
////////////////////////////////////////////////////////////////////////////////
79
void KTreeViewSearchLine::Private::rowsInserted( const QModelIndex & parentIndex, int start, int end ) const
81
QAbstractItemModel* model = qobject_cast<QAbstractItemModel*>( parent->sender() );
85
QTreeView* widget = 0L;
86
foreach ( QTreeView* tree, treeViews )
87
if ( tree->model() == model ) {
95
for ( int i = start; i <= end; ++i ) {
96
widget->setRowHidden( i, parentIndex, !parent->itemMatches( parentIndex, i, parent->text() ) );
100
void KTreeViewSearchLine::Private::treeViewDeleted( QObject *object )
102
treeViews.removeAll( static_cast<QTreeView *>( object ) );
103
parent->setEnabled( treeViews.isEmpty() );
106
void KTreeViewSearchLine::Private::slotColumnActivated( QAction *action )
112
int column = action->data().toInt( &ok );
117
if ( action->isChecked() ) {
118
if ( !searchColumns.isEmpty() ) {
119
if ( !searchColumns.contains( column ) )
120
searchColumns.append( column );
122
if ( searchColumns.count() == treeViews.first()->header()->count() - treeViews.first()->header()->hiddenSectionCount() )
123
searchColumns.clear();
126
searchColumns.append( column );
129
if ( searchColumns.isEmpty() ) {
130
QHeaderView* const header = treeViews.first()->header();
132
for ( int i = 0; i < header->count(); i++ ) {
133
if ( i != column && !header->isSectionHidden( i ) )
134
searchColumns.append( i );
137
} else if ( searchColumns.contains( column ) ) {
138
searchColumns.removeAll( column );
142
parent->updateSearch();
145
void KTreeViewSearchLine::Private::slotAllVisibleColumns()
147
if ( searchColumns.isEmpty() )
148
searchColumns.append( 0 );
150
searchColumns.clear();
152
parent->updateSearch();
155
////////////////////////////////////////////////////////////////////////////////
157
////////////////////////////////////////////////////////////////////////////////
160
void KTreeViewSearchLine::Private::checkColumns()
162
canChooseColumns = parent->canChooseColumnsCheck();
165
void KTreeViewSearchLine::Private::checkItemParentsNotVisible( QTreeView *treeView )
171
QTreeWidgetItemIterator it( treeWidget );
173
for ( ; *it; ++it ) {
174
QTreeWidgetItem *item = *it;
175
item->treeWidget()->setItemHidden( item, !parent->itemMatches( item, search ) );
181
/** Check whether \p item, its siblings and their descendents should be shown. Show or hide the items as necessary.
183
* \p item The list view item to start showing / hiding items at. Typically, this is the first child of another item, or the
184
* the first child of the list view.
185
* \return \c true if an item which should be visible is found, \c false if all items found should be hidden. If this function
186
* returns true and \p highestHiddenParent was not 0, highestHiddenParent will have been shown.
188
bool KTreeViewSearchLine::Private::checkItemParentsVisible( QTreeView *treeView, const QModelIndex &index )
190
bool childMatch = false;
191
const int rowcount = treeView->model()->rowCount( index );
192
for ( int i = 0; i < rowcount; ++i )
193
childMatch |= checkItemParentsVisible( treeView, treeView->model()->index( i, 0, index ) );
195
// Should this item be shown? It should if any children should be, or if it matches.
196
const QModelIndex parentindex = index.parent();
197
if ( childMatch || parent->itemMatches( parentindex, index.row(), search ) ) {
198
treeView->setRowHidden( index.row(), parentindex, false );
202
treeView->setRowHidden( index.row(), parentindex, true );
208
////////////////////////////////////////////////////////////////////////////////
210
////////////////////////////////////////////////////////////////////////////////
212
KTreeViewSearchLine::KTreeViewSearchLine( QWidget *parent, QTreeView *treeView )
213
: KLineEdit( parent ), d( new Private( this ) )
215
connect( this, SIGNAL( textChanged( const QString& ) ),
216
this, SLOT( queueSearch( const QString& ) ) );
218
setClearButtonShown( true );
219
setTreeView( treeView );
226
KTreeViewSearchLine::KTreeViewSearchLine( QWidget *parent,
227
const QList<QTreeView *> &treeViews )
228
: KLineEdit( parent ), d( new Private( this ) )
230
connect( this, SIGNAL( textChanged( const QString& ) ),
231
this, SLOT( queueSearch( const QString& ) ) );
233
setClearButtonShown( true );
234
setTreeViews( treeViews );
237
KTreeViewSearchLine::~KTreeViewSearchLine()
242
Qt::CaseSensitivity KTreeViewSearchLine::caseSensitivity() const
244
return d->caseSensitive;
247
QList<int> KTreeViewSearchLine::searchColumns() const
249
if ( d->canChooseColumns )
250
return d->searchColumns;
255
bool KTreeViewSearchLine::keepParentsVisible() const
257
return d->keepParentsVisible;
260
QTreeView *KTreeViewSearchLine::treeView() const
262
if ( d->treeViews.count() == 1 )
263
return d->treeViews.first();
268
QList<QTreeView *> KTreeViewSearchLine::treeViews() const
274
////////////////////////////////////////////////////////////////////////////////
276
////////////////////////////////////////////////////////////////////////////////
278
void KTreeViewSearchLine::addTreeView( QTreeView *treeView )
281
connectTreeView( treeView );
283
d->treeViews.append( treeView );
284
setEnabled( !d->treeViews.isEmpty() );
290
void KTreeViewSearchLine::removeTreeView( QTreeView *treeView )
293
int index = d->treeViews.indexOf( treeView );
296
d->treeViews.removeAt( index );
299
disconnectTreeView( treeView );
301
setEnabled( !d->treeViews.isEmpty() );
306
void KTreeViewSearchLine::updateSearch( const QString &pattern )
308
d->search = pattern.isNull() ? text() : pattern;
310
foreach ( QTreeView* treeView, d->treeViews )
311
updateSearch( treeView );
314
void KTreeViewSearchLine::updateSearch( QTreeView *treeView )
316
if ( !treeView || !treeView->model()->rowCount() )
320
// If there's a selected item that is visible, make sure that it's visible
321
// when the search changes too (assuming that it still matches).
323
QModelIndex currentIndex = treeView->currentIndex();
325
bool wasUpdateEnabled = treeView->updatesEnabled();
326
treeView->setUpdatesEnabled( false );
327
if ( d->keepParentsVisible )
328
for ( int i = 0; i < treeView->model()->rowCount(); ++i )
329
d->checkItemParentsVisible( treeView, treeView->rootIndex() );
331
d->checkItemParentsNotVisible( treeView );
332
treeView->setUpdatesEnabled( wasUpdateEnabled );
334
if ( currentIndex.isValid() )
335
treeView->scrollTo( currentIndex );
338
void KTreeViewSearchLine::setCaseSensitivity( Qt::CaseSensitivity caseSensitive )
340
if ( d->caseSensitive != caseSensitive ) {
341
d->caseSensitive = caseSensitive;
346
void KTreeViewSearchLine::setKeepParentsVisible( bool visible )
348
if ( d->keepParentsVisible != visible ) {
349
d->keepParentsVisible = visible;
354
void KTreeViewSearchLine::setSearchColumns( const QList<int> &columns )
356
if ( d->canChooseColumns )
357
d->searchColumns = columns;
360
void KTreeViewSearchLine::setTreeView( QTreeView *treeView )
362
setTreeViews( QList<QTreeView *>() );
363
addTreeView( treeView );
366
void KTreeViewSearchLine::setTreeViews( const QList<QTreeView *> &treeViews )
368
foreach ( QTreeView* treeView, d->treeViews )
369
disconnectTreeView( treeView );
371
d->treeViews = treeViews;
373
foreach ( QTreeView* treeView, d->treeViews )
374
connectTreeView( treeView );
378
setEnabled( !d->treeViews.isEmpty() );
381
////////////////////////////////////////////////////////////////////////////////
383
////////////////////////////////////////////////////////////////////////////////
385
bool KTreeViewSearchLine::itemMatches( const QModelIndex &index, int row, const QString &pattern ) const
387
if ( pattern.isEmpty() )
390
if ( !index.isValid() )
393
// If the search column list is populated, search just the columns
394
// specifified. If it is empty default to searching all of the columns.
396
const int columncount = index.model()->columnCount( index );
397
if ( !d->searchColumns.isEmpty() ) {
398
QList<int>::ConstIterator it = d->searchColumns.constBegin();
399
for ( ; it != d->searchColumns.constEnd(); ++it ) {
400
if ( *it < columncount &&
401
index.child( row, *it ).data( Qt::DisplayRole ).toString().indexOf( pattern, 0, d->caseSensitive ) >= 0 )
405
for ( int i = 0; i < columncount; ++i) {
406
if ( index.child( row, i ).data( Qt::DisplayRole ).toString().indexOf( pattern, 0, d->caseSensitive ) >= 0 )
414
void KTreeViewSearchLine::contextMenuEvent( QContextMenuEvent *event )
416
QMenu *popup = KLineEdit::createStandardContextMenu();
418
if ( d->canChooseColumns ) {
419
popup->addSeparator();
420
QMenu *subMenu = popup->addMenu( i18n("Search Columns") );
422
QAction* allVisibleColumnsAction = subMenu->addAction( i18n("All Visible Columns"),
423
this, SLOT( slotAllVisibleColumns() ) );
424
allVisibleColumnsAction->setCheckable( true );
425
allVisibleColumnsAction->setChecked( !d->searchColumns.count() );
426
subMenu->addSeparator();
428
bool allColumnsAreSearchColumns = true;
430
QActionGroup* group = new QActionGroup( popup );
431
group->setExclusive( false );
432
connect( group, SIGNAL( triggered( QAction* ) ), SLOT( slotColumnActivated( QAction* ) ) );
434
QHeaderView* const header = d->treeViews.first()->header();
435
for ( int j = 0; j < header->count(); j++ ) {
436
int i = header->logicalIndex( j );
438
if ( header->isSectionHidden( i ) )
441
QString columnText = header->model()->headerData( i, Qt::Horizontal, Qt::DisplayRole ).toString();
442
QAction* columnAction = subMenu->addAction( qvariant_cast<QIcon>( header->model()->headerData( i, Qt::Horizontal, Qt::DecorationRole ) ), columnText );
443
columnAction->setCheckable( true );
444
columnAction->setChecked( d->searchColumns.isEmpty() || d->searchColumns.contains( i ) );
445
columnAction->setData( i );
446
columnAction->setActionGroup( group );
448
if ( d->searchColumns.isEmpty() || d->searchColumns.indexOf( i ) != -1 )
449
columnAction->setChecked( true );
451
allColumnsAreSearchColumns = false;
454
allVisibleColumnsAction->setChecked( allColumnsAreSearchColumns );
456
// searchColumnsMenuActivated() relies on one possible "all" representation
457
if ( allColumnsAreSearchColumns && !d->searchColumns.isEmpty() )
458
d->searchColumns.clear();
461
popup->exec( event->globalPos() );
465
void KTreeViewSearchLine::connectTreeView( QTreeView *treeView )
467
connect( treeView, SIGNAL( destroyed( QObject* ) ),
468
this, SLOT( treeViewDeleted( QObject* ) ) );
470
connect( treeView->model(), SIGNAL( rowsInserted( const QModelIndex&, int, int) ),
471
this, SLOT( rowsInserted( const QModelIndex&, int, int ) ) );
474
void KTreeViewSearchLine::disconnectTreeView( QTreeView *treeView )
476
disconnect( treeView, SIGNAL( destroyed( QObject* ) ),
477
this, SLOT( treeViewDeleted( QObject* ) ) );
479
disconnect( treeView->model(), SIGNAL( rowsInserted( const QModelIndex&, int, int) ),
480
this, SLOT( rowsInserted( const QModelIndex&, int, int ) ) );
483
bool KTreeViewSearchLine::canChooseColumnsCheck()
485
// This is true if either of the following is true:
487
// there are no listviews connected
488
if ( d->treeViews.isEmpty() )
491
const QTreeView *first = d->treeViews.first();
493
const int numcols = first->model()->columnCount();
494
// the listviews have only one column,
499
for ( int i = 0; i < numcols; ++i )
500
headers.append( first->header()->model()->headerData( i, Qt::Horizontal, Qt::DisplayRole ).toString() );
502
QList<QTreeView *>::ConstIterator it = d->treeViews.constBegin();
503
for ( ++it /* skip the first one */; it != d->treeViews.constEnd(); ++it ) {
504
// the listviews have different numbers of columns,
505
if ( (*it)->model()->columnCount() != numcols )
508
// the listviews differ in column labels.
509
QStringList::ConstIterator jt;
511
for ( i = 0, jt = headers.constBegin(); i < numcols; ++i, ++jt ) {
512
Q_ASSERT( jt != headers.constEnd() );
514
if ( (*it)->header()->model()->headerData( i, Qt::Horizontal, Qt::DisplayRole ).toString() != *jt )
522
////////////////////////////////////////////////////////////////////////////////
524
////////////////////////////////////////////////////////////////////////////////
526
void KTreeViewSearchLine::queueSearch( const QString &search )
531
QTimer::singleShot( 200, this, SLOT( activateSearch() ) );
534
void KTreeViewSearchLine::activateSearch()
536
--(d->queuedSearches);
538
if ( d->queuedSearches == 0 )
539
updateSearch( d->search );
542
////////////////////////////////////////////////////////////////////////////////
543
// KTreeViewSearchLineWidget
544
////////////////////////////////////////////////////////////////////////////////
546
class KTreeViewSearchLineWidget::Private
556
KTreeViewSearchLine *searchLine;
559
KTreeViewSearchLineWidget::KTreeViewSearchLineWidget( QWidget *parent, QTreeView *treeView )
560
: QWidget( parent ), d( new Private )
562
d->treeView = treeView;
564
QTimer::singleShot( 0, this, SLOT( createWidgets() ) );
567
KTreeViewSearchLineWidget::~KTreeViewSearchLineWidget()
572
KTreeViewSearchLine *KTreeViewSearchLineWidget::createSearchLine( QTreeView *treeView ) const
574
return new KTreeViewSearchLine( const_cast<KTreeViewSearchLineWidget*>(this), treeView );
577
void KTreeViewSearchLineWidget::createWidgets()
579
QLabel *label = new QLabel( i18n("S&earch:"), this );
580
label->setObjectName( QLatin1String("kde toolbar widget") );
582
searchLine()->show();
584
label->setBuddy( d->searchLine );
587
QHBoxLayout* layout = new QHBoxLayout( this );
588
layout->setSpacing( 5 );
589
layout->setMargin( 0 );
590
layout->addWidget( label );
591
layout->addWidget( d->searchLine );
594
KTreeViewSearchLine *KTreeViewSearchLineWidget::searchLine() const
596
if ( !d->searchLine )
597
d->searchLine = createSearchLine( d->treeView );
599
return d->searchLine;
602
#include "ktreeviewsearchline.moc"