Save layout

private void loadLastLayout() {
        try {
            this.control.readXML(new File(LocalStore.getLoginUserPath() + "/lastLayout.xml"));
            this.control.load("lastLayout");
            logger.info("????????");
        } catch (FileNotFoundException ex) {
        } catch (Exception ex) {
            logger.warn("????????", ex);
        }
    }

private void saveCurrentLayout() {
        try {
            this.control.save("lastLayout");
            this.control.writeXML(new File(LocalStore.getLoginUserPath() + "/lastLayout.xml"));
        } catch (Exception ex) {
            logger.warn("????????", ex);
        }
    }

saveCurrentLayout() is called when the program exits.
loadLastLayout() is called when the program starts.

When the load finished, control.add(CDockable).but not work.
loadLastLayout(),then How do I know what CDockable last saved

You must first call “control.add”, afterwards you can call “loadLastLayout”. The framework does throw away any information for which it does not have a Dockable. If there are no Dockables at all, then all information is thrown away.

but, perspective = dock+layout.Only the layout can not constitute a perspective.

I don’t quite understand what you are trying to say.

When you load a layout the following thing happens:

  1. the file gets read and parsed
  2. any information for which no matching identifier is known gets thrown away
  3. the remaining information is stored

In order to introduce identifiers you either:

  • call CControl.add with all your dockables (easy way)
  • call CControl.addSingleBackupFactory with a factory that creates a dockable (harder way)

call CControl.add with all your dockables (easy way)


Can not create all dockables,Can not afford.
How do I know what a perspective contains dockables?“dockable key” should be saved .

  1. List keys = control.getDockablekeys(String settingName);

  2. loadLayout

  3. create keys dockable

  4. control.add(keys dockable)

Now I do not know the keys.

perspective = dock+layout?

perspective1 ?dock1,dock2,dock3+Layout

perspective2: dock3, dock4, dock5+layout

perspective1 ------>perspective2:

1.save perspective1 (dock+layout)

2.close perspective1(close dock1,dock2,dock3)

3.create perspective2 dock(create dock3,dock4,dock5)

4 add perspective2

Well then with the factories. A factory does cost you about 100 Bytes, so you can have many of them.

  1. add all factories (CControl.addSingleBackupFactory)

  2. load layout

  • the framework will invoke the factories for those Docakbles that will be visible.

After switching a perspective: scan for invisible Dockables, remove them. If a factory remains, then the framework can re-create the Dockable once it is needed.

I generally understand.
but, I do not know all dockable id,many dockables are dynamic created.
I think the control should tell me. Of course I can also save these IDs

public void addDock(CDock d){
    ...............................
    save d.getUniqueId()

}

standard addView method:

 @Override
    public void addViewFunction(ViewFunction view) {
        int wType = view.getWrapperType();
        if (wType == ViewFunction.SystemWrapper) {
            DockingViewWrapper dvw = new DockingViewWrapper(view.getViewTitle(), view);
            dvw.setViewFunction(view);
            control.add(dvw);
            if (selView != null) {
                CLocation cl = selView.getBaseLocation();
                if (cl != null) {
                    if (cl instanceof AbstractStackholdingLocation) {
                        if (cl instanceof CStackLocation) {
                            dvw.setLocation(((CStackLocation) cl).stack());
                        } else {
                            dvw.setLocation(((AbstractStackholdingLocation) cl).stack(0));
                        }
                    } else {
                        dvw.setLocation(cl.aside());
                    }
                }
            }

            dvw.setVisible(true);
            dvw.setViewActive(true);
            view.initFunction();
        } else {
            mainFrame.addCommonWrapperViewFunction(view, wType);
        }
    }

DockingViewWrapper is extends DefaultSingleCDockable.
ViewFunction is extends JPanel.

view.initFunction(); after dvw.setVisible(true); Because initFunction() may display a progress bar.

Now look SingleCDockableBackupFactory:

class DockingViewFactory implements SingleCDockableBackupFactory {

