
Signature
Andrew Thompson
http://www.PhySci.org/codes/ Web & IT Help
http://www.PhySci.org/ Open-source software suite
http://www.1point1C.org/ Science & Technology
http://www.LensEscapes.com/ Images that escape the mundane
Ok, here goes, I've written some classes wich sort of simulate my app
while keeping things as simple as possible. There are 3 classes, the
TestApp, TestGui and TestThread class. The TestApp sets up a TestGui,
which disables the "stop" menu item and enables "start" and "quit".
When the menu item "start" is selected, a TestThread is started up and
the "start" menu item is disabled and "stop" is enabled.
When "stop" is then selected, the stop signal is given to the
TestThread, which then sleeps for 10 seconds to simulate activity. Note
that the TestApp disables the stop menu item, and the TestThread
enables the start menu item again, so that during the 10 seconds of
simulated activity both start and stop are disabled. This is how I want
it, since the user should wait till things are finished before
proceeding.
Now here is the problem, *sometimes* (so not all of the time), the menu
does not render correctly, you'll know what I mean when you see it. You
might have to give it a few tries, when it happens is completely
unpredictable (for me anyway). I'm not sure it it has anything to do
with it, but I'm on OS X 10.3.7, using the JDK 1.4.2.
Thanks for any feedback!
Regards, Jonck
Here is the code (not that I use the JGoodies look and feel), these
classes should adhere to the sscce standard.
import javax.swing.UIManager;
/**
* @author jonck
*/
public class TestApp {
private TestGui gui;
private TestThread testThread = null;
public static void main(String args[]) {
try {
UIManager.setLookAndFeel("com.jgoodies.plaf.plastic.Plastic3DLookAndFeel");
} catch (Exception e) {
System.out.println(e);
}
new TestApp();
}
public TestApp() {
setGui(new TestGui(this));
}
public void start() {
if (getTestThread() == null) {
// set up a simulation worker thread
setTestThread(new TestThread(this));
getTestThread().start();
}
// enable the stop menu item and disable the start menu item
getGui().getStop().setEnabled(true);
getGui().getStart().setEnabled(false);
}
public void stop() {
stopThread();
/*
* set the stop menu item to disabled here, and let the start
menu-item be enabled
* by the TestThread, when it's all done
*/
getGui().getStop().setEnabled(false);
}
public void quit() {
stopThread();
System.exit(0);
}
private void stopThread() {
if (getTestThread() != null) {
getTestThread().stop();
setTestThread(null);
}
}
/**
* @return Returns the gui.
*/
public TestGui getGui() {
return gui;
}
/**
* @param gui The gui to set.
*/
public void setGui(TestGui gui) {
this.gui = gui;
}
/**
* @return Returns the thread.
*/
public TestThread getTestThread() {
return testThread;
}
/**
* @param thread The thread to set.
*/
public void setTestThread(TestThread thread) {
this.testThread = thread;
}
}
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
/**
* @author jonck
*/
public class TestGui {
private JFrame mainWindow;
private JMenuBar menuBar;
private JMenu fileMenu;
private JMenuItem start;
private JMenuItem stop;
private JMenuItem quit;
private TestApp app;
public TestGui(TestApp app) {
setApp(app);
initComponents();
}
private void initComponents() {
setMenuBar(new JMenuBar());
setFileMenu(new JMenu("File"));
setStart(new JMenuItem("Start"));
setStop(new JMenuItem("Stop"));
setQuit(new JMenuItem("Quit"));
setMainWindow(new JFrame("Test"));
getMainWindow().setResizable(false);
getFileMenu().add(getStart());
getFileMenu().add(getStop());
getFileMenu().add(getQuit());
getMenuBar().add(getFileMenu());
getMainWindow().setJMenuBar(getMenuBar());
getMainWindow().setSize(100, 100);
getMainWindow().setVisible(true);
// gray out stop
getStop().setEnabled(false);
// add the menu item listeners
LocalActionListener actionListener = new LocalActionListener();
getStart().addActionListener(actionListener);
getStop().addActionListener(actionListener);
getQuit().addActionListener(actionListener);
}
/**
* @return Returns the app.
*/
public TestApp getApp() {
return app;
}
/**
* @param app The app to set.
*/
public void setApp(TestApp app) {
this.app = app;
}
private class LocalActionListener implements ActionListener {
public void actionPerformed(ActionEvent event) {
Object menuItem = event.getSource();
if (menuItem.equals(getStart())) {
getApp().start();
} else if (menuItem.equals(getStop())) {
getApp().stop();
} else if (menuItem.equals(getQuit())) {
getApp().quit();
}
}
}
/**
* @return Returns the fileMenu.
*/
public JMenu getFileMenu() {
return fileMenu;
}
/**
* @param fileMenu The fileMenu to set.
*/
public void setFileMenu(JMenu fileMenu) {
this.fileMenu = fileMenu;
}
/**
* @return Returns the mainWindow.
*/
public JFrame getMainWindow() {
return mainWindow;
}
/**
* @param mainWindow The mainWindow to set.
*/
public void setMainWindow(JFrame mainWindow) {
this.mainWindow = mainWindow;
}
/**
* @return Returns the menuBar.
*/
public JMenuBar getMenuBar() {
return menuBar;
}
/**
* @param menuBar The menuBar to set.
*/
public void setMenuBar(JMenuBar menuBar) {
this.menuBar = menuBar;
}
/**
* @return Returns the quit.
*/
public JMenuItem getQuit() {
return quit;
}
/**
* @param quit The quit to set.
*/
public void setQuit(JMenuItem quit) {
this.quit = quit;
}
/**
* @return Returns the start.
*/
public JMenuItem getStart() {
return start;
}
/**
* @param start The start to set.
*/
public void setStart(JMenuItem start) {
this.start = start;
}
/**
* @return Returns the stop.
*/
public JMenuItem getStop() {
return stop;
}
/**
* @param stop The stop to set.
*/
public void setStop(JMenuItem stop) {
this.stop = stop;
}
}
/**
* @author jonck
*/
public class TestThread implements Runnable {
private Object sleepObject = new Object();
private TestApp app;
private boolean run = true;
public TestThread(TestApp app) {
setApp(app);
}
public void run() {
while (isRun()) {
synchronized (sleepObject) {
try {
// sleep untill awakened, for simulation purposes
only
sleepObject.wait();
} catch (InterruptedException e) {
System.out.println(e);
}
}
try {
Thread.sleep(10000); // sleep for 10 seconds to
simulate activity before stopping
} catch (InterruptedException e) {
System.out.println(e);
}
}
// here enable the menu item start again, since this thread is
all finished now
getApp().getGui().getStart().setEnabled(true);
}
public void start() {
Thread testThread = new Thread(this);
testThread.start();
testThread = null;
}
public void stop() {
setRun(false);
// awaken the thread
synchronized (sleepObject) {
sleepObject.notify();
}
}
/**
* @return Returns the run.
*/
public boolean isRun() {
return run;
}
/**
* @param run The run to set.
*/
public void setRun(boolean run) {
this.run = run;
}
/**
* @return Returns the app.
*/
public TestApp getApp() {
return app;
}
/**
* @param app The app to set.
*/
public void setApp(TestApp app) {
this.app = app;
}
}
jonck@vanderkogel.net - 03 Feb 2005 15:33 GMT
small correction: "not that I use JGoodies look and feel" should be
"note that I use JGoodies look and feel" :-)
Andrew Thompson - 04 Feb 2005 09:48 GMT
>... these classes should adhere to the sscce standard.
Whether they *should* or not is a separate matter. They don't.
You can make all this code compile as a *single* .java souurce file
if you demote both the classes with no main to 'default'.
Your lines wrap in my compiler editor. I also had to 'rejoin'
wrapped lines before I could get it to compile.
Please note those points for future, as I am almost at the stage where
any code that does that does not compile cleanly at first attempt will
go no further with me.
> Here is the code (not that I use the JGoodies look and feel),
Whoa up!
That put me across the line for various reasons.
First, connected to 'no longer an SSCCE' - if it requires
external libraries, because..
1) Karsten provides support for the JGoodies software (which he wrote).
If it is a problem with JGoodies, the best place to sort it is 'support
at jgoodies.com ', where you have the author's entire attention.
But more importantly, ..
2) I suspect this is *not* a problem with the JGoodies PLAF at
all, but something more fundamental in your code. If you can
reproduce the problem with pure core Sun classes, get back to
us here, otherwise take it to the specialist at JGoodies.
OTOH, from experience I can tell you that any theme that sub-classes
the Sun 'Metal' theme has difficulties if the 'Metal' theme itself has
ever been the PLAF of the UI. JGoodies PLAFs are all based on the Metal
them, AFAIR. If your UI starts as Metal before you go to the JGoodies
PLAF's, change that. It might fix the problem.
HTH

