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