Search code examples
javaswingsvgbatik

How to make all of the region of a Jpanel draggable including a JSVGCanvas within it


Ive been following this guide http://softwareisart.blogspot.co.uk/2011/11/drag-and-drop-of-complex-custom-objects.html

to make my custom Jpanels draggable(via drag gestures) however for some reason only the region of the JPanel with no components is draggable. I have a JSVGCanvas(from the batik library) which has some painting within it which I then added to my custom JPanel.

In the past for hover events on the JPanel to make it highlightable i would do the following to pass mouse hover events from the canvas to the outside jpanel like this to solve this issue.

@Override public void mouseClicked(MouseEvent e){outside.dispatchEvent(e);}
        @Override public void mousePressed(MouseEvent e){outside.dispatchEvent(e);}
        @Override public void mouseReleased(MouseEvent e){outside.dispatchEvent(e);}

However Im not sure how im going to pass the drag events from the canvas to the jpanel holding the canvas.

Is thier any way i can bind the canvas to the JPanel so that when i click anywhere in the Jpanel it passes through the canvas to the JPanel?

Thanks

UPDATE

Hi Richard. Using a JLayer seems good since it now encompasses the JSVGCanvas and the JPanel holding the canvas. But How am I supposed to add a drag gesture within the "drag gesture processing here" bit. I normally add a drag event to my JPanel as followed.

DragSource ds = new DragSource();
    ds.createDefaultDragGestureRecognizer(SecondChoice,
            DnDConstants.ACTION_COPY, new DragGestureListImp());

    DragSource ds2 = new DragSource();
    ds2.createDefaultDragGestureRecognizer(FirstChoice,
            DnDConstants.ACTION_COPY, new DragGestureListImp());


    new MyDropTargetListImp(FirstChoice);
    new MyDropTargetListImp(SecondChoice);'

And my DragGestures

class DragGestureListImp implements DragGestureListener {

    @Override
    public void dragGestureRecognized(DragGestureEvent event) {

        Cursor cursor = null;
        CustomJPanel panel= (CustomJPanel) event.getComponent();



        if (event.getDragAction() == DnDConstants.ACTION_COPY) {
            cursor = DragSource.DefaultCopyDrop;
        }
        Canvas canvas= panel.getCanvas();

        event.startDrag(cursor, new TransferableCanvas(canvas));
    }
} //DragGestureListImp

And MyDropTargetListImp is like this. class MyDropTargetListImp extends DropTargetAdapter implements DropTargetListener {

    private DropTarget dropTarget;
    private CustomJPanel panel;

    public MyDropTargetListImp(CustomJPanel panel) {
        this.panel = panel;

        dropTarget = new DropTarget(panel, DnDConstants.ACTION_COPY, this,
                true, null);
    }


    public void drop(DropTargetDropEvent event) {
        try {

            System.out.println("dropped Event");
            Transferable tr = event.getTransferable();
            CustomJPanel an = (CustomJPanel) tr.getTransferData(dataFlavor);

            if (event.isDataFlavorSupported(dataFlavor)) {
                event.acceptDrop(DnDConstants.ACTION_COPY);

                this.panel.updateMyStuff(an)

                event.dropComplete(true);
                this.panel.validate();
                return;
            }
            event.rejectDrop();
        } catch (Exception e) {
            e.printStackTrace();
            event.rejectDrop();
        }
    } //drop
} //MyDropTargetListImp

Error:(281, 21) java: incompatible types: java.awt.AWTEvent cannot be converted to java.awt.dnd.MouseDragGestureRecognizer

UPDATE FINAL

The JLayers helped me a little but i finally implemented it by the following. Within the JLayer class that Richard wrote i simply added a gestureListener to both the canvas and the JLayer and created separate GesturesImplementations for each one passing the JLayer to the cavas's implementation.

DragSource ds = new DragSource();
    // create a component to be decorated with the layer
    // This would be your custom component.
    CustomPanel customPanel = new CustomPanel (index, information);
    //customPanel.add(new JButton("JButton"));

    JLayer tempLayerUI = new JLayer<JComponent>(customPanel, layerUI);

    ds.createDefaultDragGestureRecognizer(customPanel.canvas,
            DnDConstants.ACTION_COPY, new DragGestureListImp1(tempLayerUI));

    ds.createDefaultDragGestureRecognizer(tempLayerUI,
            DnDConstants.ACTION_COPY, new DragGestureListImp());

return tempLayerUI;

Ali


Solution

  • In Java 7 or later, you should be able to wrap your JPanel in a JLayer instance that intercepts the mouse events that are currently going to your JSVGCanvas. This will allow you to do the drag gesture processing in the wrapper.

    Below is an adapted version of the sample code from https://docs.oracle.com/javase/7/docs/api/javax/swing/JLayer.html

    public class JLayerSample {
    
        private static JLayer<JComponent> createLayer() {
            // This custom layerUI will intercept all mouseMotion events generated within its borders and delegate to the wrapped JPanel
            LayerUI<JComponent> layerUI = new LayerUI<JComponent>() {
    
                public void installUI(JComponent c) {
                    super.installUI(c);
                    // enable mouse events for the layer's subcomponents
                    ((JLayer) c).setLayerEventMask(AWTEvent.MOUSE_EVENT_MASK);
                }
    
                public void uninstallUI(JComponent c) {
                    super.uninstallUI(c);
                    // reset the layer event mask
                    ((JLayer) c).setLayerEventMask(0);
                }
    
                // overridden method which catches MouseMotion events
                public void eventDispatched(AWTEvent e, JLayer<? extends JComponent> l) {
                    if (e instanceof MouseEvent) {
                        System.out.println("MouseEvent detected " + e);
                        // Do drag gesture processing here.
                        // Note, you cannot dispatch these events to the view component, since that
                        // creates an event loop.
                    }
                }
            };
    
            // create a component to be decorated with the layer
            // This would be your custom component.
            JPanel panel = new JPanel();
            panel.add(new JButton("JButton"));
    
            // create the layer for the panel using our custom layerUI
            return new JLayer<JComponent>(panel, layerUI);
        }
    
        private static void createAndShowGUI() {
            final JFrame frame = new JFrame();
            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    
            // work with the layer as with any other Swing component
            frame.add(createLayer());
    
            frame.setSize(200, 200);
            frame.setLocationRelativeTo(null);
            frame.setVisible(true);
        }
    
        public static void main(String[] args) throws Exception {
            SwingUtilities.invokeAndWait(new Runnable() {
                public void run() {
                    createAndShowGUI();
                }
            });
        }
    }