I think, I had an error in my application logic. After fixing this, the visibility problem gone away.
But I tried again to use CControl.readXML and CControl.writeXML with your hints. This works now in principle. But I have a problem with multiple dockables. They get invisible after loading a perspective with CControl.readXML.
I changed the example so, that both APIs can be compared.
First, let „useControlXMLAPI = true“ to use CControl.readXML and CControl.writeXML. The following does not work:
[ol]
[li]Click „New editor“ two times
[/li][li]Click „Save snapshot layout“
[/li][li]Click „Load layout“
[/li][li]Problem: The editors get invisible
[/li][/ol]
Now try the same with „useControlXMLAPI = false“ to use CControl.getPerspectives().readXML(element) and CControl.getPerspectives().writeXML. The editors stay visible, which is ok.
import bibliothek.gui.dock.common.DefaultMultipleCDockable;
import bibliothek.gui.dock.common.DefaultSingleCDockable;
import bibliothek.gui.dock.common.MissingCDockableStrategy;
import bibliothek.gui.dock.common.MultipleCDockableFactory;
import bibliothek.gui.dock.common.MultipleCDockableLayout;
import bibliothek.gui.dock.common.SingleCDockable;
import bibliothek.gui.dock.common.SingleCDockableFactory;
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.CWorkingPerspective;
import bibliothek.gui.dock.common.perspective.MultipleCDockablePerspective;
import bibliothek.gui.dock.common.perspective.SingleCDockablePerspective;
import bibliothek.util.Filter;
import bibliothek.util.xml.XElement;
import bibliothek.util.xml.XIO;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.event.ActionEvent;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.UnsupportedEncodingException;
import java.util.HashMap;
import java.util.Map;
import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JPanel;
import javax.swing.WindowConstants;
public class PerspectivesMulti {
/* Perspectives support SingleCDockables and MultipleCDockables. This example sets up
* a CWorkingArea with some dockables on it. */
/* Since we are going to work with MultipleCDockables we will need some factory and
* an unique identifier for this factory. That would be this constant. */
public static final String CUSTOM_MULTI_FACTORY_ID = "custom";
private static CControl control;
private static CPerspective initialPerspective;
private enum MODE {
SAVE_INITIAL_LAYOUT, SAVE_SNAPSHOT_LAYOUT, LOAD
};
// private static MODE mode = MODE.LOAD;
private static final boolean useControlXMLAPI = true;
public static void main(String[] args) throws FileNotFoundException, UnsupportedEncodingException, IOException {
JFrame frame = new JFrame();
control = new CControl(frame);
control.setMissingStrategy(MissingCDockableStrategy.STORE);
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
/* When working with perspectives it is always a good idea first to set up the
* CControl, then create the perspectives. */
ColorFactory colorFactory = new ColorFactory();
colorFactory.setFilterEnabled(false);
control.addSingleDockableFactory(colorFactory, colorFactory);
CustomMultiFactory customFactory = new CustomMultiFactory();
customFactory.setFilterEnabled(true);
control.addMultipleDockableFactory(CUSTOM_MULTI_FACTORY_ID, customFactory);
/* By creating the root-stations now we automatically create counterparts in the
* perspective. Otherwise we would need to do some setting up with the perspectives as
* well. */
frame.add(control.getContentArea(), BorderLayout.CENTER);
JMenuBar menuBar = new JMenuBar();
JMenu layoutMenu = new JMenu("Layout");
menuBar.add(layoutMenu);
JMenuItem newEditorItem = new JMenuItem("New editor");
layoutMenu.add(newEditorItem);
JMenuItem saveMenuItem1 = new JMenuItem("Save initial layout");
layoutMenu.add(saveMenuItem1);
JMenuItem saveMenuItem2 = new JMenuItem("Save snapshot layout");
layoutMenu.add(saveMenuItem2);
JMenuItem loadMenuItem = new JMenuItem("Load layout");
layoutMenu.add(loadMenuItem);
JMenuItem resetMenuItem = new JMenuItem("Reset layout");
layoutMenu.add(resetMenuItem);
frame.add(menuBar, BorderLayout.NORTH);
newEditorItem.addActionListener(new java.awt.event.ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
try {
createEditor();
} catch (Exception ex) {
ex.printStackTrace();
}
}
});
saveMenuItem1.addActionListener(new java.awt.event.ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
try {
saveLayout(MODE.SAVE_INITIAL_LAYOUT);
} catch (Exception ex) {
ex.printStackTrace();
}
}
});
saveMenuItem2.addActionListener(new java.awt.event.ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
try {
saveLayout(MODE.SAVE_SNAPSHOT_LAYOUT);
} catch (Exception ex) {
ex.printStackTrace();
}
}
});
loadMenuItem.addActionListener(new java.awt.event.ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
try {
loadLayout();
} catch (Exception ex) {
ex.printStackTrace();
}
}
});
resetMenuItem.addActionListener(new java.awt.event.ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
try {
resetLayout();
} catch (Exception ex) {
ex.printStackTrace();
}
}
});
/* Access to the perspective API */
CControlPerspective perspectives = control.getPerspectives();
control.createWorkingArea("work");
initialPerspective = perspectives.createEmptyPerspective();
/* Now we just drop some random SingleCDockables onto the center area */
CGridPerspective center = initialPerspective.getContentArea().getCenter();
center.gridAdd(0, 0, 50, 50, new SingleCDockablePerspective("Red"));
// center.gridAdd(50, 0, 50, 50, new SingleCDockablePerspective("Green"));
// center.gridAdd(50, 0, 50, 50, new SingleCDockablePerspective("Blue"));
center.gridAdd(20, 0, 80, 50, new SingleCDockablePerspective("Green"));
center.gridAdd(20, 0, 80, 50, new SingleCDockablePerspective("Blue"));
/* because we called "control.createWorkingArea" we can now access the
* CWorkingPerspective with the same unique identifier. We could also just
* create a new CWorkingPerspective and use "addRoot" to store it. */
CWorkingPerspective work = (CWorkingPerspective) initialPerspective.getStation("work");
center.gridAdd(0, 50, 100, 100, work);
MultipleCDockablePerspective[] cp = new MultipleCDockablePerspective[5];
for (int i = 0; i < 5; i++) {
/* To add MultipleCDockales we only need the unique identifier of their factory,
* and their content. The content is an object of type MultipleCDockableLayout.
*
* Btw. by using the same position and size for all dockables we can easily
* stack them. */
CustomMultiLayout layout = new CustomMultiLayout(i);
cp** = new MultipleCDockablePerspective(CUSTOM_MULTI_FACTORY_ID, "id" + i, layout);
work.gridAdd(20 * i, 0, 20, 100, cp**);
}
/* Finally we apply the perspective we just created */
perspectives.setPerspective(initialPerspective, true);
frame.setSize(600, 300);
frame.setVisible(true);
}
private static int editorNumber = 0;
private static void createEditor() {
CustomMultiLayout layout = new CustomMultiLayout(editorNumber);
CustomMultiFactory factory = (CustomMultiFactory) control.getMultipleDockableFactory(CUSTOM_MULTI_FACTORY_ID);
CustomMultiDockable dock = new CustomMultiDockable(factory, "D" + (editorNumber + 1), layout.getNumber());
editorNumber++;
dock.setWorkingArea(control.getStation("work"));
dock.setCloseable(true);
dock.setRemoveOnClose(false);
control.addDockable(dock);
dock.setVisible(true);
}
private static void saveLayout(MODE mode) throws FileNotFoundException, UnsupportedEncodingException, IOException {
CPerspective perspective;
if (mode == MODE.SAVE_SNAPSHOT_LAYOUT) {
// create a snapshot perspective
perspective = control.getPerspectives().getPerspective(true);
// try to add invisible dockable perspective for dockable 4
//perspective.putDockable(cp[4]);
} else {
perspective = initialPerspective;
}
if (useControlXMLAPI) {
control.save("mysettings");
control.writeXML(new File("layout-p2.xml"));
} else {
File file = new File("layout-p1.xml");
DataOutputStream out = new DataOutputStream(new BufferedOutputStream(new FileOutputStream(file)));
XElement element = new XElement("docking-layout");
control.getPerspectives().writeXML(element, perspective);
OutputStreamWriter writer = new OutputStreamWriter(out, "UTF-8");
XIO.write(element, writer);
writer.close();
out.flush();
out.close();
}
}
private static void loadLayout() throws UnsupportedEncodingException, IOException {
CPerspective perspective ;
if (useControlXMLAPI) {
control.readXML(new File("layout-p2.xml"));
control.load("mysettings");
perspective = control.getPerspectives().getPerspective("mysettings");
} else {
File file = new File("layout-p1.xml");
DataInputStream in = new DataInputStream(new BufferedInputStream(new FileInputStream(file)));
InputStreamReader reader = new InputStreamReader(in, "UTF-8");
XElement element = XIO.read(reader);
perspective = control.getPerspectives().readXML(element);
reader.close();
in.close();
}
control.getPerspectives().setPerspective(perspective, true);
}
private static void resetLayout() {
control.getPerspectives().setPerspective(initialPerspective, true);
}
/* This factory and filter creates new SingleCDockables with some panel that has some
* special color set as background. */
private static class ColorFactory implements SingleCDockableFactory, Filter<String> {
private boolean filterEnabled = true;
private Map<String, Color> colors = new HashMap<String, Color>();
public ColorFactory() {
colors.put("Red", Color.RED);
colors.put("Green", Color.GREEN);
colors.put("Blue", Color.BLUE);
colors.put("Yellow", Color.YELLOW);
colors.put("White", Color.WHITE);
colors.put("Black", Color.BLACK);
}
@Override
public boolean includes(String item) {
// example: "Red" is not available on startup
if (filterEnabled && "Red".equals(item)) {
return false;
}
return colors.containsKey(item);
}
@Override
public SingleCDockable createBackup(String id) {
return new ColorSingleCDockable(id, colors.get(id));
}
public void setFilterEnabled(boolean filterEnabled) {
this.filterEnabled = filterEnabled;
}
}
private static Color numberToColor(int number) {
switch (number) {
case 0:
return Color.BLUE;
case 1:
return Color.CYAN;
case 2:
return Color.GRAY;
case 3:
return Color.GREEN;
case 4:
return Color.MAGENTA;
case 5:
return Color.ORANGE;
default:
return Color.WHITE;
}
}
/* This is the kind of MultipleCDockable we will use on our application. */
private static class CustomMultiDockable extends DefaultMultipleCDockable {
private int number;
public CustomMultiDockable(CustomMultiFactory factory, String title, int number) {
super(factory, title);
this.number = number;
JPanel panel = new JPanel();
panel.setBackground(numberToColor(number));
panel.setOpaque(true);
add(panel, BorderLayout.CENTER);
//setTitleIcon( new ColorIcon( color ) );
}
public int getNumber() {
return number;
}
}
/* This kind of MultipleCDockableLayout describes the content of a CustomMultiDockable. */
private static class CustomMultiLayout implements MultipleCDockableLayout {
private int number;
public CustomMultiLayout(int number) {
this.number = number;
}
private int getNumber() {
return number;
}
@Override
public void readStream(DataInputStream in) throws IOException {
number = in.readInt();
}
@Override
public void readXML(XElement element) {
number = element.getInt();
}
@Override
public void writeStream(DataOutputStream out) throws IOException {
out.writeInt(number);
}
@Override
public void writeXML(XElement element) {
element.setInt(number);
}
}
/* And this factory creates new CustomMultiDockables and new CustomMultiLayouts when
* the framework needs them. */
private static class CustomMultiFactory implements MultipleCDockableFactory<CustomMultiDockable, CustomMultiLayout> {
private boolean filterEnabled = true;
@Override
public CustomMultiLayout create() {
return new CustomMultiLayout(-1);
}
@Override
public boolean match(CustomMultiDockable dockable, CustomMultiLayout layout) {
return dockable.getNumber() == layout.getNumber();
}
@Override
public CustomMultiDockable read(CustomMultiLayout layout) {
int number = layout.getNumber();
String title = "D" + (layout.getNumber() + 1); //"R=" + color.getRed() + ", G=" + color.getGreen() + ", B=" + color.getBlue();
if (filterEnabled) {
return null;
}
CustomMultiDockable multi = new CustomMultiDockable(this, title, layout.getNumber());
multi.setRemoveOnClose(false);
multi.setCloseable(true);
return multi;
}
@Override
public CustomMultiLayout write(CustomMultiDockable dockable) {
return new CustomMultiLayout(dockable.getNumber());
}
public void setFilterEnabled(boolean filterEnabled) {
this.filterEnabled = filterEnabled;
}
}
public static class ColorSingleCDockable extends DefaultSingleCDockable {
private JPanel panel = new JPanel();
public ColorSingleCDockable(String title, Color color) {
this(title, color, 1.0f);
}
public ColorSingleCDockable(String title, Color color, float brightness) {
super(title);
setTitleText(title);
if (brightness != 1.0) {
float[] hsb = Color.RGBtoHSB(color.getRed(), color.getGreen(), color.getBlue(), null);
hsb[1] = Math.min(1.0f, hsb[1] / brightness);
hsb[2] = Math.min(1.0f, hsb[2] * brightness);
color = Color.getHSBColor(hsb[0], hsb[1], hsb[2]);
}
setColor(color);
}
public void setColor(Color color) {
panel = new JPanel();
panel.setOpaque(true);
panel.setBackground(color);
add(panel);
//setTitleIcon( new ColorIcon( color ) );
}
public Color getColor() {
return panel.getBackground();
}
}
}```