PageRenderTime 649ms CodeModel.GetById 13ms RepoModel.GetById 0ms app.codeStats 0ms

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

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