        @Override
        public SingleCDockable createBackup(String id) {
            FunctionLabel fl = new FunctionLabel();
            fl.fromKey(id);
            Object obj = SystemManager.getSystemManager().createFunction(fl);
            if (obj != null && obj instanceof ViewFunction) {
                ViewFunction vf = (ViewFunction) obj;
                DockingViewWrapper dvw = new DockingViewWrapper(vf.getViewTitle(), vf);
                dvw.setViewFunction(vf);
                vf.initFunction();
                return dvw;
            }
            return null;
        }
    }

vf.initFunction(); , but DockingViewWrapper not Visible.
In addition,DockingViewWrapper title not shown.

public void addViewFunction(ViewFunction view) Has been working well.

The user clicks the menu item will eventually lead to addViewFunction.

Communicate with the server because,Some ViewFunction a longer initialization time.display the progress bar.

Now consider the support perspective, but it is difficult.

I’ll try to write a working example using your code similar to yours. Just to make sure, the factory is called? The id is correct? The “obj” is not null?

yes?actory called ,id is correct,The “obj” is not null.

but,view show ,then initFunction(),
In addition, the title has not been shown

What happens if you let the code below run? It works on my machine, and I think that is exactly what you intend to create?


import java.awt.Color;
import java.awt.Dimension;
import java.awt.GridLayout;
import java.io.IOException;

import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JPanel;

import bibliothek.gui.dock.common.CControl;
import bibliothek.gui.dock.common.CGrid;
import bibliothek.gui.dock.common.DefaultSingleCDockable;
import bibliothek.gui.dock.common.SingleCDockable;
import bibliothek.gui.dock.common.SingleCDockableBackupFactory;
import bibliothek.util.xml.XElement;
import bibliothek.util.xml.XIO;

public class Dock18 {
    public static void main(String[] args) throws IOException {
        // set up frame and control
        JFrame frame = new JFrame();
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setLayout(new GridLayout(1, 1));

        CControl control = new CControl(frame);
        
        frame.add(control.getContentArea());
        frame.setSize(new Dimension(700, 700));
        frame.setLocationRelativeTo(null);

        // register factories
        control.addSingleBackupFactory( "red", new ViewFunctionFactory(new ColorViewFunction( "red", Color.RED )));
        control.addSingleBackupFactory( "green", new ViewFunctionFactory(new ColorViewFunction( "green", Color.GREEN )));
        control.addSingleBackupFactory( "blue", new ViewFunctionFactory(new ColorViewFunction( "blue", Color.BLUE )));
        control.addSingleBackupFactory( "magenta", new ViewFunctionFactory(new ColorViewFunction( "magenta", Color.MAGENTA )));
        
        // load layout
        XElement root = XIO.read( createTestLayoutData() );
        control.readXML( root );
        
        frame.setVisible(true);
    }

    // create a layout (yeah, not very nice. But currently there is no other way)
    private static String createTestLayoutData(){
        CControl control = new CControl();
        
        CGrid grid = new CGrid( control );
        grid.add(  0, 0, 50, 100, wrap( new ColorViewFunction( "red", Color.RED )));
        grid.add( 50, 0, 50,  50, wrap( new ColorViewFunction( "green", Color.GREEN )));
        grid.add( 50, 50, 50,  50, wrap( new ColorViewFunction( "blue", Color.BLUE )));
        
        control.getContentArea().deploy( grid );
        
        XElement root = new XElement( "root" );
        control.writeXML( root );
        return root.toString();
    }
    
    // ViewFunction -> SingleCDockable
    public static SingleCDockable wrap( ViewFunction function ){
        return new DefaultSingleCDockable( function.getTitle(), function.getTitle(), function.getContent() );
    }
    
    // Out backup-factory
    private static class ViewFunctionFactory implements SingleCDockableBackupFactory {
        private ViewFunction function;
        
        public ViewFunctionFactory( ViewFunction function ){
            this.function = function;
        }
        
        public SingleCDockable createBackup( String id ){
            System.out.println( "using factory for: " + id );
            return wrap( function );
        }
    }
    
    // your view-function (simplified version)
    private static interface ViewFunction{
        public String getTitle();
        public JComponent getContent();
    }
    
