InputVerifier not executed when I close the last MultipleCDockable

Hi all,

The behaviour described here is with df 1.1.2 preview 1f. I also describe below the behaviour with 1.1.2 preview 8e.

I’m using DockingFrames to display (among other things) a bunch of editors designed like forms in DefaultMultipleCDockables. Each editor contains text fields which are validated when the text field looses the focus.

To do this, I’m using a standard Swing mechanism called InputVerifier as defined here: http://docs.oracle.com/javase/tutorial/uiswing/misc/focus.html#inputVerification

Now, use case 1:

  1. I open 2 editors
  2. In the second editor, I modify the content of one text field
  3. I close the first editor with the cross in the tab (a standard CCloseAction)
  4. I open it again

=> The InputVerifier has been executed (this means that another component gained the focus). In the use case, a listener on the defautKeyboardFocusManager says:

focus listener: comp class = MyTextField; comp name = null
focus listener: comp class = MyTextField; comp name = null
// Click on the cross
InputVerifier.shouldYieldFocus() value = true
focus listener: comp class = bibliothek.gui.dock.StackDockStation$Background; comp name = null
focus listener: comp class = bibliothek.gui.dock.StackDockStation$Background; comp name = null
focus listener: comp class = bibliothek.extension.gui.dock.theme.eclipse.displayer.EclipseDockableDisplayer2; comp name = null
focus listener: comp class = MyTextField; comp name = null
focus listener: comp class = MyTextField; comp name = null

My modifications have been saved, and the editor shows the modified data. This is correct.

Use case 2:
Now I do the same thing except that I open only one editor. I do modifications, and I close it with the cross in the tab.

=> When I reopen the editor, it does not contain the modifications.

The focus listener says:
focus listener: comp class = MyTextField; comp name = null
// Click on the cross
focus listener: comp class = javax.swing.JFrame; comp name = frame0
focus listener: comp class = javax.swing.JFrame; comp name = frame0
focus listener: comp class = javax.swing.JFrame; comp name = frame0

The docs about input verifiers says that: ‘However, InputVerifier is not called when the focus is transferred to another toplevel component’.

Because the focus is going to a JFrame, my input verifier is not executed. What make it is executed in the first case is that bibliothek.gui.dock.StackDockStation$Background gains the focus.

With df 1.1.2 preview 8e, the behaviour is slightly different but not better.

