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