JAL-902 structure conservation row for applet
[jalview.git] / src / jalview / appletgui / AlignViewport.java
1 /*
2  * Jalview - A Sequence Alignment Editor and Viewer (Version 2.7)
3  * Copyright (C) 2011 J Procter, AM Waterhouse, J Engelhardt, LM Lui, 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.api.AlignCalcManagerI;
26 import jalview.api.AlignViewportI;
27 import jalview.bin.*;
28 import jalview.datamodel.*;
29 import jalview.schemes.*;
30 import jalview.structure.SelectionSource;
31 import jalview.structure.VamsasSource;
32 import jalview.viewmodel.AlignmentViewport;
33 import jalview.workers.ConservationThread;
34 import jalview.workers.ConsensusThread;
35
36 public class AlignViewport extends AlignmentViewport implements AlignViewportI, SelectionSource, VamsasSource 
37 {
38   int startRes;
39
40   int endRes;
41
42   int startSeq;
43
44   int endSeq;
45
46   boolean cursorMode = false;
47
48   boolean showJVSuffix = true;
49
50   boolean showText = true;
51
52   boolean showColourText = false;
53
54   boolean showBoxes = true;
55
56   boolean wrapAlignment = false;
57
58   boolean renderGaps = true;
59
60   boolean showSequenceFeatures = false;
61
62   boolean showAnnotation = true;
63
64   boolean showConservation = true;
65
66   boolean showQuality = true;
67
68   boolean showConsensus = true;
69
70   boolean upperCasebold = false;
71
72   boolean colourAppliesToAllGroups = true;
73
74   boolean conservationColourSelected = false;
75
76   boolean abovePIDThreshold = false;
77
78   int charHeight;
79
80   int charWidth;
81
82   int wrappedWidth;
83
84   Font font = new Font("SansSerif", Font.PLAIN, 10);
85
86   boolean validCharWidth = true;
87
88   int threshold;
89
90   int increment;
91
92   NJTree currentTree = null;
93
94   boolean scaleAboveWrapped = true;
95
96   boolean scaleLeftWrapped = true;
97
98   boolean scaleRightWrapped = true;
99
100   // The following vector holds the features which are
101   // currently visible, in the correct order or rendering
102   public Hashtable featuresDisplayed;
103
104
105   boolean showHiddenMarkers = true;
106
107   public jalview.bin.JalviewLite applet;
108
109   Hashtable sequenceColours;
110
111   boolean MAC = false;
112
113   Stack historyList = new Stack();
114
115   Stack redoList = new Stack();
116     
117   public void finalize() {
118     applet=null;
119     quality=null;
120     alignment=null;
121     colSel=null;
122   }
123
124   public AlignViewport(AlignmentI al, JalviewLite applet)
125   {
126     calculator = new jalview.workers.AlignCalcManager();
127     this.applet = applet;
128     setAlignment(al);
129     // we always pad gaps
130     this.setPadGaps(true);
131     this.startRes = 0;
132     this.endRes = al.getWidth() - 1;
133     this.startSeq = 0;
134     this.endSeq = al.getHeight() - 1;
135     if (applet != null)
136     {
137       // get the width and height scaling factors if they were specified
138       String param = applet.getParameter("widthScale");
139       if (param != null)
140       {
141         try
142         {
143           widthScale = new Float(param).floatValue();
144         } catch (Exception e)
145         {
146         }
147         if (widthScale <= 1.0)
148         {
149           System.err
150                   .println("Invalid alignment character width scaling factor ("
151                           + widthScale + "). Ignoring.");
152           widthScale = 1;
153         }
154         if (applet.debug)
155         {
156           System.err
157                   .println("Alignment character width scaling factor is now "
158                           + widthScale);
159         }
160       }
161       param = applet.getParameter("heightScale");
162       if (param != null)
163       {
164         try
165         {
166           heightScale = new Float(param).floatValue();
167         } catch (Exception e)
168         {
169         }
170         if (heightScale <= 1.0)
171         {
172           System.err
173                   .println("Invalid alignment character height scaling factor ("
174                           + heightScale + "). Ignoring.");
175           heightScale = 1;
176         }
177         if (applet.debug)
178         {
179           System.err
180                   .println("Alignment character height scaling factor is now "
181                           + heightScale);
182         }
183       }
184     }
185     setFont(font);
186
187     MAC = new jalview.util.Platform().isAMac();
188
189     if (applet != null)
190     {
191       showJVSuffix = applet.getDefaultParameter("showFullId", showJVSuffix);
192
193       showAnnotation = applet.getDefaultParameter("showAnnotation", showAnnotation);
194       
195       showConservation = applet.getDefaultParameter("showConservation", showConservation);
196       
197       showQuality = applet.getDefaultParameter("showQuality", showQuality);
198
199       showConsensus = applet.getDefaultParameter("showConsensus", showConsensus);
200
201       showUnconserved = applet.getDefaultParameter("showUnconserved", showUnconserved);
202
203       String param = applet.getParameter("upperCase");
204       if (param != null)
205       {
206         if (param.equalsIgnoreCase("bold"))
207         {
208           upperCasebold = true;
209         }
210       }
211       sortByTree = applet.getDefaultParameter("sortByTree", sortByTree);
212
213       followHighlight = applet.getDefaultParameter("automaticScrolling",followHighlight);
214       followSelection = followHighlight;
215
216       showSequenceLogo = applet.getDefaultParameter("showSequenceLogo", showSequenceLogo);
217
218       normaliseSequenceLogo = applet.getDefaultParameter("normaliseSequenceLogo", normaliseSequenceLogo);
219
220       showGroupConsensus = applet.getDefaultParameter("showGroupConsensus", showGroupConsensus);
221       
222       showGroupConservation = applet.getDefaultParameter("showGroupConservation", showGroupConservation);
223         
224       showConsensusHistogram = applet.getDefaultParameter("showConsensusHistogram", showConsensusHistogram);
225       
226     }
227
228     if (applet != null)
229     {
230       String colour = applet.getParameter("defaultColour");
231
232       if (colour == null)
233       {
234         colour = applet.getParameter("userDefinedColour");
235         if (colour != null)
236         {
237           colour = "User Defined";
238         }
239       }
240
241       if (colour != null)
242       {
243         globalColourScheme = ColourSchemeProperty.getColour(alignment,
244                 colour);
245         if (globalColourScheme != null)
246         {
247           globalColourScheme.setConsensus(hconsensus);
248         }
249       }
250
251       if (applet.getParameter("userDefinedColour") != null)
252       {
253         ((UserColourScheme) globalColourScheme).parseAppletParameter(applet
254                 .getParameter("userDefinedColour"));
255       }
256     }
257     if (hconsensus == null)
258     {
259       if (!alignment.isNucleotide())
260       {
261         conservation = new AlignmentAnnotation("Conservation",
262                 "Conservation of total alignment less than " + getConsPercGaps()
263                         + "% gaps", new Annotation[1], 0f, 11f,
264                 AlignmentAnnotation.BAR_GRAPH);
265         conservation.hasText = true;
266         conservation.autoCalculated = true;
267
268         if (showConservation)
269         {
270           alignment.addAnnotation(conservation);
271         }
272
273         if (showQuality)
274         {
275           quality = new AlignmentAnnotation("Quality",
276                   "Alignment Quality based on Blosum62 scores",
277                   new Annotation[1], 0f, 11f, AlignmentAnnotation.BAR_GRAPH);
278           quality.hasText = true;
279           quality.autoCalculated = true;
280
281           alignment.addAnnotation(quality);
282         }
283       } else {
284         if (alignment.hasRNAStructure())
285         {
286           strucConsensus = new AlignmentAnnotation("StrucConsensus", "PID",
287                   new Annotation[1], 0f, 100f, AlignmentAnnotation.BAR_GRAPH);
288           strucConsensus.hasText = true;
289           strucConsensus.autoCalculated = true;
290         }
291       }
292
293       consensus = new AlignmentAnnotation("Consensus", "PID",
294               new Annotation[1], 0f, 100f, AlignmentAnnotation.BAR_GRAPH);
295       consensus.hasText = true;
296       consensus.autoCalculated = true;
297
298       if (showConsensus)
299       {
300         alignment.addAnnotation(consensus);
301         if (strucConsensus!=null)
302         {
303           alignment.addAnnotation(strucConsensus);
304         }
305       }
306     }
307
308   }
309
310   public void showSequenceFeatures(boolean b)
311   {
312     showSequenceFeatures = b;
313   }
314
315   public boolean getShowSequenceFeatures()
316   {
317     return showSequenceFeatures;
318   }
319
320
321   /**
322    * get the consensus sequence as displayed under the PID consensus annotation
323    * row.
324    * 
325    * @return consensus sequence as a new sequence object
326    */
327   public SequenceI getConsensusSeq()
328   {
329     if (consensus == null)
330     {
331       updateConsensus(null);
332     }
333     if (consensus == null)
334     {
335       return null;
336     }
337     StringBuffer seqs = new StringBuffer();
338     for (int i = 0; i < consensus.annotations.length; i++)
339     {
340       if (consensus.annotations[i] != null)
341       {
342         if (consensus.annotations[i].description.charAt(0) == '[')
343         {
344           seqs.append(consensus.annotations[i].description.charAt(1));
345         }
346         else
347         {
348           seqs.append(consensus.annotations[i].displayCharacter);
349         }
350       }
351     }
352     SequenceI sq = new Sequence("Consensus", seqs.toString());
353     sq.setDescription("Percentage Identity Consensus "
354             + ((ignoreGapsInConsensusCalculation) ? " without gaps" : ""));
355     return sq;
356   }
357
358   public boolean getConservationSelected()
359   {
360     return conservationColourSelected;
361   }
362
363   public void setConservationSelected(boolean b)
364   {
365     conservationColourSelected = b;
366   }
367
368   public boolean getAbovePIDThreshold()
369   {
370     return abovePIDThreshold;
371   }
372
373   public void setAbovePIDThreshold(boolean b)
374   {
375     abovePIDThreshold = b;
376   }
377
378   public int getStartRes()
379   {
380     return startRes;
381   }
382
383   public int getEndRes()
384   {
385     return endRes;
386   }
387
388   public int getStartSeq()
389   {
390     return startSeq;
391   }
392
393   public void setStartRes(int res)
394   {
395     this.startRes = res;
396   }
397
398   public void setStartSeq(int seq)
399   {
400     this.startSeq = seq;
401   }
402
403   public void setEndRes(int res)
404   {
405     if (res > alignment.getWidth() - 1)
406     {
407       // log.System.out.println(" Corrected res from " + res + " to maximum " +
408       // (alignment.getWidth()-1));
409       res = alignment.getWidth() - 1;
410     }
411     if (res < 0)
412     {
413       res = 0;
414     }
415     this.endRes = res;
416   }
417
418   public void setEndSeq(int seq)
419   {
420     if (seq > alignment.getHeight())
421     {
422       seq = alignment.getHeight();
423     }
424     if (seq < 0)
425     {
426       seq = 0;
427     }
428     this.endSeq = seq;
429   }
430
431   public int getEndSeq()
432   {
433     return endSeq;
434   }
435
436   java.awt.Frame nullFrame;
437
438   protected FeatureSettings featureSettings = null;
439
440   private float heightScale = 1, widthScale = 1;
441
442   public void setFont(Font f)
443   {
444     font = f;
445     if (nullFrame == null)
446     {
447       nullFrame = new java.awt.Frame();
448       nullFrame.addNotify();
449     }
450
451     java.awt.FontMetrics fm = nullFrame.getGraphics().getFontMetrics(font);
452     setCharHeight((int) (heightScale * fm.getHeight()));
453     charWidth = (int) (widthScale * fm.charWidth('M'));
454
455     if (upperCasebold)
456     {
457       Font f2 = new Font(f.getName(), Font.BOLD, f.getSize());
458       fm = nullFrame.getGraphics().getFontMetrics(f2);
459       charWidth = (int) (widthScale * (fm.stringWidth("MMMMMMMMMMM") / 10));
460     }
461   }
462
463   public Font getFont()
464   {
465     return font;
466   }
467
468   public int getCharWidth()
469   {
470     return charWidth;
471   }
472
473   public void setCharHeight(int h)
474   {
475     this.charHeight = h;
476   }
477
478   public int getCharHeight()
479   {
480     return charHeight;
481   }
482
483   public void setWrappedWidth(int w)
484   {
485     this.wrappedWidth = w;
486   }
487
488   public int getwrappedWidth()
489   {
490     return wrappedWidth;
491   }
492
493   public AlignmentI getAlignment()
494   {
495     return alignment;
496   }
497
498   public void setAlignment(AlignmentI align)
499   {
500     this.alignment = align;
501   }
502
503   public void setWrapAlignment(boolean state)
504   {
505     wrapAlignment = state;
506   }
507
508   public void setShowText(boolean state)
509   {
510     showText = state;
511   }
512
513   public void setRenderGaps(boolean state)
514   {
515     renderGaps = state;
516   }
517
518   public boolean getColourText()
519   {
520     return showColourText;
521   }
522
523   public void setColourText(boolean state)
524   {
525     showColourText = state;
526   }
527
528   public void setShowBoxes(boolean state)
529   {
530     showBoxes = state;
531   }
532
533   public boolean getWrapAlignment()
534   {
535     return wrapAlignment;
536   }
537
538   public boolean getShowText()
539   {
540     return showText;
541   }
542
543   public boolean getShowBoxes()
544   {
545     return showBoxes;
546   }
547
548   public char getGapCharacter()
549   {
550     return getAlignment().getGapCharacter();
551   }
552
553   public void setGapCharacter(char gap)
554   {
555     if (getAlignment() != null)
556     {
557       getAlignment().setGapCharacter(gap);
558     }
559   }
560
561   public void setThreshold(int thresh)
562   {
563     threshold = thresh;
564   }
565
566   public int getThreshold()
567   {
568     return threshold;
569   }
570
571   public void setIncrement(int inc)
572   {
573     increment = inc;
574   }
575
576   public int getIncrement()
577   {
578     return increment;
579   }
580
581   public void resetSeqLimits(int height)
582   {
583     setEndSeq(height / getCharHeight());
584   }
585
586   public void setCurrentTree(NJTree tree)
587   {
588     currentTree = tree;
589   }
590
591   public NJTree getCurrentTree()
592   {
593     return currentTree;
594   }
595
596   public void setColourAppliesToAllGroups(boolean b)
597   {
598     colourAppliesToAllGroups = b;
599   }
600
601   public boolean getColourAppliesToAllGroups()
602   {
603     return colourAppliesToAllGroups;
604   }
605
606   public boolean getShowJVSuffix()
607   {
608     return showJVSuffix;
609   }
610
611   public void setShowJVSuffix(boolean b)
612   {
613     showJVSuffix = b;
614   }
615
616   public boolean getShowAnnotation()
617   {
618     return showAnnotation;
619   }
620
621   public void setShowAnnotation(boolean b)
622   {
623     showAnnotation = b;
624   }
625
626   public boolean getScaleAboveWrapped()
627   {
628     return scaleAboveWrapped;
629   }
630
631   public boolean getScaleLeftWrapped()
632   {
633     return scaleLeftWrapped;
634   }
635
636   public boolean getScaleRightWrapped()
637   {
638     return scaleRightWrapped;
639   }
640
641   public void setScaleAboveWrapped(boolean b)
642   {
643     scaleAboveWrapped = b;
644   }
645
646   public void setScaleLeftWrapped(boolean b)
647   {
648     scaleLeftWrapped = b;
649   }
650
651   public void setScaleRightWrapped(boolean b)
652   {
653     scaleRightWrapped = b;
654   }
655
656   public void setIgnoreGapsConsensus(boolean b)
657   {
658     ignoreGapsInConsensusCalculation = b;
659     updateConsensus(null);
660     if (globalColourScheme != null)
661     {
662       globalColourScheme.setThreshold(globalColourScheme.getThreshold(),
663               ignoreGapsInConsensusCalculation);
664
665     }
666   }
667
668
669  
670
671
672   public boolean getShowHiddenMarkers()
673   {
674     return showHiddenMarkers;
675   }
676
677   public void setShowHiddenMarkers(boolean show)
678   {
679     showHiddenMarkers = show;
680   }
681
682   public Color getSequenceColour(SequenceI seq)
683   {
684     if (sequenceColours == null || !sequenceColours.containsKey(seq))
685     {
686       return Color.white;
687     }
688     else
689     {
690       return (Color) sequenceColours.get(seq);
691     }
692   }
693
694   public void setSequenceColour(SequenceI seq, Color col)
695   {
696     if (sequenceColours == null)
697     {
698       sequenceColours = new Hashtable();
699     }
700
701     if (col == null)
702     {
703       sequenceColours.remove(seq);
704     }
705     else
706     {
707       sequenceColours.put(seq, col);
708     }
709   }
710
711   boolean centreColumnLabels;
712
713   public boolean getCentreColumnLabels()
714   {
715     return centreColumnLabels;
716   }
717
718   public void updateSequenceIdColours()
719   {
720     Vector groups = alignment.getGroups();
721     for (int ig = 0, igSize = groups.size(); ig < igSize; ig++)
722     {
723       SequenceGroup sg = (SequenceGroup) groups.elementAt(ig);
724       if (sg.idColour != null)
725       {
726         Vector sqs = sg.getSequences(getHiddenRepSequences());
727         for (int s = 0, sSize = sqs.size(); s < sSize; s++)
728         {
729           this.setSequenceColour((SequenceI) sqs.elementAt(s), sg.idColour);
730         }
731       }
732     }
733   }
734
735   public boolean followHighlight = true;
736
737   public boolean getFollowHighlight()
738   {
739     return followHighlight;
740   }
741
742   public boolean followSelection = true;
743
744   /**
745    * @return true if view selection should always follow the selections
746    *         broadcast by other selection sources
747    */
748   public boolean getFollowSelection()
749   {
750     return followSelection;
751   }
752   public void sendSelection()
753   {
754     jalview.structure.StructureSelectionManager
755             .getStructureSelectionManager(applet).sendSelection(
756                     new SequenceGroup(getSelectionGroup()),
757                     new ColumnSelection(getColumnSelection()), this);
758   }
759
760
761
762
763   /**
764    * synthesize a column selection if none exists so it covers the given
765    * selection group. if wholewidth is false, no column selection is made if the
766    * selection group covers the whole alignment width.
767    * 
768    * @param sg
769    * @param wholewidth
770    */
771   public void expandColSelection(SequenceGroup sg, boolean wholewidth)
772   {
773     int sgs, sge;
774     if (sg != null
775             && (sgs = sg.getStartRes()) >= 0
776             && sg.getStartRes() <= (sge = sg.getEndRes())
777             && (colSel == null || colSel.getSelected() == null || colSel
778                     .getSelected().size() == 0))
779     {
780       if (!wholewidth && alignment.getWidth() == (1 + sge - sgs))
781       {
782         // do nothing
783         return;
784       }
785       if (colSel == null)
786       {
787         colSel = new ColumnSelection();
788       }
789       for (int cspos = sg.getStartRes(); cspos <= sg.getEndRes(); cspos++)
790       {
791         colSel.addElement(cspos);
792       }
793     }
794   }
795
796   @Override
797   public boolean hasHiddenColumns()
798   {
799     return hasHiddenColumns;
800   }
801   
802   public boolean isNormaliseSequenceLogo()
803   {
804     return normaliseSequenceLogo;
805   }
806
807   public void setNormaliseSequenceLogo(boolean state)
808   {
809     normaliseSequenceLogo = state;
810   }
811
812   /**
813    * 
814    * @return true if alignment characters should be displayed 
815    */
816   public boolean isValidCharWidth()
817   {
818     return validCharWidth;
819   }
820
821 }