RSyntaxTextArea - Customizing the Context Menu
Back to Example Index
Download as Eclipse project
Back to RSyntaxTextArea Home


Description

The standard popup menu for the RSyntaxTextArea has menu items for Undo, Redo, Cut, Copy, Paste, Delete, and Select All. But what if you want to customize this menu for your application?

RSyntaxTextArea provides several hooks from which you can customize the context menu. Note that since RSyntaxTextArea only requires Java 1.4, it does not use the getComponentPopupMenu() or setComponentPopupMenu() methods added in 1.5.

The simplest way to use a custom popup menu is via the setPopupMenu(JPopupMenu) method. This allows you to use your own popup menu for a text area. Passing null to this method causes no popup menu to be used.

But what if you'd rather augment the default popup menu, rather than replacing it? In that case, you can use the getPopupMenu() method. You can add (or remove) whatever JMenuItems you want from the returned menu.

Finally, if you are creating your own subclass of RSyntaxTextArea, you can override createPopupMenu(), which is used in the first call to getPopupMenu() to lazily create the popup.

If you want to add images for the standard actions (cut, copy, undo, etc.), you can create an IconGroup and call setIconGroup(IconGroup) on your text area. This IconGroup will be scanned for image files with the names:

  • cut.gif
  • copy.gif
  • paste.gif
  • delete.gif
  • selectall.gif
  • undo.gif
  • redo.gif

If any of these files are located, they will be used as the icons for their corresponding actions in the text area (and hence in their popup menu). If any action's image is not found, no image is used. Note also that the image type (.gif) can be changed to .png or .jpg. Note that this API will likely change in the future to become more robust.

The Example Source
import java.awt.*;
import java.awt.event.*;
import java.io.*;
import javax.swing.*;
import javax.swing.text.*;

import org.fife.ui.rtextarea.*;
import org.fife.ui.rsyntaxtextarea.*;

/**
 * A simple example showing how to use RSyntaxTextArea to add Java syntax
 * highlighting to a Swing application.<p>
 * 
 * This example uses RSyntaxTextArea 2.0.1.<p>
 * 
 * Project Home: http://fifesoft.com/rsyntaxtextarea<br>
 * Downloads: https://sourceforge.net/projects/rsyntaxtextarea
 */
public class PopupMenuDemo extends JFrame {

   private static final long serialVersionUID = 1L;

   private RSyntaxTextArea textArea;

   public PopupMenuDemo() {

      JPanel cp = new JPanel(new BorderLayout());

      textArea = new RSyntaxTextArea(20, 60);
      textArea.setSyntaxEditingStyle(SyntaxConstants.SYNTAX_STYLE_NONE);
      textArea.setAntiAliasingEnabled(true);

      // Add an item to the popup menu that opens the file whose name is
      // specified at the current caret position.
      JPopupMenu popup = textArea.getPopupMenu();
      popup.addSeparator();
      popup.add(new JMenuItem(new OpenFileAction()));
      RTextScrollPane sp = new RTextScrollPane(textArea);
      cp.add(sp);

      setContentPane(cp);
      setTitle("Popup Menu Demo");
      setDefaultCloseOperation(EXIT_ON_CLOSE);
      pack();
      setLocationRelativeTo(null);

   }

   /**
    * Loads a file's contents into the text area, or displays an error message
    * if the file does not exist.
    * 
    * @param file
    *           The file to load.
    */
   public void loadFile(File file) {

      System.out.println("DEBUG: " + file.getAbsolutePath());
      if (file.isDirectory()) { // Clicking on a space character
         JOptionPane.showMessageDialog(this, file.getAbsolutePath()
               + " is a directory", "Error", JOptionPane.ERROR_MESSAGE);
         return;
      } else if (!file.isFile()) {
         JOptionPane.showMessageDialog(this, "No such file: "
               + file.getAbsolutePath(), "Error", JOptionPane.ERROR_MESSAGE);
         return;
      }

      try {
         BufferedReader r = new BufferedReader(new FileReader(file));
         textArea.read(r, null);
         r.close();
      } catch (IOException ioe) {
         ioe.printStackTrace();
         UIManager.getLookAndFeel().provideErrorFeedback(textArea);
      }

   }

