1. 要想搞清楚Eclipse的selection机制,首先要对Eclipse的WorkBench和它的结构有一个清晰的认识。
下面是我画的一张图,原图在这里: http://www.eclipse.org/articles/Article-UI-
Workbench/workbench.html
一般来说WorkBench有一个WorkBenchWindow,一个WorkBenchWindow有一个WorkBenchPage,一个WorkBenchPage有一个
EditManager,ViewFactory和多个Perspective,ViewFactory是通过引用计数来管理ViewReference的,只有引用计数为0时,对应的View才会被dispose掉。
2. 什么是part
WorkBenchPage中的所有Editor和View是part. 见下图:
ViewReference和EditorReference都继承自WorkbenchPartReference,WorkbenchPartReference有对应的WorkbenchPart和PartPane;
因为Eclipse是lazy初始化的,所有有时WorkbenchPartReference中的WorkbenchPart是空;
PartPane是用来管理给part绘制界面组件的;
PartPane通过给control添加SWT.Activate事件来更新page的active part;
org.eclipse.ui.internal.PartPane的handleEvent方法:
public void handleEvent(Event event) {
if (event.type == SWT.Activate) {
if (inLayout) {
requestActivation();
}
}
}
处理Activate事件时,导致setActivePart被调用,来更新activeProvider和它们的listener;
org.eclipse.ui.internal.AbstractSelectionService的setActivePart方法:
/**
* Sets the current-active part (or null if none)
*
* @since 3.1
*
* @param newPart the new active part (or null if none)
*/
public void setActivePart(IWorkbenchPart newPart) {
// Optimize.
if (newPart == activePart) {
return;
}
ISelectionProvider selectionProvider = null;
if (newPart != null) {
selectionProvider = newPart.getSite().getSelectionProvider();
if (selectionProvider == null) {
newPart = null;
}
}
if (newPart == activePart) {
return;
}
if (activePart != null) {
if (activeProvider != null) { **//清除旧的SP中的listener**
activeProvider.removeSelectionChangedListener(selListener);
if (activeProvider instanceof IPostSelectionProvider) {
((IPostSelectionProvider) activeProvider)
.removePostSelectionChangedListener(postSelListener);
} else {
activeProvider
.removeSelectionChangedListener(postSelListener);
}
activeProvider = null;
}
activePart = null;
}
activePart = newPart;
if (newPart != null) {
activeProvider = selectionProvider;
// Fire an event if there's an active provider
activeProvider.addSelectionChangedListener(selListener); **//将listener加入新的SP**
ISelection sel = activeProvider.getSelection();
fireSelection(newPart, sel);
if (activeProvider instanceof IPostSelectionProvider) {
((IPostSelectionProvider) activeProvider)
.addPostSelectionChangedListener(postSelListener);
} else { **//这里的逻辑是不是有问题,如果对应的SP不是IPostSelectionProvider类型,将postSelListener加入SP** ?
activeProvider.addSelectionChangedListener(postSelListener);
}
firePostSelection(newPart, sel);
} else {
fireSelection(null, null);
firePostSelection(null, null);
}
}
selListener和postSelListener的定义如下:
/**
- The JFace selection listener to hook on the active part’s selection provider.
*/
private ISelectionChangedListener selListener = new
ISelectionChangedListener() {
public void selectionChanged(SelectionChangedEvent event) {
fireSelection(activePart, event.getSelection());
}
};
/**
- The JFace post selection listener to hook on the active part’s selection provider.
*/
private ISelectionChangedListener postSelListener = new
ISelectionChangedListener() {
public void selectionChanged(SelectionChangedEvent event) {
firePostSelection(activePart, event.getSelection());
}
};
3. WorkBenchWindow和WorkBenchPage的关系,见下图:
上面已经说过,一个WorkBenchWindow一般有一个WorkBenchPage。
从上图可以看出:
WorkBenchWindow通过busyOpenPage来创建WorkBenchPage;
WorkBenchWindow和WorkBenchPage有自己的SelectionService,但是它们都是继承自org.eclipse.ui.internal.AbstractSelectionService的;
WorkBenchWindow的WWinPartService通过给WorkBenchPage的ParService添加或者删除WWinListener,来和响应Part相关的事件;
WorkBenchWindow的SelectionService叫做WindowSelectionService;
WorkBenchPage的SelectionService叫做PageSelectionService;
SlectionService有多应全局的ListenerList(listeners)和每个part的ListenerList(PagePartSelectionTracker)
4. selection是如何工作的,加下图:
简单的说,当你在且画Editor或者Viewer的时候,WorkbenchPage会通过setActivePart方法,将selListener和postSelListener从老的SP中移除;
然后加入到新的SP中去,然后当SP中有selection改变的时候,SP会通知所有的listener。
5. WorkbenchWindow和WorkbenchPage的SelectionService不一样,那么如何给它们追加listener呢?
getSite().getWorkbenchWindow().getSelectionService().addSelectionListener(l);
给Window的SelectionService追加listener
getSite().getPage().addSelectionListener(l); 给Page的SelectionService追加listener
6. PropertyView是如何响应Selection改变的
org.eclipse.ui.views.properties.PropertySheet通过
public void init(IViewSite site) throws PartInitException {
site.getPage().addPostSelectionListener(this);
super.init(site);
}
将机制注册到Page的selection完成队列中,如果有selection改变完成的话,Page会通知PropertySheet来更新自己的。
Note:
1. 加下图,分别是Window和Page的SelectionService中listener个数,可以看到它们两个的个数分别是70和1,
Windows:
Page:
2. 每个Part都要对应的PartSite
3. 每个Part都有可能对应一个SelectionTracker
4. 在PartPane中来处理SWT.Activate事件,来更新Page中的SP
5. 有两个SelectionService,一个是Page的,一个是Window的
6. 不要混淆Page和Window的SelectionService
7. setSelectionProvider只能在 createPartControl()
被调用,之后的调用不会生效;因为没有时机给page去更新SP
8. 如果你的Part有不同的SP,你可能需要在自己写的SP中进行以下trick的处理,可以参见这里:
www.eclipse.org/articles/Article-
WorkbenchSelections/SelectionProviderIntermediate.java
参考:
http://www.eclipse.org/articles/Article-WorkbenchSelections/article.html
http://www.eclipse.org/articles/Article-UI-Workbench/workbench.html
http://wiki.eclipse.org/FAQ_Pages,_parts,_sites,_windows:_What_is_all_this_stuff%3F