1 /***************************************************************************
2 * Copyright (C) 2005-2019 by the Quassel Project *
3 * devel@quassel-irc.org *
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. *
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. *
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 ***************************************************************************/
24 #include <QAbstractItemModel>
26 #include <QGraphicsItem>
27 #include <QGraphicsScene>
32 #include "chatlinemodel.h"
33 #include "messagefilter.h"
39 class ColumnHandleItem;
43 class QGraphicsSceneMouseEvent;
45 class ChatScene : public QGraphicsScene
58 ChatLineType = QGraphicsItem::UserType + 1,
60 TimestampChatItemType,
78 ChatScene(QAbstractItemModel* model, QString idString, qreal width, ChatView* parent);
80 inline QAbstractItemModel* model() const { return _model; }
81 inline MessageFilter* filter() const { return qobject_cast<MessageFilter*>(_model); }
82 inline QString idString() const { return _idString; }
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()); }
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()); }
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
105 ChatLine* chatLine(MsgId msgId, bool matchExact = true, bool ignoreDayChange = true) const;
107 inline ChatLine* lastLine() const { return _lines.count() ? _lines.last() : 0; }
109 inline MarkerLineItem* markerLine() const { return _markerLine; }
111 inline bool isSingleBufferScene() const { return _singleBufferId.isValid(); }
112 inline BufferId singleBufferId() const { return _singleBufferId; }
113 bool containsBuffer(const BufferId& id) const;
115 ColumnHandleItem* firstColumnHandle() const;
116 ColumnHandleItem* secondColumnHandle() const;
118 inline CutoffMode senderCutoffMode() const { return _cutoffMode; }
119 inline void setSenderCutoffMode(CutoffMode mode) { _cutoffMode = mode; }
122 * Gets whether to re-add hidden brackets around sender for all message types
124 * Used within the Chat Monitor as the normal message prefixes are overridden.
126 * @return Whether to re-add hidden brackets around sender for all message types
128 inline bool alwaysBracketSender() const { return _alwaysBracketSender; }
130 * Sets whether to re-add hidden brackets around sender for all message types
132 * @see ChatScene::alwaysBracketSender()
134 * @param brackets Sets whether to re-add hidden brackets around sender for all message types
136 inline void setAlwaysBracketSender(bool alwaysBracket) { _alwaysBracketSender = alwaysBracket; }
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);
145 bool isScrollingAllowed() const;
148 void updateForViewport(qreal width, qreal height);
149 void setWidth(qreal width);
150 void layout(int start, int end, qreal width);
152 void resetColumnWidths();
154 void setMarkerLineVisible(bool visible = true);
155 void setMarkerLine(MsgId msgId = MsgId());
156 void jumpToMarkerLine(bool requestBacklog);
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);
167 void webSearchOnSelection();
169 void requestBacklog();
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);
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);
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);
190 void rowsInserted(const QModelIndex&, int, int);
191 void rowsAboutToBeRemoved(const QModelIndex&, int, int);
192 void dataChanged(const QModelIndex&, const QModelIndex&);
195 void firstHandlePositionChanged(qreal xpos);
196 void secondHandlePositionChanged(qreal xpos);
197 #if defined HAVE_WEBKIT || defined HAVE_WEBENGINE
198 void webPreviewNextStep();
200 void showWebPreviewChanged();
203 * Updates the local setting cache of whether or not to show sender brackets
205 void showSenderBracketsChanged();
208 * Updates the local setting cache of whether or not to use the custom timestamp format
210 void useCustomTimestampFormatChanged();
213 * Updates the local setting cache of the timestamp format string
215 void timestampFormatStringChanged();
218 * Updates the status of whether or not the timestamp format string contains brackets
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.
224 void updateTimestampHasBrackets();
231 void setHandleXLimits();
232 void updateSelection(const QPointF& pos);
236 QAbstractItemModel* _model;
237 QList<ChatLine*> _lines;
238 BufferId _singleBufferId;
240 // calls to QChatScene::sceneRect() are very expensive. As we manage the scenerect ourselves
241 // we store the size in a member variable.
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;
249 MarkerLineItem* _markerLine;
250 bool _markerLineVisible, _markerLineValid, _markerLineJumpPending;
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;
259 ChatItem* _selectingItem;
260 int _selectionStartCol, _selectionMinCol;
263 int _firstSelectionRow;
267 ClickMode _clickMode;
270 bool _leftButtonPressed;
272 bool _showWebPreview;
274 bool _showSenderBrackets; /// If true, show brackets around sender names
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
280 static const int _webSearchSelectionTextMaxVisible = 24;
282 #if defined HAVE_WEBKIT || defined HAVE_WEBENGINE
293 ChatItem* parentItem{nullptr};
294 QGraphicsItem* previewItem{nullptr};
297 PreviewState previewState{NoPreview};
300 WebPreview webPreview;
301 #endif // HAVE_WEBKIT || HAVE_WEBENGINE