對于Eclipse開發(fā)者來說,不管Plug-in還是RCP免不了要和SWT打交道,但兩者似乎有些不同,Plug-in主要跟Eclipse過招,開發(fā)更多是上層應用如UI/JDT/EMF/GMF等,而RCP像是應用程序,時不時與圖形系統(tǒng)交互,需求更是五花八門,舉幾例:
- RCP界面要跟Office 2007/Vista一樣。
- 我喜歡上一VC版的水晶按鈕。
- 商業(yè)版控件支持。
- 我的RCP程序要定時啟動。
這些效果讓RCP下的SWT越來越跟平臺相關,但矛盾的是SWT要跨平臺,提供的API只能是一個平衡產(chǎn)品+少許補充,更多特性依賴我們對SWT進行擴展。比較常見的是對SWT Win32 API進行擴展,因為Windows的圖形特性太豐富了,不用白不用啊。
下面就以SWT win32 x86來演示一下這個擴展過程。擴展很簡單,就是在swt的窗口上加一個自定義系統(tǒng)菜單,最終效果如下圖:
首先要有SWT JNI源代碼,在Eclipse plug-in目錄下找到org.eclipse.swt.win32.win32.x86.source_3.X.X.vXXX.jar(xx為版本號),解壓縮到c:\build\swt-jni,解完后在會發(fā)現(xiàn)有一堆h和c文件,其中比較重要的就是os, gdip, xpcom,wgl,awt,用途分別如下:
- os: 主要的JNI,用來創(chuàng)建控件,事件處理等。
- gdip: 與windows的dc交互,提供swt的gc畫圖功能。
- xpcom: 供swt調(diào)用mozilla系列瀏覽器如firefox等。
- wgl: 與3D相關,一般都用不上。
- awt:在swt中調(diào)用awt,awt也是jni。
了解swt源代碼之后,接下來為build swt做準備。
- 安裝vc6,嘗試過用高版本,但不支持。
- 安裝platform SDK 2003 February版,下載地址 遵照安裝說明將SDK解壓縮安裝。不要嘗試更新版本,不支持。
- 安裝jdk,這個無所謂,1.4, 1.5, 1.6都可以。
- 下載gecko-sdk(下載地址),請務必使用1.8,swt目前不完全支持1.9。
所以的工具都安裝或解壓之后,在swt源代碼目錄下找到build.bat,在:X86 label部分做如下配置,其中配置的目錄視你的安裝而定:
1
:X86
2
3
IF x.%DEV_TOOLS%==x. set DEV_TOOLS=c:\PROGRA~1
4
call %DEV_TOOLS%\MICROS~2\vc98\bin\vcvars32.bat
5
IF x.%MSSDK%==x. set MSSDK=%DEV_TOOLS%\MICROS~3
6
call %MSSDK%\setenv /XP32 /RETAIL
7
IF x.%OUTPUT_DIR%==x. set OUTPUT_DIR=..\out
8
IF x.%JAVA_HOME%==x. set JAVA_HOME=%DEV_TOOLS%\Java\jdk1.6.0_06
9
IF x.%XULRUNNER_SDK%==x. set XULRUNNER_SDK=C:\gecko-sdk
10
set XULRUNNER_MAKE=make_xulrunner
11
IF x.%1==x.x86 shift
12
GOTO MAKE
如果你和我一樣的用的是Sun的JDK,且版本是1.5或1.6,還須修改一下和build.bat同目錄的make_win32.mak文件。
替換
AWT_LIBS = "$(JAVA_HOME)\jre\bin\jawt.lib"
為
AWT_LIBS = "$(JAVA_HOME)\lib\jawt.lib"
一旦配置完成后,就可以在命令行中運行 build.bat x86 來build swt了,如果你的build過程中出錯,使用build x86 clean刪除垃圾文件,然后再查明原因。如果沒有錯誤的話,目錄下會生成5個dll文件,分別是swt-awt-win32-XXXX.dll, swt-gdip-win32-XXXX.dll, swt-wgl-win32-XXXX.dll, swt-win32-XXXX.dll, swt-xulrunner-win32-XXXX.dll,其中XXXX是視你的Eclipse版本而定。
要實現(xiàn)我們想要的效果,還需針對SWT的消息處理程序(WndProc) 擴展一下OS,那就是在就在主窗口建立過程中候往系統(tǒng)菜單里面加一自定義項。在Win32中,窗口建立的消息為WM_CREATE(值為1)。為簡單起見,這里只是覆蓋了SWT的默認消息處理程序,它通過os.c中的 OS_NATIVE(DefWindowProcW)方法來實現(xiàn)的(在98以后的版本api都是unicode,所以只覆蓋W版本)
改動之前:
1
#ifndef NO_DefWindowProcW
2
JNIEXPORT jint JNICALL OS_NATIVE(DefWindowProcW)
3
(JNIEnv *env, jclass that, jint arg0, jint arg1, jint arg2, jint arg3)
4

