'Close all' and 'Close others' actions in the eclipse way

Hello all,

First thanks for the good job that DF is.

My question is how can I implement ‘Close all’ and ‘Close others’ actions in a MultipleCDockable like the ones in Eclipse ?

In fact 2 questions here :

  1. By ‘like the ones in Eclipse’, I mean actions available in the popup menu when right clicking on a tab, but not available in the title.
  2. How to close all the dockables ? I’ve tried things like in CCloseAction and CloseAction, but for dockables in the result of CControlRegister.getDockables(). But not sure that it is really the solution.

Thanks in advance.

The solution may not be obvious, but this little example application should show you everything you need to know. In this case “Close Others” means “Close all Dockable which have the same parent” (e.g. are in the same stack).


import javax.swing.JFrame;

import bibliothek.gui.DockStation;
import bibliothek.gui.Dockable;
import bibliothek.gui.dock.action.view.ActionViewConverter;
import bibliothek.gui.dock.action.view.ViewTarget;
import bibliothek.gui.dock.common.CControl;
import bibliothek.gui.dock.common.CGrid;
import bibliothek.gui.dock.common.DefaultMultipleCDockable;
import bibliothek.gui.dock.common.action.CAction;
import bibliothek.gui.dock.common.action.CButton;
import bibliothek.gui.dock.common.action.core.CommonSimpleButtonAction;
import bibliothek.gui.dock.common.intern.CDockable;
import bibliothek.gui.dock.common.intern.CommonDockable;
import bibliothek.gui.dock.common.theme.ThemeMap;

public class CloseTest {
	public static void main(String[] args) {
		// set up a frame with some content...
		JFrame frame = new JFrame();
		frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		
		CControl control = new CControl( frame );
		control.setTheme( ThemeMap.KEY_ECLIPSE_THEME );
		frame.add( control.getContentArea() );
		
		CGrid grid = new CGrid( control );
		for( int i = 0; i < 4; i++ ){
			grid.add( 0, 0, 1, 1, createDockable( 2*i ));
			grid.add( 1, 0, 1, 1, createDockable( 2*i+1 ));
		}
		control.getContentArea().deploy( grid );
		
		frame.setBounds( 20, 20, 400, 300 );
		frame.setVisible( true );
	}
	
	private static CDockable createDockable( int index ){
		DefaultMultipleCDockable dockable = new DefaultMultipleCDockable( null );
		dockable.setCloseable( true );
		dockable.setRemoveOnClose( true );
		dockable.setTitleText( "Dockable " + index );
		
		// here we add our new special action to the dockable.
		dockable.addAction( new CloseOthersAction( dockable ));
		
		return dockable;
	}
	
	// This is, as the name suggests, a button that closes other dockables.
	// A CButton is actually a wrapper around a DockAction, we need to access the DockAction if we 
	// want to limit the places where the action shows up.
	private static class CloseOthersAction extends CButton{
		private CDockable dockable;
		
		public CloseOthersAction( CDockable dockable ){
			// prevent standard initialization of the action by calling the protected constructor
			super( null );
			// initialize with a modified action
			init( new MenuOnlySimpleAction( this ) );
			this.dockable = dockable;
			setText( "Close Others" );
		}
		
		@Override
		protected void action() {
			super.action();
			
			// We need to access the Core API to find out which other Dockables exist.
			DockStation parent = dockable.intern().getDockParent();
			
			// Because closing a Dockable may remove the parent DockStation, we first collect all the
			// Dockables we may later close
			Dockable[] children = new Dockable[ parent.getDockableCount() ];
			for( int i = 0; i < children.length; i++ ){
				children** = parent.getDockable( i );
			}
			for( Dockable child : children ){
				// we are not interested in things like entire stacks, or our own Dockable. So let's do
				// some checks before closing a Dockable
				if( child instanceof CommonDockable ){
					CDockable cChild = ((CommonDockable)child).getDockable();
					if( cChild != dockable ){
						cChild.setVisible( false );
					}
				}
			}
		}
	}
	
	private static class MenuOnlySimpleAction extends CommonSimpleButtonAction{
		public MenuOnlySimpleAction( CAction action ){
			super( action );
		}
		
		@Override
		public <V> V createView( ViewTarget<V> target, ActionViewConverter converter, Dockable dockable ) {
			// This method creates the view (e.g. a JMenuItem) for this DockAction. Since we do not want
			// to show it up everywhere, we just ignore some places (targets).
			if( ViewTarget.TITLE == target ){
				return null;
			}
			return super.createView(target, converter, dockable);
		}
	}
}

Thank you. That’s exactly what I need.

One last question. To run the code, I had to manually install version 1.1.1 of common and core, because it is not available on maven central. Any reason for this ? Did I miss something ?

Thanks again.

Another question : is there a way to group in the menu those new actions with the predefined ‘Close’ action ?

I’ve almost managed to get it work with this code in the constructor of my DefaultMultipleCDockable implementation :

AbstractDockable intern = this.intern();
LocationHint locationHint = new LocationHint(LocationHint.ACTION_GUARD, LocationHint.RIGHT_OF_ALL);
DockAction[] miscCloseActions = new DockAction[] { new CloseOthersAction(this).intern(), new CloseAllAction(this).intern() };
DockActionSource source = new DefaultDockActionSource(locationHint, miscCloseActions);
intern.setActionOffers(source);

This makes that my custom actions appear just above the Close action.

But it seems fragile. I would have prefered something that really groups all close actions. Can this be achieved ?

[QUOTE=bcantin]Thank you. That’s exactly what I need.

One last question. To run the code, I had to manually install version 1.1.1 of common and core, because it is not available on maven central. Any reason for this ? Did I miss something ?

