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.views;
20 import java.awt.Color;
21 import java.awt.Dimension;
22 import java.awt.Graphics2D;
23 import java.awt.Point;
24 import java.awt.geom.AffineTransform;
25 import java.awt.geom.Point2D;
26 import java.awt.image.BufferedImage;
28 import java.io.FileNotFoundException;
29 import java.io.IOException;
30 import java.text.ParseException;
31 import java.util.ArrayList;
32 import java.util.Arrays;
33 import java.util.HashSet;
34 import java.util.Hashtable;
35 import java.util.Vector;
37 import javax.imageio.IIOImage;
38 import javax.imageio.ImageIO;
39 import javax.imageio.ImageWriteParam;
40 import javax.imageio.ImageWriter;
41 import javax.imageio.stream.FileImageOutputStream;
42 import javax.swing.JColorChooser;
43 import javax.swing.JFileChooser;
44 import javax.swing.JOptionPane;
45 import javax.swing.JPanel;
46 import javax.swing.filechooser.FileFilter;
47 import javax.swing.plaf.UIResource;
48 import javax.swing.undo.UndoManager;
49 import javax.swing.undo.UndoableEditSupport;
51 import fr.orsay.lri.varna.VARNAPanel;
52 import fr.orsay.lri.varna.applications.FileNameExtensionFilter;
53 import fr.orsay.lri.varna.applications.VARNAPrinter;
54 import fr.orsay.lri.varna.exceptions.ExceptionExportFailed;
55 import fr.orsay.lri.varna.exceptions.ExceptionFileFormatOrSyntax;
56 import fr.orsay.lri.varna.exceptions.ExceptionJPEGEncoding;
57 import fr.orsay.lri.varna.exceptions.ExceptionLoadingFailed;
58 import fr.orsay.lri.varna.exceptions.ExceptionNAViewAlgorithm;
59 import fr.orsay.lri.varna.exceptions.ExceptionNonEqualLength;
60 import fr.orsay.lri.varna.exceptions.ExceptionPermissionDenied;
61 import fr.orsay.lri.varna.exceptions.ExceptionUnmatchedClosingParentheses;
62 import fr.orsay.lri.varna.exceptions.ExceptionWritingForbidden;
63 import fr.orsay.lri.varna.factories.RNAFactory;
64 import fr.orsay.lri.varna.models.FullBackup;
65 import fr.orsay.lri.varna.models.VARNAConfig;
66 import fr.orsay.lri.varna.models.VARNAEdits;
67 import fr.orsay.lri.varna.models.annotations.ChemProbAnnotation;
68 import fr.orsay.lri.varna.models.annotations.HighlightRegionAnnotation;
69 import fr.orsay.lri.varna.models.annotations.TextAnnotation;
70 import fr.orsay.lri.varna.models.rna.ModeleBP;
71 import fr.orsay.lri.varna.models.rna.ModeleBase;
72 import fr.orsay.lri.varna.models.rna.ModeleBaseNucleotide;
73 import fr.orsay.lri.varna.models.rna.ModeleBasesComparison;
74 import fr.orsay.lri.varna.models.rna.RNA;
83 * JavaScript cannot wait on a thread and so cannot wait for the result
84 * of a customized modal JOptionPane.
86 * Instead, we make any JPanel that is placed in the JOptionPane
87 * implement Runnable. In Java, we simply run that runnable here when an OK
88 * response is delivered; in JavaScript, we run the runnable within the
89 * JavaScript version of JOptionPane as an asynchronous callback.
91 * Same for a CANCEL, CLOSED, ERROR, or FINAL response.
93 * Note that for simple error or warning messages, this is not required; they will use simple HTML5 messages.
96 public Runnable okBtnCallback, cancelBtnCallback, closeBtnCallback, errorCallback, finalCallback, noBtnCallback, objectCallback;
98 public Object dialogReturnValue;
100 public Object getDialogReturnValue() {
101 return dialogReturnValue;
104 public Throwable dialogError;
112 public Throwable getDialogError() {
120 * Initiate a message dialog with callback for OK and close.
122 * @param messagePanel
127 private void showMessageDialog(Object messagePanel, String title, int messageType, Runnable ok, Runnable close) {
129 closeBtnCallback = close;
130 JOptionPane.showMessageDialog(_vp, messagePanel, title, messageType);
139 * Initiate a confirm dialog with callbacks.
145 * @param close_final_error optional close,finally,error
147 public void showConfirmDialog(JPanel optionPanel, String title, Runnable ok, Runnable cancel, Runnable... close_final_error) {
149 cancelBtnCallback = cancel;
150 closeBtnCallback = (close_final_error.length > 0 ? close_final_error[0] : null);
151 finalCallback = (close_final_error.length > 1 ? close_final_error[1] : null);
152 errorCallback = (close_final_error.length > 2 ? close_final_error[2] : null);
153 onDialogReturn(JOptionPane.showConfirmDialog(_vp, optionPanel, title, JOptionPane.OK_CANCEL_OPTION));
159 * Initiate an input dialog with callbacks.
162 * @param initialValue
164 * @param close_final_error optional [close,finally,error]
166 public void showInputDialog(String message, Object initialValue, Runnable input, Runnable... close_final_error) {
167 objectCallback = input;
168 closeBtnCallback = (close_final_error.length > 0 ? close_final_error[0] : null);
169 finalCallback = (close_final_error.length > 1 ? close_final_error[1] : null);
170 errorCallback = (close_final_error.length > 2 ? close_final_error[2] : null);
171 onDialogReturn(JOptionPane.showInputDialog(_vp, message, initialValue));
177 * Initiate an color chooser dialog with callbacks.
180 * @param initialValue
182 * @param close_final_error optional [close,finally,error]
184 public void showColorDialog(String message, Object initialValue, Runnable ret) {
185 objectCallback = ret;
186 onDialogReturn(JColorChooser.showDialog(_vp, message, (Color) initialValue));
193 * A general method to handle all the showInputDialog, JFileChooser, and JColorChooser callbacks from all the VueXXX classes.
195 * The initial return to be ignored is an object that is an instanceof UIResource.
198 public void onDialogReturn(Object value) {
199 dialogReturnValue = value;
200 if (objectCallback != null && !(value instanceof UIResource))
201 objectCallback.run();
207 * A general method to handle all the showConfirmDialog callbacks from all
208 * the VueXXX classes.
210 * The initial return to be ignored is NaN, testable as
211 * value != Math.floor(value).
214 public void onDialogReturn(int value) {
217 case JOptionPane.OK_OPTION | JOptionPane.YES_OPTION:
218 if (okBtnCallback != null)
221 case JOptionPane.NO_OPTION:
222 if (noBtnCallback != null)
225 case JOptionPane.CANCEL_OPTION:
226 if (cancelBtnCallback != null)
227 cancelBtnCallback.run();
229 case JOptionPane.CLOSED_OPTION:
230 if (closeBtnCallback != null)
231 closeBtnCallback.run();
234 } catch (Throwable e) {
236 if (errorCallback != null)
239 if (value != Math.floor(value)) {
240 // asynchronous deferred
243 if (finalCallback != null)
245 okBtnCallback = noBtnCallback = cancelBtnCallback = closeBtnCallback = errorCallback = objectCallback = null;
251 protected VARNAPanel _vp;
252 private File _fileChooserDirectory = null;
253 private UndoableEditSupport _undoableEditSupport;
255 public VueUI(VARNAPanel vp) {
257 _undoableEditSupport = new UndoableEditSupport(_vp);
260 public void addUndoableEditListener(UndoManager manager) {
261 _undoableEditSupport.addUndoableEditListener(manager);
264 public void UIToggleColorMap() {
265 if (_vp.isModifiable()) {
266 _vp.setColorMapVisible(!_vp.getColorMapVisible());
271 public void UIToggleDrawBackbone() {
272 if (_vp.isModifiable()) {
273 _vp.setDrawBackbone(!_vp.getDrawBackbone());
278 public Hashtable<Integer, Point2D.Double> backupAllCoords() {
279 Hashtable<Integer, Point2D.Double> tmp = new Hashtable<Integer, Point2D.Double>();
280 for (int i = 0; i < _vp.getRNA().getSize(); i++) {
281 tmp.put(i, _vp.getRNA().getCoords(i));
286 public void UIToggleFlatExteriorLoop() {
287 if (_vp.isModifiable()
288 && _vp.getRNA().get_drawMode() == RNA.DRAW_MODE_RADIATE) {
289 Hashtable<Integer, Point2D.Double> bck = backupAllCoords();
290 _undoableEditSupport.postEdit(new VARNAEdits.RedrawEdit(
291 RNA.DRAW_MODE_RADIATE, _vp, !_vp.getFlatExteriorLoop()));
292 _vp.setFlatExteriorLoop(!_vp.getFlatExteriorLoop());
294 _vp.drawRNA(_vp.getRNA(), RNA.DRAW_MODE_RADIATE);
296 _vp.fireLayoutChanged(bck);
300 public void UIRadiate() {
301 if (_vp.isModifiable()) {
302 Hashtable<Integer, Point2D.Double> bck = backupAllCoords();
303 _undoableEditSupport.postEdit(new VARNAEdits.RedrawEdit(
304 RNA.DRAW_MODE_RADIATE, _vp));
306 _vp.drawRNA(_vp.getRNA(), RNA.DRAW_MODE_RADIATE);
308 _vp.fireLayoutChanged(bck);
312 public void UIMOTIFView() {
313 if (_vp.isModifiable()) {
314 Hashtable<Integer, Point2D.Double> bck = backupAllCoords();
315 _undoableEditSupport.postEdit(new VARNAEdits.RedrawEdit(
316 RNA.DRAW_MODE_MOTIFVIEW, _vp));
318 _vp.drawRNA(_vp.getRNA(), RNA.DRAW_MODE_MOTIFVIEW);
320 _vp.fireLayoutChanged(bck);
324 public void UILine() {
325 if (_vp.isModifiable()) {
326 Hashtable<Integer, Point2D.Double> bck = backupAllCoords();
327 _undoableEditSupport.postEdit(new VARNAEdits.RedrawEdit(
328 RNA.DRAW_MODE_LINEAR, _vp));
330 _vp.drawRNA(_vp.getRNA(), RNA.DRAW_MODE_LINEAR);
332 _vp.fireLayoutChanged(bck);
336 public void UICircular() {
337 if (_vp.isModifiable()) {
338 Hashtable<Integer, Point2D.Double> bck = backupAllCoords();
339 _undoableEditSupport.postEdit(new VARNAEdits.RedrawEdit(
340 RNA.DRAW_MODE_CIRCULAR, _vp));
342 _vp.drawRNA(_vp.getRNA(), RNA.DRAW_MODE_CIRCULAR);
344 _vp.fireLayoutChanged(bck);
348 public void UINAView() {
349 if (_vp.isModifiable()) {
350 Hashtable<Integer, Point2D.Double> bck = backupAllCoords();
351 _undoableEditSupport.postEdit(new VARNAEdits.RedrawEdit(
352 RNA.DRAW_MODE_NAVIEW, _vp));
354 _vp.drawRNA(_vp.getRNA(), RNA.DRAW_MODE_NAVIEW);
356 _vp.fireLayoutChanged(bck);
360 public void UIVARNAView() {
361 if (_vp.isModifiable()) {
362 Hashtable<Integer, Point2D.Double> bck = backupAllCoords();
363 _undoableEditSupport.postEdit(new VARNAEdits.RedrawEdit(
364 RNA.DRAW_MODE_VARNA_VIEW, _vp));
366 _vp.drawRNA(_vp.getRNA(), RNA.DRAW_MODE_VARNA_VIEW);
368 _vp.fireLayoutChanged(bck);
372 public void UIReset() {
373 if (_vp.isModifiable()) {
374 Hashtable<Integer, Point2D.Double> bck = backupAllCoords();
375 _undoableEditSupport.postEdit(new VARNAEdits.RedrawEdit(_vp
376 .getRNA().get_drawMode(), _vp));
378 _vp.drawRNA(_vp.getRNA(), _vp.getRNA().get_drawMode());
380 _vp.fireLayoutChanged(bck);
384 private void savePath(JFileChooser jfc) {
385 _fileChooserDirectory = jfc.getCurrentDirectory();
388 private void loadPath(JFileChooser jfc) {
389 if (_fileChooserDirectory != null) {
390 jfc.setCurrentDirectory(_fileChooserDirectory);
394 public void UIChooseRNAs(ArrayList<RNA> rnas) {
395 if (rnas.size() > 5) {
396 final VueRNAList vrna = new VueRNAList(rnas);
397 Runnable ok = new Runnable() {
401 for (RNA r : vrna.getSelectedRNAs()) {
403 r.drawRNA(_vp.getConfig());
404 } catch (ExceptionNAViewAlgorithm e) {
413 showConfirmDialog(vrna, "Select imported sequence/structures", ok, null);
417 r.drawRNA(_vp.getConfig());
418 } catch (ExceptionNAViewAlgorithm e) {
427 public void UIFile() throws ExceptionNonEqualLength {
428 if (_vp.isModifiable()) {
429 JFileChooser fc = new JFileChooser();
430 fc.setFileSelectionMode(JFileChooser.OPEN_DIALOG);
431 fc.setDialogTitle("Open...");
433 if (fc.showOpenDialog(_vp) == JFileChooser.APPROVE_OPTION) {
436 String path = fc.getSelectedFile().getAbsolutePath();
437 if (!path.toLowerCase().endsWith(".varna")) {
438 ArrayList<RNA> rnas = RNAFactory.loadSecStr(path);
439 if (rnas.isEmpty()) {
440 throw new ExceptionFileFormatOrSyntax("No RNA could be parsed from that source.");
445 FullBackup bck = _vp.loadSession(fc.getSelectedFile()); // was path
447 } catch (ExceptionExportFailed e1) {
449 } catch (ExceptionPermissionDenied e1) {
451 } catch (ExceptionLoadingFailed e1) {
453 } catch (ExceptionFileFormatOrSyntax e1) {
455 } catch (ExceptionUnmatchedClosingParentheses e1) {
457 } catch (FileNotFoundException e) {
464 public void UISetColorMapStyle() {
465 final VueColorMapStyle vcms = new VueColorMapStyle(_vp);
466 Runnable ok = new Runnable() {
470 _vp.setColorMap(vcms.getColorMap());
474 Runnable cancel = new Runnable() {
478 vcms.cancelChanges();
482 showConfirmDialog(vcms, "Choose color map style", ok, cancel, cancel, null, null);
485 public void UILoadColorMapValues() {
486 final VueLoadColorMapValues vcmv = new VueLoadColorMapValues(_vp);
487 Runnable ok = new Runnable(){
491 _vp.setColorMapVisible(true);
493 _vp.readValues(vcmv.getReader());
494 } catch (IOException e) {
495 _vp.errorDialog((Exception) getDialogError());
500 showConfirmDialog(vcmv, "Load base values", ok, null);
503 public void UISetColorMapValues() {
504 final VueBaseValues vbv = new VueBaseValues(_vp);
505 Runnable cancel = new Runnable() {
513 showConfirmDialog(vbv, "Choose base values", null, cancel);
516 public void UIManualInput() throws ParseException, ExceptionNonEqualLength {
517 if (_vp.isModifiable()) {
518 final VueManualInput manualInput = new VueManualInput(_vp);
519 Runnable ok = new Runnable() {
523 if (_vp.getRNA().getSize() == 0) {
528 VARNAConfig cfg = new VARNAConfig();
529 r.setRNA(manualInput.getTseq().getText(), manualInput.getTstr().getText());
530 r.drawRNA(_vp.getRNA().get_drawMode(), cfg);
531 _vp.drawRNAInterpolated(r);
533 } catch (ExceptionFileFormatOrSyntax e) {
534 // TODO Auto-generated catch block
536 } catch (ExceptionNAViewAlgorithm e) {
537 // TODO Auto-generated catch block
539 } catch (ExceptionUnmatchedClosingParentheses e) {
540 // TODO Auto-generated catch block
546 showConfirmDialog(manualInput.getPanel(), "Input sequence/structure", ok, null);
550 public void UISetTitle() {
551 if (_vp.isModifiable()) {
552 Runnable input = new Runnable() {
556 String res = (String) getDialogReturnValue();
565 showInputDialog("Input title", _vp.getTitle(), input);
569 public void UISetColorMapCaption() {
570 if (_vp.isModifiable()) {
571 Runnable input = new Runnable() {
575 String res = (String) getDialogReturnValue();
577 _vp.setColorMapCaption(res);
583 showInputDialog("Input new color map caption", _vp.getColorMapCaption(), input);
587 public void UISetBaseCharacter() {
588 if (_vp.isModifiable()) {
589 final int i = _vp.getNearestBase();
591 if (_vp.isComparisonMode()) {
593 Runnable input = new Runnable() {
597 String res = (String) getDialogReturnValue();
599 ModeleBasesComparison mb = (ModeleBasesComparison) _vp.getRNA().get_listeBases().get(i);
600 String bck = mb.getBase1() + "|" + mb.getBase2();
601 mb.setBase1(((res.length() > 0) ? res.charAt(0) : ' '));
602 mb.setBase2(((res.length() > 1) ? res.charAt(1) : ' '));
604 _vp.fireSequenceChanged(i, bck, res);
612 showInputDialog("Input base", ((ModeleBasesComparison) _vp.getRNA().get_listeBases().get(i)).getBases(), input);
616 Runnable input = new Runnable() {
620 String res = (String) getDialogReturnValue();
622 ModeleBaseNucleotide mb = (ModeleBaseNucleotide) _vp.getRNA().get_listeBases().get(i);
623 String bck = mb.getBase();
626 _vp.fireSequenceChanged(i, bck, res);
631 showInputDialog("Input base", ((ModeleBaseNucleotide) _vp.getRNA().get_listeBases().get(i)).getBase(), input);
636 FileNameExtensionFilter _varnaFilter = new FileNameExtensionFilter(
637 "VARNA Session File", "varna", "VARNA");
638 FileNameExtensionFilter _bpseqFilter = new FileNameExtensionFilter(
639 "BPSeq (CRW) File", "bpseq", "BPSEQ");
640 FileNameExtensionFilter _ctFilter = new FileNameExtensionFilter(
641 "Connect (MFold) File", "ct", "CT");
642 FileNameExtensionFilter _dbnFilter = new FileNameExtensionFilter(
643 "Dot-bracket notation (Vienna) File", "dbn", "DBN", "faa", "FAA");
645 FileNameExtensionFilter _jpgFilter = new FileNameExtensionFilter(
646 "JPEG Picture", "jpeg", "jpg", "JPG", "JPEG");
647 FileNameExtensionFilter _pngFilter = new FileNameExtensionFilter(
648 "PNG Picture", "png", "PNG");
649 FileNameExtensionFilter _epsFilter = new FileNameExtensionFilter(
650 "EPS File", "eps", "EPS");
651 FileNameExtensionFilter _svgFilter = new FileNameExtensionFilter(
652 "SVG Picture", "svg", "SVG");
653 FileNameExtensionFilter _xfigFilter = new FileNameExtensionFilter(
654 "XFig Diagram", "fig", "xfig", "FIG", "XFIG");
655 FileNameExtensionFilter _tikzFilter = new FileNameExtensionFilter(
656 "PGF/Tikz diagram", "tex", "pgf");
658 public void UIExport() throws ExceptionExportFailed,
659 ExceptionPermissionDenied, ExceptionWritingForbidden,
660 ExceptionJPEGEncoding {
661 ArrayList<FileNameExtensionFilter> v = new ArrayList<FileNameExtensionFilter>();
668 String dest = UIChooseOutputFile(v);
670 String extLower = dest.substring(dest.lastIndexOf('.'))
672 // System.out.println(extLower);
673 if (extLower.equals(".eps")) {
674 _vp.getRNA().saveRNAEPS(dest, _vp.getConfig());
675 } else if (extLower.equals(".svg")) {
676 _vp.getRNA().saveRNASVG(dest, _vp.getConfig());
677 } else if (extLower.equals(".fig") || extLower.equals(".xfig")) {
678 _vp.getRNA().saveRNAXFIG(dest, _vp.getConfig());
679 } else if (extLower.equals(".pgf") || extLower.equals(".tex")) {
680 _vp.getRNA().saveRNATIKZ(dest, _vp.getConfig());
681 } else if (extLower.equals(".png")) {
683 } else if (extLower.equals(".jpg") || extLower.equals(".jpeg")) {
689 public void UIExportJPEG() throws ExceptionJPEGEncoding,
690 ExceptionExportFailed {
691 String dest = UIChooseOutputFile(_jpgFilter);
697 public void UIPrint() {
698 VARNAPrinter.printComponent(_vp);
701 public void UIExportPNG() throws ExceptionExportFailed {
702 String dest = UIChooseOutputFile(_pngFilter);
708 public void UIExportXFIG() throws ExceptionExportFailed,
709 ExceptionWritingForbidden {
710 String dest = UIChooseOutputFile(_xfigFilter);
712 _vp.getRNA().saveRNAXFIG(dest, _vp.getConfig());
716 public void UIExportTIKZ() throws ExceptionExportFailed,
717 ExceptionWritingForbidden {
718 String dest = UIChooseOutputFile(_tikzFilter);
720 _vp.getRNA().saveRNATIKZ(dest, _vp.getConfig());
724 public void UIExportEPS() throws ExceptionExportFailed,
725 ExceptionWritingForbidden {
726 String dest = UIChooseOutputFile(_epsFilter);
728 _vp.getRNA().saveRNAEPS(dest, _vp.getConfig());
732 public void UIExportSVG() throws ExceptionExportFailed,
733 ExceptionWritingForbidden {
734 String dest = UIChooseOutputFile(_svgFilter);
736 _vp.getRNA().saveRNASVG(dest, _vp.getConfig());
740 public void UISaveAsDBN() throws ExceptionExportFailed,
741 ExceptionPermissionDenied {
742 String name = _vp.getVARNAUI().UIChooseOutputFile(_dbnFilter);
744 _vp.getRNA().saveAsDBN(name, _vp.getTitle());
747 public void UISaveAsCT() throws ExceptionExportFailed,
748 ExceptionPermissionDenied {
749 String name = _vp.getVARNAUI().UIChooseOutputFile(_ctFilter);
751 _vp.getRNA().saveAsCT(name, _vp.getTitle());
754 public void UISaveAsBPSEQ() throws ExceptionExportFailed,
755 ExceptionPermissionDenied {
756 String name = _vp.getVARNAUI().UIChooseOutputFile(_bpseqFilter);
758 _vp.getRNA().saveAsBPSEQ(name, _vp.getTitle());
761 public void UISaveAs() throws ExceptionExportFailed,
762 ExceptionPermissionDenied {
763 ArrayList<FileNameExtensionFilter> v = new ArrayList<FileNameExtensionFilter>();
768 String dest = UIChooseOutputFile(v);
770 String extLower = dest.substring(dest.lastIndexOf('.'))
772 if (extLower.endsWith("bpseq")) {
773 _vp.getRNA().saveAsBPSEQ(dest, _vp.getTitle());
774 } else if (extLower.endsWith("ct")) {
775 _vp.getRNA().saveAsCT(dest, _vp.getTitle());
776 } else if (extLower.endsWith("dbn") || extLower.endsWith("faa")) {
777 _vp.getRNA().saveAsDBN(dest, _vp.getTitle());
778 } else if (extLower.endsWith("varna")) {
779 _vp.saveSession(dest);
784 public String UIChooseOutputFile(FileNameExtensionFilter filtre) {
785 ArrayList<FileNameExtensionFilter> v = new ArrayList<FileNameExtensionFilter>();
787 return UIChooseOutputFile(v);
791 * Opens a save dialog with right extensions and return the absolute path
795 * @return <code>null</code> if the user doesn't approve the save dialog,<br>
796 * <code>absolutePath</code> if the user approve the save dialog
798 public String UIChooseOutputFile(ArrayList<FileNameExtensionFilter> filtre) {
799 JFileChooser fc = new JFileChooser();
801 String absolutePath = null;
802 // applique le filtre
803 for (int i = 0; i < filtre.size(); i++) {
804 fc.addChoosableFileFilter(filtre.get(i));
806 // en mode open dialog pour voir les autres fichiers avec la meme
808 fc.setFileSelectionMode(JFileChooser.OPEN_DIALOG);
809 fc.setDialogTitle("Save...");
810 // Si l'utilisateur a valider
811 if (fc.showSaveDialog(_vp) == JFileChooser.APPROVE_OPTION) {
813 absolutePath = fc.getSelectedFile().getAbsolutePath();
814 String extension = _vp.getPopupMenu().get_controleurMenu()
815 .getExtension(fc.getSelectedFile());
816 FileFilter f = fc.getFileFilter();
817 if (f instanceof FileNameExtensionFilter) {
818 ArrayList<String> listeExtension = new ArrayList<String>();
819 listeExtension.addAll(Arrays
820 .asList(((FileNameExtensionFilter) f).getExtensions()));
821 // si l'extension du fichier ne fait pas partie de la liste
822 // d'extensions acceptées
823 if (!listeExtension.contains(extension)) {
824 absolutePath += "." + listeExtension.get(0);
831 public void UISetBorder() {
832 VueBorder border = new VueBorder(_vp);
833 final Dimension oldBorder = _vp.getBorderSize();
835 _vp.drawBorder(true);
837 Runnable cancel = new Runnable () {
841 _vp.setBorderSize(oldBorder);
845 Runnable final_ = new Runnable () {
849 _vp.drawBorder(false);
856 showConfirmDialog(border.getPanel(), "Set new border size", null, cancel, cancel, final_);
859 public void UISetBackground() {
860 showColorDialog("Choose new background color", _vp.getBackground(), new Runnable() {
864 if (dialogReturnValue != null) {
865 _vp.setBackground((Color) dialogReturnValue);
873 public void UIZoomIn() {
874 double _actualZoom = _vp.getZoom();
875 double _actualAmount = _vp.getZoomIncrement();
876 Point _actualTranslation = _vp.getTranslation();
877 double newZoom = Math.min(VARNAConfig.MAX_ZOOM, _actualZoom
879 double ratio = newZoom / _actualZoom;
880 Point newTrans = new Point((int) (_actualTranslation.x * ratio),
881 (int) (_actualTranslation.y * ratio));
882 _vp.setZoom(newZoom);
883 _vp.setTranslation(newTrans);
884 // verification que la translation ne pose pas de problemes
885 _vp.checkTranslation();
886 // System.out.println("Zoom in");
890 public void UIZoomOut() {
891 double _actualZoom = _vp.getZoom();
892 double _actualAmount = _vp.getZoomIncrement();
893 Point _actualTranslation = _vp.getTranslation();
894 double newZoom = Math.max(_actualZoom / _actualAmount,
895 VARNAConfig.MIN_ZOOM);
896 double ratio = newZoom / _actualZoom;
897 Point newTrans = new Point((int) (_actualTranslation.x * ratio),
898 (int) (_actualTranslation.y * ratio));
899 _vp.setZoom(newZoom);
900 _vp.setTranslation(newTrans);
901 // verification que la translation ne pose pas de problemes
902 _vp.checkTranslation();
906 public void UICustomZoom() {
907 VueZoom zoom = new VueZoom(_vp);
908 final double oldZoom = _vp.getZoom();
909 final double oldZoomAmount = _vp.getZoomIncrement();
912 Runnable cancel = new Runnable() {
916 _vp.setZoom(oldZoom);
917 _vp.setZoomIncrement(oldZoomAmount);
921 Runnable final_ = new Runnable() {
930 showConfirmDialog(zoom.getPanel(), "Set zoom", null, cancel, cancel, final_);
933 public void UIGlobalRescale() {
934 if (_vp.isModifiable()) {
935 if (_vp.getRNA().get_listeBases().size() > 0) {
936 final VueGlobalRescale rescale = new VueGlobalRescale(_vp);
937 Runnable cancel = new Runnable() {
941 UIGlobalRescale(1. / rescale.getScale());
945 Runnable final_ = new Runnable() {
954 showConfirmDialog(rescale.getPanel(), "Rescales the whole RNA (No redraw)", null, cancel, cancel, final_);
959 public void UIGlobalRescale(double d) {
960 if (_vp.isModifiable()) {
961 if (_vp.getRNA().get_listeBases().size() > 0) {
962 _vp.globalRescale(d);
963 _undoableEditSupport.postEdit(new VARNAEdits.RescaleRNAEdit(d,
969 public void UIGlobalRotation() {
970 if (_vp.isModifiable()) {
971 if (_vp.getRNA().get_listeBases().size() > 0) {
974 final VueGlobalRotation rotation = new VueGlobalRotation(_vp);
975 Runnable cancel = new Runnable() {
979 UIGlobalRotation(-rotation.getAngle());
983 Runnable final_ = new Runnable() {
992 showConfirmDialog(rotation.getPanel(), "Rotates the whole RNA", null, cancel, cancel, final_, null);
997 public void UIGlobalRotation(double d) {
998 if (_vp.isModifiable()) {
999 if (_vp.getRNA().get_listeBases().size() > 0) {
1000 _vp.globalRotation(d);
1001 _undoableEditSupport.postEdit(new VARNAEdits.RotateRNAEdit(d,
1007 public void UISetBPStyle() {
1008 if (_vp.getRNA().get_listeBases().size() > 0) {
1009 VueStyleBP bpstyle = new VueStyleBP(_vp);
1010 final VARNAConfig.BP_STYLE bck = _vp.getBPStyle();
1011 Runnable cancel = new Runnable() {
1015 _vp.setBPStyle(bck);
1020 showConfirmDialog(bpstyle.getPanel(), "Set main base pair style", null, cancel, cancel);
1024 public void UISetTitleColor() {
1025 if (_vp.isModifiable()) {
1026 showColorDialog("Choose new title color", _vp.getTitleColor(), new Runnable() {
1030 if (dialogReturnValue != null) {
1031 _vp.setTitleColor((Color) dialogReturnValue);
1040 public void UISetBackboneColor() {
1041 if (_vp.isModifiable()) {
1042 showColorDialog("Choose new backbone color", _vp.getBackboneColor(), new Runnable() {
1046 if (dialogReturnValue != null) {
1047 _vp.setBackboneColor((Color) dialogReturnValue);
1056 public void UISetTitleFont() {
1057 if (_vp.isModifiable()) {
1058 final VueFont font = new VueFont(_vp);
1059 Runnable ok = new Runnable() {
1063 _vp.setTitleFont(font.getFont());
1068 showConfirmDialog(font.getPanel(), "New Title font", ok, null);
1072 public void UISetSpaceBetweenBases() {
1073 if (_vp.isModifiable()) {
1075 final VueSpaceBetweenBases vsbb = new VueSpaceBetweenBases(_vp);
1076 final Double oldSpace = _vp.getSpaceBetweenBases();
1077 Runnable cancel = new Runnable () {
1081 _vp.setSpaceBetweenBases(oldSpace);
1082 _vp.drawRNA(_vp.getRNA());
1087 showConfirmDialog(vsbb.getPanel(), "Set the space between each base", null, cancel, cancel);
1091 public void UISetBPHeightIncrement() {
1092 if (_vp.isModifiable()) {
1094 VueBPHeightIncrement v = new VueBPHeightIncrement(_vp);
1095 final Double oldSpace = _vp.getBPHeightIncrement();
1096 Runnable cancel = new Runnable() {
1100 _vp.setBPHeightIncrement(oldSpace);
1101 _vp.drawRNA(_vp.getRNA());
1106 showConfirmDialog(v.getPanel(), "Set the vertical increment in linear mode", null, cancel, cancel);
1110 public void UISetNumPeriod() {
1111 if (_vp.getRNA().get_listeBases().size() != 0) {
1112 final int oldNumPeriod = _vp.getNumPeriod();
1113 VueNumPeriod vnp = new VueNumPeriod(_vp);
1114 Runnable cancel = new Runnable() {
1118 _vp.setNumPeriod(oldNumPeriod);
1123 showConfirmDialog(vnp.getPanel(), "Set new numbering period", null, cancel, cancel);
1127 public void UIEditBasePair() {
1128 if (_vp.isModifiable()) {
1129 ModeleBase mb = _vp.getRNA().get_listeBases().get(_vp.getNearestBase());
1130 if (mb.getElementStructure() != -1) {
1131 final ModeleBP msbp = mb.getStyleBP();
1132 final ModeleBP.Edge bck5 = msbp.getEdgePartner5();
1133 final ModeleBP.Edge bck3 = msbp.getEdgePartner3();
1134 final ModeleBP.Stericity bcks = msbp.getStericity();
1136 VueBPType vbpt = new VueBPType(_vp, msbp);
1137 Runnable cancel = new Runnable() {
1141 msbp.setEdge5(bck5);
1142 msbp.setEdge3(bck3);
1143 msbp.setStericity(bcks);
1148 showConfirmDialog(vbpt.getPanel(), "Set base pair L/W type", null, cancel, cancel);
1153 public void UIColorBasePair() {
1154 if (_vp.isModifiable()) {
1155 ModeleBase mb = _vp.getRNA().get_listeBases().get(_vp.getNearestBase());
1156 if (mb.getElementStructure() != -1) {
1157 final ModeleBP msbp = mb.getStyleBP();
1158 showColorDialog("Choose custom base pair color",
1159 msbp.getStyle().getColor(_vp.getConfig()._bondColor), new Runnable() {
1163 if (dialogReturnValue != null) {
1164 msbp.getStyle().setCustomColor((Color) dialogReturnValue);
1174 public void UIThicknessBasePair() {
1175 if (_vp.isModifiable()) {
1176 ModeleBase mb = _vp.getRNA().get_listeBases()
1177 .get(_vp.getNearestBase());
1178 if (mb.getElementStructure() != -1) {
1179 ModeleBP msbp = mb.getStyleBP();
1180 ArrayList<ModeleBP> bases = new ArrayList<ModeleBP>();
1182 UIThicknessBasePairs(bases);
1187 public void saveToPNG(final String filename) {
1188 final VueJPEG jpeg = new VueJPEG(true, false);
1189 Runnable ok = new Runnable() {
1193 Double scale = jpeg.getScaleSlider().getValue() / 100.0;
1194 BufferedImage myImage = new BufferedImage((int) Math.round(_vp.getWidth() * scale),
1195 (int) Math.round(_vp.getHeight() * scale), BufferedImage.TYPE_INT_ARGB);
1196 // BH j2s SwingJS: was BufferedImage.TRANSLUCENT, which is TYPE_INT_ARGB_PRE ?? no transparent background?
1197 Graphics2D g2 = myImage.createGraphics();
1198 AffineTransform AF = new AffineTransform();
1199 AF.setToScale(scale, scale);
1200 g2.setTransform(AF);
1201 _vp.paintComponent(g2, !_vp.getConfig()._drawBackground);
1204 ImageIO.write(myImage, "PNG", new File(filename));
1205 } catch (IOException e) {
1206 // cannot throw an exception from Runnable.run()
1207 _vp.errorDialog(new ExceptionExportFailed(e.getMessage(), filename));
1212 showConfirmDialog(jpeg.getPanel(), "Set resolution", ok, null);
1215 public void saveToJPEG(final String filename) {
1216 final VueJPEG jpeg = new VueJPEG(true, true);
1217 Runnable ok = new Runnable() {
1222 if (jpeg.getScaleSlider().getValue() == 0)
1225 scale = jpeg.getScaleSlider().getValue() / 100.;
1226 BufferedImage myImage = new BufferedImage((int) Math.round(_vp
1227 .getWidth() * scale), (int) Math.round(_vp.getHeight()
1228 * scale), BufferedImage.TYPE_INT_RGB);
1229 Graphics2D g2 = myImage.createGraphics();
1230 AffineTransform AF = new AffineTransform();
1231 AF.setToScale(scale, scale);
1232 g2.setTransform(AF);
1233 _vp.paintComponent(g2);
1235 FileImageOutputStream out = new FileImageOutputStream(new File(
1237 ImageWriter writer = ImageIO
1238 .getImageWritersByFormatName("jpeg").next();
1239 ImageWriteParam params = writer.getDefaultWriteParam();
1240 params.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
1241 params.setCompressionQuality(jpeg.getQualitySlider().getValue() / 100.0f);
1242 writer.setOutput(out);
1243 IIOImage myIIOImage = new IIOImage(myImage, null, null);
1244 writer.write(null, myIIOImage, params);
1246 } catch (IOException e) {
1247 // cannot throw an exception from Runnable.run()
1248 _vp.errorDialog(new ExceptionExportFailed(e.getMessage(), filename));
1253 showConfirmDialog(jpeg.getPanel(), "Set resolution/quality", ok, null);
1256 public void UIToggleShowNCBP() {
1257 if (_vp.isModifiable()) {
1258 _vp.setShowNonCanonicalBP(!_vp.getShowNonCanonicalBP());
1263 public void UIToggleColorSpecialBases() {
1264 _vp.setColorNonStandardBases(!_vp.getColorSpecialBases());
1268 public void UIToggleColorGapsBases() {
1269 _vp.setColorGapsBases(!_vp.getColorGapsBases());
1273 public void UIToggleShowNonPlanar() {
1274 if (_vp.isModifiable()) {
1275 _vp.setShowNonPlanarBP(!_vp.getShowNonPlanarBP());
1280 public void UIToggleShowWarnings() {
1281 _vp.setShowWarnings(!_vp.getShowWarnings());
1285 public void UIPickSpecialBasesColor() {
1286 showColorDialog("Choose new special bases color", _vp.getNonStandardBasesColor(), new Runnable() {
1290 if (dialogReturnValue != null) {
1291 _vp.setNonStandardBasesColor((Color) dialogReturnValue);
1292 _vp.setColorNonStandardBases(true);
1300 public void UIPickGapsBasesColor() {
1301 showColorDialog("Choose new gaps bases color", _vp.getGapsBasesColor(), new Runnable() {
1305 if (dialogReturnValue != null) {
1306 _vp.setGapsBasesColor((Color) dialogReturnValue);
1307 _vp.setColorGapsBases(true);
1315 public void UIBaseTypeColor() {
1316 if (_vp.isModifiable()) {
1317 new VueBases(_vp, VueBases.KIND_MODE);
1321 public void UIToggleModifiable() {
1322 _vp.setModifiable(!_vp.isModifiable());
1325 public void UIBasePairTypeColor() {
1326 if (_vp.isModifiable()) {
1327 new VueBases(_vp, VueBases.COUPLE_MODE);
1331 public void UIBaseAllColor() {
1332 if (_vp.isModifiable()) {
1333 new VueBases(_vp, VueBases.ALL_MODE);
1337 public void UIAbout() {
1338 final VueAboutPanel about = new VueAboutPanel();
1339 Runnable ok = new Runnable() {
1343 about.gracefulStop();
1347 showMessageDialog(about, "About VARNA " + VARNAConfig.MAJOR_VERSION + "." + VARNAConfig.MINOR_VERSION,
1348 JOptionPane.PLAIN_MESSAGE, ok, ok);
1351 public void UIAutoAnnotateHelices() {
1352 if (_vp.isModifiable()) {
1353 _vp.getRNA().autoAnnotateHelices();
1358 public void UIAutoAnnotateStrandEnds() {
1359 if (_vp.isModifiable()) {
1360 _vp.getRNA().autoAnnotateStrandEnds();
1365 public void UIAutoAnnotateInteriorLoops() {
1366 if (_vp.isModifiable()) {
1367 _vp.getRNA().autoAnnotateInteriorLoops();
1372 public void UIAutoAnnotateTerminalLoops() {
1373 if (_vp.isModifiable()) {
1374 _vp.getRNA().autoAnnotateTerminalLoops();
1379 public void UIAnnotationRemoveFromAnnotation(TextAnnotation textAnnotation) {
1380 if (_vp.isModifiable()) {
1381 _vp.set_selectedAnnotation(null);
1382 _vp.getListeAnnotations().remove(textAnnotation);
1387 public void UIAnnotationEditFromAnnotation(TextAnnotation textAnnotation) {
1389 if (textAnnotation.getType() == TextAnnotation.AnchorType.POSITION)
1390 vue = new VueAnnotation(_vp, textAnnotation, false);
1392 vue = new VueAnnotation(_vp, textAnnotation, true, false);
1396 public void UIAnnotationAddFromStructure(TextAnnotation.AnchorType type, ArrayList<Integer> listeIndex)
1398 TextAnnotation textAnnot;
1399 ArrayList<ModeleBase> listeBase;
1403 textAnnot = new TextAnnotation("", _vp.getRNA().get_listeBases()
1404 .get(listeIndex.get(0)));
1405 vue = new VueAnnotation(_vp, textAnnot, true);
1409 listeBase = new ArrayList<ModeleBase>();
1410 for (Integer i : listeIndex) {
1411 listeBase.add(_vp.getRNA().get_listeBases().get(i));
1413 textAnnot = new TextAnnotation("", listeBase, type);
1414 vue = new VueAnnotation(_vp, textAnnot, true);
1418 listeBase = new ArrayList<ModeleBase>();
1419 for (Integer i : listeIndex) {
1420 listeBase.add(_vp.getRNA().get_listeBases().get(i));
1422 textAnnot = new TextAnnotation("", listeBase, type);
1423 vue = new VueAnnotation(_vp, textAnnot, true);
1427 _vp.errorDialog(new Exception("Unknown structure type"));
1432 public void UIAnnotationEditFromStructure(TextAnnotation.AnchorType type,
1433 ArrayList<Integer> listeIndex) {
1434 if (_vp.isModifiable()) {
1435 ModeleBase mb = _vp.getRNA().get_listeBases()
1436 .get(listeIndex.get(0));
1437 TextAnnotation ta = _vp.getRNA().getAnnotation(type, mb);
1439 UIAnnotationEditFromAnnotation(ta);
1443 public void UIAnnotationRemoveFromStructure(TextAnnotation.AnchorType type,
1444 ArrayList<Integer> listeIndex) {
1445 if (_vp.isModifiable()) {
1446 ModeleBase mb = _vp.getRNA().get_listeBases()
1447 .get(listeIndex.get(0));
1448 TextAnnotation ta = _vp.getRNA().getAnnotation(type, mb);
1450 UIAnnotationRemoveFromAnnotation(ta);
1454 public void UIAnnotationsAddPosition(int x, int y) {
1455 if (_vp.isModifiable()) {
1456 Point2D.Double p = _vp.panelToLogicPoint(new Point2D.Double(x, y));
1457 VueAnnotation annotationAdd = new VueAnnotation(_vp, (int) p.x,
1459 annotationAdd.show();
1463 public void UIAnnotationsAddBase(int x, int y) {
1464 if (_vp.isModifiable()) {
1465 ModeleBase mb = _vp.getBaseAt(new Point2D.Double(x, y));
1467 _vp.highlightSelectedBase(mb);
1468 TextAnnotation textAnnot = new TextAnnotation("", mb);
1469 VueAnnotation annotationAdd = new VueAnnotation(_vp, textAnnot,
1471 annotationAdd.show();
1476 public void UIAnnotationsAddLoop(int x, int y) {
1477 if (_vp.isModifiable()) {
1479 ModeleBase mb = _vp.getBaseAt(new Point2D.Double(x, y));
1481 Vector<Integer> v = _vp.getRNA()
1482 .getLoopBases(mb.getIndex());
1483 ArrayList<ModeleBase> mbs = _vp.getRNA().getBasesAt(v);
1484 TextAnnotation textAnnot;
1485 textAnnot = new TextAnnotation("", mbs,
1486 TextAnnotation.AnchorType.LOOP);
1487 _vp.setSelection(mbs);
1488 VueAnnotation annotationAdd = new VueAnnotation(_vp,
1490 annotationAdd.show();
1492 } catch (Exception e) {
1493 // TODO Auto-generated catch block
1494 e.printStackTrace();
1499 private ArrayList<ModeleBase> extractMaxContiguousPortion(
1500 ArrayList<ModeleBase> m) {
1501 ModeleBase[] tab = new ModeleBase[_vp.getRNA().getSize()];
1502 for (int i = 0; i < tab.length; i++) {
1505 for (ModeleBase mb : m) {
1506 tab[mb.getIndex()] = mb;
1508 ArrayList<ModeleBase> best = new ArrayList<ModeleBase>();
1509 ArrayList<ModeleBase> current = new ArrayList<ModeleBase>();
1510 for (int i = 0; i < tab.length; i++) {
1511 if (tab[i] != null) {
1512 current.add(tab[i]);
1514 if (current.size() > best.size())
1516 current = new ArrayList<ModeleBase>();
1519 if (current.size() > best.size()) {
1525 public void UIAnnotationsAddRegion(int x, int y) {
1526 if (_vp.isModifiable()) {
1527 ArrayList<ModeleBase> mb = _vp.getSelection().getBases();
1528 if (mb.size() == 0) {
1529 ModeleBase m = _vp.getBaseAt(new Point2D.Double(x, y));
1532 mb = extractMaxContiguousPortion(extractMaxContiguousPortion(mb));
1533 _vp.setSelection(mb);
1534 HighlightRegionAnnotation regionAnnot = new HighlightRegionAnnotation(
1536 _vp.addHighlightRegion(regionAnnot);
1537 VueHighlightRegionEdit annotationAdd = new VueHighlightRegionEdit(
1539 if (!annotationAdd.show()) {
1540 _vp.removeHighlightRegion(regionAnnot);
1542 _vp.clearSelection();
1546 public void UIAnnotationsAddChemProb(int x, int y) {
1547 if (_vp.isModifiable() && _vp.getRNA().getSize() > 1) {
1548 Point2D.Double p = _vp.panelToLogicPoint(new Point2D.Double(x, y));
1549 ModeleBase m1 = _vp.getBaseAt(new Point2D.Double(x, y));
1550 ModeleBase best = null;
1551 if (m1.getIndex() - 1 >= 0) {
1552 best = _vp.getRNA().getBaseAt(m1.getIndex() - 1);
1554 if (m1.getIndex() + 1 < _vp.getRNA().getSize()) {
1555 ModeleBase m2 = _vp.getRNA().getBaseAt(m1.getIndex() + 1);
1559 if (best.getCoords().distance(p) > m2.getCoords().distance(
1565 ArrayList<ModeleBase> tab = new ArrayList<ModeleBase>();
1568 _vp.setSelection(tab);
1569 ChemProbAnnotation regionAnnot = new ChemProbAnnotation(m1, best);
1570 _vp.getRNA().addChemProbAnnotation(regionAnnot);
1571 VueChemProbAnnotation annotationAdd = new VueChemProbAnnotation(
1573 if (!annotationAdd.show()) {
1574 _vp.getRNA().removeChemProbAnnotation(regionAnnot);
1576 _vp.clearSelection();
1580 public void UIAnnotationsAddHelix(int x, int y) {
1581 if (_vp.isModifiable()) {
1583 ModeleBase mb = _vp.getBaseAt(new Point2D.Double(x, y));
1585 ArrayList<Integer> v = _vp.getRNA().findHelix(mb.getIndex());
1586 ArrayList<ModeleBase> mbs = _vp.getRNA().getBasesAt(v);
1587 TextAnnotation textAnnot;
1588 textAnnot = new TextAnnotation("", mbs,
1589 TextAnnotation.AnchorType.HELIX);
1590 _vp.setSelection(mbs);
1591 VueAnnotation annotationAdd = new VueAnnotation(_vp,
1593 annotationAdd.show();
1595 } catch (Exception e) {
1596 // TODO Auto-generated catch block
1597 e.printStackTrace();
1602 public void UIToggleGaspinMode() {
1603 if (_vp.isModifiable()) {
1604 _vp.toggleDrawOutlineBases();
1605 _vp.toggleFillBases();
1610 public void UIAnnotationsAdd() {
1611 if (_vp.isModifiable()) {
1612 VueAnnotation annotationAdd = new VueAnnotation(_vp);
1613 annotationAdd.show();
1617 public void UIEditAllBasePairs() {
1618 if (_vp.isModifiable()) {
1623 public void UIEditAllBases() {
1624 if (_vp.isModifiable()) {
1625 new VueBases(_vp, VueBases.ALL_MODE);
1629 public void UIAnnotationsRemove() {
1630 if (_vp.isModifiable()) {
1631 new VueListeAnnotations(_vp, VueListeAnnotations.REMOVE);
1635 public void UIAnnotationsEdit() {
1636 if (_vp.isModifiable()) {
1637 new VueListeAnnotations(_vp, VueListeAnnotations.EDIT);
1641 public void UIAddBP(int i, int j, ModeleBP ms) {
1642 if (_vp.isModifiable()) {
1643 _vp.getRNA().addBP(i, j, ms);
1644 _undoableEditSupport.postEdit(new VARNAEdits.AddBPEdit(i, j, ms,
1648 HashSet<ModeleBP> tmp = new HashSet<ModeleBP>();
1650 _vp.fireStructureChanged(new HashSet<ModeleBP>(_vp.getRNA()
1651 .getAllBPs()), tmp, new HashSet<ModeleBP>());
1655 public void UIRemoveBP(ModeleBP ms) {
1656 if (_vp.isModifiable()) {
1657 _undoableEditSupport.postEdit(new VARNAEdits.RemoveBPEdit(ms
1658 .getIndex5(), ms.getIndex3(), ms, _vp));
1659 _vp.getRNA().removeBP(ms);
1662 HashSet<ModeleBP> tmp = new HashSet<ModeleBP>();
1664 _vp.fireStructureChanged(new HashSet<ModeleBP>(_vp.getRNA()
1665 .getAllBPs()), new HashSet<ModeleBP>(), tmp);
1669 public void UIShiftBaseCoord(ArrayList<Integer> indices, double dx, double dy) {
1670 if (_vp.isModifiable()) {
1671 Hashtable<Integer, Point2D.Double> backupPos = new Hashtable<Integer, Point2D.Double>();
1673 for (int index : indices) {
1674 ModeleBase mb = _vp.getRNA().getBaseAt(index);
1675 Point2D.Double d = mb.getCoords();
1676 backupPos.put(index, d);
1677 _vp.getRNA().setCoord(index, d.x + dx, d.y + dy);
1678 _vp.getRNA().setCenter(index, mb.getCenter().x + dx, mb.getCenter().y + dy);
1680 _undoableEditSupport.postEdit(new VARNAEdits.BasesShiftEdit(
1681 indices, dx, dy, _vp));
1683 _vp.fireLayoutChanged(backupPos);
1687 public void UIShiftBaseCoord(ArrayList<Integer> indices, Point2D.Double dv) {
1688 UIShiftBaseCoord(indices, dv.x, dv.y);
1691 public void UIMoveSingleBase(int index, double nx, double ny) {
1692 if (_vp.isModifiable()) {
1693 ModeleBase mb = _vp.getRNA().getBaseAt(index);
1694 Point2D.Double d = mb.getCoords();
1695 Hashtable<Integer, Point2D.Double> backupPos = new Hashtable<Integer, Point2D.Double>();
1696 backupPos.put(index, d);
1697 _undoableEditSupport.postEdit(new VARNAEdits.SingleBaseMoveEdit(
1698 index, nx, ny, _vp));
1699 _vp.getRNA().setCoord(index, nx, ny);
1701 _vp.fireLayoutChanged(backupPos);
1705 public void UIMoveSingleBase(int index, Point2D.Double dv) {
1706 UIMoveSingleBase(index, dv.x, dv.y);
1709 public void UISetBaseCenter(int index, double x, double y) {
1710 UISetBaseCenter(index, new Point2D.Double(x, y));
1713 public void UISetBaseCenter(int index, Point2D.Double p) {
1714 if (_vp.isModifiable()) {
1715 _vp.getRNA().setCenter(index, p);
1719 public void UIUndo() {
1723 public void UIRedo() {
1728 * Move a helix of the rna
1731 * :the index of the selected base
1733 * :the new xy coordinate, within the logical system of
1736 public void UIMoveHelixAtom(int index, Point2D.Double newPos) {
1737 if (_vp.isModifiable() && (index >= 0)
1738 && (index < _vp.getRNA().get_listeBases().size())) {
1739 int indexTo = _vp.getRNA().get_listeBases().get(index)
1740 .getElementStructure();
1741 Point h = _vp.getRNA().getHelixInterval(index);
1742 Point ml = _vp.getRNA().getMultiLoop(h.x);
1744 if (indexTo != -1) {
1746 if (shouldFlip(index, newPos)) {
1748 _undoableEditSupport
1749 .postEdit(new VARNAEdits.HelixFlipEdit(h, _vp));
1752 UIRotateHelixAtom(index, newPos);
1756 _vp.fireLayoutChanged();
1761 * Flip an helix around its supporting base
1763 public void UIFlipHelix(Point h) {
1766 Point2D.Double A = _vp.getRNA().getCoords(hBeg);
1767 Point2D.Double B = _vp.getRNA().getCoords(hEnd);
1768 Point2D.Double AB = new Point2D.Double(B.x - A.x, B.y - A.y);
1769 double normAB = Math.sqrt(AB.x * AB.x + AB.y * AB.y);
1770 // Creating a coordinate system centered on A and having
1771 // unit x-vector Ox.
1772 Point2D.Double O = A;
1773 Point2D.Double Ox = new Point2D.Double(AB.x / normAB, AB.y / normAB);
1774 Hashtable<Integer, Point2D.Double> old = new Hashtable<Integer, Point2D.Double>();
1775 for (int i = hBeg + 1; i < hEnd; i++) {
1776 Point2D.Double P = _vp.getRNA().getCoords(i);
1777 Point2D.Double nP = RNA.project(O, Ox, P);
1780 _vp.getRNA().flipHelix(h);
1781 _vp.fireLayoutChanged(old);
1785 * Tests if an helix needs to be flipped.
1787 boolean shouldFlip(int index, Point2D.Double P) {
1788 Point h = _vp.getRNA().getHelixInterval(index);
1790 Point2D.Double A = _vp.getRNA().getCoords(h.x);
1791 Point2D.Double B = _vp.getRNA().getCoords(h.y);
1792 Point2D.Double C = _vp.getRNA().getCoords(h.x + 1);
1793 // Creating a vector that is orthogonal to AB
1794 Point2D.Double hAB = new Point2D.Double(B.y - A.y, -(B.x - A.x));
1795 Point2D.Double AC = new Point2D.Double(C.x - A.x, C.y - A.y);
1796 Point2D.Double AP = new Point2D.Double(P.x - A.x, P.y - A.y);
1797 double signC = (hAB.x * AC.x + hAB.y * AC.y);
1798 double signP = (hAB.x * AP.x + hAB.y * AP.y);
1799 // Now, the product signC*signP is negative iff the mouse and the first
1801 // the helix are on different sides of the end of the helix => Flip the
1803 return (signC * signP < 0.0);
1806 public void UIRotateHelixAtom(int index, Point2D.Double newPos) {
1807 Point h = _vp.getRNA().getHelixInterval(index);
1808 Point ml = _vp.getRNA().getMultiLoop(h.x);
1810 int prevIndex = h.x;
1811 int nextIndex = h.y;
1813 int j = _vp.getRNA().get_listeBases().get(i).getElementStructure();
1814 if ((j != -1) && (i < h.x)) {
1817 if ((j != -1) && (i > h.y) && (nextIndex == h.y)) {
1820 if ((j > i) && (j < ml.y)) {
1821 i = _vp.getRNA().get_listeBases().get(i).getElementStructure();
1826 Point2D.Double oldPos = _vp.getRNA().getCoords(index);
1827 Point2D.Double limitLoopLeft, limitLoopRight, limitLeft, limitRight, helixStart, helixStop;
1828 boolean isDirect = _vp.getRNA().testDirectionality(ml.x, ml.y, h.x);
1830 limitLoopLeft = _vp.getRNA().getCoords(ml.y);
1831 limitLoopRight = _vp.getRNA().getCoords(ml.x);
1832 limitLeft = _vp.getRNA().getCoords(prevIndex);
1833 limitRight = _vp.getRNA().getCoords(nextIndex);
1834 helixStart = _vp.getRNA().getCoords(h.x);
1835 helixStop = _vp.getRNA().getCoords(h.y);
1837 limitLoopLeft = _vp.getRNA().getCoords(ml.x);
1838 limitLoopRight = _vp.getRNA().getCoords(ml.y);
1839 limitLeft = _vp.getRNA().getCoords(nextIndex);
1840 limitRight = _vp.getRNA().getCoords(prevIndex);
1841 helixStart = _vp.getRNA().getCoords(h.y);
1842 helixStop = _vp.getRNA().getCoords(h.x);
1845 Point2D.Double center = _vp.getRNA().get_listeBases().get(h.x)
1847 double base = (RNA.computeAngle(center, limitLoopRight) + RNA
1848 .computeAngle(center, limitLoopLeft)) / 2.0;
1849 double pLimR = RNA.computeAngle(center, limitLeft) - base;
1850 double pHelR = RNA.computeAngle(center, helixStart) - base;
1851 double pNew = RNA.computeAngle(center, newPos) - base;
1852 double pOld = RNA.computeAngle(center, oldPos) - base;
1853 double pHelL = RNA.computeAngle(center, helixStop) - base;
1854 double pLimL = RNA.computeAngle(center, limitRight) - base;
1857 pLimR += 2.0 * Math.PI;
1858 while (pHelR < pLimR)
1859 pHelR += 2.0 * Math.PI;
1860 while ((pNew < pHelR))
1861 pNew += 2.0 * Math.PI;
1862 while ((pOld < pHelR))
1863 pOld += 2.0 * Math.PI;
1864 while ((pHelL < pOld))
1865 pHelL += 2.0 * Math.PI;
1866 while ((pLimL < pHelL))
1867 pLimL += 2.0 * Math.PI;
1869 double minDelta = normalizeAngle((pLimR - pHelR) + 0.25);
1870 double maxDelta = normalizeAngle((pLimL - pHelL) - 0.25);
1871 while (maxDelta < minDelta)
1872 maxDelta += 2.0 * Math.PI;
1873 double delta = normalizeAngle(pNew - pOld);
1874 while (delta < minDelta)
1875 delta += 2.0 * Math.PI;
1877 if (delta > maxDelta) {
1878 double distanceMax = delta - maxDelta;
1879 double distanceMin = minDelta - (delta - 2.0 * Math.PI);
1880 if (distanceMin < distanceMax) {
1886 double corrected = RNA
1887 .correctHysteresis((delta + base + (pHelR + pHelL) / 2.));
1888 delta = corrected - (base + (pHelR + pHelL) / 2.);
1889 _undoableEditSupport.postEdit(new VARNAEdits.HelixRotateEdit(delta,
1890 base, pLimL, pLimR, h, ml, _vp));
1891 UIRotateEverything(delta, base, pLimL, pLimR, h, ml);
1894 public void UIRotateEverything(double delta, double base, double pLimL,
1895 double pLimR, Point h, Point ml) {
1896 Hashtable<Integer, Point2D.Double> backupPos = new Hashtable<Integer, Point2D.Double>();
1897 _vp.getRNA().rotateEverything(delta, base, pLimL, pLimR, h, ml,
1899 _vp.fireLayoutChanged(backupPos);
1902 private double normalizeAngle(double angle) {
1903 return normalizeAngle(angle, 0.0);
1906 private double normalizeAngle(double angle, double base) {
1907 while (angle < base) {
1908 angle += 2.0 * Math.PI;
1910 while (angle >= (2.0 * Math.PI) - base) {
1911 angle -= 2.0 * Math.PI;
1916 public void UIThicknessBasePairs(ArrayList<ModeleBP> bases) {
1917 final VueBPThickness vbpt = new VueBPThickness(_vp, bases);
1918 Runnable cancel = new Runnable() {
1922 vbpt.restoreThicknesses();
1927 showConfirmDialog(vbpt.getPanel(), "Set base pair(s) thickness", null, cancel, cancel);