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

Subversion Repositories or1k_old

[/] [or1k_old/] [trunk/] [insight/] [tk/] [win/] [tkWinMenu.c] - Blame information for rev 1782

Details | Compare with Previous | View Log

Line No. Rev Author Line
1 578 markom
/*
2
 * tkWinMenu.c --
3
 *
4
 *      This module implements the Windows platform-specific features of menus.
5
 *
6
 * Copyright (c) 1996-1997 by Sun Microsystems, Inc.
7
 *
8
 * See the file "license.terms" for information on usage and redistribution
9
 * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
10
 *
11
 * RCS: @(#) $Id: tkWinMenu.c,v 1.1.1.1 2002-01-16 10:26:03 markom Exp $
12
 */
13
 
14
#define OEMRESOURCE
15
#include <string.h>
16
#include "tkMenu.h"
17
#include "tkWinInt.h"
18
 
19
/*
20
 * The class of the window for popup menus.
21
 */
22
 
23
#define MENU_CLASS_NAME "MenuWindowClass"
24
 
25
/*
26
 * Used to align a windows bitmap inside a rectangle
27
 */
28
 
29
#define ALIGN_BITMAP_LEFT   0x00000001
30
#define ALIGN_BITMAP_RIGHT  0x00000002
31
#define ALIGN_BITMAP_TOP    0x00000004
32
#define ALIGN_BITMAP_BOTTOM 0x00000008
33
 
34
/*
35
 * Platform-specific menu flags:
36
 *
37
 * MENU_SYSTEM_MENU     Non-zero means that the Windows menu handle
38
 *                      was retrieved with GetSystemMenu and needs
39
 *                      to be disposed of specially.
40
 * MENU_RECONFIGURE_PENDING
41
 *                      Non-zero means that an idle handler has
42
 *                      been set up to reconfigure the Windows menu
43
 *                      handle for this menu.
44
 */
45
 
46
#define MENU_SYSTEM_MENU            MENU_PLATFORM_FLAG1
47
#define MENU_RECONFIGURE_PENDING    MENU_PLATFORM_FLAG2
48
 
49
static int indicatorDimensions[2];
50
                                /* The dimensions of the indicator space
51
                                 * in a menu entry. Calculated at init
52
                                 * time to save time. */
53
static Tcl_HashTable commandTable;
54
                                /* A map of command ids to menu entries */
55
static int inPostMenu;          /* We cannot be re-entrant like X Windows. */
56
static WORD lastCommandID;      /* The last command ID we allocated. */
57
static HWND menuHWND;           /* A window to service popup-menu messages
58
                                 * in. */
59
static int oldServiceMode;      /* Used while processing a menu; we need
60
                                 * to set the event mode specially when we
61
                                 * enter the menu processing modal loop
62
                                 * and reset it when menus go away. */
63
static TkMenu *modalMenuPtr;    /* The menu we are processing inside the modal
64
                                 * loop. We need this to reset all of the
65
                                 * active items when menus go away since
66
                                 * Windows does not see fit to give this
67
                                 * to us when it sends its WM_MENUSELECT. */
68
static OSVERSIONINFO versionInfo;
69
                                /* So we don't have to keep doing this */
70
static Tcl_HashTable winMenuTable;
71
                                /* Need this to map HMENUs back to menuPtrs */
72
 
73
/*
74
 * The following are default menu value strings.
75
 */
76
 
77
static char borderString[5];    /* The string indicating how big the border is */
78
static Tcl_DString menuFontDString;
79
                                /* A buffer to store the default menu font
80
                                 * string. */
81
 
82
/*
83
 * Forward declarations for procedures defined later in this file:
84
 */
85
 
86
static void             DrawMenuEntryAccelerator _ANSI_ARGS_((
87
                            TkMenu *menuPtr, TkMenuEntry *mePtr,
88
                            Drawable d, GC gc, Tk_Font tkfont,
89
                            CONST Tk_FontMetrics *fmPtr,
90
                            Tk_3DBorder activeBorder, int x, int y,
91
                            int width, int height, int drawArrow));
92
static void             DrawMenuEntryBackground _ANSI_ARGS_((
93
                            TkMenu *menuPtr, TkMenuEntry *mePtr,
94
                            Drawable d, Tk_3DBorder activeBorder,
95
                            Tk_3DBorder bgBorder, int x, int y,
96
                            int width, int heigth));
97
static void             DrawMenuEntryIndicator _ANSI_ARGS_((
98
                            TkMenu *menuPtr, TkMenuEntry *mePtr,
99
                            Drawable d, GC gc, GC indicatorGC,
100
                            Tk_Font tkfont,
101
                            CONST Tk_FontMetrics *fmPtr, int x, int y,
102
                            int width, int height));
103
static void             DrawMenuEntryLabel _ANSI_ARGS_((
104
                            TkMenu * menuPtr, TkMenuEntry *mePtr, Drawable d,
105
                            GC gc, Tk_Font tkfont,
106
                            CONST Tk_FontMetrics *fmPtr, int x, int y,
107
                            int width, int height));
108
static void             DrawMenuSeparator _ANSI_ARGS_((TkMenu *menuPtr,
109
                            TkMenuEntry *mePtr, Drawable d, GC gc,
110
                            Tk_Font tkfont, CONST Tk_FontMetrics *fmPtr,
111
                            int x, int y, int width, int height));
112
static void             DrawTearoffEntry _ANSI_ARGS_((TkMenu *menuPtr,
113
                            TkMenuEntry *mePtr, Drawable d, GC gc,
114
                            Tk_Font tkfont, CONST Tk_FontMetrics *fmPtr,
115
                            int x, int y, int width, int height));
116
static void             DrawMenuUnderline _ANSI_ARGS_((TkMenu *menuPtr,
117
                            TkMenuEntry *mePtr, Drawable d, GC gc,
118
                            Tk_Font tkfont, CONST Tk_FontMetrics *fmPtr, int x,
119
                            int y, int width, int height));
120
static void             DrawWindowsSystemBitmap _ANSI_ARGS_((
121
                            Display *display, Drawable drawable,
122
                            GC gc, CONST RECT *rectPtr, int bitmapID,
123
                            int alignFlags));
124
static void             FreeID _ANSI_ARGS_((int commandID));
125
static char *           GetEntryText _ANSI_ARGS_((TkMenuEntry *mePtr));
126
static void             GetMenuAccelGeometry _ANSI_ARGS_((TkMenu *menuPtr,
127
                            TkMenuEntry *mePtr, Tk_Font tkfont,
128
                            CONST Tk_FontMetrics *fmPtr, int *widthPtr,
129
                            int *heightPtr));
130
static void             GetMenuLabelGeometry _ANSI_ARGS_((TkMenuEntry *mePtr,
131
                            Tk_Font tkfont, CONST Tk_FontMetrics *fmPtr,
132
                            int *widthPtr, int *heightPtr));
133
static void             GetMenuIndicatorGeometry _ANSI_ARGS_((
134
                            TkMenu *menuPtr, TkMenuEntry *mePtr,
135
                            Tk_Font tkfont, CONST Tk_FontMetrics *fmPtr,
136
                            int *widthPtr, int *heightPtr));
137
static void             GetMenuSeparatorGeometry _ANSI_ARGS_((
138
                            TkMenu *menuPtr, TkMenuEntry *mePtr,
139
                            Tk_Font tkfont, CONST Tk_FontMetrics *fmPtr,
140
                            int *widthPtr, int *heightPtr));
141
static void             GetTearoffEntryGeometry _ANSI_ARGS_((TkMenu *menuPtr,
142
                            TkMenuEntry *mePtr, Tk_Font tkfont,
143
                            CONST Tk_FontMetrics *fmPtr, int *widthPtr,
144
                            int *heightPtr));
145
static int              GetNewID _ANSI_ARGS_((TkMenuEntry *mePtr,
146
                            int *menuIDPtr));
147
static void             MenuExitProc _ANSI_ARGS_((ClientData clientData));
148
static int              MenuKeyBindProc _ANSI_ARGS_((
149
                            ClientData clientData,
150
                            Tcl_Interp *interp, XEvent *eventPtr,
151
                            Tk_Window tkwin, KeySym keySym));
152
static void             MenuSelectEvent _ANSI_ARGS_((TkMenu *menuPtr));
153
static void             ReconfigureWindowsMenu _ANSI_ARGS_((
154
                            ClientData clientData));
155
static void             RecursivelyClearActiveMenu _ANSI_ARGS_((
156
                            TkMenu *menuPtr));
157
static LRESULT CALLBACK TkWinMenuProc _ANSI_ARGS_((HWND hwnd,
158
                            UINT message, WPARAM wParam,
159
                            LPARAM lParam));
160
 
161
 
162
 
163
/*
164
 *----------------------------------------------------------------------
165
 *
166
 * GetNewID --
167
 *
168
 *      Allocates a new menu id and marks it in use.
169
 *
170
 * Results:
171
 *      Returns TCL_OK if succesful; TCL_ERROR if there are no more
172
 *      ids of the appropriate type to allocate. menuIDPtr contains
173
 *      the new id if succesful.
174
 *
175
 * Side effects:
176
 *      An entry is created for the menu in the command hash table,
177
 *      and the hash entry is stored in the appropriate field in the
178
 *      menu data structure.
179
 *
180
 *----------------------------------------------------------------------
181
 */
182
 
183
static int
184
GetNewID(mePtr, menuIDPtr)
185
    TkMenuEntry *mePtr;         /* The menu we are working with */
186
    int *menuIDPtr;             /* The resulting id */
187
{
188
    int found = 0;
189
    int newEntry;
190
    Tcl_HashEntry *commandEntryPtr;
191
    WORD returnID;
192
 
193
    WORD curID = lastCommandID + 1;
194
 
195
    /*
196
     * The following code relies on WORD wrapping when the highest value is
197
     * incremented.
198
     */
199
 
200
    while (curID != lastCommandID) {
201
        commandEntryPtr = Tcl_CreateHashEntry(&commandTable,
202
                (char *) curID, &newEntry);
203
        if (newEntry == 1) {
204
            found = 1;
205
            returnID = curID;
206
            break;
207
        }
208
        curID++;
209
    }
210
 
211
    if (found) {
212
        Tcl_SetHashValue(commandEntryPtr, (char *) mePtr);
213
        *menuIDPtr = (int) returnID;
214
        lastCommandID = returnID;
215
        return TCL_OK;
216
    } else {
217
        return TCL_ERROR;
218
    }
219
}
220
 
221
/*
222
 *----------------------------------------------------------------------
223
 *
224
 * FreeID --
225
 *
226
 *      Marks the itemID as free.
227
 *
228
 * Results:
229
 *      None.
230
 *
231
 * Side effects:
232
 *      The hash table entry for the ID is cleared.
233
 *
234
 *----------------------------------------------------------------------
235
 */
236
 
237
static void
238
FreeID(commandID)
239
    int commandID;
240
{
241
    Tcl_HashEntry *entryPtr = Tcl_FindHashEntry(&commandTable,
242
            (char *) commandID);
243
 
244
    if (entryPtr != NULL) {
245
         Tcl_DeleteHashEntry(entryPtr);
246
    }
247
}
248
 
249
/*
250
 *----------------------------------------------------------------------
251
 *
252
 * TkpNewMenu --
253
 *
254
 *      Gets a new blank menu. Only the platform specific options are filled
255
 *      in.
256
 *
257
 * Results:
258
 *      Standard TCL error.
259
 *
260
 * Side effects:
261
 *      Allocates a Windows menu handle and places it in the platformData
262
 *      field of the menuPtr.
263
 *
264
 *----------------------------------------------------------------------
265
 */
266
 
267
int
268
TkpNewMenu(menuPtr)
269
    TkMenu *menuPtr;    /* The common structure we are making the
270
                         * platform structure for. */
271
{
272
    HMENU winMenuHdl;
273
    Tcl_HashEntry *hashEntryPtr;
274
    int newEntry;
275
 
276
    winMenuHdl = CreatePopupMenu();
277
 
278
    if (winMenuHdl == NULL) {
279
        Tcl_AppendResult(menuPtr->interp, "No more menus can be allocated.",
280
                (char *) NULL);
281
        return TCL_ERROR;
282
    }
283
 
284
    /*
285
     * We hash all of the HMENU's so that we can get their menu ptrs
286
     * back when dispatch messages.
287
     */
288
 
289
    hashEntryPtr = Tcl_CreateHashEntry(&winMenuTable, (char *) winMenuHdl,
290
            &newEntry);
291
    Tcl_SetHashValue(hashEntryPtr, (char *) menuPtr);
292
 
293
    menuPtr->platformData = (TkMenuPlatformData) winMenuHdl;
294
    return TCL_OK;
295
}
296
 
297
/*
298
 *----------------------------------------------------------------------
299
 *
300
 * TkpDestroyMenu --
301
 *
302
 *      Destroys platform-specific menu structures.
303
 *
304
 * Results:
305
 *      None.
306
 *
307
 * Side effects:
308
 *      All platform-specific allocations are freed up.
309
 *
310
 *----------------------------------------------------------------------
311
 */
312
 
313
void
314
TkpDestroyMenu(menuPtr)
315
    TkMenu *menuPtr;        /* The common menu structure */
