Skip to content

Example: Configuring the popup menu

Robert Futrell edited this page Apr 11, 2015 · 1 revision

The standard popup menu for 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 originally only required Java 1.4, it does not use the getComponentPopupMenu() or setComponentPopupMenu() methods added in 1.5. This may change in the future, now that RSTA's Java requirement is Java 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 either of the following two methods for customization purposes:

  • createPopupMenu(), which is used in the first call to getPopupMenu() to lazily create the popup
  • configurePopupMenu(JPopupMenu), which is called each time the popup is displayed. This is useful to customize the menu based on the caret position or selection

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.

Below is an example of customizing RSTA's menu:

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.5.6.
 */
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);

      // 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;
      }

   }

}

When running the above program, 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. 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.