package org.gvt;
import org.biopax.paxtools.io.jena.JenaIOHandler;
import org.biopax.paxtools.model.BioPAXElement;
import org.biopax.paxtools.model.BioPAXLevel;
import org.biopax.paxtools.model.Model;
import org.biopax.paxtools.model.level2.Level2Factory;
import org.biopax.paxtools.model.level2.pathway;
import org.eclipse.draw2d.ColorConstants;
import org.eclipse.draw2d.FigureCanvas;
import org.eclipse.draw2d.Layer;
import org.eclipse.draw2d.geometry.Dimension;
import org.eclipse.draw2d.geometry.Point;
import org.eclipse.gef.*;
import org.eclipse.gef.KeyStroke;
import org.eclipse.gef.ui.actions.ActionRegistry;
import org.eclipse.gef.ui.actions.GEFActionConstants;
import org.eclipse.gef.ui.parts.ScrollingGraphicalViewer;
import org.eclipse.jface.action.IMenuListener;
import org.eclipse.jface.action.IMenuManager;
import org.eclipse.jface.action.MenuManager;
import org.eclipse.jface.action.ToolBarManager;
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.window.ApplicationWindow;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.CTabFolder;
import org.eclipse.swt.custom.CTabItem;
import org.eclipse.swt.events.*;
import org.eclipse.swt.graphics.*;
import org.eclipse.swt.layout.FillLayout;
import org.eclipse.swt.widgets.*;
import org.gvt.action.*;
import org.gvt.editpart.ChsEditPartFactory;
import org.gvt.editpart.ChsNodeEditPart;
import org.gvt.editpart.ChsRootEditPart;
import org.gvt.editpart.ChsScalableRootEditPart;
import org.gvt.figure.HighlightLayer;
import org.gvt.model.CompoundModel;
import org.gvt.model.GraphObject;
import org.gvt.model.sif.SIFGraph;
import org.gvt.model.biopaxl2.Actor;
import org.gvt.model.biopaxl2.BioPAXGraph;
import org.gvt.model.biopaxl2.Complex;
import org.gvt.model.biopaxl2.Conversion;
import org.patika.mada.dataXML.ChisioExperimentData;
import org.patika.mada.util.ExperimentDataManager;
import javax.swing.*;
import java.util.*;
import java.util.List;
/**
* This class maintains the main function for this application. Chisio is a
* compound graph editor with support for various types of layout algorithms.
*
* @author Cihan Kucukkececi
* @author Ozgun Babur
*
* Copyright: Bilkent Center for Bioinformatics, 2007 - present
*/
public class ChisioMain extends ApplicationWindow
implements MouseListener, KeyListener, MouseTrackListener
{
private CTabFolder tabFolder;
private PopupManager popupManager;
private KeyHandler keyHandler;
private Set<String> openTabNames;
private Set<String> allPathwayNames;
private Map<CTabItem, ScrollingGraphicalViewer> tabToViewerMap;
private Map<String, CTabItem> nameToTabMap;
public static boolean transferNode = false;
public static Color higlightColor;
public static Combo zoomCombo;
private String owlFileName;
public Point clickLocation;
private Map<String, ExperimentDataManager> dataManagerMap;
private Shell lockShell;
/**
* This is the subject graph. Not shown in any view.
*/
private BioPAXGraph rootGraph;
/**
* Used for BioPAX owl file reading and writing operations.
*/
private JenaIOHandler jenaIOHandler;
/**
* Used to undeerstand if there is a modification to the biopax file which is not captured by
* views' command stakes.
*/
private boolean dirty;
public ChisioMain()
{
super(null);
// createChangeModeAction();
this.dataManagerMap = new HashMap<String, ExperimentDataManager>();
}
protected void handleShellCloseEvent()
{
if (LoadBioPaxModelAction.saveChangesBeforeDiscard(this))
{
super.handleShellCloseEvent();
Shell[] inspectors = Display.getDefault().getShells();
int size = inspectors.length;
for(int i = 0; i < size ; i++)
{
Shell current = inspectors[i];
if(current.getText().indexOf("Properties") > 0)
{
current.close();
}
}
}
}
public static void main(String[] args)
{
ChisioMain window = new ChisioMain();
window.setBlockOnOpen(true);
window.addMenuBar();
window.addToolBar(SWT.FLAT | SWT.RIGHT);
window.open();
Display.getCurrent().dispose();
}
public Dimension getCurrentSize()
{
int w = getShell().getSize().x;
int h = getShell().getSize().y;
return new Dimension(w, h);
}
public JenaIOHandler getJenaIOHandler()
{
if (jenaIOHandler == null)
{
this.jenaIOHandler = new JenaIOHandler(null, BioPAXLevel.L2);
this.jenaIOHandler.setStrict(false);
}
return jenaIOHandler;
}
protected Control createContents(Composite parent)
{
getShell().setText(TOOL_NAME);
getShell().setSize(800, 600);
this.getShell().setImage(
ImageDescriptor.createFromFile(ChisioMain.class,
"icon/cbe-icon.png").createImage());
Composite compositeRoot = new Composite(parent, SWT.BORDER);
compositeRoot.setLayout(new FillLayout());
tabFolder = new CTabFolder(compositeRoot, SWT.NONE);
tabFolder.setSimple(false);
tabFolder.addSelectionListener(new SelectionAdapter()
{
public void widgetSelected(SelectionEvent event)
{
if (getViewer() != null)
{
updateCombo(((ChsScalableRootEditPart) getViewer().getRootEditPart()).
getZoomManager().getZoomAsText());
}
}
});
this.openTabNames = new HashSet<String>();
this.allPathwayNames = new HashSet<String>();
this.nameToTabMap = new HashMap<String, CTabItem>();
this.tabToViewerMap = new HashMap<CTabItem, ScrollingGraphicalViewer>();
popupManager = new PopupManager(this);
popupManager.setRemoveAllWhenShown(true);
popupManager.addMenuListener(new IMenuListener() {
public void menuAboutToShow(IMenuManager manager) {
popupManager.createActions(manager);
}
});
keyHandler = new KeyHandler();
ActionRegistry a = new ActionRegistry();
keyHandler.put(KeyStroke.getPressed(SWT.DEL, 127, 0),
new DeleteAction(this));
keyHandler.put(KeyStroke.getPressed('+',SWT.KEYPAD_ADD, 0),
new ZoomAction(this,1,null));
keyHandler.put(KeyStroke.getPressed('-',SWT.KEYPAD_SUBTRACT, 0),
new ZoomAction(this,-1,null));
/*keyHandler.put(KeyStroke.getPressed(SWT.CTRL, 0),
new ZoomAction(this,-1,null));*/
keyHandler.put(KeyStroke.getPressed(SWT.F2, 0), a
.getAction(GEFActionConstants.DIRECT_EDIT));
this.higlightColor = ColorConstants.yellow;
createCombos();
return compositeRoot;
}
public CTabItem createNewTab(CompoundModel root)
{
if (root == null)
{
root = new BioPAXGraph();
}
root.setAsRoot();
String name;
if (root instanceof BioPAXGraph)
{
BioPAXGraph graph = (BioPAXGraph) root;
if (graph.isMechanistic() && graph.getPathway() == null)
{
pathway p = createNewPathway();
if (graph.getName() != null)
{
p.setNAME(adviceTabName(graph.getName()));
}
else
{
p.setNAME(adviceTabName(null));
}
graph.setName(p.getNAME());
graph.setName(p.getNAME());
graph.setPathway(p);
graph.registerContentsToPathway();
allPathwayNames.add(p.getNAME());
}
else
{
graph.setName(graph.getPathway() != null ? graph.getPathway().getNAME() :
adviceTabName(graph.getName()));
}
name = graph.getName();
}
else
{
name = adviceTabName(null);
}
if (openTabNames.contains(name))
{
return null;
}
CTabItem tab = new CTabItem(tabFolder, SWT.NONE);
if (root instanceof SIFGraph)
{
ImageDescriptor id = ImageDescriptor.createFromFile(
ChisioMain.class, "/org/gvt/icon/sif.png");
tab.setImage(id.createImage());
}
Composite composite = new Composite(tabFolder, SWT.NONE);
composite.setLayout(new FillLayout());
tab.setControl(composite);
ScrollingGraphicalViewer viewer = new ScrollingGraphicalViewer();
viewer.setEditDomain(new EditDomain());
viewer.createControl(composite);
viewer.getControl().setBackground(ColorConstants.white);
RootEditPart rootEditPart = new ChsScalableRootEditPart();
viewer.setRootEditPart(rootEditPart);
viewer.setEditPartFactory(new ChsEditPartFactory());
((FigureCanvas)viewer.getControl()).setScrollBarVisibility(FigureCanvas.ALWAYS);
// DropTargetListener
viewer.addDropTargetListener(new ChsFileDropTargetListener(viewer, this));
// DragSourceListener
viewer.addDragSourceListener(new ChsFileDragSourceListener(viewer));
tab.setText(name);
viewer.setContents(root);
viewer.getControl().addMouseListener(this);
viewer.setKeyHandler(keyHandler);
this.tabToViewerMap.put(tab, viewer);
this.setSelectedTab(tab);
this.openTabNames.add(name);
this.nameToTabMap.put(name, tab);
// Temporary solution to a very annoying bug. We start to be able to see the graph after
// resizing the window. And just redrawing the contents does not solve it.
Rectangle bounds = getShell().getBounds();
bounds.width -= 1;
getShell().setBounds(bounds);
bounds.width += 1;
getShell().setBounds(bounds);
return tab;
}
/**
* Advices a tab name that is derived from the parameter name and not exists.
*/
public String adviceTabName(String candidate)
{
String name = candidate != null ? candidate :
"Tab - " + Long.toString(System.currentTimeMillis()).substring(8);
int i = 2;
while (allPathwayNames.contains(name) || openTabNames.contains(name))
{
name = candidate + " (" + (i++) + ")";
}
return name;
}
private pathway createNewPathway()
{
pathway p = ((Level2Factory) BioPAXLevel.L2.getDefaultFactory()).createPathway();
p.setRDFId("http://www.chisio.org/user#" + System.currentTimeMillis());
this.getOwlModel().add(p);
return p;
}
public CTabItem getSelectedTab()
{
if (this.tabFolder != null && this.tabFolder.getItemCount() > 0)
{
return this.tabFolder.getSelection();
}
return null;
}
public void setSelectedTab(CTabItem tab)
{
this.tabFolder.setSelection(tab);
}
public void closeTab(String tabName, boolean remmeberLayout)
{
assert openTabNames.contains(tabName) : "Tab close request with an unknown name:" + tabName;
assert nameToTabMap.containsKey(tabName) : "Tab name not known: " + tabName;
CTabItem tab = nameToTabMap.get(tabName);
closeTab(tab, remmeberLayout);
}
public void closeTab(CTabItem tab, boolean rememberLayout)
{
ScrollingGraphicalViewer viewer = tabToViewerMap.get(tab);
CompoundModel root = (CompoundModel) viewer.getContents().getModel();
if (root instanceof BioPAXGraph)
{
BioPAXGraph graph = (BioPAXGraph) root;
assert graph.getName().equals(tab.getText()) :
"graph name: " + graph.getName() + " tab name: " + tab.getText();
if (rememberLayout && graph.isMechanistic())
{
graph.recordLayout();
}
}
boolean removed = openTabNames.remove(tab.getText());
assert removed : "tab name: " + tab.getText();
ScrollingGraphicalViewer v = tabToViewerMap.remove(tab);
assert v != null;
CTabItem t = nameToTabMap.remove(tab.getText());
assert t != null;
tab.dispose();
}
public void closeAllTabs(boolean rememberLayout)
{
for (CTabItem tab : tabFolder.getItems())
{
closeTab(tab, rememberLayout);
}
assert tabFolder.getItemCount() == 0;
assert tabToViewerMap.size() == 0;
assert nameToTabMap.size() == 0;
assert openTabNames.size() == 0;
}
public Map<CTabItem, ScrollingGraphicalViewer> getTabToViewerMap()
{
return tabToViewerMap;
}
public ScrollingGraphicalViewer getViewer()
{
CTabItem tab = getSelectedTab();
if (tab != null)
{
return this.tabToViewerMap.get(tab);
}
return null;
}
public Set<String> getOpenTabNames()
{
return openTabNames;
}
public Set<String> getAllPathwayNames()
{
return allPathwayNames;
}
public void renamePathway(CTabItem tab, String newName)
{
nameToTabMap.remove(tab.getText());
openTabNames.remove(tab.getText());
nameToTabMap.put(newName, tab);
openTabNames.add(newName);
if (allPathwayNames.contains(tab.getText()))
{
allPathwayNames.remove(tab.getText());
allPathwayNames.add(newName);
}
Object compmod = tabToViewerMap.get(tab).getContents().getModel();
if (compmod instanceof BioPAXGraph)
{
BioPAXGraph graph = (BioPAXGraph) compmod;
pathway p = graph.getPathway();
if (p != null)
{
p.setNAME(newName);
}
graph.setName(newName);
}
tab.setText(newName);
assert nameToTabMap.size() == openTabNames.size();
}
/**
* Checks if any view is dirty, i.e. needs to saved to be persistent.
* @return true if at least one view is dirty
*/
public boolean isDirty()
{
if (this.dirty)
{
return true;
}
for (ScrollingGraphicalViewer viewer : tabToViewerMap.values())
{
if (viewer.getEditDomain().getCommandStack().isDirty())
{
return true;
}
}
return false;
}
/**
* Marks the command stacks of edit domains of views as saved at the current location. This save
* location is used for understanding if the view is dirty.
*/
public void markSaved()
{
this.dirty = false;
for (ScrollingGraphicalViewer viewer : tabToViewerMap.values())
{
viewer.getEditDomain().getCommandStack().markSaveLocation();
}
}
public void makeDirty()
{
this.dirty = true;
}
/**
* Locks the application window, showing a message in the middle. This is useful while doing
* time consuming operations. So the user has an idea why they wait.
* @param msg message about the task being performed
*/
public void lockWithMessage(String msg)
{
unlock();
lockShell = new Shell(this.getShell(), SWT.APPLICATION_MODAL);
lockShell.setLocation(getShell().getLocation());
lockShell.setLayout(new FillLayout());
Text txt = new Text(lockShell, SWT.MULTI);
FontData fd = txt.getFont().getFontData()[0];
fd.setHeight(fd.getHeight() + 2);
fd.setStyle(SWT.BOLD);
txt.setFont(new Font(null, new FontData[]{fd}));
txt.setText("\n " + msg + " \n");
txt.setBackground(new Color(null, 255, 247, 240));
txt.setForeground(new Color(null, 90, 100, 90));
lockShell.pack();
lockShell.setLocation(
lockShell.getLocation().x +
(getShell().getBounds().width / 2) -
(msg.length() * fd.getHeight() / 2),
lockShell.getLocation().y + (getShell().getBounds().height / 2) - 50);
lockShell.setVisible(true);
}
/**
* Removes any message and unlocks the application.
*/
public void unlock()
{
if (lockShell != null)
{
lockShell.dispose();
lockShell = null;
}
}
protected MenuManager createMenuManager()
{
return TopMenuBar.createBarMenu(this);
}
protected ToolBarManager createToolBarManager(int style)
{
return new ToolbarManager(style, this);
}
public void mouseDoubleClick(MouseEvent e)
{
InspectorAction inspectorAction = new InspectorAction(this, false);
inspectorAction.run();
}
public void mouseDown(MouseEvent e)
{
clickLocation = new Point(e.x,e.y);
popupManager.setClickLocation(clickLocation);
getViewer().getControl().setMenu(
popupManager.createContextMenu(getViewer().getControl()));
}
public void mouseUp(MouseEvent e)
{
clickLocation = new Point(e.x,e.y);
}
public void keyPressed(KeyEvent e)
{
transferNode = !transferNode;
JOptionPane.showMessageDialog(null, "Transfer Mode: " + transferNode);
}
public void keyReleased(KeyEvent e)
{
}
public void mouseEnter(MouseEvent e)
{
}
public void mouseExit(MouseEvent e)
{
}
public void mouseHover(MouseEvent e)
{
}
public EditDomain getEditDomain()
{
ScrollingGraphicalViewer viewer = getViewer();
if (viewer != null)
{
return viewer.getEditDomain();
}
return null;
}
public void createCombos()
{
ToolBar toolbar = this.getToolBarManager().getControl();
ToolItem item = new ToolItem(toolbar, SWT.SEPARATOR, 15);
zoomCombo = new Combo(toolbar, SWT.NONE);
zoomCombo.add("2000%");
zoomCombo.add("1000%");
zoomCombo.add("500%");
zoomCombo.add("150%");
zoomCombo.add("100%");
zoomCombo.add("75%");
zoomCombo.add("50%");
zoomCombo.add("25%");
zoomCombo.pack();
item.setWidth(zoomCombo.getSize().x);
item.setControl(zoomCombo);
zoomCombo.addKeyListener(new KeyListener()
{
public void keyPressed(KeyEvent keyEvent)
{
// When ENTER is pressed, zoom to given level
if(keyEvent.keyCode == 13)
{
if (getViewer() != null)
{
((ChsScalableRootEditPart)getViewer().getRootEditPart()).
getZoomManager().setZoomAsText(zoomCombo.getText());
}
}
}
public void keyReleased(KeyEvent keyEvent)
{
// nothing
}
});
zoomCombo.addSelectionListener(new SelectionAdapter()
{
public void widgetSelected(SelectionEvent event)
{
if (getViewer() != null)
{
((ChsScalableRootEditPart) getViewer().getRootEditPart()).
getZoomManager().setZoomAsText(zoomCombo.getText());
}
}
});
if (getViewer() != null)
{
updateCombo(((ChsScalableRootEditPart) getViewer().getRootEditPart()).
getZoomManager().getZoomAsText());
}
}
public static void updateCombo(String newValue)
{
zoomCombo.setText(newValue);
zoomCombo.update();
}
public void setOwlFileName(String filename)
{
this.owlFileName = filename;
if (filename != null)
{
this.getShell().setText(filename + " - " + TOOL_NAME);
}
else
{
this.getShell().setText(TOOL_NAME);
}
}
public String getOwlFileName()
{
return owlFileName;
}
public BioPAXGraph getRootGraph()
{
return rootGraph;
}
public void setRootGraph(BioPAXGraph rootGraph)
{
this.rootGraph = rootGraph;
if (this.rootGraph != null)
{
assert rootGraph.getBiopaxModel() != null;
this.associateGraphWithExperimentData(rootGraph);
allPathwayNames.clear();
for (pathway p : getOwlModel().getObjects(pathway.class))
{
if (!p.getPATHWAY_COMPONENTS().isEmpty())
{
allPathwayNames.add(p.getNAME());
}
}
}
else
{
this.dirty = false;
}
}
public Model getOwlModel()
{
return rootGraph == null ? null : rootGraph.getBiopaxModel();
}
public HighlightLayer getHighlightLayer()
{
HighlightLayer highlight = (HighlightLayer)
((ChsScalableRootEditPart) getViewer().getRootEditPart()).
getLayer(HighlightLayer.HIGHLIGHT_LAYER);
return highlight;
}
public Layer getHandleLayer()
{
Layer handle = (Layer) ((ChsScalableRootEditPart) getViewer().getRootEditPart()).
getLayer(LayerConstants.HANDLE_LAYER);
return handle;
}
public java.util.List getSelectedModel()
{
if (getViewer() == null) return null;
java.util.List models = new ArrayList();
java.util.List parts = ((IStructuredSelection) getViewer().getSelection()).toList();
for (Object partObj : parts)
{
EditPart ep = (EditPart) partObj;
models.add(ep.getModel());
}
return models;
}
//----------------------------------------------------------------------------------------------
// Section: BioPAX related methods
//----------------------------------------------------------------------------------------------
public BioPAXGraph getPathwayGraph()
{
ScrollingGraphicalViewer viewer = getViewer();
if (viewer != null)
{
Object o = viewer.getContents().getModel();
if (o instanceof BioPAXGraph)
{
return (BioPAXGraph) o;
}
}
return null;
}
public List<BioPAXGraph> getAllPathwayGraphs()
{
List<BioPAXGraph> list = new ArrayList<BioPAXGraph>();
for (CTabItem tab : tabFolder.getItems())
{
ScrollingGraphicalViewer viewer = tabToViewerMap.get(tab);
if (viewer != null)
{
Object o = viewer.getContents().getModel();
if (o instanceof BioPAXGraph)
{
list.add((BioPAXGraph) o);
}
}
}
return list;
}
/**
* A method to highlight graph objects in the current model
* according to a collection of Rdf Ids.
* @param rdfs rdfs of the objects to be highlighted as a collecton of string
*/
public void highlightRDFs(Collection<String> rdfs)
{
// Below, we iterate starting from the root
// to find nodes who have the element(s) in interest
// (check via RDFId)
ChsRootEditPart root = (ChsRootEditPart) getViewer().
getRootEditPart().getChildren().get(0);
for (int i = 0; i < root.getChildren().size(); i++)
{
EditPart node = (EditPart) root.getChildren().get(i);
ListIterator nodeIterator = node.getChildren().listIterator();
while( nodeIterator.hasNext() ) {
ChsNodeEditPart oneObj = (ChsNodeEditPart) nodeIterator.next();
Object oneModel = oneObj.getModel();
BioPAXElement be = null;
if( oneModel instanceof Actor)
{
be = ((Actor) oneModel).getEntity();
} else if( oneModel instanceof Complex)
{
be = ((Complex) oneModel).getComplex();
} else if( oneModel instanceof Conversion)
{
be = ((Conversion) oneModel).getConversion();
} else if( oneModel instanceof org.gvt.model.biopaxl2.Control)
{
be = ((org.gvt.model.biopaxl2.Control) oneModel).getControl();
}
// We have the element and its type
// Let's check whether it's rdf has a match
// If so, simply higlight the node.
if( be != null && rdfs.contains(be.getRDFId()) )
{
((GraphObject) oneModel).setHighlightColor(higlightColor);
((GraphObject) oneModel).setHighlight(true);
}
} // End of node iteration
} // End of root iteration
} // End of method
//----------------------------------------------------------------------------------------------
// Section: Experiment data related methods
//----------------------------------------------------------------------------------------------
public boolean hasExperimentData(String type)
{
return dataManagerMap.containsKey(type) && dataManagerMap.get(type).isDataAvailable();
}
public void setExperimentData(ChisioExperimentData data, String fileLocation)
{
String type = data.getExperimentType();
ExperimentDataManager man = new ExperimentDataManager(data, fileLocation);
dataManagerMap.put(type, man);
if (rootGraph != null)
{
man.associateExperimentData(rootGraph);
for (ScrollingGraphicalViewer viewer : tabToViewerMap.values())
{
BioPAXGraph graph = (BioPAXGraph) viewer.getContents().getModel();
man.associateExperimentData(graph);
}
}
}
public Set<String> getLoadedExperimentTypes()
{
return dataManagerMap.keySet();
}
public ExperimentDataManager getExperimentDataManager(String type)
{
return dataManagerMap.get(type);
}
public ChisioExperimentData getExperimentData(String type)
{
return dataManagerMap.get(type).getCed();
}
public void associateGraphWithExperimentData()
{
associateGraphWithExperimentData(this.getPathwayGraph());
}
public void associateGraphWithExperimentData(BioPAXGraph graph)
{
if (graph == null || dataManagerMap.keySet().isEmpty())
{
return;
}
for (String type : dataManagerMap.keySet())
{
ExperimentDataManager man = dataManagerMap.get(type);
man.associateExperimentData(graph);
}
String type = graph.getLastAppliedColoring();
if (type != null)
{
graph.setLastAppliedColoring(null);
new ColorWithExperimentAction(this, graph, type).run();
}
}
// -------------------------------------------------------------------------
// Class Constants
// -------------------------------------------------------------------------
public final static String TOOL_NAME = "Chisio BioPAX Editor";
/**
* Used for preventing antialiasing and transparent colors in non-windows
* systems. During packaging this variable should be manually set.
*/
// public static boolean runningOnWindows = true;
// Following expression can be used for automatic detection of operating
// system. However we don't use it for safety.
public static boolean runningOnWindows = System.getProperty("os.name").startsWith("Windows");
}