316
{
317
    HMENU winMenuHdl = (HMENU) menuPtr->platformData;
318
 
319
    if (menuPtr->menuFlags & MENU_RECONFIGURE_PENDING) {
320
        Tcl_CancelIdleCall(ReconfigureWindowsMenu, (ClientData) menuPtr);
321
    }
322
 
323
    if (winMenuHdl == NULL) {
324
        return;
325
    }
326
 
327
    if (menuPtr->menuFlags & MENU_SYSTEM_MENU) {
328
        TkMenuEntry *searchEntryPtr;
329
        Tcl_HashTable *tablePtr = TkGetMenuHashTable(menuPtr->interp);
330
        char *menuName = Tcl_GetHashKey(tablePtr,
331
                menuPtr->menuRefPtr->hashEntryPtr);
332
 
333
        /*
334
         * Search for the menu in the menubar, if it is present, get the
335
         * wrapper window associated with the toplevel and reset its
336
         * system menu to the default menu.
337
         */
338
 
339
        for (searchEntryPtr = menuPtr->menuRefPtr->parentEntryPtr;
340
             searchEntryPtr != NULL;
341
             searchEntryPtr = searchEntryPtr->nextCascadePtr) {
342
            if (strcmp(searchEntryPtr->name, menuName) == 0) {
343
                Tk_Window parentTopLevelPtr = searchEntryPtr
344
                    ->menuPtr->parentTopLevelPtr;
345
 
346
                if (parentTopLevelPtr != NULL) {
347
                    GetSystemMenu(TkWinGetWrapperWindow(parentTopLevelPtr),
348
                            TRUE);
349
                }
350
                break;
351
            }
352
        }
353
    } else {
354
        Tcl_HashEntry *hashEntryPtr;
355
 
356
        /*
357
         * Remove the menu from the menu hash table, then destroy the handle.
358
         */
359
 
360
        hashEntryPtr = Tcl_FindHashEntry(&winMenuTable, (char *) winMenuHdl);
361
        if (hashEntryPtr != NULL) {
362
            Tcl_DeleteHashEntry(hashEntryPtr);
363
        }
364
        DestroyMenu(winMenuHdl);
365
    }
366
    menuPtr->platformData = NULL;
367
 
368
    if (menuPtr == modalMenuPtr) {
369
        modalMenuPtr = NULL;
370
    }
371
}
372
 
373
/*
374
 *----------------------------------------------------------------------
375
 *
376
 * TkpDestroyMenuEntry --
377
 *
378
 *      Cleans up platform-specific menu entry items.
379
 *
380
 * Results:
381
 *      None
382
 *
383
 * Side effects:
384
 *      All platform-specific allocations are freed up.
385
 *
386
 *----------------------------------------------------------------------
387
 */
388
 
389
void
390
TkpDestroyMenuEntry(mePtr)
391
    TkMenuEntry *mePtr;             /* The entry to destroy */
392
{
393
    TkMenu *menuPtr = mePtr->menuPtr;
394
    HMENU winMenuHdl = (HMENU) menuPtr->platformData;
395
 
396
    if (NULL != winMenuHdl) {
397
        if (!(menuPtr->menuFlags & MENU_RECONFIGURE_PENDING)) {
398
            menuPtr->menuFlags |= MENU_RECONFIGURE_PENDING;
399
            Tcl_DoWhenIdle(ReconfigureWindowsMenu, (ClientData) menuPtr);
400
        }
401
    }
402
    FreeID((int) mePtr->platformEntryData);
403
    mePtr->platformEntryData = NULL;
404
}
405
 
406
/*
407
 *----------------------------------------------------------------------
408
 *
409
 * GetEntryText --
410
 *
411
 *      Given a menu entry, gives back the text that should go in it.
412
 *      Separators should be done by the caller, as they have to be
413
 *      handled specially. Allocates the memory with alloc. The caller
414
 *      should free the memory.
415
 *
416
 * Results:
417
 *      itemText points to the new text for the item.
418
 *
419
 * Side effects:
420
 *      None.
421
 *
422
 *----------------------------------------------------------------------
423
 */
424
 
425
static char *
426
GetEntryText(mePtr)
427
    TkMenuEntry *mePtr;         /* A pointer to the menu entry. */
428
{
429
    char *itemText;
430
 
431
    if (mePtr->type == TEAROFF_ENTRY) {
432
        itemText = ckalloc(sizeof("(Tear-off)"));
433
        strcpy(itemText, "(Tear-off)");
434
    } else if (mePtr->imageString != NULL) {
435
        itemText = ckalloc(sizeof("(Image)"));
436
        strcpy(itemText, "(Image)");
437
    } else if (mePtr->bitmap != None) {
438
        itemText = ckalloc(sizeof("(Pixmap)"));
439
        strcpy(itemText, "(Pixmap)");
440
    } else if (mePtr->label == NULL || mePtr->labelLength == 0) {
441
        itemText = ckalloc(sizeof("( )"));
442
        strcpy(itemText, "( )");
443
    } else {
444
        int size = mePtr->labelLength + 1;
445
        int i, j;
446
 
447
        /*
448
         * We have to construct the string with an ampersand
449
         * preceeding the underline character, and a tab seperating
450
         * the text and the accel text. We have to be careful with
451
         * ampersands in the string.
452
         */
453
 
454
        for (i = 0; i < mePtr->labelLength; i++) {
455
            if (mePtr->label[i] == '&') {
456
                size++;
457
            }
458
        }
459
 
460
        if (mePtr->underline >= 0) {
461
            size++;
462
            if (mePtr->label[mePtr->underline] == '&') {
463
                size++;
464
            }
465
        }
466
 
467
        if (mePtr->accelLength > 0) {
468
            size += mePtr->accelLength + 1;
469
        }
470
 
471
        for (i = 0; i < mePtr->accelLength; i++) {
472
            if (mePtr->accel[i] == '&') {
473
                size++;
474
            }
475
        }
476
 
477
        itemText = ckalloc(size);
478
 
479
        if (mePtr->labelLength == 0) {
480
            itemText[0] = 0;
481
        } else {
482
            for (i = 0, j = 0; i < mePtr->labelLength; i++, j++) {
483
                if (mePtr->label[i] == '&') {
484
                    itemText[j++] = '&';
485
                }
486
                if (i == mePtr->underline) {
487
                    itemText[j++] = '&';
488
                }
489
                itemText[j] = mePtr->label[i];
490
            }
491
            itemText[j] = '\0';
492
        }
493
 
494
        if (mePtr->accelLength > 0) {
495
            strcat(itemText, "\t");
496
            for (i = 0, j = strlen(itemText); i < mePtr->accelLength;
497
                    i++, j++) {
498
                if (mePtr->accel[i] == '&') {
499
                    itemText[j++] = '&';
500
                }
501
                itemText[j] = mePtr->accel[i];
502
            }
503
            itemText[j] = '\0';
504
        }
505
    }
506
    return itemText;
507
}
508
 
509
/*
510
 *----------------------------------------------------------------------
511
 *
512
 * ReconfigureWindowsMenu --
513
 *
514
 *      Tears down and rebuilds the platform-specific part of this menu.
515
 *
516
 * Results:
517
 *      None.
518
 *
519
 * Side effects:
520
 *      Configuration information get set for mePtr; old resources
521
 *      get freed, if any need it.
522
 *
523
 *----------------------------------------------------------------------
524
 */
525
 
526
static void
527
ReconfigureWindowsMenu(
528
    ClientData clientData)          /* The menu we are rebuilding */
529
{
530
    TkMenu *menuPtr = (TkMenu *) clientData;
531
    TkMenuEntry *mePtr;
532
    HMENU winMenuHdl = (HMENU) menuPtr->platformData;
533
    char *itemText = NULL;
534
    LPCTSTR lpNewItem;
535
    UINT flags;
536
    UINT itemID;
537
    int i, count, systemMenu = 0, base;
538
    int width, height;
539
 
540
    if (NULL == winMenuHdl) {
541
        return;
542
    }
543
 
544
    /*
545
     * Reconstruct the entire menu. Takes care of nasty system menu and index
546
     * problem.
547
     *
548
     */
549
 
550
    if ((menuPtr->menuType == MENUBAR)
551
            && (menuPtr->parentTopLevelPtr != NULL)) {
552
        width = Tk_Width(menuPtr->parentTopLevelPtr);
553
        height = Tk_Height(menuPtr->parentTopLevelPtr);
554
    }
555
 
556
    base = (menuPtr->menuFlags & MENU_SYSTEM_MENU) ? 7 : 0;
557
    count = GetMenuItemCount(winMenuHdl);
558
    for (i = base; i < count; i++) {
559
        RemoveMenu(winMenuHdl, base, MF_BYPOSITION);
560
    }
561
 
562
    count = menuPtr->numEntries;
563
    for (i = 0; i < count; i++) {
564
        mePtr = menuPtr->entries[i];
565
        lpNewItem = NULL;
566
        flags = MF_BYPOSITION;
567
        itemID = 0;
568
 
569
        if ((menuPtr->menuType == MENUBAR) && (mePtr->type == TEAROFF_ENTRY)) {
570
            continue;
571
        }
572
 
573
        if (mePtr->type == SEPARATOR_ENTRY) {
574
            flags |= MF_SEPARATOR;
575
        } else {
576
            itemText = GetEntryText(mePtr);
577
            if ((menuPtr->menuType == MENUBAR)
578
                    || (menuPtr->menuFlags & MENU_SYSTEM_MENU)) {
579
                lpNewItem = itemText;
580
            } else {
581
                lpNewItem = (LPCTSTR) mePtr;
582
                flags |= MF_OWNERDRAW;
583
            }
584
 
585
            /*
586
             * Set enabling and disabling correctly.
587
             */
588
 
589
            if (mePtr->state == tkDisabledUid) {
590
                flags |= MF_DISABLED;
591
            }
592
 
593
            /*
594
             * Set the check mark for check entries and radio entries.
595
             */
596
 
597
            if (((mePtr->type == CHECK_BUTTON_ENTRY)
598
                    || (mePtr->type == RADIO_BUTTON_ENTRY))
599
                    && (mePtr->entryFlags & ENTRY_SELECTED)) {
600
                flags |= MF_CHECKED;
601
            }
602
 
603
            if (mePtr->columnBreak) {
604
                flags |= MF_MENUBREAK;
605
            }
606
 
607
            itemID = (int) mePtr->platformEntryData;
608
            if ((mePtr->type == CASCADE_ENTRY)
609
                    && (mePtr->childMenuRefPtr != NULL)
610
                    && (mePtr->childMenuRefPtr->menuPtr != NULL)) {
611
                HMENU childMenuHdl = (HMENU) mePtr->childMenuRefPtr->menuPtr
612
                    ->platformData;
613
                if (childMenuHdl != NULL) {
614
                    itemID = (UINT) childMenuHdl;
615
                    flags |= MF_POPUP;
616
                }
617
                if ((menuPtr->menuType == MENUBAR)
618
                        && !(mePtr->childMenuRefPtr->menuPtr->menuFlags
619
                                & MENU_SYSTEM_MENU)) {
620
                    TkMenuReferences *menuRefPtr;
621
                    TkMenu *systemMenuPtr = mePtr->childMenuRefPtr
622
                        ->menuPtr;
623
                    char *systemMenuName = ckalloc(strlen(
624
                        Tk_PathName(menuPtr->masterMenuPtr->tkwin))
625
                            + strlen(".system") + 1);
626
 
627
                    strcpy(systemMenuName,
628
                            Tk_PathName(menuPtr->masterMenuPtr->tkwin));
629
                    strcat(systemMenuName, ".system");
630
                    menuRefPtr = TkFindMenuReferences(menuPtr->interp,
631
                            systemMenuName);
632
                    if ((menuRefPtr != NULL)
633
                            && (menuRefPtr->menuPtr != NULL)
634
                            && (menuPtr->parentTopLevelPtr != NULL)
635
                            && (systemMenuPtr->masterMenuPtr
636
                                    == menuRefPtr->menuPtr)) {
637
                        HMENU systemMenuHdl =
638
                            (HMENU) systemMenuPtr->platformData;
639
                        HWND wrapper = TkWinGetWrapperWindow(menuPtr
640
                                ->parentTopLevelPtr);
641
                        if (wrapper != NULL) {
642
                            DestroyMenu(systemMenuHdl);
643
                            systemMenuHdl = GetSystemMenu(wrapper, FALSE);
644
                            systemMenuPtr->menuFlags |= MENU_SYSTEM_MENU;
645
                            systemMenuPtr->platformData =
646
                                (TkMenuPlatformData) systemMenuHdl;
647
                            if (!(systemMenuPtr->menuFlags
648
                                    & MENU_RECONFIGURE_PENDING)) {
649
                                systemMenuPtr->menuFlags
650
                                    |= MENU_RECONFIGURE_PENDING;
651
                                Tcl_DoWhenIdle(ReconfigureWindowsMenu,
652
                                        (ClientData) systemMenuPtr);
653
                            }
654
                        }
655
                    }
656
                    ckfree(systemMenuName);
657
                }
658
                if (mePtr->childMenuRefPtr->menuPtr->menuFlags
659
                        & MENU_SYSTEM_MENU) {
660
                    systemMenu++;
661
                }
662
            }
663
        }
664
        if (!systemMenu) {
665
            InsertMenu(winMenuHdl, 0xFFFFFFFF, flags, itemID, lpNewItem);
666
        }
667
        if (itemText != NULL) {
668
            ckfree(itemText);
669
            itemText = NULL;
670
        }
671
    }
672
 
673
 
674
    if ((menuPtr->menuType == MENUBAR)
675
            && (menuPtr->parentTopLevelPtr != NULL)) {
676
        DrawMenuBar(TkWinGetWrapperWindow(menuPtr->parentTopLevelPtr));
677
        Tk_GeometryRequest(menuPtr->parentTopLevelPtr, width, height);
678
    }
679
 
680
    menuPtr->menuFlags &= ~(MENU_RECONFIGURE_PENDING);
681
}
682
 
683
/*
684
 *----------------------------------------------------------------------
685
 *
686
 * TkpPostMenu --
687
 *
688
 *      Posts a menu on the screen
689
 *
690
 * Results:
691
 *      None.
692
 *
693
 * Side effects:
694
 *      The menu is posted and handled.
695
 *
696
 *----------------------------------------------------------------------
697
 */
698
 
699
int
700
TkpPostMenu(interp, menuPtr, x, y)
701
    Tcl_Interp *interp;
702
    TkMenu *menuPtr;
703
    int x;
704
    int y;
705
{
706
    HMENU winMenuHdl = (HMENU) menuPtr->platformData;
707
    int result, flags;
708
    RECT noGoawayRect;
709
    POINT point;
710
    Tk_Window parentWindow = Tk_Parent(menuPtr->tkwin);
711
    int oldServiceMode = Tcl_GetServiceMode();
712
 
713
    inPostMenu++;
714
 
715
    if (menuPtr->menuFlags & MENU_RECONFIGURE_PENDING) {
716
        Tcl_CancelIdleCall(ReconfigureWindowsMenu, (ClientData) menuPtr);
717
        ReconfigureWindowsMenu((ClientData) menuPtr);
718
    }
719
 
720
    result = TkPreprocessMenu(menuPtr);
721
    if (result != TCL_OK) {
722
        inPostMenu--;
723
        return result;
724
    }
725
 
726
    /*
727
     * The post commands could have deleted the menu, which means
728
     * we are dead and should go away.
729
     */
730
 
731
    if (menuPtr->tkwin == NULL) {
732
        inPostMenu--;
733
        return TCL_OK;
734
    }
735
 
736
    if (NULL == parentWindow) {
737
        noGoawayRect.top = y - 50;
738
        noGoawayRect.bottom = y + 50;
739
        noGoawayRect.left = x - 50;
740
        noGoawayRect.right = x + 50;
741
    } else {
742
        int left, top;
743
        Tk_GetRootCoords(parentWindow, &left, &top);
744
        noGoawayRect.left = left;
745
        noGoawayRect.top = top;
746
        noGoawayRect.bottom = noGoawayRect.top + Tk_Height(parentWindow);
747
        noGoawayRect.right = noGoawayRect.left + Tk_Width(parentWindow);
748
    }
749
 
750
    Tcl_SetServiceMode(TCL_SERVICE_NONE);
751
 
752
    /*
753
     * Make an assumption here. If the right button is down,
754
     * then we want to track it. Otherwise, track the left mouse button.
755
     */
756
 
757
    flags = TPM_LEFTALIGN;
758
    if (GetSystemMetrics(SM_SWAPBUTTON)) {
759
        if (GetAsyncKeyState(VK_LBUTTON) < 0) {
760
            flags |= TPM_RIGHTBUTTON;
761
        } else {
762
            flags |= TPM_LEFTBUTTON;
763
        }
764
    } else {
765
        if (GetAsyncKeyState(VK_RBUTTON) < 0) {
766
            flags |= TPM_RIGHTBUTTON;
767
        } else {
768
            flags |= TPM_LEFTBUTTON;
769
        }
770
    }
771
 
772
    TrackPopupMenu(winMenuHdl, flags, x, y, 0,
773
            menuHWND, &noGoawayRect);
774
    Tcl_SetServiceMode(oldServiceMode);
775
 
776
    GetCursorPos(&point);
777
    Tk_PointerEvent(NULL, point.x, point.y);
778
 
779
    if (inPostMenu) {
780
        inPostMenu = 0;
781
    }
782
    return TCL_OK;
783
}
784
 
785
/*
786
 *----------------------------------------------------------------------
787
 *
788
 * TkpMenuNewEntry --
789
 *
790
 *      Adds a pointer to a new menu entry structure with the platform-
791
 *      specific fields filled in.
792
 *
793
 * Results:
794
 *      Standard TCL error.
795
 *
796
 * Side effects:
797
 *      A new command ID is allocated and stored in the platformEntryData
798
 *      field of mePtr.
799
 *
800
 *----------------------------------------------------------------------
801
 */
802
 
803
int
804
TkpMenuNewEntry(mePtr)
805
    TkMenuEntry *mePtr;
806
{
807
    int commandID;
808
    TkMenu *menuPtr = mePtr->menuPtr;
809
 
810
    if (GetNewID(mePtr, &commandID) != TCL_OK) {
811
        return TCL_ERROR;
812
    }
813
 
814
    if (!(menuPtr->menuFlags & MENU_RECONFIGURE_PENDING)) {
815
        menuPtr->menuFlags |= MENU_RECONFIGURE_PENDING;
816
        Tcl_DoWhenIdle(ReconfigureWindowsMenu, (ClientData) menuPtr);
817
    }
818
 
819
    mePtr->platformEntryData = (TkMenuPlatformEntryData) commandID;
820
 
821
    return TCL_OK;
822
}
823
 
824
/*
825
 *----------------------------------------------------------------------
826
 *
827
 * TkWinMenuProc --
828
 *
829
 *      The window proc for the dummy window we put popups in. This allows
830
 *      is to post a popup whether or not we know what the parent window
831
 *      is.
832
 *
833
 * Results:
834
 *      Returns whatever is appropriate for the message in question.
835
 *
836
 * Side effects:
837
 *      Normal side-effect for windows messages.
838
 *
839
 *----------------------------------------------------------------------
840
 */
841
 
842
static LRESULT CALLBACK
843
TkWinMenuProc(hwnd, message, wParam, lParam)
844
    HWND hwnd;
845
    UINT message;
846
    WPARAM wParam;
847
    LPARAM lParam;
848
{
849
    LRESULT lResult;
850
 
851
    if (!TkWinHandleMenuEvent(&hwnd, &message, &wParam, &lParam, &lResult)) {
852
        lResult = DefWindowProc(hwnd, message, wParam, lParam);
853
    }
854
    return lResult;
855
}
856
 
857
/*
858
 *----------------------------------------------------------------------
859
 *
860
 * TkWinHandleMenuEvent --
861
 *
862
 *      Filters out menu messages from messages passed to a top-level.
863
 *      Will respond appropriately to WM_COMMAND, WM_MENUSELECT,
864
 *      WM_MEASUREITEM, WM_DRAWITEM
865
 *
866
 * Result:
867
 *      Returns 1 if this handled the message; 0 if it did not.
868
 *
869
 * Side effects:
870
 *      All of the parameters may be modified so that the caller can
871
 *      think it is getting a different message. plResult points to
872
 *      the result that should be returned to windows from this message.
873
 *
874
 *----------------------------------------------------------------------
875
 */
876
 
877
int
878
TkWinHandleMenuEvent(phwnd, pMessage, pwParam, plParam, plResult)
879
    HWND *phwnd;
880
    UINT *pMessage;
881
    WPARAM *pwParam;
882
    LPARAM *plParam;
883
    LRESULT *plResult;
884
{
885
    Tcl_HashEntry *hashEntryPtr;
886
    int returnResult = 0;
887
    TkMenu *menuPtr;
888
    TkMenuEntry *mePtr;
889
 
890
    switch (*pMessage) {
891
        case WM_INITMENU:
892
            TkMenuInit();
893
            hashEntryPtr = Tcl_FindHashEntry(&winMenuTable, (char *) *pwParam);
894
            if (hashEntryPtr != NULL) {
895
                oldServiceMode = Tcl_SetServiceMode(TCL_SERVICE_ALL);
896
                menuPtr = (TkMenu *) Tcl_GetHashValue(hashEntryPtr);
897
                modalMenuPtr = menuPtr;
898
                if (menuPtr->menuFlags & MENU_RECONFIGURE_PENDING) {
899
                    Tcl_CancelIdleCall(ReconfigureWindowsMenu,
900
                            (ClientData) menuPtr);
901
                    ReconfigureWindowsMenu((ClientData) menuPtr);
902
                }
903
                if (!inPostMenu) {
904
                    Tcl_Interp *interp;
905
                    int code;
906
 
907
                    interp = menuPtr->interp;
908
                    Tcl_Preserve((ClientData)interp);
909
                    code = TkPreprocessMenu(menuPtr);
910
                    if ((code != TCL_OK) && (code != TCL_CONTINUE)
911
                            && (code != TCL_BREAK)) {
912
                        Tcl_AddErrorInfo(interp, "\n    (menu preprocess)");
913
                        Tcl_BackgroundError(interp);
914
                    }
915
                    Tcl_Release((ClientData)interp);
916
                }
917
                TkActivateMenuEntry(menuPtr, -1);
918
                *plResult = 0;
919
                returnResult = 1;
920
            } else {
921
                modalMenuPtr = NULL;
922
            }
923
            break;
924
 
925
#if 0
926
        /* CYGNUS LOCAL: WM_SYSCOMMAND is not the same as WM_COMMAND.  */
927
        case WM_SYSCOMMAND:
928
#endif
929
        case WM_COMMAND: {
930
            TkMenuInit();
931
            if (HIWORD(*pwParam) != 0) {
932
                break;
933
            }
934
            hashEntryPtr = Tcl_FindHashEntry(&commandTable,
935
                    (char *)LOWORD(*pwParam));
936
            if (hashEntryPtr == NULL) {
937
                break;
938
            }
939
            mePtr = (TkMenuEntry *) Tcl_GetHashValue(hashEntryPtr);
940
            if (mePtr != NULL) {
941
                TkMenuReferences *menuRefPtr;
942
                TkMenuEntry *parentEntryPtr;
943
                Tcl_Interp *interp;
944
                int code;
945
 
946
                /*
947
                 * We have to set the parent of this menu to be active
948
                 * if this is a submenu so that tearoffs will get the
949
                 * correct title.
950
                 */
951
 
952
                menuPtr = mePtr->menuPtr;
953
                menuRefPtr = TkFindMenuReferences(menuPtr->interp,
954
                        Tk_PathName(menuPtr->tkwin));
955
                if ((menuRefPtr != NULL) && (menuRefPtr->parentEntryPtr
956
                        != NULL)) {
957
                    for (parentEntryPtr = menuRefPtr->parentEntryPtr;
958
                         strcmp(parentEntryPtr->name,
959
                                 Tk_PathName(menuPtr->tkwin)) != 0;
960
                         parentEntryPtr = parentEntryPtr->nextCascadePtr) {
961
 
962
                        /*
963
                         * Empty loop body.
964
                         */
965
 
966
                    }
967
                    if (parentEntryPtr->menuPtr
968
                            ->entries[parentEntryPtr->index]->state
969
                            != tkDisabledUid) {
970
                        TkActivateMenuEntry(parentEntryPtr->menuPtr,
971
                                parentEntryPtr->index);
972
                    }
973
                }
974
 
975
                interp = menuPtr->interp;
976
                Tcl_Preserve((ClientData)interp);
977
                code = TkInvokeMenu(interp, menuPtr, mePtr->index);
978
                if ((code != TCL_OK) && (code != TCL_CONTINUE)
979
                        && (code != TCL_BREAK)) {
980
                    Tcl_AddErrorInfo(interp, "\n    (menu invoke)");
981
                    Tcl_BackgroundError(interp);
982
                }
983
                Tcl_Release((ClientData)interp);
984
            }
985
            *plResult = 0;
986
            returnResult = 1;
987
            break;
988
        }
989
 
990
 
991
        case WM_MENUCHAR: {
992
            unsigned char menuChar = (unsigned char) LOWORD(*pwParam);
993
            hashEntryPtr = Tcl_FindHashEntry(&winMenuTable, (char *) *plParam);
994
            if (hashEntryPtr != NULL) {
995
                int i;
996
 
997
                *plResult = 0;
998
                menuPtr = (TkMenu *) Tcl_GetHashValue(hashEntryPtr);
999
                for (i = 0; i < menuPtr->numEntries; i++) {
1000
                    int underline = menuPtr->entries[i]->underline;
1001
                    if ((-1 != underline)
1002
                            && (NULL != menuPtr->entries[i]->label)
1003
                            && (CharUpper((LPTSTR) menuChar)
1004
                            == CharUpper((LPTSTR) (unsigned char) menuPtr
1005
                            ->entries[i]->label[underline]))) {
1006
                        *plResult = (2 << 16) | i;
1007
                        returnResult = 1;
1008
                        break;
1009
                    }
1010
                }
1011
            }
1012
            break;
1013
        }
1014
 
1015
        case WM_MEASUREITEM: {
1016
            LPMEASUREITEMSTRUCT itemPtr = (LPMEASUREITEMSTRUCT) *plParam;
1017
 
1018
            if (itemPtr != NULL) {
1019
                mePtr = (TkMenuEntry *) itemPtr->itemData;
1020
                menuPtr = mePtr->menuPtr;
1021
 
1022
                TkRecomputeMenu(menuPtr);
1023
                itemPtr->itemHeight = mePtr->height;
1024
                itemPtr->itemWidth = mePtr->width;
1025
                if (mePtr->hideMargin) {
1026
                    itemPtr->itemWidth += 2 - indicatorDimensions[1];
1027
                } else {
1028
                    itemPtr->itemWidth += 2 * menuPtr->activeBorderWidth;
1029
                }
1030
                *plResult = 1;
1031
                returnResult = 1;
1032
            }
1033
            break;
1034
        }
1035
 
1036
        case WM_DRAWITEM: {
1037
            TkWinDrawable *twdPtr;
1038
            LPDRAWITEMSTRUCT itemPtr = (LPDRAWITEMSTRUCT) *plParam;
1039
            Tk_FontMetrics fontMetrics;
1040
 
1041
            if (itemPtr != NULL) {
1042
                mePtr = (TkMenuEntry *) itemPtr->itemData;
1043
                menuPtr = mePtr->menuPtr;
1044
                twdPtr = (TkWinDrawable *) ckalloc(sizeof(TkWinDrawable));
1045
                twdPtr->type = TWD_WINDC;
1046
                twdPtr->winDC.hdc = itemPtr->hDC;
1047
 
1048
                if (mePtr->state != tkDisabledUid) {
1049
                    if (itemPtr->itemState & ODS_SELECTED) {
1050
                        TkActivateMenuEntry(menuPtr, mePtr->index);
1051
                    } else {
1052
                        TkActivateMenuEntry(menuPtr, -1);
1053
                    }
1054
                }
1055
 
1056
                Tk_GetFontMetrics(menuPtr->tkfont, &fontMetrics);
1057
                TkpDrawMenuEntry(mePtr, (Drawable) twdPtr, menuPtr->tkfont,
1058
                        &fontMetrics, itemPtr->rcItem.left,
1059
                        itemPtr->rcItem.top, itemPtr->rcItem.right
1060
                        - itemPtr->rcItem.left, itemPtr->rcItem.bottom
1061
                        - itemPtr->rcItem.top, 0, 0);
1062
 
1063
                ckfree((char *) twdPtr);
1064
                *plResult = 1;
1065
                returnResult = 1;
1066
            }
1067
            break;
1068
        }
1069
 
1070
        case WM_MENUSELECT: {
1071
            UINT flags = HIWORD(*pwParam);
1072
 
1073
            TkMenuInit();
1074
 
1075
            if ((flags == 0xFFFF) && (*plParam == 0)) {
1076
                Tcl_SetServiceMode(oldServiceMode);
1077
                if (modalMenuPtr != NULL) {
1078
                    RecursivelyClearActiveMenu(modalMenuPtr);
1079
                }
1080
            } else {
1081
                menuPtr = NULL;
1082
                if (*plParam != 0) {
1083
                    hashEntryPtr = Tcl_FindHashEntry(&winMenuTable,
1084
                            (char *) *plParam);
1085
                    if (hashEntryPtr != NULL) {
1086
                        menuPtr = (TkMenu *) Tcl_GetHashValue(hashEntryPtr);
1087
                    }
1088
                }
1089
 
1090
                if (menuPtr != NULL) {
1091
                    mePtr = NULL;
1092
                    if (flags != 0xFFFF) {
1093
                        if (flags & MF_POPUP) {
1094
                            mePtr = menuPtr->entries[LOWORD(*pwParam)];
1095
                        } else {
1096
                            hashEntryPtr = Tcl_FindHashEntry(&commandTable,
1097
                                    (char *) LOWORD(*pwParam));
1098
                            if (hashEntryPtr != NULL) {
1099
                                mePtr = (TkMenuEntry *) Tcl_GetHashValue(hashEntryPtr);
1100
                            }
1101
                        }
1102
                    }
1103
 
1104
                    if ((mePtr == NULL) || (mePtr->state == tkDisabledUid)) {
1105
                        TkActivateMenuEntry(menuPtr, -1);
1106
                    } else {
1107
                        TkActivateMenuEntry(menuPtr, mePtr->index);
1108
                    }
1109
                    MenuSelectEvent(menuPtr);
1110
                    Tcl_ServiceAll();
1111
                }
1112
            }
1113
        }
1114
    }
1115
    return returnResult;
1116
}
1117
 
