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