PageRenderTime 161ms CodeModel.GetById 138ms app.highlight 18ms RepoModel.GetById 1ms 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
  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
  2import 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
 11title = &#8220;<span class="quote">Add prefix and suffix to selected lines</span>&#8221;;
 12dialog = new JDialog(view, title, false);
 13content = new JPanel(new BorderLayout());
 14content.setBorder(new EmptyBorder(12, 12, 12, 12));
 15dialog.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>
 64Create 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
 65fieldPanel = new JPanel(new GridLayout(4, 1, 0, 6));
 66prefixField = new HistoryTextField("macro.add-prefix");
 67prefixLabel = new JLabel(&#8220;<span class="quote">Prefix to add</span>&#8221;:);
 68suffixField = new HistoryTextField(&#8220;<span class="quote">macro.add-suffix</span>&#8221;);
 69suffixLabel = new JLabel(&#8220;<span class="quote">Suffix to add:</span>&#8221;);
 70fieldPanel.add(prefixLabel);
 71fieldPanel.add(prefixField);
 72fieldPanel.add(suffixLabel);
 73fieldPanel.add(suffixField);
 74content.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    
 86  </p><p>
 87    To create the <a href="../api/org/gjt/sp/jedit/gui/HistoryTextField.html" target="_top">HistoryTextField</a> objects we use a
 88    constructor method that takes a single parameter: the name of the tag
 89    under which history values will be stored. Here we choose names that are
 90    not likely to conflict with existing jEdit history items.
 91  </p><p>
 92    The labels that accompany the text fields are
 93    <tt class="classname">JLabel</tt> objects from the Java Swing
 94    package. The constructor we use for both labels takes the label text
 95    as a single <tt class="classname">String</tt> parameter.
 96  </p><p>
 97    We wish to arrange these four components from top to bottom,
 98    one after the other. To achieve that, we use a
 99    <tt class="classname">JPanel</tt> container object named
100    <tt class="varname">fieldPanel</tt> that
101    will be nested inside the dialog's content pane that we have
102    already created. In the constructor for <tt class="varname">fieldPanel</tt>,
103    we assign a new <tt class="classname">GridLayout</tt> with the indicated
104    parameters: four rows, one column, zero spacing between columns (a
105    meaningless element of a grid with only one column, but
106    nevertheless a required parameter) and spacing of six pixels between
107    rows. The spacing between rows spreads out the four &#8220;<span class="quote">grid</span>&#8221;
108    elements. After the components, the panel and the layout are
109    specified, the components are added to <tt class="varname">fieldPanel</tt>
110    top to bottom, one &#8220;<span class="quote">grid cell</span>&#8221; at a time. Finally, the complete
111    <tt class="varname">fieldPanel</tt> is added to the dialog's content pane to
112    occupy the &#8220;<span class="quote">Center</span>&#8221; section of the content pane.
113  </p></div><div class="sect2" lang="en"><div class="titlepage"><div><div><h3 class="title"><a name="explain-button-panel"></a>
114Create the Buttons</h3></div></div><div></div></div><div class="informalexample"><table border="0" bgcolor="#E0E0E0"><tr><td><pre class="programlisting">// add the buttons
115buttonPanel = new JPanel();
116buttonPanel.setLayout(new BoxLayout(buttonPanel,
117    BoxLayout.X_AXIS));
118buttonPanel.setBorder(new EmptyBorder(12, 50, 0, 50));
119buttonPanel.add(Box.createGlue());
120ok = new JButton(&#8220;<span class="quote">OK</span>&#8221;);
121cancel = new JButton(&#8220;<span class="quote">Cancel</span>&#8221;);
122ok.setPreferredSize(cancel.getPreferredSize());
123dialog.getRootPane().setDefaultButton(ok);
124buttonPanel.add(ok);
125buttonPanel.add(Box.createHorizontalStrut(6));
126buttonPanel.add(cancel);
127buttonPanel.add(Box.createGlue());
128content.add(buttonPanel, &#8220;<span class="quote">South</span>&#8221;);</pre></td></tr></table></div><p>
129    To create the dialog's buttons, we follow repeat the &#8220;<span class="quote">nested container</span>&#8221;
130    pattern we used in creating the text fields.
131    First, we create a new, nested panel. This time we use a <tt class="classname">BoxLayout</tt>
132    that places components either in a single row or
133    column, depending on the parameter passed to its constructor. This layout object
134    is more flexible than a <tt class="classname">GridLayout</tt> in that variable spacing
135    between elements can be specified easily. We put an
136    <tt class="classname">EmptyBorder</tt> in the new panel to set margins for placing
137    the buttons. Then we create the buttons, using a <tt class="classname">JButton</tt>
138    constructor that specifies the button text. After setting the size of the
139    <span><b class="guilabel">OK</b></span> button to equal the size of the
140    <span><b class="guilabel">Cancel</b></span> button, we designate the <span><b class="guilabel">OK</b></span>
141    button as the default button in the dialog. This causes the
142    <span><b class="guilabel">OK</b></span> button to be outlined when the dialog if first displayed.
143    Finally, we place the buttons side by side with a 6 pixel gap between them (for aesthetic
144    reasons), and place the completed <tt class="varname">buttonPanel</tt> in the
145    &#8220;<span class="quote">South</span>&#8221; section of the dialog's content pane.
146  </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
147// the buttons and text fields
148ok.addActionListener(this);
149cancel.addActionListener(this);
150prefixField.addActionListener(this);
151suffixField.addActionListener(this);</pre></td></tr></table></div><p>
152    In order to specify the action to be taken upon clicking a
153    button or pressing the <tt class="keycap">Enter</tt> key, we must register
154    an <tt class="classname">ActionListener</tt> for each of the four active
155    components of the dialog - the two
156    <a href="../api/org/gjt/sp/jedit/HistoryTextField.html" target="_top">HistoryTextField</a>
157    components and the two buttons. In Java, an
158    <tt class="classname">ActionListener</tt> is an <i class="glossterm">interface</i> - an
159    abstract specification for a derived class to implement. The
160    <tt class="classname">ActionListener</tt> interface contains a single method to
161    be implemented:
162  </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>
163    BeanShell does not permit a script to create derived classes.
164    However, BeanShell offers a useful substitute: a method can be
165    used as a scripted object that can include nested methods implementing a
166    number of Java interfaces. The method
167    <tt class="function">prefixSuffixDialog()</tt> that we are writing can thus be
168    treated as an <tt class="classname">ActionListener</tt> object. To accomplish this, we
169    call <tt class="function">addActionListener()</tt> on each of the four
170    components specifying <tt class="varname">this</tt> as the
171    <tt class="classname">ActionListener</tt>. We still need to implement the
172    interface. We will do that shortly.
173  </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
174// editing pane and make it visible
175dialog.pack();
176dialog.setLocationRelativeTo(view);
177dialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
178dialog.setVisible(true);</pre></td></tr></table></div><p>
179    Here we do three things. First, we activate all the layout routines we have
180    established by calling the <tt class="function">pack()</tt> method for the dialog as
181    the top-level window. Next we center the dialog's position in the active jEdit
182    <tt class="varname">view</tt> by calling <tt class="function">setLocationRelativeTo()</tt>
183    on the dialog. We also call the <tt class="function">setDefaultCloseOperation()</tt>
184    function to specify that the dialog box should be immediately disposed if the
185    user clicks the close box. Finally, we activate the dialog by calling
186    <tt class="function">setVisible()</tt>with the state parameter set to
187    <tt class="constant">true</tt>.
188  </p><p>
189    At this point we have a decent looking dialog window that
190    doesn't do anything. Without more code, it will not respond to
191    user input and will not accomplish any text manipulation. The
192    remainder of the script deals with these two requirements.
193  </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
194// or when ENTER is pressed
195void actionPerformed(e)
196{
197    if(e.getSource() != cancel)
198    {
199        processText();
200    }
201    dialog.dispose();
202}</pre></td></tr></table></div><p>
203    The method <tt class="function">actionPerformed()</tt> nested inside
204    <tt class="function">prefixSuffixDialog()</tt> implements the implicit
205    <tt class="classname">ActionListener</tt> interface. It looks at the source
206    of the <tt class="classname">ActionEvent</tt>, determined by a call to
207    <tt class="function">getSource()</tt>. What we do with this return value is
208    straightforward: if the source is not the <span><b class="guibutton">Cancel</b></span> button, we
209    call the <tt class="function">processText()</tt> method to insert the prefix
210    and suffix text. Then the dialog is closed by calling its
211    <tt class="function">dispose()</tt> method.
212  </p><p>
213    The ability to implement interfaces like
214    <tt class="classname">ActionListener</tt> inside a BeanShell script is
215    one of the more powerful features of the BeanShell package.
216    this technique is discussed in
217    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>.
218  </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
219// the prefix and suffix
220void processText()
221{
222    prefix = prefixField.getText();
223    suffix = suffixField.getText();
224    if(prefix.length() == 0 &amp;&amp; suffix.length() == 0)
225        return;
226    prefixField.addCurrentToHistory();
227    suffixField.addCurrentToHistory();</pre></td></tr></table></div><p>
228    The method <tt class="function">processText()</tt> does the work of our
229    macro. First we obtain the input from the two text fields with a
230    call to their <tt class="function">getText()</tt> methods. If they are both
231    empty, there is nothing to do, so the method returns. If there is
232    input, any text in the field is added to that field's stored
233    history list by calling <tt class="function">addCurrentToHistory()</tt>.
234    We do not need to test the <tt class="varname">prefixField</tt> or
235    <tt class="varname">suffixField</tt> controls for <tt class="constant">null</tt>
236    or empty values because <tt class="function">addCurrentToHistory()</tt>
237    does that internally.
238  </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
239    // to jEdit methods
240    buffer.beginCompoundEdit();
241    selectedLines = textArea.getSelectedLines();
242    for(i = 0; i &lt; selectedLines.length; ++i)
243    {
244        offsetBOL = textArea.getLineStartOffset(
245            selectedLines[i]);
246        textArea.setCaretPosition(offsetBOL);
247        textArea.goToStartOfWhiteSpace(false);
248        textArea.goToEndOfWhiteSpace(true);
249        text = textArea.getSelectedText();
250        if(text == null) text = "";
251        textArea.setSelectedText(prefix + text + suffix);
252    }
253    buffer.endCompoundEdit();
254}</pre></td></tr></table></div><p>
255    The text manipulation routine loops through each selected line
256    in the text buffer. We get the loop parameters by calling
257    <tt class="function">textArea.getSelectedLines()</tt>, which returns an array
258    consisting of the line numbers of every selected line.  The array includes the
259    number of the current line, whether or not it is selected, and the line numbers
260    are sorted in increasing order.  We iterate through each member of the
261    <tt class="varname">selectedLines</tt> array, which represents the number of a
262    selected line, and apply the following routine:
263  </p><div class="itemizedlist"><ul type="disc"><li><p>
264      Get the buffer position of the start of the line (expressed
265      as a zero-based index from the start of the buffer) by calling
266      <tt class="function">textArea.getLineStartOffset(selectedLines[i])</tt>;
267    </p></li><li><p>
268      Move the caret to that position by calling
269      <tt class="function">textArea.setCaretPosition()</tt>;
270    </p></li><li><p>
271      Find the first and last non-whitespace characters on the line
272      by calling <tt class="function">textArea.goToStartOfWhiteSpace()</tt> and
273      <tt class="function">textArea.goToEndOfWhiteSpace()</tt>;
274    </p><p>
275      The <tt class="function">goTo...</tt> methods in
276      <a href="../api/org/gjt/sp/jedit/textarea/JEditTextArea.html" target="_top">JEditTextArea</a> take a single parameter which
277      tells jEdit whether the text between the current caret position and
278      the desired position should be selected. Here, we call
279      <tt class="function">textArea.goToStartOfWhiteSpace(false)</tt> so that
280      no text is selected, then call
281      <tt class="function">textArea.goToEndOfWhiteSpace(true)</tt> so that all of
282      the text between the beginning and ending whitespace is
283      selected.
284    </p></li><li><p>
285      Retrieve the selected text by storing the return value of
286      <tt class="function">textArea.getSelectedText()</tt> in a new variable
287      <tt class="function">text</tt>.
288    </p><p>
289      If the line is empty, <tt class="function">getSelectedText()</tt> will
290      return <tt class="constant">null</tt>. In that case, we assign an empty
291      string to <tt class="varname">text</tt> to avoid calling methods on a
292      null object.
293    </p></li><li><p>
294      Change the selected text to <tt class="varname">prefix + text +
295      suffix</tt> by calling
296      <tt class="function">textArea.setSelectedText()</tt>.
297      If there is no selected text (for example, if the line is empty),
298      the prefix and suffix will be inserted without any intervening
299      characters.
300    </p></li></ul></div><div class="sidebar"><p class="title"><b>Compound edits</b></p><p>
301  Note the <tt class="function">beginCompoundEdit()</tt> and
302  <tt class="function">endCompoundEdit()</tt> calls. These ensure that all edits
303  performed between the two calls can be undone in one step. Normally,
304  jEdit automatically wraps a macro call in these methods; however if
305  the macro shows a non-modal dialog box, as far as jEdit is concerned
306  the macro has finished executing by the time the dialog is shown,
307  since control returns to the event dispatch thread.
308 </p><p>
309  If you do not understand this, don't worry; just keep it in mind if
310  your macro needs to show a non-modal dialog box for some reason;
311  Most macros won't.
312 </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
313prefixSuffixDialog();</pre></td></tr></table></div><p>
314    The call to <tt class="function">prefixSuffixDialog()</tt>is the only line
315    in the macro that is not inside an enclosing block. BeanShell
316    treats such code as a top-level <tt class="function">main</tt> method and
317    begins execution with it.
318  </p><p>
319    Our analysis of <tt class="filename">Add_Prefix_and_Suffix.bsh</tt> is now
320    complete. In the next section, we look at other ways in which a macro
321    can obtain user input, as well as other macro writing techniques.
322  </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>