Česky   |  Deutsch   |  English   |  Español   |  Français   |  Indonesia   |  日本語   |  한글   |  Polski   |  Português (BR)   |  Türkçe   |  中文   |  正體中文   |  Your Language  
PlanetNetbeans
NetBeans 星球收集了全球 NetBeans 網誌中的精華.
頻道
[RSS 1.0 Feed] [RSS 2.0 Feed]
[FOAF Subscriptions] [OPML Subscriptions]
您正在寫關於 NetBeans 的網誌嗎?請將您的網誌加入到 PlanetNetBeans 吧
訂閱網誌

Powered by:    Planet

Last updated:
November 21, 2008 03:31 PM
All times are UTC

sponsored by Sun Microsystems

visit NetBeans website
NetBeans Rich Client Application (RCP) - January 18, 2008 04:16 PM
SaveAction part 2

說真的,SaveAction 的問題經常困題著筆者。最近,筆者因為要離職了,想說要多利用一下時間把 NetBeans 的東西學一學。所以,筆者就照著 NetBeans RCP 的書中第十章的範例練習。只不過,筆者是執行在 NetBeans 6.0,而非書中的 NetBeans 5.5.1。當然,在筆者心中存著一個想法: NetBeans 6.0 跟 NetBeans 5.5.1 一定有什麼地方不一樣。

經過一些時間之後,筆者發現了一個現象,照書中的範例,居然沒辦法讓 SaveAction 啟用。這…對筆者來說,可以說是其恥大辱啊。我怎麼可能會沒辦法讓它啟用,我都寫過了一篇文章來討論如何啟用 SaveAction。

後來,筆者發現,原來是筆者忘了把 Node 加入 Lookup 之中。

當筆者解決完這個問題後,筆者就想起之前寫的那篇文章中的專案『EnableSaveAction』是用 NetBeans 5.5.1 。如果我們把它移到 NetBeans 6 之後,是不是 SaveAction 就會改變了。

結果,答案果然是 NetBeans 6 把這個問題給修正了。

大家可以在這邊下載 NetBeans 6 的版本:下載

NetBeans Rich Client Application (RCP) - December 29, 2007 01:40 PM
CallbackSystemAction and Lookup

這一兩天,有一個中國的朋友寫信來問我一個問題,他/她說:為什麼 CallbackSystemAction 跟 Lookup 一起合著使用時, action map 即使設定了,也無法被 enable。當然,我馬上想到的是,他應該沒有設定對,所以會造成無法被 enable。後來,我看一看我之前的 source code 才發現下面的事實:

當我們在 TopComponent 了設定了 Lookup(即使用 associateLookup 方法)後,必須要把 action map 加到 Lookup 之中(在之前的例子中,我們大多使用 InstanceContent 物件來當 Lookup 的 provider)。大家可以參考下面的網址得到答案:

http://openide.netbeans.org/proposals/actions/impl.html

NetBeans Rich Client Application (RCP) - September 04, 2007 01:44 PM
Spider.Net

What do I do recently??? Spider.Net is the answer. I have opened an open source project, that is Spider.Net, recently. Spider.Net is a framework for web crawler. It provides many building blocks for web crawler construction. With these building blocks, you can build a web crawler easily. In Spider.Net, there are three main project: 1. Spider Framework, 2. Spider Runtime, 3. Runtime Environment. Spider Framework provides building blocks. Spider Runtime is driven by an XML configuration file. With XML configuration file, Spider Runtime can builds and runs a web crawler which is built dynamically. Runtime Environment is a kind of sample application which wraps Spider Runtime.
The url is: http://code.google.com/p/spiderframework/. Hope this project is useful to you.

NetBeans Rich Client Application (RCP) - June 22, 2007 08:30 AM
啟用 SaveAction 及 SaveAllAction

之前,筆者在開發 NetBeans RCP 的過程中,發生了一個非常怪異的問題。在 NetBeans Platform 的文件中指出,SaveAction 只要在 Lookup 中放入一個 Node 及一個 SaveCookie 就能夠啟用。但是,筆者不管怎麼試,都試不出來。最後,筆者也忘了是做了什麼事才讓它 enable ,可能是把某個 Netbeans 的 Module 加入之後吧。

最近,筆者又被 assign 了另一個專案,也是要用 NetBeans Platform 來設計。好死不死,筆者又遇到了一模一樣的問題(SaveAction 無法被 enable)。這次,筆者就決定把它給記錄下來。

