Clover icon

Coverage Report

  1. Project Clover database Mon Nov 18 2024 09:38:20 GMT
  2. Package jalview.gui

File AlignViewport.java

 

Coverage histogram

../../img/srcFileCovDistChart6.png
27% of files have more coverage

Code metrics

122
292
52
1
1,217
739
129
0.44
5.62
52
2.48

Classes

Class Line # Actions
AlignViewport 83 292 129
0.603004360.3%
 

Contributing tests

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