Edit Menu

With the File menu completed, the next major menu item is the Edit menu. In the Editor2 app, I created this menu with the three common commands: Cut, Copy, and Paste. Just for good measure, I added one further menu item, Format, which has the single submenu Font.

Forms Designer Work

To add the Edit menu, in the Forms Designer click in the Type Here box next to the File menu. From there, add the Cut, Copy, and Paste menu items below the Edit menu. In the Properties window, change the name property of these menu items to cutEditMI, copyEditMI, and pasteEditMI to match the naming convention we used with the File menu items. While changing the names, you can also set the shortcut property for these properties to the standard Ctrl+X, Ctrl+C, and Ctrl+V, respectively.

You can add the Format menu with the Font menu item in a similar fashion; however, the Font menu item requires the addition of a FontDialog control. Drag a FontDialog control from the Toolbox and place it next to the OpenFileDialog and SaveFileDialog controls. Rename the FontDialog control to fontDialog. Also add a status bar panel named fontPanel to the right of the existing panel to provide a place to display the current font selection.

Code for Editor2

The extra code to handle the edit and font menu items turns out to be a little trickier than you might think. (I have removed the File and RichEditControl sections from the following listing to save space, since they are the same as in the previous example.)

import com.ms.wfc.app.*;
import com.ms.wfc.core.*;
import com.ms.wfc.ui.*;
import com.ms.wfc.html.*;
import com.ms.wfc.io.*;
/**
 * This class represents a more complete RTF editor.
 */
public class Form1 extends Form
{
 // …the File menu section is identical to Editor1…
 private void cutEditMI_click(Object source, Event e)
 {
 // perform a copy
 copyEditMI_click(source, e);
 // now delete the selection by // replacing the selected RTF with nothing
 richEdit.setSelRTF("");
 }
 private void copyEditMI_click(Object source, Event e)
 {
 // get the selected text
 String rtf = richEdit.getSelRTF();
 // now put this in the Clipboard
 Clipboard.setDataObject(rtf, true);
 }
 private void pasteEditMI_click(Object source, Event e)
 {
 // get an object off the Clipboard that represents
 // its contents
 IDataObject dobj = Clipboard.getDataObject();
 // first check for RTF
 if (dobj.getDataPresent("Rich Text Format"))
 {
 // the getDataPresent() method can return true
 // even when the content is not RTF; if it's not
 // RTF, just catch the illegal format event and
 // keep going
 try
 {
 // get the RTF data as a string, and paste
 // it into the rich edit control
 String s = (String)dobj.getData("Rich Text Format");
 // the string that gets extracted from
 // the Clipboard is null terminated - trim()
 // sets the length to match the text up to
 // the null
 s = trim(s);
 // the resulting text is pasted into the rich edit box
 richEdit.setSelRTF(s);
 return;
 }
 catch(Exception ex)
 {
 }
 }
 // we can handle plain text (although it screws up
 // the format of the RTF data)
 if (dobj.getDataPresent("Text"))
 {
 // first get the text off of the Clipboard and
 // trim it to the terminating null
 String insert = (String)dobj.getData("Text");
 insert = trim(insert);
 // the resulting text is pasted into the rich edit box
 richEdit.setSelText(insert);
 return;
 }
 }
 /**
 * Trim a String off at the first null.
 */
 static String trim(String s)
 {
 for (int i = 0; i < s.length(); i++)
 {
 if (s.charAt(i) == '\0')
 {
 s = s.substring(0, i);
 break;
 }
 }
 return s;
 }
 private void fontFormatMI_click(Object source, Event e)
 {
 // display the font dialog box; if user selects OK…
 if (fontDialog.showDialog() == DialogResult.OK)
 {
 // get the font selected
 Font f = fontDialog.getFont();
 // set the selected area to that font
 richEdit.setSelFont(f);
 // store the name of the font on the status bar
 fontPanel.setText(f.getName());
 }
 }
 private void richEdit_selChange(Object source, Event e)
 {
 // every time the cursor position moves, get
 // the current font and store it in the status bar
 try
 {
 Font f = richEdit.getSelFont();
 fontPanel.setText(f.getName() + ":" + f.getSize());
 }
 catch(Exception ex)
 {
 fontPanel.setText("<mixed>");
 }
 }
 /**
 * NOTE: The following code is required by the Visual J Plus Plus form
 * designer. It can be modified using the form editor. Do not
 * modify it using the code editor.
 */
 Container components = new Container();
 MainMenu mainMenu = new MainMenu();
 MenuItem fileMI = new MenuItem();
 MenuItem newFileMI = new MenuItem();
 MenuItem openFileMI = new MenuItem();
 MenuItem saveFileMI = new MenuItem();
 MenuItem saveAsFileMI = new MenuItem();
 MenuItem exitFileMI = new MenuItem();
 OpenFileDialog openFD = new OpenFileDialog();
 SaveFileDialog saveFD = new SaveFileDialog();
 RichEdit richEdit = new RichEdit();
 StatusBar statusBar = new StatusBar();
 StatusBarPanel outputPanel = new StatusBarPanel();
 MenuItem menuItem1 = new MenuItem();
 MenuItem copyEditMI = new MenuItem();
 MenuItem cutEditMI = new MenuItem();
 MenuItem pasteEditMI = new MenuItem();
 MenuItem menuItem5 = new MenuItem();
 MenuItem fontFormatMI = new MenuItem();
 FontDialog fontDialog = new FontDialog();
 StatusBarPanel fontPanel = new StatusBarPanel();
 private void initForm()
 {
 // …created by the Forms Designer…
 }
 /**
 * The main entry point for the app. */
 public static void main(String args[])
 {
 app.run(new Form1(args));
 }
}


