/** * Copyright (C) 2001-2017 by RapidMiner and the contributors * * Complete list of developers available at our web site: * * http://rapidminer.com * * This program is free software: you can redistribute it and/or modify it under the terms of the * GNU Affero General Public License as published by the Free Software Foundation, either version 3 * of the License, or (at your option) any later version. * * 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 * Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License along with this program. * If not, see http://www.gnu.org/licenses/. */ package com.rapidminer.gui.new_plotter.gui; import java.awt.CardLayout; import java.awt.Color; import java.awt.Component; import java.awt.Dimension; import java.awt.Font; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.GridBagConstraints; import java.awt.GridBagLayout; import java.awt.Insets; import java.awt.Rectangle; import java.awt.datatransfer.Transferable; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.KeyEvent; import java.awt.event.KeyListener; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.util.ArrayList; import java.util.Collection; import java.util.LinkedList; import java.util.List; import java.util.Vector; import javax.management.RuntimeErrorException; import javax.swing.BorderFactory; import javax.swing.Box; import javax.swing.BoxLayout; import javax.swing.DefaultListModel; import javax.swing.JButton; import javax.swing.JComboBox; import javax.swing.JLabel; import javax.swing.JList; import javax.swing.JMenuItem; import javax.swing.JPanel; import javax.swing.JPopupMenu; import javax.swing.JScrollPane; import javax.swing.JTree; import javax.swing.ListSelectionModel; import javax.swing.ScrollPaneConstants; import javax.swing.SwingUtilities; import javax.swing.TransferHandler.TransferSupport; import javax.swing.border.Border; import javax.swing.event.PopupMenuEvent; import javax.swing.event.PopupMenuListener; import javax.swing.event.TreeExpansionEvent; import javax.swing.event.TreeSelectionEvent; import javax.swing.event.TreeSelectionListener; import javax.swing.event.TreeWillExpandListener; import javax.swing.tree.DefaultMutableTreeNode; import javax.swing.tree.ExpandVetoException; import javax.swing.tree.TreeNode; import javax.swing.tree.TreePath; import com.rapidminer.datatable.DataTable; import com.rapidminer.datatable.DataTableExampleSetAdapter; import com.rapidminer.example.ExampleSet; import com.rapidminer.gui.ApplicationFrame; import com.rapidminer.gui.RapidMinerGUI; import com.rapidminer.gui.actions.export.PrintableComponent; import com.rapidminer.gui.dnd.DragListener; import com.rapidminer.gui.flow.processrendering.draw.ProcessDrawer; import com.rapidminer.gui.new_plotter.MasterOfDesaster; import com.rapidminer.gui.new_plotter.configuration.DataTableColumn; import com.rapidminer.gui.new_plotter.configuration.DataTableColumn.ValueType; import com.rapidminer.gui.new_plotter.configuration.DimensionConfig; import com.rapidminer.gui.new_plotter.configuration.DimensionConfig.PlotDimension; import com.rapidminer.gui.new_plotter.configuration.PlotConfiguration; import com.rapidminer.gui.new_plotter.configuration.RangeAxisConfig; import com.rapidminer.gui.new_plotter.configuration.ValueSource; import com.rapidminer.gui.new_plotter.data.PlotInstance; import com.rapidminer.gui.new_plotter.engine.jfreechart.JFreeChartPlotEngine; import com.rapidminer.gui.new_plotter.engine.jfreechart.link_and_brush.LinkAndBrushChartPanel; import com.rapidminer.gui.new_plotter.gui.cellrenderer.DataTableColumnListCellRenderer; import com.rapidminer.gui.new_plotter.gui.cellrenderer.EnumComboBoxCellRenderer; import com.rapidminer.gui.new_plotter.gui.dnd.DataTableColumnListTransferHandler; import com.rapidminer.gui.new_plotter.gui.dnd.PlotConfigurationTreeTransferHandler; import com.rapidminer.gui.new_plotter.gui.treenodes.DimensionConfigTreeNode; import com.rapidminer.gui.new_plotter.gui.treenodes.PlotConfigurationTreeNode; import com.rapidminer.gui.new_plotter.gui.treenodes.RangeAxisConfigTreeNode; import com.rapidminer.gui.new_plotter.gui.treenodes.ValueSourceTreeNode; import com.rapidminer.gui.new_plotter.listener.MasterOfDesasterListener; import com.rapidminer.gui.new_plotter.listener.PlotConfigurationProcessingListener; import com.rapidminer.gui.new_plotter.listener.PlotInstanceChangedListener; import com.rapidminer.gui.new_plotter.listener.events.PlotConfigurationChangeEvent; import com.rapidminer.gui.new_plotter.utility.DataTransformation; import com.rapidminer.gui.tools.ExtendedHTMLJEditorPane; import com.rapidminer.gui.tools.ResourceAction; import com.rapidminer.gui.tools.ResourceLabel; import com.rapidminer.gui.tools.SwingTools; import com.rapidminer.gui.tools.dialogs.ConfirmDialog; import com.rapidminer.tools.I18N; /** * @author Nils Woehler * */ public class ChartConfigurationPanel extends AbstractConfigurationPanel implements MasterOfDesasterListener, DragListener, PlotConfigurationProcessingListener, PrintableComponent { private static final Insets STANDARD_INSETS = new Insets(2, 2, 2, 2); private static final long serialVersionUID = 1L; private static final int LEFT_SIDE_WIDTH = 100; private static final Dimension MAX_LIST_AND_TREE_DIMENSION = new Dimension(LEFT_SIDE_WIDTH, 300); private static final Dimension MIN_LIST_AND_TREE_DIMENSION = new Dimension(LEFT_SIDE_WIDTH, 100); private static final Dimension PREFFERED_LIST_AND_TREE_DIMENSION = new Dimension(LEFT_SIDE_WIDTH, 170); private static final int PREFERRED_CHART_HEIGTH = 500; private static final int PERFERRED_CHART_WIDTH = 600; private static final int MAX_CHART_HEIGTH = 650; private static final int MAX_CHART_WIDTH = 780; private static final int MIN_CHART_HEIGTH = 200; private static final int MIN_CHART_WIDTH = 200; private static final Dimension PREFERRED_CHART_SIZE = new Dimension(PERFERRED_CHART_WIDTH, PREFERRED_CHART_HEIGTH); private static final Dimension MAX_CHART_SIZE = new Dimension(MAX_CHART_WIDTH, MAX_CHART_HEIGTH); private static final Dimension MIN_CHART_SIZE = new Dimension(MIN_CHART_WIDTH, MIN_CHART_HEIGTH); private static final String EMPTY = "Empty"; private static final String GLOBAL_CONFIG = "GlobalConfig"; private static final String RANGE_AXIS_CONFIG = "RangeAxisConfig"; private static final String VALUE_SOURCE_CONFIG = "ValueSourceConfig"; private static final String COLOR_DIMENSION_CONFIG = "ColorDimensionConfig"; private static final String SIZE_DIMENSION_CONFIG = "SizeDimensionConfig"; private static final String SHAPE_DIMENSION_CONFIG = "ShapeDimensionConfig"; private static final String DOMAIN_DIMENSION = "DomainDimension"; private DataTable dataTable; private final JFreeChartPlotEngine plotEngine; private final LinkAndBrushChartPanel chartPanel; private JList<DataTableColumn> attributeList = new JList<>(); private ExtendedHTMLJEditorPane statusTextArea; private PlotConfigurationTree plotConfigurationTree; private JPanel configurationContainerPanel; private final boolean smallIcons; private boolean allowCollapse = true; private DataTableColumnListTransferHandler attributeListTransferHandler; private JPopupMenu rangeAxisPopupMenu = new JPopupMenu("Remove Axis"); private JPopupMenu valueSourcePopupMenu; private JPopupMenu dimensionConfigPopupMenu; private static final Color DROP_BORDER_COLOR = ProcessDrawer.BORDER_DRAG_COLOR; private static final Border ONGOING_DROP_BORDER = BorderFactory.createLineBorder(DROP_BORDER_COLOR, 2); private static final Border DROP_ENDED_BORDER = BorderFactory.createEmptyBorder(2, 2, 2, 2); private JScrollPane plotConfigurationTreeScrollPane; private PlotConfigurationTreeTransferHandler plotConfigurationTreeTransferHandler; private JPanel leftSideConfigPanel; private JPanel leftSideShowHiddenPanel; private JScrollPane chartPanelScrollPane; private JScrollPane statusTextAreaScrollPane; private MasterOfDesaster masterOfDesaster; // private DimensionDialog dialog; private JButton resetButton; private JMenuItem removeRangeAxisMenuItem; private JMenuItem removeAttributeFromDimensionMenuItem; private transient DataTable cachedDePivotedDataTable; private JComboBox<DatasetTransformationType> datasetTransformationSelectionComboBox; private JButton configureDataSetTransformationButton; private PlotConfiguration dePivotedPlotConfig; private String exampleSetSource = null; public ChartConfigurationPanel(boolean smallIcons, PlotInstance plotInstance, DataTable dataTable, PlotConfiguration dePivotedPlotConfig, String exampleSetSource) { this(smallIcons, plotInstance, dataTable, dePivotedPlotConfig); this.exampleSetSource = exampleSetSource; } public ChartConfigurationPanel(boolean smallIcons, PlotInstance plotInstance, DataTable dataTable) { super(plotInstance); this.smallIcons = smallIcons; this.dataTable = dataTable; this.cachedDePivotedDataTable = null; this.plotEngine = new JFreeChartPlotEngine(plotInstance, true); chartPanel = plotEngine.getChartPanel(); plotEngine.startInitializing(); masterOfDesaster = plotInstance.getMasterOfDesaster(); createComponents(); registerAsPlotConfigurationListener(); plotConfigurationTree.setSelectionPath(plotConfigurationTree.getPathForRow(0)); masterOfDesaster.addListener(this); attributeListTransferHandler.addDragListener(this); plotConfigurationTreeTransferHandler.addDragListener(this); attributeList.requestFocusInWindow(); plotConfigurationTree.expandAll(); plotEngine.endInitializing(); } public ChartConfigurationPanel(boolean smallIcons, PlotInstance plotInstance, DataTable dataTable, PlotConfiguration dePivotedPlotConfig) { this(smallIcons, plotInstance, dataTable); this.dePivotedPlotConfig = dePivotedPlotConfig; } /** * Returns the de-pivoted data table. If grouping attributes is not <code>null</code> a now data * table is created in any case. If grouping attributes is <code>null</code> the cached * datatable, if available, is returned. * * @param selectedNominalToNumericAttributesList2 * * @return <code>null</code> if creating the dePivoted datatable fails, a dePivoted datatable * otherwise. */ private DataTable getDePivotedDataTable(Collection<String> excludedNumericalAttributeList, Collection<String> selectedNominalToNumericAttributesList) { if (cachedDePivotedDataTable == null || selectedNominalToNumericAttributesList != null || excludedNumericalAttributeList != null) { Vector<DataTableColumn> dataTableColumns = assembleDataTableColumnList(dataTable); List<String> selectedNumericAttributes = new ArrayList<>(); for (DataTableColumn column : dataTableColumns) { if (column.isNumerical() && !excludedNumericalAttributeList.contains(column.getName())) { selectedNumericAttributes.add(column.getName()); } } ExampleSet metaSet = DataTransformation.createDePivotizedExampleSet( DataTableExampleSetAdapter.createExampleSetFromDataTable(dataTable), selectedNumericAttributes, selectedNominalToNumericAttributesList); // in case of error or no attributes specified if (metaSet == null) { return null; } cachedDePivotedDataTable = new DataTableExampleSetAdapter(metaSet, null); } return cachedDePivotedDataTable; } @Override public void print(Graphics pg) { JPanel printPanel = new JPanel() { private static final long serialVersionUID = 7315234075649335574L; @Override public void paintComponent(Graphics g) { Graphics2D g2 = (Graphics2D) g; plotEngine.getChartPanel().print(g2); } }; Insets insets = plotEngine.getChartPanel().getInsets(); int w = plotEngine.getChartPanel().getWidth() - insets.left - insets.right; int h = plotEngine.getChartPanel().getHeight() - insets.top - insets.bottom; printPanel.setSize(new Dimension(w, h)); printPanel.print(pg); } private void createPopups() { // creat popup menus { rangeAxisPopupMenu = new JPopupMenu(); removeRangeAxisMenuItem = new JMenuItem( I18N.getGUILabel("plotter.configuration_dialog.remove_axis_menu_item.label")); removeRangeAxisMenuItem.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { removeRangeAxisAction(); } }); rangeAxisPopupMenu.add(removeRangeAxisMenuItem); valueSourcePopupMenu = new JPopupMenu(); JMenuItem menuItem = new JMenuItem( I18N.getGUILabel("plotter.configuration_dialog.remove_source_menu_item.label")); menuItem.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { removeValueSourceAction(); } }); valueSourcePopupMenu.add(menuItem); dimensionConfigPopupMenu = new JPopupMenu(); removeAttributeFromDimensionMenuItem = new JMenuItem( I18N.getGUILabel("plotter.configuration_dialog.remove_attribute_menu_item.label")); removeAttributeFromDimensionMenuItem.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { removeAttributeFromDimensionAction(); } }); dimensionConfigPopupMenu.add(removeAttributeFromDimensionMenuItem); } } /** * * Returns a new PlotInstance with a dePivoted data table of the original data table. If the * transformation could not be achieved, <code>null</code> is returned. If no plot config for * the new PlotInstance is provided a PlotConfiguration with an invalid domain dimension will be * created. * * @param nominalToNumericalAttributeList */ private PlotInstance getNewDePivotedPlotInstance(PlotConfiguration newPlotConfig, Collection<String> excludedNumericalAttributeList, Collection<String> selectedNominalToNumericAttributesList) { PlotInstance plotInstance; DataTable transformed = getDePivotedDataTable(excludedNumericalAttributeList, selectedNominalToNumericAttributesList); if (transformed == null) { return null; } if (newPlotConfig == null) { newPlotConfig = new PlotConfiguration(new DataTableColumn("-Empty-", ValueType.INVALID)); } plotInstance = new PlotInstance(newPlotConfig, transformed); return plotInstance; } private void createComponents() { // set gridbag layout this.setLayout(new GridBagLayout()); createPopups(); GridBagConstraints itemConstraint = new GridBagConstraints(); leftSideConfigPanel = createLeftSidePanel(); itemConstraint = new GridBagConstraints(); itemConstraint.fill = GridBagConstraints.BOTH; itemConstraint.weightx = 0; itemConstraint.weighty = 1; itemConstraint.insets = new Insets(5, 10, 2, 2); this.add(leftSideConfigPanel, itemConstraint); leftSideShowHiddenPanel = new JPanel(new GridBagLayout()); leftSideShowHiddenPanel.setBackground(Color.red); JButton showConfigButton = new JButton(new ResourceAction(smallIcons, "plotter.configuration_dialog.show_configuration") { private static final long serialVersionUID = 1L; @Override public void actionPerformed(ActionEvent e) { leftSideShowHiddenPanel.setVisible(false); leftSideConfigPanel.setVisible(true); statusTextArea.setVisible(true); statusTextAreaScrollPane.setVisible(true); chartPanel.chartChanged(null); } }); itemConstraint = new GridBagConstraints(); itemConstraint.insets = STANDARD_INSETS; itemConstraint.anchor = GridBagConstraints.NORTH; itemConstraint.weightx = 0; itemConstraint.weighty = 0; itemConstraint.fill = GridBagConstraints.NONE; itemConstraint.gridwidth = GridBagConstraints.REMAINDER; leftSideShowHiddenPanel.add(showConfigButton, itemConstraint); itemConstraint = new GridBagConstraints(); itemConstraint.anchor = GridBagConstraints.SOUTH; itemConstraint.weightx = 1; itemConstraint.weighty = 1; itemConstraint.fill = GridBagConstraints.BOTH; itemConstraint.gridwidth = GridBagConstraints.REMAINDER; leftSideShowHiddenPanel.add(new JPanel(), itemConstraint); itemConstraint = new GridBagConstraints(); itemConstraint.fill = GridBagConstraints.BOTH; itemConstraint.insets = STANDARD_INSETS; itemConstraint.weightx = 0; itemConstraint.weighty = 1; leftSideShowHiddenPanel.setVisible(false); this.add(leftSideShowHiddenPanel, itemConstraint); JPanel rightSidePanel = createRightSidePanel(); itemConstraint = new GridBagConstraints(); itemConstraint.gridwidth = GridBagConstraints.REMAINDER; // end row itemConstraint.insets = STANDARD_INSETS; itemConstraint.weightx = 1; itemConstraint.weighty = 1; itemConstraint.fill = GridBagConstraints.BOTH; this.add(rightSidePanel, itemConstraint); } private JPanel createRightSidePanel() { GridBagConstraints itemConstraint; // create panel for right side JPanel rightSidePanel = new JPanel(new GridBagLayout()); // add chart panel to box { chartPanelScrollPane = new JScrollPane(chartPanel); chartPanelScrollPane.setPreferredSize(PREFERRED_CHART_SIZE); chartPanelScrollPane.setMaximumSize(MAX_CHART_SIZE); chartPanelScrollPane.setMinimumSize(MIN_CHART_SIZE); chartPanelScrollPane.setBorder(null); chartPanelScrollPane.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED); chartPanelScrollPane.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED); itemConstraint = new GridBagConstraints(); itemConstraint.fill = GridBagConstraints.BOTH; itemConstraint.weightx = 1; itemConstraint.weighty = 0.5; itemConstraint.gridwidth = GridBagConstraints.REMAINDER; itemConstraint.insets = STANDARD_INSETS; rightSidePanel.add(chartPanelScrollPane, itemConstraint); } // add status text field { statusTextArea = new ExtendedHTMLJEditorPane("", masterOfDesaster.toHtmlString()); statusTextArea.installDefaultStylesheet(); statusTextArea.setEditable(false); statusTextArea.setFocusable(false); statusTextArea.setDoubleBuffered(true); statusTextAreaScrollPane = new JScrollPane(statusTextArea); statusTextAreaScrollPane.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER); statusTextAreaScrollPane.setPreferredSize(new Dimension(PERFERRED_CHART_WIDTH, 100)); statusTextAreaScrollPane.setMinimumSize(new Dimension(MIN_CHART_WIDTH, 70)); statusTextAreaScrollPane.setMaximumSize(new Dimension(MAX_CHART_WIDTH, 120)); statusTextAreaScrollPane.setWheelScrollingEnabled(true); itemConstraint = new GridBagConstraints(); itemConstraint.fill = GridBagConstraints.BOTH; itemConstraint.weightx = 1; itemConstraint.weighty = 0.1; itemConstraint.gridwidth = GridBagConstraints.REMAINDER; itemConstraint.insets = STANDARD_INSETS; rightSidePanel.add(statusTextAreaScrollPane, itemConstraint); } return rightSidePanel; } /** * @return */ private JPanel createLeftSidePanel() { GridBagConstraints itemConstraint = new GridBagConstraints(); Insets standardInsets = STANDARD_INSETS; JPanel leftSidePanel = new JPanel(new GridBagLayout()); // add attribute selection combobox { JPanel datasetTranformationContainerPanel = new JPanel(new GridBagLayout()); { ResourceLabel datasetTransformationLabel = new ResourceLabel("plotter.datasetTransformation"); datasetTransformationLabel.setFont(datasetTransformationLabel.getFont().deriveFont(Font.BOLD)); itemConstraint = new GridBagConstraints(); itemConstraint.gridx = 0; itemConstraint.weightx = 0.0; itemConstraint.gridwidth = 1; itemConstraint.anchor = GridBagConstraints.WEST; itemConstraint.insets = new Insets(0, 0, 0, 5); datasetTranformationContainerPanel.add(datasetTransformationLabel, itemConstraint); datasetTransformationSelectionComboBox = new JComboBox<>(DatasetTransformationType.values()); itemConstraint = new GridBagConstraints(); itemConstraint.gridwidth = GridBagConstraints.REMAINDER; itemConstraint.fill = GridBagConstraints.HORIZONTAL; itemConstraint.insets = standardInsets; datasetTransformationSelectionComboBox.addPopupMenuListener(new PopupMenuListener() { @Override public void popupMenuWillBecomeVisible(PopupMenuEvent e) { return; } @Override public void popupMenuWillBecomeInvisible(PopupMenuEvent e) { if (datasetTransformationSelectionComboBox.getSelectedItem() != getCurrentTranformationType()) { DatasetTransformationType type = (DatasetTransformationType) datasetTransformationSelectionComboBox .getSelectedItem(); if (!changeDatatableTransformationType(type, false)) { datasetTransformationSelectionComboBox.setSelectedItem(getCurrentTranformationType()); } configureDataSetTransformationButton .setEnabled((DatasetTransformationType) datasetTransformationSelectionComboBox .getSelectedItem() != DatasetTransformationType.ORIGINAL); } } @Override public void popupMenuCanceled(PopupMenuEvent e) { return; } }); datasetTransformationSelectionComboBox .setRenderer(new EnumComboBoxCellRenderer<>("plotter.DatasetTransformationType")); itemConstraint = new GridBagConstraints(); itemConstraint.gridx = 1; itemConstraint.weightx = 1.0; itemConstraint.gridwidth = GridBagConstraints.RELATIVE; itemConstraint.fill = GridBagConstraints.HORIZONTAL; datasetTranformationContainerPanel.add(datasetTransformationSelectionComboBox, itemConstraint); configureDataSetTransformationButton = new JButton(new ResourceAction(smallIcons, "plotter.configure_dataset_transformation") { private static final long serialVersionUID = 1L; @Override public void actionPerformed(ActionEvent e) { DatasetTransformationType type = (DatasetTransformationType) datasetTransformationSelectionComboBox .getSelectedItem(); changeDatatableTransformationType(type, true); } }); itemConstraint = new GridBagConstraints(); itemConstraint.gridx = 2; itemConstraint.weightx = 0.0; itemConstraint.gridwidth = GridBagConstraints.REMAINDER; // end row itemConstraint.fill = GridBagConstraints.NONE; configureDataSetTransformationButton.setEnabled(false); datasetTranformationContainerPanel.add(configureDataSetTransformationButton, itemConstraint); } itemConstraint = new GridBagConstraints(); itemConstraint.weightx = 1.0; itemConstraint.gridwidth = GridBagConstraints.REMAINDER; // end row itemConstraint.fill = GridBagConstraints.HORIZONTAL; leftSidePanel.add(datasetTranformationContainerPanel, itemConstraint); } JLabel attributeListLabel = new ResourceLabel("plotter.configuration_dialog.attribute_list"); { Font oldFont = attributeListLabel.getFont(); JPanel labelPanel = new JPanel(); labelPanel.setLayout(new BoxLayout(labelPanel, BoxLayout.LINE_AXIS)); attributeListLabel.setFont(new Font(oldFont.getFamily(), Font.BOLD, oldFont.getSize())); // add attribute list label labelPanel.add(attributeListLabel); labelPanel.add(new JLabel(" ")); // add attribute drag from here label JLabel dragLabel = new ResourceLabel("plotter.configuration_dialog.drag_from_here"); labelPanel.add(dragLabel); labelPanel.add(Box.createHorizontalGlue()); itemConstraint = new GridBagConstraints(); itemConstraint.weightx = 1.0; itemConstraint.weighty = 0; itemConstraint.fill = GridBagConstraints.HORIZONTAL; itemConstraint.gridwidth = GridBagConstraints.RELATIVE; itemConstraint.insets = STANDARD_INSETS; leftSidePanel.add(labelPanel, itemConstraint); } // add panel with reset and hide buttons { JPanel buttonPanel = new JPanel(new GridBagLayout()); // add reset configuration button { resetButton = new JButton( new ResourceAction(smallIcons, "plotter.configuration_dialog.reset_configuration") { private static final long serialVersionUID = 1L; @Override public void actionPerformed(ActionEvent e) { if (ConfirmDialog.showConfirmDialogWithOptionalCheckbox( ApplicationFrame.getApplicationFrame(), "plotter.configuration_dialog.reset_configuration", ConfirmDialog.YES_NO_OPTION, null, ConfirmDialog.NO_OPTION, false) == ConfirmDialog.YES_OPTION) { getPlotConfiguration().resetToDefaults(); } } }); itemConstraint = new GridBagConstraints(); itemConstraint.gridwidth = GridBagConstraints.RELATIVE; itemConstraint.insets = STANDARD_INSETS; buttonPanel.add(resetButton, itemConstraint); } // add hide configuration button { JButton hideButton = new JButton(new ResourceAction(smallIcons, "plotter.configuration_dialog.hide_configuration") { private static final long serialVersionUID = 1L; @Override public void actionPerformed(ActionEvent e) { boolean visible = !leftSideConfigPanel.isVisible(); leftSideConfigPanel.setVisible(visible); statusTextArea.setVisible(visible); statusTextAreaScrollPane.setVisible(visible); leftSideShowHiddenPanel.setVisible(!visible); chartPanel.chartChanged(null); } }); itemConstraint = new GridBagConstraints(); itemConstraint.gridwidth = GridBagConstraints.REMAINDER; itemConstraint.insets = STANDARD_INSETS; buttonPanel.add(hideButton, itemConstraint); } // actually add panel { itemConstraint = new GridBagConstraints(); itemConstraint.gridwidth = GridBagConstraints.REMAINDER; itemConstraint.insets = STANDARD_INSETS; leftSidePanel.add(buttonPanel, itemConstraint); } } // add attribute list { // assemble attributeVector Vector<DataTableColumn> attributeVector = assembleDataTableColumnList(dataTable); // add attribute list to panel attributeList = new JList<>(attributeVector); attributeListLabel.setLabelFor(attributeList); attributeList.setDragEnabled(true); attributeList.setCellRenderer(new DataTableColumnListCellRenderer()); attributeListTransferHandler = new DataTableColumnListTransferHandler(); attributeList.setTransferHandler(attributeListTransferHandler); attributeList.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION); JScrollPane attributeListScrollPane = new JScrollPane(attributeList); attributeListScrollPane.setPreferredSize(PREFFERED_LIST_AND_TREE_DIMENSION); attributeListScrollPane.setMaximumSize(MAX_LIST_AND_TREE_DIMENSION); attributeListScrollPane.setMinimumSize(MIN_LIST_AND_TREE_DIMENSION); attributeListScrollPane.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER); attributeListScrollPane.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED); itemConstraint = new GridBagConstraints(); itemConstraint.fill = GridBagConstraints.BOTH; itemConstraint.weightx = 1; itemConstraint.weighty = 0.5; itemConstraint.gridwidth = GridBagConstraints.REMAINDER; itemConstraint.insets = standardInsets; leftSidePanel.add(attributeListScrollPane, itemConstraint); } // add configarution tree label JLabel plotConfigurationLabel = new ResourceLabel("plotter.configuration_dialog.chart_configuration"); { Font oldFont = plotConfigurationLabel.getFont(); JPanel labelPanel = new JPanel(); labelPanel.setLayout(new BoxLayout(labelPanel, BoxLayout.LINE_AXIS)); plotConfigurationLabel.setFont(new Font(oldFont.getFamily(), Font.BOLD, oldFont.getSize())); // add attribute list label labelPanel.add(plotConfigurationLabel); labelPanel.add(new JLabel(" ")); // add attribute drag from here label JLabel dropLabel = new ResourceLabel("plotter.configuration_dialog.drop_here"); labelPanel.add(dropLabel); labelPanel.add(Box.createHorizontalGlue()); itemConstraint = new GridBagConstraints(); itemConstraint.weightx = 1.0; itemConstraint.weighty = 0; itemConstraint.fill = GridBagConstraints.HORIZONTAL; itemConstraint.gridwidth = GridBagConstraints.REMAINDER; itemConstraint.insets = STANDARD_INSETS; leftSidePanel.add(labelPanel, itemConstraint); } // add plot configuration tree { // create tree plotConfigurationTree = new PlotConfigurationTree(getPlotConfiguration(), dataTable, attributeListTransferHandler); plotConfigurationTree.addTreeSelectionListener(new TreeSelectionListener() { @Override public void valueChanged(TreeSelectionEvent e) { TreePath newLeadSelectionPath = e.getNewLeadSelectionPath(); if (newLeadSelectionPath == null) { CardLayout cl = (CardLayout) configurationContainerPanel.getLayout(); cl.show(configurationContainerPanel, EMPTY); return; } Object lastPathComponent = newLeadSelectionPath.getLastPathComponent(); if (lastPathComponent instanceof PlotConfigurationTreeNode) { CardLayout cl = (CardLayout) configurationContainerPanel.getLayout(); cl.show(configurationContainerPanel, GLOBAL_CONFIG); } else if (lastPathComponent instanceof RangeAxisConfigTreeNode) { CardLayout cl = (CardLayout) configurationContainerPanel.getLayout(); cl.show(configurationContainerPanel, RANGE_AXIS_CONFIG); } else if (lastPathComponent instanceof ValueSourceTreeNode) { CardLayout cl = (CardLayout) configurationContainerPanel.getLayout(); cl.show(configurationContainerPanel, VALUE_SOURCE_CONFIG); } else if (lastPathComponent instanceof DimensionConfigTreeNode) { CardLayout cl = (CardLayout) configurationContainerPanel.getLayout(); switch (((DimensionConfigTreeNode) lastPathComponent).getDimension()) { case COLOR: cl.show(configurationContainerPanel, COLOR_DIMENSION_CONFIG); break; case SHAPE: cl.show(configurationContainerPanel, SHAPE_DIMENSION_CONFIG); break; case DOMAIN: cl.show(configurationContainerPanel, DOMAIN_DIMENSION); break; case SIZE: cl.show(configurationContainerPanel, SIZE_DIMENSION_CONFIG); break; default: throw new RuntimeException("Unsupported Dimension Config.. this should not happen"); } } // else { // throw new RuntimeException("Unknown TreeNode type. This should not happen"); // } } }); MouseAdapter ma = new MouseAdapter() { private void myPopupEvent(MouseEvent e) { int x = e.getX(); int y = e.getY(); JTree tree = (JTree) e.getSource(); TreePath path = tree.getPathForLocation(x, y); if (path == null) { return; } tree.setSelectionPath(path); TreeNode node = (TreeNode) path.getLastPathComponent(); if (node instanceof RangeAxisConfigTreeNode) { PlotConfiguration plotConfig = (PlotConfiguration) ((DefaultMutableTreeNode) tree.getModel() .getRoot()).getUserObject(); removeRangeAxisMenuItem.setEnabled(plotConfig.getRangeAxisCount() > 1); rangeAxisPopupMenu.show(tree, x, y); } else if (node instanceof ValueSourceTreeNode) { valueSourcePopupMenu.show(tree, x, y); } else if (node instanceof DimensionConfigTreeNode) { // only enable remove attribute from dimension action when there is actually // one there DimensionConfig config = ((DimensionConfigTreeNode) node).getUserObject(); if (config != null) { if (config.getDimension() == PlotDimension.DOMAIN && getPlotConfiguration().getDomainConfigManager().getDataTableColumn().getValueType() == ValueType.INVALID) { removeAttributeFromDimensionMenuItem.setEnabled(false); } else { removeAttributeFromDimensionMenuItem.setEnabled(true); } } else { removeAttributeFromDimensionMenuItem.setEnabled(false); } dimensionConfigPopupMenu.show(tree, x, y); } } @Override public void mousePressed(MouseEvent e) { if (e.isPopupTrigger()) { myPopupEvent(e); } } @Override public void mouseReleased(MouseEvent e) { if (e.isPopupTrigger()) { myPopupEvent(e); } } }; plotConfigurationTree.addMouseListener(ma); plotConfigurationTree.addKeyListener(new KeyListener() { @Override public void keyTyped(KeyEvent e) { return; // Nothing to be done } @Override public void keyReleased(KeyEvent e) { return; // Nothing to be done } @Override public void keyPressed(KeyEvent e) { int key = e.getKeyCode(); if (key == KeyEvent.VK_DELETE) { JTree tree = (JTree) e.getSource(); TreePath selectionPath = plotConfigurationTree.getSelectionPath(); if (selectionPath != null) { Object node = selectionPath.getLastPathComponent(); if (node instanceof RangeAxisConfigTreeNode) { PlotConfiguration plotConfig = (PlotConfiguration) ((DefaultMutableTreeNode) tree.getModel() .getRoot()).getUserObject(); if (plotConfig.getRangeAxisCount() > 1) { removeRangeAxisAction(); } } else if (node instanceof ValueSourceTreeNode) { removeValueSourceAction(); } else if (node instanceof DimensionConfigTreeNode) { removeAttributeFromDimensionAction(); } } e.consume(); } } }); plotConfigurationTree.addTreeWillExpandListener(new TreeWillExpandListener() { @Override public void treeWillExpand(TreeExpansionEvent event) throws ExpandVetoException { return; // Nothing to be done } @Override public void treeWillCollapse(TreeExpansionEvent event) throws ExpandVetoException { // this stops the tree from collapsing if (!allowCollapse) { throw new ExpandVetoException(event); } } }); // create Transferhandler plotConfigurationTreeTransferHandler = new PlotConfigurationTreeTransferHandler(plotConfigurationTree); plotConfigurationTree.setTransferHandler(plotConfigurationTreeTransferHandler); plotConfigurationTreeScrollPane = new JScrollPane(plotConfigurationTree); plotConfigurationLabel.setLabelFor(plotConfigurationTree); plotConfigurationTreeScrollPane.setPreferredSize(PREFFERED_LIST_AND_TREE_DIMENSION); plotConfigurationTreeScrollPane.setMaximumSize(MAX_LIST_AND_TREE_DIMENSION); plotConfigurationTreeScrollPane.setMinimumSize(MIN_LIST_AND_TREE_DIMENSION); plotConfigurationTreeScrollPane.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER); plotConfigurationTreeScrollPane.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED); plotConfigurationTreeScrollPane.setBorder(DROP_ENDED_BORDER); itemConstraint = new GridBagConstraints(); itemConstraint.fill = GridBagConstraints.BOTH; itemConstraint.weightx = 1; itemConstraint.weighty = 1; itemConstraint.gridwidth = GridBagConstraints.REMAINDER; itemConstraint.insets = standardInsets; leftSidePanel.add(plotConfigurationTreeScrollPane, itemConstraint); } // add container for configuration { configurationContainerPanel = new JPanel(new CardLayout()); // add global config panel { JPanel globalConfigPanel = new GlobalConfigurationPanel(getCurrentPlotInstance()); addPlotInstanceChangeListener((PlotInstanceChangedListener) globalConfigPanel); configurationContainerPanel.add(globalConfigPanel, GLOBAL_CONFIG); } // add range axis config panel { JPanel rangeAxisPanel = new RangeAxisConfigPanel(plotConfigurationTree, getCurrentPlotInstance()); addPlotInstanceChangeListener((PlotInstanceChangedListener) rangeAxisPanel); configurationContainerPanel.add(rangeAxisPanel, RANGE_AXIS_CONFIG); } // add plot axes config panel { JPanel plotAxesConfigPanel = new ValueSourceConfigurationPanel(smallIcons, plotConfigurationTree, attributeListTransferHandler, getCurrentPlotInstance()); addPlotInstanceChangeListener((PlotInstanceChangedListener) plotAxesConfigPanel); configurationContainerPanel.add(plotAxesConfigPanel, VALUE_SOURCE_CONFIG); } // add domain dimension config panel { JPanel domainDimensionPanel = new DimensionConfigPanel(PlotDimension.DOMAIN, plotConfigurationTree, getCurrentPlotInstance()); addPlotInstanceChangeListener((PlotInstanceChangedListener) domainDimensionPanel); configurationContainerPanel.add(domainDimensionPanel, DOMAIN_DIMENSION); } // add empty panel { configurationContainerPanel.add(new JPanel(), EMPTY); } // add color dimension config panel { JPanel dimensionConfigPanel = new DimensionConfigPanel(PlotDimension.COLOR, plotConfigurationTree, getCurrentPlotInstance()); addPlotInstanceChangeListener((PlotInstanceChangedListener) dimensionConfigPanel); configurationContainerPanel.add(dimensionConfigPanel, COLOR_DIMENSION_CONFIG); } // add shape dimension config panel { JPanel dimensionConfigPanel = new DimensionConfigPanel(PlotDimension.SHAPE, plotConfigurationTree, getCurrentPlotInstance()); addPlotInstanceChangeListener((PlotInstanceChangedListener) dimensionConfigPanel); configurationContainerPanel.add(dimensionConfigPanel, SHAPE_DIMENSION_CONFIG); } // add size dimension config panel { JPanel dimensionConfigPanel = new DimensionConfigPanel(PlotDimension.SIZE, plotConfigurationTree, getCurrentPlotInstance()); addPlotInstanceChangeListener((PlotInstanceChangedListener) dimensionConfigPanel); configurationContainerPanel.add(dimensionConfigPanel, SIZE_DIMENSION_CONFIG); } CardLayout cl = (CardLayout) configurationContainerPanel.getLayout(); cl.show(configurationContainerPanel, EMPTY); itemConstraint = new GridBagConstraints(); itemConstraint.fill = GridBagConstraints.BOTH; itemConstraint.insets = STANDARD_INSETS; itemConstraint.weightx = 1; itemConstraint.weighty = 0; itemConstraint.gridwidth = GridBagConstraints.REMAINDER; leftSidePanel.add(configurationContainerPanel, itemConstraint); } return leftSidePanel; } private void removeRangeAxisAction() { RangeAxisConfigTreeNode node = (RangeAxisConfigTreeNode) plotConfigurationTree.getSelectionPath() .getLastPathComponent(); RangeAxisConfig axisConfig = node.getUserObject(); getPlotConfiguration().removeRangeAxisConfig(axisConfig); } private void removeValueSourceAction() { ValueSourceTreeNode node = (ValueSourceTreeNode) plotConfigurationTree.getSelectionPath().getLastPathComponent(); ValueSource source = node.getUserObject(); RangeAxisConfigTreeNode parent = (RangeAxisConfigTreeNode) node.getParent(); if (parent == null) { new RuntimeErrorException(null, "ValueSource Node has no parent!!").printStackTrace(); } else { RangeAxisConfig rangeAxis = parent.getUserObject(); rangeAxis.removeValueSource(source); } } private void removeAttributeFromDimensionAction() { DimensionConfigTreeNode node = (DimensionConfigTreeNode) plotConfigurationTree.getSelectionPath() .getLastPathComponent(); DimensionConfig config = node.getUserObject(); if (config != null) { if (config.getDimension() != PlotDimension.DOMAIN) { getPlotConfiguration().setDimensionConfig(config.getDimension(), null); } else { getPlotConfiguration().getDomainConfigManager().setDataTableColumn( new DataTableColumn(null, ValueType.INVALID)); } } } /** * Inspects the {@link DataTable} and assembles a model for the attribute selection list. */ private Vector<DataTableColumn> assembleDataTableColumnList(DataTable dataTable) { Vector<DataTableColumn> dataTableColumnVector = new Vector<DataTableColumn>(); int numberOfColumns = dataTable.getNumberOfColumns(); for (int i = 0; i < numberOfColumns; ++i) { dataTableColumnVector.add(new DataTableColumn(dataTable, i)); } return dataTableColumnVector; } private boolean changeDatatableTransformationType(DatasetTransformationType type, boolean reconfigure) { PlotInstance plotInstance = getPlotInstance(type); if (plotInstance == null || reconfigure) { if (type == DatasetTransformationType.DE_PIVOTED) { List<DataTableColumn> columns = new LinkedList<DataTableColumn>(); // fetch nominal columns Vector<DataTableColumn> dataTableColumns = assembleDataTableColumnList(dataTable); for (DataTableColumn column : dataTableColumns) { if ((column.isNominal() || column.isNumerical()) && !column.getName().equals("id") && !column.getName().equals("value") && !column.getName().equals("attribute")) { columns.add(column); } } Collection<String> selectedAttributeExclusionList = null; // show grouping attributes configuration dialog if nominal attributes have been // found AttributeSelectionDialog dialog = new AttributeSelectionDialog(columns); dialog.setVisible(true); if (dialog.wasConfirmed()) { selectedAttributeExclusionList = dialog.getSelectedAttributeNames(); } else { return false; } Collection<String> nominalToNumericalAttributeList = new LinkedList<String>(); Collection<String> excludedNumericalAttributesList = new LinkedList<String>(); if (selectedAttributeExclusionList != null) { for (DataTableColumn column : dataTableColumns) { String name = column.getName(); if (selectedAttributeExclusionList.contains(name)) { if (column.isNumerical()) { excludedNumericalAttributesList.add(name); } } else { if (column.isNominal()) { nominalToNumericalAttributeList.add(name); } } } } // if a plotInstance already exists and the data should be reconfigured, save // current PlotConfig PlotConfiguration currentPlotConfiguration = null; if (plotInstance != null) { currentPlotConfiguration = plotInstance.getMasterPlotConfiguration(); } else { currentPlotConfiguration = dePivotedPlotConfig; } plotInstance = getNewDePivotedPlotInstance(currentPlotConfiguration, excludedNumericalAttributesList, nominalToNumericalAttributeList); if (plotInstance == null) { SwingTools.showVerySimpleErrorMessage("plotter.no_datatable_for_transformation_type", type); return false; } } else { SwingTools.showVerySimpleErrorMessage("plotter.no_datatable_for_transformation_type", type); return false; } } plotInstanceChanged(null, plotInstance, type); return true; } @Override public void masterOfDesasterChanged(MasterOfDesaster masterOfDesaster) { final String htmlString = masterOfDesaster.toHtmlString(); if (statusTextArea != null) { // String oldHtmlString = statusTextArea.getText(); // if (!(htmlString.contains("ok.png") && oldHtmlString.contains("ok.png"))) { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { statusTextArea.setText(htmlString); statusTextArea.setCaretPosition(0); statusTextAreaScrollPane.scrollRectToVisible(new Rectangle(1, 1, 1, 1)); } }); // } } } @Override public void dragStarted(Transferable t) { TransferSupport support = new TransferSupport(this, t); boolean doesSupportFlavor = ((PlotConfigurationTreeTransferHandler) plotConfigurationTree.getTransferHandler()) .doesSupportFlavor(support); if (doesSupportFlavor) { if (SwingUtilities.isEventDispatchThread()) { switch (RapidMinerGUI.getDragHighlighteMode()) { case FULL: plotConfigurationTree.setBackground(ProcessDrawer.INNER_DRAG_COLOR); case BORDER: plotConfigurationTreeScrollPane.setBorder(ONGOING_DROP_BORDER); break; default: break; } } else { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { switch (RapidMinerGUI.getDragHighlighteMode()) { case FULL: plotConfigurationTree.setBackground(ProcessDrawer.INNER_DRAG_COLOR); case BORDER: plotConfigurationTreeScrollPane.setBorder(ONGOING_DROP_BORDER); break; default: break; } } }); } } } @Override public void dragEnded() { if (SwingUtilities.isEventDispatchThread()) { plotConfigurationTreeScrollPane.setBorder(DROP_ENDED_BORDER); plotConfigurationTree.setBackground(Color.WHITE); } else { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { plotConfigurationTreeScrollPane.setBorder(DROP_ENDED_BORDER); plotConfigurationTree.setBackground(Color.WHITE); } }); } } @Override protected void adaptGUI() { plotConfigurationTree.treeDidChange(); } @Override public boolean plotConfigurationChanged(PlotConfigurationChangeEvent change) { if (plotConfigurationTree == null) { return true; } switch (change.getType()) { case DIMENSION_CONFIG_ADDED: adaptGUI(); break; case DIMENSION_CONFIG_CHANGED: adaptGUI(); break; case DIMENSION_CONFIG_REMOVED: adaptGUI(); break; case RANGE_AXIS_CONFIG_ADDED: adaptGUI(); break; case RANGE_AXIS_CONFIG_CHANGED: adaptGUI(); break; case RANGE_AXIS_CONFIG_REMOVED: adaptGUI(); break; case META_CHANGE: adaptGUI(); break; default: } return true; } @Override public void startProcessing() { resetButton.setEnabled(false); } @Override public void endProcessing() { resetButton.setEnabled(true); } @Override public void plotInstanceChanged(PlotInstance oldPlotInstance, PlotInstance newPlotInstance, DatasetTransformationType newType) { // remove old listeners unregisterAsPlotConfigurationListener(); masterOfDesaster.removeListener(this); // change to new PlotInstance setPlotInstance(newPlotInstance, newType); // register as listener masterOfDesaster = getCurrentPlotInstance().getMasterOfDesaster(); masterOfDesaster.addListener(this); registerAsPlotConfigurationListener(); // get new attribute list Vector<DataTableColumn> columnList = new Vector<DataTableColumn>(); switch (newType) { case DE_PIVOTED: columnList = assembleDataTableColumnList(getDePivotedDataTable(null, null)); break; case ORIGINAL: default: columnList = assembleDataTableColumnList(dataTable); } // adapt attribute list to selection DefaultListModel<DataTableColumn> attributeListModel = new DefaultListModel<>(); for (DataTableColumn column : columnList) { attributeListModel.addElement(column); } attributeList.setModel(attributeListModel); // adapt config tree to selection PlotConfigurationTreeModel newModel = (PlotConfigurationTreeModel) plotConfigurationTree.getModel(); newModel.exchangePlotConfiguration(getPlotConfiguration()); plotConfigurationTree.expandAll(); // tell plotEngine that PlotInstance has changed plotEngine.setPlotInstance(newPlotInstance); } public JFreeChartPlotEngine getPlotEngine() { return plotEngine; } @Override public Component getExportComponent() { // create new LinkAndBrushChartPanel with double buffering set to false to get vector // graphic export // The real chart has to use double buffering for a) performance and b) zoom rectangle // drawing LinkAndBrushChartPanel newLaBPanel = new LinkAndBrushChartPanel(getPlotEngine().getChartPanel().getChart(), getPlotEngine().getChartPanel().getWidth(), getPlotEngine().getChartPanel().getHeight(), getPlotEngine() .getChartPanel().getMinimumDrawWidth(), getPlotEngine().getChartPanel().getMinimumDrawHeight(), false, false); newLaBPanel.setSize(getPlotEngine().getChartPanel().getSize()); newLaBPanel.setOverlayList(getPlotEngine().getChartPanel().getOverlayList()); return newLaBPanel; } @Override public String getExportIconName() { return I18N.getGUIMessage("gui.cards.result_view.advanced_charts.icon"); } @Override public String getExportName() { return I18N.getMessage(I18N.getGUIBundle(), "gui.cards.result_view.advanced_charts.title"); } @Override public String getIdentifier() { return exampleSetSource; } }