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 ***********************************************************/ 25 static GlutMenu **menuList = 0; 26 static int menuListSize = 0; 27 28 /*********************************************************** 29 * FUNCTION: getUnusedMenuSlot 30 * 31 * DESCRIPTION: helper function to get a new menu slot 32 ***********************************************************/ 33 GlutMenu *__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 ***********************************************************/ 46 static int 47 getUnusedMenuSlot(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 ***********************************************************/ 72 int APIENTRY 73 glutCreateMenu(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 ***********************************************************/ 91 int APIENTRY 92 glutGetMenu(void) 93 { 94 if (gState.currentMenu) { 95 return gState.currentMenu->id + 1; 96 } else { 97 return 0; 98 } 99 } 100 101 void APIENTRY 102 glutSetMenu(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 ***********************************************************/ 123 void APIENTRY 124 glutDestroyMenu(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 ***********************************************************/ 139 void 140 glutAddMenuEntry(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 ***********************************************************/ 150 void 151 glutAddSubMenu(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 ***********************************************************/ 161 void 162 glutChangeToMenuEntry(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 ***********************************************************/ 188 void 189 glutChangeToSubMenu(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 ***********************************************************/ 215 void 216 glutRemoveMenuItem(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 ***********************************************************/ 249 void 250 glutAttachMenu(int button) 251 { 252 gState.currentWindow->menu[button] = gState.currentMenu->id + 1; 253 } 254 255 void 256 glutDetachMenu(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 ***********************************************************/ 268 BMenu *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 ***********************************************************/ 302 GlutMenu::~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 ***********************************************************/ 317 GlutMenuItem::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