/***************************************************************************
- * Copyright (C) 2005-09 by the Quassel Project *
+ * Copyright (C) 2005-2010 by the Quassel Project *
* devel@quassel-irc.org *
* *
* This program is free software; you can redistribute it and/or modify *
#include "buffermodel.h"
#include "bufferview.h"
#include "chatitem.h"
+#include "chatline.h"
#include "chatlinemodel.h"
#include "contextmenuactionprovider.h"
#include "iconloader.h"
#include "qtui.h"
#include "qtuistyle.h"
-ChatItem::ChatItem(const qreal &width, const qreal &height, const QPointF &pos, QGraphicsItem *parent)
- : QGraphicsItem(parent),
- _boundingRect(0, 0, width, height),
- _selectionMode(NoSelection),
- _selectionStart(-1)
+ChatItem::ChatItem(const QRectF &boundingRect, ChatLine *parent)
+: _parent(parent),
+ _boundingRect(boundingRect),
+ _selectionMode(NoSelection),
+ _selectionStart(-1)
{
- setAcceptHoverEvents(true);
- setZValue(20);
- setPos(pos);
+
}
QVariant ChatItem::data(int role) const {
return model()->data(index, role);
}
-qint16 ChatItem::posToCursor(const QPointF &pos) const {
+qint16 ChatItem::posToCursor(const QPointF &posInLine) const {
+ QPointF pos = mapFromLine(posInLine);
if(pos.y() > height()) return data(MessageModel::DisplayRole).toString().length();
if(pos.y() < 0) return 0;
}
void ChatItem::paintBackground(QPainter *painter) {
- painter->setClipRect(boundingRect()); // no idea why QGraphicsItem clipping won't work
-
QVariant bgBrush;
if(_selectionMode == FullSelection)
bgBrush = data(ChatLineModel::SelectedBackgroundRole);
QTextLayout layout;
initLayout(&layout);
- layout.draw(painter, QPointF(0,0), additionalFormats(), boundingRect());
+ layout.draw(painter, pos(), additionalFormats(), boundingRect());
// layout()->draw(painter, QPointF(0,0), formats, boundingRect());
_selectionMode = mode;
_selectionStart = start;
_selectionEnd = end;
- update();
+ chatLine()->update();
}
void ChatItem::setFullSelection() {
if(_selectionMode != FullSelection) {
_selectionMode = FullSelection;
- update();
+ chatLine()->update();
}
}
void ChatItem::clearSelection() {
if(_selectionMode != NoSelection) {
_selectionMode = NoSelection;
- update();
+ chatLine()->update();
}
}
void ChatItem::continueSelecting(const QPointF &pos) {
_selectionMode = PartialSelection;
_selectionEnd = posToCursor(pos);
- update();
+ chatLine()->update();
}
bool ChatItem::isPosOverSelection(const QPointF &pos) const {
chatScene()->setSelectingItem(this);
_selectionStart = _selectionEnd = posToCursor(pos);
_selectionMode = NoSelection; // will be set to PartialSelection by mouseMoveEvent
- update();
+ chatLine()->update();
}
}
void ChatItem::mouseMoveEvent(QGraphicsSceneMouseEvent *event) {
if(event->buttons() == Qt::LeftButton) {
- if(contains(event->pos())) {
+ if(boundingRect().contains(event->pos())) {
qint16 end = posToCursor(event->pos());
if(end != _selectionEnd) {
_selectionEnd = end;
_selectionMode = (_selectionStart != _selectionEnd ? PartialSelection : NoSelection);
- update();
+ chatLine()->update();
}
} else {
setFullSelection();
gradient.setColorAt(0, Qt::white);
gradient.setColorAt(1, Qt::black);
}
- maskPainter.fillRect(boundingRect(), gradient);
+ maskPainter.fillRect(0, 0, pixmap.width(), pixmap.height(), gradient);
pixmap.setAlphaChannel(mask);
- painter->drawPixmap(0, 0, pixmap);
+ painter->drawPixmap(pos(), pixmap);
} else {
- layout.draw(painter, QPointF(0,0), additionalFormats(), boundingRect());
+ layout.draw(painter, pos(), additionalFormats(), boundingRect());
}
}
ContentsChatItem::ActionProxy ContentsChatItem::_actionProxy;
-ContentsChatItem::ContentsChatItem(const qreal &width, const QPointF &pos, QGraphicsItem *parent)
- : ChatItem(0, 0, pos, parent),
+ContentsChatItem::ContentsChatItem(const QPointF &pos, const qreal &width, ChatLine *parent)
+ : ChatItem(QRectF(pos, QSizeF(width, 0)), parent),
_data(0)
{
+ setPos(pos);
setGeometryByWidth(width);
}
WrapColumnFinder finder(this);
while(finder.nextWrapColumn(w) > 0)
lines++;
- qreal h = lines * fontMetrics()->lineSpacing();
+ qreal spacing = qMax(fontMetrics()->lineSpacing(), fontMetrics()->height()); // cope with negative leading()
+ qreal h = lines * spacing;
delete _data;
_data = 0;
- if(w != width() || h != height()) {
- prepareGeometryChange();
+ if(w != width() || h != height())
setGeometry(w, h);
- }
+
return h;
}
void ContentsChatItem::doLayout(QTextLayout *layout) const {
+ // QString t = data(Qt::DisplayRole).toString(); bool d = t.contains("protien");
ChatLineModel::WrapList wrapList = data(ChatLineModel::WrapListRole).value<ChatLineModel::WrapList>();
if(!wrapList.count()) return; // empty chatitem
qreal h = 0;
+ qreal spacing = qMax(fontMetrics()->lineSpacing(), fontMetrics()->height()); // cope with negative leading()
WrapColumnFinder finder(this);
layout->beginLayout();
forever {
break;
int col = finder.nextWrapColumn(width());
- line.setNumColumns(col >= 0 ? col - line.textStart() : layout->text().length());
+ if(col < 0)
+ col = layout->text().length();
+ int num = col - line.textStart();
+
+ line.setNumColumns(num);
+
+ // Sometimes, setNumColumns will create a line that's too long (cf. Qt bug 238249)
+ // We verify this and try setting the width again, making it shorter each time until the lengths match.
+ // Dead fugly, but seems to work…
+ for(int i = line.textLength()-1; i >= 0 && line.textLength() > num; i--) {
+ line.setNumColumns(i);
+ }
+ if(num != line.textLength()) {
+ qWarning() << "WARNING: Layout engine couldn't workaround Qt bug 238249, please report!";
+ // qDebug() << num << line.textLength() << t.mid(line.textStart(), line.textLength()) << t.mid(line.textStart() + line.textLength());
+ }
+
line.setPosition(QPointF(0, h));
- h += fontMetrics()->lineSpacing();
+ h += spacing;
}
layout->endLayout();
}
void ContentsChatItem::endHoverMode() {
if(privateData()) {
if(privateData()->currentClickable.isValid()) {
- setCursor(Qt::ArrowCursor);
+ chatLine()->setCursor(Qt::ArrowCursor);
privateData()->currentClickable = Clickable();
}
clearWebPreview();
- update();
+ chatLine()->update();
}
}
setSelectionStart(start);
setSelectionEnd(end);
}
- update();
+ chatLine()->update();
} else if(clickMode == ChatScene::TripleClick) {
setSelection(PartialSelection, 0, data(ChatLineModel::DisplayRole).toString().length());
}
onClickable = true;
}
if(onClickable) {
- setCursor(Qt::PointingHandCursor);
+ chatLine()->setCursor(Qt::PointingHandCursor);
privateData()->currentClickable = click;
- update();
+ chatLine()->update();
return;
}
}
qreal height = line.height();
qreal y = height * line.lineNumber();
- QPointF topLeft = scenePos() + QPointF(x, y);
+ QPointF topLeft = mapToScene(pos()) + QPointF(x, y);
QRectF urlRect = QRectF(topLeft.x(), topLeft.y(), width, height);
- QString url = data(ChatLineModel::DisplayRole).toString().mid(click.start(), click.length());
- if(!url.contains("://"))
- url = "http://" + url;
+ QString urlstr = data(ChatLineModel::DisplayRole).toString().mid(click.start(), click.length());
+ if(!urlstr.contains("://"))
+ urlstr= "http://" + urlstr;
+ QUrl url = QUrl::fromEncoded(urlstr.toUtf8(), QUrl::TolerantMode);
chatScene()->loadWebPreview(this, url, urlRect);
#endif
}