Make intra-item selections work
authorManuel Nickschas <sputnick@quassel-irc.org>
Mon, 28 Jul 2008 20:23:50 +0000 (22:23 +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 235868d..7d52c5a 100644 (file)
  *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
  ***************************************************************************/
 
  *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
  ***************************************************************************/
 
+#include <QApplication>
+#include <QClipboard>
 #include <QFontMetrics>
 #include <QGraphicsSceneMouseEvent>
 #include <QPainter>
 #include <QFontMetrics>
 #include <QGraphicsSceneMouseEvent>
 #include <QPainter>
+#include <QPalette>
 #include <QTextLayout>
 
 #include "chatitem.h"
 #include <QTextLayout>
 
 #include "chatitem.h"
@@ -31,6 +34,7 @@ ChatItem::ChatItem(const QPersistentModelIndex &index_, QGraphicsItem *parent) :
   _fontMetrics = QtUi::style()->fontMetrics(data(ChatLineModel::FormatRole).value<UiStyle::FormatList>().at(0).second);
   _layout = 0;
   _lines = 0;
   _fontMetrics = QtUi::style()->fontMetrics(data(ChatLineModel::FormatRole).value<UiStyle::FormatList>().at(0).second);
   _layout = 0;
   _lines = 0;
+  _selectionStart = -1;
 }
 
 ChatItem::~ChatItem() {
 }
 
 ChatItem::~ChatItem() {
@@ -137,17 +141,72 @@ void ChatItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option,
   _layout->draw(painter, QPointF(0,0), QVector<QTextLayout::FormatRange>(), boundingRect());
 }
 
   _layout->draw(painter, QPointF(0,0), QVector<QTextLayout::FormatRange>(), boundingRect());
 }
 
-/*
-void ChatItem::mouseMoveEvent ( QGraphicsSceneMouseEvent * event ) {
-  qDebug() << (void*)this << "moving" << event->pos();
-  if(event->pos().y() < 0) {
-    QTextCursor cursor(document());
-    //cursor.insertText("foo");
-    //cursor.select(QTextCursor::Document);
+int ChatItem::posToCursor(const QPointF &pos) {
+  if(pos.y() > height()) return data(MessageModel::DisplayRole).toString().length();
+  if(pos.y() < 0) return 0;
+  if(!haveLayout()) updateLayout();
+  for(int l = _layout->lineCount() - 1; l >= 0; l--) {
+    QTextLine line = _layout->lineAt(l);
+    if(pos.y() >= line.y()) {
+      return line.xToCursor(pos.x(), QTextLine::CursorOnCharacter);
+    }
+  }
+  return 0;
+}
+
+void ChatItem::clearSelection() {
+  if(_selectionStart >= 0) {
+    QList<QTextLayout::FormatRange> formats = _layout->additionalFormats();
+    formats.removeLast();
+    _layout->setAdditionalFormats(formats);
+    _selectionStart = -1;
+    updateLayout();
+    update();
+  }
+}
+
+void ChatItem::mouseMoveEvent(QGraphicsSceneMouseEvent *event) {
+  int selectionEnd = posToCursor(event->pos());
+  QList<QTextLayout::FormatRange> formats = _layout->additionalFormats();
+  formats.last().start = qMin(_selectionStart, selectionEnd);
+  formats.last().length = qMax(_selectionStart, selectionEnd) - formats.last().start;
+  _layout->setAdditionalFormats(formats);
+  updateLayout();
+  update();
+}
+
+void ChatItem::mousePressEvent(QGraphicsSceneMouseEvent *event) {
+  if(event->buttons() & Qt::LeftButton) {
+    if(!haveLayout()) updateLayout();
+    chatScene()->setSelectingItem(this);
+    _selectionStart = posToCursor(event->pos());
+    QList<QTextLayout::FormatRange> formats = _layout->additionalFormats();
+    QTextLayout::FormatRange selectFmt;
+    QPalette pal = QApplication::palette();
+    selectFmt.format.setForeground(pal.brush(QPalette::HighlightedText));
+    selectFmt.format.setBackground(pal.brush(QPalette::Highlight));
+    selectFmt.length = 0;
+    formats.append(selectFmt);
+    _layout->setAdditionalFormats(formats);
+    updateLayout();
+    update();
+    event->accept();
+  } else {
     event->ignore();
     event->ignore();
-  } else QGraphicsTextItem::mouseMoveEvent(event);
+  }
+}
+
+void ChatItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) {
+  if(_selectionStart >= 0) {
+    int selectionEnd = posToCursor(event->pos());
+    QString selection
+        = data(MessageModel::DisplayRole).toString().mid(qMin(_selectionStart, selectionEnd), qAbs(_selectionStart - selectionEnd));
+    QApplication::clipboard()->setText(selection, QClipboard::Clipboard);  // TODO configure where selections should go
+    event->accept();
+  } else {
+    event->ignore();
+  }
 }
 }
-*/
 
 /*************************************************************************************************/
 
 
 /*************************************************************************************************/
 
index 73e1806..713d808 100644 (file)
 #define CHATITEM_H_
 
 #include <QGraphicsItem>
 #define CHATITEM_H_
 
 #include <QGraphicsItem>
+#include <QObject>
 
 #include "chatline.h"
 #include "chatlinemodel.h"
 
 #include "chatline.h"
 #include "chatlinemodel.h"
