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