OpenCores
URL https://opencores.org/ocsvn/or1k/or1k/trunk

Subversion Repositories or1k

[/] [or1k/] [tags/] [start/] [insight/] [libgui/] [src/] [tclmsgbox.c] - Blame information for rev 1780

Go to most recent revision | Details | Compare with Previous | View Log

Line No. Rev Author Line
1 578 markom
/* 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 *) Tcl_Alloc (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
  Tcl_Free (me->md->code);
206
  Tcl_Free (me->md->message);
207
  Tcl_Free (me->md->title);
208
  Tcl_Free ((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 *) Tcl_Alloc (sizeof *md);
405
      md->interp = interp;
406
      md->code = Tcl_Alloc (strlen (code) + 1);
407
      strcpy (md->code, code);
408
      md->hidden_hwnd = hidden_hwnd;
409
      md->hwnd = hWnd;
410
      md->message = Tcl_Alloc (strlen (message) + 1);
411
      strcpy (md->message, message);
412
      md->title = Tcl_Alloc (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 */

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.