PDA

View Full Version : Java's MenuBar versus the Mac MenuBar & other ?s




toddburch
Mar 21, 2007, 10:11 PM
Point of reference - I'm new to Java and new to Macs.

I'm coding up a Java Swing application... practicing, practicing, practicing.

I can add a JMenuBar to my JFrame, but....

Question 1: how do I add the JMenu items to the Mac OS menu bar (the topmost, forever present, menubar)?

Then, some other questions:

Question 2: Why doesn't a new instance of this app launch when I click File>New? Obvioulsy, "new RPM();" is not doing what I want it to.

Question 3: Why don't I get a visible separator bar in the File pulldown between OPEN and QUIT?

Here's the code I have so far. File name is RPM.java.

Thanks for any insight. Todd (OS X 10.4.8)


public class RPM extends javax.swing.JFrame implements
java.awt.event.ActionListener,
java.awt.event.ComponentListener,
java.awt.event.FocusListener {
javax.swing.JFrame frame ;
static final String HNS = "Hide & Seek" ;

public static void main(String[] args) {

RPM rpm = new RPM() ;

// Create a JFrame

javax.swing.JFrame f = new javax.swing.JFrame("Resize this frame!") ;
f.setSize(300,300) ;
f.setLocation(100,100) ;
rpm.frame = f ;

// Get the JFrame's container.

java.awt.Container content = f.getContentPane() ;

// Add a Single Menu Dropdown. Do the JMenuItems first.
// File
// - New
// - Open
// - Quit
javax.swing.JMenuItem mi_New = new javax.swing.JMenuItem("New") ;
javax.swing.JMenuItem mi_Open = new javax.swing.JMenuItem("Open") ;
javax.swing.JMenuItem mi_Quit = new javax.swing.JMenuItem("Quit") ;


// Define all the jmenuitem special behaviors

mi_New.setAccelerator(
javax.swing.KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_N, java.awt.Event.META_MASK)) ;
mi_New.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent e) { new RPM() ; } } ) ;

mi_Open.setAccelerator(
javax.swing.KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_O, java.awt.Event.META_MASK)) ;
mi_Open.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent e) { System.out.println("Open...") ; } } ) ;

mi_Quit.setAccelerator(
javax.swing.KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_Q, java.awt.Event.META_MASK)) ;
mi_Quit.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent e) { System.exit(0) ; } } ) ;

// Add the File pulldown

javax.swing.JMenu jMenu = new javax.swing.JMenu("File") ;
jMenu.add(mi_New) ;
jMenu.add(mi_Open) ;
jMenu.addSeparator() ;
jMenu.add(mi_Quit) ;

// Get a JMenuBar, add out JMenu to it, and then ad it to the JFrame

javax.swing.JMenuBar jMenuBar = new javax.swing.JMenuBar() ;
jMenuBar.add(jMenu) ;
f.setJMenuBar(jMenuBar) ;

// Set the Layout Manager
content.setLayout(new java.awt.FlowLayout()) ;

// Add some widgets

content.add(new javax.swing.JLabel("Dumb Label")) ;

javax.swing.JButton myButton1 = new javax.swing.JButton("Mango") ;
javax.swing.JButton myButton2 = new javax.swing.JButton(HNS) ;

myButton1.addActionListener(rpm) ;
myButton2.addActionListener(rpm) ;
content.add(myButton1) ;
content.add(myButton2) ;

//* Add a JTextField
javax.swing.JTextField textField = new javax.swing.JTextField(20) ; // 20 columns wide
content.add(textField) ;
textField.addFocusListener(rpm) ;

//f.pack() ; // Collapse the window to the bare minimum size

f.setDefaultCloseOperation(javax.swing.JFrame.EXIT_ON_CLOSE) ; // Close app when RED X is clicked.
f.addComponentListener(rpm) ;

f.setVisible(true) ; // make window appear

}

public void actionPerformed(java.awt.event.ActionEvent ae) {
System.out.println("Button pressed...") ;
System.out.println("ae.getActionCommand()=" + ae.getActionCommand() ) ;
System.out.println("Modifiers are=" + ae.getModifiers() ) ;
if (ae.getActionCommand() == HNS) {
this.frame.setVisible(false) ; // Disappear for 1 second
try {
Thread.sleep(1000) ; // sleep for 1 second
}
catch (Exception e) {}
this.frame.setVisible(true) ; // Make the screen visible again.
}
}

