Inter- and intra-item selections now behave properly, except deactivating a global...
authorManuel Nickschas <sputnick@quassel-irc.org>
Thu, 31 Jul 2008 16:37:19 +0000 (18:37 +0200)
committerManuel Nickschas <sputnick@quassel-irc.org>
Sat, 2 Aug 2008 13:17:11 +0000 (15:17 +0200)
src/qtui/chatitem.cpp
src/qtui/chatitem.h
src/qtui/chatscene.cpp
src/qtui/chatscene.h

index 5d1bcb7..286fd62 100644 (file)
@@ -36,6 +36,8 @@ ChatItem::ChatItem(const QPersistentModelIndex &index_, QGraphicsItem *parent) :
   _lines = 0;
   _selectionStart = -1;
   _selectionMode = NoSelection;
+  setAcceptHoverEvents(true);
+  setZValue(20);
 }
 
 ChatItem::~ChatItem() {
@@ -138,11 +140,11 @@ void ChatItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option,
   Q_UNUSED(option); Q_UNUSED(widget);
   if(!haveLayout()) updateLayout();
   painter->setClipRect(boundingRect()); // no idea why QGraphicsItem clipping won't work
-  if(_selectionMode == FullSelection) {
-    painter->save();
-   painter->fillRect(boundingRect(), QApplication::palette().brush(QPalette::Highlight));
-    painter->restore();
-  } // TODO: add selection format here
+  //if(_selectionMode == FullSelection) {
+    //painter->save();
+    //painter->fillRect(boundingRect(), QApplication::palette().brush(QPalette::Highlight));
+    //painter->restore();
+  //}
   QVector<QTextLayout::FormatRange> formats;
   if(_selectionMode != NoSelection) {
     QTextLayout::FormatRange selectFmt;
@@ -174,12 +176,22 @@ qint16 ChatItem::posToCursor(const QPointF &pos) {
 }
 
 void ChatItem::setFullSelection() {
-  _selectionMode = FullSelection;
-  update();
+  if(_selectionMode != FullSelection) {
+    _selectionMode = FullSelection;
+    update();
+  }
 }
 
 void ChatItem::clearSelection() {
-  _selectionMode = NoSelection;
+  if(_selectionMode != NoSelection) {
+    _selectionMode = NoSelection;
+    update();
+  }
+}
+
+void ChatItem::continueSelecting(const QPointF &pos) {
+  _selectionMode = PartialSelection;
+  _selectionEnd = posToCursor(pos);
   update();
 }
 
@@ -196,12 +208,14 @@ void ChatItem::mousePressEvent(QGraphicsSceneMouseEvent *event) {
 
 void ChatItem::mouseMoveEvent(QGraphicsSceneMouseEvent *event) {
   if(contains(event->pos())) {
-    _selectionEnd = posToCursor(event->pos());
-    update();
+    qint16 end = posToCursor(event->pos());
+    if(end != _selectionEnd) {
+      _selectionEnd = end;
+      update();
+    }
   } else {
     setFullSelection();
-    ungrabMouse();
-    chatScene()->startGlobalSelection(this);
+    chatScene()->startGlobalSelection(this, event->pos());
   }
 }
 
@@ -217,6 +231,22 @@ void ChatItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) {
   }
 }
 
+void ChatItem::hoverEnterEvent(QGraphicsSceneHoverEvent *event) {
+  //qDebug() << (void*)this << "entering";
+
+}
+
+void ChatItem::hoverLeaveEvent(QGraphicsSceneHoverEvent *event) {
+  //qDebug() << (void*)this << "leaving";
+
+}
+
+void ChatItem::hoverMoveEvent(QGraphicsSceneHoverEvent *event) {
+  //qDebug() << (void*)this << event->pos();
+
+}
+
+
 /*************************************************************************************************/
 
 ChatItem::WrapColumnFinder::WrapColumnFinder(ChatItem *_item) : item(_item) {
index 3cf5a9b..ddbffdb 100644 (file)
@@ -60,13 +60,17 @@ class ChatItem : public QGraphicsItem {
     // selection stuff, to be called by the scene
     void clearSelection();
     void setFullSelection();
-    void continueSelecting();
+    void continueSelecting(const QPointF &pos);
 
   protected:
     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);
+
   private:
     qint16 posToCursor(const QPointF &pos);
     qreal computeHeight();
index f6a5e33..797290d 100644 (file)
@@ -38,6 +38,7 @@ ChatScene::ChatScene(QAbstractItemModel *model, const QString &idString, QObject
 {
   _width = 0;
   _selectingItem = 0;
+  _isSelecting = false;
   connect(this, SIGNAL(sceneRectChanged(const QRectF &)), this, SLOT(rectChanged(const QRectF &)));
 
   connect(model, SIGNAL(rowsInserted(const QModelIndex &, int, int)), this, SLOT(rowsInserted(const QModelIndex &, int, int)));
@@ -141,28 +142,85 @@ void ChatScene::handlePositionChanged(qreal xpos) {
   update(qMin(oldx, xpos) - firstColHandle->width()/2, 0, qMax(oldx, xpos) + firstColHandle->width()/2, height());
 }
 
-void ChatScene::mouseMoveEvent(QGraphicsSceneMouseEvent *event) {
-
-  QGraphicsScene::mouseMoveEvent(event);
+void ChatScene::setSelectingItem(ChatItem *item) {
+  if(_selectingItem) _selectingItem->clearSelection();
+  _selectingItem = item;
 }
 
-void ChatScene::mousePressEvent(QGraphicsSceneMouseEvent *event) {
-
-  QGraphicsScene::mousePressEvent(event);
+void ChatScene::startGlobalSelection(ChatItem *item, const QPointF &itemPos) {
+  _selectionStart = _selectionEnd = item->index().row();
+  _selectionStartCol = _selectionMinCol = item->index().column();
+  _isSelecting = true;
+  _lines[_selectionStart]->setSelected(true, (ChatLineModel::ColumnType)_selectionMinCol);
+  updateSelection(item->mapToScene(itemPos));
 }
 
-void ChatScene::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) {
+void ChatScene::updateSelection(const QPointF &pos) {
+  // This is somewhat hacky... we look at the contents item that is at the cursor's y position (ignoring x), since
+  // it has the full height. From this item, we can then determine the row index and hence the ChatLine.
+  ChatItem *contentItem = static_cast<ChatItem *>(itemAt(QPointF(secondColHandlePos + secondColHandle->width()/2, pos.y())));
+  if(!contentItem) return;
+
+  int curRow = contentItem->index().row();
+  int curColumn;
+  if(pos.x() > secondColHandlePos + secondColHandle->width()/2) curColumn = ChatLineModel::ContentsColumn;
+  else if(pos.x() > firstColHandlePos) curColumn = ChatLineModel::SenderColumn;
+  else curColumn = ChatLineModel::TimestampColumn;
+
+  ChatLineModel::ColumnType minColumn = (ChatLineModel::ColumnType)qMin(curColumn, _selectionStartCol);
+  if(minColumn != _selectionMinCol) {
+    _selectionMinCol = minColumn;
+    for(int l = qMin(_selectionStart, _selectionEnd); l <= qMax(_selectionStart, _selectionEnd); l++) {
+      _lines[l]->setSelected(true, minColumn);
+    }
+  }
 
-  QGraphicsScene::mouseReleaseEvent(event);
-}
+  if(curRow > _selectionEnd && curRow > _selectionStart) {  // select further towards bottom
+    for(int l = _selectionEnd + 1; l <= curRow; l++) {
+      _lines[l]->setSelected(true, minColumn);
+    }
+  } else if(curRow > _selectionEnd && curRow <= _selectionStart) { // deselect towards bottom
+    for(int l = _selectionEnd; l < curRow; l++) {
+      _lines[l]->setSelected(false);
+    }
+  } else if(curRow < _selectionEnd && curRow >= _selectionStart) {
+    for(int l = _selectionEnd; l > curRow; l--) {
+      _lines[l]->setSelected(false);
+    }
+  } else if(curRow < _selectionEnd && curRow < _selectionStart) {
+    for(int l = _selectionEnd - 1; l >= curRow; l--) {
+      _lines[l]->setSelected(true, minColumn);
+    }
+  }
+  _selectionEnd = curRow;
 
-void ChatScene::setSelectingItem(ChatItem *item) {
-  if(_selectingItem) _selectingItem->clearSelection();
-  _selectingItem = item;
+  if(curRow == _selectionStart && minColumn == ChatLineModel::ContentsColumn) {
+    _lines[curRow]->setSelected(false);
+    _isSelecting = false;
+    _selectingItem->continueSelecting(_selectingItem->mapFromScene(pos));
+  }
 }
 
-void ChatScene::startGlobalSelection(ChatItem *item) {
+void ChatScene::mouseMoveEvent(QGraphicsSceneMouseEvent *event) {
+  if(_isSelecting && event->buttons() & Qt::LeftButton) {
+    updateSelection(event->scenePos());
+    event->accept();
+  } else {
+    QGraphicsScene::mouseMoveEvent(event);
+  }
+}
 
+void ChatScene::mousePressEvent(QGraphicsSceneMouseEvent *event) {
+  qDebug() << "pressed";
+  QGraphicsScene::mousePressEvent(event);
+}
 
+void ChatScene::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) {
+  if(_isSelecting) {
+    _isSelecting = false;
+    event->accept();
+  } else {
+    QGraphicsScene::mouseReleaseEvent(event);
+  }
 }
 
index 7bfd6eb..62a65f2 100644 (file)
@@ -23,6 +23,7 @@
 
 #include <QAbstractItemModel>
 #include <QGraphicsScene>
+#include <QSet>
 
 class AbstractUiMsg;
 class Buffer;
@@ -45,10 +46,10 @@ class ChatScene : public QGraphicsScene {
   public slots:
     void setWidth(qreal);
 
-    // these are used by the chatitems to notify the scene
+    // these are used by the chatitems to notify the scene and manage selections
     void setSelectingItem(ChatItem *item);
     ChatItem *selectingItem() const { return _selectingItem; }
-    void startGlobalSelection(ChatItem *item);
+    void startGlobalSelection(ChatItem *item, const QPointF &itemPos);
 
   signals:
     void heightChanged(qreal height);
@@ -66,6 +67,8 @@ class ChatScene : public QGraphicsScene {
     void handlePositionChanged(qreal xpos);
 
   private:
+    void updateSelection(const QPointF &pos);
+
     QString _idString;
     qreal _width, _height;
     QAbstractItemModel *_model;
@@ -74,7 +77,12 @@ class ChatScene : public QGraphicsScene {
     ColumnHandleItem *firstColHandle, *secondColHandle;
     qreal firstColHandlePos, secondColHandlePos;
 
-    ChatItem *_selectingItem;
+    ChatItem *_selectingItem, *_lastItem;
+    QSet<ChatLine *> _selectedItems;
+    int _selectionStartCol, _selectionMinCol;
+    int _selectionStart;
+    int _selectionEnd;
+    bool _isSelecting;
 };
 
 #endif