Undo

Hello,

does the framework support an undo functionality?
For example to undo (or record)
a state change (max, min, etc.) or a rearrangement
within a SplitDockStation or a StackDockStation.

best regards,
Maciej Modelski

No, there is no such thing.

Changing the state of a CDockable could be catched with a CDockableStateListener and you could build your own history. But what’s happening inside a DockStation is invisible (I admit, it might be a good idea to expose these events).

Is there maybe some other indirect way of listening to changes in the stations?
If I knew that “something” changed within a SplitDockStation or a StackDockStation,
I could check and store the new docking layout myself.

best regards,
MM

Sorry, neither station offers anything. But I’ll put this on the todo list and if I find the time I’ll implement some solution during the weekend.

I’ve added a new method to DockStationListener: “dockablesRepositioned” will be invoked whenever a child of a station was moved around (you can keep track of the parent of a Dockable with the help of a DockHierarchyListener).

I’ve also added a new listener “DockableStateListener” which can be added to a Dockable and will receive events if the position of itself or its parent changed, if the visibility state changed (visible to the user or not), or if the selection state changed.

I’ll upload a new version including these changes during this week.

Hi Beni,

I tried the CDockableLocationListener and I have some questions/found some bugs.
Below you can find the code I tested it with.

When you start up there is no event for Blue. Blue is on a stack and it is not visible.
When you click on the Black tab the event is triggered.

When you switch the order of Blue and Black tabs, no event is triggered.
This does not happen in Basic theme, i.e. in Basic theme the event is triggered.
I checked Eclipse, Bubble and Flat themes and in all of them, there is no event
if you rearrange the order of the tabs of a StackDockStation.

There are no events if you rearrange Orange and Yellow. They are both on
an external SplitDockStation so maybe I have to implement something extra for them?

best regards,
MM

package test;
 
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Rectangle;

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

import test.Dock37.LocationListener;

import bibliothek.gui.DockController;
import bibliothek.gui.DockStation;
import bibliothek.gui.Dockable;
import bibliothek.gui.dock.ScreenDockStation;
import bibliothek.gui.dock.SplitDockStation;
import bibliothek.gui.dock.common.CControl;
import bibliothek.gui.dock.common.CGrid;
import bibliothek.gui.dock.common.CStation;
import bibliothek.gui.dock.common.DefaultSingleCDockable;
import bibliothek.gui.dock.common.action.predefined.CBlank;
import bibliothek.gui.dock.common.event.CDockableLocationEvent;
import bibliothek.gui.dock.common.event.CDockableLocationListener;
import bibliothek.gui.dock.common.event.CDockableStateListener;
import bibliothek.gui.dock.common.intern.CDockable;
import bibliothek.gui.dock.common.intern.DefaultCDockable;
import bibliothek.gui.dock.common.intern.ui.CSingleParentRemover;
import bibliothek.gui.dock.common.mode.ExtendedMode;
import bibliothek.gui.dock.common.theme.ThemeMap;
import bibliothek.gui.dock.event.DockStationAdapter;
import bibliothek.gui.dock.station.split.SplitDockPathProperty;
 