Thanks again.[/QUOTE]

Ok, I’ve found your statement about maven and 1.1.1 (in the thread concerning 1.1.2). I’ll do it manually.

Your solution is close to what I would do, but you will have some bad side-effects from calling “setActionOffers”: you will no longer be able to show any other custom actions.

By extending DefaultMultipleCDockable you can create a DockActionSource that includes the “real” close action:


import javax.swing.JFrame;

import bibliothek.gui.DockStation;
import bibliothek.gui.Dockable;
import bibliothek.gui.dock.action.DefaultDockActionSource;
import bibliothek.gui.dock.action.DockActionSource;
import bibliothek.gui.dock.action.MultiDockActionSource;
import bibliothek.gui.dock.action.view.ActionViewConverter;
import bibliothek.gui.dock.action.view.ViewTarget;
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.action.CAction;
import bibliothek.gui.dock.common.action.CButton;
import bibliothek.gui.dock.common.action.CSeparator;
import bibliothek.gui.dock.common.action.core.CommonSimpleButtonAction;
import bibliothek.gui.dock.common.intern.CDockable;
import bibliothek.gui.dock.common.intern.CommonDockable;
import bibliothek.gui.dock.common.intern.DefaultCommonDockable;
import bibliothek.gui.dock.common.intern.action.CloseActionSource;
import bibliothek.gui.dock.common.theme.ThemeMap;

public class CloseTest {
	public static void main(String[] args) {
		// set up a frame with some content...
		JFrame frame = new JFrame();
		frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		
		CControl control = new CControl( frame );
		control.setTheme( ThemeMap.KEY_ECLIPSE_THEME );
		frame.add( control.getContentArea() );
		
		CGrid grid = new CGrid( control );
		for( int i = 0; i < 4; i++ ){
			grid.add( 0, 0, 1, 1, createDockable( 2*i ));
			grid.add( 1, 0, 1, 1, createDockable( 2*i+1 ));
		}
		control.getContentArea().deploy( grid );
		
		frame.setBounds( 20, 20, 400, 300 );
		frame.setVisible( true );
	}
	
	private static CDockable createDockable( int index ){
		DefaultMultipleCDockable dockable = new CloseableDefaultMultipleCDockable( null );

		dockable.setTitleText( "Dockable " + index );
		
		return dockable;
	}
	
	// our special Dockable for setting up additional actions at an unusual place
	private static class CloseableDefaultMultipleCDockable extends DefaultMultipleCDockable{
		public CloseableDefaultMultipleCDockable( MultipleCDockableFactory<? extends CloseableDefaultMultipleCDockable, ?> factory ) {
			super( factory );
			setCloseable( true );
			setRemoveOnClose( true );
		}
		
		@Override
		protected DefaultCommonDockable createCommonDockable() {
			// note: this method is called from a super constructor!
			
			// this is the "real" close action
			DockActionSource defaultCloseAction = getClose();
			
			// we create a list of additional actions.
			DefaultDockActionSource additionalCloseActions = new DefaultDockActionSource();
			additionalCloseActions.add(CSeparator.MENU_SEPARATOR.intern());
			// actually the CloseOthersAction class would no longer need to extend CButton, extending
			// SimpleButtonAction would be enough. But let's not change our first example.
			additionalCloseActions.add(new CloseOthersAction(this).intern());

			// we combine (group) our actions in one list of actions
			MultiDockActionSource closeActions = new MultiDockActionSource( defaultCloseAction.getLocationHint() );
			closeActions.add( additionalCloseActions );
			closeActions.add( defaultCloseAction );
			
			// and we create the new Core Dockable using our new list of actions
			return new DefaultCommonDockable( this, closeActions );
		}
	}
	
	// This is, as the name suggests, a button that closes other dockables.
	// A CButton is actually a wrapper around a DockAction, we need to access the DockAction if we 
	// want to limit the places where the action shows up.
	private static class CloseOthersAction extends CButton{
		private CDockable dockable;
		
		public CloseOthersAction( CDockable dockable ){
			// prevent standard initialization of the action by calling the protected constructor
			super( null );
			// initialize with a modified action
			init( new MenuOnlySimpleAction( this ) );
			this.dockable = dockable;
			setText( "Close Others" );
		}
		
		@Override
		protected void action() {
			super.action();
			
			// We need to access the Core API to find out which other Dockables exist.
			DockStation parent = dockable.intern().getDockParent();
			
			// Because closing a Dockable may remove the parent DockStation, we first collect all the
			// Dockables we may later close
			Dockable[] children = new Dockable[ parent.getDockableCount() ];
			for( int i = 0; i < children.length; i++ ){
				children** = parent.getDockable( i );
			}
			for( Dockable child : children ){
				// we are not interested in things like entire stacks, or our own Dockable. So let's do
				// some checks before closing a Dockable
				if( child instanceof CommonDockable ){
					CDockable cChild = ((CommonDockable)child).getDockable();
					if( cChild != dockable ){
						cChild.setVisible( false );
					}
				}
			}
		}
	}
	
	private static class MenuOnlySimpleAction extends CommonSimpleButtonAction{
		public MenuOnlySimpleAction( CAction action ){
			super( action );
		}
		
		@Override
		public <V> V createView( ViewTarget<V> target, ActionViewConverter converter, Dockable dockable ) {
			// This method creates the view (e.g. a JMenuItem) for this DockAction. Since we do not want
			// to show it up everywhere, we just ignore some places (targets).
			if( ViewTarget.TITLE == target ){
				return null;
			}
			return super.createView(target, converter, dockable);
		}
	}
}

Thank you very much ! It is working as expected.