JAL-1483 model and api for feature display list on alignment view (refactor)
[jalview.git] / src / jalview / appletgui / AlignViewport.java
1 /*
2  * Jalview - A Sequence Alignment Editor and Viewer (Version 2.8.2)
3  * Copyright (C) 2014 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.util.*;
24
25 import java.awt.*;
26
27 import jalview.analysis.*;
28 import jalview.api.AlignViewportI;
29 import jalview.bin.*;
30 import jalview.datamodel.*;
31 import jalview.schemes.*;
32 import jalview.structure.SelectionSource;
33 import jalview.structure.VamsasSource;
34 import jalview.viewmodel.AlignmentViewport;
35
36 public class AlignViewport extends AlignmentViewport implements
37         AlignViewportI, SelectionSource, VamsasSource
38 {
39   int startRes;
40
41   int endRes;
42
43   int startSeq;
44
45   int endSeq;
46
47   boolean cursorMode = false;
48
49   boolean showJVSuffix = true;
50
51   boolean showText = true;
52
53   boolean showColourText = false;
54
55   boolean showBoxes = true;
56
57   boolean wrapAlignment = false;
58
59   boolean renderGaps = true;
60
61   boolean showSequenceFeatures = false;
62
63   boolean showAnnotation = true;
64
65   boolean upperCasebold = false;
66
67   int charHeight;
68
69   int charWidth;
70
71   int wrappedWidth;
72
73   Font font = new Font("SansSerif", Font.PLAIN, 10);
74
75   boolean validCharWidth = true;
76
77   int threshold;
78
79   int increment;
80
81   NJTree currentTree = null;
82
83   boolean scaleAboveWrapped = true;
84
85   boolean scaleLeftWrapped = true;
86
87   boolean scaleRightWrapped = true;
88
89   boolean showHiddenMarkers = true;
90
91   public jalview.bin.JalviewLite applet;
92
93   boolean MAC = false;
94
95   Stack historyList = new Stack();
96
97   Stack redoList = new Stack();
98
99   public void finalize()
100   {
101     applet = null;
102     quality = null;
103     alignment = null;
104     colSel = null;
105   }
106
107   public AlignViewport(AlignmentI al, JalviewLite applet)
108   {
109     calculator = new jalview.workers.AlignCalcManager();
110     this.applet = applet;
111     setAlignment(al);
112     // we always pad gaps
113     this.setPadGaps(true);
114     this.startRes = 0;
115     this.endRes = al.getWidth() - 1;
116     this.startSeq = 0;
117     this.endSeq = al.getHeight() - 1;
118     if (applet != null)
119     {
120       // get the width and height scaling factors if they were specified
121       String param = applet.getParameter("widthScale");
122       if (param != null)
123       {
124         try
125         {
126           widthScale = new Float(param).floatValue();
127         } catch (Exception e)
128         {
129         }
130         if (widthScale <= 1.0)
131         {
132           System.err
133                   .println("Invalid alignment character width scaling factor ("
134                           + widthScale + "). Ignoring.");
135           widthScale = 1;
136         }
137         if (applet.debug)
138         {
139           System.err
140                   .println("Alignment character width scaling factor is now "
141                           + widthScale);
142         }
143       }
144       param = applet.getParameter("heightScale");
145       if (param != null)
146       {
147         try
148         {
149           heightScale = new Float(param).floatValue();
150         } catch (Exception e)
151         {
152         }
153         if (heightScale <= 1.0)
154         {
155           System.err
156                   .println("Invalid alignment character height scaling factor ("
157                           + heightScale + "). Ignoring.");
158           heightScale = 1;
159         }
160         if (applet.debug)
161         {
162           System.err
163                   .println("Alignment character height scaling factor is now "
164                           + heightScale);
165         }
166       }
167     }
168     setFont(font);
169
170     MAC = new jalview.util.Platform().isAMac();
171
172     if (applet != null)
173     {
174       showJVSuffix = applet.getDefaultParameter("showFullId", showJVSuffix);
175
176       showAnnotation = applet.getDefaultParameter("showAnnotation",
177               showAnnotation);
178
179       showConservation = applet.getDefaultParameter("showConservation",
180               showConservation);
181
182       showQuality = applet.getDefaultParameter("showQuality", showQuality);
183
184       showConsensus = applet.getDefaultParameter("showConsensus",
185               showConsensus);
186
187       showUnconserved = applet.getDefaultParameter("showUnconserved",
188               showUnconserved);
189
190       String param = applet.getParameter("upperCase");
191       if (param != null)
192       {
193         if (param.equalsIgnoreCase("bold"))
194         {
195           upperCasebold = true;
196         }
197       }
198       sortByTree = applet.getDefaultParameter("sortByTree", sortByTree);
199
200       followHighlight = applet.getDefaultParameter("automaticScrolling",
201               followHighlight);
202       followSelection = followHighlight;
203
204       showSequenceLogo = applet.getDefaultParameter("showSequenceLogo",
205               showSequenceLogo);
206
207       normaliseSequenceLogo = applet.getDefaultParameter(
208               "normaliseSequenceLogo", applet.getDefaultParameter(
209                       "normaliseLogo", normaliseSequenceLogo));
210
211       showGroupConsensus = applet.getDefaultParameter("showGroupConsensus",
212               showGroupConsensus);
213
214       showGroupConservation = applet.getDefaultParameter(
215               "showGroupConservation", showGroupConservation);
216
217       showConsensusHistogram = applet.getDefaultParameter(
218               "showConsensusHistogram", showConsensusHistogram);
219
220     }
221
222     if (applet != null)
223     {
224       String colour = applet.getParameter("defaultColour");
225
226       if (colour == null)
227       {
228         colour = applet.getParameter("userDefinedColour");
229         if (colour != null)
230         {
231           colour = "User Defined";
232         }
233       }
234
235       if (colour != null)
236       {
237         globalColourScheme = ColourSchemeProperty.getColour(alignment,
238                 colour);
239         if (globalColourScheme != null)
240         {
241           globalColourScheme.setConsensus(hconsensus);
242         }
243       }
244
245       if (applet.getParameter("userDefinedColour") != null)
246       {
247         ((UserColourScheme) globalColourScheme).parseAppletParameter(applet
248                 .getParameter("userDefinedColour"));
249       }
250     }
251     initAutoAnnotation();
252
253   }
254
255   public void showSequenceFeatures(boolean b)
256   {
257     showSequenceFeatures = b;
258   }
259
260   public boolean getShowSequenceFeatures()
261   {
262     return showSequenceFeatures;
263   }
264
265   /**
266    * get the consensus sequence as displayed under the PID consensus annotation
267    * row.
268    * 
269    * @return consensus sequence as a new sequence object
270    */
271   public SequenceI getConsensusSeq()
272   {
273     if (consensus == null)
274     {
275       updateConsensus(null);
276     }
277     if (consensus == null)
278     {
279       return null;
280     }
281     StringBuffer seqs = new StringBuffer();
282     for (int i = 0; i < consensus.annotations.length; i++)
283     {
284       if (consensus.annotations[i] != null)
285       {
286         if (consensus.annotations[i].description.charAt(0) == '[')
287         {
288           seqs.append(consensus.annotations[i].description.charAt(1));
289         }
290         else
291         {
292           seqs.append(consensus.annotations[i].displayCharacter);
293         }
294       }
295     }
296     SequenceI sq = new Sequence("Consensus", seqs.toString());
297     sq.setDescription("Percentage Identity Consensus "
298             + ((ignoreGapsInConsensusCalculation) ? " without gaps" : ""));
299     return sq;
300   }
301
302   public int getStartRes()
303   {
304     return startRes;
305   }
306
307   public int getEndRes()
308   {
309     return endRes;
310   }
311
312   public int getStartSeq()
313   {
314     return startSeq;
315   }
316
317   public void setStartRes(int res)
318   {
319     this.startRes = res;
320   }
321
322   public void setStartSeq(int seq)
323   {
324     this.startSeq = seq;
325   }
326
327   public void setEndRes(int res)
328   {
329     if (res > alignment.getWidth() - 1)
330     {
331       // log.System.out.println(" Corrected res from " + res + " to maximum " +
332       // (alignment.getWidth()-1));
333       res = alignment.getWidth() - 1;
334     }
335     if (res < 0)
336     {
337       res = 0;
338     }
339     this.endRes = res;
340   }
341
342   public void setEndSeq(int seq)
343   {
344     if (seq > alignment.getHeight())
345     {
346       seq = alignment.getHeight();
347     }
348     if (seq < 0)
349     {
350       seq = 0;
351     }
352     this.endSeq = seq;
353   }
354
355   public int getEndSeq()
356   {
357     return endSeq;
358   }
359
360   java.awt.Frame nullFrame;
361
362   protected FeatureSettings featureSettings = null;
363
364   private float heightScale = 1, widthScale = 1;
365
366   public void setFont(Font f)
367   {
368     font = f;
369     if (nullFrame == null)
370     {
371       nullFrame = new java.awt.Frame();
372       nullFrame.addNotify();
373     }
374
375     java.awt.FontMetrics fm = nullFrame.getGraphics().getFontMetrics(font);
376     setCharHeight((int) (heightScale * fm.getHeight()));
377     charWidth = (int) (widthScale * fm.charWidth('M'));
378
379     if (upperCasebold)
380     {
381       Font f2 = new Font(f.getName(), Font.BOLD, f.getSize());
382       fm = nullFrame.getGraphics().getFontMetrics(f2);
383       charWidth = (int) (widthScale * (fm.stringWidth("MMMMMMMMMMM") / 10));
384     }
385   }
386
387   public Font getFont()
388   {
389     return font;
390   }
391
392   public int getCharWidth()
393   {
394     return charWidth;
395   }
396
397   public void setCharHeight(int h)
398   {
399     this.charHeight = h;
400   }
401
402   public int getCharHeight()
403   {
404     return charHeight;
405   }
406
407   public void setWrappedWidth(int w)
408   {
409     this.wrappedWidth = w;
410   }
411
412   public int getwrappedWidth()
413   {
414     return wrappedWidth;
415   }
416
417   public AlignmentI getAlignment()
418   {
419     return alignment;
420   }
421
422   public void setAlignment(AlignmentI align)
423   {
424     this.alignment = align;
425   }
426
427   public void setWrapAlignment(boolean state)
428   {
429     wrapAlignment = state;
430   }
431
432   public void setShowText(boolean state)
433   {
434     showText = state;
435   }
436
437   public void setRenderGaps(boolean state)
438   {
439     renderGaps = state;
440   }
441
442   public boolean getColourText()
443   {
444     return showColourText;
445   }
446
447   public void setColourText(boolean state)
448   {
449     showColourText = state;
450   }
451
452   public void setShowBoxes(boolean state)
453   {
454     showBoxes = state;
455   }
456
457   public boolean getWrapAlignment()
458   {
459     return wrapAlignment;
460   }
461
462   public boolean getShowText()
463   {
464     return showText;
465   }
466
467   public boolean getShowBoxes()
468   {
469     return showBoxes;
470   }
471
472   public char getGapCharacter()
473   {
474     return getAlignment().getGapCharacter();
475   }
476
477   public void setGapCharacter(char gap)
478   {
479     if (getAlignment() != null)
480     {
481       getAlignment().setGapCharacter(gap);
482     }
483   }
484
485   public void resetSeqLimits(int height)
486   {
487     setEndSeq(height / getCharHeight());
488   }
489
490   public void setCurrentTree(NJTree tree)
491   {
492     currentTree = tree;
493   }
494
495   public NJTree getCurrentTree()
496   {
497     return currentTree;
498   }
499
500   public boolean getShowJVSuffix()
501   {
502     return showJVSuffix;
503   }
504
505   public void setShowJVSuffix(boolean b)
506   {
507     showJVSuffix = b;
508   }
509
510   public boolean getShowAnnotation()
511   {
512     return showAnnotation;
513   }
514
515   public void setShowAnnotation(boolean b)
516   {
517     showAnnotation = b;
518   }
519
520   public boolean getScaleAboveWrapped()
521   {
522     return scaleAboveWrapped;
523   }
524
525   public boolean getScaleLeftWrapped()
526   {
527     return scaleLeftWrapped;
528   }
529
530   public boolean getScaleRightWrapped()
531   {
532     return scaleRightWrapped;
533   }
534
535   public void setScaleAboveWrapped(boolean b)
536   {
537     scaleAboveWrapped = b;
538   }
539
540   public void setScaleLeftWrapped(boolean b)
541   {
542     scaleLeftWrapped = b;
543   }
544
545   public void setScaleRightWrapped(boolean b)
546   {
547     scaleRightWrapped = b;
548   }
549
550   public void setIgnoreGapsConsensus(boolean b)
551   {
552     ignoreGapsInConsensusCalculation = b;
553     updateConsensus(null);
554     if (globalColourScheme != null)
555     {
556       globalColourScheme.setThreshold(globalColourScheme.getThreshold(),
557               ignoreGapsInConsensusCalculation);
558
559     }
560   }
561
562   public boolean getShowHiddenMarkers()
563   {
564     return showHiddenMarkers;
565   }
566
567   public void setShowHiddenMarkers(boolean show)
568   {
569     showHiddenMarkers = show;
570   }
571
572   boolean centreColumnLabels;
573
574   public boolean getCentreColumnLabels()
575   {
576     return centreColumnLabels;
577   }
578
579   public boolean followHighlight = true;
580
581   public boolean getFollowHighlight()
582   {
583     return followHighlight;
584   }
585
586   public boolean followSelection = true;
587
588   /**
589    * @return true if view selection should always follow the selections
590    *         broadcast by other selection sources
591    */
592   public boolean getFollowSelection()
593   {
594     return followSelection;
595   }
596
597   public void sendSelection()
598   {
599     jalview.structure.StructureSelectionManager
600             .getStructureSelectionManager(applet).sendSelection(
601                     new SequenceGroup(getSelectionGroup()),
602                     new ColumnSelection(getColumnSelection()), this);
603   }
604
605   /**
606    * synthesize a column selection if none exists so it covers the given
607    * selection group. if wholewidth is false, no column selection is made if the
608    * selection group covers the whole alignment width.
609    * 
610    * @param sg
611    * @param wholewidth
612    */
613   public void expandColSelection(SequenceGroup sg, boolean wholewidth)
614   {
615     int sgs, sge;
616     if (sg != null
617             && (sgs = sg.getStartRes()) >= 0
618             && sg.getStartRes() <= (sge = sg.getEndRes())
619             && (colSel == null || colSel.getSelected() == null || colSel
620                     .getSelected().size() == 0))
621     {
622       if (!wholewidth && alignment.getWidth() == (1 + sge - sgs))
623       {
624         // do nothing
625         return;
626       }
627       if (colSel == null)
628       {
629         colSel = new ColumnSelection();
630       }
631       for (int cspos = sg.getStartRes(); cspos <= sg.getEndRes(); cspos++)
632       {
633         colSel.addElement(cspos);
634       }
635     }
636   }
637
638   @Override
639   public boolean hasHiddenColumns()
640   {
641     return hasHiddenColumns;
642   }
643
644   public boolean isNormaliseSequenceLogo()
645   {
646     return normaliseSequenceLogo;
647   }
648
649   public void setNormaliseSequenceLogo(boolean state)
650   {
651     normaliseSequenceLogo = state;
652   }
653
654   /**
655    * 
656    * @return true if alignment characters should be displayed
657    */
658   public boolean isValidCharWidth()
659   {
660     return validCharWidth;
661   }
662
663 }