Class |
Line # |
Actions |
|||
---|---|---|---|---|---|
IdCanvas | 35 | 154 | 58 |
1 | /* | |
2 | * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$) | |
3 | * Copyright (C) $$Year-Rel$$ The Jalview Authors | |
4 | * | |
5 | * This file is part of Jalview. | |
6 | * | |
7 | * Jalview is free software: you can redistribute it and/or | |
8 | * modify it under the terms of the GNU General Public License | |
9 | * as published by the Free Software Foundation, either version 3 | |
10 | * of the License, or (at your option) any later version. | |
11 | * | |
12 | * Jalview is distributed in the hope that it will be useful, but | |
13 | * WITHOUT ANY WARRANTY; without even the implied warranty | |
14 | * of MERCHANTABILITY or FITNESS FOR A PARTICULAR | |
15 | * PURPOSE. See the GNU General Public License for more details. | |
16 | * | |
17 | * You should have received a copy of the GNU General Public License | |
18 | * along with Jalview. If not, see <http://www.gnu.org/licenses/>. | |
19 | * The Jalview Authors are detailed in the 'AUTHORS' file. | |
20 | */ | |
21 | package jalview.appletgui; | |
22 | ||
23 | import jalview.datamodel.SequenceI; | |
24 | import jalview.viewmodel.ViewportListenerI; | |
25 | import jalview.viewmodel.ViewportRanges; | |
26 | ||
27 | import java.awt.Color; | |
28 | import java.awt.Font; | |
29 | import java.awt.Graphics; | |
30 | import java.awt.Image; | |
31 | import java.awt.Panel; | |
32 | import java.beans.PropertyChangeEvent; | |
33 | import java.util.List; | |
34 | ||
35 | public class IdCanvas extends Panel implements ViewportListenerI | |
36 | { | |
37 | protected AlignViewport av; | |
38 | ||
39 | protected boolean showScores = true; | |
40 | ||
41 | protected int maxIdLength = -1; | |
42 | ||
43 | protected String maxIdStr = null; | |
44 | ||
45 | Image image; | |
46 | ||
47 | Graphics gg; | |
48 | ||
49 | int imgHeight = 0; | |
50 | ||
51 | boolean fastPaint = false; | |
52 | ||
53 | List<SequenceI> searchResults; | |
54 | ||
55 | 0 | public IdCanvas(AlignViewport av) |
56 | { | |
57 | 0 | setLayout(null); |
58 | 0 | this.av = av; |
59 | 0 | PaintRefresher.Register(this, av.getSequenceSetId()); |
60 | 0 | av.getRanges().addPropertyChangeListener(this); |
61 | } | |
62 | ||
63 | 0 | public void drawIdString(Graphics gg, boolean hiddenRows, SequenceI s, |
64 | int i, int starty, int ypos) | |
65 | { | |
66 | 0 | int charHeight = av.getCharHeight(); |
67 | ||
68 | 0 | if (searchResults != null && searchResults.contains(s)) |
69 | { | |
70 | 0 | gg.setColor(Color.black); |
71 | 0 | gg.fillRect(0, ((i - starty) * charHeight) + ypos, getSize().width, |
72 | charHeight); | |
73 | 0 | gg.setColor(Color.white); |
74 | } | |
75 | 0 | else if (av.getSelectionGroup() != null |
76 | && av.getSelectionGroup().getSequences(null).contains(s)) | |
77 | { | |
78 | 0 | gg.setColor(Color.lightGray); |
79 | 0 | gg.fillRect(0, ((i - starty) * charHeight) + ypos, getSize().width, |
80 | charHeight); | |
81 | 0 | gg.setColor(Color.white); |
82 | } | |
83 | else | |
84 | { | |
85 | 0 | gg.setColor(av.getSequenceColour(s)); |
86 | 0 | gg.fillRect(0, ((i - starty) * charHeight) + ypos, getSize().width, |
87 | charHeight); | |
88 | 0 | gg.setColor(Color.black); |
89 | } | |
90 | ||
91 | 0 | gg.drawString(s.getDisplayId(av.getShowJVSuffix()), 0, |
92 | ((i - starty) * charHeight) + ypos + charHeight | |
93 | - (charHeight / 5)); | |
94 | ||
95 | 0 | if (hiddenRows) |
96 | { | |
97 | 0 | drawMarker(i, starty, ypos); |
98 | } | |
99 | ||
100 | } | |
101 | ||
102 | 0 | public void fastPaint(int vertical) |
103 | { | |
104 | 0 | if (gg == null || av.getWrapAlignment()) |
105 | { | |
106 | 0 | repaint(); |
107 | 0 | return; |
108 | } | |
109 | ||
110 | 0 | ViewportRanges ranges = av.getRanges(); |
111 | ||
112 | 0 | gg.copyArea(0, 0, getSize().width, imgHeight, 0, |
113 | -vertical * av.getCharHeight()); | |
114 | ||
115 | 0 | int ss = ranges.getStartSeq(), es = ranges.getEndSeq(), transY = 0; |
116 | 0 | if (vertical > 0) // scroll down |
117 | { | |
118 | 0 | ss = es - vertical; |
119 | 0 | if (ss < ranges.getStartSeq()) // ie scrolling too fast, more than a page |
120 | // at a | |
121 | // time | |
122 | { | |
123 | 0 | ss = ranges.getStartSeq(); |
124 | } | |
125 | else | |
126 | { | |
127 | 0 | transY = imgHeight - ((vertical + 1) * av.getCharHeight()); |
128 | } | |
129 | } | |
130 | 0 | else if (vertical < 0) |
131 | { | |
132 | 0 | es = ss - vertical; |
133 | 0 | if (es > ranges.getEndSeq()) |
134 | { | |
135 | 0 | es = ranges.getEndSeq(); |
136 | } | |
137 | } | |
138 | ||
139 | 0 | gg.translate(0, transY); |
140 | ||
141 | 0 | drawIds(ss, es); |
142 | ||
143 | 0 | gg.translate(0, -transY); |
144 | ||
145 | 0 | fastPaint = true; |
146 | 0 | repaint(); |
147 | } | |
148 | ||
149 | 0 | @Override |
150 | public void update(Graphics g) | |
151 | { | |
152 | 0 | paint(g); |
153 | } | |
154 | ||
155 | 0 | @Override |
156 | public void paint(Graphics g) | |
157 | { | |
158 | 0 | if (getSize().height < 0 || getSize().width < 0) |
159 | { | |
160 | 0 | return; |
161 | } | |
162 | 0 | if (fastPaint) |
163 | { | |
164 | 0 | fastPaint = false; |
165 | 0 | g.drawImage(image, 0, 0, this); |
166 | 0 | return; |
167 | } | |
168 | ||
169 | 0 | imgHeight = getSize().height; |
170 | 0 | imgHeight -= imgHeight % av.getCharHeight(); |
171 | ||
172 | 0 | if (imgHeight < 1) |
173 | { | |
174 | 0 | return; |
175 | } | |
176 | ||
177 | 0 | if (image == null || imgHeight != image.getHeight(this)) |
178 | { | |
179 | 0 | image = createImage(getSize().width, imgHeight); |
180 | 0 | gg = image.getGraphics(); |
181 | 0 | gg.setFont(av.getFont()); |
182 | } | |
183 | ||
184 | // Fill in the background | |
185 | 0 | gg.setColor(Color.white); |
186 | 0 | Font italic = new Font(av.getFont().getName(), Font.ITALIC, |
187 | av.getFont().getSize()); | |
188 | 0 | gg.setFont(italic); |
189 | ||
190 | 0 | gg.fillRect(0, 0, getSize().width, getSize().height); |
191 | 0 | drawIds(av.getRanges().getStartSeq(), av.getRanges().getEndSeq()); |
192 | 0 | g.drawImage(image, 0, 0, this); |
193 | } | |
194 | ||
195 | /** | |
196 | * local copy of av.getCharHeight set at top of drawIds | |
197 | */ | |
198 | private int avcharHeight; | |
199 | ||
200 | 0 | void drawIds(int starty, int endy) |
201 | { | |
202 | 0 | avcharHeight = av.getCharHeight(); |
203 | ||
204 | 0 | Color currentColor = Color.white; |
205 | 0 | Color currentTextColor = Color.black; |
206 | ||
207 | 0 | final boolean doHiddenCheck = av.isDisplayReferenceSeq() |
208 | || av.hasHiddenRows(); | |
209 | 0 | boolean hiddenRows = av.hasHiddenRows() && av.getShowHiddenMarkers(); |
210 | ||
211 | 0 | if (av.getWrapAlignment()) |
212 | { | |
213 | 0 | drawIdsWrapped(starty, doHiddenCheck, hiddenRows); |
214 | 0 | return; |
215 | } | |
216 | ||
217 | // Now draw the id strings | |
218 | 0 | SequenceI seq; |
219 | 0 | for (int i = starty; i <= endy; i++) |
220 | { | |
221 | 0 | seq = av.getAlignment().getSequenceAt(i); |
222 | 0 | if (seq == null) |
223 | { | |
224 | 0 | continue; |
225 | } | |
226 | // hardwired italic IDs in applet currently | |
227 | 0 | Font italic = new Font(av.getFont().getName(), Font.ITALIC, |
228 | av.getFont().getSize()); | |
229 | 0 | gg.setFont(italic); |
230 | // boolean isrep=false; | |
231 | 0 | if (doHiddenCheck) |
232 | { | |
233 | // isrep = | |
234 | 0 | setHiddenFont(seq); |
235 | } | |
236 | ||
237 | // Selected sequence colours | |
238 | 0 | if ((searchResults != null) && searchResults.contains(seq)) |
239 | { | |
240 | 0 | currentColor = Color.black; |
241 | 0 | currentTextColor = Color.white; |
242 | } | |
243 | 0 | else if ((av.getSelectionGroup() != null) |
244 | && av.getSelectionGroup().getSequences(null).contains(seq)) | |
245 | { | |
246 | 0 | currentColor = Color.lightGray; |
247 | 0 | currentTextColor = Color.black; |
248 | } | |
249 | else | |
250 | { | |
251 | 0 | currentColor = av.getSequenceColour(seq); |
252 | 0 | currentTextColor = Color.black; |
253 | } | |
254 | ||
255 | 0 | gg.setColor(currentColor); |
256 | // TODO: isrep could be used to highlight the representative in a | |
257 | // different way | |
258 | 0 | gg.fillRect(0, (i - starty) * avcharHeight, getSize().width, |
259 | avcharHeight); | |
260 | 0 | gg.setColor(currentTextColor); |
261 | ||
262 | 0 | gg.drawString(seq.getDisplayId(av.getShowJVSuffix()), 0, |
263 | (((i - starty) * avcharHeight) + avcharHeight) | |
264 | - (avcharHeight / 5)); | |
265 | ||
266 | 0 | if (hiddenRows) |
267 | { | |
268 | 0 | drawMarker(i, starty, 0); |
269 | } | |
270 | } | |
271 | } | |
272 | ||
273 | /** | |
274 | * Draws sequence ids in wrapped mode | |
275 | * | |
276 | * @param starty | |
277 | * @param doHiddenCheck | |
278 | * @param hiddenRows | |
279 | */ | |
280 | 0 | protected void drawIdsWrapped(int starty, final boolean doHiddenCheck, |
281 | boolean hiddenRows) | |
282 | { | |
283 | 0 | int maxwidth = av.getAlignment().getVisibleWidth(); |
284 | 0 | int alheight = av.getAlignment().getHeight(); |
285 | ||
286 | 0 | int annotationHeight = 0; |
287 | 0 | AnnotationLabels labels = null; |
288 | ||
289 | 0 | if (av.isShowAnnotation()) |
290 | { | |
291 | 0 | AnnotationPanel ap = new AnnotationPanel(av); |
292 | 0 | annotationHeight = ap.adjustPanelHeight(); |
293 | 0 | labels = new AnnotationLabels(av); |
294 | } | |
295 | 0 | int hgap = avcharHeight; |
296 | 0 | if (av.getScaleAboveWrapped()) |
297 | { | |
298 | 0 | hgap += avcharHeight; |
299 | } | |
300 | ||
301 | 0 | int cHeight = alheight * avcharHeight + hgap + annotationHeight; |
302 | ||
303 | 0 | int rowSize = av.getRanges().getViewportWidth(); |
304 | ||
305 | // hardwired italic IDs in applet currently | |
306 | 0 | Font italic = new Font(av.getFont().getName(), Font.ITALIC, |
307 | av.getFont().getSize()); | |
308 | 0 | gg.setFont(italic); |
309 | ||
310 | /* | |
311 | * draw repeating sequence ids until out of sequence data or | |
312 | * out of visible space, whichever comes first | |
313 | */ | |
314 | 0 | int ypos = hgap; |
315 | 0 | int row = av.getRanges().getStartRes(); |
316 | 0 | while ((ypos <= getHeight()) && (row < maxwidth)) |
317 | { | |
318 | 0 | for (int i = starty; i < alheight; i++) |
319 | { | |
320 | ||
321 | 0 | SequenceI s = av.getAlignment().getSequenceAt(i); |
322 | // gg.setFont(italic); | |
323 | 0 | if (doHiddenCheck) |
324 | { | |
325 | 0 | setHiddenFont(s); |
326 | } | |
327 | 0 | drawIdString(gg, hiddenRows, s, i, 0, ypos); |
328 | } | |
329 | ||
330 | 0 | if (labels != null) |
331 | { | |
332 | 0 | gg.translate(0, ypos + (alheight * avcharHeight)); |
333 | 0 | labels.drawComponent(gg, getSize().width); |
334 | 0 | gg.translate(0, -ypos - (alheight * avcharHeight)); |
335 | } | |
336 | 0 | ypos += cHeight; |
337 | 0 | row += rowSize; |
338 | } | |
339 | } | |
340 | ||
341 | 0 | public void setHighlighted(List<SequenceI> list) |
342 | { | |
343 | 0 | searchResults = list; |
344 | 0 | repaint(); |
345 | } | |
346 | ||
347 | 0 | void drawMarker(int i, int starty, int yoffset) |
348 | { | |
349 | 0 | SequenceI[] hseqs = av.getAlignment() |
350 | .getHiddenSequences().hiddenSequences; | |
351 | // Use this method here instead of calling hiddenSeq adjust | |
352 | // 3 times. | |
353 | 0 | int hSize = hseqs.length; |
354 | ||
355 | 0 | int hiddenIndex = i; |
356 | 0 | int lastIndex = i - 1; |
357 | 0 | int nextIndex = i + 1; |
358 | ||
359 | 0 | for (int j = 0; j < hSize; j++) |
360 | { | |
361 | 0 | if (hseqs[j] != null) |
362 | { | |
363 | 0 | if (j - 1 < hiddenIndex) |
364 | { | |
365 | 0 | hiddenIndex++; |
366 | } | |
367 | 0 | if (j - 1 < lastIndex) |
368 | { | |
369 | 0 | lastIndex++; |
370 | } | |
371 | 0 | if (j - 1 < nextIndex) |
372 | { | |
373 | 0 | nextIndex++; |
374 | } | |
375 | } | |
376 | } | |
377 | ||
378 | 0 | boolean below = (hiddenIndex > lastIndex + 1); |
379 | 0 | boolean above = (nextIndex > hiddenIndex + 1); |
380 | ||
381 | 0 | gg.setColor(Color.blue); |
382 | 0 | if (below) |
383 | { | |
384 | 0 | gg.fillPolygon( |
385 | new int[] | |
386 | { getSize().width - avcharHeight, | |
387 | getSize().width - avcharHeight, getSize().width }, | |
388 | new int[] | |
389 | { (i - starty) * avcharHeight + yoffset, | |
390 | (i - starty) * avcharHeight + yoffset + avcharHeight / 4, | |
391 | (i - starty) * avcharHeight + yoffset }, | |
392 | 3); | |
393 | } | |
394 | 0 | if (above) |
395 | { | |
396 | 0 | gg.fillPolygon( |
397 | new int[] | |
398 | { getSize().width - avcharHeight, | |
399 | getSize().width - avcharHeight, getSize().width }, | |
400 | new int[] | |
401 | { (i - starty + 1) * avcharHeight + yoffset, | |
402 | (i - starty + 1) * avcharHeight + yoffset | |
403 | - avcharHeight / 4, | |
404 | (i - starty + 1) * avcharHeight + yoffset }, | |
405 | 3); | |
406 | ||
407 | } | |
408 | } | |
409 | ||
410 | 0 | boolean setHiddenFont(SequenceI seq) |
411 | { | |
412 | 0 | Font bold = new Font(av.getFont().getName(), Font.BOLD, |
413 | av.getFont().getSize()); | |
414 | ||
415 | 0 | if (av.isReferenceSeq(seq) || av.isHiddenRepSequence(seq)) |
416 | { | |
417 | 0 | gg.setFont(bold); |
418 | 0 | return true; |
419 | } | |
420 | 0 | return false; |
421 | } | |
422 | ||
423 | /** | |
424 | * Respond to viewport range changes (e.g. alignment panel was scrolled). Both | |
425 | * scrolling and resizing change viewport ranges. Scrolling changes both start | |
426 | * and end points, but resize only changes end values. Here we only want to | |
427 | * fastpaint on a scroll, with resize using a normal paint, so scroll events | |
428 | * are identified as changes to the horizontal or vertical start value. | |
429 | * <p> | |
430 | * In unwrapped mode, only responds to a vertical scroll, as horizontal scroll | |
431 | * leaves sequence ids unchanged. In wrapped mode, only vertical scroll is | |
432 | * provided, but it generates a change of "startres" which does require an | |
433 | * update here. | |
434 | */ | |
435 | 0 | @Override |
436 | public void propertyChange(PropertyChangeEvent evt) | |
437 | { | |
438 | 0 | String propertyName = evt.getPropertyName(); |
439 | 0 | if (propertyName.equals(ViewportRanges.STARTSEQ) |
440 | || (av.getWrapAlignment() | |
441 | && propertyName.equals(ViewportRanges.STARTRES))) | |
442 | { | |
443 | 0 | fastPaint((int) evt.getNewValue() - (int) evt.getOldValue()); |
444 | } | |
445 | 0 | else if (propertyName.equals(ViewportRanges.STARTRESANDSEQ)) |
446 | { | |
447 | 0 | fastPaint(((int[]) evt.getNewValue())[1] |
448 | - ((int[]) evt.getOldValue())[1]); | |
449 | } | |
450 | 0 | else if (propertyName.equals(ViewportRanges.MOVE_VIEWPORT)) |
451 | { | |
452 | 0 | repaint(); |
453 | } | |
454 | } | |
455 | } |