database error
[notmuch-archives.git] / b8 / 4f3bcc545295b676cf3cef3f1a5c2ad9728f58
1 Return-Path: <amdragon@mit.edu>\r
2 X-Original-To: notmuch@notmuchmail.org\r
3 Delivered-To: notmuch@notmuchmail.org\r
4 Received: from localhost (localhost [127.0.0.1])\r
5         by olra.theworths.org (Postfix) with ESMTP id D0B08429E5F\r
6         for <notmuch@notmuchmail.org>; Tue, 17 Jan 2012 15:03:10 -0800 (PST)\r
7 X-Virus-Scanned: Debian amavisd-new at olra.theworths.org\r
8 X-Spam-Flag: NO\r
9 X-Spam-Score: -0.7\r
10 X-Spam-Level: \r
11 X-Spam-Status: No, score=-0.7 tagged_above=-999 required=5\r
12         tests=[RCVD_IN_DNSWL_LOW=-0.7] autolearn=disabled\r
13 Received: from olra.theworths.org ([127.0.0.1])\r
14         by localhost (olra.theworths.org [127.0.0.1]) (amavisd-new, port 10024)\r
15         with ESMTP id e0wzQcsFR-bM for <notmuch@notmuchmail.org>;\r
16         Tue, 17 Jan 2012 15:03:09 -0800 (PST)\r
17 Received: from dmz-mailsec-scanner-3.mit.edu (DMZ-MAILSEC-SCANNER-3.MIT.EDU\r
18         [18.9.25.14])\r
19         by olra.theworths.org (Postfix) with ESMTP id 80EFC429E27\r
20         for <notmuch@notmuchmail.org>; Tue, 17 Jan 2012 15:03:09 -0800 (PST)\r
21 X-AuditID: 1209190e-b7f7c6d0000008c3-d7-4f15fe2c02a4\r
22 Received: from mailhub-auth-4.mit.edu ( [18.7.62.39])\r
23         by dmz-mailsec-scanner-3.mit.edu (Symantec Messaging Gateway) with SMTP\r
24         id 4C.4D.02243.C2EF51F4; Tue, 17 Jan 2012 18:03:09 -0500 (EST)\r
25 Received: from outgoing.mit.edu (OUTGOING-AUTH.MIT.EDU [18.7.22.103])\r
26         by mailhub-auth-4.mit.edu (8.13.8/8.9.2) with ESMTP id q0HN38I5025284; \r
27         Tue, 17 Jan 2012 18:03:08 -0500\r
28 Received: from awakening.csail.mit.edu (awakening.csail.mit.edu [18.26.4.91])\r
29         (authenticated bits=0)\r
30         (User authenticated as amdragon@ATHENA.MIT.EDU)\r
31         by outgoing.mit.edu (8.13.6/8.12.4) with ESMTP id q0HN37YK008150\r
32         (version=TLSv1/SSLv3 cipher=AES256-SHA bits=256 verify=NOT);\r
33         Tue, 17 Jan 2012 18:03:07 -0500 (EST)\r
34 Received: from amthrax by awakening.csail.mit.edu with local (Exim 4.77)\r
35         (envelope-from <amdragon@mit.edu>)\r
36         id 1RnI3L-0008EH-BV; Tue, 17 Jan 2012 18:02:55 -0500\r
37 Date: Tue, 17 Jan 2012 18:02:55 -0500\r
38 From: Austin Clements <amdragon@MIT.EDU>\r
39 To: Mark Walters <markwalters1009@gmail.com>\r
40 Subject: Re: [PATCH 1/1] Make buttons for attachments allow viewing as well\r
41         as saving\r
42 Message-ID: <20120117230255.GW16740@mit.edu>\r
43 References: <1326629796-11436-1-git-send-email-markwalters1009@gmail.com>\r
44         <1326629796-11436-2-git-send-email-markwalters1009@gmail.com>\r
45         <87wr8r5trv.fsf@servo.finestructure.net>\r
46         <87lip7fhkc.fsf@qmul.ac.uk> <20120117022330.GE16740@mit.edu>\r
47         <8739beitq4.fsf@qmul.ac.uk> <20120117202603.GP16740@mit.edu>\r
48         <871uqy3vy4.fsf@qmul.ac.uk> <20120117210158.GS16740@mit.edu>\r
49         <87obu2q80o.fsf@qmul.ac.uk>\r
50 MIME-Version: 1.0\r
51 Content-Type: text/plain; charset=us-ascii\r
52 Content-Disposition: inline\r
53 In-Reply-To: <87obu2q80o.fsf@qmul.ac.uk>\r
54 User-Agent: Mutt/1.5.21 (2010-09-15)\r
55 X-Brightmail-Tracker:\r
56  H4sIAAAAAAAAA+NgFupjleLIzCtJLcpLzFFi42IRYrdT19X9J+pvsDDCYvVcHovrN2cyOzB5\r
57         7Jx1l93j2apbzAFMUVw2Kak5mWWpRfp2CVwZa9fMYimYkl/RvaqftYGxN7KLkZNDQsBEYua8\r
58         98wQtpjEhXvr2boYuTiEBPYxSrScnc0K4WxglLj2/QoThHOSSeLttOfMEM4SRokz13+B9bMI\r
59         qEp0f7oCZrMJaEhs27+cEcQWEdCRuH1oATuIzSwgLfHtdzMTiC0sEC5x5w5EnBeo5sC372C2\r
60         kMBUZomj39Ug4oISJ2c+YYHo1ZK48e8lUC8H2Jzl/zhAwpxAq648eglWIiqgIjHl5Da2CYxC\r
61         s5B0z0LSPQuhewEj8ypG2ZTcKt3cxMyc4tRk3eLkxLy81CJdY73czBK91JTSTYygoOaU5NvB\r
62         +PWg0iFGAQ5GJR5eiQ2i/kKsiWXFlbmHGCU5mJREea1/AYX4kvJTKjMSizPii0pzUosPMUpw\r
63         MCuJ8F66DJTjTUmsrEotyodJSXOwKInzqmm98xMSSE8sSc1OTS1ILYLJynBwKEnwOv4FahQs\r
64         Sk1PrUjLzClBSDNxcIIM5wEavuMPyPDigsTc4sx0iPwpRl2Ok2uvnGMUYsnLz0uVEuddCDJI\r
65         AKQoozQPbg4sGb1iFAd6S5j3K8goHmAig5v0CmgJE9CSnFYhkCUliQgpqQZGo7UKMosbryw8\r
66         ssXah+9v9MKQ289Omm1atDj9x+ay3OojfVLC//xepWlddHr1bdPhd+xGdZHMXsmRaaFHe5NY\r
67         fotIegmlKu7wO1e2lOle4Mrdt70sD3M6L1ubMH9ziZXUkqxtpl+rBBzZM/lesFkuFD3xdU5U\r
68         cmhye4/c9Xfy1uX/Za3XyHkqsRRnJBpqMRcVJwIArjfqVyEDAAA=\r
69 Cc: notmuch@notmuchmail.org\r
70 X-BeenThere: notmuch@notmuchmail.org\r
71 X-Mailman-Version: 2.1.13\r
72 Precedence: list\r
73 List-Id: "Use and development of the notmuch mail system."\r
74         <notmuch.notmuchmail.org>\r
75 List-Unsubscribe: <http://notmuchmail.org/mailman/options/notmuch>,\r
76         <mailto:notmuch-request@notmuchmail.org?subject=unsubscribe>\r
77 List-Archive: <http://notmuchmail.org/pipermail/notmuch>\r
78 List-Post: <mailto:notmuch@notmuchmail.org>\r
79 List-Help: <mailto:notmuch-request@notmuchmail.org?subject=help>\r
80 List-Subscribe: <http://notmuchmail.org/mailman/listinfo/notmuch>,\r
81         <mailto:notmuch-request@notmuchmail.org?subject=subscribe>\r
82 X-List-Received-Date: Tue, 17 Jan 2012 23:03:11 -0000\r
83 \r
84 Quoth Mark Walters on Jan 17 at 10:27 pm:\r
85 > > (defmacro notmuch-with-temp-part-buffer (message-id nth &rest body)\r
86 > >   (declare (indent 2))\r
87 > >   (let ((process-crypto (make-symbol "process-crypto")))\r
88 > >     `(let ((,process-crypto notmuch-show-process-crypto))\r
89 > >        (with-temp-buffer\r
90 > >          (setq notmuch-show-process-crypto ,process-crypto)\r
91 > >          ;; Always acquires the part via `notmuch part', even if it is\r
92 > >          ;; available in the JSON output.\r
93 > >          (insert (notmuch-show-get-bodypart-internal message-id nth))\r
94 > >          ,@body))))\r
95\r
96 > I have followed the macro approach: since notmuch-show-save-part also\r
97 > uses it (which doesn't appear in the diff as it was unchanged). I have\r
98 > made all three functions use notmuch-with-temp-part-buffer. However, I\r
99 > used the macro exactly as you wrote it (and it seems to work) but I\r
100 > moderately understand why but could not justify it to someone! \r
101 \r
102 Oops, actually there was a bug in that macro.  It should have been\r
103 \r
104 (defmacro notmuch-with-temp-part-buffer (message-id nth &rest body)\r
105   (declare (indent 2))\r
106   (let ((process-crypto (make-symbol "process-crypto")))\r
107     `(let ((,process-crypto notmuch-show-process-crypto))\r
108        (with-temp-buffer\r
109          (setq notmuch-show-process-crypto ,process-crypto)\r
110          ;; Always acquires the part via `notmuch part', even if it is\r
111          ;; available in the JSON output.\r
112          (insert (notmuch-show-get-bodypart-internal ,message-id ,nth))\r
113          ,@body))))\r
114 \r
115 The only difference is on the "insert" line.  Sorry about that.\r
116 \r
117 I don't know how familiar you are with syntactic abstraction, so\r
118 here's a top-down explanation.  A macro is like the compile-time\r
119 equivalent of a function: rather than the arguments and return being\r
120 values that result from evaluation, they are pieces of code, the body\r
121 of the macro executes at compile time instead of at run time, and the\r
122 compiler replaces the invocation of the macro with the code that the\r
123 macro returns.  This is not unlike a C/C++ macro, but the\r
124 implementation of the macro is regular Lisp code that runs at compile\r
125 time and the code is represented as S-expressions rather than strings\r
126 (one beauty of Lisp is that the representation of code and data is the\r
127 same).\r
128 \r
129 The above macro returns the code starting after the "`" (pronounced\r
130 quasiquote), so that's what invocations of the macro get replaced\r
131 with.  Quasiquote (like quote) inhibits the evaluation of what follows\r
132 it and results in the code as data, rather than the result of\r
133 evaluating the code.  Unlike quote, quasiquote lets you jump back into\r
134 the evaluator using "," and ",@", so it's great for piecing together\r
135 code from a template, which is what macros spend most of their time\r
136 doing.\r
137 \r
138 The "declare" is simply a specification to emacs-lisp-mode about how\r
139 to indent uses of this macro.  The "make-symbol" is necessary to avoid\r
140 conflicting with variable names that may appear in the code that uses\r
141 this macro (since the invoking code and the code returned by the macro\r
142 will be interleaved, you have to worry about variable conflicts).\r
143 \r
144 > > (defun notmuch-show-interactively-view-part (message-id nth content-type)\r
145 > >   (notmuch-with-temp-part-buffer message-id nth\r
146 > >     (let ((handle (mm-make-handle (current-buffer) (list content-type))))\r
147 > >       (mm-interactively-view-part handle)))))\r
148\r
149 > Emacs wants to indent the (let line level with message-id in the line\r
150 > above which looks odd (and makes the lines too long). Do I overrule\r
151 > emacs, or put message-id and nth onto a separate line or is there\r
152 > something better?\r
153 \r
154 If you evaluate the defmacro, it'll pick up on that declare line at\r
155 the beginning of it and indent this correctly.\r
156 \r
157 > Also note that, because of the unification with notmuch-show-save-part\r
158 > all three functions have to have the four arguments message-id, nth,\r
159 > filename and content-type (even though currently each individual\r
160 > function only uses three of them). However see below for another comment\r
161 > on this.\r
162 \r
163 That makes sense.\r
164 \r
165 > > > +(defcustom notmuch-show-part-button-default-action 'notmuch-show-part-button-save\r
166 > > > +  "Default part header button action (on ENTER or mouse click)."\r
167 > > > +  :group 'notmuch\r
168 > > > +  :type '(choice (const :tag "Save part"\r
169 > > > +                 notmuch-show-part-button-save)\r
170 > > > +          (const :tag "View part"\r
171 > > > +                 notmuch-show-part-button-view)\r
172 > > > +          (const :tag "View interactively"\r
173 > > > +                 notmuch-show-part-button-interactively-view)))\r
174 > > \r
175 > > You probably want this to be the handler function, rather than the\r
176 > > button function, since the interface to the button function is rather\r
177 > > awkward.  That is, if someone wanted to plug in their own action, they\r
178 > > would want to define it in terms of the high-level handler interface\r
179 > > that you use above, rather than the low-level\r
180 > > button-with-magic-properties interface that Emacs forces you to use\r
181 > > below.\r
182\r
183 > I have done this.\r
184\r
185 > > This duplication is much worse, but also less necessary.\r
186 > > \r
187 > > (defun notmuch-show-part-button-interactively-view (&optional button)\r
188 > >   (interactive)\r
189 > >   (notmuch-show-part-button-internal button #'notmuch-show-interactively-view-part))\r
190 > > \r
191 > > (defun notmuch-show-part-button-internal (button handler)\r
192 > >   (let ((button (or button (button-at (point)))))\r
193 > >     (if button\r
194 > >     (let ((nth (button-get button :notmuch-part)))\r
195 > >       (if nth\r
196 > >           (funcall handler (notmuch-show-get-message-id) nth\r
197 > >                            (button-get button :notmuch-content-type))\r
198 > >         (message "Not a valid part (is it a fake part?)."))))))\r
199\r
200 > Yes this is much nicer and I have done this too (modulo the extra\r
201 > argument mentioned above).\r
202\r
203 > Finally, I have discovered one bug/misfeature. If you try to "view" an\r
204 > attachment then it will offer to save it but will not offer a\r
205 > filename. If you try and save it (or use the default action) it will\r
206 > offer a filename as now. As far as I can see this is not fixable if I\r
207 > use mm-display-part: however, I could include a slight tweaked version,\r
208 > notmuch-show-mm-display-part say, which would fix this corner\r
209 > case. (Essentially, it would call notmuch-show-save-part if it failed to\r
210 > find a handler rather than mailcap-save-binary-file.) However, this is\r
211 > about 50 lines of lisp so I am not sure it is worth it.\r
212 \r
213 Hmm.  This is probably worth fixing, but probably in a separate patch.\r
214 Duplicating mm-display-part is probably not the way to go.  It think\r
215 it will work to pass t as the no-default argument to mm-display-part\r
216 and check the return value, which should be 'inline if it was able to\r
217 handle it internally or 'external if it found an external helper.  I'm\r
218 pretty sure it will never fall in to mailcap-save-binary-file in that\r
219 case.  If that doesn't work, you could flet mailcap-save-binary-file\r
220 around the call to mm-display-part.\r
221 \r
222 > Best wishes\r
223\r
224 > Mark\r
225\r
226 > From bda4bb7637fb7d09c50f95b6b76fd42a377e0dde Mon Sep 17 00:00:00 2001\r
227 > From: Mark Walters <markwalters1009@gmail.com>\r
228 > Date: Sat, 14 Jan 2012 18:04:22 +0000\r
229 > Subject: [PATCH] Make buttons for attachments allow viewing as well as saving\r
230\r
231 > Define a keymap for attachment buttons to allow multiple actions.\r
232 > Define 3 possible actions:\r
233 >     save attachment: exactly as currently,\r
234 >     view attachment: uses mailcap entry,\r
235 >     view attachment with user chosen program\r
236\r
237 > Keymap on a button is: s for save, v for view and o for view with\r
238 > other program. Default (i.e. enter or mouse button) is save but this\r
239 > is configurable in notmuch customize.\r
240\r
241 > One implementation detail: the view attachment function forces all\r
242 > attachments to be "displayed" using mailcap even if emacs could\r
243 > display them itself. Thus, for example, text/html appears in a browser\r
244 > and text/plain asks whether to save (on a standard debian setup)\r
245 > ---\r
246 >  emacs/notmuch-show.el |  105 +++++++++++++++++++++++++++++++++++++-----------\r
247 >  1 files changed, 81 insertions(+), 24 deletions(-)\r
248\r
249 > diff --git a/emacs/notmuch-show.el b/emacs/notmuch-show.el\r
250 > index 03c1f6b..2e4fecd 100644\r
251 > --- a/emacs/notmuch-show.el\r
252 > +++ b/emacs/notmuch-show.el\r
253 > @@ -281,10 +281,21 @@ message at DEPTH in the current thread."\r
254 >       (run-hooks 'notmuch-show-markup-headers-hook)))))\r
255 >  \r
256 >  (define-button-type 'notmuch-show-part-button-type\r
257 > -  'action 'notmuch-show-part-button-action\r
258 > +  'action 'notmuch-show-part-button-default\r
259 > +  'keymap 'notmuch-show-part-button-map\r
260 >    'follow-link t\r
261 >    'face 'message-mml)\r
262 >  \r
263 > +(defvar notmuch-show-part-button-map\r
264 > +  (let ((map (make-sparse-keymap)))\r
265 > +       (set-keymap-parent map button-map)\r
266 > +       (define-key map "s" 'notmuch-show-part-button-save)\r
267 > +       (define-key map "v" 'notmuch-show-part-button-view)\r
268 > +       (define-key map "o" 'notmuch-show-part-button-interactively-view)\r
269 > +    map)\r
270 > +  "Submap for button commands")\r
271 > +(fset 'notmuch-show-part-button-map notmuch-show-part-button-map)\r
272 > +\r
273 >  (defun notmuch-show-insert-part-header (nth content-type declared-type &optional name comment)\r
274 >    (let ((button))\r
275 >      (setq button\r
276 > @@ -299,29 +310,48 @@ message at DEPTH in the current thread."\r
277 >                  " ]")\r
278 >          :type 'notmuch-show-part-button-type\r
279 >          :notmuch-part nth\r
280 > -        :notmuch-filename name))\r
281 > +        :notmuch-filename name\r
282 > +        :notmuch-content-type content-type))\r
283 >      (insert "\n")\r
284 >      ;; return button\r
285 >      button))\r
286 >  \r
287 >  ;; Functions handling particular MIME parts.\r
288 >  \r
289 > -(defun notmuch-show-save-part (message-id nth &optional filename)\r
290 > -  (let ((process-crypto notmuch-show-process-crypto))\r
291 > -    (with-temp-buffer\r
292 > -      (setq notmuch-show-process-crypto process-crypto)\r
293 > -      ;; Always acquires the part via `notmuch part', even if it is\r
294 > -      ;; available in the JSON output.\r
295 > -      (insert (notmuch-show-get-bodypart-internal message-id nth))\r
296 > -      (let ((file (read-file-name\r
297 > -                "Filename to save as: "\r
298 > -                (or mailcap-download-directory "~/")\r
299 > -                nil nil\r
300 > -                filename)))\r
301 > -     ;; Don't re-compress .gz & al.  Arguably we should make\r
302 > -     ;; `file-name-handler-alist' nil, but that would chop\r
303 > -     ;; ange-ftp, which is reasonable to use here.\r
304 > -     (mm-write-region (point-min) (point-max) file nil nil nil 'no-conversion t)))))\r
305 > +(defmacro notmuch-with-temp-part-buffer (message-id nth &rest body)\r
306 > +  (declare (indent 2))\r
307 > +  (let ((process-crypto (make-symbol "process-crypto")))\r
308 > +    `(let ((,process-crypto notmuch-show-process-crypto))\r
309 > +       (with-temp-buffer\r
310 > +      (setq notmuch-show-process-crypto ,process-crypto)\r
311 > +      ;; Always acquires the part via `notmuch part', even if it is\r
312 > +      ;; available in the JSON output.\r
313 > +      (insert (notmuch-show-get-bodypart-internal message-id nth))\r
314 \r
315 Should be ",message-id ,nth" instead of "message-id nth" (my fault).\r
316 \r
317 > +      ,@body))))\r
318 > +\r
319 > +(defun notmuch-show-save-part (message-id nth &optional filename content-type)\r
320 > +  (notmuch-with-temp-part-buffer message-id nth\r
321 > +     (let ((file (read-file-name\r
322 > +               "Filename to save as: "\r
323 > +               (or mailcap-download-directory "~/")\r
324 > +               nil nil\r
325 > +               filename)))\r
326 > +       ;; Don't re-compress .gz & al.  Arguably we should make\r
327 > +       ;; `file-name-handler-alist' nil, but that would chop\r
328 > +       ;; ange-ftp, which is reasonable to use here.\r
329 > +       (mm-write-region (point-min) (point-max) file nil nil nil 'no-conversion t))))\r
330 > +\r
331 > +(defun notmuch-show-view-part (message-id nth &optional filename content-type )\r
332 > +  (notmuch-with-temp-part-buffer message-id nth\r
333 > +   ;; set mm-inlined-types to nil to force an external viewer\r
334 > +    (let ((handle (mm-make-handle (current-buffer) (list content-type)))\r
335 > +       (mm-inlined-types nil))\r
336 > +      (mm-display-part handle t))))\r
337 > +\r
338 > +(defun notmuch-show-interactively-view-part (message-id nth &optional filename content-type)\r
339 > +  (notmuch-with-temp-part-buffer message-id nth\r
340 > +   (let ((handle (mm-make-handle (current-buffer) (list content-type))))\r
341 > +     (mm-interactively-view-part handle))))\r
342 >  \r
343 >  (defun notmuch-show-mm-display-part-inline (msg part nth content-type)\r
344 >    "Use the mm-decode/mm-view functions to display a part in the\r
345 > @@ -1502,13 +1532,40 @@ buffer."\r
346 >  \r
347 >  ;; Commands typically bound to buttons.\r
348 >  \r
349 > -(defun notmuch-show-part-button-action (button)\r
350 > -  (let ((nth (button-get button :notmuch-part)))\r
351 > -    (if nth\r
352 > -     (notmuch-show-save-part (notmuch-show-get-message-id) nth\r
353 > -                             (button-get button :notmuch-filename))\r
354 > -      (message "Not a valid part (is it a fake part?)."))))\r
355 > +(defcustom notmuch-show-part-button-default-action 'notmuch-show-save-part\r
356 > +  "Default part header button action (on ENTER or mouse click)."\r
357 > +  :group 'notmuch\r
358 > +  :type '(choice (const :tag "Save part"\r
359 > +                     notmuch-show-save-part)\r
360 > +              (const :tag "View part"\r
361 > +                     notmuch-show-view-part)\r
362 > +              (const :tag "View interactively"\r
363 > +                     notmuch-show-interactively-view-part)))\r
364 > +\r
365 > +(defun notmuch-show-part-button-default (&optional button)\r
366 > +  (interactive)\r
367 > +  (notmuch-show-part-button-internal button notmuch-show-part-button-default-action))\r
368 >  \r
369 > +(defun notmuch-show-part-button-save (&optional button)\r
370 > +  (interactive)\r
371 > +  (notmuch-show-part-button-internal button #'notmuch-show-save-part))\r
372 > +\r
373 > +(defun notmuch-show-part-button-view (&optional button)\r
374 > +  (interactive)\r
375 > +  (notmuch-show-part-button-internal button #'notmuch-show-view-part))\r
376 > +\r
377 > +(defun notmuch-show-part-button-interactively-view (&optional button)\r
378 > +  (interactive)\r
379 > +  (notmuch-show-part-button-internal button #'notmuch-show-interactively-view-part))\r
380 \r
381 Much better!\r
382 \r
383 > +\r
384 > +(defun notmuch-show-part-button-internal (button handler)\r
385 > +  (let ((button (or button (button-at (point)))))\r
386 > +    (if button\r
387 > +     (let ((nth (button-get button :notmuch-part)))\r
388 > +       (if nth\r
389 > +           (funcall handler (notmuch-show-get-message-id) nth\r
390 > +                    (button-get button :notmuch-filename)\r
391 > +                    (button-get button :notmuch-content-type)))))))\r
392 >  ;;\r
393 >  \r
394 >  (provide 'notmuch-show)\r