org.trianacode.gui.hci.ApplicationFrame.java Source code

Java tutorial

Introduction

Here is the source code for org.trianacode.gui.hci.ApplicationFrame.java

Source

/*
 * The University of Wales, Cardiff Triana Project Software License (Based
 * on the Apache Software License Version 1.1)
 *
 * Copyright (c) 2007 University of Wales, Cardiff. All rights reserved.
 *
 * Redistribution and use of the software in source and binary forms, with
 * or without modification, are permitted provided that the following
 * conditions are met:
 *
 * 1.  Redistributions of source code must retain the above copyright
 *     notice, this list of conditions and the following disclaimer.
 *
 * 2.  Redistributions in binary form must reproduce the above copyright
 *     notice, this list of conditions and the following disclaimer in the
 *     documentation and/or other materials provided with the distribution.
 *
 * 3. The end-user documentation included with the redistribution, if any,
 *    must include the following acknowledgment: "This product includes
 *    software developed by the University of Wales, Cardiff for the Triana
 *    Project (http://www.trianacode.org)." Alternately, this
 *    acknowledgment may appear in the software itself, if and wherever
 *    such third-party acknowledgments normally appear.
 *
 * 4. The names "Triana" and "University of Wales, Cardiff" must not be
 *    used to endorse or promote products derived from this software
 *    without prior written permission. For written permission, please
 *    contact triana@trianacode.org.
 *
 * 5. Products derived from this software may not be called "Triana," nor
 *    may Triana appear in their name, without prior written permission of
 *    the University of Wales, Cardiff.
 *
 * 6. This software may not be sold, used or incorporated into any product
 *    for sale to third parties.
 *
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN
 * NO EVENT SHALL UNIVERSITY OF WALES, CARDIFF OR ITS CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
 * THE POSSIBILITY OF SUCH DAMAGE.
 *
 * ------------------------------------------------------------------------
 *
 * This software consists of voluntary contributions made by many
 * individuals on behalf of the Triana Project. For more information on the
 * Triana Project, please see. http://www.trianacode.org.
 *
 * This license is based on the BSD license as adopted by the Apache
 * Foundation and is governed by the laws of England and Wales.
 *
 */
package org.trianacode.gui.hci;

import org.apache.commons.logging.Log;
import org.trianacode.TrianaInstance;
import org.trianacode.TrianaInstanceProgressListener;
import org.trianacode.config.Locations;
import org.trianacode.config.cl.ArgumentParser;
import org.trianacode.config.cl.TrianaOptions;
import org.trianacode.enactment.addon.CLIaddon;
import org.trianacode.enactment.logging.Loggers;
import org.trianacode.gui.action.*;
import org.trianacode.gui.action.clipboard.CopyAction;
import org.trianacode.gui.action.clipboard.CutAction;
import org.trianacode.gui.action.clipboard.PasteAction;
import org.trianacode.gui.action.files.*;
import org.trianacode.gui.action.taskgraph.*;
import org.trianacode.gui.action.tools.*;
import org.trianacode.gui.components.hidden.HiddenComponentModel;
import org.trianacode.gui.components.map.MapComponentModel;
import org.trianacode.gui.components.map.MapLocationComponentModel;
import org.trianacode.gui.components.script.ScriptColorModel;
import org.trianacode.gui.components.script.ScriptComponentModel;
import org.trianacode.gui.components.text.TextToolComponentModel;
import org.trianacode.gui.components.triana.TrianaColorModel;
import org.trianacode.gui.components.triana.TrianaComponentModel;
import org.trianacode.gui.desktop.DesktopView;
import org.trianacode.gui.desktop.DesktopViewController;
import org.trianacode.gui.desktop.DesktopViewListener;
import org.trianacode.gui.desktop.DesktopViewManager;
import org.trianacode.gui.extensions.*;
import org.trianacode.gui.hci.color.ColorManager;
import org.trianacode.gui.hci.color.ColorTable;
import org.trianacode.gui.hci.color.RegisterableColorModel;
import org.trianacode.gui.hci.tools.*;
import org.trianacode.gui.main.TaskGraphPanel;
import org.trianacode.gui.panels.MsgBoxErrorListener;
import org.trianacode.gui.panels.ParameterPanelManager;
import org.trianacode.gui.service.LocalServer;
import org.trianacode.gui.service.WorkflowActionManager;
import org.trianacode.gui.util.Env;
import org.trianacode.gui.windows.ErrorDialog;
import org.trianacode.gui.windows.SplashScreen;
import org.trianacode.gui.windows.TrianaWindow;
import org.trianacode.taskgraph.Task;
import org.trianacode.taskgraph.TaskException;
import org.trianacode.taskgraph.TaskGraph;
import org.trianacode.taskgraph.TaskGraphManager;
import org.trianacode.taskgraph.constants.HiddenToolConstants;
import org.trianacode.taskgraph.constants.MapConstants;
import org.trianacode.taskgraph.constants.ScriptConstants;
import org.trianacode.taskgraph.constants.TextToolConstants;
import org.trianacode.taskgraph.event.*;
import org.trianacode.taskgraph.ser.XMLReader;
import org.trianacode.taskgraph.service.LocalDeployAssistant;
import org.trianacode.taskgraph.service.TrianaClient;
import org.trianacode.taskgraph.tool.Tool;
import org.trianacode.taskgraph.tool.ToolTable;

