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