Moved '#include "config.h"' to freeglut_internal.h, we will need it
[freeglut] / src / freeglut_menu.c
index 9bdf9a0..5733262 100644 (file)
  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  */
 
-#ifdef HAVE_CONFIG_H
-#include "config.h"
-#endif
-
 #include <GL/freeglut.h>
 #include "freeglut_internal.h"
 
@@ -164,7 +160,8 @@ static GLboolean fghCheckMenuStatus( SFG_Window* window, SFG_Menu* menu )
 
         /* The mouse cursor is somewhere over our box, check it out. */
         menuEntry = fghFindMenuEntry( menu, menuID + 1 );
-        assert( menuEntry );
+        FREEGLUT_INTERNAL_ERROR_EXIT( menuEntry, "Cannot find menu entry",
+                                      "fghCheckMenuStatus" );
 
         menuEntry->IsActive = GL_TRUE;
         menuEntry->Ordinal = menuID;
@@ -385,6 +382,30 @@ static void fghSetSubmenuParentWindow( SFG_Window *window, SFG_Menu *menu )
             fghSetSubmenuParentWindow( window, menuEntry->SubMenu );
 }
 
+/*
+ * Function to check for menu entry selection on menu deactivation
+ */
+static void fghExecuteMenuCallback( SFG_Menu* menu )
+{
+    SFG_MenuEntry *menuEntry;
+
+    /* First of all check any of the active sub menus... */
+    for( menuEntry = (SFG_MenuEntry *)menu->Entries.First;
+         menuEntry;
+         menuEntry = (SFG_MenuEntry *)menuEntry->Node.Next)
+    {
+        if( menuEntry->IsActive )
+        {
+            if( menuEntry->SubMenu )
+                fghExecuteMenuCallback( menuEntry->SubMenu );
+            else
+                if( menu->Callback )
+                    menu->Callback( menuEntry->ID );
+            return;
+        }
+    }
+}
+
 
 /*
  * Displays the currently active menu for the current window
@@ -394,7 +415,8 @@ void fgDisplayMenu( void )
     SFG_Window* window = fgStructure.Window;
     SFG_Menu* menu = NULL;
 
-    freeglut_return_if_fail ( fgStructure.Window != NULL );
+    FREEGLUT_INTERNAL_ERROR_EXIT ( fgStructure.Window, "Displaying menu in nonexistent window",
+                                   "fgDisplayMenu" );
 
     /* Check if there is an active menu attached to this window... */
     menu = window->ActiveMenu;
@@ -472,45 +494,93 @@ void fgActivateMenu( SFG_Window* window, int button )
 /*
  * Check whether an active menu absorbs a mouse click
  */
