Predefined locations using perspectives

Hi Beni,
I am trying to use DockingFrames for one of our projects. First of all congratulations for your work. Especially you have a good documentation and good examples (often a missing feature in open source projects).

But despite the documentation I can not get one desired behaviour work.

I have some SingleCDockables and want to layout out them using CPerspective (CGridPerspective) in Common. With a menu I can switch the visibility of these dockables. I want some dockables by default not be visible, but have their default location (if they are made visible). This default location should be defined in CPerspective during the initialization.

Is this possible with the framework? I tried to use placeholders, but I alwas got an NullPointerException. I thought probably I have to write my own MissingDockableStrategy.

But I do not know exactly what to do now?

Thanks in advance

You have found a missing feature, and a bug. I’ve updated the framework, the current version is 1.1.1p8b. There is now a special method “remove” with which you can remove dockables from the perspective. Basically you build a layout with all dockables, them remove those which should be invisible. Like in the example below.

To be honest, it was possible to make the dockables invisible, but it required very nasty code.

What was the NullPointerException? You might get one if you used the Toolbar Extension (the exception is now converted into a warning, there is still work to do).


import javax.swing.JFrame;
import javax.swing.JMenuBar;

import bibliothek.gui.dock.common.CControl;
import bibliothek.gui.dock.common.DefaultSingleCDockable;
import bibliothek.gui.dock.common.menu.SingleCDockableListMenuPiece;
import bibliothek.gui.dock.common.perspective.CControlPerspective;
import bibliothek.gui.dock.common.perspective.CGridPerspective;
import bibliothek.gui.dock.common.perspective.CPerspective;
import bibliothek.gui.dock.common.perspective.SingleCDockablePerspective;
import bibliothek.gui.dock.facile.menu.RootMenuPiece;

public class Dock27 {
	public static void main( String[] args ){
		/* A frame and control... */
		JFrame frame = new JFrame( "Test" );
		frame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
		frame.setBounds( 20, 20, 300, 300 );

		CControl control = new CControl( frame );
		frame.add( control.getContentArea() );
		
		/* We just add the dockables right now, so we don't have to care about automatic removal of placeholders */
		control.addDockable( create( "a", "A" ) );
		control.addDockable( create( "b", "B" ) );
		control.addDockable( create( "c", "C" ) );
		control.addDockable( create( "d", "D" ) );
		
		/* Start building a perspective... */
		CControlPerspective controlPerspective = control.getPerspectives();
		CPerspective layout = controlPerspective.createEmptyPerspective();
		
		SingleCDockablePerspective dockableA = new SingleCDockablePerspective( "a" );
		SingleCDockablePerspective dockableB = new SingleCDockablePerspective( "b" );
		SingleCDockablePerspective dockableC = new SingleCDockablePerspective( "c" );
		SingleCDockablePerspective dockableD = new SingleCDockablePerspective( "d" );
		
		/* Adding the dockables at their place */
		CGridPerspective center = layout.getContentArea().getCenter();
		center.gridAdd( 0, 0, 1, 1, dockableA );
		center.gridAdd( 0, 1, 1, 1, dockableB );
		center.gridAdd( 1, 0, 1, 1, dockableC);
		center.gridAdd( 1, 1, 1, 1, dockableD );
		center.gridDeploy();
		
		/* Now we remove the dockables we do not want to show up */
		dockableA.remove();
		dockableB.remove();
		dockableC.remove();
		dockableD.remove();
		
		/* Cleaning up the layout */
		layout.shrink();
		
		/* Applying the layout */
		controlPerspective.setPerspective( layout, true );
		
		/* And we add a menu that allows us to make the invisible dockables visible again */
		JMenuBar menuBar = new JMenuBar();
		RootMenuPiece menu = new RootMenuPiece( "Panels", false, new SingleCDockableListMenuPiece( control ) );
		menuBar.add( menu.getMenu() );
		frame.setJMenuBar( menuBar );
		frame.setVisible( true );
	}
	
	private static DefaultSingleCDockable create( String id, String title ){
		DefaultSingleCDockable dockable = new DefaultSingleCDockable( id, title );
		dockable.setCloseable( true );
		return dockable;
	}
}

Hi Beni,
thanks for your quick response and updating of the code.
The NullPointerException I get when I use something like this (Line 46).

package com.test;

import javax.swing.JFrame;
import javax.swing.JMenuBar;
 
