Jalview 2.6 source licence
[jalview.git] / src / jalview / appletgui / 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 package jalview.appletgui;
19
20 import java.util.*;
21
22 import java.awt.*;
23
24 import jalview.analysis.*;
25 import jalview.bin.*;
26 import jalview.datamodel.*;
27 import jalview.schemes.*;
28
29 public class AlignViewport
30 {
31   int startRes;
32
33   int endRes;
34
35   int startSeq;
36
37   int endSeq;
38
39   boolean cursorMode = false;
40
41   boolean showJVSuffix = true;
42
43   boolean showText = true;
44
45   boolean showColourText = false;
46
47   boolean showBoxes = true;
48
49   boolean wrapAlignment = false;
50
51   boolean renderGaps = true;
52
53   boolean showSequenceFeatures = false;
54
55   boolean showAnnotation = true;
56
57   boolean showConservation = true;
58
59   boolean showQuality = true;
60
61   boolean showConsensus = true;
62
63   boolean upperCasebold = false;
64
65   boolean colourAppliesToAllGroups = true;
66
67   ColourSchemeI globalColourScheme = null;
68
69   boolean conservationColourSelected = false;
70
71   boolean abovePIDThreshold = false;
72
73   SequenceGroup selectionGroup;
74
75   int charHeight;
76
77   int charWidth;
78
79   int wrappedWidth;
80
81   Font font = new Font("SansSerif", Font.PLAIN, 10);
82
83   boolean validCharWidth = true;
84
85   AlignmentI alignment;
86
87   ColumnSelection colSel = new ColumnSelection();
88
89   int threshold;
90
91   int increment;
92
93   NJTree currentTree = null;
94
95   boolean scaleAboveWrapped = true;
96
97   boolean scaleLeftWrapped = true;
98
99   boolean scaleRightWrapped = true;
100
101   // The following vector holds the features which are
102   // currently visible, in the correct order or rendering
103   public Hashtable featuresDisplayed;
104
105   boolean hasHiddenColumns = false;
106
107   boolean hasHiddenRows = false;
108
109   boolean showHiddenMarkers = true;
110
111   public Hashtable[] hconsensus;
112
113   AlignmentAnnotation consensus;
114
115   AlignmentAnnotation conservation;
116
117   AlignmentAnnotation quality;
118
119   AlignmentAnnotation[] groupConsensus;
120
121   AlignmentAnnotation[] groupConservation;
122
123   boolean autocalculateConsensus = true;
124
125   public int ConsPercGaps = 25; // JBPNote : This should be a scalable property!
126
127   private java.beans.PropertyChangeSupport changeSupport = new java.beans.PropertyChangeSupport(
128           this);
129
130   boolean ignoreGapsInConsensusCalculation = false;
131
132   jalview.bin.JalviewLite applet;
133
134   Hashtable sequenceColours;
135
136   boolean MAC = false;
137
138   Stack historyList = new Stack();
139
140   Stack redoList = new Stack();
141
142   String sequenceSetID;
143
144   Hashtable hiddenRepSequences;
145
146   public AlignViewport(AlignmentI al, JalviewLite applet)
147   {
148     this.applet = applet;
149     setAlignment(al);
150     this.startRes = 0;
151     this.endRes = al.getWidth() - 1;
152     this.startSeq = 0;
153     this.endSeq = al.getHeight() - 1;
154     if (applet != null)
155     {
156       // get the width and height scaling factors if they were specified
157       String param = applet.getParameter("widthScale");
158       if (param != null)
159       {
160         try
161         {
162           widthScale = new Float(param).floatValue();
163         } catch (Exception e)
164         {
165         }
166         if (widthScale <= 1.0)
167         {
168           System.err
169                   .println("Invalid alignment character width scaling factor ("
170                           + widthScale + "). Ignoring.");
171           widthScale = 1;
172         }
173         if (applet.debug)
174         {
175           System.err
176                   .println("Alignment character width scaling factor is now "
177                           + widthScale);
178         }
179       }
180       param = applet.getParameter("heightScale");
181       if (param != null)
182       {
183         try
184         {
185           heightScale = new Float(param).floatValue();
186         } catch (Exception e)
187         {
188         }
189         if (heightScale <= 1.0)
190         {
191           System.err
192                   .println("Invalid alignment character height scaling factor ("
193                           + heightScale + "). Ignoring.");
194           heightScale = 1;
195         }
196         if (applet.debug)
197         {
198           System.err
199                   .println("Alignment character height scaling factor is now "
200                           + heightScale);
201         }
202       }
203     }
204     setFont(font);
205
206     MAC = new jalview.util.Platform().isAMac();
207
208     if (applet != null)
209     {
210       String param = applet.getParameter("showFullId");
211       if (param != null)
212       {
213         showJVSuffix = Boolean.valueOf(param).booleanValue();
214       }
215
216       param = applet.getParameter("showAnnotation");
217       if (param != null)
218       {
219         showAnnotation = Boolean.valueOf(param).booleanValue();
220       }
221
222       param = applet.getParameter("showConservation");
223       if (param != null)
224       {
225         showConservation = Boolean.valueOf(param).booleanValue();
226       }
227
228       param = applet.getParameter("showQuality");
229       if (param != null)
230       {
231         showQuality = Boolean.valueOf(param).booleanValue();
232       }
233
234       param = applet.getParameter("showConsensus");
235       if (param != null)
236       {
237         showConsensus = Boolean.valueOf(param).booleanValue();
238       }
239
240       param = applet.getParameter("showUnconserved");
241       if (param != null)
242       {
243         this.showUnconserved = Boolean.valueOf(param).booleanValue();
244       }
245
246       param = applet.getParameter("upperCase");
247       if (param != null)
248       {
249         if (param.equalsIgnoreCase("bold"))
250         {
251           upperCasebold = true;
252         }
253       }
254       param = applet.getParameter("sortByTree");
255       if (param != null)
256       {
257         sortByTree = Boolean.valueOf(param).booleanValue();
258       }
259
260     }
261
262     if (applet != null)
263     {
264       String colour = applet.getParameter("defaultColour");
265
266       if (colour == null)
267       {
268         colour = applet.getParameter("userDefinedColour");
269         if (colour != null)
270         {
271           colour = "User Defined";
272         }
273       }
274
275       if (colour != null)
276       {
277         globalColourScheme = ColourSchemeProperty.getColour(alignment,
278                 colour);
279         if (globalColourScheme != null)
280         {
281           globalColourScheme.setConsensus(hconsensus);
282         }
283       }
284
285       if (applet.getParameter("userDefinedColour") != null)
286       {
287         ((UserColourScheme) globalColourScheme).parseAppletParameter(applet
288                 .getParameter("userDefinedColour"));
289       }
290     }
291     if (hconsensus == null)
292     {
293       if (!alignment.isNucleotide())
294       {
295         conservation = new AlignmentAnnotation("Conservation",
296                 "Conservation of total alignment less than " + ConsPercGaps
297                         + "% gaps", new Annotation[1], 0f, 11f,
298                 AlignmentAnnotation.BAR_GRAPH);
299         conservation.hasText = true;
300         conservation.autoCalculated = true;
301
302         if (showConservation)
303         {
304           alignment.addAnnotation(conservation);
305         }
306
307         if (showQuality)
308         {
309           quality = new AlignmentAnnotation("Quality",
310                   "Alignment Quality based on Blosum62 scores",
311                   new Annotation[1], 0f, 11f, AlignmentAnnotation.BAR_GRAPH);
312           quality.hasText = true;
313           quality.autoCalculated = true;
314
315           alignment.addAnnotation(quality);
316         }
317       }
318
319       consensus = new AlignmentAnnotation("Consensus", "PID",
320               new Annotation[1], 0f, 100f, AlignmentAnnotation.BAR_GRAPH);
321       consensus.hasText = true;
322       consensus.autoCalculated = true;
323
324       if (showConsensus)
325       {
326         alignment.addAnnotation(consensus);
327       }
328     }
329
330   }
331
332   public void showSequenceFeatures(boolean b)
333   {
334     showSequenceFeatures = b;
335   }
336
337   public boolean getShowSequenceFeatures()
338   {
339     return showSequenceFeatures;
340   }
341
342   class ConservationThread extends Thread
343   {
344     AlignmentPanel ap;
345
346     public ConservationThread(AlignmentPanel ap)
347     {
348       this.ap = ap;
349     }
350
351     public void run()
352     {
353       try
354       {
355         updatingConservation = true;
356
357         while (UPDATING_CONSERVATION)
358         {
359           try
360           {
361             if (ap != null)
362             {
363               ap.paintAlignment(false);
364             }
365             Thread.sleep(200);
366           } catch (Exception ex)
367           {
368             ex.printStackTrace();
369           }
370         }
371
372         UPDATING_CONSERVATION = true;
373
374         int alWidth = alignment.getWidth();
375         if (alWidth < 0)
376         {
377           return;
378         }
379
380         Conservation cons = new jalview.analysis.Conservation("All",
381                 jalview.schemes.ResidueProperties.propHash, 3, alignment
382                         .getSequences(), 0, alWidth - 1);
383
384         cons.calculate();
385         cons.verdict(false, ConsPercGaps);
386
387         if (quality != null)
388         {
389           cons.findQuality();
390         }
391
392         char[] sequence = cons.getConsSequence().getSequence();
393         float minR;
394         float minG;
395         float minB;
396         float maxR;
397         float maxG;
398         float maxB;
399         minR = 0.3f;
400         minG = 0.0f;
401         minB = 0f;
402         maxR = 1.0f - minR;
403         maxG = 0.9f - minG;
404         maxB = 0f - minB; // scalable range for colouring both Conservation and
405         // Quality
406
407         float min = 0f;
408         float max = 11f;
409         float qmin = 0f;
410         float qmax = 0f;
411
412         char c;
413
414         conservation.annotations = new Annotation[alWidth];
415
416         if (quality != null)
417         {
418           quality.graphMax = cons.qualityRange[1].floatValue();
419           quality.annotations = new Annotation[alWidth];
420           qmin = cons.qualityRange[0].floatValue();
421           qmax = cons.qualityRange[1].floatValue();
422         }
423
424         for (int i = 0; i < alWidth; i++)
425         {
426           float value = 0;
427
428           c = sequence[i];
429
430           if (Character.isDigit(c))
431           {
432             value = (int) (c - '0');
433           }
434           else if (c == '*')
435           {
436             value = 11;
437           }
438           else if (c == '+')
439           {
440             value = 10;
441           }
442           // TODO - refactor to use a graduatedColorScheme to calculate the
443           // histogram colors.
444           float vprop = value - min;
445           vprop /= max;
446           conservation.annotations[i] = new Annotation(String.valueOf(c),
447                   String.valueOf(value), ' ', value, new Color(minR
448                           + (maxR * vprop), minG + (maxG * vprop), minB
449                           + (maxB * vprop)));
450
451           // Quality calc
452           if (quality != null)
453           {
454             value = ((Double) cons.quality.elementAt(i)).floatValue();
455             vprop = value - qmin;
456             vprop /= qmax;
457             quality.annotations[i] = new Annotation(" ", String
458                     .valueOf(value), ' ', value, new Color(minR
459                     + (maxR * vprop), minG + (maxG * vprop), minB
460                     + (maxB * vprop)));
461           }
462         }
463       } catch (OutOfMemoryError error)
464       {
465         System.out.println("Out of memory calculating conservation!!");
466         conservation = null;
467         quality = null;
468         System.gc();
469       }
470
471       UPDATING_CONSERVATION = false;
472       updatingConservation = false;
473
474       if (ap != null)
475       {
476         ap.paintAlignment(true);
477       }
478
479     }
480   }
481
482   ConservationThread conservationThread;
483
484   ConsensusThread consensusThread;
485
486   boolean consUpdateNeeded = false;
487
488   static boolean UPDATING_CONSENSUS = false;
489
490   static boolean UPDATING_CONSERVATION = false;
491
492   boolean updatingConsensus = false;
493
494   boolean updatingConservation = false;
495
496   /**
497    * DOCUMENT ME!
498    */
499   public void updateConservation(final AlignmentPanel ap)
500   {
501     if (alignment.isNucleotide() || conservation == null)
502     {
503       return;
504     }
505
506     conservationThread = new ConservationThread(ap);
507     conservationThread.start();
508   }
509
510   /**
511    * DOCUMENT ME!
512    */
513   public void updateConsensus(final AlignmentPanel ap)
514   {
515     consensusThread = new ConsensusThread(ap);
516     consensusThread.start();
517   }
518
519   class ConsensusThread extends Thread
520   {
521     AlignmentPanel ap;
522
523     public ConsensusThread(AlignmentPanel ap)
524     {
525       this.ap = ap;
526     }
527
528     public void run()
529     {
530       updatingConsensus = true;
531       while (UPDATING_CONSENSUS)
532       {
533         try
534         {
535           if (ap != null)
536           {
537             ap.paintAlignment(false);
538           }
539
540           Thread.sleep(200);
541         } catch (Exception ex)
542         {
543           ex.printStackTrace();
544         }
545       }
546
547       UPDATING_CONSENSUS = true;
548
549       try
550       {
551         int aWidth = alignment.getWidth();
552         if (aWidth < 0)
553         {
554           return;
555         }
556
557         consensus.annotations = null;
558         consensus.annotations = new Annotation[aWidth];
559
560         hconsensus = new Hashtable[aWidth];
561         AAFrequency.calculate(alignment.getSequencesArray(), 0, alignment
562                 .getWidth(), hconsensus, true); // always calculate the full profile
563         AAFrequency.completeConsensus(consensus, hconsensus, 0, aWidth,
564                 ignoreGapsInConsensusCalculation,
565                 includeAllConsensusSymbols);
566
567         if (globalColourScheme != null)
568         {
569           globalColourScheme.setConsensus(hconsensus);
570         }
571
572       } catch (OutOfMemoryError error)
573       {
574         alignment.deleteAnnotation(consensus);
575
576         consensus = null;
577         hconsensus = null;
578         System.out.println("Out of memory calculating consensus!!");
579         System.gc();
580       }
581       UPDATING_CONSENSUS = false;
582       updatingConsensus = false;
583
584       if (ap != null)
585       {
586         ap.paintAlignment(true);
587       }
588     }
589   }
590
591   /**
592    * get the consensus sequence as displayed under the PID consensus annotation
593    * row.
594    * 
595    * @return consensus sequence as a new sequence object
596    */
597   public SequenceI getConsensusSeq()
598   {
599     if (consensus == null)
600     {
601       return null;
602     }
603     StringBuffer seqs = new StringBuffer();
604     for (int i = 0; i < consensus.annotations.length; i++)
605     {
606       if (consensus.annotations[i] != null)
607       {
608         if (consensus.annotations[i].description.charAt(0) == '[')
609         {
610           seqs.append(consensus.annotations[i].description.charAt(1));
611         }
612         else
613         {
614           seqs.append(consensus.annotations[i].displayCharacter);
615         }
616       }
617     }
618     SequenceI sq = new Sequence("Consensus", seqs.toString());
619     sq.setDescription("Percentage Identity Consensus "
620             + ((ignoreGapsInConsensusCalculation) ? " without gaps" : ""));
621     return sq;
622   }
623
624   public SequenceGroup getSelectionGroup()
625   {
626     return selectionGroup;
627   }
628
629   public void setSelectionGroup(SequenceGroup sg)
630   {
631     selectionGroup = sg;
632   }
633
634   public boolean getConservationSelected()
635   {
636     return conservationColourSelected;
637   }
638
639   public void setConservationSelected(boolean b)
640   {
641     conservationColourSelected = b;
642   }
643
644   public boolean getAbovePIDThreshold()
645   {
646     return abovePIDThreshold;
647   }
648
649   public void setAbovePIDThreshold(boolean b)
650   {
651     abovePIDThreshold = b;
652   }
653
654   public int getStartRes()
655   {
656     return startRes;
657   }
658
659   public int getEndRes()
660   {
661     return endRes;
662   }
663
664   public int getStartSeq()
665   {
666     return startSeq;
667   }
668
669   public void setGlobalColourScheme(ColourSchemeI cs)
670   {
671     globalColourScheme = cs;
672   }
673
674   public ColourSchemeI getGlobalColourScheme()
675   {
676     return globalColourScheme;
677   }
678
679   public void setStartRes(int res)
680   {
681     this.startRes = res;
682   }
683
684   public void setStartSeq(int seq)
685   {
686     this.startSeq = seq;
687   }
688
689   public void setEndRes(int res)
690   {
691     if (res > alignment.getWidth() - 1)
692     {
693       // log.System.out.println(" Corrected res from " + res + " to maximum " +
694       // (alignment.getWidth()-1));
695       res = alignment.getWidth() - 1;
696     }
697     if (res < 0)
698     {
699       res = 0;
700     }
701     this.endRes = res;
702   }
703
704   public void setEndSeq(int seq)
705   {
706     if (seq > alignment.getHeight())
707     {
708       seq = alignment.getHeight();
709     }
710     if (seq < 0)
711     {
712       seq = 0;
713     }
714     this.endSeq = seq;
715   }
716
717   public int getEndSeq()
718   {
719     return endSeq;
720   }
721
722   java.awt.Frame nullFrame;
723
724   protected FeatureSettings featureSettings = null;
725
726   private float heightScale = 1, widthScale = 1;
727
728   public void setFont(Font f)
729   {
730     font = f;
731     if (nullFrame == null)
732     {
733       nullFrame = new java.awt.Frame();
734       nullFrame.addNotify();
735     }
736
737     java.awt.FontMetrics fm = nullFrame.getGraphics().getFontMetrics(font);
738     setCharHeight((int) (heightScale * fm.getHeight()));
739     charWidth = (int) (widthScale * fm.charWidth('M'));
740
741     if (upperCasebold)
742     {
743       Font f2 = new Font(f.getName(), Font.BOLD, f.getSize());
744       fm = nullFrame.getGraphics().getFontMetrics(f2);
745       charWidth = (int) (widthScale * (fm.stringWidth("MMMMMMMMMMM") / 10));
746     }
747   }
748
749   public Font getFont()
750   {
751     return font;
752   }
753
754   public int getCharWidth()
755   {
756     return charWidth;
757   }
758
759   public void setCharHeight(int h)
760   {
761     this.charHeight = h;
762   }
763
764   public int getCharHeight()
765   {
766     return charHeight;
767   }
768
769   public void setWrappedWidth(int w)
770   {
771     this.wrappedWidth = w;
772   }
773
774   public int getwrappedWidth()
775   {
776     return wrappedWidth;
777   }
778
779   public AlignmentI getAlignment()
780   {
781     return alignment;
782   }
783
784   public void setAlignment(AlignmentI align)
785   {
786     this.alignment = align;
787   }
788
789   public void setWrapAlignment(boolean state)
790   {
791     wrapAlignment = state;
792   }
793
794   public void setShowText(boolean state)
795   {
796     showText = state;
797   }
798
799   public void setRenderGaps(boolean state)
800   {
801     renderGaps = state;
802   }
803
804   public boolean getColourText()
805   {
806     return showColourText;
807   }
808
809   public void setColourText(boolean state)
810   {
811     showColourText = state;
812   }
813
814   public void setShowBoxes(boolean state)
815   {
816     showBoxes = state;
817   }
818
819   public boolean getWrapAlignment()
820   {
821     return wrapAlignment;
822   }
823
824   public boolean getShowText()
825   {
826     return showText;
827   }
828
829   public boolean getShowBoxes()
830   {
831     return showBoxes;
832   }
833
834   public char getGapCharacter()
835   {
836     return getAlignment().getGapCharacter();
837   }
838
839   public void setGapCharacter(char gap)
840   {
841     if (getAlignment() != null)
842     {
843       getAlignment().setGapCharacter(gap);
844     }
845   }
846
847   public void setThreshold(int thresh)
848   {
849     threshold = thresh;
850   }
851
852   public int getThreshold()
853   {
854     return threshold;
855   }
856
857   public void setIncrement(int inc)
858   {
859     increment = inc;
860   }
861
862   public int getIncrement()
863   {
864     return increment;
865   }
866
867   public void setHiddenColumns(ColumnSelection colsel)
868   {
869     this.colSel = colsel;
870     if (colSel.getHiddenColumns() != null)
871     {
872       hasHiddenColumns = true;
873     }
874   }
875
876   public ColumnSelection getColumnSelection()
877   {
878     return colSel;
879   }
880
881   public void resetSeqLimits(int height)
882   {
883     setEndSeq(height / getCharHeight());
884   }
885
886   public void setCurrentTree(NJTree tree)
887   {
888     currentTree = tree;
889   }
890
891   public NJTree getCurrentTree()
892   {
893     return currentTree;
894   }
895
896   public void setColourAppliesToAllGroups(boolean b)
897   {
898     colourAppliesToAllGroups = b;
899   }
900
901   public boolean getColourAppliesToAllGroups()
902   {
903     return colourAppliesToAllGroups;
904   }
905
906   public boolean getShowJVSuffix()
907   {
908     return showJVSuffix;
909   }
910
911   public void setShowJVSuffix(boolean b)
912   {
913     showJVSuffix = b;
914   }
915
916   public boolean getShowAnnotation()
917   {
918     return showAnnotation;
919   }
920
921   public void setShowAnnotation(boolean b)
922   {
923     showAnnotation = b;
924   }
925
926   public boolean getScaleAboveWrapped()
927   {
928     return scaleAboveWrapped;
929   }
930
931   public boolean getScaleLeftWrapped()
932   {
933     return scaleLeftWrapped;
934   }
935
936   public boolean getScaleRightWrapped()
937   {
938     return scaleRightWrapped;
939   }
940
941   public void setScaleAboveWrapped(boolean b)
942   {
943     scaleAboveWrapped = b;
944   }
945
946   public void setScaleLeftWrapped(boolean b)
947   {
948     scaleLeftWrapped = b;
949   }
950
951   public void setScaleRightWrapped(boolean b)
952   {
953     scaleRightWrapped = b;
954   }
955
956   public void setIgnoreGapsConsensus(boolean b)
957   {
958     ignoreGapsInConsensusCalculation = b;
959     updateConsensus(null);
960     if (globalColourScheme != null)
961     {
962       globalColourScheme.setThreshold(globalColourScheme.getThreshold(),
963               ignoreGapsInConsensusCalculation);
964
965     }
966   }
967
968   /**
969    * Property change listener for changes in alignment
970    * 
971    * @param listener
972    *          DOCUMENT ME!
973    */
974   public void addPropertyChangeListener(
975           java.beans.PropertyChangeListener listener)
976   {
977     changeSupport.addPropertyChangeListener(listener);
978   }
979
980   /**
981    * DOCUMENT ME!
982    * 
983    * @param listener
984    *          DOCUMENT ME!
985    */
986   public void removePropertyChangeListener(
987           java.beans.PropertyChangeListener listener)
988   {
989     changeSupport.removePropertyChangeListener(listener);
990   }
991
992   /**
993    * Property change listener for changes in alignment
994    * 
995    * @param prop
996    *          DOCUMENT ME!
997    * @param oldvalue
998    *          DOCUMENT ME!
999    * @param newvalue
1000    *          DOCUMENT ME!
1001    */
1002   public void firePropertyChange(String prop, Object oldvalue,
1003           Object newvalue)
1004   {
1005     changeSupport.firePropertyChange(prop, oldvalue, newvalue);
1006   }
1007
1008   public boolean getIgnoreGapsConsensus()
1009   {
1010     return ignoreGapsInConsensusCalculation;
1011   }
1012
1013   public void hideSelectedColumns()
1014   {
1015     if (colSel.size() < 1)
1016     {
1017       return;
1018     }
1019
1020     colSel.hideSelectedColumns();
1021     setSelectionGroup(null);
1022
1023     hasHiddenColumns = true;
1024   }
1025
1026   public void invertColumnSelection()
1027   {
1028     for (int i = 0; i < alignment.getWidth(); i++)
1029     {
1030       if (colSel.contains(i))
1031       {
1032         colSel.removeElement(i);
1033       }
1034       else
1035       {
1036         if (!hasHiddenColumns || colSel.isVisible(i))
1037         {
1038           colSel.addElement(i);
1039         }
1040       }
1041     }
1042   }
1043
1044   public void hideColumns(int start, int end)
1045   {
1046     if (start == end)
1047     {
1048       colSel.hideColumns(start);
1049     }
1050     else
1051     {
1052       colSel.hideColumns(start, end);
1053     }
1054
1055     hasHiddenColumns = true;
1056   }
1057
1058   public void hideRepSequences(SequenceI repSequence, SequenceGroup sg)
1059   {
1060     int sSize = sg.getSize();
1061     if (sSize < 2)
1062     {
1063       return;
1064     }
1065
1066     if (hiddenRepSequences == null)
1067     {
1068       hiddenRepSequences = new Hashtable();
1069     }
1070
1071     hiddenRepSequences.put(repSequence, sg);
1072
1073     // Hide all sequences except the repSequence
1074     SequenceI[] seqs = new SequenceI[sSize - 1];
1075     int index = 0;
1076     for (int i = 0; i < sSize; i++)
1077     {
1078       if (sg.getSequenceAt(i) != repSequence)
1079       {
1080         if (index == sSize - 1)
1081         {
1082           return;
1083         }
1084
1085         seqs[index++] = sg.getSequenceAt(i);
1086       }
1087     }
1088
1089     hideSequence(seqs);
1090
1091   }
1092
1093   public void hideAllSelectedSeqs()
1094   {
1095     if (selectionGroup == null || selectionGroup.getSize() < 1)
1096     {
1097       return;
1098     }
1099
1100     SequenceI[] seqs = selectionGroup.getSequencesInOrder(alignment);
1101
1102     hideSequence(seqs);
1103
1104     setSelectionGroup(null);
1105   }
1106
1107   public void hideSequence(SequenceI[] seq)
1108   {
1109     if (seq != null)
1110     {
1111       for (int i = 0; i < seq.length; i++)
1112       {
1113         alignment.getHiddenSequences().hideSequence(seq[i]);
1114       }
1115
1116       hasHiddenRows = true;
1117       firePropertyChange("alignment", null, alignment.getSequences());
1118     }
1119   }
1120
1121   public void showColumn(int col)
1122   {
1123     colSel.revealHiddenColumns(col);
1124     if (colSel.getHiddenColumns() == null)
1125     {
1126       hasHiddenColumns = false;
1127     }
1128   }
1129
1130   public void showAllHiddenColumns()
1131   {
1132     colSel.revealAllHiddenColumns();
1133     hasHiddenColumns = false;
1134   }
1135
1136   public void showAllHiddenSeqs()
1137   {
1138     if (alignment.getHiddenSequences().getSize() > 0)
1139     {
1140       if (selectionGroup == null)
1141       {
1142         selectionGroup = new SequenceGroup();
1143         selectionGroup.setEndRes(alignment.getWidth() - 1);
1144       }
1145       Vector tmp = alignment.getHiddenSequences().showAll(
1146               hiddenRepSequences);
1147       for (int t = 0; t < tmp.size(); t++)
1148       {
1149         selectionGroup.addSequence((SequenceI) tmp.elementAt(t), false);
1150       }
1151       firePropertyChange("alignment", null, alignment.getSequences());
1152       hasHiddenRows = false;
1153       hiddenRepSequences = null;
1154     }
1155   }
1156
1157   public int adjustForHiddenSeqs(int alignmentIndex)
1158   {
1159     return alignment.getHiddenSequences().adjustForHiddenSeqs(
1160             alignmentIndex);
1161   }
1162
1163   /**
1164    * This method returns the a new SequenceI [] with the selection sequence and
1165    * start and end points adjusted
1166    * 
1167    * @return String[]
1168    */
1169   public SequenceI[] getSelectionAsNewSequence()
1170   {
1171     SequenceI[] sequences;
1172
1173     if (selectionGroup == null)
1174     {
1175       sequences = alignment.getSequencesArray();
1176     }
1177     else
1178     {
1179       sequences = selectionGroup.getSelectionAsNewSequences(alignment);
1180     }
1181
1182     return sequences;
1183   }
1184
1185   /**
1186    * get the currently selected sequence objects or all the sequences in the
1187    * alignment.
1188    * 
1189    * @return array of references to sequence objects
1190    */
1191   public SequenceI[] getSequenceSelection()
1192   {
1193     SequenceI[] sequences = null;
1194     if (selectionGroup != null)
1195     {
1196       sequences = selectionGroup.getSequencesInOrder(alignment);
1197     }
1198     if (sequences == null)
1199     {
1200       sequences = alignment.getSequencesArray();
1201     }
1202     return sequences;
1203   }
1204
1205   /**
1206    * This method returns the visible alignment as text, as seen on the GUI, ie
1207    * if columns are hidden they will not be returned in the result. Use this for
1208    * calculating trees, PCA, redundancy etc on views which contain hidden
1209    * columns.
1210    * 
1211    * @return String[]
1212    */
1213   public jalview.datamodel.CigarArray getViewAsCigars(
1214           boolean selectedRegionOnly)
1215   {
1216     CigarArray selection = null;
1217     SequenceI[] seqs = null;
1218     int i, iSize;
1219     int start = 0, end = 0;
1220     if (selectedRegionOnly && selectionGroup != null)
1221     {
1222       iSize = selectionGroup.getSize();
1223       seqs = selectionGroup.getSequencesInOrder(alignment);
1224       start = selectionGroup.getStartRes();
1225       end = selectionGroup.getEndRes(); // inclusive for start and end in
1226       // SeqCigar constructor
1227     }
1228     else
1229     {
1230       iSize = alignment.getHeight();
1231       seqs = alignment.getSequencesArray();
1232       end = alignment.getWidth() - 1;
1233     }
1234     SeqCigar[] selseqs = new SeqCigar[iSize];
1235     for (i = 0; i < iSize; i++)
1236     {
1237       selseqs[i] = new SeqCigar(seqs[i], start, end);
1238     }
1239     selection = new CigarArray(selseqs);
1240     // now construct the CigarArray operations
1241     if (hasHiddenColumns)
1242     {
1243       Vector regions = colSel.getHiddenColumns();
1244       int[] region;
1245       int hideStart, hideEnd;
1246       int last = start;
1247       for (int j = 0; last < end & j < regions.size(); j++)
1248       {
1249         region = (int[]) regions.elementAt(j);
1250         hideStart = region[0];
1251         hideEnd = region[1];
1252         // edit hidden regions to selection range
1253         if (hideStart < last)
1254         {
1255           if (hideEnd > last)
1256           {
1257             hideStart = last;
1258           }
1259           else
1260           {
1261             continue;
1262           }
1263         }
1264
1265         if (hideStart > end)
1266         {
1267           break;
1268         }
1269
1270         if (hideEnd > end)
1271         {
1272           hideEnd = end;
1273         }
1274
1275         if (hideStart > hideEnd)
1276         {
1277           break;
1278         }
1279         /**
1280          * form operations...
1281          */
1282         if (last < hideStart)
1283         {
1284           selection.addOperation(CigarArray.M, hideStart - last);
1285         }
1286         selection.addOperation(CigarArray.D, 1 + hideEnd - hideStart);
1287         last = hideEnd + 1;
1288       }
1289       // Final match if necessary.
1290       if (last < end)
1291       {
1292         selection.addOperation(CigarArray.M, end - last + 1);
1293       }
1294     }
1295     else
1296     {
1297       selection.addOperation(CigarArray.M, end - start + 1);
1298     }
1299     return selection;
1300   }
1301
1302   /**
1303    * return a compact representation of the current alignment selection to pass
1304    * to an analysis function
1305    * 
1306    * @param selectedOnly
1307    *          boolean true to just return the selected view
1308    * @return AlignmentView
1309    */
1310   jalview.datamodel.AlignmentView getAlignmentView(boolean selectedOnly)
1311   {
1312     // JBPNote:
1313     // this is here because the AlignmentView constructor modifies the
1314     // CigarArray
1315     // object. Refactoring of Cigar and alignment view representation should
1316     // be done to remove redundancy.
1317     CigarArray aligview = getViewAsCigars(selectedOnly);
1318     if (aligview != null)
1319     {
1320       return new AlignmentView(aligview,
1321               (selectedOnly && selectionGroup != null) ? selectionGroup
1322                       .getStartRes() : 0);
1323     }
1324     return null;
1325   }
1326
1327   /**
1328    * This method returns the visible alignment as text, as seen on the GUI, ie
1329    * if columns are hidden they will not be returned in the result. Use this for
1330    * calculating trees, PCA, redundancy etc on views which contain hidden
1331    * columns.
1332    * 
1333    * @return String[]
1334    */
1335   public String[] getViewAsString(boolean selectedRegionOnly)
1336   {
1337     String[] selection = null;
1338     SequenceI[] seqs = null;
1339     int i, iSize;
1340     int start = 0, end = 0;
1341     if (selectedRegionOnly && selectionGroup != null)
1342     {
1343       iSize = selectionGroup.getSize();
1344       seqs = selectionGroup.getSequencesInOrder(alignment);
1345       start = selectionGroup.getStartRes();
1346       end = selectionGroup.getEndRes() + 1;
1347     }
1348     else
1349     {
1350       iSize = alignment.getHeight();
1351       seqs = alignment.getSequencesArray();
1352       end = alignment.getWidth();
1353     }
1354
1355     selection = new String[iSize];
1356
1357     for (i = 0; i < iSize; i++)
1358     {
1359       if (hasHiddenColumns)
1360       {
1361         StringBuffer visibleSeq = new StringBuffer();
1362         Vector regions = colSel.getHiddenColumns();
1363
1364         int blockStart = start, blockEnd = end;
1365         int[] region;
1366         int hideStart, hideEnd;
1367
1368         for (int j = 0; j < regions.size(); j++)
1369         {
1370           region = (int[]) regions.elementAt(j);
1371           hideStart = region[0];
1372           hideEnd = region[1];
1373
1374           if (hideStart < start)
1375           {
1376             continue;
1377           }
1378
1379           blockStart = Math.min(blockStart, hideEnd + 1);
1380           blockEnd = Math.min(blockEnd, hideStart);
1381
1382           if (blockStart > blockEnd)
1383           {
1384             break;
1385           }
1386
1387           visibleSeq.append(seqs[i].getSequence(blockStart, blockEnd));
1388
1389           blockStart = hideEnd + 1;
1390           blockEnd = end;
1391         }
1392
1393         if (end > blockStart)
1394         {
1395           visibleSeq.append(seqs[i].getSequence(blockStart, end));
1396         }
1397
1398         selection[i] = visibleSeq.toString();
1399       }
1400       else
1401       {
1402         selection[i] = seqs[i].getSequenceAsString(start, end);
1403       }
1404     }
1405
1406     return selection;
1407   }
1408
1409   public boolean getShowHiddenMarkers()
1410   {
1411     return showHiddenMarkers;
1412   }
1413
1414   public void setShowHiddenMarkers(boolean show)
1415   {
1416     showHiddenMarkers = show;
1417   }
1418
1419   public Color getSequenceColour(SequenceI seq)
1420   {
1421     if (sequenceColours == null || !sequenceColours.containsKey(seq))
1422     {
1423       return Color.white;
1424     }
1425     else
1426     {
1427       return (Color) sequenceColours.get(seq);
1428     }
1429   }
1430
1431   public void setSequenceColour(SequenceI seq, Color col)
1432   {
1433     if (sequenceColours == null)
1434     {
1435       sequenceColours = new Hashtable();
1436     }
1437
1438     if (col == null)
1439     {
1440       sequenceColours.remove(seq);
1441     }
1442     else
1443     {
1444       sequenceColours.put(seq, col);
1445     }
1446   }
1447
1448   public String getSequenceSetId()
1449   {
1450     if (sequenceSetID == null)
1451     {
1452       sequenceSetID = alignment.hashCode() + "";
1453     }
1454
1455     return sequenceSetID;
1456   }
1457
1458   public void alignmentChanged(AlignmentPanel ap)
1459   {
1460     alignment.padGaps();
1461
1462     if (hconsensus != null && autocalculateConsensus)
1463     {
1464       updateConsensus(ap);
1465       updateConservation(ap);
1466     }
1467
1468     // Reset endRes of groups if beyond alignment width
1469     int alWidth = alignment.getWidth();
1470     Vector groups = alignment.getGroups();
1471     if (groups != null)
1472     {
1473       for (int i = 0; i < groups.size(); i++)
1474       {
1475         SequenceGroup sg = (SequenceGroup) groups.elementAt(i);
1476         if (sg.getEndRes() > alWidth)
1477         {
1478           sg.setEndRes(alWidth - 1);
1479         }
1480       }
1481     }
1482
1483     if (selectionGroup != null && selectionGroup.getEndRes() > alWidth)
1484     {
1485       selectionGroup.setEndRes(alWidth - 1);
1486     }
1487
1488     resetAllColourSchemes();
1489
1490     // AW alignment.adjustSequenceAnnotations();
1491   }
1492
1493   void resetAllColourSchemes()
1494   {
1495     ColourSchemeI cs = globalColourScheme;
1496     if (cs != null)
1497     {
1498       if (cs instanceof ClustalxColourScheme)
1499       {
1500         ((ClustalxColourScheme) cs).resetClustalX(alignment.getSequences(),
1501                 alignment.getWidth());
1502       }
1503
1504       cs.setConsensus(hconsensus);
1505       if (cs.conservationApplied())
1506       {
1507         Alignment al = (Alignment) alignment;
1508         Conservation c = new Conservation("All",
1509                 ResidueProperties.propHash, 3, al.getSequences(), 0, al
1510                         .getWidth() - 1);
1511         c.calculate();
1512         c.verdict(false, ConsPercGaps);
1513
1514         cs.setConservation(c);
1515       }
1516     }
1517
1518     int s, sSize = alignment.getGroups().size();
1519     for (s = 0; s < sSize; s++)
1520     {
1521       SequenceGroup sg = (SequenceGroup) alignment.getGroups().elementAt(s);
1522       if (sg.cs != null && sg.cs instanceof ClustalxColourScheme)
1523       {
1524         ((ClustalxColourScheme) sg.cs).resetClustalX(sg
1525                 .getSequences(hiddenRepSequences), sg.getWidth());
1526       }
1527       sg.recalcConservation();
1528     }
1529   }
1530
1531   boolean centreColumnLabels;
1532
1533   public boolean getCentreColumnLabels()
1534   {
1535     return centreColumnLabels;
1536   }
1537
1538   public void updateSequenceIdColours()
1539   {
1540     Vector groups = alignment.getGroups();
1541     for (int ig = 0, igSize = groups.size(); ig < igSize; ig++)
1542     {
1543       SequenceGroup sg = (SequenceGroup) groups.elementAt(ig);
1544       if (sg.idColour != null)
1545       {
1546         Vector sqs = sg.getSequences(hiddenRepSequences);
1547         for (int s = 0, sSize = sqs.size(); s < sSize; s++)
1548         {
1549           this.setSequenceColour((SequenceI) sqs.elementAt(s), sg.idColour);
1550         }
1551       }
1552     }
1553   }
1554
1555   public boolean followHighlight = false;
1556
1557   public boolean getFollowHighlight()
1558   {
1559     return followHighlight;
1560   }
1561
1562   /**
1563    * show non-conserved residues only
1564    */
1565   public boolean showUnconserved = false;
1566
1567   /**
1568    * when set, alignment should be reordered according to a newly opened tree
1569    */
1570   public boolean sortByTree = false;
1571
1572   /**
1573    * @return the showUnconserved
1574    */
1575   public boolean getShowunconserved()
1576   {
1577     return showUnconserved;
1578   }
1579
1580   /**
1581    * @param showNonconserved
1582    *          the showUnconserved to set
1583    */
1584   public void setShowunconserved(boolean displayNonconserved)
1585   {
1586     this.showUnconserved = displayNonconserved;
1587   }
1588
1589   /**
1590    * consensus annotation includes all percentage for all symbols in column
1591    * DISABLED FOR 2.5 RELEASE (bug #60064 logo rendering is not AWT 1.1
1592    * compatible)
1593    */
1594   private boolean includeAllConsensusSymbols = false;
1595
1596   /**
1597    * should conservation rows be shown for groups DISABLED FOR 2.5 RELEASE (bug
1598    * 62446)
1599    */
1600   boolean showGroupConservation = false;
1601
1602   /**
1603    * should consensus rows be shown for groups DISABLED FOR 2.5 RELEASE (bug
1604    * 62446)
1605    */
1606   boolean showGroupConsensus = false;
1607
1608   /**
1609    * should consensus profile be rendered by default DISABLED FOR 2.5 RELEASE
1610    * (bug #60064 logo rendering is not AWT 1.1 compatible)
1611    */
1612   public boolean showSequenceLogo = false;
1613
1614   /**
1615    * should consensus histograms be rendered by default
1616    */
1617   public boolean showConsensusHistogram = true;
1618
1619   /**
1620    * @return the showConsensusProfile
1621    */
1622   public boolean isShowSequenceLogo()
1623   {
1624     return showSequenceLogo;
1625   }
1626
1627   /**
1628    * @param showSequenceLogo
1629    *          the new value public void setShowSequenceLogo(boolean
1630    *          showSequenceLogo) { this.showSequenceLogo = showSequenceLogo; }
1631    */
1632   /**
1633    * @param showGroupConsensus
1634    *          the showGroupConsensus to set
1635    */
1636   public void setShowGroupConsensus(boolean showGroupConsensus)
1637   {
1638     this.showGroupConsensus = showGroupConsensus;
1639   }
1640
1641   /**
1642    * @return the includeAllConsensusSymbols
1643    */
1644   public boolean isIncludeAllConsensusSymbols()
1645   {
1646     return false;
1647   }
1648
1649   /**
1650    * 
1651    * @return flag to indicate if the consensus histogram should be rendered by
1652    *         default
1653    */
1654   public boolean isShowConsensusHistogram()
1655   {
1656     return this.showConsensusHistogram;
1657   }
1658
1659   /**
1660    * synthesize a column selection if none exists so it covers the given
1661    * selection group. if wholewidth is false, no column selection is made if the
1662    * selection group covers the whole alignment width.
1663    * 
1664    * @param sg
1665    * @param wholewidth
1666    */
1667   public void expandColSelection(SequenceGroup sg, boolean wholewidth)
1668   {
1669     int sgs, sge;
1670     if (sg != null
1671             && (sgs = sg.getStartRes()) >= 0
1672             && sg.getStartRes() <= (sge = sg.getEndRes())
1673             && (colSel == null || colSel.getSelected() == null || colSel
1674                     .getSelected().size() == 0))
1675     {
1676       if (!wholewidth && alignment.getWidth() == (1 + sge - sgs))
1677       {
1678         // do nothing
1679         return;
1680       }
1681       if (colSel == null)
1682       {
1683         colSel = new ColumnSelection();
1684       }
1685       for (int cspos = sg.getStartRes(); cspos <= sg.getEndRes(); cspos++)
1686       {
1687         colSel.addElement(cspos);
1688       }
1689     }
1690   }
1691
1692 }