/*
    ####             #    #     # #
    #   #            #    #       #          The FreeWare C library for 
    #   #  ##   ###  #  # #     # ###             RISC OS machines
    #   # #  # #     # #  #     # #  #   ___________________________________
    #   # ####  ###  ##   #     # #  #                                      
    #   # #        # # #  #     # #  #    Please refer to the accompanying
    ####   ### ####  #  # ##### # ###    documentation for conditions of use
    ________________________________________________________________________

    File:    Menu2.Menu2.c
    Author:  Copyright  1995 Julian Smith
    Version: 1.02 (11 Jan 1996)
    Purpose: Easy menu handling
    History: 1.00 (21 Jun 1995)
             1.01 (14 Nov 1995) Added SDLS-compatibility things like 
                                Desk_SDLS_PtrFn etc.
                                Fixed a bug which called the wrong selection
                                function if a non-leaf menuitem was chosen.
             1.02 (11 Jan 1996) Fixed bug which caused problems if 
                                Desk_message_MENUSDELETED wasn't received - the
                                old event handler wasn't released.
*/


#include <stdlib.h>

#include "Desk.Icon.h"
#include "Desk.WimpSWIs.h"
#include "Desk.Event.h"
#include "Desk.Menu.h"
#include "Desk.Error.h"
#include "Desk.Debug.h"
#include "Desk.Menu2.h"
#include "Desk.DeskMem.h"


typedef struct	{
	const char	*title;
	const char	*spec;
	Desk_menu2_makefn	makefn;
	Desk_menu2_flagsfn	flagsfn;
	Desk_menu2_subfn	subfn;
	Desk_menu2_selectfn	selectfn;
	Desk_menu_ptr	menu;
	void		*reference;
	}
	Desk_menu2_block;



#define Desk_menu2_MAXNEST	10
static Desk_menu2_block	*Desk_menu2__openmenus[ Desk_menu2_MAXNEST+1];
static int		Desk_menu2__numopen = 0;



#ifdef Desk_DeskLib_DEBUG
int	Desk_menu2_debuglevel = 0;
#endif



static void	Desk_Menu2__ClaimRelease( Desk_event_claimorreleasefn fn, void *reference);

#define Desk_Menu2__Claim( reference)			\
	Desk_Menu2__ClaimRelease( Desk_Event_Claim, reference)

#define Desk_Menu2__Release( reference)			\
	Desk_Menu2__ClaimRelease( Desk_Event_Release, reference)








static void	Desk_Menu2__FreeMenu( Desk_menu2_block *m)
/* Frees all data associated with Desk_menu2_block m	*/
/* Doesn't free submenus...			*/
{
Desk_menu_item	*item = Desk_Menu_FirstItem( m->menu);
for( ; ; item++)	{
	Desk_Icon_DisposeIndData( &item->icondata, item->iconflags);
	if ( item->menuflags.data.last)	break;
	}
Desk_DeskMem_Free( m->menu);
m->menu = NULL;
}


static void	Desk_Menu2__Free( int i)
/* Free menus starting from nesting i	*/
{
int	ii;
Desk_Debug_Printf( "Desk_Menu2__Free, i=%i, Desk_menu2__numopen=%i\n", i, Desk_menu2__numopen);

for ( ii=i; ii<Desk_menu2__numopen; ii++)	{
	
	Desk_Debug_Printf( Desk_error_PLACE "Desk_menu2__openmenus[ ii(=%i)]->makefn = %p\n", 
		ii, Desk_menu2__openmenus[ ii]->makefn
		);
	
	if ( !Desk_menu2__openmenus[ ii]->makefn)	{
		Desk_Debug_Printf( Desk_error_PLACE "Freeing menu number %i\n", ii);
		Desk_Menu2__FreeMenu( Desk_menu2__openmenus[ ii]);
		}
	else	{
		Desk_Debug_Printf( "Haven't free-d menu - it had a special creator function\n");
		}
	/* We don't free menus we haven't created	*/
	}

Desk_menu2__numopen = i;
}