    // finally an implementation of a view-function (also simplified)
    private static class ColorViewFunction implements ViewFunction{
        private String title;
        private Color color;

        public ColorViewFunction( String title, Color color ){
            this.title = title;
            this.color = color;
        }
        
        public String getTitle(){
            return title;
        }

        public JComponent getContent(){
            JPanel panel = new JPanel();
            panel.setOpaque(true);
            panel.setBackground(color);
            return panel;
        }
    }
}

yep; works here;

http://code.google.com/p/docking-frames/source/browse/demo/trunk/docking-frames-examples/src/main/java/org/dockingframes/example/persist_by_client/Main_3047.java

this is actually very needed pattern; probably needs mentioning in the guides;

or expand api to make it more easy: since currently


SingleCDockableBackupFactory.createBackup(String id)
MultipleCDockableFactory.create()

do not allow for extra “context” injection

The problem is
ColorViewFunction can not be pre-constructed

// Out backup-factory
    private static class ViewFunctionFactory implements SingleCDockableBackupFactory {
        private ViewFunction function;
        
        public ViewFunctionFactory( ViewFunction function ){
            this.function = function;
        }
        
        public SingleCDockable createBackup( String id ){
            System.out.println( "using factory for: " + id );
            return wrap( function );
        }
    }

System has many ViewFunctions,ViewFunction also called initFunction ();Time-consuming is ViewFunction,not the wrapper.

so,

// register factories
ViewFunctionFactory vff = new ViewFunctionFactory();
control.addSingleBackupFactory( "red", vff);
control.addSingleBackupFactory( "green", vff));
control.addSingleBackupFactory( "blue", vff);
control.addSingleBackupFactory( "magenta", vff);

only one Factory.
so,

public SingleCDockable createBackup( String id ){
          // id------------>viewFunction(can if...else...,or Reflection(id contains classname))
          //then wrap
            return wrap( function );
}

but,When calling initFunction ()?wrapper.setVisible(true),then viewFunction.initFunction().
I tried this:

// load layout
XElement root = XIO.read( createTestLayoutData() );
control.readXML( root );
for(...){
     if(dockwrapper.isVisible()){
          dockwrapper.initFunction();
      }
}

but,Can not completely work.

sorry,dockwrapper.initFunction(); should be dockwrapper.getViewFunction().initFunction();

Well, but then it sounds as if the initFunction has some issue, not the DF library. Could you create a small, compiling and running example so I could test it on my machine too?

@Andrei: The design of the framework expects a developer to know all Single-Dockables he will ever use. Multi-Dockables are more dynamic. But in general the framework is not yet designed for highly dynamic content.

@HHH: An additional suggestion: use MultipleCDockable instead of SingleCDockable. As you can see in the example below, this way you would not need to know all the ViewFunctions in advance. In fact the framework would tell you, which ViewFunctions it requires to create the layout. Of course you need some way to get from an identifier to the ViewFunction.


import java.awt.Color;
import java.awt.Dimension;
import java.awt.GridLayout;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;

import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JPanel;

import bibliothek.gui.dock.common.CControl;
import bibliothek.gui.dock.common.CGrid;
import bibliothek.gui.dock.common.DefaultMultipleCDockable;
import bibliothek.gui.dock.common.MultipleCDockableFactory;
import bibliothek.gui.dock.common.MultipleCDockableLayout;
import bibliothek.util.xml.XElement;
import bibliothek.util.xml.XIO;

public class Dock18 {
    public static void main(String[] args) throws IOException {
        // set up frame and control
        JFrame frame = new JFrame();
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setLayout(new GridLayout(1, 1));

        CControl control = new CControl(frame);
        
        frame.add(control.getContentArea());
        frame.setSize(new Dimension(700, 700));
        frame.setLocationRelativeTo(null);

        // register factory
        ViewFunctionFactory factory = new ViewFunctionFactory();
        control.addMultipleDockableFactory( "view", factory );
        
        // load layout
        XElement root = XIO.read( createTestLayoutData() );
        control.readXML( root );
        
        frame.setVisible(true);
    }

