← Back to team overview

zim-wiki team mailing list archive

Re: Release planning for Zim 0.25

 

Roberto Suarez Soto wrote:
I've got two suggestions (both of them for 0.24; maybe any of these is already covered in 0.25, specially with the TODO stuff):

- Some documentation about how to make plugins. I know a bit of Perl, but digging into some of the already present plugins is a bit confusing. Some little guide about the basics would be a godsent.

Took some time in the air to type out some more details, see attached files, one with current state + example and one with plan for extension of plugin interface. If only laptop batteries lasted for the whole of a 12h flight I could have done a lot more :( anyway, back in european time zone now.

- I miss a "Link below this page" button (or shortcut, or whatever). I.e., a button like the "Link" one, but that links a page below the current one, not at the same level.

Might be a nice exercise to implement this one as a plugin :) Defined button and call Link on the pageview. I can see the use case for this request, but I don't know which keybinding to use for this.

Note that the link button works as a format toggle (same as bold and italic) which this feature could not do that easily.

Regards,

Jaap
====== Plugins ======

See [[:Usage:Plugins]] for information on the available plugins.
See [[TODO:PluginInterface]] for planned improvements.

Zim has a simple plugin system to extend the functionality. Plugins are just perl scripts that are installed in a directory where zim can find them. If the user chooses to use the plugin the script is loaded during program initialization. The script can add some code to zim directly or it can load a module with the functionality.

