All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.jidesoft.swing.JideSplitPaneDivider Maven / Gradle / Ivy

/*
 * @(#)JideSplitPaneDivider.java
 *
 * Copyright 2002 JIDE Software Inc. All rights reserved.
 */
/*
 * @(#)JideSplitPaneDivider.java
 *
 * Copyright 2002 JIDE Software Inc. All rights reserved.
 */


package com.jidesoft.swing;


import com.jidesoft.plaf.UIDefaultsLookup;
import com.jidesoft.plaf.basic.Painter;

import javax.swing.*;
import javax.swing.border.Border;
import javax.swing.event.MouseInputAdapter;
import java.awt.*;
import java.awt.event.MouseEvent;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;


/**
 * Divider used by JideSplitPane.
 */
public class JideSplitPaneDivider extends JPanel
        implements PropertyChangeListener {
    /**
     * Handles mouse dragging message to do the actual dragging.
     */
    protected DragController _dragger;

    /**
     * Size of the divider.
     */
    protected int _dividerSize = UIDefaultsLookup.getInt("JideSplitPane.dividerSize"); // default - SET TO 0???

    /**
     * JideSplitPane the receiver is contained in.
     */
    protected JideSplitPane _jideSplitPane;

    /**
     * Handles mouse events from both this class, and the split pane. Mouse events are handled for the splitpane since
     * you want to be able to drag when clicking on the border of the divider, which is not drawn by the divider.
     */
    protected MouseHandler _mouseHandler;

    /**
     * Orientation of the JideSplitPane.
     */
    protected int _orientation;

    /**
     * Cursor used for HORIZONTAL_SPLIT splitpanes.
     */
    static final Cursor HORIZONTAL_CURSOR =
            JideCursors.getPredefinedCursor(JideCursors.HSPLIT_CURSOR);

    /**
     * Cursor used for VERTICAL_SPLIT splitpanes.
     */
    static final Cursor VERTICAL_CURSOR =
            JideCursors.getPredefinedCursor(JideCursors.VSPLIT_CURSOR);

    /**
     * Default cursor.
     */
    static final Cursor DEFAULT_CURSOR = Cursor.getDefaultCursor();

    private Painter _gripperPainter;

    /**
     * Creates an instance of BasicJideSplitPaneDivider. Registers this instance for mouse events and mouse dragged
     * events.
     *
     * @param splitPane the JideSplitPane.
     */
    public JideSplitPaneDivider(JideSplitPane splitPane) {
        setJideSplitPane(splitPane);
        _orientation = _jideSplitPane.getOrientation();

        // get divider size from JideSplitPane
        setDividerSize(splitPane.getDividerSize());
        setDefaultResizeCursor();

        setBackground(UIDefaultsLookup.getColor("JideSplitPaneDivider.background"));
        setBorder(UIDefaultsLookup.getBorder("JideSplitPaneDivider.border"));
        _gripperPainter = (Painter) UIDefaultsLookup.get("JideSplitPaneDivider.gripperPainter");
        setOpaque(false);
    }

    public void setDefaultResizeCursor() {
        setCursor((_orientation == JideSplitPane.HORIZONTAL_SPLIT) ?
                HORIZONTAL_CURSOR : VERTICAL_CURSOR);
    }

    /**
     * Gets the JideSplitPane.
     *
     * @return the JideSplitPane
     */
    public JideSplitPane getJideSplitPane() {
        return _jideSplitPane;
    }

    /**
     * Sets the JideSplitPane that is using this divider.
     *
     * @param splitPane the JideSplitPane.
     */
    public void setJideSplitPane(JideSplitPane splitPane) {
        uninstallListeners();
        _jideSplitPane = splitPane;
        installListeners();
    }

    private void installListeners() {
        if (_jideSplitPane != null) {
            if (_mouseHandler == null) {
                _mouseHandler = createMouseHandler();
            }
            _jideSplitPane.addMouseListener(_mouseHandler);
            _jideSplitPane.addMouseMotionListener(_mouseHandler);
            addMouseListener(_mouseHandler);
            addMouseMotionListener(_mouseHandler);
            _jideSplitPane.addPropertyChangeListener(this);
        }
    }

    private void uninstallListeners() {
        if (_jideSplitPane != null) {
            _jideSplitPane.removePropertyChangeListener(this);
            if (_mouseHandler != null) {
                _jideSplitPane.removeMouseListener(_mouseHandler);
                _jideSplitPane.removeMouseMotionListener(_mouseHandler);
                removeMouseListener(_mouseHandler);
                removeMouseMotionListener(_mouseHandler);
                _mouseHandler = null;
            }
        }
    }

    protected MouseHandler createMouseHandler() {
        return new MouseHandler();
    }

    /**
     * Sets the size of the divider to newSize. That is the width if the splitpane is
     * HORIZONTAL_SPLIT, or the height of VERTICAL_SPLIT.
     *
     * @param newSize the new divider size.
     */
    public void setDividerSize(int newSize) {
        _dividerSize = newSize;
    }


    /**
     * Returns the size of the divider, that is the width if the splitpane is HORIZONTAL_SPLIT, or the height of
     * VERTICAL_SPLIT.
     *
     * @return the divider size.
     */
    public int getDividerSize() {
        return _dividerSize;
    }

    /**
     * Returns dividerSize x dividerSize
     */
    @Override
    public Dimension getPreferredSize() {
        return new Dimension(getDividerSize(), getDividerSize());
    }

    /**
     * Returns dividerSize x dividerSize
     */
    @Override
    public Dimension getMinimumSize() {
        return getPreferredSize();
    }


    /**
     * Property change event, presumably from the JideSplitPane, will message updateOrientation if necessary.
     */
    public void propertyChange(PropertyChangeEvent e) {
        if (e.getSource() == _jideSplitPane) {
            if (e.getPropertyName().equals(JideSplitPane.ORIENTATION_PROPERTY)) {
                _orientation = _jideSplitPane.getOrientation();
                setCursor((_orientation == JideSplitPane.HORIZONTAL_SPLIT) ?
                        HORIZONTAL_CURSOR : VERTICAL_CURSOR);
                invalidate();
                validate();
            }
        }
    }


    /**
     * Resets the UI property to a value from the current look and feel. JComponent subclasses must
     * override this method like this:
     * 
     *   public void updateUI() {
     *      setUI((SliderUI)UIManager.getUI(this);
     *   }
     *  
* * @see #setUI * @see UIManager#getLookAndFeel * @see UIManager#getUI */ @Override public void updateUI() { super.updateUI(); setBackground(UIDefaultsLookup.getColor("JideSplitPaneDivider.background")); setBorder(UIDefaultsLookup.getBorder("JideSplitPaneDivider.border")); _gripperPainter = (Painter) UIDefaultsLookup.get("JideSplitPaneDivider.gripperPainter"); } /** * Paints the divider. */ @Override public void paint(Graphics g) { super.paint(g); // Paint the border. Border border = getBorder(); Dimension size = getSize(); if (isOpaque()) { g.setColor(getBackground()); g.fillRect(0, 0, size.width, size.height); } if (border != null) { border.paintBorder(this, g, 0, 0, size.width, size.height); } if (_jideSplitPane.isShowGripper()) { Rectangle rect = new Rectangle(size); if (_gripperPainter != null) { if (rect.width > rect.height) { rect.x = rect.x + rect.width / 2 - 10; rect.width = 22; _gripperPainter.paint(this, g, rect, SwingConstants.VERTICAL, 0); } else { rect.y = rect.y + rect.height / 2 - 10; rect.height = 22; _gripperPainter.paint(this, g, rect, SwingConstants.HORIZONTAL, 0); } } else { rect.x++; rect.y++; JideSwingUtilities.drawGrip(g, rect, 9, UIDefaultsLookup.getInt("JideSplitPane.dividerSize") / 3); } } } /** * Message to prepare for dragging. This messages the BasicJideSplitPaneUI with startDragging. */ protected void prepareForDragging() { _jideSplitPane.startDragging(this); } protected void dragDividerTo(int location) { _jideSplitPane.dragDividerTo(this, location); } protected void finishDraggingTo(int location) { _jideSplitPane.finishDraggingTo(this, location); } protected int getPreviousDividerLocation(boolean ignoreVisibility, boolean reversed) { return _jideSplitPane.getPreviousDividerLocation(this, ignoreVisibility, reversed); } protected int getNextDividerLocation(boolean ignoreVisibility, boolean reversed) { return _jideSplitPane.getNextDividerLocation(this, ignoreVisibility, reversed); } /** * Gets the first component. This divider is installed between two components. The first component is usually the * one on the left or on the top. * * @param ignoreVisibility true to not check if the component is visible. * @return the first component */ public Component getFirstComponent(boolean ignoreVisibility) { int index = _jideSplitPane.indexOf(this); if (index - 1 >= 0) { for (int i = (index - 1); i >= 0; i--) { if (ignoreVisibility || _jideSplitPane.getComponent(i).isVisible()) { return _jideSplitPane.getComponent(i); } } // return an invisible component in lieu of null return _jideSplitPane.getComponent(index - 1); } else { throw new IndexOutOfBoundsException("There is no component before divider " + index); } } /** * Gets the second component. This divider is installed between two components. The second component is usually the * one on the right or on the bottom. * * @param ignoreVisibility true to not check if the component is visible. * @return the first component */ public Component getSecondComponent(boolean ignoreVisibility) { int index = _jideSplitPane.indexOf(this); if (index + 1 < _jideSplitPane.getComponentCount()) { for (int i = (index + 1); i >= 0; i++) { if (ignoreVisibility || _jideSplitPane.getComponent(i).isVisible()) { return _jideSplitPane.getComponent(i); } } // return an invisible component in lieu of null return _jideSplitPane.getComponent(index + 1); } else { throw new IndexOutOfBoundsException("There is no component before divider " + index); } } /** * MouseHandler is responsible for converting mouse events (released, dragged...) into the appropriate * DragController methods. *

*/ protected class MouseHandler extends MouseInputAdapter { /** * Starts the dragging session by creating the appropriate instance of DragController. */ @Override public void mousePressed(MouseEvent e) { if ((e.getSource() == JideSplitPaneDivider.this/*|| e.getSource() == _jideSplitPane*/) && _dragger == null && _jideSplitPane.isEnabled()) { if (getFirstComponent(true) != null && getSecondComponent(true) != null) { if (_orientation == JideSplitPane.HORIZONTAL_SPLIT) { _dragger = new DragController(e); } else { _dragger = new VerticalDragController(e); } if (!_dragger.isValid()) { _dragger = null; } else { prepareForDragging(); _dragger.continueDrag(e); } } e.consume(); } } /** * If dragger is not null it is messaged with completeDrag. */ @Override public void mouseReleased(MouseEvent e) { if (_dragger != null) { if (e.getSource() == _jideSplitPane) { _dragger.completeDrag(e.getX(), e.getY()); } else if (e.getSource() == JideSplitPaneDivider.this) { Point ourLoc = getLocation(); _dragger.completeDrag(e.getX() + ourLoc.x, e.getY() + ourLoc.y); } _dragger = null; e.consume(); } } // // MouseMotionListener // /** * If dragger is not null it is messaged with continueDrag. */ @Override public void mouseDragged(MouseEvent e) { if (_dragger != null) { if (e.getSource() == _jideSplitPane) { _dragger.continueDrag(e.getX(), e.getY()); } else if (e.getSource() == JideSplitPaneDivider.this) { Point ourLoc = getLocation(); _dragger.continueDrag(e.getX() + ourLoc.x, e.getY() + ourLoc.y); } e.consume(); } } } /** * Handles the events during a dragging session for a HORIZONTAL_SPLIT oriented split pane. This continually * messages dragDividerTo and then when done messages finishDraggingTo. When an instance * is created it should be messaged with isValid to insure that dragging can happen (dragging won't be * allowed if the two views can not be resized). */ protected class DragController { /** * Initial location of the divider. */ int initialLocation; /** * Maximum and minimum positions to drag to. */ int maxLocation, minLocation; /** * Initial location the mouse down happened at. */ int offset; protected DragController(MouseEvent e) { ComponentOrientation o = getComponentOrientation(); boolean ltr = o.isLeftToRight(); boolean reversed = !ltr && _jideSplitPane.getOrientation() == JideSplitPane.HORIZONTAL_SPLIT; Component leftC = reversed ? getSecondComponent(false) : getFirstComponent(false); Component rightC = reversed ? getFirstComponent(false) : getSecondComponent(false); initialLocation = getLocation().x; if (e.getSource() == JideSplitPaneDivider.this) { offset = e.getX(); } else { // splitPane offset = e.getX() - initialLocation; } if (leftC == null || rightC == null || offset < -1 || offset >= _jideSplitPane.getSize().width) { // Don't allow dragging. maxLocation = -1; } else { if (leftC.isVisible()) { minLocation = getPreviousDividerLocation(false, reversed) + leftC.getMinimumSize().width; } else { minLocation = getPreviousDividerLocation(true, reversed); } if (rightC.isVisible()) { maxLocation = Math.max(0, getNextDividerLocation(false, reversed) - (getSize().width) - rightC.getMinimumSize().width); } else { maxLocation = Math.max(0, getNextDividerLocation(true, reversed) - (getSize().width)); } if (maxLocation < minLocation) minLocation = maxLocation = 0; } } /** * Returns true if the dragging session is valid. * * @return true or false. */ protected boolean isValid() { return (maxLocation > 0); } /** * Returns the new position to put the divider at based on the passed in MouseEvent. * * @param e the mouse event. * @return the position of the mouse event after considering the max and min size it is allowed. */ protected int positionForMouseEvent(MouseEvent e) { int newX = (e.getSource() == JideSplitPaneDivider.this) ? (e.getX() + getLocation().x) : e.getX(); newX = Math.min(maxLocation, Math.max(minLocation, newX - offset)); return newX; } /** * Returns the x argument, since this is used for horizontal splits. * * @param x x position * @param y y position * @return the actual position after considering the max and min size it is allowed. */ protected int getNeededLocation(int x, int y) { int newX; newX = Math.min(maxLocation, Math.max(minLocation, x - offset)); return newX; } protected void continueDrag(int newX, int newY) { dragDividerTo(getNeededLocation(newX, newY)); } /** * Messages dragDividerTo with the new location for the mouse event. * * @param e the mouse event. */ protected void continueDrag(MouseEvent e) { dragDividerTo(positionForMouseEvent(e)); } protected void completeDrag(int x, int y) { finishDraggingTo(getNeededLocation(x, y)); } /** * Messages finishDraggingTo with the new location for the mouse event. * * @param e the mouse event. */ protected void completeDrag(MouseEvent e) { finishDraggingTo(positionForMouseEvent(e)); } } // End of BasicJideSplitPaneDivider.DragController /** * Handles the events during a dragging session for a VERTICAL_SPLIT oriented split pane. This continually messages * dragDividerTo and then when done messages finishDraggingTo. When an instance is created * it should be messaged with isValid to insure that dragging can happen (dragging won't be allowed if * the two views can not be resized). */ protected class VerticalDragController extends DragController { /* DragControllers ivars are now in terms of y, not x. */ protected VerticalDragController(MouseEvent e) { super(e); Component leftC = getFirstComponent(false); Component rightC = getSecondComponent(false); initialLocation = getLocation().y; if (e.getSource() == JideSplitPaneDivider.this) { offset = e.getY(); } else { // splitPane offset = e.getY() - initialLocation; } if (leftC == null || rightC == null || offset < -1 || offset >= _jideSplitPane.getSize().height) { // Don't allow dragging. maxLocation = -1; } else { if (leftC.isVisible()) { minLocation = getPreviousDividerLocation(false, false) + leftC.getMinimumSize().height; } else { minLocation = 0; } if (rightC.isVisible()) { maxLocation = Math.max(0, getNextDividerLocation(false, false) - getSize().height - rightC.getMinimumSize().height); } else { maxLocation = Math.max(0, getNextDividerLocation(true, false) - getSize().height); } if (maxLocation < minLocation) minLocation = maxLocation = 0; } } /** * Returns the y argument, since this is used for vertical splits. */ @Override protected int getNeededLocation(int x, int y) { int newY; newY = Math.min(maxLocation, Math.max(minLocation, y - offset)); return newY; } /** * Returns the new position to put the divider at based on the passed in MouseEvent. */ @Override protected int positionForMouseEvent(MouseEvent e) { int newY = (e.getSource() == JideSplitPaneDivider.this) ? (e.getY() + getLocation().y) : e.getY(); newY = Math.min(maxLocation, Math.max(minLocation, newY - offset)); return newY; } } // End of BasicSplitPaneDividier.VerticalDragController }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy