4 * Pull-down menu creation and handling.
6 * Copyright (c) 1999-2000 Pawel W. Olszta. All Rights Reserved.
7 * Written by Pawel W. Olszta, <olszta@sourceforge.net>
8 * Creation date: Thu Dec 16 1999
10 * Permission is hereby granted, free of charge, to any person obtaining a
11 * copy of this software and associated documentation files (the "Software"),
12 * to deal in the Software without restriction, including without limitation
13 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
14 * and/or sell copies of the Software, and to permit persons to whom the
15 * Software is furnished to do so, subject to the following conditions:
17 * The above copyright notice and this permission notice shall be included
18 * in all copies or substantial portions of the Software.
20 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
21 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
23 * PAWEL W. OLSZTA BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
24 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
25 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
28 #define FREEGLUT_BUILDING_LIB
29 #include <GL/freeglut.h>
30 #include "fg_internal.h"
33 /* -- DEFINITIONS ---------------------------------------------------------- */
36 * FREEGLUT_MENU_FONT can be any freeglut bitmapped font.
37 * (Stroked fonts would not be out of the question, but we'd need to alter
38 * code, since GLUT (hence freeglut) does not quite unify stroked and
39 * bitmapped font handling.)
40 * Old UNIX/X11 GLUT (BSD, UNIX, IRIX, LINUX, HPUX, ...) used a system
41 * font best approximated by an 18-pixel HELVETICA, I think. MS-WINDOWS
42 * GLUT used something closest to the 8x13 fixed-width font. (Old
43 * GLUT apparently uses host-system menus rather than building its own.
44 * freeglut is building its own menus from scratch.)
46 * FREEGLUT_MENUENTRY_HEIGHT gives the height of ONE menu box. This should
47 * be the distances between two adjacent menu entries. It should scale
48 * automatically with the font choice, so you needn't alter it---unless you
51 * FREEGLUT_MENU_BORDER says how many pixels to allow around the edge of a
52 * menu. (It also seems to be the same as the number of pixels used as
53 * a border around *items* to separate them from neighbors. John says
54 * that that wasn't the original intent...if not, perhaps we need another
55 * symbolic constant, FREEGLUT_MENU_ITEM_BORDER, or such.)
57 /* See platform-specific header files for menu font and color definitions */
59 #define FREEGLUT_MENUENTRY_HEIGHT(font) (glutBitmapHeight(font) + \
61 #define FREEGLUT_MENU_BORDER 2
65 * These variables are for rendering the freeglut menu items.
67 * The choices are fore- and background, with and without h for Highlighting.
68 * Old GLUT appeared to be system-dependant for its colors (sigh) so we are
69 * too. These variables should be stuffed into global state and initialized
70 * via the glutInit*() system.
72 static float menu_pen_fore [4] = FREEGLUT_MENU_PEN_FORE_COLORS ;
73 static float menu_pen_back [4] = FREEGLUT_MENU_PEN_BACK_COLORS ;
74 static float menu_pen_hfore [4] = FREEGLUT_MENU_PEN_HFORE_COLORS;
75 static float menu_pen_hback [4] = FREEGLUT_MENU_PEN_HBACK_COLORS;
78 extern GLvoid fgPlatformGetGameModeVMaxExtent( SFG_Window* window, int* x, int* y );
79 extern void fghPlatformGetCursorPos(const SFG_Window *window, GLboolean client, SFG_XYUse *mouse_pos);
80 extern SFG_Font* fghFontByID( void* font );
82 /* -- PRIVATE FUNCTIONS ---------------------------------------------------- */
85 * Private function to find a menu entry by index
87 static SFG_MenuEntry *fghFindMenuEntry( SFG_Menu* menu, int index )
92 for( entry = (SFG_MenuEntry *)menu->Entries.First;
94 entry = (SFG_MenuEntry *)entry->Node.Next )
105 * Deactivates a menu pointed by the function argument.
107 static void fghDeactivateSubMenu( SFG_MenuEntry *menuEntry )
109 SFG_MenuEntry *subMenuIter;
110 /* Hide the present menu's window */
111 fgSetWindow( menuEntry->SubMenu->Window );
114 /* Forget about having that menu active anymore, now: */
115 menuEntry->SubMenu->Window->ActiveMenu = NULL;
116 menuEntry->SubMenu->IsActive = GL_FALSE;
117 menuEntry->SubMenu->ActiveEntry = NULL;
119 /* Hide all submenu windows, and the root menu's window. */
120 for ( subMenuIter = (SFG_MenuEntry *)menuEntry->SubMenu->Entries.First;
122 subMenuIter = (SFG_MenuEntry *)subMenuIter->Node.Next )
124 subMenuIter->IsActive = GL_FALSE;
126 /* Is that an active submenu by any case? */
127 if( subMenuIter->SubMenu )
128 fghDeactivateSubMenu( subMenuIter );
131 fgSetWindow ( menuEntry->SubMenu->ParentWindow ) ;
135 * Private function to get the virtual maximum screen extent
137 static GLvoid fghGetVMaxExtent( SFG_Window* window, int* x, int* y )
139 if( fgStructure.GameModeWindow )
140 fgPlatformGetGameModeVMaxExtent ( window, x, y );
143 *x = fgDisplay.ScreenWidth;
144 *y = fgDisplay.ScreenHeight;
149 * Private function to check for the current menu/sub menu activity state
151 static GLboolean fghCheckMenuStatus( SFG_Menu* menu )
153 SFG_MenuEntry* menuEntry;
156 /* First of all check any of the active sub menus... */
157 for( menuEntry = (SFG_MenuEntry *)menu->Entries.First;
159 menuEntry = (SFG_MenuEntry *)menuEntry->Node.Next )
161 if( menuEntry->SubMenu && menuEntry->IsActive )
164 * OK, have the sub-menu checked, too. If it returns GL_TRUE, it
165 * will mean that it caught the mouse cursor and we do not need
166 * to regenerate the activity list, and so our parents do...
168 GLboolean return_status;
170 menuEntry->SubMenu->Window->State.MouseX =
171 menu->Window->State.MouseX + menu->X - menuEntry->SubMenu->X;
172 menuEntry->SubMenu->Window->State.MouseY =
173 menu->Window->State.MouseY + menu->Y - menuEntry->SubMenu->Y;
174 return_status = fghCheckMenuStatus( menuEntry->SubMenu );
181 /* That much about our sub menus, let's get to checking the current menu: */
182 x = menu->Window->State.MouseX;
183 y = menu->Window->State.MouseY;
185 /* Check if the mouse cursor is contained within the current menu box */
186 if( ( x >= FREEGLUT_MENU_BORDER ) &&
187 ( x < menu->Width - FREEGLUT_MENU_BORDER ) &&
188 ( y >= FREEGLUT_MENU_BORDER ) &&
189 ( y < menu->Height - FREEGLUT_MENU_BORDER ) )
191 int menuID = ( y - FREEGLUT_MENU_BORDER ) / FREEGLUT_MENUENTRY_HEIGHT(menu->Font);
193 /* The mouse cursor is somewhere over our box, check it out. */
194 menuEntry = fghFindMenuEntry( menu, menuID + 1 );
195 FREEGLUT_INTERNAL_ERROR_EXIT( menuEntry, "Cannot find menu entry",
196 "fghCheckMenuStatus" );
198 menuEntry->IsActive = GL_TRUE;
199 menuEntry->Ordinal = menuID;
202 * If this is not the same as the last active menu entry, deactivate
203 * the previous entry. Specifically, if the previous active entry
204 * was a submenu then deactivate it.
206 if( menu->ActiveEntry && ( menuEntry != menu->ActiveEntry ) )
207 if( menu->ActiveEntry->SubMenu )
208 fghDeactivateSubMenu( menu->ActiveEntry );
210 if( menuEntry != menu->ActiveEntry )
212 menu->Window->State.Redisplay = GL_TRUE;
213 if( menu->ActiveEntry )
214 menu->ActiveEntry->IsActive = GL_FALSE;
217 menu->ActiveEntry = menuEntry;
218 menu->IsActive = GL_TRUE; /* XXX Do we need this? */
221 * OKi, we have marked that entry as active, but it would be also
222 * nice to have its contents updated, in case it's a sub menu.
223 * Also, ignore the return value of the check function:
225 if( menuEntry->SubMenu )
227 if ( ! menuEntry->SubMenu->IsActive )
230 SFG_Window *current_window = fgStructure.CurrentWindow;
232 /* Set up the initial menu position now... */
233 menuEntry->SubMenu->IsActive = GL_TRUE;
235 /* Set up the initial submenu position now: */
236 fghGetVMaxExtent(menu->ParentWindow, &max_x, &max_y);
237 menuEntry->SubMenu->X = menu->X + menu->Width;
238 menuEntry->SubMenu->Y = menu->Y +
239 menuEntry->Ordinal * FREEGLUT_MENUENTRY_HEIGHT(menu->Font);
241 if( menuEntry->SubMenu->X + menuEntry->SubMenu->Width > max_x )
242 menuEntry->SubMenu->X = menu->X - menuEntry->SubMenu->Width;
244 if( menuEntry->SubMenu->Y + menuEntry->SubMenu->Height > max_y )
246 menuEntry->SubMenu->Y -= ( menuEntry->SubMenu->Height -
247 FREEGLUT_MENUENTRY_HEIGHT(menu->Font) -
248 2 * FREEGLUT_MENU_BORDER );
249 if( menuEntry->SubMenu->Y < 0 )
250 menuEntry->SubMenu->Y = 0;
253 fgSetWindow( menuEntry->SubMenu->Window );
254 glutPositionWindow( menuEntry->SubMenu->X,
255 menuEntry->SubMenu->Y );
256 glutReshapeWindow( menuEntry->SubMenu->Width,
257 menuEntry->SubMenu->Height );
260 menuEntry->SubMenu->Window->ActiveMenu = menuEntry->SubMenu;
261 fgSetWindow( current_window );
262 menuEntry->SubMenu->Window->State.MouseX =
263 x + menu->X - menuEntry->SubMenu->X;
264 menuEntry->SubMenu->Window->State.MouseY =
265 y + menu->Y - menuEntry->SubMenu->Y;
266 fghCheckMenuStatus( menuEntry->SubMenu );
269 /* Activate it because its parent entry is active */
270 menuEntry->SubMenu->IsActive = GL_TRUE; /* XXX Do we need this? */
273 /* Report back that we have caught the menu cursor */
277 /* Looks like the menu cursor is somewhere else... */
278 if( menu->ActiveEntry && menu->ActiveEntry->IsActive &&
279 ( !menu->ActiveEntry->SubMenu ||
280 !menu->ActiveEntry->SubMenu->IsActive ) )
282 menu->Window->State.Redisplay = GL_TRUE;
283 menu->ActiveEntry->IsActive = GL_FALSE;
284 menu->ActiveEntry = NULL;
291 * Displays a menu box and all of its submenus (if they are active)
293 static void fghDisplayMenuBox( SFG_Menu* menu )
295 SFG_MenuEntry *menuEntry;
297 int border = FREEGLUT_MENU_BORDER;
300 * Have the menu box drawn first. The +- values are
301 * here just to make it more nice-looking...
303 /* a non-black dark version of the below. */
304 glColor4f( 1.0f, 1.0f, 1.0f, 1.0f );
305 glBegin( GL_QUAD_STRIP );
306 glVertex2i( menu->Width , 0 );
307 glVertex2i( menu->Width - border, border);
309 glVertex2i( border, border);
310 glVertex2i( 0 , menu->Height );
311 glVertex2i( border, menu->Height - border);
314 /* a non-black dark version of the below. */
315 glColor4f( 0.5f, 0.5f, 0.5f, 1.0f );
316 glBegin( GL_QUAD_STRIP );
317 glVertex2i( 0 , menu->Height );
318 glVertex2i( border, menu->Height - border);
319 glVertex2i( menu->Width , menu->Height );
320 glVertex2i( menu->Width - border, menu->Height - border);
321 glVertex2i( menu->Width , 0 );
322 glVertex2i( menu->Width - border, border);
325 glColor4fv( menu_pen_back );
327 glVertex2i( border, border);
328 glVertex2i( menu->Width - border, border);
329 glVertex2i( menu->Width - border, menu->Height - border);
330 glVertex2i( border, menu->Height - border);
333 /* Check if any of the submenus is currently active... */
334 for( menuEntry = (SFG_MenuEntry *)menu->Entries.First;
336 menuEntry = (SFG_MenuEntry *)menuEntry->Node.Next )
338 /* Has the menu been marked as active, maybe? */
339 if( menuEntry->IsActive )
342 * That's truly right, and we need to have it highlighted.
343 * There is an assumption that mouse cursor didn't move
344 * since the last check of menu activity state:
346 int menuID = menuEntry->Ordinal;
348 /* So have the highlight drawn... */
349 glColor4fv( menu_pen_hback );
352 (menuID + 0)*FREEGLUT_MENUENTRY_HEIGHT(menu->Font) + border );
353 glVertex2i( menu->Width - border,
354 (menuID + 0)*FREEGLUT_MENUENTRY_HEIGHT(menu->Font) + border );
355 glVertex2i( menu->Width - border,
356 (menuID + 1)*FREEGLUT_MENUENTRY_HEIGHT(menu->Font) + border );
358 (menuID + 1)*FREEGLUT_MENUENTRY_HEIGHT(menu->Font) + border );
363 /* Print the menu entries now... */
365 glColor4fv( menu_pen_fore );
367 for( menuEntry = (SFG_MenuEntry *)menu->Entries.First, i = 0;
369 menuEntry = (SFG_MenuEntry *)menuEntry->Node.Next, ++i )
371 /* If the menu entry is active, set the color to white */
372 if( menuEntry->IsActive )
373 glColor4fv( menu_pen_hfore );
375 /* Move the raster into position... */
376 /* Try to center the text - JCJ 31 July 2003*/
379 ( i + 1 )*FREEGLUT_MENUENTRY_HEIGHT(menu->Font) -
380 ( int )( FREEGLUT_MENUENTRY_HEIGHT(menu->Font)*0.3 - border )
383 /* Have the label drawn, character after character: */
384 glutBitmapString( menu->Font,
385 (unsigned char *)menuEntry->Text);
387 /* If it's a submenu, draw a right arrow */
388 if( menuEntry->SubMenu )
390 int width = glutBitmapWidth( menu->Font, '_' );
391 int x_base = menu->Width - 2 - width;
392 int y_base = i*FREEGLUT_MENUENTRY_HEIGHT(menu->Font) + border;
393 glBegin( GL_TRIANGLES );
394 glVertex2i( x_base, y_base + 2*border);
395 glVertex2i( menu->Width - 2, y_base +
396 ( FREEGLUT_MENUENTRY_HEIGHT(menu->Font) + border) / 2 );
397 glVertex2i( x_base, y_base + FREEGLUT_MENUENTRY_HEIGHT(menu->Font) - border );
401 /* If the menu entry is active, reset the color */
402 if( menuEntry->IsActive )
403 glColor4fv( menu_pen_fore );
408 * Private static function to set the parent window of a submenu and all
411 static void fghSetMenuParentWindow( SFG_Window *window, SFG_Menu *menu )
413 SFG_MenuEntry *menuEntry;
415 menu->ParentWindow = window;
417 for( menuEntry = ( SFG_MenuEntry * )menu->Entries.First;
419 menuEntry = ( SFG_MenuEntry * )menuEntry->Node.Next )
420 if( menuEntry->SubMenu )
421 fghSetMenuParentWindow( window, menuEntry->SubMenu );
425 * Function to check for menu entry selection on menu deactivation
427 static void fghExecuteMenuCallback( SFG_Menu* menu )
429 SFG_MenuEntry *menuEntry;
431 /* First of all check any of the active sub menus... */
432 for( menuEntry = (SFG_MenuEntry *)menu->Entries.First;
434 menuEntry = (SFG_MenuEntry *)menuEntry->Node.Next)
436 if( menuEntry->IsActive )
438 if( menuEntry->SubMenu )
439 fghExecuteMenuCallback( menuEntry->SubMenu );
443 SFG_Menu *save_menu = fgStructure.CurrentMenu;
444 fgStructure.CurrentMenu = menu;
445 menu->Callback( menuEntry->ID );
446 fgStructure.CurrentMenu = save_menu;
456 * Displays the currently active menu for the current window
458 void fgDisplayMenu( void )
460 SFG_Window* window = fgStructure.CurrentWindow;
461 SFG_Menu* menu = NULL;
463 FREEGLUT_INTERNAL_ERROR_EXIT ( fgStructure.CurrentWindow, "Displaying menu in nonexistent window",
466 /* Check if there is an active menu attached to this window... */
467 menu = window->ActiveMenu;
468 freeglut_return_if_fail( menu );
470 fgSetWindow( menu->Window );
472 glPushAttrib( GL_DEPTH_BUFFER_BIT | GL_TEXTURE_BIT | GL_LIGHTING_BIT |
475 glDisable( GL_DEPTH_TEST );
476 glDisable( GL_TEXTURE_2D );
477 glDisable( GL_LIGHTING );
478 glDisable( GL_CULL_FACE );
480 glMatrixMode( GL_PROJECTION );
484 0, glutGet( GLUT_WINDOW_WIDTH ),
485 glutGet( GLUT_WINDOW_HEIGHT ), 0,
489 glMatrixMode( GL_MODELVIEW );
493 fghDisplayMenuBox( menu );
497 glMatrixMode( GL_PROJECTION );
499 glMatrixMode( GL_MODELVIEW );
504 fgSetWindow ( window );
508 * Activates a menu pointed by the function argument
510 static void fghActivateMenu( SFG_Window* window, int button )
515 /* We'll be referencing this menu a lot, so remember its address: */
516 SFG_Menu* menu = window->Menu[ button ];
517 SFG_Window* current_window = fgStructure.CurrentWindow;
519 /* If the menu is already active in another window, deactivate it (and any submenus) there */
520 if ( menu->ParentWindow )
521 fgDeactivateMenu(menu->ParentWindow);
523 /* Mark the menu as active, so that it gets displayed: */
524 window->ActiveMenu = menu;
525 menu->IsActive = GL_TRUE;
526 fghSetMenuParentWindow ( window, menu );
527 fgState.ActiveMenus++;
529 /* Set up the initial menu position now: */
530 fghGetVMaxExtent(menu->ParentWindow, &max_x, &max_y);
531 fgSetWindow( window );
532 /* get mouse position on screen (window->State.MouseX and window->State.MouseY
533 * are relative to client area origin), and not easy to correct given that
534 * glutGet( GLUT_WINDOW_X ) and glutGet( GLUT_WINDOW_Y ) return relative to parent
535 * origin when looking at a child window
536 * for parent windows: window->State.MouseX + glutGet( GLUT_WINDOW_X ) == mouse_pos.X
538 fghPlatformGetCursorPos(NULL, GL_FALSE, &mouse_pos);
539 menu->X = mouse_pos.X;
540 menu->Y = mouse_pos.Y;
542 /* Make sure the whole menu is on the screen */
543 if( menu->X + menu->Width > max_x )
544 menu->X -=menu->Width;
546 if( menu->Y + menu->Height > max_y )
548 menu->Y -=menu->Height;
553 /* Set position of mouse relative to top-left menu in menu's window state (could as well set 0 at creation time...) */
554 menu->Window->State.MouseX = mouse_pos.X - menu->X;
555 menu->Window->State.MouseY = mouse_pos.Y - menu->Y;
557 /* Menu status callback */
558 if (fgState.MenuStateCallback || fgState.MenuStatusCallback)
560 fgStructure.CurrentMenu = menu;
561 fgStructure.CurrentWindow = window;
562 if (fgState.MenuStateCallback)
563 fgState.MenuStateCallback(GLUT_MENU_IN_USE);
564 if (fgState.MenuStatusCallback)
565 /* window->State.MouseX and window->State.MouseY are relative to client area origin, as needed */
566 fgState.MenuStatusCallback(GLUT_MENU_IN_USE, window->State.MouseX, window->State.MouseY);
569 fgSetWindow( menu->Window );
570 glutPositionWindow( menu->X, menu->Y );
571 glutReshapeWindow( menu->Width, menu->Height );
574 menu->Window->ActiveMenu = menu;
575 fghCheckMenuStatus( menu );
576 fgSetWindow( current_window );
580 * Update Highlight states of the menu
582 * Current mouse position is in menu->Window->State.MouseX/Y.
584 void fgUpdateMenuHighlight ( SFG_Menu *menu )
586 fghCheckMenuStatus( menu );
590 * Check whether an active menu absorbs a mouse click
592 GLboolean fgCheckActiveMenu ( SFG_Window *window, int button, GLboolean pressed,
593 int mouse_x, int mouse_y )
596 * Near as I can tell, this is the menu behaviour:
597 * - Down-click the menu button, menu not active: activate
598 * the menu with its upper left-hand corner at the mouse
600 * - Down-click any button outside the menu, menu active:
601 * deactivate the menu
602 * - Down-click any button inside the menu, menu active:
603 * select the menu entry and deactivate the menu
604 * - Up-click the menu button, menu not active: nothing happens
605 * - Up-click the menu button outside the menu, menu active:
607 * - Up-click the menu button inside the menu, menu active:
608 * select the menu entry and deactivate the menu
609 * Since menus can have submenus, we need to check this recursively.
611 if( window->ActiveMenu )
613 if( window == window->ActiveMenu->ParentWindow )
615 window->ActiveMenu->Window->State.MouseX =
616 mouse_x - window->ActiveMenu->X;
617 window->ActiveMenu->Window->State.MouseY =
618 mouse_y - window->ActiveMenu->Y;
621 /* In the menu, invoke the callback and deactivate the menu */
622 if( fghCheckMenuStatus( window->ActiveMenu ) )
625 * Save the current window and menu and set the current
626 * window to the window whose menu this is
628 SFG_Window *save_window = fgStructure.CurrentWindow;
629 SFG_Menu *save_menu = fgStructure.CurrentMenu;
630 SFG_Window *parent_window = window->ActiveMenu->ParentWindow;
631 fgSetWindow( parent_window );
632 fgStructure.CurrentMenu = window->ActiveMenu;
634 /* Execute the menu callback */
635 fghExecuteMenuCallback( window->ActiveMenu );
636 fgDeactivateMenu( parent_window );
638 /* Restore the current window and menu */
639 fgSetWindow( save_window );
640 fgStructure.CurrentMenu = save_menu;
644 * Outside the menu, deactivate if it's a downclick
646 * A downclick outside of the interior of our freeglut windows
647 * is dealt with in the WM_KILLFOCUS handler of fgPlatformWindowProc
650 fgDeactivateMenu( window->ActiveMenu->ParentWindow );
654 * XXX Why does an active menu require a redisplay at
655 * XXX this point? If this can come out cleanly, then
656 * XXX it probably should do so; if not, a comment should
659 if( ! window->IsMenu )
660 window->State.Redisplay = GL_TRUE;
665 /* No active menu, let's check whether we need to activate one. */
666 if( ( 0 <= button ) &&
667 ( FREEGLUT_MAX_MENUS > button ) &&
668 ( window->Menu[ button ] ) &&
671 /* If mouseclick was outside the parent window, ignore. This can
672 * happen when another mouse button is already depressed and the
673 * window thus has mouse capture
675 if (window->State.MouseX>0 && window->State.MouseY>0 &&
676 window->State.MouseX<window->State.Width && window->State.MouseY<window->State.Height)
678 /* XXX Posting a requisite Redisplay seems bogus. */
679 window->State.Redisplay = GL_TRUE;
680 fghActivateMenu( window, button );
689 * Deactivates a menu pointed by the function argument.
691 static SFG_Menu* menuDeactivating = NULL;
692 void fgDeactivateMenu( SFG_Window *window )
694 SFG_Window *parent_window = NULL;
696 SFG_MenuEntry *menuEntry;
698 /* Did we find an active window? */
699 freeglut_return_if_fail( window );
700 /* Check if there is an active menu attached to this window... */
701 menu = window->ActiveMenu;
702 freeglut_return_if_fail( menu );
703 /* Check if we are already deactivating this menu, abort in that case (glutHideWindow below can cause this function to be called again on the same menu..) */
704 if (menu==menuDeactivating)
706 menuDeactivating = menu;
708 parent_window = menu->ParentWindow;
710 /* Hide the present menu's window */
711 fgSetWindow( menu->Window );
714 /* Forget about having that menu active anymore, now: */
715 menu->Window->ActiveMenu = NULL;
716 menu->ParentWindow->ActiveMenu = NULL;
717 fghSetMenuParentWindow ( NULL, menu );
718 menu->IsActive = GL_FALSE;
719 menu->ActiveEntry = NULL;
721 fgState.ActiveMenus--;
723 /* Hide all submenu windows, and the root menu's window. */
724 for ( menuEntry = ( SFG_MenuEntry * )menu->Entries.First;
726 menuEntry = ( SFG_MenuEntry * )menuEntry->Node.Next )
728 menuEntry->IsActive = GL_FALSE;
730 /* Is that an active submenu by any chance? */
731 if( menuEntry->SubMenu )
732 fghDeactivateSubMenu( menuEntry );
734 /* Done deactivating menu */
735 menuDeactivating = NULL;
737 fgSetWindow ( parent_window ) ;
739 /* Menu status callback */
740 if (fgState.MenuStateCallback || fgState.MenuStatusCallback)
742 fgStructure.CurrentMenu = menu;
743 fgStructure.CurrentWindow = parent_window;
744 if (fgState.MenuStateCallback)
745 fgState.MenuStateCallback(GLUT_MENU_NOT_IN_USE);
746 if (fgState.MenuStatusCallback)
748 /* Get cursor position relative to parent_window's client area */
750 fghPlatformGetCursorPos(parent_window, GL_TRUE, &mouse_pos);
752 fgState.MenuStatusCallback(GLUT_MENU_NOT_IN_USE, mouse_pos.X, mouse_pos.Y);
758 * Recalculates current menu's box size
760 void fghCalculateMenuBoxSize( void )
762 SFG_MenuEntry* menuEntry;
763 int width = 0, height = 0;
765 /* Make sure there is a current menu set */
766 freeglut_return_if_fail( fgStructure.CurrentMenu );
768 /* The menu's box size depends on the menu entries: */
769 for( menuEntry = ( SFG_MenuEntry * )fgStructure.CurrentMenu->Entries.First;
771 menuEntry = ( SFG_MenuEntry * )menuEntry->Node.Next )
773 /* Update the menu entry's width value */
774 menuEntry->Width = glutBitmapLength(
775 fgStructure.CurrentMenu->Font,
776 (unsigned char *)menuEntry->Text
780 * If the entry is a submenu, then it needs to be wider to
781 * accomodate the arrow. JCJ 31 July 2003
783 if (menuEntry->SubMenu )
784 menuEntry->Width += glutBitmapLength(
785 fgStructure.CurrentMenu->Font,
789 /* Check if it's the biggest we've found */
790 if( menuEntry->Width > width )
791 width = menuEntry->Width;
793 height += FREEGLUT_MENUENTRY_HEIGHT(fgStructure.CurrentMenu->Font);
796 /* Store the menu's box size now: */
797 fgStructure.CurrentMenu->Height = height + 2 * FREEGLUT_MENU_BORDER;
798 fgStructure.CurrentMenu->Width = width + 4 * FREEGLUT_MENU_BORDER;
802 /* -- INTERFACE FUNCTIONS -------------------------------------------------- */
805 * Creates a new menu object, adding it to the freeglut structure
807 int FGAPIENTRY glutCreateMenu( FGCBMenu callback )
809 /* The menu object creation code resides in freeglut_structure.c */
810 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutCreateMenu" );
811 if (fgGetActiveMenu())
812 fgError("Menu manipulation not allowed while menus in use.");
814 return fgCreateMenu( callback )->ID;
818 * Destroys a menu object, removing all references to it
820 void FGAPIENTRY glutDestroyMenu( int menuID )
824 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutDestroyMenu" );
825 menu = fgMenuByID( menuID );
827 freeglut_return_if_fail( menu );
828 if (fgGetActiveMenu())
829 fgError("Menu manipulation not allowed while menus in use.");
831 /* The menu object destruction code resides in freeglut_structure.c */
832 fgDestroyMenu( menu );
836 * Returns the ID number of the currently active menu
838 int FGAPIENTRY glutGetMenu( void )
840 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutGetMenu" );
842 if( fgStructure.CurrentMenu )
843 return fgStructure.CurrentMenu->ID;
849 * Sets the current menu given its menu ID
851 void FGAPIENTRY glutSetMenu( int menuID )
855 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutSetMenu" );
856 menu = fgMenuByID( menuID );
858 freeglut_return_if_fail( menu );
860 fgStructure.CurrentMenu = menu;
864 * Adds a menu entry to the bottom of the current menu
866 void FGAPIENTRY glutAddMenuEntry( const char* label, int value )
868 SFG_MenuEntry* menuEntry;
869 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutAddMenuEntry" );
870 menuEntry = (SFG_MenuEntry *)calloc( sizeof(SFG_MenuEntry), 1 );
872 freeglut_return_if_fail( fgStructure.CurrentMenu );
873 if (fgGetActiveMenu())
874 fgError("Menu manipulation not allowed while menus in use.");
876 menuEntry->Text = strdup( label );
877 menuEntry->ID = value;
879 /* Have the new menu entry attached to the current menu */
880 fgListAppend( &fgStructure.CurrentMenu->Entries, &menuEntry->Node );
882 fghCalculateMenuBoxSize( );
886 * Add a sub menu to the bottom of the current menu
888 void FGAPIENTRY glutAddSubMenu( const char *label, int subMenuID )
890 SFG_MenuEntry *menuEntry;
893 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutAddSubMenu" );
894 menuEntry = ( SFG_MenuEntry * )calloc( sizeof( SFG_MenuEntry ), 1 );
895 subMenu = fgMenuByID( subMenuID );
897 freeglut_return_if_fail( fgStructure.CurrentMenu );
898 if (fgGetActiveMenu())
899 fgError("Menu manipulation not allowed while menus in use.");
901 freeglut_return_if_fail( subMenu );
903 menuEntry->Text = strdup( label );
904 menuEntry->SubMenu = subMenu;
907 fgListAppend( &fgStructure.CurrentMenu->Entries, &menuEntry->Node );
908 fghCalculateMenuBoxSize( );
912 * Changes the current menu's font
914 void FGAPIENTRY glutSetMenuFont( void* fontID )
917 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutSetMenuFont" );
918 freeglut_return_if_fail( fgStructure.CurrentMenu );
920 if (fgGetActiveMenu())
921 fgError("Menu manipulation not allowed while menus in use.");
923 font = fghFontByID( fontID );
925 fgWarning("glutChangeMenuFont: bitmap font 0x%08x not found. Make sure you're not passing a stroke font. Ignoring...\n",fontID);
926 freeglut_return_if_fail( font );
928 fgStructure.CurrentMenu->Font = fontID;
929 fghCalculateMenuBoxSize( );
933 * Changes the specified menu item in the current menu into a menu entry
935 void FGAPIENTRY glutChangeToMenuEntry( int item, const char* label, int value )
937 SFG_MenuEntry* menuEntry = NULL;
939 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutChangeToMenuEntry" );
941 freeglut_return_if_fail( fgStructure.CurrentMenu );
942 if (fgGetActiveMenu())
943 fgError("Menu manipulation not allowed while menus in use.");
945 /* Get n-th menu entry in the current menu, starting from one: */
946 menuEntry = fghFindMenuEntry( fgStructure.CurrentMenu, item );
948 freeglut_return_if_fail( menuEntry );
950 /* We want it to become a normal menu entry, so: */
951 if( menuEntry->Text )
952 free( menuEntry->Text );
954 menuEntry->Text = strdup( label );
955 menuEntry->ID = value;
956 menuEntry->SubMenu = NULL;
957 fghCalculateMenuBoxSize( );
961 * Changes the specified menu item in the current menu into a sub-menu trigger.
963 void FGAPIENTRY glutChangeToSubMenu( int item, const char* label,
967 SFG_MenuEntry* menuEntry;
969 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutChangeToSubMenu" );
971 freeglut_return_if_fail( fgStructure.CurrentMenu );
972 if (fgGetActiveMenu())
973 fgError("Menu manipulation not allowed while menus in use.");
975 /* Get handle to sub menu */
976 subMenu = fgMenuByID( subMenuID );
978 freeglut_return_if_fail( subMenu );
980 /* Get n-th menu entry in the current menu, starting from one: */
981 menuEntry = fghFindMenuEntry( fgStructure.CurrentMenu, item );
983 freeglut_return_if_fail( menuEntry );
985 /* We want it to become a sub menu entry, so: */
986 if( menuEntry->Text )
987 free( menuEntry->Text );
989 menuEntry->Text = strdup( label );
990 menuEntry->SubMenu = subMenu;
992 fghCalculateMenuBoxSize( );
996 * Removes the specified menu item from the current menu
998 void FGAPIENTRY glutRemoveMenuItem( int item )
1000 SFG_MenuEntry* menuEntry;
1002 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutRemoveMenuItem" );
1004 freeglut_return_if_fail( fgStructure.CurrentMenu );
1005 if (fgGetActiveMenu())
1006 fgError("Menu manipulation not allowed while menus in use.");
1008 /* Get n-th menu entry in the current menu, starting from one: */
1009 menuEntry = fghFindMenuEntry( fgStructure.CurrentMenu, item );
1011 freeglut_return_if_fail( menuEntry );
1013 fgListRemove( &fgStructure.CurrentMenu->Entries, &menuEntry->Node );
1014 if ( menuEntry->Text )
1015 free( menuEntry->Text );
1018 fghCalculateMenuBoxSize( );
1022 * Attaches a menu to the current window
1024 void FGAPIENTRY glutAttachMenu( int button )
1026 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutAttachMenu" );
1028 freeglut_return_if_fail( fgStructure.CurrentWindow );
1030 freeglut_return_if_fail( fgStructure.CurrentMenu );
1031 if (fgGetActiveMenu())
1032 fgError("Menu manipulation not allowed while menus in use.");
1034 freeglut_return_if_fail( button >= 0 );
1035 freeglut_return_if_fail( button < FREEGLUT_MAX_MENUS );
1037 fgStructure.CurrentWindow->Menu[ button ] = fgStructure.CurrentMenu;
1041 * Detaches a menu from the current window
1043 void FGAPIENTRY glutDetachMenu( int button )
1045 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutDetachMenu" );
1047 freeglut_return_if_fail( fgStructure.CurrentWindow );
1049 freeglut_return_if_fail( fgStructure.CurrentMenu );
1050 if (fgGetActiveMenu())
1051 fgError("Menu manipulation not allowed while menus in use.");
1053 freeglut_return_if_fail( button >= 0 );
1054 freeglut_return_if_fail( button < FREEGLUT_MAX_MENUS );
1056 fgStructure.CurrentWindow->Menu[ button ] = NULL;
1060 * A.Donev: Set and retrieve the menu's user data
1062 void* FGAPIENTRY glutGetMenuData( void )
1064 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutGetMenuData" );
1065 return fgStructure.CurrentMenu->UserData;
1068 void FGAPIENTRY glutSetMenuData(void* data)
1070 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutSetMenuData" );
1071 fgStructure.CurrentMenu->UserData=data;
1074 /*** END OF FILE ***/