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