Forums > Java

Creating an Alert Box with Swing/mxj

January 23, 2006 | 6:42 pm

Hi All,

I’ve put together a simple mxj class to create an alert box using Swing. It compiles and runs fine on our Athlon 64 4000+, running XP with MaxMSP 4.5.6, with JVM Version 1.5.0_04, and displays the dialog upon receiving a bang with little or no noticeable lag.

However, when I run the app on my OS X 10.4.4 machine, a G4 1 gHz, running 4.5.6 with JVM 1.5.0_05, I get the "spinning wheel of death" and Max hangs indefinitely and requires me to force quit. I should clarify that the code compiles fine and gives me no errors on either machine.

Both the patch and the java code are included in the text below. I’ve also attached the .class file as well.

Does anyone know why this might be?

Thanks in Advance,
-Henry

< ----save below as SaveChanges_test.pat----->

max v2;
#N vpatcher 100 100 326 424;
#P window setfont "Sans Serif" 9.;
#P newex 75 140 59 9109513 fromsymbol;
#P message 115 90 49 9109513 name bob;
#P button 105 255 15 0;
#P button 90 237 15 0;
#P button 75 219 15 0;
#P newex 75 169 57 9109513 select 0 1 2;
#P button 75 69 33 0;
#P newex 75 115 85 9109513 mxj SaveChanges;
#P connect 6 0 0 0;
#P connect 1 0 0 0;
#P connect 0 0 7 0;
#P connect 7 0 2 0;
#P connect 2 0 3 0;
#P connect 2 1 4 0;
#P connect 2 2 5 0;
#P pop;

< ----save below as SaveChanges.java and compile----->

//
// SaveChanges.java
//
// 01.21.06, Henry Till (henrytill@gmail.com)
//

import com.cycling74.max.*;
import javax.swing.JOptionPane;

public class SaveChanges extends MaxObject
{
String name = null;

public SaveChanges(Atom[] atoms) {
declareInlets(new int[] { 1 });
declareOutlets(new int[] { 1 });
}

public void bang() {

Object[] options = {"Don’t Save",
"Cancel",
"Save"};

int n = JOptionPane.showOptionDialog(null, // null to hatch from parent frame
"Do you want to save changes to "
+ name,
"Save Changes?",
JOptionPane.YES_NO_CANCEL_OPTION,
JOptionPane.QUESTION_MESSAGE,
null,
options,
options[2]);

if (n == JOptionPane.YES_OPTION) {
outlet(0, "2");

} else if (n == JOptionPane.NO_OPTION) {
outlet(0, "0");
} else if (n == JOptionPane.CANCEL_OPTION) {
outlet(0, "1");
} else {
post("no answer given");
}
}

public void inlet(int i) { }

public void inlet(float f) { }

public void list(Atom[] list) { }

public void anything(String s, Atom[] args) {
if(s.equals("name"))
name = Atom.toOneString(args);
}

}


January 23, 2006 | 7:22 pm

You have to call nextWindowIsModal() before displaying the Swing window, that should fix it.
Look at the javadocs for MaxSystem, it’s somewhere in there.

Roby


January 23, 2006 | 8:26 pm

Hi Roby, Thanks for the reply. The api doc makes it sound as though
this should do the trick.

Just to clarify, would it be correct to put this line:

MaxSystem.nextWindowIsModal();

right before the following block?

int n = JOptionPane.showOptionDialog(null, // null to hatch from
parent frame
"Do you want to save changes to "
+ name,
"Save Changes?",
JOptionPane.YES_NO_CANCEL_OPTION,
JOptionPane.QUESTION_MESSAGE,
null,
options,
options[2]);

This compiled okay, but when I went to run it on OS X, it still hung,
which made me think maybe I am doing this wrong.

-H


January 23, 2006 | 9:40 pm

> Just to clarify, would it be correct to put this line:
>
> MaxSystem.nextWindowIsModal();
>
> right before the following block?
>

> This compiled okay, but when I went to run it on OS X, it still hung,
> which made me think maybe I am doing this wrong.
>

What I do is the following:
I declare in my class:
MaxSystem maxsys = new MaxSystem();

and later before displaying the window I put:

maxsys.nextWindowIsModal();

Roby


January 23, 2006 | 10:56 pm

Roby et al,

That’s clearly the right way to do it – so I changed my code, and
it’s compiling fine, but still hanging when I run it.

Have you or anybody else successfully displayed an Alert Box using
JOptionPane in mxj on OS X before?

-Henry


January 24, 2006 | 11:08 am

Henry Till wrote:
> Have you or anybody else successfully displayed an Alert Box using
> JOptionPane in mxj on OS X before?

Have you tried using SwingUtilities.invokeLater() to handle Swing
display stuff asynchronously in Swing’s own event thread? Thus:

SwingUtilities.invokeLater(new Runnable(){
public void run(){
JOptionPane.showConfirmDialog(null,"Hello"); //eg
}
});


Owen


January 25, 2006 | 6:24 pm

Owen,

Thanks a lot for the tip. Using this technique did the trick.

-Henry


January 25, 2006 | 8:56 pm

Henry,
Would you be so kind to share your code of the original example
as enhanced with this trick? I am particularly interested in how
you communicate the result of the asynchronously executed OptionPane
call
back to the mxj thread.
thanks
-jennek


January 26, 2006 | 12:19 pm

Hi Jennek,

In general you don’t need to – the rest of the action can be done in the
Swing thread, or some other event handling thread. In this specific
case there doesn’t seem to be much choice as it was the very condition
of having the mxj thread wait on the swing thread that seemed to be
causing deadlock.


Owen


January 26, 2006 | 9:05 pm

OK, I was worried that calls to outlet would cause more threading
problems,
as they run in in a Max thread. But from the ‘Writing Max externals
in Java’
document from Topher I understand that this is safe to do (v0.3 page 42)
"It is worth noting that when using Java threads inside of your Max
external, any outlet
calls you make back into the Max application will automatically be
deferred for handling
by the low-priority main thread for normal outlet calls and the high-
priority scheduler
thread for outletHigh calls."

One thing puzzles me: why is there no hang up on WinXP?
Anyway, I am glad to have learned about invokeLater().

For the sake of archiving, I’ll post my edition of your code below.
-jennek

//
// SaveChanges.java
//
// 01.21.06, Henry Till (henrytill@gmail.com)
//

import com.cycling74.max.*;
import javax.swing.*;

public class SaveChanges extends MaxObject {
String name = null;

public SaveChanges(Atom[] atoms) {
declareInlets(new int[] { 1 });
declareOutlets(new int[] { 1 });
}

public void bang() {

SwingUtilities.invokeLater(new Runnable() {
public void run() {

Object[] options = { "Don’t Save", "Cancel", "Save" };

int n = JOptionPane
.showOptionDialog(null, // null to hatch from parent
// frame
"Do you want to save changes to " + name,
"Save Changes?",
JOptionPane.YES_NO_CANCEL_OPTION,
JOptionPane.QUESTION_MESSAGE, null, options,
options[2]);

if (n == JOptionPane.YES_OPTION) {
outlet(0, "2");

} else if (n == JOptionPane.NO_OPTION) {
outlet(0, "0");
} else if (n == JOptionPane.CANCEL_OPTION) {
outlet(0, "1");
} else {
post("no answer given");
}

}
});

}

public void inlet(int i) {
}

public void inlet(float f) {
}

public void list(Atom[] list) {
}

public void anything(String s, Atom[] args) {
if (s.equals("name"))
name = Atom.toOneString(args);
}

}


January 26, 2006 | 11:13 pm

jennek geels wrote:

> One thing puzzles me: why is there no hang up on WinXP?

It seems to have something to do with Carbon-Cocoa interaction. As far
as I can tell, one has to make sure your main thread and the AWT/Swing
thread are never in contention or a deadlock will occur.


Owen


January 29, 2006 | 12:45 am


January 29, 2006 | 1:16 am

Yes. There is indeed much potential for deadlock when using swing on OS X due to the fact that swing does its event handling via cocoa and max/msp has its own event handlin loop which uses carbon. It definitely took a significant amount of vudu to get swing to work at all on os x.

Regardless. Anytime you display a frame or other UI element you should do it from the swing/awt thread. From suns own docs.

"To avoid the possibility of thread problems, we recommend that you use invokeLater to create the GUI on the event-dispatching thread for all new applications. If you have old programs that are working fine they are probably OK; however you might want to convert them when it’s convenient to do so. "

This is probably particularly important on OS X given the fact that we are dealing with cocoa java embedded in a carbon application. I wish it wasn’t so but i think we need to live with it for the time being!


Viewing 13 posts - 1 through 13 (of 13 total)