Jalview 2.6 source licence
[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() : 0; // null
543         // pointer
544         // possibility
545         // here.
546         if (aWidth < 0)
547         {
548           return;
549         }
550
551         consensus.annotations = null;
552         consensus.annotations = new Annotation[aWidth];
553
554         hconsensus = new Hashtable[aWidth];
555         AAFrequency.calculate(alignment.getSequencesArray(), 0, alignment
556                 .getWidth(), hconsensus, true);
557         updateAnnotation(true);
558
559         if (globalColourScheme != null)
560         {
561           globalColourScheme.setConsensus(hconsensus);
562         }
563
564       } catch (OutOfMemoryError error)
565       {
566         alignment.deleteAnnotation(consensus);
567
568         consensus = null;
569         hconsensus = null;
570         new OOMWarning("calculating consensus", error);
571       }
572       UPDATING_CONSENSUS = false;
573       updatingConsensus = false;
574
575       if (ap != null)
576       {
577         ap.paintAlignment(true);
578       }
579     }
580
581     /**
582      * update the consensus annotation from the sequence profile data using
583      * current visualization settings.
584      */
585     public void updateAnnotation()
586     {
587       updateAnnotation(false);
588     }
589
590     protected void updateAnnotation(boolean immediate)
591     {
592       // TODO: make calls thread-safe, so if another thread calls this method, it will either return or wait until one calculation is finished.
593       if (immediate
594               || (!updatingConsensus && consensus != null && hconsensus != null))
595       {
596         AAFrequency.completeConsensus(consensus, hconsensus, 0,
597                 hconsensus.length, ignoreGapsInConsensusCalculation,
598                 showSequenceLogo);
599       }
600     }
601   }
602
603   /**
604    * get the consensus sequence as displayed under the PID consensus annotation
605    * row.
606    * 
607    * @return consensus sequence as a new sequence object
608    */
609   public SequenceI getConsensusSeq()
610   {
611     if (consensus == null)
612     {
613       updateConsensus(null);
614     }
615     if (consensus == null)
616     {
617       return null;
618     }
619     StringBuffer seqs = new StringBuffer();
620     for (int i = 0; i < consensus.annotations.length; i++)
621     {
622       if (consensus.annotations[i] != null)
623       {
624         if (consensus.annotations[i].description.charAt(0) == '[')
625         {
626           seqs.append(consensus.annotations[i].description.charAt(1));
627         }
628         else
629         {
630           seqs.append(consensus.annotations[i].displayCharacter);
631         }
632       }
633     }
634
635     SequenceI sq = new Sequence("Consensus", seqs.toString());
636     sq.setDescription("Percentage Identity Consensus "
637             + ((ignoreGapsInConsensusCalculation) ? " without gaps" : ""));
638     return sq;
639   }
640
641   /**
642    * DOCUMENT ME!
643    * 
644    * @return DOCUMENT ME!
645    */
646   public SequenceGroup getSelectionGroup()
647   {
648     return selectionGroup;
649   }
650
651   /**
652    * DOCUMENT ME!
653    * 
654    * @param sg
655    *          DOCUMENT ME!
656    */
657   public void setSelectionGroup(SequenceGroup sg)
658   {
659     selectionGroup = sg;
660   }
661
662   /**
663    * DOCUMENT ME!
664    * 
665    * @return DOCUMENT ME!
666    */
667   public boolean getConservationSelected()
668   {
669     return conservationColourSelected;
670   }
671
672   /**
673    * DOCUMENT ME!
674    * 
675    * @param b
676    *          DOCUMENT ME!
677    */
678   public void setConservationSelected(boolean b)
679   {
680     conservationColourSelected = b;
681   }
682
683   /**
684    * DOCUMENT ME!
685    * 
686    * @return DOCUMENT ME!
687    */
688   public boolean getAbovePIDThreshold()
689   {
690     return abovePIDThreshold;
691   }
692
693   /**
694    * DOCUMENT ME!
695    * 
696    * @param b
697    *          DOCUMENT ME!
698    */
699   public void setAbovePIDThreshold(boolean b)
700   {
701     abovePIDThreshold = b;
702   }
703
704   /**
705    * DOCUMENT ME!
706    * 
707    * @return DOCUMENT ME!
708    */
709   public int getStartRes()
710   {
711     return startRes;
712   }
713
714   /**
715    * DOCUMENT ME!
716    * 
717    * @return DOCUMENT ME!
718    */
719   public int getEndRes()
720   {
721     return endRes;
722   }
723
724   /**
725    * DOCUMENT ME!
726    * 
727    * @return DOCUMENT ME!
728    */
729   public int getStartSeq()
730   {
731     return startSeq;
732   }
733
734   /**
735    * DOCUMENT ME!
736    * 
737    * @param cs
738    *          DOCUMENT ME!
739    */
740   public void setGlobalColourScheme(ColourSchemeI cs)
741   {
742     globalColourScheme = cs;
743   }
744
745   /**
746    * DOCUMENT ME!
747    * 
748    * @return DOCUMENT ME!
749    */
750   public ColourSchemeI getGlobalColourScheme()
751   {
752     return globalColourScheme;
753   }
754
755   /**
756    * DOCUMENT ME!
757    * 
758    * @param res
759    *          DOCUMENT ME!
760    */
761   public void setStartRes(int res)
762   {
763     this.startRes = res;
764   }
765
766   /**
767    * DOCUMENT ME!
768    * 
769    * @param seq
770    *          DOCUMENT ME!
771    */
772   public void setStartSeq(int seq)
773   {
774     this.startSeq = seq;
775   }
776
777   /**
778    * DOCUMENT ME!
779    * 
780    * @param res
781    *          DOCUMENT ME!
782    */
783   public void setEndRes(int res)
784   {
785     if (res > (alignment.getWidth() - 1))
786     {
787       // log.System.out.println(" Corrected res from " + res + " to maximum " +
788       // (alignment.getWidth()-1));
789       res = alignment.getWidth() - 1;
790     }
791
792     if (res < 0)
793     {
794       res = 0;
795     }
796
797     this.endRes = res;
798   }
799
800   /**
801    * DOCUMENT ME!
802    * 
803    * @param seq
804    *          DOCUMENT ME!
805    */
806   public void setEndSeq(int seq)
807   {
808     if (seq > alignment.getHeight())
809     {
810       seq = alignment.getHeight();
811     }
812
813     if (seq < 0)
814     {
815       seq = 0;
816     }
817
818     this.endSeq = seq;
819   }
820
821   /**
822    * DOCUMENT ME!
823    * 
824    * @return DOCUMENT ME!
825    */
826   public int getEndSeq()
827   {
828     return endSeq;
829   }
830
831   /**
832    * DOCUMENT ME!
833    * 
834    * @param f
835    *          DOCUMENT ME!
836    */
837   public void setFont(Font f)
838   {
839     font = f;
840
841     Container c = new Container();
842
843     java.awt.FontMetrics fm = c.getFontMetrics(font);
844     setCharHeight(fm.getHeight());
845     setCharWidth(fm.charWidth('M'));
846     validCharWidth = true;
847   }
848
849   /**
850    * DOCUMENT ME!
851    * 
852    * @return DOCUMENT ME!
853    */
854   public Font getFont()
855   {
856     return font;
857   }
858
859   /**
860    * DOCUMENT ME!
861    * 
862    * @param w
863    *          DOCUMENT ME!
864    */
865   public void setCharWidth(int w)
866   {
867     this.charWidth = w;
868   }
869
870   /**
871    * DOCUMENT ME!
872    * 
873    * @return DOCUMENT ME!
874    */
875   public int getCharWidth()
876   {
877     return charWidth;
878   }
879
880   /**
881    * DOCUMENT ME!
882    * 
883    * @param h
884    *          DOCUMENT ME!
885    */
886   public void setCharHeight(int h)
887   {
888     this.charHeight = h;
889   }
890
891   /**
892    * DOCUMENT ME!
893    * 
894    * @return DOCUMENT ME!
895    */
896   public int getCharHeight()
897   {
898     return charHeight;
899   }
900
901   /**
902    * DOCUMENT ME!
903    * 
904    * @param w
905    *          DOCUMENT ME!
906    */
907   public void setWrappedWidth(int w)
908   {
909     this.wrappedWidth = w;
910   }
911
912   /**
913    * DOCUMENT ME!
914    * 
915    * @return DOCUMENT ME!
916    */
917   public int getWrappedWidth()
918   {
919     return wrappedWidth;
920   }
921
922   /**
923    * DOCUMENT ME!
924    * 
925    * @return DOCUMENT ME!
926    */
927   public AlignmentI getAlignment()
928   {
929     return alignment;
930   }
931
932   /**
933    * DOCUMENT ME!
934    * 
935    * @param align
936    *          DOCUMENT ME!
937    */
938   public void setAlignment(AlignmentI align)
939   {
940     if (alignment != null && alignment.getCodonFrames() != null)
941     {
942       StructureSelectionManager.getStructureSelectionManager()
943               .removeMappings(alignment.getCodonFrames());
944     }
945     this.alignment = align;
946     if (alignment.getCodonFrames() != null)
947     {
948       StructureSelectionManager.getStructureSelectionManager().addMappings(
949               alignment.getCodonFrames());
950     }
951   }
952
953   /**
954    * DOCUMENT ME!
955    * 
956    * @param state
957    *          DOCUMENT ME!
958    */
959   public void setWrapAlignment(boolean state)
960   {
961     wrapAlignment = state;
962   }
963
964   /**
965    * DOCUMENT ME!
966    * 
967    * @param state
968    *          DOCUMENT ME!
969    */
970   public void setShowText(boolean state)
971   {
972     showText = state;
973   }
974
975   /**
976    * DOCUMENT ME!
977    * 
978    * @param state
979    *          DOCUMENT ME!
980    */
981   public void setRenderGaps(boolean state)
982   {
983     renderGaps = state;
984   }
985
986   /**
987    * DOCUMENT ME!
988    * 
989    * @return DOCUMENT ME!
990    */
991   public boolean getColourText()
992   {
993     return showColourText;
994   }
995
996   /**
997    * DOCUMENT ME!
998    * 
999    * @param state
1000    *          DOCUMENT ME!
1001    */
1002   public void setColourText(boolean state)
1003   {
1004     showColourText = state;
1005   }
1006
1007   /**
1008    * DOCUMENT ME!
1009    * 
1010    * @param state
1011    *          DOCUMENT ME!
1012    */
1013   public void setShowBoxes(boolean state)
1014   {
1015     showBoxes = state;
1016   }
1017
1018   /**
1019    * DOCUMENT ME!
1020    * 
1021    * @return DOCUMENT ME!
1022    */
1023   public boolean getWrapAlignment()
1024   {
1025     return wrapAlignment;
1026   }
1027
1028   /**
1029    * DOCUMENT ME!
1030    * 
1031    * @return DOCUMENT ME!
1032    */
1033   public boolean getShowText()
1034   {
1035     return showText;
1036   }
1037
1038   /**
1039    * DOCUMENT ME!
1040    * 
1041    * @return DOCUMENT ME!
1042    */
1043   public boolean getShowBoxes()
1044   {
1045     return showBoxes;
1046   }
1047
1048   /**
1049    * DOCUMENT ME!
1050    * 
1051    * @return DOCUMENT ME!
1052    */
1053   public char getGapCharacter()
1054   {
1055     return getAlignment().getGapCharacter();
1056   }
1057
1058   /**
1059    * DOCUMENT ME!
1060    * 
1061    * @param gap
1062    *          DOCUMENT ME!
1063    */
1064   public void setGapCharacter(char gap)
1065   {
1066     if (getAlignment() != null)
1067     {
1068       getAlignment().setGapCharacter(gap);
1069     }
1070   }
1071
1072   /**
1073    * DOCUMENT ME!
1074    * 
1075    * @param thresh
1076    *          DOCUMENT ME!
1077    */
1078   public void setThreshold(int thresh)
1079   {
1080     threshold = thresh;
1081   }
1082
1083   /**
1084    * DOCUMENT ME!
1085    * 
1086    * @return DOCUMENT ME!
1087    */
1088   public int getThreshold()
1089   {
1090     return threshold;
1091   }
1092
1093   /**
1094    * DOCUMENT ME!
1095    * 
1096    * @param inc
1097    *          DOCUMENT ME!
1098    */
1099   public void setIncrement(int inc)
1100   {
1101     increment = inc;
1102   }
1103
1104   /**
1105    * DOCUMENT ME!
1106    * 
1107    * @return DOCUMENT ME!
1108    */
1109   public int getIncrement()
1110   {
1111     return increment;
1112   }
1113
1114   /**
1115    * DOCUMENT ME!
1116    * 
1117    * @return DOCUMENT ME!
1118    */
1119   public ColumnSelection getColumnSelection()
1120   {
1121     return colSel;
1122   }
1123
1124   /**
1125    * DOCUMENT ME!
1126    * 
1127    * @param tree
1128    *          DOCUMENT ME!
1129    */
1130   public void setCurrentTree(NJTree tree)
1131   {
1132     currentTree = tree;
1133   }
1134
1135   /**
1136    * DOCUMENT ME!
1137    * 
1138    * @return DOCUMENT ME!
1139    */
1140   public NJTree getCurrentTree()
1141   {
1142     return currentTree;
1143   }
1144
1145   /**
1146    * DOCUMENT ME!
1147    * 
1148    * @param b
1149    *          DOCUMENT ME!
1150    */
1151   public void setColourAppliesToAllGroups(boolean b)
1152   {
1153     colourAppliesToAllGroups = b;
1154   }
1155
1156   /**
1157    * DOCUMENT ME!
1158    * 
1159    * @return DOCUMENT ME!
1160    */
1161   public boolean getColourAppliesToAllGroups()
1162   {
1163     return colourAppliesToAllGroups;
1164   }
1165
1166   /**
1167    * DOCUMENT ME!
1168    * 
1169    * @return DOCUMENT ME!
1170    */
1171   public boolean getShowJVSuffix()
1172   {
1173     return showJVSuffix;
1174   }
1175
1176   /**
1177    * DOCUMENT ME!
1178    * 
1179    * @param b
1180    *          DOCUMENT ME!
1181    */
1182   public void setShowJVSuffix(boolean b)
1183   {
1184     showJVSuffix = b;
1185   }
1186
1187   /**
1188    * DOCUMENT ME!
1189    * 
1190    * @return DOCUMENT ME!
1191    */
1192   public boolean getShowAnnotation()
1193   {
1194     return showAnnotation;
1195   }
1196
1197   /**
1198    * DOCUMENT ME!
1199    * 
1200    * @param b
1201    *          DOCUMENT ME!
1202    */
1203   public void setShowAnnotation(boolean b)
1204   {
1205     showAnnotation = b;
1206   }
1207
1208   /**
1209    * DOCUMENT ME!
1210    * 
1211    * @return DOCUMENT ME!
1212    */
1213   public boolean getScaleAboveWrapped()
1214   {
1215     return scaleAboveWrapped;
1216   }
1217
1218   /**
1219    * DOCUMENT ME!
1220    * 
1221    * @return DOCUMENT ME!
1222    */
1223   public boolean getScaleLeftWrapped()
1224   {
1225     return scaleLeftWrapped;
1226   }
1227
1228   /**
1229    * DOCUMENT ME!
1230    * 
1231    * @return DOCUMENT ME!
1232    */
1233   public boolean getScaleRightWrapped()
1234   {
1235     return scaleRightWrapped;
1236   }
1237
1238   /**
1239    * DOCUMENT ME!
1240    * 
1241    * @param b
1242    *          DOCUMENT ME!
1243    */
1244   public void setScaleAboveWrapped(boolean b)
1245   {
1246     scaleAboveWrapped = b;
1247   }
1248
1249   /**
1250    * DOCUMENT ME!
1251    * 
1252    * @param b
1253    *          DOCUMENT ME!
1254    */
1255   public void setScaleLeftWrapped(boolean b)
1256   {
1257     scaleLeftWrapped = b;
1258   }
1259
1260   /**
1261    * DOCUMENT ME!
1262    * 
1263    * @param b
1264    *          DOCUMENT ME!
1265    */
1266   public void setScaleRightWrapped(boolean b)
1267   {
1268     scaleRightWrapped = b;
1269   }
1270
1271   /**
1272    * Property change listener for changes in alignment
1273    * 
1274    * @param listener
1275    *          DOCUMENT ME!
1276    */
1277   public void addPropertyChangeListener(
1278           java.beans.PropertyChangeListener listener)
1279   {
1280     changeSupport.addPropertyChangeListener(listener);
1281   }
1282
1283   /**
1284    * DOCUMENT ME!
1285    * 
1286    * @param listener
1287    *          DOCUMENT ME!
1288    */
1289   public void removePropertyChangeListener(
1290           java.beans.PropertyChangeListener listener)
1291   {
1292     changeSupport.removePropertyChangeListener(listener);
1293   }
1294
1295   /**
1296    * Property change listener for changes in alignment
1297    * 
1298    * @param prop
1299    *          DOCUMENT ME!
1300    * @param oldvalue
1301    *          DOCUMENT ME!
1302    * @param newvalue
1303    *          DOCUMENT ME!
1304    */
1305   public void firePropertyChange(String prop, Object oldvalue,
1306           Object newvalue)
1307   {
1308     changeSupport.firePropertyChange(prop, oldvalue, newvalue);
1309   }
1310
1311   public void setIgnoreGapsConsensus(boolean b, AlignmentPanel ap)
1312   {
1313     ignoreGapsInConsensusCalculation = b;
1314     updateConsensus(ap);
1315     if (globalColourScheme != null)
1316     {
1317       globalColourScheme.setThreshold(globalColourScheme.getThreshold(),
1318               ignoreGapsInConsensusCalculation);
1319     }
1320   }
1321
1322   public boolean getIgnoreGapsConsensus()
1323   {
1324     return ignoreGapsInConsensusCalculation;
1325   }
1326
1327   public void setDataset(boolean b)
1328   {
1329     isDataset = b;
1330   }
1331
1332   public boolean isDataset()
1333   {
1334     return isDataset;
1335   }
1336
1337   public void hideSelectedColumns()
1338   {
1339     if (colSel.size() < 1)
1340     {
1341       return;
1342     }
1343
1344     colSel.hideSelectedColumns();
1345     setSelectionGroup(null);
1346
1347     hasHiddenColumns = true;
1348   }
1349
1350   public void hideColumns(int start, int end)
1351   {
1352     if (start == end)
1353     {
1354       colSel.hideColumns(start);
1355     }
1356     else
1357     {
1358       colSel.hideColumns(start, end);
1359     }
1360
1361     hasHiddenColumns = true;
1362   }
1363
1364   public void hideRepSequences(SequenceI repSequence, SequenceGroup sg)
1365   {
1366     int sSize = sg.getSize();
1367     if (sSize < 2)
1368     {
1369       return;
1370     }
1371
1372     if (hiddenRepSequences == null)
1373     {
1374       hiddenRepSequences = new Hashtable();
1375     }
1376
1377     hiddenRepSequences.put(repSequence, sg);
1378
1379     // Hide all sequences except the repSequence
1380     SequenceI[] seqs = new SequenceI[sSize - 1];
1381     int index = 0;
1382     for (int i = 0; i < sSize; i++)
1383     {
1384       if (sg.getSequenceAt(i) != repSequence)
1385       {
1386         if (index == sSize - 1)
1387         {
1388           return;
1389         }
1390
1391         seqs[index++] = sg.getSequenceAt(i);
1392       }
1393     }
1394     sg.setSeqrep(repSequence);
1395     sg.setHidereps(true);
1396     hideSequence(seqs);
1397
1398   }
1399
1400   public void hideAllSelectedSeqs()
1401   {
1402     if (selectionGroup == null || selectionGroup.getSize() < 1)
1403     {
1404       return;
1405     }
1406
1407     SequenceI[] seqs = selectionGroup.getSequencesInOrder(alignment);
1408
1409     hideSequence(seqs);
1410
1411     setSelectionGroup(null);
1412   }
1413
1414   public void hideSequence(SequenceI[] seq)
1415   {
1416     if (seq != null)
1417     {
1418       for (int i = 0; i < seq.length; i++)
1419       {
1420         alignment.getHiddenSequences().hideSequence(seq[i]);
1421       }
1422       hasHiddenRows = true;
1423       firePropertyChange("alignment", null, alignment.getSequences());
1424     }
1425   }
1426
1427   public void showSequence(int index)
1428   {
1429     Vector tmp = alignment.getHiddenSequences().showSequence(index,
1430             hiddenRepSequences);
1431     if (tmp.size() > 0)
1432     {
1433       if (selectionGroup == null)
1434       {
1435         selectionGroup = new SequenceGroup();
1436         selectionGroup.setEndRes(alignment.getWidth() - 1);
1437       }
1438
1439       for (int t = 0; t < tmp.size(); t++)
1440       {
1441         selectionGroup.addSequence((SequenceI) tmp.elementAt(t), false);
1442       }
1443       firePropertyChange("alignment", null, alignment.getSequences());
1444       sendSelection();
1445     }
1446
1447     if (alignment.getHiddenSequences().getSize() < 1)
1448     {
1449       hasHiddenRows = false;
1450     }
1451   }
1452
1453   public void showColumn(int col)
1454   {
1455     colSel.revealHiddenColumns(col);
1456     if (colSel.getHiddenColumns() == null)
1457     {
1458       hasHiddenColumns = false;
1459     }
1460   }
1461
1462   public void showAllHiddenColumns()
1463   {
1464     colSel.revealAllHiddenColumns();
1465     hasHiddenColumns = false;
1466   }
1467
1468   public void showAllHiddenSeqs()
1469   {
1470     if (alignment.getHiddenSequences().getSize() > 0)
1471     {
1472       if (selectionGroup == null)
1473       {
1474         selectionGroup = new SequenceGroup();
1475         selectionGroup.setEndRes(alignment.getWidth() - 1);
1476       }
1477       Vector tmp = alignment.getHiddenSequences().showAll(
1478               hiddenRepSequences);
1479       for (int t = 0; t < tmp.size(); t++)
1480       {
1481         selectionGroup.addSequence((SequenceI) tmp.elementAt(t), false);
1482       }
1483       firePropertyChange("alignment", null, alignment.getSequences());
1484       sendSelection();
1485       hasHiddenRows = false;
1486       hiddenRepSequences = null;
1487     }
1488   }
1489
1490   public void invertColumnSelection()
1491   {
1492     colSel.invertColumnSelection(0, alignment.getWidth());
1493   }
1494
1495   public int adjustForHiddenSeqs(int alignmentIndex)
1496   {
1497     return alignment.getHiddenSequences().adjustForHiddenSeqs(
1498             alignmentIndex);
1499   }
1500
1501   /**
1502    * This method returns an array of new SequenceI objects derived from the
1503    * whole alignment or just the current selection with start and end points
1504    * adjusted
1505    * 
1506    * @note if you need references to the actual SequenceI objects in the
1507    *       alignment or currently selected then use getSequenceSelection()
1508    * @return selection as new sequenceI objects
1509    */
1510   public SequenceI[] getSelectionAsNewSequence()
1511   {
1512     SequenceI[] sequences;
1513
1514     if (selectionGroup == null)
1515     {
1516       sequences = alignment.getSequencesArray();
1517       AlignmentAnnotation[] annots = alignment.getAlignmentAnnotation();
1518       for (int i = 0; i < sequences.length; i++)
1519       {
1520         sequences[i] = new Sequence(sequences[i], annots); // construct new
1521         // sequence with
1522         // subset of visible
1523         // annotation
1524       }
1525     }
1526     else
1527     {
1528       sequences = selectionGroup.getSelectionAsNewSequences(alignment);
1529     }
1530
1531     return sequences;
1532   }
1533
1534   /**
1535    * get the currently selected sequence objects or all the sequences in the
1536    * alignment.
1537    * 
1538    * @return array of references to sequence objects
1539    */
1540   public SequenceI[] getSequenceSelection()
1541   {
1542     SequenceI[] sequences = null;
1543     if (selectionGroup != null)
1544     {
1545       sequences = selectionGroup.getSequencesInOrder(alignment);
1546     }
1547     if (sequences == null)
1548     {
1549       sequences = alignment.getSequencesArray();
1550     }
1551     return sequences;
1552   }
1553
1554   /**
1555    * This method returns the visible alignment as text, as seen on the GUI, ie
1556    * if columns are hidden they will not be returned in the result. Use this for
1557    * calculating trees, PCA, redundancy etc on views which contain hidden
1558    * columns.
1559    * 
1560    * @return String[]
1561    */
1562   public jalview.datamodel.CigarArray getViewAsCigars(
1563           boolean selectedRegionOnly)
1564   {
1565     CigarArray selection = null;
1566     SequenceI[] seqs = null;
1567     int i, iSize;
1568     int start = 0, end = 0;
1569     if (selectedRegionOnly && selectionGroup != null)
1570     {
1571       iSize = selectionGroup.getSize();
1572       seqs = selectionGroup.getSequencesInOrder(alignment);
1573       start = selectionGroup.getStartRes();
1574       end = selectionGroup.getEndRes(); // inclusive for start and end in
1575       // SeqCigar constructor
1576     }
1577     else
1578     {
1579       iSize = alignment.getHeight();
1580       seqs = alignment.getSequencesArray();
1581       end = alignment.getWidth() - 1;
1582     }
1583     SeqCigar[] selseqs = new SeqCigar[iSize];
1584     for (i = 0; i < iSize; i++)
1585     {
1586       selseqs[i] = new SeqCigar(seqs[i], start, end);
1587     }
1588     selection = new CigarArray(selseqs);
1589     // now construct the CigarArray operations
1590     if (hasHiddenColumns)
1591     {
1592       Vector regions = colSel.getHiddenColumns();
1593       int[] region;
1594       int hideStart, hideEnd;
1595       int last = start;
1596       for (int j = 0; last < end & j < regions.size(); j++)
1597       {
1598         region = (int[]) regions.elementAt(j);
1599         hideStart = region[0];
1600         hideEnd = region[1];
1601         // edit hidden regions to selection range
1602         if (hideStart < last)
1603         {
1604           if (hideEnd > last)
1605           {
1606             hideStart = last;
1607           }
1608           else
1609           {
1610             continue;
1611           }
1612         }
1613
1614         if (hideStart > end)
1615         {
1616           break;
1617         }
1618
1619         if (hideEnd > end)
1620         {
1621           hideEnd = end;
1622         }
1623
1624         if (hideStart > hideEnd)
1625         {
1626           break;
1627         }
1628         /**
1629          * form operations...
1630          */
1631         if (last < hideStart)
1632         {
1633           selection.addOperation(CigarArray.M, hideStart - last);
1634         }
1635         selection.addOperation(CigarArray.D, 1 + hideEnd - hideStart);
1636         last = hideEnd + 1;
1637       }
1638       // Final match if necessary.
1639       if (last < end)
1640       {
1641         selection.addOperation(CigarArray.M, end - last + 1);
1642       }
1643     }
1644     else
1645     {
1646       selection.addOperation(CigarArray.M, end - start + 1);
1647     }
1648     return selection;
1649   }
1650
1651   /**
1652    * return a compact representation of the current alignment selection to pass
1653    * to an analysis function
1654    * 
1655    * @param selectedOnly
1656    *          boolean true to just return the selected view
1657    * @return AlignmentView
1658    */
1659   jalview.datamodel.AlignmentView getAlignmentView(boolean selectedOnly)
1660   {
1661     // JBPNote:
1662     // this is here because the AlignmentView constructor modifies the
1663     // CigarArray
1664     // object. Refactoring of Cigar and alignment view representation should
1665     // be done to remove redundancy.
1666     CigarArray aligview = getViewAsCigars(selectedOnly);
1667     if (aligview != null)
1668     {
1669       return new AlignmentView(aligview,
1670               (selectedOnly && selectionGroup != null) ? selectionGroup
1671                       .getStartRes() : 0);
1672     }
1673     return null;
1674   }
1675
1676   /**
1677    * This method returns the visible alignment as text, as seen on the GUI, ie
1678    * if columns are hidden they will not be returned in the result. Use this for
1679    * calculating trees, PCA, redundancy etc on views which contain hidden
1680    * columns.
1681    * 
1682    * @return String[]
1683    */
1684   public String[] getViewAsString(boolean selectedRegionOnly)
1685   {
1686     String[] selection = null;
1687     SequenceI[] seqs = null;
1688     int i, iSize;
1689     int start = 0, end = 0;
1690     if (selectedRegionOnly && selectionGroup != null)
1691     {
1692       iSize = selectionGroup.getSize();
1693       seqs = selectionGroup.getSequencesInOrder(alignment);
1694       start = selectionGroup.getStartRes();
1695       end = selectionGroup.getEndRes() + 1;
1696     }
1697     else
1698     {
1699       iSize = alignment.getHeight();
1700       seqs = alignment.getSequencesArray();
1701       end = alignment.getWidth();
1702     }
1703
1704     selection = new String[iSize];
1705     if (hasHiddenColumns)
1706     {
1707       selection = colSel.getVisibleSequenceStrings(start, end, seqs);
1708     }
1709     else
1710     {
1711       for (i = 0; i < iSize; i++)
1712       {
1713         selection[i] = seqs[i].getSequenceAsString(start, end);
1714       }
1715
1716     }
1717     return selection;
1718   }
1719
1720   public int[][] getVisibleRegionBoundaries(int min, int max)
1721   {
1722     Vector regions = new Vector();
1723     int start = min;
1724     int end = max;
1725
1726     do
1727     {
1728       if (hasHiddenColumns)
1729       {
1730         if (start == 0)
1731         {
1732           start = colSel.adjustForHiddenColumns(start);
1733         }
1734
1735         end = colSel.getHiddenBoundaryRight(start);
1736         if (start == end)
1737         {
1738           end = max;
1739         }
1740         if (end > max)
1741         {
1742           end = max;
1743         }
1744       }
1745
1746       regions.addElement(new int[]
1747       { start, end });
1748
1749       if (hasHiddenColumns)
1750       {
1751         start = colSel.adjustForHiddenColumns(end);
1752         start = colSel.getHiddenBoundaryLeft(start) + 1;
1753       }
1754     } while (end < max);
1755
1756     int[][] startEnd = new int[regions.size()][2];
1757
1758     regions.copyInto(startEnd);
1759
1760     return startEnd;
1761
1762   }
1763
1764   public boolean getShowHiddenMarkers()
1765   {
1766     return showHiddenMarkers;
1767   }
1768
1769   public void setShowHiddenMarkers(boolean show)
1770   {
1771     showHiddenMarkers = show;
1772   }
1773
1774   public String getSequenceSetId()
1775   {
1776     if (sequenceSetID == null)
1777     {
1778       sequenceSetID = alignment.hashCode() + "";
1779     }
1780
1781     return sequenceSetID;
1782   }
1783
1784   /**
1785    * unique viewId for synchronizing state with stored Jalview Project
1786    * 
1787    */
1788   private String viewId = null;
1789
1790   public String getViewId()
1791   {
1792     if (viewId == null)
1793     {
1794       viewId = this.getSequenceSetId() + "." + this.hashCode() + "";
1795     }
1796     return viewId;
1797   }
1798
1799   public void alignmentChanged(AlignmentPanel ap)
1800   {
1801     if (padGaps)
1802     {
1803       alignment.padGaps();
1804     }
1805     if (hconsensus != null && autoCalculateConsensus)
1806     {
1807       updateConservation(ap);
1808     }
1809     if (autoCalculateConsensus)
1810     {
1811       updateConsensus(ap);
1812     }
1813
1814     // Reset endRes of groups if beyond alignment width
1815     int alWidth = alignment.getWidth();
1816     Vector groups = alignment.getGroups();
1817     if (groups != null)
1818     {
1819       for (int i = 0; i < groups.size(); i++)
1820       {
1821         SequenceGroup sg = (SequenceGroup) groups.elementAt(i);
1822         if (sg.getEndRes() > alWidth)
1823         {
1824           sg.setEndRes(alWidth - 1);
1825         }
1826       }
1827     }
1828
1829     if (selectionGroup != null && selectionGroup.getEndRes() > alWidth)
1830     {
1831       selectionGroup.setEndRes(alWidth - 1);
1832     }
1833
1834     resetAllColourSchemes();
1835
1836     // alignment.adjustSequenceAnnotations();
1837   }
1838
1839   void resetAllColourSchemes()
1840   {
1841     ColourSchemeI cs = globalColourScheme;
1842     if (cs != null)
1843     {
1844       if (cs instanceof ClustalxColourScheme)
1845       {
1846         ((ClustalxColourScheme) cs).resetClustalX(alignment.getSequences(),
1847                 alignment.getWidth());
1848       }
1849
1850       cs.setConsensus(hconsensus);
1851       if (cs.conservationApplied())
1852       {
1853         Alignment al = (Alignment) alignment;
1854         Conservation c = new Conservation("All",
1855                 ResidueProperties.propHash, 3, al.getSequences(), 0, al
1856                         .getWidth() - 1);
1857         c.calculate();
1858         c.verdict(false, ConsPercGaps);
1859
1860         cs.setConservation(c);
1861       }
1862     }
1863
1864     int s, sSize = alignment.getGroups().size();
1865     for (s = 0; s < sSize; s++)
1866     {
1867       SequenceGroup sg = (SequenceGroup) alignment.getGroups().elementAt(s);
1868       if (sg.cs != null && sg.cs instanceof ClustalxColourScheme)
1869       {
1870         ((ClustalxColourScheme) sg.cs).resetClustalX(sg
1871                 .getSequences(hiddenRepSequences), sg.getWidth());
1872       }
1873       sg.recalcConservation();
1874     }
1875   }
1876
1877   public Color getSequenceColour(SequenceI seq)
1878   {
1879     if (sequenceColours == null || !sequenceColours.containsKey(seq))
1880     {
1881       return Color.white;
1882     }
1883     else
1884     {
1885       return (Color) sequenceColours.get(seq);
1886     }
1887   }
1888
1889   public void setSequenceColour(SequenceI seq, Color col)
1890   {
1891     if (sequenceColours == null)
1892     {
1893       sequenceColours = new Hashtable();
1894     }
1895
1896     if (col == null)
1897     {
1898       sequenceColours.remove(seq);
1899     }
1900     else
1901     {
1902       sequenceColours.put(seq, col);
1903     }
1904   }
1905
1906   /**
1907    * returns the visible column regions of the alignment
1908    * 
1909    * @param selectedRegionOnly
1910    *          true to just return the contigs intersecting with the selected
1911    *          area
1912    * @return
1913    */
1914   public int[] getViewAsVisibleContigs(boolean selectedRegionOnly)
1915   {
1916     int[] viscontigs = null;
1917     int start = 0, end = 0;
1918     if (selectedRegionOnly && selectionGroup != null)
1919     {
1920       start = selectionGroup.getStartRes();
1921       end = selectionGroup.getEndRes() + 1;
1922     }
1923     else
1924     {
1925       end = alignment.getWidth();
1926     }
1927     viscontigs = colSel.getVisibleContigs(start, end);
1928     return viscontigs;
1929   }
1930
1931   /**
1932    * get hash of undo and redo list for the alignment
1933    * 
1934    * @return long[] { historyList.hashCode, redoList.hashCode };
1935    */
1936   public long[] getUndoRedoHash()
1937   {
1938     if (historyList == null || redoList == null)
1939       return new long[]
1940       { -1, -1 };
1941     return new long[]
1942     { historyList.hashCode(), this.redoList.hashCode() };
1943   }
1944
1945   /**
1946    * test if a particular set of hashcodes are different to the hashcodes for
1947    * the undo and redo list.
1948    * 
1949    * @param undoredo
1950    *          the stored set of hashcodes as returned by getUndoRedoHash
1951    * @return true if the hashcodes differ (ie the alignment has been edited) or
1952    *         the stored hashcode array differs in size
1953    */
1954   public boolean isUndoRedoHashModified(long[] undoredo)
1955   {
1956     if (undoredo == null)
1957     {
1958       return true;
1959     }
1960     long[] cstate = getUndoRedoHash();
1961     if (cstate.length != undoredo.length)
1962     {
1963       return true;
1964     }
1965
1966     for (int i = 0; i < cstate.length; i++)
1967     {
1968       if (cstate[i] != undoredo[i])
1969       {
1970         return true;
1971       }
1972     }
1973     return false;
1974   }
1975
1976   public boolean getCentreColumnLabels()
1977   {
1978     return centreColumnLabels;
1979   }
1980
1981   public void setCentreColumnLabels(boolean centrecolumnlabels)
1982   {
1983     centreColumnLabels = centrecolumnlabels;
1984   }
1985
1986   public void updateSequenceIdColours()
1987   {
1988     Vector groups = alignment.getGroups();
1989     if (sequenceColours == null)
1990     {
1991       sequenceColours = new Hashtable();
1992     }
1993     for (int ig = 0, igSize = groups.size(); ig < igSize; ig++)
1994     {
1995       SequenceGroup sg = (SequenceGroup) groups.elementAt(ig);
1996       if (sg.idColour != null)
1997       {
1998         Vector sqs = sg.getSequences(hiddenRepSequences);
1999         for (int s = 0, sSize = sqs.size(); s < sSize; s++)
2000         {
2001           sequenceColours.put(sqs.elementAt(s), sg.idColour);
2002         }
2003       }
2004     }
2005   }
2006
2007   /**
2008    * enable or disable the display of Database Cross References in the sequence
2009    * ID tooltip
2010    */
2011   public void setShowDbRefs(boolean show)
2012   {
2013     showdbrefs = show;
2014   }
2015
2016   /**
2017    * 
2018    * @return true if Database References are to be displayed on tooltips.
2019    */
2020   public boolean isShowDbRefs()
2021   {
2022     return showdbrefs;
2023   }
2024
2025   /**
2026    * 
2027    * @return true if Non-positional features are to be displayed on tooltips.
2028    */
2029   public boolean isShowNpFeats()
2030   {
2031     return shownpfeats;
2032   }
2033
2034   /**
2035    * enable or disable the display of Non-Positional sequence features in the
2036    * sequence ID tooltip
2037    * 
2038    * @param show
2039    */
2040   public void setShowNpFeats(boolean show)
2041   {
2042     shownpfeats = show;
2043   }
2044
2045   /**
2046    * 
2047    * @return true if view has hidden rows
2048    */
2049   public boolean hasHiddenRows()
2050   {
2051     return hasHiddenRows;
2052   }
2053
2054   /**
2055    * 
2056    * @return true if view has hidden columns
2057    */
2058   public boolean hasHiddenColumns()
2059   {
2060     return hasHiddenColumns;
2061   }
2062
2063   /**
2064    * when set, view will scroll to show the highlighted position
2065    */
2066   public boolean followHighlight = true;
2067
2068   /**
2069    * @return true if view should scroll to show the highlighted region of a
2070    *         sequence
2071    * @return
2072    */
2073   public boolean getFollowHighlight()
2074   {
2075     return followHighlight;
2076   }
2077
2078   public boolean followSelection = true;
2079
2080   /**
2081    * @return true if view selection should always follow the selections
2082    *         broadcast by other selection sources
2083    */
2084   public boolean getFollowSelection()
2085   {
2086     return followSelection;
2087   }
2088
2089   private long sgrouphash = -1, colselhash = -1;
2090
2091   boolean showSeqFeaturesHeight;
2092
2093   /**
2094    * checks current SelectionGroup against record of last hash value, and
2095    * updates record.
2096    * 
2097    * @return true if SelectionGroup changed since last call
2098    */
2099   boolean isSelectionGroupChanged()
2100   {
2101     int hc = (selectionGroup == null) ? -1 : selectionGroup.hashCode();
2102     if (hc != sgrouphash)
2103     {
2104       sgrouphash = hc;
2105       return true;
2106     }
2107     return false;
2108   }
2109
2110   /**
2111    * checks current colsel against record of last hash value, and updates
2112    * record.
2113    * 
2114    * @return true if colsel changed since last call
2115    */
2116   boolean isColSelChanged()
2117   {
2118     int hc = (colSel == null) ? -1 : colSel.hashCode();
2119     if (hc != colselhash)
2120     {
2121       colselhash = hc;
2122       return true;
2123     }
2124     return false;
2125   }
2126
2127   public void sendSelection()
2128   {
2129     jalview.structure.StructureSelectionManager
2130             .getStructureSelectionManager().sendSelection(
2131                     new SequenceGroup(getSelectionGroup()),
2132                     new ColumnSelection(getColumnSelection()), this);
2133   }
2134
2135   public void setShowSequenceFeaturesHeight(boolean selected)
2136   {
2137     showSeqFeaturesHeight = selected;
2138   }
2139
2140   public boolean getShowSequenceFeaturesHeight()
2141   {
2142     return showSeqFeaturesHeight;
2143   }
2144
2145   boolean showUnconserved = false;
2146
2147   public boolean getShowUnconserved()
2148   {
2149     return showUnconserved;
2150   }
2151
2152   public void setShowUnconserved(boolean showunconserved)
2153   {
2154     showUnconserved = showunconserved;
2155   }
2156
2157   /**
2158    * return the alignPanel containing the given viewport. Use this to get the
2159    * components currently handling the given viewport.
2160    * 
2161    * @param av
2162    * @return null or an alignPanel guaranteed to have non-null alignFrame
2163    *         reference
2164    */
2165   public AlignmentPanel getAlignPanel()
2166   {
2167     AlignmentPanel[] aps = PaintRefresher.getAssociatedPanels(this
2168             .getSequenceSetId());
2169     AlignmentPanel ap = null;
2170     for (int p = 0; aps != null && p < aps.length; p++)
2171     {
2172       if (aps[p].av == this)
2173       {
2174         return aps[p];
2175       }
2176     }
2177     return null;
2178   }
2179
2180   public boolean getSortByTree()
2181   {
2182     return sortByTree;
2183   }
2184
2185   public void setSortByTree(boolean sort)
2186   {
2187     sortByTree = sort;
2188   }
2189
2190   /**
2191    * should conservation rows be shown for groups
2192    */
2193   boolean showGroupConservation = false;
2194
2195   /**
2196    * should consensus rows be shown for groups
2197    */
2198   boolean showGroupConsensus = false;
2199
2200   /**
2201    * should consensus profile be rendered by default
2202    */
2203   public boolean showSequenceLogo = false;
2204
2205   /**
2206    * should consensus histograms be rendered by default
2207    */
2208   public boolean showConsensusHistogram = true;
2209
2210   /**
2211    * @return the showConsensusProfile
2212    */
2213   public boolean isShowSequenceLogo()
2214   {
2215     return showSequenceLogo;
2216   }
2217
2218   /**
2219    * @param showSequenceLogo
2220    *          the new value
2221    */
2222   public void setShowSequenceLogo(boolean showSequenceLogo)
2223   {
2224     if (showSequenceLogo != this.showSequenceLogo)
2225     {
2226       // TODO: decouple settings setting from calculation when refactoring
2227       // annotation update method from alignframe to viewport
2228       this.showSequenceLogo = showSequenceLogo;
2229       if (consensusThread != null)
2230       {
2231         consensusThread.updateAnnotation();
2232       }
2233     }
2234     this.showSequenceLogo = showSequenceLogo;
2235   }
2236
2237   /**
2238    * @param showConsensusHistogram
2239    *          the showConsensusHistogram to set
2240    */
2241   public void setShowConsensusHistogram(boolean showConsensusHistogram)
2242   {
2243     this.showConsensusHistogram = showConsensusHistogram;
2244   }
2245
2246   /**
2247    * @return the showGroupConservation
2248    */
2249   public boolean isShowGroupConservation()
2250   {
2251     return showGroupConservation;
2252   }
2253
2254   /**
2255    * @param showGroupConservation
2256    *          the showGroupConservation to set
2257    */
2258   public void setShowGroupConservation(boolean showGroupConservation)
2259   {
2260     this.showGroupConservation = showGroupConservation;
2261   }
2262
2263   /**
2264    * @return the showGroupConsensus
2265    */
2266   public boolean isShowGroupConsensus()
2267   {
2268     return showGroupConsensus;
2269   }
2270
2271   /**
2272    * @param showGroupConsensus
2273    *          the showGroupConsensus to set
2274    */
2275   public void setShowGroupConsensus(boolean showGroupConsensus)
2276   {
2277     this.showGroupConsensus = showGroupConsensus;
2278   }
2279
2280   /**
2281    * 
2282    * @return flag to indicate if the consensus histogram should be rendered by
2283    *         default
2284    */
2285   public boolean isShowConsensusHistogram()
2286   {
2287     return this.showConsensusHistogram;
2288   }
2289
2290   /**
2291    * synthesize a column selection if none exists so it covers the given
2292    * selection group. if wholewidth is false, no column selection is made if the
2293    * selection group covers the whole alignment width.
2294    * 
2295    * @param sg
2296    * @param wholewidth
2297    */
2298   public void expandColSelection(SequenceGroup sg, boolean wholewidth)
2299   {
2300     int sgs, sge;
2301     if (sg != null
2302             && (sgs = sg.getStartRes()) >= 0
2303             && sg.getStartRes() <= (sge = sg.getEndRes())
2304             && (colSel == null || colSel.getSelected() == null || colSel
2305                     .getSelected().size() == 0))
2306     {
2307       if (!wholewidth && alignment.getWidth() == (1 + sge - sgs))
2308       {
2309         // do nothing
2310         return;
2311       }
2312       if (colSel == null)
2313       {
2314         colSel = new ColumnSelection();
2315       }
2316       for (int cspos = sg.getStartRes(); cspos <= sg.getEndRes(); cspos++)
2317       {
2318         colSel.addElement(cspos);
2319       }
2320     }
2321   }
2322 }