• Skip to content
  • Skip to link menu
KDE 4.7 API Reference
  • KDE API Reference
  • kdelibs
  • KDE Home
  • Contact Us
 

KDEUI

ktreewidgetsearchline.cpp
Go to the documentation of this file.
00001 /* This file is part of the KDE libraries
00002    Copyright (c) 2003 Scott Wheeler <wheeler@kde.org>
00003    Copyright (c) 2005 Rafal Rzepecki <divide@users.sourceforge.net>
00004    Copyright (c) 2006 Hamish Rodda <rodda@kde.org>
00005 
00006    This library is free software; you can redistribute it and/or
00007    modify it under the terms of the GNU Library General Public
00008    License version 2 as published by the Free Software Foundation.
00009 
00010    This library is distributed in the hope that it will be useful,
00011    but WITHOUT ANY WARRANTY; without even the implied warranty of
00012    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00013    Library General Public License for more details.
00014 
00015    You should have received a copy of the GNU Library General Public License
00016    along with this library; see the file COPYING.LIB.  If not, write to
00017    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
00018    Boston, MA 02110-1301, USA.
00019 */
00020 
00021 #include "ktreewidgetsearchline.h"
00022 
00023 #include <QtCore/QList>
00024 #include <QtCore/QTimer>
00025 #include <QtGui/QApplication>
00026 #include <QtGui/QContextMenuEvent>
00027 #include <QtGui/QHBoxLayout>
00028 #include <QtGui/QHeaderView>
00029 #include <QtGui/QLabel>
00030 #include <QtGui/QMenu>
00031 #include <QtGui/QToolButton>
00032 #include <QtGui/QTreeWidget>
00033 
00034 #include <kdebug.h>
00035 #include <klocale.h>
00036 
00037 class KTreeWidgetSearchLine::Private
00038 {
00039   public:
00040     Private( KTreeWidgetSearchLine *_q )
00041       : q( _q ),
00042         caseSensitive( Qt::CaseInsensitive ),
00043         keepParentsVisible( true ),
00044         canChooseColumns( true ),
00045         queuedSearches( 0 )
00046     {
00047     }
00048 
00049     KTreeWidgetSearchLine *q;
00050     QList<QTreeWidget *> treeWidgets;
00051     Qt::CaseSensitivity caseSensitive;
00052     bool keepParentsVisible;
00053     bool canChooseColumns;
00054     QString search;
00055     int queuedSearches;
00056     QList<int> searchColumns;
00057 
00058     void _k_rowsInserted(const QModelIndex & parent, int start, int end) const;
00059     void _k_treeWidgetDeleted( QObject *treeWidget );
00060     void _k_slotColumnActivated(QAction* action);
00061     void _k_slotAllVisibleColumns();
00062     void _k_queueSearch(const QString&);
00063     void _k_activateSearch();
00064 
00065     void checkColumns();
00066     void checkItemParentsNotVisible(QTreeWidget *treeWidget);
00067     bool checkItemParentsVisible(QTreeWidgetItem* item);
00068 };
00069 
00071 // private slots
00073 
00074 // Hack to make a protected method public
00075 class QTreeWidgetWorkaround : public QTreeWidget
00076 {
00077   public:
00078     QTreeWidgetItem *itemFromIndex( const QModelIndex &index ) const
00079     {
00080       return QTreeWidget::itemFromIndex( index );
00081     }
00082 };
00083 
00084 void KTreeWidgetSearchLine::Private::_k_rowsInserted( const QModelIndex & parentIndex, int start, int end ) const
00085 {
00086   QAbstractItemModel* model = qobject_cast<QAbstractItemModel*>( q->sender() );
00087   if ( !model )
00088     return;
00089 
00090   QTreeWidget* widget = 0L;
00091   foreach ( QTreeWidget* tree, treeWidgets )
00092     if ( tree->model() == model ) {
00093       widget = tree;
00094       break;
00095     }
00096 
00097   if ( !widget )
00098     return;
00099 
00100   QTreeWidgetWorkaround* widgetW = static_cast<QTreeWidgetWorkaround *>(widget);
00101   for (int i = start; i <= end; ++i) {
00102       if (QTreeWidgetItem *item = widgetW->itemFromIndex(model->index(i, 0, parentIndex))) {
00103           bool newHidden = !q->itemMatches(item, q->text());
00104           if (item->isHidden() != newHidden) {
00105               item->setHidden(newHidden);
00106               emit q->hiddenChanged(item, newHidden);
00107           }
00108       }
00109   }
00110 }
00111 
00112 void KTreeWidgetSearchLine::Private::_k_treeWidgetDeleted( QObject *object )
00113 {
00114   treeWidgets.removeAll( static_cast<QTreeWidget *>( object ) );
00115   q->setEnabled( treeWidgets.isEmpty() );
00116 }
00117 
00118 void KTreeWidgetSearchLine::Private::_k_slotColumnActivated( QAction *action )
00119 {
00120   if ( !action )
00121     return;
00122 
00123   bool ok;
00124   int column = action->data().toInt( &ok );
00125 
00126   if ( !ok )
00127     return;
00128 
00129   if ( action->isChecked() ) {
00130     if ( !searchColumns.isEmpty() ) {
00131       if ( !searchColumns.contains( column ) )
00132         searchColumns.append( column );
00133 
00134       if ( searchColumns.count() == treeWidgets.first()->header()->count() - treeWidgets.first()->header()->hiddenSectionCount() )
00135         searchColumns.clear();
00136 
00137     } else {
00138       searchColumns.append( column );
00139     }
00140   } else {
00141     if ( searchColumns.isEmpty() ) {
00142       QHeaderView* const header = treeWidgets.first()->header();
00143 
00144       for ( int i = 0; i < header->count(); i++ ) {
00145         if ( i != column && !header->isSectionHidden( i ) )
00146           searchColumns.append( i );
00147       }
00148 
00149     } else if ( searchColumns.contains( column ) ) {
00150       searchColumns.removeAll( column );
00151     }
00152   }
00153 
00154   q->updateSearch();
00155 }
00156 
00157 void KTreeWidgetSearchLine::Private::_k_slotAllVisibleColumns()
00158 {
00159   if ( searchColumns.isEmpty() )
00160     searchColumns.append( 0 );
00161   else
00162     searchColumns.clear();
00163 
00164   q->updateSearch();
00165 }
00166 
00168 // private methods
00170 
00171 
00172 void KTreeWidgetSearchLine::Private::checkColumns()
00173 {
00174   canChooseColumns = q->canChooseColumnsCheck();
00175 }
00176 
00177 void KTreeWidgetSearchLine::Private::checkItemParentsNotVisible(QTreeWidget *treeWidget)
00178 {
00179     for (QTreeWidgetItemIterator it(treeWidget); *it; ++it) {
00180         QTreeWidgetItem *item = *it;
00181         bool newHidden = !q->itemMatches(item, search);
00182         if (item->isHidden() != newHidden) {
00183             item->setHidden(newHidden);
00184             emit q->hiddenChanged(item, newHidden);
00185         }
00186     }
00187 }
00188 
00196 bool KTreeWidgetSearchLine::Private::checkItemParentsVisible(QTreeWidgetItem *item)
00197 {
00198     bool childMatch = false;
00199     for (int i = 0; i < item->childCount(); ++i) {
00200         childMatch |= checkItemParentsVisible(item->child(i));
00201     }
00202 
00203     // Should this item be shown? It should if any children should be, or if it matches.
00204     bool newHidden = !childMatch && !q->itemMatches(item, search);
00205     if (item->isHidden() != newHidden) {
00206         item->setHidden(newHidden);
00207         emit q->hiddenChanged(item, newHidden);
00208     }
00209 
00210     return !newHidden;
00211 }
00212 
00213 
00215 // public methods
00217 
00218 KTreeWidgetSearchLine::KTreeWidgetSearchLine( QWidget *q, QTreeWidget *treeWidget )
00219   : KLineEdit( q ), d( new Private( this ) )
00220 {
00221   connect( this, SIGNAL( textChanged( const QString& ) ),
00222            this, SLOT( _k_queueSearch( const QString& ) ) );
00223 
00224   setClearButtonShown( true );
00225   setTreeWidget( treeWidget );
00226 
00227   if ( !treeWidget ) {
00228       setEnabled( false );
00229   }
00230 }
00231 
00232 KTreeWidgetSearchLine::KTreeWidgetSearchLine( QWidget *q,
00233                                               const QList<QTreeWidget *> &treeWidgets )
00234   : KLineEdit( q ), d( new Private( this ) )
00235 {
00236   connect( this, SIGNAL( textChanged( const QString& ) ),
00237            this, SLOT( _k_queueSearch( const QString& ) ) );
00238 
00239   setClearButtonShown( true );
00240   setTreeWidgets( treeWidgets );
00241 }
00242 
00243 KTreeWidgetSearchLine::~KTreeWidgetSearchLine()
00244 {
00245   delete d;
00246 }
00247 
00248 Qt::CaseSensitivity KTreeWidgetSearchLine::caseSensitivity() const
00249 {
00250   return d->caseSensitive;
00251 }
00252 
00253 QList<int> KTreeWidgetSearchLine::searchColumns() const
00254 {
00255   if ( d->canChooseColumns )
00256     return d->searchColumns;
00257   else
00258     return QList<int>();
00259 }
00260 
00261 bool KTreeWidgetSearchLine::keepParentsVisible() const
00262 {
00263   return d->keepParentsVisible;
00264 }
00265 
00266 QTreeWidget *KTreeWidgetSearchLine::treeWidget() const
00267 {
00268   if ( d->treeWidgets.count() == 1 )
00269     return d->treeWidgets.first();
00270   else
00271     return 0;
00272 }
00273 
00274 QList<QTreeWidget *> KTreeWidgetSearchLine::treeWidgets() const
00275 {
00276   return d->treeWidgets;
00277 }
00278 
00279 
00281 // public slots
00283 
00284 void KTreeWidgetSearchLine::addTreeWidget( QTreeWidget *treeWidget )
00285 {
00286   if ( treeWidget ) {
00287     connectTreeWidget( treeWidget );
00288 
00289     d->treeWidgets.append( treeWidget );
00290     setEnabled( !d->treeWidgets.isEmpty() );
00291 
00292     d->checkColumns();
00293   }
00294 }
00295 
00296 void KTreeWidgetSearchLine::removeTreeWidget( QTreeWidget *treeWidget )
00297 {
00298   if ( treeWidget ) {
00299     int index = d->treeWidgets.indexOf( treeWidget );
00300 
00301     if ( index != -1 ) {
00302       d->treeWidgets.removeAt( index );
00303       d->checkColumns();
00304 
00305       disconnectTreeWidget( treeWidget );
00306 
00307       setEnabled( !d->treeWidgets.isEmpty() );
00308     }
00309   }
00310 }
00311 
00312 void KTreeWidgetSearchLine::updateSearch( const QString &pattern )
00313 {
00314   d->search = pattern.isNull() ? text() : pattern;
00315 
00316   foreach ( QTreeWidget* treeWidget, d->treeWidgets )
00317     updateSearch( treeWidget );
00318 }
00319 
00320 void KTreeWidgetSearchLine::updateSearch( QTreeWidget *treeWidget )
00321 {
00322   if ( !treeWidget || !treeWidget->topLevelItemCount() )
00323     return;
00324 
00325 
00326   // If there's a selected item that is visible, make sure that it's visible
00327   // when the search changes too (assuming that it still matches).
00328 
00329   QTreeWidgetItem *currentItem = treeWidget->currentItem();
00330 
00331   if ( d->keepParentsVisible )
00332     for ( int i = 0; i < treeWidget->topLevelItemCount(); ++i )
00333       d->checkItemParentsVisible( treeWidget->topLevelItem( i ) );
00334   else
00335     d->checkItemParentsNotVisible( treeWidget );
00336 
00337   if ( currentItem )
00338     treeWidget->scrollToItem( currentItem );
00339 }
00340 
00341 void KTreeWidgetSearchLine::setCaseSensitivity( Qt::CaseSensitivity caseSensitive )
00342 {
00343   if ( d->caseSensitive != caseSensitive ) {
00344     d->caseSensitive = caseSensitive;
00345     updateSearch();
00346   }
00347 }
00348 
00349 void KTreeWidgetSearchLine::setKeepParentsVisible( bool visible )
00350 {
00351   if ( d->keepParentsVisible != visible ) {
00352     d->keepParentsVisible = visible;
00353     updateSearch();
00354   }
00355 }
00356 
00357 void KTreeWidgetSearchLine::setSearchColumns( const QList<int> &columns )
00358 {
00359   if ( d->canChooseColumns )
00360     d->searchColumns = columns;
00361 }
00362 
00363 void KTreeWidgetSearchLine::setTreeWidget( QTreeWidget *treeWidget )
00364 {
00365   setTreeWidgets( QList<QTreeWidget *>() );
00366   addTreeWidget( treeWidget );
00367 }
00368 
00369 void KTreeWidgetSearchLine::setTreeWidgets( const QList<QTreeWidget *> &treeWidgets )
00370 {
00371   foreach ( QTreeWidget* treeWidget, d->treeWidgets )
00372     disconnectTreeWidget( treeWidget );
00373 
00374   d->treeWidgets = treeWidgets;
00375 
00376   foreach ( QTreeWidget* treeWidget, d->treeWidgets )
00377     connectTreeWidget( treeWidget );
00378 
00379   d->checkColumns();
00380 
00381   setEnabled( !d->treeWidgets.isEmpty() );
00382 }
00383 
00385 // protected members
00387 
00388 bool KTreeWidgetSearchLine::itemMatches( const QTreeWidgetItem *item, const QString &pattern ) const
00389 {
00390   if ( pattern.isEmpty() )
00391     return true;
00392 
00393   // If the search column list is populated, search just the columns
00394   // specified.  If it is empty default to searching all of the columns.
00395 
00396   if ( !d->searchColumns.isEmpty() ) {
00397     QList<int>::ConstIterator it = d->searchColumns.constBegin();
00398     for ( ; it != d->searchColumns.constEnd(); ++it ) {
00399       if ( *it < item->treeWidget()->columnCount() &&
00400            item->text( *it ).indexOf( pattern, 0, d->caseSensitive ) >= 0 )
00401         return true;
00402     }
00403   } else {
00404     for ( int i = 0; i < item->treeWidget()->columnCount(); i++) {
00405       if ( item->treeWidget()->columnWidth(i) > 0 &&
00406            item->text( i ).indexOf( pattern, 0, d->caseSensitive ) >= 0 )
00407         return true;
00408     }
00409   }
00410 
00411   return false;
00412 }
00413 
00414 void KTreeWidgetSearchLine::contextMenuEvent( QContextMenuEvent *event )
00415 {
00416   QMenu *popup = KLineEdit::createStandardContextMenu();
00417 
00418   if ( d->canChooseColumns ) {
00419     popup->addSeparator();
00420     QMenu *subMenu = popup->addMenu( i18n("Search Columns") );
00421 
00422     QAction* allVisibleColumnsAction = subMenu->addAction( i18n("All Visible Columns"),
00423                                                            this, SLOT( _k_slotAllVisibleColumns() ) );
00424     allVisibleColumnsAction->setCheckable( true );
00425     allVisibleColumnsAction->setChecked( !d->searchColumns.count() );
00426     subMenu->addSeparator();
00427 
00428     bool allColumnsAreSearchColumns = true;
00429 
00430     QActionGroup* group = new QActionGroup( popup );
00431     group->setExclusive( false );
00432     connect( group, SIGNAL( triggered( QAction* ) ), SLOT( _k_slotColumnActivated( QAction* ) ) );
00433 
00434     QHeaderView* const header = d->treeWidgets.first()->header();
00435     for ( int j = 0; j < header->count(); j++ ) {
00436       int i = header->logicalIndex( j );
00437 
00438       if ( header->isSectionHidden( i ) )
00439         continue;
00440 
00441       QString columnText = d->treeWidgets.first()->headerItem()->text( i );
00442       QAction* columnAction = subMenu->addAction( d->treeWidgets.first()->headerItem()->icon( i ), columnText );
00443       columnAction->setCheckable( true );
00444       columnAction->setChecked( d->searchColumns.isEmpty() || d->searchColumns.contains( i ) );
00445       columnAction->setData( i );
00446       columnAction->setActionGroup( group );
00447 
00448       if ( d->searchColumns.isEmpty() || d->searchColumns.indexOf( i ) != -1 )
00449         columnAction->setChecked( true );
00450       else
00451         allColumnsAreSearchColumns = false;
00452     }
00453 
00454     allVisibleColumnsAction->setChecked( allColumnsAreSearchColumns );
00455 
00456     // searchColumnsMenuActivated() relies on one possible "all" representation
00457     if ( allColumnsAreSearchColumns && !d->searchColumns.isEmpty() )
00458       d->searchColumns.clear();
00459   }
00460 
00461   popup->exec( event->globalPos() );
00462   delete popup;
00463 }
00464 
00465 void KTreeWidgetSearchLine::connectTreeWidget( QTreeWidget *treeWidget )
00466 {
00467   connect( treeWidget, SIGNAL( destroyed( QObject* ) ),
00468            this, SLOT( _k_treeWidgetDeleted( QObject* ) ) );
00469 
00470   connect( treeWidget->model(), SIGNAL( rowsInserted( const QModelIndex&, int, int) ),
00471            this, SLOT( _k_rowsInserted( const QModelIndex&, int, int ) ) );
00472 }
00473 
00474 void KTreeWidgetSearchLine::disconnectTreeWidget( QTreeWidget *treeWidget )
00475 {
00476   disconnect( treeWidget, SIGNAL( destroyed( QObject* ) ),
00477               this, SLOT( _k_treeWidgetDeleted( QObject* ) ) );
00478 
00479   disconnect( treeWidget->model(), SIGNAL( rowsInserted( const QModelIndex&, int, int) ),
00480               this, SLOT( _k_rowsInserted( const QModelIndex&, int, int ) ) );
00481 }
00482 
00483 bool KTreeWidgetSearchLine::canChooseColumnsCheck()
00484 {
00485   // This is true if either of the following is true:
00486 
00487   // there are no listviews connected
00488   if ( d->treeWidgets.isEmpty() )
00489     return false;
00490 
00491   const QTreeWidget *first = d->treeWidgets.first();
00492 
00493   const unsigned int numcols = first->columnCount();
00494   // the listviews have only one column,
00495   if ( numcols < 2 )
00496     return false;
00497 
00498   QStringList headers;
00499   for ( unsigned int i = 0; i < numcols; ++i )
00500     headers.append( first->headerItem()->text( i ) );
00501 
00502   QList<QTreeWidget *>::ConstIterator it = d->treeWidgets.constBegin();
00503   for ( ++it /* skip the first one */; it != d->treeWidgets.constEnd(); ++it ) {
00504     // the listviews have different numbers of columns,
00505     if ( (unsigned int) (*it)->columnCount() != numcols )
00506       return false;
00507 
00508     // the listviews differ in column labels.
00509     QStringList::ConstIterator jt;
00510     unsigned int i;
00511     for ( i = 0, jt = headers.constBegin(); i < numcols; ++i, ++jt ) {
00512       Q_ASSERT( jt != headers.constEnd() );
00513 
00514       if ( (*it)->headerItem()->text( i ) != *jt )
00515         return false;
00516     }
00517   }
00518 
00519   return true;
00520 }
00521 
00522 bool KTreeWidgetSearchLine::event(QEvent *event) {
00523 
00524     if (event->type() == QEvent::KeyPress) {
00525         QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event);
00526         if(keyEvent->matches(QKeySequence::MoveToNextLine) || keyEvent->matches(QKeySequence::SelectNextLine) ||
00527            keyEvent->matches(QKeySequence::MoveToPreviousLine) || keyEvent->matches(QKeySequence::SelectPreviousLine) ||
00528            keyEvent->matches(QKeySequence::MoveToNextPage) ||  keyEvent->matches(QKeySequence::SelectNextPage) ||
00529            keyEvent->matches(QKeySequence::MoveToPreviousPage) ||  keyEvent->matches(QKeySequence::SelectPreviousPage) ||
00530            keyEvent->key() == Qt::Key_Enter || keyEvent->key() == Qt::Key_Return)
00531         {
00532             QTreeWidget *first = d->treeWidgets.first();
00533             if(first) {
00534                 QApplication::sendEvent(first, event);
00535                 return true;
00536             }
00537         }
00538     }
00539     return KLineEdit::event(event);
00540 }
00541 
00543 // protected slots
00545 
00546 void KTreeWidgetSearchLine::Private::_k_queueSearch( const QString &_search )
00547 {
00548   queuedSearches++;
00549   search = _search;
00550 
00551   QTimer::singleShot( 200, q, SLOT( _k_activateSearch() ) );
00552 }
00553 
00554 void KTreeWidgetSearchLine::Private::_k_activateSearch()
00555 {
00556   --queuedSearches;
00557 
00558   if ( queuedSearches == 0 )
00559     q->updateSearch( search );
00560 }
00561 
00563 // KTreeWidgetSearchLineWidget
00565 
00566 class KTreeWidgetSearchLineWidget::Private
00567 {
00568   public:
00569     Private()
00570       : treeWidget( 0 ),
00571         searchLine( 0 )
00572     {
00573     }
00574 
00575     QTreeWidget *treeWidget;
00576     KTreeWidgetSearchLine *searchLine;
00577 };
00578 
00579 KTreeWidgetSearchLineWidget::KTreeWidgetSearchLineWidget( QWidget *parent, QTreeWidget *treeWidget )
00580   : QWidget( parent ), d( new Private )
00581 {
00582   d->treeWidget = treeWidget;
00583 
00584   // can't call createWidgets directly because it calls virtual functions
00585   // that might not work if called directly from here due to how inheritance works
00586   QMetaObject::invokeMethod(this, "createWidgets", Qt::QueuedConnection);
00587 }
00588 
00589 KTreeWidgetSearchLineWidget::~KTreeWidgetSearchLineWidget()
00590 {
00591   delete d;
00592 }
00593 
00594 KTreeWidgetSearchLine *KTreeWidgetSearchLineWidget::createSearchLine( QTreeWidget *treeWidget ) const
00595 {
00596   return new KTreeWidgetSearchLine( const_cast<KTreeWidgetSearchLineWidget*>(this), treeWidget );
00597 }
00598 
00599 void KTreeWidgetSearchLineWidget::createWidgets()
00600 {
00601   QLabel *label = new QLabel( i18n("S&earch:"), this );
00602 
00603   searchLine()->show();
00604 
00605   label->setBuddy( d->searchLine );
00606   label->show();
00607 
00608   QHBoxLayout* layout = new QHBoxLayout( this );
00609   layout->setMargin( 0 );
00610   layout->addWidget( label );
00611   layout->addWidget( d->searchLine );
00612   setFocusProxy( searchLine() );
00613 }
00614 
00615 KTreeWidgetSearchLine *KTreeWidgetSearchLineWidget::searchLine() const
00616 {
00617   if ( !d->searchLine )
00618     d->searchLine = createSearchLine( d->treeWidget );
00619 
00620   return d->searchLine;
00621 }
00622 
00623 #include "ktreewidgetsearchline.moc"

KDEUI

Skip menu "KDEUI"
  • Main Page
  • Namespace List
  • Namespace Members
  • Alphabetical List
  • Class List
  • Class Hierarchy
  • Class Members
  • File List
  • File Members
  • Modules
  • Related Pages

kdelibs

Skip menu "kdelibs"
  • DNSSD
  • Interfaces
  •   KHexEdit
  •   KMediaPlayer
  •   KSpeech
  •   KTextEditor
  • kconf_update
  • KDE3Support
  •   KUnitTest
  • KDECore
  • KDED
  • KDEsu
  • KDEUI
  • KDEWebKit
  • KDocTools
  • KFile
  • KHTML
  • KImgIO
  • KInit
  • kio
  • KIOSlave
  • KJS
  •   KJS-API
  •   WTF
  • kjsembed
  • KNewStuff
  • KParts
  • KPty
  • Kross
  • KUnitConversion
  • KUtils
  • Nepomuk
  • Plasma
  • Solid
  • Sonnet
  • ThreadWeaver
Generated for kdelibs by doxygen 1.7.5
This website is maintained by Adriaan de Groot and Allen Winter.
KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal