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"
24 #include <QtGui/QHeaderView>
25 #include <QtGui/QBoxLayout>
26 #include <QtGui/QKeyEvent>
30 ////////////////////////////////////////////////////////////////////////////////
32 ////////////////////////////////////////////////////////////////////////////////
33 class KViewSearchLine::KViewSearchLinePrivate
36 KViewSearchLinePrivate() :
41 keepParentsVisible(true),
48 bool keepParentsVisible
;
51 QLinkedList
<int> searchColumns
;
55 KViewSearchLine::KViewSearchLine(QWidget
*parent
, QAbstractItemView
*v
) :
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
&)));
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()));
87 KViewSearchLine::KViewSearchLine(QWidget
*parent
) :
90 d
= new KViewSearchLinePrivate
;
92 setClearButtonShown(true);
97 connect(this, SIGNAL(textChanged(const QString
&)),
98 this, SLOT(queueSearch(const QString
&)));
103 KViewSearchLine::~KViewSearchLine()
108 QAbstractItemView
* KViewSearchLine::view() const
116 bool KViewSearchLine::caseSensitive() const
118 return d
->caseSensitive
;
121 bool KViewSearchLine::keepParentsVisible() const
123 return d
->keepParentsVisible
;
126 ////////////////////////////////////////////////////////////////////////////////
128 ////////////////////////////////////////////////////////////////////////////////
130 void KViewSearchLine::updateSearch(const QString
&s
)
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).
141 if(d
->keepParentsVisible
)
142 checkItemParentsVisible(model()->index(0,0, QModelIndex()));
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
)
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
);
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 ////////////////////////////////////////////////////////////////////////////////
206 ////////////////////////////////////////////////////////////////////////////////
208 bool KViewSearchLine::itemMatches(const QModelIndex
& item
, const QString
&s
) const
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.
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)
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)
248 QString text
= model()->data(item
).toString();
249 if(text
.indexOf(s
, 0, d
->caseSensitive
? Qt::CaseSensitive
: Qt::CaseInsensitive
) >= 0)
256 void KViewSearchLine::contextMenuEvent( QContextMenuEvent
*e
)
259 QMenu
*popup
= KLineEdit::createStandardContextMenu();
262 int columnCount
= d
->treeView
->header()->count();
263 actions
.resize(columnCount
+ 1);
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() );
304 ////////////////////////////////////////////////////////////////////////////////
306 ////////////////////////////////////////////////////////////////////////////////
308 void KViewSearchLine::queueSearch(const QString
&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 ////////////////////////////////////////////////////////////////////////////////
325 ////////////////////////////////////////////////////////////////////////////////
327 void KViewSearchLine::listViewDeleted()
334 void KViewSearchLine::searchColumnsMenuActivated(QAction
* action
)
337 int count
= actions
.count();
338 for(int i
=0; i
<count
; ++i
)
340 if(action
== actions
[i
])
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);
352 d
->searchColumns
.clear();
356 if(d
->searchColumns
.contains(index
))
357 d
->searchColumns
.removeAll(index
);
360 if(d
->searchColumns
.isEmpty()) //all columns was checked
362 for(int i
=0; i
<columnCount
; ++i
)
364 d
->searchColumns
.append(i
);
367 d
->searchColumns
.append(index
);
374 void KViewSearchLine::slotRowsRemoved(const QModelIndex
&parent
, int, int)
376 if(!d
->keepParentsVisible
)
379 QModelIndex p
= parent
;
382 int count
= model()->rowCount(p
);
383 if(count
&& anyVisible( model()->index(0,0, p
), model()->index( count
-1, 0, p
)))
385 if(itemMatches(p
, d
->search
))
387 setVisible(p
, false);
392 void KViewSearchLine::slotColumnsInserted(const QModelIndex
&, int, int )
397 void KViewSearchLine::slotColumnsRemoved(const QModelIndex
&, int first
, int last
)
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
;
413 void KViewSearchLine::slotModelReset()
415 //FIXME Is there a way to ensure that the view
416 // has already responded to the reset signal?
420 void KViewSearchLine::slotDataChanged(const QModelIndex
&topLeft
, const QModelIndex
&bottomRight
)
422 QModelIndex parent
= topLeft
.parent();
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
)
429 if(!parent
.isValid()) // includes listview
433 QModelIndex p
= parent
;
440 else //no match => might need to hide parents (this is ugly)
442 if(isVisible(parent
) == false) // parent is already hidden
444 //parent is visible => implies all parents visible
446 // first check if all of the unchanged rows are hidden
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
))
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();
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
)))
469 if(itemMatches(p
, d
->search
))
471 setVisible(p
, false);
478 ////////////////////////////////////////////////////////////////////////////////
480 ////////////////////////////////////////////////////////////////////////////////
481 QAbstractItemModel
* KViewSearchLine::model() const
484 return d
->treeView
->model();
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
;
497 if( isVisible(index
))
501 index
= nextRow(index
);
506 bool KViewSearchLine::isVisible(const QModelIndex
& index
)
509 return !d
->treeView
->isRowHidden(index
.row(), index
.parent());
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
;
525 int rowCount
= model()->rowCount(index
);
526 if(d
->keepParentsVisible
&& rowCount
&& anyVisible( index
.child(0,0), index
.child( rowCount
-1, 0)))
530 else // no children visible
532 bool match
= itemMatches(index
, d
->search
);
533 setVisible(index
, match
);
534 visible
= visible
|| match
;
538 index
= nextRow(index
);
543 void KViewSearchLine::slotRowsInserted(const QModelIndex
&parent
, int first
, int last
)
545 bool visible
= false;
548 column
= d
->listView
->modelColumn();
550 QModelIndex index
= model()->index(first
, column
, parent
);
551 QModelIndex end
= model()->index(last
, column
, parent
);
554 if(itemMatches(index
, d
->search
))
557 setVisible(index
, true);
560 setVisible(index
, false);
563 index
= nextRow(index
);
566 if(!d
->keepParentsVisible
)
570 QModelIndex p
= parent
;
579 void KViewSearchLine::setVisible(QModelIndex index
, bool v
)
582 d
->treeView
->setRowHidden(index
.row(), index
.parent(), !v
);
584 d
->listView
->setRowHidden(index
.row(), !v
);
587 void KViewSearchLine::checkItemParentsNotVisible()
589 int rowCount
= model()->rowCount( QModelIndex() );
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);
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());
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
))
623 setVisible(index
, true);
626 setVisible(index
, false);
632 ////////////////////////////////////////////////////////////////////////////////
633 // KViewSearchLineWidget
634 ////////////////////////////////////////////////////////////////////////////////
636 class KViewSearchLineWidget::KViewSearchLineWidgetPrivate
639 KViewSearchLineWidgetPrivate() : view(0), searchLine(0), layout(0) {}
640 QAbstractItemView
*view
;
641 KViewSearchLine
*searchLine
;
645 KViewSearchLineWidget::KViewSearchLineWidget(QAbstractItemView
*view
,
649 d
= new KViewSearchLineWidgetPrivate
;
652 QTimer::singleShot(0, this, SLOT(createWidgets()));
655 KViewSearchLineWidget::~KViewSearchLineWidget()
661 KViewSearchLine
*KViewSearchLineWidget::createSearchLine(QAbstractItemView
*view
)
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
);
685 KViewSearchLine
*KViewSearchLineWidget::searchLine() const
687 return d
->searchLine
;
690 #include "kebsearchline.moc"