Store the type of the current tab completion (user or channel) in the TabCompleter...
[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 = contentsRect().width();
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 *e) {
167   QFrame::paintEvent(e);
168   QPainter painter(this);
169
170   qreal y = contentsRect().y() + (contentsRect().height() - _layout.boundingRect().height()) / 2;
171   _layout.draw(&painter, QPointF(contentsRect().x(), y), _extraLayoutList);
172 }
173
174 int StyledLabel::posToCursor(const QPointF &pos) {
175   if(pos.y() < 0 || pos.y() > height())
176     return -1;
177
178   for(int l = _layout.lineCount() - 1; l >= 0; l--) {
179     QTextLine line = _layout.lineAt(l);
180     if(pos.y() >= line.y()) {
181       return line.xToCursor(pos.x(), QTextLine::CursorOnCharacter);
182     }
183   }
184   return -1;
185 }
186
187 void StyledLabel::mouseMoveEvent(QMouseEvent *event) {
188   if(event->buttons() == Qt::NoButton) {
189     Clickable click = _clickables.atCursorPos(posToCursor(event->posF()));
190     if(click.isValid())
191       setHoverMode(click.start(), click.length());
192     else
193       endHoverMode();
194   }
195 }
196
197 void StyledLabel::enterEvent(QEvent *) {
198   if(resizeMode() == ResizeOnHover)
199     setWrapMode(QTextOption::WrapAtWordBoundaryOrAnywhere);
200 }
201
202 void StyledLabel::leaveEvent(QEvent *) {
203   endHoverMode();
204   if(resizeMode() == ResizeOnHover)
205     setWrapMode(QTextOption::NoWrap);
206 }
207
208 void StyledLabel::mousePressEvent(QMouseEvent *event) {
209   if(event->button() == Qt::LeftButton) {
210     Clickable click = _clickables.atCursorPos(posToCursor(event->posF()));
211     if(click.isValid())
212       emit clickableActivated(click);
213   }
214 }
215
216 void StyledLabel::setHoverMode(int start, int length) {
217   if(_extraLayoutList.count() >= 1 && _extraLayoutList.first().start == start && _extraLayoutList.first().length == length)
218     return;
219
220   QTextLayout::FormatRange range;
221   range.start = start;
222   range.length = length;
223   range.format.setFontUnderline(true);
224   _extraLayoutList.clear();
225   _extraLayoutList << range;
226
227   setCursor(Qt::PointingHandCursor);
228   update();
229 }
230
231 void StyledLabel::endHoverMode() {
232   _extraLayoutList.clear();
233   setCursor(Qt::ArrowCursor);
234   update();
235 }
236