Clover icon

Coverage Report

  1. Project Clover database Thu Nov 27 2025 16:51:35 GMT
  2. Package jalview.gui

File AlignFrame.java

 

Coverage histogram

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

Code metrics

694
1,944
264
2
6,615
4,743
756
0.39
7.36
132
2.86

Classes

Class Line # Actions
AlignFrame 181 1,935 751
0.3297540833%
PrintThread 6579 9 5
0.00%
 

Contributing tests

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