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