Google Tag Manager

Showing posts with label JPopupMenu. Show all posts
Showing posts with label JPopupMenu. Show all posts

2025/06/30

Automatically open and close JPopupMenu without mouse click

Code

/* If the mouse cursor goes out of the region at high speed, JPopupMenu may not close, use AWTEventListener to work around this problem. */
class AutoClosePopupMenu extends JPopupMenu {
  private transient PopupMenuListener listener;

  @Override public void updateUI() {
    removePopupMenuListener(listener);
    super.updateUI();
    listener = new AwtPopupMenuListener();
    addPopupMenuListener(listener);
  }

  private void checkAutoClose(MouseEvent e) {
    Component c = e.getComponent();
    Rectangle r = getBounds();
    r.grow(0, 5);
    Point pt = SwingUtilities.convertPoint(c, e.getPoint(), this);
    if (!r.contains(pt) && !(c instanceof JButton)) {
      setVisible(false);
    }
  }

  private final class AwtPopupMenuListener implements PopupMenuListener {
    private final AWTEventListener handler = e -> {
      if (e instanceof MouseEvent) {
        int id = e.getID();
        if (id == MouseEvent.MOUSE_MOVED || id == MouseEvent.MOUSE_EXITED) {
          checkAutoClose((MouseEvent) e);
        }
      }
    };

    @Override public void popupMenuWillBecomeVisible(PopupMenuEvent e) {
      long mask = AWTEvent.MOUSE_EVENT_MASK | AWTEvent.MOUSE_MOTION_EVENT_MASK;
      Toolkit.getDefaultToolkit().addAWTEventListener(handler, mask);
    }

    @Override public void popupMenuWillBecomeInvisible(PopupMenuEvent e) {
      Toolkit.getDefaultToolkit().removeAWTEventListener(handler);
    }

    @Override public void popupMenuCanceled(PopupMenuEvent e) {
      /* not needed */
    }
  }
}
Set up an event listener to automatically open JPopupMenu when the mouse cursor enters the JButton area and automatically close it when the mouse cursor leaves the JButton or JPopupMenu area.

References

2024/03/31

Switching between JToolBar and JMenuBar

Click on the hamburger menu-like JButton placed on the JToolBar to switch this with the JMenuBar

Code

JMenuBar mainMenuBar = makeMenuBar();
JButton button = makeHamburgerMenuButton(mainMenuBar);
JMenuBar wrappingMenuBar = new JMenuBar();
wrappingMenuBar.add(makeToolBar(button));
EventQueue.invokeLater(() -> getRootPane().setJMenuBar(wrappingMenuBar));

PopupMenuListener handler = new PopupMenuListener() {
  @Override public void popupMenuWillBecomeVisible(PopupMenuEvent e) {
    // not need
  }

  @Override public void popupMenuWillBecomeInvisible(PopupMenuEvent e) {
    EventQueue.invokeLater(() -> {
      if (MenuSelectionManager.defaultManager().getSelectedPath().length == 0) {
        getRootPane().setJMenuBar(wrappingMenuBar);
      }
    });
  }

  @Override public void popupMenuCanceled(PopupMenuEvent e) {
    EventQueue.invokeLater(() -> getRootPane().setJMenuBar(wrappingMenuBar));
  }
};
for (int i = 0; i < mainMenuBar.getMenuCount(); i++) {
  mainMenuBar.getMenu(i).getPopupMenu().addPopupMenuListener(handler);
}
// ...
private JButton makeHamburgerMenuButton(JMenuBar menuBar) {
  JButton button = new JButton("Ξ") {
    @Override public Dimension getPreferredSize() {
      Dimension d = super.getPreferredSize();
      d.height = menuBar.getMenu(0).getPreferredSize().height;
      return d;
    }

    @Override public void updateUI() {
      super.updateUI();
      setContentAreaFilled(false);
      setFocusPainted(false);
      setBorder(BorderFactory.createEmptyBorder(0, 2, 0, 2));
    }
  };
  button.addActionListener(e -> {
    getRootPane().setJMenuBar(menuBar);
    getRootPane().revalidate();
    EventQueue.invokeLater(() -> menuBar.getMenu(0).doClick());
  });
  button.setMnemonic('\\');
  button.setToolTipText("Main Menu(Alt+\\)");
  return button;
}

References

2024/02/29

Paint JMenuItem selection rollover with rounded rectangle

Code

class BasicRoundMenuItemUI extends BasicMenuItemUI {
  @Override protected void paintBackground(
      Graphics g, JMenuItem menuItem, Color bgColor) {
    ButtonModel m = menuItem.getModel();
    Color oldColor = g.getColor();
    int menuWidth = menuItem.getWidth();
    int menuHeight = menuItem.getHeight();
    if (menuItem.isOpaque()) {
      if (m.isArmed() || (menuItem instanceof JMenu && m.isSelected())) {
        Graphics2D g2 = (Graphics2D) g.create();
        g2.setRenderingHint(
          RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        // g2.clearRect(0, 0, menuWidth, menuHeight);
        g2.setPaint(menuItem.getBackground());
        g2.fillRect(0, 0, menuWidth, menuHeight);
        g2.setColor(bgColor);
        g2.fillRoundRect(2, 2, menuWidth - 4, menuHeight - 4, 8, 8);
        g2.dispose();
      } else {
        g.setColor(menuItem.getBackground());
        g.fillRect(0, 0, menuWidth, menuHeight);
      }
      g.setColor(oldColor);
    } else if (m.isArmed() || (menuItem instanceof JMenu && m.isSelected())) {
      g.setColor(bgColor);
      g.fillRoundRect(2, 2, menuWidth - 4, menuHeight - 4, 8, 8);
      g.setColor(oldColor);
    }
  }
}

References

2022/10/31

Add a header to JPopupMenu to enable repositioning by mouse dragging

Code

class PopupHeaderMouseListener extends MouseAdapter {
  private final Point startPt = new Point();

  @Override public void mousePressed(MouseEvent e) {
    if (SwingUtilities.isLeftMouseButton(e)) {
      startPt.setLocation(e.getPoint());
    }
  }

  @Override public void mouseDragged(MouseEvent e) {
    Component c = e.getComponent();
    Window w = SwingUtilities.getWindowAncestor(c);
    if (w != null && SwingUtilities.isLeftMouseButton(e)) {
      if (w.getType() == Window.Type.POPUP) { // Popup$HeavyWeightWindow
        Point pt = e.getLocationOnScreen();
        w.setLocation(pt.x - startPt.x, pt.y - startPt.y);
      } else { // Popup$LightWeightWindow
        Container popup = SwingUtilities.getAncestorOfClass(JPopupMenu.class, c);
        Point pt = popup.getLocation();
        popup.setLocation(pt.x - startPt.x + e.getX(), pt.y - startPt.y + e.getY());
      }
    }
  }
}

References

2022/07/31

use a newspaper-style JList that can scroll and display multiple items as a ComboBoxEditor

Code

ListModel<ListItem> model = makeModel();
JList<ListItem> list = new NewspaperStyleList<>(model);
JScrollPane scroll = new JScrollPane(list) {
  @Override public Dimension getPreferredSize() {
    Dimension d = super.getPreferredSize();
    d.width = 62 * 3 + 10;
    d.height = 32;
    return d;
  }
};
scroll.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_NEVER);
scroll.setBorder(BorderFactory.createEmptyBorder());
scroll.setViewportBorder(BorderFactory.createEmptyBorder());

JList<ListItem> list2 = new NewspaperStyleList<>(model);
JScrollPane scroll2 = new JScrollPane(list2) {
  @Override public Dimension getPreferredSize() {
    Dimension d = super.getPreferredSize();
    d.width = 62 * 4 + 10;
    return d;
  }
};
scroll2.setBorder(BorderFactory.createEmptyBorder());
scroll2.setViewportBorder(BorderFactory.createEmptyBorder());

JPopupMenu popup = new JPopupMenu();
popup.setLayout(new BorderLayout());
list2.addMouseListener(new MouseAdapter() {
  @Override public void mouseClicked(MouseEvent e) {
    if (popup.isVisible() && e.getClickCount() >= 2) {
      popup.setVisible(false);
    }
  }
});
popup.add(scroll2);
popup.setBorder(BorderFactory.createLineBorder(Color.GRAY));

JToggleButton dropDown = new JToggleButton(new DropDownArrowIcon());
popup.addPopupMenuListener(new PopupMenuListener() {
  @Override public void popupMenuWillBecomeVisible(PopupMenuEvent e) {
    list2.setSelectedIndex(list.getSelectedIndex());
    EventQueue.invokeLater(popup::pack);
  }

  @Override public void popupMenuWillBecomeInvisible(PopupMenuEvent e) {
    dropDown.setSelected(false);
    list.requestFocusInWindow();
    int i = list2.getSelectedIndex();
    if (i >= 0) {
      list.setSelectedIndex(i);
      list.scrollRectToVisible(list.getCellBounds(i, i));
    }
  }

  @Override public void popupMenuCanceled(PopupMenuEvent e) {
    popupMenuWillBecomeInvisible(e);
  }
});

dropDown.setBorder(BorderFactory.createEmptyBorder());
dropDown.setContentAreaFilled(false);
dropDown.setFocusPainted(false);
dropDown.setFocusable(false);
dropDown.addItemListener(e -> {
  AbstractButton b = (AbstractButton) e.getItemSelectable();
  if (e.getStateChange() == ItemEvent.SELECTED) {
    popup.show(b, -scroll.getWidth(), b.getHeight());
  }
});

JScrollBar verticalScrollBar = scroll.getVerticalScrollBar();
JPanel verticalBox = new JPanel(new BorderLayout()) {
  @Override public Dimension getPreferredSize() {
    Dimension d = super.getPreferredSize();
    d.height = 32 + 5 + 5;
    return d;
  }
};
verticalBox.setOpaque(false);
verticalBox.add(verticalScrollBar);
verticalBox.add(dropDown, BorderLayout.SOUTH);

JPanel panel = new JPanel(new BorderLayout(0, 0));
panel.add(scroll);
panel.add(verticalBox, BorderLayout.EAST);
panel.setBorder(BorderFactory.createLineBorder(Color.DARK_GRAY));

References

2022/06/30

Change the height of a drop-down list of JComboBox by mouse dragging

Code

JPopupMenu popup = new JPopupMenu();
popup.setBorder(BorderFactory.createEmptyBorder());
popup.setPopupSize(240, 120);