-GLboolean fgCheckActiveMenu ( SFG_Window *window, SFG_Menu *menu )
+GLboolean fgCheckActiveMenu ( SFG_Window *window, int button, GLboolean pressed,
+                              int mouse_x, int mouse_y )
 {
     /*
-     * Near as I can tell, this is the active menu behaviour:
+     * Near as I can tell, this is the menu behaviour:
+     *  - Down-click the menu button, menu not active:  activate
+     *    the menu with its upper left-hand corner at the mouse
+     *    location.
      *  - Down-click any button outside the menu, menu active:
      *    deactivate the menu
      *  - Down-click any button inside the menu, menu active:
      *    select the menu entry and deactivate the menu
+     *  - Up-click the menu button, menu not active:  nothing happens
      *  - Up-click the menu button outside the menu, menu active:
      *    nothing happens
      *  - Up-click the menu button inside the menu, menu active:
      *    select the menu entry and deactivate the menu
      * Since menus can have submenus, we need to check this recursively.
      */
-  return fghCheckMenuStatus( window, menu );
-}
-
-/*
- * Function to check for menu entry selection on menu deactivation
- */
-void fgExecuteMenuCallback( SFG_Menu* menu )
-{
-    SFG_MenuEntry *menuEntry;
-
-    /* First of all check any of the active sub menus... */
-    for( menuEntry = (SFG_MenuEntry *)menu->Entries.First;
-         menuEntry;
-         menuEntry = (SFG_MenuEntry *)menuEntry->Node.Next)
+    if( window->ActiveMenu )
     {
-        if( menuEntry->IsActive )
+        if( window == window->ActiveMenu->ParentWindow )
         {
-            if( menuEntry->SubMenu )
-                fgExecuteMenuCallback( menuEntry->SubMenu );
-            else
-                if( menu->Callback )
-                    menu->Callback( menuEntry->ID );
-            return;
+            window->ActiveMenu->Window->State.MouseX =
+                                       mouse_x - window->ActiveMenu->X;
+            window->ActiveMenu->Window->State.MouseY =
+                                       mouse_y - window->ActiveMenu->Y;
         }
+
+        /* In the menu, invoke the callback and deactivate the menu */
+        if( fghCheckMenuStatus( window->ActiveMenu->Window,
+                                window->ActiveMenu ) )
+        {
+            /*
+             * Save the current window and menu and set the current
+             * window to the window whose menu this is
+             */
+            SFG_Window *save_window = fgStructure.Window;
+            SFG_Menu *save_menu = fgStructure.Menu;
+            SFG_Window *parent_window = window->ActiveMenu->ParentWindow;
+            fgSetWindow( parent_window );
+            fgStructure.Menu = window->ActiveMenu;
+
+            /* Execute the menu callback */
+            fghExecuteMenuCallback( window->ActiveMenu );
+            fgDeactivateMenu( parent_window );
+
+            /* Restore the current window and menu */
+            fgSetWindow( save_window );
+            fgStructure.Menu = save_menu;
+        }
+        else if( pressed )
+            /*
+             * 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.
+             */
+            fgDeactivateMenu( window->ActiveMenu->ParentWindow );
+
+        /*
+         * XXX Why does an active menu require a redisplay at
+         * XXX this point?  If this can come out cleanly, then
+         * XXX it probably should do so; if not, a comment should
+         * XXX explain it.
+         */
+        if( ! window->IsMenu )
+            window->State.Redisplay = GL_TRUE;
+
+        return GL_TRUE;
     }
+
+    /* No active menu, let's check whether we need to activate one. */
+    if( ( 0 <= button ) &&
+        ( FREEGLUT_MAX_MENUS > button ) &&
+        ( window->Menu[ button ] ) &&
+        pressed )
+    {
+        /* XXX Posting a requisite Redisplay seems bogus. */
+        window->State.Redisplay = GL_TRUE;
+        fgSetWindow( window );
+        fgActivateMenu( window, button );
+        return GL_TRUE;
+    }
+
+    return GL_FALSE;
 }
 
 /*
@@ -588,7 +658,6 @@ void fghCalculateMenuBoxSize( void )
     int width = 0, height = 0;
 
     /* Make sure there is a current menu set */
-    freeglut_assert_ready;
     freeglut_return_if_fail( fgStructure.Menu );
 
     /* The menu's box size depends on the menu entries: */
@@ -633,6 +702,7 @@ void fghCalculateMenuBoxSize( void )
 int FGAPIENTRY glutCreateMenu( void(* callback)( int ) )
 {
     /* The menu object creation code resides in freeglut_structure.c */
+    FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutCreateMenu" );
     return fgCreateMenu( callback )->ID;
 }
 
