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