new selection manipulation method
[jalview.git] / src / jalview / gui / AlignViewport.java
1 /*
2  * Jalview - A Sequence Alignment Editor and Viewer (Development Version 2.4.1)
3  * Copyright (C) 2009 AM Waterhouse, J Procter, G Barton, M Clamp, S Searle
4  * 
5  * This program is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU General Public License
7  * as published by the Free Software Foundation; either version 2
8  * of the License, or (at your option) any later version.
9  * 
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  * 
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
18  */
19 /*
20  * Jalview - A Sequence Alignment Editor and Viewer
21  * Copyright (C) 2007 AM Waterhouse, J Procter, G Barton, M Clamp, S Searle
22  *
23  * This program is free software; you can redistribute it and/or
24  * modify it under the terms of the GNU General Public License
25  * as published by the Free Software Foundation; either version 2
26  * of the License, or (at your option) any later version.
27  *
28  * This program is distributed in the hope that it will be useful,
29  * but WITHOUT ANY WARRANTY; without even the implied warranty of
30  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
31  * GNU General Public License for more details.
32  *
33  * You should have received a copy of the GNU General Public License
34  * along with this program; if not, write to the Free Software
35  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
36  */
37 package jalview.gui;
38
39 import java.util.*;
40
41 import java.awt.*;
42
43 import jalview.analysis.*;
44
45 import jalview.bin.*;
46
47 import jalview.datamodel.*;
48
49 import jalview.schemes.*;
50 import jalview.structure.SelectionSource;
51 import jalview.structure.StructureSelectionManager;
52
53 /**
54  * DOCUMENT ME!
55  * 
56  * @author $author$
57  * @version $Revision$
58  */
59 public class AlignViewport implements SelectionSource
60 {
61   private static final int RIGHT_JUSTIFY = 1;
62
63   int startRes;
64
65   int endRes;
66
67   int startSeq;
68
69   int endSeq;
70
71   boolean showJVSuffix = true;
72
73   boolean showText = true;
74
75   boolean showColourText = false;
76
77   boolean showBoxes = true;
78
79   boolean wrapAlignment = false;
80
81   boolean renderGaps = true;
82
83   boolean showSequenceFeatures = false;
84
85   boolean showAnnotation = true;
86
87   boolean colourAppliesToAllGroups = true;
88
89   ColourSchemeI globalColourScheme = null;
90
91   boolean conservationColourSelected = false;
92
93   boolean abovePIDThreshold = false;
94
95   SequenceGroup selectionGroup;
96
97   int charHeight;
98
99   int charWidth;
100
101   boolean validCharWidth;
102
103   int wrappedWidth;
104
105   Font font;
106
107   boolean seqNameItalics;
108
109   AlignmentI alignment;
110
111   ColumnSelection colSel = new ColumnSelection();
112
113   int threshold;
114
115   int increment;
116
117   NJTree currentTree = null;
118
119   boolean scaleAboveWrapped = false;
120
121   boolean scaleLeftWrapped = true;
122
123   boolean scaleRightWrapped = true;
124
125   boolean hasHiddenColumns = false;
126
127   boolean hasHiddenRows = false;
128
129   boolean showHiddenMarkers = true;
130
131   boolean cursorMode = false;
132
133   /**
134    * Keys are the feature types which are
135    * currently visible. Note: Values are not used!
136    */
137   Hashtable featuresDisplayed = null;
138
139   /** DOCUMENT ME!! */
140   public Hashtable[] hconsensus;
141
142   AlignmentAnnotation consensus;
143
144   AlignmentAnnotation conservation;
145
146   AlignmentAnnotation quality;
147   AlignmentAnnotation[] groupConsensus;
148   AlignmentAnnotation[] groupConservation;
149   
150   boolean autoCalculateConsensus = true;
151
152   /** DOCUMENT ME!! */
153   public int ConsPercGaps = 25; // JBPNote : This should be a scalable property!
154
155   // JBPNote Prolly only need this in the applet version.
156   private java.beans.PropertyChangeSupport changeSupport = new java.beans.PropertyChangeSupport(
157           this);
158
159   boolean ignoreGapsInConsensusCalculation = false;
160
161   boolean isDataset = false;
162
163   boolean antiAlias = false;
164
165   boolean padGaps = false;
166
167   Rectangle explodedPosition;
168
169   String viewName;
170
171   String sequenceSetID;
172
173   boolean gatherViewsHere = false;
174
175   Stack historyList = new Stack();
176
177   Stack redoList = new Stack();
178
179   Hashtable sequenceColours;
180
181   int thresholdTextColour = 0;
182
183   Color textColour = Color.black;
184
185   Color textColour2 = Color.white;
186
187   boolean rightAlignIds = false;
188
189   Hashtable hiddenRepSequences;
190
191   boolean sortByTree;
192
193   /**
194    * Creates a new AlignViewport object.
195    * 
196    * @param al alignment to view
197    */
198   public AlignViewport(AlignmentI al)
199   {
200     setAlignment(al);
201     init();
202   }
203   /**
204    * Create a new AlignViewport object with a specific sequence set ID
205    * @param al
206    * @param seqsetid (may be null - but potential for ambiguous constructor exception)
207    */
208   public AlignViewport(AlignmentI al, String seqsetid)
209   {
210     this(al,seqsetid,null);
211   }
212   public AlignViewport(AlignmentI al, String seqsetid, String viewid)
213   {
214     sequenceSetID = seqsetid;
215     viewId = viewid;
216     // TODO remove these once 2.4.VAMSAS release finished
217     if (Cache.log!=null && Cache.log.isDebugEnabled() && seqsetid!=null) { Cache.log.debug("Setting viewport's sequence set id : "+sequenceSetID); }
218     if (Cache.log!=null && Cache.log.isDebugEnabled() && viewId!=null) { Cache.log.debug("Setting viewport's view id : "+viewId); }
219     setAlignment(al);
220     init();
221   }
222
223   /**
224    * Create a new AlignViewport with hidden regions
225    * 
226    * @param al
227    *                AlignmentI
228    * @param hiddenColumns
229    *                ColumnSelection
230    */
231   public AlignViewport(AlignmentI al, ColumnSelection hiddenColumns)
232   {
233     setAlignment(al);
234     if (hiddenColumns != null)
235     {
236       this.colSel = hiddenColumns;
237       if (hiddenColumns.getHiddenColumns() != null && hiddenColumns.getHiddenColumns().size()>0)
238       {
239         hasHiddenColumns = true;
240       } else {
241         hasHiddenColumns = false;
242       }
243     }
244     init();
245   }
246   /**
247    * New viewport with hidden columns and an existing sequence set id
248    * @param al
249    * @param hiddenColumns
250    * @param seqsetid (may be null)
251    */
252   public AlignViewport(AlignmentI al, ColumnSelection hiddenColumns, String seqsetid)
253   {
254     this(al,hiddenColumns,seqsetid,null);
255   }
256   /**
257    * New viewport with hidden columns and an existing sequence set id and viewid
258    * @param al
259    * @param hiddenColumns
260    * @param seqsetid (may be null)
261    * @param viewid (may be null)
262    */
263   public AlignViewport(AlignmentI al, ColumnSelection hiddenColumns, String seqsetid, String viewid)
264   {
265     sequenceSetID = seqsetid;
266     viewId = viewid;
267     // TODO remove these once 2.4.VAMSAS release finished
268     if (Cache.log!=null && Cache.log.isDebugEnabled() && seqsetid!=null) { Cache.log.debug("Setting viewport's sequence set id : "+sequenceSetID); }
269     if (Cache.log!=null && Cache.log.isDebugEnabled() && viewId!=null) { Cache.log.debug("Setting viewport's view id : "+viewId); }
270     setAlignment(al);
271     if (hiddenColumns != null)
272     {
273       this.colSel = hiddenColumns;
274       if (hiddenColumns.getHiddenColumns() != null && hiddenColumns.getHiddenColumns().size()>0)
275       {
276         hasHiddenColumns = true;
277       } else {
278         hasHiddenColumns = false;
279       }
280     }
281     init();
282   }
283
284   void init()
285   {
286     this.startRes = 0;
287     this.endRes = alignment.getWidth() - 1;
288     this.startSeq = 0;
289     this.endSeq = alignment.getHeight() - 1;
290
291     antiAlias = Cache.getDefault("ANTI_ALIAS", false);
292
293     showJVSuffix = Cache.getDefault("SHOW_JVSUFFIX", true);
294     showAnnotation = Cache.getDefault("SHOW_ANNOTATIONS", true);
295
296     rightAlignIds = Cache.getDefault("RIGHT_ALIGN_IDS", false);
297     centreColumnLabels = Cache.getDefault("CENTRE_COLUMN_LABELS", false);
298     autoCalculateConsensus = Cache.getDefault("AUTO_CALC_CONSENSUS", true);
299
300     padGaps = Cache.getDefault("PAD_GAPS", true);
301     shownpfeats = Cache.getDefault("SHOW_NPFEATS_TOOLTIP",true);
302     showdbrefs = Cache.getDefault("SHOW_DBREFS_TOOLTIP",true);
303     
304     String fontName = Cache.getDefault("FONT_NAME", "SansSerif");
305     String fontStyle = Cache.getDefault("FONT_STYLE", Font.PLAIN + "");
306     String fontSize = Cache.getDefault("FONT_SIZE", "10");
307
308     seqNameItalics = Cache.getDefault("ID_ITALICS", true);
309
310     int style = 0;
311
312     if (fontStyle.equals("bold"))
313     {
314       style = 1;
315     }
316     else if (fontStyle.equals("italic"))
317     {
318       style = 2;
319     }
320
321     setFont(new Font(fontName, style, Integer.parseInt(fontSize)));
322
323     alignment
324             .setGapCharacter(Cache.getDefault("GAP_SYMBOL", "-").charAt(0));
325
326     // We must set conservation and consensus before setting colour,
327     // as Blosum and Clustal require this to be done
328     if (hconsensus == null && !isDataset)
329     {
330       if (!alignment.isNucleotide())
331       {
332         conservation = new AlignmentAnnotation("Conservation",
333                 "Conservation of total alignment less than " + ConsPercGaps
334                         + "% gaps", new Annotation[1], 0f, 11f,
335                 AlignmentAnnotation.BAR_GRAPH);
336         conservation.hasText = true;
337         conservation.autoCalculated = true;
338
339         if (Cache.getDefault("SHOW_CONSERVATION", true))
340         {
341           alignment.addAnnotation(conservation);
342         }
343
344         if (Cache.getDefault("SHOW_QUALITY", true))
345         {
346           quality = new AlignmentAnnotation("Quality",
347                   "Alignment Quality based on Blosum62 scores",
348                   new Annotation[1], 0f, 11f, AlignmentAnnotation.BAR_GRAPH);
349           quality.hasText = true;
350           quality.autoCalculated = true;
351
352           alignment.addAnnotation(quality);
353         }
354         showGroupConservation = Cache.getDefault("SHOW_GROUP_CONSERVATION", false);
355         
356         {
357           
358         }
359       }
360       showConsensusHistogram = Cache.getDefault("SHOW_CONSENSUS_HISTOGRAM", true);
361       showSequenceLogo = Cache.getDefault("SHOW_CONSENSUS_LOGO", false);
362       showGroupConsensus = Cache.getDefault("SHOW_GROUP_CONSENSUS", false);
363       // TODO: add menu option action that nulls or creates consensus object depending on if the user wants to see the annotation or not in a specific alignment
364       consensus = new AlignmentAnnotation("Consensus", "PID",
365               new Annotation[1], 0f, 100f, AlignmentAnnotation.BAR_GRAPH);
366       consensus.hasText = true;
367       consensus.autoCalculated = true;
368
369       if (Cache.getDefault("SHOW_IDENTITY", true))
370       {
371         alignment.addAnnotation(consensus);
372       }
373     }
374
375     if (jalview.bin.Cache.getProperty("DEFAULT_COLOUR") != null)
376     {
377       globalColourScheme = ColourSchemeProperty.getColour(alignment,
378               jalview.bin.Cache.getProperty("DEFAULT_COLOUR"));
379
380       if (globalColourScheme instanceof UserColourScheme)
381       {
382         globalColourScheme = UserDefinedColours.loadDefaultColours();
383         ((UserColourScheme) globalColourScheme).setThreshold(0,
384                 getIgnoreGapsConsensus());
385       }
386
387       if (globalColourScheme != null)
388       {
389         globalColourScheme.setConsensus(hconsensus);
390       }
391     }
392
393     wrapAlignment = jalview.bin.Cache.getDefault("WRAP_ALIGNMENT", false);
394     showUnconserved = jalview.bin.Cache.getDefault("SHOW_UNCONSERVED", false);
395     sortByTree = jalview.bin.Cache.getDefault("SORT_BY_TREE", false);
396   }
397
398   /**
399    * set the flag
400    * 
401    * @param b
402    *             features are displayed if true
403    */
404   public void setShowSequenceFeatures(boolean b)
405   {
406     showSequenceFeatures = b;
407   }
408
409   public boolean getShowSequenceFeatures()
410   {
411     return showSequenceFeatures;
412   }
413
414   ConservationThread conservationThread;
415
416   ConsensusThread consensusThread;
417
418   boolean consUpdateNeeded = false;
419
420   static boolean UPDATING_CONSENSUS = false;
421
422   static boolean UPDATING_CONSERVATION = false;
423
424   boolean updatingConsensus = false;
425
426   boolean updatingConservation = false;
427
428   /**
429    * centre columnar annotation labels in displayed alignment annotation TODO:
430    * add to jalviewXML and annotation display settings
431    */
432   boolean centreColumnLabels = false;
433
434   private boolean showdbrefs;
435
436   private boolean shownpfeats;
437
438   /**
439    * consensus annotation includes all percentage for all symbols in column
440    */
441   private boolean includeAllConsensusSymbols=true;
442
443   /**
444    * trigger update of conservation annotation
445    */
446   public void updateConservation(final AlignmentPanel ap)
447   {
448     // see note in mantis : issue number 8585
449     if (alignment.isNucleotide() || conservation == null || !autoCalculateConsensus)
450     {
451       return;
452     }
453
454     conservationThread = new ConservationThread(this, ap);
455     conservationThread.start();
456   }
457
458   /**
459    * trigger update of consensus annotation
460    */
461   public void updateConsensus(final AlignmentPanel ap)
462   {
463     // see note in mantis : issue number 8585
464     if (consensus == null || !autoCalculateConsensus)
465     {
466       return;
467     }
468     consensusThread = new ConsensusThread(ap);
469     consensusThread.start();
470   }
471
472   class ConsensusThread extends Thread
473   {
474     AlignmentPanel ap;
475
476     public ConsensusThread(AlignmentPanel ap)
477     {
478       this.ap = ap;
479     }
480
481     public void run()
482     {
483       updatingConsensus = true;
484       while (UPDATING_CONSENSUS)
485       {
486         try
487         {
488           if (ap != null)
489           {
490             ap.paintAlignment(false);
491           }
492
493           Thread.sleep(200);
494         } catch (Exception ex)
495         {
496           ex.printStackTrace();
497         }
498       }
499
500       UPDATING_CONSENSUS = true;
501
502       try
503       {
504         int aWidth = (alignment != null) ? alignment.getWidth() : 0; // null
505                                                                       // pointer
506                                                                       // possibility
507                                                                       // here.
508         if (aWidth < 0)
509         {
510           return;
511         }
512
513         consensus.annotations = null;
514         consensus.annotations = new Annotation[aWidth];
515
516         hconsensus = new Hashtable[aWidth];
517         AAFrequency.calculate(alignment.getSequencesArray(), 0, alignment
518                 .getWidth(), hconsensus, includeAllConsensusSymbols);
519         AAFrequency.completeConsensus(consensus,hconsensus,0,aWidth,ignoreGapsInConsensusCalculation, includeAllConsensusSymbols);
520         
521         if (globalColourScheme != null)
522         {
523           globalColourScheme.setConsensus(hconsensus);
524         }
525
526       } catch (OutOfMemoryError error)
527       {
528         alignment.deleteAnnotation(consensus);
529
530         consensus = null;
531         hconsensus = null;
532         new OOMWarning("calculating consensus", error);
533       }
534       UPDATING_CONSENSUS = false;
535       updatingConsensus = false;
536
537       if (ap != null)
538       {
539         ap.paintAlignment(true);
540       }
541     }
542   }
543
544   /**
545    * get the consensus sequence as displayed under the PID consensus annotation
546    * row.
547    * 
548    * @return consensus sequence as a new sequence object
549    */
550   public SequenceI getConsensusSeq()
551   {
552     if (consensus == null)
553     {
554       updateConsensus(null);
555     }
556     if (consensus == null)
557     {
558       return null;
559     }
560     StringBuffer seqs = new StringBuffer();
561     for (int i = 0; i < consensus.annotations.length; i++)
562     {
563       if (consensus.annotations[i] != null)
564       {
565         if (consensus.annotations[i].description.charAt(0) == '[')
566         {
567           seqs.append(consensus.annotations[i].description.charAt(1));
568         }
569         else
570         {
571           seqs.append(consensus.annotations[i].displayCharacter);
572         }
573       }
574     }
575
576     SequenceI sq = new Sequence("Consensus", seqs.toString());
577     sq.setDescription("Percentage Identity Consensus "
578             + ((ignoreGapsInConsensusCalculation) ? " without gaps" : ""));
579     return sq;
580   }
581
582   /**
583    * DOCUMENT ME!
584    * 
585    * @return DOCUMENT ME!
586    */
587   public SequenceGroup getSelectionGroup()
588   {
589     return selectionGroup;
590   }
591
592   /**
593    * DOCUMENT ME!
594    * 
595    * @param sg
596    *                DOCUMENT ME!
597    */
598   public void setSelectionGroup(SequenceGroup sg)
599   {
600     selectionGroup = sg;    
601   }
602
603   /**
604    * DOCUMENT ME!
605    * 
606    * @return DOCUMENT ME!
607    */
608   public boolean getConservationSelected()
609   {
610     return conservationColourSelected;
611   }
612
613   /**
614    * DOCUMENT ME!
615    * 
616    * @param b
617    *                DOCUMENT ME!
618    */
619   public void setConservationSelected(boolean b)
620   {
621     conservationColourSelected = b;
622   }
623
624   /**
625    * DOCUMENT ME!
626    * 
627    * @return DOCUMENT ME!
628    */
629   public boolean getAbovePIDThreshold()
630   {
631     return abovePIDThreshold;
632   }
633
634   /**
635    * DOCUMENT ME!
636    * 
637    * @param b
638    *                DOCUMENT ME!
639    */
640   public void setAbovePIDThreshold(boolean b)
641   {
642     abovePIDThreshold = b;
643   }
644
645   /**
646    * DOCUMENT ME!
647    * 
648    * @return DOCUMENT ME!
649    */
650   public int getStartRes()
651   {
652     return startRes;
653   }
654
655   /**
656    * DOCUMENT ME!
657    * 
658    * @return DOCUMENT ME!
659    */
660   public int getEndRes()
661   {
662     return endRes;
663   }
664
665   /**
666    * DOCUMENT ME!
667    * 
668    * @return DOCUMENT ME!
669    */
670   public int getStartSeq()
671   {
672     return startSeq;
673   }
674
675   /**
676    * DOCUMENT ME!
677    * 
678    * @param cs
679    *                DOCUMENT ME!
680    */
681   public void setGlobalColourScheme(ColourSchemeI cs)
682   {
683     globalColourScheme = cs;
684   }
685
686   /**
687    * DOCUMENT ME!
688    * 
689    * @return DOCUMENT ME!
690    */
691   public ColourSchemeI getGlobalColourScheme()
692   {
693     return globalColourScheme;
694   }
695
696   /**
697    * DOCUMENT ME!
698    * 
699    * @param res
700    *                DOCUMENT ME!
701    */
702   public void setStartRes(int res)
703   {
704     this.startRes = res;
705   }
706
707   /**
708    * DOCUMENT ME!
709    * 
710    * @param seq
711    *                DOCUMENT ME!
712    */
713   public void setStartSeq(int seq)
714   {
715     this.startSeq = seq;
716   }
717
718   /**
719    * DOCUMENT ME!
720    * 
721    * @param res
722    *                DOCUMENT ME!
723    */
724   public void setEndRes(int res)
725   {
726     if (res > (alignment.getWidth() - 1))
727     {
728       // log.System.out.println(" Corrected res from " + res + " to maximum " +
729       // (alignment.getWidth()-1));
730       res = alignment.getWidth() - 1;
731     }
732
733     if (res < 0)
734     {
735       res = 0;
736     }
737
738     this.endRes = res;
739   }
740
741   /**
742    * DOCUMENT ME!
743    * 
744    * @param seq
745    *                DOCUMENT ME!
746    */
747   public void setEndSeq(int seq)
748   {
749     if (seq > alignment.getHeight())
750     {
751       seq = alignment.getHeight();
752     }
753
754     if (seq < 0)
755     {
756       seq = 0;
757     }
758
759     this.endSeq = seq;
760   }
761
762   /**
763    * DOCUMENT ME!
764    * 
765    * @return DOCUMENT ME!
766    */
767   public int getEndSeq()
768   {
769     return endSeq;
770   }
771
772   /**
773    * DOCUMENT ME!
774    * 
775    * @param f
776    *                DOCUMENT ME!
777    */
778   public void setFont(Font f)
779   {
780     font = f;
781
782     Container c = new Container();
783
784     java.awt.FontMetrics fm = c.getFontMetrics(font);
785     setCharHeight(fm.getHeight());
786     setCharWidth(fm.charWidth('M'));
787     validCharWidth = true;
788   }
789
790   /**
791    * DOCUMENT ME!
792    * 
793    * @return DOCUMENT ME!
794    */
795   public Font getFont()
796   {
797     return font;
798   }
799
800   /**
801    * DOCUMENT ME!
802    * 
803    * @param w
804    *                DOCUMENT ME!
805    */
806   public void setCharWidth(int w)
807   {
808     this.charWidth = w;
809   }
810
811   /**
812    * DOCUMENT ME!
813    * 
814    * @return DOCUMENT ME!
815    */
816   public int getCharWidth()
817   {
818     return charWidth;
819   }
820
821   /**
822    * DOCUMENT ME!
823    * 
824    * @param h
825    *                DOCUMENT ME!
826    */
827   public void setCharHeight(int h)
828   {
829     this.charHeight = h;
830   }
831
832   /**
833    * DOCUMENT ME!
834    * 
835    * @return DOCUMENT ME!
836    */
837   public int getCharHeight()
838   {
839     return charHeight;
840   }
841
842   /**
843    * DOCUMENT ME!
844    * 
845    * @param w
846    *                DOCUMENT ME!
847    */
848   public void setWrappedWidth(int w)
849   {
850     this.wrappedWidth = w;
851   }
852
853   /**
854    * DOCUMENT ME!
855    * 
856    * @return DOCUMENT ME!
857    */
858   public int getWrappedWidth()
859   {
860     return wrappedWidth;
861   }
862
863   /**
864    * DOCUMENT ME!
865    * 
866    * @return DOCUMENT ME!
867    */
868   public AlignmentI getAlignment()
869   {
870     return alignment;
871   }
872
873   /**
874    * DOCUMENT ME!
875    * 
876    * @param align
877    *                DOCUMENT ME!
878    */
879   public void setAlignment(AlignmentI align)
880   {
881     if (alignment != null && alignment.getCodonFrames() != null)
882     {
883       StructureSelectionManager.getStructureSelectionManager()
884               .removeMappings(alignment.getCodonFrames());
885     }
886     this.alignment = align;
887     if (alignment.getCodonFrames() != null)
888     {
889       StructureSelectionManager.getStructureSelectionManager().addMappings(
890               alignment.getCodonFrames());
891     }
892   }
893
894   /**
895    * DOCUMENT ME!
896    * 
897    * @param state
898    *                DOCUMENT ME!
899    */
900   public void setWrapAlignment(boolean state)
901   {
902     wrapAlignment = state;
903   }
904
905   /**
906    * DOCUMENT ME!
907    * 
908    * @param state
909    *                DOCUMENT ME!
910    */
911   public void setShowText(boolean state)
912   {
913     showText = state;
914   }
915
916   /**
917    * DOCUMENT ME!
918    * 
919    * @param state
920    *                DOCUMENT ME!
921    */
922   public void setRenderGaps(boolean state)
923   {
924     renderGaps = state;
925   }
926
927   /**
928    * DOCUMENT ME!
929    * 
930    * @return DOCUMENT ME!
931    */
932   public boolean getColourText()
933   {
934     return showColourText;
935   }
936
937   /**
938    * DOCUMENT ME!
939    * 
940    * @param state
941    *                DOCUMENT ME!
942    */
943   public void setColourText(boolean state)
944   {
945     showColourText = state;
946   }
947
948   /**
949    * DOCUMENT ME!
950    * 
951    * @param state
952    *                DOCUMENT ME!
953    */
954   public void setShowBoxes(boolean state)
955   {
956     showBoxes = state;
957   }
958
959   /**
960    * DOCUMENT ME!
961    * 
962    * @return DOCUMENT ME!
963    */
964   public boolean getWrapAlignment()
965   {
966     return wrapAlignment;
967   }
968
969   /**
970    * DOCUMENT ME!
971    * 
972    * @return DOCUMENT ME!
973    */
974   public boolean getShowText()
975   {
976     return showText;
977   }
978
979   /**
980    * DOCUMENT ME!
981    * 
982    * @return DOCUMENT ME!
983    */
984   public boolean getShowBoxes()
985   {
986     return showBoxes;
987   }
988
989   /**
990    * DOCUMENT ME!
991    * 
992    * @return DOCUMENT ME!
993    */
994   public char getGapCharacter()
995   {
996     return getAlignment().getGapCharacter();
997   }
998
999   /**
1000    * DOCUMENT ME!
1001    * 
1002    * @param gap
1003    *                DOCUMENT ME!
1004    */
1005   public void setGapCharacter(char gap)
1006   {
1007     if (getAlignment() != null)
1008     {
1009       getAlignment().setGapCharacter(gap);
1010     }
1011   }
1012
1013   /**
1014    * DOCUMENT ME!
1015    * 
1016    * @param thresh
1017    *                DOCUMENT ME!
1018    */
1019   public void setThreshold(int thresh)
1020   {
1021     threshold = thresh;
1022   }
1023
1024   /**
1025    * DOCUMENT ME!
1026    * 
1027    * @return DOCUMENT ME!
1028    */
1029   public int getThreshold()
1030   {
1031     return threshold;
1032   }
1033
1034   /**
1035    * DOCUMENT ME!
1036    * 
1037    * @param inc
1038    *                DOCUMENT ME!
1039    */
1040   public void setIncrement(int inc)
1041   {
1042     increment = inc;
1043   }
1044
1045   /**
1046    * DOCUMENT ME!
1047    * 
1048    * @return DOCUMENT ME!
1049    */
1050   public int getIncrement()
1051   {
1052     return increment;
1053   }
1054
1055   /**
1056    * DOCUMENT ME!
1057    * 
1058    * @return DOCUMENT ME!
1059    */
1060   public ColumnSelection getColumnSelection()
1061   {
1062     return colSel;
1063   }
1064
1065   /**
1066    * DOCUMENT ME!
1067    * 
1068    * @param tree
1069    *                DOCUMENT ME!
1070    */
1071   public void setCurrentTree(NJTree tree)
1072   {
1073     currentTree = tree;
1074   }
1075
1076   /**
1077    * DOCUMENT ME!
1078    * 
1079    * @return DOCUMENT ME!
1080    */
1081   public NJTree getCurrentTree()
1082   {
1083     return currentTree;
1084   }
1085
1086   /**
1087    * DOCUMENT ME!
1088    * 
1089    * @param b
1090    *                DOCUMENT ME!
1091    */
1092   public void setColourAppliesToAllGroups(boolean b)
1093   {
1094     colourAppliesToAllGroups = b;
1095   }
1096
1097   /**
1098    * DOCUMENT ME!
1099    * 
1100    * @return DOCUMENT ME!
1101    */
1102   public boolean getColourAppliesToAllGroups()
1103   {
1104     return colourAppliesToAllGroups;
1105   }
1106
1107   /**
1108    * DOCUMENT ME!
1109    * 
1110    * @return DOCUMENT ME!
1111    */
1112   public boolean getShowJVSuffix()
1113   {
1114     return showJVSuffix;
1115   }
1116
1117   /**
1118    * DOCUMENT ME!
1119    * 
1120    * @param b
1121    *                DOCUMENT ME!
1122    */
1123   public void setShowJVSuffix(boolean b)
1124   {
1125     showJVSuffix = b;
1126   }
1127
1128   /**
1129    * DOCUMENT ME!
1130    * 
1131    * @return DOCUMENT ME!
1132    */
1133   public boolean getShowAnnotation()
1134   {
1135     return showAnnotation;
1136   }
1137
1138   /**
1139    * DOCUMENT ME!
1140    * 
1141    * @param b
1142    *                DOCUMENT ME!
1143    */
1144   public void setShowAnnotation(boolean b)
1145   {
1146     showAnnotation = b;
1147   }
1148
1149   /**
1150    * DOCUMENT ME!
1151    * 
1152    * @return DOCUMENT ME!
1153    */
1154   public boolean getScaleAboveWrapped()
1155   {
1156     return scaleAboveWrapped;
1157   }
1158
1159   /**
1160    * DOCUMENT ME!
1161    * 
1162    * @return DOCUMENT ME!
1163    */
1164   public boolean getScaleLeftWrapped()
1165   {
1166     return scaleLeftWrapped;
1167   }
1168
1169   /**
1170    * DOCUMENT ME!
1171    * 
1172    * @return DOCUMENT ME!
1173    */
1174   public boolean getScaleRightWrapped()
1175   {
1176     return scaleRightWrapped;
1177   }
1178
1179   /**
1180    * DOCUMENT ME!
1181    * 
1182    * @param b
1183    *                DOCUMENT ME!
1184    */
1185   public void setScaleAboveWrapped(boolean b)
1186   {
1187     scaleAboveWrapped = b;
1188   }
1189
1190   /**
1191    * DOCUMENT ME!
1192    * 
1193    * @param b
1194    *                DOCUMENT ME!
1195    */
1196   public void setScaleLeftWrapped(boolean b)
1197   {
1198     scaleLeftWrapped = b;
1199   }
1200
1201   /**
1202    * DOCUMENT ME!
1203    * 
1204    * @param b
1205    *                DOCUMENT ME!
1206    */
1207   public void setScaleRightWrapped(boolean b)
1208   {
1209     scaleRightWrapped = b;
1210   }
1211
1212   /**
1213    * Property change listener for changes in alignment
1214    * 
1215    * @param listener
1216    *                DOCUMENT ME!
1217    */
1218   public void addPropertyChangeListener(
1219           java.beans.PropertyChangeListener listener)
1220   {
1221     changeSupport.addPropertyChangeListener(listener);
1222   }
1223
1224   /**
1225    * DOCUMENT ME!
1226    * 
1227    * @param listener
1228    *                DOCUMENT ME!
1229    */
1230   public void removePropertyChangeListener(
1231           java.beans.PropertyChangeListener listener)
1232   {
1233     changeSupport.removePropertyChangeListener(listener);
1234   }
1235
1236   /**
1237    * Property change listener for changes in alignment
1238    * 
1239    * @param prop
1240    *                DOCUMENT ME!
1241    * @param oldvalue
1242    *                DOCUMENT ME!
1243    * @param newvalue
1244    *                DOCUMENT ME!
1245    */
1246   public void firePropertyChange(String prop, Object oldvalue,
1247           Object newvalue)
1248   {
1249     changeSupport.firePropertyChange(prop, oldvalue, newvalue);
1250   }
1251
1252   public void setIgnoreGapsConsensus(boolean b, AlignmentPanel ap)
1253   {
1254     ignoreGapsInConsensusCalculation = b;
1255     updateConsensus(ap);
1256     if (globalColourScheme != null)
1257     {
1258       globalColourScheme.setThreshold(globalColourScheme.getThreshold(),
1259               ignoreGapsInConsensusCalculation);
1260     }
1261   }
1262
1263   public boolean getIgnoreGapsConsensus()
1264   {
1265     return ignoreGapsInConsensusCalculation;
1266   }
1267
1268   public void setDataset(boolean b)
1269   {
1270     isDataset = b;
1271   }
1272
1273   public boolean isDataset()
1274   {
1275     return isDataset;
1276   }
1277
1278   public void hideSelectedColumns()
1279   {
1280     if (colSel.size() < 1)
1281     {
1282       return;
1283     }
1284
1285     colSel.hideSelectedColumns();
1286     setSelectionGroup(null);
1287
1288     hasHiddenColumns = true;
1289   }
1290
1291   public void hideColumns(int start, int end)
1292   {
1293     if (start == end)
1294     {
1295       colSel.hideColumns(start);
1296     }
1297     else
1298     {
1299       colSel.hideColumns(start, end);
1300     }
1301
1302     hasHiddenColumns = true;
1303   }
1304
1305   public void hideRepSequences(SequenceI repSequence, SequenceGroup sg)
1306   {
1307     int sSize = sg.getSize();
1308     if (sSize < 2)
1309     {
1310       return;
1311     }
1312
1313     if (hiddenRepSequences == null)
1314     {
1315       hiddenRepSequences = new Hashtable();
1316     }
1317
1318     hiddenRepSequences.put(repSequence, sg);
1319
1320     // Hide all sequences except the repSequence
1321     SequenceI[] seqs = new SequenceI[sSize - 1];
1322     int index = 0;
1323     for (int i = 0; i < sSize; i++)
1324     {
1325       if (sg.getSequenceAt(i) != repSequence)
1326       {
1327         if (index == sSize - 1)
1328         {
1329           return;
1330         }
1331
1332         seqs[index++] = sg.getSequenceAt(i);
1333       }
1334     }
1335     sg.setSeqrep(repSequence);
1336     sg.setHidereps(true);
1337     hideSequence(seqs);
1338
1339   }
1340
1341   public void hideAllSelectedSeqs()
1342   {
1343     if (selectionGroup == null || selectionGroup.getSize() < 1)
1344     {
1345       return;
1346     }
1347
1348     SequenceI[] seqs = selectionGroup.getSequencesInOrder(alignment);
1349
1350     hideSequence(seqs);
1351
1352     setSelectionGroup(null);
1353   }
1354
1355   public void hideSequence(SequenceI[] seq)
1356   {
1357     if (seq != null)
1358     {
1359       for (int i = 0; i < seq.length; i++)
1360       {
1361         alignment.getHiddenSequences().hideSequence(seq[i]);
1362       }
1363       hasHiddenRows = true;
1364       firePropertyChange("alignment", null, alignment.getSequences());
1365     }
1366   }
1367
1368   public void showSequence(int index)
1369   {
1370     Vector tmp = alignment.getHiddenSequences().showSequence(index,
1371             hiddenRepSequences);
1372     if (tmp.size() > 0)
1373     {
1374       if (selectionGroup == null)
1375       {
1376         selectionGroup = new SequenceGroup();
1377         selectionGroup.setEndRes(alignment.getWidth() - 1);
1378       }
1379
1380       for (int t = 0; t < tmp.size(); t++)
1381       {
1382         selectionGroup.addSequence((SequenceI) tmp.elementAt(t), false);
1383       }
1384       firePropertyChange("alignment", null, alignment.getSequences());
1385       sendSelection();
1386     }
1387
1388     if (alignment.getHiddenSequences().getSize() < 1)
1389     {
1390       hasHiddenRows = false;
1391     }
1392   }
1393
1394   public void showColumn(int col)
1395   {
1396     colSel.revealHiddenColumns(col);
1397     if (colSel.getHiddenColumns() == null)
1398     {
1399       hasHiddenColumns = false;
1400     }
1401   }
1402
1403   public void showAllHiddenColumns()
1404   {
1405     colSel.revealAllHiddenColumns();
1406     hasHiddenColumns = false;
1407   }
1408
1409   public void showAllHiddenSeqs()
1410   {
1411     if (alignment.getHiddenSequences().getSize() > 0)
1412     {
1413       if (selectionGroup == null)
1414       {
1415         selectionGroup = new SequenceGroup();
1416         selectionGroup.setEndRes(alignment.getWidth() - 1);
1417       }
1418       Vector tmp = alignment.getHiddenSequences().showAll(
1419               hiddenRepSequences);
1420       for (int t = 0; t < tmp.size(); t++)
1421       {
1422         selectionGroup.addSequence((SequenceI) tmp.elementAt(t), false);
1423       }
1424       firePropertyChange("alignment", null, alignment.getSequences());
1425       sendSelection();
1426       hasHiddenRows = false;
1427       hiddenRepSequences = null;
1428     }
1429   }
1430
1431   public void invertColumnSelection()
1432   {
1433     colSel.invertColumnSelection(0, alignment.getWidth());
1434   }
1435
1436   public int adjustForHiddenSeqs(int alignmentIndex)
1437   {
1438     return alignment.getHiddenSequences().adjustForHiddenSeqs(
1439             alignmentIndex);
1440   }
1441
1442   /**
1443    * This method returns an array of new SequenceI objects derived from the
1444    * whole alignment or just the current selection with start and end points
1445    * adjusted
1446    * 
1447    * @note if you need references to the actual SequenceI objects in the
1448    *       alignment or currently selected then use getSequenceSelection()
1449    * @return selection as new sequenceI objects
1450    */
1451   public SequenceI[] getSelectionAsNewSequence()
1452   {
1453     SequenceI[] sequences;
1454
1455     if (selectionGroup == null)
1456     {
1457       sequences = alignment.getSequencesArray();
1458       AlignmentAnnotation[] annots = alignment.getAlignmentAnnotation();
1459       for (int i = 0; i < sequences.length; i++)
1460       {
1461         sequences[i] = new Sequence(sequences[i], annots); // construct new
1462                                                             // sequence with
1463                                                             // subset of visible
1464                                                             // annotation
1465       }
1466     }
1467     else
1468     {
1469       sequences = selectionGroup.getSelectionAsNewSequences(alignment);
1470     }
1471
1472     return sequences;
1473   }
1474
1475   /**
1476    * get the currently selected sequence objects or all the sequences in the
1477    * alignment.
1478    * 
1479    * @return array of references to sequence objects
1480    */
1481   public SequenceI[] getSequenceSelection()
1482   {
1483     SequenceI[] sequences=null;
1484     if (selectionGroup!=null)
1485     {
1486       sequences = selectionGroup.getSequencesInOrder(alignment);
1487     }
1488     if (sequences == null)
1489     {
1490       sequences = alignment.getSequencesArray();
1491     }
1492     return sequences;
1493   }
1494
1495   /**
1496    * This method returns the visible alignment as text, as seen on the GUI, ie
1497    * if columns are hidden they will not be returned in the result. Use this for
1498    * calculating trees, PCA, redundancy etc on views which contain hidden
1499    * columns.
1500    * 
1501    * @return String[]
1502    */
1503   public jalview.datamodel.CigarArray getViewAsCigars(
1504           boolean selectedRegionOnly)
1505   {
1506     CigarArray selection = null;
1507     SequenceI[] seqs = null;
1508     int i, iSize;
1509     int start = 0, end = 0;
1510     if (selectedRegionOnly && selectionGroup != null)
1511     {
1512       iSize = selectionGroup.getSize();
1513       seqs = selectionGroup.getSequencesInOrder(alignment);
1514       start = selectionGroup.getStartRes();
1515       end = selectionGroup.getEndRes(); // inclusive for start and end in
1516                                         // SeqCigar constructor
1517     }
1518     else
1519     {
1520       iSize = alignment.getHeight();
1521       seqs = alignment.getSequencesArray();
1522       end = alignment.getWidth() - 1;
1523     }
1524     SeqCigar[] selseqs = new SeqCigar[iSize];
1525     for (i = 0; i < iSize; i++)
1526     {
1527       selseqs[i] = new SeqCigar(seqs[i], start, end);
1528     }
1529     selection = new CigarArray(selseqs);
1530     // now construct the CigarArray operations
1531     if (hasHiddenColumns)
1532     {
1533       Vector regions = colSel.getHiddenColumns();
1534       int[] region;
1535       int hideStart, hideEnd;
1536       int last = start;
1537       for (int j = 0; last < end & j < regions.size(); j++)
1538       {
1539         region = (int[]) regions.elementAt(j);
1540         hideStart = region[0];
1541         hideEnd = region[1];
1542         // edit hidden regions to selection range
1543         if (hideStart < last)
1544         {
1545           if (hideEnd > last)
1546           {
1547             hideStart = last;
1548           }
1549           else
1550           {
1551             continue;
1552           }
1553         }
1554
1555         if (hideStart > end)
1556         {
1557           break;
1558         }
1559
1560         if (hideEnd > end)
1561         {
1562           hideEnd = end;
1563         }
1564
1565         if (hideStart > hideEnd)
1566         {
1567           break;
1568         }
1569         /**
1570          * form operations...
1571          */
1572         if (last < hideStart)
1573         {
1574           selection.addOperation(CigarArray.M, hideStart - last);
1575         }
1576         selection.addOperation(CigarArray.D, 1 + hideEnd - hideStart);
1577         last = hideEnd + 1;
1578       }
1579       // Final match if necessary.
1580       if (last < end)
1581       {
1582         selection.addOperation(CigarArray.M, end - last + 1);
1583       }
1584     }
1585     else
1586     {
1587       selection.addOperation(CigarArray.M, end - start + 1);
1588     }
1589     return selection;
1590   }
1591
1592   /**
1593    * return a compact representation of the current alignment selection to pass
1594    * to an analysis function
1595    * 
1596    * @param selectedOnly
1597    *                boolean true to just return the selected view
1598    * @return AlignmentView
1599    */
1600   jalview.datamodel.AlignmentView getAlignmentView(boolean selectedOnly)
1601   {
1602     // JBPNote:
1603     // this is here because the AlignmentView constructor modifies the
1604     // CigarArray
1605     // object. Refactoring of Cigar and alignment view representation should
1606     // be done to remove redundancy.
1607     CigarArray aligview = getViewAsCigars(selectedOnly);
1608     if (aligview != null)
1609     {
1610       return new AlignmentView(aligview,
1611               (selectedOnly && selectionGroup != null) ? selectionGroup
1612                       .getStartRes() : 0);
1613     }
1614     return null;
1615   }
1616
1617   /**
1618    * This method returns the visible alignment as text, as seen on the GUI, ie
1619    * if columns are hidden they will not be returned in the result. Use this for
1620    * calculating trees, PCA, redundancy etc on views which contain hidden
1621    * columns.
1622    * 
1623    * @return String[]
1624    */
1625   public String[] getViewAsString(boolean selectedRegionOnly)
1626   {
1627     String[] selection = null;
1628     SequenceI[] seqs = null;
1629     int i, iSize;
1630     int start = 0, end = 0;
1631     if (selectedRegionOnly && selectionGroup != null)
1632     {
1633       iSize = selectionGroup.getSize();
1634       seqs = selectionGroup.getSequencesInOrder(alignment);
1635       start = selectionGroup.getStartRes();
1636       end = selectionGroup.getEndRes() + 1;
1637     }
1638     else
1639     {
1640       iSize = alignment.getHeight();
1641       seqs = alignment.getSequencesArray();
1642       end = alignment.getWidth();
1643     }
1644
1645     selection = new String[iSize];
1646     if (hasHiddenColumns)
1647     {
1648       selection = colSel.getVisibleSequenceStrings(start, end, seqs);
1649     }
1650     else
1651     {
1652       for (i = 0; i < iSize; i++)
1653       {
1654         selection[i] = seqs[i].getSequenceAsString(start, end);
1655       }
1656
1657     }
1658     return selection;
1659   }
1660
1661   public int[][] getVisibleRegionBoundaries(int min, int max)
1662   {
1663     Vector regions = new Vector();
1664     int start = min;
1665     int end = max;
1666
1667     do
1668     {
1669       if (hasHiddenColumns)
1670       {
1671         if (start == 0)
1672         {
1673           start = colSel.adjustForHiddenColumns(start);
1674         }
1675
1676         end = colSel.getHiddenBoundaryRight(start);
1677         if (start == end)
1678         {
1679           end = max;
1680         }
1681         if (end > max)
1682         {
1683           end = max;
1684         }
1685       }
1686
1687       regions.addElement(new int[]
1688       { start, end });
1689
1690       if (hasHiddenColumns)
1691       {
1692         start = colSel.adjustForHiddenColumns(end);
1693         start = colSel.getHiddenBoundaryLeft(start) + 1;
1694       }
1695     } while (end < max);
1696
1697     int[][] startEnd = new int[regions.size()][2];
1698
1699     regions.copyInto(startEnd);
1700
1701     return startEnd;
1702
1703   }
1704
1705   public boolean getShowHiddenMarkers()
1706   {
1707     return showHiddenMarkers;
1708   }
1709
1710   public void setShowHiddenMarkers(boolean show)
1711   {
1712     showHiddenMarkers = show;
1713   }
1714
1715   public String getSequenceSetId()
1716   {
1717     if (sequenceSetID == null)
1718     {
1719       sequenceSetID = alignment.hashCode() + "";
1720     }
1721
1722     return sequenceSetID;
1723   }
1724   /**
1725    * unique viewId for synchronizing state with stored Jalview Project 
1726    * 
1727    */
1728   private String viewId=null;
1729
1730   
1731   public String getViewId()
1732   {
1733     if (viewId==null)
1734     {
1735       viewId = this.getSequenceSetId()+"."+this.hashCode()+"";
1736     }
1737     return viewId;
1738   }
1739   
1740   public void alignmentChanged(AlignmentPanel ap)
1741   {
1742     if (padGaps)
1743     {
1744       alignment.padGaps();
1745     }
1746     if (hconsensus != null && autoCalculateConsensus)
1747     {
1748       updateConservation(ap);
1749     }
1750     if (autoCalculateConsensus)
1751     {
1752       updateConsensus(ap);
1753     }
1754
1755     // Reset endRes of groups if beyond alignment width
1756     int alWidth = alignment.getWidth();
1757     Vector groups = alignment.getGroups();
1758     if (groups != null)
1759     {
1760       for (int i = 0; i < groups.size(); i++)
1761       {
1762         SequenceGroup sg = (SequenceGroup) groups.elementAt(i);
1763         if (sg.getEndRes() > alWidth)
1764         {
1765           sg.setEndRes(alWidth - 1);
1766         }
1767       }
1768     }
1769
1770     if (selectionGroup != null && selectionGroup.getEndRes() > alWidth)
1771     {
1772       selectionGroup.setEndRes(alWidth - 1);
1773     }
1774
1775     resetAllColourSchemes();
1776
1777     // alignment.adjustSequenceAnnotations();
1778   }
1779
1780   void resetAllColourSchemes()
1781   {
1782     ColourSchemeI cs = globalColourScheme;
1783     if (cs != null)
1784     {
1785       if (cs instanceof ClustalxColourScheme)
1786       {
1787         ((ClustalxColourScheme) cs).resetClustalX(alignment.getSequences(),
1788                 alignment.getWidth());
1789       }
1790
1791       cs.setConsensus(hconsensus);
1792       if (cs.conservationApplied())
1793       {
1794         Alignment al = (Alignment) alignment;
1795         Conservation c = new Conservation("All",
1796                 ResidueProperties.propHash, 3, al.getSequences(), 0, al
1797                         .getWidth() - 1);
1798         c.calculate();
1799         c.verdict(false, ConsPercGaps);
1800
1801         cs.setConservation(c);
1802       }
1803     }
1804
1805     int s, sSize = alignment.getGroups().size();
1806     for (s = 0; s < sSize; s++)
1807     {
1808       SequenceGroup sg = (SequenceGroup) alignment.getGroups().elementAt(s);
1809       if (sg.cs != null && sg.cs instanceof ClustalxColourScheme)
1810       {
1811         ((ClustalxColourScheme) sg.cs).resetClustalX(sg
1812                 .getSequences(hiddenRepSequences), sg.getWidth());
1813       }
1814       sg.recalcConservation();
1815     }
1816   }
1817
1818   public Color getSequenceColour(SequenceI seq)
1819   {
1820     if (sequenceColours == null || !sequenceColours.containsKey(seq))
1821     {
1822       return Color.white;
1823     }
1824     else
1825     {
1826       return (Color) sequenceColours.get(seq);
1827     }
1828   }
1829
1830   public void setSequenceColour(SequenceI seq, Color col)
1831   {
1832     if (sequenceColours == null)
1833     {
1834       sequenceColours = new Hashtable();
1835     }
1836
1837     if (col == null)
1838     {
1839       sequenceColours.remove(seq);
1840     }
1841     else
1842     {
1843       sequenceColours.put(seq, col);
1844     }
1845   }
1846
1847   /**
1848    * returns the visible column regions of the alignment
1849    * 
1850    * @param selectedRegionOnly
1851    *                true to just return the contigs intersecting with the
1852    *                selected area
1853    * @return
1854    */
1855   public int[] getViewAsVisibleContigs(boolean selectedRegionOnly)
1856   {
1857     int[] viscontigs = null;
1858     int start = 0, end = 0;
1859     if (selectedRegionOnly && selectionGroup != null)
1860     {
1861       start = selectionGroup.getStartRes();
1862       end = selectionGroup.getEndRes() + 1;
1863     }
1864     else
1865     {
1866       end = alignment.getWidth();
1867     }
1868     viscontigs = colSel.getVisibleContigs(start, end);
1869     return viscontigs;
1870   }
1871
1872   /**
1873    * get hash of undo and redo list for the alignment
1874    * 
1875    * @return long[] { historyList.hashCode, redoList.hashCode };
1876    */
1877   public long[] getUndoRedoHash()
1878   {
1879     if (historyList == null || redoList == null)
1880       return new long[]
1881       { -1, -1 };
1882     return new long[]
1883     { historyList.hashCode(), this.redoList.hashCode() };
1884   }
1885
1886   /**
1887    * test if a particular set of hashcodes are different to the hashcodes for
1888    * the undo and redo list.
1889    * 
1890    * @param undoredo
1891    *                the stored set of hashcodes as returned by getUndoRedoHash
1892    * @return true if the hashcodes differ (ie the alignment has been edited) or
1893    *         the stored hashcode array differs in size
1894    */
1895   public boolean isUndoRedoHashModified(long[] undoredo)
1896   {
1897     if (undoredo == null)
1898     {
1899       return true;
1900     }
1901     long[] cstate = getUndoRedoHash();
1902     if (cstate.length != undoredo.length)
1903     {
1904       return true;
1905     }
1906
1907     for (int i = 0; i < cstate.length; i++)
1908     {
1909       if (cstate[i] != undoredo[i])
1910       {
1911         return true;
1912       }
1913     }
1914     return false;
1915   }
1916
1917   public boolean getCentreColumnLabels()
1918   {
1919     return centreColumnLabels;
1920   }
1921
1922   public void setCentreColumnLabels(boolean centrecolumnlabels)
1923   {
1924     centreColumnLabels = centrecolumnlabels;
1925   }
1926
1927   public void updateSequenceIdColours()
1928   {
1929     Vector groups = alignment.getGroups();
1930     if (sequenceColours == null)
1931     {
1932       sequenceColours = new Hashtable();
1933     }
1934     for (int ig = 0, igSize = groups.size(); ig < igSize; ig++)
1935     {
1936       SequenceGroup sg = (SequenceGroup) groups.elementAt(ig);
1937       if (sg.idColour != null)
1938       {
1939         Vector sqs = sg.getSequences(hiddenRepSequences);
1940         for (int s = 0, sSize = sqs.size(); s < sSize; s++)
1941         {
1942           sequenceColours.put(sqs.elementAt(s), sg.idColour);
1943         }
1944       }
1945     }
1946   }
1947
1948   /**
1949    *  enable or disable the display of Database Cross References in the sequence ID tooltip
1950    */ 
1951   public void setShowDbRefs(boolean show)
1952   {
1953     showdbrefs=show;
1954   }
1955
1956   /**
1957    * 
1958    * @return true if Database References are to be displayed on tooltips.
1959    */
1960   public boolean isShowDbRefs()
1961   {
1962     return showdbrefs;
1963   }
1964
1965   /**
1966    * 
1967    * @return true if Non-positional features are to be displayed on tooltips.
1968    */
1969   public boolean isShowNpFeats()
1970   {
1971     return shownpfeats;
1972   }
1973   /**
1974    * enable or disable the display of Non-Positional sequence features in the sequence ID tooltip 
1975    * @param show 
1976    */
1977   public void setShowNpFeats(boolean show)
1978   {
1979     shownpfeats=show;
1980   }
1981   /**
1982    * 
1983    * @return true if view has hidden rows
1984    */
1985   public boolean hasHiddenRows()
1986   {
1987     return hasHiddenRows;
1988   }
1989   /**
1990    * 
1991    * @return true if view has hidden columns
1992    */
1993   public boolean hasHiddenColumns()
1994   {
1995     return hasHiddenColumns;
1996   }
1997   /**
1998    * when set, view will scroll to show the highlighted position
1999    */
2000   public boolean followHighlight=true;
2001   /**
2002    * @return true if view should scroll to show the highlighted region of a sequence
2003    * @return
2004    */
2005   public boolean getFollowHighlight() {
2006     return followHighlight;
2007   }
2008   public boolean followSelection=true;
2009   /**
2010    * @return true if view selection should always follow the selections broadcast by other selection sources
2011    */
2012   public boolean getFollowSelection() {
2013     return followSelection;
2014   }
2015   private long sgrouphash=-1,colselhash=-1;
2016
2017   boolean showSeqFeaturesHeight;
2018   /**
2019    * checks current SelectionGroup against record of last hash value, and updates record.
2020    * @return true if SelectionGroup changed since last call
2021    */
2022   boolean isSelectionGroupChanged() {
2023     int hc=(selectionGroup==null) ? -1 : selectionGroup.hashCode();
2024     if (hc!=sgrouphash)
2025     {
2026       sgrouphash = hc;
2027       return true;
2028     }
2029     return false;
2030   }
2031   /**
2032    * checks current colsel against record of last hash value, and updates record.
2033    * @return true if colsel changed since last call
2034    */
2035   boolean isColSelChanged() {
2036     int hc=(colSel==null) ? -1 : colSel.hashCode();
2037     if (hc!=colselhash)
2038     {
2039       colselhash = hc;
2040       return true;
2041     }
2042     return false;
2043   }
2044   public void sendSelection()
2045   {
2046     jalview.structure.StructureSelectionManager.getStructureSelectionManager().sendSelection(new SequenceGroup(getSelectionGroup()), new ColumnSelection(getColumnSelection()), this);
2047   }
2048   public void setShowSequenceFeaturesHeight(boolean selected)
2049   {
2050     showSeqFeaturesHeight = selected; 
2051   }
2052   public boolean getShowSequenceFeaturesHeight()
2053   {
2054     return showSeqFeaturesHeight; 
2055   }
2056   boolean showUnconserved=false;
2057   public boolean getShowUnconserved()
2058   {
2059     return showUnconserved;
2060   }
2061   public void setShowUnconserved(boolean showunconserved)
2062   {
2063     showUnconserved=showunconserved;
2064   }
2065   /**
2066    * return the alignPanel containing the given viewport. Use this to get the
2067    * components currently handling the given viewport.
2068    * @param av
2069    * @return null or an alignPanel guaranteed to have non-null alignFrame reference
2070    */
2071   public AlignmentPanel getAlignPanel()
2072   {
2073     AlignmentPanel[] aps = PaintRefresher.getAssociatedPanels(this.getSequenceSetId());
2074     AlignmentPanel ap=null;
2075     for (int p=0;aps!=null && p<aps.length; p++)
2076     {
2077       if (aps[p].av == this)
2078       {
2079         return aps[p];
2080       }
2081     }
2082     return null;
2083   }
2084   public boolean getSortByTree()
2085   {
2086     return sortByTree;
2087   }
2088   public void setSortByTree(boolean sort) {
2089     sortByTree = sort;
2090   }
2091   /**
2092    * should conservation rows be shown for groups
2093    */
2094   boolean showGroupConservation = false;
2095   /**
2096    * should consensus rows be shown for groups
2097    */
2098   boolean showGroupConsensus = false;
2099   /**
2100    * should consensus profile be rendered by default
2101    */
2102   public boolean showSequenceLogo = false;
2103   /**
2104    * should consensus histograms be rendered by default
2105    */
2106   public boolean showConsensusHistogram = true;
2107   /**
2108    * @return the showConsensusProfile
2109    */
2110   public boolean isShowSequenceLogo()
2111   {
2112     return showSequenceLogo;
2113   }
2114   /**
2115    * @param showSequenceLogo the new value
2116    */
2117   public void setShowSequenceLogo(boolean showSequenceLogo)
2118   {
2119     this.showSequenceLogo = showSequenceLogo;
2120   }
2121   /**
2122    * @param showConsensusHistogram the showConsensusHistogram to set
2123    */
2124   public void setShowConsensusHistogram(boolean showConsensusHistogram)
2125   {
2126     this.showConsensusHistogram = showConsensusHistogram;
2127   }
2128   
2129   /**
2130    * @return the showGroupConservation
2131    */
2132   public boolean isShowGroupConservation()
2133   {
2134     return showGroupConservation;
2135   }
2136   /**
2137    * @param showGroupConservation the showGroupConservation to set
2138    */
2139   public void setShowGroupConservation(boolean showGroupConservation)
2140   {
2141     this.showGroupConservation = showGroupConservation;
2142   }
2143   /**
2144    * @return the showGroupConsensus
2145    */
2146   public boolean isShowGroupConsensus()
2147   {
2148     return showGroupConsensus;
2149   }
2150   /**
2151    * @param showGroupConsensus the showGroupConsensus to set
2152    */
2153   public void setShowGroupConsensus(boolean showGroupConsensus)
2154   {
2155     this.showGroupConsensus = showGroupConsensus;
2156   }
2157   /**
2158    * @return the includeAllConsensusSymbols
2159    */
2160   public boolean isIncludeAllConsensusSymbols()
2161   {
2162     return includeAllConsensusSymbols;
2163   }
2164   /**
2165    * @param includeAllConsensusSymbols the includeAllConsensusSymbols to set
2166    */
2167   public void setIncludeAllConsensusSymbols(boolean includeAllConsensusSymbols)
2168   {
2169     this.includeAllConsensusSymbols = includeAllConsensusSymbols;
2170   }
2171   
2172   /**
2173    * 
2174    * @return flag to indicate if the consensus histogram should be rendered by default
2175    */
2176   public boolean isShowConsensusHistogram()
2177   {
2178     return this.showConsensusHistogram;
2179   }
2180   /**
2181    * synthesize a column selection if none exists so it covers the given selection group.
2182    * if wholewidth is false,  no column selection is made if the selection group covers the whole alignment width.
2183    * @param sg
2184    * @param wholewidth 
2185    */
2186   public void expandColSelection(SequenceGroup sg, boolean wholewidth)
2187   {
2188     int sgs,sge;
2189     if (sg!=null && (sgs=sg.getStartRes())>=0 && sg.getStartRes()<=(sge=sg.getEndRes())&& (colSel==null || colSel.getSelected()==null || colSel.getSelected().size()==0))
2190     {
2191       if (!wholewidth && alignment.getWidth()==(1+sge-sgs))
2192       {
2193         // do nothing 
2194         return;
2195       }
2196       if (colSel==null)
2197       {
2198         colSel = new ColumnSelection();
2199       }
2200       for (int cspos = sg.getStartRes(); cspos<=sg.getEndRes(); cspos++) {
2201         colSel.addElement(cspos);
2202       }
2203     }    
2204   }
2205 }