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