| /*********************************************************** |
| * Copyright (C) 1997, Be Inc. Copyright (C) 1999, Jake Hamby. |
| * |
| * This program is freely distributable without licensing fees |
| * and is provided without guarantee or warrantee expressed or |
| * implied. This program is -not- in the public domain. |
| * |
| * FILE: glutMenu.cpp |
| * |
| * DESCRIPTION: code for popup menu handling |
| ***********************************************************/ |
| |
| /*********************************************************** |
| * Headers |
| ***********************************************************/ |
| #include <GL/glut.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include "glutint.h" |
| #include "glutState.h" |
| |
| /*********************************************************** |
| * Private variables |
| ***********************************************************/ |
| static GlutMenu **menuList = 0; |
| static int menuListSize = 0; |
| |
| /*********************************************************** |
| * FUNCTION: getUnusedMenuSlot |
| * |
| * DESCRIPTION: helper function to get a new menu slot |
| ***********************************************************/ |
| GlutMenu *__glutGetMenuByNum(int menunum) |
| { |
| if (menunum < 1 || menunum > menuListSize) { |
| return NULL; |
| } |
| return menuList[menunum - 1]; |
| } |
| |
| /*********************************************************** |
| * FUNCTION: getUnusedMenuSlot |
| * |
| * DESCRIPTION: helper function to get a new menu slot |
| ***********************************************************/ |
| static int |
| getUnusedMenuSlot(void) |
| { |
| int i; |
| |
| /* Look for allocated, unused slot. */ |
| for (i = 0; i < menuListSize; i++) { |
| if (!menuList[i]) { |
| return i; |
| } |
| } |
| /* Allocate a new slot. */ |
| menuListSize++; |
| menuList = (GlutMenu **) |
| realloc(menuList, menuListSize * sizeof(GlutMenu *)); |
| if (!menuList) |
| __glutFatalError("out of memory."); |
| menuList[menuListSize - 1] = NULL; |
| return menuListSize - 1; |
| } |
| |
| /*********************************************************** |
| * FUNCTION: glutCreateMenu (6.1) |
| * |
| * DESCRIPTION: create a new menu |
| ***********************************************************/ |
| int APIENTRY |
| glutCreateMenu(GLUTselectCB selectFunc) |
| { |
| GlutMenu *menu; |
| int menuid; |
| |
| menuid = getUnusedMenuSlot(); |
| menu = new GlutMenu(menuid, selectFunc); // constructor sets up members |
| menuList[menuid] = menu; |
| gState.currentMenu = menu; |
| return menuid + 1; |
| } |
| |
| /*********************************************************** |
| * FUNCTION: glutSetMenu (6.2) |
| * glutGetMenu |
| * |
| * DESCRIPTION: set and get the current menu |
| ***********************************************************/ |
| int APIENTRY |
| glutGetMenu(void) |
| { |
| if (gState.currentMenu) { |
| return gState.currentMenu->id + 1; |
| } else { |
| return 0; |
| } |
| } |
| |
| void APIENTRY |
| glutSetMenu(int menuid) |
| { |
| GlutMenu *menu; |
| |
| if (menuid < 1 || menuid > menuListSize) { |
| __glutWarning("glutSetMenu attempted on bogus menu."); |
| return; |
| } |
| menu = menuList[menuid - 1]; |
| if (!menu) { |
| __glutWarning("glutSetMenu attempted on bogus menu."); |
| return; |
| } |
| gState.currentMenu = menu; |
| } |
| |
| /*********************************************************** |
| * FUNCTION: glutDestroyMenu (6.3) |
| * |
| * DESCRIPTION: destroy the specified menu |
| ***********************************************************/ |
| void APIENTRY |
| glutDestroyMenu(int menunum) |
| { |
| GlutMenu *menu = __glutGetMenuByNum(menunum); |
| menuList[menunum - 1] = 0; |
| if (gState.currentMenu == menu) { |
| gState.currentMenu = 0; |
| } |
| delete menu; |
| } |
| |
| /*********************************************************** |
| * FUNCTION: glutAddMenuEntry (6.4) |
| * |
| * DESCRIPTION: add a new menu item |
| ***********************************************************/ |
| void |
| glutAddMenuEntry(const char *label, int value) |
| { |
| new GlutMenuItem(gState.currentMenu, false, value, label); |
| } |
| |
| /*********************************************************** |
| * FUNCTION: glutAddSubMenu (6.5) |
| * |
| * DESCRIPTION: add a new submenu |
| ***********************************************************/ |
| void |
| glutAddSubMenu(const char *label, int menu) |
| { |
| new GlutMenuItem(gState.currentMenu, true, menu-1, label); |
| } |
| |
| /*********************************************************** |
| * FUNCTION: glutChangeToMenuEntry (6.6) |
| * |
| * DESCRIPTION: change menuitem into a menu entry |
| ***********************************************************/ |
| void |
| glutChangeToMenuEntry(int num, const char *label, int value) |
| { |
| GlutMenuItem *item; |
| int i; |
| |
| i = gState.currentMenu->num; |
| item = gState.currentMenu->list; |
| while (item) { |
| if (i == num) { |
| free(item->label); |
| item->label = strdup(label); |
| item->isTrigger = false; |
| item->value = value; |
| return; |
| } |
| i--; |
| item = item->next; |
| } |
| __glutWarning("Current menu has no %d item.", num); |
| } |
| |
| /*********************************************************** |
| * FUNCTION: glutChangeToSubMenu (6.7) |
| * |
| * DESCRIPTION: change menuitem into a submenu |
| ***********************************************************/ |
| void |
| glutChangeToSubMenu(int num, const char *label, int menu) |
| { |
| GlutMenuItem *item; |
| int i; |
| |
| i = gState.currentMenu->num; |
| item = gState.currentMenu->list; |
| while (item) { |
| if (i == num) { |
| free(item->label); |
| item->label = strdup(label); |
| item->isTrigger = true; |
| item->value = menu-1; |
| return; |
| } |
| i--; |
| item = item->next; |
| } |
| __glutWarning("Current menu has no %d item.", num); |
| } |
| |
| /*********************************************************** |
| * FUNCTION: glutRemoveMenuItem (6.8) |
| * |
| * DESCRIPTION: remove a menu item |
| ***********************************************************/ |
| void |
| glutRemoveMenuItem(int num) |
| { |
| GlutMenuItem *item, **prev; |
| int i; |
| |
| i = gState.currentMenu->num; |
| prev = &gState.currentMenu->list; |
| item = gState.currentMenu->list; |
| |
| while (item) { |
| if (i == num) { |
| gState.currentMenu->num--; |
| |
| /* Patch up menu's item list. */ |
| *prev = item->next; |
| |
| free(item->label); |
| delete item; |
| return; |
| } |
| i--; |
| prev = &item->next; |
| item = item->next; |
| } |
| __glutWarning("Current menu has no %d item.", num); |
| } |
| |
| /*********************************************************** |
| * FUNCTION: glutAttachMenu (6.9) |
| * glutDetachMenu |
| * |
| * DESCRIPTION: attach and detach menu from view |
| ***********************************************************/ |
| void |
| glutAttachMenu(int button) |
| { |
| gState.currentWindow->menu[button] = gState.currentMenu->id + 1; |
| } |
| |
| void |
| glutDetachMenu(int button) |
| { |
| gState.currentWindow->menu[button] = 0; |
| } |
| |
| /*********************************************************** |
| * CLASS: GlutMenu |
| * |
| * FUNCTION: CreateBMenu |
| * |
| * DESCRIPTION: construct a BPopupMenu for this menu |
| ***********************************************************/ |
| BMenu *GlutMenu::CreateBMenu(bool toplevel) { |
| BMenu *bpopup; |
| if(toplevel) { |
| bpopup = new GlutPopUp(id+1); |
| } else { |
| bpopup = new BMenu(""); |
| } |
| GlutMenuItem *item = list; |
| while (item) { |
| GlutBMenuItem *bitem; |
| if(item->isTrigger) { |
| // recursively call CreateBMenu |
| bitem = new GlutBMenuItem(menuList[item->value]->CreateBMenu(false)); |
| bitem->SetLabel(item->label); |
| bitem->menu = 0; // real menu items start at 1 |
| bitem->value = 0; |
| } else { |
| bitem = new GlutBMenuItem(item->label); |
| bitem->menu = id + 1; |
| bitem->value = item->value; |
| } |
| bpopup->AddItem(bitem, 0); |
| item = item->next; |
| } |
| return bpopup; |
| } |
| |
| /*********************************************************** |
| * CLASS: GlutMenu |
| * |
| * FUNCTION: (destructor) |
| * |
| * DESCRIPTION: destroy the menu and its items (but not submenus!) |
| ***********************************************************/ |
| GlutMenu::~GlutMenu() { |
| while (list) { |
| GlutMenuItem *next = list->next; |
| delete list; |
| list = next; |
| } |
| } |
| |
| /*********************************************************** |
| * CLASS: GlutMenuItem |
| * |
| * FUNCTION: (constructor) |
| * |
| * DESCRIPTION: construct the new menu item and add to parent |
| ***********************************************************/ |
| GlutMenuItem::GlutMenuItem(GlutMenu *n_menu, bool n_trig, int n_value, const char *n_label) |
| { |
| menu = n_menu; |
| isTrigger = n_trig; |
| value = n_value; |
| label = strdup(n_label); |
| next = menu->list; |
| menu->list = this; |
| menu->num++; |
| } |