    // create a layout (yeah, not very nice. But currently there is no other way)
    private static String createTestLayoutData(){
        CControl control = new CControl();
        ViewFunctionFactory factory = new ViewFunctionFactory();
        control.addMultipleDockableFactory( "view", factory );
        
        CGrid grid = new CGrid( control );
        grid.add(  0, 0, 50, 100, new ViewFunctionDockable( factory, createFunctionFor( "red" ) ));
        grid.add( 50, 0, 50,  50, new ViewFunctionDockable( factory, createFunctionFor( "green" ) ));
        grid.add( 50, 50, 50,  50, new ViewFunctionDockable( factory, createFunctionFor( "blue" ) ));
        
        control.getContentArea().deploy( grid );
        
        XElement root = new XElement( "root" );
        control.writeXML( root );
        return root.toString();
    }
    
    // A method that works some magic and can translate an "id" to a view-function 
    public static ViewFunction createFunctionFor( String id ){
        if( "red".equals( id )){
            return new ColorViewFunction( id, Color.RED );
        } else if( "green".equals( id )){
            return new ColorViewFunction( id, Color.GREEN );
        } else if( "blue".equals( id )){
            return new ColorViewFunction( id, Color.BLUE );
        } else if( "yellow".equals( id )){
            return new ColorViewFunction( id, Color.YELLOW );
        } else {
            return null;
        }
    }
   
    
    // A factory
    private static class ViewFunctionFactory implements MultipleCDockableFactory<ViewFunctionDockable, ViewFunctionLayout> {
        public ViewFunctionLayout create(){
            return new ViewFunctionLayout();
        }

        public boolean match( ViewFunctionDockable dockable, ViewFunctionLayout layout ){
            return layout.getId().equals( dockable.getFunction().getTitle() );
        }

        public ViewFunctionDockable read( ViewFunctionLayout layout ){
            ViewFunction function = createFunctionFor( layout.getId() );
            if( function == null ){
                return null;
            }
            return new ViewFunctionDockable( this, function );
        }

        public ViewFunctionLayout write( ViewFunctionDockable dockable ){
            ViewFunctionLayout layout = new ViewFunctionLayout();
            layout.setId( dockable.getFunction().getTitle() );
            return layout;
        }
        
    }
    
    // your view-function (simplified version)
    private static interface ViewFunction{
        public String getTitle();
        public JComponent getContent();
    }
    
    // this layout describes the contents of a Dockable that shows a view-function
    private static class ViewFunctionLayout implements MultipleCDockableLayout{
        private String id;
        
        public void setId( String id ){
            this.id = id;
        }
        
        public String getId(){
            return id;
        }
        
        public void readStream( DataInputStream in ) throws IOException{
            id = in.readUTF();    
        }

        public void readXML( XElement element ){
            id = element.getString();
        }

        public void writeStream( DataOutputStream out ) throws IOException{
            out.writeUTF( id );
        }

        public void writeXML( XElement element ){
            element.setString( id );
        }
    }
    
    // this dockable shows a view function
    private static class ViewFunctionDockable extends DefaultMultipleCDockable{
        private ViewFunction function;
        
        public ViewFunctionDockable( ViewFunctionFactory factory, ViewFunction function ){
            super( factory, function.getTitle(), function.getContent() );
            this.function = function;
            setRemoveOnClose( false );
        }
        
        public ViewFunction getFunction(){
            return function;
        }
    }
    
    // finally an implementation of a view-function (also simplified)
    private static class ColorViewFunction implements ViewFunction{
        private String title;
        private Color color;

        public ColorViewFunction( String title, Color color ){
            this.title = title;
            this.color = color;
        }
        
        public String getTitle(){
            return title;
        }

        public JComponent getContent(){
            JPanel panel = new JPanel();
            panel.setOpaque(true);
            panel.setBackground(color);
            return panel;
        }
    }
}```

Beni:

@Andrei: The design of the framework expects a developer to know all Single-Dockables he will ever use. Multi-Dockables are more dynamic. But in general the framework is not yet designed for highly dynamic content.

got it; my use case is dynamic; I’ll see what I can do with Multi-Dockables;

Andrei