{
5
jint rc = 0;
6
OS_NATIVE_ENTER(env, that, DefWindowProcW_FUNC);
7
rc = (jint)DefWindowProcW((HWND)arg0, arg1, (WPARAM)arg2, (LPARAM)arg3);
8
OS_NATIVE_EXIT(env, that, DefWindowProcW_FUNC);
9
return rc;
10
}
11
#endif
改動之后:
1
#ifndef NO_DefWindowProcW
2
3
#define ID_CUSTOM_MENU 32888
4
5
const wchar_t* wcTitle=L"Hello";
6
const wchar_t* wcContent=L"World";
7
const wchar_t* wcName = L"Bang";
8
9
LRESULT CALLBACK MyWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
10

{
11
int wmId, wmEvent;
12
HMENU hSysMenu;
13
switch (message)
14
{
15
case WM_CREATE:
16
hSysMenu = GetSystemMenu(hWnd,FALSE);
17
InsertMenuW(hSysMenu,1,MF_BYPOSITION | MF_STRING,ID_CUSTOM_MENU,wcName);
18
break;
19
case WM_DESTROY:
20
PostQuitMessage(0);
21
break;
22
case WM_SYSCOMMAND:
23
wmId = LOWORD(wParam);
24
if (wmId==ID_CUSTOM_MENU)
25
MessageBoxW(hWnd,wcTitle,wcContent,MB_OK);
26
else
27
return DefWindowProcW(hWnd, message, wParam, lParam);
28
break;
29
default:
30
return DefWindowProcW(hWnd, message, wParam, lParam);
31
}
32
return 0;
33
}
34
35
JNIEXPORT jint JNICALL OS_NATIVE(DefWindowProcW)
36
(JNIEnv *env, jclass that, jint arg0, jint arg1, jint arg2, jint arg3)
37

{
38
jint rc = 0;
39
OS_NATIVE_ENTER(env, that, DefWindowProcW_FUNC);
40
rc = (jint)MyWndProc((HWND)arg0, arg1, (WPARAM)arg2, (LPARAM)arg3);
41
OS_NATIVE_EXIT(env, that, DefWindowProcW_FUNC);
42
return rc;
43
}
44
45
#endif
改動的目的就是在系統(tǒng)菜單里面加了一個菜單項 "Bang",點擊后會出現(xiàn)“Hello world” 對話框。
重新在swt源目錄下運行build x86,生成的swt就是擴展以后的版本。
現(xiàn)在我們來測試一下效果,寫一個如下所示的HelloWorld1.java,放到swt源目錄下,先運行javac HelloWorld1.java,再運行java HelloWorld1。不用擔心classpath與library path,build之后它們默認都在當前目錄下。
import org.eclipse.swt.widgets.*;


public class HelloWorld1
{


public static void main (String [] args)
{
Display display = new Display ();
Shell shell = new HelloWorld1 ().open (display);

while (!shell.isDisposed ())
{
if (!display.readAndDispatch ()) display.sleep ();
}
display.dispose ();
}

public Shell open (Display display)
{
Shell shell = new Shell (display);
shell.open ();
return shell;
}
}
在窗口標題欄單擊右鍵,點擊出現(xiàn)的Bang菜單會有如下效果圖:

這個例子雖然簡單,但大致描述了擴展swt的過程,更復雜的擴展等待YOU來實現(xiàn)!