Signature
Andrew Thompson
http://www.PhySci.org/codes/ Web & IT Help
http://www.PhySci.org/ Open-source software suite
http://www.1point1C.org/ Science & Technology
http://www.LensEscapes.com/ Images that escape the mundane
jonck@vanderkogel.net - 04 Feb 2005 10:45 GMT
Sorry about the line-wrapping, should have thought of that. I also do
not think it is a problem with JGoodies (have never had any problems
with that most excellent library), but in order to make this test as
closely resemble my real app I thought I would leave it in.
But I take it from your comments that you did not see the behavior as I
described it? Because I am getting the behavior as I described it with
this example. I am not switching PLAFs (not here, nor in my real app),
setting it to JGoodies right away and staying there. Therefore it seems
reasonable to say that the problem lies in the example as I have
presented it here. Hmm... perhaps it's an OS X thing? Might I ask what
platform you tried my examples on (assuming that you did not see the
behavior as I described it)?
Thanks for your assistance, Jonck
Andrew Thompson - 04 Feb 2005 11:01 GMT
> But I take it from your comments that you did not see the behavior as I
> described it?
(I will be blunt)
It crashed on not finding the JGoodies PLAF. (shrugs) I have
JGoodies on the system, but that is not the point.
Either separate it as a cause (and post an example using Sun PLAFs),
or take it to the author.
If you post a pure *core java classes only* example that displays
the behaviour you describe, I (and potentially others) will have
another look at it.

