Class |
Line # |
Actions |
|||
---|---|---|---|---|---|
FeatureColourChooser | 52 | 178 | 45 |
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.api.FeatureColourI; | |
24 | import jalview.datamodel.GraphLine; | |
25 | import jalview.schemes.AnnotationColourGradient; | |
26 | import jalview.schemes.FeatureColour; | |
27 | import jalview.util.MessageManager; | |
28 | ||
29 | import java.awt.Checkbox; | |
30 | import java.awt.Choice; | |
31 | import java.awt.Color; | |
32 | import java.awt.Dimension; | |
33 | import java.awt.FlowLayout; | |
34 | import java.awt.Font; | |
35 | import java.awt.Frame; | |
36 | import java.awt.GridLayout; | |
37 | import java.awt.Label; | |
38 | import java.awt.Panel; | |
39 | import java.awt.Scrollbar; | |
40 | import java.awt.TextField; | |
41 | import java.awt.event.ActionEvent; | |
42 | import java.awt.event.ActionListener; | |
43 | import java.awt.event.AdjustmentEvent; | |
44 | import java.awt.event.AdjustmentListener; | |
45 | import java.awt.event.FocusAdapter; | |
46 | import java.awt.event.FocusEvent; | |
47 | import java.awt.event.ItemEvent; | |
48 | import java.awt.event.ItemListener; | |
49 | import java.awt.event.MouseEvent; | |
50 | import java.awt.event.MouseListener; | |
51 | ||
52 | public class FeatureColourChooser extends Panel implements ActionListener, | |
53 | AdjustmentListener, ItemListener, MouseListener | |
54 | { | |
55 | /* | |
56 | * the absolute min-max range of a feature score is scaled to | |
57 | * 1000 positions on the colour threshold slider | |
58 | */ | |
59 | private static final int SCALE_FACTOR_1K = 1000; | |
60 | ||
61 | private static final String COLON = ":"; | |
62 | ||
63 | private JVDialog frame; | |
64 | ||
65 | private Frame owner; | |
66 | ||
67 | private FeatureRenderer fr; | |
68 | ||
69 | private FeatureSettings fs = null; | |
70 | ||
71 | private FeatureColourI cs; | |
72 | ||
73 | private FeatureColourI oldcs; | |
74 | ||
75 | private boolean adjusting = false; | |
76 | ||
77 | private float min, max; | |
78 | ||
79 | private String type = null; | |
80 | ||
81 | private AlignFrame af = null; | |
82 | ||
83 | private Panel minColour = new Panel(); | |
84 | ||
85 | private Panel maxColour = new Panel(); | |
86 | ||
87 | private Choice threshold = new Choice(); | |
88 | ||
89 | private Scrollbar slider = new Scrollbar(Scrollbar.HORIZONTAL); | |
90 | ||
91 | private TextField thresholdValue = new TextField(20); | |
92 | ||
93 | private Checkbox thresholdIsMin = new Checkbox(); | |
94 | ||
95 | private Checkbox colourFromLabel = new Checkbox(); | |
96 | ||
97 | private GraphLine threshline; | |
98 | ||
99 | /** | |
100 | * Constructor given a context AlignFrame and a feature type. This is used | |
101 | * when opening the graduated colour dialog from the Amend Feature dialog. | |
102 | * | |
103 | * @param alignFrame | |
104 | * @param featureType | |
105 | */ | |
106 | 0 | public FeatureColourChooser(AlignFrame alignFrame, String featureType) |
107 | { | |
108 | 0 | this.af = alignFrame; |
109 | 0 | init(alignFrame.getSeqcanvas().getFeatureRenderer(), featureType); |
110 | } | |
111 | ||
112 | /** | |
113 | * Constructor given a context FeatureSettings and a feature type. This is | |
114 | * used when opening the graduated colour dialog from Feature Settings. | |
115 | * | |
116 | * @param fsettings | |
117 | * @param featureType | |
118 | */ | |
119 | 0 | public FeatureColourChooser(FeatureSettings fsettings, String featureType) |
120 | { | |
121 | 0 | this.fs = fsettings; |
122 | 0 | init(fsettings.fr, featureType); |
123 | } | |
124 | ||
125 | 0 | private void init(FeatureRenderer frenderer, String featureType) |
126 | { | |
127 | 0 | this.type = featureType; |
128 | 0 | fr = frenderer; |
129 | 0 | float mm[] = fr.getMinMax().get(type)[0]; |
130 | 0 | min = mm[0]; |
131 | 0 | max = mm[1]; |
132 | 0 | threshline = new GraphLine((max - min) / 2f, "Threshold", Color.black); |
133 | 0 | oldcs = fr.getFeatureColours().get(type); |
134 | 0 | if (oldcs.isGraduatedColour()) |
135 | { | |
136 | 0 | threshline.value = oldcs.getThreshold(); |
137 | 0 | cs = new FeatureColour(oldcs.getColour(), oldcs.getMinColour(), |
138 | oldcs.getMaxColour(), oldcs.getNoColour(), min, max); | |
139 | } | |
140 | else | |
141 | { | |
142 | // promote original color to a graduated color | |
143 | 0 | Color bl = Color.black; |
144 | 0 | if (oldcs.isSimpleColour()) |
145 | { | |
146 | 0 | bl = oldcs.getColour(); |
147 | } | |
148 | // original colour becomes the maximum colour | |
149 | 0 | cs = new FeatureColour(bl, Color.white, bl, Color.white, mm[0], |
150 | mm[1]); | |
151 | } | |
152 | 0 | minColour.setBackground(cs.getMinColour()); |
153 | 0 | maxColour.setBackground(cs.getMaxColour()); |
154 | 0 | minColour.setForeground(cs.getMinColour()); |
155 | 0 | maxColour.setForeground(cs.getMaxColour()); |
156 | 0 | colourFromLabel.setState(cs.isColourByLabel()); |
157 | 0 | adjusting = true; |
158 | ||
159 | 0 | try |
160 | { | |
161 | 0 | jbInit(); |
162 | } catch (Exception ex) | |
163 | { | |
164 | } | |
165 | 0 | threshold.select( |
166 | 0 | cs.isAboveThreshold() ? 1 : (cs.isBelowThreshold() ? 2 : 0)); |
167 | ||
168 | 0 | adjusting = false; |
169 | 0 | changeColour(true); |
170 | 0 | colourFromLabel.addItemListener(this); |
171 | 0 | slider.addAdjustmentListener(this); |
172 | 0 | slider.addMouseListener(this); |
173 | 0 | owner = (af != null) ? af : fs.frame; |
174 | 0 | frame = new JVDialog(owner, MessageManager |
175 | .formatMessage("label.variable_color_for", new String[] | |
176 | { type }), true, 480, 248); | |
177 | 0 | frame.setMainPanel(this); |
178 | 0 | validate(); |
179 | 0 | frame.setVisible(true); |
180 | 0 | if (frame.accept) |
181 | { | |
182 | 0 | changeColour(true); |
183 | } | |
184 | else | |
185 | { | |
186 | // cancel | |
187 | 0 | reset(); |
188 | 0 | frame.setVisible(false); |
189 | } | |
190 | } | |
191 | ||
192 | 0 | public FeatureColourChooser() |
193 | { | |
194 | 0 | try |
195 | { | |
196 | 0 | jbInit(); |
197 | } catch (Exception ex) | |
198 | { | |
199 | 0 | ex.printStackTrace(); |
200 | } | |
201 | } | |
202 | ||
203 | 0 | private void jbInit() throws Exception |
204 | { | |
205 | 0 | Label minLabel = new Label( |
206 | MessageManager.getString("label.min_value") + COLON); | |
207 | 0 | Label maxLabel = new Label( |
208 | MessageManager.getString("label.max_value") + COLON); | |
209 | 0 | minLabel.setFont(new java.awt.Font("Verdana", Font.PLAIN, 11)); |
210 | 0 | maxLabel.setFont(new java.awt.Font("Verdana", Font.PLAIN, 11)); |
211 | // minColour.setFont(new java.awt.Font("Verdana", Font.PLAIN, 11)); | |
212 | // minColour.setLabel("Min Colour"); | |
213 | ||
214 | 0 | minColour.setBounds(0, 0, 40, 27); |
215 | 0 | maxColour.setBounds(0, 0, 40, 27); |
216 | 0 | minColour.addMouseListener(this); |
217 | ||
218 | 0 | maxColour.setFont(new java.awt.Font("Verdana", Font.PLAIN, 11)); |
219 | 0 | maxColour.addMouseListener(this); |
220 | ||
221 | 0 | thresholdIsMin.addItemListener(this); |
222 | ||
223 | 0 | this.setLayout(new GridLayout(4, 1)); |
224 | 0 | Panel jPanel1 = new Panel(); |
225 | 0 | jPanel1.setLayout(new FlowLayout()); |
226 | 0 | Panel jPanel2 = new Panel(); |
227 | 0 | jPanel2.setLayout(new FlowLayout()); |
228 | 0 | Panel jPanel3 = new Panel(); |
229 | 0 | jPanel3.setLayout(new GridLayout(1, 1)); |
230 | 0 | Panel jPanel4 = new Panel(); |
231 | 0 | jPanel4.setLayout(new FlowLayout()); |
232 | 0 | jPanel1.setBackground(Color.white); |
233 | 0 | jPanel2.setBackground(Color.white); |
234 | 0 | jPanel4.setBackground(Color.white); |
235 | 0 | threshold.addItemListener(this); |
236 | 0 | threshold.addItem(MessageManager |
237 | .getString("label.threshold_feature_no_threshold")); | |
238 | 0 | threshold.addItem(MessageManager |
239 | .getString("label.threshold_feature_above_threshold")); | |
240 | 0 | threshold.addItem(MessageManager |
241 | .getString("label.threshold_feature_below_threshold")); | |
242 | 0 | thresholdValue.addActionListener(this); |
243 | 0 | thresholdValue.addFocusListener(new FocusAdapter() |
244 | { | |
245 | 0 | @Override |
246 | public void focusLost(FocusEvent e) | |
247 | { | |
248 | 0 | thresholdValue_actionPerformed(); |
249 | } | |
250 | }); | |
251 | 0 | slider.setBackground(Color.white); |
252 | 0 | slider.setEnabled(false); |
253 | 0 | slider.setSize(new Dimension(93, 21)); |
254 | 0 | thresholdValue.setEnabled(false); |
255 | 0 | thresholdValue.setSize(new Dimension(79, 22)); // setBounds(new |
256 | // Rectangle(248, 2, 79, | |
257 | // 22)); | |
258 | 0 | thresholdValue.setColumns(5); |
259 | 0 | jPanel3.setBackground(Color.white); |
260 | ||
261 | 0 | colourFromLabel.setFont(new java.awt.Font("Verdana", Font.PLAIN, 11)); |
262 | 0 | colourFromLabel |
263 | .setLabel(MessageManager.getString("label.colour_by_label")); | |
264 | 0 | colourFromLabel.setSize(new Dimension(139, 22)); |
265 | // threshold.setBounds(new Rectangle(11, 3, 139, 22)); | |
266 | 0 | thresholdIsMin.setBackground(Color.white); |
267 | 0 | thresholdIsMin |
268 | .setLabel(MessageManager.getString("label.threshold_minmax")); | |
269 | 0 | thresholdIsMin.setSize(new Dimension(135, 23)); |
270 | // thresholdIsMin.setBounds(new Rectangle(328, 3, 135, 23)); | |
271 | 0 | jPanel1.add(minLabel); |
272 | 0 | jPanel1.add(minColour); |
273 | 0 | jPanel1.add(maxLabel); |
274 | 0 | jPanel1.add(maxColour); |
275 | 0 | jPanel1.add(colourFromLabel); |
276 | 0 | jPanel2.add(threshold); |
277 | 0 | jPanel3.add(slider); |
278 | 0 | jPanel4.add(thresholdValue); |
279 | 0 | jPanel4.add(thresholdIsMin); |
280 | 0 | this.add(jPanel1);// , java.awt.BorderLayout.NORTH); |
281 | 0 | this.add(jPanel2);// , java.awt.BorderLayout.NORTH); |
282 | 0 | this.add(jPanel3);// , java.awt.BorderLayout.CENTER); |
283 | 0 | this.add(jPanel4);// , java.awt.BorderLayout.CENTER); |
284 | } | |
285 | ||
286 | 0 | @Override |
287 | public void actionPerformed(ActionEvent evt) | |
288 | { | |
289 | 0 | if (evt.getSource() == thresholdValue) |
290 | { | |
291 | 0 | thresholdValue_actionPerformed(); |
292 | } | |
293 | 0 | else if (evt.getSource() == minColour) |
294 | { | |
295 | 0 | minColour_actionPerformed(null); |
296 | } | |
297 | 0 | else if (evt.getSource() == maxColour) |
298 | { | |
299 | 0 | maxColour_actionPerformed(null); |
300 | } | |
301 | else | |
302 | { | |
303 | 0 | changeColour(true); |
304 | } | |
305 | } | |
306 | ||
307 | /** | |
308 | * Action on input of a value for colour score threshold | |
309 | */ | |
310 | 0 | protected void thresholdValue_actionPerformed() |
311 | { | |
312 | 0 | try |
313 | { | |
314 | 0 | float f = Float.valueOf(thresholdValue.getText()).floatValue(); |
315 | 0 | slider.setValue((int) (f * SCALE_FACTOR_1K)); |
316 | 0 | adjustmentValueChanged(null); |
317 | ||
318 | /* | |
319 | * force repaint of any Overview window or structure | |
320 | */ | |
321 | 0 | changeColour(true); |
322 | } catch (NumberFormatException ex) | |
323 | { | |
324 | } | |
325 | } | |
326 | ||
327 | 0 | @Override |
328 | public void itemStateChanged(ItemEvent evt) | |
329 | { | |
330 | 0 | maxColour.setEnabled(!colourFromLabel.getState()); |
331 | 0 | minColour.setEnabled(!colourFromLabel.getState()); |
332 | 0 | changeColour(true); |
333 | } | |
334 | ||
335 | /** | |
336 | * Handler called when the value of the threshold slider changes, either by | |
337 | * user action or programmatically | |
338 | */ | |
339 | 0 | @Override |
340 | public void adjustmentValueChanged(AdjustmentEvent evt) | |
341 | { | |
342 | 0 | if (!adjusting) |
343 | { | |
344 | 0 | thresholdValue.setText((slider.getValue() / 1000f) + ""); |
345 | 0 | valueChanged(); |
346 | } | |
347 | } | |
348 | ||
349 | /** | |
350 | * Responds to a change of colour threshold by computing the absolute value | |
351 | * and refreshing the alignment. | |
352 | */ | |
353 | 0 | protected void valueChanged() |
354 | { | |
355 | 0 | threshline.value = slider.getValue() / 1000f; |
356 | 0 | cs.setThreshold(threshline.value); |
357 | 0 | changeColour(false); |
358 | 0 | PaintRefresher.Refresh(this, fr.getViewport().getSequenceSetId()); |
359 | } | |
360 | ||
361 | 0 | public void minColour_actionPerformed(Color newCol) |
362 | { | |
363 | 0 | if (newCol == null) |
364 | { | |
365 | 0 | new UserDefinedColours(this, minColour.getBackground(), owner, |
366 | MessageManager | |
367 | .getString("label.select_colour_minimum_value")); | |
368 | } | |
369 | else | |
370 | { | |
371 | 0 | minColour.setBackground(newCol); |
372 | 0 | minColour.setForeground(newCol); |
373 | 0 | minColour.repaint(); |
374 | 0 | changeColour(true); |
375 | } | |
376 | ||
377 | } | |
378 | ||
379 | 0 | public void maxColour_actionPerformed(Color newCol) |
380 | { | |
381 | 0 | if (newCol == null) |
382 | { | |
383 | 0 | new UserDefinedColours(this, maxColour.getBackground(), owner, |
384 | MessageManager | |
385 | .getString("label.select_colour_maximum_value")); | |
386 | } | |
387 | else | |
388 | { | |
389 | 0 | maxColour.setBackground(newCol); |
390 | 0 | maxColour.setForeground(newCol); |
391 | 0 | maxColour.repaint(); |
392 | 0 | changeColour(true); |
393 | } | |
394 | } | |
395 | ||
396 | 0 | void changeColour(boolean updateOverview) |
397 | { | |
398 | // Check if combobox is still adjusting | |
399 | 0 | if (adjusting) |
400 | { | |
401 | 0 | return; |
402 | } | |
403 | ||
404 | 0 | int thresholdOption = AnnotationColourGradient.NO_THRESHOLD; |
405 | 0 | if (threshold.getSelectedIndex() == 1) |
406 | { | |
407 | 0 | thresholdOption = AnnotationColourGradient.ABOVE_THRESHOLD; |
408 | } | |
409 | 0 | else if (threshold.getSelectedIndex() == 2) |
410 | { | |
411 | 0 | thresholdOption = AnnotationColourGradient.BELOW_THRESHOLD; |
412 | } | |
413 | ||
414 | 0 | slider.setEnabled(true); |
415 | 0 | thresholdValue.setEnabled(true); |
416 | 0 | Color minc = minColour.getBackground(); |
417 | 0 | Color maxc = maxColour.getBackground(); |
418 | 0 | FeatureColour acg = new FeatureColour(maxc, minc, maxc, minc, min, max); |
419 | ||
420 | 0 | acg.setColourByLabel(colourFromLabel.getState()); |
421 | 0 | maxColour.setEnabled(!colourFromLabel.getState()); |
422 | 0 | minColour.setEnabled(!colourFromLabel.getState()); |
423 | 0 | if (thresholdOption == AnnotationColourGradient.NO_THRESHOLD) |
424 | { | |
425 | 0 | slider.setEnabled(false); |
426 | 0 | thresholdValue.setEnabled(false); |
427 | 0 | thresholdValue.setText(""); |
428 | } | |
429 | ||
430 | 0 | if (thresholdOption != AnnotationColourGradient.NO_THRESHOLD) |
431 | { | |
432 | 0 | adjusting = true; |
433 | 0 | acg.setThreshold(threshline.value); |
434 | ||
435 | 0 | slider.setMinimum((int) (min * SCALE_FACTOR_1K)); |
436 | 0 | slider.setMaximum((int) (max * SCALE_FACTOR_1K)); |
437 | 0 | slider.setValue((int) (threshline.value * SCALE_FACTOR_1K)); |
438 | 0 | thresholdValue.setText(threshline.value + ""); |
439 | 0 | slider.setEnabled(true); |
440 | 0 | thresholdValue.setEnabled(true); |
441 | 0 | adjusting = false; |
442 | } | |
443 | ||
444 | 0 | acg.setAboveThreshold( |
445 | thresholdOption == AnnotationColourGradient.ABOVE_THRESHOLD); | |
446 | 0 | acg.setBelowThreshold( |
447 | thresholdOption == AnnotationColourGradient.BELOW_THRESHOLD); | |
448 | ||
449 | 0 | if (thresholdIsMin.getState() |
450 | && thresholdOption != AnnotationColourGradient.NO_THRESHOLD) | |
451 | { | |
452 | 0 | if (thresholdOption == AnnotationColourGradient.ABOVE_THRESHOLD) |
453 | { | |
454 | 0 | acg = new FeatureColour(acg.getColour(), acg.getMinColour(), |
455 | acg.getMaxColour(), acg.getNoColour(), threshline.value, | |
456 | max); | |
457 | } | |
458 | else | |
459 | { | |
460 | 0 | acg = new FeatureColour(acg.getColour(), acg.getMinColour(), |
461 | acg.getMaxColour(), acg.getNoColour(), min, | |
462 | threshline.value); | |
463 | } | |
464 | } | |
465 | ||
466 | 0 | fr.setColour(type, acg); |
467 | 0 | cs = acg; |
468 | 0 | fs.selectionChanged(updateOverview); |
469 | } | |
470 | ||
471 | 0 | void reset() |
472 | { | |
473 | 0 | fr.setColour(type, oldcs); |
474 | 0 | fs.selectionChanged(true); |
475 | } | |
476 | ||
477 | 0 | @Override |
478 | public void mouseClicked(MouseEvent evt) | |
479 | { | |
480 | } | |
481 | ||
482 | 0 | @Override |
483 | public void mousePressed(MouseEvent evt) | |
484 | { | |
485 | } | |
486 | ||
487 | 0 | @Override |
488 | public void mouseReleased(MouseEvent evt) | |
489 | { | |
490 | 0 | if (evt.getSource() == minColour) |
491 | { | |
492 | 0 | minColour_actionPerformed(null); |
493 | } | |
494 | 0 | else if (evt.getSource() == maxColour) |
495 | { | |
496 | 0 | maxColour_actionPerformed(null); |
497 | } | |
498 | else | |
499 | { | |
500 | 0 | changeColour(true); |
501 | // PaintRefresher.Refresh(this, fr.getViewport().getSequenceSetId()); | |
502 | } | |
503 | } | |
504 | ||
505 | 0 | @Override |
506 | public void mouseEntered(MouseEvent evt) | |
507 | { | |
508 | } | |
509 | ||
510 | 0 | @Override |
511 | public void mouseExited(MouseEvent evt) | |
512 | { | |
513 | } | |
514 | ||
515 | } |