/*
* 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;
import java.applet.AppletContext;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Frame;
import java.awt.Graphics;
import java.awt.GraphicsConfiguration;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.GridLayout;
import java.awt.Insets;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.Toolkit;
import java.awt.Window;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.FocusAdapter;
import java.awt.event.FocusEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.File;
import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import javax.swing.AbstractAction;
import javax.swing.AbstractButton;
import javax.swing.Action;
import javax.swing.BorderFactory;
import javax.swing.JApplet;
import javax.swing.JButton;
import javax.swing.JCheckBoxMenuItem;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JMenu;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.JScrollPane;
import javax.swing.JSlider;
import javax.swing.JSplitPane;
import javax.swing.JTabbedPane;
import javax.swing.JTextArea;
import javax.swing.JTextField;
import javax.swing.JWindow;
import javax.swing.KeyStroke;
import javax.swing.SwingConstants;
import javax.swing.SwingUtilities;
import javax.swing.ToolTipManager;
import javax.swing.border.Border;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.text.AttributeSet;
import javax.swing.text.BadLocationException;
import javax.swing.text.PlainDocument;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathExpression;
import javax.xml.xpath.XPathFactory;
import org.apache.log4j.ConsoleAppender;
import org.apache.log4j.Level;
import org.apache.log4j.Logger;
import org.apache.log4j.PatternLayout;
import org.apache.log4j.WriterAppender;
import org.xml.sax.InputSource;
import tufts.Util;
import tufts.vue.NodeTool.NodeModeTool;
import tufts.vue.action.AboutAction;
import tufts.vue.action.ExitAction;
import tufts.vue.action.OpenAction;
import tufts.vue.action.SaveAction;
import tufts.vue.gui.DockWindow;
import tufts.vue.gui.FullScreen;
import tufts.vue.gui.GUI;
import tufts.vue.gui.VueFrame;
import tufts.vue.gui.VueMenuBar;
import tufts.vue.ui.InspectorPane;
import edu.tufts.vue.compare.ui.MergeMapsControlPanel;
import edu.tufts.vue.dsm.impl.VueDataSourceManager;
import edu.tufts.vue.preferences.implementations.MetadataSchemaPreference;
import edu.tufts.vue.preferences.implementations.ShowAgainDialog;
import edu.tufts.vue.preferences.implementations.WindowPropertiesPreference;
import javax.swing.plaf.ProgressBarUI;
/**
* Vue application class.
* Create an application frame and layout all the components
* we want to see there (including menus, toolbars, etc).
*
* @version $Revision: 1.714 $ / $Date: 2010-02-03 19:17:40 $ / $Author: mike $
*/
public class VUE
implements VueConstants
{
public static final boolean VUE3 = true;
public static boolean BLOCKING_OSID_LOAD = edu.tufts.vue.dsm.impl.VueDataSourceManager.BLOCKING_OSID_LOAD;
/** This is the root logger for all classes named tufts.* */
private static final Logger TuftsLog = Logger.getLogger("tufts");
/** This is the root logger for all classes named edu.tufts.* */
private static final Logger EduTuftsLog = Logger.getLogger("edu.tufts");
private static final Logger Log = Logger.getLogger(VUE.class);
private static AppletContext sAppletContext = null;
/** The currently active selection.
* elements in ModelSelection should always be from the ActiveModel */
static final LWSelection ModelSelection = new LWSelection();
private static VueFrame ApplicationFrame;
private static MapTabbedPane mMapTabsLeft;
private static MapTabbedPane mMapTabsRight;
public static JSplitPane mViewerSplit;
public static DockWindow[] acrossTop = null;
private static tufts.vue.ui.SlideViewer slideViewer;
// TODO: get rid of this
public static boolean dropIsLocal = false;
private static boolean isStartupUnderway = false;
private static java.util.List FilesToOpen = Collections.synchronizedList(new java.util.ArrayList());
private static InspectorPane inspectorPane = null;
private static FormatPanel formattingPanel;
private static FloatingZoomPanel floatingZoomPanel;
private static PathwayPanel pathwayPanel = null;
private static MapInspectorPanel mapInspectorPanel = null;
private static JButton returnToMapButton = null;
private static MergeMapsControlPanel mergeMapsControlPanel = null;
private static MetadataSearchMainGUI metadataSearchMainPanel = null;
private static InteractionTools interactionToolsPanel = null;
private static ContentPanel contentPanel = null;
private static JPopupMenu popup;
private static JPopupMenu editPopup;
static final SearchTextField mSearchTextField = new SearchTextField();
public static final int FIRST_TAB_STOP = 6;
//public static JCheckBoxMenuItem resetSettingsMenuItem;
public static ExpandSelectionControl depthSelectionControl = null;
public static JPanel searchPanel = new JPanel(new GridBagLayout());
/** this was for VUE applet -- currently unused */
public static void finalizeDocks()
{
//SwingUtilities.invokeLater(new Runnable() { public void run() {
//inspectorPane.removeAll();
// inspectorPane = null;
// formattingPanel.removeAll();
formattingPanel = null;
// floatingZoomPanel.removeAll();
floatingZoomPanel = null;
//pathwayPanel.removeAll();
//pathwayPanel = null;
// mapInspectorPanel.removeAll();
mapInspectorPanel = null;
metadataSearchMainPanel = null;
//returnToMapButton = null;
mergeMapsControlPanel = null;
// pathwayDock = null;
// pathwayDock = null;
formatDock = null;
slideDock = null;
pannerDock = null;;
MapInspector = null;
ObjectInspector = null;
outlineDock = null;
floatingZoomDock = null;
layersDock = null;
metadataSearchMainPanel = null;
mergeMapsControlPanel = null;
interactionToolsPanel = null;
contentPanel = null;
// EditorManager.destroy();
// VueToolbarController.destroyController();
// VUE.getActiveViewer().destroyContextMenus();
//
}
/** simplest form of threadsafe static lazy initializer: for CategoryModel */
private static final class HolderCM {
static final edu.tufts.vue.metadata.CategoryModel _CategoryModel = new edu.tufts.vue.metadata.CategoryModel();
}
/** simplest form of threadsafe static lazy initializer: for RDFIndex */
private static final class HolderRDFIndex {
static final edu.tufts.vue.rdf.RDFIndex _RDFIndex = edu.tufts.vue.rdf.RDFIndex.getDefaultIndex();
}
public static edu.tufts.vue.metadata.CategoryModel getCategoryModel() {
return HolderCM._CategoryModel;
}
// Can't find anywhere in code-base this is currently used -- was it ever? SMF 2012-06-25
public static edu.tufts.vue.rdf.RDFIndex getRDFIndex() {
return SKIP_RDF_INDEX ? null : HolderRDFIndex._RDFIndex;
}
public static JButton getReturnToMapButton()
{
return returnToMapButton;
}
/** active pathway handler -- will update active pathway-entry handler if needed */
private static final ActiveInstance<LWPathway>
ActivePathwayHandler = new ActiveInstance<LWPathway>(LWPathway.class) {
@Override
protected void onChange(ActiveEvent<LWPathway> e) {
// Only run the update if this isn't already an auxillary sync update from ActivePathwayEntryHandler:
if (e.source instanceof ActiveEvent && ((ActiveEvent)e.source).type == LWPathway.Entry.class)
return;
if (e.active != null)
ActivePathwayEntryHandler.setActive(e, e.active.getCurrentEntry());
else
ActivePathwayEntryHandler.setActive(e, null);
}
};
/**
* active LWComponent handler (the active single-selection, if there is one).
* Will guess at and update the active pathway entry if it can.
*/
private static final ActiveInstance<LWComponent>
ActiveComponentHandler = new ActiveInstance<LWComponent>(LWComponent.class) {
@Override
protected void onChange(ActiveEvent<LWComponent> e)
{
final LWComponent node = e.active;
if (node == null) {
// nothing to do
}
else if (node instanceof LWSlide && ((LWSlide)node).getEntry() != null) {
ActivePathwayEntryHandler.setActive(e, ((LWSlide)node).getEntry());
}
else if (node instanceof LWPortal && node.hasEntries()) {
final LWPathway knownUnique = node.getExclusiveVisiblePathway();
if (knownUnique != null)
ActivePathwayEntryHandler.setActive(e, knownUnique.getFirstEntry(node));
else if (node.inPathway(VUE.getActivePathway()))
ActivePathwayEntryHandler.setActive(e, VUE.getActivePathway().getFirstEntry(node));
}
// //-------------------------------------------------------
// // now set the active Layer
// //-------------------------------------------------------
// if (node != null && node.getLayer() != null)
// ActiveInstance.set(LWMap.Layer.class, this, node.getLayer());
//-------------------------------------------------------
// now set the active Resource
//-------------------------------------------------------
if (node == null) {
ActiveResourceHandler.setActive(e, null);
return;
} else {
ActiveResourceHandler.setActive(e, node.getResource());
}
// Note: the below problem also applies to the selection of raw images (raw
// images are nodes) that have been added to a pathway. This is
// particularly a problem there in that raw images on a pathway never have a
// slide, so there's never an option for picking as the start of a
// presentation via selection -- that will have to be handled specially in
// the presentation tool.
// SOLUTION: there should be an "add" ('+') button on each pathway line
// in the pathway table, analogous to the "move here" button on each
// layer line, instead of the one "add" button at top that applies
// only to the selected pathway -- then we'd never have this problem.
// However, for some cases, we should be able to work around this, yes?
// E.g., if the node is only on ONE pathway, this should be safe. Also,
// we could only apply this to the VISIBLE pathways it's on, yes?
// We'd still then have the problem of not being able to add to a hidden
// pathway, and then this would also be confusing -- sometimes it would
// appear to work, othertimes it wouldn't.
// Or, howabout it selects the pathway entry ONLY if it's on the
// currently selected pathway? Then it's guaranteed never to change
// the selected pathway, which fixes for now the add problem, and
// gives us at least some benefit.
LWPathway.Entry newActiveEntry = null;
final LWPathway activePathway = ActivePathwayHandler.getActive();
if (activePathway != null && node.inPathway(activePathway))
newActiveEntry = activePathway.getEntry(activePathway.firstIndexOf(node));
if (newActiveEntry != null)
ActivePathwayEntryHandler.setActive(e, newActiveEntry);
// // This code will auto-select the first pathway entry for a selected node.
// // There is a problem if we do this tho, in that changing the active
// // entry also changes the active pathway, and doing that means
// // that once a node is added to a given pathway, it makes it
// // impossible to add it to any other pathways, because as soon
// // as it's selected, it then makes active the pathway it's on!
// // (and adding a node to a pathway always adds it to the active pathway).
// LWPathway.Entry newActiveEntry = null;
// if (node.numEntries() == 1) {
// newActiveEntry = node.mEntries.get(0);
// } else {
// final LWPathway activePathway = ActivePathwayHandler.getActive();
// if (activePathway != null && node.inPathway(activePathway))
// newActiveEntry = activePathway.getEntry(activePathway.firstIndexOf(node));
// }
// if (newActiveEntry != null)
// ActivePathwayEntryHandler.setActive(e, newActiveEntry);
}
};
/**
* Global active Layer handler for the active layer within the active map.
*/
private static final ActiveInstance<LWMap.Layer>
ActiveLayerHandler = new ActiveInstance<LWMap.Layer>(LWMap.Layer.class) {
// @Override
// protected void onChange(ActiveEvent<LWMap.Layer> e)
// {
// ////if (ActiveComponentHandler.getActive() == null)
// if (getSelection().size() == 0 || getSelection().only() instanceof LWMap.Layer)
// {
// getSelection().setTo(e.active);
// //ActiveComponentHandler.setActive(this, e.active);
// }
// // keep the active layer in the map up to date
// e.active.getMap().setActiveLayer(e.active);
// }
};
/**
* Global active Resource handler.
*/
private static final ActiveInstance<Resource>
ActiveResourceHandler = new ActiveInstance<Resource>(Resource.class) {
@Override
protected void onChange(ActiveEvent<Resource> e)
{
if (VUE.inPresentMode()) {
// do NOT attempt to update anything if we're in the
// middle of a presentation (e.g., the current focal
// is a raw image, making it also the active resource,
// as the current focal in a presentation is also
// set as the single selection).
} else {
if (e.active != null)
checkForAndHandleResourceUpdate(e.active);
}
}
};
/**
* If and only if the given Resource has changed on disk: check all open maps for
* any LWImage's using it, and tell them to reload. Also, find any OTHER
* Resources that have changed on disk, and update their LWImage's.
*/
public static boolean checkForAndHandleResourceUpdate(Resource r)
{
if (r != null && r.isImage() && r.dataHasChanged()) {
VUE.activateWaitCursor();
try {
final Collection<LWComponent> everything = new ArrayList(64);
for (int i = 0; i < mMapTabsLeft.getTabCount(); i++) {
LWMap map = mMapTabsLeft.getMapAt(i);
if (map == null)
continue;
everything.addAll(map.getAllDescendents(LWMap.ChildKind.ANY));
}
handleResourceUpdate(r, everything, true);
} catch (Throwable t) {
Log.error("resource update: " + r, t);
} finally {
GUI.invokeAfterAWT(new Runnable() { public void run() {
VUE.clearWaitCursor();
}});
}
return true;
} else
return false;
}
private static void handleResourceUpdate(Resource r, Iterable<LWComponent> everything, boolean firstPass) {
r.flushCache();
// This will find all LWImage's anywhere in any open map that points to the
// same resource, and thus may (probably) need updatng.
for (LWComponent c : everything) {
if (c instanceof LWImage) {
final LWImage image = (LWImage) c;
if (r.equals(image.getResource())) {
if (r != image.getResource())
image.getResource().dataHasChanged(); // make sure all other instances are also current
Log.info("reloading in " + c.getMap().getLabel() + ": " + image.getUniqueComponentTypeLabel() + "; " + image.getLabel());
image.reloadImage();
} else if (firstPass) {
// Check them all as long as we're updating.
if (image.hasResource() && image.getResource().dataHasChanged()) {
try {
handleResourceUpdate(image.getResource(), everything, false);
} catch (Throwable t) {
Log.error("auto-discovery resource update " + image.getResource(), t);
}
}
}
}
}
}
/**
* The active pathway-entry handler -- will update the active pathway handler if needed.
* If there is an on-map node associated with the pathway entry, it will force a single-selection of the
* of that node (triggering an change to the active LWComponent), unless the LWSelection is
* is the middle of a notification of it's own.
*/
private static final ActiveInstance<LWPathway.Entry>
ActivePathwayEntryHandler = new ActiveInstance<LWPathway.Entry>(LWPathway.Entry.class) {
@Override
protected void onChange(ActiveEvent<LWPathway.Entry> e) {
// Only run the update if this isn't already an auxillary sync update from ActivePathwayHandler:
if (e.source instanceof ActiveEvent && ((ActiveEvent)e.source).type == LWPathway.class)
return;
if (e.active != null)
ActivePathwayHandler.setActive(e, e.active.pathway);
else
ActivePathwayHandler.setActive(e, null);
//if (e.active != null && e.active.getSelectable() != null && !e.active.isPathway()) {
if (e.active != null && e.active.getSelectable() != null) {
if (false && e.active.isPathway()) {
final LWComponent activeNode = ActiveComponentHandler.getActive();
// hack: only allow a pathway to become the active component (so we can see its
// notes), if the current active component is NOT a slide owned by a pathway
// (meaning really: not a pathway entry) So if you want to see pathway notes,
// you have to select an entry first. This is to support the workflow of
// selecting a node, then selecting a pathway to add it to. If we didn't
// exclude this case, selecting a node, then selecting a pathway, would
// DE-select the node, and you could never add it to the pathway! The only
// slides that currently WOULDN'T be pathway owned would be on-map slides,
// which aren't offically supported at the moment, but just in case we check that.
if (activeNode == null || activeNode instanceof LWSlide == false || !activeNode.isPathwayOwned()) {
; // allow pathway selection
} else {
return;
}
}
final LWSelection s = VUE.getSelection();
synchronized (s) {
// as long as we're not already here due to a selection notification,
// auto-select the appropriate component for this entry
if (!s.inNotify())
s.setTo(e.active.getSelectable());
}
}
if ((DEBUG.BOXES&&DEBUG.META) && e.active.isPathway() && !Images.lowMemoryConditions()) {
// for visually verifying the load-order
e.active.pathway.preCacheContent();
}
}
};
/**
* The active map handler -- on changes, will update the active pathway handler, as well as
* the global undo action labels.
*/
private static final ActiveInstance<LWMap>
ActiveMapHandler = new ActiveInstance<LWMap>(LWMap.class) {
@Override
protected void onChange(ActiveEvent<LWMap> e, final LWMap nowActive) {
if (nowActive != null) {
ActivePathwayHandler.setActive(e, nowActive.getActivePathway());
setMapActionsEnabled(true);
if (nowActive.getUndoManager() != null)
nowActive.getUndoManager().updateGlobalActionLabels();
} else {
ActivePathwayHandler.setActive(e, null);
setMapActionsEnabled(false);
}
}
};
private static void setMapActionsEnabled(boolean enable) {
//Util.printStackTrace("ENABLED MAP ACTIONS " + enable);
VueMenuBar.RootMenuBar.saveAction.setEnabled(enable);
VueMenuBar.RootMenuBar.saveAsAction.setEnabled(enable);
if (VUE.isApplet() && VueApplet.isZoteroApplet())
Actions.SaveCopyToZotero.setEnabled(enable);
VueMenuBar.RootMenuBar.publishMenu.setEnabled(enable);
Actions.CloseMap.setEnabled(enable);
VueMenuBar.RootMenuBar.printAction.setEnabled(enable);
VueMenuBar.RootMenuBar.pdfExportMenu.setEnabled(enable);
Actions.Undo.setEnabled(enable);
Actions.Redo.setEnabled(enable);
Actions.Paste.setEnabled(enable);
Actions.SelectAll.setEnabled(enable);
Actions.SelectAllNodes.setEnabled(enable);
Actions.SelectAllLinks.setEnabled(enable);
Actions.Reselect.setEnabled(enable);
Actions.Revert.setEnabled(enable);
Actions.ZoomIn.setEnabled(enable);
Actions.ZoomOut.setEnabled(enable);
Actions.ZoomFit.setEnabled(enable);
Actions.ZoomActual.setEnabled(enable);
Actions.ToggleFullScreen.setEnabled(enable);
Actions.ToggleSplitScreen.setEnabled(enable);
Actions.ToggleSlideIcons.setEnabled(enable);
Actions.TogglePruning.setEnabled(enable);
Actions.ToggleAutoZoom.setEnabled(enable);
VueMenuBar.RootMenuBar.setMenuEnableFlg(enable);
VueMenuBar.RootMenuBar.transformMenu.setEnabled(enable);
VueMenuBar.RootMenuBar.arrangeMenu.setEnabled(enable);
VueMenuBar.RootMenuBar.alignMenu.setEnabled(enable);
VueMenuBar.RootMenuBar.layoutMenu.setEnabled(enable);
VueMenuBar.RootMenuBar.linkMenu.setEnabled(enable);
Actions.NewNode.setEnabled(enable);
Actions.NewRichText.setEnabled(enable);
VueMenuBar.RootMenuBar.playbackMenu.setEnabled(enable);
}
/**
* For the currently active viewer (e.g., is visible
* and has focus). Actions (@see Actions.java) are performed on
* the active map in the active viewer.
* The active viewer can be null, which happens when we close the active viewer
* and until another grabs the application focus (unles it was the last viewer).
*
* Will update the active map handler.
*/
private static final ActiveInstance<MapViewer>
ActiveViewerHandler = new ActiveInstance<MapViewer>(MapViewer.class) {
@Override
protected void notifyListeners(ActiveEvent<MapViewer> e) {
if (!(e.active instanceof tufts.vue.ui.SlideViewer)) {
// SlideViewer not treated as application-level viewer: ignore when gets selected
super.notifyListeners(e);
if (e.active != null)
ActiveMapHandler.setActive(e, e.active.getMap());
else
ActiveMapHandler.setActive(e, null);
}
}
protected void onChange(ActiveEvent<MapViewer> e) {
}
};
/**
* For the currently active tufts.vue.DataSource
*/
private static final ActiveInstance<tufts.vue.DataSource>
ActiveDataSourceHandler = new ActiveInstance<tufts.vue.DataSource>(tufts.vue.DataSource.class) {
@Override
protected void onChange(ActiveEvent<tufts.vue.DataSource> e) {
// perhaps set active tufts.vue.BrowseDataSource
}
};
public static LWMap getActiveMap() { return ActiveMapHandler.getActive(); }
public static LWPathway getActivePathway() { return ActivePathwayHandler.getActive(); }
public static LWPathway.Entry getActiveEntry() { return ActivePathwayEntryHandler.getActive(); }
public static MapViewer getActiveViewer() { return ActiveViewerHandler.getActive(); }
public static LWComponent getActiveComponent() { return ActiveComponentHandler.getActive(); }
public static Resource getActiveResource() { return ActiveResourceHandler.getActive(); }
public static LWMap.Layer getActiveLayer() { return ActiveLayerHandler.getActive(); }
public static tufts.vue.DataSource getActiveDataSource() { return ActiveDataSourceHandler.getActive(); }
public static LWComponent getActiveFocal() {
MapViewer viewer = getActiveViewer();
if (viewer != null)
return viewer.getFocal();
else
return null;
}
/** @return the active tool as reported by the toolbar controller, which may only be a top-level tool*/
public static VueTool getActiveTool() { return VueToolbarController.getActiveTool(); }
public static boolean inPresentMode() {
return inFullScreen() && getActiveTool() instanceof PresentationTool;
}
// TODO: refactor this so that the only place that makes a distinction between a top
// level tool and a sub tool (e.g., SelectionTool v.s. SelectionTool.Direct) is in the toolbar
// controller.
/** @return the active subtool, or the active top-level tool if it has no active sub-tool */
public static VueTool getActiveSubTool() {
final VueTool tool = getActiveTool();
final VueTool subTool = tool == null ? null : tool.getSelectedSubTool();
if (subTool != null && subTool.getClass() != VueSimpleTool.class)
return subTool;
else
return tool;
}
public static void setActive(Class clazz, Object source, Object newActive) {
if (DEBUG.EVENTS && DEBUG.META) {
Util.printStackTrace("Generic setActive of " + clazz + " from " + source + ": " + newActive);
}
if (newActive == null || clazz.isInstance(newActive))
ActiveInstance.getHandler(clazz).setActive(source, newActive);
else
tufts.Util.printStackTrace("not an instance of " + clazz + ": " + newActive);
}
public static Object getActive(Class clazz) {
// todo: if a handler doesn't already exist for this class, just return null
return ActiveInstance.getHandler(clazz).getActive();
}
public static void addActiveListener(Class clazz, ActiveListener listener) {
ActiveInstance.addListener(clazz, listener);
}
public static void addActiveListener(Class clazz, Object reflectedListener) {
ActiveInstance.addListener(clazz, reflectedListener);
}
public static void removeActiveListener(Class clazz, ActiveListener listener) {
ActiveInstance.removeListener(clazz, listener);
}
public static void removeActiveListener(Class clazz, Object reflectedListener) {
ActiveInstance.removeListener(clazz, reflectedListener);
}
public static void setAppletContext(AppletContext ac) {
sAppletContext = ac;
}
public static AppletContext getAppletContext() {
return sAppletContext;
}
public static boolean isApplet() {
if (VueApplet.getInstance() !=null)
return VueApplet.getInstance().getAppletContext() != null;
else
return false;
// return sAppletContext != null;
}
public static String getSystemProperty(String name) {
// If we're an applet, System.getProperty will throw an AccessControlException
if (false && isApplet())
return null;
else {
String prop;
try {
prop = System.getProperty(name);
if (DEBUG.INIT) Log.debug("fetched system property " + name + "=[" + prop + "]");
} catch (java.security.AccessControlException e) {
System.err.println(e);
prop = null;
}
return prop;
}
}
public static final String NullSystemProperty = "";
/**
* Getl's a system property, guaranteeing a non-null return value.
* @return value or given system property, or an empty String
*/
public static String getSystemPropertyValue(String name) {
String value = getSystemProperty(name);
if (value == null)
return NullSystemProperty;
else
return value;
}
public static boolean isSystemPropertyTrue(String name) {
String value = getSystemProperty(name);
return value != null && value.toLowerCase().equals("true");
}
public static boolean hasSystemProperty(String name) {
return getSystemProperty(name) != null;
}
/*
public static java.net.URL getResource(String name) {
java.net.URL url = null;
// First, check the current directory:
java.io.File f = new java.io.File(name);
boolean foundInCWD = false;
if (f.exists()) {
try {
url = f.toURL();
foundInCWD = true;
} catch (Exception e) {
e.printStackTrace();
}
}
// If not found in the current directory, check the classpath:
if (url == null)
url = ClassLoader.getSystemResource(name);
if (foundInCWD)
System.out.println("resource \"" + name + "\" found in CWD at " + url);
else
System.out.println("resource \"" + name + "\" found in classpath at " + url);
return url;
}
*/
public static LWSelection getSelection() {
return ModelSelection;
}
public static tufts.vue.ui.SlideViewer getSlideViewer()
{
return slideViewer;
}
public static InspectorPane getInspectorPane() {
return inspectorPane;
}
public static boolean isStartupUnderway() {
return isStartupUnderway;
}
public static void activateWaitCursor() {
GUI.activateWaitCursor();
}
public static void clearWaitCursor() {
GUI.clearWaitCursor();
}
static void initUI() {
GUI.init();
// Doesn't work in Snow Leopard & not needed
// try {
// if (DEBUG.Enabled && Util.isMacPlatform() && !VUE.isApplet()) {
// // This is for debugging. The application icon for a distributed version
// // of VUE is set via an icons file specified in the Info.plist from
// // the VUE.app directory.
// // Be sure to call this after GUI initialized, or we are hidden from the OSX app dock.
// tufts.macosx.MacOSX.setApplicationIcon
// (VUE.class.getResource("/tufts/vue/images/vueicon32x32.gif").getFile());
// }
// } catch (Throwable t) {
// t.printStackTrace();
// }
if (DEBUG.Enabled) {
GUI.invokeAfterAWT(new Runnable() { public void run() {
// This, of course, must run in the AWT thread to do what we want:
Thread.currentThread().setName("*AWT*");
//Thread.currentThread().setName(Util.TERM_RED + "*AWT*" + Util.TERM_CLEAR);
} });
}
}
/** initialize based on command line args, and the initlaize the GUI */
public static void init(String[] args) {
if (args != null)
parseArgs(args);
initUI();
}
public static void init() {
init(null);
}
public static void parseArgs(String[] args) {
String allArgs = "";
for (int i = 0; i < args.length; i++) {
allArgs += "[" + args[i] + "]";
if (args[i].equals("-nosplash")) {
SKIP_SPLASH = true;
} else if (args[i].equals("-nodr")) {
SKIP_DR = true;
SKIP_SPLASH = true;
} else if (args[i].equals("-noem")) {
SKIP_EDITOR_MANAGER = true;
} else if (args[i].equals("-noidx")) {
SKIP_RDF_INDEX = true;
} else if (args[i].equals("-nocat")) {
SKIP_CAT = true;
} else if (args[i].equals("-skip")) {
SKIP_DR = true;
SKIP_CAT = true;
SKIP_SPLASH = true;
} else if (args[i].equals("-exit_after_init")) // for startup time trials
exitAfterInit = true;
else
DEBUG.parseArg(args[i]);
if (args[i].startsWith("-debug")) DEBUG.Enabled = true;
}
GUI.parseArgs(args);
if (DEBUG.INIT) System.out.println("VUE: parsed args " + allArgs);
if (DEBUG.Enabled) debugInit(DEBUG.TRACE);
}
public static final PatternLayout MasterLogPattern = new PatternLayout("VUE %d %5p [%t]%x %c{1}: %m%n");
public static void debugInit(boolean heavy) {
if (heavy) {
// this handy for finding code locations:
// Note: %F, %C and %M are "very slow"
MasterLogPattern.setConversionPattern("@%6r [%t] %5p %x "
+ Util.TERM_RED + "(%F/%C/%M)" + Util.TERM_CLEAR
+ " %m%n");
} else {
MasterLogPattern.setConversionPattern("@%.1p%6r [%t]%x %c{1}: %m%n");
//MasterLogPattern.setConversionPattern("@%6r %5p [%t]%x %c{1}: %m%n");
//MasterLogPattern.setConversionPattern("@%6r [%t] %5p %c %x %m%n");
}
// This will enable it for every logger in any jar, which is tons of stuff.
// Logger.getRootLogger().setLevel(Level.DEBUG);
TuftsLog.setLevel(Level.DEBUG);
EduTuftsLog.setLevel(Level.DEBUG);
}
/*
// no longer needed as is: can clean up to dump all loggers in the VM if we like:
private static void updateTuftsLoggers(Level newLevel)
{
if (newLevel == null)
newLevel = DEBUG.Enabled ? Level.DEBUG : Level.INFO;
Enumeration<Logger> e = LogManager.getCurrentLoggers();
while (e.hasMoreElements()) {
Logger l = e.nextElement();
//System.out.println("Found logger: " + l + "; " + l.getName() + " at " + l.getLevel()
if (DEBUG.Enabled && DEBUG.META)
System.out.println("Found in "
+ l.getParent() + ": "
+ l + "; " + l.getName() + "; at " + l.getLevel()
//+ " " + l.getParent().getName()
);
if (newLevel != null && l.getName().startsWith("tufts")) {
Level curLevel = l.getLevel();
if (true) {
System.out.println("\tlogger " + l.getName() + "; " + curLevel + " -> " + l.getLevel());
continue;
}
if (curLevel == null || newLevel.toInt() > curLevel.toInt()) {
l.setLevel(newLevel);
if (true||DEBUG.Enabled)
System.out.println("\tlogger " + l.getName() + "; " + curLevel + " -> " + l.getLevel());
}
}
}
}
*/
//-----------------------------------------------------------------------------
// Variables used during VUE startup
//-----------------------------------------------------------------------------
private static boolean exitAfterInit = false;
private static boolean SKIP_DR = false; // don't load DRBrowser, no splash & no startup map
private static boolean SKIP_CAT = false; // don't load category model
private static boolean SKIP_SPLASH = false;
private static boolean SKIP_EDITOR_MANAGER = false;
private static boolean SKIP_RDF_INDEX = false;
private static String NAME;
private static DockWindow pathwayDock;
private static DockWindow formatDock;
private static DockWindow slideDock;
private static DockWindow pannerDock;
private static DockWindow MapInspector;
private static DockWindow ObjectInspector;
private static DockWindow outlineDock;
private static DockWindow floatingZoomDock;
private static DockWindow layersDock;
private static DockWindow metaDataSearchDock;
private static DockWindow interactionToolsDock;
private static DockWindow contentDock;
private static DockWindow mergeMapsDock;
private static DockWindow anchor;
private static boolean UseLeopardAnchor = false;
static {
Logger.getRootLogger().removeAllAppenders(); // need to do this or we get everything twice
//BasicConfigurator.configure();
//Logger.getRootLogger().addAppender(new ConsoleAppender(new PatternLayout("VUE %d [%t] %-5p %c:%x %m%n")));
//final org.apache.log4j.Layout pattern = new PatternLayout("VUE %d [%t] %5p %x %m%n");
//final PatternLayout pattern = new PatternLayout("VUE %d [%t] %5p %x %F/%C/%M %m%n");
final PatternLayout pattern = MasterLogPattern;
//Logger.getRootLogger().addAppender(new ConsoleAppender(pattern, "System.err"));
Logger.getRootLogger().addAppender(new ConsoleAppender(pattern));
Logger.getRootLogger().addAppender(new WriterAppender(pattern, Util.getLogWriter()));
Logger.getRootLogger().setLevel(Level.INFO);
//Log.addAppender(new ConsoleAppender(new PatternLayout("[%t] %-5p %c %x - %m%n")));
//set tooltips to psuedo-perm
ToolTipManager.sharedInstance().setDismissDelay(240000);
ToolTipManager.sharedInstance().setInitialDelay(500);
//if (VueUtil.isMacPlatform())
// installMacOSXApplicationEventHandlers();
//Preference initialzation for UI.
MetadataSchemaPreference.getInstance();
}
/** push a short diagnostic string onto the log output stack */
public static void diagPush(String s) {
s = "[" + s + "]";
//s = "#" + s;
if (org.apache.log4j.NDC.getDepth() == 0)
org.apache.log4j.NDC.push(" " + s);
else
org.apache.log4j.NDC.push(s);
}
public static void diagPop() {
org.apache.log4j.NDC.pop();
}
public static void main(String[] args)
{
VUE.isStartupUnderway = true;
parseArgs(args);
if (DEBUG.Enabled) {
// dump a date if debug is on, as it will have installed a log format that leaves it out
Log.info("VUE startup: " + new Date());
}
Log.info("VUE build: " + tufts.vue.Version.AllInfo);
Log.info("Current Platform: " + Util.getPlatformName());
final String requestedJavaArch = System.getenv("JAVA_ARCH");
if (requestedJavaArch != null)
Log.info("JAVA_ARCH REQUEST: " + requestedJavaArch);
Log.info("Running JVM: " +
getSystemProperty("java.runtime.version") + " / " + getSystemProperty("sun.arch.data.model") + " bit"
+ "; MaxMemory(-Xmx)=" + VueUtil.abbrevBytes(Runtime.getRuntime().maxMemory())
+ ", CurMemory(-Xms)=" + VueUtil.abbrevBytes(Runtime.getRuntime().totalMemory())
+ ", FreeMemory=" + VueUtil.abbrevBytes(Runtime.getRuntime().freeMemory())
);
Log.info("VUE version: " + VueResources.getString("vue.version"));
Log.info("Current Working Directory: " + getSystemProperty("user.dir"));
String host = System.getenv("HOST");
if (host == null) host = System.getenv("HOSTNAME");
if (host == null) host = System.getenv("COMPUTERNAME");
if (host == null) host = System.getenv("USERDOMAIN");
Log.info("User/host: " + getSystemProperty("user.name") + "@" + host);
if (VueUtil.isMacPlatform()) {
try {
installMacOSXApplicationEventHandlers();
} catch (Throwable t) {
Log.error("unable to install handler for Finder double-click to open .vue files; /System/Library/Java may be missing or not in the CLASSPATH", t);
}
}
for (int i = 0; i < args.length; i++) {
if (args[i] == null || args[i].length() < 1 || args[i].charAt(0) == '-')
continue;
if (DEBUG.INIT) out("command-line file to open " + args[i]);
VUE.FilesToOpen.add(args[i]);
}
try {
initUI();
diagPush("init");
initApplication();
diagPop();
} catch (Throwable t) {
Util.printStackTrace(t, "VUE init failed");
VueUtil.alert(VueResources.getString("dialog.initfailed.message"), t);
}
VUE.isStartupUnderway = false;
Log.info("UI startup completed.");
//-------------------------------------------------------
// Handle any outstanding file (map) open requests.
//-------------------------------------------------------
try {
// This must now happen AFTER data-sources are loaded, as it's possible that
// they will be providing authentication that will be required to get
// content on the maps that will be opened [VUE-879]
// Note: if BLOCKING_OSID_LOAD is false (the new default, so VUE startup
// can't be hung by any single repository problem), the OSID's repository
// configuration threads in the VDSM will be in a race with the the below
// map open below if it contains any content URL's that require
// authentication keys. That content will fail to load unless the relevant
// repository configure thread has managed to complete before the URL access
// is attempted. Resources don't currently know what repository object they
// came from, so we can't wait for the relevant keys to be present, and any
// repository may hang, so we can't just wait for them all.
// UrlAuthentication does now dynamically update itself as new repositories
// come online, so this isn't such an issue, but the VUE-879 bug will appear
// if a URL authenticating repository is not present at all (which has
// always been an unresolved issue if a user shares a map with authenticated
// content with someone who doesn't have the authenticating data source
// installed), or if the configure thread loses the race with the map open.
// todo: someday, Resources can be tagged as requiring authentication (or
// actually reference some kind of repository object that knows this) so
// when URL content is requested, we could know to wait on the
// DS_ALL_CONFIGURED message from the VDSM before attempting access, or even
// better, the DS_CONFIGURED message for the relevant DataSource/Repository.
handleOutstandingVueMapFileOpenRequests();
} catch (Throwable t) {
Log.error("failed to handle file open at init: " + FilesToOpen, t);
}
if (DEBUG.KEYS) {
GUI.invokeAfterAWT(new Runnable() { public void run() {
for (AbstractAction a : VueAction.getAllActions()) {
KeyStroke ks = (KeyStroke) a.getValue(Action.ACCELERATOR_KEY);
if (ks != null && a.getPropertyChangeListeners().length == 0)
Log.warn("Action has no listeners, may be unable to activate by keystroke: " + Util.tags(a) + "; " + ks);
}
}});
}
if (exitAfterInit) {
out("init completed: exiting");
System.exit(0);
}
//-------------------------------------------------------
// Trigger the load of the OSID's and set up UrlAuthentication
//-------------------------------------------------------
initDataSources();
GUI.invokeAfterAWT(new Runnable() { public void run() {
if (contentPanel != null)
contentPanel.loadDataSourceViewer();
// Kick-off tufts.vue.VueDataSource viewer build threads:
// must be done in AWT to be threadsafe, as involves
// non-synhcronized code in tufts.vue.VueDataSource while
// setting up the threads
// DataSourceViewer.cacheDataSourceViewers();
}});
//-------------------------------------------------------
// complete the rest of our tasks at min priority
//-------------------------------------------------------
Thread.currentThread().setPriority(Thread.MIN_PRIORITY);
if (!SKIP_SPLASH) {
// start in another thread as may pop dialog
// that will block further progress on the
// run-out of main
final Thread versionThread = new Thread("version-check") {
public void run() {
if (DEBUG.THREAD) Log.debug("version-check kicked off");
checkLatestVersion();
}
};
versionThread.setPriority(Thread.MIN_PRIORITY);
versionThread.setDaemon(true);
// delay kickoff until after any already outstanding AWT invocations (e.g., map open's, data source loads)
GUI.invokeAfterAWT(new Runnable() { public void run() {
versionThread.start();
}});
}
// try {
// // any immediate user UI requests (mouse-click) for a tufts.vue.DataSource
// // viewer will happen in the higher priority AWT thread, and will be loaded
// // immediately, no matter where we are in the caching process. The cache /
// // viewer construction code is fully synchronized, so if the desired viewer
// // is already loading, AWT will just block until it's complete.
// DataSourceViewer.cacheDataSourceViewers();
// } catch (Throwable t) {
// t.printStackTrace();
// }
try {
Log.debug("loading fonts...");
// let this run in the remainder of the main thread
FontEditorPanel.getFontNames();
} catch (Throwable t) {
Log.warn("font cache", t);
}
Log.info("main complete");
}
private static boolean didDataInit = false;
/**
* Unmarshall the user installed data sources and get them configured with their
* repositories, and ensure UrlAuthentication is initialized, which will listen for
* configuring DataSources and scan them for authentication keys
*/
static synchronized void initDataSources() {
if (!didDataInit && SKIP_DR == false ){//&& !VUE.isApplet()) {
VUE.diagPush("initDS");
Log.debug("initDataSources: started");
try {
_initDataSources();
didDataInit = true;
} catch (Throwable t) {
Log.error("failed to init data sources", t);
}
Log.debug("initDataSources: completed");
VUE.diagPop();
}
}
private static void _initDataSources()
{
// PROBLEM:
// org.exolab.castor.xml.MarshalException: Declared encoding "UTF-8" does not
// match actual one "MacRoman"; this might not be an error.{File: [not
// available]; line: [not available]; column: [not available]}
// VUE-879: UrlAuthentication should initialized BEFORE
// startRepositoryConfiguration. It will listen for events from VDSM to scan
// each DataSource as it's configured for any authentication credentials.
if (VUE.isApplet())
return;
UrlAuthentication.getInstance();
final VueDataSourceManager VDSM =
edu.tufts.vue.dsm.impl.VueDataSourceManager.getInstance();
// unmarshall the installed data sources
VDSM.load();
// As of 2008-12-21: This will instance a DataSourceViewer, which calls
// VDSM.getDataSources(), which will will trigger a load (unmarshalling) of
// DataSources if they're not already loaded. If BLOCKING_OSID_LOAD is true, it
// won't return until they're all configured. If false, it will return right
// away, returning the list of unconfigured DataSources, which will normally be
// quickly configured on separate threads and issue callbacks.
//DR_BROWSER.loadDataSourceViewer();
if (!BLOCKING_OSID_LOAD) {
// Will deliver DS_CONFIGURED events to UrlAuthentication as they come in
VDSM.startRepositoryConfiguration(null);
}
}
static void initApplication()
{
final Window splashScreen;
if (VUE.isApplet())
{
//SKIP_DR=true;
SKIP_SPLASH=true;
SKIP_CAT=true;
}
if (SKIP_DR || SKIP_SPLASH ) {
splashScreen = null;
//DEBUG.Enabled = true;
} else
splashScreen = new SplashScreen();
//------------------------------------------------------------------
// Make sure these classes are all fully loaded to establish
// their Keys. todo: can get all subclasses of LWComponent
// and newInstance them just to be sure. In any case, this
// probably isn't even required, but it's helping debugging
// while implementing the new Key & Property LWComponent
// code. -- SMF
Log.debug("pre-constructing core LW types...");
new LWComponent();
new LWLink();
new LWImage();
new LWNode();
new LWText();
// Load images even before building the interface, in case
// UI may trigger image-icon (thumbnail) loads of user content.
// (E.g., "My Saved Content")
Log.debug("loading disk cache...");
Images.loadDiskCache();
Log.debug("loading disk cache: done");
//------------------------------------------------------------------
Log.debug("building interface...");
diagPush("build");
buildApplicationInterface();
diagPop();
if (Util.isMacLeopard()) {
// Critical for keeping DockWindow's on top.
// See tufts.vue.gui.FullScreen.FSWindow constructor for more on this.
DockWindow.raiseAll();
}
Log.debug("interface built; splash down...");
if (splashScreen != null)
splashScreen.setVisible(false);
//------------------------------------------------------------------
//VUE.clearWaitCursor();
// No longer used:
// if (SKIP_DR == false) {
// Log.debug("caching tool panels...");
// NodeTool.getNodeToolPanel();
// // LinkTool.getLinkToolPanel();
// }
// Must call this after all LWEditors have been created and
// put in the AWT hierarchy:
if (!SKIP_EDITOR_MANAGER)
EditorManager.install();
// initialize enabled state of actions via a selection set:
VUE.getSelection().clearAndNotify();
// //---------------------------------------------
// // Start the loading of the data source viewer
// if (SKIP_DR == false && DR_BROWSER != null && FilesToOpen.size() > 0)
// initDataSources();
// //---------------------------------------------
//Preferences p = Preferences.userNodeForPackage(VUE.class);
//p.put("DRBROWSER.RUN", "yes, it has");
// MAC v.s. PC WINDOW PARENTAGE & FOCUS BEHAVIOUR:
//
// Window's that are shown before their parent's are shown do NOT adopt a
// stay-on-top-of-parent behaviour! (at least on mac). FURTHERMORE: if you iconfiy the
// parent and de-iconify it, the keep-on-top is also lost permanently! (Even if you
// hide/show the child window after that) None of this happens on the PC, only Mac OS X.
// Iconifying also hides the child windows on the PC, but not on Mac. On the PC, there's
// also no automatic way to install the action behaviours to take effect (the ones in the
// menu bar) when a tool window has focus. Actually, mac appears to do something smart
// also: if parent get's MAXIMIZED, it will return to the keep on top behaviour, but you
// have to manually hide/show it to get it back on top.
//
// Also: for some odd reason, if we use an intermediate root window as the
// master parent, the MapPanner display doesn't repaint itself when dragging it
// or it's map!
//
// Addendum: keep-on-top now appears to survive iconification on mac.
//
// [ Assuming this is java 1.4 -- 1.5? ]
// is done in buildApplicationInterface
//getRootWindow().setVisible(true);
//out("ACTIONTMAP " + java.util.Arrays.asList(frame.getRootPane().getActionMap().allKeys()));
//out("INPUTMAP " + java.util.Arrays.asList(frame.getRootPane().getInputMap().allKeys()));
//out("\n\nACTIONTMAP " + java.util.Arrays.asList(frame.getActionMap().allKeys()));
//out("ACTIONTMAP " + Arrays.asList(VUE.getActiveViewer().getActionMap().allKeys()));
//out("INPUTMAP " + Arrays.asList(VUE.getActiveViewer().getInputMap().keys()));
//out("INPUTMAP " + Arrays.asList(getInputMap().keys()));
//VUE.clearWaitCursor();
if (!SKIP_DR) // SKIP_CAT?
getCategoryModel(); // load the category model
Log.debug("initApplication completed.");
}
private static void handleOutstandingVueMapFileOpenRequests()
{
boolean openedUserMap = false;
if (FilesToOpen.size() > 0) {
Log.info("outstanding file open requests: " + Util.tags(FilesToOpen));
if (DEBUG.Enabled) Util.dump(FilesToOpen);
// [OLD: in case not already loaded, make absolutely sure
// all data sources are loaded (VUE-879), so any needed
// authentication keys have been found] -- We don't
// actually need this until the map is displayed, as no
// content should be requested until then. SMF 2008-12-21
//
// initDataSources();
try {
Iterator i = FilesToOpen.iterator();
while (i.hasNext()) {
final String fileName = (String) i.next();
openedUserMap = true;
GUI.invokeAfterAWT(new Runnable() {
public void run() {
VUE.activateWaitCursor();
//LWMap map = OpenAction.loadMap(fileName);
if (DEBUG.Enabled) Log.debug("opening map during startup " + fileName);
if (fileName != null) {
displayMap(new File(fileName));
//openedUserMap = true;
}
}});
}
} finally {
VUE.clearWaitCursor();
}
GUI.invokeAfterAWT(new Runnable() { public void run() {
// ensure first item gets selected
if (mMapTabsLeft.getTabCount() > 0)
mMapTabsLeft.setSelectedIndex(0);
// above would normally focus grab, but it skips these during startup,
// so do it manually here: (so the pathway panel loads, etc)
// [re-enabled focus grabs even during startup -- was mainly for command line debug loads]
//mMapTabsLeft.getViewerAt(0).grabVueApplicationFocus("startup", null);
}});
}
if (false && DEBUG.Enabled && !openedUserMap) {
//if (SKIP_DR && FilesToOpen.size() == 0) {
//-------------------------------------------------------
// create example map(s)
//-------------------------------------------------------
//LWMap map1 = new LWMap("Map 1");
LWMap map2 = new LWMap("Map 2");
//installExampleNodes(map1);
installExampleMap(map2);
//map1.setFillColor(new Color(255, 255, 192));
//displayMap(map1);
displayMap(map2);
//toolPanel.add(new JLabel("Empty Label"), BorderLayout.CENTER);
}
//VUE.clearWaitCursor();
}
private static void installMacOSXApplicationEventHandlers()
{
if (!VueUtil.isMacPlatform())
throw new RuntimeException("can only install OSX event handlers on Mac OS X");
VUE.Log.debug("INSTALLING MAC OSX APPLICATION HANDLER");
File test = new File("/System/Library/Java/com/apple/cocoa/application/NSWindow.class");
if (test.exists())
Log.info("cocoa-java bridge appears present; found " + test);
else
Log.info("cocoa-java bridge is not present; couldn't find " + test);
tufts.macosx.MacOSX.registerApplicationListener(new tufts.macosx.MacOSX.ApplicationListener() {
public boolean handleOpenFile(String filename) {
VUE.Log.info("OSX OPEN FILE " + filename);
if (VUE.isStartupUnderway)
VUE.FilesToOpen.add(filename);
else
VUE.displayMap(new File(filename));
return true;
}
public boolean handleQuit() {
VUE.Log.debug("OSX QUIT");
ExitAction.exitVue();
// Always return false. If we claim this is "handled",
// OSX will do the quit for us, and even if the ExitAction
// was aborted, we'd exit anyway...
return false;
}
public boolean handleAbout() {
VUE.Log.debug("OSX ABOUT");
new AboutAction().fire(tufts.macosx.MacOSX.ApplicationListener.class);
return true;
}
public boolean handlePreferences() {
VUE.Log.debug("OSX PREFERENCES");
Actions.Preferences.fire(tufts.macosx.MacOSX.ApplicationListener.class);
return true;
}
});
}
private static final boolean ToolbarAtTopScreen = false && VueUtil.isMacPlatform();
private static void buildApplicationInterface() {
//-------------------------------------------------------
// Create the tabbed panes for the viewers
//-------------------------------------------------------
if (!VUE.isApplet())
{
mMapTabsLeft = new MapTabbedPane("*left", true);
mMapTabsRight = new MapTabbedPane("right", false);
}
else
mMapTabsLeft = new MapTabbedPane("*left",true);//VueApplet.getMapTabbedPane();
//-------------------------------------------------------
// Create the split pane
//-------------------------------------------------------
mViewerSplit = buildSplitPane(mMapTabsLeft, mMapTabsRight);
if (VUE.isApplet())
{
mViewerSplit.setBackground(new Color(244,244,244));
}
//GUI.applyToolbarColor(mMapTabsRight);
//-------------------------------------------------------
// create a an application frame and layout components
//-------------------------------------------------------
if (DEBUG.INIT) out("creating VueFrame...");
if (!VUE.isApplet())
VUE.ApplicationFrame = new VueFrame();
if (DEBUG.INIT) out("created VueFrame");
//------------------------------
// Set popups heavyweight throughout the application
//-------------------------------
/* Originally doing this created some problems, but it looks like previous
* changes I made in the heavyweight popup stuff have made. I can't find
* a case where this would *really* help out on the mac so I'm strictly
* using this for windows/unix right now. Unix because when i booted this
* up in linux i noticed the same problem melanie reported on windows
*/
//if (Util.isWindowsPlatform() || Util.isUnixPlatform())
// if (VUE.isApplet())
//PopupFactory.setSharedInstance(new VuePopupFactory(PopupFactory.getSharedInstance()));
//-----------------------------------------------------------------------------
// Man VUE Toolbar (map editing tool)
//-----------------------------------------------------------------------------
// The real tool palette window withtools and contextual tools
VueToolbarController tbc = VueToolbarController.getController();
ModelSelection.addListener(tbc);
DockWindow toolbarDock = null;
final JComponent toolbar;
JPanel toolbarPanel = null;
if (!VUE.isApplet())
{
if (VueToolPanel.IS_CONTEXTUAL_TOOLBAR_ENABLED)
toolbar = tbc.getToolbar();
else
toolbar = tbc.getToolbar().getMainToolbar();
toolbarPanel = constructToolPanel(toolbar);
if (ToolbarAtTopScreen) {
toolbarDock = GUI.createToolbar("Toolbar", toolbar);
} else {
ApplicationFrame.addComp(toolbarPanel, BorderLayout.NORTH);
}
}
createDockWindows();
final tufts.vue.gui.Screen screen = GUI.getScreenForWindow(null);
if (!VUE.isApplet())
{
// GUI.createDockWindow("Font").add(new FontEditorPanel()); // just add automatically?
//final DockWindow fontDock = GUI.createToolbar("Font", new FontPropertyPanel());
// final DockWindow fontDock = GUI.createToolbar("Font", new FontEditorPanel(LWKey.Font));
//final DockWindow linkDock = GUI.createToolbar("Link", new LinkPropertyPanel());
//final DockWindow actionDock = GUI.createToolbar("Actions", new VueActionBar());
//final DockWindow fontDock = null;
//final DockWindow linkDock = null;
//final DockWindow actionDock = null;
//fontDock.setResizeEnabled(false);
//linkDock.setResizeEnabled(false);
//pannerDock.setChild(linkDock);
//fontDock.setChild(linkDock);
//fontDock.setLowerRightCorner(GUI.GScreenWidth, GUI.GScreenHeight);
/*
* This isn't currently used now but I have a feeling it'll come back if not i'll remove it.
*/
// Now that we have all the DockWindow's created the VueMenuBar, which needs the
// list of Windows for the Window's menu. The order they appear in this list is
// the order they appear in the Window's menu.
//VUE.ToolWindows = new Object[] {
//unused stuff.
//searchDock,
/* keywords goes here when its done*/
//ObjectInspector,
/* Linear View goes here when its done*/
//MapInspector,
/* node inspector */
/*notes didn't end up getting its own window*/
//outlineDock,
//pannerDock,
//DR_BROWSER_DOCK,
//slideDock,
//resourceDock,
//formatDock,
//htWindow,
//pathwayDock,
//actionDock,
//fontDock,
//linkDock,
// toolbarDock,
//};
// adding the menus and toolbars
if (DEBUG.INIT) out("setting JMenuBar...");
ApplicationFrame.setJMenuBar(VueMenuBar.RootMenuBar = new VueMenuBar(/*VUE.ToolWindows*/));
if (DEBUG.INIT) out("VueMenuBar installed.");;
if (true){
ApplicationFrame.addComp(mViewerSplit, BorderLayout.CENTER);
}
else{
ApplicationFrame.addComp(mMapTabsLeft, BorderLayout.CENTER);
}
try {
ApplicationFrame.pack();
} catch (ArrayIndexOutOfBoundsException e) {
Log.error("OSX TIGER JAVA BUG at frame.pack()", e);
}
/*
if (SKIP_DR) {
ApplicationFrame.setSize(750,450);
} else {
ApplicationFrame.setSize(800,600);
// todo: make % of screen, make sure tool windows below don't go off screen!
}
*/
//if (DEBUG.INIT) out("validating frame...");
ApplicationFrame.validate();
//if (DEBUG.INIT) out("frame validated");
//int appWidth = (int) (GUI.GScreenWidth * 0.75);
//int appHeight = (int) (GUI.GScreenHeight * 0.75);
// If you've got a wide screen, leave at least 600
// pixels at the right for two full 300pix DockWindow's
/* VUE-795 replaces the default screen sizing logic...
* if (GUI.GScreenWidth >= 1600) {
int maxWidth = GUI.GScreenWidth - (GUI.GInsets.left + DockWindow.DefaultWidth * 2);
if (appWidth > maxWidth)
appWidth = maxWidth;
}
if (appWidth > 1600)
appWidth = 1600;
if (appHeight > 1024)
appHeight = 1024;
*/
int appWidth = (int) (screen.width * 0.90);
int appHeight = (int) (screen.height * 0.90);
if (screen.width > 1280) {
appWidth = (int) (screen.width * 0.75);
appHeight = (int) (screen.height * 0.90);
}
WindowPropertiesPreference wpframe = ApplicationFrame.getWindowProperties();
Dimension sz = wpframe.getWindowSize();
Point pos = wpframe.getWindowLocationOnScreen();
if (wpframe.isEnabled() && !wpframe.isAllValuesDefaults() &&
ApplicationFrame.isPointFullyOnScreen(pos,sz))
{
if ((sz.getWidth() < 100) || (sz.getHeight() < 100))
ApplicationFrame.setSize((int)appWidth, (int)appHeight);
else
ApplicationFrame.setSize((int)sz.getWidth(), (int)sz.getHeight());
if ((pos.getX() < 0) || (pos.getY() < 0)) {
ApplicationFrame.setLocation
(screen.margin.left,
screen.margin.top + (ToolbarAtTopScreen ? DockWindow.ToolbarHeight : 0));
} else {
ApplicationFrame.setLocation((int)pos.getX(),(int)pos.getY());
}
}
else
{
ApplicationFrame.setSize(appWidth, appHeight);
ApplicationFrame.setLocation
(screen.margin.left,
screen.margin.top + (ToolbarAtTopScreen ? DockWindow.ToolbarHeight : 0));
}
// MAC NOTE WITH MAXIMIZING: if Frame's current location y value
// is less than whatever's it's maximized value is set to, maximizing
// it will use the y value, not the max value. True even if set
// y value after setting to maximized but before it's put on screen.
//GUI.centerOnScreen(ApplicationFrame);
/*-------------------------------------------------------
final boolean loadTopDock = false;
if (loadTopDock && DockWindow.getMainDock() != null) {
// leave room for dock at top
Rectangle maxBounds = GUI.getMaximumWindowBounds();
int adj = DockWindow.getCollapsedHeight();
maxBounds.y += adj;
maxBounds.height -= adj;
ApplicationFrame.setMaximizedBounds(maxBounds);
}
if (false) ApplicationFrame.setExtendedState(Frame.MAXIMIZED_BOTH);
-------------------------------------------------------*/
/*
if (!SKIP_DR) {
LWMap startupMap = null;
try {
final java.net.URL startupURL;
startupURL = VueResources.getURL("resource.startmap");
startupMap = OpenAction.loadMap(startupURL);
startupMap.setFile(null); // dissassociate startup map from it's file so we don't write over it
startupMap.setLabel("Welcome");
startupMap.markAsSaved();
} catch (Exception ex) {
ex.printStackTrace();
VueUtil.alert(null, "Cannot load the Start-up map", "Start Up Map Error");
}
try {
if (startupMap != null)
displayMap(startupMap);
} catch (Exception ex) {
ex.printStackTrace();
VueUtil.alert(null, "Failed to display Start-up Map", "Internal Error");
}
} else {
//pannerTool.setVisible(true);
}
*/
if (FilesToOpen.size() == 0)
VUE.displayMap(new LWMap(VueResources.getString("vue.main.newmap")));
// Generally, we need to wait until java 1.5 JSplitPane's have been validated to
// use the % set divider location. Unfortunately there's a bug in at MacOS java
// 1.5 BasicSplitPaneUI (it's not in the 1.4 version), where setKeepHidden isn't
// being called when the divider goes to the wall via setDividerLocation, only when
// the one-touch buttons are manually clicked. So, for example, if the user
// de-maximizes the frame, suddenly a hidden split-pane will pop out! So, we've
// hacked into the UI code, grabbed the damn right-one-touch button, grabbed
// it's action listener, and here just call it directly...
//
// See javax.swing.plaf.basic.BasicSplitPaneDivider.OneTouchActionHandler.
//
// It appears on Windows we need to actually wait till the frame is shown also...
// show before split adjust on pc
if (!Util.isMacPlatform())
ApplicationFrame.setVisible(true);
if (SplitPaneRightButtonOneTouchActionHandler != null) {
if (DEBUG.INIT) Util.printStackTrace("\"pressing\": " + SplitPaneRightButtonOneTouchActionHandler);
// Not reliable on PC unless we invokeLater
if (!VUE.isApplet())
{
GUI.invokeAfterAWT(new Runnable() { public void run() {
SplitPaneRightButtonOneTouchActionHandler.actionPerformed(null);
}});
}
// this is also eventually getting eaten in java 1.5: no matter where
// we put this call during init: will have to patch w/more hacking
// or live with it. Actually, it get's eaten eventually in java 1.4.2
// also.
// Maybe because we maximized the frame before it was shown?
// [ not making a difference]
// Well. this is working at least the first time now by
// doing it BEFORE the peers are created.
//mViewerSplit.setResizeWeight(0.5d);
} else {
// for java 1.4.2
mViewerSplit.setDividerLocation(1.0);
}
// can show after split adjust on mac (turns out: only on older, slower macs)
if (Util.isMacPlatform()) {
ApplicationFrame.setVisible(true);
if (SplitPaneRightButtonOneTouchActionHandler != null) {
// This is backup: hit it one more time just in case, as on the
// newer, faster intel Mac's, the timing is changed and the
// above is no longer catching it.
GUI.invokeAfterAWT(new Runnable() { public void run() {
SplitPaneRightButtonOneTouchActionHandler.actionPerformed(null);
}});
}
}
}
if (toolbarDock != null) {
toolbarDock.suggestLocation(screen.top, screen.left);
toolbarDock.setWidth(screen.width);
toolbarDock.setVisible(true);
}
//-----------------------------------------------------------------------------
//
// Set locations for the inspector windows and make some of them visible
//
//-----------------------------------------------------------------------------
positionDockWindows(screen);
mapInspectorPanel.metadataPanel.refreshAll();
//I'm just putting a comment in here becuase this seems odd to me, and I wanted it to be clear it was intentional.
//"As we move away from a "datasource" centric vision of VUE, the "Content" window should be collapsed when launching VUE"
//This will only take effect the first time VUE is started or when preference to remember window position is disabled.
// -MK
if (!formatDock.getWindowProperties().isEnabled())
{
formatDock.setLowerLeftCorner(VueResources.getInt("formatting.location.x"),
VueResources.getInt("formatting.location.y")+100);
// formatDock.setLocation(,
// );
DockWindow.flickerAnchorDock();
if (!VUE.isApplet())
formatDock.setVisible(true);
}
}
/**
* There are two main cases to deal with: windows that have no saved position (as when VUE is
* run the first time, or the window position saving preference is turned off) -- these must be
* given a reasonable initial position.
*
* The other case is to restore the old user size and position of the
* window.
*
* Care must be taken that these two cases don't run into conflict, in particular accidentally
* giving a "resonable" position to a window that was supposed to be restored, and/or moving
* windows around in front of the user after they appear on the screen.
*/
private static void positionDockWindows(final tufts.vue.gui.Screen screen)
{
// by default, order the windows left to right across the top
final List<DockWindow> acrossTopList = new ArrayList<DockWindow>();
if (!MapInspector.getWindowProperties().isEnabled() || !MapInspector.getWindowProperties().isWindowVisible())
acrossTopList.add(MapInspector);
if (!pathwayDock.getWindowProperties().isEnabled() || !pathwayDock.getWindowProperties().isWindowVisible())
acrossTopList.add(pathwayDock);
// if (!DR_BROWSER_DOCK.getWindowProperties().isEnabled() || !DR_BROWSER_DOCK.getWindowProperties().isWindowVisible())
// acrossTopList.add(DR_BROWSER_DOCK);
if (!ObjectInspector.getWindowProperties().isEnabled() || !ObjectInspector.getWindowProperties().isWindowVisible())
acrossTopList.add(ObjectInspector);
// if (!metaDataSearchDock.getWindowProperties().isEnabled() || !metaDataSearchDock.getWindowProperties().isWindowVisible())
// acrossTopList.add(metaDataSearchDock);
// if (!mergeMapsDock.getWindowProperties().isEnabled() || !mergeMapsDock.getWindowProperties().isWindowVisible())
// acrossTopList.add(mergeMapsDock);
// if (!ontologyDock.getWindowProperties().isEnabled() || !ontologyDock.getWindowProperties().isWindowVisible())
// acrossTopList.add(ontologyDock);
// acrossTopList.add(formatDock);
acrossTopList.add(outlineDock);
acrossTopList.add(pannerDock);
acrossTopList.add(metaDataSearchDock);
acrossTopList.add(layersDock);
acrossTopList.add(contentDock);
acrossTopList.add(mergeMapsDock);
acrossTopList.add(interactionToolsDock);
acrossTop = acrossTopList.toArray(new DockWindow[acrossTopList.size()]);
if (outlineDock != null)
outlineDock.setLowerLeftCorner(screen.left, screen.bottomIn);
if (pannerDock != null)
pannerDock.setLowerRightCorner(screen.rightIn, screen.bottomIn);
if (DockWindow.getTopDock() != null) {
// This was for old experimental GUI code -- getTopDock() always returns null these days -- SMF 2012
prepareForTopDockDisplay(acrossTop);
}
if (acrossTop.length > 0) {
// Run after AWT to ensure all peers to have been created & shown
GUI.invokeAfterAWT(new Runnable() { public void run() {
// TODO: doesn't appear to be allowing old state to rule as is supposed to:
assignDefaultPositions(acrossTop);
if (!VUE.isApplet())
DockWindow.RestoreAllWindowStates();
}});
}
// restoreSavedDockWindowPositions();
// If we do this here, positionForDocking could run after us, blowing away our restore positions...
// // old positioning code
// int inspectorx = ApplicationFrame.getX() + ApplicationFrame.getWidth();
// MapInspector.suggestLocation(inspectorx, ApplicationFrame.getY());
// ObjectInspector.suggestLocation(inspectorx, ApplicationFrame.getY() + MapInspector.getHeight() );
// pannerDock.suggestLocation(ApplicationFrame.getX() - pannerDock.getWidth(), ApplicationFrame.getY());
}
private static void saveAllWindowProperties() {
DockWindow.SaveAllWindowStates();
ApplicationFrame.saveWindowProperties();
}
// private static void restoreSavedDockWindowPositions() {
// if (!VUE.isApplet()) {
// // Restore the size & position of windows
// // to check: are we affected by Lion+ app window auto-restore?
// pathwayDock.positionWindowFromProperties();
// formatDock.positionWindowFromProperties();
// if (slideDock != null)
// slideDock.positionWindowFromProperties();
// pannerDock.positionWindowFromProperties();
// MapInspector.positionWindowFromProperties();
// metaDataSearchDock.positionWindowFromProperties();
// interactionToolsDock.positionWindowFromProperties();
// if (contentDock != null)
// contentDock.positionWindowFromProperties();
// mergeMapsDock.positionWindowFromProperties();
// ObjectInspector.positionWindowFromProperties();
// if (outlineDock != null)
// outlineDock.positionWindowFromProperties();
// if (layersDock != null)
// layersDock.positionWindowFromProperties();
// }
// }
protected static void createDockWindows()
{
final tufts.vue.gui.Screen screen = GUI.getScreenForWindow(null);
//=============================================================================
//
// Create all the DockWindow's
//
//=============================================================================
//-----------------------------------------------------------------------------
// Pathways panel
//-----------------------------------------------------------------------------
pathwayPanel = new PathwayPanel(VUE.getDialogParentAsFrame());
if (pathwayDock == null || VUE.isApplet())
pathwayDock = GUI.createDockWindow(VueResources.getString("dockWindow.presentation.title"),
VueResources.getString("dockWindow.Pathways.helpText"), pathwayPanel);
//-----------------------------------------------------------------------------
// Formatting
//-----------------------------------------------------------------------------
//formatDock = null;
floatingZoomPanel = new FloatingZoomPanel();
if (floatingZoomDock == null || VUE.isApplet())
{
floatingZoomDock = GUI.createDockWindow("Floating Zoom",true);
floatingZoomDock.setContent(floatingZoomPanel);
//floatingZoomDock.setFocusable(true); // can grab key events causing MapViewer actions to be disabled
//floatingZoomDock.setSize(new Dimension(280,30));
floatingZoomDock.setHeight(40);
floatingZoomDock.setLocation(screen.left, screen.topIn + 15);
}
//-----------------------------------------------------------------------------
// Panner
//-----------------------------------------------------------------------------
if (pannerDock == null || VUE.isApplet())
{
pannerDock = GUI.createDockWindow(VueResources.getString("dockWindow.panner.title"),
VueResources.getString("dockWindow.Panner.helpText"), new MapPanner());
//pannerDock.getWidgetPanel().setBorder(new javax.swing.border.MatteBorder(5,5,5,5, Color.green));
//pannerDock.getContentPanel().setBorder(new EmptyBorder(1,2,2,2));
//pannerDock.setSize(120,120);
//pannerDock.setSize(112,120);
//pannerDock.setUpperRightCorner(GUI.GScreenWidth, 150);
if (Util.isMacPlatform()) {
// Can't do this on PC as 'x' close button is on right
pannerDock.setMenuActions(new Action[] {
Actions.ZoomFit,
Actions.ZoomActual
});
}
}
//-----------------------------------------------------------------------------
// Map Inspector
//-----------------------------------------------------------------------------
if (MapInspector == null || VUE.isApplet())
{
MapInspector = GUI.createDockWindow(VueResources.getString("mapInspectorTitle"),
VueResources.getString("dockWindow.MapInfo.helpText"));
mapInspectorPanel = new MapInspectorPanel(MapInspector);
// MapInspector.setContent(mapInspectorPanel.getMapInfoStack());
// MapInspector.setHeight(450);
}
//-----------------------------------------------------------------------------
// Meta data Search
//-----------------------------------------------------------------------------
if (metaDataSearchDock == null || VUE.isApplet())
{
metaDataSearchDock = GUI.createDockWindow(VueResources.getString("dockWindow.search.title"),
VueResources.getString("dockWindow.Search.helpText"));
metadataSearchMainPanel = new MetadataSearchMainGUI(metaDataSearchDock);
}
//-----------------------------------------------------------------------------
// Interaction Tools panel
//-----------------------------------------------------------------------------
if (interactionToolsDock == null || VUE.isApplet())
{
interactionToolsDock = GUI.createDockWindow(VueResources.getString("dockWindow.interactionTools.title"),
VueResources.getString("dockWindow.ExplorationTools.helpText"));
interactionToolsPanel = new InteractionTools(interactionToolsDock);
if (depthSelectionControl != null) {
depthSelectionControl.addExpandSelectionListener(interactionToolsPanel);
interactionToolsPanel.addExpandSelectionListener(depthSelectionControl);
}
}
//-----------------------------------------------------------------------------
// Content window
//-----------------------------------------------------------------------------
if (!SKIP_DR && (contentDock == null || VUE.isApplet()))
{
contentDock = GUI.createDockWindow(VueResources.getString("dockWindow.contentPanel.title"),
VueResources.getString("dockWindow.Content.helpText"));
contentPanel = new ContentPanel(contentDock);
contentDock.setSize(300, (int) (screen.height * 0.75));
}
//-----------------------------------------------------------------------------
// Merge Maps
//-----------------------------------------------------------------------------
if (mergeMapsDock == null || VUE.isApplet())
{
mergeMapsDock = GUI.createDockWindow(VueResources.getString("dockWindow.mergemaps.title"),
VueResources.getString("dockWindow.MergeMaps.helpText"));
mergeMapsControlPanel = new MergeMapsControlPanel(mergeMapsDock);
}
//-----------------------------------------------------------------------------
// Object Inspector / Resource Inspector
//-----------------------------------------------------------------------------
//final DockWindow resourceDock = GUI.createDockWindow("Resource Inspector", new ResourcePanel());
inspectorPane = new tufts.vue.ui.InspectorPane();
if (ObjectInspector == null || VUE.isApplet())
{
ObjectInspector = GUI.createDockWindow(VueResources.getString("dockWindow.info.title"),
VueResources.getString("dockWindow.Info.helpText"));
ObjectInspector.setContent(inspectorPane.getWidgetStack());
ObjectInspector.setMenuName(VueResources.getString("dockWindow.infopreview.menu"));
ObjectInspector.setHeight(575);
}
if (layersDock == null || VUE.isApplet())
{
layersDock = GUI.createDockWindow(VueResources.getString("dockWindow.layers.title"),
VueResources.getString("dockWindow.Layers.helpText"), new tufts.vue.ui.LayersUI());
//layersDock.setFocusableWindowState(false);
layersDock.setSize(300,260);
layersDock.setLocation(0, 144);
}
//-----------------------------------------------------------------------------
// Slide Viewer
//-----------------------------------------------------------------------------
if (false && DEBUG.Enabled) {
slideViewer = new tufts.vue.ui.SlideViewer(null);
slideDock = GUI.createDockWindow(slideViewer);
slideDock.setLocation(100,100);
VueAction defSize;
slideDock.setMenuActions(new Action[] {
Actions.ZoomFit,
Actions.ZoomActual,
defSize = new VueAction("1/8 Screen") {
public void act() {
GraphicsConfiguration gc = GUI.getDeviceConfigForWindow(slideDock.window());
Rectangle screen = gc.getBounds();
slideDock.setContentSize(screen.width / 4, screen.height / 4);
}
},
new VueAction("1/4 Screen") {
public void act() {
GraphicsConfiguration gc = GUI.getDeviceConfigForWindow(slideDock.window());
Rectangle screen = gc.getBounds();
slideDock.setContentSize(screen.width / 2, screen.height / 2);
}
},
new VueAction("Maximize") {
public void act() {
slideDock.setBounds(GUI.getMaximumWindowBounds(slideDock.window()));
}
},
});
defSize.act();
}
//-----------------------------------------------------------------------------
// Object Inspector
//-----------------------------------------------------------------------------
//GUI.createDockWindow("Test OI", new ObjectInspectorPanel()).setVisible(true);
/*
ObjectInspector = GUI.createDockWindow(VueResources.getString("objectInspectorTitle"));
ObjectInspectorPanel = new ObjectInspectorPanel();
ModelSelection.addListener(ObjectInspectorPanel);
ObjectInspector.setContent(ObjectInspectorPanel);
*/
//-----------------------------------------------------------------------------
// Pathway Panel
//-----------------------------------------------------------------------------
// todo
//-----------------------------------------------------------------------------
// Outline View
//-----------------------------------------------------------------------------
if (true||!SKIP_DR) {
OutlineViewTree outlineTree = new OutlineViewTree();
JScrollPane outlineScroller = new JScrollPane(outlineTree);
VUE.getSelection().addListener(outlineTree);
//VUE.addActiveMapListener(outlineTree);
VUE.ActiveMapHandler.addListener(outlineTree);
outlineScroller.setPreferredSize(new Dimension(500, 300));
//outlineScroller.setBorder(null); // so DockWindow will add 1 pixel to bottom
if (outlineDock == null || VUE.isApplet())
outlineDock = GUI.createDockWindow(VueResources.getString("dockWindow.outline.title"),
VueResources.getString("dockWindow.Outline.helpText"), outlineScroller);
DataSourceViewer.initUI();
}
//-----------------------------------------------------------------------------
// Formatting
//-----------------------------------------------------------------------------
//formatDock = null;
formattingPanel = new FormatPanel();
if (formatDock == null || VUE.isApplet())
{
formatDock = GUI.createDockWindow(VueResources.getString("dockWindow.format.title"),true,true);
formatDock.setContent(formattingPanel);
}
//formatDock.setFocusable(true);
//-----------------------------------------------------------------------------
VUE.UseLeopardAnchor =
Util.getMacOSXVersion() >= 10.5 // Leopard
&& Util.getMacOSXVersion() <= 10.6 // Snow Leopard
//Util.isMacLeopard()
&& Util.getJavaVersion() >= 1.5f
&& DockWindow.useManagedWindowHacks()
&& !VUE.isApplet();
// 2009-07-23 SMF: Current Mac OS X 1.5 JVM appears to NOT need the Leopard anchor,
// but java 1.6 JVM still does. This just with one test -- we'll need to keep
// an eye on this. SMF 2012-06-16: Lion appears not to need the anchor -- disabled
// for Lion + future versions -- only tested java version "1.6.0_33" 64bit.
if (VUE.UseLeopardAnchor) {
// Workaround for DockWindow's going behind the VUE Window on Leopard bug, especially
// when there's only one DockWindow open. So we literally create a DockWindow
// to always hang around, and just set it off screen.
// See DockWindow.ShowPreviouslyHiddenWindows and FullScreenWindow.FSWindow for more related comments.
// SMF 2008-04-22
JTextArea t = new JTextArea("Do Not Close This Window.\nSee http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6373178");
t.setFont(VueConstants.SmallFont);
t.setEditable(false);
anchor = GUI.createDockWindow(VueResources.getString("dockWindow.vueleopard.title"), t);
if (!DEBUG.DOCK)
GUI.setOffScreen(anchor.window());
anchor.pack();
anchor.setVisible(true);
// NOTE: changes to the graphics configuration appear to cause a
// COMPONENT_MOVED event on this window, which could be a way to
// to auto-detect changes after a "Detect Displays"
}
}
protected static JPanel constructToolPanel(JComponent toolbar)
{
JPanel toolbarPanel = new JPanel();
toolbarPanel.setLayout(new GridBagLayout());
FlowLayout flowLayout = new FlowLayout(FlowLayout.LEFT);
flowLayout.setVgap(0);
//toolbarPanel.
//toolbarPanel.setLayout(flowLayout);
GridBagConstraints gBC = new GridBagConstraints();
gBC.fill = GridBagConstraints.NONE;
gBC.gridx = 0;
gBC.gridy = 0;
gBC.weightx = 0.0;
gBC.insets = new Insets(0, 0, 0, 0);
//add(searchResultTbl, gBC);
//toolbarPanel.add(returnToMapButton,gBC);
toolbarPanel.add(toolbar,gBC);
gBC.fill = GridBagConstraints.NONE;
gBC.gridx = 1;
gBC.gridy = 0;
gBC.weightx = 0.0;
gBC.insets = new Insets(0, 0, 0, 0);
// toolbarPanel.add(new BackwardForwardPanel(), gBC);
returnToMapButton = new JButton(VueResources.getString("returnToMap.label"));
returnToMapButton.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent e)
{
Actions.ReturnToMap.act();
}
});
tufts.vue.VUE.addActiveListener(tufts.vue.LWMap.class,new ActiveListener()
{
public void activeChanged(ActiveEvent e) {
if ((VUE.getActiveViewer()!=null && VUE.getActiveViewer().getFocal()!= null)
&& (VUE.getActiveViewer().getFocal() instanceof LWSlide ||
VUE.getActiveViewer().getFocal() instanceof MasterSlide ||
VUE.getActiveViewer().getFocal() instanceof LWGroup))
{
returnToMapButton.setVisible(true);
}
else
returnToMapButton.setVisible(false);
}
});
returnToMapButton.setVisible(false);
gBC.fill = GridBagConstraints.NONE;
gBC.gridx = 2;
gBC.gridy = 0;
gBC.weightx = 0.0;
gBC.insets = new Insets(5, 0, 5, 0);
toolbarPanel.add(returnToMapButton,gBC);
// Empty JPanel to take up extra horizontal room
gBC.fill = GridBagConstraints.HORIZONTAL;
gBC.gridx = 3;
gBC.gridy = 0;
gBC.weightx = 1.0;
gBC.insets = new Insets(0, 0, 0, 0);
toolbarPanel.add(new JPanel(), gBC);
JPanel searchPnl = new JPanel(new FlowLayout(FlowLayout.LEFT,0,0));
JLabel searchLbl = new JLabel();
JLabel arrowLbl = new JLabel();
JLabel clearLbl = new JLabel();
final JTextField valueTxt = new JTextField();
valueTxt.setMinimumSize(new Dimension(100,23));
valueTxt.setPreferredSize(new Dimension(100,23));
searchLbl.setIcon(tufts.vue.VueResources
.getImageIcon("search.toolbar.find"));
searchLbl.addMouseListener(new MouseAdapter()
{
public void mouseClicked(MouseEvent e)
{
//TO DO
// System.err.println("Clicked on Search Icon:::::");
}
});
arrowLbl.setIcon(tufts.vue.VueResources
.getImageIcon("search.toolbar.arrow"));
arrowLbl.addMouseListener(new MouseAdapter()
{
public void mouseClicked(MouseEvent e)
{
//TO DO
// System.err.println("Clicked on Down Arrow:::::");
}
});
clearLbl.setIcon(tufts.vue.VueResources
.getImageIcon("search.toolbar.clear"));
clearLbl.addMouseListener(new MouseAdapter()
{
public void mouseClicked(MouseEvent e)
{
valueTxt.setText("");
}
});
searchPnl.add(searchLbl);
searchPnl.add(arrowLbl);
valueTxt.setBorder(BorderFactory.createMatteBorder(
1, 0, 1, 0, Color.gray));
searchPnl.add(valueTxt);
searchPnl.add(clearLbl);
searchPnl.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0));
searchPnl.setPreferredSize(new Dimension(155,23));
searchPnl.setSize(new Dimension(155,23));
searchPnl.setMaximumSize(new Dimension(155,23));
// toolbarPanel.add(searchPnl, SwingConstants.LEFT);
// JPanel searchPanel = new JPanel();
// searchPanel.setPreferredSize(new Dimension(200,25));
// searchPanel.setSize(new Dimension(200,25));
// searchPanel.setMaximumSize(new Dimension(200,25));
//
// searchPanel.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0));
//
//searchPanel.setBorder(BorderFactory.createLineBorder(Color.red,1));
//sliderSearchPanel = new JPanel(new FlowLayout());
//framesPerSecond.setMajorTickSpacing(6);
//framesPerSecond.setPaintTicks(true);
final int leftPad = 4;
final int rightPad = 4;
final GridBagConstraints searchPanelGBC =
new GridBagConstraints(0, 0, 1, 1, 0.0, 0.0,
GridBagConstraints.EAST,
GridBagConstraints.NONE,
new Insets(0,leftPad,0,rightPad),
0, 0);
depthSelectionControl = new ExpandSelectionControl();
searchPanel.add(depthSelectionControl, searchPanelGBC);
// This empty border will align search field with the MapViewer below on Mac OS X.
// We'll probably want to tweak this by a few pixels either way for Windows.
//searchPanel.setBorder(GUI.makeSpace(0,0,0,13-rightPad));
if (interactionToolsPanel != null) {
depthSelectionControl.addExpandSelectionListener(interactionToolsPanel);
interactionToolsPanel.addExpandSelectionListener(depthSelectionControl);
}
//final JComponent searchField = new SearchTextField();
final JComponent searchField = mSearchTextField;
// final javax.swing.ImageIcon searchTigerImg = VueResources.getImageIcon("search.tiger.searchicon");
// searchPanelGBC.gridx = 0;
// searchPanelGBC.weightx = 1;
// searchPanelGBC.anchor = GridBagConstraints.WEST;
// searchPanelGBC.fill = GridBagConstraints.NONE;
// searchPanel.add(new JLabel(searchTigerImg));
searchPanelGBC.gridx = 1;
searchPanelGBC.anchor = GridBagConstraints.EAST;
searchPanelGBC.fill = GridBagConstraints.HORIZONTAL;
searchPanelGBC.weightx = 1.0;
searchPanel.add(searchField, searchPanelGBC);
if (!VUE.isApplet()) {
gBC.fill = GridBagConstraints.HORIZONTAL;
gBC.gridx = 4;
gBC.gridy = 0;
gBC.weightx = 1.0;
gBC.insets = new Insets(0, 0, 0, 0);
toolbarPanel.add(searchPanel, gBC);
// Need to add a tiny bit of pad at right:
//if (DEBUG.Enabled) toolbarPanel.add(new JLabel(" "));
}
if (DEBUG.INIT) out("created ToolBar");
return toolbarPanel;
}
static class TabStopDocument extends PlainDocument {
final JTextField textField;
int tabStop;
TabStopDocument(JTextField textField, int tabStop){
this.textField = textField;
this.textField.addFocusListener(new FocusAdapter() {
public void focusLost(FocusEvent e) {
}
public void focusGained(FocusEvent e) {
}
});
this.tabStop = tabStop;
}
private String tabStopString(){
String tabStopString = "";
for(int i=0; i<tabStop; ++i){
tabStopString += " ";
}
return tabStopString;
}
protected void insertUpdate(DefaultDocumentEvent chng, AttributeSet attr) {
int offset = chng.getOffset();
if ( offset <= tabStop){
return;
}
super.insertUpdate(chng, attr);
}
protected void removeUpdate(DefaultDocumentEvent chng) {
int offset = chng.getOffset()+1;
if ( offset <= tabStop){
chng.undo();
return;
}
super.removeUpdate(chng);
}
public void insertString(int offset, String str, AttributeSet attr) throws BadLocationException {
if (str == null)
return;
if (textField.getText().trim().length() == 0 ){
if ( textField.getText().length() == tabStop){
} else {
if (textField.getText().trim().length() == 0 && offset <= tabStop) {
str = tabStopString() + str;
}
}
}
if (textField.getText().trim().length() != 0 && offset <= tabStop) {
return;
}
super.insertString(offset, str, attr);
}
}
static public class SubtleSquareBorder implements Border
{
protected int m_w = 6;
protected int m_h = 6;
protected Color m_topColor = Color.gray;
protected Color m_bottomColor = Color.gray;
protected boolean roundc = false; // Do we want rounded corners on the border?
public SubtleSquareBorder(boolean round_corners)
{
roundc = round_corners;
}
public Insets getBorderInsets(Component c)
{
return new Insets(m_h, m_w, m_h, m_w);
}
public boolean isBorderOpaque()
{
return true;
}
public void paintBorder(Component c, Graphics g, int x, int y, int w, int h)
{
w = w - 3;
h = h - 3;
x ++;
y ++;
// Rounded corners
if(roundc)
{
g.setColor(m_topColor);
g.drawLine(x, y + 2, x, y + h - 2);
g.drawLine(x + 2, y, x + w - 2, y);
g.drawLine(x, y + 2, x + 2, y); // Top left diagonal
g.drawLine(x, y + h - 2, x + 2, y + h); // Bottom left diagonal
g.setColor(m_bottomColor);
g.drawLine(x + w, y + 2, x + w, y + h - 2);
g.drawLine(x + 2, y + h, x + w -2, y + h);
g.drawLine(x + w - 2, y, x + w, y + 2); // Top right diagonal
g.drawLine(x + w, y + h - 2, x + w -2, y + h); // Bottom right diagonal
}
// Square corners
else
{
g.setColor(m_topColor);
g.drawLine(x, y, x, y + h);
g.drawLine(x, y, x + w, y);
g.setColor(m_bottomColor);
g.drawLine(x + w, y, x + w, y + h);
g.drawLine(x, y + h, x + w, y + h);
}
}
}
public static FormatPanel getFormattingPanel()
{
return formattingPanel;
}
public static FloatingZoomPanel getFloatingZoomPanel()
{
return floatingZoomPanel;
}
public static DockWindow getInfoDock()
{
return ObjectInspector;
}
static void _setInfoDock(DockWindow dw) { ObjectInspector = dw; }
public static DockWindow getFloatingZoomDock()
{
return floatingZoomDock;
}
public static DockWindow getPannerDock()
{
return pannerDock;
}
public static DockWindow getOutlineDock()
{
return outlineDock;
}
public static DockWindow getSlideDock()
{
return slideDock;
}
public static MapInspectorPanel getMapInspectorPanel()
{
return mapInspectorPanel;
}
public static DockWindow getMapInfoDock()
{
return MapInspector;
}
public static MetadataSearchMainGUI getMetadataSearchMainPanel()
{
return metadataSearchMainPanel;
}
public static DockWindow getMetadataSearchMainGUI()
{
return metaDataSearchDock;
}
public static InteractionTools getInteractionToolsPanel()
{
return interactionToolsPanel;
}
public static DockWindow getInteractionToolsDock()
{
return interactionToolsDock;
}
public static ContentPanel getContentPanel()
{
return contentPanel;
}
public static DockWindow getContentDock()
{
return contentDock;
}
public static MergeMapsControlPanel getMergeMapsControlPanel()
{
return mergeMapsControlPanel;
}
public static DockWindow getMergeMapsDock()
{
return mergeMapsDock;
}
public static DockWindow getFormatDock()
{
return formatDock;
}
public static DockWindow getAnchorDock()
{
return anchor;
}
public static boolean usingAnchorDock()
{
return VUE.UseLeopardAnchor;
}
public static DockWindow getPresentationDock()
{
return pathwayDock;
}
public static DockWindow getLayersDock()
{
return layersDock;
}
public static PathwayPanel getPathwayPanel()
{
return pathwayPanel;
}
/**
* Get the given windows displayed, but off screen, ready to be moved
* into position.
*/
private static void prepareForTopDockDisplay(final DockWindow[] preShown)
{
if (DEBUG.INIT || DEBUG.DOCK) Util.printStackTrace("\n\n***ROLLING UP OFFSCREEN");
// get the peer's created so we can turn off their shadow if need be
for (int i = 0; i < preShown.length; i++) {
DockWindow dw = preShown[i];
if (dw == null)
continue;
GUI.setOffScreen(dw.window());
dw.setDockTemporary(DockWindow.getTopDock());
dw.showRolledUp();
}
}
/**
* This used to be called "positionForDocking" when we we're using the idea of ui DockRegions.
*/
public static void assignDefaultPositions(DockWindow[] preShown) {
// Set last in preSown at the right, moving back up list
// setting them to the left of that, and then set first in
// preShown at left edge of screen
if (DEBUG.DOCK) Log.debug("assignDefaultPositions " + Arrays.asList(preShown));
if (DEBUG.INIT || (DEBUG.DOCK && DEBUG.META)) Util.printStackTrace("\n\nSTARTING PLACEMENT");
final tufts.vue.gui.Screen screen = GUI.getScreenForWindow(null);
int top = screen.topIn;
if (ToolbarAtTopScreen)
top += DockWindow.ToolbarHeight;
else
top += 152; // todo: tweak for PC
boolean squeezeDown = screen.width < 1200;
boolean didSqueeze = false;
int nextLayout = preShown.length - 1;
if (squeezeDown) {
// Swap last two, so "last" is the one pushed down
DockWindow tmp = preShown[nextLayout];
preShown[nextLayout] = preShown[nextLayout - 1];
preShown[nextLayout - 1] = tmp;
}
DockWindow toRightDW = null;
toRightDW = preShown[nextLayout];
toRightDW.setUpperRightCorner(screen.width, top);
if (squeezeDown)
toRightDW.setHeight(toRightDW.getHeight() / 2);
DockWindow curDW = null;
while (--nextLayout > 0) {
curDW = preShown[nextLayout];
if (curDW == null) {
// This will happen if we run with -nodr
Log.debug("Missing DockWindow at index " + nextLayout);
continue;
}
if (squeezeDown && !didSqueeze) {
didSqueeze = true;
toRightDW.addChild(curDW);
} else {
int spot = screen.right;
if (nextLayout %3 != 0) {
spot = toRightDW.getX();
} else
top +=50;
curDW.setUpperRightCorner(spot, top);
toRightDW = curDW;
// int spot=GUI.GScreenWidth;
// if (nextLayout %3 != 0) {
// spot = toRightDW.getX();
// } else
// top +=50;
// curDW.setUpperRightCorner(spot, top);
// toRightDW = curDW;
}
}
if (preShown.length > 1)
preShown[0].setLocation(0, top);
DockWindow.assignAllDockRegions();
}
private static ActionListener SplitPaneRightButtonOneTouchActionHandler = null;
public static boolean toggleSplitScreen()
{
// Oops -- no good for updating the action state -- will need
// to extract both the AbstractButtons for the expand & collapse
// (currently we only grab the ActionListener for the collapse button),
// and manually add an action listener to them in our ToggleSplitScreen
// action. Or, maybe easier, would be to monitor the display events
// on the right MapViewer component or PropertyChangeEvents on mViewerSplit.
if (mViewerSplit.getDividerLocation() >= mViewerSplit.getMaximumDividerLocation()) {
// open to split:
mViewerSplit.setDividerLocation(0.50D);
return true;
} else {
// close the split:
SplitPaneRightButtonOneTouchActionHandler.actionPerformed(null);
return false;
}
}
private static JSplitPane buildSplitPane(Component leftComponent, Component rightComponent)
{
JSplitPane split;
if (Util.getJavaVersion() < 1.5f) {
split = new JSplitPane();
} else {
// Only appears to happen on the Mac? But even if we're
// running with Metal Look and Feel??
split = new JSplitPane() {
// This JSplitPane hack is dependent on the UI implementation, but it only
// uses the cross platform parts: see bottom of this method for more info.
Container divider;
public void XsetUI(javax.swing.plaf.SplitPaneUI newUI) {
Util.printStackTrace("setUI: " + newUI);
super.setUI(newUI);
}
@Override
protected void addImpl(Component c, Object constraints, int index) {
//out("splitPane.addImpl: index=" + index + " constraints=" + constraints + " " + GUI.name(c));
if (c instanceof javax.swing.plaf.basic.BasicSplitPaneDivider) {
//Util.printStackTrace("addImpl: divider is " + c);
divider = (Container) c;
}
super.addImpl(c, constraints, index);
}
@Override
public void addNotify() {
//Util.printStackTrace("splitPane.addNotify");
super.addNotify();
try {
// Util.printStackTrace("addNotify");
//AbstractButton jumpLeft = (AbstractButton) divider.getComponent(0);
AbstractButton jumpRight = (AbstractButton) divider.getComponent(1);
//System.err.println("child0 " + jumpLeft);
//System.err.println("child1 " + jumpRight);
//System.err.println(Arrays.asList(jumpLeft.getActionListeners()));
//System.err.println(Arrays.asList(jumpRight.getActionListeners()));
// todo: as long as we're grabbing this out, add a short-cut key
// to activate it: the arrow buttons are so damn tiny!
SplitPaneRightButtonOneTouchActionHandler
= jumpRight.getActionListeners()[0];
// BTW: can't call action listener now: must wait till after validation
} catch (Throwable t) {
Util.printStackTrace(t);
}
}
};
}
split.setName("splitPane");
split.setResizeWeight(0.5d);
split.setOneTouchExpandable(true);
split.setRightComponent(rightComponent);
// NOTE: set left component AFTER set right component -- the LAST set left/right
// call determines the default focus component It needs to be the LEFT
// component as the right one isn't even visible at startup.
split.setLeftComponent(leftComponent);
split.addPropertyChangeListener(new PropertyChangeListener() {
public void propertyChange(PropertyChangeEvent e) {
//System.out.println("VS " + e);
if (!e.getPropertyName().equals("dividerLocation"))
return;
if (DEBUG.TOOL || DEBUG.INIT || DEBUG.FOCUS)
out("split.propertyChange[" + e.getPropertyName() + "] "
+ "\n\tnew=" + e.getNewValue().getClass().getName()
+ " " + e.getNewValue()
+ "\n\told=" + e.getOldValue()
+ "\n\tsrc=" + GUI.name(e.getSource())
);
//Util.printStackTrace();
MapViewer leftViewer = null;
MapViewer rightViewer = null;
if (mMapTabsLeft != null)
leftViewer = mMapTabsLeft.getSelectedViewer();
if (mMapTabsRight != null)
rightViewer = mMapTabsRight.getSelectedViewer();
if (multipleMapsVisible()) {
/*
// should be handled by MapVewer.reshape
if (leftViewer != null)
leftViewer.fireViewerEvent(MapViewerEvent.PAN);
if (rightViewer != null)
rightViewer.fireViewerEvent(MapViewerEvent.PAN);
*/
// why did we ever need to handle this condition? was this just-in-case code?
if (leftViewer != null) leftViewer.setVisible(true);
if (rightViewer != null) rightViewer.setVisible(true);
} else {
if (leftViewer != null && leftViewer != getActiveViewer()) {
if (DEBUG.TOOL || DEBUG.FOCUS)
out("split: active viewer: " + getActiveViewer()
+ " focus going to " + leftViewer);
leftViewer.requestFocus();
if (rightViewer != null)
//rightViewer.fireViewerEvent(MapViewerEvent.HIDDEN);
rightViewer.setVisible(false);
}
}
}});
return split;
}
/*
private static void XbuildToolbar(DockWindow toolbarDock, JPanel toolPanel) {
if (JIDE_TEST) {
/* JIDE ENABLE
frame.getDockableBarManager().addDockableBar(new VueToolBar());
frame.getDockableBarManager().setShowInitial(false);
frame.getDockableBarManager().resetToDefault();
*
} else if (true||VUE.TUFTS) {
//toolBarPanel = new JPanel();
//toolBarPanel.add(tbc.getToolbar());
if (toolbarDock == null)
//ApplicationFrame.addComp(tbc.getToolbar(), BorderLayout.NORTH);
ApplicationFrame.addComp(toolPanel, BorderLayout.NORTH);
} else {
//JDialog.setDefaultLookAndFeelDecorated(false);
JPanel toolBarPanel = null;
toolBarPanel = new JPanel(new BorderLayout());
//toolBarPanel.add(tbc.getToolbar(), BorderLayout.NORTH);
JPanel floatingToolbarContainer = new JPanel(new BorderLayout());
//JPanel floatingToolbarContainer = new com.jidesoft.action.DockableBarDockableHolderPanel(frame);
//floatingToolbarContainer.setPreferredSize(new Dimension(500,50));
//floatingToolbarContainer.setMinimumSize(new Dimension(500,5));
floatingToolbarContainer.setBackground(Color.orange);
VueToolBar vueToolBar = new VueToolBar();
floatingToolbarContainer.add(vueToolBar, BorderLayout.PAGE_START);
//toolBarPanel.add(new VueToolBar(), BorderLayout.SOUTH);
if (false) {
// Yes: drop-downs work in a JToolBar (note that our MenuButtons
// that are rounded become square tho)
JToolBar tb = new JToolBar();
tb.add(tbc.getToolbar());
toolBarPanel.add(tb);
} else {
toolBarPanel.add(tbc.getToolbar(), BorderLayout.NORTH);
}
toolBarPanel.add(floatingToolbarContainer, BorderLayout.SOUTH);
ApplicationFrame.addComp(toolBarPanel, BorderLayout.NORTH);
////frame.getDockableBarManager().addDockableBar(vueToolBar);
}
}
*/
// public static int openMapCount() {
// return ActiveMapHandler.instanceCount();
// //return mMapTabsLeft == null ? 0 : mMapTabsLeft.getTabCount();
// }
public static boolean multipleMapsVisible() {
if (mViewerSplit == null)
return false;
// TODO: this is no longer a reliable method of determining this in java 1.5
int dl = mViewerSplit.getDividerLocation();
return dl >= mViewerSplit.getMinimumDividerLocation()
&& dl <= mViewerSplit.getMaximumDividerLocation();
}
public static JTabbedPane getTabbedPane() {
return getLeftTabbedPane();
}
public static MapTabbedPane getLeftTabbedPane() {
return mMapTabsLeft;
}
public static MapTabbedPane getRightTabbedPane() {
return mMapTabsRight;
}
public static Collection<LWMap> getAllMaps() {
if (mMapTabsLeft != null)
return mMapTabsLeft.getAllMapsBag();
else
return Collections.EMPTY_LIST;
}
public static void layoutAllMaps(Object layoutKey) {
for (LWMap map : getAllMaps())
map.layoutAll(layoutKey);
}
public static boolean isActiveViewerOnLeft() {
final MapViewer activeViewer = ActiveViewerHandler.getActive();
return activeViewer == null || activeViewer.getName().startsWith("*");
}
public static boolean isActiveViewerOnRight() {
final MapViewer activeViewer = ActiveViewerHandler.getActive();
return activeViewer != null && activeViewer.getName().equals("right");
}
public static UndoManager getUndoManager() {
// todo: eventually, way may want to ask the active viewer for
// it's undo manager, e.g. -- if we want the slide viewer to
// have it's own undo queue.
LWMap map = getActiveMap();
if (map != null)
return map.getUndoManager();
else
return null;
}
public static void markUndo() {
markUndo(null);
}
/** mark prior change(s) with the given undo name */
public static void markUndo(String name) {
LWMap map = getActiveMap();
if (map != null) {
UndoManager um = map.getUndoManager();
if (um != null) {
if (name != null)
um.markChangesAsUndo(name);
else
um.mark();
}
}
}
/**
* If any open maps have been modified and not saved, run
* dialogs to determine what to do.
* @return true if we're cleared to exit, false if we want to abort the exit
*/
public static boolean isOkayToExit() {
//update the windows properties
try {
saveAllWindowProperties();
} catch (Throwable t) {
Log.error("saving window props", t);
}
if (mMapTabsLeft == null) // so debug harnesses can quit (no maps displayed)
return true;
// TODO: use active map instances
int tabs = mMapTabsLeft.getTabCount();
// LWMap ensureChecked = getActiveMap(); // in case of full-screen
// disabled indexing
/**
for (int i = 0; i < tabs; i++) {
final LWMap map = mMapTabsLeft.getMapAt(i);
if (VUE.getIndex() != null) {
System.out.println("indexing map "+map.getLabel()+ " index size="+VUE.getIndex().size());
try {
VUE.getIndex().index(map);
} catch (Throwable t) {
Util.printStackTrace(t, "Exception indexing duing exit: " + map);
}
}
// if (map == ensureChecked)
// ensureChecked = null;
if (!askSaveIfModified(mMapTabsLeft.getMapAt(i)))
return false;
}
**/
/**
if (getIndex() != null) {
try {
VUE.getIndex().write(new FileWriter(VueUtil. getDefaultUserFolder()+File.separator+VueResources.getString("rdf.index.file")));
System.out.println("Writing index to"+VueUtil. getDefaultUserFolder()+File.separator+VueResources.getString("rdf.index.file"));
} catch (Throwable t) {
System.out.println("Exception attempting to save index " +t);
t.printStackTrace();
}
}
**/
LWMap ensureChecked = getActiveMap(); // in case of full-screen
for (int i = 0; i < tabs; i++)
{
final LWMap map = mMapTabsLeft.getMapAt(i);
if (map == ensureChecked)
ensureChecked = null;
if (!askSaveIfModified(mMapTabsLeft.getMapAt(i)))
return false;
}
if (ensureChecked != null)
{
if (!askSaveIfModified(ensureChecked))
return false;
}
return true;
// if (ensureChecked != null && !askSaveIfModified(ensureChecked))
// return false;
// else
// return true;
}
private static boolean askIfRevertOK(LWMap map)
{
final Object[] defaultOrderButtons = { VueResources.getString("optiondialog.revertlastsave.yes"),VueResources.getString("optiondialog.revertlastsave.cancel")};
if (!map.isModified())
return true;
// todo: won't need this if full screen is child of root frame
if (inNativeFullScreen())
toggleFullScreen();
Component c = VUE.getDialogParent();
if (VUE.getDialogParent() != null)
{
//Get the screen size
Toolkit toolkit = Toolkit.getDefaultToolkit();
Dimension screenSize = toolkit.getScreenSize();
Point p = c.getLocationOnScreen();
if ((p.x + c.getWidth() > screenSize.width) ||
(p.y + c.getHeight() > screenSize.height))
{
c = null;
}
}
int response = VueUtil.option
(c,
VueResources.getString("optiondialog.revertlastsave.message"),
VueResources.getString("optiondialog.revertlastsave.title"),
JOptionPane.YES_NO_OPTION,
JOptionPane.PLAIN_MESSAGE,
defaultOrderButtons,
VueResources.getString("optiondialog.revertlastsave.cancel")
);
// If they change focus to another button, then hit "return"
// (v.s. "space" for kbd button press), do action of button
// that had focus instead of always save?
if (response == JOptionPane.YES_OPTION) { // Save
return true;
} else if (response == JOptionPane.NO_OPTION) { // Don't Save
// don't save -- just close
return false;
} else // anything else (Cancel or dialog window closed)
return false;
}
/*
* Returns true if either they save it or say go ahead and close w/out saving.
*/
static boolean askSaveIfModified(LWMap map) {
//final Object[] defaultOrderButtons = { "Save", "Don't Save", "Cancel"};
final Object[] defaultOrderButtons = { VueResources.getString("optiondialog.savechages.dontsave"),VueResources.getString("optiondialog.revertlastsave.cancel"),VueResources.getString("optiondialog.savechages.save")};
final Object[] macOrderButtons = { VueResources.getString("optiondialog.savechages.save"),VueResources.getString("optiondialog.revertlastsave.cancel"),VueResources.getString("optiondialog.savechages.dontsave")};
// oddly, mac aqua is reversing order of these buttons
//final Object[] macAquaOrderButtons = { "Cancel", "Don't Save", "Save" };
if (!map.isModified() || !map.hasContent())
return true;
// todo: won't need this if full screen is child of root frame
if (inNativeFullScreen())
toggleFullScreen();
Component c = VUE.getDialogParent();
if (VUE.getDialogParent() != null)
{
//Get the screen size
Toolkit toolkit = Toolkit.getDefaultToolkit();
Dimension screenSize = toolkit.getScreenSize();
Point p = c.getLocationOnScreen();
if ((p.x + c.getWidth() > screenSize.width) ||
(p.y + c.getHeight() > screenSize.height))
{
c = null;
}
}
final String debug;
if (DEBUG.EVENTS || DEBUG.UNDO)
debug = "\n[modifications="+map.getModCount()+"]";
else
debug = "";
int response = VueUtil.option
(c,
VueResources.getString("optiondialog.savechages.message")
+ " '" + map.getLabel() + "'?"
+ debug,
VueResources.getString("optiondialog.savechages.title"),
JOptionPane.YES_NO_CANCEL_OPTION,
JOptionPane.PLAIN_MESSAGE,
Util.isMacPlatform() ? macOrderButtons : defaultOrderButtons,
VueResources.getString("optiondialog.savechages.save")
);
if (!Util.isMacPlatform()) {
switch (response) {
case 0: response = 1; break;
case 1: response = 2; break;
case 2: response = 0; break;
}
} else {
switch (response) {
case 0: response = 0; break;
case 1: response = 2; break;
case 2: response = 1; break;
}
}
// If they change focus to another button, then hit "return"
// (v.s. "space" for kbd button press), do action of button
// that had focus instead of always save?
if (response == JOptionPane.YES_OPTION) { // Save
return SaveAction.saveMap(map);
} else if (response == JOptionPane.NO_OPTION) { // Don't Save
// don't save -- just close
return true;
} else // anything else (Cancel or dialog window closed)
return false;
}
public static void closeMap(LWMap map) {
closeMap(map,false);
}
public static void closeMap(LWMap map, boolean reverting) {
if (!reverting)
{
if (askSaveIfModified(map)) {
try{
mMapTabsLeft.closeMap(map);
}
catch(ArrayIndexOutOfBoundsException abe){}
try
{
if (mMapTabsRight != null)
mMapTabsRight.closeMap(map);
}
catch(ArrayIndexOutOfBoundsException abe){}
}
}
else
{
if (askIfRevertOK(map)) {
mMapTabsLeft.closeMap(map);
if (mMapTabsRight != null)
mMapTabsRight.closeMap(map);
}
}
if (mMapTabsRight !=null)
{
int selectedIndex = mMapTabsRight.getTabCount();
if(selectedIndex>0){
setMapActionsEnabled(true);
}else{
setMapActionsEnabled(false);
}
}
else if (mMapTabsLeft == null)
{
setMapActionsEnabled(false);
}
else
{
int selectedIndex = mMapTabsLeft.getTabCount();
if(selectedIndex>0){
setMapActionsEnabled(true);
}else{
setMapActionsEnabled(false);
}
}
// if(selectedIndex>0){
// VueMenuBar.RootMenuBar.saveAction.setEnabled(true);
// VueMenuBar.RootMenuBar.saveAsAction.setEnabled(true);
// VueMenuBar.RootMenuBar.publishMenu.setEnabled(true);
// Actions.Revert.setEnabled(true);
// }else{
// VueMenuBar.RootMenuBar.saveAction.setEnabled(false);
// VueMenuBar.RootMenuBar.saveAsAction.setEnabled(false);
// VueMenuBar.RootMenuBar.publishMenu.setEnabled(false);
// Actions.Revert.setEnabled(false);
// }
}
/**
* If we already have open a map tied to the given file, display it.
* Otherwise, open it anew and display it.
*/
public static void displayMap(File file) {
if (VUE.isStartupUnderway() || DEBUG.INIT || DEBUG.IO) Log.info("displayMap " + Util.tags(file));
// Call initDataSources again just in case a user can make it to the file
// open-recent menu before the data sources finish loading on the remaining
// "main" thread. If it's still running, we'll just block until it's done, as
// this method is synchronized.
initDataSources();
if (file == null)
return;
if (VUE.isApplet())
{
if ( (getActiveMap() != null) &&
!getActiveMap().hasContent() &&
getActiveMap().getFile() == null)
{
try
{
closeMap(getActiveMap());
}
catch(ArrayIndexOutOfBoundsException abe)
{
abe.printStackTrace();
}
}
}
else
{
/*
* If there is 1 map open, and it has no content and hasn't been saved yet close it.
* requested in vue-520
*/
if (isActiveViewerOnLeft())
{
if ((mMapTabsLeft != null) &&
mMapTabsLeft.getTabCount() == 1 &&
(getActiveMap() != null) &&
!getActiveMap().hasContent() &&
getActiveMap().getFile() == null)
{
try
{
closeMap(getActiveMap());
}
catch(ArrayIndexOutOfBoundsException abe)
{
abe.printStackTrace();
}
}
} else
{
if ((mMapTabsRight != null) &&
mMapTabsRight.getTabCount() == 1 &&
(getActiveMap() != null) &&
!getActiveMap().hasContent() &&
getActiveMap().getFile() == null)
closeMap(getActiveMap());
}
}
for (int i = 0; i < mMapTabsLeft.getTabCount(); i++) {
LWMap map = mMapTabsLeft.getMapAt(i);
if (map == null)
continue;
File existingFile = map.getFile();
if (existingFile != null && existingFile.equals(file)) {
if (DEBUG.Enabled) out("displayMap found existing open map " + map + " matching file " + file);
if (isActiveViewerOnLeft())
mMapTabsLeft.setSelectedIndex(i);
else
mMapTabsRight.setSelectedIndex(i);
return;
}
}
// for (LWMap map : ActiveMapHandler.getAllInstances()) {
// File existingFile = map.getFile();
// if (existingFile != null && existingFile.equals(file)) {
// if (DEBUG.Enabled) out("displayMap found existing open map " + map + " matching file " + file);
// ActiveMapHandler.setActive(file, map);
// // TODO: sanity check this... (oh, and I supposed we can use the tab panes again... don't need active instances tracking!)
// //mMapTabsLeft.setSelectedIndex(i);
// return;
// }
// }
final RecentlyOpenedFilesManager rofm = RecentlyOpenedFilesManager.getInstance();
rofm.updateRecentlyOpenedFiles(file.getAbsolutePath());
VUE.activateWaitCursor();
LWMap loadedMap = null;
boolean alerted = false;
try {
loadedMap = OpenAction.loadMap(file.getAbsolutePath());
alerted = true; // OpenAction.loadMap now always alerts
if (loadedMap != null)
VUE.displayMap(loadedMap);
VUE.getMetadataSearchMainPanel().fillSavedSearch();
} catch (Throwable t) {
Util.printStackTrace(t, "failed to load map[" + file + "]");
VUE.clearWaitCursor();
alerted = true;
VueUtil.alert(VueResources.getString("dialog.failedtoloadmap.message")+" " + file + " \n"
+ (t.getCause() == null ? t : t.getCause()),
VueResources.getString("dialog.failedtoloadmap.message")+" "+ file);
} finally {
VUE.clearWaitCursor();
}
if (loadedMap == null && !alerted)
VueUtil.alert(VueResources.getString("dialog.failedtoloadmap.message")+" "+ file + " \n", VueResources.getString("dialog.failedtoloadmap.message") + file);
// if(getActiveMap()==null){
// VueMenuBar.RootMenuBar.saveAction.setEnabled(false);
// VueMenuBar.RootMenuBar.saveAsAction.setEnabled(false);
// VueMenuBar.RootMenuBar.publishMenu.setEnabled(false);
// Actions.Revert.setEnabled(false);
// }else{
// VueMenuBar.RootMenuBar.saveAction.setEnabled(true);
// VueMenuBar.RootMenuBar.saveAsAction.setEnabled(true);
// VueMenuBar.RootMenuBar.publishMenu.setEnabled(true);
// Actions.Revert.setEnabled(true);
// }
}
/**
* If we already have open a map tied to the given file, display it.
* Otherwise, open it anew and display it.
*/
public static void displayMap(java.net.URL url) {
if (VUE.isStartupUnderway() || DEBUG.INIT || DEBUG.IO) Log.info("displayMap " + Util.tags(url));
// Call initDataSources again just in case a user can make it to the file
// open-recent menu before the data sources finish loading on the remaining
// "main" thread. If it's still running, we'll just block until it's done, as
// this method is synchronized.
initDataSources();
if (url == null)
return;
if (VUE.isApplet())
{
if ( (getActiveMap() != null) &&
!getActiveMap().hasContent() &&
getActiveMap().getFile() == null)
{
try
{
closeMap(getActiveMap());
}
catch(ArrayIndexOutOfBoundsException abe)
{
abe.printStackTrace();
}
}
}
else
{
/*
* If there is 1 map open, and it has no content and hasn't been saved yet close it.
* requested in vue-520
*/
if (isActiveViewerOnLeft())
{
if ((mMapTabsLeft != null) &&
mMapTabsLeft.getTabCount() == 1 &&
(getActiveMap() != null) &&
!getActiveMap().hasContent() &&
getActiveMap().getFile() == null)
{
try
{
closeMap(getActiveMap());
}
catch(ArrayIndexOutOfBoundsException abe)
{
abe.printStackTrace();
}
}
} else
{
if ((mMapTabsRight != null) &&
mMapTabsRight.getTabCount() == 1 &&
(getActiveMap() != null) &&
!getActiveMap().hasContent() &&
getActiveMap().getFile() == null)
closeMap(getActiveMap());
}
}
/*
for (int i = 0; i < mMapTabsLeft.getTabCount(); i++) {
LWMap map = mMapTabsLeft.getMapAt(i);
if (map == null)
continue;
File existingFile = map.getFile();
if (existingFile != null && existingFile.equals(file)) {
if (DEBUG.Enabled) out("displayMap found existing open map " + map + " matching file " + file);
if (isActiveViewerOnLeft())
mMapTabsLeft.setSelectedIndex(i);
else
mMapTabsRight.setSelectedIndex(i);
return;
}
}
*/
VUE.activateWaitCursor();
LWMap loadedMap = null;
boolean alerted = false;
try {
loadedMap = OpenAction.loadMap(url);
if (loadedMap != null)
VUE.displayMap(loadedMap);
} catch (Throwable t) {
Util.printStackTrace(t, "failed to load map[" + url + "]");
VUE.clearWaitCursor();
alerted = true;
VueUtil.alert(VueResources.getString("dialog.failedtoloadmap.message")+" " + url + " \n"
+ (t.getCause() == null ? t : t.getCause()),
VueResources.getString("dialog.failedtoloadmap.title")+" "+ url);
} finally {
VUE.clearWaitCursor();
}
if (loadedMap == null && !alerted)
VueUtil.alert(VueResources.getString("dialog.failedtoloadmap.message")+" " + url + " \n", VueResources.getString("dialog.failedtoloadmap.title")+" " + url);
// if(getActiveMap()==null){
// VueMenuBar.RootMenuBar.saveAction.setEnabled(false);
// VueMenuBar.RootMenuBar.saveAsAction.setEnabled(false);
// VueMenuBar.RootMenuBar.publishMenu.setEnabled(false);
// Actions.Revert.setEnabled(false);
// }else{
// VueMenuBar.RootMenuBar.saveAction.setEnabled(true);
// VueMenuBar.RootMenuBar.saveAsAction.setEnabled(true);
// VueMenuBar.RootMenuBar.publishMenu.setEnabled(true);
// Actions.Revert.setEnabled(true);
// }
}
/**
* Create a new viewer and display the given map in it.
*/
public static MapViewer displayMap(LWMap pMap) {
if (VUE.isStartupUnderway() || DEBUG.Enabled) out("displayMap " + pMap);
diagPush("displayMap");
if (DEBUG.INIT) out(pMap.toString());
MapViewer leftViewer = null;
MapViewer rightViewer = null;
for (int i = 0; i < mMapTabsLeft.getTabCount(); i++) {
LWMap map = mMapTabsLeft.getMapAt(i);
if (map == null)
continue;
File existingFile = map.getFile();
if (existingFile != null && existingFile.equals(pMap.getFile())) {
Util.printStackTrace("warning: found open map with same file: " + map);
//Log.error("** found open map with same file! " + map);
// TODO: pop dialog asking to revert existing if there any changes.
//break;
}
}
if (leftViewer == null) {
leftViewer = new MapViewer(pMap, "*LEFT");
rightViewer = new MapViewer(pMap, "right");
// Start them both off unfocusable, so we get no
// focus transfers until we're ready to decide what
// wants to get the focus.
leftViewer.setFocusable(false);
rightViewer.setFocusable(false);
// if (rightViewer != null && isActiveViewerOnLeft()) {
// // so doesn't grab focus till we're ready
// // NOTE: grabVueApplicationFocus restore's focusability
// // when called directly -- which is why it must
// // be called directly to ensure focus grabs
// // in right viewers.
// rightViewer.setFocusable(false);
// }
if (DEBUG.FOCUS) {
out("currently active viewer: " + getActiveViewer());
out("created new left viewer: " + leftViewer);
}
mMapTabsLeft.addViewer(leftViewer);
if (mMapTabsRight != null)
mMapTabsRight.addViewer(rightViewer);
}
if (isActiveViewerOnLeft()) {
mMapTabsLeft.setSelectedComponent(leftViewer);
} else if (mMapTabsRight != null){
mMapTabsRight.setSelectedComponent(rightViewer);
}
diagPop();
if (VUE.isApplet())
{
if (LWPathway.isShowingSlideIcons())
LWPathway.toggleSlideIcons();
}
return leftViewer;
}
/**
* @return the root VUE Frame used for parenting dialogs -- currently always NULL do to java
* bugs w/dialogs
*/
// WARNING: opening a dialog appears to cause our full-screen
// window as root parent of everything hack to fail and
// permit DockWindow's to start going over it -- thus we must
// use "null" as a parent. TODO: we'll prob need to do this
// for all dialogs... We can still manually center the
// window if we like...
// CORRECTION: popping this at ALL seems to do it
public static Component getDialogParent() {
final Component dialogParent;
// any dialog parent at all in 1.4.2 causes the full-screen
// window to go behind the DockWindow's
if (Util.getJavaVersion() >= 1.5f)
dialogParent = getActiveViewer();
else
dialogParent = null;
if (DEBUG.FOCUS) out("getDialogParent: " + dialogParent);
return dialogParent;
/*
// this is not helping for preving dialogs from screwing us up and
// sending them behind the full-screen window as soon as the dialog pops
if (true)
// this will put dialogs at screen bottom when it's off-screen
return GUI.getFullScreenWindow();
else
return null;
*/
}
public static Frame getDialogParentAsFrame() {
Frame frame;
if (getDialogParent() instanceof Frame) {
frame = (Frame) getDialogParent();
} else {
// JOptionPane's will take any Component as a parent, but they just do a
// search up for the root frame as the parent of the JDialog. But if we
// return our real root frame here, to be used with a raw JDialog, things
// behave differently: it allows the dialog to go behind. Don't know what
// JOptionPane is causing to happen differently. E.g., the "are you sure"
// before quit dialog works fine and doesn't go behind it's parent, but raw
// JDialogs constructed with an invisible parent CAN go behind... Oh, wait a
// sec.. what if we use our DockWindow parent...
// We're in the java 1.5 case here: if it's parented to the application
// frame, it lets DockWindow's go behind full-screen (yet JOptionPane
// created dialogs don't). If it's parented to a hidden frame, the dialog
// itself can go behind either full-screen or ApplicationFrame.
// So for now, in 1.5, Dialog's wanting a frame get this special hidden
// frame, and the FocusManager forces them alwaysOnTop when they're shown.
frame = GUI.getHiddenDialogParentFrame();
//frame = DockWindow.getHiddenFrame();
}
if (DEBUG.FOCUS) out("getDialogParentAsFrame: " + frame);
return frame;
}
public static VueMenuBar getJMenuBar() {
return VueMenuBar.RootMenuBar;
//return (VueMenuBar) ((VueFrame)getRootWindow()).getJMenuBar();
}
/** Return the main VUE window. Usually == getRoowWindow, unless we're
* using a special root window for parenting the tool windows.
*/
// todo: wanted package private
public static Window getMainWindow() {
return VUE.ApplicationFrame;
}
/** return the root VUE window, mainly for those who'd like it to be their parent */
public static Window getRootWindow() {
if (!VUE.isApplet())
return VUE.ApplicationFrame;
else
{
Frame[] frames = JFrame.getFrames();
//System.out.println("FRAME LENGTH " + frames.length);
JApplet app = VueApplet.getInstance();
Container c = app.getParent();
while (!(c instanceof Window))
{
c = c.getParent();
}
return (Window)c;
}
/*
if (true) {
return VUE.frame;
} else {
if (rootWindow == null) {
//rootWindow = makeRootFrame();
rootWindow = makeRootWindow();
}
return rootWindow;
}
*/
}
/*
private static Window makeRootWindow() {
if (true||DEBUG.INIT) out("making the ROOT WINDOW with parent " + VUE.frame);
Window w = new ToolWindow("Vue Root", VUE.frame);
//w.show();
return w;
}
*/
/*
private static boolean makingRootFrame = false;
private static Frame makeRootFrame() {
if (makingRootFrame) {
new Throwable("RECURSIVE MAKE ROOT WINDOW CALL").printStackTrace();
return null;
}
makingRootFrame = true;
JFrame f = null;
try {
if (DEBUG.INIT) out("creating the ROOT WINDOW");
f = new JFrame("Vue Root");
if (VueUtil.isMacPlatform() && useMacLAF) {
JMenuBar menu = new VueMenuBar();
f.setJMenuBar(menu);
}
f.show();
//rootFrame = createFrame();
} finally {
makingRootFrame = false;
}
return f;
}
*/
/* This mehtod checks if later version of VUE is available. In case later version
* is available it prompts user to download it.
*/
public static void checkLatestVersion() {
Log.info("Checking for latest version of VUE");
try {
URL url = new URL(VueResources.getString("vue.release.url"));
XPathFactory factory=XPathFactory.newInstance();
XPath xPath=factory.newXPath();
if (DEBUG.Enabled) Log.debug("opening " + url);
InputSource inputSource = new InputSource(url.openStream());
XPathExpression xSession= xPath.compile("/current_release/version/text()");
String version = xSession.evaluate(inputSource);
if (DEBUG.Enabled) Log.debug("got current version id [" + version + "]");
final String currentVersion = VueResources.getString("vue.version").trim();
final String newVersion = version.trim();
if (!isHigherVersion(currentVersion, newVersion))
{
//final ShowAgainDialog sad = new ShowAgainDialog(VUE.getApplicationFrame(),"checkForNewVersion2","New Release Available","Remind me later",(String)null);
final ShowAgainDialog sad = new ShowAgainDialog(VUE.getApplicationFrame(),"checkForNewVersion2","New Release Available","Get latest version","Close Window");
JPanel panel = new JPanel(new GridLayout(1,1));
/*JLabel vLabel = new JLabel("<html>A newer version of VUE is available ("
+ newVersion
+ ") <font color=\"#20316A\"><u>Get the latest version</u></font></html", JLabel.LEFT);*/
JLabel vLabel = new JLabel("<html>"+VueResources.getString("jlabel.newversion") +" ("
+ newVersion
+ ") <font color=\"#20316A\"></html", JLabel.LEFT);
if(Util.isMacPlatform()){
panel.setPreferredSize(new Dimension(425,25));
panel.setSize(new Dimension(425,25));
panel.setMinimumSize(new Dimension(425,25));
}else{
panel.setPreferredSize(new Dimension(425,25));
}
//vLabel.setBorder(BorderFactory.createLineBorder(Color.red, 1));
//panel.setBorder(BorderFactory.createLineBorder(Color.green, 1));
panel.add(vLabel, FlowLayout.LEFT);
sad.setContentPanel(panel);
vLabel.addMouseListener(new javax.swing.event.MouseInputAdapter() {
public void mouseClicked(MouseEvent evt) {
try {
VueUtil.openURL(VueResources.getString("vue.download.url"));
sad.setVisible(false);
sad.dispose();
}catch (Throwable t) { t.printStackTrace();}
}
});
VueUtil.centerOnScreen(sad);
if (sad.showAgain())
{
sad.setVisible(true);
sad.setVisible(false);
sad.dispose();
}
}
}catch(Throwable t) {
Log.error("Error Checking latest VUE release:", t);
}
}
// check if v1 > v2
private static boolean isHigherVersion(String v1,String v2) {
HashMap<String,Integer> priorityMap = new HashMap<String,Integer>();
priorityMap.put("alpha", 0);
priorityMap.put("beta", 1);
priorityMap.put("preview",2);
priorityMap.put("gold", 3);
// if current version is same as latest version
if(v1.equalsIgnoreCase(v2)) {
return true;
}
String[] v1Parts = v1.split("\\D+");
String[] v2Parts = v2.split("\\D+");
//check the first number in version
if(v1Parts.length>0 && v2Parts.length > 0) {
if(Integer.parseInt(v1Parts[0])> Integer.parseInt(v2Parts[0])) return true;
else if(Integer.parseInt(v1Parts[0])< Integer.parseInt(v2Parts[0])) return false;
else {
System.out.println("0\t"+v1Parts[0]+"\t"+v2Parts[0]+"\t"+v1Parts.length+"\t"+v2Parts.length);
if(v1Parts.length>1 && v2Parts.length > 1) {
if(Integer.parseInt(v1Parts[1])> Integer.parseInt(v2Parts[1])) return true;
else if(Integer.parseInt(v1Parts[1])< Integer.parseInt(v2Parts[1])) return false;
else {
System.out.println("1\t"+v1Parts[1]+"\t"+v2Parts[1]);
String p1 = getPriority(v1, priorityMap);
String p2 = getPriority(v2,priorityMap);
if(priorityMap.get(p1)> priorityMap.get(p2)) return true;
else if(priorityMap.get(p1)< priorityMap.get(p2)) return false;
else {
System.out.println("P\t"+p1+"\t"+p2);
if(v1Parts.length>2 ) {
if(v2Parts.length == 2) return true;
else {
System.out.println("2\t"+v1Parts[2]+"\t"+v2Parts[2]);
if(Integer.parseInt(v1Parts[2])> Integer.parseInt(v2Parts[2])) return true;
else if(Integer.parseInt(v1Parts[2])< Integer.parseInt(v2Parts[2])) return false;
else {
if(v1Parts.length>3 ) {
if(v2Parts.length == 3) return true;
else {
System.out.println("1\t"+v1Parts[3]+"\t"+v2Parts[3]);
if(Integer.parseInt(v1Parts[3])>= Integer.parseInt(v2Parts[3])) return true;
}
}
}
}
}
}
}
}
}
}
return false;
}
private static String getPriority(String version,HashMap<String, Integer> priorityMap) {
String priority = "gold";
for(String key: priorityMap.keySet()) {
if(version.toLowerCase().contains(key)) {
return key;
}
}
return priority;
}
public static String getName() {
if (NAME == null)
NAME = VueResources.getString("application.name");
return NAME;
}
/** return the root VUE application frame (where the documents are) */
public static JFrame getApplicationFrame() {
//if (getRootWindow() instanceof Frame)
// return (Frame) getRootWindow();
//else
return VUE.ApplicationFrame;
}
/** @return a new JWindow, parented to the root VUE window */
public static JWindow createWindow()
{
return new JWindow(getRootWindow());
}
/* @return a new ToolWindow, parented to getRootWindow()
public static ToolWindow createToolWindow(String title) {
return createToolWindow(title, null);
}
/** @return a new ToolWindow, containing the given component, parented to getRootWindow()
public static ToolWindow createToolWindow(String title, JComponent component) {
return createToolWindow(title, component, false);
}
/* @return a new ToolWindow, containing the given component, parented to getRootWindow()
private static ToolWindow createToolWindow(String title, JComponent component, boolean palette) {
//Window parent = getRootFrame();
Window parent = getRootWindow();
if (DEBUG.INIT) out("creating ToolWindow " + title + " with parent " + parent);
final ToolWindow w;
if (palette) {
w = new ToolWindow(title, parent, false);
} else {
w = new ToolWindow(title, parent, true);
if (component != null)
w.addTool(component);
}
/*
// ToolWindows not set yet...
if (VueUtil.isMacPlatform() && useMacLAF && w instanceof JFrame)
((JFrame)w).setJMenuBar(new VUE.VueMenuBar());
*
return w;
}
/** @return a new ToolWindow styled as a ToolPalette
public static ToolWindow createToolPalette(String title) {
return createToolWindow(title, null, true);
}
*/
/** call the given runnable after all pending AWT events are completed */
public static void invokeAfterAWT(Runnable runnable) {
java.awt.EventQueue.invokeLater(runnable);
}
/** @return true if in any full screen mode */
public static boolean inFullScreen() {
return FullScreen.inFullScreen();
}
/** @return true if in working full screen mode (menu still at top, DockWindow's can be seen at the same time) */
public static boolean inWorkingFullScreen() {
return FullScreen.inWorkingFullScreen();
}
/** @return true if in total full screen mode (no menu, and on mac, if any window even tries to display, we hang...) */
public static boolean inNativeFullScreen() {
return FullScreen.inNativeFullScreen();
}
public static void toggleFullScreen() {
toggleFullScreen(false);
}
public static void toggleFullScreen(boolean goNative) {
toggleFullScreen(goNative, false);
}
public static void toggleFullScreen(boolean goNative,boolean showFloatingToolbar)
{
FullScreen.toggleFullScreen(goNative);
if (showFloatingToolbar)
{
DockWindow.flickerAnchorDock();
floatingZoomDock.setVisible(inWorkingFullScreen());
}
}
static void installExampleNodes(LWMap map) {
map.setFillColor(new Color(255,255,220));
/*
map.addLWC(new LWNode("Oval", 0)).setFillColor(Color.red);
map.addLWC(new LWNode("Circle", 1)).setFillColor(Color.green);
map.addLWC(new LWNode("Square", 2)).setFillColor(Color.orange);
map.addLWC(new LWNode("Rectangle", 3)).setFillColor(Color.blue);
map.addLWC(new LWNode("Rounded Rectangle", 4)).setFillColor(Color.yellow);
LWNode triangle = new LWNode("Triangle", 5);
triangle.setAutoSized(false);
triangle.setSize(60,60);
triangle.setFillColor(Color.orange);
map.addLWC(triangle);
//map.addLWC(new LWNode("Triangle", 5)).setFillColor(Color.orange);
map.addLWC(new LWNode("Diamond", 6)).setFillColor(Color.yellow);
*/
map.addNode(new LWNode("One"));
map.addNode(new LWNode("Two"));
map.addNode(new LWNode("Three"));
map.addNode(new LWNode("Four"));
map.addNode(new LWNode("WWWWWWWWWWWWWWWWWWWW"));
map.addNode(new LWNode("iiiiiiiiiiiiiiiiiiii"));
map.addNode(NodeModeTool.createTextNode("jumping"));
// Experiment in internal actions -- only works
// partially here because they're all auto sized
// based on text, and since haven't been painted yet,
// and so don't really know their size.
// Addendum: with new TextBox, above no longer true.
LWSelection s = new LWSelection();
s.setTo(map.getChildIterator());
Actions.MakeColumn.act(s);
s.clear(); // clear isSelected bits
}
public static void installExampleMap(LWMap map) {
/*
* create some test nodes & links
*/
//map.addLWC(new LWImage(new MapResource("/Users/sfraize/Desktop/Test Image.jpg"))).setLocation(350, 90);
LWNode n1 = new LWNode("Google", URLResource.create("http://www.google.com/"));
LWNode n2 = new LWNode("Program\nFiles", URLResource.create("C:\\Program Files"));
LWNode n3 = new LWNode("readme.txt", URLResource.create("readme.txt"));
LWNode n4 = new LWNode("Slash", URLResource.create("file:///"));
LWNode n5 = new LWNode("Program\nFiles 2", URLResource.create("\\Program Files"));
n1.setLocation(100, 30);
n2.setLocation(100, 100);
n3.setLocation(50, 180);
n4.setLocation(200, 180);
n5.setLocation(150, 200);
n4.setNotes("I am a note.");
map.addNode(n1);
map.addNode(n2);
map.addNode(n3);
map.addNode(n4);
map.addNode(n5);
LWLink k1 = new LWLink(n1, n2);
LWLink k2 = new LWLink(n2, n3);
LWLink k3 = new LWLink(n2, n4);
k1.setLabel("Link label");
k1.setNotes("I am link note");
k3.setControlCount(1);
k2.setControlCount(2);
map.addLink(k1);
map.addLink(k2);
map.addLink(k3);
// LWSlide slide = LWSlide.create();
// slide.setLocation(300,100);
// map.addLWC(slide);
// create test pathways
if (false) {
// FYI: I dno't think PathwayTableModel will
// detect this creation, so can't use this
// for full testing (e.g., note setting, undo, etc)
LWPathway p = new LWPathway("Test Pathway");
p.add(n1);
p.add(n2);
p.add(n3);
map.addPathway(p);
}
map.markAsSaved();
/*else if(map.getLabel().equals("Test Nodes")){
}/*else if(map.getLabel().equals("Test Nodes")){
LWPathway p2 = new LWPathway("Pathway 2");
p2.setComment("A comment.");
LinkedList anotherList = new LinkedList();
anotherList.add(n3);
anotherList.add(n4);
anotherList.add(n2);
anotherList.add(k2);
anotherList.add(k3);
p2.setElementList(anotherList);
map.addPathway(p2);
map.markAsSaved();
}*/
}
static protected void out(Object o) {
//System.out.println(o == null ? "null" : o.toString());
Log.info(o == null ? "null" : o.toString());
}
public static Color getPresentationBackground() {
final Color defaultColor= new Color(32,32,32);
if (VUE.getActiveMap() != null)
return VUE.getActiveMap().getPresentationBackgroundValue();
else
return defaultColor;
}
public static DRBrowser getDRBrowser() {
return contentPanel.getDRBrowser();
}
}