2 /* Copyright (c) Mark J. Kilgard, 1994, 1997, 1998. */
3 /* Copyright (c) Nate Robins, 1997. */
5 /* This program is freely distributable without licensing fees
6 and is provided without guarantee or warrantee expressed or
7 implied. This program is -not- in the public domain. */
9 /* This file completely re-implements glut_menu.c and glut_menu2.c
10 for Win32. Note that neither glut_menu.c nor glut_menu2.c are
11 compiled into Win32 GLUT. */
21 void (GLUTCALLBACK
*__glutMenuStatusFunc
) (int, int, int);
22 GLUTmenu
*__glutMappedMenu
;
23 GLUTwindow
*__glutMenuWindow
;
24 GLUTmenuItem
*__glutItemSelected
;
25 unsigned __glutMenuButton
;
27 static GLUTmenu
**menuList
= NULL
;
28 static int menuListSize
= 0;
29 static UINT uniqueMenuHandler
= 1;
31 /* DEPRICATED, use glutMenuStatusFunc instead. */
33 glutMenuStateFunc(GLUTmenuStateCB menuStateFunc
)
35 __glutMenuStatusFunc
= (GLUTmenuStatusCB
) menuStateFunc
;
39 glutMenuStatusFunc(GLUTmenuStatusCB menuStatusFunc
)
41 __glutMenuStatusFunc
= menuStatusFunc
;
45 __glutSetMenu(GLUTmenu
* menu
)
47 __glutCurrentMenu
= menu
;
51 unmapMenu(GLUTmenu
* menu
)
54 unmapMenu(menu
->cascade
);
58 menu
->highlighted
= NULL
;
62 __glutFinishMenu(Window win
, int x
, int y
)
65 unmapMenu(__glutMappedMenu
);
67 /* XXX Put in a GdiFlush just in case. Probably unnecessary. -mjk */
70 if (__glutMenuStatusFunc
) {
71 __glutSetWindow(__glutMenuWindow
);
72 __glutSetMenu(__glutMappedMenu
);
74 /* Setting __glutMappedMenu to NULL permits operations that
75 change menus or destroy the menu window again. */
76 __glutMappedMenu
= NULL
;
78 __glutMenuStatusFunc(GLUT_MENU_NOT_IN_USE
, x
, y
);
80 /* Setting __glutMappedMenu to NULL permits operations that
81 change menus or destroy the menu window again. */
82 __glutMappedMenu
= NULL
;
84 /* If an item is selected and it is not a submenu trigger,
85 generate menu callback. */
86 if (__glutItemSelected
&& !__glutItemSelected
->isTrigger
) {
87 __glutSetWindow(__glutMenuWindow
);
88 /* When menu callback is triggered, current menu should be
89 set to the callback menu. */
90 __glutSetMenu(__glutItemSelected
->menu
);
91 __glutItemSelected
->menu
->select(__glutItemSelected
->value
);
93 __glutMenuWindow
= NULL
;
97 mapMenu(GLUTmenu
* menu
, int x
, int y
)
99 TrackPopupMenu((HMENU
) menu
->win
, TPM_LEFTALIGN
|
100 ((__glutMenuButton
== TPM_RIGHTBUTTON
) ? TPM_RIGHTBUTTON
: TPM_LEFTBUTTON
),
101 x
, y
, 0, __glutCurrentWindow
->win
, NULL
);
105 __glutStartMenu(GLUTmenu
* menu
, GLUTwindow
* window
,
106 int x
, int y
, int x_win
, int y_win
)
108 assert(__glutMappedMenu
== NULL
);
109 __glutMappedMenu
= menu
;
110 __glutMenuWindow
= window
;
111 __glutItemSelected
= NULL
;
112 if (__glutMenuStatusFunc
) {
114 __glutSetWindow(window
);
115 __glutMenuStatusFunc(GLUT_MENU_IN_USE
, x_win
, y_win
);
121 __glutGetUniqueMenuItem(GLUTmenu
* menu
, UINT unique
)
129 if (item
->unique
== unique
) {
132 if (item
->isTrigger
) {
133 GLUTmenuItem
*subitem
;
134 subitem
= __glutGetUniqueMenuItem(menuList
[item
->value
], unique
);
146 __glutGetMenuItem(GLUTmenu
* menu
, Window win
, int *which
)
154 if (item
->win
== win
) {
158 if (item
->isTrigger
) {
159 GLUTmenuItem
*subitem
;
161 subitem
= __glutGetMenuItem(menuList
[item
->value
],
174 __glutGetMenu(Window win
)
178 menu
= __glutMappedMenu
;
180 if (win
== menu
->win
) {
183 menu
= menu
->cascade
;
189 __glutGetMenuByNum(int menunum
)
191 if (menunum
< 1 || menunum
> menuListSize
) {
194 return menuList
[menunum
- 1];
198 getUnusedMenuSlot(void)
202 /* Look for allocated, unused slot. */
203 for (i
= 0; i
< menuListSize
; i
++) {
208 /* Allocate a new slot. */
211 menuList
= (GLUTmenu
**)
212 realloc(menuList
, menuListSize
* sizeof(GLUTmenu
*));
214 /* XXX Some realloc's do not correctly perform a malloc
215 when asked to perform a realloc on a NULL pointer,
216 though the ANSI C library spec requires this. */
217 menuList
= (GLUTmenu
**) malloc(sizeof(GLUTmenu
*));
220 __glutFatalError("out of memory.");
222 menuList
[menuListSize
- 1] = NULL
;
223 return menuListSize
- 1;
227 menuModificationError(void)
229 /* XXX Remove the warning after GLUT 3.0. */
230 __glutWarning("The following is a new check for GLUT 3.0; update your code.");
231 __glutFatalError("menu manipulation not allowed while menus in use.");
235 glutCreateMenu(GLUTselectCB selectFunc
)
240 if (__glutMappedMenu
) {
241 menuModificationError();
243 menuid
= getUnusedMenuSlot();
244 menu
= (GLUTmenu
*) malloc(sizeof(GLUTmenu
));
246 __glutFatalError("out of memory.");
251 menu
->select
= selectFunc
;
253 menu
->cascade
= NULL
;
254 menu
->highlighted
= NULL
;
256 menu
->win
= (HWND
) CreatePopupMenu();
257 menuList
[menuid
] = menu
;
263 __glutCreateMenuWithExit(GLUTselectCB selectFunc
, void (__cdecl
*exitfunc
)(int))
265 __glutExitFunc
= exitfunc
;
266 return glutCreateMenu(selectFunc
);
270 glutDestroyMenu(int menunum
)
272 GLUTmenu
*menu
= __glutGetMenuByNum(menunum
);
273 GLUTmenuItem
*item
, *next
;
275 if (__glutMappedMenu
) {
276 menuModificationError();
278 assert(menu
->id
== menunum
- 1);
279 DestroyMenu( (HMENU
) menu
->win
);
280 menuList
[menunum
- 1] = NULL
;
281 /* free all menu entries */
284 assert(item
->menu
== menu
);
290 if (__glutCurrentMenu
== menu
) {
291 __glutCurrentMenu
= NULL
;
299 if (__glutCurrentMenu
) {
300 return __glutCurrentMenu
->id
+ 1;
307 glutSetMenu(int menuid
)
311 if (menuid
< 1 || menuid
> menuListSize
) {
312 __glutWarning("glutSetMenu attempted on bogus menu.");
315 menu
= menuList
[menuid
- 1];
317 __glutWarning("glutSetMenu attempted on bogus menu.");
324 setMenuItem(GLUTmenuItem
* item
, const char *label
,
325 int value
, Bool isTrigger
)
330 item
->label
= __glutStrdup(label
);
332 __glutFatalError("out of memory.");
334 item
->isTrigger
= isTrigger
;
335 item
->len
= (int) strlen(label
);
337 item
->unique
= uniqueMenuHandler
++;
339 AppendMenu((HMENU
) menu
->win
, MF_POPUP
, (UINT
)item
->win
, label
);
341 AppendMenu((HMENU
) menu
->win
, MF_STRING
, item
->unique
, label
);
346 glutAddMenuEntry(const char *label
, int value
)
350 if (__glutMappedMenu
) {
351 menuModificationError();
353 entry
= (GLUTmenuItem
*) malloc(sizeof(GLUTmenuItem
));
355 __glutFatalError("out of memory.");
357 entry
->menu
= __glutCurrentMenu
;
358 setMenuItem(entry
, label
, value
, FALSE
);
359 __glutCurrentMenu
->num
++;
360 entry
->next
= __glutCurrentMenu
->list
;
361 __glutCurrentMenu
->list
= entry
;
365 glutAddSubMenu(const char *label
, int menu
)
367 GLUTmenuItem
*submenu
;
370 if (__glutMappedMenu
) {
371 menuModificationError();
373 submenu
= (GLUTmenuItem
*) malloc(sizeof(GLUTmenuItem
));
375 __glutFatalError("out of memory.");
377 __glutCurrentMenu
->submenus
++;
378 submenu
->menu
= __glutCurrentMenu
;
379 popupmenu
= __glutGetMenuByNum(menu
);
381 submenu
->win
= popupmenu
->win
;
383 setMenuItem(submenu
, label
, /* base 0 */ menu
- 1, TRUE
);
384 __glutCurrentMenu
->num
++;
385 submenu
->next
= __glutCurrentMenu
->list
;
386 __glutCurrentMenu
->list
= submenu
;
390 glutChangeToMenuEntry(int num
, const char *label
, int value
)
395 if (__glutMappedMenu
) {
396 menuModificationError();
398 i
= __glutCurrentMenu
->num
;
399 item
= __glutCurrentMenu
->list
;
402 if (item
->isTrigger
) {
403 /* If changing a submenu trigger to a menu entry, we
404 need to account for submenus. */
405 item
->menu
->submenus
--;
406 /* Nuke the Win32 menu. */
407 DestroyMenu((HMENU
) item
->win
);
411 item
->label
= strdup(label
);
413 __glutFatalError("out of memory");
414 item
->isTrigger
= FALSE
;
415 item
->len
= (int) strlen(label
);
417 item
->unique
= uniqueMenuHandler
++;
418 ModifyMenu((HMENU
) __glutCurrentMenu
->win
, (UINT
) i
- 1,
419 MF_BYPOSITION
| MFT_STRING
, item
->unique
, label
);
426 __glutWarning("Current menu has no %d item.", num
);
430 glutChangeToSubMenu(int num
, const char *label
, int menu
)
436 if (__glutMappedMenu
) {
437 menuModificationError();
439 i
= __glutCurrentMenu
->num
;
440 item
= __glutCurrentMenu
->list
;
443 if (!item
->isTrigger
) {
444 /* If changing a menu entry to as submenu trigger, we
445 need to account for submenus. */
446 item
->menu
->submenus
++;
447 item
->win
= (HWND
) CreatePopupMenu();
451 item
->label
= strdup(label
);
453 __glutFatalError("out of memory");
454 item
->isTrigger
= TRUE
;
455 item
->len
= (int) strlen(label
);
456 item
->value
= menu
- 1;
457 item
->unique
= uniqueMenuHandler
++;
458 popupmenu
= __glutGetMenuByNum(menu
);
460 item
->win
= popupmenu
->win
;
461 ModifyMenu((HMENU
) __glutCurrentMenu
->win
, (UINT
) i
- 1,
462 MF_BYPOSITION
| MF_POPUP
, (UINT
) item
->win
, label
);
468 __glutWarning("Current menu has no %d item.", num
);
472 glutRemoveMenuItem(int num
)
474 GLUTmenuItem
*item
, **prev
;
477 if (__glutMappedMenu
) {
478 menuModificationError();
480 i
= __glutCurrentMenu
->num
;
481 prev
= &__glutCurrentMenu
->list
;
482 item
= __glutCurrentMenu
->list
;
485 /* Found the menu item in list to remove. */
486 __glutCurrentMenu
->num
--;
488 /* Patch up menu's item list. */
491 RemoveMenu((HMENU
) __glutCurrentMenu
->win
, (UINT
) i
- 1, MF_BYPOSITION
);
501 __glutWarning("Current menu has no %d item.", num
);
505 glutAttachMenu(int button
)
507 if (__glutCurrentWindow
== __glutGameModeWindow
) {
508 __glutWarning("cannot attach menus in game mode.");
511 if (__glutMappedMenu
) {
512 menuModificationError();
514 if (__glutCurrentWindow
->menu
[button
] < 1) {
515 __glutCurrentWindow
->buttonUses
++;
517 __glutCurrentWindow
->menu
[button
] = __glutCurrentMenu
->id
+ 1;
521 glutDetachMenu(int button
)
523 if (__glutMappedMenu
) {
524 menuModificationError();
526 if (__glutCurrentWindow
->menu
[button
] > 0) {
527 __glutCurrentWindow
->buttonUses
--;
528 __glutCurrentWindow
->menu
[button
] = 0;