Fast paint method implemented
[jalview.git] / src / jalview / gui / AlignmentPanel.java
1 package jalview.gui;\r
2 \r
3 import jalview.jbgui.GAlignmentPanel;\r
4 import jalview.schemes.*;\r
5 import jalview.analysis.*;\r
6 import jalview.datamodel.*;\r
7 import java.awt.*;\r
8 import java.awt.event.*;\r
9 import java.awt.print.*;\r
10 import java.io.*;\r
11 import java.awt.image.*;\r
12 import org.jibble.epsgraphics.*;\r
13 import javax.imageio.*;\r
14 \r
15 \r
16 \r
17 public class AlignmentPanel extends GAlignmentPanel implements AdjustmentListener, Printable\r
18 {\r
19 \r
20   AlignViewport     av;\r
21   OverviewPanel overviewPanel;\r
22   SeqPanel   seqPanel;\r
23   IdPanel    idPanel;\r
24   IdwidthAdjuster idwidthAdjuster;\r
25   public AlignFrame alignFrame;\r
26   ScalePanel scalePanel;\r
27   AnnotationPanel annotationPanel;\r
28   AnnotationLabels alabels;\r
29 \r
30   public AlignmentPanel(AlignFrame af, final AlignViewport av)\r
31   {\r
32     alignFrame = af;\r
33     this.av         = av;\r
34     seqPanel        = new SeqPanel  (av, this);\r
35     idPanel         = new IdPanel   (av, this);\r
36 \r
37     scalePanel = new ScalePanel(av, this);\r
38 \r
39     idPanelHolder.add(idPanel, BorderLayout.CENTER);\r
40     idwidthAdjuster = new IdwidthAdjuster(this);\r
41     idSpaceFillerPanel1.add(idwidthAdjuster, BorderLayout.CENTER);\r
42 \r
43     annotationPanel = new AnnotationPanel(this);\r
44     alabels = new AnnotationLabels(this);\r
45 \r
46     annotationSpaceFillerHolder.setPreferredSize(annotationPanel.getPreferredSize());\r
47     annotationScroller.setPreferredSize(annotationPanel.getPreferredSize());\r
48     annotationScroller.setViewportView(annotationPanel);\r
49     annotationSpaceFillerHolder.add(alabels, BorderLayout.CENTER);\r
50 \r
51     Dimension d = calculateIdWidth();\r
52     d.setSize( d.width+4, d.height);\r
53     idPanel.idCanvas.setPreferredSize( d );\r
54     hscrollFillerPanel.setPreferredSize( d );\r
55 \r
56     scalePanelHolder.add(scalePanel, BorderLayout.CENTER);\r
57     seqPanelHolder.add(seqPanel, BorderLayout.CENTER);\r
58 \r
59 \r
60     if(seqPanel.seqCanvas.getWidth()==0)\r
61     javax.swing.SwingUtilities.invokeLater(new Runnable()\r
62      {\r
63        public void run()\r
64        {\r
65          setScrollValues(0,0);\r
66          Dimension d = calculateIdWidth();\r
67          d.setSize( d.width+4, d.height);\r
68          idPanel.idCanvas.setPreferredSize( d );\r
69          RefreshPanels();\r
70        }\r
71     });\r
72 \r
73 \r
74     hscroll.addAdjustmentListener(this);\r
75     vscroll.addAdjustmentListener(this);\r
76 \r
77     addComponentListener(new ComponentAdapter()\r
78    {\r
79      public void componentResized(ComponentEvent evt)\r
80      {\r
81           RefreshPanels();\r
82      }\r
83    });\r
84 \r
85    setFocusable(true);\r
86    addKeyListener(new KeyAdapter()\r
87    {\r
88      public void keyPressed(KeyEvent evt)\r
89      {\r
90        switch(evt.getKeyCode())\r
91        {\r
92          case  27: // escape key\r
93            av.setSelectionGroup(null);\r
94            RefreshPanels();\r
95            break;\r
96          case KeyEvent.VK_X:\r
97            if(evt.isControlDown())\r
98            alignFrame.cut_actionPerformed(null);\r
99            break;\r
100          case KeyEvent.VK_C:\r
101          if(evt.isControlDown())\r
102            alignFrame.copy_actionPerformed(null);\r
103            break;\r
104          case KeyEvent.VK_V:\r
105           if(evt.isControlDown())\r
106            alignFrame.paste(true);\r
107            break;\r
108          case KeyEvent.VK_A:\r
109          if(evt.isControlDown())\r
110            alignFrame.selectAllSequenceMenuItem_actionPerformed(null);\r
111            break;\r
112         case KeyEvent.VK_DOWN:\r
113           alignFrame.moveSelectedSequences(false);\r
114           break;\r
115         case KeyEvent.VK_UP:\r
116           alignFrame.moveSelectedSequences(true);\r
117           break;\r
118         case KeyEvent.VK_F:\r
119          if(evt.isControlDown())\r
120           alignFrame.findMenuItem_actionPerformed(null);\r
121           break;\r
122        }\r
123      }\r
124    });\r
125   }\r
126 \r
127   Dimension calculateIdWidth()\r
128   {\r
129     Graphics g = this.getGraphics();\r
130     if(g==null)\r
131     {\r
132       javax.swing.JFrame f = new javax.swing.JFrame();\r
133       f.addNotify();\r
134       g = f.getGraphics();\r
135     }\r
136 \r
137     FontMetrics fm = g.getFontMetrics(av.font);\r
138     AlignmentI al = av.getAlignment();\r
139 \r
140        int i   = 0;\r
141        int idWidth = 0;\r
142        String id;\r
143        while (i < al.getHeight() && al.getSequenceAt(i) != null)\r
144        {\r
145          SequenceI s   = al.getSequenceAt(i);\r
146          if(av.getShowFullId())\r
147            id   = s.getDisplayId();\r
148          else\r
149            id = s.getName();\r
150 \r
151          if (fm.stringWidth(id) > idWidth)\r
152            idWidth = fm.stringWidth(id);\r
153          i++;\r
154        }\r
155 \r
156        return new Dimension(idWidth, 12);\r
157   }\r
158 \r
159 \r
160  public void highlightSearchResults(int [] results)\r
161  {\r
162    seqPanel.seqCanvas.highlightSearchResults( results );\r
163 \r
164    // do we need to scroll the panel?\r
165    if(results!=null && (av.getStartSeq()>results[0]\r
166                         || av.getEndSeq()<results[0]\r
167                         || av.getStartRes()>results[1]\r
168                         || av.getEndRes()<results[2]))\r
169        setScrollValues(results[1], results[0]);\r
170 \r
171 \r
172  }\r
173 \r
174 \r
175  public OverviewPanel getOverviewPanel()\r
176  {\r
177    return overviewPanel;\r
178  }\r
179 \r
180  public void setOverviewPanel(OverviewPanel op)\r
181  {\r
182    overviewPanel = op;\r
183  }\r
184 \r
185 \r
186   public void setAnnotationVisible(boolean b)\r
187   {\r
188     annotationSpaceFillerHolder.setVisible(b);\r
189     annotationScroller.setVisible(b);\r
190     javax.swing.SwingUtilities.invokeLater(new Runnable()\r
191      {\r
192        public void run()\r
193        {\r
194          RefreshPanels();\r
195        }\r
196     });\r
197 \r
198   }\r
199 \r
200 \r
201   public void setWrapAlignment(boolean wrap)\r
202   {\r
203     scalePanelHolder.setVisible(!wrap);\r
204     hscroll.setVisible(!wrap);\r
205     idwidthAdjuster.setVisible(!wrap);\r
206     annotationScroller.setVisible(!wrap);\r
207     annotationSpaceFillerHolder.setVisible(!wrap);\r
208     idSpaceFillerPanel1.setVisible(!wrap);\r
209 \r
210     RefreshPanels();\r
211 \r
212   }\r
213 \r
214 \r
215   public void setColourScheme()\r
216   {\r
217     ColourSchemeI cs = av.getGlobalColourScheme();\r
218 \r
219     if(av.getConservationSelected())\r
220     {\r
221 \r
222        Alignment al = (Alignment)av.getAlignment();\r
223        Conservation c = new Conservation("All",\r
224                             ResidueProperties.propHash, 3, al.getSequences(), 0,\r
225                             al.getWidth() );\r
226 \r
227        c.calculate();\r
228        c.verdict(false, 100);\r
229        ConservationColourScheme ccs = new ConservationColourScheme(c, cs);\r
230 \r
231        av.setGlobalColourScheme( ccs );\r
232 \r
233     }\r
234 \r
235     RefreshPanels();\r
236   }\r
237 \r
238 \r
239   public void RefreshPanels()\r
240   {\r
241     requestFocus();\r
242     invalidate();\r
243 \r
244     Dimension d = idPanel.idCanvas.getPreferredSize();\r
245     idPanelHolder.setPreferredSize(d);\r
246     hscrollFillerPanel.setPreferredSize(new Dimension(d.width, 12));\r
247 \r
248     if (av.getWrapAlignment())\r
249     {\r
250       int max = av.alignment.getWidth() /\r
251           (seqPanel.seqCanvas.getWidth() / av.charWidth) + 1;\r
252       vscroll.setValues(0, 1, 0, max);\r
253     }\r
254     else\r
255     {\r
256       if (overviewPanel != null)\r
257         overviewPanel.updateOverviewImage();\r
258       setScrollValues(av.getStartRes(), av.getStartSeq());\r
259     }\r
260 \r
261     validate();\r
262     repaint();\r
263   }\r
264 \r
265   int hextent = 0;\r
266   int vextent = 0;\r
267 \r
268   // return value is true if the scroll is valid\r
269   public boolean scrollUp(boolean up)\r
270   {\r
271     if(up)\r
272     {\r
273       if(vscroll.getValue()<1)\r
274         return false;\r
275       vscroll.setValue(vscroll.getValue() - 1);\r
276     }\r
277     else\r
278     {\r
279      if(vextent+vscroll.getValue() >= av.getAlignment().getHeight())\r
280        return false;\r
281       vscroll.setValue(vscroll.getValue() + 1);\r
282     }\r
283     return true;\r
284   }\r
285 \r
286   public boolean scrollRight(boolean right)\r
287   {\r
288     if(right)\r
289    {\r
290      if(hscroll.getValue()<1)\r
291        return false;\r
292      hscroll.setValue(hscroll.getValue() - 1);\r
293    }\r
294    else\r
295    {\r
296     if(hextent+hscroll.getValue() >= av.getAlignment().getWidth())\r
297       return false;\r
298      hscroll.setValue(hscroll.getValue() + 1);\r
299    }\r
300 \r
301    return true;\r
302  }\r
303 \r
304 \r
305   public void setScrollValues(int x, int y)\r
306   {\r
307     hextent = seqPanel.seqCanvas.getWidth()/av.charWidth;\r
308     vextent = seqPanel.seqCanvas.getHeight()/av.charHeight;\r
309 \r
310     if(hextent > av.alignment.getWidth())\r
311       hextent = av.alignment.getWidth();\r
312     if(vextent > av.alignment.getHeight())\r
313       vextent = av.alignment.getHeight();\r
314 \r
315     if(hextent+x  >  av.getAlignment().getWidth())\r
316       x =  av.getAlignment().getWidth()- hextent;\r
317 \r
318     if(vextent+y > av.getAlignment().getHeight())\r
319       y = av.getAlignment().getHeight() - vextent;\r
320 \r
321     if(y<0)\r
322       y = 0;\r
323 \r
324     if(x<0)\r
325       x=0;\r
326 \r
327     hscroll.setValues(x,hextent,0,av.getAlignment().getWidth());\r
328     vscroll.setValues(y,vextent,0,av.getAlignment().getHeight() );\r
329 \r
330   }\r
331 \r
332 \r
333   public void adjustmentValueChanged(AdjustmentEvent evt)\r
334   {\r
335     int oldX = av.getStartRes();\r
336     int oldY = av.getStartSeq();\r
337 \r
338     if (evt.getSource() == hscroll)\r
339     {\r
340       int x = hscroll.getValue();\r
341       av.setStartRes(x);\r
342       av.setEndRes(x + seqPanel.seqCanvas.getWidth()/av.getCharWidth()-1);\r
343     }\r
344 \r
345     if (evt.getSource() == vscroll)\r
346     {\r
347       int offy = vscroll.getValue();\r
348       if (av.getWrapAlignment())\r
349       {\r
350         av.setStartRes( vscroll.getValue() * av.getChunkWidth());\r
351       }\r
352       else\r
353       {\r
354         av.setStartSeq(offy);\r
355         av.setEndSeq(offy + seqPanel.seqCanvas.getHeight() / av.getCharHeight());\r
356       }\r
357     }\r
358 \r
359 \r
360     if(overviewPanel!=null)\r
361       overviewPanel.setBoxPosition();\r
362 \r
363     seqPanel.seqCanvas.fastPaint(av.getStartRes()-oldX, av.getStartSeq()-oldY);\r
364     idPanel.idCanvas.fastPaint( av.getStartSeq()-oldY );\r
365     scalePanel.repaint();\r
366     annotationPanel.fastPaint(av.getStartRes()-oldX);\r
367 \r
368   }\r
369 \r
370   public int print(Graphics pg, PageFormat pf, int pi) throws PrinterException\r
371   {\r
372     pg.translate((int)pf.getImageableX(), (int)pf.getImageableY());\r
373 \r
374     int pwidth = (int) pf.getImageableWidth();\r
375     int pheight = (int) pf.getImageableHeight();\r
376 \r
377     if (av.getWrapAlignment())\r
378       return printWrappedAlignment(pg, pwidth,pheight, pi);\r
379     else\r
380       return printUnwrapped(pg,pwidth, pheight,pi);\r
381   }\r
382 \r
383   public int printUnwrapped(Graphics pg, int pwidth, int pheight, int pi) throws PrinterException\r
384   {\r
385 \r
386     int idWidth = calculateIdWidth().width + 4;\r
387 \r
388 \r
389     pg.setColor(Color.white);\r
390     pg.fillRect(0,0,pwidth, pheight);\r
391     pg.setFont( av.getFont() );\r
392 \r
393     ////////////////////////////////////\r
394     /// How many sequences and residues can we fit on a printable page?\r
395     int totalRes = (pwidth - idWidth)/av.getCharWidth();\r
396     int totalSeq = (int)((pheight - 30)/av.getCharHeight())-1;\r
397     int pagesWide = av.getAlignment().getWidth() / totalRes +1;\r
398     int pagesHigh = av.getAlignment().getHeight() / totalSeq +1;\r
399 \r
400     if (pi >= pagesWide*pagesHigh)\r
401      return Printable.NO_SUCH_PAGE;\r
402 \r
403     /////////////////////////////\r
404     /// Only print these sequences and residues on this page\r
405     int startRes, endRes, startSeq, endSeq;\r
406     startRes = (pi % pagesWide) * totalRes;\r
407     endRes = startRes + totalRes-1;\r
408     if(endRes>av.getAlignment().getWidth())\r
409       endRes = av.getAlignment().getWidth();\r
410 \r
411      startSeq = (pi / pagesWide) * totalSeq;\r
412      endSeq = startSeq + totalSeq;\r
413      if(endSeq > av.getAlignment().getHeight())\r
414        endSeq = av.getAlignment().getHeight();\r
415 \r
416 \r
417     ////////////////\r
418     //draw Scale\r
419     pg.translate(idWidth,0);\r
420     scalePanel.drawScale(pg, startRes, endRes, pwidth-idWidth);\r
421 \r
422     pg.translate(-idWidth, 30);\r
423     ////////////////\r
424     // Draw the ids\r
425     Color currentColor=null;\r
426     Color currentTextColor=null;\r
427     for(int i=startSeq; i<endSeq; i++)\r
428     {\r
429       if (av.getSelectionGroup()!=null && av.getSelectionGroup().sequences.contains(av.getAlignment().getSequenceAt(i)))\r
430       {\r
431         currentColor = Color.gray;\r
432         currentTextColor = Color.black;\r
433       }\r
434       else\r
435       {\r
436         currentColor = av.getAlignment().getSequenceAt(i).getColor();\r
437         currentTextColor = Color.black;\r
438       }\r
439 \r
440       pg.setColor(currentColor);\r
441       pg.fillRect(0,  jalview.analysis.AlignmentUtil.getPixelHeight(startSeq, i, av.getCharHeight()),\r
442                               idWidth,\r
443                               av.getCharHeight());\r
444 \r
445       pg.setColor(currentTextColor);\r
446 \r
447       String string = av.getAlignment().getSequenceAt(i).getName();\r
448       if(av.getShowFullId())\r
449         string = av.getAlignment().getSequenceAt(i).getDisplayId();\r
450 \r
451       pg.drawString(string, 0,  jalview.analysis.AlignmentUtil.getPixelHeight\r
452                     (startSeq, i, av.getCharHeight()) + av.getCharHeight() - (av.getCharHeight() / 5));\r
453     }\r
454 \r
455     // draw main sequence panel\r
456     pg.translate(idWidth,0);\r
457     pg.setClip(0,0,pwidth-idWidth, pheight);\r
458     seqPanel.seqCanvas.drawPanel(pg,startRes,endRes,startSeq,endSeq,startRes,startSeq,0);\r
459 \r
460     return Printable.PAGE_EXISTS;\r
461   }\r
462 \r
463 \r
464   public int printWrappedAlignment(Graphics pg, int pwidth, int pheight, int pi) throws PrinterException\r
465   {\r
466 \r
467     int idWidth = calculateIdWidth().width+4;\r
468 \r
469     if( seqPanel.seqCanvas.getWidth() < pwidth-idWidth)\r
470       pwidth = seqPanel.seqCanvas.getWidth() + idWidth;\r
471 \r
472 \r
473     pg.setColor(Color.white);\r
474     pg.fillRect(0,0,pwidth, pheight);\r
475     pg.setFont( av.getFont() );\r
476 \r
477     ////////////////////////////////////\r
478     /// How many sequences and residues can we fit on a printable page?\r
479     AlignmentI da = av.alignment;\r
480     int endy   = da.getHeight();\r
481     int chunkHeight =  (da.getHeight() + 2)*av.charHeight;\r
482     int chunkWidth  =   (pwidth-idWidth)/av.charWidth;\r
483 \r
484     int noChunksOnPage = pheight / chunkHeight;\r
485     int totalChunks = da.getWidth() / chunkWidth;\r
486 \r
487     if ( pi*noChunksOnPage > totalChunks )\r
488      return Printable.NO_SUCH_PAGE;\r
489 \r
490     ////////////////\r
491     // Draw the ids\r
492     pg.setClip(0,0,pwidth, noChunksOnPage*chunkHeight);\r
493 \r
494     int row = pi*noChunksOnPage;\r
495     pg.setColor(Color.black);\r
496     for(int ypos=2*av.charHeight;\r
497         ypos <= pheight && row*chunkWidth<da.getWidth();\r
498         ypos += chunkHeight, row++ )\r
499     {\r
500       for (int i = 0; i < endy; i++)\r
501       {\r
502         SequenceI s = da.getSequenceAt(i);\r
503         String string = s.getName();\r
504         if (av.getShowFullId())\r
505           string = s.getDisplayId();\r
506 \r
507         pg.drawString(string, 0,\r
508                       AlignmentUtil.getPixelHeight(0, i, av.charHeight) + ypos +\r
509                       av.charHeight - (av.charHeight / 5));\r
510       }\r
511     }\r
512 \r
513     // draw main sequence panel\r
514     pg.translate(idWidth,0);\r
515     seqPanel.seqCanvas.drawWrappedPanel(pg, pwidth-idWidth, pheight, pi*noChunksOnPage*chunkWidth);\r
516 \r
517 \r
518     return Printable.PAGE_EXISTS;\r
519 \r
520   }\r
521 \r
522 \r
523   public void makeEPS()\r
524   {\r
525     int height = (av.alignment.getWidth() / av.getChunkWidth() +1) * av.chunkHeight;\r
526     int width = seqPanel.getWidth() + idPanel.getWidth();\r
527 \r
528     if (!av.getWrapAlignment())\r
529     {\r
530       height = (av.alignment.getHeight()+1) * av.charHeight + 30;\r
531       width = idPanel.getWidth() + av.alignment.getWidth() * av.charWidth;\r
532     }\r
533 \r
534     try\r
535     {\r
536       jalview.io.JalviewFileChooser chooser = new jalview.io.JalviewFileChooser(jalview.bin.Cache.getProperty(\r
537           "LAST_DIRECTORY"), new String[]{"eps"}, "Encapsulated Postscript");\r
538       chooser.setFileView(new jalview.io.JalviewFileView());\r
539       chooser.setDialogTitle("Create EPS file from alignment");\r
540       chooser.setToolTipText("Save");\r
541 \r
542       int value = chooser.showSaveDialog(this);\r
543       if (value != jalview.io.JalviewFileChooser.APPROVE_OPTION)\r
544         return;\r
545 \r
546       jalview.bin.Cache.setProperty("LAST_DIRECTORY",chooser.getSelectedFile().getPath());\r
547       FileOutputStream out = new FileOutputStream(chooser.getSelectedFile());\r
548       EpsGraphics2D pg = new EpsGraphics2D("Example", out, 0, 0, width, height);\r
549 \r
550         if (av.getWrapAlignment())\r
551           printWrappedAlignment(pg, width, height, 0);\r
552         else\r
553           printUnwrapped(pg, width, height, 0);\r
554 \r
555 \r
556         pg.flush();\r
557         pg.close();\r
558     }\r
559     catch (Exception ex)\r
560     {\r
561       ex.printStackTrace();\r
562     }\r
563   }\r
564 \r
565   public void makePNG()\r
566   {\r
567       int height = (av.alignment.getWidth() / av.getChunkWidth() +1) * av.chunkHeight;\r
568       int width = seqPanel.getWidth() + idPanel.getWidth();\r
569 \r
570       if (!av.getWrapAlignment())\r
571       {\r
572         height = (av.alignment.getHeight()+1) * av.charHeight + 30;\r
573         width = idPanel.getWidth() + av.alignment.getWidth() * av.charWidth;\r
574       }\r
575 \r
576 \r
577 \r
578     try\r
579     {\r
580       jalview.io.JalviewFileChooser chooser = new jalview.io.JalviewFileChooser(jalview.bin.Cache.getProperty(\r
581           "LAST_DIRECTORY"), new String[]{"png"}, "Portable network graphics");\r
582       chooser.setFileView(new jalview.io.JalviewFileView());\r
583       chooser.setDialogTitle("Create EPS file from alignment");\r
584       chooser.setToolTipText("Save");\r
585 \r
586       int value = chooser.showSaveDialog(this);\r
587       if (value != jalview.io.JalviewFileChooser.APPROVE_OPTION)\r
588         return;\r
589 \r
590       jalview.bin.Cache.setProperty("LAST_DIRECTORY",chooser.getSelectedFile().getPath());\r
591       FileOutputStream out = new FileOutputStream(chooser.getSelectedFile());\r
592 \r
593       BufferedImage bi = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);\r
594       Graphics png = bi.getGraphics();\r
595 \r
596 \r
597         if (av.getWrapAlignment())\r
598           printWrappedAlignment(png, width, height, 0);\r
599         else\r
600           printUnwrapped(png, width, height, 0);\r
601 \r
602         ImageIO.write(bi, "png", out);\r
603         out.close();\r
604     }\r
605     catch (Exception ex)\r
606     {\r
607       ex.printStackTrace();\r
608     }\r
609   }\r
610 \r
611 }\r
612 \r
613 \r
614 \r
615 \r