View | Details | Raw Unified | Return to bug 54834
Collapse All | Expand All

(-)src/core/org/apache/jmeter/gui/MainFrame.java (-42 / +28 lines)
Lines 49-54 import java.util.concurrent.atomic.AtomicInteger; Link Here
49
import javax.swing.BorderFactory;
49
import javax.swing.BorderFactory;
50
import javax.swing.Box;
50
import javax.swing.Box;
51
import javax.swing.BoxLayout;
51
import javax.swing.BoxLayout;
52
import javax.swing.DropMode;
52
import javax.swing.ImageIcon;
53
import javax.swing.ImageIcon;
53
import javax.swing.JButton;
54
import javax.swing.JButton;
54
import javax.swing.JComponent;
55
import javax.swing.JComponent;
Lines 74-79 import org.apache.jmeter.gui.action.ActionRouter; Link Here
74
import org.apache.jmeter.gui.action.LoadDraggedFile;
75
import org.apache.jmeter.gui.action.LoadDraggedFile;
75
import org.apache.jmeter.gui.tree.JMeterCellRenderer;
76
import org.apache.jmeter.gui.tree.JMeterCellRenderer;
76
import org.apache.jmeter.gui.tree.JMeterTreeListener;
77
import org.apache.jmeter.gui.tree.JMeterTreeListener;
78
import org.apache.jmeter.gui.tree.JMeterTreeTransferHandler;
77
import org.apache.jmeter.gui.util.EscapeDialog;
79
import org.apache.jmeter.gui.util.EscapeDialog;
78
import org.apache.jmeter.gui.util.JMeterMenuBar;
80
import org.apache.jmeter.gui.util.JMeterMenuBar;
79
import org.apache.jmeter.gui.util.JMeterToolBar;
81
import org.apache.jmeter.gui.util.JMeterToolBar;
Lines 151-162 public class MainFrame extends JFrame implements TestStateListener, Remoteable, Link Here
151
    /** The button used to display the running/stopped image. */
153
    /** The button used to display the running/stopped image. */
152
    private JButton runningIndicator;
154
    private JButton runningIndicator;
153
155
154
    /** The x coordinate of the last location where a component was dragged. */
155
    private int previousDragXLocation = 0;
156
157
    /** The y coordinate of the last location where a component was dragged. */
158
    private int previousDragYLocation = 0;
159
160
    /** The set of currently running hosts. */
156
    /** The set of currently running hosts. */
161
    private final Set<String> hosts = new HashSet<String>();
157
    private final Set<String> hosts = new HashSet<String>();
162
158
Lines 619-625 public class MainFrame extends JFrame implements TestStateListener, Remoteable, Link Here
619
                return null;
615
                return null;
620
                }
616
                }
621
            };
617
            };
622
           treevar.setToolTipText("");
618
        treevar.setToolTipText("");
623
        treevar.setCellRenderer(getCellRenderer());
619
        treevar.setCellRenderer(getCellRenderer());
624
        treevar.setRootVisible(false);
620
        treevar.setRootVisible(false);
625
        treevar.setShowsRootHandles(true);
621
        treevar.setShowsRootHandles(true);
Lines 627-634 public class MainFrame extends JFrame implements TestStateListener, Remoteable, Link Here
627
        treeListener.setJTree(treevar);
623
        treeListener.setJTree(treevar);
628
        treevar.addTreeSelectionListener(treeListener);
624
        treevar.addTreeSelectionListener(treeListener);
629
        treevar.addMouseListener(treeListener);
625
        treevar.addMouseListener(treeListener);
630
        treevar.addMouseMotionListener(treeListener);
631
        treevar.addKeyListener(treeListener);
626
        treevar.addKeyListener(treeListener);
627
        
628
        // enable drag&drop, install a custom transfer handler
629
        treevar.setDragEnabled(true);
630
        treevar.setDropMode(DropMode.ON_OR_INSERT);
631
        treevar.setTransferHandler(new JMeterTreeTransferHandler());
632
632
633
        return treevar;
633
        return treevar;
634
    }
634
    }
Lines 645-670 public class MainFrame extends JFrame implements TestStateListener, Remoteable, Link Here
645
    }
