java.lang.StringIndexOutOfBoundsException: String index out of range: -1

Hello !

I have an exception when I try to read a saved file.

java.lang.StringIndexOutOfBoundsException: String index out of range: -1
    at java.lang.String.substring(String.java:1937)
    at java.lang.String.substring(String.java:1904)
    at bibliothek.gui.dock.common.intern.DefaultCControlRegister.multiToNormalId(DefaultCControlRegister.java:253)
    at bibliothek.gui.dock.common.CControl.shouldStore(CControl.java:2062)
    at bibliothek.gui.dock.common.CControl.access$100(CControl.java:199)
    at bibliothek.gui.dock.common.CControl$1.shouldStoreShown(CControl.java:464)
    at bibliothek.gui.dock.frontend.DefaultLayoutChangeStrategy.applyLayout(DefaultLayoutChangeStrategy.java:275)

I try to save and load from a file, the layout below. If anyone has an idea on how to do that or why I have an exception, I’m interested.


import java.awt.BorderLayout;
import java.awt.Color;
import java.io.File;

import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;

import bibliothek.gui.dock.common.CControl;
import bibliothek.gui.dock.common.CGrid;
import bibliothek.gui.dock.common.CGridArea;
import bibliothek.gui.dock.common.CLocation;
import bibliothek.gui.dock.common.DefaultSingleCDockable;
import bibliothek.gui.dock.common.SingleCDockable;
import bibliothek.gui.dock.common.SingleCDockableFactory;
import bibliothek.gui.dock.common.mode.ExtendedMode;
import bibliothek.util.filter.PresetFilter;

public class Dock8 {
    private static class StackableCGridArea extends CGridArea {
        public StackableCGridArea(CControl control, String id) {
            super(control, id);
        }

        @Override
        public boolean isStackable() {
            return true;
        }
    }

    public static CGridArea createGridArea(String id, CControl control) {
        CGridArea area = new StackableCGridArea(control, id);
        area.setTitleShown(true);
        area.setTitleText(id);
        control.addDockable(area);
        control.addStation(area, true);
        System.out.println("Dock8.createGridArea() : " + id);
        return area;
    }

    private static class MyFactory implements SingleCDockableFactory {

        public SingleCDockable createBackup(String id) {
            return Dock8.createDockable(id, getColorByName(id));
        }

    }

    public static SingleCDockable createDockable(String title, Color color) {
        JPanel panel = new JPanel();
        panel.setOpaque(true);
        panel.setBackground(color);
        DefaultSingleCDockable dockable = new DefaultSingleCDockable(title,
                title, panel);

        dockable.setDefaultLocation(ExtendedMode.MINIMIZED, CLocation.base()
                .minimalEast());
        dockable.setDefaultLocation(ExtendedMode.EXTERNALIZED,
                CLocation.external(0, 0, 300, 300));
        System.out.println("Dock8.createDockable() " + title);
        return dockable;
    }

    /**
     * Gets a color from {@link Color}'s static constants.
     */
    private static Color getColorByName(String colorName) {
        try {
            return (Color) Color.class.getDeclaredField(colorName).get(null);
        } catch (Exception ex) {
            throw new RuntimeException(ex);
        }
    }

    private static final String[] COLOR_NAMES = { "green", "blue", "red",
            "yellow", "cyan", "magenta" };

    private static final String[] GRID = { "grid1", "grid2" };

