Mercurial > hg > xemacs-beta
comparison lisp/dragdrop.el @ 428:3ecd8885ac67 r21-2-22
Import from CVS: tag r21-2-22
author | cvs |
---|---|
date | Mon, 13 Aug 2007 11:28:15 +0200 |
parents | |
children | abe6d1db359e |
comparison
equal
deleted
inserted
replaced
427:0a0253eac470 | 428:3ecd8885ac67 |
---|---|
1 ;;; dragdrop.el --- window system-independent Drag'n'Drop support. | |
2 | |
3 ;; Copyright (C) 1998 Oliver Graf <ograf@fga.de> | |
4 | |
5 ;; Maintainer: XEmacs Development Team, Oliver Graf <ograf@fga.de> | |
6 ;; Keywords: drag, drop, dumped | |
7 | |
8 ;; This file is part of XEmacs. | |
9 | |
10 ;; XEmacs is free software; you can redistribute it and/or modify it | |
11 ;; under the terms of the GNU General Public License as published by | |
12 ;; the Free Software Foundation; either version 2, or (at your option) | |
13 ;; any later version. | |
14 | |
15 ;; XEmacs is distributed in the hope that it will be useful, but | |
16 ;; WITHOUT ANY WARRANTY; without even the implied warranty of | |
17 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
18 ;; General Public License for more details. | |
19 | |
20 ;; You should have received a copy of the GNU General Public License | |
21 ;; along with XEmacs; see the file COPYING. If not, write to the | |
22 ;; Free Software Foundation, 59 Temple Place - Suite 330, | |
23 ;; Boston, MA 02111-1307, USA. | |
24 | |
25 ;;; Synched up with: Not in FSF. | |
26 | |
27 ;;; Commentary: | |
28 | |
29 ;; This file is dumped with XEmacs (when drag'n'drop support is compiled in). | |
30 | |
31 ;;; Code: | |
32 | |
33 ;; we need mouse-set-point | |
34 (require 'mouse) | |
35 (provide 'dragdrop) | |
36 | |
37 ;; I think this is a better name for the custom group | |
38 ;; looks better in the menu and the group display as dragdrop | |
39 ;; Anyway: is dragdrop- a good prefix for all this? | |
40 ;; What if someone trys drop<TAB> in the minibuffer? | |
41 (defgroup drag-n-drop nil | |
42 "*{EXPERIMENTAL} Window system-independent drag'n'drop support." | |
43 :group 'editing) | |
44 | |
45 (defcustom dragdrop-drop-at-point nil | |
46 "*{EXPERIMENTAL} If non-nil, drop text at the cursor location. | |
47 Otherwise, the cursor will be moved to the location of the pointer drop before | |
48 text is inserted." | |
49 :type 'boolean | |
50 :group 'drag-n-drop) | |
51 | |
52 (defcustom dragdrop-autoload-tm-view nil | |
53 "*{EXPERIMENTAL} If non-nil, autoload tm-view to decode MIME data. | |
54 Otherwise, the buffer is only decoded if tm-view is already available." | |
55 :type 'boolean | |
56 :group 'drag-n-drop) | |
57 | |
58 ;; the widget for editing the drop-functions | |
59 (define-widget 'dragdrop-function-widget 'list | |
60 "*{EXPERIMENTAL} Widget for editing drop dispatch functions." | |
61 :args `((choice :tag "Function" | |
62 (function-item experimental-dragdrop-drop-url-default) | |
63 (function-item experimental-dragdrop-drop-mime-default) | |
64 (function-item experimental-dragdrop-drop-log-function) | |
65 (function :tag "Other" nil)) | |
66 (choice :tag "Button" :value t | |
67 (choice-item :tag "Ignore" t) | |
68 (choice-item 0) (choice-item 1) (choice-item 2) | |
69 (choice-item 3) (choice-item 4) (choice-item 5) | |
70 (choice-item 6) (choice-item 7)) | |
71 (radio-button-choice :tag "Modifiers" | |
72 (const :tag "Ignore Modifier Keys" t) | |
73 (checklist :greedy t | |
74 :format "Modifier Keys:\n%v" | |
75 :extra-offset 6 | |
76 (const shift) | |
77 (const control) | |
78 (const meta) | |
79 (const alt) | |
80 (const hyper) | |
81 (const super))) | |
82 (repeat :inline t :value nil :tag "Extra Function Arguments" | |
83 (sexp :tag "Arg" :value nil))) | |
84 :value '(nil t t)) | |
85 | |
86 (defcustom experimental-dragdrop-drop-functions '((experimental-dragdrop-drop-url-default t t) | |
87 (experimental-dragdrop-drop-mime-default t t)) | |
88 "*{EXPERIMENTAL} This is the standart drop function search list. | |
89 Each element is a list of a function, a button selector, a modifier | |
90 selector and optional argumets to the function call. | |
91 The function must accept at least two arguments: first is the event | |
92 of the drop, second the object data, followed by any of the optional | |
93 arguments provided in this list. | |
94 The functions are called in order, until one returns t." | |
95 :group 'drag-n-drop | |
96 :type '(repeat dragdrop-function-widget)) | |
97 | |
98 (defgroup dnd-debug nil | |
99 "*{EXPERIMENTAL} Drag'n'Drop debugging options." | |
100 :group 'drag-n-drop) | |
101 | |
102 (defcustom dragdrop-drop-log nil | |
103 "*{EXPERIMENTAL} If non-nil, every drop is logged. | |
104 The name of the buffer is set in the custom 'dragdrop-drop-log-name" | |
105 :group 'dnd-debug | |
106 :type 'boolean) | |
107 | |
108 (defcustom dragdrop-drop-log-name "*drop log buffer*" | |
109 "*{EXPERIMENTAL} The name of the buffer used to log drops. | |
110 Set dragdrop-drop-log to non-nil to enable this feature." | |
111 :group 'dnd-debug | |
112 :type 'string) | |
113 | |
114 (defvar dragdrop-drop-log-buffer nil | |
115 "*{EXPERIMENTAL} Buffer to log drops in debug mode.") | |
116 | |
117 ;; | |
118 ;; Drop API | |
119 ;; | |
120 (defun dragdrop-drop-dispatch (object) | |
121 "*{EXPERIMENTAL} This function identifies DROP type misc-user-events. | |
122 It calls functions which will handle the drag." | |
123 (let ((event current-mouse-event)) | |
124 (and dragdrop-drop-log | |
125 (experimental-dragdrop-drop-log-function event object)) | |
126 (dragdrop-drop-find-functions event object))) | |
127 | |
128 (defun dragdrop-drop-find-functions (event object) | |
129 "Finds valid drop-handle functions and executes them to dispose the drop. | |
130 It does this by looking for extent-properties called | |
131 'experimental-dragdrop-drop-functions and for variables named like this." | |
132 (catch 'dragdrop-drop-is-done | |
133 (and (event-over-text-area-p event) | |
134 ;; let's search the extents | |
135 (catch 'dragdrop-extents-done | |
136 (let ((window (event-window event)) | |
137 (pos (event-point event)) | |
138 (cpos (event-closest-point event)) | |
139 (buffer nil)) | |
140 (or window (throw 'dragdrop-extents-done nil)) | |
141 (or pos (setq pos cpos)) | |
142 (select-window window) | |
143 (setq buffer (window-buffer)) | |
144 (let ((ext (extent-at pos buffer 'experimental-dragdrop-drop-functions))) | |
145 (while (not (eq ext nil)) | |
146 (dragdrop-drop-do-functions | |
147 (extent-property ext 'experimental-dragdrop-drop-functions) | |
148 event | |
149 object) | |
150 (setq ext (extent-at pos buffer | |
151 'experimental-dragdrop-drop-functions | |
152 ext))))))) | |
153 ;; now look into the variable experimental-dragdrop-drop-functions | |
154 (dragdrop-drop-do-functions experimental-dragdrop-drop-functions event object))) | |
155 | |
156 (defun dragdrop-compare-mods (first-mods second-mods) | |
157 "Returns t if both first-mods and second-mods contain the same elements. | |
158 Order is not important." | |
159 (let ((moda (copy-sequence first-mods)) | |
160 (modb (copy-sequence second-mods))) | |
161 (while (and (not (eq moda ())) | |
162 (not (eq modb ()))) | |
163 (setq modb (delete (car moda) modb)) | |
164 (setq moda (delete (car moda) moda))) | |
165 (and (eq moda ()) | |
166 (eq modb ())))) | |
167 | |
168 (defun dragdrop-drop-do-functions (drop-funs event object) | |
169 "Calls all functions in drop-funs with object until one returns t. | |
170 Returns t if one of drop-funs returns t. Otherwise returns nil." | |
171 (let ((flist nil) | |
172 (button (event-button event)) | |
173 (mods (event-modifiers event))) | |
174 (while (not (eq drop-funs ())) | |
175 (setq flist (car drop-funs)) | |
176 (and (or (eq (cadr flist) t) | |
177 (= (cadr flist) button)) | |
178 (or (eq (caddr flist) t) | |
179 (dragdrop-compare-mods (caddr flist) mods)) | |
180 (apply (car flist) `(,event ,object ,@(cdddr flist))) | |
181 ;; (funcall (car flist) event object) | |
182 (throw 'dragdrop-drop-is-done t)) | |
183 (setq drop-funs (cdr drop-funs)))) | |
184 nil) | |
185 | |
186 (defun experimental-dragdrop-drop-log-function (event object &optional message buffer) | |
187 "*{EXPERIMENTAL} Logs any drops into a buffer. | |
188 If buffer is nil, it inserts the data into a buffer called after | |
189 dragdrop-drop-log-name. | |
190 If dragdrop-drop-log is non-nil, this is done automatically for each drop. | |
191 The function always returns nil." | |
192 (save-excursion | |
193 (cond ((buffer-live-p buffer) | |
194 (set-buffer buffer)) | |
195 ((stringp buffer) | |
196 (set-buffer (get-buffer-create buffer))) | |
197 ((buffer-live-p dragdrop-drop-log-buffer) | |
198 (set-buffer dragdrop-drop-log-buffer)) | |
199 (t | |
200 (setq dragdrop-drop-log-buffer (get-buffer-create dragdrop-drop-log-name)) | |
201 (set-buffer dragdrop-drop-log-buffer))) | |
202 (insert (format "* %s: %s\n" | |
203 (current-time-string) | |
204 (if message message "received a drop"))) | |
205 (insert (format " at %d,%d (%d,%d) with button %d and mods %s\n" | |
206 (event-x event) | |
207 (event-y event) | |
208 (event-x-pixel event) | |
209 (event-y-pixel event) | |
210 (event-button event) | |
211 (event-modifiers event))) | |
212 (insert (format " data is of type %s (%d %s)\n" | |
213 (cond ((eq (car object) 'dragdrop-URL) "URL") | |
214 ((eq (car object) 'dragdrop-MIME) "MIME") | |
215 (t "UNKNOWN")) | |
216 (length (cdr object)) | |
217 (if (= (length (cdr object)) 1) "element" "elements"))) | |
218 (let ((i 1) | |
219 (data (cdr object))) | |
220 (while (not (eq data ())) | |
221 (insert (format " Element %d: %S\n" | |
222 i (car data))) | |
223 (setq i (1+ i)) | |
224 (setq data (cdr data)))) | |
225 (insert "----------\n")) | |
226 nil) | |
227 | |
228 (defun experimental-dragdrop-drop-url-default (event object) | |
229 "*{EXPERIMENTAL} Default handler for dropped URL data. | |
230 Finds files and URLs. Returns nil if object does not contain URL data." | |
231 (cond ((eq (car object) 'dragdrop-URL) | |
232 (let ((data (cdr object)) | |
233 (frame (event-channel event)) | |
234 (x pop-up-windows) | |
235 (window (event-window event))) | |
236 (setq pop-up-windows nil) | |
237 (while (not (eq data ())) | |
238 (cond ((dragdrop-is-some-url "file" (car data)) | |
239 ;; if it is some file, pop it to a buffer | |
240 (cond (window | |
241 (select-window window))) | |
242 (switch-to-buffer (find-file-noselect | |
243 (substring (car data) 5)))) | |
244 ;; to-do: open ftp URLs with efs... | |
245 (t | |
246 ;; some other URL, try to fire up some browser for it | |
247 (if (boundp 'browse-url-browser-function) | |
248 (funcall browse-url-browser-function (car data)) | |
249 (display-message 'error | |
250 "Can't show URL, no browser selected")))) | |
251 (undo-boundary) | |
252 (setq data (cdr data))) | |
253 (make-frame-visible frame) | |
254 (setq pop-up-windows x) | |
255 t)) | |
256 (t nil))) | |
257 | |
258 (defun experimental-dragdrop-drop-mime-default (event object) | |
259 "*{EXPERIMENTAL} Default handler for dropped MIME data. | |
260 Inserts text into buffer, creates MIME buffers for other types. | |
261 Returns nil if object does not contain MIME data." | |
262 (cond ((eq (car object) 'dragdrop-MIME) | |
263 (let ((ldata (cdr object)) | |
264 (frame (event-channel event)) | |
265 (x pop-up-windows) | |
266 (data nil)) | |
267 ;; how should this be handled??? | |
268 ;; insert drops of text/* into buffer | |
269 ;; create new buffer if pointer is outside buffer... | |
270 ;; but there are many other ways... | |
271 ;; | |
272 ;; first thing: check if it's only text/plain and if the | |
273 ;; drop happened inside some buffer. if yes insert it into | |
274 ;; this buffer (hope it is not encoded in some MIME way) | |
275 ;; | |
276 ;; Remember: ("text/plain" "dosnotmatter" "somedata") | |
277 ;; drops are inserted at mouse-point, if inside a buffer | |
278 (while (not (eq ldata ())) | |
279 (setq data (car ldata)) | |
280 (if (and (listp data) | |
281 (= (length data) 3) | |
282 (listp (car data)) | |
283 (stringp (caar data)) | |
284 (string= (caar data) "text/plain") | |
285 (event-over-text-area-p event)) | |
286 (let ((window (event-window event))) | |
287 (and window | |
288 (select-window window)) | |
289 (and (not dragdrop-drop-at-point) | |
290 (mouse-set-point event)) | |
291 (insert (caddr data))) | |
292 (let ((buf (get-buffer-create "*MIME-Drop data*"))) | |
293 (set-buffer buf) | |
294 (pop-to-buffer buf nil frame) | |
295 (or (featurep 'tm-view) | |
296 (and dragdrop-autoload-tm-view | |
297 (require 'tm-view))) | |
298 (cond ((stringp data) | |
299 ;; this is some raw MIME stuff | |
300 ;; create some buffer and let tm do the job | |
301 ;; | |
302 ;; this is always the same buffer!!! | |
303 ;; change? | |
304 (erase-buffer) | |
305 (insert data) | |
306 (and (featurep 'tm-view) | |
307 (mime/viewer-mode buf))) | |
308 ((and (listp data) | |
309 (= (length data) 3)) | |
310 ;; change the internal content-type representation to the | |
311 ;; way tm does it ("content/type" (key . value)*) | |
312 ;; but for now list will do the job | |
313 ;; | |
314 ;; this is always the same buffer!!! | |
315 ;; change? | |
316 (erase-buffer) | |
317 (insert (caddr data)) | |
318 (and (featurep 'tm-view) | |
319 ;; this list of (car data) should be done before | |
320 ;; enqueing the event | |
321 (mime/viewer-mode buf (car data) (cadr data)))) | |
322 (t | |
323 (display-message 'error "Wrong drop data"))))) | |
324 (undo-boundary) | |
325 (setq ldata (cdr ldata))) | |
326 (make-frame-visible frame) | |
327 (setq pop-up-windows x)) | |
328 t) | |
329 (t nil))) | |
330 | |
331 (defun dragdrop-is-some-url (method url) | |
332 "Returns true if method equals the start of url. | |
333 If method does not end into ':' this is appended before the | |
334 compare." | |
335 (cond ((and (stringp url) | |
336 (stringp method) | |
337 (> (length url) (length method))) | |
338 ;; is this ?: check efficient enough? | |
339 (if (not (string= (substring method -1) ":")) | |
340 (setq method (concat method ":"))) | |
341 (string= method (substring url 0 (length method)))) | |
342 (t nil))) | |
343 | |
344 ;; | |
345 ;; Drag API | |
346 ;; | |
347 (defun experimental-dragdrop-drag (event object) | |
348 "*{EXPERIMENTAL} The generic drag function. | |
349 Tries to do the best with object in the selected protocol. | |
350 Object must comply to the standart drag'n'drop object | |
351 format." | |
352 (error "Not implemented")) | |
353 | |
354 (defun experimental-dragdrop-drag-region (event begin end) | |
355 "*{EXPERIMENTAL} Drag a region. | |
356 This function uses special data types if the low-level | |
357 protocol requires it. It does so by calling | |
358 dragdrop-drag-pure-text." | |
359 (experimental-dragdrop-drag-pure-text event | |
360 (buffer-substring-no-properties begin end))) | |
361 | |
362 (defun experimental-dragdrop-drag-pure-text (event text) | |
363 "*{EXPERIMENTAL} Drag text-only data. | |
364 Takes care of special low-level protocol data types. | |
365 Text must be a list of strings." | |
366 (error "Not implemented")) | |
367 | |
368 (defun experimental-dragdrop-drag-pure-file (event file) | |
369 "*{EXPERIMENTAL} Drag filepath-only data. | |
370 Takes care of special low-level protocol data types. | |
371 file must be a list of strings." | |
372 (error "Not implemented")) | |
373 | |
374 ;; | |
375 ;; The following ones come from frame.el but the better belong here | |
376 ;; until changed | |
377 ;; | |
378 (defun cde-start-drag (event type data) | |
379 "Implement the CDE drag operation. | |
380 Calls the internal function cde-start-drag-internal to do the actual work." | |
381 (interactive "_eXX") | |
382 (if (featurep 'cde) | |
383 ;; Avoid build-time doc string warning by calling the function | |
384 ;; in the following roundabout way: | |
385 (funcall (intern "cde-start-drag-internal") | |
386 event type data) | |
387 (error "CDE functionality not compiled in."))) | |
388 | |
389 (defun cde-start-drag-region (event begin end) | |
390 "Implement the CDE drag operation for a region. | |
391 Calls the internal function CDE-start-drag-internal to do the actual work. | |
392 This always does buffer transfers." | |
393 ;; Oliver Graf <ograf@fga.de> | |
394 (interactive "_er") | |
395 (if (featurep 'cde) | |
396 (funcall (intern "cde-start-drag-internal") | |
397 event nil (list (buffer-substring-no-properties begin end))) | |
398 (error "CDE functionality not compiled in."))) | |
399 | |
400 ;; the OffiX drag stuff will soon move also (perhaps mouse.el) | |
401 ;; if the drag event is done | |
402 (defun offix-start-drag (event data &optional type) | |
403 "Implement the OffiX drag operation. | |
404 Calls the internal function offix-start-drag-internal to do the actual work. | |
405 If type is not given, DndText is assumed." | |
406 ;; Oliver Graf <ograf@fga.de> | |
407 (interactive "esi") | |
408 (if (featurep 'offix) | |
409 (funcall (intern "offix-start-drag-internal") event data type) | |
410 (error "OffiX functionality not compiled in."))) | |
411 | |
412 (defun offix-start-drag-region (event begin end) | |
413 "Implement the OffiX drag operation for a region. | |
414 Calls the internal function offix-start-drag-internal to do the actual work. | |
415 This always assumes DndText as type." | |
416 ;; Oliver Graf <ograf@fga.de> | |
417 (interactive "_er") | |
418 (if (featurep 'offix) | |
419 (funcall (intern "offix-start-drag-internal") | |
420 event (buffer-substring-no-properties begin end)) | |
421 (error "OffiX functionality not compiled in."))) | |
422 | |
423 | |
424 ;;; dragdrop.el ends here |