1/*********************************************************** 2 * Copyright (C) 1997, Be Inc. Copyright (C) 1999, Jake Hamby. 3 * 4 * This program is freely distributable without licensing fees 5 * and is provided without guarantee or warrantee expressed or 6 * implied. This program is -not- in the public domain. 7 * 8 * FILE: glutMenu.cpp 9 * 10 * DESCRIPTION: code for popup menu handling 11 ***********************************************************/ 12 13/*********************************************************** 14 * Headers 15 ***********************************************************/ 16#include <GL/glut.h> 17#include <stdlib.h> 18#include <string.h> 19#include "glutint.h" 20#include "glutState.h" 21 22/*********************************************************** 23 * Private variables 24 ***********************************************************/ 25static GlutMenu **menuList = 0; 26static int menuListSize = 0; 27 28/*********************************************************** 29 * FUNCTION: getUnusedMenuSlot 30 * 31 * DESCRIPTION: helper function to get a new menu slot 32 ***********************************************************/ 33GlutMenu *__glutGetMenuByNum(int menunum) 34{ 35 if (menunum < 1 || menunum > menuListSize) { 36 return NULL; 37 } 38 return menuList[menunum - 1]; 39} 40 41/*********************************************************** 42 * FUNCTION: getUnusedMenuSlot 43 * 44 * DESCRIPTION: helper function to get a new menu slot 45 ***********************************************************/ 46static int 47getUnusedMenuSlot(void) 48{ 49 int i; 50 51 /* Look for allocated, unused slot. */ 52 for (i = 0; i < menuListSize; i++) { 53 if (!menuList[i]) { 54 return i; 55 } 56 } 57 /* Allocate a new slot. */ 58 menuListSize++; 59 menuList = (GlutMenu **) 60 realloc(menuList, menuListSize * sizeof(GlutMenu *)); 61 if (!menuList) 62 __glutFatalError("out of memory."); 63 menuList[menuListSize - 1] = NULL; 64 return menuListSize - 1; 65} 66 67/*********************************************************** 68 * FUNCTION: glutCreateMenu (6.1) 69 * 70 * DESCRIPTION: create a new menu 71 ***********************************************************/ 72int APIENTRY 73glutCreateMenu(GLUTselectCB selectFunc) 74{ 75 GlutMenu *menu; 76 int menuid; 77 78 menuid = getUnusedMenuSlot(); 79 menu = new GlutMenu(menuid, selectFunc); // constructor sets up members 80 menuList[menuid] = menu; 81 gState.currentMenu = menu; 82 return menuid + 1; 83} 84 85/*********************************************************** 86 * FUNCTION: glutSetMenu (6.2) 87 * glutGetMenu 88 * 89 * DESCRIPTION: set and get the current menu 90 ***********************************************************/ 91int APIENTRY 92glutGetMenu(void) 93{ 94 if (gState.currentMenu) { 95 return gState.currentMenu->id + 1; 96 } else { 97 return 0; 98 } 99} 100 101void APIENTRY 102glutSetMenu(int menuid) 103{ 104 GlutMenu *menu; 105 106 if (menuid < 1 || menuid > menuListSize) { 107 __glutWarning("glutSetMenu attempted on bogus menu."); 108 return; 109 } 110 menu = menuList[menuid - 1]; 111 if (!menu) { 112 __glutWarning("glutSetMenu attempted on bogus menu."); 113 return; 114 } 115 gState.currentMenu = menu; 116} 117 118/*********************************************************** 119 * FUNCTION: glutDestroyMenu (6.3) 120 * 121 * DESCRIPTION: destroy the specified menu 122 ***********************************************************/ 123void APIENTRY 124glutDestroyMenu(int menunum) 125{ 126 GlutMenu *menu = __glutGetMenuByNum(menunum); 127 menuList[menunum - 1] = 0; 128 if (gState.currentMenu == menu) { 129 gState.currentMenu = 0; 130 } 131 delete menu; 132} 133 134/*********************************************************** 135 * FUNCTION: glutAddMenuEntry (6.4) 136 * 137 * DESCRIPTION: add a new menu item 138 ***********************************************************/ 139void 140glutAddMenuEntry(const char *label, int value) 141{ 142 new GlutMenuItem(gState.currentMenu, false, value, label); 143} 144 145/*********************************************************** 146 * FUNCTION: glutAddSubMenu (6.5) 147 * 148 * DESCRIPTION: add a new submenu 149 ***********************************************************/ 150void 151glutAddSubMenu(const char *label, int menu) 152{ 153 new GlutMenuItem(gState.currentMenu, true, menu-1, label); 154} 155 156/*********************************************************** 157 * FUNCTION: glutChangeToMenuEntry (6.6) 158 * 159 * DESCRIPTION: change menuitem into a menu entry 160 ***********************************************************/ 161void 162glutChangeToMenuEntry(int num, const char *label, int value) 163{ 164 GlutMenuItem *item; 165 int i; 166 167 i = gState.currentMenu->num; 168 item = gState.currentMenu->list; 169 while (item) { 170 if (i == num) { 171 free(item->label); 172 item->label = strdup(label); 173 item->isTrigger = false; 174 item->value = value; 175 return; 176 } 177 i--; 178 item = item->next; 179 } 180 __glutWarning("Current menu has no %d item.", num); 181} 182 183/*********************************************************** 184 * FUNCTION: glutChangeToSubMenu (6.7) 185 * 186 * DESCRIPTION: change menuitem into a submenu 187 ***********************************************************/ 188void 189glutChangeToSubMenu(int num, const char *label, int menu) 190{ 191 GlutMenuItem *item; 192 int i; 193 194 i = gState.currentMenu->num; 195 item = gState.currentMenu->list; 196 while (item) { 197 if (i == num) { 198 free(item->label); 199 item->label = strdup(label); 200 item->isTrigger = true; 201 item->value = menu-1; 202 return; 203 } 204 i--; 205 item = item->next; 206 } 207 __glutWarning("Current menu has no %d item.", num); 208} 209 210/*********************************************************** 211 * FUNCTION: glutRemoveMenuItem (6.8) 212 * 213 * DESCRIPTION: remove a menu item 214 ***********************************************************/ 215void 216glutRemoveMenuItem(int num) 217{ 218 GlutMenuItem *item, **prev; 219 int i; 220 221 i = gState.currentMenu->num; 222 prev = &gState.currentMenu->list; 223 item = gState.currentMenu->list; 224 225 while (item) { 226 if (i == num) { 227 gState.currentMenu->num--; 228 229 /* Patch up menu's item list. */ 230 *prev = item->next; 231 232 free(item->label); 233 delete item; 234 return; 235 } 236 i--; 237 prev = &item->next; 238 item = item->next; 239 } 240 __glutWarning("Current menu has no %d item.", num); 241} 242 243/*********************************************************** 244 * FUNCTION: glutAttachMenu (6.9) 245 * glutDetachMenu 246 * 247 * DESCRIPTION: attach and detach menu from view 248 ***********************************************************/ 249void 250glutAttachMenu(int button) 251{ 252 gState.currentWindow->menu[button] = gState.currentMenu->id + 1; 253} 254 255void 256glutDetachMenu(int button) 257{ 258 gState.currentWindow->menu[button] = 0; 259} 260 261/*********************************************************** 262 * CLASS: GlutMenu 263 * 264 * FUNCTION: CreateBMenu 265 * 266 * DESCRIPTION: construct a BPopupMenu for this menu 267 ***********************************************************/ 268BMenu *GlutMenu::CreateBMenu(bool toplevel) { 269 BMenu *bpopup; 270 if(toplevel) { 271 bpopup = new GlutPopUp(id+1); 272 } else { 273 bpopup = new BMenu(""); 274 } 275 GlutMenuItem *item = list; 276 while (item) { 277 GlutBMenuItem *bitem; 278 if(item->isTrigger) { 279 // recursively call CreateBMenu 280 bitem = new GlutBMenuItem(menuList[item->value]->CreateBMenu(false)); 281 bitem->SetLabel(item->label); 282 bitem->menu = 0; // real menu items start at 1 283 bitem->value = 0; 284 } else { 285 bitem = new GlutBMenuItem(item->label); 286 bitem->menu = id + 1; 287 bitem->value = item->value; 288 } 289 bpopup->AddItem(bitem, 0); 290 item = item->next; 291 } 292 return bpopup; 293} 294 295/*********************************************************** 296 * CLASS: GlutMenu 297 * 298 * FUNCTION: (destructor) 299 * 300 * DESCRIPTION: destroy the menu and its items (but not submenus!) 301 ***********************************************************/ 302GlutMenu::~GlutMenu() { 303 while (list) { 304 GlutMenuItem *next = list->next; 305 delete list; 306 list = next; 307 } 308} 309 310/*********************************************************** 311 * CLASS: GlutMenuItem 312 * 313 * FUNCTION: (constructor) 314 * 315 * DESCRIPTION: construct the new menu item and add to parent 316 ***********************************************************/ 317GlutMenuItem::GlutMenuItem(GlutMenu *n_menu, bool n_trig, int n_value, const char *n_label) 318{ 319 menu = n_menu; 320 isTrigger = n_trig; 321 value = n_value; 322 label = strdup(n_label); 323 next = menu->list; 324 menu->list = this; 325 menu->num++; 326} 327