clang-tidy: Avoid another warning about potential memory leak
[quassel.git] / src / qtui / chatitem.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 CHATITEM_H_
22 #define CHATITEM_H_
23
24 #include <QAction>
25 #include <QObject>
26
27 #include "chatlinemodel.h"
28 #include "chatscene.h"
29 #include "clickable.h"
30 #include "uistyle.h"
31 #include "qtui.h"
32
33 #include <QTextLayout>
34
35 class ChatLine;
36 class ChatView;
37
38 /* All external positions are relative to the parent ChatLine */
39 /* Yes, that's also true for the boundingRect() and related things */
40
41 class ChatItem
42 {
43 protected:
44     // boundingRect is relative to the parent ChatLine
45     ChatItem(const QRectF &boundingRect, ChatLine *parent);
46     virtual ~ChatItem();
47
48 public:
49     const QAbstractItemModel *model() const;
50     ChatLine *chatLine() const;
51     ChatScene *chatScene() const;
52     ChatView *chatView() const;
53     int row() const;
54     virtual ChatLineModel::ColumnType column() const = 0;
55
56     // The boundingRect() is relative to the parent ChatLine
57     inline QRectF boundingRect() const;
58     inline qreal width() const;
59     inline qreal height() const;
60     inline QPointF pos() const;
61     inline qreal x() const;
62     inline qreal y() const;
63
64     QPointF mapToLine(const QPointF &) const;
65     QPointF mapFromLine(const QPointF &) const;
66     QPointF mapToScene(const QPointF &) const;
67     QPointF mapFromScene(const QPointF &) const;
68
69     virtual void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = 0);
70     virtual inline int type() const { return ChatScene::ChatItemType; }
71
72     QVariant data(int role) const;
73
74     // selection stuff, to be called by the scene
75     QString selection() const;
76     void clearSelection();
77     void setFullSelection();
78     void continueSelecting(const QPointF &pos);
79     bool hasSelection() const;
80     bool isPosOverSelection(const QPointF &pos) const;
81
82     QList<QRectF> findWords(const QString &searchWord, Qt::CaseSensitivity caseSensitive);
83
84     virtual void addActionsToMenu(QMenu *menu, const QPointF &itemPos);
85     virtual void handleClick(const QPointF &pos, ChatScene::ClickMode);
86
87     void initLayoutHelper(QTextLayout *layout, QTextOption::WrapMode, Qt::Alignment = Qt::AlignLeft) const;
88
89     //! Remove internally cached data
90     /** This removes e.g. the cached QTextLayout to avoid wasting space for nonvisible ChatLines
91      */
92     virtual void clearCache();
93
94 protected:
95     enum SelectionMode {
96         NoSelection,
97         PartialSelection,
98         FullSelection
99     };
100
101     virtual void mouseMoveEvent(QGraphicsSceneMouseEvent *event);
102     virtual void mousePressEvent(QGraphicsSceneMouseEvent *event);
103     virtual void mouseReleaseEvent(QGraphicsSceneMouseEvent *event);
104     virtual void hoverEnterEvent(QGraphicsSceneHoverEvent *) {}
105     virtual void hoverLeaveEvent(QGraphicsSceneHoverEvent *) {}
106     virtual void hoverMoveEvent(QGraphicsSceneHoverEvent *) {}
107
108     QTextLayout *layout() const;
109
110     virtual void initLayout(QTextLayout *layout) const;
111     virtual void doLayout(QTextLayout *) const;
112     virtual UiStyle::FormatList formatList() const;
113
114     void paintBackground(QPainter *);
115     virtual QVector<QTextLayout::FormatRange> additionalFormats() const;
116     void overlayFormat(UiStyle::FormatList &fmtList, quint16 start, quint16 end, UiStyle::FormatType overlayFmt) const;
117
118     inline qint16 selectionStart() const { return _selectionStart; }
119     inline void setSelectionStart(qint16 start) { _selectionStart = start; }
120     inline qint16 selectionEnd() const { return _selectionEnd; }
121     inline void setSelectionEnd(qint16 end) { _selectionEnd = end; }
122     inline SelectionMode selectionMode() const { return _selectionMode; }
123     inline void setSelectionMode(SelectionMode mode) { _selectionMode = mode; }
124     void setSelection(SelectionMode mode, qint16 selectionStart, qint16 selectionEnd);
125
126     virtual bool hasActiveClickable() const;
127     virtual std::pair<quint16, quint16> activeClickableRange() const;
128
129     qint16 posToCursor(const QPointF &pos) const;
130
131     inline void setGeometry(qreal width, qreal height) { clearCache(); _boundingRect.setSize(QSizeF(width, height)); }
132     inline void setHeight(const qreal &height) { clearCache(); _boundingRect.setHeight(height); }
133     inline void setWidth(const qreal &width) { clearCache(); _boundingRect.setWidth(width); }
134     inline void setPos(const QPointF &pos) { _boundingRect.moveTopLeft(pos); }
135
136 private:
137     ChatLine *_parent;
138     QRectF _boundingRect;
139
140     SelectionMode _selectionMode;
141     qint16 _selectionStart, _selectionEnd;
142
143     mutable QTextLayout *_cachedLayout;
144
145     // internal selection stuff
146     void setSelection(int start, int length);
147
148     friend class ChatLine;
149 };
150
151
152 // ************************************************************
153 // TimestampChatItem
154 // ************************************************************
155
156 //! A ChatItem for the timestamp column
157 class TimestampChatItem : public ChatItem
158 {
159 public:
160     TimestampChatItem(const QRectF &boundingRect, ChatLine *parent) : ChatItem(boundingRect, parent) {}
161     virtual inline int type() const { return ChatScene::TimestampChatItemType; }
162     virtual inline ChatLineModel::ColumnType column() const { return ChatLineModel::TimestampColumn; }
163 };
164
165
166 // ************************************************************
167 // SenderChatItem
168 // ************************************************************
169 //! A ChatItem for the sender column
170 class SenderChatItem : public ChatItem
171 {
172 public:
173     SenderChatItem(const QRectF &boundingRect, ChatLine *parent) : ChatItem(boundingRect, parent) {}
174     virtual inline ChatLineModel::ColumnType column() const { return ChatLineModel::SenderColumn; }
175     virtual void handleClick(const QPointF &pos, ChatScene::ClickMode clickMode);
176
177 protected:
178     virtual void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = 0);
179     virtual inline int type() const { return ChatScene::SenderChatItemType; }
180     virtual void initLayout(QTextLayout *layout) const;
181 };
182
183
184 // ************************************************************
185 // ContentsChatItem
186 // ************************************************************
187 struct ContentsChatItemPrivate;
188
189 //! A ChatItem for the contents column
190 class ContentsChatItem : public ChatItem
191 {
192     Q_DECLARE_TR_FUNCTIONS(ContentsChatItem)
193
194 public:
195     ContentsChatItem(const QPointF &pos, const qreal &width, ChatLine *parent);
196     ~ContentsChatItem();
197
198     virtual inline int type() const { return ChatScene::ContentsChatItemType; }
199
200     inline ChatLineModel::ColumnType column() const { return ChatLineModel::ContentsColumn; }
201     QFontMetricsF *fontMetrics() const;
202
203     virtual void clearCache();
204
205 protected:
206     virtual void mouseMoveEvent(QGraphicsSceneMouseEvent *event);
207     virtual void hoverLeaveEvent(QGraphicsSceneHoverEvent *event);
208     virtual void hoverMoveEvent(QGraphicsSceneHoverEvent *event);
209     virtual void handleClick(const QPointF &pos, ChatScene::ClickMode clickMode);
210
211     virtual bool hasActiveClickable() const;
212     virtual std::pair<quint16, quint16> activeClickableRange() const;
213
214     virtual void addActionsToMenu(QMenu *menu, const QPointF &itemPos);
215     virtual void copyLinkToClipboard();
216
217     virtual void initLayout(QTextLayout *layout) const;
218     virtual void doLayout(QTextLayout *layout) const;
219     virtual UiStyle::FormatList formatList() const;
220
221 private:
222     class ActionProxy;
223     class WrapColumnFinder;
224
225     mutable ContentsChatItemPrivate *_data;
226     ContentsChatItemPrivate *privateData() const;
227
228     Clickable clickableAt(const QPointF &pos) const;
229
230     void endHoverMode();
231     void showWebPreview(const Clickable &click);
232     void clearWebPreview();
233
234     qreal setGeometryByWidth(qreal w);
235
236     QFontMetricsF *_fontMetrics;
237
238     // we need a receiver for Action signals
239     static ActionProxy _actionProxy;
240
241     friend class ChatLine;
242     friend struct ContentsChatItemPrivate;
243 };
244
245
246 struct ContentsChatItemPrivate {
247     ContentsChatItem *contentsItem;
248     ClickableList clickables;
249     Clickable currentClickable;
250     Clickable activeClickable;
251
252     ContentsChatItemPrivate(const ClickableList &c, ContentsChatItem *parent) : contentsItem(parent), clickables(c) {}
253 };
254
255 class ContentsChatItem::WrapColumnFinder
256 {
257 public:
258     WrapColumnFinder(const ChatItem *parent);
259     ~WrapColumnFinder();
260
261     qint16 nextWrapColumn(qreal width);
262
263 private:
264     const ChatItem *item;
265     QTextLayout layout;
266     QTextLine line;
267     ChatLineModel::WrapList wrapList;
268     qint16 wordidx;
269     qint16 lineCount;
270     qreal choppedTrailing;
271 };
272
273
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.
278  */
279 class ContentsChatItem::ActionProxy : public QObject
280 {
281     Q_OBJECT
282
283 public slots:
284     inline void copyLinkToClipboard() { item()->copyLinkToClipboard(); }
285
286 private:
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
291      */
292     inline ContentsChatItem *item() const
293     {
294         return static_cast<ContentsChatItem *>(qobject_cast<QAction *>(sender())->data().value<void *>());
295     }
296 };
297
298
299 /*************************************************************************************************/
300
301 // Inlines
302
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(); }
309
310 #endif