Some Eclipse Foundation services are deprecated, or will be soon. Please ensure you've read this important communication.
Bug 60970 - [EditorMgmt] setActive() called on an editor when it does not have focus
Summary: [EditorMgmt] setActive() called on an editor when it does not have focus
Status: RESOLVED FIXED
Alias: None
Product: Platform
Classification: Eclipse Project
Component: UI (show other bugs)
Version: 3.0   Edit
Hardware: PC Windows XP
: P2 major (vote)
Target Milestone: 3.0 M9   Edit
Assignee: Nick Edgar CLA
QA Contact:
URL:
Whiteboard:
Keywords:
: 56361 61289 63060 (view as bug list)
Depends on:
Blocks: 54296 56361
  Show dependency tree
 
Reported: 2004-05-04 17:35 EDT by Ines Khelifi CLA
Modified: 2004-05-21 09:55 EDT (History)
7 users (show)

See Also:


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Ines Khelifi CLA 2004-05-04 17:35:38 EDT
Steps:
- Import "org.eclipse.ui.workbench".
- Add a print statement to
"WorkbenchPage#ActivationList#setActive(IWorkbenchPart part)".
- Run as run-time workbench.
- Switch to Java perspective.
- Import a project.
- Open a Java file.
- Select the "Hierarchy" view, then the "Package Explorer" (give focus).
- Notice that switching to the "Package Explorer" view calls setActive() on the
open file before calling setActive() on that view.

This behavior is affecting the "Ctrl+F7" behavior by altering the ordering of
the items in that list.

I am using:
Eclipse Platform
Version: 3.0.0
Build id: 200405040800

I also tried this in 2.1 and M8, and everything worked fine. No unnecessary
“setActive()” calls were being made.
Comment 1 Michael Fraenkel CLA 2004-05-04 23:40:42 EDT
This problem is caused by hiding the control with focus and then calling 
Control.fixFocus().  The algorithm to select the next control to gain focus is 
causing the editor to gain focus since its the first focusable child from the 
same parent.
Comment 2 Nick Edgar CLA 2004-05-05 09:52:10 EDT
Michael's analysis is correct.  
The new focus handling is causing grief for workbench part activation.
You can see this if you add the following to WorkbenchPage.setActivePart, after
the initial == check:
    System.err.println("activating " + (newPart == null ? null :
newPart.getTitle()));
    Thread.dumpStack();

Concrete steps are:
- in new Java perspective,
- open some editor
- activate Package Explorer
- click on Hierarchy view tab
- workbench hides the Package Explorer's control
- fixFocus gives focus to the editor
- workbench picks this up as an editor part activation  
  *** previous 2 steps did not happen before
- workbench activates Hierarchy

The stack trace for the editor activation is:

activating .classpath
java.lang.Exception: Stack trace
	at java.lang.Thread.dumpStack(Thread.java:1064)
	at org.eclipse.ui.internal.WorkbenchPage.setActivePart(WorkbenchPage.java:2569)
	at org.eclipse.ui.internal.WorkbenchPage.requestActivation(WorkbenchPage.java:2307)
	at org.eclipse.ui.internal.PartPane.requestActivation(PartPane.java:246)
	at org.eclipse.ui.internal.EditorPane.requestActivation(EditorPane.java:150)
	at org.eclipse.ui.internal.PartPane.handleEvent(PartPane.java:226)
	at org.eclipse.swt.widgets.EventTable.sendEvent(EventTable.java:82)
	at org.eclipse.swt.widgets.Widget.sendEvent(Widget.java:796)
	at org.eclipse.swt.widgets.Widget.sendEvent(Widget.java:820)
	at org.eclipse.swt.widgets.Widget.sendEvent(Widget.java:801)
	at org.eclipse.swt.widgets.Shell.setActiveControl(Shell.java:913)
	at org.eclipse.swt.widgets.Control.sendFocusEvent(Control.java:1756)
	at org.eclipse.swt.widgets.Control.WM_SETFOCUS(Control.java:4120)
	at org.eclipse.swt.widgets.Canvas.WM_SETFOCUS(Canvas.java:239)
	at org.eclipse.swt.widgets.Control.windowProc(Control.java:3012)
	at org.eclipse.swt.widgets.Display.windowProc(Display.java:3160)
	at org.eclipse.swt.internal.win32.OS.SetFocus(Native Method)
	at org.eclipse.swt.widgets.Control.forceFocus(Control.java:599)
	at org.eclipse.swt.widgets.Control.setFocus(Control.java:2037)
	at org.eclipse.swt.widgets.Composite.setFocus(Composite.java:447)
	at org.eclipse.swt.widgets.Composite.setFocus(Composite.java:445)
	at org.eclipse.swt.widgets.Composite.setFocus(Composite.java:445)
	at org.eclipse.swt.widgets.Composite.setFocus(Composite.java:445)
	at org.eclipse.swt.widgets.Composite.setFocus(Composite.java:445)
	at org.eclipse.swt.widgets.Composite.setFocus(Composite.java:445)
	at org.eclipse.swt.widgets.Composite.setFocus(Composite.java:445)
	at org.eclipse.swt.widgets.Composite.setFocus(Composite.java:445)
	at org.eclipse.swt.widgets.Control.fixFocus(Control.java:560)
	at org.eclipse.swt.widgets.Control.setVisible(Control.java:2402)
	at org.eclipse.ui.internal.LayoutPart.setVisible(LayoutPart.java:240)
	at org.eclipse.ui.internal.PartPane.setVisible(PartPane.java:259)
	at org.eclipse.ui.internal.ViewPane.setVisible(ViewPane.java:630)
	at
