blob: 2f9e6a2c67c27a98298e8b2ad7460366ece00b28 [file] [log] [blame]
/***********************************************************
* 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++;
}