===== Locations =====
Plugin scripts should be installed in ''XDG_DATA_DIR/zim/plugins/'' this means that by default they will be located in ''/usr/share/zim/plugins'' or ''/usr/local/zim/plugins''. Users can install their own plugins in ''~/.local/share/zim/plugins''. ( For more information on this directory scheme have a look at the freedesktop [[http://freedesktop.org/wiki/Standards_2fbasedir_2dspec|basedir specification]]. )

If the plugin uses it's own perl module this module should be installed in the normal perl library path.

Any custom icons you need should be put in ''XDG_DATA_DIR/pixmaps/zim/'', they will be loaded automatically as stock icons. You can use the basename of the icon file as the stock name.

===== Object structure =====
The main purpose of plugins is to add new functionality in the user interface. Adding stuff to the data backend is done by adding new modules to the backend. Of course there are cases where a plugin can be used to load specific backend modules. However you should realize that since plugins are only loaded by the user interface the result will be that these backend modules then are not available when using for example the command line export function.

The picture below shows schematically where the plugins go in the stack of classes.

{{../layers.png}}

Plugins can extend the GUI classes or add new GUI components. For an overview of the GUI classes see [[UserInterface]], for an overview of the classes see [[Notebook]], and for the filesystem classes see [[FileSystem]]. For detailed documentation of each class look for the inline POD documentation. For example type "''perldoc Zim::GUI''" in a terminal to see the methods defined in the main GUI object.

===== Example =====
Below an example is given of a plugin defining a new menu item. See [[../ExamplePlugin.pl]] for the full template.

To access the application objects "''Zim::GUI->current()''" can be called from the plugin script. This will return the main application object which hold references to all other objects in use. A standard start of a plugin script would be:

	use strict;
	use Zim::Utils; # import util functions

	my $app = Zim::GUI->current;

Typically plugins start with defining a new menu item. This is done using by calling "''$app->add_actions()''" and "''$app->add_ui()''". These methods are defined in in the ''Zim::GUI::Component'' base class and are wrappers for the ''Gtk2::UIManager ''functionality. We wrap the action definition with the "''__actions()''" function in order to make them translatable. So we can add an action to the "Tools" menu using:

	my $actions = __actions( q{
	MyAction	gtk-open	My Action	.	Execute my action
	} );

	$app->add_ui( q{
	<ui>
		<menubar name='MenuBar'>
			<menu action='ToolsMenu'>
				<menuitem action='MyAction'/>
			</menu>
		</menubar>
	</ui> } );

Now the actual code for this action goes in a subroutine called "''MyAction()''". This method will be called when the menu item is clicked but also serves as public interface that might be called by other plugins.

===== Random remarks =====

**Error handling**
The plugin script should return ''true'' when successful. When the script does not return ''true'' or dies the user gets an error dialog and the plugin will not be loaded again next time zim starts. If your plugin has dependencies it is nice to check them and trow an error dialog explaining to the user what went wrong before returning ''false''. See for the TrayIcon plugin for an example.

**Autoloading a module**
For small plugin functions all the code goes directly in the plugin script. Larger plugins will be easier to maintain when the logic goes into a separate perl module and the plugin script is used to load this module. Zim autoloads modules from the ''Zim::GUI::'' namespace, so when the module defines for example a dialog that only needs to be loaded after the user clicks a menu item the script is used to add the menu items while it trusts the module with the dialog code to be loaded when needed. This is how the Calendar and TODOList plugins work. Typically modules in the ''Zim::GUI::'' namespace inherit from ''Zim::GUI::Component'' and define a single interface component, e.g. a dialog or a pane in the main interface.

**Adding a section to the config file**
Zim uses ini-style config files that can have multiple sections. So it makes sense for plugins that have config items to define a new section to store their config items. See [[ConfigFiles]] to determine which kind of config items go in which config file. There are two convenience routines to get a hash that maps to a section in one of these files: ''init_settings()'' and ''init_state()'' which are defined in ''Zim::GUI::Component''. These can be called on the ''$app'' object from the plugin script or can be used during initialization of a module. For example the EquationEditor plugin uses this to set config items for the latex and dvipng commands that are used.

**Use signals as hooks for new functionality**
The idea is that you can connect to signals to trigger new functionality. Basically triggering an action trough a menu or toolbar item also emits a signal, but here the signal is wrapped by default already and will call the like named module. The main application object and the main notebook object also emit signals for events like loading a new page - see the POD documentation. And of course all the Gtk widget have signals for various events. This is used by for example the Calendar plugin to know when a page is loaded for a date and update the dialog to show the corresponding month.
use strict;
use Zim::Utils;

# This file gives a template for a simple plugin
# that adds just one function to the Tools menu.

=head1 NAME

[name of this plugin]

=head1 DESCRIPTION

[Description of functionality provided by this plugin]

=head1 ABOUT

[Name of this plugin] - [Version number]

[Name and email of the author]

Copyright (c) [year(s)] [Name author(s)]. All rights reserved.
This program is free software; you can redistribute it and/or
modify it under the same terms as Perl itself.

=cut

# get main application object
my $app  = Zim::GUI->current;

# define action attributes (translatable !),
# separate by TAB, use "." for undefined
my $actions = __actions( 
# name		icon name	menu label	key binding	tooltip
q{
MyAction	gtk-open	My Action	.	Execute my action
} );

# add actions to interface
$app->add_actions($actions);

# add actions to the menu layout
# see source of Zim::GUI for complete menu structure
$app->add_ui( q{
<ui>
	<menubar name='MenuBar'>
		<menu action='ToolsMenu'>
			<menuitem action='MyAction'/>
		</menu>
	</menubar>
</ui> } );

return 1;

# callbacks for the actions go below, method name should be the same as
# the action name

=head1 ACTIONS

=over 4

=item C<MyAction()>

[Some description of what this method does]

=cut

sub MyAction {
	print "Some action triggered !\n";
}

=back

=cut

====== PluginInterface ======

**Summary:** document, use signals more extensively and use POD for info.

**short term:**
* Documentations (See e.g. http://live.gnome.org/Gedit/PythonPluginHowTo)
	* ~~document adding new actions~~
	* Document overloading existing actions (see below)
	* Document using standard methods from Utils.pm and FS.pm - any others ?
	* Document how to add new formatting modules
* Keep current structure with plugin scripts to allow various checks before loading module
	* use "singleton" signal for registering unplug method
	* idem for registering method to show preferences popup
	* base class for plugins could automatically connect these signals and map to methods
	* (maybe have way to register these signals without using array for optimization ?)
* Turn all action methods for all zim classes into signal handlers
	* autoload should check for any signal handlers and return after the first that returns TRUE
	* method in the class as default handler
	* this allows overloading actions without currying the method
	* (move AUTOLOAD from Component.pm to Events.pm)
* Use POD to store meta data in plugin file
	* two sections to show in tabs in dialog: description and about
	* machine readable section with requirements
		* disable the plugin and show message when requirements fail

**Long term:**
* Installer package that handles plugin file, libs and docs
	* also do translations
* Finalize and document how to get alternative views for the pageview
* Come up with a way to add alternative views to side pane (integrate with search drop down (seperator between views and searches) and optionally have tabs ?)
* Come up with a way to have a bottom view with alternative views.

References