93ee11735cb418f4728c293e72e833bee990e15e
[quassel.git] / gui / chatwidget.h
1 /***************************************************************************
2  *   Copyright (C) 2005/06 by The Quassel Team                             *
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) any later version.                                   *
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 _CHATWIDGET_H_
22 #define _CHATWIDGET_H_
23
24 #include "style.h"
25 #include "message.h"
26 #include <QtGui>
27
28 class ChatWidgetContents;
29
30 //!\brief Scroll area showing part of the chat messages for a given buffer.
31 /** The contents of the scroll area, i.e. a widget of type ChatWidgetContents,
32  * needs to be provided by calling init(). We don't create this widget ourselves, because
33  * while a ChatWidget will be destroyed and recreated quite often (for example when switching
34  * buffers), there ususally is no need to re-render its content every time (which can be time-consuming).
35  * Before a ChatWidget is destroyed, it gives up its ownership of its contents, referring responsibility
36  * back to where it came from.
37  *
38  * Because we use this as a custom widget in Qt Designer, we cannot use a constructor that takes custom
39  * parameters. Instead, it is mandatory to call init() before using this widget.
40  */
41 class ChatWidget : public QScrollArea {
42   Q_OBJECT
43
44   public:
45     ChatWidget(QWidget *parent = 0);
46     ~ChatWidget();
47     void init(QString net, QString buf, ChatWidgetContents *contents);
48
49   public slots:
50     void clear();
51     void appendMsg(Message);
52
53   protected:
54     void resizeEvent(QResizeEvent *event);
55
56   private:
57     QString networkName, bufferName;
58     ChatWidgetContents *contents;
59 };
60
61 class ChatLine;
62
63 //!\brief Renders the complete contents of a Buffer.
64 /** Usually, this widget is used within a scroll
65  * area. Because Qt's rich-text rendering engine is much too slow for our purposes, we do everything
66  * except for the actual glyph painting ourselves. While this makes managing text quite cumbersome
67  * (we cannot, for example, use any of Qt's text editing, selecting or layouting features), it is
68  * also orders of magnitudes faster than any of the usual methods.
69  */
70 class ChatWidgetContents : public QWidget {
71   Q_OBJECT
72
73   public:
74     ChatWidgetContents(QString net, QString buf, QWidget *parent = 0);
75     ~ChatWidgetContents();
76     //int heightForWidth(int w) const;
77     virtual QSize sizeHint() const;
78
79   public slots:
80     void clear();
81     void appendMsg(Message);
82     void setWidth(qreal);
83
84   protected:
85     virtual void paintEvent(QPaintEvent *event);
86
87   protected slots:
88     virtual void mousePressEvent(QMouseEvent *event);
89     virtual void mouseReleaseEvent(QMouseEvent *event);
90     virtual void mouseMoveEvent(QMouseEvent *event);
91
92   private slots:
93     void layout(bool timer = false);
94     void triggerLayout();
95
96   private:
97     enum SelectionMode { NoSelection, TextSelected, LinesSelected };
98     enum MouseMode { Normal, Pressed, DragTsSep, DragTextSep, MarkText, MarkLines };
99     enum MousePos { None, OverTsSep, OverTextSep, OverUrl };
100     MouseMode mouseMode;
101     MousePos mousePos;
102     QPoint dragStartPos;
103     MouseMode dragStartMode;
104     int dragStartLine;
105     int dragStartCursor;
106     int curCursor;
107     int curLine;
108     SelectionMode selectionMode;
109     int selectionStart, selectionEnd, selectionLine;
110     QString networkName, bufferName;
111     QTimer *layoutTimer;
112     bool doLayout;
113
114     QList<ChatLine *> lines;
115     QList<qreal> ycoords;
116
117     int senderX;
118     int textX;
119     int tsWidth;
120     int senderWidth;
121     int textWidth;
122     int tsGrabPos;     ///< X-Position for changing the timestamp width
123     int senderGrabPos;
124     void computePositions();
125
126     qreal width;
127     qreal height;
128     qreal y;
129
130     int yToLineIdx(qreal y);
131     void clearSelection();
132     QString selectionToString();
133 };
134
135 //!\brief Containing the layout and providing the rendering of a single message.
136 /** A ChatLine takes a Message object,
137  * formats it (by turning the various message types into a human-readable form and afterwards pumping it through
138  * our Style engine), and stores it as a number of QTextLayouts representing the three fields of a chat line
139  * (timestamp, sender and text). These layouts already include any rendering information such as font,
140  * color, or selected characters. By calling layout(), they can be quickly layouted to fit a given set of field widths.
141  * Afterwards, they can quickly be painted whenever necessary.
142  *
143  * By separating the complex and slow task of interpreting and formatting Message objects (which happens exactly once
144  * per message) from the actual layouting and painting, we gain a lot of speed compared to the standard Qt rendering
145  * functions.
146  */
147 class ChatLine : public QObject {
148   Q_OBJECT
149
150   public:
151     ChatLine(Message message, QString networkName, QString bufferName);
152     ~ChatLine();
153
154     qreal layout(qreal tsWidth, qreal nickWidth, qreal textWidth);
155     qreal height() { return hght; }
156     int posToCursor(QPointF pos);
157     void draw(QPainter *p, const QPointF &pos);
158
159     enum SelectionMode { None, Partial, Full };
160     void setSelection(SelectionMode, int start = 0, int end = 0);
161     QDateTime getTimeStamp();
162     QString getSender();
163     QString getText();
164   private:
165     qreal hght;
166     Message msg;
167     QString networkName, bufferName;
168     //QString ts, nick, text;
169     qreal tsWidth, senderWidth, textWidth;
170     QTextLayout tsLayout;
171     QTextLayout senderLayout;
172     QTextLayout textLayout;
173     QVector<QTextLayout::FormatRange> tsFormat, senderFormat, textFormat;
174     Style::StringFormats tsFormatted, senderFormatted, textFormatted;
175
176     SelectionMode selectionMode;
177     int selectionStart, selectionEnd;
178     void formatMsg(Message);
179 };
180
181
182 #endif