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