All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.jhotdraw.app.AbstractApplication Maven / Gradle / Ivy

The newest version!
/*
 * @(#)AbstractApplication.java
 *
 * Copyright (c) 1996-2010 by the original authors of JHotDraw and all its 
 * contributors. All rights reserved.
 *
 * You may not use, copy or modify this file, except in compliance with the
 * license agreement you entered into with the copyright holders. For details
 * see accompanying license terms.
 */
package org.jhotdraw.app;

import edu.umd.cs.findbugs.annotations.Nullable;
import java.awt.Container;
import java.awt.Window;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.File;
import java.net.URISyntaxException;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.jhotdraw.beans.*;
import org.jhotdraw.gui.Worker;
import org.jhotdraw.util.*;
import java.util.prefs.*;
import javax.swing.*;
import java.net.URI;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import org.jhotdraw.app.action.file.ClearRecentFilesMenuAction;
import org.jhotdraw.app.action.file.LoadDirectoryAction;
import org.jhotdraw.app.action.file.LoadFileAction;
import org.jhotdraw.app.action.file.LoadRecentFileAction;
import org.jhotdraw.app.action.file.OpenRecentFileAction;
import org.jhotdraw.gui.URIChooser;
import org.jhotdraw.util.prefs.PreferencesUtil;

/**
 * This abstract class can be extended to implement an {@link Application}.
 * 

* {@code AbstractApplication} supports the command line parameter * {@code -open filename} to open views for specific URI's upon launch of * the application. * * @author Werner Randelshofer * @version $Id: AbstractApplication.java 722 2010-11-26 08:49:25Z rawcoder $ */ public abstract class AbstractApplication extends AbstractBean implements Application { private LinkedList views = new LinkedList(); private Collection unmodifiableViews; private boolean isEnabled = true; protected ResourceBundleUtil labels; protected ApplicationModel model; private Preferences prefs; @Nullable private View activeView; public final static String VIEW_COUNT_PROPERTY = "viewCount"; private LinkedList recentFiles = new LinkedList(); private final static int maxRecentFilesCount = 10; private ActionMap actionMap; private URIChooser openChooser; private URIChooser saveChooser; private URIChooser importChooser; private URIChooser exportChooser; /** Creates a new instance. */ public AbstractApplication() { } @Override public void init() { prefs = PreferencesUtil.userNodeForPackage((getModel() == null) ? getClass() : getModel().getClass()); int count = prefs.getInt("recentFileCount", 0); for (int i = 0; i < count; i++) { String path = prefs.get("recentFile." + i, null); if (path != null) { try { recentFiles.add(new URI(path)); } catch (URISyntaxException ex) { // Silently don't add this URI } } } } @Override public void start(List uris) { if (uris.isEmpty()) { final View v = createView(); add(v); v.setEnabled(false); show(v); // Set the start view immediately active, so that // ApplicationOpenFileAction picks it up on Mac OS X. setActiveView(v); v.execute(new Worker() { @Override public Object construct() { v.clear(); return null; } @Override public void finished() { v.setEnabled(true); } }); } else { for (final URI uri : uris) { final View v = createView(); add(v); v.setEnabled(false); show(v); // Set the start view immediately active, so that // ApplicationOpenFileAction picks it up on Mac OS X. setActiveView(v); v.execute(new Worker() { @Override public Object construct() throws Exception { v.read(uri, null); return null; } @Override public void finished() { v.setURI(uri); v.setEnabled(true); } }); } } } @Override public final View createView() { View v = basicCreateView(); v.setActionMap(createViewActionMap(v)); return v; } @Override public void setModel(ApplicationModel newValue) { ApplicationModel oldValue = model; model = newValue; firePropertyChange("model", oldValue, newValue); } @Override public ApplicationModel getModel() { return model; } protected View basicCreateView() { return model.createView(); } /** * Sets the active view. Calls deactivate on the previously * active view, and then calls activate on the given view. * * @param newValue Active view, can be null. */ public void setActiveView(@Nullable View newValue) { View oldValue = activeView; if (activeView != null) { activeView.deactivate(); } activeView = newValue; if (activeView != null) { activeView.activate(); } firePropertyChange(ACTIVE_VIEW_PROPERTY, oldValue, newValue); } /** * Gets the active view. * * @return The active view can be null. */ @Override @Nullable public View getActiveView() { return activeView; } @Override public String getName() { return model.getName(); } @Override public String getVersion() { return model.getVersion(); } @Override public String getCopyright() { return model.getCopyright(); } @Override public void stop() { for (View p : new LinkedList(views())) { dispose(p); } } @Override public void destroy() { stop(); model.destroyApplication(this); System.exit(0); } @Override public void remove(View v) { hide(v); if (v == getActiveView()) { setActiveView(null); } int oldCount = views.size(); views.remove(v); v.setApplication(null); firePropertyChange(VIEW_COUNT_PROPERTY, oldCount, views.size()); } @Override public void add(View v) { if (v.getApplication() != this) { int oldCount = views.size(); views.add(v); v.setApplication(this); v.init(); model.initView(this, v); firePropertyChange(VIEW_COUNT_PROPERTY, oldCount, views.size()); } } protected abstract ActionMap createViewActionMap(View p); @Override public void dispose(View view) { remove(view); model.destroyView(this, view); view.dispose(); } @Override public Collection views() { if (unmodifiableViews == null) { unmodifiableViews = Collections.unmodifiableCollection(views); } return unmodifiableViews; } @Override public boolean isEnabled() { return isEnabled; } @Override public void setEnabled(boolean newValue) { boolean oldValue = isEnabled; isEnabled = newValue; firePropertyChange("enabled", oldValue, newValue); } public Container createContainer() { return new JFrame(); } /** Launches the application. * * @param args This implementation supports the command-line parameter "-open" * which can be followed by one or more filenames or URI's. */ @Override public void launch(String[] args) { configure(args); // Get URI's from command line final List uris = getOpenURIsFromMainArgs(args); SwingUtilities.invokeLater(new Runnable() { @Override public void run() { init(); start(uris); } }); } /** Parses the arguments to the main method and returns a list of URI's * for which views need to be opened upon launch of the application. *

* This implementation supports the command-line parameter "-open" * which can be followed by one or more filenames or URI's. *

* This method is invoked from the {@code Application.launch} method. * * @param args Arguments to the main method. * @return A list of URI's parsed from the arguments. Returns an empty list * if no URI's shall be opened. */ protected List getOpenURIsFromMainArgs(String[] args) { LinkedList uris = new LinkedList(); for (int i = 0; i < args.length; ++i) { if (args[i].equals("-open")) { for (++i; i < args.length; ++i) { if (args[i].startsWith("-")) { break; } URI uri; uri = new File(args[i]).toURI(); uris.add(uri); } } } return uris; } protected void initLabels() { labels = ResourceBundleUtil.getBundle("org.jhotdraw.app.Labels"); } @Override public void configure(String[] args) { } @Override public void removePalette(Window palette) { } @Override public void addPalette(Window palette) { } @Override public void removeWindow(Window window) { } @Override public void addWindow(Window window, @Nullable View p) { } protected Action getAction(@Nullable View view, String actionID) { return getActionMap(view).get(actionID); } /** Adds the specified action as a menu item to the supplied menu. */ protected void addAction(JMenu m, @Nullable View view, String actionID) { addAction(m, getAction(view, actionID)); } /** Adds the specified action as a menu item to the supplied menu. */ protected void addAction(JMenu m, Action a) { if (a != null) { if (m.getClientProperty("needsSeparator") == Boolean.TRUE) { m.addSeparator(); m.putClientProperty("needsSeparator", null); } JMenuItem mi; mi = m.add(a); mi.setIcon(null); mi.setToolTipText(null); } } /** Adds the specified action as a menu item to the supplied menu. */ protected void addMenuItem(JMenu m, JMenuItem mi) { if (mi != null) { if (m.getClientProperty("needsSeparator") == Boolean.TRUE) { m.addSeparator(); m.putClientProperty("needsSeparator", null); } m.add(mi); } } /** Adds a separator to the supplied menu. The separator will only be added, if the previous item is not a separator. */ protected void maybeAddSeparator(JMenu m) { JPopupMenu pm = m.getPopupMenu(); if (pm.getComponentCount() > 0 // && !(pm.getComponent(pm.getComponentCount() - 1) instanceof JSeparator)) { m.addSeparator(); } } @Override public java.util.List getRecentURIs() { return Collections.unmodifiableList(recentFiles); } @Override public void clearRecentURIs() { @SuppressWarnings("unchecked") java.util.List oldValue = (java.util.List) recentFiles.clone(); recentFiles.clear(); prefs.putInt("recentFileCount", recentFiles.size()); firePropertyChange("recentFiles", Collections.unmodifiableList(oldValue), Collections.unmodifiableList(recentFiles)); } @Override public void addRecentURI(URI uri) { @SuppressWarnings("unchecked") java.util.List oldValue = (java.util.List) recentFiles.clone(); if (recentFiles.contains(uri)) { recentFiles.remove(uri); } recentFiles.addFirst(uri); if (recentFiles.size() > maxRecentFilesCount) { recentFiles.removeLast(); } prefs.putInt("recentFileCount", recentFiles.size()); int i = 0; for (URI f : recentFiles) { prefs.put("recentFile." + i, f.toString()); i++; } firePropertyChange("recentFiles", oldValue, 0); firePropertyChange("recentFiles", Collections.unmodifiableList(oldValue), Collections.unmodifiableList(recentFiles)); } protected JMenu createOpenRecentFileMenu(@Nullable View view) { JMenuItem mi; JMenu m; m = new JMenu(); labels.configureMenu(m, // (getAction(view, LoadFileAction.ID) != null || // getAction(view, LoadDirectoryAction.ID) != null) ?// "file.loadRecent" :// "file.openRecent"// ); m.setIcon(null); m.add(getAction(view, ClearRecentFilesMenuAction.ID)); new OpenRecentMenuHandler(m, view); return m; } /** Updates the menu items in the "Open Recent" file menu. */ private class OpenRecentMenuHandler implements PropertyChangeListener, Disposable { private JMenu openRecentMenu; private LinkedList openRecentActions = new LinkedList(); @Nullable private View view; public OpenRecentMenuHandler(JMenu openRecentMenu, @Nullable View view) { this.openRecentMenu = openRecentMenu; this.view = view; if (view != null) { view.addDisposable(this); } updateOpenRecentMenu(); addPropertyChangeListener(this); } @Override public void propertyChange(PropertyChangeEvent evt) { String name = evt.getPropertyName(); if (name == "recentFiles") { updateOpenRecentMenu(); } } /** * Updates the "File > Open Recent" menu. */ protected void updateOpenRecentMenu() { if (openRecentMenu.getItemCount() > 0) { JMenuItem clearRecentFilesItem = (JMenuItem) openRecentMenu.getItem( openRecentMenu.getItemCount() - 1); openRecentMenu.remove(openRecentMenu.getItemCount() - 1); // Dispose the actions and the menu items that are currently in the menu for (Action action : openRecentActions) { if (action instanceof Disposable) { ((Disposable) action).dispose(); } } openRecentActions.clear(); openRecentMenu.removeAll(); // Create new actions and add them to the menu if (getAction(view, LoadFileAction.ID) != null || // getAction(view, LoadDirectoryAction.ID) != null) { for (URI f : getRecentURIs()) { LoadRecentFileAction action = new LoadRecentFileAction(AbstractApplication.this, view, f); openRecentMenu.add(action); openRecentActions.add(action); } } else { for (URI f : getRecentURIs()) { OpenRecentFileAction action = new OpenRecentFileAction(AbstractApplication.this, f); openRecentMenu.add(action); openRecentActions.add(action); } } if (getRecentURIs().size() > 0) { openRecentMenu.addSeparator(); } // Add a separator and the clear recent files item. openRecentMenu.add(clearRecentFilesItem); } } @Override public void dispose() { removePropertyChangeListener(this); // Dispose the actions and the menu items that are currently in the menu for (Action action : openRecentActions) { if (action instanceof Disposable) { ((Disposable) action).dispose(); } } openRecentActions.clear(); } } @Override public URIChooser getOpenChooser(View v) { if (v == null) { if (openChooser == null) { openChooser = model.createOpenChooser(this, null); List ruris = getRecentURIs(); if (ruris.size() > 0) { try { openChooser.setSelectedURI(ruris.get(0)); } catch (IllegalArgumentException e) { // Ignore illegal values in recent URI list. } } } return openChooser; } else { URIChooser chooser = (URIChooser) v.getComponent().getClientProperty("openChooser"); if (chooser == null) { chooser = model.createOpenChooser(this, v); v.getComponent().putClientProperty("openChooser", chooser); List ruris = getRecentURIs(); if (ruris.size() > 0) { try { chooser.setSelectedURI(ruris.get(0)); } catch (IllegalArgumentException e) { // Ignore illegal values in recent URI list. } } } return chooser; } } @Override public URIChooser getSaveChooser(View v) { if (v == null) { if (saveChooser == null) { saveChooser = model.createSaveChooser(this, null); } return saveChooser; } else { URIChooser chooser = (URIChooser) v.getComponent().getClientProperty("saveChooser"); if (chooser == null) { chooser = model.createSaveChooser(this, v); v.getComponent().putClientProperty("saveChooser", chooser); try { chooser.setSelectedURI(v.getURI()); } catch (IllegalArgumentException e) { // ignore illegal values } } return chooser; } } @Override public URIChooser getImportChooser(View v) { if (v == null) { if (importChooser == null) { importChooser = model.createImportChooser(this, null); } return importChooser; } else { URIChooser chooser = (URIChooser) v.getComponent().getClientProperty("importChooser"); if (chooser == null) { chooser = model.createImportChooser(this, v); v.getComponent().putClientProperty("importChooser", chooser); } return chooser; } } @Override public URIChooser getExportChooser(View v) { if (v == null) { if (exportChooser == null) { exportChooser = model.createExportChooser(this, null); } return exportChooser; } else { URIChooser chooser = (URIChooser) v.getComponent().getClientProperty("exportChooser"); if (chooser == null) { chooser = model.createExportChooser(this, v); v.getComponent().putClientProperty("exportChooser", chooser); } return chooser; } } /** * Sets the application-wide action map. */ public void setActionMap(ActionMap m) { actionMap = m; } /** * Gets the action map. */ @Override public ActionMap getActionMap(@Nullable View v) { return (v == null) ? actionMap : v.getActionMap(); } }