/*
- * Jalview - A Sequence Alignment Editor and Viewer (Development Version 2.4.1)
- * Copyright (C) 2009 AM Waterhouse, J Procter, G Barton, M Clamp, S Searle
+ * Jalview - A Sequence Alignment Editor and Viewer (Version 2.7)
+ * Copyright (C) 2011 J Procter, AM Waterhouse, J Engelhardt, LM Lui, G Barton, M Clamp, S Searle
*
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
+ * This file is part of Jalview.
*
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
+ * Jalview is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
*
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
+ * Jalview is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ * PURPOSE. See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with Jalview. If not, see <http://www.gnu.org/licenses/>.
*/
package jalview.gui;
import java.util.*;
+import java.util.concurrent.ConcurrentHashMap;
import java.awt.*;
import java.awt.event.*;
* @author $author$
* @version $Revision$
*/
-public class FeatureRenderer
+public class FeatureRenderer implements jalview.api.FeatureRenderer
{
AlignmentPanel ap;
FontMetrics fm;
int charOffset;
-
- Hashtable featureColours = new Hashtable();
+
+ Map featureColours = new ConcurrentHashMap();
// A higher level for grouping features of a
// particular type
- Hashtable featureGroups = new Hashtable();
+ Map featureGroups = new ConcurrentHashMap();
// This is actually an Integer held in the hashtable,
// Retrieved using the key feature type
* Creates a new FeatureRenderer object.
*
* @param av
- * DOCUMENT ME!
+ * DOCUMENT ME!
*/
public FeatureRenderer(AlignmentPanel ap)
{
{
String[] renderOrder;
- Hashtable featureGroups;
+ Map featureGroups;
- Hashtable featureColours;
+ Map featureColours;
float transparency;
- Hashtable featureOrder;
+ Map featureOrder;
public FeatureRendererSettings(String[] renderOrder,
Hashtable featureGroups, Hashtable featureColours,
this.featureOrder = featureOrder;
}
+ /**
+ * create an independent instance of the feature renderer settings
+ *
+ * @param fr
+ */
public FeatureRendererSettings(FeatureRenderer fr)
{
- this.renderOrder = fr.renderOrder;
- this.featureGroups = fr.featureGroups;
- this.featureColours = fr.featureColours;
+ renderOrder = null;
+ featureGroups = new ConcurrentHashMap();
+ featureColours = new ConcurrentHashMap();
+ featureOrder = new ConcurrentHashMap();
+ if (fr.renderOrder != null)
+ {
+ this.renderOrder = new String[fr.renderOrder.length];
+ System.arraycopy(fr.renderOrder, 0, renderOrder, 0,
+ fr.renderOrder.length);
+ }
+ if (fr.featureGroups != null)
+ {
+ this.featureGroups = new ConcurrentHashMap(fr.featureGroups);
+ }
+ if (fr.featureColours != null)
+ {
+ this.featureColours = new ConcurrentHashMap(fr.featureColours);
+ }
+ Iterator en = fr.featureColours.keySet().iterator();
+ while (en.hasNext())
+ {
+ Object next = en.next();
+ Object val = featureColours.get(next);
+ if (val instanceof GraduatedColor)
+ {
+ featureColours
+ .put(next, new GraduatedColor((GraduatedColor) val));
+ }
+ }
this.transparency = fr.transparency;
- this.featureOrder = fr.featureOrder;
+ if (fr.featureOrder != null)
+ {
+ this.featureOrder = new ConcurrentHashMap(fr.featureOrder);
+ }
}
}
public void transferSettings(FeatureRenderer fr)
{
- this.renderOrder = fr.renderOrder;
- this.featureGroups = fr.featureGroups;
- this.featureColours = fr.featureColours;
- this.transparency = fr.transparency;
- this.featureOrder = fr.featureOrder;
+ FeatureRendererSettings frs = new FeatureRendererSettings(fr);
+ this.renderOrder = frs.renderOrder;
+ this.featureGroups = frs.featureGroups;
+ this.featureColours = frs.featureColours;
+ this.transparency = frs.transparency;
+ this.featureOrder = frs.featureOrder;
+ if (av != null && av!=fr.av)
+ {
+ // copy over the displayed feature settings
+ if (fr.av != null)
+ {
+ if (fr.av.featuresDisplayed != null)
+ {
+ // update display settings
+ if (av.featuresDisplayed == null)
+ {
+ av.featuresDisplayed = new Hashtable(fr.av.featuresDisplayed);
+ }
+ else
+ {
+ av.featuresDisplayed.clear();
+ Enumeration en = fr.av.featuresDisplayed.keys();
+ while (en.hasMoreElements())
+ {
+ av.featuresDisplayed.put(en.nextElement(), Boolean.TRUE);
+ }
+
+ }
+ }
+ }
+ }
}
BufferedImage offscreenImage;
* This is used by the Molecule Viewer and Overview to get the accurate
* colourof the rendered sequence
*/
- public int findFeatureColour(int initialCol, SequenceI seq, int column)
+ public synchronized int findFeatureColour(int initialCol, SequenceI seq, int column)
{
if (!av.showSequenceFeatures)
{
}
currentColour = null;
-
+ // TODO: non-threadsafe - each rendering thread needs its own instance of the feature renderer - or this should be synchronized.
offscreenRender = true;
if (offscreenImage != null)
* DOCUMENT ME!
*
* @param g
- * DOCUMENT ME!
+ * DOCUMENT ME!
* @param seq
- * DOCUMENT ME!
+ * DOCUMENT ME!
* @param sg
- * DOCUMENT ME!
+ * DOCUMENT ME!
* @param start
- * DOCUMENT ME!
+ * DOCUMENT ME!
* @param end
- * DOCUMENT ME!
+ * DOCUMENT ME!
* @param x1
- * DOCUMENT ME!
+ * DOCUMENT ME!
* @param y1
- * DOCUMENT ME!
+ * DOCUMENT ME!
* @param width
- * DOCUMENT ME!
+ * DOCUMENT ME!
* @param height
- * DOCUMENT ME!
+ * DOCUMENT ME!
*/
// String type;
// SequenceFeature sf;
if (sequenceFeatures[sfindex].begin <= start
&& sequenceFeatures[sfindex].end >= start)
{
- // this is passed out to the overview and other sequence renderers (e.g. molecule viewer) to get displayed colour for rendered sequence
- currentColour = new Integer(getColour(sequenceFeatures[sfindex]).getRGB());
- // used to be retreived from av.featuresDisplayed
+ // this is passed out to the overview and other sequence renderers
+ // (e.g. molecule viewer) to get displayed colour for rendered
+ // sequence
+ currentColour = new Integer(
+ getColour(sequenceFeatures[sfindex]).getRGB());
+ // used to be retreived from av.featuresDisplayed
// currentColour = av.featuresDisplayed
- // .get(sequenceFeatures[sfindex].type);
-
-
+ // .get(sequenceFeatures[sfindex].type);
+
}
}
else if (sequenceFeatures[sfindex].type.equals("disulfide bond"))
{
- renderFeature(
- g,
- seq,
+ renderFeature(g, seq,
seq.findIndex(sequenceFeatures[sfindex].begin) - 1,
seq.findIndex(sequenceFeatures[sfindex].begin) - 1,
getColour(sequenceFeatures[sfindex])
- //new Color(((Integer) av.featuresDisplayed
- // .get(sequenceFeatures[sfindex].type)).intValue())
- ,
- start, end, y1);
- renderFeature(
- g,
- seq,
+ // new Color(((Integer) av.featuresDisplayed
+ // .get(sequenceFeatures[sfindex].type)).intValue())
+ , start, end, y1);
+ renderFeature(g, seq,
seq.findIndex(sequenceFeatures[sfindex].end) - 1,
seq.findIndex(sequenceFeatures[sfindex].end) - 1,
getColour(sequenceFeatures[sfindex])
- //new Color(((Integer) av.featuresDisplayed
- // .get(sequenceFeatures[sfindex].type)).intValue())
- ,
- start, end, y1);
+ // new Color(((Integer) av.featuresDisplayed
+ // .get(sequenceFeatures[sfindex].type)).intValue())
+ , start, end, y1);
}
- else
- if (showFeature(sequenceFeatures[sfindex]))
+ else if (showFeature(sequenceFeatures[sfindex]))
+ {
+ if (av.showSeqFeaturesHeight
+ && sequenceFeatures[sfindex].score != Float.NaN)
{
- if (av.showSeqFeaturesHeight
- && sequenceFeatures[sfindex].score != Float.NaN)
- {
- renderScoreFeature(g, seq, seq
- .findIndex(sequenceFeatures[sfindex].begin) - 1,
- seq.findIndex(sequenceFeatures[sfindex].end) - 1,
- getColour(sequenceFeatures[sfindex]), start, end,
- y1, normaliseScore(sequenceFeatures[sfindex]));
- }
- else
- {
- renderFeature(g, seq, seq
- .findIndex(sequenceFeatures[sfindex].begin) - 1,
- seq.findIndex(sequenceFeatures[sfindex].end) - 1,
- getColour(sequenceFeatures[sfindex]), start, end,
- y1);
- }
+ renderScoreFeature(g, seq,
+ seq.findIndex(sequenceFeatures[sfindex].begin) - 1,
+ seq.findIndex(sequenceFeatures[sfindex].end) - 1,
+ getColour(sequenceFeatures[sfindex]), start, end, y1,
+ normaliseScore(sequenceFeatures[sfindex]));
}
+ else
+ {
+ renderFeature(g, seq,
+ seq.findIndex(sequenceFeatures[sfindex].begin) - 1,
+ seq.findIndex(sequenceFeatures[sfindex].end) - 1,
+ getColour(sequenceFeatures[sfindex]), start, end, y1);
+ }
+ }
}
* Searches alignment for all features and updates colours
*
* @param newMadeVisible
- * if true newly added feature types will be rendered
- * immediatly
+ * if true newly added feature types will be rendered immediatly
*/
synchronized void findAllFeatures(boolean newMadeVisible)
{
{
minmax = new Hashtable();
}
- for (int i = 0; i < av.alignment.getHeight(); i++)
+ AlignmentI alignment=av.getAlignment();
+ for (int i = 0; i < alignment.getHeight(); i++)
{
- SequenceFeature[] features = av.alignment.getSequenceAt(i)
+ SequenceFeature[] features = alignment.getSequenceAt(i)
.getDatasetSequence().getSequenceFeatures();
if (features == null)
}
if (features[index].score != Float.NaN)
{
- int nonpos= features[index].getBegin()>=1 ? 0 : 1;
+ int nonpos = features[index].getBegin() >= 1 ? 0 : 1;
float[][] mm = (float[][]) minmax.get(features[index].getType());
if (mm == null)
{
- mm = new float[][] {null, null };
+ mm = new float[][]
+ { null, null };
minmax.put(features[index].getType(), mm);
}
- if (mm[nonpos]==null)
- {
- mm[nonpos] = new float[] { features[index].score, features[index].score };
-
+ if (mm[nonpos] == null)
+ {
+ mm[nonpos] = new float[]
+ { features[index].score, features[index].score };
+
}
else
{
* Note. this operates directly on the featureOrder hash for efficiency. TODO:
* eliminate the float storage for computing/recalling the persistent ordering
* New Cability: updates min/max for colourscheme range if its dynamic
+ *
* @param allFeatures
*/
private void updateRenderOrder(Vector allFeatures)
Vector allfeatures = new Vector(allFeatures);
String[] oldRender = renderOrder;
renderOrder = new String[allfeatures.size()];
- Object mmrange,fc=null;
+ Object mmrange, fc = null;
boolean initOrders = (featureOrder == null);
int opos = 0;
if (oldRender != null && oldRender.length > 0)
renderOrder[opos++] = oldRender[j]; // existing features always
// appear below new features
allfeatures.removeElement(oldRender[j]);
- if (minmax!=null)
+ if (minmax != null)
{
mmrange = minmax.get(oldRender[j]);
- if (mmrange!=null)
+ if (mmrange != null)
{
fc = featureColours.get(oldRender[j]);
- if (fc!=null && fc instanceof GraduatedColor && ((GraduatedColor)fc).isAutoScale())
+ if (fc != null && fc instanceof GraduatedColor
+ && ((GraduatedColor) fc).isAutoScale())
{
- ((GraduatedColor) fc).updateBounds(((float[][])mmrange)[0][0],((float[][])mmrange)[0][1]);
+ ((GraduatedColor) fc).updateBounds(
+ ((float[][]) mmrange)[0][0],
+ ((float[][]) mmrange)[0][1]);
}
+ }
}
}
}
}
- }
}
if (allfeatures.size() == 0)
{
while (en.hasMoreElements())
{
newf[i] = en.nextElement().toString();
- if (minmax!=null)
+ if (minmax != null)
{
// update from new features minmax if necessary
mmrange = minmax.get(newf[i]);
- if (mmrange!=null)
+ if (mmrange != null)
{
fc = featureColours.get(newf[i]);
- if (fc!=null && fc instanceof GraduatedColor && ((GraduatedColor)fc).isAutoScale())
+ if (fc != null && fc instanceof GraduatedColor
+ && ((GraduatedColor) fc).isAutoScale())
{
- ((GraduatedColor) fc).updateBounds(((float[][])mmrange)[0][0],((float[][])mmrange)[0][1]);
+ ((GraduatedColor) fc).updateBounds(((float[][]) mmrange)[0][0],
+ ((float[][]) mmrange)[0][1]);
}
}
}
sortOrder = null;
System.arraycopy(newf, 0, renderOrder, opos, newf.length);
}
+
/**
- * get a feature style object for the given type string.
- * Creates a java.awt.Color for a featureType with no existing
- * colourscheme.
- * TODO: replace return type with object implementing standard abstract colour/style interface
+ * get a feature style object for the given type string. Creates a
+ * java.awt.Color for a featureType with no existing colourscheme. TODO:
+ * replace return type with object implementing standard abstract colour/style
+ * interface
+ *
* @param featureType
* @return java.awt.Color or GraduatedColor
*/
- public Object getFeatureStyle(String featureType) {
+ public Object getFeatureStyle(String featureType)
+ {
Object fc = featureColours.get(featureType);
if (fc == null)
{
}
return fc;
}
+
/**
* return a nominal colour for this feature
+ *
* @param featureType
* @return standard color, or maximum colour for graduated colourscheme
*/
public Color getColour(String featureType)
{
Object fc = getFeatureStyle(featureType);
-
+
if (fc instanceof Color)
{
return (Color) fc;
return ((GraduatedColor) fc).getMaxColor();
}
}
- throw new Error("Implementation Error: Unrecognised render object "+fc.getClass()+" for features of type "+featureType);
+ throw new Error("Implementation Error: Unrecognised render object "
+ + fc.getClass() + " for features of type " + featureType);
}
/**
- * calculate the render colour for a specific feature using current feature settings.
+ * calculate the render colour for a specific feature using current feature
+ * settings.
*
* @param feature
* @return render colour for the given feature
return ((GraduatedColor) fc).findColor(feature);
}
}
- throw new Error("Implementation Error: Unrecognised render object "+fc.getClass()+" for features of type "+feature.getType());
+ throw new Error("Implementation Error: Unrecognised render object "
+ + fc.getClass() + " for features of type " + feature.getType());
}
+
private boolean showFeature(SequenceFeature sequenceFeature)
{
Object fc = getFeatureStyle(sequenceFeature.type);
if (fc instanceof GraduatedColor)
{
return ((GraduatedColor) fc).isColored(sequenceFeature);
- } else { return true; }
+ }
+ else
+ {
+ return true;
+ }
}
+ // // /////////////
+ // // Feature Editing Dialog
+ // // Will be refactored in next release.
static String lastFeatureAdded;
static String lastFeatureGroupAdded;
static String lastDescriptionAdded;
- Object oldcol,fcol;
+
+ Object oldcol, fcol;
+
int featureIndex = 0;
boolean amendFeatures(final SequenceI[] sequences,
final FeatureRenderer me = this;
final JLabel colour = new JLabel();
colour.setOpaque(true);
- //colour.setBorder(BorderFactory.createEtchedBorder());
+ // colour.setBorder(BorderFactory.createEtchedBorder());
colour.setMaximumSize(new Dimension(30, 16));
colour.addMouseListener(new MouseAdapter()
{
- FeatureColourChooser fcc =null;
+ FeatureColourChooser fcc = null;
+
public void mousePressed(MouseEvent evt)
{
if (fcol instanceof Color)
{
- Color col = JColorChooser.showDialog(Desktop.desktop,
- "Select Feature Colour", ((Color)fcol));
- if (col != null)
- {
- fcol = col;
- updateColourButton(bigPanel,colour,col);
+ Color col = JColorChooser.showDialog(Desktop.desktop,
+ "Select Feature Colour", ((Color) fcol));
+ if (col != null)
+ {
+ fcol = col;
+ updateColourButton(bigPanel, colour, col);
+ }
}
- } else {
-
- if (fcc==null) {
- final String type = features[featureIndex].getType();
+ else
+ {
+
+ if (fcc == null)
+ {
+ final String type = features[featureIndex].getType();
fcc = new FeatureColourChooser(me, type);
fcc.setRequestFocusEnabled(true);
fcc.requestFocus();
- fcc.addActionListener(new ActionListener() {
+ fcc.addActionListener(new ActionListener()
+ {
public void actionPerformed(ActionEvent e)
{
fcol = fcc.getLastColour();
fcc = null;
setColour(type, fcol);
- updateColourButton(bigPanel,colour,fcol);
+ updateColourButton(bigPanel, colour, fcol);
}
- }
- );
-
+ });
+
}
}
}
.createColourFromName(name.getText());
}
oldcol = fcol = col;
- updateColourButton(bigPanel, colour,col);
+ updateColourButton(bigPanel, colour, col);
}
});
tmp.add(new JLabel("Colour: ", JLabel.RIGHT));
tmp.add(colour);
colour.setPreferredSize(new Dimension(150, 15));
-
+ colour.setFont(new java.awt.Font("Verdana", Font.PLAIN, 9));
+ colour.setForeground(Color.black);
+ colour.setHorizontalAlignment(SwingConstants.CENTER);
+ colour.setVerticalAlignment(SwingConstants.CENTER);
+ colour.setHorizontalTextPosition(SwingConstants.CENTER);
+ colour.setVerticalTextPosition(SwingConstants.CENTER);
bigPanel.add(panel, BorderLayout.NORTH);
panel = new JPanel();
panel.add(new JLabel("Description: ", JLabel.RIGHT));
- description.setFont(new java.awt.Font("Verdana", Font.PLAIN, 11));
+ description.setFont(JvSwingUtils.getTextAreaFont());
description.setLineWrap(true);
panel.add(new JScrollPane(description));
start.setValue(new Integer(features[0].getBegin()));
end.setValue(new Integer(features[0].getEnd()));
description.setText(features[0].getDescription());
- updateColourButton(bigPanel, colour, (oldcol = fcol = getFeatureStyle(name.getText())));
+ updateColourButton(bigPanel, colour,
+ (oldcol = fcol = getFeatureStyle(name.getText())));
Object[] options;
if (!newFeatures)
{
lastFeatureAdded = name.getText().trim();
lastFeatureGroupAdded = source.getText().trim();
lastDescriptionAdded = description.getText().replaceAll("\n", " ");
-
+ // TODO: determine if the null feature group is valid
if (lastFeatureGroupAdded.length() < 1)
lastFeatureGroupAdded = null;
}
featureGroups.put(lastFeatureGroupAdded, new Boolean(true));
}
setColour(lastFeatureAdded, fcol);
- av.featuresDisplayed.put(lastFeatureAdded, getColour(lastFeatureAdded));
+ av.featuresDisplayed.put(lastFeatureAdded,
+ getColour(lastFeatureAdded));
findAllFeatures(false);
/**
* update the amend feature button dependent on the given style
- * @param bigPanel
+ *
+ * @param bigPanel
* @param col
- * @param col2
+ * @param col2
*/
- protected void updateColourButton(JPanel bigPanel, JLabel colour, Object col2)
+ protected void updateColourButton(JPanel bigPanel, JLabel colour,
+ Object col2)
{
colour.removeAll();
colour.setIcon(null);
colour.setToolTipText(null);
colour.setText("");
- if (col2 instanceof Color) {
- colour.setBackground((Color)col2);
- } else {
+
+ if (col2 instanceof Color)
+ {
+ colour.setBackground((Color) col2);
+ }
+ else
+ {
colour.setBackground(bigPanel.getBackground());
- colour.setForeground(bigPanel.getForeground());
+ colour.setForeground(Color.black);
FeatureSettings.renderGraduatedColor(colour, (GraduatedColor) col2);
- //colour.setForeground(colour.getBackground());
+ // colour.setForeground(colour.getBackground());
}
}
public void setColour(String featureType, Object col)
{
// overwrite
-// Color _col = (col instanceof Color) ? ((Color) col) : (col instanceof GraduatedColor) ? ((GraduatedColor) col).getMaxColor() : null;
-// Object c = featureColours.get(featureType);
-// if (c == null || c instanceof Color || (c instanceof GraduatedColor && !((GraduatedColor)c).getMaxColor().equals(_col)))
+ // Color _col = (col instanceof Color) ? ((Color) col) : (col instanceof
+ // GraduatedColor) ? ((GraduatedColor) col).getMaxColor() : null;
+ // Object c = featureColours.get(featureType);
+ // if (c == null || c instanceof Color || (c instanceof GraduatedColor &&
+ // !((GraduatedColor)c).getMaxColor().equals(_col)))
{
featureColours.put(featureType, col);
}
/**
* Replace current ordering with new ordering
*
- * @param data {
- * String(Type), Colour(Type), Boolean(Displayed) }
+ * @param data
+ * { String(Type), Colour(Type), Boolean(Displayed) }
*/
public void setFeaturePriority(Object[][] data)
{
/**
*
- * @param data {
- * String(Type), Colour(Type), Boolean(Displayed) }
+ * @param data
+ * { String(Type), Colour(Type), Boolean(Displayed) }
* @param visibleNew
- * when true current featureDisplay list will be cleared
+ * when true current featureDisplay list will be cleared
*/
public void setFeaturePriority(Object[][] data, boolean visibleNew)
{
for (int i = 0; i < data.length; i++)
{
String type = data[i][0].toString();
- setColour(type, data[i][1]); // todo : typesafety - feature color interface object
+ setColour(type, data[i][1]); // todo : typesafety - feature color
+ // interface object
if (((Boolean) data[i][2]).booleanValue())
{
av.featuresDisplayed.put(type, new Integer(getColour(type)
}
- Hashtable featureOrder = null;
+ Map featureOrder = null;
/**
* analogous to colour - store a normalized ordering for all feature types in
* this rendering context.
*
* @param type
- * Feature type string
+ * Feature type string
* @param position
- * normalized priority - 0 means always appears on top, 1 means
- * always last.
+ * normalized priority - 0 means always appears on top, 1 means
+ * always last.
*/
public float setOrder(String type, float position)
{