diff options
Diffstat (limited to 'app/wlib/gtklib/menu.c')
-rw-r--r-- | app/wlib/gtklib/menu.c | 1084 |
1 files changed, 1084 insertions, 0 deletions
diff --git a/app/wlib/gtklib/menu.c b/app/wlib/gtklib/menu.c new file mode 100644 index 0000000..fb115a3 --- /dev/null +++ b/app/wlib/gtklib/menu.c @@ -0,0 +1,1084 @@ +/** \file menu.c + * Menu creation and handling. + */ + +/* XTrkCad - Model Railroad CAD + * Copyright (C) 2005 Dave Bullis, 2012 Martin Fischer + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <ctype.h> + +#define GTK_DISABLE_SINGLE_INCLUDES +#define GDK_DISABLE_DEPRECATED +#define GTK_DISABLE_DEPRECATED +#define GSEAL_ENABLE + +#include <gtk/gtk.h> +#include <gdk/gdk.h> + +#include "gtkint.h" +#include "i18n.h" + +#define WLISTITEM "wListItem" /**< id for object data */ + +/* + ***************************************************************************** + * + * Menus + * + ***************************************************************************** + */ + +typedef enum { M_MENU, M_SEPARATOR, M_PUSH, M_LIST, M_LISTITEM, M_TOGGLE, M_RADIO } mtype_e; +typedef enum { MM_BUTT, MM_MENU, MM_BAR, MM_POPUP } mmtype_e; + +typedef struct{ mtype_e mtype; /**< menu entry type */ + GtkWidget *menu_item; + wMenu_p parentMenu; + int recursion; /**< recursion counter */ + } MOBJ_COMMON; /**< menu item specific data */ + + +struct wMenuItem_t { + WOBJ_COMMON + MOBJ_COMMON m; + }; + +typedef struct wMenuItem_t * wMenuItem_p; + +// a few macros to make access to members easier +//#define PTR2M( ptr ) ((ptr)->m) +#define MMENUITEM( ptr ) (((ptr)->m).menu_item) +#define MPARENT( ptr ) (((ptr)->m).parentMenu) +#define MITEMTYPE( ptr ) (((ptr)->m).mtype) +#define MRECURSION( ptr ) (((ptr)->m).recursion) + + +struct wMenu_t { + WOBJ_COMMON + MOBJ_COMMON m; + mmtype_e mmtype; + wMenuItem_p first, last; + GtkWidget * menu; + GSList *radioGroup; /* radio button group */ + wMenuTraceCallBack_p traceFunc; + void * traceData; + GtkLabel * labelG; + GtkWidget * imageG; + }; + +struct wMenuPush_t { + WOBJ_COMMON + MOBJ_COMMON m; + wMenuCallBack_p action; + wBool_t enabled; + }; + +struct wMenuRadio_t { + WOBJ_COMMON + MOBJ_COMMON m; + wMenuCallBack_p action; + wBool_t enabled; + }; + +struct wMenuList_t { + WOBJ_COMMON + MOBJ_COMMON m; + int max; + int count; + wMenuListCallBack_p action; + }; + +struct wMenuListItem_t { + WOBJ_COMMON + MOBJ_COMMON m; + wMenuList_p mlist; + }; + +typedef struct wMenuListItem_t * wMenuListItem_p; + +struct wMenuToggle_t { + WOBJ_COMMON + MOBJ_COMMON m; + wMenuToggleCallBack_p action; + wBool_t enabled; + wBool_t set; + }; + + +/*-----------------------------------------------------------------*/ + +/** + * Handle activate event for menu items. + * + * \param widget IN widget that emitted the signal + * \param value IN application data + * \return + */ + +static void pushMenuItem( + GtkWidget * widget, + gpointer value ) +{ + wMenuItem_p m = (wMenuItem_p)value; + wMenuToggle_p mt; + + if (MRECURSION( m )) + return; + + switch MITEMTYPE( m ) { + case M_PUSH: + ((wMenuPush_p)m)->action( ((wMenuPush_p)m)->data ); + break; + case M_TOGGLE: + mt = (wMenuToggle_p)m; + wMenuToggleSet( mt, !mt->set ); + mt->action( mt->set, mt->data ); + break; + case M_RADIO: + /* NOTE: action is only called when radio button is activated, not when deactivated */ + if( gtk_check_menu_item_get_active((GtkCheckMenuItem *)widget ) == TRUE ) + ((wMenuRadio_p)m)->action( ((wMenuRadio_p)m)->data ); + break; + case M_MENU: + return; + default: + /*fprintf(stderr," Oops menu\n");*/ + return; + } + if( MPARENT(m)->traceFunc ) { + MPARENT(m)->traceFunc( MPARENT( m ), m->labelStr, MPARENT(m)->traceData ); + } +} + +/** + * Create a new menu element, add to the parent menu and to help + * + * \param m IN parent menu + * \param mtype IN type of new entry + * \param helpStr IN help topic + * \param labelStr IN display label + * \param size IN size of additional data? + * \return the newly created menu element + */ + +static wMenuItem_p createMenuItem( + wMenu_p m, + mtype_e mtype, + const char * helpStr, + const char * labelStr, + int size ) +{ + wMenuItem_p mi; + mi = (wMenuItem_p)wlibAlloc( NULL, B_MENUITEM, 0, 0, labelStr, size, NULL ); + MITEMTYPE( mi )= mtype; + + switch ( mtype ) { + case M_LIST: + MMENUITEM( mi ) = gtk_menu_item_new_with_mnemonic(wlibConvertInput(mi->labelStr)); // NULL; //PTR2M(m).menu_item + break; + case M_SEPARATOR: + MMENUITEM( mi ) = gtk_separator_menu_item_new(); + break; + case M_TOGGLE: + MMENUITEM( mi ) = gtk_check_menu_item_new_with_mnemonic(wlibConvertInput(mi->labelStr)); + break; + case M_RADIO: + MMENUITEM( mi ) = gtk_radio_menu_item_new_with_mnemonic(m->radioGroup, wlibConvertInput(mi->labelStr)); + m->radioGroup = gtk_radio_menu_item_get_group (GTK_RADIO_MENU_ITEM (MMENUITEM( mi ))); + break; + default: + MMENUITEM( mi ) = gtk_menu_item_new_with_mnemonic(wlibConvertInput(mi->labelStr)); + break; + } + if (MMENUITEM( mi )) { + if (m) + gtk_menu_shell_append( (GtkMenuShell *)(m->menu), MMENUITEM( mi ) ); + + g_signal_connect( GTK_OBJECT(MMENUITEM( mi )), "activate", + G_CALLBACK(pushMenuItem), mi ); + gtk_widget_show(MMENUITEM( mi )); + } + + // this is a link list of all menu items belonging to a specific menu + // is used in automatic processing (macro)?? + if (m) { + if (m->first == NULL) { + m->first = mi; + } else { + m->last->next = (wControl_p)mi; + } + m->last = mi; + } + mi->next = NULL; + + + if (helpStr != NULL) { + wlibAddHelpString( MMENUITEM( mi ), helpStr ); + } + MPARENT( mi ) = m; + return mi; +} + + +static void setAcclKey( wWin_p w, GtkWidget * menu, GtkWidget * menu_item, int acclKey ) +{ + int mask; + static GtkAccelGroup * accel_alpha_group = NULL; + static GtkAccelGroup * accel_nonalpha_group = NULL; + guint oldmods; + + + if (accel_alpha_group == NULL) { + accel_alpha_group = gtk_accel_group_new(); + /*gtk_accel_group_set_mod_mask( accel_group, GDK_SHIFT_MASK|GDK_CONTROL_MASK|GDK_MOD1_MASK );*/ + gtk_window_add_accel_group(GTK_WINDOW(gtkMainW->gtkwin), accel_alpha_group ); + } + if (accel_nonalpha_group == NULL) { + oldmods = gtk_accelerator_get_default_mod_mask(); + gtk_accelerator_set_default_mod_mask( GDK_CONTROL_MASK | GDK_MOD1_MASK ); + accel_nonalpha_group = gtk_accel_group_new(); + /*gtk_accel_group_set_mod_mask( accel_group, GDK_SHIFT_MASK|GDK_CONTROL_MASK|GDK_MOD1_MASK );*/ + gtk_window_add_accel_group(GTK_WINDOW(gtkMainW->gtkwin), accel_nonalpha_group ); + gtk_accelerator_set_default_mod_mask( oldmods ); + } + + mask = 0; + if (acclKey) { + int len; + char acclStr[40]; + + len = 0; + if (acclKey&WALT) { + mask |= GDK_MOD1_MASK; + strcpy( acclStr+len, "Meta+" ); + len += 5; + } + if (acclKey&WSHIFT) { + mask |= GDK_SHIFT_MASK; + strcpy( acclStr+len, "Shift+" ); + len += 6; + switch ( (acclKey&0xFF) ) { + case '0': acclKey += ')'-'0'; break; + case '1': acclKey += '!'-'1'; break; + case '2': acclKey += '@'-'2'; break; + case '3': acclKey += '#'-'3'; break; + case '4': acclKey += '$'-'4'; break; + case '5': acclKey += '%'-'5'; break; + case '6': acclKey += '^'-'6'; break; + case '7': acclKey += '&'-'7'; break; + case '8': acclKey += '*'-'8'; break; + case '9': acclKey += '('-'9'; break; + case '`': acclKey += '~'-'`'; break; + case '-': acclKey += '_'-'-'; break; + case '=': acclKey += '+'-'='; break; + case '\\': acclKey += '|'-'\\'; break; + case '[': acclKey += '{'-'['; break; + case ']': acclKey += '}'-']'; break; + case ';': acclKey += ':'-';'; break; + case '\'': acclKey += '"'-'\''; break; + case ',': acclKey += '<'-','; break; + case '.': acclKey += '>'-'.'; break; + case '/': acclKey += '?'-'/'; break; + default: break; + } + } + if (acclKey&WCTL) { + mask |= GDK_CONTROL_MASK; + strcpy( acclStr+len, "Ctrl+" ); + len += 5; + } + acclStr[len++] = (acclKey & 0xFF); + acclStr[len++] = '\0'; + gtk_widget_add_accelerator( menu_item, "activate", + (isalpha(acclKey&0xFF)?accel_alpha_group:accel_nonalpha_group), + toupper(acclKey&0xFF), mask, GTK_ACCEL_VISIBLE|GTK_ACCEL_LOCKED ); + } +} + +/*-----------------------------------------------------------------*/ +/** + * Create a radio button as a menu entry + * + * \param m IN menu to be extended + * \param helpStr IN reference into help + * \param labelStr IN text for entry + * \param acclKey IN accelerator key to add + * \param action IN callback function + * \param data IN application data + * \param helpStr IN + * \return menu entry + */ + +wMenuRadio_p wMenuRadioCreate( + wMenu_p m, + const char * helpStr, + const char * labelStr, + long acclKey, + wMenuCallBack_p action, + void *data ) +{ + wMenuRadio_p mi; + + mi = (wMenuRadio_p)createMenuItem( m, M_RADIO, helpStr, labelStr, sizeof *mi ); + //~ if (m->mmtype == MM_POPUP && !testMenuPopup) + //~ return mi; + setAcclKey( m->parent, m->menu, MMENUITEM( mi ), acclKey ); + mi->action = action; + mi->data = data; + mi->enabled = TRUE; + return mi; +} + +/** + * Set radio button active + * + * \param mi IN menu entry for radio button + * \return + */ + +void wMenuRadioSetActive( + wMenuRadio_p mi ) +{ + gtk_check_menu_item_set_active( (GtkCheckMenuItem *)MMENUITEM(mi), TRUE ); +} + +/*-----------------------------------------------------------------*/ + +/** + * Create a menu entry + * + * \param m IN menu to be extended + * \param helpStr IN reference into help + * \param labelStr IN text for entry + * \param acclKey IN acceleratoor key to add + * \param action IN callback function + * \param data IN application data + * \return menu entry + */ + +wMenuPush_p wMenuPushCreate( + wMenu_p m, + const char * helpStr, + const char * labelStr, + long acclKey, + wMenuCallBack_p action, + void *data ) +{ + wMenuPush_p mi; + + mi = (wMenuPush_p)createMenuItem( m, M_PUSH, helpStr, labelStr, sizeof( struct wMenuPush_t )); + + setAcclKey( m->parent, m->menu, MMENUITEM( mi ), acclKey ); + + mi->action = action; + mi->data = data; + mi->enabled = TRUE; + return mi; +} + +/** + * Enable menu entry + * + * \param mi IN menu entry + * \param enable IN new state + * \return + */ + +void wMenuPushEnable( + wMenuPush_p mi, + wBool_t enable ) +{ + mi->enabled = enable; + gtk_widget_set_sensitive( GTK_WIDGET(MMENUITEM( mi )), enable ); +} + + +/*-----------------------------------------------------------------*/ +/** + * Create a submenu + * + * \param m IN menu to be extended + * \param helpStr IN reference into help + * \param labelStr IN text for entry + * \return menu entry + */ + +wMenu_p wMenuMenuCreate( + wMenu_p m, + const char * helpStr, + const char * labelStr ) +{ + wMenu_p mi; + mi = (wMenu_p)createMenuItem( m, M_MENU, helpStr, labelStr, sizeof( struct wMenu_t )); + mi->mmtype = MM_MENU; + mi->menu = gtk_menu_new(); + + gtk_menu_item_set_submenu( GTK_MENU_ITEM(MMENUITEM( mi )), mi->menu ); + return mi; +} + + +/*-----------------------------------------------------------------*/ +/** + * Create a menu separator + * + * \param mi IN menu entry + * \return + */ + +void wMenuSeparatorCreate( + wMenu_p m ) +{ + createMenuItem( m, M_SEPARATOR, NULL, "", sizeof( struct wMenuItem_t )); +} + + +/*-----------------------------------------------------------------*/ + +/** + * Find the start of a menu item list in a menu + * + * \param ml IN menu list to be searched + * \param pChildren OUT list of children in menu container + * \return -1 if not found, index in children list otherwise + */ + +int getMlistOrigin( wMenuList_p ml, GList **pChildren ) +{ + GtkWidget *mi; + int count = 0; + int found = -1; + GtkWidget *mitem = MMENUITEM( ml ); + + *pChildren = gtk_container_get_children( GTK_CONTAINER( MPARENT( ml )->menu )); + if( !*pChildren ) + return( -1 ); + + while( (mi = g_list_nth_data( *pChildren, count ))) { + if( mi == mitem ) { + found = TRUE; + break; + } + else { + count++; + } + } + + if( found ) + return( count ); + else + return( -1 ); +} + +/** + * Signal handler for clicking onto a menu list item. + * Parameters are the GtkWidget as expected and the pointer to the MenuListItem + * + * \param widget IN the GtkWidget + * \param value IN the menu list item + * \return + */ + +static void pushMenuList( + GtkWidget * widget, + gpointer value ) +{ + // pointer to the list item + wMenuListItem_p ml = (wMenuListItem_p)value; + + if (MRECURSION( ml )) + return; + + if (ml->mlist->count <= 0) { + // this should never happen + fprintf( stderr, "pushMenuItem: empty list\n" ); + return; + } + // this is the applications callback routine + if (ml->mlist->action) { + const char * itemLabel; + + itemLabel = gtk_menu_item_get_label( GTK_MENU_ITEM( widget )); + + ml->mlist->action( 0, itemLabel, ml->data ); + return; + } + fprintf( stderr, "pushMenuItem: item (%lx) not found\n", (long)widget ); +} + +/** + * Create a list menu entry + * + * \param m IN menu to be extended + * \param helpStr IN reference into help + * \param max IN maximum number of elements + * \param action IN callback function + * \return menu entry + */ + +wMenuList_p wMenuListCreate( + wMenu_p m, + const char * helpStr, + int max, + wMenuListCallBack_p action ) +{ + wMenuList_p mi; + mi = (wMenuList_p)createMenuItem( m, M_LIST, NULL, _("<Empty List>"), sizeof( struct wMenuList_t )); + gtk_widget_set_sensitive( GTK_WIDGET(MMENUITEM( mi )), FALSE ); + mi->next = NULL; + mi->count = 0; + mi->max = max; + MPARENT( mi ) = m; + mi->action = action; + return (wMenuList_p)mi; +} + + +/** + * Add a new item to a list of menu entries + * The placeholder for the list is looked up. Then the new item is added immediately + * behind it. In case the maximum number of items is reached the last item is removed. + * + * \param ml IN handle for the menu list - the placeholder item + * \param index IN currently ignored + * \param labelStr IN the menu label for the new item + * \param data IN application data for the new item + * \return + */ + +void wMenuListAdd( + wMenuList_p ml, + int index, + const char * labelStr, + const void * data ) +{ + int i = 0; + GList * children; + + i = getMlistOrigin( ml, &children ); + + if( i > -1 ) { + wMenuListItem_p mi; + + // we're adding an item, so hide the default placeholder + gtk_widget_hide( MMENUITEM( ml )); + + // delete an earlier entry with the same label + wMenuListDelete( ml, labelStr ); + + // a new item + ml->count ++; + + // is there a maximum number of items set and reached with the new item? + if(( ml->max != -1 ) && ( ml->count > ml-> max )) { + wMenuListItem_p mold; + GtkWidget * item; + + // get the last item in the list + item = g_list_nth_data( children, i + ml->max ); + // get the pointer to the data structure + mold = g_object_get_data( G_OBJECT( item ), WLISTITEM ); + // kill the menu entry + gtk_widget_destroy( item ); + // free the data + free( (void *)mold->labelStr ); + free( (void *)mold ); + + ml->count--; + } + + // create the new menu item and initialize the data fields + mi = (wMenuListItem_p)wlibAlloc( NULL, B_MENUITEM, 0, 0, labelStr, sizeof( struct wMenuListItem_t ), NULL ); + MITEMTYPE( mi ) = M_LISTITEM; + MMENUITEM( mi ) = gtk_menu_item_new_with_label(wlibConvertInput(mi->labelStr)); + mi->data = (void *)data; + mi->mlist = ml; + g_object_set_data( G_OBJECT(MMENUITEM( mi )), WLISTITEM, mi ); + + // add the item to the menu + gtk_menu_shell_insert((GtkMenuShell *)(MPARENT( ml )->menu), MMENUITEM( mi ), i + 1 ); + g_signal_connect( GTK_OBJECT(MMENUITEM( mi )), "activate", G_CALLBACK(pushMenuList), mi ); + + gtk_widget_show(MMENUITEM( mi )); + } + + if( children ) + g_list_free( children ); +} + +/** + * Remove the menu entry identified by a given label. + * + * \param ml IN menu list + * \param labelStr IN label string of item + */ + +void wMenuListDelete( + wMenuList_p ml, + const char * labelStr ) +{ + int i; + int found = FALSE; + GList * children; + + // find the placeholder for the list in the menu + i = getMlistOrigin( ml, &children ); + + if( i > -1 ) { + int origin; + GtkWidget * item; + char * labelStrConverted; + + // starting from the placeholder, find the menu item with the correct text + found = FALSE; + labelStrConverted = wlibConvertInput( labelStr ); + origin = i; + + // get menu item + // get label of item + // compare items + // if identical, leave loop + while( i <= origin + ml->count && !found ) { + const char * itemLabel; + + item = g_list_nth_data( children, i ); + itemLabel = gtk_menu_item_get_label( GTK_MENU_ITEM( item )); + if( !g_utf8_collate (itemLabel, labelStrConverted )) + found = TRUE; + else + i++; + } + if( found ) { + wMenuListItem_p mold; + + mold = g_object_get_data( G_OBJECT( item ), WLISTITEM ); + // kill the menu entry + gtk_widget_destroy( item ); + // free the data + free( (void *)mold->labelStr ); + free( (void *)mold ); + + ml->count--; + } + } + + if( children ) + g_list_free( children ); +} + +/** + * Get the label and the application data of a specific menu list item + * + * \param ml IN menu list + * \param index IN item within list + * \param data OUT application data + * \return item label + */ + +const char * +wMenuListGet( wMenuList_p ml, int index, void ** data ) +{ + int i; + + GList * children; + const char * itemLabel = NULL; + + // check whether index is in range, if not return immediately + if ( index >= ml->count || ml->count <= 0 ) { + if (data) + *data = NULL; + return NULL; + } + + // find the placeholder for the list in the menu + i = getMlistOrigin( ml, &children ); + + if( i > -1 ) { + GtkWidget * item; + wMenuListItem_p mold; + + item = g_list_nth_data( children, i + index + 1 ); + itemLabel = gtk_menu_item_get_label( GTK_MENU_ITEM( item )); + mold = g_object_get_data( G_OBJECT( GTK_MENU_ITEM( item ) ), WLISTITEM ); + *data = mold->data; + } + + if( children ) + g_list_free( children ); + + return itemLabel; +} + +/** + * Remove all items from menu list + * + * \param ml IN menu item list + */ + +void wMenuListClear( + wMenuList_p ml ) +{ + int origin; + GList * children; + + if (ml->count == 0) + return; + + origin = getMlistOrigin( ml, &children ); + + if( origin > -1 ) { + int i; + + i = origin; + while( i < origin + ml->count ) { + wMenuListItem_p mold; + GtkWidget * item; + + item = g_list_nth_data( children, i + 1 ); + mold = g_object_get_data( G_OBJECT( item ), WLISTITEM ); + // kill the menu entry + gtk_widget_destroy( item ); + // free the data + free( (void *)mold->labelStr ); + free( (void *)mold ); + i++; + } + } + + ml->count = 0; + gtk_widget_show( MMENUITEM( ml )); + + if( children ) + g_list_free( children ); +} +/*-----------------------------------------------------------------*/ +/** + * Create a check box as part of a menu + * + * \param m IN menu to be extended + * \param helpStr IN reference into help + * \param labelStr IN text for entry + * \param acclKey IN acceleratoor key to add + * \param set IN initial state + * \param action IN callback function + * \param data IN application data + * \return menu entry + */ + +wMenuToggle_p wMenuToggleCreate( + wMenu_p m, + const char * helpStr, + const char * labelStr, + long acclKey, + wBool_t set, + wMenuToggleCallBack_p action, + void * data ) +{ + wMenuToggle_p mt; + + mt = (wMenuToggle_p)createMenuItem( m, M_TOGGLE, helpStr, labelStr, sizeof( struct wMenuToggle_t )); + setAcclKey( m->parent, m->menu, MMENUITEM( mt ), acclKey ); + mt->action = action; + mt->data = data; + mt->enabled = TRUE; + MPARENT( mt ) = m; + wMenuToggleSet( mt, set ); + + return mt; +} + +/** + * Get the state of a menu check box + * + * \param mt IN menu to be extended + * \return current state + */ + +wBool_t wMenuToggleGet( + wMenuToggle_p mt ) +{ + return mt->set; +} + +/** + * Set a menu check box active / inactive + * + * \param mt IN menu to be extended + * \param set IN new state + * \return previous state + */ + +wBool_t wMenuToggleSet( + wMenuToggle_p mt, + wBool_t set ) +{ + wBool_t rc; + if (mt==NULL) return 0; + MRECURSION( mt )++; + gtk_check_menu_item_set_active( GTK_CHECK_MENU_ITEM(MMENUITEM( mt )), set ); + MRECURSION( mt )--; + rc = mt->set; + mt->set = set; + return rc; +} + +/** + * Enable menu entry containing a check box + * + * \param mi IN menu entry + * \param enable IN new state + * \return + */ + +void wMenuToggleEnable( + wMenuToggle_p mt, + wBool_t enable ) +{ + mt->enabled = enable; +} + + +/*-----------------------------------------------------------------*/ + +/** + * Set the text for a menu + * + * \param m IN menu entry + * \param labelStr IN new text + * \return + */ + +void wMenuSetLabel( wMenu_p m, const char * labelStr) { + wlibSetLabel( m->widget, m->option, labelStr, &m->labelG, &m->imageG ); +} + +/** + * Signal handler for menu items. Parameters are the GtkWidget as + * expected and the pointer to the MenuListItem + * + * \param widget IN the GtkWidget + * \param value IN the menu list item + * \return + */ + +static gint pushMenu( + GtkWidget * widget, + wMenu_p m ) +{ + gtk_menu_popup( GTK_MENU(m->menu), NULL, NULL, NULL, NULL, 0, 0 ); + /* Tell calling code that we have handled this event; the buck + * stops here. */ + return TRUE; +} + +/** + * Create a button with a drop down menu + * + * \param parent IN parent window + * \param x IN x position + * \param y IN y position + * \param helpStr IN help anchor string + * \param labelStr IN label for menu + * \param option IN options (Whatever they are) + * \return pointer to the created menu + */ + +wMenu_p wMenuCreate( + wWin_p parent, + wPos_t x, + wPos_t y, + const char * helpStr, + const char * labelStr, + long option ) +{ + wMenu_p m; + m = wlibAlloc( parent, B_MENU, x, y, labelStr, sizeof( struct wMenu_t ), NULL ); + m->mmtype = MM_BUTT; + m->option = option; + m->traceFunc = NULL; + m->traceData = NULL; + wlibComputePos( (wControl_p)m ); + + m->widget = gtk_button_new(); + g_signal_connect (GTK_OBJECT(m->widget), "clicked", + G_CALLBACK(pushMenu), m ); + + m->menu = gtk_menu_new(); + + wMenuSetLabel( m, labelStr ); + + gtk_fixed_put( GTK_FIXED(parent->widget), m->widget, m->realX, m->realY ); + wlibControlGetSize( (wControl_p)m ); + if ( m->w < 80 && (m->option&BO_ICON)==0) { + m->w = 80; + gtk_widget_set_size_request( m->widget, m->w, m->h ); + } + gtk_widget_show( m->widget ); + wlibAddButton( (wControl_p)m ); + wlibAddHelpString( m->widget, helpStr ); + return m; +} + +/** + * Add a drop-down menu to the menu bar. + * + * \param w IN main window handle + * \param helpStr IN unused (should be help topic ) + * \param labelStr IN label for the drop-down menu + * \return pointer to the created drop-down menu + */ + +wMenu_p wMenuBarAdd( + wWin_p w, + const char * helpStr, + const char * labelStr ) +{ + wMenu_p m; + GtkWidget * menuItem; + + m = wlibAlloc( w, B_MENU, 0, 0, labelStr, sizeof( struct wMenu_t ), NULL ); + m->mmtype = MM_BAR; + m->realX = 0; + m->realY = 0; + + menuItem = gtk_menu_item_new_with_mnemonic( wlibConvertInput(m->labelStr) ); + m->menu = gtk_menu_new(); + gtk_menu_item_set_submenu( GTK_MENU_ITEM(menuItem), m->menu ); + gtk_menu_shell_append( GTK_MENU_SHELL(w->menubar), menuItem ); + gtk_widget_show( menuItem ); + + m->w = 0; + m->h = 0; + + /* TODO: why is help not supported here? */ + /*gtkAddHelpString( m->panel_item, helpStr );*/ + + return m; +} + + +/*-----------------------------------------------------------------*/ + +/** + * Create a popup menu (context menu) + * + * \param w IN parent window + * \param labelStr IN label + * \return the created menu + */ + +wMenu_p wMenuPopupCreate( + wWin_p w, + const char * labelStr ) +{ + wMenu_p b; + b = wlibAlloc( w, B_MENU, 0, 0, labelStr, sizeof *b, NULL ); + b->mmtype = MM_POPUP; + b->option = 0; + + b->menu = gtk_menu_new(); + b->w = 0; + b->h = 0; + g_signal_connect( GTK_OBJECT (b->menu), "key_press_event", + G_CALLBACK(catch_shift_ctrl_alt_keys), b); + g_signal_connect( GTK_OBJECT (b->menu), "key_release_event", + G_CALLBACK (catch_shift_ctrl_alt_keys), b); + gtk_widget_set_events ( GTK_WIDGET(b->menu), GDK_EXPOSURE_MASK|GDK_KEY_PRESS_MASK|GDK_KEY_RELEASE_MASK ); + return b; +} + +/** + * Show a context menu + * + * \param mp IN the context menu + */ + +void wMenuPopupShow( wMenu_p mp ) +{ + gtk_menu_popup( GTK_MENU(mp->menu), NULL, NULL, NULL, NULL, 0, 0 ); +} + + +/*-----------------------------------------------------------------*/ + +/** + * ?? Seems to be related to macro / automatic playback functionality + * + * \param m IN + * \param func IN + * \param data IN + */ + +void wMenuSetTraceCallBack( + wMenu_p m, + wMenuTraceCallBack_p func, + void * data ) +{ + m->traceFunc = func; + m->traceData = data; +} + +/** + * ??? same as above + * \param m IN + * \param label IN + * \return describe the return value + */ + +wBool_t wMenuAction( + wMenu_p m, + const char * label ) +{ + wMenuItem_p mi; + wMenuToggle_p mt; + for ( mi = m->first; mi != NULL; mi = (wMenuItem_p)mi->next ) { + if ( strcmp( mi->labelStr, label ) == 0 ) { + switch( MITEMTYPE( mi )) { + case M_SEPARATOR: + break; + case M_PUSH: + if ( ((wMenuPush_p)mi)->enabled == FALSE ) + wBeep(); + else + ((wMenuPush_p)mi)->action( ((wMenuPush_p)mi)->data ); + break; + case M_TOGGLE: + mt = (wMenuToggle_p)mi; + if ( mt->enabled == FALSE ) { + wBeep(); + } else { + wMenuToggleSet( mt, !mt->set ); + mt->action( mt->set, mt->data ); + } + break; + case M_MENU: + break; + case M_LIST: + break; + default: + /*fprintf(stderr, "Oops: wMenuAction\n");*/ + break; + } + return TRUE; + } + } + return FALSE; +} |