這篇文章算是一篇記錄性質的文章,我們將討論一下,如何讓 SaveAction(呈現出來是在 File/Save 的選單中)被啟用。同時,我們也會討論一下,如何使用最少的程式來啟用 SaveAction 及 SaveAllAction。這些問題都是在 dev@openide.netbeans.org 討論串中常出現的文章。如果,大家曾經看過,或已經知道 work around ,就可以略過啦。

1. 問題描述

NetBeans 的 SaveAction 可以說是一個奇怪的玩意。 NetBeans 的文件中指出,SaveAction 要被啟用的條件是 Lookup 中有只有一個 Node ,且這個 Node 的 Lookup 有一個 SaveCookie 在裡面,筆者還去看過 SaveAction 的 source code 也是這樣設計的。但是,不論我們如何設計,都還是無法研 SaveAction 被啟用。根據 http://openide.netbeans.org/servlets/ReadMsg?listName=dev&msgNo=19141 這篇文章的討論,同樣的問題不止出現在 SaveAction ,還出現在 PrintAction 。本文也將根據上述文章的內容為主,來設計一個測試這個問題及其 work around 的專案。

2. 建立測試用的專案

我們要建立一個名叫 EnableSaveAction 的 module suite 及一個名叫 EnableSave 的 module 專案,並將 EnableSaveAction 專案設定成 Standalone Application。

3. 建立 TopComponent

我們接著要建立一個名叫 SaveAction 的 TopComponent,並在它的設計畫面中放入兩個 JButton 及一個 JTextArea ,如下圖。其中,JButton 分別用來啟用 Save 及停用 Save ;JTextArea 是用來顯示一些訊息。

4. 測試問題

首先,我們要建立一個沒用的 Node 及一個會顯示儲存訊號的 SaveCookie ,程式碼如下:

JAVA:
  1. private SaveCookie m_SaveCookie;
  2. private Node m_DummyNode;
  3.  
  4. public SaveCookie getSaveCookie(){
  5. if(m_SaveCookie==null){
  6. m_SaveCookie=new SaveCookie(){
  7. public void save() throws IOException {
  8. jTextArea1.setText(jTextArea1.getText() + "I am saved.\r\n");
  9. m_DummyDO.setModified(false);
  10. }
  11. };
  12. }
  13. return m_SaveCookie;
  14. }
  15.  
  16. public Node getNode(){
  17. if(m_DummyNode==null){
  18. m_DummyNode=new AbstractNode(Children.LEAF, m_Lookup);
  19. m_DummyNode.setName("Dummy Node");
  20. m_DummyNode.setShortDescription("Useless node");
  21. }
  22. return m_DummyNode;
  23. }

接著就是依照我們常用的方式來建立一個 InstanceContent 及 Lookup 如下:

JAVA:
  1. private Lookup m_Lookup;
  2. private InstanceContent m_InstanceContent;
  3. private SaveActionTopComponent() {
  4. initComponents();
  5. setName(NbBundle.getMessage(SaveActionTopComponent.class, "CTL_SaveActionTopComponent"));
  6. setToolTipText(NbBundle.getMessage(SaveActionTopComponent.class, "HINT_SaveActionTopComponent"));
  7. //        setIcon(Utilities.loadImage(ICON_PATH, true));
  8. m_InstanceContent=new InstanceContent();
  9. m_Lookup=new AbstractLookup(m_InstanceContent);
  10. this.associateLookup(m_Lookup);
  11. m_InstanceContent.add(getNode());
  12. }

這段程式碼是在 SaveActionTopComponent 的 constructor 中設計的,所以在程式開始執行的時候,就會將我們所建立出來的 Dummy Node 加入 Lookup 中。

接下來,我們在 Enable Save(jButton1)及 Disable Save(jButton2)的兩個 JButton 中加入以下的程式碼:

JAVA:
  1. private void jButton2ActionPerformed(java.awt.event.ActionEvent evt) {
  2. m_InstanceContent.remove(getSaveCookie());
  3. jTextArea1.setText(jTextArea1.getText() + "remove save cookie.\r\n");
  4. }
  5.  
  6. private void jButton1ActionPerformed(java.awt.event.ActionEvent evt) {
  7. m_InstanceContent.add(getSaveCookie());
  8. jTextArea1.setText(jTextArea1.getText() + "put save cookie.\r\n");
  9. }

在這段程式碼中,我們分別將 SaveCookie 加入到 InstanceContent 或從 InstanceContent 移除。