JLabel bottom = new JLabel("", new DotIcon(), SwingConstants.CENTER);
MouseInputListener rwl = new ResizeWindowListener(popup);
bottom.addMouseListener(rwl);
bottom.addMouseMotionListener(rwl);
bottom.setCursor(Cursor.getPredefinedCursor(Cursor.S_RESIZE_CURSOR));
bottom.setOpaque(true);
bottom.setBackground(new Color(0xE0_E0_E0));
bottom.setFocusable(false);

JPanel resizePanel = new JPanel(new BorderLayout());
resizePanel.add(scroll);
resizePanel.add(bottom, BorderLayout.SOUTH);
resizePanel.add(Box.createHorizontalStrut(240), BorderLayout.NORTH);
resizePanel.setBorder(BorderFactory.createLineBorder(new Color(0x64_64_64)));

JPopupMenu popup = new JPopupMenu();
popup.add(resizePanel);

// ...
class ResizeWindowListener extends MouseInputAdapter {
  private final Rectangle rect = new Rectangle();
  private final JPopupMenu popup;
  private final Point startPt = new Point();
  private final Dimension startDim = new Dimension();

  protected ResizeWindowListener(JPopupMenu popup) {
    this.popup = popup;
  }

  @Override public void mousePressed(MouseEvent e) {
    rect.setSize(popup.getSize());
    startDim.setSize(popup.getSize());
    startPt.setLocation(e.getComponent().getLocationOnScreen());
  }

  @Override public void mouseDragged(MouseEvent e) {
    rect.height = startDim.height + e.getLocationOnScreen().y - startPt.y;
    popup.setPreferredSize(rect.getSize());
    Window w = SwingUtilities.getWindowAncestor(popup);
    if (w != null && w.getType() == Window.Type.POPUP) {
      // Popup$HeavyWeightWindow
      w.setSize(rect.width, rect.height);
    } else {
      // Popup$LightWeightWindow
      popup.pack();
    }
  }
}

References

2021/11/30

Add a vertical JSlider in the JPopupMenu and display it at the top of the JToggleButton

Code

JPopupMenu popup = new JPopupMenu();
popup.setLayout(new BorderLayout());
popup.addMouseWheelListener(InputEvent::consume);

UIManager.put("Slider.paintValue", Boolean.TRUE);
UIManager.put("Slider.focus", UIManager.get("Slider.background"));
JSlider slider = new JSlider(SwingConstants.VERTICAL, 0, 100, 80);
slider.addMouseWheelListener(e -> {
  JSlider s = (JSlider) e.getComponent();
  if (s.isEnabled()) {
    BoundedRangeModel m = s.getModel();
    m.setValue(m.getValue() - e.getWheelRotation() * 2);
  }
  e.consume();
});
popup.add(slider);

JToggleButton button = new JToggleButton("🔊") {
  @Override public JToolTip createToolTip() {
    JToolTip tip = super.createToolTip();
    tip.addHierarchyListener(e -> {
      long flg = e.getChangeFlags() & HierarchyEvent.SHOWING_CHANGED;
      if (flg != 0 && e.getComponent().isShowing()) {
        Dimension d = popup.getPreferredSize();
        popup.show(this, (getWidth() - d.width) / 2, -d.height);
      }
    });
    return tip;
  }

  @Override public Point getToolTipLocation(MouseEvent e) {
    return new Point(getWidth() / 2, -getHeight());
  }

  @Override public void setEnabled(boolean b) {
    super.setEnabled(b);
    setText(b ? "🔊" : "🔇");
  }
};
button.setToolTipText("");
button.addMouseListener(new MouseAdapter() {
  @Override public void mousePressed(MouseEvent e) {
    if (!button.isEnabled()) {
      slider.setValue(80);
      button.setEnabled(true);
    }
    Component b = (Component) e.getSource();
    Dimension d = popup.getPreferredSize();
    popup.show(b, (b.getWidth() - d.width) / 2, -d.height);
  }

  @Override public void mouseEntered(MouseEvent e) {
    if (!popup.isVisible()) {
      ToolTipManager.sharedInstance().setEnabled(true);
    }
  }

  @Override public void mouseExited(MouseEvent e) {
    if (!popup.isVisible()) {
      ToolTipManager.sharedInstance().setEnabled(true);
    }
  }
});
popup.addPopupMenuListener(new PopupMenuListener() {
  @Override public void popupMenuCanceled(PopupMenuEvent e) {
    /* not needed */
  }

  @Override public void popupMenuWillBecomeVisible(PopupMenuEvent e) {
    EventQueue.invokeLater(() -> ToolTipManager.sharedInstance().setEnabled(false));
  }

  @Override public void popupMenuWillBecomeInvisible(PopupMenuEvent e) {
    button.setSelected(false);
  }
});

References

2020/10/31

Show or hide each TableColumn added to the JTableHeader

Code

class TableHeaderPopupMenu extends JPopupMenu {
  protected TableHeaderPopupMenu(JTable table) {
    super();
    TableColumnModel columnModel = table.getColumnModel();
    List>TableColumn> list = Collections.list(columnModel.getColumns());
    list.forEach(tableColumn -> {
      String name = Objects.toString(tableColumn.getHeaderValue());
      // System.out.format("%s - %s%n", name, tableColumn.getIdentifier());
      JCheckBoxMenuItem item = new JCheckBoxMenuItem(name, true);
      item.addItemListener(e -> {
        if (((AbstractButton) e.getItemSelectable()).isSelected()) {
          columnModel.addColumn(tableColumn);
        } else {
          columnModel.removeColumn(tableColumn);
        }
        updateMenuItems(columnModel);
      });
      add(item);
    });
  }

  @Override public void show(Component c, int x, int y) {
    if (c instanceof JTableHeader) {
      JTableHeader header = (JTableHeader) c;
      JTable table = header.getTable();
      header.setDraggedColumn(null);
      header.repaint();
      table.repaint();
      updateMenuItems(header.getColumnModel());
      super.show(c, x, y);
    }
  }

  private void updateMenuItems(TableColumnModel columnModel) {
    boolean isOnlyOneMenu = columnModel.getColumnCount() == 1;
    if (isOnlyOneMenu) {
      stream(this).map(MenuElement::getComponent).forEach(mi ->
          mi.setEnabled(!(mi instanceof AbstractButton)
                        || !((AbstractButton) mi).isSelected()));
    } else {
      stream(this).forEach(me -> me.getComponent().setEnabled(true));
    }
  }

  private static Stream>MenuElement> stream(MenuElement me) {
    return Stream.of(me.getSubElements())
      .flatMap(m -> Stream.concat(Stream.of(m), stream(m)));
  }
}

Explanation

  • Initially shows all TableColumns generated from a TableModel
    • All JCheckBoxMenuItems are also selected
  • TableColumn hidden with TableColumnModel#removeColumn(TableColumn) method when deselected with JCheckBoxMenuItem
    • The column is removed from TableColumnModel and hidden from JTableHeader, but the column remains in TableModel
    • Check the number of TableColumn columns to enable/disable the JCheckBoxMenuItem, e.g. when opening a JPopupMenu, so that all TableColumns are not hidden
  • Show TableColumn with TableColumnModel#addColumn(TableColumn) method when selected and set with JCheckBoxMenuItem
    • Columns are added to TableColumnModel and appear in JTableHeader, but TableModel is unchanged from its initial state

  • UIManager.put("CheckBoxMenuItem.doNotCloseOnMouseClick", true) in Java 9 or higher; If set to and the currently selected TableColumn is hidden with JCheckBoxMenuItem while JPopupMenu is open, an ArrayIndexOutOfBoundsException will occur
    • Add PopupMenuListener to JPopupMenu or override the JPopupMenu#show(...) method to JTableHeader.setDraggedColumn(null) can be avoided by clearing the selection state in the scene view

References

2020/05/31

Make text in a JToolTip selectable and copyable

Code

JEditorPane hint = new JEditorPane();
hint.setEditorKit(new HTMLEditorKit());
hint.setEditable(false);
hint.setOpaque(false);

JCheckBox check = new JCheckBox();
check.setOpaque(false);

JPanel panel = new JPanel(new BorderLayout());
panel.add(hint);
panel.add(check, BorderLayout.EAST);

JPopupMenu popup = new JPopupMenu();
popup.add(new JScrollPane(panel));
popup.setBorder(BorderFactory.createEmptyBorder());

JEditorPane editor = new JEditorPane() {
  @Override public JToolTip createToolTip() {
    JToolTip tip = super.createToolTip();
    tip.addHierarchyListener(e -> {
      if ((e.getChangeFlags() & HierarchyEvent.SHOWING_CHANGED) != 0
            && e.getComponent().isShowing()) {
        panel.setBackground(tip.getBackground());
        popup.show(tip, 0, 0);
      }
    });
    return tip;
  }
};
editor.setEditorKit(new HTMLEditorKit());
editor.setText(HTML_TEXT);
editor.setEditable(false);
editor.addHyperlinkListener(e -> {
  JEditorPane editorPane = (JEditorPane) e.getSource();
  if (e.getEventType() == HyperlinkEvent.EventType.ACTIVATED) {
    JOptionPane.showMessageDialog(editorPane, "You click the link with the URL " + e.getURL());
  } else if (e.getEventType() == HyperlinkEvent.EventType.ENTERED) {
    editorPane.setToolTipText("");
    Optional.ofNullable(e.getSourceElement())
        .map(elem -> (AttributeSet) elem.getAttributes().getAttribute(HTML.Tag.A))
        .ifPresent(attr -> {
          String title = Objects.toString(attr.getAttribute(HTML.Attribute.TITLE));
          String url = Objects.toString(e.getURL());
          // String url = Objects.toString(attr.getAttribute(HTML.Attribute.HREF));
          hint.setText(String.format("%s: %s", title, url, url));
          popup.pack();
        });
  } else if (e.getEventType() == HyperlinkEvent.EventType.EXITED) {
    editorPane.setToolTipText(null);
  }
});

  • Override JComponent#createToolTip() method to add HierarchyListener to JToolTip
  • Show JPopupMenu with JToolTip as parent when JToolTip is visible
    • JToolTip hides behind JPopupMenu
    • JPopupMenu adds a JPanel with a JEditorPane and a JCheckBox instead of a JMenuItem
  • Hiding the parent JToolTip with the mouse cursor does not close the JPopupMenu, so you can click the internal JCheckBox or select the text in the JEditorPane and copy it with Ctrl-C, etc
    • It is a normal JPopupMenu, so it is hidden when the focus is moved by clicking on the parent JFrame etc

References

2018/06/29

Move items by copying and pasting between JLists

Code

class ListPopupMenu extends JPopupMenu {
  private final JMenuItem cutItem;
  private final JMenuItem copyItem;
  protected ListPopupMenu(JList<?> list) {
    super();
    Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard();
    TransferHandler handler = list.getTransferHandler();
    cutItem = add("cut");
    cutItem.addActionListener(e -> {
      handler.exportToClipboard(list, clipboard, TransferHandler.MOVE);
    });
    copyItem = add("copy");
    copyItem.addActionListener(e -> {
      handler.exportToClipboard(list, clipboard, TransferHandler.COPY);
    });
    add("paste").addActionListener(e -> {
      handler.importData(list, clipboard.getContents(null));
    });
    addSeparator();
    add("clearSelection").addActionListener(e -> list.clearSelection());
  }
  @Override public void show(Component c, int x, int y) {
    if (c instanceof JList) {
      boolean isSelected = !((JList<?>) c).isSelectionEmpty();
      cutItem.setEnabled(isSelected);
      copyItem.setEnabled(isSelected);
      super.show(c, x, y);
    }
  }
}

// ...
private static int getIndex(TransferHandler.TransferSupport info) {
  JList<?> target = (JList<?>) info.getComponent();
  int index; // = dl.getIndex();
  if (info.isDrop()) { // Mouse Drag & Drop
    System.out.println("Mouse Drag & Drop");
    TransferHandler.DropLocation tdl = info.getDropLocation();
    if (tdl instanceof JList.DropLocation) {
      index = ((JList.DropLocation) tdl).getIndex();
    } else {
      index = target.getSelectedIndex();
    }
  } else { // Keyboard Copy & Paste
    index = target.getSelectedIndex();
  }
  DefaultListModel<?> listModel = (DefaultListModel<?>) target.getModel();
  // boolean insert = dl.isInsert();
  int max = listModel.getSize();
  // int index = dl.getIndex();
  index = index < 0 ? max : index; // If it is out of range, it is appended to the end
  index = Math.min(index, max);
  return index;
}

References

2016/05/30

Keep visible the JPopupMenu while clicking on CheckBox

Code

JMenuItem mi = new JMenuItem(" ");
mi.setLayout(new BorderLayout());
mi.add(new JCheckBox(title) {
  private transient MouseAdapter handler;

  @Override public void updateUI() {
    removeMouseListener(handler);
    removeMouseMotionListener(handler);
    super.updateUI();
    handler = new DispatchParentHandler();
    addMouseListener(handler);
    addMouseMotionListener(handler);
    setFocusable(false);
    setOpaque(false);
  }
});
popup.add(mi);

popup.add(new JCheckBoxMenuItem("keeping open #2") {
  @Override public void updateUI() {
    super.updateUI();
    setUI(new BasicCheckBoxMenuItemUI() {
      @Override protected void doClick(MenuSelectionManager msm) {
        // super.doClick(msm);
        System.out.println("MenuSelectionManager: doClick");
        menuItem.doClick(0);
      }
    });
  }
});
// https://bugs.openjdk.java.net/browse/JDK-8165234
// Provide a way to not close toggle menu items on mouse click on component level
// JDK 9
JMenuItem menuItem = new JMenuItem("JMenuItem");
menuItem.putClientProperty("CheckBoxMenuItem.doNotCloseOnMouseClick", true);

References

2014/09/29

Change a layout of a JPopupMenu to use their iconic menu items

Code

JPopupMenu popup = new JPopupMenu();
GridBagConstraints c = new GridBagConstraints();
popup.setLayout(new GridBagLayout());
c.gridheight = 1;

c.weightx = 1.0;
c.weighty = 0.0;
c.fill = GridBagConstraints.HORIZONTAL;
c.anchor = GridBagConstraints.CENTER;

c.gridwidth = 1;
c.gridy = 0;
c.gridx = 0; popup.add(makeButton("\u21E6"), c);
c.gridx = 1; popup.add(makeButton("\u21E8"), c);
c.gridx = 2; popup.add(makeButton("\u21BB"), c);
c.gridx = 3; popup.add(makeButton("\u2729"), c);

c.gridwidth = 4;
c.gridx = 0;
c.insets = new Insets(2, 0, 2, 0);
c.gridy = 1; popup.add(new JSeparator(), c);
c.insets = new Insets(0, 0, 0, 0);
c.gridy = 2; popup.add(new JMenuItem("aaaaaaaaaa"), c);
c.gridy = 3; popup.add(new JPopupMenu.Separator(), c);
c.gridy = 4; popup.add(new JMenuItem("bbbb"), c);
c.gridy = 5; popup.add(new JMenuItem("ccccccccccccccccccccc"), c);
c.gridy = 6; popup.add(new JMenuItem("dddddddddd"), c);

// ...
final Icon icon = new SymbolIcon(symbol);
JMenuItem b = new JMenuItem() {
  private final Dimension d = new Dimension(icon.getIconWidth(), icon.getIconHeight());
  @Override public Dimension getPreferredSize() {
    return d;
  }
  @Override public void paintComponent(Graphics g) {
    super.paintComponent(g);
    Dimension cd = getSize();
    Dimension pd = getPreferredSize();
    int offx = (int) (.5 + .5 * (cd.width  - pd.width));
    int offy = (int) (.5 + .5 * (cd.height - pd.height));
    icon.paintIcon(this, g, offx, offy);
  }
};
b.setOpaque(true);

References

2014/03/27

Long pressing the JButton to get a JPopupMenu

Code

class PressAndHoldHandler extends AbstractAction implements MouseListener {
  public final JPopupMenu pop = new JPopupMenu();
  public final ButtonGroup bg = new ButtonGroup();
  private AbstractButton arrowButton;
  private final Timer holdTimer = new Timer(1000, e -> {
    if (arrowButton != null && arrowButton.getModel().isPressed()
        && holdTimer.isRunning()) {
      holdTimer.stop();
      pop.show(arrowButton, 0, arrowButton.getHeight());
      pop.requestFocusInWindow();
    }
  });

  public PressAndHoldHandler() {
    super();
    holdTimer.setInitialDelay(1000);
    pop.setLayout(new GridLayout(0, 3, 5, 5));
    for (MenuContext m: makeMenuList()) {
      AbstractButton b = new JRadioButton(m.command);
      b.setActionCommand(m.command);
      b.setForeground(m.color);
      b.setBorder(BorderFactory.createEmptyBorder());
      b.addActionListener(e -> {
        System.out.println(e.getActionCommand());
        pop.setVisible(false);
      });
      pop.add(b);
      bg.add(b);
    }
  }

  private List<MenuContext> makeMenuList() {
    return Arrays.asList(
      new MenuContext("BLACK", Color.BLACK),
      new MenuContext("BLUE", Color.BLUE),
      new MenuContext("CYAN", Color.CYAN),
      new MenuContext("GREEN", Color.GREEN),
      new MenuContext("MAGENTA", Color.MAGENTA),
      new MenuContext("ORANGE", Color.ORANGE),
      new MenuContext("PINK", Color.PINK),
      new MenuContext("RED", Color.RED),
      new MenuContext("YELLOW", Color.YELLOW));
  }

  @Override public void actionPerformed(ActionEvent e) {
    System.out.println("actionPerformed");
    if (holdTimer.isRunning()) {
      ButtonModel model = bg.getSelection();
      if (model != null) {
        System.out.println(model.getActionCommand());
      }
      holdTimer.stop();
    }
  }

  @Override public void mousePressed(MouseEvent e) {
    System.out.println("mousePressed");
    Component c = e.getComponent();
    if (SwingUtilities.isLeftMouseButton(e) && c.isEnabled()) {
      arrowButton = (AbstractButton) c;
      holdTimer.start();
    }
  }

  @Override public void mouseReleased(MouseEvent e) {
    holdTimer.stop();
  }

  @Override public void mouseExited(MouseEvent e) {
    if (holdTimer.isRunning()) {
      holdTimer.stop();
    }
  }

  @Override public void mouseEntered(MouseEvent e) {
    /* not needed */
  }

  @Override public void mouseClicked(MouseEvent e) {
    /* not needed */
  }
}

References

2013/03/30

JTree node edit only from JPopupMenu

Code

tree.setCellEditor(new DefaultTreeCellEditor(
    tree, (DefaultTreeCellRenderer) tree.getCellRenderer()) {
  @Override public boolean isCellEditable(EventObject e) {
    return !(e instanceof MouseEvent) && super.isCellEditable(e);
  }

  // @Override protected boolean canEditImmediately(EventObject e) {
  //   // ((MouseEvent) e).getClickCount() > 2
  //   return (e instanceof MouseEvent) ? false : super.canEditImmediately(e);
  // }
});
tree.setEditable(true);
tree.setComponentPopupMenu(new TreePopupMenu());

// ...
public TreePopupMenu() {
  super();
  add(new JMenuItem(new AbstractAction("Edit") {
    @Override public void actionPerformed(ActionEvent e) {
      JTree tree = (JTree) getInvoker();
      if (path != null) {
        tree.startEditingAtPath(path);
      }
    }
  }));
  add(new JMenuItem(new AbstractAction("Edit Dialog") {
    @Override public void actionPerformed(ActionEvent e) {
      JTree tree = (JTree) getInvoker();
      if (path == null) {
        return;
      }
      Object node = path.getLastPathComponent();
      if (node instanceof DefaultMutableTreeNode) {
        DefaultMutableTreeNode leaf = (DefaultMutableTreeNode) node;
        textField.setText(leaf.getUserObject().toString());
        int result = JOptionPane.showConfirmDialog(
            tree, textField, "Rename",
            JOptionPane.OK_CANCEL_OPTION, JOptionPane.PLAIN_MESSAGE);
        if (result == JOptionPane.OK_OPTION) {
          String str = textField.getText();
          if (!str.trim().isEmpty()) {
            ((DefaultTreeModel) tree.getModel()).valueForPathChanged(path, str);
          }
        }
      }
    }
  }));
  // ...

References

2012/07/04

Translucent JPopupMenu

Code

class TranslucentPopupMenu extends JPopupMenu {
  private static final Color ALPHA_ZERO = new Color(0x0, true);
  private static final Color POPUP_BACK = new Color(250, 250, 250, 200);
  private static final Color POPUP_LEFT = new Color(230, 230, 230, 200);
  private static final int LEFT_WIDTH = 24;
  @Override public boolean isOpaque() {
    return false;
  }

  @Override public void updateUI() {
    super.updateUI();
    if (UIManager.getBorder("PopupMenu.border") == null) {
      setBorder(new BorderUIResource(BorderFactory.createLineBorder(Color.GRAY)));
    }
  }

  @Override public JMenuItem add(JMenuItem menuItem) {
    menuItem.setOpaque(false);
    // menuItem.setBackground(ALPHA_ZERO);
    return super.add(menuItem);
  }

  @Override public void show(Component c, int x, int y) {
    EventQueue.invokeLater(new Runnable() {
      @Override public void run() {
        Window p = SwingUtilities.getWindowAncestor(TranslucentPopupMenu.this);
        if (p instanceof JWindow) {
          System.out.println("Heavy weight");
          JWindow w = (JWindow) p;
          w.setBackground(ALPHA_ZERO);
        } else {
          System.out.println("Light weight");
        }
      }
    });
    super.show(c, x, y);
  }

  @Override protected void paintComponent(Graphics g) {
    Graphics2D g2 = (Graphics2D) g.create();
    g2.setPaint(POPUP_LEFT);
    g2.fillRect(0, 0, LEFT_WIDTH, getHeight());
    g2.setPaint(POPUP_BACK);
    g2.fillRect(LEFT_WIDTH, 0, getWidth(), getHeight());
    g2.dispose();
    //super.paintComponent(g);
  }
}

References

2011/12/22

Dropdown menu button in the JTableHeader

Code

@Override public void mouseClicked(MouseEvent e) {
  JTableHeader header = (JTableHeader) e.getSource();
  JTable table = header.getTable();
  TableColumnModel columnModel = table.getColumnModel();
  int vci = columnModel.getColumnIndexAtX(e.getX());
  int mci = table.convertColumnIndexToModel(vci);
  TableColumn column = table.getColumnModel().getColumn(mci);
  Rectangle r = header.getHeaderRect(vci);
  Container c = (Container) getTableCellRendererComponent(table, "", true, true, -1, vci);
  // if (!isNimbus) {
  //   Insets i = c.getInsets();
  //   r.translate(r.width - i.right, 0);
  // } else {
  r.translate(r.width - BUTTON_WIDTH, 0);
  r.setSize(BUTTON_WIDTH, r.height);
  Point pt = e.getPoint();
  if (c.getComponentCount() > 0 && r.contains(pt) && pop != null) {
    pop.show(header, r.x, r.height);
    JButton b = (JButton) c.getComponent(0);
    b.doClick();
    e.consume();
  }
}

@Override public void mouseExited(MouseEvent e) {
  rolloverIndex = -1;
}

@Override public void mouseMoved(MouseEvent e) {
  JTableHeader header = (JTableHeader) e.getSource();
  JTable table = header.getTable();
  TableColumnModel columnModel = table.getColumnModel();
  int vci = columnModel.getColumnIndexAtX(e.getX());
  int mci = table.convertColumnIndexToModel(vci);
  rolloverIndex = mci;
}

References

2010/11/30

JTable CellEditor PopupMenu

Code

public static JPopupMenu installTextComponentPopupMenu(final JTextComponent tc) {
  final UndoManager manager = new UndoManager();
  final Action undoAction   = new UndoAction(manager);
  final Action redoAction   = new RedoAction(manager);
  final Action cutAction    = new DefaultEditorKit.CutAction();
  final Action copyAction   = new DefaultEditorKit.CopyAction();
  final Action pasteAction  = new DefaultEditorKit.PasteAction();
  final Action deleteAction = new AbstractAction("delete") {
    public void actionPerformed(ActionEvent e) {
      JPopupMenu pop = (JPopupMenu)e.getSource();
      ((JTextComponent)pop.getInvoker()).replaceSelection(null);
    }
  };
  tc.addAncestorListener(new AncestorListener() {
    public void ancestorAdded(AncestorEvent e) {
      manager.discardAllEdits();
      tc.requestFocusInWindow();
    }
    public void ancestorMoved(AncestorEvent e) {}
    public void ancestorRemoved(AncestorEvent e) {}
  });
  tc.getDocument().addUndoableEditListener(manager);
  tc.getActionMap().put("undo", undoAction);
  tc.getActionMap().put("redo", redoAction);
  InputMap imap = tc.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
  imap.put(KeyStroke.getKeyStroke(KeyEvent.VK_Z, Event.CTRL_MASK), "undo");
  imap.put(KeyStroke.getKeyStroke(KeyEvent.VK_Y, Event.CTRL_MASK), "redo");

  JPopupMenu popup = new JPopupMenu();
  popup.add(cutAction);
  popup.add(copyAction);
  popup.add(pasteAction);
  popup.add(deleteAction);
  popup.addSeparator();
  popup.add(undoAction);
  popup.add(redoAction);

  popup.addPopupMenuListener(new PopupMenuListener() {
    public void popupMenuCanceled(PopupMenuEvent e) {}
    public void popupMenuWillBecomeInvisible(PopupMenuEvent e) {
      undoAction.setEnabled(true);
      redoAction.setEnabled(true);
    }
    public void popupMenuWillBecomeVisible(PopupMenuEvent e) {
      JPopupMenu pop = (JPopupMenu)e.getSource();
      JTextField field = (JTextField)pop.getInvoker();
      boolean flg = field.getSelectedText()!=null;
      cutAction.setEnabled(flg);
      copyAction.setEnabled(flg);
      deleteAction.setEnabled(flg);
      undoAction.setEnabled(manager.canUndo());
      redoAction.setEnabled(manager.canRedo());
    }
  });
  tc.setComponentPopupMenu(popup);
  return popup;
}

References

2009/01/23

create Auto Suggest JComboBox

Code

String[] array = {
    "aaaa", "aaaabbb", "aaaabbbcc", "aaaabbbccddd",
    "abcde", "abefg", "bbb1", "bbb12"};
JComboBox combo = new JComboBox(array);
combo.setEditable(true);
combo.setSelectedIndex(-1);
JTextField field = (JTextField) combo.getEditor().getEditorComponent();
field.setText("");
field.addKeyListener(new ComboKeyHandler(combo));

// ...
class ComboKeyHandler extends KeyAdapter {
  private final JComboBox<String> comboBox;
  private final List<String> list = new ArrayList<>();
  private boolean shouldHide;

  public ComboKeyHandler(JComboBox<String> combo) {
    super();
    this.comboBox = combo;
    for (int i = 0; i < comboBox.getModel().getSize(); i++) {
      list.add(comboBox.getItemAt(i));
    }
  }

  @Override public void keyTyped(final KeyEvent e) {
    EventQueue.invokeLater(new Runnable() {
      @Override public void run() {
        String text = ((JTextField) e.getComponent()).getText();
        ComboBoxModel<String> m;
        if (text.isEmpty()) {
          String[] array = list.toArray(new String[list.size()]);
          m = new DefaultComboBoxModel<String>(array);
          setSuggestionModel(comboBox, m, "");
          comboBox.hidePopup();
        } else {
          m = getSuggestedModel(list, text);
          if (m.getSize() == 0 || shouldHide) {
            comboBox.hidePopup();
          } else {
            setSuggestionModel(comboBox, m, text);
            comboBox.showPopup();
          }
        }
      }
    });
  }

  @Override public void keyPressed(KeyEvent e) {
    JTextField textField = (JTextField) e.getComponent();
    String text = textField.getText();
    shouldHide = false;
    switch (e.getKeyCode()) {
    case KeyEvent.VK_RIGHT:
      for (String s : list) {
        if (s.startsWith(text)) {
          textField.setText(s);
          return;
        }
      }
      break;
    case KeyEvent.VK_ENTER:
      if (!list.contains(text)) {
        list.add(text);
        Collections.sort(list);
        setSuggestionModel(comboBox, getSuggestedModel(list, text), text);
      }
      shouldHide = true;
      break;
    case KeyEvent.VK_ESCAPE:
      shouldHide = true;
      break;
    default:
      break;
    }
  }

  private static void setSuggestionModel(
      JComboBox<String> comboBox, ComboBoxModel<String> mdl, String str) {
    comboBox.setModel(mdl);
    comboBox.setSelectedIndex(-1);
    ((JTextField) comboBox.getEditor().getEditorComponent()).setText(str);
  }

  private static ComboBoxModel<String> getSuggestedModel(List<String> list, String text) {
    DefaultComboBoxModel<String> m = new DefaultComboBoxModel<>();
    for (String s : list) {
      if (s.startsWith(text)) {
        m.addElement(s);
      }
    }
    return m;
  }
}

References

2008/12/15

Adding JPopupMenu to JToolBar-Button

Code

class MenuArrowIcon implements Icon {
  @Override public void paintIcon(Component c, Graphics g, int x, int y) {
    Graphics2D g2 = (Graphics2D) g.create();
    g2.setPaint(Color.BLACK);
    g2.translate(x, y);
    g2.drawLine(2, 3, 6, 3);
    g2.drawLine(3, 4, 5, 4);
    g2.drawLine(4, 5, 4, 5);
    g2.dispose();
  }

  @Override public int getIconWidth()  {
    return 9;
  }

  @Override public int getIconHeight() {
    return 9;
  }
}

class MenuToggleButton extends JToggleButton {
  private static final Icon ARROW_ICON = new MenuArrowIcon();
  private JPopupMenu popup;

  protected MenuToggleButton() {
    this("", null);
  }

  protected MenuToggleButton(Icon icon) {
    this("", icon);
  }

  protected MenuToggleButton(String text) {
    this(text, null);
  }

  protected MenuToggleButton(String text, Icon icon) {
    super();
    Action action = new AbstractAction(text) {
      @Override public void actionPerformed(ActionEvent e) {
        Component b = (Component) e.getSource();
        Optional.ofNullable(getPopupMenu()).ifPresent(p -> p.show(b, 0, b.getHeight()));
      }
    };
    action.putValue(Action.SMALL_ICON, icon);
    setAction(action);
    setFocusable(false);
    setBorder(BorderFactory.createEmptyBorder(4, 4, 4, 4 + ARROW_ICON.getIconWidth()));
  }

  public JPopupMenu getPopupMenu() {
    return popup;
  }

  public void setPopupMenu(JPopupMenu pop) {
    this.popup = pop;
    pop.addPopupMenuListener(new PopupMenuListener() {
      @Override public void popupMenuCanceled(PopupMenuEvent e) {
        /* not needed */
      }

      @Override public void popupMenuWillBecomeVisible(PopupMenuEvent e) {
        /* not needed */
      }

      @Override public void popupMenuWillBecomeInvisible(PopupMenuEvent e) {
        setSelected(false);
      }
    });
  }

  @Override protected void paintComponent(Graphics g) {
    super.paintComponent(g);
    Dimension dim = getSize();
    Insets ins = getInsets();
    int x = dim.width - ins.right;
    int y = ins.top + (dim.height - ins.top - ins.bottom - ARROW_ICON.getIconHeight()) / 2;
    ARROW_ICON.paintIcon(this, g, x, y);
  }
}

References