1 /***************************************************************************
2 * Copyright (C) 2005-2010 by the Quassel Project *
3 * devel@quassel-irc.org *
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. *
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. *
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 ***************************************************************************/
27 #include "chatlinemodel.h"
28 #include "chatscene.h"
29 #include "clickable.h"
33 #include <QTextLayout>
37 /* All external positions are relative to the parent ChatLine */
38 /* Yes, that's also true for the boundingRect() and related things */
42 // boundingRect is relative to the parent ChatLine
43 ChatItem(const QRectF &boundingRect, ChatLine *parent);
44 virtual ~ChatItem() {}
47 const QAbstractItemModel *model() const;
48 ChatLine *chatLine() const;
49 ChatScene *chatScene() const;
51 virtual ChatLineModel::ColumnType column() const = 0;
53 // The boundingRect() is relative to the parent ChatLine
54 inline QRectF boundingRect() const;
55 inline qreal width() const;
56 inline qreal height() const;
57 inline QPointF pos() const;
58 inline qreal x() const;
59 inline qreal y() const;
61 QPointF mapToLine(const QPointF &) const;
62 QPointF mapFromLine(const QPointF &) const;
63 QPointF mapToScene(const QPointF &) const;
64 QPointF mapFromScene(const QPointF &) const;
66 void initLayoutHelper(QTextLayout *layout, QTextOption::WrapMode, Qt::Alignment = Qt::AlignLeft) const;
67 virtual inline void initLayout(QTextLayout *layout) const {
68 initLayoutHelper(layout, QTextOption::NoWrap);
71 virtual void doLayout(QTextLayout *) const;
72 virtual UiStyle::FormatList formatList() const;
74 virtual void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = 0);
75 virtual inline int type() const { return ChatScene::ChatItemType; }
77 QVariant data(int role) const;
79 // selection stuff, to be called by the scene
80 QString selection() const;
81 void clearSelection();
82 void setFullSelection();
83 void continueSelecting(const QPointF &pos);
84 bool hasSelection() const;
85 bool isPosOverSelection(const QPointF &pos) const;
87 QList<QRectF> findWords(const QString &searchWord, Qt::CaseSensitivity caseSensitive);
89 virtual void addActionsToMenu(QMenu *menu, const QPointF &itemPos);
90 virtual void handleClick(const QPointF &pos, ChatScene::ClickMode);
99 virtual void mouseMoveEvent(QGraphicsSceneMouseEvent *event);
100 virtual void mousePressEvent(QGraphicsSceneMouseEvent *event);
101 virtual void mouseReleaseEvent(QGraphicsSceneMouseEvent *event);
102 virtual void hoverEnterEvent(QGraphicsSceneHoverEvent *) {};
103 virtual void hoverLeaveEvent(QGraphicsSceneHoverEvent *) {};
104 virtual void hoverMoveEvent(QGraphicsSceneHoverEvent *) {};
106 void paintBackground(QPainter *);
107 QVector<QTextLayout::FormatRange> selectionFormats() const;
108 virtual QVector<QTextLayout::FormatRange> additionalFormats() const;
109 void overlayFormat(UiStyle::FormatList &fmtList, int start, int end, quint32 overlayFmt) const;
111 inline qint16 selectionStart() const { return _selectionStart; }
112 inline void setSelectionStart(qint16 start) { _selectionStart = start; }
113 inline qint16 selectionEnd() const { return _selectionEnd; }
114 inline void setSelectionEnd(qint16 end) { _selectionEnd = end; }
115 inline SelectionMode selectionMode() const { return _selectionMode; }
116 inline void setSelectionMode(SelectionMode mode) { _selectionMode = mode; }
117 void setSelection(SelectionMode mode, qint16 selectionStart, qint16 selectionEnd);
119 qint16 posToCursor(const QPointF &pos) const;
121 inline void setGeometry(qreal width, qreal height) { _boundingRect.setSize(QSizeF(width, height)); }
122 inline void setHeight(const qreal &height) { _boundingRect.setHeight(height); }
123 inline void setWidth(const qreal &width) { _boundingRect.setWidth(width); }
124 inline void setPos(const QPointF &pos) { _boundingRect.moveTopLeft(pos); }
128 QRectF _boundingRect;
130 SelectionMode _selectionMode;
131 qint16 _selectionStart, _selectionEnd;
133 // internal selection stuff
134 void setSelection(int start, int length);
136 friend class ChatLine;
139 // ************************************************************
141 // ************************************************************
143 //! A ChatItem for the timestamp column
144 class TimestampChatItem : public ChatItem {
146 TimestampChatItem(const QRectF &boundingRect, ChatLine *parent) : ChatItem(boundingRect, parent) {}
147 virtual inline int type() const { return ChatScene::TimestampChatItemType; }
148 virtual inline ChatLineModel::ColumnType column() const { return ChatLineModel::TimestampColumn; }
151 // ************************************************************
153 // ************************************************************
154 //! A ChatItem for the sender column
155 class SenderChatItem : public ChatItem {
157 SenderChatItem(const QRectF &boundingRect, ChatLine *parent) : ChatItem(boundingRect, parent) {}
158 virtual inline ChatLineModel::ColumnType column() const { return ChatLineModel::SenderColumn; }
159 virtual void handleClick(const QPointF &pos, ChatScene::ClickMode clickMode);
162 virtual void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = 0);
163 virtual inline int type() const { return ChatScene::SenderChatItemType; }
164 virtual inline void initLayout(QTextLayout *layout) const {
165 initLayoutHelper(layout, QTextOption::ManualWrap, Qt::AlignRight);
170 // ************************************************************
172 // ************************************************************
173 struct ContentsChatItemPrivate;
175 //! A ChatItem for the contents column
176 class ContentsChatItem : public ChatItem {
177 Q_DECLARE_TR_FUNCTIONS(ContentsChatItem)
180 ContentsChatItem(const QPointF &pos, const qreal &width, ChatLine *parent);
183 virtual inline int type() const { return ChatScene::ContentsChatItemType; }
185 inline ChatLineModel::ColumnType column() const { return ChatLineModel::ContentsColumn; }
186 QFontMetricsF *fontMetrics() const;
189 virtual void mouseMoveEvent(QGraphicsSceneMouseEvent *event);
190 virtual void hoverLeaveEvent(QGraphicsSceneHoverEvent *event);
191 virtual void hoverMoveEvent(QGraphicsSceneHoverEvent *event);
192 virtual void handleClick(const QPointF &pos, ChatScene::ClickMode clickMode);
194 virtual void addActionsToMenu(QMenu *menu, const QPointF &itemPos);
195 virtual void copyLinkToClipboard();
197 virtual QVector<QTextLayout::FormatRange> additionalFormats() const;
199 virtual inline void initLayout(QTextLayout *layout) const {
200 initLayoutHelper(layout, QTextOption::WrapAtWordBoundaryOrAnywhere);
203 virtual void doLayout(QTextLayout *layout) const;
204 virtual UiStyle::FormatList formatList() const;
208 class WrapColumnFinder;
210 ContentsChatItemPrivate *_data;
211 ContentsChatItemPrivate *privateData() const;
213 Clickable clickableAt(const QPointF &pos) const;
216 void showWebPreview(const Clickable &click);
217 void clearWebPreview();
219 qreal setGeometryByWidth(qreal w);
220 friend class ChatLine;
221 friend struct ContentsChatItemPrivate;
223 QFontMetricsF *_fontMetrics;
225 // we need a receiver for Action signals
226 static ActionProxy _actionProxy;
229 struct ContentsChatItemPrivate {
230 ContentsChatItem *contentsItem;
231 ClickableList clickables;
232 Clickable currentClickable;
233 Clickable activeClickable;
235 ContentsChatItemPrivate(const ClickableList &c, ContentsChatItem *parent) : contentsItem(parent), clickables(c) {}
238 class ContentsChatItem::WrapColumnFinder {
240 WrapColumnFinder(const ChatItem *parent);
243 qint16 nextWrapColumn(qreal width);
246 const ChatItem *item;
249 ChatLineModel::WrapList wrapList;
252 qreal choppedTrailing;
255 //! Acts as a proxy for Action signals targetted at a ContentsChatItem
256 /** Since a ChatItem is not a QObject, hence cannot receive signals, we use a static ActionProxy
257 * as a receiver instead. This avoids having to handle ChatItem actions (e.g. context menu entries)
258 * outside the ChatItem.
260 class ContentsChatItem::ActionProxy : public QObject {
264 inline void copyLinkToClipboard() { item()->copyLinkToClipboard(); }
267 /// Returns the ContentsChatItem that should receive the action event.
268 /** For efficiency reasons, values are not checked for validity. You gotta make sure that you set the data() member
269 * in the Action correctly.
270 * @return The ChatItem from which the sending Action originated
272 inline ContentsChatItem *item() const {
273 return static_cast<ContentsChatItem *>(qobject_cast<QAction *>(sender())->data().value<void *>());
277 /*************************************************************************************************/
281 QRectF ChatItem::boundingRect() const { return _boundingRect; }
282 qreal ChatItem::width() const { return _boundingRect.width(); }
283 qreal ChatItem::height() const { return _boundingRect.height(); }
284 QPointF ChatItem::pos() const { return _boundingRect.topLeft(); }
285 qreal ChatItem::x() const { return pos().x(); }
286 qreal ChatItem::y() const { return pos().y(); }