/* * Licensed Materials - Property of Perforce Software, Inc. * © Copyright Perforce Software, Inc. 2014, 2021 * © 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 Perforce 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 { 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; } }