-
Notifications
You must be signed in to change notification settings - Fork 10
/
smart-dnd.el
207 lines (186 loc) · 6.78 KB
/
smart-dnd.el
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
;;; smart-dnd.el --- user-configurable drag-n-drop feature
;; Copyright (C) 2003-2008, 2012, 2014, 2017, 2020 by Seiji Zenitani
;; Author: Seiji Zenitani <zenitani@gmail.com>
;; Keywords: tools
;; Created: 2003-04-27
;; Compatibility: Emacs 22 or later
;; URL(en): https://github.com/zenitani/elisp/blob/master/smart-dnd.el
;; URL(jp): https://sci.nao.ac.jp/MEMBER/zenitani/elisp-j.html#smart-dnd
;; This file is free software; you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation; either version 3, or (at your option)
;; any later version.
;; This file is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
;; You should have received a copy of the GNU General Public License
;; along with GNU Emacs; see the file COPYING. If not, write to the
;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
;; Boston, MA 02110-1301, USA.
;;; Commentary
;; This package provides user-configurable drag-n-drop feature to Emacs 22.
;;
;; Usage:
;;
;; First, evaluate `smart-dnd-setup' function with an alist in the buffer.
;; The code modifies drag-n-drop behaviour in the local buffer and then
;; a string "image file: file.png" will be inserted when *.png file is dropped.
;;
;; (require 'smart-dnd)
;; (smart-dnd-setup
;; '(
;; ("\\.png\\'" . "image file: %f\n")
;; ("\\.jpg\\'" . "image file: %f\n")
;; (".exe\\'" . (message (concat "executable: " f)))
;; (".*" . "any filename: %f\n")
;; ))
;;
;; String elements will be formatted by `smart-dnd-string'.
;; You can also put elisp expression into the alist.
;; In the case of ".exe" in the above list, a local variable 'f'
;; will be replaced by the dropped filename in the expression.
;;
;; Major-mode-hook is a good place to install your configuration.
;; For example,
;;
;; html-mode:
;;
;; (add-hook
;; 'html-mode-hook
;; (lambda ()
;; (smart-dnd-setup
;; '(
;; ("\\.png\\'" . "<img src=\"%R\" />\n")
;; ("\\.gif\\'" . "<img src=\"%R\" />\n")
;; ("\\.jpg\\'" . "<img src=\"%R\" />\n")
;; ("\\.css\\'" . "<link rel=\"stylesheet\" type=\"text/css\" href=\"%R\" />\n" )
;; ("\\.js\\'" . "<script type=\"text/javascript\" src=\"%R\"></script>\n" )
;; (".*" . "<a href=\"%R\">%f</a>\n")
;; ))))
;;
;; LaTeX mode:
;;
;; (add-hook
;; 'latex-mode-hook
;; (lambda ()
;; (smart-dnd-setup
;; '(
;; ("\\.tex\\'" . "\\input{%r}\n")
;; ("\\.cls\\'" . "\\documentclass{%f}\n")
;; ("\\.sty\\'" . "\\usepackage{%f}\n")
;; ("\\.eps\\'" . "\\includegraphics[]{%r}\n")
;; ("\\.ps\\'" . "\\includegraphics[]{%r}\n")
;; ("\\.pdf\\'" . "\\includegraphics[]{%r}\n")
;; ("\\.jpg\\'" . "\\includegraphics[]{%r}\n")
;; ("\\.png\\'" . "\\includegraphics[]{%r}\n")
;; ))))
;;
;; C/C++ mode:
;;
;; (add-hook 'c-mode-common-hook
;; (lambda () (smart-dnd-setup '(("\\.h\\'" . "#include <%f>")))))
;;
;;; Code:
(require 'dnd)
(defvar smart-dnd-protocol-alist
'(("^file:///" . smart-dnd-handle-local-file)
("^file://" . smart-dnd-handle-file)
("^file:" . smart-dnd-handle-local-file))
"The functions to call when a file is dropped to the buffer.
See `dnd-protocol-alist' for more information."
)
(put 'smart-dnd-protocol-alist 'risky-local-variable t)
(defvar smart-dnd-replace-alist
'(
("%F" . f)
("%f" . (file-name-nondirectory f))
("%r" . (if buffer-file-name
(file-relative-name
f (file-name-directory buffer-file-name))
f))
("%R" . (if buffer-file-name
(file-relative-name
f (file-name-directory buffer-file-name))
(concat "file://" f)))
("%n" . (file-name-sans-extension (file-name-nondirectory f)))
("%e" . (or (file-name-extension f) ""))
))
(put 'smart-dnd-replace-alist 'risky-local-variable t)
(defun smart-dnd-handle-local-file (uri action)
"Open a local file. See also `dnd-open-local-file'."
(let* ((f (dnd-get-local-file-name uri t)))
(if (and f (file-readable-p f))
(progn
(or (smart-dnd-execute f)
(dnd-open-local-file uri action))
'private)
(error "Can not read %s" uri))))
(defun smart-dnd-handle-file (uri action)
"Handle a local or remote file."
(let ((local-file (dnd-get-local-file-uri uri)))
(if local-file (smart-dnd-handle-local-file local-file action)
(error "Remote files not supported"))))
(defun smart-dnd-execute (f)
"Execute a Drag'n'Drop action with filename F
depending on `smart-dnd-string-alist'."
(interactive "f")
(save-excursion
(if (eq (car-safe last-nonmenu-event) 'drag-n-drop)
(goto-char (posn-point (car (cdr-safe last-nonmenu-event)))))
(let( (alist smart-dnd-string-alist)
(case-fold-search nil)
(my-string nil)
(succeed nil) )
(while alist
(when (string-match (caar alist) f)
(setq my-string (cdar alist))
(when (stringp my-string)
(insert (smart-dnd-string my-string f))
(setq alist nil)
(setq succeed t)
)
(when (not (stringp my-string))
(eval (cdar alist))
(setq alist nil)
(setq succeed t)
)
)
(setq alist (cdr alist))
)
succeed)))
;;;###autoload
(defun smart-dnd-setup (alist)
"Install smart-dnd feature to the local buffer."
(interactive)
(set (make-local-variable 'dnd-protocol-alist)
(append smart-dnd-protocol-alist dnd-protocol-alist))
(set (make-local-variable 'smart-dnd-string-alist) alist)
)
(defun smart-dnd-string (string filename)
"Generate a string, based on a format STRING and the FILENAME.
You can use the following keywords in the format control STRING.
%F means absolute pathname. [ /home/zenitani/public_html/index.html ]
%f means file name without directory. [ index.html ]
%r and %R means relative path to the FILENAME from a file in the current buffer.
[ public_html/index.html ]
When the target buffer hasn't been assigned a file name yet,
%r returns the absolute pathname [ /home/zenitani/public_html/index.html ]
while %R returns the URL. [ file:///home/zenitani/ .. /index.html ]
%n means file name without extension. [ index ]
%e means extension of file name. [ html ]
"
(interactive)
(let ((rlist smart-dnd-replace-alist)
(case-fold-search nil)
(f filename))
(while rlist
(while (string-match (caar rlist) string)
(setq string
(replace-match
(eval (cdar rlist)) t nil string)))
(setq rlist (cdr rlist))
))
string)
(provide 'smart-dnd)
;;; smart-dnd.el ends here