/*
* @(#)OSXApplication.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;
import javax.annotation.Nullable;
import org.jhotdraw.app.action.app.AbstractPreferencesAction;
import org.jhotdraw.app.action.window.TogglePaletteAction;
import org.jhotdraw.app.action.window.FocusWindowAction;
import org.jhotdraw.app.action.window.MaximizeWindowAction;
import org.jhotdraw.app.action.window.MinimizeWindowAction;
import org.jhotdraw.app.action.file.SaveFileAsAction;
import org.jhotdraw.app.action.file.SaveFileAction;
import org.jhotdraw.app.action.file.PrintFileAction;
import org.jhotdraw.app.action.file.NewFileAction;
import org.jhotdraw.app.action.file.OpenFileAction;
import org.jhotdraw.app.action.file.CloseFileAction;
import org.jhotdraw.app.action.file.OpenDirectoryAction;
import org.jhotdraw.app.action.file.ExportFileAction;
import org.jhotdraw.app.action.app.OpenApplicationAction;
import org.jhotdraw.app.action.app.OpenApplicationFileAction;
import org.jhotdraw.app.action.app.AboutAction;
import org.jhotdraw.app.action.app.ExitAction;
import org.jhotdraw.app.action.app.PrintApplicationFileAction;
import org.jhotdraw.app.action.app.ReOpenApplicationAction;
import org.jhotdraw.app.osx.OSXPaletteHandler;
import org.jhotdraw.gui.Worker;
import org.jhotdraw.util.*;
import org.jhotdraw.util.prefs.*;
import java.util.*;
import java.util.prefs.*;
import java.awt.event.*;
import java.beans.*;
import java.awt.*;
import javax.swing.*;
import java.io.*;
import java.net.URI;
import org.jhotdraw.app.action.ActionUtil;
import org.jhotdraw.app.action.edit.AbstractFindAction;
import org.jhotdraw.app.action.edit.ClearSelectionAction;
import org.jhotdraw.app.action.edit.CopyAction;
import org.jhotdraw.app.action.edit.CutAction;
import org.jhotdraw.app.action.edit.DeleteAction;
import org.jhotdraw.app.action.edit.DuplicateAction;
import org.jhotdraw.app.action.edit.PasteAction;
import org.jhotdraw.app.action.edit.RedoAction;
import org.jhotdraw.app.action.edit.SelectAllAction;
import org.jhotdraw.app.action.edit.UndoAction;
import org.jhotdraw.app.action.file.ClearFileAction;
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.NewWindowAction;
import org.jhotdraw.app.osx.OSXAdapter;
import org.jhotdraw.net.URIUtil;
/**
* {@code OSXApplication} handles the lifecycle of multiple {@link View}s using
* a Mac OS X application interface.
* <p>
* This user interface created by this application follows the guidelines given
* in the
* <a href="http://developer.apple.com/mac/library/documentation/UserExperience/Conceptual/AppleHIGuidelines/"
* >Apple Human Interface Guidelines</a>.
* <p>
* An application of this type can open multiple {@link View}s. Each view is
* shown in a separate {@code JFrame}.
* <p>
* Conceptually all views share a global 'screen menu bar'. In Swing this is
* implemented as multiple JMenuBar instances. There is one JMenuBar for
* each opened JFrame, and a special JMenuBar which is shown when all views of
* the application are closed.
* <p>
* The application also provides floating toolbars and palette windows for the
* views.
* <p>
* In order for the screen menu bar and the floating palettes to function
* properly, it is essential that all code which opens JFrame's, JDialog's or
* JWindow's calls addWindow/Palette and removeWindow/Palette on the application
* object.
* <p>
* The life cycle of the application is tied to the screen menu bar. Choosing
* the quit action in the screen menu bar quits the application.
* <p>
* The screen menu bar has the following standard menus:
* <pre>
* "Application-Name" File Edit Window</pre>
*
* The first menu, is the <b>application menu</b>. It has the following standard
* menu items:
* <pre>
* About "Application-Name" ({@link AboutAction#ID})
* -
* Preferences... ({@link AbstractPreferencesAction#ID})
* -
* Services
* -
* Hide "Application-Name"
* Hide Others
* Show All
* -
* Quit "Application-Name" ({@link ExitAction#ID})
* </pre>
*
* The <b>file menu</b> has the following standard menu items:
* <pre>
* Clear ({@link ClearFileAction#ID}})
* New ({@link NewFileAction#ID}})
* New Window ({@link NewWindowAction#ID}})
* Load... ({@link LoadFileAction#ID}})
* Open... ({@link OpenFileAction#ID}})
* Load Directory... ({@link LoadDirectoryAction#ID}})
* Open Directory... ({@link OpenDirectoryAction#ID}})
* Load Recent > "Filename" ({@link org.jhotdraw.app.action.file.LoadRecentFileAction#ID})
* Open Recent > "Filename" ({@link org.jhotdraw.app.action.file.OpenRecentFileAction#ID})
* -
* Close ({@link CloseFileAction#ID})
* Save ({@link SaveFileAction#ID})
* Save As... ({@link SaveFileAsAction#ID})
* Export... ({@link ExportFileAction#ID})
* Print... ({@link PrintFileAction#ID})
* </pre>
*
* The <b>edit menu</b> has the following standard menu items:
* <pre>
* Undo ({@link UndoAction#ID}})
* Redo ({@link RedoAction#ID}})
* -
* Cut ({@link CutAction#ID}})
* Copy ({@link CopyAction#ID}})
* Paste ({@link PasteAction#ID}})
* Duplicate ({@link DuplicateAction#ID}})
* Delete... ({@link DeleteAction#ID}})
* -
* Select All ({@link SelectAllAction#ID}})
* Clear Selection ({@link ClearSelectionAction#ID}})
* -
* Find ({@link AbstractFindAction#ID}})
* </pre>
*
* The <b>window menu</b> has the following standard menu items:
* <pre>
* Minimize ({@link MinimizeWindowAction#ID})
* Zoom ({@link MaximizeWindowAction#ID})
* -
* "Filename" ({@link FocusWindowAction})
* -
* "Palette" ({@link TogglePaletteAction})
* </pre>
*
* The menus provided by the {@code ApplicationModel} are inserted between
* the file menu and the window menu. In case the application model supplies
* a menu with the title "Help", it is inserted after the window menu.
*
* @author Werner Randelshofer
* @version $Id$
*/
public class OSXApplication extends AbstractApplication {
private static final long serialVersionUID = 1L;
private OSXPaletteHandler paletteHandler;
private Preferences prefs;
private LinkedList<Action> paletteActions;
/** The "invisible" frame is used to hold the frameless menu bar on Mac OS X.
*/
private JFrame invisibleFrame;
/** Creates a new instance. */
public OSXApplication() {
}
@Override
public void init() {
super.init();
ResourceBundleUtil.putPropertyNameModifier("os", "mac", "default");
prefs = PreferencesUtil.userNodeForPackage((getModel() == null) ? getClass() : getModel().getClass());
initLookAndFeel();
paletteHandler = new OSXPaletteHandler(this);
initLabels();
paletteActions = new LinkedList<>();
setActionMap(createModelActionMap(model));
initPalettes(paletteActions);
initScreenMenuBar();
}
@Override
public void launch(String[] args) {
System.setProperty("apple.awt.graphics.UseQuartz", "false");
super.launch(args);
}
@Override
public void configure(String[] args) {
System.setProperty("apple.laf.useScreenMenuBar", "true");
System.setProperty("com.apple.macos.useScreenMenuBar", "true");
}
protected void initLookAndFeel() {
try {
String lafName=(String)Methods.invokeStatic("ch.randelshofer.quaqua.QuaquaManager","getLookAndFeelClassName");
UIManager.setLookAndFeel(lafName);
} catch (Exception e) {
e.printStackTrace();
}
if (UIManager.getString("OptionPane.css") == null) {
UIManager.put("OptionPane.css", "<head>"
+ "<style type=\"text/css\">"
+ "b { font: 13pt \"Dialog\" }"
+ "p { font: 11pt \"Dialog\"; margin-top: 8px }"
+ "</style>"
+ "</head>");
}
}
@Override
public void dispose(View p) {
FocusWindowAction a = (FocusWindowAction) getAction(p, FocusWindowAction.ID);
if (a != null) {
a.dispose();
}
super.dispose(p);
}
@Override
public void addPalette(Window palette) {
paletteHandler.addPalette(palette);
}
@Override
public void removePalette(Window palette) {
paletteHandler.removePalette(palette);
}
@Override
public void addWindow(Window window, final View view) {
if (window instanceof JFrame) {
((JFrame) window).setJMenuBar(createMenuBar(view));
} else if (window instanceof JDialog) {
// ((JDialog) window).setJMenuBar(createMenuBar(null));
}
paletteHandler.add(window, view);
}
@Override
public void removeWindow(Window window) {
if (window instanceof JFrame) {
// Unlink all menu items from action objects
JMenuBar mb = ((JFrame) window).getJMenuBar();
Stack<JMenu> s = new Stack<>();
for (int i = 0, n = mb.getMenuCount(); i < n; ++i) {
if (mb.getMenu(i) != null) {
s.push(mb.getMenu(i));
}
}
while (!s.isEmpty()) {
JPopupMenu m = s.pop().getPopupMenu();
for (int i = 0, n = m.getComponentCount(); i < n; ++i) {
if (m.getComponent(i) instanceof JMenu) {
s.push((JMenu) m.getComponent(i));
} else if (m.getComponent(i) instanceof AbstractButton) {
((AbstractButton) m.getComponent(i)).setAction(null);
}
}
}
// We explicitly set the JMenuBar to null to facilitate garbage
// collection
((JFrame) window).setJMenuBar(null);
}
paletteHandler.remove(window);
}
@Override
public void show(View view) {
if (!view.isShowing()) {
view.setShowing(true);
JFrame f = new JFrame();
f.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
f.setSize(new Dimension(600, 400));
updateViewTitle(view, f);
PreferencesUtil.installFramePrefsHandler(prefs, "view", f);
Point loc = f.getLocation();
boolean moved;
do {
moved = false;
for (View aView : views()) {
if (aView != view && aView.isShowing()
&& SwingUtilities.getWindowAncestor(aView.getComponent()).
getLocation().equals(loc)) {
loc.x += 22;
loc.y += 22;
moved = true;
break;
}
}
} while (moved);
f.setLocation(loc);
new FrameHandler(f, view);
addWindow(f, view);
f.getContentPane().add(view.getComponent());
f.setVisible(true);
view.start();
}
}
/**
* Updates the title of a view and displays it in the given frame.
*
* @param v The view.
* @param f The frame.
*/
protected void updateViewTitle(View v, JFrame f) {
String title;
URI uri = v.getURI();
if (uri == null) {
title = labels.getString("unnamedFile");
} else {
title = URIUtil.getName(uri);
}
v.setTitle(labels.getFormatted("frame.title", title, getName(), v.getMultipleOpenId()));
f.setTitle(v.getTitle());
// Adds a proxy icon for the file to the title bar
// See http://developer.apple.com/technotes/tn2007/tn2196.html#WINDOW_DOCUMENTFILE
if (uri != null && uri.getScheme() != null && "file".equals(uri.getScheme())) {
f.getRootPane().putClientProperty("Window.documentFile", new File(uri));
} else {
f.getRootPane().putClientProperty("Window.documentFile", null);
}
}
@Override
public void hide(View p) {
if (p.isShowing()) {
JFrame f = (JFrame) SwingUtilities.getWindowAncestor(p.getComponent());
if (getActiveView() == p) {
setActiveView(null);
}
f.setVisible(false);
removeWindow(f);
f.remove(p.getComponent());
f.dispose();
}
}
/**
* Creates a menu bar.
*/
protected JMenuBar createMenuBar(@Nullable View v) {
JMenuBar mb = new JMenuBar();
// Get menus from application model
JMenu fileMenu = null;
JMenu editMenu = null;
JMenu helpMenu = null;
JMenu viewMenu = null;
JMenu windowMenu = null;
String fileMenuText = labels.getString("file.text");
String editMenuText = labels.getString("edit.text");
String viewMenuText = labels.getString("view.text");
String windowMenuText = labels.getString("window.text");
String helpMenuText = labels.getString("help.text");
LinkedList<JMenu> ll = new LinkedList<>();
getModel().getMenuBuilder().addOtherMenus(ll, this, v);
for (JMenu mm : ll) {
String text = mm.getText();
if (text == null) {
mm.setText("-null-");
} else if (text.equals(fileMenuText)) {
fileMenu = mm;
continue;
} else if (text.equals(editMenuText)) {
editMenu = mm;
continue;
} else if (text.equals(viewMenuText)) {
viewMenu = mm;
continue;
} else if (text.equals(windowMenuText)) {
windowMenu = mm;
continue;
} else if (text.equals(helpMenuText)) {
helpMenu = mm;
continue;
}
mb.add(mm);
}
// Create missing standard menus
if (fileMenu == null) {
fileMenu = createFileMenu(v);
}
if (editMenu == null) {
editMenu = createEditMenu(v);
}
if (viewMenu == null) {
viewMenu = createViewMenu(v);
}
if (windowMenu == null) {
windowMenu = createWindowMenu(v);
}
if (helpMenu == null) {
helpMenu = createHelpMenu(v);
}
// Insert standard menus into menu bar
if (fileMenu != null) {
mb.add(fileMenu, 0);
}
if (editMenu != null) {
mb.add(editMenu, Math.min(1, mb.getComponentCount()));
}
if (viewMenu != null) {
mb.add(viewMenu, Math.min(2, mb.getComponentCount()));
}
if (windowMenu != null) {
mb.add(windowMenu);
}
if (helpMenu != null) {
mb.add(helpMenu);
}
return mb;
}
@Override
@Nullable
public JMenu createViewMenu(final View view) {
JMenu m = new JMenu();
labels.configureMenu(m, "view");
MenuBuilder mb = model.getMenuBuilder();
mb.addOtherViewItems(m, this, view);
return (m.getItemCount() > 0) ? m : null;
}
@Override
public JMenu createWindowMenu(View view) {
JMenu m;
JMenuItem mi;
m = new JMenu();
JMenu windowMenu = m;
labels.configureMenu(m, "window");
m.addSeparator();
MenuBuilder mb = model.getMenuBuilder();
mb.addOtherWindowItems(m, this, view);
new WindowMenuHandler(windowMenu, view);
return (m.getItemCount() == 0) ? null : m;
}
@Override
@Nullable
public JMenu createFileMenu(View view) {
JMenu m;
m = new JMenu();
labels.configureMenu(m, "file");
MenuBuilder mb = model.getMenuBuilder();
mb.addClearFileItems(m, this, view);
mb.addNewFileItems(m, this, view);
mb.addNewWindowItems(m, this, view);
mb.addLoadFileItems(m, this, view);
mb.addOpenFileItems(m, this, view);
if (getAction(view, LoadFileAction.ID) != null ||//
getAction(view, OpenFileAction.ID) != null ||//
getAction(view, LoadDirectoryAction.ID) != null ||//
getAction(view, OpenDirectoryAction.ID) != null) {
m.add(createOpenRecentFileMenu(view));
}
maybeAddSeparator(m);
mb.addCloseFileItems(m, this, view);
mb.addSaveFileItems(m, this, view);
mb.addExportFileItems(m, this, view);
mb.addPrintFileItems(m, this, view);
mb.addOtherFileItems(m, this, view);
return (m.getItemCount() == 0) ? null : m;
}
@Override
@Nullable
public JMenu createEditMenu(View view) {
JMenu m;
JMenuItem mi;
Action a;
m = new JMenu();
labels.configureMenu(m, "edit");
MenuBuilder mb = model.getMenuBuilder();
mb.addUndoItems(m, this, view);
maybeAddSeparator(m);
mb.addClipboardItems(m, this, view);
maybeAddSeparator(m);
mb.addSelectionItems(m, this, view);
maybeAddSeparator(m);
mb.addFindItems(m, this, view);
maybeAddSeparator(m);
mb.addOtherEditItems(m, this, view);
removeTrailingSeparators(m);
return (m.getItemCount() == 0) ? null : m;
}
@Override
public JMenu createHelpMenu(View view) {
JMenu m = new JMenu();
labels.configureMenu(m, "help");
MenuBuilder mb = model.getMenuBuilder();
mb.addHelpItems(m, this, view);
return (m.getItemCount() == 0) ? null : m;
}
protected void initScreenMenuBar() {
setScreenMenuBar(createMenuBar(null));
paletteHandler.add((JFrame) getComponent(), null);
Action a;
if (null != (a = getAction(null, OpenApplicationAction.ID))) {
OSXAdapter.setOpenApplicationHandler(a);
}
if (null != (a = getAction(null, ReOpenApplicationAction.ID))) {
OSXAdapter.setReOpenApplicationHandler(a);
}
if (null != (a = getAction(null, OpenApplicationFileAction.ID))) {
OSXAdapter.setOpenFileHandler(a);
}
if (null != (a = getAction(null, PrintApplicationFileAction.ID))) {
OSXAdapter.setPrintFileHandler(a);
}
if (null != (a = getAction(null, AboutAction.ID))) {
OSXAdapter.setAboutHandler(a);
}
if (null != (a = getAction(null, AbstractPreferencesAction.ID))) {
OSXAdapter.setPreferencesHandler(a);
}
if (null != (a = getAction(null, ExitAction.ID))) {
OSXAdapter.setQuitHandler(a);
}
}
protected void initPalettes(final LinkedList<Action> paletteActions) {
SwingUtilities.invokeLater(new Worker<LinkedList<JFrame>>() {
@Override
public LinkedList<JFrame> construct() {
LinkedList<JFrame> palettes = new LinkedList<>();
LinkedList<JToolBar> toolBars = new LinkedList<>(getModel().createToolBars(OSXApplication.this, null));
int i = 0;
int x = 0;
for (JToolBar tb : toolBars) {
i++;
tb.setFloatable(false);
tb.setOrientation(JToolBar.VERTICAL);
tb.setFocusable(false);
JFrame d = new JFrame();
// Note: Client properties must be set before heavy-weight
// peers are created
d.getRootPane().putClientProperty("Window.style", "small");
d.getRootPane().putClientProperty("Quaqua.RootPane.isVertical", Boolean.FALSE);
d.getRootPane().putClientProperty("Quaqua.RootPane.isPalette", Boolean.TRUE);
d.setFocusable(false);
d.setResizable(false);
d.getContentPane().setLayout(new BorderLayout());
d.getContentPane().add(tb, BorderLayout.CENTER);
d.setAlwaysOnTop(true);
d.setUndecorated(true);
d.getRootPane().setWindowDecorationStyle(JRootPane.FRAME);
d.getRootPane().setFont(
new Font("Lucida Grande", Font.PLAIN, 11));
d.setJMenuBar(createMenuBar(null));
d.pack();
d.setFocusableWindowState(false);
PreferencesUtil.installPalettePrefsHandler(prefs, "toolbar." + i, d, x);
x += d.getWidth();
TogglePaletteAction tpa = new TogglePaletteAction(OSXApplication.this, d, tb.getName());
palettes.add(d);
if (prefs.getBoolean("toolbar." + i + ".visible", true)) {
addPalette(d);
tpa.putValue(ActionUtil.SELECTED_KEY, true);
}
paletteActions.add(tpa);
}
return palettes;
}
@Override
protected void done(LinkedList<JFrame> result) {
@SuppressWarnings("unchecked")
LinkedList<JFrame> palettes = result;
if (palettes != null) {
/*for (JFrame p : palettes) {
if (prefs.getBoolean("toolbar.", true))
addPalette(p);
}*/
firePropertyChange("paletteCount", 0, palettes.size());
}
}
});
}
@Override
public boolean isSharingToolsAmongViews() {
return true;
}
/** Returns the Frame which holds the frameless JMenuBar.
*/
@Override
public Component getComponent() {
if (invisibleFrame == null) {
invisibleFrame = new JFrame();
invisibleFrame.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
invisibleFrame.setUndecorated(true);
// Move it way off screen
invisibleFrame.setLocation(10000, 10000);
// make the frame transparent and shadowless
// see https://developer.apple.com/mac/library/technotes/tn2007/tn2196.html
invisibleFrame.getRootPane().putClientProperty("Window.alpha", 0f);
invisibleFrame.getRootPane().putClientProperty("Window.shadow", false);
// make it visible, so the menu bar will show
invisibleFrame.setVisible(true);
}
return invisibleFrame;
}
protected void setScreenMenuBar(JMenuBar mb) {
((JFrame) getComponent()).setJMenuBar(mb);
// pack it (without calling pack, the screen menu bar won't work for some reason)
invisibleFrame.pack();
}
protected ActionMap createModelActionMap(ApplicationModel mo) {
ActionMap rootMap = new ActionMap();
rootMap.put(AboutAction.ID, new AboutAction(this));
rootMap.put(ExitAction.ID, new ExitAction(this));
rootMap.put(OpenApplicationAction.ID, new OpenApplicationAction(this));
rootMap.put(OpenApplicationFileAction.ID, new OpenApplicationFileAction(this));
rootMap.put(ReOpenApplicationAction.ID, new ReOpenApplicationAction(this));
rootMap.put(ClearRecentFilesMenuAction.ID, new ClearRecentFilesMenuAction(this));
rootMap.put(MaximizeWindowAction.ID, new MaximizeWindowAction(this, null));
rootMap.put(MinimizeWindowAction.ID, new MinimizeWindowAction(this, null));
ActionMap moMap = mo.createActionMap(this, null);
moMap.setParent(rootMap);
return moMap;
}
@Override
protected ActionMap createViewActionMap(View v) {
ActionMap intermediateMap = new ActionMap();
intermediateMap.put(FocusWindowAction.ID, new FocusWindowAction(v));
intermediateMap.put(MaximizeWindowAction.ID, new MaximizeWindowAction(this, v));
intermediateMap.put(MinimizeWindowAction.ID, new MinimizeWindowAction(this, v));
ActionMap vMap = model.createActionMap(this, v);
vMap.setParent(intermediateMap);
intermediateMap.setParent(getActionMap(null));
return vMap;
}
/** Updates the menu items in the "Window" menu. */
private class WindowMenuHandler implements PropertyChangeListener, Disposable {
private JMenu windowMenu;
@Nullable
private View view;
public WindowMenuHandler(JMenu windowMenu, @Nullable View view) {
this.windowMenu = windowMenu;
this.view = view;
OSXApplication.this.addPropertyChangeListener(this);
if (view != null) {
view.addDisposable(this);
}
updateWindowMenu();
}
@Override
public void propertyChange(PropertyChangeEvent evt) {
String name = evt.getPropertyName();
if (name == VIEW_COUNT_PROPERTY || name == "paletteCount") {
updateWindowMenu();
}
}
protected void updateWindowMenu() {
JMenu m = windowMenu;
JMenuItem mi;
// FIXME - We leak memory here!!
m.removeAll();
mi = m.add(getAction(view, MinimizeWindowAction.ID));
mi.setIcon(null);
mi = m.add(getAction(view, MaximizeWindowAction.ID));
mi.setIcon(null);
m.addSeparator();
for (View pr : views()) {
if (getAction(pr, FocusWindowAction.ID) != null) {
mi = m.add(getAction(pr, FocusWindowAction.ID));
}
}
if (paletteActions.size() > 0) {
m.addSeparator();
for (Action a : paletteActions) {
JCheckBoxMenuItem cbmi = new JCheckBoxMenuItem(a);
ActionUtil.configureJCheckBoxMenuItem(cbmi, a);
cbmi.setIcon(null);
m.add(cbmi);
}
}
MenuBuilder mb = model.getMenuBuilder();
mb.addOtherWindowItems(m, OSXApplication.this, view);
}
@Override
public void dispose() {
windowMenu.removeAll();
removePropertyChangeListener(this);
view = null;
}
}
/** Updates the modifedState of the frame. */
private class FrameHandler extends WindowAdapter implements PropertyChangeListener, Disposable {
private JFrame frame;
private View view;
public FrameHandler(JFrame frame, View view) {
this.frame = frame;
this.view = view;
view.addPropertyChangeListener(this);
frame.addWindowListener(this);
view.addDisposable(this);
}
@Override
public void propertyChange(PropertyChangeEvent evt) {
String name = evt.getPropertyName();
if (name.equals(View.HAS_UNSAVED_CHANGES_PROPERTY)) {
frame.getRootPane().putClientProperty("windowModified", view.hasUnsavedChanges());
} else if (name.equals(View.URI_PROPERTY) || name.equals(View.TITLE_PROPERTY)) {
updateViewTitle(view, frame);
}
}
@Override
public void windowClosing(final WindowEvent evt) {
getAction(view, CloseFileAction.ID).actionPerformed(
new ActionEvent(evt.getSource(), ActionEvent.ACTION_PERFORMED,
"windowClosing"));
}
@Override
public void windowClosed(final WindowEvent evt) {
}
@Override
public void windowIconified(WindowEvent e) {
if (view == getActiveView()) {
setActiveView(null);
}
view.stop();
}
@Override
public void windowDeiconified(WindowEvent e) {
view.start();
}
@Override
public void dispose() {
frame.removeWindowListener(this);
view.removePropertyChangeListener(this);
}
@Override
public void windowGainedFocus(WindowEvent e) {
setActiveView(view);
}
}
private static class QuitHandler {
/** This method is invoked, when the user has selected the Quit menu item.
*
* @return Returns true if the application has no unsaved changes and
* can be closed.
*/
public boolean handleQuit() {
return false;
}
}
}