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