OSDN Git Service

* security.cc (alloc_sd): Re-enable generating default permission
[pf3gnuchains/pf3gnuchains3x.git] / libgui / src / tclmsgbox.c
1 /* tclmsgbox.c -- Tcl code to handle a Windows MessageBox in the background.
2    Copyright (C) 1998 Cygnus Solutions.
3    Written by Ian Lance Taylor <ian@cygnus.com>.  */
4
5 #ifdef _WIN32
6
7 #include <tcl.h>
8 #include <tk.h>
9
10 #include <windows.h>
11
12 /* FIXME: We use some internal Tcl and Tk Windows stuff.  */
13 #include <tkWinInt.h>
14
15 EXTERN HINSTANCE TclWinGetTclInstance (void);
16
17 #include "guitcl.h"
18
19 /* This file defines a single Tcl command.
20
21    ide_messageBox CODE [ARGUMENTS]
22
23        This is just like tk_messageBox, except that it does not return
24        a value.  Instead, when the user clicks on a button closing the
25        message box, this invokes CODE, appending the selected value.
26
27        On Windows, this runs the MessageBox function in another
28        thread.  This permits a program which handles IDE requests from
29        other programs to not return from the request until the
30        MessageBox completes.  This is not possible without using
31        another thread, since the MessageBox function call will be
32        running its own event loop, and will be higher on the stack
33        than the IDE request.
34
35        On Unix tk_messageBox runs in the regular Tk event loop, so
36        another thread is not required.
37
38    */
39
40 static LRESULT CALLBACK msgbox_wndproc (HWND, UINT, WPARAM, LPARAM);
41 static int msgbox_eventproc (Tcl_Event *, int);
42
43 /* The hidden message box window.  */
44
45 static HWND hidden_hwnd;
46
47 /* The message number we use to indicate that the MessageBox call has
48    completed.  */
49
50 #define MSGBOX_MESSAGE (WM_USER + 1)
51
52 /* We pass a pointer to this structure to the thread function.  It
53    passes it back to the hidden window procedure.  */
54
55 struct msgbox_data
56 {
57   /* Tcl interpreter.  */
58   Tcl_Interp *interp;
59   /* Tcl code to execute when MessageBox completes.  */
60   char *code;
61   /* Hidden window handle.  */
62   HWND hidden_hwnd;
63   /* MessageBox arguments.  */
64   HWND hwnd;
65   char *message;
66   char *title;
67   int flags;
68   /* Result of MessageBox call.  */
69   int result;
70 };
71
72 /* This is the structure we pass to Tcl_QueueEvent.  */
73
74 struct msgbox_event
75 {
76   /* The base structure for all events.  */
77   Tcl_Event header;
78   /* The message box data for this event.  */
79   struct msgbox_data *md;
80 };
81
82 /* Initialize a hidden window to handle messages from the message box
83    thread.  */
84
85 static int
86 msgbox_init ()
87 {
88   WNDCLASS class;
89
90   if (hidden_hwnd != NULL)
91     return TCL_OK;
92
93   class.style = 0;
94   class.cbClsExtra = 0;
95   class.cbWndExtra = 0;
96   class.hInstance = TclWinGetTclInstance();
97   class.hbrBackground = NULL;
98   class.lpszMenuName = NULL;
99   class.lpszClassName = "ide_messagebox";
100   class.lpfnWndProc = msgbox_wndproc;
101   class.hIcon = NULL;
102   class.hCursor = NULL;
103
104   if (! RegisterClass (&class))
105     return TCL_ERROR;
106
107   hidden_hwnd = CreateWindow ("ide_messagebox", "ide_messagebox", WS_TILED,
108                               0, 0, 0, 0, NULL, NULL, class.hInstance, NULL);
109   if (hidden_hwnd == NULL)
110     return TCL_ERROR;
111
112   return TCL_OK;
113 }
114
115 /* This is called as an exit handler.  */
116
117 static void
118 msgbox_exit (ClientData cd)
119 {
120   if (hidden_hwnd != NULL)
121     {
122       UnregisterClass ("ide_messagebox", TclWinGetTclInstance ());
123       DestroyWindow (hidden_hwnd);
124       hidden_hwnd = NULL;
125
126       /* FIXME: Ideally we would kill off any remaining threads and
127          somehow free up the associated data.  */
128     }
129 }
130
131 /* This is the thread function which actually invokes the MessageBox
132    function.  This function runs in a separate thread.  */
133
134 static DWORD WINAPI
135 msgbox_thread (LPVOID arg)
136 {
137   struct msgbox_data *md = (struct msgbox_data *) arg;
138
139   md->result = MessageBox (md->hwnd, md->message, md->title,
140                            md->flags | MB_SETFOREGROUND);
141   PostMessage (md->hidden_hwnd, MSGBOX_MESSAGE, 0, (LPARAM) arg);
142   return 0;
143 }
144
145 /* This function handles Windows events for the hidden window.  When
146    the MessageBox function call completes in the thread, this function
147    will be called with MSGBOX_MESSAGE.  */
148
149 static LRESULT CALLBACK
150 msgbox_wndproc (HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam)
151 {
152   struct msgbox_event *me;
153
154   if (message != MSGBOX_MESSAGE)
155     return DefWindowProc (hwnd, message, wparam, lparam);
156
157   /* Queue up a Tcl event.  */
158   me = (struct msgbox_event *) ckalloc (sizeof *me);
159   me->header.proc = msgbox_eventproc;
160   me->md = (struct msgbox_data *) lparam;
161   Tcl_QueueEvent ((Tcl_Event *) me, TCL_QUEUE_TAIL);
162
163   return 0;
164 }
165
166 /* This function handles Tcl events.  It is invoked when a MessageBox
167    has completed.  */
168
169 static int
170 msgbox_eventproc (Tcl_Event *event, int flags)
171 {
172   struct msgbox_event *me = (struct msgbox_event *) event;
173   char *resstr;
174   Tcl_DString ds;
175   int ret;
176
177   /* Only execute the Tcl code if we are waiting for window events.  */
178   if ((flags & TCL_WINDOW_EVENTS) == 0)
179     return 0;
180
181   /* This switch is copied from Tk_MessageBoxCmd in Tk.  */
182   switch (me->md->result)
183     {
184       case IDABORT:     resstr = "abort";  break;
185       case IDCANCEL:    resstr = "cancel"; break;
186       case IDIGNORE:    resstr = "ignore"; break;
187       case IDNO:        resstr = "no";     break;
188       case IDOK:        resstr = "ok";     break;
189       case IDRETRY:     resstr = "retry";  break;
190       case IDYES:       resstr = "yes";    break;
191       default:          resstr = "";
192     }
193
194   Tcl_DStringInit (&ds);
195   Tcl_DStringAppend (&ds, me->md->code, -1);
196   Tcl_DStringAppendElement (&ds, resstr);
197
198   /* FIXME: What if the interpreter has been deleted?  */
199   ret = Tcl_GlobalEval (me->md->interp, Tcl_DStringValue (&ds));
200
201   Tcl_DStringFree (&ds);
202
203   /* We are now done with the msgbox_data structure, so we can free
204      the fields and the structure itself.  */
205   ckfree (me->md->code);
206   ckfree (me->md->message);
207   ckfree (me->md->title);
208   ckfree ((char *) me->md);
209
210   if (ret != TCL_OK)
211     Tcl_BackgroundError (me->md->interp);
212
213   return 1;
214 }
215
216 /* This is a direct steal from tkWinDialog.c, for the use of msgbox.
217    I kept the same formatting as well, to make it easier to merge
218    changes.  */
219
220 typedef struct MsgTypeInfo {
221     char * name;
222     int type;
223     int numButtons;
224     char * btnNames[3];
225 } MsgTypeInfo;
226
227 #define NUM_TYPES 6
228
229 static MsgTypeInfo 
230 msgTypeInfo[NUM_TYPES] = {
231     {"abortretryignore", MB_ABORTRETRYIGNORE, 3, {"abort", "retry", "ignore"}},
232     {"ok",               MB_OK,               1, {"ok"                      }},
233     {"okcancel",         MB_OKCANCEL,         2, {"ok",    "cancel"         }},
234     {"retrycancel",      MB_RETRYCANCEL,      2, {"retry", "cancel"         }},
235     {"yesno",            MB_YESNO,            2, {"yes",   "no"             }},
236     {"yesnocancel",      MB_YESNOCANCEL,      3, {"yes",   "no",    "cancel"}}
237 };
238
239 /* This is mostly a direct steal from Tk_MessageBoxCmd in Tk.  I kept
240    the same formatting as well, to make it easier to merge changes.  */
241
242 static int
243 msgbox_internal (ClientData clientData, Tcl_Interp *interp, int argc,
244                  char **argv, char *code)
245 {
246     int flags;
247     Tk_Window parent = NULL;
248     HWND hWnd;
249     char *message = "";
250     char *title = "";
251     int icon = MB_ICONINFORMATION;
252     int type = MB_OK;
253     int modal = MB_SYSTEMMODAL;
254     int i, j;
255     char *defaultBtn = NULL;
256     int defaultBtnIdx = -1;
257
258     for (i=1; i<argc; i+=2) {
259         int v = i+1;
260         int len = strlen(argv[i]);
261
262         if (strncmp(argv[i], "-default", len)==0) {
263             if (v==argc) {goto arg_missing;}
264
265             defaultBtn = argv[v];
266         }
267         else if (strncmp(argv[i], "-icon", len)==0) {
268             if (v==argc) {goto arg_missing;}
269
270             if (strcmp(argv[v], "error") == 0) {
271                 icon = MB_ICONERROR;
272             }
273             else if (strcmp(argv[v], "info") == 0) {
274                 icon = MB_ICONINFORMATION;
275             }
276             else if (strcmp(argv[v], "question") == 0) {
277                 icon = MB_ICONQUESTION;
278             }
279             else if (strcmp(argv[v], "warning") == 0) {
280                 icon = MB_ICONWARNING;
281             }
282             else {
283                 Tcl_AppendResult(interp, "invalid icon \"", argv[v],
284                     "\", must be error, info, question or warning", NULL);
285                 return TCL_ERROR;
286             }
287         }
288         else if (strncmp(argv[i], "-message", len)==0) {
289             if (v==argc) {goto arg_missing;}
290
291             message = argv[v];
292         }
293         else if (strncmp(argv[i], "-parent", len)==0) {
294             if (v==argc) {goto arg_missing;}
295
296             parent=Tk_NameToWindow(interp, argv[v], Tk_MainWindow(interp));
297             if (parent == NULL) {
298                 return TCL_ERROR;
299             }
300         }
301         else if (strncmp(argv[i], "-title", len)==0) {
302             if (v==argc) {goto arg_missing;}
303
304             title = argv[v];
305         }
306         else if (strncmp(argv[i], "-type", len)==0) {
307             int found = 0;
308
309             if (v==argc) {goto arg_missing;}
310
311             for (j=0; j<NUM_TYPES; j++) {
312                 if (strcmp(argv[v], msgTypeInfo[j].name) == 0) {
313                     type = msgTypeInfo[j].type;
314                     found = 1;
315                     break;
316                 }
317             }
318             if (!found) {
319                 Tcl_AppendResult(interp, "invalid message box type \"", 
320                     argv[v], "\", must be abortretryignore, ok, ",
321                     "okcancel, retrycancel, yesno or yesnocancel", NULL);
322                 return TCL_ERROR;
323             }
324         }
325         else if (strncmp (argv[i], "-modal", len) == 0) {
326             if (v==argc) {goto arg_missing;}
327
328             if (strcmp(argv[v], "system") == 0) {
329                 modal = MB_SYSTEMMODAL;
330             }
331             else if (strcmp(argv[v], "task") == 0) {
332                 modal = MB_TASKMODAL;
333             }
334             else if (strcmp(argv[v], "owner") == 0) {
335                 modal = MB_APPLMODAL;
336             }
337             else {
338                 Tcl_AppendResult(interp, "invalid modality \"", argv[v],
339                     "\", must be system, task or owner", NULL);
340                 return TCL_ERROR;
341             }
342         }
343         else {
344             Tcl_AppendResult(interp, "unknown option \"", 
345                 argv[i], "\", must be -default, -icon, ",
346                 "-message, -parent, -title or -type", NULL);
347                 return TCL_ERROR;
348         }
349     }
350
351     /* Make sure we have a valid hWnd to act as the parent of this message box
352      */
353     if (parent == NULL && modal == MB_TASKMODAL) {
354         hWnd = NULL;
355     }
356     else {
357         if (parent == NULL) {
358             parent = Tk_MainWindow(interp);
359         }
360         if (Tk_WindowId(parent) == None) {
361             Tk_MakeWindowExist(parent);
362         }
363         hWnd = Tk_GetHWND(Tk_WindowId(parent));
364     }
365
366     if (defaultBtn != NULL) {
367         for (i=0; i<NUM_TYPES; i++) {
368             if (type == msgTypeInfo[i].type) {
369                 for (j=0; j<msgTypeInfo[i].numButtons; j++) {
370                     if (strcmp(defaultBtn, msgTypeInfo[i].btnNames[j])==0) {
371                         defaultBtnIdx = j;
372                         break;
373                     }
374                 }
375                 if (defaultBtnIdx < 0) {
376                     Tcl_AppendResult(interp, "invalid default button \"",
377                         defaultBtn, "\"", NULL);
378                     return TCL_ERROR;
379                 }
380                 break;
381             }
382         }
383
384         switch (defaultBtnIdx) {
385           case 0: flags = MB_DEFBUTTON1; break;
386           case 1: flags = MB_DEFBUTTON2; break;
387           case 2: flags = MB_DEFBUTTON3; break;
388           case 3: flags = MB_DEFBUTTON4; break;
389         }
390     } else {
391         flags = 0;
392     }
393     
394     flags |= icon | type;
395
396     /* At this point we diverge from Tk_MessageBoxCmd.  */
397     {
398       struct msgbox_data *md;
399       HANDLE thread;
400       DWORD tid;
401
402       msgbox_init ();
403
404       md = (struct msgbox_data *) ckalloc (sizeof *md);
405       md->interp = interp;
406       md->code = ckalloc (strlen (code) + 1);
407       strcpy (md->code, code);
408       md->hidden_hwnd = hidden_hwnd;
409       md->hwnd = hWnd;
410       md->message = ckalloc (strlen (message) + 1);
411       strcpy (md->message, message);
412       md->title = ckalloc (strlen (title) + 1);
413       strcpy (md->title, title);
414       md->flags = flags | modal;
415
416       /* Start the thread.  This will call MessageBox, and then start
417          the ball rolling to execute the specified code.  */
418       thread = CreateThread (NULL, 0, msgbox_thread, (LPVOID) md, 0, &tid);
419       CloseHandle (thread);
420     }
421
422     return TCL_OK;
423
424   arg_missing:
425     Tcl_AppendResult(interp, "value for \"", argv[argc-1], "\" missing",
426         NULL);
427     return TCL_ERROR;
428 }
429
430 /* This is the ide_messageBox function.  */
431
432 static int
433 msgbox (ClientData cd, Tcl_Interp *interp, int argc, char **argv)
434 {
435   if (argc < 2)
436     {
437       char buf[10];
438
439       sprintf (buf, "%d", argc);
440       Tcl_AppendResult (interp, "wrong # args: got ", buf,
441                         " but expected at least 2", (char *) NULL);
442       return TCL_ERROR;
443     }
444
445   /* Note that we don't bother to pass the correct value for argv[0]
446      to msgbox_internal, since it doesn't look at it anyhow.  Note
447      that we will pass a NULL clientdata argument.  */
448   return msgbox_internal (cd, interp, argc - 1, argv + 1, argv[1]);
449 }
450
451 /* Create the Tcl command.  */
452
453 int
454 ide_create_messagebox_command (Tcl_Interp *interp)
455 {
456   Tcl_CreateExitHandler (msgbox_exit, NULL);
457   if (Tcl_CreateCommand (interp, "ide_messageBox", msgbox, NULL, NULL) == NULL)
458     return TCL_ERROR;
459   return TCL_OK;
460 }
461
462 #endif  /* _WIN32 */