org.noos.xing.mydoggy.plaf.ui.cmp.MultiSplitDockableContainer Maven / Gradle / Ivy
The newest version!
package org.noos.xing.mydoggy.plaf.ui.cmp;
import org.noos.xing.mydoggy.AggregationPosition;
import org.noos.xing.mydoggy.Dockable;
import org.noos.xing.mydoggy.plaf.MyDoggyToolWindowManager;
import org.noos.xing.mydoggy.plaf.ui.MyDoggyKeySpace;
import org.noos.xing.mydoggy.plaf.ui.cmp.multisplit.DockableLeaf;
import org.noos.xing.mydoggy.plaf.ui.cmp.multisplit.MultiSplitLayout.Divider;
import org.noos.xing.mydoggy.plaf.ui.cmp.multisplit.MultiSplitLayout.Leaf;
import org.noos.xing.mydoggy.plaf.ui.cmp.multisplit.MultiSplitLayout.Node;
import org.noos.xing.mydoggy.plaf.ui.cmp.multisplit.MultiSplitLayout.Split;
import org.noos.xing.mydoggy.plaf.ui.cmp.multisplit.MultiSplitPanel;
import org.noos.xing.mydoggy.plaf.ui.util.SwingUtil;
import javax.swing.*;
import java.awt.*;
import java.beans.XMLDecoder;
import java.beans.XMLEncoder;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.util.*;
import java.util.List;
/**
* @author Angelo De Caro ([email protected])
*/
public class MultiSplitDockableContainer extends JPanel {
public enum Action {
ADD_DOCK,
REMOVE_DOCK
}
protected MyDoggyToolWindowManager toolWindowManager;
protected Map entries;
protected int orientation;
protected AggregationPosition defaultAggregationPosition;
protected MultiSplitPanel multiSplitPane;
protected Node multiSplitPaneModelRoot;
protected boolean storeLayout;
protected byte[] lastLayout;
protected Dockable removedDockable;
protected boolean useAlwaysContentWrapper;
protected boolean jumpResetBounds;
protected int leafNameCounter = 0;
public MultiSplitDockableContainer(MyDoggyToolWindowManager toolWindowManager, int orientation) {
this.orientation = orientation;
this.toolWindowManager = toolWindowManager;
this.entries = new LinkedHashMap();
this.multiSplitPane = new MultiSplitPanel();
this.multiSplitPane.setDividerSize(5);
this.multiSplitPane.setFocusable(false);
this.storeLayout = true;
this.lastLayout = null;
this.removedDockable = null;
if (orientation != JSplitPane.VERTICAL_SPLIT) {
defaultAggregationPosition = AggregationPosition.RIGHT;
} else
defaultAggregationPosition = AggregationPosition.BOTTOM;
this.multiSplitPaneModelRoot = null;
this.useAlwaysContentWrapper = false;
setLayout(new ExtendedTableLayout(new double[][]{{-1}, {-1}}));
setBackground(Color.GRAY);
setOpaque(true);
setFocusable(false);
}
public void addDockable(Dockable dockable,
Component content,
Dockable aggregationOnDockable,
int aggregationIndexLocation,
AggregationPosition aggregationPosition) {
// Validate the model
if (!checkModel())
System.out.println("Check model fail. addDockable before");
if (entries.size() == 0) {
// Just one leaf
DockableLeaf leaf = new DockableLeaf(getNextLeanName(),
dockable.getId());
leaf.setWeight(1.0d);
multiSplitPaneModelRoot = leaf;
multiSplitPane.setModel(leaf);
multiSplitPane.add(getWrapperForComponent(dockable, content, Action.ADD_DOCK), "1");
setRootComponent(multiSplitPane);
SwingUtil.repaint(this);
} else {
boolean resetB = true;
byte[] oldModel = null;
if (dockable.equals(removedDockable)) {
oldModel = lastLayout;
removedDockable = null;
lastLayout = null;
}
boolean invalidAggregationPosition = false;
if (aggregationPosition == AggregationPosition.DEFAULT || aggregationPosition == null) {
invalidAggregationPosition = true;
aggregationPosition = defaultAggregationPosition;
}
if (multiSplitPaneModelRoot instanceof DockableLeaf) {
// The root is a leaf...
DockableLeaf rootLeaf = (DockableLeaf) multiSplitPaneModelRoot;
if (aggregationOnDockable != null && (aggregationPosition == null || invalidAggregationPosition)) {
// Aggregate to an already registered leaf
Component componentWrapper;
if (rootLeaf.getDockables().size() > 1) {
componentWrapper = multiSplitPane.getComponent(0);
} else {
Component rootLeafCmp = getComponentFromWrapper(multiSplitPane.getComponent(0));
multiSplitPane.removeAll();
componentWrapper = forceWrapperForComponent(entries.values().iterator().next().dockable, rootLeafCmp);
multiSplitPane.add(componentWrapper, rootLeaf.getName());
}
// The requeste is to add more than one dockable on the same leaf... no need to request a new leaf name
addToWrapper(componentWrapper, dockable, aggregationIndexLocation, content);
repaintMultiSplit(toolWindowManager.getClientProperty(MyDoggyKeySpace.PERSISTENCE_DELEGATE_PARSING) != null,
multiSplitPaneModelRoot);
rootLeaf.addDockable(dockable.getId());
} else {
// Create a new leaf...
DockableLeaf firstLeaf = rootLeaf;
DockableLeaf secondLeaf;
// Init two leafs
firstLeaf.setWeight(0.5);
secondLeaf = new DockableLeaf(getNextLeanName());
secondLeaf.setWeight(0.5);
secondLeaf.addDockable(dockable.getId());
List children = null;
switch (aggregationPosition) {
case LEFT:
case TOP:
children = Arrays.asList(secondLeaf,
new Divider(),
firstLeaf);
break;
case RIGHT:
case BOTTOM:
children = Arrays.asList(firstLeaf,
new Divider(),
secondLeaf);
break;
}
boolean rowLayout = (aggregationPosition == AggregationPosition.LEFT || aggregationPosition == AggregationPosition.RIGHT);
Split split = new Split();
split.setRowLayout(rowLayout);
split.setBounds(split.getBounds());
split.setChildren(children);
// update the model
multiSplitPaneModelRoot = split;
if (!multiSplitPane.getMultiSplitLayout().getFloatingDividers())
multiSplitPane.getMultiSplitLayout().setFloatingDividers(true);
validateModel(multiSplitPaneModelRoot);
multiSplitPane.setModel(multiSplitPaneModelRoot);
// update the components in the container
Component wrapper;
if (rootLeaf.getDockables().size() > 1) {
wrapper = multiSplitPane.getComponent(0);
} else {
Dockable delegator = entries.values().iterator().next().dockable;
wrapper = getWrapperForComponent(delegator, getComponentFromWrapper(multiSplitPane.getComponent(0)), Action.ADD_DOCK);
}
multiSplitPane.removeAll();
multiSplitPane.add(wrapper, firstLeaf.getName());
multiSplitPane.add(getWrapperForComponent(dockable, content, Action.ADD_DOCK), secondLeaf.getName());
}
} else {
// Take the root, it's a split
Split splitRoot = (Split) multiSplitPaneModelRoot;
// Build content to add
boolean addCmp = true;
String leafName = null;
// Modify model
if (aggregationOnDockable != null) {
// Search for aggregationOnDockable leaf
Stack stack = new Stack();
stack.push(splitRoot);
while (!stack.isEmpty()) {
Split split = stack.pop();
for (Node child : split.getChildren()) {
if (child instanceof DockableLeaf) {
DockableLeaf leaf = (DockableLeaf) child;
if (leaf.getDockables().contains(aggregationOnDockable.getId())) {
if (invalidAggregationPosition) {
// The requeste is to add more than one dockable on the same leaf...
addToWrapper(multiSplitPane.getMultiSplitLayout().getChildMap().get(leaf.getName()),
dockable,
aggregationIndexLocation,
content);
leaf.addDockable(dockable.getId());
addCmp = false;
resetB = false;
} else {
leafName = getNextLeanName();
boolean step1Failed = false;
// Check for concordance to leaf.getParent().isRowLayout and aggregationPosition
Split parent = leaf.getParent();
boolean rowLayout = parent.isRowLayout();
List parentChildren = parent.getChildren();
int startIndex = parentChildren.indexOf(leaf);
if (rowLayout) {
boolean finalize = false;
switch (aggregationPosition) {
case LEFT:
parentChildren.add(startIndex, new DockableLeaf(leafName, dockable.getId()));
parentChildren.add(startIndex + 1, new Divider());
finalize = true;
break;
case RIGHT:
parentChildren.add(startIndex + 1, new Divider());
parentChildren.add(startIndex + 2, new DockableLeaf(leafName, dockable.getId()));
finalize = true;
break;
default:
step1Failed = true;
}
if (finalize) {
// Set new children
forceWeight(parentChildren);
parent.setChildren(parentChildren);
}
} else {
boolean finalize = false;
switch (aggregationPosition) {
case TOP:
parentChildren.add(startIndex, new DockableLeaf(leafName, dockable.getId()));
parentChildren.add(startIndex + 1, new Divider());
finalize = true;
break;
case BOTTOM:
parentChildren.add(startIndex + 1, new Divider());
parentChildren.add(startIndex + 2, new DockableLeaf(leafName, dockable.getId()));
finalize = true;
break;
default:
step1Failed = true;
}
if (finalize) {
// Set new children
forceWeight(parentChildren);
parent.setChildren(parentChildren);
}
}
if (step1Failed) {
// Create two leafs
Leaf newleaf = new DockableLeaf(leafName, dockable.getId());
newleaf.setWeight(0.5);
// Creat the split
Split newSplit = new Split();
newSplit.setBounds(leaf.getBounds());
newSplit.setRowLayout((aggregationPosition == AggregationPosition.LEFT || aggregationPosition == AggregationPosition.RIGHT));
newSplit.setWeight(leaf.getWeight());
leaf.getParent().removeNode(leaf);
switch (aggregationPosition) {
case LEFT:
case TOP:
newSplit.setChildren(Arrays.asList(newleaf,
new Divider(),
leaf));
break;
default:
newSplit.setChildren(Arrays.asList(leaf,
new Divider(),
newleaf));
break;
}
leaf.setWeight(0.5);
// Switch the leaf with the new split
parentChildren.set(startIndex, newSplit);
parent.setChildren(parentChildren);
}
}
stack.clear();
break;
}
} else if (child instanceof Split) {
stack.push((Split) child);
}
}
}
if (!multiSplitPane.getMultiSplitLayout().getFloatingDividers())
multiSplitPane.getMultiSplitLayout().setFloatingDividers(true);
} else {
leafName = getNextLeanName();
boolean rowLayout = (aggregationPosition == AggregationPosition.LEFT || aggregationPosition == AggregationPosition.RIGHT);
if (splitRoot.isRowLayout() == rowLayout) {
List children = splitRoot.getChildren();
switch (aggregationPosition) {
case LEFT:
case TOP:
children.add(0, new DockableLeaf(leafName, dockable.getId()));
children.add(1, new Divider());
break;
case RIGHT:
case BOTTOM:
children.add(new Divider());
children.add(new DockableLeaf(leafName, dockable.getId()));
break;
}
forceWeight(children);
splitRoot.setChildren(children);
} else {
Split newRoot = new Split();
newRoot.setRowLayout(rowLayout);
newRoot.setBounds(multiSplitPaneModelRoot.getBounds());
Leaf leaf = new DockableLeaf(leafName, dockable.getId());
leaf.setWeight(0.5);
multiSplitPaneModelRoot.setWeight(0.5);
List children = null;
switch (aggregationPosition) {
case LEFT:
case TOP:
children = Arrays.asList(leaf,
new Divider(),
multiSplitPaneModelRoot);
break;
case RIGHT:
case BOTTOM:
children = Arrays.asList(multiSplitPaneModelRoot,
new Divider(),
leaf);
break;
}
forceWeight(children);
newRoot.setChildren(children);
multiSplitPaneModelRoot = newRoot;
}
if (!multiSplitPane.getMultiSplitLayout().getFloatingDividers())
multiSplitPane.getMultiSplitLayout().setFloatingDividers(true);
}
// }
validateModel(multiSplitPaneModelRoot);
multiSplitPane.setModel(multiSplitPaneModelRoot);
if (addCmp)
multiSplitPane.add(getWrapperForComponent(dockable, content, Action.ADD_DOCK), leafName);
}
if (!checkModel())
System.out.println("Check model fail. addDockable end");
if (storeLayout && oldModel != null) {
// Decode stored model
final Node decodedModel = decode(oldModel);
// jumpResetBounds = true;
multiSplitPane.getMultiSplitLayout().setFloatingDividers(false);
SwingUtilities.invokeLater(new Runnable() {
public void run() {
setMultiSplitLayout(decodedModel);
}
});
} else {
if (resetB) {
Node split = getLeaf(dockable).getParent();
if (split == null)
split = multiSplitPaneModelRoot;
repaintMultiSplit(toolWindowManager.getClientProperty(MyDoggyKeySpace.PERSISTENCE_DELEGATE_PARSING) == null,
split);
} else {
repaintMultiSplit(false,
null);
}
}
}
entries.put(dockable, new DockableEntry(dockable, content));
}
public void addDockable(Dockable dockable, Component component, DockableConstraint dockableConstraint) {
if (dockableConstraint == null)
addDockable(dockable, component, null, -1, AggregationPosition.DEFAULT);
else {
if (dockableConstraint.getNode() == null)
addDockable(dockable, component, null, dockableConstraint.getIndex(), dockableConstraint.getAggregationPosition());
else {
if (dockableConstraint.getNode() instanceof DockableLeaf) {
DockableLeaf leaf = (DockableLeaf) dockableConstraint.getNode();
for (String leafDockableId : leaf.getDockables()) {
Dockable leafDockable = toolWindowManager.lookupDockable(leafDockableId);
if (leafDockable != null && !leafDockable.isDetached()) {
addDockable(dockable, component, leafDockable, dockableConstraint.getIndex(), dockableConstraint.getAggregationPosition());
return;
}
}
addDockable(dockable, component, null, dockableConstraint.getIndex(), dockableConstraint.getAggregationPosition());
} else if (dockableConstraint.getNode() instanceof Split) {
Split split = (Split) dockableConstraint.getNode();
if (multiSplitPaneModelRoot == split)
addDockable(dockable, component, null, dockableConstraint.getIndex(), dockableConstraint.getAggregationPosition());
else {
Split parent = split.getParent();
if (!isNodeAttached(split) || parent == null) {
addDockable(dockable, component, null, dockableConstraint.getIndex(), dockableConstraint.getAggregationPosition());
} else {
AggregationPosition aggregationPosition = dockableConstraint.getAggregationPosition();
switch (aggregationPosition) {
case TOP:
case LEFT:
List nodes = parent.getChildren();
DockableLeaf dockableLeaf = new DockableLeaf(getNextLeanName(), dockable.getId());
dockableLeaf.setWeight(0.5d);
int index = nodes.indexOf(split);
nodes.add(index, dockableLeaf);
nodes.add(index + 1, new Divider());
parent.setChildren(nodes);
multiSplitPane.add(getWrapperForComponent(dockable, component, Action.ADD_DOCK),
dockableLeaf.getName());
break;
case BOTTOM:
case RIGHT:
nodes = parent.getChildren();
dockableLeaf = new DockableLeaf(getNextLeanName(), dockable.getId());
dockableLeaf.setWeight(0.5d);
index = nodes.indexOf(split);
nodes.add(index, dockableLeaf);
nodes.add(index - 1, new Divider());
parent.setChildren(nodes);
multiSplitPane.add(getWrapperForComponent(dockable, component, Action.ADD_DOCK),
dockableLeaf.getName());
break;
}
entries.put(dockable, new DockableEntry(dockable, component));
validateModel(multiSplitPaneModelRoot);
repaintMultiSplit(toolWindowManager.getClientProperty(MyDoggyKeySpace.PERSISTENCE_DELEGATE_PARSING) != null,
multiSplitPaneModelRoot);
}
}
} else
addDockable(dockable, component, null, dockableConstraint.getIndex(), dockableConstraint.getAggregationPosition());
}
}
}
public DockableConstraint removeDockable(Dockable dockable) {
// Validate the request
if (dockable == null)
throw new IllegalArgumentException("Cannot remove dockable. [dockable null]");
DockableEntry dockableEntry = entries.get(dockable);
if (dockableEntry == null)
return null;
// Store layout
if (storeLayout) {
lastLayout = encode();
removedDockable = dockable;
}
// Remove the dockable
entries.remove(dockable);
// Adjust the layout...
if (entries.size() == 0) {
DockableLeaf dockableLeaf = getLeaf(dockable);
Component cmp = multiSplitPane.getMultiSplitLayout().getChildMap().get(dockableLeaf.getName());
if (isWrapper(cmp))
removeFromWrapper(cmp, dockable);
multiSplitPaneModelRoot = null;
multiSplitPane.removeAll();
resetRootComponent();
leafNameCounter = 0;
SwingUtil.repaint(this);
// No constraint for the content because it was alone on the container
return null;
}
if (entries.size() == 1) {
if (multiSplitPaneModelRoot instanceof DockableLeaf) {
DockableLeaf leaf = (DockableLeaf) multiSplitPaneModelRoot;
int index = removeFromWrapper(multiSplitPane.getComponent(0), dockable);
leaf.getDockables().remove(dockable.getId());
if (!useAlwaysContentWrapper) {
Component root = multiSplitPane.getComponent(0);
multiSplitPane.removeAll();
multiSplitPane.add(getComponentFromWrapper(root), "1");
}
return new DockableConstraint(leaf,
AggregationPosition.DEFAULT,
index);
} else {
// the root is a split
// remove the component related to dockable
Component component = multiSplitPane.getMultiSplitLayout().getChildMap().get(getLeafName(dockable));
if (isWrapper(component))
removeFromWrapper(component, dockable);
multiSplitPane.remove(component);
// retrieve the component related to sole entry in entries
Dockable leftDockable = entries.keySet().iterator().next();
DockableLeaf leftLeaf = getLeaf(leftDockable);
Component masterLeftLeafCmp = multiSplitPane.getMultiSplitLayout().getChildMap().get(leftLeaf.getName());
Component leftLeafCmp = getComponentFromWrapper(masterLeftLeafCmp);
leftLeaf.setName("1");
if (isWrapper(masterLeftLeafCmp))
removeFromWrapper(masterLeftLeafCmp, leftDockable);
// Update the model
AggregationPosition constraintAggPosition = AggregationPosition.DEFAULT;
Split rootSplit = (Split) multiSplitPaneModelRoot;
List children = rootSplit.getChildren();
for (int i = 0, size = children.size(); i < size; i++) {
Node node = children.get(i);
if (node == leftLeaf) {
if (i == 0) {
if (rootSplit.isRowLayout())
constraintAggPosition = AggregationPosition.RIGHT;
else
constraintAggPosition = AggregationPosition.BOTTOM;
} else if (rootSplit.isRowLayout())
constraintAggPosition = AggregationPosition.LEFT;
else
constraintAggPosition = AggregationPosition.TOP;
break;
}
}
multiSplitPaneModelRoot = leftLeaf;
multiSplitPaneModelRoot.setParent(null);
// Update the pane
multiSplitPane.setModel(multiSplitPaneModelRoot);
multiSplitPane.removeAll();
multiSplitPane.add(getWrapperForComponent(leftDockable, leftLeafCmp, Action.REMOVE_DOCK), "1");
// Finalize
leafNameCounter = 1;
SwingUtil.repaint(this);
// Prepare constraint
return new DockableConstraint(leftLeaf,
constraintAggPosition,
-1);
}
} else {
DockableConstraint dockableConstraint = null;
DockableLeaf dockableLeaf = getLeaf(dockable);
if (dockableLeaf == null)
throw new IllegalArgumentException("Cannot remove the dockable. Cannot find leaf. [id : " + dockable.getId() + "]");
if (dockableLeaf.getDockables().size() > 1) {
// There are more than one dockable on the same leaf
// Remove the dockable from leaf and from aggregating component...
dockableLeaf.getDockables().remove(dockable.getId());
int index = removeFromWrapper(multiSplitPane.getMultiSplitLayout().getChildMap().get(dockableLeaf.getName()),
dockable);
// Prepare dockableConstraint
return new DockableConstraint(dockableLeaf,
AggregationPosition.DEFAULT,
index);
} else {
leafNameCounter--;
// There is one dockable on the leaf. We have to rearrange the layout...
String leafKey = dockableLeaf.getName();
int leafValue = Integer.parseInt(leafKey);
Component component = multiSplitPane.getMultiSplitLayout().getChildMap().get(leafKey);
// Remove content
if (component != null) {
// Remove the component from the multiSplitPane
if (isWrapper(component))
removeFromWrapper(component, dockable);
multiSplitPane.remove(component);
// Update model
// Navigate the model to look for the requested leaf
Stack stack = new Stack();
stack.push((Split) multiSplitPaneModelRoot);
boolean setChild = true;
while (!stack.isEmpty()) {
Split split = stack.pop();
List children = split.getChildren();
for (int i = 0; i < children.size(); i++) {
Node child = children.get(i);
if (child instanceof Leaf) {
Leaf leaf = (Leaf) child;
String leafName = leaf.getName();
if (leafName.equals(leafKey)) {
// Analyze parent
children.remove(i);
// Analyze children now...
if (children.size() == 2) {
Split grandpa = split.getParent();
if (grandpa == null) {
multiSplitPaneModelRoot = getFirstNotDivider(children);
multiSplitPaneModelRoot.setParent(null);
// Prepare dockableConstraint
AggregationPosition position;
if (children.get(0) == multiSplitPaneModelRoot) {
position = (split.isRowLayout()) ? AggregationPosition.RIGHT : AggregationPosition.BOTTOM;
} else
position = (split.isRowLayout()) ? AggregationPosition.LEFT : AggregationPosition.TOP;
dockableConstraint = new DockableConstraint(multiSplitPaneModelRoot,
position,
-1);
setChild = false;
} else {
List grenpaChildren = grandpa.getChildren();
if (children.get(0) instanceof Divider) {
grenpaChildren.set(grenpaChildren.indexOf(split),
children.get(1));
// Prepare dockableConstraint
dockableConstraint = new DockableConstraint(children.get(1),
(split.isRowLayout()) ? AggregationPosition.LEFT : AggregationPosition.TOP,
-1);
} else {
grenpaChildren.set(grenpaChildren.indexOf(split),
children.get(0));
// Prepare dockableConstraint
dockableConstraint = new DockableConstraint(children.get(0),
(split.isRowLayout()) ? AggregationPosition.RIGHT : AggregationPosition.BOTTOM,
-1);
}
grandpa.setChildren(grenpaChildren);
setChild = false;
}
} else {
// Remove the divider
if (i < children.size()) {
children.remove(i);
dockableConstraint = new DockableConstraint(children.get(i),
(split.isRowLayout()) ? AggregationPosition.LEFT : AggregationPosition.TOP,
-1);
} else {
children.remove(i - 1);
dockableConstraint = new DockableConstraint(children.get(i - 2),
(split.isRowLayout()) ? AggregationPosition.RIGHT : AggregationPosition.BOTTOM,
-1);
}
i--;
}
} else {
// We have to rename the leaf if the name is not valid.
Integer keyValue = Integer.parseInt(leafName);
if (keyValue > leafValue) {
String newKey = "" + (keyValue - 1);
leaf.setName(newKey);
}
}
} else if (child instanceof Split) {
stack.push((Split) child);
}
}
if (setChild)
split.setChildren(children);
if (!checkModel())
System.out.println("Check model fail. removeDockable inner");
}
// Change constaints for component to the new leaf order.
Map childMap = multiSplitPane.getMultiSplitLayout().getChildMap();
String[] keys = childMap.keySet().toArray(new String[childMap.keySet().size()]);
//Arrays.sort(keys);
for (String key : keys) {
Integer keyValue = Integer.parseInt(key);
if (keyValue > leafValue) {
String newKey = "" + (keyValue - 1);
Component oldCmpForLeaf = multiSplitPane.getMultiSplitLayout().getChildMap().get(key);
multiSplitPane.remove(oldCmpForLeaf);
multiSplitPane.add(oldCmpForLeaf, newKey);
}
}
validateModel(multiSplitPaneModelRoot);
multiSplitPane.setModel(multiSplitPaneModelRoot);
multiSplitPane.revalidate();
} else
throw new IllegalArgumentException("Cannot find component on multisplit...");
}
if (!checkModel())
System.out.println("Check model fail. removeDockable end");
repaintMultiSplit(false/*toolWindowManager.getClientProperty(MyDoggyKeySpace.PERSISTENCE_DELEGATE_PARSING) == null*/,
null);
return dockableConstraint;
}
}
public DockableConstraint removeDockable(Dockable dockable, boolean storeLayout) {
boolean old = this.storeLayout;
this.storeLayout = storeLayout;
try {
return removeDockable(dockable);
} finally {
this.storeLayout = old;
}
}
public void setConstraints(Dockable dockable,
Component content,
Dockable aggregationOnDockable,
int aggregationIndexLocation,
AggregationPosition aggregationPosition) {
boolean old = this.storeLayout;
storeLayout = false;
try {
removeDockable(dockable);
addDockable(dockable, content, aggregationOnDockable, aggregationIndexLocation, aggregationPosition);
} finally {
storeLayout = old;
}
}
public Node getMultiSplitLayout() {
return multiSplitPaneModelRoot;
}
public void setMultiSplitLayout(Node root) {
if (root == null)
return;
// Check for contents root
if (root instanceof DockableLeaf) {
DockableLeaf dockableLeaf = (DockableLeaf) root;
if (!containsDockable(dockableLeaf) || dockableLeaf.getDockables().size() != entries.size())
return;
} else if (root instanceof Split) {
Stack stack = new Stack();
stack.push((Split) root);
int dockCounter = 0;
while (!stack.isEmpty()) {
Split split = stack.pop();
for (Node child : split.getChildren()) {
if (child instanceof Split) {
stack.push((Split) child);
} else if (child instanceof DockableLeaf) {
DockableLeaf dockableLeaf = (DockableLeaf) child;
if (!containsDockable(dockableLeaf))
return;
dockCounter += dockableLeaf.getDockables().size();
}
}
}
if (dockCounter != entries.size())
return;
} else
throw new RuntimeException("Invalid model. [model : " + root + "]");
// Mount new root
Map currentChildMap = multiSplitPane.getMultiSplitLayout().getChildMap();
// First Step: disaggregate...
if (multiSplitPaneModelRoot instanceof DockableLeaf) {
DockableLeaf leaf = (DockableLeaf) multiSplitPaneModelRoot;
String[] dockIds = leaf.getDockables().toArray(new String[leaf.getDockables().size()]);
for (String dockId : dockIds) {
if (leaf.getDockables().size() == 1)
break;
Component component = currentChildMap.get(leaf.getName());
Dockable dockable = toolWindowManager.lookupDockable(dockId);
setConstraints(dockable,
getComponentFromWrapper(component, dockable),
null,
-1,
AggregationPosition.DEFAULT
);
}
} else {
Stack stack = new Stack();
stack.push((Split) multiSplitPaneModelRoot);
while (!stack.isEmpty()) {
Split split = stack.pop();
for (Node child : split.getChildren()) {
if (child instanceof DockableLeaf) {
DockableLeaf leaf = (DockableLeaf) child;
String[] dockIds = leaf.getDockables().toArray(new String[leaf.getDockables().size()]);
for (String dockId : dockIds) {
if (leaf.getDockables().size() == 1)
break;
Component component = currentChildMap.get(leaf.getName());
Dockable dockable = toolWindowManager.lookupDockable(dockId);
setConstraints(dockable,
getComponentFromWrapper(component, dockable),
null,
-1,
AggregationPosition.DEFAULT
);
}
} else if (child instanceof Split) {
stack.push((Split) child);
}
}
}
}
if (root instanceof Split) {
// Step Two: apply model 1...Aggregate
Stack stack = new Stack();
stack.push((Split) root);
while (!stack.isEmpty()) {
Split split = stack.pop();
for (Node child : split.getChildren()) {
if (child instanceof DockableLeaf) {
DockableLeaf leaf = (DockableLeaf) child;
List ids = leaf.getDockables();
Dockable master = toolWindowManager.lookupDockable(leaf.getDockable());
for (int i = 1; i < ids.size(); i++) {
Dockable dockable = toolWindowManager.lookupDockable(ids.get(i));
setConstraints(dockable,
getComponentFromWrapper(currentChildMap.get(getLeaf(dockable).getName()), dockable),
master,
-1,
AggregationPosition.DEFAULT
);
}
} else if (child instanceof Split) {
stack.push((Split) child);
}
}
}
// Step Two: apply model 2...Isomorphing
Map newChildMap = new LinkedHashMap();
stack = new Stack();
stack.push((Split) root);
while (!stack.isEmpty()) {
Split split = stack.pop();
for (Node child : split.getChildren()) {
if (child instanceof DockableLeaf) {
DockableLeaf leaf = (DockableLeaf) child;
DockableLeaf currentLeaf = getLeaf(multiSplitPaneModelRoot, leaf.getDockable());
newChildMap.put(
leaf.getName(),
currentChildMap.get(currentLeaf.getName())
);
} else if (child instanceof Split) {
stack.push((Split) child);
}
}
}
multiSplitPane.getMultiSplitLayout().setChildMap(newChildMap);
} else {
DockableLeaf leaf = (DockableLeaf) root;
List dockIds = leaf.getDockables();
Dockable masterDockable = toolWindowManager.lookupDockable(leaf.getDockable());
for (int i = 1; i < dockIds.size(); i++) {
String dockId = dockIds.get(i);
Dockable dockable = toolWindowManager.lookupDockable(dockId);
setConstraints(dockable,
getComponentFromWrapper(currentChildMap.get(getLeaf(dockable).getName()), dockable),
masterDockable,
-1,
AggregationPosition.DEFAULT
);
}
}
validateModel(root);
multiSplitPaneModelRoot = root;
multiSplitPaneModelRoot.setParent(null);
multiSplitPane.setModel(multiSplitPaneModelRoot);
repaintMultiSplit(false, multiSplitPaneModelRoot);
}
public boolean isEmpty() {
return entries.size() == 0;
}
public int getDockableCount() {
return entries.size();
}
public List getDockableEntries() {
return new ArrayList(entries.values());
}
public void clear() {
resetRootComponent();
multiSplitPane.removeAll();
entries.clear();
}
public boolean isStoreLayout() {
return storeLayout;
}
public void setStoreLayout(boolean storeLayout) {
this.storeLayout = storeLayout;
}
public boolean isUseAlwaysContentWrapper() {
return useAlwaysContentWrapper;
}
public void setUseAlwaysContentWrapper(boolean useAlwaysContentWrapper) {
if (this.useAlwaysContentWrapper == useAlwaysContentWrapper)
return;
this.useAlwaysContentWrapper = useAlwaysContentWrapper;
if (useAlwaysContentWrapper) {
if (entries.size() == 1) {
Component componentWrapper = getWrapperForComponent(entries.keySet().iterator().next(),
multiSplitPane.getComponent(0),
Action.REMOVE_DOCK);
multiSplitPane.removeAll();
multiSplitPane.add(componentWrapper, "1");
SwingUtil.repaint(this);
}
} else {
if (entries.size() == 1) {
Component wrappedComponent = getComponentFromWrapper(multiSplitPane.getComponent(0));
multiSplitPane.removeAll();
multiSplitPane.add(wrappedComponent, "1");
SwingUtil.repaint(this);
}
}
}
public Rectangle getBoundsRelativeToScreen(Dockable dockable) {
DockableLeaf leaf = getLeaf(dockable);
if (leaf != null) {
Rectangle bounds = leaf.getBounds();
Point location = bounds.getLocation();
SwingUtilities.convertPointToScreen(location, this);
bounds.setLocation(location);
bounds.y += toolWindowManager.getJMenuBarExtraHeight();
return bounds;
}
return null;
}
public boolean containsDockable(Dockable dockable) {
for (DockableEntry entry : getDockableEntries()) {
if (entry.dockable == dockable)
return true;
}
return false;
}
public Dockable getDockable() {
return getDockableEntries().get(0).dockable;
}
public Component getDockableComponent() {
return getDockableEntries().get(0).component;
}
public List getDockables() {
List dockables = new ArrayList(entries.size());
for (DockableEntry dockableEntry : entries.values()) {
dockables.add((D) dockableEntry.dockable);
}
return dockables;
}
public boolean setComponent(D dockable, Component component) {
DockableEntry dockableEntry = entries.get(dockable);
if (dockableEntry != null) {
for (int i = 0; i < multiSplitPane.getComponentCount(); i++) {
DockableOwner dockableOwner = (DockableOwner) multiSplitPane.getComponent(i);
if (dockableOwner instanceof MultiDockableOwner) {
MultiDockableOwner multiDockableOwner = (MultiDockableOwner) dockableOwner;
if (multiDockableOwner.containsDockable(dockable)) {
multiDockableOwner.setComponent(dockable, component);
return true;
}
} else {
if (dockableOwner.getDockable() == dockable) {
dockableOwner.setComponent(component);
return true;
}
}
}
}
return false;
}
// Methods to manage container root
protected Component getRootComponent() {
return getComponent(0);
}
protected void setRootComponent(Component component) {
resetRootComponent();
add(component, "0,0,FULL,FULL");
}
protected void resetRootComponent() {
removeAll();
}
// Methods to manage wrappers
protected Component getWrapperForComponent(Dockable dockable, Component component, Action action) {
return new DockablePanel(dockable, component);
}
protected Component forceWrapperForComponent(Dockable dockable, Component component) {
return new DockablePanel(dockable, component);
}
protected Component getComponentFromWrapper(Component wrapper) {
if (wrapper instanceof DockablePanel)
return ((DockablePanel) wrapper).getComponent(0);
return wrapper;
}
protected void addToWrapper(Component wrapperSource, Dockable dockable, int aggregationIndexLocation, Component content) {
throw new IllegalStateException("Cannot call this method...");
}
protected int removeFromWrapper(Component wrapperSource, Dockable dockable) {
throw new IllegalStateException("Cannot call this method...");
}
protected Component getComponentFromWrapper(Component wrapper, Dockable dockable) {
throw new IllegalStateException("Cannot call this method...");
}
protected boolean isWrapRequest(Dockable dockable, Action action) {
return useAlwaysContentWrapper;
}
protected boolean isWrapper(Component component) {
return false;
}
// Model related methods
protected byte[] encode() {
// change context classloader
ClassLoader bundleClassLoader = this.getClass().getClassLoader();
ClassLoader originalClassLoader = Thread.currentThread().getContextClassLoader();
Thread.currentThread().setContextClassLoader(bundleClassLoader);
try {
ByteArrayOutputStream os = new ByteArrayOutputStream();
XMLEncoder e = new XMLEncoder(os);
e.writeObject(multiSplitPaneModelRoot);
e.flush();
e.close();
return os.toByteArray();
} finally {
// Restore
Thread.currentThread().setContextClassLoader(originalClassLoader);
}
}
protected Node decode(byte[] bytes) {
// change context classloader
ClassLoader bundleClassLoader = this.getClass().getClassLoader();
ClassLoader originalClassLoader = Thread.currentThread().getContextClassLoader();
Thread.currentThread().setContextClassLoader(bundleClassLoader);
try {
XMLDecoder d = new XMLDecoder(new ByteArrayInputStream(bytes));
Node node = (Node) (d.readObject());
return node;
} finally {
// Restore
Thread.currentThread().setContextClassLoader(originalClassLoader);
}
}
protected void validateModel(Node root) {
if (root == null || !(root instanceof Split))
return;
List children = ((Split) root).getChildren();
double sum = 0.0;
for (Node node : children) {
if (!(node instanceof Divider)) {
sum += node.getWeight();
}
if (node instanceof Split) {
validateModel(node);
}
if (sum > 1.0d)
break;
}
if (sum != 1.0d) {
double w = 1.0 / ((children.size() / 2) + 1);
sum = 0;
for (int i = 0, size = children.size() - 1; i < size; i++) {
Node node = children.get(i);
node.resetBounds();
if (!(node instanceof Divider)) {
node.setWeight(w);
sum += w;
}
}
Node lastNode = children.get(children.size() - 1);
lastNode.resetBounds();
lastNode.setWeight(1.0d - sum);
multiSplitPane.getMultiSplitLayout().setFloatingDividers(true);
}
}
protected void forceWeight(List children) {
double w = 1.0 / ((children.size() / 2) + 1);
for (Node node : children) {
// node.resetBounds();
if (!(node instanceof Divider)) {
node.setWeight(w);
}
}
multiSplitPane.getMultiSplitLayout().setFloatingDividers(true);
}
protected void resetBounds() {
resetBounds(multiSplitPaneModelRoot);
}
protected void resetBounds(Node rootNode) {
// Reset the model bounds...
if (rootNode == null || !(rootNode instanceof Split))
return;
Stack stack = new Stack();
stack.push((Split) rootNode);
rootNode.resetBounds();
while (!stack.isEmpty()) {
Split split = stack.pop();
for (Node child : split.getChildren()) {
child.resetBounds();
if (child instanceof Split) {
stack.push((Split) child);
}
}
}
}
protected boolean checkModel() {
if (multiSplitPaneModelRoot == null)
return true;
if (multiSplitPaneModelRoot.getParent() != null)
return false;
if (!(multiSplitPaneModelRoot instanceof Split))
return true;
Stack stack = new Stack();
stack.push((Split) multiSplitPaneModelRoot);
while (!stack.isEmpty()) {
Split split = stack.pop();
if (split.getChildren().size() <= 2)
return false;
for (Node child : split.getChildren()) {
if (child.getParent() == null || child.getParent() != split)
return false;
if (child instanceof Split)
stack.push((Split) child);
}
}
return true;
}
protected void repaintMultiSplit(boolean resetBounds, Node rootNode) {
SwingUtilities.invokeLater(new RepaintRunnable(resetBounds, rootNode));
}
protected Node getFirstNotDivider(List children) {
for (Node child : children) {
if (!(child instanceof Divider))
return child;
}
return null;
}
protected String getLeafName(Dockable dockable) {
DockableLeaf leaf = getLeaf(multiSplitPaneModelRoot, dockable.getId());
return (leaf != null) ? leaf.getName() : null;
}
protected DockableLeaf getLeaf(Dockable dockable) {
return getLeaf(multiSplitPaneModelRoot, dockable.getId());
}
protected DockableLeaf getLeaf(Node root, String dockableId) {
if (root == null || !(root instanceof Split)) {
if (root instanceof Leaf) {
DockableLeaf leaf = (DockableLeaf) root;
if (leaf.getDockables().contains(dockableId))
return leaf;
}
return null;
}
Stack stack = new Stack();
stack.push((Split) root);
while (!stack.isEmpty()) {
Split split = stack.pop();
for (Node child : split.getChildren()) {
if (child instanceof DockableLeaf) {
DockableLeaf leaf = (DockableLeaf) child;
if (leaf.getDockables().contains(dockableId))
return leaf;
} else if (child instanceof Split) {
stack.push((Split) child);
}
}
}
return null;
}
protected String getNextLeanName() {
return "" + (++leafNameCounter);
}
protected boolean isNodeAttached(Node node) {
if (node == null)
return false;
if (multiSplitPaneModelRoot == node)
return true;
else if (multiSplitPaneModelRoot instanceof Split) {
Stack stack = new Stack();
stack.push((Split) multiSplitPaneModelRoot);
while (!stack.isEmpty()) {
Split split = stack.pop();
if (split.getChildren().size() <= 2)
return false;
for (Node child : split.getChildren()) {
if (child == node)
return true;
if (child instanceof Split)
stack.push((Split) child);
}
}
}
return false;
}
protected boolean containsDockable(DockableLeaf leaf) {
for (String dockId : leaf.getDockables()) {
boolean found = false;
for (Dockable dockable : entries.keySet()) {
if (dockable.getId().equals(dockId)) {
found = true;
break;
}
}
if (!found)
return false;
}
return true;
}
public class DockableEntry {
Dockable dockable;
Component component;
DockableEntry(Dockable dockable, Component component) {
this.dockable = dockable;
this.component = component;
}
public Dockable getDockable() {
return dockable;
}
public Component getComponent() {
return component;
}
}
public class RepaintRunnable implements Runnable {
protected boolean reset;
protected Node rootNode;
public RepaintRunnable(boolean reset, Node rootNode) {
this.reset = reset;
this.rootNode = rootNode;
}
public void run() {
checkModel();
// if (reset)
// resetBounds(rootNode);
multiSplitPane.invalidate();
multiSplitPane.validate();
multiSplitPane.repaint();
multiSplitPane.getMultiSplitLayout().setFloatingDividers(false);
}
}
public class DebugRepaintRunnable implements Runnable {
protected Exception exception;
public DebugRepaintRunnable(Exception exception) {
this.exception = exception;
}
public void run() {
// exception.printStackTrace();
try {
checkModel();
if (jumpResetBounds) {
jumpResetBounds = false;
} else
resetBounds();
multiSplitPane.invalidate();
multiSplitPane.validate();
multiSplitPane.repaint();
multiSplitPane.getMultiSplitLayout().setFloatingDividers(false);
} catch (RuntimeException e) {
System.out.println("-----------------------------------------------------------------------");
exception.printStackTrace();
System.out.println("-----------------------------------------------------------------------");
throw e;
}
}
}
public class DockableConstraint {
protected Node node;
protected AggregationPosition aggregationPosition;
protected int index = -1;
public DockableConstraint(Node node, AggregationPosition aggregationPosition, int index) {
this.node = node;
this.aggregationPosition = aggregationPosition;
this.index = index;
}
public Node getNode() {
return node;
}
public AggregationPosition getAggregationPosition() {
return aggregationPosition;
}
public int getIndex() {
return index;
}
}
}