/*
* Copyright (c) 2004 Christopher M Butler
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package org.flexdock.docking.defaults;
import java.awt.Component;
import java.awt.Graphics;
import javax.swing.JComponent;
import javax.swing.JSplitPane;
import javax.swing.JTabbedPane;
import javax.swing.border.Border;
import javax.swing.plaf.basic.BasicSplitPaneDivider;
import javax.swing.plaf.basic.BasicSplitPaneUI;
import javax.swing.plaf.basic.BasicTabbedPaneUI;
import org.flexdock.docking.DockingPort;
/**
* This class provides a standard implementation of the {@code BorderManager}
* interface. It is designed to avoid the negative visual effects caused by
* nesting docked components that have individual borders. It accomplishes this
* by establishing and maintaining a single border for all docked components
*
* This class encapsulates a {@code javax.swing.border.Border} instance, or a
* {@code null} border reference, for application to a
* {@code DefaultDockingPort} and its child components. If the
* {@code DefaultDockingPort} has no child component, then
* {@code managePortNullChild()} will apply the encapsulated border to the
* {@code DefaultDockingPort} itself, rendering the visible outline of an empty
* {@code DockingPort}. If the {@code DefaultDockingPort} has a generic
* {@code Component} as its child, excluding a {@code JTabbedPane} or
* {@code JSplitPane}, then the border for that component is set to
* {@code null} and the encapsulated border is applied to the
* {@code DockingPort} via {@code managePortSimpleChild()}. If the
* {@code DefaultDockingPort} has a {@code JTabbedPane} as its child, then a
* {@code null} border is set for the {@code JTabbedPane} and all of its child
* components, and the encapsulated border is applied to the {@code DockingPort}.
* This is accomplished by calling {@code managePortTabbedChild()}. Finally,
* {@code managePortSplitChild()} will manage the border for a
* {@code DefaultDockingPort} whose docked component is a {@code JSplitPane}.
* This method removes all borders from the {@code DefaultDockingPort} and the
* split pane divider and applies the encapsulated border to both left and right
* child components of the {@code JSplitPane}.
*
* @author Christopher Butler
*/
public class StandardBorderManager implements BorderManager {
private Border assignedBorder;
/**
* Creates a new {@code StandardBorderManager} with a {@code null} assigned
* border.
*/
public StandardBorderManager() {
}
/**
* Creates a new {@code StandardBorderManager} with the specified assigned
* border.
*
* @param border
* the currently assigned border.
*/
public StandardBorderManager(Border border) {
setBorder(border);
}
/**
* Returns the currently assigned border.
*
* @return the currently assigned border.
*/
public Border getBorder() {
return assignedBorder;
}
/**
* Sets the assigned border. Null values are acceptable.
*
* @param border
* the assigned border.
*/
public void setBorder(Border border) {
assignedBorder = border;
}
/**
* Set the border on the supplied {@code DockingPort} to the currently
* assigned border.
*
* @see BorderManager#managePortNullChild(DockingPort)
*/
public void managePortNullChild(DockingPort port) {
setBorder(port, assignedBorder);
}
/**
* Removes any border from the {@code DockingPort's} docked component and
* set the border on the {@code DockingPort} itself to the currently
* assigned border.
*
* @see BorderManager#managePortSimpleChild(DockingPort)
*/
public void managePortSimpleChild(DockingPort port) {
if (port != null) {
setBorder(port.getDockedComponent(), null);
setBorder(port, assignedBorder);
}
}
/**
* Removes any border from the {@code DockingPort} itself and places the
* currently assigned border on the two child components of the
* {@code DockingPort's</code JSplitPane} child.
*
* @see BorderManager#managePortSplitChild(DockingPort)
*/
public void managePortSplitChild(DockingPort port) {
if (port == null || !(port.getDockedComponent() instanceof JSplitPane))
return;
setBorder(port, null);
// clear the border from the split pane
JSplitPane split = (JSplitPane) port.getDockedComponent();
if (split.getUI() instanceof BasicSplitPaneUI) {
// grab the divider from the UI and remove the border from it
BasicSplitPaneDivider divider = ((BasicSplitPaneUI) split.getUI())
.getDivider();
if (divider != null && divider.getBorder() != null)
divider.setBorder(null);
}
setBorder(split, null);
// set the borders on each of the child components
setSubComponentBorder(split.getLeftComponent(), assignedBorder);
setSubComponentBorder(split.getRightComponent(), assignedBorder);
}
private void setSubComponentBorder(Component comp, Border border) {
if (comp instanceof DefaultDockingPort)
((DefaultDockingPort) comp).evaluateDockingBorderStatus();
else
setBorder(comp, border);
}
/**
* Removes any border from the {@code DockingPort's} docked
* {@code JTabbedPane} component and sets the border on the
* {@code DockingPort} itself to the currently assigned border.
*
* @see BorderManager#managePortTabbedChild(DockingPort)
*/
public void managePortTabbedChild(DockingPort port) {
managePortSimpleChild(port);
if (port == null || !(port.getDockedComponent() instanceof JTabbedPane))
return;
// we need to use a special UI to remove the outline around a
// JTabbedPane.
// this UI will only allow the outline on the side of the JTabbedPane
// that
// contains the tabs.
JTabbedPane tabs = (JTabbedPane) port.getDockedComponent();
// if(!(tabs.getUI() instanceof SimpleTabbedPaneUI))
// tabs.setUI(new SimpleTabbedPaneUI());
// remove any borders from the tabPane children
int tc = tabs.getTabCount();
Component cmp = null;
for (int i = 0; i < tc; i++) {
cmp = tabs.getComponentAt(i);
if (cmp instanceof JComponent)
((JComponent) cmp).setBorder(null);
}
}
private void setBorder(Component cmp, Border border) {
if (cmp instanceof JComponent)
((JComponent) cmp).setBorder(border);
}
private void setBorder(DockingPort port, Border border) {
if (port instanceof JComponent)
((JComponent) port).setBorder(border);
}
private static class SimpleTabbedPaneUI extends BasicTabbedPaneUI {
protected void paintContentBorderBottomEdge(Graphics g,
int tabPlacement, int selectedIndex, int x, int y, int w, int h) {
if (tabPlacement == BOTTOM)
super.paintContentBorderBottomEdge(g, tabPlacement,
selectedIndex, x, y, w, h);
}
protected void paintContentBorderLeftEdge(Graphics g, int tabPlacement,
int selectedIndex, int x, int y, int w, int h) {
if (tabPlacement == LEFT)
super.paintContentBorderLeftEdge(g, tabPlacement,
selectedIndex, x, y, w, h);
}
protected void paintContentBorderRightEdge(Graphics g,
int tabPlacement, int selectedIndex, int x, int y, int w, int h) {
if (tabPlacement == RIGHT)
super.paintContentBorderRightEdge(g, tabPlacement,
selectedIndex, x, y, w, h);
}
protected void paintContentBorderTopEdge(Graphics g, int tabPlacement,
int selectedIndex, int x, int y, int w, int h) {
if (tabPlacement == TOP)
super.paintContentBorderTopEdge(g, tabPlacement, selectedIndex,
x, y, w, h);
}
}
}