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