Clover icon

Coverage Report

  1. Project Clover database Thu Dec 4 2025 16:11:35 GMT
  2. Package jalview.gui

File AlignViewport.java

 

Coverage histogram

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

Code metrics

108
274
51
1
1,186
718
121
0.44
5.37
51
2.37

Classes

Class Line # Actions
AlignViewport 95 274 121
0.672055467.2%
 

Contributing tests

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