KDE3Support
k3listviewsearchline.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 00005 This library is free software; you can redistribute it and/or 00006 modify it under the terms of the GNU Library General Public 00007 License version 2 as published by the Free Software Foundation. 00008 00009 This library is distributed in the hope that it will be useful, 00010 but WITHOUT ANY WARRANTY; without even the implied warranty of 00011 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00012 Library General Public License for more details. 00013 00014 You should have received a copy of the GNU Library General Public License 00015 along with this library; see the file COPYING.LIB. If not, write to 00016 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 00017 Boston, MA 02110-1301, USA. 00018 */ 00019 00020 #include "k3listviewsearchline.h" 00021 00022 #include <k3listview.h> 00023 #include <kiconloader.h> 00024 #include <ktoolbar.h> 00025 #include <kdebug.h> 00026 #include <klocale.h> 00027 00028 #include <QApplication> 00029 #include <QTimer> 00030 #include <QMenu> 00031 #include <QLabel> 00032 #include <QContextMenuEvent> 00033 #include <QList> 00034 #include <Q3Header> 00035 #include <QToolButton> 00036 00037 class K3ListViewSearchLine::K3ListViewSearchLinePrivate 00038 { 00039 public: 00040 K3ListViewSearchLinePrivate() : 00041 caseSensitive(Qt::CaseInsensitive), 00042 activeSearch(false), 00043 keepParentsVisible(true), 00044 canChooseColumns(true), 00045 queuedSearches(0), 00046 allVisibleColumnsAction(0) 00047 {} 00048 00049 QList<K3ListView *> listViews; 00050 Qt::CaseSensitivity caseSensitive; 00051 bool activeSearch; 00052 bool keepParentsVisible; 00053 bool canChooseColumns; 00054 QString search; 00055 int queuedSearches; 00056 QList<int> searchColumns; 00057 QAction *allVisibleColumnsAction; 00058 }; 00059 00061 // public methods 00063 00064 K3ListViewSearchLine::K3ListViewSearchLine(QWidget *parent, K3ListView *listView) : 00065 KLineEdit(parent) 00066 { 00067 d = new K3ListViewSearchLinePrivate; 00068 setClearButtonShown( true ); 00069 00070 connect(this, SIGNAL(textChanged(const QString &)), 00071 this, SLOT(queueSearch(const QString &))); 00072 00073 setListView( listView ); 00074 if( !listView) 00075 setEnabled(false); 00076 } 00077 00078 K3ListViewSearchLine::K3ListViewSearchLine(QWidget *parent, 00079 const QList<K3ListView *> &listViews) : 00080 KLineEdit(parent) 00081 { 00082 d = new K3ListViewSearchLinePrivate; 00083 setClearButtonShown( true ); 00084 00085 connect(this, SIGNAL(textChanged(const QString &)), 00086 this, SLOT(queueSearch(const QString &))); 00087 00088 setListViews( listViews ); 00089 } 00090 00091 00092 K3ListViewSearchLine::~K3ListViewSearchLine() 00093 { 00094 delete d; 00095 } 00096 00097 bool K3ListViewSearchLine::caseSensitive() const 00098 { 00099 return (d->caseSensitive ==Qt::CaseSensitive); 00100 } 00101 00102 QList<int> K3ListViewSearchLine::searchColumns() const 00103 { 00104 if (d->canChooseColumns) 00105 return d->searchColumns; 00106 else 00107 return QList<int>(); 00108 } 00109 00110 bool K3ListViewSearchLine::keepParentsVisible() const 00111 { 00112 return d->keepParentsVisible; 00113 } 00114 00115 K3ListView *K3ListViewSearchLine::listView() const 00116 { 00117 if ( d->listViews.count() == 1 ) 00118 return d->listViews.first(); 00119 else 00120 return 0; 00121 } 00122 00123 const QList<K3ListView *> &K3ListViewSearchLine::listViews() const 00124 { 00125 return d->listViews; 00126 } 00127 00128 00130 // public slots 00132 00133 void K3ListViewSearchLine::addListView(K3ListView *lv) 00134 { 00135 if (lv) { 00136 connectListView(lv); 00137 00138 d->listViews.append(lv); 00139 setEnabled(!d->listViews.isEmpty()); 00140 00141 checkColumns(); 00142 } 00143 } 00144 00145 void K3ListViewSearchLine::removeListView(K3ListView *lv) 00146 { 00147 if (lv) { 00148 int idx = d->listViews.indexOf(lv); 00149 00150 if ( idx != -1 ) { 00151 d->listViews.removeAt( idx ); 00152 checkColumns(); 00153 00154 disconnectListView(lv); 00155 00156 setEnabled(!d->listViews.isEmpty()); 00157 } 00158 } 00159 } 00160 00161 void K3ListViewSearchLine::updateSearch(const QString &s) 00162 { 00163 d->search = s.isNull() ? text() : s; 00164 00165 for (QList<K3ListView *>::Iterator it = d->listViews.begin(); 00166 it != d->listViews.end(); ++it) 00167 updateSearch( *it ); 00168 } 00169 00170 void K3ListViewSearchLine::updateSearch(K3ListView *listView) 00171 { 00172 if(!listView) 00173 return; 00174 00175 00176 // If there's a selected item that is visible, make sure that it's visible 00177 // when the search changes too (assuming that it still matches). 00178 00179 Q3ListViewItem *currentItem = 0; 00180 00181 switch(listView->selectionMode()) 00182 { 00183 case K3ListView::NoSelection: 00184 break; 00185 case K3ListView::Single: 00186 currentItem = listView->selectedItem(); 00187 break; 00188 default: 00189 { 00190 int flags = Q3ListViewItemIterator::Selected | Q3ListViewItemIterator::Visible; 00191 for(Q3ListViewItemIterator it(listView, flags); 00192 it.current() && !currentItem; 00193 ++it) 00194 { 00195 if(listView->itemRect(it.current()).isValid()) 00196 currentItem = it.current(); 00197 } 00198 } 00199 } 00200 00201 if(d->keepParentsVisible) 00202 checkItemParentsVisible(listView->firstChild()); 00203 else 00204 checkItemParentsNotVisible(listView); 00205 00206 if(currentItem) 00207 listView->ensureItemVisible(currentItem); 00208 } 00209 00210 void K3ListViewSearchLine::setCaseSensitive(bool cs) 00211 { 00212 d->caseSensitive = cs?Qt::CaseSensitive:Qt::CaseInsensitive; 00213 } 00214 00215 void K3ListViewSearchLine::setKeepParentsVisible(bool v) 00216 { 00217 d->keepParentsVisible = v; 00218 } 00219 00220 void K3ListViewSearchLine::setSearchColumns(const QList<int> &columns) 00221 { 00222 if (d->canChooseColumns) 00223 d->searchColumns = columns; 00224 } 00225 00226 void K3ListViewSearchLine::setListView(K3ListView *lv) 00227 { 00228 setListViews(QList<K3ListView *>()); 00229 addListView(lv); 00230 } 00231 00232 void K3ListViewSearchLine::setListViews(const QList<K3ListView *> &lv) 00233 { 00234 for (QList<K3ListView *>::Iterator it = d->listViews.begin(); 00235 it != d->listViews.end(); ++it) 00236 disconnectListView(*it); 00237 00238 d->listViews = lv; 00239 00240 for (QList<K3ListView *>::Iterator it = d->listViews.begin(); 00241 it != d->listViews.end(); ++it) 00242 connectListView(*it); 00243 00244 checkColumns(); 00245 setEnabled(!d->listViews.isEmpty()); 00246 } 00247 00249 // protected members 00251 00252 bool K3ListViewSearchLine::itemMatches(const Q3ListViewItem *item, const QString &s) const 00253 { 00254 if(s.isEmpty()) 00255 return true; 00256 00257 // If the search column list is populated, search just the columns 00258 // specifified. If it is empty default to searching all of the columns. 00259 00260 if(!d->searchColumns.isEmpty()) { 00261 QList<int>::ConstIterator it = d->searchColumns.constBegin(); 00262 for(; it != d->searchColumns.constEnd(); ++it) { 00263 if(*it < item->listView()->columns() && 00264 item->text(*it).indexOf(s, 0, d->caseSensitive) >= 0) 00265 return true; 00266 } 00267 } 00268 else { 00269 for(int i = 0; i < item->listView()->columns(); i++) { 00270 if(item->listView()->columnWidth(i) > 0 && 00271 item->text(i).indexOf(s, 0, d->caseSensitive) >= 0) 00272 { 00273 return true; 00274 } 00275 } 00276 } 00277 00278 return false; 00279 } 00280 00281 void K3ListViewSearchLine::contextMenuEvent( QContextMenuEvent*e ) 00282 { 00283 QMenu *popup = KLineEdit::createStandardContextMenu(); 00284 00285 if (d->canChooseColumns) { 00286 popup->addSeparator(); 00287 QMenu *subMenu = popup->addMenu(i18n("Search Columns")); 00288 connect(subMenu, SIGNAL(triggered(QAction*)), this, SLOT(searchColumnsMenuActivated(QAction*))); 00289 00290 d->allVisibleColumnsAction = subMenu->addAction(i18n("All Visible Columns")); 00291 d->allVisibleColumnsAction->setCheckable( true ); 00292 subMenu->addSeparator(); 00293 00294 bool allColumnsAreSearchColumns = true; 00295 // TODO Make the entry order match the actual column order 00296 Q3Header* const header = d->listViews.first()->header(); 00297 int visibleColumns = 0; 00298 for(int i = 0; i < d->listViews.first()->columns(); i++) { 00299 if(d->listViews.first()->columnWidth(i)>0) { 00300 QString columnText = d->listViews.first()->columnText(i); 00301 if(columnText.isEmpty()) { 00302 int visiblePosition=1; 00303 for(int j = 0; j < header->mapToIndex(i); j++) 00304 if(d->listViews.first()->columnWidth(header->mapToSection(j))>0) 00305 visiblePosition++; 00306 00307 columnText = i18nc("Column number %1","Column No. %1", visiblePosition); 00308 } 00309 QAction *action = subMenu->addAction(columnText); 00310 action->setData( visibleColumns ); 00311 action->setCheckable( true ); 00312 00313 if(d->searchColumns.isEmpty() || d->searchColumns.indexOf(i) != -1) 00314 action->setChecked(true); 00315 else 00316 allColumnsAreSearchColumns = false; 00317 00318 visibleColumns++; 00319 } 00320 } 00321 d->allVisibleColumnsAction->setChecked( allColumnsAreSearchColumns ); 00322 00323 // searchColumnsMenuActivated() relies on one possible "all" representation 00324 if(allColumnsAreSearchColumns && !d->searchColumns.isEmpty()) 00325 d->searchColumns.clear(); 00326 } 00327 00328 popup->exec( e->globalPos() ); 00329 00330 delete popup; 00331 } 00332 00333 void K3ListViewSearchLine::connectListView(K3ListView *lv) 00334 { 00335 connect(lv, SIGNAL(destroyed( QObject * )), 00336 this, SLOT(listViewDeleted( QObject *))); 00337 connect(lv, SIGNAL(itemAdded(Q3ListViewItem *)), 00338 this, SLOT(itemAdded(Q3ListViewItem *))); 00339 } 00340 00341 void K3ListViewSearchLine::disconnectListView(K3ListView *lv) 00342 { 00343 disconnect(lv, SIGNAL(destroyed( QObject * )), 00344 this, SLOT(listViewDeleted( QObject *))); 00345 disconnect(lv, SIGNAL(itemAdded(Q3ListViewItem *)), 00346 this, SLOT(itemAdded(Q3ListViewItem *))); 00347 } 00348 00349 bool K3ListViewSearchLine::canChooseColumnsCheck() 00350 { 00351 // This is true if either of the following is true: 00352 00353 // there are no listviews connected 00354 if (d->listViews.isEmpty()) 00355 return false; 00356 00357 const K3ListView *first = d->listViews.first(); 00358 00359 const unsigned int numcols = first->columns(); 00360 // the listviews have only one column, 00361 if (numcols < 2) 00362 return false; 00363 00364 QStringList headers; 00365 for (unsigned int i = 0; i < numcols; ++i) 00366 headers.append(first->columnText(i)); 00367 00368 QList<K3ListView *>::ConstIterator it = d->listViews.constBegin(); 00369 for (++it /* skip the first one */; it !=d->listViews.constEnd(); ++it) { 00370 // the listviews have different numbers of columns, 00371 if ((unsigned int) (*it)->columns() != numcols) 00372 return false; 00373 00374 // the listviews differ in column labels. 00375 QStringList::ConstIterator jt; 00376 unsigned int i; 00377 for (i = 0, jt = headers.constBegin(); i < numcols; ++i, ++jt) { 00378 Q_ASSERT(jt != headers.constEnd()); 00379 if ((*it)->columnText(i) != *jt) 00380 return false; 00381 } 00382 } 00383 00384 return true; 00385 } 00386 00388 // protected slots 00390 00391 void K3ListViewSearchLine::queueSearch(const QString &search) 00392 { 00393 d->queuedSearches++; 00394 d->search = search; 00395 QTimer::singleShot(200, this, SLOT(activateSearch())); 00396 } 00397 00398 void K3ListViewSearchLine::activateSearch() 00399 { 00400 --(d->queuedSearches); 00401 00402 if(d->queuedSearches == 0) 00403 updateSearch(d->search); 00404 } 00405 00407 // private slots 00409 00410 void K3ListViewSearchLine::itemAdded(Q3ListViewItem *item) const 00411 { 00412 item->setVisible(itemMatches(item, text())); 00413 } 00414 00415 void K3ListViewSearchLine::listViewDeleted(QObject *o) 00416 { 00417 d->listViews.removeAll(static_cast<K3ListView *>(o)); 00418 setEnabled(d->listViews.isEmpty()); 00419 } 00420 00421 void K3ListViewSearchLine::searchColumnsMenuActivated(QAction *action) 00422 { 00423 int id = action->data().toInt(); 00424 00425 if(action == d->allVisibleColumnsAction) { 00426 if(d->searchColumns.isEmpty()) 00427 d->searchColumns.append(0); 00428 else 00429 d->searchColumns.clear(); 00430 } 00431 else { 00432 if(d->searchColumns.indexOf(id) != -1) 00433 d->searchColumns.removeAll(id); 00434 else { 00435 if(d->searchColumns.isEmpty()) { 00436 for(int i = 0; i < d->listViews.first()->columns(); i++) { 00437 if(i != id) 00438 d->searchColumns.append(i); 00439 } 00440 } 00441 else 00442 d->searchColumns.append(id); 00443 } 00444 } 00445 updateSearch(); 00446 } 00447 00449 // private methods 00451 00452 void K3ListViewSearchLine::checkColumns() 00453 { 00454 d->canChooseColumns = canChooseColumnsCheck(); 00455 } 00456 00457 void K3ListViewSearchLine::checkItemParentsNotVisible(K3ListView *listView) 00458 { 00459 Q3ListViewItemIterator it(listView); 00460 for(; it.current(); ++it) 00461 { 00462 Q3ListViewItem *item = it.current(); 00463 if(itemMatches(item, d->search)) 00464 item->setVisible(true); 00465 else 00466 item->setVisible(false); 00467 } 00468 } 00469 00470 #include <kvbox.h> 00471 00481 bool K3ListViewSearchLine::checkItemParentsVisible(Q3ListViewItem *item, Q3ListViewItem *highestHiddenParent) 00482 { 00483 bool visible = false; 00484 Q3ListViewItem * first = item; 00485 for(; item; item = item->nextSibling()) 00486 { 00487 //What we pass to our children as highestHiddenParent: 00488 Q3ListViewItem * hhp = highestHiddenParent ? highestHiddenParent : item->isVisible() ? 0L : item; 00489 bool childMatch = false; 00490 if(item->firstChild() && checkItemParentsVisible(item->firstChild(), hhp)) 00491 childMatch = true; 00492 // Should this item be shown? It should if any children should be, or if it matches. 00493 if(childMatch || itemMatches(item, d->search)) 00494 { 00495 visible = true; 00496 if (highestHiddenParent) 00497 { 00498 highestHiddenParent->setVisible(true); 00499 // Calling setVisible on our ancestor will unhide all its descendents. Hide the ones 00500 // before us that should not be shown. 00501 for(Q3ListViewItem *hide = first; hide != item; hide = hide->nextSibling()) 00502 hide->setVisible(false); 00503 highestHiddenParent = 0; 00504 // If we matched, than none of our children matched, yet the setVisible() call on our 00505 // ancestor unhid them, undo the damage: 00506 if(!childMatch) 00507 for(Q3ListViewItem *hide = item->firstChild(); hide; hide = hide->nextSibling()) 00508 hide->setVisible(false); 00509 } 00510 else 00511 item->setVisible(true); 00512 } 00513 else 00514 item->setVisible(false); 00515 } 00516 return visible; 00517 } 00518 00520 // K3ListViewSearchLineWidget 00522 00523 class K3ListViewSearchLineWidget::K3ListViewSearchLineWidgetPrivate 00524 { 00525 public: 00526 K3ListViewSearchLineWidgetPrivate() : listView(0), searchLine(0) {} 00527 K3ListView *listView; 00528 K3ListViewSearchLine *searchLine; 00529 }; 00530 00531 K3ListViewSearchLineWidget::K3ListViewSearchLineWidget(K3ListView *listView, 00532 QWidget *parent) : 00533 KHBox(parent) 00534 { 00535 d = new K3ListViewSearchLineWidgetPrivate; 00536 d->listView = listView; 00537 00538 setSpacing(5); 00539 00540 QTimer::singleShot(0, this, SLOT(createWidgets())); 00541 } 00542 00543 K3ListViewSearchLineWidget::~K3ListViewSearchLineWidget() 00544 { 00545 delete d; 00546 } 00547 00548 K3ListViewSearchLine *K3ListViewSearchLineWidget::createSearchLine(K3ListView *listView) 00549 { 00550 if(!d->searchLine) 00551 d->searchLine = new K3ListViewSearchLine(this, listView); 00552 return d->searchLine; 00553 } 00554 00555 void K3ListViewSearchLineWidget::createWidgets() 00556 { 00557 QLabel *label = new QLabel(i18n("S&earch:"), this); 00558 label->setObjectName(QLatin1String("kde toolbar widget")); 00559 00560 d->searchLine = createSearchLine(d->listView); 00561 d->searchLine->show(); 00562 00563 label->setBuddy(d->searchLine); 00564 label->show(); 00565 } 00566 00567 K3ListViewSearchLine *K3ListViewSearchLineWidget::searchLine() const 00568 { 00569 return d->searchLine; 00570 } 00571 00572 #include "k3listviewsearchline.moc"
KDE 4.6 API Reference