Sanitizing clipboard handling
authorManuel Nickschas <sputnick@quassel-irc.org>
Mon, 17 Nov 2008 00:50:52 +0000 (01:50 +0100)
committerManuel Nickschas <sputnick@quassel-irc.org>
Thu, 20 Nov 2008 14:35:36 +0000 (15:35 +0100)
Quassel now respects the difference between the clipboard and the X11 selection, and won't
put stuff into the clipboard without a) Ctrl+C or b) the new shiny context menu in ChatView.
This means that just selecting stuff won't overwrite your clipboard on Win and Mac, and on X11
only if your system is configured that way. Oh, and we have a context menu now, and Ctrl+C works ;-)

src/qtui/bufferwidget.cpp
src/qtui/chatitem.cpp
src/qtui/chatitem.h
src/qtui/chatscene.cpp
src/qtui/chatscene.h
src/uisupport/inputline.cpp
src/uisupport/inputline.h

index 6fc0287..86f791b 100644 (file)
  *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
  ***************************************************************************/
 
+#include <QLayout>
+#include <QKeyEvent>
+#include <QScrollBar>
+
+#include "action.h"
+#include "actioncollection.h"
 #include "bufferwidget.h"
 #include "chatview.h"
 #include "chatviewsearchbar.h"
 #include "chatviewsearchcontroller.h"
-#include "settings.h"
 #include "client.h"
-
-#include "action.h"
-#include "actioncollection.h"
+#include "inputline.h"
 #include "qtui.h"
+#include "settings.h"
 
-#include <QLayout>
-#include <QKeyEvent>
-#include <QScrollBar>
 
 BufferWidget::BufferWidget(QWidget *parent)
   : AbstractBufferContainer(parent),
@@ -64,7 +65,7 @@ BufferWidget::BufferWidget(QWidget *parent)
 
   connect(_chatViewSearchController, SIGNAL(newCurrentHighlight(QGraphicsItem *)),
          this, SLOT(scrollToHighlight(QGraphicsItem *)));
-  
+
   ActionCollection *coll = QtUi::actionCollection();
 
   Action *zoomChatview = coll->add<Action>("ZoomChatView");
@@ -149,6 +150,19 @@ bool BufferWidget::eventFilter(QObject *watched, QEvent *event) {
 
   QKeyEvent *keyEvent = static_cast<QKeyEvent*>(event);
 
+  // Intercept copy key presses
+  if(keyEvent == QKeySequence::Copy) {
+    InputLine *inputLine = qobject_cast<InputLine *>(watched);
+    if(!inputLine)
+      return false;
+    if(inputLine->hasSelectedText())
+      return false;
+    ChatView *view = qobject_cast<ChatView *>(ui.stackedWidget->currentWidget());
+    if(view)
+      view->scene()->selectionToClipboard();
+    return true;
+  }
+
   int direction = 1;
   switch(keyEvent->key()) {
     case Qt::Key_PageUp:
index ed24bba..9e7e1eb 100644 (file)
@@ -289,9 +289,7 @@ void ChatItem::mousePressEvent(QGraphicsSceneMouseEvent *event) {
 
 void ChatItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) {
   if(_selectionMode != NoSelection && !event->buttons() & Qt::LeftButton) {
-    QString selection
-        = data(MessageModel::DisplayRole).toString().mid(qMin(_selectionStart, _selectionEnd), qAbs(_selectionStart - _selectionEnd));
-    chatScene()->putToClipboard(selection);
+    chatScene()->selectionToClipboard(QClipboard::Selection);
     event->accept();
   } else
     event->ignore();
@@ -559,6 +557,7 @@ void ContentsChatItem::hoverMoveEvent(QGraphicsSceneHoverEvent *event) {
   event->accept();
 }
 
+/*
 void ContentsChatItem::contextMenuEvent(QGraphicsSceneContextMenuEvent *event) {
   qint16 idx = posToCursor(event->pos());
   for(int i = 0; i < privateData()->clickables.count(); i++) {
@@ -581,6 +580,7 @@ void ContentsChatItem::contextMenuEvent(QGraphicsSceneContextMenuEvent *event) {
     }
   }
 }
+*/
 
 void ContentsChatItem::showWebPreview(const Clickable &click) {
 #ifndef HAVE_WEBKIT
index 3cb9c3d..a3927eb 100644 (file)
@@ -187,7 +187,7 @@ protected:
   virtual void mouseMoveEvent(QGraphicsSceneMouseEvent *event);
   virtual void hoverLeaveEvent(QGraphicsSceneHoverEvent *event);
   virtual void hoverMoveEvent(QGraphicsSceneHoverEvent *event);
-  virtual void contextMenuEvent(QGraphicsSceneContextMenuEvent *event);
+  //virtual void contextMenuEvent(QGraphicsSceneContextMenuEvent *event);
 
   virtual void handleClick(const QPointF &pos, ChatScene::ClickMode clickMode);
 
index dc38854..9d1e04a 100644 (file)
@@ -22,6 +22,7 @@
 #include <QClipboard>
 #include <QDrag>
 #include <QGraphicsSceneMouseEvent>
+#include <QMenu>
 #include <QPersistentModelIndex>
 #include <QWebView>
 
@@ -32,6 +33,7 @@
 #include "client.h"
 #include "clientbacklogmanager.h"
 #include "columnhandleitem.h"
+#include "iconloader.h"
 #include "messagefilter.h"
 #include "qtui.h"
 #include "qtuistyle.h"
@@ -571,6 +573,19 @@ bool ChatScene::isScrollingAllowed() const {
 
 /******** MOUSE HANDLING **************************************************************************/
 
+void ChatScene::contextMenuEvent(QGraphicsSceneContextMenuEvent *event) {
+  QPointF pos = event->scenePos();
+  QMenu menu;
+
+  if(isPosOverSelection(pos))
+    menu.addAction(SmallIcon("edit-copy"), tr("Copy Selection"),
+                    this, SLOT(selectionToClipboard()),
+                    QKeySequence::Copy);
+
+  menu.exec(event->screenPos());
+
+}
+
 void ChatScene::mouseMoveEvent(QGraphicsSceneMouseEvent *event) {
   if(event->buttons() == Qt::LeftButton) {
     if(!_clickHandled && (event->scenePos() - _clickPos).toPoint().manhattanLength() >= QApplication::startDragDistance()) {
@@ -635,7 +650,7 @@ void ChatScene::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) {
     } else {
       // no click -> drag or selection move
       if(isGloballySelecting()) {
-        putToClipboard(selection());
+        selectionToClipboard(QClipboard::Selection);
         _isSelecting = false;
         event->accept();
         return;
@@ -660,9 +675,6 @@ void ChatScene::handleClick(Qt::MouseButton button, const QPointF &scenePos) {
       chatItem->handleClick(chatItem->mapFromScene(scenePos), _clickMode);
     }
     _clickHandled = true;
-  } else if(button == Qt::RightButton) {
-    // TODO: context menu
-
   }
 }
 
@@ -677,14 +689,21 @@ void ChatScene::initiateDrag(QWidget *source) {
 
 /******** SELECTIONS ******************************************************************************/
 
-void ChatScene::putToClipboard(const QString &selection) {
-  // TODO Configure clipboards
-#   ifdef Q_WS_X11
-  QApplication::clipboard()->setText(selection, QClipboard::Selection);
-#   endif
-//# else
-  QApplication::clipboard()->setText(selection);
-//# endif
+void ChatScene::selectionToClipboard(QClipboard::Mode mode) {
+  if(!hasSelection())
+    return;
+
+  switch(mode) {
+    case QClipboard::Clipboard:
+      QApplication::clipboard()->setText(selection());
+      break;
+    case QClipboard::Selection:
+      if(QApplication::clipboard()->supportsSelection())
+        QApplication::clipboard()->setText(selection(), QClipboard::Selection);
+      break;
+    default:
+      break;
+  };
 }
 
 //!\brief Convert current selection to human-readable string.
@@ -711,6 +730,18 @@ QString ChatScene::selection() const {
   return QString();
 }
 
+bool ChatScene::hasSelection() const {
+  return hasGlobalSelection() || (selectingItem() && selectingItem()->hasSelection());
+}
+
+bool ChatScene::hasGlobalSelection() const {
+  return _selectionStart >= 0;
+}
+
+bool ChatScene::isGloballySelecting() const {
+  return _isSelecting;
+}
+
 void ChatScene::clearGlobalSelection() {
   if(hasGlobalSelection()) {
     for(int l = qMin(_selectionStart, _selectionEnd); l <= qMax(_selectionStart, _selectionEnd); l++)
index 3349d2f..1c64109 100644 (file)
@@ -22,6 +22,7 @@
 #define CHATSCENE_H_
 
 #include <QAbstractItemModel>
+#include <QClipboard>
 #include <QGraphicsItem>
 #include <QGraphicsScene>
 #include <QSet>
@@ -92,9 +93,10 @@ public:
   inline void setSenderCutoffMode(CutoffMode mode) { _cutoffMode = mode; }
 
   QString selection() const;
-  inline bool hasGlobalSelection() const { return _selectionStart >= 0; }
-  inline bool isGloballySelecting() const { return _isSelecting; }
+  bool hasSelection() const;
+  bool hasGlobalSelection() const;
   bool isPosOverSelection(const QPointF &) const;
+  bool isGloballySelecting() const;
   void initiateDrag(QWidget *source);
 
   bool isScrollingAllowed() const;
@@ -111,8 +113,7 @@ public:
   void startGlobalSelection(ChatItem *item, const QPointF &itemPos);
   void clearGlobalSelection();
   void clearSelection();
-
-  void putToClipboard(const QString &);
+  void selectionToClipboard(QClipboard::Mode = QClipboard::Clipboard);
 
   void requestBacklog();
 
@@ -125,6 +126,7 @@ signals:
   void mouseMoveWhileSelecting(const QPointF &scenePos);
 
 protected:
+  virtual void contextMenuEvent(QGraphicsSceneContextMenuEvent *contextMenuEvent);
   virtual void mouseMoveEvent(QGraphicsSceneMouseEvent *mouseEvent);
   virtual void mousePressEvent(QGraphicsSceneMouseEvent *mouseEvent);
   virtual void mouseReleaseEvent(QGraphicsSceneMouseEvent *mouseEvent);
index b48c053..b74e10e 100644 (file)
@@ -19,7 +19,6 @@
  ***************************************************************************/
 
 #include "inputline.h"
-
 #include "tabcompleter.h"
 
 InputLine::InputLine(QWidget *parent)
@@ -40,19 +39,19 @@ void InputLine::keyPressEvent(QKeyEvent * event) {
     event->accept();
 
     addToHistory(text(), true);
-    
+
     if(idx > 0) {
       idx--;
       showHistoryEntry();
     }
 
     break;
-    
+
   case Qt::Key_Down:
     event->accept();
 
     addToHistory(text(), true);
-    
+
     if(idx < history.count()) {
       idx++;
       if(idx < history.count() || tempHistory.contains(idx)) // tempHistory might have an entry for idx == history.count() + 1
@@ -65,7 +64,7 @@ void InputLine::keyPressEvent(QKeyEvent * event) {
     }
 
     break;
-    
+
   case Qt::Key_Select:         // for Qtopia
     emit returnPressed();
 
@@ -79,7 +78,7 @@ bool InputLine::addToHistory(const QString &text, bool temporary) {
     return false;
 
   Q_ASSERT(0 <= idx && idx <= history.count());
-  
+
   if(history.isEmpty() || text != history[idx - (int)(idx == history.count())]) {
     // if an entry of the history is changed, we remember it and show it again at this
     // position until a line was actually sent
@@ -107,7 +106,7 @@ void InputLine::on_textChanged(QString newText) {
   lineSeperators << QString("\r\n")
                 << QString('\n')
                 << QString('\r');
-  
+
   QString lineSep;
   foreach(QString seperator, lineSeperators) {
     if(newText.contains(seperator)) {
@@ -118,7 +117,7 @@ void InputLine::on_textChanged(QString newText) {
 
   if(lineSep.isEmpty())
     return;
-  
+
   if(newText.contains(lineSep)) {
     clear();
     QString line = newText.section(lineSep, 0, 0);
index 88d6062..ff6091d 100644 (file)
@@ -31,7 +31,7 @@ class InputLine : public QLineEdit {
 public:
   InputLine(QWidget *parent = 0);
   ~InputLine();
-    
+
 protected:
   //    virtual bool event(QEvent *);
   virtual void keyPressEvent(QKeyEvent * event);