Clover icon

Coverage Report

  1. Project Clover database Wed Jan 7 2026 02:49:01 GMT
  2. Package jalview.gui

File AlignViewport.java

 

Coverage histogram

../../img/srcFileCovDistChart7.png
30% of files have more coverage

Code metrics

118
293
52
1
1,243
757
128
0.44
5.63
52
2.46

Classes

Class Line # Actions
AlignViewport 96 293 128
0.6328293763.3%
 

Contributing tests

This file is covered by 321 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.Container;
24    import java.awt.Dimension;
25    import java.awt.FlowLayout;
26    import java.awt.Font;
27    import java.awt.FontMetrics;
28    import java.awt.Rectangle;
29    import java.io.File;
30    import java.util.ArrayList;
31    import java.util.Hashtable;
32    import java.util.List;
33   
34    import javax.swing.JInternalFrame;
35   
36    import jalview.analysis.AlignmentUtils;
37    import jalview.analysis.AnnotationSorter.SequenceAnnotationOrder;
38    import jalview.api.AlignViewportI;
39    import jalview.api.AlignmentViewPanel;
40    import jalview.api.FeatureColourI;
41    import jalview.api.FeatureSettingsModelI;
42    import jalview.api.FeaturesDisplayedI;
43    import jalview.api.ViewStyleI;
44    import jalview.bin.Cache;
45    import jalview.bin.Console;
46    import jalview.commands.CommandI;
47    import jalview.datamodel.AlignedCodonFrame;
48    import jalview.datamodel.Alignment;
49    import jalview.datamodel.AlignmentI;
50    import jalview.datamodel.ColumnSelection;
51    import jalview.datamodel.ContactMatrixI;
52    import jalview.datamodel.HiddenColumns;
53    import jalview.datamodel.SearchResults;
54    import jalview.datamodel.SearchResultsI;
55    import jalview.datamodel.SequenceGroup;
56    import jalview.datamodel.SequenceI;
57    import jalview.io.AppletFormatAdapter;
58    import jalview.io.DataSourceType;
59    import jalview.io.FileFormatException;
60    import jalview.io.FileFormatI;
61    import jalview.io.FileFormats;
62    import jalview.io.FileLoader;
63    import jalview.io.IdentifyFile;
64    import jalview.datamodel.features.FeatureMatcherSetI;
65    import jalview.renderer.ResidueShader;
66    import jalview.schemes.ColourSchemeI;
67    import jalview.schemes.ColourSchemeProperty;
68    import jalview.schemes.ResidueColourScheme;
69    import jalview.schemes.UserColourScheme;
70    import jalview.structure.SelectionSource;
71    import jalview.structure.StructureSelectionManager;
72    import jalview.structure.VamsasSource;
73    import jalview.util.ColorUtils;
74    import jalview.util.MessageManager;
75    import jalview.viewmodel.AlignmentViewport;
76    import jalview.ws.params.AutoCalcSetting;
77   
78    import java.awt.Container;
79    import java.awt.Dimension;
80    import java.awt.Font;
81    import java.awt.FontMetrics;
82    import java.awt.Rectangle;
83    import java.util.ArrayList;
84    import java.util.Hashtable;
85    import java.util.Iterator;
86    import java.util.List;
87   
88    import javax.swing.JInternalFrame;
89   
90    /**
91    * DOCUMENT ME!
92    *
93    * @author $author$
94    * @version $Revision: 1.141 $
95    */
 
