/*
* 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);

//         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;
    }
    */


    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();
	}
}
