Introduce the concept of an "active" bufferview
[quassel.git] / src / qtui / chatitem.h
1 /***************************************************************************
2  *   Copyright (C) 2005-2010 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  *   59 Temple Place - Suite 330, Boston, MA  02111-1307, 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 protected:
43   // boundingRect is relative to the parent ChatLine
44   ChatItem(const QRectF &boundingRect, ChatLine *parent);
45   virtual ~ChatItem();
46
47 public:
48   const QAbstractItemModel *model() const;
49   ChatLine *chatLine() const;
50   ChatScene *chatScene() const;
51   ChatView *chatView() const;
52   int row() const;
53   virtual ChatLineModel::ColumnType column() const = 0;
54
55   // The boundingRect() is relative to the parent ChatLine
56   inline QRectF boundingRect() const;
57   inline qreal width() const;
58   inline qreal height() const;
59   inline QPointF pos() const;
60   inline qreal x() const;
61   inline qreal y() const;
62
63   QPointF mapToLine(const QPointF &) const;
64   QPointF mapFromLine(const QPointF &) const;
65   QPointF mapToScene(const QPointF &) const;
66   QPointF mapFromScene(const QPointF &) const;
67
68   virtual void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = 0);
69   virtual inline int type() const { return ChatScene::ChatItemType; }
70
71   QVariant data(int role) const;
72
73   // selection stuff, to be called by the scene
74   QString selection() const;
75   void clearSelection();
76   void setFullSelection();
77   void continueSelecting(const QPointF &pos);
78   bool hasSelection() const;
79   bool isPosOverSelection(const QPointF &pos) const;
80
81   QList<QRectF> findWords(const QString &searchWord, Qt::CaseSensitivity caseSensitive);
82
83   virtual void addActionsToMenu(QMenu *menu, const QPointF &itemPos);
84   virtual void handleClick(const QPointF &pos, ChatScene::ClickMode);
85
86   void initLayoutHelper(QTextLayout *layout, QTextOption::WrapMode, Qt::Alignment = Qt::AlignLeft) const;
87
88   //! Remove internally cached data
89   /** This removes e.g. the cached QTextLayout to avoid wasting space for nonvisible ChatLines
90    */
91   virtual void clearCache();
92
93 protected:
94   enum SelectionMode {
95     NoSelection,
96     PartialSelection,
97     FullSelection
98   };
99
100   virtual void mouseMoveEvent(QGraphicsSceneMouseEvent *event);
101   virtual void mousePressEvent(QGraphicsSceneMouseEvent *event);
102   virtual void mouseReleaseEvent(QGraphicsSceneMouseEvent *event);
103   virtual void hoverEnterEvent(QGraphicsSceneHoverEvent *) {}
104   virtual void hoverLeaveEvent(QGraphicsSceneHoverEvent *) {}
105   virtual void hoverMoveEvent(QGraphicsSceneHoverEvent *) {}
106
107   QTextLayout *layout() const;
108
109   virtual void initLayout(QTextLayout *layout) const;
110   virtual void doLayout(QTextLayout *) const;
111   virtual UiStyle::FormatList formatList() const;
112
113   void paintBackground(QPainter *);
114   QVector<QTextLayout::FormatRange> selectionFormats() const;
115   virtual QVector<QTextLayout::FormatRange> additionalFormats() const;
116   void overlayFormat(UiStyle::FormatList &fmtList, int start, int end, quint32 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   qint16 posToCursor(const QPointF &pos) const;
127
128   inline void setGeometry(qreal width, qreal height) { clearCache(); _boundingRect.setSize(QSizeF(width, height)); }
129   inline void setHeight(const qreal &height) { clearCache(); _boundingRect.setHeight(height); }
130   inline void setWidth(const qreal &width) { clearCache(); _boundingRect.setWidth(width); }
131   inline void setPos(const QPointF &pos) { _boundingRect.moveTopLeft(pos); }
132
133 private:
134   ChatLine *_parent;
135   QRectF _boundingRect;
136
137   SelectionMode _selectionMode;
138   qint16 _selectionStart, _selectionEnd;
139
140   mutable QTextLayout *_cachedLayout;
141
142   // internal selection stuff
143   void setSelection(int start, int length);
144
145   friend class ChatLine;
146 };
147
148 // ************************************************************
149 // TimestampChatItem
150 // ************************************************************
151
152 //! A ChatItem for the timestamp column
153 class TimestampChatItem : public ChatItem {
154 public:
155   TimestampChatItem(const QRectF &boundingRect, ChatLine *parent) : ChatItem(boundingRect, parent) {}
156   virtual inline int type() const { return ChatScene::TimestampChatItemType; }
157   virtual inline ChatLineModel::ColumnType column() const { return ChatLineModel::TimestampColumn; }
158 };
159
160 // ************************************************************
161 // SenderChatItem
162 // ************************************************************
163 //! A ChatItem for the sender column
164 class SenderChatItem : public ChatItem {
165 public:
166   SenderChatItem(const QRectF &boundingRect, ChatLine *parent) : ChatItem(boundingRect, parent) {}
167   virtual inline ChatLineModel::ColumnType column() const { return ChatLineModel::SenderColumn; }
168   virtual void handleClick(const QPointF &pos, ChatScene::ClickMode clickMode);
169
170 protected:
171   virtual void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = 0);
172   virtual inline int type() const { return ChatScene::SenderChatItemType; }
173   virtual void initLayout(QTextLayout *layout) const;
174 };
175
176 // ************************************************************
177 // ContentsChatItem
178 // ************************************************************
179 struct ContentsChatItemPrivate;
180
181 //! A ChatItem for the contents column
182 class ContentsChatItem : public ChatItem {
183   Q_DECLARE_TR_FUNCTIONS(ContentsChatItem)
184
185 public:
186   ContentsChatItem(const QPointF &pos, const qreal &width, ChatLine *parent);
187   ~ContentsChatItem();
188
189   virtual inline int type() const { return ChatScene::ContentsChatItemType; }
190
191   inline ChatLineModel::ColumnType column() const { return ChatLineModel::ContentsColumn; }
192   QFontMetricsF *fontMetrics() const;
193
194   virtual void clearCache();
195
196 protected:
197   virtual void mouseMoveEvent(QGraphicsSceneMouseEvent *event);
198   virtual void hoverLeaveEvent(QGraphicsSceneHoverEvent *event);
199   virtual void hoverMoveEvent(QGraphicsSceneHoverEvent *event);
200   virtual void handleClick(const QPointF &pos, ChatScene::ClickMode clickMode);
201
202   virtual void addActionsToMenu(QMenu *menu, const QPointF &itemPos);
203   virtual void copyLinkToClipboard();
204
205   virtual QVector<QTextLayout::FormatRange> additionalFormats() const;
206
207   virtual void initLayout(QTextLayout *layout) const;
208   virtual void doLayout(QTextLayout *layout) const;
209   virtual UiStyle::FormatList formatList() const;
210
211 private:
212   class ActionProxy;
213   class WrapColumnFinder;
214
215   mutable ContentsChatItemPrivate *_data;
216   ContentsChatItemPrivate *privateData() const;
217
218   Clickable clickableAt(const QPointF &pos) const;
219
220   void endHoverMode();
221   void showWebPreview(const Clickable &click);
222   void clearWebPreview();
223
224   qreal setGeometryByWidth(qreal w);
225
226   QFontMetricsF *_fontMetrics;
227
228   // we need a receiver for Action signals
229   static ActionProxy _actionProxy;
230
231   friend class ChatLine;
232   friend struct ContentsChatItemPrivate;
233 };
234
235 struct ContentsChatItemPrivate {
236   ContentsChatItem *contentsItem;
237   ClickableList clickables;
238   Clickable currentClickable;
239   Clickable activeClickable;
240
241   ContentsChatItemPrivate(const ClickableList &c, ContentsChatItem *parent) : contentsItem(parent), clickables(c) {}
242 };
243
244 class ContentsChatItem::WrapColumnFinder {
245 public:
246   WrapColumnFinder(const ChatItem *parent);
247   ~WrapColumnFinder();
248
249   qint16 nextWrapColumn(qreal width);
250
251 private:
252   const ChatItem *item;
253   QTextLayout layout;
254   QTextLine line;
255   ChatLineModel::WrapList wrapList;
256   qint16 wordidx;
257   qint16 lineCount;
258   qreal choppedTrailing;
259 };
260
261 //! Acts as a proxy for Action signals targetted at a ContentsChatItem
262 /** Since a ChatItem is not a QObject, hence cannot receive signals, we use a static ActionProxy
263  *  as a receiver instead. This avoids having to handle ChatItem actions (e.g. context menu entries)
264  *  outside the ChatItem.
265  */
266 class ContentsChatItem::ActionProxy : public QObject {
267   Q_OBJECT
268
269 public slots:
270   inline void copyLinkToClipboard() { item()->copyLinkToClipboard(); }
271
272 private:
273   /// Returns the ContentsChatItem that should receive the action event.
274   /** For efficiency reasons, values are not checked for validity. You gotta make sure that you set the data() member
275    *  in the Action correctly.
276    *  @return The ChatItem from which the sending Action originated
277    */
278   inline ContentsChatItem *item() const {
279     return static_cast<ContentsChatItem *>(qobject_cast<QAction *>(sender())->data().value<void *>());
280   }
281 };
282
283 /*************************************************************************************************/
284
285 // Inlines
286
287 QRectF ChatItem::boundingRect() const { return _boundingRect; }
288 qreal ChatItem::width() const { return _boundingRect.width(); }
289 qreal ChatItem::height() const { return _boundingRect.height(); }
290 QPointF ChatItem::pos() const { return _boundingRect.topLeft(); }
291 qreal ChatItem::x() const { return pos().x(); }
292 qreal ChatItem::y() const { return pos().y(); }
293
294 #endif