import bibliothek.gui.dock.common.CControl;
import bibliothek.gui.dock.common.DefaultSingleCDockable;
import bibliothek.gui.dock.common.menu.SingleCDockableListMenuPiece;
import bibliothek.gui.dock.common.perspective.CControlPerspective;
import bibliothek.gui.dock.common.perspective.CGridPerspective;
import bibliothek.gui.dock.common.perspective.CPerspective;
import bibliothek.gui.dock.common.perspective.SingleCDockablePerspective;
import bibliothek.gui.dock.facile.menu.RootMenuPiece;
 
public class Dock27 {
    public static void main( String[] args ){
        /* A frame and control... */
        JFrame frame = new JFrame( "Test" );
        frame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
        frame.setBounds( 20, 20, 300, 300 );
 
        CControl control = new CControl( frame );
        frame.add( control.getContentArea() );
        
        /* We just add the dockables right now, so we don't have to care about automatic removal of placeholders */
        control.addDockable( create( "a", "A" ) );
        control.addDockable( create( "b", "B" ) );
        control.addDockable( create( "c", "C" ) );
        control.addDockable( create( "d", "D" ) );
        
        /* Start building a perspective... */
        CControlPerspective controlPerspective = control.getPerspectives();
        CPerspective layout = controlPerspective.createEmptyPerspective();
        
        SingleCDockablePerspective dockableA = new SingleCDockablePerspective( "a" );
        SingleCDockablePerspective dockableB = new SingleCDockablePerspective( "b" );
        SingleCDockablePerspective dockableC = new SingleCDockablePerspective( "c" );
        SingleCDockablePerspective dockableD = new SingleCDockablePerspective( "d" );
        
        /* Adding the dockables at their place */
        CGridPerspective center = layout.getContentArea().getCenter();
        center.gridAdd( 0, 0, 1, 1, dockableA );
        center.gridAdd( 0, 1, 1, 1, dockableB );
        center.gridAdd( 1, 0, 1, 1, dockableC);
//        center.gridAdd( 1, 1, 1, 1, dockableD );
        center.gridPlaceholder( 1, 1, 1, 1, dockableD );
        center.gridDeploy();
        
        /* Now we remove the dockables we do not want to show up */
//        dockableA.remove();
//        dockableB.remove();
//        dockableC.remove();
//        dockableD.remove();
        
        /* Cleaning up the layout */
//        layout.shrink();
        
        /* Applying the layout */
        controlPerspective.setPerspective( layout, true );
        
        /* And we add a menu that allows us to make the invisible dockables visible again */
        JMenuBar menuBar = new JMenuBar();
        RootMenuPiece menu = new RootMenuPiece( "Panels", false, new SingleCDockableListMenuPiece( control ) );
        menuBar.add( menu.getMenu() );
        frame.setJMenuBar( menuBar );
        frame.setVisible( true );
    }
    
    private static DefaultSingleCDockable create( String id, String title ){
        DefaultSingleCDockable dockable = new DefaultSingleCDockable( id, title );
        dockable.setCloseable( true );
        return dockable;
    }
}

I still do not know why this is not allowed. I do not know how to use Placeholders in CPerspective.

I tried to use the 1.1.1p8b, but I got a NullPointerException, when I called CControlPerspective#getPerspective(String name). Unfortunately I can not reproduce this error in a simple example. But I will paste the error log:

Caused by: java.lang.NullPointerException
	at bibliothek.gui.dock.common.intern.station.CommonDockStationFactory.layoutPerspective(CommonDockStationFactory.java:217)
	at bibliothek.gui.dock.common.intern.station.CommonDockStationFactory.layoutPerspective(CommonDockStationFactory.java:65)
	at bibliothek.gui.dock.frontend.RegisteringDockFactory.layoutPerspective(RegisteringDockFactory.java:139)
	at bibliothek.gui.dock.frontend.RegisteringDockFactory.layoutPerspective(RegisteringDockFactory.java:51)
	at bibliothek.gui.dock.layout.PredefinedDockSituation$PreloadFactory.layoutPerspective(PredefinedDockSituation.java:740)
	at bibliothek.gui.dock.layout.PredefinedDockSituation$PreloadFactory.layoutPerspective(PredefinedDockSituation.java:567)
	at bibliothek.gui.dock.perspective.Perspective.convert(Perspective.java:255)
	at bibliothek.gui.dock.common.perspective.CControlPerspective.convert(CControlPerspective.java:456)
	at bibliothek.gui.dock.common.perspective.CControlPerspective.getPerspective(CControlPerspective.java:136)
	at com.mevisco.mpc.statisticsviewer.StatisticsViewer.initPerspectives(StatisticsViewer.java:296)
	at com.mevisco.application.gui.swing.DockableFrameApplication.initGUI(DockableFrameApplication.java:76)
	at com.mevisco.mpc.statisticsviewer.StatisticsViewer.initGUI(StatisticsViewer.java:207)
	at com.mevisco.application.Application$2.run(Application.java:598)
	at java.awt.event.InvocationEvent.dispatch(InvocationEvent.java:199)
	at java.awt.EventQueue.dispatchEvent(EventQueue.java:597)
	at java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:269)
	at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:184)
	at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:174)
	at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:169)
	at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:161)
	at java.awt.EventDispatchThread.run(EventDispatchThread.java:122)

