// PathVisio, // a tool for data visualization and analysis using Biological Pathways // Copyright 2006-2011 BiGCaT Bioinformatics // // Licensed under the Apache 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.apache.org/licenses/LICENSE-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 org.wikipathways.gpmldiff; import java.awt.AWTEvent; import java.awt.BorderLayout; import java.awt.Dimension; import java.awt.Toolkit; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.io.File; import java.io.IOException; import java.text.DecimalFormat; import javax.swing.AbstractAction; import javax.swing.Action; import javax.swing.BoxLayout; import javax.swing.JComboBox; import javax.swing.JFileChooser; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JToolBar; import javax.swing.RootPaneContainer; import org.pathvisio.core.gpmldiff.BasicCost; import org.pathvisio.core.gpmldiff.BetterSim; import org.pathvisio.core.gpmldiff.GlassPane; import org.pathvisio.core.gpmldiff.PanelOutputter; import org.pathvisio.core.gpmldiff.PwyDoc; import org.pathvisio.core.gpmldiff.SearchNode; import org.pathvisio.core.view.VPathway; import org.pathvisio.core.view.VPathwayEvent; import org.pathvisio.core.view.VPathwayEvent.VPathwayEventType; import org.pathvisio.core.view.VPathwayListener; import org.pathvisio.gui.WrapLayout; class GpmlDiffWindow extends JPanel implements VPathwayListener { private JScrollPane[] pwyPane = new JScrollPane[2]; private JToolBar toolbar = null; private JComboBox zoomCombo = null; public static final int PWY_OLD = 0; public static final int PWY_NEW = 1; private VPathwayDiffViewer[] wrapper = { null, null }; private VPathway[] view = { null, null }; private PwyDoc[] doc = { null, null }; private PanelOutputter outputter = null; double zoomFactor = 100; JPanel centerPanel = null; GlassPane glassPane; public void setFile (int pwyType, File f) { // Pathway pwy = new Pathway(); doc[pwyType] = PwyDoc.read (f); assert (doc[pwyType] != null); wrapper[pwyType] = new VPathwayDiffViewer(pwyPane[pwyType], this); view[pwyType] = wrapper[pwyType].createVPathway(); view[pwyType].setEditMode(false); view[pwyType].addVPathwayListener(this); view[pwyType].setSelectionEnabled (false); view[pwyType].fromModel(doc[pwyType].getPathway()); view[pwyType].setPctZoom (zoomFactor); outputter = null; // invalidate putative remaining outputter if (view[PWY_OLD] != null && view[PWY_NEW] != null) { SearchNode result = doc[PWY_OLD].findCorrespondence (doc[PWY_NEW], new BetterSim(), new BasicCost()); outputter = new PanelOutputter(view[PWY_OLD], view[PWY_NEW]); doc[PWY_OLD].writeResult (result, doc[PWY_NEW], outputter); try { outputter.flush(); } catch (IOException ex) { ex.printStackTrace(); } // merge models of the two pathways // TODO: find solution for inequal sized pathways. pwyPane[0].getHorizontalScrollBar().setModel( pwyPane[1].getHorizontalScrollBar().getModel()); pwyPane[0].getVerticalScrollBar().setModel( pwyPane[1].getVerticalScrollBar().getModel()); } } private class LoadPwyAction extends AbstractAction { private int pwyType; private JPanel parent; public LoadPwyAction (JPanel window, int value) { super ("Load " + ((value == PWY_OLD) ? "old" : "new") + " pathway"); String s = (value == PWY_OLD) ? "old" : "new"; putValue (Action.SHORT_DESCRIPTION, "Load the " + s + " pathway"); putValue (Action.LONG_DESCRIPTION, "Load the " + s + " pathway"); pwyType = value; parent = window; } public void actionPerformed(ActionEvent e) { JFileChooser jfc = new JFileChooser(); jfc.setDialogTitle("Load a pathway"); jfc.setDialogType (JFileChooser.OPEN_DIALOG); int status = jfc.showDialog (parent, "Load"); if (status == JFileChooser.APPROVE_OPTION) { setFile (pwyType, jfc.getSelectedFile()); } } } class CenterAction extends AbstractAction { public CenterAction() { super ("Center"); } public void actionPerformed (ActionEvent e) { } } private class ZoomAction extends AbstractAction { double actionZoomFactor; public ZoomAction(double zf) { actionZoomFactor = zf; String descr = "Set zoom to " + (int)zf + "%"; putValue(Action.NAME, toString()); putValue(Action.SHORT_DESCRIPTION, descr); putValue(Action.LONG_DESCRIPTION, descr); } public void actionPerformed(ActionEvent e) { setZoomFactor (actionZoomFactor); } public String toString() { return (int)actionZoomFactor + "%"; } } private class ZoomToFitAction extends AbstractAction { public ZoomToFitAction() { putValue(Action.NAME, toString()); putValue(Action.SHORT_DESCRIPTION, "Fit pathway to window"); } public void actionPerformed(ActionEvent e) { zoomToFit(); } public String toString() { return "Fit to window"; } } /** * calculate the best zoom factor to fit both pathways, * then zoom to it. */ void zoomToFit () { double result = -1; if (view[0] != null) { result = view[0].getFitZoomFactor(); } if (view[1] != null) { double temp = view[1].getFitZoomFactor(); if (view[0] != null && temp < result) { result = temp; } } if (result > 0) { setZoomFactor (result); } zoomCombo.setSelectedItem( (int)zoomFactor + "%"); } /** * Set the zoom factor for both panels at once. */ void setZoomFactor (double factor) { zoomFactor = factor; if(view[0] != null) { view[0].setPctZoom(zoomFactor); } if(view[1] != null) { view[1].setPctZoom(zoomFactor); glassPane.setPctZoom (view[1].getPctZoom()); } } void addToolbarActions() { toolbar.setLayout (new WrapLayout (1, 1)); toolbar.add(new JLabel("Zoom:", JLabel.LEFT)); zoomCombo = new JComboBox(new Object[] { new ZoomToFitAction(), new ZoomAction(20), new ZoomAction(30), new ZoomAction(50), new ZoomAction(75), new ZoomAction(100), new ZoomAction(120), new ZoomAction(150) }); zoomCombo.setMaximumSize(zoomCombo.getPreferredSize()); zoomCombo.setEditable(true); zoomCombo.setSelectedIndex(5); // 100% zoomCombo.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { JComboBox combo = (JComboBox) e.getSource(); Object s = combo.getSelectedItem(); if (s instanceof Action) { ((Action) s).actionPerformed(e); } else if (s instanceof String) { String zs = (String) s; try { double zf = Double.parseDouble(zs); setZoomFactor(zf); } catch (Exception ex) { // Ignore bad input } } } }); toolbar.add (zoomCombo); // toolbar.addSeparator(); // toolbar.add (new CenterAction()); } void addFileActions() { toolbar.addSeparator(); toolbar.add (new LoadPwyAction(this, PWY_OLD)); toolbar.add (new LoadPwyAction(this, PWY_NEW)); } GpmlDiffWindow (RootPaneContainer parent) { setLayout (new BorderLayout ()); JPanel subpanel = new JPanel(); subpanel.setLayout (new BoxLayout(subpanel, BoxLayout.X_AXIS)); add (subpanel, BorderLayout.CENTER); toolbar = new JToolBar(); addToolbarActions(); add(toolbar, BorderLayout.PAGE_START); glassPane = new GlassPane(this); parent.setGlassPane (glassPane); glassPane.setVisible(true); Toolkit.getDefaultToolkit().addAWTEventListener( glassPane, AWTEvent.MOUSE_MOTION_EVENT_MASK | AWTEvent.MOUSE_EVENT_MASK); for (int i = 0; i < 2; ++i) { pwyPane[i] = new JScrollPane(); } subpanel.add (pwyPane[PWY_OLD]); pwyPane[PWY_OLD].setPreferredSize (new Dimension (400, 300)); subpanel.add (pwyPane[PWY_NEW]); pwyPane[PWY_NEW].setPreferredSize (new Dimension (400, 300)); glassPane.setViewPorts (pwyPane[0].getViewport(), pwyPane[1].getViewport()); validate(); } public void vPathwayEvent (VPathwayEvent e) { if (e.getType() == VPathwayEventType.ELEMENT_CLICKED_DOWN) { if (outputter != null) { PanelOutputter.ModData mod = outputter.modsByElt.get (e.getAffectedElement()); if (mod != null) { switch (mod.getType()) { case CHANGED: glassPane.setModifyHint (mod.getHints(), mod.getX1(), mod.getY1(), mod.getX2(), mod.getY2()); break; case ADDED: glassPane.setAddHint (mod.getHints(), mod.getX2(), mod.getY2()); break; case REMOVED: glassPane.setRemoveHint (mod.getHints(), mod.getX1(), mod.getY1()); break; } } else { glassPane.clearHint (); } } } } int oldw = 0; int oldh = 0; public void updatePreferredSize() { int v0w = (view[0] == null) ? 0 : view[0].getVWidth(); int v1w = (view[1] == null) ? 0 : view[1].getVWidth(); int v0h = (view[0] == null) ? 0 : view[0].getVHeight(); int v1h = (view[1] == null) ? 0 : view[1].getVHeight(); int maxVw = Math.max(v0w, v1w); int maxVh = Math.max(v0h, v1h); if (oldw == maxVw && oldh == maxVh) return; oldw = maxVw; oldh = maxVh; for (int i = 0; i < 2; ++i) { wrapper[i].setPreferredSize(new Dimension(maxVw, maxVh)); wrapper[i].revalidate(); // causes scrollbars to be added / removed when appropriate } } public void mouseWheelMoved(int notches) { double newZoom; if(notches > 0) { newZoom = (view[0].getPctZoom() * 21 / 20); } else { newZoom = (view[0].getPctZoom() * 20 / 21); } setZoomFactor(newZoom); wrapper[0].redraw(); wrapper[1].redraw(); DecimalFormat df = new DecimalFormat("###.#"); zoomCombo.setSelectedItem(df.format(newZoom)+"%"); } }