package org.chesmapper.view.gui; import java.awt.BorderLayout; import java.awt.CardLayout; import java.awt.Color; import java.awt.Dimension; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedHashMap; import java.util.List; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.SwingConstants; import javax.swing.SwingUtilities; import javax.swing.border.EmptyBorder; import org.chesmapper.map.dataInterface.CompoundProperty; import org.chesmapper.map.dataInterface.CompoundPropertyUtil; import org.chesmapper.map.dataInterface.FragmentProperty; import org.chesmapper.map.dataInterface.NominalProperty; import org.chesmapper.map.dataInterface.NumericProperty; import org.chesmapper.map.gui.SmartsViewDialog; import org.chesmapper.map.main.ScreenSetup; import org.chesmapper.map.main.Settings; import org.chesmapper.view.cluster.Cluster; import org.chesmapper.view.cluster.ClusterController; import org.chesmapper.view.cluster.Clustering; import org.chesmapper.view.cluster.ClusteringImpl; import org.chesmapper.view.cluster.Compound; import org.chesmapper.view.cluster.CompoundFilter; import org.chesmapper.view.cluster.CompoundFilterImpl; import org.chesmapper.view.cluster.Clustering.SelectionListener; import org.chesmapper.view.gui.swing.ComponentFactory; import org.chesmapper.view.gui.swing.TransparentViewPanel; import org.chesmapper.view.gui.swing.ComponentFactory.ClickableLabel; import org.chesmapper.view.gui.swing.ComponentFactory.DimensionProvider; import org.chesmapper.view.gui.util.Highlighter; import org.mg.javalib.freechart.AbstractFreeChartPanel; import org.mg.javalib.freechart.HistogramPanel; import org.mg.javalib.freechart.StackedBarPlot; import org.mg.javalib.freechart.FreeChartPanel.ChartMouseSelectionListener; import org.mg.javalib.util.ArrayUtil; import org.mg.javalib.util.ColorUtil; import org.mg.javalib.util.CountedSet; import org.mg.javalib.util.DefaultComparator; import org.mg.javalib.util.ObjectUtil; import org.mg.javalib.util.SequentialWorkerThread; import org.mg.javalib.util.StringUtil; import com.jgoodies.forms.builder.DefaultFormBuilder; import com.jgoodies.forms.layout.FormLayout; import com.jgoodies.forms.layout.Sizes; public class ChartPanel extends JPanel { Clustering clustering; ClusterController clusterControler; ViewControler viewControler; GUIControler guiControler; String plotKey; CompoundProperty property; private DimensionProvider maxLabelWidth = new DimensionProvider() { @Override public Dimension getPreferredSize(Dimension orig) { return new Dimension(Math.min(guiControler.getComponentMaxWidth(0.2), orig.width), featureNameLabel.getHeight()); } }; private JPanel featurePanel; private JLabel featureNameLabelHeader = ComponentFactory.createViewLabel("<html><b>Feature:</b><html>"); private JLabel featureNameLabel = ComponentFactory.createViewLabel("", maxLabelWidth); private JLabel featureSetLabel = ComponentFactory.createViewLabel("", maxLabelWidth); private JLabel featureValuesLabel = ComponentFactory.createViewLabel("", maxLabelWidth); private JLabel featureValuesLabelHeader = ComponentFactory.createViewLabel("Values:"); private JLabel featureDescriptionLabel = ComponentFactory.createViewLabel("", maxLabelWidth); private JLabel featureDescriptionLabelHeader = ComponentFactory.createViewLabel("Description:"); private JLabel featureSmartsLabelHeader = ComponentFactory.createViewLabel("Smarts:"); private JLabel featureSmartsLabel = ComponentFactory.createViewLabel("", maxLabelWidth); private JLabel featureMappingLabelHeader = ComponentFactory.createViewLabel("Usage:"); private JLabel featureMappingLabel = ComponentFactory.createViewLabel("", maxLabelWidth); private JLabel featureMissingLabelHeader = ComponentFactory.createViewLabel("Missing values:"); private JLabel featureMissingLabel = ComponentFactory.createViewLabel(""); private JLabel labelList[] = { featureNameLabelHeader, featureNameLabel, featureSetLabel, featureValuesLabel, featureValuesLabelHeader, featureDescriptionLabel, featureDescriptionLabelHeader, featureSmartsLabel, featureSmartsLabelHeader, featureMappingLabel, featureMappingLabelHeader, featureMissingLabel, featureMissingLabelHeader }; private ClickableLabel clearSelectedFeatureButton; HashMap<String, AbstractFreeChartPanel> cardContents = new HashMap<String, AbstractFreeChartPanel>(); JPanel cardPanel; AbstractFreeChartPanel currentChartPanel; SequentialWorkerThread workerThread = new SequentialWorkerThread(); public ChartPanel(Clustering clustering, ViewControler viewControler, ClusterController clusterControler, GUIControler guiControler) { this.clusterControler = clusterControler; this.clustering = clustering; this.viewControler = viewControler; this.guiControler = guiControler; buildLayout(); addListeners(); update(true); } private void buildLayout() { for (JLabel l : labelList) { l.setHorizontalAlignment(SwingConstants.LEFT); l.setVerticalAlignment(SwingConstants.TOP); l.setBorder(new EmptyBorder(0, 0, 4, 0)); } clearSelectedFeatureButton = ComponentFactory.createCrossViewButton(); JPanel nameLabelAndClearButton = new JPanel(new BorderLayout()); nameLabelAndClearButton.setOpaque(false); nameLabelAndClearButton.add(featureNameLabel); nameLabelAndClearButton.add(clearSelectedFeatureButton, BorderLayout.EAST); DefaultFormBuilder b = new DefaultFormBuilder(new FormLayout("p,3dlu,p")); b.setLineGapSize(Sizes.pixel(0)); b.append(featureNameLabelHeader); b.append(nameLabelAndClearButton); b.nextLine(); b.append(""); b.append(featureSetLabel); b.nextLine(); b.append(featureValuesLabelHeader); b.append(featureValuesLabel); b.nextLine(); b.append(featureDescriptionLabelHeader); b.append(featureDescriptionLabel); b.nextLine(); b.append(featureSmartsLabelHeader); b.append(featureSmartsLabel); b.nextLine(); b.append(featureMappingLabelHeader); b.append(featureMappingLabel); b.nextLine(); b.append(featureMissingLabelHeader); b.append(featureMissingLabel); featurePanel = b.getPanel(); featurePanel.setOpaque(false); JPanel viewFeaturePanel = new TransparentViewPanel(); viewFeaturePanel.add(featurePanel); JPanel wrappedFeaturePanel = new JPanel(new BorderLayout()); wrappedFeaturePanel.setOpaque(false); wrappedFeaturePanel.add(viewFeaturePanel, BorderLayout.EAST); setLayout(new BorderLayout(0, 0)); add(wrappedFeaturePanel, BorderLayout.NORTH); cardPanel = new TransparentViewPanel(new CardLayout()) { public Dimension getPreferredSize() { Dimension dim = super.getPreferredSize(); dim.width = Math.min(dim.width, guiControler.getComponentMaxWidth(0.33)); dim.height = Math.min(dim.height, Math.min(guiControler.getComponentMaxHeight(0.25), dim.width)); return dim; } }; viewControler.addIgnoreMouseMovementComponents(cardPanel); add(cardPanel, BorderLayout.CENTER); setOpaque(false); // SwingUtil.setDebugBorder(viewFeaturePanel, Color.RED); // SwingUtil.setDebugBorder(wrappedFeaturePanel, Color.MAGENTA); // SwingUtil.setDebugBorder(cardPanel, Color.CYAN); } private void addListeners() { viewControler.addViewListener(new PropertyChangeListener() { @Override public void propertyChange(PropertyChangeEvent evt) { if (evt.getPropertyName().equals(ViewControler.PROPERTY_HIGHLIGHT_CHANGED) || evt.getPropertyName().equals(ViewControler.PROPERTY_HIGHLIGHT_CHANGED) || evt.getPropertyName().equals(ViewControler.PROPERTY_HIGHLIGHT_COLORS_CHANGED) || evt.getPropertyName().equals(ViewControler.PROPERTY_COMPOUND_FILTER_CHANGED)) { update(false); } else if (evt.getPropertyName().equals(ViewControler.PROPERTY_BACKGROUND_CHANGED)) { if (currentChartPanel != null) currentChartPanel.setForegroundColor(ComponentFactory.FOREGROUND); } else if (evt.getPropertyName().equals(ViewControler.PROPERTY_FONT_SIZE_CHANGED)) { update(false); } } }); clustering.addListener(new PropertyChangeListener() { @Override public void propertyChange(PropertyChangeEvent evt) { if (evt.getPropertyName().equals(ClusteringImpl.CLUSTER_MODIFIED)) { update(true); } else if (evt.getPropertyName().equals(ClusteringImpl.CLUSTER_REMOVED) || evt.getPropertyName().equals(ClusteringImpl.CLUSTER_CLEAR) || evt.getPropertyName().equals(ClusteringImpl.CLUSTER_ADDED)) { cardPanel.removeAll(); cardContents.clear(); update(true); } } }); clustering.addSelectionListener(new SelectionListener() { @Override public void compoundWatchedChanged(Compound[] c) { update(false); } @Override public void compoundActiveChanged(Compound[] c) { update(false); } @Override public void clusterWatchedChanged(Cluster c) { update(false); } @Override public void clusterActiveChanged(Cluster c) { update(false); } }); featureSmartsLabel.setIcon(ComponentFactory.createViewStringImageIcon("show")); featureSmartsLabel.addMouseListener(new MouseAdapter() { @Override public void mouseClicked(MouseEvent e) { if (property instanceof FragmentProperty) { SmartsViewDialog.show(Settings.TOP_LEVEL_FRAME, ((FragmentProperty) property).getSmarts(), Settings.TOP_LEVEL_FRAME.getWidth(), Settings.TOP_LEVEL_FRAME.getHeight()); } } }); clearSelectedFeatureButton.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { viewControler.setHighlighter(Highlighter.DEFAULT_HIGHLIGHTER); } }); } private static String getKey(Cluster c, CompoundProperty p, Compound m[], CompoundFilter f, int fontSize) { String mString = ""; for (Compound compound : m) mString = mString.concat(compound.toString()); String cString = ""; if (p instanceof FragmentProperty) cString += CompoundPropertyUtil.HIGHILIGHT_MATCH_COLORS; if (p instanceof NominalProperty) cString += ((NominalProperty) p).getHighlightColorSequence(); return (c == null ? "null" : c.getName()) + "_" + p.toString() + "_" + cString + " " + mString.hashCode() + "_" + (f == null ? "" : f.hashCode()) + "_" + fontSize; } static boolean selfUpdate = false; HashSet<Compound> selfUpdateCompounds = null; private abstract class CompoundSelector implements ChartMouseSelectionListener { protected abstract boolean hasSelectionCriterionChanged(); protected abstract void updateSelectionCriterion(); protected abstract String selectionCriterionToString(CompoundProperty p); protected abstract boolean isSelected(Compound m, CompoundProperty p); @Override public void hoverEvent() { handleEvent(true, false, false); } @Override public void clickEvent(boolean ctrlDown, boolean doubleClick) { viewControler.clearMouseMoveWatchUpdates(false); handleEvent(false, ctrlDown, doubleClick); } private void handleEvent(final boolean hover, final boolean ctrlDown, final boolean doubleClick) { if (selfUpdate) { return; } selfUpdate = true; Thread th = new Thread(new Runnable() { public void run() { clusterControler.clearClusterWatched(); try { if (!hasSelectionCriterionChanged() && hover) return; updateSelectionCriterion(); CompoundProperty prop = viewControler.getHighlightedProperty(); final HashSet<Compound> comps = new HashSet<Compound>(); Iterable<Compound> compounds; if (clustering.isClusterActive()) compounds = clustering.getActiveCluster().getCompounds(); else compounds = clustering.getCompounds(false); for (Compound compound : compounds) if (isSelected(compound, prop)) comps.add(compound); if (hover) { if (ObjectUtil.equals(selfUpdateCompounds, comps)) return; selfUpdateCompounds = comps; if (comps.size() == 0) viewControler.clearMouseMoveWatchUpdates(true); else viewControler.doMouseMoveWatchUpdates(new Runnable() { @Override public void run() { clusterControler.setCompoundWatched(ArrayUtil.toArray(comps)); } }); } else { if (comps.size() == 0) clusterControler.clearCompoundActive(true); else { if (ctrlDown) { for (Compound compound : clustering.getActiveCompounds()) if (comps.contains(compound)) comps.remove(compound); else comps.add(compound); } if (doubleClick) { CompoundFilter compoundFilter = new CompoundFilterImpl(clustering, new ArrayList<Compound>(comps), selectionCriterionToString(prop)); clusterControler.setCompoundFilter(compoundFilter, true); } else { if (comps.size() == 0) clusterControler.clearCompoundActive(true); else clusterControler.setCompoundActive(ArrayUtil.toArray(comps), true); } } } } finally { workerThread.addJob(new Runnable() { @Override public void run() { selfUpdate = false; } }, "selfUpdate false after chart was created!"); } } }); th.start(); } } double selectedMin = 1.0; double selectedMax = 0.0; private class NumericCompoundSelector extends CompoundSelector { HistogramPanel hist; public NumericCompoundSelector(HistogramPanel hist) { this.hist = hist; } @Override public String toString() { return "selector " + hist.hashCode(); } @Override protected boolean hasSelectionCriterionChanged() { return selectedMin != hist.getSelectedMin() || selectedMax != hist.getSelectedMax(); } @Override protected void updateSelectionCriterion() { selectedMin = hist.getSelectedMin(); selectedMax = hist.getSelectedMax(); } @Override protected String selectionCriterionToString(CompoundProperty p) { return StringUtil.formatDouble(selectedMin) + " <= " + p + " <= " + StringUtil.formatDouble(selectedMax); } @Override protected boolean isSelected(Compound m, CompoundProperty p) { Double d = m.getDoubleValue((NumericProperty) p); return d != null && d >= selectedMin && d <= selectedMax; } } private abstract class PlotData { AbstractFreeChartPanel plot; public AbstractFreeChartPanel getPlot() { return plot; } } private class NumericPlotData extends PlotData { List<String> captions; List<double[]> vals; public NumericPlotData(Cluster c, NumericProperty p, Compound m[], int fontsize) { Double v[] = clustering.getDoubleValues(p); captions = new ArrayList<String>(); vals = new ArrayList<double[]>(); captions.add("Dataset"); vals.add(ArrayUtil.toPrimitiveDoubleArray(ArrayUtil.removeNullValues(v))); if (c != null) { captions.add(c.toString()); vals.add(ArrayUtil.toPrimitiveDoubleArray(ArrayUtil.removeNullValues(c.getDoubleValues(p)))); } Double mVals[] = new Double[m.length]; boolean notNull = false; for (int i = 0; i < mVals.length; i++) { mVals[i] = m[i].getDoubleValue(p); notNull |= mVals[i] != null; } if (m.length > 0 && notNull) { if (m.length == 1) captions.add(m[0].toString()); else captions.add("Selected compounds"); vals.add(ArrayUtil.toPrimitiveDoubleArray(ArrayUtil.removeNullValues(mVals))); } plot = new HistogramPanel(null, null, null, "#compounds", captions, vals, 20); configurePlotColors(plot, c, m, p, fontsize); SwingUtilities.invokeLater(new Runnable()// wait to add listerner, so that chart has finished painting { @Override public void run() { plot.addSelectionListener(new NumericCompoundSelector((HistogramPanel) plot)); } }); } } String selectedCategory; private class NominalCompoundSelector extends CompoundSelector { StackedBarPlot bar; public NominalCompoundSelector(StackedBarPlot bar) { this.bar = bar; } @Override public String toString() { return "selector " + bar.hashCode(); } @Override protected boolean hasSelectionCriterionChanged() { return !ObjectUtil.equals(selectedCategory, bar.getSelectedCategory()); } @Override protected void updateSelectionCriterion() { selectedCategory = bar.getSelectedCategory(); } @Override protected String selectionCriterionToString(CompoundProperty p) { return p + " = " + selectedCategory; } @Override protected boolean isSelected(Compound m, CompoundProperty p) { return selectedCategory != null && selectedCategory.equals(m.getFormattedValue(p)); } } private class NominalPlotData extends PlotData { LinkedHashMap<String, List<Double>> data; String vals[]; public NominalPlotData(Cluster c, NominalProperty p, Compound ms[], int fontsize) { // TODO rewrite this code // * add counted-set for compounds as well? // * remove old getStringValues() method (second param is not needed) // * fix order String v[] = clustering.getStringValues(p, null); CountedSet<String> datasetSet = CountedSet.fromArray(v); List<String> datasetValues = datasetSet.values(new DefaultComparator<String>()); CountedSet<String> clusterSet = null; if (c != null) { v = c.getStringValues(p, null); clusterSet = CountedSet.fromArray(v); } data = new LinkedHashMap<String, List<Double>>(); double compoundCounts[] = new double[datasetValues.size()]; int count = 0; for (String o : datasetValues) { for (Compound m : ms) if (ObjectUtil.equals(m.getStringValue(p), o)) compoundCounts[count]++; count++; } if (ms.length == 1) data.put(ms[0].toString(), ArrayUtil.toList(compoundCounts)); else if (ms.length > 1) data.put("Selected compounds", ArrayUtil.toList(compoundCounts)); if (c != null) { List<Double> clusterCounts = new ArrayList<Double>(); for (String o : datasetValues) clusterCounts.add((double) clusterSet.getCount(o)); data.put(c.toString(), clusterCounts); } List<Double> datasetCounts = new ArrayList<Double>(); for (String o : datasetValues) datasetCounts.add((double) datasetSet.getCount(o)); data.put("Dataset", datasetCounts); vals = new String[datasetValues.size()]; // datasetValues.toArray(vals); for (int i = 0; i < vals.length; i++) vals[i] = p.getFormattedValue(datasetValues.get(i)); plot = new StackedBarPlot(null, null, "#compounds", StackedBarPlot.convertTotalToAdditive(data), vals); configurePlotColors(plot, c, ms, p, fontsize); SwingUtilities.invokeLater(new Runnable() // wait to add listerner, so that chart has finished painting { @Override public void run() { plot.addSelectionListener(new NominalCompoundSelector((StackedBarPlot) plot)); } }); } } private void configurePlotColors(AbstractFreeChartPanel chartPanel, Cluster cluster, Compound[] compounds, CompoundProperty property, int fontsize) { int dIndex = -1; int cIndex = -1; int mIndex = -1; if (cluster == null) { if (compounds.length == 0) dIndex = 0; else { mIndex = 0; dIndex = 1; } } else { if (compounds.length == 0) { cIndex = 0; dIndex = 1; } else { mIndex = 0; cIndex = 1; dIndex = 2; } } if (chartPanel instanceof StackedBarPlot) { Color cols[] = CompoundPropertyUtil.getNominalColors((NominalProperty) property); if (dIndex == 0) { chartPanel.setSeriesColor(0, ColorUtil.grayscale(cols[0])); ((StackedBarPlot) chartPanel).setSeriesCategoryColors(0, cols); } else if (dIndex == 1) { chartPanel.setSeriesColor(1, ColorUtil.grayscale(cols[0].darker().darker().darker())); chartPanel.setSeriesColor(0, ColorUtil.grayscale(cols[0]).brighter()); ((StackedBarPlot) chartPanel).setSeriesCategoryColors(1, ColorUtil.darker(ColorUtil.darker(ColorUtil.darker(cols)))); ((StackedBarPlot) chartPanel).setSeriesCategoryColors(0, ColorUtil.brighter(cols)); } else { chartPanel.setSeriesColor(2, ColorUtil.grayscale(cols[0].darker().darker().darker())); chartPanel.setSeriesColor(1, ColorUtil.grayscale(cols[0]).brighter()); chartPanel.setSeriesColor(0, ColorUtil.grayscale(cols[0]).brighter().brighter().brighter().brighter()); ((StackedBarPlot) chartPanel).setSeriesCategoryColors(2, ColorUtil.darker(ColorUtil.darker(ColorUtil.darker(cols)))); ((StackedBarPlot) chartPanel).setSeriesCategoryColors(1, ColorUtil.brighter(cols)); ((StackedBarPlot) chartPanel).setSeriesCategoryColors(0, ColorUtil.brighter(ColorUtil.brighter(ColorUtil.brighter(ColorUtil.brighter(cols))))); } } else { if (cIndex == -1) chartPanel.setSeriesColor(dIndex, CompoundPropertyUtil.getNumericChartColor()); else { chartPanel.setSeriesColor(dIndex, CompoundPropertyUtil.getNumericChartColor().darker().darker() .darker()); chartPanel.setSeriesColor(cIndex, CompoundPropertyUtil.getNumericChartColor().brighter()); } if (mIndex != -1) chartPanel.setSeriesColor(mIndex, CompoundPropertyUtil.getNumericChartHighlightColor()); } chartPanel.setOpaqueFalse(); chartPanel.setForegroundColor(ComponentFactory.FOREGROUND); chartPanel.setShadowVisible(false); chartPanel.setIntegerTickUnits(); chartPanel.setBarWidthLimited(); chartPanel.setFontSize(fontsize); } private void update(final boolean force) { if (!SwingUtilities.isEventDispatchThread()) throw new IllegalStateException("GUI updates only in event dispatch thread plz"); if (clustering.getNumClusters() == 0) { setVisible(false); return; } CompoundProperty prop = viewControler.getHighlightedProperty(); Cluster c = clustering.getActiveCluster(); if (c == null) c = clustering.getWatchedCluster(); if (clustering.getNumClusters() == 1) c = null; Compound comps[] = clustering.getActiveCompounds(); if (comps.length == 0) comps = clustering.getWatchedCompounds(); if (prop instanceof NominalProperty && comps.length == 1) { //does NOT help much in terms of visualisation for 1 compound (color code should be enough) //difficult to realize in terms of color brightness comps = new Compound[0]; } String key; if (prop == null) key = ""; else key = getKey(c, prop, comps, clusterControler.getCompoundFilter(), ScreenSetup.INSTANCE.getFontSize()); if (force || property != prop || !plotKey.equals(key)) { property = prop; plotKey = key; if (property == null) setVisible(false); else { final CompoundProperty fProperty = property; final String fPlotKey = plotKey; final Cluster fCluster = c; final Compound fCompounds[] = comps; final int fFontSize = ScreenSetup.INSTANCE.getFontSize(); workerThread.addJob(new Runnable() { public void run() { if (fProperty != property || !fPlotKey.equals(plotKey)) return; if (force && cardContents.containsKey(plotKey)) cardContents.remove(plotKey); if (!cardContents.containsKey(plotKey)) { PlotData d = null; if (!fProperty.isUndefined()) { if (fProperty instanceof NominalProperty) d = new NominalPlotData(fCluster, (NominalProperty) fProperty, fCompounds, fFontSize); else if (fProperty instanceof NumericProperty) d = new NumericPlotData(fCluster, (NumericProperty) fProperty, fCompounds, fFontSize); } if (d != null) { cardContents.put(plotKey, d.getPlot()); cardPanel.add(d.getPlot(), plotKey); } } if (fProperty != property || !fPlotKey.equals(plotKey)) return; setIgnoreRepaint(true); featureNameLabel.setText(fProperty.toString()); //hack, do not show integrated features, cdk features or openbabel features if (fProperty.getCompoundPropertySet() != null && fProperty.getCompoundPropertySet().isSizeDynamic()) { featureSetLabel.setText(fProperty.getCompoundPropertySet().toString()); featureSetLabel.setVisible(true); } else featureSetLabel.setVisible(false); featureValuesLabel.setText("<html>" + clustering.getSummaryStringValue(fProperty, true) + "</html>"); featureDescriptionLabel.setText(fProperty.getDescription() + ""); featureDescriptionLabel.setVisible(fProperty.getDescription() != null); featureDescriptionLabelHeader.setVisible(fProperty.getDescription() != null); if (fProperty instanceof FragmentProperty) { featureSmartsLabel.setText(((FragmentProperty) fProperty).getSmarts() + ""); featureSmartsLabel.setVisible(true); featureSmartsLabelHeader.setVisible(true); } else { featureSmartsLabel.setVisible(false); featureSmartsLabelHeader.setVisible(false); } String usage; if (fProperty.getCompoundPropertySet() != null && fProperty.getCompoundPropertySet().isSelectedForMapping()) { if (fProperty.numDistinctValues() <= 1) usage = "Ignored for mapping (equal value for each compound)"; else if (fProperty.getRedundantProp() != null) usage = "Ignored for mapping (redundant to " + fProperty.getRedundantProp() + ")"; else usage = "Used for mapping"; } else usage = "NOT used for mapping"; featureMappingLabel.setText(usage); featureMissingLabelHeader.setVisible(clustering.numMissingValues(fProperty) > 0); featureMissingLabel.setVisible(clustering.numMissingValues(fProperty) > 0); featureMissingLabel.setText(clustering.numMissingValues(fProperty) + ""); if (cardContents.containsKey(plotKey)) { cardPanel.setVisible(true); currentChartPanel = cardContents.get(plotKey); currentChartPanel.setFontSize(ScreenSetup.INSTANCE.getFontSize()); ((CardLayout) cardPanel.getLayout()).show(cardPanel, plotKey); } else cardPanel.setVisible(false); revalidate(); setVisible(true); setIgnoreRepaint(false); repaint(); } }, "update chart"); } } } }