96    public class AlignViewport extends AlignmentViewport<AlignmentPanel>
97    implements SelectionSource
98    {
99    public final static int NO_SPLIT = 0;
100   
101    public final static int SPLIT_FRAME = 1;
102   
103    public final static int NEW_WINDOW = 2;
104   
105    Font font;
106   
107    boolean cursorMode = false;
108   
109    boolean antiAlias = false;
110   
111    private Rectangle explodedGeometry = null;
112   
113    private String viewName = null;
114   
115    /*
116    * Flag set true on the view that should 'gather' multiple views of the same
117    * sequence set id when a project is reloaded. Set false on all views when
118    * they are 'exploded' into separate windows. Set true on the current view
119    * when 'Gather' is performed, and also on the first tab when the first new
120    * view is created.
121    */
122    private boolean gatherViewsHere = false;
123   
124    private AnnotationColumnChooser annotationColumnSelectionState;
125   
126    /**
127    * Creates a new AlignViewport object.
128    *
129    * @param al
130    * alignment to view
131    */
 
132  82 toggle public AlignViewport(AlignmentI al)
133    {
134  82 super(al);
135  82 init();
136    }
137   
138    /**
139    * Create a new AlignViewport object with a specific sequence set ID
140    *
141    * @param al
142    * @param seqsetid
143    * (may be null - but potential for ambiguous constructor exception)
144    */
 
145  0 toggle public AlignViewport(AlignmentI al, String seqsetid)
146    {
147  0 this(al, seqsetid, null);
148    }
149   
 
150  0 toggle public AlignViewport(AlignmentI al, String seqsetid, String viewid)
151    {
152  0 super(al);
153  0 sequenceSetID = seqsetid;
154  0 viewId = viewid;
155    // TODO remove these once 2.4.VAMSAS release finished
156  0 if (seqsetid != null)
157    {
158  0 Console.debug(
159    "Setting viewport's sequence set id : " + sequenceSetID);
160    }
161  0 if (viewId != null)
162    {
163  0 Console.debug("Setting viewport's view id : " + viewId);
164    }
165  0 init();
166   
167    }
168   
169    /**
170    * Create a new AlignViewport with hidden regions
171    *
172    * @param al
173    * AlignmentI
174    * @param hiddenColumns
175    * ColumnSelection
176    */
 
177  36 toggle public AlignViewport(AlignmentI al, HiddenColumns hiddenColumns)
178    {
179  36 super(al);
180  36 if (hiddenColumns != null)
181    {
182  36 al.setHiddenColumns(hiddenColumns);
183    }
184  36 init();
185    }
186   
187    /**
188    * New viewport with hidden columns and an existing sequence set id
189    *
190    * @param al
191    * @param hiddenColumns
192    * @param seqsetid
193    * (may be null)
194    */
 
195  0 toggle public AlignViewport(AlignmentI al, HiddenColumns hiddenColumns,
196    String seqsetid)
197    {
198  0 this(al, hiddenColumns, seqsetid, null);
199    }
200   
201    /**
202    * New viewport with hidden columns and an existing sequence set id and viewid
203    *
204    * @param al
205    * @param hiddenColumns
206    * @param seqsetid
207    * (may be null)
208    * @param viewid
209    * (may be null)
210    */
 
211  477 toggle public AlignViewport(AlignmentI al, HiddenColumns hiddenColumns,
212    String seqsetid, String viewid)
213    {
214  477 super(al);
215  477 sequenceSetID = seqsetid;
216  477 viewId = viewid;
217    // TODO remove these once 2.4.VAMSAS release finished
218  477 if (seqsetid != null)
219    {
220  96 Console.debug(
221    "Setting viewport's sequence set id : " + sequenceSetID);
222    }
223  477 if (viewId != null)
224    {
225  86 Console.debug("Setting viewport's view id : " + viewId);
226    }
227   
228  477 if (hiddenColumns != null)
229    {
230  0 al.setHiddenColumns(hiddenColumns);
231    }
232  477 init();
233    }
234   
235    /**
236    * Apply any settings saved in user preferences
237    */
 
238  595 toggle private void applyViewProperties()
239    {
240  595 antiAlias = Cache.getDefault("ANTI_ALIAS", true);
241   
242  595 viewStyle.setShowJVSuffix(Cache.getDefault("SHOW_JVSUFFIX", true));
243  595 setShowAnnotation(Cache.getDefault("SHOW_ANNOTATIONS", true));
244   
245  595 setRightAlignIds(Cache.getDefault("RIGHT_ALIGN_IDS", false));
246  595 setCentreColumnLabels(Cache.getDefault("CENTRE_COLUMN_LABELS", false));
247  595 autoCalculateConsensusAndConservation = Cache.getDefault("AUTO_CALC_CONSENSUS", true);
248   
249  595 setPadGaps(Cache.getDefault("PAD_GAPS", true));
250  595 setShowNPFeats(Cache.getDefault("SHOW_NPFEATS_TOOLTIP", true));
251  595 setShowDBRefs(Cache.getDefault("SHOW_DBREFS_TOOLTIP", true));
252  595 viewStyle.setSeqNameItalics(Cache.getDefault("ID_ITALICS", true));
253  595 viewStyle.setWrapAlignment(Cache.getDefault("WRAP_ALIGNMENT", false));
254  595 viewStyle.setShowUnconserved(
255    Cache.getDefault("SHOW_UNCONSERVED", false));
256  595 sortByTree = Cache.getDefault("SORT_BY_TREE", false);
257  595 followSelection = Cache.getDefault("FOLLOW_SELECTIONS", true);
258  595 sortAnnotationsBy = SequenceAnnotationOrder
259    .valueOf(Cache.getDefault(Preferences.SORT_ANNOTATIONS,
260    SequenceAnnotationOrder.NONE.name()));
261  595 showAutocalculatedAbove = Cache
262    .getDefault(Preferences.SHOW_AUTOCALC_ABOVE, false);
263  595 viewStyle.setScaleProteinAsCdna(
264    Cache.getDefault(Preferences.SCALE_PROTEIN_TO_CDNA, true));
265  595 viewStyle.setShowStructureProvider(
266    Cache.getDefault(Preferences.SHOW_STRUC_PROVIDER, false));
267    }
268   
 
269  595 toggle void init()
270    {
271  595 applyViewProperties();
272   
273  595 String fontName = Cache.getDefault("FONT_NAME", "SansSerif");
274  595 String fontStyle = Cache.getDefault("FONT_STYLE", Font.PLAIN + "");
275  595 String fontSize = Cache.getDefault("FONT_SIZE", "10");
276   
277  595 int style = 0;
278   
279  595 if (fontStyle.equals("bold"))
280    {
281  0 style = 1;
282    }
283  595 else if (fontStyle.equals("italic"))
284    {
285  0 style = 2;
286    }
287   
288  595 setFont(new Font(fontName, style, Integer.parseInt(fontSize)), true);
289   
290  595 alignment.setGapCharacter(Cache.getDefault("GAP_SYMBOL", "-").charAt(0));
291   
292    // We must set conservation and consensus before setting colour,
293    // as Blosum and Clustal require this to be done
294  595 if (hconsensus == null && !isDataset)
295    {
296  595 if (!alignment.isNucleotide())
297    {
298  475 showConservation = Cache.getDefault("SHOW_CONSERVATION", true);
299  475 showQuality = Cache.getDefault("SHOW_QUALITY", true);
300  475 showGroupConservation = Cache.getDefault("SHOW_GROUP_CONSERVATION",
301    false);
302    }
303  595 showConsensusHistogram = Cache.getDefault("SHOW_CONSENSUS_HISTOGRAM",
304    true);
305  595 showSequenceLogo = Cache.getDefault("SHOW_CONSENSUS_LOGO", true);
306  595 normaliseSequenceLogo = Cache.getDefault("NORMALISE_CONSENSUS_LOGO",
307    false);
308    // for now, use consensus options for Information till it gets its own
309  595 setShowHMMSequenceLogo(showSequenceLogo);
310  595 setNormaliseHMMSequenceLogo(normaliseSequenceLogo);
311  595 setShowInformationHistogram(showConsensusHistogram);
312  595 showGroupConsensus = Cache.getDefault("SHOW_GROUP_CONSENSUS", false);
313  595 showConsensus = Cache.getDefault("SHOW_IDENTITY", true);
314   
315  595 showSSConsensus = Cache.getDefault("SHOW_SS_CONSENSUS", false);
316   
317  595 showOccupancy = Cache.getDefault(Preferences.SHOW_OCCUPANCY, true);
318    }
319  595 initAutoAnnotation();
320    // initInformation();
321   
322  595 String colourProperty = alignment.isNucleotide()
323    ? Preferences.DEFAULT_COLOUR_NUC
324    : Preferences.DEFAULT_COLOUR_PROT;
325  595 String schemeName = Cache.getProperty(colourProperty);
326  595 if (schemeName == null)
327    {
328    // only DEFAULT_COLOUR available in Jalview before 2.9
329  584 schemeName = Cache.getDefault(Preferences.DEFAULT_COLOUR,
330    ResidueColourScheme.NONE);
331    }
332  595 ColourSchemeI colourScheme = ColourSchemeProperty.getColourScheme(this,
333    alignment, schemeName);
334  595 residueShading = new ResidueShader(colourScheme);
335   
336  595 if (colourScheme instanceof UserColourScheme)
337    {
338  0 residueShading = new ResidueShader(
339    UserDefinedColours.loadDefaultColours());
340  0 residueShading.setThreshold(0, isIgnoreGapsConsensus());
341    }
342   
343  595 if (residueShading != null)
344    {
345  595 residueShading.setConsensus(hconsensus);
346  595 residueShading.setSSConsensusProfileMap(hSSConsensusProfileMap);
347    }
348  595 setColourAppliesToAllGroups(true);
349    }
350   
351   
352    boolean validCharWidth;
353   
354    /**
355    * {@inheritDoc}
356    */
 
357  796 toggle @Override
358    public void setFont(Font f, boolean setGrid)
359    {
360  796 font = f;
361   
362  796 Container c = new Container();
363   
364  796 if (setGrid)
365    {
366  646 FontMetrics fm = c.getFontMetrics(font);
367  646 int ww = fm.charWidth('M');
368  646 setCharHeight(fm.getHeight());
369  646 setCharWidth(ww);
370    }
371  796 viewStyle.setFontName(font.getName());
372  796 viewStyle.setFontStyle(font.getStyle());
373  796 viewStyle.setFontSize(font.getSize());
374   
375  796 validCharWidth = true;
376    }
377   
 
378  97 toggle @Override
379    public void setViewStyle(ViewStyleI settingsForView)
380    {
381  97 super.setViewStyle(settingsForView);
382  97 setFont(new Font(viewStyle.getFontName(), viewStyle.getFontStyle(),
383    viewStyle.getFontSize()), false);
384    }
385   
386    /**
387    * DOCUMENT ME!
388    *
389    * @return DOCUMENT ME!
390    */
 
391  36955 toggle public Font getFont()
392    {
393  36955 return font;
394    }
395   
396    /**
397    * DOCUMENT ME!
398    *
399    * @param align
400    * DOCUMENT ME!
401    */
 
402  907 toggle @Override
403    public void setAlignment(AlignmentI align)
404    {
405  907 replaceMappings(align);
406  907 super.setAlignment(align);
407    }
408   
409    /**
410    * Replace any codon mappings for this viewport with those for the given
411    * viewport
412    *
413    * @param align
414    */
 
415  917 toggle public void replaceMappings(AlignmentI align)
416    {
417   
418    /*
419    * Deregister current mappings (if any)
420    */
421  917 deregisterMappings();
422   
423    /*
424    * Register new mappings (if any)
425    */
426  917 if (align != null)
427    {
428  605 StructureSelectionManager ssm = StructureSelectionManager
429    .getStructureSelectionManager(Desktop.getInstance());
430  605 ssm.registerMappings(align.getCodonFrames());
431    }
432   
433    /*
434    * replace mappings on our alignment
435    */
436  917 if (alignment != null && align != null)
437    {
438  10 alignment.setCodonFrames(align.getCodonFrames());
439    }
440    }
441   
 
442  917 toggle protected void deregisterMappings()
443    {
444  917 AlignmentI al = getAlignment();
445  917 if (al != null)
446    {
447  322 List<AlignedCodonFrame> mappings = al.getCodonFrames();
448  322 if (mappings != null)
449    {
450  322 StructureSelectionManager ssm = StructureSelectionManager
451    .getStructureSelectionManager(Desktop.getInstance());
452  322 for (AlignedCodonFrame acf : mappings)
453    {
454  130 if (noReferencesTo(acf))
455    {
456  32 ssm.deregisterMapping(acf);
457    }
458    }
459    }
460    }
461    }
462   
463    /**
464    * DOCUMENT ME!
465    *
466    * @return DOCUMENT ME!
467    */
 
468  80 toggle @Override
469    public char getGapCharacter()
470    {
471  80 return getAlignment().getGapCharacter();
472    }
473   
474    /**
475    * DOCUMENT ME!
476    *
477    * @param gap
478    * DOCUMENT ME!
479    */
 
480  0 toggle public void setGapCharacter(char gap)
481    {
482  0 if (getAlignment() != null)
483    {
484  0 getAlignment().setGapCharacter(gap);
485    }
486    }
487   
488    /**
489    * get hash of undo and redo list for the alignment
490    *
491    * @return long[] { historyList.hashCode, redoList.hashCode };
492    */
 
493  1 toggle public long[] getUndoRedoHash()
494    {
495    // TODO: JAL-1126
496  1 if (historyList == null || redoList == null)
497    {
498  0 return new long[] { -1, -1 };
499    }
500  1 return new long[] { historyList.hashCode(), this.redoList.hashCode() };
501    }
502   
503    /**
504    * test if a particular set of hashcodes are different to the hashcodes for
505    * the undo and redo list.
506    *
507    * @param undoredo
508    * the stored set of hashcodes as returned by getUndoRedoHash
509    * @return true if the hashcodes differ (ie the alignment has been edited) or
510    * the stored hashcode array differs in size
511    */
 
512  0 toggle public boolean isUndoRedoHashModified(long[] undoredo)
513    {
514  0 if (undoredo == null)
515    {
516  0 return true;
517    }
518  0 long[] cstate = getUndoRedoHash();
519  0 if (cstate.length != undoredo.length)
520    {
521  0 return true;
522    }
523   
524  0 for (int i = 0; i < cstate.length; i++)
525    {
526  0 if (cstate[i] != undoredo[i])
527    {
528  0 return true;
529    }
530    }
531  0 return false;
532    }
533   
534    public boolean followSelection = true;
535   
536    /**
537    * @return true if view selection should always follow the selections
538    * broadcast by other selection sources
539    */
 
540  0 toggle public boolean getFollowSelection()
541    {
542  0 return followSelection;
543    }
544   
545    /**
546    * Send the current selection to be broadcast to any selection listeners.
547    */
 
548  23 toggle @Override
549    public void sendSelection()
550    {
551  23 jalview.structure.StructureSelectionManager
552    .getStructureSelectionManager(Desktop.getInstance())
553    .sendSelection(new SequenceGroup(getSelectionGroup()),
554    new ColumnSelection(getColumnSelection()),
555    new HiddenColumns(getAlignment().getHiddenColumns()),
556    this);
557    }
558   
 
559  20 toggle public boolean getSortByTree()
560    {
561  20 return sortByTree;
562    }
563   
 
564  3 toggle public void setSortByTree(boolean sort)
565    {
566  3 sortByTree = sort;
567    }
568   
569    /**
570    * Returns the (Desktop) instance of the StructureSelectionManager
571    */
 
572  3841 toggle @Override
573    public StructureSelectionManager getStructureSelectionManager()
574    {
575  3841 return StructureSelectionManager
576    .getStructureSelectionManager(Desktop.getInstance());
577    }
578   
579   
 
580  4966 toggle @Override
581    public boolean isNormaliseSequenceLogo()
582    {
583  4966 return normaliseSequenceLogo;
584    }
585   
 
586  96 toggle @Override
587    public void setNormaliseSequenceLogo(boolean state)
588    {
589  96 normaliseSequenceLogo = state;
590    }
591   
592    /**
593    *
594    * @return true if alignment characters should be displayed
595    */
 
596  21186 toggle @Override
597    public boolean isValidCharWidth()
598    {
599  21186 return validCharWidth;
600    }
601   
602   
603    private Hashtable<String, AutoCalcSetting> calcIdParams = new Hashtable<>();
604   
 
605  16 toggle public AutoCalcSetting getCalcIdSettingsFor(String calcId)
606    {
607  16 return calcIdParams.get(calcId);
608    }
609   
 
610  0 toggle public void setCalcIdSettingsFor(String calcId, AutoCalcSetting settings,
611    boolean needsUpdate)
612    {
613  0 calcIdParams.put(calcId, settings);
614    // TODO: create a restart list to trigger any calculations that need to be
615    // restarted after load
616    // calculator.getRegisteredWorkersOfClass(settings.getWorkerClass())
617  0 if (needsUpdate)
618    {
619  0 Console.debug("trigger update for " + calcId);
620    }
621    }
622   
623    /**
624    * Method called when another alignment's edit (or possibly other) command is
625    * broadcast to here.
626    *
627    * To allow for sequence mappings (e.g. protein to cDNA), we have to first
628    * 'unwind' the command on the source sequences (in simulation, not in fact),
629    * and then for each edit in turn:
630    * <ul>
631    * <li>compute the equivalent edit on the mapped sequences</li>
632    * <li>apply the mapped edit</li>
633    * <li>'apply' the source edit to the working copy of the source
634    * sequences</li>
635    * </ul>
636    *
637    * @param command
638    * @param undo
639    * @param ssm
640    */
 
641  0 toggle @Override
642    public void mirrorCommand(CommandI command, boolean undo,
643    StructureSelectionManager ssm, VamsasSource source)
644    {
645    /*
646    * Do nothing unless we are a 'complement' of the source. May replace this
647    * with direct calls not via SSM.
648    */
649  0 if (source instanceof AlignViewportI
650    && ((AlignViewportI) source).getCodingComplement() == this)
651    {
652    // ok to continue;
653    }
654    else
655    {
656  0 return;
657    }
658   
659  0 CommandI mappedCommand = ssm.mapCommand(command, undo, getAlignment(),
660    getGapCharacter());
661  0 if (mappedCommand != null)
662    {
663  0 AlignmentI[] views = getAlignPanel().alignFrame.getViewAlignments();
664  0 mappedCommand.doCommand(views);
665  0 getAlignPanel().alignmentChanged();
666    }
667    }
668   
669    /**
670    * Add the sequences from the given alignment to this viewport. Optionally,
671    * may give the user the option to open a new frame, or split panel, with cDNA
672    * and protein linked.
673    *
674    * @param toAdd
675    * @param title
676    */
 
677  19 toggle public void addAlignment(AlignmentI toAdd, String title)
678    {
679    // TODO: promote to AlignViewportI? applet CutAndPasteTransfer is different
680   
681    // JBPComment: title is a largely redundant parameter at the moment
682    // JBPComment: this really should be an 'insert/pre/append' controller
683    // JBPComment: but the DNA/Protein check makes it a bit more complex
684   
685    // refactored from FileLoader / CutAndPasteTransfer / SequenceFetcher with
686    // this comment:
687    // TODO: create undo object for this JAL-1101
688   
689    /*
690    * Ensure datasets are created for the new alignment as
691    * mappings operate on dataset sequences
692    */
693  19 toAdd.setDataset(null);
694   
695    /*
696    * Check if any added sequence could be the object of a mapping or
697    * cross-reference; if so, make the mapping explicit
698    */
699  19 getAlignment().realiseMappings(toAdd.getSequences());
700   
701    /*
702    * If any cDNA/protein mappings exist or can be made between the alignments,
703    * offer to open a split frame with linked alignments
704    */
705  19 if (Cache.getDefault(Preferences.ENABLE_SPLIT_FRAME, true))
706    {
707  19 if (AlignmentUtils.isMappable(toAdd, getAlignment()))
708    {
709  0 openLinkedAlignment(toAdd, title);
710  0 return;
711    }
712    }
713  19 addDataToAlignment(toAdd);
714    }
715   
716    /**
717    * adds sequences to this alignment
718    *
719    * @param toAdd
720    */
 
721  19 toggle void addDataToAlignment(AlignmentI toAdd)
722    {
723    // TODO: JAL-407 regardless of above - identical sequences (based on ID and
724    // provenance) should share the same dataset sequence
725   
726  19 AlignmentI al = getAlignment();
727  19 String gap = String.valueOf(al.getGapCharacter());
728  69 for (int i = 0; i < toAdd.getHeight(); i++)
729    {
730  50 SequenceI seq = toAdd.getSequenceAt(i);
731    /*
732    * experimental!
733    * - 'align' any mapped sequences as per existing
734    * e.g. cdna to genome, domain hit to protein sequence
735    * very experimental! (need a separate menu option for this)
736    * - only add mapped sequences ('select targets from a dataset')
737    */
738  50 if (true /*AlignmentUtils.alignSequenceAs(seq, al, gap, true, true)*/)
739    {
740  50 al.addSequence(seq);
741    }
742    }
743  19 for (ContactMatrixI cm : toAdd.getContactMaps())
744    {
745  0 al.addContactList(cm);
746    }
747  19 ranges.setEndSeq(getAlignment().getHeight() - 1); // BH 2019.04.18
748  19 notifyAlignment();
749    }
750   
751    /**
752    * Load a File into this AlignViewport attempting to detect format if not
753    * given or given as null.
754    *
755    * @param file
756    * @param format
757    */
 
758  0 toggle public void addFile(File file, FileFormatI format)
759    {
760  0 addFile(file, format, true);
761    }
762   
 
763  0 toggle public void addFile(File file, FileFormatI format, boolean async)
764    {
765  0 DataSourceType protocol = AppletFormatAdapter.checkProtocol(file);
766   
767  0 if (format == null)
768    {
769  0 try
770    {
771  0 format = new IdentifyFile().identify(file, protocol);
772    } catch (FileFormatException e1)
773    {
774  0 jalview.bin.Console.error("Unknown file format for '" + file + "'");
775    }
776    }
777  0 else if (FileFormats.getInstance().isIdentifiable(format))
778    {
779  0 try
780    {
781  0 format = new IdentifyFile().identify(file, protocol);
782    } catch (FileFormatException e)
783    {
784  0 jalview.bin.Console.error("Unknown file format for '" + file + "'",
785    e);
786    }
787    }
788   
789  0 new FileLoader().LoadFile(this, file, DataSourceType.FILE, format,
790    async);
791    }
792   
 
793  0 toggle public void addFile(File file)
794    {
795  0 addFile(file, null);
796    }
797   
798    /**
799    * Show a dialog with the option to open and link (cDNA <-> protein) as a new
800    * alignment, either as a standalone alignment or in a split frame. Returns
801    * true if the new alignment was opened, false if not, because the user
802    * declined the offer.
803    *
804    * @param al
805    * @param title
806    */
 
807  0 toggle protected void openLinkedAlignment(AlignmentI al, String title)
808    {
809  0 String[] options = new String[] { MessageManager.getString("action.no"),
810    MessageManager.getString("label.split_window")
811    + "(update new alignment)",
812    MessageManager.getString("label.new_window"),
813    MessageManager.getString(
814    "label.split_window") + "(update this alignment)",
815    MessageManager.getString(
816    "label.split_window") + "(leave both unchanged)" };
817  0 final String question = JvSwingUtils.wrapTooltip(true,
818    MessageManager.getString("label.open_split_window?"));
819  0 final AlignViewport us = this;
820   
821    /*
822    * options No, Split Window, New Window correspond to
823    * dialog responses 0, 1, 2 (even though JOptionPane shows them
824    * in reverse order)
825    */
826  0 JvOptionPane dialog = JvOptionPane.newOptionDialog(Desktop.getDesktopPane())
827    .setResponseHandler(NO_SPLIT, () -> {
828  0 addDataToAlignment(al);
829    }).setResponseHandler(1, () -> {
830    // Make a copy of this one to open it in a splitframe
831  0 openLinkedAlignmentAs(getAlignPanel().alignFrame,
832    new Alignment(getAlignment()),al, title, true, SPLIT_FRAME);
833    }).setResponseHandler(2, () -> {
834    // Make a copy of this one to open it in a splitframe
835  0 openLinkedAlignmentAs(getAlignPanel().alignFrame,
836    new Alignment(getAlignment()),al, title, false, SPLIT_FRAME);
837    }).setResponseHandler(3, () -> {
838  0 openLinkedAlignmentAs(getAlignPanel().alignFrame,
839    getAlignment(),al, title, true, NEW_WINDOW);
840    }).setResponseHandler(4, () -> {
841  0 openLinkedAlignmentAs(getAlignPanel().alignFrame,
842    getAlignment(), al, title, true, NO_SPLIT);
843    });
844  0 dialog.setLayout(new FlowLayout(FlowLayout.CENTER));
845  0 dialog.setPreferredSize(new Dimension(350,300));
846  0 dialog.showDialog(question,
847    MessageManager.getString("label.open_split_window"),
848    JvOptionPane.DEFAULT_OPTION, JvOptionPane.PLAIN_MESSAGE, null,
849    options, options[0]);
850    }
851   
852    /**
853    * called by CLI - always aligns al according to thisAlignment
854    * Open a split frame or a new window
855    *
856    * @param al
857    * @param title
858    * @param mode
859    * SPLIT_FRAME or NEW_WINDOW
860    */
 
861  0 toggle public static AlignFrame openLinkedAlignmentAs(AlignFrame thisFrame,
862    AlignmentI thisAlignment, AlignmentI al, String title, int mode)
863    {
864  0 return openLinkedAlignmentAs(thisFrame, thisAlignment, al, title, mode==SPLIT_FRAME, 1);
865    }
866   
867    /**
868    * Open a split frame or a new window given an existing AlignFrame, its alignment, and a new alignment to relate it to
869    *
870    * TODO: this shouldn't be a static method on alignViewport, but a controller/high-level op method.
871    *
872    * @param thisFrame the AlignFrame
873    * @param thisAlignment the AlignFrame's alignment
874    * @param al - alignment containing sequences to link to this alignment
875    * @param title
876    * @param mode - create splitframe
877    * @param alignAs - 0 - don't modify either alignment, 1 - align al according to us, 2- align us according to al
878    * @return alignFrame holding al
879    */
 
880  0 toggle public static AlignFrame openLinkedAlignmentAs(AlignFrame thisFrame, AlignmentI thisAlignment, AlignmentI al, String title,
881    boolean createSplitFrame, int alignAs)
882    {
883  0 if (!al.isNucleotide() && !thisAlignment.isNucleotide())
884    {
885    // link AA to 3di or other kind of 'alternative' 1:1 mapping alignment
886  0 if (al.getDataset()==null)
887    {
888  0 al.setDataset(thisAlignment.getDataset());
889    }
890  0 AlignmentUtils.map3diPeptideToProteinAligment(thisAlignment,al);
891  0 if (thisAlignment.getCodonFrames().isEmpty()) {thisAlignment.getCodonFrames().addAll(al.getCodonFrames()); }
892  0 else {al.getCodonFrames().addAll(thisAlignment.getCodonFrames()); };
893    }
894    else
895    {
896    // shouldn't we be merging dataset here ?
897    // always create dataset for imported alignment before doing anything else..
898  0 al.setDataset(null);
899    // link CODON triplets to Protein
900  0 AlignmentI protein = al.isNucleotide() ? thisAlignment : al;
901  0 final AlignmentI cdna = al.isNucleotide() ? al : thisAlignment;
902   
903    /*
904    * Map sequences. At least one should get mapped as we have already passed
905    * the test for 'mappability'. Any mappings made will be added to the
906    * protein alignment. Note creating dataset sequences on the new alignment
907    * is a pre-requisite for building mappings.
908    */
909  0 AlignmentUtils.mapProteinAlignmentToCdna(protein, cdna);
910    }
911   
912    /*
913    * Create the AlignFrame for the added alignment. If it is protein, mappings
914    * are registered with StructureSelectionManager as a side-effect.
915    */
916  0 AlignFrame newAlignFrame = new AlignFrame(al, AlignFrame.DEFAULT_WIDTH,
917    AlignFrame.DEFAULT_HEIGHT);
918  0 newAlignFrame.setTitle(title);
919  0 newAlignFrame.setStatus(MessageManager
920    .formatMessage("label.successfully_loaded_file", new Object[]
921    { title }));
922   
923    // TODO if we want this (e.g. to enable reload of the alignment from file),
924    // we will need to add parameters to the stack.
925    // if (!protocol.equals(DataSourceType.PASTE))
926    // {
927    // alignFrame.setFileName(file, format);
928    // }
929   
930  0 if (!createSplitFrame)
931    {
932  0 Desktop.addInternalFrame(newAlignFrame, title,
933    AlignFrame.DEFAULT_WIDTH, AlignFrame.DEFAULT_HEIGHT);
934    }
935   
936  0 try
937    {
938  0 newAlignFrame.setMaximum(Cache.getDefault("SHOW_FULLSCREEN", false));
939    } catch (java.beans.PropertyVetoException ex)
940    {
941    }
942   
943  0 if (createSplitFrame)
944    {
945  0 if (alignAs==1) {
946  0 al.alignAs(thisAlignment);
947    }
948  0 if (alignAs==2) {
949  0 thisAlignment.alignAs(al);
950    }
951  0 AlignmentI mapped = openSplitFrame(thisFrame, newAlignFrame, thisAlignment);
952    }
953  0 return newAlignFrame;
954    }
955   
956    /**
957    * Helper method to open a new SplitFrame holding linked dna and protein
958    * alignments.
959    *
960    * @param newAlignFrame
961    * containing a new alignment to be shown
962    * @param complement
963    * cdna/protein complement alignment to show in the other split half
964    * @return the protein alignment in the split frame
965    */
 
966  1 toggle static protected AlignmentI openSplitFrame(AlignFrame thisFrame,
967    AlignFrame newAlignFrame, AlignmentI complement)
968    {
969    /*
970    * Make a new frame with a copy of the alignment we are adding to. If this
971    * is protein, the mappings to cDNA will be registered with
972    * StructureSelectionManager as a side-effect.
973    */
974  1 AlignFrame copyMe = new AlignFrame(complement, AlignFrame.DEFAULT_WIDTH,
975    AlignFrame.DEFAULT_HEIGHT);
976  1 copyMe.setTitle(thisFrame.getTitle());
977   
978  1 AlignmentI al = newAlignFrame.viewport.getAlignment();
979  1 final AlignFrame proteinFrame = al.isNucleotide() ? copyMe
980    : newAlignFrame;
981  1 final AlignFrame cdnaFrame = al.isNucleotide() ? newAlignFrame : copyMe;
982  1 cdnaFrame.setVisible(true);
983  1 proteinFrame.setVisible(true);
984  1 String linkedTitle = MessageManager
985    .getString("label.linked_view_title");
986   
987    /*
988    * Open in split pane. DNA sequence above, protein below.
989    */
990  1 JInternalFrame splitFrame = new SplitFrame(cdnaFrame, proteinFrame);
991  1 Desktop.addInternalFrame(splitFrame, linkedTitle, -1, -1);
992  1 cdnaFrame.viewport.updateConsensus(cdnaFrame.alignPanel);
993  1 proteinFrame.viewport.updateConsensus(proteinFrame.alignPanel);
994   
995  1 return proteinFrame.viewport.getAlignment();
996    }
997   
 
998  7 toggle public AnnotationColumnChooser getAnnotationColumnSelectionState()
999    {
1000  7 return annotationColumnSelectionState;
1001    }
1002   
 
1003  1 toggle public void setAnnotationColumnSelectionState(
1004    AnnotationColumnChooser currentAnnotationColumnSelectionState)
1005    {
1006  1 this.annotationColumnSelectionState = currentAnnotationColumnSelectionState;
1007    }
1008   
 
1009  771 toggle @Override
1010    public void setIdWidth(int i)
1011    {
1012  771 super.setIdWidth(i);
1013  771 AlignmentPanel ap = getAlignPanel();
1014  771 if (ap != null)
1015    {
1016    // modify GUI elements to reflect geometry change
1017  771 Dimension idw = ap.getIdPanel().getIdCanvas().getPreferredSize();
1018  771 idw.width = i;
1019  771 ap.getIdPanel().getIdCanvas().setPreferredSize(idw);
1020    }
1021    }
1022   
 
1023  55 toggle public Rectangle getExplodedGeometry()
1024    {
1025  55 return explodedGeometry;
1026    }
1027   
 
1028  53 toggle public void setExplodedGeometry(Rectangle explodedPosition)
1029    {
1030  53 this.explodedGeometry = explodedPosition;
1031    }
1032   
 
1033  136 toggle public boolean isGatherViewsHere()
1034    {
1035  136 return gatherViewsHere;
1036    }
1037   
 
1038  173 toggle public void setGatherViewsHere(boolean gatherViewsHere)
1039    {
1040  173 this.gatherViewsHere = gatherViewsHere;
1041    }
1042   
1043    /**
1044    * If this viewport has a (Protein/cDNA) complement, then scroll the
1045    * complementary alignment to match this one.
1046    */
 
1047  612 toggle public void scrollComplementaryAlignment()
1048    {
1049    /*
1050    * Populate a SearchResults object with the mapped location to scroll to. If
1051    * there is no complement, or it is not following highlights, or no mapping
1052    * is found, the result will be empty.
1053    */
1054  612 SearchResultsI sr = new SearchResults();
1055  612 int verticalOffset = findComplementScrollTarget(sr);
1056  612 if (!sr.isEmpty())
1057    {
1058    // TODO would like next line without cast but needs more refactoring...
1059  2 final AlignmentPanel complementPanel = ((AlignViewport) getCodingComplement())
1060    .getAlignPanel();
1061  2 complementPanel.setToScrollComplementPanel(false);
1062  2 complementPanel.scrollToCentre(sr, verticalOffset);
1063  2 complementPanel.setToScrollComplementPanel(true);
1064    }
1065    }
1066   
1067    /**
1068    * Answers true if no alignment holds a reference to the given mapping
1069    *
1070    * @param acf
1071    * @return
1072    */
 
1073  130 toggle protected boolean noReferencesTo(AlignedCodonFrame acf)
1074    {
1075  130 AlignFrame[] frames = Desktop.getDesktopAlignFrames();
1076  130 if (frames == null)
1077    {
1078  3 return true;
1079    }
1080  127 for (AlignFrame af : frames)
1081    {
1082  573 if (!af.isClosed())
1083    {
1084  573 for (AlignmentViewPanel ap : af.getAlignPanels())
1085    {
1086  573 AlignmentI al = ap.getAlignment();
1087  573 if (al != null && al.getCodonFrames().contains(acf))
1088    {
1089  98 return false;
1090    }
1091    }
1092    }
1093    }
1094  29 return true;
1095    }
1096   
1097    /**
1098    * Applies the supplied feature settings descriptor to currently known
1099    * features. This supports an 'initial configuration' of feature colouring
1100    * based on a preset or user favourite. This may then be modified in the usual
1101    * way using the Feature Settings dialogue.
1102    *
1103    * @param featureSettings
1104    */
 
1105  65 toggle @Override
1106    public void applyFeaturesStyle(FeatureSettingsModelI featureSettings)
1107    {
1108  65 transferFeaturesStyles(featureSettings, false);
1109    }
1110   
1111    /**
1112    * Applies the supplied feature settings descriptor to currently known
1113    * features. This supports an 'initial configuration' of feature colouring
1114    * based on a preset or user favourite. This may then be modified in the usual
1115    * way using the Feature Settings dialogue.
1116    *
1117    * @param featureSettings
1118    */
 
1119  2 toggle @Override
1120    public void mergeFeaturesStyle(FeatureSettingsModelI featureSettings)
1121    {
1122  2 transferFeaturesStyles(featureSettings, true);
1123    }
1124   
1125    /**
1126    * when mergeOnly is set, then group and feature visibility or feature colours
1127    * are not modified for features and groups already known to the feature
1128    * renderer. Feature ordering is always adjusted, and transparency is always
1129    * set regardless.
1130    *
1131    * @param featureSettings
1132    * @param mergeOnly
1133    */
 
1134  67 toggle private void transferFeaturesStyles(FeatureSettingsModelI featureSettings,
1135    boolean mergeOnly)
1136    {
1137  67 if (featureSettings == null)
1138    {
1139  0 return;
1140    }
1141  67 FeatureRenderer fr = getAlignPanel().getSeqPanel().seqCanvas
1142    .getFeatureRenderer();
1143  67 List<String> origRenderOrder = new ArrayList(),
1144    origGroups = new ArrayList();
1145    // preserve original render order - allows differentiation between user
1146    // configured colours and autogenerated ones
1147  67 origRenderOrder.addAll(fr.getRenderOrder());
1148  67 origGroups.addAll(fr.getFeatureGroups());
1149   
1150  67 fr.findAllFeatures(true);
1151  67 List<String> renderOrder = fr.getRenderOrder();
1152  67 FeaturesDisplayedI displayed = fr.getFeaturesDisplayed();
1153  67 if (!mergeOnly)
1154    {
1155    // only clear displayed features if we are mergeing
1156  65 displayed.clear();
1157    }
1158    // TODO this clears displayed.featuresRegistered - do we care?
1159    //
1160    // JAL-3330 - JBP - yes we do - calling applyFeatureStyle to a view where
1161    // feature visibility has already been configured is not very friendly
1162    /*
1163    * set feature colour if specified by feature settings
1164    * set visibility of all features
1165    */
1166  67 for (String type : renderOrder)
1167    {
1168  266 FeatureColourI preferredColour = featureSettings
1169    .getFeatureColour(type);
1170  266 FeatureMatcherSetI preferredFilters = featureSettings
1171    .getFeatureFilters(type);
1172  266 FeatureColourI origColour = fr.getFeatureStyle(type);
1173  266 if (!mergeOnly || (!origRenderOrder.contains(type)
1174    || origColour == null
1175    || (!origColour.isGraduatedColour()
1176    && origColour.getColour() != null
1177    && origColour.getColour().equals(
1178    ColorUtils.createColourFromName(type)))))
1179    {
1180    // if we are merging, only update if there wasn't already a colour
1181    // defined for
1182    // this type
1183  262 if (preferredColour != null)
1184    {
1185  12 fr.setColour(type, preferredColour);
1186    }
1187  262 if (preferredFilters != null
1188    && (!mergeOnly || fr.getFeatureFilter(type) != null))
1189    {
1190  0 fr.setFeatureFilter(type, preferredFilters);
1191    }
1192  262 if (featureSettings.isFeatureDisplayed(type))
1193    {
1194  0 displayed.setVisible(type);
1195    }
1196  262 else if (featureSettings.isFeatureHidden(type))
1197    {
1198  63 displayed.setHidden(type);
1199    }
1200    }
1201    }
1202   
1203    /*
1204    * set visibility of feature groups
1205    */
1206  67 for (String group : fr.getFeatureGroups())
1207    {
1208  142 if (!mergeOnly || !origGroups.contains(group))
1209    {
1210    // when merging, display groups only if the aren't already marked as not
1211    // visible
1212  134 fr.setGroupVisibility(group,
1213    featureSettings.isGroupDisplayed(group));
1214    }
1215    }
1216   
1217    /*
1218    * order the features
1219    */
1220  67 if (featureSettings.optimiseOrder())
1221    {
1222    // TODO not supported (yet?)
1223    }
1224    else
1225    {
1226  67 fr.orderFeatures(featureSettings);
1227    }
1228  67 fr.setTransparency(featureSettings.getTransparency());
1229   
1230  67 fr.notifyFeaturesChanged();
1231    }
1232   
 
1233  5804 toggle public String getViewName()
1234    {
1235  5804 return viewName;
1236    }
1237   
 
1238  78 toggle public void setViewName(String viewName)
1239    {
1240  78 this.viewName = viewName;
1241    }
1242   
1243    }