ef09b976e4b4b6979f50e146be3947770d25fb3a
[jalview.git] / src / jalview / appletgui / AlignViewport.java
1 /*
2  * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9.0b1)
3  * Copyright (C) 2015 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 jalview.analysis.NJTree;
24 import jalview.api.AlignViewportI;
25 import jalview.bin.JalviewLite;
26 import jalview.commands.CommandI;
27 import jalview.datamodel.AlignmentI;
28 import jalview.datamodel.ColumnSelection;
29 import jalview.datamodel.SearchResults;
30 import jalview.datamodel.Sequence;
31 import jalview.datamodel.SequenceGroup;
32 import jalview.datamodel.SequenceI;
33 import jalview.schemes.ColourSchemeProperty;
34 import jalview.schemes.UserColourScheme;
35 import jalview.structure.CommandListener;
36 import jalview.structure.SelectionSource;
37 import jalview.structure.StructureSelectionManager;
38 import jalview.structure.VamsasSource;
39 import jalview.viewmodel.AlignmentViewport;
40
41 import java.awt.Font;
42
43 public class AlignViewport extends AlignmentViewport implements
44         SelectionSource, VamsasSource, CommandListener
45 {
46   boolean cursorMode = false;
47
48   Font font = new Font("SansSerif", Font.PLAIN, 10);
49
50   boolean validCharWidth = true;
51
52   NJTree currentTree = null;
53
54   public jalview.bin.JalviewLite applet;
55
56   boolean MAC = false;
57
58   private AnnotationColumnChooser annotationColumnSelectionState;
59
60   public void finalize()
61   {
62     applet = null;
63     quality = null;
64     alignment = null;
65     colSel = null;
66   }
67
68   public AlignViewport(AlignmentI al, JalviewLite applet)
69   {
70     super();
71     calculator = new jalview.workers.AlignCalcManager();
72     this.applet = applet;
73     alignment = al;
74     // we always pad gaps
75     this.setPadGaps(true);
76     this.startRes = 0;
77     this.endRes = al.getWidth() - 1;
78     this.startSeq = 0;
79     this.endSeq = al.getHeight() - 1;
80     if (applet != null)
81     {
82       // get the width and height scaling factors if they were specified
83       String param = applet.getParameter("widthScale");
84       if (param != null)
85       {
86         try
87         {
88           widthScale = new Float(param).floatValue();
89         } catch (Exception e)
90         {
91         }
92         if (widthScale <= 1.0)
93         {
94           System.err
95                   .println("Invalid alignment character width scaling factor ("
96                           + widthScale + "). Ignoring.");
97           widthScale = 1;
98         }
99         if (JalviewLite.debug)
100         {
101           System.err
102                   .println("Alignment character width scaling factor is now "
103                           + widthScale);
104         }
105       }
106       param = applet.getParameter("heightScale");
107       if (param != null)
108       {
109         try
110         {
111           heightScale = new Float(param).floatValue();
112         } catch (Exception e)
113         {
114         }
115         if (heightScale <= 1.0)
116         {
117           System.err
118                   .println("Invalid alignment character height scaling factor ("
119                           + heightScale + "). Ignoring.");
120           heightScale = 1;
121         }
122         if (JalviewLite.debug)
123         {
124           System.err
125                   .println("Alignment character height scaling factor is now "
126                           + heightScale);
127         }
128       }
129     }
130     setFont(font);
131
132     MAC = new jalview.util.Platform().isAMac();
133
134     if (applet != null)
135     {
136       setShowJVSuffix(applet.getDefaultParameter("showFullId",
137               getShowJVSuffix()));
138
139       setShowAnnotation(applet.getDefaultParameter("showAnnotation",
140               isShowAnnotation()));
141
142       showConservation = applet.getDefaultParameter("showConservation",
143               showConservation);
144
145       showQuality = applet.getDefaultParameter("showQuality", showQuality);
146
147       showConsensus = applet.getDefaultParameter("showConsensus",
148               showConsensus);
149
150       setShowUnconserved(applet.getDefaultParameter("showUnconserved",
151               getShowUnconserved()));
152
153       setScaleProteinAsCdna(applet.getDefaultParameter(
154               "scaleProteinAsCdna", isScaleProteinAsCdna()));
155
156       String param = applet.getParameter("upperCase");
157       if (param != null)
158       {
159         if (param.equalsIgnoreCase("bold"))
160         {
161           setUpperCasebold(true);
162         }
163       }
164       sortByTree = applet.getDefaultParameter("sortByTree", sortByTree);
165
166       setFollowHighlight(applet.getDefaultParameter("automaticScrolling",
167               isFollowHighlight()));
168       followSelection = isFollowHighlight();
169
170       showSequenceLogo = applet.getDefaultParameter("showSequenceLogo",
171               showSequenceLogo);
172
173       normaliseSequenceLogo = applet.getDefaultParameter(
174               "normaliseSequenceLogo", applet.getDefaultParameter(
175                       "normaliseLogo", normaliseSequenceLogo));
176
177       showGroupConsensus = applet.getDefaultParameter("showGroupConsensus",
178               showGroupConsensus);
179
180       showGroupConservation = applet.getDefaultParameter(
181               "showGroupConservation", showGroupConservation);
182
183       showConsensusHistogram = applet.getDefaultParameter(
184               "showConsensusHistogram", showConsensusHistogram);
185
186     }
187
188     if (applet != null)
189     {
190       String colour = al.isNucleotide() ? applet
191               .getParameter("defaultColourNuc") : applet
192               .getParameter("defaultColourProt");
193       if (colour == null)
194       {
195         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   public void resetSeqLimits(int height)
296   {
297     setEndSeq(height / getCharHeight());
298   }
299
300   public void setCurrentTree(NJTree tree)
301   {
302     currentTree = tree;
303   }
304
305   public NJTree getCurrentTree()
306   {
307     return currentTree;
308   }
309
310   boolean centreColumnLabels;
311
312   public boolean getCentreColumnLabels()
313   {
314     return centreColumnLabels;
315   }
316
317   public boolean followSelection = true;
318
319   /**
320    * @return true if view selection should always follow the selections
321    *         broadcast by other selection sources
322    */
323   public boolean getFollowSelection()
324   {
325     return followSelection;
326   }
327
328   public void sendSelection()
329   {
330     getStructureSelectionManager().sendSelection(
331             new SequenceGroup(getSelectionGroup()),
332             new ColumnSelection(getColumnSelection()), this);
333   }
334
335   /**
336    * Returns an instance of the StructureSelectionManager scoped to this applet
337    * instance.
338    * 
339    * @return
340    */
341   @Override
342   public StructureSelectionManager getStructureSelectionManager()
343   {
344     return jalview.structure.StructureSelectionManager
345             .getStructureSelectionManager(applet);
346   }
347
348   /**
349    * synthesize a column selection if none exists so it covers the given
350    * selection group. if wholewidth is false, no column selection is made if the
351    * selection group covers the whole alignment width.
352    * 
353    * @param sg
354    * @param wholewidth
355    */
356   public void expandColSelection(SequenceGroup sg, boolean wholewidth)
357   {
358     int sgs, sge;
359     if (sg != null
360             && (sgs = sg.getStartRes()) >= 0
361             && sg.getStartRes() <= (sge = sg.getEndRes())
362             && (colSel == null || colSel.getSelected() == null || colSel
363                     .getSelected().size() == 0))
364     {
365       if (!wholewidth && alignment.getWidth() == (1 + sge - sgs))
366       {
367         // do nothing
368         return;
369       }
370       if (colSel == null)
371       {
372         colSel = new ColumnSelection();
373       }
374       for (int cspos = sg.getStartRes(); cspos <= sg.getEndRes(); cspos++)
375       {
376         colSel.addElement(cspos);
377       }
378     }
379   }
380
381   public boolean isNormaliseSequenceLogo()
382   {
383     return normaliseSequenceLogo;
384   }
385
386   public void setNormaliseSequenceLogo(boolean state)
387   {
388     normaliseSequenceLogo = state;
389   }
390
391   /**
392    * 
393    * @return true if alignment characters should be displayed
394    */
395   public boolean isValidCharWidth()
396   {
397     return validCharWidth;
398   }
399
400   public AnnotationColumnChooser getAnnotationColumnSelectionState()
401   {
402     return annotationColumnSelectionState;
403   }
404
405   public void setAnnotationColumnSelectionState(
406           AnnotationColumnChooser annotationColumnSelectionState)
407   {
408     this.annotationColumnSelectionState = annotationColumnSelectionState;
409   }
410
411   @Override
412   public void mirrorCommand(CommandI command, boolean undo,
413           StructureSelectionManager ssm, VamsasSource source)
414   {
415     // TODO refactor so this can be pulled up to superclass or controller
416     /*
417      * Do nothing unless we are a 'complement' of the source. May replace this
418      * with direct calls not via SSM.
419      */
420     if (source instanceof AlignViewportI
421             && ((AlignViewportI) source).getCodingComplement() == this)
422     {
423       // ok to continue;
424     }
425     else
426     {
427       return;
428     }
429
430     CommandI mappedCommand = ssm.mapCommand(command, undo, getAlignment(),
431             getGapCharacter());
432     if (mappedCommand != null)
433     {
434       mappedCommand.doCommand(null);
435       firePropertyChange("alignment", null, getAlignment().getSequences());
436
437       // ap.scalePanelHolder.repaint();
438       // ap.repaint();
439     }
440   }
441
442   @Override
443   public VamsasSource getVamsasSource()
444   {
445     return this;
446   }
447
448   /**
449    * If this viewport has a (Protein/cDNA) complement, then scroll the
450    * complementary alignment to match this one.
451    */
452   public void scrollComplementaryAlignment(AlignmentPanel complementPanel)
453   {
454     if (complementPanel == null)
455     {
456       return;
457     }
458
459     /*
460      * Populate a SearchResults object with the mapped location to scroll to. If
461      * there is no complement, or it is not following highlights, or no mapping
462      * is found, the result will be empty.
463      */
464     SearchResults sr = new SearchResults();
465     int seqOffset = findComplementScrollTarget(sr);
466     if (!sr.isEmpty())
467     {
468       complementPanel.setFollowingComplementScroll(true);
469       complementPanel.scrollToCentre(sr, seqOffset);
470     }
471   }
472
473 }