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