1 /***************************************************************************
2 * Copyright (C) 2005-2020 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 ***************************************************************************/
28 #include <QTextLayout>
30 #include "chatlinemodel.h"
31 #include "chatscene.h"
32 #include "clickable.h"
39 /* All external positions are relative to the parent ChatLine */
40 /* Yes, that's also true for the boundingRect() and related things */
45 // boundingRect is relative to the parent ChatLine
46 ChatItem(const QRectF& boundingRect, ChatLine* parent);
50 const QAbstractItemModel* model() const;
51 ChatLine* chatLine() const;
52 ChatScene* chatScene() const;
53 ChatView* chatView() const;
55 virtual ChatLineModel::ColumnType column() const = 0;
57 // The boundingRect() is relative to the parent ChatLine
58 inline QRectF boundingRect() const;
59 inline qreal width() const;
60 inline qreal height() const;
61 inline QPointF pos() const;
62 inline qreal x() const;
63 inline qreal y() const;
65 QPointF mapToLine(const QPointF&) const;
66 QPointF mapFromLine(const QPointF&) const;
67 QPointF mapToScene(const QPointF&) const;
68 QPointF mapFromScene(const QPointF&) const;
70 virtual void paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget = nullptr);
71 virtual inline int type() const { return ChatScene::ChatItemType; }
73 QVariant data(int role) const;
75 // selection stuff, to be called by the scene
76 QString selection() const;
77 void clearSelection();
78 void setFullSelection();
79 void continueSelecting(const QPointF& pos);
80 bool hasSelection() const;
81 bool isPosOverSelection(const QPointF& pos) const;
83 QList<QRectF> findWords(const QString& searchWord, Qt::CaseSensitivity caseSensitive);
85 virtual void addActionsToMenu(QMenu* menu, const QPointF& itemPos);
86 virtual void handleClick(const QPointF& pos, ChatScene::ClickMode);
88 void initLayoutHelper(QTextLayout* layout, QTextOption::WrapMode, Qt::Alignment = Qt::AlignLeft) const;
90 //! Remove internally cached data
91 /** This removes e.g. the cached QTextLayout to avoid wasting space for nonvisible ChatLines
93 virtual void clearCache();
103 virtual void mouseMoveEvent(QGraphicsSceneMouseEvent* event);
104 virtual void mousePressEvent(QGraphicsSceneMouseEvent* event);
105 virtual void mouseReleaseEvent(QGraphicsSceneMouseEvent* event);
106 virtual void hoverEnterEvent(QGraphicsSceneHoverEvent*) {}
107 virtual void hoverLeaveEvent(QGraphicsSceneHoverEvent*) {}
108 virtual void hoverMoveEvent(QGraphicsSceneHoverEvent*) {}
110 QTextLayout* layout() const;
112 virtual void initLayout(QTextLayout* layout) const;
113 virtual void doLayout(QTextLayout*) const;
114 virtual UiStyle::FormatList formatList() const;
116 void paintBackground(QPainter*);
117 virtual QVector<QTextLayout::FormatRange> additionalFormats() const;
118 void overlayFormat(UiStyle::FormatList& fmtList, quint16 start, quint16 end, UiStyle::FormatType overlayFmt) const;
120 inline qint16 selectionStart() const { return _selectionStart; }
121 inline void setSelectionStart(qint16 start) { _selectionStart = start; }
122 inline qint16 selectionEnd() const { return _selectionEnd; }
123 inline void setSelectionEnd(qint16 end) { _selectionEnd = end; }
124 inline SelectionMode selectionMode() const { return _selectionMode; }
125 inline void setSelectionMode(SelectionMode mode) { _selectionMode = mode; }
126 void setSelection(SelectionMode mode, qint16 selectionStart, qint16 selectionEnd);
128 virtual bool hasActiveClickable() const;
129 virtual std::pair<quint16, quint16> activeClickableRange() const;
131 qint16 posToCursor(const QPointF& pos) const;
133 inline void setGeometry(qreal width, qreal height)
136 _boundingRect.setSize(QSizeF(width, height));
138 inline void setHeight(const qreal& height)
141 _boundingRect.setHeight(height);
143 inline void setWidth(const qreal& width)
146 _boundingRect.setWidth(width);
148 inline void setPos(const QPointF& pos) { _boundingRect.moveTopLeft(pos); }
152 QRectF _boundingRect;
154 SelectionMode _selectionMode;
155 qint16 _selectionStart, _selectionEnd;
157 mutable QTextLayout* _cachedLayout;
159 // internal selection stuff
160 void setSelection(int start, int length);
162 friend class ChatLine;
165 // ************************************************************
167 // ************************************************************
169 //! A ChatItem for the timestamp column
170 class TimestampChatItem : public ChatItem
173 TimestampChatItem(const QRectF& boundingRect, ChatLine* parent)
174 : ChatItem(boundingRect, parent)
176 inline int type() const override { return ChatScene::TimestampChatItemType; }
177 inline ChatLineModel::ColumnType column() const override { return ChatLineModel::TimestampColumn; }
180 // ************************************************************
182 // ************************************************************
183 //! A ChatItem for the sender column
184 class SenderChatItem : public ChatItem
187 SenderChatItem(const QRectF& boundingRect, ChatLine* parent)
188 : ChatItem(boundingRect, parent)
190 inline ChatLineModel::ColumnType column() const override { return ChatLineModel::SenderColumn; }
191 void handleClick(const QPointF& pos, ChatScene::ClickMode clickMode) override;
194 void paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget = nullptr) override;
195 inline int type() const override { return ChatScene::SenderChatItemType; }
196 void initLayout(QTextLayout* layout) const override;
199 // ************************************************************
201 // ************************************************************
202 struct ContentsChatItemPrivate;
204 //! A ChatItem for the contents column
205 class ContentsChatItem : public ChatItem
207 Q_DECLARE_TR_FUNCTIONS(ContentsChatItem)
210 ContentsChatItem(const QPointF& pos, const qreal& width, ChatLine* parent);
211 ~ContentsChatItem() override;
213 inline int type() const override { return ChatScene::ContentsChatItemType; }
215 inline ChatLineModel::ColumnType column() const override { return ChatLineModel::ContentsColumn; }
216 QFontMetricsF* fontMetrics() const;
218 void clearCache() override;
221 void mouseMoveEvent(QGraphicsSceneMouseEvent* event) override;
222 void hoverLeaveEvent(QGraphicsSceneHoverEvent* event) override;
223 void hoverMoveEvent(QGraphicsSceneHoverEvent* event) override;
224 void handleClick(const QPointF& pos, ChatScene::ClickMode clickMode) override;
226 bool hasActiveClickable() const override;
227 std::pair<quint16, quint16> activeClickableRange() const override;
229 void addActionsToMenu(QMenu* menu, const QPointF& itemPos) override;
230 virtual void copyLinkToClipboard();
232 void initLayout(QTextLayout* layout) const override;
233 void doLayout(QTextLayout* layout) const override;
234 UiStyle::FormatList formatList() const override;
238 class WrapColumnFinder;
240 mutable ContentsChatItemPrivate* _data;
241 ContentsChatItemPrivate* privateData() const;
243 Clickable clickableAt(const QPointF& pos) const;
246 void showWebPreview(const Clickable& click);
247 void clearWebPreview();
249 qreal setGeometryByWidth(qreal w);
251 QFontMetricsF* _fontMetrics;
253 // we need a receiver for Action signals
254 static ActionProxy _actionProxy;
256 friend class ChatLine;
257 friend struct ContentsChatItemPrivate;
260 struct ContentsChatItemPrivate
262 ContentsChatItem* contentsItem;
263 ClickableList clickables;
264 Clickable currentClickable;
265 Clickable activeClickable;
267 ContentsChatItemPrivate(ClickableList c, ContentsChatItem* parent)
268 : contentsItem(parent)
269 , clickables(std::move(c))
273 class ContentsChatItem::WrapColumnFinder
276 WrapColumnFinder(const ChatItem* parent);
278 qint16 nextWrapColumn(qreal width);
281 const ChatItem* item;
284 ChatLineModel::WrapList wrapList;
287 qreal choppedTrailing;
290 //! Acts as a proxy for Action signals targetted at a ContentsChatItem
291 /** Since a ChatItem is not a QObject, hence cannot receive signals, we use a static ActionProxy
292 * as a receiver instead. This avoids having to handle ChatItem actions (e.g. context menu entries)
293 * outside the ChatItem.
295 class ContentsChatItem::ActionProxy : public QObject
300 inline void copyLinkToClipboard() { item()->copyLinkToClipboard(); }
303 /// Returns the ContentsChatItem that should receive the action event.
304 /** For efficiency reasons, values are not checked for validity. You gotta make sure that you set the data() member
305 * in the Action correctly.
306 * @return The ChatItem from which the sending Action originated
308 inline ContentsChatItem* item() const
310 return static_cast<ContentsChatItem*>(qobject_cast<QAction*>(sender())->data().value<void*>());
314 /*************************************************************************************************/
318 QRectF ChatItem::boundingRect() const
320 return _boundingRect;
322 qreal ChatItem::width() const
324 return _boundingRect.width();
326 qreal ChatItem::height() const
328 return _boundingRect.height();
330 QPointF ChatItem::pos() const
332 return _boundingRect.topLeft();
334 qreal ChatItem::x() const
338 qreal ChatItem::y() const