1118
/*
1119
 *----------------------------------------------------------------------
1120
 *
1121
 * RecursivelyClearActiveMenu --
1122
 *
1123
 *      Recursively clears the active entry in the menu's cascade hierarchy.
1124
 *
1125
 * Results:
1126
 *      None.
1127
 *
1128
 * Side effects:
1129
 *      Generates <<MenuSelect>> virtual events.
1130
 *
1131
 *----------------------------------------------------------------------
1132
 */
1133
 
1134
void
1135
RecursivelyClearActiveMenu(
1136
    TkMenu *menuPtr)            /* The menu to reset. */
1137
{
1138
    int i;
1139
    TkMenuEntry *mePtr;
1140
 
1141
    TkActivateMenuEntry(menuPtr, -1);
1142
    MenuSelectEvent(menuPtr);
1143
    for (i = 0; i < menuPtr->numEntries; i++) {
1144
        mePtr = menuPtr->entries[i];
1145
        if (mePtr->type == CASCADE_ENTRY) {
1146
            if ((mePtr->childMenuRefPtr != NULL)
1147
                    && (mePtr->childMenuRefPtr->menuPtr != NULL)) {
1148
                RecursivelyClearActiveMenu(mePtr->childMenuRefPtr->menuPtr);
1149
            }
1150
        }
1151
    }
1152
}
1153
 
1154
/*
1155
 *----------------------------------------------------------------------
1156
 *
1157
 * TkpSetWindowMenuBar --
1158
 *
1159
 *      Associates a given menu with a window.
1160
 *
1161
 * Results:
1162
 *      None.
1163
 *
1164
 * Side effects:
1165
 *      On Windows and UNIX, associates the platform menu with the
1166
 *      platform window.
1167
 *
1168
 *----------------------------------------------------------------------
1169
 */
1170
 
1171
void
1172
TkpSetWindowMenuBar(tkwin, menuPtr)
1173
    Tk_Window tkwin;        /* The window we are putting the menubar into.*/
1174
    TkMenu *menuPtr;        /* The menu we are inserting */
1175
{
1176
    HMENU winMenuHdl;
1177
 
1178
    if (menuPtr != NULL) {
1179
        Tcl_HashEntry *hashEntryPtr;
1180
        int newEntry;
1181
 
1182
        winMenuHdl = (HMENU) menuPtr->platformData;
1183
        hashEntryPtr = Tcl_FindHashEntry(&winMenuTable, (char *) winMenuHdl);
1184
        Tcl_DeleteHashEntry(hashEntryPtr);
1185
        DestroyMenu(winMenuHdl);
1186
        winMenuHdl = CreateMenu();
1187
        hashEntryPtr = Tcl_CreateHashEntry(&winMenuTable, (char *) winMenuHdl,
1188
                &newEntry);
1189
        Tcl_SetHashValue(hashEntryPtr, (char *) menuPtr);
1190
        menuPtr->platformData = (TkMenuPlatformData) winMenuHdl;
1191
        TkWinSetMenu(tkwin, winMenuHdl);
1192
        if (menuPtr->menuFlags & MENU_RECONFIGURE_PENDING) {
1193
            Tcl_DoWhenIdle(ReconfigureWindowsMenu, (ClientData) menuPtr);
1194
            menuPtr->menuFlags |= MENU_RECONFIGURE_PENDING;
1195
        }
1196
    } else {
1197
        TkWinSetMenu(tkwin, NULL);
1198
    }
1199
}
1200
 
1201
 
1202
/*
1203
 *----------------------------------------------------------------------
1204
 *
1205
 * TkpSetMainMenubar --
1206
 *
1207
 *      Puts the menu associated with a window into the menubar. Should
1208
 *      only be called when the window is in front.
1209
 *
1210
 * Results:
1211
 *      None.
1212
 *
1213
 * Side effects:
1214
 *      The menubar is changed.
1215
 *
1216
 *----------------------------------------------------------------------
1217
 */
1218
void
1219
TkpSetMainMenubar(
1220
    Tcl_Interp *interp,         /* The interpreter of the application */
1221
    Tk_Window tkwin,            /* The frame we are setting up */
1222
    char *menuName)             /* The name of the menu to put in front.
1223
                                 * If NULL, use the default menu bar.
1224
                                 */
1225
{
1226
    /*
1227
     * Nothing to do.
1228
     */
1229
}
1230
 
1231
/*
1232
 *----------------------------------------------------------------------
1233
 *
1234
 * GetMenuIndicatorGeometry --
1235
 *
1236
 *      Gets the width and height of the indicator area of a menu.
1237
 *
1238
 * Results:
1239
 *      widthPtr and heightPtr are set.
1240
 *
1241
 * Side effects:
1242
 *      None.
1243
 *
1244
 *----------------------------------------------------------------------
1245
 */
1246
 
1247
void
1248
GetMenuIndicatorGeometry (
1249
    TkMenu *menuPtr,                    /* The menu we are measuring */
1250
    TkMenuEntry *mePtr,                 /* The entry we are measuring */
1251
    Tk_Font tkfont,                     /* Precalculated font */
1252
    CONST Tk_FontMetrics *fmPtr,        /* Precalculated font metrics */
1253
    int *widthPtr,                      /* The resulting width */
1254
    int *heightPtr)                     /* The resulting height */
1255
{
1256
    *heightPtr = indicatorDimensions[0];
1257
    if (mePtr->hideMargin) {
1258
        *widthPtr = 0;
1259
    } else {
1260
        *widthPtr = indicatorDimensions[1] - menuPtr->borderWidth;
1261
    }
1262
}
1263
 
1264
/*
1265
 *----------------------------------------------------------------------
1266
 *
1267
 * GetMenuAccelGeometry --
1268
 *
1269
 *      Gets the width and height of the indicator area of a menu.
1270
 *
1271
 * Results:
1272
 *      widthPtr and heightPtr are set.
1273
 *
1274
 * Side effects:
1275
 *      None.
1276
 *
1277
 *----------------------------------------------------------------------
1278
 */
1279
 
1280
void
1281
GetMenuAccelGeometry (
1282
    TkMenu *menuPtr,                    /* The menu we are measuring */
1283
    TkMenuEntry *mePtr,                 /* The entry we are measuring */
1284
    Tk_Font tkfont,                     /* The precalculated font */
1285
    CONST Tk_FontMetrics *fmPtr,        /* The precalculated font metrics */
1286
    int *widthPtr,                      /* The resulting width */
1287
    int *heightPtr)                     /* The resulting height */
1288
{
1289
    *heightPtr = fmPtr->linespace;
1290
    if (mePtr->type == CASCADE_ENTRY) {
1291
        *widthPtr = 0;
1292
    } else if (mePtr->accel == NULL) {
1293
        *widthPtr = 0;
1294
    } else {
1295
        *widthPtr = Tk_TextWidth(tkfont, mePtr->accel, mePtr->accelLength);
1296
    }
1297
}
1298
 
1299
/*
1300
 *----------------------------------------------------------------------
1301
 *
1302
 * GetTearoffEntryGeometry --
1303
 *
1304
 *      Gets the width and height of the indicator area of a menu.
1305
 *
1306
 * Results:
1307
 *      widthPtr and heightPtr are set.
1308
 *
1309
 * Side effects:
1310
 *      None.
1311
 *
1312
 *----------------------------------------------------------------------
1313
 */
1314
 
1315
void
1316
GetTearoffEntryGeometry (
1317
    TkMenu *menuPtr,                    /* The menu we are measuring */
1318
    TkMenuEntry *mePtr,                 /* The entry we are measuring */
1319
    Tk_Font tkfont,                     /* The precalculated font */
1320
    CONST Tk_FontMetrics *fmPtr,        /* The precalculated font metrics */
1321
    int *widthPtr,                      /* The resulting width */
1322
    int *heightPtr)                     /* The resulting height */
1323
{
1324
    if (menuPtr->menuType != MASTER_MENU) {
1325
        *heightPtr = 0;
1326
    } else {
1327
        *heightPtr = fmPtr->linespace;
1328
    }
1329
    *widthPtr = 0;
1330
}
1331
 
1332
/*
1333
 *----------------------------------------------------------------------
1334
 *
1335
 * GetMenuSeparatorGeometry --
1336
 *
1337
 *      Gets the width and height of the indicator area of a menu.
1338
 *
1339
 * Results:
1340
 *      widthPtr and heightPtr are set.
1341
 *
1342
 * Side effects:
1343
 *      None.
1344
 *
1345
 *----------------------------------------------------------------------
1346
 */
1347
 
1348
void
1349
GetMenuSeparatorGeometry (
1350
    TkMenu *menuPtr,                    /* The menu we are measuring */
1351
    TkMenuEntry *mePtr,                 /* The entry we are measuring */
1352
    Tk_Font tkfont,                     /* The precalculated font */
1353
    CONST Tk_FontMetrics *fmPtr,        /* The precalcualted font metrics */
1354
    int *widthPtr,                      /* The resulting width */
1355
    int *heightPtr)                     /* The resulting height */
1356
{
1357
    *widthPtr = 0;
1358
    *heightPtr = fmPtr->linespace;
1359
}
1360
 
1361
/*
1362
 *----------------------------------------------------------------------
1363
 *
1364
 * DrawWindowsSystemBitmap --
1365
 *
1366
 *      Draws the windows system bitmap given by bitmapID into the rect
1367
 *      given by rectPtr in the drawable. The bitmap is centered in the
1368
 *      rectangle. It is not clipped, so if the bitmap is bigger than
1369
 *      the rect it will bleed.
1370
 *
1371
 * Results:
1372
 *      None.
1373
 *
1374
 * Side effects:
1375
 *      Drawing occurs. Some storage is allocated and released.
1376
 *
1377
 *----------------------------------------------------------------------
1378
 */
1379
 
1380
static void
1381
DrawWindowsSystemBitmap(display, drawable, gc, rectPtr, bitmapID, alignFlags)
1382
    Display *display;                   /* The display we are drawing into */
1383
    Drawable drawable;                  /* The drawable we are working with */
1384
    GC gc;                              /* The GC to draw with */
1385
    CONST RECT *rectPtr;                /* The rectangle to draw into */
1386
    int bitmapID;                       /* The windows id of the system
1387
                                         * bitmap to draw. */
1388
    int alignFlags;                     /* How to align the bitmap inside the
1389
                                         * rectangle. */
1390
{
1391
    TkWinDCState state;
1392
    HDC hdc = TkWinGetDrawableDC(display, drawable, &state);
1393
    HDC scratchDC;
1394
    HBITMAP bitmap;
1395
    BITMAP bm;
1396
    POINT ptSize;
1397
    POINT ptOrg;
1398
    int topOffset, leftOffset;
1399
 
1400
    SetBkColor(hdc, gc->background);
1401
    SetTextColor(hdc, gc->foreground);
1402
 
1403
    scratchDC = CreateCompatibleDC(hdc);
1404
    bitmap = LoadBitmap(NULL, MAKEINTRESOURCE(bitmapID));
1405
 
1406
    SelectObject(scratchDC, bitmap);
1407
    SetMapMode(scratchDC, GetMapMode(hdc));
1408
    GetObject(bitmap, sizeof(BITMAP), &bm);
1409
    ptSize.x = bm.bmWidth;
1410
    ptSize.y = bm.bmHeight;
1411
    DPtoLP(hdc, &ptSize, 1);
1412
 
1413
    ptOrg.y = ptOrg.x = 0;
1414
    DPtoLP(hdc, &ptOrg, 1);
1415
 
1416
    if (alignFlags & ALIGN_BITMAP_TOP) {
1417
        topOffset = 0;
1418
    } else if (alignFlags & ALIGN_BITMAP_BOTTOM) {
1419
        topOffset = (rectPtr->bottom - rectPtr->top) - ptSize.y;
1420
    } else {
1421
        topOffset = (rectPtr->bottom - rectPtr->top) / 2 - (ptSize.y / 2);
1422
    }
1423
 
1424
    if (alignFlags & ALIGN_BITMAP_LEFT) {
1425
        leftOffset = 0;
1426
    } else if (alignFlags & ALIGN_BITMAP_RIGHT) {
1427
        leftOffset = (rectPtr->right - rectPtr->left) - ptSize.x;
1428
    } else {
1429
        leftOffset = (rectPtr->right - rectPtr->left) / 2 - (ptSize.x / 2);
1430
    }
1431
 
1432
    BitBlt(hdc, rectPtr->left + leftOffset, rectPtr->top + topOffset, ptSize.x,
1433
            ptSize.y, scratchDC, ptOrg.x, ptOrg.y, SRCCOPY);
1434
    DeleteDC(scratchDC);
1435
    DeleteObject(bitmap);
1436
 
1437
    TkWinReleaseDrawableDC(drawable, hdc, &state);
1438
}
1439
 
