fixing crashes due to missing private data of chatitems
authorMarcus Eggenberger <egs@quassel-irc.org>
Sat, 4 Oct 2008 19:13:55 +0000 (21:13 +0200)
committerMarcus Eggenberger <egs@quassel-irc.org>
Sat, 4 Oct 2008 19:13:55 +0000 (21:13 +0200)
src/qtui/chatitem.cpp
src/qtui/chatitem.h

index e0ab019..e72cbdc 100644 (file)
@@ -57,7 +57,7 @@ QVariant ChatItem::data(int role) const {
   return model()->data(index, role);
 }
 
   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;
   QTextLayout *layout = new QTextLayout(data(MessageModel::DisplayRole).toString());
 
   QTextOption option;
@@ -71,10 +71,7 @@ QTextLayout *ChatItem::createLayout(QTextOption::WrapMode wrapMode, Qt::Alignmen
   return layout;
 }
 
   return layout;
 }
 
-void ChatItem::updateLayout() {
-  if(!privateData()) {
-    setPrivateData(new ChatItemPrivate(createLayout()));
-  }
+void ChatItem::doLayout() {
   QTextLayout *layout_ = layout();
   layout_->beginLayout();
   QTextLine line = layout_->createLine();
   QTextLayout *layout_ = layout();
   layout_->beginLayout();
   QTextLine line = layout_->createLine();
@@ -90,12 +87,19 @@ void ChatItem::clearLayout() {
   _data = 0;
 }
 
   _data = 0;
 }
 
+ChatItemPrivate *ChatItem::privateData() const {
+  if(!_data) {
+    ChatItem *that = const_cast<ChatItem *>(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);
 // 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();
   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;
 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()) {
   for(int l = layout()->lineCount() - 1; l >= 0; l--) {
     QTextLine line = layout()->lineAt(l);
     if(pos.y() >= line.y()) {
@@ -192,9 +194,7 @@ QList<QRectF> ChatItem::findWords(const QString &searchWord, Qt::CaseSensitivity
     searchIdx = plainText.indexOf(searchWord, searchIdx + 1, caseSensitive);
   }
 
     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);
 
   foreach(int idx, indexList) {
     QTextLine line = layout()->lineForTextPosition(idx);
@@ -205,7 +205,7 @@ QList<QRectF> ChatItem::findWords(const QString &searchWord, Qt::CaseSensitivity
     resultList << QRectF(x, y, width, height);
   }
 
     resultList << QRectF(x, y, width, height);
   }
 
-  if(!hadLayout)
+  if(!hadPrivateData)
     clearLayout();
   return resultList;
 }
     clearLayout();
   return resultList;
 }
@@ -283,13 +283,7 @@ qreal ContentsChatItem::setGeometryByWidth(qreal w) {
   return height();
 }
 
   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<ChatLineModel::WrapList>();
   if(!wrapList.count()) return; // empty chatitem
 
   ChatLineModel::WrapList wrapList = data(ChatLineModel::WrapListRole).value<ChatLineModel::WrapList>();
   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)
 
 // NOTE: This method is not threadsafe and not reentrant!
 //       (RegExps are not constant while matching, and they are static here for efficiency)
-QList<ContentsChatItem::Clickable> ContentsChatItem::findClickables() {
+QList<ContentsChatItem::Clickable> ContentsChatItem::findClickables() const {
   // For matching URLs
   static QString urlEnd("(?:>|[,.;:\"]*\\s|\\b|$)");
   static QString urlChars("(?:[\\w\\-~@/?&=+$()!%#]|[,.;:]\\w)");
   // For matching URLs
   static QString urlEnd("(?:>|[,.;:\"]*\\s|\\b|$)");
   static QString urlChars("(?:[\\w\\-~@/?&=+$()!%#]|[,.;:]\\w)");
@@ -387,7 +381,7 @@ QVector<QTextLayout::FormatRange> ContentsChatItem::additionalFormats() const {
 }
 
 void ContentsChatItem::endHoverMode() {
 }
 
 void ContentsChatItem::endHoverMode() {
-  if(hasLayout()) {
+  if(hasPrivateData()) {
     if(privateData()->currentClickable.isValid()) {
       setCursor(Qt::ArrowCursor);
       privateData()->currentClickable = Clickable();
     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
   // 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);
     && (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
 
 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;
   QTextLine line = layout()->lineForTextPosition(click.start);
   qreal x = line.cursorToX(click.start);
   qreal width = line.cursorToX(click.start + click.length) - x;
index b8b6e14..51b59f9 100644 (file)
@@ -47,10 +47,8 @@ public:
   inline qreal width() const { return _boundingRect.width(); }
   inline qreal height() const { return _boundingRect.height(); }
 
   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);
   void clearLayout();
 
   virtual void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = 0);
@@ -74,8 +72,9 @@ protected:
   virtual inline QVector<QTextLayout::FormatRange> additionalFormats() const { return QVector<QTextLayout::FormatRange>(); }
   qint16 posToCursor(const QPointF &pos);
 
   virtual inline QVector<QTextLayout::FormatRange> additionalFormats() const { return QVector<QTextLayout::FormatRange>(); }
   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()
 
   // 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; }
 
 // 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
 
 // ************************************************************
 // 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; }
 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<QTextLayout::FormatRange> additionalFormats() const;
 
 
   virtual QVector<QTextLayout::FormatRange> additionalFormats() const;
 
-  virtual void updateLayout();
+  virtual void doLayout();
+  virtual inline ChatItemPrivate *newPrivateData();
 
 private:
   struct Clickable;
 
 private:
   struct Clickable;
@@ -165,7 +167,7 @@ private:
 
   inline ContentsChatItemPrivate *privateData() const;
 
 
   inline ContentsChatItemPrivate *privateData() const;
 
-  QList<Clickable> findClickables();
+  QList<Clickable> findClickables() const;
   void endHoverMode();
   void showWebPreview(const Clickable &click);
   void clearWebPreview();
   void endHoverMode();
   void showWebPreview(const Clickable &click);
   void clearWebPreview();
@@ -208,8 +210,8 @@ struct ContentsChatItemPrivate : ChatItemPrivate {
   ContentsChatItemPrivate(QTextLayout *l, const QList<ContentsChatItem::Clickable> &c, ContentsChatItem *parent) : ChatItemPrivate(l), contentsItem(parent), clickables(c), hasDragged(false) {}
 };
 
   ContentsChatItemPrivate(QTextLayout *l, const QList<ContentsChatItem::Clickable> &c, ContentsChatItem *parent) : ChatItemPrivate(l), contentsItem(parent), clickables(c), hasDragged(false) {}
 };
 
-
 //inlines regarding ContentsChatItemPrivate
 //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 {
 ContentsChatItemPrivate *ContentsChatItem::privateData() const { return (ContentsChatItemPrivate *)ChatItem::privateData(); }
 
 class ContentsChatItem::WrapColumnFinder {