1 | /* |
2 | SPDX-FileCopyrightText: 2014 Martin Gräßlin <mgraesslin@kde.org> |
3 | |
4 | SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL |
5 | */ |
6 | #include "dataoffer.h" |
7 | #include "datadevice.h" |
8 | #include "wayland_pointer_p.h" |
9 | // Qt |
10 | #include <QMimeDatabase> |
11 | #include <QMimeType> |
12 | // Wayland |
13 | #include <wayland-client-protocol.h> |
14 | |
15 | namespace KWayland |
16 | { |
17 | namespace Client |
18 | { |
19 | class Q_DECL_HIDDEN DataOffer::Private |
20 | { |
21 | public: |
22 | Private(wl_data_offer *offer, DataOffer *q); |
23 | WaylandPointer<wl_data_offer, wl_data_offer_destroy> dataOffer; |
24 | QList<QMimeType> mimeTypes; |
25 | DataDeviceManager::DnDActions sourceActions = DataDeviceManager::DnDAction::None; |
26 | DataDeviceManager::DnDAction selectedAction = DataDeviceManager::DnDAction::None; |
27 | |
28 | private: |
29 | void offer(const QString &mimeType); |
30 | void setAction(DataDeviceManager::DnDAction action); |
31 | static void offerCallback(void *data, wl_data_offer *dataOffer, const char *mimeType); |
32 | static void sourceActionsCallback(void *data, wl_data_offer *wl_data_offer, uint32_t source_actions); |
33 | static void actionCallback(void *data, wl_data_offer *wl_data_offer, uint32_t dnd_action); |
34 | DataOffer *q; |
35 | |
36 | static const struct wl_data_offer_listener s_listener; |
37 | }; |
38 | |
39 | #ifndef K_DOXYGEN |
40 | const struct wl_data_offer_listener DataOffer::Private::s_listener = {.offer: offerCallback, .source_actions: sourceActionsCallback, .action: actionCallback}; |
41 | #endif |
42 | |
43 | DataOffer::Private::Private(wl_data_offer *offer, DataOffer *q) |
44 | : q(q) |
45 | { |
46 | dataOffer.setup(pointer: offer); |
47 | wl_data_offer_add_listener(wl_data_offer: offer, listener: &s_listener, data: this); |
48 | } |
49 | |
50 | void DataOffer::Private::offerCallback(void *data, wl_data_offer *dataOffer, const char *mimeType) |
51 | { |
52 | auto d = reinterpret_cast<Private *>(data); |
53 | Q_ASSERT(d->dataOffer == dataOffer); |
54 | d->offer(mimeType: QString::fromUtf8(utf8: mimeType)); |
55 | } |
56 | |
57 | void DataOffer::Private::offer(const QString &mimeType) |
58 | { |
59 | QMimeDatabase db; |
60 | const auto &m = db.mimeTypeForName(nameOrAlias: mimeType); |
61 | if (m.isValid()) { |
62 | mimeTypes << m; |
63 | Q_EMIT q->mimeTypeOffered(m.name()); |
64 | } |
65 | } |
66 | |
67 | void DataOffer::Private::sourceActionsCallback(void *data, wl_data_offer *wl_data_offer, uint32_t source_actions) |
68 | { |
69 | Q_UNUSED(wl_data_offer) |
70 | DataDeviceManager::DnDActions actions; |
71 | if (source_actions & WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY) { |
72 | actions |= DataDeviceManager::DnDAction::Copy; |
73 | } |
74 | if (source_actions & WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE) { |
75 | actions |= DataDeviceManager::DnDAction::Move; |
76 | } |
77 | if (source_actions & WL_DATA_DEVICE_MANAGER_DND_ACTION_ASK) { |
78 | actions |= DataDeviceManager::DnDAction::Ask; |
79 | } |
80 | auto d = reinterpret_cast<Private *>(data); |
81 | if (d->sourceActions != actions) { |
82 | d->sourceActions = actions; |
83 | Q_EMIT d->q->sourceDragAndDropActionsChanged(); |
84 | } |
85 | } |
86 | |
87 | void DataOffer::Private::actionCallback(void *data, wl_data_offer *wl_data_offer, uint32_t dnd_action) |
88 | { |
89 | Q_UNUSED(wl_data_offer) |
90 | auto d = reinterpret_cast<Private *>(data); |
91 | switch (dnd_action) { |
92 | case WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY: |
93 | d->setAction(DataDeviceManager::DnDAction::Copy); |
94 | break; |
95 | case WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE: |
96 | d->setAction(DataDeviceManager::DnDAction::Move); |
97 | break; |
98 | case WL_DATA_DEVICE_MANAGER_DND_ACTION_ASK: |
99 | d->setAction(DataDeviceManager::DnDAction::Ask); |
100 | break; |
101 | case WL_DATA_DEVICE_MANAGER_DND_ACTION_NONE: |
102 | d->setAction(DataDeviceManager::DnDAction::None); |
103 | break; |
104 | default: |
105 | Q_UNREACHABLE(); |
106 | } |
107 | } |
108 | |
109 | void DataOffer::Private::setAction(DataDeviceManager::DnDAction action) |
110 | { |
111 | if (action == selectedAction) { |
112 | return; |
113 | } |
114 | selectedAction = action; |
115 | Q_EMIT q->selectedDragAndDropActionChanged(); |
116 | } |
117 | |
118 | DataOffer::DataOffer(DataDevice *parent, wl_data_offer *dataOffer) |
119 | : QObject(parent) |
120 | , d(new Private(dataOffer, this)) |
121 | { |
122 | } |
123 | |
124 | DataOffer::~DataOffer() |
125 | { |
126 | release(); |
127 | } |
128 | |
129 | void DataOffer::release() |
130 | { |
131 | d->dataOffer.release(); |
132 | } |
133 | |
134 | void DataOffer::destroy() |
135 | { |
136 | d->dataOffer.destroy(); |
137 | } |
138 | |
139 | bool DataOffer::isValid() const |
140 | { |
141 | return d->dataOffer.isValid(); |
142 | } |
143 | |
144 | QList<QMimeType> DataOffer::offeredMimeTypes() const |
145 | { |
146 | return d->mimeTypes; |
147 | } |
148 | |
149 | void DataOffer::accept(const QMimeType &mimeType, quint32 serial) |
150 | { |
151 | accept(mimeType: mimeType.name(), serial); |
152 | } |
153 | |
154 | void DataOffer::accept(const QString &mimeType, quint32 serial) |
155 | { |
156 | wl_data_offer_accept(wl_data_offer: d->dataOffer, serial, mime_type: mimeType.toUtf8().constData()); |
157 | } |
158 | |
159 | void DataOffer::receive(const QMimeType &mimeType, qint32 fd) |
160 | { |
161 | receive(mimeType: mimeType.name(), fd); |
162 | } |
163 | |
164 | void DataOffer::receive(const QString &mimeType, qint32 fd) |
165 | { |
166 | Q_ASSERT(isValid()); |
167 | wl_data_offer_receive(wl_data_offer: d->dataOffer, mime_type: mimeType.toUtf8().constData(), fd); |
168 | } |
169 | |
170 | DataOffer::operator wl_data_offer *() |
171 | { |
172 | return d->dataOffer; |
173 | } |
174 | |
175 | DataOffer::operator wl_data_offer *() const |
176 | { |
177 | return d->dataOffer; |
178 | } |
179 | |
180 | void DataOffer::dragAndDropFinished() |
181 | { |
182 | Q_ASSERT(isValid()); |
183 | if (wl_proxy_get_version(proxy: d->dataOffer) < WL_DATA_OFFER_FINISH_SINCE_VERSION) { |
184 | return; |
185 | } |
186 | wl_data_offer_finish(wl_data_offer: d->dataOffer); |
187 | } |
188 | |
189 | DataDeviceManager::DnDActions DataOffer::sourceDragAndDropActions() const |
190 | { |
191 | return d->sourceActions; |
192 | } |
193 | |
194 | void DataOffer::setDragAndDropActions(DataDeviceManager::DnDActions supported, DataDeviceManager::DnDAction preferred) |
195 | { |
196 | if (wl_proxy_get_version(proxy: d->dataOffer) < WL_DATA_OFFER_SET_ACTIONS_SINCE_VERSION) { |
197 | return; |
198 | } |
199 | auto toWayland = [](DataDeviceManager::DnDAction action) { |
200 | switch (action) { |
201 | case DataDeviceManager::DnDAction::Copy: |
202 | return WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY; |
203 | case DataDeviceManager::DnDAction::Move: |
204 | return WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE; |
205 | case DataDeviceManager::DnDAction::Ask: |
206 | return WL_DATA_DEVICE_MANAGER_DND_ACTION_ASK; |
207 | case DataDeviceManager::DnDAction::None: |
208 | return WL_DATA_DEVICE_MANAGER_DND_ACTION_NONE; |
209 | default: |
210 | Q_UNREACHABLE(); |
211 | } |
212 | }; |
213 | uint32_t wlSupported = WL_DATA_DEVICE_MANAGER_DND_ACTION_NONE; |
214 | if (supported.testFlag(flag: DataDeviceManager::DnDAction::Copy)) { |
215 | wlSupported |= toWayland(DataDeviceManager::DnDAction::Copy); |
216 | } |
217 | if (supported.testFlag(flag: DataDeviceManager::DnDAction::Move)) { |
218 | wlSupported |= toWayland(DataDeviceManager::DnDAction::Move); |
219 | } |
220 | if (supported.testFlag(flag: DataDeviceManager::DnDAction::Ask)) { |
221 | wlSupported |= toWayland(DataDeviceManager::DnDAction::Ask); |
222 | } |
223 | wl_data_offer_set_actions(wl_data_offer: d->dataOffer, dnd_actions: wlSupported, preferred_action: toWayland(preferred)); |
224 | } |
225 | |
226 | DataDeviceManager::DnDAction DataOffer::selectedDragAndDropAction() const |
227 | { |
228 | return d->selectedAction; |
229 | } |
230 | |
231 | } |
232 | } |
233 | |
234 | #include "moc_dataoffer.cpp" |
235 | |