PageRenderTime 55ms CodeModel.GetById 45ms app.highlight 4ms RepoModel.GetById 1ms app.codeStats 0ms

/jEdit/tags/jedit-4-0-pre5/doc/users-guide/dialog-macro.xml

#
XML | 642 lines | 538 code | 84 blank | 20 comment | 0 complexity | 1616cb02b5fb9e703743ace0c2b95801 MD5 | raw file
  1<!-- jEdit 3.2 Macro Guide, (C) 2001 John Gellene                       -->
  2<!-- jEdit buffer-local properties:                                     -->
  3<!-- :indentSize=1:noTabs=yes:maxLineLen=72:tabSize=2:                  -->
  4
  5<!-- Wed Jun 20 16:45:29 EDT 2001 @906 /Internet Time/                  -->
  6
  7<!-- This file contains an extended discussion of a                     -->
  8<!-- dialog-based macro example "Add_Prefix_and_Suffix.bsh"             -->
  9
 10<!-- Note: macro has been revised to accomodate Selection API in jEdit3.2 -->
 11
 12<chapter id="dialog-macro"><title>A Dialog-Based Macro</title>
 13 <para>
 14  Now we will look at a more complicated macro which will demonstrate
 15  some useful techniques and BeanShell features.
 16 </para>
 17
 18 <sect1 id="dialog-macro-intro"><title>Use of the Macro</title>
 19  <para>
 20    Our new example adds prefix and suffix text to a series of selected lines. This
 21    macro can be used to reduce typing for a series of text items that must be
 22    preceded and following by identical text. In Java, for example, if we are
 23    interested in making a series of calls to
 24    <function>StringBuffer.append()</function> to construct a lengthy, formatted
 25    string, we could type the parameter for each call on successive lines as follows:
 26  </para>
 27
 28<screen>profileString_1
 29secretThing.toString()
 30name
 31address
 32addressSupp
 33city
 34<quote>state/province</quote>
 35country</screen>
 36
 37   <para>
 38    Our macro would ask for input for the common <quote>prefix</quote> and
 39    <quote>suffix</quote> to be applied to each line; in this case, the prefix is
 40    <userinput>ourStringBuffer.append(</userinput> and the suffix is
 41    <userinput>);</userinput>. After selecting these lines and
 42    running the macro, the the resulting text would look like this:
 43   </para>
 44
 45<screen>ourStringBuffer.append(profileString_1);
 46ourStringBuffer.append(secretThing.toString());
 47ourStringBuffer.append(name);
 48ourStringBuffer.append(address);
 49ourStringBuffer.append(addressSupp);
 50ourStringBuffer.append(city);
 51ourStringBuffer.append(<quote>state/province</quote>);
 52ourStringBuffer.append(country);</screen>
 53 </sect1>
 54
 55<sect1 id="add-prefix-and-suffix"><title>Listing of the Macro</title>
 56
 57   <para>
 58    The macro script follows. You can find it in the jEdit
 59    distribution in the <filename>Text</filename> subdirectory of the
 60    <filename>macros</filename> directory. You can also try it out by invoking
 61    <guimenu>Macros</guimenu>&gt;<guisubmenu>Text</guisubmenu>&gt;<guimenuitem>Add
 62    Prefix and Suffix</guimenuitem>.
 63   </para>
 64
 65<informalexample>
 66<!-- <title>Add_Prefix_and_Suffix.bsh</title> -->
 67<programlisting>// beginning of Add_Prefix_and_Suffix.bsh
 68
 69<anchor id="imports"/>// import statement (see <xref linkend=
 70"explain-imports"/>)
 71import javax.swing.border.*;
 72
 73<anchor id="main-routine"/>// main routine
 74void prefixSuffixDialog()
 75{
 76<anchor id="create-dialog"/>    // create dialog object (see <xref
 77 linkend="explain-create-dialog"/>)
 78    title = <quote>Add prefix and suffix to selected lines</quote>;
 79    dialog = new JDialog(view, title, false);
 80    content = new JPanel(new BorderLayout());
 81    content.setBorder(new EmptyBorder(12, 12, 12, 12));
 82    content.setPreferredSize(new Dimension(320, 160));
 83    dialog.setContentPane(content);
 84
 85<anchor id="fields-panel"/>    // add the text fields (see <xref linkend=
 86"explain-fields-panel"/>)
 87    fieldPanel = new JPanel(new GridLayout(4, 1, 0, 6));
 88    prefixField = new HistoryTextField(<quote>macro.add-prefix</quote>);
 89    prefixLabel = new JLabel(<quote>Prefix to add:</quote>);
 90    suffixField = new HistoryTextField(<quote>macro.add-suffix</quote>);
 91    suffixLabel = new JLabel(<quote>Suffix to add:</quote>);
 92    fieldPanel.add(prefixLabel);
 93    fieldPanel.add(prefixField);
 94    fieldPanel.add(suffixLabel);
 95    fieldPanel.add(suffixField);
 96    content.add(fieldPanel, <quote>Center</quote>);
 97
 98<anchor id="button-panel"/>    // add the buttons (see <xref
 99 linkend="explain-button-panel"/>)
