JAL-1641 updated applet version
[jalview.git] / src / jalview / appletgui / AlignViewport.java
1 /*
2  * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
3  * Copyright (C) $$Year-Rel$$ The Jalview Authors
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
10  * of the License, or (at your option) any later version.
11  *  
12  * Jalview is distributed in the hope that it will be useful, but 
13  * WITHOUT ANY WARRANTY; without even the implied warranty 
14  * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
15  * PURPOSE.  See the GNU General Public License for more details.
16  * 
17  * You should have received a copy of the GNU General Public License
18  * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
19  * The Jalview Authors are detailed in the 'AUTHORS' file.
20  */
21 package jalview.appletgui;
22
23 import java.awt.Font;
24
25 import jalview.analysis.NJTree;
26 import jalview.api.AlignViewportI;
27 import jalview.api.FeatureRenderer;
28 import jalview.bin.JalviewLite;
29 import jalview.commands.CommandI;
30 import jalview.datamodel.AlignmentI;
31 import jalview.datamodel.ColumnSelection;
32 import jalview.datamodel.SearchResults;
33 import jalview.datamodel.Sequence;
34 import jalview.datamodel.SequenceGroup;
35 import jalview.datamodel.SequenceI;
36 import jalview.schemes.ColourSchemeProperty;
37 import jalview.schemes.UserColourScheme;
38 import jalview.structure.CommandListener;
39 import jalview.structure.SelectionSource;
40 import jalview.structure.StructureSelectionManager;
41 import jalview.structure.VamsasSource;
42 import jalview.viewmodel.AlignmentViewport;
43
44 public class AlignViewport extends AlignmentViewport implements
45         SelectionSource, VamsasSource, CommandListener
46 {
47   boolean cursorMode = false;
48
49   Font font = new Font("SansSerif", Font.PLAIN, 10);
50
51   boolean validCharWidth = true;
52
53   NJTree currentTree = null;
54
55   public jalview.bin.JalviewLite applet;
56
57   boolean MAC = false;
58
59   private AnnotationColumnChooser annotationColumnSelectionState;
60
61   private FeatureRenderer featureRenderer;
62
63   private boolean includeHiddenRegion = true;
64
65   public void finalize()
66   {
67     applet = null;
68     quality = null;
69     alignment = null;
70     colSel = null;
71   }
72
73   public AlignViewport(AlignmentI al, JalviewLite applet)
74   {
75     super();
76     calculator = new jalview.workers.AlignCalcManager();
77     this.applet = applet;
78     alignment = al;
79     // we always pad gaps
80     this.setPadGaps(true);
81     this.startRes = 0;
82     this.endRes = al.getWidth() - 1;
83     this.startSeq = 0;
84     this.endSeq = al.getHeight() - 1;
85     if (applet != null)
86     {
87       // get the width and height scaling factors if they were specified
88       String param = applet.getParameter("widthScale");
89       if (param != null)
90       {
91         try
92         {
93           widthScale = new Float(param).floatValue();
94         } catch (Exception e)
95         {
96         }
97         if (widthScale <= 1.0)
98         {
99           System.err
100                   .println("Invalid alignment character width scaling factor ("
101                           + widthScale + "). Ignoring.");
102           widthScale = 1;
103         }
104         if (JalviewLite.debug)
105         {
106           System.err
107                   .println("Alignment character width scaling factor is now "
108                           + widthScale);
109         }
110       }
111       param = applet.getParameter("heightScale");
112       if (param != null)
113       {
114         try
115         {
116           heightScale = new Float(param).floatValue();
117         } catch (Exception e)
118         {
119         }
120         if (heightScale <= 1.0)
121         {
122           System.err
123                   .println("Invalid alignment character height scaling factor ("
124                           + heightScale + "). Ignoring.");
125           heightScale = 1;
126         }
127         if (JalviewLite.debug)
128         {
129           System.err
130                   .println("Alignment character height scaling factor is now "
131                           + heightScale);
132         }
133       }
134     }
135     setFont(font);
136
137     MAC = new jalview.util.Platform().isAMac();
138
139     if (applet != null)
140     {
141       setShowJVSuffix(applet.getDefaultParameter("showFullId",
142               getShowJVSuffix()));
143
144       setShowAnnotation(applet.getDefaultParameter("showAnnotation",
145               isShowAnnotation()));
146
147       showConservation = applet.getDefaultParameter("showConservation",
148               showConservation);
149
150       showQuality = applet.getDefaultParameter("showQuality", showQuality);
151
152       showConsensus = applet.getDefaultParameter("showConsensus",
153               showConsensus);
154
155       setShowUnconserved(applet.getDefaultParameter("showUnconserved",
156               getShowUnconserved()));
157
158       setScaleProteinAsCdna(applet.getDefaultParameter(
159               "scaleProteinAsCdna", isScaleProteinAsCdna()));
160
161       String param = applet.getParameter("upperCase");
162       if (param != null)
163       {
164         if (param.equalsIgnoreCase("bold"))
165         {
166           setUpperCasebold(true);
167         }
168       }
169       sortByTree = applet.getDefaultParameter("sortByTree", sortByTree);
170
171       setFollowHighlight(applet.getDefaultParameter("automaticScrolling",
172               isFollowHighlight()));
173       followSelection = isFollowHighlight();
174
175       showSequenceLogo = applet.getDefaultParameter("showSequenceLogo",
176               showSequenceLogo);
177
178       normaliseSequenceLogo = applet.getDefaultParameter(
179               "normaliseSequenceLogo", applet.getDefaultParameter(
180                       "normaliseLogo", normaliseSequenceLogo));
181
182       showGroupConsensus = applet.getDefaultParameter("showGroupConsensus",
183               showGroupConsensus);
184
185       showGroupConservation = applet.getDefaultParameter(
186               "showGroupConservation", showGroupConservation);
187
188       showConsensusHistogram = applet.getDefaultParameter(
189               "showConsensusHistogram", showConsensusHistogram);
190
191     }
192
193     if (applet != null)
194     {
195       String colour = applet.getParameter("defaultColour");
196
197       if (colour == null)
198       {
199         colour = applet.getParameter("userDefinedColour");
200         if (colour != null)
201         {
202           colour = "User Defined";
203         }
204       }
205
206       if (colour != null)
207       {
208         globalColourScheme = ColourSchemeProperty.getColour(alignment,
209                 colour);
210         if (globalColourScheme != null)
211         {
212           globalColourScheme.setConsensus(hconsensus);
213         }
214       }
215
216       if (applet.getParameter("userDefinedColour") != null)
217       {
218         ((UserColourScheme) globalColourScheme).parseAppletParameter(applet
219                 .getParameter("userDefinedColour"));
220       }
221     }
222     initAutoAnnotation();
223
224   }
225
226   /**
227    * get the consensus sequence as displayed under the PID consensus annotation
228    * row.
229    * 
230    * @return consensus sequence as a new sequence object
231    */
232   public SequenceI getConsensusSeq()
233   {
234     if (consensus == null)
235     {
236       updateConsensus(null);
237     }
238     if (consensus == null)
239     {
240       return null;
241     }
242     StringBuilder seqs = new StringBuilder(consensus.annotations.length);
243     for (int i = 0; i < consensus.annotations.length; i++)
244     {
245       if (consensus.annotations[i] != null)
246       {
247         if (consensus.annotations[i].description.charAt(0) == '[')
248         {
249           seqs.append(consensus.annotations[i].description.charAt(1));
250         }
251         else
252         {
253           seqs.append(consensus.annotations[i].displayCharacter);
254         }
255       }
256     }
257     SequenceI sq = new Sequence("Consensus", seqs.toString());
258     sq.setDescription("Percentage Identity Consensus "
259             + ((ignoreGapsInConsensusCalculation) ? " without gaps" : ""));
260     return sq;
261   }
262
263   java.awt.Frame nullFrame;
264
265   protected FeatureSettings featureSettings = null;
266
267   private float heightScale = 1, widthScale = 1;
268
269   public void setFont(Font f)
270   {
271     font = f;
272     if (nullFrame == null)
273     {
274       nullFrame = new java.awt.Frame();
275       nullFrame.addNotify();
276     }
277
278     java.awt.FontMetrics fm = nullFrame.getGraphics().getFontMetrics(font);
279     setCharHeight((int) (heightScale * fm.getHeight()));
280     setCharWidth((int) (widthScale * fm.charWidth('M')));
281
282     if (isUpperCasebold())
283     {
284       Font f2 = new Font(f.getName(), Font.BOLD, f.getSize());
285       fm = nullFrame.getGraphics().getFontMetrics(f2);
286       setCharWidth((int) (widthScale * (fm.stringWidth("MMMMMMMMMMM") / 10)));
287     }
288   }
289
290   public Font getFont()
291   {
292     return font;
293   }
294
295
296   public void resetSeqLimits(int height)
297   {
298     setEndSeq(height / getCharHeight());
299   }
300
301   public void setCurrentTree(NJTree tree)
302   {
303     currentTree = tree;
304   }
305
306   public NJTree getCurrentTree()
307   {
308     return currentTree;
309   }
310
311
312   boolean centreColumnLabels;
313
314   public boolean getCentreColumnLabels()
315   {
316     return centreColumnLabels;
317   }
318
319   public boolean followSelection = true;
320
321   /**
322    * @return true if view selection should always follow the selections
323    *         broadcast by other selection sources
324    */
325   public boolean getFollowSelection()
326   {
327     return followSelection;
328   }
329
330   public void sendSelection()
331   {
332     getStructureSelectionManager().sendSelection(
333                     new SequenceGroup(getSelectionGroup()),
334                     new ColumnSelection(getColumnSelection()), this);
335   }
336
337   /**
338    * Returns an instance of the StructureSelectionManager scoped to this applet
339    * instance.
340    * 
341    * @return
342    */
343   @Override
344   public StructureSelectionManager getStructureSelectionManager()
345   {
346     return jalview.structure.StructureSelectionManager
347             .getStructureSelectionManager(applet);
348   }
349
350   /**
351    * synthesize a column selection if none exists so it covers the given
352    * selection group. if wholewidth is false, no column selection is made if the
353    * selection group covers the whole alignment width.
354    * 
355    * @param sg
356    * @param wholewidth
357    */
358   public void expandColSelection(SequenceGroup sg, boolean wholewidth)
359   {
360     int sgs, sge;
361     if (sg != null
362             && (sgs = sg.getStartRes()) >= 0
363             && sg.getStartRes() <= (sge = sg.getEndRes())
364             && (colSel == null || colSel.getSelected() == null || colSel
365                     .getSelected().size() == 0))
366     {
367       if (!wholewidth && alignment.getWidth() == (1 + sge - sgs))
368       {
369         // do nothing
370         return;
371       }
372       if (colSel == null)
373       {
374         colSel = new ColumnSelection();
375       }
376       for (int cspos = sg.getStartRes(); cspos <= sg.getEndRes(); cspos++)
377       {
378         colSel.addElement(cspos);
379       }
380     }
381   }
382
383   public boolean isNormaliseSequenceLogo()
384   {
385     return normaliseSequenceLogo;
386   }
387
388   public void setNormaliseSequenceLogo(boolean state)
389   {
390     normaliseSequenceLogo = state;
391   }
392
393   /**
394    * 
395    * @return true if alignment characters should be displayed
396    */
397   public boolean isValidCharWidth()
398   {
399     return validCharWidth;
400   }
401
402   public AnnotationColumnChooser getAnnotationColumnSelectionState()
403   {
404     return annotationColumnSelectionState;
405   }
406
407   public void setAnnotationColumnSelectionState(
408           AnnotationColumnChooser annotationColumnSelectionState)
409   {
410     this.annotationColumnSelectionState = annotationColumnSelectionState;
411   }
412
413   @Override
414   public void mirrorCommand(CommandI command, boolean undo,
415           StructureSelectionManager ssm, VamsasSource source)
416   {
417     // TODO refactor so this can be pulled up to superclass or controller
418     /*
419      * Do nothing unless we are a 'complement' of the source. May replace this
420      * with direct calls not via SSM.
421      */
422     if (source instanceof AlignViewportI
423             && ((AlignViewportI) source).getCodingComplement() == this)
424     {
425       // ok to continue;
426     }
427     else
428     {
429       return;
430     }
431
432     CommandI mappedCommand = ssm.mapCommand(command, undo, getAlignment(),
433             getGapCharacter());
434     if (mappedCommand != null)
435     {
436       mappedCommand.doCommand(null);
437       firePropertyChange("alignment", null, getAlignment().getSequences());
438
439       // ap.scalePanelHolder.repaint();
440       // ap.repaint();
441     }
442   }
443
444   @Override
445   public VamsasSource getVamsasSource()
446   {
447     return this;
448   }
449
450   /**
451    * If this viewport has a (Protein/cDNA) complement, then scroll the
452    * complementary alignment to match this one.
453    */
454   public void scrollComplementaryAlignment(AlignmentPanel complementPanel)
455   {
456     if (complementPanel == null)
457     {
458       return;
459     }
460
461     /*
462      * Populate a SearchResults object with the mapped location to scroll to. If
463      * there is no complement, or it is not following highlights, or no mapping
464      * is found, the result will be empty.
465      */
466     SearchResults sr = new SearchResults();
467     int seqOffset = findComplementScrollTarget(sr);
468     if (!sr.isEmpty())
469     {
470       complementPanel.setFollowingComplementScroll(true);
471       complementPanel.scrollToCentre(sr, seqOffset);
472     }
473   }
474
475   @Override
476   public FeatureRenderer getFeatureRenderer()
477   {
478     return featureRenderer;
479   }
480
481   @Override
482   public void setFeatureRenderer(FeatureRenderer featureRenderer)
483   {
484     this.featureRenderer = featureRenderer;
485
486   }
487
488   public boolean isIncludeHiddenRegion()
489   {
490     return includeHiddenRegion;
491   }
492
493   public void setIncludeHiddenRegion(boolean includeHiddenRegion)
494   {
495     this.includeHiddenRegion = includeHiddenRegion;
496   }
497
498 }