4 package fr.orsay.lri.varna.applications.templateEditor;
6 import java.awt.BasicStroke;
8 import java.awt.Dimension;
9 import java.awt.Graphics;
10 import java.awt.Graphics2D;
11 import java.awt.Point;
12 import java.awt.Polygon;
13 import java.awt.Rectangle;
14 import java.awt.RenderingHints;
15 import java.awt.Stroke;
16 import java.awt.geom.Point2D;
18 import java.util.ArrayList;
19 import java.util.HashMap;
20 import java.util.Hashtable;
21 import java.util.Iterator;
22 import java.util.List;
24 import java.util.Stack;
26 import javax.swing.JOptionPane;
27 import javax.swing.JPanel;
28 import javax.swing.undo.UndoManager;
30 import fr.orsay.lri.varna.applications.templateEditor.GraphicalTemplateElement.RelativePosition;
31 import fr.orsay.lri.varna.controlers.ControleurMolette;
32 import fr.orsay.lri.varna.exceptions.ExceptionInvalidRNATemplate;
33 import fr.orsay.lri.varna.exceptions.ExceptionXmlLoading;
34 import fr.orsay.lri.varna.models.templates.RNATemplate;
35 import fr.orsay.lri.varna.models.templates.RNATemplate.RNATemplateElement;
36 import fr.orsay.lri.varna.models.templates.RNATemplate.RNATemplateHelix;
37 import fr.orsay.lri.varna.models.templates.RNATemplate.RNATemplateUnpairedSequence;
38 import fr.orsay.lri.varna.models.templates.RNATemplate.RNATemplateElement.EdgeEndPoint;
47 public class TemplatePanel extends JPanel {
51 private static final long serialVersionUID = 3162771335587335679L;
54 private ArrayList<GraphicalTemplateElement> _RNAComponents;
55 private ArrayList<Connection> _RNAConnections;
56 private Hashtable<Couple<GraphicalTemplateElement,GraphicalTemplateElement.RelativePosition>,Connection> _helixToConnection;
57 private TemplateEditorPanelUI _ui;
59 private RNATemplate _template;
62 private static Color[] BackgroundColors = {Color.blue,Color.red,Color.cyan,Color.green,Color.lightGray,Color.magenta,Color.PINK};
64 private int _nextBackgroundColor = 0;
66 private static double scaleFactorDefault = 0.7;
67 private double scaleFactor = scaleFactorDefault;
70 private TemplateEditor _editor;
73 public double getScaleFactor() {
77 public void setScaleFactor(double scaleFactor) {
78 this.scaleFactor = scaleFactor;
81 public Color nextBackgroundColor()
83 Color c = BackgroundColors[_nextBackgroundColor++];
84 _nextBackgroundColor = _nextBackgroundColor % BackgroundColors.length;
85 return new Color(c.getRed(),c.getBlue(),c.getGreen(),50);
88 public TemplatePanel(TemplateEditor parent)
94 public RNATemplate getTemplate()
99 List<GraphicalTemplateElement> getRNAComponents() {
100 return _RNAComponents;
105 _ui = new TemplateEditorPanelUI(this);
107 _RNAComponents = new ArrayList<GraphicalTemplateElement>();
108 _RNAConnections = new ArrayList<Connection>();
109 _helixToConnection = new Hashtable<Couple<GraphicalTemplateElement,GraphicalTemplateElement.RelativePosition>,Connection>();
111 _template = new RNATemplate();
113 setBackground(Color.WHITE);
114 MouseControler mc = new MouseControler(this,_ui);
115 addMouseListener(mc);
116 addMouseMotionListener(mc);
117 addMouseWheelListener(mc);
118 _solidStroke = new BasicStroke(1.5f, BasicStroke.CAP_ROUND,BasicStroke.JOIN_ROUND, 3.0f);
119 float[] dash = { 5.0f, 5.0f };
120 _dashedStroke = new BasicStroke(1.5f, BasicStroke.CAP_ROUND,BasicStroke.JOIN_ROUND, 3.0f, dash, 0);
123 public void addUndoableEditListener(UndoManager manager)
125 _ui.addUndoableEditListener(manager);
128 public TemplateEditorPanelUI getTemplateUI()
134 public void flip(Helix h)
139 public void addElement(GraphicalTemplateElement h)
141 _RNAComponents.add(h);
144 public void removeElement(GraphicalTemplateElement h)
146 _RNAComponents.remove(h);
148 _template.removeElement(h.getTemplateElement());
149 } catch (ExceptionInvalidRNATemplate e) {
150 //e.printStackTrace();
154 private GraphicalTemplateElement _selected = null;
156 public GraphicalTemplateElement getSelected()
161 public void setSelected(GraphicalTemplateElement sel)
164 if (_selected instanceof Helix) {
165 _editor.flipButtonEnable();
167 _editor.flipButtonDisable();
171 Helix.RelativePosition _relpos = Helix.RelativePosition.RP_OUTER;
173 public void setSelectedEdge(Helix.RelativePosition rel)
178 public void unselectEdge(Helix.RelativePosition rel)
183 Point2D.Double _mousePos = new Point2D.Double();
185 public void setPointerPos(Point2D.Double p)
190 public void Unselect()
192 _editor.flipButtonDisable();
196 public GraphicalTemplateElement getElement(RNATemplateElement t)
198 for(GraphicalTemplateElement t2: _RNAComponents)
199 if (t==t2.getTemplateElement())
205 public GraphicalTemplateElement getElementAt(double x, double y)
207 return getElementAt(x, y, null);
209 public GraphicalTemplateElement getElementAt(double x, double y, GraphicalTemplateElement excluded)
211 GraphicalTemplateElement h = null;
212 for (int i=0; i<_RNAComponents.size();i++)
214 GraphicalTemplateElement h2 = _RNAComponents.get(i);
216 (h2.getRelativePosition(x, y)== Helix.RelativePosition.RP_CONNECT_END3)
217 || (h2.getRelativePosition(x, y)== Helix.RelativePosition.RP_CONNECT_END5)
218 || (h2.getRelativePosition(x, y)== Helix.RelativePosition.RP_CONNECT_START3)
219 || (h2.getRelativePosition(x, y)== Helix.RelativePosition.RP_CONNECT_START5))
226 { h = getElementCloseTo(x, y, excluded);};
230 public GraphicalTemplateElement getElementCloseTo(double x, double y)
232 return getElementCloseTo(x, y, null);
234 public GraphicalTemplateElement getElementCloseTo(double x, double y, GraphicalTemplateElement excluded)
236 GraphicalTemplateElement h = null;
237 for (int i=0; i<_RNAComponents.size();i++)
239 GraphicalTemplateElement h2 = _RNAComponents.get(i);
240 if ((h2.getRelativePosition(x, y) != Helix.RelativePosition.RP_OUTER)
249 public void addConnection(Connection c)
251 _RNAConnections.add(c);
252 _helixToConnection.put(new Couple<GraphicalTemplateElement,GraphicalTemplateElement.RelativePosition>(c._h1,c._edge1), c);
253 _helixToConnection.put(new Couple<GraphicalTemplateElement,GraphicalTemplateElement.RelativePosition>(c._h2,c._edge2), c);
255 c._h1.attach(c._h2, c._edge1, c._edge2);
256 c._h2.attach(c._h1, c._edge2, c._edge1);
257 } catch (ExceptionInvalidRNATemplate e) {
258 System.out.println(e.toString());// TODO Auto-generated catch block
263 public Connection addConnection(GraphicalTemplateElement h1, GraphicalTemplateElement.RelativePosition edge1,GraphicalTemplateElement h2, GraphicalTemplateElement.RelativePosition edge2)
265 if ((h1!=h2)&&(getPartner(h1,edge1)==null)&&(getPartner(h2,edge2)==null))
267 Connection c = new Connection(h1,edge1,h2,edge2);
275 * When there is already a connection in the underlying RNATemplate
276 * and we want to create one at the graphical level.
278 public void addGraphicalConnection(GraphicalTemplateElement h1, GraphicalTemplateElement.RelativePosition edge1,GraphicalTemplateElement h2, GraphicalTemplateElement.RelativePosition edge2) {
279 //System.out.println("Connecting " + h1 + " " + edge1 + " to " + h2 + " " + edge2);
280 Connection c = new Connection(h1,edge1,h2,edge2);
281 _RNAConnections.add(c);
282 _helixToConnection.put(new Couple<GraphicalTemplateElement,GraphicalTemplateElement.RelativePosition>(c._h1,c._edge1), c);
283 _helixToConnection.put(new Couple<GraphicalTemplateElement,GraphicalTemplateElement.RelativePosition>(c._h2,c._edge2), c);
284 c._h1.graphicalAttach(c._h2, c._edge1, c._edge2);
285 c._h2.graphicalAttach(c._h1, c._edge2, c._edge1);
289 public void removeConnection(Connection c)
291 _RNAConnections.remove(c);
292 _helixToConnection.remove(new Couple<GraphicalTemplateElement,GraphicalTemplateElement.RelativePosition>(c._h1,c._edge1));
293 _helixToConnection.remove(new Couple<GraphicalTemplateElement,GraphicalTemplateElement.RelativePosition>(c._h2,c._edge2));
294 System.out.println("[A]"+c);
295 c._h1.detach(c._edge1);
298 public boolean isInCycle(GraphicalTemplateElement el, GraphicalTemplateElement.RelativePosition edge)
300 Stack<Couple<GraphicalTemplateElement,GraphicalTemplateElement.RelativePosition> > p = new Stack<Couple<GraphicalTemplateElement,GraphicalTemplateElement.RelativePosition>>();
301 Hashtable<Couple<GraphicalTemplateElement,GraphicalTemplateElement.RelativePosition>,Integer> alreadySeen = new Hashtable<Couple<GraphicalTemplateElement,GraphicalTemplateElement.RelativePosition>,Integer>();
302 p.add(new Couple<GraphicalTemplateElement,GraphicalTemplateElement.RelativePosition>(el,edge));
305 Couple<GraphicalTemplateElement,GraphicalTemplateElement.RelativePosition> c2 = p.pop();
306 if (alreadySeen.containsKey(c2))
312 alreadySeen.put(c2, new Integer(1));
314 GraphicalTemplateElement.RelativePosition next = c2.first.getConnectedEdge(c2.second);
315 Couple<GraphicalTemplateElement,GraphicalTemplateElement.RelativePosition> otherEnd = new Couple<GraphicalTemplateElement,GraphicalTemplateElement.RelativePosition>(c2.first,next);
316 if (!alreadySeen.containsKey(otherEnd))
322 Couple<GraphicalTemplateElement,GraphicalTemplateElement.RelativePosition> child = getPartner(c2.first,c2.second);
334 private static Color[] _colors = {Color.gray,Color.pink,Color.cyan,Color.RED,Color.green,Color.orange};
336 public static Color getIndexedColor(int n)
338 return _colors[n%_colors.length];
341 public HashMap<Couple<GraphicalTemplateElement,GraphicalTemplateElement.RelativePosition>,Integer> buildConnectedComponents()
343 HashMap<Couple<GraphicalTemplateElement,GraphicalTemplateElement.RelativePosition>,Integer> alreadySeen = new HashMap<Couple<GraphicalTemplateElement,GraphicalTemplateElement.RelativePosition>,Integer>();
344 int numConnectedComponents = 0;
345 for (GraphicalTemplateElement el : this._RNAComponents)
347 for (GraphicalTemplateElement.RelativePosition edge : el.getConnectedEdges())
349 Couple<GraphicalTemplateElement,GraphicalTemplateElement.RelativePosition> c = new Couple<GraphicalTemplateElement,GraphicalTemplateElement.RelativePosition>(el,edge);
350 if (!alreadySeen.containsKey(c))
352 Stack<Couple<GraphicalTemplateElement,GraphicalTemplateElement.RelativePosition> > p = new Stack<Couple<GraphicalTemplateElement,GraphicalTemplateElement.RelativePosition>>();
354 p.add(new Couple<GraphicalTemplateElement,GraphicalTemplateElement.RelativePosition>(el,el.getConnectedEdge(edge)));
357 Couple<GraphicalTemplateElement,GraphicalTemplateElement.RelativePosition> c2 = p.pop();
358 if (!alreadySeen.containsKey(c2))
360 //System.out.println(" "+numConnectedComponents+" "+c2);
361 c2.first.setMainColor(c2.second, getIndexedColor(numConnectedComponents));
362 alreadySeen.put(c2, new Integer(numConnectedComponents));
363 GraphicalTemplateElement.RelativePosition next = c2.first.getConnectedEdge(c2.second);
364 Couple<GraphicalTemplateElement,GraphicalTemplateElement.RelativePosition> otherEnd = new Couple<GraphicalTemplateElement,GraphicalTemplateElement.RelativePosition>(c2.first,next);
366 Couple<GraphicalTemplateElement,GraphicalTemplateElement.RelativePosition> child = getPartner(c2.first,c2.second);
371 numConnectedComponents += 1;
378 public boolean isInCycle(Connection c)
380 return isInCycle(c._h1,c._edge1);
383 public Couple<GraphicalTemplateElement,GraphicalTemplateElement.RelativePosition> getPartner(GraphicalTemplateElement h, GraphicalTemplateElement.RelativePosition edge)
385 Connection c = getConnection(h, edge);
388 if ((c._h1==h)&&(c._edge1==edge))
389 { return new Couple<GraphicalTemplateElement,GraphicalTemplateElement.RelativePosition>(c._h2,c._edge2); }
391 { return new Couple<GraphicalTemplateElement,GraphicalTemplateElement.RelativePosition>(c._h1,c._edge1); }
399 public Connection getConnection(GraphicalTemplateElement h, Helix.RelativePosition edge)
401 Couple<GraphicalTemplateElement,GraphicalTemplateElement.RelativePosition> target = new Couple<GraphicalTemplateElement,GraphicalTemplateElement.RelativePosition>(h,edge);
402 if (_helixToConnection.containsKey(target))
404 return _helixToConnection.get(target);
410 private boolean isConnected(Helix h, GraphicalTemplateElement.RelativePosition edge)
412 Couple<GraphicalTemplateElement,GraphicalTemplateElement.RelativePosition> partner = getPartner(h,edge);
413 return partner!=null;
417 // Aspects graphiques
419 private static final Color CYCLE_COLOR = Color.red;
420 private static final Color NON_EXISTANT_COLOR = Color.gray.brighter();
421 private static final Color CONTROL_COLOR = Color.gray.darker();
422 private static final Color BACKGROUND_COLOR = Color.white;
424 private Stroke _solidStroke;
425 private Stroke _dashedStroke;
430 private void drawConnections(Graphics2D g2d, Connection c)
432 GraphicalTemplateElement h1 = c._h1;
433 GraphicalTemplateElement.RelativePosition edge1 = c._edge1;
434 Point2D.Double p1 = h1.getEdgePosition(edge1);
435 GraphicalTemplateElement h2 = c._h2;
436 GraphicalTemplateElement.RelativePosition edge2 = c._edge2;
437 Point2D.Double p2 = h2.getEdgePosition(edge2);
440 g2d.setColor(CYCLE_COLOR);
444 g2d.setColor(GraphicalTemplateElement.BACKBONE_COLOR);
446 g2d.drawLine((int)p1.x,(int)p1.y,(int)p2.x,(int)p2.y);
450 public void paintComponent(Graphics g)
454 // Debug code to show drawing area
455 // g.setColor(Color.red);
456 // g.fillRect(0,0,getWidth(),getHeight());
457 // g.setColor(Color.white);
458 // g.fillRect(10,10,getWidth()-20,getHeight()-20);
460 g.setColor(Color.white);
461 g.fillRect(0,0,getWidth(),getHeight());
463 Graphics2D g2d = (Graphics2D) g;
464 g2d.scale(scaleFactor, scaleFactor);
465 g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
466 RenderingHints.VALUE_ANTIALIAS_ON);
468 //super.paintComponent(g2d);
470 buildConnectedComponents();
474 if (_relpos != GraphicalTemplateElement.RelativePosition.RP_OUTER)
476 Point2D.Double p = _selected.getEdgePosition(_relpos);
477 g2d.setStroke(_solidStroke);
478 g2d.drawLine((int)_mousePos.x, (int)_mousePos.y, (int)p.x, (int)p.y);
481 for (int i=0;i<_RNAConnections.size();i++)
483 Connection c = _RNAConnections.get(i);
484 drawConnections(g2d,c);
486 for (int i=0;i<_RNAComponents.size();i++)
488 GraphicalTemplateElement elem = _RNAComponents.get(i);
489 //g2d.setColor(elem.getDominantColor());
490 //g2d.fill(elem.getArea());
491 if (_selected == elem)
496 { elem.draw(g2d,false); }
501 * Get the bounding rectangle of the RNA components, always including the origin (0,0).
503 public Rectangle getBoundingRectange() {
508 for(int i=0;i<this._RNAComponents.size();i++)
510 GraphicalTemplateElement h = _RNAComponents.get(i);
511 Polygon p = h.getBoundingPolygon();
512 Rectangle r = p.getBounds();
513 minX = Math.min(minX,r.x);
514 maxX = Math.max(maxX,r.x+r.width);
515 minY = Math.min(minY,r.y);
516 maxY = Math.max(maxY,r.y+r.height);
518 Rectangle res = new Rectangle();
521 res.width = maxX - minX;
522 res.height = maxY - minY;
526 public void rescale()
528 Rectangle rect = getBoundingRectange();
530 if (rect.x < 0 || rect.y < 0) {
531 for(int i=0;i<this._RNAComponents.size();i++)
533 GraphicalTemplateElement h = _RNAComponents.get(i);
534 h.translate(rect.x<0 ? -rect.x : 0, rect.y<0 ? -rect.y : 0);
536 rect = getBoundingRectange();
539 // areaW and H are width and height, in pixels, of the drawing area
540 // including the "outside" parts available with scrolling
541 int areaW = (int) ((rect.width + 100) * scaleFactor);
542 int areaH = (int) ((rect.height + 100) * scaleFactor);
543 // make it cover at least the space visible in the window
544 //areaW = Math.max(areaW, (int) (_editor.getJp().getViewport().getViewSize().width));
545 //areaH = Math.max(areaH, (int) (_editor.getJp().getViewport().getViewSize().height));
546 //System.out.println(areaW + " x " + areaH);
547 setPreferredSize(new Dimension(areaW, areaH));
551 public void clearTemplate() {
552 loadTemplate(new RNATemplate());
556 * Load an existing RNATemplate object in this panel.
558 public void loadTemplate(RNATemplate template) {
559 _template = template;
560 _RNAComponents.clear();
561 _RNAConnections.clear();
562 _helixToConnection.clear();
564 // We need a template element -> graphical template element mapping
565 Map<RNATemplateElement, GraphicalTemplateElement> map = new HashMap<RNATemplateElement, GraphicalTemplateElement>();
567 // First, we load elements
569 Iterator<RNATemplateElement> iter = template.classicIterator();
570 while (iter.hasNext()) {
571 RNATemplateElement templateElement = iter.next();
572 if (templateElement instanceof RNATemplateHelix) {
573 RNATemplateHelix templateHelix = (RNATemplateHelix) templateElement;
574 Helix graphicalHelix = new Helix(templateHelix);
575 graphicalHelix.setDominantColor(nextBackgroundColor());
576 _RNAComponents.add(graphicalHelix);
577 map.put(templateHelix, graphicalHelix);
578 } else if (templateElement instanceof RNATemplateUnpairedSequence) {
579 RNATemplateUnpairedSequence templateSequence = (RNATemplateUnpairedSequence) templateElement;
580 UnpairedRegion graphicalSequence = new UnpairedRegion(templateSequence);
581 graphicalSequence.setDominantColor(nextBackgroundColor());
582 _RNAComponents.add(graphicalSequence);
583 map.put(templateSequence, graphicalSequence);
588 // Now, we load edges
590 Iterator<EdgeEndPoint> iter = template.makeEdgeList().iterator();
591 while (iter.hasNext()) {
592 EdgeEndPoint v1 = iter.next();
593 EdgeEndPoint v2 = v1.getOtherEndPoint();
594 GraphicalTemplateElement gte1 = map.get(v1.getElement());
595 GraphicalTemplateElement gte2 = map.get(v2.getElement());
596 RelativePosition rp1 = gte1.relativePositionFromEdgeEndPointPosition(v1.getPosition());
597 RelativePosition rp2 = gte2.relativePositionFromEdgeEndPointPosition(v2.getPosition());
598 addGraphicalConnection(gte1, rp1, gte2, rp2);
607 * Load a template from an XML file.
609 public void loadFromXmlFile(File filename) {
611 RNATemplate newTemplate = RNATemplate.fromXMLFile(filename);
612 loadTemplate(newTemplate);
613 } catch (ExceptionXmlLoading e) {
615 JOptionPane.showMessageDialog(this, e.getMessage(), "Template loading error", JOptionPane.ERROR_MESSAGE);
619 private void zoomFinish() {
624 public void zoomIn() {
629 public void zoomOut() {
634 public void zoomReset() {
635 scaleFactor = scaleFactorDefault;
639 public void zoomFit() {
640 if (_RNAComponents.isEmpty()) {
643 Rectangle rect = getBoundingRectange();
644 double areaW = (rect.width + 100);
645 double areaH = (rect.height + 100);
646 // make it cover at least the space visible in the window
648 scaleFactor = Math.min(scaleFactor, _editor.getJp().getViewport().getSize().width / areaW);
649 scaleFactor = Math.min(scaleFactor, _editor.getJp().getViewport().getSize().height / areaH);
654 public void translateView(Point trans) {
655 int newX = _editor.getJp().getHorizontalScrollBar().getValue() - trans.x;
656 int newY = _editor.getJp().getVerticalScrollBar().getValue() - trans.y;
657 newX = Math.max(0, Math.min(newX, _editor.getJp().getHorizontalScrollBar().getMaximum()));
658 newY = Math.max(0, Math.min(newY, _editor.getJp().getVerticalScrollBar().getMaximum()));
659 _editor.getJp().getHorizontalScrollBar().setValue(newX);
660 _editor.getJp().getVerticalScrollBar().setValue(newY);