From 49e61d9b74ae8520b5b7fddbcfc727081ac54391 Mon Sep 17 00:00:00 2001 From: Diederick Niehorster Date: Sat, 21 Jul 2012 04:04:45 +0000 Subject: [PATCH] can now also handle the case where menus is opened, client area of window with menu is clicked and then user switches to another window. Implemented menu enumerator and a function for getting currently active menu, if any git-svn-id: svn+ssh://svn.code.sf.net/p/freeglut/code/trunk/freeglut/freeglut@1354 7f0cb862-5218-0410-a997-914c9d46530a --- src/fg_internal.h | 13 ++++-- src/fg_menu.c | 2 +- src/fg_structure.c | 104 +++++++++++++++++++++++++++++++++++++++------ src/mswin/fg_main_mswin.c | 57 +++++++++++++------------ 4 files changed, 134 insertions(+), 42 deletions(-) diff --git a/src/fg_internal.h b/src/fg_internal.h index 13e57a3..61ae5ce 100644 --- a/src/fg_internal.h +++ b/src/fg_internal.h @@ -661,7 +661,8 @@ struct tagSFG_Enumerator GLboolean found; /* Used to terminate search */ void* data; /* Custom data pointer */ }; -typedef void (* FGCBenumerator )( SFG_Window *, SFG_Enumerator * ); +typedef void (* FGCBWindowEnumerator )( SFG_Window *, SFG_Enumerator * ); +typedef void (* FGCBMenuEnumerator )( SFG_Menu *, SFG_Enumerator * ); /* The bitmap font structure */ typedef struct tagSFG_Font SFG_Font; @@ -915,8 +916,8 @@ void fgSetCursor ( SFG_Window *window, int cursorID ); * and userData is the a custom user-supplied pointer. Functions * are defined and exported from freeglut_structure.c file. */ -void fgEnumWindows( FGCBenumerator enumCallback, SFG_Enumerator* enumerator ); -void fgEnumSubWindows( SFG_Window* window, FGCBenumerator enumCallback, +void fgEnumWindows( FGCBWindowEnumerator enumCallback, SFG_Enumerator* enumerator ); +void fgEnumSubWindows( SFG_Window* window, FGCBWindowEnumerator enumCallback, SFG_Enumerator* enumerator ); /* @@ -940,6 +941,12 @@ SFG_Window* fgWindowByID( int windowID ); SFG_Menu* fgMenuByID( int menuID ); /* + * Returns active menu, if any. Assumption: only one menu active throughout application at any one time. + * This is easier than fgWindowByXXX as all menus are placed in one doubly linked list... + */ +SFG_Menu* fgGetActiveMenu( ); + +/* * The menu activation and deactivation the code. This is the meat * of the menu user interface handling code... */ diff --git a/src/fg_menu.c b/src/fg_menu.c index 2460b1d..0c6c64e 100644 --- a/src/fg_menu.c +++ b/src/fg_menu.c @@ -512,7 +512,7 @@ static void fghActivateMenu( SFG_Window* window, int button ) SFG_Menu* menu = window->Menu[ button ]; SFG_Window* current_window = fgStructure.CurrentWindow; - /* If the menu is already active in another window, deactivate it (and any submenu's) there */ + /* If the menu is already active in another window, deactivate it (and any submenus) there */ if ( menu->ParentWindow ) fgDeactivateMenu(menu->ParentWindow); diff --git a/src/fg_structure.c b/src/fg_structure.c index 1e05653..7c2c143 100644 --- a/src/fg_structure.c +++ b/src/fg_structure.c @@ -378,7 +378,7 @@ void fgDestroyStructure( void ) /* * Helper function to enumerate through all registered top-level windows */ -void fgEnumWindows( FGCBenumerator enumCallback, SFG_Enumerator* enumerator ) +void fgEnumWindows( FGCBWindowEnumerator enumCallback, SFG_Enumerator* enumerator ) { SFG_Window *window; @@ -398,10 +398,32 @@ void fgEnumWindows( FGCBenumerator enumCallback, SFG_Enumerator* enumerator ) } /* +* Helper function to enumerate through all registered top-level windows +*/ +void fgEnumMenus( FGCBMenuEnumerator enumCallback, SFG_Enumerator* enumerator ) +{ + SFG_Menu *menu; + + FREEGLUT_INTERNAL_ERROR_EXIT ( enumCallback && enumerator, + "Enumerator or callback missing from window enumerator call", + "fgEnumWindows" ); + + /* It's enough to check all entries in fgStructure.Menus... */ + for( menu = (SFG_Menu *)fgStructure.Menus.First; + menu; + menu = (SFG_Menu *)menu->Node.Next ) + { + enumCallback( menu, enumerator ); + if( enumerator->found ) + return; + } +} + +/* * Helper function to enumerate through all a window's subwindows * (single level descent) */ -void fgEnumSubWindows( SFG_Window* window, FGCBenumerator enumCallback, +void fgEnumSubWindows( SFG_Window* window, FGCBWindowEnumerator enumCallback, SFG_Enumerator* enumerator ) { SFG_Window *child; @@ -485,7 +507,7 @@ static void fghcbWindowByID( SFG_Window *window, SFG_Enumerator *enumerator ) } /* - * This function is similiar to the previous one, except it is + * This function is similar to the previous one, except it is * looking for a specified (sub)window identifier. The function * is defined in freeglut_structure.c file. */ @@ -493,7 +515,7 @@ SFG_Window* fgWindowByID( int windowID ) { SFG_Enumerator enumerator; - /* Uses a method very similiar for fgWindowByHandle... */ + /* Uses a method very similar for fgWindowByHandle... */ enumerator.found = GL_FALSE; enumerator.data = ( void * )&windowID; fgEnumWindows( fghcbWindowByID, &enumerator ); @@ -503,19 +525,77 @@ SFG_Window* fgWindowByID( int windowID ) } /* - * Looks up a menu given its ID. This is easier that fgWindowByXXX + * A static helper function to look for a menu given its ID + */ +static void fghcbMenuByID( SFG_Menu *menu, + SFG_Enumerator *enumerator ) +{ + if ( enumerator->found ) + return; + + /* Check the menu's ID. */ + if( menu->ID == (int)(enumerator->data) ) + { + enumerator->found = GL_TRUE; + enumerator->data = menu; + + return; + } +} + +/* + * Looks up a menu given its ID. This is easier than fgWindowByXXX * as all menus are placed in one doubly linked list... */ SFG_Menu* fgMenuByID( int menuID ) { - SFG_Menu *menu = NULL; + SFG_Enumerator enumerator; + + /* This is easy and makes use of the menus enumeration defined above */ + enumerator.found = GL_FALSE; + enumerator.data = (void *)menuID; + fgEnumMenus( fghcbMenuByID, &enumerator ); + + if( enumerator.found ) + return( SFG_Menu *) enumerator.data; + + return NULL; +} + +/* + * A static helper function to look for an active menu + */ +static void fghcbGetActiveMenu( SFG_Menu *menu, + SFG_Enumerator *enumerator ) +{ + if ( enumerator->found ) + return; + + /* Check the menu's ID. */ + if( menu->IsActive ) + { + enumerator->found = GL_TRUE; + enumerator->data = menu; + + return; + } +} + +/* + * Returns active menu, if any. Assumption: only one menu active throughout application at any one time. + * This is easier than fgWindowByXXX as all menus are placed in one doubly linked list... + */ +SFG_Menu* fgGetActiveMenu( ) +{ + SFG_Enumerator enumerator; + + /* This is easy and makes use of the menus enumeration defined above */ + enumerator.found = GL_FALSE; + fgEnumMenus( fghcbGetActiveMenu, &enumerator ); + + if( enumerator.found ) + return( SFG_Menu *) enumerator.data; - /* It's enough to check all entries in fgStructure.Menus... */ - for( menu = (SFG_Menu *)fgStructure.Menus.First; - menu; - menu = (SFG_Menu *)menu->Node.Next ) - if( menu->ID == menuID ) - return menu; return NULL; } diff --git a/src/mswin/fg_main_mswin.c b/src/mswin/fg_main_mswin.c index c72e467..a992b2b 100644 --- a/src/mswin/fg_main_mswin.c +++ b/src/mswin/fg_main_mswin.c @@ -493,34 +493,39 @@ LRESULT CALLBACK fgPlatformWindowProc( HWND hWnd, UINT uMsg, WPARAM wParam, break; case WM_KILLFOCUS: -/* printf("WM_KILLFOCUS (ismenu: %i): %p\n", window->IsMenu, window ); */ - lRet = DefWindowProc( hWnd, uMsg, wParam, lParam ); - INVOKE_WCB( *window, Entry, ( GLUT_LEFT ) ); - - /* If this is a menu that lost focus, see if user either switched - application or FreeGLUT window (if one is running multiple - windows). If so, close menu that lost focus. - */ - if( window->IsMenu && - window->ActiveMenu && window->ActiveMenu->IsActive ) { - SFG_Window* wnd = NULL; - HWND hwnd = GetForegroundWindow(); /* Get window with current focus */ - if (hwnd) - /* See if its one of our windows */ - wnd = fgWindowByHandle(hwnd); - - if (!hwnd || !wnd) - /* User switched to another application*/ - fgDeactivateMenu(window->ActiveMenu->ParentWindow); - else if ( - ( wnd->IsMenu && wnd->ActiveMenu->ParentWindow!=window->ActiveMenu->ParentWindow) || /* Make sure we don't kill the menu when trying to enter a submenu */ - (!wnd->IsMenu && wnd!=window->ActiveMenu->ParentWindow) - ) - /* User switched to another FreeGLUT window */ - fgDeactivateMenu(window->ActiveMenu->ParentWindow); - } + SFG_Menu* menu = NULL; +/* printf("WM_KILLFOCUS (ismenu: %i): %p\n", window->IsMenu, window ); */ + lRet = DefWindowProc( hWnd, uMsg, wParam, lParam ); + INVOKE_WCB( *window, Entry, ( GLUT_LEFT ) ); + /* If we have an open menu, see if the open menu should be closed + when focus was lost because user either switched + application or FreeGLUT window (if one is running multiple + windows). If so, close menu the active menu. + */ + if ( fgStructure.CurrentMenu ) + menu = fgGetActiveMenu(); + + if ( menu ) + { + SFG_Window* wnd = NULL; + HWND hwnd = GetForegroundWindow(); /* Get window with current focus */ + if (hwnd) + /* See if its one of our windows */ + wnd = fgWindowByHandle(hwnd); + + if (!hwnd || !wnd) + /* User switched to another application*/ + fgDeactivateMenu(menu->ParentWindow); + else if ( + ( wnd->IsMenu && wnd->ActiveMenu && wnd->ActiveMenu->ParentWindow!=menu->ParentWindow) || /* Make sure we don't kill the menu when trying to enter a submenu */ + (!wnd->IsMenu && wnd!=menu->ParentWindow) + ) + /* User switched to another FreeGLUT window */ + fgDeactivateMenu(menu->ParentWindow); + } + } break; #if 0 -- 1.7.10.4