Desk_menu2_handle	Desk_Menu2_Create( 
	const char	*title,
	const char	*spec,
	Desk_menu2_makefn	makefn, 		/* If !=NULL, called to make the menu	*/
	Desk_menu2_flagsfn	initfn, 	/* Called every time menu is opened	*/
	Desk_menu2_subfn	subfn, 		/* Called when submenu is needed	*/
	Desk_menu2_selectfn	selectfn, 		/* Called when selection is made	*/
	void		*reference
	)
{
Desk_menu2_block	*m = (Desk_menu2_block *) Desk_DeskMem_XMalloc( sizeof( Desk_menu2_block));
if (!m)	return 0;
	
m->title	= title;
m->spec		= spec;
m->makefn	= makefn;
m->flagsfn	= initfn;
m->subfn	= subfn;
m->selectfn	= selectfn;
m->reference	= reference;
m->menu		= NULL;

Desk_Debug_Printf( "Desk_Menu2_Create, title=%s, subfn=%p\n", title, subfn);

return (Desk_menu2_handle) m;
}





static void	Desk_Menu2__EnsureMenu( Desk_menu2_block *m)
{
if ( m->menu == NULL)	{
	if ( m->makefn)	{
		Desk_Debug_Printf( "Desk_Menu2__EnsureMenu calling makefn at %p\n", m->makefn);
		m->menu = m->makefn( m->reference);
		}
	else	{
		m->menu = Desk_Menu_New( m->title, m->spec);
		};
	}

if ( m->flagsfn)	{
	Desk_Debug_Printf( "Desk_Menu2__EnsureMenu calling flagsfn at %p\n", m->flagsfn);
	m->flagsfn( m->menu, m->reference);
	}
}




void	Desk_Menu2_Open( Desk_menu2_handle handle, int x, int y)
{
Desk_menu2_block	*m = (Desk_menu2_block *) handle;

Desk_Debug_Printf( "Desk_Menu2_Open called\n");

if ( Desk_menu2__numopen > 0)	Desk_Menu2__Release( NULL);
	/*
	Under RO2, the previous menu can have closed without us hearing
	about it.
	 */
Desk_Menu2__Free( 0);	/* Close all menus	*/

Desk_Menu2__EnsureMenu( m);
Desk_Menu_Show( m->menu, x, y);
Desk_menu2__openmenus[0] = m;
Desk_menu2__numopen = 1;
Desk_Menu2__Claim( NULL);
}




static int	Desk_Menu2__SelectionDepth( int *selections)
/* Returns the number of menus in the selection	*/
{
int	i;
for ( i=0; selections[i]!=-1; i++)	;
Desk_Debug_Printf( "Desk_Menu2__SelectionDepth, returning %i\n", i);
return i;
}




static void	Desk_Menu2__OpenSub( Desk_event_pollblock *event, Desk_menu2_handle handle)
{
Desk_menu2_block	*m = (Desk_menu2_block *) handle;
int		selectdepth = Desk_Menu2__SelectionDepth( &event->data.message.data.words[3]);

Desk_Debug_Printf( "Desk_Menu2__OpenSub, handle=%i, selectdepth= %i\n", handle, selectdepth);

Desk_Menu2__Free( selectdepth);
	
Desk_Menu2__EnsureMenu( m);
Desk_Wimp_eCreateSubMenu(
	m->menu,
	event->data.message.data.words[1], 
	event->data.message.data.words[2]
	);

Desk_menu2__openmenus[ Desk_menu2__numopen] = m;
Desk_menu2__numopen++;
}





Desk_SDLS_PtrFn( 
	static, 
	Desk_bool, 
	Desk_Menu2__MessageHandler( Desk_event_pollblock *event, void *reference)
	)
