将JConsole移植到Eclipse平台(1)--实现基于SWT的MDI风格界面

JConsole是一个基于JMX的GUI工具,用于连接正在运行的JVM,它是一个MDI风格的Java桌面应用。SWing实现MDI风格界面的基础是JInternalFrame和JDesktopPane。那么如何基于SWT实现MDI风格界面呢?Eclipse平台中我们找不到这样的实现做参考(如果哪位网友发现有个非常好的这类实现一定要告诉我),但实际上SWT还是提供了类似的实现基础---那就是Decorations。从下图中的类继承关系看,既然Decorations是一个Control,而且没有说它不可以嵌到其他Composite中去,那它就应该可以(它的子类Shell明确声明为不可以)。

 

将JConsole移植到Eclipse平台(1)--实现基于SWT的MDI风格界面_第1张图片

创建一个View作为主界面是个理所当然的选择,使用ViewMenu和ToolBar实现Sun Jconsole提供的菜单项(Help除外):

  • 创建一个JMX连接
  • 层铺所有内部窗口
  • 平铺所有内部窗口
  • 最小化所有内部窗口
  • 恢复所有内部窗口到normal状态
  • 将某个内部窗口切换到最前面。

下图为Sun的界面与本人实现的界面对比图:

 

 将JConsole移植到Eclipse平台(1)--实现基于SWT的MDI风格界面_第2张图片

对于目前实现的demo来说最重要的2个类就是MyJConsoleView和VMInternalFrame,其中VMInternalFrame继承Decorations并于Sun JConsole中的命名保持一致,MyJConsoleView的菜单中固定不变部分定义在plugin.xml中,而随着内部窗口增加、删除,对应的菜单项也将动态加到菜单中或从菜单中删除。

private Map<VMInternalFrame, AboveFrameAction> windows = 
		new LinkedHashMap<VMInternalFrame, AboveFrameAction>();
	public MyJConsoleView() {
	}

	@Override
	public void createPartControl(Composite parent) {
		container = new Composite(parent, SWT.NONE);
		initializeMenu();
		setPartName("Java Monitoring & Management Console");
	}

	private void initializeMenu() {
		final IMenuManager manager = getViewSite().getActionBars().getMenuManager();
		manager.add(new Separator());
		manager.add(new Separator("layout"));
		manager.add(new Separator());
		manager.add(new Separator("connections"));
	}
	
	private void refeshViewMenu(){
		final IMenuManager manager = getViewSite().getActionBars().getMenuManager();
		IContributionItem[] items = manager.getItems();
		for (int i = 0; i < items.length; i++) {
			if(items[i] instanceof ActionContributionItem){
				IAction action = ((ActionContributionItem)items[i]).getAction();
				if(!(action instanceof IPluginContribution)){
					manager.remove(items[i]);
				}
			}
		}
		for (VMInternalFrame frame : windows.keySet()) {
			manager.appendToGroup("connections", windows.get(frame));
		}
	}

 

对于MDI风格界面层铺和平铺布局至关重要,本实例中采用了和Sun JConsole相类似的布局算法。

private void cascadeWindows(){
		int num = windows.size();
		if(num > 0){
			Point childSize = windows.keySet().iterator().next().getPreferredSize();
			Point parentSize = container.getSize();
			int dx = (parentSize.x - childSize.x)/(num==1?1:num - 1);
			int dy = (parentSize.y - childSize.y)/(num==1?1:num - 1);
			int i=0;
			for (VMInternalFrame frame : windows.keySet()) {
				frame.setToAbove();
				frame.setBounds(dx*i, dy*i, childSize.x, childSize.y);
				i++;
			}
		}
	}
	
	private void tileWindows(){
		int num = windows.size();
		int rows = (int)Math.ceil(Math.sqrt(num));
	    int cols = num / rows;
	    if (rows * cols < num) cols++;
	    int x = 0;
	    int y = 0;
	    Point parentSize = container.getSize();
	    int width = parentSize.x/cols;
	    int height = parentSize.y/rows;
	    int col = 0;
	    for (VMInternalFrame frame : windows.keySet()) {
	    	frame.setToAbove();
	    	frame.setBounds(x, y, width, height);
	    	frame.setMaximized(num==1);
			if (col < cols-1) {
				col++;
				x += width;
			} else {
				col = 0;
				x = 0;
				y += height;
			}
		}
	}

 

对于MDI界面,当有内部窗口处于最大化状态时,如果外部窗体(我们这就是view)的大小发生变化或最大化时,这个内部窗口也应该随之改变大小。为此VMInternalFrame实现了ControlListener用于监听view的变化。

@Override
	public void controlResized(ControlEvent e) {
		final Control control = (Control)e.getSource();
		if(control == getParent() && isMax){
			isMax = false;
			removeControlListener(controlListener);
			setMaximized(false);
			getShell().getDisplay().asyncExec(new Runnable() {
				public void run() {
					setSize(new Point(control.getSize().x, control.getSize().y));
					setMaximized(true);
					setFocus();
					addControlListener(controlListener);
					isMax = true;
	            }
	        });
		}
	}

 

在controlResized()方法中式所以用到了asyncExec是因为如果不这样当view最大化时,处于最大化状态的内部窗口并不随之变化。而定义变量isMax的原因是getMaximized()方法有问题,当view最大化时,处于normal状态的内部窗口的getMaximized()也返回true(我没有细究其中的原因,如果哪位细心的网友发现原因别忘了告诉我)。

注意:继承Decorations的类必须重载checkSubclass (),否则SWT验证通不过。

问题:

Decorations实现的内部窗口存在一些问题,有些甚至是目前无法解决的,以至于eclipse官方不建议使用Decorations。我发现2个比较重要的问题:

    1. Decorations在创建后style无法变更,因此不能在最大化时与主窗口融合。

    2. 因为在View中创建了Decorations,在退出eclipse时需要在“Confirm Exit”对话框上多点几下才能获取焦点,直接点“OK”按钮根本没反应。

网上有人建议使用ViewForm来实现内部窗口,但找不到一个像样的实例,用它模拟出内部窗口所具有的全部特性肯定需要费些周折。接下来我将使用Decorations先实现JConsole的基本功能(1.0),而在2.0中采用ViewForm模拟内部窗口。

 关于源代码的声明:

本实例源代码中有一部分可能直接来自SUN JConsole,因此如果用于商业目的而引起知识产权纠纷,本人概不负责。另外所用图标均非本人创建,在此向图标设计者表示感谢。

你可能感兴趣的:(eclipse,算法,windows,swing,sun)