/* * @(#)OpenFileAction.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.file; import org.jhotdraw.util.*; import org.jhotdraw.gui.*; import java.awt.*; import java.awt.event.*; import javax.swing.*; import java.io.*; import java.net.URI; import java.util.prefs.Preferences; 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; import org.jhotdraw.util.prefs.PreferencesUtil; /** * Presents an {@code URIChooser} and loads the selected URI into an * empty view. If no empty view is available, a new view is created. * <p> * This action is called when the user selects the Open item in the File * menu. The menu item is automatically created by the application. * A Recent Files sub-menu is also automatically generated. * <p> * If you want this behavior in your application, you have to create it * and put it in your {@code ApplicationModel} in method * {@link org.jhotdraw.app.ApplicationModel#initApplication}. * <p> * This action is designed for applications which automatically * create a new view for each opened file. This action goes together with * {@link NewFileAction}, {@link OpenDirectoryAction} and {@link CloseFileAction}. * This action should not be used together with {@link LoadFileAction}. * <hr> * <b>Features</b> * *<p><em>Allow multiple views per URI</em><br> * When the feature is disabled, {@code OpenFileAction} prevents opening an URI * which* is opened in another view.<br> * See {@link org.jhotdraw.app} for a description of the feature. * </p> * * <p><em>Open last URI on launch</em><br> * {@code OpenFileAction} supplies data for this feature by calling * {@link Application#addRecentURI} when it successfully opened a file. * See {@link org.jhotdraw.app} for a description of the feature. * </p> * * @author Werner Randelshofer * @version $Id$ */ public class OpenFileAction extends AbstractApplicationAction { private static final long serialVersionUID = 1L; public static final String ID = "file.open"; /** Creates a new instance. */ public OpenFileAction(Application app) { super(app); ResourceBundleUtil labels = ResourceBundleUtil.getBundle("org.jhotdraw.app.Labels"); labels.configureAction(this, ID); } protected URIChooser getChooser(View view) { // Note: We pass null here, because we want the application-wide chooser return getApplication().getOpenChooser(null); } @Override public void actionPerformed(ActionEvent evt) { final Application app = getApplication(); if (app.isEnabled()) { app.setEnabled(false); // Search for an empty view View emptyView = app.getActiveView(); if (emptyView == null || !emptyView.isEmpty() || !emptyView.isEnabled()) { emptyView = null; } final View view; boolean disposeView; if (emptyView == null) { view = app.createView(); app.add(view); disposeView = true; } else { view = emptyView; disposeView = false; } URIChooser chooser = getChooser(view); chooser.setDialogType(JFileChooser.OPEN_DIALOG); if (showDialog(chooser, app.getComponent()) == JFileChooser.APPROVE_OPTION) { app.show(view); URI uri = chooser.getSelectedURI(); // Prevent same URI from being opened more than once if (!getApplication().getModel().isAllowMultipleViewsPerURI()) { for (View v : getApplication().getViews()) { if (v.getURI() != null && v.getURI().equals(uri)) { v.getComponent().requestFocus(); if (disposeView) { app.dispose(view); } app.setEnabled(true); return; } } } openViewFromURI(view, uri, chooser); } else { if (disposeView) { app.dispose(view); } app.setEnabled(true); } } } protected void openViewFromURI(final View view, final URI uri, final URIChooser chooser) { final Application app = getApplication(); app.setEnabled(true); view.setEnabled(false); // If there is another view with the same URI we set the multiple open // id of our view to max(multiple open id) + 1. int multipleOpenId = 1; for (View aView : app.views()) { if (aView != view && aView.isEmpty()) { multipleOpenId = Math.max(multipleOpenId, aView.getMultipleOpenId() + 1); } } view.setMultipleOpenId(multipleOpenId); view.setEnabled(false); // Open the file view.execute(new BackgroundTask() { @Override public void construct() throws IOException { boolean exists = true; try { exists = new File(uri).exists(); } catch (IllegalArgumentException e) { } if (exists) { view.read(uri, chooser); } else { ResourceBundleUtil labels = ResourceBundleUtil.getBundle("org.jhotdraw.app.Labels"); throw new IOException(labels.getFormatted("file.open.fileDoesNotExist.message", URIUtil.getName(uri))); } } @Override protected void done() { final Application app = getApplication(); view.setURI(uri); view.setEnabled(true); Frame w = (Frame) SwingUtilities.getWindowAncestor(view.getComponent()); if (w != null) { w.setExtendedState(w.getExtendedState() & ~Frame.ICONIFIED); w.toFront(); } view.getComponent().requestFocus(); app.addRecentURI(uri); app.setEnabled(true); } @Override protected void failed(Throwable value) { value.printStackTrace(); view.setEnabled(true); app.setEnabled(true); String message = value.getMessage() != null ? value.getMessage() : value.toString(); ResourceBundleUtil labels = ResourceBundleUtil.getBundle("org.jhotdraw.app.Labels"); JSheet.showMessageSheet(view.getComponent(), "<html>" + UIManager.getString("OptionPane.css") + "<b>" + labels.getFormatted("file.open.couldntOpen.message", URIUtil.getName(uri)) + "</b><p>" + ((message == null) ? "" : message), JOptionPane.ERROR_MESSAGE); } }); } /** We implement JFileChooser.showDialog by ourselves, so that we can center * dialogs properly on screen on Mac OS X. */ public int showDialog(URIChooser chooser, Component parent) { final Component finalParent = parent; final int[] returnValue = new int[1]; final JDialog dialog = createDialog(chooser, finalParent); dialog.addWindowListener(new WindowAdapter() { @Override public void windowClosing(WindowEvent e) { returnValue[0] = JFileChooser.CANCEL_OPTION; } }); chooser.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { if ("CancelSelection".equals(e.getActionCommand())) { returnValue[0] = JFileChooser.CANCEL_OPTION; dialog.setVisible(false); } else if ("ApproveSelection".equals(e.getActionCommand())) { returnValue[0] = JFileChooser.APPROVE_OPTION; dialog.setVisible(false); } } }); returnValue[0] = JFileChooser.ERROR_OPTION; chooser.rescanCurrentDirectory(); dialog.setVisible(true); //chooser.firePropertyChange("JFileChooserDialogIsClosingProperty", dialog, null); dialog.removeAll(); dialog.dispose(); return returnValue[0]; } /** We implement JFileChooser.showDialog by ourselves, so that we can center * dialogs properly on screen on Mac OS X. */ protected JDialog createDialog(URIChooser chooser, Component parent) throws HeadlessException { String title = chooser.getDialogTitle(); if (chooser instanceof JFileChooser) { ((JFileChooser) chooser).getAccessibleContext().setAccessibleDescription(title); } JDialog dialog; Window window = (parent == null || (parent instanceof Window)) ? (Window) parent : SwingUtilities.getWindowAncestor(parent); dialog = new JDialog(window, title, Dialog.ModalityType.APPLICATION_MODAL); dialog.setComponentOrientation(chooser.getComponent().getComponentOrientation()); Container contentPane = dialog.getContentPane(); contentPane.setLayout(new BorderLayout()); contentPane.add(chooser.getComponent(), BorderLayout.CENTER); if (JDialog.isDefaultLookAndFeelDecorated()) { boolean supportsWindowDecorations = UIManager.getLookAndFeel().getSupportsWindowDecorations(); if (supportsWindowDecorations) { dialog.getRootPane().setWindowDecorationStyle(JRootPane.FILE_CHOOSER_DIALOG); } } //dialog.pack(); Preferences prefs = PreferencesUtil.userNodeForPackage(getApplication().getModel().getClass()); PreferencesUtil.installFramePrefsHandler(prefs, "openChooser", dialog); /* if (window.getBounds().isEmpty()) { Rectangle screenBounds = window.getGraphicsConfiguration().getBounds(); dialog.setLocation(screenBounds.x + (screenBounds.width - dialog.getWidth()) / 2, // screenBounds.y + (screenBounds.height - dialog.getHeight()) / 3); } else { dialog.setLocationRelativeTo(parent); }*/ return dialog; } }