Mercurial > emacs
comparison lisp/x-dnd.el @ 88155:d7ddb3e565de
sync with trunk
author | Henrik Enberg <henrik.enberg@telia.com> |
---|---|
date | Mon, 16 Jan 2006 00:03:54 +0000 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
88154:8ce476d3ba36 | 88155:d7ddb3e565de |
---|---|
1 ;;; x-dnd.el --- drag and drop support for X. | |
2 | |
3 ;; Copyright (C) 2004, 2005 Free Software Foundation, Inc. | |
4 | |
5 ;; Author: Jan Dj,Ad(Brv <jan.h.d@swipnet.se> | |
6 ;; Maintainer: FSF | |
7 ;; Keywords: window, drag, drop | |
8 | |
9 ;; This file is part of GNU Emacs. | |
10 | |
11 ;; GNU Emacs is free software; you can redistribute it and/or modify | |
12 ;; it under the terms of the GNU General Public License as published by | |
13 ;; the Free Software Foundation; either version 2, or (at your option) | |
14 ;; any later version. | |
15 | |
16 ;; GNU Emacs is distributed in the hope that it will be useful, | |
17 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of | |
18 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
19 ;; GNU General Public License for more details. | |
20 | |
21 ;; You should have received a copy of the GNU General Public License | |
22 ;; along with GNU Emacs; see the file COPYING. If not, write to the | |
23 ;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, | |
24 ;; Boston, MA 02110-1301, USA. | |
25 | |
26 ;;; Commentary: | |
27 | |
28 ;; This file provides the drop part only. Currently supported protocols | |
29 ;; are XDND, Motif and the old KDE 1.x protocol. | |
30 | |
31 ;;; Code: | |
32 | |
33 (require 'dnd) | |
34 | |
35 ;;; Customizable variables | |
36 (defcustom x-dnd-test-function 'x-dnd-default-test-function | |
37 "The function drag and drop uses to determine if to accept or reject a drop. | |
38 The function takes three arguments, WINDOW ACTION and TYPES. | |
39 WINDOW is where the mouse is when the function is called. WINDOW may be a | |
40 frame if the mouse isn't over a real window (i.e. menu bar, tool bar or | |
41 scroll bar). ACTION is the suggested action from the drag and drop source, | |
42 one of the symbols move, copy link or ask. TYPES is a list of available types | |
43 for the drop. | |
44 | |
45 The function shall return nil to reject the drop or a cons with two values, | |
46 the wanted action as car and the wanted type as cdr. The wanted action | |
47 can be copy, move, link, ask or private. | |
48 The default value for this variable is `x-dnd-default-test-function'." | |
49 :version "22.1" | |
50 :type 'symbol | |
51 :group 'x) | |
52 | |
53 | |
54 | |
55 (defcustom x-dnd-types-alist | |
56 '( | |
57 ("text/uri-list" . x-dnd-handle-uri-list) | |
58 ("text/x-moz-url" . x-dnd-handle-moz-url) | |
59 ("_NETSCAPE_URL" . x-dnd-handle-uri-list) | |
60 ("FILE_NAME" . x-dnd-handle-file-name) | |
61 ("UTF8_STRING" . x-dnd-insert-utf8-text) | |
62 ("text/plain;charset=UTF-8" . x-dnd-insert-utf8-text) | |
63 ("text/plain;charset=utf-8" . x-dnd-insert-utf8-text) | |
64 ("text/unicode" . x-dnd-insert-utf16-text) | |
65 ("text/plain" . dnd-insert-text) | |
66 ("COMPOUND_TEXT" . x-dnd-insert-ctext) | |
67 ("STRING" . dnd-insert-text) | |
68 ("TEXT" . dnd-insert-text) | |
69 ) | |
70 "Which function to call to handle a drop of that type. | |
71 If the type for the drop is not present, or the function is nil, | |
72 the drop is rejected. The function takes three arguments, WINDOW, ACTION | |
73 and DATA. WINDOW is where the drop occured, ACTION is the action for | |
74 this drop (copy, move, link, private or ask) as determined by a previous | |
75 call to `x-dnd-test-function'. DATA is the drop data. | |
76 The function shall return the action used (copy, move, link or private) if drop | |
77 is successful, nil if not." | |
78 :version "22.1" | |
79 :type 'alist | |
80 :group 'x) | |
81 | |
82 (defcustom x-dnd-known-types | |
83 '("text/uri-list" | |
84 "text/x-moz-url" | |
85 "_NETSCAPE_URL" | |
86 "FILE_NAME" | |
87 "UTF8_STRING" | |
88 "text/plain;charset=UTF-8" | |
89 "text/plain;charset=utf-8" | |
90 "text/unicode" | |
91 "text/plain" | |
92 "COMPOUND_TEXT" | |
93 "STRING" | |
94 "TEXT" | |
95 ) | |
96 "The types accepted by default for dropped data. | |
97 The types are chosen in the order they appear in the list." | |
98 :version "22.1" | |
99 :type '(repeat string) | |
100 :group 'x | |
101 ) | |
102 | |
103 ;; Internal variables | |
104 | |
105 (defvar x-dnd-current-state nil | |
106 "The current state for a drop. | |
107 This is an alist with one entry for each display. The value for each display | |
108 is a vector that contains the state for drag and drop for that display. | |
109 Elements in the vector are: | |
110 Last buffer drag was in, | |
111 last window drag was in, | |
112 types available for drop, | |
113 the action suggested by the source, | |
114 the type we want for the drop, | |
115 the action we want for the drop, | |
116 any protocol specific data.") | |
117 | |
118 (defvar x-dnd-empty-state [nil nil nil nil nil nil nil]) | |
119 | |
120 | |
121 | |
122 (defun x-dnd-init-frame (&optional frame) | |
123 "Setup drag and drop for FRAME (i.e. create appropriate properties)." | |
124 (x-dnd-init-xdnd-for-frame frame) | |
125 (x-dnd-init-motif-for-frame frame)) | |
126 | |
127 (defun x-dnd-get-state-cons-for-frame (frame-or-window) | |
128 "Return the entry in x-dnd-current-state for a frame or window." | |
129 (let* ((frame (if (framep frame-or-window) frame-or-window | |
130 (window-frame frame-or-window))) | |
131 (display (frame-parameter frame 'display))) | |
132 (if (not (assoc display x-dnd-current-state)) | |
133 (push (cons display (copy-sequence x-dnd-empty-state)) | |
134 x-dnd-current-state)) | |
135 (assoc display x-dnd-current-state))) | |
136 | |
137 (defun x-dnd-get-state-for-frame (frame-or-window) | |
138 "Return the state in x-dnd-current-state for a frame or window." | |
139 (cdr (x-dnd-get-state-cons-for-frame frame-or-window))) | |
140 | |
141 (defun x-dnd-default-test-function (window action types) | |
142 "The default test function for drag and drop. | |
143 WINDOW is where the mouse is when this function is called. It may be a frame | |
144 if the mouse is over the menu bar, scroll bar or tool bar. | |
145 ACTION is the suggested action from the source, and TYPES are the | |
146 types the drop data can have. This function only accepts drops with | |
147 types in `x-dnd-known-types'. It always returns the action private." | |
148 (let ((type (x-dnd-choose-type types))) | |
149 (when type (cons 'private type)))) | |
150 | |
151 | |
152 (defun x-dnd-current-type (frame-or-window) | |
153 "Return the type we want the DND data to be in for the current drop. | |
154 FRAME-OR-WINDOW is the frame or window that the mouse is over." | |
155 (aref (x-dnd-get-state-for-frame frame-or-window) 4)) | |
156 | |
157 (defun x-dnd-forget-drop (frame-or-window) | |
158 "Remove all state for the last drop. | |
159 FRAME-OR-WINDOW is the frame or window that the mouse is over." | |
160 (setcdr (x-dnd-get-state-cons-for-frame frame-or-window) | |
161 (copy-sequence x-dnd-empty-state))) | |
162 | |
163 (defun x-dnd-maybe-call-test-function (window action) | |
164 "Call `x-dnd-test-function' if something has changed. | |
165 WINDOW is the window the mouse is over. ACTION is the suggested | |
166 action from the source. If nothing has changed, return the last | |
167 action and type we got from `x-dnd-test-function'." | |
168 (let ((buffer (when (and (windowp window) (window-live-p window)) | |
169 (window-buffer window))) | |
170 (current-state (x-dnd-get-state-for-frame window))) | |
171 (when (or (not (equal buffer (aref current-state 0))) | |
172 (not (equal window (aref current-state 1))) | |
173 (not (equal action (aref current-state 3)))) | |
174 (save-excursion | |
175 (when buffer (set-buffer buffer)) | |
176 (let* ((action-type (funcall x-dnd-test-function | |
177 window | |
178 action | |
179 (aref current-state 2))) | |
180 (handler (cdr (assoc (cdr action-type) x-dnd-types-alist)))) | |
181 ;; Ignore action-type if we have no handler. | |
182 (setq current-state | |
183 (x-dnd-save-state window | |
184 action | |
185 (when handler action-type))))))) | |
186 (let ((current-state (x-dnd-get-state-for-frame window))) | |
187 (cons (aref current-state 5) | |
188 (aref current-state 4)))) | |
189 | |
190 (defun x-dnd-save-state (window action action-type &optional types extra-data) | |
191 "Save the state of the current drag and drop. | |
192 WINDOW is the window the mouse is over. ACTION is the action suggested | |
193 by the source. ACTION-TYPE is the result of calling `x-dnd-test-function'. | |
194 If given, TYPES are the types for the drop data that the source supports. | |
195 EXTRA-DATA is data needed for a specific protocol." | |
196 (let ((current-state (x-dnd-get-state-for-frame window))) | |
197 (aset current-state 5 (car action-type)) | |
198 (aset current-state 4 (cdr action-type)) | |
199 (aset current-state 3 action) | |
200 (when types (aset current-state 2 types)) | |
201 (when extra-data (aset current-state 6 extra-data)) | |
202 (aset current-state 1 window) | |
203 (aset current-state 0 (if (and (windowp window) | |
204 (window-live-p window)) | |
205 (window-buffer window) nil)) | |
206 (setcdr (x-dnd-get-state-cons-for-frame window) current-state))) | |
207 | |
208 | |
209 (defun x-dnd-handle-moz-url (window action data) | |
210 "Handle one item of type text/x-moz-url. | |
211 WINDOW is the window where the drop happened. ACTION is ignored. | |
212 DATA is the moz-url, which is formatted as two strings separated by \r\n. | |
213 The first string is the URL, the second string is the title of that URL. | |
214 DATA is encoded in utf-16. Decode the URL and call `x-dnd-handle-uri-list'." | |
215 ;; Mozilla and applications based on it (Galeon for example) uses | |
216 ;; text/unicode, but it is impossible to tell if it is le or be. Use what | |
217 ;; the machine Emacs runs on use. This looses if dropping between machines | |
218 ;; with different endian, but it is the best we can do. | |
219 (let* ((coding (if (eq (byteorder) ?B) 'utf-16be 'utf-16le)) | |
220 (string (decode-coding-string data coding)) | |
221 (strings (split-string string "[\r\n]" t)) | |
222 ;; Can one drop more than one moz-url ?? Assume not. | |
223 (url (car strings)) | |
224 (title (car (cdr strings)))) | |
225 (x-dnd-handle-uri-list window action url))) | |
226 | |
227 (defun x-dnd-insert-utf8-text (window action text) | |
228 "Decode the UTF-8 text and insert it at point. | |
229 TEXT is the text as a string, WINDOW is the window where the drop happened." | |
230 (dnd-insert-text window action (decode-coding-string text 'utf-8))) | |
231 | |
232 (defun x-dnd-insert-utf16-text (window action text) | |
233 "Decode the UTF-16 text and insert it at point. | |
234 TEXT is the text as a string, WINDOW is the window where the drop happened." | |
235 ;; See comment in x-dnd-handle-moz-url about coding. | |
236 (let ((coding (if (eq (byteorder) ?B) 'utf-16be 'utf-16le))) | |
237 (dnd-insert-text window action (decode-coding-string text coding)))) | |
238 | |
239 (defun x-dnd-insert-ctext (window action text) | |
240 "Decode the compound text and insert it at point. | |
241 TEXT is the text as a string, WINDOW is the window where the drop happened." | |
242 (dnd-insert-text window action | |
243 (decode-coding-string text | |
244 'compound-text-with-extensions))) | |
245 | |
246 (defun x-dnd-handle-uri-list (window action string) | |
247 "Split an uri-list into separate URIs and call `dnd-handle-one-url'. | |
248 WINDOW is the window where the drop happened. | |
249 STRING is the uri-list as a string. The URIs are separated by \r\n." | |
250 (let ((uri-list (split-string string "[\0\r\n]" t)) | |
251 retval) | |
252 (dolist (bf uri-list) | |
253 ;; If one URL is handeled, treat as if the whole drop succeeded. | |
254 (let ((did-action (dnd-handle-one-url window action bf))) | |
255 (when did-action (setq retval did-action)))) | |
256 retval)) | |
257 | |
258 (defun x-dnd-handle-file-name (window action string) | |
259 "Prepend file:// to file names and call `dnd-handle-one-url'. | |
260 WINDOW is the window where the drop happened. | |
261 STRING is the file names as a string, separated by nulls." | |
262 (let ((uri-list (split-string string "[\0\r\n]" t)) | |
263 retval) | |
264 (dolist (bf uri-list) | |
265 ;; If one URL is handeled, treat as if the whole drop succeeded. | |
266 (let* ((file-uri (concat "file://" bf)) | |
267 (did-action (dnd-handle-one-url window action file-uri))) | |
268 (when did-action (setq retval did-action)))) | |
269 retval)) | |
270 | |
271 | |
272 (defun x-dnd-choose-type (types &optional known-types) | |
273 "Choose which type we want to receive for the drop. | |
274 TYPES are the types the source of the drop offers, a vector of type names | |
275 as strings or symbols. Select among the types in `x-dnd-known-types' or | |
276 KNOWN-TYPES if given, and return that type name. | |
277 If no suitable type is found, return nil." | |
278 (let* ((known-list (or known-types x-dnd-known-types)) | |
279 (first-known-type (car known-list)) | |
280 (types-array types) | |
281 (found (when first-known-type | |
282 (catch 'done | |
283 (dotimes (i (length types-array)) | |
284 (let* ((type (aref types-array i)) | |
285 (typename (if (symbolp type) | |
286 (symbol-name type) type))) | |
287 (when (equal first-known-type typename) | |
288 (throw 'done first-known-type)))) | |
289 nil)))) | |
290 | |
291 (if (and (not found) (cdr known-list)) | |
292 (x-dnd-choose-type types (cdr known-list)) | |
293 found))) | |
294 | |
295 (defun x-dnd-drop-data (event frame window data type) | |
296 "Drop one data item onto a frame. | |
297 EVENT is the client message for the drop, FRAME is the frame the drop occurred | |
298 on. WINDOW is the window of FRAME where the drop happened. DATA is the data | |
299 received from the source, and type is the type for DATA, see | |
300 `x-dnd-types-alist'). | |
301 | |
302 Returns the action used (move, copy, link, private) if drop was successful, | |
303 nil if not." | |
304 (let* ((type-info (assoc type x-dnd-types-alist)) | |
305 (handler (cdr type-info)) | |
306 (state (x-dnd-get-state-for-frame frame)) | |
307 (action (aref state 5)) | |
308 (w (posn-window (event-start event)))) | |
309 (when handler | |
310 (if (and (windowp w) (window-live-p w) | |
311 (not (window-minibuffer-p w)) | |
312 (not (window-dedicated-p w))) | |
313 ;; If dropping in an ordinary window which we could use, | |
314 ;; let dnd-open-file-other-window specify what to do. | |
315 (progn | |
316 (goto-char (posn-point (event-start event))) | |
317 (funcall handler window action data)) | |
318 ;; If we can't display the file here, | |
319 ;; make a new window for it. | |
320 (let ((dnd-open-file-other-window t)) | |
321 (select-frame frame) | |
322 (funcall handler window action data)))))) | |
323 | |
324 (defun x-dnd-handle-drag-n-drop-event (event) | |
325 "Receive drag and drop events (X client messages). | |
326 Currently XDND, Motif and old KDE 1.x protocols are recognized." | |
327 (interactive "e") | |
328 (let* ((client-message (car (cdr (cdr event)))) | |
329 (window (posn-window (event-start event))) | |
330 (message-atom (aref client-message 0)) | |
331 (frame (aref client-message 1)) | |
332 (format (aref client-message 2)) | |
333 (data (aref client-message 3))) | |
334 | |
335 (cond ((equal "DndProtocol" message-atom) ; Old KDE 1.x. | |
336 (x-dnd-handle-old-kde event frame window message-atom format data)) | |
337 | |
338 ((equal "_MOTIF_DRAG_AND_DROP_MESSAGE" message-atom) ; Motif | |
339 (x-dnd-handle-motif event frame window message-atom format data)) | |
340 | |
341 ((and (> (length message-atom) 4) ; XDND protocol. | |
342 (equal "Xdnd" (substring message-atom 0 4))) | |
343 (x-dnd-handle-xdnd event frame window message-atom format data))))) | |
344 | |
345 | |
346 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
347 ;;; Old KDE protocol. Only dropping of files. | |
348 | |
349 (defun x-dnd-handle-old-kde (event frame window message format data) | |
350 "Open the files in a KDE 1.x drop." | |
351 (let ((values (x-window-property "DndSelection" frame nil 0 t))) | |
352 (x-dnd-handle-uri-list window 'private | |
353 (replace-regexp-in-string "\0$" "" values)))) | |
354 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
355 | |
356 | |
357 | |
358 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
359 ;;; XDND protocol. | |
360 | |
361 (defvar x-dnd-xdnd-to-action | |
362 '(("XdndActionPrivate" . private) | |
363 ("XdndActionCopy" . copy) | |
364 ("XdndActionMove" . move) | |
365 ("XdndActionLink" . link) | |
366 ("XdndActionAsk" . ask)) | |
367 "Mapping from XDND action types to lisp symbols.") | |
368 | |
369 (defun x-dnd-init-xdnd-for-frame (frame) | |
370 "Set the XdndAware property for FRAME to indicate that we do XDND." | |
371 (x-change-window-property "XdndAware" | |
372 '(5) ;; The version of XDND we support. | |
373 frame "ATOM" 32 t)) | |
374 | |
375 (defun x-dnd-get-drop-width-height (frame w accept) | |
376 "Return the widht/height to be sent in a XDndStatus message. | |
377 FRAME is the frame and W is the window where the drop happened. | |
378 If ACCEPT is nil return 0 (empty rectangle), | |
379 otherwise if W is a window, return its widht/height, | |
380 otherwise return the frame width/height." | |
381 (if accept | |
382 (if (windowp w) ;; w is not a window if dropping on the menu bar, | |
383 ;; scroll bar or tool bar. | |
384 (let ((edges (window-inside-pixel-edges w))) | |
385 (cons | |
386 (- (nth 2 edges) (nth 0 edges)) ;; right - left | |
387 (- (nth 3 edges) (nth 1 edges)))) ;; bottom - top | |
388 (cons (frame-pixel-width frame) | |
389 (frame-pixel-height frame))) | |
390 0)) | |
391 | |
392 (defun x-dnd-get-drop-x-y (frame w) | |
393 "Return the x/y coordinates to be sent in a XDndStatus message. | |
394 Coordinates are required to be absolute. | |
395 FRAME is the frame and W is the window where the drop happened. | |
396 If W is a window, return its absolute corrdinates, | |
397 otherwise return the frame coordinates." | |
398 (let* ((frame-left (frame-parameter frame 'left)) | |
399 ;; If the frame is outside the display, frame-left looks like | |
400 ;; '(0 -16). Extract the -16. | |
401 (frame-real-left (if (consp frame-left) (car (cdr frame-left)) | |
402 frame-left)) | |
403 (frame-top (frame-parameter frame 'top)) | |
404 (frame-real-top (if (consp frame-top) (car (cdr frame-top)) | |
405 frame-top))) | |
406 (if (windowp w) | |
407 (let ((edges (window-inside-pixel-edges w))) | |
408 (cons | |
409 (+ frame-real-left (nth 0 edges)) | |
410 (+ frame-real-top (nth 1 edges)))) | |
411 (cons frame-real-left frame-real-top)))) | |
412 | |
413 (defun x-dnd-handle-xdnd (event frame window message format data) | |
414 "Receive one XDND event (client message) and send the appropriate reply. | |
415 EVENT is the client message. FRAME is where the mouse is now. | |
416 WINDOW is the window within FRAME where the mouse is now. | |
417 FORMAT is 32 (not used). MESSAGE is the data part of an XClientMessageEvent." | |
418 (cond ((equal "XdndEnter" message) | |
419 (let* ((flags (aref data 1)) | |
420 (version (and (consp flags) (ash (car flags) -8))) | |
421 (more-than-3 (and (consp flags) (cdr flags))) | |
422 (dnd-source (aref data 0))) | |
423 (if version ;; If flags is bad, version will be nil. | |
424 (x-dnd-save-state | |
425 window nil nil | |
426 (if (> more-than-3 0) | |
427 (x-window-property "XdndTypeList" | |
428 frame "AnyPropertyType" | |
429 dnd-source nil t) | |
430 (vector (x-get-atom-name (aref data 2)) | |
431 (x-get-atom-name (aref data 3)) | |
432 (x-get-atom-name (aref data 4)))))))) | |
433 | |
434 ((equal "XdndPosition" message) | |
435 (let* ((x (car (aref data 2))) | |
436 (y (cdr (aref data 2))) | |
437 (action (x-get-atom-name (aref data 4))) | |
438 (dnd-source (aref data 0)) | |
439 (dnd-time (aref data 3)) | |
440 (action-type (x-dnd-maybe-call-test-function | |
441 window | |
442 (cdr (assoc action x-dnd-xdnd-to-action)))) | |
443 (reply-action (car (rassoc (car action-type) | |
444 x-dnd-xdnd-to-action))) | |
445 (accept ;; 1 = accept, 0 = reject | |
446 (if (and reply-action action-type) 1 0)) | |
447 (list-to-send | |
448 (list (string-to-number | |
449 (frame-parameter frame 'outer-window-id)) | |
450 accept ;; 1 = Accept, 0 = reject. | |
451 (x-dnd-get-drop-x-y frame window) | |
452 (x-dnd-get-drop-width-height | |
453 frame window (eq accept 1)) | |
454 (or reply-action 0) | |
455 ))) | |
456 (x-send-client-message | |
457 frame dnd-source frame "XdndStatus" 32 list-to-send) | |
458 )) | |
459 | |
460 ((equal "XdndLeave" message) | |
461 (x-dnd-forget-drop window)) | |
462 | |
463 ((equal "XdndDrop" message) | |
464 (if (windowp window) (select-window window)) | |
465 (let* ((dnd-source (aref data 0)) | |
466 (value (and (x-dnd-current-type window) | |
467 (x-get-selection-internal | |
468 'XdndSelection | |
469 (intern (x-dnd-current-type window))))) | |
470 success action ret-action) | |
471 | |
472 (setq action (if value | |
473 (condition-case info | |
474 (x-dnd-drop-data event frame window value | |
475 (x-dnd-current-type window)) | |
476 (error | |
477 (message "Error: %s" info) | |
478 nil)))) | |
479 | |
480 (setq success (if action 1 0)) | |
481 (setq ret-action | |
482 (if (eq success 1) | |
483 (or (car (rassoc action x-dnd-xdnd-to-action)) | |
484 "XdndActionPrivate") | |
485 0)) | |
486 | |
487 (x-send-client-message | |
488 frame dnd-source frame "XdndFinished" 32 | |
489 (list (string-to-number (frame-parameter frame 'outer-window-id)) | |
490 success ;; 1 = Success, 0 = Error | |
491 (if success "XdndActionPrivate" 0) | |
492 )) | |
493 (x-dnd-forget-drop window))) | |
494 | |
495 (t (error "Unknown XDND message %s %s" message data)))) | |
496 | |
497 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
498 ;;; Motif protocol. | |
499 | |
500 (defun x-dnd-init-motif-for-frame (frame) | |
501 "Set _MOTIF_DRAG_RECEIVER_INFO for FRAME to indicate that we do Motif DND." | |
502 (x-change-window-property "_MOTIF_DRAG_RECEIVER_INFO" | |
503 (list | |
504 (byteorder) | |
505 0 ; The Motif DND version. | |
506 5 ; We want drag dynamic. | |
507 0 0 0 0 0 0 0 | |
508 0 0 0 0 0 0) ; Property must be 16 bytes. | |
509 frame "_MOTIF_DRAG_RECEIVER_INFO" 8 t)) | |
510 | |
511 (defun x-dnd-get-motif-value (data offset size byteorder) | |
512 (cond ((eq size 2) | |
513 (if (eq byteorder ?l) | |
514 (+ (ash (aref data (1+ offset)) 8) | |
515 (aref data offset)) | |
516 (+ (ash (aref data offset) 8) | |
517 (aref data (1+ offset))))) | |
518 | |
519 ((eq size 4) | |
520 (if (eq byteorder ?l) | |
521 (cons (+ (ash (aref data (+ 3 offset)) 8) | |
522 (aref data (+ 2 offset))) | |
523 (+ (ash (aref data (1+ offset)) 8) | |
524 (aref data offset))) | |
525 (cons (+ (ash (aref data offset) 8) | |
526 (aref data (1+ offset))) | |
527 (+ (ash (aref data (+ 2 offset)) 8) | |
528 (aref data (+ 3 offset)))))))) | |
529 | |
530 (defun x-dnd-motif-value-to-list (value size byteorder) | |
531 (let ((bytes (cond ((eq size 2) | |
532 (list (logand (lsh value -8) ?\xff) | |
533 (logand value ?\xff))) | |
534 | |
535 ((eq size 4) | |
536 (if (consp value) | |
537 (list (logand (lsh (car value) -8) ?\xff) | |
538 (logand (car value) ?\xff) | |
539 (logand (lsh (cdr value) -8) ?\xff) | |
540 (logand (cdr value) ?\xff)) | |
541 (list (logand (lsh value -24) ?\xff) | |
542 (logand (lsh value -16) ?\xff) | |
543 (logand (lsh value -8) ?\xff) | |
544 (logand value ?\xff))))))) | |
545 (if (eq byteorder ?l) | |
546 (reverse bytes) | |
547 bytes))) | |
548 | |
549 | |
550 (defvar x-dnd-motif-message-types | |
551 '((0 . XmTOP_LEVEL_ENTER) | |
552 (1 . XmTOP_LEVEL_LEAVE) | |
553 (2 . XmDRAG_MOTION) | |
554 (3 . XmDROP_SITE_ENTER) | |
555 (4 . XmDROP_SITE_LEAVE) | |
556 (5 . XmDROP_START) | |
557 (6 . XmDROP_FINISH) | |
558 (7 . XmDRAG_DROP_FINISH) | |
559 (8 . XmOPERATION_CHANGED)) | |
560 "Mapping from numbers to Motif DND message types.") | |
561 | |
562 (defvar x-dnd-motif-to-action | |
563 '((1 . move) | |
564 (2 . copy) | |
565 (3 . link) ; Both 3 and 4 has been seen as link. | |
566 (4 . link) | |
567 (2 . private)) ; Motif does not have private, so use copy for private. | |
568 "Mapping from number to operation for Motif DND.") | |
569 | |
570 (defun x-dnd-handle-motif (event frame window message-atom format data) | |
571 (let* ((message-type (cdr (assoc (aref data 0) x-dnd-motif-message-types))) | |
572 (source-byteorder (aref data 1)) | |
573 (my-byteorder (byteorder)) | |
574 (source-flags (x-dnd-get-motif-value data 2 2 source-byteorder)) | |
575 (source-action (cdr (assoc (logand ?\xF source-flags) | |
576 x-dnd-motif-to-action)))) | |
577 | |
578 (cond ((eq message-type 'XmTOP_LEVEL_ENTER) | |
579 (let* ((dnd-source (x-dnd-get-motif-value | |
580 data 8 4 source-byteorder)) | |
581 (selection-atom (x-dnd-get-motif-value | |
582 data 12 4 source-byteorder)) | |
583 (atom-name (x-get-atom-name selection-atom)) | |
584 (types (when atom-name | |
585 (x-get-selection-internal (intern atom-name) | |
586 'TARGETS)))) | |
587 (x-dnd-forget-drop frame) | |
588 (when types (x-dnd-save-state window nil nil | |
589 types | |
590 dnd-source)))) | |
591 | |
592 ;; Can not forget drop here, LEAVE comes before DROP_START and | |
593 ;; we need the state in DROP_START. | |
594 ((eq message-type 'XmTOP_LEVEL_LEAVE) | |
595 nil) | |
596 | |
597 ((eq message-type 'XmDRAG_MOTION) | |
598 (let* ((state (x-dnd-get-state-for-frame frame)) | |
599 (timestamp (x-dnd-motif-value-to-list | |
600 (x-dnd-get-motif-value data 4 4 | |
601 source-byteorder) | |
602 4 my-byteorder)) | |
603 (x (x-dnd-motif-value-to-list | |
604 (x-dnd-get-motif-value data 8 2 source-byteorder) | |
605 2 my-byteorder)) | |
606 (y (x-dnd-motif-value-to-list | |
607 (x-dnd-get-motif-value data 10 2 source-byteorder) | |
608 2 my-byteorder)) | |
609 (dnd-source (aref state 6)) | |
610 (first-move (not (aref state 3))) | |
611 (action-type (x-dnd-maybe-call-test-function | |
612 window | |
613 source-action)) | |
614 (reply-action (car (rassoc (car action-type) | |
615 x-dnd-motif-to-action))) | |
616 (reply-flags | |
617 (x-dnd-motif-value-to-list | |
618 (if reply-action | |
619 (+ reply-action | |
620 ?\x30 ; 30: valid drop site | |
621 ?\x700) ; 700: can do copy, move or link | |
622 ?\x30) ; 30: drop site, but noop. | |
623 2 my-byteorder)) | |
624 (reply (append | |
625 (list | |
626 (+ ?\x80 ; 0x80 indicates a reply. | |
627 (if first-move | |
628 3 ; First time, reply is SITE_ENTER. | |
629 2)) ; Not first time, reply is DRAG_MOTION. | |
630 my-byteorder) | |
631 reply-flags | |
632 timestamp | |
633 x | |
634 y))) | |
635 (x-send-client-message frame | |
636 dnd-source | |
637 frame | |
638 "_MOTIF_DRAG_AND_DROP_MESSAGE" | |
639 8 | |
640 reply))) | |
641 | |
642 ((eq message-type 'XmOPERATION_CHANGED) | |
643 (let* ((state (x-dnd-get-state-for-frame frame)) | |
644 (timestamp (x-dnd-motif-value-to-list | |
645 (x-dnd-get-motif-value data 4 4 source-byteorder) | |
646 4 my-byteorder)) | |
647 (dnd-source (aref state 6)) | |
648 (action-type (x-dnd-maybe-call-test-function | |
649 window | |
650 source-action)) | |
651 (reply-action (car (rassoc (car action-type) | |
652 x-dnd-motif-to-action))) | |
653 (reply-flags | |
654 (x-dnd-motif-value-to-list | |
655 (if reply-action | |
656 (+ reply-action | |
657 ?\x30 ; 30: valid drop site | |
658 ?\x700) ; 700: can do copy, move or link | |
659 ?\x30) ; 30: drop site, but noop | |
660 2 my-byteorder)) | |
661 (reply (append | |
662 (list | |
663 (+ ?\x80 ; 0x80 indicates a reply. | |
664 8) ; 8 is OPERATION_CHANGED | |
665 my-byteorder) | |
666 reply-flags | |
667 timestamp))) | |
668 (x-send-client-message frame | |
669 dnd-source | |
670 frame | |
671 "_MOTIF_DRAG_AND_DROP_MESSAGE" | |
672 8 | |
673 reply))) | |
674 | |
675 ((eq message-type 'XmDROP_START) | |
676 (let* ((x (x-dnd-motif-value-to-list | |
677 (x-dnd-get-motif-value data 8 2 source-byteorder) | |
678 2 my-byteorder)) | |
679 (y (x-dnd-motif-value-to-list | |
680 (x-dnd-get-motif-value data 10 2 source-byteorder) | |
681 2 my-byteorder)) | |
682 (selection-atom (x-dnd-get-motif-value | |
683 data 12 4 source-byteorder)) | |
684 (atom-name (x-get-atom-name selection-atom)) | |
685 (dnd-source (x-dnd-get-motif-value | |
686 data 16 4 source-byteorder)) | |
687 (action-type (x-dnd-maybe-call-test-function | |
688 window | |
689 source-action)) | |
690 (reply-action (car (rassoc (car action-type) | |
691 x-dnd-motif-to-action))) | |
692 (reply-flags | |
693 (x-dnd-motif-value-to-list | |
694 (if reply-action | |
695 (+ reply-action | |
696 ?\x30 ; 30: valid drop site | |
697 ?\x700) ; 700: can do copy, move or link | |
698 (+ ?\x30 ; 30: drop site, but noop. | |
699 ?\x200)) ; 200: drop cancel. | |
700 2 my-byteorder)) | |
701 (reply (append | |
702 (list | |
703 (+ ?\x80 ; 0x80 indicates a reply. | |
704 5) ; DROP_START. | |
705 my-byteorder) | |
706 reply-flags | |
707 x | |
708 y)) | |
709 (timestamp (x-dnd-get-motif-value | |
710 data 4 4 source-byteorder)) | |
711 action) | |
712 | |
713 (x-send-client-message frame | |
714 dnd-source | |
715 frame | |
716 "_MOTIF_DRAG_AND_DROP_MESSAGE" | |
717 8 | |
718 reply) | |
719 (setq action | |
720 (when (and reply-action atom-name) | |
721 (let* ((value (x-get-selection-internal | |
722 (intern atom-name) | |
723 (intern (x-dnd-current-type window))))) | |
724 (when value | |
725 (condition-case info | |
726 (x-dnd-drop-data event frame window value | |
727 (x-dnd-current-type window)) | |
728 (error | |
729 (message "Error: %s" info) | |
730 nil)))))) | |
731 (x-get-selection-internal | |
732 (intern atom-name) | |
733 (if action 'XmTRANSFER_SUCCESS 'XmTRANSFER_FAILURE) | |
734 timestamp) | |
735 (x-dnd-forget-drop frame))) | |
736 | |
737 (t (error "Unknown Motif DND message %s %s" message-atom data))))) | |
738 | |
739 | |
740 ;;; | |
741 | |
742 (provide 'x-dnd) | |
743 | |
744 ;;; arch-tag: b621fb7e-50da-4323-850b-5fc71ae64621 | |
745 ;;; x-dnd.el ends here |