The dialog-based macro discussed in Chapter 14, A Dialog-Based Macro reflects a conventional approach to obtaining input in a Java program. Nevertheless, it can be too lengthy or tedious for someone trying to write a macro quickly. Not every macro needs a user interface specified in such detail; some macros require only a single keystroke or no input at all. In this section we outline some other techniques for obtaining input that will help you write macros quickly.
As mentioned earlier in the section called “Helpful Methods in the Macros Class”,
            the method Macros.input() offers a convenient
            way to obtain a single line of text input. Here is an example that
            inserts a pair of HTML markup tags specified by the user.
// Insert_Tag.bsh
void insertTag()
{
    caret = textArea.getCaretPosition();
    tag = Macros.input(view, “Enter name of tag:”);
    if( tag == null || tag.length() == 0) return;
    text = textArea.getSelectedText();
    if(text == null) text = “”;
    sb = new StringBuffer();
    sb.append(“<”).append(tag).append(“>”);
    sb.append(text);
    sb.append(“</”).append(tag).append(“>”);
    textArea.setSelectedText(sb.toString());
    if(text.length() == 0)
        textArea.setCaretPosition(caret + tag.length() + 2);
}
insertTag();
// end Insert_Tag.bshHere the call to Macros.input() seeks the
            name of the markup tag. This method sets the message box title to a
            fixed string, “Macro input”, but the specific message
            Enter name of tag provides all the information
            necessary. The return value tag must be tested to
            see if it is null. This would occur if the user presses the
            Cancel button or closes the dialog window
            displayed by Macros.input().
If more than one item of input is needed, a succession of
            calls to Macros.input() is a possible, but
            awkward approach, because it would not be possible to correct early
            input after the corresponding message box is dismissed. Where more
            is required, but a full dialog layout is either unnecessary or too
            much work, the Java method
            JOptionPane.showConfirmDialog() is available.
            The version to use has the following prototype:
| public static int
                            showConfirmDialog( | Component parentComponent, | 
| Object message, | |
| String title, | |
| int optionType, | |
| int
                            messageType ); | 
The usefulness of this method arises from the fact that the
            message parameter can be an object of any Java
            class (since all classes are derived from
            Object), or any array of objects. The
            following example shows how this feature can be used.
// excerpt from Write_File_Header.bsh title = “Write file header”; currentName = buffer.getName(); nameField = new JTextField(currentName); authorField = new JTextField(“Your name here”); descField = new JTextField(“”, 25); namePanel = new JPanel(new GridLayout(1, 2)); nameLabel = new JLabel(“Name of file:”, SwingConstants.LEFT); saveField = new JCheckBox(“Save file when done”, !buffer.isNewFile()); namePanel.add(nameLabel); namePanel.add(saveField); message = new Object[9]; message[0] = namePanel; message[1] = nameField; message[2] = Box.createVerticalStrut(10); message[3] = “Author's name:”; message[4] = authorField; message[5] = Box.createVerticalStrut(10); message[6] = “Enter description:”; message[7] = descField; message[8] = Box.createVerticalStrut(5); if( JOptionPane.OK_OPTION != JOptionPane.showConfirmDialog(view, message, title, JOptionPane.OK_CANCEL_OPTION, JOptionPane.QUESTION_MESSAGE)) return null; // *****remainder of macro script omitted***** // end excerpt from Write_File_Header.bsh
This macro takes several items of user input and produces a formatted file header at the beginning of the buffer. The full macro is included in the set of macros installed by jEdit. There are a number of input features of this excerpt worth noting.
The macro uses a total of seven visible components.
                    Two of them are created behind the scenes by
                    showConfirmDialog(), the rest are made
                    by the macro. To arrange them, the script creates an array
                    of Object objects and assigns
                    components to each location in the array. This translates to
                    a fixed, top-to-bottom arrangement in the message box
                    created by showConfirmDialog().
The macro uses JTextField
                    objects to obtain most of the input data. The fields
                    nameField and
                    authorField are created with constructors
                    that take the initial, default text to be displayed in the
                    field as a parameter. When the message box is displayed, the
                    default text will appear and can be altered or deleted by
                    the user.
The text field descField uses an
                    empty string for its initial value. The second parameter in
                    its constructor sets the width of the text field component,
                    expressed as the number of characters of
                    “average” width. When
                    showConfirmDialog() prepares the layout
                    of the message box, it sets the width wide enough to
                    accommodate the designated with of
                    descField. This technique produces a
                    message box and input text fields that are wide enough for
                    your data with one line of code.
The displayed message box includes a
                    JCheckBox component that determines
                    whether the buffer will be saved to disk immediately after
                    the file header is written. To conserve space in the message
                    box, we want to display the check box to the right of the
                    label Name of file:. To do that, we
                    create a JPanel object and populate
                    it with the label and the checkbox in a left-to-right
                    GridLayout. The
                    JPanel containing the two components
                    is then added to the beginning of message
                    array.
The two visible components created by
                    showConfirmDialog() appear at positions
                    3 and 6 of the message array. Only the
                    text is required; they are rendered as text labels.
There are three invisible components created by
                    showConfirmDialog(). Each of them
                    involves a call to
                    Box.createVerticalStrut(). The
                    Box class is a sophisticated layout
                    class that gives the user great flexibility in sizing and
                    positioning components. Here we use a
                    static method of the
                    Box class that produces a vertical
                    strut. This is a transparent
                    component whose width expands to fill its parent component
                    (in this case, the message box). The single parameter
                    indicates the height of the strut in pixels. The last call
                    to createVerticalStrut() separates the
                    description text field from the OK and
                    Cancel buttons that are automatically
                    added by showConfirmDialog().