Signature
Andrew Thompson
http://www.PhySci.org/codes/ Web & IT Help
http://www.PhySci.org/ Open-source software suite
http://www.1point1C.org/ Science & Technology
http://www.LensEscapes.com/ Images that escape the mundane
jonck@vanderkogel.net - 12 Feb 2005 13:06 GMT
Ok, after a bit of correspondence with Karsten we determined that this
was indeed an issue with the underlying Metal PLAF. So I set the PLAF
to Metal and the same behavior is indeed taking place (under OS X). If
you would please be so kind to look at my SSCCE, I made sure there are
no lines longer than 80 characters and put all the source in one file.
All you have to do to get this working now is:
javac TestApp.java
java TestApp
Thanks for any feedback, Jonck
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.UIManager;
import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
/**
* @author jonck
*/
public class TestApp {
private TestGui gui;
private TestThread testThread = null;
public static void main(String args[]) {
try {
UIManager.setLookAndFeel(
"javax.swing.plaf.metal.MetalLookAndFeel");
} catch (Exception e) {
System.out.println(e);
}
new TestApp();
}
public TestApp() {
setGui(new TestGui(this));
}
public void start() {
if (getTestThread() == null) {
// set up a simulation worker thread
setTestThread(new TestThread(this));
getTestThread().start();
}
// enable the stop menu item and disable the start menu item
getGui().getStop().setEnabled(true);
getGui().getStart().setEnabled(false);
}
public void stop() {
stopThread();
/*
* set the stop menu item to disabled here, and let the
* start menu-item be enabled by the TestThread
* when it's all done
*/
getGui().getStop().setEnabled(false);
}
public void quit() {
stopThread();
System.exit(0);
}
private void stopThread() {
if (getTestThread() != null) {
getTestThread().stop();
setTestThread(null);
}
}
/**
* @return Returns the gui.
*/
public TestGui getGui() {
return gui;
}
/**
* @param gui The gui to set.
*/
public void setGui(TestGui gui) {
this.gui = gui;
}
/**
* @return Returns the thread.
*/
public TestThread getTestThread() {
return testThread;
}
/**
* @param thread The thread to set.
*/
public void setTestThread(TestThread thread) {
this.testThread = thread;
}
class TestGui {
private JFrame mainWindow;
private JMenuBar menuBar;
private JMenu fileMenu;
private JMenuItem start;
private JMenuItem stop;
private JMenuItem quit;
private TestApp app;
public TestGui(TestApp app) {
setApp(app);
initComponents();
}
private void initComponents() {
setMenuBar(new JMenuBar());
setFileMenu(new JMenu("File"));
setStart(new JMenuItem("Start"));
setStop(new JMenuItem("Stop"));
setQuit(new JMenuItem("Quit"));
setMainWindow(new JFrame("Test"));
getMainWindow().setResizable(false);
getFileMenu().add(getStart());
getFileMenu().add(getStop());
getFileMenu().add(getQuit());
getMenuBar().add(getFileMenu());
getMainWindow().setJMenuBar(getMenuBar());
getMainWindow().setSize(100, 100);
getMainWindow().setVisible(true);
// gray out stop
getStop().setEnabled(false);
// add the menu item listeners
LocalActionListener actionListener = new LocalActionListener();
getStart().addActionListener(actionListener);
getStop().addActionListener(actionListener);
getQuit().addActionListener(actionListener);
}
/**
* @return Returns the app.
*/
public TestApp getApp() {
return app;
}
/**
* @param app The app to set.
*/
public void setApp(TestApp app) {
this.app = app;
}
private class LocalActionListener implements ActionListener {
public void actionPerformed(ActionEvent event) {
Object menuItem = event.getSource();
if (menuItem.equals(getStart())) {
getApp().start();
} else if (menuItem.equals(getStop())) {
getApp().stop();
} else if (menuItem.equals(getQuit())) {
getApp().quit();
}
}
}
/**
* @return Returns the fileMenu.
*/
public JMenu getFileMenu() {
return fileMenu;
}
/**
* @param fileMenu The fileMenu to set.
*/
public void setFileMenu(JMenu fileMenu) {
this.fileMenu = fileMenu;
}
/**
* @return Returns the mainWindow.
*/
public JFrame getMainWindow() {
return mainWindow;
}
/**
* @param mainWindow The mainWindow to set.
*/
public void setMainWindow(JFrame mainWindow) {
this.mainWindow = mainWindow;
}
/**
* @return Returns the menuBar.
*/
public JMenuBar getMenuBar() {
return menuBar;
}
/**
* @param menuBar The menuBar to set.
*/
public void setMenuBar(JMenuBar menuBar) {
this.menuBar = menuBar;
}
/**
* @return Returns the quit.
*/
public JMenuItem getQuit() {
return quit;
}
/**
* @param quit The quit to set.
*/
public void setQuit(JMenuItem quit) {
this.quit = quit;
}
/**
* @return Returns the start.
*/
public JMenuItem getStart() {
return start;
}
/**
* @param start The start to set.
*/
public void setStart(JMenuItem start) {
this.start = start;
}
/**
* @return Returns the stop.
*/
public JMenuItem getStop() {
return stop;
}
/**
* @param stop The stop to set.
*/
public void setStop(JMenuItem stop) {
this.stop = stop;
}
}
class TestThread implements Runnable {
private Object sleepObject = new Object();
private TestApp app;
private boolean run = true;
public TestThread(TestApp app) {
setApp(app);
}
public void run() {
while (isRun()) {
synchronized (sleepObject) {
try {
/*
* sleep untill awakened
* for simulation purposes only
*/
sleepObject.wait();
} catch (InterruptedException e) {
System.out.println(e);
}
}
try {
/*
* sleep for 10 seconds to simulate activity
* before stopping
*/
Thread.sleep(10000);
} catch (InterruptedException e) {
System.out.println(e);
}
}
/*
* here enable the menu item start again
* since this thread is all finished now
*/
getApp().getGui().getStart().setEnabled(true);
}
public void start() {
Thread testThread = new Thread(this);
testThread.start();
testThread = null;
}
public void stop() {
setRun(false);
// awaken the thread
synchronized (sleepObject) {
sleepObject.notify();
}
}
/**
* @return Returns the run.
*/
public boolean isRun() {
return run;
}
/**
* @param run The run to set.
*/
public void setRun(boolean run) {
this.run = run;
}
/**
* @return Returns the app.
*/
public TestApp getApp() {
return app;
}
/**
* @param app The app to set.
*/
public void setApp(TestApp app) {
this.app = app;
}
}
}
jonck@vanderkogel.net - 12 Feb 2005 13:10 GMT
Well, I see that in Google my code is getting completely screwed up. So
if you use Google as your newsgroup browser please let me know and I'll
send a copy of the TestApp.java file over email.
Kind regards, Jonck
jonck@vanderkogel.net - 12 Feb 2005 14:06 GMT
Oh wait, I see that if you click "show options" and then "Show
original" you get the posted text unaltered.