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