For the most part this works perfectly. However, once in a while i get an erroneous dockable in my layout. I have noticed that when this happens i get the following in my XML file:
If i remove those “empty” child nodes, then the erroneous dockables go away. Can someone tell me how to avoid getting these? I have not yet discovered a repeatable way to get them. And if not, any suggestion on how to remove them programmatically would be appreciated. I can always remove them from the XML programmatically just before reading in the file, but that just seems ugly.
CControl already has a method „readXML“, no need to call „getResources“
I have to think about the empty Dockable. But you might want to check, that no „new DefaultDockableFactory“ is written anywhere in your code. I’ll write again once I did some testing for myself.
A factory whose name starts with “delegate_” is a “backup factory”, they are mostly generated automatically. “DefaultDockableFactory” especially is one of the default backup factories that are always present.
Properly wired CDockables are not linked to the DefaultDockableFactory. However when they are not registered at a CControl they might present a link to the DefaultDockableFactory. You could find out if this happens by setting a breakpoint in “DefaultMultipleCDockable.setControlAccess”. If this method is called with “null” as an argument, does the layout get corrupted afterwards?
Maybe it is a multi threading issue? Can you be certain that any reading or writing of the layout (and all other interactions with the framework) is only done in the EDT (the UI-thread)?
Maybe you have some piece of code that stores the layout while a Dockable is modified? This could happen if you have a listener like a “CControlListener” that stores the layout. While the listener is executed the framework might be in a state that is not 100% legal.
For now these are all the ideas I have. Let me know if anything helps (or not). Knowing more about your application would also be nice, because right now I cannot reproduce this bug.
I have set a breakpoint in DefaultMultipleCDockable. If i catch something i will let you know. I have a feeling that it might have to do with an exception getting thrown during my “creation” cycle. Meaning that a panel is being setup and before it gets added to the CControl an exception happens. At this point the dockable would still exist but would not be properly registered. So if that happens, what is the correct course of action. Is there some sort of “dispose” method i should be calling against it?
On another note, assuming i am not able to find the culprit, can you think of any good way to find these things after i load my layout? So that i can somehow kill them before the UI gets stood up? When i look in the CControl with the debugger i don’t see any sign of them.
One of the methods of „CControl“ like „removeDockable“ would be the closest thing to a dispose method.
Not having exception would probably be good too
Could you please send me an entire xml file of a corrupted layout? It might give me more ideas on what could be going wrong (don’t get your hopes up).
As for fixing a layout. Just updating the xml is fine for me, but DockingFrames offers some methods to read and modify the file too. The example below only works with version 1.1.2_19d, which I uploaded a few minutes ago. (Yes, I did find a nasty bug when trying to write the example).
The application makes use of the „perspective“ API, which is just a wrapper around the content read from the XML file.
public static void main( String[] args ) throws IOException {
// buildFile();
EventQueue.invokeLater( () -> {
try {
// setup the CControl and other stuff
JFrame frame = new JFrame();
CControl control = new CControl( frame );
control.setMissingStrategy( MissingCDockableStrategy.STORE );
frame.add( control.getContentArea() );
DefaultSingleCDockable da = new DefaultSingleCDockable( "Aaa" );
control.addDockable( da );
DefaultSingleCDockable db = new DefaultSingleCDockable( "Bbb" );
control.addDockable( db );
DefaultSingleCDockable dc = new DefaultSingleCDockable( "Ccc" );
control.addDockable( dc );
// we read a corrupted layout from "test.xml" and write the cleaned version back to "out.xml"
try( FileReader in = new FileReader( new File( "test.xml" ) );
FileWriter out = new FileWriter( new File( "out.xml" ) ) ) {
XElement input = XIO.read( in );
// loading the data without affecting the CControl.
CControlPerspectiveBlop perspectives = control.getPerspectives().readAllXML( input );
// represents the layout that was shown when the file was stored
CPerspective perspective = perspectives.getPerspective();
// iterate over all elements, find the corrupted entries
Iterator<PerspectiveElement> elements = perspective.elements();
while( elements.hasNext() ) {
PerspectiveElement next = elements.next();
// all instances of DefaultDockablePerspective are corrupted entries
if( next instanceof DefaultDockablePerspective ) {
// removing them from their parent will delete them from the layout
DefaultDockablePerspective evil = (DefaultDockablePerspective) next;
evil.getParent().remove( evil );
}
}
// apply the fixed perspective
perspectives.setPerspective( perspective );
// write the settings back to some xml file
XElement output = new XElement( input.getName() );
perspectives.writeXML( output );
XIO.write( output, out );
}
frame.setBounds( 20, 20, 800, 800 );
frame.setVisible( true );
control.readXML( new File( "out.xml" ) );
// control.readXML( new File( "test.xml" ) );
} catch( IOException e ) {
throw new RuntimeException( e );
}
} );
}
Did not help much. Well it tells that the empty Dockable was properly added to a parent station - but that does not help at all.
I was not able to reproduce the issue, but I have an idea how we could find more information. We catch the offending Dockable that causes all the issues by stopping the application at the moment when the DefaultDockableFactory is accessed.
There are two places where we might find that object:
Add a breakpoint to DefaultDockableFactory.getLayout, it should get the offender as the first argument.
– or –
Add a breakpoint to “DockSituation.getFactory”. Make it a conditional breakpoint, that stops if the “id” contains “DefaultDockableFactory”
If the breakpoint is hit, head up the stacktrace until you find the “DockElement” from which the id was read. Usually that’s only one or two steps.
What kind of Dockable is that “DockElement”. Is it really a DefaultDockable? Is it a subclass? Is it one of yours? Is it a “CommonDockable” and does it point to a an “AbstractCDockable” with the field “control” set?