/*
 * ModalDialogs.java
 *
 * Purpose:
 *   Shows how to use modal dialogs (including FileDialog) so that
 *   they don't deadlock on Macintosh and Windows machines.
 *   It has been tested on Macintosh (Metrowerks Java), Windows 95
 *   (Sun's JDK) and Solaris (Sun's JDK), always with Java v1.0.2.
 *
 * I, the author, don't claim to provide the best solution with this
 * example. However, I personally found that this is the only way to
 * get it to work in all cases that I encountered.
 * Of course, I cannot take any responsibility for these examples.
 * As always, be sure to save your stuff before you test suspicious
 * software. I hope that you'll find this useful anyway.
 *
 * If you know a better way to handle modal dialogs or if you have other
 * comments that would make my example look or work better, please let
 * me know.
 *
 * By the way, the provided solution here has a valuable side-effect:
 * If you handle _all_ your received events through the messaging system
 * (see Mediator class) and not only those where you need to use modal
 * dialogs, you get all your events serialized automatically. Think
 * about it.
 *
 * Written for the public domain by Thomas Tempelmann, August 15 1996
 * Developed on a Power Macintosh 7600 using Metrowerks CodeWarrior 9
 * Author contact: tt@muc.de, <http://www.muc.de/~tt/>
 */

import java.awt.*;
import java.util.*;

public class ModalDialogs extends Frame {

    // main stand-alone application entry.
    public static void main (String args[]) {
        new ModalDialogs ();
    }

    // The constructor of the Frame
    ModalDialogs () {
        setLayout (new FlowLayout ());
        add (new TestButton (this, "Click here for an investigation"));
        resize (250, 80);
        move (230, 100);
        show();
    }
}

/**
 * This class listens to the Button that was added to the
 * ModalDialogs' window above.
 */
class TestButton extends Button {

    final boolean badSolution = false;  // change to 'true' to see your Wintel/Mac deadlock

    Frame parent;

    // The Button's constructor
    public TestButton (Frame parent, String title) {
        super (title);
        this.parent = parent;
    }

    // The Button's handler.
    // This is invoked when the user clicks the button
    // in the ModalDialogs' window.
    public boolean action (Event e, Object arg) {
        
        System.out.println ("OK, here we go...");
        
        Inquisitor fred = new Inquisitor (parent);

        if (badSolution) {
            // This one will not work on Windows & Macintosh systems properly,
            // since you may not invoke modal dialogs from within event handler
            // callbacks, like handleEvent().
            fred.process ();
        } else {
            // Here's the solution: get it processed by a different thread
            // and finish this event callback method without any further
            // user interaction.
            Mediator.put (fred);
        }

        return false;
    }
}

/**
 * This is a handler for a modal dialog.
 * Taken from the book "Exploring Java", chapter 10, pg. 317)
 * Enhancements over the book's version: 'done' added.
 */
class ModalDialog extends Dialog {
    private boolean done = false;
    private boolean answer = false;

    // The constructor
    // Adds the text of the question and a button to its Window content.
    ModalDialog (Frame frame, String question) {
        super (frame, true);
        add ("Center", new Label(question));
        Panel yn = new Panel();
        yn.add (new Button("Yes, Sir"));
        add ("South", yn);
        resize (120, 80);
        move (280, 120);
        validate ();
        show ();
    }

    // The overidden action method
    // - will be called if the button is pressed
    synchronized public boolean action (Event e, Object arg) {
        answer = true;
        done = true;
        notifyAll();    // releases the lock in answer()
        dispose();      // closes dialog's window
        return true;
    }

    // The answer method waits until the action() method has been called.
    synchronized public boolean answer() {
        if (!done) {    // without this line it won't work on real MT GUI systems (Unix)
            try {
                wait ();
            } catch (InterruptedException e) { }
        }
        return answer;
    }
}


/**
 * This class contains the method that wants to use a modal dialog.
 */
class Inquisitor implements MessageHandler {

    Frame pope;

    Inquisitor (Frame parent) {
        pope = parent;
    }

    /**
     * In this method the modal dialog will be opened.
     */
    public void process () {
        ModalDialog query = new ModalDialog (pope, "Answer!");
        boolean yes = query.answer();
        if (yes) {
            System.out.println ("Good.");
            System.out.println ();
        }
    }
}


/**
 * This interface defines the method 'process' which will invoked
 * by the @see Mediator.
 */
interface MessageHandler {
    public void process ();
}

/**
 * The Message Queue Handler
 * You add your things-to-do to his queue by calling his
 * static method 'put'.
 * Immediately after your other (important) jobs, like
 * menu selections, mouse button clicks, are handled,
 * he will invoke the method 'process' of the object
 * that was passed to his 'put' method.
 */
final class Mediator extends Thread {

    private static Vector msgs = new Vector ();

    private static Mediator agent;

    static {
        // create one local instance of Mediator (required for wait & notify)
        agent = new Mediator ();
    }

    private Mediator () {
        super ();
        setPriority (Thread.MIN_PRIORITY);
        start ();
    }

    public static void put (MessageHandler h) {
        agent.sput (h);
    }

    private synchronized void sput (MessageHandler h) {
        msgs.addElement (h);
        notify ();
    }

    private synchronized MessageHandler get () throws InterruptedException {
        if (msgs.size () == 0) {
            wait ();
        }
        MessageHandler h = (MessageHandler) msgs.firstElement ();
        msgs.removeElement (h);
        return h;
    }

    public void run () {
        while (true) {
            try {
                MessageHandler h = get ();
                h.process ();
            } catch (InterruptedException e) { }
        }
    }
}

// EOF

