Class |
Line # |
Actions |
|||
---|---|---|---|---|---|
FeatureRenderer | 64 | 160 | 37 | ||
FeatureRenderer.FeatureColourPanel | 96 | 29 | 9 |
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.SearchResults; | |
25 | import jalview.datamodel.SearchResultsI; | |
26 | import jalview.datamodel.SequenceFeature; | |
27 | import jalview.datamodel.SequenceI; | |
28 | import jalview.io.FeaturesFile; | |
29 | import jalview.schemes.FeatureColour; | |
30 | import jalview.util.ColorUtils; | |
31 | import jalview.util.MessageManager; | |
32 | import jalview.viewmodel.AlignmentViewport; | |
33 | ||
34 | import java.awt.BorderLayout; | |
35 | import java.awt.Button; | |
36 | import java.awt.Choice; | |
37 | import java.awt.Color; | |
38 | import java.awt.Dimension; | |
39 | import java.awt.FlowLayout; | |
40 | import java.awt.Font; | |
41 | import java.awt.Frame; | |
42 | import java.awt.Graphics; | |
43 | import java.awt.GridLayout; | |
44 | import java.awt.Label; | |
45 | import java.awt.Panel; | |
46 | import java.awt.ScrollPane; | |
47 | import java.awt.TextArea; | |
48 | import java.awt.TextField; | |
49 | import java.awt.event.ActionEvent; | |
50 | import java.awt.event.ActionListener; | |
51 | import java.awt.event.MouseAdapter; | |
52 | import java.awt.event.MouseEvent; | |
53 | import java.awt.event.TextEvent; | |
54 | import java.awt.event.TextListener; | |
55 | import java.util.Hashtable; | |
56 | import java.util.List; | |
57 | ||
58 | /** | |
59 | * DOCUMENT ME! | |
60 | * | |
61 | * @author $author$ | |
62 | * @version $Revision$ | |
63 | */ | |
64 | public class FeatureRenderer | |
65 | extends jalview.renderer.seqfeatures.FeatureRenderer | |
66 | { | |
67 | /* | |
68 | * creating a new feature defaults to the type and group as | |
69 | * the last one created | |
70 | */ | |
71 | static String lastFeatureAdded = "feature_1"; | |
72 | ||
73 | static String lastFeatureGroupAdded = "Jalview"; | |
74 | ||
75 | // Holds web links for feature groups and feature types | |
76 | // in the form label|link | |
77 | Hashtable featureLinks = null; | |
78 | ||
79 | /** | |
80 | * Creates a new FeatureRenderer object. | |
81 | * | |
82 | * @param av | |
83 | */ | |
84 | 0 | public FeatureRenderer(AlignmentViewport av) |
85 | { | |
86 | 0 | super(av); |
87 | ||
88 | } | |
89 | ||
90 | int featureIndex = 0; | |
91 | ||
92 | boolean deleteFeature = false; | |
93 | ||
94 | FeatureColourPanel colourPanel; | |
95 | ||
96 | class FeatureColourPanel extends Panel | |
97 | { | |
98 | String label = ""; | |
99 | ||
100 | private Color maxCol; | |
101 | ||
102 | private boolean isColourByLabel, isGcol; | |
103 | ||
104 | /** | |
105 | * render a feature style in the amend feature dialog box | |
106 | */ | |
107 | 0 | public void updateColor(FeatureColourI newcol) |
108 | { | |
109 | 0 | Color bg = null; |
110 | 0 | String vlabel = ""; |
111 | 0 | if (newcol.isSimpleColour()) |
112 | { | |
113 | 0 | bg = newcol.getColour(); |
114 | 0 | setBackground(bg); |
115 | } | |
116 | else | |
117 | { | |
118 | 0 | if (newcol.isAboveThreshold()) |
119 | { | |
120 | 0 | vlabel += " (>)"; |
121 | } | |
122 | 0 | else if (newcol.isBelowThreshold()) |
123 | { | |
124 | 0 | vlabel += " (<)"; |
125 | } | |
126 | ||
127 | 0 | if (isColourByLabel = newcol.isColourByLabel()) |
128 | { | |
129 | 0 | setBackground(bg = Color.white); |
130 | 0 | vlabel += " (by Label)"; |
131 | } | |
132 | else | |
133 | { | |
134 | 0 | setBackground(bg = newcol.getMinColour()); |
135 | 0 | maxCol = newcol.getMaxColour(); |
136 | } | |
137 | } | |
138 | 0 | label = vlabel; |
139 | 0 | setBackground(bg); |
140 | 0 | repaint(); |
141 | } | |
142 | ||
143 | 0 | FeatureColourPanel() |
144 | { | |
145 | 0 | super(null); |
146 | } | |
147 | ||
148 | 0 | @Override |
149 | public void paint(Graphics g) | |
150 | { | |
151 | 0 | Dimension d = getSize(); |
152 | 0 | if (isGcol) |
153 | { | |
154 | 0 | if (isColourByLabel) |
155 | { | |
156 | 0 | g.setColor(Color.white); |
157 | 0 | g.fillRect(d.width / 2, 0, d.width / 2, d.height); |
158 | 0 | g.setColor(Color.black); |
159 | 0 | Font f = new Font("Verdana", Font.PLAIN, 10); |
160 | 0 | g.setFont(f); |
161 | 0 | g.drawString(MessageManager.getString("label.label"), 0, 0); |
162 | } | |
163 | else | |
164 | { | |
165 | 0 | g.setColor(maxCol); |
166 | 0 | g.fillRect(d.width / 2, 0, d.width / 2, d.height); |
167 | ||
168 | } | |
169 | } | |
170 | } | |
171 | ||
172 | } | |
173 | ||
174 | /** | |
175 | * Shows a dialog allowing the user to create, or amend or delete, sequence | |
176 | * features. If null in the supplied feature(s), feature type and group | |
177 | * default to those for the last feature created (with initial defaults of | |
178 | * "feature_1" and "Jalview"). | |
179 | * | |
180 | * @param sequences | |
181 | * @param features | |
182 | * @param create | |
183 | * @param ap | |
184 | * @return | |
185 | */ | |
186 | 0 | boolean amendFeatures(final List<SequenceI> sequences, |
187 | final List<SequenceFeature> features, boolean create, | |
188 | final AlignmentPanel ap) | |
189 | { | |
190 | 0 | final Panel bigPanel = new Panel(new BorderLayout()); |
191 | 0 | final TextField name = new TextField(16); |
192 | 0 | final TextField group = new TextField(16); |
193 | 0 | final TextArea description = new TextArea(3, 35); |
194 | 0 | final TextField start = new TextField(8); |
195 | 0 | final TextField end = new TextField(8); |
196 | 0 | final Choice overlaps; |
197 | 0 | Button deleteButton = new Button("Delete"); |
198 | 0 | deleteFeature = false; |
199 | ||
200 | 0 | name.addTextListener(new TextListener() |
201 | { | |
202 | 0 | @Override |
203 | public void textValueChanged(TextEvent e) | |
204 | { | |
205 | 0 | warnIfTypeHidden(ap.alignFrame, name.getText()); |
206 | } | |
207 | }); | |
208 | 0 | group.addTextListener(new TextListener() |
209 | { | |
210 | 0 | @Override |
211 | public void textValueChanged(TextEvent e) | |
212 | { | |
213 | 0 | warnIfGroupHidden(ap.alignFrame, group.getText()); |
214 | } | |
215 | }); | |
216 | 0 | colourPanel = new FeatureColourPanel(); |
217 | 0 | colourPanel.setSize(110, 15); |
218 | 0 | final FeatureRenderer fr = this; |
219 | ||
220 | 0 | Panel panel = new Panel(new GridLayout(3, 1)); |
221 | ||
222 | 0 | featureIndex = 0; // feature to be amended. |
223 | 0 | Panel tmp; |
224 | ||
225 | // ///////////////////////////////////// | |
226 | // /MULTIPLE FEATURES AT SELECTED RESIDUE | |
227 | 0 | if (!create && features.size() > 1) |
228 | { | |
229 | 0 | panel = new Panel(new GridLayout(4, 1)); |
230 | 0 | tmp = new Panel(); |
231 | 0 | tmp.add(new Label("Select Feature: ")); |
232 | 0 | overlaps = new Choice(); |
233 | 0 | for (SequenceFeature sf : features) |
234 | { | |
235 | 0 | String item = sf.getType() + "/" + sf.getBegin() + "-" |
236 | + sf.getEnd(); | |
237 | 0 | if (sf.getFeatureGroup() != null) |
238 | { | |
239 | 0 | item += " (" + sf.getFeatureGroup() + ")"; |
240 | } | |
241 | 0 | overlaps.addItem(item); |
242 | } | |
243 | ||
244 | 0 | tmp.add(overlaps); |
245 | ||
246 | 0 | overlaps.addItemListener(new java.awt.event.ItemListener() |
247 | { | |
248 | 0 | @Override |
249 | public void itemStateChanged(java.awt.event.ItemEvent e) | |
250 | { | |
251 | 0 | int index = overlaps.getSelectedIndex(); |
252 | 0 | if (index != -1) |
253 | { | |
254 | 0 | featureIndex = index; |
255 | 0 | SequenceFeature sf = features.get(index); |
256 | 0 | name.setText(sf.getType()); |
257 | 0 | description.setText(sf.getDescription()); |
258 | 0 | group.setText(sf.getFeatureGroup()); |
259 | 0 | start.setText(sf.getBegin() + ""); |
260 | 0 | end.setText(sf.getEnd() + ""); |
261 | ||
262 | 0 | SearchResultsI highlight = new SearchResults(); |
263 | 0 | highlight.addResult(sequences.get(0), sf.getBegin(), |
264 | sf.getEnd()); | |
265 | ||
266 | 0 | ap.seqPanel.seqCanvas.highlightSearchResults(highlight); |
267 | ||
268 | } | |
269 | 0 | FeatureColourI col = getFeatureStyle(name.getText()); |
270 | 0 | if (col == null) |
271 | { | |
272 | 0 | Color generatedColour = ColorUtils |
273 | .createColourFromName(name.getText()); | |
274 | 0 | col = new FeatureColour(generatedColour); |
275 | } | |
276 | ||
277 | 0 | colourPanel.updateColor(col); |
278 | } | |
279 | }); | |
280 | ||
281 | 0 | panel.add(tmp); |
282 | } | |
283 | // //////// | |
284 | // //////////////////////////////////// | |
285 | ||
286 | 0 | tmp = new Panel(); |
287 | 0 | panel.add(tmp); |
288 | 0 | tmp.add(new Label(MessageManager.getString("label.name:"), |
289 | Label.RIGHT)); | |
290 | 0 | tmp.add(name); |
291 | ||
292 | 0 | tmp = new Panel(); |
293 | 0 | panel.add(tmp); |
294 | 0 | tmp.add(new Label(MessageManager.getString("label.group:"), |
295 | Label.RIGHT)); | |
296 | 0 | tmp.add(group); |
297 | ||
298 | 0 | tmp = new Panel(); |
299 | 0 | panel.add(tmp); |
300 | 0 | tmp.add(new Label(MessageManager.getString("label.colour"), |
301 | Label.RIGHT)); | |
302 | 0 | tmp.add(colourPanel); |
303 | ||
304 | 0 | bigPanel.add(panel, BorderLayout.NORTH); |
305 | ||
306 | 0 | panel = new Panel(); |
307 | 0 | panel.add(new Label(MessageManager.getString("label.description:"), |
308 | Label.RIGHT)); | |
309 | 0 | panel.add(new ScrollPane().add(description)); |
310 | ||
311 | 0 | if (!create) |
312 | { | |
313 | 0 | bigPanel.add(panel, BorderLayout.SOUTH); |
314 | ||
315 | 0 | panel = new Panel(); |
316 | 0 | panel.add(new Label(MessageManager.getString("label.start"), |
317 | Label.RIGHT)); | |
318 | 0 | panel.add(start); |
319 | 0 | panel.add(new Label(MessageManager.getString("label.end"), |
320 | Label.RIGHT)); | |
321 | 0 | panel.add(end); |
322 | 0 | bigPanel.add(panel, BorderLayout.CENTER); |
323 | } | |
324 | else | |
325 | { | |
326 | 0 | bigPanel.add(panel, BorderLayout.CENTER); |
327 | } | |
328 | ||
329 | /* | |
330 | * use defaults for type and group (and update them on Confirm) only | |
331 | * if feature type has not been supplied by the caller | |
332 | * (e.g. for Amend, or create features from Find) | |
333 | */ | |
334 | 0 | SequenceFeature firstFeature = features.get(0); |
335 | 0 | boolean useLastDefaults = firstFeature.getType() == null; |
336 | 0 | String featureType = useLastDefaults ? lastFeatureAdded |
337 | : firstFeature.getType(); | |
338 | 0 | String featureGroup = useLastDefaults ? lastFeatureGroupAdded |
339 | : firstFeature.getFeatureGroup(); | |
340 | ||
341 | 0 | String title = create |
342 | ? MessageManager.getString("label.create_new_sequence_features") | |
343 | : MessageManager.formatMessage("label.amend_delete_features", | |
344 | new String[] | |
345 | { sequences.get(0).getName() }); | |
346 | ||
347 | 0 | final JVDialog dialog = new JVDialog(ap.alignFrame, title, true, 385, |
348 | 240); | |
349 | ||
350 | 0 | dialog.setMainPanel(bigPanel); |
351 | ||
352 | 0 | name.setText(featureType); |
353 | 0 | group.setText(featureGroup); |
354 | ||
355 | 0 | if (!create) |
356 | { | |
357 | 0 | dialog.ok.setLabel(MessageManager.getString("label.amend")); |
358 | 0 | dialog.buttonPanel.add(deleteButton, 1); |
359 | 0 | deleteButton.addActionListener(new ActionListener() |
360 | { | |
361 | 0 | @Override |
362 | public void actionPerformed(ActionEvent evt) | |
363 | { | |
364 | 0 | deleteFeature = true; |
365 | 0 | dialog.setVisible(false); |
366 | } | |
367 | }); | |
368 | } | |
369 | ||
370 | 0 | start.setText(firstFeature.getBegin() + ""); |
371 | 0 | end.setText(firstFeature.getEnd() + ""); |
372 | 0 | description.setText(firstFeature.getDescription()); |
373 | // lookup (or generate) the feature colour | |
374 | 0 | FeatureColourI fcol = getFeatureStyle(name.getText()); |
375 | // simply display the feature color in a box | |
376 | 0 | colourPanel.updateColor(fcol); |
377 | 0 | dialog.setResizable(true); |
378 | // TODO: render the graduated color in the box. | |
379 | 0 | colourPanel.addMouseListener(new MouseAdapter() |
380 | { | |
381 | 0 | @Override |
382 | public void mousePressed(MouseEvent evt) | |
383 | { | |
384 | 0 | if (!colourPanel.isGcol) |
385 | { | |
386 | 0 | new UserDefinedColours(fr, ap.alignFrame); |
387 | } | |
388 | else | |
389 | { | |
390 | 0 | new FeatureColourChooser(ap.alignFrame, name.getText()); |
391 | 0 | dialog.transferFocus(); |
392 | } | |
393 | } | |
394 | }); | |
395 | 0 | dialog.setVisible(true); |
396 | ||
397 | 0 | FeaturesFile ffile = new FeaturesFile(); |
398 | ||
399 | /* | |
400 | * only update default type and group if we used defaults | |
401 | */ | |
402 | 0 | final String enteredType = name.getText().trim(); |
403 | 0 | final String enteredGroup = group.getText().trim(); |
404 | 0 | final String enteredDesc = description.getText().replace('\n', ' '); |
405 | ||
406 | 0 | if (dialog.accept && useLastDefaults) |
407 | { | |
408 | 0 | lastFeatureAdded = enteredType; |
409 | 0 | lastFeatureGroupAdded = enteredGroup; |
410 | } | |
411 | ||
412 | 0 | if (!create) |
413 | { | |
414 | 0 | SequenceFeature sf = features.get(featureIndex); |
415 | 0 | if (dialog.accept) |
416 | { | |
417 | 0 | if (!colourPanel.isGcol) |
418 | { | |
419 | // update colour - otherwise its already done. | |
420 | 0 | setColour(enteredType, |
421 | new FeatureColour(colourPanel.getBackground())); | |
422 | } | |
423 | 0 | int newBegin = sf.begin; |
424 | 0 | int newEnd = sf.end; |
425 | 0 | try |
426 | { | |
427 | 0 | newBegin = Integer.parseInt(start.getText()); |
428 | 0 | newEnd = Integer.parseInt(end.getText()); |
429 | } catch (NumberFormatException ex) | |
430 | { | |
431 | // | |
432 | } | |
433 | ||
434 | /* | |
435 | * replace the feature by deleting it and adding a new one | |
436 | * (to ensure integrity of SequenceFeatures data store) | |
437 | */ | |
438 | 0 | sequences.get(0).deleteFeature(sf); |
439 | 0 | SequenceFeature newSf = new SequenceFeature(sf, enteredType, |
440 | newBegin, newEnd, enteredGroup, sf.getScore()); | |
441 | 0 | newSf.setDescription(enteredDesc); |
442 | 0 | ffile.parseDescriptionHTML(newSf, false); |
443 | // amend features dialog only updates one sequence at a time | |
444 | 0 | sequences.get(0).addSequenceFeature(newSf); |
445 | 0 | boolean typeOrGroupChanged = (!featureType.equals(newSf.getType()) |
446 | || !featureGroup.equals(newSf.getFeatureGroup())); | |
447 | ||
448 | 0 | ffile.parseDescriptionHTML(sf, false); |
449 | 0 | if (typeOrGroupChanged) |
450 | { | |
451 | 0 | featuresAdded(); |
452 | } | |
453 | } | |
454 | 0 | if (deleteFeature) |
455 | { | |
456 | 0 | sequences.get(0).deleteFeature(sf); |
457 | // ensure Feature Settings reflects removal of feature / group | |
458 | 0 | featuresAdded(); |
459 | } | |
460 | } | |
461 | else | |
462 | { | |
463 | /* | |
464 | * adding feature(s) | |
465 | */ | |
466 | 0 | if (dialog.accept && name.getText().length() > 0) |
467 | { | |
468 | 0 | for (int i = 0; i < sequences.size(); i++) |
469 | { | |
470 | 0 | SequenceFeature sf = features.get(i); |
471 | 0 | SequenceFeature sf2 = new SequenceFeature(enteredType, |
472 | enteredDesc, sf.getBegin(), sf.getEnd(), enteredGroup); | |
473 | 0 | ffile.parseDescriptionHTML(sf2, false); |
474 | 0 | sequences.get(i).addSequenceFeature(sf2); |
475 | } | |
476 | ||
477 | 0 | Color newColour = colourPanel.getBackground(); |
478 | // setColour(lastFeatureAdded, fcol); | |
479 | ||
480 | 0 | setColour(enteredType, new FeatureColour(newColour)); // was fcol |
481 | 0 | featuresAdded(); |
482 | } | |
483 | else | |
484 | { | |
485 | // no update to the alignment | |
486 | 0 | return false; |
487 | } | |
488 | } | |
489 | // refresh the alignment and the feature settings dialog | |
490 | 0 | if (((jalview.appletgui.AlignViewport) av).featureSettings != null) |
491 | { | |
492 | 0 | ((jalview.appletgui.AlignViewport) av).featureSettings.refreshTable(); |
493 | } | |
494 | // findAllFeatures(); | |
495 | ||
496 | 0 | ap.paintAlignment(true, true); |
497 | ||
498 | 0 | return true; |
499 | } | |
500 | ||
501 | 0 | protected void warnIfGroupHidden(Frame frame, String group) |
502 | { | |
503 | 0 | if (featureGroups.containsKey(group) && !featureGroups.get(group)) |
504 | { | |
505 | 0 | String msg = MessageManager.formatMessage("label.warning_hidden", |
506 | MessageManager.getString("label.group"), group); | |
507 | 0 | showWarning(frame, msg); |
508 | } | |
509 | } | |
510 | ||
511 | 0 | protected void warnIfTypeHidden(Frame frame, String type) |
512 | { | |
513 | 0 | if (getRenderOrder().contains(type)) |
514 | { | |
515 | 0 | if (!showFeatureOfType(type)) |
516 | { | |
517 | 0 | String msg = MessageManager.formatMessage("label.warning_hidden", |
518 | MessageManager.getString("label.feature_type"), type); | |
519 | 0 | showWarning(frame, msg); |
520 | } | |
521 | } | |
522 | } | |
523 | ||
524 | /** | |
525 | * @param frame | |
526 | * @param msg | |
527 | */ | |
528 | 0 | protected void showWarning(Frame frame, String msg) |
529 | { | |
530 | 0 | JVDialog d = new JVDialog(frame, "", true, 350, 200); |
531 | 0 | Panel mp = new Panel(); |
532 | 0 | d.ok.setLabel(MessageManager.getString("action.ok")); |
533 | 0 | d.cancel.setVisible(false); |
534 | 0 | mp.setLayout(new FlowLayout()); |
535 | 0 | mp.add(new Label(msg)); |
536 | 0 | d.setMainPanel(mp); |
537 | 0 | d.setVisible(true); |
538 | } | |
539 | } |