SplitDockPathProperty and Placeholders

Hi Beni,

I am using SplitDockStation.getDockablePathProperty to obtain a path for a dockable.
The returned path accounts for closed dockables or dockables moved to another station.
For example take the following 3 dockables:
dockA: TOP
dockB: BOTTOM, LEFT
dockC: BOTTOM, RIGHT
If we now close dockC the path for dockB is still BOTTOM, LEFT.
I would like to get the path that takes into account only the “real”
dockables that are present in the station. In this case just BOTTOM.
When I disable Placeholders (set an empty placeholder strategy),
the returned path is the one I expect (i.e. BOTTOM).
I don’t want to disable the placeholders though…

Is there a way to get the path that takes into account only the “real” dockables?

Thanks,
Maciej

Why not using “getDockableProperty”, that method would also take the placeholder of the Dockable in account (assuming there is a placeholder)?

I can add a method that returns the “real” location (which probably will completely mess up your layout), [edit: or even easier, provide you with a method that traverses the tree. I’ll do that in the evening]. But I’m not yet convinced that you really need the “real” location. What exactly are you doing with the DockableProperty-object?

I need the SplitDockPathProperty to save the docking position of the dockable.
I cannot use the framework mechanism for storing layout.
For each dockable I remember its path in the docking tree.
I use the paths for restoring the layout.
That is why I need a path the does NOT account for placeholders.
I tried getDockableProperty and also traversing the tree but in both cases I got paths
that take placeholders into account…

It is your application… I would advise to find a way and use the frameworks layouting mechanism, doing it yourself will be a lot of work.
If you have only the paths you have to carefully consider the order in which the Dockables are inserted into the tree, because a DockableProperty is used once to insert a Dockable, afterwards the framework never looks at it again. Placeholders allow you not to care about the order, and the class “DockSituation” allows to store the placeholders.

Anyway, this is a way to find the path containing only visible elements.


import javax.swing.JFrame;

import bibliothek.gui.Dockable;
import bibliothek.gui.dock.SplitDockStation;
import bibliothek.gui.dock.SplitDockStation.Orientation;
import bibliothek.gui.dock.common.CControl;
import bibliothek.gui.dock.common.CGrid;
import bibliothek.gui.dock.common.DefaultSingleCDockable;
import bibliothek.gui.dock.common.theme.ThemeMap;
import bibliothek.gui.dock.layout.DockableProperty;
import bibliothek.gui.dock.station.split.Leaf;
import bibliothek.gui.dock.station.split.Node;
import bibliothek.gui.dock.station.split.SplitDockPathProperty;
import bibliothek.gui.dock.station.split.SplitNode;
import bibliothek.gui.dock.station.split.SplitTreeFactory;
import bibliothek.gui.dock.station.split.SplitDockPathProperty.Location;
import bibliothek.gui.dock.station.support.PlaceholderMap;
import bibliothek.util.Path;

public class Dock47 {
	public static void main(String[] args) {
		JFrame frame = new JFrame();
		frame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
		
		CControl control = new CControl(frame);
		frame.add(control.getContentArea());
		
		DefaultSingleCDockable a = new DefaultSingleCDockable( "a", "Aaaaa" );
		DefaultSingleCDockable b = new DefaultSingleCDockable( "b", "Bbbbb" );
		DefaultSingleCDockable c = new DefaultSingleCDockable( "c", "Ccccc" );
		DefaultSingleCDockable d = new DefaultSingleCDockable( "d", "Ddddd" );
		
		CGrid grid = new CGrid( control );
		grid.add( 0, 0, 1, 1, a );
		grid.add( 0, 1, 1, 1, b );
		grid.add( 1, 0, 1, 1, c );
		grid.add( 1, 1, 1, 1, d );
		control.getContentArea().deploy( grid );
		
		c.setVisible( false );
		d.setVisible( false );
		
		System.out.println( control.getContentArea().getCenter().getDockablePathProperty( a.intern() ));
		System.out.println( get( control.getContentArea().getCenter(), a.intern(), true ));
		System.out.println( get( control.getContentArea().getCenter(), a.intern(), false ));
		
		frame.setBounds( 20, 20, 400, 400 );
		frame.setVisible( true );
	}
	
	private static DockableProperty get( final SplitDockStation station, final Dockable dockable, final boolean placeholders ){
		final SplitDockPathProperty path = new SplitDockPathProperty();
		
		Leaf leaf = station.getRoot().getLeaf( dockable );
		path.setLeafId( leaf.getId() );
		SplitNode child = leaf;
		SplitNode parent = leaf.getParent();
		
		while( parent != null ){
			if( parent instanceof Node){
				Node node = (Node)parent;
				boolean visible;
				if( placeholders ){
					visible = node.isVisible();
				}
				else{
					visible = node.getLeft().isVisible() && node.getRight().isVisible();
				}
				if( visible ){
					if( node.getLeft() == child ){
						if( node.getOrientation() == Orientation.HORIZONTAL ){
							path.insert( Location.LEFT, node.getDivider(), 0, node.getId() );
						}
						else{
							path.insert( Location.TOP, node.getDivider(), 0, node.getId() );
						}
					}
					else{
						if( node.getOrientation() == Orientation.HORIZONTAL ){
							path.insert( Location.RIGHT, 1.0 - node.getDivider(), 0, node.getId() );
						}
						else{
							path.insert( Location.BOTTOM, 1.0 - node.getDivider(), 0, node.getId() );
						}
					}
				}
			}
			child = parent;
			parent = child.getParent();
		}
		
		return path;
	}
}

Hi Beni,

Thanks! That is exactly what I needed!

The reason for not using the frameworks layouting mechanism is as follows:
I am developing a thin client application in java that connects to an engine written in C++.
The thin client application sends the engine messages whenever the user takes some action
(mouseclick, keypress) and the engine handles all the logic behind the user actions (open/hide a form, change the content).
When the user click a button that opens a certain perspective
the engine sends all the data of the forms that are part of the perspective.
The data includes the content of the forms as well as their docking position (SplitDockPathProperty).
When all forms all sent the thin client receives a message to layout them.
As far as I understand, if I wanted to use the frameworks mechanism,
I would need to request opening a form from the thin client (factory), which I cannot do.

I use the following algorithm for layouting dockables basing on their SplitDockPathProperties:

void layout (SplitDockStation split, List<MyDockable> dockables)
{
   split.drop(dockables.get(0));
   layoutRecursive (split, dockables.get(0), dockables.subList(1), 0);
}

void layoutRecursive(SplitDockStation split, MyDockable inserted, List<MyDockable> dockables, int index)
{
  List<MyDockable> sameSide = //from dockables get all that on the indexTh place in the path have the same Node as inserted
  List<MyDockable> others = //dockables - sameSide
  
  // drop the first from the others to create the splitter between the two groups
  split.drop(others.get(0), others.get(0).getSplitDockPath()); 

 layoutRecursive(split, inserted, sameSide, index+1);
 layoutRecursive(split, orhets.get(0), others.subList(1), index+1);
}

The above pseudocode is missing some details like the handling of multiple tabs (StackDockStations).

kind regards,
Maciej Modelski