1 | /*************************************************************************** |
2 | * Copyright (C) 2005-2014 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 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * |
19 | ***************************************************************************/ |
20 | |
21 | #ifndef CHATSCENE_H_ |
22 | #define CHATSCENE_H_ |
23 | |
24 | #include <QAbstractItemModel> |
25 | #include <QClipboard> |
26 | #include <QGraphicsItem> |
27 | #include <QGraphicsScene> |
28 | #include <QSet> |
29 | #include <QTimer> |
30 | #include <QUrl> |
31 | |
32 | #include "chatlinemodel.h" |
33 | #include "messagefilter.h" |
34 | |
35 | class AbstractUiMsg; |
36 | class ChatItem; |
37 | class ChatLine; |
38 | class ChatView; |
39 | class ColumnHandleItem; |
40 | class MarkerLineItem; |
41 | class WebPreviewItem; |
42 | |
43 | class QGraphicsSceneMouseEvent; |
44 | |
45 | class ChatScene : public QGraphicsScene |
46 | { |
47 | Q_OBJECT |
48 | |
49 | public: |
50 | enum CutoffMode { |
51 | CutoffLeft, |
52 | CutoffRight |
53 | }; |
54 | |
55 | enum ItemType { |
56 | ChatLineType = QGraphicsItem::UserType + 1, |
57 | ChatItemType, |
58 | TimestampChatItemType, |
59 | SenderChatItemType, |
60 | ContentsChatItemType, |
61 | SearchHighlightType, |
62 | WebPreviewType, |
63 | ColumnHandleType, |
64 | MarkerLineType |
65 | }; |
66 | |
67 | enum ClickMode { |
68 | NoClick, |
69 | DragStartClick, |
70 | SingleClick, |
71 | DoubleClick, |
72 | TripleClick |
73 | }; |
74 | |
75 | ChatScene(QAbstractItemModel *model, const QString &idString, qreal width, ChatView *parent); |
76 | virtual ~ChatScene(); |
77 | |
78 | inline QAbstractItemModel *model() const { return _model; } |
79 | inline MessageFilter *filter() const { return qobject_cast<MessageFilter *>(_model); } |
80 | inline QString idString() const { return _idString; } |
81 | |
82 | int rowByScenePos(qreal y) const; |
83 | inline int rowByScenePos(const QPointF &pos) const { return rowByScenePos(pos.y()); } |
84 | ChatLineModel::ColumnType columnByScenePos(qreal x) const; |
85 | inline ChatLineModel::ColumnType columnByScenePos(const QPointF &pos) const { return columnByScenePos(pos.x()); } |
86 | |
87 | ChatView *chatView() const; |
88 | ChatItem *chatItemAt(const QPointF &pos) const; |
89 | inline ChatLine *chatLine(int row) const { return (row < _lines.count()) ? _lines.value(row) : 0; } |
90 | inline ChatLine *chatLine(const QModelIndex &index) const { return _lines.value(index.row()); } |
91 | |
92 | //! Find the ChatLine belonging to a MsgId |
93 | /** Searches for the ChatLine belonging to a MsgId. If there are more than one ChatLine with the same msgId, |
94 | * the first one is returned. |
95 | * Note that this method performs a binary search, hence it has as complexity of O(log n). |
96 | * If matchExact is false, and we don't have an exact match for the given msgId, we return the visible line right |
97 | * above the requested one. |
98 | * \param msgId The message ID to look for |
99 | * \param matchExact Whether we find only exact matches |
100 | * \param ignoreDayChange Whether we ignore day change messages |
101 | * \return The ChatLine corresponding to the given MsgId |
102 | */ |
103 | ChatLine *chatLine(MsgId msgId, bool matchExact = true, bool ignoreDayChange = true) const; |
104 | |
105 | inline ChatLine *lastLine() const { return _lines.count() ? _lines.last() : 0; } |
106 | |
107 | inline MarkerLineItem *markerLine() const { return _markerLine; } |
108 | |
109 | inline bool isSingleBufferScene() const { return _singleBufferId.isValid(); } |
110 | inline BufferId singleBufferId() const { return _singleBufferId; } |
111 | bool containsBuffer(const BufferId &id) const; |
112 | |
113 | ColumnHandleItem *firstColumnHandle() const; |
114 | ColumnHandleItem *secondColumnHandle() const; |
115 | |
116 | inline CutoffMode senderCutoffMode() const { return _cutoffMode; } |
117 | inline void setSenderCutoffMode(CutoffMode mode) { _cutoffMode = mode; } |
118 | |
119 | QString selection() const; |
120 | bool hasSelection() const; |
121 | bool hasGlobalSelection() const; |
122 | bool isPosOverSelection(const QPointF &) const; |
123 | bool isGloballySelecting() const; |
124 | void initiateDrag(QWidget *source); |
125 | |
126 | bool isScrollingAllowed() const; |
127 | |
128 | public slots: |
129 | void updateForViewport(qreal width, qreal height); |
130 | void setWidth(qreal width); |
131 | void layout(int start, int end, qreal width); |
132 | |
133 | void resetColumnWidths(); |
134 | |
135 | void setMarkerLineVisible(bool visible = true); |
136 | void setMarkerLine(MsgId msgId = MsgId()); |
137 | void jumpToMarkerLine(bool requestBacklog); |
138 | |
139 | // these are used by the chatitems to notify the scene and manage selections |
140 | void setSelectingItem(ChatItem *item); |
141 | ChatItem *selectingItem() const { return _selectingItem; } |
142 | void startGlobalSelection(ChatItem *item, const QPointF &itemPos); |
143 | void clearGlobalSelection(); |
144 | void clearSelection(); |
145 | void selectionToClipboard(QClipboard::Mode = QClipboard::Clipboard); |
146 | void stringToClipboard(const QString &str, QClipboard::Mode = QClipboard::Clipboard); |
147 | |
148 | void webSearchOnSelection(); |
149 | |
150 | void requestBacklog(); |
151 | |
152 | #ifdef HAVE_WEBKIT |
153 | void loadWebPreview(ChatItem *parentItem, const QUrl &url, const QRectF &urlRect); |
154 | void clearWebPreview(ChatItem *parentItem = 0); |
155 | #endif |
156 | |
157 | signals: |
158 | void lastLineChanged(QGraphicsItem *item, qreal offset); |
159 | void layoutChanged(); // indicates changes to the scenerect due to resizing of the contentsitems |
160 | void mouseMoveWhileSelecting(const QPointF &scenePos); |
161 | |
162 | protected: |
163 | virtual void (QGraphicsSceneContextMenuEvent *); |
164 | virtual void mouseMoveEvent(QGraphicsSceneMouseEvent *mouseEvent); |
165 | virtual void mousePressEvent(QGraphicsSceneMouseEvent *mouseEvent); |
166 | virtual void mouseReleaseEvent(QGraphicsSceneMouseEvent *mouseEvent); |
167 | virtual void mouseDoubleClickEvent(QGraphicsSceneMouseEvent *mouseEvent); |
168 | virtual void handleClick(Qt::MouseButton button, const QPointF &scenePos); |
169 | |
170 | protected slots: |
171 | void rowsInserted(const QModelIndex &, int, int); |
172 | void rowsAboutToBeRemoved(const QModelIndex &, int, int); |
173 | void dataChanged(const QModelIndex &, const QModelIndex &); |
174 | |
175 | private slots: |
176 | void firstHandlePositionChanged(qreal xpos); |
177 | void secondHandlePositionChanged(qreal xpos); |
178 | #ifdef HAVE_WEBKIT |
179 | void webPreviewNextStep(); |
180 | #endif |
181 | void showWebPreviewChanged(); |
182 | |
183 | void rowsRemoved(); |
184 | |
185 | void clickTimeout(); |
186 | |
187 | private: |
188 | void setHandleXLimits(); |
189 | void updateSelection(const QPointF &pos); |
190 | |
191 | ChatView *_chatView; |
192 | QString _idString; |
193 | QAbstractItemModel *_model; |
194 | QList<ChatLine *> _lines; |
195 | BufferId _singleBufferId; |
196 | |
197 | // calls to QChatScene::sceneRect() are very expensive. As we manage the scenerect ourselves |
198 | // we store the size in a member variable. |
199 | QRectF _sceneRect; |
200 | int _firstLineRow; // the first row to display (aka: not a daychange msg) |
201 | void updateSceneRect(qreal width); |
202 | inline void updateSceneRect() { updateSceneRect(_sceneRect.width()); } |
203 | void updateSceneRect(const QRectF &rect); |
204 | qreal _viewportHeight; |
205 | |
206 | MarkerLineItem *_markerLine; |
207 | bool _markerLineVisible, _markerLineValid, _markerLineJumpPending; |
208 | |
209 | ColumnHandleItem *_firstColHandle, *_secondColHandle; |
210 | qreal _firstColHandlePos, _secondColHandlePos; |
211 | int _defaultFirstColHandlePos, _defaultSecondColHandlePos; |
212 | CutoffMode _cutoffMode; |
213 | |
214 | ChatItem *_selectingItem; |
215 | int _selectionStartCol, _selectionMinCol; |
216 | int _selectionStart; |
217 | int _selectionEnd; |
218 | int _firstSelectionRow; |
219 | bool _isSelecting; |
220 | |
221 | QTimer _clickTimer; |
222 | ClickMode _clickMode; |
223 | QPointF _clickPos; |
224 | bool _clickHandled; |
225 | bool _leftButtonPressed; |
226 | |
227 | bool _showWebPreview; |
228 | |
229 | static const int _webSearchSelectionTextMaxVisible = 24; |
230 | |
231 | #ifdef HAVE_WEBKIT |
232 | struct WebPreview { |
233 | enum PreviewState { |
234 | NoPreview, |
235 | NewPreview, |
236 | DelayPreview, |
237 | ShowPreview, |
238 | HidePreview |
239 | }; |
240 | ChatItem *parentItem; |
241 | QGraphicsItem *previewItem; |
242 | QUrl url; |
243 | QRectF urlRect; |
244 | PreviewState previewState; |
245 | QTimer timer; |
246 | WebPreview() : parentItem(0), previewItem(0), previewState(NoPreview) {} |
247 | }; |
248 | WebPreview webPreview; |
249 | #endif // HAVE_WEBKIT |
250 | }; |
251 | |
252 | |
253 | #endif |
254 | |