import javax.swing.*;
import javax.swing.event.TreeModelEvent;
import javax.swing.event.TreeModelListener;
import javax.swing.plaf.FontUIResource;
import javax.swing.plaf.metal.MetalLookAndFeel;
import javax.swing.plaf.metal.OceanTheme;
import javax.swing.tree.TreePath;
import java.awt.*;
import java.awt.event.ComponentEvent;
import java.awt.event.ComponentListener;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
import java.io.File;
import java.io.FileReader;
import java.util.*;
import java.util.List;

/**
 * @author Mathew Shields & Ian Taylor
 * @version $Revision: 4051 $
 */
public class ApplicationFrame extends TrianaWindow implements TaskListener, TaskGraphListener, ToolSelectionHandler,
        SelectionManager, TreeModelListener, ComponentListener, LocalDeployAssistant, FocusListener,
        TrianaInstanceProgressListener, DesktopViewListener {

    private static Log log = Loggers.LOGGER;

    /**
     * tool tip delays
     */
    private static int TOOL_TIP_SHOW_DELAY = ToolTipManager.sharedInstance().getInitialDelay();
    private static int TOOL_TIP_HIDE_DELAY = Integer.MAX_VALUE;

    /**
     * the current loaded tools
     */
    private ToolTable tools;

    /**
     * The manager responsible for instantiating parameter panels
     */
    private ParameterPanelManager panelmanager = new ParameterPanelManager();

    /**
     * a hashtable of the Triana Clients for each taskgraph
     */
    private Hashtable clienttable = new Hashtable();

    /**
     * an array list of all the top-level main trianas
     */
    private ArrayList<TaskGraphPanel> parents = new ArrayList<TaskGraphPanel>();

    /**
     * the main workspace containing the tools panel and the main trianas
     */
    private Container workspace = new JPanel(new BorderLayout());

    /**
     * The leaf listener that handles all mouse events on desktop panes.
     */
    private LeafListener leaflistener;

    // a lookup table for MainTriana object and their containers
    //private static Hashtable taskGraphConts = new Hashtable();

    private TaskGraphFileHandler taskGraphFileHandler = null;
    private JTree toolboxTree;

    /**
     * a thread that monitors which tools are broken
     */
    private BrokenToolMonitor toolmonitor;

    /**
     * the currently selected component (tool tree/main triana)
     */
    private Object selected;

    TrianaInstance engine;
    SplashScreen splash = new SplashScreen();

    /**
     * Initialise the application
     */
    public static ApplicationFrame initTriana(String args[]) {
        // todo: this is crap, use andrew's UI stuff
        // Andrew Sept 2010: Done - 6 years on... :-)
        UIDefaults uiDefaults = UIManager.getDefaults();
        Object font = ((FontUIResource) uiDefaults.get("TextArea.font")).deriveFont((float) 11);

        Enumeration enumeration = uiDefaults.keys();
        while (enumeration.hasMoreElements()) {
            Object key = enumeration.nextElement();

            if (key.toString().endsWith("font")) {
                uiDefaults.put(key, font);
            }
        }

        String myOSName = Locations.os();
        if (myOSName.equals("windows")) {
            try {
                UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
            } catch (Exception e) {
            }
        } else {
            if (!myOSName.equals("osx")) {
                try {
                    MetalLookAndFeel.setCurrentTheme(new OceanTheme());
                    UIManager.setLookAndFeel(new MetalLookAndFeel());
                } catch (Exception e) {
                }
            }

        }

        ApplicationFrame app = new ApplicationFrame("Triana");
        app.init(args);

        return app;
    }

    /**
     * Constructor for the ApplicationFrame object
     */
    private ApplicationFrame(String title) {
        super(title);
    }

    public void showCurrentProgress(String progress) {
        splash.setSplashProgress(progress);
    }

    public void setProgressSteps(int stepsInInitialization) {
        splash.showSplashScreen(stepsInInitialization);
    }

    private void init(String args[]) {

        try {
            log.info("Initialising");

            engine = new TrianaInstance(args);
            engine.addExtensionClasses(Extension.class, CLIaddon.class, TaskGraphExporterInterface.class,
                    TaskGraphImporterInterface.class, ToolImporterInterface.class,
                    RegisterableToolComponentModel.class, RegisterableColorModel.class);
            engine.init(this, false);
            engine.getErrorTracker().addErrorListener(new MsgBoxErrorListener());
            tools = engine.getToolTable();

            initTools();
            initActionTable();
            initWorkflowVerifiers();
            initMonitors();
            initExtensions();

            Env.initConfig(true);

            initLayout();
            // init extensions that require some gui stuff to be there first
            initPostLayoutExtensions();

            Env.readStateFiles();

            initWindow(super.getTitle());

            List<String> workflows = null;
            if (args.length > 0) {
                ArgumentParser parser = new ArgumentParser(args);
                parser.parse();
                workflows = TrianaOptions.getOptionValues(parser, TrianaOptions.WORKFLOW_OPTION);
            }
            loadTools(workflows);
            if (workflows == null || workflows.size() == 0) {
                addParentTaskGraphPanel();
            }

            splash.setSplashProgress("");
            splash.hideSplashScreen();

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private void loadTools(final List<String> workflows) {
        new Thread() {
            public void run() {
                engine.resolve();
                if (workflows != null) {
                    for (String workflow : workflows) {
                        try {
                            File f = new File(workflow);
                            XMLReader reader = new XMLReader(new FileReader(f));
                            Tool t = reader.readComponent(engine.getProperties());
                            if (t instanceof TaskGraph) {
                                addParentTaskGraphPanel((TaskGraph) t);
                            }
                        } catch (Exception e) {
                            log.error(e);
                        }
                    }
                }
            }
        }.start();

    }

    public TrianaInstance getEngine() {
        return engine;
    }

    /**
     * @return the cuurently loaded tool table
     */
    ToolTable getTools() {
        return tools;
    }

    /**
     * @return the MenuBar for the main application
     */
    public TrianaMainMenu getTrianaMenuBar() {
        return (TrianaMainMenu) trianaMenuBar;
    }

    /**
     * @return the handler for opening/saving taskgraphs
     */
    public TaskGraphFileHandler getTaskGraphFileHandler() {
        return taskGraphFileHandler;
    }

    /**
     * Initialises the main window
     */
    private void initWindow(String title) {
        setName(title);

        setSize(Env.getWindowSize());
        setLocation(Env.getWindowPosition());

        addComponentListener(this);

        setVisible(true);
        getTrianaMenuBar().updateRecentMenu();
    }

    /**
     * Initialise the actions in the ActionTable
     */
    private void initActionTable() {
        log.debug("Init");
        ActionTable.putAction(ActionTable.NEW_ACTION, new NewAction());
        ActionTable.putAction(ActionTable.OPEN_ACTION, new OpenAction(this));
        ActionTable.putAction(ActionTable.OPEN_FILE_ACTION, new OpenAction(this, OpenAction.FILE_ONLY_MODE));
        ActionTable.putAction(ActionTable.SAVE_ACTION, new SaveAction(this, tools));
        ActionTable.putAction(ActionTable.SAVE_AS_ACTION, new SaveAsAction(this, tools));
        ActionTable.putAction(ActionTable.CLOSE_ACTION, new CloseAction());
        ActionTable.putAction(ActionTable.IMPORT_ACTION, new ImportAction());
        ActionTable.putAction(ActionTable.EXPORT_ACTION, new ExportAction());
        ActionTable.putAction(ActionTable.PRINT_ACTION, new PrintAction(this));
        ActionTable.putAction(ActionTable.FIND_ACTION, new FindAction(this));
        ActionTable.putAction(ActionTable.HELP_ACTION, new HelpAction(this));
        ActionTable.putAction(ActionTable.RENDER_ACTION, new RenderAction(this));

        ActionTable.putAction(ActionTable.CUT_ACTION, new CutAction(this));
        ActionTable.putAction(ActionTable.COPY_ACTION, new CopyAction(this));
        ActionTable.putAction(ActionTable.PASTE_ACTION, new PasteAction(this));
        ActionTable.putAction(ActionTable.PASTE_INTO_ACTION, new PasteIntoAction(this, tools));
        ActionTable.putAction(ActionTable.DELETE_ACTION, new DeleteAction(this));
        ActionTable.putAction(ActionTable.DELETE_REFERENCES_ACTION, new DeleteRefsAction(this));
        ActionTable.putAction(ActionTable.RENAME_ACTION, new RenameAction(this));

        ActionTable.putAction(ActionTable.PROPERTIES_ACTION, new PropertiesAction(this));
        ActionTable.putAction(ActionTable.CONTROL_PROERTIES_ACTION, new ControlPropertiesAction(this));
        ActionTable.putAction(ActionTable.NODE_EDITOR_ACTION, new NodeEditorAction(this));
        ActionTable.putAction(ActionTable.HISTORY_TRACKING_ACTION, new HistoryTrackingAction(this));

        ActionTable.putAction(ActionTable.GROUP_ACTION, new GroupAction(this, tools));
        ActionTable.putAction(ActionTable.UNGROUP_ACTION, new UnGroupAction(this));
        ActionTable.putAction(ActionTable.SELECT_ALL_ACTION, new SelectAllAction(this));
        ActionTable.putAction(ActionTable.CLEAR_ACTION, new ClearAction(this));
        ActionTable.putAction(ActionTable.ORGANIZE_ACTION, new OrganizeAction(this));

        ActionTable.putAction(ActionTable.ZOOMIN_ACTION, new ZoomAction(this));
        ActionTable.putAction(ActionTable.ZOOMOUT_ACTION, new ZoomOutAction(this));

        ActionTable.putAction(ActionTable.RUN_ACTION, new RunAction(this));
        ActionTable.putAction(ActionTable.PAUSE_ACTION, new PauseAction(this));
        ActionTable.putAction(ActionTable.RESET_ACTION, new ResetAction(this));

        ActionTable.putAction(ActionTable.COMPILE_ACTION, new CompileAction(this, tools));

        ActionTable.putAction(ActionTable.RUN_SCRIPT_ACTION, new RunScriptAction(this, tools));

        ActionTable.putAction(ActionTable.DEC_INPUT_NODES_ACTION, new DecInNodeAction(this));
        ActionTable.putAction(ActionTable.DEC_OUTPUT_NODES_ACTION, new DecOutNodeAction(this));
        ActionTable.putAction(ActionTable.INC_INPUT_NODES_ACTION, new IncInNodeAction(this));
        ActionTable.putAction(ActionTable.INC_OUTPUT_NODES_ACTION, new IncOutNodeAction(this));
        ActionTable.putAction(ActionTable.ADD_TRIGGER_NODE_ACTION, new AddTriggerAction(this));
        ActionTable.putAction(ActionTable.REMOVE_TRIGGER_NODE_ACTION, new RemoveTriggerAction(this));
        ActionTable.putAction(ActionTable.TOGGLE_ERROR_NODE_ACTION, new ToggerErrorAction(this));

        ActionTable.putAction(Actions.TABBED_DESKTOP_VIEW, new TabbedDesktopAction(this));
        ActionTable.putAction(Actions.VIRTUAL_DESKTOP_VIEW, new VirtualDesktopAction(this));
        ActionTable.putAction(Actions.EDIT_TOOLBOXES, new EditToolboxesAction(this.tools));
        ActionTable.putAction(Actions.OPTIONS, new OptionsAction(this.tools));
        ActionTable.putAction(Actions.CREATE_IMAGE, new ImageAction());
        ActionTable.putAction(Actions.SAVE_AS_FILE, new SaveAsFileAction(this, tools));

    }

    /**
     * Initialise the actions in the ActionTable
     */
    private void initWorkflowVerifiers() {
        log.debug("Init");
        WorkflowActionManager.registerWorkflowAction(new TrianaWorkflowVerifier());
        //WorkflowActionManager.registerWorkflowAction(new ProtoServiceWorkflowVerifier());
    }

    /**
     * Initialise the application monitors
     */
    private void initMonitors() {
        log.debug("Init");
        //new ServicesMonitor();
    }

    /**
     * Discover and initialize the extension classes and populate the extension manager
     */
    private void initExtensions() {
        Set<Object> en = engine.getExtensions(Extension.class);
        //        System.out.println("Extensions : " + Arrays.toString(en.toArray()));
        for (Object o : en) {
            Extension e = (Extension) o;
            e.init(this);
            ExtensionManager.registerExtension(e);
        }
        en = engine.getExtensions(TaskGraphExporterInterface.class);
        for (Object o : en) {
            TaskGraphExporterInterface e = (TaskGraphExporterInterface) o;
            log.debug("**** Found an exporter : " + e.toString());
            ImportExportRegistry.addExporter(e);
        }
        en = engine.getExtensions(TaskGraphImporterInterface.class);
        for (Object o : en) {
            TaskGraphImporterInterface e = (TaskGraphImporterInterface) o;
            log.debug("**** Found an importer : " + e.toString());

            ImportExportRegistry.addImporter(e);
        }
        en = engine.getExtensions(ToolImporterInterface.class);
        for (Object o : en) {
            ToolImporterInterface e = (ToolImporterInterface) o;
            ImportExportRegistry.addToolImporter(e);
        }
        en = engine.getExtensions(RegisterableColorModel.class);
        for (Object o : en) {
            RegisterableColorModel e = (RegisterableColorModel) o;
            String[] names = e.getRegistrationNames();
            for (String name : names) {
                ColorManager.registerColorModel(name, e);
                Color c = e.getDefaultColorForRegistrationName(name);
                if (c != null) {
                    ColorTable.instance().initDefaultColor(e, name, c);
                }
            }
        }
    }

    private void initPostLayoutExtensions() {
        TaskGraphView defaultview = TaskGraphViewManager.getDefaultTaskgraphView();
        Set<Object> en = engine.getExtensions(RegisterableToolComponentModel.class);
        for (Object o : en) {
            RegisterableToolComponentModel e = (RegisterableToolComponentModel) o;
            defaultview.registerToolModel(e.getRegistrationString(), e);
        }
    }

    /**
     * Initialises the panels in the main window
     */
    private void initLayout() {

        GUIEnv.setApplicationFrame(this);
        ColorManager.setDefaultColorModel(new TrianaColorModel());
        ColorManager.registerColorModel(ScriptConstants.SCRIPT_RENDERING_HINT, new ScriptColorModel());

        // do this after all other color loading
        ColorTable.instance().loadUserPrefs();

        TaskGraphView defaultview = new TaskGraphView("Default View");
        TrianaComponentModel compmodel = new TrianaComponentModel(tools, this, this);

        defaultview.setDefaultToolModel(compmodel);
        defaultview.setDefaultOpenGroupModel(compmodel);
        defaultview.registerToolModel(ScriptConstants.SCRIPT_RENDERING_HINT, new ScriptComponentModel());
        defaultview.registerToolModel(TextToolConstants.TEXT_TOOL_RENDERING_HINT, new TextToolComponentModel());
        defaultview.registerToolModel(HiddenToolConstants.HIDDEN_RENDERING_HINT, new HiddenComponentModel());

        TaskGraphView mapview = new TaskGraphView("Map View", defaultview);
        mapview.registerOpenGroupModel(MapConstants.MAP_RENDERING_HINT, new MapComponentModel());
        mapview.registerToolModel(MapConstants.MAP_LOCATION_RENDERING_HINT, new MapLocationComponentModel());

        TaskGraphViewManager.setDefaultTaskGraphView(defaultview);
        TaskGraphViewManager.registerTaskGraphView(MapConstants.MAP_RENDERING_HINT, mapview);

        taskGraphFileHandler = new TaskGraphFileHandler();

        trianaMenuBar = new TrianaMainMenu(this, tools);
        this.setJMenuBar(trianaMenuBar);

        TrianaShutdownHook shutDownHook = new TrianaShutdownHook();
        Runtime.getRuntime().addShutdownHook(shutDownHook);
        getDesktopViewManager().addDesktopViewListener(this);
        this.workspace.add(getDesktopViewManager().getWorkspace(), BorderLayout.CENTER);

        ((TrianaMainMenu) trianaMenuBar).addHelp();

        ToolTreeModel treemodel = new ToolTreeModel(tools);
        toolboxTree = new JTree(treemodel);
        toolboxTree.addFocusListener(this);
        toolboxTree.setCellRenderer(new TrianaTreeRenderer());
        toolmonitor.setTree(toolboxTree);

        treemodel.addTreeModelListener(this);

        ToolTipManager.sharedInstance().registerComponent(toolboxTree);
        ToolTipManager.sharedInstance().setInitialDelay(TOOL_TIP_SHOW_DELAY);
        ToolTipManager.sharedInstance().setDismissDelay(TOOL_TIP_HIDE_DELAY);

        //set up key maps
        MainTrianaKeyMapFactory keymaps = new MainTrianaKeyMapFactory(this, ActionDisplayOptions.DISPLAY_NAME);
        InputMap inputMap = keymaps.getInputMap();
        inputMap.setParent(this.getRootPane().getInputMap());
        this.getRootPane().setInputMap(JComponent.WHEN_FOCUSED, inputMap);
        ActionMap actMap = keymaps.getActionMap();
        actMap.setParent(this.getRootPane().getActionMap());
        this.getRootPane().setActionMap(actMap);

        leaflistener = new LeafListener(toolboxTree, this, tools);

        keymaps = new MainTrianaKeyMapFactory(leaflistener, ActionDisplayOptions.DISPLAY_NAME);
        inputMap = keymaps.getInputMap();
        inputMap.setParent(toolboxTree.getInputMap());
        toolboxTree.setInputMap(JComponent.WHEN_FOCUSED, inputMap);
        actMap = keymaps.getActionMap();
        actMap.setParent(toolboxTree.getActionMap());
        toolboxTree.setActionMap(actMap);

        toolboxTree.addMouseListener(leaflistener);
        toolboxTree.addMouseMotionListener(leaflistener);
        //toolboxTree.setRootVisible(false);
        JPanel toolPanel = new JPanel(new BorderLayout());

        SearchToolBar searchtoolbar = new SearchToolBar("Search", toolboxTree, treemodel);
        searchtoolbar.setFloatable(false);

        toolPanel.add(searchtoolbar, BorderLayout.NORTH);
        JScrollPane scroll = new JScrollPane(toolboxTree);

        toolPanel.add(scroll, BorderLayout.CENTER);

        JSplitPane verticalSplit = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, toolPanel, workspace);

        TrianaToolBar toolbar = new TrianaToolBar("Main ToolBar", this);
        TrianaUnitToolBar unitToolbar = new TrianaUnitToolBar("Unit ToolBar");
        toolbar.setRollover(true);
        unitToolbar.setRollover(true);

        JPanel innerpanel = new JPanel();
        innerpanel.setLayout(new BoxLayout(innerpanel, BoxLayout.X_AXIS));
        innerpanel.add(toolbar);
        innerpanel.add(Box.createHorizontalStrut(10));
        innerpanel.add(unitToolbar);
        innerpanel.add(Box.createHorizontalGlue());

        JPanel outerpanel = new JPanel(new BorderLayout());

        outerpanel.add(innerpanel, BorderLayout.NORTH);
        outerpanel.add(verticalSplit, BorderLayout.CENTER);
        getContentPane().add(outerpanel);
    }

    /**
     * Initialises the tool table
     */
    public void initTools() {
        log.debug("Init");

        toolmonitor = new BrokenToolMonitor(tools);
        toolmonitor.start();

        /*
        TODO
        ServiceManager.registerImporter(new WSServiceImporter(tools));
        ServiceManager.registerDeployer(new GAPServiceDeployer(GAPPeerTypes.WEB_SERVICES, tools, new WSDeployAssistant(), this));
        ServiceManager.registerImporter(new P2PSServiceImporter(tools));
        ServiceManager.registerDeployer(new GAPServiceDeployer(GAPPeerTypes.P2PS, tools, new P2PSDeployAssistant(), this));
        */
    }

    /**
     * Adds a listener to be notified when the tool selection changes
     */
    public void addToolSelectionListener(ToolSelectionListener listener) {
    }

    /**
     * Removes a listener from being notified when the tool selection changes
     */
    public void removeToolSelectionListener(ToolSelectionListener listener) {
    }

    /**
     * Close the selected MainTriana window.
     * <p/>
     * TODO add checks for saved or not, finalise and tidy up graph etc.
     */
    void closeSelectedWindow() {
        closeTaskGraphPanel(getDesktopViewManager().getDesktopViewFor(getSelectedTaskGraphPanel()));
    }

    /**
     * Closes the specified main triana and cleans-up the taskgraph if required
     */
    public void closeTaskGraphPanel(DesktopView panel) {
        disposeTaskGraphPanel(panel);
    }

    public DesktopView getDesktopView(TaskGraphPanel panel) {
        return getDesktopViewManager().getDesktopViewFor(panel);
    }

    /**
     * @return an array all the taskgraph panels that are open within the application
     */
    public TaskGraphPanel[] getTaskGraphPanels() {
        DesktopView[] views = getDesktopViewManager().getViews();
        TaskGraphPanel[] panels = new TaskGraphPanel[views.length];
        for (int i = 0; i < views.length; i++) {
            DesktopView view = views[i];
            panels[i] = view.getTaskgraphPanel();
        }
        return panels;
    }

    /**
     * @return an array of all taskgraph panels with no parents
     */
    public TaskGraphPanel[] getRootTaskGraphPanels() {
        DesktopView[] views = getDesktopViewManager().getViews();
        List<TaskGraphPanel> panels = new ArrayList<TaskGraphPanel>();
        for (int i = 0; i < views.length; i++) {
            DesktopView view = views[i];
            if (view.getTaskgraphPanel().getTaskGraph().getParent() == null) {
                panels.add(view.getTaskgraphPanel());
            }
        }
        return panels.toArray(new TaskGraphPanel[panels.size()]);

    }

    /**
     * @param parent the taskgraph panel to find children for
     * @return the child taskgraph panels
     */
    public TaskGraphPanel[] getChildTaskGraphPanels(TaskGraphPanel parent) {
        DesktopView[] views = getDesktopViewManager().getViews();
        List<TaskGraphPanel> panels = new ArrayList<TaskGraphPanel>();
        for (int i = 0; i < views.length; i++) {
            DesktopView view = views[i];
            if (view.getTaskgraphPanel().getTaskGraph().getParent() == parent.getTaskGraph()) {
                panels.add(view.getTaskgraphPanel());
            }
        }
        return panels.toArray(new TaskGraphPanel[panels.size()]);

    }

    /**
     * @return the taskgraph panel which is representing the specified task graph, or null if the task isn't
     *         represented
     */
    public DesktopView getDesktopViewFor(TaskGraph group) {
        return getDesktopViewManager().getTaskgraphViewFor(group);
    }

    public void removeDesktopView(DesktopView view) {
        getDesktopViewManager().remove(view);
    }

    public String getTitle(DesktopView view) {
        return getDesktopViewManager().getTitle(view);
    }

    /**
     * Add a blank taskgraph panel
     */
    public TaskGraph addParentTaskGraphPanel() {
        try {
            return addParentTaskGraphPanel(TaskGraphManager.createTaskGraph());
        } catch (TaskException except) {
            ErrorDialog.show(this, "Error Creating Parent TaskGraph", except);
            return null;
        }
    }

    /**
     * Add a taskgraph panel for the specified taskgraph. This method creates a new instance of the specified taskgraph
     * using the current taskgraph factory
     *
     * @return the instance of taskgraph created using the current taskgraph factory
     */
    public TaskGraph addParentTaskGraphPanel(TaskGraph initgraph) {
        String factoryType = TaskGraphManager.DEFAULT_FACTORY_TYPE;
        try {
            return addParentTaskGraphPanel(initgraph, factoryType);
        } catch (TaskException e) {
            ErrorDialog.show(this, "Error Rendering TaskGraph: " + initgraph.getToolName(), e);
            return null;
        }
    }

    public TaskGraph addNoExecParentTaskGraphPanel(TaskGraph initgraph) {
        String factoryType = TaskGraphManager.NON_RUNNABLE_FACTORY_TYPE;
        try {
            return addParentTaskGraphPanel(initgraph, factoryType);
        } catch (TaskException e) {
            ErrorDialog.show(this, "Error Rendering TaskGraph: " + initgraph.getToolName(), e);
            return null;
        }
    }

    private TaskGraph addParentTaskGraphPanel(TaskGraph initgraph, String factoryType) throws TaskException {
        TaskGraph taskgraph = (TaskGraph) TaskGraphManager.createTask(initgraph, factoryType, false);

        LocalServer server = new LocalServer(taskgraph);
        TaskGraphManager.setTrianaServer(taskgraph, server);

        if ((taskgraph.getToolName() == null) || taskgraph.getToolName().equals("")) {
            String name = getNextUntitledName();
            taskgraph.setToolName(name);
        }

        TaskGraphPanel parent = addChildTaskGraphPanel(taskgraph, server);
        parents.add(parent);

        return taskgraph;
    }

    /**
     * Add an taskgraph panel for a child taskgraph to the workspace.
     */
    public TaskGraphPanel addChildTaskGraphPanel(final TaskGraph taskgraph, TrianaClient client) {
        registerTrianaClient(taskgraph, client);

        final TaskGraphPanel panel = TaskGraphViewManager.getTaskGraphPanel(taskgraph, client);
        panelmanager.monitorTaskGraph(taskgraph);
        panel.getContainer().addFocusListener(this);
        new ToolMouseHandler(panel);
        panel.init();
        panel.getTaskGraph().addTaskGraphListener(this);
        panel.getTaskGraph().addTaskListener(this);

        SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                DesktopView view = getDesktopViewManager().newDesktopView(panel);
                selected = panel;
            }
        });

        return panel;
    }

    /**
     * @return A string for the new window/taskgraph, Untitled1, Untitled2...
     */
    private String getNextUntitledName() {
        int untitledCount = 1;
        DesktopView[] views = getDesktopViewManager().getViews();
        for (DesktopView view : views) {
            String name = getDesktopViewManager().getTitle(view);
            if (name.startsWith("Untitled")) {
                String numberString = name.substring(8);
                int number = Integer.parseInt(numberString);
                if (number + 1 > untitledCount) {
                    untitledCount = number + 1;
                }
            }
        }
        return "Untitled" + untitledCount;
    }

    /**
     * Registers the specified TrianaClient to handle the specified taskgraph. This is generally called when
     * addTaskGraphPanel/addParentTaskGraphPanel is called and does not need to be called explicitally.
     */
    public void registerTrianaClient(TaskGraph taskgraph, TrianaClient client) {
        clienttable.put(taskgraph, client);
    }

    /**
     * Unregisters the TrianaClient for the specified task
     */
    public void unregisterTrianaClient(TaskGraph taskgraph) {
        clienttable.remove(taskgraph);
    }

    /**
     * @return the TrianaClient for the specified taskgraph (null if none registered)
     */
    public TrianaClient getTrianaClient(TaskGraph taskgraph) {
        if (clienttable.containsKey(taskgraph)) {
            return (TrianaClient) clienttable.get(taskgraph);
        } else {
            return null;
        }
    }

    /**
     * Handle the local publish (and view if required) of the specified taskgraph
     */
    public void localDeploy(TaskGraph taskgraph, TrianaClient client) {
        addChildTaskGraphPanel(taskgraph, client);
    }

    /**
     * Handle the local retract of the specified taskgraph
     */
    public void localRetract(TaskGraph taskgraph) {
        TaskGraphPanel[] panels = getTaskGraphPanels();
        String id = taskgraph.getInstanceID();

        for (int count = 0; count < panels.length; count++) {
            if (id.equals(panels[count].getTaskGraph().getInstanceID())) {
                cleanUpWindows(panels[count].getTaskGraph());
            }
        }
    }

    /**
     * Gets the helpFile attribute of the ApplicationFrame object
     *
     * @return The helpFile value
     */
    public String getHelpFile() {
        return Env.home() + "help" + File.separator + "index.html";
    }

    /**
     * Hides of a main triana window and all its sub windows. If the main triana is a parent this also disposes of the
     * whole taskgraph.
     */
    private void disposeTaskGraphPanel(DesktopView panel) {
        TaskGraph taskgraph = panel.getTaskgraphPanel().getTaskGraph();
        cleanUpWindows(taskgraph);
    }

    private void cleanUpWindows(TaskGraph taskgraph) {
        Task[] tasks = taskgraph.getTasks(false);
        DesktopView cont;

        cont = getDesktopViewManager().getTaskgraphViewFor(taskgraph);

        if (cont != null) {
            disposeWindow(cont);
        }

        for (int count = 0; count < tasks.length; count++) {
            if (tasks[count] instanceof TaskGraph) {
                cleanUpWindows((TaskGraph) tasks[count]);
            }
        }

        unregisterTrianaClient(taskgraph);
    }

    /**
     * Cleans up a main triana window
     */
    private void disposeWindow(final DesktopView view) {
        final TaskListener tasklist = this;
        final TaskGraphListener tgraphlist = this;

        SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                if (view != null) {
                    TaskGraphPanel comp = view.getTaskgraphPanel();
                    comp.getTaskGraph().removeTaskGraphListener(tgraphlist);
                    comp.getTaskGraph().removeTaskListener(tasklist);
                    comp.dispose();
                    if (parents.contains(comp)) {
                        parents.remove(comp);
                        comp.getTaskGraph().dispose();
                    }
                }
            }
        });
    }

    public DesktopViewManager getDesktopViewManager() {
        return DesktopViewController.getCurrentView();
    }

    /**
     * Called when the user wants to close the window. If the window is in another application then the window is just
     * made invisible, but if it is a stand alone application then a really Quit ? window is given to ask the user if
     * he/she really wants to quit or not.
     */
    public void cleanUp() {
        Env.writeConfig();
        Env.stopConfigWriters();

        TaskGraphPanel[] cont = parents.toArray(new TaskGraphPanel[parents.size()]);

        for (int count = 0; count < cont.length; count++) {
            DesktopView tdv = getDesktopViewManager().getDesktopViewFor(cont[count]);
            closeTaskGraphPanel(tdv);
        }
    }

    /**
     * @return the current selected MainTriana panel within the main Triana application within e.g. the reference to the
     *         frame within the workspace which has the focus
     */
    public TaskGraphPanel getSelectedTaskGraphPanel() {
        if (selected instanceof TaskGraphPanel) {
            return (TaskGraphPanel) selected;
        } else {
            return null;
        }
    }

    public DesktopView getSelectedDesktopView() {
        log.debug("ApplicationFrame.getSelectedDesktopView selected:" + selected);
        if (selected instanceof TaskGraphPanel) {
            return getDesktopViewManager().getDesktopViewFor((TaskGraphPanel) selected);
        } else {
            return null;
        }
    }

    /**
     * @return true if only a single tool is selected
     */
    public boolean isSingleSelectedTool() {
        if ((getSelectionHandler() != this) && (getSelectionHandler() instanceof ToolSelectionHandler)) {
            return ((ToolSelectionHandler) getSelectionHandler()).isSingleSelectedTool();
        } else {
            return false;
        }
    }

    /**
     * @return the currently selected tool (null if none selected)
     */
    public Tool getSelectedTool() {
        if ((getSelectionHandler() != this) && (getSelectionHandler() instanceof ToolSelectionHandler)) {
            return ((ToolSelectionHandler) getSelectionHandler()).getSelectedTool();
        } else {
            return null;
        }
    }

    /**
     * @return an array of the currently selected tools
     */
    public Tool[] getSelectedTools() {
        if ((getSelectionHandler() != this) && (getSelectionHandler() instanceof ToolSelectionHandler)) {
            return ((ToolSelectionHandler) getSelectionHandler()).getSelectedTools();
        } else {
            return new Tool[0];
        }
    }

    /**
     * @return the triana client responsible for the selected tools (null if none)
     */
    public TrianaClient getSelectedTrianaClient() {
        if ((getSelectionHandler() != this) && (getSelectionHandler() instanceof ToolSelectionHandler)) {
            return ((ToolSelectionHandler) getSelectionHandler()).getSelectedTrianaClient();
        } else {
            return null;
        }
    }

    /**
     * @return the currently selected taskgraph (usually parent of selected tool)
     */
    public TaskGraph getSelectedTaskgraph() {
        if ((getSelectionHandler() != this) && (getSelectionHandler() instanceof ToolSelectionHandler)) {
            return ((ToolSelectionHandler) getSelectionHandler()).getSelectedTaskgraph();
        } else {
            return null;
        }
    }

    /**
     * @return The object that is selected for this handler.
     */
    public Object getSelectionHandler() {
        return selected;
    }

    /**
     * Called when the core options of a task change.
     */
    public void taskPropertyUpdate(TaskPropertyEvent event) {
        Task task = event.getTask();

        TaskGraphPanel[] comps = getTaskGraphPanels();
        int count = 0;

        while ((count < comps.length) && (comps[count].getTaskGraph() != task)) {
            count++;
        }

        if (comps[count].getTaskGraph() == task) {
            DesktopView view = getDesktopViewManager().getDesktopViewFor(comps[count]);
            getDesktopViewManager().setTitle(view, comps[count].getTaskGraph().getToolName());
        }
    }

    /**
     * Called when the value of a parameter is changed, including when a parameter is removed.
     */
    public void parameterUpdated(ParameterUpdateEvent event) {
    }

    /**
     * Called when a data input node is added.
     */
    public void nodeAdded(TaskNodeEvent event) {
    }

    /**
     * Called before a data input node is removed.
     */
    public void nodeRemoved(TaskNodeEvent event) {
    }

    /**
     * Called before the task is disposed
     */
    public void taskDisposed(TaskDisposedEvent event) {
    }

    /**
     * Called when a new task is created in a taskgraph.
     */
    public void taskCreated(TaskGraphTaskEvent event) {
    }

    /**
     * Called when a task is removed from a taskgraph. Note that this method is called when tasks are removed from a
     * taskgraph due to being grouped (they are placed in the new groups taskgraph).
     */
    public void taskRemoved(TaskGraphTaskEvent event) {
        if (event.getTask() instanceof TaskGraph) {
            DesktopView view = getDesktopViewFor((TaskGraph) event.getTask());
            if (view != null) {
                closeTaskGraphPanel(view);
            }
        }
    }

    /**
     * Called when a new connection is made between two tasks.
     */
    public void cableConnected(TaskGraphCableEvent event) {
    }

    /**
     * Called when a connection is reconnected to a different task.
     */
    public void cableReconnected(TaskGraphCableEvent event) {
    }

    /**
     * Called before a connection between two tasks is removed.
     */
    public void cableDisconnected(TaskGraphCableEvent event) {
    }

    /**
     * Called when the control task is connected/disconnected or unstable
     */
    public void controlTaskStateChanged(ControlTaskStateEvent event) {
    }

    public void treeNodesChanged(TreeModelEvent event) {
    }

    public void treeNodesInserted(TreeModelEvent event) {
        TreePath path = event.getTreePath();
        if (path.getPathCount() >= 2) {
            toolboxTree.scrollPathToVisible(event.getTreePath());
            toolboxTree.getModel().removeTreeModelListener(this);
        }
    }

    public void treeNodesRemoved(TreeModelEvent event) {

    }

    public void treeStructureChanged(TreeModelEvent event) {
    }

    public void componentHidden(ComponentEvent event) {
    }

    public void componentMoved(ComponentEvent event) {
        if (event.getSource() == this) {
            Env.setWindowPosition(getLocation());
        }
    }

    public void componentResized(ComponentEvent event) {
        if (event.getSource() == this) {
            Env.setWindowSize(getSize());
        }
    }

    public void componentShown(ComponentEvent event) {
    }

    /**
     * Invoked when a component gains the keyboard focus.
     */
    public void focusGained(FocusEvent event) {
        if (event.getSource() == toolboxTree) {
            selected = leaflistener;
        } else {
            selected = event.getSource();
        }
    }

    /**
     * Invoked when a component loses the keyboard focus.
     * ANDREW: IS THIS NEEDED NOW?
     */
    public void focusLost(FocusEvent event) {
        // required to fix internal frame focus/selection bug
        if (event.getComponent() instanceof TaskGraphPanel) {
            DesktopView frame = getDesktopViewManager().getDesktopViewFor((TaskGraphPanel) event.getComponent());

            if (frame != null) {
                getDesktopViewManager().setSelected(frame, false);
            }
        }
    }

    @Override
    public void ViewClosing(DesktopView view) {
        disposeTaskGraphPanel(view);
    }

    @Override
    public void ViewClosed(DesktopView view) {
        disposeTaskGraphPanel(view);
    }

    @Override
    public void ViewOpened(DesktopView view) {
        selected = view.getTaskgraphPanel();
    }

    @Override
    public void desktopChanged(DesktopViewManager manager) {
        if (workspace.getComponentCount() > 0) {
            workspace.remove(0);
            workspace.add(manager.getWorkspace());
            workspace.invalidate();
            workspace.validate();
            workspace.repaint();
        }
    }

    public void repaintWorkspace() {
        workspace.repaint();
    }

    private class TrianaShutdownHook extends Thread {
        /**
         * If this thread was constructed using a separate <code>Runnable</code> run object, then that
         * <code>Runnable</code> object's <code>run</code> method is called; otherwise, this method does nothing and
         * returns.
         * <p/>
         * Subclasses of <code>Thread</code> should override this method.
         *
         * @see Thread#start()
         * @see Thread#stop()
         * @see Thread#Thread(ThreadGroup, Runnable, String)
         * @see Runnable#run()
         */
        public void run() {
            cleanUp();
        }

    }

}