Creating an Alert Box with Swing/mxj


    Jan 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
    // // 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); }
    }

    • Jan 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
    • Jan 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
    • Jan 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
    • Jan 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
    • Jan 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
    • Jan 25 2006 | 6:24 pm
      Owen,
      Thanks a lot for the tip. Using this technique did the trick.
      -Henry
    • Jan 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
    • Jan 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
    • Jan 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); }
      }
    • Jan 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
    • Jan 29 2006 | 12:45 am
    • Jan 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!