package org.limewire.ui.swing.components;
import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Cursor;
import java.awt.event.MouseEvent;
import javax.swing.BorderFactory;
import javax.swing.JComponent;
import javax.swing.JSplitPane;
import javax.swing.plaf.basic.BasicSplitPaneDivider;
import javax.swing.plaf.basic.BasicSplitPaneUI;
import org.limewire.util.OSUtils;
/**
* SplitPane where the divider component can be specified.
*
*/
public class LimeSplitPane extends JSplitPane{
private boolean currentlyDraggable = true;
public LimeSplitPane(int orientation, boolean continuousLayout, Component leftComponent, Component rightComponent, JComponent dividerComponent){
super(orientation, continuousLayout, leftComponent, rightComponent);
BasicSplitPaneUI splitUI = new BasicSplitPaneUI() {
@Override
public BasicSplitPaneDivider createDefaultDivider() {
return new CustomDivider(this);
}
};
setUI(splitUI);
BasicSplitPaneDivider divider = splitUI.getDivider();
divider.setBorder(BorderFactory.createEmptyBorder());
divider.setLayout(new BorderLayout());
divider.removeAll();
divider.add(dividerComponent);
setDividerSize(dividerComponent.getPreferredSize().height);
// The upper panel was flickering on OS X due to some strange interactions with the browser.
// As a temporary solution to the flickering problem, we're turned off automatic repainting
// until we can solve the underlying problem.
if (OSUtils.isMacOSX()) {
setContinuousLayout(false);
}
}
/**
* Sets up a certain component as a drag controller for
* the position of the divider.
*/
public void setDragComponent(JComponent component) {
assertUISafeForDragabilityChanges();
CustomDivider divider = (CustomDivider) ((BasicSplitPaneUI)getUI()).getDivider();
CustomDivider.ReroutedMouseHandler reroutedMouseHandler
= divider.new ReroutedMouseHandler(component);
component.addMouseListener(reroutedMouseHandler);
component.addMouseMotionListener(reroutedMouseHandler);
}
/**
* Enables or disables divider position changes by drags on the main
* divider component. This will not apply to secondary drag components
* set up using {@link #setDragComponent}
*/
public void setDividerDraggable(boolean draggable) {
assertUISafeForDragabilityChanges();
CustomDivider divider = (CustomDivider) ((BasicSplitPaneUI)getUI()).getDivider();
if (!draggable) {
if (currentlyDraggable) {
currentlyDraggable = false;
divider.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
divider.removeMouseListener(divider.getMouseHandler());
divider.removeMouseMotionListener(divider.getMouseHandler());
removeMouseListener(divider.getMouseHandler());
removeMouseMotionListener(divider.getMouseHandler());
}
}
else {
if (!currentlyDraggable) {
currentlyDraggable = true;
divider.setCursor((orientation == JSplitPane.HORIZONTAL_SPLIT) ?
Cursor.getPredefinedCursor(Cursor.E_RESIZE_CURSOR) :
Cursor.getPredefinedCursor(Cursor.S_RESIZE_CURSOR));
divider.addMouseListener(divider.getMouseHandler());
divider.addMouseMotionListener(divider.getMouseHandler());
addMouseListener(divider.getMouseHandler());
addMouseMotionListener(divider.getMouseHandler());
}
}
}
/**
* Used to Ensure (soft assertion) that the modified ui elements are still
* installed before attempting to make changes.
*/
private void assertUISafeForDragabilityChanges() {
if (!(getUI() instanceof BasicSplitPaneUI)) {
throw new IllegalStateException("Can't change the divider draggability if the UI has been modified");
}
if (!(((BasicSplitPaneUI)getUI()).getDivider() instanceof CustomDivider)) {
throw new IllegalStateException("Can't change the divider draggability if the divider has been modified");
}
}
/**
* A wrapper class that allows access to protected fields and methods
* in order to extend the drag functionality.
*/
private static class CustomDivider extends BasicSplitPaneDivider {
public CustomDivider(BasicSplitPaneUI ui) {
super(ui);
}
/**
* Peeks the enternal mouseHandler element used to control dragging
* on the primary drag source. Can be used to detach it if necessary.
*/
public MouseHandler getMouseHandler() {
return mouseHandler;
}
/**
* An adapter to use the normal drag mechanism with a nub rather than the
* entire slider component.
*
* <p> Done by rerouting the message source to its parent component
* if it matches the drag component. This in essence relaxes
* the drag preconditions so it can be initiated by a specific
* component. Without this modification the superclass requires
* the event source to be the actual divider for anything to happen.
*/
public class ReroutedMouseHandler extends MouseHandler {
private final JComponent rerouteComponent;
public ReroutedMouseHandler(JComponent rerouteComponent) {
this.rerouteComponent = rerouteComponent;
}
@Override
public void mousePressed(MouseEvent e) {
super.mousePressed(rerouteEvent(e));
}
@Override
public void mouseReleased(MouseEvent e) {
super.mouseReleased(rerouteEvent(e));
}
@Override
public void mouseDragged(MouseEvent e) {
super.mouseDragged(rerouteEvent(e));
}
@Override
public void mouseMoved(MouseEvent e) {
super.mouseMoved(rerouteEvent(e));
}
@Override
public void mouseEntered(MouseEvent e) {
super.mouseEntered(rerouteEvent(e));
}
@Override
public void mouseExited(MouseEvent e) {
super.mouseExited(rerouteEvent(e));
}
private MouseEvent rerouteEvent(MouseEvent e) {
if (e.getSource() == rerouteComponent) {
return new MouseEvent(CustomDivider.this, e.getID(),
e.getWhen(), e.getModifiers(), e.getX(),
e.getY(), e.getClickCount(),
e.isPopupTrigger());
}
else {
return e;
}
}
}
}
}