Add new ItemView settingspage
[quassel.git] / src / qtui / chatitem.h
1 /***************************************************************************
2  *   Copyright (C) 2005-09 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 <QGraphicsItem>
26 #include <QObject>
27
28 #include "chatlinemodel.h"
29 #include "chatscene.h"
30 #include "uistyle.h"
31 #include "qtui.h"
32
33 #include <QTextLayout>
34
35 class ChatItem : public QGraphicsItem {
36 protected:
37   ChatItem(const qreal &width, const qreal &height, const QPointF &pos, QGraphicsItem *parent);
38
39 public:
40   inline const QAbstractItemModel *model() const;
41   inline int row() const;
42   virtual ChatLineModel::ColumnType column() const = 0;
43   inline ChatScene *chatScene() const { return qobject_cast<ChatScene *>(scene()); }
44
45   inline QRectF boundingRect() const { return _boundingRect; }
46   inline qreal width() const { return _boundingRect.width(); }
47   inline qreal height() const { return _boundingRect.height(); }
48
49   void initLayoutHelper(QTextLayout *layout, QTextOption::WrapMode, Qt::Alignment = Qt::AlignLeft) const;
50   virtual inline void initLayout(QTextLayout *layout) const {
51     initLayoutHelper(layout, QTextOption::NoWrap);
52     doLayout(layout);
53   }
54   virtual void doLayout(QTextLayout *) const;
55
56   virtual void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = 0);
57   enum { Type = ChatScene::ChatItemType };
58   virtual inline int type() const { return Type; }
59
60   QVariant data(int role) const;
61
62   // selection stuff, to be called by the scene
63   QString selection() const;
64   void clearSelection();
65   void setFullSelection();
66   void continueSelecting(const QPointF &pos);
67   bool hasSelection() const;
68   bool isPosOverSelection(const QPointF &pos) const;
69
70   QList<QRectF> findWords(const QString &searchWord, Qt::CaseSensitivity caseSensitive);
71
72   virtual void addActionsToMenu(QMenu *menu, const QPointF &itemPos);
73   virtual void handleClick(const QPointF &pos, ChatScene::ClickMode);
74
75 protected:
76   enum SelectionMode {
77     NoSelection,
78     PartialSelection,
79     FullSelection
80   };
81
82   virtual void mouseMoveEvent(QGraphicsSceneMouseEvent *event);
83   virtual void mousePressEvent(QGraphicsSceneMouseEvent *event);
84   virtual void mouseReleaseEvent(QGraphicsSceneMouseEvent *event);
85
86   void paintBackground(QPainter *);
87   QVector<QTextLayout::FormatRange> selectionFormats() const;
88   virtual inline QVector<QTextLayout::FormatRange> additionalFormats() const { return QVector<QTextLayout::FormatRange>(); }
89
90   inline qint16 selectionStart() const { return _selectionStart; }
91   inline void setSelectionStart(qint16 start) { _selectionStart = start; }
92   inline qint16 selectionEnd() const { return _selectionEnd; }
93   inline void setSelectionEnd(qint16 end) { _selectionEnd = end; }
94   inline SelectionMode selectionMode() const { return _selectionMode; }
95   inline void setSelectionMode(SelectionMode mode) { _selectionMode = mode; }
96   void setSelection(SelectionMode mode, qint16 selectionStart, qint16 selectionEnd);
97
98   qint16 posToCursor(const QPointF &pos) const;
99
100   // WARNING: setGeometry and setHeight should not be used without either:
101   //  a) calling prepareGeometryChange() immediately before setColumns()
102   //  b) calling Chatline::setPos() immediately afterwards
103   inline void setGeometry(qreal width, qreal height) {
104     _boundingRect.setWidth(width);
105     _boundingRect.setHeight(height);
106   }
107   inline void setHeight(const qreal &height) {
108     _boundingRect.setHeight(height);
109   }
110   inline void setWidth(const qreal &width) {
111     _boundingRect.setWidth(width);
112   }
113
114 private:
115   // internal selection stuff
116   void setSelection(int start, int length);
117
118   QRectF _boundingRect;
119
120   SelectionMode _selectionMode;
121   qint16 _selectionStart, _selectionEnd;
122
123   friend class ChatLine;
124 };
125
126 // ************************************************************
127 // TimestampChatItem
128 // ************************************************************
129
130 //! A ChatItem for the timestamp column
131 class TimestampChatItem : public ChatItem {
132 public:
133   TimestampChatItem(const qreal &width, const qreal &height, QGraphicsItem *parent) : ChatItem(width, height, QPointF(0, 0), parent) {}
134   enum { Type = ChatScene::TimestampChatItemType };
135   virtual inline int type() const { return Type; }
136   virtual inline ChatLineModel::ColumnType column() const { return ChatLineModel::TimestampColumn; }
137 };
138
139 // ************************************************************
140 // SenderChatItem
141 // ************************************************************
142 //! A ChatItem for the sender column
143 class SenderChatItem : public ChatItem {
144 public:
145   SenderChatItem(const qreal &width, const qreal &height, const QPointF &pos, QGraphicsItem *parent) : ChatItem(width, height, pos, parent) {}
146   virtual inline ChatLineModel::ColumnType column() const { return ChatLineModel::SenderColumn; }
147
148 protected:
149   virtual void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = 0);
150   enum { Type = ChatScene::SenderChatItemType };
151   virtual inline int type() const { return Type; }
152   virtual inline void initLayout(QTextLayout *layout) const {
153     initLayoutHelper(layout, QTextOption::ManualWrap, Qt::AlignRight);
154     doLayout(layout);
155   }
156 };
157
158 // ************************************************************
159 // ContentsChatItem
160 // ************************************************************
161 struct ContentsChatItemPrivate;
162
163 //! A ChatItem for the contents column
164 class ContentsChatItem : public ChatItem {
165   Q_DECLARE_TR_FUNCTIONS(ContentsChatItem);
166
167 public:
168   ContentsChatItem(const qreal &width, const QPointF &pos, QGraphicsItem *parent);
169   ~ContentsChatItem();
170
171   enum { Type = ChatScene::ContentsChatItemType };
172   virtual inline int type() const { return Type; }
173
174   inline ChatLineModel::ColumnType column() const { return ChatLineModel::ContentsColumn; }
175   QFontMetricsF *fontMetrics() const;
176
177 protected:
178   virtual void mouseMoveEvent(QGraphicsSceneMouseEvent *event);
179   virtual void hoverLeaveEvent(QGraphicsSceneHoverEvent *event);
180   virtual void hoverMoveEvent(QGraphicsSceneHoverEvent *event);
181   virtual void handleClick(const QPointF &pos, ChatScene::ClickMode clickMode);
182
183   virtual void addActionsToMenu(QMenu *menu, const QPointF &itemPos);
184   virtual void copyLinkToClipboard();
185
186   virtual QVector<QTextLayout::FormatRange> additionalFormats() const;
187
188   virtual inline void initLayout(QTextLayout *layout) const {
189     initLayoutHelper(layout, QTextOption::WrapAnywhere);
190     doLayout(layout);
191   }
192   virtual void doLayout(QTextLayout *layout) const;
193
194 private:
195   struct Clickable;
196   class ActionProxy;
197   class WrapColumnFinder;
198
199   ContentsChatItemPrivate *_data;
200   ContentsChatItemPrivate *privateData() const;
201
202   QList<Clickable> findClickables() const;
203   Clickable clickableAt(const QPointF &pos) const;
204
205   void endHoverMode();
206   void showWebPreview(const Clickable &click);
207   void clearWebPreview();
208
209   qreal setGeometryByWidth(qreal w);
210   friend class ChatLine;
211   friend struct ContentsChatItemPrivate;
212
213   QFontMetricsF *_fontMetrics;
214
215   // we need a receiver for Action signals
216   static ActionProxy _actionProxy;
217 };
218
219 struct ContentsChatItem::Clickable {
220   // Don't change these enums without also changing the regexps in analyze()!
221   enum Type {
222     Invalid = -1,
223     Url = 0,
224     Channel = 1,
225     Nick = 2
226   };
227
228   Type type;
229   quint16 start;
230   quint16 length;
231
232   inline Clickable() : type(Invalid) {};
233   inline Clickable(Type type_, quint16 start_, quint16 length_) : type(type_), start(start_), length(length_) {};
234   inline bool isValid() const { return type != Invalid; }
235 };
236
237 struct ContentsChatItemPrivate {
238   ContentsChatItem *contentsItem;
239   QList<ContentsChatItem::Clickable> clickables;
240   ContentsChatItem::Clickable currentClickable;
241   ContentsChatItem::Clickable activeClickable;
242
243   ContentsChatItemPrivate(const QList<ContentsChatItem::Clickable> &c, ContentsChatItem *parent) : contentsItem(parent), clickables(c) {}
244 };
245
246 class ContentsChatItem::WrapColumnFinder {
247 public:
248   WrapColumnFinder(const ChatItem *parent);
249   ~WrapColumnFinder();
250
251   qint16 nextWrapColumn(qreal width);
252
253 private:
254   const ChatItem *item;
255   QTextLayout layout;
256   QTextLine line;
257   ChatLineModel::WrapList wrapList;
258   qint16 wordidx;
259   qint16 lineCount;
260   qreal choppedTrailing;
261 };
262
263 //! Acts as a proxy for Action signals targetted at a ContentsChatItem
264 /** Since a ChatItem is not a QObject, hence cannot receive signals, we use a static ActionProxy
265  *  as a receiver instead. This avoids having to handle ChatItem actions (e.g. context menu entries)
266  *  outside the ChatItem.
267  */
268 class ContentsChatItem::ActionProxy : public QObject {
269   Q_OBJECT
270
271 public slots:
272   inline void copyLinkToClipboard() { item()->copyLinkToClipboard(); }
273
274 private:
275   /// Returns the ContentsChatItem that should receive the action event.
276   /** For efficiency reasons, values are not checked for validity. You gotta make sure that you set the data() member
277    *  in the Action correctly.
278    *  @return The ChatItem from which the sending Action originated
279    */
280   inline ContentsChatItem *item() const {
281     return static_cast<ContentsChatItem *>(qobject_cast<QAction *>(sender())->data().value<void *>());
282   }
283 };
284
285 /*************************************************************************************************/
286
287 // Avoid circular include deps
288 #include "chatline.h"
289 const QAbstractItemModel *ChatItem::model() const { return static_cast<ChatLine *>(parentItem())->model(); }
290 int ChatItem::row() const { return static_cast<ChatLine *>(parentItem())->row(); }
291
292 #endif