PageRenderTime 52ms CodeModel.GetById 19ms RepoModel.GetById 0ms app.codeStats 0ms

/www/tags/NOV_07_2009/htdocs/42docs/users-guide/macro-analysis.html

#
HTML | 322 lines | 321 code | 1 blank | 0 comment | 0 complexity | 2501bb67b10b46d2cc6168c28656f49f 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. <html><head><meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"><title>Analysis of the Macro</title><meta name="generator" content="DocBook XSL Stylesheets V1.65.1"><link rel="home" href="index.html" title="jEdit 4.2 User's Guide"><link rel="up" href="dialog-macro.html" title="Chapter 14. A Dialog-Based Macro"><link rel="previous" href="add-prefix-and-suffix.html" title="Listing of the Macro"><link rel="next" href="macro-tips.html" title="Chapter 15. Macro Tips and Techniques"></head><body bgcolor="white" text="black" link="#0000FF" vlink="#840084" alink="#0000FF"><div class="navheader"><table width="100%" summary="Navigation header"><tr><th colspan="3" align="center">Analysis of the Macro</th></tr><tr><td width="20%" align="left"><a accesskey="p" href="add-prefix-and-suffix.html">Prev</a> </td><th width="60%" align="center">Chapter 14. A Dialog-Based Macro</th><td width="20%" align="right"> <a accesskey="n" href="macro-tips.html">Next</a></td></tr></table><hr></div><div class="sect1" lang="en"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="macro-analysis"></a>Analysis of the Macro</h2></div></div><div></div></div><div class="sect2" lang="en"><div class="titlepage"><div><div><h3 class="title"><a name="explain-imports"></a>Import Statements</h3></div></div><div></div></div><div class="informalexample"><table border="0" bgcolor="#E0E0E0"><tr><td><pre class="programlisting">// import statement
  2. import javax.swing.border.*;</pre></td></tr></table></div><p>
  3. This macro makes use of classes in the
  4. <tt class="literal">javax.swing.border</tt> package, which is not
  5. automatically imported. As we mentioned
  6. previously (see <a href="first-example.html" title="The Mandatory First Example">the section called &#8220;The Mandatory First Example&#8221;</a>), jEdit's implementation of
  7. BeanShell causes a number of classes to be automatically imported. Classes
  8. that are not automatically imported must be identified by a full qualified
  9. name or be the subject of an <tt class="function">import</tt> statement.
  10. </p></div><div class="sect2" lang="en"><div class="titlepage"><div><div><h3 class="title"><a name="explain-create-dialog"></a>Create the Dialog</h3></div></div><div></div></div><div class="informalexample"><table border="0" bgcolor="#E0E0E0"><tr><td><pre class="programlisting">// create dialog object
  11. title = &#8220;<span class="quote">Add prefix and suffix to selected lines</span>&#8221;;
  12. dialog = new JDialog(view, title, false);
  13. content = new JPanel(new BorderLayout());
  14. content.setBorder(new EmptyBorder(12, 12, 12, 12));
  15. dialog.setContentPane(content);</pre></td></tr></table></div><p>
  16. To get input for the macro, we need a dialog that provides for input of the prefix
  17. and suffix strings, an <span><b class="guibutton">OK</b></span> button to perform text
  18. insertion, and a <span><b class="guibutton">Cancel</b></span> button in case we change our
  19. mind. We have decided to make the dialog window non-modal. This will allow us to
  20. move around in the text buffer to find things we may need (including text to cut
  21. and paste) while the macro is running and the dialog is visible.
  22. </p><p>
  23. The Java object we need is a <tt class="classname">JDialog</tt> object from
  24. the Swing package. To construct one, we use the <tt class="function">new</tt>
  25. keyword and call a <i class="glossterm">constructor</i> function. The
  26. constructor we use takes three parameters: the owner of the new dialog,
  27. the title to be displayed in the dialog frame, and a
  28. <tt class="classname">boolean</tt> parameter (<tt class="constant">true</tt> or
  29. <tt class="constant">false</tt>) that specifies whether the dialog will be
  30. modal or non-modal. We define the variable <tt class="varname">title</tt>
  31. using a string literal, then use it immediately in the
  32. <tt class="classname">JDialog</tt> constructor.
  33. </p><p>
  34. A <tt class="classname">JDialog</tt> object is a window containing a single object
  35. called a <i class="glossterm">content pane</i>. The content pane in turn contains
  36. the various visible components of the dialog. A
  37. <tt class="classname">JDialog</tt> creates an empty content pane for itself as
  38. during its construction. However, to control the dialog's appearance
  39. as much as possible, we will separately create our own content pane and
  40. attach it to the <tt class="classname">JDialog</tt>. We do this by creating a
  41. <tt class="classname">JPanel</tt> object. A <tt class="classname">JPanel</tt> is a
  42. lightweight container for other components that can be set to a given size and
  43. color. It also contains a <i class="glossterm">layout</i> scheme for arranging the
  44. size and position of its components. Here we are constructing a
  45. <tt class="classname">JPanel</tt> as a content pane with a
  46. <tt class="classname">BorderLayout</tt>. We put a <tt class="classname">EmptyBorder</tt>
  47. inside it to serve as a margin between the edge of the window and the components
  48. inside. We then attach the <tt class="classname">JPanel</tt> as the dialog's content
  49. pane, replacing the dialog's home-grown version.
  50. </p><p>
  51. A <tt class="classname">BorderLayout</tt> is one of the simpler layout
  52. schemes available for container objects like <tt class="classname">JPanel</tt>.
  53. A <tt class="classname">BorderLayout</tt>
  54. divides the container into five sections: &#8220;<span class="quote">North</span>&#8221;,
  55. &#8220;<span class="quote">South</span>&#8221;, &#8220;<span class="quote">East</span>&#8221;, &#8220;<span class="quote">West</span>&#8221; and
  56. &#8220;<span class="quote">Center</span>&#8221;. Components are added to the layout using the
  57. container's <tt class="function">add</tt> method, specifying the component to
  58. be added and the section to which it is assigned. Building a
  59. component like our dialog window involves building a set of
  60. nested containers and specifying the location of each of their
  61. member components. We have taken the first step by creating a
  62. <tt class="classname">JPanel</tt> as the dialog's content pane.
  63. </p></div><div class="sect2" lang="en"><div class="titlepage"><div><div><h3 class="title"><a name="explain-fields-panel"></a>
  64. Create the Text Fields</h3></div></div><div></div></div><div class="informalexample"><table border="0" bgcolor="#E0E0E0"><tr><td><pre class="programlisting">// add the text fields
  65. fieldPanel = new JPanel(new GridLayout(4, 1, 0, 6));
  66. prefixField = new HistoryTextField("macro.add-prefix");
  67. prefixLabel = new JLabel(&#8220;<span class="quote">Prefix to add</span>&#8221;:);
  68. suffixField = new HistoryTextField(&#8220;<span class="quote">macro.add-suffix</span>&#8221;);
  69. suffixLabel = new JLabel(&#8220;<span class="quote">Suffix to add:</span>&#8221;);
  70. fieldPanel.add(prefixLabel);
  71. fieldPanel.add(prefixField);
  72. fieldPanel.add(suffixLabel);
  73. fieldPanel.add(suffixField);
  74. content.add(fieldPanel, &#8220;<span class="quote">Center</span>&#8221;);</pre></td></tr></table></div><p>
  75. Next we shall create a smaller panel containing two fields for
  76. entering the prefix and suffix text and two labels identifying the
  77. input fields.
  78. </p><p>
  79. For the text fields, we will use jEdit's
  80. <a href="../api/org/gjt/sp/jedit/gui/HistoryTextField.html" target="_top">HistoryTextField</a>
  81. class. It is derived from the Java Swing class
  82. <tt class="classname">JTextField</tt>. This class offers the enhancement of a stored
  83. list of prior values used as text input. When the component has input focus, the
  84. up and down keys scroll through the prior values for the variable.
  85. </p><p>
  86. To create the <a href="../api/org/gjt/sp/jedit/gui/HistoryTextField.html" target="_top">HistoryTextField</a> objects we use a
  87. constructor method that takes a single parameter: the name of the tag
  88. under which history values will be stored. Here we choose names that are
  89. not likely to conflict with existing jEdit history items.
  90. </p><p>
  91. The labels that accompany the text fields are
  92. <tt class="classname">JLabel</tt> objects from the Java Swing
  93. package. The constructor we use for both labels takes the label text
  94. as a single <tt class="classname">String</tt> parameter.
  95. </p><p>
  96. We wish to arrange these four components from top to bottom,
  97. one after the other. To achieve that, we use a
  98. <tt class="classname">JPanel</tt> container object named
  99. <tt class="varname">fieldPanel</tt> that
  100. will be nested inside the dialog's content pane that we have
  101. already created. In the constructor for <tt class="varname">fieldPanel</tt>,
  102. we assign a new <tt class="classname">GridLayout</tt> with the indicated
  103. parameters: four rows, one column, zero spacing between columns (a
  104. meaningless element of a grid with only one column, but
  105. nevertheless a required parameter) and spacing of six pixels between
  106. rows. The spacing between rows spreads out the four &#8220;<span class="quote">grid</span>&#8221;
  107. elements. After the components, the panel and the layout are
  108. specified, the components are added to <tt class="varname">fieldPanel</tt>
  109. top to bottom, one &#8220;<span class="quote">grid cell</span>&#8221; at a time. Finally, the complete
  110. <tt class="varname">fieldPanel</tt> is added to the dialog's content pane to
  111. occupy the &#8220;<span class="quote">Center</span>&#8221; section of the content pane.
  112. </p></div><div class="sect2" lang="en"><div class="titlepage"><div><div><h3 class="title"><a name="explain-button-panel"></a>
  113. Create the Buttons</h3></div></div><div></div></div><div class="informalexample"><table border="0" bgcolor="#E0E0E0"><tr><td><pre class="programlisting">// add the buttons
  114. buttonPanel = new JPanel();
  115. buttonPanel.setLayout(new BoxLayout(buttonPanel,
  116. BoxLayout.X_AXIS));
  117. buttonPanel.setBorder(new EmptyBorder(12, 50, 0, 50));
  118. buttonPanel.add(Box.createGlue());
  119. ok = new JButton(&#8220;<span class="quote">OK</span>&#8221;);
  120. cancel = new JButton(&#8220;<span class="quote">Cancel</span>&#8221;);
  121. ok.setPreferredSize(cancel.getPreferredSize());
  122. dialog.getRootPane().setDefaultButton(ok);
  123. buttonPanel.add(ok);
  124. buttonPanel.add(Box.createHorizontalStrut(6));
  125. buttonPanel.add(cancel);
  126. buttonPanel.add(Box.createGlue());
  127. content.add(buttonPanel, &#8220;<span class="quote">South</span>&#8221;);</pre></td></tr></table></div><p>
  128. To create the dialog's buttons, we follow repeat the &#8220;<span class="quote">nested container</span>&#8221;
  129. pattern we used in creating the text fields.
  130. First, we create a new, nested panel. This time we use a <tt class="classname">BoxLayout</tt>
  131. that places components either in a single row or
  132. column, depending on the parameter passed to its constructor. This layout object
  133. is more flexible than a <tt class="classname">GridLayout</tt> in that variable spacing
  134. between elements can be specified easily. We put an
  135. <tt class="classname">EmptyBorder</tt> in the new panel to set margins for placing
  136. the buttons. Then we create the buttons, using a <tt class="classname">JButton</tt>
  137. constructor that specifies the button text. After setting the size of the
  138. <span><b class="guilabel">OK</b></span> button to equal the size of the
  139. <span><b class="guilabel">Cancel</b></span> button, we designate the <span><b class="guilabel">OK</b></span>
  140. button as the default button in the dialog. This causes the
  141. <span><b class="guilabel">OK</b></span> button to be outlined when the dialog if first displayed.
  142. Finally, we place the buttons side by side with a 6 pixel gap between them (for aesthetic
  143. reasons), and place the completed <tt class="varname">buttonPanel</tt> in the
  144. &#8220;<span class="quote">South</span>&#8221; section of the dialog's content pane.
  145. </p></div><div class="sect2" lang="en"><div class="titlepage"><div><div><h3 class="title"><a name="explain-add-listeners"></a>Register the Action Listeners</h3></div></div><div></div></div><div class="informalexample"><table border="0" bgcolor="#E0E0E0"><tr><td><pre class="programlisting">// register this method as an ActionListener for
  146. // the buttons and text fields
  147. ok.addActionListener(this);
  148. cancel.addActionListener(this);
  149. prefixField.addActionListener(this);
  150. suffixField.addActionListener(this);</pre></td></tr></table></div><p>
  151. In order to specify the action to be taken upon clicking a
  152. button or pressing the <tt class="keycap">Enter</tt> key, we must register
  153. an <tt class="classname">ActionListener</tt> for each of the four active
  154. components of the dialog - the two
  155. <a href="../api/org/gjt/sp/jedit/HistoryTextField.html" target="_top">HistoryTextField</a>
  156. components and the two buttons. In Java, an
  157. <tt class="classname">ActionListener</tt> is an <i class="glossterm">interface</i> - an
  158. abstract specification for a derived class to implement. The
  159. <tt class="classname">ActionListener</tt> interface contains a single method to
  160. be implemented:
  161. </p><div class="funcsynopsis"><p><code class="funcdef">public void <b class="fsfunc">actionPerformed</b>(</code>ActionEvent <var class="pdparam">e</var><code>)</code>;</p></div><p>
  162. BeanShell does not permit a script to create derived classes.
  163. However, BeanShell offers a useful substitute: a method can be
  164. used as a scripted object that can include nested methods implementing a
  165. number of Java interfaces. The method
  166. <tt class="function">prefixSuffixDialog()</tt> that we are writing can thus be
  167. treated as an <tt class="classname">ActionListener</tt> object. To accomplish this, we
  168. call <tt class="function">addActionListener()</tt> on each of the four
  169. components specifying <tt class="varname">this</tt> as the
  170. <tt class="classname">ActionListener</tt>. We still need to implement the
  171. interface. We will do that shortly.
  172. </p></div><div class="sect2" lang="en"><div class="titlepage"><div><div><h3 class="title"><a name="explain-set-visible"></a>Make the Dialog Visible</h3></div></div><div></div></div><div class="informalexample"><table border="0" bgcolor="#E0E0E0"><tr><td><pre class="programlisting">// locate the dialog in the center of the
  173. // editing pane and make it visible
  174. dialog.pack();
  175. dialog.setLocationRelativeTo(view);
  176. dialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
  177. dialog.setVisible(true);</pre></td></tr></table></div><p>
  178. Here we do three things. First, we activate all the layout routines we have
  179. established by calling the <tt class="function">pack()</tt> method for the dialog as
  180. the top-level window. Next we center the dialog's position in the active jEdit
  181. <tt class="varname">view</tt> by calling <tt class="function">setLocationRelativeTo()</tt>
  182. on the dialog. We also call the <tt class="function">setDefaultCloseOperation()</tt>
  183. function to specify that the dialog box should be immediately disposed if the
  184. user clicks the close box. Finally, we activate the dialog by calling
  185. <tt class="function">setVisible()</tt>with the state parameter set to
  186. <tt class="constant">true</tt>.
  187. </p><p>
  188. At this point we have a decent looking dialog window that
  189. doesn't do anything. Without more code, it will not respond to
  190. user input and will not accomplish any text manipulation. The
  191. remainder of the script deals with these two requirements.
  192. </p></div><div class="sect2" lang="en"><div class="titlepage"><div><div><h3 class="title"><a name="explain-action-listener"></a>The Action Listener</h3></div></div><div></div></div><div class="informalexample"><table border="0" bgcolor="#E0E0E0"><tr><td><pre class="programlisting">// this method will be called when a button is clicked
  193. // or when ENTER is pressed
  194. void actionPerformed(e)
  195. {
  196. if(e.getSource() != cancel)
  197. {
  198. processText();
  199. }
  200. dialog.dispose();
  201. }</pre></td></tr></table></div><p>
  202. The method <tt class="function">actionPerformed()</tt> nested inside
  203. <tt class="function">prefixSuffixDialog()</tt> implements the implicit
  204. <tt class="classname">ActionListener</tt> interface. It looks at the source
  205. of the <tt class="classname">ActionEvent</tt>, determined by a call to
  206. <tt class="function">getSource()</tt>. What we do with this return value is
  207. straightforward: if the source is not the <span><b class="guibutton">Cancel</b></span> button, we
  208. call the <tt class="function">processText()</tt> method to insert the prefix
  209. and suffix text. Then the dialog is closed by calling its
  210. <tt class="function">dispose()</tt> method.
  211. </p><p>
  212. The ability to implement interfaces like
  213. <tt class="classname">ActionListener</tt> inside a BeanShell script is
  214. one of the more powerful features of the BeanShell package.
  215. this technique is discussed in
  216. the next chapter; see <a href="macro-tips-BeanShell.html#macro-tips-BeanShell-class" title="Implementing Classes and Interfaces">the section called &#8220;Implementing Classes and Interfaces&#8221;</a>.
  217. </p></div><div class="sect2" lang="en"><div class="titlepage"><div><div><h3 class="title"><a name="explain-process-text"></a>Get the User's Input</h3></div></div><div></div></div><div class="informalexample"><table border="0" bgcolor="#E0E0E0"><tr><td><pre class="programlisting">// this is where the work gets done to insert
  218. // the prefix and suffix
  219. void processText()
  220. {
  221. prefix = prefixField.getText();
  222. suffix = suffixField.getText();
  223. if(prefix.length() == 0 &amp;&amp; suffix.length() == 0)
  224. return;
  225. prefixField.addCurrentToHistory();
  226. suffixField.addCurrentToHistory();</pre></td></tr></table></div><p>
  227. The method <tt class="function">processText()</tt> does the work of our
  228. macro. First we obtain the input from the two text fields with a
  229. call to their <tt class="function">getText()</tt> methods. If they are both
  230. empty, there is nothing to do, so the method returns. If there is
  231. input, any text in the field is added to that field's stored
  232. history list by calling <tt class="function">addCurrentToHistory()</tt>.
  233. We do not need to test the <tt class="varname">prefixField</tt> or
  234. <tt class="varname">suffixField</tt> controls for <tt class="constant">null</tt>
  235. or empty values because <tt class="function">addCurrentToHistory()</tt>
  236. does that internally.
  237. </p></div><div class="sect2" lang="en"><div class="titlepage"><div><div><h3 class="title"><a name="explain-jedit-calls"></a>Call jEdit Methods to Manipulate Text</h3></div></div><div></div></div><div class="informalexample"><table border="0" bgcolor="#E0E0E0"><tr><td><pre class="programlisting"> // text manipulation begins here using calls
  238. // to jEdit methods
  239. buffer.beginCompoundEdit();
  240. selectedLines = textArea.getSelectedLines();
  241. for(i = 0; i &lt; selectedLines.length; ++i)
  242. {
  243. offsetBOL = textArea.getLineStartOffset(
  244. selectedLines[i]);
  245. textArea.setCaretPosition(offsetBOL);
  246. textArea.goToStartOfWhiteSpace(false);
  247. textArea.goToEndOfWhiteSpace(true);
  248. text = textArea.getSelectedText();
  249. if(text == null) text = "";
  250. textArea.setSelectedText(prefix + text + suffix);
  251. }
  252. buffer.endCompoundEdit();
  253. }</pre></td></tr></table></div><p>
  254. The text manipulation routine loops through each selected line
  255. in the text buffer. We get the loop parameters by calling
  256. <tt class="function">textArea.getSelectedLines()</tt>, which returns an array
  257. consisting of the line numbers of every selected line. The array includes the
  258. number of the current line, whether or not it is selected, and the line numbers
  259. are sorted in increasing order. We iterate through each member of the
  260. <tt class="varname">selectedLines</tt> array, which represents the number of a
  261. selected line, and apply the following routine:
  262. </p><div class="itemizedlist"><ul type="disc"><li><p>
  263. Get the buffer position of the start of the line (expressed
  264. as a zero-based index from the start of the buffer) by calling
  265. <tt class="function">textArea.getLineStartOffset(selectedLines[i])</tt>;
  266. </p></li><li><p>
  267. Move the caret to that position by calling
  268. <tt class="function">textArea.setCaretPosition()</tt>;
  269. </p></li><li><p>
  270. Find the first and last non-whitespace characters on the line
  271. by calling <tt class="function">textArea.goToStartOfWhiteSpace()</tt> and
  272. <tt class="function">textArea.goToEndOfWhiteSpace()</tt>;
  273. </p><p>
  274. The <tt class="function">goTo...</tt> methods in
  275. <a href="../api/org/gjt/sp/jedit/textarea/JEditTextArea.html" target="_top">JEditTextArea</a> take a single parameter which
  276. tells jEdit whether the text between the current caret position and
  277. the desired position should be selected. Here, we call
  278. <tt class="function">textArea.goToStartOfWhiteSpace(false)</tt> so that
  279. no text is selected, then call
  280. <tt class="function">textArea.goToEndOfWhiteSpace(true)</tt> so that all of
  281. the text between the beginning and ending whitespace is
  282. selected.
  283. </p></li><li><p>
  284. Retrieve the selected text by storing the return value of
  285. <tt class="function">textArea.getSelectedText()</tt> in a new variable
  286. <tt class="function">text</tt>.
  287. </p><p>
  288. If the line is empty, <tt class="function">getSelectedText()</tt> will
  289. return <tt class="constant">null</tt>. In that case, we assign an empty
  290. string to <tt class="varname">text</tt> to avoid calling methods on a
  291. null object.
  292. </p></li><li><p>
  293. Change the selected text to <tt class="varname">prefix + text +
  294. suffix</tt> by calling
  295. <tt class="function">textArea.setSelectedText()</tt>.
  296. If there is no selected text (for example, if the line is empty),
  297. the prefix and suffix will be inserted without any intervening
  298. characters.
  299. </p></li></ul></div><div class="sidebar"><p class="title"><b>Compound edits</b></p><p>
  300. Note the <tt class="function">beginCompoundEdit()</tt> and
  301. <tt class="function">endCompoundEdit()</tt> calls. These ensure that all edits
  302. performed between the two calls can be undone in one step. Normally,
  303. jEdit automatically wraps a macro call in these methods; however if
  304. the macro shows a non-modal dialog box, as far as jEdit is concerned
  305. the macro has finished executing by the time the dialog is shown,
  306. since control returns to the event dispatch thread.
  307. </p><p>
  308. If you do not understand this, don't worry; just keep it in mind if
  309. your macro needs to show a non-modal dialog box for some reason;
  310. Most macros won't.
  311. </p></div></div><div class="sect2" lang="en"><div class="titlepage"><div><div><h3 class="title"><a name="explain-main"></a>The Main Routine</h3></div></div><div></div></div><div class="informalexample"><table border="0" bgcolor="#E0E0E0"><tr><td><pre class="programlisting">// this single line of code is the script's main routine
  312. prefixSuffixDialog();</pre></td></tr></table></div><p>
  313. The call to <tt class="function">prefixSuffixDialog()</tt>is the only line
  314. in the macro that is not inside an enclosing block. BeanShell
  315. treats such code as a top-level <tt class="function">main</tt> method and
  316. begins execution with it.
  317. </p><p>
  318. Our analysis of <tt class="filename">Add_Prefix_and_Suffix.bsh</tt> is now
  319. complete. In the next section, we look at other ways in which a macro
  320. can obtain user input, as well as other macro writing techniques.
  321. </p></div></div><div class="navfooter"><hr><table width="100%" summary="Navigation footer"><tr><td width="40%" align="left"><a accesskey="p" href="add-prefix-and-suffix.html">Prev</a> </td><td width="20%" align="center"><a accesskey="u" href="dialog-macro.html">Up</a></td><td width="40%" align="right"> <a accesskey="n" href="macro-tips.html">Next</a></td></tr><tr><td width="40%" align="left" valign="top">Listing of the Macro </td><td width="20%" align="center"><a accesskey="h" href="index.html">Home</a></td><td width="40%" align="right" valign="top"> Chapter 15. Macro Tips and Techniques</td></tr></table></div></body></html>