org.eclipse.ui.internal.PresentableViewPart.setVisible(PresentableViewPart.java:112)
	at
org.eclipse.ui.internal.presentations.BasicStackPresentation.selectPart(BasicStackPresentation.java:790)
	at
org.eclipse.ui.internal.PartStack.refreshPresentationSelection(PartStack.java:666)
	at org.eclipse.ui.internal.PartStack.setSelection(PartStack.java:649)
	at org.eclipse.ui.internal.PartTabFolder.setSelection(PartTabFolder.java:108)
	at
org.eclipse.ui.internal.PartStack.presentationSelectionChanged(PartStack.java:457)
	at org.eclipse.ui.internal.PartStack.access$0(PartStack.java:447)
	at org.eclipse.ui.internal.PartStack$1.selectPart(PartStack.java:77)
	at
org.eclipse.ui.internal.presentations.BasicStackPresentation$4.handleEvent(BasicStackPresentation.java:148)
	at org.eclipse.swt.widgets.EventTable.sendEvent(EventTable.java:82)
	at org.eclipse.swt.widgets.Widget.sendEvent(Widget.java:796)
	at org.eclipse.swt.widgets.Widget.sendEvent(Widget.java:820)
	at org.eclipse.swt.widgets.Widget.sendEvent(Widget.java:805)
	at org.eclipse.swt.widgets.Widget.notifyListeners(Widget.java:613)
	at org.eclipse.swt.custom.CTabFolder.setSelection(CTabFolder.java:2966)
	at org.eclipse.swt.custom.CTabFolder.onMouse(CTabFolder.java:1855)
	at org.eclipse.swt.custom.CTabFolder$1.handleEvent(CTabFolder.java:288)
	at org.eclipse.swt.widgets.EventTable.sendEvent(EventTable.java:82)
	at org.eclipse.swt.widgets.Widget.sendEvent(Widget.java:796)
	at org.eclipse.swt.widgets.Display.runDeferredEvents(Display.java:2594)
	at org.eclipse.swt.widgets.Display.readAndDispatch(Display.java:2272)
	at org.eclipse.ui.internal.Workbench.runEventLoop(Workbench.java:1351)
	at org.eclipse.ui.internal.Workbench.runUI(Workbench.java:1322)
	at org.eclipse.ui.internal.Workbench.createAndRunWorkbench(Workbench.java:241)
	at org.eclipse.ui.PlatformUI.createAndRunWorkbench(PlatformUI.java:140)
	at org.eclipse.ui.internal.ide.IDEApplication.run(IDEApplication.java:90)
	at
org.eclipse.core.internal.runtime.PlatformActivator$1.run(PlatformActivator.java:283)
	at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:242)
	at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:119)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
	at
sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
	at java.lang.reflect.Method.invoke(Method.java:324)
	at org.eclipse.core.launcher.Main.basicRun(Main.java:269)
	at org.eclipse.core.launcher.Main.run(Main.java:700)
	at org.eclipse.core.launcher.Main.main(Main.java:684)
