JAL-1065 JAL-1066 generic 'annotation collection updated' method called when alignmen...
[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       cs.alignmentChanged(alignment);
1767       if (cs instanceof ClustalxColourScheme)
1768       {
1769         ((ClustalxColourScheme) cs).resetClustalX(alignment.getSequences(),
1770                 alignment.getWidth());
1771       }
1772
1773       cs.setConsensus(hconsensus);
1774       if (cs.conservationApplied())
1775       {
1776         Alignment al = (Alignment) alignment;
1777         Conservation c = new Conservation("All",
1778                 ResidueProperties.propHash, 3, al.getSequences(), 0,
1779                 al.getWidth() - 1);
1780         c.calculate();
1781         c.verdict(false, ConsPercGaps);
1782
1783         cs.setConservation(c);
1784       }
1785     }
1786
1787     int s, sSize = alignment.getGroups().size();
1788     for (s = 0; s < sSize; s++)
1789     {
1790       SequenceGroup sg = (SequenceGroup) alignment.getGroups().elementAt(s);
1791       if (sg.cs != null && sg.cs instanceof ClustalxColourScheme)
1792       {
1793         ((ClustalxColourScheme) sg.cs).resetClustalX(
1794                 sg.getSequences(hiddenRepSequences), sg.getWidth());
1795       }
1796       sg.recalcConservation();
1797     }
1798   }
1799
1800   public Color getSequenceColour(SequenceI seq)
1801   {
1802     if (sequenceColours == null || !sequenceColours.containsKey(seq))
1803     {
1804       return Color.white;
1805     }
1806     else
1807     {
1808       return (Color) sequenceColours.get(seq);
1809     }
1810   }
1811
1812   public void setSequenceColour(SequenceI seq, Color col)
1813   {
1814     if (sequenceColours == null)
1815     {
1816       sequenceColours = new Hashtable();
1817     }
1818
1819     if (col == null)
1820     {
1821       sequenceColours.remove(seq);
1822     }
1823     else
1824     {
1825       sequenceColours.put(seq, col);
1826     }
1827   }
1828
1829   /**
1830    * returns the visible column regions of the alignment
1831    * 
1832    * @param selectedRegionOnly
1833    *          true to just return the contigs intersecting with the selected
1834    *          area
1835    * @return
1836    */
1837   public int[] getViewAsVisibleContigs(boolean selectedRegionOnly)
1838   {
1839     int[] viscontigs = null;
1840     int start = 0, end = 0;
1841     if (selectedRegionOnly && selectionGroup != null)
1842     {
1843       start = selectionGroup.getStartRes();
1844       end = selectionGroup.getEndRes() + 1;
1845     }
1846     else
1847     {
1848       end = alignment.getWidth();
1849     }
1850     viscontigs = colSel.getVisibleContigs(start, end);
1851     return viscontigs;
1852   }
1853
1854   /**
1855    * get hash of undo and redo list for the alignment
1856    * 
1857    * @return long[] { historyList.hashCode, redoList.hashCode };
1858    */
1859   public long[] getUndoRedoHash()
1860   {
1861     if (historyList == null || redoList == null)
1862       return new long[]
1863       { -1, -1 };
1864     return new long[]
1865     { historyList.hashCode(), this.redoList.hashCode() };
1866   }
1867
1868   /**
1869    * test if a particular set of hashcodes are different to the hashcodes for
1870    * the undo and redo list.
1871    * 
1872    * @param undoredo
1873    *          the stored set of hashcodes as returned by getUndoRedoHash
1874    * @return true if the hashcodes differ (ie the alignment has been edited) or
1875    *         the stored hashcode array differs in size
1876    */
1877   public boolean isUndoRedoHashModified(long[] undoredo)
1878   {
1879     if (undoredo == null)
1880     {
1881       return true;
1882     }
1883     long[] cstate = getUndoRedoHash();
1884     if (cstate.length != undoredo.length)
1885     {
1886       return true;
1887     }
1888
1889     for (int i = 0; i < cstate.length; i++)
1890     {
1891       if (cstate[i] != undoredo[i])
1892       {
1893         return true;
1894       }
1895     }
1896     return false;
1897   }
1898
1899   public boolean getCentreColumnLabels()
1900   {
1901     return centreColumnLabels;
1902   }
1903
1904   public void setCentreColumnLabels(boolean centrecolumnlabels)
1905   {
1906     centreColumnLabels = centrecolumnlabels;
1907   }
1908
1909   public void updateSequenceIdColours()
1910   {
1911     Vector groups = alignment.getGroups();
1912     if (sequenceColours == null)
1913     {
1914       sequenceColours = new Hashtable();
1915     }
1916     for (int ig = 0, igSize = groups.size(); ig < igSize; ig++)
1917     {
1918       SequenceGroup sg = (SequenceGroup) groups.elementAt(ig);
1919       if (sg.idColour != null)
1920       {
1921         Vector sqs = sg.getSequences(hiddenRepSequences);
1922         for (int s = 0, sSize = sqs.size(); s < sSize; s++)
1923         {
1924           sequenceColours.put(sqs.elementAt(s), sg.idColour);
1925         }
1926       }
1927     }
1928   }
1929
1930   /**
1931    * enable or disable the display of Database Cross References in the sequence
1932    * ID tooltip
1933    */
1934   public void setShowDbRefs(boolean show)
1935   {
1936     showdbrefs = show;
1937   }
1938
1939   /**
1940    * 
1941    * @return true if Database References are to be displayed on tooltips.
1942    */
1943   public boolean isShowDbRefs()
1944   {
1945     return showdbrefs;
1946   }
1947
1948   /**
1949    * 
1950    * @return true if Non-positional features are to be displayed on tooltips.
1951    */
1952   public boolean isShowNpFeats()
1953   {
1954     return shownpfeats;
1955   }
1956
1957   /**
1958    * enable or disable the display of Non-Positional sequence features in the
1959    * sequence ID tooltip
1960    * 
1961    * @param show
1962    */
1963   public void setShowNpFeats(boolean show)
1964   {
1965     shownpfeats = show;
1966   }
1967
1968   /**
1969    * 
1970    * @return true if view has hidden rows
1971    */
1972   public boolean hasHiddenRows()
1973   {
1974     return hasHiddenRows;
1975   }
1976
1977   /**
1978    * 
1979    * @return true if view has hidden columns
1980    */
1981   public boolean hasHiddenColumns()
1982   {
1983     return hasHiddenColumns;
1984   }
1985
1986   /**
1987    * when set, view will scroll to show the highlighted position
1988    */
1989   public boolean followHighlight = true;
1990
1991   /**
1992    * @return true if view should scroll to show the highlighted region of a
1993    *         sequence
1994    * @return
1995    */
1996   public boolean getFollowHighlight()
1997   {
1998     return followHighlight;
1999   }
2000
2001   public boolean followSelection = true;
2002
2003   /**
2004    * @return true if view selection should always follow the selections
2005    *         broadcast by other selection sources
2006    */
2007   public boolean getFollowSelection()
2008   {
2009     return followSelection;
2010   }
2011
2012   private long sgrouphash = -1, colselhash = -1;
2013
2014   boolean showSeqFeaturesHeight;
2015
2016   /**
2017    * checks current SelectionGroup against record of last hash value, and
2018    * updates record.
2019    * @param b update the record of last hash value
2020    * 
2021    * @return true if SelectionGroup changed since last call (when b is true)
2022    */
2023   boolean isSelectionGroupChanged(boolean b)
2024   {
2025     int hc = (selectionGroup == null || selectionGroup.getSize()==0) ? -1 : selectionGroup.hashCode();
2026     if (hc!=-1 && hc != sgrouphash)
2027     {
2028       if (b) {sgrouphash = hc;}
2029       return true;
2030     }
2031     return false;
2032   }
2033
2034   /**
2035    * checks current colsel against record of last hash value, and optionally updates
2036    * record.
2037
2038    * @param b update the record of last hash value
2039    * @return true if colsel changed since last call (when b is true)
2040    */
2041   boolean isColSelChanged(boolean b)
2042   {
2043     int hc = (colSel == null || colSel.size()==0) ? -1 : colSel.hashCode();
2044     if (hc!=-1 && hc != colselhash)
2045     {
2046       if (b) {colselhash = hc;}
2047       return true;
2048     }
2049     return false;
2050   }
2051
2052   public void sendSelection()
2053   {
2054     jalview.structure.StructureSelectionManager
2055             .getStructureSelectionManager(Desktop.instance).sendSelection(
2056                     new SequenceGroup(getSelectionGroup()),
2057                     new ColumnSelection(getColumnSelection()), this);
2058   }
2059
2060   public void setShowSequenceFeaturesHeight(boolean selected)
2061   {
2062     showSeqFeaturesHeight = selected;
2063   }
2064
2065   public boolean getShowSequenceFeaturesHeight()
2066   {
2067     return showSeqFeaturesHeight;
2068   }
2069
2070   boolean showUnconserved = false;
2071
2072   public boolean getShowUnconserved()
2073   {
2074     return showUnconserved;
2075   }
2076
2077   public void setShowUnconserved(boolean showunconserved)
2078   {
2079     showUnconserved = showunconserved;
2080   }
2081
2082   /**
2083    * return the alignPanel containing the given viewport. Use this to get the
2084    * components currently handling the given viewport.
2085    * 
2086    * @param av
2087    * @return null or an alignPanel guaranteed to have non-null alignFrame
2088    *         reference
2089    */
2090   public AlignmentPanel getAlignPanel()
2091   {
2092     AlignmentPanel[] aps = PaintRefresher.getAssociatedPanels(this
2093             .getSequenceSetId());
2094     AlignmentPanel ap = null;
2095     for (int p = 0; aps != null && p < aps.length; p++)
2096     {
2097       if (aps[p].av == this)
2098       {
2099         return aps[p];
2100       }
2101     }
2102     return null;
2103   }
2104
2105   public boolean getSortByTree()
2106   {
2107     return sortByTree;
2108   }
2109
2110   public void setSortByTree(boolean sort)
2111   {
2112     sortByTree = sort;
2113   }
2114
2115   /**
2116    * should conservation rows be shown for groups
2117    */
2118   boolean showGroupConservation = false;
2119
2120   /**
2121    * should consensus rows be shown for groups
2122    */
2123   boolean showGroupConsensus = false;
2124
2125   /**
2126    * should consensus profile be rendered by default
2127    */
2128   public boolean showSequenceLogo = false;
2129
2130   /**
2131    * should consensus histograms be rendered by default
2132    */
2133   public boolean showConsensusHistogram = true;
2134
2135   /**
2136    * @return the showConsensusProfile
2137    */
2138   public boolean isShowSequenceLogo()
2139   {
2140     return showSequenceLogo;
2141   }
2142
2143   /**
2144    * @param showSequenceLogo
2145    *          the new value
2146    */
2147   public void setShowSequenceLogo(boolean showSequenceLogo)
2148   {
2149     if (showSequenceLogo != this.showSequenceLogo)
2150     {
2151       // TODO: decouple settings setting from calculation when refactoring
2152       // annotation update method from alignframe to viewport
2153       this.showSequenceLogo = showSequenceLogo;
2154       if (consensusThread != null)
2155       {
2156         consensusThread.updateAnnotation();
2157       }
2158     }
2159     this.showSequenceLogo = showSequenceLogo;
2160   }
2161
2162   /**
2163    * @param showConsensusHistogram
2164    *          the showConsensusHistogram to set
2165    */
2166   public void setShowConsensusHistogram(boolean showConsensusHistogram)
2167   {
2168     this.showConsensusHistogram = showConsensusHistogram;
2169   }
2170
2171   /**
2172    * @return the showGroupConservation
2173    */
2174   public boolean isShowGroupConservation()
2175   {
2176     return showGroupConservation;
2177   }
2178
2179   /**
2180    * @param showGroupConservation
2181    *          the showGroupConservation to set
2182    */
2183   public void setShowGroupConservation(boolean showGroupConservation)
2184   {
2185     this.showGroupConservation = showGroupConservation;
2186   }
2187
2188   /**
2189    * @return the showGroupConsensus
2190    */
2191   public boolean isShowGroupConsensus()
2192   {
2193     return showGroupConsensus;
2194   }
2195
2196   /**
2197    * @param showGroupConsensus
2198    *          the showGroupConsensus to set
2199    */
2200   public void setShowGroupConsensus(boolean showGroupConsensus)
2201   {
2202     this.showGroupConsensus = showGroupConsensus;
2203   }
2204
2205   /**
2206    * 
2207    * @return flag to indicate if the consensus histogram should be rendered by
2208    *         default
2209    */
2210   public boolean isShowConsensusHistogram()
2211   {
2212     return this.showConsensusHistogram;
2213   }
2214
2215   /**
2216    * synthesize a column selection if none exists so it covers the given
2217    * selection group. if wholewidth is false, no column selection is made if the
2218    * selection group covers the whole alignment width.
2219    * 
2220    * @param sg
2221    * @param wholewidth
2222    */
2223   public void expandColSelection(SequenceGroup sg, boolean wholewidth)
2224   {
2225     int sgs, sge;
2226     if (sg != null
2227             && (sgs = sg.getStartRes()) >= 0
2228             && sg.getStartRes() <= (sge = sg.getEndRes())
2229             && (colSel == null || colSel.getSelected() == null || colSel
2230                     .getSelected().size() == 0))
2231     {
2232       if (!wholewidth && alignment.getWidth() == (1 + sge - sgs))
2233       {
2234         // do nothing
2235         return;
2236       }
2237       if (colSel == null)
2238       {
2239         colSel = new ColumnSelection();
2240       }
2241       for (int cspos = sg.getStartRes(); cspos <= sg.getEndRes(); cspos++)
2242       {
2243         colSel.addElement(cspos);
2244       }
2245     }
2246   }
2247
2248   public StructureSelectionManager getStructureSelectionManager()
2249   {
2250     return StructureSelectionManager.getStructureSelectionManager(Desktop.instance);
2251   }
2252
2253   /**
2254    * 
2255    * @param pdbEntries
2256    * @return a series of SequenceI arrays, one for each PDBEntry, listing which sequence in the alignment holds a reference to it
2257    */
2258   public SequenceI[][] collateForPDB(PDBEntry[] pdbEntries)
2259   {
2260     ArrayList<SequenceI[]> seqvectors = new ArrayList<SequenceI[]>();
2261     for (PDBEntry pdb: pdbEntries) {
2262     ArrayList<SequenceI> seqs = new ArrayList<SequenceI>();
2263     for (int i = 0; i < alignment.getHeight(); i++)
2264     {
2265       Vector pdbs = alignment.getSequenceAt(i)
2266               .getDatasetSequence().getPDBId();
2267       if (pdbs == null)
2268         continue;
2269       SequenceI sq;
2270       for (int p = 0; p < pdbs.size(); p++)
2271       {
2272         PDBEntry p1 = (PDBEntry) pdbs.elementAt(p);
2273         if (p1.getId().equals(pdb.getId()))
2274         {
2275           if (!seqs.contains(sq=alignment.getSequenceAt(i)))
2276             seqs.add(sq);
2277
2278           continue;
2279         }
2280       }
2281     }
2282     seqvectors.add(seqs.toArray(new SequenceI[seqs.size()]));
2283     }
2284     return seqvectors.toArray(new SequenceI[seqvectors.size()][]);
2285   }
2286 }