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