Save Main ToolBar state when not built against KDE. Fixes #1116
[quassel.git] / src / qtui / chatitem.h
1 /***************************************************************************
2  *   Copyright (C) 2005-2014 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     QVector<QTextLayout::FormatRange> selectionFormats() const;
116     virtual QVector<QTextLayout::FormatRange> additionalFormats() const;
117     void overlayFormat(UiStyle::FormatList &fmtList, int start, int end, quint32 overlayFmt) const;
118
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);
126
127     qint16 posToCursor(const QPointF &pos) const;
128
129     inline void setGeometry(qreal width, qreal height) { clearCache(); _boundingRect.setSize(QSizeF(width, height)); }
130     inline void setHeight(const qreal &height) { clearCache(); _boundingRect.setHeight(height); }
131     inline void setWidth(const qreal &width) { clearCache(); _boundingRect.setWidth(width); }
132     inline void setPos(const QPointF &pos) { _boundingRect.moveTopLeft(pos); }
133
134 private:
135     ChatLine *_parent;
136     QRectF _boundingRect;
137
138     SelectionMode _selectionMode;
139     qint16 _selectionStart, _selectionEnd;
140
141     mutable QTextLayout *_cachedLayout;
142
143     // internal selection stuff
144     void setSelection(int start, int length);
145
146     friend class ChatLine;
147 };
148
149
150 // ************************************************************
151 // TimestampChatItem
152 // ************************************************************
153
154 //! A ChatItem for the timestamp column
155 class TimestampChatItem : public ChatItem
156 {
157 public:
158     TimestampChatItem(const QRectF &boundingRect, ChatLine *parent) : ChatItem(boundingRect, parent) {}
159     virtual inline int type() const { return ChatScene::TimestampChatItemType; }
160     virtual inline ChatLineModel::ColumnType column() const { return ChatLineModel::TimestampColumn; }
161 };
162
163
164 // ************************************************************
165 // SenderChatItem
166 // ************************************************************
167 //! A ChatItem for the sender column
168 class SenderChatItem : public ChatItem
169 {
170 public:
171     SenderChatItem(const QRectF &boundingRect, ChatLine *parent) : ChatItem(boundingRect, parent) {}
172     virtual inline ChatLineModel::ColumnType column() const { return ChatLineModel::SenderColumn; }
173     virtual void handleClick(const QPointF &pos, ChatScene::ClickMode clickMode);
174
175 protected:
176     virtual void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = 0);
177     virtual inline int type() const { return ChatScene::SenderChatItemType; }
178     virtual void initLayout(QTextLayout *layout) const;
179 };
180
181
182 // ************************************************************
183 // ContentsChatItem
184 // ************************************************************
185 struct ContentsChatItemPrivate;
186
187 //! A ChatItem for the contents column
188 class ContentsChatItem : public ChatItem
189 {
190     Q_DECLARE_TR_FUNCTIONS(ContentsChatItem)
191
192 public:
193     ContentsChatItem(const QPointF &pos, const qreal &width, ChatLine *parent);
194     ~ContentsChatItem();
195
196     virtual inline int type() const { return ChatScene::ContentsChatItemType; }
197
198     inline ChatLineModel::ColumnType column() const { return ChatLineModel::ContentsColumn; }
199     QFontMetricsF *fontMetrics() const;
200
201     virtual void clearCache();
202
203 protected:
204     virtual void mouseMoveEvent(QGraphicsSceneMouseEvent *event);
205     virtual void hoverLeaveEvent(QGraphicsSceneHoverEvent *event);
206     virtual void hoverMoveEvent(QGraphicsSceneHoverEvent *event);
207     virtual void handleClick(const QPointF &pos, ChatScene::ClickMode clickMode);
208
209     virtual void addActionsToMenu(QMenu *menu, const QPointF &itemPos);
210     virtual void copyLinkToClipboard();
211
212     virtual QVector<QTextLayout::FormatRange> additionalFormats() const;
213
214     virtual void initLayout(QTextLayout *layout) const;
215     virtual void doLayout(QTextLayout *layout) const;
216     virtual UiStyle::FormatList formatList() const;
217
218 private:
219     class ActionProxy;
220     class WrapColumnFinder;
221
222     mutable ContentsChatItemPrivate *_data;
223     ContentsChatItemPrivate *privateData() const;
224
225     Clickable clickableAt(const QPointF &pos) const;
226
227     void endHoverMode();
228     void showWebPreview(const Clickable &click);
229     void clearWebPreview();
230
231     qreal setGeometryByWidth(qreal w);
232
233     QFontMetricsF *_fontMetrics;
234
235     // we need a receiver for Action signals
236     static ActionProxy _actionProxy;
237
238     friend class ChatLine;
239     friend struct ContentsChatItemPrivate;
240 };
241
242
243 struct ContentsChatItemPrivate {
244     ContentsChatItem *contentsItem;
245     ClickableList clickables;
246     Clickable currentClickable;
247     Clickable activeClickable;
248
249     ContentsChatItemPrivate(const ClickableList &c, ContentsChatItem *parent) : contentsItem(parent), clickables(c) {}
250 };
251
252 class ContentsChatItem::WrapColumnFinder
253 {
254 public:
255     WrapColumnFinder(const ChatItem *parent);
256     ~WrapColumnFinder();
257
258     qint16 nextWrapColumn(qreal width);
259
260 private:
261     const ChatItem *item;
262     QTextLayout layout;
263     QTextLine line;
264     ChatLineModel::WrapList wrapList;
265     qint16 wordidx;
266     qint16 lineCount;
267     qreal choppedTrailing;
268 };
269
270
271 //! Acts as a proxy for Action signals targetted at a ContentsChatItem
272 /** Since a ChatItem is not a QObject, hence cannot receive signals, we use a static ActionProxy
273  *  as a receiver instead. This avoids having to handle ChatItem actions (e.g. context menu entries)
274  *  outside the ChatItem.
275  */
276 class ContentsChatItem::ActionProxy : public QObject
277 {
278     Q_OBJECT
279
280 public slots:
281     inline void copyLinkToClipboard() { item()->copyLinkToClipboard(); }
282
283 private:
284     /// Returns the ContentsChatItem that should receive the action event.
285     /** For efficiency reasons, values are not checked for validity. You gotta make sure that you set the data() member
286      *  in the Action correctly.
287      *  @return The ChatItem from which the sending Action originated
288      */
289     inline ContentsChatItem *item() const
290     {
291         return static_cast<ContentsChatItem *>(qobject_cast<QAction *>(sender())->data().value<void *>());
292     }
293 };
294
295
296 /*************************************************************************************************/
297
298 // Inlines
299
300 QRectF ChatItem::boundingRect() const { return _boundingRect; }
301 qreal ChatItem::width() const { return _boundingRect.width(); }
302 qreal ChatItem::height() const { return _boundingRect.height(); }
303 QPointF ChatItem::pos() const { return _boundingRect.topLeft(); }
304 qreal ChatItem::x() const { return pos().x(); }
305 qreal ChatItem::y() const { return pos().y(); }
306
307 #endif