In the use case 1, I get an exception on the EDT so my app is broken:

	at bibliothek.gui.dock.DockHierarchyLock.ensureUnlinked(DockHierarchyLock.java:359)
	at bibliothek.gui.dock.DockHierarchyLock.access$500(DockHierarchyLock.java:45)
	at bibliothek.gui.dock.DockHierarchyLock$Token.release(DockHierarchyLock.java:420)
	at bibliothek.gui.dock.StackDockStation.remove(StackDockStation.java:1536)
	at bibliothek.gui.dock.StackDockStation.drag(StackDockStation.java:1267)
	at bibliothek.gui.dock.control.SingleParentRemover.test(SingleParentRemover.java:184)
	at bibliothek.gui.dock.control.SingleParentRemover.testAll(SingleParentRemover.java:99)
	at bibliothek.gui.dock.control.SingleParentRemover$DockRegisterObserver.dockableUnregistered(SingleParentRemover.java:218)
	at bibliothek.gui.dock.control.DockRegister.fireDockableUnregistered(DockRegister.java:441)
	at bibliothek.gui.dock.control.DockRegister.unregister(DockRegister.java:363)
	at bibliothek.gui.dock.control.DockRegister$StationListener.removeDockable(DockRegister.java:723)
	at bibliothek.gui.dock.control.DockRegister$StationListener.fire(DockRegister.java:574)
	at bibliothek.gui.dock.control.DockRegister.setStalled(DockRegister.java:533)
	at bibliothek.gui.dock.common.CControl$Access.hide(CControl.java:2761)
	at bibliothek.gui.dock.common.intern.AbstractCDockable.setVisible(AbstractCDockable.java:319)
	at bibliothek.gui.dock.common.action.predefined.CCloseAction.close(CCloseAction.java:96)
	at bibliothek.gui.dock.common.action.predefined.CCloseAction$Action.close(CCloseAction.java:119)
	at bibliothek.gui.dock.facile.action.CloseAction.action(CloseAction.java:128)
	at bibliothek.gui.dock.themes.basic.action.BasicButtonHandler.triggered(BasicButtonHandler.java:48)
	at bibliothek.gui.dock.themes.basic.action.BasicButtonModel.trigger(BasicButtonModel.java:704)
	at bibliothek.gui.dock.themes.basic.action.BasicButtonModel$Listener.mouseReleased(BasicButtonModel.java:742)
	at java.awt.AWTEventMulticaster.mouseReleased(AWTEventMulticaster.java:272)
	at java.awt.Component.processMouseEvent(Component.java:6288)
	at javax.swing.JComponent.processMouseEvent(JComponent.java:3267)
	at java.awt.Component.processEvent(Component.java:6053)
	at java.awt.Container.processEvent(Container.java:2041)
	at java.awt.Component.dispatchEventImpl(Component.java:4651)
	at java.awt.Container.dispatchEventImpl(Container.java:2099)
	at java.awt.Component.dispatchEvent(Component.java:4481)
	at java.awt.LightweightDispatcher.retargetMouseEvent(Container.java:4577)
	at java.awt.LightweightDispatcher.processMouseEvent(Container.java:4238)
	at java.awt.LightweightDispatcher.dispatchEvent(Container.java:4168)
	at java.awt.Container.dispatchEventImpl(Container.java:2085)
	at java.awt.Window.dispatchEventImpl(Window.java:2478)
	at java.awt.Component.dispatchEvent(Component.java:4481)
	at java.awt.EventQueue.dispatchEventImpl(EventQueue.java:643)
	at java.awt.EventQueue.access$000(EventQueue.java:84)
	at java.awt.EventQueue$1.run(EventQueue.java:602)
	at java.awt.EventQueue$1.run(EventQueue.java:600)
	at java.security.AccessController.doPrivileged(Native Method)
	at java.security.AccessControlContext$1.doIntersectionPrivilege(AccessControlContext.java:87)
	at java.security.AccessControlContext$1.doIntersectionPrivilege(AccessControlContext.java:98)
	at java.awt.EventQueue$2.run(EventQueue.java:616)
	at java.awt.EventQueue$2.run(EventQueue.java:614)
	at java.security.AccessController.doPrivileged(Native Method)
	at java.security.AccessControlContext$1.doIntersectionPrivilege(AccessControlContext.java:87)
	at java.awt.EventQueue.dispatchEvent(EventQueue.java:613)
	at java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:269)
	at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:184)
	at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:174)
	at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:169)
	at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:161)
	at java.awt.EventDispatchThread.run(EventDispatchThread.java:122)```

In this stack, EditorDockableImpl is my implementation of DefaultMultipleCDockable.

In use case 2, I get the same behaviour (the frame gets the focus).

Sorry for this long description... Any idea ?

One thing I don't understand, is that the close button never gains the focus (an instance of RoundRectButton) even if it is visible and focusable. Maybe because it doesn't have any parent ? If the button gained the focus, the problem would be solved.

Well, I have some code…

To see the problem, in dockable D2, in one of the text fields, type in bbb and close the tab.

You should see in the console something like : shouldYieldFocus() ? false

Do the same thing in dockable D1. You won’t see the log… I think the reason is that the frame is getting the focus.

import java.awt.Color;
import java.awt.Component;
import java.awt.KeyboardFocusManager;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;

import javax.swing.BorderFactory;
import javax.swing.BoxLayout;
import javax.swing.JFrame;
import javax.swing.JPanel;

import bibliothek.gui.dock.common.CControl;
import bibliothek.gui.dock.common.CGrid;
import bibliothek.gui.dock.common.CWorkingArea;
import bibliothek.gui.dock.common.DefaultSingleCDockable;
import bibliothek.gui.dock.common.theme.ThemeMap;
import bibliothek.gui.dock.util.Priority;
import bibliothek.gui.dock.util.color.ColorManager;

public class TabTest2 {

    private static int count = 1;

    public static void main(String[] args) {

        KeyboardFocusManager focusManager = KeyboardFocusManager.getCurrentKeyboardFocusManager();
        focusManager.addPropertyChangeListener(new PropertyChangeListener() {
            @Override
            public void propertyChange(PropertyChangeEvent e) {
                Component comp = (Component) e.getNewValue();
                if (comp == null)
                    return;

                System.out.println("Focus listener comp class = " + comp.getClass().getName() + "; comp name = "
                        + comp.getName());
            }
        });

        JFrame frame = new JFrame("Test");
        frame.getContentPane().setLayout(new BoxLayout(frame.getContentPane(), BoxLayout.Y_AXIS));

        CControl control = new CControl(frame);
        control.setTheme(ThemeMap.KEY_ECLIPSE_THEME);
        frame.add(control.getContentArea());

        CWorkingArea work = control.createWorkingArea("work");
        work.setVisible(true);
        work.setMaximizingArea(true);
        work.getStation().setContinousDisplay(true);

        ColorManager colors = control.getController().getColors();
        colors.put(Priority.CLIENT, "paint.insertion.area", Color.RED);

        CGrid grid = new CGrid(control);
        DefaultSingleCDockable d1 = createDockable("D1");
        grid.add(0, 0, 1, 1, d1);
        DefaultSingleCDockable d2 = createDockable("D2");
        grid.add(0, 0, 1, 1, d2);

        work.deploy(grid);

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

    private static DefaultSingleCDockable createDockable(String title) {
        JPanel p = new JPanel();
        p.setBorder(BorderFactory.createTitledBorder("Inside dockable"));
        p.setLayout(new BoxLayout(p, BoxLayout.Y_AXIS));
        p.add(new MyTextField("tf " + count++));
        p.add(new MyTextField("tf " + count++));
        p.add(new MyTextField("tf " + count++));
        DefaultSingleCDockable d1 = new DefaultSingleCDockable(title, title, p);
        d1.setCloseable(true);
        return d1;
    }
}
import java.awt.Color;

import javax.swing.BorderFactory;
import javax.swing.InputVerifier;
import javax.swing.JComponent;
import javax.swing.JTextField;
import javax.swing.border.Border;

/**
 * @author Benoit Cantin
 */
public class MyTextField extends JTextField {

    Border originalBorder;

    public MyTextField(String name) {
        super();

        this.setName(name);

        this.originalBorder = this.getBorder();

        this.setInputVerifier(new InputVerifier() {
            @Override
            public boolean verify(JComponent input) {
                boolean result = "aaa".equals(MyTextField.this.getText());

                if (!result) {
                    MyTextField.this.setBorder(BorderFactory.createLineBorder(Color.RED));
                    System.out.println("border set to RED for tf '" + MyTextField.this.getName() + "'");
                } else {
                    MyTextField.this.setBorder(MyTextField.this.originalBorder);
                    System.out.println("border set to original for tf '" + MyTextField.this.getName() + "'");
                }
                return result;
            }

            @Override
            public boolean shouldYieldFocus(JComponent input) {
                boolean shouldYieldFocus = super.shouldYieldFocus(input);
                System.out.println("shouldYieldFocus() ? " + shouldYieldFocus);
                return shouldYieldFocus;
            }
        });
    }

}

In general the framework does have issues with focus. I have not found good solutions for focus issues since years. I just would need a level of control that Swing does not offer me (or I haved missed to find).

The framework listens to MouseEvents (including the x button), and MouseEvents can be monitored even if a Component does not have the focus. As a result the framework does not know and is not influenced by your InputVerifyer.

As if that would not be enough: I guess a huge part of the issue is, that a stack will make the Dockables that are not visible “invisible” (using Component.setVisible). And since an invisible JTextField cannot have the focus, the InputVerifier gets ignored.

I can only offer you to have a look at the CVetoFocusListener and the CVetoClosingListener, both can be added to CControl - the CVetoClosingListener could also be added to a CDockable. With help of the first listener you can prevent a Dockable from loosing focus, with the second listener you can prevent a Dockable from being closed. Unfortunatelly the CVetoFocusListener might be too restrictive. But let me know if using these listeners could go in a direction that fits for your application, maybe with some more thinking this issue can be solved.

You can have a look at a prototype for a veto-listeners with this code, just call the “installOn” method.


import java.awt.Component;
import java.awt.Container;

import javax.swing.InputVerifier;
import javax.swing.JComponent;

import bibliothek.gui.dock.common.CControl;
import bibliothek.gui.dock.common.event.CVetoClosingEvent;
import bibliothek.gui.dock.common.event.CVetoClosingListener;
import bibliothek.gui.dock.common.event.CVetoFocusListener;
import bibliothek.gui.dock.common.intern.CDockable;

public class InputVerifyerVetoListeners implements CVetoClosingListener, CVetoFocusListener{
	public static void installOn(CControl control){
		InputVerifyerVetoListeners listener = new InputVerifyerVetoListeners();
		control.addVetoClosingListener(listener);
		control.addVetoFocusListener(listener);
	}
	
	@Override
	public void closed(CVetoClosingEvent event) {
		// that is ok	
	}
	
	@Override
	public void closing(CVetoClosingEvent event) {
		for( int i = 0, n = event.getDockableCount(); i<n; i++ ){
			if( !shouldYieldFocus( event.getDockable( i ))){
				event.cancel();
				break;
			}
		}
	}
	
	@Override
	public boolean willGainFocus(CDockable dockable) {
		// that is ok
		return true;
	}
	
	@Override
	public boolean willLoseFocus(CDockable dockable) {
		return shouldYieldFocus( dockable );
	}
	
	private boolean shouldYieldFocus( CDockable dockable ){
		return shouldYieldFocus( dockable.intern().getComponent() );
	}
	
	private boolean shouldYieldFocus( Component component ){
		if( component instanceof JComponent ){
			JComponent jcomponent = (JComponent)component;
			InputVerifier verifier = jcomponent.getInputVerifier();
			if( verifier != null ){
				if( !verifier.shouldYieldFocus( jcomponent )){
					return false;
				}
			}
		}
		
		if( component instanceof Container ){
			Container container = (Container)component;
			
			for( int i = 0, n = container.getComponentCount(); i<n; i++ ){
				if( !shouldYieldFocus( container.getComponent( i ))){
					return false;
				}
			}
		}
		
		return true;
	}
}

I did not get the exception with the code you posted. But then it is a strange exception, it tells that somehow a Dockable could not be removed from its parent - an operation that usually does not cause any problems. Are there any other exceptions? Or some code that tries to prevent the Dockable from beeing closed?

Thank you Beni,

I understand that focus is not an easy thing with Swing.

I am not sure, but I am probably going to remove the input verifiers, and manage form validation with my own key strokes listeners.

Concerning the code, you are right, it does not demonstrate the exception. My use case is a little bit more complicated with CVetoCloseListeners involved, but not preventing the tab from being closed. I am going to try to reproduce the exception in the sample code. With a little bit more work…

Here again !

I have almost found where the problem below started to happen:

Preview 1.1.2p2a is working correctly. The problem starts with 1.1.2p2b.

Concerning the initial problem, I got around. I prevent the tab from being closed when my editor is dirty, ie. when it contains unsaved changes. Because changes are committed when the field loses focus, I also had to make the close button not focusable.

But to do this, I had to upgrade to v 1.1.2p8e… You see the problem ? :smiley: I now have the exception from the previous post…

Some more clues:
[ul]
[li]the problem is happening when I have 2 tabs only
[/li][/ul]

I think that I have tracked down the problem… In the inner class called Listener in StackDockStation :

    private class Listener implements DockableListener{

SNIP

        public void titleTextChanged( Dockable dockable, String oldTitle, String newTitle ) {
            int index = indexOf( dockable );
            if( index >= 0 ){
                updateContent( index );
            }
        }

SNIP

I have 2 dockables… I do some modifications in dockable at index 1 and I close the other at index 0. The dockable which was at index 1, is now at index 0 and I have never asked for it to be removed. That’s why it still has a parent, leading to the exception.

May it be correct ?

If a Dockable on a stack is closed, and the number of children of said stack falls to 1, then the stack is removed, and the remaining child placed at the old position of the stack. This could lead to some focus issues. But that is all what I can read from the stacktrace. I do not know how the Listener fits into the picture.

Could you send me an application that reproduces the issue, so I can attach a debugger? I think that would be much easier than trying to guess what is wrong. You could send me a zip file to benjamin_sigg@gmx.ch , I promise not to use or publish your code.

Looking at the changes from 1.1.2p2b I did not see anything that looks really suspicious :frowning:

I really appreciate your help, and I would be glap to send the application, but I am not allowed to do so. I am realizing why you don’t understand why I am talking about this listener. Please let me give you some more clues.

In the previous example, in the class TabTest2 above, replace :

DefaultSingleCDockable d1 = createDockable("D1");
grid.add(0, 0, 1, 1, d1);
DefaultSingleCDockable d2 = createDockable("D2");
grid.add(0, 0, 1, 1, d2);

with :

DefaultSingleCDockable d1 = createDockable("D1");
grid.add(0, 0, 1, 1, d1);
DefaultSingleCDockable d2 = createDockable("D2");
grid.add(0, 0, 1, 1, d2);
DefaultSingleCDockable d3 = createDockable("D3");
grid.add(0, 0, 1, 1, d3);

Put the focus in a field in D3, and close D2. In the logs, you will see that the focus didn’t move. The field in D3 is not validated.
Now keep the focus in the same field in D3, and close D1. The focus goes through bibliothek.extension.gui.dock.theme.eclipse.displayer.EclipseDockableDisplayer and come back in the field in D3. The field in D3 is validated.

I have the same problem in my application, with a slight difference. When I close D1, the focus goes through bibliothek.gui.dock.StackDockStation$Background, before coming in bibliothek.extension.gui.dock.theme.eclipse.displayer.EclipseDockableDisplayer.

Now the important part. In my application, what’s happening when I close the equivalent of D1, is that data in D3 is committed which updates the title of the dockable (both text and icon).

Now see the stack trace below (not an exception, just a fragment of all the method calls) from my application with df v 1.1.2pe8. It corresponds to the moment when I have only 2 dockables, D1 and D3, with D3 containing a modification in one field:

  • I click on the close button of D1
  • The text field in D3 loses the focus, and the data is commited
  • Which results in the update of the title of the dockable
Thread [AWT-EventQueue-0] (Suspended (breakpoint at line 1680 in StackDockStation$Listener))	
	owns: Component$AWTTreeLock  (id=12629)	
	StackDockStation$Listener.titleTextChanged(Dockable, String, String) line: 1680	
	DefaultCommonDockable(AbstractDockable).fireTitleTextChanged(String, String) line: 522	
	AbstractDockable$1.valueChanged(String, String) line: 133	
	AbstractDockable$1.valueChanged(Object, Object) line: 124	
	AbstractDockable$1(PropertyValue<A>).setValue(A) line: 224	
	DefaultCommonDockable(AbstractDockable).setTitleText(String) line: 386	
	EditorDockableImpl(DefaultCDockable).setTitleText(String) line: 264	
	EditorDockableImpl(AbstractMultipleCDockable).setTitle(String) line: 167	

	[SNIP the code that listens the change in the text field and results in the title update]

	MyInputVerifier.verify(JComponent) line: 36	
	MyInputVerifier(InputVerifier).shouldYieldFocus(JComponent) line: 114	
	JComponent$1.acceptRequestFocus(Component, Component, boolean, boolean, CausedFocusEvent$Cause) line: 3535	
	StackDockStation$Background(Component).isRequestFocusAccepted(boolean, boolean, CausedFocusEvent$Cause) line: 7502	
	StackDockStation$Background(Component).requestFocusHelper(boolean, boolean, CausedFocusEvent$Cause) line: 7405	
	StackDockStation$Background(Component).requestFocus(boolean, CausedFocusEvent$Cause) line: 7277	
	Component.postNextFocusHelper(Component) line: 7681	
	MyTextField(Component).nextFocusHelper() line: 7646	
	MyTextField(Container).nextFocusHelper() line: 3154	
	MyTextField(Component).doAutoTransfer(boolean) line: 7574	
	MyTextField(Component).autoTransferFocus(boolean) line: 7566	
	JPanel(Component).autoTransferFocus(boolean) line: 7541	
	JPanel(Component).hide() line: 1617	
	JPanel(Component).show(boolean) line: 1586	
	JPanel(Component).setVisible(boolean) line: 1536	
	JPanel(JComponent).setVisible(boolean) line: 2612	
	EclipseTabPane(CombinedStackDockComponent<T,M,I>).setSelectedDockable(Dockable) line: 442	
	EclipseTabPane(AbstractTabPane<T,M,I>).remove(int) line: 301	
	EclipseTabPane(CombinedStackDockComponent<T,M,I>).remove(int) line: 523	
	StackDockStation.remove(int) line: 1503	
	StackDockStation.drag(Dockable) line: 1267	
	CSingleParentRemover(SingleParentRemover).test(DockStation) line: 184	
	CSingleParentRemover(SingleParentRemover).testAll(DockController) line: 99	
	SingleParentRemover$DockRegisterObserver.dockableUnregistered(DockController, Dockable) line: 218	
	DockRegister.fireDockableUnregistered(Dockable) line: 441	
	DockRegister.unregister(Dockable) line: 363	
	DockRegister$StationListener.removeDockable(Dockable) line: 723	
	DockRegister$StationListener.fire() line: 574	
	DockRegister.setStalled(boolean) line: 533	
	CControl$Access.hide(CDockable) line: 2761	
	EditorDockableImpl(AbstractCDockable).setVisible(boolean) line: 319	
	NotFocusableCloseAction(CCloseAction).close(CDockable) line: 96	
	NotFocusableCloseAction$1(CCloseAction$Action).close(Dockable) line: 119	
	NotFocusableCloseAction$1(CloseAction).action(Dockable) line: 128	
	BasicButtonHandler.triggered() line: 48	
	BasicButtonModel.trigger() line: 704	
	BasicButtonModel$Listener.mouseReleased(MouseEvent) line: 742	
	AWTEventMulticaster.mouseReleased(MouseEvent) line: 272	
	RoundRectButton(Component).processMouseEvent(MouseEvent) line: 6288	
	RoundRectButton(JComponent).processMouseEvent(MouseEvent) line: 3267	
	RoundRectButton(Component).processEvent(AWTEvent) line: 6053	
	RoundRectButton(Container).processEvent(AWTEvent) line: 2041	
	RoundRectButton(Component).dispatchEventImpl(AWTEvent) line: 4651	
	RoundRectButton(Container).dispatchEventImpl(AWTEvent) line: 2099	
	RoundRectButton(Component).dispatchEvent(AWTEvent) line: 4481	
	LightweightDispatcher.retargetMouseEvent(Component, int, MouseEvent) line: 4577	
	LightweightDispatcher.processMouseEvent(MouseEvent) line: 4238	
	LightweightDispatcher.dispatchEvent(AWTEvent) line: 4168	
	JFrame(Container).dispatchEventImpl(AWTEvent) line: 2085	
	JFrame(Window).dispatchEventImpl(AWTEvent) line: 2478	
	JFrame(Component).dispatchEvent(AWTEvent) line: 4481	
	EventQueue.dispatchEventImpl(AWTEvent, Object) line: 643	
	EventQueue.access$000(EventQueue, AWTEvent, Object) line: 84	
	EventQueue$1.run() line: 602	
	EventQueue$1.run() line: 600	
	AccessController.doPrivileged(PrivilegedAction<T>, AccessControlContext) line: not available [native method]	
	AccessControlContext$1.doIntersectionPrivilege(PrivilegedAction<T>, AccessControlContext, AccessControlContext) line: 87	
	AccessControlContext$1.doIntersectionPrivilege(PrivilegedAction<T>, AccessControlContext) line: 98	
	EventQueue$2.run() line: 616	
	EventQueue$2.run() line: 614	
	AccessController.doPrivileged(PrivilegedAction<T>, AccessControlContext) line: not available [native method]	
	AccessControlContext$1.doIntersectionPrivilege(PrivilegedAction<T>, AccessControlContext, AccessControlContext) line: 87	
	EventQueue.dispatchEvent(AWTEvent) line: 613	
	EventDispatchThread.pumpOneEventForFilters(int) line: 269	
	EventDispatchThread.pumpEventsForFilter(int, Conditional, EventFilter) line: 184	
	EventDispatchThread.pumpEventsForHierarchy(int, Conditional, Component) line: 174	
	EventDispatchThread.pumpEvents(int, Conditional) line: 169	
	EventDispatchThread.pumpEvents(Conditional) line: 161	
	EventDispatchThread.run() line: 122	

And it is followed by the exception I mentioned:

d [AWT-EventQueue-0] (Suspended)	
	owns: DockHierarchyLock$Token  (id=12702)	
	DockHierarchyLock.ensureUnlinked(DockStation, Dockable) line: 359	
	DockHierarchyLock.access$500(DockHierarchyLock, DockStation, Dockable) line: 45	
	DockHierarchyLock$Token.release() line: 420	
	StackDockStation.remove(int) line: 1536	
	StackDockStation.drag(Dockable) line: 1267	
	CSingleParentRemover(SingleParentRemover).test(DockStation) line: 184	
	CSingleParentRemover(SingleParentRemover).testAll(DockController) line: 99	
	SingleParentRemover$DockRegisterObserver.dockableUnregistered(DockController, Dockable) line: 218	
	DockRegister.fireDockableUnregistered(Dockable) line: 441	
	DockRegister.unregister(Dockable) line: 363	
	DockRegister$StationListener.removeDockable(Dockable) line: 723	
	DockRegister$StationListener.fire() line: 574	
	DockRegister.setStalled(boolean) line: 533	
	CControl$Access.hide(CDockable) line: 2761	
	EditorDockableImpl(AbstractCDockable).setVisible(boolean) line: 319	
	NotFocusableCloseAction(CCloseAction).close(CDockable) line: 96	
	NotFocusableCloseAction$1(CCloseAction$Action).close(Dockable) line: 119	
	NotFocusableCloseAction$1(CloseAction).action(Dockable) line: 128	
	BasicButtonHandler.triggered() line: 48	
	BasicButtonModel.trigger() line: 704	
	BasicButtonModel$Listener.mouseReleased(MouseEvent) line: 742	
	AWTEventMulticaster.mouseReleased(MouseEvent) line: 272	
	RoundRectButton(Component).processMouseEvent(MouseEvent) line: 6288	
	RoundRectButton(JComponent).processMouseEvent(MouseEvent) line: 3267	
	RoundRectButton(Component).processEvent(AWTEvent) line: 6053	
	RoundRectButton(Container).processEvent(AWTEvent) line: 2041	
	RoundRectButton(Component).dispatchEventImpl(AWTEvent) line: 4651	
	RoundRectButton(Container).dispatchEventImpl(AWTEvent) line: 2099	
	RoundRectButton(Component).dispatchEvent(AWTEvent) line: 4481	
	LightweightDispatcher.retargetMouseEvent(Component, int, MouseEvent) line: 4577	
	LightweightDispatcher.processMouseEvent(MouseEvent) line: 4238	
	LightweightDispatcher.dispatchEvent(AWTEvent) line: 4168	
	JFrame(Container).dispatchEventImpl(AWTEvent) line: 2085	
	JFrame(Window).dispatchEventImpl(AWTEvent) line: 2478	
	JFrame(Component).dispatchEvent(AWTEvent) line: 4481	
	EventQueue.dispatchEventImpl(AWTEvent, Object) line: 643	
	EventQueue.access$000(EventQueue, AWTEvent, Object) line: 84	
	EventQueue$1.run() line: 602	
	EventQueue$1.run() line: 600	
	AccessController.doPrivileged(PrivilegedAction<T>, AccessControlContext) line: not available [native method]	
	AccessControlContext$1.doIntersectionPrivilege(PrivilegedAction<T>, AccessControlContext, AccessControlContext) line: 87	
	AccessControlContext$1.doIntersectionPrivilege(PrivilegedAction<T>, AccessControlContext) line: 98	
	EventQueue$2.run() line: 616	
	EventQueue$2.run() line: 614	
	AccessController.doPrivileged(PrivilegedAction<T>, AccessControlContext) line: not available [native method]	
	AccessControlContext$1.doIntersectionPrivilege(PrivilegedAction<T>, AccessControlContext, AccessControlContext) line: 87	
	EventQueue.dispatchEvent(AWTEvent) line: 613	
	EventDispatchThread.pumpOneEventForFilters(int) line: 269	
	EventDispatchThread.pumpEventsForFilter(int, Conditional, EventFilter) line: 184	
	EventDispatchThread.pumpEventsForHierarchy(int, Conditional, Component) line: 174	
	EventDispatchThread.pumpEvents(int, Conditional) line: 169	
	EventDispatchThread.pumpEvents(Conditional) line: 161	
	EventDispatchThread.run() line: 122	

So what’s happening… In the first stack trace, it starts to remove the dockable. It goes to StackDockStation.remove(int) line: 1503 and does the specific job concerning the title update. Everything goes well. Then it continues the job after StackDockStation.remove(int) line: 1503 until it reaches line StackDockStation.remove(int) line: 1536 to end in the exception.

The last important thing to note, is that if I don’t update the icon and the title of the dockable, then I don’t get the exception.

The pity is that I just don’t manage to reproduce this in a simple example.

I think that the differences between 1.1.2p2a and 1.1.2p2b which are linked to the error are in commits 513b8f2cba42fb067914b38c69fc506f397c70ac and c750f9d9af60ba7d3e27f37c907846705d8a90c1

Those commits modify the listener I was talking above. I am not telling that they contain a bug, just that my code now hangs up. And I don’t understand why.

I just found that the exception I mentionned is just a side effect of another exception happening earlier when the dockable title is being set:

Thread [AWT-EventQueue-0] (Suspended)	
	owns: Component$AWTTreeLock  (id=214)	
	ArrayList<E>.RangeCheck(int) line: 547	
	ArrayList<E>.get(int) line: 322	
	EclipseTabPane(AbstractTabPane<T,M,I>).getDockable(int) line: 398	
	EclipseTabPane(CombinedStackDockComponent<T,M,I>).setTitleAt(int, String) line: 641	
	StackDockStation.updateContent(int) line: 1620	
	StackDockStation.access$300(StackDockStation, int) line: 134	
	StackDockStation$Listener.titleTextChanged(Dockable, String, String) line: 1682	
	DefaultCommonDockable(AbstractDockable).fireTitleTextChanged(String, String) line: 522	
	AbstractDockable$1.valueChanged(String, String) line: 133	
	AbstractDockable$1.valueChanged(Object, Object) line: 124	
	AbstractDockable$1(PropertyValue<A>).setValue(A) line: 224	
	DefaultCommonDockable(AbstractDockable).setTitleText(String) line: 386	
	EditorDockableImpl(DefaultCDockable).setTitleText(String) line: 264	
	EditorDockableImpl(AbstractMultipleCDockable).setTitle(String) line: 167	

	[SNIP the code that listens the change in the text field and results in the title update]
	
	MyInputVerifier.verify(JComponent) line: 36	
	MyInputVerifier(InputVerifier).shouldYieldFocus(JComponent) line: 114	
	JComponent$1.acceptRequestFocus(Component, Component, boolean, boolean, CausedFocusEvent$Cause) line: 3535	
	StackDockStation$Background(Component).isRequestFocusAccepted(boolean, boolean, CausedFocusEvent$Cause) line: 7502	
	StackDockStation$Background(Component).requestFocusHelper(boolean, boolean, CausedFocusEvent$Cause) line: 7405	
	StackDockStation$Background(Component).requestFocus(boolean, CausedFocusEvent$Cause) line: 7277	
	Component.postNextFocusHelper(Component) line: 7681	
	NoBorderTextField(Component).nextFocusHelper() line: 7646	
	NoBorderTextField(Container).nextFocusHelper() line: 3154	
	NoBorderTextField(Component).doAutoTransfer(boolean) line: 7574	
	NoBorderTextField(Component).autoTransferFocus(boolean) line: 7566	
	JPanel(Component).autoTransferFocus(boolean) line: 7541	
	JPanel(Component).hide() line: 1617	
	JPanel(Component).show(boolean) line: 1586	
	JPanel(Component).setVisible(boolean) line: 1536	
	JPanel(JComponent).setVisible(boolean) line: 2612	
	EclipseTabPane(CombinedStackDockComponent<T,M,I>).setSelectedDockable(Dockable) line: 442	
	EclipseTabPane(AbstractTabPane<T,M,I>).remove(int) line: 301	
	EclipseTabPane(CombinedStackDockComponent<T,M,I>).remove(int) line: 523	
	StackDockStation.remove(int) line: 1503	
	StackDockStation.drag(Dockable) line: 1267	
	CSingleParentRemover(SingleParentRemover).test(DockStation) line: 184	
	CSingleParentRemover(SingleParentRemover).testAll(DockController) line: 99	
	SingleParentRemover$DockRegisterObserver.dockableUnregistered(DockController, Dockable) line: 218	
	DockRegister.fireDockableUnregistered(Dockable) line: 441	
	DockRegister.unregister(Dockable) line: 363	
	DockRegister$StationListener.removeDockable(Dockable) line: 723	
	DockRegister$StationListener.fire() line: 574	
	DockRegister.setStalled(boolean) line: 533	
	CControl$Access.hide(CDockable) line: 2761	
	EditorDockableImpl(AbstractCDockable).setVisible(boolean) line: 319	
	NotFocusableCloseAction(CCloseAction).close(CDockable) line: 96	
	NotFocusableCloseAction$1(CCloseAction$Action).close(Dockable) line: 119	
	NotFocusableCloseAction$1(CloseAction).action(Dockable) line: 128	
	BasicButtonHandler.triggered() line: 48	
	BasicButtonModel.trigger() line: 704	
	BasicButtonModel$Listener.mouseReleased(MouseEvent) line: 742	
	AWTEventMulticaster.mouseReleased(MouseEvent) line: 272	
	RoundRectButton(Component).processMouseEvent(MouseEvent) line: 6288	
	RoundRectButton(JComponent).processMouseEvent(MouseEvent) line: 3267	
	RoundRectButton(Component).processEvent(AWTEvent) line: 6053	
	RoundRectButton(Container).processEvent(AWTEvent) line: 2041	
	RoundRectButton(Component).dispatchEventImpl(AWTEvent) line: 4651	
	RoundRectButton(Container).dispatchEventImpl(AWTEvent) line: 2099	
	RoundRectButton(Component).dispatchEvent(AWTEvent) line: 4481	
	LightweightDispatcher.retargetMouseEvent(Component, int, MouseEvent) line: 4577	
	LightweightDispatcher.processMouseEvent(MouseEvent) line: 4238	
	LightweightDispatcher.dispatchEvent(AWTEvent) line: 4168	
	JFrame(Container).dispatchEventImpl(AWTEvent) line: 2085	
	JFrame(Window).dispatchEventImpl(AWTEvent) line: 2478	
	JFrame(Component).dispatchEvent(AWTEvent) line: 4481	
	EventQueue.dispatchEventImpl(AWTEvent, Object) line: 643	
	EventQueue.access$000(EventQueue, AWTEvent, Object) line: 84	
	EventQueue$1.run() line: 602	
	EventQueue$1.run() line: 600	
	AccessController.doPrivileged(PrivilegedAction<T>, AccessControlContext) line: not available [native method]	
	AccessControlContext$1.doIntersectionPrivilege(PrivilegedAction<T>, AccessControlContext, AccessControlContext) line: 87	
	AccessControlContext$1.doIntersectionPrivilege(PrivilegedAction<T>, AccessControlContext) line: 98	
	EventQueue$2.run() line: 616	
	EventQueue$2.run() line: 614	
	AccessController.doPrivileged(PrivilegedAction<T>, AccessControlContext) line: not available [native method]	
	AccessControlContext$1.doIntersectionPrivilege(PrivilegedAction<T>, AccessControlContext, AccessControlContext) line: 87	
	EventQueue.dispatchEvent(AWTEvent) line: 613	
	EventDispatchThread.pumpOneEventForFilters(int) line: 269	
	EventDispatchThread.pumpEventsForFilter(int, Conditional, EventFilter) line: 184	
	EventDispatchThread.pumpEventsForHierarchy(int, Conditional, Component) line: 174	
	EventDispatchThread.pumpEvents(int, Conditional) line: 169	
	EventDispatchThread.pumpEvents(Conditional) line: 161	
	EventDispatchThread.run() line: 122	

This is an IndexOutOfBoundsException…

Sorry for all the noise that led to this. I was getting confused because I was not getting this exception in the log…

Well, that last Exception is much more interesting then the first one. I think this is enough information for me to reproduce the error, I’ll write if I find a bugfix (hopefully tomorrow).

That would be great ! Thank you !

I’ve uploaded version 1.1.2p8f (the code is also pushed onto Github). This should fix the exception, let me know when you run into more trouble.

I just remove this listener before playing removing the Dockables - there is no need to handle events when the Component is going to be removed anyway.

Thank you ! This is working correctly now !

The last thing is that when I close the penultimate dockable while the focus is in the dockable that won’t be closed, the focus goes through bibliothek.gui.dock.StackDockStation$Background and then through bibliothek.extension.gui.dock.theme.eclipse.displayer.EclipseDockableDisplayer before coming in the remaining dockable.

This is not happening when there are more than 2 opened dockables when I close one of them.

The code sample provided some days ago shows this. If both Background and EclipseDockableDisplayer were not focusable, the problem would be solved. But it might be the solution for me and not for other clients.

Any clue to realize this ?

I added an option to configure the “focusable” property, you will need to upgrade the framework once again. This time the version is 1.1.2p9.

To disable “focusable” on said two Components you should add this piece of code:

        CControl control = new CControl(frame);
       
        control.getController().getDockComponentManager().setDefaultConfiguration( new DockComponentConfiguration() {
			@Override
			public void unconfigure( DockComponentConfigurationEvent event ) {
				// ignore
			}
			
			@Override
			public void configure( DockComponentConfigurationEvent event ) {
				if( event.getRoot() instanceof StackDockStation ){
					if( event.getComponent() == ((StackDockStation)event.getRoot()).getComponent() ){
						event.getComponent().setFocusable( false );
					}
				}
				
				if( event.getRoot() instanceof EclipseDockableDisplayer ){
					if( event.getComponent() == ((EclipseDockableDisplayer)event.getRoot()).getComponent()){
						event.getComponent().setFocusable( false );
					}
				}
			}
		});```

This is a little bit an experiment... I hope this works out.

Thank you so much. Let me just a minute to test…

You did it. This is now working completely. Once again, thank you very muck :slight_smile:

You are welcome. And let me know, if you find any other issues :wink: