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