Clover icon

Coverage Report

  1. Project Clover database Thu Nov 27 2025 17:07:57 GMT
  2. Package jalview.gui

File AlignFrame.java

 

Coverage histogram

../../img/srcFileCovDistChart4.png
49% of files have more coverage

Code metrics

694
1,940
264
2
6,609
4,739
756
0.39
7.35
132
2.86

Classes

Class Line # Actions
AlignFrame 181 1,931 751
0.3270898532.7%
PrintThread 6573 9 5
0.00%
 

Contributing tests

This file is covered by 300 tests. .

Source view

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