a87aa8ce77cee0b57eceeca13b3a79d200dee323
[quassel.git] / src / uisupport / styledlabel.cpp
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 #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 void StyledLabel::setCustomFont(const QFont &font) {
45   setFont(font);
46   _layout.setFont(font);
47   setText(_layout.text());
48 }
49
50 void StyledLabel::setWrapMode(QTextOption::WrapMode mode) {
51   if(_wrapMode == mode)
52     return;
53
54   _wrapMode = mode;
55   QTextOption opt = _layout.textOption();
56   opt.setWrapMode(mode);
57   _layout.setTextOption(opt);
58
59   layout();
60 }
61
62 void StyledLabel::setAlignment(Qt::Alignment alignment) {
63   if(_alignment == alignment)
64     return;
65
66   _alignment = alignment;
67   QTextOption opt = _layout.textOption();
68   opt.setAlignment(alignment);
69   _layout.setTextOption(opt);
70
71   layout();
72 }
73
74 void StyledLabel::setResizeMode(ResizeMode mode) {
75   if(_resizeMode == mode)
76     return;
77
78   _resizeMode = mode;
79   if(mode == DynamicResize)
80     setWrapMode(QTextOption::WrapAtWordBoundaryOrAnywhere);
81   else
82     setWrapMode(QTextOption::NoWrap);
83 }
84
85 void StyledLabel::resizeEvent(QResizeEvent *event) {
86   QFrame::resizeEvent(event);
87
88   layout();
89 }
90
91 QSize StyledLabel::sizeHint() const {
92   return _sizeHint;
93 }
94
95 void StyledLabel::updateSizeHint() {
96   QSize sh;
97   int padding = frameWidth() * 2;
98   sh = _layout.boundingRect().size().toSize() + QSize(padding, padding);
99
100   if(_sizeHint != sh) {
101     _sizeHint = sh;
102     updateGeometry();
103   }
104 }
105
106 void StyledLabel::setText(const QString &text) {
107   UiStyle *style = GraphicalUi::uiStyle();
108
109   UiStyle::StyledString sstr = style->styleString(style->mircToInternal(text), UiStyle::PlainMsg);
110   QList<QTextLayout::FormatRange> layoutList = style->toTextLayoutList(sstr.formatList, sstr.plainText.length(), 0);
111
112   // Use default font rather than the style's
113   QTextLayout::FormatRange fmtRange;
114   fmtRange.format.setFont(font());
115   fmtRange.start = 0;
116   fmtRange.length = sstr.plainText.length();
117   layoutList << fmtRange;
118
119   // Mark URLs
120   _clickables = ClickableList::fromString(sstr.plainText);
121   foreach(Clickable click, _clickables) {
122     if(click.type() == Clickable::Url) {
123       QTextLayout::FormatRange range;
124       range.start = click.start();
125       range.length = click.length();
126       range.format.setForeground(palette().link());
127       layoutList << range;
128     }
129   }
130
131   _layout.setText(sstr.plainText);
132   _layout.setAdditionalFormats(layoutList);
133
134   layout();
135
136   endHoverMode();
137 }
138
139 void StyledLabel::updateToolTip() {
140   if(frameRect().width() - 2*frameWidth() < _layout.minimumWidth())
141     setToolTip(QString("<qt>%1</qt>").arg(Qt::escape(_layout.text()))); // only rich text gets wordwrapped!
142   else
143     setToolTip(QString());
144 }
145
146 void StyledLabel::layout() {
147   qreal h = 0;
148   qreal w = frameRect().width() - 2*frameWidth();
149
150   _layout.beginLayout();
151   forever {
152     QTextLine line = _layout.createLine();
153     if(!line.isValid())
154       break;
155     line.setLineWidth(w);
156     line.setPosition(QPointF(0, h));
157     h += line.height();
158   }
159   _layout.endLayout();
160
161   updateSizeHint();
162   updateToolTip();
163   update();
164 }
165
166 void StyledLabel::paintEvent(QPaintEvent *) {
167   QPainter painter(this);
168
169   qreal y = (frameRect().height() - _layout.boundingRect().height()) / 2;
170   _layout.draw(&painter, QPointF(0, y), _extraLayoutList);
171 }
172
173 int StyledLabel::posToCursor(const QPointF &pos) {
174   if(pos.y() < 0 || pos.y() > height())
175     return -1;
176
177   for(int l = _layout.lineCount() - 1; l >= 0; l--) {
178     QTextLine line = _layout.lineAt(l);
179     if(pos.y() >= line.y()) {
180       return line.xToCursor(pos.x(), QTextLine::CursorOnCharacter);
181     }
182   }
183   return -1;
184 }
185
186 void StyledLabel::mouseMoveEvent(QMouseEvent *event) {
187   if(event->buttons() == Qt::NoButton) {
188     Clickable click = _clickables.atCursorPos(posToCursor(event->posF()));
189     if(click.isValid())
190       setHoverMode(click.start(), click.length());
191     else
192       endHoverMode();
193   }
194 }
195
196 void StyledLabel::enterEvent(QEvent *) {
197   if(resizeMode() == ResizeOnHover)
198     setWrapMode(QTextOption::WrapAtWordBoundaryOrAnywhere);
199 }
200
201 void StyledLabel::leaveEvent(QEvent *) {
202   endHoverMode();
203   if(resizeMode() == ResizeOnHover)
204     setWrapMode(QTextOption::NoWrap);
205 }
206
207 void StyledLabel::mousePressEvent(QMouseEvent *event) {
208   if(event->button() == Qt::LeftButton) {
209     Clickable click = _clickables.atCursorPos(posToCursor(event->posF()));
210     if(click.isValid())
211       emit clickableActivated(click);
212   }
213 }
214
215 void StyledLabel::setHoverMode(int start, int length) {
216   if(_extraLayoutList.count() >= 1 && _extraLayoutList.first().start == start && _extraLayoutList.first().length == length)
217     return;
218
219   QTextLayout::FormatRange range;
220   range.start = start;
221   range.length = length;
222   range.format.setFontUnderline(true);
223   _extraLayoutList.clear();
224   _extraLayoutList << range;
225
226   setCursor(Qt::PointingHandCursor);
227   update();
228 }
229
230 void StyledLabel::endHoverMode() {
231   _extraLayoutList.clear();
232   setCursor(Qt::ArrowCursor);
233   update();
234 }
235