1440
/*
1441
 *----------------------------------------------------------------------
1442
 *
1443
 * DrawMenuEntryIndicator --
1444
 *
1445
 *      This procedure draws the indicator part of a menu.
1446
 *
1447
 * Results:
1448
 *      None.
1449
 *
1450
 * Side effects:
1451
 *      Commands are output to X to display the menu in its
1452
 *      current mode.
1453
 *
1454
 *----------------------------------------------------------------------
1455
 */
1456
void
1457
DrawMenuEntryIndicator(menuPtr, mePtr, d, gc, indicatorGC, tkfont, fmPtr, x,
1458
        y, width, height)
1459
    TkMenu *menuPtr;                /* The menu we are drawing */
1460
    TkMenuEntry *mePtr;             /* The entry we are drawing */
1461
    Drawable d;                     /* What we are drawing into */
1462
    GC gc;                          /* The gc we are drawing with */
1463
    GC indicatorGC;                 /* The gc for indicator objects */
1464
    Tk_Font tkfont;                 /* The precalculated font */
1465
    CONST Tk_FontMetrics *fmPtr;    /* The precalculated font metrics */
1466
    int x;                          /* Left edge */
1467
    int y;                          /* Top edge */
1468
    int width;
1469
    int height;
1470
{
1471
    if ((mePtr->type == CHECK_BUTTON_ENTRY ||
1472
            mePtr->type == RADIO_BUTTON_ENTRY)
1473
            && mePtr->indicatorOn
1474
            && mePtr->entryFlags & ENTRY_SELECTED) {
1475
        RECT rect;
1476
        GC whichGC;
1477
 
1478
        if (mePtr->state != tkNormalUid) {
1479
            whichGC = gc;
1480
        } else {
1481
            whichGC = indicatorGC;
1482
        }
1483
 
1484
        rect.top = y;
1485
        rect.bottom = y + mePtr->height;
1486
        rect.left = menuPtr->borderWidth + menuPtr->activeBorderWidth + x;
1487
        rect.right = mePtr->indicatorSpace + x;
1488
 
1489
        if ((mePtr->state == tkDisabledUid) && (menuPtr->disabledFg != NULL)
1490
                && (versionInfo.dwMajorVersion >= 4)) {
1491
            RECT hilightRect;
1492
            COLORREF oldFgColor = whichGC->foreground;
1493
 
1494
            whichGC->foreground = GetSysColor(COLOR_3DHILIGHT);
1495
            hilightRect.top = rect.top + 1;
1496
            hilightRect.bottom = rect.bottom + 1;
1497
            hilightRect.left = rect.left + 1;
1498
            hilightRect.right = rect.right + 1;
1499
            DrawWindowsSystemBitmap(menuPtr->display, d, whichGC,
1500
                    &hilightRect, OBM_CHECK, 0);
1501
            whichGC->foreground = oldFgColor;
1502
        }
1503
 
1504
        DrawWindowsSystemBitmap(menuPtr->display, d, whichGC, &rect,
1505
                OBM_CHECK, 0);
1506
 
1507
        if ((mePtr->state == tkDisabledUid)
1508
                && (menuPtr->disabledImageGC != None)
1509
                && (versionInfo.dwMajorVersion < 4)) {
1510
            XFillRectangle(menuPtr->display, d, menuPtr->disabledImageGC,
1511
                    rect.left, rect.top, rect.right, rect.bottom);
1512
        }
1513
    }
1514
}
1515
 
1516
/*
1517
 *----------------------------------------------------------------------
1518
 *
1519
 * DrawMenuEntryAccelerator --
1520
 *
1521
 *      This procedure draws the accelerator part of a menu. We
1522
 *      need to decide what to draw here. Should we replace strings
1523
 *      like "Control", "Command", etc?
1524
 *
1525
 * Results:
1526
 *      None.
1527
 *
1528
 * Side effects:
1529
 *      Commands are output to X to display the menu in its
1530
 *      current mode.
1531
 *
1532
 *----------------------------------------------------------------------
1533
 */
1534
 
1535
void
1536
DrawMenuEntryAccelerator(menuPtr, mePtr, d, gc, tkfont, fmPtr,
1537
        activeBorder, x, y, width, height, drawArrow)
1538
    TkMenu *menuPtr;                    /* The menu we are drawing */
1539
    TkMenuEntry *mePtr;                 /* The entry we are drawing */
1540
    Drawable d;                         /* What we are drawing into */
1541
    GC gc;                              /* The gc we are drawing with */
1542
    Tk_Font tkfont;                     /* The precalculated font */
1543
    CONST Tk_FontMetrics *fmPtr;        /* The precalculated font metrics */
1544
    Tk_3DBorder activeBorder;           /* The border when an item is active */
1545
    int x;                              /* left edge */
1546
    int y;                              /* top edge */
1547
    int width;                          /* Width of menu entry */
1548
    int height;                         /* Height of menu entry */
1549
    int drawArrow;                      /* For cascade menus, whether of not
1550
                                         * to draw the arraw. I cannot figure
1551
                                         * out Windows' algorithm for where
1552
                                         * to draw this. */
1553
{
1554
    int baseline;
1555
    int leftEdge = x + mePtr->indicatorSpace + mePtr->labelWidth;
1556
 
1557
    baseline = y + (height + fmPtr->ascent - fmPtr->descent) / 2;
1558
 
1559
    if ((mePtr->state == tkDisabledUid) && (menuPtr->disabledFg != NULL)
1560
            && ((mePtr->accel != NULL)
1561
            || ((mePtr->type == CASCADE_ENTRY) && drawArrow))) {
1562
        if (versionInfo.dwMajorVersion >= 4) {
1563
            COLORREF oldFgColor = gc->foreground;
1564
 
1565
            gc->foreground = GetSysColor(COLOR_3DHILIGHT);
1566
            if (mePtr->accel != NULL) {
1567
                Tk_DrawChars(menuPtr->display, d, gc, tkfont, mePtr->accel,
1568
                        mePtr->accelLength, leftEdge + 1, baseline + 1);
1569
            }
1570
 
1571
            if (mePtr->type == CASCADE_ENTRY) {
1572
                RECT rect;
1573
 
1574
                rect.top = y + GetSystemMetrics(SM_CYBORDER) + 1;
1575
                rect.bottom = y + height - GetSystemMetrics(SM_CYBORDER) + 1;
1576
                rect.left = x + mePtr->indicatorSpace + mePtr->labelWidth + 1;
1577
                rect.right = x + width;
1578
                DrawWindowsSystemBitmap(menuPtr->display, d, gc, &rect,
1579
                        OBM_MNARROW, ALIGN_BITMAP_RIGHT);
1580
            }
1581
            gc->foreground = oldFgColor;
1582
        }
1583
    }
1584
 
1585
    if (mePtr->accel != NULL) {
1586
        Tk_DrawChars(menuPtr->display, d, gc, tkfont, mePtr->accel,
1587
                mePtr->accelLength, leftEdge, baseline);
1588
    }
1589
 
1590
    if ((mePtr->state == tkDisabledUid)
1591
            && (menuPtr->disabledImageGC != None)
1592
            && (versionInfo.dwMajorVersion < 4)) {
1593
        XFillRectangle(menuPtr->display, d, menuPtr->disabledImageGC,
1594
                leftEdge, y, width - mePtr->labelWidth
1595
                - mePtr->indicatorSpace, height);
1596
    }
1597
 
1598
    if ((mePtr->type == CASCADE_ENTRY) && drawArrow) {
1599
        RECT rect;
1600
 
1601
        rect.top = y + GetSystemMetrics(SM_CYBORDER);
1602
        rect.bottom = y + height - GetSystemMetrics(SM_CYBORDER);
1603
        rect.left = x + mePtr->indicatorSpace + mePtr->labelWidth;
1604
        rect.right = x + width - 1;
1605
        DrawWindowsSystemBitmap(menuPtr->display, d, gc, &rect, OBM_MNARROW,
1606
                ALIGN_BITMAP_RIGHT);
1607
        if ((mePtr->state == tkDisabledUid)
1608
                && (menuPtr->disabledImageGC != None)
1609
                && (versionInfo.dwMajorVersion < 4)) {
1610
            XFillRectangle(menuPtr->display, d, menuPtr->disabledImageGC,
1611
                    rect.left, rect.top, rect.right, rect.bottom);
1612
        }
1613
    }
1614
}
1615
 
1616
/*
1617
 *----------------------------------------------------------------------
1618
 *
1619
 * DrawMenuSeparator --
1620
 *
1621
 *      The menu separator is drawn.
1622
 *
1623
 * Results:
1624
 *      None.
1625
 *
1626
 * Side effects:
1627
 *      Commands are output to X to display the menu in its
1628
 *      current mode.
1629
 *
1630
 *----------------------------------------------------------------------
1631
 */
1632
void
1633
DrawMenuSeparator(menuPtr, mePtr, d, gc, tkfont, fmPtr, x, y, width, height)
1634
    TkMenu *menuPtr;                    /* The menu we are drawing */
1635
    TkMenuEntry *mePtr;                 /* The entry we are drawing */
1636
    Drawable d;                         /* What we are drawing into */
1637
    GC gc;                              /* The gc we are drawing with */
1638
    Tk_Font tkfont;                     /* The precalculated font */
1639
    CONST Tk_FontMetrics *fmPtr;        /* The precalculated font metrics */
1640
    int x;                              /* left edge */
1641
    int y;                              /* top edge */
1642
    int width;                          /* width of item */
1643
    int height;                         /* height of item */
1644
{
1645
    XPoint points[2];
1646
 
1647
    points[0].x = x;
1648
    points[0].y = y + height / 2;
1649
    points[1].x = x + width - 1;
1650
    points[1].y = points[0].y;
1651
    Tk_Draw3DPolygon(menuPtr->tkwin, d,
1652
            menuPtr->border, points, 2, 1, TK_RELIEF_RAISED);
1653
}
1654
 
1655
/*
1656
 *----------------------------------------------------------------------
1657
 *
1658
 * DrawMenuUnderline --
1659
 *
1660
 *      On appropriate platforms, draw the underline character for the
1661
 *      menu.
1662
 *
1663
 * Results:
1664
 *      None.
1665
 *
1666
 * Side effects:
1667
 *      Commands are output to X to display the menu in its
1668
 *      current mode.
1669
 *
1670
 *----------------------------------------------------------------------
1671
 */
1672
static void
1673
DrawMenuUnderline(
1674
    TkMenu *menuPtr,                    /* The menu to draw into */
1675
    TkMenuEntry *mePtr,                 /* The entry we are drawing */
1676
    Drawable d,                         /* What we are drawing into */
1677
    GC gc,                              /* The gc to draw into */
1678
    Tk_Font tkfont,                     /* The precalculated font */
1679
    CONST Tk_FontMetrics *fmPtr,        /* The precalculated font metrics */
1680
    int x,                              /* Left Edge */
1681
    int y,                              /* Top Edge */
1682
    int width,                          /* Width of entry */
1683
    int height)                         /* Height of entry */
1684
{
1685
    if (mePtr->underline >= 0) {
1686
        Tk_UnderlineChars(menuPtr->display, d,
1687
                gc, tkfont, mePtr->label, x + mePtr->indicatorSpace,
1688
                y + (height + fmPtr->ascent - fmPtr->descent) / 2,
1689
                mePtr->underline, mePtr->underline + 1);
1690
    }
1691
}
1692
 
1693
/*
1694
 *--------------------------------------------------------------
1695
 *
1696
 * MenuKeyBindProc --
1697
 *
1698
 *      This procedure is invoked when keys related to pulling
1699
 *      down menus is pressed. The corresponding Windows events
1700
 *      are generated and passed to DefWindowProc if appropriate.
1701
 *
1702
 * Results:
1703
 *      Always returns TCL_OK.
1704
 *
1705
 * Side effects:
1706
 *      The menu system may take over and process user events
1707
 *      for menu input.
1708
 *
1709
 *--------------------------------------------------------------
1710
 */
1711
 
1712
static int
1713
MenuKeyBindProc(clientData, interp, eventPtr, tkwin, keySym)
1714
    ClientData clientData;      /* not used in this proc */
1715
    Tcl_Interp *interp;         /* The interpreter of the receiving window. */
1716
    XEvent *eventPtr;           /* The XEvent to process */
1717
    Tk_Window tkwin;            /* The window receiving the event */
1718
    KeySym keySym;              /* The key sym that is produced. */
