Clover icon

Coverage Report

  1. Project Clover database Wed Nov 12 2025 09:00:47 GMT
  2. Package jalview.gui

File AlignFrame.java

Code metrics

700
1,947
264
2
6,625
4,751
761
0.39
7.38
132
2.88
Syntax error detected in source file:
/srv/bamboo/bamboo-home/xml-data/build-dir/196609/JB-GDB724-JOB1/src/jalview/gui/AlignFrame.java
Comparison method violates its general contract!
1   /*
2   * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
3   * Copyright (C) $$Year-Rel$$ The Jalview Authors
4   *
5   * This file is part of Jalview.
6   *
7   * Jalview is free software: you can redistribute it and/or
8   * modify it under the terms of the GNU General Public License
9   * as published by the Free Software Foundation, either version 3
10   * of the License, or (at your option) any later version.
11   *
12   * Jalview is distributed in the hope that it will be useful, but
13   * WITHOUT ANY WARRANTY; without even the implied warranty
14   * of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15   * PURPOSE. See the GNU General Public License for more details.
16   *
17   * You should have received a copy of the GNU General Public License
18   * along with Jalview. If not, see .
19   * The Jalview Authors are detailed in the 'AUTHORS' file.
20   */
21   package jalview.gui;
22  
23   import java.awt.BorderLayout;
24   import java.awt.Color;
25   import java.awt.Component;
26   import java.awt.Rectangle;
27   import java.awt.Toolkit;
28   import java.awt.datatransfer.Clipboard;
29   import java.awt.datatransfer.DataFlavor;
30   import java.awt.datatransfer.StringSelection;
31   import java.awt.datatransfer.Transferable;
32   import java.awt.dnd.DnDConstants;
33   import java.awt.dnd.DropTargetDragEvent;
34   import java.awt.dnd.DropTargetDropEvent;
35   import java.awt.dnd.DropTargetEvent;
36   import java.awt.dnd.DropTargetListener;
37   import java.awt.event.ActionEvent;
38   import java.awt.event.ActionListener;
39   import java.awt.event.FocusAdapter;
40   import java.awt.event.FocusEvent;
41   import java.awt.event.ItemEvent;
42   import java.awt.event.ItemListener;
43   import java.awt.event.KeyAdapter;
44   import java.awt.event.KeyEvent;
45   import java.awt.event.MouseEvent;
46   import java.awt.print.PageFormat;
47   import java.awt.print.PrinterJob;
48   import java.beans.PropertyChangeEvent;
49   import java.io.File;
50   import java.io.FileWriter;
51   import java.io.IOException;
52   import java.io.OutputStreamWriter;
53   import java.io.PrintWriter;
54   import java.net.URL;
55   import java.util.ArrayList;
56   import java.util.Arrays;
57   import java.util.Deque;
58   import java.util.Enumeration;
59   import java.util.HashMap;
60   import java.util.Hashtable;
61   import java.util.List;
62   import java.util.Locale;
63   import java.util.Map;
64   import java.util.Vector;
65  
66   import javax.swing.ButtonGroup;
67   import javax.swing.JCheckBoxMenuItem;
68   import javax.swing.JComponent;
69   import javax.swing.JEditorPane;
70   import javax.swing.JInternalFrame;
71   import javax.swing.JLabel;
72   import javax.swing.JLayeredPane;
73   import javax.swing.JMenu;
74   import javax.swing.JMenuItem;
75   import javax.swing.JPanel;
76   import javax.swing.JProgressBar;
77   import javax.swing.JScrollPane;
78   import javax.swing.SwingUtilities;
79  
80   import ext.vamsas.ServiceHandle;
81   import jalview.analysis.AlignmentAnnotationUtils;
82   import jalview.analysis.AlignmentSorter;
83   import jalview.analysis.AlignmentUtils;
84   import jalview.analysis.AnnotationSorter.SequenceAnnotationOrder;
85   import jalview.analysis.CrossRef;
86   import jalview.analysis.Dna;
87   import jalview.analysis.GeneticCodeI;
88   import jalview.analysis.ParseProperties;
89   import jalview.analysis.SequenceIdMatcher;
90   import jalview.api.AlignExportSettingsI;
91   import jalview.api.AlignViewControllerGuiI;
92   import jalview.api.AlignViewControllerI;
93   import jalview.api.AlignViewportI;
94   import jalview.api.AlignmentViewPanel;
95   import jalview.api.FeatureSettingsControllerI;
96   import jalview.api.FeatureSettingsModelI;
97   import jalview.api.SplitContainerI;
98   import jalview.api.ViewStyleI;
99   import jalview.api.analysis.SimilarityParamsI;
100   import jalview.bin.Cache;
101   import jalview.bin.Console;
102   import jalview.bin.Jalview;
103   import jalview.bin.groovy.JalviewObjectI;
104   import jalview.commands.CommandI;
105   import jalview.commands.EditCommand;
106   import jalview.commands.EditCommand.Action;
107   import jalview.commands.OrderCommand;
108   import jalview.commands.RemoveGapColCommand;
109   import jalview.commands.RemoveGapsCommand;
110   import jalview.commands.SlideSequencesCommand;
111   import jalview.commands.TrimRegionCommand;
112   import jalview.datamodel.AlignExportSettingsAdapter;
113   import jalview.datamodel.AlignedCodonFrame;
114   import jalview.datamodel.Alignment;
115   import jalview.datamodel.AlignmentAnnotation;
116   import jalview.datamodel.AlignmentExportData;
117   import jalview.datamodel.AlignmentI;
118   import jalview.datamodel.AlignmentOrder;
119   import jalview.datamodel.AlignmentView;
120   import jalview.datamodel.ColumnSelection;
121   import jalview.datamodel.ContactMatrixI;
122   import jalview.datamodel.HiddenColumns;
123   import jalview.datamodel.PDBEntry;
124   import jalview.datamodel.SeqCigar;
125   import jalview.datamodel.Sequence;
126   import jalview.datamodel.SequenceGroup;
127   import jalview.datamodel.SequenceI;
128   import jalview.datamodel.annotations.AlphaFoldAnnotationRowBuilder;
129   import jalview.gui.ColourMenuHelper.ColourChangeListener;
130   import jalview.gui.ViewSelectionMenu.ViewSetProvider;
131   import jalview.io.AlignmentProperties;
132   import jalview.io.AnnotationFile;
133   import jalview.io.BackupFiles;
134   import jalview.io.BioJsHTMLOutput;
135   import jalview.io.DataSourceType;
136   import jalview.io.FileFormat;
137   import jalview.io.FileFormatI;
138   import jalview.io.FileFormats;
139   import jalview.io.FileLoader;
140   import jalview.io.FileParse;
141   import jalview.io.FormatAdapter;
142   import jalview.io.HtmlSvgOutput;
143   import jalview.io.IdentifyFile;
144   import jalview.io.JPredFile;
145   import jalview.io.JalviewFileChooser;
146   import jalview.io.JalviewFileView;
147   import jalview.io.JnetAnnotationMaker;
148   import jalview.io.NewickFile;
149   import jalview.io.ScoreMatrixFile;
150   import jalview.io.TCoffeeScoreFile;
151   import jalview.io.exceptions.ImageOutputException;
152   import jalview.io.vcf.VCFLoader;
153   import jalview.jbgui.GAlignFrame;
154   import jalview.project.Jalview2XML;
155   import jalview.schemes.ColourSchemeI;
156   import jalview.schemes.ColourSchemes;
157   import jalview.schemes.ResidueColourScheme;
158   import jalview.schemes.TCoffeeColourScheme;
159   import jalview.util.Constants;
160   import jalview.util.HttpUtils;
161   import jalview.util.ImageMaker.TYPE;
162   import jalview.util.MessageManager;
163   import jalview.util.Platform;
164   import jalview.util.imagemaker.BitmapImageSizing;
165   import jalview.viewmodel.AlignmentViewport;
166   import jalview.viewmodel.ViewportRanges;
167   import jalview.ws.DBRefFetcher;
168   import jalview.ws.DBRefFetcher.FetchFinishedListenerI;
169   import jalview.ws.jws1.Discoverer;
170   import jalview.ws.jws2.Jws2Discoverer;
171   import jalview.ws.jws2.jabaws2.Jws2Instance;
172   import jalview.ws.seqfetcher.DbSourceProxy;
173  
174   /**
175   * DOCUMENT ME!
176   *
177   * @author $author$
178   * @version $Revision$
179   */
180   @SuppressWarnings("serial")
181   public class AlignFrame extends GAlignFrame implements DropTargetListener,
182   IProgressIndicator, AlignViewControllerGuiI, ColourChangeListener
183   {
184  
185   public static final int DEFAULT_WIDTH = 700;
186  
187   public static final int DEFAULT_HEIGHT = 500;
188  
189   /*
190   * The currently displayed panel (selected tabbed view if more than one)
191   */
192   public AlignmentPanel alignPanel;
193  
194   AlignViewport viewport;
195  
196   public AlignViewControllerI avc;
197  
198   List alignPanels = new ArrayList<>();
199  
200   /**
201   * Last format used to load or save alignments in this window
202   */
203   FileFormatI currentFileFormat = null;
204  
205   /**
206   * Current filename for this alignment
207   */
208   String fileName = null;
209  
210   File fileObject;
211  
212   /**
213   * Creates a new AlignFrame object with specific width and height.
214   *
215   * @param al
216   * @param width
217   * @param height
218   */
219   public AlignFrame(AlignmentI al, int width, int height)
220   {
221   this(al, null, width, height);
222   }
223  
224   /**
225   * Creates a new AlignFrame object with specific width, height and
226   * sequenceSetId
227   *
228   * @param al
229   * @param width
230   * @param height
231   * @param sequenceSetId
232   */
233   public AlignFrame(AlignmentI al, int width, int height,
234   String sequenceSetId)
235   {
236   this(al, null, width, height, sequenceSetId);
237   }
238  
239   /**
240   * Creates a new AlignFrame object with specific width, height and
241   * sequenceSetId
242   *
243   * @param al
244   * @param width
245   * @param height
246   * @param sequenceSetId
247   * @param viewId
248   */
249   public AlignFrame(AlignmentI al, int width, int height,
250   String sequenceSetId, String viewId)
251   {
252   this(al, null, width, height, sequenceSetId, viewId);
253   }
254  
255   /**
256   * new alignment window with hidden columns
257   *
258   * @param al
259   * AlignmentI
260   * @param hiddenColumns
261   * ColumnSelection or null
262   * @param width
263   * Width of alignment frame
264   * @param height
265   * height of frame.
266   */
267   public AlignFrame(AlignmentI al, HiddenColumns hiddenColumns, int width,
268   int height)
269   {
270   this(al, hiddenColumns, width, height, null);
271   }
272  
273   /**
274   * Create alignment frame for al with hiddenColumns, a specific width and
275   * height, and specific sequenceId
276   *
277   * @param al
278   * @param hiddenColumns
279   * @param width
280   * @param height
281   * @param sequenceSetId
282   * (may be null)
283   */
284   public AlignFrame(AlignmentI al, HiddenColumns hiddenColumns, int width,
285   int height, String sequenceSetId)
286   {
287   this(al, hiddenColumns, width, height, sequenceSetId, null);
288   }
289  
290   /**
291   * Create alignment frame for al with hiddenColumns, a specific width and
292   * height, and specific sequenceId
293   *
294   * @param al
295   * @param hiddenColumns
296   * @param width
297   * @param height
298   * @param sequenceSetId
299   * (may be null)
300   * @param viewId
301   * (may be null)
302   */
303   public AlignFrame(AlignmentI al, HiddenColumns hiddenColumns, int width,
304   int height, String sequenceSetId, String viewId)
305   {
306   setSize(width, height);
307  
308   if (al.getDataset() == null)
309   {
310   al.setDataset(null);
311   }
312  
313   viewport = new AlignViewport(al, hiddenColumns, sequenceSetId, viewId);
314  
315   alignPanel = new AlignmentPanel(this, viewport);
316  
317   addAlignmentPanel(alignPanel, true);
318   init();
319   }
320  
321   public AlignFrame(AlignmentI al, SequenceI[] hiddenSeqs,
322   HiddenColumns hiddenColumns, int width, int height)
323   {
324   setSize(width, height);
325  
326   if (al.getDataset() == null)
327   {
328   al.setDataset(null);
329   }
330  
331   viewport = new AlignViewport(al, hiddenColumns);
332  
333   if (hiddenSeqs != null && hiddenSeqs.length > 0)
334   {
335   viewport.hideSequence(hiddenSeqs);
336   }
337   alignPanel = new AlignmentPanel(this, viewport);
338   addAlignmentPanel(alignPanel, true);
339   init();
340   }
341  
342   /**
343   * Make a new AlignFrame from existing alignmentPanels
344   *
345   * @param ap
346   * AlignmentPanel
347   * @param av
348   * AlignViewport
349   */
350   public AlignFrame(AlignmentPanel ap)
351   {
352   viewport = ap.av;
353   alignPanel = ap;
354   addAlignmentPanel(ap, false);
355   init();
356   }
357  
358   /**
359   * initalise the alignframe from the underlying viewport data and the
360   * configurations
361   */
362   void init()
363   {
364   setFrameIcon(null);
365  
366   // setBackground(Color.white); // BH 2019
367  
368   if (!Jalview.isHeadlessMode())
369   {
370   progressBar = new ProgressBar(this.statusPanel, this.statusBar);
371   }
372  
373   avc = new jalview.controller.AlignViewController(this, viewport,
374   alignPanel);
375   if (viewport.getAlignmentConservationAnnotation() == null)
376   {
377   // BLOSUM62Colour.setEnabled(false);
378   conservationMenuItem.setEnabled(false);
379   modifyConservation.setEnabled(false);
380   // PIDColour.setEnabled(false);
381   // abovePIDThreshold.setEnabled(false);
382   // modifyPID.setEnabled(false);
383   }
384  
385   String sortby = Cache.getDefault("SORT_ALIGNMENT", "No sort");
386  
387   if (sortby.equals("Id"))
388   {
389   sortIDMenuItem_actionPerformed(null);
390   }
391   else if (sortby.equals("Pairwise Identity"))
392   {
393   sortPairwiseMenuItem_actionPerformed(null);
394   }
395  
396   this.alignPanel.av
397   .setShowAutocalculatedAbove(isShowAutoCalculatedAbove());
398  
399   setMenusFromViewport(viewport);
400   buildSortByAnnotationScoresMenu();
401   calculateTree.addActionListener(new ActionListener()
402   {
403  
404   @Override
405   public void actionPerformed(ActionEvent e)
406   {
407   openTreePcaDialog();
408   }
409   });
410   buildColourMenu();
411  
412   if (Desktop.desktop != null)
413   {
414   this.setDropTarget(new java.awt.dnd.DropTarget(this, this));
415   if (!Platform.isJS())
416   {
417   addServiceListeners();
418   }
419   setGUINucleotide();
420   }
421  
422   if (viewport.getWrapAlignment())
423   {
424   wrapMenuItem_actionPerformed(null);
425   }
426  
427   if (Cache.getDefault("SHOW_OVERVIEW", false))
428   {
429   this.overviewMenuItem_actionPerformed(null);
430   }
431  
432   addKeyListener();
433  
434   final List selviews = new ArrayList<>();
435   final List origview = new ArrayList<>();
436   final String menuLabel = MessageManager
437   .getString("label.copy_format_from");
438   ViewSelectionMenu vsel = new ViewSelectionMenu(menuLabel,
439   new ViewSetProvider()
440   {
441  
442   @Override
443   public AlignmentPanel[] getAllAlignmentPanels()
444   {
445   origview.clear();
446   origview.add(alignPanel);
447   // make an array of all alignment panels except for this one
448   List aps = new ArrayList<>(
449   Arrays.asList(Desktop.getAlignmentPanels(null)));
450   aps.remove(AlignFrame.this.alignPanel);
451   return aps.toArray(new AlignmentPanel[aps.size()]);
452   }
453   }, selviews, new ItemListener()
454   {
455  
456   @Override
457   public void itemStateChanged(ItemEvent e)
458   {
459   if (origview.size() > 0)
460   {
461   final AlignmentPanel ap = origview.get(0);
462  
463   /*
464   * Copy the ViewStyle of the selected panel to 'this one'. Don't change value of
465   * 'scaleProteinAsCdna' unless copying from a SplitFrame.
466   */
467   ViewStyleI vs = selviews.get(0).getAlignViewport()
468   .getViewStyle();
469   boolean fromSplitFrame = selviews.get(0)
470   .getAlignViewport().getCodingComplement() != null;
471   if (!fromSplitFrame)
472   {
473   vs.setScaleProteinAsCdna(ap.getAlignViewport()
474   .getViewStyle().isScaleProteinAsCdna());
475   }
476   ap.getAlignViewport().setViewStyle(vs);
477  
478   /*
479   * Also rescale ViewStyle of SplitFrame complement if there is one _and_ it is
480   * set to 'scaledProteinAsCdna'; we don't copy the whole ViewStyle (allow cDNA
481   * protein to have different fonts)
482   */
483   AlignViewportI complement = ap.getAlignViewport()
484   .getCodingComplement();
485   if (complement != null && vs.isScaleProteinAsCdna())
486   {
487   AlignFrame af = Desktop.getAlignFrameFor(complement);
488   ((SplitFrame) af.getSplitViewContainer())
489   .adjustLayout();
490   af.setMenusForViewport();
491   }
492  
493   ap.updateLayout();
494   ap.setSelected(true);
495   ap.alignFrame.setMenusForViewport();
496  
497   }
498   }
499   });
500   if (Cache.getDefault("VERSION", "DEVELOPMENT").toLowerCase(Locale.ROOT)
501   .indexOf("devel") > -1
502   || Cache.getDefault("VERSION", "DEVELOPMENT")
503   .toLowerCase(Locale.ROOT).indexOf("test") > -1)
504   {
505   formatMenu.add(vsel);
506   }
507   addFocusListener(new FocusAdapter()
508   {
509   @Override
510   public void focusGained(FocusEvent e)
511   {
512   Jalview.getInstance().setCurrentAlignFrame(AlignFrame.this);
513   }
514   });
515  
516   }
517  
518   /**
519   * Change the filename and format for the alignment, and enable the 'reload'
520   * button functionality.
521   *
522   * @param file
523   * valid filename
524   * @param format
525   * format of file
526   */
527   public void setFileName(String file, FileFormatI format)
528   {
529   fileName = file;
530   setFileFormat(format);
531   reload.setEnabled(true);
532   }
533  
534   /**
535   * JavaScript will have this, maybe others. More dependable than a file name
536   * and maintains a reference to the actual bytes loaded.
537   *
538   * @param file
539   */
540   public void setFileObject(File file)
541   {
542   this.fileObject = file;
543   }
544  
545   /**
546   * Add a KeyListener with handlers for various KeyPressed and KeyReleased
547   * events
548   */
549   void addKeyListener()
550   {
551   addKeyListener(new KeyAdapter()
552   {
553   @Override
554   public void keyPressed(KeyEvent evt)
555   {
556   if (viewport.cursorMode
557   && ((evt.getKeyCode() >= KeyEvent.VK_0
558   && evt.getKeyCode() <= KeyEvent.VK_9)
559   || (evt.getKeyCode() >= KeyEvent.VK_NUMPAD0
560   && evt.getKeyCode() <= KeyEvent.VK_NUMPAD9))
561   && Character.isDigit(evt.getKeyChar()))
562   {
563   alignPanel.getSeqPanel().numberPressed(evt.getKeyChar());
564   }
565  
566   switch (evt.getKeyCode())
567   {
568  
569   case 27: // escape key
570   deselectAllSequenceMenuItem_actionPerformed(null);
571  
572   break;
573  
574   case KeyEvent.VK_DOWN:
575   if (evt.isAltDown() || !viewport.cursorMode)
576   {
577   moveSelectedSequences(false);
578   }
579   if (viewport.cursorMode)
580   {
581   alignPanel.getSeqPanel().moveCursor(0, 1, evt.isShiftDown());
582   }
583   break;
584  
585   case KeyEvent.VK_UP:
586   if (evt.isAltDown() || !viewport.cursorMode)
587   {
588   moveSelectedSequences(true);
589   }
590   if (viewport.cursorMode)
591   {
592   alignPanel.getSeqPanel().moveCursor(0, -1, evt.isShiftDown());
593   }
594  
595   break;
596  
597   case KeyEvent.VK_LEFT:
598   if (evt.isAltDown() || !viewport.cursorMode)
599   {
600   slideSequences(false,
601   alignPanel.getSeqPanel().getKeyboardNo1());
602   }
603   else
604   {
605   alignPanel.getSeqPanel().moveCursor(-1, 0, evt.isShiftDown());
606   }
607  
608   break;
609  
610   case KeyEvent.VK_RIGHT:
611   if (evt.isAltDown() || !viewport.cursorMode)
612   {
613   slideSequences(true, alignPanel.getSeqPanel().getKeyboardNo1());
614   }
615   else
616   {
617   alignPanel.getSeqPanel().moveCursor(1, 0, evt.isShiftDown());
618   }
619   break;
620  
621   case KeyEvent.VK_SPACE:
622   if (viewport.cursorMode)
623   {
624   alignPanel.getSeqPanel().insertGapAtCursor(evt.isControlDown()
625   || evt.isShiftDown() || evt.isAltDown());
626   }
627   break;
628  
629   // case KeyEvent.VK_A:
630   // if (viewport.cursorMode)
631   // {
632   // alignPanel.seqPanel.insertNucAtCursor(false,"A");
633   // //jalview.bin.Console.outPrintln("A");
634   // }
635   // break;
636   /*
637   * case KeyEvent.VK_CLOSE_BRACKET: if (viewport.cursorMode) {
638   * jalview.bin.Console.outPrintln("closing bracket"); } break;
639   */
640   case KeyEvent.VK_DELETE:
641   case KeyEvent.VK_BACK_SPACE:
642   if (!viewport.cursorMode)
643   {
644   cut_actionPerformed();
645   }
646   else
647   {
648   alignPanel.getSeqPanel().deleteGapAtCursor(evt.isControlDown()
649   || evt.isShiftDown() || evt.isAltDown());
650   }
651  
652   break;
653  
654   case KeyEvent.VK_S:
655   if (viewport.cursorMode)
656   {
657   alignPanel.getSeqPanel().setCursorRow();
658   }
659   break;
660   case KeyEvent.VK_C:
661   if (viewport.cursorMode && !evt.isControlDown())
662   {
663   alignPanel.getSeqPanel().setCursorColumn();
664   }
665   break;
666   case KeyEvent.VK_P:
667   if (viewport.cursorMode)
668   {
669   alignPanel.getSeqPanel().setCursorPosition();
670   }
671   break;
672  
673   case KeyEvent.VK_ENTER:
674   case KeyEvent.VK_COMMA:
675   if (viewport.cursorMode)
676   {
677   alignPanel.getSeqPanel().setCursorRowAndColumn();
678   }
679   break;
680  
681   case KeyEvent.VK_Q:
682   if (viewport.cursorMode)
683   {
684   alignPanel.getSeqPanel().setSelectionAreaAtCursor(true);
685   }
686   break;
687   case KeyEvent.VK_M:
688   if (viewport.cursorMode)
689   {
690   alignPanel.getSeqPanel().setSelectionAreaAtCursor(false);
691   }
692   break;
693  
694   case KeyEvent.VK_F2:
695   viewport.cursorMode = !viewport.cursorMode;
696   setStatus(MessageManager
697   .formatMessage("label.keyboard_editing_mode", new String[]
698   { (viewport.cursorMode ? "on" : "off") }));
699   if (viewport.cursorMode)
700   {
701   ViewportRanges ranges = viewport.getRanges();
702   alignPanel.getSeqPanel().seqCanvas.cursorX = ranges
703   .getStartRes();
704   alignPanel.getSeqPanel().seqCanvas.cursorY = ranges
705   .getStartSeq();
706   }
707   alignPanel.getSeqPanel().seqCanvas.repaint();
708   break;
709  
710   case KeyEvent.VK_F1:
711   try
712   {
713   Help.showHelpWindow();
714   } catch (Exception ex)
715   {
716   ex.printStackTrace();
717   }
718   break;
719   case KeyEvent.VK_H:
720   {
721   boolean toggleSeqs = !evt.isControlDown();
722   boolean toggleCols = !evt.isShiftDown();
723   toggleHiddenRegions(toggleSeqs, toggleCols);
724   break;
725   }
726   case KeyEvent.VK_B:
727   {
728   boolean toggleSel = evt.isControlDown() || evt.isMetaDown();
729   boolean modifyExisting = true; // always modify, don't clear
730   // evt.isShiftDown();
731   boolean invertHighlighted = evt.isAltDown();
732   avc.markHighlightedColumns(invertHighlighted, modifyExisting,
733   toggleSel);
734   break;
735   }
736   case KeyEvent.VK_PAGE_UP:
737   viewport.getRanges().pageUp();
738   break;
739   case KeyEvent.VK_PAGE_DOWN:
740   viewport.getRanges().pageDown();
741   break;
742   }
743   }
744  
745   @Override
746   public void keyReleased(KeyEvent evt)
747   {
748   switch (evt.getKeyCode())
749   {
750   case KeyEvent.VK_LEFT:
751   if (evt.isAltDown() || !viewport.cursorMode)
752   {
753   viewport.firePropertyChange("alignment", null,
754   viewport.getAlignment().getSequences());
755   }
756   break;
757  
758   case KeyEvent.VK_RIGHT:
759   if (evt.isAltDown() || !viewport.cursorMode)
760   {
761   viewport.firePropertyChange("alignment", null,
762   viewport.getAlignment().getSequences());
763   }
764   break;
765   }
766   }
767   });
768   }
769  
770   public void addAlignmentPanel(final AlignmentPanel ap, boolean newPanel)
771   {
772   ap.alignFrame = this;
773   avc = new jalview.controller.AlignViewController(this, viewport,
774   alignPanel);
775  
776   alignPanels.add(ap);
777  
778   PaintRefresher.Register(ap, ap.av.getSequenceSetId());
779  
780   int aSize = alignPanels.size();
781  
782   tabbedPane.setVisible(aSize > 1 || ap.av.getViewName() != null);
783  
784   if (aSize == 1 && ap.av.getViewName() == null)
785   {
786   this.getContentPane().add(ap, BorderLayout.CENTER);
787   }
788   else
789   {
790   if (aSize == 2)
791   {
792   setInitialTabVisible();
793   }
794  
795   expandViews.setEnabled(true);
796   gatherViews.setEnabled(true);
797   tabbedPane.addTab(ap.av.getViewName(), ap);
798  
799   ap.setVisible(false);
800   }
801  
802   if (newPanel)
803   {
804   if (ap.av.isPadGaps())
805   {
806   ap.av.getAlignment().padGaps();
807   }
808   ap.av.updateConservation(ap);
809   ap.av.updateConsensus(ap);
810   ap.av.updateSecondaryStructureConsensus(ap);
811   ap.av.updateStrucConsensus(ap);
812   }
813   }
814  
815   public void setInitialTabVisible()
816   {
817   expandViews.setEnabled(true);
818   gatherViews.setEnabled(true);
819   tabbedPane.setVisible(true);
820   AlignmentPanel first = alignPanels.get(0);
821   tabbedPane.addTab(first.av.getViewName(), first);
822   this.getContentPane().add(tabbedPane, BorderLayout.CENTER);
823   }
824  
825   public AlignViewport getViewport()
826   {
827   return viewport;
828   }
829  
830   /* Set up intrinsic listeners for dynamically generated GUI bits. */
831   private void addServiceListeners()
832   {
833   final java.beans.PropertyChangeListener thisListener;
834   Desktop.instance.addJalviewPropertyChangeListener("services",
835   thisListener = new java.beans.PropertyChangeListener()
836   {
837   @Override
838   public void propertyChange(PropertyChangeEvent evt)
839   {
840   // // jalview.bin.Console.outPrintln("Discoverer property
841   // change.");
842   // if (evt.getPropertyName().equals("services"))
843   {
844   SwingUtilities.invokeLater(new Runnable()
845   {
846  
847   @Override
848   public void run()
849   {
850   jalview.bin.Console.errPrintln(
851   "Rebuild WS Menu for service change");
852   BuildWebServiceMenu();
853   }
854  
855   });
856   }
857   }
858   });
859   addInternalFrameListener(new javax.swing.event.InternalFrameAdapter()
860   {
861   @Override
862   public void internalFrameClosed(
863   javax.swing.event.InternalFrameEvent evt)
864   {
865   // jalview.bin.Console.outPrintln("deregistering discoverer listener");
866   Desktop.instance.removeJalviewPropertyChangeListener("services",
867   thisListener);
868   closeMenuItem_actionPerformed(true);
869   }
870   });
871   // Finally, build the menu once to get current service state
872   new Thread(new Runnable()
873   {
874   @Override
875   public void run()
876   {
877   BuildWebServiceMenu();
878   }
879   }).start();
880   }
881  
882   /**
883   * Configure menu items that vary according to whether the alignment is
884   * nucleotide or protein
885   */
886   public void setGUINucleotide()
887   {
888   AlignmentI al = getViewport().getAlignment();
889   boolean nucleotide = al.isNucleotide();
890  
891   loadVcf.setVisible(nucleotide);
892   showTranslation.setVisible(nucleotide);
893   showReverse.setVisible(nucleotide);
894   showReverseComplement.setVisible(nucleotide);
895   conservationMenuItem.setEnabled(!nucleotide);
896   modifyConservation
897   .setEnabled(!nucleotide && conservationMenuItem.isSelected());
898   byConsensusSecondaryStructureMenuItem.setEnabled(!nucleotide);
899   modifyConsensusSecondaryStructureThreshold.setEnabled(!nucleotide
900   && byConsensusSecondaryStructureMenuItem.isSelected());
901   showGroupConservation.setEnabled(!nucleotide);
902  
903   showComplementMenuItem
904   .setText(nucleotide ? MessageManager.getString("label.protein")
905   : MessageManager.getString("label.nucleotide"));
906   }
907  
908   /**
909   * set up menus for the current viewport. This may be called after any
910   * operation that affects the data in the current view (selection changed,
911   * etc) to update the menus to reflect the new state.
912   */
913   @Override
914   public void setMenusForViewport()
915   {
916   setMenusFromViewport(viewport);
917   }
918  
919   /**
920   * Need to call this method when tabs are selected for multiple views, or when
921   * loading from Jalview2XML.java
922   *
923   * @param av
924   * AlignViewport
925   */
926   public void setMenusFromViewport(AlignViewport av)
927   {
928   padGapsMenuitem.setSelected(av.isPadGaps());
929   colourTextMenuItem.setSelected(av.isShowColourText());
930   abovePIDThreshold.setSelected(av.getAbovePIDThreshold());
931   modifyPID.setEnabled(abovePIDThreshold.isSelected());
932   conservationMenuItem.setSelected(av.getConservationSelected());
933   modifyConservation.setEnabled(conservationMenuItem.isSelected());
934   byConsensusSecondaryStructureMenuItem
935   .setSelected(av.getByConsensusSecondaryStructureSelected());
936   modifyConsensusSecondaryStructureThreshold
937   .setEnabled(byConsensusSecondaryStructureMenuItem.isSelected());
938   seqLimits.setSelected(av.getShowJVSuffix());
939   idRightAlign.setSelected(av.isRightAlignIds());
940   centreColumnLabelsMenuItem.setState(av.isCentreColumnLabels());
941   renderGapsMenuItem.setSelected(av.isRenderGaps());
942   wrapMenuItem.setSelected(av.getWrapAlignment());
943   scaleAbove.setVisible(av.getWrapAlignment());
944   scaleLeft.setVisible(av.getWrapAlignment());
945   scaleRight.setVisible(av.getWrapAlignment());
946   annotationPanelMenuItem.setState(av.isShowAnnotation());
947   /*
948   * Show/hide annotations only enabled if annotation panel is shown
949   */
950   showAllSeqAnnotations.setEnabled(annotationPanelMenuItem.getState());
951   hideAllSeqAnnotations.setEnabled(annotationPanelMenuItem.getState());
952   showAllAlAnnotations.setEnabled(annotationPanelMenuItem.getState());
953   hideAllAlAnnotations.setEnabled(annotationPanelMenuItem.getState());
954   viewBoxesMenuItem.setSelected(av.getShowBoxes());
955   viewTextMenuItem.setSelected(av.getShowText());
956   showNonconservedMenuItem.setSelected(av.getShowUnconserved());
957   showGroupConsensus.setSelected(av.isShowGroupConsensus());
958   showGroupSSConsensus.setSelected(av.isShowGroupSSConsensus());
959   showStrucProvider.setSelected(av.isShowStructureProvider());
960   showGroupConservation.setSelected(av.isShowGroupConservation());
961   showConsensusHistogram.setSelected(av.isShowConsensusHistogram());
962   showSequenceLogo.setSelected(av.isShowSequenceLogo());
963   normaliseSequenceLogo.setSelected(av.isNormaliseSequenceLogo());
964  
965   ColourMenuHelper.setColourSelected(colourMenu,
966   av.getGlobalColourScheme());
967  
968   showSeqFeatures.setSelected(av.isShowSequenceFeatures());
969   hiddenMarkers.setState(av.getShowHiddenMarkers());
970   applyToAllGroups.setState(av.getColourAppliesToAllGroups());
971   showNpFeatsMenuitem.setSelected(av.isShowNPFeats());
972   showDbRefsMenuitem.setSelected(av.isShowDBRefs());
973   autoCalculate.setSelected(av.autoCalculateConsensus);
974   sortByTree.setSelected(av.sortByTree);
975   listenToViewSelections.setSelected(av.followSelection);
976  
977   showProducts.setEnabled(canShowProducts());
978   setGroovyEnabled(Desktop.getGroovyConsole() != null);
979  
980   updateEditMenuBar();
981   }
982  
983   /**
984   * Set the enabled state of the 'Run Groovy' option in the Calculate menu
985   *
986   * @param b
987   */
988   public void setGroovyEnabled(boolean b)
989   {
990   runGroovy.setEnabled(b);
991   }
992  
993   private IProgressIndicator progressBar;
994  
995   /*
996   * (non-Javadoc)
997   *
998   * @see jalview.gui.IProgressIndicator#setProgressBar(java.lang.String, long)
999   */
1000   @Override
1001   public void setProgressBar(String message, long id)
1002   {
1003   if (!Platform.isHeadless() && progressBar != null)
1004   progressBar.setProgressBar(message, id);
1005   }
1006  
1007   @Override
1008   public JProgressBar getProgressBar(long id)
1009   {
1010   if (progressBar != null)
1011   return progressBar.getProgressBar(id);
1012   return null;
1013   }
1014  
1015   @Override
1016   public String getMessage(long id)
1017   {
1018   return progressBar.getMessage(id);
1019   }
1020  
1021   @Override
1022   public void setProgressBarMessage(long id, String message)
1023   {
1024   progressBar.setProgressBarMessage(id, message);
1025   }
1026  
1027   @Override
1028   public void registerHandler(final long id,
1029   final IProgressIndicatorHandler handler)
1030   {
1031   if (progressBar != null)
1032   progressBar.registerHandler(id, handler);
1033   }
1034  
1035   /**
1036   *
1037   * @return true if any progress bars are still active
1038   */
1039   @Override
1040   public boolean operationInProgress()
1041   {
1042   return progressBar == null ? false : progressBar.operationInProgress();
1043   }
1044  
1045   /**
1046   * Sets the text of the status bar. Note that setting a null or empty value
1047   * will cause the status bar to be hidden, with possibly undesirable flicker
1048   * of the screen layout.
1049   */
1050   @Override
1051   public void setStatus(String text)
1052   {
1053   statusBar.setText(text == null || text.isEmpty() ? " " : text);
1054   }
1055  
1056   /*
1057   * Added so Castor Mapping file can obtain Jalview Version
1058   */
1059   public String getVersion()
1060   {
1061   return Cache.getProperty("VERSION");
1062   }
1063  
1064   public FeatureRenderer getFeatureRenderer()
1065   {
1066   return alignPanel.getSeqPanel().seqCanvas.getFeatureRenderer();
1067   }
1068  
1069   @Override
1070   public void fetchSequence_actionPerformed()
1071   {
1072   new SequenceFetcher(this);
1073   }
1074  
1075   @Override
1076   public void addFromFile_actionPerformed(ActionEvent e)
1077   {
1078   Desktop.instance.inputLocalFileMenuItem_actionPerformed(viewport);
1079   }
1080  
1081   @Override
1082   public void reload_actionPerformed(ActionEvent e)
1083   {
1084   if (fileName != null)
1085   {
1086   // TODO: JAL-1108 - ensure all associated frames are closed regardless of
1087   // originating file's format
1088   // TODO: work out how to recover feature settings for correct view(s) when
1089   // file is reloaded.
1090   if (FileFormat.Jalview.equals(currentFileFormat))
1091   {
1092   JInternalFrame[] frames = Desktop.desktop.getAllFrames();
1093   for (int i = 0; i < frames.length; i++)
1094   {
1095   if (frames[i] instanceof AlignFrame && frames[i] != this
1096   && ((AlignFrame) frames[i]).fileName != null
1097   && ((AlignFrame) frames[i]).fileName.equals(fileName))
1098   {
1099   try
1100   {
1101   frames[i].setSelected(true);
1102   Desktop.instance.closeAssociatedWindows();
1103   } catch (java.beans.PropertyVetoException ex)
1104   {
1105   }
1106   }
1107  
1108   }
1109   Desktop.instance.closeAssociatedWindows();
1110  
1111   FileLoader loader = new FileLoader();
1112   DataSourceType protocol = HttpUtils.startsWithHttpOrHttps(fileName)
1113   ? DataSourceType.URL
1114   : DataSourceType.FILE;
1115   loader.LoadFile(viewport, fileName, protocol, currentFileFormat);
1116   }
1117   else
1118   {
1119   Rectangle bounds = this.getBounds();
1120  
1121   FileLoader loader = new FileLoader();
1122  
1123   AlignFrame newframe = null;
1124  
1125   if (fileObject == null)
1126   {
1127  
1128   DataSourceType protocol = HttpUtils.startsWithHttpOrHttps(
1129   fileName) ? DataSourceType.URL : DataSourceType.FILE;
1130   newframe = loader.LoadFileWaitTillLoaded(fileName, protocol,
1131   currentFileFormat);
1132   }
1133   else
1134   {
1135   newframe = loader.LoadFileWaitTillLoaded(fileObject,
1136   DataSourceType.FILE, currentFileFormat);
1137   }
1138  
1139   newframe.setBounds(bounds);
1140   if (featureSettings != null && featureSettings.isShowing())
1141   {
1142   final Rectangle fspos = featureSettings.frame.getBounds();
1143   // TODO: need a 'show feature settings' function that takes bounds -
1144   // need to refactor Desktop.addFrame
1145   newframe.featureSettings_actionPerformed(null);
1146   final FeatureSettings nfs = newframe.featureSettings;
1147   SwingUtilities.invokeLater(new Runnable()
1148   {
1149   @Override
1150   public void run()
1151   {
1152   nfs.frame.setBounds(fspos);
1153   }
1154   });
1155   this.featureSettings.close();
1156   this.featureSettings = null;
1157   }
1158   this.closeMenuItem_actionPerformed(true);
1159   }
1160   }
1161   }
1162  
1163   @Override
1164   public void addFromText_actionPerformed(ActionEvent e)
1165   {
1166   Desktop.instance
1167   .inputTextboxMenuItem_actionPerformed(viewport.getAlignPanel());
1168   }
1169  
1170   @Override
1171   public void addFromURL_actionPerformed(ActionEvent e)
1172   {
1173   Desktop.instance.inputURLMenuItem_actionPerformed(viewport);
1174   }
1175  
1176   @Override
1177   public void save_actionPerformed(ActionEvent e)
1178   {
1179   if (fileName == null || (currentFileFormat == null)
1180   || HttpUtils.startsWithHttpOrHttps(fileName))
1181   {
1182   saveAs_actionPerformed();
1183   }
1184   else
1185   {
1186   saveAlignment(fileName, currentFileFormat);
1187   }
1188   }
1189  
1190   /**
1191   * Saves the alignment to a file with a name chosen by the user, if necessary
1192   * warning if a file would be overwritten
1193   */
1194   @Override
1195   public void saveAs_actionPerformed()
1196   {
1197   JalviewFileChooser chooser = JalviewFileChooser.forWrite(
1198   Cache.getProperty("LAST_DIRECTORY"),
1199   currentFileFormat == null ? null : currentFileFormat.getName(),
1200   true);
1201  
1202   chooser.setFileView(new JalviewFileView());
1203   chooser.setDialogTitle(
1204   MessageManager.getString("label.save_alignment_to_file"));
1205   chooser.setToolTipText(MessageManager.getString("action.save"));
1206  
1207   int value = chooser.showSaveDialog(this);
1208  
1209   if (value != JalviewFileChooser.APPROVE_OPTION)
1210   {
1211   return;
1212   }
1213   currentFileFormat = chooser.getSelectedFormat();
1214   while (currentFileFormat == null)
1215   {
1216   // if the autodetect format from extension failed..
1217   JvOptionPane.showInternalMessageDialog(Desktop.desktop,
1218   MessageManager
1219   .getString("label.select_file_format_before_saving"),
1220   MessageManager.getString("label.file_format_not_specified"),
1221   JvOptionPane.WARNING_MESSAGE);
1222   value = chooser.showSaveDialog(this);
1223   if (value != JalviewFileChooser.APPROVE_OPTION)
1224   {
1225   return;
1226   }
1227   currentFileFormat = chooser.getSelectedFormat();
1228   }
1229  
1230   fileName = chooser.getSelectedFile().getPath();
1231  
1232   Cache.setProperty("DEFAULT_FILE_FORMAT", currentFileFormat.getName());
1233   Cache.setProperty("LAST_DIRECTORY", fileName);
1234   saveAlignment(fileName, currentFileFormat);
1235   }
1236  
1237   boolean lastSaveSuccessful = false;
1238  
1239   FileFormatI lastFormatSaved;
1240  
1241   String lastFilenameSaved;
1242  
1243   // globalLastFormatSaved across alignment windows (only for alignments with no
1244   // previous format)
1245   private static FileFormatI globalLastFormatSaved = null;
1246  
1247   public static void setLastAlignmentSavedFormat(FileFormatI f)
1248   {
1249   globalLastFormatSaved = f;
1250   }
1251  
1252   public static FileFormatI getLastAlignmentSavedFormat()
1253   {
1254   return globalLastFormatSaved;
1255   }
1256  
1257   /**
1258   * Raise a dialog or status message for the last call to saveAlignment.
1259   *
1260   * @return true if last call to saveAlignment(file, format) was successful.
1261   */
1262   public boolean isSaveAlignmentSuccessful()
1263   {
1264  
1265   if (!lastSaveSuccessful)
1266   {
1267   if (!Platform.isHeadless())
1268   {
1269   JvOptionPane.showInternalMessageDialog(this, MessageManager
1270   .formatMessage("label.couldnt_save_file", new Object[]
1271   { lastFilenameSaved }),
1272   MessageManager.getString("label.error_saving_file"),
1273   JvOptionPane.WARNING_MESSAGE);
1274   }
1275   else
1276   {
1277   Console.error(MessageManager
1278   .formatMessage("label.couldnt_save_file", new Object[]
1279   { lastFilenameSaved }));
1280   }
1281   }
1282   else
1283   {
1284  
1285   setStatus(MessageManager.formatMessage(
1286   "label.successfully_saved_to_file_in_format", new Object[]
1287   { lastFilenameSaved, lastFormatSaved }));
1288  
1289   }
1290   return lastSaveSuccessful;
1291   }
1292  
1293   /**
1294   * Saves the alignment to the specified file path, in the specified format,
1295   * which may be an alignment format, or Jalview project format. If the
1296   * alignment has hidden regions, or the format is one capable of including
1297   * non-sequence data (features, annotations, groups), then the user may be
1298   * prompted to specify what to include in the output.
1299   *
1300   * @param file
1301   * @param format
1302   */
1303   public void saveAlignment(String file, FileFormatI format)
1304   {
1305   saveAlignment(file, format, false, false);
1306   }
1307  
1308   public void saveAlignment(String file, FileFormatI format, boolean stdout,
1309   boolean forceBackup)
1310   {
1311   lastSaveSuccessful = true;
1312   if (!stdout)
1313   {
1314   lastFilenameSaved = file;
1315   }
1316   lastFormatSaved = format;
1317   globalLastFormatSaved = format;
1318  
1319   if (FileFormat.Jalview.equals(format))
1320   {
1321   String shortName = title;
1322   if (shortName.indexOf(File.separatorChar) > -1)
1323   {
1324   shortName = shortName
1325   .substring(shortName.lastIndexOf(File.separatorChar) + 1);
1326   }
1327   // TODO deal with stdout=true
1328   lastSaveSuccessful = new Jalview2XML().saveAlignment(this, file,
1329   shortName);
1330  
1331   Console.debug("lastSaveSuccessful=" + lastSaveSuccessful);
1332   if (lastSaveSuccessful)
1333   {
1334   this.getViewport().setSavedUpToDate(true);
1335   }
1336  
1337   statusBar.setText(MessageManager.formatMessage(
1338   "label.successfully_saved_to_file_in_format", new Object[]
1339   { file, format }));
1340  
1341   return;
1342   }
1343  
1344   AlignExportSettingsI options = new AlignExportSettingsAdapter(false);
1345   Runnable cancelAction = () -> {
1346   lastSaveSuccessful = false;
1347   };
1348   Runnable outputAction = () -> {
1349   // todo defer this to inside formatSequences (or later)
1350   AlignmentExportData exportData = viewport.getAlignExportData(options);
1351   String output = new FormatAdapter(alignPanel, options)
1352   .formatSequences(format, exportData.getAlignment(),
1353   exportData.getOmitHidden(),
1354   exportData.getStartEndPostions(),
1355   viewport.getAlignment().getHiddenColumns());
1356   if (output == null)
1357   {
1358   lastSaveSuccessful = false;
1359   }
1360   else
1361   {
1362   // create backupfiles object and get new temp filename destination
1363   boolean doBackup = forceBackup
1364   || (BackupFiles.getEnabled() && !stdout);
1365   BackupFiles backupfiles = null;
1366   if (doBackup)
1367   {
1368   Console.trace("ALIGNFRAME making backupfiles object for " + file);
1369   backupfiles = new BackupFiles(file);
1370   }
1371   try
1372   {
1373   String tempFilePath = doBackup ? backupfiles.getTempFilePath()
1374   : file;
1375   Console.trace("ALIGNFRAME setting PrintWriter");
1376   PrintWriter out = stdout
1377   ? new PrintWriter(new OutputStreamWriter(System.out))
1378   : new PrintWriter(new FileWriter(tempFilePath));
1379  
1380   if (backupfiles != null)
1381   {
1382   Console.trace("ALIGNFRAME about to write to temp file "
1383   + backupfiles.getTempFilePath());
1384   }
1385  
1386   out.print(output);
1387   out.flush();
1388   if (!stdout)
1389   {
1390   Console.trace("ALIGNFRAME about to close file");
1391   out.close();
1392   Console.trace("ALIGNFRAME closed file");
1393   }
1394   AlignFrame.this.setTitle(stdout ? "STDOUT" : file);
1395   if (stdout)
1396   {
1397   statusBar.setText(MessageManager.formatMessage(
1398   "label.successfully_printed_to_stdout_in_format",
1399   new Object[]
1400   { format.getName() }));
1401   }
1402   else
1403   {
1404   statusBar.setText(MessageManager.formatMessage(
1405   "label.successfully_saved_to_file_in_format",
1406   new Object[]
1407   { fileName, format.getName() }));
1408   }
1409   lastSaveSuccessful = true;
1410   } catch (IOException e)
1411   {
1412   lastSaveSuccessful = false;
1413   Console.error(
1414   "ALIGNFRAME Something happened writing the temp file");
1415   Console.error(e.getMessage());
1416   Console.debug(Cache.getStackTraceString(e));
1417   } catch (Exception ex)
1418   {
1419   lastSaveSuccessful = false;
1420   Console.error(
1421   "ALIGNFRAME Something unexpected happened writing the temp file");
1422   Console.error(ex.getMessage());
1423   Console.debug(Cache.getStackTraceString(ex));
1424   }
1425  
1426   if (doBackup)
1427   {
1428   backupfiles.setWriteSuccess(lastSaveSuccessful);
1429   Console.debug("ALIGNFRAME writing temp file was "
1430   + (lastSaveSuccessful ? "" : "NOT ") + "successful");
1431   // do the backup file roll and rename the temp file to actual file
1432   Console.trace("ALIGNFRAME about to rollBackupsAndRenameTempFile");
1433   lastSaveSuccessful = backupfiles.rollBackupsAndRenameTempFile();
1434   Console.debug("ALIGNFRAME performed rollBackupsAndRenameTempFile "
1435   + (lastSaveSuccessful ? "" : "un") + "successfully");
1436   }
1437  
1438   Console.debug("lastSaveSuccessful=" + lastSaveSuccessful);
1439   if (lastSaveSuccessful)
1440   {
1441   AlignFrame.this.getViewport().setSavedUpToDate(true);
1442   }
1443   }
1444   };
1445  
1446   /*
1447   * show dialog with export options if applicable; else just do it
1448   */
1449   if (AlignExportOptions.isNeeded(viewport, format))
1450   {
1451   AlignExportOptions choices = new AlignExportOptions(
1452   alignPanel.getAlignViewport(), format, options);
1453   choices.setResponseAction(0, outputAction);
1454   choices.setResponseAction(1, cancelAction);
1455   choices.showDialog();
1456   }
1457   else
1458   {
1459   try
1460   {
1461   outputAction.run();
1462   } catch (Exception e)
1463   {
1464   // TODO Auto-generated catch block
1465   e.printStackTrace();
1466   }
1467   }
1468   }
1469  
1470   /**
1471   * Outputs the alignment to textbox in the requested format, if necessary
1472   * first prompting the user for whether to include hidden regions or
1473   * non-sequence data
1474   *
1475   * @param fileFormatName
1476   */
1477   @Override
1478   protected void outputText_actionPerformed(String fileFormatName)
1479   {
1480   FileFormatI fileFormat = FileFormats.getInstance()
1481   .forName(fileFormatName);
1482   AlignExportSettingsI options = new AlignExportSettingsAdapter(false);
1483   Runnable outputAction = () -> {
1484   // todo defer this to inside formatSequences (or later)
1485   AlignmentExportData exportData = viewport.getAlignExportData(options);
1486   CutAndPasteTransfer cap = new CutAndPasteTransfer();
1487   cap.setForInput(null);
1488   try
1489   {
1490   FileFormatI format = fileFormat;
1491   cap.setText(new FormatAdapter(alignPanel, options).formatSequences(
1492   format, exportData.getAlignment(),
1493   exportData.getOmitHidden(),
1494   exportData.getStartEndPostions(),
1495   viewport.getAlignment().getHiddenColumns()));
1496   Desktop.addInternalFrame(cap, MessageManager.formatMessage(
1497   "label.alignment_output_command", new Object[]
1498   { fileFormat.getName() }), 600, 500);
1499   } catch (OutOfMemoryError oom)
1500   {
1501   new OOMWarning("Outputting alignment as " + fileFormat.getName(),
1502   oom);
1503   cap.dispose();
1504   }
1505   };
1506  
1507   /*
1508   * show dialog with export options if applicable; else just do it
1509   */
1510   if (AlignExportOptions.isNeeded(viewport, fileFormat))
1511   {
1512   AlignExportOptions choices = new AlignExportOptions(
1513   alignPanel.getAlignViewport(), fileFormat, options);
1514   choices.setResponseAction(0, outputAction);
1515   choices.showDialog();
1516   }
1517   else
1518   {
1519   try
1520   {
1521   outputAction.run();
1522   } catch (Exception e)
1523   {
1524   e.printStackTrace();
1525   }
1526   }
1527   }
1528  
1529   /**
1530   * DOCUMENT ME!
1531   *
1532   * @param e
1533   * DOCUMENT ME!
1534   */
1535   @Override
1536   protected void htmlMenuItem_actionPerformed(ActionEvent e)
1537   {
1538   HtmlSvgOutput htmlSVG = new HtmlSvgOutput(alignPanel);
1539   try
1540   {
1541   htmlSVG.exportHTML(null);
1542   } catch (ImageOutputException x)
1543   {
1544   // report problem to console and raise dialog
1545   }
1546   }
1547  
1548   @Override
1549   public void bioJSMenuItem_actionPerformed(ActionEvent e)
1550   {
1551   BioJsHTMLOutput bjs = new BioJsHTMLOutput(alignPanel);
1552   try
1553   {
1554   bjs.exportHTML(null);
1555   } catch (ImageOutputException x)
1556   {
1557   // report problem to console and raise dialog
1558   }
1559   }
1560  
1561   public void createImageMap(File file, String image)
1562   {
1563   try
1564   {
1565   alignPanel.makePNGImageMap(file, image);
1566   } catch (ImageOutputException x)
1567   {
1568   // report problem to console and raise dialog
1569   }
1570   }
1571  
1572   @Override
1573   public void createPNG_actionPerformed(ActionEvent e)
1574   {
1575   try
1576   {
1577   createPNG(null);
1578   } catch (ImageOutputException ioex)
1579   {
1580   // raise dialog, and report via console
1581   }
1582   }
1583  
1584   @Override
1585   public void createEPS_actionPerformed(ActionEvent e)
1586   {
1587   try
1588   {
1589   createEPS(null);
1590   } catch (ImageOutputException ioex)
1591   {
1592   // raise dialog, and report via console
1593   }
1594  
1595   }
1596  
1597   @Override
1598   public void createSVG_actionPerformed(ActionEvent e)
1599   {
1600   try
1601   {
1602   createSVG(null);
1603   } catch (ImageOutputException ioex)
1604   {
1605   // raise dialog, and report via console
1606   }
1607  
1608   }
1609  
1610   /**
1611   * Creates a PNG image of the alignment and writes it to the given file. If
1612   * the file is null, the user is prompted to choose a file.
1613   *
1614   * @param f
1615   */
1616   public void createPNG(File f) throws ImageOutputException
1617   {
1618   createPNG(f, null, BitmapImageSizing.defaultBitmapImageSizing());
1619   }
1620  
1621   public void createPNG(File f, String renderer, BitmapImageSizing userBis)
1622   throws ImageOutputException
1623   {
1624   alignPanel.makeAlignmentImage(TYPE.PNG, f, renderer, userBis);
1625   }
1626  
1627   /**
1628   * Creates an EPS image of the alignment and writes it to the given file. If
1629   * the file is null, the user is prompted to choose a file.
1630   *
1631   * @param f
1632   */
1633   public void createEPS(File f) throws ImageOutputException
1634   {
1635   createEPS(f, null);
1636   }
1637  
1638   public void createEPS(File f, String renderer) throws ImageOutputException
1639   {
1640   alignPanel.makeAlignmentImage(TYPE.EPS, f, renderer);
1641   }
1642  
1643   /**
1644   * Creates an SVG image of the alignment and writes it to the given file. If
1645   * the file is null, the user is prompted to choose a file.
1646   *
1647   * @param f
1648   */
1649   public void createSVG(File f) throws ImageOutputException
1650   {
1651   createSVG(f, null);
1652   }
1653  
1654   public void createSVG(File f, String renderer) throws ImageOutputException
1655   {
1656   alignPanel.makeAlignmentImage(TYPE.SVG, f, renderer);
1657   }
1658  
1659   @Override
1660   public void pageSetup_actionPerformed(ActionEvent e)
1661   {
1662   PrinterJob printJob = PrinterJob.getPrinterJob();
1663   PrintThread.pf = printJob.pageDialog(printJob.defaultPage());
1664   }
1665  
1666   /**
1667   * DOCUMENT ME!
1668   *
1669   * @param e
1670   * DOCUMENT ME!
1671   */
1672   @Override
1673   public void printMenuItem_actionPerformed(ActionEvent e)
1674   {
1675   // Putting in a thread avoids Swing painting problems
1676   PrintThread thread = new PrintThread(alignPanel);
1677   thread.start();
1678   }
1679  
1680   @Override
1681   public void exportFeatures_actionPerformed(ActionEvent e)
1682   {
1683   new AnnotationExporter(alignPanel).exportFeatures();
1684   }
1685  
1686   @Override
1687   public void exportAnnotations_actionPerformed(ActionEvent e)
1688   {
1689   new AnnotationExporter(alignPanel).exportAnnotations();
1690   }
1691  
1692   @Override
1693   public void associatedData_actionPerformed(ActionEvent e)
1694   {
1695   final JalviewFileChooser chooser = new JalviewFileChooser(
1696   Cache.getProperty("LAST_DIRECTORY"));
1697   chooser.setFileView(new JalviewFileView());
1698   String tooltip = MessageManager
1699   .getString("label.load_jalview_annotations");
1700   chooser.setDialogTitle(tooltip);
1701   chooser.setToolTipText(tooltip);
1702   chooser.setResponseHandler(0, () -> {
1703   String choice = chooser.getSelectedFile().getPath();
1704   Cache.setProperty("LAST_DIRECTORY", choice);
1705   loadJalviewDataFile(chooser.getSelectedFile(), null, null, null);
1706   });
1707  
1708   chooser.showOpenDialog(this);
1709   }
1710  
1711   /**
1712   * Close the current view or all views in the alignment frame. If the frame
1713   * only contains one view then the alignment will be removed from memory.
1714   *
1715   * @param closeAllTabs
1716   */
1717   @Override
1718   public void closeMenuItem_actionPerformed(boolean closeAllTabs)
1719   {
1720   if (alignPanels != null && alignPanels.size() < 2)
1721   {
1722   closeAllTabs = true;
1723   }
1724  
1725   Desktop.closeModal(this);
1726  
1727   try
1728   {
1729   if (alignPanels != null)
1730   {
1731   if (closeAllTabs)
1732   {
1733   if (this.isClosed())
1734   {
1735   // really close all the windows - otherwise wait till
1736   // setClosed(true) is called
1737   for (int i = 0; i < alignPanels.size(); i++)
1738   {
1739   AlignmentPanel ap = alignPanels.get(i);
1740   ap.closePanel();
1741   }
1742   }
1743   }
1744   else
1745   {
1746   closeView(alignPanel);
1747   }
1748   }
1749   if (closeAllTabs)
1750   {
1751   if (featureSettings != null && featureSettings.isOpen())
1752   {
1753   featureSettings.close();
1754   featureSettings = null;
1755   }
1756  
1757   /*
1758   * this will raise an INTERNAL_FRAME_CLOSED event and this method will be called
1759   * recursively, with the frame now in 'closed' state
1760   */
1761   this.setClosed(true);
1762   }
1763   } catch (Exception ex)
1764   {
1765   ex.printStackTrace();
1766   }
1767   }
1768  
1769   /**
1770   * Close the specified panel and close up tabs appropriately.
1771   *
1772   * @param panelToClose
1773   */
1774   public void closeView(AlignmentPanel panelToClose)
1775   {
1776   int index = tabbedPane.getSelectedIndex();
1777   int closedindex = tabbedPane.indexOfComponent(panelToClose);
1778   alignPanels.remove(panelToClose);
1779   panelToClose.closePanel();
1780   panelToClose = null;
1781  
1782   tabbedPane.removeTabAt(closedindex);
1783   tabbedPane.validate();
1784  
1785   if (index > closedindex || index == tabbedPane.getTabCount())
1786   {
1787   // modify currently selected tab index if necessary.
1788   index--;
1789   }
1790  
1791   this.tabSelectionChanged(index);
1792   }
1793  
1794   /**
1795   * DOCUMENT ME!
1796   */
1797   void updateEditMenuBar()
1798   {
1799  
1800   if (viewport.getHistoryList().size() > 0)
1801   {
1802   undoMenuItem.setEnabled(true);
1803   CommandI command = viewport.getHistoryList().peek();
1804   undoMenuItem.setText(MessageManager
1805   .formatMessage("label.undo_command", new Object[]
1806   { command.getDescription() }));
1807   }
1808   else
1809   {
1810   undoMenuItem.setEnabled(false);
1811   undoMenuItem.setText(MessageManager.getString("action.undo"));
1812   }
1813  
1814   if (viewport.getRedoList().size() > 0)
1815   {
1816   redoMenuItem.setEnabled(true);
1817  
1818   CommandI command = viewport.getRedoList().peek();
1819   redoMenuItem.setText(MessageManager
1820   .formatMessage("label.redo_command", new Object[]
1821   { command.getDescription() }));
1822   }
1823   else
1824   {
1825   redoMenuItem.setEnabled(false);
1826   redoMenuItem.setText(MessageManager.getString("action.redo"));
1827   }
1828   }
1829  
1830   @Override
1831   public void addHistoryItem(CommandI command)
1832   {
1833   if (command.getSize() > 0)
1834   {
1835   viewport.addToHistoryList(command);
1836   viewport.clearRedoList();
1837   updateEditMenuBar();
1838   viewport.updateHiddenColumns();
1839   // viewport.hasHiddenColumns = (viewport.getColumnSelection() != null
1840   // && viewport.getColumnSelection().getHiddenColumns() != null &&
1841   // viewport.getColumnSelection()
1842   // .getHiddenColumns().size() > 0);
1843   }
1844   }
1845  
1846   /**
1847   *
1848   * @return alignment objects for all views
1849   */
1850   public AlignmentI[] getViewAlignments()
1851   {
1852   if (alignPanels != null)
1853   {
1854   AlignmentI[] als = new AlignmentI[alignPanels.size()];
1855   int i = 0;
1856   for (AlignmentPanel ap : alignPanels)
1857   {
1858   als[i++] = ap.av.getAlignment();
1859   }
1860   return als;
1861   }
1862   if (viewport != null)
1863   {
1864   return new AlignmentI[] { viewport.getAlignment() };
1865   }
1866   return null;
1867   }
1868  
1869   /**
1870   * DOCUMENT ME!
1871   *
1872   * @param e
1873   * DOCUMENT ME!
1874   */
1875   @Override
1876   protected void undoMenuItem_actionPerformed(ActionEvent e)
1877   {
1878   if (viewport.getHistoryList().isEmpty())
1879   {
1880   return;
1881   }
1882   CommandI command = viewport.getHistoryList().pop();
1883   viewport.addToRedoList(command);
1884   command.undoCommand(getViewAlignments());
1885  
1886   AlignmentViewport originalSource = getOriginatingSource(command);
1887   updateEditMenuBar();
1888  
1889   if (originalSource != null)
1890   {
1891   if (originalSource != viewport)
1892   {
1893   Console.warn(
1894   "Implementation worry: mismatch of viewport origin for undo");
1895   }
1896   originalSource.updateHiddenColumns();
1897   // originalSource.hasHiddenColumns = (viewport.getColumnSelection() !=
1898   // null
1899   // && viewport.getColumnSelection().getHiddenColumns() != null &&
1900   // viewport.getColumnSelection()
1901   // .getHiddenColumns().size() > 0);
1902   originalSource.firePropertyChange("alignment", null,
1903   originalSource.getAlignment().getSequences());
1904   }
1905   }
1906  
1907   /**
1908   * DOCUMENT ME!
1909   *
1910   * @param e
1911   * DOCUMENT ME!
1912   */
1913   @Override
1914   protected void redoMenuItem_actionPerformed(ActionEvent e)
1915   {
1916   if (viewport.getRedoList().size() < 1)
1917   {
1918   return;
1919   }
1920  
1921   CommandI command = viewport.getRedoList().pop();
1922   viewport.addToHistoryList(command);
1923   command.doCommand(getViewAlignments());
1924  
1925   AlignmentViewport originalSource = getOriginatingSource(command);
1926   updateEditMenuBar();
1927  
1928   if (originalSource != null)
1929   {
1930  
1931   if (originalSource != viewport)
1932   {
1933   Console.warn(
1934   "Implementation worry: mismatch of viewport origin for redo");
1935   }
1936   originalSource.updateHiddenColumns();
1937   // originalSource.hasHiddenColumns = (viewport.getColumnSelection() !=
1938   // null
1939   // && viewport.getColumnSelection().getHiddenColumns() != null &&
1940   // viewport.getColumnSelection()
1941   // .getHiddenColumns().size() > 0);
1942   originalSource.firePropertyChange("alignment", null,
1943   originalSource.getAlignment().getSequences());
1944   }
1945   }
1946  
1947   AlignmentViewport getOriginatingSource(CommandI command)
1948   {
1949   AlignmentViewport originalSource = null;
1950   // For sequence removal and addition, we need to fire
1951   // the property change event FROM the viewport where the
1952   // original alignment was altered
1953   AlignmentI al = null;
1954   if (command instanceof EditCommand)
1955   {
1956   EditCommand editCommand = (EditCommand) command;
1957   al = editCommand.getAlignment();
1958   List comps = PaintRefresher.components
1959   .get(viewport.getSequenceSetId());
1960  
1961   for (Component comp : comps)
1962   {
1963   if (comp instanceof AlignmentPanel)
1964   {
1965   if (al == ((AlignmentPanel) comp).av.getAlignment())
1966   {
1967   originalSource = ((AlignmentPanel) comp).av;
1968   break;
1969   }
1970   }
1971   }
1972   }
1973  
1974   if (originalSource == null)
1975   {
1976   // The original view is closed, we must validate
1977   // the current view against the closed view first
1978   if (al != null)
1979   {
1980   PaintRefresher.validateSequences(al, viewport.getAlignment());
1981   }
1982  
1983   originalSource = viewport;
1984   }
1985  
1986   return originalSource;
1987   }
1988  
1989   /**
1990   * Calls AlignmentI.moveSelectedSequencesByOne with current sequence selection
1991   * or the sequence under cursor in keyboard mode
1992   *
1993   * @param up
1994   * or down (if !up)
1995   */
1996   public void moveSelectedSequences(boolean up)
1997   {
1998   SequenceGroup sg = viewport.getSelectionGroup();
1999  
2000   if (sg == null)
2001   {
2002   if (viewport.cursorMode)
2003   {
2004   sg = new SequenceGroup();
2005   sg.addSequence(viewport.getAlignment().getSequenceAt(
2006   alignPanel.getSeqPanel().seqCanvas.cursorY), false);
2007   }
2008   else
2009   {
2010   return;
2011   }
2012   }
2013  
2014   if (sg.getSize() < 1)
2015   {
2016   return;
2017   }
2018  
2019   // TODO: JAL-3733 - add an event to the undo buffer for this !
2020  
2021   viewport.getAlignment().moveSelectedSequencesByOne(sg,
2022   viewport.getHiddenRepSequences(), up);
2023   alignPanel.paintAlignment(true, false);
2024   }
2025  
2026   synchronized void slideSequences(boolean right, int size)
2027   {
2028   List sg = new ArrayList<>();
2029   if (viewport.cursorMode)
2030   {
2031   sg.add(viewport.getAlignment()
2032   .getSequenceAt(alignPanel.getSeqPanel().seqCanvas.cursorY));
2033   }
2034   else if (viewport.getSelectionGroup() != null
2035   && viewport.getSelectionGroup().getSize() != viewport
2036   .getAlignment().getHeight())
2037   {
2038   sg = viewport.getSelectionGroup()
2039   .getSequences(viewport.getHiddenRepSequences());
2040   }
2041  
2042   if (sg.size() < 1)
2043   {
2044   return;
2045   }
2046  
2047   List invertGroup = new ArrayList<>();
2048  
2049   for (SequenceI seq : viewport.getAlignment().getSequences())
2050   {
2051   if (!sg.contains(seq))
2052   {
2053   invertGroup.add(seq);
2054   }
2055   }
2056  
2057   SequenceI[] seqs1 = sg.toArray(new SequenceI[0]);
2058  
2059   SequenceI[] seqs2 = new SequenceI[invertGroup.size()];
2060   for (int i = 0; i < invertGroup.size(); i++)
2061   {
2062   seqs2[i] = invertGroup.get(i);
2063   }
2064  
2065   SlideSequencesCommand ssc;
2066   if (right)
2067   {
2068   ssc = new SlideSequencesCommand("Slide Sequences", seqs2, seqs1, size,
2069   viewport.getGapCharacter());
2070   }
2071   else
2072   {
2073   ssc = new SlideSequencesCommand("Slide Sequences", seqs1, seqs2, size,
2074   viewport.getGapCharacter());
2075   }
2076  
2077   int groupAdjustment = 0;
2078   if (ssc.getGapsInsertedBegin() && right)
2079   {
2080   if (viewport.cursorMode)
2081   {
2082   alignPanel.getSeqPanel().moveCursor(size, 0);
2083   }
2084   else
2085   {
2086   groupAdjustment = size;
2087   }
2088   }
2089   else if (!ssc.getGapsInsertedBegin() && !right)
2090   {
2091   if (viewport.cursorMode)
2092   {
2093   alignPanel.getSeqPanel().moveCursor(-size, 0);
2094   }
2095   else
2096   {
2097   groupAdjustment = -size;
2098   }
2099   }
2100  
2101   if (groupAdjustment != 0)
2102   {
2103   viewport.getSelectionGroup().setStartRes(
2104   viewport.getSelectionGroup().getStartRes() + groupAdjustment);
2105   viewport.getSelectionGroup().setEndRes(
2106   viewport.getSelectionGroup().getEndRes() + groupAdjustment);
2107   }
2108  
2109   /*
2110   * just extend the last slide command if compatible; but not if in SplitFrame
2111   * mode (to ensure all edits are broadcast - JAL-1802)
2112   */
2113   boolean appendHistoryItem = false;
2114   Deque historyList = viewport.getHistoryList();
2115   boolean inSplitFrame = getSplitViewContainer() != null;
2116   if (!inSplitFrame && historyList != null && historyList.size() > 0
2117   && historyList.peek() instanceof SlideSequencesCommand)
2118   {
2119   appendHistoryItem = ssc.appendSlideCommand(
2120   (SlideSequencesCommand) historyList.peek());
2121   }
2122  
2123   if (!appendHistoryItem)
2124   {
2125   addHistoryItem(ssc);
2126   }
2127  
2128   repaint();
2129   }
2130  
2131   /**
2132   * DOCUMENT ME!
2133   *
2134   * @param e
2135   * DOCUMENT ME!
2136   */
2137   @Override
2138   protected void copy_actionPerformed()
2139   {
2140   if (viewport.getSelectionGroup() == null)
2141   {
2142   return;
2143   }
2144   // TODO: preserve the ordering of displayed alignment annotation in any
2145   // internal paste (particularly sequence associated annotation)
2146   SequenceI[] seqs = viewport.getSelectionAsNewSequence();
2147   String[] omitHidden = null;
2148  
2149   if (viewport.hasHiddenColumns())
2150   {
2151   omitHidden = viewport.getViewAsString(true);
2152   }
2153  
2154   String output = new FormatAdapter().formatSequences(FileFormat.Fasta,
2155   seqs, omitHidden, null);
2156  
2157   StringSelection ss = new StringSelection(output);
2158  
2159   try
2160   {
2161   jalview.gui.Desktop.internalCopy = true;
2162   // Its really worth setting the clipboard contents
2163   // to empty before setting the large StringSelection!!
2164   Toolkit.getDefaultToolkit().getSystemClipboard()
2165   .setContents(new StringSelection(""), null);
2166  
2167   Toolkit.getDefaultToolkit().getSystemClipboard().setContents(ss,
2168   Desktop.instance);
2169   } catch (OutOfMemoryError er)
2170   {
2171   new OOMWarning("copying region", er);
2172   return;
2173   }
2174  
2175   HiddenColumns hiddenColumns = null;
2176   if (viewport.hasHiddenColumns())
2177   {
2178   int hiddenOffset = viewport.getSelectionGroup().getStartRes();
2179   int hiddenCutoff = viewport.getSelectionGroup().getEndRes();
2180  
2181   // create new HiddenColumns object with copy of hidden regions
2182   // between startRes and endRes, offset by startRes
2183   hiddenColumns = new HiddenColumns(
2184   viewport.getAlignment().getHiddenColumns(), hiddenOffset,
2185   hiddenCutoff, hiddenOffset);
2186   }
2187  
2188   Desktop.jalviewClipboard = new Object[] { seqs,
2189   viewport.getAlignment().getDataset(), hiddenColumns };
2190   setStatus(MessageManager.formatMessage(
2191   "label.copied_sequences_to_clipboard", new Object[]
2192   { Integer.valueOf(seqs.length).toString() }));
2193   }
2194  
2195   /**
2196   * DOCUMENT ME!
2197   *
2198   * @param e
2199   * DOCUMENT ME!
2200   */
2201   @Override
2202   protected void pasteNew_actionPerformed(ActionEvent e)
2203   {
2204   paste(true);
2205   }
2206  
2207   /**
2208   * DOCUMENT ME!
2209   *
2210   * @param e
2211   * DOCUMENT ME!
2212   */
2213   @Override
2214   protected void pasteThis_actionPerformed(ActionEvent e)
2215   {
2216   paste(false);
2217   }
2218  
2219   /**
2220   * Paste contents of Jalview clipboard
2221   *
2222   * @param newAlignment
2223   * true to paste to a new alignment, otherwise add to this.
2224   */
2225   void paste(boolean newAlignment)
2226   {
2227   boolean externalPaste = true;
2228   try
2229   {
2230   Clipboard c = Toolkit.getDefaultToolkit().getSystemClipboard();
2231   Transferable contents = c.getContents(this);
2232  
2233   if (contents == null)
2234   {
2235   return;
2236   }
2237  
2238   String str;
2239   FileFormatI format;
2240   try
2241   {
2242   str = (String) contents.getTransferData(DataFlavor.stringFlavor);
2243   if (str.length() < 1)
2244   {
2245   return;
2246   }
2247  
2248   format = new IdentifyFile().identify(str, DataSourceType.PASTE);
2249  
2250   } catch (OutOfMemoryError er)
2251   {
2252   new OOMWarning("Out of memory pasting sequences!!", er);
2253   return;
2254   }
2255  
2256   SequenceI[] sequences;
2257   boolean annotationAdded = false;
2258   AlignmentI alignment = null;
2259  
2260   if (Desktop.jalviewClipboard != null)
2261   {
2262   // The clipboard was filled from within Jalview, we must use the
2263   // sequences
2264   // And dataset from the copied alignment
2265   SequenceI[] newseq = (SequenceI[]) Desktop.jalviewClipboard[0];
2266   // be doubly sure that we create *new* sequence objects.
2267   sequences = new SequenceI[newseq.length];
2268   for (int i = 0; i < newseq.length; i++)
2269   {
2270   sequences[i] = new Sequence(newseq[i]);
2271   }
2272   alignment = new Alignment(sequences);
2273   externalPaste = false;
2274   }
2275   else
2276   {
2277   // parse the clipboard as an alignment.
2278   alignment = new FormatAdapter().readFile(str, DataSourceType.PASTE,
2279   format);
2280   sequences = alignment.getSequencesArray();
2281   }
2282  
2283   int alwidth = 0;
2284   ArrayList newGraphGroups = new ArrayList<>();
2285   int fgroup = -1;
2286  
2287   if (newAlignment)
2288   {
2289  
2290   if (Desktop.jalviewClipboard != null)
2291   {
2292   // dataset is inherited
2293   alignment.setDataset((Alignment) Desktop.jalviewClipboard[1]);
2294   }
2295   else
2296   {
2297   // new dataset is constructed
2298   alignment.setDataset(null);
2299   }
2300   alwidth = alignment.getWidth() + 1;
2301   }
2302   else
2303   {
2304   AlignmentI pastedal = alignment; // preserve pasted alignment object
2305   // Add pasted sequences and dataset into existing alignment.
2306   alignment = viewport.getAlignment();
2307   alwidth = alignment.getWidth() + 1;
2308   // decide if we need to import sequences from an existing dataset
2309   boolean importDs = Desktop.jalviewClipboard != null
2310   && Desktop.jalviewClipboard[1] != alignment.getDataset();
2311   // importDs==true instructs us to copy over new dataset sequences from
2312   // an existing alignment
2313   Vector newDs = (importDs) ? new Vector<>() : null; // used to
2314   // create
2315   // minimum dataset set
2316  
2317   for (int i = 0; i < sequences.length; i++)
2318   {
2319   if (importDs)
2320   {
2321   newDs.addElement(null);
2322   }
2323   SequenceI ds = sequences[i].getDatasetSequence(); // null for a simple
2324   // paste
2325   if (importDs && ds != null)
2326   {
2327   if (!newDs.contains(ds))
2328   {
2329   newDs.setElementAt(ds, i);
2330   ds = new Sequence(ds);
2331   // update with new dataset sequence
2332   sequences[i].setDatasetSequence(ds);
2333   }
2334   else
2335   {
2336   ds = sequences[newDs.indexOf(ds)].getDatasetSequence();
2337   }
2338   }
2339   else
2340   {
2341   // copy and derive new dataset sequence
2342   sequences[i] = sequences[i].deriveSequence();
2343   alignment.getDataset()
2344   .addSequence(sequences[i].getDatasetSequence());
2345   // TODO: avoid creation of duplicate dataset sequences with a
2346   // 'contains' method using SequenceI.equals()/SequenceI.contains()
2347   }
2348   alignment.addSequence(sequences[i]); // merges dataset
2349   }
2350   if (newDs != null)
2351   {
2352   newDs.clear(); // tidy up
2353   }
2354   if (alignment.getAlignmentAnnotation() != null)
2355   {
2356   for (AlignmentAnnotation alan : alignment
2357   .getAlignmentAnnotation())
2358   {
2359   if (alan.graphGroup > fgroup)
2360   {
2361   fgroup = alan.graphGroup;
2362   }
2363   }
2364   }
2365   if (pastedal.getAlignmentAnnotation() != null)
2366   {
2367   // Add any annotation attached to alignment.
2368   AlignmentAnnotation[] alann = pastedal.getAlignmentAnnotation();
2369   for (int i = 0; i < alann.length; i++)
2370   {
2371   annotationAdded = true;
2372   if (alann[i].sequenceRef == null && !alann[i].autoCalculated)
2373   {
2374   AlignmentAnnotation newann = new AlignmentAnnotation(
2375   alann[i]);
2376   if (newann.graphGroup > -1)
2377   {
2378   if (newGraphGroups.size() <= newann.graphGroup
2379   || newGraphGroups.get(newann.graphGroup) == null)
2380   {
2381   for (int q = newGraphGroups
2382   .size(); q <= newann.graphGroup; q++)
2383   {
2384   newGraphGroups.add(q, null);
2385   }
2386   newGraphGroups.set(newann.graphGroup,
2387   Integer.valueOf(++fgroup));
2388   }
2389   newann.graphGroup = newGraphGroups.get(newann.graphGroup)
2390   .intValue();
2391   }
2392  
2393   newann.padAnnotation(alwidth);
2394   alignment.addAnnotation(newann);
2395   }
2396   }
2397   }
2398   }
2399   if (!newAlignment)
2400   {
2401   // /////
2402   // ADD HISTORY ITEM
2403   //
2404   addHistoryItem(new EditCommand(
2405   MessageManager.getString("label.add_sequences"),
2406   Action.PASTE, sequences, 0, alignment.getWidth(),
2407   alignment));
2408   }
2409   // Add any annotations attached to sequences
2410   for (int i = 0; i < sequences.length; i++)
2411   {
2412   if (sequences[i].getAnnotation() != null)
2413   {
2414   AlignmentAnnotation newann;
2415   for (int a = 0; a < sequences[i].getAnnotation().length; a++)
2416   {
2417   annotationAdded = true;
2418   newann = sequences[i].getAnnotation()[a];
2419   newann.adjustForAlignment();
2420   newann.padAnnotation(alwidth);
2421   if (newann.graphGroup > -1)
2422   {
2423   if (newann.graphGroup > -1)
2424   {
2425   if (newGraphGroups.size() <= newann.graphGroup
2426   || newGraphGroups.get(newann.graphGroup) == null)
2427   {
2428   for (int q = newGraphGroups
2429   .size(); q <= newann.graphGroup; q++)
2430   {
2431   newGraphGroups.add(q, null);
2432   }
2433   newGraphGroups.set(newann.graphGroup,
2434   Integer.valueOf(++fgroup));
2435   }
2436   newann.graphGroup = newGraphGroups.get(newann.graphGroup)
2437   .intValue();
2438   }
2439   }
2440   // annotation was duplicated earlier
2441   alignment.addAnnotation(sequences[i].getAnnotation()[a]);
2442   // take care of contact matrix too
2443   ContactMatrixI cm = sequences[i]
2444   .getContactMatrixFor(sequences[i].getAnnotation()[a]);
2445   if (cm != null)
2446   {
2447   alignment.addContactListFor(sequences[i].getAnnotation()[a],
2448   cm);
2449   }
2450  
2451   alignment.setAnnotationIndex(sequences[i].getAnnotation()[a],
2452   a);
2453   }
2454   }
2455   }
2456   if (!newAlignment)
2457   {
2458  
2459   // propagate alignment changed.
2460   viewport.getRanges().setEndSeq(alignment.getHeight() - 1);
2461   if (annotationAdded)
2462   {
2463   // Duplicate sequence annotation in all views.
2464   AlignmentI[] alview = this.getViewAlignments();
2465   for (int i = 0; i < sequences.length; i++)
2466   {
2467   AlignmentAnnotation sann[] = sequences[i].getAnnotation();
2468   if (sann == null)
2469   {
2470   continue;
2471   }
2472   for (int avnum = 0; avnum < alview.length; avnum++)
2473   {
2474   if (alview[avnum] != alignment)
2475   {
2476   // duplicate in a view other than the one with input focus
2477   int avwidth = alview[avnum].getWidth() + 1;
2478   // this relies on sann being preserved after we
2479   // modify the sequence's annotation array for each duplication
2480   for (int a = 0; a < sann.length; a++)
2481   {
2482   AlignmentAnnotation newann = new AlignmentAnnotation(
2483   sann[a]);
2484   sequences[i].addAlignmentAnnotation(newann);
2485   newann.padAnnotation(avwidth);
2486   alview[avnum].addAnnotation(newann); // annotation was
2487   // duplicated earlier
2488   // TODO JAL-1145 graphGroups are not updated for sequence
2489   // annotation added to several views. This may cause
2490   // strangeness
2491   alview[avnum].setAnnotationIndex(newann, a);
2492   }
2493   }
2494   }
2495   }
2496   buildSortByAnnotationScoresMenu();
2497   }
2498   viewport.firePropertyChange("alignment", null,
2499   alignment.getSequences());
2500   if (alignPanels != null)
2501   {
2502   for (AlignmentPanel ap : alignPanels)
2503   {
2504   ap.validateAnnotationDimensions(false);
2505   }
2506   }
2507   else
2508   {
2509   alignPanel.validateAnnotationDimensions(false);
2510   }
2511  
2512   }
2513   else
2514   {
2515   AlignFrame af = new AlignFrame(alignment, DEFAULT_WIDTH,
2516   DEFAULT_HEIGHT);
2517   String newtitle = new String("Copied sequences");
2518  
2519   if (Desktop.jalviewClipboard != null
2520   && Desktop.jalviewClipboard[2] != null)
2521   {
2522   HiddenColumns hc = (HiddenColumns) Desktop.jalviewClipboard[2];
2523   af.viewport.setHiddenColumns(hc);
2524   }
2525  
2526   // >>>This is a fix for the moment, until a better solution is
2527   // found!!<<<
2528   af.alignPanel.getSeqPanel().seqCanvas.getFeatureRenderer()
2529   .transferSettings(alignPanel.getSeqPanel().seqCanvas
2530   .getFeatureRenderer());
2531  
2532   // TODO: maintain provenance of an alignment, rather than just make the
2533   // title a concatenation of operations.
2534   if (!externalPaste)
2535   {
2536   if (title.startsWith("Copied sequences"))
2537   {
2538   newtitle = title;
2539   }
2540   else
2541   {
2542   newtitle = newtitle.concat("- from " + title);
2543   }
2544   }
2545   else
2546   {
2547   newtitle = new String("Pasted sequences");
2548   }
2549  
2550   Desktop.addInternalFrame(af, newtitle, DEFAULT_WIDTH,
2551   DEFAULT_HEIGHT);
2552  
2553   }
2554  
2555   } catch (Exception ex)
2556   {
2557   ex.printStackTrace();
2558   jalview.bin.Console.outPrintln("Exception whilst pasting: " + ex);
2559   // could be anything being pasted in here
2560   }
2561  
2562   }
2563  
2564   @Override
2565   protected void expand_newalign(ActionEvent e)
2566   {
2567   try
2568   {
2569   AlignmentI alignment = AlignmentUtils
2570   .expandContext(getViewport().getAlignment(), -1);
2571   AlignFrame af = new AlignFrame(alignment, DEFAULT_WIDTH,
2572   DEFAULT_HEIGHT);
2573   String newtitle = new String("Flanking alignment");
2574  
2575   if (Desktop.jalviewClipboard != null
2576   && Desktop.jalviewClipboard[2] != null)
2577   {
2578   HiddenColumns hc = (HiddenColumns) Desktop.jalviewClipboard[2];
2579   af.viewport.setHiddenColumns(hc);
2580   }
2581  
2582   // >>>This is a fix for the moment, until a better solution is
2583   // found!!<<<
2584   af.alignPanel.getSeqPanel().seqCanvas.getFeatureRenderer()
2585   .transferSettings(alignPanel.getSeqPanel().seqCanvas
2586   .getFeatureRenderer());
2587  
2588   // TODO: maintain provenance of an alignment, rather than just make the
2589   // title a concatenation of operations.
2590   {
2591   if (title.startsWith("Copied sequences"))
2592   {
2593   newtitle = title;
2594   }
2595   else
2596   {
2597   newtitle = newtitle.concat("- from " + title);
2598   }
2599   }
2600  
2601   Desktop.addInternalFrame(af, newtitle, DEFAULT_WIDTH, DEFAULT_HEIGHT);
2602  
2603   } catch (Exception ex)
2604   {
2605   ex.printStackTrace();
2606   jalview.bin.Console.outPrintln("Exception whilst pasting: " + ex);
2607   // could be anything being pasted in here
2608   } catch (OutOfMemoryError oom)
2609   {
2610   new OOMWarning("Viewing flanking region of alignment", oom);
2611   }
2612   }
2613  
2614   /**
2615   * Action Cut (delete and copy) the selected region
2616   */
2617   @Override
2618   protected void cut_actionPerformed()
2619   {
2620   copy_actionPerformed();
2621   delete_actionPerformed();
2622   }
2623  
2624   /**
2625   * Performs menu option to Delete the currently selected region
2626   */
2627   @Override
2628   protected void delete_actionPerformed()
2629   {
2630  
2631   SequenceGroup sg = viewport.getSelectionGroup();
2632   if (sg == null)
2633   {
2634   return;
2635   }
2636  
2637   Runnable okAction = () -> {
2638   SequenceI[] cut = sg.getSequences()
2639   .toArray(new SequenceI[sg.getSize()]);
2640  
2641   addHistoryItem(new EditCommand(
2642   MessageManager.getString("label.cut_sequences"), Action.CUT,
2643   cut, sg.getStartRes(), sg.getEndRes() - sg.getStartRes() + 1,
2644   viewport.getAlignment()));
2645  
2646   viewport.setSelectionGroup(null);
2647   viewport.sendSelection();
2648   viewport.getAlignment().deleteGroup(sg);
2649  
2650   viewport.firePropertyChange("alignment", null,
2651   viewport.getAlignment().getSequences());
2652   if (viewport.getAlignment().getHeight() < 1)
2653   {
2654   try
2655   {
2656   AlignFrame.this.setClosed(true);
2657   } catch (Exception ex)
2658   {
2659   }
2660   }
2661   };
2662  
2663   /*
2664   * If the cut affects all sequences, prompt for confirmation
2665   */
2666   boolean wholeHeight = sg.getSize() == viewport.getAlignment()
2667   .getHeight();
2668   boolean wholeWidth = (((sg.getEndRes() - sg.getStartRes())
2669   + 1) == viewport.getAlignment().getWidth()) ? true : false;
2670   if (wholeHeight && wholeWidth)
2671   {
2672   JvOptionPane dialog = JvOptionPane.newOptionDialog(Desktop.desktop);
2673   dialog.setResponseHandler(0, okAction); // 0 = OK_OPTION
2674   Object[] options = new Object[] {
2675   MessageManager.getString("action.ok"),
2676   MessageManager.getString("action.cancel") };
2677   dialog.showDialog(MessageManager.getString("warn.delete_all"),
2678   MessageManager.getString("label.delete_all"),
2679   JvOptionPane.DEFAULT_OPTION, JvOptionPane.PLAIN_MESSAGE, null,
2680   options, options[0]);
2681   }
2682   else
2683   {
2684   try
2685   {
2686   okAction.run();
2687   } catch (Exception e)
2688   {
2689   e.printStackTrace();
2690   }
2691   }
2692   }
2693  
2694   /**
2695   * DOCUMENT ME!
2696   *
2697   * @param e
2698   * DOCUMENT ME!
2699   */
2700   @Override
2701   protected void deleteGroups_actionPerformed(ActionEvent e)
2702   {
2703   if (avc.deleteGroups())
2704   {
2705   PaintRefresher.Refresh(this, viewport.getSequenceSetId());
2706   alignPanel.updateAnnotation();
2707   alignPanel.paintAlignment(true, true);
2708   }
2709   }
2710  
2711   /**
2712   * DOCUMENT ME!
2713   *
2714   * @param e
2715   * DOCUMENT ME!
2716   */
2717   @Override
2718   public void selectAllSequenceMenuItem_actionPerformed(ActionEvent e)
2719   {
2720   SequenceGroup sg = new SequenceGroup(
2721   viewport.getAlignment().getSequences());
2722  
2723   sg.setEndRes(viewport.getAlignment().getWidth() - 1);
2724   viewport.setSelectionGroup(sg);
2725   viewport.isSelectionGroupChanged(true);
2726   viewport.sendSelection();
2727   // JAL-2034 - should delegate to
2728   // alignPanel to decide if overview needs
2729   // updating.
2730   alignPanel.paintAlignment(false, false);
2731   PaintRefresher.Refresh(alignPanel, viewport.getSequenceSetId());
2732   }
2733  
2734   /**
2735   * DOCUMENT ME!
2736   *
2737   * @param e
2738   * DOCUMENT ME!
2739   */
2740   @Override
2741   public void deselectAllSequenceMenuItem_actionPerformed(ActionEvent e)
2742   {
2743   if (viewport.cursorMode)
2744   {
2745   alignPanel.getSeqPanel().keyboardNo1 = null;
2746   alignPanel.getSeqPanel().keyboardNo2 = null;
2747   }
2748   viewport.setSelectionGroup(null);
2749   viewport.getColumnSelection().clear();
2750   viewport.setSearchResults(null);
2751   alignPanel.getIdPanel().getIdCanvas().searchResults = null;
2752   // JAL-2034 - should delegate to
2753   // alignPanel to decide if overview needs
2754   // updating.
2755   alignPanel.paintAlignment(false, false);
2756   PaintRefresher.Refresh(alignPanel, viewport.getSequenceSetId());
2757   viewport.sendSelection();
2758   }
2759  
2760   /**
2761   * DOCUMENT ME!
2762   *
2763   * @param e
2764   * DOCUMENT ME!
2765   */
2766   @Override
2767   public void invertSequenceMenuItem_actionPerformed(ActionEvent e)
2768   {
2769   SequenceGroup sg = viewport.getSelectionGroup();
2770  
2771   if (sg == null)
2772   {
2773   selectAllSequenceMenuItem_actionPerformed(null);
2774  
2775   return;
2776   }
2777  
2778   for (int i = 0; i < viewport.getAlignment().getSequences().size(); i++)
2779   {
2780   sg.addOrRemove(viewport.getAlignment().getSequenceAt(i), false);
2781   }
2782   // JAL-2034 - should delegate to
2783   // alignPanel to decide if overview needs
2784   // updating.
2785  
2786   alignPanel.paintAlignment(true, false);
2787   PaintRefresher.Refresh(alignPanel, viewport.getSequenceSetId());
2788   viewport.sendSelection();
2789   }
2790  
2791   @Override
2792   public void invertColSel_actionPerformed(ActionEvent e)
2793   {
2794   viewport.invertColumnSelection();
2795   alignPanel.paintAlignment(true, false);
2796   PaintRefresher.Refresh(alignPanel, viewport.getSequenceSetId());
2797   viewport.sendSelection();
2798   }
2799  
2800   /**
2801   * DOCUMENT ME!
2802   *
2803   * @param e
2804   * DOCUMENT ME!
2805   */
2806   @Override
2807   public void remove2LeftMenuItem_actionPerformed(ActionEvent e)
2808   {
2809   trimAlignment(true);
2810   }
2811  
2812   /**
2813   * DOCUMENT ME!
2814   *
2815   * @param e
2816   * DOCUMENT ME!
2817   */
2818   @Override
2819   public void remove2RightMenuItem_actionPerformed(ActionEvent e)
2820   {
2821   trimAlignment(false);
2822   }
2823  
2824   void trimAlignment(boolean trimLeft)
2825   {
2826   ColumnSelection colSel = viewport.getColumnSelection();
2827   int column;
2828  
2829   if (!colSel.isEmpty())
2830   {
2831   if (trimLeft)
2832   {
2833   column = colSel.getMin();
2834   }
2835   else
2836   {
2837   column = colSel.getMax();
2838   }
2839  
2840   SequenceI[] seqs;
2841   if (viewport.getSelectionGroup() != null)
2842   {
2843   seqs = viewport.getSelectionGroup()
2844   .getSequencesAsArray(viewport.getHiddenRepSequences());
2845   }
2846   else
2847   {
2848   seqs = viewport.getAlignment().getSequencesArray();
2849   }
2850  
2851   TrimRegionCommand trimRegion;
2852   if (trimLeft)
2853   {
2854   trimRegion = new TrimRegionCommand("Remove Left", true, seqs,
2855   column, viewport.getAlignment());
2856   viewport.getRanges().setStartRes(0);
2857   }
2858   else
2859   {
2860   trimRegion = new TrimRegionCommand("Remove Right", false, seqs,
2861   column, viewport.getAlignment());
2862   }
2863  
2864   setStatus(MessageManager.formatMessage("label.removed_columns",
2865   new String[]
2866   { Integer.valueOf(trimRegion.getSize()).toString() }));
2867  
2868   addHistoryItem(trimRegion);
2869  
2870   for (SequenceGroup sg : viewport.getAlignment().getGroups())
2871   {
2872   if ((trimLeft && !sg.adjustForRemoveLeft(column))
2873   || (!trimLeft && !sg.adjustForRemoveRight(column)))
2874   {
2875   viewport.getAlignment().deleteGroup(sg);
2876   }
2877   }
2878  
2879   viewport.firePropertyChange("alignment", null,
2880   viewport.getAlignment().getSequences());
2881   }
2882   }
2883  
2884   /**
2885   * DOCUMENT ME!
2886   *
2887   * @param e
2888   * DOCUMENT ME!
2889   */
2890   @Override
2891   public void removeGappedColumnMenuItem_actionPerformed(ActionEvent e)
2892   {
2893   int start = 0, end = viewport.getAlignment().getWidth() - 1;
2894  
2895   SequenceI[] seqs;
2896   if (viewport.getSelectionGroup() != null)
2897   {
2898   seqs = viewport.getSelectionGroup()
2899   .getSequencesAsArray(viewport.getHiddenRepSequences());
2900   start = viewport.getSelectionGroup().getStartRes();
2901   end = viewport.getSelectionGroup().getEndRes();
2902   }
2903   else
2904   {
2905   seqs = viewport.getAlignment().getSequencesArray();
2906   }
2907  
2908   RemoveGapColCommand removeGapCols = new RemoveGapColCommand(
2909   "Remove Gapped Columns", seqs, start, end,
2910   viewport.getAlignment());
2911  
2912   addHistoryItem(removeGapCols);
2913  
2914   setStatus(MessageManager.formatMessage("label.removed_empty_columns",
2915   new Object[]
2916   { Integer.valueOf(removeGapCols.getSize()).toString() }));
2917  
2918   // This is to maintain viewport position on first residue
2919   // of first sequence
2920   SequenceI seq = viewport.getAlignment().getSequenceAt(0);
2921   ViewportRanges ranges = viewport.getRanges();
2922   int startRes = seq.findPosition(ranges.getStartRes());
2923   // ShiftList shifts;
2924   // viewport.getAlignment().removeGaps(shifts=new ShiftList());
2925   // edit.alColumnChanges=shifts.getInverse();
2926   // if (viewport.hasHiddenColumns)
2927   // viewport.getColumnSelection().compensateForEdits(shifts);
2928   ranges.setStartRes(seq.findIndex(startRes) - 1);
2929   viewport.firePropertyChange("alignment", null,
2930   viewport.getAlignment().getSequences());
2931  
2932   }
2933  
2934   /**
2935   * DOCUMENT ME!
2936   *
2937   * @param e
2938   * DOCUMENT ME!
2939   */
2940   @Override
2941   public void removeAllGapsMenuItem_actionPerformed(ActionEvent e)
2942   {
2943   int start = 0, end = viewport.getAlignment().getWidth() - 1;
2944  
2945   SequenceI[] seqs;
2946   if (viewport.getSelectionGroup() != null)
2947   {
2948   seqs = viewport.getSelectionGroup()
2949   .getSequencesAsArray(viewport.getHiddenRepSequences());
2950   start = viewport.getSelectionGroup().getStartRes();
2951   end = viewport.getSelectionGroup().getEndRes();
2952   }
2953   else
2954   {
2955   seqs = viewport.getAlignment().getSequencesArray();
2956   }
2957  
2958   // This is to maintain viewport position on first residue
2959   // of first sequence
2960   SequenceI seq = viewport.getAlignment().getSequenceAt(0);
2961   int startRes = seq.findPosition(viewport.getRanges().getStartRes());
2962  
2963   addHistoryItem(new RemoveGapsCommand("Remove Gaps", seqs, start, end,
2964   viewport.getAlignment()));
2965  
2966   viewport.getRanges().setStartRes(seq.findIndex(startRes) - 1);
2967  
2968   viewport.firePropertyChange("alignment", null,
2969   viewport.getAlignment().getSequences());
2970  
2971   }
2972  
2973   /**
2974   * DOCUMENT ME!
2975   *
2976   * @param e
2977   * DOCUMENT ME!
2978   */
2979   @Override
2980   public void padGapsMenuitem_actionPerformed(ActionEvent e)
2981   {
2982   viewport.setPadGaps(padGapsMenuitem.isSelected());
2983   viewport.firePropertyChange("alignment", null,
2984   viewport.getAlignment().getSequences());
2985   }
2986  
2987   /**
2988   * Opens a Finder dialog
2989   *
2990   * @param e
2991   */
2992   @Override
2993   public void findMenuItem_actionPerformed(ActionEvent e)
2994   {
2995   new Finder(alignPanel, false, null);
2996   }
2997  
2998   /**
2999   * Create a new view of the current alignment.
3000   */
3001   @Override
3002   public void newView_actionPerformed(ActionEvent e)
3003   {
3004   newView(null, true);
3005   }
3006  
3007   /**
3008   * Creates and shows a new view of the current alignment.
3009   *
3010   * @param viewTitle
3011   * title of newly created view; if null, one will be generated
3012   * @param copyAnnotation
3013   * if true then duplicate all annnotation, groups and settings
3014   * @return new alignment panel, already displayed.
3015   */
3016   public AlignmentPanel newView(String viewTitle, boolean copyAnnotation)
3017   {
3018   /*
3019   * Create a new AlignmentPanel (with its own, new Viewport)
3020   */
3021   AlignmentPanel newap = new jalview.project.Jalview2XML()
3022   .copyAlignPanel(alignPanel);
3023   if (!copyAnnotation)
3024   {
3025   /*
3026   * remove all groups and annotation except for the automatic stuff
3027   */
3028   newap.av.getAlignment().deleteAllGroups();
3029   newap.av.getAlignment().deleteAllAnnotations(false);
3030   }
3031  
3032   newap.av.setGatherViewsHere(false);
3033  
3034   if (viewport.getViewName() == null)
3035   {
3036   viewport.setViewName(
3037   MessageManager.getString("label.view_name_original"));
3038   }
3039  
3040   /*
3041   * Views share the same edits undo and redo stacks
3042   */
3043   newap.av.setHistoryList(viewport.getHistoryList());
3044   newap.av.setRedoList(viewport.getRedoList());
3045  
3046   /*
3047   * copy any visualisation settings that are not saved in the project
3048   */
3049   newap.av.setColourAppliesToAllGroups(
3050   viewport.getColourAppliesToAllGroups());
3051  
3052   /*
3053   * Views share the same mappings; need to deregister any new mappings created by
3054   * copyAlignPanel, and register the new reference to the shared mappings
3055   */
3056   newap.av.replaceMappings(viewport.getAlignment());
3057  
3058   /*
3059   * start up cDNA consensus (if applicable) now mappings are in place
3060   */
3061   if (newap.av.initComplementConsensus())
3062   {
3063   newap.refresh(true); // adjust layout of annotations
3064   }
3065  
3066   newap.av.setViewName(getNewViewName(viewTitle));
3067  
3068   addAlignmentPanel(newap, true);
3069   newap.alignmentChanged();
3070  
3071   if (alignPanels.size() == 2)
3072   {
3073   viewport.setGatherViewsHere(true);
3074   }
3075   tabbedPane.setSelectedIndex(tabbedPane.getTabCount() - 1);
3076  
3077   return newap;
3078   }
3079  
3080   /**
3081   * Make a new name for the view, ensuring it is unique within the current
3082   * sequenceSetId. (This used to be essential for Jalview Project archives, but
3083   * these now use viewId. Unique view names are still desirable for usability.)
3084   *
3085   * @param viewTitle
3086   * @return
3087   */
3088   protected String getNewViewName(String viewTitle)
3089   {
3090   int index = Desktop.getViewCount(viewport.getSequenceSetId());
3091   boolean addFirstIndex = false;
3092   if (viewTitle == null || viewTitle.trim().length() == 0)
3093   {
3094   viewTitle = MessageManager.getString("action.view");
3095   addFirstIndex = true;
3096   }
3097   else
3098   {
3099   index = 1;// we count from 1 if given a specific name
3100   }
3101   String newViewName = viewTitle + ((addFirstIndex) ? " " + index : "");
3102  
3103   List comps = PaintRefresher.components
3104   .get(viewport.getSequenceSetId());
3105  
3106   List existingNames = getExistingViewNames(comps);
3107  
3108   while (existingNames.contains(newViewName))
3109   {
3110   newViewName = viewTitle + " " + (++index);
3111   }
3112   return newViewName;
3113   }
3114  
3115   /**
3116   * Returns a list of distinct view names found in the given list of
3117   * components. View names are held on the viewport of an AlignmentPanel.
3118   *
3119   * @param comps
3120   * @return
3121   */
3122   protected List getExistingViewNames(List comps)
3123   {
3124   List existingNames = new ArrayList<>();
3125   for (Component comp : comps)
3126   {
3127   if (comp instanceof AlignmentPanel)
3128   {
3129   AlignmentPanel ap = (AlignmentPanel) comp;
3130   if (!existingNames.contains(ap.av.getViewName()))
3131   {
3132   existingNames.add(ap.av.getViewName());
3133   }
3134   }
3135   }
3136   return existingNames;
3137   }
3138  
3139   /**
3140   * Explode tabbed views into separate windows.
3141   */
3142   @Override
3143   public void expandViews_actionPerformed(ActionEvent e)
3144   {
3145   Desktop.explodeViews(this);
3146   }
3147  
3148   /**
3149   * Gather views in separate windows back into a tabbed presentation.
3150   */
3151   @Override
3152   public void gatherViews_actionPerformed(ActionEvent e)
3153   {
3154   Desktop.instance.gatherViews(this);
3155   }
3156  
3157   /**
3158   * DOCUMENT ME!
3159   *
3160   * @param e
3161   * DOCUMENT ME!
3162   */
3163   @Override
3164   public void font_actionPerformed(ActionEvent e)
3165   {
3166   new FontChooser(alignPanel);
3167   }
3168  
3169   /**
3170   * DOCUMENT ME!
3171   *
3172   * @param e
3173   * DOCUMENT ME!
3174   */
3175   @Override
3176   protected void seqLimit_actionPerformed(ActionEvent e)
3177   {
3178   viewport.setShowJVSuffix(seqLimits.isSelected());
3179  
3180   alignPanel.getIdPanel().getIdCanvas()
3181   .setPreferredSize(alignPanel.calculateIdWidth());
3182   alignPanel.paintAlignment(true, false);
3183   }
3184  
3185   @Override
3186   public void idRightAlign_actionPerformed(ActionEvent e)
3187   {
3188   viewport.setRightAlignIds(idRightAlign.isSelected());
3189   alignPanel.paintAlignment(false, false);
3190   }
3191  
3192   @Override
3193   public void centreColumnLabels_actionPerformed(ActionEvent e)
3194   {
3195   viewport.setCentreColumnLabels(centreColumnLabelsMenuItem.getState());
3196   alignPanel.paintAlignment(false, false);
3197   }
3198  
3199   /*
3200   * (non-Javadoc)
3201   *
3202   * @see jalview.jbgui.GAlignFrame#followHighlight_actionPerformed()
3203   */
3204   @Override
3205   protected void followHighlight_actionPerformed()
3206   {
3207   /*
3208   * Set the 'follow' flag on the Viewport (and scroll to position if now true).
3209   */
3210   final boolean state = this.followHighlightMenuItem.getState();
3211   viewport.setFollowHighlight(state);
3212   if (state)
3213   {
3214   alignPanel.scrollToPosition(viewport.getSearchResults());
3215   }
3216   }
3217  
3218   /**
3219   * DOCUMENT ME!
3220   *
3221   * @param e
3222   * DOCUMENT ME!
3223   */
3224   @Override
3225   protected void colourTextMenuItem_actionPerformed(ActionEvent e)
3226   {
3227   viewport.setColourText(colourTextMenuItem.isSelected());
3228   alignPanel.paintAlignment(false, false);
3229   }
3230  
3231   /**
3232   * DOCUMENT ME!
3233   *
3234   * @param e
3235   * DOCUMENT ME!
3236   */
3237   @Override
3238   public void wrapMenuItem_actionPerformed(ActionEvent e)
3239   {
3240   setWrapFormat(wrapMenuItem.isSelected(), false);
3241   }
3242  
3243   public void setWrapFormat(boolean b, boolean setMenuItem)
3244   {
3245   scaleAbove.setVisible(b);
3246   scaleLeft.setVisible(b);
3247   scaleRight.setVisible(b);
3248   viewport.setWrapAlignment(b);
3249   alignPanel.updateLayout();
3250   if (setMenuItem)
3251   {
3252   wrapMenuItem.setSelected(b);
3253   }
3254   }
3255  
3256   @Override
3257   public void showAllSeqs_actionPerformed(ActionEvent e)
3258   {
3259   viewport.showAllHiddenSeqs();
3260   }
3261  
3262   @Override
3263   public void showAllColumns_actionPerformed(ActionEvent e)
3264   {
3265   viewport.showAllHiddenColumns();
3266   alignPanel.paintAlignment(true, true);
3267   viewport.sendSelection();
3268   }
3269  
3270   @Override
3271   public void hideSelSequences_actionPerformed(ActionEvent e)
3272   {
3273   viewport.hideAllSelectedSeqs();
3274   }
3275  
3276   /**
3277   * called by key handler and the hide all/show all menu items
3278   *
3279   * @param toggleSeqs
3280   * @param toggleCols
3281   */
3282   protected void toggleHiddenRegions(boolean toggleSeqs, boolean toggleCols)
3283   {
3284  
3285   boolean hide = false;
3286   SequenceGroup sg = viewport.getSelectionGroup();
3287   if (!toggleSeqs && !toggleCols)
3288   {
3289   // Hide everything by the current selection - this is a hack - we do the
3290   // invert and then hide
3291   // first check that there will be visible columns after the invert.
3292   if (viewport.hasSelectedColumns() || (sg != null && sg.getSize() > 0
3293   && sg.getStartRes() <= sg.getEndRes()))
3294   {
3295   // now invert the sequence set, if required - empty selection implies
3296   // that no hiding is required.
3297   if (sg != null)
3298   {
3299   invertSequenceMenuItem_actionPerformed(null);
3300   sg = viewport.getSelectionGroup();
3301   toggleSeqs = true;
3302  
3303   }
3304   viewport.expandColSelection(sg, true);
3305   // finally invert the column selection and get the new sequence
3306   // selection.
3307   invertColSel_actionPerformed(null);
3308   toggleCols = true;
3309   }
3310   }
3311  
3312   if (toggleSeqs)
3313   {
3314   if (sg != null && sg.getSize() != viewport.getAlignment().getHeight())
3315   {
3316   hideSelSequences_actionPerformed(null);
3317   hide = true;
3318   }
3319   else if (!(toggleCols && viewport.hasSelectedColumns()))
3320   {
3321   showAllSeqs_actionPerformed(null);
3322   }
3323   }
3324  
3325   if (toggleCols)
3326   {
3327   if (viewport.hasSelectedColumns())
3328   {
3329   hideSelColumns_actionPerformed(null);
3330   if (!toggleSeqs)
3331   {
3332   viewport.setSelectionGroup(sg);
3333   }
3334   }
3335   else if (!hide)
3336   {
3337   showAllColumns_actionPerformed(null);
3338   }
3339   }
3340   }
3341  
3342   /*
3343   * (non-Javadoc)
3344   *
3345   * @see jalview.jbgui.GAlignFrame#hideAllButSelection_actionPerformed(java.awt.
3346   * event.ActionEvent)
3347   */
3348   @Override
3349   public void hideAllButSelection_actionPerformed(ActionEvent e)
3350   {
3351   toggleHiddenRegions(false, false);
3352   viewport.sendSelection();
3353   }
3354  
3355   /*
3356   * (non-Javadoc)
3357   *
3358   * @see
3359   * jalview.jbgui.GAlignFrame#hideAllSelection_actionPerformed(java.awt.event
3360   * .ActionEvent)
3361   */
3362   @Override
3363   public void hideAllSelection_actionPerformed(ActionEvent e)
3364   {
3365   SequenceGroup sg = viewport.getSelectionGroup();
3366   viewport.expandColSelection(sg, false);
3367   viewport.hideAllSelectedSeqs();
3368   viewport.hideSelectedColumns();
3369   alignPanel.updateLayout();
3370   alignPanel.paintAlignment(true, true);
3371   viewport.sendSelection();
3372   }
3373  
3374   /*
3375   * (non-Javadoc)
3376   *
3377   * @see jalview.jbgui.GAlignFrame#showAllhidden_actionPerformed(java.awt.event.
3378   * ActionEvent)
3379   */
3380   @Override
3381   public void showAllhidden_actionPerformed(ActionEvent e)
3382   {
3383   viewport.showAllHiddenColumns();
3384   viewport.showAllHiddenSeqs();
3385   alignPanel.paintAlignment(true, true);
3386   viewport.sendSelection();
3387   }
3388  
3389   @Override
3390   public void hideSelColumns_actionPerformed(ActionEvent e)
3391   {
3392   viewport.hideSelectedColumns();
3393   alignPanel.updateLayout();
3394   alignPanel.paintAlignment(true, true);
3395   viewport.sendSelection();
3396   }
3397  
3398   @Override
3399   public void hiddenMarkers_actionPerformed(ActionEvent e)
3400   {
3401   viewport.setShowHiddenMarkers(hiddenMarkers.isSelected());
3402   repaint();
3403   }
3404  
3405   /**
3406   * DOCUMENT ME!
3407   *
3408   * @param e
3409   * DOCUMENT ME!
3410   */
3411   @Override
3412   protected void scaleAbove_actionPerformed(ActionEvent e)
3413   {
3414   viewport.setScaleAboveWrapped(scaleAbove.isSelected());
3415   alignPanel.updateLayout();
3416   alignPanel.paintAlignment(true, false);
3417   }
3418  
3419   /**
3420   * DOCUMENT ME!
3421   *
3422   * @param e
3423   * DOCUMENT ME!
3424   */
3425   @Override
3426   protected void scaleLeft_actionPerformed(ActionEvent e)
3427   {
3428   viewport.setScaleLeftWrapped(scaleLeft.isSelected());
3429   alignPanel.updateLayout();
3430   alignPanel.paintAlignment(true, false);
3431   }
3432  
3433   /**
3434   * DOCUMENT ME!
3435   *
3436   * @param e
3437   * DOCUMENT ME!
3438   */
3439   @Override
3440   protected void scaleRight_actionPerformed(ActionEvent e)
3441   {
3442   viewport.setScaleRightWrapped(scaleRight.isSelected());
3443   alignPanel.updateLayout();
3444   alignPanel.paintAlignment(true, false);
3445   }
3446  
3447   /**
3448   * DOCUMENT ME!
3449   *
3450   * @param e
3451   * DOCUMENT ME!
3452   */
3453   @Override
3454   public void viewBoxesMenuItem_actionPerformed(ActionEvent e)
3455   {
3456   viewport.setShowBoxes(viewBoxesMenuItem.isSelected());
3457   alignPanel.paintAlignment(false, false);
3458   }
3459  
3460   /**
3461   * DOCUMENT ME!
3462   *
3463   * @param e
3464   * DOCUMENT ME!
3465   */
3466   @Override
3467   public void viewTextMenuItem_actionPerformed(ActionEvent e)
3468   {
3469   viewport.setShowText(viewTextMenuItem.isSelected());
3470   alignPanel.paintAlignment(false, false);
3471   }
3472  
3473   /**
3474   * DOCUMENT ME!
3475   *
3476   * @param e
3477   * DOCUMENT ME!
3478   */
3479   @Override
3480   protected void renderGapsMenuItem_actionPerformed(ActionEvent e)
3481   {
3482   viewport.setRenderGaps(renderGapsMenuItem.isSelected());
3483   alignPanel.paintAlignment(false, false);
3484   }
3485  
3486   public FeatureSettings featureSettings;
3487  
3488   @Override
3489   public FeatureSettingsControllerI getFeatureSettingsUI()
3490   {
3491   return featureSettings;
3492   }
3493  
3494   @Override
3495   public void featureSettings_actionPerformed(ActionEvent e)
3496   {
3497   showFeatureSettingsUI();
3498   }
3499  
3500   @Override
3501   public FeatureSettingsControllerI showFeatureSettingsUI()
3502   {
3503   if (featureSettings != null)
3504   {
3505   featureSettings.closeOldSettings();
3506   featureSettings = null;
3507   }
3508   if (!showSeqFeatures.isSelected())
3509   {
3510   // make sure features are actually displayed
3511   showSeqFeatures.setSelected(true);
3512   showSeqFeatures_actionPerformed(null);
3513   }
3514   featureSettings = new FeatureSettings(this);
3515   return featureSettings;
3516   }
3517  
3518   /**
3519   * Set or clear 'Show Sequence Features'
3520   *
3521   * @param evt
3522   * DOCUMENT ME!
3523   */
3524   @Override
3525   public void showSeqFeatures_actionPerformed(ActionEvent evt)
3526   {
3527   viewport.setShowSequenceFeatures(showSeqFeatures.isSelected());
3528   alignPanel.paintAlignment(true, true);
3529   }
3530  
3531   /**
3532   * Action on toggle of the 'Show annotations' menu item. This shows or hides
3533   * the annotations panel as a whole.
3534   *
3535   * The options to show/hide all annotations should be enabled when the panel
3536   * is shown, and disabled when the panel is hidden.
3537   *
3538   * @param e
3539   */
3540   @Override
3541   public void annotationPanelMenuItem_actionPerformed(ActionEvent e)
3542   {
3543   final boolean setVisible = annotationPanelMenuItem.isSelected();
3544   viewport.setShowAnnotation(setVisible);
3545   this.showAllSeqAnnotations.setEnabled(setVisible);
3546   this.hideAllSeqAnnotations.setEnabled(setVisible);
3547   this.showAllAlAnnotations.setEnabled(setVisible);
3548   this.hideAllAlAnnotations.setEnabled(setVisible);
3549   alignPanel.updateLayout();
3550   }
3551  
3552   @Override
3553   public void alignmentProperties()
3554   {
3555   JComponent pane;
3556   StringBuffer contents = new AlignmentProperties(viewport.getAlignment())
3557  
3558   .formatAsHtml();
3559   String content = MessageManager.formatMessage("label.html_content",
3560   new Object[]
3561   { contents.toString() });
3562   contents = null;
3563  
3564   if (Platform.isJS())
3565   {
3566   JLabel textLabel = new JLabel();
3567   textLabel.setText(content);
3568   textLabel.setBackground(Color.WHITE);
3569  
3570   pane = new JPanel(new BorderLayout());
3571   ((JPanel) pane).setOpaque(true);
3572   pane.setBackground(Color.WHITE);
3573   ((JPanel) pane).add(textLabel, BorderLayout.NORTH);
3574   }
3575   else
3576   /**
3577   * Java only
3578   *
3579   * @j2sIgnore
3580   */
3581   {
3582   JEditorPane editPane = new JEditorPane("text/html", "");
3583   editPane.setEditable(false);
3584   editPane.setText(content);
3585   pane = editPane;
3586   }
3587  
3588   JInternalFrame frame = new JInternalFrame();
3589   frame.setFrameIcon(null);
3590   frame.getContentPane().add(new JScrollPane(pane));
3591  
3592   Desktop.addInternalFrame(frame, MessageManager
3593   .formatMessage("label.alignment_properties", new Object[]
3594   { getTitle() }), 500, 400);
3595   }
3596  
3597   /**
3598   * Opens an Overview panel for the alignment, unless one is open already
3599   *
3600   * @param e
3601   */
3602   @Override
3603   public void overviewMenuItem_actionPerformed(ActionEvent e)
3604   {
3605   boolean showHiddenRegions = Cache
3606   .getDefault(Preferences.SHOW_OV_HIDDEN_AT_START, false);
3607   openOverviewPanel(showHiddenRegions);
3608   }
3609  
3610   public OverviewPanel openOverviewPanel(boolean showHidden)
3611   {
3612   if (alignPanel.overviewPanel != null)
3613   {
3614   return alignPanel.overviewPanel;
3615   }
3616   JInternalFrame frame = new JInternalFrame();
3617   frame.setFrameIcon(null);
3618   final OverviewPanel overview = new OverviewPanel(alignPanel, frame,
3619   showHidden);
3620   frame.setContentPane(overview);
3621  
3622   alignPanel.setOverviewPanel(overview);
3623   alignPanel.setOverviewTitle(this);
3624  
3625   Desktop.addInternalFrame(frame, overview.getTitle(), true,
3626   frame.getWidth(), frame.getHeight(), true, true);
3627   frame.pack();
3628   frame.setLayer(JLayeredPane.PALETTE_LAYER);
3629   final AlignmentPanel thePanel = this.alignPanel;
3630   frame.addInternalFrameListener(
3631   new javax.swing.event.InternalFrameAdapter()
3632   {
3633   @Override
3634   public void internalFrameClosed(
3635   javax.swing.event.InternalFrameEvent evt)
3636   {
3637   overview.dispose();
3638   thePanel.setOverviewPanel(null);
3639   }
3640   });
3641   if (getKeyListeners().length > 0)
3642   {
3643   frame.addKeyListener(getKeyListeners()[0]);
3644   }
3645  
3646   return overview;
3647   }
3648  
3649   @Override
3650   public void textColour_actionPerformed()
3651   {
3652   new TextColourChooser().chooseColour(alignPanel, null);
3653   }
3654  
3655   /*
3656   * public void covariationColour_actionPerformed() { changeColour(new
3657   * CovariationColourScheme(viewport.getAlignment().getAlignmentAnnotation
3658   * ()[0])); }
3659   */
3660   @Override
3661   public void annotationColour_actionPerformed()
3662   {
3663   new AnnotationColourChooser(viewport, alignPanel);
3664   }
3665  
3666   @Override
3667   public void annotationColumn_actionPerformed(ActionEvent e)
3668   {
3669   new AnnotationColumnChooser(viewport, alignPanel);
3670   }
3671  
3672   /**
3673   * Action on the user checking or unchecking the option to apply the selected
3674   * colour scheme to all groups. If unchecked, groups may have their own
3675   * independent colour schemes.
3676   *
3677   * @param selected
3678   */
3679   @Override
3680   public void applyToAllGroups_actionPerformed(boolean selected)
3681   {
3682   viewport.setColourAppliesToAllGroups(selected);
3683   }
3684  
3685   /**
3686   * Action on user selecting a colour from the colour menu
3687   *
3688   * @param name
3689   * the name (not the menu item label!) of the colour scheme
3690   */
3691   @Override
3692   public void changeColour_actionPerformed(String name)
3693   {
3694   /*
3695   * 'User Defined' opens a panel to configure or load a user-defined colour
3696   * scheme
3697   */
3698   if (ResidueColourScheme.USER_DEFINED_MENU.equals(name))
3699   {
3700   new UserDefinedColours(alignPanel);
3701   return;
3702   }
3703  
3704   /*
3705   * otherwise set the chosen colour scheme (or null for 'None')
3706   */
3707   ColourSchemeI cs = ColourSchemes.getInstance().getColourScheme(name,
3708   viewport, viewport.getAlignment(),
3709   viewport.getHiddenRepSequences());
3710   changeColour(cs);
3711   }
3712  
3713   /**
3714   * Actions on setting or changing the alignment colour scheme
3715   *
3716   * @param cs
3717   */
3718   @Override
3719   public void changeColour(ColourSchemeI cs)
3720   {
3721   // TODO: pull up to controller method
3722   ColourMenuHelper.setColourSelected(colourMenu, cs);
3723  
3724   viewport.setGlobalColourScheme(cs);
3725  
3726   alignPanel.paintAlignment(true, true);
3727   }
3728  
3729   /**
3730   * Show the PID threshold slider panel
3731   */
3732   @Override
3733   protected void modifyPID_actionPerformed()
3734   {
3735   SliderPanel.setPIDSliderSource(alignPanel, viewport.getResidueShading(),
3736   alignPanel.getViewName());
3737   SliderPanel.showPIDSlider();
3738   }
3739  
3740   /**
3741   * Show the Conservation slider panel
3742   */
3743   @Override
3744   protected void modifyConservation_actionPerformed()
3745   {
3746   SliderPanel.setConservationSlider(alignPanel,
3747   viewport.getResidueShading(), alignPanel.getViewName());
3748   SliderPanel.showConservationSlider();
3749   }
3750  
3751   @Override
3752   protected void modifyConsensusSecondaryStructureThreshold_actionPerformed()
3753   {
3754   SliderPanel.setConsensusSecondaryStructureSlider(alignPanel,
3755   viewport.getResidueShading(), alignPanel.getViewName());
3756   SliderPanel.showConsensusSecondaryStructureSlider();
3757   }
3758  
3759   /**
3760   * Action on selecting or deselecting (Colour) By Conservation
3761   */
3762   @Override
3763   public void conservationMenuItem_actionPerformed(boolean selected)
3764   {
3765   modifyConservation.setEnabled(selected);
3766   viewport.setConservationSelected(selected);
3767   viewport.getResidueShading().setConservationApplied(selected);
3768  
3769   changeColour(viewport.getGlobalColourScheme());
3770   if (selected)
3771   {
3772   modifyConservation_actionPerformed();
3773   }
3774   else
3775   {
3776   SliderPanel.hideConservationSlider();
3777   }
3778   }
3779  
3780   @Override
3781   public void colourByConsensusSecondaryStructureMenuItem_actionPerformed(
3782   boolean selected)
3783   {
3784   modifyConsensusSecondaryStructureThreshold.setEnabled(selected);
3785   viewport.setByConsensusSecondaryStructureSelected(selected);
3786   viewport.getResidueShading()
3787   .setConsensusSecondaryStructureColouring(selected);
3788  
3789   changeColour(viewport.getGlobalColourScheme());
3790   if (selected)
3791   {
3792   modifyConsensusSecondaryStructureThreshold_actionPerformed();
3793   }
3794   else
3795   {
3796   SliderPanel.hideConsensusSecondaryStructureSlider();
3797   }
3798   }
3799  
3800   /**
3801   * Action on selecting or deselecting (Colour) Above PID Threshold
3802   */
3803   @Override
3804   public void abovePIDThreshold_actionPerformed(boolean selected)
3805   {
3806   modifyPID.setEnabled(selected);
3807   viewport.setAbovePIDThreshold(selected);
3808   if (!selected)
3809   {
3810   viewport.getResidueShading().setThreshold(0,
3811   viewport.isIgnoreGapsConsensus());
3812   }
3813  
3814   changeColour(viewport.getGlobalColourScheme());
3815   if (selected)
3816   {
3817   modifyPID_actionPerformed();
3818   }
3819   else
3820   {
3821   SliderPanel.hidePIDSlider();
3822   }
3823   }
3824  
3825   /**
3826   * DOCUMENT ME!
3827   *
3828   * @param e
3829   * DOCUMENT ME!
3830   */
3831   @Override
3832   public void sortPairwiseMenuItem_actionPerformed(ActionEvent e)
3833   {
3834   SequenceI[] oldOrder = viewport.getAlignment().getSequencesArray();
3835   AlignmentSorter.sortByPID(viewport.getAlignment(),
3836   viewport.getAlignment().getSequenceAt(0));
3837   addHistoryItem(new OrderCommand("Pairwise Sort", oldOrder,
3838   viewport.getAlignment()));
3839   alignPanel.paintAlignment(true, false);
3840   }
3841  
3842   /**
3843   * DOCUMENT ME!
3844   *
3845   * @param e
3846   * DOCUMENT ME!
3847   */
3848   @Override
3849   public void sortIDMenuItem_actionPerformed(ActionEvent e)
3850   {
3851   SequenceI[] oldOrder = viewport.getAlignment().getSequencesArray();
3852   AlignmentSorter.sortByID(viewport.getAlignment());
3853   addHistoryItem(
3854   new OrderCommand("ID Sort", oldOrder, viewport.getAlignment()));
3855   alignPanel.paintAlignment(true, false);
3856   }
3857  
3858   @Override
3859   protected void sortDescriptionMenuItem_actionPerformed(ActionEvent e)
3860   {
3861   SequenceI[] oldOrder = viewport.getAlignment().getSequencesArray();
3862   AlignmentSorter.sortByDescription(viewport.getAlignment());
3863   addHistoryItem(new OrderCommand("Description Sort", oldOrder,
3864   viewport.getAlignment()));
3865   alignPanel.paintAlignment(true, false);
3866   }
3867  
3868   /**
3869   * DOCUMENT ME!
3870   *
3871   * @param e
3872   * DOCUMENT ME!
3873   */
3874   @Override
3875   public void sortLengthMenuItem_actionPerformed(ActionEvent e)
3876   {
3877   SequenceI[] oldOrder = viewport.getAlignment().getSequencesArray();
3878   AlignmentSorter.sortByLength(viewport.getAlignment());
3879   addHistoryItem(new OrderCommand("Length Sort", oldOrder,
3880   viewport.getAlignment()));
3881   alignPanel.paintAlignment(true, false);
3882   }
3883  
3884   /**
3885   * DOCUMENT ME!
3886   *
3887   * @param e
3888   * DOCUMENT ME!
3889   */
3890   @Override
3891   public void sortGroupMenuItem_actionPerformed(ActionEvent e)
3892   {
3893   SequenceI[] oldOrder = viewport.getAlignment().getSequencesArray();
3894   AlignmentSorter.sortByGroup(viewport.getAlignment());
3895   addHistoryItem(new OrderCommand("Group Sort", oldOrder,
3896   viewport.getAlignment()));
3897  
3898   alignPanel.paintAlignment(true, false);
3899   }
3900  
3901   /**
3902   * DOCUMENT ME!
3903   *
3904   * @param e
3905   * DOCUMENT ME!
3906   */
3907   @Override
3908   public void removeRedundancyMenuItem_actionPerformed(ActionEvent e)
3909   {
3910   new RedundancyPanel(alignPanel, this);
3911   }
3912  
3913   /**
3914   * DOCUMENT ME!
3915   *
3916   * @param e
3917   * DOCUMENT ME!
3918   */
3919   @Override
3920   public void pairwiseAlignmentMenuItem_actionPerformed(ActionEvent e)
3921   {
3922   if ((viewport.getSelectionGroup() == null)
3923   || (viewport.getSelectionGroup().getSize() < 2))
3924   {
3925   JvOptionPane.showInternalMessageDialog(this,
3926   MessageManager.getString(
3927   "label.you_must_select_least_two_sequences"),
3928   MessageManager.getString("label.invalid_selection"),
3929   JvOptionPane.WARNING_MESSAGE);
3930   }
3931   else
3932   {
3933   new Thread(new Runnable()
3934   {
3935   @Override
3936   public void run()
3937   {
3938   JInternalFrame frame = new JInternalFrame();
3939   frame.setFrameIcon(null);
3940   frame.setContentPane(new PairwiseAlignPanel(viewport));
3941   Desktop.addInternalFrame(frame,
3942   MessageManager.getString("action.pairwise_alignment"),
3943   600, 500);
3944   }
3945   }).start();
3946  
3947   }
3948   }
3949  
3950   @Override
3951   public void autoCalculate_actionPerformed(ActionEvent e)
3952   {
3953   viewport.autoCalculateConsensus = autoCalculate.isSelected();
3954   if (viewport.autoCalculateConsensus)
3955   {
3956   viewport.firePropertyChange("alignment", null,
3957   viewport.getAlignment().getSequences());
3958   }
3959   }
3960  
3961   @Override
3962   public void sortByTreeOption_actionPerformed(ActionEvent e)
3963   {
3964   viewport.sortByTree = sortByTree.isSelected();
3965   }
3966  
3967   @Override
3968   protected void listenToViewSelections_actionPerformed(ActionEvent e)
3969   {
3970   viewport.followSelection = listenToViewSelections.isSelected();
3971   }
3972  
3973   /**
3974   * Constructs a tree panel and adds it to the desktop
3975   *
3976   * @param type
3977   * tree type (NJ or AV)
3978   * @param modelName
3979   * name of score model used to compute the tree
3980   * @param options
3981   * parameters for the distance or similarity calculation
3982   */
3983   void newTreePanel(String type, String modelName,
3984   SimilarityParamsI options, boolean isAnnotationBased)
3985   {
3986   String frameTitle = "";
3987   TreePanel tp;
3988  
3989   boolean onSelection = false;
3990   if (viewport.getSelectionGroup() != null
3991   && viewport.getSelectionGroup().getSize() > 0)
3992   {
3993   SequenceGroup sg = viewport.getSelectionGroup();
3994  
3995   /* Decide if the selection is a column region */
3996   for (SequenceI _s : sg.getSequences())
3997   {
3998   if (_s.getLength() < sg.getEndRes())
3999   {
4000   JvOptionPane.showMessageDialog(Desktop.desktop,
4001   MessageManager.getString(
4002   "label.selected_region_to_tree_may_only_contain_residues_or_gaps"),
4003   MessageManager.getString(
4004   "label.sequences_selection_not_aligned"),
4005   JvOptionPane.WARNING_MESSAGE);
4006  
4007   return;
4008   }
4009   }
4010   onSelection = true;
4011   }
4012   else
4013   {
4014   // Fix for JAL-4521. For annotation based tree, min number of sequence
4015   // required for tree calculation is 1 as multiple annotations can be
4016   // present for a single sequence
4017   if ((viewport.getAlignment().getHeight() < 2 && !isAnnotationBased)
4018   || (viewport.getAlignment().getHeight() < 1
4019   && isAnnotationBased))
4020   {
4021   return;
4022   }
4023   }
4024  
4025   tp = new TreePanel(alignPanel, type, modelName, options);
4026  
4027   frameTitle = formCalculationTitle(tp.getPanelTitle(), onSelection,
4028   this.title);
4029  
4030   Desktop.addInternalFrame(tp, frameTitle, 600, 500);
4031   }
4032  
4033   /**
4034   *
4035   * @param panelTitle
4036   * - calculation name
4037   * @param onSelection
4038   * - true if a selection was analysed
4039   * @param viewName
4040   * - null or current view name
4041   * @param title
4042   * - alignment frame title
4043   * @return (?on region) from (?viewName of) alignment name
4044   */
4045   public String formCalculationTitle(String panelTitle, boolean onSelection,
4046   String title)
4047   {
4048   String frameTitle = panelTitle;
4049  
4050   frameTitle += (onSelection ? " on region" : "");
4051  
4052   frameTitle += " from ";
4053  
4054   if (viewport.getViewName() != null)
4055   {
4056   frameTitle += viewport.getViewName() + " of ";
4057   }
4058  
4059   frameTitle += title;
4060   return frameTitle;
4061   }
4062  
4063   /**
4064   * DOCUMENT ME!
4065   *
4066   * @param title
4067   * DOCUMENT ME!
4068   * @param order
4069   * DOCUMENT ME!
4070   */
4071   public void addSortByOrderMenuItem(String title,
4072   final AlignmentOrder order)
4073   {
4074   final JMenuItem item = new JMenuItem(MessageManager
4075   .formatMessage("action.by_title_param", new Object[]
4076   { title }));
4077   sort.add(item);
4078   item.addActionListener(new java.awt.event.ActionListener()
4079   {
4080   @Override
4081   public void actionPerformed(ActionEvent e)
4082   {
4083   SequenceI[] oldOrder = viewport.getAlignment().getSequencesArray();
4084  
4085   // TODO: JBPNote - have to map order entries to curent SequenceI
4086   // pointers
4087   AlignmentSorter.sortBy(viewport.getAlignment(), order);
4088  
4089   addHistoryItem(new OrderCommand(order.getName(), oldOrder,
4090   viewport.getAlignment()));
4091  
4092   alignPanel.paintAlignment(true, false);
4093   }
4094   });
4095   }
4096  
4097   /**
4098   * Add a new sort by annotation score menu item
4099   *
4100   * @param sort
4101   * the menu to add the option to
4102   * @param scoreLabel
4103   * the label used to retrieve scores for each sequence on the
4104   * alignment
4105   */
4106   public void addSortByAnnotScoreMenuItem(JMenu sort,
4107   final String scoreLabel)
4108   {
4109   final JMenuItem item = new JMenuItem(scoreLabel);
4110   sort.add(item);
4111   item.addActionListener(new java.awt.event.ActionListener()
4112   {
4113   @Override
4114   public void actionPerformed(ActionEvent e)
4115   {
4116   SequenceI[] oldOrder = viewport.getAlignment().getSequencesArray();
4117   AlignmentSorter.sortByAnnotationScore(scoreLabel,
4118   viewport.getAlignment());// ,viewport.getSelectionGroup());
4119   addHistoryItem(new OrderCommand("Sort by " + scoreLabel, oldOrder,
4120   viewport.getAlignment()));
4121   alignPanel.paintAlignment(true, false);
4122   }
4123   });
4124   }
4125  
4126   /**
4127   * last hash for alignment's annotation array - used to minimise cost of
4128   * rebuild.
4129   */
4130   protected int _annotationScoreVectorHash;
4131  
4132   /**
4133   * search the alignment and rebuild the sort by annotation score submenu the
4134   * last alignment annotation vector hash is stored to minimize cost of
4135   * rebuilding in subsequence calls.
4136   *
4137   */
4138   @Override
4139   public void buildSortByAnnotationScoresMenu()
4140   {
4141   if (viewport.getAlignment().getAlignmentAnnotation() == null)
4142   {
4143   return;
4144   }
4145  
4146   if (viewport.getAlignment().getAlignmentAnnotation()
4147   .hashCode() != _annotationScoreVectorHash)
4148   {
4149   sortByAnnotScore.removeAll();
4150   // almost certainly a quicker way to do this - but we keep it simple
4151   Hashtable scoreSorts = new Hashtable<>();
4152   AlignmentAnnotation aann[];
4153   for (SequenceI sqa : viewport.getAlignment().getSequences())
4154   {
4155   aann = sqa.getAnnotation();
4156   for (int i = 0; aann != null && i < aann.length; i++)
4157   {
4158   if (aann[i].hasScore() && aann[i].sequenceRef != null)
4159   {
4160   scoreSorts.put(aann[i].label, aann[i].label);
4161   }
4162   }
4163   }
4164   Enumeration labels = scoreSorts.keys();
4165   while (labels.hasMoreElements())
4166   {
4167   addSortByAnnotScoreMenuItem(sortByAnnotScore, labels.nextElement());
4168   }
4169   sortByAnnotScore.setVisible(scoreSorts.size() > 0);
4170   scoreSorts.clear();
4171  
4172   _annotationScoreVectorHash = viewport.getAlignment()
4173   .getAlignmentAnnotation().hashCode();
4174   }
4175   }
4176  
4177   /**
4178   * Maintain the Order by->Displayed Tree menu. Creates a new menu item for a
4179   * TreePanel with an appropriate jalview.analysis.AlignmentSorter
4180   * call. Listeners are added to remove the menu item when the treePanel is
4181   * closed, and adjust the tree leaf to sequence mapping when the alignment is
4182   * modified.
4183   */
4184   @Override
4185   public void buildTreeSortMenu()
4186   {
4187   sortByTreeMenu.removeAll();
4188  
4189   List comps = PaintRefresher.components
4190   .get(viewport.getSequenceSetId());
4191   List treePanels = new ArrayList<>();
4192  
4193   List annotTreePanels = new ArrayList<>();
4194   for (Component comp : comps)
4195   {
4196   if (comp instanceof TreePanel)
4197   {
4198   treePanels.add((TreePanel) comp);
4199  
4200   if (((TreePanel) comp).isAnnotationBased())
4201   {
4202   annotTreePanels.add((TreePanel) comp);
4203   }
4204   }
4205   }
4206  
4207   if (treePanels.size() < 1)
4208   {
4209   sortByTreeMenu.setVisible(false);
4210   return;
4211   }
4212  
4213   sortByTreeMenu.setVisible(true);
4214  
4215   for (final TreePanel tp : treePanels)
4216   {
4217   final JMenuItem item = new JMenuItem(tp.getTitle());
4218   item.addActionListener(new java.awt.event.ActionListener()
4219   {
4220   @Override
4221   public void actionPerformed(ActionEvent e)
4222   {
4223   tp.sortByTree_actionPerformed();
4224   addHistoryItem(tp.sortAlignmentIn(alignPanel));
4225  
4226   }
4227   });
4228  
4229   sortByTreeMenu.add(item);
4230   }
4231   }
4232  
4233   @Override
4234   public void buildSortAnnotationByTreeMenu()
4235   {
4236   sortAnnotationByTreeMenu.removeAll();
4237  
4238   List comps = PaintRefresher.components
4239   .get(viewport.getSequenceSetId());
4240  
4241   List annotTreePanels = new ArrayList<>();
4242   for (Component comp : comps)
4243   {
4244   if (comp instanceof TreePanel)
4245   {
4246   if (((TreePanel) comp).isAnnotationBased())
4247   {
4248   annotTreePanels.add((TreePanel) comp);
4249   }
4250   }
4251   }
4252  
4253   if (annotTreePanels.size() < 1)
4254   {
4255   sortAnnotationByTreeMenu.setVisible(false);
4256   return;
4257   }
4258  
4259   sortAnnotationByTreeMenu.setVisible(true);
4260  
4261   for (final TreePanel tp : annotTreePanels)
4262   {
4263   final JMenuItem item = new JMenuItem(tp.getTitle());
4264   item.addActionListener(new java.awt.event.ActionListener()
4265   {
4266   @Override
4267   public void actionPerformed(ActionEvent e)
4268   {
4269   sortAnnByLabel.setSelected(false);
4270   sortAnnBySequence.setSelected(false);
4271   setAnnotationSortOrder(SequenceAnnotationOrder.NONE);
4272   tp.sortAnnotationByTree_actionPerformed();
4273   }
4274   });
4275  
4276   sortAnnotationByTreeMenu.add(item);
4277   }
4278   }
4279  
4280   public boolean sortBy(AlignmentOrder alorder, String undoname)
4281   {
4282   SequenceI[] oldOrder = viewport.getAlignment().getSequencesArray();
4283   AlignmentSorter.sortBy(viewport.getAlignment(), alorder);
4284   if (undoname != null)
4285   {
4286   addHistoryItem(new OrderCommand(undoname, oldOrder,
4287   viewport.getAlignment()));
4288   }
4289   alignPanel.paintAlignment(true, false);
4290   return true;
4291   }
4292  
4293   /**
4294   * Work out whether the whole set of sequences or just the selected set will
4295   * be submitted for multiple alignment.
4296   *
4297   */
4298   public jalview.datamodel.AlignmentView gatherSequencesForAlignment()
4299   {
4300   // Now, check we have enough sequences
4301   AlignmentView msa = null;
4302  
4303   if ((viewport.getSelectionGroup() != null)
4304   && (viewport.getSelectionGroup().getSize() > 1))
4305   {
4306   // JBPNote UGLY! To prettify, make SequenceGroup and Alignment conform to
4307   // some common interface!
4308   /*
4309   * SequenceGroup seqs = viewport.getSelectionGroup(); int sz; msa = new
4310   * SequenceI[sz = seqs.getSize(false)];
4311   *
4312   * for (int i = 0; i < sz; i++) { msa[i] = (SequenceI) seqs.getSequenceAt(i); }
4313   */
4314   msa = viewport.getAlignmentView(true);
4315   }
4316   else if (viewport.getSelectionGroup() != null
4317   && viewport.getSelectionGroup().getSize() == 1)
4318   {
4319   int option = JvOptionPane.showConfirmDialog(this,
4320   MessageManager.getString("warn.oneseq_msainput_selection"),
4321   MessageManager.getString("label.invalid_selection"),
4322   JvOptionPane.OK_CANCEL_OPTION);
4323   if (option == JvOptionPane.OK_OPTION)
4324   {
4325   msa = viewport.getAlignmentView(false);
4326   }
4327   }
4328   else
4329   {
4330   msa = viewport.getAlignmentView(false);
4331   }
4332   return msa;
4333   }
4334  
4335   /**
4336   * Decides what is submitted to a secondary structure prediction service: the
4337   * first sequence in the alignment, or in the current selection, or, if the
4338   * alignment is 'aligned' (ie padded with gaps), then the currently selected
4339   * region or the whole alignment. (where the first sequence in the set is the
4340   * one that the prediction will be for).
4341   */
4342   public AlignmentView gatherSeqOrMsaForSecStrPrediction()
4343   {
4344   AlignmentView seqs = null;
4345  
4346   if ((viewport.getSelectionGroup() != null)
4347   && (viewport.getSelectionGroup().getSize() > 0))
4348   {
4349   seqs = viewport.getAlignmentView(true);
4350   }
4351   else
4352   {
4353   seqs = viewport.getAlignmentView(false);
4354   }
4355   // limit sequences - JBPNote in future - could spawn multiple prediction
4356   // jobs
4357   // TODO: viewport.getAlignment().isAligned is a global state - the local
4358   // selection may well be aligned - we preserve 2.0.8 behaviour for moment.
4359   if (!viewport.getAlignment().isAligned(false))
4360   {
4361   seqs.setSequences(new SeqCigar[] { seqs.getSequences()[0] });
4362   // TODO: if seqs.getSequences().length>1 then should really have warned
4363   // user!
4364  
4365   }
4366   return seqs;
4367   }
4368  
4369   //// ***************************************************
4370   //// TODO REFACTOR TREE I/O TO CONTROLLER/SEPARATE CLASS
4371   //// ***************************************************
4372  
4373   /**
4374   * DOCUMENT ME!
4375   *
4376   * @param e
4377   * DOCUMENT ME!
4378   */
4379   @Override
4380   protected void loadTreeMenuItem_actionPerformed(ActionEvent e)
4381   {
4382   // Pick the tree file
4383   JalviewFileChooser chooser = new JalviewFileChooser(
4384   Cache.getProperty("LAST_DIRECTORY"));
4385   chooser.setFileView(new JalviewFileView());
4386   chooser.setDialogTitle(
4387   MessageManager.getString("label.select_newick_like_tree_file"));
4388   chooser.setToolTipText(
4389   MessageManager.getString("label.load_tree_file"));
4390  
4391   chooser.setResponseHandler(0, () -> {
4392   String filePath = chooser.getSelectedFile().getPath();
4393   Cache.setProperty("LAST_DIRECTORY", filePath);
4394   NewickFile fin = null;
4395   try
4396   {
4397   fin = new NewickFile(new FileParse(chooser.getSelectedFile(),
4398   DataSourceType.FILE));
4399   viewport.setCurrentTree(showNewickTree(fin, filePath).getTree());
4400   } catch (Exception ex)
4401   {
4402   JvOptionPane.showMessageDialog(Desktop.desktop, ex.getMessage(),
4403   MessageManager.getString("label.problem_reading_tree_file"),
4404   JvOptionPane.WARNING_MESSAGE);
4405   ex.printStackTrace();
4406   }
4407   if (fin != null && fin.hasWarningMessage())
4408   {
4409   JvOptionPane.showMessageDialog(Desktop.desktop,
4410   fin.getWarningMessage(),
4411   MessageManager
4412   .getString("label.possible_problem_with_tree_file"),
4413   JvOptionPane.WARNING_MESSAGE);
4414   }
4415   });
4416   chooser.showOpenDialog(this);
4417   }
4418  
4419   public TreePanel showNewickTree(NewickFile nf, String treeTitle)
4420   {
4421   return showNewickTree(nf, treeTitle, 600, 500, 4, 5);
4422   }
4423  
4424   public TreePanel showNewickTree(NewickFile nf, String treeTitle, int w,
4425   int h, int x, int y)
4426   {
4427   return showNewickTree(nf, treeTitle, null, w, h, x, y);
4428   }
4429  
4430   /**
4431   * Add a treeviewer for the tree extracted from a Newick file object to the
4432   * current alignment view
4433   *
4434   * @param nf
4435   * the tree
4436   * @param title
4437   * tree viewer title
4438   * @param input
4439   * Associated alignment input data (or null)
4440   * @param w
4441   * width
4442   * @param h
4443   * height
4444   * @param x
4445   * position
4446   * @param y
4447   * position
4448   * @return TreePanel handle
4449   */
4450   public TreePanel showNewickTree(NewickFile nf, String treeTitle,
4451   AlignmentView input, int w, int h, int x, int y)
4452   {
4453   TreePanel tp = null;
4454  
4455   try
4456   {
4457   nf.parse();
4458  
4459   if (nf.getTree() != null)
4460   {
4461   tp = new TreePanel(alignPanel, nf, treeTitle, input);
4462  
4463   layoutTreePanel(tp, treeTitle, w, h, x, y);
4464   }
4465   } catch (Exception ex)
4466   {
4467   ex.printStackTrace();
4468   }
4469  
4470   return tp;
4471   }
4472  
4473   /**
4474   * called by Jalview2XML only just now - import a tree that should be resolved
4475   * against annotations.
4476   *
4477   * @param nf
4478   * @param annotationIds
4479   * @param treeTitle
4480   * @param input
4481   * @param w
4482   * @param h
4483   * @param x
4484   * @param y
4485   * @return
4486   */
4487   public TreePanel showNewickTreeForAnnotation(NewickFile nf,
4488   Map annotationIds, String treeTitle,
4489   AlignmentView input, int w, int h, int x, int y)
4490   {
4491   TreePanel tp = null;
4492  
4493   try
4494   {
4495   nf.parse();
4496  
4497   if (nf.getTree() != null)
4498   {
4499   tp = TreePanel.newTreeForAnnotations(alignPanel, nf, annotationIds,
4500   treeTitle, input);
4501  
4502   layoutTreePanel(tp, treeTitle, w, h, x, y);
4503   }
4504   } catch (Exception ex)
4505   {
4506   ex.printStackTrace();
4507   }
4508  
4509   return tp;
4510   }
4511  
4512   /**
4513   * Applies layout configuration to a TreePanel and add it to the desktop.
4514   *
4515   * @param treePanel
4516   * the TreePanel to configure and display
4517   * @param title
4518   * the title of the tree panel
4519   * @param width
4520   * the width of the tree panel
4521   * @param height
4522   * the height of the tree panel
4523   * @param x
4524   * the x-coordinate of the panel
4525   * @param y
4526   * the y-coordinate of the panel
4527   */
4528   public static void layoutTreePanel(TreePanel tp, String title, int w,
4529   int h, int x, int y)
4530   {
4531   tp.setSize(w, h);
4532   if (x > 0 && y > 0)
4533   {
4534   tp.setLocation(x, y);
4535   }
4536   Desktop.addInternalFrame(tp, title, w, h);
4537   }
4538  
4539   public void showContactMapTree(AlignmentAnnotation aa, ContactMatrixI cm)
4540   {
4541   int x = 4, y = 5;
4542   int w = 400, h = 500;
4543  
4544   try
4545   {
4546   NewickFile fin = new NewickFile(
4547   new FileParse(cm.getNewick(), DataSourceType.PASTE));
4548   String title = aa.label + " " + cm.getTreeMethod() + " tree"
4549   + (aa.sequenceRef != null
4550   ? (" for " + aa.sequenceRef.getDisplayId(false))
4551   : "");
4552  
4553   showColumnWiseTree(fin, aa, title, w, h, x, y);
4554   } catch (Throwable xx)
4555   {
4556   Console.error("Unexpected exception showing tree for contact matrix",
4557   xx);
4558   }
4559   }
4560  
4561   public TreePanel showColumnWiseTree(NewickFile nf, AlignmentAnnotation aa,
4562   String treeTitle, int w, int h, int x, int y)
4563   {
4564   try
4565   {
4566   nf.parse();
4567   if (nf.getTree() == null)
4568   {
4569   return null;
4570   }
4571   TreePanel tp = new TreePanel(alignPanel, nf, aa, treeTitle);
4572  
4573   tp.setSize(w, h);
4574  
4575   if (x > 0 && y > 0)
4576   {
4577   tp.setLocation(x, y);
4578   }
4579  
4580   tp.setLayer(JLayeredPane.PALETTE_LAYER);
4581  
4582   Desktop.addInternalFrame(tp, treeTitle, w, h);
4583   return tp;
4584   } catch (Throwable xx)
4585   {
4586   Console.error("Unexpected exception showing tree for contact matrix",
4587   xx);
4588   }
4589   return null;
4590   }
4591   //// ***************************************************
4592   //// TODO REFACTOR TREE I/O TO CONTROLLER/SEPARATE CLASS - END OF METHODS
4593   //// ***************************************************
4594  
4595   private boolean buildingMenu = false;
4596  
4597   /**
4598   * Generates menu items and listener event actions for web service clients
4599   *
4600   */
4601   public void BuildWebServiceMenu()
4602   {
4603   while (buildingMenu)
4604   {
4605   try
4606   {
4607   jalview.bin.Console
4608   .errPrintln("Waiting for building menu to finish.");
4609   Thread.sleep(10);
4610   } catch (Exception e)
4611   {
4612   }
4613   }
4614   final AlignFrame me = this;
4615   buildingMenu = true;
4616   new Thread(new Runnable()
4617   {
4618   @Override
4619   public void run()
4620   {
4621   final List legacyItems = new ArrayList<>();
4622   try
4623   {
4624   // jalview.bin.Console.errPrintln("Building ws menu again "
4625   // + Thread.currentThread());
4626   // TODO: add support for context dependent disabling of services based
4627   // on
4628   // alignment and current selection
4629   // TODO: add additional serviceHandle parameter to specify abstract
4630   // handler
4631   // class independently of AbstractName
4632   // TODO: add in rediscovery GUI function to restart discoverer
4633   // TODO: group services by location as well as function and/or
4634   // introduce
4635   // object broker mechanism.
4636   final Vector wsmenu = new Vector<>();
4637   final IProgressIndicator af = me;
4638  
4639   /*
4640   * do not i18n these strings - they are hard-coded in class
4641   * compbio.data.msa.Category, Jws2Discoverer.isRecalculable() and
4642   * SequenceAnnotationWSClient.initSequenceAnnotationWSClient()
4643   */
4644   final JMenu msawsmenu = new JMenu("Alignment");
4645   final JMenu secstrmenu = new JMenu(
4646   "Secondary Structure Prediction");
4647   final JMenu seqsrchmenu = new JMenu("Sequence Database Search");
4648   final JMenu analymenu = new JMenu("Analysis");
4649   final JMenu dismenu = new JMenu("Protein Disorder");
4650   // JAL-940 - only show secondary structure prediction services from
4651   // the legacy server
4652   if (// Cache.getDefault("SHOW_JWS1_SERVICES", true)
4653   // &&
4654   Discoverer.services != null && (Discoverer.services.size() > 0))
4655   {
4656   // TODO: refactor to allow list of AbstractName/Handler bindings to
4657   // be
4658   // stored or retrieved from elsewhere
4659   // No MSAWS used any more:
4660   // Vector msaws = null; // (Vector)
4661   // Discoverer.services.get("MsaWS");
4662   Vector secstrpr = Discoverer.services
4663   .get("SecStrPred");
4664   if (secstrpr != null)
4665   {
4666   // Add any secondary structure prediction services
4667   for (int i = 0, j = secstrpr.size(); i < j; i++)
4668   {
4669   final ext.vamsas.ServiceHandle sh = secstrpr.get(i);
4670   jalview.ws.WSMenuEntryProviderI impl = jalview.ws.jws1.Discoverer
4671   .getServiceClient(sh);
4672   int p = secstrmenu.getItemCount();
4673   impl.attachWSMenuEntry(secstrmenu, me);
4674   int q = secstrmenu.getItemCount();
4675   for (int litm = p; litm < q; litm++)
4676   {
4677   legacyItems.add(secstrmenu.getItem(litm));
4678   }
4679   }
4680   }
4681   }
4682  
4683   // Add all submenus in the order they should appear on the web
4684   // services menu
4685   wsmenu.add(msawsmenu);
4686   wsmenu.add(secstrmenu);
4687   wsmenu.add(dismenu);
4688   wsmenu.add(analymenu);
4689   // No search services yet
4690   // wsmenu.add(seqsrchmenu);
4691  
4692   javax.swing.SwingUtilities.invokeLater(new Runnable()
4693   {
4694   @Override
4695   public void run()
4696   {
4697   try
4698   {
4699   webService.removeAll();
4700   // first, add discovered services onto the webservices menu
4701   if (wsmenu.size() > 0)
4702   {
4703   for (int i = 0, j = wsmenu.size(); i < j; i++)
4704   {
4705   webService.add(wsmenu.get(i));
4706   }
4707   }
4708   else
4709   {
4710   webService.add(me.webServiceNoServices);
4711   }
4712   // TODO: move into separate menu builder class.
4713   {
4714   // logic for 2.11.1.4 is
4715   // always look to see if there is a discover. if there isn't
4716   // we can't show any Jws2 services
4717   // if there are services available, show them - regardless of
4718   // the 'show JWS2 preference'
4719   // if the discoverer is running then say so
4720   // otherwise offer to trigger discovery if 'show JWS2' is not
4721   // enabled
4722   Jws2Discoverer jws2servs = Jws2Discoverer.getDiscoverer();
4723   if (jws2servs != null)
4724   {
4725   if (jws2servs.hasServices())
4726   {
4727   jws2servs.attachWSMenuEntry(webService, me);
4728   for (Jws2Instance sv : jws2servs.getServices())
4729   {
4730   if (sv.description.toLowerCase(Locale.ROOT)
4731   .contains("jpred"))
4732   {
4733   for (JMenuItem jmi : legacyItems)
4734   {
4735   jmi.setVisible(false);
4736   }
4737   }
4738   }
4739   }
4740  
4741   if (jws2servs.isRunning())
4742   {
4743   JMenuItem tm = new JMenuItem(
4744   "Still discovering JABA Services");
4745   tm.setEnabled(false);
4746   webService.add(tm);
4747   }
4748   else if (!Cache.getDefault("SHOW_JWS2_SERVICES", true))
4749   {
4750   JMenuItem enableJws2 = new JMenuItem(
4751   "Discover Web Services");
4752   enableJws2.setToolTipText(
4753   "Select to start JABA Web Service discovery (or enable option in Web Service preferences)");
4754   enableJws2.setEnabled(true);
4755   enableJws2.addActionListener(new ActionListener()
4756   {
4757  
4758   @Override
4759   public void actionPerformed(ActionEvent e)
4760   {
4761   // start service discoverer, but ignore preference
4762   Desktop.instance.startServiceDiscovery(false,
4763   true);
4764   }
4765   });
4766   webService.add(enableJws2);
4767   }
4768   }
4769   }
4770   build_urlServiceMenu(me.webService);
4771   build_fetchdbmenu(webService);
4772   for (JMenu item : wsmenu)
4773   {
4774   if (item.getItemCount() == 0)
4775   {
4776   item.setEnabled(false);
4777   }
4778   else
4779   {
4780   item.setEnabled(true);
4781   }
4782   }
4783   } catch (Exception e)
4784   {
4785   Console.debug(
4786   "Exception during web service menu building process.",
4787   e);
4788   }
4789   }
4790   });
4791   } catch (Exception e)
4792   {
4793   }
4794   buildingMenu = false;
4795   }
4796   }).start();
4797  
4798   }
4799  
4800   /**
4801   * construct any groupURL type service menu entries.
4802   *
4803   * @param webService
4804   */
4805   protected void build_urlServiceMenu(JMenu webService)
4806   {
4807   // TODO: remove this code when 2.7 is released
4808   // DEBUG - alignmentView
4809   /*
4810   * JMenuItem testAlView = new JMenuItem("Test AlignmentView"); final AlignFrame
4811   * af = this; testAlView.addActionListener(new ActionListener() {
4812   *
4813   * @Override public void actionPerformed(ActionEvent e) {
4814   * jalview.datamodel.AlignmentView
4815   * .testSelectionViews(af.viewport.getAlignment(),
4816   * af.viewport.getColumnSelection(), af.viewport.selectionGroup); }
4817   *
4818   * }); webService.add(testAlView);
4819   */
4820   // TODO: refactor to RestClient discoverer and merge menu entries for
4821   // rest-style services with other types of analysis/calculation service
4822   // SHmmr test client - still being implemented.
4823   // DEBUG - alignmentView
4824  
4825   for (jalview.ws.rest.RestClient client : jalview.ws.rest.RestClient
4826   .getRestClients())
4827   {
4828   client.attachWSMenuEntry(
4829   JvSwingUtils.findOrCreateMenu(webService, client.getAction()),
4830   this);
4831   }
4832   }
4833  
4834   /**
4835   * Searches the alignment sequences for xRefs and builds the Show
4836   * Cross-References menu (formerly called Show Products), with database
4837   * sources for which cross-references are found (protein sources for a
4838   * nucleotide alignment and vice versa)
4839   *
4840   * @return true if Show Cross-references menu should be enabled
4841   */
4842   public boolean canShowProducts()
4843   {
4844   SequenceI[] seqs = viewport.getAlignment().getSequencesArray();
4845   AlignmentI dataset = viewport.getAlignment().getDataset();
4846  
4847   showProducts.removeAll();
4848   final boolean dna = viewport.getAlignment().isNucleotide();
4849  
4850   if (seqs == null || seqs.length == 0)
4851   {
4852   // nothing to see here.
4853   return false;
4854   }
4855  
4856   boolean showp = false;
4857   try
4858   {
4859   List ptypes = new CrossRef(seqs, dataset)
4860   .findXrefSourcesForSequences(dna);
4861  
4862   for (final String source : ptypes)
4863   {
4864   showp = true;
4865   final AlignFrame af = this;
4866   JMenuItem xtype = new JMenuItem(source);
4867   xtype.addActionListener(new ActionListener()
4868   {
4869   @Override
4870   public void actionPerformed(ActionEvent e)
4871   {
4872   showProductsFor(af.viewport.getSequenceSelection(), dna,
4873   source);
4874   }
4875   });
4876   showProducts.add(xtype);
4877   }
4878   showProducts.setVisible(showp);
4879   showProducts.setEnabled(showp);
4880   } catch (Exception e)
4881   {
4882   Console.warn(
4883   "canShowProducts threw an exception - please report to help@jalview.org",
4884   e);
4885   return false;
4886   }
4887   return showp;
4888   }
4889  
4890   /**
4891   * Finds and displays cross-references for the selected sequences (protein
4892   * products for nucleotide sequences, dna coding sequences for peptides).
4893   *
4894   * @param sel
4895   * the sequences to show cross-references for
4896   * @param dna
4897   * true if from a nucleotide alignment (so showing proteins)
4898   * @param source
4899   * the database to show cross-references for
4900   */
4901   protected void showProductsFor(final SequenceI[] sel, final boolean _odna,
4902   final String source)
4903   {
4904   new Thread(CrossRefAction.getHandlerFor(sel, _odna, source, this))
4905   .start();
4906   }
4907  
4908   /**
4909   * Construct and display a new frame containing the translation of this
4910   * frame's DNA sequences to their aligned protein (amino acid) equivalents.
4911   */
4912   @Override
4913   public void showTranslation_actionPerformed(GeneticCodeI codeTable)
4914   {
4915   AlignmentI al = null;
4916   try
4917   {
4918   Dna dna = new Dna(viewport, viewport.getViewAsVisibleContigs(true));
4919  
4920   al = dna.translateCdna(codeTable);
4921   } catch (Exception ex)
4922   {
4923   Console.error("Exception during translation. Please report this !",
4924   ex);
4925   final String msg = MessageManager.getString(
4926   "label.error_when_translating_sequences_submit_bug_report");
4927   final String errorTitle = MessageManager
4928   .getString("label.implementation_error")
4929   + MessageManager.getString("label.translation_failed");
4930   JvOptionPane.showMessageDialog(Desktop.desktop, msg, errorTitle,
4931   JvOptionPane.ERROR_MESSAGE);
4932   return;
4933   }
4934   if (al == null || al.getHeight() == 0)
4935   {
4936   final String msg = MessageManager.getString(
4937   "label.select_at_least_three_bases_in_at_least_one_sequence_to_cDNA_translation");
4938   final String errorTitle = MessageManager
4939   .getString("label.translation_failed");
4940   JvOptionPane.showMessageDialog(Desktop.desktop, msg, errorTitle,
4941   JvOptionPane.WARNING_MESSAGE);
4942   }
4943   else
4944   {
4945   AlignFrame af = new AlignFrame(al, DEFAULT_WIDTH, DEFAULT_HEIGHT);
4946   af.setFileFormat(this.currentFileFormat);
4947   final String newTitle = MessageManager
4948   .formatMessage("label.translation_of_params", new Object[]
4949   { this.getTitle(), codeTable.getId() });
4950   af.setTitle(newTitle);
4951   if (Cache.getDefault(Preferences.ENABLE_SPLIT_FRAME, true))
4952   {
4953   final SequenceI[] seqs = viewport.getSelectionAsNewSequence();
4954   viewport.openSplitFrame(af, new Alignment(seqs));
4955   }
4956   else
4957   {
4958   Desktop.addInternalFrame(af, newTitle, DEFAULT_WIDTH,
4959   DEFAULT_HEIGHT);
4960   }
4961   }
4962   }
4963  
4964   /**
4965   * Set the file format
4966   *
4967   * @param format
4968   */
4969   public void setFileFormat(FileFormatI format)
4970   {
4971   this.currentFileFormat = format;
4972   }
4973  
4974   /**
4975   * Try to load a features file onto the alignment.
4976   *
4977   * @param file
4978   * contents or path to retrieve file or a File object
4979   * @param sourceType
4980   * access mode of file (see jalview.io.AlignFile)
4981   * @return true if features file was parsed correctly.
4982   */
4983   public boolean parseFeaturesFile(Object file, DataSourceType sourceType)
4984   {
4985   // BH 2018
4986   return avc.parseFeaturesFile(file, sourceType,
4987   Cache.getDefault("RELAXEDSEQIDMATCHING", false));
4988  
4989   }
4990  
4991   @Override
4992   public void refreshFeatureUI(boolean enableIfNecessary)
4993   {
4994   // note - currently this is only still here rather than in the controller
4995   // because of the featureSettings hard reference that is yet to be
4996   // abstracted
4997   if (enableIfNecessary)
4998   {
4999   viewport.setShowSequenceFeatures(true);
5000   showSeqFeatures.setSelected(true);
5001   }
5002  
5003   }
5004  
5005   @Override
5006   public void dragEnter(DropTargetDragEvent evt)
5007   {
5008   }
5009  
5010   @Override
5011   public void dragExit(DropTargetEvent evt)
5012   {
5013   }
5014  
5015   @Override
5016   public void dragOver(DropTargetDragEvent evt)
5017   {
5018   }
5019  
5020   @Override
5021   public void dropActionChanged(DropTargetDragEvent evt)
5022   {
5023   }
5024  
5025   @Override
5026   public void drop(DropTargetDropEvent evt)
5027   {
5028   // JAL-1552 - acceptDrop required before getTransferable call for
5029   // Java's Transferable for native dnd
5030   evt.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);
5031   Transferable t = evt.getTransferable();
5032  
5033   final AlignFrame thisaf = this;
5034   final List files = new ArrayList<>();
5035   List protocols = new ArrayList<>();
5036  
5037   try
5038   {
5039   Desktop.transferFromDropTarget(files, protocols, evt, t);
5040   } catch (Exception e)
5041   {
5042   e.printStackTrace();
5043   }
5044   if (files != null)
5045   {
5046   new Thread(new Runnable()
5047   {
5048   @Override
5049   public void run()
5050   {
5051   try
5052   {
5053   // check to see if any of these files have names matching sequences
5054   // in
5055   // the alignment
5056   SequenceIdMatcher idm = new SequenceIdMatcher(
5057   viewport.getAlignment().getSequencesArray());
5058   /**
5059   * Object[] { String,SequenceI}
5060   */
5061   ArrayList filesmatched = new ArrayList<>();
5062   ArrayList filesnotmatched = new ArrayList<>();
5063   for (int i = 0; i < files.size(); i++)
5064   {
5065   // BH 2018
5066   Object file = files.get(i);
5067   String fileName = file.toString();
5068   String pdbfn = "";
5069   DataSourceType protocol = (file instanceof File
5070   ? DataSourceType.FILE
5071   : FormatAdapter.checkProtocol(fileName));
5072   if (protocol == DataSourceType.FILE)
5073   {
5074   File fl;
5075   if (file instanceof File)
5076   {
5077   fl = (File) file;
5078   Platform.cacheFileData(fl);
5079   }
5080   else
5081   {
5082   fl = new File(fileName);
5083   }
5084   pdbfn = fl.getName();
5085   }
5086   else if (protocol == DataSourceType.URL)
5087   {
5088   URL url = new URL(fileName);
5089   pdbfn = url.getFile();
5090   }
5091   if (pdbfn.length() > 0)
5092   {
5093   // attempt to find a match in the alignment
5094   SequenceI[] mtch = idm.findAllIdMatches(pdbfn);
5095   int l = 0, c = pdbfn.indexOf(".");
5096   while (mtch == null && c != -1)
5097   {
5098   do
5099   {
5100   l = c;
5101   } while ((c = pdbfn.indexOf(".", l)) > l);
5102   if (l > -1)
5103   {
5104   pdbfn = pdbfn.substring(0, l);
5105   }
5106   mtch = idm.findAllIdMatches(pdbfn);
5107   }
5108   FileFormatI type = null;
5109   if (mtch != null)
5110   {
5111   try
5112   {
5113   type = new IdentifyFile().identify(file, protocol);
5114   } catch (Exception ex)
5115   {
5116   type = null;
5117   }
5118   if (type != null && type.isStructureFile())
5119   {
5120   filesmatched.add(new Object[] { file, protocol, mtch });
5121   continue;
5122   }
5123   }
5124   // File wasn't named like one of the sequences or wasn't a PDB
5125   // file.
5126   filesnotmatched.add(new Object[] { file, protocol, type });
5127   }
5128   }
5129   int assocfiles = 0;
5130   if (filesmatched.size() > 0)
5131   {
5132   boolean autoAssociate = Cache
5133   .getDefault("AUTOASSOCIATE_PDBANDSEQS", false);
5134   if (!autoAssociate)
5135   {
5136   String msg = MessageManager.formatMessage(
5137   "label.automatically_associate_structure_files_with_sequences_same_name",
5138   new Object[]
5139   { Integer.valueOf(filesmatched.size())
5140   .toString() });
5141   String ttl = MessageManager.getString(
5142   "label.automatically_associate_structure_files_by_name");
5143   int choice = JvOptionPane.showConfirmDialog(thisaf, msg,
5144   ttl, JvOptionPane.YES_NO_OPTION);
5145   autoAssociate = choice == JvOptionPane.YES_OPTION;
5146   }
5147   if (autoAssociate)
5148   {
5149   for (Object[] fm : filesmatched)
5150   {
5151   // try and associate
5152   // TODO: may want to set a standard ID naming formalism for
5153   // associating PDB files which have no IDs.
5154   for (SequenceI toassoc : (SequenceI[]) fm[2])
5155   {
5156   PDBEntry pe = new AssociatePdbFileWithSeq()
5157   .associatePdbWithSeq(fm[0].toString(),
5158   (DataSourceType) fm[1], toassoc, false,
5159   Desktop.instance);
5160   if (pe != null)
5161   {
5162   jalview.bin.Console.errPrintln("Associated file : "
5163   + (fm[0].toString()) + " with "
5164   + toassoc.getDisplayId(true));
5165   assocfiles++;
5166   }
5167   }
5168   // TODO: do we need to update overview ? only if features are
5169   // shown I guess
5170   alignPanel.paintAlignment(true, false);
5171   }
5172   }
5173   else
5174   {
5175   /*
5176   * add declined structures as sequences
5177   */
5178   for (Object[] o : filesmatched)
5179   {
5180   filesnotmatched.add(new Object[] { o[0], o[1] });
5181   }
5182   }
5183   }
5184   if (filesnotmatched.size() > 0)
5185   {
5186   if (assocfiles > 0 && (Cache.getDefault(
5187   "AUTOASSOCIATE_PDBANDSEQS_IGNOREOTHERS", false)
5188   || JvOptionPane.showConfirmDialog(thisaf,
5189   "" + MessageManager.formatMessage(
5190   "label.ignore_unmatched_dropped_files_info",
5191   new Object[]
5192   { Integer.valueOf(
5193   filesnotmatched.size())
5194   .toString() })
5195   + "",
5196   MessageManager.getString(
5197   "label.ignore_unmatched_dropped_files"),
5198   JvOptionPane.YES_NO_OPTION) == JvOptionPane.YES_OPTION))
5199   {
5200   return;
5201   }
5202   for (Object[] fn : filesnotmatched)
5203   {
5204   loadJalviewDataFile(fn[0], (DataSourceType) fn[1],
5205   (FileFormatI) fn[2], null);
5206   }
5207  
5208   }
5209   } catch (Exception ex)
5210   {
5211   ex.printStackTrace();
5212   }
5213   }
5214   }).start();
5215   }
5216   }
5217  
5218   /**
5219   * Attempt to load a "dropped" file or URL string, by testing in turn for
5220   *
5221   *
  • an Annotation file
  • 5222   *
  • a JNet file
  • 5223   *
  • a features file
  • 5224   *
  • else try to interpret as an alignment file
  • 5225   *
    5226   *
    5227   * @param file
    5228   * either a filename or a URL string.
    5229   */
    5230   public void loadJalviewDataFile(Object file, DataSourceType sourceType,
    5231   FileFormatI format, SequenceI assocSeq)
    5232   {
    5233   // BH 2018 was String file
    5234   try
    5235   {
    5236   if (sourceType == null)
    5237   {
    5238   sourceType = FormatAdapter.checkProtocol(file);
    5239   }
    5240   // if the file isn't identified, or not positively identified as some
    5241   // other filetype (PFAM is default unidentified alignment file type) then
    5242   // try to parse as annotation.
    5243   boolean isAnnotation = (format == null
    5244   || FileFormat.Pfam.equals(format))
    5245   ? new AnnotationFile().annotateAlignmentView(viewport,
    5246   file, sourceType)
    5247   : false;
    5248   // The 'true' flag checks for annotations
    5249   isAnnotation |= new IdentifyFile().identify(file, sourceType,
    5250   true) == FileFormat.JalviewAnnotation;
    5251   ;
    5252  
    5253   if (!isAnnotation)
    5254   {
    5255   // first see if its a T-COFFEE score file
    5256   TCoffeeScoreFile tcf = null;
    5257   try
    5258   {
    5259   tcf = new TCoffeeScoreFile(file, sourceType);
    5260   if (tcf.isValid())
    5261   {
    5262   if (tcf.annotateAlignment(viewport.getAlignment(), true))
    5263   {
    5264   buildColourMenu();
    5265   changeColour(
    5266   new TCoffeeColourScheme(viewport.getAlignment()));
    5267   isAnnotation = true;
    5268   setStatus(MessageManager.getString(
    5269   "label.successfully_pasted_tcoffee_scores_to_alignment"));
    5270   }
    5271   else
    5272   {
    5273   // some problem - if no warning its probable that the ID matching
    5274   // process didn't work
    5275   JvOptionPane.showMessageDialog(Desktop.desktop,
    5276   tcf.getWarningMessage() == null
    5277   ? MessageManager.getString(
    5278   "label.check_file_matches_sequence_ids_alignment")
    5279   : tcf.getWarningMessage(),
    5280   MessageManager.getString(
    5281   "label.problem_reading_tcoffee_score_file"),
    5282   JvOptionPane.WARNING_MESSAGE);
    5283   }
    5284   }
    5285   else
    5286   {
    5287   tcf = null;
    5288   }
    5289   } catch (Exception x)
    5290   {
    5291   Console.debug(
    5292   "Exception when processing data source as T-COFFEE score file",
    5293   x);
    5294   tcf = null;
    5295   }
    5296   if (tcf == null)
    5297   {
    5298   // try to see if its a JNet 'concise' style annotation file *before*
    5299   // we
    5300   // try to parse it as a features file
    5301   if (format == null)
    5302   {
    5303   format = new IdentifyFile().identify(file, sourceType);
    5304   }
    5305   if (FileFormat.FeatureSettings == format)
    5306   {
    5307   if (featureSettings != null)
    5308   {
    5309   featureSettings.load(file, sourceType);
    5310   }
    5311   else
    5312   {
    5313   FeatureSettings.loadFeatureSettingsFile(getFeatureRenderer(),
    5314   fileObject, sourceType);
    5315   }
    5316   }
    5317   else if (FileFormat.ScoreMatrix == format)
    5318   {
    5319   ScoreMatrixFile sm = new ScoreMatrixFile(
    5320   new FileParse(file, sourceType));
    5321   sm.parse();
    5322   // todo: i18n this message
    5323   setStatus(MessageManager.formatMessage(
    5324   "label.successfully_loaded_matrix",
    5325   sm.getMatrixName()));
    5326   }
    5327   else if (FileFormat.Jnet.equals(format))
    5328   {
    5329   JPredFile predictions = new JPredFile(file, sourceType);
    5330   new JnetAnnotationMaker();
    5331   JnetAnnotationMaker.add_annotation(predictions,
    5332   viewport.getAlignment(), 0, false);
    5333   viewport.getAlignment().setupJPredAlignment();
    5334   isAnnotation = true;
    5335   }
    5336  
    5337   // else if (IdentifyFile.FeaturesFile.equals(format))
    5338   else if (FileFormat.Features.equals(format))
    5339   {
    5340   if (parseFeaturesFile(file, sourceType))
    5341   {
    5342   SplitFrame splitFrame = (SplitFrame) getSplitViewContainer();
    5343   if (splitFrame != null)
    5344   {
    5345   splitFrame.repaint();
    5346   }
    5347   else
    5348   {
    5349   alignPanel.paintAlignment(true, true);
    5350   }
    5351   }
    5352   }
    5353   else
    5354   {
    5355   new FileLoader().LoadFile(viewport, file, sourceType, format);
    5356   }
    5357   }
    5358   }
    5359   if (isAnnotation)
    5360   {
    5361  
    5362   alignPanel.adjustAnnotationHeight();
    5363   viewport.updateSequenceIdColours();
    5364   viewport.updateAnnotationColours();
    5365   buildSortByAnnotationScoresMenu();
    5366   alignPanel.paintAlignment(true, true);
    5367   }
    5368   } catch (Exception ex)
    5369   {
    5370   ex.printStackTrace();
    5371   } catch (OutOfMemoryError oom)
    5372   {
    5373   try
    5374   {
    5375   System.gc();
    5376   } catch (Exception x)
    5377   {
    5378   }
    5379   new OOMWarning(
    5380   "loading data "
    5381   + (sourceType != null
    5382   ? (sourceType == DataSourceType.PASTE
    5383   ? "from clipboard."
    5384   : "using " + sourceType + " from "
    5385   + file)
    5386   : ".")
    5387   + (format != null
    5388   ? "(parsing as '" + format + "' file)"
    5389   : ""),
    5390   oom, Desktop.desktop);
    5391   }
    5392   }
    5393  
    5394   /**
    5395   * Method invoked by the ChangeListener on the tabbed pane, in other words
    5396   * when a different tabbed pane is selected by the user or programmatically.
    5397   */
    5398   @Override
    5399   public void tabSelectionChanged(int index)
    5400   {
    5401   if (index > -1)
    5402   {
    5403   /*
    5404   * update current Overview window title (if there is one) to add view name
    5405   * "Original" if necessary
    5406   */
    5407   alignPanel.setOverviewTitle(this);
    5408  
    5409   /*
    5410   * switch panels and set Overview title (if there is one because it was opened
    5411   * automatically)
    5412   */
    5413   alignPanel = alignPanels.get(index);
    5414   alignPanel.setOverviewTitle(this);
    5415  
    5416   viewport = alignPanel.av;
    5417   avc.setViewportAndAlignmentPanel(viewport, alignPanel);
    5418   setMenusFromViewport(viewport);
    5419   if (featureSettings != null && featureSettings.isOpen()
    5420   && featureSettings.fr.getViewport() != viewport)
    5421   {
    5422   if (viewport.isShowSequenceFeatures())
    5423   {
    5424   // refresh the featureSettings to reflect UI change
    5425   showFeatureSettingsUI();
    5426   }
    5427   else
    5428   {
    5429   // close feature settings for this view.
    5430   featureSettings.close();
    5431   }
    5432   }
    5433  
    5434   }
    5435  
    5436   /*
    5437   * 'focus' any colour slider that is open to the selected viewport
    5438   */
    5439   if (viewport.getConservationSelected())
    5440   {
    5441   SliderPanel.setConservationSlider(alignPanel,
    5442   viewport.getResidueShading(), alignPanel.getViewName());
    5443   }
    5444   else
    5445   {
    5446   SliderPanel.hideConservationSlider();
    5447   }
    5448   if (viewport.getByConsensusSecondaryStructureSelected())
    5449   {
    5450   SliderPanel.setConsensusSecondaryStructureSlider(alignPanel,
    5451   viewport.getResidueShading(), alignPanel.getViewName());
    5452   }
    5453   else
    5454   {
    5455   SliderPanel.hideConsensusSecondaryStructureSlider();
    5456   }
    5457   if (viewport.getAbovePIDThreshold())
    5458   {
    5459   SliderPanel.setPIDSliderSource(alignPanel,
    5460   viewport.getResidueShading(), alignPanel.getViewName());
    5461   }
    5462   else
    5463   {
    5464   SliderPanel.hidePIDSlider();
    5465   }
    5466  
    5467   /*
    5468   * If there is a frame linked to this one in a SplitPane, switch it to the same
    5469   * view tab index. No infinite recursion of calls should happen, since
    5470   * tabSelectionChanged() should not get invoked on setting the selected index to
    5471   * an unchanged value. Guard against setting an invalid index before the new
    5472   * view peer tab has been created.
    5473   */
    5474   final AlignViewportI peer = viewport.getCodingComplement();
    5475   if (peer != null)
    5476   {
    5477   AlignFrame linkedAlignFrame = ((AlignViewport) peer)
    5478   .getAlignPanel().alignFrame;
    5479   if (linkedAlignFrame.tabbedPane.getTabCount() > index)
    5480   {
    5481   linkedAlignFrame.tabbedPane.setSelectedIndex(index);
    5482   }
    5483   }
    5484   }
    5485  
    5486   /**
    5487   * On right mouse click on view tab, prompt for and set new view name.
    5488   */
    5489   @Override
    5490   public void tabbedPane_mousePressed(MouseEvent e)
    5491   {
    5492   if (e.isPopupTrigger())
    5493   {
    5494   String msg = MessageManager.getString("label.enter_view_name");
    5495   String ttl = tabbedPane.getTitleAt(tabbedPane.getSelectedIndex());
    5496   String reply = JvOptionPane.showInputDialog(msg, ttl);
    5497  
    5498   if (reply != null)
    5499   {
    5500   viewport.setViewName(reply);
    5501   // TODO warn if reply is in getExistingViewNames()?
    5502   tabbedPane.setTitleAt(tabbedPane.getSelectedIndex(), reply);
    5503   }
    5504   }
    5505   }
    5506  
    5507   public AlignViewport getCurrentView()
    5508   {
    5509   return viewport;
    5510   }
    5511  
    5512   /**
    5513   * Open the dialog for regex description parsing.
    5514   */
    5515   @Override
    5516   protected void extractScores_actionPerformed(ActionEvent e)
    5517   {
    5518   ParseProperties pp = new jalview.analysis.ParseProperties(
    5519   viewport.getAlignment());
    5520   // TODO: verify regex and introduce GUI dialog for version 2.5
    5521   // if (pp.getScoresFromDescription("col", "score column ",
    5522   // "\\W*([-+]?\\d*\\.?\\d*e?-?\\d*)\\W+([-+]?\\d*\\.?\\d*e?-?\\d*)",
    5523   // true)>0)
    5524   if (pp.getScoresFromDescription("description column",
    5525   "score in description column ", "\\W*([-+eE0-9.]+)", true) > 0)
    5526   {
    5527   buildSortByAnnotationScoresMenu();
    5528   }
    5529   }
    5530  
    5531   /*
    5532   * (non-Javadoc)
    5533   *
    5534   * @see jalview.jbgui.GAlignFrame#showDbRefs_actionPerformed(java.awt.event.
    5535   * ActionEvent )
    5536   */
    5537   @Override
    5538   protected void showDbRefs_actionPerformed(ActionEvent e)
    5539   {
    5540   viewport.setShowDBRefs(showDbRefsMenuitem.isSelected());
    5541   }
    5542  
    5543   /*
    5544   * (non-Javadoc)
    5545   *
    5546   * @seejalview.jbgui.GAlignFrame#showNpFeats_actionPerformed(java.awt.event.
    5547   * ActionEvent)
    5548   */
    5549   @Override
    5550   protected void showNpFeats_actionPerformed(ActionEvent e)
    5551   {
    5552   viewport.setShowNPFeats(showNpFeatsMenuitem.isSelected());
    5553   }
    5554  
    5555   /**
    5556   * find the viewport amongst the tabs in this alignment frame and close that
    5557   * tab
    5558   *
    5559   * @param av
    5560   */
    5561   public boolean closeView(AlignViewportI av)
    5562   {
    5563   if (viewport == av)
    5564   {
    5565   this.closeMenuItem_actionPerformed(false);
    5566   return true;
    5567   }
    5568   Component[] comp = tabbedPane.getComponents();
    5569   for (int i = 0; comp != null && i < comp.length; i++)
    5570   {
    5571   if (comp[i] instanceof AlignmentPanel)
    5572   {
    5573   if (((AlignmentPanel) comp[i]).av == av)
    5574   {
    5575   // close the view.
    5576   closeView((AlignmentPanel) comp[i]);
    5577   return true;
    5578   }
    5579   }
    5580   }
    5581   return false;
    5582   }
    5583  
    5584   protected void build_fetchdbmenu(JMenu webService)
    5585   {
    5586   // Temporary hack - DBRef Fetcher always top level ws entry.
    5587   // TODO We probably want to store a sequence database checklist in
    5588   // preferences and have checkboxes.. rather than individual sources selected
    5589   // here
    5590   final JMenu rfetch = new JMenu(
    5591   MessageManager.getString("action.fetch_db_references"));
    5592   rfetch.setToolTipText(MessageManager.getString(
    5593   "label.retrieve_parse_sequence_database_records_alignment_or_selected_sequences"));
    5594   webService.add(rfetch);
    5595  
    5596   final JCheckBoxMenuItem trimrs = new JCheckBoxMenuItem(
    5597   MessageManager.getString("option.trim_retrieved_seqs"));
    5598   trimrs.setToolTipText(
    5599   MessageManager.getString("label.trim_retrieved_sequences"));
    5600   trimrs.setSelected(
    5601   Cache.getDefault(DBRefFetcher.TRIM_RETRIEVED_SEQUENCES, true));
    5602   trimrs.addActionListener(new ActionListener()
    5603   {
    5604   @Override
    5605   public void actionPerformed(ActionEvent e)
    5606   {
    5607   trimrs.setSelected(trimrs.isSelected());
    5608   Cache.setProperty(DBRefFetcher.TRIM_RETRIEVED_SEQUENCES,
    5609   Boolean.valueOf(trimrs.isSelected()).toString());
    5610   }
    5611   });
    5612   rfetch.add(trimrs);
    5613   JMenuItem fetchr = new JMenuItem(
    5614   MessageManager.getString("label.standard_databases"));
    5615   fetchr.setToolTipText(
    5616   MessageManager.getString("label.fetch_embl_uniprot"));
    5617   fetchr.addActionListener(new ActionListener()
    5618   {
    5619  
    5620   @Override
    5621   public void actionPerformed(ActionEvent e)
    5622   {
    5623   new Thread(new Runnable()
    5624   {
    5625   @Override
    5626   public void run()
    5627   {
    5628   boolean isNucleotide = alignPanel.alignFrame.getViewport()
    5629   .getAlignment().isNucleotide();
    5630   DBRefFetcher dbRefFetcher = new DBRefFetcher(
    5631   alignPanel.av.getSequenceSelection(),
    5632   alignPanel.alignFrame, null,
    5633   alignPanel.alignFrame.featureSettings, isNucleotide);
    5634   dbRefFetcher.addListener(new FetchFinishedListenerI()
    5635   {
    5636   @Override
    5637   public void finished()
    5638   {
    5639  
    5640   for (FeatureSettingsModelI srcSettings : dbRefFetcher
    5641   .getFeatureSettingsModels())
    5642   {
    5643  
    5644   alignPanel.av.mergeFeaturesStyle(srcSettings);
    5645   }
    5646   AlignFrame.this.setMenusForViewport();
    5647   }
    5648   });
    5649   dbRefFetcher.fetchDBRefs(false);
    5650   }
    5651   }).start();
    5652  
    5653   }
    5654  
    5655   });
    5656   rfetch.add(fetchr);
    5657   new Thread(new Runnable()
    5658   {
    5659   @Override
    5660   public void run()
    5661   {
    5662   final jalview.ws.SequenceFetcher sf = jalview.gui.SequenceFetcher
    5663   .getSequenceFetcherSingleton();
    5664   javax.swing.SwingUtilities.invokeLater(new Runnable()
    5665   {
    5666   @Override
    5667   public void run()
    5668   {
    5669   String[] dbclasses = sf.getNonAlignmentSources();
    5670   List otherdb;
    5671   JMenu dfetch = new JMenu();
    5672   JMenu ifetch = new JMenu();
    5673   JMenuItem fetchr = null;
    5674   int comp = 0, icomp = 0, mcomp = 15;
    5675   String mname = null;
    5676   int dbi = 0;
    5677   for (String dbclass : dbclasses)
    5678   {
    5679   otherdb = sf.getSourceProxy(dbclass);
    5680   // add a single entry for this class, or submenu allowing 'fetch
    5681   // all' or pick one
    5682   if (otherdb == null || otherdb.size() < 1)
    5683   {
    5684   continue;
    5685   }
    5686   if (mname == null)
    5687   {
    5688   mname = "From " + dbclass;
    5689   }
    5690   if (otherdb.size() == 1)
    5691   {
    5692   final DbSourceProxy[] dassource = otherdb
    5693   .toArray(new DbSourceProxy[0]);
    5694   DbSourceProxy src = otherdb.get(0);
    5695   fetchr = new JMenuItem(src.getDbSource());
    5696   fetchr.addActionListener(new ActionListener()
    5697   {
    5698  
    5699   @Override
    5700   public void actionPerformed(ActionEvent e)
    5701   {
    5702   new Thread(new Runnable()
    5703   {
    5704  
    5705   @Override
    5706   public void run()
    5707   {
    5708   boolean isNucleotide = alignPanel.alignFrame
    5709   .getViewport().getAlignment()
    5710   .isNucleotide();
    5711   DBRefFetcher dbRefFetcher = new DBRefFetcher(
    5712   alignPanel.av.getSequenceSelection(),
    5713   alignPanel.alignFrame, dassource,
    5714   alignPanel.alignFrame.featureSettings,
    5715   isNucleotide);
    5716   dbRefFetcher
    5717   .addListener(new FetchFinishedListenerI()
    5718   {
    5719   @Override
    5720   public void finished()
    5721   {
    5722   FeatureSettingsModelI srcSettings = dassource[0]
    5723   .getFeatureColourScheme();
    5724   alignPanel.av.mergeFeaturesStyle(
    5725   srcSettings);
    5726   AlignFrame.this.setMenusForViewport();
    5727   }
    5728   });
    5729   dbRefFetcher.fetchDBRefs(false);
    5730   }
    5731   }).start();
    5732   }
    5733  
    5734   });
    5735   fetchr.setToolTipText(JvSwingUtils.wrapTooltip(true,
    5736   MessageManager.formatMessage(
    5737   "label.fetch_retrieve_from", new Object[]
    5738   { src.getDbName() })));
    5739   dfetch.add(fetchr);
    5740   comp++;
    5741   }
    5742   else
    5743   {
    5744   final DbSourceProxy[] dassource = otherdb
    5745   .toArray(new DbSourceProxy[0]);
    5746   // fetch all entry
    5747   DbSourceProxy src = otherdb.get(0);
    5748   fetchr = new JMenuItem(MessageManager
    5749   .formatMessage("label.fetch_all_param", new Object[]
    5750   { src.getDbSource() }));
    5751   fetchr.addActionListener(new ActionListener()
    5752   {
    5753   @Override
    5754   public void actionPerformed(ActionEvent e)
    5755   {
    5756   new Thread(new Runnable()
    5757   {
    5758  
    5759   @Override
    5760   public void run()
    5761   {
    5762   boolean isNucleotide = alignPanel.alignFrame
    5763   .getViewport().getAlignment()
    5764   .isNucleotide();
    5765   DBRefFetcher dbRefFetcher = new DBRefFetcher(
    5766   alignPanel.av.getSequenceSelection(),
    5767   alignPanel.alignFrame, dassource,
    5768   alignPanel.alignFrame.featureSettings,
    5769   isNucleotide);
    5770   dbRefFetcher
    5771   .addListener(new FetchFinishedListenerI()
    5772   {
    5773   @Override
    5774   public void finished()
    5775   {
    5776   AlignFrame.this.setMenusForViewport();
    5777   }
    5778   });
    5779   dbRefFetcher.fetchDBRefs(false);
    5780   }
    5781   }).start();
    5782   }
    5783   });
    5784  
    5785   fetchr.setToolTipText(JvSwingUtils.wrapTooltip(true,
    5786   MessageManager.formatMessage(
    5787   "label.fetch_retrieve_from_all_sources",
    5788   new Object[]
    5789   { Integer.valueOf(otherdb.size())
    5790   .toString(),
    5791   src.getDbSource(), src.getDbName() })));
    5792   dfetch.add(fetchr);
    5793   comp++;
    5794   // and then build the rest of the individual menus
    5795   ifetch = new JMenu(MessageManager.formatMessage(
    5796   "label.source_from_db_source", new Object[]
    5797   { src.getDbSource() }));
    5798   icomp = 0;
    5799   String imname = null;
    5800   int i = 0;
    5801   for (DbSourceProxy sproxy : otherdb)
    5802   {
    5803   String dbname = sproxy.getDbName();
    5804   String sname = dbname.length() > 5
    5805   ? dbname.substring(0, 5) + "..."
    5806   : dbname;
    5807   String msname = dbname.length() > 10
    5808   ? dbname.substring(0, 10) + "..."
    5809   : dbname;
    5810   if (imname == null)
    5811   {
    5812   imname = MessageManager
    5813   .formatMessage("label.from_msname", new Object[]
    5814   { sname });
    5815   }
    5816   fetchr = new JMenuItem(msname);
    5817   final DbSourceProxy[] dassrc = { sproxy };
    5818   fetchr.addActionListener(new ActionListener()
    5819   {
    5820  
    5821   @Override
    5822   public void actionPerformed(ActionEvent e)
    5823   {
    5824   new Thread(new Runnable()
    5825   {
    5826  
    5827   @Override
    5828   public void run()
    5829   {
    5830   boolean isNucleotide = alignPanel.alignFrame
    5831   .getViewport().getAlignment()
    5832   .isNucleotide();
    5833   DBRefFetcher dbRefFetcher = new DBRefFetcher(
    5834   alignPanel.av.getSequenceSelection(),
    5835   alignPanel.alignFrame, dassrc,
    5836   alignPanel.alignFrame.featureSettings,
    5837   isNucleotide);
    5838   dbRefFetcher
    5839   .addListener(new FetchFinishedListenerI()
    5840   {
    5841   @Override
    5842   public void finished()
    5843   {
    5844   AlignFrame.this.setMenusForViewport();
    5845   }
    5846   });
    5847   dbRefFetcher.fetchDBRefs(false);
    5848   }
    5849   }).start();
    5850   }
    5851  
    5852   });
    5853   fetchr.setToolTipText(
    5854   "" + MessageManager.formatMessage(
    5855   "label.fetch_retrieve_from", new Object[]
    5856   { dbname }));
    5857   ifetch.add(fetchr);
    5858   ++i;
    5859   if (++icomp >= mcomp || i == (otherdb.size()))
    5860   {
    5861   ifetch.setText(MessageManager.formatMessage(
    5862   "label.source_to_target", imname, sname));
    5863   dfetch.add(ifetch);
    5864   ifetch = new JMenu();
    5865   imname = null;
    5866   icomp = 0;
    5867   comp++;
    5868   }
    5869   }
    5870   }
    5871   ++dbi;
    5872   if (comp >= mcomp || dbi >= (dbclasses.length))
    5873   {
    5874   dfetch.setText(MessageManager.formatMessage(
    5875   "label.source_to_target", mname, dbclass));
    5876   rfetch.add(dfetch);
    5877   dfetch = new JMenu();
    5878   mname = null;
    5879   comp = 0;
    5880   }
    5881   }
    5882   }
    5883  
    5884   });
    5885   }
    5886   }).start();
    5887  
    5888   }
    5889  
    5890   /**
    5891   * Left justify the whole alignment.
    5892   */
    5893   @Override
    5894   protected void justifyLeftMenuItem_actionPerformed(ActionEvent e)
    5895   {
    5896   avc.justify_Region(true);
    5897   }
    5898  
    5899   /**
    5900   * Right justify the whole alignment.
    5901   */
    5902   @Override
    5903   protected void justifyRightMenuItem_actionPerformed(ActionEvent e)
    5904   {
    5905   avc.justify_Region(false);
    5906   }
    5907  
    5908   @Override
    5909   public void setShowSeqFeatures(boolean b)
    5910   {
    5911   showSeqFeatures.setSelected(b);
    5912   viewport.setShowSequenceFeatures(b);
    5913   }
    5914  
    5915   /*
    5916   * (non-Javadoc)
    5917   *
    5918   * @see jalview.jbgui.GAlignFrame#showUnconservedMenuItem_actionPerformed(java.
    5919   * awt.event.ActionEvent)
    5920   */
    5921   @Override
    5922   protected void showUnconservedMenuItem_actionPerformed(ActionEvent e)
    5923   {
    5924   viewport.setShowUnconserved(showNonconservedMenuItem.getState());
    5925   alignPanel.paintAlignment(false, false);
    5926   }
    5927  
    5928   @Override
    5929   protected void showStructureProvider_actionPerformed(ActionEvent e)
    5930   {
    5931   viewport.setShowStructureProvider(showStrucProvider.getState());
    5932   alignPanel.paintAlignment(false, false);
    5933  
    5934   }
    5935  
    5936   @Override
    5937   protected void updateShowSecondaryStructureMenu(JMenu showSS,
    5938   ButtonGroup ssButtonGroup)
    5939   {
    5940  
    5941   List ssSources = new ArrayList();
    5942   AlignmentAnnotation[] anns = alignPanel.getAlignment()
    5943   .getAlignmentAnnotation();
    5944   Map checkboxMap = getCheckboxesInMenu(
    5945   showSS);
    5946  
    5947   ssSources = AlignmentUtils.extractSSSourceInAlignmentAnnotation(anns);
    5948  
    5949   if (ssSources == null)
    5950   {
    5951   showSS.removeAll();
    5952   ssButtonGroup.clearSelection();
    5953   return;
    5954   }
    5955  
    5956   List selectedCheckBoxes = getSelectedOptions(checkboxMap);
    5957  
    5958   // Add checkboxes for categories
    5959   for (String ssSource : ssSources)
    5960   {
    5961  
    5962   if (checkboxMap.get(ssSource) == null)
    5963   {
    5964   JCheckBoxMenuItem checkBox = new JCheckBoxMenuItem(ssSource);
    5965   checkBox.setSelected(false);
    5966  
    5967   checkBox.addItemListener(e -> {
    5968   if (e.getStateChange() == ItemEvent.SELECTED)
    5969   {
    5970  
    5971   showOrHideSecondaryStructureForSource(ssSource, true);
    5972  
    5973   }
    5974   else
    5975   {
    5976  
    5977   showOrHideSecondaryStructureForSource(ssSource, false);
    5978  
    5979   }
    5980   });
    5981   showSS.add(checkBox);
    5982   }
    5983   }
    5984   // Iterate over the keys of checkboxMap
    5985   for (String key : checkboxMap.keySet())
    5986   {
    5987   // Check if the key is not in ssSources
    5988   if (!ssSources.contains(key))
    5989   {
    5990   showSS.remove(checkboxMap.get(key));
    5991   checkboxMap.remove(key);
    5992   selectedCheckBoxes.remove(key);
    5993   }
    5994   if (selectedCheckBoxes.contains(key))
    5995   {
    5996   checkboxMap.get(key).setSelected(true);
    5997   }
    5998   else
    5999   {
    6000   checkboxMap.get(key).setSelected(false);
    6001   }
    6002  
    6003   ssButtonGroup.clearSelection();
    6004   }
    6005  
    6006   }
    6007  
    6008   private List getSelectedOptions(
    6009   Map checkboxMap)
    6010   {
    6011   List selectedOptions = new ArrayList<>();
    6012   for (String key : checkboxMap.keySet())
    6013   {
    6014   JCheckBoxMenuItem checkbox = checkboxMap.get(key);
    6015   if (checkbox.isSelected())
    6016   {
    6017   selectedOptions.add(key);
    6018   }
    6019   }
    6020   return selectedOptions;
    6021   }
    6022  
    6023   private Map getCheckboxesInMenu(JMenu menu)
    6024   {
    6025   Map checkboxMap = new HashMap<>();
    6026   for (Component component : menu.getMenuComponents())
    6027   {
    6028   if (component instanceof JCheckBoxMenuItem)
    6029   {
    6030   JCheckBoxMenuItem checkbox = (JCheckBoxMenuItem) component;
    6031   checkboxMap.put(checkbox.getText(), checkbox);
    6032   }
    6033   }
    6034   return checkboxMap;
    6035   }
    6036  
    6037   @Override
    6038   protected void showOrHideSecondaryStructureForSource(
    6039   String ssSourceSelection, boolean visible)
    6040   {
    6041  
    6042   String _noneOption = MessageManager
    6043   .getString("option.ss_providers_none");
    6044   boolean nonOption = ssSourceSelection!=null && ssSourceSelection.equals(_noneOption);
    6045   String allOption = MessageManager.getString("option.ss_providers_all");
    6046  
    6047   AlignmentAnnotation[] annotations = alignPanel.getAlignment()
    6048   .getAlignmentAnnotation();
    6049  
    6050   for (AlignmentAnnotation aa : annotations)
    6051   {
    6052  
    6053   if (aa.groupRef != null)
    6054   {
    6055   continue;
    6056   }
    6057  
    6058   boolean isSSConsensus = aa.label.startsWith(
    6059   MessageManager.getString("label.ssconsensus_label"));
    6060   boolean matchesSSSourceSelection = aa.description
    6061   .startsWith(ssSourceSelection);
    6062  
    6063   if (isSSConsensus && (matchesSSSourceSelection
    6064   || nonOption))
    6065   {
    6066  
    6067   if (ssSourceSelection.equals(allOption))
    6068   {
    6069   aa.visible = true;
    6070   break;
    6071   }
    6072  
    6073   if (!aa.description.startsWith(allOption))
    6074   aa.visible = visible;
    6075  
    6076   }
    6077  
    6078   for (String label : Constants.SECONDARY_STRUCTURE_LABELS.keySet())
    6079   {
    6080  
    6081   if (label.equals(aa.label))
    6082   {
    6083  
    6084   String ssSource = AlignmentAnnotationUtils
    6085   .extractSSSourceFromAnnotationDescription(aa);
    6086  
    6087   if (nonOption || ssSource != null && (ssSource.equals(ssSourceSelection)))
    6088   {
    6089   aa.visible = visible;
    6090   }
    6091   }
    6092   }
    6093  
    6094   }
    6095  
    6096   PaintRefresher.Refresh(this, viewport.getSequenceSetId());
    6097   alignPanel.updateAnnotation();
    6098   alignPanel.paintAlignment(true, true);
    6099  
    6100   }
    6101  
    6102   @Override
    6103   protected void showSSConsensus_actionPerformed(ActionEvent e)
    6104   {
    6105   viewport.setShowSSConsensus(showSSConsensus.getState());
    6106   alignPanel.updateAnnotation(applyAutoAnnotationSettings.getState());
    6107   }
    6108  
    6109   /*
    6110   * (non-Javadoc)
    6111   *
    6112   * @see
    6113   * jalview.jbgui.GAlignFrame#showGroupConsensus_actionPerformed(java.awt.event
    6114   * .ActionEvent)
    6115   */
    6116   @Override
    6117   protected void showGroupConsensus_actionPerformed(ActionEvent e)
    6118   {
    6119   viewport.setShowGroupConsensus(showGroupConsensus.getState());
    6120   alignPanel.updateAnnotation(applyAutoAnnotationSettings.getState());
    6121  
    6122   }
    6123  
    6124   @Override
    6125   protected void showGroupSSConsensus_actionPerformed(ActionEvent e)
    6126   {
    6127   viewport.setShowGroupSSConsensus(showGroupSSConsensus.getState());
    6128   alignPanel.updateAnnotation(applyAutoAnnotationSettings.getState());
    6129  
    6130   }
    6131  
    6132   /*
    6133   * (non-Javadoc)
    6134   *
    6135   * @see jalview.jbgui.GAlignFrame#showGroupConservation_actionPerformed(java.awt
    6136   * .event.ActionEvent)
    6137   */
    6138   @Override
    6139   protected void showGroupConservation_actionPerformed(ActionEvent e)
    6140   {
    6141   viewport.setShowGroupConservation(showGroupConservation.getState());
    6142   alignPanel.updateAnnotation(applyAutoAnnotationSettings.getState());
    6143   }
    6144  
    6145   /*
    6146   * (non-Javadoc)
    6147   *
    6148   * @see
    6149   * jalview.jbgui.GAlignFrame#showConsensusHistogram_actionPerformed(java.awt
    6150   * .event.ActionEvent)
    6151   */
    6152   @Override
    6153   protected void showConsensusHistogram_actionPerformed(ActionEvent e)
    6154   {
    6155   viewport.setShowConsensusHistogram(showConsensusHistogram.getState());
    6156   alignPanel.updateAnnotation(applyAutoAnnotationSettings.getState());
    6157   }
    6158  
    6159   /*
    6160   * (non-Javadoc)
    6161   *
    6162   * @see jalview.jbgui.GAlignFrame#showConsensusProfile_actionPerformed(java.awt
    6163   * .event.ActionEvent)
    6164   */
    6165   @Override
    6166   protected void showSequenceLogo_actionPerformed(ActionEvent e)
    6167   {
    6168   viewport.setShowSequenceLogo(showSequenceLogo.getState());
    6169   alignPanel.updateAnnotation(applyAutoAnnotationSettings.getState());
    6170   }
    6171  
    6172   @Override
    6173   protected void normaliseSequenceLogo_actionPerformed(ActionEvent e)
    6174   {
    6175   showSequenceLogo.setState(true);
    6176   viewport.setShowSequenceLogo(true);
    6177   viewport.setNormaliseSequenceLogo(normaliseSequenceLogo.getState());
    6178   alignPanel.updateAnnotation(applyAutoAnnotationSettings.getState());
    6179   }
    6180  
    6181   @Override
    6182   protected void applyAutoAnnotationSettings_actionPerformed(ActionEvent e)
    6183   {
    6184   alignPanel.updateAnnotation(applyAutoAnnotationSettings.getState());
    6185   }
    6186  
    6187   /*
    6188   * (non-Javadoc)
    6189   *
    6190   * @see jalview.jbgui.GAlignFrame#makeGrpsFromSelection_actionPerformed(java.awt
    6191   * .event.ActionEvent)
    6192   */
    6193   @Override
    6194   protected void makeGrpsFromSelection_actionPerformed(ActionEvent e)
    6195   {
    6196   if (avc.makeGroupsFromSelection())
    6197   {
    6198   PaintRefresher.Refresh(this, viewport.getSequenceSetId());
    6199   alignPanel.updateAnnotation();
    6200   alignPanel.paintAlignment(true,
    6201   viewport.needToUpdateStructureViews());
    6202   }
    6203   }
    6204  
    6205   public void clearAlignmentSeqRep()
    6206   {
    6207   // TODO refactor alignmentseqrep to controller
    6208   if (viewport.getAlignment().hasSeqrep())
    6209   {
    6210   viewport.getAlignment().setSeqrep(null);
    6211   PaintRefresher.Refresh(this, viewport.getSequenceSetId());
    6212   alignPanel.updateAnnotation();
    6213   alignPanel.paintAlignment(true, true);
    6214   }
    6215   }
    6216  
    6217   @Override
    6218   protected void createGroup_actionPerformed(ActionEvent e)
    6219   {
    6220   if (avc.createGroup())
    6221   {
    6222   if (applyAutoAnnotationSettings.isSelected())
    6223   {
    6224   alignPanel.updateAnnotation(true, false);
    6225   }
    6226   alignPanel.alignmentChanged();
    6227   }
    6228   }
    6229  
    6230   @Override
    6231   protected void unGroup_actionPerformed(ActionEvent e)
    6232   {
    6233   if (avc.unGroup())
    6234   {
    6235   alignPanel.alignmentChanged();
    6236   }
    6237   }
    6238  
    6239   /**
    6240   * make the given alignmentPanel the currently selected tab
    6241   *
    6242   * @param alignmentPanel
    6243   */
    6244   public void setDisplayedView(AlignmentPanel alignmentPanel)
    6245   {
    6246   if (!viewport.getSequenceSetId()
    6247   .equals(alignmentPanel.av.getSequenceSetId()))
    6248   {
    6249   throw new Error(MessageManager.getString(
    6250   "error.implementation_error_cannot_show_view_alignment_frame"));
    6251   }
    6252   if (tabbedPane != null && tabbedPane.getTabCount() > 0 && alignPanels
    6253   .indexOf(alignmentPanel) != tabbedPane.getSelectedIndex())
    6254   {
    6255   tabbedPane.setSelectedIndex(alignPanels.indexOf(alignmentPanel));
    6256   }
    6257   }
    6258  
    6259   /**
    6260   * Action on selection of menu options to Show or Hide annotations.
    6261   *
    6262   * @param visible
    6263   * @param forSequences
    6264   * update sequence-related annotations
    6265   * @param forAlignment
    6266   * update non-sequence-related annotations
    6267   */
    6268   @Override
    6269   public void setAnnotationsVisibility(boolean visible,
    6270   boolean forSequences, boolean forAlignment)
    6271   {
    6272   AlignmentAnnotation[] anns = alignPanel.getAlignment()
    6273   .getAlignmentAnnotation();
    6274   if (anns == null)
    6275   {
    6276   return;
    6277   }
    6278   for (AlignmentAnnotation aa : anns)
    6279   {
    6280   /*
    6281   * don't display non-positional annotations on an alignment
    6282   */
    6283   if (aa.annotations == null)
    6284   {
    6285   continue;
    6286   }
    6287   boolean apply = (aa.sequenceRef == null && forAlignment)
    6288   || (aa.sequenceRef != null && forSequences);
    6289   if (apply)
    6290   {
    6291   aa.visible = visible;
    6292   }
    6293   }
    6294   alignPanel.validateAnnotationDimensions(true);
    6295   // TODO this triggers relayout of annotation panel - otherwise annotation
    6296   // label height is different to panel height
    6297   alignPanel.fontChanged();
    6298   alignPanel.alignmentChanged();
    6299   }
    6300  
    6301   /**
    6302   * Store selected annotation sort order for the view and repaint.
    6303   */
    6304   @Override
    6305   protected void sortAnnotations_actionPerformed()
    6306   {
    6307   this.alignPanel.av.setSortAnnotationsBy(getAnnotationSortOrder());
    6308   this.alignPanel.av
    6309   .setShowAutocalculatedAbove(isShowAutoCalculatedAbove());
    6310   alignPanel.paintAlignment(false, false);
    6311   }
    6312  
    6313   /**
    6314   *
    6315   * @return alignment panels in this alignment frame
    6316   */
    6317   public List getAlignPanels()
    6318   {
    6319   // alignPanels is never null
    6320   // return alignPanels == null ? Arrays.asList(alignPanel) : alignPanels;
    6321   return alignPanels;
    6322   }
    6323  
    6324   /**
    6325   * Open a new alignment window, with the cDNA associated with this (protein)
    6326   * alignment, aligned as is the protein.
    6327   */
    6328   protected void viewAsCdna_actionPerformed()
    6329   {
    6330   // TODO no longer a menu action - refactor as required
    6331   final AlignmentI alignment = getViewport().getAlignment();
    6332   List mappings = alignment.getCodonFrames();
    6333   if (mappings == null)
    6334   {
    6335   return;
    6336   }
    6337   List cdnaSeqs = new ArrayList<>();
    6338   for (SequenceI aaSeq : alignment.getSequences())
    6339   {
    6340   for (AlignedCodonFrame acf : mappings)
    6341   {
    6342   SequenceI dnaSeq = acf.getDnaForAaSeq(aaSeq.getDatasetSequence());
    6343   if (dnaSeq != null)
    6344   {
    6345   /*
    6346   * There is a cDNA mapping for this protein sequence - add to new alignment. It
    6347   * will share the same dataset sequence as other mapped cDNA (no new mappings
    6348   * need to be created).
    6349   */
    6350   final Sequence newSeq = new Sequence(dnaSeq);
    6351   newSeq.setDatasetSequence(dnaSeq);
    6352   cdnaSeqs.add(newSeq);
    6353   }
    6354   }
    6355   }
    6356   if (cdnaSeqs.size() == 0)
    6357   {
    6358   // show a warning dialog no mapped cDNA
    6359   return;
    6360   }
    6361   AlignmentI cdna = new Alignment(
    6362   cdnaSeqs.toArray(new SequenceI[cdnaSeqs.size()]));
    6363   GAlignFrame alignFrame = new AlignFrame(cdna, AlignFrame.DEFAULT_WIDTH,
    6364   AlignFrame.DEFAULT_HEIGHT);
    6365   cdna.alignAs(alignment);
    6366   String newtitle = "cDNA " + MessageManager.getString("label.for") + " "
    6367   + this.title;
    6368   Desktop.addInternalFrame(alignFrame, newtitle, AlignFrame.DEFAULT_WIDTH,
    6369   AlignFrame.DEFAULT_HEIGHT);
    6370   }
    6371  
    6372   /**
    6373   * Set visibility of dna/protein complement view (available when shown in a
    6374   * split frame).
    6375   *
    6376   * @param show
    6377   */
    6378   @Override
    6379   protected void showComplement_actionPerformed(boolean show)
    6380   {
    6381   SplitContainerI sf = getSplitViewContainer();
    6382   if (sf != null)
    6383   {
    6384   sf.setComplementVisible(this, show);
    6385   }
    6386   }
    6387  
    6388   /**
    6389   * Generate the reverse (optionally complemented) of the selected sequences,
    6390   * and add them to the alignment
    6391   */
    6392   @Override
    6393   protected void showReverse_actionPerformed(boolean complement)
    6394   {
    6395   AlignmentI al = null;
    6396   try
    6397   {
    6398   Dna dna = new Dna(viewport, viewport.getViewAsVisibleContigs(true));
    6399   al = dna.reverseCdna(complement);
    6400   viewport.addAlignment(al, "");
    6401   addHistoryItem(new EditCommand(
    6402   MessageManager.getString("label.add_sequences"), Action.PASTE,
    6403   al.getSequencesArray(), 0, al.getWidth(),
    6404   viewport.getAlignment()));
    6405   } catch (Exception ex)
    6406   {
    6407   jalview.bin.Console.errPrintln(ex.getMessage());
    6408   return;
    6409   }
    6410   }
    6411  
    6412   /**
    6413   * Try to run a script in the Groovy console, having first ensured that this
    6414   * AlignFrame is set as currentAlignFrame in Desktop, to allow the script to
    6415   * be targeted at this alignment.
    6416   */
    6417   @Override
    6418   protected void runGroovy_actionPerformed()
    6419   {
    6420   Jalview.getInstance().setCurrentAlignFrame(this);
    6421   groovy.console.ui.Console console = Desktop.getGroovyConsole();
    6422   if (console != null)
    6423   {
    6424   try
    6425   {
    6426   console.setVariable(JalviewObjectI.currentAlFrameName, this);
    6427   console.runScript();
    6428   } catch (Exception ex)
    6429   {
    6430   jalview.bin.Console.errPrintln((ex.toString()));
    6431   JvOptionPane.showInternalMessageDialog(Desktop.desktop,
    6432   MessageManager.getString("label.couldnt_run_groovy_script"),
    6433   MessageManager.getString("label.groovy_support_failed"),
    6434   JvOptionPane.ERROR_MESSAGE);
    6435   }
    6436   }
    6437   else
    6438   {
    6439   jalview.bin.Console
    6440   .errPrintln("Can't run Groovy script as console not found");
    6441   }
    6442   }
    6443  
    6444   /**
    6445   * Hides columns containing (or not containing) a specified feature, provided
    6446   * that would not leave all columns hidden
    6447   *
    6448   * @param featureType
    6449   * @param columnsContaining
    6450   * @return
    6451   */
    6452   public boolean hideFeatureColumns(String featureType,
    6453   boolean columnsContaining)
    6454   {
    6455   boolean notForHiding = avc.markColumnsContainingFeatures(
    6456   columnsContaining, false, false, featureType);
    6457   if (notForHiding)
    6458   {
    6459   if (avc.markColumnsContainingFeatures(!columnsContaining, false,
    6460   false, featureType))
    6461   {
    6462   getViewport().hideSelectedColumns();
    6463   return true;
    6464   }
    6465   }
    6466   return false;
    6467   }
    6468  
    6469   @Override
    6470   protected void selectHighlightedColumns_actionPerformed(
    6471   ActionEvent actionEvent)
    6472   {
    6473   // include key modifier check in case user selects from menu
    6474   avc.markHighlightedColumns(
    6475   (actionEvent.getModifiers() & ActionEvent.ALT_MASK) != 0, true,
    6476   (actionEvent.getModifiers() & (ActionEvent.META_MASK
    6477   | ActionEvent.CTRL_MASK)) != 0);
    6478   }
    6479  
    6480   @Override
    6481   protected void copyHighlightedColumns_actionPerformed(
    6482   ActionEvent actionEvent)
    6483   {
    6484   avc.copyHighlightedRegionsToClipboard();
    6485   }
    6486  
    6487   /**
    6488   * Rebuilds the Colour menu, including any user-defined colours which have
    6489   * been loaded either on startup or during the session
    6490   */
    6491   public void buildColourMenu()
    6492   {
    6493   colourMenu.removeAll();
    6494  
    6495   colourMenu.add(applyToAllGroups);
    6496   colourMenu.add(textColour);
    6497   colourMenu.addSeparator();
    6498  
    6499   ButtonGroup bg = ColourMenuHelper.addMenuItems(colourMenu, this,
    6500   viewport.getAlignment(), false);
    6501  
    6502   colourMenu.add(annotationColour);
    6503   bg.add(annotationColour);
    6504   colourMenu.addSeparator();
    6505   colourMenu.add(conservationMenuItem);
    6506   colourMenu.add(modifyConservation);
    6507   colourMenu.add(byConsensusSecondaryStructureMenuItem);
    6508   colourMenu.add(modifyConsensusSecondaryStructureThreshold);
    6509   colourMenu.add(abovePIDThreshold);
    6510   colourMenu.add(modifyPID);
    6511  
    6512   ColourSchemeI colourScheme = viewport.getGlobalColourScheme();
    6513   ColourMenuHelper.setColourSelected(colourMenu, colourScheme);
    6514   }
    6515  
    6516   /**
    6517   * Open a dialog (if not already open) that allows the user to select and
    6518   * calculate PCA or Tree analysis
    6519   */
    6520   protected void openTreePcaDialog()
    6521   {
    6522   if (alignPanel.getCalculationDialog() == null)
    6523   {
    6524   new CalculationChooser(AlignFrame.this);
    6525   }
    6526   }
    6527  
    6528   @Override
    6529   protected void loadVcf_actionPerformed()
    6530   {
    6531   JalviewFileChooser chooser = new JalviewFileChooser(
    6532   Cache.getProperty("LAST_DIRECTORY"));
    6533   chooser.setFileView(new JalviewFileView());
    6534   chooser.setDialogTitle(MessageManager.getString("label.load_vcf_file"));
    6535   chooser.setToolTipText(MessageManager.getString("label.load_vcf_file"));
    6536   final AlignFrame us = this;
    6537   chooser.setResponseHandler(0, () -> {
    6538   String choice = chooser.getSelectedFile().getPath();
    6539   Cache.setProperty("LAST_DIRECTORY", choice);
    6540   SequenceI[] seqs = viewport.getAlignment().getSequencesArray();
    6541   new VCFLoader(choice).loadVCF(seqs, us);
    6542   });
    6543   chooser.showOpenDialog(null);
    6544  
    6545   }
    6546  
    6547   private Rectangle lastFeatureSettingsBounds = null;
    6548  
    6549   @Override
    6550   public void setFeatureSettingsGeometry(Rectangle bounds)
    6551   {
    6552   lastFeatureSettingsBounds = bounds;
    6553   }
    6554  
    6555   @Override
    6556   public Rectangle getFeatureSettingsGeometry()
    6557   {
    6558   return lastFeatureSettingsBounds;
    6559   }
    6560  
    6561   /**
    6562   * helper for jalview.bin.Commands and loader that sets annotation visibility in a single call
    6563   * @param showSSAnnotations
    6564   * @param showAnnotations
    6565   * @param hideTFrows
    6566   */
    6567   public void showOrHideAnnotations(boolean showSSAnnotations, boolean showAnnotations,
    6568   boolean hideTFrows)
    6569   {
    6570   setAnnotationsVisibility(showSSAnnotations, true, false);
    6571   setAnnotationsVisibility(showAnnotations, false, true);
    6572  
    6573   // show temperature factor annotations?
    6574   if (hideTFrows)
    6575   {
    6576   // do this better (annotation types?)
    6577   // TODO: JAL-4595
    6578   List hideThese = new ArrayList<>();
    6579   hideThese.add("Temperature Factor");
    6580   hideThese.add(AlphaFoldAnnotationRowBuilder.LABEL);
    6581   AlignmentUtils.showOrHideSequenceAnnotations(
    6582   getCurrentView().getAlignment(), hideThese, null, false,
    6583   false);
    6584   }
    6585   }
    6586  
    6587   }
    6588  
    6589   class PrintThread extends Thread
    6590   {
    6591   AlignmentPanel ap;
    6592  
    6593   public PrintThread(AlignmentPanel ap)
    6594   {
    6595   this.ap = ap;
    6596   }
    6597  
    6598   static PageFormat pf;
    6599  
    6600   @Override
    6601   public void run()
    6602   {
    6603   PrinterJob printJob = PrinterJob.getPrinterJob();
    6604  
    6605   if (pf != null)
    6606   {
    6607   printJob.setPrintable(ap, pf);
    6608   }
    6609   else
    6610   {
    6611   printJob.setPrintable(ap);
    6612   }
    6613  
    6614   if (printJob.printDialog())
    6615   {
    6616   try
    6617   {
    6618   printJob.print();
    6619   } catch (Exception PrintException)
    6620   {
    6621   PrintException.printStackTrace();
    6622   }
    6623   }
    6624   }
    6625   }