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