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