1 /***************************************************************************
2 * Copyright (C) 2005-08 by the Quassel Project *
3 * devel@quassel-irc.org *
5 * This program is free software; you can redistribute it and/or modify *
6 * it under the terms of the GNU General Public License as published by *
7 * the Free Software Foundation; either version 2 of the License, or *
8 * (at your option) version 3. *
10 * This program is distributed in the hope that it will be useful, *
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
13 * GNU General Public License for more details. *
15 * You should have received a copy of the GNU General Public License *
16 * along with this program; if not, write to the *
17 * Free Software Foundation, Inc., *
18 * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
19 ***************************************************************************/
21 #include "chatviewsearchcontroller.h"
23 #include <QAbstractItemModel>
27 #include "chatlinemodel.h"
28 #include "chatscene.h"
29 #include "messagemodel.h"
31 ChatViewSearchController::ChatViewSearchController(QObject *parent)
35 _caseSensitive(false),
36 _searchSenders(false),
38 _searchOnlyRegularMsgs(true)
42 void ChatViewSearchController::setSearchString(const QString &searchString) {
43 QString oldSearchString = _searchString;
44 _searchString = searchString;
46 if(!searchString.startsWith(oldSearchString) || oldSearchString.isEmpty()) {
47 // we can't reuse our all findings... cler the scene and do it all over
51 updateHighlights(true);
56 void ChatViewSearchController::setScene(ChatScene *scene) {
62 disconnect(_scene, 0, this, 0);
63 qDeleteAll(_highlightItems);
64 _highlightItems.clear();
71 connect(_scene, SIGNAL(destroyed()), this, SLOT(sceneDestroyed()));
75 void ChatViewSearchController::highlightNext() {
76 if(_highlightItems.isEmpty())
79 if(_currentHighlight < _highlightItems.count()) {
80 _highlightItems.at(_currentHighlight)->setHighlighted(false);
84 if(_currentHighlight >= _highlightItems.count())
85 _currentHighlight = 0;
86 _highlightItems.at(_currentHighlight)->setHighlighted(true);
87 emit newCurrentHighlight(_highlightItems.at(_currentHighlight));
90 void ChatViewSearchController::highlightPrev() {
91 if(_highlightItems.isEmpty())
94 if(_currentHighlight < _highlightItems.count()) {
95 _highlightItems.at(_currentHighlight)->setHighlighted(false);
99 if(_currentHighlight < 0)
100 _currentHighlight = _highlightItems.count() - 1;
101 _highlightItems.at(_currentHighlight)->setHighlighted(true);
102 emit newCurrentHighlight(_highlightItems.at(_currentHighlight));
105 void ChatViewSearchController::updateHighlights(bool reuse) {
109 QAbstractItemModel *model = _scene->model();
113 QList<ChatLine *> chatLines;
115 foreach(SearchHighlightItem *highlightItem, _highlightItems) {
116 ChatLine *line = dynamic_cast<ChatLine *>(highlightItem->parentItem());
117 if(!line || chatLines.contains(line))
123 qDeleteAll(_highlightItems);
124 _highlightItems.clear();
125 Q_ASSERT(_highlightItems.isEmpty());
127 if(searchString().isEmpty() || !(_searchSenders || _searchMsgs))
132 foreach(ChatLine *line, chatLines) {
133 if(_searchOnlyRegularMsgs) {
134 index = model->index(line->row(), 0);
135 if(!checkType((Message::Type)index.data(MessageModel::TypeRole).toInt()))
141 // we have to crawl through the data
144 int rowCount = model->rowCount();
145 for(int row = 0; row < rowCount; row++) {
146 ChatLine *line = _scene->chatLine(row);
148 if(_searchOnlyRegularMsgs) {
149 index = model->index(row, 0);
150 if(!checkType((Message::Type)index.data(MessageModel::TypeRole).toInt()))
157 if(!_highlightItems.isEmpty()) {
158 _highlightItems.last()->setHighlighted(true);
159 _currentHighlight = _highlightItems.count() - 1;
160 emit newCurrentHighlight(_highlightItems.last());
164 void ChatViewSearchController::highlightLine(ChatLine *line) {
165 QList<ChatItem *> checkItems;
167 checkItems << &(line->item(MessageModel::SenderColumn));
170 checkItems << &(line->item(MessageModel::ContentsColumn));
172 foreach(ChatItem *item, checkItems) {
173 foreach(QRectF wordRect, item->findWords(searchString(), caseSensitive())) {
174 _highlightItems << new SearchHighlightItem(wordRect.adjusted(item->x(), 0, item->x(), 0), line);
179 void ChatViewSearchController::sceneDestroyed() {
180 // WARNING: don't call any methods on scene!
182 // the items will be automatically deleted when the scene is destroyed
183 // so we just have to clear the list;
184 _highlightItems.clear();
187 void ChatViewSearchController::setCaseSensitive(bool caseSensitive) {
188 if(_caseSensitive == caseSensitive)
191 _caseSensitive = caseSensitive;
193 // we can reuse the original search results if the new search
194 // parameters are a restriction of the original one
195 updateHighlights(caseSensitive);
198 void ChatViewSearchController::setSearchSenders(bool searchSenders) {
199 if(_searchSenders == searchSenders)
202 _searchSenders = searchSenders;
203 // we can reuse the original search results if the new search
204 // parameters are a restriction of the original one
205 updateHighlights(!searchSenders);
208 void ChatViewSearchController::setSearchMsgs(bool searchMsgs) {
209 if(_searchMsgs == searchMsgs)
212 _searchMsgs = searchMsgs;
214 // we can reuse the original search results if the new search
215 // parameters are a restriction of the original one
216 updateHighlights(!searchMsgs);
219 void ChatViewSearchController::setSearchOnlyRegularMsgs(bool searchOnlyRegularMsgs) {
220 if(_searchOnlyRegularMsgs == searchOnlyRegularMsgs)
223 _searchOnlyRegularMsgs = searchOnlyRegularMsgs;
225 // we can reuse the original search results if the new search
226 // parameters are a restriction of the original one
227 updateHighlights(searchOnlyRegularMsgs);
231 // ==================================================
232 // SearchHighlightItem
233 // ==================================================
234 SearchHighlightItem::SearchHighlightItem(QRectF wordRect, QGraphicsItem *parent)
236 QGraphicsItem(parent),
241 setPos(wordRect.x(), wordRect.y());
242 qreal sizedelta = wordRect.height() * 0.1;
243 _boundingRect = QRectF(-sizedelta, -sizedelta, wordRect.width() + 2 * sizedelta, wordRect.height() + 2 * sizedelta);
245 connect(&_timeLine, SIGNAL(valueChanged(qreal)), this, SLOT(updateHighlight(qreal)));
248 void SearchHighlightItem::setHighlighted(bool highlighted) {
249 _highlighted = highlighted;
252 _timeLine.setDirection(QTimeLine::Forward);
254 _timeLine.setDirection(QTimeLine::Backward);
256 if(_timeLine.state() != QTimeLine::Running)
262 void SearchHighlightItem::updateHighlight(qreal value) {
263 _alpha = 100 + 155 * value;
267 void SearchHighlightItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) {
271 painter->setPen(QPen(QColor(0, 0, 0, _alpha), 1.5));
272 painter->setBrush(QColor(254, 237, 45, _alpha));
273 painter->setRenderHints(QPainter::Antialiasing);
274 qreal radius = boundingRect().height() * 0.30;
275 painter->drawRoundedRect(boundingRect(), radius, radius);