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 GLUTmenuItem *__glutItemSelected;
23 unsigned __glutMenuButton;
25 static GLUTmenu **menuList = NULL;
26 static int menuListSize = 0;
27 static UINT uniqueMenuHandler = 1;
29 /* DEPRICATED, use glutMenuStatusFunc instead. */
31 glutMenuStateFunc(GLUTmenuStateCB menuStateFunc)
33 __glutMenuStatusFunc = (GLUTmenuStatusCB) menuStateFunc;
37 glutMenuStatusFunc(GLUTmenuStatusCB menuStatusFunc)
39 __glutMenuStatusFunc = menuStatusFunc;
43 __glutSetMenu(GLUTmenu * menu)
45 __glutCurrentMenu = menu;
49 unmapMenu(GLUTmenu * menu)
52 unmapMenu(menu->cascade);
56 menu->highlighted = NULL;
60 __glutFinishMenu(Window win, int x, int y)
63 unmapMenu(__glutMappedMenu);
65 /* XXX Put in a GdiFlush just in case. Probably unnecessary. -mjk */
68 if (__glutMenuStatusFunc) {
69 __glutSetWindow(__glutMenuWindow);
70 __glutSetMenu(__glutMappedMenu);
72 /* Setting __glutMappedMenu to NULL permits operations that
73 change menus or destroy the menu window again. */
74 __glutMappedMenu = NULL;
76 __glutMenuStatusFunc(GLUT_MENU_NOT_IN_USE, x, y);
78 /* Setting __glutMappedMenu to NULL permits operations that
79 change menus or destroy the menu window again. */
80 __glutMappedMenu = NULL;
82 /* If an item is selected and it is not a submenu trigger,
83 generate menu callback. */
84 if (__glutItemSelected && !__glutItemSelected->isTrigger) {
85 __glutSetWindow(__glutMenuWindow);
86 /* When menu callback is triggered, current menu should be
87 set to the callback menu. */
88 __glutSetMenu(__glutItemSelected->menu);
89 __glutItemSelected->menu->select(__glutItemSelected->value);
91 __glutMenuWindow = NULL;
95 mapMenu(GLUTmenu * menu, int x, int y)
97 TrackPopupMenu((HMENU) menu->win, TPM_LEFTALIGN |
98 ((__glutMenuButton == TPM_RIGHTBUTTON) ? TPM_RIGHTBUTTON : TPM_LEFTBUTTON),
99 x, y, 0, __glutCurrentWindow->win, NULL);
103 __glutStartMenu(GLUTmenu * menu, GLUTwindow * window,
104 int x, int y, int x_win, int y_win)
106 assert(__glutMappedMenu == NULL);
107 __glutMappedMenu = menu;
108 __glutMenuWindow = window;
109 __glutItemSelected = NULL;
110 if (__glutMenuStatusFunc) {
112 __glutSetWindow(window);
113 __glutMenuStatusFunc(GLUT_MENU_IN_USE, x_win, y_win);
119 __glutGetUniqueMenuItem(GLUTmenu * menu, UINT unique)
127 if (item->unique == unique) {
130 if (item->isTrigger) {
131 GLUTmenuItem *subitem;
132 subitem = __glutGetUniqueMenuItem(menuList[item->value], unique);
144 __glutGetMenuItem(GLUTmenu * menu, Window win, int *which)
152 if (item->win == win) {
156 if (item->isTrigger) {
157 GLUTmenuItem *subitem;
159 subitem = __glutGetMenuItem(menuList[item->value],
172 __glutGetMenu(Window win)
176 menu = __glutMappedMenu;
178 if (win == menu->win) {
181 menu = menu->cascade;
187 __glutGetMenuByNum(int menunum)
189 if (menunum < 1 || menunum > menuListSize) {
192 return menuList[menunum - 1];
196 getUnusedMenuSlot(void)
200 /* Look for allocated, unused slot. */
201 for (i = 0; i < menuListSize; i++) {
206 /* Allocate a new slot. */
209 menuList = (GLUTmenu **)
210 realloc(menuList, menuListSize * sizeof(GLUTmenu *));
212 /* XXX Some realloc's do not correctly perform a malloc
213 when asked to perform a realloc on a NULL pointer,
214 though the ANSI C library spec requires this. */
215 menuList = (GLUTmenu **) malloc(sizeof(GLUTmenu *));
218 __glutFatalError("out of memory.");
220 menuList[menuListSize - 1] = NULL;
221 return menuListSize - 1;
225 menuModificationError(void)
227 /* XXX Remove the warning after GLUT 3.0. */
228 __glutWarning("The following is a new check for GLUT 3.0; update your code.");
229 __glutFatalError("menu manipulation not allowed while menus in use.");
233 glutCreateMenu(GLUTselectCB selectFunc)
238 if (__glutMappedMenu) {
239 menuModificationError();
241 menuid = getUnusedMenuSlot();
242 menu = (GLUTmenu *) malloc(sizeof(GLUTmenu));
244 __glutFatalError("out of memory.");
249 menu->select = selectFunc;
251 menu->cascade = NULL;
252 menu->highlighted = NULL;
254 menu->win = (HWND) CreatePopupMenu();
255 menuList[menuid] = menu;
261 __glutCreateMenuWithExit(GLUTselectCB selectFunc, void (__cdecl *exitfunc)(int))
263 __glutExitFunc = exitfunc;
264 return glutCreateMenu(selectFunc);
268 glutDestroyMenu(int menunum)
270 GLUTmenu *menu = __glutGetMenuByNum(menunum);
271 GLUTmenuItem *item, *next;
273 if (__glutMappedMenu) {
274 menuModificationError();
276 assert(menu->id == menunum - 1);
277 DestroyMenu( (HMENU) menu->win);
278 menuList[menunum - 1] = NULL;
279 /* free all menu entries */
282 assert(item->menu == menu);
288 if (__glutCurrentMenu == menu) {
289 __glutCurrentMenu = NULL;
297 if (__glutCurrentMenu) {
298 return __glutCurrentMenu->id + 1;
305 glutSetMenu(int menuid)
309 if (menuid < 1 || menuid > menuListSize) {
310 __glutWarning("glutSetMenu attempted on bogus menu.");
313 menu = menuList[menuid - 1];
315 __glutWarning("glutSetMenu attempted on bogus menu.");
322 setMenuItem(GLUTmenuItem * item, const char *label,
323 int value, Bool isTrigger)
328 item->label = __glutStrdup(label);
330 __glutFatalError("out of memory.");
332 item->isTrigger = isTrigger;
333 item->len = (int) strlen(label);
335 item->unique = uniqueMenuHandler++;
337 AppendMenu((HMENU) menu->win, MF_POPUP, (UINT)item->win, label);
339 AppendMenu((HMENU) menu->win, MF_STRING, item->unique, label);
344 glutAddMenuEntry(const char *label, int value)
348 if (__glutMappedMenu) {
349 menuModificationError();
351 entry = (GLUTmenuItem *) malloc(sizeof(GLUTmenuItem));
353 __glutFatalError("out of memory.");
355 entry->menu = __glutCurrentMenu;
356 setMenuItem(entry, label, value, FALSE);
357 __glutCurrentMenu->num++;
358 entry->next = __glutCurrentMenu->list;
359 __glutCurrentMenu->list = entry;
363 glutAddSubMenu(const char *label, int menu)
365 GLUTmenuItem *submenu;
368 if (__glutMappedMenu) {
369 menuModificationError();
371 submenu = (GLUTmenuItem *) malloc(sizeof(GLUTmenuItem));
373 __glutFatalError("out of memory.");
375 __glutCurrentMenu->submenus++;
376 submenu->menu = __glutCurrentMenu;
377 popupmenu = __glutGetMenuByNum(menu);
379 submenu->win = popupmenu->win;
381 setMenuItem(submenu, label, /* base 0 */ menu - 1, TRUE);
382 __glutCurrentMenu->num++;
383 submenu->next = __glutCurrentMenu->list;
384 __glutCurrentMenu->list = submenu;
388 glutChangeToMenuEntry(int num, const char *label, int value)
393 if (__glutMappedMenu) {
394 menuModificationError();
396 i = __glutCurrentMenu->num;
397 item = __glutCurrentMenu->list;
400 if (item->isTrigger) {
401 /* If changing a submenu trigger to a menu entry, we
402 need to account for submenus. */
403 item->menu->submenus--;
404 /* Nuke the Win32 menu. */
405 DestroyMenu((HMENU) item->win);
409 item->label = strdup(label);
411 __glutFatalError("out of memory");
412 item->isTrigger = FALSE;
413 item->len = (int) strlen(label);
415 item->unique = uniqueMenuHandler++;
416 ModifyMenu((HMENU) __glutCurrentMenu->win, (UINT) i - 1,
417 MF_BYPOSITION | MFT_STRING, item->unique, label);
424 __glutWarning("Current menu has no %d item.", num);
428 glutChangeToSubMenu(int num, const char *label, int menu)
434 if (__glutMappedMenu) {
435 menuModificationError();
437 i = __glutCurrentMenu->num;
438 item = __glutCurrentMenu->list;
441 if (!item->isTrigger) {
442 /* If changing a menu entry to as submenu trigger, we
443 need to account for submenus. */
444 item->menu->submenus++;
445 item->win = (HWND) CreatePopupMenu();
449 item->label = strdup(label);
451 __glutFatalError("out of memory");
452 item->isTrigger = TRUE;
453 item->len = (int) strlen(label);
454 item->value = menu - 1;
455 item->unique = uniqueMenuHandler++;
456 popupmenu = __glutGetMenuByNum(menu);
458 item->win = popupmenu->win;
459 ModifyMenu((HMENU) __glutCurrentMenu->win, (UINT) i - 1,
460 MF_BYPOSITION | MF_POPUP, (UINT) item->win, label);
466 __glutWarning("Current menu has no %d item.", num);
470 glutRemoveMenuItem(int num)
472 GLUTmenuItem *item, **prev;
475 if (__glutMappedMenu) {
476 menuModificationError();
478 i = __glutCurrentMenu->num;
479 prev = &__glutCurrentMenu->list;
480 item = __glutCurrentMenu->list;
483 /* Found the menu item in list to remove. */
484 __glutCurrentMenu->num--;
486 /* Patch up menu's item list. */
489 RemoveMenu((HMENU) __glutCurrentMenu->win, (UINT) i - 1, MF_BYPOSITION);
499 __glutWarning("Current menu has no %d item.", num);
503 glutAttachMenu(int button)
505 if (__glutCurrentWindow == __glutGameModeWindow) {
506 __glutWarning("cannot attach menus in game mode.");
509 if (__glutMappedMenu) {
510 menuModificationError();
512 if (__glutCurrentWindow->menu[button] < 1) {
513 __glutCurrentWindow->buttonUses++;
515 __glutCurrentWindow->menu[button] = __glutCurrentMenu->id + 1;
519 glutDetachMenu(int button)
521 if (__glutMappedMenu) {
522 menuModificationError();
524 if (__glutCurrentWindow->menu[button] > 0) {
525 __glutCurrentWindow->buttonUses--;
526 __glutCurrentWindow->menu[button] = 0;