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