From: Manuel Nickschas Date: Fri, 29 Jan 2010 13:46:56 +0000 (+0100) Subject: ChatItems are no longer QGraphicsItems X-Git-Tag: 0.6-beta1~65 X-Git-Url: https://git.quassel-irc.org/?p=quassel.git;a=commitdiff_plain;h=6330f7fe3d19113cbf29944a9b6e8b503893d4a9;ds=sidebyside ChatItems are no longer QGraphicsItems 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. --- diff --git a/src/qtui/chatitem.cpp b/src/qtui/chatitem.cpp index 6add4465..4855f0c1 100644 --- a/src/qtui/chatitem.cpp +++ b/src/qtui/chatitem.cpp @@ -1,5 +1,5 @@ /*************************************************************************** - * 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 * @@ -39,23 +39,13 @@ #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(parentItem())->model(); -} -int ChatItem::row() const { - return static_cast(parentItem())->row(); } QVariant ChatItem::data(int role) const { @@ -67,7 +57,8 @@ 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; @@ -112,8 +103,6 @@ void ChatItem::doLayout(QTextLayout *layout) const { } 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); @@ -131,7 +120,7 @@ void ChatItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, 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()); @@ -238,27 +227,27 @@ void ChatItem::setSelection(SelectionMode mode, qint16 start, qint16 end) { _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 { @@ -305,18 +294,18 @@ void ChatItem::handleClick(const QPointF &pos, ChatScene::ClickMode clickMode) { 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(); @@ -390,11 +379,11 @@ void SenderChatItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *op 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()); } } @@ -404,10 +393,11 @@ void SenderChatItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *op 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); } @@ -440,10 +430,9 @@ qreal ContentsChatItem::setGeometryByWidth(qreal w) { delete _data; _data = 0; - if(w != width() || h != height()) { - prepareGeometryChange(); + if(w != width() || h != height()) setGeometry(w, h); - } + return h; } @@ -517,11 +506,11 @@ QVector ContentsChatItem::additionalFormats() const { void ContentsChatItem::endHoverMode() { if(privateData()) { if(privateData()->currentClickable.isValid()) { - setCursor(Qt::ArrowCursor); + chatLine()->setCursor(Qt::ArrowCursor); privateData()->currentClickable = Clickable(); } clearWebPreview(); - update(); + chatLine()->update(); } } @@ -551,7 +540,7 @@ void ContentsChatItem::handleClick(const QPointF &pos, ChatScene::ClickMode clic setSelectionStart(start); setSelectionEnd(end); } - update(); + chatLine()->update(); } else if(clickMode == ChatScene::TripleClick) { setSelection(PartialSelection, 0, data(ChatLineModel::DisplayRole).toString().length()); } @@ -584,9 +573,9 @@ void ContentsChatItem::hoverMoveEvent(QGraphicsSceneHoverEvent *event) { onClickable = true; } if(onClickable) { - setCursor(Qt::PointingHandCursor); + chatLine()->setCursor(Qt::PointingHandCursor); privateData()->currentClickable = click; - update(); + chatLine()->update(); return; } } @@ -644,7 +633,7 @@ void ContentsChatItem::showWebPreview(const Clickable &click) { 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()); diff --git a/src/qtui/chatitem.h b/src/qtui/chatitem.h index 36fb1c1e..0e6a3ded 100644 --- a/src/qtui/chatitem.h +++ b/src/qtui/chatitem.h @@ -1,5 +1,5 @@ /*************************************************************************** - * 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 * @@ -22,7 +22,6 @@ #define CHATITEM_H_ #include -#include #include #include "chatlinemodel.h" @@ -33,19 +32,36 @@ #include -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(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 { @@ -56,8 +72,7 @@ public: 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; @@ -84,6 +99,9 @@ protected: 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 selectionFormats() const; @@ -100,29 +118,21 @@ protected: 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; }; @@ -133,9 +143,8 @@ private: //! 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; } }; @@ -145,13 +154,12 @@ public: //! 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); @@ -168,11 +176,10 @@ class ContentsChatItem : public ChatItem { 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; @@ -268,4 +275,26 @@ private: /*************************************************************************************************/ +#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 diff --git a/src/qtui/chatline.cpp b/src/qtui/chatline.cpp index 38e78c69..6032cbef 100644 --- a/src/qtui/chatline.cpp +++ b/src/qtui/chatline.cpp @@ -1,5 +1,5 @@ /*************************************************************************** - * 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 * @@ -27,6 +27,7 @@ #include "client.h" #include "chatitem.h" #include "chatline.h" +#include "chatview.h" #include "columnhandleitem.h" #include "messagemodel.h" #include "networkmodel.h" @@ -35,63 +36,81 @@ #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) @@ -103,14 +122,13 @@ void ChatLine::setSecondColumn(const qreal &senderWidth, const qreal &contentsWi } 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); } @@ -129,9 +147,9 @@ void ChatLine::setSelected(bool selected, ChatLineModel::ColumnType minColumn) { 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 { @@ -139,7 +157,7 @@ void ChatLine::setSelected(bool selected, ChatLineModel::ColumnType minColumn) { if(sel != _selection) { _selection = sel; for(int i = 0; i <= ChatLineModel::ContentsColumn; i++) - item((ChatLineModel::ColumnType)i).clearSelection(); + item((ChatLineModel::ColumnType)i)->clearSelection(); update(); } } @@ -168,12 +186,18 @@ void ChatLine::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, 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); @@ -186,7 +210,7 @@ void ChatLine::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, 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); @@ -194,3 +218,47 @@ void ChatLine::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, } } } + +// 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); +} diff --git a/src/qtui/chatline.h b/src/qtui/chatline.h index 6fc21c75..24947d0a 100644 --- a/src/qtui/chatline.h +++ b/src/qtui/chatline.h @@ -1,5 +1,5 @@ /*************************************************************************** - * 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 * @@ -30,10 +30,10 @@ 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); } @@ -46,15 +46,17 @@ public: 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. @@ -64,6 +66,22 @@ public: 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; @@ -73,14 +91,16 @@ private: 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 diff --git a/src/qtui/chatscene.cpp b/src/qtui/chatscene.cpp index b748e1a8..ed90582c 100644 --- a/src/qtui/chatscene.cpp +++ b/src/qtui/chatscene.cpp @@ -1,5 +1,5 @@ /*************************************************************************** - * 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 * @@ -138,8 +138,10 @@ ColumnHandleItem *ChatScene::secondColumnHandle() const { } ChatItem *ChatScene::chatItemAt(const QPointF &scenePos) const { - QGraphicsItem *item = itemAt(scenePos); - return dynamic_cast(item); + ChatLine *line = qgraphicsitem_cast(itemAt(scenePos)); + if(line) + return line->itemAt(line->mapFromScene(scenePos)); + return 0; } bool ChatScene::containsBuffer(const BufferId &id) const { @@ -759,10 +761,10 @@ QString ChatScene::selection() 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()) @@ -816,12 +818,15 @@ ChatLineModel::ColumnType ChatScene::columnByScenePos(qreal x) const { } 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(itemAt(QPointF(_secondColHandle->sceneRight() + 1, y))); - if(!contentItem) return -1; - return contentItem->row(); + QList 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(itemList.at(i)); + if(line) + return line->row(); + } + return -1; } void ChatScene::updateSceneRect(qreal width) { diff --git a/src/qtui/chatscene.h b/src/qtui/chatscene.h index 7bf5be3a..f3d62734 100644 --- a/src/qtui/chatscene.h +++ b/src/qtui/chatscene.h @@ -1,5 +1,5 @@ /*************************************************************************** - * 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 * diff --git a/src/qtui/chatviewsearchcontroller.cpp b/src/qtui/chatviewsearchcontroller.cpp index add7478b..671ac72c 100644 --- a/src/qtui/chatviewsearchcontroller.cpp +++ b/src/qtui/chatviewsearchcontroller.cpp @@ -1,5 +1,5 @@ /*************************************************************************** - * 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 * @@ -134,40 +134,40 @@ void ChatViewSearchController::updateHighlights(bool reuse) { 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]); @@ -190,7 +190,7 @@ void ChatViewSearchController::checkMessagesForHighlight(int start, int end) { if(_searchOnlyRegularMsgs) { index = model->index(row, 0); if(!checkType((Message::Type)index.data(MessageModel::TypeRole).toInt())) - continue; + continue; } highlightLine(_scene->chatLine(row)); } @@ -199,10 +199,10 @@ void ChatViewSearchController::checkMessagesForHighlight(int start, int end) { void ChatViewSearchController::updateHighlights(ChatLine *line) { QList 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 > wordRects; foreach(ChatItem *item, checkItems) { @@ -220,7 +220,7 @@ void ChatViewSearchController::updateHighlights(ChatLine *line) { deleteAll = true; } - + foreach(QGraphicsItem *child, line->childItems()) { SearchHighlightItem *highlightItem = qgraphicsitem_cast(child); if(!highlightItem) @@ -232,9 +232,9 @@ void ChatViewSearchController::updateHighlights(ChatLine *line) { } else { int pos = _highlightItems.indexOf(highlightItem); if(pos == _currentHighlight) { - highlightPrev(); + highlightPrev(); } else if (pos < _currentHighlight) { - _currentHighlight--; + _currentHighlight--; } _highlightItems.removeAt(pos); @@ -246,10 +246,10 @@ void ChatViewSearchController::updateHighlights(ChatLine *line) { void ChatViewSearchController::highlightLine(ChatLine *line) { QList 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())) { @@ -284,13 +284,13 @@ void ChatViewSearchController::repositionHighlights(ChatLine *line) { QList 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()); } } @@ -361,7 +361,7 @@ SearchHighlightItem::SearchHighlightItem(QRectF wordRect, QGraphicsItem *parent) : QObject(), QGraphicsItem(parent), _highlighted(false), - _alpha(100), + _alpha(70), _timeLine(150) { setPos(wordRect.x(), wordRect.y()); @@ -385,7 +385,7 @@ void SearchHighlightItem::setHighlighted(bool highlighted) { } void SearchHighlightItem::updateHighlight(qreal value) { - _alpha = 100 + (int)(155 * value); + _alpha = 70 + (int)(80 * value); update(); } @@ -393,7 +393,7 @@ void SearchHighlightItem::paint(QPainter *painter, const QStyleOptionGraphicsIte 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;