Class |
Line # |
Actions |
|||
---|---|---|---|---|---|
SequenceRenderer | 33 | 112 | 54 |
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.SequenceGroup; | |
24 | import jalview.datamodel.SequenceI; | |
25 | import jalview.renderer.ResidueColourFinder; | |
26 | import jalview.renderer.seqfeatures.FeatureColourFinder; | |
27 | ||
28 | import java.awt.Color; | |
29 | import java.awt.Font; | |
30 | import java.awt.FontMetrics; | |
31 | import java.awt.Graphics; | |
32 | ||
33 | public class SequenceRenderer implements jalview.api.SequenceRenderer | |
34 | { | |
35 | final static int CHAR_TO_UPPER = 'A' - 'a'; | |
36 | ||
37 | AlignViewport av; | |
38 | ||
39 | FontMetrics fm; | |
40 | ||
41 | boolean renderGaps = true; | |
42 | ||
43 | SequenceGroup[] allGroups = null; | |
44 | ||
45 | Color resBoxColour; | |
46 | ||
47 | Graphics graphics; | |
48 | ||
49 | ResidueColourFinder resColourFinder; | |
50 | ||
51 | 0 | public SequenceRenderer(AlignViewport av) |
52 | { | |
53 | 0 | this.av = av; |
54 | 0 | resColourFinder = new ResidueColourFinder(); |
55 | } | |
56 | ||
57 | /** | |
58 | * DOCUMENT ME! | |
59 | * | |
60 | * @param b | |
61 | * DOCUMENT ME! | |
62 | */ | |
63 | 0 | public void prepare(Graphics g, boolean renderGaps) |
64 | { | |
65 | 0 | graphics = g; |
66 | 0 | fm = g.getFontMetrics(); |
67 | ||
68 | 0 | this.renderGaps = renderGaps; |
69 | } | |
70 | ||
71 | /** | |
72 | * Get the residue colour at the given sequence position - as determined by | |
73 | * the sequence group colour (if any), else the colour scheme, possibly | |
74 | * overridden by a feature colour. | |
75 | * | |
76 | * @param seq | |
77 | * @param position | |
78 | * @param finder | |
79 | * @return | |
80 | */ | |
81 | 0 | @Override |
82 | public Color getResidueColour(final SequenceI seq, int position, | |
83 | FeatureColourFinder finder) | |
84 | { | |
85 | // TODO replace 8 or so code duplications with calls to this method | |
86 | // (refactored as needed) | |
87 | 0 | return resColourFinder.getResidueColour(av.getShowBoxes(), |
88 | av.getResidueShading(), allGroups, seq, position, finder); | |
89 | } | |
90 | ||
91 | 0 | public Color findSequenceColour(SequenceI seq, int i) |
92 | { | |
93 | 0 | allGroups = av.getAlignment().findAllGroups(seq); |
94 | 0 | drawBoxes(seq, i, i, 0); |
95 | 0 | return resBoxColour; |
96 | } | |
97 | ||
98 | 0 | public void drawSequence(SequenceI seq, SequenceGroup[] sg, int start, |
99 | int end, int y1) | |
100 | { | |
101 | 0 | if (seq == null) |
102 | { | |
103 | 0 | return; |
104 | } | |
105 | ||
106 | 0 | allGroups = sg; |
107 | ||
108 | 0 | drawBoxes(seq, start, end, y1); |
109 | ||
110 | 0 | if (av.validCharWidth) |
111 | { | |
112 | 0 | drawText(seq, start, end, y1); |
113 | } | |
114 | } | |
115 | ||
116 | 0 | public void drawBoxes(SequenceI seq, int start, int end, int y1) |
117 | { | |
118 | 0 | int i = start; |
119 | 0 | int length = seq.getLength(); |
120 | ||
121 | 0 | int curStart = -1; |
122 | 0 | int curWidth = av.getCharWidth(), avCharWidth = av.getCharWidth(), |
123 | avCharHeight = av.getCharHeight(); | |
124 | ||
125 | 0 | Color resBoxColour = Color.white; |
126 | 0 | Color tempColour = null; |
127 | 0 | while (i <= end) |
128 | { | |
129 | 0 | resBoxColour = Color.white; |
130 | 0 | if (i < length) |
131 | { | |
132 | 0 | SequenceGroup currentSequenceGroup = resColourFinder |
133 | .getCurrentSequenceGroup(allGroups, i); | |
134 | 0 | if (currentSequenceGroup != null) |
135 | { | |
136 | 0 | if (currentSequenceGroup.getDisplayBoxes()) |
137 | { | |
138 | 0 | resBoxColour = resColourFinder.getBoxColour( |
139 | currentSequenceGroup.getGroupColourScheme(), seq, i); | |
140 | } | |
141 | } | |
142 | 0 | else if (av.getShowBoxes()) |
143 | { | |
144 | 0 | resBoxColour = resColourFinder |
145 | .getBoxColour(av.getResidueShading(), seq, i); | |
146 | } | |
147 | } | |
148 | ||
149 | 0 | if (resBoxColour != tempColour) |
150 | { | |
151 | 0 | if (tempColour != null) |
152 | { | |
153 | 0 | graphics.fillRect(avCharWidth * (curStart - start), y1, curWidth, |
154 | avCharHeight); | |
155 | } | |
156 | 0 | graphics.setColor(resBoxColour); |
157 | ||
158 | 0 | curStart = i; |
159 | 0 | curWidth = avCharWidth; |
160 | 0 | tempColour = resBoxColour; |
161 | ||
162 | } | |
163 | else | |
164 | { | |
165 | 0 | curWidth += avCharWidth; |
166 | } | |
167 | ||
168 | 0 | i++; |
169 | } | |
170 | ||
171 | 0 | graphics.fillRect(avCharWidth * (curStart - start), y1, curWidth, |
172 | avCharHeight); | |
173 | } | |
174 | ||
175 | 0 | public void drawText(SequenceI seq, int start, int end, int y1) |
176 | { | |
177 | 0 | int avCharWidth = av.getCharWidth(), avCharHeight = av.getCharHeight(); |
178 | 0 | Font boldFont = null; |
179 | 0 | boolean bold = false; |
180 | 0 | if (av.isUpperCasebold()) |
181 | { | |
182 | 0 | boldFont = new Font(av.getFont().getName(), Font.BOLD, avCharHeight); |
183 | ||
184 | 0 | graphics.setFont(av.getFont()); |
185 | } | |
186 | ||
187 | 0 | y1 += avCharHeight - avCharHeight / 5; // height/5 replaces pady |
188 | ||
189 | 0 | int charOffset = 0; |
190 | ||
191 | // Need to find the sequence position here. | |
192 | 0 | if (end + 1 >= seq.getLength()) |
193 | { | |
194 | 0 | end = seq.getLength() - 1; |
195 | } | |
196 | ||
197 | 0 | char s = ' '; |
198 | 0 | boolean srep = av.isDisplayReferenceSeq(); |
199 | 0 | for (int i = start; i <= end; i++) |
200 | { | |
201 | 0 | graphics.setColor(Color.black); |
202 | ||
203 | 0 | s = seq.getCharAt(i); |
204 | 0 | if (!renderGaps && jalview.util.Comparison.isGap(s)) |
205 | { | |
206 | 0 | continue; |
207 | } | |
208 | ||
209 | 0 | SequenceGroup currentSequenceGroup = resColourFinder |
210 | .getCurrentSequenceGroup(allGroups, i); | |
211 | 0 | if (currentSequenceGroup != null) |
212 | { | |
213 | 0 | if (!currentSequenceGroup.getDisplayText()) |
214 | { | |
215 | 0 | continue; |
216 | } | |
217 | ||
218 | 0 | if (currentSequenceGroup.getColourText()) |
219 | { | |
220 | 0 | resBoxColour = resColourFinder.getBoxColour( |
221 | currentSequenceGroup.getGroupColourScheme(), seq, i); | |
222 | 0 | graphics.setColor(resBoxColour.darker()); |
223 | } | |
224 | 0 | if (currentSequenceGroup.getShowNonconserved()) |
225 | { | |
226 | 0 | s = getDisplayChar(srep, i, s, '.', currentSequenceGroup); |
227 | } | |
228 | } | |
229 | else | |
230 | { | |
231 | 0 | if (!av.getShowText()) |
232 | { | |
233 | 0 | continue; |
234 | } | |
235 | ||
236 | 0 | if (av.getColourText()) |
237 | { | |
238 | 0 | resBoxColour = resColourFinder |
239 | .getBoxColour(av.getResidueShading(), seq, i); | |
240 | 0 | if (av.getShowBoxes()) |
241 | { | |
242 | 0 | graphics.setColor(resBoxColour.darker()); |
243 | } | |
244 | else | |
245 | { | |
246 | 0 | graphics.setColor(resBoxColour); |
247 | } | |
248 | } | |
249 | 0 | if (av.getShowUnconserved()) |
250 | { | |
251 | 0 | s = getDisplayChar(srep, i, s, '.', null); |
252 | ||
253 | } | |
254 | } | |
255 | ||
256 | 0 | if (av.isUpperCasebold()) |
257 | { | |
258 | 0 | fm = graphics.getFontMetrics(); |
259 | 0 | if ('A' <= s && s <= 'Z') |
260 | { | |
261 | 0 | if (!bold) |
262 | { | |
263 | ||
264 | 0 | graphics.setFont(boldFont); |
265 | } | |
266 | 0 | bold = true; |
267 | } | |
268 | 0 | else if (bold) |
269 | { | |
270 | 0 | graphics.setFont(av.font); |
271 | 0 | bold = false; |
272 | } | |
273 | ||
274 | } | |
275 | ||
276 | 0 | charOffset = (avCharWidth - fm.charWidth(s)) / 2; |
277 | 0 | graphics.drawString(String.valueOf(s), |
278 | charOffset + avCharWidth * (i - start), y1); | |
279 | } | |
280 | ||
281 | } | |
282 | ||
283 | /** | |
284 | * Returns 'conservedChar' to represent the given position if the sequence | |
285 | * character at that position is equal to the consensus (ignoring case), else | |
286 | * returns the sequence character | |
287 | * | |
288 | * @param usesrep | |
289 | * @param position | |
290 | * @param sequenceChar | |
291 | * @param conservedChar | |
292 | * @return | |
293 | */ | |
294 | 0 | private char getDisplayChar(final boolean usesrep, int position, |
295 | char sequenceChar, char conservedChar, SequenceGroup currentGroup) | |
296 | { | |
297 | // TODO - use currentSequenceGroup rather than alignment | |
298 | // currentSequenceGroup.getConsensus() | |
299 | 0 | char conschar = (usesrep) ? (currentGroup == null |
300 | || position < currentGroup.getStartRes() | |
301 | || position > currentGroup.getEndRes() | |
302 | ? av.getAlignment().getSeqrep().getCharAt(position) | |
303 | 0 | : (currentGroup.getSeqrep() != null |
304 | ? currentGroup.getSeqrep().getCharAt(position) | |
305 | : av.getAlignment().getSeqrep() | |
306 | .getCharAt(position))) | |
307 | 0 | : (currentGroup != null && currentGroup.getConsensus() != null |
308 | && position >= currentGroup.getStartRes() | |
309 | && position <= currentGroup.getEndRes() | |
310 | && currentGroup | |
311 | .getConsensus().annotations.length > position) | |
312 | ? currentGroup | |
313 | .getConsensus().annotations[position].displayCharacter | |
314 | .charAt(0) | |
315 | : av.getAlignmentConsensusAnnotation().annotations[position].displayCharacter | |
316 | .charAt(0); | |
317 | 0 | if (!jalview.util.Comparison.isGap(conschar) |
318 | && (sequenceChar == conschar | |
319 | || sequenceChar + CHAR_TO_UPPER == conschar)) | |
320 | { | |
321 | 0 | sequenceChar = conservedChar; |
322 | } | |
323 | 0 | return sequenceChar; |
324 | } | |
325 | ||
326 | 0 | public void drawHighlightedText(SequenceI seq, int start, int end, int x1, |
327 | int y1) | |
328 | { | |
329 | 0 | int avCharWidth = av.getCharWidth(), avCharHeight = av.getCharHeight(); |
330 | 0 | int pady = avCharHeight / 5; |
331 | 0 | int charOffset = 0; |
332 | 0 | graphics.setColor(Color.black); |
333 | 0 | graphics.fillRect(x1, y1, avCharWidth * (end - start + 1), |
334 | avCharHeight); | |
335 | 0 | graphics.setColor(Color.white); |
336 | ||
337 | 0 | char s = '~'; |
338 | // Need to find the sequence position here. | |
339 | 0 | if (av.validCharWidth) |
340 | { | |
341 | 0 | for (int i = start; i <= end; i++) |
342 | { | |
343 | 0 | if (i < seq.getLength()) |
344 | { | |
345 | 0 | s = seq.getCharAt(i); |
346 | } | |
347 | ||
348 | 0 | charOffset = (avCharWidth - fm.charWidth(s)) / 2; |
349 | 0 | graphics.drawString(String.valueOf(s), |
350 | charOffset + x1 + avCharWidth * (i - start), | |
351 | y1 + avCharHeight - pady); | |
352 | } | |
353 | } | |
354 | } | |
355 | ||
356 | 0 | public void drawCursor(Graphics graphics, char s, int x1, int y1) |
357 | { | |
358 | 0 | int pady = av.getCharHeight() / 5; |
359 | 0 | int charOffset = 0; |
360 | 0 | graphics.setColor(Color.black); |
361 | 0 | graphics.fillRect(x1, y1, av.getCharWidth(), av.getCharHeight()); |
362 | 0 | graphics.setColor(Color.white); |
363 | ||
364 | 0 | graphics.setColor(Color.white); |
365 | ||
366 | 0 | if (av.validCharWidth) |
367 | { | |
368 | ||
369 | 0 | charOffset = (av.getCharWidth() - fm.charWidth(s)) / 2; |
370 | 0 | graphics.drawString(String.valueOf(s), charOffset + x1, |
371 | (y1 + av.getCharHeight()) - pady); | |
372 | } | |
373 | } | |
374 | ||
375 | } |