e8d7f90ff45bf8c4ec5a66fe4c0ee29309c1d83b
[quassel.git] / src / qtui / chatviewsearchcontroller.cpp
1 /***************************************************************************
2  *   Copyright (C) 2005-08 by the Quassel Project                          *
3  *   devel@quassel-irc.org                                                 *
4  *                                                                         *
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.                                           *
9  *                                                                         *
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.                          *
14  *                                                                         *
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  ***************************************************************************/
20
21 #include "chatviewsearchcontroller.h"
22
23 #include <QAbstractItemModel>
24 #include <QPainter>
25
26 #include "chatitem.h"
27 #include "chatlinemodel.h"
28 #include "chatscene.h"
29 #include "messagemodel.h"
30
31 ChatViewSearchController::ChatViewSearchController(QObject *parent)
32   : QObject(parent),
33     _scene(0),
34     _caseSensitive(false),
35     _searchSenders(false),
36     _searchMsgs(true),
37     _searchOnlyRegularMsgs(true)
38 {
39 }
40
41 void ChatViewSearchController::setSearchString(const QString &searchString) {
42   QString oldSearchString = _searchString;
43   _searchString = searchString;
44   if(_scene) {
45     if(!searchString.startsWith(oldSearchString) || oldSearchString.isEmpty()) {
46       // we can't reuse our all findings... cler the scene and do it all over
47       updateHighlights();
48     } else {
49       // reuse all findings
50       updateHighlights(true);
51     }
52   }
53 }
54
55  void ChatViewSearchController::setScene(ChatScene *scene) {
56   Q_ASSERT(scene);
57   if(scene == _scene)
58     return;
59
60   if(_scene) {
61     disconnect(_scene, 0, this, 0);
62     qDeleteAll(_highlightItems);
63     _highlightItems.clear();
64   }
65
66   _scene = scene;
67   if(!scene)
68     return;
69
70   connect(_scene, SIGNAL(destroyed()), this, SLOT(sceneDestroyed()));
71   updateHighlights();
72  }
73
74
75
76 void ChatViewSearchController::updateHighlights(bool reuse) {
77   if(!_scene)
78     return;
79
80   QAbstractItemModel *model = _scene->model();
81   Q_ASSERT(model);
82
83
84   QList<ChatLine *> chatLines;
85   if(reuse) {
86     foreach(SearchHighlightItem *highlightItem, _highlightItems) {
87       ChatLine *line = dynamic_cast<ChatLine *>(highlightItem->parentItem());
88       if(!line || chatLines.contains(line))
89         continue;
90       chatLines << line;
91     }
92   }
93
94   qDeleteAll(_highlightItems);
95   _highlightItems.clear();
96   Q_ASSERT(_highlightItems.isEmpty());
97
98   if(searchString().isEmpty() || !(_searchSenders || _searchMsgs))
99     return;
100
101   if(reuse) {
102     QModelIndex index;
103     foreach(ChatLine *line, chatLines) {
104       if(_searchOnlyRegularMsgs) {
105         index = model->index(line->row(), 0);
106         if(!checkType((Message::Type)index.data(MessageModel::TypeRole).toInt()))
107           continue;
108       }
109       highlightLine(line);
110     }
111   } else {
112     // we have to crawl through the data
113     QModelIndex index;
114     QString plainText;
115     int rowCount = model->rowCount();
116     for(int row = 0; row < rowCount; row++) {
117       ChatLine *line = _scene->chatLine(row);
118
119       if(_searchOnlyRegularMsgs) {
120         index = model->index(row, 0);
121         if(!checkType((Message::Type)index.data(MessageModel::TypeRole).toInt()))
122           continue;
123       }
124       highlightLine(line);
125     }
126   }
127 }
128
129 void ChatViewSearchController::highlightLine(ChatLine *line) {
130   QList<ChatItem *> checkItems;
131   if(_searchSenders)
132     checkItems << &(line->item(MessageModel::SenderColumn));
133
134   if(_searchMsgs)
135     checkItems << &(line->item(MessageModel::ContentsColumn));
136
137   foreach(ChatItem *item, checkItems) {
138     foreach(QRectF wordRect, item->findWords(searchString(), caseSensitive())) {
139       _highlightItems << new SearchHighlightItem(wordRect.adjusted(item->x(), 0, item->x(), 0), line);
140     }
141   }
142 }
143
144 void ChatViewSearchController::sceneDestroyed() {
145   // WARNING: don't call any methods on scene!
146   _scene = 0;
147   // the items will be automatically deleted when the scene is destroyed
148   // so we just have to clear the list;
149   _highlightItems.clear();
150 }
151
152 void ChatViewSearchController::setCaseSensitive(bool caseSensitive) {
153   if(_caseSensitive == caseSensitive)
154     return;
155
156   _caseSensitive = caseSensitive;
157
158   // we can reuse the original search results if the new search
159   // parameters are a restriction of the original one
160   updateHighlights(caseSensitive);
161 }
162
163 void ChatViewSearchController::setSearchSenders(bool searchSenders) {
164   if(_searchSenders == searchSenders)
165     return;
166
167   _searchSenders = searchSenders;
168   // we can reuse the original search results if the new search
169   // parameters are a restriction of the original one
170   updateHighlights(!searchSenders);
171 }
172
173 void ChatViewSearchController::setSearchMsgs(bool searchMsgs) {
174   if(_searchMsgs == searchMsgs)
175     return;
176
177   _searchMsgs = searchMsgs;
178
179   // we can reuse the original search results if the new search
180   // parameters are a restriction of the original one
181   updateHighlights(!searchMsgs);
182 }
183
184 void ChatViewSearchController::setSearchOnlyRegularMsgs(bool searchOnlyRegularMsgs) {
185   if(_searchOnlyRegularMsgs == searchOnlyRegularMsgs)
186     return;
187
188   _searchOnlyRegularMsgs = searchOnlyRegularMsgs;
189
190   // we can reuse the original search results if the new search
191   // parameters are a restriction of the original one
192   updateHighlights(searchOnlyRegularMsgs);
193 }
194
195 SearchHighlightItem::SearchHighlightItem(QRectF wordRect, QGraphicsItem *parent)
196   : QGraphicsItem(parent)
197 {
198   setPos(wordRect.x(), wordRect.y());
199   qreal sizedelta = wordRect.height() * 0.1;
200   _boundingRect = QRectF(-sizedelta, -sizedelta, wordRect.width() + 2 * sizedelta, wordRect.height() + 2 * sizedelta);
201 }
202
203 void SearchHighlightItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) {
204   Q_UNUSED(option);
205   Q_UNUSED(widget);
206
207   painter->setPen(QPen(Qt::black, 1.5));
208   painter->setBrush(QColor(254, 237, 45));
209   painter->setRenderHints(QPainter::Antialiasing);
210   qreal radius = boundingRect().height() * 0.30;
211   painter->drawRoundedRect(boundingRect(), radius, radius);
212 }