1 /***************************************************************************
2 * Copyright (C) 2005-2018 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 ***************************************************************************/
27 #include "chatlinemodel.h"
28 #include "chatscene.h"
29 #include "clickable.h"
33 #include <QTextLayout>
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();
102 virtual void mouseMoveEvent(QGraphicsSceneMouseEvent *event);
103 virtual void mousePressEvent(QGraphicsSceneMouseEvent *event);
104 virtual void mouseReleaseEvent(QGraphicsSceneMouseEvent *event);
105 virtual void hoverEnterEvent(QGraphicsSceneHoverEvent *) {}
106 virtual void hoverLeaveEvent(QGraphicsSceneHoverEvent *) {}
107 virtual void hoverMoveEvent(QGraphicsSceneHoverEvent *) {}
109 QTextLayout *layout() const;
111 virtual void initLayout(QTextLayout *layout) const;
112 virtual void doLayout(QTextLayout *) const;
113 virtual UiStyle::FormatList formatList() const;
115 void paintBackground(QPainter *);
116 virtual QVector<QTextLayout::FormatRange> additionalFormats() const;
117 void overlayFormat(UiStyle::FormatList &fmtList, quint16 start, quint16 end, UiStyle::FormatType overlayFmt) const;
119 inline qint16 selectionStart() const { return _selectionStart; }
120 inline void setSelectionStart(qint16 start) { _selectionStart = start; }
121 inline qint16 selectionEnd() const { return _selectionEnd; }
122 inline void setSelectionEnd(qint16 end) { _selectionEnd = end; }
123 inline SelectionMode selectionMode() const { return _selectionMode; }
124 inline void setSelectionMode(SelectionMode mode) { _selectionMode = mode; }
125 void setSelection(SelectionMode mode, qint16 selectionStart, qint16 selectionEnd);
127 virtual bool hasActiveClickable() const;
128 virtual std::pair<quint16, quint16> activeClickableRange() const;
130 qint16 posToCursor(const QPointF &pos) const;
132 inline void setGeometry(qreal width, qreal height) { clearCache(); _boundingRect.setSize(QSizeF(width, height)); }
133 inline void setHeight(const qreal &height) { clearCache(); _boundingRect.setHeight(height); }
134 inline void setWidth(const qreal &width) { clearCache(); _boundingRect.setWidth(width); }
135 inline void setPos(const QPointF &pos) { _boundingRect.moveTopLeft(pos); }
139 QRectF _boundingRect;
141 SelectionMode _selectionMode;
142 qint16 _selectionStart, _selectionEnd;
144 mutable QTextLayout *_cachedLayout;
146 // internal selection stuff
147 void setSelection(int start, int length);
149 friend class ChatLine;
153 // ************************************************************
155 // ************************************************************
157 //! A ChatItem for the timestamp column
158 class TimestampChatItem : public ChatItem
161 TimestampChatItem(const QRectF &boundingRect, ChatLine *parent) : ChatItem(boundingRect, parent) {}
162 inline int type() const override { return ChatScene::TimestampChatItemType; }
163 inline ChatLineModel::ColumnType column() const override { return ChatLineModel::TimestampColumn; }
167 // ************************************************************
169 // ************************************************************
170 //! A ChatItem for the sender column
171 class SenderChatItem : public ChatItem
174 SenderChatItem(const QRectF &boundingRect, ChatLine *parent) : ChatItem(boundingRect, parent) {}
175 inline ChatLineModel::ColumnType column() const override { return ChatLineModel::SenderColumn; }
176 void handleClick(const QPointF &pos, ChatScene::ClickMode clickMode) override;
179 void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = nullptr) override;
180 inline int type() const override { return ChatScene::SenderChatItemType; }
181 void initLayout(QTextLayout *layout) const override;
185 // ************************************************************
187 // ************************************************************
188 struct ContentsChatItemPrivate;
190 //! A ChatItem for the contents column
191 class ContentsChatItem : public ChatItem
193 Q_DECLARE_TR_FUNCTIONS(ContentsChatItem)
196 ContentsChatItem(const QPointF &pos, const qreal &width, ChatLine *parent);
197 ~ContentsChatItem() override;
199 inline int type() const override { return ChatScene::ContentsChatItemType; }
201 inline ChatLineModel::ColumnType column() const override { return ChatLineModel::ContentsColumn; }
202 QFontMetricsF *fontMetrics() const;
204 void clearCache() override;
207 void mouseMoveEvent(QGraphicsSceneMouseEvent *event) override;
208 void hoverLeaveEvent(QGraphicsSceneHoverEvent *event) override;
209 void hoverMoveEvent(QGraphicsSceneHoverEvent *event) override;
210 void handleClick(const QPointF &pos, ChatScene::ClickMode clickMode) override;
212 bool hasActiveClickable() const override;
213 std::pair<quint16, quint16> activeClickableRange() const override;
215 void addActionsToMenu(QMenu *menu, const QPointF &itemPos) override;
216 virtual void copyLinkToClipboard();
218 void initLayout(QTextLayout *layout) const override;
219 void doLayout(QTextLayout *layout) const override;
220 UiStyle::FormatList formatList() const override;
224 class WrapColumnFinder;
226 mutable ContentsChatItemPrivate *_data;
227 ContentsChatItemPrivate *privateData() const;
229 Clickable clickableAt(const QPointF &pos) const;
232 void showWebPreview(const Clickable &click);
233 void clearWebPreview();
235 qreal setGeometryByWidth(qreal w);
237 QFontMetricsF *_fontMetrics;
239 // we need a receiver for Action signals
240 static ActionProxy _actionProxy;
242 friend class ChatLine;
243 friend struct ContentsChatItemPrivate;
247 struct ContentsChatItemPrivate {
248 ContentsChatItem *contentsItem;
249 ClickableList clickables;
250 Clickable currentClickable;
251 Clickable activeClickable;
253 ContentsChatItemPrivate(ClickableList c, ContentsChatItem *parent) : contentsItem(parent), clickables(std::move(c)) {}
256 class ContentsChatItem::WrapColumnFinder
259 WrapColumnFinder(const ChatItem *parent);
261 qint16 nextWrapColumn(qreal width);
264 const ChatItem *item;
267 ChatLineModel::WrapList wrapList;
270 qreal choppedTrailing;
274 //! Acts as a proxy for Action signals targetted at a ContentsChatItem
275 /** Since a ChatItem is not a QObject, hence cannot receive signals, we use a static ActionProxy
276 * as a receiver instead. This avoids having to handle ChatItem actions (e.g. context menu entries)
277 * outside the ChatItem.
279 class ContentsChatItem::ActionProxy : public QObject
284 inline void copyLinkToClipboard() { item()->copyLinkToClipboard(); }
287 /// Returns the ContentsChatItem that should receive the action event.
288 /** For efficiency reasons, values are not checked for validity. You gotta make sure that you set the data() member
289 * in the Action correctly.
290 * @return The ChatItem from which the sending Action originated
292 inline ContentsChatItem *item() const
294 return static_cast<ContentsChatItem *>(qobject_cast<QAction *>(sender())->data().value<void *>());
299 /*************************************************************************************************/
303 QRectF ChatItem::boundingRect() const { return _boundingRect; }
304 qreal ChatItem::width() const { return _boundingRect.width(); }
305 qreal ChatItem::height() const { return _boundingRect.height(); }
306 QPointF ChatItem::pos() const { return _boundingRect.topLeft(); }
307 qreal ChatItem::x() const { return pos().x(); }
308 qreal ChatItem::y() const { return pos().y(); }