1719
{
1720
    UINT scanCode;
1721
    UINT virtualKey;
1722
    TkWindow *winPtr = (TkWindow *)tkwin;
1723
    int i;
1724
 
1725
    if (eventPtr->type == KeyPress) {
1726
        switch (keySym) {
1727
        case XK_Alt_L:
1728
            scanCode = MapVirtualKey(VK_LMENU, 0);
1729
            CallWindowProc(DefWindowProc, Tk_GetHWND(Tk_WindowId(tkwin)),
1730
                    WM_SYSKEYDOWN, VK_MENU, (scanCode << 16)
1731
                    | (1 << 29));
1732
            break;
1733
        case XK_Alt_R:
1734
            scanCode = MapVirtualKey(VK_RMENU, 0);
1735
            CallWindowProc(DefWindowProc, Tk_GetHWND(Tk_WindowId(tkwin)),
1736
                    WM_SYSKEYDOWN, VK_MENU, (scanCode << 16)
1737
                    | (1 << 29) | (1 << 24));
1738
            break;
1739
        case XK_F10:
1740
            scanCode = MapVirtualKey(VK_F10, 0);
1741
            CallWindowProc(DefWindowProc, Tk_GetHWND(Tk_WindowId(tkwin)),
1742
                    WM_SYSKEYDOWN, VK_F10, (scanCode << 16));
1743
            break;
1744
        default:
1745
            virtualKey = XKeysymToKeycode(winPtr->display, keySym);
1746
            scanCode = MapVirtualKey(virtualKey, 0);
1747
            if (0 != scanCode) {
1748
                CallWindowProc(DefWindowProc, Tk_GetHWND(Tk_WindowId(tkwin)),
1749
                        WM_SYSKEYDOWN, virtualKey, ((scanCode << 16)
1750
                        | (1 << 29)));
1751
                if (eventPtr->xkey.nchars > 0) {
1752
                    for (i = 0; i < eventPtr->xkey.nchars; i++) {
1753
                        CallWindowProc(DefWindowProc,
1754
                                Tk_GetHWND(Tk_WindowId(tkwin)),
1755
                                WM_SYSCHAR,
1756
                                eventPtr->xkey.trans_chars[i],
1757
                                ((scanCode << 16) | (1 << 29)));
1758
                    }
1759
                }
1760
            }
1761
        }
1762
    } else if (eventPtr->type == KeyRelease) {
1763
        switch (keySym) {
1764
        case XK_Alt_L:
1765
            scanCode = MapVirtualKey(VK_LMENU, 0);
1766
            CallWindowProc(DefWindowProc, Tk_GetHWND(Tk_WindowId(tkwin)),
1767
                    WM_SYSKEYUP, VK_MENU, (scanCode << 16)
1768
                    | (1 << 29) | (1 << 30) | (1 << 31));
1769
            break;
1770
        case XK_Alt_R:
1771
            scanCode = MapVirtualKey(VK_RMENU, 0);
1772
            CallWindowProc(DefWindowProc, Tk_GetHWND(Tk_WindowId(tkwin)),
1773
                    WM_SYSKEYUP, VK_MENU, (scanCode << 16) | (1 << 24)
1774
                    | (0x111 << 29) | (1 << 30) | (1 << 31));
1775
            break;
1776
        case XK_F10:
1777
            scanCode = MapVirtualKey(VK_F10, 0);
1778
            CallWindowProc(DefWindowProc, Tk_GetHWND(Tk_WindowId(tkwin)),
1779
                    WM_SYSKEYUP, VK_F10, (scanCode << 16)
1780
                    | (1 << 30) | (1 << 31));
1781
            break;
1782
        default:
1783
            virtualKey = XKeysymToKeycode(winPtr->display, keySym);
1784
            scanCode = MapVirtualKey(virtualKey, 0);
1785
            if (0 != scanCode) {
1786
                CallWindowProc(DefWindowProc, Tk_GetHWND(Tk_WindowId(tkwin)),
1787
                        WM_SYSKEYUP, virtualKey, ((scanCode << 16)
1788
                        | (1 << 29) | (1 << 30) | (1 << 31)));
1789
            }
1790
        }
1791
    }
1792
    return TCL_OK;
1793
}
1794
 
1795
/*
1796
 *--------------------------------------------------------------
1797
 *
1798
 * TkpInitializeMenuBindings --
1799
 *
1800
 *      For every interp, initializes the bindings for Windows
1801
 *      menus. Does nothing on Mac or XWindows.
1802
 *
1803
 * Results:
1804
 *      None.
1805
 *
1806
 * Side effects:
1807
 *      C-level bindings are setup for the interp which will
1808
 *      handle Alt-key sequences for menus without beeping
1809
 *      or interfering with user-defined Alt-key bindings.
1810
 *
1811
 *--------------------------------------------------------------
1812
 */
1813
 
1814
void
1815
TkpInitializeMenuBindings(interp, bindingTable)
1816
    Tcl_Interp *interp;             /* The interpreter to set. */
1817
    Tk_BindingTable bindingTable;   /* The table to add to. */
1818
{
1819
    Tk_Uid uid = Tk_GetUid("all");
1820
 
1821
    /*
1822
     * We need to set up the bindings for menubars. These have to
1823
     * recreate windows events, so we need to have a C-level
1824
     * binding for this. We have to generate the WM_SYSKEYDOWNS
1825
     * and WM_SYSKEYUPs appropriately.
1826
     */
1827
 
1828
    TkCreateBindingProcedure(interp, bindingTable, (ClientData)uid,
1829
            "<Alt_L>", MenuKeyBindProc, NULL, NULL);
1830
    TkCreateBindingProcedure(interp, bindingTable, (ClientData)uid,
1831
            "<KeyRelease-Alt_L>", MenuKeyBindProc, NULL, NULL);
1832
    TkCreateBindingProcedure(interp, bindingTable, (ClientData)uid,
1833
            "<Alt_R>", MenuKeyBindProc, NULL, NULL);
1834
    TkCreateBindingProcedure(interp, bindingTable, (ClientData)uid,
1835
            "<KeyRelease-Alt_R>", MenuKeyBindProc, NULL, NULL);
1836
    TkCreateBindingProcedure(interp, bindingTable, (ClientData)uid,
1837
            "<Alt-KeyPress>", MenuKeyBindProc, NULL, NULL);
1838
    TkCreateBindingProcedure(interp, bindingTable, (ClientData)uid,
1839
            "<Alt-KeyRelease>", MenuKeyBindProc, NULL, NULL);
1840
    TkCreateBindingProcedure(interp, bindingTable, (ClientData)uid,
1841
            "<KeyPress-F10>", MenuKeyBindProc, NULL, NULL);
1842
    TkCreateBindingProcedure(interp, bindingTable, (ClientData)uid,
1843
            "<KeyRelease-F10>", MenuKeyBindProc, NULL, NULL);
1844
}
1845
 
1846
/*
1847
 *----------------------------------------------------------------------
1848
 *
1849
 * DrawMenuEntryLabel --
1850
 *
1851
 *      This procedure draws the label part of a menu.
1852
 *
1853
 * Results:
1854
 *      None.
1855
 *
1856
 * Side effects:
1857
 *      Commands are output to X to display the menu in its
1858
 *      current mode.
1859
 *
1860
 *----------------------------------------------------------------------
1861
 */
1862
 
1863
static void
1864
DrawMenuEntryLabel(
1865
    TkMenu *menuPtr,                    /* The menu we are drawing */
1866
    TkMenuEntry *mePtr,                 /* The entry we are drawing */
1867
    Drawable d,                         /* What we are drawing into */
1868
    GC gc,                              /* The gc we are drawing into */
1869
    Tk_Font tkfont,                     /* The precalculated font */
1870
    CONST Tk_FontMetrics *fmPtr,        /* The precalculated font metrics */
1871
    int x,                              /* left edge */
1872
    int y,                              /* right edge */
1873
    int width,                          /* width of entry */
1874
    int height)                         /* height of entry */
1875
{
1876
    int baseline;
1877
    int indicatorSpace =  mePtr->indicatorSpace;
1878
    int leftEdge = x + indicatorSpace + menuPtr->activeBorderWidth;
1879
    int imageHeight, imageWidth;
1880
 
1881
    /*
1882
     * Draw label or bitmap or image for entry.
1883
     */
1884
 
1885
    baseline = y + (height + fmPtr->ascent - fmPtr->descent) / 2;
1886
    if (mePtr->image != NULL) {
1887
        Tk_SizeOfImage(mePtr->image, &imageWidth, &imageHeight);
1888
        if ((mePtr->selectImage != NULL)
1889
                && (mePtr->entryFlags & ENTRY_SELECTED)) {
1890
            Tk_RedrawImage(mePtr->selectImage, 0, 0,
1891
                    imageWidth, imageHeight, d, leftEdge,
1892
                    (int) (y + (mePtr->height - imageHeight)/2));
1893
        } else {
1894
            Tk_RedrawImage(mePtr->image, 0, 0, imageWidth,
1895
                    imageHeight, d, leftEdge,
1896
                    (int) (y + (mePtr->height - imageHeight)/2));
1897
        }
1898
    } else if (mePtr->bitmap != None) {
1899
        int width, height;
1900
 
1901
        Tk_SizeOfBitmap(menuPtr->display,
1902
                mePtr->bitmap, &width, &height);
1903
        XCopyPlane(menuPtr->display,
1904
                mePtr->bitmap, d,
1905
                gc, 0, 0, (unsigned) width, (unsigned) height, leftEdge,
1906
                (int) (y + (mePtr->height - height)/2), 1);
1907
    } else {
1908
        if (mePtr->labelLength > 0) {
1909
            Tk_DrawChars(menuPtr->display, d, gc,
1910
                    tkfont, mePtr->label, mePtr->labelLength,
1911
                    leftEdge, baseline);
1912
            DrawMenuUnderline(menuPtr, mePtr, d, gc, tkfont, fmPtr, x, y,
1913
                    width, height);
1914
        }
1915
    }
1916
 
1917
    if (mePtr->state == tkDisabledUid) {
1918
        if (menuPtr->disabledFg == NULL) {
1919
            XFillRectangle(menuPtr->display, d, menuPtr->disabledGC, x, y,
1920
                    (unsigned) width, (unsigned) height);
1921
        } else if ((mePtr->image != NULL)
1922
                && (menuPtr->disabledImageGC != None)) {
1923
            XFillRectangle(menuPtr->display, d, menuPtr->disabledImageGC,
1924
                    leftEdge,
1925
                    (int) (y + (mePtr->height - imageHeight)/2),
1926
                    (unsigned) imageWidth, (unsigned) imageHeight);
1927
        }
1928
    }
1929
}
1930
 
1931
/*
1932
 *--------------------------------------------------------------
1933
 *
1934
 * TkpComputeMenubarGeometry --
1935
 *
1936
 *      This procedure is invoked to recompute the size and
1937
 *      layout of a menu that is a menubar clone.
1938
 *
1939
 * Results:
1940
 *      None.
1941
 *
1942
 * Side effects:
1943
 *      Fields of menu entries are changed to reflect their
1944
 *      current positions, and the size of the menu window
1945
 *      itself may be changed.
1946
 *
1947
 *--------------------------------------------------------------
1948
 */
1949
 
1950
void
1951
TkpComputeMenubarGeometry(menuPtr)
1952
    TkMenu *menuPtr;            /* Structure describing menu. */
1953
{
1954
    TkpComputeStandardMenuGeometry(menuPtr);
1955
}
1956
 
1957
/*
1958
 *----------------------------------------------------------------------
1959
 *
1960
 * DrawTearoffEntry --
1961
 *
1962
 *      This procedure draws the background part of a menu.
1963
 *
1964
 * Results:
1965
 *      None.
1966
 *
1967
 * Side effects:
1968
 *      Commands are output to X to display the menu in its
1969
 *      current mode.
1970
 *
1971
 *----------------------------------------------------------------------
1972
 */
1973
 
1974
void
1975
DrawTearoffEntry(menuPtr, mePtr, d, gc, tkfont, fmPtr, x, y, width, height)
1976
    TkMenu *menuPtr;                    /* The menu we are drawing */
1977
    TkMenuEntry *mePtr;                 /* The entry we are drawing */
1978
    Drawable d;                         /* The drawable we are drawing into */
1979
    GC gc;                              /* The gc we are drawing with */
1980
    Tk_Font tkfont;                     /* The font we are drawing with */
1981
    CONST Tk_FontMetrics *fmPtr;        /* The metrics we are drawing with */
1982
    int x;
1983
    int y;
1984
    int width;
1985
    int height;
1986
{
1987
    XPoint points[2];
1988
    int segmentWidth, maxX;
1989
 
1990
    if (menuPtr->menuType != MASTER_MENU) {
1991
        return;
1992
    }
1993
 
1994
    points[0].x = x;
1995
    points[0].y = y + height/2;
1996
    points[1].y = points[0].y;
1997
    segmentWidth = 6;
1998
    maxX  = width - 1;
1999
 
2000
    while (points[0].x < maxX) {
2001
        points[1].x = points[0].x + segmentWidth;
2002
        if (points[1].x > maxX) {
2003
            points[1].x = maxX;
2004
        }
2005
        Tk_Draw3DPolygon(menuPtr->tkwin, d, menuPtr->border, points, 2, 1,
2006
                TK_RELIEF_RAISED);
2007
        points[0].x += 2*segmentWidth;
2008
    }
2009
}
2010
 
2011
/*
2012
 *----------------------------------------------------------------------
2013
 *
2014
 * TkpConfigureMenuEntry --
2015
 *
2016
 *      Processes configurations for menu entries.
2017
 *
2018
 * Results:
2019
 *      Returns standard TCL result. If TCL_ERROR is returned, then
2020
 *      interp->result contains an error message.
2021
 *
2022
 * Side effects:
2023
 *      Configuration information get set for mePtr; old resources
2024
 *      get freed, if any need it.
2025
 *
2026
 *----------------------------------------------------------------------
2027
 */
2028
 
2029
int
2030
TkpConfigureMenuEntry(mePtr)
2031
    register TkMenuEntry *mePtr;        /* Information about menu entry;  may
2032
                                         * or may not already have values for
2033
                                         * some fields. */
2034
{
2035
    TkMenu *menuPtr = mePtr->menuPtr;
2036
 
2037
    if (!(menuPtr->menuFlags & MENU_RECONFIGURE_PENDING)) {
2038
        menuPtr->menuFlags |= MENU_RECONFIGURE_PENDING;
2039
        Tcl_DoWhenIdle(ReconfigureWindowsMenu, (ClientData) menuPtr);
2040
    }
2041
    return TCL_OK;
2042
}
2043
 