public class Dock34 {
    @SuppressWarnings("unchecked")
    public static void main( String[] args ){
        // setting up frame and controller
      
        JFrame frame = new JFrame();
        CControl control = new CControl( frame );
                      
        control.setTheme(ThemeMap.KEY_ECLIPSE_THEME);
                
        // a listener that removes the standard maximize action when externalizing
        // and adds it back when unexternalizing
        control.addStateListener(new WMSMultipleStateListener());
        
        // now access the DockStation which shows our detached (externalized) items
        CStation<ScreenDockStation> screen = (CStation<ScreenDockStation>) control.getStation( CControl.EXTERNALIZED_STATION_ID );
 
        // if a Dockable is added to that station...
        screen.getStation().addDockStationListener( new DockStationAdapter(){
            @Override
            public void dockableAdded( DockStation station, Dockable dockable ){
                // ... and the new child is not a SplitDockStation ...
                if( !(dockable instanceof SplitDockStation )){
                    // .. then we just insert a SplitDockStation
                    SplitDockStation split = new SplitDockStation();
                   
                    DockController controller = station.getController();
                   
                    try{
                        // disable events while rearanging our layout
                        controller.freezeLayout();
                       
                        station.replace( dockable, split );
                        split.drop( dockable );
                    }
                    finally{
                        // and enable events after we finished
                        controller.meltLayout();
                    }
                }
            }
        });
       
        // make sure a SplitDockStation with one child and a parent that is a ScreenDockStation does not get removed
        control.intern().getController().setSingleParentRemover( new CSingleParentRemover( control ){
            @Override
            protected boolean shouldTest( DockStation station ){
                if( station instanceof SplitDockStation ){
                    SplitDockStation split = (SplitDockStation)station;
                    if( split.getDockParent() instanceof ScreenDockStation ){
                        // but we want to remove the station if it does not have any children at all
                        return split.getDockableCount() == 0;
                    }
                }
                return super.shouldTest( station );
            }
        });
 
        frame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
 
        DefaultSingleCDockable red = create(control, "Red", Color.RED);
        DefaultSingleCDockable green = create(control, "Green", Color.GREEN);
        DefaultSingleCDockable blue = create(control, "Blue", Color.BLUE);
        DefaultSingleCDockable black = create(control, "Black", Color.BLACK);
        DefaultSingleCDockable yellow = create(control, "Yellow", Color.YELLOW);
        DefaultSingleCDockable orange = create(control, "Orange", Color.ORANGE);

        LocationListener ll = new LocationListener();    
        red.addCDockableLocationListener(ll);
        green.addCDockableLocationListener(ll);
        black.addCDockableLocationListener(ll);
        blue.addCDockableLocationListener(ll);
        orange.addCDockableLocationListener(ll);
        yellow.addCDockableLocationListener(ll);
        
        CGrid grid = new CGrid(control);
        grid.add(0, 0, 5, 3, red);
        grid.add(0, 5, 5, 7, green);
        grid.add(10, 5, 5, 5, blue);
        grid.add(10, 5, 5, 5, black);

        control.getContentArea().deploy(grid);
        
        control.addDockable(yellow);
        control.addDockable(orange);
        
        //insert orange as external
        screen.getStation().addDockable(orange.intern().asDockable(), new Rectangle(610, 100, 500, 500));
        //drop yellow on the same dock station as orange
        orange.intern().asDockable().getDockParent().drop(yellow.intern().asDockable());
        
        frame.getContentPane().add( control.getContentArea(), BorderLayout.CENTER );
        frame.setSize( new Dimension( 600, 600 ) );
        frame.setVisible( true );
    }
 
    public static DefaultSingleCDockable create(CControl control, String title, Color color)
    {
      JPanel panel = new JPanel();
      panel.setOpaque(true);
      panel.setBackground(color);
      return new DefaultSingleCDockable(title, title, panel);
    }

    protected static class LocationListener implements CDockableLocationListener
    {

      public void changed(CDockableLocationEvent event)
      {
        System.out.println("changed: " + event.getDockable().intern().asDockable().getTitleText() + " " + event);
      }
    }        
    
    public static class WMSMultipleStateListener implements CDockableStateListener
    {

      public void visibilityChanged(CDockable cd)
      {
        // ignore
      }

      public void extendedModeChanged(CDockable cd, ExtendedMode mode)
      {
        if ( cd instanceof DefaultCDockable )
        {
          DefaultCDockable dockable = (DefaultCDockable) cd;
          if ( mode.equals(ExtendedMode.EXTERNALIZED) )
          {
            dockable.putAction(CDockable.ACTION_KEY_MAXIMIZE, CBlank.BLANK);            
          }
          else
          {
            dockable.putAction(CDockable.ACTION_KEY_MAXIMIZE, null);            
          }
        }
      }
    }    
}

[QUOTE=mmodelski]Hi Beni,

I tried the CDockableLocationListener and I have some questions/found some bugs.
Below you can find the code I tested it with.

When you start up there is no event for Blue. Blue is on a stack and it is not visible.
When you click on the Black tab the event is triggered.

When you switch the order of Blue and Black tabs, no event is triggered.
This does not happen in Basic theme, i.e. in Basic theme the event is triggered.
I checked Eclipse, Bubble and Flat themes and in all of them, there is no event
if you rearrange the order of the tabs of a StackDockStation.

There are no events if you rearrange Orange and Yellow. They are both on
an external SplitDockStation so maybe I have to implement something extra for them?

best regards,
MM[/quote]

Ok, I did not do enough testing, sorry. These events should be triggered, there is just some code missing. I’ll have these issues fixed an upload a repaired version soon.

Ok, I’ve uploaded a fixed version. Also the orange and yellow Dockable in your application should now get an event.

There may still be some issues, I’ll make some testing on the weekend.

Oh, and you should initialize the application in the EDT. Currently the listeners will have race conditions if you do not.

        // setting up frame and controller
    	SwingUtilities.invokeLater( new Runnable(){
    		@Override
    		public void run(){
	    		init();	
    		}
    	});
    	
    }
    
    public static void init(){
      // initialization goes here
   }```