/*
* Copyright 2003-2010 Tufts University Licensed under the
* Educational Community License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License. You may
* obtain a copy of the License at
*
* http://www.osedu.org/licenses/ECL-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an "AS IS"
* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/
package tufts.vue.gui;
import tufts.vue.VUE;
import tufts.vue.MapViewer;
import tufts.vue.ActiveListener;
import tufts.vue.ActiveEvent;
import tufts.vue.VueResources;
import tufts.vue.DEBUG;
import java.awt.Dimension;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.event.*;
import javax.swing.SwingUtilities;
import edu.tufts.vue.preferences.implementations.WindowPropertiesPreference;
/**
* The top-level VUE application Frame.
*
* Set's the icon-image for the vue application and set's the window title.
*
* @version $Revision: 1.27 $ / $Date: 2010-02-03 19:15:46 $ / $Author: mike $
*/
public class VueFrame extends javax.swing.JFrame
implements ActiveListener<MapViewer>, WindowListener, WindowStateListener, WindowFocusListener
{
private static final org.apache.log4j.Logger Log = org.apache.log4j.Logger.getLogger(VueFrame.class);
private static int sNameIndex = 0;
private WindowPropertiesPreference wpp = null;
public VueFrame() {
this(VueResources.getString("application.title"));
}
public VueFrame(String title) {
super(title);
wpp = WindowPropertiesPreference.create(
"windows",
"windowVueMainFrame",
//"window" + title.replace(" ", ""),
VueResources.getString("preferences.savewindowstates.title"),
VueResources.getString("prefernces.savewindowstates.description"),
true);
this.setDefaultCloseOperation(this.DO_NOTHING_ON_CLOSE);
setName("VueFrame" + sNameIndex++);
GUI.setRootPaneNames(this, getName());
if (GUI.isMacAqua()) {
// note that this actually has no effect on MacOSX
setIconImage(VueResources.getImage("vueIcon128"));
} else {
setIconImage(VueResources.getImage("vueIcon32"));
}
// if (GUI.ControlMaxWindow)
// setMaximizedBounds(GUI.getMaximumWindowBounds());
// we need this to make sure kdb input
//setJMenuBar(new VueMenuBar());
// JIDE ENABLE getDockableBarManager().getMainContainer().setLayout(new BorderLayout());
//addMouseWheelListener(this); this causing stack overflows in JVM 1.4 & 1.5, and only works for unclaimed areas
// (e.g., not mapviewer, even if it hasn't registered a wheel listener)
addComponentListener(new java.awt.event.ComponentAdapter() {
public void componentMoved(ComponentEvent e) {
//out("MOVED " + e);
GUI.reloadGraphicsInfo();
}
});
addWindowListener(this);
addWindowStateListener(this);
addWindowFocusListener(this);
VUE.addActiveListener(MapViewer.class, this);
}
public void windowOpened(WindowEvent e) {
Log.debug("opened; " + e);
}
public void windowClosing(WindowEvent e) {
Log.warn(e);
tufts.vue.action.ExitAction.exitVue();
// If we get here, it means the exit was aborted by the user (something
// wasn't saved & they decided to cancel or there was an error during
// the save)
//frame.show(); (doesn't work) How to cancel this windowClose? According
// to WindowEvent.java & WindowAdapter.java, canceling this
// windowClosing is supposed to be possible, but they don't mention
// how. Anyway, we've overriden setVisible on VueFrame to make it
// impossible to hide it, and that works, so this event just becomes the
// they've pressed on the close button event.
}
public void windowClosed(WindowEvent e) {
// I've never see us even get this event...
Log.fatal("Too late: window disposed: exiting. " + e);
System.exit(-1);
}
public void windowStateChanged(WindowEvent e) {
Log.debug("windowStateChanged: " + e);
if (VUE.getActiveViewer() != null)
VUE.getActiveViewer().setFastPaint("windowStateChanged");
}
public void windowActivated(WindowEvent e) {
if (DEBUG.FOCUS) Log.debug("activated; " + e);
if (VUE.getActiveViewer() != null)
VUE.getActiveViewer().setFastPaint("windowActivated");
DockWindow.ShowPreviouslyHiddenWindows();
if (LastOpenedResource != null) {
try {
if (DEBUG.Enabled) Log.debug("resource check: " + LastOpenedResource);
tufts.vue.VUE.checkForAndHandleResourceUpdate(LastOpenedResource);
// TODO: skip clearing to null to handle repeated editing
// bad idea of now until this is threaded, as if happened to
// be local network resource that went offline, we could
// hang -- so at least this would only happen the first time.
// Once this is in a thread, the check could just run entirely
// at low priority in the background checking everything once
// VUE gains focus again.
LastOpenedResource = null;
} catch (Throwable t) {
Log.error("resource check: " + LastOpenedResource, t);
LastOpenedResource = null;
}
}
}
public void windowGainedFocus(WindowEvent e) {
if (DEBUG.FOCUS) Log.debug("focus-gained; " + e);
}
public void windowLostFocus(WindowEvent e) {
if (DEBUG.FOCUS) Log.debug("focus-lost; " + e);
MapViewer v = VUE.getActiveViewer();
if (v !=null)
{
v.clearTip();
}
}
public void windowDeactivated(WindowEvent e) {
if (DEBUG.FOCUS) Log.debug("deactivated; " + e);
MapViewer v = VUE.getActiveViewer();
if (v !=null)
{
v.clearTip();
}
// if (!tufts.Util.isUnixPlatform()) {
// // this causes VueFrame de-activate/activate loop on Ubuntu 8.04 / JVM 1.6
// // Works nicely on Leopard & XP, but may not be what we want: a user
// // might want to drag text into notes panel, which would be impossible
// // if we do this. Could make this a preference.
// DockWindow.DeactivateAllWindows();
// }
}
public void windowIconified(WindowEvent e) {
Log.debug("iconfied; " + e);
}
public void windowDeiconified(WindowEvent e) {
Log.debug("de-iconfied; " + e);
}
private static tufts.vue.Resource LastOpenedResource;
public static void setLastOpenedResource(tufts.vue.Resource r) {
LastOpenedResource = r;
}
public void activeChanged(ActiveEvent<MapViewer> e) {
setTitleFromViewer(e.active);
if (tufts.Util.isMacPlatform()) {
if (e.active != null && e.active.getMap() != null) {
final tufts.vue.LWMap map = e.active.getMap();
final javax.swing.JRootPane rootPane = getRootPane();
// If the below documentModified is TRUE, the icon in the title-bar will appear, but
// will be grayed out (as the file is considered to be in an indeterminate state).
// Note that putting null values for client properties removes the property completely.
// These client properties only have effect in Java 1.5 on Mac OS X Leopard:
rootPane.putClientProperty("Window.documentFile", map.getFile());
//if (tufts.Util.isMacLeopard()) {
if (DEBUG.Enabled) {
// will also need this update on ANY change to the map's status (from either edits or undo's)
// in order to keep this up to date.
// Technically, I'm guessing that the Mac guidelines would say this dirty bit should be
// set if ANY open document is modified (as it's feedback that says if you click on the
// dirty red close icon, you're going to get dialogs to confirm this). Anyway,
// for the moment it's more handy if it displays for the active viewer, and the user
// is going to be asked anyway.
rootPane.putClientProperty("Window.documentModified", Boolean.valueOf(map.isModified()));
}
}
}
}
/*
final static int TitleChangeMask =
MapViewerEvent.DISPLAYED |
MapViewerEvent.FOCUSED;
//MapViewerEvent.ZOOM; // title includes zoom
public void mapViewerEventRaised(MapViewerEvent e) {
if ((e.getID() & TitleChangeMask) != 0)
setTitleFromViewer(e.getMapViewer());
}
*/
public boolean isPointFullyOnScreen(Point p, Dimension size)
{
Dimension screenSize = java.awt.Toolkit.getDefaultToolkit().getScreenSize();
int rightCorner = (int)p.getX() + (int)size.getWidth();
int bottomCorner = (int)p.getY() + (int)size.getHeight();
if ((rightCorner <= screenSize.getWidth()) && (bottomCorner <= screenSize.getHeight()))
return true;
else
return false;
}
public void saveWindowProperties()
{
Dimension size = getSize();
Point p;
p = getLocationOnScreen();
wpp.updateWindowProperties(true, (int)size.getWidth(), (int)size.getHeight(), (int)p.getX(), (int)p.getY(),false);
}
public void positionWindowFromProperties()
{
if (wpp.isEnabled())
{
Point p = wpp.getWindowLocationOnScreen();
Dimension size = wpp.getWindowSize();
if (((int)p.getX()) > -1)
setBounds((int)p.getX(),(int)p.getY(),(int)size.getWidth(),(int)size.getHeight());
else
setSize(size);
setVisible(wpp.isWindowVisible());
}
}
public WindowPropertiesPreference getWindowProperties()
{
return wpp;
}
public void setMaximizedBounds(Rectangle r) {
if (DEBUG.INIT) out("SETMAX " + r);
super.setMaximizedBounds(r);
}
public void setVisible(boolean visible) {
if (DEBUG.INIT) out("SET-VISIBLE " + visible);
super.setVisible(visible);
}
public void XsetExtendedState(int state) {
out("SET-STATE: " + state + (state == MAXIMIZED_BOTH ? " MAX" : " other"));
super.setExtendedState(state);
}
public void XsetLocation(int x, int y) {
out("setLocation: " + x + "," + y);
super.setLocation(x, y);
}
//public void mouseWheelMoved(MouseWheelEvent e) { System.err.println("VUE MSW"); }
protected void X_processEvent(java.awt.AWTEvent e) {
// if (e instanceof MouseWheelEvent) System.err.println("VUE MSM PE"); only works w/listener, which has problem as above
// try a generic AWT event queue listener?
if (e instanceof java.awt.event.MouseEvent) {
super.processEvent(e);
return;
}
if (DEBUG.FOCUS) System.out.println("VueFrame: processEvent " + e);
// todo: if frame is getting key events, handle them
// and/or pass off to MapViewer (e.g., tool switch events!)
// or: put tool's w/action events in vue menu
if (e instanceof WindowEvent) {
switch (e.getID()) {
case WindowEvent.WINDOW_CLOSING:
case WindowEvent.WINDOW_CLOSED:
case WindowEvent.WINDOW_ICONIFIED:
//case WindowEvent.WINDOW_DEACTIVATED:
super.processEvent(e);
return;
/*
case WindowEvent.WINDOW_ACTIVATED:
tufts.macosx.Screen.dumpWindows();
case WindowEvent.WINDOW_OPENED:
case WindowEvent.WINDOW_DEICONIFIED:
case WindowEvent.WINDOW_GAINED_FOCUS:
if (VUE.getRootWindow() != VUE.getMainWindow())
VUE.getRootWindow().toFront();
*/
}
}
// why do we do this? Must have to do with full-screen or something...
if (VUE.getRootWindow() != VUE.getMainWindow()) {
Log.warn("VueFrame: processEvent: root != main: forcing root visible & front");
VUE.getRootWindow().setVisible(true);
VUE.getRootWindow().toFront();
}
super.processEvent(e);
}
public void addComp(java.awt.Component c, String constraints) {
getContentPane().add(c, constraints);
// JIDE ENABLE getDockableBarManager().getMainContainer().add(c, constraints);
}
/*
public void show() {
out("VueFrame: show");
super.show();
pannerTool.toFront();
}
public void toFront()
{
//if (DEBUG.FOCUS)
out("VueFrame: toFront");
super.toFront();
}
*/
/** never let the frame be hidden -- always ignored */
public void X_setVisible(boolean tv) {
//System.out.println("VueFrame setVisible " + tv + " OVERRIDE");
// The frame should never be "hidden" -- iconification
// doesn't trigger that (nor Mac os "hide") -- so if we're
// here the OS window manager is attempting to hide us
// (the 'x' button on the window frame).
//super.setVisible(true);
super.setVisible(tv);
}
private void setTitleFromViewer(MapViewer viewer) {
String title = VUE.getName();
final tufts.vue.LWMap map = viewer == null ? null : viewer.getMap();
if (map != null) {
if (DEBUG.Enabled) title += ("[" + map.getSaveFileModelVersion() + "]");
title += ": " + map.getLabel();
}
//if (viewer.getMap().isCurrentlyFiltered())
// will need to listen to map for filter change state or this gets out of date
// title += " (Filtered)";
setTitle(title);
//setTitle("VUE: " + getViewerTitle(viewer));
}
private String getViewerTitle(MapViewer viewer) {
String title = viewer.getMap().getLabel();
int displayZoom = (int) (viewer.getZoomFactor() * 10000.0);
// Present the zoom factor as a percentange
// truncated down to 2 digits
title += " (";
if ((displayZoom / 100) * 100 == displayZoom)
title += (displayZoom / 100) + "%";
else
title += (((float) displayZoom) / 100f) + "%";
title += ")";
return title;
}
public void setTitle(String title)
{
if (title.endsWith(".vpk") || title.endsWith(".vue"))
super.setTitle(title);
}
private void out(String s) {
System.out.println("VueFrame: " + s);
}
public String toString() {
return "VueFrame[" + getTitle() + "]";
}
}