PageRenderTime 142ms CodeModel.GetById 127ms app.highlight 8ms RepoModel.GetById 1ms app.codeStats 1ms

/www/tags/NOV_07_2009/htdocs/42docs/users-guide/plugin-implement.xml

#
XML | 1185 lines | 973 code | 194 blank | 18 comment | 0 complexity | a7ea7e9708de441a6373175c8f02c387 MD5 | raw file
   1<!-- jEdit 4.0 Plugin Guide, (C) 2001, 2002 John Gellene            -->
   2
   3<!-- jEdit buffer-local properties:                           -->
   4<!-- :indentSize=1:tabSize=2:noTabs=true:maxLineLen=72:       -->
   5<!-- :xml.root=users-guide.xml:                               -->
   6
   7<!-- Sat Jun 23 08:54:21 EDT 2001 @579 /Internet Time/        -->
   8
   9<!-- This chapter of the jEdit 3.2 Plugin Guide               -->
  10<!-- discusses how to implement a plugin                      -->
  11
  12<chapter id="plugin-implement"><title>Implementing a Simple Plugin</title>
  13
  14<para>
  15  There are many applications for the leading operating systems that
  16  provide a <quote>scratch-pad</quote> or <quote>sticky note</quote>
  17  facility for the desktop display. A similar type of facility operating
  18  within the jEdit display would be a convenience. The use of dockable
  19  windows would allow the notepad to be displayed or hidden with a single
  20  mouse click or keypress (if a keyboard shortcut were defined). The
  21  contents of the notepad could be saved at program exit (or, if earlier,
  22  deactivation of the plugin) and retrieved at program startup or plugin
  23  activation.
  24</para>
  25
  26<para>
  27  We will keep the capabilities of this plugin modest, but a few
  28  other features would be worthwhile. The user should be able to write
  29  the contents of the notepad to storage on demand.  It should also be
  30  possible to choose the name and location of the file that will be
  31  used to hold the notepad text.  This would allow the user to load
  32  other files into the notepad display.  The path of the notepad file
  33  should be displayed in the plugin window, but will give the user the
  34  option to hide the file name. Finally, there should be an action
  35  by which a single click or keypress would cause the contents of the
  36  notepad to be written to the new text buffer for further processing.
  37</para>
  38
  39<para>
  40  The full source code for QuickNotepad is contained in jEdit's
  41  source code distribution.  We will provide excerpts in this discussion
  42  where it is helpful to illustrate specific points. You are invited
  43  to obtain the source code for further study or to use as a starting point
  44  for your own plugin.
  45</para>
  46
  47<sect1 id="plugin-load"><title>
  48<indexterm>
  49	<primary>Plugin API</primary>
  50	<secondary>loading at startup</secondary>
  51</indexterm>
  52How Plugins are Loaded</title>
  53
  54<para>
  55	We will
  56  discuss the implementation of the <application>QuickNotepad</application>
  57  plugin, along with the jEdit APIs
  58  it makes use of. But first, we describe how plugins are loaded.
  59</para>
  60
  61<para>
  62  As part of its startup routine, jEdit's <function>main</function>
  63  method calls various methods to load and initialize plugins.
  64</para>
  65
  66<para>
  67  Additionally, plugins using the new jEdit 4.2 plugin API can be 
  68  loaded and unloaded at any time. This is a great help when 
  69  developing your own plugins -- there is no need to restart the 
  70  editor after making changes (see <xref linkend="plugin-implement-reloading"/> ).
  71</para>
  72
  73<para>
  74  Note that plugins using the older jEdit 4.1 API are still only loaded on editor startup, and unloaded on editor exit. The jEdit 4.1 API is deprecated and will not be described in this guide.
  75</para>
  76
  77<para>
  78  Plugins are loaded from files with the <filename>.jar</filename>
  79  filename extension located in the <filename>jars</filename>
  80  subdirectories of the jEdit installation and user settings directories
  81  (see <xref linkend="settings-directory" />).
  82</para>
  83
  84<para>
  85  For each JAR archive file it finds, jEdit scans its entries and
  86  performs the following tasks:
  87</para>
  88
  89<itemizedlist>
  90 <listitem><para>
  91   Adds to a collection maintained by jEdit a new object of
  92   type <ulink url="../api/org/gjt/sp/jedit/PluginJAR.html">
  93   <classname>PluginJAR</classname></ulink>. This is a data structure
  94   holding the name of the JAR archive file, a reference to the
  95   <ulink url="../api/org/gjt/sp/jedit/JARClassLoader.html">
  96   <classname>JARClassLoader</classname></ulink>, and a collection
  97   of plugins found in the archive file.
  98  </para></listitem>
  99  <listitem><para>
 100    Loads any properties defined in files ending with
 101    the extension <filename>.props</filename> that are contained
 102    in the archive. See <xref linkend="plugin-implement-properties" />.
 103  </para></listitem>
 104  <listitem><para>
 105    Reads action definitions from any file named
 106    <filename>actions.xml</filename> in the archive (the file need
 107    not be at the top level). See <xref
 108    linkend="plugin-implement-actions" />.
 109  </para></listitem>
 110  <listitem><para>
 111    Parses and loads the contents of any file named
 112    <filename>dockables.xml</filename> in the archive (the file need
 113    not be at the top level). This file contains BeanShell code for
 114    creating docking or floating windows that will contain the visible
 115    components of the plugin.  Not all plugins define dockable
 116    windows,
 117    but those that do need a <filename>dockables.xml</filename> file.
 118    See <xref linkend="plugin-implement-dockables" />.
 119  </para></listitem>
 120  <listitem><para>
 121    Checks for a class name with a name ending with
 122    <filename>Plugin.class</filename>.
 123  </para>
 124  <para>
 125    Such a class is known as a <firstterm>plugin core class</firstterm> and must
 126    extend jEdit's abstract
 127    <ulink url="../api/org/gjt/sp/jedit/EditPlugin.html">
 128    <classname>EditPlugin</classname></ulink>
 129    class. The initialization routine checks the plugin's
 130    properties to see if it is subject to any dependencies. For example, a
 131    plugin may require that the version of the Java runtime environment or
 132    of jEdit itself be equal to or above some threshold version. A plugin
 133    can also require the presence of another plugin.
 134  </para>
 135  <para>
 136    If any dependency is
 137    not satisfied, the loader marks the plugin as <quote>broken</quote> and
 138    logs an error message.
 139  </para>
 140  </listitem>
 141</itemizedlist>
 142
 143<para>
 144  After scanning the plugin JAR file and loading any resources,
 145  a new instance
 146  of the plugin core class is created and added to the collection
 147  maintained by the appropriate <ulink url="../api/org/gjt/sp/jedit/PluginJAR.html">
 148  <classname>PluginJAR</classname></ulink>.
 149  jEdit then calls the
 150  <function>start()</function> method of the plugin core class.
 151  The <function>start()</function> method can perform initialization of the
 152  object's data members.
 153  Because this method is defined as an empty <quote>no-op</quote> in the
 154  <ulink url="../api/org/gjt/sp/jedit/EditPlugin.html">
 155  <classname>EditPlugin</classname></ulink> abstract class, a plugin need not
 156  provide an implementation if no unique initialization is required.
 157</para>
 158
 159<sidebar>
 160
 161<title>Updating 4.1 plugins</title>
 162
 163<para>
 164 Note that while jEdit 4.1 plugins were only loaded on startup, jEdit 4.2 plugins can be loaded at any time. As a result, the <function>start()</function> method needs to cope with being called at any time, and <function>stop()</function> needs to fully clean up after the plugin. See the API documentation for the <ulink url="../api/org/gjt/sp/jedit/EditPlugin.html">
 165  <classname>EditPlugin</classname></ulink> class for details.
 166 </para>
 167</sidebar>
 168
 169</sect1>
 170
 171<sect1 id="plugin-implement-quicknotepadplugin"><title>The QuickNotepadPlugin Class</title>
 172
 173<para>
 174    The major issues encountered when writing a plugin core class arise
 175    from the developer's decisions on what features the plugin will make
 176    available. These issues have implications for other plugin elements
 177    as well.
 178</para>
 179
 180<itemizedlist>
 181  <listitem><para>
 182    Will the plugin provide for actions that the user can trigger using
 183    jEdit's menu items, toolbar buttons and keyboard shortcuts?
 184  </para></listitem>
 185  <listitem><para>
 186    Will the plugin have its own visible interface?
 187  </para></listitem>
 188  <listitem><para>
 189    Will the plugin have settings that the user can configure?
 190  </para></listitem>
 191  <listitem><para>
 192    Will the plugin
 193    respond to any messages reflecting changes in the host
 194    application's state?
 195  </para></listitem>
 196
 197</itemizedlist>
 198
 199<para>
 200  Recall that the plugin core class must extend
 201  <ulink url="../api/org/gjt/sp/jedit/EditPlugin.html">
 202  <classname>EditPlugin</classname></ulink>.
 203  In QuickNotepad's plugin core class, there are no special
 204  initialization or shutdown chores to perform, so we will not need
 205  a <function>start()</function> or <function>stop()</function> method.
 206</para>
 207
 208<para>
 209  The resulting plugin core class is lightweight and straightforward to implement:
 210</para>
 211
 212<itemizedlist><listitem>
 213
 214<informalexample><programlisting>public class QuickNotepadPlugin extends EditPlugin {
 215    public static final String NAME = "quicknotepad";
 216    public static final String MENU = "quicknotepad.menu";
 217    public static final String PROPERTY_PREFIX
 218        = "plugin.QuickNotepadPlugin.";
 219    public static final String OPTION_PREFIX
 220        = "options.quicknotepad.";
 221</programlisting></informalexample>
 222
 223<para>
 224  First we define a few static
 225  <classname>String</classname> data members to enforce consistent syntax
 226  for the name of properties we will use throughout the plugin.
 227</para>
 228
 229</listitem>
 230
 231<listitem>
 232
 233<informalexample><programlisting>
 234    public void createMenuItems(Vector menuItems) {
 235        menuItems.addElement(GUIUtilities.loadMenu(MENU));
 236    }</programlisting></informalexample>
 237
 238<para>
 239  This implementation of
 240  the <ulink url="../api/org/gjt/sp/jedit/EditPlugin.html#createMenuItems(java.util.Vector)">
 241  <classname>EditPlugin.createMenuItems()</classname></ulink> method
 242  is very typical.
 243  It uses a jEdit utility function to create the menu, taking the list
 244  of actions from the <filename>quicknotepad</filename> property, and
 245  the label from <filename>quotenotepad.label</filename>.
 246</para>
 247
 248<para>
 249  If the plugin only had a single menu item (for example, an item
 250  activating a dockable window), we would call
 251  <ulink url="../api/org/gjt/sp/jedit/GUIUtilities.html#loadMenuItem(java.lang.String)">
 252  <function>GUIUtilities.loadMenuItem()</function></ulink> instead of
 253  <ulink url="../api/org/gjt/sp/jedit/GUIUtilities.html#loadMenu(java.lang.String)">
 254  <function>GUIUtilities.loadMenu()</function></ulink>.
 255</para>
 256
 257</listitem>
 258
 259<listitem>
 260
 261<informalexample><programlisting>public void createOptionPanes(OptionsDialog od) {
 262        od.addOptionPane(new QuickNotepadOptionPane());
 263    }
 264
 265
 266}</programlisting></informalexample>
 267
 268<para>
 269  This implementation of
 270  the <ulink
 271  url="../api/org/gjt/sp/jedit/EditPlugin.html#createOptionPanes(org.gjt.sp.jedit.gui.OptionsDialog)">
 272  <classname>EditPlugin.createOptionPanes()</classname></ulink> method
 273  adds a new instance of <classname>QuickNotepadOptionPane</classname>
 274  to the given instance of the <guimenuitem>Global Options</guimenuitem>
 275  dialog box.
 276</para>
 277
 278</listitem>
 279
 280</itemizedlist>
 281
 282</sect1>
 283
 284<sect1 id="plugin-implement-editbus"><title>The EditBus</title>
 285
 286<para>
 287  Plugins register <ulink url="../api/org/gjt/sp/jedit/EBComponent.html">
 288  <function>EBComponent</function></ulink> instances with the
 289  <ulink url="../api/org/gjt/sp/jedit/EditBus.html">
 290  <classname>EditBus</classname></ulink> to receive messages reflecting
 291  changes in jEdit's state.
 292</para>
 293
 294<para>
 295  The message
 296  classes derived from <ulink url="../api/org/gjt/sp/jedit/EBMessage.html">
 297  <classname>EBMessage</classname></ulink> cover the opening
 298  and closing of the application, changes in the status of buffers and views,
 299  changes in user settings, as well as changes in
 300  the state of other program features. A full list of messages can be found in the
 301  <ulink url="../api/org/gjt/sp/jedit/msg/package-summary.html">org.gjt.sp.jedit.msg</ulink>
 302  package.
 303</para>
 304
 305<para>
 306  <ulink url="../api/org/gjt/sp/jedit/EBComponent.html">
 307  <function>EBComponent</function></ulink>s are added and removed with the
 308  <ulink url="../api/org/gjt/sp/jedit/EditBus.html#addToBus(org.gjt.sp.jedit.EBComponent)">
 309  <function>EditBus.addToBus()</function></ulink> and
 310  <ulink url="../api/org/gjt/sp/jedit/EditBus.html#removeFromBus(org.gjt.sp.jedit.EBComponent)">
 311  <function>EditBus.removeFromBus()</function></ulink>
 312  methods.
 313</para>
 314
 315<para>
 316  Typically, the <ulink url="../api/org/gjt/sp/jedit/EBComponent.html#handleMessage(org.gjt.sp.jedit.EBMessage)">
 317  <function>EBComponent.handleMessage()</function></ulink> method
 318  is implemented with one or more <function>if</function> blocks that test
 319	 whether the message is an instance of a derived message class in
 320	 which the component has an interest.
 321</para>
 322
 323<programlisting>if(msg instanceof BufferUpdate) {
 324    // a buffer's state has changed!
 325}
 326else if(msg instanceof ViewUpdate) {
 327    // a view's state has changed!
 328}
 329// ... and so on</programlisting>
 330
 331<para>
 332  If a plugin core class will respond to EditBus
 333  messages, it can be derived from
 334  <ulink url="../api/org/gjt/sp/jedit/EBPlugin.html">
 335  <classname>EBPlugin</classname></ulink>, in which case no explicit
 336  <function>addToBus()</function> call is necessary.
 337  Otherwise,
 338  <ulink url="../api/org/gjt/sp/jedit/EditPlugin.html">
 339  <classname>EditPlugin</classname></ulink> will suffice as a
 340  plugin base class. Note that QuickNotepad uses the latter.
 341</para>
 342
 343</sect1>
 344
 345<sect1 id="plugin-implement-properties"><title>The Property File</title>
 346
 347<para>
 348  jEdit maintains a list of <quote>properties</quote>, which are
 349  name/value pairs used to store human-readable strings, user settings,
 350  and various other forms of meta-data. During startup, jEdit loads the
 351  default set of properties, followed by plugin properties stored in
 352  plugin JAR files, finally followed by user properties.
 353</para>
 354
 355<para>
 356  Some properties are used by the plugin API itself. Others are
 357  accessed by the plugin using methods in the
 358  <ulink url="../api/org/gjt/sp/jedit/jEdit.html">
 359  <classname>jEdit</classname></ulink>
 360  class.
 361</para>
 362
 363<para>
 364  Property files contained in plugin JARs must end with the filename
 365  extension <filename>.props</filename>, and have a very simple syntax,
 366  which the following example illustrates:
 367</para>
 368
 369<informalexample><programlisting># Lines starting with '#' are ignored.
 370name=value
 371another.name=another value
 372long.property=Long property value, split over \
 373    several lines
 374escape.property=Newlines and tabs can be inserted \
 375    using the \t and \n escapes
 376backslash.property=A backslash can be inserted by writing \\.</programlisting>
 377</informalexample>
 378
 379<para>
 380  Now we look at the <filename>QuickNotepad.props</filename> file
 381  which contains properties for the QuickNotepad plugin.
 382  The first type of property data is information about the plugin itself;
 383  these are the only properties that must be specified in order for the
 384  plugin to load:
 385</para>
 386
 387<informalexample><programlisting># general plugin information
 388plugin.QuickNotepadPlugin.activate=defer
 389plugin.QuickNotepadPlugin.name=QuickNotepad
 390plugin.QuickNotepadPlugin.author=John Gellene
 391plugin.QuickNotepadPlugin.version=4.1
 392plugin.QuickNotepadPlugin.docs=QuickNotepad.html
 393plugin.QuickNotepadPlugin.depend.0=jedit 04.02.10.00</programlisting></informalexample>
 394<para>
 395  These properties are described in detail in the documentation for the
 396  <ulink url="../api/org/gjt/sp/jedit/EditPlugin.html">
 397  <classname>EditPlugin</classname></ulink> class
 398  and do not require further
 399  discussion here.
 400</para>
 401
 402<para>
 403  Next in the file comes a property that sets the title of the
 404  plugin's dockable window. Dockable windows are discussed in detail
 405  in <xref linkend="plugin-implement-dockables"/>.
 406</para>
 407
 408<informalexample><programlisting># dockable window name
 409quicknotepad.title=QuickNotepad</programlisting></informalexample>
 410
 411<para>
 412  Next, we see menu item labels for the plugin's actions.
 413  Actions are discussed in detail
 414  in <xref linkend="plugin-implement-actions"/>.
 415</para>
 416
 417<informalexample><programlisting># action labels
 418quicknotepad.label=QuickNotepad
 419quicknotepad.choose-file.label=Choose notepad file
 420quicknotepad.save-file.label=Save notepad file
 421quicknotepad.copy-to-buffer.label=Copy notepad to buffer</programlisting></informalexample>
 422
 423<para>
 424  Next, the plugin's menu is defined. See
 425  <xref linkend="plugin-implement-quicknotepadplugin"/>.
 426</para>
 427
 428<informalexample><programlisting># application menu items
 429quicknotepad.menu.label=QuickNotepad
 430quicknotepad.menu=quicknotepad - quicknotepad.choose-file \
 431  quicknotepad.save-file quicknotepad.copy-to-buffer</programlisting></informalexample>
 432
 433<para>
 434  We have created a small toolbar as a component of QuickNotepad, so
 435  file names for the button icons follow:
 436</para>
 437
 438<informalexample><programlisting># plugin toolbar buttons
 439quicknotepad.choose-file.icon=Open.png
 440quicknotepad.save-file.icon=Save.png
 441quicknotepad.copy-to-buffer.icon=Edit.png</programlisting></informalexample>
 442
 443<para>
 444  The menu item labels corresponding to these icons will also serve as tooltip
 445  text.
 446</para>
 447
 448<para>
 449  Finally, the properties file set forth the labels and settings
 450  used by the option pane:
 451</para>
 452
 453<informalexample><programlisting># Option pane labels
 454options.quicknotepad.label=QuickNotepad
 455options.quicknotepad.file=File:
 456options.quicknotepad.choose-file=Choose
 457options.quicknotepad.choose-file.title=Choose a notepad file
 458options.quicknotepad.choose-font=Font:
 459options.quicknotepad.show-filepath.title=Display notepad file path
 460
 461# Initial default font settings
 462options.quicknotepad.show-filepath=true
 463options.quicknotepad.font=Monospaced
 464options.quicknotepad.fontstyle=0
 465options.quicknotepad.fontsize=14
 466
 467# Setting not defined but supplied for completeness
 468options.quicknotepad.filepath=</programlisting></informalexample>
 469
 470<sidebar>
 471
 472<title>Updating 4.1 plugins</title>
 473
 474<para>
 475 jEdit 4.2 plugins are distinguished from jEdit 4.1 plugins by the presence of the <literal>plugin.<replaceable>name</replaceable>.activate</literal> property. If this property is set, the plugin is treated like a jEdit 4.2 plugin. Usually, this property should be set to <literal>defer</literal>. See the API documentation for the <ulink url="../api/org/gjt/sp/jedit/EditPlugin.html">
 476  <classname>EditPlugin</classname></ulink> class for details.
 477 </para>
 478</sidebar>
 479
 480</sect1>
 481
 482<sect1 id="plugin-implement-actions"><title>The Action Catalog</title>
 483
 484<para>
 485  Actions define procedures that can be bound to a menu
 486  item, a toolbar button or a keyboard shortcut. Actions are short
 487  scripts written in BeanShell, jEdit's macro scripting
 488  language.  These scripts either direct the action themselves,
 489  delegate to a method in one of the plugin's classes that
 490  encapsulates the action, or do a little of both.  The scripts are
 491  usually short; elaborate action protocols are usually contained in
 492  compiled code, rather than an interpreted macro script, to speed
 493  execution.
 494</para>
 495
 496<para>
 497  Actions are defined by creating an XML file entitled
 498  <filename>actions.xml</filename> and placing it in the plugin JAR
 499  file.
 500</para>
 501
 502<para>
 503  The <filename>actions.xml</filename>
 504  file from the <application>QuickNotepad</application> plugin looks
 505  as follows:
 506</para>
 507
 508<informalexample><programlisting>&lt;?xml version="1.0"?&gt;
 509
 510&lt;!DOCTYPE ACTIONS SYSTEM "actions.dtd"&gt;
 511
 512&lt;ACTIONS&gt;
 513    &lt;ACTION NAME="quicknotepad.choose-file"&gt;
 514        &lt;CODE&gt;
 515            wm.getDockable(QuickNotepadPlugin.NAME).chooseFile();
 516        &lt;/CODE&gt;
 517    &lt;/ACTION&gt;
 518
 519    &lt;ACTION NAME="quicknotepad.save-file"&gt;
 520        &lt;CODE&gt;
 521            wm.getDockable(QuickNotepadPlugin.NAME).saveFile();
 522        &lt;/CODE&gt;
 523    &lt;/ACTION&gt;
 524
 525    &lt;ACTION NAME="quicknotepad.copy-to-buffer"&gt;
 526        &lt;CODE&gt;
 527            wm.getDockable(QuickNotepadPlugin.NAME).copyToBuffer();
 528        &lt;/CODE&gt;
 529    &lt;/ACTION&gt;
 530&lt;/ACTIONS&gt;</programlisting></informalexample>
 531
 532<para>
 533  This file defines three actions. They use the current view's
 534  <ulink url="../api/org/gjt/sp/jedit/gui/DockableWindowManager.html">
 535  <classname>DockableWindowManager</classname></ulink> object and the method
 536  <filename>getDockable()</filename> to find the QuickNotepad plugin
 537  window and call the desired method.
 538</para>
 539
 540<para>
 541  When an action is invoked, the BeanShell scripts address
 542  the plugin through static methods, or if instance data is needed, the
 543  current <ulink url="../api/org/gjt/sp/jedit/View.html">
 544  <classname>View</classname></ulink>, its
 545  <ulink url="../api/org/gjt/sp/jedit/gui/DockableWindowManager.html">
 546  <classname>DockableWindowManager</classname></ulink>, and the plugin
 547  object return by the <filename>getDockable()</filename> method.
 548</para>
 549
 550<para>
 551  If you are unfamiliar with BeanShell code, you may nevertheless notice
 552  that the code statements bear a strong resemblance to Java code, with
 553  one exception: the
 554  variable <varname>view</varname> is never assigned any value.
 555</para>
 556
 557<para>
 558  For complete answers to this and other BeanShell
 559  mysteries, see <xref linkend="writing-macros-part" />; two
 560  observations will suffice here. First, the variable
 561  <varname>view</varname> is predefined by jEdit's implementation of
 562  BeanShell to refer to the current <classname>View</classname> object.
 563  Second, the
 564  BeanShell scripting language is based upon Java syntax, but allows
 565  variables to be typed at run time, so explicit types for variables
 566  need not be declared.
 567</para>
 568
 569<para>
 570  A formal description of each element of the
 571  <filename>actions.xml</filename> file can be found in the
 572  documentation of the
 573  <ulink url="../api/org/gjt/sp/jedit/ActionSet.html">
 574  <classname>ActionSet</classname></ulink> class.
 575</para>
 576
 577</sect1>
 578
 579<sect1 id="plugin-implement-dockables"><title>The Dockable Window Catalog</title>
 580
 581<para>
 582  The jEdit plugin API uses BeanShell to create the top-level visible container
 583  of a plugin's interface.  The BeanShell code is contained in a file named
 584  <filename>dockables.xml</filename>.  It usually is quite short, providing only
 585  a single BeanShell expression used to create a visible plugin window.
 586</para>
 587
 588<para>
 589  The following example from the QuickNotepad plugin illustrates the
 590  requirements of the data file:
 591</para>
 592
 593<informalexample><programlisting><![CDATA[<?xml version="1.0"?>
 594
 595<!DOCTYPE DOCKABLES SYSTEM "dockables.dtd">
 596
 597<DOCKABLES>
 598	<DOCKABLE NAME="quicknotepad">
 599		new QuickNotepad(view, position);
 600	</DOCKABLE>
 601</DOCKABLES>]]></programlisting></informalexample>
 602
 603<para>
 604
 605In this example, the <classname>&lt;DOCKABLE&gt;</classname> element has
 606a single attribute, the dockable window's identifier. This attribute is
 607used to key a property where the window title is stored; see
 608<xref linkend="plugin-implement-properties"/>.
 609
 610</para>
 611
 612<para>
 613
 614The contents of the <classname>&lt;DOCKABLE&gt;</classname> element itself is a
 615BeanShell expression that constructs a new <classname>QuickNotepad</classname>
 616object.  The <varname>view</varname> and <varname>position</varname> are
 617predefined by the plugin API as the view in which the plugin window will reside,
 618and the docking position of the plugin.
 619
 620</para>
 621
 622<para>
 623  A formal description of each element of the
 624  <filename>dockables.xml</filename> file can be found in the
 625  documentation of the
 626  <ulink url="../api/org/gjt/sp/jedit/gui/DockableWindowManager.html">
 627  <classname>DockableWindowManager</classname></ulink> class.
 628</para>
 629
 630</sect1>
 631
 632<sect1 id="plugin-implement-quicknotepad"><title>The QuickNotepad Class</title>
 633
 634<para>
 635  Here is where most of the features of the plugin will be implemented.
 636  To work with the dockable window API, the top level window will be a
 637  <classname>JPanel</classname>.  The visible components reflect a
 638  simple layout. Inside the top-level panel we will place a scroll pane with
 639  a text area. Above the scroll pane we will place a panel containing a small
 640  tool bar and a label displaying the path of the current notepad file.
 641</para>
 642
 643<para>
 644  We have identified three user actions that need
 645  implementation here: <function>chooseFile()</function>,
 646  <function>saveFile()</function>, and
 647  <function>copyToBuffer()</function>. As noted earlier, we also want the
 648  text area to change its appearance in immediate response to a change in
 649  user options settings. In order to do that, the window class must
 650  respond to a <classname>PropertiesChanged</classname> message from
 651  the EditBus.
 652</para>
 653
 654<!-- <para>
 655  We could have the plugin core class receive and delegate
 656  <classname>PropertiesChanged</classname> messages to the window class.
 657  However, this would require the plugin core class to hold a reference
 658  to either the plugin window class or the visible window class and to
 659  update that reference when the user activates or deactivates the
 660  plugin.  It is simpler to have the plugin window class subscribe to the
 661  EditBus directly; many plugins take this approach.  This means that
 662  <classname>QuickNotepad</classname> must implement the
 663  <classname>EBComponent</classname> interface.
 664</para> -->
 665
 666<para>
 667  Unlike the <classname>EBPlugin</classname> class, the
 668  <classname>EBComponent</classname> interface does not deal with the
 669  component's actual subscribing and unsubscribing to the EditBus.  To
 670  accomplish this, we use a pair of methods inherited from the
 671  Java platform's <classname>JComponent</classname> class
 672  that are called when the window is made visible, and when it is hidden.
 673  These two methods,
 674  <function>addNotify()</function> and
 675  <function>removeNotify()</function>, are overridden to add and remove
 676  the visible window from the list of EditBus subscribers.
 677</para>
 678
 679<para>
 680  We will provide for two minor features when the notepad is
 681  displayed in the floating window.  First, when a floating plugin window
 682  is created, we will give the notepad text area input focus.  Second,
 683  when the notepad if floating and has input focus, we will have the
 684  <keycap>Escape</keycap> key dismiss the notepad window.  An
 685  <classname>AncestorListener</classname> and a
 686  <classname>KeyListener</classname> will implement these details.
 687</para>
 688
 689<para>
 690  Here is the listing for the data members, the constructor, and the
 691  implementation of the <classname>EBComponent</classname> interface:
 692</para>
 693
 694
 695<informalexample><programlisting>public class QuickNotepad extends JPanel
 696    implements EBComponent
 697{
 698    private String filename;
 699    private String defaultFilename;
 700    private View view;
 701    private boolean floating;
 702
 703    private QuickNotepadTextArea textArea;
 704    private QuickNotepadToolPanel toolPanel;
 705
 706    //
 707    // Constructor
 708    //
 709
 710    public QuickNotepad(View view, String position)
 711    {
 712        super(new BorderLayout());
 713
 714        this.view = view;
 715        this.floating = position.equals(
 716            DockableWindowManager.FLOATING);
 717
 718        this.filename = jEdit.getProperty(
 719            QuickNotepadPlugin.OPTION_PREFIX
 720            + "filepath");
 721        if(this.filename == null || this.filename.length() == 0)
 722        {
 723            this.filename = new String(jEdit.getSettingsDirectory()
 724                + File.separator + "qn.txt");
 725            jEdit.setProperty(QuickNotepadPlugin.OPTION_PREFIX
 726                + "filepath",this.filename);
 727        }
 728        this.defaultFilename = new String(this.filename);
 729
 730        this.toolPanel = new QuickNotepadToolPanel(this);
 731        add(BorderLayout.NORTH, this.toolPanel);
 732
 733        if(floating)
 734            this.setPreferredSize(new Dimension(500, 250));
 735
 736        textArea = new QuickNotepadTextArea();
 737        textArea.setFont(QuickNotepadOptionPane.makeFont());
 738        textArea.addKeyListener(new KeyHandler());
 739        textArea.addAncestorListener(new AncestorHandler());
 740        JScrollPane pane = new JScrollPane(textArea);
 741        add(BorderLayout.CENTER, pane);
 742
 743        readFile();
 744    }
 745
 746    //
 747    // Attribute methods
 748    //
 749
 750    // for toolBar display
 751    public String getFilename()
 752    {
 753        return filename;
 754    }
 755
 756    //
 757    // EBComponent implementation
 758    //
 759
 760    public void handleMessage(EBMessage message)
 761    {
 762        if (message instanceof PropertiesChanged)
 763        {
 764            propertiesChanged();
 765        }
 766    }
 767
 768
 769    private void propertiesChanged()
 770    {
 771        String propertyFilename = jEdit.getProperty(
 772            QuickNotepadPlugin.OPTION_PREFIX + "filepath");
 773        if(!defaultFilename.equals(propertyFilename))
 774        {
 775            saveFile();
 776            toolPanel.propertiesChanged();
 777            defaultFilename = propertyFilename.clone();
 778            filename = defaultFilename.clone();
 779            readFile();
 780        }
 781        Font newFont = QuickNotepadOptionPane.makeFont();
 782        if(!newFont.equals(textArea.getFont()))
 783        {
 784            textArea.setFont(newFont);
 785            textArea.invalidate();
 786        }
 787    }
 788
 789    // These JComponent methods provide the appropriate points
 790    // to subscribe and unsubscribe this object to the EditBus
 791
 792    public void addNotify()
 793    {
 794        super.addNotify();
 795        EditBus.addToBus(this);
 796    }
 797
 798
 799    public void removeNotify()
 800    {
 801        saveFile();
 802        super.removeNotify();
 803        EditBus.removeFromBus(this);
 804    }
 805
 806    ...
 807
 808}</programlisting></informalexample>
 809
 810<para>
 811  This listing refers to a <classname>QuickNotebookTextArea</classname>
 812  object. It is currently implemented as a <classname>JTextArea</classname> with
 813  word wrap and tab sizes hard-coded.  Placing the object in a separate
 814  class will simply future modifications.
 815</para>
 816
 817</sect1>
 818
 819<sect1 id="plugin-implement-quicknotepadtoolbar"><title>The QuickNotepadToolBar Class</title>
 820
 821<para>
 822  There is nothing remarkable about the toolbar panel that is placed
 823  inside the <classname>QuickNotepad</classname> object. The constructor
 824  shows the continued use of items from the plugin's properties file.
 825</para>
 826
 827<informalexample><programlisting>public class QuickNotepadToolPanel extends JPanel
 828{
 829    private QuickNotepad pad;
 830    private JLabel label;
 831
 832    public QuickNotepadToolPanel(QuickNotepad qnpad)
 833    {
 834        pad = qnpad;
 835        JToolBar toolBar = new JToolBar();
 836        toolBar.setFloatable(false);
 837
 838        toolBar.add(makeCustomButton("quicknotepad.choose-file",
 839            new ActionListener() {
 840                public void actionPerformed(ActionEvent evt) {
 841                    QuickNotepadToolPanel.this.pad.chooseFile();
 842                }
 843            }));
 844        toolBar.add(makeCustomButton("quicknotepad.save-file",
 845            new ActionListener() {
 846                public void actionPerformed(ActionEvent evt) {
 847                    QuickNotepadToolPanel.this.pad.saveFile();
 848                }
 849            }));
 850        toolBar.add(makeCustomButton("quicknotepad.copy-to-buffer",
 851            new ActionListener() {
 852                public void actionPerformed(ActionEvent evt) {
 853                    QuickNotepadToolPanel.this.pad.copyToBuffer();
 854                }
 855            }));
 856        label = new JLabel(pad.getFilename(),
 857            SwingConstants.RIGHT);
 858        label.setForeground(Color.black);
 859        label.setVisible(jEdit.getProperty(
 860            QuickNotepadPlugin.OPTION_PREFIX
 861            + "show-filepath").equals("true"));
 862        this.setLayout(new BorderLayout(10, 0));
 863        this.add(BorderLayout.WEST, toolBar);
 864        this.add(BorderLayout.CENTER, label);
 865        this.setBorder(BorderFactory.createEmptyBorder(0, 0, 3, 10));
 866    }
 867
 868    ...
 869
 870}</programlisting></informalexample>
 871
 872<para>
 873  The method <classname>makeCustomButton()</classname> provides uniform
 874  attributes for the three toolbar buttons corresponding to three of the
 875  plugin's use actions.  The menu titles for the user actions serve double
 876  duty as tooltip text for the buttons. There is also a
 877  <function>propertiesChanged()</function> method for the toolbar that
 878  sets the text and visibility of the label containing the notepad file path.
 879</para>
 880
 881</sect1>
 882
 883<sect1 id="plugin-implement-options"><title>The QuickNotepadOptionPane Class</title>
 884
 885<para>
 886  Using the default implementation provided by
 887  <classname>AbstractOptionPane</classname> reduces the preparation of an
 888  option pane to two principal tasks: writing a
 889  <function>_init()</function> method to layout and initialize the pane,
 890  and writing a <function>_save()</function> method to commit any settings
 891  changed by user input. If a button on the option pane should trigger
 892  another dialog, such as a <classname>JFileChooser</classname> or jEdit's
 893  own enhanced <classname>VFSFileChooserDialog</classname>, the option
 894  pane will also have to implement the
 895  <classname>ActionListener</classname> interface to display additional
 896  components.
 897</para>
 898
 899<para>
 900  The QuickNotepad plugin has only three options to set: the path name of
 901  the file that will store the notepad text, the visibility of the
 902  path name on the tool bar, and the notepad's display font.
 903  Using the shortcut methods of the plugin API, the implementation of
 904  <function>_init()</function> looks like this:
 905</para>
 906
 907<informalexample><programlisting>public class QuickNotepadOptionPane extends AbstractOptionPane
 908      implements ActionListener
 909{
 910    private JTextField pathName;
 911    private JButton pickPath;
 912    private FontSelector font;
 913
 914    ...
 915
 916    public void _init()
 917    {
 918        showPath = new JCheckBox(jEdit.getProperty(
 919            QuickNotepadPlugin.OPTION_PREFIX
 920            + "show-filepath.title"),
 921        jEdit.getProperty(
 922            QuickNotepadPlugin.OPTION_PREFIX +  "show-filepath")
 923            .equals("true"));
 924        addComponent(showPath);
 925
 926        pathName = new JTextField(jEdit.getProperty(
 927            QuickNotepadPlugin.OPTION_PREFIX
 928            + "filepath"));
 929        JButton pickPath = new JButton(jEdit.getProperty(
 930            QuickNotepadPlugin.OPTION_PREFIX
 931            + "choose-file"));
 932        pickPath.addActionListener(this);
 933
 934        JPanel pathPanel = new JPanel(new BorderLayout(0, 0));
 935        pathPanel.add(pathName, BorderLayout.CENTER);
 936        pathPanel.add(pickPath, BorderLayout.EAST);
 937
 938        addComponent(jEdit.getProperty(
 939            QuickNotepadPlugin.OPTION_PREFIX + "file"),
 940            pathPanel);
 941
 942        font = new FontSelector(makeFont());
 943        addComponent(jEdit.getProperty(
 944            QuickNotepadPlugin.OPTION_PREFIX + "choose-font"),
 945            font);
 946    }
 947
 948    ...
 949
 950}</programlisting></informalexample>
 951
 952<para>
 953  Here we adopt the vertical arrangement offered by use of the
 954  <function>addComponent()</function> method with one embellishment.
 955  We want the first <quote>row</quote> of the option pane to contain
 956  a text field with the current notepad file path and a button that will
 957  trigger a file chooser dialog when pressed.  To place both of them on
 958  the same line (along with an identifying label for the file option),
 959  we create a <classname>JPanel</classname> to contain both components and
 960  pass the configured panel to <function>addComponent()</function>.
 961</para>
 962
 963<para>
 964  The <function>_init()</function> method uses properties from the plugin's
 965  property file to provide the names of label for the components placed
 966  in the option pane.  It also uses a property whose name begins with
 967  <function>PROPERTY_PREFIX</function> as a persistent data item - the
 968  path of the current notepad file.  The elements of the notepad's font
 969  are also extracted from properties using a static method of the option
 970  pane class.
 971</para>
 972
 973<para>
 974  The <function>_save()</function> method extracts data from the user
 975  input components and
 976  assigns them to the plugin's properties.  The implementation is
 977  straightforward:
 978</para>
 979
 980<informalexample><programlisting>public void _save()
 981{
 982    jEdit.setProperty(QuickNotepadPlugin.OPTION_PREFIX
 983        + "filepath", pathName.getText());
 984    Font _font = font.getFont();
 985
 986    jEdit.setProperty(QuickNotepadPlugin.OPTION_PREFIX
 987        + "font", _font.getFamily());
 988    jEdit.setProperty(QuickNotepadPlugin.OPTION_PREFIX
 989        + "fontsize", String.valueOf(_font.getSize()));
 990    jEdit.setProperty(QuickNotepadPlugin.OPTION_PREFIX
 991        + "fontstyle", String.valueOf(_font.getStyle()));
 992    jEdit.setProperty(QuickNotepadPlugin.OPTION_PREFIX
 993        + "show-filepath", String.valueOf(showPath.isSelected()));
 994}</programlisting></informalexample>
 995
 996<para>
 997  The class has only two other methods, one to display a file chooser
 998  dialog in response to user action, and the other
 999  to construct a <classname>Font</classname> object from the plugin's font
