ostinato/client/clipboardhelper.cpp
Srivats P 3b31a4fade Fix crash when updating 'cut' status
Also added a failsafe to workaround at run time if more problems are
detected in the field while updating cut-copy-paste action status
2020-03-17 18:50:20 +05:30

222 lines
7.4 KiB
C++

/*
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 <http://www.gnu.org/licenses/>
*/
#include "clipboardhelper.h"
#include "xtableview.h"
#include <QAction>
#include <QApplication>
#include <QClipboard>
#include <QIcon>
#include <QItemSelection>
#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<QAction*> ClipboardHelper::actions()
{
QList<QAction*> 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<XTableView*>(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<XTableView*>(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<XTableView*>(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<XTableView*>(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