4aac9945989f96392621bec12bef1469e4625297
[quassel.git] / src / uisupport / styledlabel.cpp
1 /***************************************************************************
2  *   Copyright (C) 2005-2014 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  *   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.         *
19  ***************************************************************************/
20
21 #include <QPainter>
22 #include <QTextDocument>
23 #include <QTextLayout>
24
25 #include "graphicalui.h"
26 #include "styledlabel.h"
27 #include "uistyle.h"
28
29 StyledLabel::StyledLabel(QWidget *parent)
30     : QFrame(parent),
31     _wrapMode(QTextOption::NoWrap),
32     _alignment(Qt::AlignVCenter|Qt::AlignLeft),
33     _toolTipEnabled(true),
34     _resizeMode(NoResize)
35 {
36     setMouseTracking(true);
37
38     QTextOption opt = _layout.textOption();
39     opt.setWrapMode(_wrapMode);
40     opt.setAlignment(_alignment);
41     _layout.setTextOption(opt);
42 }
43
44
45 void StyledLabel::setCustomFont(const QFont &font)
46 {
47     setFont(font);
48     _layout.setFont(font);
49     setText(_layout.text());
50 }
51
52
53 void StyledLabel::setWrapMode(QTextOption::WrapMode mode)
54 {
55     if (_wrapMode == mode)
56         return;
57
58     _wrapMode = mode;
59     QTextOption opt = _layout.textOption();
60     opt.setWrapMode(mode);
61     _layout.setTextOption(opt);
62
63     layout();
64 }
65
66
67 void StyledLabel::setAlignment(Qt::Alignment alignment)
68 {
69     if (_alignment == alignment)
70         return;
71
72     _alignment = alignment;
73     QTextOption opt = _layout.textOption();
74     opt.setAlignment(alignment);
75     _layout.setTextOption(opt);
76
77     layout();
78 }
79
80
81 void StyledLabel::setResizeMode(ResizeMode mode)
82 {
83     if (_resizeMode == mode)
84         return;
85
86     _resizeMode = mode;
87     if (mode == DynamicResize)
88         setWrapMode(QTextOption::WrapAtWordBoundaryOrAnywhere);
89     else
90         setWrapMode(QTextOption::NoWrap);
91 }
92
93
94 void StyledLabel::resizeEvent(QResizeEvent *event)
95 {
96     QFrame::resizeEvent(event);
97
98     layout();
99 }
100
101
102 QSize StyledLabel::sizeHint() const
103 {
104     return _sizeHint;
105 }
106
107
108 void StyledLabel::updateSizeHint()
109 {
110     QSize sh;
111     int padding = frameWidth() * 2;
112     sh = _layout.boundingRect().size().toSize() + QSize(padding, padding);
113
114     if (_sizeHint != sh) {
115         _sizeHint = sh;
116         updateGeometry();
117     }
118 }
119
120
121 void StyledLabel::setText(const QString &text)
122 {
123     UiStyle *style = GraphicalUi::uiStyle();
124
125     UiStyle::StyledString sstr = style->styleString(style->mircToInternal(text), UiStyle::PlainMsg);
126     QList<QTextLayout::FormatRange> layoutList = style->toTextLayoutList(sstr.formatList, sstr.plainText.length(), 0);
127
128     // Use default font rather than the style's
129     QTextLayout::FormatRange fmtRange;
130     fmtRange.format.setFont(font());
131     fmtRange.start = 0;
132     fmtRange.length = sstr.plainText.length();
133     layoutList << fmtRange;
134
135     // Mark URLs
136     _clickables = ClickableList::fromString(sstr.plainText);
137     foreach(Clickable click, _clickables) {
138         if (click.type() == Clickable::Url) {
139             QTextLayout::FormatRange range;
140             range.start = click.start();
141             range.length = click.length();
142             range.format.setForeground(palette().link());
143             layoutList << range;
144         }
145     }
146
147     _layout.setText(sstr.plainText);
148     _layout.setAdditionalFormats(layoutList);
149
150     layout();
151
152     endHoverMode();
153 }
154
155
156 void StyledLabel::updateToolTip()
157 {
158     if (frameRect().width() - 2*frameWidth() < _layout.minimumWidth())
159 #if QT_VERSION < 0x050000
160         setToolTip(QString("<qt>%1</qt>").arg(Qt::escape(_layout.text())));  // only rich text gets wordwrapped!
161 #else
162         setToolTip(QString("<qt>%1</qt>").arg(_layout.text().toHtmlEscaped()));  // only rich text gets wordwrapped!
163 #endif
164     else
165         setToolTip(QString());
166 }
167
168
169 void StyledLabel::layout()
170 {
171     qreal h = 0;
172     qreal w = contentsRect().width();
173
174     _layout.beginLayout();
175     forever {
176         QTextLine line = _layout.createLine();
177         if (!line.isValid())
178             break;
179         line.setLineWidth(w);
180         line.setPosition(QPointF(0, h));
181         h += line.height();
182     }
183     _layout.endLayout();
184
185     updateSizeHint();
186     updateToolTip();
187     update();
188 }
189
190
191 void StyledLabel::paintEvent(QPaintEvent *e)
192 {
193     QFrame::paintEvent(e);
194     QPainter painter(this);
195
196     qreal y = contentsRect().y() + (contentsRect().height() - _layout.boundingRect().height()) / 2;
197     _layout.draw(&painter, QPointF(contentsRect().x(), y), _extraLayoutList);
198 }
199
200
201 int StyledLabel::posToCursor(const QPointF &pos)
202 {
203     if (pos.y() < 0 || pos.y() > height())
204         return -1;
205
206     for (int l = _layout.lineCount() - 1; l >= 0; l--) {
207         QTextLine line = _layout.lineAt(l);
208         if (pos.y() >= line.y()) {
209             return line.xToCursor(pos.x(), QTextLine::CursorOnCharacter);
210         }
211     }
212     return -1;
213 }
214
215
216 void StyledLabel::mouseMoveEvent(QMouseEvent *event)
217 {
218     if (event->buttons() == Qt::NoButton) {
219         Clickable click = _clickables.atCursorPos(posToCursor(event->posF()));
220         if (click.isValid())
221             setHoverMode(click.start(), click.length());
222         else
223             endHoverMode();
224     }
225 }
226
227
228 void StyledLabel::enterEvent(QEvent *)
229 {
230     if (resizeMode() == ResizeOnHover)
231         setWrapMode(QTextOption::WrapAtWordBoundaryOrAnywhere);
232 }
233
234
235 void StyledLabel::leaveEvent(QEvent *)
236 {
237     endHoverMode();
238     if (resizeMode() == ResizeOnHover)
239         setWrapMode(QTextOption::NoWrap);
240 }
241
242
243 void StyledLabel::mousePressEvent(QMouseEvent *event)
244 {
245     if (event->button() == Qt::LeftButton) {
246         Clickable click = _clickables.atCursorPos(posToCursor(event->posF()));
247         if (click.isValid())
248             emit clickableActivated(click);
249     }
250 }
251
252
253 void StyledLabel::setHoverMode(int start, int length)
254 {
255     if (_extraLayoutList.count() >= 1 && _extraLayoutList.first().start == start && _extraLayoutList.first().length == length)
256         return;
257
258     QTextLayout::FormatRange range;
259     range.start = start;
260     range.length = length;
261     range.format.setFontUnderline(true);
262     _extraLayoutList.clear();
263     _extraLayoutList << range;
264
265     setCursor(Qt::PointingHandCursor);
266     update();
267 }
268
269
270 void StyledLabel::endHoverMode()
271 {
272     _extraLayoutList.clear();
273     setCursor(Qt::ArrowCursor);
274     update();
275 }