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