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