[[PageOutline(1-4)]] [wiki:PadrePluginDialog <-menu] = Icons, DEBUG and Translations or some sort of Conformance and all the bit's I have missed so far :) This page compleats {{{Padre::Plugin::Cookbook}}} series, It is just meant to be an aid, with a suggested layout and some ideas to speed you along your way, enjoy. * Unlike the previous recipes which have covered, Plug-ins with dialogs, this is more concerned, with conformance, playing nice with Padre. * The following is as a result of analysing Plug-ins for this page. * The examples here, where relevant will build upon Cookbook, some relate to other Plug-ins as it is more pertinent to use them, * Updated to Padre plug-in API 2.2 ---- == Deceleration Initial thoughts, of what a {{{Padre::Plugin}}} should provide, critiques welcome. Things to do to enable Plugin Manager to load your Plug-in === Plugin must have a Version * For compliance you will need to have a version in your Padre::Plugin::Name.pm. {{{#!perl our $VERSION = '0.01'; }}} ---- == Methods === plugin_name * Plugin must have a Name Don't forget to include the following sub. 1. ok I must be stupid but with out the **return** the POD just confused me, it's to terse. 1. {{{Wx::gettext(...)}}} this is use to wrap all text to be displayed in a Wx-object, more to follow relating to translations. 1. If you chose to use //menu_plugins// , you should consider the use of an ellipsis. {{{#!perl ####### # Define Plugin Name required ####### sub plugin_name { return Wx::gettext('Plugin Cookbook'); } }}} ---- === //plugin_menu// You have a Choice, you can have a menu and sub menus, or just an item on the tools. ==== plugin_menu_simple {{{#!perl sub menu_plugins_simple { my $self = shift; return $self->plugin_name => [ 'About' => sub { $self->about_dialog }, # 'Another Menu Entry' => sub { $self->other_method }, # 'A Sub-Menu...' => [ # 'Sub-Menu Entry' => sub { $self->yet_another_method }, # ], ]; } }}} From My Plugin, covered in screen cast. ==== menu_plugins If you chose this option, you should consider the use of an ellipsis in //plugin_name//. {{{#!perl sub menu_plugins { my $self = shift; my $main = $self->main; # Create a manual menu item my $item = Wx::MenuItem->new( undef, -1, $self->plugin_name, ); Wx::Event::EVT_MENU( $main, $item, sub { local $@; eval { $self->load_dialog_main($main); }; }, ); return $item; } }}} see {{{Padre::Plugin::FormBuilder}}} ---- === padre_interfaces * Plugin must declare it's **Interfaces**, all Padre Modules should be included, just look at some other Plug-ins to see the pattern. * Don't forget to include the following sub. * Use the version number of Padre you are developing against, as shown below. {{{#!perl ####### # Define Padre Interfaces required ####### sub padre_interfaces { return ( # Default, required 'Padre::Plugin' => '0.96', # used by Main, About and by Padre::Plugin::FormBuilder 'Padre::Wx' => '0.96', 'Padre::Wx::Main' => '0.96', 'Padre::Wx::Role::Main' => '0.96', 'Padre::DB' => '0.96', 'Padre::Logger' => '0.96', ); } }}} * draft 2 needs more work, in tiding up {{{#!text bowtie Padre::Plugin POD, padre_interfaces, lists Padre modules and version numbers. 1, is it upto date 2, it appairs to me you only need, 'Padre::Plugin' => 0.nn, Alias You need to declare everything you use So if you use Padre::Logger, you need to declare that If you do $plugin->main->some_method you need to do Padre::Wx::Main etc You declare the parts of Padre that you use So when we break compatibility in one part of Padre, ALL the plugins that use it aren't loaded, but ONLY the plugins that use it are disabled bowtie but plugins still work and don't through any errors or warnings at present Alias You THINK they work They MIGHT work But you can't know that This is about failure modes We have a choice about how we want plugins to fail 1, Always have every plugin load, wait for each one to do something illegal in Padre and blow up, leaving it half-loaded or in some unknown state and maybe crashing Padre 2. Disable some plugins due to "compatibility" which really will still work, just in case one was using the specific part you changed In the first case, we have unknown and uncontrolled impact, and random side effects, any of which are potentially disastrous In the second case, plugin authors need to check they still work, bump the plugin's dependency hash past the change, and do a simple incremental release The second is much more responsible, and much safer when there's no firewall between the plugins and the core Firefox doesn't even give THAT much leeway Every plugin is automatically disabled, until they SPECIFICALLY test they still work We are at least doing piece-meal disable on a per class basis Which is more cpan friendly bowtie ok, so the the the modules version will be that of the Padre version it was created against, hence all module version numbers should be the same? Alias Usually yes But someone might have checked that one specific class hasn't changed in the place we care about further back Or one plugin might depend on another plugin And so on bowtie I will add this to cookbook wiki then, ok Alias But USUALLY they will all be the same bowtie the POD shows diffrent versions Alias Well, it IS theoretically possible bowtie so hidden some whare in your brain is an padre-plugin api version v padre version numbers then :) Alias api version is the same Padre::Plugin is just another class that the plugin "depends on" bowtie from POD, Padre::Plugin - Padre plug-in API 2.2 Alias oh, that That's mostly just a way of mentally marking time It's not really any kind of official thing bowtie yes it's confuses me Alias Basically it just says "We've completely rewritten it twice, and made some big additions twice since then" It's for humans, not machines A bit like Padre::Task 2.0 bowtie on reflection I got that, but I don't know what the padre version was then, and if I use that version will a plugin be back compatable Alias You don't have to You just say the most recent version of Padre you are SURE that the plugin works with And Padre itself tracks compatibility for you bowtie ie developed against :) Alias our $COMPATIBLE = '0.43'; That's in Padre::Plugin So at a guess, the last time I broke compatibility for ALL plugins was likely 2.0 landing bowtie POD shows 0.29 Alias Which means 2.0 probably arrived around about 0.43 bowtie can I do perl dev -t Class::Name, Class::Name2 Alias Nope bowtie only one :( all modules used by a Plugin that are not part of Padre core should be in sub plugin_disable even if required Alias All plugin modules should be Don't unload third party modules You have no idea if anything else needs them bowtie you mean Mouse for example Alias right Only unload the plugin's own modules bowtie ok if a Plugin over loads sub plugin_icon Alias You can overload pretty much anything you like bowtie you are so happy it must be your un-Birthday again :), thanks }}} ---- === plugin_enable * Some Plug-ins will require a plugin_enable method, * Why because it's using an external resource, which is not being picked up via perl (M::I). * The use of File::Which::which is OS independent, and already in Padre stack. Alias++ {{{#!perl ######### # We need plugin_enable # as we have an external dependency ######### sub plugin_enable { my $pdflatex_exists = 0; # Tests for external file in Path if ( File::Which::which('pdflatex') ) { $pdflatex_exists = 1; } return $pdflatex_exists; } }}} From {{{Padre::Plugin::LaTeX}}} {{{Padre::Plugin::SpellCheck}}} dose not need this as it depends upon Text::Aspell which won't install with out Aspell files being present. ---- === plugin_disable * Plugin_disable is a must so that we can load and unloaded our Plug-in repeatedly, using Tools -> Reload All Plug-ins. Don't forget to include all the relevant sections. 1. Wx::Dialogs, as shown 1. your plugin modules, as shown, 1. other cpan modules that are pertinent to only your plugin, such as sockets or demons, not shown. 1. boot n braces, as shown, this is a new development, run SUPER plugin_disable, an Ode to before :) {{{#!perl # Child modules we need to unload when disabled use constant CHILDREN => qw{ Padre::Plugin::Cookbook::Recipe01::Main Padre::Plugin::Cookbook::Recipe01::FBP::MainFB Padre::Plugin::Cookbook::Recipe02::Main Padre::Plugin::Cookbook::Recipe02::FBP::MainFB Padre::Plugin::Cookbook::Recipe03::Main Padre::Plugin::Cookbook::Recipe03::FBP::MainFB Padre::Plugin::Cookbook::Recipe03::About Padre::Plugin::Cookbook::Recipe03::FBP::AboutFB Padre::Plugin::Cookbook::Recipe04::Main Padre::Plugin::Cookbook::Recipe04::FBP::MainFB Padre::Plugin::Cookbook::Recipe04::About Padre::Plugin::Cookbook::Recipe04::FBP::AboutFB }; sub plugin_disable { my $self = shift; # Close the dialog if it is hanging around $self->clean_dialog; # Unload all our child classes for my $package (CHILDREN) { require Padre::Unload; Padre::Unload->unload($package); } $self->SUPER::plugin_disable(@_); return 1; } }}} Why a composed Method you ask, in Cookbook it just makes sense, if you have more than one dialog and we have more than two. {{{#!perl ######## # Composed Method clean_dialog ######## sub clean_dialog { my $self = shift; # Close the main dialog if it is hanging around if ( $self->{dialog} ) { $self->{dialog}->Hide; $self->{dialog}->Destroy; delete $self->{dialog}; } return 1; } }}} ---- === plugin_icon 1. from Padra you could use the blue morpho {{{#!perl sub plugin_icon { require Padre::Wx::Icon; Padre::Wx::Icon::find('logo'); } }}} 1. To define your your own icon. {{{#!perl ####### # Add icon to Plugin ####### sub plugin_icon { my $class = shift; my $share = $class->plugin_directory_share or return; my $file = File::Spec->catfile( $share, 'icons', '16x16', 'cookbook.png' ); return unless -f $file; return unless -r $file; return Wx::Bitmap->new( $file, Wx::wxBITMAP_TYPE_PNG ); } }}} {{{#!text ├── lib │   └── Padre │   └── Plugin ├── share │   └── icons │   └── 16x16 │   └── cookbook.png └── t }}} ---- === plugin_about Example: [[Image(git_about.png)]] and code {{{#!perl ####### # plugin_about ####### sub plugin_about { my $self = shift; my $share = $self->plugin_directory_share or return; my $file = File::Spec->catfile( $share, 'icons', '48x48', 'git.png' ); return unless -f $file; return unless -r $file; my $info = Wx::AboutDialogInfo->new; $info->SetIcon( Wx::Icon->new( $file, Wx::wxBITMAP_TYPE_PNG ) ); $info->SetName('Padre::Plugin::Git'); $info->SetVersion($VERSION); $info->SetDescription( Wx::gettext('A Simple Git interface for Padre') ); $info->SetCopyright('(c) 2008-2012 The Padre development team'); $info->SetWebSite('http://padre.perlide.org/trac/wiki/PadrePluginGit'); $info->AddDeveloper('Kaare Rasmussen, '); $info->AddDeveloper('Kevin Dawson '); $info->SetArtists( [ 'Scott Chacon ', 'Licence ' ] ); Wx::AboutBox($info); return; } }}} Also see the following from wxperl_demo.pl both work in a Plugin, chose and edit to taste. {{{#!perl sub simple_about_dialog { my $self = shift; my $info = Wx::AboutDialogInfo->new; $info->SetName( 'The wxPerl demo' ); $info->SetVersion( $VERSION ); $info->SetDescription( 'The cool and pluggable wxPerl demo' ); $info->SetCopyright( '(c) 2001-today Me ' ); Wx::AboutBox( $info ); } }}} {{{#!perl sub complex_about_dialog { my $self = shift; my $info = Wx::AboutDialogInfo->new; $info->SetName( 'The wxPerl demo' ); $info->SetVersion( $VERSION ); $info->SetDescription( 'The cool and pluggable wxPerl demo' ); $info->SetCopyright( '(c) 2001-today Me ' ); $info->SetWebSite( 'http://wxperl.eu/', 'The wxPerl demo web site' ); $info->AddDeveloper( 'Mattia Barbon ' ); $info->AddDeveloper( 'I wish there was somebody else...' ); $info->SetArtists( [ 'Unluckily', 'none', 'so', 'the', 'graphic', 'is', 'bad' ] ); Wx::AboutBox( $info ); } }}} ---- === plugin_preferences from {{{Padre::Plugin::SpellCheck}}} **{{{ToDo}}} add text** {{{#!perl ####### # plugin_preferences ####### sub plugin_preferences { my $self = shift; my $main = $self->main; # Clean up any previous existing dialog $self->clean_dialog; try { require Padre::Plugin::SpellCheck::Preferences; $self->{dialog} = Padre::Plugin::SpellCheck::Preferences->new($main); $self->{dialog}->ShowModal; } catch { $self->main->error( sprintf Wx::gettext('Error: %s'), $_ ); }; return; } }}} ---- === Context Menu from {{{Padre::Plugin::Nopaste}}} {{{#!perl ####### # Add Preferences to Context Menu ####### sub event_on_context_menu { my ( $self, $document, $editor, $menu, $event ) = @_; #Test for valid file type return if not $document->filename; $menu->AppendSeparator; my $item = $menu->Append( -1, Wx::gettext('Nopaste Preferences...') ); Wx::Event::EVT_MENU( $self->main, $item, sub { $self->plugin_preferences }, ); return; } }}} ---- === registered_documents * only if you have them {{{#!perl sub registered_documents { 'application/x-latex' => 'Padre::Document::LaTeX', 'application/x-bibtex' => 'Padre::Document::BibTeX',; } }}} From {{{Padre::Plugin::LaTeX}}} NB don't forget to check [http://padre.perlide.org/trac/browser/trunk/Padre/lib/Padre/MimeTypes.pm Padre/MimeTypes.pm] {{{%EXT_MIME}}} section already contains your type. Vivtek+ ---- == POD I have found {{{Catalyst::Controller::POD}}} to be the best way to view POD for me, as it correctly formats, highlights and colourise the syntax. it may be overkill but it's pretty. * Licence * Acknowledge others * Fail: Padre POD viewer displays an error * Poor: Fail < Poor < Min * Min, contains: NAME, AUTHOR, LICENCE * acceptable: Min < acceptable < Good * Good, contains: VERSION, BUGS AND LIMITATIONS, DEPENDENCIES. Passes xt/pod.t and xt/podcoverage.t for all Plugin files. ---- == DEBUG This is cool * now I can keep my say(print) statements, but turn then on when I want, with out littering the terminal :) Make sure you have added {{{Padre::Logger}}} to {{{padre_interfaces}}} and loaded the module in your package. * Read POD for [http://search.cpan.org/~plaven/Padre/lib/Padre/Logger.pm Padre::Logger] {{{#!perl TRACE( "found missing file in history " . $events[$_][2] ) if DEBUG; }}} To view the output in terminal, try the following {{{#!sh perl dev -a -t Padre::Plugin::Swarm::Transport::Global::WxSocket }}} or {{{#!sh perl dev -a -t Padre::Plugin::Cookbook::Recipe04::Main }}} ---- == Translations * [http://padre.perlide.org/trac/wiki/TranslationIntro Intro To Translation] * Language Support [http://perlide.org/translations/ translations update] * English * etc.. ---- == OS Support, don't forget to consider, your Plug-in should ideally work on all of the following platforms. * Linux * 64 bit * i386/i686 * Microsoft * Vista * XP * Apple * Lion * Snow Leopard * Leopard ---- == Build * Plug-in builds so it can be install in local repository * We recommend using {{{Module::Include}}} at present, and suggest following in {{{Makefile.PL}}} * The use of {{{Module::Include}}} also enables the use of our plug-in via {{{perl dev -a}}} * Don't forget to include all the Perl modules, you require from CPAN * if install_share appears not to work, check your MANIFEST, or just re-name it to .old. **Tip** you can use to view required modules from CPAN you have used in a file. {{{#!perl use inc::Module::Install 1.01; # Define meta-data abstract 'Cookbook contains recipes to assist you in making your own Padre::Plugin'; # All from our plugin all_from 'lib/Padre/Plugin/Cookbook.pm'; # Recommended, Alias+ requires_from 'lib/Padre/Plugin/Cookbook/Recipe01/Main.pm'; requires_from 'lib/Padre/Plugin/Cookbook/Recipe02/Main.pm'; requires_from 'lib/Padre/Plugin/Cookbook/Recipe03/Main.pm'; requires_from 'lib/Padre/Plugin/Cookbook/Recipe04/Main.pm'; # Padre version, should match that of the latest version, used in padre_interfaces, requires 'Padre' => '0.91'; # required modules from CPAN requires 'Moose' => '2.00'; requires 'namespace::autoclean' => '0.12'; requires 'Data::Printer' => '0.19'; # Optional if you have used the share directory install_share; WriteAll; }}} * **NB** {{{Module::Install::API experimental}}} **requires_from** command takes a module file path, and looks for //use// statements with explicit module version (like {{{use Foo::Bar 0.01 }}}), and from which it sets //requires// attributes. ---- == CPAN this is probably our end goal. * public repository * about dialogue ---- == Additional documentation or Marketing your Plug-in and Padre * trac wiki * blogg ---- == Tickets Please Create New Tickets with the following additional information. * start summary with {{{Padre::Plugin::....}}} * set component = plugins ---- == Miscellaneous ==== ellipsis Label the menu item with a trailing ellipsis ("...") only if the command requires further input from the user before it can be performed. Do not add an ellipsis to items that only present a confirmation dialog (such as Delete), or that do not require further input (such as Properties, Preferences or About). dolman+ see sample from {{{Padre::Plugin::SpellCheck}}} ==== {{{FormBuilder fbp}}} We are now suggesting that all Plug-in Dialogues should be in a single file; as {{{Padre::Plugin::FormBuilder}}} now supports *.fbp with multiple dialogues enclosed. This is shown below: {{{#!text ├── Changes ├── lib ├── Makefile.PL ├── Padre-Plugin-ParserTool.fbp └── t }}} code sample from {{{Padre::Plugin::ParserTool}}} new current standard, thanks Alias :) **{{{__END__}}}** [wiki:PadrePluginDialog <-menu]