This saves ~75% of graphicsitems in use, and should save some RAM and CPU.
It also solves some bugs with drawing, e.g. the marker line is now also
visible if the following line is highlighted.
This is the first step of ChatView optimizations I plan.
/***************************************************************************
- * 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 "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);
-}
-
-const QAbstractItemModel *ChatItem::model() const {
- return static_cast<ChatLine *>(parentItem())->model();
-}
-int ChatItem::row() const {
- return static_cast<ChatLine *>(parentItem())->row();
}
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);
}
delete _data;
_data = 0;
- if(w != width() || h != height()) {
- prepareGeometryChange();
+ if(w != width() || h != height())
setGeometry(w, h);
- }
+
return h;
}
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 urlstr = data(ChatLineModel::DisplayRole).toString().mid(click.start(), click.length());
/***************************************************************************
- * 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 *
#define CHATITEM_H_
#include <QAction>
-#include <QGraphicsItem>
#include <QObject>
#include "chatlinemodel.h"
#include <QTextLayout>
-class ChatItem : public QGraphicsItem {
+class ChatLine;
+
+/* All external positions are relative to the parent ChatLine */
+/* Yes, that's also true for the boundingRect() and related things */
+
+class ChatItem {
protected:
- ChatItem(const qreal &width, const qreal &height, const QPointF &pos, QGraphicsItem *parent);
+ // boundingRect is relative to the parent ChatLine
+ ChatItem(const QRectF &boundingRect, ChatLine *parent);
+ virtual ~ChatItem() {}
public:
- const QAbstractItemModel *model() const;
- int row() const;
+ inline const QAbstractItemModel *model() const;
+ inline ChatLine *chatLine() const;
+ inline ChatScene *chatScene() const;
+ inline int row() const;
virtual ChatLineModel::ColumnType column() const = 0;
- inline ChatScene *chatScene() const { return qobject_cast<ChatScene *>(scene()); }
- inline QRectF boundingRect() const { return _boundingRect; }
- inline qreal width() const { return _boundingRect.width(); }
- inline qreal height() const { return _boundingRect.height(); }
+ // The boundingRect() is relative to the parent ChatLine
+ inline QRectF boundingRect() const;
+ inline qreal width() const;
+ inline qreal height() const;
+ inline QPointF pos() const;
+ inline qreal x() const;
+ inline qreal y() const;
+
+ inline QPointF mapToLine(const QPointF &) const;
+ inline QPointF mapFromLine(const QPointF &) const;
+ inline QPointF mapToScene(const QPointF &) const;
+ inline QPointF mapFromScene(const QPointF &) const;
void initLayoutHelper(QTextLayout *layout, QTextOption::WrapMode, Qt::Alignment = Qt::AlignLeft) const;
virtual inline void initLayout(QTextLayout *layout) const {
virtual UiStyle::FormatList formatList() const;
virtual void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = 0);
- enum { Type = ChatScene::ChatItemType };
- virtual inline int type() const { return Type; }
+ virtual inline int type() const { return ChatScene::ChatItemType; }
QVariant data(int role) const;
virtual void mouseMoveEvent(QGraphicsSceneMouseEvent *event);
virtual void mousePressEvent(QGraphicsSceneMouseEvent *event);
virtual void mouseReleaseEvent(QGraphicsSceneMouseEvent *event);
+ virtual void hoverEnterEvent(QGraphicsSceneHoverEvent *) {};
+ virtual void hoverLeaveEvent(QGraphicsSceneHoverEvent *) {};
+ virtual void hoverMoveEvent(QGraphicsSceneHoverEvent *) {};
void paintBackground(QPainter *);
QVector<QTextLayout::FormatRange> selectionFormats() const;
qint16 posToCursor(const QPointF &pos) const;
- // WARNING: setGeometry and setHeight should not be used without either:
- // a) calling prepareGeometryChange() immediately before setColumns()
- // b) calling Chatline::setPos() immediately afterwards
- inline void setGeometry(qreal width, qreal height) {
- _boundingRect.setWidth(width);
- _boundingRect.setHeight(height);
- }
- inline void setHeight(const qreal &height) {
- _boundingRect.setHeight(height);
- }
- inline void setWidth(const qreal &width) {
- _boundingRect.setWidth(width);
- }
+ inline void setGeometry(qreal width, qreal height) { _boundingRect.setSize(QSizeF(width, height)); }
+ inline void setHeight(const qreal &height) { _boundingRect.setHeight(height); }
+ inline void setWidth(const qreal &width) { _boundingRect.setWidth(width); }
+ inline void setPos(const QPointF &pos) { _boundingRect.moveTopLeft(pos); }
private:
- // internal selection stuff
- void setSelection(int start, int length);
-
+ ChatLine *_parent;
QRectF _boundingRect;
SelectionMode _selectionMode;
qint16 _selectionStart, _selectionEnd;
+ // internal selection stuff
+ void setSelection(int start, int length);
+
friend class ChatLine;
};
//! A ChatItem for the timestamp column
class TimestampChatItem : public ChatItem {
public:
- TimestampChatItem(const qreal &width, const qreal &height, QGraphicsItem *parent) : ChatItem(width, height, QPointF(0, 0), parent) {}
- enum { Type = ChatScene::TimestampChatItemType };
- virtual inline int type() const { return Type; }
+ TimestampChatItem(const QRectF &boundingRect, ChatLine *parent) : ChatItem(boundingRect, parent) {}
+ virtual inline int type() const { return ChatScene::TimestampChatItemType; }
virtual inline ChatLineModel::ColumnType column() const { return ChatLineModel::TimestampColumn; }
};
//! A ChatItem for the sender column
class SenderChatItem : public ChatItem {
public:
- SenderChatItem(const qreal &width, const qreal &height, const QPointF &pos, QGraphicsItem *parent) : ChatItem(width, height, pos, parent) {}
+ SenderChatItem(const QRectF &boundingRect, ChatLine *parent) : ChatItem(boundingRect, parent) {}
virtual inline ChatLineModel::ColumnType column() const { return ChatLineModel::SenderColumn; }
protected:
virtual void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = 0);
- enum { Type = ChatScene::SenderChatItemType };
- virtual inline int type() const { return Type; }
+ virtual inline int type() const { return ChatScene::SenderChatItemType; }
virtual inline void initLayout(QTextLayout *layout) const {
initLayoutHelper(layout, QTextOption::ManualWrap, Qt::AlignRight);
doLayout(layout);
Q_DECLARE_TR_FUNCTIONS(ContentsChatItem)
public:
- ContentsChatItem(const qreal &width, const QPointF &pos, QGraphicsItem *parent);
+ ContentsChatItem(const QPointF &pos, const qreal &width, ChatLine *parent);
~ContentsChatItem();
- enum { Type = ChatScene::ContentsChatItemType };
- virtual inline int type() const { return Type; }
+ virtual inline int type() const { return ChatScene::ContentsChatItemType; }
inline ChatLineModel::ColumnType column() const { return ChatLineModel::ContentsColumn; }
QFontMetricsF *fontMetrics() const;
/*************************************************************************************************/
+#include "chatline.h" /* avoid circular includes */
+
+// Inlines
+
+ChatLine *ChatItem::chatLine() const { return _parent; }
+ChatScene *ChatItem::chatScene() const { return chatLine()->chatScene(); }
+const QAbstractItemModel *ChatItem::model() const { return chatLine()->model(); }
+int ChatItem::row() const { return chatLine()->row(); }
+
+QRectF ChatItem::boundingRect() const { return _boundingRect; }
+qreal ChatItem::width() const { return _boundingRect.width(); }
+qreal ChatItem::height() const { return _boundingRect.height(); }
+QPointF ChatItem::pos() const { return _boundingRect.topLeft(); }
+qreal ChatItem::x() const { return pos().x(); }
+qreal ChatItem::y() const { return pos().y(); }
+
+QPointF ChatItem::mapToLine(const QPointF &p) const { return p + pos(); }
+QPointF ChatItem::mapFromLine(const QPointF &p) const { return p - pos(); }
+// relative to the ChatLine
+QPointF ChatItem::mapToScene(const QPointF &p) const { return chatLine()->mapToScene(p /* + pos() */); }
+QPointF ChatItem::mapFromScene(const QPointF &p) const { return chatLine()->mapFromScene(p) /* - pos() */; }
+
#endif
/***************************************************************************
- * 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 "client.h"
#include "chatitem.h"
#include "chatline.h"
+#include "chatview.h"
#include "columnhandleitem.h"
#include "messagemodel.h"
#include "networkmodel.h"
#include "qtuistyle.h"
ChatLine::ChatLine(int row, QAbstractItemModel *model,
- const qreal &width,
- const qreal ×tampWidth, const qreal &senderWidth, const qreal &contentsWidth,
- const QPointF &senderPos, const QPointF &contentsPos,
- QGraphicsItem *parent)
+ const qreal &width,
+ const qreal ×tampWidth, const qreal &senderWidth, const qreal &contentsWidth,
+ const QPointF &senderPos, const QPointF &contentsPos,
+ QGraphicsItem *parent)
: QGraphicsItem(parent),
_row(row), // needs to be set before the items
_model(model),
- _contentsItem(contentsWidth, contentsPos, this),
- _senderItem(senderWidth, _contentsItem.height(), senderPos, this),
- _timestampItem(timestampWidth, _contentsItem.height(), this),
+ _contentsItem(contentsPos, contentsWidth, this),
+ _senderItem(QRectF(senderPos, QSizeF(senderWidth, _contentsItem.height())), this),
+ _timestampItem(QRectF(0, 0, timestampWidth, _contentsItem.height()), this),
_width(width),
_height(_contentsItem.height()),
- _selection(0)
+ _selection(0),
+ _mouseGrabberItem(0)
{
Q_ASSERT(model);
QModelIndex index = model->index(row, ChatLineModel::ContentsColumn);
setZValue(0);
- setHighlighted(model->data(index, MessageModel::FlagsRole).toInt() & Message::Highlight);
+ setAcceptHoverEvents(true);
+ setHighlighted(index.data(MessageModel::FlagsRole).toInt() & Message::Highlight);
}
-ChatItem &ChatLine::item(ChatLineModel::ColumnType column) {
+ChatItem *ChatLine::item(ChatLineModel::ColumnType column) {
switch(column) {
case ChatLineModel::TimestampColumn:
- return _timestampItem;
+ return &_timestampItem;
case ChatLineModel::SenderColumn:
- return _senderItem;
+ return &_senderItem;
case ChatLineModel::ContentsColumn:
- return _contentsItem;
+ return &_contentsItem;
default:
- return *(ChatItem *)0; // provoke an error
+ return 0;
}
}
-// NOTE: senderPos is in ChatLines coordinate system!
+ChatItem *ChatLine::itemAt(const QPointF &pos) {
+ if(_contentsItem.boundingRect().contains(pos))
+ return &_contentsItem;
+ if(_senderItem.boundingRect().contains(pos))
+ return &_senderItem;
+ if(_timestampItem.boundingRect().contains(pos))
+ return &_timestampItem;
+ return 0;
+}
+
+void ChatLine::setMouseGrabberItem(ChatItem *item) {
+ _mouseGrabberItem = item;
+}
+
+bool ChatLine::sceneEvent(QEvent *event) {
+ if(event->type() == QEvent::GrabMouse) {
+ // get mouse cursor pos relative to us
+ ChatView *view = chatScene()->chatView();
+ QPointF linePos = mapFromScene(view->mapToScene(view->mapFromGlobal(QCursor::pos())));
+ setMouseGrabberItem(itemAt(linePos));
+ } else if(event->type() == QEvent::UngrabMouse) {
+ setMouseGrabberItem(0);
+ }
+ return QGraphicsItem::sceneEvent(event);
+}
+
void ChatLine::setFirstColumn(const qreal ×tampWidth, const qreal &senderWidth, const QPointF &senderPos) {
- _timestampItem.prepareGeometryChange();
_timestampItem.setGeometry(timestampWidth, _height);
- // senderItem doesn't need a geom change as it's Pos is changed (ensured by void ChatScene::firstHandlePositionChanged(qreal xpos))
_senderItem.setGeometry(senderWidth, _height);
_senderItem.setPos(senderPos);
}
-// NOTE: contentsPos is in ChatLines coordinate system!
-void ChatLine::setSecondColumn(const qreal &senderWidth, const qreal &contentsWidth,
- const QPointF &contentsPos, qreal &linePos) {
- // contentsItem doesn't need a geom change as it's Pos is changed (ensured by void ChatScene::firstHandlePositionChanged(qreal xpos))
+void ChatLine::setSecondColumn(const qreal &senderWidth, const qreal &contentsWidth, const QPointF &contentsPos, qreal &linePos) {
+ // linepos is the *bottom* position for the line
qreal height = _contentsItem.setGeometryByWidth(contentsWidth);
linePos -= height;
- bool needGeometryChange = linePos == pos().y();
+ bool needGeometryChange = (height != _height);
- if(needGeometryChange) {
- _timestampItem.prepareGeometryChange();
- _senderItem.prepareGeometryChange();
- }
_timestampItem.setHeight(height);
_senderItem.setGeometry(senderWidth, height);
-
_contentsItem.setPos(contentsPos);
if(needGeometryChange)
}
void ChatLine::setGeometryByWidth(const qreal &width, const qreal &contentsWidth, qreal &linePos) {
+ // linepos is the *bottom* position for the line
qreal height = _contentsItem.setGeometryByWidth(contentsWidth);
linePos -= height;
bool needGeometryChange = (height != _height || width != _width);
if(height != _height) {
- _timestampItem.prepareGeometryChange();
_timestampItem.setHeight(height);
- _senderItem.prepareGeometryChange();
_senderItem.setHeight(height);
}
if(sel != _selection) {
_selection = sel;
for(int i = 0; i < minColumn; i++)
- item((ChatLineModel::ColumnType)i).clearSelection();
+ item((ChatLineModel::ColumnType)i)->clearSelection();
for(int i = minColumn; i <= ChatLineModel::ContentsColumn; i++)
- item((ChatLineModel::ColumnType)i).setFullSelection();
+ item((ChatLineModel::ColumnType)i)->setFullSelection();
update();
}
} else {
if(sel != _selection) {
_selection = sel;
for(int i = 0; i <= ChatLineModel::ContentsColumn; i++)
- item((ChatLineModel::ColumnType)i).clearSelection();
+ item((ChatLineModel::ColumnType)i)->clearSelection();
update();
}
}
if(_selection & Selected) {
QTextCharFormat selFmt = QtUi::style()->format(UiStyle::formatType(type), label | UiStyle::Selected);
if(selFmt.hasProperty(QTextFormat::BackgroundBrush)) {
- qreal left = item((ChatLineModel::ColumnType)(_selection & ItemMask)).x();
+ qreal left = item((ChatLineModel::ColumnType)(_selection & ItemMask))->pos().x();
QRectF selectRect(left, 0, width() - left, height());
painter->fillRect(selectRect, selFmt.background());
}
}
+ // draw chatitems
+ // the items draw themselves at the correct position
+ timestampItem()->paint(painter, option, widget);
+ senderItem()->paint(painter, option, widget);
+ contentsItem()->paint(painter, option, widget);
+
// new line marker
if(model_ && row() > 0 && chatScene()->isSingleBufferScene()) {
QModelIndex prevRowIdx = model_->index(row() - 1, 0);
BufferId bufferId = BufferId(chatScene()->idString().toInt());
MsgId lastSeenMsgId = Client::networkModel()->markerLineMsgId(bufferId);
if(lastSeenMsgId < myMsgId && lastSeenMsgId >= prevMsgId) {
- QLinearGradient gradient(0, 0, 0, contentsItem().fontMetrics()->lineSpacing());
+ QLinearGradient gradient(0, 0, 0, contentsItem()->fontMetrics()->lineSpacing());
gradient.setColorAt(0, QtUi::style()->brush(UiStyle::MarkerLine).color()); // FIXME: Use full (gradient?) brush instead of just the color
gradient.setColorAt(0.1, Qt::transparent);
painter->fillRect(boundingRect(), gradient);
}
}
}
+
+// We need to dispatch all mouse-related events to the appropriate (mouse grabbing) ChatItem
+
+ChatItem *ChatLine::mouseEventTargetItem(const QPointF &pos) {
+ if(mouseGrabberItem())
+ return mouseGrabberItem();
+ return itemAt(pos);
+}
+
+void ChatLine::mouseMoveEvent(QGraphicsSceneMouseEvent *event) {
+ ChatItem *item = mouseEventTargetItem(event->pos());
+ if(item)
+ item->mouseMoveEvent(event);
+}
+
+void ChatLine::mousePressEvent(QGraphicsSceneMouseEvent *event) {
+ ChatItem *item = mouseEventTargetItem(event->pos());
+ if(item)
+ item->mousePressEvent(event);
+}
+
+void ChatLine::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) {
+ ChatItem *item = mouseEventTargetItem(event->pos());
+ if(item)
+ item->mouseReleaseEvent(event);
+}
+
+void ChatLine::hoverEnterEvent(QGraphicsSceneHoverEvent *event) {
+ ChatItem *item = mouseEventTargetItem(event->pos());
+ if(item)
+ item->hoverEnterEvent(event);
+}
+
+void ChatLine::hoverLeaveEvent(QGraphicsSceneHoverEvent *event) {
+ ChatItem *item = mouseEventTargetItem(event->pos());
+ if(item)
+ item->hoverLeaveEvent(event);
+}
+
+void ChatLine::hoverMoveEvent(QGraphicsSceneHoverEvent *event) {
+ ChatItem *item = mouseEventTargetItem(event->pos());
+ if(item)
+ item->hoverMoveEvent(event);
+}
/***************************************************************************
- * 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 *
class ChatLine : public QGraphicsItem {
public:
ChatLine(int row, QAbstractItemModel *model,
- const qreal &width,
- const qreal ×tampWidth, const qreal &senderWidth, const qreal &contentsWidth,
- const QPointF &senderPos, const QPointF &contentsPos,
- QGraphicsItem *parent = 0);
+ const qreal &width,
+ const qreal ×tampWidth, const qreal &senderWidth, const qreal &contentsWidth,
+ const QPointF &senderPos, const QPointF &contentsPos,
+ QGraphicsItem *parent = 0);
virtual inline QRectF boundingRect () const { return QRectF(0, 0, _width, _height); }
inline qreal width() const { return _width; }
inline qreal height() const { return _height; }
- ChatItem &item(ChatLineModel::ColumnType);
- inline ChatItem ×tampItem() { return _timestampItem; }
- inline ChatItem &senderItem() { return _senderItem; }
- inline ContentsChatItem &contentsItem() { return _contentsItem; }
+ ChatItem *item(ChatLineModel::ColumnType);
+ ChatItem *itemAt(const QPointF &pos);
+ inline ChatItem *timestampItem() { return &_timestampItem; }
+ inline ChatItem *senderItem() { return &_senderItem; }
+ inline ContentsChatItem *contentsItem() { return &_contentsItem; }
virtual void paint (QPainter * painter, const QStyleOptionGraphicsItem *option, QWidget *widget = 0);
enum { Type = ChatScene::ChatLineType };
virtual inline int type() const { return Type; }
+ // pos is relative to the parent ChatLine
void setFirstColumn(const qreal ×tampWidth, const qreal &senderWidth, const QPointF &senderPos);
// setSecondColumn and setGeometryByWidth both also relocate the chatline.
// the _bottom_ position is passed via linePos. linePos is updated to the top of the chatLine.
void setSelected(bool selected, ChatLineModel::ColumnType minColumn = ChatLineModel::ContentsColumn);
void setHighlighted(bool highlighted);
+protected:
+ virtual bool sceneEvent(QEvent *event);
+
+ // These need to be relayed to the appropriate ChatItem
+ virtual void mouseMoveEvent(QGraphicsSceneMouseEvent *event);
+ virtual void mousePressEvent(QGraphicsSceneMouseEvent *event);
+ virtual void mouseReleaseEvent(QGraphicsSceneMouseEvent *event);
+ virtual void hoverEnterEvent(QGraphicsSceneHoverEvent *event);
+ virtual void hoverLeaveEvent(QGraphicsSceneHoverEvent *event);
+ virtual void hoverMoveEvent(QGraphicsSceneHoverEvent *event);
+
+ ChatItem *mouseEventTargetItem(const QPointF &pos);
+
+ inline ChatItem *mouseGrabberItem() const { return _mouseGrabberItem; }
+ void setMouseGrabberItem(ChatItem *item);
+
private:
int _row;
QAbstractItemModel *_model;
qreal _width, _height;
enum { ItemMask = 0x3f,
- Selected = 0x40,
- Highlighted = 0x80
+ Selected = 0x40,
+ Highlighted = 0x80
};
// _selection[1..0] ... Min Selected Column (See MessageModel::ColumnType)
// _selection[5..2] ... reserved for new column types
// _selection[6] ...... Selected
// _selection[7] ...... Highlighted
quint8 _selection; // save space, so we put both the col and the flags into one byte
+
+ ChatItem *_mouseGrabberItem;
};
#endif
/***************************************************************************
- * 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 *
}
ChatItem *ChatScene::chatItemAt(const QPointF &scenePos) const {
- QGraphicsItem *item = itemAt(scenePos);
- return dynamic_cast<ChatItem *>(item);
+ ChatLine *line = qgraphicsitem_cast<ChatLine*>(itemAt(scenePos));
+ if(line)
+ return line->itemAt(line->mapFromScene(scenePos));
+ return 0;
}
bool ChatScene::containsBuffer(const BufferId &id) const {
QString result;
for(int l = start; l <= end; l++) {
if(_selectionMinCol == ChatLineModel::TimestampColumn)
- result += _lines[l]->item(ChatLineModel::TimestampColumn).data(MessageModel::DisplayRole).toString() + " ";
+ result += _lines[l]->item(ChatLineModel::TimestampColumn)->data(MessageModel::DisplayRole).toString() + " ";
if(_selectionMinCol <= ChatLineModel::SenderColumn)
- result += _lines[l]->item(ChatLineModel::SenderColumn).data(MessageModel::DisplayRole).toString() + " ";
- result += _lines[l]->item(ChatLineModel::ContentsColumn).data(MessageModel::DisplayRole).toString() + "\n";
+ result += _lines[l]->item(ChatLineModel::SenderColumn)->data(MessageModel::DisplayRole).toString() + " ";
+ result += _lines[l]->item(ChatLineModel::ContentsColumn)->data(MessageModel::DisplayRole).toString() + "\n";
}
return result;
} else if(selectingItem())
}
int ChatScene::rowByScenePos(qreal y) const {
- // This is somewhat hacky... we look at the contents item that is at the given y position, since
- // it has the full height. From this item, we can then determine the row index and hence the ChatLine.
- // ChatItems cover their ChatLine, so we won't get to the latter directly.
- ChatItem *contentItem = static_cast<ChatItem *>(itemAt(QPointF(_secondColHandle->sceneRight() + 1, y)));
- if(!contentItem) return -1;
- return contentItem->row();
+ QList<QGraphicsItem*> itemList = items(QPointF(0, y));
+
+ // ChatLine should be at the bottom of the list
+ for(int i = itemList.count()-1; i >= 0; i--) {
+ ChatLine *line = qgraphicsitem_cast<ChatLine *>(itemList.at(i));
+ if(line)
+ return line->row();
+ }
+ return -1;
}
void ChatScene::updateSceneRect(qreal width) {
/***************************************************************************
- * 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 *
/***************************************************************************
- * 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 *
if(!_highlightItems.isEmpty()) {
if(!oldHighlightPos.isNull()) {
- int start = 0; int end = _highlightItems.count() - 1;
- QPointF startPos;
- QPointF endPos;
- while(1) {
- startPos = _highlightItems[start]->scenePos();
- endPos = _highlightItems[end]->scenePos();
- if(startPos == oldHighlightPos) {
- _currentHighlight = start;
- break;
- }
- if(endPos == oldHighlightPos) {
- _currentHighlight = end;
- break;
- }
- if(end - start == 1) {
- _currentHighlight = start;
- break;
- }
- int pivot = (end + start) / 2;
- QPointF pivotPos = _highlightItems[pivot]->scenePos();
- if(startPos.y() == endPos.y()) {
- if(oldHighlightPos.x() <= pivotPos.x())
- end = pivot;
- else
- start = pivot;
- } else {
- if(oldHighlightPos.y() <= pivotPos.y())
- end = pivot;
- else
- start = pivot;
- }
- }
+ int start = 0; int end = _highlightItems.count() - 1;
+ QPointF startPos;
+ QPointF endPos;
+ while(1) {
+ startPos = _highlightItems[start]->scenePos();
+ endPos = _highlightItems[end]->scenePos();
+ if(startPos == oldHighlightPos) {
+ _currentHighlight = start;
+ break;
+ }
+ if(endPos == oldHighlightPos) {
+ _currentHighlight = end;
+ break;
+ }
+ if(end - start == 1) {
+ _currentHighlight = start;
+ break;
+ }
+ int pivot = (end + start) / 2;
+ QPointF pivotPos = _highlightItems[pivot]->scenePos();
+ if(startPos.y() == endPos.y()) {
+ if(oldHighlightPos.x() <= pivotPos.x())
+ end = pivot;
+ else
+ start = pivot;
+ } else {
+ if(oldHighlightPos.y() <= pivotPos.y())
+ end = pivot;
+ else
+ start = pivot;
+ }
+ }
} else {
- _currentHighlight = _highlightItems.count() - 1;
+ _currentHighlight = _highlightItems.count() - 1;
}
_highlightItems[_currentHighlight]->setHighlighted(true);
emit newCurrentHighlight(_highlightItems[_currentHighlight]);
if(_searchOnlyRegularMsgs) {
index = model->index(row, 0);
if(!checkType((Message::Type)index.data(MessageModel::TypeRole).toInt()))
- continue;
+ continue;
}
highlightLine(_scene->chatLine(row));
}
void ChatViewSearchController::updateHighlights(ChatLine *line) {
QList<ChatItem *> checkItems;
if(_searchSenders)
- checkItems << &(line->item(MessageModel::SenderColumn));
+ checkItems << line->item(MessageModel::SenderColumn);
if(_searchMsgs)
- checkItems << &(line->item(MessageModel::ContentsColumn));
+ checkItems << line->item(MessageModel::ContentsColumn);
QHash<quint64, QHash<quint64, QRectF> > wordRects;
foreach(ChatItem *item, checkItems) {
deleteAll = true;
}
-
+
foreach(QGraphicsItem *child, line->childItems()) {
SearchHighlightItem *highlightItem = qgraphicsitem_cast<SearchHighlightItem *>(child);
if(!highlightItem)
} else {
int pos = _highlightItems.indexOf(highlightItem);
if(pos == _currentHighlight) {
- highlightPrev();
+ highlightPrev();
} else if (pos < _currentHighlight) {
- _currentHighlight--;
+ _currentHighlight--;
}
_highlightItems.removeAt(pos);
void ChatViewSearchController::highlightLine(ChatLine *line) {
QList<ChatItem *> checkItems;
if(_searchSenders)
- checkItems << &(line->item(MessageModel::SenderColumn));
+ checkItems << line->item(MessageModel::SenderColumn);
if(_searchMsgs)
- checkItems << &(line->item(MessageModel::ContentsColumn));
+ checkItems << line->item(MessageModel::ContentsColumn);
foreach(ChatItem *item, checkItems) {
foreach(QRectF wordRect, item->findWords(searchString(), caseSensitive())) {
QList<QPointF> wordPos;
if(_searchSenders) {
- foreach(QRectF wordRect, line->senderItem().findWords(searchString(), caseSensitive())) {
- wordPos << QPointF(wordRect.x() + line->senderItem().x(), wordRect.y());
+ foreach(QRectF wordRect, line->senderItem()->findWords(searchString(), caseSensitive())) {
+ wordPos << QPointF(wordRect.x() + line->senderItem()->x(), wordRect.y());
}
}
if(_searchMsgs) {
- foreach(QRectF wordRect, line->contentsItem().findWords(searchString(), caseSensitive())) {
- wordPos << QPointF(wordRect.x() + line->contentsItem().x(), wordRect.y());
+ foreach(QRectF wordRect, line->contentsItem()->findWords(searchString(), caseSensitive())) {
+ wordPos << QPointF(wordRect.x() + line->contentsItem()->x(), wordRect.y());
}
}
: QObject(),
QGraphicsItem(parent),
_highlighted(false),
- _alpha(100),
+ _alpha(70),
_timeLine(150)
{
setPos(wordRect.x(), wordRect.y());
}
void SearchHighlightItem::updateHighlight(qreal value) {
- _alpha = 100 + (int)(155 * value);
+ _alpha = 70 + (int)(80 * value);
update();
}
Q_UNUSED(option);
Q_UNUSED(widget);
- painter->setPen(QPen(QColor(0, 0, 0, _alpha), 1.5));
+ painter->setPen(QPen(QColor(0, 0, 0), 1.5));
painter->setBrush(QColor(254, 237, 45, _alpha));
painter->setRenderHints(QPainter::Antialiasing);
qreal radius = boundingRect().height() * 0.30;