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

KDEUI

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

kdelibs

Skip menu "kdelibs"
  • DNSSD
  • Interfaces
  •   KHexEdit
  •   KMediaPlayer
  •   KSpeech
  •   KTextEditor
  • Kate
  • kconf_update
  • KDE3Support
  •   KUnitTest
  • KDECore
  • KDED
  • KDEsu
  • KDEUI
  • KDocTools
  • KFile
  • KHTML
  • KImgIO
  • KInit
  • kio
  • KIOSlave
  • KJS
  •   KJS-API
  •   WTF
  • kjsembed
  • KNewStuff
  • KParts
  • KPty
  • Kross
  • KUtils
  • Nepomuk
  • Plasma
  • Solid
  • Sonnet
  • ThreadWeaver
Generated for kdelibs by doxygen 1.6.1
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