(JAL-969) refactored alignmentViewport base class to own package and pulled up most...
[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       }
284
285       consensus = new AlignmentAnnotation("Consensus", "PID",
286               new Annotation[1], 0f, 100f, AlignmentAnnotation.BAR_GRAPH);
287       consensus.hasText = true;
288       consensus.autoCalculated = true;
289
290       if (showConsensus)
291       {
292         alignment.addAnnotation(consensus);
293       }
294     }
295
296   }
297
298   public void showSequenceFeatures(boolean b)
299   {
300     showSequenceFeatures = b;
301   }
302
303   public boolean getShowSequenceFeatures()
304   {
305     return showSequenceFeatures;
306   }
307
308
309   /**
310    * get the consensus sequence as displayed under the PID consensus annotation
311    * row.
312    * 
313    * @return consensus sequence as a new sequence object
314    */
315   public SequenceI getConsensusSeq()
316   {
317     if (consensus == null)
318     {
319       updateConsensus(null);
320     }
321     if (consensus == null)
322     {
323       return null;
324     }
325     StringBuffer seqs = new StringBuffer();
326     for (int i = 0; i < consensus.annotations.length; i++)
327     {
328       if (consensus.annotations[i] != null)
329       {
330         if (consensus.annotations[i].description.charAt(0) == '[')
331         {
332           seqs.append(consensus.annotations[i].description.charAt(1));
333         }
334         else
335         {
336           seqs.append(consensus.annotations[i].displayCharacter);
337         }
338       }
339     }
340     SequenceI sq = new Sequence("Consensus", seqs.toString());
341     sq.setDescription("Percentage Identity Consensus "
342             + ((ignoreGapsInConsensusCalculation) ? " without gaps" : ""));
343     return sq;
344   }
345
346   public boolean getConservationSelected()
347   {
348     return conservationColourSelected;
349   }
350
351   public void setConservationSelected(boolean b)
352   {
353     conservationColourSelected = b;
354   }
355
356   public boolean getAbovePIDThreshold()
357   {
358     return abovePIDThreshold;
359   }
360
361   public void setAbovePIDThreshold(boolean b)
362   {
363     abovePIDThreshold = b;
364   }
365
366   public int getStartRes()
367   {
368     return startRes;
369   }
370
371   public int getEndRes()
372   {
373     return endRes;
374   }
375
376   public int getStartSeq()
377   {
378     return startSeq;
379   }
380
381   public void setStartRes(int res)
382   {
383     this.startRes = res;
384   }
385
386   public void setStartSeq(int seq)
387   {
388     this.startSeq = seq;
389   }
390
391   public void setEndRes(int res)
392   {
393     if (res > alignment.getWidth() - 1)
394     {
395       // log.System.out.println(" Corrected res from " + res + " to maximum " +
396       // (alignment.getWidth()-1));
397       res = alignment.getWidth() - 1;
398     }
399     if (res < 0)
400     {
401       res = 0;
402     }
403     this.endRes = res;
404   }
405
406   public void setEndSeq(int seq)
407   {
408     if (seq > alignment.getHeight())
409     {
410       seq = alignment.getHeight();
411     }
412     if (seq < 0)
413     {
414       seq = 0;
415     }
416     this.endSeq = seq;
417   }
418
419   public int getEndSeq()
420   {
421     return endSeq;
422   }
423
424   java.awt.Frame nullFrame;
425
426   protected FeatureSettings featureSettings = null;
427
428   private float heightScale = 1, widthScale = 1;
429
430   public void setFont(Font f)
431   {
432     font = f;
433     if (nullFrame == null)
434     {
435       nullFrame = new java.awt.Frame();
436       nullFrame.addNotify();
437     }
438
439     java.awt.FontMetrics fm = nullFrame.getGraphics().getFontMetrics(font);
440     setCharHeight((int) (heightScale * fm.getHeight()));
441     charWidth = (int) (widthScale * fm.charWidth('M'));
442
443     if (upperCasebold)
444     {
445       Font f2 = new Font(f.getName(), Font.BOLD, f.getSize());
446       fm = nullFrame.getGraphics().getFontMetrics(f2);
447       charWidth = (int) (widthScale * (fm.stringWidth("MMMMMMMMMMM") / 10));
448     }
449   }
450
451   public Font getFont()
452   {
453     return font;
454   }
455
456   public int getCharWidth()
457   {
458     return charWidth;
459   }
460
461   public void setCharHeight(int h)
462   {
463     this.charHeight = h;
464   }
465
466   public int getCharHeight()
467   {
468     return charHeight;
469   }
470
471   public void setWrappedWidth(int w)
472   {
473     this.wrappedWidth = w;
474   }
475
476   public int getwrappedWidth()
477   {
478     return wrappedWidth;
479   }
480
481   public AlignmentI getAlignment()
482   {
483     return alignment;
484   }
485
486   public void setAlignment(AlignmentI align)
487   {
488     this.alignment = align;
489   }
490
491   public void setWrapAlignment(boolean state)
492   {
493     wrapAlignment = state;
494   }
495
496   public void setShowText(boolean state)
497   {
498     showText = state;
499   }
500
501   public void setRenderGaps(boolean state)
502   {
503     renderGaps = state;
504   }
505
506   public boolean getColourText()
507   {
508     return showColourText;
509   }
510
511   public void setColourText(boolean state)
512   {
513     showColourText = state;
514   }
515
516   public void setShowBoxes(boolean state)
517   {
518     showBoxes = state;
519   }
520
521   public boolean getWrapAlignment()
522   {
523     return wrapAlignment;
524   }
525
526   public boolean getShowText()
527   {
528     return showText;
529   }
530
531   public boolean getShowBoxes()
532   {
533     return showBoxes;
534   }
535
536   public char getGapCharacter()
537   {
538     return getAlignment().getGapCharacter();
539   }
540
541   public void setGapCharacter(char gap)
542   {
543     if (getAlignment() != null)
544     {
545       getAlignment().setGapCharacter(gap);
546     }
547   }
548
549   public void setThreshold(int thresh)
550   {
551     threshold = thresh;
552   }
553
554   public int getThreshold()
555   {
556     return threshold;
557   }
558
559   public void setIncrement(int inc)
560   {
561     increment = inc;
562   }
563
564   public int getIncrement()
565   {
566     return increment;
567   }
568
569   public void resetSeqLimits(int height)
570   {
571     setEndSeq(height / getCharHeight());
572   }
573
574   public void setCurrentTree(NJTree tree)
575   {
576     currentTree = tree;
577   }
578
579   public NJTree getCurrentTree()
580   {
581     return currentTree;
582   }
583
584   public void setColourAppliesToAllGroups(boolean b)
585   {
586     colourAppliesToAllGroups = b;
587   }
588
589   public boolean getColourAppliesToAllGroups()
590   {
591     return colourAppliesToAllGroups;
592   }
593
594   public boolean getShowJVSuffix()
595   {
596     return showJVSuffix;
597   }
598
599   public void setShowJVSuffix(boolean b)
600   {
601     showJVSuffix = b;
602   }
603
604   public boolean getShowAnnotation()
605   {
606     return showAnnotation;
607   }
608
609   public void setShowAnnotation(boolean b)
610   {
611     showAnnotation = b;
612   }
613
614   public boolean getScaleAboveWrapped()
615   {
616     return scaleAboveWrapped;
617   }
618
619   public boolean getScaleLeftWrapped()
620   {
621     return scaleLeftWrapped;
622   }
623
624   public boolean getScaleRightWrapped()
625   {
626     return scaleRightWrapped;
627   }
628
629   public void setScaleAboveWrapped(boolean b)
630   {
631     scaleAboveWrapped = b;
632   }
633
634   public void setScaleLeftWrapped(boolean b)
635   {
636     scaleLeftWrapped = b;
637   }
638
639   public void setScaleRightWrapped(boolean b)
640   {
641     scaleRightWrapped = b;
642   }
643
644   public void setIgnoreGapsConsensus(boolean b)
645   {
646     ignoreGapsInConsensusCalculation = b;
647     updateConsensus(null);
648     if (globalColourScheme != null)
649     {
650       globalColourScheme.setThreshold(globalColourScheme.getThreshold(),
651               ignoreGapsInConsensusCalculation);
652
653     }
654   }
655
656
657  
658
659
660   public boolean getShowHiddenMarkers()
661   {
662     return showHiddenMarkers;
663   }
664
665   public void setShowHiddenMarkers(boolean show)
666   {
667     showHiddenMarkers = show;
668   }
669
670   public Color getSequenceColour(SequenceI seq)
671   {
672     if (sequenceColours == null || !sequenceColours.containsKey(seq))
673     {
674       return Color.white;
675     }
676     else
677     {
678       return (Color) sequenceColours.get(seq);
679     }
680   }
681
682   public void setSequenceColour(SequenceI seq, Color col)
683   {
684     if (sequenceColours == null)
685     {
686       sequenceColours = new Hashtable();
687     }
688
689     if (col == null)
690     {
691       sequenceColours.remove(seq);
692     }
693     else
694     {
695       sequenceColours.put(seq, col);
696     }
697   }
698
699   boolean centreColumnLabels;
700
701   public boolean getCentreColumnLabels()
702   {
703     return centreColumnLabels;
704   }
705
706   public void updateSequenceIdColours()
707   {
708     Vector groups = alignment.getGroups();
709     for (int ig = 0, igSize = groups.size(); ig < igSize; ig++)
710     {
711       SequenceGroup sg = (SequenceGroup) groups.elementAt(ig);
712       if (sg.idColour != null)
713       {
714         Vector sqs = sg.getSequences(getHiddenRepSequences());
715         for (int s = 0, sSize = sqs.size(); s < sSize; s++)
716         {
717           this.setSequenceColour((SequenceI) sqs.elementAt(s), sg.idColour);
718         }
719       }
720     }
721   }
722
723   public boolean followHighlight = true;
724
725   public boolean getFollowHighlight()
726   {
727     return followHighlight;
728   }
729
730   public boolean followSelection = true;
731
732   /**
733    * @return true if view selection should always follow the selections
734    *         broadcast by other selection sources
735    */
736   public boolean getFollowSelection()
737   {
738     return followSelection;
739   }
740   public void sendSelection()
741   {
742     jalview.structure.StructureSelectionManager
743             .getStructureSelectionManager(applet).sendSelection(
744                     new SequenceGroup(getSelectionGroup()),
745                     new ColumnSelection(getColumnSelection()), this);
746   }
747
748
749
750
751   /**
752    * synthesize a column selection if none exists so it covers the given
753    * selection group. if wholewidth is false, no column selection is made if the
754    * selection group covers the whole alignment width.
755    * 
756    * @param sg
757    * @param wholewidth
758    */
759   public void expandColSelection(SequenceGroup sg, boolean wholewidth)
760   {
761     int sgs, sge;
762     if (sg != null
763             && (sgs = sg.getStartRes()) >= 0
764             && sg.getStartRes() <= (sge = sg.getEndRes())
765             && (colSel == null || colSel.getSelected() == null || colSel
766                     .getSelected().size() == 0))
767     {
768       if (!wholewidth && alignment.getWidth() == (1 + sge - sgs))
769       {
770         // do nothing
771         return;
772       }
773       if (colSel == null)
774       {
775         colSel = new ColumnSelection();
776       }
777       for (int cspos = sg.getStartRes(); cspos <= sg.getEndRes(); cspos++)
778       {
779         colSel.addElement(cspos);
780       }
781     }
782   }
783
784   @Override
785   public boolean hasHiddenColumns()
786   {
787     return hasHiddenColumns;
788   }
789   
790   public boolean isNormaliseSequenceLogo()
791   {
792     return normaliseSequenceLogo;
793   }
794
795   public void setNormaliseSequenceLogo(boolean state)
796   {
797     normaliseSequenceLogo = state;
798   }
799
800   /**
801    * 
802    * @return true if alignment characters should be displayed 
803    */
804   public boolean isValidCharWidth()
805   {
806     return validCharWidth;
807   }
808
809 }