Another problem, is the menu. I implemented a available docks menu myself using CControlListener, but I think the invisiable dock is not added to the CDockCOntrol so it does not appear in the menu. If I try to use SingleCDockableListMenuPiece in my apllication it is totally empty. Probably I initialize the menu before the layout is set up.

I do not stick to CPerspective concept. What I liked was the factory concept. But at the moment our application is small, so it will not be the problem not to use factories for now. But this could change and I want to find a future proof concept.

Do you have any better suggestions. How to set this up? We have some docks, that are known at compile time (this is what I implement know). Some docks will be only known at runtime, depending on the configuration. The configuration could change during the runtime. But it will rarely. Should I use for this purpose Single or MultiCDockables? All the docks will contain some life statistics. So the user does not have a real editor. It is just visualization.

About how to use placeholders.
Basicially you do it right, calling „gridPlaceholder“ will insert a placeholder for the dockable. You would also need to modify the history of the dockable (dockable.getHistory().add( „the location“ )), so it knows it has a placeholder. But I think I do not go into the details, because the new remove method does add the placeholder and modify the history in one step, so you won’t need to modify placeholders anymore.

The exception.
No clue, I did not find a way to reproduce it. I’ll continue to search, but without a reliable way to cause the exception…

The menu.
Invisible dockables are not added, that is correct. Even if the framework knows the factories, it cannot help you, because it is lacking basic information like for example the icon of the dockable. There really is no other option than to write a custom menu for your case, and add all information of dockables „manually“. At least you could use „CControl.getSingleDockableFactory“ to help you not duplicate the code for initializing the dockables. After the application started the CControlListener should inform you about any changes in the list of dockables, so updating the menu at a later time is still an option.

Set up.
Your setup does look well enough, if I could find the cause of the exception it would probably work just fine. The dockables you know at compile time really should be SingleCDockables, it’s open for debate what the other dockables should be. Some ideas that could help you decide:

  • Do you need to store information about these dockables that appear during runtime? For example what they show? If yes: use MultipleCDockable, because of the factory that can store data.
  • Are these dockables that appear during runtime always of the same type? If yes: MultipleCDockable, if no: probably SingleCDockable.
  • Should these dockables re-appear on the next application start? If you use MultipleCDockable the factory could help you, when using SingleCDockables you can have a factory which is associated with a „Filter“ (see CControl.addSingleDockableFactory), such a factory can handle SingleCDockables with different identifiers.

You should know that perspectives are still a new feature, there are so many ways to combine these classes, to call methods in different order… I simply will never be able to test all possibilities. So, you will find bugs, but if you tell me, I’ll fix them (if possible) :wink: But perspectives are still the best way to efficiently (in regards of memory and CPU time) build a layout with many and/or invisible dockables.

Hi Beni,
thanks for your help. I will at first try to set up the missing docks. I think for me it is best to use SingleDockables and create an own menu which will create the docks on demand.

Then I will try to find out where the Exception comes from.

If I need any support, I will contact you again.

[QUOTE=Hami]Then I will try to find out where the Exception comes from.

If I need any support, I will contact you again.[/QUOTE]
Ok. If you don’t find a „simple example“ that reproduces the bug, don’t hesitate to send me a big example. I’m able to search through a few 100 classes to find a bug. Actually, if you find any kind of example, just send it to me :wink:

Hi Beni,
it seems to work now. I can not reproduce the error myself. But I ran into another problem. I will post it in different thread, because it is another topic.