diff --git a/client/clipboardhelper.cpp b/client/clipboardhelper.cpp new file mode 100644 index 0000000..cbaf62f --- /dev/null +++ b/client/clipboardhelper.cpp @@ -0,0 +1,109 @@ +/* +Copyright (C) 2020 Srivats P. + +This file is part of "Ostinato" + +This is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see +*/ + +#include "clipboardhelper.h" + +#include +#include +#include +#include + +ClipboardHelper::ClipboardHelper(QObject *parent) + : QObject(parent) +{ + actionCut_ = new QAction(tr("&Cut"), this); + actionCut_->setObjectName(QStringLiteral("actionCut")); + actionCut_->setIcon(QIcon(QString::fromUtf8(":/icons/cut.png"))); + + actionCopy_ = new QAction(tr("Cop&y"), this); + actionCopy_->setObjectName(QStringLiteral("actionCopy")); + actionCopy_->setIcon(QIcon(QString::fromUtf8(":/icons/copy.png"))); + + actionPaste_ = new QAction(tr("&Paste"), this); + actionPaste_->setObjectName(QStringLiteral("actionPaste")); + actionPaste_->setIcon(QIcon(QString::fromUtf8(":/icons/paste.png"))); + + connect(actionCut_, SIGNAL(triggered()), SLOT(actionTriggered())); + connect(actionCopy_, SIGNAL(triggered()), SLOT(actionTriggered())); + connect(actionPaste_, SIGNAL(triggered()), SLOT(actionTriggered())); + +#if 0 // FIXME: doesn't work correctly yet + connect(qGuiApp, SIGNAL(focusChanged(QWidget*, QWidget*)), + SLOT(updateClipboardActions())); +#endif +} + +QList ClipboardHelper::actions() +{ + QList actionList({actionCut_, actionCopy_, actionPaste_}); + return actionList; +} + +void ClipboardHelper::actionTriggered() +{ + QWidget *focusWidget = QApplication::focusWidget(); + + if (!focusWidget) + return; + + // single slot to handle cut/copy/paste - find which action was triggered + QString action = sender()->objectName() + .remove("action").append("()").toLower(); + if (focusWidget->metaObject()->indexOfSlot(qPrintable(action)) < 0) { + qDebug("%s slot not found for object %s:%s ", + qPrintable(action), + qPrintable(focusWidget->objectName()), + focusWidget->metaObject()->className()); + return; + } + + action.remove("()"); + QMetaObject::invokeMethod(focusWidget, qPrintable(action), + Qt::DirectConnection); +} + +void ClipboardHelper::updateActionStatus() +{ + QWidget *focusWidget = QApplication::focusWidget(); + if (!focusWidget) + return; + + qDebug("In %s", __FUNCTION__); + + const QMetaObject *meta = focusWidget->metaObject(); + // FIXME: we should check if the mimeData's mimeType can be pasted in + // the focusWidget + actionPaste_->setEnabled(qGuiApp->clipboard()->mimeData() + && (meta->indexOfSlot("paste()") >= 0)); + + bool hasSelection = false; + if (meta->indexOfProperty("hasSelectedText") >= 0) + hasSelection |= focusWidget->property("hasSelectedText").toBool(); + + bool ret = false; + if (meta->indexOfMethod("hasSelection()") >= 0) { + if (QMetaObject::invokeMethod(focusWidget, "hasSelection", + Qt::DirectConnection, Q_RETURN_ARG(bool, ret))) + hasSelection |= ret; + } + + actionCut_->setEnabled(hasSelection && (meta->indexOfSlot("cut") >= 0)); + actionCopy_->setEnabled(hasSelection && (meta->indexOfSlot("copy") >= 0)); +} + diff --git a/client/clipboardhelper.h b/client/clipboardhelper.h new file mode 100644 index 0000000..9d70382 --- /dev/null +++ b/client/clipboardhelper.h @@ -0,0 +1,45 @@ +/* +Copyright (C) 2020 Srivats P. + +This file is part of "Ostinato" + +This is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see +*/ + +#ifndef _CLIPBOARD_HELPER_H +#define _CLIPBOARD_HELPER_H + +#include + +class QAction; + +class ClipboardHelper : public QObject +{ + Q_OBJECT +public: + ClipboardHelper(QObject *parent=nullptr); + + QList actions(); + +private slots: + void actionTriggered(); + void updateActionStatus(); + +private: + QAction *actionCut_{nullptr}; + QAction *actionCopy_{nullptr}; + QAction *actionPaste_{nullptr}; +}; + +#endif diff --git a/client/deviceswidget.cpp b/client/deviceswidget.cpp index 1a4b303..124bdeb 100644 --- a/client/deviceswidget.cpp +++ b/client/deviceswidget.cpp @@ -19,6 +19,7 @@ along with this program. If not, see #include "deviceswidget.h" +#include "clipboardhelper.h" #include "devicegroupdialog.h" #include "port.h" #include "portgrouplist.h" @@ -26,6 +27,8 @@ along with this program. If not, see #include #include +extern ClipboardHelper *clipboardHelper; + DevicesWidget::DevicesWidget(QWidget *parent) : QWidget(parent), portGroups_(NULL) { @@ -49,6 +52,14 @@ DevicesWidget::DevicesWidget(QWidget *parent) // DevicesWidget's actions is an aggegate of all sub-widget's actions addActions(deviceGroupList->actions()); + + // Add the clipboard actions to the context menu of deviceGroupList + // but not to DeviceWidget's actions since they are already available + // in the global Edit Menu + QAction *sep = new QAction("Clipboard", this); + sep->setSeparator(true); + deviceGroupList->addAction(sep); + deviceGroupList->addActions(clipboardHelper->actions()); } void DevicesWidget::setPortGroupList(PortGroupList *portGroups) diff --git a/client/icons/copy.png b/client/icons/copy.png new file mode 100644 index 0000000..195dc6d Binary files /dev/null and b/client/icons/copy.png differ diff --git a/client/icons/cut.png b/client/icons/cut.png new file mode 100644 index 0000000..f215d6f Binary files /dev/null and b/client/icons/cut.png differ diff --git a/client/icons/paste.png b/client/icons/paste.png new file mode 100644 index 0000000..968f073 Binary files /dev/null and b/client/icons/paste.png differ diff --git a/client/mainwindow.cpp b/client/mainwindow.cpp index 8ad83c3..7579c1d 100644 --- a/client/mainwindow.cpp +++ b/client/mainwindow.cpp @@ -23,6 +23,7 @@ along with this program. If not, see #include "dbgthread.h" #endif +#include "clipboardhelper.h" #include "jumpurl.h" #include "logsmodel.h" #include "logswindow.h" @@ -60,6 +61,7 @@ extern const char* revision; PortGroupList *pgl; LogsModel *appLogs; +ClipboardHelper *clipboardHelper; MainWindow::MainWindow(QWidget *parent) : QMainWindow (parent) @@ -94,6 +96,7 @@ MainWindow::MainWindow(QWidget *parent) pgl = new PortGroupList; appLogs = new LogsModel(this); + clipboardHelper = new ClipboardHelper(this); portsWindow = new PortsWindow(pgl, this); statsWindow = new PortStatsWindow(pgl, this); @@ -117,6 +120,7 @@ MainWindow::MainWindow(QWidget *parent) setupUi(this); menuFile->insertActions(menuFile->actions().at(3), portsWindow->actions()); + menuEdit->addActions(clipboardHelper->actions()); statsDock->setWidget(statsWindow); addDockWidget(Qt::BottomDockWidgetArea, statsDock); diff --git a/client/mainwindow.ui b/client/mainwindow.ui index e4cbe55..a01a331 100644 --- a/client/mainwindow.ui +++ b/client/mainwindow.ui @@ -40,6 +40,11 @@ + + + &Edit + + &View @@ -48,6 +53,7 @@ + diff --git a/client/ostinato.pro b/client/ostinato.pro index 77aaa44..d7c6eb6 100644 --- a/client/ostinato.pro +++ b/client/ostinato.pro @@ -35,6 +35,7 @@ LIBS += -L"../extra/qhexedit2/$(OBJECTS_DIR)/" -lqhexedit2 RESOURCES += ostinato.qrc HEADERS += \ arpstatusmodel.h \ + clipboardhelper.h \ devicegroupdialog.h \ devicegroupmodel.h \ devicemodel.h \ @@ -64,7 +65,8 @@ HEADERS += \ streamstatsfiltermodel.h \ streamstatsmodel.h \ streamstatswindow.h \ - variablefieldswidget.h + variablefieldswidget.h \ + xtableview.h FORMS += \ about.ui \ @@ -83,6 +85,7 @@ FORMS += \ SOURCES += \ arpstatusmodel.cpp \ + clipboardhelper.cpp \ devicegroupdialog.cpp \ devicegroupmodel.cpp \ devicemodel.cpp \ diff --git a/client/ostinato.qrc b/client/ostinato.qrc index e734848..bf35a72 100644 --- a/client/ostinato.qrc +++ b/client/ostinato.qrc @@ -14,8 +14,10 @@ icons/bullet_red.png icons/bullet_white.png icons/bullet_yellow.png + icons/copy.png icons/control_play.png icons/control_stop.png + icons/cut.png icons/delete.png icons/devicegroup_add.png icons/devicegroup_delete.png @@ -35,6 +37,7 @@ icons/name.png icons/neighbor_clear.png icons/neighbor_resolve.png + icons/paste.png icons/portgroup_add.png icons/portgroup_connect.png icons/portgroup_delete.png diff --git a/client/portswindow.cpp b/client/portswindow.cpp index a45fda2..cce7678 100644 --- a/client/portswindow.cpp +++ b/client/portswindow.cpp @@ -20,6 +20,7 @@ along with this program. If not, see #include "portswindow.h" #include "applymsg.h" +#include "clipboardhelper.h" #include "deviceswidget.h" #include "portconfigdialog.h" #include "settings.h" @@ -38,6 +39,7 @@ along with this program. If not, see #include #include +extern ClipboardHelper *clipboardHelper; extern QMainWindow *mainWindow; PortsWindow::PortsWindow(PortGroupList *pgl, QWidget *parent) @@ -78,9 +80,9 @@ PortsWindow::PortsWindow(PortGroupList *pgl, QWidget *parent) tvStreamList->addAction(actionDuplicate_Stream); tvStreamList->addAction(actionDelete_Stream); - sep = new QAction(this); - sep->setSeparator(true); - tvStreamList->addAction(sep); + QAction *sep2 = new QAction(this); + sep2->setSeparator(true); + tvStreamList->addAction(sep2); tvStreamList->addAction(actionOpen_Streams); tvStreamList->addAction(actionSave_Streams); @@ -97,6 +99,14 @@ PortsWindow::PortsWindow(PortGroupList *pgl, QWidget *parent) addAction(sep); addActions(devicesWidget->actions()); + // Add the clipboard actions to the context menu of streamList + // but not to PortsWindow's actions since they are already available + // in the global Edit Menu + sep = new QAction("Clipboard", this); + sep->setSeparator(true); + tvStreamList->insertAction(sep2, sep); + tvStreamList->insertActions(sep2, clipboardHelper->actions()); + tvStreamList->setModel(plm->getStreamModel()); // XXX: It would be ideal if we only needed to do the below to diff --git a/client/xtableview.h b/client/xtableview.h index 222e399..0f17496 100644 --- a/client/xtableview.h +++ b/client/xtableview.h @@ -22,6 +22,7 @@ along with this program. If not, see #include +#include #include #include #include @@ -29,10 +30,19 @@ along with this program. If not, see class XTableView : public QTableView { + Q_OBJECT + public: XTableView(QWidget *parent) : QTableView(parent) {} virtual ~XTableView() {} +#if 0 + Q_INVOKABLE bool hasSelection() const + { + return !selectionModel()->selectedIndexes().isEmpty(); + } +#endif + protected: virtual void paintEvent(QPaintEvent *event) { @@ -58,7 +68,7 @@ protected: QTableView::keyPressEvent(event); } -private: +public slots: void cut() { copy();