+#include "chatscene.h"
 #include "uistyle.h"
 
 class QTextLayout;
 #include "uistyle.h"
 
 class QTextLayout;
@@ -38,6 +40,7 @@ class ChatItem : public QGraphicsItem {
     inline QPersistentModelIndex index() const { return _index; }
     inline const MessageModel *model() const { return _index.isValid() ? qobject_cast<const MessageModel *>(_index.model()) : 0; }
     inline int row() const { return _index.isValid() ? _index.row() : 0; }
     inline QPersistentModelIndex index() const { return _index; }
     inline const MessageModel *model() const { return _index.isValid() ? qobject_cast<const MessageModel *>(_index.model()) : 0; }
     inline int row() const { return _index.isValid() ? _index.row() : 0; }
+    inline ChatScene *chatScene() const { return qobject_cast<ChatScene *>(scene()); }
 
     inline QFontMetricsF *fontMetrics() const { return _fontMetrics; }
     inline virtual QRectF boundingRect() const { return _boundingRect; }
 
     inline QFontMetricsF *fontMetrics() const { return _fontMetrics; }
     inline virtual QRectF boundingRect() const { return _boundingRect; }
@@ -54,10 +57,17 @@ class ChatItem : public QGraphicsItem {
     // returns height
     int setWidth(int width);
 
     // returns height
     int setWidth(int width);
 
+    // selection stuff, to be called by the scene
+    void clearSelection();
+    void setFullSelection();
+
   protected:
   protected:
-    //void mouseMoveEvent ( QGraphicsSceneMouseEvent * event );
+    virtual void mouseMoveEvent(QGraphicsSceneMouseEvent *event);
+    virtual void mousePressEvent(QGraphicsSceneMouseEvent *event);
+    virtual void mouseReleaseEvent(QGraphicsSceneMouseEvent *event);
 
   private:
 
   private:
+    int posToCursor(const QPointF &pos);
     int heightForWidth(int width);
     QTextLayout *createLayout(QTextOption::WrapMode, Qt::Alignment = Qt::AlignLeft);
 
     int heightForWidth(int width);
     QTextLayout *createLayout(QTextOption::WrapMode, Qt::Alignment = Qt::AlignLeft);
 
@@ -68,6 +78,7 @@ class ChatItem : public QGraphicsItem {
 
     QTextLayout *_layout;
     QList<quint16> _wrapPositions;
 
     QTextLayout *_layout;
     QList<quint16> _wrapPositions;
+    int _selectionStart;
 
     class WrapColumnFinder;
 };
 
     class WrapColumnFinder;
 };
index 8d830b6..e4a0bd7 100644 (file)
@@ -37,6 +37,7 @@ ChatScene::ChatScene(QAbstractItemModel *model, const QString &idString, QObject
   _model(model)
 {
   _width = 0;
   _model(model)
 {
   _width = 0;
+  _selectingItem = 0;
   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)));
   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)));
@@ -139,3 +140,23 @@ void ChatScene::handlePositionChanged(qreal xpos) {
   // width() should be the same for both handles, so just use firstColHandle regardless
   update(qMin(oldx, xpos) - firstColHandle->width()/2, 0, qMax(oldx, xpos) + firstColHandle->width()/2, height());
 }
   // width() should be the same for both handles, so just use firstColHandle regardless
   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::mousePressEvent(QGraphicsSceneMouseEvent *event) {
+
+  QGraphicsScene::mousePressEvent(event);
+}
+
+void ChatScene::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) {
+
+  QGraphicsScene::mouseReleaseEvent(event);
+}
+
+void ChatScene::setSelectingItem(ChatItem *item) {
+  if(_selectingItem) _selectingItem->clearSelection();
+  _selectingItem = item;
+}
index 8701f69..8fd5bf4 100644 (file)
@@ -45,16 +45,25 @@ class ChatScene : public QGraphicsScene {
   public slots:
     void setWidth(qreal);
 
   public slots:
     void setWidth(qreal);
 
-  private slots:
-    void rectChanged(const QRectF &);
-    void handlePositionChanged(qreal xpos);
+    // these are used by the chatitems to notify the scene
+    void setSelectingItem(ChatItem *item);
+    ChatItem *selectingItem() const { return _selectingItem; }
 
   signals:
     void heightChanged(qreal height);
 
 
   signals:
     void heightChanged(qreal height);
 
+  protected:
+    virtual void mouseMoveEvent(QGraphicsSceneMouseEvent *mouseEvent);
+    virtual void mousePressEvent(QGraphicsSceneMouseEvent *mouseEvent);
+    virtual void mouseReleaseEvent(QGraphicsSceneMouseEvent *mouseEvent);
+
   protected slots:
     void rowsInserted(const QModelIndex &, int, int);
 
   protected slots:
     void rowsInserted(const QModelIndex &, int, int);
 
+  private slots:
+    void rectChanged(const QRectF &);
+    void handlePositionChanged(qreal xpos);
+
   private:
     QString _idString;
     qreal _width, _height;
   private:
     QString _idString;
     qreal _width, _height;
@@ -63,6 +72,8 @@ class ChatScene : public QGraphicsScene {
 
     ColumnHandleItem *firstColHandle, *secondColHandle;
     qreal firstColHandlePos, secondColHandlePos;
 
     ColumnHandleItem *firstColHandle, *secondColHandle;
     qreal firstColHandlePos, secondColHandlePos;
+
+    ChatItem *_selectingItem;
 };
 
 #endif
 };
 
 #endif