#include <GL/freeglut.h>
#include "fg_internal.h"
+
/* -- DEFINITIONS ---------------------------------------------------------- */
/*
extern GLvoid fgPlatformGetGameModeVMaxExtent( SFG_Window* window, int* x, int* y );
+extern void fghPlatformGetCursorPos(const SFG_Window *window, GLboolean client, SFG_XYUse *mouse_pos);
/* -- PRIVATE FUNCTIONS ---------------------------------------------------- */
/*
* Private static function to set the parent window of a submenu and all
- * of its submenus
+ * of its submenus.
*/
static void fghSetMenuParentWindow( SFG_Window *window, SFG_Menu *menu )
{
static void fghActivateMenu( SFG_Window* window, int button )
{
int max_x, max_y;
+ SFG_XYUse mouse_pos;
/* We'll be referencing this menu a lot, so remember its address: */
SFG_Menu* menu = window->Menu[ button ];
SFG_Window* current_window = fgStructure.CurrentWindow;
- /* If the menu is already active in another window, deactivate it there */
+ /* If the menu is already active in another window, deactivate it (and any submenus) there */
if ( menu->ParentWindow )
- menu->ParentWindow->ActiveMenu = NULL ;
+ fgDeactivateMenu(menu->ParentWindow);
/* Mark the menu as active, so that it gets displayed: */
window->ActiveMenu = menu;
/* Set up the initial menu position now: */
fghGetVMaxExtent(menu->ParentWindow, &max_x, &max_y);
fgSetWindow( window );
- menu->X = window->State.MouseX + glutGet( GLUT_WINDOW_X );
- menu->Y = window->State.MouseY + glutGet( GLUT_WINDOW_Y );
+ /* get mouse position on screen (window->State.MouseX and window->State.MouseY
+ * are relative to client area origin), and not easy to correct given that
+ * glutGet( GLUT_WINDOW_X ) and glutGet( GLUT_WINDOW_Y ) return relative to parent
+ * origin when looking at a child window
+ * for parent windows: window->State.MouseX + glutGet( GLUT_WINDOW_X ) == mouse_pos.X
+ */
+ fghPlatformGetCursorPos(NULL, GL_FALSE, &mouse_pos);
+ menu->X = mouse_pos.X;
+ menu->Y = mouse_pos.Y;
+ /* Make sure the whole menu is on the screen */
if( menu->X + menu->Width > max_x )
menu->X -=menu->Width;
menu->Y = 0;
}
- menu->Window->State.MouseX =
- window->State.MouseX + glutGet( GLUT_WINDOW_X ) - menu->X;
- menu->Window->State.MouseY =
- window->State.MouseY + glutGet( GLUT_WINDOW_Y ) - menu->Y;
+ /* Set position of mouse relative to top-left menu in menu's window state (could as well set 0 at creation time...) */
+ menu->Window->State.MouseX = mouse_pos.X - menu->X;
+ menu->Window->State.MouseY = mouse_pos.Y - menu->Y;
+
+ /* Menu status callback */
+ if (fgState.MenuStateCallback || fgState.MenuStatusCallback)
+ {
+ fgStructure.CurrentMenu = menu;
+ fgStructure.CurrentWindow = window;
+ if (fgState.MenuStateCallback)
+ fgState.MenuStateCallback(GLUT_MENU_IN_USE);
+ if (fgState.MenuStatusCallback)
+ /* window->State.MouseX and window->State.MouseY are relative to client area origin, as needed */
+ fgState.MenuStatusCallback(GLUT_MENU_IN_USE, window->State.MouseX, window->State.MouseY);
+ }
fgSetWindow( menu->Window );
glutPositionWindow( menu->X, menu->Y );
/*
* Outside the menu, deactivate if it's a downclick
*
- * XXX This isn't enough. A downclick outside of
- * XXX the interior of our freeglut windows should also
- * XXX deactivate the menu. This is more complicated.
+ * A downclick outside of the interior of our freeglut windows
+ * is dealt with in the WM_KILLFOCUS handler of fgPlatformWindowProc
*/
+ {
fgDeactivateMenu( window->ActiveMenu->ParentWindow );
+ }
/*
* XXX Why does an active menu require a redisplay at
( window->Menu[ button ] ) &&
pressed )
{
- /* XXX Posting a requisite Redisplay seems bogus. */
- window->State.Redisplay = GL_TRUE;
- fghActivateMenu( window, button );
- return GL_TRUE;
+ /* If mouseclick was outside the parent window, ignore. This can
+ * happen when another mouse button is already depressed and the
+ * window thus has mouse capture
+ */
+ if (window->State.MouseX>0 && window->State.MouseY>0 &&
+ window->State.MouseX<window->State.Width && window->State.MouseY<window->State.Height)
+ {
+ /* XXX Posting a requisite Redisplay seems bogus. */
+ window->State.Redisplay = GL_TRUE;
+ fghActivateMenu( window, button );
+ return GL_TRUE;
+ }
}
return GL_FALSE;
/*
* Deactivates a menu pointed by the function argument.
*/
+static SFG_Menu* menuDeactivating = NULL;
void fgDeactivateMenu( SFG_Window *window )
{
SFG_Window *parent_window = NULL;
-
- /* Check if there is an active menu attached to this window... */
- SFG_Menu* menu = window->ActiveMenu;
+ SFG_Menu* menu;
SFG_MenuEntry *menuEntry;
/* Did we find an active window? */
+ freeglut_return_if_fail( window );
+ /* Check if there is an active menu attached to this window... */
+ menu = window->ActiveMenu;
freeglut_return_if_fail( menu );
+ /* 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..) */
+ if (menu==menuDeactivating)
+ return;
+ menuDeactivating = menu;
parent_window = menu->ParentWindow;
{
menuEntry->IsActive = GL_FALSE;
- /* Is that an active submenu by any case? */
+ /* Is that an active submenu by any chance? */
if( menuEntry->SubMenu )
fghDeactivateSubMenu( menuEntry );
}
+ /* Done deactivating menu */
+ menuDeactivating = NULL;
fgSetWindow ( parent_window ) ;
+
+ /* Menu status callback */
+ if (fgState.MenuStateCallback || fgState.MenuStatusCallback)
+ {
+ fgStructure.CurrentMenu = menu;
+ fgStructure.CurrentWindow = parent_window;
+ if (fgState.MenuStateCallback)
+ fgState.MenuStateCallback(GLUT_MENU_NOT_IN_USE);
+ if (fgState.MenuStatusCallback)
+ {
+ /* Get cursor position relative to parent_window's client area */
+ SFG_XYUse mouse_pos;
+ fghPlatformGetCursorPos(parent_window, GL_TRUE, &mouse_pos);
+
+ fgState.MenuStatusCallback(GLUT_MENU_NOT_IN_USE, mouse_pos.X, mouse_pos.Y);
+ }
+ }
}
/*
/*
* Creates a new menu object, adding it to the freeglut structure
*/
-int FGAPIENTRY glutCreateMenu( void(* callback)( int ) )
+int FGAPIENTRY glutCreateMenu( FGCBMenu callback )
{
/* The menu object creation code resides in freeglut_structure.c */
FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutCreateMenu" );
+ if (fgGetActiveMenu())
+ fgError("Menu manipulation not allowed while menus in use.");
+
return fgCreateMenu( callback )->ID;
}
menu = fgMenuByID( menuID );
freeglut_return_if_fail( menu );
+ if (fgGetActiveMenu())
+ fgError("Menu manipulation not allowed while menus in use.");
/* The menu object destruction code resides in freeglut_structure.c */
fgDestroyMenu( menu );
SFG_MenuEntry* menuEntry;
FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutAddMenuEntry" );
menuEntry = (SFG_MenuEntry *)calloc( sizeof(SFG_MenuEntry), 1 );
+
freeglut_return_if_fail( fgStructure.CurrentMenu );
+ if (fgGetActiveMenu())
+ fgError("Menu manipulation not allowed while menus in use.");
menuEntry->Text = strdup( label );
menuEntry->ID = value;
subMenu = fgMenuByID( subMenuID );
freeglut_return_if_fail( fgStructure.CurrentMenu );
+ if (fgGetActiveMenu())
+ fgError("Menu manipulation not allowed while menus in use.");
+
freeglut_return_if_fail( subMenu );
menuEntry->Text = strdup( label );
SFG_MenuEntry* menuEntry = NULL;
FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutChangeToMenuEntry" );
+
freeglut_return_if_fail( fgStructure.CurrentMenu );
+ if (fgGetActiveMenu())
+ fgError("Menu manipulation not allowed while menus in use.");
/* Get n-th menu entry in the current menu, starting from one: */
menuEntry = fghFindMenuEntry( fgStructure.CurrentMenu, item );
SFG_MenuEntry* menuEntry;
FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutChangeToSubMenu" );
- subMenu = fgMenuByID( subMenuID );
- menuEntry = NULL;
freeglut_return_if_fail( fgStructure.CurrentMenu );
+ if (fgGetActiveMenu())
+ fgError("Menu manipulation not allowed while menus in use.");
+
+ /* Get handle to sub menu */
+ subMenu = fgMenuByID( subMenuID );
+ menuEntry = NULL;
freeglut_return_if_fail( subMenu );
/* Get n-th menu entry in the current menu, starting from one: */
SFG_MenuEntry* menuEntry;
FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutRemoveMenuItem" );
+
freeglut_return_if_fail( fgStructure.CurrentMenu );
+ if (fgGetActiveMenu())
+ fgError("Menu manipulation not allowed while menus in use.");
/* Get n-th menu entry in the current menu, starting from one: */
menuEntry = fghFindMenuEntry( fgStructure.CurrentMenu, item );
FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutAttachMenu" );
freeglut_return_if_fail( fgStructure.CurrentWindow );
+
freeglut_return_if_fail( fgStructure.CurrentMenu );
+ if (fgGetActiveMenu())
+ fgError("Menu manipulation not allowed while menus in use.");
freeglut_return_if_fail( button >= 0 );
freeglut_return_if_fail( button < FREEGLUT_MAX_MENUS );
FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutDetachMenu" );
freeglut_return_if_fail( fgStructure.CurrentWindow );
+
freeglut_return_if_fail( fgStructure.CurrentMenu );
+ if (fgGetActiveMenu())
+ fgError("Menu manipulation not allowed while menus in use.");
freeglut_return_if_fail( button >= 0 );
freeglut_return_if_fail( button < FREEGLUT_MAX_MENUS );