The new code starts out simple enough. I created the Copy handler by double-clicking the Copy menu item in the Forms Designer and then adding the necessary code. The new copyEditMI_click() method first calls richEdit.getSelRTF(), which returns the currently selected text in RTF format. If no text is selected, this call returns an empty string. The second call, to Clipboard.setDataObject(), pastes this text to the Clipboard. The method's second argument, true, tells the method to copy the text rather than to simply retain a reference to it. This makes the Clipboard data persistent even if the Editor2 app exits.

The cutEditMI_click() handler is even easier. First it performs a copy action by calling copyEditMI_click(). Then it uses setSelRTF() to delete the selected text by replacing the selected text with an empty string.

The pasteEditMI_click() method begins by calling Clipboard.getDataObject(). Like the data object contained within the in-progress drag-and-drop DragEvent, the Clipboard object represents the contents of the Clipboard. The Editor2 app is prepared to accept two types of Clipboard contents: RTF or ANSI text.

Since RTF is preferred, pasteEditMI_click() checks for the presence of RTF text by calling getDataPresent("Rich Text Format") first. This call is supposed to return true if RTF text is present on the Clipboard; however, it returns true when any text is on the Clipboard, whether it's in RTF format or not.

The call getData("Rich Text Format") returns the RTF text as a Java string. The string returned by getData() is actually a null terminated string. A null terminated string makes a lot of sense in a language like C++, but the null has no particular significance to Java. The length of the String object is the size of the buffer and extends beyond the null. The method RichEdit.setSelRTF() seems to be able to handle this problem without any ill effects, but to be safe I wrote the function trim() to set the length of the String object to include the text up to but not including the terminating null.

Once the string has been trimmed, it is pasted into the RichEdit object using the setSelRTF() call. If the string returned from the Clipboard isn't actually RTF data, setSelRTF() throws an exception. Since this data might be plain text, pasteEditMI_click() catches the exception and continues on without complaint. If setSelRTF() runs successfully, the function returns with no further processing.

As a second step, the pasteEditMI_click() method checks the Clipboard for the presence of "Text", that is, raw ANSI data. If this data is present, it is read from the Clipboard by calling getData("Text") and stored into the variable insert. (This allows the user to cut text out of a text editor such as Notepad and paste the text into our RTFEditor.) Once the insert variable has been trimmed, we paste it into the RichEdit object by calling the method RichEdit.setSelText().

The method fontFormatMI_click() handles the Font menu item by opening the FontDialog object. If the user exits the dialog box by choosing the OK button, fontFormatMI_click() reads the selected font from the dialog box by calling getFont(). The currently selected area within the rich edit control is set to this font by calling setSelFont(). The name of the font is then stored into the status bar panel fontPanel.

The current Editor2 has one final addition to Editor1: it sets richEdit_selChange() to handle selection changes in the RichEdit object. A selChange event occurs whenever the user selects a new section of code or whenever the user moves the insertion point. The method richEdit_selChange() first calls getSelFont() to find out the font of the current selection. If there is no selection, this call returns the font of the text to the immediate right of the insertion point. Finally, richEdit_selChange() displays the name and size of the font in the fontPanel object.

If the current selection contains more than one font, the call to getSelFont() throws an exception. This exception is captured by richEdit_selChange(), which then displays the string "<mixed>" in the fontPanel object to let the user know that more than one font is currently selected. Comments