8bea8fd90ec3f218f52ecb4baa18a71f38a38b3a
[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     {
52         CutoffLeft,
53         CutoffRight
54     };
55
56     enum ItemType
57     {
58         ChatLineType = QGraphicsItem::UserType + 1,
59         ChatItemType,
60         TimestampChatItemType,
61         SenderChatItemType,
62         ContentsChatItemType,
63         SearchHighlightType,
64         WebPreviewType,
65         ColumnHandleType,
66         MarkerLineType
67     };
68
69     enum ClickMode
70     {
71         NoClick,
72         DragStartClick,
73         SingleClick,
74         DoubleClick,
75         TripleClick
76     };
77
78     ChatScene(QAbstractItemModel* model, QString idString, qreal width, ChatView* parent);
79
80     inline QAbstractItemModel* model() const { return _model; }
81     inline MessageFilter* filter() const { return qobject_cast<MessageFilter*>(_model); }
82     inline QString idString() const { return _idString; }
83
84     int rowByScenePos(qreal y) const;
85     inline int rowByScenePos(const QPointF& pos) const { return rowByScenePos(pos.y()); }
86     ChatLineModel::ColumnType columnByScenePos(qreal x) const;
87     inline ChatLineModel::ColumnType columnByScenePos(const QPointF& pos) const { return columnByScenePos(pos.x()); }
88
89     ChatView* chatView() const;
90     ChatItem* chatItemAt(const QPointF& pos) const;
91     inline ChatLine* chatLine(int row) const { return (row < _lines.count()) ? _lines.value(row) : 0; }
92     inline ChatLine* chatLine(const QModelIndex& index) const { return _lines.value(index.row()); }
93
94     //! Find the ChatLine belonging to a MsgId
95     /** Searches for the ChatLine belonging to a MsgId. If there are more than one ChatLine with the same msgId,
96      *  the first one is returned.
97      *  Note that this method performs a binary search, hence it has as complexity of O(log n).
98      *  If matchExact is false, and we don't have an exact match for the given msgId, we return the visible line right
99      *  above the requested one.
100      *  \param msgId      The message ID to look for
101      *  \param matchExact Whether we find only exact matches
102      *  \param ignoreDayChange Whether we ignore day change messages
103      *  \return The ChatLine corresponding to the given MsgId
104      */
105     ChatLine* chatLine(MsgId msgId, bool matchExact = true, bool ignoreDayChange = true) const;
106
107     inline ChatLine* lastLine() const { return _lines.count() ? _lines.last() : 0; }
108
109     inline MarkerLineItem* markerLine() const { return _markerLine; }
110
111     inline bool isSingleBufferScene() const { return _singleBufferId.isValid(); }
112     inline BufferId singleBufferId() const { return _singleBufferId; }
113     bool containsBuffer(const BufferId& id) const;
114
115     ColumnHandleItem* firstColumnHandle() const;
116     ColumnHandleItem* secondColumnHandle() const;
117
118     inline CutoffMode senderCutoffMode() const { return _cutoffMode; }
119     inline void setSenderCutoffMode(CutoffMode mode) { _cutoffMode = mode; }
120
121     /**
122      * Gets whether to re-add hidden brackets around sender for all message types
123      *
124      * Used within the Chat Monitor as the normal message prefixes are overridden.
125      *
126      * @return Whether to re-add hidden brackets around sender for all message types
127      */
128     inline bool alwaysBracketSender() const { return _alwaysBracketSender; }
129     /**
130      * Sets whether to re-add hidden brackets around sender for all message types
131      *
132      * @see ChatScene::alwaysBracketSender()
133      *
134      * @param brackets Sets whether to re-add hidden brackets around sender for all message types
135      */
136     inline void setAlwaysBracketSender(bool alwaysBracket) { _alwaysBracketSender = alwaysBracket; }
137
138     QString selection() const;
139     bool hasSelection() const;
140     bool hasGlobalSelection() const;
141     bool isPosOverSelection(const QPointF&) const;
142     bool isGloballySelecting() const;
143     void initiateDrag(QWidget* source);
144
145     bool isScrollingAllowed() const;
146
147 public slots:
148     void updateForViewport(qreal width, qreal height);
149     void setWidth(qreal width);
150     void layout(int start, int end, qreal width);
151
152     void resetColumnWidths();
153
154     void setMarkerLineVisible(bool visible = true);
155     void setMarkerLine(MsgId msgId = MsgId());
156     void jumpToMarkerLine(bool requestBacklog);
157
158     // these are used by the chatitems to notify the scene and manage selections
159     void setSelectingItem(ChatItem* item);
160     ChatItem* selectingItem() const { return _selectingItem; }
161     void startGlobalSelection(ChatItem* item, const QPointF& itemPos);
162     void clearGlobalSelection();
163     void clearSelection();
164     void selectionToClipboard(QClipboard::Mode = QClipboard::Clipboard);
165     void stringToClipboard(const QString& str, QClipboard::Mode = QClipboard::Clipboard);
166
167     void webSearchOnSelection();
168
169     void requestBacklog();
170
171 #if defined HAVE_WEBKIT || defined HAVE_WEBENGINE
172     void loadWebPreview(ChatItem* parentItem, const QUrl& url, const QRectF& urlRect);
173     void clearWebPreview(ChatItem* parentItem = nullptr);
174 #endif
175
176 signals:
177     void lastLineChanged(QGraphicsItem* item, qreal offset);
178     void layoutChanged();  // indicates changes to the scenerect due to resizing of the contentsitems
179     void mouseMoveWhileSelecting(const QPointF& scenePos);
180
181 protected:
182     void contextMenuEvent(QGraphicsSceneContextMenuEvent* contextMenuEvent) override;
183     void mouseMoveEvent(QGraphicsSceneMouseEvent* mouseEvent) override;
184     void mousePressEvent(QGraphicsSceneMouseEvent* mouseEvent) override;
185     void mouseReleaseEvent(QGraphicsSceneMouseEvent* mouseEvent) override;
186     void mouseDoubleClickEvent(QGraphicsSceneMouseEvent* mouseEvent) override;
187     virtual void handleClick(Qt::MouseButton button, const QPointF& scenePos);
188
189 protected slots:
190     void rowsInserted(const QModelIndex&, int, int);
191     void rowsAboutToBeRemoved(const QModelIndex&, int, int);
192     void dataChanged(const QModelIndex&, const QModelIndex&);
193
194 private slots:
195     void firstHandlePositionChanged(qreal xpos);
196     void secondHandlePositionChanged(qreal xpos);
197 #if defined HAVE_WEBKIT || defined HAVE_WEBENGINE
198     void webPreviewNextStep();
199 #endif
200     void showWebPreviewChanged();
201
202     /**
203      * Updates the local setting cache of whether or not to show sender brackets
204      */
205     void showSenderBracketsChanged();
206
207     /**
208      * Updates the local setting cache of whether or not to use the custom timestamp format
209      */
210     void useCustomTimestampFormatChanged();
211
212     /**
213      * Updates the local setting cache of the timestamp format string
214      */
215     void timestampFormatStringChanged();
216
217     /**
218      * Updates the status of whether or not the timestamp format string contains brackets
219      *
220      * When the timestamp contains brackets -and- showSenderBrackets is disabled, we need to
221      * automatically add brackets.  This function checks if the timestamp has brackets and stores
222      * the result, rather than checking each time text is copied.
223      */
224     void updateTimestampHasBrackets();
225
226     void rowsRemoved();
227
228     void clickTimeout();
229
230 private:
231     void setHandleXLimits();
232     void updateSelection(const QPointF& pos);
233
234     ChatView* _chatView;
235     QString _idString;
236     QAbstractItemModel* _model;
237     QList<ChatLine*> _lines;
238     BufferId _singleBufferId;
239
240     // calls to QChatScene::sceneRect() are very expensive. As we manage the scenerect ourselves
241     // we store the size in a member variable.
242     QRectF _sceneRect;
243     int _firstLineRow;  // the first row to display (aka: not a daychange msg)
244     void updateSceneRect(qreal width);
245     inline void updateSceneRect() { updateSceneRect(_sceneRect.width()); }
246     void updateSceneRect(const QRectF& rect);
247     qreal _viewportHeight;
248
249     MarkerLineItem* _markerLine;
250     bool _markerLineVisible, _markerLineValid, _markerLineJumpPending;
251
252     ColumnHandleItem *_firstColHandle, *_secondColHandle;
253     qreal _firstColHandlePos, _secondColHandlePos;
254     int _defaultFirstColHandlePos, _defaultSecondColHandlePos;
255     CutoffMode _cutoffMode;
256     /// Whether to re-add hidden brackets around sender for all message types
257     bool _alwaysBracketSender;
258
259     ChatItem* _selectingItem;
260     int _selectionStartCol, _selectionMinCol;
261     int _selectionStart;
262     int _selectionEnd;
263     int _firstSelectionRow;
264     bool _isSelecting;
265
266     QTimer _clickTimer;
267     ClickMode _clickMode;
268     QPointF _clickPos;
269     bool _clickHandled;
270     bool _leftButtonPressed;
271
272     bool _showWebPreview;
273
274     bool _showSenderBrackets;  /// If true, show brackets around sender names
275
276     bool _useCustomTimestampFormat;  /// If true, use the custom timestamp format
277     QString _timestampFormatString;  /// Format of the timestamp string
278     bool _timestampHasBrackets;      /// If true, timestamp format has [brackets] of some sort
279
280     static const int _webSearchSelectionTextMaxVisible = 24;
281
282 #if defined HAVE_WEBKIT || defined HAVE_WEBENGINE
283     struct WebPreview
284     {
285         enum PreviewState
286         {
287             NoPreview,
288             NewPreview,
289             DelayPreview,
290             ShowPreview,
291             HidePreview
292         };
293         ChatItem* parentItem{nullptr};
294         QGraphicsItem* previewItem{nullptr};
295         QUrl url;
296         QRectF urlRect;
297         PreviewState previewState{NoPreview};
298         QTimer timer;
299     };
300     WebPreview webPreview;
301 #endif  // HAVE_WEBKIT || HAVE_WEBENGINE
302 };
303
304 #endif