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