Comment 3 Steve Northover CLA 2004-05-05 10:04:26 EDT
Where should focus be assigned when a control is hidden?  Originally (way back 
when before 2.0?), we did not attempt to fix the focus.  Some platforms fixed 
focus and others did not, causing portablility problems.  Not sure how to 
proceed but I am open to suggestions.
Comment 4 Nick Edgar CLA 2004-05-05 10:13:22 EDT
I think keeping focus unchanged is preferable.  If the app is hiding/showing
controls, it will need to adjust focus itself, but should not get free focus
changes.  Not sure what algorithm the other platforms follow.
Comment 5 Michael Fraenkel CLA 2004-05-05 10:21:46 EDT
This is where AWT struggled for years and ended up with some 
complicated/"corrective" solution in JDK 1.4 and further in 1.5.  They have 
written a lot of info around this whole focus management issue.  What is 
interesting is that SWT is relying on the OS to have the correct order whereas 
AWT relies on its own hierarchy and policy.  This isn't going to be an easy 
problem to solve in the short term.  Just my $0.02.
Comment 6 Steve Northover CLA 2004-05-05 11:09:39 EDT
The fact that we "fix focus" is relied upon in Eclipse so we can't take the 
code out.  Originally, I was against doing this but there were just too many 
problems and the code went in (keys going nowhere etc.).

If the application knows where focus is supposed to go, can it set focus 
before hiding the control?
Comment 7 Nick Edgar CLA 2004-05-05 11:57:38 EDT
Does a programmatic change to focus fire focus changed or activation listeners?
If so, then we'd have to remove such listeners, otherwise the workbench would
issue part activation callbacks before part deactivation.
Comment 8 Nick Edgar CLA 2004-05-06 16:07:27 EDT
*** Bug 61289 has been marked as a duplicate of this bug. ***
Comment 9 Nick Edgar CLA 2004-05-06 16:08:47 EDT
Steve, we need to sit down and sort this out.
Comment 10 Steve Northover CLA 2004-05-06 16:20:04 EDT
A programatic change of focus fires the listeners.

Something to think about: Is the problem specific to Eclipse (and SWT) or is 
it a general UI problem.  For example, Motif will "fix focus" for you when you 
hide a widget.  How are Motif C programs supposed to work around this?  Talk 
to you tomorrow. 
Comment 11 Nick Edgar CLA 2004-05-07 11:25:20 EDT
I'm also wondering why this is only showing up now.
If the fixFocus change was made prior to M8, then this must be due to a
workbench change.  Or were there changes in this area in SWT in the last couple
of weeks?
Comment 12 Nick Edgar CLA 2004-05-11 17:00:35 EDT
The fixFocus() behaviour in SWT existed even in 2.1.

We did not encounter the problem in 2.1 because when clicking on a view's tab,
CTabFolder's mouse down handler would force focus to the CTabFolder before
firing the selection event.  So when the workbench hid the previously selected
part, it was not the focus control.

Testing on 2.1, there were cases where the problem was hit even there, e.g. when
opening or closing a view, we just didn't notice because views are opened/closed
much less frequently than switching between them.  I did notice a flicker of the
editor tab when closing a view though (when the previously activated part was
also a view).

I've looked into whether we can avoid fixFocus() by changing the order of
setVisible calls, but it's trickier than that since the order would have to be:
- setVisible(true) on new control
- setFocus() on new control
- setVisible(false) on old control

This is not only difficult to achieve given the way the responsibilities are
split between presentation and workbench, it would also affect when workbench
part lifecycle notifications are fired (see IPartListener2).

Instead, I've put in a workaround in LayoutPart.setVisible.  If the argument is
false, it first forces focus on the shell.  This seems safer than forcing focus
on the container's control since the container may be going away too.

This fixes the editor activation problem for all 3 cases: switching between
views in the same folder, showing a view in the same folder as the active one,
and closing a view when the previous active part was in the same folder.
This also reduces flicker.


Comment 13 Nick Edgar CLA 2004-05-11 17:05:20 EDT
*** Bug 56361 has been marked as a duplicate of this bug. ***
Comment 14 Steve Northover CLA 2004-05-11 17:08:25 EDT
This looks like the right code:

- setVisible(true) on new control
- setFocus() on new control
- setVisible(false) on old control

Veronika, was this changed for accessibility?  I thought that CTabFolder was 
supposed to forceFocus() to its tabs when it got a mouse down.  Please don't 
change the code back without consulting Nick.  Thanks.
Comment 15 Matthew Hatem CLA 2004-05-11 18:27:36 EDT
I was able to workaround my problems with the following change in selectPart in 
my stackPresentation class.  It's not the best solution but it works for now.


	public void selectPart(IPresentablePart toSelect) {
		if (toSelect == current) {
			return;
		}
		
		tabFolder.setFocus();
		final IPresentablePart old = current;
		Display.getDefault().asyncExec(new Runnable() {
			public void run() {
				if (old != null) {
					old.setVisible(false);
				}
			}
		});
		
		current = toSelect;
		
		if (current != null) {
			tabFolder.setSelection(indexOf(current));
			setControlSize();
			current.setVisible(true);
			tabFolder.setFocus();
		}
	}
