modernize: Use override instead of virtual
[quassel.git] / src / qtui / chatscene.h
1 /***************************************************************************
2  *   Copyright (C) 2005-2018 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     ~ChatScene() override;
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     /**
120      * Gets whether to re-add hidden brackets around sender for all message types
121      *
122      * Used within the Chat Monitor as the normal message prefixes are overridden.
123      *
124      * @return Whether to re-add hidden brackets around sender for all message types
125      */
126     inline bool alwaysBracketSender() const { return _alwaysBracketSender; }
127     /**
128      * Sets whether to re-add hidden brackets around sender for all message types
129      *
130      * @see ChatScene::alwaysBracketSender()
131      *
132      * @param brackets Sets whether to re-add hidden brackets around sender for all message types
133      */
134     inline void setAlwaysBracketSender(bool alwaysBracket) { _alwaysBracketSender = alwaysBracket; }
135
136     QString selection() const;
137     bool hasSelection() const;
138     bool hasGlobalSelection() const;
139     bool isPosOverSelection(const QPointF &) const;
140     bool isGloballySelecting() const;
141     void initiateDrag(QWidget *source);
142
143     bool isScrollingAllowed() const;
144
145 public slots:
146     void updateForViewport(qreal width, qreal height);
147     void setWidth(qreal width);
148     void layout(int start, int end, qreal width);
149
150     void resetColumnWidths();
151
152     void setMarkerLineVisible(bool visible = true);
153     void setMarkerLine(MsgId msgId = MsgId());
154     void jumpToMarkerLine(bool requestBacklog);
155
156     // these are used by the chatitems to notify the scene and manage selections
157     void setSelectingItem(ChatItem *item);
158     ChatItem *selectingItem() const { return _selectingItem; }
159     void startGlobalSelection(ChatItem *item, const QPointF &itemPos);
160     void clearGlobalSelection();
161     void clearSelection();
162     void selectionToClipboard(QClipboard::Mode = QClipboard::Clipboard);
163     void stringToClipboard(const QString &str, QClipboard::Mode = QClipboard::Clipboard);
164
165     void webSearchOnSelection();
166
167     void requestBacklog();
168
169 #if defined HAVE_WEBKIT || defined HAVE_WEBENGINE
170     void loadWebPreview(ChatItem *parentItem, const QUrl &url, const QRectF &urlRect);
171     void clearWebPreview(ChatItem *parentItem = nullptr);
172 #endif
173
174 signals:
175     void lastLineChanged(QGraphicsItem *item, qreal offset);
176     void layoutChanged(); // indicates changes to the scenerect due to resizing of the contentsitems
177     void mouseMoveWhileSelecting(const QPointF &scenePos);
178
179 protected:
180     void contextMenuEvent(QGraphicsSceneContextMenuEvent *contextMenuEvent) override;
181     void mouseMoveEvent(QGraphicsSceneMouseEvent *mouseEvent) override;
182     void mousePressEvent(QGraphicsSceneMouseEvent *mouseEvent) override;
183     void mouseReleaseEvent(QGraphicsSceneMouseEvent *mouseEvent) override;
184     void mouseDoubleClickEvent(QGraphicsSceneMouseEvent *mouseEvent) override;
185     virtual void handleClick(Qt::MouseButton button, const QPointF &scenePos);
186
187 protected slots:
188     void rowsInserted(const QModelIndex &, int, int);
189     void rowsAboutToBeRemoved(const QModelIndex &, int, int);
190     void dataChanged(const QModelIndex &, const QModelIndex &);
191
192 private slots:
193     void firstHandlePositionChanged(qreal xpos);
194     void secondHandlePositionChanged(qreal xpos);
195 #if defined HAVE_WEBKIT || defined HAVE_WEBENGINE
196     void webPreviewNextStep();
197 #endif
198     void showWebPreviewChanged();
199
200     /**
201      * Updates the local setting cache of whether or not to show sender brackets
202      */
203     void showSenderBracketsChanged();
204
205     /**
206      * Updates the local setting cache of whether or not to use the custom timestamp format
207      */
208     void useCustomTimestampFormatChanged();
209
210     /**
211      * Updates the local setting cache of the timestamp format string
212      */
213     void timestampFormatStringChanged();
214
215     /**
216      * Updates the status of whether or not the timestamp format string contains brackets
217      *
218      * When the timestamp contains brackets -and- showSenderBrackets is disabled, we need to
219      * automatically add brackets.  This function checks if the timestamp has brackets and stores
220      * the result, rather than checking each time text is copied.
221      */
222     void updateTimestampHasBrackets();
223
224     void rowsRemoved();
225
226     void clickTimeout();
227
228 private:
229     void setHandleXLimits();
230     void updateSelection(const QPointF &pos);
231
232     ChatView *_chatView;
233     QString _idString;
234     QAbstractItemModel *_model;
235     QList<ChatLine *> _lines;
236     BufferId _singleBufferId;
237
238     // calls to QChatScene::sceneRect() are very expensive. As we manage the scenerect ourselves
239     // we store the size in a member variable.
240     QRectF _sceneRect;
241     int _firstLineRow; // the first row to display (aka: not a daychange msg)
242     void updateSceneRect(qreal width);
243     inline void updateSceneRect() { updateSceneRect(_sceneRect.width()); }
244     void updateSceneRect(const QRectF &rect);
245     qreal _viewportHeight;
246
247     MarkerLineItem *_markerLine;
248     bool _markerLineVisible, _markerLineValid, _markerLineJumpPending;
249
250     ColumnHandleItem *_firstColHandle, *_secondColHandle;
251     qreal _firstColHandlePos, _secondColHandlePos;
252     int _defaultFirstColHandlePos, _defaultSecondColHandlePos;
253     CutoffMode _cutoffMode;
254     /// Whether to re-add hidden brackets around sender for all message types
255     bool _alwaysBracketSender;
256
257     ChatItem *_selectingItem;
258     int _selectionStartCol, _selectionMinCol;
259     int _selectionStart;
260     int _selectionEnd;
261     int _firstSelectionRow;
262     bool _isSelecting;
263
264     QTimer _clickTimer;
265     ClickMode _clickMode;
266     QPointF _clickPos;
267     bool _clickHandled;
268     bool _leftButtonPressed;
269
270     bool _showWebPreview;
271
272     bool _showSenderBrackets;  /// If true, show brackets around sender names
273
274     bool _useCustomTimestampFormat; /// If true, use the custom timestamp format
275     QString _timestampFormatString; /// Format of the timestamp string
276     bool _timestampHasBrackets;     /// If true, timestamp format has [brackets] of some sort
277
278     static const int _webSearchSelectionTextMaxVisible = 24;
279
280 #if defined HAVE_WEBKIT || defined HAVE_WEBENGINE
281     struct WebPreview {
282         enum PreviewState {
283             NoPreview,
284             NewPreview,
285             DelayPreview,
286             ShowPreview,
287             HidePreview
288         };
289         ChatItem *parentItem;
290         QGraphicsItem *previewItem;
291         QUrl url;
292         QRectF urlRect;
293         PreviewState previewState;
294         QTimer timer;
295         WebPreview() : parentItem(nullptr), previewItem(nullptr), previewState(NoPreview) {}
296     };
297     WebPreview webPreview;
298 #endif // HAVE_WEBKIT || HAVE_WEBENGINE
299 };
300
301
302 #endif