/* 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 "xtableview.h" #include #include #include #include #include #if 0 // change 0 to 1 for debug #define xDebug(...) qDebug(__VA_ARGS__) #else #define xDebug(...) #endif 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())); // XXX: failsafe in case updation of cut/copy/status causes issues // Temporary for 1 release - will be removed after that if (qEnvironmentVariableIsSet("X-OSTINATO-CCP-STATUS")) { qWarning("FAILSAFE: Cut-Copy-Paste action status will not be updated"); return; } connect(qApp, SIGNAL(focusChanged(QWidget*, QWidget*)), SLOT(updateCutCopyStatus(QWidget*, QWidget*))); connect(qApp, SIGNAL(focusChanged(QWidget*, QWidget*)), SLOT(updatePasteStatus())); connect(QGuiApplication::clipboard(), SIGNAL(dataChanged()), SLOT(updatePasteStatus())); } QList ClipboardHelper::actions() { QList actionList({actionCut_, actionCopy_, actionPaste_}); return actionList; } void ClipboardHelper::actionTriggered() { QWidget *focusWidget = qApp->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) { xDebug("%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::updateCutCopyStatus(QWidget *old, QWidget *now) { xDebug("In %s", __FUNCTION__); const XTableView *view = dynamic_cast(old); if (view) { disconnect(view->selectionModel(), SIGNAL(selectionChanged(const QItemSelection&, const QItemSelection&)), this, SLOT(focusWidgetSelectionChanged(const QItemSelection&, const QItemSelection&))); disconnect(view->model(), SIGNAL(modelReset()), this, SLOT(focusWidgetModelReset())); } if (!now) { xDebug("No focus widget to copy from"); actionCut_->setEnabled(false); actionCopy_->setEnabled(false); return; } const QMetaObject *meta = now->metaObject(); if (meta->indexOfSlot("copy()") < 0) { xDebug("Focus Widget (%s) doesn't have a copy slot", qPrintable(now->objectName())); actionCut_->setEnabled(false); actionCopy_->setEnabled(false); return; } view = dynamic_cast(now); if (view) { connect(view->selectionModel(), SIGNAL(selectionChanged(const QItemSelection&, const QItemSelection&)), SLOT(focusWidgetSelectionChanged(const QItemSelection&, const QItemSelection&))); connect(view->model(), SIGNAL(modelReset()), this, SLOT(focusWidgetModelReset())); if (!view->hasSelection()) { xDebug("%s doesn't have anything selected to copy", qPrintable(view->objectName())); actionCut_->setEnabled(false); actionCopy_->setEnabled(false); return; } xDebug("%s model can cut: %d", qPrintable(view->objectName()), view->canCut()); actionCut_->setEnabled(view->canCut()); } xDebug("%s has a selection and copy slot: copy possible", qPrintable(now->objectName())); actionCopy_->setEnabled(true); } void ClipboardHelper::focusWidgetSelectionChanged( const QItemSelection &selected, const QItemSelection &/*deselected*/) { xDebug("In %s", __FUNCTION__); // Selection changed in the XTableView that has focus const XTableView *view = dynamic_cast(qApp->focusWidget()); xDebug("canCut:%d empty:%d", view->canCut(), selected.indexes().isEmpty()); actionCut_->setEnabled(!selected.indexes().isEmpty() && view && view->canCut()); actionCopy_->setEnabled(!selected.indexes().isEmpty()); } void ClipboardHelper::updatePasteStatus() { xDebug("In %s", __FUNCTION__); QWidget *focusWidget = qApp->focusWidget(); if (!focusWidget) { xDebug("No focus widget to paste into"); actionPaste_->setEnabled(false); return; } const QMimeData *item = QGuiApplication::clipboard()->mimeData(); if (!item || item->formats().isEmpty()) { xDebug("Nothing on clipboard to paste"); actionPaste_->setEnabled(false); return; } const QMetaObject *meta = focusWidget->metaObject(); if (meta->indexOfSlot("paste()") < 0) { xDebug("Focus Widget (%s) doesn't have a paste slot", qPrintable(focusWidget->objectName())); actionPaste_->setEnabled(false); return; } const XTableView *view = dynamic_cast(focusWidget); if (view && !view->canPaste(item)) { xDebug("%s cannot accept this item (%s)", qPrintable(view->objectName()), qPrintable(item->formats().join("|"))); actionPaste_->setEnabled(false); return; } xDebug("%s can accept this item (%s): paste possible", qPrintable(focusWidget->objectName()), qPrintable(item->formats().join("|"))); actionPaste_->setEnabled(true); } void ClipboardHelper::focusWidgetModelReset() { xDebug("In %s", __FUNCTION__); QWidget *focusWidget = qApp->focusWidget(); updateCutCopyStatus(focusWidget, focusWidget); // re-eval cut/copy status } #undef xDebug