[GEF]實現模板功能

來源:互聯網
上載者:User

最近遇到一個需求是要在GEF應用裡實現“模板”的功能,也就是像word那樣在建立一個檔案的時候,使用者可以選擇一個模板,然後以這個模板為基礎進行編輯,而不用每次都從頭開始。同時,使用者要能夠建立新模板和對模板進行編輯,在模板裡可以指定一些對模型的限制,例如樹的最大高度、子節點數目的限制,等等。

最簡單的模板可以就是一個執行個體,以它為模板時建立的新執行個體就是這個執行個體的副本。這種情況下,模板編輯器就是執行個體編輯器。問題主要有兩個,一是無法通過模板實現上述對執行個體的限制,二是除了副檔名以外,使用者難以通過編輯器外觀區分是在編輯模板還是編輯執行個體(本文中執行個體指原有的那個模型)。

更一般的模板可以這樣設計:首先,模板模型和執行個體模型是很相似的,只是在執行個體模型的基礎上,每個模板類多了一些用來限制執行個體的屬性,因此,模板模型中的每個類應該是執行個體模型中對應類的子類,這樣就解決了限制執行個體的問題。其次,對模板的編輯不能使用現有的Editor,而應該是對現有Editor的擴充,目的是區分兩種編輯器的外觀。

等一下,按照這個設計在實現上會有一些問題,在根據某個模板編輯執行個體的時候,必須知道模板資訊,這樣才能判斷比如新增子節點操作是否可用。但是如果你某天開啟一個執行個體時模板早已被刪除了呢?我們實際需要的是,一旦執行個體被建立,它就與模板脫離關係,即使模板被修改和刪除。因此,我認為模板和執行個體必須共用完全相同的模型,這樣執行個體中就有完整的限制資訊,並且還有一個額外的好處,它們的讀取方法是完全一致的,可以節約不少工作量。

現在來看看實現。為了更加靈活,我們把模板單獨作為一個新的Plugin,這樣一是便於分開代碼,二是很方便添加或移除模板功能(只要刪掉模板Plugin就能移除模板功能)。因此,在模板Plugin裡要額外指定對原來Plugin的依賴(在Plugin編輯器的dependencies屬性頁面裡),而原來Plugin裡要確保Export了模板Plugin需要的所有包(在runtime屬性頁面裡)。

在這個Plugin裡,建立一個繼承執行個體Editor的新Editor(名為TemplateEditor)。怎樣讓這個Editor裡顯示的介面與執行個體Editor有所區別呢?最簡單的一個方法是設定viewer的背景顏色,也就是覆蓋configureGraphicalViewer()方法,加上下面這句:

getGraphicalViewer().getControl().setBackground(new Color(null, 250, 250, 200)); 

如此一來,這個Editor的背景就是淡黃色了。但我還想進一步改造它,讓每個節點的顯示都與執行個體編輯器中不同。因為節點的figure是在editpart裡建立的,所以就必須使用與原來不同的editpart,而editpart是通過partfactory建立的,所以要讓viewer使用與原來不同的partfactory:

getGraphicalViewer().setEditPartFactory(new TemplatePartFactory()); 

當然,我們還要通過繼承原來的那些editpart得到一套新的editpart,在TemplatePartFactory的createEditPart()方法裡返回它們。在新的editpart的createFigure()方法裡,返回你希望的圖形。

模板編輯(左圖)和執行個體編輯(右圖)

圖形的問題解決了,還剩下另一個問題,應該只在編輯模板的時候在properties view裡顯示那些限制屬性,而在編輯執行個體時不顯示(這些屬性在起作用,但不應該能被編輯)。按照GEF提供的例子,一般是在model類裡定義用來控制哪些屬性顯示在properties view的IPropertyDescriptor數組,我們現在的情況是兩個Plugin使用同一個model,這樣就造成了矛盾。解決的辦法是改為在editpart裡定義IPropertyDescriptor數組。GEF的editpart通過getAdapter()方法返回實現IPropertySource介面的對象:

public Object getAdapter(Class key) {
    if (IPropertySource.class == key) {
        if (getModel() instanceof IPropertySource)
            return getModel();
        if (getModel() instanceof IAdaptable)
            return ((IAdaptable)getModel()).getAdapter(key);
    }
    if (AccessibleEditPart.class == key)
        return getAccessibleEditPart();
    return null;
}

可以看到當model類實現了IPropertySource時,就會返回model類。我們要覆蓋這個方法,讓它返回editpart本身,同時讓editpart實現IPropertySource並把原來放在model類裡的那些代碼移動到editpart裡。最好在原來的editpart裡就做出修改,這樣模板部分只要簡單的繼承下來並修改getPropertyDescriptors()等幾個相關方法即可:

public Object getAdapter(Class key) {
    if (IPropertySource.class == key) {
        return this;
    }
    return super.getAdapter(key);
}

模板模型的屬性視圖(左圖)和執行個體模型的屬性視圖(右圖)

要記住,為了讓這些限制屬性在執行個體編輯中起作用,必須修改原來的代碼,增加這些額外的判斷。之後,再在原來的CreationWizard裡增加一個選擇模板的page,模板功能就算實現了。按照這個思路,不需要額外寫很多代碼。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.