Hi Scott,

First off, you can only do this in DesktopApplicationContext, since
BrowserApplicationContext can't reliably prevent the applet from getting
destroyed.  If you are in DesktopAplpicationContext, then the very short
answer is that you return true from Application.shutdown() to prevent the
shutdown until the user has answered your question.  Then, when they answer
your dialog (or sheet), you call DesktopApplicationContext.exit(), and allow
the shutdown at that time.

The long answer is that it's kinda tricky to get right.  To aid you on your
way, I'll blindly paste snippets of the source of my Pivot app that does
exactly this - you should be able to gleam what you need from it.

I've attached two snippet source files.  Let me know if they don't come
through.

-T

On Thu, Oct 22, 2009 at 5:58 PM, Scott Lanham <li...@sael.com.au> wrote:

> Hi All,
>
> I am at the point in my application where during Application.shutdown I
> would
> like a dialog to popup if they haven't saved their changes. The dialog
> would
> ask if they want to Save, Not Save, Cancel with the last cancelling the
> shutdown. I am uncertain of how to approach this and was hoping someone
> could
> give me some guidance :-)
>
> Thanks,
>
> Scott.
>
import org.apache.pivot.collections.ArrayList;
import org.apache.pivot.collections.List;
import org.apache.pivot.collections.Map;
import org.apache.pivot.collections.immutable.ImmutableList;
import org.apache.pivot.serialization.JSONSerializer;
import org.apache.pivot.util.ListenerList;
import org.apache.pivot.util.Resources;
import org.apache.pivot.util.Vote;
import org.apache.pivot.util.concurrent.Task;
import org.apache.pivot.util.concurrent.TaskGroup;
import org.apache.pivot.util.concurrent.TaskListener;
import org.apache.pivot.web.BasicAuthentication;
import org.apache.pivot.web.GetQuery;
import org.apache.pivot.web.Query;
import org.apache.pivot.wtk.Action;
import org.apache.pivot.wtk.Component;
import org.apache.pivot.wtk.DesktopApplicationContext;
import org.apache.pivot.wtk.Direction;
import org.apache.pivot.wtk.Display;
import org.apache.pivot.wtk.Keyboard;
import org.apache.pivot.wtk.MessageType;
import org.apache.pivot.wtk.Prompt;
import org.apache.pivot.wtk.Sheet;
import org.apache.pivot.wtk.SheetCloseListener;
import org.apache.pivot.wtk.SheetStateListener;
import org.apache.pivot.wtk.TaskAdapter;
import org.apache.pivot.wtk.Window;
import org.apache.pivot.wtkx.WTKXSerializer;

public class Foo implements Application {
    // Application state
    private boolean dirty = false;
    private boolean halting = false;

    // Main app window
    private Window window;

    ...

    @Override
    public boolean shutdown(boolean optional) throws Exception {
        boolean consumed = false;

        if (optional && dirty) {
            // We'll give the user the option of saving before shutting down.
            consumed = true;

            // Only perform this work once
            if (!halting) {
                halting = true;

                int ownedWindowCount = window.getOwnedWindowCount();

                if (ownedWindowCount > 0) {
                    // Delay until all our owned sheets are closed
                    for (int i = 0; i < ownedWindowCount; i++) {
                        Window ownedWindow = window.getOwnedWindow(i);

                        if (ownedWindow instanceof Sheet) {
                            Sheet ownedSheet = (Sheet)ownedWindow;

                            ownedSheet.getSheetStateListeners().add(new SheetStateListener.Adapter() {
                                @Override
                                public void sheetClosed(Sheet sheet) {
                                    if (window.getOwnedWindowCount() == 0) {
                                        // We're clear to show our confirmation
                                        confirmShutdown();
                                    }
                                }
                            });

                            ownedSheet.close(false);
                        }
                    }
                } else {
                    // We're clear to show our confirmation immediately
                    confirmShutdown();
                }
            }
        }

        return consumed;
    }

    /**
     * Gives the user a "save/discard/cancel" prompt, allowing them to save
     * their work before shutting the application down.
     */
    private void confirmShutdown() {
        SaveDiscardCancelTask saveDiscardCancelTask = new SaveDiscardCancelTask();
        saveDiscardCancelTask.execute(new TaskAdapter<Choice>(new TaskListener<Choice>() {
            @Override
            public void taskExecuted(Task<Choice> task) {
                switch (task.getResult()) {
                case SAVE:
                    // Inform the user that the invoice was saved
                    List<String> options = new ArrayList<String>(resources.getString("options.ok"));
                    String message = resources.getString("invoiceSaved");
                    Prompt prompt = new Prompt(MessageType.WARNING, message, options, null);
                    prompt.setSelectedOption(0);

                    // Shutdown the application when the user has acknowledged our prompt
                    prompt.open(window, new SheetCloseListener() {
                        @Override
                        public void sheetClosed(Sheet sheet) {
                            DesktopApplicationContext.exit();
                        }
                    });

                    break;

                case DISCARD:
                    // Clear the dirty bit to allow the shutdown to go through
                    dirty = false;

                    // Shutdown immediately
                    DesktopApplicationContext.exit();

                    break;

                case CANCEL:
                    halting = false;

                    break;
                }
            }

            @Override
            public void executeFailed(Task<Choice> task) {
                task.getFault().printStackTrace();
            }
        }));
    }

    ...
}
import org.apache.pivot.collections.List;
import org.apache.pivot.serialization.JSONSerializer;
import org.apache.pivot.util.concurrent.Task;
import org.apache.pivot.util.concurrent.TaskExecutionException;
import org.apache.pivot.util.concurrent.TaskListener;
import org.apache.pivot.wtk.Action;
import org.apache.pivot.wtk.ApplicationContext;
import org.apache.pivot.wtk.Label;
import org.apache.pivot.wtk.MessageType;
import org.apache.pivot.wtk.Prompt;
import org.apache.pivot.wtk.Sheet;
import org.apache.pivot.wtk.SheetCloseListener;
import org.apache.pivot.wtk.Window;

/**
 * Task that alerts the user that there are unsaved changes and
 * gives them the choice of saving, discarding their changes, or
 * cancelling what they were about to do.  This task's result is the user's
 * choice.
 * <p>
 * If the user chooses to save, this task will automatically save their changes
 * for them and will not complete until the save is complete. If the save
 * faults, this task will propagate the fault.
 * <p>
 * If the user chooses to discard or cancel, this task takes no extra action
 * other than returning the user's choice.
*/
public class SaveDiscardCancelTask extends Task<SaveDiscardCancelTask.Choice> {
    /**
     * The user's choice.
     */
    public enum Choice {
        SAVE,
        DISCARD,
        CANCEL;
    }

    private Choice choice;
    private Exception fault;

    @Override
    public synchronized Choice execute() throws TaskExecutionException {
        // Reset our variables
        choice = null;
        fault = null;

        ApplicationContext.queueCallback(new Runnable() {
            public void run() {
                try {
                    String message = "Save Changes?";
                    List<?> options = JSONSerializer.parseList("['Save', 'Discard', 'Cancel']");
                    Label body = new Label("You have unsaved changes. " +
                        "Do you want to save your changes?");
                    body.getStyles().put("wrapText", true);

                    final Prompt prompt = new Prompt(MessageType.WARNING, message, options, body);
                    prompt.setSelectedOption(0);

                    Window owner = Foo.getWindow();
                    prompt.open(owner, new SheetCloseListener() {
                        @Override
                        public void sheetClosed(Sheet sheet) {
                            boolean notify = true;

                            if (prompt.getResult()) {
                                int selectedOption = prompt.getSelectedOption();

                                if (selectedOption == 0) {
                                    // Save
                                    notify = false;

                                    SaveAction saveAction = (SaveAction)
                                        Action.getNamedActions().get("save");

                                    saveAction.perform(new TaskListener<Void>() {
                                        @Override
                                        public void taskExecuted(Task<Void> task) {
                                            choice = Choice.SAVE;

                                            synchronized(SaveDiscardCancelTask.this) {
                                                SaveDiscardCancelTask.this.notify();
                                            }
                                        }

                                        @Override
                                        public void executeFailed(Task<Void> task) {
                                            fault = task.getFault();

                                            synchronized(SaveDiscardCancelTask.this) {
                                                SaveDiscardCancelTask.this.notify();
                                            }
                                        }
                                    });
                                } else if (selectedOption == 1) {
                                    // Discard
                                    choice = Choice.DISCARD;
                                } else {
                                    // Explicit cancel
                                    choice = Choice.CANCEL;
                                }
                            } else {
                                // Implicit cancel
                                choice = Choice.CANCEL;
                            }

                            if (notify) {
                                synchronized(SaveDiscardCancelTask.this) {
                                    SaveDiscardCancelTask.this.notify();
                                }
                            }
                        }
                    });
                } catch (Exception ex) {
                    fault = ex;

                    synchronized(SaveDiscardCancelTask.this) {
                        SaveDiscardCancelTask.this.notify();
                    }
                }
            }
        });

        try {
            wait();
        } catch (InterruptedException ex) {
            throw new TaskExecutionException(ex);
        }

        if (fault != null) {
            throw new TaskExecutionException(fault);
        }

        return choice;
    }
}

Reply via email to