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