diff --git a/client/clipboardhelper.cpp b/client/clipboardhelper.cpp index cbaf62f..8b2d46a 100644 --- a/client/clipboardhelper.cpp +++ b/client/clipboardhelper.cpp @@ -19,10 +19,19 @@ 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) @@ -43,10 +52,13 @@ ClipboardHelper::ClipboardHelper(QObject *parent) 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 + 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() @@ -57,7 +69,7 @@ QList ClipboardHelper::actions() void ClipboardHelper::actionTriggered() { - QWidget *focusWidget = QApplication::focusWidget(); + QWidget *focusWidget = qApp->focusWidget(); if (!focusWidget) return; @@ -66,7 +78,7 @@ void ClipboardHelper::actionTriggered() QString action = sender()->objectName() .remove("action").append("()").toLower(); if (focusWidget->metaObject()->indexOfSlot(qPrintable(action)) < 0) { - qDebug("%s slot not found for object %s:%s ", + xDebug("%s slot not found for object %s:%s ", qPrintable(action), qPrintable(focusWidget->objectName()), focusWidget->metaObject()->className()); @@ -78,32 +90,126 @@ void ClipboardHelper::actionTriggered() Qt::DirectConnection); } -void ClipboardHelper::updateActionStatus() +void ClipboardHelper::updateCutCopyStatus(QWidget *old, QWidget *now) { - QWidget *focusWidget = QApplication::focusWidget(); - if (!focusWidget) - return; + xDebug("In %s", __FUNCTION__); - 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; + 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())); } - actionCut_->setEnabled(hasSelection && (meta->indexOfSlot("cut") >= 0)); - actionCopy_->setEnabled(hasSelection && (meta->indexOfSlot("copy") >= 0)); + 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 + diff --git a/client/clipboardhelper.h b/client/clipboardhelper.h index e43be4f..0c2c8fe 100644 --- a/client/clipboardhelper.h +++ b/client/clipboardhelper.h @@ -24,6 +24,7 @@ along with this program. If not, see #include class QAction; +class QItemSelection; class ClipboardHelper : public QObject { @@ -35,7 +36,11 @@ public: private slots: void actionTriggered(); - void updateActionStatus(); + void updateCutCopyStatus(QWidget *old, QWidget *now); + void focusWidgetSelectionChanged(const QItemSelection &selected, + const QItemSelection &deselected); + void focusWidgetModelReset(); + void updatePasteStatus(); private: QAction *actionCut_{nullptr}; diff --git a/client/logsmodel.cpp b/client/logsmodel.cpp index b23feb5..1aa8600 100644 --- a/client/logsmodel.cpp +++ b/client/logsmodel.cpp @@ -109,6 +109,11 @@ QVariant LogsModel::data(const QModelIndex &index, int role) const return QVariant(); } +Qt::DropActions LogsModel::supportedDropActions() const +{ + return Qt::IgnoreAction; // read-only model, doesn't accept any data +} + QStringList LogsModel::mimeTypes() const { return QStringList() << "text/plain"; diff --git a/client/logsmodel.h b/client/logsmodel.h index dd27e37..8c7c937 100644 --- a/client/logsmodel.h +++ b/client/logsmodel.h @@ -43,6 +43,8 @@ public: QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const; QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const; + + Qt::DropActions supportedDropActions() const; QStringList mimeTypes() const; QMimeData* mimeData(const QModelIndexList &indexes) const; diff --git a/client/xtableview.h b/client/xtableview.h index 0f17496..2f87f2b 100644 --- a/client/xtableview.h +++ b/client/xtableview.h @@ -22,6 +22,9 @@ along with this program. If not, see #include +#include "devicegroupmodel.h" +#include "streammodel.h" + #include #include #include @@ -36,36 +39,33 @@ public: XTableView(QWidget *parent) : QTableView(parent) {} virtual ~XTableView() {} -#if 0 - Q_INVOKABLE bool hasSelection() const + void setModel(QAbstractItemModel *model) + { + // XXX: yes, this is hacky; but there's no way to figure out + // if a model allows removeRows() or not + if (dynamic_cast(model) + || dynamic_cast(model)) + _modelAllowsRemove = true; + else + _modelAllowsRemove = false; + + QTableView::setModel(model); + } + + bool hasSelection() const { return !selectionModel()->selectedIndexes().isEmpty(); } -#endif -protected: - virtual void paintEvent(QPaintEvent *event) + bool canCut() const { - if (!model()->hasChildren()) { - QPainter painter(viewport()); - style()->drawItemText(&painter, viewport()->rect(), - layoutDirection() | Qt::AlignCenter, palette(), - true, whatsThis(), QPalette::WindowText); - } - else - QTableView::paintEvent(event); + return _modelAllowsRemove; } - virtual void keyPressEvent(QKeyEvent *event) + bool canPaste(const QMimeData *data) const { - if (event->matches(QKeySequence::Cut)) { - cut(); - } else if (event->matches(QKeySequence::Copy)) { - copy(); - } else if (event->matches(QKeySequence::Paste)) { - paste(); - } else - QTableView::keyPressEvent(event); + return model()->canDropMimeData(data, Qt::CopyAction, + 0, 0, QModelIndex()); } public slots: @@ -102,7 +102,7 @@ public slots: { const QMimeData *mimeData = qApp->clipboard()->mimeData(); - if (!mimeData) + if (!mimeData || mimeData->formats().isEmpty()) return; if (selectionModel()->hasSelection() @@ -128,6 +128,34 @@ public slots: model()->dropMimeData(mimeData, Qt::CopyAction, row, column, QModelIndex()); } + +protected: + virtual void paintEvent(QPaintEvent *event) + { + if (!model()->hasChildren()) { + QPainter painter(viewport()); + style()->drawItemText(&painter, viewport()->rect(), + layoutDirection() | Qt::AlignCenter, palette(), + true, whatsThis(), QPalette::WindowText); + } + else + QTableView::paintEvent(event); + } + + virtual void keyPressEvent(QKeyEvent *event) + { + if (event->matches(QKeySequence::Cut)) { + cut(); + } else if (event->matches(QKeySequence::Copy)) { + copy(); + } else if (event->matches(QKeySequence::Paste)) { + paste(); + } else + QTableView::keyPressEvent(event); + } + +private: + bool _modelAllowsRemove{false}; }; #endif