1 2/* Copyright (c) Mark J. Kilgard, 1994, 1997, 1998. */ 3/* Copyright (c) Nate Robins, 1997. */ 4 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. */ 8 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. */ 12 13#include <stdlib.h> 14#include <string.h> 15#include <stdio.h> 16#include <errno.h> 17#include <assert.h> 18 19#include "glutint.h" 20 21void (GLUTCALLBACK *__glutMenuStatusFunc) (int, int, int); 22GLUTmenu *__glutMappedMenu; 23GLUTwindow *__glutMenuWindow; 24GLUTmenuItem *__glutItemSelected; 25unsigned __glutMenuButton; 26 27static GLUTmenu **menuList = NULL; 28static int menuListSize = 0; 29static UINT uniqueMenuHandler = 1; 30 31/* DEPRICATED, use glutMenuStatusFunc instead. */ 32void GLUTAPIENTRY 33glutMenuStateFunc(GLUTmenuStateCB menuStateFunc) 34{ 35 __glutMenuStatusFunc = (GLUTmenuStatusCB) menuStateFunc; 36} 37 38void GLUTAPIENTRY 39glutMenuStatusFunc(GLUTmenuStatusCB menuStatusFunc) 40{ 41 __glutMenuStatusFunc = menuStatusFunc; 42} 43 44void 45__glutSetMenu(GLUTmenu * menu) 46{ 47 __glutCurrentMenu = menu; 48} 49 50static void 51unmapMenu(GLUTmenu * menu) 52{ 53 if (menu->cascade) { 54 unmapMenu(menu->cascade); 55 menu->cascade = NULL; 56 } 57 menu->anchor = NULL; 58 menu->highlighted = NULL; 59} 60 61void 62__glutFinishMenu(Window win, int x, int y) 63{ 64 65 unmapMenu(__glutMappedMenu); 66 67 /* XXX Put in a GdiFlush just in case. Probably unnecessary. -mjk */ 68 GdiFlush(); 69 70 if (__glutMenuStatusFunc) { 71 __glutSetWindow(__glutMenuWindow); 72 __glutSetMenu(__glutMappedMenu); 73 74 /* Setting __glutMappedMenu to NULL permits operations that 75 change menus or destroy the menu window again. */ 76 __glutMappedMenu = NULL; 77 78 __glutMenuStatusFunc(GLUT_MENU_NOT_IN_USE, x, y); 79 } 80 /* Setting __glutMappedMenu to NULL permits operations that 81 change menus or destroy the menu window again. */ 82 __glutMappedMenu = NULL; 83 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); 92 } 93 __glutMenuWindow = NULL; 94} 95 96static void 97mapMenu(GLUTmenu * menu, int x, int y) 98{ 99 TrackPopupMenu((HMENU) menu->win, TPM_LEFTALIGN | 100 ((__glutMenuButton == TPM_RIGHTBUTTON) ? TPM_RIGHTBUTTON : TPM_LEFTBUTTON), 101 x, y, 0, __glutCurrentWindow->win, NULL); 102} 103 104void 105__glutStartMenu(GLUTmenu * menu, GLUTwindow * window, 106 int x, int y, int x_win, int y_win) 107{ 108 assert(__glutMappedMenu == NULL); 109 __glutMappedMenu = menu; 110 __glutMenuWindow = window; 111 __glutItemSelected = NULL; 112 if (__glutMenuStatusFunc) { 113 __glutSetMenu(menu); 114 __glutSetWindow(window); 115 __glutMenuStatusFunc(GLUT_MENU_IN_USE, x_win, y_win); 116 } 117 mapMenu(menu, x, y); 118} 119 120GLUTmenuItem * 121__glutGetUniqueMenuItem(GLUTmenu * menu, UINT unique) 122{ 123 GLUTmenuItem *item; 124 int i; 125 126 i = menu->num; 127 item = menu->list; 128 while (item) { 129 if (item->unique == unique) { 130 return item; 131 } 132 if (item->isTrigger) { 133 GLUTmenuItem *subitem; 134 subitem = __glutGetUniqueMenuItem(menuList[item->value], unique); 135 if (subitem) { 136 return subitem; 137 } 138 } 139 i--; 140 item = item->next; 141 } 142 return NULL; 143} 144 145GLUTmenuItem * 146__glutGetMenuItem(GLUTmenu * menu, Window win, int *which) 147{ 148 GLUTmenuItem *item; 149 int i; 150 151 i = menu->num; 152 item = menu->list; 153 while (item) { 154 if (item->win == win) { 155 *which = i; 156 return item; 157 } 158 if (item->isTrigger) { 159 GLUTmenuItem *subitem; 160 161 subitem = __glutGetMenuItem(menuList[item->value], 162 win, which); 163 if (subitem) { 164 return subitem; 165 } 166 } 167 i--; 168 item = item->next; 169 } 170 return NULL; 171} 172 173GLUTmenu * 174__glutGetMenu(Window win) 175{ 176 GLUTmenu *menu; 177 178 menu = __glutMappedMenu; 179 while (menu) { 180 if (win == menu->win) { 181 return menu; 182 } 183 menu = menu->cascade; 184 } 185 return NULL; 186} 187 188GLUTmenu * 189__glutGetMenuByNum(int menunum) 190{ 191 if (menunum < 1 || menunum > menuListSize) { 192 return NULL; 193 } 194 return menuList[menunum - 1]; 195} 196 197static int 198getUnusedMenuSlot(void) 199{ 200 int i; 201 202 /* Look for allocated, unused slot. */ 203 for (i = 0; i < menuListSize; i++) { 204 if (!menuList[i]) { 205 return i; 206 } 207 } 208 /* Allocate a new slot. */ 209 menuListSize++; 210 if (menuList) { 211 menuList = (GLUTmenu **) 212 realloc(menuList, menuListSize * sizeof(GLUTmenu *)); 213 } else { 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 *)); 218 } 219 if (!menuList) { 220 __glutFatalError("out of memory."); 221 } 222 menuList[menuListSize - 1] = NULL; 223 return menuListSize - 1; 224} 225 226static void 227menuModificationError(void) 228{ 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."); 232} 233 234int GLUTAPIENTRY 235glutCreateMenu(GLUTselectCB selectFunc) 236{ 237 GLUTmenu *menu; 238 int menuid; 239 240 if (__glutMappedMenu) { 241 menuModificationError(); 242 } 243 menuid = getUnusedMenuSlot(); 244 menu = (GLUTmenu *) malloc(sizeof(GLUTmenu)); 245 if (!menu) { 246 __glutFatalError("out of memory."); 247 } 248 menu->id = menuid; 249 menu->num = 0; 250 menu->submenus = 0; 251 menu->select = selectFunc; 252 menu->list = NULL; 253 menu->cascade = NULL; 254 menu->highlighted = NULL; 255 menu->anchor = NULL; 256 menu->win = (HWND) CreatePopupMenu(); 257 menuList[menuid] = menu; 258 __glutSetMenu(menu); 259 return menuid + 1; 260} 261 262int GLUTAPIENTRY 263__glutCreateMenuWithExit(GLUTselectCB selectFunc, void (__cdecl *exitfunc)(int)) 264{ 265 __glutExitFunc = exitfunc; 266 return glutCreateMenu(selectFunc); 267} 268 269void GLUTAPIENTRY 270glutDestroyMenu(int menunum) 271{ 272 GLUTmenu *menu = __glutGetMenuByNum(menunum); 273 GLUTmenuItem *item, *next; 274 275 if (__glutMappedMenu) { 276 menuModificationError(); 277 } 278 assert(menu->id == menunum - 1); 279 DestroyMenu( (HMENU) menu->win); 280 menuList[menunum - 1] = NULL; 281 /* free all menu entries */ 282 item = menu->list; 283 while (item) { 284 assert(item->menu == menu); 285 next = item->next; 286 free(item->label); 287 free(item); 288 item = next; 289 } 290 if (__glutCurrentMenu == menu) { 291 __glutCurrentMenu = NULL; 292 } 293 free(menu); 294} 295 296int GLUTAPIENTRY 297glutGetMenu(void) 298{ 299 if (__glutCurrentMenu) { 300 return __glutCurrentMenu->id + 1; 301 } else { 302 return 0; 303 } 304} 305 306void GLUTAPIENTRY 307glutSetMenu(int menuid) 308{ 309 GLUTmenu *menu; 310 311 if (menuid < 1 || menuid > menuListSize) { 312 __glutWarning("glutSetMenu attempted on bogus menu."); 313 return; 314 } 315 menu = menuList[menuid - 1]; 316 if (!menu) { 317 __glutWarning("glutSetMenu attempted on bogus menu."); 318 return; 319 } 320 __glutSetMenu(menu); 321} 322 323static void 324setMenuItem(GLUTmenuItem * item, const char *label, 325 int value, Bool isTrigger) 326{ 327 GLUTmenu *menu; 328 329 menu = item->menu; 330 item->label = __glutStrdup(label); 331 if (!item->label) { 332 __glutFatalError("out of memory."); 333 } 334 item->isTrigger = isTrigger; 335 item->len = (int) strlen(label); 336 item->value = value; 337 item->unique = uniqueMenuHandler++; 338 if (isTrigger) { 339 AppendMenu((HMENU) menu->win, MF_POPUP, (UINT)item->win, label); 340 } else { 341 AppendMenu((HMENU) menu->win, MF_STRING, item->unique, label); 342 } 343} 344 345void GLUTAPIENTRY 346glutAddMenuEntry(const char *label, int value) 347{ 348 GLUTmenuItem *entry; 349 350 if (__glutMappedMenu) { 351 menuModificationError(); 352 } 353 entry = (GLUTmenuItem *) malloc(sizeof(GLUTmenuItem)); 354 if (!entry) { 355 __glutFatalError("out of memory."); 356 } 357 entry->menu = __glutCurrentMenu; 358 setMenuItem(entry, label, value, FALSE); 359 __glutCurrentMenu->num++; 360 entry->next = __glutCurrentMenu->list; 361 __glutCurrentMenu->list = entry; 362} 363 364void GLUTAPIENTRY 365glutAddSubMenu(const char *label, int menu) 366{ 367 GLUTmenuItem *submenu; 368 GLUTmenu *popupmenu; 369 370 if (__glutMappedMenu) { 371 menuModificationError(); 372 } 373 submenu = (GLUTmenuItem *) malloc(sizeof(GLUTmenuItem)); 374 if (!submenu) { 375 __glutFatalError("out of memory."); 376 } 377 __glutCurrentMenu->submenus++; 378 submenu->menu = __glutCurrentMenu; 379 popupmenu = __glutGetMenuByNum(menu); 380 if (popupmenu) { 381 submenu->win = popupmenu->win; 382 } 383 setMenuItem(submenu, label, /* base 0 */ menu - 1, TRUE); 384 __glutCurrentMenu->num++; 385 submenu->next = __glutCurrentMenu->list; 386 __glutCurrentMenu->list = submenu; 387} 388 389void GLUTAPIENTRY 390glutChangeToMenuEntry(int num, const char *label, int value) 391{ 392 GLUTmenuItem *item; 393 int i; 394 395 if (__glutMappedMenu) { 396 menuModificationError(); 397 } 398 i = __glutCurrentMenu->num; 399 item = __glutCurrentMenu->list; 400 while (item) { 401 if (i == num) { 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); 408 } 409 free(item->label); 410 411 item->label = strdup(label); 412 if (!item->label) 413 __glutFatalError("out of memory"); 414 item->isTrigger = FALSE; 415 item->len = (int) strlen(label); 416 item->value = value; 417 item->unique = uniqueMenuHandler++; 418 ModifyMenu((HMENU) __glutCurrentMenu->win, (UINT) i - 1, 419 MF_BYPOSITION | MFT_STRING, item->unique, label); 420 421 return; 422 } 423 i--; 424 item = item->next; 425 } 426 __glutWarning("Current menu has no %d item.", num); 427} 428 429void GLUTAPIENTRY 430glutChangeToSubMenu(int num, const char *label, int menu) 431{ 432 GLUTmenu *popupmenu; 433 GLUTmenuItem *item; 434 int i; 435 436 if (__glutMappedMenu) { 437 menuModificationError(); 438 } 439 i = __glutCurrentMenu->num; 440 item = __glutCurrentMenu->list; 441 while (item) { 442 if (i == num) { 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(); 448 } 449 free(item->label); 450 451 item->label = strdup(label); 452 if (!item->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); 459 if (popupmenu) 460 item->win = popupmenu->win; 461 ModifyMenu((HMENU) __glutCurrentMenu->win, (UINT) i - 1, 462 MF_BYPOSITION | MF_POPUP, (UINT) item->win, label); 463 return; 464 } 465 i--; 466 item = item->next; 467 } 468 __glutWarning("Current menu has no %d item.", num); 469} 470 471void GLUTAPIENTRY 472glutRemoveMenuItem(int num) 473{ 474 GLUTmenuItem *item, **prev; 475 int i; 476 477 if (__glutMappedMenu) { 478 menuModificationError(); 479 } 480 i = __glutCurrentMenu->num; 481 prev = &__glutCurrentMenu->list; 482 item = __glutCurrentMenu->list; 483 while (item) { 484 if (i == num) { 485 /* Found the menu item in list to remove. */ 486 __glutCurrentMenu->num--; 487 488 /* Patch up menu's item list. */ 489 *prev = item->next; 490 491 RemoveMenu((HMENU) __glutCurrentMenu->win, (UINT) i - 1, MF_BYPOSITION); 492 493 free(item->label); 494 free(item); 495 return; 496 } 497 i--; 498 prev = &item->next; 499 item = item->next; 500 } 501 __glutWarning("Current menu has no %d item.", num); 502} 503 504void GLUTAPIENTRY 505glutAttachMenu(int button) 506{ 507 if (__glutCurrentWindow == __glutGameModeWindow) { 508 __glutWarning("cannot attach menus in game mode."); 509 return; 510 } 511 if (__glutMappedMenu) { 512 menuModificationError(); 513 } 514 if (__glutCurrentWindow->menu[button] < 1) { 515 __glutCurrentWindow->buttonUses++; 516 } 517 __glutCurrentWindow->menu[button] = __glutCurrentMenu->id + 1; 518} 519 520void GLUTAPIENTRY 521glutDetachMenu(int button) 522{ 523 if (__glutMappedMenu) { 524 menuModificationError(); 525 } 526 if (__glutCurrentWindow->menu[button] > 0) { 527 __glutCurrentWindow->buttonUses--; 528 __glutCurrentWindow->menu[button] = 0; 529 } 530} 531 532