Use propertyListener for alignment edits
[jalview.git] / src / jalview / appletgui / AlignViewport.java
1 /*\r
2  * Jalview - A Sequence Alignment Editor and Viewer\r
3  * Copyright (C) 2005 AM Waterhouse, J Procter, G Barton, M Clamp, S Searle\r
4  *\r
5  * This program is free software; you can redistribute it and/or\r
6  * modify it under the terms of the GNU General Public License\r
7  * as published by the Free Software Foundation; either version 2\r
8  * of the License, or (at your option) any later version.\r
9  *\r
10  * This program is distributed in the hope that it will be useful,\r
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
13  * GNU General Public License for more details.\r
14  *\r
15  * You should have received a copy of the GNU General Public License\r
16  * along with this program; if not, write to the Free Software\r
17  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA\r
18  */\r
19 \r
20 package jalview.appletgui;\r
21 \r
22 import java.util.*;\r
23 \r
24 import java.awt.*;\r
25 \r
26 import jalview.analysis.*;\r
27 import jalview.bin.*;\r
28 import jalview.datamodel.*;\r
29 import jalview.schemes.*;\r
30 \r
31 public class AlignViewport\r
32 {\r
33   int startRes;\r
34   int endRes;\r
35 \r
36   int startSeq;\r
37   int endSeq;\r
38 \r
39   boolean showFullId = true;\r
40   boolean showText = true;\r
41   boolean showColourText = false;\r
42   boolean showBoxes = true;\r
43   boolean wrapAlignment = false;\r
44   boolean renderGaps = true;\r
45   boolean showSequenceFeatures = false;\r
46   boolean showAnnotation = true;\r
47   boolean showConservation = true;\r
48   boolean showQuality = true;\r
49   boolean showConsensus = true;\r
50 \r
51   boolean colourAppliesToAllGroups = true;\r
52   ColourSchemeI globalColourScheme = null;\r
53   boolean conservationColourSelected = false;\r
54   boolean abovePIDThreshold = false;\r
55 \r
56   SequenceGroup selectionGroup = new SequenceGroup();\r
57 \r
58   int charHeight;\r
59   int charWidth;\r
60   int chunkWidth;\r
61   int chunkHeight;\r
62 \r
63   Font font = new Font("SansSerif", Font.PLAIN, 10);\r
64   AlignmentI alignment;\r
65 \r
66   ColumnSelection colSel = new ColumnSelection();\r
67 \r
68   int threshold;\r
69   int increment;\r
70 \r
71   NJTree currentTree = null;\r
72 \r
73   boolean scaleAboveWrapped = true;\r
74   boolean scaleLeftWrapped = true;\r
75   boolean scaleRightWrapped = true;\r
76 \r
77   public Vector vconsensus;\r
78   AlignmentAnnotation consensus;\r
79   AlignmentAnnotation conservation;\r
80   AlignmentAnnotation quality;\r
81 \r
82   public int ConsPercGaps = 25; // JBPNote : This should be a scalable property!\r
83 \r
84   private java.beans.PropertyChangeSupport changeSupport = new java.beans.PropertyChangeSupport(this);\r
85 \r
86   boolean ignoreGapsInConsensusCalculation = false;\r
87 \r
88   public AlignViewport(AlignmentI al, JalviewLite applet)\r
89   {\r
90     setAlignment(al);\r
91     this.startRes = 0;\r
92     this.endRes = al.getWidth() - 1;\r
93     this.startSeq = 0;\r
94     this.endSeq = al.getHeight() - 1;\r
95     setFont(font);\r
96 \r
97     if (applet != null)\r
98     {\r
99       String param = applet.getParameter("showFullId");\r
100       if (param != null)\r
101       {\r
102         showFullId = Boolean.valueOf(param).booleanValue();\r
103       }\r
104 \r
105       param = applet.getParameter("showAnnotation");\r
106       if (param != null)\r
107       {\r
108         showAnnotation = Boolean.valueOf(param).booleanValue();\r
109       }\r
110 \r
111       param = applet.getParameter("showConservation");\r
112       if (param != null)\r
113       {\r
114         showConservation = Boolean.valueOf(param).booleanValue();\r
115       }\r
116 \r
117       param = applet.getParameter("showQuality");\r
118       if (param != null)\r
119       {\r
120         showQuality = Boolean.valueOf(param).booleanValue();\r
121       }\r
122 \r
123       param = applet.getParameter("showConsensus");\r
124       if (param != null)\r
125       {\r
126         showConsensus = Boolean.valueOf(param).booleanValue();\r
127       }\r
128     }\r
129     // We must set conservation and consensus before setting colour,\r
130     // as Blosum and Clustal require this to be done\r
131     updateConservation();\r
132     updateConsensus();\r
133 \r
134     if (applet != null && applet.getParameter("defaultColour") != null)\r
135     {\r
136       globalColourScheme = ColourSchemeProperty.getColour(alignment,\r
137           applet.getParameter("defaultColour"));\r
138       if (globalColourScheme != null)\r
139       {\r
140         globalColourScheme.setConsensus(vconsensus);\r
141       }\r
142     }\r
143   }\r
144 \r
145   public void showSequenceFeatures(boolean b)\r
146   {\r
147     showSequenceFeatures = b;\r
148   }\r
149 \r
150 \r
151   public void updateConservation()\r
152   {\r
153     Conservation cons = new jalview.analysis.Conservation("All",\r
154         jalview.schemes.ResidueProperties.propHash, 3,\r
155         alignment.getSequences(), 0,\r
156         alignment.getWidth() - 1);\r
157     cons.calculate();\r
158     cons.verdict(false, ConsPercGaps);\r
159     cons.findQuality();\r
160     int alWidth = alignment.getWidth();\r
161     Annotation[] annotations = new Annotation[alWidth];\r
162     Annotation[] qannotations = new Annotation[alWidth];\r
163     String sequence = cons.getConsSequence().getSequence();\r
164     float minR, minG, minB, maxR, maxG, maxB;\r
165     minR = 0.3f;\r
166     minG = 0.0f;\r
167     minB = 0f;\r
168     maxR = 1.0f - minR;\r
169     maxG = 0.9f - minG;\r
170     maxB = 0f - minB; // scalable range for colouring both Conservation and Quality\r
171     float min = 0f;\r
172     float max = 11f;\r
173     float qmin = cons.qualityRange[0].floatValue();\r
174     float qmax = cons.qualityRange[1].floatValue();\r
175 \r
176     for (int i = 0; i < alWidth; i++)\r
177     {\r
178       float value = 0;\r
179       try\r
180       {\r
181         value = Integer.parseInt(sequence.charAt(i) + "");\r
182       }\r
183       catch (Exception ex)\r
184       {\r
185         if (sequence.charAt(i) == '*')\r
186         {\r
187           value = 11;\r
188         }\r
189         if (sequence.charAt(i) == '+')\r
190         {\r
191           value = 10;\r
192         }\r
193       }\r
194       float vprop = value - min;\r
195       vprop /= max;\r
196 \r
197       annotations[i] = new Annotation(sequence.charAt(i) + "",\r
198                                       "", ' ', value,\r
199                                       new Color(minR + maxR * vprop,\r
200                                                 minG + maxG * vprop,\r
201                                                 minB + maxB * vprop));\r
202       // Quality calc\r
203       value = ( (Double) cons.quality.elementAt(i)).floatValue();\r
204       vprop = value - qmin;\r
205       vprop /= qmax;\r
206       qannotations[i] = new Annotation(" ",\r
207                                        String.valueOf(value), ' ', value,\r
208                                        new\r
209                                        Color(minR + maxR * vprop,\r
210                                              minG + maxG * vprop,\r
211                                              minB + maxB * vprop));\r
212     }\r
213 \r
214     if (conservation == null)\r
215     {\r
216       conservation = new AlignmentAnnotation("Conservation",\r
217                                              "Conservation of total alignment less than " +\r
218                                              ConsPercGaps + "% gaps",\r
219                                              annotations,\r
220                                              0f, // cons.qualityRange[0].floatValue(),\r
221                                              11f, // cons.qualityRange[1].floatValue()\r
222                                              1);\r
223       if (showConservation)\r
224       {\r
225         alignment.addAnnotation(conservation);\r
226       }\r
227       quality = new AlignmentAnnotation("Quality",\r
228                                         "Alignment Quality based on Blosum62 scores",\r
229                                         qannotations,\r
230                                         cons.qualityRange[0].floatValue(),\r
231                                         cons.qualityRange[1].floatValue(),\r
232                                         1);\r
233       if (showQuality)\r
234       {\r
235         alignment.addAnnotation(quality);\r
236       }\r
237     }\r
238     else\r
239     {\r
240       conservation.annotations = annotations;\r
241       quality.annotations = qannotations;\r
242       quality.graphMax = cons.qualityRange[1].floatValue();\r
243     }\r
244 \r
245   }\r
246 \r
247   public void updateConsensus()\r
248   {\r
249     Annotation[] annotations = new Annotation[alignment.getWidth()];\r
250 \r
251     // this routine prevents vconsensus becoming a new object each time\r
252     // consenus is calculated. Important for speed of Blosum62\r
253     // and PID colouring of alignment\r
254     if (vconsensus == null)\r
255     {\r
256       vconsensus = alignment.getAAFrequency();\r
257     }\r
258     else\r
259     {\r
260       Vector temp = alignment.getAAFrequency();\r
261       vconsensus.removeAllElements();\r
262       Enumeration e = temp.elements();\r
263       while (e.hasMoreElements())\r
264       {\r
265         vconsensus.addElement(e.nextElement());\r
266       }\r
267     }\r
268     Hashtable hash = null;\r
269     for (int i = 0; i < alignment.getWidth(); i++)\r
270     {\r
271       hash = (Hashtable) vconsensus.elementAt(i);\r
272       float value = 0;\r
273       if(ignoreGapsInConsensusCalculation)\r
274         value = ((Float)hash.get("pid_nogaps")).floatValue();\r
275       else\r
276         value = ((Float)hash.get("pid_gaps")).floatValue();\r
277 \r
278       String maxRes = hash.get("maxResidue").toString();\r
279       String mouseOver = hash.get("maxResidue") + " ";\r
280       if (maxRes.length() > 1)\r
281       {\r
282         mouseOver = "[" + maxRes + "] ";\r
283         maxRes = "+";\r
284       }\r
285 \r
286 \r
287       mouseOver += (int) value + "%";\r
288       annotations[i] = new Annotation(maxRes, mouseOver, ' ', value);\r
289 \r
290     }\r
291 \r
292     if (consensus == null)\r
293     {\r
294       consensus = new AlignmentAnnotation("Consensus",\r
295                                           "PID", annotations, 0f, 100f, 1);\r
296       if (showConsensus)\r
297       {\r
298         alignment.addAnnotation(consensus);\r
299       }\r
300     }\r
301     else\r
302     {\r
303       consensus.annotations = annotations;\r
304     }\r
305 \r
306     if(globalColourScheme!=null)\r
307           globalColourScheme.setConsensus(vconsensus);\r
308 \r
309   }\r
310 \r
311   public SequenceGroup getSelectionGroup()\r
312   {\r
313     return selectionGroup;\r
314   }\r
315 \r
316   public void setSelectionGroup(SequenceGroup sg)\r
317   {\r
318     selectionGroup = sg;\r
319   }\r
320 \r
321   public boolean getConservationSelected()\r
322   {\r
323     return conservationColourSelected;\r
324   }\r
325 \r
326   public void setConservationSelected(boolean b)\r
327   {\r
328     conservationColourSelected = b;\r
329   }\r
330 \r
331   public boolean getAbovePIDThreshold()\r
332   {\r
333     return abovePIDThreshold;\r
334   }\r
335 \r
336   public void setAbovePIDThreshold(boolean b)\r
337   {\r
338     abovePIDThreshold = b;\r
339   }\r
340 \r
341   public int getStartRes()\r
342   {\r
343     return startRes;\r
344   }\r
345 \r
346   public int getEndRes()\r
347   {\r
348     return endRes;\r
349   }\r
350 \r
351   public int getStartSeq()\r
352   {\r
353     return startSeq;\r
354   }\r
355 \r
356   public void setGlobalColourScheme(ColourSchemeI cs)\r
357   {\r
358     globalColourScheme = cs;\r
359   }\r
360 \r
361   public ColourSchemeI getGlobalColourScheme()\r
362   {\r
363     return globalColourScheme;\r
364   }\r
365 \r
366   public void setStartRes(int res)\r
367   {\r
368     this.startRes = res;\r
369   }\r
370 \r
371   public void setStartSeq(int seq)\r
372   {\r
373     this.startSeq = seq;\r
374   }\r
375 \r
376   public void setEndRes(int res)\r
377   {\r
378     if (res > alignment.getWidth() - 1)\r
379     {\r
380       // log.System.out.println(" Corrected res from " + res + " to maximum " + (alignment.getWidth()-1));\r
381       res = alignment.getWidth() - 1;\r
382     }\r
383     if (res < 0)\r
384     {\r
385       res = 0;\r
386     }\r
387     this.endRes = res;\r
388   }\r
389 \r
390   public void setEndSeq(int seq)\r
391   {\r
392     if (seq > alignment.getHeight())\r
393     {\r
394       seq = alignment.getHeight();\r
395     }\r
396     if (seq < 0)\r
397     {\r
398       seq = 0;\r
399     }\r
400     this.endSeq = seq;\r
401   }\r
402 \r
403   public int getEndSeq()\r
404   {\r
405     return endSeq;\r
406   }\r
407 \r
408   public void setFont(Font f)\r
409   {\r
410     font = f;\r
411     java.awt.Frame temp = new java.awt.Frame();\r
412     temp.addNotify();\r
413     java.awt.FontMetrics fm = temp.getGraphics().getFontMetrics(font);\r
414     setCharHeight(fm.getHeight());\r
415     setCharWidth(fm.charWidth('M'));\r
416   }\r
417 \r
418   public Font getFont()\r
419   {\r
420     return font;\r
421   }\r
422 \r
423   public void setCharWidth(int w)\r
424   {\r
425     this.charWidth = w;\r
426   }\r
427 \r
428   public int getCharWidth()\r
429   {\r
430     return charWidth;\r
431   }\r
432 \r
433   public void setCharHeight(int h)\r
434   {\r
435     this.charHeight = h;\r
436   }\r
437 \r
438   public int getCharHeight()\r
439   {\r
440     return charHeight;\r
441   }\r
442 \r
443   public void setChunkWidth(int w)\r
444   {\r
445     this.chunkWidth = w;\r
446   }\r
447 \r
448   public int getChunkWidth()\r
449   {\r
450     return chunkWidth;\r
451   }\r
452 \r
453   public void setChunkHeight(int h)\r
454   {\r
455     this.chunkHeight = h;\r
456   }\r
457 \r
458   public int getChunkHeight()\r
459   {\r
460     return chunkHeight;\r
461   }\r
462 \r
463   public AlignmentI getAlignment()\r
464   {\r
465     return alignment;\r
466   }\r
467 \r
468   public void setAlignment(AlignmentI align)\r
469   {\r
470     this.alignment = align;\r
471   }\r
472 \r
473   public void setWrapAlignment(boolean state)\r
474   {\r
475     wrapAlignment = state;\r
476   }\r
477 \r
478   public void setShowText(boolean state)\r
479   {\r
480     showText = state;\r
481   }\r
482 \r
483   public void setRenderGaps(boolean state)\r
484   {\r
485     renderGaps = state;\r
486   }\r
487 \r
488   public boolean getColourText()\r
489   {\r
490     return showColourText;\r
491   }\r
492 \r
493   public void setColourText(boolean state)\r
494   {\r
495     showColourText = state;\r
496   }\r
497 \r
498   public void setShowBoxes(boolean state)\r
499   {\r
500     showBoxes = state;\r
501   }\r
502 \r
503   public boolean getWrapAlignment()\r
504   {\r
505     return wrapAlignment;\r
506   }\r
507 \r
508   public boolean getShowText()\r
509   {\r
510     return showText;\r
511   }\r
512 \r
513   public boolean getShowBoxes()\r
514   {\r
515     return showBoxes;\r
516   }\r
517 \r
518   public char getGapCharacter()\r
519   {\r
520     return getAlignment().getGapCharacter();\r
521   }\r
522 \r
523   public void setGapCharacter(char gap)\r
524   {\r
525     if (getAlignment() != null)\r
526     {\r
527       getAlignment().setGapCharacter(gap);\r
528     }\r
529   }\r
530 \r
531   public void setThreshold(int thresh)\r
532   {\r
533     threshold = thresh;\r
534   }\r
535 \r
536   public int getThreshold()\r
537   {\r
538     return threshold;\r
539   }\r
540 \r
541   public void setIncrement(int inc)\r
542   {\r
543     increment = inc;\r
544   }\r
545 \r
546   public int getIncrement()\r
547   {\r
548     return increment;\r
549   }\r
550 \r
551   public int getIndex(int y)\r
552   {\r
553     int y1 = 0;\r
554     int starty = getStartSeq();\r
555     int endy = getEndSeq();\r
556 \r
557     for (int i = starty; i <= endy; i++)\r
558     {\r
559       if (i < alignment.getHeight() && alignment.getSequenceAt(i) != null)\r
560       {\r
561         int y2 = y1 + getCharHeight();\r
562 \r
563         if (y >= y1 && y <= y2)\r
564         {\r
565           return i;\r
566         }\r
567         y1 = y2;\r
568       }\r
569       else\r
570       {\r
571         return -1;\r
572       }\r
573     }\r
574     return -1;\r
575   }\r
576 \r
577   public ColumnSelection getColumnSelection()\r
578   {\r
579     return colSel;\r
580   }\r
581 \r
582   public void resetSeqLimits(int height)\r
583   {\r
584     setEndSeq(height / getCharHeight());\r
585   }\r
586 \r
587   public void setCurrentTree(NJTree tree)\r
588   {\r
589     currentTree = tree;\r
590   }\r
591 \r
592   public NJTree getCurrentTree()\r
593   {\r
594     return currentTree;\r
595   }\r
596 \r
597   public void setColourAppliesToAllGroups(boolean b)\r
598   {\r
599     colourAppliesToAllGroups = b;\r
600   }\r
601 \r
602   public boolean getColourAppliesToAllGroups()\r
603   {\r
604     return colourAppliesToAllGroups;\r
605   }\r
606 \r
607   public boolean getShowFullId()\r
608   {\r
609     return showFullId;\r
610   }\r
611 \r
612   public void setShowFullId(boolean b)\r
613   {\r
614     showFullId = b;\r
615   }\r
616 \r
617   public boolean getShowAnnotation()\r
618   {\r
619     return showAnnotation;\r
620   }\r
621 \r
622   public void setShowAnnotation(boolean b)\r
623   {\r
624     showAnnotation = b;\r
625   }\r
626 \r
627   public boolean getScaleAboveWrapped()\r
628   {\r
629     return scaleAboveWrapped;\r
630   }\r
631 \r
632   public boolean getScaleLeftWrapped()\r
633   {\r
634     return scaleLeftWrapped;\r
635   }\r
636 \r
637   public boolean getScaleRightWrapped()\r
638   {\r
639     return scaleRightWrapped;\r
640   }\r
641 \r
642   public void setScaleAboveWrapped(boolean b)\r
643   {\r
644     scaleAboveWrapped = b;\r
645   }\r
646 \r
647   public void setScaleLeftWrapped(boolean b)\r
648   {\r
649     scaleLeftWrapped = b;\r
650   }\r
651 \r
652   public void setScaleRightWrapped(boolean b)\r
653   {\r
654     scaleRightWrapped = b;\r
655   }\r
656 \r
657   public void setIgnoreGapsConsensus(boolean b)\r
658   {\r
659     ignoreGapsInConsensusCalculation = b;\r
660     updateConsensus();\r
661     if (globalColourScheme!=null)\r
662     {\r
663       globalColourScheme.setThreshold(globalColourScheme.getThreshold(),\r
664           ignoreGapsInConsensusCalculation);\r
665 \r
666     }\r
667   }\r
668 \r
669   /**\r
670    * Property change listener for changes in alignment\r
671    *\r
672    * @param listener DOCUMENT ME!\r
673    */\r
674   public void addPropertyChangeListener(\r
675       java.beans.PropertyChangeListener listener)\r
676   {\r
677       changeSupport.addPropertyChangeListener(listener);\r
678   }\r
679 \r
680   /**\r
681    * DOCUMENT ME!\r
682    *\r
683    * @param listener DOCUMENT ME!\r
684    */\r
685   public void removePropertyChangeListener(\r
686       java.beans.PropertyChangeListener listener)\r
687   {\r
688       changeSupport.removePropertyChangeListener(listener);\r
689   }\r
690 \r
691   /**\r
692    * Property change listener for changes in alignment\r
693    *\r
694    * @param prop DOCUMENT ME!\r
695    * @param oldvalue DOCUMENT ME!\r
696    * @param newvalue DOCUMENT ME!\r
697    */\r
698   public void firePropertyChange(String prop, Object oldvalue, Object newvalue)\r
699   {\r
700       changeSupport.firePropertyChange(prop, oldvalue, newvalue);\r
701   }\r
702 \r
703 \r
704 \r
705   public boolean getIgnoreGapsConsensus()\r
706   {\r
707     return ignoreGapsInConsensusCalculation;\r
708   }\r
709 \r
710 \r
711 }\r