JAL-969 - refactor colourscheme machinery from GUI code to core alignment view model
[jalview.git] / src / jalview / gui / AlignViewport.java
1 /*
2  * Jalview - A Sequence Alignment Editor and Viewer (Version 2.7)
3  * Copyright (C) 2011 J Procter, AM Waterhouse, J Engelhardt, LM Lui, 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 java.util.*;
39
40 import java.awt.*;
41
42 import jalview.analysis.*;
43 import jalview.api.AlignCalcManagerI;
44 import jalview.api.AlignViewportI;
45 import jalview.api.AlignmentViewPanel;
46 import jalview.api.OOMHandlerI;
47
48 import jalview.bin.*;
49
50 import jalview.datamodel.*;
51
52 import jalview.schemes.*;
53 import jalview.structure.SelectionSource;
54 import jalview.structure.StructureSelectionManager;
55 import jalview.structure.VamsasSource;
56 import jalview.viewmodel.AlignmentViewport;
57 import jalview.workers.AlignCalcManager;
58 import jalview.workers.ConsensusThread;
59 import jalview.workers.ConservationThread;
60 import jalview.workers.StrucConsensusThread;
61
62 /**
63  * DOCUMENT ME!
64  * 
65  * @author $author$
66  * @version $Revision: 1.141 $
67  */
68 public class AlignViewport extends AlignmentViewport implements SelectionSource, VamsasSource, AlignViewportI
69 {
70   private static final int RIGHT_JUSTIFY = 1;
71
72   int startRes;
73
74   int endRes;
75
76   int startSeq;
77
78   int endSeq;
79
80   boolean showJVSuffix = true;
81
82   boolean showText = true;
83
84   boolean showColourText = false;
85
86   boolean showBoxes = true;
87
88   boolean wrapAlignment = false;
89
90   boolean renderGaps = true;
91
92   boolean showSequenceFeatures = false;
93
94   boolean showAnnotation = true;
95
96   int charHeight;
97
98   int charWidth;
99
100   boolean validCharWidth;
101
102   int wrappedWidth;
103
104   Font font;
105
106   boolean seqNameItalics;
107
108
109   NJTree currentTree = null;
110
111   boolean scaleAboveWrapped = false;
112
113   boolean scaleLeftWrapped = true;
114
115   boolean scaleRightWrapped = true;
116
117   boolean showHiddenMarkers = true;
118
119   boolean cursorMode = false;
120
121   /**
122    * Keys are the feature types which are currently visible. Note: Values are
123    * not used!
124    */
125   Hashtable featuresDisplayed = null;
126
127   boolean antiAlias = false;
128
129   Rectangle explodedPosition;
130
131   String viewName;
132
133   boolean gatherViewsHere = false;
134
135   Stack historyList = new Stack();
136
137   Stack redoList = new Stack();
138
139   Hashtable sequenceColours;
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", false);
331       showGroupConsensus = Cache.getDefault("SHOW_GROUP_CONSENSUS", false);
332       showConsensus=Cache.getDefault("SHOW_IDENTITY", true);
333       consensus = new AlignmentAnnotation("Consensus", "PID",
334               new Annotation[1], 0f, 100f, AlignmentAnnotation.BAR_GRAPH);
335       consensus.hasText = true;
336       consensus.autoCalculated = true;
337     }
338     initAutoAnnotation();
339     if (jalview.bin.Cache.getProperty("DEFAULT_COLOUR") != null)
340     {
341       globalColourScheme = ColourSchemeProperty.getColour(alignment,
342               jalview.bin.Cache.getProperty("DEFAULT_COLOUR"));
343
344       if (globalColourScheme instanceof UserColourScheme)
345       {
346         globalColourScheme = UserDefinedColours.loadDefaultColours();
347         ((UserColourScheme) globalColourScheme).setThreshold(0,
348                 getIgnoreGapsConsensus());
349       }
350
351       if (globalColourScheme != null)
352       {
353         globalColourScheme.setConsensus(hconsensus);
354       }
355     }
356
357     wrapAlignment = jalview.bin.Cache.getDefault("WRAP_ALIGNMENT", false);
358     showUnconserved = jalview.bin.Cache.getDefault("SHOW_UNCONSERVED",
359             false);
360     sortByTree = jalview.bin.Cache.getDefault("SORT_BY_TREE", false);
361     followSelection = jalview.bin.Cache.getDefault("FOLLOW_SELECTIONS",
362             true);
363   }
364
365   /**
366    * set the flag
367    * 
368    * @param b
369    *          features are displayed if true
370    */
371   public void setShowSequenceFeatures(boolean b)
372   {
373     showSequenceFeatures = b;
374   }
375
376   public boolean getShowSequenceFeatures()
377   {
378     return showSequenceFeatures;
379   }
380
381   /**
382    * centre columnar annotation labels in displayed alignment annotation TODO:
383    * add to jalviewXML and annotation display settings
384    */
385   boolean centreColumnLabels = false;
386
387   private boolean showdbrefs;
388
389   private boolean shownpfeats;
390
391   // --------END Structure Conservation
392
393   /**
394    * get the consensus sequence as displayed under the PID consensus annotation
395    * row.
396    * 
397    * @return consensus sequence as a new sequence object
398    */
399   public SequenceI getConsensusSeq()
400   {
401     if (consensus == null)
402     {
403       updateConsensus(null);
404     }
405     if (consensus == null)
406     {
407       return null;
408     }
409     StringBuffer seqs = new StringBuffer();
410     for (int i = 0; i < consensus.annotations.length; i++)
411     {
412       if (consensus.annotations[i] != null)
413       {
414         if (consensus.annotations[i].description.charAt(0) == '[')
415         {
416           seqs.append(consensus.annotations[i].description.charAt(1));
417         }
418         else
419         {
420           seqs.append(consensus.annotations[i].displayCharacter);
421         }
422       }
423     }
424
425     SequenceI sq = new Sequence("Consensus", seqs.toString());
426     sq.setDescription("Percentage Identity Consensus "
427             + ((ignoreGapsInConsensusCalculation) ? " without gaps" : ""));
428     return sq;
429   }
430
431   /**
432    * DOCUMENT ME!
433    * 
434    * @return DOCUMENT ME!
435    */
436   public int getStartRes()
437   {
438     return startRes;
439   }
440
441   /**
442    * DOCUMENT ME!
443    * 
444    * @return DOCUMENT ME!
445    */
446   public int getEndRes()
447   {
448     return endRes;
449   }
450
451   /**
452    * DOCUMENT ME!
453    * 
454    * @return DOCUMENT ME!
455    */
456   public int getStartSeq()
457   {
458     return startSeq;
459   }
460
461   /**
462    * DOCUMENT ME!
463    * 
464    * @param res
465    *          DOCUMENT ME!
466    */
467   public void setStartRes(int res)
468   {
469     this.startRes = res;
470   }
471
472   /**
473    * DOCUMENT ME!
474    * 
475    * @param seq
476    *          DOCUMENT ME!
477    */
478   public void setStartSeq(int seq)
479   {
480     this.startSeq = seq;
481   }
482
483   /**
484    * DOCUMENT ME!
485    * 
486    * @param res
487    *          DOCUMENT ME!
488    */
489   public void setEndRes(int res)
490   {
491     if (res > (alignment.getWidth() - 1))
492     {
493       // log.System.out.println(" Corrected res from " + res + " to maximum " +
494       // (alignment.getWidth()-1));
495       res = alignment.getWidth() - 1;
496     }
497
498     if (res < 0)
499     {
500       res = 0;
501     }
502
503     this.endRes = res;
504   }
505
506   /**
507    * DOCUMENT ME!
508    * 
509    * @param seq
510    *          DOCUMENT ME!
511    */
512   public void setEndSeq(int seq)
513   {
514     if (seq > alignment.getHeight())
515     {
516       seq = alignment.getHeight();
517     }
518
519     if (seq < 0)
520     {
521       seq = 0;
522     }
523
524     this.endSeq = seq;
525   }
526
527   /**
528    * DOCUMENT ME!
529    * 
530    * @return DOCUMENT ME!
531    */
532   public int getEndSeq()
533   {
534     return endSeq;
535   }
536
537   /**
538    * DOCUMENT ME!
539    * 
540    * @param f
541    *          DOCUMENT ME!
542    */
543   public void setFont(Font f)
544   {
545     font = f;
546
547     Container c = new Container();
548
549     java.awt.FontMetrics fm = c.getFontMetrics(font);
550     setCharHeight(fm.getHeight());
551     setCharWidth(fm.charWidth('M'));
552     validCharWidth = true;
553   }
554
555   /**
556    * DOCUMENT ME!
557    * 
558    * @return DOCUMENT ME!
559    */
560   public Font getFont()
561   {
562     return font;
563   }
564
565   /**
566    * DOCUMENT ME!
567    * 
568    * @param w
569    *          DOCUMENT ME!
570    */
571   public void setCharWidth(int w)
572   {
573     this.charWidth = w;
574   }
575
576   /**
577    * DOCUMENT ME!
578    * 
579    * @return DOCUMENT ME!
580    */
581   public int getCharWidth()
582   {
583     return charWidth;
584   }
585
586   /**
587    * DOCUMENT ME!
588    * 
589    * @param h
590    *          DOCUMENT ME!
591    */
592   public void setCharHeight(int h)
593   {
594     this.charHeight = h;
595   }
596
597   /**
598    * DOCUMENT ME!
599    * 
600    * @return DOCUMENT ME!
601    */
602   public int getCharHeight()
603   {
604     return charHeight;
605   }
606
607   /**
608    * DOCUMENT ME!
609    * 
610    * @param w
611    *          DOCUMENT ME!
612    */
613   public void setWrappedWidth(int w)
614   {
615     this.wrappedWidth = w;
616   }
617
618   /**
619    * DOCUMENT ME!
620    * 
621    * @return DOCUMENT ME!
622    */
623   public int getWrappedWidth()
624   {
625     return wrappedWidth;
626   }
627
628   /**
629    * DOCUMENT ME!
630    * 
631    * @return DOCUMENT ME!
632    */
633   public AlignmentI getAlignment()
634   {
635     return alignment;
636   }
637
638   /**
639    * DOCUMENT ME!
640    * 
641    * @param align
642    *          DOCUMENT ME!
643    */
644   public void setAlignment(AlignmentI align)
645   {
646     if (alignment != null && alignment.getCodonFrames() != null)
647     {
648       StructureSelectionManager.getStructureSelectionManager(
649               Desktop.instance).removeMappings(alignment.getCodonFrames());
650     }
651     this.alignment = align;
652     if (alignment!=null && alignment.getCodonFrames() != null)
653     {
654       StructureSelectionManager.getStructureSelectionManager(
655               Desktop.instance).addMappings(alignment.getCodonFrames());
656     }
657   }
658
659   /**
660    * DOCUMENT ME!
661    * 
662    * @param state
663    *          DOCUMENT ME!
664    */
665   public void setWrapAlignment(boolean state)
666   {
667     wrapAlignment = state;
668   }
669
670   /**
671    * DOCUMENT ME!
672    * 
673    * @param state
674    *          DOCUMENT ME!
675    */
676   public void setShowText(boolean state)
677   {
678     showText = state;
679   }
680
681   /**
682    * DOCUMENT ME!
683    * 
684    * @param state
685    *          DOCUMENT ME!
686    */
687   public void setRenderGaps(boolean state)
688   {
689     renderGaps = state;
690   }
691
692   /**
693    * DOCUMENT ME!
694    * 
695    * @return DOCUMENT ME!
696    */
697   public boolean getColourText()
698   {
699     return showColourText;
700   }
701
702   /**
703    * DOCUMENT ME!
704    * 
705    * @param state
706    *          DOCUMENT ME!
707    */
708   public void setColourText(boolean state)
709   {
710     showColourText = state;
711   }
712
713   /**
714    * DOCUMENT ME!
715    * 
716    * @param state
717    *          DOCUMENT ME!
718    */
719   public void setShowBoxes(boolean state)
720   {
721     showBoxes = state;
722   }
723
724   /**
725    * DOCUMENT ME!
726    * 
727    * @return DOCUMENT ME!
728    */
729   public boolean getWrapAlignment()
730   {
731     return wrapAlignment;
732   }
733
734   /**
735    * DOCUMENT ME!
736    * 
737    * @return DOCUMENT ME!
738    */
739   public boolean getShowText()
740   {
741     return showText;
742   }
743
744   /**
745    * DOCUMENT ME!
746    * 
747    * @return DOCUMENT ME!
748    */
749   public boolean getShowBoxes()
750   {
751     return showBoxes;
752   }
753
754   /**
755    * DOCUMENT ME!
756    * 
757    * @return DOCUMENT ME!
758    */
759   public char getGapCharacter()
760   {
761     return getAlignment().getGapCharacter();
762   }
763
764   /**
765    * DOCUMENT ME!
766    * 
767    * @param gap
768    *          DOCUMENT ME!
769    */
770   public void setGapCharacter(char gap)
771   {
772     if (getAlignment() != null)
773     {
774       getAlignment().setGapCharacter(gap);
775     }
776   }
777
778   /**
779    * DOCUMENT ME!
780    * 
781    * @return DOCUMENT ME!
782    */
783   public ColumnSelection getColumnSelection()
784   {
785     return colSel;
786   }
787
788   /**
789    * DOCUMENT ME!
790    * 
791    * @param tree
792    *          DOCUMENT ME!
793    */
794   public void setCurrentTree(NJTree tree)
795   {
796     currentTree = tree;
797   }
798
799   /**
800    * DOCUMENT ME!
801    * 
802    * @return DOCUMENT ME!
803    */
804   public NJTree getCurrentTree()
805   {
806     return currentTree;
807   }
808
809   /**
810    * DOCUMENT ME!
811    * 
812    * @return DOCUMENT ME!
813    */
814   public boolean getShowJVSuffix()
815   {
816     return showJVSuffix;
817   }
818
819   /**
820    * DOCUMENT ME!
821    * 
822    * @param b
823    *          DOCUMENT ME!
824    */
825   public void setShowJVSuffix(boolean b)
826   {
827     showJVSuffix = b;
828   }
829
830   /**
831    * DOCUMENT ME!
832    * 
833    * @return DOCUMENT ME!
834    */
835   public boolean getShowAnnotation()
836   {
837     return showAnnotation;
838   }
839
840   /**
841    * DOCUMENT ME!
842    * 
843    * @param b
844    *          DOCUMENT ME!
845    */
846   public void setShowAnnotation(boolean b)
847   {
848     showAnnotation = b;
849   }
850
851   /**
852    * DOCUMENT ME!
853    * 
854    * @return DOCUMENT ME!
855    */
856   public boolean getScaleAboveWrapped()
857   {
858     return scaleAboveWrapped;
859   }
860
861   /**
862    * DOCUMENT ME!
863    * 
864    * @return DOCUMENT ME!
865    */
866   public boolean getScaleLeftWrapped()
867   {
868     return scaleLeftWrapped;
869   }
870
871   /**
872    * DOCUMENT ME!
873    * 
874    * @return DOCUMENT ME!
875    */
876   public boolean getScaleRightWrapped()
877   {
878     return scaleRightWrapped;
879   }
880
881   /**
882    * DOCUMENT ME!
883    * 
884    * @param b
885    *          DOCUMENT ME!
886    */
887   public void setScaleAboveWrapped(boolean b)
888   {
889     scaleAboveWrapped = b;
890   }
891
892   /**
893    * DOCUMENT ME!
894    * 
895    * @param b
896    *          DOCUMENT ME!
897    */
898   public void setScaleLeftWrapped(boolean b)
899   {
900     scaleLeftWrapped = b;
901   }
902
903   /**
904    * DOCUMENT ME!
905    * 
906    * @param b
907    *          DOCUMENT ME!
908    */
909   public void setScaleRightWrapped(boolean b)
910   {
911     scaleRightWrapped = b;
912   }
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
926
927   public boolean getShowHiddenMarkers()
928   {
929     return showHiddenMarkers;
930   }
931
932   public void setShowHiddenMarkers(boolean show)
933   {
934     showHiddenMarkers = show;
935   }
936
937   public Color getSequenceColour(SequenceI seq)
938   {
939     if (sequenceColours == null || !sequenceColours.containsKey(seq))
940     {
941       return Color.white;
942     }
943     else
944     {
945       return (Color) sequenceColours.get(seq);
946     }
947   }
948
949   public void setSequenceColour(SequenceI seq, Color col)
950   {
951     if (sequenceColours == null)
952     {
953       sequenceColours = new Hashtable();
954     }
955
956     if (col == null)
957     {
958       sequenceColours.remove(seq);
959     }
960     else
961     {
962       sequenceColours.put(seq, col);
963     }
964   }
965
966   /**
967    * returns the visible column regions of the alignment
968    * 
969    * @param selectedRegionOnly
970    *          true to just return the contigs intersecting with the selected
971    *          area
972    * @return
973    */
974   public int[] getViewAsVisibleContigs(boolean selectedRegionOnly)
975   {
976     int[] viscontigs = null;
977     int start = 0, end = 0;
978     if (selectedRegionOnly && selectionGroup != null)
979     {
980       start = selectionGroup.getStartRes();
981       end = selectionGroup.getEndRes() + 1;
982     }
983     else
984     {
985       end = alignment.getWidth();
986     }
987     viscontigs = colSel.getVisibleContigs(start, end);
988     return viscontigs;
989   }
990
991   /**
992    * get hash of undo and redo list for the alignment
993    * 
994    * @return long[] { historyList.hashCode, redoList.hashCode };
995    */
996   public long[] getUndoRedoHash()
997   {
998     if (historyList == null || redoList == null)
999       return new long[]
1000       { -1, -1 };
1001     return new long[]
1002     { historyList.hashCode(), this.redoList.hashCode() };
1003   }
1004
1005   /**
1006    * test if a particular set of hashcodes are different to the hashcodes for
1007    * the undo and redo list.
1008    * 
1009    * @param undoredo
1010    *          the stored set of hashcodes as returned by getUndoRedoHash
1011    * @return true if the hashcodes differ (ie the alignment has been edited) or
1012    *         the stored hashcode array differs in size
1013    */
1014   public boolean isUndoRedoHashModified(long[] undoredo)
1015   {
1016     if (undoredo == null)
1017     {
1018       return true;
1019     }
1020     long[] cstate = getUndoRedoHash();
1021     if (cstate.length != undoredo.length)
1022     {
1023       return true;
1024     }
1025
1026     for (int i = 0; i < cstate.length; i++)
1027     {
1028       if (cstate[i] != undoredo[i])
1029       {
1030         return true;
1031       }
1032     }
1033     return false;
1034   }
1035
1036   public boolean getCentreColumnLabels()
1037   {
1038     return centreColumnLabels;
1039   }
1040
1041   public void setCentreColumnLabels(boolean centrecolumnlabels)
1042   {
1043     centreColumnLabels = centrecolumnlabels;
1044   }
1045
1046   public void updateSequenceIdColours()
1047   {
1048     if (sequenceColours == null)
1049     {
1050       sequenceColours = new Hashtable();
1051     }
1052     for (SequenceGroup sg:alignment.getGroups())
1053     {
1054       if (sg.idColour != null)
1055       {
1056         for (SequenceI s:sg.getSequences(getHiddenRepSequences()))
1057         {
1058           sequenceColours.put(s, sg.idColour);
1059         }
1060       }
1061     }
1062   }
1063
1064   /**
1065    * enable or disable the display of Database Cross References in the sequence
1066    * ID tooltip
1067    */
1068   public void setShowDbRefs(boolean show)
1069   {
1070     showdbrefs = show;
1071   }
1072
1073   /**
1074    * 
1075    * @return true if Database References are to be displayed on tooltips.
1076    */
1077   public boolean isShowDbRefs()
1078   {
1079     return showdbrefs;
1080   }
1081
1082   /**
1083    * 
1084    * @return true if Non-positional features are to be displayed on tooltips.
1085    */
1086   public boolean isShowNpFeats()
1087   {
1088     return shownpfeats;
1089   }
1090
1091   /**
1092    * enable or disable the display of Non-Positional sequence features in the
1093    * sequence ID tooltip
1094    * 
1095    * @param show
1096    */
1097   public void setShowNpFeats(boolean show)
1098   {
1099     shownpfeats = show;
1100   }
1101
1102   /**
1103    * 
1104    * @return true if view has hidden rows
1105    */
1106   public boolean hasHiddenRows()
1107   {
1108     return hasHiddenRows;
1109   }
1110
1111   /**
1112    * 
1113    * @return true if view has hidden columns
1114    */
1115   public boolean hasHiddenColumns()
1116   {
1117     return hasHiddenColumns;
1118   }
1119
1120   /**
1121    * when set, view will scroll to show the highlighted position
1122    */
1123   public boolean followHighlight = true;
1124
1125   /**
1126    * @return true if view should scroll to show the highlighted region of a
1127    *         sequence
1128    * @return
1129    */
1130   public boolean getFollowHighlight()
1131   {
1132     return followHighlight;
1133   }
1134
1135   public boolean followSelection = true;
1136
1137   /**
1138    * @return true if view selection should always follow the selections
1139    *         broadcast by other selection sources
1140    */
1141   public boolean getFollowSelection()
1142   {
1143     return followSelection;
1144   }
1145
1146   boolean showSeqFeaturesHeight;
1147
1148   public void sendSelection()
1149   {
1150     jalview.structure.StructureSelectionManager
1151             .getStructureSelectionManager(Desktop.instance).sendSelection(
1152                     new SequenceGroup(getSelectionGroup()),
1153                     new ColumnSelection(getColumnSelection()), this);
1154   }
1155
1156   public void setShowSequenceFeaturesHeight(boolean selected)
1157   {
1158     showSeqFeaturesHeight = selected;
1159   }
1160
1161   public boolean getShowSequenceFeaturesHeight()
1162   {
1163     return showSeqFeaturesHeight;
1164   }
1165
1166   /**
1167    * return the alignPanel containing the given viewport. Use this to get the
1168    * components currently handling the given viewport.
1169    * 
1170    * @param av
1171    * @return null or an alignPanel guaranteed to have non-null alignFrame
1172    *         reference
1173    */
1174   public AlignmentPanel getAlignPanel()
1175   {
1176     AlignmentPanel[] aps = PaintRefresher.getAssociatedPanels(this
1177             .getSequenceSetId());
1178     AlignmentPanel ap = null;
1179     for (int p = 0; aps != null && p < aps.length; p++)
1180     {
1181       if (aps[p].av == this)
1182       {
1183         return aps[p];
1184       }
1185     }
1186     return null;
1187   }
1188
1189   public boolean getSortByTree()
1190   {
1191     return sortByTree;
1192   }
1193
1194   public void setSortByTree(boolean sort)
1195   {
1196     sortByTree = sort;
1197   }
1198
1199   /**
1200    * synthesize a column selection if none exists so it covers the given
1201    * selection group. if wholewidth is false, no column selection is made if the
1202    * selection group covers the whole alignment width.
1203    * 
1204    * @param sg
1205    * @param wholewidth
1206    */
1207   public void expandColSelection(SequenceGroup sg, boolean wholewidth)
1208   {
1209     int sgs, sge;
1210     if (sg != null
1211             && (sgs = sg.getStartRes()) >= 0
1212             && sg.getStartRes() <= (sge = sg.getEndRes())
1213             && (colSel == null || colSel.getSelected() == null || colSel
1214                     .getSelected().size() == 0))
1215     {
1216       if (!wholewidth && alignment.getWidth() == (1 + sge - sgs))
1217       {
1218         // do nothing
1219         return;
1220       }
1221       if (colSel == null)
1222       {
1223         colSel = new ColumnSelection();
1224       }
1225       for (int cspos = sg.getStartRes(); cspos <= sg.getEndRes(); cspos++)
1226       {
1227         colSel.addElement(cspos);
1228       }
1229     }
1230   }
1231
1232   public StructureSelectionManager getStructureSelectionManager()
1233   {
1234     return StructureSelectionManager
1235             .getStructureSelectionManager(Desktop.instance);
1236   }
1237
1238   /**
1239    * 
1240    * @param pdbEntries
1241    * @return a series of SequenceI arrays, one for each PDBEntry, listing which
1242    *         sequence in the alignment holds a reference to it
1243    */
1244   public SequenceI[][] collateForPDB(PDBEntry[] pdbEntries)
1245   {
1246     ArrayList<SequenceI[]> seqvectors = new ArrayList<SequenceI[]>();
1247     for (PDBEntry pdb : pdbEntries)
1248     {
1249       ArrayList<SequenceI> seqs = new ArrayList<SequenceI>();
1250       for (int i = 0; i < alignment.getHeight(); i++)
1251       {
1252         Vector pdbs = alignment.getSequenceAt(i).getDatasetSequence()
1253                 .getPDBId();
1254         if (pdbs == null)
1255           continue;
1256         SequenceI sq;
1257         for (int p = 0; p < pdbs.size(); p++)
1258         {
1259           PDBEntry p1 = (PDBEntry) pdbs.elementAt(p);
1260           if (p1.getId().equals(pdb.getId()))
1261           {
1262             if (!seqs.contains(sq = alignment.getSequenceAt(i)))
1263               seqs.add(sq);
1264
1265             continue;
1266           }
1267         }
1268       }
1269       seqvectors.add(seqs.toArray(new SequenceI[seqs.size()]));
1270     }
1271     return seqvectors.toArray(new SequenceI[seqvectors.size()][]);
1272   }
1273
1274   
1275   public boolean isNormaliseSequenceLogo()
1276   {
1277     return normaliseSequenceLogo;
1278   }
1279
1280   public void setNormaliseSequenceLogo(boolean state)
1281   {
1282     normaliseSequenceLogo = state;
1283   }
1284
1285
1286   /**
1287    * 
1288    * @return true if alignment characters should be displayed 
1289    */
1290   public boolean isValidCharWidth()
1291   {
1292     return validCharWidth;
1293   }
1294 }