From: Manuel Nickschas Date: Tue, 18 Nov 2008 17:06:33 +0000 (+0100) Subject: Adding context menu actions to ChatView X-Git-Tag: 0.4.0~424 X-Git-Url: https://git.quassel-irc.org/?p=quassel.git;a=commitdiff_plain;h=7e76b93191c8f19c24709f36992c99f8ee9d508d Adding context menu actions to ChatView This implements part of our new context menu handling and adds the Zoom actions as well as a Copy Link Address action. --- diff --git a/src/qtui/CMakeLists.txt b/src/qtui/CMakeLists.txt index b65c3182..1cc2f283 100644 --- a/src/qtui/CMakeLists.txt +++ b/src/qtui/CMakeLists.txt @@ -52,6 +52,7 @@ set(MOC_HDRS aboutdlg.h bufferwidget.h channellistdlg.h + chatitem.h chatlinemodel.h chatmonitorfilter.h chatmonitorview.h @@ -84,7 +85,6 @@ set(MOC_HDRS verticaldock.h) set(HEADERS - chatitem.h chatline.h chatlinemodelitem.h chatviewsettings.h diff --git a/src/qtui/bufferwidget.cpp b/src/qtui/bufferwidget.cpp index 86f791b6..1dbe7b73 100644 --- a/src/qtui/bufferwidget.cpp +++ b/src/qtui/bufferwidget.cpp @@ -29,6 +29,7 @@ #include "chatviewsearchbar.h" #include "chatviewsearchcontroller.h" #include "client.h" +#include "iconloader.h" #include "inputline.h" #include "qtui.h" #include "settings.h" @@ -68,20 +69,20 @@ BufferWidget::BufferWidget(QWidget *parent) ActionCollection *coll = QtUi::actionCollection(); - Action *zoomChatview = coll->add("ZoomChatView"); - connect(zoomChatview, SIGNAL(triggered()), SLOT(zoomIn())); - zoomChatview->setText(tr("Enlarge Chat View")); - zoomChatview->setShortcut(tr("Ctrl++")); + Action *zoomInChatview = coll->add("ZoomInChatView", this, SLOT(zoomIn())); + zoomInChatview->setText(tr("Zoom In")); + zoomInChatview->setIcon(SmallIcon("zoom-in")); + zoomInChatview->setShortcut(QKeySequence::ZoomIn); - Action *zoomOutChatview = coll->add("ZoomOutChatView"); - connect(zoomOutChatview, SIGNAL(triggered()), SLOT(zoomOut())); - zoomOutChatview->setText(tr("Demagnify Chat View")); - zoomOutChatview->setShortcut(tr("Ctrl+-")); + Action *zoomOutChatview = coll->add("ZoomOutChatView", this, SLOT(zoomOut())); + zoomOutChatview->setIcon(SmallIcon("zoom-out")); + zoomOutChatview->setText(tr("Zoom Out")); + zoomOutChatview->setShortcut(QKeySequence::ZoomOut); - Action *zoomNormalChatview = coll->add("ZoomNormalChatView"); - connect(zoomNormalChatview, SIGNAL(triggered()), SLOT(zoomNormal())); - zoomNormalChatview->setText(tr("Normalize zoom of Chat View")); - zoomNormalChatview->setShortcut(tr("Ctrl+0")); + Action *zoomOriginalChatview = coll->add("ZoomOriginalChatView", this, SLOT(zoomOriginal())); + zoomOriginalChatview->setIcon(SmallIcon("zoom-original")); + zoomOriginalChatview->setText(tr("Zoom Original")); + zoomOriginalChatview->setShortcut(tr("Ctrl+0")); } BufferWidget::~BufferWidget() { @@ -92,6 +93,7 @@ BufferWidget::~BufferWidget() { AbstractChatView *BufferWidget::createChatView(BufferId id) { ChatView *chatView; chatView = new ChatView(id, this); + chatView->setBufferContainer(this); _chatViews[id] = chatView; ui.stackedWidget->addWidget(chatView); chatView->setFocusProxy(this); @@ -127,24 +129,32 @@ void BufferWidget::scrollToHighlight(QGraphicsItem *highlightItem) { void BufferWidget::zoomIn() { ChatView *view = qobject_cast(ui.stackedWidget->currentWidget()); - if(!view) return; - view->zoomIn(); + if(view) + view->zoomIn(); } void BufferWidget::zoomOut() { ChatView *view = qobject_cast(ui.stackedWidget->currentWidget()); - if(!view) return; - view->zoomOut(); + if(view) + view->zoomOut(); } -void BufferWidget::zoomNormal() { +void BufferWidget::zoomOriginal() { ChatView *view = qobject_cast(ui.stackedWidget->currentWidget()); - if(!view) return; - view->zoomNormal(); + if(view) + view->zoomOriginal(); +} + +void BufferWidget::addActionsToMenu(QMenu *menu) { + ActionCollection *coll = QtUi::actionCollection(); + menu->addSeparator(); + menu->addAction(coll->action("ZoomInChatView")); + menu->addAction(coll->action("ZoomOutChatView")); + menu->addAction(coll->action("ZoomOriginalChatView")); + } bool BufferWidget::eventFilter(QObject *watched, QEvent *event) { - Q_UNUSED(watched); if(event->type() != QEvent::KeyPress) return false; diff --git a/src/qtui/bufferwidget.h b/src/qtui/bufferwidget.h index 1d70c272..fd8c9ac3 100644 --- a/src/qtui/bufferwidget.h +++ b/src/qtui/bufferwidget.h @@ -39,6 +39,7 @@ public: virtual bool eventFilter(QObject *watched, QEvent *event); inline ChatViewSearchBar *searchBar() const { return ui.searchBar; } + void addActionsToMenu(QMenu *); protected: virtual AbstractChatView *createChatView(BufferId); @@ -51,7 +52,7 @@ private slots: void scrollToHighlight(QGraphicsItem *highlightItem); void zoomIn(); void zoomOut(); - void zoomNormal(); + void zoomOriginal(); private: Ui::BufferWidget ui; diff --git a/src/qtui/chatitem.cpp b/src/qtui/chatitem.cpp index 9e7e1eb6..fcdbca49 100644 --- a/src/qtui/chatitem.cpp +++ b/src/qtui/chatitem.cpp @@ -295,6 +295,12 @@ void ChatItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) { event->ignore(); } +void ChatItem::addActionsToMenu(QMenu *menu, const QPointF &pos) { + Q_UNUSED(menu); + Q_UNUSED(pos); + +} + // ************************************************************ // SenderChatItem // ************************************************************ @@ -347,6 +353,9 @@ void SenderChatItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *op // ************************************************************ // ContentsChatItem // ************************************************************ + +ContentsChatItem::ActionProxy ContentsChatItem::_actionProxy; + ContentsChatItem::ContentsChatItem(const qreal &width, const QPointF &pos, QGraphicsItem *parent) : ChatItem(0, 0, pos, parent) { @@ -557,30 +566,33 @@ 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++) { - Clickable click = privateData()->clickables.at(i); - if(idx >= click.start && idx < click.start + click.length) { - if(click.type == Clickable::Url) { - QMenu menu; - QAction *copyToClipboard = menu.addAction(QObject::tr("Copy to Clipboard")); - QAction *selected = menu.exec(event->screenPos()); - if(selected == copyToClipboard) { - QString url = data(ChatLineModel::DisplayRole).toString().mid(click.start, click.length); -# ifdef Q_WS_X11 - QApplication::clipboard()->setText(url, QClipboard::Selection); -# endif -//# else - QApplication::clipboard()->setText(url); -//# endif - } - } +void ContentsChatItem::addActionsToMenu(QMenu *menu, const QPointF &pos) { + Q_UNUSED(pos); // we assume that the current mouse cursor pos is the point of invocation + + if(privateData()->currentClickable.isValid()) { + switch(privateData()->currentClickable.type) { + case Clickable::Url: + privateData()->activeClickable = privateData()->currentClickable; + menu->addAction(tr("Copy Link Address"), &_actionProxy, SLOT(copyLinkToClipboard()))->setData(QVariant::fromValue(this)); + break; + + default: + break; } } } -*/ + +void ContentsChatItem::copyLinkToClipboard() { + Clickable click = privateData()->activeClickable; + if(click.isValid() && click.type == Clickable::Url) { + QString url = data(ChatLineModel::DisplayRole).toString().mid(click.start, click.length); + if(!url.contains("://")) + url = "http://" + url; + chatScene()->stringToClipboard(url); + } +} + +/******** WEB PREVIEW *****************************************************************************/ void ContentsChatItem::showWebPreview(const Clickable &click) { #ifndef HAVE_WEBKIT @@ -673,3 +685,5 @@ qint16 ContentsChatItem::WrapColumnFinder::nextWrapColumn() { return -1; } +/*************************************************************************************************/ + diff --git a/src/qtui/chatitem.h b/src/qtui/chatitem.h index a3927eb5..b0afbfa1 100644 --- a/src/qtui/chatitem.h +++ b/src/qtui/chatitem.h @@ -21,6 +21,7 @@ #ifndef CHATITEM_H_ #define CHATITEM_H_ +#include #include #include @@ -67,6 +68,7 @@ public: QList findWords(const QString &searchWord, Qt::CaseSensitivity caseSensitive); + virtual void addActionsToMenu(QMenu *menu, const QPointF &itemPos); virtual void handleClick(const QPointF &pos, ChatScene::ClickMode); protected: @@ -129,7 +131,7 @@ private: struct ChatItemPrivate { QTextLayout *layout; ChatItemPrivate(QTextLayout *l) : layout(l) {} - ~ChatItemPrivate() { + virtual ~ChatItemPrivate() { delete layout; } }; @@ -174,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); @@ -187,10 +191,11 @@ protected: virtual void mouseMoveEvent(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 additionalFormats() const; virtual void doLayout(); @@ -198,6 +203,7 @@ protected: private: struct Clickable; + class ActionProxy; class WrapColumnFinder; inline ContentsChatItemPrivate *privateData() const; @@ -212,6 +218,9 @@ private: friend struct ContentsChatItemPrivate; QFontMetricsF *_fontMetrics; + + // we need a receiver for Action signals + static ActionProxy _actionProxy; }; struct ContentsChatItem::Clickable { @@ -236,6 +245,7 @@ struct ContentsChatItemPrivate : ChatItemPrivate { ContentsChatItem *contentsItem; QList clickables; ContentsChatItem::Clickable currentClickable; + ContentsChatItem::Clickable activeClickable; ContentsChatItemPrivate(QTextLayout *l, const QList &c, ContentsChatItem *parent) : ChatItemPrivate(l), contentsItem(parent), clickables(c) {} @@ -264,6 +274,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(qobject_cast(sender())->data().value()); + } +}; + /*************************************************************************************************/ // Avoid circular include deps diff --git a/src/qtui/chatscene.cpp b/src/qtui/chatscene.cpp index af6ddc3a..7d2944c6 100644 --- a/src/qtui/chatscene.cpp +++ b/src/qtui/chatscene.cpp @@ -30,6 +30,7 @@ #include "chatline.h" #include "chatlinemodelitem.h" #include "chatscene.h" +#include "chatview.h" #include "client.h" #include "clientbacklogmanager.h" #include "columnhandleitem.h" @@ -577,11 +578,19 @@ void ChatScene::contextMenuEvent(QGraphicsSceneContextMenuEvent *event) { QPointF pos = event->scenePos(); QMenu menu; + // zoom actions and similar + chatView()->addActionsToMenu(&menu); + if(isPosOverSelection(pos)) menu.addAction(SmallIcon("edit-copy"), tr("Copy Selection"), this, SLOT(selectionToClipboard()), QKeySequence::Copy); + // item-specific options (select link etc) + ChatItem *item = chatItemAt(pos); + if(item) + item->addActionsToMenu(&menu, item->mapFromScene(pos)); + menu.exec(event->screenPos()); } @@ -693,13 +702,17 @@ void ChatScene::selectionToClipboard(QClipboard::Mode mode) { if(!hasSelection()) return; + stringToClipboard(selection(), mode); +} + +void ChatScene::stringToClipboard(const QString &str, QClipboard::Mode mode) { switch(mode) { case QClipboard::Clipboard: - QApplication::clipboard()->setText(selection()); + QApplication::clipboard()->setText(str); break; case QClipboard::Selection: if(QApplication::clipboard()->supportsSelection()) - QApplication::clipboard()->setText(selection(), QClipboard::Selection); + QApplication::clipboard()->setText(str, QClipboard::Selection); break; default: break; diff --git a/src/qtui/chatscene.h b/src/qtui/chatscene.h index 12fc8c0f..6a80f6c7 100644 --- a/src/qtui/chatscene.h +++ b/src/qtui/chatscene.h @@ -114,6 +114,7 @@ public: void clearGlobalSelection(); void clearSelection(); void selectionToClipboard(QClipboard::Mode = QClipboard::Clipboard); + void stringToClipboard(const QString &str, QClipboard::Mode = QClipboard::Clipboard); void requestBacklog(); diff --git a/src/qtui/chatview.cpp b/src/qtui/chatview.cpp index 53fa024b..4672911d 100644 --- a/src/qtui/chatview.cpp +++ b/src/qtui/chatview.cpp @@ -19,8 +19,10 @@ ***************************************************************************/ #include +#include #include +#include "bufferwidget.h" #include "chatlinemodelitem.h" #include "chatscene.h" #include "chatview.h" @@ -31,6 +33,7 @@ ChatView::ChatView(BufferId bufferId, QWidget *parent) : QGraphicsView(parent), AbstractChatView(), + _bufferContainer(0), _currentScaleFactor(1) { QList filterList; @@ -147,6 +150,15 @@ MsgId ChatView::lastMsgId() const { return model->data(model->index(model->rowCount() - 1, 0), MessageModel::MsgIdRole).value(); } +void ChatView::addActionsToMenu(QMenu *menu) { + // zoom actions + BufferWidget *bw = qobject_cast(bufferContainer()); + if(bw) { + bw->addActionsToMenu(menu); + menu->addSeparator(); + } +} + void ChatView::zoomIn() { _currentScaleFactor *= 1.2; scale(1.2, 1.2); @@ -159,7 +171,7 @@ void ChatView::zoomOut() { scene()->setWidth(viewport()->width() / _currentScaleFactor - 2); } -void ChatView::zoomNormal() { +void ChatView::zoomOriginal() { scale(1/_currentScaleFactor, 1/_currentScaleFactor); _currentScaleFactor = 1; scene()->setWidth(viewport()->width() - 2); diff --git a/src/qtui/chatview.h b/src/qtui/chatview.h index e0ea20be..390366a3 100644 --- a/src/qtui/chatview.h +++ b/src/qtui/chatview.h @@ -26,11 +26,13 @@ #include "abstractbuffercontainer.h" +class AbstractBufferContainer; class AbstractUiMsg; class Buffer; class ChatLine; class ChatScene; class MessageFilter; +class QMenu; class ChatView : public QGraphicsView, public AbstractChatView { Q_OBJECT @@ -40,13 +42,18 @@ public: ChatView(BufferId bufferId, QWidget *parent = 0); virtual MsgId lastMsgId() const; + inline AbstractBufferContainer *bufferContainer() const { return _bufferContainer; } + inline void setBufferContainer(AbstractBufferContainer *c) { _bufferContainer = c; } + inline ChatScene *scene() const { return _scene; } + void addActionsToMenu(QMenu *); + public slots: inline virtual void clear() {} void zoomIn(); void zoomOut(); - void zoomNormal(); + void zoomOriginal(); protected: virtual void resizeEvent(QResizeEvent *event); @@ -63,6 +70,7 @@ private slots: private: void init(MessageFilter *filter); + AbstractBufferContainer *_bufferContainer; ChatScene *_scene; int _lastScrollbarPos; qreal _currentScaleFactor;