1000  properties. They do not require discussion here.
1001</para>
1002
1003</sect1>
1004
1005<sect1 id="plugin-implement-docs"><title>Plugin Documentation</title>
1006
1007<para>
1008  While not required by the plugin API, a help file is an essential
1009  element of any plugin written for public release. A single web page is
1010  often all that is required. There are no specific requirements on
1011  layout, but because of the design of jEdit's help viewer, the use of
1012  frames should be avoided. Topics that would be useful include
1013  the following:
1014</para>
1015
1016<itemizedlist>
1017  <listitem>
1018    <para>
1019      a description of the purpose of the plugin;
1020    </para>
1021  </listitem>
1022  <listitem>
1023    <para>
1024      an explanation of the type of input the user can supply through its
1025      visible interface (such as mouse action or text entry in controls);
1026    </para>
1027  </listitem>
1028  <listitem>
1029    <para>
1030      a listing of available user actions that can be taken when the
1031      plugin does not have input focus;
1032    </para>
1033  </listitem>
1034  <listitem>
1035    <para>
1036      a summary of configuration options;
1037    </para>
1038  </listitem>
1039  <listitem>
1040    <para>
1041      information on development of the plugin (such as a change log,
1042      a list of <quote>to do</quote> items, and contact information for
1043      the plugin's author); and
1044    </para>
1045  </listitem>
1046  <listitem>
1047    <para>
1048      licensing information, including acknowledgments for any library
1049      software used by the plugin.
1050    </para>
1051  </listitem>
1052</itemizedlist>
1053
1054<para>
1055 The location of the plugin's help file is stored in the
1056 <literal>plugin.QuickNotepad.docs</literal>
1057 property; see <xref linkend="plugin-implement-properties"/>.
1058</para>
1059
1060</sect1>
1061
1062<sect1 id="plugin-implement-building"><title>Compiling the Plugin</title>
1063
1064<para>
1065  We have already outlined the contents of the user action catalog, the
1066  properties file and the documentation file in our earlier discussion.
1067  The final step is to compile the source file and build the archive file
1068  that will hold the class files and the plugin's other resources.
1069</para>
1070
1071<para>
1072  Publicly released plugins include with their source a makefile
1073  in XML format for the
1074  <application>Ant</application> utility.  The format for this file
1075  requires few changes from plugin to plugin.  Here is the version of
1076  <filename>build.xml</filename> used by QuickNotepad and many other
1077  plugins:
1078</para>
1079
1080<informalexample><programlisting><![CDATA[<project name="QuickNotepad" default="dist" basedir=".">
1081
1082  <property name="jedit.install.dir"  value="../.."/>
1083  <property name="jar.name"  value="QuickNotepad.jar"/>
1084
1085  <property name="install.dir"  value=".."/>
1086
1087
1088  <path id="project.class.path">
1089    <pathelement location="${jedit.install.dir}/jedit.jar"/>
1090    <pathelement location="."/>
1091  </path>
1092
1093
1094  <target name="compile">
1095    <javac
1096      srcdir="."
1097      deprecation="on"
1098      includeJavaRuntime="yes"
1099    >
1100      <classpath refid="project.class.path"/>
1101    </javac>
1102  </target>
1103
1104
1105  <target name="dist" depends="compile">
1106    <mkdir dir="${install.dir}"/>
1107    <jar jarfile="${install.dir}/${jar.name}">
1108      <fileset dir=".">
1109        <include name="**/*.class"/>
1110        <include name="**/*.props"/>
1111        <include name="**/*.html"/>
1112        <include name="actions.xml"/>
1113        <include name="dockables.xml"/>
1114      </fileset>
1115    </jar>
1116  </target>
1117</project>]]></programlisting></informalexample>
1118
1119<para>
1120  For a full discussion of the <filename>Ant</filename> file format and
1121  command syntax, you should consult the <ulink
1122  url="http://jakarta.apache.org/ant/manual/index.html">Ant
1123  documentation site</ulink>. Modifying this makefile for a different
1124  plugin will likely only require three changes:
1125</para>
1126
1127<itemizedlist>
1128  <listitem><para>
1129    the name of the plugin;
1130  </para></listitem>
1131  <listitem><para>
1132    the choice of compiler (made by inserting and deleting the comment character
1133    <userinput>'#'</userinput>); and
1134  </para> </listitem>
1135  <listitem><para>
1136    the classpath variables for <filename>jedit.jar</filename>
1137    any plugins this one depends on.
1138  </para></listitem>
1139</itemizedlist>
1140
1141</sect1>
1142
1143<sect1 id="plugin-implement-reloading"><title>Reloading the Plugin</title>
1144
1145<para>
1146  Once you have compiled your plugin using the 4.2 API you will need to reload
1147  it to test it. Follow these steps to reload your plugin without restarting jEdit: 
1148</para>
1149
1150<itemizedlist>
1151  <listitem><para>
1152   From the Plugins menu open the Plugin Manager.
1153  </para></listitem>
1154  <listitem><para>
1155    On the Manage tab uncheck Hide libraries. This will 
1156    allow you to see plugins that are not loaded.
1157  </para></listitem>
1158  <listitem><para>
1159    Find the plugin on the Manage tab and uncheck it. This will unload the plugin.
1160    You will get a warning if this plugin does not support dynamic reloading. 
1161    If you get that warning you will need to restart jEdit to reload the plugin 
1162    until the plugin is converted over to the 4.2 API. 
1163  </para></listitem>
1164  <listitem><para>
1165    Recheck the plugin to reload it.
1166  </para></listitem>
1167</itemizedlist>
1168
1169
1170<para>
1171  The jEdit web site contains a macro and an Ant task that can be used as an alternative 
1172  method for dynamically reloading plugins. 
1173</para>
1174
1175
1176
1177<para>
1178  If you have reached this point in the text, you are probably serious
1179  about writing a plugin for jEdit.  Good luck with your efforts, and
1180  thank you for contributing to the jEdit project.
1181</para>
1182
1183</sect1>
1184
1185</chapter>