2 * Jalview - A Sequence Alignment Editor and Viewer (Version 2.8.1)
3 * Copyright (C) 2014 The Jalview Authors
5 * This file is part of Jalview.
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 of the License, or (at your option) any later version.
11 * Jalview is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty
13 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR
14 * PURPOSE. See the GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License along with Jalview. If not, see <http://www.gnu.org/licenses/>.
17 * The Jalview Authors are detailed in the 'AUTHORS' file.
22 import java.util.concurrent.ConcurrentHashMap;
25 import java.awt.event.*;
26 import java.awt.image.*;
27 import java.beans.PropertyChangeListener;
28 import java.beans.PropertyChangeSupport;
32 import jalview.datamodel.*;
33 import jalview.schemes.GraduatedColor;
34 import jalview.util.MessageManager;
42 public class FeatureRenderer implements jalview.api.FeatureRenderer
51 * global transparency for feature
53 float transparency = 1.0f;
59 Map featureColours = new ConcurrentHashMap();
61 // A higher level for grouping features of a
63 Map featureGroups = new ConcurrentHashMap();
65 // This is actually an Integer held in the hashtable,
66 // Retrieved using the key feature type
71 PropertyChangeSupport changeSupport = new PropertyChangeSupport(this);
76 * Creates a new FeatureRenderer object.
81 public FeatureRenderer(AlignmentPanel ap)
85 if (ap != null && ap.seqPanel != null && ap.seqPanel.seqCanvas != null
86 && ap.seqPanel.seqCanvas.fr != null)
88 transferSettings(ap.seqPanel.seqCanvas.fr);
92 public class FeatureRendererSettings implements Cloneable
104 public FeatureRendererSettings(String[] renderOrder,
105 Hashtable featureGroups, Hashtable featureColours,
106 float transparency, Hashtable featureOrder)
109 this.renderOrder = renderOrder;
110 this.featureGroups = featureGroups;
111 this.featureColours = featureColours;
112 this.transparency = transparency;
113 this.featureOrder = featureOrder;
117 * create an independent instance of the feature renderer settings
121 public FeatureRendererSettings(FeatureRenderer fr)
124 featureGroups = new ConcurrentHashMap();
125 featureColours = new ConcurrentHashMap();
126 featureOrder = new ConcurrentHashMap();
127 if (fr.renderOrder != null)
129 this.renderOrder = new String[fr.renderOrder.length];
130 System.arraycopy(fr.renderOrder, 0, renderOrder, 0,
131 fr.renderOrder.length);
133 if (fr.featureGroups != null)
135 this.featureGroups = new ConcurrentHashMap(fr.featureGroups);
137 if (fr.featureColours != null)
139 this.featureColours = new ConcurrentHashMap(fr.featureColours);
141 Iterator en = fr.featureColours.keySet().iterator();
144 Object next = en.next();
145 Object val = featureColours.get(next);
146 if (val instanceof GraduatedColor)
149 .put(next, new GraduatedColor((GraduatedColor) val));
152 this.transparency = fr.transparency;
153 if (fr.featureOrder != null)
155 this.featureOrder = new ConcurrentHashMap(fr.featureOrder);
160 public FeatureRendererSettings getSettings()
162 return new FeatureRendererSettings(this);
165 public void transferSettings(FeatureRendererSettings fr)
167 this.renderOrder = fr.renderOrder;
168 this.featureGroups = fr.featureGroups;
169 this.featureColours = fr.featureColours;
170 this.transparency = fr.transparency;
171 this.featureOrder = fr.featureOrder;
174 * update from another feature renderer
175 * @param fr settings to copy
177 public void transferSettings(FeatureRenderer fr)
179 FeatureRendererSettings frs = new FeatureRendererSettings(fr);
180 this.renderOrder = frs.renderOrder;
181 this.featureGroups = frs.featureGroups;
182 this.featureColours = frs.featureColours;
183 this.transparency = frs.transparency;
184 this.featureOrder = frs.featureOrder;
185 if (av != null && av != fr.av)
187 // copy over the displayed feature settings
190 if (fr.av.featuresDisplayed != null)
192 // update display settings
193 if (av.featuresDisplayed == null)
195 av.featuresDisplayed = new Hashtable(fr.av.featuresDisplayed);
199 av.featuresDisplayed.clear();
200 Enumeration en = fr.av.featuresDisplayed.keys();
201 while (en.hasMoreElements())
203 av.featuresDisplayed.put(en.nextElement(), Boolean.TRUE);
212 BufferedImage offscreenImage;
214 boolean offscreenRender = false;
216 public Color findFeatureColour(Color initialCol, SequenceI seq, int res)
218 return new Color(findFeatureColour(initialCol.getRGB(), seq, res));
222 * This is used by the Molecule Viewer and Overview to get the accurate
223 * colourof the rendered sequence
225 public synchronized int findFeatureColour(int initialCol, SequenceI seq,
228 if (!av.showSequenceFeatures)
236 sequenceFeatures = lastSeq.getDatasetSequence().getSequenceFeatures();
237 if (sequenceFeatures != null)
239 sfSize = sequenceFeatures.length;
243 if (sequenceFeatures != lastSeq.getDatasetSequence()
244 .getSequenceFeatures())
246 sequenceFeatures = lastSeq.getDatasetSequence().getSequenceFeatures();
247 if (sequenceFeatures != null)
249 sfSize = sequenceFeatures.length;
253 if (sequenceFeatures == null || sfSize == 0)
258 if (jalview.util.Comparison.isGap(lastSeq.getCharAt(column)))
260 return Color.white.getRGB();
263 // Only bother making an offscreen image if transparency is applied
264 if (transparency != 1.0f && offscreenImage == null)
266 offscreenImage = new BufferedImage(1, 1, BufferedImage.TYPE_INT_ARGB);
269 currentColour = null;
270 // TODO: non-threadsafe - each rendering thread needs its own instance of
271 // the feature renderer - or this should be synchronized.
272 offscreenRender = true;
274 if (offscreenImage != null)
276 offscreenImage.setRGB(0, 0, initialCol);
277 drawSequence(offscreenImage.getGraphics(), lastSeq, column, column, 0);
279 return offscreenImage.getRGB(0, 0);
283 drawSequence(null, lastSeq, lastSeq.findPosition(column), -1, -1);
285 if (currentColour == null)
291 return ((Integer) currentColour).intValue();
320 // SequenceFeature sf;
323 SequenceFeature[] sequenceFeatures;
325 int sfSize, sfindex, spos, epos;
328 * show scores as heights
330 protected boolean varyHeight = false;
332 synchronized public void drawSequence(Graphics g, SequenceI seq,
333 int start, int end, int y1)
336 if (seq.getDatasetSequence().getSequenceFeatures() == null
337 || seq.getDatasetSequence().getSequenceFeatures().length == 0)
344 fm = g.getFontMetrics();
347 if (av.featuresDisplayed == null || renderOrder == null
351 if (av.featuresDisplayed.size() < 1)
356 sequenceFeatures = seq.getDatasetSequence().getSequenceFeatures();
361 || seq.getDatasetSequence().getSequenceFeatures() != sequenceFeatures)
364 sequenceFeatures = seq.getDatasetSequence().getSequenceFeatures();
367 if (transparency != 1 && g != null)
369 Graphics2D g2 = (Graphics2D) g;
370 g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER,
374 if (!offscreenRender)
376 spos = lastSeq.findPosition(start);
377 epos = lastSeq.findPosition(end);
380 sfSize = sequenceFeatures.length;
382 for (int renderIndex = 0; renderIndex < renderOrder.length; renderIndex++)
384 type = renderOrder[renderIndex];
386 if (type == null || !av.featuresDisplayed.containsKey(type))
391 // loop through all features in sequence to find
392 // current feature to render
393 for (sfindex = 0; sfindex < sfSize; sfindex++)
395 if (!sequenceFeatures[sfindex].type.equals(type))
400 if (featureGroups != null
401 && sequenceFeatures[sfindex].featureGroup != null
402 && sequenceFeatures[sfindex].featureGroup.length() != 0
404 .containsKey(sequenceFeatures[sfindex].featureGroup)
405 && !((Boolean) featureGroups
406 .get(sequenceFeatures[sfindex].featureGroup))
413 && (sequenceFeatures[sfindex].getBegin() > epos || sequenceFeatures[sfindex]
419 if (offscreenRender && offscreenImage == null)
421 if (sequenceFeatures[sfindex].begin <= start
422 && sequenceFeatures[sfindex].end >= start)
424 // this is passed out to the overview and other sequence renderers
425 // (e.g. molecule viewer) to get displayed colour for rendered
427 currentColour = new Integer(
428 getColour(sequenceFeatures[sfindex]).getRGB());
429 // used to be retreived from av.featuresDisplayed
430 // currentColour = av.featuresDisplayed
431 // .get(sequenceFeatures[sfindex].type);
435 else if (sequenceFeatures[sfindex].type.equals("disulfide bond"))
438 renderFeature(g, seq,
439 seq.findIndex(sequenceFeatures[sfindex].begin) - 1,
440 seq.findIndex(sequenceFeatures[sfindex].begin) - 1,
441 getColour(sequenceFeatures[sfindex])
442 // new Color(((Integer) av.featuresDisplayed
443 // .get(sequenceFeatures[sfindex].type)).intValue())
445 renderFeature(g, seq,
446 seq.findIndex(sequenceFeatures[sfindex].end) - 1,
447 seq.findIndex(sequenceFeatures[sfindex].end) - 1,
448 getColour(sequenceFeatures[sfindex])
449 // new Color(((Integer) av.featuresDisplayed
450 // .get(sequenceFeatures[sfindex].type)).intValue())
454 else if (showFeature(sequenceFeatures[sfindex]))
456 if (av.showSeqFeaturesHeight
457 && sequenceFeatures[sfindex].score != Float.NaN)
459 renderScoreFeature(g, seq,
460 seq.findIndex(sequenceFeatures[sfindex].begin) - 1,
461 seq.findIndex(sequenceFeatures[sfindex].end) - 1,
462 getColour(sequenceFeatures[sfindex]), start, end, y1,
463 normaliseScore(sequenceFeatures[sfindex]));
467 renderFeature(g, seq,
468 seq.findIndex(sequenceFeatures[sfindex].begin) - 1,
469 seq.findIndex(sequenceFeatures[sfindex].end) - 1,
470 getColour(sequenceFeatures[sfindex]), start, end, y1);
478 if (transparency != 1.0f && g != null)
480 Graphics2D g2 = (Graphics2D) g;
481 g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER,
486 Hashtable minmax = new Hashtable();
489 * normalise a score against the max/min bounds for the feature type.
491 * @param sequenceFeature
492 * @return byte[] { signed, normalised signed (-127 to 127) or unsigned
495 private final byte[] normaliseScore(SequenceFeature sequenceFeature)
497 float[] mm = ((float[][]) minmax.get(sequenceFeature.type))[0];
498 final byte[] r = new byte[]
502 if (r[0] != 0 || mm[0] < 0.0)
505 r[1] = (byte) ((int) 128.0 + 127.0 * (sequenceFeature.score / mm[1]));
509 r[1] = (byte) ((int) 255.0 * (sequenceFeature.score / mm[1]));
519 void renderFeature(Graphics g, SequenceI seq, int fstart, int fend,
520 Color featureColour, int start, int end, int y1)
523 if (((fstart <= end) && (fend >= start)))
526 { // fix for if the feature we have starts before the sequence start,
527 fstart = start; // but the feature end is still valid!!
534 int pady = (y1 + av.charHeight) - av.charHeight / 5;
535 for (i = fstart; i <= fend; i++)
537 s = seq.getCharAt(i);
539 if (jalview.util.Comparison.isGap(s))
544 g.setColor(featureColour);
546 g.fillRect((i - start) * av.charWidth, y1, av.charWidth,
549 if (offscreenRender || !av.validCharWidth)
554 g.setColor(Color.white);
555 charOffset = (av.charWidth - fm.charWidth(s)) / 2;
556 g.drawString(String.valueOf(s), charOffset
557 + (av.charWidth * (i - start)), pady);
563 void renderScoreFeature(Graphics g, SequenceI seq, int fstart, int fend,
564 Color featureColour, int start, int end, int y1, byte[] bs)
567 if (((fstart <= end) && (fend >= start)))
570 { // fix for if the feature we have starts before the sequence start,
571 fstart = start; // but the feature end is still valid!!
578 int pady = (y1 + av.charHeight) - av.charHeight / 5;
579 int ystrt = 0, yend = av.charHeight;
582 // signed - zero is always middle of residue line.
585 yend = av.charHeight * (128 - bs[1]) / 512;
586 ystrt = av.charHeight - yend / 2;
590 ystrt = av.charHeight / 2;
591 yend = av.charHeight * (bs[1] - 128) / 512;
596 yend = av.charHeight * bs[1] / 255;
597 ystrt = av.charHeight - yend;
600 for (i = fstart; i <= fend; i++)
602 s = seq.getCharAt(i);
604 if (jalview.util.Comparison.isGap(s))
609 g.setColor(featureColour);
610 int x = (i - start) * av.charWidth;
611 g.drawRect(x, y1, av.charWidth, av.charHeight);
612 g.fillRect(x, y1 + ystrt, av.charWidth, yend);
614 if (offscreenRender || !av.validCharWidth)
619 g.setColor(Color.black);
620 charOffset = (av.charWidth - fm.charWidth(s)) / 2;
621 g.drawString(String.valueOf(s), charOffset
622 + (av.charWidth * (i - start)), pady);
628 boolean newFeatureAdded = false;
631 * Called when alignment in associated view has new/modified features to
632 * discover and display.
635 public void featuresAdded()
641 boolean findingFeatures = false;
644 * search the alignment for all new features, give them a colour and display
645 * them. Then fires a PropertyChangeEvent on the changeSupport object.
648 void findAllFeatures()
650 synchronized (firing)
652 if (firing.equals(Boolean.FALSE))
654 firing = Boolean.TRUE;
655 findAllFeatures(true); // add all new features as visible
656 changeSupport.firePropertyChange("changeSupport", null, null);
657 firing = Boolean.FALSE;
663 * Searches alignment for all features and updates colours
665 * @param newMadeVisible
666 * if true newly added feature types will be rendered immediatly
668 synchronized void findAllFeatures(boolean newMadeVisible)
670 newFeatureAdded = false;
674 newFeatureAdded = true;
678 findingFeatures = true;
680 if (av.featuresDisplayed == null)
682 av.featuresDisplayed = new Hashtable();
685 allfeatures = new Vector();
686 Vector oldfeatures = new Vector();
687 if (renderOrder != null)
689 for (int i = 0; i < renderOrder.length; i++)
691 if (renderOrder[i] != null)
693 oldfeatures.addElement(renderOrder[i]);
699 minmax = new Hashtable();
701 AlignmentI alignment = av.getAlignment();
702 for (int i = 0; i < alignment.getHeight(); i++)
704 SequenceFeature[] features = alignment.getSequenceAt(i)
705 .getDatasetSequence().getSequenceFeatures();
707 if (features == null)
713 while (index < features.length)
715 if (!av.featuresDisplayed.containsKey(features[index].getType()))
718 if (featureGroups.containsKey(features[index].getType()))
720 boolean visible = ((Boolean) featureGroups
721 .get(features[index].featureGroup)).booleanValue();
730 if (!(features[index].begin == 0 && features[index].end == 0))
732 // If beginning and end are 0, the feature is for the whole sequence
733 // and we don't want to render the feature in the normal way
736 && !oldfeatures.contains(features[index].getType()))
738 // this is a new feature type on the alignment. Mark it for
740 av.featuresDisplayed.put(features[index].getType(),
741 new Integer(getColour(features[index].getType())
743 setOrder(features[index].getType(), 0);
747 if (!allfeatures.contains(features[index].getType()))
749 allfeatures.addElement(features[index].getType());
751 if (features[index].score != Float.NaN)
753 int nonpos = features[index].getBegin() >= 1 ? 0 : 1;
754 float[][] mm = (float[][]) minmax.get(features[index].getType());
759 minmax.put(features[index].getType(), mm);
761 if (mm[nonpos] == null)
763 mm[nonpos] = new float[]
764 { features[index].score, features[index].score };
769 if (mm[nonpos][0] > features[index].score)
771 mm[nonpos][0] = features[index].score;
773 if (mm[nonpos][1] < features[index].score)
775 mm[nonpos][1] = features[index].score;
782 updateRenderOrder(allfeatures);
783 findingFeatures = false;
786 protected Boolean firing = Boolean.FALSE;
789 * replaces the current renderOrder with the unordered features in
790 * allfeatures. The ordering of any types in both renderOrder and allfeatures
791 * is preserved, and all new feature types are rendered on top of the existing
792 * types, in the order given by getOrder or the order given in allFeatures.
793 * Note. this operates directly on the featureOrder hash for efficiency. TODO:
794 * eliminate the float storage for computing/recalling the persistent ordering
795 * New Cability: updates min/max for colourscheme range if its dynamic
799 private void updateRenderOrder(Vector allFeatures)
801 Vector allfeatures = new Vector(allFeatures);
802 String[] oldRender = renderOrder;
803 renderOrder = new String[allfeatures.size()];
804 Object mmrange, fc = null;
805 boolean initOrders = (featureOrder == null);
807 if (oldRender != null && oldRender.length > 0)
809 for (int j = 0; j < oldRender.length; j++)
811 if (oldRender[j] != null)
815 setOrder(oldRender[j], (1 - (1 + (float) j)
816 / (float) oldRender.length));
818 if (allfeatures.contains(oldRender[j]))
820 renderOrder[opos++] = oldRender[j]; // existing features always
821 // appear below new features
822 allfeatures.removeElement(oldRender[j]);
825 mmrange = minmax.get(oldRender[j]);
828 fc = featureColours.get(oldRender[j]);
829 if (fc != null && fc instanceof GraduatedColor
830 && ((GraduatedColor) fc).isAutoScale())
832 ((GraduatedColor) fc).updateBounds(
833 ((float[][]) mmrange)[0][0],
834 ((float[][]) mmrange)[0][1]);
842 if (allfeatures.size() == 0)
844 // no new features - leave order unchanged.
847 int i = allfeatures.size() - 1;
849 boolean sort = false;
850 String[] newf = new String[allfeatures.size()];
851 float[] sortOrder = new float[allfeatures.size()];
852 Enumeration en = allfeatures.elements();
853 // sort remaining elements
854 while (en.hasMoreElements())
856 newf[i] = en.nextElement().toString();
859 // update from new features minmax if necessary
860 mmrange = minmax.get(newf[i]);
863 fc = featureColours.get(newf[i]);
864 if (fc != null && fc instanceof GraduatedColor
865 && ((GraduatedColor) fc).isAutoScale())
867 ((GraduatedColor) fc).updateBounds(((float[][]) mmrange)[0][0],
868 ((float[][]) mmrange)[0][1]);
872 if (initOrders || !featureOrder.containsKey(newf[i]))
874 int denom = initOrders ? allfeatures.size() : featureOrder.size();
875 // new unordered feature - compute persistent ordering at head of
876 // existing features.
877 setOrder(newf[i], i / (float) denom);
879 // set order from newly found feature from persisted ordering.
880 sortOrder[i] = 2 - ((Float) featureOrder.get(newf[i])).floatValue();
883 // only sort if we need to
884 sort = sort || sortOrder[i] > sortOrder[i + 1];
888 if (iSize > 1 && sort)
890 jalview.util.QuickSort.sort(sortOrder, newf);
893 System.arraycopy(newf, 0, renderOrder, opos, newf.length);
897 * get a feature style object for the given type string. Creates a
898 * java.awt.Color for a featureType with no existing colourscheme. TODO:
899 * replace return type with object implementing standard abstract colour/style
903 * @return java.awt.Color or GraduatedColor
905 public Object getFeatureStyle(String featureType)
907 Object fc = featureColours.get(featureType);
910 jalview.schemes.UserColourScheme ucs = new jalview.schemes.UserColourScheme();
911 Color col = ucs.createColourFromName(featureType);
912 featureColours.put(featureType, fc = col);
918 * return a nominal colour for this feature
921 * @return standard color, or maximum colour for graduated colourscheme
923 public Color getColour(String featureType)
925 Object fc = getFeatureStyle(featureType);
927 if (fc instanceof Color)
933 if (fc instanceof GraduatedColor)
935 return ((GraduatedColor) fc).getMaxColor();
938 throw new Error("Implementation Error: Unrecognised render object "
939 + fc.getClass() + " for features of type " + featureType);
943 * calculate the render colour for a specific feature using current feature
947 * @return render colour for the given feature
949 public Color getColour(SequenceFeature feature)
951 Object fc = getFeatureStyle(feature.getType());
952 if (fc instanceof Color)
958 if (fc instanceof GraduatedColor)
960 return ((GraduatedColor) fc).findColor(feature);
963 throw new Error("Implementation Error: Unrecognised render object "
964 + fc.getClass() + " for features of type " + feature.getType());
967 private boolean showFeature(SequenceFeature sequenceFeature)
969 Object fc = getFeatureStyle(sequenceFeature.type);
970 if (fc instanceof GraduatedColor)
972 return ((GraduatedColor) fc).isColored(sequenceFeature);
981 // // Feature Editing Dialog
982 // // Will be refactored in next release.
984 static String lastFeatureAdded;
986 static String lastFeatureGroupAdded;
988 static String lastDescriptionAdded;
992 int featureIndex = 0;
994 boolean amendFeatures(final SequenceI[] sequences,
995 final SequenceFeature[] features, boolean newFeatures,
996 final AlignmentPanel ap)
1001 final JPanel bigPanel = new JPanel(new BorderLayout());
1002 final JComboBox overlaps;
1003 final JTextField name = new JTextField(25);
1004 final JTextField source = new JTextField(25);
1005 final JTextArea description = new JTextArea(3, 25);
1006 final JSpinner start = new JSpinner();
1007 final JSpinner end = new JSpinner();
1008 start.setPreferredSize(new Dimension(80, 20));
1009 end.setPreferredSize(new Dimension(80, 20));
1010 final FeatureRenderer me = this;
1011 final JLabel colour = new JLabel();
1012 colour.setOpaque(true);
1013 // colour.setBorder(BorderFactory.createEtchedBorder());
1014 colour.setMaximumSize(new Dimension(30, 16));
1015 colour.addMouseListener(new MouseAdapter()
1017 FeatureColourChooser fcc = null;
1019 public void mousePressed(MouseEvent evt)
1021 if (fcol instanceof Color)
1023 Color col = JColorChooser.showDialog(Desktop.desktop,
1024 "Select Feature Colour", ((Color) fcol));
1028 updateColourButton(bigPanel, colour, col);
1036 final String type = features[featureIndex].getType();
1037 fcc = new FeatureColourChooser(me, type);
1038 fcc.setRequestFocusEnabled(true);
1041 fcc.addActionListener(new ActionListener()
1044 public void actionPerformed(ActionEvent e)
1046 fcol = fcc.getLastColour();
1048 setColour(type, fcol);
1049 updateColourButton(bigPanel, colour, fcol);
1057 JPanel tmp = new JPanel();
1058 JPanel panel = new JPanel(new GridLayout(3, 1));
1060 // /////////////////////////////////////
1061 // /MULTIPLE FEATURES AT SELECTED RESIDUE
1062 if (!newFeatures && features.length > 1)
1064 panel = new JPanel(new GridLayout(4, 1));
1066 tmp.add(new JLabel(MessageManager.getString("label.select_feature")));
1067 overlaps = new JComboBox();
1068 for (int i = 0; i < features.length; i++)
1070 overlaps.addItem(features[i].getType() + "/"
1071 + features[i].getBegin() + "-" + features[i].getEnd()
1072 + " (" + features[i].getFeatureGroup() + ")");
1077 overlaps.addItemListener(new ItemListener()
1079 public void itemStateChanged(ItemEvent e)
1081 int index = overlaps.getSelectedIndex();
1084 featureIndex = index;
1085 name.setText(features[index].getType());
1086 description.setText(features[index].getDescription());
1087 source.setText(features[index].getFeatureGroup());
1088 start.setValue(new Integer(features[index].getBegin()));
1089 end.setValue(new Integer(features[index].getEnd()));
1091 SearchResults highlight = new SearchResults();
1092 highlight.addResult(sequences[0], features[index].getBegin(),
1093 features[index].getEnd());
1095 ap.seqPanel.seqCanvas.highlightSearchResults(highlight);
1098 Object col = getFeatureStyle(name.getText());
1101 col = new jalview.schemes.UserColourScheme()
1102 .createColourFromName(name.getText());
1104 oldcol = fcol = col;
1105 updateColourButton(bigPanel, colour, col);
1112 // ////////////////////////////////////
1116 tmp.add(new JLabel(MessageManager.getString("label.name"), JLabel.RIGHT));
1121 tmp.add(new JLabel(MessageManager.getString("label.group")+":", JLabel.RIGHT));
1126 tmp.add(new JLabel(MessageManager.getString("label.colour"), JLabel.RIGHT));
1128 colour.setPreferredSize(new Dimension(150, 15));
1129 colour.setFont(new java.awt.Font("Verdana", Font.PLAIN, 9));
1130 colour.setForeground(Color.black);
1131 colour.setHorizontalAlignment(SwingConstants.CENTER);
1132 colour.setVerticalAlignment(SwingConstants.CENTER);
1133 colour.setHorizontalTextPosition(SwingConstants.CENTER);
1134 colour.setVerticalTextPosition(SwingConstants.CENTER);
1135 bigPanel.add(panel, BorderLayout.NORTH);
1137 panel = new JPanel();
1138 panel.add(new JLabel(MessageManager.getString("label.description"), JLabel.RIGHT));
1139 description.setFont(JvSwingUtils.getTextAreaFont());
1140 description.setLineWrap(true);
1141 panel.add(new JScrollPane(description));
1145 bigPanel.add(panel, BorderLayout.SOUTH);
1147 panel = new JPanel();
1148 panel.add(new JLabel(MessageManager.getString("label.start"), JLabel.RIGHT));
1150 panel.add(new JLabel(MessageManager.getString("label.end"), JLabel.RIGHT));
1152 bigPanel.add(panel, BorderLayout.CENTER);
1156 bigPanel.add(panel, BorderLayout.CENTER);
1159 if (lastFeatureAdded == null)
1161 if (features[0].type != null)
1163 lastFeatureAdded = features[0].type;
1167 lastFeatureAdded = "feature_1";
1171 if (lastFeatureGroupAdded == null)
1173 if (features[0].featureGroup != null)
1175 lastFeatureGroupAdded = features[0].featureGroup;
1179 lastFeatureGroupAdded = "Jalview";
1185 name.setText(lastFeatureAdded);
1186 source.setText(lastFeatureGroupAdded);
1190 name.setText(features[0].getType());
1191 source.setText(features[0].getFeatureGroup());
1194 start.setValue(new Integer(features[0].getBegin()));
1195 end.setValue(new Integer(features[0].getEnd()));
1196 description.setText(features[0].getDescription());
1197 updateColourButton(bigPanel, colour,
1198 (oldcol = fcol = getFeatureStyle(name.getText())));
1202 options = new Object[]
1203 { "Amend", "Delete", "Cancel" };
1207 options = new Object[]
1211 String title = newFeatures ? "Create New Sequence Feature(s)"
1212 : "Amend/Delete Features for " + sequences[0].getName();
1214 int reply = JOptionPane.showInternalOptionDialog(Desktop.desktop,
1215 bigPanel, title, JOptionPane.YES_NO_CANCEL_OPTION,
1216 JOptionPane.QUESTION_MESSAGE, null, options, "OK");
1218 jalview.io.FeaturesFile ffile = new jalview.io.FeaturesFile();
1220 if (reply == JOptionPane.OK_OPTION && name.getText().length() > 0)
1222 // This ensures that the last sequence
1223 // is refreshed and new features are rendered
1225 lastFeatureAdded = name.getText().trim();
1226 lastFeatureGroupAdded = source.getText().trim();
1227 lastDescriptionAdded = description.getText().replaceAll("\n", " ");
1228 // TODO: determine if the null feature group is valid
1229 if (lastFeatureGroupAdded.length() < 1)
1230 lastFeatureGroupAdded = null;
1235 SequenceFeature sf = features[featureIndex];
1237 if (reply == JOptionPane.NO_OPTION)
1239 sequences[0].getDatasetSequence().deleteFeature(sf);
1241 else if (reply == JOptionPane.YES_OPTION)
1243 sf.type = lastFeatureAdded;
1244 sf.featureGroup = lastFeatureGroupAdded;
1245 sf.description = lastDescriptionAdded;
1247 setColour(sf.type, fcol);
1248 av.featuresDisplayed.put(sf.type, getColour(sf.type));
1252 sf.begin = ((Integer) start.getValue()).intValue();
1253 sf.end = ((Integer) end.getValue()).intValue();
1254 } catch (NumberFormatException ex)
1258 ffile.parseDescriptionHTML(sf, false);
1262 // NEW FEATURES ADDED
1264 if (reply == JOptionPane.OK_OPTION && lastFeatureAdded.length() > 0)
1266 for (int i = 0; i < sequences.length; i++)
1268 features[i].type = lastFeatureAdded;
1269 if (lastFeatureGroupAdded != null)
1270 features[i].featureGroup = lastFeatureGroupAdded;
1271 features[i].description = lastDescriptionAdded;
1272 sequences[i].addSequenceFeature(features[i]);
1273 ffile.parseDescriptionHTML(features[i], false);
1276 if (av.featuresDisplayed == null)
1278 av.featuresDisplayed = new Hashtable();
1281 if (lastFeatureGroupAdded != null)
1283 if (featureGroups == null)
1284 featureGroups = new Hashtable();
1285 featureGroups.put(lastFeatureGroupAdded, new Boolean(true));
1287 setColour(lastFeatureAdded, fcol);
1288 av.featuresDisplayed.put(lastFeatureAdded,
1289 getColour(lastFeatureAdded));
1291 findAllFeatures(false);
1293 ap.paintAlignment(true);
1303 ap.paintAlignment(true);
1309 * update the amend feature button dependent on the given style
1315 protected void updateColourButton(JPanel bigPanel, JLabel colour,
1319 colour.setIcon(null);
1320 colour.setToolTipText(null);
1323 if (col2 instanceof Color)
1325 colour.setBackground((Color) col2);
1329 colour.setBackground(bigPanel.getBackground());
1330 colour.setForeground(Color.black);
1331 FeatureSettings.renderGraduatedColor(colour, (GraduatedColor) col2);
1332 // colour.setForeground(colour.getBackground());
1336 public void setColour(String featureType, Object col)
1339 // Color _col = (col instanceof Color) ? ((Color) col) : (col instanceof
1340 // GraduatedColor) ? ((GraduatedColor) col).getMaxColor() : null;
1341 // Object c = featureColours.get(featureType);
1342 // if (c == null || c instanceof Color || (c instanceof GraduatedColor &&
1343 // !((GraduatedColor)c).getMaxColor().equals(_col)))
1345 featureColours.put(featureType, col);
1349 public void setTransparency(float value)
1351 transparency = value;
1354 public float getTransparency()
1356 return transparency;
1360 * Replace current ordering with new ordering
1363 * { String(Type), Colour(Type), Boolean(Displayed) }
1365 public void setFeaturePriority(Object[][] data)
1367 setFeaturePriority(data, true);
1373 * { String(Type), Colour(Type), Boolean(Displayed) }
1375 * when true current featureDisplay list will be cleared
1377 public void setFeaturePriority(Object[][] data, boolean visibleNew)
1381 if (av.featuresDisplayed != null)
1383 av.featuresDisplayed.clear();
1387 av.featuresDisplayed = new Hashtable();
1395 // The feature table will display high priority
1396 // features at the top, but theses are the ones
1397 // we need to render last, so invert the data
1398 renderOrder = new String[data.length];
1400 if (data.length > 0)
1402 for (int i = 0; i < data.length; i++)
1404 String type = data[i][0].toString();
1405 setColour(type, data[i][1]); // todo : typesafety - feature color
1407 if (((Boolean) data[i][2]).booleanValue())
1409 av.featuresDisplayed.put(type, new Integer(getColour(type)
1413 renderOrder[data.length - i - 1] = type;
1419 Map featureOrder = null;
1422 * analogous to colour - store a normalized ordering for all feature types in
1423 * this rendering context.
1426 * Feature type string
1428 * normalized priority - 0 means always appears on top, 1 means
1431 public float setOrder(String type, float position)
1433 if (featureOrder == null)
1435 featureOrder = new Hashtable();
1437 featureOrder.put(type, new Float(position));
1442 * get the global priority (0 (top) to 1 (bottom))
1445 * @return [0,1] or -1 for a type without a priority
1447 public float getOrder(String type)
1449 if (featureOrder != null)
1451 if (featureOrder.containsKey(type))
1453 return ((Float) featureOrder.get(type)).floatValue();
1461 * @see java.beans.PropertyChangeSupport#addPropertyChangeListener(java.beans.PropertyChangeListener)
1463 public void addPropertyChangeListener(PropertyChangeListener listener)
1465 changeSupport.addPropertyChangeListener(listener);
1470 * @see java.beans.PropertyChangeSupport#removePropertyChangeListener(java.beans.PropertyChangeListener)
1472 public void removePropertyChangeListener(PropertyChangeListener listener)
1474 changeSupport.removePropertyChangeListener(listener);