ToolTip updated
[jalview.git] / src / jalview / gui / RotatableCanvas.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 package jalview.gui;\r
20 \r
21 import java.util.*;\r
22 \r
23 import java.awt.*;\r
24 import java.awt.event.*;\r
25 import javax.swing.*;\r
26 \r
27 import jalview.datamodel.*;\r
28 import jalview.math.*;\r
29 import jalview.util.*;\r
30 \r
31 public class RotatableCanvas\r
32     extends JPanel implements MouseListener,\r
33     MouseMotionListener, KeyListener\r
34 //RubberbandListener,\r
35 //SequenceSelectionListener\r
36 {\r
37   RotatableMatrix idmat = new RotatableMatrix(3, 3);\r
38   RotatableMatrix objmat = new RotatableMatrix(3, 3);\r
39   RotatableMatrix rotmat = new RotatableMatrix(3, 3);\r
40 \r
41   //RubberbandRectangle rubberband;\r
42   boolean drawAxes = true;\r
43   int omx = 0;\r
44   int mx = 0;\r
45   int omy = 0;\r
46   int my = 0;\r
47   Image img;\r
48   Graphics ig;\r
49   Dimension prefsize;\r
50   float[] centre = new float[3];\r
51   float[] width = new float[3];\r
52   float[] max = new float[3];\r
53   float[] min = new float[3];\r
54   float maxwidth;\r
55   float scale;\r
56   int npoint;\r
57   Vector points;\r
58   float[][] orig;\r
59   float[][] axes;\r
60   int startx;\r
61   int starty;\r
62   int lastx;\r
63   int lasty;\r
64   int rectx1;\r
65   int recty1;\r
66   int rectx2;\r
67   int recty2;\r
68   float scalefactor = 1;\r
69   AlignViewport av;\r
70 \r
71   //  Controller    controller;\r
72   public RotatableCanvas(AlignViewport av, Vector points, int npoint)\r
73   {\r
74     this.points = points;\r
75     this.npoint = npoint;\r
76     this.av = av;\r
77     ToolTipManager.sharedInstance().registerComponent(this);\r
78     ToolTipManager.sharedInstance().setInitialDelay(0);\r
79     ToolTipManager.sharedInstance().setDismissDelay(10000);\r
80     PaintRefresher.Register(this, av.alignment);\r
81 \r
82     prefsize = getPreferredSize();\r
83     orig = new float[npoint][3];\r
84 \r
85     for (int i = 0; i < npoint; i++)\r
86     {\r
87       SequencePoint sp = (SequencePoint) points.elementAt(i);\r
88 \r
89       for (int j = 0; j < 3; j++)\r
90       {\r
91         orig[i][j] = sp.coord[j];\r
92       }\r
93     }\r
94 \r
95     //Initialize the matrices to identity\r
96     for (int i = 0; i < 3; i++)\r
97     {\r
98       for (int j = 0; j < 3; j++)\r
99       {\r
100         if (i != j)\r
101         {\r
102           idmat.addElement(i, j, 0);\r
103           objmat.addElement(i, j, 0);\r
104           rotmat.addElement(i, j, 0);\r
105         }\r
106         else\r
107         {\r
108           idmat.addElement(i, j, 0);\r
109           objmat.addElement(i, j, 0);\r
110           rotmat.addElement(i, j, 0);\r
111         }\r
112       }\r
113     }\r
114 \r
115     axes = new float[3][3];\r
116     initAxes();\r
117 \r
118     findCentre();\r
119     findWidth();\r
120 \r
121     scale = findScale();\r
122 \r
123     //    System.out.println("Scale factor = " + scale);\r
124     addMouseListener(this);\r
125     addKeyListener(this);\r
126 \r
127     // if (getParent() != null) {\r
128     //   getParent().addKeyListener(this);\r
129     //}\r
130     addMouseMotionListener(this);\r
131 \r
132     // Add rubberband\r
133     //   rubberband  = new RubberbandRectangle(this);\r
134     //  rubberband.setActive(true);\r
135     //   rubberband.addListener(this);\r
136   }\r
137 \r
138   /* public boolean handleSequenceSelectionEvent(SequenceSelectionEvent evt) {\r
139      redrawneeded = true;\r
140      repaint();\r
141      return true;\r
142    }\r
143 \r
144    public void removeNotify() {\r
145      controller.removeListener(this);\r
146      super.removeNotify();\r
147    }*/\r
148   public void initAxes()\r
149   {\r
150     for (int i = 0; i < 3; i++)\r
151     {\r
152       for (int j = 0; j < 3; j++)\r
153       {\r
154         if (i != j)\r
155         {\r
156           axes[i][j] = 0;\r
157         }\r
158         else\r
159         {\r
160           axes[i][j] = 1;\r
161         }\r
162       }\r
163     }\r
164   }\r
165 \r
166   public void findWidth()\r
167   {\r
168     max = new float[3];\r
169     min = new float[3];\r
170 \r
171     max[0] = (float) - 1e30;\r
172     max[1] = (float) - 1e30;\r
173     max[2] = (float) - 1e30;\r
174 \r
175     min[0] = (float) 1e30;\r
176     min[1] = (float) 1e30;\r
177     min[2] = (float) 1e30;\r
178 \r
179     for (int i = 0; i < 3; i++)\r
180     {\r
181       for (int j = 0; j < npoint; j++)\r
182       {\r
183         SequencePoint sp = (SequencePoint) points.elementAt(j);\r
184 \r
185         if (sp.coord[i] >= max[i])\r
186         {\r
187           max[i] = sp.coord[i];\r
188         }\r
189 \r
190         if (sp.coord[i] <= min[i])\r
191         {\r
192           min[i] = sp.coord[i];\r
193         }\r
194       }\r
195     }\r
196 \r
197     //    System.out.println("xmax " + max[0] + " min " + min[0]);\r
198     //System.out.println("ymax " + max[1] + " min " + min[1]);\r
199     //System.out.println("zmax " + max[2] + " min " + min[2]);\r
200     width[0] = Math.abs(max[0] - min[0]);\r
201     width[1] = Math.abs(max[1] - min[1]);\r
202     width[2] = Math.abs(max[2] - min[2]);\r
203 \r
204     maxwidth = width[0];\r
205 \r
206     if (width[1] > width[0])\r
207     {\r
208       maxwidth = width[1];\r
209     }\r
210 \r
211     if (width[2] > width[1])\r
212     {\r
213       maxwidth = width[2];\r
214     }\r
215 \r
216     //System.out.println("Maxwidth = " + maxwidth);\r
217   }\r
218 \r
219   public float findScale()\r
220   {\r
221     int dim;\r
222     int width;\r
223     int height;\r
224 \r
225     if (getWidth() != 0)\r
226     {\r
227       width = getWidth();\r
228       height = getHeight();\r
229     }\r
230     else\r
231     {\r
232       width = prefsize.width;\r
233       height = prefsize.height;\r
234     }\r
235 \r
236     if (width < height)\r
237     {\r
238       dim = width;\r
239     }\r
240     else\r
241     {\r
242       dim = height;\r
243     }\r
244 \r
245     return (float) ( (dim * scalefactor) / (2 * maxwidth));\r
246   }\r
247 \r
248   public void findCentre()\r
249   {\r
250     //Find centre coordinate\r
251     findWidth();\r
252 \r
253     centre[0] = (max[0] + min[0]) / 2;\r
254     centre[1] = (max[1] + min[1]) / 2;\r
255     centre[2] = (max[2] + min[2]) / 2;\r
256 \r
257     //    System.out.println("Centre x " + centre[0]);\r
258     //System.out.println("Centre y " + centre[1]);\r
259     //System.out.println("Centre z " + centre[2]);\r
260   }\r
261 \r
262   public Dimension getPreferredSize()\r
263   {\r
264     if (prefsize != null)\r
265     {\r
266       return prefsize;\r
267     }\r
268     else\r
269     {\r
270       return new Dimension(400, 400);\r
271     }\r
272   }\r
273 \r
274   public Dimension getMinimumSize()\r
275   {\r
276     return getPreferredSize();\r
277   }\r
278 \r
279   public void paintComponent(Graphics g)\r
280   {\r
281     //Only create the image at the beginning -\r
282     if ( (img == null) || (prefsize.width != getWidth()) ||\r
283         (prefsize.height != getHeight()))\r
284     {\r
285       prefsize.width = getWidth();\r
286       prefsize.height = getHeight();\r
287 \r
288       scale = findScale();\r
289 \r
290       //      System.out.println("New scale = " + scale);\r
291       img = createImage(getWidth(), getHeight());\r
292       ig = img.getGraphics();\r
293     }\r
294 \r
295     drawBackground(ig, Color.black);\r
296     drawScene(ig);\r
297 \r
298     if (drawAxes == true)\r
299     {\r
300       drawAxes(ig);\r
301     }\r
302 \r
303     g.drawImage(img, 0, 0, this);\r
304   }\r
305 \r
306   public void drawAxes(Graphics g)\r
307   {\r
308     g.setColor(Color.yellow);\r
309 \r
310     for (int i = 0; i < 3; i++)\r
311     {\r
312       g.drawLine(getWidth() / 2, getHeight() / 2,\r
313                  (int) ( (axes[i][0] * scale * max[0]) + (getWidth() / 2)),\r
314                  (int) ( (axes[i][1] * scale * max[1]) + (getHeight() / 2)));\r
315     }\r
316   }\r
317 \r
318   public void drawBackground(Graphics g, Color col)\r
319   {\r
320     g.setColor(col);\r
321     g.fillRect(0, 0, prefsize.width, prefsize.height);\r
322   }\r
323 \r
324   public void drawScene(Graphics g)\r
325   {\r
326     boolean darker = false;\r
327 \r
328     int halfwidth = getWidth() / 2;\r
329     int halfheight = getHeight() / 2;\r
330 \r
331     for (int i = 0; i < npoint; i++)\r
332     {\r
333       SequencePoint sp = (SequencePoint) points.elementAt(i);\r
334       int x = (int) ( (float) (sp.coord[0] - centre[0]) * scale) +\r
335           halfwidth;\r
336       int y = (int) ( (float) (sp.coord[1] - centre[1]) * scale) +\r
337           halfheight;\r
338       float z = sp.coord[1] - centre[2];\r
339 \r
340       if (sp.sequence.getColor() == Color.black)\r
341       {\r
342         g.setColor(Color.white);\r
343       }\r
344       else\r
345       {\r
346         g.setColor(sp.sequence.getColor());\r
347       }\r
348 \r
349       if (av.getSelectionGroup() != null)\r
350       {\r
351         if (av.getSelectionGroup().sequences.contains(\r
352             ( (SequencePoint) points.elementAt(i)).sequence))\r
353         {\r
354           g.setColor(Color.gray);\r
355         }\r
356       }\r
357 \r
358       if (z < 0)\r
359       {\r
360         g.setColor(g.getColor().darker());\r
361       }\r
362 \r
363       g.fillRect(x - 3, y - 3, 6, 6);\r
364       g.setColor(Color.red);\r
365     }\r
366 \r
367     //    //Now the rectangle\r
368     //    if (rectx2 != -1 && recty2 != -1) {\r
369     //      g.setColor(Color.white);\r
370     //\r
371     //      g.drawRect(rectx1,recty1,rectx2-rectx1,recty2-recty1);\r
372     //    }\r
373   }\r
374 \r
375   public Dimension minimumsize()\r
376   {\r
377     return prefsize;\r
378   }\r
379 \r
380   public Dimension preferredsize()\r
381   {\r
382     return prefsize;\r
383   }\r
384 \r
385   public void keyTyped(KeyEvent evt)\r
386   {\r
387   }\r
388 \r
389   public void keyReleased(KeyEvent evt)\r
390   {\r
391   }\r
392 \r
393   public void keyPressed(KeyEvent evt)\r
394   {\r
395     requestFocus();\r
396 \r
397     if (evt.getKeyCode() == KeyEvent.VK_UP)\r
398     {\r
399       scalefactor = (float) (scalefactor * 1.1);\r
400       scale = findScale();\r
401     }\r
402     else if (evt.getKeyCode() == KeyEvent.VK_DOWN)\r
403     {\r
404       scalefactor = (float) (scalefactor * 0.9);\r
405       scale = findScale();\r
406     }\r
407     else if (evt.getKeyChar() == 's')\r
408     {\r
409       System.err.println("DEBUG: Rectangle selection"); // log.debug\r
410 \r
411       if ( (rectx2 != -1) && (recty2 != -1))\r
412       {\r
413         rectSelect(rectx1, recty1, rectx2, recty2);\r
414       }\r
415     }\r
416 \r
417     repaint();\r
418   }\r
419 \r
420   public void printPoints()\r
421   {\r
422     for (int i = 0; i < npoint; i++)\r
423     {\r
424       SequencePoint sp = (SequencePoint) points.elementAt(i);\r
425       Format.print(System.out, "%5d ", i);\r
426 \r
427       for (int j = 0; j < 3; j++)\r
428       {\r
429         Format.print(System.out, "%13.3f  ", sp.coord[j]);\r
430       }\r
431 \r
432       System.out.println();\r
433     }\r
434   }\r
435 \r
436   public void mouseClicked(MouseEvent evt)\r
437   {\r
438   }\r
439 \r
440   public void mouseEntered(MouseEvent evt)\r
441   {\r
442   }\r
443 \r
444   public void mouseExited(MouseEvent evt)\r
445   {\r
446   }\r
447 \r
448   public void mouseReleased(MouseEvent evt)\r
449   {\r
450   }\r
451 \r
452   public void mousePressed(MouseEvent evt)\r
453   {\r
454     int x = evt.getX();\r
455     int y = evt.getY();\r
456 \r
457     mx = x;\r
458     my = y;\r
459 \r
460     omx = mx;\r
461     omy = my;\r
462 \r
463     startx = x;\r
464     starty = y;\r
465 \r
466     rectx1 = x;\r
467     recty1 = y;\r
468 \r
469     rectx2 = -1;\r
470     recty2 = -1;\r
471 \r
472     SequenceI found = findPoint(x, y);\r
473 \r
474     if (found != null)\r
475     {\r
476       if (av.getSelectionGroup() != null)\r
477       {\r
478         av.getSelectionGroup().addOrRemove(found, true);\r
479         PaintRefresher.Refresh(this, av.alignment);\r
480       }\r
481       else\r
482       {\r
483         av.setSelectionGroup(new SequenceGroup());\r
484         av.getSelectionGroup().addOrRemove(found, true);\r
485         av.getSelectionGroup().setEndRes(av.alignment.getWidth());\r
486       }\r
487     }\r
488 \r
489     repaint();\r
490   }\r
491 \r
492   // private void fireSequenceSelectionEvent(Selection sel) {\r
493   //   controller.handleSequenceSelectionEvent(new SequenceSelectionEvent(this,sel));\r
494   //}\r
495   public void mouseMoved(MouseEvent evt)\r
496   {\r
497     SequenceI found = findPoint(evt.getX(), evt.getY());\r
498 \r
499     if (found != null)\r
500     {\r
501       this.setToolTipText(found.getName());\r
502     }\r
503     else\r
504     {\r
505       this.setToolTipText(null);\r
506     }\r
507   }\r
508 \r
509   public void mouseDragged(MouseEvent evt)\r
510   {\r
511     mx = evt.getX();\r
512     my = evt.getY();\r
513 \r
514     //Check if this is a rectangle drawing drag\r
515     if ( (evt.getModifiers() & InputEvent.BUTTON2_MASK) != 0)\r
516     {\r
517       //      rectx2 = evt.getX();\r
518       //      recty2 = evt.getY();\r
519     }\r
520     else\r
521     {\r
522       rotmat.setIdentity();\r
523 \r
524       rotmat.rotate( (float) (my - omy), 'x');\r
525       rotmat.rotate( (float) (mx - omx), 'y');\r
526 \r
527       for (int i = 0; i < npoint; i++)\r
528       {\r
529         SequencePoint sp = (SequencePoint) points.elementAt(i);\r
530         sp.coord[0] -= centre[0];\r
531         sp.coord[1] -= centre[1];\r
532         sp.coord[2] -= centre[2];\r
533 \r
534         //Now apply the rotation matrix\r
535         sp.coord = rotmat.vectorMultiply(sp.coord);\r
536 \r
537         //Now translate back again\r
538         sp.coord[0] += centre[0];\r
539         sp.coord[1] += centre[1];\r
540         sp.coord[2] += centre[2];\r
541       }\r
542 \r
543       for (int i = 0; i < 3; i++)\r
544       {\r
545         axes[i] = rotmat.vectorMultiply(axes[i]);\r
546       }\r
547 \r
548       omx = mx;\r
549       omy = my;\r
550 \r
551       paint(this.getGraphics());\r
552     }\r
553   }\r
554 \r
555   public void rectSelect(int x1, int y1, int x2, int y2)\r
556   {\r
557     boolean changedSel = false;\r
558 \r
559     for (int i = 0; i < npoint; i++)\r
560     {\r
561       SequencePoint sp = (SequencePoint) points.elementAt(i);\r
562       int tmp1 = (int) ( ( (sp.coord[0] - centre[0]) * scale) +\r
563                         ( (float) getWidth() / 2.0));\r
564       int tmp2 = (int) ( ( (sp.coord[1] - centre[1]) * scale) +\r
565                         ( (float) getHeight() / 2.0));\r
566 \r
567       if ( (tmp1 > x1) && (tmp1 < x2) && (tmp2 > y1) && (tmp2 < y2))\r
568       {\r
569         if (av != null)\r
570         {\r
571           if (!av.getSelectionGroup().sequences.contains(sp.sequence))\r
572           {\r
573             changedSel = true;\r
574             av.getSelectionGroup().addSequence(sp.sequence, true);\r
575           }\r
576         }\r
577       }\r
578     }\r
579 \r
580     // if (changedSel) {\r
581     //    fireSequenceSelectionEvent(av.getSelection());\r
582     // }\r
583   }\r
584 \r
585   public SequenceI findPoint(int x, int y)\r
586   {\r
587     int halfwidth = getWidth() / 2;\r
588     int halfheight = getHeight() / 2;\r
589 \r
590     int found = -1;\r
591 \r
592     for (int i = 0; i < npoint; i++)\r
593     {\r
594       SequencePoint sp = (SequencePoint) points.elementAt(i);\r
595       int px = (int) ( (float) (sp.coord[0] - centre[0]) * scale) +\r
596           halfwidth;\r
597       int py = (int) ( (float) (sp.coord[1] - centre[1]) * scale) +\r
598           halfheight;\r
599 \r
600       if ( (Math.abs(px - x) < 3) && (Math.abs(py - y) < 3))\r
601       {\r
602         found = i;\r
603       }\r
604     }\r
605 \r
606     if (found != -1)\r
607     {\r
608       return ( (SequencePoint) points.elementAt(found)).sequence;\r
609     }\r
610     else\r
611     {\r
612       return null;\r
613     }\r
614   }\r
615 \r
616   /*  public boolean handleRubberbandEvent(RubberbandEvent evt) {\r
617       System.out.println("Rubberband handler called in RotatableCanvas with " +\r
618                          evt.getBounds());\r
619 \r
620       Rubberband rb = (Rubberband)evt.getSource();\r
621 \r
622       // Clear the current selection (instance variable)\r
623       //if ((rb.getModifiers() & Event.SHIFT_MASK) == 0) {\r
624       //   clearSelection();\r
625       //}\r
626 \r
627       if (rb.getComponent() == this) {\r
628         Rectangle bounds = evt.getBounds();\r
629    rectSelect(bounds.x,bounds.y,bounds.x+bounds.width,bounds.y+bounds.height);\r
630       }\r
631 \r
632       redrawneeded = true;\r
633       paint(this.getGraphics());\r
634 \r
635       return true;\r
636     }*/\r
637 }\r