📄 defaultdockingport.java
字号:
/*
* 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.Container;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Insets;
import java.awt.LayoutManager;
import java.awt.LayoutManager2;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.image.BufferedImage;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.Set;
import java.util.WeakHashMap;
import javax.swing.Icon;
import javax.swing.JComponent;
import javax.swing.JPanel;
import javax.swing.JSplitPane;
import javax.swing.JTabbedPane;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.border.Border;
import javax.swing.border.EmptyBorder;
import org.flexdock.docking.Dockable;
import org.flexdock.docking.DockingConstants;
import org.flexdock.docking.DockingManager;
import org.flexdock.docking.DockingPort;
import org.flexdock.docking.DockingStrategy;
import org.flexdock.docking.RegionChecker;
import org.flexdock.docking.activation.ActiveDockableTracker;
import org.flexdock.docking.event.DockingEvent;
import org.flexdock.docking.event.DockingListener;
import org.flexdock.docking.event.DockingMonitor;
import org.flexdock.docking.event.TabbedDragListener;
import org.flexdock.docking.event.hierarchy.DockingPortTracker;
import org.flexdock.docking.props.DockingPortPropertySet;
import org.flexdock.docking.props.PropertyChangeListenerFactory;
import org.flexdock.docking.props.PropertyManager;
import org.flexdock.docking.state.LayoutNode;
import org.flexdock.docking.state.tree.DockableNode;
import org.flexdock.docking.state.tree.DockingPortNode;
import org.flexdock.docking.state.tree.SplitNode;
import org.flexdock.util.DockingUtility;
import org.flexdock.util.LookAndFeelSettings;
import org.flexdock.util.SwingUtility;
import org.flexdock.util.UUID;
import org.flexdock.util.Utilities;
/**
* This is a {@code Container} that implements the {@code DockingPort}
* interface. It provides a default implementation of {@code DockingPort} to
* allow ease of development within docking-enabled applications.
* <p>
* The {@code DefaultDockingPort} handles docking in one of three ways. If the
* port is empty, then all incoming {@code Dockables} are docked to the CENTER
* region. If the port is not empty, then all incoming {@code Dockables} docked
* to the CENTER region are embedded within a {@code JTabbedPane}. All incoming
* {@code Dockables} docked to an outer region (NORTH, SOUTH, EAST, and WEST) of
* a non-empty port are placed into a split layout using a {@code JSplitPane}.
* <p>
* For centrally docked {@code Components}, the immediate child of the
* {@code DefaultDockingPort} may or may not be a {@code JTabbedPane}. If
* {@code isSingleTabAllowed()} returns {@code true} for the current
* {@code DefaultDockingPort}, then the immediate child returned by
* {@code getDockedComponent()} will return a {@code JTabbedPane} instance even
* if there is only one {@code Dockable} embedded within the port. If there is a
* single {@code Dockable} in the port, but {@code isSingleTabAllowed()} returns
* {@code false}, then {@code getDockedComponent()} will return the
* {@code Component} that backs the currently docked {@code Dockable}, returned
* by the {@code Dockable's} {@code getComponent()} method.
* {@code isSingleTabAllowed()} is a scoped property that may apply to this
* port, all ports across the JVM, or all ports within a user defined scope.
* {@code getDockedComponent()} will return a {@code JTabbedPane} at all times
* if there is more than one centrally docked {@code Dockable} within the port,
* and all docked {@code Components} will reside within the tabbed pane.
* <p>
* Components that are docked in the NORTH, SOUTH, EAST, or WEST regions are
* placed in a {@code JSplitPane} splitting the layout of the
* {@code DockingPort} between child components. Each region of the
* {@code JSplitPane} contains a new {@code DefaultDockingPort}, which, in
* turn, contains the docked components. In this situation,
* {@code getDockedComponent()} will return a {@code JSplitPane} reference.
* <p>
* A key concept that drives the {@code DefaultDockingPort}, then, is the
* notion that this {@code DockingPort} implementation may only ever have one
* single child component, which may or may not be a wrapper for other child
* components. Because {@code JSplitPane} contains child
* {@code DefaultDockingPorts}, each of those {@code DefaultDockingPorts} is
* available for further sub-docking operations.
* <p>
* Since a {@code DefaultDockingPort} may only contain one child component,
* there is a container hierarchy to manage tabbed interfaces, split layouts,
* and sub-docking. As components are removed from this hierarchy, the hierarchy
* itself must be reevaluated. Removing a component from a child
* {@code DefaultDockingPort} within a {@code JSplitPane} renders the child
* {@code DefaultDockingPort} unnecessary, which, in turn, renders the notion of
* splitting the layout with a {@code JSplitPane} unnecessary (since there are
* no longer two components to split the layout between). Likewise, removing a
* child component from a {@code JTabbedPane} such that there is only one child
* left within the {@code JTabbedPane} removes the need for a tabbed interface
* to begin with.
* <p>
* When the {@code DockingManager} removes a component from a
* {@code DockingPort} via {@code DockingManager.undock(Dockable dockable)} it
* uses a call to {@code undock()} on the current {@code DockingPort}.
* {@code undock()} automatically handles the reevaluation of the container
* hierarchy to keep wrapper-container usage at a minimum. Since
* {@code DockingManager} makes this callback automatic, developers normally
* will not need to call this method explicitly. However, when removing a
* component from a {@code DefaultDockingPort} using application code,
* developers should keep in mind to use {@code undock()} instead of
* {@code remove()}.
*
* Border management after docking and undocking operations are accomplished
* using a {@code BorderManager}. {@code setBorderManager()} may be used to set
* the border manager instance and customize border management.
*
* @author Christopher Butler
*
*/
public class DefaultDockingPort extends JPanel implements DockingPort,
DockingConstants {
protected class PortLayout implements LayoutManager2, Serializable {
/**
* Returns the amount of space the layout would like to have.
*
* @param parent
* the Container for which this layout manager is being used
* @return a Dimension object containing the layout's preferred size
*/
public Dimension preferredLayoutSize(Container parent) {
Dimension dd;
Insets i = getInsets();
if (dockedComponent != null) {
dd = dockedComponent.getPreferredSize();
} else {
dd = parent.getSize();
}
return new Dimension(dd.width + i.left + i.right, dd.height + i.top
+ i.bottom);
}
/**
* Returns the minimum amount of space the layout needs.
*
* @param parent
* the Container for which this layout manager is being used
* @return a Dimension object containing the layout's minimum size
*/
public Dimension minimumLayoutSize(Container parent) {
Dimension dd;
Insets i = getInsets();
if (dockedComponent != null) {
dd = dockedComponent.getMinimumSize();
} else {
dd = parent.getSize();
}
return new Dimension(dd.width + i.left + i.right, dd.height + i.top
+ i.bottom);
}
/**
* Returns the maximum amount of space the layout can use.
*
* @param target
* the Container for which this layout manager is being used
* @return a Dimension object containing the layout's maximum size
*/
public Dimension maximumLayoutSize(Container target) {
Dimension dd;
Insets i = getInsets();
if (dockedComponent != null) {
dd = dockedComponent.getMaximumSize();
} else {
// This is silly, but should stop an overflow error
dd = new Dimension(Integer.MAX_VALUE, Integer.MAX_VALUE - i.top
- i.bottom);
}
return new Dimension(dd.width + i.left + i.right, dd.height + i.top
+ i.bottom);
}
/**
* Instructs the layout manager to perform the layout for the specified
* container.
*
* @param parent
* the Container for which this layout manager is being used
*/
public void layoutContainer(Container parent) {
Rectangle b = getBounds();
Insets i = getInsets();
int w = b.width - i.right - i.left;
int h = b.height - i.top - i.bottom;
if (dockedComponent != null) {
dockedComponent.setBounds(i.left, i.top, w, h);
}
}
public void addLayoutComponent(String name, Component comp) {
}
public void removeLayoutComponent(Component comp) {
}
public void addLayoutComponent(Component comp, Object constraints) {
}
public float getLayoutAlignmentX(Container target) {
return 0.0f;
}
public float getLayoutAlignmentY(Container target) {
return 0.0f;
}
public void invalidateLayout(Container target) {
}
}
private static final WeakHashMap COMPONENT_TITLES = new WeakHashMap();
protected ArrayList dockingListeners;
private Component dockedComponent;
private BorderManager borderManager;
private String persistentId;
private boolean tabsAsDragSource;
private boolean rootPort;
private BufferedImage dragImage;
static {
// setup PropertyChangeListenerFactory to respond to
// DefaultDockingPort-specific
// events
PropertyChangeListenerFactory
.addFactory(new DockablePropertyChangeHandler.Factory());
}
/**
* Creates a new {@code DefaultDockingPort} with a persistent ID equal to
* the {@code String} value of this a random UUID.
*
* @see org.flexdock.util.UUID
*/
public DefaultDockingPort() {
this(UUID.randomUUID().toString());
}
/**
* Creates a new {@code DefaultDockingPort} with the specified persistent
* ID. If {@code id} is {@code null}, then the {@code String} value of this
* {@code Object's} hash code is used. The persistent ID will be the same
* value returned by invoking {@code getPersistentId()} for this
* {@code DefaultDockingPort}.
*
* @param id
* the persistent ID for the new {@code DefaultDockingPort}
* instance.
*/
public DefaultDockingPort(String id) {
setPersistentId(id);
dockingListeners = new ArrayList(2);
addDockingListener(this);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -