Add a port of the kdelibs classes KTreeWidgetSearchLine* that use QTreeView/QModelIndex instead of TreeWidget/QTreeWidgetItem.
This way, we can really filter a tree view and avoid the limitations of QSortFilterProxyModel. svn path=/trunk/KDE/kdegraphics/okular/; revision=710310remotes/origin/KDE/4.0
parent
b12610e46a
commit
f770c6b542
3 changed files with 908 additions and 0 deletions
@ -0,0 +1,593 @@ |
||||
/*
|
||||
Copyright (c) 2003 Scott Wheeler <wheeler@kde.org> |
||||
Copyright (c) 2005 Rafal Rzepecki <divide@users.sourceforge.net> |
||||
Copyright (c) 2006 Hamish Rodda <rodda@kde.org> |
||||
Copyright 2007 Pino Toscano <pino@kde.org> |
||||
|
||||
This library is free software; you can redistribute it and/or |
||||
modify it under the terms of the GNU Library General Public |
||||
License version 2 as published by the Free Software Foundation. |
||||
|
||||
This library is distributed in the hope that it will be useful, |
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
||||
Library General Public License for more details. |
||||
|
||||
You should have received a copy of the GNU Library General Public License |
||||
along with this library; see the file COPYING.LIB. If not, write to |
||||
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, |
||||
Boston, MA 02110-1301, USA. |
||||
*/ |
||||
|
||||
#include "ktreeviewsearchline.h" |
||||
|
||||
#include <QtCore/QList> |
||||
#include <QtCore/QTimer> |
||||
#include <QtGui/QApplication> |
||||
#include <QtGui/QContextMenuEvent> |
||||
#include <QtGui/QHBoxLayout> |
||||
#include <QtGui/QHeaderView> |
||||
#include <QtGui/QLabel> |
||||
#include <QtGui/QMenu> |
||||
#include <QtGui/QToolButton> |
||||
#include <QtGui/QTreeView> |
||||
|
||||
#include <kdebug.h> |
||||
#include <kiconloader.h> |
||||
#include <klocale.h> |
||||
#include <ktoolbar.h> |
||||
|
||||
class KTreeViewSearchLine::Private |
||||
{ |
||||
public: |
||||
Private( KTreeViewSearchLine *_parent ) |
||||
: parent( _parent ), |
||||
caseSensitive( Qt::CaseInsensitive ), |
||||
activeSearch( false ), |
||||
keepParentsVisible( true ), |
||||
canChooseColumns( true ), |
||||
queuedSearches( 0 ) |
||||
{ |
||||
} |
||||
|
||||
KTreeViewSearchLine *parent; |
||||
QList<QTreeView *> treeViews; |
||||
Qt::CaseSensitivity caseSensitive; |
||||
bool activeSearch; |
||||
bool keepParentsVisible; |
||||
bool canChooseColumns; |
||||
QString search; |
||||
int queuedSearches; |
||||
QList<int> searchColumns; |
||||
|
||||
void rowsInserted(const QModelIndex & parent, int start, int end) const; |
||||
void treeViewDeleted( QObject *treeView ); |
||||
void slotColumnActivated(QAction* action); |
||||
void slotAllVisibleColumns(); |
||||
|
||||
void checkColumns(); |
||||
void checkItemParentsNotVisible(QTreeView *treeView); |
||||
bool checkItemParentsVisible(QTreeView *treeView, const QModelIndex &index); |
||||
}; |
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// private slots
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void KTreeViewSearchLine::Private::rowsInserted( const QModelIndex & parentIndex, int start, int end ) const |
||||
{ |
||||
QAbstractItemModel* model = qobject_cast<QAbstractItemModel*>( parent->sender() ); |
||||
if ( !model ) |
||||
return; |
||||
|
||||
QTreeView* widget = 0L; |
||||
foreach ( QTreeView* tree, treeViews ) |
||||
if ( tree->model() == model ) { |
||||
widget = tree; |
||||
break; |
||||
} |
||||
|
||||
if ( !widget ) |
||||
return; |
||||
|
||||
for ( int i = start; i <= end; ++i ) { |
||||
widget->setRowHidden( i, parentIndex, !parent->itemMatches( parentIndex, i, parent->text() ) ); |
||||
} |
||||
} |
||||
|
||||
void KTreeViewSearchLine::Private::treeViewDeleted( QObject *object ) |
||||
{ |
||||
treeViews.removeAll( static_cast<QTreeView *>( object ) ); |
||||
parent->setEnabled( treeViews.isEmpty() ); |
||||
} |
||||
|
||||
void KTreeViewSearchLine::Private::slotColumnActivated( QAction *action ) |
||||
{ |
||||
if ( !action ) |
||||
return; |
||||
|
||||
bool ok; |
||||
int column = action->data().toInt( &ok ); |
||||
|
||||
if ( !ok ) |
||||
return; |
||||
|
||||
if ( action->isChecked() ) { |
||||
if ( !searchColumns.isEmpty() ) { |
||||
if ( !searchColumns.contains( column ) ) |
||||
searchColumns.append( column ); |
||||
|
||||
if ( searchColumns.count() == treeViews.first()->header()->count() - treeViews.first()->header()->hiddenSectionCount() ) |
||||
searchColumns.clear(); |
||||
|
||||
} else { |
||||
searchColumns.append( column ); |
||||
} |
||||
} else { |
||||
if ( searchColumns.isEmpty() ) { |
||||
QHeaderView* const header = treeViews.first()->header(); |
||||
|
||||
for ( int i = 0; i < header->count(); i++ ) { |
||||
if ( i != column && !header->isSectionHidden( i ) ) |
||||
searchColumns.append( i ); |
||||
} |
||||
|
||||
} else if ( searchColumns.contains( column ) ) { |
||||
searchColumns.removeAll( column ); |
||||
} |
||||
} |
||||
|
||||
parent->updateSearch(); |
||||
} |
||||
|
||||
void KTreeViewSearchLine::Private::slotAllVisibleColumns() |
||||
{ |
||||
if ( searchColumns.isEmpty() ) |
||||
searchColumns.append( 0 ); |
||||
else |
||||
searchColumns.clear(); |
||||
|
||||
parent->updateSearch(); |
||||
} |
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// private methods
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
void KTreeViewSearchLine::Private::checkColumns() |
||||
{ |
||||
canChooseColumns = parent->canChooseColumnsCheck(); |
||||
} |
||||
|
||||
void KTreeViewSearchLine::Private::checkItemParentsNotVisible( QTreeView *treeView ) |
||||
{ |
||||
// TODO: PORT ME
|
||||
#if 0 |
||||
QTreeWidgetItemIterator it( treeWidget ); |
||||
|
||||
for ( ; *it; ++it ) { |
||||
QTreeWidgetItem *item = *it; |
||||
item->treeWidget()->setItemHidden( item, !parent->itemMatches( item, search ) ); |
||||
} |
||||
#endif |
||||
} |
||||
|
||||
#include <kvbox.h> |
||||
|
||||
/** Check whether \p item, its siblings and their descendents should be shown. Show or hide the items as necessary.
|
||||
* |
||||
* \p item The list view item to start showing / hiding items at. Typically, this is the first child of another item, or the |
||||
* the first child of the list view. |
||||
* \return \c true if an item which should be visible is found, \c false if all items found should be hidden. If this function |
||||
* returns true and \p highestHiddenParent was not 0, highestHiddenParent will have been shown. |
||||
*/ |
||||
bool KTreeViewSearchLine::Private::checkItemParentsVisible( QTreeView *treeView, const QModelIndex &index ) |
||||
{ |
||||
bool childMatch = false; |
||||
for ( int i = 0; i < treeView->model()->rowCount( index ); ++i ) |
||||
childMatch |= checkItemParentsVisible( treeView, treeView->model()->index( i, 0, index ) ); |
||||
|
||||
// Should this item be shown? It should if any children should be, or if it matches.
|
||||
if ( childMatch || parent->itemMatches( index.parent(), index.row(), search ) ) { |
||||
treeView->setRowHidden( index.row(), index.parent(), false ); |
||||
return true; |
||||
} |
||||
|
||||
treeView->setRowHidden( index.row(), index.parent(), true ); |
||||
|
||||
return false; |
||||
} |
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// public methods
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
KTreeViewSearchLine::KTreeViewSearchLine( QWidget *parent, QTreeView *treeView ) |
||||
: KLineEdit( parent ), d( new Private( this ) ) |
||||
{ |
||||
connect( this, SIGNAL( textChanged( const QString& ) ), |
||||
this, SLOT( queueSearch( const QString& ) ) ); |
||||
|
||||
setClearButtonShown( true ); |
||||
setTreeView( treeView ); |
||||
|
||||
if ( !treeView ) { |
||||
setEnabled( false ); |
||||
} |
||||
} |
||||
|
||||
KTreeViewSearchLine::KTreeViewSearchLine( QWidget *parent, |
||||
const QList<QTreeView *> &treeViews ) |
||||
: KLineEdit( parent ), d( new Private( this ) ) |
||||
{ |
||||
connect( this, SIGNAL( textChanged( const QString& ) ), |
||||
this, SLOT( queueSearch( const QString& ) ) ); |
||||
|
||||
setClearButtonShown( true ); |
||||
setTreeViews( treeViews ); |
||||
} |
||||
|
||||
KTreeViewSearchLine::~KTreeViewSearchLine() |
||||
{ |
||||
delete d; |
||||
} |
||||
|
||||
Qt::CaseSensitivity KTreeViewSearchLine::caseSensitivity() const |
||||
{ |
||||
return d->caseSensitive; |
||||
} |
||||
|
||||
QList<int> KTreeViewSearchLine::searchColumns() const |
||||
{ |
||||
if ( d->canChooseColumns ) |
||||
return d->searchColumns; |
||||
else |
||||
return QList<int>(); |
||||
} |
||||
|
||||
bool KTreeViewSearchLine::keepParentsVisible() const |
||||
{ |
||||
return d->keepParentsVisible; |
||||
} |
||||
|
||||
QTreeView *KTreeViewSearchLine::treeView() const |
||||
{ |
||||
if ( d->treeViews.count() == 1 ) |
||||
return d->treeViews.first(); |
||||
else |
||||
return 0; |
||||
} |
||||
|
||||
QList<QTreeView *> KTreeViewSearchLine::treeViews() const |
||||
{ |
||||
return d->treeViews; |
||||
} |
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// public slots
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void KTreeViewSearchLine::addTreeView( QTreeView *treeView ) |
||||
{ |
||||
if ( treeView ) { |
||||
connectTreeView( treeView ); |
||||
|
||||
d->treeViews.append( treeView ); |
||||
setEnabled( !d->treeViews.isEmpty() ); |
||||
|
||||
d->checkColumns(); |
||||
} |
||||
} |
||||
|
||||
void KTreeViewSearchLine::removeTreeView( QTreeView *treeView ) |
||||
{ |
||||
if ( treeView ) { |
||||
int index = d->treeViews.indexOf( treeView ); |
||||
|
||||
if ( index != -1 ) { |
||||
d->treeViews.removeAt( index ); |
||||
d->checkColumns(); |
||||
|
||||
disconnectTreeView( treeView ); |
||||
|
||||
setEnabled( !d->treeViews.isEmpty() ); |
||||
} |
||||
} |
||||
} |
||||
|
||||
void KTreeViewSearchLine::updateSearch( const QString &pattern ) |
||||
{ |
||||
d->search = pattern.isNull() ? text() : pattern; |
||||
|
||||
foreach ( QTreeView* treeView, d->treeViews ) |
||||
updateSearch( treeView ); |
||||
} |
||||
|
||||
void KTreeViewSearchLine::updateSearch( QTreeView *treeView ) |
||||
{ |
||||
if ( !treeView || !treeView->model()->rowCount() ) |
||||
return; |
||||
|
||||
|
||||
// If there's a selected item that is visible, make sure that it's visible
|
||||
// when the search changes too (assuming that it still matches).
|
||||
|
||||
QModelIndex currentIndex = treeView->currentIndex(); |
||||
|
||||
if ( d->keepParentsVisible ) |
||||
for ( int i = 0; i < treeView->model()->rowCount(); ++i ) |
||||
d->checkItemParentsVisible( treeView, treeView->rootIndex() ); |
||||
else |
||||
d->checkItemParentsNotVisible( treeView ); |
||||
|
||||
if ( currentIndex.isValid() ) |
||||
treeView->scrollTo( currentIndex ); |
||||
} |
||||
|
||||
void KTreeViewSearchLine::setCaseSensitivity( Qt::CaseSensitivity caseSensitive ) |
||||
{ |
||||
if ( d->caseSensitive != caseSensitive ) { |
||||
d->caseSensitive = caseSensitive; |
||||
updateSearch(); |
||||
} |
||||
} |
||||
|
||||
void KTreeViewSearchLine::setKeepParentsVisible( bool visible ) |
||||
{ |
||||
if ( d->keepParentsVisible != visible ) { |
||||
d->keepParentsVisible = visible; |
||||
updateSearch(); |
||||
} |
||||
} |
||||
|
||||
void KTreeViewSearchLine::setSearchColumns( const QList<int> &columns ) |
||||
{ |
||||
if ( d->canChooseColumns ) |
||||
d->searchColumns = columns; |
||||
} |
||||
|
||||
void KTreeViewSearchLine::setTreeView( QTreeView *treeView ) |
||||
{ |
||||
setTreeViews( QList<QTreeView *>() ); |
||||
addTreeView( treeView ); |
||||
} |
||||
|
||||
void KTreeViewSearchLine::setTreeViews( const QList<QTreeView *> &treeViews ) |
||||
{ |
||||
foreach ( QTreeView* treeView, d->treeViews ) |
||||
disconnectTreeView( treeView ); |
||||
|
||||
d->treeViews = treeViews; |
||||
|
||||
foreach ( QTreeView* treeView, d->treeViews ) |
||||
connectTreeView( treeView ); |
||||
|
||||
d->checkColumns(); |
||||
|
||||
setEnabled( !d->treeViews.isEmpty() ); |
||||
} |
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// protected members
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
bool KTreeViewSearchLine::itemMatches( const QModelIndex &index, int row, const QString &pattern ) const |
||||
{ |
||||
if ( pattern.isEmpty() ) |
||||
return true; |
||||
|
||||
if ( !index.isValid() ) |
||||
return false; |
||||
|
||||
// If the search column list is populated, search just the columns
|
||||
// specifified. If it is empty default to searching all of the columns.
|
||||
|
||||
if ( !d->searchColumns.isEmpty() ) { |
||||
QList<int>::ConstIterator it = d->searchColumns.begin(); |
||||
for ( ; it != d->searchColumns.end(); ++it ) { |
||||
if ( *it < index.model()->columnCount( index ) && |
||||
index.child( row, *it ).data( Qt::DisplayRole ).toString().indexOf( pattern, 0, d->caseSensitive ) >= 0 ) |
||||
return true; |
||||
} |
||||
} else { |
||||
for ( int i = 0; i < index.model()->columnCount( index ); ++i) { |
||||
if ( index.child( row, i ).data( Qt::DisplayRole ).toString().indexOf( pattern, 0, d->caseSensitive ) >= 0 ) |
||||
return true; |
||||
} |
||||
} |
||||
|
||||
return false; |
||||
} |
||||
|
||||
void KTreeViewSearchLine::contextMenuEvent( QContextMenuEvent *event ) |
||||
{ |
||||
QMenu *popup = KLineEdit::createStandardContextMenu(); |
||||
|
||||
if ( d->canChooseColumns ) { |
||||
popup->addSeparator(); |
||||
QMenu *subMenu = popup->addMenu( i18n("Search Columns") ); |
||||
|
||||
QAction* allVisibleColumnsAction = subMenu->addAction( i18n("All Visible Columns"), |
||||
this, SLOT( slotAllVisibleColumns() ) ); |
||||
allVisibleColumnsAction->setCheckable( true ); |
||||
allVisibleColumnsAction->setChecked( !d->searchColumns.count() ); |
||||
subMenu->addSeparator(); |
||||
|
||||
bool allColumnsAreSearchColumns = true; |
||||
|
||||
QActionGroup* group = new QActionGroup( popup ); |
||||
group->setExclusive( false ); |
||||
connect( group, SIGNAL( triggered( QAction* ) ), SLOT( slotColumnActivated( QAction* ) ) ); |
||||
|
||||
QHeaderView* const header = d->treeViews.first()->header(); |
||||
for ( int j = 0; j < header->count(); j++ ) { |
||||
int i = header->logicalIndex( j ); |
||||
|
||||
if ( header->isSectionHidden( i ) ) |
||||
continue; |
||||
|
||||
QString columnText = header->model()->headerData( i, Qt::Horizontal, Qt::DisplayRole ).toString(); |
||||
QAction* columnAction = subMenu->addAction( qvariant_cast<QIcon>( header->model()->headerData( i, Qt::Horizontal, Qt::DecorationRole ) ), columnText ); |
||||
columnAction->setCheckable( true ); |
||||
columnAction->setChecked( d->searchColumns.isEmpty() || d->searchColumns.contains( i ) ); |
||||
columnAction->setData( i ); |
||||
columnAction->setActionGroup( group ); |
||||
|
||||
if ( d->searchColumns.isEmpty() || d->searchColumns.indexOf( i ) != -1 ) |
||||
columnAction->setChecked( true ); |
||||
else |
||||
allColumnsAreSearchColumns = false; |
||||
} |
||||
|
||||
allVisibleColumnsAction->setChecked( allColumnsAreSearchColumns ); |
||||
|
||||
// searchColumnsMenuActivated() relies on one possible "all" representation
|
||||
if ( allColumnsAreSearchColumns && !d->searchColumns.isEmpty() ) |
||||
d->searchColumns.clear(); |
||||
} |
||||
|
||||
popup->exec( event->globalPos() ); |
||||
delete popup; |
||||
} |
||||
|
||||
void KTreeViewSearchLine::connectTreeView( QTreeView *treeView ) |
||||
{ |
||||
connect( treeView, SIGNAL( destroyed( QObject* ) ), |
||||
this, SLOT( treeViewDeleted( QObject* ) ) ); |
||||
|
||||
connect( treeView->model(), SIGNAL( rowsInserted( const QModelIndex&, int, int) ), |
||||
this, SLOT( rowsInserted( const QModelIndex&, int, int ) ) ); |
||||
} |
||||
|
||||
void KTreeViewSearchLine::disconnectTreeView( QTreeView *treeView ) |
||||
{ |
||||
disconnect( treeView, SIGNAL( destroyed( QObject* ) ), |
||||
this, SLOT( treeViewDeleted( QObject* ) ) ); |
||||
|
||||
disconnect( treeView->model(), SIGNAL( rowsInserted( const QModelIndex&, int, int) ), |
||||
this, SLOT( rowsInserted( const QModelIndex&, int, int ) ) ); |
||||
} |
||||
|
||||
bool KTreeViewSearchLine::canChooseColumnsCheck() |
||||
{ |
||||
// This is true if either of the following is true:
|
||||
|
||||
// there are no listviews connected
|
||||
if ( d->treeViews.isEmpty() ) |
||||
return false; |
||||
|
||||
const QTreeView *first = d->treeViews.first(); |
||||
|
||||
const int numcols = first->model()->columnCount(); |
||||
// the listviews have only one column,
|
||||
if ( numcols < 2 ) |
||||
return false; |
||||
|
||||
QStringList headers; |
||||
for ( int i = 0; i < numcols; ++i ) |
||||
headers.append( first->header()->model()->headerData( i, Qt::Horizontal, Qt::DisplayRole ).toString() ); |
||||
|
||||
QList<QTreeView *>::ConstIterator it = d->treeViews.constBegin(); |
||||
for ( ++it /* skip the first one */; it != d->treeViews.constEnd(); ++it ) { |
||||
// the listviews have different numbers of columns,
|
||||
if ( (*it)->model()->columnCount() != numcols ) |
||||
return false; |
||||
|
||||
// the listviews differ in column labels.
|
||||
QStringList::ConstIterator jt; |
||||
int i; |
||||
for ( i = 0, jt = headers.constBegin(); i < numcols; ++i, ++jt ) { |
||||
Q_ASSERT( jt != headers.constEnd() ); |
||||
|
||||
if ( (*it)->header()->model()->headerData( i, Qt::Horizontal, Qt::DisplayRole ).toString() != *jt ) |
||||
return false; |
||||
} |
||||
} |
||||
|
||||
return true; |
||||
} |
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// protected slots
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void KTreeViewSearchLine::queueSearch( const QString &search ) |
||||
{ |
||||
d->queuedSearches++; |
||||
d->search = search; |
||||
|
||||
QTimer::singleShot( 200, this, SLOT( activateSearch() ) ); |
||||
} |
||||
|
||||
void KTreeViewSearchLine::activateSearch() |
||||
{ |
||||
--(d->queuedSearches); |
||||
|
||||
if ( d->queuedSearches == 0 ) |
||||
updateSearch( d->search ); |
||||
} |
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// KTreeViewSearchLineWidget
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
class KTreeViewSearchLineWidget::Private |
||||
{ |
||||
public: |
||||
Private() |
||||
: treeView( 0 ), |
||||
searchLine( 0 ) |
||||
{ |
||||
} |
||||
|
||||
QTreeView *treeView; |
||||
KTreeViewSearchLine *searchLine; |
||||
}; |
||||
|
||||
KTreeViewSearchLineWidget::KTreeViewSearchLineWidget( QWidget *parent, QTreeView *treeView ) |
||||
: QWidget( parent ), d( new Private ) |
||||
{ |
||||
d->treeView = treeView; |
||||
|
||||
QTimer::singleShot( 0, this, SLOT( createWidgets() ) ); |
||||
} |
||||
|
||||
KTreeViewSearchLineWidget::~KTreeViewSearchLineWidget() |
||||
{ |
||||
delete d; |
||||
} |
||||
|
||||
KTreeViewSearchLine *KTreeViewSearchLineWidget::createSearchLine( QTreeView *treeView ) const |
||||
{ |
||||
return new KTreeViewSearchLine( const_cast<KTreeViewSearchLineWidget*>(this), treeView ); |
||||
} |
||||
|
||||
void KTreeViewSearchLineWidget::createWidgets() |
||||
{ |
||||
QLabel *label = new QLabel( i18n("S&earch:"), this ); |
||||
label->setObjectName( QLatin1String("kde toolbar widget") ); |
||||
|
||||
searchLine()->show(); |
||||
|
||||
label->setBuddy( d->searchLine ); |
||||
label->show(); |
||||
|
||||
QHBoxLayout* layout = new QHBoxLayout( this ); |
||||
layout->setSpacing( 5 ); |
||||
layout->setMargin( 0 ); |
||||
layout->addWidget( label ); |
||||
layout->addWidget( d->searchLine ); |
||||
} |
||||
|
||||
KTreeViewSearchLine *KTreeViewSearchLineWidget::searchLine() const |
||||
{ |
||||
if ( !d->searchLine ) |
||||
d->searchLine = createSearchLine( d->treeView ); |
||||
|
||||
return d->searchLine; |
||||
} |
||||
|
||||
#include "ktreeviewsearchline.moc" |
||||
@ -0,0 +1,314 @@ |
||||
/*
|
||||
Copyright (c) 2003 Scott Wheeler <wheeler@kde.org> |
||||
Copyright (c) 2005 Rafal Rzepecki <divide@users.sourceforge.net> |
||||
Copyright (c) 2006 Hamish Rodda <rodda@kde.org> |
||||
Copyright 2007 Pino Toscano <pino@kde.org> |
||||
|
||||
This library is free software; you can redistribute it and/or |
||||
modify it under the terms of the GNU Library General Public |
||||
License version 2 as published by the Free Software Foundation. |
||||
|
||||
This library is distributed in the hope that it will be useful, |
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
||||
Library General Public License for more details. |
||||
|
||||
You should have received a copy of the GNU Library General Public License |
||||
along with this library; see the file COPYING.LIB. If not, write to |
||||
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, |
||||
Boston, MA 02110-1301, USA. |
||||
*/ |
||||
|
||||
#ifndef KTREEVIEWSEARCHLINE_H |
||||
#define KTREEVIEWSEARCHLINE_H |
||||
|
||||
#include <klineedit.h> |
||||
|
||||
class QModelIndex; |
||||
class QTreeView; |
||||
|
||||
/**
|
||||
* This class makes it easy to add a search line for filtering the items in |
||||
* listviews based on a simple text search. |
||||
* |
||||
* No changes to the application other than instantiating this class with |
||||
* appropriate QTreeViews should be needed. |
||||
*/ |
||||
|
||||
class KTreeViewSearchLine : public KLineEdit |
||||
{ |
||||
Q_OBJECT |
||||
|
||||
Q_PROPERTY( Qt::CaseSensitivity caseSensitity READ caseSensitivity WRITE setCaseSensitivity ) |
||||
Q_PROPERTY( bool keepParentsVisible READ keepParentsVisible WRITE setKeepParentsVisible ) |
||||
|
||||
|
||||
public: |
||||
/**
|
||||
* Constructs a KTreeViewSearchLine with \a treeView being the QTreeView to |
||||
* be filtered. |
||||
* |
||||
* If \a treeView is null then the widget will be disabled until listviews |
||||
* are set with setTreeView(), setTreeViews() or added with addTreeView(). |
||||
*/ |
||||
explicit KTreeViewSearchLine( QWidget *parent = 0, QTreeView *treeView = 0 ); |
||||
|
||||
/**
|
||||
* Constructs a KTreeViewSearchLine with \a treeViews being the list of |
||||
* pointers to QTreeViews to be filtered. |
||||
* |
||||
* If \a treeViews is empty then the widget will be disabled until listviews |
||||
* are set with setTreeView(), setTreeViews() or added with addTreeView(). |
||||
*/ |
||||
KTreeViewSearchLine( QWidget *parent, const QList<QTreeView *> &treeViews ); |
||||
|
||||
|
||||
/**
|
||||
* Destroys the KTreeViewSearchLine. |
||||
*/ |
||||
virtual ~KTreeViewSearchLine(); |
||||
|
||||
/**
|
||||
* Returns true if the search is case sensitive. This defaults to false. |
||||
* |
||||
* @see setCaseSensitive() |
||||
*/ |
||||
Qt::CaseSensitivity caseSensitivity() const; |
||||
|
||||
/**
|
||||
* Returns the current list of columns that will be searched. If the |
||||
* returned list is empty all visible columns will be searched. |
||||
* |
||||
* @see setSearchColumns |
||||
*/ |
||||
QList<int> searchColumns() const; |
||||
|
||||
/**
|
||||
* If this is true (the default) then the parents of matched items will also |
||||
* be shown. |
||||
* |
||||
* @see setKeepParentsVisible() |
||||
*/ |
||||
bool keepParentsVisible() const; |
||||
|
||||
/**
|
||||
* Returns the listview that is currently filtered by the search. |
||||
* If there are multiple listviews filtered, it returns 0. |
||||
* |
||||
* @see setTreeView(), treeView() |
||||
*/ |
||||
QTreeView *treeView() const; |
||||
|
||||
/**
|
||||
* Returns the list of pointers to listviews that are currently filtered by |
||||
* the search. |
||||
* |
||||
* @see setTreeViews(), addTreeView(), treeView() |
||||
*/ |
||||
QList<QTreeView *> treeViews() const; |
||||
|
||||
public Q_SLOTS: |
||||
/**
|
||||
* Adds a QTreeView to the list of listviews filtered by this search line. |
||||
* If \a treeView is null then the widget will be disabled. |
||||
* |
||||
* @see treeView(), setTreeViews(), removeTreeView() |
||||
*/ |
||||
void addTreeView( QTreeView *treeView ); |
||||
|
||||
/**
|
||||
* Removes a QTreeView from the list of listviews filtered by this search |
||||
* line. Does nothing if \a treeView is 0 or is not filtered by the quick search |
||||
* line. |
||||
* |
||||
* @see listVew(), setTreeView(), addTreeView() |
||||
*/ |
||||
void removeTreeView( QTreeView *treeView ); |
||||
|
||||
/**
|
||||
* Updates search to only make visible the items that match \a pattern. If |
||||
* \a s is null then the line edit's text will be used. |
||||
*/ |
||||
virtual void updateSearch( const QString &pattern = QString() ); |
||||
|
||||
/**
|
||||
* Make the search case sensitive or case insensitive. |
||||
* |
||||
* @see caseSenstivity() |
||||
*/ |
||||
void setCaseSensitivity( Qt::CaseSensitivity caseSensitivity ); |
||||
|
||||
/**
|
||||
* When a search is active on a list that's organized into a tree view if |
||||
* a parent or ancesestor of an item is does not match the search then it |
||||
* will be hidden and as such so too will any children that match. |
||||
* |
||||
* If this is set to true (the default) then the parents of matching items |
||||
* will be shown. |
||||
* |
||||
* @see keepParentsVisible |
||||
*/ |
||||
void setKeepParentsVisible( bool value ); |
||||
|
||||
/**
|
||||
* Sets the list of columns to be searched. The default is to search all, |
||||
* visible columns which can be restored by passing \a columns as an empty |
||||
* list. |
||||
* If listviews to be filtered have different numbers or labels of columns |
||||
* this method has no effect. |
||||
* |
||||
* @see searchColumns |
||||
*/ |
||||
void setSearchColumns( const QList<int> &columns ); |
||||
|
||||
/**
|
||||
* Sets the QTreeView that is filtered by this search line, replacing any |
||||
* previously filtered listviews. If \a treeView is null then the widget will be |
||||
* disabled. |
||||
* |
||||
* @see treeView(), setTreeViews() |
||||
*/ |
||||
void setTreeView( QTreeView *treeView ); |
||||
|
||||
/**
|
||||
* Sets QTreeViews that are filtered by this search line, replacing any |
||||
* previously filtered listviews. If \a treeViews is empty then the widget will |
||||
* be disabled. |
||||
* |
||||
* @see treeViews(), addTreeView(), setTreeView() |
||||
*/ |
||||
void setTreeViews( const QList<QTreeView *> &treeViews ); |
||||
|
||||
|
||||
protected: |
||||
/**
|
||||
* Returns true if \a item matches the search \a pattern. This will be evaluated |
||||
* based on the value of caseSensitive(). This can be overridden in |
||||
* subclasses to implement more complicated matching schemes. |
||||
*/ |
||||
virtual bool itemMatches( const QModelIndex &item, int row, const QString &pattern ) const; |
||||
|
||||
/**
|
||||
* Re-implemented for internal reasons. API not affected. |
||||
*/ |
||||
virtual void contextMenuEvent( QContextMenuEvent* ); |
||||
|
||||
/**
|
||||
* Updates search to only make visible appropriate items in \a treeView. If |
||||
* \a treeView is null then nothing is done. |
||||
*/ |
||||
virtual void updateSearch( QTreeView *treeView ); |
||||
|
||||
/**
|
||||
* Connects signals of this listview to the appropriate slots of the search |
||||
* line. |
||||
*/ |
||||
virtual void connectTreeView( QTreeView* ); |
||||
|
||||
/**
|
||||
* Disconnects signals of a listviews from the search line. |
||||
*/ |
||||
virtual void disconnectTreeView( QTreeView* ); |
||||
|
||||
/**
|
||||
* Checks columns in all listviews and decides whether choosing columns to |
||||
* filter on makes any sense. |
||||
* |
||||
* Returns false if either of the following is true: |
||||
* * there are no listviews connected, |
||||
* * the listviews have different numbers of columns, |
||||
* * the listviews have only one column, |
||||
* * the listviews differ in column labels. |
||||
* |
||||
* Otherwise it returns true. |
||||
* |
||||
* @see setSearchColumns() |
||||
*/ |
||||
virtual bool canChooseColumnsCheck(); |
||||
|
||||
protected Q_SLOTS: |
||||
/**
|
||||
* When keys are pressed a new search string is created and a timer is |
||||
* activated. The most recent search is activated when this timer runs out |
||||
* if another key has not yet been pressed. |
||||
* |
||||
* This method makes @param search the most recent search and starts the |
||||
* timer. |
||||
* |
||||
* Together with activateSearch() this makes it such that searches are not |
||||
* started until there is a short break in the users typing. |
||||
* |
||||
* @see activateSearch() |
||||
*/ |
||||
void queueSearch( const QString &search ); |
||||
|
||||
/**
|
||||
* When the timer started with queueSearch() expires this slot is called. |
||||
* If there has been another timer started then this slot does nothing. |
||||
* However if there are no other pending searches this starts the list view |
||||
* search. |
||||
* |
||||
* @see queueSearch() |
||||
*/ |
||||
void activateSearch(); |
||||
|
||||
private: |
||||
class Private; |
||||
Private* const d; |
||||
|
||||
Q_PRIVATE_SLOT( d, void rowsInserted( const QModelIndex&, int, int ) const ) |
||||
Q_PRIVATE_SLOT( d, void treeViewDeleted( QObject* ) ) |
||||
Q_PRIVATE_SLOT( d, void slotColumnActivated( QAction* ) ) |
||||
Q_PRIVATE_SLOT( d, void slotAllVisibleColumns() ) |
||||
}; |
||||
|
||||
/**
|
||||
* Creates a widget featuring a KTreeViewSearchLine, a label with the text |
||||
* "Search" and a button to clear the search. |
||||
*/ |
||||
class KTreeViewSearchLineWidget : public QWidget |
||||
{ |
||||
Q_OBJECT |
||||
|
||||
public: |
||||
/**
|
||||
* Creates a KTreeViewSearchLineWidget for \a treeView with \a parent as the |
||||
* parent. |
||||
*/ |
||||
explicit KTreeViewSearchLineWidget( QWidget *parent = 0, QTreeView *treeView = 0 ); |
||||
|
||||
/**
|
||||
* Destroys the KTreeViewSearchLineWidget |
||||
*/ |
||||
~KTreeViewSearchLineWidget(); |
||||
|
||||
/**
|
||||
* Returns a pointer to the search line. |
||||
*/ |
||||
KTreeViewSearchLine *searchLine() const; |
||||
|
||||
protected Q_SLOTS: |
||||
/**
|
||||
* Creates the widgets inside of the widget. This is called from the |
||||
* constructor via a single shot timer so that it it guaranteed to run |
||||
* after construction is complete. This makes it suitable for overriding in |
||||
* subclasses. |
||||
*/ |
||||
virtual void createWidgets(); |
||||
|
||||
protected: |
||||
/**
|
||||
* Creates the search line. This can be useful to reimplement in cases where |
||||
* a KTreeViewSearchLine subclass is used. |
||||
* |
||||
* It is const because it is be called from searchLine(), which to the user |
||||
* doesn't conceptually alter the widget. |
||||
*/ |
||||
virtual KTreeViewSearchLine *createSearchLine( QTreeView *treeView ) const; |
||||
|
||||
private: |
||||
class Private; |
||||
Private* const d; |
||||
}; |
||||
|
||||
#endif |
||||
Loading…
Reference in new issue