patch for JAL-699 so conservation/consensus threads restart correctly
[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     CigarArray selection = null;
1222     SequenceI[] seqs = null;
1223     int i, iSize;
1224     int start = 0, end = 0;
1225     if (selectedRegionOnly && selectionGroup != null)
1226     {
1227       iSize = selectionGroup.getSize();
1228       seqs = selectionGroup.getSequencesInOrder(alignment);
1229       start = selectionGroup.getStartRes();
1230       end = selectionGroup.getEndRes(); // inclusive for start and end in
1231       // SeqCigar constructor
1232     }
1233     else
1234     {
1235       iSize = alignment.getHeight();
1236       seqs = alignment.getSequencesArray();
1237       end = alignment.getWidth() - 1;
1238     }
1239     SeqCigar[] selseqs = new SeqCigar[iSize];
1240     for (i = 0; i < iSize; i++)
1241     {
1242       selseqs[i] = new SeqCigar(seqs[i], start, end);
1243     }
1244     selection = new CigarArray(selseqs);
1245     // now construct the CigarArray operations
1246     if (hasHiddenColumns)
1247     {
1248       Vector regions = colSel.getHiddenColumns();
1249       int[] region;
1250       int hideStart, hideEnd;
1251       int last = start;
1252       for (int j = 0; last < end & j < regions.size(); j++)
1253       {
1254         region = (int[]) regions.elementAt(j);
1255         hideStart = region[0];
1256         hideEnd = region[1];
1257         // edit hidden regions to selection range
1258         if (hideStart < last)
1259         {
1260           if (hideEnd > last)
1261           {
1262             hideStart = last;
1263           }
1264           else
1265           {
1266             continue;
1267           }
1268         }
1269
1270         if (hideStart > end)
1271         {
1272           break;
1273         }
1274
1275         if (hideEnd > end)
1276         {
1277           hideEnd = end;
1278         }
1279
1280         if (hideStart > hideEnd)
1281         {
1282           break;
1283         }
1284         /**
1285          * form operations...
1286          */
1287         if (last < hideStart)
1288         {
1289           selection.addOperation(CigarArray.M, hideStart - last);
1290         }
1291         selection.addOperation(CigarArray.D, 1 + hideEnd - hideStart);
1292         last = hideEnd + 1;
1293       }
1294       // Final match if necessary.
1295       if (last < end)
1296       {
1297         selection.addOperation(CigarArray.M, end - last + 1);
1298       }
1299     }
1300     else
1301     {
1302       selection.addOperation(CigarArray.M, end - start + 1);
1303     }
1304     return selection;
1305   }
1306
1307   /**
1308    * return a compact representation of the current alignment selection to pass
1309    * to an analysis function
1310    * 
1311    * @param selectedOnly
1312    *          boolean true to just return the selected view
1313    * @return AlignmentView
1314    */
1315   jalview.datamodel.AlignmentView getAlignmentView(boolean selectedOnly)
1316   {
1317     // JBPNote:
1318     // this is here because the AlignmentView constructor modifies the
1319     // CigarArray
1320     // object. Refactoring of Cigar and alignment view representation should
1321     // be done to remove redundancy.
1322     CigarArray aligview = getViewAsCigars(selectedOnly);
1323     if (aligview != null)
1324     {
1325       return new AlignmentView(aligview,
1326               (selectedOnly && selectionGroup != null) ? selectionGroup
1327                       .getStartRes() : 0);
1328     }
1329     return null;
1330   }
1331
1332   /**
1333    * This method returns the visible alignment as text, as seen on the GUI, ie
1334    * if columns are hidden they will not be returned in the result. Use this for
1335    * calculating trees, PCA, redundancy etc on views which contain hidden
1336    * columns.
1337    * 
1338    * @return String[]
1339    */
1340   public String[] getViewAsString(boolean selectedRegionOnly)
1341   {
1342     String[] selection = null;
1343     SequenceI[] seqs = null;
1344     int i, iSize;
1345     int start = 0, end = 0;
1346     if (selectedRegionOnly && selectionGroup != null)
1347     {
1348       iSize = selectionGroup.getSize();
1349       seqs = selectionGroup.getSequencesInOrder(alignment);
1350       start = selectionGroup.getStartRes();
1351       end = selectionGroup.getEndRes() + 1;
1352     }
1353     else
1354     {
1355       iSize = alignment.getHeight();
1356       seqs = alignment.getSequencesArray();
1357       end = alignment.getWidth();
1358     }
1359
1360     selection = new String[iSize];
1361
1362     for (i = 0; i < iSize; i++)
1363     {
1364       if (hasHiddenColumns)
1365       {
1366         StringBuffer visibleSeq = new StringBuffer();
1367         Vector regions = colSel.getHiddenColumns();
1368
1369         int blockStart = start, blockEnd = end;
1370         int[] region;
1371         int hideStart, hideEnd;
1372
1373         for (int j = 0; j < regions.size(); j++)
1374         {
1375           region = (int[]) regions.elementAt(j);
1376           hideStart = region[0];
1377           hideEnd = region[1];
1378
1379           if (hideStart < start)
1380           {
1381             continue;
1382           }
1383
1384           blockStart = Math.min(blockStart, hideEnd + 1);
1385           blockEnd = Math.min(blockEnd, hideStart);
1386
1387           if (blockStart > blockEnd)
1388           {
1389             break;
1390           }
1391
1392           visibleSeq.append(seqs[i].getSequence(blockStart, blockEnd));
1393
1394           blockStart = hideEnd + 1;
1395           blockEnd = end;
1396         }
1397
1398         if (end > blockStart)
1399         {
1400           visibleSeq.append(seqs[i].getSequence(blockStart, end));
1401         }
1402
1403         selection[i] = visibleSeq.toString();
1404       }
1405       else
1406       {
1407         selection[i] = seqs[i].getSequenceAsString(start, end);
1408       }
1409     }
1410
1411     return selection;
1412   }
1413
1414   public boolean getShowHiddenMarkers()
1415   {
1416     return showHiddenMarkers;
1417   }
1418
1419   public void setShowHiddenMarkers(boolean show)
1420   {
1421     showHiddenMarkers = show;
1422   }
1423
1424   public Color getSequenceColour(SequenceI seq)
1425   {
1426     if (sequenceColours == null || !sequenceColours.containsKey(seq))
1427     {
1428       return Color.white;
1429     }
1430     else
1431     {
1432       return (Color) sequenceColours.get(seq);
1433     }
1434   }
1435
1436   public void setSequenceColour(SequenceI seq, Color col)
1437   {
1438     if (sequenceColours == null)
1439     {
1440       sequenceColours = new Hashtable();
1441     }
1442
1443     if (col == null)
1444     {
1445       sequenceColours.remove(seq);
1446     }
1447     else
1448     {
1449       sequenceColours.put(seq, col);
1450     }
1451   }
1452
1453   public String getSequenceSetId()
1454   {
1455     if (sequenceSetID == null)
1456     {
1457       sequenceSetID = alignment.hashCode() + "";
1458     }
1459
1460     return sequenceSetID;
1461   }
1462
1463   public void alignmentChanged(AlignmentPanel ap)
1464   {
1465     alignment.padGaps();
1466
1467     if (hconsensus != null && autocalculateConsensus)
1468     {
1469       updateConsensus(ap);
1470       updateConservation(ap);
1471     }
1472
1473     // Reset endRes of groups if beyond alignment width
1474     int alWidth = alignment.getWidth();
1475     Vector groups = alignment.getGroups();
1476     if (groups != null)
1477     {
1478       for (int i = 0; i < groups.size(); i++)
1479       {
1480         SequenceGroup sg = (SequenceGroup) groups.elementAt(i);
1481         if (sg.getEndRes() > alWidth)
1482         {
1483           sg.setEndRes(alWidth - 1);
1484         }
1485       }
1486     }
1487
1488     if (selectionGroup != null && selectionGroup.getEndRes() > alWidth)
1489     {
1490       selectionGroup.setEndRes(alWidth - 1);
1491     }
1492
1493     resetAllColourSchemes();
1494
1495     // AW alignment.adjustSequenceAnnotations();
1496   }
1497
1498   void resetAllColourSchemes()
1499   {
1500     ColourSchemeI cs = globalColourScheme;
1501     if (cs != null)
1502     {
1503       if (cs instanceof ClustalxColourScheme)
1504       {
1505         ((ClustalxColourScheme) cs).resetClustalX(alignment.getSequences(),
1506                 alignment.getWidth());
1507       }
1508
1509       cs.setConsensus(hconsensus);
1510       if (cs.conservationApplied())
1511       {
1512         Alignment al = (Alignment) alignment;
1513         Conservation c = new Conservation("All",
1514                 ResidueProperties.propHash, 3, al.getSequences(), 0,
1515                 al.getWidth() - 1);
1516         c.calculate();
1517         c.verdict(false, ConsPercGaps);
1518
1519         cs.setConservation(c);
1520       }
1521     }
1522
1523     int s, sSize = alignment.getGroups().size();
1524     for (s = 0; s < sSize; s++)
1525     {
1526       SequenceGroup sg = (SequenceGroup) alignment.getGroups().elementAt(s);
1527       if (sg.cs != null && sg.cs instanceof ClustalxColourScheme)
1528       {
1529         ((ClustalxColourScheme) sg.cs).resetClustalX(
1530                 sg.getSequences(hiddenRepSequences), sg.getWidth());
1531       }
1532       sg.recalcConservation();
1533     }
1534   }
1535
1536   boolean centreColumnLabels;
1537
1538   public boolean getCentreColumnLabels()
1539   {
1540     return centreColumnLabels;
1541   }
1542
1543   public void updateSequenceIdColours()
1544   {
1545     Vector groups = alignment.getGroups();
1546     for (int ig = 0, igSize = groups.size(); ig < igSize; ig++)
1547     {
1548       SequenceGroup sg = (SequenceGroup) groups.elementAt(ig);
1549       if (sg.idColour != null)
1550       {
1551         Vector sqs = sg.getSequences(hiddenRepSequences);
1552         for (int s = 0, sSize = sqs.size(); s < sSize; s++)
1553         {
1554           this.setSequenceColour((SequenceI) sqs.elementAt(s), sg.idColour);
1555         }
1556       }
1557     }
1558   }
1559
1560   public boolean followHighlight = false;
1561
1562   public boolean getFollowHighlight()
1563   {
1564     return followHighlight;
1565   }
1566
1567   /**
1568    * show non-conserved residues only
1569    */
1570   public boolean showUnconserved = false;
1571
1572   /**
1573    * when set, alignment should be reordered according to a newly opened tree
1574    */
1575   public boolean sortByTree = false;
1576
1577   /**
1578    * @return the showUnconserved
1579    */
1580   public boolean getShowunconserved()
1581   {
1582     return showUnconserved;
1583   }
1584
1585   /**
1586    * @param showNonconserved
1587    *          the showUnconserved to set
1588    */
1589   public void setShowunconserved(boolean displayNonconserved)
1590   {
1591     this.showUnconserved = displayNonconserved;
1592   }
1593
1594   /**
1595    * consensus annotation includes all percentage for all symbols in column
1596    * DISABLED FOR 2.5 RELEASE (bug #60064 logo rendering is not AWT 1.1
1597    * compatible)
1598    */
1599   private boolean includeAllConsensusSymbols = false;
1600
1601   /**
1602    * should conservation rows be shown for groups DISABLED FOR 2.5 RELEASE (bug
1603    * 62446)
1604    */
1605   boolean showGroupConservation = false;
1606
1607   /**
1608    * should consensus rows be shown for groups DISABLED FOR 2.5 RELEASE (bug
1609    * 62446)
1610    */
1611   boolean showGroupConsensus = false;
1612
1613   /**
1614    * should consensus profile be rendered by default DISABLED FOR 2.5 RELEASE
1615    * (bug #60064 logo rendering is not AWT 1.1 compatible)
1616    */
1617   public boolean showSequenceLogo = false;
1618
1619   /**
1620    * should consensus histograms be rendered by default
1621    */
1622   public boolean showConsensusHistogram = true;
1623
1624   /**
1625    * @return the showConsensusProfile
1626    */
1627   public boolean isShowSequenceLogo()
1628   {
1629     return showSequenceLogo;
1630   }
1631
1632   /**
1633    * @param showSequenceLogo
1634    *          the new value public void setShowSequenceLogo(boolean
1635    *          showSequenceLogo) { this.showSequenceLogo = showSequenceLogo; }
1636    */
1637   /**
1638    * @param showGroupConsensus
1639    *          the showGroupConsensus to set
1640    */
1641   public void setShowGroupConsensus(boolean showGroupConsensus)
1642   {
1643     this.showGroupConsensus = showGroupConsensus;
1644   }
1645
1646   /**
1647    * @return the includeAllConsensusSymbols
1648    */
1649   public boolean isIncludeAllConsensusSymbols()
1650   {
1651     return false;
1652   }
1653
1654   /**
1655    * 
1656    * @return flag to indicate if the consensus histogram should be rendered by
1657    *         default
1658    */
1659   public boolean isShowConsensusHistogram()
1660   {
1661     return this.showConsensusHistogram;
1662   }
1663
1664   /**
1665    * synthesize a column selection if none exists so it covers the given
1666    * selection group. if wholewidth is false, no column selection is made if the
1667    * selection group covers the whole alignment width.
1668    * 
1669    * @param sg
1670    * @param wholewidth
1671    */
1672   public void expandColSelection(SequenceGroup sg, boolean wholewidth)
1673   {
1674     int sgs, sge;
1675     if (sg != null
1676             && (sgs = sg.getStartRes()) >= 0
1677             && sg.getStartRes() <= (sge = sg.getEndRes())
1678             && (colSel == null || colSel.getSelected() == null || colSel
1679                     .getSelected().size() == 0))
1680     {
1681       if (!wholewidth && alignment.getWidth() == (1 + sge - sgs))
1682       {
1683         // do nothing
1684         return;
1685       }
1686       if (colSel == null)
1687       {
1688         colSel = new ColumnSelection();
1689       }
1690       for (int cspos = sg.getStartRes(); cspos <= sg.getEndRes(); cspos++)
1691       {
1692         colSel.addElement(cspos);
1693       }
1694     }
1695   }
1696
1697 }