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