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