/*
* Licensed Materials - Property of Rogue Wave Software, Inc.
* © Copyright Rogue Wave Software, Inc. 2014, 2017
* © Copyright IBM Corp. 2009, 2014
* © Copyright ILOG 1996, 2009
* All Rights Reserved.
*
* Note to U.S. Government Users Restricted Rights:
* The Software and Documentation were developed at private expense and
* are "Commercial Items" as that term is defined at 48 CFR 2.101,
* consisting of "Commercial Computer Software" and
* "Commercial Computer Software Documentation", as such terms are
* used in 48 CFR 12.212 or 48 CFR 227.7202-1 through 227.7202-4,
* as applicable.
*/
import java.util.*;
import java.awt.*;
import java.awt.event.*;
import java.io.File;
import javax.swing.*;
import javax.swing.undo.*;
import javax.swing.event.*;
import ilog.views.chart.*;
import ilog.views.chart.graphic.*;
import ilog.views.chart.customizer.IlvChartCSSAdapter;
import ilog.views.chart.customizer.swing.*;
import ilog.views.chart.data.xml.IlvXMLDataSource;
import ilog.views.customizer.IlvCustomizerException;
import ilog.views.util.IlvProductUtil;
import ilog.views.util.css.IlvCSS;
import ilog.views.util.swing.IlvSwingUtil;
// An internal undocumented class, only used for debugging.
import ilog.views.customizer.styling.internal.IlvRuleCustomizerManager;
/**
* A simple example that illustrates how to use the customizers on a
* chart component.
*/
public class ChartCustomizerExample extends JFrame {
private static final String XML_FILE = "data/simple.xml";
private static final String CSS_FILE = "data/simple.css";
// The selector of the rule loaded initially
private static final String INITIAL_RULE_SELECTOR = "chart";
protected IlvChart chart;
protected IlvChartCSSCustomizerPanel customizer;
protected String currentSelector;
protected IlvXMLDataSource xmlDS = new IlvXMLDataSource();
private JMenuItem undoMenuItem;
private JMenuItem redoMenuItem;
private JMenu rulesMenu;
private Action addImageDecoAction;
private Action removeImageDecoAction;
private Action addXRangeDataIndicatorAction;
private Action removeXRangeDataIndicatorAction;
private Action addYRangeDataIndicatorAction;
private Action removeYRangeDataIndicatorAction;
private Action addXValueDataIndicatorAction;
private Action removeXValueDataIndicatorAction;
private Action addYValueDataIndicatorAction;
private Action removeYValueDataIndicatorAction;
private Action addDataWindowIndicatorAction;
private Action removeDataWindowIndicatorAction;
// Intermediate containers.
JSplitPane wholePanel;
static final double wholePanelDefaultDivision = 0.5;
/** Creates a new <code>ChartCustomizerExample</code>. */
public ChartCustomizerExample() {
IlvSwingUtil.setDefaultAppropriateLookAndFeel();
enableEvents(AWTEvent.WINDOW_EVENT_MASK);
setDefaultCloseOperation(EXIT_ON_CLOSE);
setTitle("Chart Styling Customizer");
// the chart
this.chart = createChart();
// the chart customizer
this.customizer = new IlvChartCSSCustomizerPanel(chart);
// Disable CSS expressions, since they are not enabled in the designer
// either.
try {
customizer.setEditAsTextEnabled(false, true);
} catch (IlvCustomizerException e) {
e.printStackTrace();
}
// Possibly enable some debugging.
if (false) {
((IlvRuleCustomizerManager)customizer.getAdapter().getInternal()).getCSS().setDebugMode(true);
((IlvRuleCustomizerManager)customizer.getAdapter().getInternal()).getCSS().getCSSBeans().setStyleSheetDebugMask(IlvCSS.BAD_PROP_WITH_STACK_MASK);
}
// enable undo at the chart customizer
customizer.addUndoableEditListener(undoListener);
// a split pane to show the chart in the upper half and the
// customizer in the lower half
wholePanel = new JSplitPane(JSplitPane.VERTICAL_SPLIT);
wholePanel.setDividerLocation(wholePanelDefaultDivision);
wholePanel.setResizeWeight(wholePanelDefaultDivision);
wholePanel.setTopComponent(chart);
wholePanel.setBottomComponent(customizer);
getContentPane().add(createMenuBar(), BorderLayout.NORTH);
getContentPane().add(wholePanel, BorderLayout.CENTER);
}
/** Creates the chart. */
protected IlvChart createChart()
{
IlvChart chart = new IlvChart();
try {
xmlDS.setFilename(XML_FILE);
} catch (Exception x) {
x.printStackTrace();
}
chart.setDataSource(xmlDS);
try {
chart.setStyleSheets(new String[] {CSS_FILE});
} catch (ilog.views.util.styling.IlvStylingException x) {
System.err.println("Cannot load style sheets: " + x.getMessage());
x.printStackTrace();
}
return chart;
}
public static void main(String[] args)
{
// This sample uses JViews Charts features. When deploying an
// application that includes this code, you need to be in possession
// of a Rogue Wave JViews Charts Deployment license.
IlvProductUtil.DeploymentLicenseRequired(
IlvProductUtil.JViews_Charts_Deployment);
final ChartCustomizerExample example = new ChartCustomizerExample();
example.setSize(700,700);
SwingUtilities.invokeLater(
new Runnable() {
Override
public void run() {
example.setVisible(true);
example.wholePanel
.setDividerLocation(wholePanelDefaultDivision);
example.setRule(INITIAL_RULE_SELECTOR);
example.updateRulesMenu();
}
});
}
// -----------------------------------------------------------------------
// The undo mechanism
/**
* The undo manager.
*/
private UndoManager undoManager = new UndoManager();
/**
* Listens to the undo events coming from the charts customizer.
*/
private UndoableEditListener undoListener = new UndoableEditListener() {
Override
public void undoableEditHappened(UndoableEditEvent e) {
undoManager.addEdit(e.getEdit());
updateUndoRedoMenuItems();
}
};
/**
* Updates the undo menu entries.
*/
private void updateUndoRedoMenuItems()
{
if (undoMenuItem != null && redoMenuItem != null) {
undoMenuItem.setEnabled(undoManager.canUndo());
redoMenuItem.setEnabled(undoManager.canRedo());
}
}
// -----------------------------------------------------------------------
// The menu bar
/**
* Create a simple menu bar with an undo/redo menu.
*/
private JMenuBar createMenuBar()
{
JMenuBar bar = new JMenuBar();
rulesMenu = new JMenu("Style Rules");
fillRulesMenu(rulesMenu);
bar.add(rulesMenu);
JMenu undoMenu = new JMenu("Undo");
bar.add(undoMenu);
JMenu decoMenu = new JMenu("Decorations");
bar.add(decoMenu);
// -- undo -------------------------
Action undoAction = new AbstractAction("Undo") {
Override
public void actionPerformed(ActionEvent e) {
try {
undoManager.undo();
} catch (CannotUndoException ex) {
ex.printStackTrace();
} finally {
updateUndoRedoMenuItems();
}
}
};
Action redoAction = new AbstractAction("Redo") {
Override
public void actionPerformed(ActionEvent e) {
try {
undoManager.redo();
} catch (CannotRedoException ex) {
ex.printStackTrace();
} finally {
updateUndoRedoMenuItems();
}
}
};
undoMenuItem = new JMenuItem(undoAction);
redoMenuItem = new JMenuItem(redoAction);
undoMenu.add(undoMenuItem);
undoMenu.add(redoMenuItem);
updateUndoRedoMenuItems();
// -- decorations -------------
addImageDecoAction =
new AbstractAction("Add Image Decoration") {
Override
public void actionPerformed(ActionEvent event) {
try {
IlvImageDecoration deco =
new IlvImageDecoration(new File("data/draft.gif").toURI().toURL(),
IlvImageDecoration.ANCHORED,
SwingConstants.CENTER);
chart.addDecoration(deco);
updateDataIndicatorActions();
String selector = customizer.getAdapter().getSingleObjectCustomizableRuleSelector(deco);
if (selector != null)
setRule(selector);
updateRulesMenu();
} catch (Exception e) {
e.printStackTrace();
}
}
};
removeImageDecoAction =
new AbstractAction("Remove Image Decoration") {
Override
public void actionPerformed(ActionEvent event) {
try {
IlvChartDecoration designatedDeco = null;
for (IlvChartDecoration deco : chart.getDecorations()) {
if (deco instanceof IlvImageDecoration
&& currentSelector.equals(customizer.getAdapter().getSingleObjectCustomizableRuleSelector(deco)))
designatedDeco = deco;
}
if (designatedDeco == null) {
for (IlvChartDecoration deco : chart.getDecorations()) {
if (deco instanceof IlvImageDecoration)
designatedDeco = deco;
}
}
if (designatedDeco != null) {
chart.removeDecoration(designatedDeco);
updateDataIndicatorActions();
if (currentSelector.equals(customizer.getAdapter().getSingleObjectCustomizableRuleSelector(designatedDeco)))
setRule(INITIAL_RULE_SELECTOR);
updateRulesMenu();
}
} catch (Exception e) {
e.printStackTrace();
}
}
};
// this is only example code how to add and remove a data indicator.
// The CSS is updated automatically, i.e., a decoration rule is added.
// Since the menu should reflect the current rules, we call
// fillRulesMenu to update the rules menu.
addXRangeDataIndicatorAction =
new AbstractAction("Add X-Range Data Indicator") {
Override
public void actionPerformed(ActionEvent event) {
try {
IlvAxis axis = chart.getXAxis();
IlvDataInterval range = axis.getVisibleRange();
IlvDataIndicator deco =
new IlvDataIndicator(-1,
new IlvDataInterval(range.getMiddle(), range.getMiddle()+0.2*range.getLength()),
"Range 50%-70%");
chart.addDecoration(deco);
updateDataIndicatorActions();
String selector = customizer.getAdapter().getSingleObjectCustomizableRuleSelector(deco);
if (selector != null)
setRule(selector);
updateRulesMenu();
} catch (Exception e) {
e.printStackTrace();
}
}
};
removeXRangeDataIndicatorAction =
new AbstractAction("Remove X-Range Data Indicator") {
Override
public void actionPerformed(ActionEvent event) {
try {
IlvChartDecoration designatedDeco = null;
for (IlvChartDecoration deco : chart.getDecorations()) {
if (deco instanceof IlvDataIndicator
&& ((IlvDataIndicator)deco).getType() == IlvDataIndicator.X_RANGE
&& currentSelector.equals(customizer.getAdapter().getSingleObjectCustomizableRuleSelector(deco)))
designatedDeco = deco;
}
if (designatedDeco == null) {
for (IlvChartDecoration deco : chart.getDecorations()) {
if (deco instanceof IlvDataIndicator
&& ((IlvDataIndicator)deco).getType() == IlvDataIndicator.X_RANGE)
designatedDeco = deco;
}
}
if (designatedDeco != null) {
chart.removeDecoration(designatedDeco);
updateDataIndicatorActions();
if (currentSelector.equals(customizer.getAdapter().getSingleObjectCustomizableRuleSelector(designatedDeco)))
setRule(INITIAL_RULE_SELECTOR);
updateRulesMenu();
}
} catch (Exception e) {
e.printStackTrace();
}
}
};
addYRangeDataIndicatorAction =
new AbstractAction("Add Y-Range Data Indicator") {
Override
public void actionPerformed(ActionEvent event) {
try {
IlvAxis axis = chart.getYAxis(0);
IlvDataInterval range = axis.getVisibleRange();
IlvDataIndicator deco =
new IlvDataIndicator(0,
new IlvDataInterval(range.getMiddle(), range.getMiddle()+0.2*range.getLength()),
"Range 50%-70%");
chart.addDecoration(deco);
updateDataIndicatorActions();
String selector = customizer.getAdapter().getSingleObjectCustomizableRuleSelector(deco);
if (selector != null)
setRule(selector);
updateRulesMenu();
} catch (Exception e) {
e.printStackTrace();
}
}
};
removeYRangeDataIndicatorAction =
new AbstractAction("Remove Y-Range Data Indicator") {
Override
public void actionPerformed(ActionEvent event) {
try {
IlvChartDecoration designatedDeco = null;
for (IlvChartDecoration deco : chart.getDecorations()) {
if (deco instanceof IlvDataIndicator
&& ((IlvDataIndicator)deco).getType() == IlvDataIndicator.Y_RANGE
&& currentSelector.equals(customizer.getAdapter().getSingleObjectCustomizableRuleSelector(deco)))
designatedDeco = deco;
}
if (designatedDeco == null) {
for (IlvChartDecoration deco : chart.getDecorations()) {
if (deco instanceof IlvDataIndicator
&& ((IlvDataIndicator)deco).getType() == IlvDataIndicator.Y_RANGE)
designatedDeco = deco;
}
}
if (designatedDeco != null) {
chart.removeDecoration(designatedDeco);
updateDataIndicatorActions();
if (currentSelector.equals(customizer.getAdapter().getSingleObjectCustomizableRuleSelector(designatedDeco)))
setRule(INITIAL_RULE_SELECTOR);
updateRulesMenu();
}
} catch (Exception e) {
e.printStackTrace();
}
}
};
addXValueDataIndicatorAction =
new AbstractAction("Add X-Value Data Indicator") {
Override
public void actionPerformed(ActionEvent event) {
try {
IlvAxis axis = chart.getXAxis();
IlvDataInterval range = axis.getVisibleRange();
IlvDataIndicator deco =
new IlvDataIndicator(-1,
range.getMin()+0.2*range.getLength(),
"Value 20%");
chart.addDecoration(deco);
updateDataIndicatorActions();
String selector = customizer.getAdapter().getSingleObjectCustomizableRuleSelector(deco);
if (selector != null)
setRule(selector);
updateRulesMenu();
} catch (Exception e) {
e.printStackTrace();
}
}
};
removeXValueDataIndicatorAction =
new AbstractAction("Remove X-Value Data Indicator") {
Override
public void actionPerformed(ActionEvent event) {
try {
IlvChartDecoration designatedDeco = null;
for (IlvChartDecoration deco : chart.getDecorations()) {
if (deco instanceof IlvDataIndicator
&& ((IlvDataIndicator)deco).getType() == IlvDataIndicator.X_VALUE
&& currentSelector.equals(customizer.getAdapter().getSingleObjectCustomizableRuleSelector(deco)))
designatedDeco = deco;
}
if (designatedDeco == null) {
for (IlvChartDecoration deco : chart.getDecorations()) {
if (deco instanceof IlvDataIndicator
&& ((IlvDataIndicator)deco).getType() == IlvDataIndicator.X_VALUE)
designatedDeco = deco;
}
}
if (designatedDeco != null) {
chart.removeDecoration(designatedDeco);
updateDataIndicatorActions();
if (currentSelector.equals(customizer.getAdapter().getSingleObjectCustomizableRuleSelector(designatedDeco)))
setRule(INITIAL_RULE_SELECTOR);
updateRulesMenu();
}
} catch (Exception e) {
e.printStackTrace();
}
}
};
addYValueDataIndicatorAction =
new AbstractAction("Add Y-Value Data Indicator") {
Override
public void actionPerformed(ActionEvent event) {
try {
IlvAxis axis = chart.getYAxis(0);
IlvDataInterval range = axis.getVisibleRange();
IlvDataIndicator deco =
new IlvDataIndicator(0,
range.getMin()+0.2*range.getLength(),
"Value 20%");
chart.addDecoration(deco);
updateDataIndicatorActions();
String selector = customizer.getAdapter().getSingleObjectCustomizableRuleSelector(deco);
if (selector != null)
setRule(selector);
updateRulesMenu();
} catch (Exception e) {
e.printStackTrace();
}
}
};
removeYValueDataIndicatorAction =
new AbstractAction("Remove Y-Value Data Indicator") {
Override
public void actionPerformed(ActionEvent event) {
try {
IlvChartDecoration designatedDeco = null;
for (IlvChartDecoration deco : chart.getDecorations()) {
if (deco instanceof IlvDataIndicator
&& ((IlvDataIndicator)deco).getType() == IlvDataIndicator.Y_VALUE
&& currentSelector.equals(customizer.getAdapter().getSingleObjectCustomizableRuleSelector(deco)))
designatedDeco = deco;
}
if (designatedDeco == null) {
for (IlvChartDecoration deco : chart.getDecorations()) {
if (deco instanceof IlvDataIndicator
&& ((IlvDataIndicator)deco).getType() == IlvDataIndicator.Y_VALUE)
designatedDeco = deco;
}
}
if (designatedDeco != null) {
chart.removeDecoration(designatedDeco);
updateDataIndicatorActions();
if (currentSelector.equals(customizer.getAdapter().getSingleObjectCustomizableRuleSelector(designatedDeco)))
setRule(INITIAL_RULE_SELECTOR);
updateRulesMenu();
}
} catch (Exception e) {
e.printStackTrace();
}
}
};
addDataWindowIndicatorAction =
new AbstractAction("Add Data Window Indicator") {
Override
public void actionPerformed(ActionEvent event) {
try {
IlvAxis axis = chart.getYAxis(0);
IlvDataInterval xRange = chart.getXAxis().getVisibleRange();
IlvDataInterval yRange = chart.getYAxis(0).getVisibleRange();
IlvDataWindow window =
new IlvDataWindow(xRange.getMiddle()-0.1*xRange.getLength(),
xRange.getMiddle()+0.1*xRange.getLength(),
yRange.getMiddle()-0.1*yRange.getLength(),
yRange.getMiddle()+0.1*yRange.getLength());
IlvDataIndicator deco =
new IlvDataIndicator(0, window, null);
chart.addDecoration(deco);
updateDataIndicatorActions();
String selector = customizer.getAdapter().getSingleObjectCustomizableRuleSelector(deco);
if (selector != null)
setRule(selector);
updateRulesMenu();
} catch (Exception e) {
e.printStackTrace();
}
}
};
removeDataWindowIndicatorAction =
new AbstractAction("Remove Data Window Indicator") {
Override
public void actionPerformed(ActionEvent event) {
try {
IlvChartDecoration designatedDeco = null;
for (IlvChartDecoration deco : chart.getDecorations()) {
if (deco instanceof IlvDataIndicator
&& ((IlvDataIndicator)deco).getType() == IlvDataIndicator.WINDOW
&& currentSelector.equals(customizer.getAdapter().getSingleObjectCustomizableRuleSelector(deco)))
designatedDeco = deco;
}
if (designatedDeco == null) {
for (IlvChartDecoration deco : chart.getDecorations()) {
if (deco instanceof IlvDataIndicator
&& ((IlvDataIndicator)deco).getType() == IlvDataIndicator.WINDOW)
designatedDeco = deco;
}
}
if (designatedDeco != null) {
chart.removeDecoration(designatedDeco);
updateDataIndicatorActions();
if (currentSelector.equals(customizer.getAdapter().getSingleObjectCustomizableRuleSelector(designatedDeco)))
setRule(INITIAL_RULE_SELECTOR);
updateRulesMenu();
}
} catch (Exception e) {
e.printStackTrace();
}
}
};
decoMenu.add(new JMenuItem(addImageDecoAction));
decoMenu.add(new JMenuItem(removeImageDecoAction));
decoMenu.add(new JMenuItem(addXRangeDataIndicatorAction));
decoMenu.add(new JMenuItem(removeXRangeDataIndicatorAction));
decoMenu.add(new JMenuItem(addYRangeDataIndicatorAction));
decoMenu.add(new JMenuItem(removeYRangeDataIndicatorAction));
decoMenu.add(new JMenuItem(addXValueDataIndicatorAction));
decoMenu.add(new JMenuItem(removeXValueDataIndicatorAction));
decoMenu.add(new JMenuItem(addYValueDataIndicatorAction));
decoMenu.add(new JMenuItem(removeYValueDataIndicatorAction));
decoMenu.add(new JMenuItem(addDataWindowIndicatorAction));
decoMenu.add(new JMenuItem(removeDataWindowIndicatorAction));
updateDataIndicatorActions();
// ---------------------------------------------
// Options
JMenu optionsMenu = new JMenu("Options");
bar.add(optionsMenu);
final JCheckBoxMenuItem statusIconMenuItem =
new JCheckBoxMenuItem();
statusIconMenuItem.setAction(
new AbstractAction("Show Rule Declaration Status Icon") {
Override
public void actionPerformed(ActionEvent e) {
try {
customizer.setDeclarationStatusIconEnabled(
statusIconMenuItem.isSelected(), true);
} catch (Exception ex) {
ex.printStackTrace();
}
}
});
statusIconMenuItem.setSelected(customizer.isDeclarationStatusIconEnabled());
optionsMenu.add(statusIconMenuItem);
final JCheckBoxMenuItem editAsTextMenuItem =
new JCheckBoxMenuItem();
editAsTextMenuItem.setAction(
new AbstractAction("Allow Entering CSS Expressions") {
Override
public void actionPerformed(ActionEvent e) {
try {
customizer.setEditAsTextEnabled(
editAsTextMenuItem.isSelected(), true);
} catch (Exception ex) {
ex.printStackTrace();
}
}
});
editAsTextMenuItem.setSelected(customizer.isEditAsTextEnabled());
optionsMenu.add(editAsTextMenuItem);
return bar;
}
/**
* Update the enabled state of the data indicator actions.
*/
private void updateDataIndicatorActions()
{
boolean haveImageDeco = false;
boolean haveXRangeDataIndicator = false;
boolean haveYRangeDataIndicator = false;
boolean haveXValueDataIndicator = false;
boolean haveYValueDataIndicator = false;
boolean haveDataWindowIndicator = false;
for (IlvChartDecoration deco : chart.getDecorations()) {
if (deco instanceof IlvImageDecoration)
haveImageDeco = true;
else if (deco instanceof IlvDataIndicator) {
switch (((IlvDataIndicator)deco).getType()) {
case IlvDataIndicator.X_RANGE:
haveXRangeDataIndicator = true;
break;
case IlvDataIndicator.Y_RANGE:
haveYRangeDataIndicator = true;
break;
case IlvDataIndicator.X_VALUE:
haveXValueDataIndicator = true;
break;
case IlvDataIndicator.Y_VALUE:
haveYValueDataIndicator = true;
break;
case IlvDataIndicator.WINDOW:
haveDataWindowIndicator = true;
break;
default:
break;
}
}
}
addImageDecoAction.setEnabled(true);
removeImageDecoAction.setEnabled(haveImageDeco);
addXRangeDataIndicatorAction.setEnabled(true);
removeXRangeDataIndicatorAction.setEnabled(haveXRangeDataIndicator);
addYRangeDataIndicatorAction.setEnabled(true);
removeYRangeDataIndicatorAction.setEnabled(haveYRangeDataIndicator);
addXValueDataIndicatorAction.setEnabled(true);
removeXValueDataIndicatorAction.setEnabled(haveXValueDataIndicator);
addYValueDataIndicatorAction.setEnabled(true);
removeYValueDataIndicatorAction.setEnabled(haveYValueDataIndicator);
addDataWindowIndicatorAction.setEnabled(true);
removeDataWindowIndicatorAction.setEnabled(haveDataWindowIndicator);
}
/**
* Update the list of available rule customizers.
*/
private void updateRulesMenu()
{
fillRulesMenu(rulesMenu);
}
/**
* Fill the rules menu with the customizable rules.
*/
private void fillRulesMenu(final JMenu menu)
{
// or each selector of a customizable rule, create a menu entry
IlvChartCSSAdapter adapter = customizer.getAdapter();
String[] ruleSelectors = adapter.getCustomizableRuleSelectors();
menu.removeAll();
ButtonGroup group = new ButtonGroup();
for (int i = 0; i < ruleSelectors.length; i++) {
final String selector = ruleSelectors[i];
Action action = new AbstractAction(niceName(selector)) {
Override
public void actionPerformed(ActionEvent e) {
setRule(selector);
}
};
JCheckBoxMenuItem menuItem = new JCheckBoxMenuItem(action);
if (selector.equals(currentSelector))
menuItem.setSelected(true);
group.add(menuItem);
menu.add(menuItem);
}
menu.validate();
}
final void setRule(String selector)
{
try {
currentSelector = selector;
customizer.setRule(selector);
// When changing the target of the customizer, the undo
// stack needs to be cleaned
undoManager.discardAllEdits();
updateUndoRedoMenuItems();
} catch (Exception ex) {
ex.printStackTrace();
}
}
/**
* Generate a nicer name for the menu items of the rules.
*/
private String niceName(String selector)
{
if ("chartGrid[axisIndex=\"-1\"]".equals(selector))
return "X Grid";
if ("chartGrid[axisIndex=\"0\"]".equals(selector))
return "Y Grid";
if ("chartScale[axisIndex=\"-1\"]".equals(selector))
return "X Scale";
if ("chartScale[axisIndex=\"0\"]".equals(selector))
return "Y Scale";
if (selector.startsWith("series[name=\""))
return "series " + selector.substring(13,selector.length()-2);
return selector;
}
}