From 2e023191fb47f5cbd186f7274f8ee1b5d1cc94c3 Mon Sep 17 00:00:00 2001 From: Marcus Eggenberger Date: Sat, 4 Oct 2008 21:13:55 +0200 Subject: [PATCH] fixing crashes due to missing private data of chatitems --- src/qtui/chatitem.cpp | 43 +++++++++++++++++-------------------------- src/qtui/chatitem.h | 24 +++++++++++++----------- 2 files changed, 30 insertions(+), 37 deletions(-) diff --git a/src/qtui/chatitem.cpp b/src/qtui/chatitem.cpp index e0ab0194..e72cbdc5 100644 --- a/src/qtui/chatitem.cpp +++ b/src/qtui/chatitem.cpp @@ -57,7 +57,7 @@ QVariant ChatItem::data(int role) const { return model()->data(index, role); } -QTextLayout *ChatItem::createLayout(QTextOption::WrapMode wrapMode, Qt::Alignment alignment) { +QTextLayout *ChatItem::createLayout(QTextOption::WrapMode wrapMode, Qt::Alignment alignment) const { QTextLayout *layout = new QTextLayout(data(MessageModel::DisplayRole).toString()); QTextOption option; @@ -71,10 +71,7 @@ QTextLayout *ChatItem::createLayout(QTextOption::WrapMode wrapMode, Qt::Alignmen return layout; } -void ChatItem::updateLayout() { - if(!privateData()) { - setPrivateData(new ChatItemPrivate(createLayout())); - } +void ChatItem::doLayout() { QTextLayout *layout_ = layout(); layout_->beginLayout(); QTextLine line = layout_->createLine(); @@ -90,12 +87,19 @@ void ChatItem::clearLayout() { _data = 0; } +ChatItemPrivate *ChatItem::privateData() const { + if(!_data) { + ChatItem *that = const_cast(this); + that->_data = that->newPrivateData(); + that->doLayout(); + } + return _data; +} + // NOTE: This is not the most time-efficient implementation, but it saves space by not caching unnecessary data // This is a deliberate trade-off. (-> selectFmt creation, data() call) void ChatItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) { Q_UNUSED(option); Q_UNUSED(widget); - if(!hasLayout()) - updateLayout(); painter->setClipRect(boundingRect()); // no idea why QGraphicsItem clipping won't work //if(_selectionMode == FullSelection) { //painter->save(); @@ -149,8 +153,6 @@ void ChatItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, qint16 ChatItem::posToCursor(const QPointF &pos) { if(pos.y() > height()) return data(MessageModel::DisplayRole).toString().length(); if(pos.y() < 0) return 0; - if(!hasLayout()) - updateLayout(); for(int l = layout()->lineCount() - 1; l >= 0; l--) { QTextLine line = layout()->lineAt(l); if(pos.y() >= line.y()) { @@ -192,9 +194,7 @@ QList ChatItem::findWords(const QString &searchWord, Qt::CaseSensitivity searchIdx = plainText.indexOf(searchWord, searchIdx + 1, caseSensitive); } - bool hadLayout = hasLayout(); - if(!hadLayout) - updateLayout(); + bool hadPrivateData = hasPrivateData(); foreach(int idx, indexList) { QTextLine line = layout()->lineForTextPosition(idx); @@ -205,7 +205,7 @@ QList ChatItem::findWords(const QString &searchWord, Qt::CaseSensitivity resultList << QRectF(x, y, width, height); } - if(!hadLayout) + if(!hadPrivateData) clearLayout(); return resultList; } @@ -283,13 +283,7 @@ qreal ContentsChatItem::setGeometryByWidth(qreal w) { return height(); } -void ContentsChatItem::updateLayout() { - if(!privateData()) { - ContentsChatItemPrivate *data = new ContentsChatItemPrivate(createLayout(QTextOption::WrapAnywhere), findClickables(), this); - setPrivateData(data); - } - - // Now layout +void ContentsChatItem::doLayout() { ChatLineModel::WrapList wrapList = data(ChatLineModel::WrapListRole).value(); if(!wrapList.count()) return; // empty chatitem @@ -311,7 +305,7 @@ void ContentsChatItem::updateLayout() { // NOTE: This method is not threadsafe and not reentrant! // (RegExps are not constant while matching, and they are static here for efficiency) -QList ContentsChatItem::findClickables() { +QList ContentsChatItem::findClickables() const { // For matching URLs static QString urlEnd("(?:>|[,.;:\"]*\\s|\\b|$)"); static QString urlChars("(?:[\\w\\-~@/?&=+$()!%#]|[,.;:]\\w)"); @@ -387,7 +381,7 @@ QVector ContentsChatItem::additionalFormats() const { } void ContentsChatItem::endHoverMode() { - if(hasLayout()) { + if(hasPrivateData()) { if(privateData()->currentClickable.isValid()) { setCursor(Qt::ArrowCursor); privateData()->currentClickable = Clickable(); @@ -429,7 +423,7 @@ void ContentsChatItem::mouseMoveEvent(QGraphicsSceneMouseEvent *event) { // mouse move events always mean we're not hovering anymore... endHoverMode(); // also, check if we have dragged the mouse - if(hasLayout() && !privateData()->hasDragged && event->buttons() & Qt::LeftButton + if(hasPrivateData() && !privateData()->hasDragged && event->buttons() & Qt::LeftButton && (event->buttonDownScreenPos(Qt::LeftButton) - event->screenPos()).manhattanLength() >= QApplication::startDragDistance()) privateData()->hasDragged = true; ChatItem::mouseMoveEvent(event); @@ -467,9 +461,6 @@ void ContentsChatItem::hoverMoveEvent(QGraphicsSceneHoverEvent *event) { void ContentsChatItem::showWebPreview(const Clickable &click) { #ifdef HAVE_WEBKIT - if(!hasLayout()) - updateLayout(); - QTextLine line = layout()->lineForTextPosition(click.start); qreal x = line.cursorToX(click.start); qreal width = line.cursorToX(click.start + click.length) - x; diff --git a/src/qtui/chatitem.h b/src/qtui/chatitem.h index b8b6e145..51b59f97 100644 --- a/src/qtui/chatitem.h +++ b/src/qtui/chatitem.h @@ -47,10 +47,8 @@ public: inline qreal width() const { return _boundingRect.width(); } inline qreal height() const { return _boundingRect.height(); } - inline bool hasLayout() const { return (bool)_data; } - QTextLayout *createLayout(QTextOption::WrapMode, Qt::Alignment = Qt::AlignLeft); - virtual inline QTextLayout *createLayout() { return createLayout(QTextOption::WrapAnywhere); } - virtual void updateLayout(); + QTextLayout *createLayout(QTextOption::WrapMode, Qt::Alignment = Qt::AlignLeft) const; + virtual void doLayout(); void clearLayout(); virtual void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = 0); @@ -74,8 +72,9 @@ protected: virtual inline QVector additionalFormats() const { return QVector(); } qint16 posToCursor(const QPointF &pos); - inline void setPrivateData(ChatItemPrivate *data) { Q_ASSERT(!_data); _data = data; } - inline ChatItemPrivate *privateData() const; + inline bool hasPrivateData() const { return (bool)_data; } + ChatItemPrivate *privateData() const; + virtual inline ChatItemPrivate *newPrivateData(); // WARNING: setGeometry and setHeight should not be used without either: // a) calling prepareGeometryChange() immediately before setColumns() @@ -111,7 +110,7 @@ struct ChatItemPrivate { // inlines of ChatItem QTextLayout *ChatItem::layout() const { return privateData()->layout; } -ChatItemPrivate *ChatItem::privateData() const { return _data; } +ChatItemPrivate *ChatItem::newPrivateData() { return new ChatItemPrivate(createLayout(QTextOption::WrapAnywhere)); } // ************************************************************ // TimestampChatItem @@ -132,7 +131,9 @@ class SenderChatItem : public ChatItem { public: SenderChatItem(const qreal &width, const qreal &height, const QPointF &pos, QGraphicsItem *parent) : ChatItem(width, height, pos, parent) {} virtual inline ChatLineModel::ColumnType column() const { return ChatLineModel::SenderColumn; } - virtual inline QTextLayout *createLayout() { return ChatItem::createLayout(QTextOption::WrapAnywhere, Qt::AlignRight); } + +protected: + virtual inline ChatItemPrivate *newPrivateData() { return new ChatItemPrivate(createLayout(QTextOption::WrapAnywhere, Qt::AlignRight)); } }; // ************************************************************ @@ -157,7 +158,8 @@ protected: virtual QVector additionalFormats() const; - virtual void updateLayout(); + virtual void doLayout(); + virtual inline ChatItemPrivate *newPrivateData(); private: struct Clickable; @@ -165,7 +167,7 @@ private: inline ContentsChatItemPrivate *privateData() const; - QList findClickables(); + QList findClickables() const; void endHoverMode(); void showWebPreview(const Clickable &click); void clearWebPreview(); @@ -208,8 +210,8 @@ struct ContentsChatItemPrivate : ChatItemPrivate { ContentsChatItemPrivate(QTextLayout *l, const QList &c, ContentsChatItem *parent) : ChatItemPrivate(l), contentsItem(parent), clickables(c), hasDragged(false) {} }; - //inlines regarding ContentsChatItemPrivate +ChatItemPrivate *ContentsChatItem::newPrivateData() { return new ContentsChatItemPrivate(createLayout(QTextOption::WrapAnywhere), findClickables(), this); } ContentsChatItemPrivate *ContentsChatItem::privateData() const { return (ContentsChatItemPrivate *)ChatItem::privateData(); } class ContentsChatItem::WrapColumnFinder { -- 2.20.1