/*static Desk_bool	Desk_Menu2__MessageHandler( Desk_event_pollblock *event, void *reference)*/
/* cc warns about extern ... not declared in header in SDLS compiles	*/
{
Desk_UNUSED( reference);

Desk_Debug_Printf( "Desk_Menu2__MessageHandler\n");

if ( event->data.message.header.action == Desk_message_MENUWARN)	{

	int		*selections	= &event->data.message.data.words[3];
	int		selectdepth	= Desk_Menu2__SelectionDepth( selections);
	Desk_menu2_block	*lastmenu	= Desk_menu2__openmenus[ selectdepth-1];
	Desk_menu2_handle	nextmenu = NULL;
	
	Desk_Menu2__Free( selectdepth);
	Desk_Debug_Printf( "Desk_Menu2__MessageHandler, Desk_menu2__numopen=%i\n", Desk_menu2__numopen);
	
	if ( Desk_menu2__numopen < selectdepth)	return Desk_bool_FALSE;
		/* Some menus are open which we don't know about...	*/
	
	if ( lastmenu->subfn)	{
		Desk_Debug_Printf( "Desk_Menu2__MessageHandler, calling subfn %p\n", lastmenu->subfn);
		nextmenu = lastmenu->subfn( 
			selections[ selectdepth-1], 
			event, 
			lastmenu->reference
			);
		Desk_Debug_Printf( "Desk_Menu2__MessageHandler, nextmenu=%i\n", nextmenu);
		if (nextmenu)	Desk_Menu2__OpenSub( event, nextmenu);
		}
	}

else if ( event->data.message.header.action == Desk_message_MENUSDELETED)	{
	Desk_Menu2__Free( 0);
	Desk_Menu2__Release( reference);
	}

return Desk_bool_FALSE;
}

Desk_SDLS_PtrFn( 
	static, 
	Desk_bool, 
	Desk_Menu2__MenuHandler( Desk_event_pollblock *event, void *reference)
	)
/*static Desk_bool	Desk_Menu2__MenuHandler( Desk_event_pollblock *event, void *reference)*/
/* cc warns about extern ... not declared in header in SDLS compiles	*/
{
int		selectdepth	= Desk_Menu2__SelectionDepth( &event->data.selection[0]);
Desk_menu2_block	*leafmenu;
Desk_mouse_block	mouse;

if ( selectdepth>Desk_menu2__numopen)	selectdepth = Desk_menu2__numopen;

leafmenu	= Desk_menu2__openmenus[ selectdepth-1];

Desk_Wimp_eGetPointerInfo( &mouse);

if ( leafmenu->selectfn)	{
	Desk_Debug_Printf( "Desk_Menu2__MenuHandler calling selectfn %p\n", leafmenu->selectfn);
	leafmenu->selectfn( event->data.selection[ selectdepth-1], leafmenu->reference);
	Desk_Debug_Printf( "Desk_Menu2__MenuHandler called selectfn %p\n", leafmenu->selectfn);
	
	if ( !mouse.button.data.adjust)	Desk_Wimp_eGetPointerInfo( &mouse);
		/* Have a second chance to use Adjust (eg if selecfn	*/
		/* brought up an error-box.				*/
	}
	/*
	Note that this is not necessarily the leaf menu, just the
	leaf-est menu2 opened. This is because the last menu2 
	could have opened non-menu2 menus itself.
	*/

if ( mouse.button.data.adjust)	{

	int	i;
	
	/* Rescan all open menus...	*/
	Desk_Debug_Printf( "Desk_Menu2__MenuHandler reopening menu\n");
	
	for ( i=0; i<Desk_menu2__numopen; i++)	{
		Desk_menu2_block	*m = Desk_menu2__openmenus[ i];
		if ( m->flagsfn)	{
			Desk_Debug_Printf( "Desk_Menu2__MenuHandler calling flagsfn\n");
			/*Desk_Debug_Printf( "menublock is %p %p %p %p %p %p %p %p\n",
				m->title, m->spec, m->makefn, m->flagsfn, m->subfn,
				m->selectfn, m->menu, m->reference
				);
			*/
			m->flagsfn( m->menu, m->reference);
			}
		}
		
	Desk_Debug_Printf( "Desk_Menu2__MenuHandler calling Desk_Menu_ShowLast\n");	
	Desk_Menu_ShowLast();
	}

else	{
	Desk_Menu2__Free( 0);
	Desk_Menu2__Release( reference);
	}

return Desk_bool_FALSE;
}




static void	Desk_Menu2__ClaimRelease( Desk_event_claimorreleasefn fn, void *reference)
{
fn( Desk_event_USERMESSAGE, 		Desk_event_ANY, Desk_event_ANY, Desk_SDLS_dllEntry( Desk_Menu2__MessageHandler), 	reference);
fn( Desk_event_USERMESSAGERECORDED, 	Desk_event_ANY, Desk_event_ANY, Desk_SDLS_dllEntry( Desk_Menu2__MessageHandler), 	reference);
fn( Desk_event_MENU, 			Desk_event_ANY, Desk_event_ANY, Desk_SDLS_dllEntry( Desk_Menu2__MenuHandler), 	reference);
}