645
    }
646
646
647
    /**
647
    /**
648
     * Repaint pieces of the GUI as needed while dragging. This method should
649
     * only be called from the Swing event thread.
650
     *
651
     * @param dragIcon
652
     *            the component being dragged
653
     * @param x
654
     *            the current mouse x coordinate
655
     * @param y
656
     *            the current mouse y coordinate
657
     */
658
    public void drawDraggedComponent(Component dragIcon, int x, int y) {
659
        Dimension size = dragIcon.getPreferredSize();
660
        treePanel.paintImmediately(previousDragXLocation, previousDragYLocation, size.width, size.height);
661
        this.getLayeredPane().setLayer(dragIcon, 400);
662
        SwingUtilities.paintComponent(treePanel.getGraphics(), dragIcon, treePanel, x, y, size.width, size.height);
663
        previousDragXLocation = x;
664
        previousDragYLocation = y;
665
    }
666
667
    /**
668
     * A window adapter used to detect when the main JMeter frame is being
648
     * A window adapter used to detect when the main JMeter frame is being
669
     * closed.
649
     * closed.
670
     */
650
     */
Lines 710-729 public class MainFrame extends JFrame implements TestStateListener, Remoteable, Link Here
710
                if (flavors[i].isFlavorJavaFileListType()) {
690
                if (flavors[i].isFlavorJavaFileListType()) {
711
                    dtde.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);
691
                    dtde.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);
712
                    try {
692
                    try {
713
                        @SuppressWarnings("unchecked")
693
                        openJmxFilesFromDragAndDrop(tr);
714
                        List<File> files = (List<File>)
715
                                tr.getTransferData(DataFlavor.javaFileListFlavor);
716
                        if(files.isEmpty()) {
717
                            return;
718
                        }
719
                        File file = files.get(0);
720
                        if(!file.getName().endsWith(".jmx")) {
721
                            log.warn("Importing file:" + file.getName()+ "from DnD failed because file extension does not end with .jmx");
722
                            return;
723
                        }
724
725
                        ActionEvent fakeEvent = new ActionEvent(this, ActionEvent.ACTION_PERFORMED, ActionNames.OPEN);
726
                        LoadDraggedFile.loadProject(fakeEvent, file);
727
                    } finally {
694
                    } finally {
728
                        dtde.dropComplete(true);
695
                        dtde.dropComplete(true);
729
                    }
696
                    }
Lines 738-743 public class MainFrame extends JFrame implements TestStateListener, Remoteable, Link Here
738
705
739
    }
706
    }
740
707
708
    public boolean openJmxFilesFromDragAndDrop(Transferable tr) throws UnsupportedFlavorException, IOException {
709
        @SuppressWarnings("unchecked")
710
        List<File> files = (List<File>)
711
                tr.getTransferData(DataFlavor.javaFileListFlavor);
712
        if(files.isEmpty()) {
713
            return false;
714
        }
715
        File file = files.get(0);
716
        if(!file.getName().endsWith(".jmx")) {
717
            log.warn("Importing file:" + file.getName()+ "from DnD failed because file extension does not end with .jmx");
718
            return false;
719
        }
720
721
        ActionEvent fakeEvent = new ActionEvent(this, ActionEvent.ACTION_PERFORMED, ActionNames.OPEN);
722
        LoadDraggedFile.loadProject(fakeEvent, file);
723
        
724
        return true;
725
    }
726
741
    @Override
727
    @Override