100    buttonPanel = new JPanel();
101    buttonPanel.setLayout(new BoxLayout(buttonPanel,
102        BoxLayout.X_AXIS));
103    buttonPanel.setBorder(new EmptyBorder(12, 50, 0, 50));
104    buttonPanel.add(Box.createGlue());
105    ok = new JButton(<quote>OK</quote>);
106    cancel = new JButton(<quote>Cancel</quote>);
107    ok.setPreferredSize(cancel.getPreferredSize());
108    dialog.getRootPane().setDefaultButton(ok);
109    buttonPanel.add(ok);
110    buttonPanel.add(Box.createHorizontalStrut(6));
111    buttonPanel.add(cancel);
112    buttonPanel.add(Box.createGlue());
113    content.add(buttonPanel, <quote>South</quote>);
114
115<anchor id="add-listeners"/>    // register this method as an ActionListener for
116    // the buttons and text fields (see <xref linkend=
117"explain-add-listeners"/>)
118    ok.addActionListener(this);
119    cancel.addActionListener(this);
120    prefixField.addActionListener(this);
121    suffixField.addActionListener(this);
122
123<anchor id="set-visible"/>    // locate the dialog in the center of the
124    // editing pane and make it visible (see <xref linkend=
125"explain-set-visible"/>)
126    dialog.pack();
127    dialog.setLocationRelativeTo(view);
128    dialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
129    dialog.setVisible(true);
130
131<anchor id="action-listener"/>    // this method will be called when a button is clicked
132    // or when ENTER is pressed (see <xref linkend=
133"explain-action-listener"/>)
134    void actionPerformed(e)
135    {
136        if(e.getSource() != cancel)
137        {
138            processText();
139        }
140        dialog.dispose();
141    }
142
143<anchor id="process-text"/>    // this is where the work gets done to insert
144    // the prefix and suffix (see <xref linkend=
145"explain-process-text"/>)
146    void processText()
147    {
148        prefix = prefixField.getText();
149        suffix = suffixField.getText();
150        if(prefix.length() == 0 &amp;&amp; suffix.length() == 0)
151            return;
152        prefixField.addCurrentToHistory();
153        suffixField.addCurrentToHistory();
154
155<anchor id="jEdit-calls"/>        // text manipulation begins here using calls
156        // to jEdit methods  (see <xref linkend="explain-jedit-calls"/>)
157        selectedLines = textArea.getSelectedLines();
158        for(i = 0; i &lt; selectedLines.length; ++i)
159        {
160            offsetBOL = textArea.getLineStartOffset(
161                selectedLines[i]);
162            textArea.setCaretPosition(offsetBOL);
163            textArea.goToStartOfWhiteSpace(false);
164            textArea.goToEndOfWhiteSpace(true);
165            text = textArea.getSelectedText();
166            if(text == null) text = &quot;&quot;;
167            textArea.setSelectedText(prefix + text + suffix);
168        }
169    }
170}
171
172<anchor id="main"/>// this single line of code is the script's main routine
173// (see <xref linkend=
174"explain-main"/>)
175prefixSuffixDialog();
176
177// end of Add_Prefix_and_Suffix.bsh</programlisting>
178</informalexample>
179</sect1>
180
181<sect1 id="macro-analysis"><title>Analysis of the Macro</title>
182
183<sect2 id="explain-imports"><title>Import Statements</title>
184
185<informalexample><programlisting>// import statement
186import javax.swing.border.*;</programlisting></informalexample>
187
188  <para>
189    This macro makes use of classes in the
190    <literal>javax.swing.border</literal> package, which is not
191    automatically imported. As we mentioned
192    previously (see <xref linkend="first-example"/>), jEdit's implementation of
193    BeanShell causes a number of classes to be automatically imported. Classes
194    that are not automatically imported must be named by a full qualified name or be
195    the subject of an <function>import</function> statement.
196  </para>
197
198</sect2>
199
200<sect2 id="explain-create-dialog"><title>Create the Dialog</title>
201
202<informalexample><programlisting>// create dialog object
203title = <quote>Add prefix and suffix to selected lines</quote>;
204dialog = new JDialog(view, title, false);
205content = new JPanel(new BorderLayout());
206content.setBorder(new EmptyBorder(12, 12, 12, 12));
207dialog.setContentPane(content);</programlisting></informalexample>
208
209  <para>
210    To get input for the macro, we need a dialog that provides for input of the prefix
211    and suffix strings, an <guibutton>OK</guibutton> button to perform text
212    insertion, and a <guibutton>Cancel</guibutton> button in case we change our
213    mind. We have decided to make the dialog window non-modal. This will allow us to
214    move around in the text buffer to find things we may need (including text to cut
215    and paste) while the macro is running and the dialog is visible.
216  </para>
217
218  <para>
219    The Java object we need is a <classname>JDialog</classname> object from
220    the Swing package. To construct one, we use the <function>new</function>
221    keyword and call a <glossterm>constructor</glossterm> function. The
222    constructor we use takes three parameters: the owner of the new dialog,
223    the title to be displayed in the dialog frame, and a
224    <type>boolean</type> parameter (<constant>true</constant> or
225    <constant>false</constant>) that specifies whether the dialog will be
226    modal or non-modal. We define the variable <varname>title</varname>
227    using a string literal, then use it immediately in the
228    <classname>JDialog</classname> constructor.
229  </para>
230
231  <para>
232    A <classname>JDialog</classname> object is a window containing a single object
233    called a <glossterm>content pane</glossterm>. The content pane in turn contains
234    the various visible components of the dialog. A
235    <classname>JDialog</classname> creates an empty content pane for itself as
236    during its construction.  However, to control the dialog's appearance
237    as much as possible, we will separately create our own content pane and
238    attach it to the <classname>JDialog</classname>. We do this by creating a
239    <classname>JPanel</classname> object. A <classname>JPanel</classname> is a
240    lightweight container for other components that can be set to a given size and
241    color. It also contains a <glossterm>layout</glossterm> scheme for arranging the
242    size and position of its components. Here we are constructing a
243    <classname>JPanel</classname> as a content pane with a
244    <classname>BorderLayout</classname>. We put a <classname>EmptyBorder</classname>
245    inside it to serve as a margin between the edge of the window and the components
246    inside. We then attach the <classname>JPanel</classname> as the dialog's content
247    pane, replacing the dialog's home-grown version.
248  </para>
249
250  <para>
251    A <classname>BorderLayout</classname> is one of the simpler layout
252    schemes available for Java Swing objects.
253    A <classname>BorderLayout</classname>
254    divides the container into five sections: <quote>North</quote>,
255    <quote>South</quote>, <quote></quote>East, <quote>West</quote> and
256    <quote>Center</quote>. Components are added to the layout using the
257    container's <function>add</function> method, specifying the component to
258    be added and the section to which it is assigned. Building a
259    component like our dialog window involves building a set of
260    nested containers and specifying the location of each of their
261    member components. We have taken the first step by creating a
262    <classname>JPanel</classname> as the dialog's content pane.
263  </para>
264
265</sect2>
266
267<sect2 id="explain-fields-panel"><title>
268Create the Text Fields</title>
269
270<informalexample><programlisting>// add the text fields
271fieldPanel = new JPanel(new GridLayout(4, 1, 0, 6));
272prefixField = new HistoryTextField(&quot;macro.add-prefix&quot;);
273prefixLabel = new JLabel(<quote>Prefix to add</quote>:);
274suffixField = new HistoryTextField(<quote>macro.add-suffix</quote>);
275suffixLabel = new JLabel(<quote>Suffix to add:</quote>);
276fieldPanel.add(prefixLabel);
277fieldPanel.add(prefixField);
278fieldPanel.add(suffixLabel);
279fieldPanel.add(suffixField);
280content.add(fieldPanel, <quote>Center</quote>);</programlisting></informalexample>
281
282  <para>
283    Next we shall create a smaller panel containing two fields for
284    entering the prefix and suffix text and two labels identfying the
285    input fields.
286  </para>
287
288  <para>
289    For the text fields, we will use jEdit's <classname>HistoryTextField</classname>
290    class. It is derived from the Java Swing class
291    <classname>JTextField</classname>. This class offers the enhancement of a stored
292    list of prior values used as text input. The up and down keys scroll through the
293    prior values for the variable. <!-- The prior values are stored in a file named
294    <filename>history</filename> located in the directory in which jEdit stores
295    various user data. -->
296  </para>
297
298  <para>
299    To create the <classname>HistoryTextField</classname> objects we use a
300    constructor method that takes a single parameter: the name of the tag
301    under which history values will be stored. Here we choose names that are
302    not likely to conflict with existing jEdit history items.
303  </para>
304
305  <para>
306    The labels are <classname>JLabel</classname> objects from the Java Swing
307    package. The constructor we use takes the label text as a single
308    <classname>String</classname> parameter.
309  </para>
310
311  <para>
312    We wish to arrange these four components from top to bottom,
313    one after the other. To achieve that, we use a
314    <classname>JPanel</classname> object named <varname>fieldPanel</varname> that
315    will be nested inside the dialog's content pane that we have
316    already created. In the constructor for <varname>fieldPanel</varname>,
317    we assign a new <classname>GridLayout</classname> with the indicated
318    parameters: four rows, one column, zero spacing between columns (a
319    meaningless element of a grid with only one column, but
320    nevertheless a required parameter) and spacing of six pixels between
321    rows. The spacing between rows spreads out the four <quote>grid</quote>
322    elements. After the components, the panel and the layout are
323    specified, the components are added to <varname>fieldPanel</varname>
324    top to bottom, one <quote>grid cell</quote> at a time. Finally, the complete
325    <varname>fieldPanel</varname> is added to the dialog's content pane to
326    occupy the <quote>Center</quote> section of the content pane.
327  </para>
328</sect2>
329
330<sect2 id="explain-button-panel"><title>
331Create the Buttons</title>
332
333<informalexample><programlisting>// add the buttons
334buttonPanel = new JPanel();
335buttonPanel.setLayout(new BoxLayout(buttonPanel,
336    BoxLayout.X_AXIS));
337buttonPanel.setBorder(new EmptyBorder(12, 50, 0, 50));
338buttonPanel.add(Box.createGlue());
339ok = new JButton(<quote>OK</quote>);
340cancel = new JButton(<quote>Cancel</quote>);
341ok.setPreferredSize(cancel.getPreferredSize());
342dialog.getRootPane().setDefaultButton(ok);
343buttonPanel.add(ok);
344buttonPanel.add(Box.createHorizontalStrut(6));
345buttonPanel.add(cancel);
346buttonPanel.add(Box.createGlue());
347content.add(buttonPanel, <quote>South</quote>);</programlisting></informalexample>
348
349  <para>
350    Creating the buttons repeats the pattern we used in creating the text fields.
351    First, we create a new, nested panel with a <classname>BoxLayout</classname>. A
352    <classname>BoxLayout</classname> places components either in a single row or
353    column, depending on the parameter passed to its constructor. We put an
354    <classname>EmptyBorder</classname> in the new panel to set margins for placing
355    the buttons. Then we create the buttons, using a <classname>JButton</classname>
356    constructor that specifies the button text. After setting the size of the
357    <guilabel>OK</guilabel> button to equal the size of the
358    <guilabel>Cancel</guilabel> button, we designate the <guilabel>OK</guilabel>
359    button as the default button in the dialog. This causes the
360    <guilabel>OK</guilabel> button to be outlined as the default button. Finally, we
361    place the button side by side with a 6 pixel gap between them (for aesthetic
362    reasons), and place the completed <varname>buttonPanel</varname> in the
363    <quote>South</quote> section of the dialog's content pane.
364  </para>
365</sect2>
366
367<sect2 id="explain-add-listeners">
368<title>Register the Action Listeners</title>
369
370<informalexample><programlisting>// register this method as an ActionListener for
371// the buttons and text fields
372ok.addActionListener(this);
373cancel.addActionListener(this);
374prefixField.addActionListener(this);
375suffixField.addActionListener(this);</programlisting></informalexample>
376
377  <para>
378    In order to specify the action to be taken upon clicking a
379    button or pressing the <keycap>Enter</keycap> key, we must register
380    an <classname>ActionListener</classname> for each of the four active
381    components of the dialog - the two <classname>HistoryTextField</classname>
382    components and the two buttons. In Java, an
383    <classname>ActionListener</classname> is an <glossterm>interface</glossterm> - an
384    abstract specification for a derived class to implement. The
385    <classname>ActionListener</classname> interface contains a single method to
386    be implemented:
387  </para>
388
389<funcsynopsis>
390 <funcprototype>
391  <funcdef>public void <function>actionPerformed</function></funcdef>
392  <paramdef>ActionEvent <parameter>e</parameter></paramdef>
393 </funcprototype>
394</funcsynopsis>
395
396  <para>
397    BeanShell does not permit a script to create derived classes.
398    However, BeanShell offers a useful substitute: a method can be
399    used as a scripted object that can implement methods of a
400    number of Java interfaces. The method
401    <function>prefixSuffixDialog()</function> that we are writing can thus be
402    treated as an <function>ActionListener</function>. To accomplish this, we
403    call <function>addActionListener()</function> on each of the four
404    components specifying <varname>this</varname> as the
405    <classname>ActionListener</classname>. We still need to implement the
406    interface. We will do that shortly.
407  </para>
408
409</sect2>
410
411<sect2 id="explain-set-visible">
412<title>Make the Dialog Visible</title>
413
414<informalexample><programlisting>// locate the dialog in the center of the
415// editing pane and make it visible
416dialog.pack();
417dialog.setLocationRelativeTo(view);
418dialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
419dialog.setVisible(true);</programlisting></informalexample>
420
421  <para>
422    Here we do three things. First, we activate all the layout routines we have
423    established by calling the <function>pack()</function> method for the dialog as
424    the top-level window. Next we center the dialog's position in the active jEdit
425    <varname>view</varname> by calling <function>setLocationRelativeTo()</function>
426    on the dialog. We also call the <function>setDefaultCloseOperation()</function>
427    function to specify that the dialog box should be immediately disposed if the
428    user clicks the close box. Finally, we activate the dialog by calling
429    <function>setVisible()</function>with the state parameter set to
430    <constant>true</constant>.
431  </para>
432
433  <para>
434    At this point we have a decent looking dialog window that
435    doesn't do anything. Without more code, it will not respond to
436    user input and will not accomplish any text manipulation. The
437    remainder of the script deals with these two requirements.
438  </para>
439
440</sect2>
441
442<sect2 id="explain-action-listener">
443<title>The Action Listener</title>
444
445<informalexample><programlisting>// this method will be called when a button is clicked
446// or when ENTER is pressed
447void actionPerformed(e)
448{
449    if(e.getSource() != cancel)
450    {
451        processText();
452    }
453    dialog.dispose();
454}</programlisting></informalexample>
455
456  <para>
457    The method <function>actionPerformed()</function> nested inside
458    <function>prefixSuffixDialog()</function> implements the implicit
459    <classname>ActionListener</classname> interface. It looks at the source
460    of the <classname>ActionEvent</classname>, determined by a call to
461    <function>getSource()</function>. What we do with this return value is
462    straighforward: if the source is not the <guibutton>Cancel</guibutton> button, we
463    call the <function>processText()</function> method to insert the prefix
464    and suffix text. Then the dialog is closed by calling its
465    <function>dispose()</function> method.
466  </para>
467
468  <para>
469    The ability to implement interfaces like
470    <classname>ActionListener</classname> inside a BeanShell script is
471    one of the more powerful features of the BeanShell package. With an
472    <classname>ActionListener</classname> interface, which has only a
473    single method, implementation is simple.  When using other
474    interfaces with multiple methods, however, there are some details to
475    deal with that will vary depending on the version of the Java
476    platform that you are running.  These techniques are discussed in
477    the next chapter; see <xref linkend="macro-tips-BeanShell-interface" />.
478  </para>
479
480</sect2>
481
482<sect2 id="explain-process-text">
483<title>Get the User's Input</title>
484
485<informalexample><programlisting>// this is where the work gets done to insert
486// the prefix and suffix
487void processText()
488{
489    prefix = prefixField.getText();
490    suffix = suffixField.getText();
491    if(prefix.length() == 0 &amp;&amp; suffix.length() == 0)
492        return;
493    prefixField.addCurrentToHistory();
494    suffixField.addCurrentToHistory();</programlisting></informalexample>
495
496  <para>
497    The method <function>processText()</function> does the work of our
498    macro. First we obtain the input from the two text fields with a
499    call to their <function>getText()</function> methods. If they are both
500    empty, there is nothing to do, so the method returns. If there is
501    input, any text in the field is added to that field's stored
502    history list by calling <function>addCurrentToHistory()</function>.
503    We do not need to test the <varname>prefixField</varname> or
504    <varname>suffixField</varname> controls for <constant>null</constant>
505    or empty values because <function>addCurrentToHistory()</function>
506    does that internally.
507  </para>
508
509</sect2>
510
511<sect2 id="explain-jedit-calls">
512<title>Call jEdit Methods to Manipulate Text</title>
513
514<informalexample><programlisting>    // text manipulation begins here using calls
515    // to jEdit methods
516    selectedLines = textArea.getSelectedLines();
517    for(i = 0; i &lt; selectedLines.length; ++i)
518    {
519        offsetBOL = textArea.getLineStartOffset(
520            selectedLines[i]);
521        textArea.setCaretPosition(offsetBOL);
522        textArea.goToStartOfWhiteSpace(false);
523        textArea.goToEndOfWhiteSpace(true);
524        text = textArea.getSelectedText();
525        if(text == null) text = &quot;&quot;;
526        textArea.setSelectedText(prefix + text + suffix);
527    }
528}</programlisting></informalexample>
529
530  <para>
531    The text manipulation routine loops through each selected line
532    in the text buffer. We get the loop parameters by calling
533    <function>textArea.getSelectedLines()</function>, which returns an array
534    consisting of the line numbers of every selected line.  The array includes the
535    number of the current line, whether or not it is selected, and the line numbers
536    are sorted in increasing order.  We iterate through each member of the
537    <varname>selectedLines</varname> array, which represents the number of a
538    selected line, and apply the following routine:
539  </para>
540
541<itemizedlist>
542
543  <listitem>
544    <para>
545      Get the buffer position of the start of the line (expressed
546      as a zero-based index from the start of the buffer) by calling
547      <function>textArea.getLineStartOffset(selectedLines[i])</function>;
548    </para>
549  </listitem>
550
551  <listitem>
552    <para>
553      Move the caret to that position by calling
554      <function>textArea.setCaretPosition()</function>;
555    </para>
556  </listitem>
557
558  <listitem>
559    <para>
560      Find the first and last non-whitespace characters on the line
561      by calling <function>textArea.goToStartOfWhiteSpace()</function> and
562      <function>textArea.goToEndOfWhiteSpace()</function>;
563    </para>
564
565    <para>
566      The <function>goTo...</function> methods in
567      <classname>JEditTextArea</classname> take a single parameter which
568      tells jEdit whether the text between the current caret position and
569      the desired position should be selected. Here, we call
570      <function>textArea.goToStartOfWhiteSpace(false)</function> so that
571      no text is selected, then call
572      <function>textArea.goToEndOfWhiteSpace(true)</function> so that all of
573      the text between the beginning and ending whitespace is
574      selected.
575    </para>
576  </listitem>
577
578  <listitem>
579    <para>
580      Retrieve the selected text by storing the return value of
581      <function>textArea.getSelectedText()</function> in a new variable
582      <function>text</function>.
583    </para>
584
585    <para>
586      If the line is empty, <function>getSelectedText()</function> will
587      return <constant>null</constant>. In that case, we assign an empty
588      string to <varname>text</varname> to avoid calling methods on a
589      null object.
590    </para>
591  </listitem>
592
593  <listitem>
594    <para>
595      Change the selected text to <varname>prefix + text +
596      suffix</varname> by calling
597      <function>textArea.setSelectedText()</function>.
598      If there is no selected text (for example, if the line is empty),
599      the prefix and suffix will be inserted without any intervening
600      characters.
601    </para>
602  </listitem>
603
604</itemizedlist>
605
606  <!-- <para>
607    The loop routine is bracketed by calls to
608    <function>buffer.beginCompoundEdit()</function> and
609    <function>buffer.endCompoundEdit()</function>.  These methods
610    modify the way in which jEdit's undo facility performs.  Text
611    operations done between calls to these functions will be
612    treated as a single operation for undo purposes.  A single undo
613    command issued immediately after the macro completes will thus remove
614    the prefix and suffix text from all of the previously selected lines.
615  </para> -->
616
617</sect2>
618
619<sect2 id="explain-main">
620<title>The Main Routine</title>
621
622<informalexample><programlisting>// this single line of code is the script's main routine
623prefixSuffixDialog();</programlisting>
624</informalexample>
625
626  <para>
627    The call to <function>prefixSuffixDialog()</function>is the only line
628    in the macro that is not inside an enclosing block. BeanShell
629    treats such code as a top-level <function>main</function> method and
630    begins execution with it.
631  </para>
632
633  <para>
634    Our analysis of <filename>Add_Prefix_and_Suffix.bsh</filename> is now
635    complete. In the next section, we look at other ways in which a macro
636    can obtain user input, as well as other macro writing techniques.
637  </para>
638
639</sect2>
640</sect1>
641
642</chapter>