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