742
    public void dropActionChanged(DropTargetDragEvent dtde) {
728
    public void dropActionChanged(DropTargetDragEvent dtde) {
743
        // NOOP
729
        // NOOP
(-)src/core/org/apache/jmeter/gui/action/ActionNames.java (-3 lines)
Lines 48-54 public final class ActionNames { Link Here
48
    public static final String DEBUG_ON         = "debug_on"; // $NON-NLS-1$
48
    public static final String DEBUG_ON         = "debug_on"; // $NON-NLS-1$
49
    public static final String DEBUG_OFF        = "debug_off"; // $NON-NLS-1$
49
    public static final String DEBUG_OFF        = "debug_off"; // $NON-NLS-1$
50
    public static final String DISABLE          = "disable"; // $NON-NLS-1$
50
    public static final String DISABLE          = "disable"; // $NON-NLS-1$
51
    public static final String DRAG_ADD         = "drag_n_drop.add";//$NON-NLS-1$
52
    /** Copy, then paste afterwards */
51
    /** Copy, then paste afterwards */
53
    public static final String DUPLICATE        = "duplicate"; // $NON-NLS-1$
52
    public static final String DUPLICATE        = "duplicate"; // $NON-NLS-1$
54
    public static final String EDIT             = "edit"; // $NON-NLS-1$
53
    public static final String EDIT             = "edit"; // $NON-NLS-1$
Lines 58-65 public final class ActionNames { Link Here
58
    public static final String FUNCTIONS        = "functions"; // $NON-NLS-1$
57
    public static final String FUNCTIONS        = "functions"; // $NON-NLS-1$
59
    public static final String HELP             = "help"; // $NON-NLS-1$
58
    public static final String HELP             = "help"; // $NON-NLS-1$
60
    public static final String HEAP_DUMP        = "heap_dump"; // $NON-NLS-1$
59
    public static final String HEAP_DUMP        = "heap_dump"; // $NON-NLS-1$
61
    public static final String INSERT_AFTER     = "drag_n_drop.insert_after";//$NON-NLS-1$
62
    public static final String INSERT_BEFORE    = "drag_n_drop.insert_before";//$NON-NLS-1$
63
    public static final String LAF_PREFIX       = "laf:"; // Look and Feel prefix
60
    public static final String LAF_PREFIX       = "laf:"; // Look and Feel prefix
64
    public static final String LOGGER_PANEL_ENABLE_DISABLE     = "logger_panel_enable_disable"; // $NON-NLS-1$
61
    public static final String LOGGER_PANEL_ENABLE_DISABLE     = "logger_panel_enable_disable"; // $NON-NLS-1$
65
    public static final String MERGE            = "merge"; // $NON-NLS-1$
62
    public static final String MERGE            = "merge"; // $NON-NLS-1$
(-)src/core/org/apache/jmeter/gui/action/DragNDrop.java (-104 lines)
Lines 1-104 Link Here
1
/*
2
 * Licensed to the Apache Software Foundation (ASF) under one or more
3
 * contributor license agreements.  See the NOTICE file distributed with
4
 * this work for additional information regarding copyright ownership.
5
 * The ASF licenses this file to You under the Apache License, Version 2.0
6
 * (the "License"); you may not use this file except in compliance with
7
 * the License.  You may obtain a copy of the License at
8
 *
9
 *   http://www.apache.org/licenses/LICENSE-2.0
10
 *
11
 * Unless required by applicable law or agreed to in writing, software
12
 * distributed under the License is distributed on an "AS IS" BASIS,
13
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
 * See the License for the specific language governing permissions and
15
 * limitations under the License.
16
 *
17
 */
18
19
package org.apache.jmeter.gui.action;
20
21
import java.awt.Toolkit;
22
import java.awt.event.ActionEvent;
23
import java.util.HashSet;
24
import java.util.Set;
25
26
import org.apache.jmeter.gui.GuiPackage;
27
import org.apache.jmeter.gui.tree.JMeterTreeListener;
28
import org.apache.jmeter.gui.tree.JMeterTreeNode;
29
import org.apache.jmeter.gui.util.MenuFactory;
30
import org.apache.jmeter.testelement.TestElement;
31
import org.apache.jmeter.testelement.TestPlan;
32
import org.apache.jmeter.testelement.WorkBench;
33
34
public class DragNDrop extends AbstractAction {
35
    private static final Set<String> commands = new HashSet<String>();
36
37
    static {
38
        commands.add(ActionNames.DRAG_ADD);
39
        commands.add(ActionNames.INSERT_BEFORE);
40
        commands.add(ActionNames.INSERT_AFTER);
41
    }
42
43
    /**
44
     * @see Command#doAction(ActionEvent)
45
     */
46
    @Override
47
    public void doAction(ActionEvent e) {
48
        String action = e.getActionCommand();
49
        GuiPackage guiPackage = GuiPackage.getInstance();
50
        JMeterTreeNode[] draggedNodes = guiPackage.getTreeListener().getDraggedNodes();
51
        JMeterTreeListener treeListener = guiPackage.getTreeListener();
52
        JMeterTreeNode currentNode = treeListener.getCurrentNode();
53
        JMeterTreeNode parentNode = (JMeterTreeNode) currentNode.getParent();
54
        TestElement te = currentNode.getTestElement();
55
        if (te instanceof TestPlan || te instanceof WorkBench) {
56
            parentNode = null; // So elements can only be added as children
57
        }
58
59
        if (ActionNames.DRAG_ADD.equals(action) && canAddTo(currentNode,draggedNodes)) {
60
            removeNodesFromParents(draggedNodes);
61
            for (int i = 0; i < draggedNodes.length; i++) {
62
                GuiPackage.getInstance().getTreeModel().insertNodeInto(draggedNodes[i], currentNode,
63
                        currentNode.getChildCount());
64
            }
65
        } else if (parentNode != null) {
66
            if (ActionNames.INSERT_BEFORE.equals(action) && canAddTo(parentNode,draggedNodes)) {
67
                removeNodesFromParents(draggedNodes);
68
                for (int i = 0; i < draggedNodes.length; i++) {
69
                    int index = parentNode.getIndex(currentNode);
70
                    GuiPackage.getInstance().getTreeModel().insertNodeInto(draggedNodes[i], parentNode, index);
71
                }
72
            } else if (ActionNames.INSERT_AFTER.equals(action) && canAddTo(parentNode,draggedNodes)) {
73
                removeNodesFromParents(draggedNodes);
74
                for (int i = 0; i < draggedNodes.length; i++) {
75
                    int index = parentNode.getIndex(currentNode) + 1;
76
                    GuiPackage.getInstance().getTreeModel().insertNodeInto(draggedNodes[i], parentNode, index);
77
                }
78
            }
79
        }
80
        GuiPackage.getInstance().getMainFrame().repaint();
81
    }
82
83
    private static boolean canAddTo(JMeterTreeNode parentNode, JMeterTreeNode[] draggedNodes) {
84
        boolean ok = MenuFactory.canAddTo(parentNode, draggedNodes);
85
        if (!ok){
86
            Toolkit.getDefaultToolkit().beep();
87
        }
88
        return ok;
89
    }
90
91
    private void removeNodesFromParents(JMeterTreeNode[] nodes) {
92
        for (int i = 0; i < nodes.length; i++) {
93
            GuiPackage.getInstance().getTreeModel().removeNodeFromParent(nodes[i]);
94
        }
95
    }
96
97
    /**
98
     * @see Command#getActionNames()
99
     */
100
    @Override
101
    public Set<String> getActionNames() {
102
        return commands;
103
    }
104
}
(-)src/core/org/apache/jmeter/gui/tree/JMeterTreeListener.java (-112 / +2 lines)
Lines 26-55 import java.awt.event.KeyEvent; Link Here
26
import java.awt.event.KeyListener;
26
import java.awt.event.KeyListener;
27
import java.awt.event.MouseEvent;
27
import java.awt.event.MouseEvent;
28
import java.awt.event.MouseListener;
28
import java.awt.event.MouseListener;
29
import java.awt.event.MouseMotionListener;
30
29
31
import javax.swing.JLabel;
32
import javax.swing.JMenuItem;
33
import javax.swing.JPopupMenu;
30
import javax.swing.JPopupMenu;
34
import javax.swing.JTree;
31
import javax.swing.JTree;
35
import javax.swing.event.TreeSelectionEvent;
32
import javax.swing.event.TreeSelectionEvent;
36
import javax.swing.event.TreeSelectionListener;
33
import javax.swing.event.TreeSelectionListener;
37
import javax.swing.tree.TreeNode;
38
import javax.swing.tree.TreePath;
34
import javax.swing.tree.TreePath;
39
35
40
import org.apache.jmeter.control.gui.TestPlanGui;
41
import org.apache.jmeter.control.gui.WorkBenchGui;
42
import org.apache.jmeter.gui.GuiPackage;
36
import org.apache.jmeter.gui.GuiPackage;
43
import org.apache.jmeter.gui.MainFrame;
37
import org.apache.jmeter.gui.MainFrame;
44
import org.apache.jmeter.gui.action.ActionNames;
38
import org.apache.jmeter.gui.action.ActionNames;
45
import org.apache.jmeter.gui.action.ActionRouter;
39
import org.apache.jmeter.gui.action.ActionRouter;
46
import org.apache.jmeter.gui.action.KeyStrokes;
40
import org.apache.jmeter.gui.action.KeyStrokes;
47
import org.apache.jmeter.gui.util.MenuFactory;
48
import org.apache.jmeter.util.JMeterUtils;
49
import org.apache.jorphan.logging.LoggingManager;
41
import org.apache.jorphan.logging.LoggingManager;
50
import org.apache.log.Logger;
42
import org.apache.log.Logger;
51
43
52
public class JMeterTreeListener implements TreeSelectionListener, MouseListener, KeyListener, MouseMotionListener {
44
public class JMeterTreeListener implements TreeSelectionListener, MouseListener, KeyListener {
53
    private static final Logger log = LoggingManager.getLoggerForClass();
45
    private static final Logger log = LoggingManager.getLoggerForClass();
54
46
55
    // Container endWindow;
47
    // Container endWindow;
Lines 62-85 public class JMeterTreeListener implements TreeSelectionListener, MouseListener, Link Here
62
54
63
    private JTree tree;
55
    private JTree tree;
64
56
65
    private boolean dragging = false;
66
67
    private JMeterTreeNode[] draggedNodes;
68
69
    private final JLabel dragIcon = new JLabel(JMeterUtils.getImage("leafnode.gif")); // $NON-NLS-1$
70
71
    /**
57
    /**
72
     * Constructor for the JMeterTreeListener object.
58
     * Constructor for the JMeterTreeListener object.
73
     */
59
     */
74
    public JMeterTreeListener(JMeterTreeModel model) {
60
    public JMeterTreeListener(JMeterTreeModel model) {
75
        this.model = model;
61
        this.model = model;
76
        dragIcon.validate();
77
        dragIcon.setVisible(true);
78
    }
62
    }
79
63
80
    public JMeterTreeListener() {
64
    public JMeterTreeListener() {
81
        dragIcon.validate();
82
        dragIcon.setVisible(true);
83
    }
65
    }
84
66
85
    public void setModel(JMeterTreeModel m) {
67
    public void setModel(JMeterTreeModel m) {
Lines 171-247 public class JMeterTreeListener implements TreeSelectionListener, MouseListener, Link Here
171
153
172
    @Override
154
    @Override
173
    public void mouseReleased(MouseEvent e) {
155
    public void mouseReleased(MouseEvent e) {
174
        if (dragging && isValidDragAction(draggedNodes, getCurrentNode())) {
156
        GuiPackage.getInstance().getMainFrame().repaint();
175
            dragging = false;
176
            JPopupMenu dragNdrop = new JPopupMenu();
177
            JMenuItem item = new JMenuItem(JMeterUtils.getResString("insert_before")); // $NON-NLS-1$
178
            item.addActionListener(actionHandler);
179
            item.setActionCommand(ActionNames.INSERT_BEFORE);
180
            dragNdrop.add(item);
181
            item = new JMenuItem(JMeterUtils.getResString("insert_after")); // $NON-NLS-1$
182
            item.addActionListener(actionHandler);
183
            item.setActionCommand(ActionNames.INSERT_AFTER);
184
            dragNdrop.add(item);
185
            if (MenuFactory.canAddTo(getCurrentNode(), draggedNodes)){
186
                item = new JMenuItem(JMeterUtils.getResString("add_as_child")); // $NON-NLS-1$
187
                item.addActionListener(actionHandler);
188
                item.setActionCommand(ActionNames.DRAG_ADD);
189
                dragNdrop.add(item);
190
            }
191
            dragNdrop.addSeparator();
192
            item = new JMenuItem(JMeterUtils.getResString("cancel")); // $NON-NLS-1$
193
            dragNdrop.add(item);
194
            displayPopUp(e, dragNdrop);
195
        } else {
196
            GuiPackage.getInstance().getMainFrame().repaint();
197
        }
198
        dragging = false;
199
    }
200
201
    public JMeterTreeNode[] getDraggedNodes() {
202
        return draggedNodes;
203
    }
204
205
    /**
206
     * Tests if the node is being dragged into one of it's own sub-nodes, or
207
     * into itself.
208
     */
209
    private boolean isValidDragAction(JMeterTreeNode[] source, JMeterTreeNode dest) {
210
        boolean isValid = true;
211
        TreeNode[] path = dest.getPath();
212
        for (int i = 0; i < path.length; i++) {
213
            if (contains(source, path[i])) {
214
                isValid = false;
215
            }
216
        }
217
        return isValid;
218
    }
157
    }
219
158
220
    @Override
159
    @Override
221
    public void mouseEntered(MouseEvent e) {
160
    public void mouseEntered(MouseEvent e) {
222
    }
161
    }
223
162
224
    private void changeSelectionIfDragging(MouseEvent e) {
225
        if (dragging) {
226
            GuiPackage.getInstance().getMainFrame().drawDraggedComponent(dragIcon, e.getX(), e.getY());
227
            if (tree.getPathForLocation(e.getX(), e.getY()) != null) {
228
                currentPath = tree.getPathForLocation(e.getX(), e.getY());
229
                if (!contains(draggedNodes, getCurrentNode())) {
230
                    tree.setSelectionPath(currentPath);
231
                }
232
            }
233
        }
234
    }
235
236
    private boolean contains(Object[] container, Object item) {
237
        for (int i = 0; i < container.length; i++) {
238
            if (container[i] == item) {
239
                return true;
240
            }
241
        }
242
        return false;
243
    }
244
245
    @Override
163
    @Override
246
    public void mousePressed(MouseEvent e) {
164
    public void mousePressed(MouseEvent e) {
247
        // Get the Main Frame.
165
        // Get the Main Frame.
Lines 266-288 public class JMeterTreeListener implements TreeSelectionListener, MouseListener, Link Here
266
        }
184
        }
267
    }
185
    }
268
186
269
    @Override
270
    public void mouseDragged(MouseEvent e) {
271
        if (!dragging) {
272
            dragging = true;
273
            draggedNodes = getSelectedNodes();
274
            if (draggedNodes[0].getUserObject() instanceof TestPlanGui
275
                    || draggedNodes[0].getUserObject() instanceof WorkBenchGui) {
276
                dragging = false;
277
            }
278
279
        }
280
        changeSelectionIfDragging(e);
281
    }
282
283
    @Override
284
    public void mouseMoved(MouseEvent e) {
285
    }
286
187
287
    @Override
188
    @Override
288
    public void mouseExited(MouseEvent ev) {
189
    public void mouseExited(MouseEvent ev) {
Lines 341-355 public class JMeterTreeListener implements TreeSelectionListener, MouseListener, Link Here
341
        JPopupMenu pop = getCurrentNode().createPopupMenu();
242
        JPopupMenu pop = getCurrentNode().createPopupMenu();
342
        GuiPackage.getInstance().displayPopUp(e, pop);
243
        GuiPackage.getInstance().displayPopUp(e, pop);
343
    }
244
    }
344
345
    private void displayPopUp(MouseEvent e, JPopupMenu popup) {
346
        // See Bug 46108 - this log message is unnecessary and misleading
347
        // log.warn("Shouldn't be here");
348
        if (popup != null) {
349
            popup.pack();
350
            popup.show(tree, e.getX(), e.getY());
351
            popup.setVisible(true);
352
            popup.requestFocus();
353
        }
354
    }
355
}
245
}
(-)src/core/org/apache/jmeter/gui/tree/JMeterTreeTransferHandler.java (-1 / +239 lines)
Line 0 Link Here
0
- 
1
/*
2
 * Licensed to the Apache Software Foundation (ASF) under one or more
3
 * contributor license agreements.  See the NOTICE file distributed with
4
 * this work for additional information regarding copyright ownership.
5
 * The ASF licenses this file to You under the Apache License, Version 2.0
6
 * (the "License"); you may not use this file except in compliance with
7
 * the License.  You may obtain a copy of the License at
8
 *
9
 *   http://www.apache.org/licenses/LICENSE-2.0
10
 *
11
 * Unless required by applicable law or agreed to in writing, software
12
 * distributed under the License is distributed on an "AS IS" BASIS,
13
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
 * See the License for the specific language governing permissions and
15
 * limitations under the License.
16
 *
17
 */
18
19
package org.apache.jmeter.gui.tree;
20
21
import java.awt.datatransfer.DataFlavor;
22
import java.awt.datatransfer.Transferable;
23
import java.awt.datatransfer.UnsupportedFlavorException;
24
25
import javax.swing.JComponent;
26
import javax.swing.JTree;
27
import javax.swing.TransferHandler;
28
import javax.swing.tree.TreePath;
29
30
import org.apache.jmeter.gui.GuiPackage;
31
import org.apache.jmeter.gui.util.MenuFactory;
32
import org.apache.jorphan.logging.LoggingManager;
33
import org.apache.log.Logger;
34
35
public class JMeterTreeTransferHandler extends TransferHandler {
36
    
37
    private static final long serialVersionUID = 8560957372186260765L;
38
39
    private static final Logger LOG = LoggingManager.getLoggerForClass();
40
  
41
    private DataFlavor nodeFlavor;
42
    private DataFlavor[] jMeterTreeNodeDataFlavors = new DataFlavor[1];
43
44
    public JMeterTreeTransferHandler() {
45
        try {
46
            String mimeType = DataFlavor.javaJVMLocalObjectMimeType + ";class=\"" + JMeterTreeNode[].class.getName() + "\"";
47
            nodeFlavor = new DataFlavor(mimeType);
48
            jMeterTreeNodeDataFlavors[0] = nodeFlavor;
49
        }
50
        catch (ClassNotFoundException e) {
51
            LOG.error("Class Not Found", e);
52
        }
53
    }
54
55
    
56
    @Override
57
    public int getSourceActions(JComponent c) {
58
        return MOVE;
59
    }
60
    
61
62
    @Override
63
    protected Transferable createTransferable(JComponent c) {
64
        JTree tree = (JTree) c;
65
        TreePath[] paths = tree.getSelectionPaths();
66
        if (paths != null) {
67
           
68
            //TODO : deal with all the selected nodes
69
            JMeterTreeNode node = (JMeterTreeNode) paths[0].getLastPathComponent();
70
            
71
            return new NodesTransferable(new JMeterTreeNode[] {node});
72
        }
73
        
74
        return null;
75
    }
76
   
77
    @Override
78
    protected void exportDone(JComponent source, Transferable data, int action) {
79
    }
80
81
    @Override
82
    public boolean canImport(TransferHandler.TransferSupport support) {
83
        if (!support.isDrop()) {
84
            return false;
85
        }
86
        
87
        // the tree accepts a jmx file 
88
        DataFlavor[] flavors = support.getDataFlavors();
89
        for (int i = 0; i < flavors.length; i++) {
90
            // Check for file lists specifically
91
            if (flavors[i].isFlavorJavaFileListType()) {
92
                return true;
93
            }
94
        }
95
96
        // or a treenode from the same tree
97
        if (!support.isDataFlavorSupported(nodeFlavor)) {
98
            return false;
99
        }
100
        
101
        // the copy is disabled
102
        int action = support.getDropAction();
103
        if(action != MOVE) {
104
            return false;
105
        }
106
        
107
        support.setShowDropLocation(true);
108
109
        // Do not allow a drop on the drag source selections.
110
        JTree.DropLocation dl = (JTree.DropLocation) support.getDropLocation();
111
        JTree tree = (JTree) support.getComponent();
112
        int dropRow = tree.getRowForPath(dl.getPath());
113
        int[] selRows = tree.getSelectionRows();
114
        for (int i = 0; i < selRows.length; i++) {
115
            if (selRows[i] == dropRow) {
116
                return false;
117
            }
118
        }
119
        
120
        TreePath dest = dl.getPath();
121
        JMeterTreeNode target = (JMeterTreeNode) dest.getLastPathComponent();
122
        
123
        // TestPlan and WorkBench are the only children of the root
124
        if(target.isRoot()) {
125
            return false;
126
        }
127
        
128
        TreePath path = tree.getPathForRow(selRows[0]);
129
        JMeterTreeNode draggedNode = (JMeterTreeNode) path.getLastPathComponent();
130
        
131
        // Do not allow a non-leaf node to be moved into one of its children
132
        if (draggedNode.getChildCount() > 0
133
                && target.isNodeAncestor(draggedNode)) {
134
            return false;
135
        }
136
        
137
        // re-use node association logic
138
        return MenuFactory.canAddTo(target, new JMeterTreeNode[] { draggedNode });
139
    }
140
141
142
    @Override
143
    public boolean importData(TransferHandler.TransferSupport support) {
144
        if (!canImport(support)) {
145
            return false;
146
        }
147
        
148
        // deal with the jmx files
149
        GuiPackage guiInstance = GuiPackage.getInstance();
150
        DataFlavor[] flavors = support.getDataFlavors();
151
        Transferable t = support.getTransferable();
152
        for (int i = 0; i < flavors.length; i++) {
153
            // Check for file lists specifically
154
            if (flavors[i].isFlavorJavaFileListType()) {
155
                try {
156
                    return guiInstance.getMainFrame().openJmxFilesFromDragAndDrop(t);
157
                }
158
                catch (Exception e) {
159
                    LOG.error("Drop file failed", e);
160
                }
161
                return false;
162
            }
163
        }
164
        
165
        // Extract transfer data.
166
        JMeterTreeNode[] nodes = null;
167
        try {
168
            nodes = (JMeterTreeNode[]) t.getTransferData(nodeFlavor);
169
        }
170
        catch (Exception e) {
171
            LOG.error("Unsupported Flavor in Transferable", e);
172
        }
173
174
        if(nodes == null || nodes.length == 0) {
175
            return false;
176
        }
177
        
178
        // Get drop location and mode
179
        JTree.DropLocation dl = (JTree.DropLocation) support.getDropLocation();
180
        TreePath dest = dl.getPath();
181
        JMeterTreeNode target = (JMeterTreeNode) dest.getLastPathComponent();
182
       
183
        //TODO : deal with all the selected nodes
184
        JMeterTreeNode draggedNode = nodes[0];
185
        
186
        int index = dl.getChildIndex();
187
        if (index == -1) { // drop mode is ON
188
            index = target.getChildCount();
189
            if(draggedNode.getParent() == target) {
190
                //when the target is the current parent of the node being dragged
191
                // re-add it as the last child
192
                index--;
193
            }
194
        }
195
        else if(draggedNode.getParent() == target) { // insert mode
196
            if(guiInstance.getTreeModel().getIndexOfChild(target, draggedNode) < index) {
197
                index--;
198
            }
199
        }
200
        
201
        // remove - add the nodes
202
        for (int i = 0; i < nodes.length; i++) {
203
            guiInstance.getTreeModel().removeNodeFromParent(nodes[i]);
204
            guiInstance.getTreeModel().insertNodeInto(nodes[i], target, index);
205
        }
206
        
207
        // expand the destination node
208
        JTree tree = (JTree) support.getComponent();
209
        tree.expandPath(new TreePath(target.getPath()));
210
        
211
        return true;
212
    }
213
214
    private class NodesTransferable implements Transferable {
215
        JMeterTreeNode[] nodes;
216
217
        public NodesTransferable(JMeterTreeNode[] nodes) {
218
            this.nodes = nodes;
219
        }
220
221
        @Override
222
        public Object getTransferData(DataFlavor flavor) throws UnsupportedFlavorException {
223
            if (!isDataFlavorSupported(flavor)) {
224
                throw new UnsupportedFlavorException(flavor);
225
            }
226
            return nodes;
227
        }
228
229
        @Override
230
        public DataFlavor[] getTransferDataFlavors() {
231
            return jMeterTreeNodeDataFlavors;
232
        }
233
234
        @Override
235
        public boolean isDataFlavorSupported(DataFlavor flavor) {
236
            return nodeFlavor.equals(flavor);
237
        }
238
    }
239
}

Return to bug 54834