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