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