Finally, the call to
                    showConfirmDialog() uses defined
                    constants for the option type and the message type. The
                    constants are the same as those used with the
                    Macros.confirm() method; see the section called “Helpful Methods in the Macros Class”. The option type signifies the
                    use of OK and
                    Cancel buttons. The
                    QUERY_MESSAGE message type causes the
                    message box to display a question mark icon.
The return value of the method is tested against the
                    value OK_OPTION. If the return value is
                    something else (because the Cancel
                    button was pressed or because the message box window was
                    closed without a button press), a null
                    value is returned to a calling function, signaling that the
                    user canceled macro execution. If the return value is
                    OK_OPTION, each of the input components
                    can yield their contents for further processing by calls to
                    JTextField.getText() (or, in the case
                    of the check box,
                    JCheckBox.isSelected()).
Another useful way to get user input for a macro is to use a
            combo box containing a number of pre-set options. If this is the
            only input required, one of the versions of
            showInputDialog() in the
            JOptionPane class provides a shortcut. Here
            is its prototype:
| public static Object
                            showInputDialog( | Component parentComponent, | 
| Object message, | |
| String title, | |
| int messageType, | |
| Icon icon, | |
| Object[] selectionValues, | |
| Object
                            initialSelectionValue ); | 
This method creates a message box containing a drop-down list
            of the options specified in the method's parameters, along with
            OK and Cancel buttons.
            Compared to showConfirmDialog(), this method
            lacks an optionType parameter and has three
            additional parameters: an icon to display in the
            dialog (which can be set to null), an array of
            selectionValues objects, and a reference to one
            of the options as the initialSelectionValue to be
            displayed. In addition, instead of returning an
            int representing the user's action,
            showInputDialog() returns the
            Object corresponding to the user's selection,
            or null if the selection is canceled.
The following macro fragment illustrates the use of this method.
// fragment illustrating use of showInputDialog()
options = new Object[5];
options[0] = "JLabel";
options[1] = "JTextField";
options[2] = "JCheckBox";
options[3] = "HistoryTextField";
options[4} = "-- other --";
result = JOptionPane.showInputDialog(view,
    "Choose component class",
    "Select class for input component",
    JOptionPane.QUESTION_MESSAGE,
    null, options, options[0]);The return value result will contain either
            the String object representing the selected
            text item or null representing no selection.
            Any further use of this fragment would have to test the value of
            result and likely exit from the macro if the
            value equaled null.
A set of options can be similarly placed in a
            JComboBox component created as part of a
            larger dialog or showMessageDialog() layout.
            Here are some code fragments showing this approach:
// fragments from Display_Abbreviations.bsh // import statements and other code omitted // from main routine, this method call returns an array // of Strings representing the names of abbreviation sets abbrevSets = getActiveSets(); ... // from showAbbrevs() method combo = new JComboBox(abbrevSets); // set width to uniform size regardless of combobox contents Dimension dim = combo.getPreferredSize(); dim.width = Math.max(dim.width, 120); combo.setPreferredSize(dim); combo.setSelectedItem(STARTING_SET); // defined as "global" // end fragments
Some macros may choose to emulate the style of character-based text editors such as emacs or vi. They will require only a single keypress as input that would be handled by the macro but not displayed on the screen. If the keypress corresponds to a character value, jEdit can pass that value as a parameter to a BeanShell script.
The jEdit class InputHandler is an abstract class that that manages associations between keyboard input and editing actions, along with the recording of macros. Keyboard input in jEdit is normally managed by the derived class DefaultInputHandler. One of the methods in the InputHandler class handles input from a single keypress:
| public void
                            readNextChar( | String prompt, | 
| String
                            code ); | 
When this method is called, the contents of the
            prompt parameter is shown in the view's status
            bar. The method then waits for a key press, after which the contents
            of the code parameter will be run as a BeanShell
            script, with one important modification. Each time the string
            __char__ appears in the parameter script, it will
            be substituted by the character pressed. The key press is
            “consumed” by readNextChar(). It
            will not be displayed on the screen or otherwise processed by
            jEdit.
Using readNextChar() requires a macro
            within the macro, formatted as a single, potentially lengthy string
            literal. The following macro illustrates this technique. It selects
            a line of text from the current caret position to the first
            occurrence of the character next typed by the user. If the character
            does not appear on the line, no new selection occurs and the display
            remains unchanged.
// Next_Char.bsh
script = new StringBuffer(512);
script.append( "start = textArea.getCaretPosition();"         );
script.append( "line = textArea.getCaretLine();"              );
script.append( "end = textArea.getLineEndOffset(line) + 1;"   );
script.append( "text = buffer.getText(start, end - start);"   );
script.append( "match = text.indexOf(__char__, 1);"           );
script.append( "if(match != -1) {"                            );
script.append(   "if(__char__ != '\\n') ++match;"             );
script.append(   "textArea.select(start, start + match - 1);" );
script.append( "}"                                            );
view.getInputHandler().readNextChar("Enter a character",
    script.toString());
// end Next_Char.bshOnce again, here are a few comments on the macro's design.
A StringBuffer object is used
                    for efficiency; it obviates multiple creation of
                    fixed-length String objects. The
                    parameter to the constructor of script
                    specifies the initial size of the buffer that will receive
                    the contents of the child script.
Besides the quoting of the script code, the formatting of the macro is entirely optional but (hopefully) makes it easier to read.
It is important that the child script be
                    self-contained. It does not run in the same namespace as the
                    “parent” macro
                    Next_Char.bsh and therefore does not
                    share variables, methods, or scripted objects defined in the
                    parent macro.
Finally, access to the InputHandler
                    object used by jEdit is available by calling
                    getInputHandler() on the current
                    view.