Fix crash if key bindings specified in profile cannot be found. Improve
[personal-kdebase.git] / apps / keditbookmarks / kebsearchline.cpp
blob44b13408071174b94baa12a8f476b5c46d58ccf5
1 /* This file is part of the KDE project
2 Copyright (C) 2005 Daniel Teske <teske@squorn.de>
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the GNU General Public
6 License version 2 or at your option version 3 as published by
7 the Free Software Foundation.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; see the file COPYING. If not, write to
16 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17 Boston, MA 02110-1301, USA.
20 #include "kebsearchline.h"
22 #include <kicon.h>
24 #include <QtGui/QHeaderView>
25 #include <QtGui/QBoxLayout>
26 #include <QtGui/QKeyEvent>
30 ////////////////////////////////////////////////////////////////////////////////
31 // public methods
32 ////////////////////////////////////////////////////////////////////////////////
33 class KViewSearchLine::KViewSearchLinePrivate
35 public:
36 KViewSearchLinePrivate() :
37 listView(0),
38 treeView(0),
39 caseSensitive(false),
40 activeSearch(false),
41 keepParentsVisible(true),
42 queuedSearches(0) {}
44 QListView * listView;
45 QTreeView * treeView;
46 bool caseSensitive;
47 bool activeSearch;
48 bool keepParentsVisible;
49 QString search;
50 int queuedSearches;
51 QLinkedList<int> searchColumns;
55 KViewSearchLine::KViewSearchLine(QWidget *parent, QAbstractItemView *v) :
56 KLineEdit(parent)
58 d = new KViewSearchLinePrivate;
60 setClearButtonShown(true);
62 d->treeView = dynamic_cast<QTreeView *>(v);
63 d->listView = dynamic_cast<QListView *>(v);
65 connect(this, SIGNAL(textChanged(const QString &)),
66 this, SLOT(queueSearch(const QString &)));
68 if(view()) {
69 connect(view(), SIGNAL(destroyed()),
70 this, SLOT(listViewDeleted()));
71 connect(model(), SIGNAL(dataChanged(const QModelIndex &, const QModelIndex &)),
72 this, SLOT(slotDataChanged(const QModelIndex &, const QModelIndex &)));
73 connect(model(), SIGNAL(rowsInserted(const QModelIndex &, int , int )),
74 this, SLOT(slotRowsInserted(const QModelIndex &, int, int)));
75 connect(model(), SIGNAL(rowsRemoved(const QModelIndex &, int , int )),
76 this, SLOT(slotRowsRemoved(const QModelIndex &, int, int)));
77 connect(model(), SIGNAL(columnsInserted(const QModelIndex &, int, int )),
78 this, SLOT(slotColumnsInserted(const QModelIndex &, int, int )));
79 connect(model(), SIGNAL(columnsRemoved(const QModelIndex &, int, int)),
80 this, SLOT(slotColumnsRemoved(const QModelIndex &, int, int)));
81 connect(model(), SIGNAL(modelReset()), this, SLOT(slotModelReset()));
83 else
84 setEnabled(false);
87 KViewSearchLine::KViewSearchLine(QWidget *parent) :
88 KLineEdit(parent)
90 d = new KViewSearchLinePrivate;
92 setClearButtonShown(true);
94 d->treeView = 0;
95 d->listView = 0;
97 connect(this, SIGNAL(textChanged(const QString &)),
98 this, SLOT(queueSearch(const QString &)));
100 setEnabled(false);
103 KViewSearchLine::~KViewSearchLine()
105 delete d;
108 QAbstractItemView * KViewSearchLine::view() const
110 if(d->treeView)
111 return d->treeView;
112 else
113 return d->listView;
116 bool KViewSearchLine::caseSensitive() const
118 return d->caseSensitive;
121 bool KViewSearchLine::keepParentsVisible() const
123 return d->keepParentsVisible;
126 ////////////////////////////////////////////////////////////////////////////////
127 // public slots
128 ////////////////////////////////////////////////////////////////////////////////
130 void KViewSearchLine::updateSearch(const QString &s)
132 if(!view())
133 return;
135 d->search = s.isNull() ? text() : s;
137 // If there's a selected item that is visible, make sure that it's visible
138 // when the search changes too (assuming that it still matches).
139 //FIXME reimplement
141 if(d->keepParentsVisible)
142 checkItemParentsVisible(model()->index(0,0, QModelIndex()));
143 else
144 checkItemParentsNotVisible();
147 void KViewSearchLine::setCaseSensitive(bool cs)
149 d->caseSensitive = cs;
152 void KViewSearchLine::setKeepParentsVisible(bool v)
154 d->keepParentsVisible = v;
157 void KViewSearchLine::setSearchColumns(const QLinkedList<int> &columns)
159 d->searchColumns = columns;
162 void KViewSearchLine::setView(QAbstractItemView *v)
164 if(view()) {
165 disconnect(view(), SIGNAL(destroyed()),
166 this, SLOT(listViewDeleted()));
167 disconnect(model(), SIGNAL(dataChanged(const QModelIndex &, const QModelIndex &)),
168 this, SLOT(slotDataChanged(const QModelIndex &, const QModelIndex &)));
169 disconnect(model(), SIGNAL(rowsInserted(const QModelIndex &, int , int )),
170 this, SLOT(slotRowsInserted(const QModelIndex &, int, int)));
171 disconnect(model(), SIGNAL(rowsRemoved(const QModelIndex &, int , int )),
172 this, SLOT(slotRowsRemoved(const QModelIndex &, int, int)));
173 disconnect(model(), SIGNAL(columnsInserted(const QModelIndex &, int, int )),
174 this, SLOT(slotColumnsInserted(const QModelIndex &, int, int )));
175 disconnect(model(), SIGNAL(columnsRemoved(const QModelIndex &, int, int)),
176 this, SLOT(slotColumnsRemoved(const QModelIndex &, int, int)));
177 disconnect(model(), SIGNAL(modelReset()), this, SLOT(slotModelReset()));
180 d->treeView = dynamic_cast<QTreeView *>(v);
181 d->listView = dynamic_cast<QListView *>(v);
183 if(view()) {
184 connect(view(), SIGNAL(destroyed()),
185 this, SLOT(listViewDeleted()));
187 connect(model(), SIGNAL(dataChanged(const QModelIndex &, const QModelIndex &)),
188 this, SLOT(slotDataChanged(const QModelIndex &, const QModelIndex &)));
189 connect(model(), SIGNAL(rowsInserted(const QModelIndex &, int , int )),
190 this, SLOT(slotRowsInserted(const QModelIndex &, int, int)));
191 connect(model(), SIGNAL(rowsRemoved(const QModelIndex &, int , int )),
192 this, SLOT(slotRowsRemoved(const QModelIndex &, int, int)));
193 connect(model(), SIGNAL(columnsInserted(const QModelIndex &, int, int )),
194 this, SLOT(slotColumnsInserted(const QModelIndex &, int, int )));
195 connect(model(), SIGNAL(columnsRemoved(const QModelIndex &, int, int)),
196 this, SLOT(slotColumnsRemoved(const QModelIndex &, int, int)));
197 connect(model(), SIGNAL(modelReset()), this, SLOT(slotModelReset()));
201 setEnabled(bool(view()));
204 ////////////////////////////////////////////////////////////////////////////////
205 // protected members
206 ////////////////////////////////////////////////////////////////////////////////
208 bool KViewSearchLine::itemMatches(const QModelIndex & item, const QString &s) const
210 if(s.isEmpty())
211 return true;
213 // If the search column list is populated, search just the columns
214 // specifified. If it is empty default to searching all of the columns.
215 if(d->treeView)
217 int columnCount = d->treeView->header()->count();
218 int row = item.row();
219 QModelIndex parent = item.parent();
220 if(!d->searchColumns.isEmpty()) {
221 QLinkedList<int>::const_iterator it, end;
222 end = d->searchColumns.constEnd();
223 for(it = d->searchColumns.constBegin(); it != end; ++it)
225 if(*it < columnCount)
227 const QString & text = model()->data(parent.child(row, *it)).toString();
228 if(text.indexOf(s, 0, d->caseSensitive ? Qt::CaseSensitive : Qt::CaseInsensitive) >= 0)
229 return true;
233 else {
234 for(int i = 0; i < columnCount; i++)
236 if(d->treeView->isColumnHidden(i) == false)
238 const QString & text = model()->data(parent.child(row, i)).toString();
239 if(text.indexOf(s, 0, d->caseSensitive ? Qt::CaseSensitive : Qt::CaseInsensitive) >= 0)
240 return true;
244 return false;
246 else
248 QString text = model()->data(item).toString();
249 if(text.indexOf(s, 0, d->caseSensitive ? Qt::CaseSensitive : Qt::CaseInsensitive) >= 0)
250 return true;
251 else
252 return false;
256 void KViewSearchLine::contextMenuEvent( QContextMenuEvent*e )
258 qDeleteAll(actions);
259 QMenu *popup = KLineEdit::createStandardContextMenu();
260 if(d->treeView)
262 int columnCount = d->treeView->header()->count();
263 actions.resize(columnCount + 1);
264 if(columnCount)
266 QMenu *submenu = new QMenu(i18n("Search Columns"), popup);
267 popup->addMenu(submenu);
268 bool allVisibleColumsCheked = true;
269 QAction * allVisibleAct = new QAction(i18n("All Visible Columns"), 0);
270 allVisibleAct->setCheckable(true);
271 submenu->addAction(allVisibleAct);
272 submenu->addSeparator();
273 for(int i=0; i<columnCount; ++i)
275 int logicalIndex = d->treeView->header()->logicalIndex(i);
276 QString columnText = model()->headerData(logicalIndex, Qt::Horizontal).toString();
277 if(columnText.isEmpty())
278 columnText = i18nc("Column number %1","Column No. %1", i);
279 QAction * act = new QAction(columnText, 0);
280 act->setCheckable(true);
281 if( d->searchColumns.isEmpty() || d->searchColumns.contains(logicalIndex) )
282 act->setChecked(true);
284 actions[logicalIndex] = act;
285 if( !d->treeView || (d->treeView->isColumnHidden(i) == false) )
287 submenu->addAction(act);
288 allVisibleColumsCheked = allVisibleColumsCheked && act->isChecked();
291 actions[columnCount] = allVisibleAct;
292 if(d->searchColumns.isEmpty() || allVisibleColumsCheked)
294 allVisibleAct->setChecked(true);
295 d->searchColumns.clear();
297 connect(submenu, SIGNAL(triggered ( QAction * )), this, SLOT(searchColumnsMenuActivated(QAction *)));
300 popup->exec( e->globalPos() );
301 delete popup;
304 ////////////////////////////////////////////////////////////////////////////////
305 // protected slots
306 ////////////////////////////////////////////////////////////////////////////////
308 void KViewSearchLine::queueSearch(const QString &search)
310 d->queuedSearches++;
311 d->search = search;
312 QTimer::singleShot(200, this, SLOT(activateSearch()));
315 void KViewSearchLine::activateSearch()
317 --(d->queuedSearches);
319 if(d->queuedSearches == 0)
320 updateSearch(d->search);
323 ////////////////////////////////////////////////////////////////////////////////
324 // private slots
325 ////////////////////////////////////////////////////////////////////////////////
327 void KViewSearchLine::listViewDeleted()
329 d->treeView = 0;
330 d->listView = 0;
331 setEnabled(false);
334 void KViewSearchLine::searchColumnsMenuActivated(QAction * action)
336 int index = 0;
337 int count = actions.count();
338 for(int i=0; i<count; ++i)
340 if(action == actions[i])
342 index = i;
343 break;
346 int columnCount = d->treeView->header()->count();
347 if(index == columnCount)
349 if(d->searchColumns.isEmpty()) //all columns was checked
350 d->searchColumns.append(0);
351 else
352 d->searchColumns.clear();
354 else
356 if(d->searchColumns.contains(index))
357 d->searchColumns.removeAll(index);
358 else
360 if(d->searchColumns.isEmpty()) //all columns was checked
362 for(int i=0; i<columnCount; ++i)
363 if(i != index)
364 d->searchColumns.append(i);
366 else
367 d->searchColumns.append(index);
370 updateSearch();
374 void KViewSearchLine::slotRowsRemoved(const QModelIndex &parent, int, int)
376 if(!d->keepParentsVisible)
377 return;
379 QModelIndex p = parent;
380 while(p.isValid())
382 int count = model()->rowCount(p);
383 if(count && anyVisible( model()->index(0,0, p), model()->index( count-1, 0, p)))
384 return;
385 if(itemMatches(p, d->search))
386 return;
387 setVisible(p, false);
388 p = p.parent();
392 void KViewSearchLine::slotColumnsInserted(const QModelIndex &, int, int )
394 updateSearch();
397 void KViewSearchLine::slotColumnsRemoved(const QModelIndex &, int first, int last)
399 if(d->treeView)
400 updateSearch();
401 else
403 if(d->listView->modelColumn() >= first && d->listView->modelColumn()<= last)
405 if(d->listView->modelColumn()>last)
406 kFatal()<<"Columns were removed, the modelColumn() doesn't exist anymore. "
407 "K4listViewSearchLine can't cope with that."<<endl;
408 updateSearch();
413 void KViewSearchLine::slotModelReset()
415 //FIXME Is there a way to ensure that the view
416 // has already responded to the reset signal?
417 updateSearch();
420 void KViewSearchLine::slotDataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight)
422 QModelIndex parent = topLeft.parent();
423 int column = 0;
424 if(d->listView)
425 column = d->listView->modelColumn();
426 bool match = recheck( model()->index(topLeft.row(), column, parent), model()->index(bottomRight.row(), column, parent));
427 if(!d->keepParentsVisible)
428 return;
429 if(!parent.isValid()) // includes listview
430 return;
431 if(match)
433 QModelIndex p = parent;
434 while(p.isValid())
436 setVisible(p, true);
437 p = p.parent();
440 else //no match => might need to hide parents (this is ugly)
442 if(isVisible(parent) == false) // parent is already hidden
443 return;
444 //parent is visible => implies all parents visible
446 // first check if all of the unchanged rows are hidden
447 match = false;
448 if(topLeft.row() >= 1)
449 match = match || anyVisible( model()->index(0,0, parent), model()->index(topLeft.row()-1, 0, parent));
450 int rowCount = model()->rowCount(parent);
451 if(bottomRight.row() + 1 <= rowCount - 1)
452 match = match || anyVisible( model()->index(bottomRight.row()+1, 0, parent), model()->index(rowCount-1, 0, parent));
453 if(!match) //all child rows hidden
455 if(itemMatches(parent, d->search))
456 return;
457 // and parent didn't match, hide it
458 setVisible(parent, false);
460 // need to check all the way up to root
461 QModelIndex p = parent.parent();
462 while(p.isValid())
464 //hide p if no children of p isVisible and it doesn't match
465 int count = model()->rowCount(p);
466 if(anyVisible( model()->index(0, 0, p), model()->index(count-1, 0, p)))
467 return;
469 if(itemMatches(p, d->search))
470 return;
471 setVisible(p, false);
472 p = p.parent();
478 ////////////////////////////////////////////////////////////////////////////////
479 // private methods
480 ////////////////////////////////////////////////////////////////////////////////
481 QAbstractItemModel * KViewSearchLine::model() const
483 if(d->treeView)
484 return d->treeView->model();
485 else
486 return d->listView->model();
490 bool KViewSearchLine::anyVisible(const QModelIndex & first, const QModelIndex & last)
492 Q_ASSERT(d->treeView);
493 QModelIndex parent = first.parent();
494 QModelIndex index = first;
495 while(true)
497 if( isVisible(index))
498 return true;
499 if(index == last)
500 break;
501 index = nextRow(index);
503 return false;
506 bool KViewSearchLine::isVisible(const QModelIndex & index)
508 if(d->treeView)
509 return !d->treeView->isRowHidden(index.row(), index.parent());
510 else
511 return d->listView->isRowHidden(index.row());
514 QModelIndex KViewSearchLine::nextRow(const QModelIndex & index)
516 return model()->index(index.row()+1, index.column(), index.parent());
519 bool KViewSearchLine::recheck(const QModelIndex & first, const QModelIndex & last)
521 bool visible = false;
522 QModelIndex index = first;
523 while(true)
525 int rowCount = model()->rowCount(index);
526 if(d->keepParentsVisible && rowCount && anyVisible( index.child(0,0), index.child( rowCount-1, 0)))
528 visible = true;
530 else // no children visible
532 bool match = itemMatches(index, d->search);
533 setVisible(index, match);
534 visible = visible || match;
536 if(index == last)
537 break;
538 index = nextRow(index);
540 return visible;
543 void KViewSearchLine::slotRowsInserted(const QModelIndex &parent, int first, int last)
545 bool visible = false;
546 int column = 0;
547 if(d->listView)
548 column = d->listView->modelColumn();
550 QModelIndex index = model()->index(first, column, parent);
551 QModelIndex end = model()->index(last, column, parent);
552 while(true)
554 if(itemMatches(index, d->search))
556 visible = true;
557 setVisible(index, true);
559 else
560 setVisible(index, false);
561 if(index == end)
562 break;
563 index = nextRow(index);
566 if(!d->keepParentsVisible)
567 return;
568 if(visible)
570 QModelIndex p = parent;
571 while(p.isValid())
573 setVisible(p, true);
574 p = p.parent();
579 void KViewSearchLine::setVisible(QModelIndex index, bool v)
581 if(d->treeView)
582 d->treeView->setRowHidden(index.row(), index.parent(), !v);
583 else
584 d->listView->setRowHidden(index.row(), !v);
587 void KViewSearchLine::checkItemParentsNotVisible()
589 int rowCount = model()->rowCount( QModelIndex() );
590 int column = 0;
591 if(d->listView)
592 column = d->listView->modelColumn();
593 for(int i = 0; i < rowCount; ++i)
595 QModelIndex it = model()->index(i, column, QModelIndex());
596 if(itemMatches(it, d->search))
597 setVisible(it, true);
598 else
599 setVisible(it, false);
603 /** Check whether \p index, its siblings and their descendents should be shown. Show or hide the items as necessary.
605 * \p index The list view item to start showing / hiding items at. Typically, this is the first child of another item, or the
606 * the first child of the list view.
607 * \return \c true if an item which should be visible is found, \c false if all items found should be hidden.
609 bool KViewSearchLine::checkItemParentsVisible(QModelIndex index)
611 bool visible = false;
612 int rowCount = model()->rowCount(index.parent());
613 int column = 0;
614 if(d->listView)
615 column = d->listView->modelColumn();
616 for(int i = 0; i<rowCount; ++i)
618 index = model()->index(i, column, index.parent());
619 if(model()->rowCount(index) && checkItemParentsVisible(index.child(0,column))
620 || itemMatches(index, d->search))
622 visible = true;
623 setVisible(index, true);
625 else
626 setVisible(index, false);
628 return visible;
632 ////////////////////////////////////////////////////////////////////////////////
633 // KViewSearchLineWidget
634 ////////////////////////////////////////////////////////////////////////////////
636 class KViewSearchLineWidget::KViewSearchLineWidgetPrivate
638 public:
639 KViewSearchLineWidgetPrivate() : view(0), searchLine(0), layout(0) {}
640 QAbstractItemView *view;
641 KViewSearchLine *searchLine;
642 QHBoxLayout *layout;
645 KViewSearchLineWidget::KViewSearchLineWidget(QAbstractItemView *view,
646 QWidget *parent) :
647 QWidget(parent)
649 d = new KViewSearchLineWidgetPrivate;
650 d->view = view;
652 QTimer::singleShot(0, this, SLOT(createWidgets()));
655 KViewSearchLineWidget::~KViewSearchLineWidget()
657 delete d->layout;
658 delete d;
661 KViewSearchLine *KViewSearchLineWidget::createSearchLine(QAbstractItemView *view)
663 if(!d->searchLine)
664 d->searchLine = new KViewSearchLine(0, view);
665 return d->searchLine;
668 void KViewSearchLineWidget::createWidgets()
670 d->layout = new QHBoxLayout(this);
671 d->layout->setMargin(0);
673 QLabel *label = new QLabel(i18n("S&earch:"));
674 label->setObjectName("kde toolbar widget");
675 d->layout->addWidget(label);
677 d->searchLine = createSearchLine(d->view);
678 d->layout->addWidget(d->searchLine);
679 d->searchLine->show();
681 label->setBuddy(d->searchLine);
682 label->show();
685 KViewSearchLine *KViewSearchLineWidget::searchLine() const
687 return d->searchLine;
690 #include "kebsearchline.moc"