   public static void main(String[] args) {
      // Start all Swing applications on the EDT.
      SwingUtilities.invokeLater(new Runnable() {
         public void run() {
            new PopupMenuDemo().setVisible(true);
         }
      });
   }

   /**
    * An action that gets the filename at the current caret position and tries
    * to open that file. If there is a selection, it uses the selected text as
    * the filename.
    */
   private class OpenFileAction extends TextAction {

      public OpenFileAction() {
         super("Open File");
      }

      public void actionPerformed(ActionEvent e) {

         JTextComponent tc = getTextComponent(e);
         String filename = null;

         // Get the name of the file to load. If there is a selection, use
         // that as the file name, otherwise, scan for a filename around
         // the caret.
         try {
            int selStart = tc.getSelectionStart();
            int selEnd = tc.getSelectionEnd();
            if (selStart != selEnd) {
               filename = tc.getText(selStart, selEnd - selStart);
            } else {
               filename = getFilenameAtCaret(tc);
            }
         } catch (BadLocationException ble) {
            ble.printStackTrace();
            UIManager.getLookAndFeel().provideErrorFeedback(tc);
            return;
         }

         loadFile(new File(filename));

      }

      /**
       * Gets the filename that the caret is sitting on. Note that this is a
       * somewhat naive implementation and assumes filenames do not contain
       * whitespace or other "funny" characters, but it will catch most common
       * filenames.
       * 
       * @param tc
       *           The text component to look at.
       * @return The filename at the caret position.
       * @throws BadLocationException
       *            Shouldn't actually happen.
       */
      public String getFilenameAtCaret(JTextComponent tc) throws BadLocationException {
         int caret = tc.getCaretPosition();
         int start = caret;
         Document doc = tc.getDocument();
         while (start > 0) {
            char ch = doc.getText(start - 1, 1).charAt(0);
            if (isFilenameChar(ch)) {
               start--;
            } else {
               break;
            }
         }
         int end = caret;
         while (end < doc.getLength()) {
            char ch = doc.getText(end, 1).charAt(0);
            if (isFilenameChar(ch)) {
               end++;
            } else {
               break;
            }
         }
         return doc.getText(start, end - start);
      }

      public boolean isFilenameChar(char ch) {
         return Character.isLetterOrDigit(ch) || ch == ':' || ch == '.'
               || ch == File.separatorChar;
      }

   }

}

Save this file as PopupMenuDemo.java.
Compiling the Example
For simplicity, we will just use javac on the command line to compile. Bring up a command prompt or shell, make sure javac is on your PATH, and run the following command:
Windows:  javac -classpath <path-to-jar>\rsyntaxtextarea.jar PopupMenuDemo.java
Unix:     javac -classpath <path-to-jar>/rsyntaxtextarea.jar PopupMenuDemo.java
where <path-to-jar> is the path to the rsyntaxtextarea.jar file. This should yield no errors or warnings, and on completion there should see a file named CodeTemplateDemo.class in your current directory.
Running the Example
Running the example is just as simple as compiling it:
Windows:  java -classpath <path-to-jar>\rsyntaxtextarea.jar;. PopupMenuDemo
Unix:     java -classpath <path-to-jar>/rsyntaxtextarea.jar:. PopupMenuDemo

A window should pop up, containing a text editor. Type in the name of a file. This can be either the full path to a file (such as "C:\temp\test.txt") or a relative path to a file from the directory you're running the program in (for example, typing "PopupMenuDemo.class" to point to the class file you made while compiling). Now, click in the middle of the filename, right-click, and chose "Open File". The file's contents will be read into the text area.

Going a Little Further

You can make this action "smarter" and more usable. For example, you can toggle whether the action is enabled whenever the popup menu is displayed, or you can change the text on the popup menu to "Open File filename" so the user knows what they're trying to open.

If you subclass RSyntaxTextArea, you can override the getPopupMenu() method. This is called whenever the popup is about to be displayed. You can override this method and extend it to enable or disable the OpenFileAction, change its text description, etc. This would greatly improve the usability of this action.