PageRenderTime 18ms CodeModel.GetById 3ms app.highlight 5ms RepoModel.GetById 1ms app.codeStats 1ms

/jEdit/tags/jedit-4-5-pre1/doc/users-guide/plugin-implement.xml

#
XML | 1250 lines | 1027 code | 207 blank | 16 comment | 0 complexity | 21f5d6d6a09114153c2b06f452e1a5b7 MD5 | raw file

Large files files are truncated, but you can click here to view the full file

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

Large files files are truncated, but you can click here to view the full file