2 * Jalview - A Sequence Alignment Editor and Viewer (Version 2.4)
3 * Copyright (C) 2008 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.*;
26 import java.beans.PropertyChangeListener;
27 import java.beans.PropertyChangeSupport;
31 import jalview.datamodel.*;
39 public class FeatureRenderer
47 * global transparency for feature
49 float transparency = 1.0f;
55 Hashtable featureColours = new Hashtable();
57 // A higher level for grouping features of a
59 Hashtable featureGroups = new Hashtable();
61 // This is actually an Integer held in the hashtable,
62 // Retrieved using the key feature type
67 PropertyChangeSupport changeSupport = new PropertyChangeSupport(this);
72 * Creates a new FeatureRenderer object.
77 public FeatureRenderer(AlignmentPanel ap)
83 public class FeatureRendererSettings implements Cloneable
87 Hashtable featureGroups;
89 Hashtable featureColours;
93 Hashtable featureOrder;
95 public FeatureRendererSettings(String[] renderOrder,
96 Hashtable featureGroups, Hashtable featureColours,
97 float transparency, Hashtable featureOrder)
100 this.renderOrder = renderOrder;
101 this.featureGroups = featureGroups;
102 this.featureColours = featureColours;
103 this.transparency = transparency;
104 this.featureOrder = featureOrder;
107 public FeatureRendererSettings(FeatureRenderer fr)
109 this.renderOrder = fr.renderOrder;
110 this.featureGroups = fr.featureGroups;
111 this.featureColours = fr.featureColours;
112 this.transparency = fr.transparency;
113 this.featureOrder = fr.featureOrder;
117 public FeatureRendererSettings getSettings()
119 return new FeatureRendererSettings(this);
122 public void transferSettings(FeatureRendererSettings fr)
124 this.renderOrder = fr.renderOrder;
125 this.featureGroups = fr.featureGroups;
126 this.featureColours = fr.featureColours;
127 this.transparency = fr.transparency;
128 this.featureOrder = fr.featureOrder;
131 public void transferSettings(FeatureRenderer fr)
133 this.renderOrder = fr.renderOrder;
134 this.featureGroups = fr.featureGroups;
135 this.featureColours = fr.featureColours;
136 this.transparency = fr.transparency;
137 this.featureOrder = fr.featureOrder;
140 BufferedImage offscreenImage;
142 boolean offscreenRender = false;
144 public Color findFeatureColour(Color initialCol, SequenceI seq, int res)
146 return new Color(findFeatureColour(initialCol.getRGB(), seq, res));
150 * This is used by the Molecule Viewer and Overview to get the accurate
151 * colourof the rendered sequence
153 public int findFeatureColour(int initialCol, SequenceI seq, int column)
155 if (!av.showSequenceFeatures)
163 sequenceFeatures = lastSeq.getDatasetSequence().getSequenceFeatures();
164 if (sequenceFeatures != null)
166 sfSize = sequenceFeatures.length;
170 if (sequenceFeatures != lastSeq.getDatasetSequence()
171 .getSequenceFeatures())
173 sequenceFeatures = lastSeq.getDatasetSequence().getSequenceFeatures();
174 if (sequenceFeatures != null)
176 sfSize = sequenceFeatures.length;
180 if (sequenceFeatures == null || sfSize == 0)
185 if (jalview.util.Comparison.isGap(lastSeq.getCharAt(column)))
187 return Color.white.getRGB();
190 // Only bother making an offscreen image if transparency is applied
191 if (transparency != 1.0f && offscreenImage == null)
193 offscreenImage = new BufferedImage(1, 1, BufferedImage.TYPE_INT_ARGB);
196 currentColour = null;
198 offscreenRender = true;
200 if (offscreenImage != null)
202 offscreenImage.setRGB(0, 0, initialCol);
203 drawSequence(offscreenImage.getGraphics(), lastSeq, column, column, 0);
205 return offscreenImage.getRGB(0, 0);
209 drawSequence(null, lastSeq, lastSeq.findPosition(column), -1, -1);
211 if (currentColour == null)
217 return ((Integer) currentColour).intValue();
246 // SequenceFeature sf;
249 SequenceFeature[] sequenceFeatures;
251 int sfSize, sfindex, spos, epos;
254 * show scores as heights
256 protected boolean varyHeight=false;
258 synchronized public void drawSequence(Graphics g, SequenceI seq,
259 int start, int end, int y1)
262 if (seq.getDatasetSequence().getSequenceFeatures() == null
263 || seq.getDatasetSequence().getSequenceFeatures().length == 0)
270 fm = g.getFontMetrics();
273 if (av.featuresDisplayed == null || renderOrder == null
277 if (av.featuresDisplayed.size() < 1)
282 sequenceFeatures = seq.getDatasetSequence().getSequenceFeatures();
287 || seq.getDatasetSequence().getSequenceFeatures() != sequenceFeatures)
290 sequenceFeatures = seq.getDatasetSequence().getSequenceFeatures();
293 if (transparency != 1 && g != null)
295 Graphics2D g2 = (Graphics2D) g;
296 g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER,
300 if (!offscreenRender)
302 spos = lastSeq.findPosition(start);
303 epos = lastSeq.findPosition(end);
306 sfSize = sequenceFeatures.length;
308 for (int renderIndex = 0; renderIndex < renderOrder.length; renderIndex++)
310 type = renderOrder[renderIndex];
312 if (type == null || !av.featuresDisplayed.containsKey(type))
317 // loop through all features in sequence to find
318 // current feature to render
319 for (sfindex = 0; sfindex < sfSize; sfindex++)
321 if (!sequenceFeatures[sfindex].type.equals(type))
326 if (featureGroups != null
327 && sequenceFeatures[sfindex].featureGroup != null
328 && sequenceFeatures[sfindex].featureGroup.length() != 0
330 .containsKey(sequenceFeatures[sfindex].featureGroup)
331 && !((Boolean) featureGroups
332 .get(sequenceFeatures[sfindex].featureGroup))
339 && (sequenceFeatures[sfindex].getBegin() > epos || sequenceFeatures[sfindex]
345 if (offscreenRender && offscreenImage == null)
347 if (sequenceFeatures[sfindex].begin <= start
348 && sequenceFeatures[sfindex].end >= start)
350 currentColour = av.featuresDisplayed
351 .get(sequenceFeatures[sfindex].type);
354 else if (sequenceFeatures[sfindex].type.equals("disulfide bond"))
360 seq.findIndex(sequenceFeatures[sfindex].begin) - 1,
361 seq.findIndex(sequenceFeatures[sfindex].begin) - 1,
362 new Color(((Integer) av.featuresDisplayed
363 .get(sequenceFeatures[sfindex].type)).intValue()),
368 seq.findIndex(sequenceFeatures[sfindex].end) - 1,
369 seq.findIndex(sequenceFeatures[sfindex].end) - 1,
370 new Color(((Integer) av.featuresDisplayed
371 .get(sequenceFeatures[sfindex].type)).intValue()),
376 if (av.showSeqFeaturesHeight && sequenceFeatures[sfindex].score!=Float.NaN)
378 renderScoreFeature(g, seq, seq
379 .findIndex(sequenceFeatures[sfindex].begin) - 1, seq
380 .findIndex(sequenceFeatures[sfindex].end) - 1,
381 getColour(sequenceFeatures[sfindex].type), start, end, y1, normaliseScore(sequenceFeatures[sfindex]));
384 renderFeature(g, seq, seq
385 .findIndex(sequenceFeatures[sfindex].begin) - 1, seq
386 .findIndex(sequenceFeatures[sfindex].end) - 1,
387 getColour(sequenceFeatures[sfindex].type), start, end, y1);
394 if (transparency != 1.0f && g != null)
396 Graphics2D g2 = (Graphics2D) g;
397 g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER,
401 Hashtable minmax = new Hashtable();
403 * normalise a score against the max/min bounds for the feature type.
404 * @param sequenceFeature
405 * @return byte[] { signed, normalised signed (-127 to 127) or unsigned (0-255) value.
407 private final byte[] normaliseScore(SequenceFeature sequenceFeature)
409 float[] mm = (float[]) minmax.get(sequenceFeature.type);
410 final byte[] r=new byte[] { 0, (byte) 255};
413 if (r[0]!=0 || mm[0]<0.0)
416 r[1]=(byte) ((int) 128.0+127.0*(sequenceFeature.score/mm[1]));
418 r[1]=(byte) ((int) 255.0*(sequenceFeature.score/mm[1]));
428 void renderFeature(Graphics g, SequenceI seq, int fstart, int fend,
429 Color featureColour, int start, int end, int y1)
432 if (((fstart <= end) && (fend >= start)))
435 { // fix for if the feature we have starts before the sequence start,
436 fstart = start; // but the feature end is still valid!!
443 int pady = (y1 + av.charHeight) - av.charHeight / 5;
444 for (i = fstart; i <= fend; i++)
446 s = seq.getCharAt(i);
448 if (jalview.util.Comparison.isGap(s))
453 g.setColor(featureColour);
455 g.fillRect((i - start) * av.charWidth, y1, av.charWidth,
458 if (offscreenRender || !av.validCharWidth)
463 g.setColor(Color.white);
464 charOffset = (av.charWidth - fm.charWidth(s)) / 2;
465 g.drawString(String.valueOf(s), charOffset
466 + (av.charWidth * (i - start)), pady);
471 void renderScoreFeature(Graphics g, SequenceI seq, int fstart, int fend,
472 Color featureColour, int start, int end, int y1, byte[] bs)
475 if (((fstart <= end) && (fend >= start)))
478 { // fix for if the feature we have starts before the sequence start,
479 fstart = start; // but the feature end is still valid!!
486 int pady = (y1 + av.charHeight) - av.charHeight / 5;
487 int ystrt = 0,yend=av.charHeight;
490 // signed - zero is always middle of residue line.
493 yend = av.charHeight*(128-bs[1])/512;
494 ystrt = av.charHeight-yend/2;
496 ystrt = av.charHeight/2;
497 yend = av.charHeight*(bs[1]-128)/512;
500 yend = av.charHeight*bs[1]/255;
501 ystrt = av.charHeight-yend;
504 for (i = fstart; i <= fend; i++)
506 s = seq.getCharAt(i);
508 if (jalview.util.Comparison.isGap(s))
513 g.setColor(featureColour);
514 int x=(i-start)*av.charWidth;
515 g.drawRect(x, y1, av.charWidth, av.charHeight);
516 g.fillRect(x, y1+ystrt, av.charWidth,
519 if (offscreenRender || !av.validCharWidth)
524 g.setColor(Color.black);
525 charOffset = (av.charWidth - fm.charWidth(s)) / 2;
526 g.drawString(String.valueOf(s), charOffset
527 + (av.charWidth * (i - start)), pady);
533 boolean newFeatureAdded = false;
536 * Called when alignment in associated view has new/modified features to
537 * discover and display.
540 public void featuresAdded()
546 boolean findingFeatures = false;
549 * search the alignment for all new features, give them a colour and display
550 * them. Then fires a PropertyChangeEvent on the changeSupport object.
553 void findAllFeatures()
555 synchronized (firing)
557 if (firing.equals(Boolean.FALSE))
559 firing = Boolean.TRUE;
560 findAllFeatures(true); // add all new features as visible
561 changeSupport.firePropertyChange("changeSupport", null, null);
562 firing = Boolean.FALSE;
568 * Searches alignment for all features and updates colours
570 * @param newMadeVisible
571 * if true newly added feature types will be rendered
574 synchronized void findAllFeatures(boolean newMadeVisible)
576 newFeatureAdded = false;
580 newFeatureAdded = true;
584 findingFeatures = true;
586 if (av.featuresDisplayed == null)
588 av.featuresDisplayed = new Hashtable();
591 allfeatures = new Vector();
592 Vector oldfeatures = new Vector();
593 if (renderOrder != null)
595 for (int i = 0; i < renderOrder.length; i++)
597 if (renderOrder[i] != null)
599 oldfeatures.addElement(renderOrder[i]);
605 minmax = new Hashtable();
607 for (int i = 0; i < av.alignment.getHeight(); i++)
609 SequenceFeature[] features = av.alignment.getSequenceAt(i)
610 .getDatasetSequence().getSequenceFeatures();
612 if (features == null)
618 while (index < features.length)
620 if (!av.featuresDisplayed.containsKey(features[index].getType()))
623 if (featureGroups.containsKey(features[index].getType()))
625 boolean visible = ((Boolean) featureGroups
626 .get(features[index].featureGroup)).booleanValue();
635 if (!(features[index].begin == 0 && features[index].end == 0))
637 // If beginning and end are 0, the feature is for the whole sequence
638 // and we don't want to render the feature in the normal way
641 && !oldfeatures.contains(features[index].getType()))
643 // this is a new feature type on the alignment. Mark it for
645 av.featuresDisplayed.put(features[index].getType(),
646 new Integer(getColour(features[index].getType())
648 setOrder(features[index].getType(), 0);
652 if (!allfeatures.contains(features[index].getType()))
654 allfeatures.addElement(features[index].getType());
656 if (features[index].score!=Float.NaN)
658 float[] mm = (float[]) minmax.get(features[index].getType());
661 mm = new float[] { features[index].score,features[index].score};
662 minmax.put(features[index].getType(), mm);
664 if (mm[0]>features[index].score)
666 mm[0] = features[index].score;
668 if (mm[1]<features[index].score)
670 mm[1] = features[index].score;
677 updateRenderOrder(allfeatures);
678 findingFeatures = false;
681 protected Boolean firing = Boolean.FALSE;
684 * replaces the current renderOrder with the unordered features in
685 * allfeatures. The ordering of any types in both renderOrder and allfeatures
686 * is preserved, and all new feature types are rendered on top of the existing
687 * types, in the order given by getOrder or the order given in allFeatures.
688 * Note. this operates directly on the featureOrder hash for efficiency. TODO:
689 * eliminate the float storage for computing/recalling the persistent ordering
693 private void updateRenderOrder(Vector allFeatures)
695 Vector allfeatures = new Vector(allFeatures);
696 String[] oldRender = renderOrder;
697 renderOrder = new String[allfeatures.size()];
698 boolean initOrders = (featureOrder == null);
700 if (oldRender != null && oldRender.length > 0)
702 for (int j = 0; j < oldRender.length; j++)
704 if (oldRender[j] != null)
708 setOrder(oldRender[j], (1 - (1 + (float) j)
709 / (float) oldRender.length));
711 if (allfeatures.contains(oldRender[j]))
713 renderOrder[opos++] = oldRender[j]; // existing features always
714 // appear below new features
715 allfeatures.removeElement(oldRender[j]);
720 if (allfeatures.size() == 0)
722 // no new features - leave order unchanged.
725 int i = allfeatures.size() - 1;
727 boolean sort = false;
728 String[] newf = new String[allfeatures.size()];
729 float[] sortOrder = new float[allfeatures.size()];
730 Enumeration en = allfeatures.elements();
731 // sort remaining elements
732 while (en.hasMoreElements())
734 newf[i] = en.nextElement().toString();
735 if (initOrders || !featureOrder.containsKey(newf[i]))
737 int denom = initOrders ? allfeatures.size() : featureOrder.size();
738 // new unordered feature - compute persistent ordering at head of
739 // existing features.
740 setOrder(newf[i], i / (float) denom);
742 // set order from newly found feature from persisted ordering.
743 sortOrder[i] = 2 - ((Float) featureOrder.get(newf[i])).floatValue();
746 // only sort if we need to
747 sort = sort || sortOrder[i] > sortOrder[i + 1];
751 if (iSize > 1 && sort)
752 jalview.util.QuickSort.sort(sortOrder, newf);
754 System.arraycopy(newf, 0, renderOrder, opos, newf.length);
757 public Color getColour(String featureType)
759 if (!featureColours.containsKey(featureType))
761 jalview.schemes.UserColourScheme ucs = new jalview.schemes.UserColourScheme();
762 Color col = ucs.createColourFromName(featureType);
763 featureColours.put(featureType, col);
767 return (Color) featureColours.get(featureType);
770 static String lastFeatureAdded;
772 static String lastFeatureGroupAdded;
774 static String lastDescriptionAdded;
776 int featureIndex = 0;
778 boolean amendFeatures(final SequenceI[] sequences,
779 final SequenceFeature[] features, boolean newFeatures,
780 final AlignmentPanel ap)
785 JPanel bigPanel = new JPanel(new BorderLayout());
786 final JComboBox overlaps;
787 final JTextField name = new JTextField(25);
788 final JTextField source = new JTextField(25);
789 final JTextArea description = new JTextArea(3, 25);
790 final JSpinner start = new JSpinner();
791 final JSpinner end = new JSpinner();
792 start.setPreferredSize(new Dimension(80, 20));
793 end.setPreferredSize(new Dimension(80, 20));
795 final JPanel colour = new JPanel();
796 colour.setBorder(BorderFactory.createEtchedBorder());
797 colour.setMaximumSize(new Dimension(40, 10));
798 colour.addMouseListener(new MouseAdapter()
800 public void mousePressed(MouseEvent evt)
802 Color col = JColorChooser.showDialog(Desktop.desktop,
803 "Select Feature Colour", colour.getBackground());
805 colour.setBackground(col);
810 JPanel tmp = new JPanel();
811 JPanel panel = new JPanel(new GridLayout(3, 1));
813 // /////////////////////////////////////
814 // /MULTIPLE FEATURES AT SELECTED RESIDUE
815 if (!newFeatures && features.length > 1)
817 panel = new JPanel(new GridLayout(4, 1));
819 tmp.add(new JLabel("Select Feature: "));
820 overlaps = new JComboBox();
821 for (int i = 0; i < features.length; i++)
823 overlaps.addItem(features[i].getType() + "/"
824 + features[i].getBegin() + "-" + features[i].getEnd()
825 + " (" + features[i].getFeatureGroup() + ")");
830 overlaps.addItemListener(new ItemListener()
832 public void itemStateChanged(ItemEvent e)
834 int index = overlaps.getSelectedIndex();
837 featureIndex = index;
838 name.setText(features[index].getType());
839 description.setText(features[index].getDescription());
840 source.setText(features[index].getFeatureGroup());
841 start.setValue(new Integer(features[index].getBegin()));
842 end.setValue(new Integer(features[index].getEnd()));
844 SearchResults highlight = new SearchResults();
845 highlight.addResult(sequences[0], features[index].getBegin(),
846 features[index].getEnd());
848 ap.seqPanel.seqCanvas.highlightSearchResults(highlight);
851 Color col = getColour(name.getText());
854 col = new jalview.schemes.UserColourScheme()
855 .createColourFromName(name.getText());
858 colour.setBackground(col);
865 // ////////////////////////////////////
869 tmp.add(new JLabel("Name: ", JLabel.RIGHT));
874 tmp.add(new JLabel("Group: ", JLabel.RIGHT));
879 tmp.add(new JLabel("Colour: ", JLabel.RIGHT));
881 colour.setPreferredSize(new Dimension(150, 15));
883 bigPanel.add(panel, BorderLayout.NORTH);
885 panel = new JPanel();
886 panel.add(new JLabel("Description: ", JLabel.RIGHT));
887 description.setFont(new java.awt.Font("Verdana", Font.PLAIN, 11));
888 description.setLineWrap(true);
889 panel.add(new JScrollPane(description));
893 bigPanel.add(panel, BorderLayout.SOUTH);
895 panel = new JPanel();
896 panel.add(new JLabel(" Start:", JLabel.RIGHT));
898 panel.add(new JLabel(" End:", JLabel.RIGHT));
900 bigPanel.add(panel, BorderLayout.CENTER);
904 bigPanel.add(panel, BorderLayout.CENTER);
907 if (lastFeatureAdded == null)
909 if (features[0].type != null)
911 lastFeatureAdded = features[0].type;
915 lastFeatureAdded = "feature_1";
919 if (lastFeatureGroupAdded == null)
921 if (features[0].featureGroup != null)
923 lastFeatureGroupAdded = features[0].featureGroup;
927 lastFeatureGroupAdded = "Jalview";
933 name.setText(lastFeatureAdded);
934 source.setText(lastFeatureGroupAdded);
938 name.setText(features[0].getType());
939 source.setText(features[0].getFeatureGroup());
942 start.setValue(new Integer(features[0].getBegin()));
943 end.setValue(new Integer(features[0].getEnd()));
944 description.setText(features[0].getDescription());
945 colour.setBackground(getColour(name.getText()));
950 options = new Object[]
951 { "Amend", "Delete", "Cancel" };
955 options = new Object[]
959 String title = newFeatures ? "Create New Sequence Feature(s)"
960 : "Amend/Delete Features for " + sequences[0].getName();
962 int reply = JOptionPane.showInternalOptionDialog(Desktop.desktop,
963 bigPanel, title, JOptionPane.YES_NO_CANCEL_OPTION,
964 JOptionPane.QUESTION_MESSAGE, null, options, "OK");
966 jalview.io.FeaturesFile ffile = new jalview.io.FeaturesFile();
968 if (reply == JOptionPane.OK_OPTION && name.getText().length() > 0)
970 // This ensures that the last sequence
971 // is refreshed and new features are rendered
973 lastFeatureAdded = name.getText().trim();
974 lastFeatureGroupAdded = source.getText().trim();
975 lastDescriptionAdded = description.getText().replaceAll("\n", " ");
977 if (lastFeatureGroupAdded.length() < 1)
978 lastFeatureGroupAdded = null;
983 SequenceFeature sf = features[featureIndex];
985 if (reply == JOptionPane.NO_OPTION)
987 sequences[0].getDatasetSequence().deleteFeature(sf);
989 else if (reply == JOptionPane.YES_OPTION)
991 sf.type = lastFeatureAdded;
992 sf.featureGroup = lastFeatureGroupAdded;
993 sf.description = lastDescriptionAdded;
995 setColour(sf.type, colour.getBackground());
996 av.featuresDisplayed.put(sf.type, new Integer(colour
997 .getBackground().getRGB()));
1001 sf.begin = ((Integer) start.getValue()).intValue();
1002 sf.end = ((Integer) end.getValue()).intValue();
1003 } catch (NumberFormatException ex)
1007 ffile.parseDescriptionHTML(sf, false);
1011 // NEW FEATURES ADDED
1013 if (reply == JOptionPane.OK_OPTION && lastFeatureAdded.length() > 0)
1015 for (int i = 0; i < sequences.length; i++)
1017 features[i].type = lastFeatureAdded;
1018 if (lastFeatureGroupAdded != null)
1019 features[i].featureGroup = lastFeatureGroupAdded;
1020 features[i].description = lastDescriptionAdded;
1021 sequences[i].addSequenceFeature(features[i]);
1022 ffile.parseDescriptionHTML(features[i], false);
1025 if (av.featuresDisplayed == null)
1027 av.featuresDisplayed = new Hashtable();
1030 if (lastFeatureGroupAdded != null)
1032 if (featureGroups == null)
1033 featureGroups = new Hashtable();
1034 featureGroups.put(lastFeatureGroupAdded, new Boolean(true));
1037 Color col = colour.getBackground();
1038 setColour(lastFeatureAdded, colour.getBackground());
1039 av.featuresDisplayed.put(lastFeatureAdded,
1040 new Integer(col.getRGB()));
1042 findAllFeatures(false);
1044 ap.paintAlignment(true);
1054 ap.paintAlignment(true);
1059 public void setColour(String featureType, Color col)
1061 featureColours.put(featureType, col);
1064 public void setTransparency(float value)
1066 transparency = value;
1069 public float getTransparency()
1071 return transparency;
1075 * Replace current ordering with new ordering
1078 * String(Type), Colour(Type), Boolean(Displayed) }
1080 public void setFeaturePriority(Object[][] data)
1082 setFeaturePriority(data, true);
1088 * String(Type), Colour(Type), Boolean(Displayed) }
1090 * when true current featureDisplay list will be cleared
1092 public void setFeaturePriority(Object[][] data, boolean visibleNew)
1096 if (av.featuresDisplayed != null)
1098 av.featuresDisplayed.clear();
1102 av.featuresDisplayed = new Hashtable();
1110 // The feature table will display high priority
1111 // features at the top, but theses are the ones
1112 // we need to render last, so invert the data
1113 renderOrder = new String[data.length];
1115 if (data.length > 0)
1117 for (int i = 0; i < data.length; i++)
1119 String type = data[i][0].toString();
1120 setColour(type, (Color) data[i][1]);
1121 if (((Boolean) data[i][2]).booleanValue())
1123 av.featuresDisplayed.put(type, new Integer(getColour(type)
1127 renderOrder[data.length - i - 1] = type;
1133 Hashtable featureOrder = null;
1136 * analogous to colour - store a normalized ordering for all feature types in
1137 * this rendering context.
1140 * Feature type string
1142 * normalized priority - 0 means always appears on top, 1 means
1145 public float setOrder(String type, float position)
1147 if (featureOrder == null)
1149 featureOrder = new Hashtable();
1151 featureOrder.put(type, new Float(position));
1156 * get the global priority (0 (top) to 1 (bottom))
1159 * @return [0,1] or -1 for a type without a priority
1161 public float getOrder(String type)
1163 if (featureOrder != null)
1165 if (featureOrder.containsKey(type))
1167 return ((Float) featureOrder.get(type)).floatValue();
1175 * @see java.beans.PropertyChangeSupport#addPropertyChangeListener(java.beans.PropertyChangeListener)
1177 public void addPropertyChangeListener(PropertyChangeListener listener)
1179 changeSupport.addPropertyChangeListener(listener);
1184 * @see java.beans.PropertyChangeSupport#removePropertyChangeListener(java.beans.PropertyChangeListener)
1186 public void removePropertyChangeListener(PropertyChangeListener listener)
1188 changeSupport.removePropertyChangeListener(listener);