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