2D Layer Event Handlers

A layer event handler is what controls the behavior of the mouse and the keyboard when a specified layer is selected. For example, in seismic cross-section views, a cursor is displayed when the mouse moves over the display and the coordinates are displayed at the bottom of the window. Also, if you press Left mouse button you will start drawing a rectangular selection. If you press Right mouse button, you will see a pop-up menu with a list of options. Finally, if you double click with Left mouse button, you will generate a data synchronization event.

A sample event handler is available in the samples section.

Layer event handler registration

All event handlers for layers must implement interface com.interactive.intviewerapi.layers.ILayerEventHandler. A module can register its own event handler for a layer using:

    ILayerEventHandler myEventHandler = new ABCpluginEventHandler();
    layer.setLayerEventHandler(myEventHandler);

If your layer event handler is transient (for creating some graphical shape for example), you may want to keep a copy of the current event handler using:

    ILayerEventHandler savedEventHandler = layer.getLayerEventHandler();

This way, you can restore the regular event handler when you no longer need your own.

    layer.setLayerEventHandler(savedEventHandler);

The ILayerEventHandler interface

The ILayerEventHandler interface is pretty simple:

    public interface ILayerEventHandler extends MouseListener, MouseMotionListener, KeyListener {
        /**
         * @return a short message with instructions on what this event handler does.
         */
        public String getInstructionMessage();
        /**
         * Called when a layer is destroyed if this is the current event handler.
         */
        public void cleanupMouseHandler();
    }

where MouseListener and MouseMotionListener are the java Swing interfaces for handling mouse events.

The default event handler

This section reviews the implementation of the default event handler in INTViewer (the one used for seismic cross-section layers).

Interface MouseMotionListener has two methods, mouseMoved and mouseDragged. In method mouseMoved, we simply draw the cursor and broadcast the cursor position:

 public void mouseMoved(MouseEvent me) {
        int x = me.getX();
        int y = me.getY();
        window.drawCursor(x, y);
        window.broadcastCursorPosition(layer, x, y);
    }

Method MouseDragged broadcasts the cursor position and handles the rectangular selection if one has been initiated.

    public void mouseDragged(MouseEvent me) {
        if (_selectionStarted) {
            window.getRubberbandView().moveRectangularRubberband(me);
        }
        int x = me.getX();
        int y = me.getY();
        window.broadcastCursorPosition(layer, x, y);
    }

Interface MouseListener has five methods, mouseEntered, mouseExited, mouseClicked, mousePressed and mouseReleased. Method mouseEntered is just empty. Method mouseExited just gets rid of the cursor:

    public void mouseExited(MouseEvent me) {
        window.drawCursor(-1, -1);
        window.broadcastCursorPosition(layer, -1, -1);
    }

Method mouseClicked allows focus on selected window, checks for double clicks and calls broadcastSelectedPoint which generates a DataSynchronizeEvent.

    public void mouseClicked(MouseEvent me) {
                //get focus on selected window, and listen for KeyEvent[s]
                if (me.getSource() instanceof Component) {
                        ((Component) me.getSource()).requestFocusInWindow();
                }
        if (me.getClickCount() < 2) {
            return;
        }
        if (SwingUtilities.isLeftMouseButton(me)) {
            layer.broadcastSelectedPoint(me.getPoint());
        }
    }

Method mousePressed starts a rectangular selection if the left button is pressed and shows the popup menu if the right button is pressed.

    public void mousePressed(MouseEvent me) {
        if (me.getClickCount() > 1) {
            return;
        }
        if (SwingUtilities.isLeftMouseButton(me)) {
            window.getRubberbandView().startRectangularRubberband(me, null);
            _selectionStarted = true;
            return;
        }
        if (SwingUtilities.isRightMouseButton(me)) {
            layer.showLayerPopupMenu( me.getX(), me.getY());
        }
    }

Finally, method mouseReleased creates and broadcasts a rectangular selection event if a rectangular selection was initiated.

    public void mouseReleased(MouseEvent me) {
        if (_selectionStarted) {
            cgRect selection = window.getRubberbandView().endRectangularRubberband(me);
            _selectionStarted = false;
            // If the user just clicked, don't send an event
            if (selection.width < 4 || selection.height < 4) {
                return;
            }
            RectangularEvent event = new RectangularEvent(this.getViewerLayer(),
                    selection.getMinX(), selection.getMinY(), selection.getMaxX(), selection.getMaxY());
            EventBroadcaster.getInstance().send(event);
        }
    }

Shape selection detection

If a layer event handler implements com.interactive.intviewerapi.layers.ISingleShapeSelectionHandler, you can detect when a user clicks on a shape that this layer has created. For example, if a layer draws a shape on the com.interactive.intviewerapi.windows.AnnotationView, the layer event handler can react to the selection of that shape.

    @Override
    public boolean canHandleShapeSelection(cgPlotView view) {
        if (view instanceof AnnotationView && view == ((ILayeredWindow) layer.getViewerWindow()).getAnnotationView()) {
            return true;
        }
        return false;
    }
    @Override
    public boolean canHandleShapeSelection(cgPlotView view, cgShape lastSelectedShape) {
        if (canHandleShapeSelection(view) && this.matchesSelectedShape(lastSelectedShape)) {
            return true;
        }
        return false;
    }
    @Override
    public boolean onShapeSelection(cgPlotView view, cgShape lastSelectedShape) {
        layer.getViewerWindow().setSelectedVisual(layer); // reacts to the selection
        return true;
    }
    private boolean matchesSelectedShape(cgShape shape) {
        for (cgLine currentLine : this.lines) {
            if (currentLine == shape) {
                return true;
            }
        }
        return false;
    }