/* * @(#)ExitAction.java * * Copyright (c) 1996-2010 The authors and contributors of JHotDraw. * You may not use, copy or modify this file, except in compliance with the * accompanying license terms. */ package org.jhotdraw.app.action.app; import javax.annotation.Nullable; import org.jhotdraw.gui.*; import org.jhotdraw.gui.event.*; import org.jhotdraw.util.*; import java.awt.*; import java.awt.event.*; import javax.swing.*; import java.io.*; import java.net.URI; import org.jhotdraw.app.Application; import org.jhotdraw.app.View; import org.jhotdraw.app.action.AbstractApplicationAction; import org.jhotdraw.gui.URIChooser; import org.jhotdraw.net.URIUtil; /** * Exits the application after letting the user review all unsaved views. * <p> * This action is called when the user selects the Exit item in the Application * menu, or when the application receives a Quit event from Mac OS X Finder. * The menu item is automatically created by the application. * <p> * This action is automatically created by the application and put into * the {@code ApplicationModel} before {@link org.jhotdraw.app.ApplicationModel#initApplication} * is called. * * @author Werner Randelshofer * @version $Id$ */ public class ExitAction extends AbstractApplicationAction { private static final long serialVersionUID = 1L; public static final String ID = "application.exit"; private Component oldFocusOwner; private View unsavedView; /** Creates a new instance. */ public ExitAction(Application app) { super(app); ResourceBundleUtil labels = ResourceBundleUtil.getBundle("org.jhotdraw.app.Labels"); labels.configureAction(this, ID); } @Override public void actionPerformed(ActionEvent evt) { final Application app = getApplication(); if (app.isEnabled()) { app.setEnabled(false); int unsavedViewsCount = 0; View documentToBeReviewed = null; URI unsavedURI = null; for (View p : app.views()) { if (p.hasUnsavedChanges()) { if (p.isEnabled()) { documentToBeReviewed = p; } unsavedURI = p.getURI(); unsavedViewsCount++; } } if (unsavedViewsCount > 0 && documentToBeReviewed == null) { // Silently abort, if no view can be reviewed. app.setEnabled(true); return; } final ResourceBundleUtil labels = ResourceBundleUtil.getBundle("org.jhotdraw.app.Labels"); switch (unsavedViewsCount) { case 0: { doExit(); break; } case 1: { unsavedView = documentToBeReviewed; oldFocusOwner = SwingUtilities.getWindowAncestor(unsavedView.getComponent()).getFocusOwner(); unsavedView.setEnabled(false); JOptionPane pane = new JOptionPane( "<html>" + UIManager.getString("OptionPane.css") + "<b>" + labels.getFormatted("application.exit.doYouWantToSave.message",// (unsavedURI == null) ? labels.getString("unnamedFile") : URIUtil.getName(unsavedURI)) + "</b><p>" + labels.getString("application.exit.doYouWantToSave.details"), JOptionPane.WARNING_MESSAGE); Object[] options = {labels.getString("application.exit.saveOption"), labels.getString("application.exit.cancelOption"), labels.getString("application.exit.dontSaveOption")}; pane.setOptions(options); pane.setInitialValue(options[0]); pane.putClientProperty("Quaqua.OptionPane.destructiveOption", 2); JSheet.showSheet(pane, unsavedView.getComponent(), new SheetListener() { @Override public void optionSelected(SheetEvent evt) { Object value = evt.getValue(); if (value == null || value.equals(labels.getString("application.exit.cancelOption"))) { unsavedView.setEnabled(true); app.setEnabled(true); } else if (value.equals(labels.getString("application.exit.dontSaveOption"))) { doExit(); unsavedView.setEnabled(true); } else if (value.equals(labels.getString("application.exit.saveOption"))) { saveChanges(); } } }); break; } default: { JOptionPane pane = new JOptionPane( "<html>" + UIManager.get("OptionPane.css") + "<b>" + labels.getFormatted("application.exit.doYouWantToReview.message", unsavedViewsCount) + "</b><p>" + labels.getString("application.exit.doYouWantToReview.details"), JOptionPane.QUESTION_MESSAGE); Object[] options = {labels.getString("application.exit.reviewChangesOption"), labels.getString("application.exit.cancelOption"), labels.getString("application.exit.discardChangesOption")}; pane.setOptions(options); pane.setInitialValue(options[0]); pane.putClientProperty( "Quaqua.OptionPane.destructiveOption", 2); JDialog dialog = pane.createDialog(app.getComponent(), null); Rectangle screenBounds = dialog.getGraphicsConfiguration().getBounds(); if (app.getComponent() == null || !screenBounds.contains(app.getComponent().getBounds())) { // place dialog in center and in upper third of screen dialog.setLocation((screenBounds.width - dialog.getWidth()) / 2, (screenBounds.height - dialog.getHeight()) / 3); } dialog.setVisible(true); Object value = pane.getValue(); if (value == null || value.equals(labels.getString("application.exit.cancelOption"))) { app.setEnabled(true); } else if (value.equals(labels.getString("application.exit.discardChangesOption"))) { doExit(); app.setEnabled(true); } else if (value.equals(labels.getString("application.exit.reviewChangesOption"))) { unsavedView = documentToBeReviewed; reviewChanges(); } } } } } protected URIChooser getChooser(View view) { URIChooser chsr = (URIChooser) (view.getComponent()).getClientProperty("saveChooser"); if (chsr == null) { chsr = getApplication().getModel().createSaveChooser(getApplication(), view); view.getComponent().putClientProperty("saveChooser", chsr); } return chsr; } protected void saveChanges() { View v = unsavedView; if (v.getURI() == null) { URIChooser chooser = getChooser(v); //int option = fileChooser.showSaveDialog(this); JSheet.showSaveSheet(chooser, v.getComponent(), new SheetListener() { @Override public void optionSelected(final SheetEvent evt) { if (evt.getOption() == JFileChooser.APPROVE_OPTION) { final URI uri = evt.getChooser().getSelectedURI(); saveToFile(uri, evt.getChooser()); } else { unsavedView.setEnabled(true); if (oldFocusOwner != null) { oldFocusOwner.requestFocus(); } getApplication().setEnabled(true); } } }); } else { saveToFile(v.getURI(), null); } } protected void reviewChanges() { if (unsavedView.isEnabled()) { final ResourceBundleUtil labels = ResourceBundleUtil.getBundle("org.jhotdraw.app.Labels"); oldFocusOwner = SwingUtilities.getWindowAncestor(unsavedView.getComponent()).getFocusOwner(); unsavedView.setEnabled(false); URI unsavedURI = unsavedView.getURI(); JOptionPane pane = new JOptionPane( "<html>" + UIManager.getString("OptionPane.css") + labels.getFormatted("application.exit.doYouWantToSave.message", // (unsavedURI==null)?unsavedView.getTitle():URIUtil.getName(unsavedURI)), JOptionPane.WARNING_MESSAGE); Object[] options = {labels.getString("application.exit.saveOption"), labels.getString("application.exit.cancelOption"), labels.getString("application.exit.dontSaveOption")}; pane.setOptions(options); pane.setInitialValue(options[0]); pane.putClientProperty("Quaqua.OptionPane.destructiveOption", 2); JSheet.showSheet(pane, unsavedView.getComponent(), new SheetListener() { @Override public void optionSelected(SheetEvent evt) { Object value = evt.getValue(); if (value == null || value.equals(labels.getString("application.exit.cancelOption"))) { unsavedView.setEnabled(true); getApplication().setEnabled(true); } else if (value.equals(labels.getString("application.exit.dontSaveOption"))) { getApplication().dispose(unsavedView); reviewNext(); } else if (value.equals(labels.getString("application.exit.saveOption"))) { saveChangesAndReviewNext(); } } }); } else { getApplication().setEnabled(true); } } protected void saveChangesAndReviewNext() { final View v = unsavedView; if (v.getURI() == null) { URIChooser chooser = getChooser(v); JSheet.showSaveSheet(chooser, unsavedView.getComponent(), new SheetListener() { @Override public void optionSelected(final SheetEvent evt) { if (evt.getOption() == URIChooser.APPROVE_OPTION) { final URI uri = evt.getChooser().getSelectedURI(); saveToFileAndReviewNext(uri, evt.getChooser()); } else { v.setEnabled(true); if (oldFocusOwner != null) { oldFocusOwner.requestFocus(); } getApplication().setEnabled(true); } } }); } else { saveToFileAndReviewNext(v.getURI(), null); } } protected void reviewNext() { int unsavedViewsCount = 0; View documentToBeReviewed = null; for (View p : getApplication().views()) { if (p.hasUnsavedChanges()) { if (p.isEnabled()) { documentToBeReviewed = p; } unsavedViewsCount++; } } if (unsavedViewsCount == 0) { doExit(); } else if (documentToBeReviewed != null) { unsavedView = documentToBeReviewed; reviewChanges(); } else { getApplication().setEnabled(true); //System.out.println("exit silently aborted"); } } protected void saveToFile(final URI uri, @Nullable final URIChooser chooser) { final View v = unsavedView; v.execute(new BackgroundTask() { @Override protected void construct() throws IOException { v.write(uri, chooser); } @Override protected void done() { v.setURI(uri); doExit(); } @Override protected void failed(Throwable error) { ResourceBundleUtil labels = ResourceBundleUtil.getBundle("org.jhotdraw.app.Labels"); JSheet.showMessageSheet(v.getComponent(), "<html>" + UIManager.getString("OptionPane.css") + "<b>" + labels.format("file.save.couldntSave.message", URIUtil.getName(uri)) + "</b><p>" + error, JOptionPane.ERROR_MESSAGE); } @Override public void finished() { v.setEnabled(true); if (oldFocusOwner != null) { oldFocusOwner.requestFocus(); } getApplication().setEnabled(true); } }); } protected void saveToFileAndReviewNext(final URI uri, @Nullable final URIChooser chooser) { final View v = unsavedView; v.execute(new BackgroundTask() { @Override protected void construct() throws IOException { v.write(uri, chooser); } @Override protected void done() { v.setURI(uri); getApplication().dispose(unsavedView); reviewNext(); } @Override protected void failed(Throwable error) { ResourceBundleUtil labels = ResourceBundleUtil.getBundle("org.jhotdraw.app.Labels"); JSheet.showMessageSheet(v.getComponent(), "<html>" + UIManager.getString("OptionPane.css") + "<b>" + labels.format("file.save.couldntSave.message", uri) + "</b><p>" + error, JOptionPane.ERROR_MESSAGE); v.setEnabled(true); if (oldFocusOwner != null) { oldFocusOwner.requestFocus(); } getApplication().setEnabled(true); } }); } protected void doExit() { getApplication().destroy(); } }