| Padre documentation | Contained in the Padre distribution. |
Padre::Wx::Dialog::PluginManager - Plug-in manager dialog for Padre
Padre will have a lot of plug-ins. First plug-in manager was not taking this into account, and the first plug-in manager window was too small & too crowded to show them all properly.
This revamped plug-in manager is now using a list control, and thus can show lots of plug-ins in an effective manner.
Upon selection, the right pane will be updated with the plug-in name & plug-in documentation. Two buttons will allow to de/activate the plug-in (or see plug-in error message) and set plug-in preferences.
Double-clicking on a plug-in in the list will de/activate it.
Create and return a new Wx dialog listing all the plug-ins. It needs a
$parent window and a Padre::PluginManager object that really
handles Padre plug-ins under the hood.
Request the plug-in manager dialog to be shown. It will be refreshed first with a current list of plug-ins with their state.
Copyright 2008-2011 The Padre development team as listed in Padre.pm.
This program is free software; you can redistribute it and/or modify it under the same terms as Perl 5 itself.
| Padre documentation | Contained in the Padre distribution. |
package Padre::Wx::Dialog::PluginManager; # The Plug-in Manager GUI for Padre use 5.008; use strict; use warnings; use Carp (); use Padre::Wx (); use Padre::Wx::Icon (); use Padre::Wx::Role::Main (); our $VERSION = '0.86'; our @ISA = qw{ Padre::Wx::Role::Main Wx::Dialog }; ###################################################################### # Constructor sub new { my $class = shift; my $main = shift; my $manager = shift; # Create the basic object my $self = $class->SUPER::new( $main, -1, Wx::gettext('Plug-in Manager'), Wx::wxDefaultPosition, Wx::wxDefaultSize, Wx::wxDEFAULT_FRAME_STYLE, ); # Set some internal parameters $self->{sortcolumn} = 0; $self->{sortreverse} = 0; # Set basic dialog properties $self->SetIcon(Padre::Wx::Icon::PADRE); $self->SetMinSize( [ 750, 550 ] ); # Store plug-in manager $self->{manager} = $manager; unless ( $manager->isa('Padre::PluginManager') ) { Carp::croak("Missing or invalid Padre::PluginManager object"); } # Dialog Controls # Create the plug-in list $self->{list} = Wx::ListView->new( $self, -1, Wx::wxDefaultPosition, Wx::wxDefaultSize, Wx::wxLC_REPORT | Wx::wxLC_SINGLE_SEL, ); $self->{list}->InsertColumn( 0, Wx::gettext('Name') ); $self->{list}->InsertColumn( 1, Wx::gettext('Version') ); $self->{list}->InsertColumn( 2, Wx::gettext('Status') ); Wx::Event::EVT_LIST_ITEM_SELECTED( $self, $self->{list}, sub { shift->list_item_selected(@_); }, ); Wx::Event::EVT_LIST_ITEM_ACTIVATED( $self, $self->{list}, sub { shift->button_main; }, ); Wx::Event::EVT_LIST_COL_CLICK( $self, $self->{list}, sub { shift->list_col_click(@_); }, ); # Image List $self->{imagelist} = Wx::ImageList->new( 16, 16 ); $self->{list}->AssignImageList( $self->{imagelist}, Wx::wxIMAGE_LIST_SMALL, ); # Plug-in Name Header $self->{label} = Wx::StaticText->new( $self, -1, Wx::gettext('Plug-in Name'), ); my $font = $self->{label}->GetFont; $font->SetWeight(Wx::wxFONTWEIGHT_BOLD); $font->SetPointSize( $font->GetPointSize + 4 ); $self->{label}->SetFont($font); # Plug-in Documentation HTML Window require Padre::Wx::HtmlWindow; $self->{whtml} = Wx::HtmlWindow->new($self); # Enable/Disable Button $self->{button_main} = Wx::Button->new( $self, Wx::wxID_OK, Wx::gettext('&Enable'), ); Wx::Event::EVT_BUTTON( $self, $self->{button_main}, sub { $_[0]->button_main; }, ); # Preferences Button $self->{button_preferences} = Wx::Button->new( $self, -1, Wx::gettext('&Preferences'), ); Wx::Event::EVT_BUTTON( $self, $self->{button_preferences}, sub { $_[0]->button_preferences; }, ); # Close Button $self->{button_close} = Wx::Button->new( $self, Wx::wxID_CANCEL, Wx::gettext('&Close'), ); Wx::Event::EVT_BUTTON( $self, $self->{button_close}, sub { $_[0]->button_close; }, ); # Dialog Layout # Horizontal button sizer my $buttons = Wx::BoxSizer->new(Wx::wxHORIZONTAL); $buttons->AddStretchSpacer; $buttons->Add( $self->{button_main}, 0, Wx::wxALL, 1 ); $buttons->Add( $self->{button_preferences}, 0, Wx::wxALL, 1 ); $buttons->AddStretchSpacer; $buttons->Add( $self->{button_close}, 0, Wx::wxALL, 1 ); $buttons->AddStretchSpacer; # Horizontal plug-in name positioning my $header = Wx::BoxSizer->new(Wx::wxHORIZONTAL); $header->AddStretchSpacer; $header->Add( $self->{label}, 0, Wx::wxEXPAND | Wx::wxALIGN_CENTER, 1 ); $header->AddStretchSpacer; # Vertical layout of the right hand side my $right = Wx::BoxSizer->new(Wx::wxVERTICAL); $right->Add( $header, 0, Wx::wxALL | Wx::wxEXPAND, 1 ); $right->Add( $self->{whtml}, 1, Wx::wxALL | Wx::wxALIGN_TOP | Wx::wxALIGN_CENTER_HORIZONTAL | Wx::wxEXPAND, 1 ); $right->Add( $buttons, 0, Wx::wxALL | Wx::wxEXPAND, 1 ); # Main sizer my $sizer = Wx::BoxSizer->new(Wx::wxHORIZONTAL); $sizer->Add( $self->{list}, 0, Wx::wxALL | Wx::wxEXPAND, 1 ); $sizer->Add( $right, 1, Wx::wxALL | Wx::wxEXPAND, 1 ); # Tune the size and position it appears $self->SetSizer($sizer); $self->Fit; $self->CentreOnParent; $self->{list}->SetFocus; return $self; } # -- public methods sub show { my $self = shift; $self->_update_list; # select first item in the list. we don't need to test if # there's at least a plug-in, since there will always be # 'my plug-in' my $item = $self->{list}->GetItem(0); $item->SetState(Wx::wxLIST_STATE_SELECTED); $self->{list}->SetItem($item); $self->Show; } # GUI Handlers # # $self->list_item_selected( $event ); # # handler called when a list item has been selected. it will in turn update # the right part of the frame. # # $event is a Wx::ListEvent. # sub list_item_selected { my $self = shift; my $event = shift; my $fullname = $event->GetLabel; my $module = $self->{plugin_class}->{$fullname}; my $plugin = $self->{manager}->plugins->{$module}; $self->{plugin} = $plugin; # storing selected plug-in $self->{row} = $event->GetIndex; # storing selected row # Updating plug-in name in right pane $self->{label}->SetLabel( $plugin->plugin_name ); # Update plug-in documentation require Padre::Browser; my $browser = Padre::Browser->new; my $class = $plugin->class; my $doc = $browser->resolve($class); my $output = eval { $browser->browse($doc) }; my $html = $@ ? sprintf( Wx::gettext("Error loading pod for class '%s': %s"), $class, $@ ) : $output->body; $self->{whtml}->SetPage($html); # Update buttons $self->_update_plugin_state; # force window to recompute layout. indeed, changes are that plug-in # name has a different length, and thus should be recentered. $self->Layout; } # # $self->list_col_click; # # handler called when a column has been clicked, to reorder the list. # sub list_col_click { my $self = shift; my $event = shift; my $column = $event->GetColumn; my $prevcol = $self->{sortcolumn}; my $reversed = $self->{sortreverse}; $reversed = $column == $prevcol ? !$reversed : 0; $self->{sortcolumn} = $column; $self->{sortreverse} = $reversed; $self->_update_list; } # # $self->button_main; # # handler called when the first button has been clicked. # sub button_main { my $self = shift; # find method to call my $method = $self->{action}; # call method $self->$method(); } # # $self->button_preferences; # # handler called when the preferences button has been clicked. # sub button_preferences { my $self = shift; eval { $self->{plugin}->object->plugin_preferences; }; if ($@) { $self->{plugin}->errstr($@); $self->show_error_message; } } # # $self->button_close; # # handler called when the close button has been clicked. # sub button_close { $_[0]->Destroy; } # # $self->_plugin_disable; # # Disable plug-in, and update GUI. # sub _plugin_disable { my $self = shift; my $lock = $self->main->lock( 'UPDATE', 'DB', 'refresh_menu_plugins' ); my $plugin = $self->{plugin}->class; # disable plug-in Padre::DB::Plugin->update_enabled( $plugin => 0 ); $self->{manager}->plugin_disable($plugin); # Update plug-in manager dialog to reflect new state $self->_update_plugin_state; } # # $self->_plugin_enable; # # Enable plug-in, and update GUI. # sub _plugin_enable { my $self = shift; my $lock = $self->main->lock( 'UPDATE', 'DB', 'refresh_menu_plugins' ); my $plugin = $self->{plugin}->class; # Enable plug-in Padre::DB::Plugin->update_enabled( $plugin => 1 ); $self->{manager}->plugin_enable($plugin); # Update plug-in manager dialog to reflect new state $self->_update_plugin_state; } # # $self->show_error_message; # # show plug-in error message, in an error dialog box. # sub show_error_message { my $self = shift; my $message = $self->{plugin}->errstr; my $title = Wx::gettext('Error'); # @INC gets printed out between () remove that for now $message =~ s/\(\@INC.*\)//; Wx::MessageBox( $message, $title, Wx::wxOK | Wx::wxCENTER, $self ); } # # $dialog->_update_list; # # refresh list of plug-ins and their associated state. list is sorted # according to current sort criterion. # sub _update_list { my $self = shift; # Clear image list & fill it again $self->{imagelist}->RemoveAll; # Default plug-in icon $self->{imagelist}->Add( Padre::Wx::Icon::find('status/padre-plugin') ); my %icon = ( plugin => 0 ); # Plug-in status my $i = 0; foreach my $name ( qw{ enabled disabled error crashed incompatible } ) { $self->{imagelist}->Add( Padre::Wx::Icon::find("status/padre-plugin-$name") ); $icon{$name} = ++$i; } # Get list of plug-ins, and sort it. Note that $self->{manager}->plugins # names is sorted (with my plug-in first), and that perl sort is now # stable: sorting on another criterion will keep the alphabetical order # if new criterion is not enough. my $plugins = $self->{manager}->plugins; my @plugins = map { $plugins->{$_} } $self->{manager}->plugin_order; if ( $self->{sortcolumn} == 1 ) { # no warnings; # We see ??? in the version field for modules that don't have a version number or were not loaded @plugins = map { $_->[0] } sort { $a->[1] <=> $b->[1] } map { [ $_, version->new( ( $_->version && $_->version ne '???' ) || 0 ) ] } @plugins; } if ( $self->{sortcolumn} == 2 ) { @plugins = sort { $a->status cmp $b->status } @plugins; } if ( $self->{sortreverse} ) { @plugins = reverse @plugins; } # Clear plug-in list & fill it again my $idx = -1; my %plugin_class = (); $self->{list}->DeleteAllItems; foreach my $plugin (@plugins) { $plugin_class{ $plugin->plugin_name } = $plugin->class; # Check if plug-in is supplying its own icon my $position = 0; my $icon = $plugin->plugin_icon; if ( defined $icon ) { $self->{imagelist}->Add($icon); $position = $self->{imagelist}->GetImageCount - 1; } # Inserting the plug-in in the list $self->{list}->InsertStringImageItem( ++$idx, $plugin->plugin_name, $position, ); $self->{list}->SetItem( $idx, 1, $plugin->version || '???' ); $self->{list}->SetItem( $idx, 2, $plugin->status_localized, $icon{ $plugin->status }, ); } # Store mapping of full plug-in names / short plug-in names $self->{plugin_class} = \%plugin_class; # Auto-resize columns foreach ( 0 .. 2 ) { $self->{list}->SetColumnWidth( $_, Wx::wxLIST_AUTOSIZE ); } # Making sure the list can show all columns my $width = 15; # Taking vertical scrollbar into account foreach ( 0 .. 2 ) { $width += $self->{list}->GetColumnWidth($_); } $self->{list}->SetMinSize( [ $width, -1 ] ); return; } # # $dialog->_update_plugin_state; # # Update button caption & state, as well as status icon in the list, # depending on the new plug-in state. # sub _update_plugin_state { my $self = shift; my $plugin = $self->{plugin}; # my $list = $self->{list}; # my $item = $list->GetItem( $self->{row}, 2 ); # Updating buttons my $button_main = $self->{button_main}; my $button_preferences = $self->{button_preferences}; if ( $plugin->error ) { # Plug-in is in error state $self->{action} = 'show_error_message'; $button_main->SetLabel( Wx::gettext('&Show error message') ); $button_preferences->Disable; $self->{list}->SetItem( $self->{row}, 2, Wx::gettext('error'), 3 ); # $item->SetText( Wx::gettext('error') ); # $item->SetImage(3); # $list->SetItem($item); } elsif ( $plugin->incompatible ) { # Plugin is incompatible $self->{action} = 'show_error_message'; $button_main->SetLabel( Wx::gettext('&Show error message') ); $button_preferences->Disable; $self->{list}->SetItem( $self->{row}, 2, Wx::gettext('incompatible'), 5 ); # $item->SetText( Wx::gettext('incompatible') ); # $item->SetImage(5); # $list->SetItem($item); } else { # Plug-in is working... if ( $plugin->enabled ) { # ...and enabled $self->{action} = '_plugin_disable'; $button_main->SetLabel( Wx::gettext('&Disable') ); $button_main->Enable; $self->{list}->SetItem( $self->{row}, 2, Wx::gettext('enabled'), 1 ); # $item->SetText( Wx::gettext('enabled') ); # $item->SetImage(1); # $list->SetItem($item); } elsif ( $plugin->can_enable ) { # ...and disabled $self->{action} = '_plugin_enable'; $button_main->SetLabel( Wx::gettext('&Enable') ); $button_main->Enable; $self->{list}->SetItem( $self->{row}, 2, Wx::gettext('disabled'), 2 ); # $item->SetText( Wx::gettext('disabled') ); # $item->SetImage(2); # $list->SetItem($item); } else { # ...disabled but cannot be enabled $button_main->Disable; } # Updating preferences button if ( $plugin->object->can('plugin_preferences') ) { $button_preferences->Enable; } else { $button_preferences->Disable; } } # Update the list item # $self->_update_list; # Force window to recompute layout. indeed, changes are that plug-in # name has a different length, and thus should be recentered. $self->Layout; } 1; __END__
# Copyright 2008-2011 The Padre development team as listed in Padre.pm. # LICENSE # This program is free software; you can redistribute it and/or # modify it under the same terms as Perl 5 itself.