1 | /* $Id: win32_menu.c,v 1.6 2000-03-11 17:07:46 sandervl Exp $ */
|
---|
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 | extern GLUTmenu *__glutMappedMenu;
|
---|
23 | extern 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 GLAPIENTRY
|
---|
33 | glutMenuStateFunc(GLUTmenuStateCB menuStateFunc)
|
---|
34 | {
|
---|
35 | __glutMenuStatusFunc = (GLUTmenuStatusCB) menuStateFunc;
|
---|
36 | }
|
---|
37 |
|
---|
38 | void GLAPIENTRY
|
---|
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(menu->win, TPM_LEFTALIGN |
|
---|
100 | (__glutMenuButton == TPM_RIGHTBUTTON ?
|
---|
101 | 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 GLAPIENTRY
|
---|
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 | menu->win = CreatePopupMenu();
|
---|
258 | menuList[menuid] = menu;
|
---|
259 | __glutSetMenu(menu);
|
---|
260 | return menuid + 1;
|
---|
261 | }
|
---|
262 |
|
---|
263 | void GLAPIENTRY
|
---|
264 | glutDestroyMenu(int menunum)
|
---|
265 | {
|
---|
266 | GLUTmenu *menu = __glutGetMenuByNum(menunum);
|
---|
267 | GLUTmenuItem *item, *next;
|
---|
268 |
|
---|
269 | if (__glutMappedMenu) {
|
---|
270 | menuModificationError();
|
---|
271 | }
|
---|
272 | assert(menu->id == menunum - 1);
|
---|
273 | DestroyMenu(menu->win);
|
---|
274 | menuList[menunum - 1] = NULL;
|
---|
275 | /* free all menu entries */
|
---|
276 | item = menu->list;
|
---|
277 | while (item) {
|
---|
278 | assert(item->menu == menu);
|
---|
279 | next = item->next;
|
---|
280 | free(item->label);
|
---|
281 | free(item);
|
---|
282 | item = next;
|
---|
283 | }
|
---|
284 | if (__glutCurrentMenu == menu) {
|
---|
285 | __glutCurrentMenu = NULL;
|
---|
286 | }
|
---|
287 | free(menu);
|
---|
288 | }
|
---|
289 |
|
---|
290 | int GLAPIENTRY
|
---|
291 | glutGetMenu(void)
|
---|
292 | {
|
---|
293 | if (__glutCurrentMenu) {
|
---|
294 | return __glutCurrentMenu->id + 1;
|
---|
295 | } else {
|
---|
296 | return 0;
|
---|
297 | }
|
---|
298 | }
|
---|
299 |
|
---|
300 | void GLAPIENTRY
|
---|
301 | glutSetMenu(int menuid)
|
---|
302 | {
|
---|
303 | GLUTmenu *menu;
|
---|
304 |
|
---|
305 | if (menuid < 1 || menuid > menuListSize) {
|
---|
306 | __glutWarning("glutSetMenu attempted on bogus menu.");
|
---|
307 | return;
|
---|
308 | }
|
---|
309 | menu = menuList[menuid - 1];
|
---|
310 | if (!menu) {
|
---|
311 | __glutWarning("glutSetMenu attempted on bogus menu.");
|
---|
312 | return;
|
---|
313 | }
|
---|
314 | __glutSetMenu(menu);
|
---|
315 | }
|
---|
316 |
|
---|
317 | static void
|
---|
318 | setMenuItem(GLUTmenuItem * item, const char *label,
|
---|
319 | int value, Bool isTrigger)
|
---|
320 | {
|
---|
321 | GLUTmenu *menu;
|
---|
322 |
|
---|
323 | menu = item->menu;
|
---|
324 | item->label = __glutStrdup(label);
|
---|
325 | if (!item->label) {
|
---|
326 | __glutFatalError("out of memory.");
|
---|
327 | }
|
---|
328 | item->isTrigger = isTrigger;
|
---|
329 | item->len = (int) strlen(label);
|
---|
330 | item->value = value;
|
---|
331 | item->unique = uniqueMenuHandler++;
|
---|
332 | if (isTrigger) {
|
---|
333 | AppendMenu(menu->win, MF_POPUP, (UINT)item->win, label);
|
---|
334 | } else {
|
---|
335 | AppendMenu(menu->win, MF_STRING, item->unique, label);
|
---|
336 | }
|
---|
337 | }
|
---|
338 |
|
---|
339 | void GLAPIENTRY
|
---|
340 | glutAddMenuEntry(const char *label, int value)
|
---|
341 | {
|
---|
342 | GLUTmenuItem *entry;
|
---|
343 |
|
---|
344 | if (__glutMappedMenu) {
|
---|
345 | menuModificationError();
|
---|
346 | }
|
---|
347 | entry = (GLUTmenuItem *) malloc(sizeof(GLUTmenuItem));
|
---|
348 | if (!entry) {
|
---|
349 | __glutFatalError("out of memory.");
|
---|
350 | }
|
---|
351 | entry->menu = __glutCurrentMenu;
|
---|
352 | setMenuItem(entry, label, value, FALSE);
|
---|
353 | __glutCurrentMenu->num++;
|
---|
354 | entry->next = __glutCurrentMenu->list;
|
---|
355 | __glutCurrentMenu->list = entry;
|
---|
356 | }
|
---|
357 |
|
---|
358 | void GLAPIENTRY
|
---|
359 | glutAddSubMenu(const char *label, int menu)
|
---|
360 | {
|
---|
361 | GLUTmenuItem *submenu;
|
---|
362 | GLUTmenu *popupmenu;
|
---|
363 |
|
---|
364 | if (__glutMappedMenu) {
|
---|
365 | menuModificationError();
|
---|
366 | }
|
---|
367 | submenu = (GLUTmenuItem *) malloc(sizeof(GLUTmenuItem));
|
---|
368 | if (!submenu) {
|
---|
369 | __glutFatalError("out of memory.");
|
---|
370 | }
|
---|
371 | __glutCurrentMenu->submenus++;
|
---|
372 | submenu->menu = __glutCurrentMenu;
|
---|
373 | popupmenu = __glutGetMenuByNum(menu);
|
---|
374 | if (popupmenu) {
|
---|
375 | submenu->win = popupmenu->win;
|
---|
376 | }
|
---|
377 | setMenuItem(submenu, label, /* base 0 */ menu - 1, TRUE);
|
---|
378 | __glutCurrentMenu->num++;
|
---|
379 | submenu->next = __glutCurrentMenu->list;
|
---|
380 | __glutCurrentMenu->list = submenu;
|
---|
381 | }
|
---|
382 |
|
---|
383 | void GLAPIENTRY
|
---|
384 | glutChangeToMenuEntry(int num, const char *label, int value)
|
---|
385 | {
|
---|
386 | GLUTmenuItem *item;
|
---|
387 | int i;
|
---|
388 |
|
---|
389 | if (__glutMappedMenu) {
|
---|
390 | menuModificationError();
|
---|
391 | }
|
---|
392 | i = __glutCurrentMenu->num;
|
---|
393 | item = __glutCurrentMenu->list;
|
---|
394 | while (item) {
|
---|
395 | if (i == num) {
|
---|
396 | if (item->isTrigger) {
|
---|
397 | /* If changing a submenu trigger to a menu entry, we
|
---|
398 | need to account for submenus. */
|
---|
399 | item->menu->submenus--;
|
---|
400 | /* Nuke the Win32 menu. */
|
---|
401 | DestroyMenu(item->win);
|
---|
402 | }
|
---|
403 | free(item->label);
|
---|
404 |
|
---|
405 | item->label = strdup(label);
|
---|
406 | if (!item->label)
|
---|
407 | __glutFatalError("out of memory");
|
---|
408 | item->isTrigger = FALSE;
|
---|
409 | item->len = (int) strlen(label);
|
---|
410 | item->value = value;
|
---|
411 | item->unique = uniqueMenuHandler++;
|
---|
412 | ModifyMenu(__glutCurrentMenu->win, (UINT) i - 1,
|
---|
413 | MF_BYPOSITION | MFT_STRING, item->unique, label);
|
---|
414 | return;
|
---|
415 | }
|
---|
416 | i--;
|
---|
417 | item = item->next;
|
---|
418 | }
|
---|
419 | __glutWarning("Current menu has no %d item.", num);
|
---|
420 | }
|
---|
421 |
|
---|
422 | void GLAPIENTRY
|
---|
423 | glutChangeToSubMenu(int num, const char *label, int menu)
|
---|
424 | {
|
---|
425 | GLUTmenu *popupmenu;
|
---|
426 | GLUTmenuItem *item;
|
---|
427 | int i;
|
---|
428 |
|
---|
429 | if (__glutMappedMenu) {
|
---|
430 | menuModificationError();
|
---|
431 | }
|
---|
432 | i = __glutCurrentMenu->num;
|
---|
433 | item = __glutCurrentMenu->list;
|
---|
434 | while (item) {
|
---|
435 | if (i == num) {
|
---|
436 | if (!item->isTrigger) {
|
---|
437 | /* If changing a menu entry to as submenu trigger, we
|
---|
438 | need to account for submenus. */
|
---|
439 | item->menu->submenus++;
|
---|
440 | item->win = CreatePopupMenu();
|
---|
441 | }
|
---|
442 | free(item->label);
|
---|
443 |
|
---|
444 | item->label = strdup(label);
|
---|
445 | if (!item->label)
|
---|
446 | __glutFatalError("out of memory");
|
---|
447 | item->isTrigger = TRUE;
|
---|
448 | item->len = (int) strlen(label);
|
---|
449 | item->value = menu - 1;
|
---|
450 | item->unique = uniqueMenuHandler++;
|
---|
451 | popupmenu = __glutGetMenuByNum(menu);
|
---|
452 | if (popupmenu)
|
---|
453 | item->win = popupmenu->win;
|
---|
454 | ModifyMenu(__glutCurrentMenu->win, (UINT) i - 1,
|
---|
455 | MF_BYPOSITION | MF_POPUP, (UINT) item->win, label);
|
---|
456 | return;
|
---|
457 | }
|
---|
458 | i--;
|
---|
459 | item = item->next;
|
---|
460 | }
|
---|
461 | __glutWarning("Current menu has no %d item.", num);
|
---|
462 | }
|
---|
463 |
|
---|
464 | void GLAPIENTRY
|
---|
465 | glutRemoveMenuItem(int num)
|
---|
466 | {
|
---|
467 | GLUTmenuItem *item, **prev;
|
---|
468 | int i;
|
---|
469 |
|
---|
470 | if (__glutMappedMenu) {
|
---|
471 | menuModificationError();
|
---|
472 | }
|
---|
473 | i = __glutCurrentMenu->num;
|
---|
474 | prev = &__glutCurrentMenu->list;
|
---|
475 | item = __glutCurrentMenu->list;
|
---|
476 | while (item) {
|
---|
477 | if (i == num) {
|
---|
478 | /* Found the menu item in list to remove. */
|
---|
479 | __glutCurrentMenu->num--;
|
---|
480 |
|
---|
481 | /* Patch up menu's item list. */
|
---|
482 | *prev = item->next;
|
---|
483 |
|
---|
484 | RemoveMenu(__glutCurrentMenu->win, (UINT) i - 1, MF_BYPOSITION);
|
---|
485 |
|
---|
486 | free(item->label);
|
---|
487 | free(item);
|
---|
488 | return;
|
---|
489 | }
|
---|
490 | i--;
|
---|
491 | prev = &item->next;
|
---|
492 | item = item->next;
|
---|
493 | }
|
---|
494 | __glutWarning("Current menu has no %d item.", num);
|
---|
495 | }
|
---|
496 |
|
---|
497 | void GLAPIENTRY
|
---|
498 | glutAttachMenu(int button)
|
---|
499 | {
|
---|
500 | if (__glutCurrentWindow == __glutGameModeWindow) {
|
---|
501 | __glutWarning("cannot attach menus in game mode.");
|
---|
502 | return;
|
---|
503 | }
|
---|
504 | if (__glutMappedMenu) {
|
---|
505 | menuModificationError();
|
---|
506 | }
|
---|
507 | if (__glutCurrentWindow->menu[button] < 1) {
|
---|
508 | __glutCurrentWindow->buttonUses++;
|
---|
509 | }
|
---|
510 | __glutCurrentWindow->menu[button] = __glutCurrentMenu->id + 1;
|
---|
511 | }
|
---|
512 |
|
---|
513 | void GLAPIENTRY
|
---|
514 | glutDetachMenu(int button)
|
---|
515 | {
|
---|
516 | if (__glutMappedMenu) {
|
---|
517 | menuModificationError();
|
---|
518 | }
|
---|
519 | if (__glutCurrentWindow->menu[button] > 0) {
|
---|
520 | __glutCurrentWindow->buttonUses--;
|
---|
521 | __glutCurrentWindow->menu[button] = 0;
|
---|
522 | }
|
---|
523 | }
|
---|
524 |
|
---|