patch for JAL-699 so conservation/consensus threads restart correctly
[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     CigarArray selection = null;
1569     SequenceI[] seqs = null;
1570     int i, iSize;
1571     int start = 0, end = 0;
1572     if (selectedRegionOnly && selectionGroup != null)
1573     {
1574       iSize = selectionGroup.getSize();
1575       seqs = selectionGroup.getSequencesInOrder(alignment);
1576       start = selectionGroup.getStartRes();
1577       end = selectionGroup.getEndRes(); // inclusive for start and end in
1578       // SeqCigar constructor
1579     }
1580     else
1581     {
1582       iSize = alignment.getHeight();
1583       seqs = alignment.getSequencesArray();
1584       end = alignment.getWidth() - 1;
1585     }
1586     SeqCigar[] selseqs = new SeqCigar[iSize];
1587     for (i = 0; i < iSize; i++)
1588     {
1589       selseqs[i] = new SeqCigar(seqs[i], start, end);
1590     }
1591     selection = new CigarArray(selseqs);
1592     // now construct the CigarArray operations
1593     if (hasHiddenColumns)
1594     {
1595       Vector regions = colSel.getHiddenColumns();
1596       int[] region;
1597       int hideStart, hideEnd;
1598       int last = start;
1599       for (int j = 0; last < end & j < regions.size(); j++)
1600       {
1601         region = (int[]) regions.elementAt(j);
1602         hideStart = region[0];
1603         hideEnd = region[1];
1604         // edit hidden regions to selection range
1605         if (hideStart < last)
1606         {
1607           if (hideEnd > last)
1608           {
1609             hideStart = last;
1610           }
1611           else
1612           {
1613             continue;
1614           }
1615         }
1616
1617         if (hideStart > end)
1618         {
1619           break;
1620         }
1621
1622         if (hideEnd > end)
1623         {
1624           hideEnd = end;
1625         }
1626
1627         if (hideStart > hideEnd)
1628         {
1629           break;
1630         }
1631         /**
1632          * form operations...
1633          */
1634         if (last < hideStart)
1635         {
1636           selection.addOperation(CigarArray.M, hideStart - last);
1637         }
1638         selection.addOperation(CigarArray.D, 1 + hideEnd - hideStart);
1639         last = hideEnd + 1;
1640       }
1641       // Final match if necessary.
1642       if (last < end)
1643       {
1644         selection.addOperation(CigarArray.M, end - last + 1);
1645       }
1646     }
1647     else
1648     {
1649       selection.addOperation(CigarArray.M, end - start + 1);
1650     }
1651     return selection;
1652   }
1653
1654   /**
1655    * return a compact representation of the current alignment selection to pass
1656    * to an analysis function
1657    * 
1658    * @param selectedOnly
1659    *          boolean true to just return the selected view
1660    * @return AlignmentView
1661    */
1662   jalview.datamodel.AlignmentView getAlignmentView(boolean selectedOnly)
1663   {
1664     // JBPNote:
1665     // this is here because the AlignmentView constructor modifies the
1666     // CigarArray
1667     // object. Refactoring of Cigar and alignment view representation should
1668     // be done to remove redundancy.
1669     CigarArray aligview = getViewAsCigars(selectedOnly);
1670     if (aligview != null)
1671     {
1672       return new AlignmentView(aligview,
1673               (selectedOnly && selectionGroup != null) ? selectionGroup
1674                       .getStartRes() : 0);
1675     }
1676     return null;
1677   }
1678
1679   /**
1680    * This method returns the visible alignment as text, as seen on the GUI, ie
1681    * if columns are hidden they will not be returned in the result. Use this for
1682    * calculating trees, PCA, redundancy etc on views which contain hidden
1683    * columns.
1684    * 
1685    * @return String[]
1686    */
1687   public String[] getViewAsString(boolean selectedRegionOnly)
1688   {
1689     String[] selection = null;
1690     SequenceI[] seqs = null;
1691     int i, iSize;
1692     int start = 0, end = 0;
1693     if (selectedRegionOnly && selectionGroup != null)
1694     {
1695       iSize = selectionGroup.getSize();
1696       seqs = selectionGroup.getSequencesInOrder(alignment);
1697       start = selectionGroup.getStartRes();
1698       end = selectionGroup.getEndRes() + 1;
1699     }
1700     else
1701     {
1702       iSize = alignment.getHeight();
1703       seqs = alignment.getSequencesArray();
1704       end = alignment.getWidth();
1705     }
1706
1707     selection = new String[iSize];
1708     if (hasHiddenColumns)
1709     {
1710       selection = colSel.getVisibleSequenceStrings(start, end, seqs);
1711     }
1712     else
1713     {
1714       for (i = 0; i < iSize; i++)
1715       {
1716         selection[i] = seqs[i].getSequenceAsString(start, end);
1717       }
1718
1719     }
1720     return selection;
1721   }
1722
1723   public int[][] getVisibleRegionBoundaries(int min, int max)
1724   {
1725     Vector regions = new Vector();
1726     int start = min;
1727     int end = max;
1728
1729     do
1730     {
1731       if (hasHiddenColumns)
1732       {
1733         if (start == 0)
1734         {
1735           start = colSel.adjustForHiddenColumns(start);
1736         }
1737
1738         end = colSel.getHiddenBoundaryRight(start);
1739         if (start == end)
1740         {
1741           end = max;
1742         }
1743         if (end > max)
1744         {
1745           end = max;
1746         }
1747       }
1748
1749       regions.addElement(new int[]
1750       { start, end });
1751
1752       if (hasHiddenColumns)
1753       {
1754         start = colSel.adjustForHiddenColumns(end);
1755         start = colSel.getHiddenBoundaryLeft(start) + 1;
1756       }
1757     } while (end < max);
1758
1759     int[][] startEnd = new int[regions.size()][2];
1760
1761     regions.copyInto(startEnd);
1762
1763     return startEnd;
1764
1765   }
1766
1767   public boolean getShowHiddenMarkers()
1768   {
1769     return showHiddenMarkers;
1770   }
1771
1772   public void setShowHiddenMarkers(boolean show)
1773   {
1774     showHiddenMarkers = show;
1775   }
1776
1777   public String getSequenceSetId()
1778   {
1779     if (sequenceSetID == null)
1780     {
1781       sequenceSetID = alignment.hashCode() + "";
1782     }
1783
1784     return sequenceSetID;
1785   }
1786
1787   /**
1788    * unique viewId for synchronizing state with stored Jalview Project
1789    * 
1790    */
1791   private String viewId = null;
1792
1793   public String getViewId()
1794   {
1795     if (viewId == null)
1796     {
1797       viewId = this.getSequenceSetId() + "." + this.hashCode() + "";
1798     }
1799     return viewId;
1800   }
1801
1802   public void alignmentChanged(AlignmentPanel ap)
1803   {
1804     if (padGaps)
1805     {
1806       alignment.padGaps();
1807     }
1808     if (hconsensus != null && autoCalculateConsensus)
1809     {
1810       updateConservation(ap);
1811     }
1812     if (autoCalculateConsensus)
1813     {
1814       updateConsensus(ap);
1815     }
1816
1817     // Reset endRes of groups if beyond alignment width
1818     int alWidth = alignment.getWidth();
1819     Vector groups = alignment.getGroups();
1820     if (groups != null)
1821     {
1822       for (int i = 0; i < groups.size(); i++)
1823       {
1824         SequenceGroup sg = (SequenceGroup) groups.elementAt(i);
1825         if (sg.getEndRes() > alWidth)
1826         {
1827           sg.setEndRes(alWidth - 1);
1828         }
1829       }
1830     }
1831
1832     if (selectionGroup != null && selectionGroup.getEndRes() > alWidth)
1833     {
1834       selectionGroup.setEndRes(alWidth - 1);
1835     }
1836
1837     resetAllColourSchemes();
1838
1839     // alignment.adjustSequenceAnnotations();
1840   }
1841
1842   void resetAllColourSchemes()
1843   {
1844     ColourSchemeI cs = globalColourScheme;
1845     if (cs != null)
1846     {
1847       if (cs instanceof ClustalxColourScheme)
1848       {
1849         ((ClustalxColourScheme) cs).resetClustalX(alignment.getSequences(),
1850                 alignment.getWidth());
1851       }
1852
1853       cs.setConsensus(hconsensus);
1854       if (cs.conservationApplied())
1855       {
1856         Alignment al = (Alignment) alignment;
1857         Conservation c = new Conservation("All",
1858                 ResidueProperties.propHash, 3, al.getSequences(), 0,
1859                 al.getWidth() - 1);
1860         c.calculate();
1861         c.verdict(false, ConsPercGaps);
1862
1863         cs.setConservation(c);
1864       }
1865     }
1866
1867     int s, sSize = alignment.getGroups().size();
1868     for (s = 0; s < sSize; s++)
1869     {
1870       SequenceGroup sg = (SequenceGroup) alignment.getGroups().elementAt(s);
1871       if (sg.cs != null && sg.cs instanceof ClustalxColourScheme)
1872       {
1873         ((ClustalxColourScheme) sg.cs).resetClustalX(
1874                 sg.getSequences(hiddenRepSequences), sg.getWidth());
1875       }
1876       sg.recalcConservation();
1877     }
1878   }
1879
1880   public Color getSequenceColour(SequenceI seq)
1881   {
1882     if (sequenceColours == null || !sequenceColours.containsKey(seq))
1883     {
1884       return Color.white;
1885     }
1886     else
1887     {
1888       return (Color) sequenceColours.get(seq);
1889     }
1890   }
1891
1892   public void setSequenceColour(SequenceI seq, Color col)
1893   {
1894     if (sequenceColours == null)
1895     {
1896       sequenceColours = new Hashtable();
1897     }
1898
1899     if (col == null)
1900     {
1901       sequenceColours.remove(seq);
1902     }
1903     else
1904     {
1905       sequenceColours.put(seq, col);
1906     }
1907   }
1908
1909   /**
1910    * returns the visible column regions of the alignment
1911    * 
1912    * @param selectedRegionOnly
1913    *          true to just return the contigs intersecting with the selected
1914    *          area
1915    * @return
1916    */
1917   public int[] getViewAsVisibleContigs(boolean selectedRegionOnly)
1918   {
1919     int[] viscontigs = null;
1920     int start = 0, end = 0;
1921     if (selectedRegionOnly && selectionGroup != null)
1922     {
1923       start = selectionGroup.getStartRes();
1924       end = selectionGroup.getEndRes() + 1;
1925     }
1926     else
1927     {
1928       end = alignment.getWidth();
1929     }
1930     viscontigs = colSel.getVisibleContigs(start, end);
1931     return viscontigs;
1932   }
1933
1934   /**
1935    * get hash of undo and redo list for the alignment
1936    * 
1937    * @return long[] { historyList.hashCode, redoList.hashCode };
1938    */
1939   public long[] getUndoRedoHash()
1940   {
1941     if (historyList == null || redoList == null)
1942       return new long[]
1943       { -1, -1 };
1944     return new long[]
1945     { historyList.hashCode(), this.redoList.hashCode() };
1946   }
1947
1948   /**
1949    * test if a particular set of hashcodes are different to the hashcodes for
1950    * the undo and redo list.
1951    * 
1952    * @param undoredo
1953    *          the stored set of hashcodes as returned by getUndoRedoHash
1954    * @return true if the hashcodes differ (ie the alignment has been edited) or
1955    *         the stored hashcode array differs in size
1956    */
1957   public boolean isUndoRedoHashModified(long[] undoredo)
1958   {
1959     if (undoredo == null)
1960     {
1961       return true;
1962     }
1963     long[] cstate = getUndoRedoHash();
1964     if (cstate.length != undoredo.length)
1965     {
1966       return true;
1967     }
1968
1969     for (int i = 0; i < cstate.length; i++)
1970     {
1971       if (cstate[i] != undoredo[i])
1972       {
1973         return true;
1974       }
1975     }
1976     return false;
1977   }
1978
1979   public boolean getCentreColumnLabels()
1980   {
1981     return centreColumnLabels;
1982   }
1983
1984   public void setCentreColumnLabels(boolean centrecolumnlabels)
1985   {
1986     centreColumnLabels = centrecolumnlabels;
1987   }
1988
1989   public void updateSequenceIdColours()
1990   {
1991     Vector groups = alignment.getGroups();
1992     if (sequenceColours == null)
1993     {
1994       sequenceColours = new Hashtable();
1995     }
1996     for (int ig = 0, igSize = groups.size(); ig < igSize; ig++)
1997     {
1998       SequenceGroup sg = (SequenceGroup) groups.elementAt(ig);
1999       if (sg.idColour != null)
2000       {
2001         Vector sqs = sg.getSequences(hiddenRepSequences);
2002         for (int s = 0, sSize = sqs.size(); s < sSize; s++)
2003         {
2004           sequenceColours.put(sqs.elementAt(s), sg.idColour);
2005         }
2006       }
2007     }
2008   }
2009
2010   /**
2011    * enable or disable the display of Database Cross References in the sequence
2012    * ID tooltip
2013    */
2014   public void setShowDbRefs(boolean show)
2015   {
2016     showdbrefs = show;
2017   }
2018
2019   /**
2020    * 
2021    * @return true if Database References are to be displayed on tooltips.
2022    */
2023   public boolean isShowDbRefs()
2024   {
2025     return showdbrefs;
2026   }
2027
2028   /**
2029    * 
2030    * @return true if Non-positional features are to be displayed on tooltips.
2031    */
2032   public boolean isShowNpFeats()
2033   {
2034     return shownpfeats;
2035   }
2036
2037   /**
2038    * enable or disable the display of Non-Positional sequence features in the
2039    * sequence ID tooltip
2040    * 
2041    * @param show
2042    */
2043   public void setShowNpFeats(boolean show)
2044   {
2045     shownpfeats = show;
2046   }
2047
2048   /**
2049    * 
2050    * @return true if view has hidden rows
2051    */
2052   public boolean hasHiddenRows()
2053   {
2054     return hasHiddenRows;
2055   }
2056
2057   /**
2058    * 
2059    * @return true if view has hidden columns
2060    */
2061   public boolean hasHiddenColumns()
2062   {
2063     return hasHiddenColumns;
2064   }
2065
2066   /**
2067    * when set, view will scroll to show the highlighted position
2068    */
2069   public boolean followHighlight = true;
2070
2071   /**
2072    * @return true if view should scroll to show the highlighted region of a
2073    *         sequence
2074    * @return
2075    */
2076   public boolean getFollowHighlight()
2077   {
2078     return followHighlight;
2079   }
2080
2081   public boolean followSelection = true;
2082
2083   /**
2084    * @return true if view selection should always follow the selections
2085    *         broadcast by other selection sources
2086    */
2087   public boolean getFollowSelection()
2088   {
2089     return followSelection;
2090   }
2091
2092   private long sgrouphash = -1, colselhash = -1;
2093
2094   boolean showSeqFeaturesHeight;
2095
2096   /**
2097    * checks current SelectionGroup against record of last hash value, and
2098    * updates record.
2099    * 
2100    * @return true if SelectionGroup changed since last call
2101    */
2102   boolean isSelectionGroupChanged()
2103   {
2104     int hc = (selectionGroup == null) ? -1 : selectionGroup.hashCode();
2105     if (hc != sgrouphash)
2106     {
2107       sgrouphash = hc;
2108       return true;
2109     }
2110     return false;
2111   }
2112
2113   /**
2114    * checks current colsel against record of last hash value, and updates
2115    * record.
2116    * 
2117    * @return true if colsel changed since last call
2118    */
2119   boolean isColSelChanged()
2120   {
2121     int hc = (colSel == null) ? -1 : colSel.hashCode();
2122     if (hc != colselhash)
2123     {
2124       colselhash = hc;
2125       return true;
2126     }
2127     return false;
2128   }
2129
2130   public void sendSelection()
2131   {
2132     jalview.structure.StructureSelectionManager
2133             .getStructureSelectionManager().sendSelection(
2134                     new SequenceGroup(getSelectionGroup()),
2135                     new ColumnSelection(getColumnSelection()), this);
2136   }
2137
2138   public void setShowSequenceFeaturesHeight(boolean selected)
2139   {
2140     showSeqFeaturesHeight = selected;
2141   }
2142
2143   public boolean getShowSequenceFeaturesHeight()
2144   {
2145     return showSeqFeaturesHeight;
2146   }
2147
2148   boolean showUnconserved = false;
2149
2150   public boolean getShowUnconserved()
2151   {
2152     return showUnconserved;
2153   }
2154
2155   public void setShowUnconserved(boolean showunconserved)
2156   {
2157     showUnconserved = showunconserved;
2158   }
2159
2160   /**
2161    * return the alignPanel containing the given viewport. Use this to get the
2162    * components currently handling the given viewport.
2163    * 
2164    * @param av
2165    * @return null or an alignPanel guaranteed to have non-null alignFrame
2166    *         reference
2167    */
2168   public AlignmentPanel getAlignPanel()
2169   {
2170     AlignmentPanel[] aps = PaintRefresher.getAssociatedPanels(this
2171             .getSequenceSetId());
2172     AlignmentPanel ap = null;
2173     for (int p = 0; aps != null && p < aps.length; p++)
2174     {
2175       if (aps[p].av == this)
2176       {
2177         return aps[p];
2178       }
2179     }
2180     return null;
2181   }
2182
2183   public boolean getSortByTree()
2184   {
2185     return sortByTree;
2186   }
2187
2188   public void setSortByTree(boolean sort)
2189   {
2190     sortByTree = sort;
2191   }
2192
2193   /**
2194    * should conservation rows be shown for groups
2195    */
2196   boolean showGroupConservation = false;
2197
2198   /**
2199    * should consensus rows be shown for groups
2200    */
2201   boolean showGroupConsensus = false;
2202
2203   /**
2204    * should consensus profile be rendered by default
2205    */
2206   public boolean showSequenceLogo = false;
2207
2208   /**
2209    * should consensus histograms be rendered by default
2210    */
2211   public boolean showConsensusHistogram = true;
2212
2213   /**
2214    * @return the showConsensusProfile
2215    */
2216   public boolean isShowSequenceLogo()
2217   {
2218     return showSequenceLogo;
2219   }
2220
2221   /**
2222    * @param showSequenceLogo
2223    *          the new value
2224    */
2225   public void setShowSequenceLogo(boolean showSequenceLogo)
2226   {
2227     if (showSequenceLogo != this.showSequenceLogo)
2228     {
2229       // TODO: decouple settings setting from calculation when refactoring
2230       // annotation update method from alignframe to viewport
2231       this.showSequenceLogo = showSequenceLogo;
2232       if (consensusThread != null)
2233       {
2234         consensusThread.updateAnnotation();
2235       }
2236     }
2237     this.showSequenceLogo = showSequenceLogo;
2238   }
2239
2240   /**
2241    * @param showConsensusHistogram
2242    *          the showConsensusHistogram to set
2243    */
2244   public void setShowConsensusHistogram(boolean showConsensusHistogram)
2245   {
2246     this.showConsensusHistogram = showConsensusHistogram;
2247   }
2248
2249   /**
2250    * @return the showGroupConservation
2251    */
2252   public boolean isShowGroupConservation()
2253   {
2254     return showGroupConservation;
2255   }
2256
2257   /**
2258    * @param showGroupConservation
2259    *          the showGroupConservation to set
2260    */
2261   public void setShowGroupConservation(boolean showGroupConservation)
2262   {
2263     this.showGroupConservation = showGroupConservation;
2264   }
2265
2266   /**
2267    * @return the showGroupConsensus
2268    */
2269   public boolean isShowGroupConsensus()
2270   {
2271     return showGroupConsensus;
2272   }
2273
2274   /**
2275    * @param showGroupConsensus
2276    *          the showGroupConsensus to set
2277    */
2278   public void setShowGroupConsensus(boolean showGroupConsensus)
2279   {
2280     this.showGroupConsensus = showGroupConsensus;
2281   }
2282
2283   /**
2284    * 
2285    * @return flag to indicate if the consensus histogram should be rendered by
2286    *         default
2287    */
2288   public boolean isShowConsensusHistogram()
2289   {
2290     return this.showConsensusHistogram;
2291   }
2292
2293   /**
2294    * synthesize a column selection if none exists so it covers the given
2295    * selection group. if wholewidth is false, no column selection is made if the
2296    * selection group covers the whole alignment width.
2297    * 
2298    * @param sg
2299    * @param wholewidth
2300    */
2301   public void expandColSelection(SequenceGroup sg, boolean wholewidth)
2302   {
2303     int sgs, sge;
2304     if (sg != null
2305             && (sgs = sg.getStartRes()) >= 0
2306             && sg.getStartRes() <= (sge = sg.getEndRes())
2307             && (colSel == null || colSel.getSelected() == null || colSel
2308                     .getSelected().size() == 0))
2309     {
2310       if (!wholewidth && alignment.getWidth() == (1 + sge - sgs))
2311       {
2312         // do nothing
2313         return;
2314       }
2315       if (colSel == null)
2316       {
2317         colSel = new ColumnSelection();
2318       }
2319       for (int cspos = sg.getStartRes(); cspos <= sg.getEndRes(); cspos++)
2320       {
2321         colSel.addElement(cspos);
2322       }
2323     }
2324   }
2325 }