package CORBA::MICO::CMenu; use vars qw($serial); use Carp; use vars qw($DEBUG); #$DEBUG=1; #-------------------------------------------------------------------- # Dynamic menu. Supports a single menu for several # objects. Each time an object becomes active # CMenu automatically rebuilds menu so as it becomes appropriate # for active object. #-------------------------------------------------------------------- use Gtk2 '1.140'; #-------------------------------------------------------------------- # Create new menu # In: $topwindow - toplevel widget #-------------------------------------------------------------------- sub new { my ($type, $topwindow) = @_; my $class = ref($type) || $type; my $accel_group = new Gtk2::AccelGroup; my $menu_name = "<menu_$serial>"; ++$serial; my $item_factory = new Gtk2::ItemFactory('Gtk2::MenuBar', $menu_name, $accel_group); #$accel_group->attach($topwindow); $topwindow->add_accel_group($accel_group); my $main_widget = $item_factory->get_widget($menu_name); my $self = { 'FACTORY' => $item_factory, 'NAME' => $menu_name, 'ACCEL_GROUP' => $accel_group, 'LAST_ACTION' => 0, 'CURR_ID' => '', 'WIDGET' => $main_widget, 'ITEMS' => {} }; bless $self, $class; $self->{'SELFPTR'} = \$self; $main_widget->signal_connect('destroy', sub { $self->close(); 1; }); return $self; } #-------------------------------------------------------------------- # widget - return main menu widget #-------------------------------------------------------------------- sub widget { my $self = shift; return $self->{'WIDGET'}; } #-------------------------------------------------------------------- # Add a menu item # In: $id - ID of object, empty string if item is going to be a global one # $path - name of item (as in GtkItemFactoryEntry) # $hotkey - hotkey (as accelerator in GtkItemFactoryEntry) # $callback - callback # $cb_data - callback data #-------------------------------------------------------------------- sub add_item { my($self, $id, $path, $hotkey, $callback, $cb_data) = @_; prepare_item($self, $id, $path, $hotkey, 'Item', $callback, $cb_data); } #-------------------------------------------------------------------- # Create menu item if it doesn't exist yet # Return control structure for it # In: $id - ID of object, empty string if item is going to be a global one # $path - name of item (as in GtkItemFactoryEntry) # $hotkey - hotkey (as accelerator in GtkItemFactoryEntry) # $type - item type: Item/Branch/LastBranch # $callback - callback # $cb_data - callback data #-------------------------------------------------------------------- sub prepare_item { my($self, $id, $path, $hotkey, $type, $callback, $cb_data) = @_; my $items = $self->{'ITEMS'}; my $curr_item; if( not exists($items->{$path}) ) { $curr_item = { 'TYPE' => $type }; $items->{$path} = $curr_item; my $action = ($type eq 'Item') ? ++$self->{'LAST_ACTION'} : 0; $curr_item->{'ACTION'} = $action; if( $path =~ m#(^/.*)/[^/]*$# ) { # dirname $self->prepare_item('', $1, undef, 'Branch', undef, undef); } my $selfptr = $self->{'SELFPTR'}; $type = 'LastBranch' if $path =~ m#^/_?Help$#i; my $cdata = [$path, $hotkey, undef, $action, "<$type>"]; if( $type =~ /Item/ ) { $cdata->[2] = sub { item_activated_cb($selfptr, $curr_item, @_) }; } $self->{'FACTORY'}->create_item($cdata); } else { $curr_item = $items->{$path}; if( $curr_item->{'TYPE'} ne $type ) { carp "$self->{MENU_NAME}:$path already defined as a $type"; return undef; } } if( $id and exists($curr_item->{$id}) ) { carp "ID $id already defined in menu item $self->{MENU_NAME}:$path"; return; } $curr_item->{$id} = [ $callback, $cb_data ]; return $items; } #-------------------------------------------------------------------- # Make unsensitive all menu items except ones having given ID #-------------------------------------------------------------------- sub activate_id { my ($self, $id) = @_; my $item_factory = $self->{'FACTORY'}; my ($key, $items); while( ($key, $item) = each %{$self->{'ITEMS'}} ) { my $sflag = ($item->{'TYPE'} eq 'Item' and not exists $item->{$id})? 0: 1; $self->mask_item($key, $sflag); } $self->{'CURR_ID'} = $id; } #-------------------------------------------------------------------- # make menu item sensitive/unsensitive # In: $name - item name # $flag - TRUE - sensitive, FALSE-unsensitive #-------------------------------------------------------------------- sub mask_item { my ($self, $name, $flag) = @_; my $item_factory = $self->{'FACTORY'}; my $item = $self->{'ITEMS'}->{$name}; if( not defined($item) ) { # carp "No menu item $name"; return; } return if exists $item->{''}; # do not mask field if it is global my $widget = $item_factory->get_widget_by_action($item->{'ACTION'}); if( defined($widget) ) { $widget->set_sensitive($flag); } } #-------------------------------------------------------------------- # Menu callback: item activated #-------------------------------------------------------------------- sub item_activated_cb { my ($selfptr, @args) = @_; warn "item_activated_cb" if $DEBUG; if( defined($$selfptr) ) { $$selfptr->item_activated(@args); } } sub item_activated { my ($self, $item, $widget, $action) = @_; warn "item_activated($self)" if $DEBUG; $id = $self->{'CURR_ID'}; my $cb = $item->{$id}; if( defined($cb) ) { &{$cb->[0]}($cb->[1]); } elsif( $id ne '' ) { # global item: quit, help, etc $cb = $item->{''}; &{$cb->[0]}($cb->[1]) if defined($cb); } } #-------------------------------------------------------------------- sub close { my $self = shift; my $selfptr = $self->{'SELFPTR'}; undef $$selfptr if defined $$selfptr; my $items = $self->{'ITEMS'}; if( defined($items) ) { foreach my $k (keys %$items) { $items->{$k} = undef; } } foreach my $k (keys %$self) { $self->{$k} = undef; } } #-------------------------------------------------------------------- sub DESTROY { my $self = shift; warn "DESTROYING $self" if $DEBUG; } $serial = 1; 1;