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