Comment 16 Veronika Irvine CLA 2004-05-12 09:06:26 EDT
For accessibility, the following changed in CTabFolder:

On mouse down, a Selection event is sent.  This used to be followed by a call 
to setFocus().  For accessibility, the setFocus call was removed.  

Note that mouse down automatically gives focus to the CTabFolder.  If the 
content of the CTabItems are set, the CTabFolder makes the content of the 
selected tab visible and then hides the content of the previously selected 
tab.  When setVisible(false) is called on the previously selected content, 
focus automatically goes to the newly selected content (due to behaviour of 
setVisible(false)).

In addition, for accessibility, on page traversal and arrow traversal, a 
forceFocus is called to keep the focus in the tabs of the CTabFolder.
Comment 17 Nick Edgar CLA 2004-05-12 11:50:47 EDT
How does the mouse down automatically give focus to the CTabFolder?
In the 2.1 code, there was an explicit forceFocus before the selection event
notification.  I don't see this in the 3.0 code.
Comment 18 Nick Edgar CLA 2004-05-12 11:53:26 EDT
Matt, in your patch, doing the setFocus or forceFocus before setVisible(false)
should work.  You shouldn't need the asyncExec.
Comment 19 Nick Edgar CLA 2004-05-12 11:55:10 EDT
The asyncExec may have bad side effects on the order of workbench part
visibility notifications as well.  It's also possible for the part to have been
disposed by the time the asyncExec gets to run, which may cause widget is
disposed exceptions.
Comment 20 Veronika Irvine CLA 2004-05-12 11:59:20 EDT
LRESULT WM_LBUTTONDOWN (int wParam, int lParam) {
	LRESULT result = super.WM_LBUTTONDOWN (wParam, lParam);

	/* Set focus for a canvas with no children */
	if ((state & CANVAS) != 0) {
		if ((style & SWT.NO_FOCUS) == 0 && hooksKeys ()) {
			if (OS.GetWindow (handle, OS.GW_CHILD) == 0) setFocus 
();
		}
	}
	return result;
}

In 2.1, the UI did not create children of the CTabFolder.  In recent 3.0 
builds, it appears that a child is being created (ViewForm).
Comment 21 Matthew Hatem CLA 2004-05-12 12:03:25 EDT
Just realized the asyncExec was left overs from some trial and error.  Thanks 
for the heads up.
Comment 22 Nick Edgar CLA 2004-05-17 16:48:35 EDT
I've changed the workaround in LayoutPart.setVisible from:
		    if (!makeVisible) {
		        ctrl.getShell().forceFocus();
to:
		    if (!makeVisible && isFocusAncestor(ctrl)) {
		        ctrl.getShell().forceFocus();

since there were cases where non-active stack was changing the visible part, and
warping focus to the shell due to the workaround.

Matt, you should also add this check.
Comment 23 Ines Khelifi CLA 2004-05-18 16:44:53 EDT
I have verified that "WorkbenchPage#ActivationList#setActive(IWorkbenchPart
part)" does not get called on editors when navigating through views.

I am using Build id: 200405181200 on Mac OS X and Windows XP.
Comment 24 Ines Khelifi CLA 2004-05-19 11:27:58 EDT
Verified using Build id: 200405190010 on Windows XP and Mac OS X.
Comment 25 Nick Edgar CLA 2004-05-20 00:05:47 EDT
Reopening since I had to back out of the change in comment #22 since it was
causing failures in the DnD test suite's calls to testInvariants.
But we now believe the change is OK, so I've re-released it and commented out
the check in PartStack.testInvariants.
Comment 26 Nick Edgar CLA 2004-05-20 00:06:46 EDT
Closing.
The isFocusAncestor check will be in I20040520-0010.
Comment 27 Nick Edgar CLA 2004-05-20 00:08:41 EDT
Without the isFocusAncestor check, it was causing unwanted activations when
openEditor with activate=false was called, such as when selecting different
stack frames in the debugger when the editor could not be reused, e.g. when
crossing between .java and .class files.

Comment 28 Darin Wright CLA 2004-05-20 13:39:48 EDT
*** Bug 63060 has been marked as a duplicate of this bug. ***
Comment 29 Ines Khelifi CLA 2004-05-21 09:55:09 EDT
I have verified that "setActive" gets called once per view/editor, with Build
id: 200405210800.  Verified on Windows XP and Mac OS 10.3.3.