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