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