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