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