glut: Include X11 headers and libraries in SCons build.
[mesa.git] / src / glut / glx / win32_menu.c
1
2 /* Copyright (c) Mark J. Kilgard, 1994, 1997, 1998. */
3 /* Copyright (c) Nate Robins, 1997. */
4
5 /* This program is freely distributable without licensing fees
6 and is provided without guarantee or warrantee expressed or
7 implied. This program is -not- in the public domain. */
8
9 /* This file completely re-implements glut_menu.c and glut_menu2.c
10 for Win32. Note that neither glut_menu.c nor glut_menu2.c are
11 compiled into Win32 GLUT. */
12
13 #include <stdlib.h>
14 #include <string.h>
15 #include <stdio.h>
16 #include <errno.h>
17 #include <assert.h>
18
19 #include "glutint.h"
20
21 void (GLUTCALLBACK *__glutMenuStatusFunc) (int, int, int);
22 GLUTmenu *__glutMappedMenu;
23 GLUTwindow *__glutMenuWindow;
24 GLUTmenuItem *__glutItemSelected;
25 unsigned __glutMenuButton;
26
27 static GLUTmenu **menuList = NULL;
28 static int menuListSize = 0;
29 static UINT uniqueMenuHandler = 1;
30
31 /* DEPRICATED, use glutMenuStatusFunc instead. */
32 void GLUTAPIENTRY
33 glutMenuStateFunc(GLUTmenuStateCB menuStateFunc)
34 {
35 __glutMenuStatusFunc = (GLUTmenuStatusCB) menuStateFunc;
36 }
37
38 void GLUTAPIENTRY
39 glutMenuStatusFunc(GLUTmenuStatusCB menuStatusFunc)
40 {
41 __glutMenuStatusFunc = menuStatusFunc;
42 }
43
44 void
45 __glutSetMenu(GLUTmenu * menu)
46 {
47 __glutCurrentMenu = menu;
48 }
49
50 static void
51 unmapMenu(GLUTmenu * menu)
52 {
53 if (menu->cascade) {
54 unmapMenu(menu->cascade);
55 menu->cascade = NULL;
56 }
57 menu->anchor = NULL;
58 menu->highlighted = NULL;
59 }
60
61 void
62 __glutFinishMenu(Window win, int x, int y)
63 {
64
65 unmapMenu(__glutMappedMenu);
66
67 /* XXX Put in a GdiFlush just in case. Probably unnecessary. -mjk */
68 GdiFlush();
69
70 if (__glutMenuStatusFunc) {
71 __glutSetWindow(__glutMenuWindow);
72 __glutSetMenu(__glutMappedMenu);
73
74 /* Setting __glutMappedMenu to NULL permits operations that
75 change menus or destroy the menu window again. */
76 __glutMappedMenu = NULL;
77
78 __glutMenuStatusFunc(GLUT_MENU_NOT_IN_USE, x, y);
79 }
80 /* Setting __glutMappedMenu to NULL permits operations that
81 change menus or destroy the menu window again. */
82 __glutMappedMenu = NULL;
83
84 /* If an item is selected and it is not a submenu trigger,
85 generate menu callback. */
86 if (__glutItemSelected && !__glutItemSelected->isTrigger) {
87 __glutSetWindow(__glutMenuWindow);
88 /* When menu callback is triggered, current menu should be
89 set to the callback menu. */
90 __glutSetMenu(__glutItemSelected->menu);
91 __glutItemSelected->menu->select(__glutItemSelected->value);
92 }
93 __glutMenuWindow = NULL;
94 }
95
96 static void
97 mapMenu(GLUTmenu * menu, int x, int y)
98 {
99 TrackPopupMenu((HMENU) menu->win, TPM_LEFTALIGN |
100 ((__glutMenuButton == TPM_RIGHTBUTTON) ? TPM_RIGHTBUTTON : TPM_LEFTBUTTON),
101 x, y, 0, __glutCurrentWindow->win, NULL);
102 }
103
104 void
105 __glutStartMenu(GLUTmenu * menu, GLUTwindow * window,
106 int x, int y, int x_win, int y_win)
107 {
108 assert(__glutMappedMenu == NULL);
109 __glutMappedMenu = menu;
110 __glutMenuWindow = window;
111 __glutItemSelected = NULL;
112 if (__glutMenuStatusFunc) {
113 __glutSetMenu(menu);
114 __glutSetWindow(window);
115 __glutMenuStatusFunc(GLUT_MENU_IN_USE, x_win, y_win);
116 }
117 mapMenu(menu, x, y);
118 }
119
120 GLUTmenuItem *
121 __glutGetUniqueMenuItem(GLUTmenu * menu, UINT unique)
122 {
123 GLUTmenuItem *item;
124 int i;
125
126 i = menu->num;
127 item = menu->list;
128 while (item) {
129 if (item->unique == unique) {
130 return item;
131 }
132 if (item->isTrigger) {
133 GLUTmenuItem *subitem;
134 subitem = __glutGetUniqueMenuItem(menuList[item->value], unique);
135 if (subitem) {
136 return subitem;
137 }
138 }
139 i--;
140 item = item->next;
141 }
142 return NULL;
143 }
144
145 GLUTmenuItem *
146 __glutGetMenuItem(GLUTmenu * menu, Window win, int *which)
147 {
148 GLUTmenuItem *item;
149 int i;
150
151 i = menu->num;
152 item = menu->list;
153 while (item) {
154 if (item->win == win) {
155 *which = i;
156 return item;
157 }
158 if (item->isTrigger) {
159 GLUTmenuItem *subitem;
160
161 subitem = __glutGetMenuItem(menuList[item->value],
162 win, which);
163 if (subitem) {
164 return subitem;
165 }
166 }
167 i--;
168 item = item->next;
169 }
170 return NULL;
171 }
172
173 GLUTmenu *
174 __glutGetMenu(Window win)
175 {
176 GLUTmenu *menu;
177
178 menu = __glutMappedMenu;
179 while (menu) {
180 if (win == menu->win) {
181 return menu;
182 }
183 menu = menu->cascade;
184 }
185 return NULL;
186 }
187
188 GLUTmenu *
189 __glutGetMenuByNum(int menunum)
190 {
191 if (menunum < 1 || menunum > menuListSize) {
192 return NULL;
193 }
194 return menuList[menunum - 1];
195 }
196
197 static int
198 getUnusedMenuSlot(void)
199 {
200 int i;
201
202 /* Look for allocated, unused slot. */
203 for (i = 0; i < menuListSize; i++) {
204 if (!menuList[i]) {
205 return i;
206 }
207 }
208 /* Allocate a new slot. */
209 menuListSize++;
210 if (menuList) {
211 menuList = (GLUTmenu **)
212 realloc(menuList, menuListSize * sizeof(GLUTmenu *));
213 } else {
214 /* XXX Some realloc's do not correctly perform a malloc
215 when asked to perform a realloc on a NULL pointer,
216 though the ANSI C library spec requires this. */
217 menuList = (GLUTmenu **) malloc(sizeof(GLUTmenu *));
218 }
219 if (!menuList) {
220 __glutFatalError("out of memory.");
221 }
222 menuList[menuListSize - 1] = NULL;
223 return menuListSize - 1;
224 }
225
226 static void
227 menuModificationError(void)
228 {
229 /* XXX Remove the warning after GLUT 3.0. */
230 __glutWarning("The following is a new check for GLUT 3.0; update your code.");
231 __glutFatalError("menu manipulation not allowed while menus in use.");
232 }
233
234 int GLUTAPIENTRY
235 glutCreateMenu(GLUTselectCB selectFunc)
236 {
237 GLUTmenu *menu;
238 int menuid;
239
240 if (__glutMappedMenu) {
241 menuModificationError();
242 }
243 menuid = getUnusedMenuSlot();
244 menu = (GLUTmenu *) malloc(sizeof(GLUTmenu));
245 if (!menu) {
246 __glutFatalError("out of memory.");
247 }
248 menu->id = menuid;
249 menu->num = 0;
250 menu->submenus = 0;
251 menu->select = selectFunc;
252 menu->list = NULL;
253 menu->cascade = NULL;
254 menu->highlighted = NULL;
255 menu->anchor = NULL;
256 menu->win = (HWND) CreatePopupMenu();
257 menuList[menuid] = menu;
258 __glutSetMenu(menu);
259 return menuid + 1;
260 }
261
262 int GLUTAPIENTRY
263 __glutCreateMenuWithExit(GLUTselectCB selectFunc, void (__cdecl *exitfunc)(int))
264 {
265 __glutExitFunc = exitfunc;
266 return glutCreateMenu(selectFunc);
267 }
268
269 void GLUTAPIENTRY
270 glutDestroyMenu(int menunum)
271 {
272 GLUTmenu *menu = __glutGetMenuByNum(menunum);
273 GLUTmenuItem *item, *next;
274
275 if (__glutMappedMenu) {
276 menuModificationError();
277 }
278 assert(menu->id == menunum - 1);
279 DestroyMenu( (HMENU) menu->win);
280 menuList[menunum - 1] = NULL;
281 /* free all menu entries */
282 item = menu->list;
283 while (item) {
284 assert(item->menu == menu);
285 next = item->next;
286 free(item->label);
287 free(item);
288 item = next;
289 }
290 if (__glutCurrentMenu == menu) {
291 __glutCurrentMenu = NULL;
292 }
293 free(menu);
294 }
295
296 int GLUTAPIENTRY
297 glutGetMenu(void)
298 {
299 if (__glutCurrentMenu) {
300 return __glutCurrentMenu->id + 1;
301 } else {
302 return 0;
303 }
304 }
305
306 void GLUTAPIENTRY
307 glutSetMenu(int menuid)
308 {
309 GLUTmenu *menu;
310
311 if (menuid < 1 || menuid > menuListSize) {
312 __glutWarning("glutSetMenu attempted on bogus menu.");
313 return;
314 }
315 menu = menuList[menuid - 1];
316 if (!menu) {
317 __glutWarning("glutSetMenu attempted on bogus menu.");
318 return;
319 }
320 __glutSetMenu(menu);
321 }
322
323 static void
324 setMenuItem(GLUTmenuItem * item, const char *label,
325 int value, Bool isTrigger)
326 {
327 GLUTmenu *menu;
328
329 menu = item->menu;
330 item->label = __glutStrdup(label);
331 if (!item->label) {
332 __glutFatalError("out of memory.");
333 }
334 item->isTrigger = isTrigger;
335 item->len = (int) strlen(label);
336 item->value = value;
337 item->unique = uniqueMenuHandler++;
338 if (isTrigger) {
339 AppendMenu((HMENU) menu->win, MF_POPUP, (UINT)item->win, label);
340 } else {
341 AppendMenu((HMENU) menu->win, MF_STRING, item->unique, label);
342 }
343 }
344
345 void GLUTAPIENTRY
346 glutAddMenuEntry(const char *label, int value)
347 {
348 GLUTmenuItem *entry;
349
350 if (__glutMappedMenu) {
351 menuModificationError();
352 }
353 entry = (GLUTmenuItem *) malloc(sizeof(GLUTmenuItem));
354 if (!entry) {
355 __glutFatalError("out of memory.");
356 }
357 entry->menu = __glutCurrentMenu;
358 setMenuItem(entry, label, value, FALSE);
359 __glutCurrentMenu->num++;
360 entry->next = __glutCurrentMenu->list;
361 __glutCurrentMenu->list = entry;
362 }
363
364 void GLUTAPIENTRY
365 glutAddSubMenu(const char *label, int menu)
366 {
367 GLUTmenuItem *submenu;
368 GLUTmenu *popupmenu;
369
370 if (__glutMappedMenu) {
371 menuModificationError();
372 }
373 submenu = (GLUTmenuItem *) malloc(sizeof(GLUTmenuItem));
374 if (!submenu) {
375 __glutFatalError("out of memory.");
376 }
377 __glutCurrentMenu->submenus++;
378 submenu->menu = __glutCurrentMenu;
379 popupmenu = __glutGetMenuByNum(menu);
380 if (popupmenu) {
381 submenu->win = popupmenu->win;
382 }
383 setMenuItem(submenu, label, /* base 0 */ menu - 1, TRUE);
384 __glutCurrentMenu->num++;
385 submenu->next = __glutCurrentMenu->list;
386 __glutCurrentMenu->list = submenu;
387 }
388
389 void GLUTAPIENTRY
390 glutChangeToMenuEntry(int num, const char *label, int value)
391 {
392 GLUTmenuItem *item;
393 int i;
394
395 if (__glutMappedMenu) {
396 menuModificationError();
397 }
398 i = __glutCurrentMenu->num;
399 item = __glutCurrentMenu->list;
400 while (item) {
401 if (i == num) {
402 if (item->isTrigger) {
403 /* If changing a submenu trigger to a menu entry, we
404 need to account for submenus. */
405 item->menu->submenus--;
406 /* Nuke the Win32 menu. */
407 DestroyMenu((HMENU) item->win);
408 }
409 free(item->label);
410
411 item->label = strdup(label);
412 if (!item->label)
413 __glutFatalError("out of memory");
414 item->isTrigger = FALSE;
415 item->len = (int) strlen(label);
416 item->value = value;
417 item->unique = uniqueMenuHandler++;
418 ModifyMenu((HMENU) __glutCurrentMenu->win, (UINT) i - 1,
419 MF_BYPOSITION | MFT_STRING, item->unique, label);
420
421 return;
422 }
423 i--;
424 item = item->next;
425 }
426 __glutWarning("Current menu has no %d item.", num);
427 }
428
429 void GLUTAPIENTRY
430 glutChangeToSubMenu(int num, const char *label, int menu)
431 {
432 GLUTmenu *popupmenu;
433 GLUTmenuItem *item;
434 int i;
435
436 if (__glutMappedMenu) {
437 menuModificationError();
438 }
439 i = __glutCurrentMenu->num;
440 item = __glutCurrentMenu->list;
441 while (item) {
442 if (i == num) {
443 if (!item->isTrigger) {
444 /* If changing a menu entry to as submenu trigger, we
445 need to account for submenus. */
446 item->menu->submenus++;
447 item->win = (HWND) CreatePopupMenu();
448 }
449 free(item->label);
450
451 item->label = strdup(label);
452 if (!item->label)
453 __glutFatalError("out of memory");
454 item->isTrigger = TRUE;
455 item->len = (int) strlen(label);
456 item->value = menu - 1;
457 item->unique = uniqueMenuHandler++;
458 popupmenu = __glutGetMenuByNum(menu);
459 if (popupmenu)
460 item->win = popupmenu->win;
461 ModifyMenu((HMENU) __glutCurrentMenu->win, (UINT) i - 1,
462 MF_BYPOSITION | MF_POPUP, (UINT) item->win, label);
463 return;
464 }
465 i--;
466 item = item->next;
467 }
468 __glutWarning("Current menu has no %d item.", num);
469 }
470
471 void GLUTAPIENTRY
472 glutRemoveMenuItem(int num)
473 {
474 GLUTmenuItem *item, **prev;
475 int i;
476
477 if (__glutMappedMenu) {
478 menuModificationError();
479 }
480 i = __glutCurrentMenu->num;
481 prev = &__glutCurrentMenu->list;
482 item = __glutCurrentMenu->list;
483 while (item) {
484 if (i == num) {
485 /* Found the menu item in list to remove. */
486 __glutCurrentMenu->num--;
487
488 /* Patch up menu's item list. */
489 *prev = item->next;
490
491 RemoveMenu((HMENU) __glutCurrentMenu->win, (UINT) i - 1, MF_BYPOSITION);
492
493 free(item->label);
494 free(item);
495 return;
496 }
497 i--;
498 prev = &item->next;
499 item = item->next;
500 }
501 __glutWarning("Current menu has no %d item.", num);
502 }
503
504 void GLUTAPIENTRY
505 glutAttachMenu(int button)
506 {
507 if (__glutCurrentWindow == __glutGameModeWindow) {
508 __glutWarning("cannot attach menus in game mode.");
509 return;
510 }
511 if (__glutMappedMenu) {
512 menuModificationError();
513 }
514 if (__glutCurrentWindow->menu[button] < 1) {
515 __glutCurrentWindow->buttonUses++;
516 }
517 __glutCurrentWindow->menu[button] = __glutCurrentMenu->id + 1;
518 }
519
520 void GLUTAPIENTRY
521 glutDetachMenu(int button)
522 {
523 if (__glutMappedMenu) {
524 menuModificationError();
525 }
526 if (__glutCurrentWindow->menu[button] > 0) {
527 __glutCurrentWindow->buttonUses--;
528 __glutCurrentWindow->menu[button] = 0;
529 }
530 }
531