程式執行後,我們可以按下 Enable Save 的按鈕,將會發現 File/Save 並沒有被啟用,畫面如下:

5. 加入 SaveAction 的 work around

根據 http://openide.netbeans.org/servlets/ReadMsg?listName=dev&msgNo=19141 的討論,我們可以發現,只須要將 SaveAction 加到 Tool Bar 之中,就可以解決 SaveAction 的問題。所以,我們打開 layer.xml 檔,並加入以下的 XML :

XML:
  1. <folder name="Toolbars">
  2.         <folder name="File">
  3.             <file name="SaveAction.shadow">
  4.                 <attr name="originalFile" stringvalue="Actions/System/org-openide-actions-SaveAction.instance"/>
  5.             </file>
  6.             <attr name="com-xmlgem-xmleditor-action-OpenNewAction.shadow/org-openide-actions-SaveAllAction.instance" boolvalue="true"/>
  7.         </folder>
  8.     </folder>

加入之後,我們再次執行,並按下 Enable Save 的按鈕,發現 SaveAction 被正常地啟用。同時按下它,也能有對應的反應。畫面如下:

6. 加入 SaveAllAction 的 work around

SaveAllAction 的行為向來都是很正常的,所以這裡,我們將討論有沒有較簡單的方式可以啟用它。

這裡,我們將在記憶體中建立一個 Dummy.txt 的 FileObject ,並利用這個 FileObject 來得到系統預設的 DataObject 。透過這個 DataObject ,我們就可以利用 setModified 來讓 SaveAllAction 啟用了。

在設計之前,我們必須將 Datasystems API 及 File System API 給引用進來,完成後的畫面如下:

我們要在 SaveActionTopComponent 的 constructor 加入以下的程式碼,並多宣告一個 DataObject 的變數:

JAVA:
  1. private DataObject m_DummyDO;
  2. private Lookup m_Lookup;
  3. private InstanceContent m_InstanceContent;
  4. private SaveActionTopComponent() {
  5. initComponents();
  6. setName(NbBundle.getMessage(SaveActionTopComponent.class, "CTL_SaveActionTopComponent"));
  7. setToolTipText(NbBundle.getMessage(SaveActionTopComponent.class, "HINT_SaveActionTopComponent"));
  8. //        setIcon(Utilities.loadImage(ICON_PATH, true));
  9. m_InstanceContent=new InstanceContent();
  10. m_Lookup=new AbstractLookup(m_InstanceContent);
  11. this.associateLookup(m_Lookup);
  12. m_InstanceContent.add(getNode());
  13.  
  14. //for enable SaveAll
  15. FileObject oDummyFileObj=null;
  16. try {
  17. oDummyFileObj = FileUtil.createMemoryFileSystem().getRoot().createData("Dummy.txt");
  18. } catch (IOException ex) {
  19. ex.printStackTrace();
  20. }
  21. if(oDummyFileObj==null){
  22. throw new RuntimeException("Error, DummyFileObj hasnt been create");
  23. }
  24. try {
  25.  
  26. m_DummyDO=DataObject.find(oDummyFileObj);
  27. } catch (DataObjectNotFoundException ex) {
  28. ex.printStackTrace();
  29. }
  30. }

接下來,我們修改一下 SaveCookie 及 Enable/Disable Save 的事件處理程式:

JAVA:
  1. private void jButton2ActionPerformed(java.awt.event.ActionEvent evt) {
  2. m_InstanceContent.remove(getSaveCookie());
  3. m_DummyDO.setModified(false);
  4. jTextArea1.setText(jTextArea1.getText() + "remove save cookie.\r\n");
  5. }
  6.  
  7. private void jButton1ActionPerformed(java.awt.event.ActionEvent evt) {
  8. m_InstanceContent.add(getSaveCookie());
  9. m_DummyDO.setModified(true);
  10. jTextArea1.setText(jTextArea1.getText() + "put save cookie.\r\n");
  11. }

完成之後,可以再執行一次,並按下 Enable Save 的按鈕,我們將可以發現 Save 及 Save All 都被啟用了:

7. 討論

在這裡,我們可以使用這兩個 work around 來啟用 SaveAction 及 SaveAllAction 。但是,當我們按下 Enable Save 後,我們如果將程式關閉,NetBeans 則會顯示有個名叫 Dummy.txt 的檔案尚未被儲存,如下圖:

此時,Dummy.txt 就是我們之前在記憶體中建立的空白檔案,這邊,如果大家有須要的話,可以針對不同的情況建立不同名稱的檔案。

