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