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