# Lesson 4 : VLDocking Workspaces
This is the 4th part of the VLDocking Framework tutorial for Java Swing applications.
In this lesson, you will learn to load and save desktop configurations, also
known as _workspaces_.
## Workspaces
Workspaces are getting more common in Java applications, they are called "perspectives" in eclipse,
workbenches in other applications, but we'll stick to workspace for the Docking Framework.
A full description of a workspace in our case could be "a set of Dockables,
at a given location and size, on a given visibility state".
* A set of dockables : usually the full set of the application's dockables.
* At a given location and size : location is determined by the containment
hierarchy of the DockingDesktop, size is taken from SplitContainers divider positions.
* On a given visibility state : this includes auto-hidden and closed dockables.
This is an example of two workpaces of the same application :
![](workspace1.jpg)
_Workspace 1_
![](workspace2.jpg)
_Workspace 2_
*Note* : as of version 2.0 of VLDocking, a new set of components has been
added : the *VLToolBars*. These components
have their own methods for saving and reloading (as they may be used from outside the framework).
VLToolbars will be explained later, in their own [tutorial](tutorial9.md).
## The Workspace Editor application
_This is a new feature from VLDocking 2.0.5_
This new application can be started from here and can be used to
define or update workspace files with a GUI editor.
The user guide is currently limited to the start page of the application, but should be enough to help you define a set of
workspaces in a matter of minutes.
Once your workspace is defined and saved as XML, go to the Loading a workspace section of this page to
learn how to integrate it into your application.
## Saving a workspace
To save a workspace, use the `writeXML(OutputStream out)` of the
`DockingDesktop` object.
Here is an example of saving to a file :
```java
import java.io.*;
DockingDesktop desk = ...;
File saveFile = ...;
// here we go !
try {
BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream(saveFile)));
desk.writeXML(out);
out.close(); // stream isn't closed in case you'd like to save something else after
} catch (IOException ioe){
// process exception here
}
```
*Note* : Of course, you can save your workspace into something else (a byte
array, for example, via the `ByteArrayOutputStream` output);
Let's have a look at the resulting file (indented for readability) :
```xml
```
As you can see, it's a very simple XML structure, with nested Split (and Tab) elements.
## Loading a workspace from an XML stream
Loading a workspace is as easy as saving it, but requires another step if you
use this feature to populate an empty desktop : you have to `registerDockable(Dockable d)`
all your dockables before calling `readXML(InputStream in)`.
Loading from an XML File to populate an empty DockingDesktop :
```java
import java.io.*;
DockingDesktop desk = ...;
File loadFile = ...;
// here we go !
try {
// first : declare the dockables to the desktop (they will be in the "closed" dockable state).
desk.registerDockable(editorPanel);
desk.registerDockable(tablePanel);
desk.registerDockable(buttonGrid);
desk.registerDockable(treePanel);
BufferedInputStream in = new BufferedInputStream(new FileInputStream(loadFile)));
// then, load the workspace
desk.readXML(in);
in.close(); // stream isn't closed
} catch (IOException ioe){
// process exception here
}
```
If your desktop is already populated, you can skip the `registerDockable` step
(assuming the dockable keys of the XML file are the same than those of the desktop's dockables).
## Lazy Dockable registration : DockableResolver
Introduced with VLDocking 2.1.2, the `DockableResolver` interface is a way to register
dockable as lazily as possible (with this interface, they will be registered during the readXML() parsing).
The interface contains a single method :
```java
public interface DockableResolver {
/**
* Returns the dockable which should be associated to this DockKey identifier,
* or null if not found.
*/
public Dockable resolveDockable(String keyName);
}
```
To use this interface, you just have to give it to a DockingContext before applying a
workspace :
```java
DockingContext ctx = ...;
DockableResolver resolver = new DockableResolver() {
public Dockable resolveDockable(String keyName) {
if (keyName.equals("dockable1")) {
// instanciate dockable1
return dockable1;
} else if (keyName.equals("dockable2")) {
// instanciate dockable2
return dockable2;
} else {
return null; // for unknown dockable keys
}
}
};
ctx.setDockableResolver(resolver);
// now, there's no need to call registerDockablefirst
ctx.readXML(aWorkspaceInputStream);
```
## The com.vlsolutions.swing.docking.ws package : logical Workspace management
This new package has been introduced with VLDocking 2.1.2, it contains an API to manage workspace configurations. This
API will certainly be enhanced in future releases.
### Workspace, WSDesktop and WSDockKey
A VLDocking-enabled application can be made of multiple Desktops (on single or multiple JFrames), with dockables
transferable from a desktop to another (this is possible with the new concept or `DockingContext`).
So, the equivalent of `DockingContext` is the `Workspace` class.
```java
DockingContext ctx = ...;
Workspace workspace1 = new Workspace();
// define the workspace layout here
...
// layout defined
// later...
workspace1.apply(ctx);
// workspace applied to the desktops of this docking context
```
A WSDesktop is the equivalent of a DockingDesktop : it is a child of a Workspace (one per desktop) and
contains layout methods with the same name than the ones of DockingDesktop : split(), createTab(), etc.
The API beeing similar, it's very easy to understand : defining a layout into a WSDesktop is like creating
a layout in DockingDesktop, but that layout can be reused as often as you want with a simple `apply()`
invocation.
As workspace should be very light objects, they must be decoupled from desktop classes.
As Dockkeys are often lazily created (at the same time than Dockable objets), its equivalent has been
defined, and is called `WSDockKey`. Like DockKeys, a WSDockKey is used to
identify dockables without the need to instanciate them.
```java
DockingContext ctx = ...;
Workspace workspace1 = new Workspace();
WSDesktop wdesk = workspace1.getDesktop(0); // a single default WSDesktop object is provided
// the three dockables used by this workspace/desktop
WSDockKey key1 = new WSDockKey("dockable1");
WSDockKey key2 = new WSDockKey("dockable2");
WSDockKey key3 = new WSDockKey("dockable3");
wdesk.addDockable(key1); // similar API, but uses WSDockKey as arguments
wdesk.split(key1, key2, DockingConstants.SPLIT_LEFT, 0.4f);
wdesk.createTab(key2, key3, 1);
// layout has been defined
// later...
workspace1.apply(ctx);
// workspace applied to the desktops of this docking context
// registered dockables must have the same DockKey
// identifiers ( DockKey k1 = new DockKey("dockable1") )
```
Note : the `DockableResolver` inteface defined here works also
with Workspaces, so both registering methods work :
```java
DockingContext ctx = ...;
Workspace workspace1 = new Workspace();
WSDesktop wdesk = workspace1.getDesktop(0);
// define keys and layout
// later...
ctx.registerDockable(dockable1);
ctx.registerDockable(dockable2);
ctx.registerDockable(dockable3);
workspace1.apply(ctx);
```
Or :
```java
DockingContext ctx = null;
Workspace workspace1 = new Workspace();
WSDesktop wdesk = workspace1.getDesktop(0);
// define keys and layout
// later...
ctx.setDockableResolver(resolver); // lazy dockable resolution
workspace1.apply(ctx);
```
## Implementing a workspace switcher (updated for VLDocking 2.1.3)
Implementing a workspace switcher with the Workspace objets is really straightforward :
you just have to invoke `workspace.loadFrom(dockingContext)` to save a layout into a workspace, and then
`workspace.apply(dockingContext)` to get back to this layout later.
## Notes about workspaces and Multi-Desktop applications
As of VLDocking version 2.1, new features have been introduced that change the contents of
workspace files, such as *multiple desktop* sharing a common set of dockables,
and *compound dockable containers* allowing more complex component nesting.
There features don't change the way you load or save your workspaces, but the new workspace
XML file format can't be read from VLDocking 2.0 (while the opposite remains possible).
The only thing to remember is that readXML() and writeXML() are now redirected to the
shared DockingContext (so a *single load/save operation is required for a set of desktops* sharing the
same context). And that when using multiple desktops, you'd better create the desktop with the
`DockingDesktop(String name)` constructor : this naming information is saved
with the desktop and used on workspace reloading (this information is more detailed in [lesson 11](tutorial11)).
----
Next : [Lesson 5 - Extending VLDocking](tutorial5)