import xemacs-21.2.37
[chise/xemacs-chise.git.1] / src / dialog-msw.c
1 /* Implements elisp-programmable dialog boxes -- MS Windows interface.
2    Copyright (C) 1998 Kirill M. Katsnelson <kkm@kis.ru>
3    Copyright (C) 2000 Ben Wing.
4
5 This file is part of XEmacs.
6
7 XEmacs is free software; you can redistribute it and/or modify it
8 under the terms of the GNU General Public License as published by the
9 Free Software Foundation; either version 2, or (at your option) any
10 later version.
11
12 XEmacs is distributed in the hope that it will be useful, but WITHOUT
13 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
15 for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with XEmacs; see the file COPYING.  If not, write to
19 the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20 Boston, MA 02111-1307, USA.  */
21
22 /* Synched up with: Not in FSF. */
23
24 /* Author:
25    Initially written by kkm, May 1998
26 */
27
28 #include <config.h>
29 #include "lisp.h"
30
31 #include "buffer.h"
32 #include "console-msw.h"
33 #include "frame.h"
34 #include "gui.h"
35 #include "opaque.h"
36
37 #include <cderr.h>
38 #include <commdlg.h>
39
40 Lisp_Object Qdialog_box_error;
41
42 static Lisp_Object Q_initial_directory;
43 static Lisp_Object Q_initial_filename;
44 static Lisp_Object Q_filter_list;
45 static Lisp_Object Q_title;
46 static Lisp_Object Q_allow_multi_select;
47 static Lisp_Object Q_create_prompt_on_nonexistent;
48 static Lisp_Object Q_overwrite_prompt;
49 static Lisp_Object Q_file_must_exist;
50 static Lisp_Object Q_no_network_button;
51 static Lisp_Object Q_no_read_only_return;
52
53 /* List containing all dialog data structures of currently popped up
54    dialogs. */
55 static Lisp_Object Vdialog_data_list;
56
57 /* List of popup frames wanting keyboard traversal handled */
58 static Lisp_Object Vpopup_frame_list;
59
60 Lisp_Object Vdefault_file_dialog_filter_alist;
61
62 /* DLUs per character metrics */
63 #define X_DLU_PER_CHAR       4
64 #define Y_DLU_PER_CHAR       8
65
66 /*
67   Button metrics
68   --------------
69   All buttons have height of 15 DLU. The minimum width for a button is 32 DLU, 
70   but it can be expanded to accommodate its text, so the width is calculated as
71   8 DLU per button plus 4 DLU per character.
72   max (32, 6 * text_length). The factor of six is rather empirical, but it
73   works better than 8 which comes from the definition of a DLU. Buttons are
74   spaced with 6 DLU gap. Minimum distance from the button to the left or right 
75   dialog edges is 6 DLU, and the distance between the dialog bottom edge and
76   buttons is 7 DLU.
77 */
78
79 #define X_MIN_BUTTON        32
80 #define X_BUTTON_MARGIN      8
81 #define Y_BUTTON            15
82 #define X_BUTTON_SPACING     6
83 #define X_BUTTON_FROM_EDGE   6
84 #define Y_BUTTON_FROM_EDGE   7
85
86 /* 
87    Text field metrics
88    ------------------
89    Text distance from left and right edges is the same as for buttons, and the
90    top margin is 11 DLU. The static control has height of 2 DLU per control
91    plus 8 DLU per each line of text. Distance between the bottom edge of the
92    control and the button row is 15 DLU. Minimum width of the static control
93    is 100 DLU, thus giving minimum dialog weight of 112 DLU. Maximum width is
94    300 DLU, and, if the text is wider than that, the text is wrapped on the
95    next line. Each character in the text is considered 4 DLU wide.
96 */
97
98 #define X_MIN_TEXT         100
99 #define X_AVE_TEXT         200
100 #define X_MAX_TEXT         300
101 #define X_TEXT_FROM_EDGE      X_BUTTON_FROM_EDGE
102 #define Y_TEXT_FROM_EDGE    11
103 #define Y_TEXT_MARGIN        2
104 #define Y_TEXT_FROM_BUTTON  15
105
106 #define X_MIN_TEXT_CHAR    (X_MIN_TEXT / X_DLU_PER_CHAR)
107 #define X_AVE_TEXT_CHAR    (X_AVE_TEXT / X_DLU_PER_CHAR)
108 #define X_MAX_TEXT_CHAR    (X_MAX_TEXT / X_DLU_PER_CHAR)
109
110 /*
111   Layout algorithm
112   ----------------
113   First we calculate the minimum width of the button row, excluding "from
114   edge" distances. Note that the static control text can be narrower than
115   X_AVE_TEXT only if both text and button row are narrower than that (so,
116   even if text *can* be wrapped into 2 rows narrower than ave width, it is not 
117   done). Let WBR denote the width of the button row.
118
119   Next, the width of the static field is determined.
120   First, if all lines of text fit into max (WBR, X_MAX_TEXT), the width of the
121   control is the same as the width of the longest line. 
122   Second, if all lines of text are narrower than X_MIN_TEXT, then width of
123   the control is set to X_MIN_TEXT.
124   Otherwise, width is set to max(WBR, X_AVE_TEXT). In this case, line wrapping will
125   happen.
126
127   If width of the text control is larger than that of the button row, then the
128   latter is centered across the dialog, by giving it extra edge
129   margins. Otherwise, minimal margins are given to the button row.
130 */
131
132 #define ID_ITEM_BIAS 32
133
134 void
135 mswindows_register_popup_frame (Lisp_Object frame)
136 {
137   Vpopup_frame_list = Fcons (frame, Vpopup_frame_list);
138 }
139
140 void
141 mswindows_unregister_popup_frame (Lisp_Object frame)
142 {
143   Vpopup_frame_list = delq_no_quit (frame, Vpopup_frame_list);
144 }
145
146 /* Dispatch message to any dialog boxes.  Return non-zero if dispatched. */
147 int
148 mswindows_is_dialog_msg (MSG *msg)
149 {
150   LIST_LOOP_2 (data, Vdialog_data_list)
151     {
152       if (IsDialogMessage (XMSWINDOWS_DIALOG_ID (data)->hwnd, msg))
153         return 1;
154     }
155
156   {
157     LIST_LOOP_2 (popup, Vpopup_frame_list)
158       {
159         HWND hwnd = FRAME_MSWINDOWS_HANDLE (XFRAME (popup));
160         /* This is a windows feature that allows dialog type
161            processing to be applied to standard windows containing
162            controls. */
163         if (IsDialogMessage (hwnd, msg))
164           return 1;
165       }
166   }
167   return 0;
168 }
169
170 static Lisp_Object
171 mark_mswindows_dialog_id (Lisp_Object obj)
172 {
173   struct mswindows_dialog_id *data = XMSWINDOWS_DIALOG_ID (obj);
174   mark_object (data->frame);
175   return data->callbacks;
176 }
177
178 DEFINE_LRECORD_IMPLEMENTATION ("mswindows-dialog-id", mswindows_dialog_id,
179                                mark_mswindows_dialog_id, 0, 0, 0, 0, 0,
180                                struct mswindows_dialog_id);
181
182 /* Dialog procedure */
183 static BOOL CALLBACK 
184 dialog_proc (HWND hwnd, UINT msg, WPARAM w_param, LPARAM l_param)
185 {
186   switch (msg)
187     {
188     case WM_INITDIALOG:
189       SetWindowLong (hwnd, DWL_USER, l_param);
190       break;
191       
192     case WM_DESTROY:
193       {
194         Lisp_Object data;
195         VOID_TO_LISP (data, GetWindowLong (hwnd, DWL_USER));
196         Vdialog_data_list = delq_no_quit (data, Vdialog_data_list);
197       }
198       break;
199
200     case WM_COMMAND:
201       {
202         Lisp_Object fn, arg, data;
203         struct mswindows_dialog_id *did;
204
205         VOID_TO_LISP (data, GetWindowLong (hwnd, DWL_USER));
206         did = XMSWINDOWS_DIALOG_ID (data);
207         if (w_param != IDCANCEL) /* user pressed escape */
208           {
209             assert (w_param >= ID_ITEM_BIAS 
210                     && w_param
211                     < XVECTOR_LENGTH (did->callbacks) + ID_ITEM_BIAS);
212             
213             get_gui_callback (XVECTOR_DATA (did->callbacks)
214                               [w_param - ID_ITEM_BIAS],
215                               &fn, &arg);
216             mswindows_enqueue_misc_user_event (did->frame, fn, arg);
217           }
218         else
219           mswindows_enqueue_misc_user_event (did->frame, Qrun_hooks,
220                                              Qmenu_no_selection_hook);
221         /* #### need to error-protect!  will do so when i merge in
222            my working ws */
223         va_run_hook_with_args (Qdelete_dialog_box_hook, 1, data);
224
225         DestroyWindow (hwnd);
226       }
227       break;
228
229     default:
230       return FALSE;
231     }
232   return TRUE;
233 }
234
235 /* Helper function which converts the supplied string STRING into Unicode and
236    pushes it at the end of DYNARR */
237 static void
238 push_lisp_string_as_unicode (unsigned_char_dynarr* dynarr, Lisp_Object string)
239 {
240   Extbyte *mbcs_string;
241   Charcount length = XSTRING_CHAR_LENGTH (string);
242   LPWSTR uni_string;
243
244   TO_EXTERNAL_FORMAT (LISP_STRING, string,
245                       C_STRING_ALLOCA, mbcs_string,
246                       Qnative);
247   uni_string = alloca_array (WCHAR, length + 1);
248   length = MultiByteToWideChar (CP_ACP, 0, mbcs_string, -1,
249                                 uni_string, sizeof(WCHAR) * (length + 1));
250   Dynarr_add_many (dynarr, uni_string, sizeof(WCHAR) * length);
251 }
252
253 /* Helper function which converts the supplied string STRING into Unicode and
254    pushes it at the end of DYNARR */
255 static void
256 push_bufbyte_string_as_unicode (unsigned_char_dynarr* dynarr, Bufbyte *string,
257                                 Bytecount len)
258 {
259   Extbyte *mbcs_string;
260   Charcount length = bytecount_to_charcount (string, len);
261   LPWSTR uni_string;
262
263   TO_EXTERNAL_FORMAT (C_STRING, string,
264                       C_STRING_ALLOCA, mbcs_string,
265                       Qnative);
266   uni_string = alloca_array (WCHAR, length + 1);
267   length = MultiByteToWideChar (CP_ACP, 0, mbcs_string, -1,
268                                 uni_string, sizeof(WCHAR) * (length + 1));
269   Dynarr_add_many (dynarr, uni_string, sizeof(WCHAR) * length);
270 }
271
272 /* Given button TEXT, return button width in DLU */
273 static unsigned int
274 button_width (Lisp_Object text)
275 {
276   unsigned int width = X_DLU_PER_CHAR * XSTRING_CHAR_LENGTH (text);
277   return max (X_MIN_BUTTON, width);
278 }
279
280 /* Unwind protection routine frees a dynarr opaqued into arg */
281 static Lisp_Object
282 free_dynarr_opaque_ptr (Lisp_Object arg)
283 {
284   Dynarr_free (get_opaque_ptr (arg));
285   return arg;
286 }
287
288
289 #define ALIGN_TEMPLATE                                  \
290 {                                                       \
291   unsigned int slippage = Dynarr_length (template) & 3; \
292   if (slippage)                                         \
293     Dynarr_add_many (template, &zeroes, slippage);      \
294 }
295
296 static struct
297 {
298   int errmess;
299   char *errname;
300 } common_dialog_errors[] =
301 {
302   { CDERR_DIALOGFAILURE, "CDERR_DIALOGFAILURE" },
303   { CDERR_FINDRESFAILURE, "CDERR_FINDRESFAILURE" },
304   { CDERR_INITIALIZATION, "CDERR_INITIALIZATION" },
305   { CDERR_LOADRESFAILURE, "CDERR_LOADRESFAILURE" },
306   { CDERR_LOADSTRFAILURE, "CDERR_LOADSTRFAILURE" },
307   { CDERR_LOCKRESFAILURE, "CDERR_LOCKRESFAILURE" },
308   { CDERR_MEMALLOCFAILURE, "CDERR_MEMALLOCFAILURE" },
309   { CDERR_MEMLOCKFAILURE, "CDERR_MEMLOCKFAILURE" },
310   { CDERR_NOHINSTANCE, "CDERR_NOHINSTANCE" },
311   { CDERR_NOHOOK, "CDERR_NOHOOK" },
312   { CDERR_NOTEMPLATE, "CDERR_NOTEMPLATE" },
313   { CDERR_REGISTERMSGFAIL, "CDERR_REGISTERMSGFAIL" },
314   { CDERR_STRUCTSIZE, "CDERR_STRUCTSIZE" },
315   { PDERR_CREATEICFAILURE, "PDERR_CREATEICFAILURE" },
316   { PDERR_DEFAULTDIFFERENT, "PDERR_DEFAULTDIFFERENT" },
317   { PDERR_DNDMMISMATCH, "PDERR_DNDMMISMATCH" },
318   { PDERR_GETDEVMODEFAIL, "PDERR_GETDEVMODEFAIL" },
319   { PDERR_INITFAILURE, "PDERR_INITFAILURE" },
320   { PDERR_LOADDRVFAILURE, "PDERR_LOADDRVFAILURE" },
321   { PDERR_NODEFAULTPRN, "PDERR_NODEFAULTPRN" },
322   { PDERR_NODEVICES, "PDERR_NODEVICES" },
323   { PDERR_PARSEFAILURE, "PDERR_PARSEFAILURE" },
324   { PDERR_PRINTERNOTFOUND, "PDERR_PRINTERNOTFOUND" },
325   { PDERR_RETDEFFAILURE, "PDERR_RETDEFFAILURE" },
326   { PDERR_SETUPFAILURE, "PDERR_SETUPFAILURE" },
327   { CFERR_MAXLESSTHANMIN, "CFERR_MAXLESSTHANMIN" },
328   { CFERR_NOFONTS, "CFERR_NOFONTS" },
329   { FNERR_BUFFERTOOSMALL, "FNERR_BUFFERTOOSMALL" },
330   { FNERR_INVALIDFILENAME, "FNERR_INVALIDFILENAME" },
331   { FNERR_SUBCLASSFAILURE, "FNERR_SUBCLASSFAILURE" },
332   { FRERR_BUFFERLENGTHZERO, "FRERR_BUFFERLENGTHZERO" },
333 };
334
335 static Lisp_Object
336 handle_file_dialog_box (struct frame *f, Lisp_Object keys)
337 {
338   OPENFILENAME ofn;
339   char fnbuf[8000];
340
341   xzero (ofn);
342   ofn.lStructSize = sizeof (ofn);
343   ofn.hwndOwner = FRAME_MSWINDOWS_HANDLE (f);
344   ofn.lpstrFile = fnbuf;
345   ofn.nMaxFile = sizeof (fnbuf) / XETCHAR_SIZE;
346   xetcscpy (fnbuf, XETEXT (""));
347
348   LOCAL_FILE_FORMAT_TO_TSTR (Fexpand_file_name (build_string (""), Qnil),
349                              ofn.lpstrInitialDir);
350
351   {
352     EXTERNAL_PROPERTY_LIST_LOOP_3 (key, value, keys)
353       {
354         if (EQ (key, Q_initial_filename))
355           {
356             Extbyte *fnout;
357
358             CHECK_STRING (value);
359             LOCAL_FILE_FORMAT_TO_TSTR (value, fnout);
360             xetcscpy (fnbuf, fnout);
361           }
362         else if (EQ (key, Q_title))
363           {
364             CHECK_STRING (value);
365             LISP_STRING_TO_EXTERNAL (value, ofn.lpstrTitle, Qmswindows_tstr);
366           }
367         else if (EQ (key, Q_initial_directory))
368           LOCAL_FILE_FORMAT_TO_TSTR (Fexpand_file_name (value, Qnil),
369                                      ofn.lpstrInitialDir);
370         else if (EQ (key, Q_file_must_exist))
371           {
372             if (!NILP (value))
373               ofn.Flags |= OFN_FILEMUSTEXIST | OFN_PATHMUSTEXIST;
374             else
375               ofn.Flags &= ~(OFN_FILEMUSTEXIST | OFN_PATHMUSTEXIST);
376           }
377         else
378           syntax_error ("Unrecognized file-dialog keyword", key);
379       }
380   }
381
382   if (!GetOpenFileName (&ofn))
383     {
384       DWORD err = CommDlgExtendedError ();
385       if (!err)
386         {
387           while (1)
388             signal_quit ();
389         }
390       else
391         {
392           int i;
393
394           for (i = 0; i < countof (common_dialog_errors); i++)
395             {
396               if (common_dialog_errors[i].errmess == err)
397                 signal_type_error (Qdialog_box_error,
398                                    "Creating file-dialog-box",
399                                    build_string
400                                    (common_dialog_errors[i].errname));
401             }
402
403           signal_type_error (Qdialog_box_error,
404                              "Unknown common dialog box error???",
405                              make_int (err));
406         }
407     }
408
409   return tstr_to_local_file_format (ofn.lpstrFile);
410 }
411
412 static Lisp_Object
413 handle_question_dialog_box (struct frame *f, Lisp_Object keys)
414 {
415   Lisp_Object_dynarr *dialog_items = Dynarr_new (Lisp_Object);
416   unsigned_char_dynarr *template = Dynarr_new (unsigned_char);
417   unsigned int button_row_width = 0;
418   unsigned int text_width, text_height;
419   Lisp_Object question = Qnil, title = Qnil;
420
421   int unbind_count = specpdl_depth ();
422   record_unwind_protect (free_dynarr_opaque_ptr,
423                          make_opaque_ptr (dialog_items));
424   record_unwind_protect (free_dynarr_opaque_ptr,
425                          make_opaque_ptr (template));
426
427   /* A big NO NEED to GCPRO gui_items stored in the array: they are just
428      pointers into KEYS list, which is GC-protected by the caller */
429
430   {
431     EXTERNAL_PROPERTY_LIST_LOOP_3 (key, value, keys)
432       {
433         if (EQ (key, Q_question))
434           {
435             CHECK_STRING (value);
436             question = value;
437           }
438         else if (EQ (key, Q_title))
439           {
440             CHECK_STRING (value);
441             title = value;
442           }
443         else if (EQ (key, Q_buttons))
444           {
445             Lisp_Object item_cons;
446
447             /* Parse each item in the dialog into gui_item structs,
448                and stuff a dynarr of these. Calculate button row width
449                in this loop too */
450             EXTERNAL_LIST_LOOP (item_cons, value)
451               {
452                 if (!NILP (XCAR (item_cons)))
453                   {
454                     Lisp_Object gitem =
455                       gui_parse_item_keywords (XCAR (item_cons));
456                     Dynarr_add (dialog_items, gitem);
457                     button_row_width += button_width (XGUI_ITEM (gitem)->name) 
458                       + X_BUTTON_MARGIN;
459                   }
460               }
461
462             button_row_width -= X_BUTTON_MARGIN;
463           }
464         else
465           syntax_error ("Unrecognized question-dialog keyword", key);
466       }
467   }
468
469   if (Dynarr_length (dialog_items) == 0)
470     syntax_error ("Dialog descriptor provides no buttons", keys);
471
472   if (NILP (question))
473     syntax_error ("Dialog descriptor provides no question", keys);
474
475   /* Determine the final width layout */
476   {
477     Bufbyte *p = XSTRING_DATA (question);
478     Charcount string_max = 0, this_length = 0;
479     while (1)
480       {
481         Emchar ch = charptr_emchar (p);
482         INC_CHARPTR (p);
483         
484         if (ch == (Emchar)'\n' || ch == (Emchar)'\0')
485           {
486             string_max = max (this_length, string_max);
487             this_length = 0;
488           }
489         else
490           ++this_length;
491
492         if (ch == (Emchar)'\0')
493           break;
494       }
495
496     if (string_max * X_DLU_PER_CHAR > max (X_MAX_TEXT, button_row_width))
497       text_width = X_AVE_TEXT;
498     else if (string_max * X_DLU_PER_CHAR < X_MIN_TEXT)
499       text_width = X_MIN_TEXT;
500     else
501       text_width = string_max * X_DLU_PER_CHAR;
502     text_width = max (text_width, button_row_width);
503   }
504   
505   /* Now calculate the height for the text control */
506   {
507     Bufbyte *p = XSTRING_DATA (question);
508     Charcount break_at = text_width / X_DLU_PER_CHAR;
509     Charcount char_pos = 0;
510     int num_lines = 1;
511     Emchar ch;
512     
513     while ((ch = charptr_emchar (p)) != (Emchar) '\0')
514       {
515         INC_CHARPTR (p);
516         char_pos += ch != (Emchar) '\n';
517         if (ch == (Emchar) '\n' || char_pos == break_at)
518           {
519             ++num_lines;
520             char_pos = 0;
521           }
522       }
523     text_height = Y_TEXT_MARGIN + Y_DLU_PER_CHAR * num_lines;
524   }
525
526   /* Ok, now we are ready to stuff the dialog template and lay out controls */
527   {
528     DLGTEMPLATE dlg_tem;
529     DLGITEMTEMPLATE item_tem;
530     int i;
531     const unsigned int zeroes = 0;
532     const unsigned int ones = 0xFFFFFFFF;
533     const WORD static_class_id = 0x0082;
534     const WORD button_class_id = 0x0080;
535
536     /* Create and stuff in DLGTEMPLATE header */
537     dlg_tem.style = (DS_CENTER | DS_MODALFRAME | DS_SETFONT
538                      | WS_CAPTION | WS_POPUP | WS_VISIBLE);
539     dlg_tem.dwExtendedStyle = 0;
540     dlg_tem.cdit = Dynarr_length (dialog_items) + 1;
541     dlg_tem.x = 0;
542     dlg_tem.y = 0;
543     dlg_tem.cx = text_width + 2 * X_TEXT_FROM_EDGE;
544     dlg_tem.cy = (Y_TEXT_FROM_EDGE + text_height + Y_TEXT_FROM_BUTTON
545                   + Y_BUTTON + Y_BUTTON_FROM_EDGE);
546     Dynarr_add_many (template, &dlg_tem, sizeof (dlg_tem));
547
548     /* We want no menu and standard class */
549     Dynarr_add_many (template, &zeroes, 4);
550
551     /* And the third is the dialog title. "XEmacs" unless one is supplied.
552        Note that the string must be in Unicode. */
553     if (NILP (title))
554       Dynarr_add_many (template, L"XEmacs", 14);
555     else
556       push_lisp_string_as_unicode (template, title);
557
558     /* We want standard dialog font */
559     Dynarr_add_many (template, L"\x08MS Shell Dlg", 28);
560
561     /* Next add text control. */
562     item_tem.style = WS_CHILD | WS_VISIBLE | SS_LEFT | SS_NOPREFIX;
563     item_tem.dwExtendedStyle = 0;
564     item_tem.x = X_TEXT_FROM_EDGE;
565     item_tem.y = Y_TEXT_FROM_EDGE;
566     item_tem.cx = text_width;
567     item_tem.cy = text_height;
568     item_tem.id = 0xFFFF;
569
570     ALIGN_TEMPLATE;
571     Dynarr_add_many (template, &item_tem, sizeof (item_tem));
572
573     /* Right after class id follows */
574     Dynarr_add_many (template, &ones, 2);
575     Dynarr_add_many (template, &static_class_id, sizeof (static_class_id));
576
577     /* Next thing to add is control text, as Unicode string */
578     push_lisp_string_as_unicode (template, question);
579
580     /* Specify 0 length creation data */
581     Dynarr_add_many (template, &zeroes, 2);
582
583     /* Now it's the button time */
584     item_tem.y = Y_TEXT_FROM_EDGE + text_height + Y_TEXT_FROM_BUTTON;
585     item_tem.x = X_BUTTON_FROM_EDGE + (button_row_width < text_width
586                                        ? (text_width - button_row_width) / 2
587                                        : 0);
588     item_tem.cy = Y_BUTTON;
589     item_tem.dwExtendedStyle = 0;
590
591     for (i = 0; i < Dynarr_length (dialog_items); ++i)
592       {
593         Lisp_Object* gui_item = Dynarr_atp (dialog_items, i);
594         Lisp_Gui_Item *pgui_item = XGUI_ITEM (*gui_item);
595
596         item_tem.style = (WS_CHILD | WS_VISIBLE | WS_TABSTOP | BS_PUSHBUTTON
597                           | (gui_item_active_p (*gui_item) ? 0 : WS_DISABLED));
598         item_tem.cx = button_width (pgui_item->name);
599         /* Item ids are indices into dialog_items plus offset, to avoid having
600            items by reserved ids (IDOK, IDCANCEL) */
601         item_tem.id = i + ID_ITEM_BIAS;
602
603         ALIGN_TEMPLATE;
604         Dynarr_add_many (template, &item_tem, sizeof (item_tem));
605
606         /* Right after 0xFFFF and class id atom follows */
607         Dynarr_add_many (template, &ones, 2);
608         Dynarr_add_many (template, &button_class_id, sizeof (button_class_id));
609
610         /* Next thing to add is control text, as Unicode string */
611         {
612           Lisp_Object ctext = pgui_item->name;
613           Emchar accel_unused;
614           Bufbyte *trans = (Bufbyte *) alloca (2 * XSTRING_LENGTH (ctext) + 3);
615           Bytecount translen;
616
617           memcpy (trans, XSTRING_DATA (ctext), XSTRING_LENGTH (ctext) + 1);
618           translen =
619             mswindows_translate_menu_or_dialog_item (trans,
620                                                XSTRING_LENGTH (ctext),
621                                                2 * XSTRING_LENGTH (ctext) + 3,
622                                                &accel_unused,
623                                                ctext);
624           push_bufbyte_string_as_unicode (template, trans, translen);
625         }
626
627         /* Specify 0 length creation data. */
628         Dynarr_add_many (template, &zeroes, 2);
629
630         item_tem.x += item_tem.cx + X_BUTTON_SPACING;
631       }
632   }
633
634   /* Now the Windows dialog structure is ready. We need to prepare a
635      data structure for the new dialog, which will contain callbacks
636      and the frame for these callbacks.  This structure has to be
637      GC-protected and thus it is put into a statically protected
638      list. */
639   {
640     Lisp_Object dialog_data;
641     int i;
642     struct mswindows_dialog_id *did =
643       alloc_lcrecord_type (struct mswindows_dialog_id,
644                            &lrecord_mswindows_dialog_id);
645
646     XSETMSWINDOWS_DIALOG_ID (dialog_data, did);
647
648     did->frame = wrap_frame (f);
649     did->callbacks = make_vector (Dynarr_length (dialog_items), Qunbound);
650     for (i = 0; i < Dynarr_length (dialog_items); i++)
651       XVECTOR_DATA (did->callbacks) [i] =
652         XGUI_ITEM (*Dynarr_atp (dialog_items, i))->callback;
653     
654     /* Woof! Everything is ready. Pop pop pop in now! */
655     did->hwnd =
656       CreateDialogIndirectParam (NULL,
657                                  (LPDLGTEMPLATE) Dynarr_atp (template, 0),
658                                  FRAME_MSWINDOWS_HANDLE (f), dialog_proc,
659                                  (LPARAM) LISP_TO_VOID (dialog_data));
660     if (!did->hwnd)
661       /* Something went wrong creating the dialog */
662       signal_type_error (Qdialog_box_error, "Creating dialog", keys);
663
664     Vdialog_data_list = Fcons (dialog_data, Vdialog_data_list);
665
666     /* Cease protection and free dynarrays */
667     unbind_to (unbind_count, Qnil);
668     return dialog_data;
669   }
670 }
671
672 static Lisp_Object
673 mswindows_make_dialog_box_internal (struct frame* f, Lisp_Object type,
674                                     Lisp_Object keys)
675 {
676   if (EQ (type, Qfile))
677     return handle_file_dialog_box (f, keys);
678   else if (EQ (type, Qquestion))
679     return handle_question_dialog_box (f, keys);
680   else if (EQ (type, Qprint))
681     return mswindows_handle_print_dialog_box (f, keys);
682   else if (EQ (type, Qpage_setup))
683     return mswindows_handle_page_setup_dialog_box (f, keys);
684   else if (EQ (type, Qprint_setup))
685     return mswindows_handle_print_setup_dialog_box (f, keys);
686   else
687     signal_type_error (Qunimplemented, "Dialog box type", type);
688   return Qnil;
689 }
690
691 void
692 console_type_create_dialog_mswindows (void)
693 {
694   CONSOLE_HAS_METHOD (mswindows, make_dialog_box_internal);
695 }
696
697 void
698 syms_of_dialog_mswindows (void)
699 {
700   INIT_LRECORD_IMPLEMENTATION (mswindows_dialog_id);
701
702   DEFKEYWORD (Q_initial_directory);
703   DEFKEYWORD (Q_initial_filename);
704   DEFKEYWORD (Q_filter_list);
705   DEFKEYWORD (Q_title);
706   DEFKEYWORD (Q_allow_multi_select);
707   DEFKEYWORD (Q_create_prompt_on_nonexistent);
708   DEFKEYWORD (Q_overwrite_prompt);
709   DEFKEYWORD (Q_file_must_exist);
710   DEFKEYWORD (Q_no_network_button);
711   DEFKEYWORD (Q_no_read_only_return);
712
713   /* Errors */
714   DEFERROR_STANDARD (Qdialog_box_error, Qinvalid_operation);
715 }
716
717 void
718 vars_of_dialog_mswindows (void)
719 {
720   Vpopup_frame_list = Qnil;
721   staticpro (&Vpopup_frame_list);
722
723   Vdialog_data_list = Qnil;
724   staticpro (&Vdialog_data_list);
725
726   DEFVAR_LISP ("default-file-dialog-filter-alist",
727                &Vdefault_file_dialog_filter_alist /*
728 */ );
729   Vdefault_file_dialog_filter_alist =
730     list5 (Fcons (build_string ("Text Files"), build_string ("*.txt")),
731            Fcons (build_string ("C Files"), build_string ("*.c;*.h")),
732            Fcons (build_string ("Elisp Files"), build_string ("*.el")),
733            Fcons (build_string ("HTML Files"), build_string ("*.html;*.html")),
734            Fcons (build_string ("All Files"), build_string ("*.*")));
735 }