另外,如果我們按下 Save 或 Save All ,我們的 SaveCookie 將不會被呼叫到。這是因為,NetBeans 會透過 DataObject 來取得 SaveCookie ,然而,我們的 SaveCookie 是存在 InstanceContent 之中,並沒有存在 DataObject 中,所以不會被呼叫到。針對這個問題,我們可以使用 DataObject 的 Modified Property Change 事件來處理。

最後,筆者必須提一下,關於 SaveAction 的部份,筆者相信在不久的將來 Netbeans 的開發團隊會修正它,所以,大家如果採用這種方式,記得在程式碼中加個註記。關於 SaveAllAction 的部份,這裡所介紹的方法是在最少程式碼的方式來完成它,所以,有許多小細節會無法設定,例如 DataObject 的 icon …等。

完整的程式碼可以在這裡下載

NetBeans Rich Client Application (RCP) - June 15, 2007 03:37 AM
詭異的系統標題(Application Title)

大家在開發許多 NetBeans RCP 之後一定會發現,每次建立一個 Module Suite 專案後,NetBeans 幫我們自動帶出的 Application Title 中,都有著一段奇怪的數字(例如: MyProgressBar 200704122300,見下圖)。這個問題其實也困擾著筆者很久,所以,筆者決定花時間去研究一下如何在設計階段就將系統的標頭更改成我希望的格式。所以,這篇文章將會和大家討論一下,筆者發現可以用來修改 Application Title 的方式。這篇文章將著重在,以修改設定檔的方式來完成這項工作。當然,在先前討論的 Login Dialog 中,我們曾經使用過 ModuleInstall ,讓 NetBeans RCP 能在 Window Opened 事件發生時顯示一個 Login Dialog ,同樣的方式也用來可以動態地修改標頭。

1. 選擇要修改的專案

這篇文章,我們將使用上一篇所建立出來的 MyProgressBar 的專案當成是實行範例。大家可以在這裡下載到專案的原始碼

2. 修改設定檔

NetBeans 在顯示標題的時候,其實是從具備 branding 的 libraries 中讀出設定檔,這部份其實是和我們之前介紹的 multi-lingual 文章有關,建議大家先閱讀過這篇文章,再來看接下來的部份,將會比較了解 NetBeans 的運作。

我們要修改的設定檔是否 branding 有關的設定檔,所以我們將無法在 Projects 的 tab 中找到。我們必須要把它切換到 Files 的 tab 中才能找到。切換到 Files 的 tab 後,我們可以看到許多目錄,我們可以將 MyProgressBar/branding/modules 打開。在裡面,有兩個目錄,一個是 org-netbeans-core-windows.jar 、另一個是 org-netbeans-core.jar ,這兩個 modules 將會是影響系統載入時標題的樣子。我們接下來把這兩個 module 的目錄全部打開,將可以看到以下的畫面:

其實,我們要改的部份,只有 org-netbeans-core-windows.jar 裡面的 Bundle.properties 檔。將檔案打開後畫面如下:

我們只須要修改 CTL_MainWindow_Title 及 CTL_MainWindow_Title_No_Project 這兩個參數,就可以改變系統標題了。在這裡,我們把 {0} 的部份給刪除。完成後,再執行一次系統,我們將可以得到以下的畫面:

那個奇怪的標題就不見了。

3. 修改 About 視窗的資訊

About 視窗中的資訊也可以用類似的方式來加以修改,例如以下是原始的樣子:

畫紅線的部份將是我們會修改到的地方。

接下來,我們將剛剛 modules 的另一個模組(org-netbeans-core.jar)給打開,並且將它裡面包含的 Bundle.properties 給打開。打開後畫面會類似下圖:

我們只須要改變 LBL_ProductInformation 就可以修改 About 視窗中的 Product 的名稱。在這裡,我們將它改成 MyProgressBar ~~。

接下來,我們要在 Files 的 tab 中將 branding/core 給打開,會發現裡面有個名叫 core.jar 的目錄,它會是 NetBeans 在啟動的時候最接近核心的部份。我們可以把它目錄結構打開(如下圖),並將其中的 Bundle.properties 也打開:


在這個檔案中,我們可以修改 currentVersion 的屬性,就可以修改到 About 視窗中的 Product Version 的字。在這裡,我們將它改成 MyProgressBar Pre-Release {0} 的字樣,並再執行一次。我們可以得到下面的視窗:

4. 結語