2044
/*
2045
 *----------------------------------------------------------------------
2046
 *
2047
 * TkpDrawMenuEntry --
2048
 *
2049
 *      Draws the given menu entry at the given coordinates with the
2050
 *      given attributes.
2051
 *
2052
 * Results:
2053
 *      None.
2054
 *
2055
 * Side effects:
2056
 *      X Server commands are executed to display the menu entry.
2057
 *
2058
 *----------------------------------------------------------------------
2059
 */
2060
 
2061
void
2062
TkpDrawMenuEntry(mePtr, d, tkfont, menuMetricsPtr, x, y, width, height,
2063
        strictMotif, drawArrow)
2064
    TkMenuEntry *mePtr;             /* The entry to draw */
2065
    Drawable d;                     /* What to draw into */
2066
    Tk_Font tkfont;                 /* Precalculated font for menu */
2067
    CONST Tk_FontMetrics *menuMetricsPtr;
2068
                                    /* Precalculated metrics for menu */
2069
    int x;                          /* X-coordinate of topleft of entry */
2070
    int y;                          /* Y-coordinate of topleft of entry */
2071
    int width;                      /* Width of the entry rectangle */
2072
    int height;                     /* Height of the current rectangle */
2073
    int strictMotif;                /* Boolean flag */
2074
    int drawArrow;                  /* Whether or not to draw the cascade
2075
                                     * arrow for cascade items. Only applies
2076
                                     * to Windows. */
2077
{
2078
    GC gc, indicatorGC;
2079
    TkMenu *menuPtr = mePtr->menuPtr;
2080
    Tk_3DBorder bgBorder, activeBorder;
2081
    CONST Tk_FontMetrics *fmPtr;
2082
    Tk_FontMetrics entryMetrics;
2083
    int padY = (menuPtr->menuType == MENUBAR) ? 3 : 0;
2084
    int adjustedY = y + padY;
2085
    int adjustedHeight = height - 2 * padY;
2086
 
2087
    /*
2088
     * Choose the gc for drawing the foreground part of the entry.
2089
     */
2090
 
2091
    if ((mePtr->state == tkActiveUid)
2092
            && !strictMotif) {
2093
        gc = mePtr->activeGC;
2094
        if (gc == NULL) {
2095
            gc = menuPtr->activeGC;
2096
        }
2097
    } else {
2098
        TkMenuEntry *cascadeEntryPtr;
2099
        int parentDisabled = 0;
2100
 
2101
        for (cascadeEntryPtr = menuPtr->menuRefPtr->parentEntryPtr;
2102
                cascadeEntryPtr != NULL;
2103
                cascadeEntryPtr = cascadeEntryPtr->nextCascadePtr) {
2104
            if (strcmp(cascadeEntryPtr->name,
2105
                    Tk_PathName(menuPtr->tkwin)) == 0) {
2106
                if (cascadeEntryPtr->state == tkDisabledUid) {
2107
                    parentDisabled = 1;
2108
                }
2109
                break;
2110
            }
2111
        }
2112
 
2113
        if (((parentDisabled || (mePtr->state == tkDisabledUid)))
2114
                && (menuPtr->disabledFg != NULL)) {
2115
            gc = mePtr->disabledGC;
2116
            if (gc == NULL) {
2117
                gc = menuPtr->disabledGC;
2118
            }
2119
        } else {
2120
            gc = mePtr->textGC;
2121
            if (gc == NULL) {
2122
                gc = menuPtr->textGC;
2123
            }
2124
        }
2125
    }
2126
    indicatorGC = mePtr->indicatorGC;
2127
    if (indicatorGC == NULL) {
2128
        indicatorGC = menuPtr->indicatorGC;
2129
    }
2130
 
2131
    bgBorder = mePtr->border;
2132
    if (bgBorder == NULL) {
2133
        bgBorder = menuPtr->border;
2134
    }
2135
    if (strictMotif) {
2136
        activeBorder = bgBorder;
2137
    } else {
2138
        activeBorder = mePtr->activeBorder;
2139
        if (activeBorder == NULL) {
2140
            activeBorder = menuPtr->activeBorder;
2141
        }
2142
    }
2143
 
2144
    if (mePtr->tkfont == NULL) {
2145
        fmPtr = menuMetricsPtr;
2146
    } else {
2147
        tkfont = mePtr->tkfont;
2148
        Tk_GetFontMetrics(tkfont, &entryMetrics);
2149
        fmPtr = &entryMetrics;
2150
    }
2151
 
2152
    /*
2153
     * Need to draw the entire background, including padding. On Unix,
2154
     * for menubars, we have to draw the rest of the entry taking
2155
     * into account the padding.
2156
     */
2157
 
2158
    DrawMenuEntryBackground(menuPtr, mePtr, d, activeBorder,
2159
            bgBorder, x, y, width, height);
2160
 
2161
    if (mePtr->type == SEPARATOR_ENTRY) {
2162
        DrawMenuSeparator(menuPtr, mePtr, d, gc, tkfont,
2163
                fmPtr, x, adjustedY, width, adjustedHeight);
2164
    } else if (mePtr->type == TEAROFF_ENTRY) {
2165
        DrawTearoffEntry(menuPtr, mePtr, d, gc, tkfont, fmPtr, x, adjustedY,
2166
                width, adjustedHeight);
2167
    } else {
2168
        DrawMenuEntryLabel(menuPtr, mePtr, d, gc, tkfont, fmPtr, x, adjustedY,
2169
                width, adjustedHeight);
2170
        DrawMenuEntryAccelerator(menuPtr, mePtr, d, gc, tkfont, fmPtr,
2171
                activeBorder, x, adjustedY, width, adjustedHeight, drawArrow);
2172
        if (!mePtr->hideMargin) {
2173
            DrawMenuEntryIndicator(menuPtr, mePtr, d, gc, indicatorGC, tkfont,
2174
                    fmPtr, x, adjustedY, width, adjustedHeight);
2175
        }
2176
    }
2177
}
2178
 
2179
/*
2180
 *----------------------------------------------------------------------
2181
 *
2182
 * GetMenuLabelGeometry --
2183
 *
2184
 *      Figures out the size of the label portion of a menu item.
2185
 *
2186
 * Results:
2187
 *      widthPtr and heightPtr are filled in with the correct geometry
2188
 *      information.
2189
 *
2190
 * Side effects:
2191
 *      None.
2192
 *
2193
 *----------------------------------------------------------------------
2194
 */
2195
 
2196
static void
2197
GetMenuLabelGeometry(mePtr, tkfont, fmPtr, widthPtr, heightPtr)
2198
    TkMenuEntry *mePtr;                 /* The entry we are computing */
2199
    Tk_Font tkfont;                     /* The precalculated font */
2200
    CONST Tk_FontMetrics *fmPtr;        /* The precalculated metrics */
2201
    int *widthPtr;                      /* The resulting width of the label
2202
                                         * portion */
2203
    int *heightPtr;                     /* The resulting height of the label
2204
                                         * portion */
2205
{
2206
    TkMenu *menuPtr = mePtr->menuPtr;
2207
 
2208
    if (mePtr->image != NULL) {
2209
        Tk_SizeOfImage(mePtr->image, widthPtr, heightPtr);
2210
    } else if (mePtr->bitmap != (Pixmap) NULL) {
2211
        Tk_SizeOfBitmap(menuPtr->display, mePtr->bitmap, widthPtr, heightPtr);
2212
    } else {
2213
        *heightPtr = fmPtr->linespace;
2214
 
2215
        if (mePtr->label != NULL) {
2216
            *widthPtr = Tk_TextWidth(tkfont, mePtr->label, mePtr->labelLength);
2217
        } else {
2218
            *widthPtr = 0;
2219
        }
2220
    }
2221
    *heightPtr += 1;
2222
}
2223
 
2224
/*
2225
 *----------------------------------------------------------------------
2226
 *
2227
 * DrawMenuEntryBackground --
2228
 *
2229
 *      This procedure draws the background part of a menu.
2230
 *
2231
 * Results:
2232
 *      None.
2233
 *
2234
 * Side effects:
2235
 *      Commands are output to X to display the menu in its
2236
 *      current mode.
2237
 *
2238
 *----------------------------------------------------------------------
2239
 */
2240
 
2241
static void
2242
DrawMenuEntryBackground(
2243
    TkMenu *menuPtr,                    /* The menu we are drawing. */
2244
    TkMenuEntry *mePtr,                 /* The entry we are drawing. */
2245
    Drawable d,                         /* What we are drawing into */
2246
    Tk_3DBorder activeBorder,           /* Border for active items */
2247
    Tk_3DBorder bgBorder,               /* Border for the background */
2248
    int x,                              /* left edge */
2249
    int y,                              /* top edge */
2250
    int width,                          /* width of rectangle to draw */
2251
    int height)                         /* height of rectangle to draw */
2252
{
2253
    if (mePtr->state == tkActiveUid) {
2254
        bgBorder = activeBorder;
2255
    }
2256
    Tk_Fill3DRectangle(menuPtr->tkwin, d, bgBorder,
2257
            x, y, width, height, 0, TK_RELIEF_FLAT);
2258
}
2259
 
2260
/*
2261
 *--------------------------------------------------------------
2262
 *
2263
 * TkpComputeStandardMenuGeometry --
2264
 *
2265
 *      This procedure is invoked to recompute the size and
2266
 *      layout of a menu that is not a menubar clone.
2267
 *
2268
 * Results:
2269
 *      None.
2270
 *
2271
 * Side effects:
2272
 *      Fields of menu entries are changed to reflect their
2273
 *      current positions, and the size of the menu window
2274
 *      itself may be changed.
2275
 *
2276
 *--------------------------------------------------------------
2277
 */
2278
 
2279
void
2280
TkpComputeStandardMenuGeometry(
2281
    TkMenu *menuPtr)            /* Structure describing menu. */
2282
{
2283
    Tk_Font tkfont;
2284
    Tk_FontMetrics menuMetrics, entryMetrics, *fmPtr;
2285
    int x, y, height, width, indicatorSpace, labelWidth, accelWidth;
2286
    int windowWidth, windowHeight, accelSpace;
2287
    int i, j, lastColumnBreak = 0;
2288
 
2289
    if (menuPtr->tkwin == NULL) {
2290
        return;
2291
    }
2292
 
2293
    x = y = menuPtr->borderWidth;
2294
    indicatorSpace = labelWidth = accelWidth = 0;
2295
    windowHeight = 0;
2296
 
2297
    /*
2298
     * On the Mac especially, getting font metrics can be quite slow,
2299
     * so we want to do it intelligently. We are going to precalculate
2300
     * them and pass them down to all of the measuring and drawing
2301
     * routines. We will measure the font metrics of the menu once.
2302
     * If an entry does not have its own font set, then we give
2303
     * the geometry/drawing routines the menu's font and metrics.
2304
     * If an entry has its own font, we will measure that font and
2305
     * give all of the geometry/drawing the entry's font and metrics.
2306
     */
2307
 
2308
    Tk_GetFontMetrics(menuPtr->tkfont, &menuMetrics);
2309
    accelSpace = Tk_TextWidth(menuPtr->tkfont, "M", 1);
2310
 
2311
    for (i = 0; i < menuPtr->numEntries; i++) {
2312
        tkfont = menuPtr->entries[i]->tkfont;
2313
        if (tkfont == NULL) {
2314
            tkfont = menuPtr->tkfont;
2315
            fmPtr = &menuMetrics;
2316
        } else {
2317
            Tk_GetFontMetrics(tkfont, &entryMetrics);
2318
            fmPtr = &entryMetrics;
2319
        }
2320
 
2321
        if ((i > 0) && menuPtr->entries[i]->columnBreak) {
2322
            if (accelWidth != 0) {
2323
                labelWidth += accelSpace;
2324
            }
2325
            for (j = lastColumnBreak; j < i; j++) {
2326
                menuPtr->entries[j]->indicatorSpace = indicatorSpace;
2327
                menuPtr->entries[j]->labelWidth = labelWidth;
2328
                menuPtr->entries[j]->width = indicatorSpace + labelWidth
2329
                        + accelWidth + 2 * menuPtr->activeBorderWidth;
2330
                menuPtr->entries[j]->x = x;
2331
                menuPtr->entries[j]->entryFlags &= ~ENTRY_LAST_COLUMN;
2332
            }
2333
            x += indicatorSpace + labelWidth + accelWidth
2334
                    + 2 * menuPtr->borderWidth;
2335
            indicatorSpace = labelWidth = accelWidth = 0;
2336
            lastColumnBreak = i;
2337
            y = menuPtr->borderWidth;
2338
        }
2339
 
2340
        if (menuPtr->entries[i]->type == SEPARATOR_ENTRY) {
2341
            GetMenuSeparatorGeometry(menuPtr, menuPtr->entries[i], tkfont,
2342
                    fmPtr, &width, &height);
2343
            menuPtr->entries[i]->height = height;
2344
        } else if (menuPtr->entries[i]->type == TEAROFF_ENTRY) {
2345
            GetTearoffEntryGeometry(menuPtr, menuPtr->entries[i], tkfont,
2346
                    fmPtr, &width, &height);
2347
            menuPtr->entries[i]->height = height;
2348
        } else {
2349
 
2350
            /*
2351
             * For each entry, compute the height required by that
2352
             * particular entry, plus three widths:  the width of the
2353
             * label, the width to allow for an indicator to be displayed
2354
             * to the left of the label (if any), and the width of the
2355
             * accelerator to be displayed to the right of the label
2356
             * (if any).  These sizes depend, of course, on the type
2357
             * of the entry.
2358
             */
2359
 
2360
            GetMenuLabelGeometry(menuPtr->entries[i], tkfont, fmPtr, &width,
2361
                    &height);
2362
            menuPtr->entries[i]->height = height;
2363
            if (width > labelWidth) {
2364
                labelWidth = width;
2365
            }
2366
 
2367
            GetMenuAccelGeometry(menuPtr, menuPtr->entries[i], tkfont,
2368
                    fmPtr, &width, &height);
2369
            if (height > menuPtr->entries[i]->height) {
2370
                menuPtr->entries[i]->height = height;
2371
            }
2372
            if (width > accelWidth) {
2373
                accelWidth = width;
2374
            }
2375
 
2376
            GetMenuIndicatorGeometry(menuPtr, menuPtr->entries[i], tkfont,
2377
                    fmPtr, &width, &height);
2378
            if (height > menuPtr->entries[i]->height) {
2379
                menuPtr->entries[i]->height = height;
2380
            }
2381
            if (width > indicatorSpace) {
2382
                indicatorSpace = width;
2383
            }
2384
 
2385
            menuPtr->entries[i]->height += 2 * menuPtr->activeBorderWidth + 1;
2386
        }
2387
        menuPtr->entries[i]->y = y;
2388
        y += menuPtr->entries[i]->height;
2389
        if (y > windowHeight) {
2390
            windowHeight = y;
2391
        }
2392
    }
2393
 
2394
    if (accelWidth != 0) {
2395
        labelWidth += accelSpace;
2396
    }
2397
    for (j = lastColumnBreak; j < menuPtr->numEntries; j++) {
2398
        menuPtr->entries[j]->indicatorSpace = indicatorSpace;
2399
        menuPtr->entries[j]->labelWidth = labelWidth;
2400
        menuPtr->entries[j]->width = indicatorSpace + labelWidth
2401
                + accelWidth + 2 * menuPtr->activeBorderWidth;
2402
        menuPtr->entries[j]->x = x;
2403
        menuPtr->entries[j]->entryFlags |= ENTRY_LAST_COLUMN;
2404
    }
2405
    windowWidth = x + indicatorSpace + labelWidth + accelWidth + accelSpace
2406
            + 2 * menuPtr->activeBorderWidth
2407
            + 2 * menuPtr->borderWidth;
2408
 
2409
 
2410
    windowHeight += menuPtr->borderWidth;
2411
 
2412
    /*
2413
     * The X server doesn't like zero dimensions, so round up to at least
2414
     * 1 (a zero-sized menu should never really occur, anyway).
2415
     */
2416
 
2417
    if (windowWidth <= 0) {
2418
        windowWidth = 1;
2419
    }
2420
    if (windowHeight <= 0) {
2421
        windowHeight = 1;
2422
    }
2423
    menuPtr->totalWidth = windowWidth;
2424
    menuPtr->totalHeight = windowHeight;
2425
}
2426
 
