/** * 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.dnd; import com.rapidminer.gui.dnd.AbstractPatchedTransferHandler; import com.rapidminer.gui.new_plotter.ChartConfigurationException; import com.rapidminer.gui.new_plotter.configuration.DataTableColumn; import com.rapidminer.gui.new_plotter.configuration.DataTableColumn.ValueType; import com.rapidminer.gui.new_plotter.configuration.DefaultDimensionConfig; import com.rapidminer.gui.new_plotter.configuration.DimensionConfig; import com.rapidminer.gui.new_plotter.configuration.DimensionConfig.PlotDimension; import com.rapidminer.gui.new_plotter.configuration.DomainConfigManager; import com.rapidminer.gui.new_plotter.configuration.PlotConfiguration; import com.rapidminer.gui.new_plotter.configuration.RangeAxisConfig; import com.rapidminer.gui.new_plotter.configuration.SeriesFormat; import com.rapidminer.gui.new_plotter.configuration.ValueGrouping.GroupingType; import com.rapidminer.gui.new_plotter.configuration.ValueGrouping.ValueGroupingFactory; import com.rapidminer.gui.new_plotter.configuration.ValueSource; import com.rapidminer.gui.new_plotter.configuration.ValueSource.SeriesUsageType; import com.rapidminer.gui.new_plotter.gui.PlotConfigurationTree; import com.rapidminer.gui.new_plotter.gui.PlotConfigurationTreeModel; 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.tools.SwingTools; import com.rapidminer.tools.I18N; import com.rapidminer.tools.math.function.aggregation.AbstractAggregationFunction.AggregationFunctionType; import java.awt.Component; import java.awt.Font; import java.awt.MouseInfo; import java.awt.Point; import java.awt.PointerInfo; import java.awt.datatransfer.Transferable; import java.awt.datatransfer.UnsupportedFlavorException; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.io.IOException; import java.util.LinkedList; import java.util.List; import javax.swing.Icon; import javax.swing.JComponent; import javax.swing.JMenuItem; import javax.swing.JPopupMenu; import javax.swing.JTree; import javax.swing.SwingUtilities; import javax.swing.TransferHandler; import javax.swing.tree.DefaultMutableTreeNode; import javax.swing.tree.TreeNode; import javax.swing.tree.TreePath; /** * The {@link TransferHandler} for the {@link PlotConfigurationTree}. It handles dropping * {@link DataTableColumn}s on the tree and exporting {@link ValueSource}s and * {@link RangeAxisConfig}s. * * @author Nils Woehler * */ public class PlotConfigurationTreeTransferHandler extends AbstractPatchedTransferHandler { private static final long serialVersionUID = 1L; private static final String[] OPTIONS = { I18N.getMessage(I18N.getGUIBundle(), "gui.label.input.plotter.drop_below_last_range_axis.option.add_value_source.label"), I18N.getMessage(I18N.getGUIBundle(), "gui.label.input.plotter.drop_below_last_range_axis.option.create_new_axis.label"), I18N.getMessage(I18N.getGUIBundle(), "gui.label.plotter.configuration_dialog.cancel_menu_item.label"), I18N.getMessage(I18N.getGUIBundle(), "gui.label.input.plotter.drop_below_last_range_axis.option.move_value_source_to_axis_end.label") }; private static final String[] VALUE_SOURCE_OPTIONS = { I18N.getMessage(I18N.getGUIBundle(), "gui.label.input.plotter.drop_on_value_source.option.exchange_main_column.label"), I18N.getMessage(I18N.getGUIBundle(), "gui.label.input.plotter.drop_on_value_source.option.utility1.label"), I18N.getMessage(I18N.getGUIBundle(), "gui.label.input.plotter.drop_on_value_source.option.utility2.label"), }; private final JTree parent; public PlotConfigurationTreeTransferHandler(JTree parent) { this.parent = parent; } /* * ***************** EXPORT ********************** */ @Override public int getSourceActions(JComponent c) { return MOVE; } @Override public Icon getVisualRepresentation(Transferable t) { // ImageIcon iicon = null; // String i18nKey = null; // if (t.isDataFlavorSupported(RangeAxisConfigTreeNode.RANGE_AXIS_CONFIG_FLAVOR)) { // i18nKey = "plotter.configuration_dialog.range_axis"; // } else if (t.isDataFlavorSupported(ValueSourceTreeNode.VALUE_SOURCE_FLAVOR)) { // i18nKey = "plotter.configuration_dialog.value_source"; // } // if (i18nKey != null) { // // get icon // String icon = I18N.getMessageOrNull(I18N.getGUIBundle(), "gui.label." + i18nKey + // ".icon"); // if (icon != null) { // iicon = SwingTools.createIcon("16/" + icon); // } // } // return iicon; return null; } @Override public Transferable createTransferable(JComponent c) { JTree tree = (JTree) c; TreePath selectionPath = tree.getSelectionPath(); Object lastPathComponent = selectionPath.getLastPathComponent(); Transferable t = null; if (lastPathComponent instanceof ValueSourceTreeNode) { t = (ValueSourceTreeNode) lastPathComponent; } else if (lastPathComponent instanceof RangeAxisConfigTreeNode) { t = (RangeAxisConfigTreeNode) lastPathComponent; } return t; } /* * ****************** IMPORT ****************** */ @Override public boolean canImport(TransferSupport support) { // only support drops if (!support.isDrop()) { updateDropDeniedTooltip((JComponent) support.getComponent(), null); return false; } if (!doesSupportFlavor(support)) { updateDropDeniedTooltip((JComponent) support.getComponent(), null); return false; } // fetch the drop location JTree.DropLocation dropLocation = (javax.swing.JTree.DropLocation) support.getDropLocation(); // get path, if path is null importing is not possible TreePath path = dropLocation.getPath(); if (path == null) { updateDropDeniedTooltip((JComponent) support.getComponent(), null); return false; } Object lastPathComponent = path.getLastPathComponent(); // make drop location and transferable dependent decision int childIndex = dropLocation.getChildIndex(); Transferable transferable = support.getTransferable(); // check if drop on tree component is possible String actionDescription = isDropOnTreeComponentPossible(support.getComponent(), (TreeNode) lastPathComponent, transferable, childIndex); if (actionDescription == null) { return false; } // check if the source actions (a bitwise-OR of supported actions) // contains the COPY action boolean copySupported = (COPY & support.getSourceDropActions()) == (COPY); if (copySupported) { support.setDropAction(COPY); updateDropDeniedTooltip((JComponent) support.getComponent(), actionDescription); return true; } boolean moveSupported = (MOVE & support.getSourceDropActions()) == (MOVE); if (moveSupported) { support.setDropAction(MOVE); updateDropDeniedTooltip((JComponent) support.getComponent(), actionDescription); return true; } updateDropDeniedTooltip((JComponent) support.getComponent(), null); // reject the transfer if copy or move is not supported return false; } public boolean doesSupportFlavor(TransferSupport support) { // check if transferable is DataTableColumn, ValueSourceTreeNode or // RangeAxisConfigTreeNode if (!(support.isDataFlavorSupported(ValueSourceTreeNode.VALUE_SOURCE_FLAVOR) || support.isDataFlavorSupported(RangeAxisConfigTreeNode.RANGE_AXIS_CONFIG_FLAVOR) || support .isDataFlavorSupported(DataTableColumnCollection.DATATABLE_COLUMN_COLLECTION_FLAVOR))) { return false; } return true; } @Override public boolean importData(TransferSupport support) { try { // check if transferable can be imported if (!canImport(support)) { return false; } // fetch the drop location JTree.DropLocation dropLocation = (javax.swing.JTree.DropLocation) support.getDropLocation(); TreePath path = dropLocation.getPath(); int childIndex = dropLocation.getChildIndex(); support.setShowDropLocation(true); if (support.isDataFlavorSupported(RangeAxisConfigTreeNode.RANGE_AXIS_CONFIG_FLAVOR)) { rangeAxisConfigDrop(support, path, childIndex); } else if (support.isDataFlavorSupported(ValueSourceTreeNode.VALUE_SOURCE_FLAVOR)) { ValueSourceTreeNode valueSourceTreeNode = (ValueSourceTreeNode) support.getTransferable().getTransferData( ValueSourceTreeNode.VALUE_SOURCE_FLAVOR); valueSourceDrop(valueSourceTreeNode, path, childIndex, false); } else if (support.isDataFlavorSupported(DataTableColumnCollection.DATATABLE_COLUMN_COLLECTION_FLAVOR)) { dataTableColumnDrop(support, path, childIndex); } else { return false; } return true; } catch (Exception e) { e.printStackTrace(); return false; } } /** * Returns <code>null</code> if drop is not possible. Returns string that describes action that * would happen on drop otherwise. */ public String isDropOnTreeComponentPossible(Component container, TreeNode treeNode, Transferable transferable, int childIndex) { try { if (transferable.isDataFlavorSupported(ValueSourceTreeNode.VALUE_SOURCE_FLAVOR)) { return canImportValueSource(container, treeNode, transferable, childIndex); } else if (transferable.isDataFlavorSupported(RangeAxisConfigTreeNode.RANGE_AXIS_CONFIG_FLAVOR)) { return canImportRangeAxis(container, treeNode, childIndex, container); } else if (transferable.isDataFlavorSupported(DataTableColumnCollection.DATATABLE_COLUMN_COLLECTION_FLAVOR)) { return canImportDataTableColumnCollection(container, treeNode, transferable, childIndex); } return null; } catch (IOException e) { // Should not happen, has been checked before e.printStackTrace(); return null; } catch (UnsupportedFlavorException e) { // Should not happen, has been checked before e.printStackTrace(); return null; } } /** * @param container * @param treeNode * @param transferable * @param childIndex * @return */ private String canImportDataTableColumnCollection(Component container, TreeNode treeNode, Transferable transferable, int childIndex) { String importAction = I18N.getGUILabel("plotter.drag_and_drop.column_drop.create_or_add"); DataTableColumnCollection collection; try { collection = (DataTableColumnCollection) transferable .getTransferData(DataTableColumnCollection.DATATABLE_COLUMN_COLLECTION_FLAVOR); } catch (UnsupportedFlavorException e) { return null; } catch (IOException e) { return null; } int size = collection.getDataTableColumns().size(); if (size == 0) { updateToolTip(container, I18N.getGUILabel("plotter.drag_and_drop.column_drop.error")); return null; } if (treeNode instanceof RangeAxisConfigTreeNode) { if (childIndex == -1 || childIndex < treeNode.getChildCount()) { return I18N.getGUILabel("plotter.drag_and_drop.column_drop.add_to_axis"); } return importAction; } else if (treeNode instanceof PlotConfigurationTreeNode) { if (childIndex > -1 && childIndex < PlotConfigurationTreeModel.NUMBER_OF_PERMANENT_DIMENSIONS) { updateToolTip(container, I18N.getGUILabel("plotter.drag_and_drop.column_drop.cant_drop_between_dims")); return null; } if (childIndex == PlotConfigurationTreeModel.NUMBER_OF_PERMANENT_DIMENSIONS) { return I18N.getGUILabel("plotter.drag_and_drop.column_drop.create_after_dims"); } if (childIndex == -1) { return I18N.getGUILabel("plotter.drag_and_drop.column_drop.create_at_end"); } return importAction; } else if (size == 1) { if (treeNode instanceof DimensionConfigTreeNode) { importAction = I18N.getGUILabel("plotter.drag_and_drop.column_drop.set_dim_column"); } else if (treeNode instanceof ValueSourceTreeNode) { // check if value source allwos adding value type importAction = I18N.getGUILabel("plotter.drag_and_drop.column_drop.drop_on_data_config"); } return importAction; } else { updateToolTip(container, I18N.getGUILabel("plotter.drag_and_drop.column_drop.drop_collection_not_possible")); return null; } } /** * @param container * @param comp * @param childIndex * @param container2 */ private String canImportRangeAxis(Component container, TreeNode treeNode, int childIndex, Component container2) { String importAction = I18N.getGUILabel("plotter.drag_and_drop.axis_drop.move_axis"); // RangeAxisConfigTreeNode can only be dropped on PlotConfigTreeNodes if (!(treeNode instanceof PlotConfigurationTreeNode)) { updateToolTip(container, I18N.getGUILabel("plotter.drag_and_drop.axis_drop.cant_drop_axis_config")); return null; } // can only be dropped below static dimension configs if (childIndex > -1 && childIndex < PlotConfigurationTreeModel.NUMBER_OF_PERMANENT_DIMENSIONS) { updateToolTip(container, I18N.getGUILabel("plotter.drag_and_drop.axis_drop.only_below_dims")); return null; } if (childIndex == -1) { return I18N.getGUILabel("plotter.drag_and_drop.axis_drop.move_to_end"); } return importAction; } /** * @param comp * @param transferable * @param childIndex * @throws UnsupportedFlavorException * @throws IOException */ private String canImportValueSource(Component comp, TreeNode treeNode, Transferable transferable, int childIndex) throws UnsupportedFlavorException, IOException { String importAction = null; // ValueSourceTreeNodes can only be dropped on RangeAxisConfigTreeNodes and // PlotConfigTreeNodes if (treeNode instanceof RangeAxisConfigTreeNode) { if (childIndex == -1) { importAction = I18N.getGUILabel("plotter.drag_and_drop.source_drop.move_to_axis_end"); } else { importAction = I18N.getGUILabel("plotter.drag_and_drop.source_drop.move_or_change_position"); } } else if (treeNode instanceof PlotConfigurationTreeNode) { // can only be dropped below static dimension configs if (childIndex > -1 && childIndex < PlotConfigurationTreeModel.NUMBER_OF_PERMANENT_DIMENSIONS) { updateToolTip(comp, I18N.getGUILabel("plotter.drag_and_drop.source_drop.only_below_dims")); return null; } if (childIndex == -1) { importAction = I18N.getGUILabel("plotter.drag_and_drop.column_drop.create_at_end"); } else { importAction = I18N.getGUILabel("plotter.drag_and_drop.source_drop.create_new_or_add"); } } else { updateToolTip(comp, I18N.getGUILabel("plotter.drag_and_drop.source_drop.cant_drop")); return null; } return importAction; } private void updateToolTip(Component comp, String reason) { if (comp instanceof JComponent) { JComponent jComp = (JComponent) comp; updateDropDeniedTooltip(jComp, reason); } } /** * Is called if a {@link ValueSourceTreeNode} is dropped on the {@link PlotConfigurationTree}. */ private void valueSourceDrop(final ValueSourceTreeNode valueSourceTreeNode, TreePath path, final int childIndex, boolean importDataTableColumn) throws UnsupportedFlavorException, IOException, ChartConfigurationException { // fetch dropped value source and parent final ValueSource valueSource = valueSourceTreeNode.getUserObject(); Object lastPathComponent = path.getLastPathComponent(); if (lastPathComponent instanceof RangeAxisConfigTreeNode) { valueSourceDropOnRangeAxisConfig(valueSourceTreeNode, childIndex, valueSource, lastPathComponent); } else { valueSourceDropOnPlotConfiguration(valueSourceTreeNode, childIndex, valueSource, lastPathComponent, importDataTableColumn); } } private void valueSourceDropOnPlotConfiguration(final ValueSourceTreeNode valueSourceTreeNode, final int childIndex, final ValueSource valueSource, Object lastPathComponent, final boolean importedDataTableColumn) { // get plot configuration final PlotConfigurationTreeNode plotConfigurationTreeNode = (PlotConfigurationTreeNode) lastPathComponent; final PlotConfiguration plotConfiguration = plotConfigurationTreeNode.getUserObject(); final int index = childIndex - PlotConfigurationTreeModel.NUMBER_OF_PERMANENT_DIMENSIONS; if (index >= 0) { valueSourceDropOnPlotConfigBelowDimensionConfigs(valueSourceTreeNode, childIndex, valueSource, plotConfigurationTreeNode, plotConfiguration, index, importedDataTableColumn); } else { // value source is dropped on PlotConfigurationTreeNode // fetch old parent RangeAxisConfigTreeNode parent = (RangeAxisConfigTreeNode) valueSourceTreeNode.getParent(); boolean process = plotConfiguration.isProcessingEvents(); // save processing state plotConfiguration.setProcessEvents(false); if (parent != null) { // ValueSource is moved to new RangeAxisConfig and has a parent // Remove from old RangeAxis RangeAxisConfig oldRangeAxis = parent.getUserObject(); oldRangeAxis.removeValueSource(valueSource); } // create new RangeAxis RangeAxisConfig newRangeAxis = new RangeAxisConfig(null, plotConfiguration); SeriesFormat newSeriesFormat = null; if (importedDataTableColumn) { newSeriesFormat = plotConfiguration.getAutomaticSeriesFormatForNextValueSource(newRangeAxis); } // user drops on plot configuration tree node newRangeAxis.addValueSource(valueSource, newSeriesFormat); plotConfiguration.addRangeAxisConfig(newRangeAxis); plotConfiguration.setProcessEvents(process); } } private void valueSourceDropOnPlotConfigBelowDimensionConfigs(final ValueSourceTreeNode valueSourceTreeNode, final int childIndex, final ValueSource valueSource, final PlotConfigurationTreeNode plotConfigurationTreeNode, final PlotConfiguration plotConfiguration, final int index, final boolean importedDataTableColumn) { if (index > 0) { valueSourceDropOnPlotConfigBelowLastRangeAxis(valueSourceTreeNode, childIndex, valueSource, plotConfigurationTreeNode, plotConfiguration, index, importedDataTableColumn); } else { // fetch old parent RangeAxisConfigTreeNode parent = (RangeAxisConfigTreeNode) valueSourceTreeNode.getParent(); boolean process = plotConfiguration.isProcessingEvents(); // save processing state plotConfiguration.setProcessEvents(false); if (parent != null) { // ValueSource is moved to new RangeAxisConfig and has a parent // Remove from old RangeAxis RangeAxisConfig oldRangeAxis = parent.getUserObject(); oldRangeAxis.removeValueSource(valueSource); } RangeAxisConfig newRangeAxis = new RangeAxisConfig(null, plotConfiguration); // fetch new series format of data table column is imported SeriesFormat newSeriesFormat = null; if (importedDataTableColumn) { newSeriesFormat = plotConfiguration.getAutomaticSeriesFormatForNextValueSource(newRangeAxis); } // add value source newRangeAxis.addValueSource(valueSource, newSeriesFormat); // add range axis to plot configuration plotConfiguration.addRangeAxisConfig(index, newRangeAxis); plotConfiguration.setProcessEvents(process); } } private void valueSourceDropOnPlotConfigBelowLastRangeAxis(final ValueSourceTreeNode valueSourceTreeNode, final int childIndex, final ValueSource valueSource, final PlotConfigurationTreeNode plotConfigurationTreeNode, final PlotConfiguration plotConfiguration, final int index, final boolean importedDataTableColumn) { // ask user for advice // create popup menu final JPopupMenu rangeAxisPopupMenu = new JPopupMenu(); // get last range axis config final RangeAxisConfig rangeAxis = (RangeAxisConfig) ((DefaultMutableTreeNode) plotConfigurationTreeNode .getChildAt(childIndex - 1)).getUserObject(); String menuItemText = OPTIONS[0]; if (!importedDataTableColumn) { menuItemText = OPTIONS[3]; } JMenuItem menuItem = new JMenuItem(menuItemText); menuItem.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { // user wants to add value source to range axis config // fetch old parent RangeAxisConfigTreeNode parent = (RangeAxisConfigTreeNode) valueSourceTreeNode.getParent(); boolean process = plotConfiguration.isProcessingEvents(); plotConfiguration.setProcessEvents(false); if (parent != null) { // ValueSource is moved to new RangeAxisConfig and has a parent // Remove from old RangeAxis RangeAxisConfig oldRangeAxis = parent.getUserObject(); oldRangeAxis.removeValueSource(valueSource); } // fetch new series format of data table column is imported SeriesFormat newSeriesFormat = null; if (importedDataTableColumn) { newSeriesFormat = plotConfiguration.getAutomaticSeriesFormatForNextValueSource(rangeAxis); } // add value source rangeAxis.addValueSource(valueSource, newSeriesFormat); plotConfiguration.setProcessEvents(process); } }); rangeAxisPopupMenu.add(menuItem); menuItem = new JMenuItem(OPTIONS[1]); menuItem.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { // user wants to create a new range axis config // fetch old parent RangeAxisConfigTreeNode parent = (RangeAxisConfigTreeNode) valueSourceTreeNode.getParent(); boolean process = plotConfiguration.isProcessingEvents(); // save processing state plotConfiguration.setProcessEvents(false); if (parent != null) { // ValueSource is moved to new RangeAxisConfig and has a parent // Remove from old RangeAxis RangeAxisConfig oldRangeAxis = parent.getUserObject(); oldRangeAxis.removeValueSource(valueSource); } RangeAxisConfig newRangeAxis = new RangeAxisConfig(null, plotConfiguration); // add range axis to plot configuration plotConfiguration.addRangeAxisConfig(index, newRangeAxis); // fetch new series format of data table column is imported SeriesFormat newSeriesFormat = null; if (importedDataTableColumn) { newSeriesFormat = plotConfiguration.getAutomaticSeriesFormatForNextValueSource(newRangeAxis); } // add value source newRangeAxis.addValueSource(valueSource, newSeriesFormat); plotConfiguration.setProcessEvents(process); // restore old processing state } }); rangeAxisPopupMenu.add(menuItem); rangeAxisPopupMenu.addSeparator(); menuItem = new JMenuItem(OPTIONS[2]); Font font = menuItem.getFont(); menuItem.setFont(new Font(font.getFamily(), Font.ITALIC, font.getSize())); rangeAxisPopupMenu.add(menuItem); // get mouse position PointerInfo mouseInfo = MouseInfo.getPointerInfo(); Point point = mouseInfo.getLocation(); SwingUtilities.convertPointFromScreen(point, parent); // show popup menu rangeAxisPopupMenu.show(parent, (int) point.getX(), (int) point.getY()); } private void valueSourceDropOnRangeAxisConfig(final ValueSourceTreeNode valueSourceTreeNode, final int childIndex, final ValueSource valueSource, Object lastPathComponent) { // fetch last path range axis tree node and containing range axis RangeAxisConfigTreeNode rangeAxisTreeNode = (RangeAxisConfigTreeNode) lastPathComponent; RangeAxisConfig rangeAxis = rangeAxisTreeNode.getUserObject(); PlotConfiguration plotConfig = (PlotConfiguration) ((DefaultMutableTreeNode) rangeAxisTreeNode.getParent()) .getUserObject(); // fetch old parent RangeAxisConfigTreeNode parent = (RangeAxisConfigTreeNode) valueSourceTreeNode.getParent(); if (parent != lastPathComponent) { // ValueSource is moved to new RangeAxisConfig // Remove from old RangeAxis RangeAxisConfig oldRangeAxis = parent.getUserObject(); boolean process = plotConfig.isProcessingEvents(); plotConfig.setProcessEvents(false); oldRangeAxis.removeValueSource(valueSource); // Add to new RangeAxis if (childIndex < 0) { rangeAxis.addValueSource(valueSource, null); } else { rangeAxis.addValueSource(childIndex, valueSource, null); } plotConfig.setProcessEvents(process); } else { // ValueSource is moved within old parent RangeAxisConfig if (childIndex < 0) { rangeAxis.changeIndex(rangeAxis.getSize() - 1, valueSource); } else { rangeAxis.changeIndex(childIndex, valueSource); } } } private void rangeAxisConfigDrop(TransferSupport support, TreePath path, int childIndex) throws UnsupportedFlavorException, IOException { // fetch dropped value source and parent RangeAxisConfigTreeNode rangeAxisConfigTreeNode = (RangeAxisConfigTreeNode) support.getTransferable() .getTransferData(RangeAxisConfigTreeNode.RANGE_AXIS_CONFIG_FLAVOR); RangeAxisConfig rangeAxis = rangeAxisConfigTreeNode.getUserObject(); PlotConfigurationTreeNode root = (PlotConfigurationTreeNode) rangeAxisConfigTreeNode.getParent(); PlotConfiguration plotConfig = root.getUserObject(); int maxIndex = plotConfig.getRangeAxisCount() - 1; int newIndex = childIndex - PlotConfigurationTreeModel.NUMBER_OF_PERMANENT_DIMENSIONS; if (childIndex < 0 || newIndex > maxIndex) { plotConfig.changeIndex(maxIndex, rangeAxis); } else { plotConfig.changeIndex(newIndex, rangeAxis); } } private void dataTableColumnDrop(TransferSupport support, TreePath path, final int childIndex) throws ChartConfigurationException, UnsupportedFlavorException, IOException { // fetch data table column Transferable transferable = support.getTransferable(); List<DataTableColumn> dataTableColumnList = new LinkedList<DataTableColumn>(); DataTableColumnCollection dataTableColumCollection = (DataTableColumnCollection) transferable .getTransferData(DataTableColumnCollection.DATATABLE_COLUMN_COLLECTION_FLAVOR); dataTableColumnList.addAll(dataTableColumCollection.getDataTableColumns()); Object lastPathComponent = path.getLastPathComponent(); // check dropping target if (lastPathComponent instanceof PlotConfigurationTreeNode) { // get plot configuration final PlotConfigurationTreeNode plotConfigurationTreeNode = (PlotConfigurationTreeNode) lastPathComponent; final PlotConfiguration plotConfiguration = plotConfigurationTreeNode.getUserObject(); List<RangeAxisConfig> rangeAxisConfigs = plotConfiguration.getRangeAxisConfigs(); int size = rangeAxisConfigs.size(); boolean grouping = false; if (size > 0) { grouping = plotConfiguration.isGroupingRequiredForNewValueSource(rangeAxisConfigs.get(size - 1)); } List<ValueSource> valueSources = new LinkedList<ValueSource>(); for (DataTableColumn dtc : dataTableColumnList) { ValueSource newValueSource = new ValueSource(plotConfiguration, dtc, AggregationFunctionType.average, grouping); valueSources.add(newValueSource); } if (dataTableColumnList.size() == 1) { valueSourceDrop(new ValueSourceTreeNode(valueSources.get(0)), path, childIndex, true); } else { multipleDataTableColumnsDropOnPlotConfigTreeNode(valueSources, path, childIndex); } } else if (lastPathComponent instanceof RangeAxisConfigTreeNode) { // get tree node RangeAxisConfigTreeNode rangeAxisConfigTreeNode = (RangeAxisConfigTreeNode) lastPathComponent; // get plot configuration PlotConfiguration plotConfiguration = ((PlotConfigurationTreeNode) (((RangeAxisConfigTreeNode) lastPathComponent) .getParent())).getUserObject(); // get range axis config RangeAxisConfig rangeAxis = rangeAxisConfigTreeNode.getUserObject(); // determine grouping properties of new value source boolean grouped = plotConfiguration.isGroupingRequiredForNewValueSource(rangeAxis); ValueSource referenceValueSource = null; if (!rangeAxis.getValueSources().isEmpty()) { referenceValueSource = rangeAxis.getValueSources().get(rangeAxis.getValueSources().size() - 1); } AggregationFunctionType newAggregationFunctionType = null; if (referenceValueSource != null) { newAggregationFunctionType = referenceValueSource.getAggregationFunctionType(SeriesUsageType.MAIN_SERIES); } else if (referenceValueSource == null && grouped) { newAggregationFunctionType = AggregationFunctionType.count; } boolean process = plotConfiguration.isProcessingEvents(); // save processing state plotConfiguration.setProcessEvents(false); // create new value source if (childIndex >= 0) { for (int i = dataTableColumnList.size() - 1; i >= 0; --i) { DataTableColumn dtc = dataTableColumnList.get(i); ValueSource newValueSource = new ValueSource(plotConfiguration, dtc, newAggregationFunctionType, grouped); rangeAxis.addValueSource(childIndex, newValueSource, plotConfiguration.getAutomaticSeriesFormatForNextValueSource(rangeAxis)); } } else { for (DataTableColumn dtc : dataTableColumnList) { ValueSource newValueSource = new ValueSource(plotConfiguration, dtc, newAggregationFunctionType, grouped); rangeAxis.addValueSource(newValueSource, plotConfiguration.getAutomaticSeriesFormatForNextValueSource(rangeAxis)); } } plotConfiguration.setProcessEvents(process); // restore old processing state // FROM HERE ON DROPPING ONLY ONE DATATABLECOLUMN IS POSSIBLE THUS GET(0) IS ALLOWED IN // THIS CASE } else if (lastPathComponent instanceof DimensionConfigTreeNode) { DataTableColumn dataTableColumn = dataTableColumnList.get(0); DimensionConfigTreeNode dimensionConfigTreeNode = (DimensionConfigTreeNode) lastPathComponent; PlotDimension dimension = dimensionConfigTreeNode.getDimension(); JTree dropTarget = ((JTree) support.getComponent()); PlotConfigurationTreeNode root = (PlotConfigurationTreeNode) dropTarget.getModel().getRoot(); PlotConfiguration plotConfig = root.getUserObject(); boolean process = plotConfig.isProcessingEvents(); plotConfig.setProcessEvents(false); // set processing false; if (dimension == PlotDimension.DOMAIN) { // get domain config manager DomainConfigManager domainConfigMngr = (DomainConfigManager) dimensionConfigTreeNode.getUserObject(); // change domain column domainConfigMngr.setDataTableColumn(dataTableColumn); } else { DimensionConfig dimensionConfig = dimensionConfigTreeNode.getUserObject(); // if there is a dimension config, change data table column ValueType valueType = dataTableColumn.getValueType(); if (dimensionConfig != null) { if (dimensionConfig.isGrouping()) { if (valueType != dimensionConfig.getDataTableColumn().getValueType()) { switch (valueType) { case NOMINAL: dimensionConfig.setGrouping(ValueGroupingFactory.getValueGrouping( GroupingType.DISTINCT_VALUES, dataTableColumn, true, dimensionConfig.getDateFormat())); break; case DATE_TIME: case NUMERICAL: dimensionConfig.setGrouping(ValueGroupingFactory.getValueGrouping( GroupingType.EQUIDISTANT_FIXED_BIN_COUNT, dataTableColumn, true, dimensionConfig.getDateFormat())); break; default: break; } } } dimensionConfig.setDataTableColumn(dataTableColumn); } else { // else create a new dimension config DefaultDimensionConfig newDimensionConfig = new DefaultDimensionConfig(plotConfig, dataTableColumn, dimension); boolean grouped = plotConfig.getDomainConfigManager().isGrouping(); if (grouped) { switch (valueType) { case NOMINAL: newDimensionConfig.setGrouping(ValueGroupingFactory.getValueGrouping( GroupingType.DISTINCT_VALUES, dataTableColumn, true, newDimensionConfig.getDateFormat())); break; case DATE_TIME: case NUMERICAL: newDimensionConfig.setGrouping(ValueGroupingFactory.getValueGrouping( GroupingType.EQUIDISTANT_FIXED_BIN_COUNT, dataTableColumn, true, newDimensionConfig.getDateFormat())); break; default: break; } } plotConfig.setDimensionConfig(dimension, newDimensionConfig); } } plotConfig.setProcessEvents(process); // restore processing state } else if (lastPathComponent instanceof ValueSourceTreeNode) { final DataTableColumn dataTableColumn = dataTableColumnList.get(0); ValueSourceTreeNode valueSourceNode = (ValueSourceTreeNode) lastPathComponent; final ValueSource valueSource = valueSourceNode.getUserObject(); ActionListener valueSourceExchangeAction = new ActionListener() { @Override public void actionPerformed(ActionEvent e) { // user wants to add exchange value source data table column if (valueSource instanceof ValueSource) { ValueSource source = valueSource; try { source.setDataTableColumn(SeriesUsageType.MAIN_SERIES, dataTableColumn); } catch (ChartConfigurationException e1) { e1.printStackTrace(); SwingTools.showVerySimpleErrorMessage("plotting.general_error"); } } } }; ActionListener valueSourceUtility1Action = new ActionListener() { @Override public void actionPerformed(ActionEvent e) { // user wants to add exchange value source data table column if (valueSource instanceof ValueSource) { ValueSource source = valueSource; try { source.setDataTableColumn(SeriesUsageType.INDICATOR_1, dataTableColumn); } catch (ChartConfigurationException e1) { e1.printStackTrace(); SwingTools.showVerySimpleErrorMessage("plotting.general_error"); } } } }; ActionListener valueSourceUtility2Action = new ActionListener() { @Override public void actionPerformed(ActionEvent e) { // user wants to add exchange value source data table column if (valueSource instanceof ValueSource) { ValueSource source = valueSource; try { source.setDataTableColumn(SeriesUsageType.INDICATOR_2, dataTableColumn); } catch (ChartConfigurationException e1) { e1.printStackTrace(); SwingTools.showVerySimpleErrorMessage("plotting.general_error"); } } } }; // create popup menu JPopupMenu valueSourcePopupMenu = new JPopupMenu(); JMenuItem menuItem = new JMenuItem(VALUE_SOURCE_OPTIONS[0]); menuItem.addActionListener(valueSourceExchangeAction); valueSourcePopupMenu.add(menuItem); menuItem = new JMenuItem(VALUE_SOURCE_OPTIONS[1]); menuItem.addActionListener(valueSourceUtility1Action); valueSourcePopupMenu.add(menuItem); menuItem = new JMenuItem(VALUE_SOURCE_OPTIONS[2]); menuItem.addActionListener(valueSourceUtility2Action); valueSourcePopupMenu.add(menuItem); valueSourcePopupMenu.addSeparator(); menuItem = new JMenuItem(OPTIONS[2]); Font font = menuItem.getFont(); menuItem.setFont(new Font(font.getFamily(), Font.ITALIC, font.getSize())); valueSourcePopupMenu.add(menuItem); // get mouse position PointerInfo mouseInfo = MouseInfo.getPointerInfo(); Point point = mouseInfo.getLocation(); SwingUtilities.convertPointFromScreen(point, parent); // show popup menu valueSourcePopupMenu.show(parent, (int) point.getX(), (int) point.getY()); } } private void multipleDataTableColumnsDropOnPlotConfigTreeNode(final List<ValueSource> valueSources, TreePath path, int childIndex) { Object lastPathComponent = path.getLastPathComponent(); // get plot configuration final PlotConfigurationTreeNode plotConfigurationTreeNode = (PlotConfigurationTreeNode) lastPathComponent; final PlotConfiguration plotConfiguration = plotConfigurationTreeNode.getUserObject(); final int index = childIndex - PlotConfigurationTreeModel.NUMBER_OF_PERMANENT_DIMENSIONS; if (index > 0) { // ask user for advice // create popup menu final JPopupMenu rangeAxisPopupMenu = new JPopupMenu(); // get last range axis config final RangeAxisConfig rangeAxis = (RangeAxisConfig) ((DefaultMutableTreeNode) plotConfigurationTreeNode .getChildAt(childIndex - 1)).getUserObject(); JMenuItem menuItem = new JMenuItem(OPTIONS[0]); menuItem.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { // user wants to add value sources to range axis config // save old processing state boolean tmp_processing = plotConfiguration.isProcessingEvents(); plotConfiguration.setProcessEvents(false); for (ValueSource valueSource : valueSources) { rangeAxis.addValueSource(valueSource, plotConfiguration.getAutomaticSeriesFormatForNextValueSource(rangeAxis)); } // restore old processing state plotConfiguration.setProcessEvents(tmp_processing); } }); rangeAxisPopupMenu.add(menuItem); menuItem = new JMenuItem(OPTIONS[1]); menuItem.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { // user wants to create a new range axis config // create new RangeAxis RangeAxisConfig newRangeAxis = new RangeAxisConfig(null, plotConfiguration); boolean process = plotConfiguration.isProcessingEvents(); // save old processing // state plotConfiguration.setProcessEvents(false); // add range axis to plot config plotConfiguration.addRangeAxisConfig(newRangeAxis); // add value sources for (ValueSource valueSource : valueSources) { newRangeAxis.addValueSource(valueSource, plotConfiguration.getAutomaticSeriesFormatForNextValueSource(rangeAxis)); } plotConfiguration.setProcessEvents(process); // restore old processing state } }); rangeAxisPopupMenu.add(menuItem); rangeAxisPopupMenu.addSeparator(); menuItem = new JMenuItem(OPTIONS[2]); Font font = menuItem.getFont(); menuItem.setFont(new Font(font.getFamily(), Font.ITALIC, font.getSize())); rangeAxisPopupMenu.add(menuItem); // get mouse position PointerInfo mouseInfo = MouseInfo.getPointerInfo(); Point point = mouseInfo.getLocation(); SwingUtilities.convertPointFromScreen(point, parent); // show popup menu rangeAxisPopupMenu.show(parent, (int) point.getX(), (int) point.getY()); } else { // value source is dropped on PlotConfigurationTreeNode // create new RangeAxis RangeAxisConfig newRangeAxis = new RangeAxisConfig(null, plotConfiguration); boolean tmp_processing = plotConfiguration.isProcessingEvents(); // save all processing // status plotConfiguration.setProcessEvents(false); plotConfiguration.addRangeAxisConfig(newRangeAxis); for (ValueSource valueSource : valueSources) { newRangeAxis.addValueSource(valueSource, plotConfiguration.getAutomaticSeriesFormatForNextValueSource(newRangeAxis)); } plotConfiguration.setProcessEvents(tmp_processing); // revert old processing status } } }