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