/jEdit/tags/jedit-4-1-pre5/doc/users-guide/host-design.xml

# · XML · 1009 lines · 875 code · 116 blank · 18 comment · 0 complexity · 6fd63ab2b1e8fb10099c9736bf764960 MD5 · raw file

  1. <!-- jEdit 4.0 Plugin Guide, (C) 2001, 2002 John Gellene -->
  2. <!-- jEdit buffer-local properties: -->
  3. <!-- :indentSize=1:tabSize=2:noTabs=true:maxLineLen=72: -->
  4. <!-- This file contains a discussion of the host architecture -->
  5. <!-- in the jEdit Plugin API -->
  6. <!-- $Id: host-design.xml 4174 2002-05-19 08:31:14Z spestov $
  7. -->
  8. <chapter id="host-design"><title>jEdit as a Plugin Host</title>
  9. <para>
  10. A good way to start learning what a plugin requires is to look at what
  11. the host application does with one. We start our discussion of
  12. plugins by outlining how jEdit loads and displays them. This section
  13. only provides a broad overview of the more important components that
  14. make up jEdit; specifics of the API will be documented in
  15. subsequent chapters.
  16. </para>
  17. <sect1 id="host-design-load"><title>
  18. <indexterm>
  19. <primary>Plugin API</primary>
  20. <secondary>loading at startup</secondary>
  21. </indexterm>
  22. Loading Plugins</title>
  23. <para>
  24. As part of its startup routine, jEdit's <function>main</function>
  25. method calls various methods to load and initialize plugins.
  26. This occurs after the application has done the following:
  27. </para>
  28. <itemizedlist>
  29. <listitem><para>
  30. parsed command line options;
  31. </para></listitem>
  32. <listitem><para>
  33. started the edit server (unless instructed not to do so
  34. by a command line option) and;
  35. </para></listitem>
  36. <listitem><para>
  37. loaded application properties, any user-supplied properties, and the
  38. application's set of actions that will be available from jEdit's menu
  39. bar (as well as the toolbar and keyboard shortcuts);
  40. </para></listitem>
  41. </itemizedlist>
  42. <para>
  43. Plugin loading occurs before jEdit creates any windows or loads any files
  44. for editing. It also occurs before jEdit runs any startup scripts.
  45. </para>
  46. <sect2 id="host-design-loader"><title>
  47. <indexterm>
  48. <primary>JAR class loader</primary>
  49. </indexterm>
  50. The JARClassLoader</title>
  51. <para>
  52. Plugins are loaded from files with the <filename>.jar</filename>
  53. filename extension located in the <filename>jars</filename>
  54. subdirectories of the jEdit installation and user settings directories
  55. (see <xref linkend="settings-directory" />).
  56. </para>
  57. <para>
  58. For each JAR archive file it finds, jEdit creates an instance of the
  59. <classname>JARClassLoader</classname> class. This is a jEdit-specific
  60. class
  61. that implements the Java platform's abstract class
  62. <classname>ClassLoader</classname>. The constructor for the
  63. <classname>JARClassLoader</classname> object does the following:
  64. <itemizedlist>
  65. <listitem><para>
  66. Reads action definitions from any file named
  67. <filename>actions.xml</filename> in the archive (the file need
  68. not be at the top level). See <xref
  69. linkend="resources-action" />.
  70. </para></listitem>
  71. <listitem><para>
  72. Parses and loads the contents of any file named
  73. <filename>dockables.xml</filename> in the archive (the file need
  74. not be at the top level). This file contains BeanShell code for
  75. creating docking or floating windows that will contain the visible
  76. components of the plugin. Not all plugins define dockable
  77. windows,
  78. but those that do need a <filename>dockables.xml</filename> file.
  79. See <xref linkend="resources-dockables" />.
  80. </para></listitem>
  81. <listitem><para>
  82. Loads any properties defined in files ending with
  83. the extension <filename>.props</filename> that are contained
  84. in the archive. See <xref linkend="resources-properties" />.
  85. </para></listitem>
  86. <listitem><para>
  87. Adds any class file with a name ending with
  88. <filename>Plugin.class</filename> to an internal collection of
  89. plugin class names maintained by the
  90. <classname>JARClassLoader</classname>. See <xref
  91. linkend="class-editplugin" />.
  92. </para></listitem>
  93. <listitem><para>
  94. Adds to a collection maintained by jEdit a new object of
  95. type <classname>EditPlugin.JAR</classname>. This is a data structure
  96. holding the name of the JAR archive file, a reference to the
  97. <classname>JARClassLoader</classname> and a collection,
  98. initially empty, of plugins found in the archive file.
  99. </para></listitem>
  100. </itemizedlist>
  101. </para>
  102. <para>
  103. Once all JAR files have been examined for the above resources,
  104. jEdit initializes all class files whose names end in
  105. <filename>Plugin.class</filename>, as identified in the first pass
  106. through the JAR archive. We will call these classes
  107. <indexterm>
  108. <primary>Plugin API</primary>
  109. <secondary>plugin core class</secondary>
  110. </indexterm>
  111. <firstterm>plugin core classes</firstterm>. They provide
  112. the principal point of contact between jEdit and the plugin. A plugin
  113. core class must extend jEdit's abstract <classname>EditPlugin</classname>
  114. class. Use of a class name ending in <classname>Plugin</classname> is also
  115. required.
  116. </para>
  117. <para>
  118. <indexterm>
  119. <primary>Plugins</primary>
  120. <secondary>dependencies</secondary>
  121. </indexterm>
  122. For each plugin core class, the initialization routine first checks the plugin's
  123. properties to see if it is subject to any dependencies. For example, a
  124. plugin may require that the version of the Java runtime environment or
  125. of jEdit itself be equal to or above some threshold version. A plugin
  126. can also require the presence of another plugin or a particular class
  127. from another archive. If any dependency is
  128. not satisfied, the loader marks the plugin as <quote>broken</quote> and
  129. logs an error message.
  130. </para>
  131. <para>
  132. If all dependencies are satisfied, a new instance
  133. of the plugin core class is created and added to the collection
  134. maintained by the appropriate <classname>EditPlugin.JAR</classname>
  135. object. By accessing that object, jEdit can keep track of plugins it has
  136. successfully loaded, and call methods or perform routines on them.
  137. </para>
  138. <sidebar><title>
  139. <indexterm>
  140. <primary>Plugin API</primary>
  141. <secondary>using class libraries</secondary>
  142. </indexterm>
  143. Additional class libraries for plugins</title>
  144. <para>
  145. JAR files with no plugin core classes are also loaded by jEdit; no special
  146. initialization is performed on them, and the classes they contain are
  147. made available to other plugins. For example, many plugins that rely on
  148. third-party class libraries ship them as separate JAR archives. The libraries
  149. will be available inside the jEdit environment but are not part of a general
  150. classpath or library collection when running other Java applications.
  151. </para>
  152. <para>
  153. A plugin that bundles extra JAR archives must define a property that
  154. lists these JAR files in order for the plugin manager to be able to
  155. remove the plugin completely. See <xref
  156. linkend="resources-properties" />.
  157. </para>
  158. </sidebar>
  159. </sect2>
  160. <sect2 id="host-design-plugin-start"><title>Starting the Plugin</title>
  161. <para>
  162. After creating and storing the plugin core object, jEdit calls the
  163. <function>start()</function> method of the plugin core class.
  164. The <function>start()</function> method can perform initialization of the
  165. object's data members.
  166. Because this method is defined as an empty <quote>no-op</quote> in the
  167. <classname>EditPlugin</classname> abstract class, a plugin need not
  168. provide an implementation if no unique initialization is required.
  169. </para>
  170. </sect2>
  171. <sect2 id="class-editplugin"><title>The EditPlugin Class</title>
  172. <para>
  173. Recall that this abstract class is the base for every plugin core
  174. class. Its methods
  175. provide for basic interaction between the plugin and jEdit. The class
  176. has four methods which are called by jEdit at various times. None of
  177. these methods are required to be implemented, but most plugins will
  178. override at least one.
  179. </para>
  180. <itemizedlist>
  181. <listitem>
  182. <funcsynopsis>
  183. <funcprototype>
  184. <funcdef>public void <function>start</function></funcdef>
  185. <paramdef></paramdef>
  186. </funcprototype>
  187. </funcsynopsis>
  188. <para>
  189. The jEdit startup routine calls this method for each loaded
  190. plugin. Plugins typically use this method to register information
  191. with the EditBus and perform other initialization.
  192. </para>
  193. </listitem>
  194. <listitem>
  195. <funcsynopsis>
  196. <funcprototype>
  197. <funcdef>public void <function>stop</function></funcdef>
  198. <paramdef></paramdef>
  199. </funcprototype>
  200. </funcsynopsis>
  201. <para>
  202. When jEdit is exiting, it calls this method on each plugin. If a
  203. plugin uses or creates state information or other persistent data
  204. that should be stored in a special format, this would be a good place to write
  205. the data to storage. If you use jEdit's properties API to hold
  206. <quote>key-value</quote> type settings for your plugins, no special
  207. processing is needed for them, since jEdit loads application
  208. properties automatically at startup and writes them to the
  209. <filename>properties</filename> file in the user's settings directory
  210. when the application exits. Most plugins find this approach sufficient for
  211. saving settings.
  212. </para>
  213. </listitem>
  214. <listitem>
  215. <funcsynopsis>
  216. <funcprototype>
  217. <funcdef>public void <function>createMenuItems</function></funcdef>
  218. <paramdef>Vector <parameter>menuItems</parameter></paramdef>
  219. </funcprototype>
  220. </funcsynopsis>
  221. <para>
  222. When a <classname>View</classname> object is created, it calls this
  223. method on each plugin core class to obtain entries to be displayed
  224. in the view's <guimenu>Plugins</guimenu> menu. The
  225. <parameter>menuItems</parameter> parameter is a
  226. <classname>Vector</classname> that accumulates menu items and
  227. menus as it is passed from plugin to plugin.
  228. </para>
  229. <para>
  230. While jEdit does not require a plugin to supply menu items, a plugin's
  231. usefulness would be extremely limited without them. The easiest way to
  232. provide menu items is to
  233. package them as entries in the plugin's property
  234. file and implement <function>createMenuItems()</function> with a
  235. call to jEdit's <function>GUIUtilities.loadMenu()</function>
  236. method. The following code illustrates this approach:
  237. </para>
  238. <informalexample><programlisting>public void createMenuItems(Vector menuItems)
  239. {
  240. menuItems.addElement(GUIUtilities.loadMenu(
  241. "myplugin.menu"));
  242. }</programlisting></informalexample>
  243. <para>
  244. The parameter passed to <function>loadMenu()</function> is
  245. the name of a property defined in the plugin's own property file that
  246. contains menu data. The form of the property entry is a list of labels
  247. that in turn correspond to other property names and ultimately to
  248. the actual text for menu items as well as implementation code. We will
  249. detail the format of the menu data in <xref linkend="plugin-implement-menu"/>
  250. </para>
  251. <para>
  252. The <function>GUIUtilities.loadMenuItem()</function> method is also
  253. available for plugins that only wish to add a single menu item to
  254. the <guimenu>Plugins</guimenu> menu. The parameter names a property
  255. that points to label text in the plugin's properties file and
  256. implementing code in the plugin's <filename>actions.xml</filename>
  257. file.
  258. </para>
  259. </listitem>
  260. <listitem>
  261. <funcsynopsis>
  262. <funcprototype>
  263. <funcdef>public void <function>createOptionPanes</function></funcdef>
  264. <paramdef>OptionsDialog <parameter>dialog</parameter></paramdef>
  265. </funcprototype>
  266. </funcsynopsis>
  267. <para>
  268. This method is called for each plugin during the creation of
  269. the <guilabel>Global Options</guilabel> dialog box.
  270. To show an option pane, the plugin should define an
  271. option pane class and implement <function>createOptionPane()</function>
  272. as follows:
  273. </para>
  274. <informalexample><programlisting>dialog.addOptionPane(new MyPluginOptionPane());</programlisting></informalexample>
  275. <para>
  276. Plugins can also define more than one option pane, grouped in an
  277. <quote>option group</quote>.
  278. <!-- We will discuss the design and elements of the option pane API
  279. in <xref linkend="api-option-classes"/>. -->
  280. </para>
  281. </listitem>
  282. </itemizedlist>
  283. <para>
  284. This class defines two other methods which may be useful to some
  285. plugins or for debugging purposes. They are fully implemented
  286. in the parent class and used mainly by jEdit's core code.
  287. </para>
  288. <itemizedlist>
  289. <listitem>
  290. <funcsynopsis>
  291. <funcprototype>
  292. <funcdef>public String <function>getClassName</function></funcdef>
  293. <void/>
  294. </funcprototype>
  295. </funcsynopsis>
  296. <para>
  297. This shortcut method returns <function>getClass().getName()</function>.
  298. </para>
  299. </listitem>
  300. <listitem>
  301. <funcsynopsis>
  302. <funcprototype>
  303. <funcdef>public EditPlugin.JAR <function>getJAR</function></funcdef>
  304. <void/>
  305. </funcprototype>
  306. </funcsynopsis>
  307. <para>
  308. This method returns the <classname>EditPlugin.JAR</classname> data
  309. object associated with the plugin.
  310. </para>
  311. </listitem>
  312. </itemizedlist>
  313. </sect2>
  314. </sect1>
  315. <sect1 id="host-design-resources"><title>Plugin Resources</title>
  316. <sect2 id="resources-properties"><title>Plugin Properties</title>
  317. <para>
  318. jEdit maintains a list of <quote>properties</quote>, which are
  319. name/value pairs used to store human-readable strings, user settings,
  320. and various other forms of meta-data. During startup, jEdit loads the
  321. default set of properties, followed by plugin properties stored in
  322. plugin JAR files, finally followed by user properties. Plugins can
  323. access properties from all three sources.
  324. </para>
  325. <para>
  326. Property files contained in plugin JARs must end with the filename
  327. extension <filename>.props</filename>, and have a very simple syntax,
  328. which the following example illustrates:
  329. </para>
  330. <informalexample><programlisting># Lines starting with '#' are ignored.
  331. name=value
  332. another.name=another value
  333. long.property=Long property value, split over \
  334. several lines
  335. escape.property=Newlines and tabs can be inserted \
  336. using the \t and \n escapes
  337. backslash.property=A backslash can be inserted by writing \\.</programlisting>
  338. </informalexample>
  339. <para>
  340. The following types of plugin information
  341. are supplied using properties:
  342. </para>
  343. <itemizedlist>
  344. <listitem>
  345. <para>
  346. Information regarding the name, author, and version of the plugin.
  347. This information is required. Here is an example:
  348. </para>
  349. <informalexample><programlisting>plugin.MyPlugin.name=My Plugin
  350. plugin.MyPlugin.author=Jay Edit
  351. plugin.MyPlugin.version=1.0.3</programlisting></informalexample>
  352. <para>
  353. Note that each property is prefixed with
  354. <literal>plugin.</literal>, followed by the fully qualified name
  355. of the plugin core class (including a package name, if there is
  356. one).
  357. </para>
  358. </listitem>
  359. <listitem>
  360. <para>
  361. Identification of any dependencies the plugin may have on a
  362. particular version of a Java runtime environment, the jEdit
  363. application, or other plugins.
  364. </para>
  365. <para>
  366. Each dependency is defined in a property prefixed with
  367. <literal>plugin.<replaceable>class name</replaceable>.depend.</literal>,
  368. followed by a number. Dependencies must be numbered in order,
  369. starting from zero.
  370. </para>
  371. <para>
  372. The value of a dependency property is one of the words
  373. <literal>jdk</literal>, <literal>jedit</literal>,
  374. or <literal>plugin</literal>,
  375. followed by a Java version number, a jEdit build number,
  376. or plugin class name and plugin version number,
  377. respectively.
  378. </para>
  379. <para>
  380. Here are some examples:
  381. </para>
  382. <informalexample><programlisting>plugin.MyPlugin.depend.0=jdk 1.4
  383. plugin.MyPlugin.depend.1=jedit 04.00.99.00
  384. plugin.MyPlugin.depend.2=plugin console.ConsolePlugin 3.2.1</programlisting>
  385. </informalexample>
  386. </listitem>
  387. <listitem>
  388. <para>
  389. A list of external class library JARs shipped with the plugin.
  390. If your plugin bundles extra JARs, this property is required
  391. for the plugin manager to be able to remove the plugin completely.
  392. </para>
  393. <para>
  394. The property is a space-separated list of filenames. Here is an
  395. example:
  396. </para>
  397. <informalexample><programlisting>plugin.xslt.XSLTPlugin.jars=xml-apis.jar xalan.jar</programlisting></informalexample>
  398. </listitem>
  399. <listitem>
  400. <para>
  401. Labels for user actions for inclusion in menus and option panes
  402. relating to toolbars and keyboard shortcuts.
  403. </para>
  404. <para>
  405. Action labels are defined in properties named by the
  406. action's internal name as specified in the action catalog,
  407. followed by <literal>.label</literal>:
  408. </para>
  409. <informalexample><programlisting>myplugin.label=My Plugin
  410. myplugin-grok.label=Grok Current Buffer</programlisting>
  411. </informalexample>
  412. </listitem>
  413. <listitem>
  414. <para>
  415. The list of menu items contained in plugin menus, if any.
  416. </para>
  417. <para>
  418. This is discussed in detail in <xref
  419. linkend="plugin-implement-menu" />.
  420. </para>
  421. </listitem>
  422. <listitem>
  423. <para>
  424. Labels and other information regarding the controls contained in
  425. the plugin's windows. These properties can be named any way you
  426. like, however take care not to choose names which may conflict
  427. with those in other plugins.
  428. </para>
  429. </listitem>
  430. <listitem>
  431. <para>
  432. The titles of dockable windows, as displayed in a tabbed or
  433. floating container.
  434. </para>
  435. <para>
  436. These labels are specified in properties named by the dockable
  437. window's identifier (as specified in the <filename>dockables.xml</filename>
  438. file, see <xref linkend="resources-dockables"/>),
  439. suffixed with <literal>.title</literal>. For example:
  440. </para>
  441. <informalexample><programlisting>quick-notepad.title=QuickNotepad</programlisting>
  442. </informalexample>
  443. </listitem>
  444. </itemizedlist>
  445. </sect2>
  446. <sect2 id="resources-action"><title>The Action Catalog</title>
  447. <para>
  448. Actions define procedures that can be bound to a menu
  449. item, a toolbar button or a keyboard shortcut. They can perform any
  450. task encompassed in a public method of any class currently loaded in
  451. jEdit, including plugin classes and classes of the host application.
  452. Among other things, they can cause the appearance and disappearance of
  453. plugin windows.
  454. </para>
  455. <para>
  456. To manage user actions, jEdit maintains a lookup table of actions
  457. using descriptive strings as keys. The values in the table are
  458. sets of statements written in BeanShell, jEdit's macro scripting
  459. language. These scripts either direct the action themselves,
  460. delegate to a method in one of the plugin's classes that
  461. encapsulates the action, or do a little of both. The scripts are
  462. usually short; elaborate action protocols are usually contained in
  463. compiled code, rather than an interpreted macro script, to speed
  464. execution.
  465. </para>
  466. <para>
  467. Actions are defined by creating an XML file entitled
  468. <filename>actions.xml</filename> and placing it in the plugin JAR
  469. file. A sample action catalog looks like so:
  470. </para>
  471. <informalexample><programlisting><![CDATA[<!DOCTYPE ACTIONS SYSTEM "actions.dtd">
  472. <ACTIONS>
  473. <ACTION NAME="quicknotepad.choose-file">
  474. <CODE>
  475. view.getDockableWindowManager()
  476. .getDockable(QuickNotepadPlugin.NAME).chooseFile();
  477. </CODE>
  478. </ACTION>
  479. <ACTION NAME="quicknotepad.save-file">
  480. <CODE>
  481. view.getDockableWindowManager()
  482. .getDockable(QuickNotepadPlugin.NAME).saveFile();
  483. </CODE>
  484. </ACTION>
  485. <ACTION NAME="quicknotepad.copy-to-buffer">
  486. <CODE>
  487. view.getDockableWindowManager()
  488. .getDockable(QuickNotepadPlugin.NAME).copyToBuffer();
  489. </CODE>
  490. </ACTION>
  491. </ACTIONS>]]></programlisting></informalexample>
  492. <para>
  493. The defined elements have the following functions:
  494. </para>
  495. <itemizedlist>
  496. <listitem>
  497. <para>
  498. <varname>ACTIONS</varname> is the top-level element and refers
  499. to the set of actions used by the plugin.
  500. </para>
  501. </listitem>
  502. <listitem>
  503. <para>
  504. An <varname>ACTION</varname> contains the data for a particular action.
  505. It has three attributes: a required <varname>NAME</varname>;
  506. an optional <varname>NO_REPEAT</varname>, which is a flag
  507. indicating whether the action should not be repeated with the
  508. <keycombo><keycap>Control</keycap><keycap>Enter</keycap></keycombo>
  509. command (see <xref linkend="repeat" />); and an optional
  510. <varname>NO_RECORD</varname> which is a a flag indicating whether the
  511. action should be recorded if it is invoked while a user is recording a
  512. macro. The two flag attributes
  513. can have two possible values, <quote>TRUE</quote> or
  514. <quote>FALSE</quote>. In both cases, <quote>FALSE</quote> is the
  515. default if the attribute is not specified.
  516. </para>
  517. </listitem>
  518. <listitem>
  519. <para>
  520. An <varname>ACTION</varname> can have two child elements
  521. within it: a required <varname>CODE</varname> element which
  522. specifies the
  523. BeanShell code that will be executed when the action is invoked,
  524. and an optional <varname>IS_SELECTED</varname> element, used for
  525. checkbox
  526. menu items. The <varname>IS_SELECTED</varname> element contains
  527. BeanShell code that returns a boolean flag that will
  528. determine the state of the checkbox.
  529. </para>
  530. </listitem>
  531. </itemizedlist>
  532. <para>
  533. More discussion of the action catalog can be found in <xref
  534. linkend="plugin-implement-actions" />.
  535. </para>
  536. </sect2>
  537. <sect2 id="resources-dockables"><title>The Dockable Definition File</title>
  538. <para>
  539. The jEdit Plugin API uses BeanShell to create the top-level visible container
  540. of a plugin's interface. The BeanShell code is contained in a file named
  541. <filename>dockables.xml</filename>. It usually is quite short, providing only
  542. a single BeanShell expression used to create a visible plugin window.
  543. </para>
  544. <para>
  545. The following example from the QuickNotepad plugin illustrates the
  546. requirements of the data file:
  547. <informalexample><programlisting>&lt;?xml version="1.0"?&gt;
  548. &lt;!DOCTYPE DOCKABLES SYSTEM "dockables.dtd"&gt;
  549. &lt;!-- QuickNotepad dockable windows --&gt;
  550. &lt;DOCKABLES&gt;
  551. &lt;DOCKABLE NAME="quicknotepad"&gt;
  552. new QuickNotepad(view, position);
  553. &lt;/DOCKABLE&gt;
  554. &lt;/DOCKABLES&gt;</programlisting></informalexample>
  555. In this example, the <classname>&lt;DOCKABLE&gt;</classname> element has
  556. a single attribute, the dockable window's identifier. This attribute is
  557. used to key a property where the window title is stored; see
  558. <xref linkend="resources-properties"/>.
  559. <!-- If the plugin has no user actions,
  560. a second, implicit <classname>&lt;NO_ACTIONS&gt;</classname> attribute
  561. should be assigned a value of <constant>true</constant>. -->
  562. The contents of the <classname>&lt;DOCKABLE&gt;</classname> element itself is a
  563. BeanShell expression that constructs a new <classname>QuickNotepad</classname>
  564. object. The <varname>view</varname> and <varname>position</varname> are
  565. predefined by the Plugin API as the view in which the plugin window will reside
  566. and the docking position of the plugin.
  567. </para>
  568. </sect2>
  569. <sect2 id="resources-help"><title>Plugin Documentation</title>
  570. <para>
  571. While not required by the plugin API, a help file is an essential
  572. element of any plugin written for public release. A single web page is
  573. often all that is required. There are no specific requirements on
  574. layout, but because of the design of jEdit's help viewer, the use of
  575. frames should be avoided. Topics that would be useful include
  576. the following:
  577. </para>
  578. <itemizedlist>
  579. <listitem>
  580. <para>
  581. a description of the purpose of the plugin;
  582. </para>
  583. </listitem>
  584. <listitem>
  585. <para>
  586. an explanation of the type of input the user can supply through its
  587. visible interface (such as mouse action or text entry in controls);
  588. </para>
  589. </listitem>
  590. <listitem>
  591. <para>
  592. a listing of available user actions that can be taken when the
  593. plugin does not have input focus;
  594. </para>
  595. </listitem>
  596. <listitem>
  597. <para>
  598. a summary of configuration options;
  599. </para>
  600. </listitem>
  601. <listitem>
  602. <para>
  603. information on development of the plugin (such as a change log,
  604. a list of <quote>to do</quote> items, and contact information for
  605. the plugin's author); and
  606. </para>
  607. </listitem>
  608. <listitem>
  609. <para>
  610. licensing information, including acknowledgments for any library
  611. software used by the plugin.
  612. </para>
  613. </listitem>
  614. </itemizedlist>
  615. <para>
  616. The location of the plugin's help file should be stored in the
  617. <literal>plugin.<replaceable>class name</replaceable>.docs</literal>
  618. property.
  619. </para>
  620. </sect2>
  621. </sect1>
  622. <sect1 id="host-design-display"><title>The User Interface of a Plugin</title>
  623. <para>
  624. <!-- To display a user interface, plugins can either directly extend Java's
  625. <classname>JFrame</classname>, <classname>JDialog</classname>, or
  626. <classname>JWindow</classname> classes, or use jEdit's dockable window
  627. API. Plugin windows are typically defined in classes that are
  628. part of the plugin package but separate from the plugin core
  629. class. -->
  630. To display a user interface, plugins provide a top-level component
  631. derived (directly or indirectly) from the Swing
  632. <classname>JComponent</classname> class. This component will be
  633. embedded in a docking of floating window created by the Plugin API. It
  634. is typically defined in a class that is part of the plugin package but
  635. separate from the plugin core class (if one exists).
  636. </para>
  637. <sect2 id="host-display-view"><title>
  638. <indexterm>
  639. <primary>View</primary>
  640. <secondary>use in Plugin API</secondary>
  641. </indexterm>
  642. <indexterm>
  643. <primary>Plugin API</primary>
  644. <secondary>use of View object</secondary>
  645. </indexterm>
  646. The Role of the View Object</title>
  647. <para>
  648. A <classname>View</classname> is jEdit's top-level frame window.
  649. The largest component it contains is an edit pane that in turn contains a
  650. text area that displays a buffer. A view can have more than one edit pane
  651. in a split window configuration. The view also contains a menu bar,
  652. an optional toolbar and other window decorations, as well as docked
  653. windows.
  654. </para>
  655. <para>
  656. The <classname>View</classname> class performs two important operations
  657. dealing with plugins: creating plugin menu items, and managing dockable
  658. windows.
  659. </para>
  660. <para>
  661. <indexterm>
  662. <primary>Plugin API</primary>
  663. <secondary>menu creation</secondary>
  664. </indexterm>
  665. When a view is being created, its initialization routine
  666. iterates through the collection of loaded plugins and calls
  667. the <function>createMenuItems()</function> method of
  668. each plugin core class. In the parent class,
  669. <classname>EditPlugin</classname>, this method is an empty
  670. <quote>no-op</quote>. In order to add items to jEdit's menu bar
  671. under the <guimenu>Plugins</guimenu> menu, the plugin core class must
  672. supply its own version of <function>createMenuItems()</function>.
  673. As we will explain in <xref linkend="class-editplugin"/>,
  674. the typical plugin, instead of creating Java
  675. <classname>JMenu</classname> and <classname>JMenuItem</classname>
  676. objects directly, relies on a wrapper method in a utility class to
  677. create menu entries.
  678. </para>
  679. <para>
  680. The <classname>View</classname> also creates and initializes a
  681. <classname>DockableWindowManager</classname> object. This object is
  682. responsible for creating, closing and managing dockable windows.
  683. It will be discussed in more detail below.
  684. </para>
  685. <para>
  686. Finally, the <classname>View</classname>
  687. and <classname>DockableWindowManager</classname>
  688. classes contain a number of methods
  689. that can be called from plugins; see <xref linkend="class-view" />
  690. and <xref linkend="class-dockablewindowmanager" /> for details.
  691. </para>
  692. </sect2>
  693. <sect2 id="host-display-manager">
  694. <title>
  695. <indexterm>
  696. <primary>Plugin API</primary>
  697. <secondary>DockableWindowManager class</secondary>
  698. </indexterm>
  699. <indexterm>
  700. <primary>DockableWindowManager</primary>
  701. <secondary>use in Plugin API</secondary>
  702. </indexterm>
  703. <indexterm>
  704. <primary>Plugin API</primary>
  705. <secondary>creating dockable windows</secondary>
  706. </indexterm>
  707. The DockableWindowManager</title>
  708. <para>
  709. The <classname>DockableWindowManager</classname> in each
  710. <classname>View</classname> object keeps track of docked
  711. and floating windows. When the <classname>View</classname> object
  712. initializes its <classname>DockableWindowManager</classname>, the
  713. manager iterates through the list of registered dockable windows and
  714. examines options supplied by the user in the <guilabel>Global
  715. Options</guilabel> dialog box. It displays any plugins that the user
  716. designated for one of the four docking positions when the corresponding
  717. button a docking pane is selected.
  718. </para>
  719. <para>
  720. To create an instance of a dockable window, the
  721. <classname>DockableWindowManager</classname> finds and executes the
  722. BeanShell code extracted from the plugin's
  723. <filename>dockables.xml</filename> file during application startup. This
  724. code will typically consist of a call to the constructor of the docking
  725. window component that passes two parameters: the
  726. <classname>View</classname> associated with the docking window
  727. component, and a <classname>String</classname> representing the component's
  728. docking or floating position. The result of the BeanShell expression,
  729. typically a newly constructed component, is placed inside the
  730. docking or floating window managed by the
  731. <classname>DockableWindowManager</classname>.
  732. </para>
  733. <para>
  734. Eventually the <classname>DockableWindowManager</classname> destroys
  735. the plugin window, whether docking or floating, in response to user
  736. action or as part of the destruction of the corresponding
  737. <classname>View</classname> object.
  738. </para>
  739. <para>
  740. With this broad outline of how jEdit behaves as a plugin host in the
  741. background, we will next review the programming elements that make up
  742. a plugin.
  743. </para>
  744. </sect2>
  745. </sect1>
  746. <sect1 id="host-design-editbus"><title>The EditBus</title>
  747. <para>
  748. The
  749. EditBus maintains a list of objects that have requested to receive
  750. messages. When a message is sent using this class, all registered
  751. components receive it in turn.
  752. </para>
  753. <para>
  754. Plugins register with the EditBus to receive messages reflecting
  755. changes in the application's state, including changes in buffers,
  756. views and editing panes, changes in the set of properties maintained
  757. by the application, and the closing of the application. A full list
  758. of message classes used by the EditBus are summarized beginning with
  759. <xref linkend="class-ebmessage"/>.
  760. </para>
  761. <para>
  762. <indexterm>
  763. <primary>Plugin API</primary>
  764. <secondary>EBComponent class</secondary>
  765. </indexterm>
  766. Classes for objects that subscribe to the EditBus must implement the
  767. <classname>EBComponent</classname> interface, which defines the single
  768. method <function>handleMessage()</function>. A
  769. <classname>View</classname>, for example, can receive and handle EditBus
  770. messages because it also implements <classname>EBComponent</classname>.
  771. </para>
  772. <para>
  773. Any class in a plugin can receive messages by implementing
  774. the <classname>EBComponent</classname>
  775. interface. A <quote>plugin core class</quote> that extends the
  776. <classname>EBPlugin</classname> abstract class (and whose name ends with
  777. <quote>Plugin</quote> for identification purposes) will automatically be
  778. added to the EditBus during jEdit's startup routine. Any other
  779. class - for example, a docking window component that needs to receive
  780. notification of buffer changes - must perform its own registration by calling
  781. <function>EditBus.addToBus(this)</function> during its initialization.
  782. If this class if derived from <classname>JComponent</classname>, a
  783. convenient place to register would be in an implementation of the
  784. <classname>JComponent</classname> method
  785. <function>addNotify()</function>.
  786. </para>
  787. <sect2 id="class-ebcomponent"><title>Interface EBComponent</title>
  788. <para>
  789. Every plugin class that uses the EditBus for receiving
  790. messages
  791. must implement this interface.
  792. </para>
  793. <para>
  794. The <classname>EBComponent</classname> interface contains a single
  795. method that an implementing class (including any class derived from
  796. <classname>EBPlugin</classname>) must provide:
  797. </para>
  798. <itemizedlist>
  799. <listitem>
  800. <funcsynopsis>
  801. <funcprototype>
  802. <funcdef>public void <function>handleMessage</function></funcdef>
  803. <paramdef>EBMessage <parameter>message</parameter></paramdef>
  804. </funcprototype>
  805. </funcsynopsis>
  806. </listitem>
  807. </itemizedlist>
  808. <para>
  809. The parameter's type, <classname>EBMessage</classname>, is another
  810. abstract class which establishes the core elements of any message that
  811. is published to the EditBus. It has two attributes: an
  812. <classname>EBComponent</classname> that is the source of the message
  813. (the source will be <classname>null</classname> in some cases),
  814. and a <classname>boolean</classname> data member, <varname>vetoed</varname>. This
  815. flag indicates whether a prior recipient of the message has determined
  816. that the message has been handled and need not be passed on to other
  817. subscribers. The flag is set by a call
  818. to the <function>veto()</function> method of the
  819. <classname>EBMessage</classname>. Some message classes, however,
  820. are configured so that they cannot be vetoed, to ensure they are
  821. received by all subscribers.
  822. </para>
  823. <para>
  824. Message classes extending <classname>EBMessage</classname> typically add
  825. other data members and methods to provide subscribers with whatever is
  826. needed to handle the message appropriately. Descriptions of specific
  827. message classes can be found in <xref linkend="api-message"/>.
  828. </para>
  829. <para>
  830. The <function>handleMessage()</function> method
  831. must specify the type of responses
  832. the plugin will have for various subclasses of the
  833. <classname>EBMessage</classname> class. Typically this is done with
  834. one or more <function>if</function> blocks that test whether the message
  835. is an instance of a derived message class in which the plugin has an
  836. interest, as in the following example:
  837. </para>
  838. <informalexample><programlisting>if(msg instanceof BufferUpdate) {
  839. // a buffer's state has changed!
  840. }
  841. else if(msg instanceof ViewUpdate) {
  842. // a view's state has changed!
  843. }
  844. // ... and so on</programlisting></informalexample>
  845. <para>
  846. Note that any object, whether or not derived from
  847. <classname>EBComponent</classname>, can send a message to the EditBus
  848. by calling the static method <function>EditBus.send()</function>.
  849. This method takes a single parameter, an <classname>EBMessage</classname>
  850. object that is the message being sent. Most plugins, however, will
  851. only concern themselves with receiving, not sending, messages.
  852. </para>
  853. </sect2>
  854. </sect1>
  855. <sect1 id="host-design-conclusion"><title>Conclusion</title>
  856. <para>
  857. At this point, we can identify the following practical requirements
  858. for a plugin:
  859. </para>
  860. <itemizedlist>
  861. <listitem><para>
  862. it must be packaged as one or more JAR archives and contain a class
  863. file ending with the prefix <filename>Plugin</filename> that extends
  864. the <classname>EditPlugin</classname> abstract class;
  865. </para></listitem>
  866. <listitem><para>
  867. the archive must contain
  868. at least one properties file having a <filename>.props</filename>
  869. extension. Certain properties giving information
  870. about the plugin must be defined.
  871. </para></listitem>
  872. <listitem><para>
  873. if the plugin will provide a visible interface, the JAR archive must contain
  874. an XML data file named <filename>dockables.xml</filename> containing
  875. BeanShell code for creating a container for that interface;
  876. </para></listitem>
  877. <listitem><para>
  878. the JAR archive may contain data concerning actions for display
  879. in jEdit's menu bar and elsewhere in a file entitled
  880. <filename>actions.xml</filename>; and
  881. </para></listitem>
  882. <listitem><para>
  883. if the plugin must respond as a whole to changes in the state of the jEdit
  884. application, the plugin core class should be derived from the
  885. <classname>EBPlugin</classname> class instead of directly from
  886. <classname>EditPlugin</classname>
  887. </para></listitem>
  888. </itemizedlist>
  889. <para>
  890. We will provide more detail on these requirements later.
  891. </para>
  892. </sect1>
  893. </chapter>