/jEdit/tags/jedit-4-1-pre5/doc/users-guide/host-design.xml
# · XML · 1009 lines · 875 code · 116 blank · 18 comment · 0 complexity · 6fd63ab2b1e8fb10099c9736bf764960 MD5 · raw file
- <!-- jEdit 4.0 Plugin Guide, (C) 2001, 2002 John Gellene -->
- <!-- jEdit buffer-local properties: -->
- <!-- :indentSize=1:tabSize=2:noTabs=true:maxLineLen=72: -->
- <!-- This file contains a discussion of the host architecture -->
- <!-- in the jEdit Plugin API -->
- <!-- $Id: host-design.xml 4174 2002-05-19 08:31:14Z spestov $
- -->
- <chapter id="host-design"><title>jEdit as a Plugin Host</title>
- <para>
- A good way to start learning what a plugin requires is to look at what
- the host application does with one. We start our discussion of
- plugins by outlining how jEdit loads and displays them. This section
- only provides a broad overview of the more important components that
- make up jEdit; specifics of the API will be documented in
- subsequent chapters.
- </para>
- <sect1 id="host-design-load"><title>
- <indexterm>
- <primary>Plugin API</primary>
- <secondary>loading at startup</secondary>
- </indexterm>
- Loading Plugins</title>
- <para>
- As part of its startup routine, jEdit's <function>main</function>
- method calls various methods to load and initialize plugins.
- This occurs after the application has done the following:
- </para>
- <itemizedlist>
- <listitem><para>
- parsed command line options;
- </para></listitem>
- <listitem><para>
- started the edit server (unless instructed not to do so
- by a command line option) and;
- </para></listitem>
- <listitem><para>
- loaded application properties, any user-supplied properties, and the
- application's set of actions that will be available from jEdit's menu
- bar (as well as the toolbar and keyboard shortcuts);
- </para></listitem>
- </itemizedlist>
- <para>
- Plugin loading occurs before jEdit creates any windows or loads any files
- for editing. It also occurs before jEdit runs any startup scripts.
- </para>
- <sect2 id="host-design-loader"><title>
- <indexterm>
- <primary>JAR class loader</primary>
- </indexterm>
- The JARClassLoader</title>
- <para>
- Plugins are loaded from files with the <filename>.jar</filename>
- filename extension located in the <filename>jars</filename>
- subdirectories of the jEdit installation and user settings directories
- (see <xref linkend="settings-directory" />).
- </para>
- <para>
- For each JAR archive file it finds, jEdit creates an instance of the
- <classname>JARClassLoader</classname> class. This is a jEdit-specific
- class
- that implements the Java platform's abstract class
- <classname>ClassLoader</classname>. The constructor for the
- <classname>JARClassLoader</classname> object does the following:
- <itemizedlist>
- <listitem><para>
- Reads action definitions from any file named
- <filename>actions.xml</filename> in the archive (the file need
- not be at the top level). See <xref
- linkend="resources-action" />.
- </para></listitem>
- <listitem><para>
- Parses and loads the contents of any file named
- <filename>dockables.xml</filename> in the archive (the file need
- not be at the top level). This file contains BeanShell code for
- creating docking or floating windows that will contain the visible
- components of the plugin. Not all plugins define dockable
- windows,
- but those that do need a <filename>dockables.xml</filename> file.
- See <xref linkend="resources-dockables" />.
- </para></listitem>
- <listitem><para>
- Loads any properties defined in files ending with
- the extension <filename>.props</filename> that are contained
- in the archive. See <xref linkend="resources-properties" />.
- </para></listitem>
- <listitem><para>
- Adds any class file with a name ending with
- <filename>Plugin.class</filename> to an internal collection of
- plugin class names maintained by the
- <classname>JARClassLoader</classname>. See <xref
- linkend="class-editplugin" />.
- </para></listitem>
- <listitem><para>
- Adds to a collection maintained by jEdit a new object of
- type <classname>EditPlugin.JAR</classname>. This is a data structure
- holding the name of the JAR archive file, a reference to the
- <classname>JARClassLoader</classname> and a collection,
- initially empty, of plugins found in the archive file.
- </para></listitem>
- </itemizedlist>
- </para>
- <para>
- Once all JAR files have been examined for the above resources,
- jEdit initializes all class files whose names end in
- <filename>Plugin.class</filename>, as identified in the first pass
- through the JAR archive. We will call these classes
- <indexterm>
- <primary>Plugin API</primary>
- <secondary>plugin core class</secondary>
- </indexterm>
- <firstterm>plugin core classes</firstterm>. They provide
- the principal point of contact between jEdit and the plugin. A plugin
- core class must extend jEdit's abstract <classname>EditPlugin</classname>
- class. Use of a class name ending in <classname>Plugin</classname> is also
- required.
- </para>
- <para>
- <indexterm>
- <primary>Plugins</primary>
- <secondary>dependencies</secondary>
- </indexterm>
- For each plugin core class, the initialization routine first checks the plugin's
- properties to see if it is subject to any dependencies. For example, a
- plugin may require that the version of the Java runtime environment or
- of jEdit itself be equal to or above some threshold version. A plugin
- can also require the presence of another plugin or a particular class
- from another archive. If any dependency is
- not satisfied, the loader marks the plugin as <quote>broken</quote> and
- logs an error message.
- </para>
- <para>
- If all dependencies are satisfied, a new instance
- of the plugin core class is created and added to the collection
- maintained by the appropriate <classname>EditPlugin.JAR</classname>
- object. By accessing that object, jEdit can keep track of plugins it has
- successfully loaded, and call methods or perform routines on them.
- </para>
- <sidebar><title>
- <indexterm>
- <primary>Plugin API</primary>
- <secondary>using class libraries</secondary>
- </indexterm>
- Additional class libraries for plugins</title>
- <para>
- JAR files with no plugin core classes are also loaded by jEdit; no special
- initialization is performed on them, and the classes they contain are
- made available to other plugins. For example, many plugins that rely on
- third-party class libraries ship them as separate JAR archives. The libraries
- will be available inside the jEdit environment but are not part of a general
- classpath or library collection when running other Java applications.
- </para>
- <para>
- A plugin that bundles extra JAR archives must define a property that
- lists these JAR files in order for the plugin manager to be able to
- remove the plugin completely. See <xref
- linkend="resources-properties" />.
- </para>
- </sidebar>
- </sect2>
- <sect2 id="host-design-plugin-start"><title>Starting the Plugin</title>
- <para>
- After creating and storing the plugin core object, jEdit calls the
- <function>start()</function> method of the plugin core class.
- The <function>start()</function> method can perform initialization of the
- object's data members.
- Because this method is defined as an empty <quote>no-op</quote> in the
- <classname>EditPlugin</classname> abstract class, a plugin need not
- provide an implementation if no unique initialization is required.
- </para>
- </sect2>
- <sect2 id="class-editplugin"><title>The EditPlugin Class</title>
- <para>
- Recall that this abstract class is the base for every plugin core
- class. Its methods
- provide for basic interaction between the plugin and jEdit. The class
- has four methods which are called by jEdit at various times. None of
- these methods are required to be implemented, but most plugins will
- override at least one.
- </para>
- <itemizedlist>
- <listitem>
- <funcsynopsis>
- <funcprototype>
- <funcdef>public void <function>start</function></funcdef>
- <paramdef></paramdef>
- </funcprototype>
- </funcsynopsis>
- <para>
- The jEdit startup routine calls this method for each loaded
- plugin. Plugins typically use this method to register information
- with the EditBus and perform other initialization.
- </para>
- </listitem>
- <listitem>
- <funcsynopsis>
- <funcprototype>
- <funcdef>public void <function>stop</function></funcdef>
- <paramdef></paramdef>
- </funcprototype>
- </funcsynopsis>
- <para>
- When jEdit is exiting, it calls this method on each plugin. If a
- plugin uses or creates state information or other persistent data
- that should be stored in a special format, this would be a good place to write
- the data to storage. If you use jEdit's properties API to hold
- <quote>key-value</quote> type settings for your plugins, no special
- processing is needed for them, since jEdit loads application
- properties automatically at startup and writes them to the
- <filename>properties</filename> file in the user's settings directory
- when the application exits. Most plugins find this approach sufficient for
- saving settings.
- </para>
- </listitem>
- <listitem>
- <funcsynopsis>
- <funcprototype>
- <funcdef>public void <function>createMenuItems</function></funcdef>
- <paramdef>Vector <parameter>menuItems</parameter></paramdef>
- </funcprototype>
- </funcsynopsis>
- <para>
- When a <classname>View</classname> object is created, it calls this
- method on each plugin core class to obtain entries to be displayed
- in the view's <guimenu>Plugins</guimenu> menu. The
- <parameter>menuItems</parameter> parameter is a
- <classname>Vector</classname> that accumulates menu items and
- menus as it is passed from plugin to plugin.
- </para>
- <para>
- While jEdit does not require a plugin to supply menu items, a plugin's
- usefulness would be extremely limited without them. The easiest way to
- provide menu items is to
- package them as entries in the plugin's property
- file and implement <function>createMenuItems()</function> with a
- call to jEdit's <function>GUIUtilities.loadMenu()</function>
- method. The following code illustrates this approach:
- </para>
- <informalexample><programlisting>public void createMenuItems(Vector menuItems)
- {
- menuItems.addElement(GUIUtilities.loadMenu(
- "myplugin.menu"));
- }</programlisting></informalexample>
- <para>
- The parameter passed to <function>loadMenu()</function> is
- the name of a property defined in the plugin's own property file that
- contains menu data. The form of the property entry is a list of labels
- that in turn correspond to other property names and ultimately to
- the actual text for menu items as well as implementation code. We will
- detail the format of the menu data in <xref linkend="plugin-implement-menu"/>
- </para>
- <para>
- The <function>GUIUtilities.loadMenuItem()</function> method is also
- available for plugins that only wish to add a single menu item to
- the <guimenu>Plugins</guimenu> menu. The parameter names a property
- that points to label text in the plugin's properties file and
- implementing code in the plugin's <filename>actions.xml</filename>
- file.
- </para>
- </listitem>
- <listitem>
- <funcsynopsis>
- <funcprototype>
- <funcdef>public void <function>createOptionPanes</function></funcdef>
- <paramdef>OptionsDialog <parameter>dialog</parameter></paramdef>
- </funcprototype>
- </funcsynopsis>
- <para>
- This method is called for each plugin during the creation of
- the <guilabel>Global Options</guilabel> dialog box.
- To show an option pane, the plugin should define an
- option pane class and implement <function>createOptionPane()</function>
- as follows:
- </para>
- <informalexample><programlisting>dialog.addOptionPane(new MyPluginOptionPane());</programlisting></informalexample>
- <para>
- Plugins can also define more than one option pane, grouped in an
- <quote>option group</quote>.
- <!-- We will discuss the design and elements of the option pane API
- in <xref linkend="api-option-classes"/>. -->
- </para>
- </listitem>
- </itemizedlist>
- <para>
- This class defines two other methods which may be useful to some
- plugins or for debugging purposes. They are fully implemented
- in the parent class and used mainly by jEdit's core code.
- </para>
- <itemizedlist>
- <listitem>
- <funcsynopsis>
- <funcprototype>
- <funcdef>public String <function>getClassName</function></funcdef>
- <void/>
- </funcprototype>
- </funcsynopsis>
- <para>
- This shortcut method returns <function>getClass().getName()</function>.
- </para>
- </listitem>
- <listitem>
- <funcsynopsis>
- <funcprototype>
- <funcdef>public EditPlugin.JAR <function>getJAR</function></funcdef>
- <void/>
- </funcprototype>
- </funcsynopsis>
- <para>
- This method returns the <classname>EditPlugin.JAR</classname> data
- object associated with the plugin.
- </para>
- </listitem>
- </itemizedlist>
- </sect2>
- </sect1>
- <sect1 id="host-design-resources"><title>Plugin Resources</title>
- <sect2 id="resources-properties"><title>Plugin Properties</title>
- <para>
- jEdit maintains a list of <quote>properties</quote>, which are
- name/value pairs used to store human-readable strings, user settings,
- and various other forms of meta-data. During startup, jEdit loads the
- default set of properties, followed by plugin properties stored in
- plugin JAR files, finally followed by user properties. Plugins can
- access properties from all three sources.
- </para>
- <para>
- Property files contained in plugin JARs must end with the filename
- extension <filename>.props</filename>, and have a very simple syntax,
- which the following example illustrates:
- </para>
- <informalexample><programlisting># Lines starting with '#' are ignored.
- name=value
- another.name=another value
- long.property=Long property value, split over \
- several lines
- escape.property=Newlines and tabs can be inserted \
- using the \t and \n escapes
- backslash.property=A backslash can be inserted by writing \\.</programlisting>
- </informalexample>
- <para>
- The following types of plugin information
- are supplied using properties:
- </para>
- <itemizedlist>
- <listitem>
- <para>
- Information regarding the name, author, and version of the plugin.
- This information is required. Here is an example:
- </para>
- <informalexample><programlisting>plugin.MyPlugin.name=My Plugin
- plugin.MyPlugin.author=Jay Edit
- plugin.MyPlugin.version=1.0.3</programlisting></informalexample>
- <para>
- Note that each property is prefixed with
- <literal>plugin.</literal>, followed by the fully qualified name
- of the plugin core class (including a package name, if there is
- one).
- </para>
- </listitem>
- <listitem>
- <para>
- Identification of any dependencies the plugin may have on a
- particular version of a Java runtime environment, the jEdit
- application, or other plugins.
- </para>
- <para>
- Each dependency is defined in a property prefixed with
- <literal>plugin.<replaceable>class name</replaceable>.depend.</literal>,
- followed by a number. Dependencies must be numbered in order,
- starting from zero.
- </para>
- <para>
- The value of a dependency property is one of the words
- <literal>jdk</literal>, <literal>jedit</literal>,
- or <literal>plugin</literal>,
- followed by a Java version number, a jEdit build number,
- or plugin class name and plugin version number,
- respectively.
- </para>
- <para>
- Here are some examples:
- </para>
- <informalexample><programlisting>plugin.MyPlugin.depend.0=jdk 1.4
- plugin.MyPlugin.depend.1=jedit 04.00.99.00
- plugin.MyPlugin.depend.2=plugin console.ConsolePlugin 3.2.1</programlisting>
- </informalexample>
- </listitem>
- <listitem>
- <para>
- A list of external class library JARs shipped with the plugin.
- If your plugin bundles extra JARs, this property is required
- for the plugin manager to be able to remove the plugin completely.
- </para>
- <para>
- The property is a space-separated list of filenames. Here is an
- example:
- </para>
- <informalexample><programlisting>plugin.xslt.XSLTPlugin.jars=xml-apis.jar xalan.jar</programlisting></informalexample>
- </listitem>
- <listitem>
- <para>
- Labels for user actions for inclusion in menus and option panes
- relating to toolbars and keyboard shortcuts.
- </para>
- <para>
- Action labels are defined in properties named by the
- action's internal name as specified in the action catalog,
- followed by <literal>.label</literal>:
- </para>
- <informalexample><programlisting>myplugin.label=My Plugin
- myplugin-grok.label=Grok Current Buffer</programlisting>
- </informalexample>
- </listitem>
- <listitem>
- <para>
- The list of menu items contained in plugin menus, if any.
- </para>
- <para>
- This is discussed in detail in <xref
- linkend="plugin-implement-menu" />.
- </para>
- </listitem>
- <listitem>
- <para>
- Labels and other information regarding the controls contained in
- the plugin's windows. These properties can be named any way you
- like, however take care not to choose names which may conflict
- with those in other plugins.
- </para>
- </listitem>
- <listitem>
- <para>
- The titles of dockable windows, as displayed in a tabbed or
- floating container.
- </para>
- <para>
- These labels are specified in properties named by the dockable
- window's identifier (as specified in the <filename>dockables.xml</filename>
- file, see <xref linkend="resources-dockables"/>),
- suffixed with <literal>.title</literal>. For example:
- </para>
- <informalexample><programlisting>quick-notepad.title=QuickNotepad</programlisting>
- </informalexample>
- </listitem>
- </itemizedlist>
- </sect2>
- <sect2 id="resources-action"><title>The Action Catalog</title>
- <para>
- Actions define procedures that can be bound to a menu
- item, a toolbar button or a keyboard shortcut. They can perform any
- task encompassed in a public method of any class currently loaded in
- jEdit, including plugin classes and classes of the host application.
- Among other things, they can cause the appearance and disappearance of
- plugin windows.
- </para>
- <para>
- To manage user actions, jEdit maintains a lookup table of actions
- using descriptive strings as keys. The values in the table are
- sets of statements written in BeanShell, jEdit's macro scripting
- language. These scripts either direct the action themselves,
- delegate to a method in one of the plugin's classes that
- encapsulates the action, or do a little of both. The scripts are
- usually short; elaborate action protocols are usually contained in
- compiled code, rather than an interpreted macro script, to speed
- execution.
- </para>
- <para>
- Actions are defined by creating an XML file entitled
- <filename>actions.xml</filename> and placing it in the plugin JAR
- file. A sample action catalog looks like so:
- </para>
- <informalexample><programlisting><![CDATA[<!DOCTYPE ACTIONS SYSTEM "actions.dtd">
- <ACTIONS>
- <ACTION NAME="quicknotepad.choose-file">
- <CODE>
- view.getDockableWindowManager()
- .getDockable(QuickNotepadPlugin.NAME).chooseFile();
- </CODE>
- </ACTION>
- <ACTION NAME="quicknotepad.save-file">
- <CODE>
- view.getDockableWindowManager()
- .getDockable(QuickNotepadPlugin.NAME).saveFile();
- </CODE>
- </ACTION>
- <ACTION NAME="quicknotepad.copy-to-buffer">
- <CODE>
- view.getDockableWindowManager()
- .getDockable(QuickNotepadPlugin.NAME).copyToBuffer();
- </CODE>
- </ACTION>
- </ACTIONS>]]></programlisting></informalexample>
- <para>
- The defined elements have the following functions:
- </para>
- <itemizedlist>
- <listitem>
- <para>
- <varname>ACTIONS</varname> is the top-level element and refers
- to the set of actions used by the plugin.
- </para>
- </listitem>
- <listitem>
- <para>
- An <varname>ACTION</varname> contains the data for a particular action.
- It has three attributes: a required <varname>NAME</varname>;
- an optional <varname>NO_REPEAT</varname>, which is a flag
- indicating whether the action should not be repeated with the
- <keycombo><keycap>Control</keycap><keycap>Enter</keycap></keycombo>
- command (see <xref linkend="repeat" />); and an optional
- <varname>NO_RECORD</varname> which is a a flag indicating whether the
- action should be recorded if it is invoked while a user is recording a
- macro. The two flag attributes
- can have two possible values, <quote>TRUE</quote> or
- <quote>FALSE</quote>. In both cases, <quote>FALSE</quote> is the
- default if the attribute is not specified.
- </para>
- </listitem>
- <listitem>
- <para>
- An <varname>ACTION</varname> can have two child elements
- within it: a required <varname>CODE</varname> element which
- specifies the
- BeanShell code that will be executed when the action is invoked,
- and an optional <varname>IS_SELECTED</varname> element, used for
- checkbox
- menu items. The <varname>IS_SELECTED</varname> element contains
- BeanShell code that returns a boolean flag that will
- determine the state of the checkbox.
- </para>
- </listitem>
- </itemizedlist>
- <para>
- More discussion of the action catalog can be found in <xref
- linkend="plugin-implement-actions" />.
- </para>
- </sect2>
- <sect2 id="resources-dockables"><title>The Dockable Definition File</title>
- <para>
- The jEdit Plugin API uses BeanShell to create the top-level visible container
- of a plugin's interface. The BeanShell code is contained in a file named
- <filename>dockables.xml</filename>. It usually is quite short, providing only
- a single BeanShell expression used to create a visible plugin window.
- </para>
- <para>
- The following example from the QuickNotepad plugin illustrates the
- requirements of the data file:
- <informalexample><programlisting><?xml version="1.0"?>
- <!DOCTYPE DOCKABLES SYSTEM "dockables.dtd">
- <!-- QuickNotepad dockable windows -->
- <DOCKABLES>
- <DOCKABLE NAME="quicknotepad">
- new QuickNotepad(view, position);
- </DOCKABLE>
- </DOCKABLES></programlisting></informalexample>
- In this example, the <classname><DOCKABLE></classname> element has
- a single attribute, the dockable window's identifier. This attribute is
- used to key a property where the window title is stored; see
- <xref linkend="resources-properties"/>.
- <!-- If the plugin has no user actions,
- a second, implicit <classname><NO_ACTIONS></classname> attribute
- should be assigned a value of <constant>true</constant>. -->
- The contents of the <classname><DOCKABLE></classname> element itself is a
- BeanShell expression that constructs a new <classname>QuickNotepad</classname>
- object. The <varname>view</varname> and <varname>position</varname> are
- predefined by the Plugin API as the view in which the plugin window will reside
- and the docking position of the plugin.
- </para>
- </sect2>
- <sect2 id="resources-help"><title>Plugin Documentation</title>
- <para>
- While not required by the plugin API, a help file is an essential
- element of any plugin written for public release. A single web page is
- often all that is required. There are no specific requirements on
- layout, but because of the design of jEdit's help viewer, the use of
- frames should be avoided. Topics that would be useful include
- the following:
- </para>
- <itemizedlist>
- <listitem>
- <para>
- a description of the purpose of the plugin;
- </para>
- </listitem>
- <listitem>
- <para>
- an explanation of the type of input the user can supply through its
- visible interface (such as mouse action or text entry in controls);
- </para>
- </listitem>
- <listitem>
- <para>
- a listing of available user actions that can be taken when the
- plugin does not have input focus;
- </para>
- </listitem>
- <listitem>
- <para>
- a summary of configuration options;
- </para>
- </listitem>
- <listitem>
- <para>
- information on development of the plugin (such as a change log,
- a list of <quote>to do</quote> items, and contact information for
- the plugin's author); and
- </para>
- </listitem>
- <listitem>
- <para>
- licensing information, including acknowledgments for any library
- software used by the plugin.
- </para>
- </listitem>
- </itemizedlist>
- <para>
- The location of the plugin's help file should be stored in the
- <literal>plugin.<replaceable>class name</replaceable>.docs</literal>
- property.
- </para>
- </sect2>
- </sect1>
- <sect1 id="host-design-display"><title>The User Interface of a Plugin</title>
- <para>
- <!-- To display a user interface, plugins can either directly extend Java's
- <classname>JFrame</classname>, <classname>JDialog</classname>, or
- <classname>JWindow</classname> classes, or use jEdit's dockable window
- API. Plugin windows are typically defined in classes that are
- part of the plugin package but separate from the plugin core
- class. -->
- To display a user interface, plugins provide a top-level component
- derived (directly or indirectly) from the Swing
- <classname>JComponent</classname> class. This component will be
- embedded in a docking of floating window created by the Plugin API. It
- is typically defined in a class that is part of the plugin package but
- separate from the plugin core class (if one exists).
- </para>
- <sect2 id="host-display-view"><title>
- <indexterm>
- <primary>View</primary>
- <secondary>use in Plugin API</secondary>
- </indexterm>
- <indexterm>
- <primary>Plugin API</primary>
- <secondary>use of View object</secondary>
- </indexterm>
- The Role of the View Object</title>
- <para>
- A <classname>View</classname> is jEdit's top-level frame window.
- The largest component it contains is an edit pane that in turn contains a
- text area that displays a buffer. A view can have more than one edit pane
- in a split window configuration. The view also contains a menu bar,
- an optional toolbar and other window decorations, as well as docked
- windows.
- </para>
- <para>
- The <classname>View</classname> class performs two important operations
- dealing with plugins: creating plugin menu items, and managing dockable
- windows.
- </para>
- <para>
- <indexterm>
- <primary>Plugin API</primary>
- <secondary>menu creation</secondary>
- </indexterm>
- When a view is being created, its initialization routine
- iterates through the collection of loaded plugins and calls
- the <function>createMenuItems()</function> method of
- each plugin core class. In the parent class,
- <classname>EditPlugin</classname>, this method is an empty
- <quote>no-op</quote>. In order to add items to jEdit's menu bar
- under the <guimenu>Plugins</guimenu> menu, the plugin core class must
- supply its own version of <function>createMenuItems()</function>.
- As we will explain in <xref linkend="class-editplugin"/>,
- the typical plugin, instead of creating Java
- <classname>JMenu</classname> and <classname>JMenuItem</classname>
- objects directly, relies on a wrapper method in a utility class to
- create menu entries.
- </para>
- <para>
- The <classname>View</classname> also creates and initializes a
- <classname>DockableWindowManager</classname> object. This object is
- responsible for creating, closing and managing dockable windows.
- It will be discussed in more detail below.
- </para>
- <para>
- Finally, the <classname>View</classname>
- and <classname>DockableWindowManager</classname>
- classes contain a number of methods
- that can be called from plugins; see <xref linkend="class-view" />
- and <xref linkend="class-dockablewindowmanager" /> for details.
- </para>
- </sect2>
- <sect2 id="host-display-manager">
- <title>
- <indexterm>
- <primary>Plugin API</primary>
- <secondary>DockableWindowManager class</secondary>
- </indexterm>
- <indexterm>
- <primary>DockableWindowManager</primary>
- <secondary>use in Plugin API</secondary>
- </indexterm>
- <indexterm>
- <primary>Plugin API</primary>
- <secondary>creating dockable windows</secondary>
- </indexterm>
- The DockableWindowManager</title>
- <para>
- The <classname>DockableWindowManager</classname> in each
- <classname>View</classname> object keeps track of docked
- and floating windows. When the <classname>View</classname> object
- initializes its <classname>DockableWindowManager</classname>, the
- manager iterates through the list of registered dockable windows and
- examines options supplied by the user in the <guilabel>Global
- Options</guilabel> dialog box. It displays any plugins that the user
- designated for one of the four docking positions when the corresponding
- button a docking pane is selected.
- </para>
- <para>
- To create an instance of a dockable window, the
- <classname>DockableWindowManager</classname> finds and executes the
- BeanShell code extracted from the plugin's
- <filename>dockables.xml</filename> file during application startup. This
- code will typically consist of a call to the constructor of the docking
- window component that passes two parameters: the
- <classname>View</classname> associated with the docking window
- component, and a <classname>String</classname> representing the component's
- docking or floating position. The result of the BeanShell expression,
- typically a newly constructed component, is placed inside the
- docking or floating window managed by the
- <classname>DockableWindowManager</classname>.
- </para>
- <para>
- Eventually the <classname>DockableWindowManager</classname> destroys
- the plugin window, whether docking or floating, in response to user
- action or as part of the destruction of the corresponding
- <classname>View</classname> object.
- </para>
- <para>
- With this broad outline of how jEdit behaves as a plugin host in the
- background, we will next review the programming elements that make up
- a plugin.
- </para>
- </sect2>
- </sect1>
- <sect1 id="host-design-editbus"><title>The EditBus</title>
- <para>
- The
- EditBus maintains a list of objects that have requested to receive
- messages. When a message is sent using this class, all registered
- components receive it in turn.
- </para>
- <para>
- Plugins register with the EditBus to receive messages reflecting
- changes in the application's state, including changes in buffers,
- views and editing panes, changes in the set of properties maintained
- by the application, and the closing of the application. A full list
- of message classes used by the EditBus are summarized beginning with
- <xref linkend="class-ebmessage"/>.
- </para>
- <para>
- <indexterm>
- <primary>Plugin API</primary>
- <secondary>EBComponent class</secondary>
- </indexterm>
- Classes for objects that subscribe to the EditBus must implement the
- <classname>EBComponent</classname> interface, which defines the single
- method <function>handleMessage()</function>. A
- <classname>View</classname>, for example, can receive and handle EditBus
- messages because it also implements <classname>EBComponent</classname>.
- </para>
- <para>
- Any class in a plugin can receive messages by implementing
- the <classname>EBComponent</classname>
- interface. A <quote>plugin core class</quote> that extends the
- <classname>EBPlugin</classname> abstract class (and whose name ends with
- <quote>Plugin</quote> for identification purposes) will automatically be
- added to the EditBus during jEdit's startup routine. Any other
- class - for example, a docking window component that needs to receive
- notification of buffer changes - must perform its own registration by calling
- <function>EditBus.addToBus(this)</function> during its initialization.
- If this class if derived from <classname>JComponent</classname>, a
- convenient place to register would be in an implementation of the
- <classname>JComponent</classname> method
- <function>addNotify()</function>.
- </para>
- <sect2 id="class-ebcomponent"><title>Interface EBComponent</title>
- <para>
- Every plugin class that uses the EditBus for receiving
- messages
- must implement this interface.
- </para>
- <para>
- The <classname>EBComponent</classname> interface contains a single
- method that an implementing class (including any class derived from
- <classname>EBPlugin</classname>) must provide:
- </para>
- <itemizedlist>
- <listitem>
- <funcsynopsis>
- <funcprototype>
- <funcdef>public void <function>handleMessage</function></funcdef>
- <paramdef>EBMessage <parameter>message</parameter></paramdef>
- </funcprototype>
- </funcsynopsis>
- </listitem>
- </itemizedlist>
- <para>
- The parameter's type, <classname>EBMessage</classname>, is another
- abstract class which establishes the core elements of any message that
- is published to the EditBus. It has two attributes: an
- <classname>EBComponent</classname> that is the source of the message
- (the source will be <classname>null</classname> in some cases),
- and a <classname>boolean</classname> data member, <varname>vetoed</varname>. This
- flag indicates whether a prior recipient of the message has determined
- that the message has been handled and need not be passed on to other
- subscribers. The flag is set by a call
- to the <function>veto()</function> method of the
- <classname>EBMessage</classname>. Some message classes, however,
- are configured so that they cannot be vetoed, to ensure they are
- received by all subscribers.
- </para>
- <para>
- Message classes extending <classname>EBMessage</classname> typically add
- other data members and methods to provide subscribers with whatever is
- needed to handle the message appropriately. Descriptions of specific
- message classes can be found in <xref linkend="api-message"/>.
- </para>
- <para>
- The <function>handleMessage()</function> method
- must specify the type of responses
- the plugin will have for various subclasses of the
- <classname>EBMessage</classname> class. Typically this is done with
- one or more <function>if</function> blocks that test whether the message
- is an instance of a derived message class in which the plugin has an
- interest, as in the following example:
- </para>
- <informalexample><programlisting>if(msg instanceof BufferUpdate) {
- // a buffer's state has changed!
- }
- else if(msg instanceof ViewUpdate) {
- // a view's state has changed!
- }
- // ... and so on</programlisting></informalexample>
- <para>
- Note that any object, whether or not derived from
- <classname>EBComponent</classname>, can send a message to the EditBus
- by calling the static method <function>EditBus.send()</function>.
- This method takes a single parameter, an <classname>EBMessage</classname>
- object that is the message being sent. Most plugins, however, will
- only concern themselves with receiving, not sending, messages.
- </para>
- </sect2>
- </sect1>
- <sect1 id="host-design-conclusion"><title>Conclusion</title>
- <para>
- At this point, we can identify the following practical requirements
- for a plugin:
- </para>
- <itemizedlist>
- <listitem><para>
- it must be packaged as one or more JAR archives and contain a class
- file ending with the prefix <filename>Plugin</filename> that extends
- the <classname>EditPlugin</classname> abstract class;
- </para></listitem>
- <listitem><para>
- the archive must contain
- at least one properties file having a <filename>.props</filename>
- extension. Certain properties giving information
- about the plugin must be defined.
- </para></listitem>
- <listitem><para>
- if the plugin will provide a visible interface, the JAR archive must contain
- an XML data file named <filename>dockables.xml</filename> containing
- BeanShell code for creating a container for that interface;
- </para></listitem>
- <listitem><para>
- the JAR archive may contain data concerning actions for display
- in jEdit's menu bar and elsewhere in a file entitled
- <filename>actions.xml</filename>; and
- </para></listitem>
- <listitem><para>
- if the plugin must respond as a whole to changes in the state of the jEdit
- application, the plugin core class should be derived from the
- <classname>EBPlugin</classname> class instead of directly from
- <classname>EditPlugin</classname>
- </para></listitem>
- </itemizedlist>
- <para>
- We will provide more detail on these requirements later.
- </para>
- </sect1>
- </chapter>