2 VARNA is a tool for the automated drawing, visualization and annotation of the secondary structure of RNA, designed as a companion software for web servers and databases.
3 Copyright (C) 2008 Kevin Darty, Alain Denise and Yann Ponty.
4 electronic mail : Yann.Ponty@lri.fr
5 paper mail : LRI, bat 490 Université Paris-Sud 91405 Orsay Cedex France
7 This file is part of VARNA version 3.1.
8 VARNA version 3.1 is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License
9 as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
11 VARNA version 3.1 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
12 without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13 See the GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License along with VARNA version 3.1.
16 If not, see http://www.gnu.org/licenses.
18 package fr.orsay.lri.varna.controlers;
20 import java.awt.Component;
21 import java.awt.Point;
22 import java.awt.Rectangle;
23 import java.awt.event.MouseEvent;
24 import java.awt.event.MouseListener;
25 import java.awt.event.MouseMotionListener;
26 import java.awt.geom.Point2D;
27 import java.util.ArrayList;
28 import java.util.Collection;
29 import java.util.Vector;
31 import javax.swing.JMenu;
32 import javax.swing.JMenuItem;
33 import javax.swing.event.PopupMenuEvent;
34 import javax.swing.event.PopupMenuListener;
36 import fr.orsay.lri.varna.VARNAPanel;
37 import fr.orsay.lri.varna.exceptions.ExceptionNAViewAlgorithm;
38 import fr.orsay.lri.varna.models.annotations.TextAnnotation;
39 import fr.orsay.lri.varna.models.rna.ModeleBase;
40 import fr.orsay.lri.varna.models.rna.ModeleBaseNucleotide;
41 import fr.orsay.lri.varna.models.rna.ModeleBasesComparison;
42 import fr.orsay.lri.varna.models.rna.ModeleBP;
43 import fr.orsay.lri.varna.models.rna.RNA;
47 * Controller of the mouse click
52 public class ControleurClicMovement implements MouseListener,
53 MouseMotionListener, PopupMenuListener {
54 private VARNAPanel _vp;
55 private boolean _presenceMenuSelection;
56 private JMenu _submenuSelection;
57 public Point _spawnPoint;
58 public Point _initialPoint;
59 public Point _prevPoint;
60 public Point _currentPoint;
62 public static final double MIN_SELECTION_DISTANCE = 40.0;
63 public static final double HYSTERESIS_DISTANCE = 10.0;
65 private ModeleBase _selectedBase = null;
68 public enum MouseStates {
71 MOVE_OR_SELECT_ELEMENT,
73 SELECT_REGION_OR_UNSELECT,
79 private MouseStates _currentState = MouseStates.NONE;
83 public ControleurClicMovement(VARNAPanel _vuep) {
85 _vp.getPopup().addPopupMenuListener(this);
86 _presenceMenuSelection = false;
89 public void mouseClicked(MouseEvent arg0) {
92 public void mouseEntered(MouseEvent arg0) {
95 public void mouseExited(MouseEvent arg0) {
98 public void mousePressed(MouseEvent arg0)
101 boolean button1 = (arg0.getButton() == MouseEvent.BUTTON1);
102 boolean button2 = (arg0.getButton() == MouseEvent.BUTTON2);
103 boolean button3 = (arg0.getButton() == MouseEvent.BUTTON3);
104 boolean shift = arg0.isShiftDown();
105 boolean ctrl = arg0.isControlDown();
106 boolean alt = arg0.isAltDown();
107 _vp.removeSelectedAnnotation();
108 if (button1 && !ctrl && !alt && !shift)
110 if (_vp.isModifiable())
112 _currentState = MouseStates.MOVE_OR_SELECT_ELEMENT;
113 if (_vp.getRealCoords() != null
114 && _vp.getRealCoords().length != 0
115 && _vp.getRNA().get_listeBases().size() != 0)
117 _selectedBase = _vp.getNearestBase(arg0.getX(),arg0.getY(),false,false);
118 TextAnnotation selectedAnnotation = _vp.getNearestAnnotation(arg0.getX(), arg0.getY());
119 _initialPoint = new Point(arg0.getX(),arg0.getY());
120 _currentPoint = new Point(_initialPoint);
121 _prevPoint = new Point(_initialPoint);
122 if (_selectedBase != null)
124 if (_vp.getRNA().get_drawMode() == RNA.DRAW_MODE_RADIATE)
126 _vp.highlightSelectedBase(_selectedBase);
128 if (!_vp.getSelectionIndices().contains(_selectedBase.getIndex()))
130 _vp.highlightSelectedBase(_selectedBase);
134 // Otherwise, keep current selection as it is and move it
140 if (selectedAnnotation != null)
142 _currentState = MouseStates.MOVE_ANNOTATION;
143 _vp.set_selectedAnnotation(selectedAnnotation);
144 _vp.highlightSelectedAnnotation();
148 _vp.clearSelection();
149 _selectedBase = null;
150 _currentState = MouseStates.SELECT_REGION_OR_UNSELECT;
151 _initialPoint = new Point(arg0.getX(),arg0.getY());
152 _prevPoint = new Point(_initialPoint);
153 _currentPoint = new Point(_initialPoint);
159 else if (button1 && ctrl && !alt && !shift)
161 _selectedBase = _vp.getNearestBase(arg0.getX(),arg0.getY(),false,false);
162 if (_selectedBase != null)
164 _vp.clearSelection();
165 _currentState = MouseStates.CREATE_BP;
166 _vp.highlightSelectedBase(_selectedBase);
167 _vp.setOriginLink(_vp.logicToPanel(_selectedBase.getCoords()));
168 _initialPoint = new Point(arg0.getX(),arg0.getY());
169 _currentPoint = new Point(_initialPoint);
172 else if (button1 && !ctrl && !alt && shift)
174 _currentState = MouseStates.SELECT_ELEMENT;
175 _initialPoint = new Point(arg0.getX(),arg0.getY());
176 _currentPoint = new Point(_initialPoint);
180 _currentState = MouseStates.POPUP_MENU;
181 if (_presenceMenuSelection) {
182 _vp.getPopupMenu().removeSelectionMenu();
184 if ((_vp.getRealCoords() != null) && _vp.getRNA().get_listeBases().size() != 0) {
185 updateNearestBase(arg0);
186 // on insere dans le menu les nouvelles options
188 if (_vp.get_selectedAnnotation() != null)
189 _vp.highlightSelectedAnnotation();
191 // affichage du popup menu
192 if (_vp.getRNA().get_drawMode() == RNA.DRAW_MODE_LINEAR) {
193 _vp.getPopup().get_rotation().setEnabled(false);
195 _vp.getPopup().get_rotation().setEnabled(true);
197 _vp.getPopup().updateDialog();
198 _vp.getPopup().show(_vp, arg0.getX(), arg0.getY());
203 public void mouseDragged(MouseEvent me) {
204 if ((_currentState == MouseStates.MOVE_OR_SELECT_ELEMENT)||(_currentState == MouseStates.MOVE_ELEMENT))
208 _currentState = MouseStates.MOVE_ELEMENT;
209 // si on deplace la souris et qu'une base est selectionnée
210 if (_selectedBase != null) {
211 if (_vp.getRNA().get_drawMode() == RNA.DRAW_MODE_RADIATE) {
212 _vp.highlightSelectedStem(_selectedBase);
213 // dans le cas radiale on deplace une helice
214 _vp.getVARNAUI().UIMoveHelixAtom(_selectedBase.getIndex(), _vp.panelToLogicPoint(new Point2D.Double(me.getX(), me.getY())));
216 // dans le cas circulaire naview ou line on deplace une base
217 _currentPoint = new Point(me.getX(), me.getY());
218 moveSelection(_prevPoint,_currentPoint);
219 _prevPoint = new Point(_currentPoint);
224 else if (_currentState == MouseStates.MOVE_ANNOTATION)
226 if (_vp.get_selectedAnnotation()!=null)
228 Point2D.Double p = _vp.panelToLogicPoint(new Point2D.Double(me.getX(), me.getY()));
229 _vp.get_selectedAnnotation().setAncrage(p.x,p.y);
233 else if ((_currentState == MouseStates.SELECT_ELEMENT)||(_currentState == MouseStates.SELECT_REGION_OR_UNSELECT))
235 if (_initialPoint.distance(me.getX(),me.getY())>HYSTERESIS_DISTANCE)
236 _currentState = MouseStates.SELECT_REGION;
238 else if (_currentState == MouseStates.SELECT_REGION)
240 _currentPoint = new Point(me.getX(),me.getY());
241 int minx = Math.min(_currentPoint.x, _initialPoint.x);
242 int miny = Math.min(_currentPoint.y, _initialPoint.y);
243 int maxx = Math.max(_currentPoint.x, _initialPoint.x);
244 int maxy = Math.max(_currentPoint.y, _initialPoint.y);
245 _vp.setSelectionRectangle(new Rectangle(minx,miny,maxx-minx,maxy-miny));
247 else if (_currentState == MouseStates.CREATE_BP)
249 if (_initialPoint.distance(me.getX(),me.getY())>HYSTERESIS_DISTANCE)
251 ModeleBase newSelectedBase = _vp.getNearestBase(me.getX(),me.getY(),false,false);
252 _vp.setHoverBase(newSelectedBase);
253 if (newSelectedBase==null)
255 _vp.setDestinationLink(new Point2D.Double(me.getX(),me.getY()));
256 _vp.clearSelection();
257 _vp.addToSelection(_selectedBase.getIndex());
261 ModeleBase mborig = _selectedBase;
262 _vp.clearSelection();
263 _vp.addToSelection(newSelectedBase.getIndex());
264 _vp.addToSelection(mborig.getIndex());
265 _vp.setDestinationLink(_vp.logicToPanel(newSelectedBase.getCoords()));
274 public void mouseReleased(MouseEvent arg0) {
275 if (arg0.getButton() == MouseEvent.BUTTON1)
277 _vp.fireBaseClicked(_selectedBase, arg0);
278 //System.out.println(""+_currentState);
280 if (_currentState == MouseStates.MOVE_ELEMENT)
282 _vp.clearSelection();
283 _selectedBase = null;
284 _vp.unlockScrolling();
285 _vp.removeSelectedAnnotation();
287 else if (_currentState == MouseStates.SELECT_REGION_OR_UNSELECT)
289 _vp.clearSelection();
290 _selectedBase = null;
291 _vp.removeSelectedAnnotation();
293 else if (_currentState == MouseStates.SELECT_ELEMENT)
295 if (_vp.getRealCoords() != null
296 && _vp.getRealCoords().length != 0
297 && _vp.getRNA().get_listeBases().size() != 0)
299 int selectedIndex = _vp.getNearestBaseIndex(arg0.getX(),arg0.getY(),false,false);
300 if (selectedIndex !=-1)
302 _vp.toggleSelection(selectedIndex);
305 _selectedBase = null;
307 else if (_currentState == MouseStates.SELECT_REGION)
309 _vp.removeSelectionRectangle();
311 else if (_currentState == MouseStates.CREATE_BP)
313 if (_initialPoint.distance(arg0.getX(),arg0.getY())>HYSTERESIS_DISTANCE)
315 int selectedIndex = _vp.getNearestBaseIndex(arg0.getX(),arg0.getY(),false,false);
316 if (selectedIndex>=0)
318 ModeleBase mb = _vp.getNearestBase(arg0.getX(),arg0.getY(),false,false);
319 ModeleBase mborig = _selectedBase;
320 ModeleBP msbp = new ModeleBP(mb,mborig);
323 _vp.getVARNAUI().UIAddBP(mb.getIndex(),mborig.getIndex(),msbp);
328 _vp.clearSelection();
333 _vp.clearSelection();
337 _currentState = MouseStates.NONE;
342 private void addMenu(MouseEvent arg0) {
344 _submenuSelection = new JMenu("Selection");
346 // ajout des option sur base
348 // ajout des option sur paire de base
349 if (_vp.getRNA().get_listeBases().get(_vp.getNearestBase())
350 .getElementStructure() != -1) {
354 // detection renflement
362 // detection d'helice
366 // Ajout de toutes bases
368 // detection d'annotation
369 detectAnnotation(arg0);
371 _vp.getPopup().addSelectionMenu(_submenuSelection);
372 _presenceMenuSelection = true;
375 private void detectAnnotation(MouseEvent arg0) {
376 if (_vp.getListeAnnotations().size() != 0) {
377 double dist = Double.MAX_VALUE;
379 Point2D.Double position;
380 for (TextAnnotation textAnnot : _vp.getListeAnnotations()) {
381 // calcul de la distance
382 position = textAnnot.getCenterPosition();
383 position = _vp.transformCoord(position);
384 d2 = Math.sqrt(Math.pow((position.x - arg0.getX()), 2)
385 + Math.pow((position.y - arg0.getY()), 2));
386 // si la valeur est inferieur au minimum actuel
388 _vp.set_selectedAnnotation(textAnnot);
392 _submenuSelection.addSeparator();
393 _vp.getPopup().addAnnotationMenu(_submenuSelection,true);
397 private void detectBulge() {
398 int indiceB = _vp.getNearestBase();
399 ArrayList<Integer> indices = _vp.getRNA().findBulge(indiceB);
400 if ((indices.size() > 0)
401 && (_vp.getRNA().getHelixCountOnLoop(_vp.getNearestBase()) == 2)) {
402 JMenu submenuBulge = new JMenu("Bulge");
403 submenuBulge.addChangeListener(new ControleurSelectionHighlight(
404 new Vector<Integer>(indices), _vp, submenuBulge));
405 submenuBulge.setActionCommand("bulge");
406 if (!_vp.isModifiable())
407 submenuBulge.setEnabled(false);
408 _vp.getPopupMenu().addColorOptions(submenuBulge);
409 _submenuSelection.add(submenuBulge);
413 private void detectHelix() {
414 int indiceH = _vp.getNearestBase();
415 ArrayList<Integer> indices = _vp.getRNA().findHelix(indiceH);
416 if (indices.size() != 0) {
418 JMenu submenuHelix = new JMenu("Helix");
419 submenuHelix.addChangeListener(new ControleurSelectionHighlight(
420 new Vector<Integer>(indices), _vp, submenuHelix));
421 submenuHelix.setActionCommand("helix");
422 if (!_vp.isModifiable())
423 submenuHelix.setEnabled(false);
424 _vp.getPopupMenu().addColorOptions(submenuHelix);
425 submenuHelix.addSeparator();
426 _vp.getPopupMenu().addAnnotationMenu(submenuHelix);
427 _submenuSelection.add(submenuHelix);
431 private void detectStem() {
432 int indiceS = _vp.getNearestBase();
433 ArrayList<Integer> indices = _vp.getRNA().findStem(indiceS);
434 if (indices.size() > 0) {
435 JMenu submenuStem = new JMenu("Stem");
436 submenuStem.addChangeListener(new ControleurSelectionHighlight(
437 new Vector<Integer>(indices), _vp, submenuStem));
438 submenuStem.setActionCommand("stem");
439 if (!_vp.isModifiable())
440 submenuStem.setEnabled(false);
441 _vp.getPopupMenu().addColorOptions(submenuStem);
442 _submenuSelection.add(submenuStem);
446 private void detect3Prime() {
448 int indice3 = _vp.getNearestBase();
449 ArrayList<Integer> indices = _vp.getRNA().find3Prime(indice3);
450 if (indices.size() != 0) {
451 JMenu submenu3Prime = new JMenu("3'");
452 submenu3Prime.addChangeListener(new ControleurSelectionHighlight(
453 new Vector<Integer>(indices), _vp, submenu3Prime));
454 submenu3Prime.setActionCommand("3'");
455 if (!_vp.isModifiable())
456 submenu3Prime.setEnabled(false);
457 _vp.getPopupMenu().addColorOptions(submenu3Prime);
458 _submenuSelection.add(submenu3Prime);
462 private void detect5Prime() {
463 int indice5 = _vp.getNearestBase();
464 ArrayList<Integer> indices = _vp.getRNA().find5Prime(indice5);
465 if (indices.size() != 0) {
466 JMenu submenu5Prime = new JMenu("5'");
467 submenu5Prime.addChangeListener(new ControleurSelectionHighlight(
468 new Vector<Integer>(indices), _vp, submenu5Prime));
469 submenu5Prime.setActionCommand("5'");
470 if (!_vp.isModifiable())
471 submenu5Prime.setEnabled(false);
472 _vp.getPopupMenu().addColorOptions(submenu5Prime);
473 _submenuSelection.add(submenu5Prime);
477 private void detectLoop() {
478 int indexL = _vp.getNearestBase();
479 if (_vp.getRNA().get_listeBases().get(indexL).getElementStructure() == -1) {
480 ArrayList<Integer> listLoop = _vp.getRNA().findLoop(indexL);
481 JMenu submenuLoop = new JMenu("Loop");
482 submenuLoop.addChangeListener(new ControleurSelectionHighlight(
483 listLoop, _vp, submenuLoop));
484 submenuLoop.setActionCommand("loop1");
485 if (!_vp.isModifiable())
486 submenuLoop.setEnabled(false);
487 _vp.getPopupMenu().addColorOptions(submenuLoop);
488 submenuLoop.addSeparator();
489 _vp.getPopupMenu().addAnnotationMenu(submenuLoop);
490 _submenuSelection.add(submenuLoop);
492 ArrayList<Integer> listLoop1 = _vp.getRNA().findLoopForward(indexL);
493 if (listLoop1.size() > 0) {
494 JMenu submenuLoop1 = new JMenu("Forward loop");
496 .addChangeListener(new ControleurSelectionHighlight(
497 listLoop1, _vp, submenuLoop1));
498 submenuLoop1.setActionCommand("loop1");
499 if (!_vp.isModifiable())
500 submenuLoop1.setEnabled(false);
501 _vp.getPopupMenu().addColorOptions(submenuLoop1);
502 submenuLoop1.addSeparator();
503 _vp.getPopupMenu().addAnnotationMenu(submenuLoop1);
504 _submenuSelection.add(submenuLoop1);
506 ArrayList<Integer> listLoop2 = _vp.getRNA()
507 .findLoopBackward(indexL);
508 if (listLoop2.size() > 0) {
509 JMenu submenuLoop2 = new JMenu("Backward loop");
511 .addChangeListener(new ControleurSelectionHighlight(
512 listLoop2, _vp, submenuLoop2));
513 submenuLoop2.setActionCommand("loop2");
514 if (!_vp.isModifiable())
515 submenuLoop2.setEnabled(false);
516 _vp.getPopupMenu().addColorOptions(submenuLoop2);
517 submenuLoop2.addSeparator();
518 _vp.getPopupMenu().addAnnotationMenu(submenuLoop2);
519 _submenuSelection.add(submenuLoop2);
524 private void addCurrent() {
525 Collection<? extends ModeleBase> mbs = _vp.getSelection().getBases();
528 JMenu submenuAll = new JMenu("Current");
529 submenuAll.addChangeListener(new ControleurSelectionHighlight(
530 mbs, _vp, submenuAll));
531 submenuAll.setActionCommand("current");
532 if (!_vp.isModifiable())
533 submenuAll.setEnabled(false);
534 _vp.getPopupMenu().addColorOptions(submenuAll);
535 _submenuSelection.add(submenuAll);
540 private void addMenuBase() {
541 JMenu submenuBase = new JMenu();
542 ModeleBase mb = _vp.getRNA().get_listeBases().get(_vp.getNearestBase());
543 if (mb instanceof ModeleBasesComparison) {
544 submenuBase.setText("Base #" + (mb.getBaseNumber()) + ":"
545 + ((ModeleBasesComparison) mb).getBases());
547 submenuBase.setText("Base #" + (mb.getBaseNumber()) + ":"
548 + ((ModeleBaseNucleotide) mb).getBase());
550 submenuBase.addChangeListener(new ControleurSelectionHighlight(mb
551 .getIndex(), _vp, submenuBase));
552 submenuBase.setActionCommand("base");
553 // option disponible seulement en mode modifiable
554 if (!_vp.isModifiable())
555 submenuBase.setEnabled(false);
557 JMenuItem baseChar = new JMenuItem("Edit base");
558 baseChar.setActionCommand("baseChar");
559 baseChar.addActionListener(_vp.getPopupMenu().get_controleurMenu());
560 submenuBase.add(baseChar);
561 _vp.getPopupMenu().addColorOptions(submenuBase);
562 submenuBase.addSeparator();
563 _vp.getPopupMenu().addAnnotationMenu(submenuBase);
564 _submenuSelection.add(submenuBase);
567 private void addAllBase() {
568 ArrayList<Integer> indices = _vp.getRNA().findAll();
569 JMenu submenuAll = new JMenu("All");
570 submenuAll.addChangeListener(new ControleurSelectionHighlight(
571 new Vector<Integer>(indices), _vp, submenuAll));
572 submenuAll.setActionCommand("all");
573 if (!_vp.isModifiable())
574 submenuAll.setEnabled(false);
575 _vp.getPopupMenu().addColorOptions(submenuAll);
576 _submenuSelection.add(submenuAll);
579 private void addMenuBasePair() {
580 int indiceBP = _vp.getNearestBase();
581 ArrayList<Integer> indices = _vp.getRNA().findPair(indiceBP);
582 ModeleBase base = _vp.getRNA()
583 .get_listeBases().get(_vp.getNearestBase());
584 if (base.getElementStructure() != -1) {
585 JMenu submenuBasePair = new JMenu();
586 ModeleBase partner = _vp
587 .getRNA().get_listeBases().get(
588 base.getElementStructure());
590 .addChangeListener(new ControleurSelectionHighlight(
591 indices, _vp, submenuBasePair));
592 submenuBasePair.setText("Base pair #("
593 + (Math.min(base.getBaseNumber(), partner
596 + (Math.max(base.getBaseNumber(), partner
597 .getBaseNumber())) + ")");
598 submenuBasePair.setActionCommand("bp");
599 // option disponible seulement en mode modifiable
600 if (!_vp.isModifiable())
601 submenuBasePair.setEnabled(false);
603 JMenuItem basepair = new JMenuItem("Edit BP");
604 basepair.setActionCommand("basepair");
605 basepair.addActionListener(_vp.getPopupMenu()
606 .get_controleurMenu());
608 _vp.getPopupMenu().addColorOptions(submenuBasePair);
609 Component[] comps = submenuBasePair.getMenuComponents();
611 for (int i = 0; i < comps.length; i++) {
612 Component c = comps[i];
613 if (c instanceof JMenuItem) {
614 JMenuItem jmi = (JMenuItem) c;
615 if (jmi.getActionCommand().contains(",BPColor")) {
621 submenuBasePair.insert(basepair, offset);
623 submenuBasePair.add(basepair);
625 _submenuSelection.add(submenuBasePair);
629 private void updateNearestBase(MouseEvent arg0) {
630 int i = _vp.getNearestBaseIndex(arg0.getX(),arg0.getY(),true,false);
632 _vp.setNearestBase(i);
638 public void mouseMoved(MouseEvent arg0) {
639 _selectedBase = _vp.getNearestBase(arg0.getX(),arg0.getY());
640 TextAnnotation selectedAnnotation = _vp.getNearestAnnotation(arg0.getX(),arg0.getY());
641 _vp.setHoverBase(_selectedBase);
642 if (_selectedBase != null)
645 else if (selectedAnnotation!=null)
647 _vp.set_selectedAnnotation(selectedAnnotation);
648 _vp.highlightSelectedAnnotation();
651 _vp.setLastSelectedPosition(new Point2D.Double(arg0.getX(),arg0.getY()));
656 private void moveSelection(Point prev, Point cur)
658 Point2D.Double p1 = _vp.panelToLogicPoint(new Point2D.Double(prev.x,prev.y));
659 Point2D.Double p2 = _vp.panelToLogicPoint(new Point2D.Double(cur.x,cur.y));
660 double dx = (p2.x - p1.x);
661 double dy = (p2.y - p1.y);
663 if (_vp.isModifiable())
667 if (_vp.getRNA().get_drawMode() == RNA.DRAW_MODE_LINEAR)
671 _vp.getVARNAUI().UIShiftBaseCoord(_vp.getSelectionIndices(), ndx, ndy);
672 _vp.fireLayoutChanged();
678 public void popupMenuCanceled(PopupMenuEvent arg0) {
681 public void popupMenuWillBecomeInvisible(PopupMenuEvent arg0) {
682 _vp.resetAnnotationHighlight();
683 _selectedBase = null;
686 public void popupMenuWillBecomeVisible(PopupMenuEvent arg0) {