2 * Jalview - A Sequence Alignment Editor and Viewer
3 * Copyright (C) 2007 AM Waterhouse, J Procter, G Barton, M Clamp, S Searle
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License
7 * as published by the Free Software Foundation; either version 2
8 * of the License, or (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
24 import java.awt.event.*;
25 import java.awt.image.*;
28 import jalview.datamodel.*;
36 public class FeatureRenderer
40 float transparency = 1.0f;
44 Hashtable featureColours = new Hashtable();
46 // A higher level for grouping features of a
48 Hashtable featureGroups = null;
50 // This is actually an Integer held in the hashtable,
51 // Retrieved using the key feature type
57 * Creates a new FeatureRenderer object.
59 * @param av DOCUMENT ME!
61 public FeatureRenderer(AlignViewport av)
66 public void transferSettings(FeatureRenderer fr)
68 renderOrder = fr.renderOrder;
69 featureGroups = fr.featureGroups;
70 featureColours = fr.featureColours;
71 transparency = fr.transparency;
74 BufferedImage offscreenImage;
75 boolean offscreenRender = false;
76 public Color findFeatureColour(Color initialCol, SequenceI seq, int res)
78 return new Color(findFeatureColour(initialCol.getRGB(),
83 * This is used by the Molecule Viewer and Overview to
84 * get the accurate colourof the rendered sequence
86 public int findFeatureColour(int initialCol, SequenceI seq, int column)
88 if (!av.showSequenceFeatures)
96 sequenceFeatures = lastSeq.getDatasetSequence().getSequenceFeatures();
97 if (sequenceFeatures == null)
102 sfSize = sequenceFeatures.length;
105 if (jalview.util.Comparison.isGap(lastSeq.getCharAt(column)))
107 return Color.white.getRGB();
110 //Only bother making an offscreen image if transparency is applied
111 if (transparency != 1.0f && offscreenImage == null)
113 offscreenImage = new BufferedImage(1, 1, BufferedImage.TYPE_INT_ARGB);
116 currentColour = null;
118 offscreenRender = true;
120 if (offscreenImage != null)
122 offscreenImage.setRGB(0, 0, initialCol);
123 drawSequence(offscreenImage.getGraphics(),
127 return offscreenImage.getRGB(0, 0);
133 lastSeq.findPosition(column),
136 if (currentColour == null)
142 return ( (Integer) currentColour).intValue();
151 * @param g DOCUMENT ME!
152 * @param seq DOCUMENT ME!
153 * @param sg DOCUMENT ME!
154 * @param start DOCUMENT ME!
155 * @param end DOCUMENT ME!
156 * @param x1 DOCUMENT ME!
157 * @param y1 DOCUMENT ME!
158 * @param width DOCUMENT ME!
159 * @param height DOCUMENT ME!
162 // SequenceFeature sf;
164 SequenceFeature[] sequenceFeatures;
165 int sfSize, sfindex, spos, epos;
167 public void drawSequence(Graphics g, SequenceI seq,
168 int start, int end, int y1)
170 if (seq.getDatasetSequence().getSequenceFeatures() == null
171 || seq.getDatasetSequence().getSequenceFeatures().length == 0)
178 fm = g.getFontMetrics();
181 if (av.featuresDisplayed == null
182 || renderOrder == null
186 if (av.featuresDisplayed.size() < 1)
191 sequenceFeatures = seq.getDatasetSequence().getSequenceFeatures();
192 sfSize = sequenceFeatures.length;
195 if (lastSeq == null || seq != lastSeq
196 || lastSeq.getSequenceFeatures()!=sequenceFeatures)
199 sequenceFeatures = seq.getDatasetSequence().getSequenceFeatures();
200 sfSize = sequenceFeatures.length;
203 if (transparency != 1 && g != null)
205 Graphics2D g2 = (Graphics2D) g;
207 AlphaComposite.getInstance(
208 AlphaComposite.SRC_OVER, transparency));
211 if (!offscreenRender)
213 spos = lastSeq.findPosition(start);
214 epos = lastSeq.findPosition(end);
218 for (int renderIndex = 0; renderIndex < renderOrder.length; renderIndex++)
220 type = renderOrder[renderIndex];
222 if (type == null || !av.featuresDisplayed.containsKey(type))
227 // loop through all features in sequence to find
228 // current feature to render
229 for (sfindex = 0; sfindex < sfSize; sfindex++)
231 if (sequenceFeatures.length <= sfindex)
235 if (!sequenceFeatures[sfindex].type.equals(type))
240 if (featureGroups != null
241 && sequenceFeatures[sfindex].featureGroup != null
243 featureGroups.containsKey(sequenceFeatures[sfindex].featureGroup)
245 ! ( (Boolean) featureGroups.get(sequenceFeatures[sfindex].
252 if (!offscreenRender && (sequenceFeatures[sfindex].getBegin() > epos
253 || sequenceFeatures[sfindex].getEnd() < spos))
258 if (offscreenRender && offscreenImage == null)
260 if (sequenceFeatures[sfindex].begin <= start &&
261 sequenceFeatures[sfindex].end >= start)
263 currentColour = av.featuresDisplayed.get(sequenceFeatures[sfindex].
267 else if (sequenceFeatures[sfindex].type.equals("disulfide bond"))
270 renderFeature(g, seq,
271 seq.findIndex(sequenceFeatures[sfindex].begin) - 1,
272 seq.findIndex(sequenceFeatures[sfindex].begin) - 1,
273 new Color( ( (Integer) av.featuresDisplayed.get(
274 sequenceFeatures[sfindex].type)).intValue()),
276 renderFeature(g, seq,
277 seq.findIndex(sequenceFeatures[sfindex].end) - 1,
278 seq.findIndex(sequenceFeatures[sfindex].end) - 1,
279 new Color( ( (Integer) av.featuresDisplayed.get(
280 sequenceFeatures[sfindex].type)).intValue()),
286 renderFeature(g, seq,
287 seq.findIndex(sequenceFeatures[sfindex].begin) - 1,
288 seq.findIndex(sequenceFeatures[sfindex].end) - 1,
289 getColour(sequenceFeatures[sfindex].type),
297 if (transparency != 1.0f && g != null)
299 Graphics2D g2 = (Graphics2D) g;
301 AlphaComposite.getInstance(
302 AlphaComposite.SRC_OVER, 1.0f));
308 void renderFeature(Graphics g, SequenceI seq,
309 int fstart, int fend, Color featureColour, int start,
313 if ( ( (fstart <= end) && (fend >= start)))
316 { // fix for if the feature we have starts before the sequence start,
317 fstart = start; // but the feature end is still valid!!
324 int pady = (y1 + av.charHeight) - av.charHeight / 5;
325 for (i = fstart; i <= fend; i++)
327 s = seq.getCharAt(i);
329 if (jalview.util.Comparison.isGap(s))
334 g.setColor(featureColour);
336 g.fillRect( (i - start) * av.charWidth, y1, av.charWidth, av.charHeight);
338 if (offscreenRender || !av.validCharWidth)
343 g.setColor(Color.white);
344 charOffset = (av.charWidth - fm.charWidth(s)) / 2;
345 g.drawString(String.valueOf(s),
346 charOffset + (av.charWidth * (i - start)),
353 boolean newFeatureAdded = false;
355 public void featuresAdded()
361 boolean findingFeatures = false;
362 synchronized void findAllFeatures()
364 newFeatureAdded = false;
368 newFeatureAdded = true;
372 findingFeatures = true;
373 jalview.schemes.UserColourScheme ucs = new
374 jalview.schemes.UserColourScheme();
376 if (av.featuresDisplayed == null)
378 av.featuresDisplayed = new Hashtable();
381 av.featuresDisplayed.clear();
383 Vector allfeatures = new Vector();
384 for (int i = 0; i < av.alignment.getHeight(); i++)
386 SequenceFeature[] features
387 = av.alignment.getSequenceAt(i).getDatasetSequence().
388 getSequenceFeatures();
390 if (features == null)
396 while (index < features.length)
398 if (!av.featuresDisplayed.containsKey(features[index].getType()))
400 if (! (features[index].begin == 0 && features[index].end == 0))
402 // If beginning and end are 0, the feature is for the whole sequence
403 // and we don't want to render the feature in the normal way
405 if (getColour(features[index].getType()) == null)
407 featureColours.put(features[index].getType(),
408 ucs.createColourFromName(features[index].
412 av.featuresDisplayed.put(features[index].getType(),
413 new Integer(getColour(features[index].
414 getType()).getRGB()));
415 allfeatures.addElement(features[index].getType());
422 renderOrder = new String[allfeatures.size()];
423 Enumeration en = allfeatures.elements();
424 int i = allfeatures.size() - 1;
425 while (en.hasMoreElements())
427 renderOrder[i] = en.nextElement().toString();
431 findingFeatures = false;
434 public Color getColour(String featureType)
436 Color colour = (Color) featureColours.get(featureType);
440 static String lastFeatureAdded;
441 static String lastFeatureGroupAdded;
442 static String lastDescriptionAdded;
444 public boolean createNewFeatures(SequenceI[] sequences,
445 SequenceFeature[] features)
447 return amendFeatures(sequences, features, true, null);
450 int featureIndex = 0;
451 boolean amendFeatures(final SequenceI[] sequences,
452 final SequenceFeature[] features,
454 final AlignmentPanel ap)
456 JPanel bigPanel = new JPanel(new BorderLayout());
457 final JComboBox name = new JComboBox();
458 final JComboBox source = new JComboBox();
459 final JTextArea description = new JTextArea(3, 25);
460 final JSpinner start = new JSpinner();
461 final JSpinner end = new JSpinner();
462 start.setPreferredSize(new Dimension(80, 20));
463 end.setPreferredSize(new Dimension(80, 20));
464 final JPanel colour = new JPanel();
465 colour.setBorder(BorderFactory.createEtchedBorder());
466 colour.setMaximumSize(new Dimension(40, 10));
467 colour.addMouseListener(new MouseAdapter()
469 public void mousePressed(MouseEvent evt)
471 colour.setBackground(
472 JColorChooser.showDialog(Desktop.desktop,
473 "Select Feature Colour",
474 colour.getBackground()));
478 JPanel panel = new JPanel(new GridLayout(3, 2));
479 panel.add(new JLabel("Sequence Feature Name: ", JLabel.RIGHT));
481 panel.add(new JLabel("Feature Group: ", JLabel.RIGHT));
483 panel.add(new JLabel("Feature Colour: ", JLabel.RIGHT));
484 JPanel tmp = new JPanel();
486 colour.setPreferredSize(new Dimension(150, 15));
488 name.setEditable(true);
489 source.setEditable(true);
491 bigPanel.add(panel, BorderLayout.NORTH);
492 panel = new JPanel();
493 panel.add(new JLabel("Description: ", JLabel.RIGHT));
494 description.setFont(new java.awt.Font("Verdana", Font.PLAIN, 11));
495 description.setLineWrap(true);
496 panel.add(new JScrollPane(description));
500 bigPanel.add(panel, BorderLayout.SOUTH);
502 panel = new JPanel();
503 panel.add(new JLabel(" Start:", JLabel.RIGHT));
505 panel.add(new JLabel(" End:", JLabel.RIGHT));
507 bigPanel.add(panel, BorderLayout.CENTER);
511 bigPanel.add(panel, BorderLayout.CENTER);
514 if (lastFeatureAdded == null)
516 if (features[0].type != null)
518 lastFeatureAdded = features[0].type;
522 lastFeatureAdded = "feature_1";
526 if (lastFeatureGroupAdded == null)
528 if (features[0].featureGroup != null)
530 lastFeatureGroupAdded = features[0].featureGroup;
534 lastFeatureAdded = "Jalview";
539 if (featureGroups != null)
541 en = featureGroups.keys();
542 while (en.hasMoreElements())
544 source.addItem(en.nextElement().toString());
550 if (av.featuresDisplayed != null)
552 en = av.featuresDisplayed.keys();
553 while (en.hasMoreElements())
555 name.addItem(en.nextElement().toString());
559 name.setSelectedItem(lastFeatureAdded);
560 source.setSelectedItem(lastFeatureGroupAdded);
562 lastDescriptionAdded == null ?
563 features[0].description : lastDescriptionAdded);
565 if (getColour(lastFeatureAdded) != null)
567 colour.setBackground(getColour(lastFeatureAdded));
571 colour.setBackground(new Color(60, 160, 115));
575 else if (!newFeatures)
578 for (int f = 0; f < features.length; f++)
580 name.addItem(features[f].getType().toString());
583 description.setText(features[0].getDescription());
584 source.setSelectedItem(features[0].getFeatureGroup());
585 start.setValue(new Integer(features[0].getBegin()));
586 end.setValue(new Integer(features[0].getEnd()));
587 colour.setBackground(
588 getColour(name.getSelectedItem().toString()));
589 name.addItemListener(new ItemListener()
591 public void itemStateChanged(ItemEvent e)
593 int index = name.getSelectedIndex();
596 featureIndex = index;
597 description.setText(features[index].getDescription());
598 source.setSelectedItem(features[index].getFeatureGroup());
599 start.setValue(new Integer(features[index].getBegin()));
600 end.setValue(new Integer(features[index].getEnd()));
601 colour.setBackground(
602 getColour(name.getSelectedItem().toString()));
604 SearchResults highlight = new SearchResults();
605 highlight.addResult(sequences[0],
606 features[index].getBegin(),
607 features[index].getEnd());
609 ap.seqPanel.seqCanvas.highlightSearchResults(highlight);
612 Color col = getColour(name.getSelectedItem().toString());
616 jalview.schemes.UserColourScheme()
617 .createColourFromName(name.getSelectedItem().toString());
620 colour.setBackground(col);
629 options = new Object[]
631 "Amend", "Delete", "Cancel"};
635 options = new Object[]
640 String title = newFeatures ? "Create New Sequence Feature(s)" :
641 "Amend/Delete Features for "
642 + sequences[0].getName();
644 int reply = JOptionPane.showInternalOptionDialog(Desktop.desktop,
647 JOptionPane.YES_NO_CANCEL_OPTION,
648 JOptionPane.QUESTION_MESSAGE,
652 jalview.io.FeaturesFile ffile = new jalview.io.FeaturesFile();
654 if (reply == JOptionPane.OK_OPTION
655 && name.getSelectedItem() != null
656 && source.getSelectedItem() != null)
658 //This ensures that the last sequence
659 //is refreshed and new features are rendered
661 lastFeatureAdded = name.getSelectedItem().toString();
662 lastFeatureGroupAdded = source.getSelectedItem().toString();
663 lastDescriptionAdded = description.getText().replaceAll("\n", " ");
668 SequenceFeature sf = features[featureIndex];
670 if (reply == JOptionPane.NO_OPTION)
672 sequences[0].getDatasetSequence().deleteFeature(sf);
674 else if (reply == JOptionPane.YES_OPTION)
676 sf.type = lastFeatureAdded;
677 sf.featureGroup = lastFeatureGroupAdded;
678 sf.description = lastDescriptionAdded;
679 setColour(sf.type, colour.getBackground());
682 sf.begin = ( (Integer) start.getValue()).intValue();
683 sf.end = ( (Integer) end.getValue()).intValue();
685 catch (NumberFormatException ex)
688 ffile.parseDescriptionHTML(sf, false);
693 if (reply == JOptionPane.OK_OPTION
694 && name.getSelectedItem() != null
695 && source.getSelectedItem() != null)
697 for (int i = 0; i < sequences.length; i++)
699 features[i].type = lastFeatureAdded;
700 features[i].featureGroup = lastFeatureGroupAdded;
701 features[i].description = lastDescriptionAdded;
702 sequences[i].addSequenceFeature(features[i]);
703 ffile.parseDescriptionHTML(features[i], false);
706 if (av.featuresDisplayed == null)
708 av.featuresDisplayed = new Hashtable();
711 if (featureGroups == null)
713 featureGroups = new Hashtable();
716 featureGroups.put(lastFeatureGroupAdded, new Boolean(true));
718 Color col = colour.getBackground();
719 setColour(lastFeatureAdded, colour.getBackground());
721 av.featuresDisplayed.put(lastFeatureGroupAdded,
722 new Integer(col.getRGB()));
734 if (name.getSelectedIndex() == -1)
742 public void setColour(String featureType, Color col)
744 featureColours.put(featureType, col);
747 public void setTransparency(float value)
749 transparency = value;
752 public float getTransparency()
757 public void setFeaturePriority(Object[][] data)
759 // The feature table will display high priority
760 // features at the top, but theses are the ones
761 // we need to render last, so invert the data
762 if (av.featuresDisplayed != null)
764 av.featuresDisplayed.clear();
768 av.featuresDisplayed = new Hashtable();
772 renderOrder = new String[data.length];
776 for (int i = 0; i < data.length; i++)
778 String type = data[i][0].toString();
779 setColour(type, (Color) data[i][1]);
780 if ( ( (Boolean) data[i][2]).booleanValue())
782 av.featuresDisplayed.put(type, new Integer(getColour(type).getRGB()));
785 renderOrder[data.length - i - 1] = type;