@@ -641,9 +711,11 @@ int FGAPIENTRY glutCreateMenu( void(* callback)( int ) )
  */
 void FGAPIENTRY glutDestroyMenu( int menuID )
 {
-    SFG_Menu* menu = fgMenuByID( menuID );
+    SFG_Menu* menu;
+
+    FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutDestroyMenu" );
+    menu = fgMenuByID( menuID );
 
-    freeglut_assert_ready;
     freeglut_return_if_fail( menu );
 
     /* The menu object destruction code resides in freeglut_structure.c */
@@ -655,7 +727,7 @@ void FGAPIENTRY glutDestroyMenu( int menuID )
  */
 int FGAPIENTRY glutGetMenu( void )
 {
-    freeglut_assert_ready;
+    FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutGetMenu" );
 
     if( fgStructure.Menu )
         return fgStructure.Menu->ID;
@@ -668,9 +740,11 @@ int FGAPIENTRY glutGetMenu( void )
  */
 void FGAPIENTRY glutSetMenu( int menuID )
 {
-    SFG_Menu* menu = fgMenuByID( menuID );
+    SFG_Menu* menu;
+
+    FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutSetMenu" );
+    menu = fgMenuByID( menuID );
 
-    freeglut_assert_ready;
     freeglut_return_if_fail( menu );
 
     fgStructure.Menu = menu;
@@ -681,10 +755,9 @@ void FGAPIENTRY glutSetMenu( int menuID )
  */
 void FGAPIENTRY glutAddMenuEntry( const char* label, int value )
 {
-    SFG_MenuEntry* menuEntry =
-        (SFG_MenuEntry *)calloc( sizeof(SFG_MenuEntry), 1 );
-
-    freeglut_assert_ready;
+    SFG_MenuEntry* menuEntry;
+    FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutAddMenuEntry" );
+    menuEntry = (SFG_MenuEntry *)calloc( sizeof(SFG_MenuEntry), 1 );
     freeglut_return_if_fail( fgStructure.Menu );
 
     menuEntry->Text = strdup( label );
@@ -701,11 +774,13 @@ void FGAPIENTRY glutAddMenuEntry( const char* label, int value )
  */
 void FGAPIENTRY glutAddSubMenu( const char *label, int subMenuID )
 {
-    SFG_MenuEntry *menuEntry =
-        ( SFG_MenuEntry * )calloc( sizeof( SFG_MenuEntry ), 1 );
-    SFG_Menu *subMenu = fgMenuByID( subMenuID );
+    SFG_MenuEntry *menuEntry;
+    SFG_Menu *subMenu;
+
+    FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutAddSubMenu" );
+    menuEntry = ( SFG_MenuEntry * )calloc( sizeof( SFG_MenuEntry ), 1 );
+    subMenu = fgMenuByID( subMenuID );
 
-    freeglut_assert_ready;
     freeglut_return_if_fail( fgStructure.Menu );
     freeglut_return_if_fail( subMenu );
 
@@ -727,7 +802,7 @@ void FGAPIENTRY glutChangeToMenuEntry( int item, const char* label, int value )
 {
     SFG_MenuEntry* menuEntry = NULL;
 
-    freeglut_assert_ready;
+    FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutChangeToMenuEntry" );
     freeglut_return_if_fail( fgStructure.Menu );
 
     /* Get n-th menu entry in the current menu, starting from one: */
@@ -751,10 +826,13 @@ void FGAPIENTRY glutChangeToMenuEntry( int item, const char* label, int value )
 void FGAPIENTRY glutChangeToSubMenu( int item, const char* label,
                                      int subMenuID )
 {
-    SFG_Menu*      subMenu = fgMenuByID( subMenuID );
-    SFG_MenuEntry* menuEntry = NULL;
+    SFG_Menu*      subMenu;
+    SFG_MenuEntry* menuEntry;
+
+    FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutChangeToSubMenu" );
+    subMenu = fgMenuByID( subMenuID );
+    menuEntry = NULL;
 
-    freeglut_assert_ready;
     freeglut_return_if_fail( fgStructure.Menu );
     freeglut_return_if_fail( subMenu );
 
@@ -780,7 +858,7 @@ void FGAPIENTRY glutRemoveMenuItem( int item )
 {
     SFG_MenuEntry* menuEntry;
 
-    freeglut_assert_ready;
+    FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutRemoveMenuItem" );
     freeglut_return_if_fail( fgStructure.Menu );
 
     /* Get n-th menu entry in the current menu, starting from one: */
@@ -801,7 +879,7 @@ void FGAPIENTRY glutRemoveMenuItem( int item )
  */
 void FGAPIENTRY glutAttachMenu( int button )
 {
-    freeglut_assert_ready;
+    FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutAttachMenu" );
 
     freeglut_return_if_fail( fgStructure.Window );
     freeglut_return_if_fail( fgStructure.Menu );
@@ -820,7 +898,7 @@ void FGAPIENTRY glutAttachMenu( int button )
  */
 void FGAPIENTRY glutDetachMenu( int button )
 {
-    freeglut_assert_ready;
+    FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutDetachMenu" );
 
     freeglut_return_if_fail( fgStructure.Window );
     freeglut_return_if_fail( fgStructure.Menu );
@@ -836,11 +914,13 @@ void FGAPIENTRY glutDetachMenu( int button )
  */
 void* FGAPIENTRY glutGetMenuData( void )
 {
+    FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutGetMenuData" );
     return fgStructure.Menu->UserData;
 }
 
 void FGAPIENTRY glutSetMenuData(void* data)
 {
+    FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutSetMenuData" );
     fgStructure.Menu->UserData=data;
 }