    private static void loadDefaultLayout(final CControl control) {
        CGridArea gridArea1 = createGridArea(GRID[0], control);
        CGridArea gridArea2 = createGridArea(GRID[1], control);

        CGrid grid = new CGrid(control);
        grid.add(0, 0, 1, 2, gridArea1, gridArea2);
        grid.add(1, 0, 1, 1, createDockable("green", Color.GREEN));
        grid.add(1, 1, 1, 1, createDockable("blue", Color.BLUE));
        control.getContentArea().deploy(grid);

        CGrid grid1 = new CGrid(control);
        grid1.add(0, 0, 1, 1, createDockable("red", Color.RED));
        grid1.add(0, 1, 1, 1, createDockable("yellow", Color.YELLOW));
        gridArea1.deploy(grid1);

        CGrid grid2 = new CGrid(control);
        grid2.add(0, 0, 1, 1, createDockable("cyan", Color.CYAN));
        grid2.add(0, 1, 1, 1, createDockable("magenta", Color.MAGENTA));
        gridArea2.deploy(grid2);

        try {
            File file = new File(Dock8.class.getSimpleName() + ".xml");
            control.writeXML(file);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private static void loadFromFile(final CControl control) {
        try {
            File file = new File(Dock8.class.getSimpleName() + ".xml");
            control.readXML(file);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                JFrame frame = new JFrame("Demo");
                final CControl control = new CControl(frame);

                control.addSingleDockableFactory(
                        new PresetFilter<String>(GRID),
                        new SingleCDockableFactory() {
                            public SingleCDockable createBackup(String id) {
                                return createGridArea(id, control);
                            }
                        });

                control.addSingleDockableFactory(new PresetFilter<String>(
                        COLOR_NAMES), new MyFactory());

                frame.add(control.getContentArea(), BorderLayout.CENTER);

                //load and save 
                File layoutFile = new File(Dock8.class.getSimpleName() + ".xml");
                if (layoutFile.exists()) {
                    loadFromFile(control);
                } else {
                    loadDefaultLayout(control);
                }

                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.setBounds(20, 20, 400, 400);
                frame.setVisible(true);

            }

        });
    }
}

Thanks

Could you please post the file that you try to load? Also which version of the framework are you using?

I have an idea of what is going wrong, but the file would help me to confirm the bug.

[Edit: stupid me, your application already produces the file. Sorry… ]

This is actually a tricky bug and won’t be fixed that fast. The issue is, that you dynamically create a CGridArea, and the framework does not handle that case well (to be more precise, this case is completely ignored). I think the framework should be modified such that your application runs as it is now. I’ll work on that and the bug should be fixed in this or the next release.

I’ll write back once a version is available that handles your case. Until then there is no other option than to create and register the areas before loading the layout.

[Edit: a fix will be delivered in 1.1.1p5a, which - without guaranteeing the date - will be ready at 14.Aug. There will be a slight modification necessary on your application: the framework will register the station and dockable. I’ll post an upgraded application once the fix is ready.]

Thank you for your help.
I wait with impatience for the next release.
:slight_smile:

Ok, the new version is online. Your application should now run.

You need to modify your application such that the lines…

        control.addStation(area, true);```
... are **not** called **if** invoked from ...
```                            public SingleCDockable createBackup(String id) {
                                return createGridArea(id, control);
                            }```

The framework will know that this dockable is in reality a CStation and register it accordingly. If your application registers it as well... well then an exception will be thrown.

It works fine.
Thanks … :wink:

When loading the layout from a file (and only from a file), buttons “Minimize”, “Externalize” and “Maximize” are presents on the CStation (StackableCGridArea) although they should not be present. Furthermore, the buttons do not work.

Is there a way to remove the buttons ?

Interesting, I did not pay attention to the buttons. I have not tested this, but what happens if you call “setMinimizeable(false), setMaximizeable(false), setExternalizeable(false)” on the “area”?

I haven’t found the methods “setMinimizeable(boolean)”, “setMaximizeable(boolean)”, “setExternalizeable(boolean)” on area but i implement methods isExternalizable, isMaximizable, isMinimizable whithout any results.

    private static class StackableCGridArea extends CGridArea {
        public StackableCGridArea(CControl control, String id) {
            super(control, id);
        }

        @Override
        public boolean isStackable() {
            return true;
        }

        @Override
        public boolean isExternalizable() {
            return false;
        }

        @Override
        public boolean isMaximizable() {
            return false;
        }

        @Override
        public boolean isMinimizable() {
            return false;
        }
    }

Uh, right, they only exist on DefaultDockables… Ok, I’ll have a look at the problem. Can’t be anything serious.