Add buffer-specific actions to ChatView's context menu
[quassel.git] / src / qtui / chatitem.h
index 6c08003..d68f315 100644 (file)
@@ -21,6 +21,7 @@
 #ifndef CHATITEM_H_
 #define CHATITEM_H_
 
+#include <QAction>
 #include <QGraphicsItem>
 #include <QObject>
 
@@ -67,9 +68,16 @@ public:
 
   QList<QRectF> findWords(const QString &searchWord, Qt::CaseSensitivity caseSensitive);
 
+  virtual void addActionsToMenu(QMenu *menu, const QPointF &itemPos);
   virtual void handleClick(const QPointF &pos, ChatScene::ClickMode);
 
 protected:
+  enum SelectionMode {
+    NoSelection,
+    PartialSelection,
+    FullSelection
+  };
+
   virtual void mouseMoveEvent(QGraphicsSceneMouseEvent *event);
   virtual void mousePressEvent(QGraphicsSceneMouseEvent *event);
   virtual void mouseReleaseEvent(QGraphicsSceneMouseEvent *event);
@@ -79,6 +87,14 @@ protected:
   virtual QTextLayout::FormatRange selectionFormat() const;
   virtual inline QVector<QTextLayout::FormatRange> additionalFormats() const { return QVector<QTextLayout::FormatRange>(); }
 
+  inline qint16 selectionStart() const { return _selectionStart; }
+  inline void setSelectionStart(qint16 start) { _selectionStart = start; }
+  inline qint16 selectionEnd() const { return _selectionEnd; }
+  inline void setSelectionEnd(qint16 end) { _selectionEnd = end; }
+  inline SelectionMode selectionMode() const { return _selectionMode; }
+  inline void setSelectionMode(SelectionMode mode) { _selectionMode = mode; }
+  void setSelection(SelectionMode mode, qint16 selectionStart, qint16 selectionEnd);
+
   qint16 posToCursor(const QPointF &pos) const;
 
   inline bool hasPrivateData() const { return (bool)_data; }
@@ -106,7 +122,6 @@ private:
   ChatItemPrivate *_data;
   QRectF _boundingRect;
 
-  enum SelectionMode { NoSelection, PartialSelection, FullSelection };
   SelectionMode _selectionMode;
   qint16 _selectionStart, _selectionEnd;
 
@@ -116,7 +131,7 @@ private:
 struct ChatItemPrivate {
   QTextLayout *layout;
   ChatItemPrivate(QTextLayout *l) : layout(l) {}
-  ~ChatItemPrivate() {
+  virtual ~ChatItemPrivate() {
     delete layout;
   }
 };
@@ -161,6 +176,8 @@ struct ContentsChatItemPrivate;
 
 //! A ChatItem for the contents column
 class ContentsChatItem : public ChatItem {
+  Q_DECLARE_TR_FUNCTIONS(ContentsChatItem);
+
 public:
   ContentsChatItem(const qreal &width, const QPointF &pos, QGraphicsItem *parent);
 
@@ -172,13 +189,12 @@ public:
 
 protected:
   virtual void mouseMoveEvent(QGraphicsSceneMouseEvent *event);
-  virtual void mousePressEvent(QGraphicsSceneMouseEvent *event);
-  virtual void mouseReleaseEvent(QGraphicsSceneMouseEvent *event);
   virtual void hoverLeaveEvent(QGraphicsSceneHoverEvent *event);
   virtual void hoverMoveEvent(QGraphicsSceneHoverEvent *event);
-  virtual void contextMenuEvent(QGraphicsSceneContextMenuEvent *event);
-
+  virtual void handleClick(const QPointF &pos, ChatScene::ClickMode clickMode);
 
+  virtual void addActionsToMenu(QMenu *menu, const QPointF &itemPos);
+  virtual void copyLinkToClipboard();
 
   virtual QVector<QTextLayout::FormatRange> additionalFormats() const;
 
@@ -187,11 +203,14 @@ protected:
 
 private:
   struct Clickable;
+  class ActionProxy;
   class WrapColumnFinder;
 
   inline ContentsChatItemPrivate *privateData() const;
 
   QList<Clickable> findClickables() const;
+  Clickable clickableAt(const QPointF &pos) const;
+
   void endHoverMode();
   void showWebPreview(const Clickable &click);
   void clearWebPreview();
@@ -201,6 +220,9 @@ private:
   friend struct ContentsChatItemPrivate;
 
   QFontMetricsF *_fontMetrics;
+
+  // we need a receiver for Action signals
+  static ActionProxy _actionProxy;
 };
 
 struct ContentsChatItem::Clickable {
@@ -225,14 +247,16 @@ struct ContentsChatItemPrivate : ChatItemPrivate {
   ContentsChatItem *contentsItem;
   QList<ContentsChatItem::Clickable> clickables;
   ContentsChatItem::Clickable currentClickable;
-  bool hasDragged;
+  ContentsChatItem::Clickable activeClickable;
 
   ContentsChatItemPrivate(QTextLayout *l, const QList<ContentsChatItem::Clickable> &c, ContentsChatItem *parent)
-  : ChatItemPrivate(l), contentsItem(parent), clickables(c), hasDragged(false) {}
+  : ChatItemPrivate(l), contentsItem(parent), clickables(c) {}
 };
 
 //inlines regarding ContentsChatItemPrivate
-ChatItemPrivate *ContentsChatItem::newPrivateData() { return new ContentsChatItemPrivate(createLayout(QTextOption::WrapAnywhere), findClickables(), this); }
+ChatItemPrivate *ContentsChatItem::newPrivateData() {
+  return new ContentsChatItemPrivate(createLayout(QTextOption::WrapAnywhere), findClickables(), this);
+}
 ContentsChatItemPrivate *ContentsChatItem::privateData() const { return (ContentsChatItemPrivate *)ChatItem::privateData(); }
 
 class ContentsChatItem::WrapColumnFinder {
@@ -252,6 +276,28 @@ private:
   qreal choppedTrailing;
 };
 
+//! Acts as a proxy for Action signals targetted at a ContentsChatItem
+/** Since a ChatItem is not a QObject, hence cannot receive signals, we use a static ActionProxy
+ *  as a receiver instead. This avoids having to handle ChatItem actions (e.g. context menu entries)
+ *  outside the ChatItem.
+ */
+class ContentsChatItem::ActionProxy : public QObject {
+  Q_OBJECT
+
+public slots:
+  inline void copyLinkToClipboard() { item()->copyLinkToClipboard(); }
+
+private:
+  /// Returns the ContentsChatItem that should receive the action event.
+  /** For efficiency reasons, values are not checked for validity. You gotta make sure that you set the data() member
+   *  in the Action correctly.
+   *  @return The ChatItem from which the sending Action originated
+   */
+  inline ContentsChatItem *item() const {
+    return static_cast<ContentsChatItem *>(qobject_cast<QAction *>(sender())->data().value<void *>());
+  }
+};
+
 /*************************************************************************************************/
 
 // Avoid circular include deps