PageRenderTime 60ms CodeModel.GetById 22ms RepoModel.GetById 0ms app.codeStats 0ms

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