refactored and added methods for getting richer AlignmentView object (JAL-716)
[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
52 /**
53  * DOCUMENT ME!
54  * 
55  * @author $author$
56  * @version $Revision$
57  */
58 public class AlignViewport implements SelectionSource
59 {
60   private static final int RIGHT_JUSTIFY = 1;
61
62   int startRes;
63
64   int endRes;
65
66   int startSeq;
67
68   int endSeq;
69
70   boolean showJVSuffix = true;
71
72   boolean showText = true;
73
74   boolean showColourText = false;
75
76   boolean showBoxes = true;
77
78   boolean wrapAlignment = false;
79
80   boolean renderGaps = true;
81
82   boolean showSequenceFeatures = false;
83
84   boolean showAnnotation = true;
85
86   boolean colourAppliesToAllGroups = true;
87
88   ColourSchemeI globalColourScheme = null;
89
90   boolean conservationColourSelected = false;
91
92   boolean abovePIDThreshold = false;
93
94   SequenceGroup selectionGroup;
95
96   int charHeight;
97
98   int charWidth;
99
100   boolean validCharWidth;
101
102   int wrappedWidth;
103
104   Font font;
105
106   boolean seqNameItalics;
107
108   AlignmentI alignment;
109
110   ColumnSelection colSel = new ColumnSelection();
111
112   int threshold;
113
114   int increment;
115
116   NJTree currentTree = null;
117
118   boolean scaleAboveWrapped = false;
119
120   boolean scaleLeftWrapped = true;
121
122   boolean scaleRightWrapped = true;
123
124   boolean hasHiddenColumns = false;
125
126   boolean hasHiddenRows = false;
127
128   boolean showHiddenMarkers = true;
129
130   boolean cursorMode = false;
131
132   /**
133    * Keys are the feature types which are currently visible. Note: Values are
134    * not used!
135    */
136   Hashtable featuresDisplayed = null;
137
138   /** DOCUMENT ME!! */
139   public Hashtable[] hconsensus;
140
141   AlignmentAnnotation consensus;
142
143   AlignmentAnnotation conservation;
144
145   AlignmentAnnotation quality;
146
147   AlignmentAnnotation[] groupConsensus;
148
149   AlignmentAnnotation[] groupConservation;
150
151   boolean autoCalculateConsensus = true;
152
153   /** DOCUMENT ME!! */
154   public int ConsPercGaps = 25; // JBPNote : This should be a scalable property!
155
156   // JBPNote Prolly only need this in the applet version.
157   private java.beans.PropertyChangeSupport changeSupport = new java.beans.PropertyChangeSupport(
158           this);
159
160   boolean ignoreGapsInConsensusCalculation = false;
161
162   boolean isDataset = false;
163
164   boolean antiAlias = false;
165
166   boolean padGaps = false;
167
168   Rectangle explodedPosition;
169
170   String viewName;
171
172   String sequenceSetID;
173
174   boolean gatherViewsHere = false;
175
176   Stack historyList = new Stack();
177
178   Stack redoList = new Stack();
179
180   Hashtable sequenceColours;
181
182   int thresholdTextColour = 0;
183
184   Color textColour = Color.black;
185
186   Color textColour2 = Color.white;
187
188   boolean rightAlignIds = false;
189
190   Hashtable hiddenRepSequences;
191
192   boolean sortByTree;
193
194   /**
195    * Creates a new AlignViewport object.
196    * 
197    * @param al
198    *          alignment to view
199    */
200   public AlignViewport(AlignmentI al)
201   {
202     setAlignment(al);
203     init();
204   }
205
206   /**
207    * Create a new AlignViewport object with a specific sequence set ID
208    * 
209    * @param al
210    * @param seqsetid
211    *          (may be null - but potential for ambiguous constructor exception)
212    */
213   public AlignViewport(AlignmentI al, String seqsetid)
214   {
215     this(al, seqsetid, null);
216   }
217
218   public AlignViewport(AlignmentI al, String seqsetid, String viewid)
219   {
220     sequenceSetID = seqsetid;
221     viewId = viewid;
222     // TODO remove these once 2.4.VAMSAS release finished
223     if (Cache.log != null && Cache.log.isDebugEnabled() && seqsetid != null)
224     {
225       Cache.log.debug("Setting viewport's sequence set id : "
226               + sequenceSetID);
227     }
228     if (Cache.log != null && Cache.log.isDebugEnabled() && viewId != null)
229     {
230       Cache.log.debug("Setting viewport's view id : " + viewId);
231     }
232     setAlignment(al);
233     init();
234   }
235
236   /**
237    * Create a new AlignViewport with hidden regions
238    * 
239    * @param al
240    *          AlignmentI
241    * @param hiddenColumns
242    *          ColumnSelection
243    */
244   public AlignViewport(AlignmentI al, ColumnSelection hiddenColumns)
245   {
246     setAlignment(al);
247     if (hiddenColumns != null)
248     {
249       this.colSel = hiddenColumns;
250       if (hiddenColumns.getHiddenColumns() != null
251               && hiddenColumns.getHiddenColumns().size() > 0)
252       {
253         hasHiddenColumns = true;
254       }
255       else
256       {
257         hasHiddenColumns = false;
258       }
259     }
260     init();
261   }
262
263   /**
264    * New viewport with hidden columns and an existing sequence set id
265    * 
266    * @param al
267    * @param hiddenColumns
268    * @param seqsetid
269    *          (may be null)
270    */
271   public AlignViewport(AlignmentI al, ColumnSelection hiddenColumns,
272           String seqsetid)
273   {
274     this(al, hiddenColumns, seqsetid, null);
275   }
276
277   /**
278    * New viewport with hidden columns and an existing sequence set id and viewid
279    * 
280    * @param al
281    * @param hiddenColumns
282    * @param seqsetid
283    *          (may be null)
284    * @param viewid
285    *          (may be null)
286    */
287   public AlignViewport(AlignmentI al, ColumnSelection hiddenColumns,
288           String seqsetid, String viewid)
289   {
290     sequenceSetID = seqsetid;
291     viewId = viewid;
292     // TODO remove these once 2.4.VAMSAS release finished
293     if (Cache.log != null && Cache.log.isDebugEnabled() && seqsetid != null)
294     {
295       Cache.log.debug("Setting viewport's sequence set id : "
296               + sequenceSetID);
297     }
298     if (Cache.log != null && Cache.log.isDebugEnabled() && viewId != null)
299     {
300       Cache.log.debug("Setting viewport's view id : " + viewId);
301     }
302     setAlignment(al);
303     if (hiddenColumns != null)
304     {
305       this.colSel = hiddenColumns;
306       if (hiddenColumns.getHiddenColumns() != null
307               && hiddenColumns.getHiddenColumns().size() > 0)
308       {
309         hasHiddenColumns = true;
310       }
311       else
312       {
313         hasHiddenColumns = false;
314       }
315     }
316     init();
317   }
318
319   void init()
320   {
321     this.startRes = 0;
322     this.endRes = alignment.getWidth() - 1;
323     this.startSeq = 0;
324     this.endSeq = alignment.getHeight() - 1;
325
326     antiAlias = Cache.getDefault("ANTI_ALIAS", false);
327
328     showJVSuffix = Cache.getDefault("SHOW_JVSUFFIX", true);
329     showAnnotation = Cache.getDefault("SHOW_ANNOTATIONS", true);
330
331     rightAlignIds = Cache.getDefault("RIGHT_ALIGN_IDS", false);
332     centreColumnLabels = Cache.getDefault("CENTRE_COLUMN_LABELS", false);
333     autoCalculateConsensus = Cache.getDefault("AUTO_CALC_CONSENSUS", true);
334
335     padGaps = Cache.getDefault("PAD_GAPS", true);
336     shownpfeats = Cache.getDefault("SHOW_NPFEATS_TOOLTIP", true);
337     showdbrefs = Cache.getDefault("SHOW_DBREFS_TOOLTIP", true);
338
339     String fontName = Cache.getDefault("FONT_NAME", "SansSerif");
340     String fontStyle = Cache.getDefault("FONT_STYLE", Font.PLAIN + "");
341     String fontSize = Cache.getDefault("FONT_SIZE", "10");
342
343     seqNameItalics = Cache.getDefault("ID_ITALICS", true);
344
345     int style = 0;
346
347     if (fontStyle.equals("bold"))
348     {
349       style = 1;
350     }
351     else if (fontStyle.equals("italic"))
352     {
353       style = 2;
354     }
355
356     setFont(new Font(fontName, style, Integer.parseInt(fontSize)));
357
358     alignment
359             .setGapCharacter(Cache.getDefault("GAP_SYMBOL", "-").charAt(0));
360
361     // We must set conservation and consensus before setting colour,
362     // as Blosum and Clustal require this to be done
363     if (hconsensus == null && !isDataset)
364     {
365       if (!alignment.isNucleotide())
366       {
367         conservation = new AlignmentAnnotation("Conservation",
368                 "Conservation of total alignment less than " + ConsPercGaps
369                         + "% gaps", new Annotation[1], 0f, 11f,
370                 AlignmentAnnotation.BAR_GRAPH);
371         conservation.hasText = true;
372         conservation.autoCalculated = true;
373
374         if (Cache.getDefault("SHOW_CONSERVATION", true))
375         {
376           alignment.addAnnotation(conservation);
377         }
378
379         if (Cache.getDefault("SHOW_QUALITY", true))
380         {
381           quality = new AlignmentAnnotation("Quality",
382                   "Alignment Quality based on Blosum62 scores",
383                   new Annotation[1], 0f, 11f, AlignmentAnnotation.BAR_GRAPH);
384           quality.hasText = true;
385           quality.autoCalculated = true;
386
387           alignment.addAnnotation(quality);
388         }
389         showGroupConservation = Cache.getDefault("SHOW_GROUP_CONSERVATION",
390                 false);
391
392         {
393
394         }
395       }
396       showConsensusHistogram = Cache.getDefault("SHOW_CONSENSUS_HISTOGRAM",
397               true);
398       showSequenceLogo = Cache.getDefault("SHOW_CONSENSUS_LOGO", false);
399       showGroupConsensus = Cache.getDefault("SHOW_GROUP_CONSENSUS", false);
400       // TODO: add menu option action that nulls or creates consensus object
401       // depending on if the user wants to see the annotation or not in a
402       // specific alignment
403       consensus = new AlignmentAnnotation("Consensus", "PID",
404               new Annotation[1], 0f, 100f, AlignmentAnnotation.BAR_GRAPH);
405       consensus.hasText = true;
406       consensus.autoCalculated = true;
407
408       if (Cache.getDefault("SHOW_IDENTITY", true))
409       {
410         alignment.addAnnotation(consensus);
411       }
412     }
413
414     if (jalview.bin.Cache.getProperty("DEFAULT_COLOUR") != null)
415     {
416       globalColourScheme = ColourSchemeProperty.getColour(alignment,
417               jalview.bin.Cache.getProperty("DEFAULT_COLOUR"));
418
419       if (globalColourScheme instanceof UserColourScheme)
420       {
421         globalColourScheme = UserDefinedColours.loadDefaultColours();
422         ((UserColourScheme) globalColourScheme).setThreshold(0,
423                 getIgnoreGapsConsensus());
424       }
425
426       if (globalColourScheme != null)
427       {
428         globalColourScheme.setConsensus(hconsensus);
429       }
430     }
431
432     wrapAlignment = jalview.bin.Cache.getDefault("WRAP_ALIGNMENT", false);
433     showUnconserved = jalview.bin.Cache.getDefault("SHOW_UNCONSERVED",
434             false);
435     sortByTree = jalview.bin.Cache.getDefault("SORT_BY_TREE", false);
436     followSelection = jalview.bin.Cache.getDefault("FOLLOW_SELECTIONS",
437             true);
438   }
439
440   /**
441    * set the flag
442    * 
443    * @param b
444    *          features are displayed if true
445    */
446   public void setShowSequenceFeatures(boolean b)
447   {
448     showSequenceFeatures = b;
449   }
450
451   public boolean getShowSequenceFeatures()
452   {
453     return showSequenceFeatures;
454   }
455
456   ConservationThread conservationThread;
457
458   ConsensusThread consensusThread;
459
460   boolean consUpdateNeeded = false;
461
462   static boolean UPDATING_CONSENSUS = false;
463
464   static boolean UPDATING_CONSERVATION = false;
465
466   boolean updatingConsensus = false;
467
468   boolean updatingConservation = false;
469
470   /**
471    * centre columnar annotation labels in displayed alignment annotation TODO:
472    * add to jalviewXML and annotation display settings
473    */
474   boolean centreColumnLabels = false;
475
476   private boolean showdbrefs;
477
478   private boolean shownpfeats;
479
480   /**
481    * trigger update of conservation annotation
482    */
483   public void updateConservation(final AlignmentPanel ap)
484   {
485     // see note in mantis : issue number 8585
486     if (alignment.isNucleotide() || conservation == null
487             || !autoCalculateConsensus)
488     {
489       return;
490     }
491
492     conservationThread = new ConservationThread(this, ap);
493     conservationThread.start();
494   }
495
496   /**
497    * trigger update of consensus annotation
498    */
499   public void updateConsensus(final AlignmentPanel ap)
500   {
501     // see note in mantis : issue number 8585
502     if (consensus == null || !autoCalculateConsensus)
503     {
504       return;
505     }
506     consensusThread = new ConsensusThread(ap);
507     consensusThread.start();
508   }
509
510   class ConsensusThread extends Thread
511   {
512     AlignmentPanel ap;
513
514     public ConsensusThread(AlignmentPanel ap)
515     {
516       this.ap = ap;
517     }
518
519     public void run()
520     {
521       updatingConsensus = true;
522       while (UPDATING_CONSENSUS)
523       {
524         try
525         {
526           if (ap != null)
527           {
528             ap.paintAlignment(false);
529           }
530
531           Thread.sleep(200);
532         } catch (Exception ex)
533         {
534           ex.printStackTrace();
535         }
536       }
537
538       UPDATING_CONSENSUS = true;
539
540       try
541       {
542         int aWidth = (alignment != null) ? alignment.getWidth() : -1; // null
543         // pointer
544         // possibility
545         // here.
546         if (aWidth <= 0)
547         {
548           updatingConsensus = false;
549           UPDATING_CONSENSUS = false;
550           return;
551         }
552
553         consensus.annotations = null;
554         consensus.annotations = new Annotation[aWidth];
555
556         hconsensus = new Hashtable[aWidth];
557         AAFrequency.calculate(alignment.getSequencesArray(), 0,
558                 alignment.getWidth(), hconsensus, true);
559         updateAnnotation(true);
560
561         if (globalColourScheme != null)
562         {
563           globalColourScheme.setConsensus(hconsensus);
564         }
565
566       } catch (OutOfMemoryError error)
567       {
568         alignment.deleteAnnotation(consensus);
569
570         consensus = null;
571         hconsensus = null;
572         new OOMWarning("calculating consensus", error);
573       }
574       UPDATING_CONSENSUS = false;
575       updatingConsensus = false;
576
577       if (ap != null)
578       {
579         ap.paintAlignment(true);
580       }
581     }
582
583     /**
584      * update the consensus annotation from the sequence profile data using
585      * current visualization settings.
586      */
587     public void updateAnnotation()
588     {
589       updateAnnotation(false);
590     }
591
592     protected void updateAnnotation(boolean immediate)
593     {
594       // TODO: make calls thread-safe, so if another thread calls this method,
595       // it will either return or wait until one calculation is finished.
596       if (immediate
597               || (!updatingConsensus && consensus != null && hconsensus != null))
598       {
599         AAFrequency.completeConsensus(consensus, hconsensus, 0,
600                 hconsensus.length, ignoreGapsInConsensusCalculation,
601                 showSequenceLogo);
602       }
603     }
604   }
605
606   /**
607    * get the consensus sequence as displayed under the PID consensus annotation
608    * row.
609    * 
610    * @return consensus sequence as a new sequence object
611    */
612   public SequenceI getConsensusSeq()
613   {
614     if (consensus == null)
615     {
616       updateConsensus(null);
617     }
618     if (consensus == null)
619     {
620       return null;
621     }
622     StringBuffer seqs = new StringBuffer();
623     for (int i = 0; i < consensus.annotations.length; i++)
624     {
625       if (consensus.annotations[i] != null)
626       {
627         if (consensus.annotations[i].description.charAt(0) == '[')
628         {
629           seqs.append(consensus.annotations[i].description.charAt(1));
630         }
631         else
632         {
633           seqs.append(consensus.annotations[i].displayCharacter);
634         }
635       }
636     }
637
638     SequenceI sq = new Sequence("Consensus", seqs.toString());
639     sq.setDescription("Percentage Identity Consensus "
640             + ((ignoreGapsInConsensusCalculation) ? " without gaps" : ""));
641     return sq;
642   }
643
644   /**
645    * DOCUMENT ME!
646    * 
647    * @return DOCUMENT ME!
648    */
649   public SequenceGroup getSelectionGroup()
650   {
651     return selectionGroup;
652   }
653
654   /**
655    * DOCUMENT ME!
656    * 
657    * @param sg
658    *          DOCUMENT ME!
659    */
660   public void setSelectionGroup(SequenceGroup sg)
661   {
662     selectionGroup = sg;
663   }
664
665   /**
666    * DOCUMENT ME!
667    * 
668    * @return DOCUMENT ME!
669    */
670   public boolean getConservationSelected()
671   {
672     return conservationColourSelected;
673   }
674
675   /**
676    * DOCUMENT ME!
677    * 
678    * @param b
679    *          DOCUMENT ME!
680    */
681   public void setConservationSelected(boolean b)
682   {
683     conservationColourSelected = b;
684   }
685
686   /**
687    * DOCUMENT ME!
688    * 
689    * @return DOCUMENT ME!
690    */
691   public boolean getAbovePIDThreshold()
692   {
693     return abovePIDThreshold;
694   }
695
696   /**
697    * DOCUMENT ME!
698    * 
699    * @param b
700    *          DOCUMENT ME!
701    */
702   public void setAbovePIDThreshold(boolean b)
703   {
704     abovePIDThreshold = b;
705   }
706
707   /**
708    * DOCUMENT ME!
709    * 
710    * @return DOCUMENT ME!
711    */
712   public int getStartRes()
713   {
714     return startRes;
715   }
716
717   /**
718    * DOCUMENT ME!
719    * 
720    * @return DOCUMENT ME!
721    */
722   public int getEndRes()
723   {
724     return endRes;
725   }
726
727   /**
728    * DOCUMENT ME!
729    * 
730    * @return DOCUMENT ME!
731    */
732   public int getStartSeq()
733   {
734     return startSeq;
735   }
736
737   /**
738    * DOCUMENT ME!
739    * 
740    * @param cs
741    *          DOCUMENT ME!
742    */
743   public void setGlobalColourScheme(ColourSchemeI cs)
744   {
745     globalColourScheme = cs;
746   }
747
748   /**
749    * DOCUMENT ME!
750    * 
751    * @return DOCUMENT ME!
752    */
753   public ColourSchemeI getGlobalColourScheme()
754   {
755     return globalColourScheme;
756   }
757
758   /**
759    * DOCUMENT ME!
760    * 
761    * @param res
762    *          DOCUMENT ME!
763    */
764   public void setStartRes(int res)
765   {
766     this.startRes = res;
767   }
768
769   /**
770    * DOCUMENT ME!
771    * 
772    * @param seq
773    *          DOCUMENT ME!
774    */
775   public void setStartSeq(int seq)
776   {
777     this.startSeq = seq;
778   }
779
780   /**
781    * DOCUMENT ME!
782    * 
783    * @param res
784    *          DOCUMENT ME!
785    */
786   public void setEndRes(int res)
787   {
788     if (res > (alignment.getWidth() - 1))
789     {
790       // log.System.out.println(" Corrected res from " + res + " to maximum " +
791       // (alignment.getWidth()-1));
792       res = alignment.getWidth() - 1;
793     }
794
795     if (res < 0)
796     {
797       res = 0;
798     }
799
800     this.endRes = res;
801   }
802
803   /**
804    * DOCUMENT ME!
805    * 
806    * @param seq
807    *          DOCUMENT ME!
808    */
809   public void setEndSeq(int seq)
810   {
811     if (seq > alignment.getHeight())
812     {
813       seq = alignment.getHeight();
814     }
815
816     if (seq < 0)
817     {
818       seq = 0;
819     }
820
821     this.endSeq = seq;
822   }
823
824   /**
825    * DOCUMENT ME!
826    * 
827    * @return DOCUMENT ME!
828    */
829   public int getEndSeq()
830   {
831     return endSeq;
832   }
833
834   /**
835    * DOCUMENT ME!
836    * 
837    * @param f
838    *          DOCUMENT ME!
839    */
840   public void setFont(Font f)
841   {
842     font = f;
843
844     Container c = new Container();
845
846     java.awt.FontMetrics fm = c.getFontMetrics(font);
847     setCharHeight(fm.getHeight());
848     setCharWidth(fm.charWidth('M'));
849     validCharWidth = true;
850   }
851
852   /**
853    * DOCUMENT ME!
854    * 
855    * @return DOCUMENT ME!
856    */
857   public Font getFont()
858   {
859     return font;
860   }
861
862   /**
863    * DOCUMENT ME!
864    * 
865    * @param w
866    *          DOCUMENT ME!
867    */
868   public void setCharWidth(int w)
869   {
870     this.charWidth = w;
871   }
872
873   /**
874    * DOCUMENT ME!
875    * 
876    * @return DOCUMENT ME!
877    */
878   public int getCharWidth()
879   {
880     return charWidth;
881   }
882
883   /**
884    * DOCUMENT ME!
885    * 
886    * @param h
887    *          DOCUMENT ME!
888    */
889   public void setCharHeight(int h)
890   {
891     this.charHeight = h;
892   }
893
894   /**
895    * DOCUMENT ME!
896    * 
897    * @return DOCUMENT ME!
898    */
899   public int getCharHeight()
900   {
901     return charHeight;
902   }
903
904   /**
905    * DOCUMENT ME!
906    * 
907    * @param w
908    *          DOCUMENT ME!
909    */
910   public void setWrappedWidth(int w)
911   {
912     this.wrappedWidth = w;
913   }
914
915   /**
916    * DOCUMENT ME!
917    * 
918    * @return DOCUMENT ME!
919    */
920   public int getWrappedWidth()
921   {
922     return wrappedWidth;
923   }
924
925   /**
926    * DOCUMENT ME!
927    * 
928    * @return DOCUMENT ME!
929    */
930   public AlignmentI getAlignment()
931   {
932     return alignment;
933   }
934
935   /**
936    * DOCUMENT ME!
937    * 
938    * @param align
939    *          DOCUMENT ME!
940    */
941   public void setAlignment(AlignmentI align)
942   {
943     if (alignment != null && alignment.getCodonFrames() != null)
944     {
945       StructureSelectionManager.getStructureSelectionManager()
946               .removeMappings(alignment.getCodonFrames());
947     }
948     this.alignment = align;
949     if (alignment.getCodonFrames() != null)
950     {
951       StructureSelectionManager.getStructureSelectionManager().addMappings(
952               alignment.getCodonFrames());
953     }
954   }
955
956   /**
957    * DOCUMENT ME!
958    * 
959    * @param state
960    *          DOCUMENT ME!
961    */
962   public void setWrapAlignment(boolean state)
963   {
964     wrapAlignment = state;
965   }
966
967   /**
968    * DOCUMENT ME!
969    * 
970    * @param state
971    *          DOCUMENT ME!
972    */
973   public void setShowText(boolean state)
974   {
975     showText = state;
976   }
977
978   /**
979    * DOCUMENT ME!
980    * 
981    * @param state
982    *          DOCUMENT ME!
983    */
984   public void setRenderGaps(boolean state)
985   {
986     renderGaps = state;
987   }
988
989   /**
990    * DOCUMENT ME!
991    * 
992    * @return DOCUMENT ME!
993    */
994   public boolean getColourText()
995   {
996     return showColourText;
997   }
998
999   /**
1000    * DOCUMENT ME!
1001    * 
1002    * @param state
1003    *          DOCUMENT ME!
1004    */
1005   public void setColourText(boolean state)
1006   {
1007     showColourText = state;
1008   }
1009
1010   /**
1011    * DOCUMENT ME!
1012    * 
1013    * @param state
1014    *          DOCUMENT ME!
1015    */
1016   public void setShowBoxes(boolean state)
1017   {
1018     showBoxes = state;
1019   }
1020
1021   /**
1022    * DOCUMENT ME!
1023    * 
1024    * @return DOCUMENT ME!
1025    */
1026   public boolean getWrapAlignment()
1027   {
1028     return wrapAlignment;
1029   }
1030
1031   /**
1032    * DOCUMENT ME!
1033    * 
1034    * @return DOCUMENT ME!
1035    */
1036   public boolean getShowText()
1037   {
1038     return showText;
1039   }
1040
1041   /**
1042    * DOCUMENT ME!
1043    * 
1044    * @return DOCUMENT ME!
1045    */
1046   public boolean getShowBoxes()
1047   {
1048     return showBoxes;
1049   }
1050
1051   /**
1052    * DOCUMENT ME!
1053    * 
1054    * @return DOCUMENT ME!
1055    */
1056   public char getGapCharacter()
1057   {
1058     return getAlignment().getGapCharacter();
1059   }
1060
1061   /**
1062    * DOCUMENT ME!
1063    * 
1064    * @param gap
1065    *          DOCUMENT ME!
1066    */
1067   public void setGapCharacter(char gap)
1068   {
1069     if (getAlignment() != null)
1070     {
1071       getAlignment().setGapCharacter(gap);
1072     }
1073   }
1074
1075   /**
1076    * DOCUMENT ME!
1077    * 
1078    * @param thresh
1079    *          DOCUMENT ME!
1080    */
1081   public void setThreshold(int thresh)
1082   {
1083     threshold = thresh;
1084   }
1085
1086   /**
1087    * DOCUMENT ME!
1088    * 
1089    * @return DOCUMENT ME!
1090    */
1091   public int getThreshold()
1092   {
1093     return threshold;
1094   }
1095
1096   /**
1097    * DOCUMENT ME!
1098    * 
1099    * @param inc
1100    *          DOCUMENT ME!
1101    */
1102   public void setIncrement(int inc)
1103   {
1104     increment = inc;
1105   }
1106
1107   /**
1108    * DOCUMENT ME!
1109    * 
1110    * @return DOCUMENT ME!
1111    */
1112   public int getIncrement()
1113   {
1114     return increment;
1115   }
1116
1117   /**
1118    * DOCUMENT ME!
1119    * 
1120    * @return DOCUMENT ME!
1121    */
1122   public ColumnSelection getColumnSelection()
1123   {
1124     return colSel;
1125   }
1126
1127   /**
1128    * DOCUMENT ME!
1129    * 
1130    * @param tree
1131    *          DOCUMENT ME!
1132    */
1133   public void setCurrentTree(NJTree tree)
1134   {
1135     currentTree = tree;
1136   }
1137
1138   /**
1139    * DOCUMENT ME!
1140    * 
1141    * @return DOCUMENT ME!
1142    */
1143   public NJTree getCurrentTree()
1144   {
1145     return currentTree;
1146   }
1147
1148   /**
1149    * DOCUMENT ME!
1150    * 
1151    * @param b
1152    *          DOCUMENT ME!
1153    */
1154   public void setColourAppliesToAllGroups(boolean b)
1155   {
1156     colourAppliesToAllGroups = b;
1157   }
1158
1159   /**
1160    * DOCUMENT ME!
1161    * 
1162    * @return DOCUMENT ME!
1163    */
1164   public boolean getColourAppliesToAllGroups()
1165   {
1166     return colourAppliesToAllGroups;
1167   }
1168
1169   /**
1170    * DOCUMENT ME!
1171    * 
1172    * @return DOCUMENT ME!
1173    */
1174   public boolean getShowJVSuffix()
1175   {
1176     return showJVSuffix;
1177   }
1178
1179   /**
1180    * DOCUMENT ME!
1181    * 
1182    * @param b
1183    *          DOCUMENT ME!
1184    */
1185   public void setShowJVSuffix(boolean b)
1186   {
1187     showJVSuffix = b;
1188   }
1189
1190   /**
1191    * DOCUMENT ME!
1192    * 
1193    * @return DOCUMENT ME!
1194    */
1195   public boolean getShowAnnotation()
1196   {
1197     return showAnnotation;
1198   }
1199
1200   /**
1201    * DOCUMENT ME!
1202    * 
1203    * @param b
1204    *          DOCUMENT ME!
1205    */
1206   public void setShowAnnotation(boolean b)
1207   {
1208     showAnnotation = b;
1209   }
1210
1211   /**
1212    * DOCUMENT ME!
1213    * 
1214    * @return DOCUMENT ME!
1215    */
1216   public boolean getScaleAboveWrapped()
1217   {
1218     return scaleAboveWrapped;
1219   }
1220
1221   /**
1222    * DOCUMENT ME!
1223    * 
1224    * @return DOCUMENT ME!
1225    */
1226   public boolean getScaleLeftWrapped()
1227   {
1228     return scaleLeftWrapped;
1229   }
1230
1231   /**
1232    * DOCUMENT ME!
1233    * 
1234    * @return DOCUMENT ME!
1235    */
1236   public boolean getScaleRightWrapped()
1237   {
1238     return scaleRightWrapped;
1239   }
1240
1241   /**
1242    * DOCUMENT ME!
1243    * 
1244    * @param b
1245    *          DOCUMENT ME!
1246    */
1247   public void setScaleAboveWrapped(boolean b)
1248   {
1249     scaleAboveWrapped = b;
1250   }
1251
1252   /**
1253    * DOCUMENT ME!
1254    * 
1255    * @param b
1256    *          DOCUMENT ME!
1257    */
1258   public void setScaleLeftWrapped(boolean b)
1259   {
1260     scaleLeftWrapped = b;
1261   }
1262
1263   /**
1264    * DOCUMENT ME!
1265    * 
1266    * @param b
1267    *          DOCUMENT ME!
1268    */
1269   public void setScaleRightWrapped(boolean b)
1270   {
1271     scaleRightWrapped = b;
1272   }
1273
1274   /**
1275    * Property change listener for changes in alignment
1276    * 
1277    * @param listener
1278    *          DOCUMENT ME!
1279    */
1280   public void addPropertyChangeListener(
1281           java.beans.PropertyChangeListener listener)
1282   {
1283     changeSupport.addPropertyChangeListener(listener);
1284   }
1285
1286   /**
1287    * DOCUMENT ME!
1288    * 
1289    * @param listener
1290    *          DOCUMENT ME!
1291    */
1292   public void removePropertyChangeListener(
1293           java.beans.PropertyChangeListener listener)
1294   {
1295     changeSupport.removePropertyChangeListener(listener);
1296   }
1297
1298   /**
1299    * Property change listener for changes in alignment
1300    * 
1301    * @param prop
1302    *          DOCUMENT ME!
1303    * @param oldvalue
1304    *          DOCUMENT ME!
1305    * @param newvalue
1306    *          DOCUMENT ME!
1307    */
1308   public void firePropertyChange(String prop, Object oldvalue,
1309           Object newvalue)
1310   {
1311     changeSupport.firePropertyChange(prop, oldvalue, newvalue);
1312   }
1313
1314   public void setIgnoreGapsConsensus(boolean b, AlignmentPanel ap)
1315   {
1316     ignoreGapsInConsensusCalculation = b;
1317     updateConsensus(ap);
1318     if (globalColourScheme != null)
1319     {
1320       globalColourScheme.setThreshold(globalColourScheme.getThreshold(),
1321               ignoreGapsInConsensusCalculation);
1322     }
1323   }
1324
1325   public boolean getIgnoreGapsConsensus()
1326   {
1327     return ignoreGapsInConsensusCalculation;
1328   }
1329
1330   public void setDataset(boolean b)
1331   {
1332     isDataset = b;
1333   }
1334
1335   public boolean isDataset()
1336   {
1337     return isDataset;
1338   }
1339
1340   public void hideSelectedColumns()
1341   {
1342     if (colSel.size() < 1)
1343     {
1344       return;
1345     }
1346
1347     colSel.hideSelectedColumns();
1348     setSelectionGroup(null);
1349
1350     hasHiddenColumns = true;
1351   }
1352
1353   public void hideColumns(int start, int end)
1354   {
1355     if (start == end)
1356     {
1357       colSel.hideColumns(start);
1358     }
1359     else
1360     {
1361       colSel.hideColumns(start, end);
1362     }
1363
1364     hasHiddenColumns = true;
1365   }
1366
1367   public void hideRepSequences(SequenceI repSequence, SequenceGroup sg)
1368   {
1369     int sSize = sg.getSize();
1370     if (sSize < 2)
1371     {
1372       return;
1373     }
1374
1375     if (hiddenRepSequences == null)
1376     {
1377       hiddenRepSequences = new Hashtable();
1378     }
1379
1380     hiddenRepSequences.put(repSequence, sg);
1381
1382     // Hide all sequences except the repSequence
1383     SequenceI[] seqs = new SequenceI[sSize - 1];
1384     int index = 0;
1385     for (int i = 0; i < sSize; i++)
1386     {
1387       if (sg.getSequenceAt(i) != repSequence)
1388       {
1389         if (index == sSize - 1)
1390         {
1391           return;
1392         }
1393
1394         seqs[index++] = sg.getSequenceAt(i);
1395       }
1396     }
1397     sg.setSeqrep(repSequence);
1398     sg.setHidereps(true);
1399     hideSequence(seqs);
1400
1401   }
1402
1403   public void hideAllSelectedSeqs()
1404   {
1405     if (selectionGroup == null || selectionGroup.getSize() < 1)
1406     {
1407       return;
1408     }
1409
1410     SequenceI[] seqs = selectionGroup.getSequencesInOrder(alignment);
1411
1412     hideSequence(seqs);
1413
1414     setSelectionGroup(null);
1415   }
1416
1417   public void hideSequence(SequenceI[] seq)
1418   {
1419     if (seq != null)
1420     {
1421       for (int i = 0; i < seq.length; i++)
1422       {
1423         alignment.getHiddenSequences().hideSequence(seq[i]);
1424       }
1425       hasHiddenRows = true;
1426       firePropertyChange("alignment", null, alignment.getSequences());
1427     }
1428   }
1429
1430   public void showSequence(int index)
1431   {
1432     Vector tmp = alignment.getHiddenSequences().showSequence(index,
1433             hiddenRepSequences);
1434     if (tmp.size() > 0)
1435     {
1436       if (selectionGroup == null)
1437       {
1438         selectionGroup = new SequenceGroup();
1439         selectionGroup.setEndRes(alignment.getWidth() - 1);
1440       }
1441
1442       for (int t = 0; t < tmp.size(); t++)
1443       {
1444         selectionGroup.addSequence((SequenceI) tmp.elementAt(t), false);
1445       }
1446       firePropertyChange("alignment", null, alignment.getSequences());
1447       sendSelection();
1448     }
1449
1450     if (alignment.getHiddenSequences().getSize() < 1)
1451     {
1452       hasHiddenRows = false;
1453     }
1454   }
1455
1456   public void showColumn(int col)
1457   {
1458     colSel.revealHiddenColumns(col);
1459     if (colSel.getHiddenColumns() == null)
1460     {
1461       hasHiddenColumns = false;
1462     }
1463   }
1464
1465   public void showAllHiddenColumns()
1466   {
1467     colSel.revealAllHiddenColumns();
1468     hasHiddenColumns = false;
1469   }
1470
1471   public void showAllHiddenSeqs()
1472   {
1473     if (alignment.getHiddenSequences().getSize() > 0)
1474     {
1475       if (selectionGroup == null)
1476       {
1477         selectionGroup = new SequenceGroup();
1478         selectionGroup.setEndRes(alignment.getWidth() - 1);
1479       }
1480       Vector tmp = alignment.getHiddenSequences().showAll(
1481               hiddenRepSequences);
1482       for (int t = 0; t < tmp.size(); t++)
1483       {
1484         selectionGroup.addSequence((SequenceI) tmp.elementAt(t), false);
1485       }
1486       firePropertyChange("alignment", null, alignment.getSequences());
1487       sendSelection();
1488       hasHiddenRows = false;
1489       hiddenRepSequences = null;
1490     }
1491   }
1492
1493   public void invertColumnSelection()
1494   {
1495     colSel.invertColumnSelection(0, alignment.getWidth());
1496   }
1497
1498   public int adjustForHiddenSeqs(int alignmentIndex)
1499   {
1500     return alignment.getHiddenSequences().adjustForHiddenSeqs(
1501             alignmentIndex);
1502   }
1503
1504   /**
1505    * This method returns an array of new SequenceI objects derived from the
1506    * whole alignment or just the current selection with start and end points
1507    * adjusted
1508    * 
1509    * @note if you need references to the actual SequenceI objects in the
1510    *       alignment or currently selected then use getSequenceSelection()
1511    * @return selection as new sequenceI objects
1512    */
1513   public SequenceI[] getSelectionAsNewSequence()
1514   {
1515     SequenceI[] sequences;
1516
1517     if (selectionGroup == null)
1518     {
1519       sequences = alignment.getSequencesArray();
1520       AlignmentAnnotation[] annots = alignment.getAlignmentAnnotation();
1521       for (int i = 0; i < sequences.length; i++)
1522       {
1523         sequences[i] = new Sequence(sequences[i], annots); // construct new
1524         // sequence with
1525         // subset of visible
1526         // annotation
1527       }
1528     }
1529     else
1530     {
1531       sequences = selectionGroup.getSelectionAsNewSequences(alignment);
1532     }
1533
1534     return sequences;
1535   }
1536
1537   /**
1538    * get the currently selected sequence objects or all the sequences in the
1539    * alignment.
1540    * 
1541    * @return array of references to sequence objects
1542    */
1543   public SequenceI[] getSequenceSelection()
1544   {
1545     SequenceI[] sequences = null;
1546     if (selectionGroup != null)
1547     {
1548       sequences = selectionGroup.getSequencesInOrder(alignment);
1549     }
1550     if (sequences == null)
1551     {
1552       sequences = alignment.getSequencesArray();
1553     }
1554     return sequences;
1555   }
1556
1557   /**
1558    * This method returns the visible alignment as text, as seen on the GUI, ie
1559    * if columns are hidden they will not be returned in the result. Use this for
1560    * calculating trees, PCA, redundancy etc on views which contain hidden
1561    * columns.
1562    * 
1563    * @return String[]
1564    */
1565   public jalview.datamodel.CigarArray getViewAsCigars(
1566           boolean selectedRegionOnly)
1567   {
1568     return new jalview.datamodel.CigarArray(alignment, (hasHiddenColumns ? colSel : null), (selectedRegionOnly ? selectionGroup : null));
1569   }
1570
1571   /**
1572    * return a compact representation of the current alignment selection to pass
1573    * to an analysis function
1574    * 
1575    * @param selectedOnly
1576    *          boolean true to just return the selected view
1577    * @return AlignmentView
1578    */
1579   public jalview.datamodel.AlignmentView getAlignmentView(boolean selectedOnly)
1580   {
1581     return getAlignmentView(selectedOnly, false);
1582   }
1583   
1584   /**
1585    * return a compact representation of the current alignment selection to pass
1586    * to an analysis function
1587    * 
1588    * @param selectedOnly
1589    *          boolean true to just return the selected view
1590    * @param markGroups
1591    *          boolean true to annotate the alignment view with groups on the alignment (and intersecting with selected region if selectedOnly is true) 
1592    * @return AlignmentView
1593    */
1594   public jalview.datamodel.AlignmentView getAlignmentView(boolean selectedOnly, boolean markGroups)
1595   {
1596     return new AlignmentView(alignment, colSel, selectionGroup, hasHiddenColumns, selectedOnly, markGroups);
1597   }
1598
1599   /**
1600    * This method returns the visible alignment as text, as seen on the GUI, ie
1601    * if columns are hidden they will not be returned in the result. Use this for
1602    * calculating trees, PCA, redundancy etc on views which contain hidden
1603    * columns.
1604    * 
1605    * @return String[]
1606    */
1607   public String[] getViewAsString(boolean selectedRegionOnly)
1608   {
1609     String[] selection = null;
1610     SequenceI[] seqs = null;
1611     int i, iSize;
1612     int start = 0, end = 0;
1613     if (selectedRegionOnly && selectionGroup != null)
1614     {
1615       iSize = selectionGroup.getSize();
1616       seqs = selectionGroup.getSequencesInOrder(alignment);
1617       start = selectionGroup.getStartRes();
1618       end = selectionGroup.getEndRes() + 1;
1619     }
1620     else
1621     {
1622       iSize = alignment.getHeight();
1623       seqs = alignment.getSequencesArray();
1624       end = alignment.getWidth();
1625     }
1626
1627     selection = new String[iSize];
1628     if (hasHiddenColumns)
1629     {
1630       selection = colSel.getVisibleSequenceStrings(start, end, seqs);
1631     }
1632     else
1633     {
1634       for (i = 0; i < iSize; i++)
1635       {
1636         selection[i] = seqs[i].getSequenceAsString(start, end);
1637       }
1638
1639     }
1640     return selection;
1641   }
1642
1643   public int[][] getVisibleRegionBoundaries(int min, int max)
1644   {
1645     Vector regions = new Vector();
1646     int start = min;
1647     int end = max;
1648
1649     do
1650     {
1651       if (hasHiddenColumns)
1652       {
1653         if (start == 0)
1654         {
1655           start = colSel.adjustForHiddenColumns(start);
1656         }
1657
1658         end = colSel.getHiddenBoundaryRight(start);
1659         if (start == end)
1660         {
1661           end = max;
1662         }
1663         if (end > max)
1664         {
1665           end = max;
1666         }
1667       }
1668
1669       regions.addElement(new int[]
1670       { start, end });
1671
1672       if (hasHiddenColumns)
1673       {
1674         start = colSel.adjustForHiddenColumns(end);
1675         start = colSel.getHiddenBoundaryLeft(start) + 1;
1676       }
1677     } while (end < max);
1678
1679     int[][] startEnd = new int[regions.size()][2];
1680
1681     regions.copyInto(startEnd);
1682
1683     return startEnd;
1684
1685   }
1686
1687   public boolean getShowHiddenMarkers()
1688   {
1689     return showHiddenMarkers;
1690   }
1691
1692   public void setShowHiddenMarkers(boolean show)
1693   {
1694     showHiddenMarkers = show;
1695   }
1696
1697   public String getSequenceSetId()
1698   {
1699     if (sequenceSetID == null)
1700     {
1701       sequenceSetID = alignment.hashCode() + "";
1702     }
1703
1704     return sequenceSetID;
1705   }
1706
1707   /**
1708    * unique viewId for synchronizing state with stored Jalview Project
1709    * 
1710    */
1711   private String viewId = null;
1712
1713   public String getViewId()
1714   {
1715     if (viewId == null)
1716     {
1717       viewId = this.getSequenceSetId() + "." + this.hashCode() + "";
1718     }
1719     return viewId;
1720   }
1721
1722   public void alignmentChanged(AlignmentPanel ap)
1723   {
1724     if (padGaps)
1725     {
1726       alignment.padGaps();
1727     }
1728     if (hconsensus != null && autoCalculateConsensus)
1729     {
1730       updateConservation(ap);
1731     }
1732     if (autoCalculateConsensus)
1733     {
1734       updateConsensus(ap);
1735     }
1736
1737     // Reset endRes of groups if beyond alignment width
1738     int alWidth = alignment.getWidth();
1739     Vector groups = alignment.getGroups();
1740     if (groups != null)
1741     {
1742       for (int i = 0; i < groups.size(); i++)
1743       {
1744         SequenceGroup sg = (SequenceGroup) groups.elementAt(i);
1745         if (sg.getEndRes() > alWidth)
1746         {
1747           sg.setEndRes(alWidth - 1);
1748         }
1749       }
1750     }
1751
1752     if (selectionGroup != null && selectionGroup.getEndRes() > alWidth)
1753     {
1754       selectionGroup.setEndRes(alWidth - 1);
1755     }
1756
1757     resetAllColourSchemes();
1758
1759     // alignment.adjustSequenceAnnotations();
1760   }
1761
1762   void resetAllColourSchemes()
1763   {
1764     ColourSchemeI cs = globalColourScheme;
1765     if (cs != null)
1766     {
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    * 
2020    * @return true if SelectionGroup changed since last call
2021    */
2022   boolean isSelectionGroupChanged()
2023   {
2024     int hc = (selectionGroup == null) ? -1 : selectionGroup.hashCode();
2025     if (hc != sgrouphash)
2026     {
2027       sgrouphash = hc;
2028       return true;
2029     }
2030     return false;
2031   }
2032
2033   /**
2034    * checks current colsel against record of last hash value, and updates
2035    * record.
2036    * 
2037    * @return true if colsel changed since last call
2038    */
2039   boolean isColSelChanged()
2040   {
2041     int hc = (colSel == null) ? -1 : colSel.hashCode();
2042     if (hc != colselhash)
2043     {
2044       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 }