GPL license added
[jalview.git] / src / jalview / appletgui / SeqCanvas.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.awt.*;\r
23 import jalview.datamodel.*;\r
24 import jalview.analysis.*;\r
25 \r
26 \r
27 public class SeqCanvas extends Panel\r
28 {\r
29     FeatureRenderer fr;\r
30     SequenceRenderer sr;\r
31     Image             img;\r
32     Graphics          gg;\r
33     int               imgWidth;\r
34     int               imgHeight;\r
35 \r
36     AlignViewport     av;\r
37 \r
38     boolean showScores = false;\r
39     boolean displaySearch = false;\r
40     int [] searchResults = null;\r
41 \r
42     int chunkHeight;\r
43     int chunkWidth;\r
44 \r
45     boolean fastPaint = false;\r
46 \r
47 \r
48     public SeqCanvas(AlignViewport av)\r
49     {\r
50         this.av         = av;\r
51        fr = new FeatureRenderer(av);\r
52        sr = new SequenceRenderer(av);\r
53        PaintRefresher.Register(this);\r
54 \r
55     }\r
56 \r
57   void drawNorthScale(Graphics g, int startx, int endx,int ypos) {\r
58     int scalestartx = startx - startx % 10 + 10;\r
59 \r
60     g.setColor(Color.black);\r
61 \r
62     // NORTH SCALE\r
63     for (int i = scalestartx; i < endx; i += 10)\r
64     {\r
65       String string = String.valueOf(i);\r
66       g.drawString(string, (i - startx - 1) * av.charWidth,\r
67                    ypos - av.charHeight / 2);\r
68 \r
69       g.drawLine( (i - startx - 1) * av.charWidth + av.charWidth / 2,\r
70                  ypos + 2 - av.charHeight / 2,\r
71                  (i - startx - 1) * av.charWidth + av.charWidth / 2, ypos - 2);\r
72 \r
73     }\r
74   }\r
75 \r
76   void drawWestScale(Graphics g, int startx, int endx, int ypos)\r
77   {\r
78     FontMetrics fm = getFontMetrics(av.getFont());\r
79     ypos+= av.charHeight;\r
80       // EAST SCALE\r
81     for (int i = 0; i < av.alignment.getHeight(); i++)\r
82     {\r
83       SequenceI seq = av.alignment.getSequenceAt(i);\r
84       int index = startx;\r
85       int value = -1;\r
86       while (index < endx)\r
87       {\r
88         if (jalview.util.Comparison.isGap(seq.getCharAt(index)))\r
89         {\r
90           index++;\r
91           continue;\r
92         }\r
93 \r
94         value = av.alignment.getSequenceAt(i).findPosition(index);\r
95         break;\r
96       }\r
97       if(value!=-1)\r
98       {\r
99         int x = LABEL_WEST - fm.stringWidth(value+"");\r
100         g.drawString(value + "", x,  ypos +  i*av.charHeight - av.charHeight/5);\r
101       }\r
102     }\r
103   }\r
104 \r
105   void drawEastScale(Graphics g, int startx, int endx, int ypos)\r
106 {\r
107     ypos+= av.charHeight;\r
108     // EAST SCALE\r
109   for (int i = 0; i < av.alignment.getHeight(); i++)\r
110   {\r
111     SequenceI seq = av.alignment.getSequenceAt(i);\r
112     int index = endx;\r
113     int value = -1;\r
114     while (index > startx)\r
115     {\r
116       if (jalview.util.Comparison.isGap(seq.getCharAt(index)))\r
117       {\r
118         index--;\r
119         continue;\r
120       }\r
121 \r
122       value = av.alignment.getSequenceAt(i).findPosition(index);\r
123       break;\r
124     }\r
125     if(value!=-1)\r
126        g.drawString(value + "", 0,  ypos +  i*av.charHeight - av.charHeight/5);\r
127   }\r
128 \r
129 }\r
130 \r
131 \r
132 \r
133 public void fastPaint(int horizontal, int vertical)\r
134 {\r
135     if (horizontal == 0 && vertical == 0 || gg==null)\r
136       return;\r
137 \r
138     gg.copyArea(0, 0, imgWidth, imgHeight, -horizontal * av.charWidth,\r
139                 -vertical * av.charHeight);\r
140 \r
141     int sr = av.startRes, er = av.endRes, ss = av.startSeq, es = av.endSeq,\r
142         transX = 0, transY = 0;\r
143     if (horizontal > 0) // scrollbar pulled right, image to the left\r
144     {\r
145       transX = (er - sr - horizontal) * av.charWidth;\r
146       sr = er - horizontal;\r
147     }\r
148     else if (horizontal < 0)\r
149       er = sr - horizontal;\r
150 \r
151     else if (vertical > 0) // scroll down\r
152     {\r
153       ss = es - vertical;\r
154       if(ss<av.startSeq) // ie scrolling too fast, more than a page at a time\r
155         ss = av.startSeq;\r
156       else\r
157         transY = imgHeight - vertical * av.charHeight;\r
158     }\r
159     else if (vertical < 0)\r
160     {\r
161       es = ss - vertical;\r
162       if(es > av.endSeq)\r
163         es = av.endSeq;\r
164     }\r
165 \r
166 \r
167     gg.translate(transX, transY);\r
168 \r
169     gg.setColor(Color.white);\r
170     gg.fillRect(0,0, (er-sr+1)*av.charWidth, (es-ss)*av.charHeight);\r
171     drawPanel(gg, sr, er, ss, es, sr, ss, 0);\r
172     gg.translate( -transX, -transY);\r
173 \r
174     fastPaint = true;\r
175     repaint();\r
176 \r
177 }\r
178 \r
179 /**\r
180  * Definitions of startx and endx (hopefully):\r
181  * SMJS This is what I'm working towards!\r
182  *   startx is the first residue (starting at 0) to display.\r
183  *   endx   is the last residue to display (starting at 0).\r
184  *   starty is the first sequence to display (starting at 0).\r
185  *   endy   is the last sequence to display (starting at 0).\r
186  * NOTE 1: The av limits are set in setFont in this class and\r
187  * in the adjustment listener in SeqPanel when the scrollbars move.\r
188  */\r
189   public void update(Graphics g)\r
190   {\r
191     paint(g);\r
192   }\r
193 \r
194   public void paint(Graphics g)\r
195   {\r
196     if (fastPaint)\r
197     {\r
198       g.drawImage(img, 0, 0, this);\r
199       fastPaint = false;\r
200       return;\r
201     }\r
202 \r
203     // this draws the whole of the alignment\r
204       imgWidth  = this.getSize().width;\r
205       imgHeight = this.getSize().height;\r
206 \r
207       imgWidth -= imgWidth%av.charWidth;\r
208       imgHeight-= imgHeight%av.charHeight;\r
209 \r
210       if(imgWidth<1 || imgHeight<1)\r
211               return;\r
212 \r
213       if(img==null || imgWidth!=img.getWidth(this) || imgHeight!=img.getHeight(this))\r
214       {\r
215         img = createImage(imgWidth, imgHeight);\r
216         gg = img.getGraphics();\r
217         gg.setFont(av.getFont());\r
218       }\r
219 \r
220       gg.setColor(Color.white);\r
221       gg.fillRect(0,0,imgWidth,imgHeight);\r
222 \r
223     chunkWidth  =   getWrappedCanvasWidth( getSize().width );\r
224     chunkHeight =  (av.getAlignment().getHeight() + 2)*av.charHeight;\r
225 \r
226     av.setChunkHeight(chunkHeight);\r
227     av.setChunkWidth(chunkWidth);\r
228 \r
229 \r
230     if (av.getWrapAlignment())\r
231       drawWrappedPanel(gg, getSize().width, getSize().height, av.startRes);\r
232     else\r
233       drawPanel(gg, av.startRes, av.endRes, av.startSeq, av.endSeq, av.startRes, av.startSeq, 0);\r
234 \r
235     g.drawImage(img, 0, 0, this);\r
236 \r
237   }\r
238 \r
239   int LABEL_WEST, LABEL_EAST;\r
240   public int getWrappedCanvasWidth(int cwidth)\r
241   {\r
242     FontMetrics fm = getFontMetrics(av.getFont());\r
243 \r
244     LABEL_EAST = 0;\r
245     LABEL_WEST = 0;\r
246 \r
247     if(av.scaleRightWrapped)\r
248       LABEL_EAST = fm.stringWidth( av.alignment.getWidth()+"000" );\r
249 \r
250     if(av.scaleLeftWrapped)\r
251       LABEL_WEST = fm.stringWidth( av.alignment.getWidth()+"" );\r
252 \r
253     return  (cwidth - LABEL_EAST -LABEL_WEST)/av.charWidth;\r
254   }\r
255 \r
256   public void drawWrappedPanel(Graphics g, int canvasWidth, int canvasHeight, int startRes)\r
257   {\r
258       AlignmentI al = av.getAlignment();\r
259 \r
260       FontMetrics fm = getFontMetrics(av.getFont());\r
261 \r
262       int LABEL_EAST = 0;\r
263       if(av.scaleRightWrapped)\r
264         LABEL_EAST = fm.stringWidth( al.getWidth()+"000" );\r
265       int LABEL_WEST = 0;\r
266       if(av.scaleLeftWrapped)\r
267         LABEL_WEST = fm.stringWidth(al.getWidth()+"0");\r
268 \r
269 \r
270       int cWidth  =   (canvasWidth - LABEL_EAST -LABEL_WEST)/av.charWidth;\r
271       int cHeight =  (av.getAlignment().getHeight() + 2)*av.charHeight;\r
272 \r
273       av.endRes = av.startRes + cWidth;\r
274 \r
275       int  endx   = startRes+cWidth-1;\r
276       int  ypos  = 2*av.charHeight;\r
277 \r
278       while (ypos <= canvasHeight && startRes<av.alignment.getWidth() )\r
279       {\r
280         g.setColor(Color.black);\r
281 \r
282         if(av.scaleLeftWrapped)\r
283           drawWestScale(g, startRes, endx, ypos);\r
284 \r
285         if(av.scaleRightWrapped)\r
286         {\r
287           g.translate(canvasWidth - LABEL_EAST +av.charWidth, 0);\r
288           drawEastScale(g, startRes, endx, ypos);\r
289           g.translate( - (canvasWidth - LABEL_EAST+av.charWidth), 0);\r
290         }\r
291 \r
292         g.translate(LABEL_WEST,0);\r
293         if(av.scaleAboveWrapped)\r
294           drawNorthScale(g, startRes, endx, ypos);\r
295 \r
296 \r
297         // When printing we have an extra clipped region,\r
298         // the Printable page which we need to account for here\r
299         Shape clip = g.getClip();\r
300         if(clip==null)\r
301           g.setClip(0, 0, cWidth*av.charWidth, canvasHeight);\r
302         else\r
303           g.setClip(0,\r
304                     (int)clip.getBounds().y,\r
305                     cWidth*av.charWidth,\r
306                     (int)clip.getBounds().height\r
307                     );\r
308 \r
309         drawPanel(g, startRes, endx, 0, al.getHeight(), startRes, 0, ypos);\r
310         g.setClip(clip);\r
311         g.translate(-LABEL_WEST,0);\r
312 \r
313         ypos += cHeight;\r
314         startRes += cWidth;\r
315         endx = startRes + cWidth - 1;\r
316 \r
317         if (endx > al.getWidth())\r
318           endx = al.getWidth();\r
319       }\r
320 \r
321   }\r
322 \r
323 \r
324   synchronized public void drawPanel(Graphics g,int x1,int x2, int y1, int y2,int startx, int starty,int offset) {\r
325 \r
326     g.setFont(av.getFont());\r
327     sr.renderGaps(av.renderGaps);\r
328 \r
329     SequenceI nextSeq;\r
330 \r
331     /// First draw the sequences\r
332     /////////////////////////////\r
333     for (int i = y1 ; i < y2 ;i++)\r
334     {\r
335      nextSeq = av.alignment.getSequenceAt(i);\r
336 \r
337      sr.drawSequence(g, nextSeq, av.alignment.findAllGroups( nextSeq ),x1,x2,\r
338                  (x1 - startx) * av.charWidth,\r
339                  offset + AlignmentUtil.getPixelHeight(starty, i, av.charHeight),\r
340                  av.charWidth,av.charHeight);\r
341 \r
342      if(av.showSequenceFeatures)\r
343      {\r
344        fr.drawSequence(g, nextSeq, av.alignment.findAllGroups( nextSeq ), x1, x2,\r
345                        (x1 - startx) * av.charWidth,\r
346                        offset +\r
347                        AlignmentUtil.getPixelHeight(starty, i, av.charHeight),\r
348                        av.charWidth, av.charHeight);\r
349      }\r
350     }\r
351     //\r
352     /////////////////////////////////////\r
353 \r
354     // Now outline any areas if necessary\r
355     /////////////////////////////////////\r
356     SequenceGroup group = av.getSelectionGroup();\r
357     java.util.Vector groups = av.alignment.getGroups();\r
358 \r
359     int sx = -1, sy = -1, ex = -1;\r
360     int groupIndex = -1;\r
361     if (group == null && groups.size() > 0)\r
362     {\r
363       group = (SequenceGroup) groups.elementAt(0);\r
364       groupIndex = 0;\r
365     }\r
366 \r
367     if (group != null)\r
368       do\r
369       {\r
370         int oldY = -1;\r
371         int i = 0;\r
372         boolean inGroup = false;\r
373         int top=-1, bottom =-1;\r
374         for (i = y1; i < y2; i++)\r
375         {\r
376           sx = (group.getStartRes() - startx) * av.charWidth;\r
377           sy = offset + AlignmentUtil.getPixelHeight(starty, i, av.charHeight);\r
378           ex = (group.getEndRes() + 1 - group.getStartRes()) * av.charWidth -1;\r
379 \r
380           if (sx < getSize().width\r
381               && ex > 0\r
382               && group.sequences.contains(av.alignment.getSequenceAt(i)))\r
383           {\r
384             if (bottom == -1 && (i==av.alignment.getHeight()-1 ||\r
385                 !group.sequences.contains(av.alignment.getSequenceAt(i + 1))))\r
386               bottom = sy + av.charHeight ;\r
387 \r
388             if (!inGroup)\r
389             {\r
390               if (top == -1 && i==0 ||\r
391                   !group.sequences.contains(av.alignment.getSequenceAt(i - 1)))\r
392                 top = sy;\r
393 \r
394 \r
395               oldY = sy;\r
396               inGroup = true;\r
397               if (group == av.getSelectionGroup())\r
398               {\r
399                     g.setColor(new Color(255,0,0));\r
400               }\r
401               else\r
402               {\r
403                     g.setColor(group.getOutlineColour());\r
404               }\r
405             }\r
406           }\r
407           else\r
408           {\r
409             if (inGroup)\r
410             {\r
411               g.drawLine(sx, oldY, sx, sy );\r
412               g.drawLine(sx+ex, oldY, sx+ex, sy );\r
413 \r
414               if (top != -1)\r
415               {\r
416                 g.drawLine(sx, top, sx + ex, top);\r
417                 top =-1;\r
418               }\r
419               if (bottom != -1)\r
420               {\r
421                 g.drawLine(sx, bottom, sx + ex, bottom);\r
422                 bottom = -1;\r
423               }\r
424 \r
425 \r
426               inGroup = false;\r
427             }\r
428           }\r
429         }\r
430 \r
431         if (inGroup)\r
432         {\r
433 \r
434           if(top!=-1)\r
435           {\r
436             g.drawLine(sx, top, sx + ex, top);\r
437             top =-1;\r
438           }\r
439           if(bottom!=-1)\r
440            {\r
441              g.drawLine(sx, bottom-1, sx + ex, bottom-1);\r
442              bottom = -1;\r
443 \r
444            }\r
445           sy = offset + AlignmentUtil.getPixelHeight(starty, i, av.charHeight);\r
446           g.drawLine(sx, oldY, sx, sy );\r
447           g.drawLine(sx+ex, oldY, sx+ex, sy );\r
448           inGroup = false;\r
449         }\r
450         groupIndex++;\r
451         if (groupIndex >= groups.size())\r
452           break;\r
453 \r
454         group = (SequenceGroup) groups.elementAt(groupIndex);\r
455 \r
456       }\r
457       while (groupIndex < groups.size());\r
458 \r
459 \r
460     /// Highlight search Results once all sequences have been drawn\r
461     //////////////////////////////////////////////////////////\r
462     if(displaySearch)\r
463     {\r
464       for(int r=0; r<searchResults.length; r+=3)\r
465       {\r
466         int searchSeq = searchResults[r];\r
467 \r
468         if (searchSeq >= y1 && searchSeq < y2)\r
469         {\r
470           SequenceI seq = av.getAlignment().getSequenceAt(searchSeq);\r
471 \r
472           int searchStart = seq.findIndex( searchResults[r+1] )-1;\r
473           int searchEnd =  seq.findIndex(  searchResults[r+2] )-1;\r
474 \r
475           SequenceRenderer ssr = (SequenceRenderer) sr;\r
476           if(searchStart<x1)\r
477             searchStart = x1;\r
478           if(searchEnd > x2)\r
479             searchEnd = x2;\r
480 \r
481           ssr.drawHighlightedText(seq,\r
482                                   searchStart,\r
483                                   searchEnd,\r
484                                   (searchStart - startx) * av.charWidth,\r
485                                   offset +\r
486                                   AlignmentUtil.getPixelHeight(starty, searchSeq,\r
487               av.charHeight),\r
488                                   av.charWidth,\r
489                                   av.charHeight);\r
490         }\r
491       }\r
492     }\r
493 \r
494   }\r
495 \r
496 \r
497 \r
498   public void highlightSearchResults(int [] results)\r
499   {\r
500     // results are in the order sequence, startRes, endRes\r
501     if(results==null)\r
502       displaySearch = false;\r
503     else\r
504       displaySearch = true;\r
505 \r
506     searchResults = results;\r
507 \r
508     repaint();\r
509   }\r
510 \r
511 \r
512 }\r