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