Merge branch 'master' into gallium-0.2
[mesa.git] / src / glut / os2 / os2_menu.cpp
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 //todo
100 // TrackPopupMenu((HMENU) menu->win, TPM_LEFTALIGN |
101 // (__glutMenuButton == TPM_RIGHTBUTTON) ? TPM_RIGHTBUTTON : TPM_LEFTBUTTON,
102 // x, y, 0, __glutCurrentWindow->win, NULL);
103 }
104
105 void
106 __glutStartMenu(GLUTmenu * menu, GLUTwindow * window,
107 int x, int y, int x_win, int y_win)
108 {
109 assert(__glutMappedMenu == NULL);
110 __glutMappedMenu = menu;
111 __glutMenuWindow = window;
112 __glutItemSelected = NULL;
113 if (__glutMenuStatusFunc) {
114 __glutSetMenu(menu);
115 __glutSetWindow(window);
116 __glutMenuStatusFunc(GLUT_MENU_IN_USE, x_win, y_win);
117 }
118 mapMenu(menu, x, y);
119 }
120
121 GLUTmenuItem *
122 __glutGetUniqueMenuItem(GLUTmenu * menu, UINT unique)
123 {
124 GLUTmenuItem *item;
125 int i;
126
127 i = menu->num;
128 item = menu->list;
129 while (item) {
130 if (item->unique == unique) {
131 return item;
132 }
133 if (item->isTrigger) {
134 GLUTmenuItem *subitem;
135 subitem = __glutGetUniqueMenuItem(menuList[item->value], unique);
136 if (subitem) {
137 return subitem;
138 }
139 }
140 i--;
141 item = item->next;
142 }
143 return NULL;
144 }
145
146 GLUTmenuItem *
147 __glutGetMenuItem(GLUTmenu * menu, Window win, int *which)
148 {
149 GLUTmenuItem *item;
150 int i;
151
152 i = menu->num;
153 item = menu->list;
154 while (item) {
155 if (item->win == win) {
156 *which = i;
157 return item;
158 }
159 if (item->isTrigger) {
160 GLUTmenuItem *subitem;
161
162 subitem = __glutGetMenuItem(menuList[item->value],
163 win, which);
164 if (subitem) {
165 return subitem;
166 }
167 }
168 i--;
169 item = item->next;
170 }
171 return NULL;
172 }
173
174 GLUTmenu *
175 __glutGetMenu(Window win)
176 {
177 GLUTmenu *menu;
178
179 menu = __glutMappedMenu;
180 while (menu) {
181 if (win == menu->win) {
182 return menu;
183 }
184 menu = menu->cascade;
185 }
186 return NULL;
187 }
188
189 GLUTmenu *
190 __glutGetMenuByNum(int menunum)
191 {
192 if (menunum < 1 || menunum > menuListSize) {
193 return NULL;
194 }
195 return menuList[menunum - 1];
196 }
197
198 static int
199 getUnusedMenuSlot(void)
200 {
201 int i;
202
203 /* Look for allocated, unused slot. */
204 for (i = 0; i < menuListSize; i++) {
205 if (!menuList[i]) {
206 return i;
207 }
208 }
209 /* Allocate a new slot. */
210 menuListSize++;
211 if (menuList) {
212 menuList = (GLUTmenu **)
213 realloc(menuList, menuListSize * sizeof(GLUTmenu *));
214 } else {
215 /* XXX Some realloc's do not correctly perform a malloc
216 when asked to perform a realloc on a NULL pointer,
217 though the ANSI C library spec requires this. */
218 menuList = (GLUTmenu **) malloc(sizeof(GLUTmenu *));
219 }
220 if (!menuList) {
221 __glutFatalError("out of memory.");
222 }
223 menuList[menuListSize - 1] = NULL;
224 return menuListSize - 1;
225 }
226
227 static void
228 menuModificationError(void)
229 {
230 /* XXX Remove the warning after GLUT 3.0. */
231 __glutWarning("The following is a new check for GLUT 3.0; update your code.");
232 __glutFatalError("menu manipulation not allowed while menus in use.");
233 }
234
235 int GLUTAPIENTRY
236 glutCreateMenu(GLUTselectCB selectFunc)
237 {
238 GLUTmenu *menu;
239 int menuid;
240
241 if (__glutMappedMenu) {
242 menuModificationError();
243 }
244 menuid = getUnusedMenuSlot();
245 menu = (GLUTmenu *) malloc(sizeof(GLUTmenu));
246 if (!menu) {
247 __glutFatalError("out of memory.");
248 }
249 menu->id = menuid;
250 menu->num = 0;
251 menu->submenus = 0;
252 menu->select = selectFunc;
253 menu->list = NULL;
254 menu->cascade = NULL;
255 menu->highlighted = NULL;
256 menu->anchor = NULL;
257 //todo
258 // menu->win = (HWND) CreatePopupMenu();
259 menuList[menuid] = menu;
260 __glutSetMenu(menu);
261 return menuid + 1;
262 }
263
264
265 void GLUTAPIENTRY
266 glutDestroyMenu(int menunum)
267 {
268 GLUTmenu *menu = __glutGetMenuByNum(menunum);
269 GLUTmenuItem *item, *next;
270
271 if (__glutMappedMenu) {
272 menuModificationError();
273 }
274 assert(menu->id == menunum - 1);
275 //todo DestroyMenu( (HMENU) menu->win);
276 menuList[menunum - 1] = NULL;
277 /* free all menu entries */
278 item = menu->list;
279 while (item) {
280 assert(item->menu == menu);
281 next = item->next;
282 free(item->label);
283 free(item);
284 item = next;
285 }
286 if (__glutCurrentMenu == menu) {
287 __glutCurrentMenu = NULL;
288 }
289 free(menu);
290 }
291
292 int GLUTAPIENTRY
293 glutGetMenu(void)
294 {
295 if (__glutCurrentMenu) {
296 return __glutCurrentMenu->id + 1;
297 } else {
298 return 0;
299 }
300 }
301
302 void GLUTAPIENTRY
303 glutSetMenu(int menuid)
304 {
305 GLUTmenu *menu;
306
307 if (menuid < 1 || menuid > menuListSize) {
308 __glutWarning("glutSetMenu attempted on bogus menu.");
309 return;
310 }
311 menu = menuList[menuid - 1];
312 if (!menu) {
313 __glutWarning("glutSetMenu attempted on bogus menu.");
314 return;
315 }
316 __glutSetMenu(menu);
317 }
318
319 static void
320 setMenuItem(GLUTmenuItem * item, const char *label,
321 int value, Bool isTrigger)
322 {
323 GLUTmenu *menu;
324
325 menu = item->menu;
326 item->label = __glutStrdup(label);
327 if (!item->label) {
328 __glutFatalError("out of memory.");
329 }
330 item->isTrigger = isTrigger;
331 item->len = (int) strlen(label);
332 item->value = value;
333 item->unique = uniqueMenuHandler++;
334 //todo
335 // if (isTrigger) {
336 // AppendMenu((HMENU) menu->win, MF_POPUP, (UINT)item->win, label);
337 // } else {
338 // AppendMenu((HMENU) menu->win, MF_STRING, item->unique, label);
339 // }
340 }
341
342 void GLUTAPIENTRY
343 glutAddMenuEntry(const char *label, int value)
344 {
345 GLUTmenuItem *entry;
346
347 if (__glutMappedMenu) {
348 menuModificationError();
349 }
350 entry = (GLUTmenuItem *) malloc(sizeof(GLUTmenuItem));
351 if (!entry) {
352 __glutFatalError("out of memory.");
353 }
354 entry->menu = __glutCurrentMenu;
355 setMenuItem(entry, label, value, FALSE);
356 __glutCurrentMenu->num++;
357 entry->next = __glutCurrentMenu->list;
358 __glutCurrentMenu->list = entry;
359 }
360
361 void GLUTAPIENTRY
362 glutAddSubMenu(const char *label, int menu)
363 {
364 GLUTmenuItem *submenu;
365 GLUTmenu *popupmenu;
366
367 if (__glutMappedMenu) {
368 menuModificationError();
369 }
370 submenu = (GLUTmenuItem *) malloc(sizeof(GLUTmenuItem));
371 if (!submenu) {
372 __glutFatalError("out of memory.");
373 }
374 __glutCurrentMenu->submenus++;
375 submenu->menu = __glutCurrentMenu;
376 popupmenu = __glutGetMenuByNum(menu);
377 if (popupmenu) {
378 submenu->win = popupmenu->win;
379 }
380 setMenuItem(submenu, label, /* base 0 */ menu - 1, TRUE);
381 __glutCurrentMenu->num++;
382 submenu->next = __glutCurrentMenu->list;
383 __glutCurrentMenu->list = submenu;
384 }
385
386 void GLUTAPIENTRY
387 glutChangeToMenuEntry(int num, const char *label, int value)
388 {
389 GLUTmenuItem *item;
390 int i;
391
392 if (__glutMappedMenu) {
393 menuModificationError();
394 }
395 i = __glutCurrentMenu->num;
396 item = __glutCurrentMenu->list;
397 while (item) {
398 if (i == num) {
399 if (item->isTrigger) {
400 /* If changing a submenu trigger to a menu entry, we
401 need to account for submenus. */
402 item->menu->submenus--;
403 /* Nuke the Win32 menu. */
404 //todo
405 // DestroyMenu((HMENU) item->win);
406 }
407 free(item->label);
408
409 item->label = strdup(label);
410 if (!item->label)
411 __glutFatalError("out of memory");
412 item->isTrigger = FALSE;
413 item->len = (int) strlen(label);
414 item->value = value;
415 item->unique = uniqueMenuHandler++;
416 //todo
417 // ModifyMenu((HMENU) __glutCurrentMenu->win, (UINT) i - 1,
418 // MF_BYPOSITION | MFT_STRING, item->unique, label);
419
420 return;
421 }
422 i--;
423 item = item->next;
424 }
425 __glutWarning("Current menu has no %d item.", num);
426 }
427
428 void GLUTAPIENTRY
429 glutChangeToSubMenu(int num, const char *label, int menu)
430 {
431 GLUTmenu *popupmenu;
432 GLUTmenuItem *item;
433 int i;
434
435 if (__glutMappedMenu) {
436 menuModificationError();
437 }
438 i = __glutCurrentMenu->num;
439 item = __glutCurrentMenu->list;
440 while (item) {
441 if (i == num) {
442 if (!item->isTrigger) {
443 /* If changing a menu entry to as submenu trigger, we
444 need to account for submenus. */
445 item->menu->submenus++;
446 //todo
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 //todo
462 // ModifyMenu((HMENU) __glutCurrentMenu->win, (UINT) i - 1,
463 // MF_BYPOSITION | MF_POPUP, (UINT) item->win, label);
464 return;
465 }
466 i--;
467 item = item->next;
468 }
469 __glutWarning("Current menu has no %d item.", num);
470 }
471
472 void GLUTAPIENTRY
473 glutRemoveMenuItem(int num)
474 {
475 GLUTmenuItem *item, **prev;
476 int i;
477
478 if (__glutMappedMenu) {
479 menuModificationError();
480 }
481 i = __glutCurrentMenu->num;
482 prev = &__glutCurrentMenu->list;
483 item = __glutCurrentMenu->list;
484 while (item) {
485 if (i == num) {
486 /* Found the menu item in list to remove. */
487 __glutCurrentMenu->num--;
488
489 /* Patch up menu's item list. */
490 *prev = item->next;
491 //todo
492 // RemoveMenu((HMENU) __glutCurrentMenu->win, (UINT) i - 1, MF_BYPOSITION);
493
494 free(item->label);
495 free(item);
496 return;
497 }
498 i--;
499 prev = &item->next;
500 item = item->next;
501 }
502 __glutWarning("Current menu has no %d item.", num);
503 }
504
505 void GLUTAPIENTRY
506 glutAttachMenu(int button)
507 {
508 if (__glutCurrentWindow == __glutGameModeWindow) {
509 __glutWarning("cannot attach menus in game mode.");
510 return;
511 }
512 if (__glutMappedMenu) {
513 menuModificationError();
514 }
515 if (__glutCurrentWindow->menu[button] < 1) {
516 __glutCurrentWindow->buttonUses++;
517 }
518 __glutCurrentWindow->menu[button] = __glutCurrentMenu->id + 1;
519 }
520
521 void GLUTAPIENTRY
522 glutDetachMenu(int button)
523 {
524 if (__glutMappedMenu) {
525 menuModificationError();
526 }
527 if (__glutCurrentWindow->menu[button] > 0) {
528 __glutCurrentWindow->buttonUses--;
529 __glutCurrentWindow->menu[button] = 0;
530 }
531 }
532