Merge branch 'develop' into JAL-1640_stylesettings
[jalview.git] / src / jalview / gui / AlignViewport.java
1 /*
2  * Jalview - A Sequence Alignment Editor and Viewer (Version 2.8.2)
3  * Copyright (C) 2014 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 /*
22  * Jalview - A Sequence Alignment Editor and Viewer
23  * Copyright (C) 2007 AM Waterhouse, J Procter, G Barton, M Clamp, S Searle
24  *
25  * This program is free software; you can redistribute it and/or
26  * modify it under the terms of the GNU General Public License
27  * as published by the Free Software Foundation; either version 2
28  * of the License, or (at your option) any later version.
29  *
30  * This program is distributed in the hope that it will be useful,
31  * but WITHOUT ANY WARRANTY; without even the implied warranty of
32  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
33  * GNU General Public License for more details.
34  *
35  * You should have received a copy of the GNU General Public License
36  * along with this program; if not, write to the Free Software
37  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
38  */
39 package jalview.gui;
40
41 import jalview.analysis.AnnotationSorter.SequenceAnnotationOrder;
42 import jalview.analysis.NJTree;
43 import jalview.api.AlignViewportI;
44 import jalview.api.ViewStyleI;
45 import jalview.bin.Cache;
46 import jalview.commands.CommandI;
47 import jalview.datamodel.AlignmentI;
48 import jalview.datamodel.ColumnSelection;
49 import jalview.datamodel.PDBEntry;
50 import jalview.datamodel.Sequence;
51 import jalview.datamodel.SequenceGroup;
52 import jalview.datamodel.SequenceI;
53 import jalview.schemes.ColourSchemeProperty;
54 import jalview.schemes.UserColourScheme;
55 import jalview.structure.SelectionSource;
56 import jalview.structure.StructureSelectionManager;
57 import jalview.structure.VamsasSource;
58 import jalview.viewmodel.AlignmentViewport;
59 import jalview.ws.params.AutoCalcSetting;
60
61 import java.awt.Container;
62 import java.awt.Dimension;
63 import java.awt.Font;
64 import java.awt.Rectangle;
65 import java.util.ArrayList;
66 import java.util.Hashtable;
67 import java.util.Stack;
68 import java.util.Vector;
69
70 /**
71  * DOCUMENT ME!
72  * 
73  * @author $author$
74  * @version $Revision: 1.141 $
75  */
76 public class AlignViewport extends AlignmentViewport implements
77         SelectionSource, VamsasSource, AlignViewportI
78 {
79   int startRes;
80
81   int endRes;
82
83   int startSeq;
84
85   int endSeq;
86
87
88   SequenceAnnotationOrder sortAnnotationsBy = null;
89
90   Font font;
91
92   NJTree currentTree = null;
93
94   boolean cursorMode = false;
95
96   boolean antiAlias = false;
97
98   Rectangle explodedPosition;
99
100   String viewName;
101
102   boolean gatherViewsHere = false;
103
104   Stack<CommandI> historyList = new Stack<CommandI>();
105
106   Stack<CommandI> redoList = new Stack<CommandI>();
107
108   private AnnotationColumnChooser annotationColumnSelectionState;
109   /**
110    * Creates a new AlignViewport object.
111    * 
112    * @param al
113    *          alignment to view
114    */
115   public AlignViewport(AlignmentI al)
116   {
117     setAlignment(al);
118     init();
119   }
120
121   /**
122    * Create a new AlignViewport object with a specific sequence set ID
123    * 
124    * @param al
125    * @param seqsetid
126    *          (may be null - but potential for ambiguous constructor exception)
127    */
128   public AlignViewport(AlignmentI al, String seqsetid)
129   {
130     this(al, seqsetid, null);
131   }
132
133   public AlignViewport(AlignmentI al, String seqsetid, String viewid)
134   {
135     sequenceSetID = seqsetid;
136     viewId = viewid;
137     // TODO remove these once 2.4.VAMSAS release finished
138     if (Cache.log != null && Cache.log.isDebugEnabled() && seqsetid != null)
139     {
140       Cache.log.debug("Setting viewport's sequence set id : "
141               + sequenceSetID);
142     }
143     if (Cache.log != null && Cache.log.isDebugEnabled() && viewId != null)
144     {
145       Cache.log.debug("Setting viewport's view id : " + viewId);
146     }
147     setAlignment(al);
148     init();
149   }
150
151   /**
152    * Create a new AlignViewport with hidden regions
153    * 
154    * @param al
155    *          AlignmentI
156    * @param hiddenColumns
157    *          ColumnSelection
158    */
159   public AlignViewport(AlignmentI al, ColumnSelection hiddenColumns)
160   {
161     setAlignment(al);
162     if (hiddenColumns != null)
163     {
164       colSel = hiddenColumns;
165     }
166     init();
167   }
168
169   /**
170    * New viewport with hidden columns and an existing sequence set id
171    * 
172    * @param al
173    * @param hiddenColumns
174    * @param seqsetid
175    *          (may be null)
176    */
177   public AlignViewport(AlignmentI al, ColumnSelection hiddenColumns,
178           String seqsetid)
179   {
180     this(al, hiddenColumns, seqsetid, null);
181   }
182
183   /**
184    * New viewport with hidden columns and an existing sequence set id and viewid
185    * 
186    * @param al
187    * @param hiddenColumns
188    * @param seqsetid
189    *          (may be null)
190    * @param viewid
191    *          (may be null)
192    */
193   public AlignViewport(AlignmentI al, ColumnSelection hiddenColumns,
194           String seqsetid, String viewid)
195   {
196     sequenceSetID = seqsetid;
197     viewId = viewid;
198     // TODO remove these once 2.4.VAMSAS release finished
199     if (Cache.log != null && Cache.log.isDebugEnabled() && seqsetid != null)
200     {
201       Cache.log.debug("Setting viewport's sequence set id : "
202               + sequenceSetID);
203     }
204     if (Cache.log != null && Cache.log.isDebugEnabled() && viewId != null)
205     {
206       Cache.log.debug("Setting viewport's view id : " + viewId);
207     }
208     setAlignment(al);
209     if (hiddenColumns != null)
210     {
211       colSel = hiddenColumns;
212     }
213     init();
214   }
215
216   private void applyViewProperties()
217   {
218     antiAlias = Cache.getDefault("ANTI_ALIAS", false);
219
220     viewStyle.setShowJVSuffix(Cache.getDefault("SHOW_JVSUFFIX", true));
221     setShowAnnotation(Cache.getDefault("SHOW_ANNOTATIONS", true));
222
223     setRightAlignIds(Cache.getDefault("RIGHT_ALIGN_IDS", false));
224     setCentreColumnLabels(Cache.getDefault("CENTRE_COLUMN_LABELS", false));
225     autoCalculateConsensus = Cache.getDefault("AUTO_CALC_CONSENSUS", true);
226
227     setPadGaps(Cache.getDefault("PAD_GAPS", true));
228     setShowNPFeats(Cache.getDefault("SHOW_NPFEATS_TOOLTIP", true));
229     setShowDBRefs(Cache.getDefault("SHOW_DBREFS_TOOLTIP", true));
230     viewStyle.setSeqNameItalics(Cache.getDefault("ID_ITALICS", true));
231     viewStyle.setWrapAlignment(Cache.getDefault("WRAP_ALIGNMENT", false));
232     viewStyle.setShowUnconserved(Cache
233             .getDefault("SHOW_UNCONSERVED", false));
234     sortByTree = Cache.getDefault("SORT_BY_TREE", false);
235     followSelection = Cache.getDefault("FOLLOW_SELECTIONS", true);
236     sortAnnotationsBy = SequenceAnnotationOrder.valueOf(Cache.getDefault(
237             Preferences.SORT_ANNOTATIONS,
238             SequenceAnnotationOrder.NONE.name()));
239     showAutocalculatedAbove = Cache.getDefault(
240             Preferences.SHOW_AUTOCALC_ABOVE, false);
241
242   }
243
244   void init()
245   {
246     this.startRes = 0;
247     this.endRes = alignment.getWidth() - 1;
248     this.startSeq = 0;
249     this.endSeq = alignment.getHeight() - 1;
250     applyViewProperties();
251
252     String fontName = Cache.getDefault("FONT_NAME", "SansSerif");
253     String fontStyle = Cache.getDefault("FONT_STYLE", Font.PLAIN + "");
254     String fontSize = Cache.getDefault("FONT_SIZE", "10");
255
256     int style = 0;
257
258     if (fontStyle.equals("bold"))
259     {
260       style = 1;
261     }
262     else if (fontStyle.equals("italic"))
263     {
264       style = 2;
265     }
266
267     setFont(new Font(fontName, style, Integer.parseInt(fontSize)));
268
269     alignment
270             .setGapCharacter(Cache.getDefault("GAP_SYMBOL", "-").charAt(0));
271
272     // We must set conservation and consensus before setting colour,
273     // as Blosum and Clustal require this to be done
274     if (hconsensus == null && !isDataset)
275     {
276       if (!alignment.isNucleotide())
277       {
278         showConservation = Cache.getDefault("SHOW_CONSERVATION", true);
279         showQuality = Cache.getDefault("SHOW_QUALITY", true);
280         showGroupConservation = Cache.getDefault("SHOW_GROUP_CONSERVATION",
281                 false);
282       }
283       showConsensusHistogram = Cache.getDefault("SHOW_CONSENSUS_HISTOGRAM",
284               true);
285       showSequenceLogo = Cache.getDefault("SHOW_CONSENSUS_LOGO", false);
286       normaliseSequenceLogo = Cache.getDefault("NORMALISE_CONSENSUS_LOGO",
287               false);
288       showGroupConsensus = Cache.getDefault("SHOW_GROUP_CONSENSUS", false);
289       showConsensus = Cache.getDefault("SHOW_IDENTITY", true);
290     }
291     initAutoAnnotation();
292     if (jalview.bin.Cache.getProperty("DEFAULT_COLOUR") != null)
293     {
294       globalColourScheme = ColourSchemeProperty.getColour(alignment,
295               jalview.bin.Cache.getProperty("DEFAULT_COLOUR"));
296
297       if (globalColourScheme instanceof UserColourScheme)
298       {
299         globalColourScheme = UserDefinedColours.loadDefaultColours();
300         ((UserColourScheme) globalColourScheme).setThreshold(0,
301                 isIgnoreGapsConsensus());
302       }
303
304       if (globalColourScheme != null)
305       {
306         globalColourScheme.setConsensus(hconsensus);
307       }
308     }
309   }
310
311   /**
312    * get the consensus sequence as displayed under the PID consensus annotation
313    * row.
314    * 
315    * @return consensus sequence as a new sequence object
316    */
317   public SequenceI getConsensusSeq()
318   {
319     if (consensus == null)
320     {
321       updateConsensus(null);
322     }
323     if (consensus == null)
324     {
325       return null;
326     }
327     StringBuffer seqs = new StringBuffer();
328     for (int i = 0; i < consensus.annotations.length; i++)
329     {
330       if (consensus.annotations[i] != null)
331       {
332         if (consensus.annotations[i].description.charAt(0) == '[')
333         {
334           seqs.append(consensus.annotations[i].description.charAt(1));
335         }
336         else
337         {
338           seqs.append(consensus.annotations[i].displayCharacter);
339         }
340       }
341     }
342
343     SequenceI sq = new Sequence("Consensus", seqs.toString());
344     sq.setDescription("Percentage Identity Consensus "
345             + ((ignoreGapsInConsensusCalculation) ? " without gaps" : ""));
346     return sq;
347   }
348
349   /**
350    * DOCUMENT ME!
351    * 
352    * @return DOCUMENT ME!
353    */
354   public int getStartRes()
355   {
356     return startRes;
357   }
358
359   /**
360    * DOCUMENT ME!
361    * 
362    * @return DOCUMENT ME!
363    */
364   public int getEndRes()
365   {
366     return endRes;
367   }
368
369   /**
370    * DOCUMENT ME!
371    * 
372    * @return DOCUMENT ME!
373    */
374   public int getStartSeq()
375   {
376     return startSeq;
377   }
378
379   /**
380    * DOCUMENT ME!
381    * 
382    * @param res
383    *          DOCUMENT ME!
384    */
385   public void setStartRes(int res)
386   {
387     this.startRes = res;
388   }
389
390   /**
391    * DOCUMENT ME!
392    * 
393    * @param seq
394    *          DOCUMENT ME!
395    */
396   public void setStartSeq(int seq)
397   {
398     this.startSeq = seq;
399   }
400
401   /**
402    * DOCUMENT ME!
403    * 
404    * @param res
405    *          DOCUMENT ME!
406    */
407   public void setEndRes(int res)
408   {
409     if (res > (alignment.getWidth() - 1))
410     {
411       // log.System.out.println(" Corrected res from " + res + " to maximum " +
412       // (alignment.getWidth()-1));
413       res = alignment.getWidth() - 1;
414     }
415
416     if (res < 0)
417     {
418       res = 0;
419     }
420
421     this.endRes = res;
422   }
423
424   /**
425    * DOCUMENT ME!
426    * 
427    * @param seq
428    *          DOCUMENT ME!
429    */
430   public void setEndSeq(int seq)
431   {
432     if (seq > alignment.getHeight())
433     {
434       seq = alignment.getHeight();
435     }
436
437     if (seq < 0)
438     {
439       seq = 0;
440     }
441
442     this.endSeq = seq;
443   }
444
445   /**
446    * DOCUMENT ME!
447    * 
448    * @return DOCUMENT ME!
449    */
450   public int getEndSeq()
451   {
452     return endSeq;
453   }
454
455   boolean validCharWidth;
456
457   /**
458    * DOCUMENT ME!
459    * 
460    * @param f
461    *          DOCUMENT ME!
462    */
463   public void setFont(Font f)
464   {
465     font = f;
466
467     Container c = new Container();
468
469     java.awt.FontMetrics fm = c.getFontMetrics(font);
470     int w = viewStyle.getCharWidth(), ww = fm.charWidth('M'), h = viewStyle
471             .getCharHeight();
472     // only update width/height if the new font won't fit
473     if (h < fm.getHeight())
474     {
475       setCharHeight(fm.getHeight());
476     }
477     if (w < ww)
478     {
479       setCharWidth(ww);
480     }
481     viewStyle.setFontName(font.getName());
482     viewStyle.setFontStyle(font.getStyle());
483     viewStyle.setFontSize(font.getSize());
484
485     validCharWidth = true;
486   }
487
488   @Override
489   public void setViewStyle(ViewStyleI settingsForView)
490   {
491     super.setViewStyle(settingsForView);
492     setFont(new Font(viewStyle.getFontName(), viewStyle.getFontStyle(),
493             viewStyle.getFontSize()));
494
495   }
496   /**
497    * DOCUMENT ME!
498    * 
499    * @return DOCUMENT ME!
500    */
501   public Font getFont()
502   {
503     return font;
504   }
505
506   /**
507    * DOCUMENT ME!
508    * 
509    * @param align
510    *          DOCUMENT ME!
511    */
512   public void setAlignment(AlignmentI align)
513   {
514     if (alignment != null && alignment.getCodonFrames() != null)
515     {
516       StructureSelectionManager.getStructureSelectionManager(
517               Desktop.instance).removeMappings(alignment.getCodonFrames());
518     }
519     this.alignment = align;
520     if (alignment != null && alignment.getCodonFrames() != null)
521     {
522       StructureSelectionManager.getStructureSelectionManager(
523               Desktop.instance).addMappings(alignment.getCodonFrames());
524     }
525   }
526
527   /**
528    * DOCUMENT ME!
529    * 
530    * @return DOCUMENT ME!
531    */
532   public char getGapCharacter()
533   {
534     return getAlignment().getGapCharacter();
535   }
536
537   /**
538    * DOCUMENT ME!
539    * 
540    * @param gap
541    *          DOCUMENT ME!
542    */
543   public void setGapCharacter(char gap)
544   {
545     if (getAlignment() != null)
546     {
547       getAlignment().setGapCharacter(gap);
548     }
549   }
550
551   /**
552    * DOCUMENT ME!
553    * 
554    * @return DOCUMENT ME!
555    */
556   public ColumnSelection getColumnSelection()
557   {
558     return colSel;
559   }
560
561   /**
562    * DOCUMENT ME!
563    * 
564    * @param tree
565    *          DOCUMENT ME!
566    */
567   public void setCurrentTree(NJTree tree)
568   {
569     currentTree = tree;
570   }
571
572   /**
573    * DOCUMENT ME!
574    * 
575    * @return DOCUMENT ME!
576    */
577   public NJTree getCurrentTree()
578   {
579     return currentTree;
580   }
581
582   /**
583    * returns the visible column regions of the alignment
584    * 
585    * @param selectedRegionOnly
586    *          true to just return the contigs intersecting with the selected
587    *          area
588    * @return
589    */
590   public int[] getViewAsVisibleContigs(boolean selectedRegionOnly)
591   {
592     int[] viscontigs = null;
593     int start = 0, end = 0;
594     if (selectedRegionOnly && selectionGroup != null)
595     {
596       start = selectionGroup.getStartRes();
597       end = selectionGroup.getEndRes() + 1;
598     }
599     else
600     {
601       end = alignment.getWidth();
602     }
603     viscontigs = colSel.getVisibleContigs(start, end);
604     return viscontigs;
605   }
606
607   /**
608    * get hash of undo and redo list for the alignment
609    * 
610    * @return long[] { historyList.hashCode, redoList.hashCode };
611    */
612   public long[] getUndoRedoHash()
613   {
614     // TODO: JAL-1126
615     if (historyList == null || redoList == null)
616     {
617       return new long[]
618       { -1, -1 };
619     }
620     return new long[]
621     { historyList.hashCode(), this.redoList.hashCode() };
622   }
623
624   /**
625    * test if a particular set of hashcodes are different to the hashcodes for
626    * the undo and redo list.
627    * 
628    * @param undoredo
629    *          the stored set of hashcodes as returned by getUndoRedoHash
630    * @return true if the hashcodes differ (ie the alignment has been edited) or
631    *         the stored hashcode array differs in size
632    */
633   public boolean isUndoRedoHashModified(long[] undoredo)
634   {
635     if (undoredo == null)
636     {
637       return true;
638     }
639     long[] cstate = getUndoRedoHash();
640     if (cstate.length != undoredo.length)
641     {
642       return true;
643     }
644
645     for (int i = 0; i < cstate.length; i++)
646     {
647       if (cstate[i] != undoredo[i])
648       {
649         return true;
650       }
651     }
652     return false;
653   }
654
655   /**
656    * when set, view will scroll to show the highlighted position
657    */
658   public boolean followHighlight = true;
659
660   /**
661    * @return true if view should scroll to show the highlighted region of a
662    *         sequence
663    * @return
664    */
665   public boolean getFollowHighlight()
666   {
667     return followHighlight;
668   }
669
670   public boolean followSelection = true;
671
672   /**
673    * @return true if view selection should always follow the selections
674    *         broadcast by other selection sources
675    */
676   public boolean getFollowSelection()
677   {
678     return followSelection;
679   }
680
681   public void sendSelection()
682   {
683     jalview.structure.StructureSelectionManager
684             .getStructureSelectionManager(Desktop.instance).sendSelection(
685                     new SequenceGroup(getSelectionGroup()),
686                     new ColumnSelection(getColumnSelection()), this);
687   }
688
689   /**
690    * return the alignPanel containing the given viewport. Use this to get the
691    * components currently handling the given viewport.
692    * 
693    * @param av
694    * @return null or an alignPanel guaranteed to have non-null alignFrame
695    *         reference
696    */
697   public AlignmentPanel getAlignPanel()
698   {
699     AlignmentPanel[] aps = PaintRefresher.getAssociatedPanels(this
700             .getSequenceSetId());
701     AlignmentPanel ap = null;
702     for (int p = 0; aps != null && p < aps.length; p++)
703     {
704       if (aps[p].av == this)
705       {
706         return aps[p];
707       }
708     }
709     return null;
710   }
711
712   public boolean getSortByTree()
713   {
714     return sortByTree;
715   }
716
717   public void setSortByTree(boolean sort)
718   {
719     sortByTree = sort;
720   }
721
722   /**
723    * synthesize a column selection if none exists so it covers the given
724    * selection group. if wholewidth is false, no column selection is made if the
725    * selection group covers the whole alignment width.
726    * 
727    * @param sg
728    * @param wholewidth
729    */
730   public void expandColSelection(SequenceGroup sg, boolean wholewidth)
731   {
732     int sgs, sge;
733     if (sg != null
734             && (sgs = sg.getStartRes()) >= 0
735             && sg.getStartRes() <= (sge = sg.getEndRes())
736             && (colSel == null || colSel.getSelected() == null || colSel
737                     .getSelected().size() == 0))
738     {
739       if (!wholewidth && alignment.getWidth() == (1 + sge - sgs))
740       {
741         // do nothing
742         return;
743       }
744       if (colSel == null)
745       {
746         colSel = new ColumnSelection();
747       }
748       for (int cspos = sg.getStartRes(); cspos <= sg.getEndRes(); cspos++)
749       {
750         colSel.addElement(cspos);
751       }
752     }
753   }
754
755   public StructureSelectionManager getStructureSelectionManager()
756   {
757     return StructureSelectionManager
758             .getStructureSelectionManager(Desktop.instance);
759   }
760
761   /**
762    * 
763    * @param pdbEntries
764    * @return a series of SequenceI arrays, one for each PDBEntry, listing which
765    *         sequence in the alignment holds a reference to it
766    */
767   public SequenceI[][] collateForPDB(PDBEntry[] pdbEntries)
768   {
769     ArrayList<SequenceI[]> seqvectors = new ArrayList<SequenceI[]>();
770     for (PDBEntry pdb : pdbEntries)
771     {
772       ArrayList<SequenceI> seqs = new ArrayList<SequenceI>();
773       for (int i = 0; i < alignment.getHeight(); i++)
774       {
775         Vector pdbs = alignment.getSequenceAt(i).getDatasetSequence()
776                 .getPDBId();
777         if (pdbs == null)
778         {
779           continue;
780         }
781         SequenceI sq;
782         for (int p = 0; p < pdbs.size(); p++)
783         {
784           PDBEntry p1 = (PDBEntry) pdbs.elementAt(p);
785           if (p1.getId().equals(pdb.getId()))
786           {
787             if (!seqs.contains(sq = alignment.getSequenceAt(i)))
788             {
789               seqs.add(sq);
790             }
791
792             continue;
793           }
794         }
795       }
796       seqvectors.add(seqs.toArray(new SequenceI[seqs.size()]));
797     }
798     return seqvectors.toArray(new SequenceI[seqvectors.size()][]);
799   }
800
801   public boolean isNormaliseSequenceLogo()
802   {
803     return normaliseSequenceLogo;
804   }
805
806   public void setNormaliseSequenceLogo(boolean state)
807   {
808     normaliseSequenceLogo = state;
809   }
810
811   /**
812    * 
813    * @return true if alignment characters should be displayed
814    */
815   public boolean isValidCharWidth()
816   {
817     return validCharWidth;
818   }
819
820   private Hashtable<String, AutoCalcSetting> calcIdParams = new Hashtable<String, AutoCalcSetting>();
821
822   private boolean showAutocalculatedAbove;
823
824   public AutoCalcSetting getCalcIdSettingsFor(String calcId)
825   {
826     return calcIdParams.get(calcId);
827   }
828
829   public void setCalcIdSettingsFor(String calcId, AutoCalcSetting settings,
830           boolean needsUpdate)
831   {
832     calcIdParams.put(calcId, settings);
833     // TODO: create a restart list to trigger any calculations that need to be
834     // restarted after load
835     // calculator.getRegisteredWorkersOfClass(settings.getWorkerClass())
836     if (needsUpdate)
837     {
838       Cache.log.debug("trigger update for " + calcId);
839     }
840   }
841
842   protected SequenceAnnotationOrder getSortAnnotationsBy()
843   {
844     return sortAnnotationsBy;
845   }
846
847   protected void setSortAnnotationsBy(SequenceAnnotationOrder sortAnnotationsBy)
848   {
849     this.sortAnnotationsBy = sortAnnotationsBy;
850   }
851
852   protected boolean isShowAutocalculatedAbove()
853   {
854     return showAutocalculatedAbove;
855   }
856
857   protected void setShowAutocalculatedAbove(boolean showAutocalculatedAbove)
858   {
859     this.showAutocalculatedAbove = showAutocalculatedAbove;
860   }
861
862   public AnnotationColumnChooser getAnnotationColumnSelectionState()
863   {
864     return annotationColumnSelectionState;
865   }
866
867   public void setAnnotationColumnSelectionState(
868           AnnotationColumnChooser currentAnnotationColumnSelectionState)
869   {
870     this.annotationColumnSelectionState = currentAnnotationColumnSelectionState;
871   }
872
873   @Override
874   public void setIdWidth(int i)
875   {
876     super.setIdWidth(i);
877     AlignmentPanel ap = getAlignPanel();
878     if (ap != null)
879     {
880       // modify GUI elements to reflect geometry change
881       Dimension idw = getAlignPanel().getIdPanel().getIdCanvas()
882               .getPreferredSize();
883       idw.width = i;
884       getAlignPanel().getIdPanel().getIdCanvas().setPreferredSize(idw);
885     }
886   }
887 }