/*- * Copyright (c) 1997-2002 The Protein Laboratory, University of Copenhagen * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $Id$ */ #include "apricot.h" #include "AbstractMenu.h" #include "Image.h" #include "Menu.h" #include "Widget.h" #include #ifdef __cplusplus extern "C" { #endif #undef my #define inherited CComponent-> #define my ((( PAbstractMenu) self)-> self) #define var (( PAbstractMenu) self) typedef Bool MenuProc ( Handle self, PMenuItemReg m, void * params); typedef MenuProc *PMenuProc; static int key_normalize( const char * key) { /* * Valid keys: * keycode as a string representing decimal number; * any combination of ^, @, # (Control, Alt, Shift) plus * exactly one character - lowercase and get ascii code of this * fN - function key, N is a number from 1 to 16 inclusive * All other combinations will result in kbNoKey returned */ int r = 0, r1; for (;;) { if (*key == '^') r |= kmCtrl; else if (*key == '@') r |= kmAlt; else if (*key == '#') r |= kmShift; else break; key++; } if (!*key) return kbNoKey; /* #, ^, @ alone are not allowed */ if (!key[1]) { return (r&kmCtrl) && isalpha(*key) ? r | (toupper(*key)-'@') : r | tolower(*key); } else { char *e; if (isdigit(*key)) { if (r) return kbNoKey; r = strtol( key, &e, 10); if (*e) return kbNoKey; if ( !( r & kmCtrl)) return r; return ( isalpha( r & kbCharMask)) ? ( r & kbModMask) | ( toupper( r & kbCharMask)-'@') : r; } else if (tolower(*key) != 'f') return kbNoKey; key++; r1 = strtol( key, &e, 10); if (*e || r1 < 1 || r1 > 16) return kbNoKey; return r | (kbF1 + ((r1-1) << 8)); } } static int is_var_id_name( char * name) { int ret; char * e; if ( !name || *(name++) != '#') return 0; ret = strtol( name, &e, 10); if ( *e || ret < 0) return 0; return ret; } void AbstractMenu_dispose_menu( Handle self, void * menu) { PMenuItemReg m = ( PMenuItemReg) menu; if ( m == nil) return; free( m-> text); free( m-> accel); free( m-> variable); free( m-> perlSub); if ( m-> code) sv_free( m-> code); if ( m-> data) sv_free( m-> data); if ( m-> bitmap) { if ( PObject( m-> bitmap)-> stage < csDead) SvREFCNT_dec( SvRV(( PObject( m-> bitmap))-> mate)); unprotect_object( m-> bitmap); } my-> dispose_menu( self, m-> next); my-> dispose_menu( self, m-> down); free( m); } /* #define log_write debug_write */ void * AbstractMenu_new_menu( Handle self, SV * sv, int level) { AV * av; int i, count; int n; PMenuItemReg m = nil; PMenuItemReg curr = nil; Bool rightAdjust = false; /* char buf [ 200]; memset( buf, ' ', 200); buf[ level * 3] = '\0'; */ if ( level == 0) { if ( SvTYPE( sv) == SVt_NULL) return nil; /* null menu */ } if ( !SvROK( sv) || ( SvTYPE( SvRV( sv)) != SVt_PVAV)) { warn("RTC0034: menu build error: menu is not an array"); return nil; } av = (AV *) SvRV( sv); n = av_len( av); if ( n == -1) { if ( level == 0) return nil; /* null menu */ warn("RTC003E: menu build error: empty array passed"); return nil; } /* log_write("%s(%d){", buf, n+1); */ /* cycling the list of items */ for ( i = 0; i <= n; i++) { SV **itemHolder = av_fetch( av, i, 0); AV *item; SV *subItem; PMenuItemReg r; SV **holder; int l_var = -1; int l_text = -1; int l_sub = -1; int l_accel = -1; int l_key = -1; int l_data = -1; if ( itemHolder == nil) { warn("RTC0035: menu build error: array panic"); my-> dispose_menu( self, m); return nil; } if ( !SvROK( *itemHolder) || ( SvTYPE( SvRV( *itemHolder)) != SVt_PVAV)) { warn("RTC0036: menu build error: submenu is not an array"); my-> dispose_menu( self, m); return nil; } /* entering item description */ item = ( AV *) SvRV( *itemHolder); count = av_len( item) + 1; if ( count > 6) { warn("RTC0032: menu build error: extra declaration"); count = 5; } if ( !( r = alloc1z( MenuItemReg))) { warn( "Not enough memory"); my-> dispose_menu( self, m); return nil; } r-> key = kbNoKey; /* log_write("%sNo: %d, count: %d", buf, i, count); */ if ( count < 2) { /* empty or 1 means line divisor, no matter of text */ r-> flags. divider = true; rightAdjust = (( level == 0) && ( var-> anchored)); if ( count == 1) l_var = 0; } else if ( count == 2) { l_text = 0; l_sub = 1; } else if ( count == 3) { l_var = 0; l_text = 1; l_sub = 2; } else if ( count == 4) { l_text = 0; l_accel = 1; l_key = 2; l_sub = 3; } else if ( count == 5) { l_var = 0; l_text = 1; l_accel = 2; l_key = 3; l_sub = 4; } else { l_var = 0; l_text = 1; l_accel = 2; l_key = 3; l_sub = 4; l_data = 5; } if ( m) curr = curr-> next = r; else curr = m = r; /* adding to list */ r-> flags. rightAdjust = rightAdjust ? 1 : 0; r-> id = ++(var-> autoEnum); #define a_get( l_, fl_, num) if ( num >= 0 ) { \ holder = av_fetch( item, num, 0); \ if ( holder) { \ if ( SvTYPE(*holder) != SVt_NULL) { \ l_ = duplicate_string( SvPV_nolen( *holder)); \ fl_ = prima_is_utf8_sv(*holder); \ } \ } else { \ warn("RTC003A: menu build error: array panic"); \ my-> dispose_menu( self, m); \ return nil; \ } \ } a_get( r-> accel , r-> flags. utf8_accel, l_accel); a_get( r-> variable, r-> flags. utf8_variable, l_var); if ( l_key >= 0) { holder = av_fetch( item, l_key, 0); if ( !holder) { warn("RTC003B: menu build error: array panic"); my-> dispose_menu( self, m); return nil; } r-> key = key_normalize( SvPV_nolen( *holder)); } if ( r-> variable) { #define s r-> variable int i, decr = 0; for ( i = 0; i < 2; i++) { switch ( s[i]) { case '-': r-> flags. disabled = 1; decr++; break; case '*': r-> flags. checked = 1; decr++; break; default: break; } } if ( decr) memmove( s, s + decr, strlen( s) + 1 - decr); if ( strlen( s) == 0 || is_var_id_name( s) != 0) { free( r-> variable); r-> variable = nil; } #undef s } /* parsing text */ if ( l_text >= 0) { holder = av_fetch( item, l_text, 0); if ( !holder) { warn("RTC003C: menu build error: array panic"); my-> dispose_menu( self, m); return nil; } subItem = *holder; if ( SvROK( subItem)) { Handle c_object = gimme_the_mate( subItem); if (( c_object == nilHandle) || !( kind_of( c_object, CImage))) { warn("RTC0033: menu build error: not an image passed"); goto TEXT; } /* log_write("%sbmp: %s %d", buf, ((PComponent)c_object)->name, kind_of( c_object, CImage)); */ if (((( PImage) c_object)-> w == 0) || ((( PImage) c_object)-> h == 0)) { warn("RTC0037: menu build error: invalid image passed"); goto TEXT; } protect_object( r-> bitmap = c_object); SvREFCNT_inc( SvRV(( PObject( r-> bitmap))-> mate)); } else { TEXT: r-> text = duplicate_string( SvPV_nolen( subItem)); r-> flags. utf8_text = prima_is_utf8_sv( subItem); } } /* parsing sub */ if ( l_sub >= 0) { holder = av_fetch( item, l_sub, 0); if ( !holder) { warn("RTC003D: menu build error: array panic"); my-> dispose_menu( self, m); return nil; } subItem = *holder; if ( SvROK( subItem)) { if ( SvTYPE( SvRV( subItem)) == SVt_PVCV) { r-> code = newSVsv( subItem); } else { r-> down = ( PMenuItemReg) my-> new_menu( self, subItem, level + 1); if ( r-> down == nil) { /* seems error was occured inside this call */ my-> dispose_menu( self, m); return nil; } } } else { if ( SvPOK( subItem)) { r-> perlSub = duplicate_string( SvPV_nolen( subItem)); r-> flags. utf8_perlSub = prima_is_utf8_sv( subItem); } else { warn("RTC0038: menu build error: invalid sub name passed"); } } } /* parsing data */ if ( l_data >= 0) { holder = av_fetch( item, l_data, 0); if ( !holder) { warn("RTC003D: menu build error: array panic"); my-> dispose_menu( self, m); return nil; } r-> data = newSVsv( *holder); } } /* log_write("%s}", buf); */ /* log_write("adda bunch:"); { PMenuItemReg x = m; while ( x) { log_write( x-> variable); x = x-> next; } } log_write("end."); */ return m; } void AbstractMenu_init( Handle self, HV * profile) { dPROFILE; inherited init( self, profile); var-> anchored = kind_of( self, CMenu); my-> update_sys_handle( self, profile); my-> set_items( self, pget_sv( items)); if ( var-> system) apc_menu_update( self, nil, var-> tree); if ( pget_B( selected)) my-> set_selected( self, true); CORE_INIT_TRANSIENT(AbstractMenu); } void AbstractMenu_done( Handle self) { if ( var-> system) apc_menu_destroy( self); my-> dispose_menu( self, var-> tree); var-> tree = nil; inherited done( self); } Bool AbstractMenu_validate_owner( Handle self, Handle * owner, HV * profile) { dPROFILE; *owner = pget_H( owner); if ( !kind_of( *owner, CWidget)) return false; return inherited validate_owner( self, owner, profile); } void AbstractMenu_cleanup( Handle self) { if ( my-> get_selected( self)) my-> set_selected( self, false); inherited cleanup( self); } void AbstractMenu_set( Handle self, HV * profile) { dPROFILE; Bool select = false; if ( pexist( owner)) { select = pexist( selected) ? pget_B( selected) : my-> get_selected( self); pdelete( selected); } inherited set( self, profile); if ( select) my-> set_selected( self, true); } static SV * new_av( PMenuItemReg m, int level) { AV * glo; if ( m == nil) return nilSV; glo = newAV(); while ( m) { AV * loc = newAV(); if ( !m-> flags. divider) { if ( m-> variable) { /* has name */ SV * sv; int shift = ( m-> flags. checked ? 1 : 0) + ( m-> flags. disabled ? 1 : 0); if ( shift > 0) { /* has flags */ int len = (int) strlen( m-> variable); char * name = allocs( len + shift); if ( name) { int slen = len + shift; memcpy( name + shift, m-> variable, len); if ( m-> flags. checked) name[ --shift] = '*'; if ( m-> flags. disabled) name[ --shift] = '-'; sv = newSVpv( name, slen); } else sv = newSVpv( m-> variable, len); } else /* has name but no flags */ sv = newSVpv( m-> variable, 0); if ( m-> flags. utf8_variable) SvUTF8_on( sv); av_push( loc, sv); } else { /* has flags but no name - autogenerate */ int len; char buffer[20]; len = sprintf( buffer, "%s%s#%d", m-> flags. disabled ? "-" : "", m-> flags. checked ? "*" : "", m-> id); av_push( loc, newSVpv( buffer, ( STRLEN) len)); } if ( m-> bitmap) { if ( PObject( m-> bitmap)-> stage < csDead) av_push( loc, newRV( SvRV((( PObject)( m-> bitmap))-> mate))); else av_push( loc, newSVpv( "", 0)); } else { SV * sv = newSVpv( m-> text, 0); if ( m-> flags. utf8_text) SvUTF8_on( sv); av_push( loc, sv); } if ( m-> accel) { SV * sv = newSVpv( m-> accel, 0); av_push( loc, sv); if ( m-> flags. utf8_accel) SvUTF8_on( sv); } else { av_push( loc, newSVpv( "", 0)); } av_push( loc, newSViv( m-> key)); if ( m-> down) { av_push( loc, new_av( m-> down, level + 1)); } else if ( m-> code) { av_push( loc, newSVsv( m-> code)); } else if ( m-> perlSub) { SV * sv = newSVpv( m-> perlSub, 0); if ( m-> flags. utf8_perlSub) SvUTF8_on( sv); av_push( loc, sv); } else { av_push( loc, newSVpv( "", 0)); } if ( m-> data) av_push( loc, newSVsv( m-> data)); } else { /* divider */ if ( m-> variable) { SV * sv = newSVpv( m-> variable, 0); if ( m-> flags. utf8_perlSub) SvUTF8_on( sv); av_push( loc, sv); } else { int len; char buffer[20]; len = sprintf( buffer, "#%d", m-> id); av_push( loc, newSVpv( buffer, ( STRLEN) len)); } } av_push( glo, newRV_noinc(( SV *) loc)); m = m-> next; } return newRV_noinc(( SV *) glo); } static Bool var_match( Handle self, PMenuItemReg m, void * params) { if ( m-> variable == nil) return false; return ( strcmp( m-> variable, ( char *) params) == 0); } static Bool id_match( Handle self, PMenuItemReg m, void * params) { return m-> id == *(( int*) params); } static Bool key_match( Handle self, PMenuItemReg m, void * params) { return (( m-> key == *(( int*) params)) && ( m-> key != kbNoKey) && !( m-> flags. disabled)); } static PMenuItemReg find_menuitem( Handle self, char * var_name, Bool match_disabled) { int num; if ( !var_name) return nil; /* match special case /^#\d+$/ */ if (( num = is_var_id_name( var_name)) != 0) return ( PMenuItemReg) my-> first_that( self, (void*)id_match, &num, match_disabled); else return ( PMenuItemReg) my-> first_that( self, (void*)var_match, var_name, match_disabled); } char * AbstractMenu_make_var_context( Handle self, PMenuItemReg m, char * buffer) { if ( !m) return ""; if ( m-> variable) return m-> variable; sprintf( buffer, "#%d", m-> id); return buffer; } char * AbstractMenu_make_id_context( Handle self, int id, char * buffer) { return my-> make_var_context( self, my-> first_that( self, (void*)id_match, &id, true), buffer); } SV * AbstractMenu_get_items( Handle self, char * varName) { if ( var-> stage > csFrozen) return nilSV; if ( strlen( varName)) { PMenuItemReg m = find_menuitem( self, varName, true); if ( m && m-> down) { return new_av( m-> down, 1); } else if ( m) { return newRV_noinc(( SV *) newAV()); } else { return nilSV; } } else { return var-> tree ? new_av( var-> tree, 0) : newRV_noinc(( SV *) newAV()); } } void AbstractMenu_set_items( Handle self, SV * items) { PMenuItemReg oldBranch = var-> tree; if ( var-> stage > csFrozen) return; var-> tree = ( PMenuItemReg) my-> new_menu( self, items, 0); if ( var-> stage <= csNormal && var-> system) apc_menu_update( self, oldBranch, var-> tree); my-> dispose_menu( self, oldBranch); } static PMenuItemReg do_link( Handle self, PMenuItemReg m, PMenuProc p, void * params, Bool useDisabled) { while( m) { if ( !m-> flags. disabled || useDisabled) { if ( m-> down) { PMenuItemReg i = do_link( self, m-> down, p, params, useDisabled); if ( i) return i; } if ( p( self, m, params)) return m; } m = m-> next; } return nil; } void * AbstractMenu_first_that( Handle self, void * actionProc, void * params, Bool useDisabled) { return actionProc ? do_link( self, var-> tree, ( PMenuProc) actionProc, params, useDisabled) : nil; } Bool AbstractMenu_has_item( Handle self, char * varName) { return find_menuitem( self, varName, true) != nil; } SV * AbstractMenu_accel( Handle self, Bool set, char * varName, SV * accel) { PMenuItemReg m; if ( var-> stage > csFrozen) return nilSV; m = find_menuitem( self, varName, true); if ( !m) return nilSV; if ( !set) { SV * sv = newSVpv( m-> accel ? m-> accel : "", 0); if ( m-> flags. utf8_accel) SvUTF8_on( sv); return sv; } if ( m-> text == nil) return nilSV; free( m-> accel); m-> accel = duplicate_string( SvPV_nolen( accel)); m-> flags. utf8_accel = prima_is_utf8_sv( accel); if ( m-> id > 0) if ( var-> stage <= csNormal && var-> system) apc_menu_item_set_accel( self, m); return nilSV; } SV * AbstractMenu_action( Handle self, Bool set, char * varName, SV * action) { PMenuItemReg m; if ( var-> stage > csFrozen) return nilSV; m = find_menuitem( self, varName, true); if ( !m) return nilSV; if ( !set) { if ( m-> code) return newSVsv( m-> code); if ( m-> perlSub) { SV * sv = newSVpv( m-> perlSub, 0); if ( m-> flags. utf8_perlSub) SvUTF8_on( sv); return sv; } return nilSV; } if ( m-> flags. divider || m-> down) return nilSV; if ( SvROK( action)) { if ( m-> code) sv_free( m-> code); m-> code = nil; if ( SvTYPE( SvRV( action)) == SVt_PVCV) { m-> code = newSVsv( action); free( m-> perlSub); m-> perlSub = nil; } m-> flags. utf8_perlSub = 0; } else { char * line = ( char *) SvPV_nolen( action); free( m-> perlSub); if ( m-> code) sv_free( m-> code); m-> code = nil; m-> perlSub = duplicate_string( line); m-> flags. utf8_perlSub = prima_is_utf8_sv( action); } return nilSV; } Bool AbstractMenu_checked( Handle self, Bool set, char * varName, Bool checked) { PMenuItemReg m; if ( var-> stage > csFrozen) return false; m = find_menuitem( self, varName, true); if ( m == nil) return false; if ( !set) return m ? m-> flags. checked : false; if ( m-> flags. divider || m-> down) return false; m-> flags. checked = checked ? 1 : 0; if ( m-> id > 0) if ( var-> stage <= csNormal && var-> system) apc_menu_item_set_check( self, m); return checked; } SV * AbstractMenu_data( Handle self, Bool set, char * varName, SV * data) { PMenuItemReg m; if ( var-> stage > csFrozen) return nilSV; m = find_menuitem( self, varName, true); if ( m == nil) return nilSV; if ( !set) return m-> data ? newSVsv( m-> data) : nilSV; sv_free( m-> data); m-> data = newSVsv( data); return nilSV; } Bool AbstractMenu_enabled( Handle self, Bool set, char * varName, Bool enabled) { PMenuItemReg m; if ( var-> stage > csFrozen) return false; m = find_menuitem( self, varName, true); if ( m == nil) return false; if ( !set) return m ? !m-> flags. disabled : false; if (m-> flags. divider) return false; m-> flags. disabled = ( enabled ? 0 : 1 ) ; if ( m-> id > 0) if ( var-> stage <= csNormal && var-> system) apc_menu_item_set_enabled( self, m); return enabled; } Handle AbstractMenu_image( Handle self, Bool set, char * varName, Handle image) { PMenuItemReg m; PImage i = ( PImage) image; if ( var-> stage > csFrozen) return nilHandle; m = find_menuitem( self, varName, true); if ( m == nil) return nilHandle; if ( !m-> bitmap) return nilHandle; if ( !set) { if ( PObject( m-> bitmap)-> stage == csDead) return nilHandle; return m-> bitmap; } if (( image == nilHandle) || !( kind_of( image, CImage))) { warn("RTC0039: invalid object passed to ::image"); return nilHandle; } if ( i-> w == 0 || i-> h == 0) { warn("RTC0039: invalid object passed to ::image"); return nilHandle; } SvREFCNT_inc( SvRV(( PObject( image))-> mate)); protect_object( image); if ( PObject( m-> bitmap)-> stage < csDead) SvREFCNT_dec( SvRV(( PObject( m-> bitmap))-> mate)); unprotect_object( m-> bitmap); m-> bitmap = image; if ( m-> id > 0) if ( var-> stage <= csNormal && var-> system) apc_menu_item_set_image( self, m); return nilHandle; } SV * AbstractMenu_text( Handle self, Bool set, char * varName, SV * text) { PMenuItemReg m; if ( var-> stage > csFrozen) return nilSV; m = find_menuitem( self, varName, true); if ( m == nil) return nilSV; if ( m-> text == nil) return nilSV; if ( !set) { SV * sv = newSVpv( m-> text ? m-> text : "", 0); if ( m-> flags. utf8_text) SvUTF8_on( sv); return sv; } free( m-> text); m-> text = duplicate_string( SvPV_nolen( text)); m-> flags. utf8_accel = prima_is_utf8_sv( text); if ( m-> id > 0) if ( var-> stage <= csNormal && var-> system) apc_menu_item_set_text( self, m); return nilSV; } SV * AbstractMenu_key( Handle self, Bool set, char * varName, SV * key) { PMenuItemReg m; if ( var-> stage > csFrozen) return nilSV; m = find_menuitem( self, varName, true); if ( m == nil) return nilSV; if ( m-> flags. divider || m-> down) return nilSV; if ( !set) return newSViv( m-> key); m-> key = key_normalize( SvPV_nolen( key)); if ( m-> id > 0) if ( var-> stage <= csNormal && var-> system) apc_menu_item_set_key( self, m); return nilSV; } void AbstractMenu_set_variable( Handle self, char * varName, SV * newName) { PMenuItemReg m; if ( var-> stage > csFrozen) return; m = find_menuitem( self, varName, true); if ( m == nil) return; free( m-> variable); if ( SvTYPE(newName) != SVt_NULL) { STRLEN len; char * v; v = SvPV( newName, len); if ( len > 0) { m-> variable = duplicate_string( v); m-> flags. utf8_variable = prima_is_utf8_sv( newName); return; } } m-> variable = nil; m-> flags. utf8_variable = 0; } Bool AbstractMenu_sub_call( Handle self, PMenuItemReg m) { char buffer[16], *context; if ( m == nil) return false; context = AbstractMenu_make_var_context( self, m, buffer); if ( m-> code) { if ( m-> flags. utf8_variable) { SV * sv = newSVpv( context, 0); SvUTF8_on( sv); cv_call_perl((( PComponent) var-> owner)-> mate, SvRV( m-> code), "S", sv); sv_free( sv); } else cv_call_perl((( PComponent) var-> owner)-> mate, SvRV( m-> code), "s", context); } else if ( m-> perlSub) { if ( m-> flags. utf8_variable) { SV * sv = newSVpv( context, 0); SvUTF8_on( sv); call_perl( var-> owner, m-> perlSub, "S", sv); sv_free( sv); } else call_perl( var-> owner, m-> perlSub, "s", context); } return true; } Bool AbstractMenu_sub_call_id( Handle self, int sysId) { return my-> sub_call( self, ( PMenuItemReg) my-> first_that( self, (void*)id_match, &sysId, false)); } #define keyRealize( key) if ((( key & 0xFF) >= 'A') && (( key & 0xFF) <= 'z')) \ key = tolower( key & 0xFF) | \ (( key & ( kmCtrl | kmAlt)) ? \ ( key & ( kmCtrl | kmAlt | kmShift)) \ : 0) Bool AbstractMenu_sub_call_key ( Handle self, int key) { keyRealize( key); return my-> sub_call( self, ( PMenuItemReg) my-> first_that( self, (void*)key_match, &key, false)); } typedef struct _Kmcc { int key; Bool enabled; } Kmcc, *PKmcc; static Bool kmcc ( Handle self, PMenuItemReg m, void * params) { if ((( PKmcc) params)-> key == m-> key) { m-> flags. disabled = ((( PKmcc) params)-> enabled ? 0 : 1); if ( m-> id > 0) if ( var-> stage <= csNormal && var-> system) apc_menu_item_set_enabled( self, m); } return false; } void AbstractMenu_set_command( Handle self, char * key, Bool enabled) { Kmcc mcc; mcc. key = key_normalize( key); mcc. enabled = enabled; if ( var-> stage > csFrozen) return; my-> first_that( self, (void*)kmcc, &mcc, true); } Bool AbstractMenu_selected( Handle self, Bool set, Bool selected) { return false; } SV * AbstractMenu_get_handle( Handle self) { char buf[ 256]; snprintf( buf, 256, "0x%08lx", var-> system ? apc_menu_get_handle( self) : self); return newSVpv( buf, 0); } int AbstractMenu_translate_accel( Handle self, char * accel) { if ( !accel) return 0; while ( *accel) { if ( *(accel++) == '~') { switch ( *accel) { case '~' : accel++; break; case 0: return 0; default: return isalnum( *accel) ? *accel : tolower( *accel); } } } return 0; } int AbstractMenu_translate_key( Handle self, int code, int key, int mod) { mod &= kmAlt | kmShift | kmCtrl; key = ( key != kbNoKey ? key : code) | mod; keyRealize( key); return key; } int AbstractMenu_translate_shortcut( Handle self, char * key) { return key_normalize( key); } static Bool up_match ( Handle self, PMenuItemReg m, void * params) { return m-> down == params; } static Bool prev_match ( Handle self, PMenuItemReg m, void * params) { return m-> next == params; } void AbstractMenu_remove( Handle self, char * varName) { PMenuItemReg up, prev, m; if ( var-> stage > csFrozen) return; m = find_menuitem( self, varName, true); if ( m == nil) return; if ( var-> stage <= csNormal && var-> system) apc_menu_item_delete( self, m); up = ( PMenuItemReg) my-> first_that( self, (void*)up_match, m, true); prev = ( PMenuItemReg) my-> first_that( self, (void*)prev_match, m, true); if ( up) up -> down = m-> next; if ( prev) prev-> next = m-> next; if ( m == var-> tree) var-> tree = m-> next; m-> next = nil; my-> dispose_menu( self, m); } void AbstractMenu_insert( Handle self, SV * menuItems, char * rootName, int index) { int level; PMenuItemReg *up, m, addFirst, addLast, branch; if ( var-> stage > csFrozen) return; if ( SvTYPE( menuItems) == SVt_NULL) return; if ( strlen( rootName) == 0) { if ( var-> tree == nil) { var-> tree = ( PMenuItemReg) my-> new_menu( self, menuItems, 0); if ( var-> stage <= csNormal && var-> system) apc_menu_update( self, nil, var-> tree); return; } branch = m = var-> tree; up = &var-> tree; level = 0; } else { branch = m = find_menuitem( self, rootName, true); if ( m == nil) return; if ( m-> down) index = 0; up = &m-> down; m = m-> down; level = 1; } /* the level is 0 or 1 for the sake of rightAdjust */ addFirst = ( PMenuItemReg) my-> new_menu( self, menuItems, level); if ( !addFirst) return; /* error in menuItems */ addLast = addFirst; while ( addLast-> next) addLast = addLast-> next; if ( index == 0) { addLast-> next = *up; *up = addFirst; } else { int i = 1; while ( m-> next) { if ( i++ == index) break; m = m-> next; } addLast-> next = m-> next; m-> next = addFirst; } if ( m && m-> flags. rightAdjust) { while ( addFirst != addLast-> next) { addFirst-> flags. rightAdjust = true; addFirst = addFirst-> next; } } if ( var-> stage <= csNormal && var-> system) apc_menu_update( self, branch, branch); } #ifdef __cplusplus } #endif