筆者首經在 NetBeans 的官網或 Google 中搜尋如何修改標題的方法,但是都無所獲。所以,筆者才會進行這篇文章所描述的測試。說真的,這個方法可能不是比較好的方法,但是,還算得上是一個簡單的方法。如果,大家在學習 NetBeans 上知道其它的修改標題的方法,還煩請教導一下我們吧。

NetBeans Rich Client Application (RCP) - June 13, 2007 07:01 AM
Getting Started 系列 - Progress API 補充

這篇文章算是關於 Progress API 的小小註記。

幾天前,我寫了一篇關於 Progress API 的文章,當然,我也很高興地使用著 NetBeans 的 Progerss API 來建立我希望的介面。但是,在我進行程式設計的過程中,卻發生了一些些的小插曲:

1. ProgressHandle.start()ProgressHandleFactory.createProgressComponent(ProgressHandle handle) 的使用

如果,我們須要使用 ProgressHandleFactory.createProgressComponent 來建立一個使用者介面的話,它必須要在 ProgressHandle.start 被呼叫之前進行,否則會出現 exception 。

老實說,個註記是理所當然的,所有的初始化,都必須在 start 之前完成。但是,很多時候,我們都還是會乎略掉這個小細節。

2. ProgressHandle.start() 、ProgressHandle.start(int workunits) ProgressHandle.start(int workunits, long estimate) 的使用

到底什麼時候,我們要使用 start() 而不使用 start(int) 或 start(int, long)。基本上,筆者認為,這部份也是大家容易搞錯的地方,在 Progress API 的 javadoc 中有明確地提到:start() 是用在處理一個不確定工作項目(indeterminate mode)時使用; start(int) 則是用在知道有幾個工作項目(determinate mode)的時候使用;start(int, long) 除了指定有幾個工作項目外,還多指定了一個預計完成時間。所以,我們在使用時,必須要明確地使用正確的方法。

當然,乍看之下,大家可能會覺得只是一個 start 而己沒什麼。 但是,根據 Progress API 的設計,一個 ProgressHandle 在 start 的時候,如果是屬於不確定工作項目的情況下,接下來所使用的 ProgressHandle.progress 也要是不確定工作項目的方法。ProgressHandle 的 progress 有三種不同的 override 形式:1. progress(int workunit)、2. progress(String message)、3. progress(String message, int workunit)。這三種形式中,第二個項目( progress(String message))是不確定工作項目專用的方法;第一、三項(progress(int workunit)progress(String message, int workunit))目則是用在已經工作項目的方法。如果這個地方用錯了,也將會有 exception 出現哦。

NetBeans Rich Client Application (RCP) - June 08, 2007 09:47 AM
Getting Started 系列 - Progress API

Progress 是 NetBeans 中用來呈現目前執行狀態的一個 API ,大家可能會覺得,我們己經有了 JProgressBar ,幹嘛還要這種東西。其實, Progress API 提供了一些簡易的方法讓我們可以很容易地在 NetBeans 中呈現一個 Progress Bar 。
大家在開啟 NetBeans 的時候,可以發現 NetBeans 在右下角( Status Bar 的右方)中有一個顯示 Scanning Project Classpaths 的 Progress Bar,它代表著 NetBeans 正在執行 Scanning 專案的 Classpath 的進度。 Progress API 即可做到這項功能。我們將在這篇文章討論一下, Progress API 較簡單的使用方法,致於 Aggregate 的部份將留到之後的文章再做介紹。在這次的範例中,我們將建立一個 Thread 用來測試 Progress Bar ,所以我們讓它執行 100 次的迴圈,每次執行時,透過呼叫 Thread.sleep (300) 的方式,讓它暫停 300 ms ,直到執行完成。

1. 備準專案

在每次的一開始,我們都必須準備一個 NetBeans Module Suite 的專案,用來呈現這次的結果。這次,我們建立一個名叫 MyProgressBar 的 Module Suite 專案,及一個名叫 ProgressBar 的 Module 專案。如果大家忘了如何建立專案,可以參考之前的文章。建立完成後畫面如下:

2. 設計 TopComponent

我們可以使用 NetBeans 的 Wizard 來建立一個 TopComponent(即 Window Component)。首先,我們先建立一個名叫 HeavyJob 的 TopComponent ,完成後,如下圖所示:

接下來,我們在畫面上加入一個 JButton ,讓它顯示成『Execute Heavy Job』,並叫它 m_Execute 。完成後如下:

在我們進行程式設計之前,我們要將 Progress API 的模組引用進來,我們可以在 ProgressBar 專案上按右鍵,選擇 Project Properties 的項目。接下來,我們要在 Categories 為 Libraries 的項目中加入 Progress API & UI 的模組,加入完成後如下圖所示:

3. 程式設計-ProgressHandle
我們在 m_Execute 的 JButton 的 actionPerformed 事件中加入以下的程式碼:

JAVA:
  1. private void m_ExecuteActionPerformed(java.awt.event.ActionEvent evt) {                                         
  2.         final ProgressHandle oHandle=ProgressHandleFactory.createHandle("Heavy Job Progress Bar");
  3.         Thread oHeavyThread=new Thread(new Runnable(){
  4.             public void run() {
  5.                 oHandle.start(0);
  6.                 for(int i=0;i<100;i++){
  7.                     try {
  8.                         Thread.sleep(300);
  9.                     } catch (InterruptedException ex) {
  10.                         ex.printStackTrace();
  11.                     }
  12.                     oHandle.progress("前進 #" + i);
  13.                     System.out.println("前進 #" + i);
  14.                 }
  15.                 oHandle.finish();
  16.             }
  17.         });
  18.         oHeavyThread.start();
  19.     }

在這段程式碼中,我們首先使用 ProgressHandleFactory 建立一個名叫『Heavy Job Progress Bar』的 ProgressHandle 物件。我們可以使用 ProgressHandle 來讓 Progress Bar 前進,並顯示一些訊息。接下來,我們建立一個 Thread ,讓它每隔 300 ms 呼叫 ProgressHandle.progress 一次。在這段程式中,我們一定要把 heavy job 放在一個新的 Thread 之中,否則,整個系統將不會有任何的反應。這是因為,這段程式碼是在 m_Execute 處理事件的 Tread 中執行,如果我們將 heavy job 放在這個 Thread 中執行,外加這個 Thread 是 Swing Thread (它的優先權為最高),它將會把 UI 的更新動作給卡住。所以,在這邊,我們建立了另一個 Thread ,這個 Thread 的優先權會比 Swing Thread 低,利用這個 Thread 來執行耗時的工作,將可讓 Swing 又更新 UI 又執行工作。

執行後,畫面如下:

上面的訊息是當我們按下 Progress Bar 後顯示才能出來。

4. 顯示 Progress Bar 的 Dialog

很多時候,我們都會須要在顯示一個 modal dialog 用來防止使用者在程式執行的過程中按下其它的指令。所以,我們接下來討論一下如何使用 NetBeans 的 Progress API 來達到這功能。

首先,我們先在 HeavyJobTopComponent 中加入另一個 JButton ,顯示成『Execute Job in Dialog』,並讓它叫 m_ExecuteDialog 。完成後畫面如下:

接下來,我們使用 NetBeans 的 wizard 建立一個名叫 ProgressDialog 的 JDialog ,並在上面放入一個名叫 m_ProgressBarContainer 的 JPanel ,建立完後成畫面如下:

我們接著在 ProgressDialog 加入以下的程式碼:

JAVA:
  1. private Thread m_Thread;
  2.    
  3.     public void init(ProgressHandle h, Runnable r){
  4.         m_ProgressBarContainer.add(ProgressHandleFactory.createProgressComponent(h), BorderLayout.CENTER);
  5.         m_Thread=new Thread(r);
  6.     }
  7.    
  8.     public void setVisible(boolean b) {
  9.         if(b && m_Thread!=null){
  10.             m_Thread.start();
  11.             m_Thread=null;
  12.         }
  13.         super.setVisible(b);
  14.     }

我們在 init 的時候,使用 ProgressHandleFactory.createProgressComponent 的方法來建立一個 Progress Bar,並把它放在 m_ProgressBarContainer 之中。接著,我們建立了一個名叫 m_Thread 的 Thread 會負責執行傳入的 Runnable 物件。接下來,我們在 setVisible 中執行 m_Thread。

完成 ProgressDialog 後,我們可以在 HeavyJobTopComponent 中 m_ExecuteDialog 的 actionPerformed 事件中,加入以下的程式碼:

JAVA:
  1. private void m_ExecuteDialogActionPerformed(java.awt.event.ActionEvent evt) {                                               
  2.         final ProgressDialog oDialog=new ProgressDialog(WindowManager.getDefault().getMainWindow(), true);
  3.         final ProgressHandle oHandle=ProgressHandleFactory.createHandle("Heavy Job Progress Bar");
  4.         Runnable oRunnable=new Runnable(){
  5.             public void run() {
  6.                 oHandle.start(0);
  7.                 for(int i=0;i<100;i++){
  8.                     try {
  9.                         Thread.sleep(300);
  10.                     } catch (InterruptedException ex) {
  11.                         ex.printStackTrace();
  12.                     }
  13.                     oHandle.progress("前進 #" + i);
  14.                     System.out.println("前進 #" + i);
  15.                 }
  16.                 oHandle.finish();
  17.                 SwingUtilities.invokeLater(new Runnable(){
  18.                     public void run() {
  19.                         oDialog.setVisible(false);
  20.                     }
  21.                 });
  22.             }
  23.         };
  24.         oDialog.init(oHandle, oRunnable);
  25.         oDialog.setVisible(true);
  26.         System.out.println("執行完成...");
  27.     }

在這段程式碼中,它和之前的程式一樣,但是我們加入了 ProgressDialog 的物件,並將我們所建立起來的 ProgressHandle 及 Runnable 物件交給它。比較特別的地方是,我們在 Runnable 物件執行完成後,使用 SwingUtilities.invokeLater 方法來關閉 ProgressDialog ,這是因為,所有 Swing 的更新都要在 Swing Thread 之中完成。

執行的畫面如下:

完整專案下載

NetBeans Rich Client Application (RCP) - June 08, 2007 08:32 AM
近況

前陣子,我被我們老闆(或叫老師吧)調去做另一個專案的事情,所以導致整個五月都沒有什麼文章進帳,真的很對不起各位。還好,目前那個專案已經告一段落了,我又被調回設計使用 NetBeans RCP 的專案。所以,最近,應該又能再寫些文章。

NetBeans Rich Client Application (RCP) - April 30, 2007 02:19 AM
Getting Started 系列 - 讀取及儲存 Preference

一般的系統大多會使用自己設計的格式來儲存使用者的偏好設定,可是,要設計一套很完整的模型將不是一件容易的事。NetBeans 在這個地方有很不錯的表現,我們可以使用先前介紹的 layer.xml 機制來完成這項工作。這篇文章將討論如何使用 layer.xml 來儲存使用者的偏好設定,以及簡單介紹 FileObject 的應用,共分成三個部份:讀取設定值、透過程式寫入設定值、及透過程式讀取連結到外部檔案的設定。

1. 建立專案

我們首先要建立 Module Suite 及 Module 的專案,建立的方式可以參考之前的文章。在這篇文章中,我們會建立一個名叫『Save Your Preference Info』的 Module Suite 及『ReadWrite Preference』的 Module,建立完成後,畫面如下:

2. 由 layer.xml 中讀取設定值

layer.xml 在 NetBeans 中會被轉成一個虛擬的 FileSystem ,所有的 file 及 folder 的 element 都會被轉成 FileObject ,而 attr 的 element 將會被轉成 FileObject 的 Attribute。所以,我們可以透過 FileSystem 來存取到它。我們可以使用 Repository.getDefault(). getDefaultFileSystem() 來取得 layer.xml 的 FileSystem 物件,這個 FileSystem 物件也將會是 NetBeans 用來儲存系統設定及偏好設定的地方。

在我們這個例子中,我們預計會將 JButton 的 X, Y 位置儲存在 layer.xml 當中,然後在程式執行起來後,將 JButton 定位在指定的位置。所以,首先我們先在 layer.xml 中加入一個名叫『Button.position』的 file element,然後在它底下,宣告兩個 atrribute 分別是 x 和 y,完成後的樣子如下:

XML:
  1. <file name="Button.position">
  2.         <attr name="x" intvalue="10"/>
  3.         <attr name="y" intvalue="250"/>
  4.     </file>

接下來,我們在我們建立的專案中,新增一個名叫 Type1 的 TopComponent ,並讓它在一開始的時候就顯示在 editor 的位置中,如下圖:

建立完成後,我們一樣要將會用到的 Module 給加進來,加入完成後,會如下圖示所:

接下來就是在我們建立的 Type1TopComponent 中放置一個 JButton,另外,因為我們希望能狀態地指定位置,所以要將 layout manager 給取消掉,讓它成為 null layout ,如下圖:

設定成 null layout:

完成後,我們要在 Type1TopComponent 的建構子中加入以下的程式碼:

JAVA:
  1. private Type1TopComponent() {
  2.         initComponents();
  3.         setName(NbBundle.getMessage(Type1TopComponent.class, "CTL_Type1TopComponent"));
  4.         setToolTipText(NbBundle.getMessage(Type1TopComponent.class, "HINT_Type1TopComponent"));
  5. //        setIcon(Utilities.loadImage(ICON_PATH, true));
  6.         FileSystem oFS=Repository.getDefault().getDefaultFileSystem();
  7.         FileObject oFile=oFS.findResource("Button.position");
  8.         if(oFile!=null){
  9.             int iX=((Integer)oFile.getAttribute("x")).intValue();
  10.             int iY=((Integer)oFile.getAttribute("y")).intValue();
  11.             jButton1.setLocation(iX, iY);
  12.         }
  13.     }

這段程式碼是從 Repository 中取得 DefaultFileSystem ,再透過 FileSystem.findResource 將 Button.position 的 FileObject 給讀取出來。 取得 Button.position 後,我們可以使用 getAttribute(String attrName) 的方法,將我們儲存在 layer.xml 中的 x 與 y 給讀出。

程式執行後的畫面如下:

接下來,當我們把 x, y 改成 10 及 250 ,執行後的畫面就變成:

3. 從程式寫入偏好設定到 layer.xml

在這個例子中,我們預計設計一個文字編輯器(使用 JTextArea 就好),以及一個能夠動態改變字型的功能(使用 JSlider),讓使用者能自行決定希望的字型大小及輸入文字。當使用者輸入完成後了之後,可以按下 Save 的按鈕,將輸入的文字及字型大小儲存起來,下次執行的時候,系統就會自動載入前次設定的字型大小。另外,當使用者按下 Load 的按鈕時,會將之前儲存的文字也載入進來。
首先,我們先在 layer.xml 中建立一個 Folder 名叫 Type2 ,在裡面建立一個 File 名叫 FontSize.size,並給它一個名叫 size 的 attribute,完成後如下所示:

XML:
  1. <folder name="Type2">
  2.         <file name="FontSize.size">
  3.             <attr name="size" intvalue="10"/>
  4.         </file>
  5.     </folder>

我們再建立一個名叫 Type2 的 TopComponent ,並且讓它和 Type1 一樣程式一啟動就顯示在 editor 的位置。接下來,我們在畫面中放入 JSlider (名叫 jSlide1)、 JTextArea (名叫 jTextArea1)、及一個 Save JButton (名叫 jButton1)及一個 Load JButton( jButton2),如下圖所示:

接下來,我們一樣在 Type2TopComponent 的建構子中加入以下的程式碼:

JAVA:
  1. private Type2TopComponent() {
  2.         initComponents();
  3.         setName(NbBundle.getMessage(Type2TopComponent.class, "CTL_Type2TopComponent"));
  4.         setToolTipText(NbBundle.getMessage(Type2TopComponent.class, "HINT_Type2TopComponent"));
  5. //        setIcon(Utilities.loadImage(ICON_PATH, true));
  6.        
  7.         FileSystem oFS=Repository.getDefault().getDefaultFileSystem();
  8.         FileObject oFile=oFS.findResource("Type2/FontSize.size");
  9.         if(oFile!=null){
  10.             int iSize=((Integer)oFile.getAttribute("size")).intValue();
  11.             jSlider1.setValue(iSize);
  12.             jTextArea1.setFont(jTextArea1.getFont().deriveFont((float) iSize));
  13.             jTextArea1.revalidate();
  14.             jTextArea1.repaint();
  15.         }
  16.     }

這段程式碼中,我們和上一節一樣,使用 Repository 將設定值讀出。這裡比較不一樣的地方是,我們這次是將設定值儲存在 Type2 Folder 中的 FontSize.size,所以,我們在寫路徑的時候必須寫下 Type2/FontSize.size 。這裡,有個地方須要注意,NetBeans 中的 FileObject 的路徑都是使用『/』來當分隔,所以,不論我們在什麼平台下,都是要寫成 Type2/FontSize.size。

接下來就是實作寫入的程式碼,我們可以先選擇 jButton1 ,然後在 Properties 中選擇 Events 的頁籤,並在 actionPerformed 中加入程式碼:

JAVA:
  1. private void jButton1ActionPerformed(java.awt.event.ActionEvent evt) {                                         
  2.         FileSystem oFS=Repository.getDefault().getDefaultFileSystem();
  3.         FileObject oType2Folder=oFS.findResource("Type2/");
  4.         try {
  5.             // save text
  6.             FileObject oSaveData=oType2Folder.getFileObject("Save.txt");
  7.             if(oSaveData==null){