-
Bug
-
Resolution: Unresolved
-
P3
-
8, 11, 16, 17
-
generic
-
generic
ADDITIONAL SYSTEM INFORMATION :
C:\>ver
Microsoft Windows [Version 10.0.19042.985]
C:\>java -version
openjdk version "11.0.11" 2021-04-20 LTS
OpenJDK Runtime Environment 18.9 (build 11.0.11+9-LTS)
OpenJDK 64-Bit Server VM 18.9 (build 11.0.11+9-LTS, mixed mode)
A DESCRIPTION OF THE PROBLEM :
Changing the menubar in a frame does not free the unused menucomponents.
STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
After starting the example-application press the only button to toggle the menubar (severl times).
Stop when only the File-menu is shown.
Use VisualVM or other tool to see the current instances.
There should be only 1 JMenuBar, 1 JMenu and 1 JMenuItem.
This is true for the Motif-LookAndFeel (USE_WINDOWS_LAF = false)
but not for the Windows-LookAndFeel (USE_WINDOWS_LAF = true)
Use 'static boolean USE_WINDOWS_LAF = true;' to switch between test (restart application neccessary).
EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
There should be only 1 JMenuBar, 1 JMenu and 1 JMenuItem (ok with Motif).
ACTUAL -
This is not true when the Windows-LookAndFeel is used.
---------- BEGIN SOURCE ----------
public class MenuTest
extends JFrame {
// --- switch for L&F -> Windows-L&F leaks JMenuBar, JMenu, JMenuItem
static boolean USE_WINDOWS_LAF = true;
private static final String PREFIX = "PREFIX";
private JPanel pnl;
private boolean doFullMenu;
// --- just to have it like in a real app
private JMenuBar mMenuBar;
private JMenu mFile;
private JMenu mEdit;
private JMenu mView;
private JMenu mHelp;
private JMenuItem miFile_New;
private JMenuItem miFile_Open;
private JMenuItem miFile_Print;
private JMenuItem miFile_SwitchMenu;
public MenuTest() {
super("MenuTest " + UIManager.getLookAndFeel().getDescription() + " ("
+ UIManager.getLookAndFeel().getName() + ")");
setDefaultCloseOperation(DISPOSE_ON_CLOSE);
createContent();
}
private Action a(final String name, final ActionListener al) {
return new AbstractAction(name) {
@Override
public void actionPerformed(final ActionEvent e) {
al.actionPerformed(e);
}
};
}
private void createContent() {
pnl = new JPanel(new GridLayout(1, 0));
pnl.add(new JButton(a("switch menubar", ae -> installMB())));
getContentPane().add(pnl);
installMB();
setMinimumSize(new Dimension(300, 100));
}
private void setMnem(final boolean forced, final boolean add2FLM,
final AbstractButton ab, final String key) {
final String mk = key + "_Mnem";
final String mv = mk.substring(0, 1).toLowerCase();
if (forced || !mv.equals(mk)) {
final char c = mv.charAt(0);
ab.setMnemonic(c);
}
}
private JMenu crm(final String key) {
final String miText = key;
final JMenu m = new JMenu(miText);
m.setName(PREFIX + "." + key);
setMnem(true, true, m, key);
return m;
}
private JMenuItem crmi(final String key, final String cmd, final KeyStroke ks,
final ActionListener al) {
final String miText = key;
final JMenuItem mi = new JMenuItem(miText);
mi.setName(PREFIX + "." + key);
if (cmd != null)
mi.setActionCommand(cmd);
setMnem(false, false, mi, key);
if (ks != null)
mi.setAccelerator(ks);
if (al != null)
mi.addActionListener(al);
return mi;
}
private final class ALSwitchMenu
implements ActionListener {
@Override
public void actionPerformed(final ActionEvent e) {
performSwitchMenu(e);
}
}
private final class ALExit
implements ActionListener {
@Override
public void actionPerformed(final ActionEvent e) {
performExit(e);
}
}
private void installMB() {
// --- set null is neccessary !
mMenuBar = null;
mFile = null;
mEdit = null;
mView = null;
mHelp = null;
miFile_New = null;
miFile_Open = null;
miFile_Print = null;
miFile_SwitchMenu = null;
final JMenuBar mb = mMenuBar = new JMenuBar();
mb.add(mFile = crm("File"));
if (doFullMenu) {
mb.add(mEdit = crm("Edit"));
mb.add(mView = crm("View"));
mb.add(mHelp = crm("Help"));
mFile.add(miFile_New =
crmi("New", "miFile_New", KeyStroke.getKeyStroke("alt N"), null));
mFile.add(miFile_Open = crmi("Open", "miFile_Open", null, null));
mFile.add(miFile_Print = crmi("Print", "miFile_Print", null, null));
mFile.addSeparator();
mFile.add(miFile_SwitchMenu =
crmi("Switch menu", "miFile_SwitchMenu", null, new ALSwitchMenu()));
}
mFile.add(crmi("Exit", "miFile_Exit", null, new ALExit()));
setJMenuBar(mb);
revalidate();
doFullMenu = !doFullMenu;
}
private void performSwitchMenu(final ActionEvent e) {
installMB();
}
private void performExit(final ActionEvent e) {
dispose();
}
public static void main(final String[] args) {
if (USE_WINDOWS_LAF) {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (final Exception e) {
e.printStackTrace();
}
}
EventQueue.invokeLater(() -> {
final MenuTest mt = new MenuTest();
mt.pack();
mt.setVisible(true);
});
}
}
---------- END SOURCE ----------
FREQUENCY : always
C:\>ver
Microsoft Windows [Version 10.0.19042.985]
C:\>java -version
openjdk version "11.0.11" 2021-04-20 LTS
OpenJDK Runtime Environment 18.9 (build 11.0.11+9-LTS)
OpenJDK 64-Bit Server VM 18.9 (build 11.0.11+9-LTS, mixed mode)
A DESCRIPTION OF THE PROBLEM :
Changing the menubar in a frame does not free the unused menucomponents.
STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
After starting the example-application press the only button to toggle the menubar (severl times).
Stop when only the File-menu is shown.
Use VisualVM or other tool to see the current instances.
There should be only 1 JMenuBar, 1 JMenu and 1 JMenuItem.
This is true for the Motif-LookAndFeel (USE_WINDOWS_LAF = false)
but not for the Windows-LookAndFeel (USE_WINDOWS_LAF = true)
Use 'static boolean USE_WINDOWS_LAF = true;' to switch between test (restart application neccessary).
EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
There should be only 1 JMenuBar, 1 JMenu and 1 JMenuItem (ok with Motif).
ACTUAL -
This is not true when the Windows-LookAndFeel is used.
---------- BEGIN SOURCE ----------
public class MenuTest
extends JFrame {
// --- switch for L&F -> Windows-L&F leaks JMenuBar, JMenu, JMenuItem
static boolean USE_WINDOWS_LAF = true;
private static final String PREFIX = "PREFIX";
private JPanel pnl;
private boolean doFullMenu;
// --- just to have it like in a real app
private JMenuBar mMenuBar;
private JMenu mFile;
private JMenu mEdit;
private JMenu mView;
private JMenu mHelp;
private JMenuItem miFile_New;
private JMenuItem miFile_Open;
private JMenuItem miFile_Print;
private JMenuItem miFile_SwitchMenu;
public MenuTest() {
super("MenuTest " + UIManager.getLookAndFeel().getDescription() + " ("
+ UIManager.getLookAndFeel().getName() + ")");
setDefaultCloseOperation(DISPOSE_ON_CLOSE);
createContent();
}
private Action a(final String name, final ActionListener al) {
return new AbstractAction(name) {
@Override
public void actionPerformed(final ActionEvent e) {
al.actionPerformed(e);
}
};
}
private void createContent() {
pnl = new JPanel(new GridLayout(1, 0));
pnl.add(new JButton(a("switch menubar", ae -> installMB())));
getContentPane().add(pnl);
installMB();
setMinimumSize(new Dimension(300, 100));
}
private void setMnem(final boolean forced, final boolean add2FLM,
final AbstractButton ab, final String key) {
final String mk = key + "_Mnem";
final String mv = mk.substring(0, 1).toLowerCase();
if (forced || !mv.equals(mk)) {
final char c = mv.charAt(0);
ab.setMnemonic(c);
}
}
private JMenu crm(final String key) {
final String miText = key;
final JMenu m = new JMenu(miText);
m.setName(PREFIX + "." + key);
setMnem(true, true, m, key);
return m;
}
private JMenuItem crmi(final String key, final String cmd, final KeyStroke ks,
final ActionListener al) {
final String miText = key;
final JMenuItem mi = new JMenuItem(miText);
mi.setName(PREFIX + "." + key);
if (cmd != null)
mi.setActionCommand(cmd);
setMnem(false, false, mi, key);
if (ks != null)
mi.setAccelerator(ks);
if (al != null)
mi.addActionListener(al);
return mi;
}
private final class ALSwitchMenu
implements ActionListener {
@Override
public void actionPerformed(final ActionEvent e) {
performSwitchMenu(e);
}
}
private final class ALExit
implements ActionListener {
@Override
public void actionPerformed(final ActionEvent e) {
performExit(e);
}
}
private void installMB() {
// --- set null is neccessary !
mMenuBar = null;
mFile = null;
mEdit = null;
mView = null;
mHelp = null;
miFile_New = null;
miFile_Open = null;
miFile_Print = null;
miFile_SwitchMenu = null;
final JMenuBar mb = mMenuBar = new JMenuBar();
mb.add(mFile = crm("File"));
if (doFullMenu) {
mb.add(mEdit = crm("Edit"));
mb.add(mView = crm("View"));
mb.add(mHelp = crm("Help"));
mFile.add(miFile_New =
crmi("New", "miFile_New", KeyStroke.getKeyStroke("alt N"), null));
mFile.add(miFile_Open = crmi("Open", "miFile_Open", null, null));
mFile.add(miFile_Print = crmi("Print", "miFile_Print", null, null));
mFile.addSeparator();
mFile.add(miFile_SwitchMenu =
crmi("Switch menu", "miFile_SwitchMenu", null, new ALSwitchMenu()));
}
mFile.add(crmi("Exit", "miFile_Exit", null, new ALExit()));
setJMenuBar(mb);
revalidate();
doFullMenu = !doFullMenu;
}
private void performSwitchMenu(final ActionEvent e) {
installMB();
}
private void performExit(final ActionEvent e) {
dispose();
}
public static void main(final String[] args) {
if (USE_WINDOWS_LAF) {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (final Exception e) {
e.printStackTrace();
}
}
EventQueue.invokeLater(() -> {
final MenuTest mt = new MenuTest();
mt.pack();
mt.setVisible(true);
});
}
}
---------- END SOURCE ----------
FREQUENCY : always