2427
/*
2428
 *----------------------------------------------------------------------
2429
 *
2430
 * MenuSelectEvent --
2431
 *
2432
 *      Generates a "MenuSelect" virtual event. This can be used to
2433
 *      do context-sensitive menu help.
2434
 *
2435
 * Results:
2436
 *      None.
2437
 *
2438
 * Side effects:
2439
 *      Places a virtual event on the event queue.
2440
 *
2441
 *----------------------------------------------------------------------
2442
 */
2443
 
2444
static void
2445
MenuSelectEvent(
2446
    TkMenu *menuPtr)            /* the menu we have selected. */
2447
{
2448
    XVirtualEvent event;
2449
    POINTS rootPoint;
2450
    DWORD msgPos;
2451
 
2452
    event.type = VirtualEvent;
2453
    event.serial = menuPtr->display->request;
2454
    event.send_event = 0;
2455
    event.display = menuPtr->display;
2456
    Tk_MakeWindowExist(menuPtr->tkwin);
2457
    event.event = Tk_WindowId(menuPtr->tkwin);
2458
    event.root = XRootWindow(menuPtr->display, 0);
2459
    event.subwindow = None;
2460
    event.time = TkpGetMS();
2461
 
2462
    msgPos = GetMessagePos();
2463
    rootPoint = MAKEPOINTS(msgPos);
2464
    event.x_root = rootPoint.x;
2465
    event.y_root = rootPoint.y;
2466
    event.state = TkWinGetModifierState();
2467
    event.same_screen = 1;
2468
    event.name = Tk_GetUid("MenuSelect");
2469
    Tk_QueueWindowEvent((XEvent *) &event, TCL_QUEUE_TAIL);
2470
}
2471
 
2472
/*
2473
 *----------------------------------------------------------------------
2474
 *
2475
 * TkpMenuNotifyToplevelCreate --
2476
 *
2477
 *      This routine reconfigures the menu and the clones indicated by
2478
 *      menuName becuase a toplevel has been created and any system
2479
 *      menus need to be created.
2480
 *
2481
 * Results:
2482
 *      None.
2483
 *
2484
 * Side effects:
2485
 *      An idle handler is set up to do the reconfiguration.
2486
 *
2487
 *----------------------------------------------------------------------
2488
 */
2489
 
2490
void
2491
TkpMenuNotifyToplevelCreate(
2492
    Tcl_Interp *interp,                 /* The interp the menu lives in. */
2493
    char *menuName)                     /* The name of the menu to
2494
                                         * reconfigure. */
2495
{
2496
    TkMenuReferences *menuRefPtr;
2497
    TkMenu *menuPtr;
2498
 
2499
    if ((menuName != NULL) && (menuName[0] != '\0')) {
2500
        menuRefPtr = TkFindMenuReferences(interp, menuName);
2501
        if ((menuRefPtr != NULL) && (menuRefPtr->menuPtr != NULL)) {
2502
            for (menuPtr = menuRefPtr->menuPtr->masterMenuPtr; menuPtr != NULL;
2503
                    menuPtr = menuPtr->nextInstancePtr) {
2504
                if ((menuPtr->menuType == MENUBAR)
2505
                        && !(menuPtr->menuFlags & MENU_RECONFIGURE_PENDING)) {
2506
                    menuPtr->menuFlags |= MENU_RECONFIGURE_PENDING;
2507
                    Tcl_DoWhenIdle(ReconfigureWindowsMenu,
2508
                            (ClientData) menuPtr);
2509
                }
2510
            }
2511
        }
2512
    }
2513
}
2514
 
2515
/*
2516
 *----------------------------------------------------------------------
2517
 *
2518
 * MenuExitHandler --
2519
 *
2520
 *      Throws away the utility window needed for menus and unregisters
2521
 *      the class.
2522
 *
2523
 * Results:
2524
 *      None.
2525
 *
2526
 * Side effects:
2527
 *      Menus have to be reinitialized next time.
2528
 *
2529
 *----------------------------------------------------------------------
2530
 */
2531
 
2532
static void
2533
MenuExitHandler(
2534
    ClientData clientData)          /* Not used */
2535
{
2536
    DestroyWindow(menuHWND);
2537
    UnregisterClass(MENU_CLASS_NAME, Tk_GetHINSTANCE());
2538
}
2539
 
2540
/*
2541
 *----------------------------------------------------------------------
2542
 *
2543
 * TkpMenuInit --
2544
 *
2545
 *      Sets up the hash tables and the variables used by the menu package.
2546
 *
2547
 * Results:
2548
 *      None.
2549
 *
2550
 * Side effects:
2551
 *      lastMenuID gets initialized, and the parent hash and the command hash
2552
 *      are allocated.
2553
 *
2554
 *----------------------------------------------------------------------
2555
 */
2556
 
2557
void
2558
TkpMenuInit()
2559
{
2560
    WNDCLASS wndClass;
2561
    char sizeString[4];
2562
    char faceName[LF_FACESIZE];
2563
    HDC scratchDC;
2564
    Tcl_DString boldItalicDString;
2565
    int bold = 0;
2566
    int italic = 0;
2567
    int i;
2568
    TEXTMETRIC tm;
2569
 
2570
    Tcl_InitHashTable(&winMenuTable, TCL_ONE_WORD_KEYS);
2571
    Tcl_InitHashTable(&commandTable, TCL_ONE_WORD_KEYS);
2572
 
2573
    wndClass.style = CS_OWNDC;
2574
    wndClass.lpfnWndProc = TkWinMenuProc;
2575
    wndClass.cbClsExtra = 0;
2576
    wndClass.cbWndExtra = 0;
2577
    wndClass.hInstance = Tk_GetHINSTANCE();
2578
    wndClass.hIcon = NULL;
2579
    wndClass.hCursor = NULL;
2580
    wndClass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
2581
    wndClass.lpszMenuName = NULL;
2582
    wndClass.lpszClassName = MENU_CLASS_NAME;
2583
    RegisterClass(&wndClass);
2584
 
2585
    menuHWND = CreateWindow(MENU_CLASS_NAME, "MenuWindow", WS_POPUP,
2586
        0, 0, 10, 10, NULL, NULL, Tk_GetHINSTANCE(), NULL);
2587
 
2588
    Tcl_CreateExitHandler(MenuExitHandler, (ClientData) NULL);
2589
 
2590
    versionInfo.dwOSVersionInfoSize = sizeof(versionInfo);
2591
 
2592
    /*
2593
     * If GetVersionEx fails, it means that the version info record
2594
     * is too big for what is compiled. Should never happen, but if
2595
     * it does, we are later than Windows 95 or NT 4.0.
2596
     */
2597
 
2598
    if (!GetVersionEx(&versionInfo)) {
2599
        versionInfo.dwMajorVersion = 4;
2600
    }
2601
 
2602
    /*
2603
     * Set all of the default options. The loop will terminate when we run
2604
     * out of options via a break statement.
2605
     */
2606
 
2607
    for (i = 0; ; i++) {
2608
        if (tkMenuConfigSpecs[i].type == TK_CONFIG_END) {
2609
            break;
2610
        }
2611
 
2612
        if ((strcmp(tkMenuConfigSpecs[i].dbName,
2613
                "activeBorderWidth") == 0) ||
2614
                (strcmp(tkMenuConfigSpecs[i].dbName, "borderWidth") == 0)) {
2615
            int borderWidth;
2616
 
2617
            borderWidth = GetSystemMetrics(SM_CXBORDER);
2618
            if (GetSystemMetrics(SM_CYBORDER) > borderWidth) {
2619
                borderWidth = GetSystemMetrics(SM_CYBORDER);
2620
            }
2621
            sprintf(borderString, "%d", borderWidth);
2622
            tkMenuConfigSpecs[i].defValue = borderString;
2623
        } else if ((strcmp(tkMenuConfigSpecs[i].dbName, "font") == 0)) {
2624
            int pointSize;
2625
            HFONT menuFont;
2626
 
2627
            scratchDC = CreateDC("DISPLAY", NULL, NULL, NULL);
2628
            Tcl_DStringInit(&menuFontDString);
2629
 
2630
            if (versionInfo.dwMajorVersion >= 4) {
2631
                NONCLIENTMETRICS ncMetrics;
2632
 
2633
                ncMetrics.cbSize = sizeof(ncMetrics);
2634
                SystemParametersInfo(SPI_GETNONCLIENTMETRICS, sizeof(ncMetrics),
2635
                        &ncMetrics, 0);
2636
                menuFont = CreateFontIndirect(&ncMetrics.lfMenuFont);
2637
            } else {
2638
                menuFont = GetStockObject(SYSTEM_FONT);
2639
            }
2640
            SelectObject(scratchDC, menuFont);
2641
            GetTextMetrics(scratchDC, &tm);
2642
            GetTextFace(scratchDC, sizeof(menuFontDString), faceName);
2643
            pointSize = MulDiv(tm.tmHeight - tm.tmInternalLeading,
2644
                    72, GetDeviceCaps(scratchDC, LOGPIXELSY));
2645
            if (tm.tmWeight >= 700) {
2646
                bold = 1;
2647
            }
2648
            if (tm.tmItalic) {
2649
                italic = 1;
2650
            }
2651
 
2652
            SelectObject(scratchDC, GetStockObject(SYSTEM_FONT));
2653
            DeleteDC(scratchDC);
2654
 
2655
            DeleteObject(menuFont);
2656
 
2657
            Tcl_DStringAppendElement(&menuFontDString, faceName);
2658
            sprintf(sizeString, "%d", pointSize);
2659
            Tcl_DStringAppendElement(&menuFontDString, sizeString);
2660
 
2661
            if (bold == 1 || italic == 1) {
2662
                Tcl_DStringInit(&boldItalicDString);
2663
                if (bold == 1) {
2664
                    Tcl_DStringAppendElement(&boldItalicDString, "bold");
2665
                }
2666
                if (italic == 1) {
2667
                    Tcl_DStringAppendElement(&boldItalicDString, "italic");
2668
                }
2669
                Tcl_DStringAppendElement(&menuFontDString,
2670
                        Tcl_DStringValue(&boldItalicDString));
2671
            }
2672
 
2673
            tkMenuConfigSpecs[i].defValue = Tcl_DStringValue(&menuFontDString);
2674
        }
2675
    }
2676
 
2677
    /*
2678
     * Now we go ahead and get the dimensions of the check mark and the
2679
     * appropriate margins. Since this is fairly hairy, we do it here
2680
     * to save time when traversing large sets of menu items.
2681
     *
2682
     * The code below was given to me by Microsoft over the phone. It
2683
     * is the only way to insure menu items lining up, and is not
2684
     * documented.
2685
     */
2686
 
2687
    if (versionInfo.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS) {
2688
        indicatorDimensions[0] = GetSystemMetrics(SM_CYMENUCHECK);
2689
        indicatorDimensions[1] = ((GetSystemMetrics(SM_CXFIXEDFRAME) +
2690
                GetSystemMetrics(SM_CXBORDER)
2691
                + GetSystemMetrics(SM_CXMENUCHECK) + 7) & 0xFFF8)
2692
                - GetSystemMetrics(SM_CXFIXEDFRAME);
2693
    } else {
2694
        DWORD dimensions = GetMenuCheckMarkDimensions();
2695
        indicatorDimensions[0] = HIWORD(dimensions);
2696
        indicatorDimensions[1] = LOWORD(dimensions);
2697
   }
2698
 
2699
}

powered by: WebSVN 2.1.0

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