Skip to content

Commit 4f24731

Browse files
committed
Add clipboard
1 parent 37a577e commit 4f24731

7 files changed

Lines changed: 478 additions & 1 deletion

File tree

src/plugins/coreplugin/internal/CorePlugin.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@
6262
#include <coreplugin/ProjectWindowInterface.h>
6363
#include <coreplugin/internal/CloseSaveCheckAddOn.h>
6464
#include <coreplugin/internal/PlatformJumpListHelper.h>
65+
#include <coreplugin/DspxClipboard.h>
6566

6667
static auto getCoreActionExtension() {
6768
return QAK_STATIC_ACTION_EXTENSION(coreplugin);
@@ -253,6 +254,7 @@ namespace Core::Internal {
253254
void CorePlugin::initializeSingletons() {
254255
new CoreInterface(this);
255256
new BehaviorPreference(this);
257+
new DspxClipboard(this);
256258
}
257259

258260
void CorePlugin::initializeActions() {
Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
#include "DspxClipboard.h"
2+
#include "DspxClipboard_p.h"
3+
4+
#include <QClipboard>
5+
#include <QGuiApplication>
6+
#include <QMimeData>
7+
8+
namespace Core {
9+
10+
void DspxClipboardPrivate::handleClipboardChanged() {
11+
Q_Q(DspxClipboard);
12+
if (mode == DspxClipboard::Global)
13+
Q_EMIT q->changed();
14+
}
15+
16+
static QList<DspxClipboardData::Type> supportedTypes() {
17+
static const QList<DspxClipboardData::Type> types = {
18+
DspxClipboardData::Tempo,
19+
DspxClipboardData::Label,
20+
DspxClipboardData::Track,
21+
DspxClipboardData::Clip,
22+
DspxClipboardData::Note
23+
};
24+
return types;
25+
}
26+
27+
static DspxClipboard *m_instance = nullptr;
28+
29+
DspxClipboard::DspxClipboard(QObject *parent) : QObject(parent), d_ptr(new DspxClipboardPrivate) {
30+
Q_D(DspxClipboard);
31+
Q_ASSERT(!m_instance);
32+
m_instance = this;
33+
d->q_ptr = this;
34+
auto *clipboard = QGuiApplication::clipboard();
35+
connect(clipboard, &QClipboard::dataChanged, this, [this] {
36+
Q_D(DspxClipboard);
37+
d->handleClipboardChanged();
38+
});
39+
}
40+
41+
DspxClipboard::~DspxClipboard() {
42+
m_instance = nullptr;
43+
}
44+
45+
DspxClipboard *DspxClipboard::instance() {
46+
return m_instance;
47+
}
48+
49+
DspxClipboard::Mode DspxClipboard::mode() const {
50+
Q_D(const DspxClipboard);
51+
return d->mode;
52+
}
53+
54+
void DspxClipboard::setMode(Mode mode) {
55+
Q_D(DspxClipboard);
56+
if (d->mode == mode)
57+
return;
58+
d->mode = mode;
59+
Q_EMIT changed();
60+
}
61+
62+
void DspxClipboard::copy(const QList<DspxClipboardData> &data, QMimeData *additionalMimeData) {
63+
Q_D(DspxClipboard);
64+
if (d->mode == Internal) {
65+
d->internalData.clear();
66+
for (const auto &item : data) {
67+
auto type = item.type();
68+
if (d->internalData.contains(type))
69+
continue;
70+
d->internalData.insert(type, item);
71+
}
72+
Q_EMIT changed();
73+
return;
74+
}
75+
76+
auto *mimeData = additionalMimeData ? additionalMimeData : new QMimeData;
77+
78+
for (const auto &item : data) {
79+
const auto type = item.type();
80+
const auto mimeType = DspxClipboardData::mimeType(type);
81+
if (mimeType.isEmpty() || mimeData->hasFormat(mimeType))
82+
continue;
83+
mimeData->setData(mimeType, item.toData());
84+
}
85+
86+
QGuiApplication::clipboard()->setMimeData(mimeData);
87+
}
88+
89+
DspxClipboardData DspxClipboard::paste(DspxClipboardData::Type expectedType, bool *ok) {
90+
if (ok)
91+
*ok = false;
92+
93+
Q_D(DspxClipboard);
94+
95+
if (d->mode == Internal) {
96+
const auto it = d->internalData.constFind(expectedType);
97+
if (it != d->internalData.cend() && it->has_value()) {
98+
if (ok)
99+
*ok = true;
100+
return it->value();
101+
}
102+
return {};
103+
}
104+
105+
const auto mimeType = DspxClipboardData::mimeType(expectedType);
106+
const QMimeData *mimeData = QGuiApplication::clipboard()->mimeData();
107+
if (!mimeData || mimeType.isEmpty() || !mimeData->hasFormat(mimeType))
108+
return {};
109+
110+
return DspxClipboardData::fromData(mimeData->data(mimeType), expectedType, ok);
111+
}
112+
113+
QList<DspxClipboardData::Type> DspxClipboard::availablePasteTypes() const {
114+
QList<DspxClipboardData::Type> types;
115+
Q_D(const DspxClipboard);
116+
117+
if (d->mode == Internal) {
118+
for (auto it = d->internalData.cbegin(); it != d->internalData.cend(); ++it) {
119+
if (it.value().has_value())
120+
types.append(it.key());
121+
}
122+
return types;
123+
}
124+
125+
const QMimeData *mimeData = QGuiApplication::clipboard()->mimeData();
126+
if (!mimeData)
127+
return types;
128+
129+
for (const auto type : supportedTypes()) {
130+
const auto mimeType = DspxClipboardData::mimeType(type);
131+
if (!mimeType.isEmpty() && mimeData->hasFormat(mimeType))
132+
types.append(type);
133+
}
134+
return types;
135+
}
136+
137+
}
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
#ifndef DIFFSCOPE_COREPLUGIN_DSPXCLIPBOARD_H
2+
#define DIFFSCOPE_COREPLUGIN_DSPXCLIPBOARD_H
3+
4+
#include <QObject>
5+
6+
#include <coreplugin/DspxClipboardData.h>
7+
8+
class QMimeData;
9+
10+
namespace Core {
11+
12+
class DspxClipboardPrivate;
13+
14+
class CORE_EXPORT DspxClipboard : public QObject {
15+
Q_OBJECT
16+
Q_PROPERTY(Mode mode READ mode WRITE setMode NOTIFY changed)
17+
Q_DECLARE_PRIVATE(DspxClipboard)
18+
19+
public:
20+
enum Mode {
21+
Global,
22+
Internal
23+
};
24+
Q_ENUM(Mode)
25+
26+
explicit DspxClipboard(QObject *parent = nullptr);
27+
~DspxClipboard() override;
28+
29+
static DspxClipboard *instance();
30+
31+
Mode mode() const;
32+
void setMode(Mode mode);
33+
34+
void copy(const QList<DspxClipboardData> &data, QMimeData *additionalMimeData);
35+
DspxClipboardData paste(DspxClipboardData::Type expectedType, bool *ok = nullptr);
36+
QList<DspxClipboardData::Type> availablePasteTypes() const;
37+
38+
Q_SIGNALS:
39+
void changed();
40+
41+
private:
42+
QScopedPointer<DspxClipboardPrivate> d_ptr;
43+
};
44+
45+
}
46+
47+
#endif //DIFFSCOPE_COREPLUGIN_DSPXCLIPBOARD_H
Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
#include "DspxClipboardData.h"
2+
3+
#include <QJsonDocument>
4+
#include <QJsonObject>
5+
#include <QJsonArray>
6+
7+
#include <opendspxserializer/serializer.h>
8+
#include <opendspxserializer/jsonconverterv1.h>
9+
10+
namespace Core {
11+
12+
QString DspxClipboardData::mimeType(Type type) {
13+
switch (type) {
14+
case Tempo:
15+
return "application/x.diffscope.clipboard.tempo+json";
16+
case Label:
17+
return "application/x.diffscope.clipboard.label+json";
18+
case Track:
19+
return "application/x.diffscope.clipboard.track+json";
20+
case Clip:
21+
return "application/x.diffscope.clipboard.clip+json";
22+
case Note:
23+
return "application/x.diffscope.clipboard.note+json";
24+
default:
25+
return {};
26+
}
27+
}
28+
29+
DspxClipboardData::Type DspxClipboardData::typeFromMimeType(const QString &mimeType, bool *ok) {
30+
if (ok)
31+
*ok = true;
32+
if (mimeType == "application/x.diffscope.clipboard.tempo+json") {
33+
return Tempo;
34+
}
35+
if (mimeType == "application/x.diffscope.clipboard.label+json") {
36+
return Label;
37+
}
38+
if (mimeType == "application/x.diffscope.clipboard.track+json") {
39+
return Track;
40+
}
41+
if (mimeType == "application/x.diffscope.clipboard.clip+json") {
42+
return Clip;
43+
}
44+
if (mimeType == "application/x.diffscope.clipboard.note+json") {
45+
return Note;
46+
}
47+
if (ok)
48+
*ok = false;
49+
return {};
50+
}
51+
52+
QByteArray DspxClipboardData::toData() const {
53+
QJsonObject json;
54+
json.insert("version", QDspx::Serializer::versionToText(QDspx::Model::V1));
55+
json.insert("playhead", m_playhead);
56+
json.insert("absolute", m_absolute);
57+
json.insert("track", m_track);
58+
QJsonArray dataArray;
59+
QDspx::SerializationErrorList errors;
60+
auto toJsonArray = [&]<Type t> {
61+
for (const auto &item : std::get<t>(m_data)) {
62+
dataArray.append(QDspx::JsonConverterV1::toJson(item, errors, {}));
63+
}
64+
};
65+
switch (type()) {
66+
case Tempo:
67+
toJsonArray.operator()<Tempo>();
68+
break;
69+
case Label:
70+
toJsonArray.operator()<Label>();
71+
break;
72+
case Track:
73+
toJsonArray.operator()<Track>();
74+
break;
75+
case Clip:
76+
toJsonArray.operator()<Clip>();
77+
break;
78+
case Note:
79+
toJsonArray.operator()<Note>();
80+
break;
81+
}
82+
json.insert("data", dataArray);
83+
return QJsonDocument(json).toJson(QJsonDocument::Compact);
84+
}
85+
86+
DspxClipboardData DspxClipboardData::fromData(const QByteArray &data, Type type, bool *ok) {
87+
QJsonParseError jsonError;
88+
auto json = QJsonDocument::fromJson(data, &jsonError).object();
89+
if (jsonError.error != QJsonParseError::NoError) {
90+
if (ok)
91+
*ok = false;
92+
return {};
93+
}
94+
auto version = QDspx::Serializer::versionFromText(json.value("version").toString(), ok);
95+
if (ok && !*ok) {
96+
return {};
97+
}
98+
DspxClipboardData result;
99+
result.m_playhead = json.value("playhead").toInt();
100+
result.m_absolute = json.value("absolute").toInt();
101+
result.m_track = json.value("track").toInt();
102+
if (!json.value("data").isArray()) {
103+
if (ok)
104+
*ok = false;
105+
return {};
106+
}
107+
auto dataArray = json.value("data").toArray();
108+
QDspx::SerializationErrorList errors;
109+
switch (version) {
110+
case QDspx::Model::V1: {
111+
auto fromJsonArrayV1 = [&]<Type t> {
112+
using T = std::variant_alternative_t<t, decltype(m_data)>;
113+
T list;
114+
for (const auto &item : dataArray) {
115+
list.append(QDspx::JsonConverterV1::fromJson<typename T::value_type>(item, errors));
116+
}
117+
result.m_data = std::move(list);
118+
};
119+
switch (type) {
120+
case Tempo:
121+
fromJsonArrayV1.operator()<Tempo>();
122+
break;
123+
case Label:
124+
fromJsonArrayV1.operator()<Label>();
125+
break;
126+
case Track:
127+
fromJsonArrayV1.operator()<Track>();
128+
break;
129+
case Clip:
130+
fromJsonArrayV1.operator()<Clip>();
131+
break;
132+
case Note:
133+
fromJsonArrayV1.operator()<Note>();
134+
break;
135+
}
136+
break;
137+
}
138+
139+
}
140+
if (errors.containsFatal() || errors.containsError()) {
141+
if (ok)
142+
*ok = false;
143+
return {};
144+
}
145+
if (ok)
146+
*ok = true;
147+
return result;
148+
}
149+
150+
}

0 commit comments

Comments
 (0)