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