JAL-1628 drop getShowAnnotation accessor in favour of isShowAnnotation in AlignViewpo...
[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.bin.Cache;
45 import jalview.commands.CommandI;
46 import jalview.datamodel.AlignmentI;
47 import jalview.datamodel.ColumnSelection;
48 import jalview.datamodel.PDBEntry;
49 import jalview.datamodel.Sequence;
50 import jalview.datamodel.SequenceGroup;
51 import jalview.datamodel.SequenceI;
52 import jalview.schemes.ColourSchemeProperty;
53 import jalview.schemes.UserColourScheme;
54 import jalview.structure.SelectionSource;
55 import jalview.structure.StructureSelectionManager;
56 import jalview.structure.VamsasSource;
57 import jalview.viewmodel.AlignmentViewport;
58 import jalview.ws.params.AutoCalcSetting;
59
60 import java.awt.Color;
61 import java.awt.Container;
62 import java.awt.Font;
63 import java.awt.Rectangle;
64 import java.util.ArrayList;
65 import java.util.Hashtable;
66 import java.util.Stack;
67 import java.util.Vector;
68
69 /**
70  * DOCUMENT ME!
71  * 
72  * @author $author$
73  * @version $Revision: 1.141 $
74  */
75 public class AlignViewport extends AlignmentViewport implements
76         SelectionSource, VamsasSource, AlignViewportI
77 {
78   int startRes;
79
80   int endRes;
81
82   int startSeq;
83
84   int endSeq;
85
86   boolean showJVSuffix = true;
87
88   boolean showText = true;
89
90   boolean showColourText = false;
91
92   boolean showBoxes = true;
93
94   boolean wrapAlignment = false;
95
96   boolean renderGaps = true;
97
98   SequenceAnnotationOrder sortAnnotationsBy = null;
99
100   int charHeight;
101
102   int charWidth;
103
104   boolean validCharWidth;
105
106   int wrappedWidth;
107
108   Font font;
109
110   boolean seqNameItalics;
111
112   NJTree currentTree = null;
113
114   boolean scaleAboveWrapped = false;
115
116   boolean scaleLeftWrapped = true;
117
118   boolean scaleRightWrapped = true;
119
120   boolean showHiddenMarkers = true;
121
122   boolean cursorMode = false;
123
124   boolean antiAlias = false;
125
126   Rectangle explodedPosition;
127
128   String viewName;
129
130   boolean gatherViewsHere = false;
131
132   Stack<CommandI> historyList = new Stack<CommandI>();
133
134   Stack<CommandI> redoList = new Stack<CommandI>();
135
136   int thresholdTextColour = 0;
137
138   Color textColour = Color.black;
139
140   Color textColour2 = Color.white;
141
142   private AnnotationColumnChooser annotationColumnSelectionState;
143   /**
144    * Creates a new AlignViewport object.
145    * 
146    * @param al
147    *          alignment to view
148    */
149   public AlignViewport(AlignmentI al)
150   {
151     setAlignment(al);
152     init();
153   }
154
155   /**
156    * Create a new AlignViewport object with a specific sequence set ID
157    * 
158    * @param al
159    * @param seqsetid
160    *          (may be null - but potential for ambiguous constructor exception)
161    */
162   public AlignViewport(AlignmentI al, String seqsetid)
163   {
164     this(al, seqsetid, null);
165   }
166
167   public AlignViewport(AlignmentI al, String seqsetid, String viewid)
168   {
169     sequenceSetID = seqsetid;
170     viewId = viewid;
171     // TODO remove these once 2.4.VAMSAS release finished
172     if (Cache.log != null && Cache.log.isDebugEnabled() && seqsetid != null)
173     {
174       Cache.log.debug("Setting viewport's sequence set id : "
175               + sequenceSetID);
176     }
177     if (Cache.log != null && Cache.log.isDebugEnabled() && viewId != null)
178     {
179       Cache.log.debug("Setting viewport's view id : " + viewId);
180     }
181     setAlignment(al);
182     init();
183   }
184
185   /**
186    * Create a new AlignViewport with hidden regions
187    * 
188    * @param al
189    *          AlignmentI
190    * @param hiddenColumns
191    *          ColumnSelection
192    */
193   public AlignViewport(AlignmentI al, ColumnSelection hiddenColumns)
194   {
195     setAlignment(al);
196     if (hiddenColumns != null)
197     {
198       this.colSel = hiddenColumns;
199       if (hiddenColumns.getHiddenColumns() != null
200               && hiddenColumns.getHiddenColumns().size() > 0)
201       {
202         hasHiddenColumns = true;
203       }
204       else
205       {
206         hasHiddenColumns = false;
207       }
208     }
209     init();
210   }
211
212   /**
213    * New viewport with hidden columns and an existing sequence set id
214    * 
215    * @param al
216    * @param hiddenColumns
217    * @param seqsetid
218    *          (may be null)
219    */
220   public AlignViewport(AlignmentI al, ColumnSelection hiddenColumns,
221           String seqsetid)
222   {
223     this(al, hiddenColumns, seqsetid, null);
224   }
225
226   /**
227    * New viewport with hidden columns and an existing sequence set id and viewid
228    * 
229    * @param al
230    * @param hiddenColumns
231    * @param seqsetid
232    *          (may be null)
233    * @param viewid
234    *          (may be null)
235    */
236   public AlignViewport(AlignmentI al, ColumnSelection hiddenColumns,
237           String seqsetid, String viewid)
238   {
239     sequenceSetID = seqsetid;
240     viewId = viewid;
241     // TODO remove these once 2.4.VAMSAS release finished
242     if (Cache.log != null && Cache.log.isDebugEnabled() && seqsetid != null)
243     {
244       Cache.log.debug("Setting viewport's sequence set id : "
245               + sequenceSetID);
246     }
247     if (Cache.log != null && Cache.log.isDebugEnabled() && viewId != null)
248     {
249       Cache.log.debug("Setting viewport's view id : " + viewId);
250     }
251     setAlignment(al);
252     if (hiddenColumns != null)
253     {
254       this.colSel = hiddenColumns;
255       if (hiddenColumns.getHiddenColumns() != null
256               && hiddenColumns.getHiddenColumns().size() > 0)
257       {
258         hasHiddenColumns = true;
259       }
260       else
261       {
262         hasHiddenColumns = false;
263       }
264     }
265     init();
266   }
267
268   void init()
269   {
270     this.startRes = 0;
271     this.endRes = alignment.getWidth() - 1;
272     this.startSeq = 0;
273     this.endSeq = alignment.getHeight() - 1;
274
275     antiAlias = Cache.getDefault("ANTI_ALIAS", false);
276
277     showJVSuffix = Cache.getDefault("SHOW_JVSUFFIX", true);
278     setShowAnnotation(Cache.getDefault("SHOW_ANNOTATIONS", true));
279
280     setRightAlignIds(Cache.getDefault("RIGHT_ALIGN_IDS", false));
281     centreColumnLabels = Cache.getDefault("CENTRE_COLUMN_LABELS", false);
282     autoCalculateConsensus = Cache.getDefault("AUTO_CALC_CONSENSUS", true);
283
284     setPadGaps(Cache.getDefault("PAD_GAPS", true));
285     shownpfeats = Cache.getDefault("SHOW_NPFEATS_TOOLTIP", true);
286     showdbrefs = Cache.getDefault("SHOW_DBREFS_TOOLTIP", true);
287
288     String fontName = Cache.getDefault("FONT_NAME", "SansSerif");
289     String fontStyle = Cache.getDefault("FONT_STYLE", Font.PLAIN + "");
290     String fontSize = Cache.getDefault("FONT_SIZE", "10");
291
292     seqNameItalics = Cache.getDefault("ID_ITALICS", true);
293
294     int style = 0;
295
296     if (fontStyle.equals("bold"))
297     {
298       style = 1;
299     }
300     else if (fontStyle.equals("italic"))
301     {
302       style = 2;
303     }
304
305     setFont(new Font(fontName, style, Integer.parseInt(fontSize)));
306
307     alignment
308             .setGapCharacter(Cache.getDefault("GAP_SYMBOL", "-").charAt(0));
309
310     // We must set conservation and consensus before setting colour,
311     // as Blosum and Clustal require this to be done
312     if (hconsensus == null && !isDataset)
313     {
314       if (!alignment.isNucleotide())
315       {
316         showConservation = Cache.getDefault("SHOW_CONSERVATION", true);
317         showQuality = Cache.getDefault("SHOW_QUALITY", true);
318         showGroupConservation = Cache.getDefault("SHOW_GROUP_CONSERVATION",
319                 false);
320       }
321       showConsensusHistogram = Cache.getDefault("SHOW_CONSENSUS_HISTOGRAM",
322               true);
323       showSequenceLogo = Cache.getDefault("SHOW_CONSENSUS_LOGO", false);
324       normaliseSequenceLogo = Cache.getDefault("NORMALISE_CONSENSUS_LOGO",
325               false);
326       showGroupConsensus = Cache.getDefault("SHOW_GROUP_CONSENSUS", false);
327       showConsensus = Cache.getDefault("SHOW_IDENTITY", true);
328     }
329     initAutoAnnotation();
330     if (jalview.bin.Cache.getProperty("DEFAULT_COLOUR") != null)
331     {
332       globalColourScheme = ColourSchemeProperty.getColour(alignment,
333               jalview.bin.Cache.getProperty("DEFAULT_COLOUR"));
334
335       if (globalColourScheme instanceof UserColourScheme)
336       {
337         globalColourScheme = UserDefinedColours.loadDefaultColours();
338         ((UserColourScheme) globalColourScheme).setThreshold(0,
339                 getIgnoreGapsConsensus());
340       }
341
342       if (globalColourScheme != null)
343       {
344         globalColourScheme.setConsensus(hconsensus);
345       }
346     }
347
348     wrapAlignment = Cache.getDefault("WRAP_ALIGNMENT", false);
349     showUnconserved = Cache.getDefault("SHOW_UNCONSERVED", false);
350     sortByTree = Cache.getDefault("SORT_BY_TREE", false);
351     followSelection = Cache.getDefault("FOLLOW_SELECTIONS", true);
352     sortAnnotationsBy = SequenceAnnotationOrder.valueOf(Cache.getDefault(
353             Preferences.SORT_ANNOTATIONS,
354             SequenceAnnotationOrder.NONE.name()));
355     showAutocalculatedAbove = Cache.getDefault(
356             Preferences.SHOW_AUTOCALC_ABOVE, false);
357   }
358
359   /**
360    * centre columnar annotation labels in displayed alignment annotation TODO:
361    * add to jalviewXML and annotation display settings
362    */
363   boolean centreColumnLabels = false;
364
365   private boolean showdbrefs;
366
367   private boolean shownpfeats;
368
369   // --------END Structure Conservation
370
371   /**
372    * get the consensus sequence as displayed under the PID consensus annotation
373    * row.
374    * 
375    * @return consensus sequence as a new sequence object
376    */
377   public SequenceI getConsensusSeq()
378   {
379     if (consensus == null)
380     {
381       updateConsensus(null);
382     }
383     if (consensus == null)
384     {
385       return null;
386     }
387     StringBuffer seqs = new StringBuffer();
388     for (int i = 0; i < consensus.annotations.length; i++)
389     {
390       if (consensus.annotations[i] != null)
391       {
392         if (consensus.annotations[i].description.charAt(0) == '[')
393         {
394           seqs.append(consensus.annotations[i].description.charAt(1));
395         }
396         else
397         {
398           seqs.append(consensus.annotations[i].displayCharacter);
399         }
400       }
401     }
402
403     SequenceI sq = new Sequence("Consensus", seqs.toString());
404     sq.setDescription("Percentage Identity Consensus "
405             + ((ignoreGapsInConsensusCalculation) ? " without gaps" : ""));
406     return sq;
407   }
408
409   /**
410    * DOCUMENT ME!
411    * 
412    * @return DOCUMENT ME!
413    */
414   public int getStartRes()
415   {
416     return startRes;
417   }
418
419   /**
420    * DOCUMENT ME!
421    * 
422    * @return DOCUMENT ME!
423    */
424   public int getEndRes()
425   {
426     return endRes;
427   }
428
429   /**
430    * DOCUMENT ME!
431    * 
432    * @return DOCUMENT ME!
433    */
434   public int getStartSeq()
435   {
436     return startSeq;
437   }
438
439   /**
440    * DOCUMENT ME!
441    * 
442    * @param res
443    *          DOCUMENT ME!
444    */
445   public void setStartRes(int res)
446   {
447     this.startRes = res;
448   }
449
450   /**
451    * DOCUMENT ME!
452    * 
453    * @param seq
454    *          DOCUMENT ME!
455    */
456   public void setStartSeq(int seq)
457   {
458     this.startSeq = seq;
459   }
460
461   /**
462    * DOCUMENT ME!
463    * 
464    * @param res
465    *          DOCUMENT ME!
466    */
467   public void setEndRes(int res)
468   {
469     if (res > (alignment.getWidth() - 1))
470     {
471       // log.System.out.println(" Corrected res from " + res + " to maximum " +
472       // (alignment.getWidth()-1));
473       res = alignment.getWidth() - 1;
474     }
475
476     if (res < 0)
477     {
478       res = 0;
479     }
480
481     this.endRes = res;
482   }
483
484   /**
485    * DOCUMENT ME!
486    * 
487    * @param seq
488    *          DOCUMENT ME!
489    */
490   public void setEndSeq(int seq)
491   {
492     if (seq > alignment.getHeight())
493     {
494       seq = alignment.getHeight();
495     }
496
497     if (seq < 0)
498     {
499       seq = 0;
500     }
501
502     this.endSeq = seq;
503   }
504
505   /**
506    * DOCUMENT ME!
507    * 
508    * @return DOCUMENT ME!
509    */
510   public int getEndSeq()
511   {
512     return endSeq;
513   }
514
515   /**
516    * DOCUMENT ME!
517    * 
518    * @param f
519    *          DOCUMENT ME!
520    */
521   public void setFont(Font f)
522   {
523     font = f;
524
525     Container c = new Container();
526
527     java.awt.FontMetrics fm = c.getFontMetrics(font);
528     setCharHeight(fm.getHeight());
529     setCharWidth(fm.charWidth('M'));
530     validCharWidth = true;
531   }
532
533   /**
534    * DOCUMENT ME!
535    * 
536    * @return DOCUMENT ME!
537    */
538   public Font getFont()
539   {
540     return font;
541   }
542
543   /**
544    * DOCUMENT ME!
545    * 
546    * @param w
547    *          DOCUMENT ME!
548    */
549   public void setCharWidth(int w)
550   {
551     this.charWidth = w;
552   }
553
554   /**
555    * DOCUMENT ME!
556    * 
557    * @return DOCUMENT ME!
558    */
559   public int getCharWidth()
560   {
561     return charWidth;
562   }
563
564   /**
565    * DOCUMENT ME!
566    * 
567    * @param h
568    *          DOCUMENT ME!
569    */
570   public void setCharHeight(int h)
571   {
572     this.charHeight = h;
573   }
574
575   /**
576    * DOCUMENT ME!
577    * 
578    * @return DOCUMENT ME!
579    */
580   public int getCharHeight()
581   {
582     return charHeight;
583   }
584
585   /**
586    * DOCUMENT ME!
587    * 
588    * @param w
589    *          DOCUMENT ME!
590    */
591   public void setWrappedWidth(int w)
592   {
593     this.wrappedWidth = w;
594   }
595
596   /**
597    * DOCUMENT ME!
598    * 
599    * @return DOCUMENT ME!
600    */
601   public int getWrappedWidth()
602   {
603     return wrappedWidth;
604   }
605
606   /**
607    * DOCUMENT ME!
608    * 
609    * @return DOCUMENT ME!
610    */
611   public AlignmentI getAlignment()
612   {
613     return alignment;
614   }
615
616   /**
617    * DOCUMENT ME!
618    * 
619    * @param align
620    *          DOCUMENT ME!
621    */
622   public void setAlignment(AlignmentI align)
623   {
624     if (alignment != null && alignment.getCodonFrames() != null)
625     {
626       StructureSelectionManager.getStructureSelectionManager(
627               Desktop.instance).removeMappings(alignment.getCodonFrames());
628     }
629     this.alignment = align;
630     if (alignment != null && alignment.getCodonFrames() != null)
631     {
632       StructureSelectionManager.getStructureSelectionManager(
633               Desktop.instance).addMappings(alignment.getCodonFrames());
634     }
635   }
636
637   /**
638    * DOCUMENT ME!
639    * 
640    * @param state
641    *          DOCUMENT ME!
642    */
643   public void setWrapAlignment(boolean state)
644   {
645     wrapAlignment = state;
646   }
647
648   /**
649    * DOCUMENT ME!
650    * 
651    * @param state
652    *          DOCUMENT ME!
653    */
654   public void setShowText(boolean state)
655   {
656     showText = state;
657   }
658
659   /**
660    * DOCUMENT ME!
661    * 
662    * @param state
663    *          DOCUMENT ME!
664    */
665   public void setRenderGaps(boolean state)
666   {
667     renderGaps = state;
668   }
669
670   /**
671    * DOCUMENT ME!
672    * 
673    * @return DOCUMENT ME!
674    */
675   public boolean getColourText()
676   {
677     return showColourText;
678   }
679
680   /**
681    * DOCUMENT ME!
682    * 
683    * @param state
684    *          DOCUMENT ME!
685    */
686   public void setColourText(boolean state)
687   {
688     showColourText = state;
689   }
690
691   /**
692    * DOCUMENT ME!
693    * 
694    * @param state
695    *          DOCUMENT ME!
696    */
697   public void setShowBoxes(boolean state)
698   {
699     showBoxes = state;
700   }
701
702   /**
703    * DOCUMENT ME!
704    * 
705    * @return DOCUMENT ME!
706    */
707   public boolean getWrapAlignment()
708   {
709     return wrapAlignment;
710   }
711
712   /**
713    * DOCUMENT ME!
714    * 
715    * @return DOCUMENT ME!
716    */
717   public boolean getShowText()
718   {
719     return showText;
720   }
721
722   /**
723    * DOCUMENT ME!
724    * 
725    * @return DOCUMENT ME!
726    */
727   public boolean getShowBoxes()
728   {
729     return showBoxes;
730   }
731
732   /**
733    * DOCUMENT ME!
734    * 
735    * @return DOCUMENT ME!
736    */
737   public char getGapCharacter()
738   {
739     return getAlignment().getGapCharacter();
740   }
741
742   /**
743    * DOCUMENT ME!
744    * 
745    * @param gap
746    *          DOCUMENT ME!
747    */
748   public void setGapCharacter(char gap)
749   {
750     if (getAlignment() != null)
751     {
752       getAlignment().setGapCharacter(gap);
753     }
754   }
755
756   /**
757    * DOCUMENT ME!
758    * 
759    * @return DOCUMENT ME!
760    */
761   public ColumnSelection getColumnSelection()
762   {
763     return colSel;
764   }
765
766   /**
767    * DOCUMENT ME!
768    * 
769    * @param tree
770    *          DOCUMENT ME!
771    */
772   public void setCurrentTree(NJTree tree)
773   {
774     currentTree = tree;
775   }
776
777   /**
778    * DOCUMENT ME!
779    * 
780    * @return DOCUMENT ME!
781    */
782   public NJTree getCurrentTree()
783   {
784     return currentTree;
785   }
786
787   /**
788    * DOCUMENT ME!
789    * 
790    * @return DOCUMENT ME!
791    */
792   public boolean getShowJVSuffix()
793   {
794     return showJVSuffix;
795   }
796
797   /**
798    * DOCUMENT ME!
799    * 
800    * @param b
801    *          DOCUMENT ME!
802    */
803   public void setShowJVSuffix(boolean b)
804   {
805     showJVSuffix = b;
806   }
807
808   /**
809    * DOCUMENT ME!
810    * 
811    * @return DOCUMENT ME!
812    */
813   public boolean getScaleAboveWrapped()
814   {
815     return scaleAboveWrapped;
816   }
817
818   /**
819    * DOCUMENT ME!
820    * 
821    * @return DOCUMENT ME!
822    */
823   public boolean getScaleLeftWrapped()
824   {
825     return scaleLeftWrapped;
826   }
827
828   /**
829    * DOCUMENT ME!
830    * 
831    * @return DOCUMENT ME!
832    */
833   public boolean getScaleRightWrapped()
834   {
835     return scaleRightWrapped;
836   }
837
838   /**
839    * DOCUMENT ME!
840    * 
841    * @param b
842    *          DOCUMENT ME!
843    */
844   public void setScaleAboveWrapped(boolean b)
845   {
846     scaleAboveWrapped = b;
847   }
848
849   /**
850    * DOCUMENT ME!
851    * 
852    * @param b
853    *          DOCUMENT ME!
854    */
855   public void setScaleLeftWrapped(boolean b)
856   {
857     scaleLeftWrapped = b;
858   }
859
860   /**
861    * DOCUMENT ME!
862    * 
863    * @param b
864    *          DOCUMENT ME!
865    */
866   public void setScaleRightWrapped(boolean b)
867   {
868     scaleRightWrapped = b;
869   }
870
871   public void setDataset(boolean b)
872   {
873     isDataset = b;
874   }
875
876   public boolean isDataset()
877   {
878     return isDataset;
879   }
880
881   public boolean getShowHiddenMarkers()
882   {
883     return showHiddenMarkers;
884   }
885
886   public void setShowHiddenMarkers(boolean show)
887   {
888     showHiddenMarkers = show;
889   }
890
891   /**
892    * returns the visible column regions of the alignment
893    * 
894    * @param selectedRegionOnly
895    *          true to just return the contigs intersecting with the selected
896    *          area
897    * @return
898    */
899   public int[] getViewAsVisibleContigs(boolean selectedRegionOnly)
900   {
901     int[] viscontigs = null;
902     int start = 0, end = 0;
903     if (selectedRegionOnly && selectionGroup != null)
904     {
905       start = selectionGroup.getStartRes();
906       end = selectionGroup.getEndRes() + 1;
907     }
908     else
909     {
910       end = alignment.getWidth();
911     }
912     viscontigs = colSel.getVisibleContigs(start, end);
913     return viscontigs;
914   }
915
916   /**
917    * get hash of undo and redo list for the alignment
918    * 
919    * @return long[] { historyList.hashCode, redoList.hashCode };
920    */
921   public long[] getUndoRedoHash()
922   {
923     // TODO: JAL-1126
924     if (historyList == null || redoList == null)
925     {
926       return new long[]
927       { -1, -1 };
928     }
929     return new long[]
930     { historyList.hashCode(), this.redoList.hashCode() };
931   }
932
933   /**
934    * test if a particular set of hashcodes are different to the hashcodes for
935    * the undo and redo list.
936    * 
937    * @param undoredo
938    *          the stored set of hashcodes as returned by getUndoRedoHash
939    * @return true if the hashcodes differ (ie the alignment has been edited) or
940    *         the stored hashcode array differs in size
941    */
942   public boolean isUndoRedoHashModified(long[] undoredo)
943   {
944     if (undoredo == null)
945     {
946       return true;
947     }
948     long[] cstate = getUndoRedoHash();
949     if (cstate.length != undoredo.length)
950     {
951       return true;
952     }
953
954     for (int i = 0; i < cstate.length; i++)
955     {
956       if (cstate[i] != undoredo[i])
957       {
958         return true;
959       }
960     }
961     return false;
962   }
963
964   public boolean getCentreColumnLabels()
965   {
966     return centreColumnLabels;
967   }
968
969   public void setCentreColumnLabels(boolean centrecolumnlabels)
970   {
971     centreColumnLabels = centrecolumnlabels;
972   }
973
974   /**
975    * enable or disable the display of Database Cross References in the sequence
976    * ID tooltip
977    */
978   public void setShowDbRefs(boolean show)
979   {
980     showdbrefs = show;
981   }
982
983   /**
984    * 
985    * @return true if Database References are to be displayed on tooltips.
986    */
987   public boolean isShowDbRefs()
988   {
989     return showdbrefs;
990   }
991
992   /**
993    * 
994    * @return true if Non-positional features are to be displayed on tooltips.
995    */
996   public boolean isShowNpFeats()
997   {
998     return shownpfeats;
999   }
1000
1001   /**
1002    * enable or disable the display of Non-Positional sequence features in the
1003    * sequence ID tooltip
1004    * 
1005    * @param show
1006    */
1007   public void setShowNpFeats(boolean show)
1008   {
1009     shownpfeats = show;
1010   }
1011
1012   /**
1013    * 
1014    * @return true if view has hidden rows
1015    */
1016   public boolean hasHiddenRows()
1017   {
1018     return hasHiddenRows;
1019   }
1020
1021   /**
1022    * 
1023    * @return true if view has hidden columns
1024    */
1025   public boolean hasHiddenColumns()
1026   {
1027     return hasHiddenColumns;
1028   }
1029
1030   /**
1031    * when set, view will scroll to show the highlighted position
1032    */
1033   public boolean followHighlight = true;
1034
1035   /**
1036    * @return true if view should scroll to show the highlighted region of a
1037    *         sequence
1038    * @return
1039    */
1040   public boolean getFollowHighlight()
1041   {
1042     return followHighlight;
1043   }
1044
1045   public boolean followSelection = true;
1046
1047   /**
1048    * @return true if view selection should always follow the selections
1049    *         broadcast by other selection sources
1050    */
1051   public boolean getFollowSelection()
1052   {
1053     return followSelection;
1054   }
1055
1056   public void sendSelection()
1057   {
1058     jalview.structure.StructureSelectionManager
1059             .getStructureSelectionManager(Desktop.instance).sendSelection(
1060                     new SequenceGroup(getSelectionGroup()),
1061                     new ColumnSelection(getColumnSelection()), this);
1062   }
1063
1064   /**
1065    * return the alignPanel containing the given viewport. Use this to get the
1066    * components currently handling the given viewport.
1067    * 
1068    * @param av
1069    * @return null or an alignPanel guaranteed to have non-null alignFrame
1070    *         reference
1071    */
1072   public AlignmentPanel getAlignPanel()
1073   {
1074     AlignmentPanel[] aps = PaintRefresher.getAssociatedPanels(this
1075             .getSequenceSetId());
1076     AlignmentPanel ap = null;
1077     for (int p = 0; aps != null && p < aps.length; p++)
1078     {
1079       if (aps[p].av == this)
1080       {
1081         return aps[p];
1082       }
1083     }
1084     return null;
1085   }
1086
1087   public boolean getSortByTree()
1088   {
1089     return sortByTree;
1090   }
1091
1092   public void setSortByTree(boolean sort)
1093   {
1094     sortByTree = sort;
1095   }
1096
1097   /**
1098    * synthesize a column selection if none exists so it covers the given
1099    * selection group. if wholewidth is false, no column selection is made if the
1100    * selection group covers the whole alignment width.
1101    * 
1102    * @param sg
1103    * @param wholewidth
1104    */
1105   public void expandColSelection(SequenceGroup sg, boolean wholewidth)
1106   {
1107     int sgs, sge;
1108     if (sg != null
1109             && (sgs = sg.getStartRes()) >= 0
1110             && sg.getStartRes() <= (sge = sg.getEndRes())
1111             && (colSel == null || colSel.getSelected() == null || colSel
1112                     .getSelected().size() == 0))
1113     {
1114       if (!wholewidth && alignment.getWidth() == (1 + sge - sgs))
1115       {
1116         // do nothing
1117         return;
1118       }
1119       if (colSel == null)
1120       {
1121         colSel = new ColumnSelection();
1122       }
1123       for (int cspos = sg.getStartRes(); cspos <= sg.getEndRes(); cspos++)
1124       {
1125         colSel.addElement(cspos);
1126       }
1127     }
1128   }
1129
1130   public StructureSelectionManager getStructureSelectionManager()
1131   {
1132     return StructureSelectionManager
1133             .getStructureSelectionManager(Desktop.instance);
1134   }
1135
1136   /**
1137    * 
1138    * @param pdbEntries
1139    * @return a series of SequenceI arrays, one for each PDBEntry, listing which
1140    *         sequence in the alignment holds a reference to it
1141    */
1142   public SequenceI[][] collateForPDB(PDBEntry[] pdbEntries)
1143   {
1144     ArrayList<SequenceI[]> seqvectors = new ArrayList<SequenceI[]>();
1145     for (PDBEntry pdb : pdbEntries)
1146     {
1147       ArrayList<SequenceI> seqs = new ArrayList<SequenceI>();
1148       for (int i = 0; i < alignment.getHeight(); i++)
1149       {
1150         Vector pdbs = alignment.getSequenceAt(i).getDatasetSequence()
1151                 .getPDBId();
1152         if (pdbs == null)
1153         {
1154           continue;
1155         }
1156         SequenceI sq;
1157         for (int p = 0; p < pdbs.size(); p++)
1158         {
1159           PDBEntry p1 = (PDBEntry) pdbs.elementAt(p);
1160           if (p1.getId().equals(pdb.getId()))
1161           {
1162             if (!seqs.contains(sq = alignment.getSequenceAt(i)))
1163             {
1164               seqs.add(sq);
1165             }
1166
1167             continue;
1168           }
1169         }
1170       }
1171       seqvectors.add(seqs.toArray(new SequenceI[seqs.size()]));
1172     }
1173     return seqvectors.toArray(new SequenceI[seqvectors.size()][]);
1174   }
1175
1176   public boolean isNormaliseSequenceLogo()
1177   {
1178     return normaliseSequenceLogo;
1179   }
1180
1181   public void setNormaliseSequenceLogo(boolean state)
1182   {
1183     normaliseSequenceLogo = state;
1184   }
1185
1186   /**
1187    * 
1188    * @return true if alignment characters should be displayed
1189    */
1190   public boolean isValidCharWidth()
1191   {
1192     return validCharWidth;
1193   }
1194
1195   private Hashtable<String, AutoCalcSetting> calcIdParams = new Hashtable<String, AutoCalcSetting>();
1196
1197   private boolean showAutocalculatedAbove;
1198
1199   public AutoCalcSetting getCalcIdSettingsFor(String calcId)
1200   {
1201     return calcIdParams.get(calcId);
1202   }
1203
1204   public void setCalcIdSettingsFor(String calcId, AutoCalcSetting settings,
1205           boolean needsUpdate)
1206   {
1207     calcIdParams.put(calcId, settings);
1208     // TODO: create a restart list to trigger any calculations that need to be
1209     // restarted after load
1210     // calculator.getRegisteredWorkersOfClass(settings.getWorkerClass())
1211     if (needsUpdate)
1212     {
1213       Cache.log.debug("trigger update for " + calcId);
1214     }
1215   }
1216
1217   protected SequenceAnnotationOrder getSortAnnotationsBy()
1218   {
1219     return sortAnnotationsBy;
1220   }
1221
1222   protected void setSortAnnotationsBy(SequenceAnnotationOrder sortAnnotationsBy)
1223   {
1224     this.sortAnnotationsBy = sortAnnotationsBy;
1225   }
1226
1227   protected boolean isShowAutocalculatedAbove()
1228   {
1229     return showAutocalculatedAbove;
1230   }
1231
1232   protected void setShowAutocalculatedAbove(boolean showAutocalculatedAbove)
1233   {
1234     this.showAutocalculatedAbove = showAutocalculatedAbove;
1235   }
1236
1237   public AnnotationColumnChooser getAnnotationColumnSelectionState()
1238   {
1239     return annotationColumnSelectionState;
1240   }
1241
1242   public void setAnnotationColumnSelectionState(
1243           AnnotationColumnChooser currentAnnotationColumnSelectionState)
1244   {
1245     this.annotationColumnSelectionState = currentAnnotationColumnSelectionState;
1246   }
1247 }