xref: /haiku/src/libs/glut/glutMenu.cpp (revision 68ea01249e1e2088933cb12f9c28d4e5c5d1c9ef)
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