XEmacs 21.4.9 "Informed Management".
[chise/xemacs-chise.git.1] / lisp / dialog.el
1 ;;; dialog.el --- Dialog-box support for XEmacs
2
3 ;; Copyright (C) 1991-4, 1997 Free Software Foundation, Inc.
4 ;; Copyright (C) 2000 Ben Wing.
5
6 ;; Maintainer: XEmacs Development Team
7 ;; Keywords: extensions, internal, dumped
8
9 ;; This file is part of XEmacs.
10
11 ;; XEmacs is free software; you can redistribute it and/or modify it
12 ;; 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 ;; XEmacs is distributed in the hope that it will be useful, but
17 ;; WITHOUT ANY WARRANTY; without even the implied warranty of
18 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
19 ;; General Public License for more details.
20
21 ;; You should have received a copy of the GNU General Public License
22 ;; along with XEmacs; see the file COPYING.  If not, write to the 
23 ;; Free Software Foundation, 59 Temple Place - Suite 330,
24 ;; Boston, MA 02111-1307, USA.
25
26 ;;; Synched up with: Not in FSF.
27
28 ;;; Commentary:
29
30 ;; This file is dumped with XEmacs (when dialog boxes are compiled in).
31
32 ;; Dialog boxes are non-modal at the C level, but made modal at the
33 ;; Lisp level via hacks in functions such as yes-or-no-p-dialog-box
34 ;; below.  Perhaps there should be truly modal dialog boxes
35 ;; implemented at the C level for safety.  All code using dialog boxes
36 ;; should be careful to assume that the environment, for example the
37 ;; current buffer, might be completely different after returning from
38 ;; yes-or-no-p-dialog-box, but such code is difficult to write and test.
39
40 ;;; Code:
41 (defun yes-or-no-p-dialog-box (prompt)
42   "Ask user a yes-or-no question with a popup dialog box.
43 Return t if the answer is \"yes\", nil if \"no\".  Takes one argument,
44 the question string to display."
45   (save-selected-frame
46     (make-dialog-box 'question
47                      :question prompt
48                      :modal t
49                      :buttons '(["Yes" (dialog-box-finish t)]
50                                 ["No" (dialog-box-finish nil)]
51                                 nil
52                                 ["Cancel" (dialog-box-cancel)]))))
53
54 ;; FSF has a similar function `x-popup-dialog'.
55 (defun get-dialog-box-response (position contents)
56   "Pop up a dialog box and return user's selection.
57 POSITION specifies which frame to use.
58 This is normally an event or a window or frame.
59 If POSITION is t or nil, it means to use the frame the mouse is on.
60 The dialog box appears in the middle of the specified frame.
61
62 CONTENTS specifies the alternatives to display in the dialog box.
63 It is a list of the form (TITLE ITEM1 ITEM2...).
64 Each ITEM is a cons cell (STRING . VALUE).
65 The return value is VALUE from the chosen item.
66
67 An ITEM may also be just a string--that makes a nonselectable item.
68 An ITEM may also be nil--that means to put all preceding items
69 on the left of the dialog box and all following items on the right."
70   (cond
71    ((eventp position)
72     (select-frame (event-frame position)))
73    ((framep position)
74     (select-frame position))
75    ((windowp position)
76     (select-window position)))
77   (make-dialog-box 'question
78                    :question (car contents)
79                    :modal t
80                    :buttons
81                    (mapcar #'(lambda (x)
82                                (cond
83                                 ((null x)
84                                  nil)
85                                 ((stringp x)
86                                  ;;this will never get selected
87                                  `[,x 'ignore nil])
88                                 (t
89                                  `[,(car x) (dialog-box-finish ',(cdr x)) t])))
90                            (cdr contents))))
91
92 (defun message-box (fmt &rest args)
93   "Display a message, in a dialog box if possible.
94 If the selected device has no dialog-box support, use the echo area.
95 The arguments are the same as to `format'.
96
97 If the only argument is nil, clear any existing message; let the
98 minibuffer contents show."
99   (if (and (null fmt) (null args))
100       (progn
101         (clear-message nil)
102         nil)
103     (let ((str (apply 'format fmt args)))
104       (if (device-on-window-system-p)
105           (get-dialog-box-response nil (list str (cons "%_OK" t)))
106         (display-message 'message str))
107       str)))
108
109 (defun message-or-box (fmt &rest args)
110   "Display a message in a dialog box or in the echo area.
111 If this command was invoked with the mouse, use a dialog box.
112 Otherwise, use the echo area.
113 The arguments are the same as to `format'.
114
115 If the only argument is nil, clear any existing message; let the
116 minibuffer contents show."
117   (if (should-use-dialog-box-p)
118       (apply 'message-box fmt args)
119     (apply 'message fmt args)))
120
121 (defun make-dialog-box (type &rest cl-keys)
122   "Pop up a dialog box.
123 TYPE is a symbol, the type of dialog box.  Remaining arguments are
124 keyword-value pairs, specifying the particular characteristics of the
125 dialog box.  The allowed keywords are particular to each type, but
126 some standard keywords are common to many types:
127
128 :title
129   The title of the dialog box's window.
130
131 :modal
132   If true, indicates that XEmacs will wait until the user is \"done\"
133   with the dialog box (usually, this means that a response has been
134   given).  Typically, the response is returned.  NOTE: Some dialog
135   boxes are always modal.  If the dialog box is modal, `make-dialog-box'
136   returns immediately.  The return value will be either nil or a
137   dialog box handle of some sort, e.g. a frame for type `general'.
138
139 ---------------------------------------------------------------------------
140
141 Recognized types are
142
143 general
144   A dialog box consisting of an XEmacs glyph, typically a `layout'
145   widget specifying a dialog box arrangement.  This is the most
146   general and powerful dialog box type, but requires more work than
147   the other types below.
148
149 question
150   A simple dialog box that displays a question and contains one or
151   more user-defined buttons to specify possible responses. (This is
152   compatible with the old built-in dialog boxes formerly specified
153   using `popup-dialog-box'.)
154
155 file
156   A file dialog box, of the type typically used in the window system
157   XEmacs is running on.
158
159 color
160   A color picker.
161
162 find
163   A find dialog box.
164
165 font
166   A font chooser.
167
168 print
169   A dialog box used when printing (e.g. number of pages, printer).
170
171 page-setup
172   A dialog box for setting page options (e.g. margins) for printing.
173
174 replace
175   A find/replace dialog box.
176
177 mswindows-message
178   An MS Windows-specific standard dialog box type similar to `question'.
179
180 ---------------------------------------------------------------------------
181
182 For type `general':
183
184 This type creates a frame and puts the specified widget layout in it.
185 \(Currently this is done by eliminating all areas but the gutter and placing
186 the layout there; but this is an implementation detail and may change.)
187
188 The keywords allowed for `general' are
189
190 :spec
191   The widget spec -- anything that can be passed to `make-glyph'.
192 :title
193   The title of the frame.
194 :parent
195   The frame is made a child of this frame (defaults to the selected frame).
196 :properties
197   Additional properties of the frame, as well as `dialog-frame-plist'.
198
199 ---------------------------------------------------------------------------
200
201 For type `question':
202
203 The keywords allowed are
204
205 :modal
206   t or nil.  When t, the dialog box callback should exit the dialog box
207   using the functions `dialog-box-finish' or `dialog-box-cancel'.
208 :title
209   The title of the frame.
210 :question
211   A string, the question.
212 :buttons
213   A list, describing the buttons below the question.  Each of these is a
214   vector, the syntax of which is essentially the same as that of popup menu
215   items.  They may have any of the following forms:
216
217    [ \"name\" callback <active-p> ]
218    [ \"name\" callback <active-p> \"suffix\" ]
219    [ \"name\" callback :<keyword> <value>  :<keyword> <value> ... ]
220   
221   The name is the string to display on the button; it is filtered through the
222   resource database, so it is possible for resources to override what string
223   is actually displayed.
224   
225   Accelerators can be indicated in the string by putting the sequence
226   \"%_\" before the character corresponding to the key that will invoke
227   the button.  Uppercase and lowercase accelerators are equivalent.  The
228   sequence \"%%\" is also special, and is translated into a single %.
229   
230   If the `callback' of a button is a symbol, then it must name a command.
231   It will be invoked with `call-interactively'.  If it is a list, then it is
232   evaluated with `eval'.
233   
234   One (and only one) of the buttons may be `nil'.  This marker means that all
235   following buttons should be flushright instead of flushleft.
236   
237   Though the keyword/value syntax is supported for dialog boxes just as in
238   popup menus, the only keyword which is both meaningful and fully implemented
239   for dialog box buttons is `:active'.
240
241 ---------------------------------------------------------------------------
242
243 For type `file':
244
245 The keywords allowed are
246
247 :initial-filename
248   The initial filename to be placed in the dialog box (defaults to nothing).
249 :initial-directory
250   The initial directory to be selected in the dialog box (defaults to the
251   current buffer's `default-directory).
252 :filter-list
253   A list of                     (filter-desc filter ...)
254 :title
255   The title of the dialog box (defaults to \"Open\").
256 :allow-multi-select             t or nil
257 :create-prompt-on-nonexistent   t or nil
258 :overwrite-prompt               t or nil
259 :file-must-exist                t or nil
260 :no-network-button              t or nil
261 :no-read-only-return            t or nil
262
263 ---------------------------------------------------------------------------
264
265 For type `directory':
266
267 The keywords allowed are
268
269 :initial-directory
270   The initial directory to be selected in the dialog box (defaults to the
271   current buffer's `default-directory).
272 :title
273   The title of the dialog box (defaults to \"Open\").
274
275 ---------------------------------------------------------------------------
276
277 For type `print':
278
279 This invokes the Windows standard Print dialog.
280 This dialog is usually invoked when the user selects the Print command.
281 After the user presses OK, the program should start actual printout.
282
283 The keywords allowed are
284
285 :device
286   An 'msprinter device.
287 :print-settings
288   A printer settings object.
289 :allow-selection
290   t or nil -- whether the \"Selection\" button is enabled (defaults to nil).
291 :allow-pages
292   t or nil -- whether the \"Pages\" button and associated edit controls
293   are enabled (defaults to t).
294 :selected-page-button
295   `all', `selection', or `pages' -- which page button is initially
296   selected.
297
298 Exactly one of :device and :print-settings must be given.
299
300 The function brings up the Print dialog, where the user can
301 select a different printer and/or change printer options.  Connection
302 name can change as a result of selecting a different printer device.  If
303 a device is specified, then changes are stored into the settings object
304 currently selected into that printer.  If a settings object is supplied,
305 then changes are recorded into it, and, it is selected into a
306 printer, then changes are propagated to that printer 
307 too.
308
309 Return value is nil if the user has canceled the dialog.  Otherwise, it
310 is a new plist, with the following properties:
311   name                   Printer device name, even if unchanged by the user.
312   from-page              First page to print, 1-based.  Returned if
313                          `selected-page-button' is `pages'.
314                          user, then this value is not included in the plist.
315   to-page                Last page to print, inclusive, 1-based.  Returned if
316                          `selected-page-button' is `pages'.
317   copies                 Number of copies to print.  Always returned.
318   selected-page-button   Which page button was selected (`all', `selection',
319                          or `pages').
320
321 The DEVICE is destroyed and an error is signaled in case of
322 initialization problem with the new printer.
323
324 See also the `page-setup' dialog box type.
325
326 ---------------------------------------------------------------------------
327
328 For type `page-setup':
329
330 This invokes the Windows standard Page Setup dialog.
331 This dialog is usually invoked in response to the Page Setup command,
332 and used to choose such parameters as page orientation, print margins
333 etc.  Note that this dialog contains the \"Printer\" button, which
334 invokes the Printer Setup dialog so that the user can update the
335 printer options or even select a different printer as well.
336
337 The keywords allowed are
338
339 :device
340   An 'msprinter device.
341 :print-settings
342   A printer settings object.
343 :properties
344   A plist of job properties.
345
346 Exactly one of these keywords must be given.
347
348 The function brings up the Page Setup dialog, where the user
349 can select a different printer and/or change printer options.
350 Connection name can change as a result of selecting a different printer
351 device.  If a device is specified, then changes are stored into the
352 settings object currently selected into that printer.  If a settings
353 object is supplied, then changes are recorded into it, and, it is
354 selected into a printer, then changes are propagated to that printer
355 too.
356
357 :properties specifies a plist of job properties;
358 see `default-msprinter-frame-plist' for the complete list.  The plist
359 is used to initialize the dialog.
360
361 Return value is nil if the user has canceled the dialog.  Otherwise,
362 it is a new plist, containing the new list of properties.
363
364 NOTE: The margin properties (returned by this function) are *NOT* stored
365 into the print-settings or device object.
366
367 The DEVICE is destroyed and an error is signaled in case of
368 initialization problem with the new printer.
369
370 See also the `print' dialog box type.
371
372 ---------------------------------------------------------------------------
373
374 For type `mswindows-message':
375
376 The keywords allowed are
377
378 :title
379   The title of the dialog box.
380 :message
381   The string to display.
382 :flags
383   A symbol or list of symbols:
384
385     -- To specify the buttons in the message box:
386     
387     abortretryignore 
388       The message box contains three push buttons: Abort, Retry, and Ignore. 
389     ok 
390       The message box contains one push button: OK. This is the default. 
391     okcancel 
392       The message box contains two push buttons: OK and Cancel. 
393     retrycancel 
394       The message box contains two push buttons: Retry and Cancel. 
395     yesno 
396       The message box contains two push buttons: Yes and No. 
397     yesnocancel 
398       The message box contains three push buttons: Yes, No, and Cancel. 
399     
400     
401     -- To display an icon in the message box:
402      
403     iconexclamation, iconwarning
404       An exclamation-point icon appears in the message box. 
405     iconinformation, iconasterisk
406       An icon consisting of a lowercase letter i in a circle appears in
407       the message box. 
408     iconquestion
409       A question-mark icon appears in the message box. 
410     iconstop, iconerror, iconhand
411       A stop-sign icon appears in the message box. 
412     
413     
414     -- To indicate the default button: 
415     
416     defbutton1
417       The first button is the default button.  This is the default.
418     defbutton2
419       The second button is the default button. 
420     defbutton3
421       The third button is the default button. 
422     defbutton4
423       The fourth button is the default button. 
424     
425     
426     -- To indicate the modality of the dialog box:
427      
428     applmodal
429       The user must respond to the message box before continuing work in
430       the window identified by the hWnd parameter. However, the user can
431       move to the windows of other applications and work in those windows.
432       Depending on the hierarchy of windows in the application, the user
433       may be able to move to other windows within the application. All
434       child windows of the parent of the message box are automatically
435       disabled, but popup windows are not.  This is the default.
436     systemmodal
437       Same as applmodal except that the message box has the WS_EX_TOPMOST
438       style. Use system-modal message boxes to notify the user of serious,
439       potentially damaging errors that require immediate attention (for
440       example, running out of memory). This flag has no effect on the
441       user's ability to interact with windows other than those associated
442       with hWnd.
443     taskmodal
444       Same as applmodal except that all the top-level windows belonging to
445       the current task are disabled if the hWnd parameter is NULL. Use
446       this flag when the calling application or library does not have a
447       window handle available but still needs to prevent input to other
448       windows in the current application without suspending other
449       applications.
450     
451     
452     In addition, you can specify the following flags: 
453     
454     default-desktop-only 
455       The desktop currently receiving input must be a default desktop;
456       otherwise, the function fails. A default desktop is one an
457       application runs on after the user has logged on.
458     help 
459       Adds a Help button to the message box. Choosing the Help button or
460       pressing F1 generates a Help event.
461     right 
462       The text is right-justified. 
463     rtlreading 
464       Displays message and caption text using right-to-left reading order
465       on Hebrew and Arabic systems.
466     setforeground 
467       The message box becomes the foreground window. Internally, Windows
468       calls the SetForegroundWindow function for the message box.
469     topmost 
470       The message box is created with the WS_EX_TOPMOST window style. 
471     service-notification 
472       Windows NT only: The caller is a service notifying the user of an
473       event. The function displays a message box on the current active
474       desktop, even if there is no user logged on to the computer.  If
475       this flag is set, the hWnd parameter must be NULL. This is so the
476       message box can appear on a desktop other than the desktop
477       corresponding to the hWnd.
478     
479
480   The return value is one of the following menu-item values returned by
481   the dialog box:
482    
483   abort
484     Abort button was selected. 
485   cancel
486     Cancel button was selected. 
487   ignore
488     Ignore button was selected. 
489   no
490     No button was selected. 
491   ok
492     OK button was selected. 
493   retry
494     Retry button was selected. 
495   yes
496     Yes button was selected. 
497   
498   If a message box has a Cancel button, the function returns the
499   `cancel' value if either the ESC key is pressed or the Cancel button
500   is selected.  If the message box has no Cancel button, pressing ESC has
501   no effect."
502   (flet ((dialog-box-modal-loop (thunk)
503            (let* ((frames (frame-list))
504                   (result
505                    ;; ok, this is extremely tricky.  normally a modal
506                    ;; dialog will pop itself down using (dialog-box-finish)
507                    ;; or (dialog-box-cancel), which throws back to this
508                    ;; catch.  but question dialog boxes pop down themselves
509                    ;; regardless, so a badly written question dialog box
510                    ;; that does not use (dialog-box-finish) could seriously
511                    ;; wedge us.  furthermore, we disable all other frames
512                    ;; in order to implement modality; we need to restore
513                    ;; them before the dialog box is destroyed, because
514                    ;; otherwise windows at least will notice that no top-
515                    ;; level window can have the focus and will shift the
516                    ;; focus to a different app, raising it and obscuring us.
517                    ;; so we create `delete-dialog-box-hook', which is
518                    ;; called right *before* the dialog box gets destroyed.
519                    ;; here, we put a hook on it, and when it's our dialog
520                    ;; box and not someone else's that's being destroyed,
521                    ;; we reenable all the frames and remove the hook.
522                    ;; BUT ...  we still have to deal with exiting the
523                    ;; modal loop in case it doesn't happen before us.
524                    ;; we can't do this until after the callbacks for this
525                    ;; dialog box get executed, and that doesn't happen until
526                    ;; after the dialog box is destroyed.  so to keep things
527                    ;; synchronous, we enqueue an eval event, which goes into
528                    ;; the same queue as the misc-user events encapsulating
529                    ;; the dialog callbacks and will go after it (because
530                    ;; destroying the dialog box happens after processing
531                    ;; its selection).  if the dialog boxes are written
532                    ;; properly, we don't see this eval event, because we've
533                    ;; already exited our modal loop. (Thus, we make sure the
534                    ;; function given in this eval event is actually defined
535                    ;; and does nothing.) If we do see it, though, we know
536                    ;; that we encountered a badly written dialog box and
537                    ;; need to exit now.  Currently we just return nil, but
538                    ;; maybe we should signal an error or issue a warning.
539                    (catch 'internal-dialog-box-finish
540                      (let ((id (eval thunk))
541                            (sym (gensym)))
542                        (fset sym
543                              `(lambda (did)
544                                 (when (eq ',id did)
545                                   (mapc 'enable-frame ',frames)
546                                   (enqueue-eval-event
547                                    'internal-make-dialog-box-exit did)
548                                   (remove-hook 'delete-dialog-box-hook
549                                                ',sym))))
550                        (add-hook 'delete-dialog-box-hook sym)
551                        (mapc 'disable-frame frames)
552                        (block nil
553                          (while t
554                            (let ((event (next-event)))
555                              (if (and (eval-event-p event)
556                                       (eq (event-function event)
557                                           'internal-make-dialog-box-exit)
558                                       (eq (event-object event) id))
559                                  (return '(nil))
560                                (dispatch-event event)))))))))
561              (if (listp result)
562                  (car result)
563                (signal 'quit nil)))))
564     (case type
565       (general
566         (cl-parsing-keywords
567             ((:title "XEmacs")
568              (:parent (selected-frame))
569              :modal
570              :properties
571              :spec)
572             ()
573           (flet ((create-dialog-box-frame ()
574                    (let* ((ftop (frame-property cl-parent 'top))
575                           (fleft (frame-property cl-parent 'left))
576                           (fwidth (frame-pixel-width cl-parent))
577                           (fheight (frame-pixel-height cl-parent))
578                           (fonth (font-height (face-font 'default)))
579                           (fontw (font-width (face-font 'default)))
580                           (cl-properties (append cl-properties
581                                                  dialog-frame-plist))
582                           (dfheight (plist-get cl-properties 'height))
583                           (dfwidth (plist-get cl-properties 'width))
584                           (unmapped (plist-get cl-properties
585                                                'initially-unmapped))
586                           (gutter-spec cl-spec)
587                           (name (or (plist-get cl-properties 'name) "XEmacs"))
588                           (frame nil))
589                      (plist-remprop cl-properties 'initially-unmapped)
590                      ;; allow the user to just provide a glyph
591                      (or (glyphp cl-spec) (setq cl-spec (make-glyph cl-spec)))
592                      (setq gutter-spec (copy-sequence "\n"))
593                      (set-extent-begin-glyph (make-extent 0 1 gutter-spec)
594                                              cl-spec)
595                      ;; under FVWM at least, if I don't specify the
596                      ;; initial position, it ends up always at (0, 0).
597                      ;; xwininfo doesn't tell me that there are any
598                      ;; program-specified position hints, so it must be
599                      ;; an FVWM bug.  So just be smashing and position in
600                      ;; the center of the selected frame.
601                      (setq frame
602                            (make-frame
603                             (append cl-properties
604                                     `(popup ,cl-parent initially-unmapped t
605                                             menubar-visible-p nil
606                                             has-modeline-p nil
607                                             default-toolbar-visible-p nil
608                                             top-gutter-visible-p t
609                                             top-gutter-height ,
610                                             (* dfheight fonth)
611                                             top-gutter ,gutter-spec
612                                             minibuffer none
613                                             name ,name
614                                             modeline-shadow-thickness 0
615                                             vertical-scrollbar-visible-p nil
616                                             horizontal-scrollbar-visible-p nil
617                                             unsplittable t
618                                             left ,(+ fleft (- (/ fwidth 2)
619                                                               (/ (* dfwidth
620                                                                     fontw)
621                                                                  2)))
622                                             top ,(+ ftop (- (/ fheight 2)
623                                                             (/ (* dfheight
624                                                                   fonth)
625                                                                2)))))))
626                      (set-face-foreground 'modeline [default foreground] frame)
627                      (set-face-background 'modeline [default background] frame)
628                      (unless unmapped (make-frame-visible frame))
629                      (let ((newbuf (generate-new-buffer " *dialog box*")))
630                        (set-buffer-dedicated-frame newbuf frame)
631                        (set-frame-property frame 'dialog-box-buffer newbuf)
632                        (set-window-buffer (frame-root-window frame) newbuf)
633                        (with-current-buffer newbuf
634                          (set (make-local-variable 'frame-title-format)
635                               cl-title)
636                          (add-local-hook 'delete-frame-hook
637                                          #'(lambda (frame)
638                                              (kill-buffer
639                                               (frame-property
640                                                frame
641                                                'dialog-box-buffer))))))
642                      frame)))
643             (if cl-modal
644                 (dialog-box-modal-loop '(create-dialog-box-frame))
645               (create-dialog-box-frame)))))
646       (question
647         (cl-parsing-keywords
648             ((:modal nil))
649             t
650           (remf cl-keys :modal)
651           (if cl-modal
652               (dialog-box-modal-loop `(make-dialog-box-internal ',type
653                                                                 ',cl-keys))
654             (make-dialog-box-internal type cl-keys))))
655       (t
656         (make-dialog-box-internal type cl-keys)))))
657
658 (defun dialog-box-finish (result)
659   "Exit a modal dialog box, returning RESULT.
660 This is meant to be executed from a dialog box callback function."
661   (throw 'internal-dialog-box-finish (list result)))
662
663 (defun dialog-box-cancel ()
664   "Cancel a modal dialog box.
665 This is meant to be executed from a dialog box callback function."
666   (throw 'internal-dialog-box-finish 'cancel))
667
668 ;; an eval event, used as a trigger inside of the dialog modal loop.
669 (defun internal-make-dialog-box-exit (did)
670   nil)
671
672 (make-obsolete 'popup-dialog-box 'make-dialog-box)
673 (defun popup-dialog-box (desc)
674   "Obsolete equivalent of (make-dialog-box 'question ...).
675
676 \(popup-dialog-box (QUESTION BUTTONS ...)
677
678 is equivalent to
679
680 \(make-dialog-box 'question :question QUESTION :buttons BUTTONS)"
681   (check-argument-type 'stringp (car desc))
682   (or (consp (cdr desc))
683       (error 'syntax-error
684              "Dialog descriptor must supply at least one button"
685              desc))
686   (make-dialog-box 'question :question (car desc) :buttons (cdr desc)))
687
688 ;;; dialog.el ends here