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