1 /***************************************************************************
2 * Copyright (C) 2005-2011 by the Quassel Project *
3 * devel@quassel-irc.org *
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. *
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. *
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 #include <QApplication>
23 #include "graphicalui.h"
24 #include "qmlchatline.h"
26 void QmlChatLine::registerTypes() {
27 qRegisterMetaType<RenderData>("QmlChatLine::RenderData");
28 qRegisterMetaTypeStreamOperators<RenderData>("QmlChatLine::RenderData");
29 qmlRegisterType<QmlChatLine>("eu.quassel.qml", 1, 0, "ChatLine");
32 QDataStream &operator<<(QDataStream &out, const QmlChatLine::RenderData &data) {
33 out << data.isValid << data.messageLabel;
34 for(int i = 0; i < (int)QmlChatLine::NumColumns; ++i) {
35 const QmlChatLine::RenderData::Column &col = data[static_cast<QmlChatLine::ColumnType>(i)];
36 out << col.text << col.formats << col.background << col.selectedBackground;
41 QDataStream &operator>>(QDataStream &in, QmlChatLine::RenderData &data) {
42 in >> data.isValid >> data.messageLabel;
43 for(int i = 0; i < (int)QmlChatLine::NumColumns; ++i) {
44 QmlChatLine::RenderData::Column &col = data[static_cast<QmlChatLine::ColumnType>(i)];
45 in >> col.text >> col.formats >> col.background >> col.selectedBackground;
50 QmlChatLine::QmlChatLine(QDeclarativeItem *parent)
51 : QDeclarativeItem(parent),
57 setFlag(ItemHasNoContents, false);
58 setImplicitHeight(QApplication::fontMetrics().height());
59 setImplicitWidth(1000);
60 connect(this, SIGNAL(columnWidthChanged(ColumnType)), SLOT(onColumnWidthChanged(ColumnType)));
63 QmlChatLine::~QmlChatLine() {
67 void QmlChatLine::setTimestampWidth(qreal w) {
68 if(w != _timestampWidth) {
70 emit timestampWidthChanged(w);
71 emit columnWidthChanged(TimestampColumn);
75 void QmlChatLine::setSenderWidth(qreal w) {
76 if(w != _senderWidth) {
78 emit senderWidthChanged(w);
79 emit columnWidthChanged(SenderColumn);
83 void QmlChatLine::setContentsWidth(qreal w) {
84 if(w != _contentsWidth) {
87 if(renderData().isValid)
90 emit contentsWidthChanged(w);
91 emit columnWidthChanged(ContentsColumn);
95 void QmlChatLine::setColumnSpacing(qreal s) {
96 if(s != _columnSpacing) {
98 emit columnSpacingChanged(s);
102 QPointF QmlChatLine::columnPos(ColumnType colType) const {
104 case TimestampColumn:
105 return QPointF(0, 0);
107 return QPointF(timestampWidth(), 0);
109 return QPointF(timestampWidth() + senderWidth(), 0);
115 qreal QmlChatLine::columnWidth(ColumnType colType) const {
117 case TimestampColumn:
118 return timestampWidth();
120 return senderWidth();
122 return contentsWidth();
128 QRectF QmlChatLine::columnBoundingRect(ColumnType colType) const {
131 case TimestampColumn:
132 return QRectF(columnPos(TimestampColumn), QSizeF(timestampWidth() - columnSpacing(), implicitHeight()));
134 return QRectF(columnPos(SenderColumn), QSizeF(senderWidth() - columnSpacing(), implicitHeight()));
136 return QRectF(columnPos(ContentsColumn), QSizeF(contentsWidth(), implicitHeight()));
142 void QmlChatLine::setRenderData(const RenderData &data) {
153 QmlChatLine::Layout *QmlChatLine::layout() {
155 _layout = new Layout(this);
160 void QmlChatLine::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) {
163 if(renderData().isValid)
164 layout()->draw(painter);
167 void QmlChatLine::onColumnWidthChanged(ColumnType colType) {
169 setImplicitWidth(_timestampWidth + _senderWidth + _contentsWidth);
173 /**************************************************************************************/
175 QmlChatLine::Layout::Layout(QmlChatLine *parent)
178 _timestampLayout = new TimestampLayout(parent);
179 _senderLayout = new SenderLayout(parent);
180 _contentsLayout = new ContentsLayout(parent);
183 QmlChatLine::Layout::~Layout() {
184 delete _timestampLayout;
185 delete _senderLayout;
186 delete _contentsLayout;
189 qreal QmlChatLine::Layout::height() const {
190 return _contentsLayout->height();
193 void QmlChatLine::Layout::compute() {
194 _timestampLayout->compute();
195 _senderLayout->compute();
196 _contentsLayout->compute();
199 void QmlChatLine::Layout::draw(QPainter *p) {
200 _timestampLayout->draw(p);
201 _senderLayout->draw(p);
202 _contentsLayout->draw(p);
207 QmlChatLine::ColumnLayout::ColumnLayout(QmlChatLine::ColumnType type, QmlChatLine *parent)
215 QmlChatLine::ColumnLayout::~ColumnLayout() {
219 void QmlChatLine::ColumnLayout::initLayout(QTextOption::WrapMode wrapMode, Qt::Alignment alignment) {
223 const RenderData::Column &data = chatLine()->renderData()[columnType()];
224 _layout = new QTextLayout(data.text);
227 option.setWrapMode(wrapMode);
228 option.setAlignment(alignment);
229 layout()->setTextOption(option);
231 QList<QTextLayout::FormatRange> formats = GraphicalUi::uiStyle()->toTextLayoutList(data.formats, layout()->text().length(), chatLine()->renderData().messageLabel);
232 layout()->setAdditionalFormats(formats);
237 qreal QmlChatLine::ColumnLayout::height() const {
238 return layout()->boundingRect().height();
241 void QmlChatLine::ColumnLayout::compute() {
242 qreal width = chatLine()->columnBoundingRect(columnType()).width();
244 layout()->beginLayout();
246 QTextLine line = layout()->createLine();
250 line.setLineWidth(width);
251 line.setPosition(QPointF(0, h));
254 layout()->endLayout();
257 void QmlChatLine::ColumnLayout::draw(QPainter *p) {
260 QRectF rect = chatLine()->columnBoundingRect(columnType());
262 qreal layoutWidth = layout()->minimumWidth();
265 if(layout()->textOption().alignment() == Qt::AlignRight) {
267 if(chatScene()->senderCutoffMode() == ChatScene::CutoffLeft)
268 offset = qMin(width() - layoutWidth, (qreal)0);
270 offset = qMax(layoutWidth - width(), (qreal)0);
272 offset = qMax(layoutWidth - rect.width(), (qreal)0);
275 if(layoutWidth > rect.width()) {
276 // Draw a nice gradient for longer items
278 QLinearGradient gradient;
280 gradient.setStart(0, 0);
281 gradient.setFinalStop(12, 0);
282 gradient.setColorAt(0, Qt::transparent);
283 gradient.setColorAt(1, Qt::white);
285 gradient.setStart(rect.width()-12, 0);
286 gradient.setFinalStop(rect.width(), 0);
287 gradient.setColorAt(0, Qt::white);
288 gradient.setColorAt(1, Qt::transparent);
291 QImage img(layout()->boundingRect().toRect().size(), QImage::Format_ARGB32_Premultiplied);
292 //img.fill(Qt::transparent);
293 QPainter imgPainter(&img);
294 imgPainter.fillRect(img.rect(), gradient);
295 imgPainter.setCompositionMode(QPainter::CompositionMode_SourceIn);
296 layout()->draw(&imgPainter, QPointF(qMax(offset, (qreal)0), 0), selectionFormats());
298 p->drawImage(rect.topLeft(), img);
300 layout()->draw(p, rect.topLeft(), selectionFormats(), rect);
306 QVector<QTextLayout::FormatRange> QmlChatLine::ColumnLayout::selectionFormats() const {
307 return QVector<QTextLayout::FormatRange>();
312 QmlChatLine::TimestampLayout::TimestampLayout(QmlChatLine *chatLine)
313 : ColumnLayout(TimestampColumn, chatLine)
315 initLayout(QTextOption::NoWrap, Qt::AlignLeft);
321 QmlChatLine::SenderLayout::SenderLayout(QmlChatLine *chatLine)
322 : ColumnLayout(SenderColumn, chatLine)
324 initLayout(QTextOption::NoWrap, Qt::AlignRight);
330 QmlChatLine::ContentsLayout::ContentsLayout(QmlChatLine *chatLine)
331 : ColumnLayout(ContentsColumn, chatLine)
333 initLayout(QTextOption::WrapAtWordBoundaryOrAnywhere, Qt::AlignLeft);
336 void QmlChatLine::ContentsLayout::compute() {
337 ColumnLayout::compute();
338 chatLine()->setImplicitHeight(layout()->boundingRect().height());