Uploaded image for project: 'JDK'
  1. JDK
  2. JDK-8055874

Action.DISPLAYED_MNEMONIC_INDEX_KEY treated as null if putValue equals old value

XMLWordPrintable

    • Icon: Bug Bug
    • Resolution: Not an Issue
    • Icon: P4 P4
    • 9
    • 7u67, 9
    • client-libs

      FULL PRODUCT VERSION :
      java version "1.7.0_67"
      Java(TM) SE Runtime Environment (build 1.7.0_67-b01)
      Java HotSpot(TM) Client VM (build 24.65-b04, mixed mode, sharing)

      ADDITIONAL OS VERSION INFORMATION :
      Microsoft Windows [Version 5.2.3790]

      A DESCRIPTION OF THE PROBLEM :
      I am attempting to implement Undo in a right-click context menu for a JTextArea. Starting with the example code in TextComponentDemo, I modified the UndoAction class slightly to use an instance of it when adding components to a JPopupMenu in the constructor for ContextMenuMouseListener. In the UndoAction class, I added calls to putValue() to set Action.DISPLAYED_MNEMONIC_INDEX_KEY to the index of "U" but discovered that ***the displayed mnemonic is removed when the index key is set to a value equal to its current value***. At runtime, the Undo's text in the popup behaves as though the new value of DISPLAYED_MNEMONIC_INDEX_KEY was set to null or outside the bounds of its Action.NAME string when it was not.

      To check, I added a System.out.println(getValue()) after each putValue() call. They returned the correct putValue() values, so it does not appear they were set internally to null. However, the displayed mnemonic is removed from the pupup's text during runtime.

      STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
      1. In the UndoAction class, add putValue() statements that set Action.DISPLAYED_MNEMONIC_INDEX_KEY to appropriate values after each putValue() statement that sets Action.NAME.
      2. Compile and run.
      3. Right-click on the JTextArea, and observe the displayed mnemonic.
      4. Type some text into the JTextArea.
      5. Right-click on the JTextArea, and observe that the displayed mnemonic of the Undo menu item is unexpectedly removed when its DISPLAYED_MNEMONIC_INDEX_KEY is set to a value equal to its current value.

      EXPECTED VERSUS ACTUAL BEHAVIOR :
      EXPECTED -
      A call to putValue() that sets DISPLAYED_MNEMONIC_INDEX_KEY to a value equal to its current value should cause the mnemonic at that index to be displayed at runtime as long as its index is still within the bounds of NAME.

      (NAME can be set independently of DISPLAYED_MNEMONIC_INDEX_KEY)
      ACTUAL -
      The calls to putValue() that set DISPLAYED_MNEMONIC_INDEX_KEY to a value equal to its current value caused the displayed mnemonic to be removed at runtime despite its index still being within the bounds of NAME.

      ERROR MESSAGES/STACK TRACES THAT OCCUR :
      There were no error messages. The mnemonic was removed as though it was expected behavior.

      REPRODUCIBILITY :
      This bug can be reproduced always.

      ---------- BEGIN SOURCE ----------
      /*
       * For bug report:
       * Action.DISPLAYED_MNEMONIC_INDEX_KEY treated as null if putValue
       * equals old value
       *
       * The relevant code section is commented.
       */

      import java.awt.event.*;
      import javax.swing.*;
      import javax.swing.event.*;
      import javax.swing.text.*;
      import javax.swing.undo.*;

      class MainClass {
          static class UndoAction extends AbstractAction {
              private final UndoManager undo;
              private String name = "Undo";

              protected UndoAction(final UndoManager undo) {
                  setEnabled(false);
                  putValue(Action.NAME, name);
                  putValue(
                      Action.DISPLAYED_MNEMONIC_INDEX_KEY, name.indexOf("U")
                  );
                  System.out.println(
                      getValue(Action.DISPLAYED_MNEMONIC_INDEX_KEY)
                  );
                  this.undo = undo;
              }

              public void actionPerformed(ActionEvent e) {
                  try { undo.undo(); }
                  catch (CannotUndoException ex) {
                      System.err.println("Unable to undo: " + ex);
                      ex.printStackTrace();
                  }
                  updateUndoState();
              }

              protected void updateUndoState() {
                  if (undo.canUndo()) {
                      setEnabled(true);
                      name = undo.getUndoPresentationName();
                  } else {
                      setEnabled(false);
                      name = "Undo";
                  }
                  putValue(Action.NAME, name);

                  /* For unknown reasons, if this key is set to a new value
                   * equal to its existing value, then its new value is
                   * treated as null. To remedy that here, its value is set
                   * to -1 before setting it to its intended value.
                   * Uncomment the -1 line to remedy the bug.
                   */
                  //putValue(Action.DISPLAYED_MNEMONIC_INDEX_KEY, -1);
                  putValue(
                      Action.DISPLAYED_MNEMONIC_INDEX_KEY, name.indexOf("U")
                  );
                  System.out.println(
                      getValue(Action.DISPLAYED_MNEMONIC_INDEX_KEY)
                  );
              }
          }

          static class MyUndoableEditListener
                  implements UndoableEditListener {
              private UndoManager undo;
              private UndoAction undoAction;

              public MyUndoableEditListener(
                      final UndoManager undo, final UndoAction undoAction
                  ) {
                  this.undo = undo;
                  this.undoAction = undoAction;
              }

              public void undoableEditHappened(UndoableEditEvent e) {
                  undo.addEdit(e.getEdit());
                  undoAction.updateUndoState();
              }
          }

          static class ContextMenuMouseListener extends MouseAdapter {
              private JPopupMenu popup = new JPopupMenu();

              protected UndoAction undoAction;
              protected UndoManager undo = new UndoManager();
              private JTextComponent textcomponent;

              public ContextMenuMouseListener(final JTextComponent tc) {
                  textcomponent = tc;
                  String name;
                  undoAction = new UndoAction(undo);
                  Document document_textcomponent =
                      textcomponent.getDocument();
                  document_textcomponent.addUndoableEditListener(
                      new MyUndoableEditListener(undo, undoAction)
                  );
                  popup.add(undoAction);
              }

              @Override
              public void mouseClicked(MouseEvent e) {
                  if (e.getModifiers() == InputEvent.BUTTON3_MASK) {
                      if (!(e.getSource() instanceof JTextComponent)) {
                          return;
                      }

                      textcomponent = (JTextComponent) e.getSource();
                      textcomponent.requestFocus();

                      int nx = e.getX();
                      if (nx > 500) { nx = nx - popup.getSize().width; }
                      popup.show(
                          e.getComponent(), nx,
                          e.getY() - popup.getSize().height
                      );
                  }
              }
          }

          static class RunnableGUI implements Runnable {
              private JFrame frame_main = new JFrame();
              private JTextArea textarea = new JTextArea();

              public RunnableGUI() {}

              private JPanel getMainPanel() {
                  JPanel panel_content = new JPanel();

                  textarea.setText("");
                  textarea.setLineWrap(true);
                  textarea.setRows(2);
                  textarea.addMouseListener(
                      new ContextMenuMouseListener(textarea)
                  );
                  JScrollPane scrollpane = new JScrollPane(textarea);
                  panel_content.add(scrollpane);

                  return panel_content;
              }

              public void run() {
                  frame_main.setContentPane(getMainPanel());
                  frame_main.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                  frame_main.pack();
                  frame_main.setVisible(true);
              }
          }

          public static void main(final String[] args) {
              javax.swing.SwingUtilities.invokeLater(new RunnableGUI());
          }
      }

      ---------- END SOURCE ----------

      CUSTOMER SUBMITTED WORKAROUND :
      Add the following line before each putValue() that does not initialize Action.DISPLAYED_MNEMONIC_INDEX_KEY:

      putValue(Action.DISPLAYED_MNEMONIC_INDEX_KEY, -1);

            anashaty Anton Nashatyrev (Inactive)
            webbuggrp Webbug Group
            Votes:
            0 Vote for this issue
            Watchers:
            3 Start watching this issue

              Created:
              Updated:
              Resolved: