refactored VamsasListener to allow the source of the event to be passed to handlers
[jalview.git] / src / jalview / gui / AlignViewport.java
1 /*
2  * Jalview - A Sequence Alignment Editor and Viewer (Version 2.6)
3  * Copyright (C) 2010 J Procter, AM Waterhouse, G Barton, M Clamp, S Searle
4  * 
5  * This file is part of Jalview.
6  * 
7  * Jalview is free software: you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License 
9  * as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
10  * 
11  * Jalview is distributed in the hope that it will be useful, but 
12  * WITHOUT ANY WARRANTY; without even the implied warranty 
13  * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
14  * PURPOSE.  See the GNU General Public License for more details.
15  * 
16  * You should have received a copy of the GNU General Public License along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
17  */
18 /*
19  * Jalview - A Sequence Alignment Editor and Viewer
20  * Copyright (C) 2007 AM Waterhouse, J Procter, G Barton, M Clamp, S Searle
21  *
22  * This program is free software; you can redistribute it and/or
23  * modify it under the terms of the GNU General Public License
24  * as published by the Free Software Foundation; either version 2
25  * of the License, or (at your option) any later version.
26  *
27  * This program is distributed in the hope that it will be useful,
28  * but WITHOUT ANY WARRANTY; without even the implied warranty of
29  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
30  * GNU General Public License for more details.
31  *
32  * You should have received a copy of the GNU General Public License
33  * along with this program; if not, write to the Free Software
34  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
35  */
36 package jalview.gui;
37
38 import java.util.*;
39
40 import java.awt.*;
41
42 import jalview.analysis.*;
43
44 import jalview.bin.*;
45
46 import jalview.datamodel.*;
47
48 import jalview.schemes.*;
49 import jalview.structure.SelectionSource;
50 import jalview.structure.StructureSelectionManager;
51 import jalview.structure.VamsasSource;
52
53 /**
54  * DOCUMENT ME!
55  * 
56  * @author $author$
57  * @version $Revision$
58  */
59 public class AlignViewport implements SelectionSource, VamsasSource
60 {
61   private static final int RIGHT_JUSTIFY = 1;
62
63   int startRes;
64
65   int endRes;
66
67   int startSeq;
68
69   int endSeq;
70
71   boolean showJVSuffix = true;
72
73   boolean showText = true;
74
75   boolean showColourText = false;
76
77   boolean showBoxes = true;
78
79   boolean wrapAlignment = false;
80
81   boolean renderGaps = true;
82
83   boolean showSequenceFeatures = false;
84
85   boolean showAnnotation = true;
86
87   boolean colourAppliesToAllGroups = true;
88
89   ColourSchemeI globalColourScheme = null;
90
91   boolean conservationColourSelected = false;
92
93   boolean abovePIDThreshold = false;
94
95   SequenceGroup selectionGroup;
96
97   int charHeight;
98
99   int charWidth;
100
101   boolean validCharWidth;
102
103   int wrappedWidth;
104
105   Font font;
106
107   boolean seqNameItalics;
108
109   AlignmentI alignment;
110
111   ColumnSelection colSel = new ColumnSelection();
112
113   int threshold;
114
115   int increment;
116
117   NJTree currentTree = null;
118
119   boolean scaleAboveWrapped = false;
120
121   boolean scaleLeftWrapped = true;
122
123   boolean scaleRightWrapped = true;
124
125   boolean hasHiddenColumns = false;
126
127   boolean hasHiddenRows = false;
128
129   boolean showHiddenMarkers = true;
130
131   boolean cursorMode = false;
132
133   /**
134    * Keys are the feature types which are currently visible. Note: Values are
135    * not used!
136    */
137   Hashtable featuresDisplayed = null;
138
139   /** DOCUMENT ME!! */
140   public Hashtable[] hconsensus;
141
142   AlignmentAnnotation consensus;
143
144   AlignmentAnnotation conservation;
145
146   AlignmentAnnotation quality;
147
148   AlignmentAnnotation[] groupConsensus;
149
150   AlignmentAnnotation[] groupConservation;
151
152   boolean autoCalculateConsensus = true;
153
154   /** DOCUMENT ME!! */
155   public int ConsPercGaps = 25; // JBPNote : This should be a scalable property!
156
157   // JBPNote Prolly only need this in the applet version.
158   private java.beans.PropertyChangeSupport changeSupport = new java.beans.PropertyChangeSupport(
159           this);
160
161   boolean ignoreGapsInConsensusCalculation = false;
162
163   boolean isDataset = false;
164
165   boolean antiAlias = false;
166
167   boolean padGaps = false;
168
169   Rectangle explodedPosition;
170
171   String viewName;
172
173   String sequenceSetID;
174
175   boolean gatherViewsHere = false;
176
177   Stack historyList = new Stack();
178
179   Stack redoList = new Stack();
180
181   Hashtable sequenceColours;
182
183   int thresholdTextColour = 0;
184
185   Color textColour = Color.black;
186
187   Color textColour2 = Color.white;
188
189   boolean rightAlignIds = false;
190
191   Hashtable hiddenRepSequences;
192
193   boolean sortByTree;
194
195   /**
196    * Creates a new AlignViewport object.
197    * 
198    * @param al
199    *          alignment to view
200    */
201   public AlignViewport(AlignmentI al)
202   {
203     setAlignment(al);
204     init();
205   }
206
207   /**
208    * Create a new AlignViewport object with a specific sequence set ID
209    * 
210    * @param al
211    * @param seqsetid
212    *          (may be null - but potential for ambiguous constructor exception)
213    */
214   public AlignViewport(AlignmentI al, String seqsetid)
215   {
216     this(al, seqsetid, null);
217   }
218
219   public AlignViewport(AlignmentI al, String seqsetid, String viewid)
220   {
221     sequenceSetID = seqsetid;
222     viewId = viewid;
223     // TODO remove these once 2.4.VAMSAS release finished
224     if (Cache.log != null && Cache.log.isDebugEnabled() && seqsetid != null)
225     {
226       Cache.log.debug("Setting viewport's sequence set id : "
227               + sequenceSetID);
228     }
229     if (Cache.log != null && Cache.log.isDebugEnabled() && viewId != null)
230     {
231       Cache.log.debug("Setting viewport's view id : " + viewId);
232     }
233     setAlignment(al);
234     init();
235   }
236
237   /**
238    * Create a new AlignViewport with hidden regions
239    * 
240    * @param al
241    *          AlignmentI
242    * @param hiddenColumns
243    *          ColumnSelection
244    */
245   public AlignViewport(AlignmentI al, ColumnSelection hiddenColumns)
246   {
247     setAlignment(al);
248     if (hiddenColumns != null)
249     {
250       this.colSel = hiddenColumns;
251       if (hiddenColumns.getHiddenColumns() != null
252               && hiddenColumns.getHiddenColumns().size() > 0)
253       {
254         hasHiddenColumns = true;
255       }
256       else
257       {
258         hasHiddenColumns = false;
259       }
260     }
261     init();
262   }
263
264   /**
265    * New viewport with hidden columns and an existing sequence set id
266    * 
267    * @param al
268    * @param hiddenColumns
269    * @param seqsetid
270    *          (may be null)
271    */
272   public AlignViewport(AlignmentI al, ColumnSelection hiddenColumns,
273           String seqsetid)
274   {
275     this(al, hiddenColumns, seqsetid, null);
276   }
277
278   /**
279    * New viewport with hidden columns and an existing sequence set id and viewid
280    * 
281    * @param al
282    * @param hiddenColumns
283    * @param seqsetid
284    *          (may be null)
285    * @param viewid
286    *          (may be null)
287    */
288   public AlignViewport(AlignmentI al, ColumnSelection hiddenColumns,
289           String seqsetid, String viewid)
290   {
291     sequenceSetID = seqsetid;
292     viewId = viewid;
293     // TODO remove these once 2.4.VAMSAS release finished
294     if (Cache.log != null && Cache.log.isDebugEnabled() && seqsetid != null)
295     {
296       Cache.log.debug("Setting viewport's sequence set id : "
297               + sequenceSetID);
298     }
299     if (Cache.log != null && Cache.log.isDebugEnabled() && viewId != null)
300     {
301       Cache.log.debug("Setting viewport's view id : " + viewId);
302     }
303     setAlignment(al);
304     if (hiddenColumns != null)
305     {
306       this.colSel = hiddenColumns;
307       if (hiddenColumns.getHiddenColumns() != null
308               && hiddenColumns.getHiddenColumns().size() > 0)
309       {
310         hasHiddenColumns = true;
311       }
312       else
313       {
314         hasHiddenColumns = false;
315       }
316     }
317     init();
318   }
319
320   void init()
321   {
322     this.startRes = 0;
323     this.endRes = alignment.getWidth() - 1;
324     this.startSeq = 0;
325     this.endSeq = alignment.getHeight() - 1;
326
327     antiAlias = Cache.getDefault("ANTI_ALIAS", false);
328
329     showJVSuffix = Cache.getDefault("SHOW_JVSUFFIX", true);
330     showAnnotation = Cache.getDefault("SHOW_ANNOTATIONS", true);
331
332     rightAlignIds = Cache.getDefault("RIGHT_ALIGN_IDS", false);
333     centreColumnLabels = Cache.getDefault("CENTRE_COLUMN_LABELS", false);
334     autoCalculateConsensus = Cache.getDefault("AUTO_CALC_CONSENSUS", true);
335
336     padGaps = Cache.getDefault("PAD_GAPS", true);
337     shownpfeats = Cache.getDefault("SHOW_NPFEATS_TOOLTIP", true);
338     showdbrefs = Cache.getDefault("SHOW_DBREFS_TOOLTIP", true);
339
340     String fontName = Cache.getDefault("FONT_NAME", "SansSerif");
341     String fontStyle = Cache.getDefault("FONT_STYLE", Font.PLAIN + "");
342     String fontSize = Cache.getDefault("FONT_SIZE", "10");
343
344     seqNameItalics = Cache.getDefault("ID_ITALICS", true);
345
346     int style = 0;
347
348     if (fontStyle.equals("bold"))
349     {
350       style = 1;
351     }
352     else if (fontStyle.equals("italic"))
353     {
354       style = 2;
355     }
356
357     setFont(new Font(fontName, style, Integer.parseInt(fontSize)));
358
359     alignment
360             .setGapCharacter(Cache.getDefault("GAP_SYMBOL", "-").charAt(0));
361
362     // We must set conservation and consensus before setting colour,
363     // as Blosum and Clustal require this to be done
364     if (hconsensus == null && !isDataset)
365     {
366       if (!alignment.isNucleotide())
367       {
368         conservation = new AlignmentAnnotation("Conservation",
369                 "Conservation of total alignment less than " + ConsPercGaps
370                         + "% gaps", new Annotation[1], 0f, 11f,
371                 AlignmentAnnotation.BAR_GRAPH);
372         conservation.hasText = true;
373         conservation.autoCalculated = true;
374
375         if (Cache.getDefault("SHOW_CONSERVATION", true))
376         {
377           alignment.addAnnotation(conservation);
378         }
379
380         if (Cache.getDefault("SHOW_QUALITY", true))
381         {
382           quality = new AlignmentAnnotation("Quality",
383                   "Alignment Quality based on Blosum62 scores",
384                   new Annotation[1], 0f, 11f, AlignmentAnnotation.BAR_GRAPH);
385           quality.hasText = true;
386           quality.autoCalculated = true;
387
388           alignment.addAnnotation(quality);
389         }
390         showGroupConservation = Cache.getDefault("SHOW_GROUP_CONSERVATION",
391                 false);
392
393         {
394
395         }
396       }
397       showConsensusHistogram = Cache.getDefault("SHOW_CONSENSUS_HISTOGRAM",
398               true);
399       showSequenceLogo = Cache.getDefault("SHOW_CONSENSUS_LOGO", false);
400       showGroupConsensus = Cache.getDefault("SHOW_GROUP_CONSENSUS", false);
401       // TODO: add menu option action that nulls or creates consensus object
402       // depending on if the user wants to see the annotation or not in a
403       // specific alignment
404       consensus = new AlignmentAnnotation("Consensus", "PID",
405               new Annotation[1], 0f, 100f, AlignmentAnnotation.BAR_GRAPH);
406       consensus.hasText = true;
407       consensus.autoCalculated = true;
408
409       if (Cache.getDefault("SHOW_IDENTITY", true))
410       {
411         alignment.addAnnotation(consensus);
412       }
413     }
414
415     if (jalview.bin.Cache.getProperty("DEFAULT_COLOUR") != null)
416     {
417       globalColourScheme = ColourSchemeProperty.getColour(alignment,
418               jalview.bin.Cache.getProperty("DEFAULT_COLOUR"));
419
420       if (globalColourScheme instanceof UserColourScheme)
421       {
422         globalColourScheme = UserDefinedColours.loadDefaultColours();
423         ((UserColourScheme) globalColourScheme).setThreshold(0,
424                 getIgnoreGapsConsensus());
425       }
426
427       if (globalColourScheme != null)
428       {
429         globalColourScheme.setConsensus(hconsensus);
430       }
431     }
432
433     wrapAlignment = jalview.bin.Cache.getDefault("WRAP_ALIGNMENT", false);
434     showUnconserved = jalview.bin.Cache.getDefault("SHOW_UNCONSERVED",
435             false);
436     sortByTree = jalview.bin.Cache.getDefault("SORT_BY_TREE", false);
437     followSelection = jalview.bin.Cache.getDefault("FOLLOW_SELECTIONS",
438             true);
439   }
440
441   /**
442    * set the flag
443    * 
444    * @param b
445    *          features are displayed if true
446    */
447   public void setShowSequenceFeatures(boolean b)
448   {
449     showSequenceFeatures = b;
450   }
451
452   public boolean getShowSequenceFeatures()
453   {
454     return showSequenceFeatures;
455   }
456
457   ConservationThread conservationThread;
458
459   ConsensusThread consensusThread;
460
461   boolean consUpdateNeeded = false;
462
463   static boolean UPDATING_CONSENSUS = false;
464
465   static boolean UPDATING_CONSERVATION = false;
466
467   boolean updatingConsensus = false;
468
469   boolean updatingConservation = false;
470
471   /**
472    * centre columnar annotation labels in displayed alignment annotation TODO:
473    * add to jalviewXML and annotation display settings
474    */
475   boolean centreColumnLabels = false;
476
477   private boolean showdbrefs;
478
479   private boolean shownpfeats;
480
481   /**
482    * trigger update of conservation annotation
483    */
484   public void updateConservation(final AlignmentPanel ap)
485   {
486     // see note in mantis : issue number 8585
487     if (alignment.isNucleotide() || conservation == null
488             || !autoCalculateConsensus)
489     {
490       return;
491     }
492
493     conservationThread = new ConservationThread(this, ap);
494     conservationThread.start();
495   }
496
497   /**
498    * trigger update of consensus annotation
499    */
500   public void updateConsensus(final AlignmentPanel ap)
501   {
502     // see note in mantis : issue number 8585
503     if (consensus == null || !autoCalculateConsensus)
504     {
505       return;
506     }
507     consensusThread = new ConsensusThread(ap);
508     consensusThread.start();
509   }
510
511   class ConsensusThread extends Thread
512   {
513     AlignmentPanel ap;
514
515     public ConsensusThread(AlignmentPanel ap)
516     {
517       this.ap = ap;
518     }
519
520     public void run()
521     {
522       updatingConsensus = true;
523       while (UPDATING_CONSENSUS)
524       {
525         try
526         {
527           if (ap != null)
528           {
529             ap.paintAlignment(false);
530           }
531
532           Thread.sleep(200);
533         } catch (Exception ex)
534         {
535           ex.printStackTrace();
536         }
537       }
538
539       UPDATING_CONSENSUS = true;
540
541       try
542       {
543         int aWidth = (alignment != null) ? alignment.getWidth() : -1; // null
544         // pointer
545         // possibility
546         // here.
547         if (aWidth <= 0)
548         {
549           updatingConsensus = false;
550           UPDATING_CONSENSUS = false;
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,
559                 alignment.getWidth(), hconsensus, true);
560         updateAnnotation(true);
561
562         if (globalColourScheme != null)
563         {
564           globalColourScheme.setConsensus(hconsensus);
565         }
566
567       } catch (OutOfMemoryError error)
568       {
569         alignment.deleteAnnotation(consensus);
570
571         consensus = null;
572         hconsensus = null;
573         new OOMWarning("calculating consensus", error);
574       }
575       UPDATING_CONSENSUS = false;
576       updatingConsensus = false;
577
578       if (ap != null)
579       {
580         ap.paintAlignment(true);
581       }
582     }
583
584     /**
585      * update the consensus annotation from the sequence profile data using
586      * current visualization settings.
587      */
588     public void updateAnnotation()
589     {
590       updateAnnotation(false);
591     }
592
593     protected void updateAnnotation(boolean immediate)
594     {
595       // TODO: make calls thread-safe, so if another thread calls this method,
596       // it will either return or wait until one calculation is finished.
597       if (immediate
598               || (!updatingConsensus && consensus != null && hconsensus != null))
599       {
600         AAFrequency.completeConsensus(consensus, hconsensus, 0,
601                 hconsensus.length, ignoreGapsInConsensusCalculation,
602                 showSequenceLogo);
603       }
604     }
605   }
606
607   /**
608    * get the consensus sequence as displayed under the PID consensus annotation
609    * row.
610    * 
611    * @return consensus sequence as a new sequence object
612    */
613   public SequenceI getConsensusSeq()
614   {
615     if (consensus == null)
616     {
617       updateConsensus(null);
618     }
619     if (consensus == null)
620     {
621       return null;
622     }
623     StringBuffer seqs = new StringBuffer();
624     for (int i = 0; i < consensus.annotations.length; i++)
625     {
626       if (consensus.annotations[i] != null)
627       {
628         if (consensus.annotations[i].description.charAt(0) == '[')
629         {
630           seqs.append(consensus.annotations[i].description.charAt(1));
631         }
632         else
633         {
634           seqs.append(consensus.annotations[i].displayCharacter);
635         }
636       }
637     }
638
639     SequenceI sq = new Sequence("Consensus", seqs.toString());
640     sq.setDescription("Percentage Identity Consensus "
641             + ((ignoreGapsInConsensusCalculation) ? " without gaps" : ""));
642     return sq;
643   }
644
645   /**
646    * DOCUMENT ME!
647    * 
648    * @return DOCUMENT ME!
649    */
650   public SequenceGroup getSelectionGroup()
651   {
652     return selectionGroup;
653   }
654
655   /**
656    * DOCUMENT ME!
657    * 
658    * @param sg
659    *          DOCUMENT ME!
660    */
661   public void setSelectionGroup(SequenceGroup sg)
662   {
663     selectionGroup = sg;
664   }
665
666   /**
667    * DOCUMENT ME!
668    * 
669    * @return DOCUMENT ME!
670    */
671   public boolean getConservationSelected()
672   {
673     return conservationColourSelected;
674   }
675
676   /**
677    * DOCUMENT ME!
678    * 
679    * @param b
680    *          DOCUMENT ME!
681    */
682   public void setConservationSelected(boolean b)
683   {
684     conservationColourSelected = b;
685   }
686
687   /**
688    * DOCUMENT ME!
689    * 
690    * @return DOCUMENT ME!
691    */
692   public boolean getAbovePIDThreshold()
693   {
694     return abovePIDThreshold;
695   }
696
697   /**
698    * DOCUMENT ME!
699    * 
700    * @param b
701    *          DOCUMENT ME!
702    */
703   public void setAbovePIDThreshold(boolean b)
704   {
705     abovePIDThreshold = b;
706   }
707
708   /**
709    * DOCUMENT ME!
710    * 
711    * @return DOCUMENT ME!
712    */
713   public int getStartRes()
714   {
715     return startRes;
716   }
717
718   /**
719    * DOCUMENT ME!
720    * 
721    * @return DOCUMENT ME!
722    */
723   public int getEndRes()
724   {
725     return endRes;
726   }
727
728   /**
729    * DOCUMENT ME!
730    * 
731    * @return DOCUMENT ME!
732    */
733   public int getStartSeq()
734   {
735     return startSeq;
736   }
737
738   /**
739    * DOCUMENT ME!
740    * 
741    * @param cs
742    *          DOCUMENT ME!
743    */
744   public void setGlobalColourScheme(ColourSchemeI cs)
745   {
746     globalColourScheme = cs;
747   }
748
749   /**
750    * DOCUMENT ME!
751    * 
752    * @return DOCUMENT ME!
753    */
754   public ColourSchemeI getGlobalColourScheme()
755   {
756     return globalColourScheme;
757   }
758
759   /**
760    * DOCUMENT ME!
761    * 
762    * @param res
763    *          DOCUMENT ME!
764    */
765   public void setStartRes(int res)
766   {
767     this.startRes = res;
768   }
769
770   /**
771    * DOCUMENT ME!
772    * 
773    * @param seq
774    *          DOCUMENT ME!
775    */
776   public void setStartSeq(int seq)
777   {
778     this.startSeq = seq;
779   }
780
781   /**
782    * DOCUMENT ME!
783    * 
784    * @param res
785    *          DOCUMENT ME!
786    */
787   public void setEndRes(int res)
788   {
789     if (res > (alignment.getWidth() - 1))
790     {
791       // log.System.out.println(" Corrected res from " + res + " to maximum " +
792       // (alignment.getWidth()-1));
793       res = alignment.getWidth() - 1;
794     }
795
796     if (res < 0)
797     {
798       res = 0;
799     }
800
801     this.endRes = res;
802   }
803
804   /**
805    * DOCUMENT ME!
806    * 
807    * @param seq
808    *          DOCUMENT ME!
809    */
810   public void setEndSeq(int seq)
811   {
812     if (seq > alignment.getHeight())
813     {
814       seq = alignment.getHeight();
815     }
816
817     if (seq < 0)
818     {
819       seq = 0;
820     }
821
822     this.endSeq = seq;
823   }
824
825   /**
826    * DOCUMENT ME!
827    * 
828    * @return DOCUMENT ME!
829    */
830   public int getEndSeq()
831   {
832     return endSeq;
833   }
834
835   /**
836    * DOCUMENT ME!
837    * 
838    * @param f
839    *          DOCUMENT ME!
840    */
841   public void setFont(Font f)
842   {
843     font = f;
844
845     Container c = new Container();
846
847     java.awt.FontMetrics fm = c.getFontMetrics(font);
848     setCharHeight(fm.getHeight());
849     setCharWidth(fm.charWidth('M'));
850     validCharWidth = true;
851   }
852
853   /**
854    * DOCUMENT ME!
855    * 
856    * @return DOCUMENT ME!
857    */
858   public Font getFont()
859   {
860     return font;
861   }
862
863   /**
864    * DOCUMENT ME!
865    * 
866    * @param w
867    *          DOCUMENT ME!
868    */
869   public void setCharWidth(int w)
870   {
871     this.charWidth = w;
872   }
873
874   /**
875    * DOCUMENT ME!
876    * 
877    * @return DOCUMENT ME!
878    */
879   public int getCharWidth()
880   {
881     return charWidth;
882   }
883
884   /**
885    * DOCUMENT ME!
886    * 
887    * @param h
888    *          DOCUMENT ME!
889    */
890   public void setCharHeight(int h)
891   {
892     this.charHeight = h;
893   }
894
895   /**
896    * DOCUMENT ME!
897    * 
898    * @return DOCUMENT ME!
899    */
900   public int getCharHeight()
901   {
902     return charHeight;
903   }
904
905   /**
906    * DOCUMENT ME!
907    * 
908    * @param w
909    *          DOCUMENT ME!
910    */
911   public void setWrappedWidth(int w)
912   {
913     this.wrappedWidth = w;
914   }
915
916   /**
917    * DOCUMENT ME!
918    * 
919    * @return DOCUMENT ME!
920    */
921   public int getWrappedWidth()
922   {
923     return wrappedWidth;
924   }
925
926   /**
927    * DOCUMENT ME!
928    * 
929    * @return DOCUMENT ME!
930    */
931   public AlignmentI getAlignment()
932   {
933     return alignment;
934   }
935
936   /**
937    * DOCUMENT ME!
938    * 
939    * @param align
940    *          DOCUMENT ME!
941    */
942   public void setAlignment(AlignmentI align)
943   {
944     if (alignment != null && alignment.getCodonFrames() != null)
945     {
946       StructureSelectionManager.getStructureSelectionManager()
947               .removeMappings(alignment.getCodonFrames());
948     }
949     this.alignment = align;
950     if (alignment.getCodonFrames() != null)
951     {
952       StructureSelectionManager.getStructureSelectionManager().addMappings(
953               alignment.getCodonFrames());
954     }
955   }
956
957   /**
958    * DOCUMENT ME!
959    * 
960    * @param state
961    *          DOCUMENT ME!
962    */
963   public void setWrapAlignment(boolean state)
964   {
965     wrapAlignment = state;
966   }
967
968   /**
969    * DOCUMENT ME!
970    * 
971    * @param state
972    *          DOCUMENT ME!
973    */
974   public void setShowText(boolean state)
975   {
976     showText = state;
977   }
978
979   /**
980    * DOCUMENT ME!
981    * 
982    * @param state
983    *          DOCUMENT ME!
984    */
985   public void setRenderGaps(boolean state)
986   {
987     renderGaps = state;
988   }
989
990   /**
991    * DOCUMENT ME!
992    * 
993    * @return DOCUMENT ME!
994    */
995   public boolean getColourText()
996   {
997     return showColourText;
998   }
999
1000   /**
1001    * DOCUMENT ME!
1002    * 
1003    * @param state
1004    *          DOCUMENT ME!
1005    */
1006   public void setColourText(boolean state)
1007   {
1008     showColourText = state;
1009   }
1010
1011   /**
1012    * DOCUMENT ME!
1013    * 
1014    * @param state
1015    *          DOCUMENT ME!
1016    */
1017   public void setShowBoxes(boolean state)
1018   {
1019     showBoxes = state;
1020   }
1021
1022   /**
1023    * DOCUMENT ME!
1024    * 
1025    * @return DOCUMENT ME!
1026    */
1027   public boolean getWrapAlignment()
1028   {
1029     return wrapAlignment;
1030   }
1031
1032   /**
1033    * DOCUMENT ME!
1034    * 
1035    * @return DOCUMENT ME!
1036    */
1037   public boolean getShowText()
1038   {
1039     return showText;
1040   }
1041
1042   /**
1043    * DOCUMENT ME!
1044    * 
1045    * @return DOCUMENT ME!
1046    */
1047   public boolean getShowBoxes()
1048   {
1049     return showBoxes;
1050   }
1051
1052   /**
1053    * DOCUMENT ME!
1054    * 
1055    * @return DOCUMENT ME!
1056    */
1057   public char getGapCharacter()
1058   {
1059     return getAlignment().getGapCharacter();
1060   }
1061
1062   /**
1063    * DOCUMENT ME!
1064    * 
1065    * @param gap
1066    *          DOCUMENT ME!
1067    */
1068   public void setGapCharacter(char gap)
1069   {
1070     if (getAlignment() != null)
1071     {
1072       getAlignment().setGapCharacter(gap);
1073     }
1074   }
1075
1076   /**
1077    * DOCUMENT ME!
1078    * 
1079    * @param thresh
1080    *          DOCUMENT ME!
1081    */
1082   public void setThreshold(int thresh)
1083   {
1084     threshold = thresh;
1085   }
1086
1087   /**
1088    * DOCUMENT ME!
1089    * 
1090    * @return DOCUMENT ME!
1091    */
1092   public int getThreshold()
1093   {
1094     return threshold;
1095   }
1096
1097   /**
1098    * DOCUMENT ME!
1099    * 
1100    * @param inc
1101    *          DOCUMENT ME!
1102    */
1103   public void setIncrement(int inc)
1104   {
1105     increment = inc;
1106   }
1107
1108   /**
1109    * DOCUMENT ME!
1110    * 
1111    * @return DOCUMENT ME!
1112    */
1113   public int getIncrement()
1114   {
1115     return increment;
1116   }
1117
1118   /**
1119    * DOCUMENT ME!
1120    * 
1121    * @return DOCUMENT ME!
1122    */
1123   public ColumnSelection getColumnSelection()
1124   {
1125     return colSel;
1126   }
1127
1128   /**
1129    * DOCUMENT ME!
1130    * 
1131    * @param tree
1132    *          DOCUMENT ME!
1133    */
1134   public void setCurrentTree(NJTree tree)
1135   {
1136     currentTree = tree;
1137   }
1138
1139   /**
1140    * DOCUMENT ME!
1141    * 
1142    * @return DOCUMENT ME!
1143    */
1144   public NJTree getCurrentTree()
1145   {
1146     return currentTree;
1147   }
1148
1149   /**
1150    * DOCUMENT ME!
1151    * 
1152    * @param b
1153    *          DOCUMENT ME!
1154    */
1155   public void setColourAppliesToAllGroups(boolean b)
1156   {
1157     colourAppliesToAllGroups = b;
1158   }
1159
1160   /**
1161    * DOCUMENT ME!
1162    * 
1163    * @return DOCUMENT ME!
1164    */
1165   public boolean getColourAppliesToAllGroups()
1166   {
1167     return colourAppliesToAllGroups;
1168   }
1169
1170   /**
1171    * DOCUMENT ME!
1172    * 
1173    * @return DOCUMENT ME!
1174    */
1175   public boolean getShowJVSuffix()
1176   {
1177     return showJVSuffix;
1178   }
1179
1180   /**
1181    * DOCUMENT ME!
1182    * 
1183    * @param b
1184    *          DOCUMENT ME!
1185    */
1186   public void setShowJVSuffix(boolean b)
1187   {
1188     showJVSuffix = b;
1189   }
1190
1191   /**
1192    * DOCUMENT ME!
1193    * 
1194    * @return DOCUMENT ME!
1195    */
1196   public boolean getShowAnnotation()
1197   {
1198     return showAnnotation;
1199   }
1200
1201   /**
1202    * DOCUMENT ME!
1203    * 
1204    * @param b
1205    *          DOCUMENT ME!
1206    */
1207   public void setShowAnnotation(boolean b)
1208   {
1209     showAnnotation = b;
1210   }
1211
1212   /**
1213    * DOCUMENT ME!
1214    * 
1215    * @return DOCUMENT ME!
1216    */
1217   public boolean getScaleAboveWrapped()
1218   {
1219     return scaleAboveWrapped;
1220   }
1221
1222   /**
1223    * DOCUMENT ME!
1224    * 
1225    * @return DOCUMENT ME!
1226    */
1227   public boolean getScaleLeftWrapped()
1228   {
1229     return scaleLeftWrapped;
1230   }
1231
1232   /**
1233    * DOCUMENT ME!
1234    * 
1235    * @return DOCUMENT ME!
1236    */
1237   public boolean getScaleRightWrapped()
1238   {
1239     return scaleRightWrapped;
1240   }
1241
1242   /**
1243    * DOCUMENT ME!
1244    * 
1245    * @param b
1246    *          DOCUMENT ME!
1247    */
1248   public void setScaleAboveWrapped(boolean b)
1249   {
1250     scaleAboveWrapped = b;
1251   }
1252
1253   /**
1254    * DOCUMENT ME!
1255    * 
1256    * @param b
1257    *          DOCUMENT ME!
1258    */
1259   public void setScaleLeftWrapped(boolean b)
1260   {
1261     scaleLeftWrapped = b;
1262   }
1263
1264   /**
1265    * DOCUMENT ME!
1266    * 
1267    * @param b
1268    *          DOCUMENT ME!
1269    */
1270   public void setScaleRightWrapped(boolean b)
1271   {
1272     scaleRightWrapped = b;
1273   }
1274
1275   /**
1276    * Property change listener for changes in alignment
1277    * 
1278    * @param listener
1279    *          DOCUMENT ME!
1280    */
1281   public void addPropertyChangeListener(
1282           java.beans.PropertyChangeListener listener)
1283   {
1284     changeSupport.addPropertyChangeListener(listener);
1285   }
1286
1287   /**
1288    * DOCUMENT ME!
1289    * 
1290    * @param listener
1291    *          DOCUMENT ME!
1292    */
1293   public void removePropertyChangeListener(
1294           java.beans.PropertyChangeListener listener)
1295   {
1296     changeSupport.removePropertyChangeListener(listener);
1297   }
1298
1299   /**
1300    * Property change listener for changes in alignment
1301    * 
1302    * @param prop
1303    *          DOCUMENT ME!
1304    * @param oldvalue
1305    *          DOCUMENT ME!
1306    * @param newvalue
1307    *          DOCUMENT ME!
1308    */
1309   public void firePropertyChange(String prop, Object oldvalue,
1310           Object newvalue)
1311   {
1312     changeSupport.firePropertyChange(prop, oldvalue, newvalue);
1313   }
1314
1315   public void setIgnoreGapsConsensus(boolean b, AlignmentPanel ap)
1316   {
1317     ignoreGapsInConsensusCalculation = b;
1318     updateConsensus(ap);
1319     if (globalColourScheme != null)
1320     {
1321       globalColourScheme.setThreshold(globalColourScheme.getThreshold(),
1322               ignoreGapsInConsensusCalculation);
1323     }
1324   }
1325
1326   public boolean getIgnoreGapsConsensus()
1327   {
1328     return ignoreGapsInConsensusCalculation;
1329   }
1330
1331   public void setDataset(boolean b)
1332   {
1333     isDataset = b;
1334   }
1335
1336   public boolean isDataset()
1337   {
1338     return isDataset;
1339   }
1340
1341   public void hideSelectedColumns()
1342   {
1343     if (colSel.size() < 1)
1344     {
1345       return;
1346     }
1347
1348     colSel.hideSelectedColumns();
1349     setSelectionGroup(null);
1350
1351     hasHiddenColumns = true;
1352   }
1353
1354   public void hideColumns(int start, int end)
1355   {
1356     if (start == end)
1357     {
1358       colSel.hideColumns(start);
1359     }
1360     else
1361     {
1362       colSel.hideColumns(start, end);
1363     }
1364
1365     hasHiddenColumns = true;
1366   }
1367
1368   public void hideRepSequences(SequenceI repSequence, SequenceGroup sg)
1369   {
1370     int sSize = sg.getSize();
1371     if (sSize < 2)
1372     {
1373       return;
1374     }
1375
1376     if (hiddenRepSequences == null)
1377     {
1378       hiddenRepSequences = new Hashtable();
1379     }
1380
1381     hiddenRepSequences.put(repSequence, sg);
1382
1383     // Hide all sequences except the repSequence
1384     SequenceI[] seqs = new SequenceI[sSize - 1];
1385     int index = 0;
1386     for (int i = 0; i < sSize; i++)
1387     {
1388       if (sg.getSequenceAt(i) != repSequence)
1389       {
1390         if (index == sSize - 1)
1391         {
1392           return;
1393         }
1394
1395         seqs[index++] = sg.getSequenceAt(i);
1396       }
1397     }
1398     sg.setSeqrep(repSequence);
1399     sg.setHidereps(true);
1400     hideSequence(seqs);
1401
1402   }
1403
1404   public void hideAllSelectedSeqs()
1405   {
1406     if (selectionGroup == null || selectionGroup.getSize() < 1)
1407     {
1408       return;
1409     }
1410
1411     SequenceI[] seqs = selectionGroup.getSequencesInOrder(alignment);
1412
1413     hideSequence(seqs);
1414
1415     setSelectionGroup(null);
1416   }
1417
1418   public void hideSequence(SequenceI[] seq)
1419   {
1420     if (seq != null)
1421     {
1422       for (int i = 0; i < seq.length; i++)
1423       {
1424         alignment.getHiddenSequences().hideSequence(seq[i]);
1425       }
1426       hasHiddenRows = true;
1427       firePropertyChange("alignment", null, alignment.getSequences());
1428     }
1429   }
1430
1431   public void showSequence(int index)
1432   {
1433     Vector tmp = alignment.getHiddenSequences().showSequence(index,
1434             hiddenRepSequences);
1435     if (tmp.size() > 0)
1436     {
1437       if (selectionGroup == null)
1438       {
1439         selectionGroup = new SequenceGroup();
1440         selectionGroup.setEndRes(alignment.getWidth() - 1);
1441       }
1442
1443       for (int t = 0; t < tmp.size(); t++)
1444       {
1445         selectionGroup.addSequence((SequenceI) tmp.elementAt(t), false);
1446       }
1447       firePropertyChange("alignment", null, alignment.getSequences());
1448       sendSelection();
1449     }
1450
1451     if (alignment.getHiddenSequences().getSize() < 1)
1452     {
1453       hasHiddenRows = false;
1454     }
1455   }
1456
1457   public void showColumn(int col)
1458   {
1459     colSel.revealHiddenColumns(col);
1460     if (colSel.getHiddenColumns() == null)
1461     {
1462       hasHiddenColumns = false;
1463     }
1464   }
1465
1466   public void showAllHiddenColumns()
1467   {
1468     colSel.revealAllHiddenColumns();
1469     hasHiddenColumns = false;
1470   }
1471
1472   public void showAllHiddenSeqs()
1473   {
1474     if (alignment.getHiddenSequences().getSize() > 0)
1475     {
1476       if (selectionGroup == null)
1477       {
1478         selectionGroup = new SequenceGroup();
1479         selectionGroup.setEndRes(alignment.getWidth() - 1);
1480       }
1481       Vector tmp = alignment.getHiddenSequences().showAll(
1482               hiddenRepSequences);
1483       for (int t = 0; t < tmp.size(); t++)
1484       {
1485         selectionGroup.addSequence((SequenceI) tmp.elementAt(t), false);
1486       }
1487       firePropertyChange("alignment", null, alignment.getSequences());
1488       sendSelection();
1489       hasHiddenRows = false;
1490       hiddenRepSequences = null;
1491     }
1492   }
1493
1494   public void invertColumnSelection()
1495   {
1496     colSel.invertColumnSelection(0, alignment.getWidth());
1497   }
1498
1499   public int adjustForHiddenSeqs(int alignmentIndex)
1500   {
1501     return alignment.getHiddenSequences().adjustForHiddenSeqs(
1502             alignmentIndex);
1503   }
1504
1505   /**
1506    * This method returns an array of new SequenceI objects derived from the
1507    * whole alignment or just the current selection with start and end points
1508    * adjusted
1509    * 
1510    * @note if you need references to the actual SequenceI objects in the
1511    *       alignment or currently selected then use getSequenceSelection()
1512    * @return selection as new sequenceI objects
1513    */
1514   public SequenceI[] getSelectionAsNewSequence()
1515   {
1516     SequenceI[] sequences;
1517
1518     if (selectionGroup == null)
1519     {
1520       sequences = alignment.getSequencesArray();
1521       AlignmentAnnotation[] annots = alignment.getAlignmentAnnotation();
1522       for (int i = 0; i < sequences.length; i++)
1523       {
1524         sequences[i] = new Sequence(sequences[i], annots); // construct new
1525         // sequence with
1526         // subset of visible
1527         // annotation
1528       }
1529     }
1530     else
1531     {
1532       sequences = selectionGroup.getSelectionAsNewSequences(alignment);
1533     }
1534
1535     return sequences;
1536   }
1537
1538   /**
1539    * get the currently selected sequence objects or all the sequences in the
1540    * alignment.
1541    * 
1542    * @return array of references to sequence objects
1543    */
1544   public SequenceI[] getSequenceSelection()
1545   {
1546     SequenceI[] sequences = null;
1547     if (selectionGroup != null)
1548     {
1549       sequences = selectionGroup.getSequencesInOrder(alignment);
1550     }
1551     if (sequences == null)
1552     {
1553       sequences = alignment.getSequencesArray();
1554     }
1555     return sequences;
1556   }
1557
1558   /**
1559    * This method returns the visible alignment as text, as seen on the GUI, ie
1560    * if columns are hidden they will not be returned in the result. Use this for
1561    * calculating trees, PCA, redundancy etc on views which contain hidden
1562    * columns.
1563    * 
1564    * @return String[]
1565    */
1566   public jalview.datamodel.CigarArray getViewAsCigars(
1567           boolean selectedRegionOnly)
1568   {
1569     return new jalview.datamodel.CigarArray(alignment, (hasHiddenColumns ? colSel : null), (selectedRegionOnly ? selectionGroup : null));
1570   }
1571
1572   /**
1573    * return a compact representation of the current alignment selection to pass
1574    * to an analysis function
1575    * 
1576    * @param selectedOnly
1577    *          boolean true to just return the selected view
1578    * @return AlignmentView
1579    */
1580   public jalview.datamodel.AlignmentView getAlignmentView(boolean selectedOnly)
1581   {
1582     return getAlignmentView(selectedOnly, false);
1583   }
1584   
1585   /**
1586    * return a compact representation of the current alignment selection to pass
1587    * to an analysis function
1588    * 
1589    * @param selectedOnly
1590    *          boolean true to just return the selected view
1591    * @param markGroups
1592    *          boolean true to annotate the alignment view with groups on the alignment (and intersecting with selected region if selectedOnly is true) 
1593    * @return AlignmentView
1594    */
1595   public jalview.datamodel.AlignmentView getAlignmentView(boolean selectedOnly, boolean markGroups)
1596   {
1597     return new AlignmentView(alignment, colSel, selectionGroup, hasHiddenColumns, selectedOnly, markGroups);
1598   }
1599
1600   /**
1601    * This method returns the visible alignment as text, as seen on the GUI, ie
1602    * if columns are hidden they will not be returned in the result. Use this for
1603    * calculating trees, PCA, redundancy etc on views which contain hidden
1604    * columns.
1605    * 
1606    * @return String[]
1607    */
1608   public String[] getViewAsString(boolean selectedRegionOnly)
1609   {
1610     String[] selection = null;
1611     SequenceI[] seqs = null;
1612     int i, iSize;
1613     int start = 0, end = 0;
1614     if (selectedRegionOnly && selectionGroup != null)
1615     {
1616       iSize = selectionGroup.getSize();
1617       seqs = selectionGroup.getSequencesInOrder(alignment);
1618       start = selectionGroup.getStartRes();
1619       end = selectionGroup.getEndRes() + 1;
1620     }
1621     else
1622     {
1623       iSize = alignment.getHeight();
1624       seqs = alignment.getSequencesArray();
1625       end = alignment.getWidth();
1626     }
1627
1628     selection = new String[iSize];
1629     if (hasHiddenColumns)
1630     {
1631       selection = colSel.getVisibleSequenceStrings(start, end, seqs);
1632     }
1633     else
1634     {
1635       for (i = 0; i < iSize; i++)
1636       {
1637         selection[i] = seqs[i].getSequenceAsString(start, end);
1638       }
1639
1640     }
1641     return selection;
1642   }
1643
1644   public int[][] getVisibleRegionBoundaries(int min, int max)
1645   {
1646     Vector regions = new Vector();
1647     int start = min;
1648     int end = max;
1649
1650     do
1651     {
1652       if (hasHiddenColumns)
1653       {
1654         if (start == 0)
1655         {
1656           start = colSel.adjustForHiddenColumns(start);
1657         }
1658
1659         end = colSel.getHiddenBoundaryRight(start);
1660         if (start == end)
1661         {
1662           end = max;
1663         }
1664         if (end > max)
1665         {
1666           end = max;
1667         }
1668       }
1669
1670       regions.addElement(new int[]
1671       { start, end });
1672
1673       if (hasHiddenColumns)
1674       {
1675         start = colSel.adjustForHiddenColumns(end);
1676         start = colSel.getHiddenBoundaryLeft(start) + 1;
1677       }
1678     } while (end < max);
1679
1680     int[][] startEnd = new int[regions.size()][2];
1681
1682     regions.copyInto(startEnd);
1683
1684     return startEnd;
1685
1686   }
1687
1688   public boolean getShowHiddenMarkers()
1689   {
1690     return showHiddenMarkers;
1691   }
1692
1693   public void setShowHiddenMarkers(boolean show)
1694   {
1695     showHiddenMarkers = show;
1696   }
1697
1698   public String getSequenceSetId()
1699   {
1700     if (sequenceSetID == null)
1701     {
1702       sequenceSetID = alignment.hashCode() + "";
1703     }
1704
1705     return sequenceSetID;
1706   }
1707
1708   /**
1709    * unique viewId for synchronizing state with stored Jalview Project
1710    * 
1711    */
1712   private String viewId = null;
1713
1714   public String getViewId()
1715   {
1716     if (viewId == null)
1717     {
1718       viewId = this.getSequenceSetId() + "." + this.hashCode() + "";
1719     }
1720     return viewId;
1721   }
1722
1723   public void alignmentChanged(AlignmentPanel ap)
1724   {
1725     if (padGaps)
1726     {
1727       alignment.padGaps();
1728     }
1729     if (hconsensus != null && autoCalculateConsensus)
1730     {
1731       updateConservation(ap);
1732     }
1733     if (autoCalculateConsensus)
1734     {
1735       updateConsensus(ap);
1736     }
1737
1738     // Reset endRes of groups if beyond alignment width
1739     int alWidth = alignment.getWidth();
1740     Vector groups = alignment.getGroups();
1741     if (groups != null)
1742     {
1743       for (int i = 0; i < groups.size(); i++)
1744       {
1745         SequenceGroup sg = (SequenceGroup) groups.elementAt(i);
1746         if (sg.getEndRes() > alWidth)
1747         {
1748           sg.setEndRes(alWidth - 1);
1749         }
1750       }
1751     }
1752
1753     if (selectionGroup != null && selectionGroup.getEndRes() > alWidth)
1754     {
1755       selectionGroup.setEndRes(alWidth - 1);
1756     }
1757
1758     resetAllColourSchemes();
1759
1760     // alignment.adjustSequenceAnnotations();
1761   }
1762
1763   void resetAllColourSchemes()
1764   {
1765     ColourSchemeI cs = globalColourScheme;
1766     if (cs != null)
1767     {
1768       if (cs instanceof ClustalxColourScheme)
1769       {
1770         ((ClustalxColourScheme) cs).resetClustalX(alignment.getSequences(),
1771                 alignment.getWidth());
1772       }
1773
1774       cs.setConsensus(hconsensus);
1775       if (cs.conservationApplied())
1776       {
1777         Alignment al = (Alignment) alignment;
1778         Conservation c = new Conservation("All",
1779                 ResidueProperties.propHash, 3, al.getSequences(), 0,
1780                 al.getWidth() - 1);
1781         c.calculate();
1782         c.verdict(false, ConsPercGaps);
1783
1784         cs.setConservation(c);
1785       }
1786     }
1787
1788     int s, sSize = alignment.getGroups().size();
1789     for (s = 0; s < sSize; s++)
1790     {
1791       SequenceGroup sg = (SequenceGroup) alignment.getGroups().elementAt(s);
1792       if (sg.cs != null && sg.cs instanceof ClustalxColourScheme)
1793       {
1794         ((ClustalxColourScheme) sg.cs).resetClustalX(
1795                 sg.getSequences(hiddenRepSequences), sg.getWidth());
1796       }
1797       sg.recalcConservation();
1798     }
1799   }
1800
1801   public Color getSequenceColour(SequenceI seq)
1802   {
1803     if (sequenceColours == null || !sequenceColours.containsKey(seq))
1804     {
1805       return Color.white;
1806     }
1807     else
1808     {
1809       return (Color) sequenceColours.get(seq);
1810     }
1811   }
1812
1813   public void setSequenceColour(SequenceI seq, Color col)
1814   {
1815     if (sequenceColours == null)
1816     {
1817       sequenceColours = new Hashtable();
1818     }
1819
1820     if (col == null)
1821     {
1822       sequenceColours.remove(seq);
1823     }
1824     else
1825     {
1826       sequenceColours.put(seq, col);
1827     }
1828   }
1829
1830   /**
1831    * returns the visible column regions of the alignment
1832    * 
1833    * @param selectedRegionOnly
1834    *          true to just return the contigs intersecting with the selected
1835    *          area
1836    * @return
1837    */
1838   public int[] getViewAsVisibleContigs(boolean selectedRegionOnly)
1839   {
1840     int[] viscontigs = null;
1841     int start = 0, end = 0;
1842     if (selectedRegionOnly && selectionGroup != null)
1843     {
1844       start = selectionGroup.getStartRes();
1845       end = selectionGroup.getEndRes() + 1;
1846     }
1847     else
1848     {
1849       end = alignment.getWidth();
1850     }
1851     viscontigs = colSel.getVisibleContigs(start, end);
1852     return viscontigs;
1853   }
1854
1855   /**
1856    * get hash of undo and redo list for the alignment
1857    * 
1858    * @return long[] { historyList.hashCode, redoList.hashCode };
1859    */
1860   public long[] getUndoRedoHash()
1861   {
1862     if (historyList == null || redoList == null)
1863       return new long[]
1864       { -1, -1 };
1865     return new long[]
1866     { historyList.hashCode(), this.redoList.hashCode() };
1867   }
1868
1869   /**
1870    * test if a particular set of hashcodes are different to the hashcodes for
1871    * the undo and redo list.
1872    * 
1873    * @param undoredo
1874    *          the stored set of hashcodes as returned by getUndoRedoHash
1875    * @return true if the hashcodes differ (ie the alignment has been edited) or
1876    *         the stored hashcode array differs in size
1877    */
1878   public boolean isUndoRedoHashModified(long[] undoredo)
1879   {
1880     if (undoredo == null)
1881     {
1882       return true;
1883     }
1884     long[] cstate = getUndoRedoHash();
1885     if (cstate.length != undoredo.length)
1886     {
1887       return true;
1888     }
1889
1890     for (int i = 0; i < cstate.length; i++)
1891     {
1892       if (cstate[i] != undoredo[i])
1893       {
1894         return true;
1895       }
1896     }
1897     return false;
1898   }
1899
1900   public boolean getCentreColumnLabels()
1901   {
1902     return centreColumnLabels;
1903   }
1904
1905   public void setCentreColumnLabels(boolean centrecolumnlabels)
1906   {
1907     centreColumnLabels = centrecolumnlabels;
1908   }
1909
1910   public void updateSequenceIdColours()
1911   {
1912     Vector groups = alignment.getGroups();
1913     if (sequenceColours == null)
1914     {
1915       sequenceColours = new Hashtable();
1916     }
1917     for (int ig = 0, igSize = groups.size(); ig < igSize; ig++)
1918     {
1919       SequenceGroup sg = (SequenceGroup) groups.elementAt(ig);
1920       if (sg.idColour != null)
1921       {
1922         Vector sqs = sg.getSequences(hiddenRepSequences);
1923         for (int s = 0, sSize = sqs.size(); s < sSize; s++)
1924         {
1925           sequenceColours.put(sqs.elementAt(s), sg.idColour);
1926         }
1927       }
1928     }
1929   }
1930
1931   /**
1932    * enable or disable the display of Database Cross References in the sequence
1933    * ID tooltip
1934    */
1935   public void setShowDbRefs(boolean show)
1936   {
1937     showdbrefs = show;
1938   }
1939
1940   /**
1941    * 
1942    * @return true if Database References are to be displayed on tooltips.
1943    */
1944   public boolean isShowDbRefs()
1945   {
1946     return showdbrefs;
1947   }
1948
1949   /**
1950    * 
1951    * @return true if Non-positional features are to be displayed on tooltips.
1952    */
1953   public boolean isShowNpFeats()
1954   {
1955     return shownpfeats;
1956   }
1957
1958   /**
1959    * enable or disable the display of Non-Positional sequence features in the
1960    * sequence ID tooltip
1961    * 
1962    * @param show
1963    */
1964   public void setShowNpFeats(boolean show)
1965   {
1966     shownpfeats = show;
1967   }
1968
1969   /**
1970    * 
1971    * @return true if view has hidden rows
1972    */
1973   public boolean hasHiddenRows()
1974   {
1975     return hasHiddenRows;
1976   }
1977
1978   /**
1979    * 
1980    * @return true if view has hidden columns
1981    */
1982   public boolean hasHiddenColumns()
1983   {
1984     return hasHiddenColumns;
1985   }
1986
1987   /**
1988    * when set, view will scroll to show the highlighted position
1989    */
1990   public boolean followHighlight = true;
1991
1992   /**
1993    * @return true if view should scroll to show the highlighted region of a
1994    *         sequence
1995    * @return
1996    */
1997   public boolean getFollowHighlight()
1998   {
1999     return followHighlight;
2000   }
2001
2002   public boolean followSelection = true;
2003
2004   /**
2005    * @return true if view selection should always follow the selections
2006    *         broadcast by other selection sources
2007    */
2008   public boolean getFollowSelection()
2009   {
2010     return followSelection;
2011   }
2012
2013   private long sgrouphash = -1, colselhash = -1;
2014
2015   boolean showSeqFeaturesHeight;
2016
2017   /**
2018    * checks current SelectionGroup against record of last hash value, and
2019    * updates record.
2020    * 
2021    * @return true if SelectionGroup changed since last call
2022    */
2023   boolean isSelectionGroupChanged()
2024   {
2025     int hc = (selectionGroup == null) ? -1 : selectionGroup.hashCode();
2026     if (hc != sgrouphash)
2027     {
2028       sgrouphash = hc;
2029       return true;
2030     }
2031     return false;
2032   }
2033
2034   /**
2035    * checks current colsel against record of last hash value, and updates
2036    * record.
2037    * 
2038    * @return true if colsel changed since last call
2039    */
2040   boolean isColSelChanged()
2041   {
2042     int hc = (colSel == null) ? -1 : colSel.hashCode();
2043     if (hc != colselhash)
2044     {
2045       colselhash = hc;
2046       return true;
2047     }
2048     return false;
2049   }
2050
2051   public void sendSelection()
2052   {
2053     jalview.structure.StructureSelectionManager
2054             .getStructureSelectionManager().sendSelection(
2055                     new SequenceGroup(getSelectionGroup()),
2056                     new ColumnSelection(getColumnSelection()), this);
2057   }
2058
2059   public void setShowSequenceFeaturesHeight(boolean selected)
2060   {
2061     showSeqFeaturesHeight = selected;
2062   }
2063
2064   public boolean getShowSequenceFeaturesHeight()
2065   {
2066     return showSeqFeaturesHeight;
2067   }
2068
2069   boolean showUnconserved = false;
2070
2071   public boolean getShowUnconserved()
2072   {
2073     return showUnconserved;
2074   }
2075
2076   public void setShowUnconserved(boolean showunconserved)
2077   {
2078     showUnconserved = showunconserved;
2079   }
2080
2081   /**
2082    * return the alignPanel containing the given viewport. Use this to get the
2083    * components currently handling the given viewport.
2084    * 
2085    * @param av
2086    * @return null or an alignPanel guaranteed to have non-null alignFrame
2087    *         reference
2088    */
2089   public AlignmentPanel getAlignPanel()
2090   {
2091     AlignmentPanel[] aps = PaintRefresher.getAssociatedPanels(this
2092             .getSequenceSetId());
2093     AlignmentPanel ap = null;
2094     for (int p = 0; aps != null && p < aps.length; p++)
2095     {
2096       if (aps[p].av == this)
2097       {
2098         return aps[p];
2099       }
2100     }
2101     return null;
2102   }
2103
2104   public boolean getSortByTree()
2105   {
2106     return sortByTree;
2107   }
2108
2109   public void setSortByTree(boolean sort)
2110   {
2111     sortByTree = sort;
2112   }
2113
2114   /**
2115    * should conservation rows be shown for groups
2116    */
2117   boolean showGroupConservation = false;
2118
2119   /**
2120    * should consensus rows be shown for groups
2121    */
2122   boolean showGroupConsensus = false;
2123
2124   /**
2125    * should consensus profile be rendered by default
2126    */
2127   public boolean showSequenceLogo = false;
2128
2129   /**
2130    * should consensus histograms be rendered by default
2131    */
2132   public boolean showConsensusHistogram = true;
2133
2134   /**
2135    * @return the showConsensusProfile
2136    */
2137   public boolean isShowSequenceLogo()
2138   {
2139     return showSequenceLogo;
2140   }
2141
2142   /**
2143    * @param showSequenceLogo
2144    *          the new value
2145    */
2146   public void setShowSequenceLogo(boolean showSequenceLogo)
2147   {
2148     if (showSequenceLogo != this.showSequenceLogo)
2149     {
2150       // TODO: decouple settings setting from calculation when refactoring
2151       // annotation update method from alignframe to viewport
2152       this.showSequenceLogo = showSequenceLogo;
2153       if (consensusThread != null)
2154       {
2155         consensusThread.updateAnnotation();
2156       }
2157     }
2158     this.showSequenceLogo = showSequenceLogo;
2159   }
2160
2161   /**
2162    * @param showConsensusHistogram
2163    *          the showConsensusHistogram to set
2164    */
2165   public void setShowConsensusHistogram(boolean showConsensusHistogram)
2166   {
2167     this.showConsensusHistogram = showConsensusHistogram;
2168   }
2169
2170   /**
2171    * @return the showGroupConservation
2172    */
2173   public boolean isShowGroupConservation()
2174   {
2175     return showGroupConservation;
2176   }
2177
2178   /**
2179    * @param showGroupConservation
2180    *          the showGroupConservation to set
2181    */
2182   public void setShowGroupConservation(boolean showGroupConservation)
2183   {
2184     this.showGroupConservation = showGroupConservation;
2185   }
2186
2187   /**
2188    * @return the showGroupConsensus
2189    */
2190   public boolean isShowGroupConsensus()
2191   {
2192     return showGroupConsensus;
2193   }
2194
2195   /**
2196    * @param showGroupConsensus
2197    *          the showGroupConsensus to set
2198    */
2199   public void setShowGroupConsensus(boolean showGroupConsensus)
2200   {
2201     this.showGroupConsensus = showGroupConsensus;
2202   }
2203
2204   /**
2205    * 
2206    * @return flag to indicate if the consensus histogram should be rendered by
2207    *         default
2208    */
2209   public boolean isShowConsensusHistogram()
2210   {
2211     return this.showConsensusHistogram;
2212   }
2213
2214   /**
2215    * synthesize a column selection if none exists so it covers the given
2216    * selection group. if wholewidth is false, no column selection is made if the
2217    * selection group covers the whole alignment width.
2218    * 
2219    * @param sg
2220    * @param wholewidth
2221    */
2222   public void expandColSelection(SequenceGroup sg, boolean wholewidth)
2223   {
2224     int sgs, sge;
2225     if (sg != null
2226             && (sgs = sg.getStartRes()) >= 0
2227             && sg.getStartRes() <= (sge = sg.getEndRes())
2228             && (colSel == null || colSel.getSelected() == null || colSel
2229                     .getSelected().size() == 0))
2230     {
2231       if (!wholewidth && alignment.getWidth() == (1 + sge - sgs))
2232       {
2233         // do nothing
2234         return;
2235       }
2236       if (colSel == null)
2237       {
2238         colSel = new ColumnSelection();
2239       }
2240       for (int cspos = sg.getStartRes(); cspos <= sg.getEndRes(); cspos++)
2241       {
2242         colSel.addElement(cspos);
2243       }
2244     }
2245   }
2246 }