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