public void componentHidden(java.awt.event.ComponentEvent ce) { // Fired when window is not visible
System.out.println("Component Event Fired = Hidden") ; // See "Hide & Seek" (HNS) button.
}
public void componentShown(java.awt.event.ComponentEvent ce) {
System.out.println("Component Event Fired = Shown") ;
}
public void componentMoved(java.awt.event.ComponentEvent ce) {
System.out.println("Component Event Fired = Moved") ;
}
public void componentResized(java.awt.event.ComponentEvent ce) {
System.out.println("Component Event Fired = Resized") ;
this.frame.setTitle("Width=" + this.frame.getWidth() + ", Height=" + this.frame.getHeight() ) ;
}
public void focusGained(java.awt.event.FocusEvent fe) { // Fires when the JTextField is clicked
System.out.println("Keyboard Focus: Gained") ;
}
public void focusLost(java.awt.event.FocusEvent fe) {
System.out.println("Keyboard Focus: Lost") ;
}
}



portent
Mar 21, 2007, 11:09 PM
Answer 1:: Call
System.setProperty("apple.laf.useScreenMenuBar", "true");
in your main() method.

Answer 2: All of your code is in the main() method, which gets called exactly once (when you first run the program.) So you get exactly one window.

The ActionListener for the "New" menu item is just creating a new RPM instance. It creates it, in memory, but doesn't do anything with it.

To get what you want, you'd need to move most of your code from the main() into a constructor method for the RPM class. Have your main method call new rpm().

Answer 3 I don't rightly know, off the top of my head, and I'm too lazy to find out.

toddburch
Mar 21, 2007, 11:41 PM
Thanks portent - you got me on the right track.

I pulled all but the essentials out of main() into RPM(). Worked great.

Then, I added the System.setProperty - worked perfectly! Now I'll figure out how to tell what platform I'm running on, so when this runs on Windows, it won't issue the call. Research project for me. Something in the back of my mind tells me there will be a method i need in the System class... ;)

Then, I figured out why the File>New wasn't working. Basically, I needed to set it visible. Modfifying the routine to new up an RPM() and then setVisible on it worked perfect... almost. Now I get to go back and take out all the System.exit(0) calls and change them to JFrame kills so when I close any frame, only that frame will go away. Another research project for me.

Thanks again. :)
Todd


public class RPM extends javax.swing.JFrame implements
java.awt.event.ActionListener,
java.awt.event.ComponentListener,
java.awt.event.FocusListener {

static final String HNS = "Hide & Seek" ;

// Main Method. Create a JFrame and Display it.
public static void main(String[] args) {

System.setProperty("apple.laf.useScreenMenuBar", "true"); // Use Mac's menubar.
javax.swing.JFrame rpm = new RPM() ;
rpm.setVisible(true) ;

}


public RPM() {
super("RPM V1.0") ;

this.setTitle("Resize this frame!") ;
this.setSize(300,300) ;
this.setLocation(100,100) ;


java.awt.Container content = this.getContentPane() ;

// Add a Single Menu Dropdown. Do the JMenuItems first.
// File
// - New
// - Open
// - Quit
javax.swing.JMenuItem mi_New = new javax.swing.JMenuItem("New") ;
javax.swing.JMenuItem mi_Open = new javax.swing.JMenuItem("Open") ;
javax.swing.JMenuItem mi_Quit = new javax.swing.JMenuItem("Quit") ;


// Define all the jmenuitem special behaviors

mi_New.setAccelerator(
javax.swing.KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_N, java.awt.Event.META_MASK)) ;
mi_New.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent e) {
javax.swing.JFrame newFrame = new RPM() ;
newFrame.setVisible(true) ;
} } ) ;

mi_Open.setAccelerator(
javax.swing.KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_O, java.awt.Event.META_MASK)) ;
mi_Open.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent e) { System.out.println("Open...") ; } } ) ;

mi_Quit.setAccelerator(
javax.swing.KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_Q, java.awt.Event.META_MASK)) ;
mi_Quit.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent e) { System.exit(0) ; } } ) ;

// Add the File pulldown

javax.swing.JMenu jMenu = new javax.swing.JMenu("File") ;
jMenu.add(mi_New) ;
jMenu.add(mi_Open) ;
jMenu.addSeparator() ;
jMenu.add(mi_Quit) ;

// Get a JMenuBar, add out JMenu to it, and then ad it to the JFrame

javax.swing.JMenuBar jMenuBar = new javax.swing.JMenuBar() ;
jMenuBar.add(jMenu) ;
this.setJMenuBar(jMenuBar) ;

// Set the Layout Manager
content.setLayout(new java.awt.FlowLayout()) ;

// Add some widgets

content.add(new javax.swing.JLabel("Dumb Label")) ;

javax.swing.JButton myButton1 = new javax.swing.JButton("Mango") ;
javax.swing.JButton myButton2 = new javax.swing.JButton(HNS) ;

myButton1.addActionListener(this) ;
myButton2.addActionListener(this) ;
content.add(myButton1) ;
content.add(myButton2) ;

//* Add a JTextField
javax.swing.JTextField textField = new javax.swing.JTextField(20) ; // 20 columns wide
content.add(textField) ;
textField.addFocusListener(this) ;

//this.pack() ; // Collapse the window to the bare minimum size

this.setDefaultCloseOperation(javax.swing.JFrame.EXIT_ON_CLOSE) ; // Close app when RED X is clicked.
this.addComponentListener(this) ;


}

public void actionPerformed(java.awt.event.ActionEvent ae) {
System.out.println("Button pressed...") ;
System.out.println("ae.getActionCommand()=" + ae.getActionCommand() ) ;
System.out.println("Modifiers are=" + ae.getModifiers() ) ;
if (ae.getActionCommand() == HNS) {
this.setVisible(false) ; // Disappear for 1 second
try {
Thread.sleep(1000) ; // sleep for 1 second
}
catch (Exception e) {}
this.setVisible(true) ; // Make the screen visible again.
}
}

public void componentHidden(java.awt.event.ComponentEvent ce) { // Fired when window is not visible
System.out.println("Component Event Fired = Hidden") ; // See "Hide & Seek" (HNS) button.
}
public void componentShown(java.awt.event.ComponentEvent ce) {
System.out.println("Component Event Fired = Shown") ;
}
public void componentMoved(java.awt.event.ComponentEvent ce) {
System.out.println("Component Event Fired = Moved") ;
}
public void componentResized(java.awt.event.ComponentEvent ce) {
System.out.println("Component Event Fired = Resized") ;
this.setTitle("Width=" + this.getWidth() + ", Height=" + this.getHeight() ) ;
}
public void focusGained(java.awt.event.FocusEvent fe) { // Fires when the JTextField is clicked
System.out.println("Keyboard Focus: Gained") ;
}
public void focusLost(java.awt.event.FocusEvent fe) {
System.out.println("Keyboard Focus: Lost") ;
}
}

toddburch
Mar 22, 2007, 12:01 AM
Well that didn't take too long...


String sys = System.getProperty("os.name").substring(0,3) ;
System.out.println("->" + sys + "<-") ;

if (sys.equalsIgnoreCase("Mac")) {
System.setProperty("apple.laf.useScreenMenuBar", "true");
}

However, in the end, Windows appeared to ignore the call anyways. But, it's cleaner.

Thanks again.

therevolution
Mar 22, 2007, 12:41 AM
There's no harm in calling

System.setProperty("apple.laf.useScreenMenuBar", "true");

on Windows. As you found out, the Windows JVM will simply ignore that property. I don't really agree that it's "cleaner" to add the check first - I bet the check uses up more cycles than simply setting the property, not to mention it clutters up your code.

But this is a pretty trivial thing for me to nitpick. Just felt like pointing it out anyway. :p

toddburch
Mar 22, 2007, 08:02 AM
I researched this a litle more; found it documented in the XCode Help>Documentation. It appears this system property can be used as a command line option as well:

java -Dapple.laf.useScreenMenuBar="true" yourApplication

The purpose the added logic is serving - allowing me to learn more about the Java environment, the Mac environment and Java programming - is a greater need right now than it's performance implication. ;)

Thanks for the feedback. It's always welcome - no matter how nitpicky. The beauty of any (nitpicky or otherwise) comment about code or coding style is that I can take it or leave it. No harm, no foul.

Todd