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
29StyledLabel::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
45void StyledLabel::setCustomFont(const QFont &font)
46{
47 setFont(font);
48 _layout.setFont(font);
49 setText(_layout.text());
50}
51
52
53void 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
67void 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
81void 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
94void StyledLabel::resizeEvent(QResizeEvent *event)
95{
96 QFrame::resizeEvent(event);
97
98 layout();
99}
100
101
102QSize StyledLabel::sizeHint() const
103{
104 return _sizeHint;
105}
106
107
108void 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
121void 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
156void 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
169void 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
191void 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
201int 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
216void StyledLabel::mouseMoveEvent(QMouseEvent *event)
217{
218 if (event->buttons() == Qt::NoButton) {
219#if QT_VERSION < 0x050000
220 Clickable click = _clickables.atCursorPos(posToCursor(event->posF()));
221#else
222 Clickable click = _clickables.atCursorPos(posToCursor(event->localPos()));
223#endif
224 if (click.isValid())
225 setHoverMode(click.start(), click.length());
226 else
227 endHoverMode();
228 }
229}
230
231
232void StyledLabel::enterEvent(QEvent *)
233{
234 if (resizeMode() == ResizeOnHover)
235 setWrapMode(QTextOption::WrapAtWordBoundaryOrAnywhere);
236}
237
238
239void StyledLabel::leaveEvent(QEvent *)
240{
241 endHoverMode();
242 if (resizeMode() == ResizeOnHover)
243 setWrapMode(QTextOption::NoWrap);
244}
245
246
247void StyledLabel::mousePressEvent(QMouseEvent *event)
248{
249 if (event->button() == Qt::LeftButton) {
250#if QT_VERSION < 0x050000
251 Clickable click = _clickables.atCursorPos(posToCursor(event->posF()));
252#else
253 Clickable click = _clickables.atCursorPos(posToCursor(event->localPos()));
254#endif
255 if (click.isValid())
256 emit clickableActivated(click);
257 }
258}
259
260
261void StyledLabel::setHoverMode(int start, int length)
262{
263 if (_extraLayoutList.count() >= 1 && _extraLayoutList.first().start == start && _extraLayoutList.first().length == length)
264 return;
265
266 QTextLayout::FormatRange range;
267 range.start = start;
268 range.length = length;
269 range.format.setFontUnderline(true);
270 _extraLayoutList.clear();
271 _extraLayoutList << range;
272
273 setCursor(Qt::PointingHandCursor);
274 update();
275}
276
277
278void StyledLabel::endHoverMode()
279{
280 _extraLayoutList.clear();
281 setCursor(Qt::ArrowCursor);
282 update();
283}
284