Plugin SWT Main View Tutorial

From VuzeWiki
Jump to: navigation, search

Adding Views Tutorial for Azureus 2.3.0.6[edit]

Prerequisite For This Tutorial[edit]

You must already know how to make a plugin. You must ignore my spelling mistakes.

Attach your plugin to an UIInstance[edit]

In 2.3.0.5, the UI Plugin Interface has moved to org.gudy.azureus2.ui.swt.plugins, and a new way to access it was introduced. In order to interact with the UI, you must listen to the UIManagerListener. So, in your initialize method, gain a reference to the UIManager and call addUIListener with a UIManagerListener:

public class SampleMainViewPlugin implements UnloadablePlugin {
    public void initialize(PluginInterface pluginInterface)
            throws PluginException {
        
        // Get notified when the UI is attached
        pluginInterface.getUIManager().addUIListener(new UIManagerListener() {
            public void UIAttached(UIInstance instance) {
                if (instance instanceof UISWTInstance) {
                    UISWTInstance swtInstance = ((UISWTInstance) instance);
                    // Do code here
                }
            }

            public void UIDetached(UIInstance instance) {
            }
        });
    }

    public void unload() throws PluginException {
    }
}

UIAttached(UIInstance instance) is called when any UI interface is attached. In this tutorial, we are only interested in the SWT interface, so we check if it's a UISWTInstance, and go from there.

Create a class implementing UISWTViewEventListener[edit]

To add a view, you must implement an UISWTViewEventListener class and hook it into the plugin interface by either calling addView or openMainView. UISWTViewEventListener only has one trigger, eventOccurred which passes you an UISWTViewEvent. From UISWTViewEvent, you can get the type of event that was triggered, and access to the UISWTView object, which gives you control over the view.

The first event your listener will recieve is the TYPE_CREATE event. If you are wanting to allow your view to have multiple instances, return true. If you want only one instance of your view at a time, return false. If you want to use AWT objects, now is the only time to call UISWTView#setControlType(CONTROLTYPE_AWT)

The UISWTView object will always be the same for the same instance of the view, so, storing it for later use and comparison is acceptable.

The second event sent will be TYPE_INITIALIZE. This is where you create your view. The getData() function returns a Composite (or an AWT Component) for you to place your objects into. Go wild.

If you have SWT objects that require frequent update, it's recommended that you listen to the TYPE_REFRESH event and do your updating then (rather than making your own timer). It will be called on the GUI thread, and the interval is linked to the user's preference.

For our example, we'll make 2 labels and a button. One label will say welcome, and change when the button is pressed. The other label will have an incrementing number, updated on refresh.

    private class ViewListener implements UISWTViewEventListener {

        UISWTView view = null;

        Label funLabel;

        int iIndex = 0;

        public boolean eventOccurred(UISWTViewEvent event) {
            switch (event.getType()) {
                case UISWTViewEvent.TYPE_CREATE:
                    System.out.println("TYPE_CREATE Called");
                    /* We only want one view
                     * 
                     * If we wanted multiple views, we would need a class to handle
                     * one view.  Then, we could set up a Map, with the key
                     * being the UISWTView, and the value being a new instance of the
                     * class.  When the other types of events are called, we would
                     * lookup our class using getView(), and then pass the work to
                     * the our class.
                     */
                    if (view != null)
                        return false;
                    view = event.getView();
                    break;

                case UISWTViewEvent.TYPE_INITIALIZE:
                    System.out.println("TYPE_INITIALIZE Called");
                    initialize((Composite) event.getData());
                    break;

                case UISWTViewEvent.TYPE_REFRESH:
                    refresh();
                    break;

                case UISWTViewEvent.TYPE_DESTROY:
                    System.out.println("TYPE_DESTROY Called");
                    view = null;
                    break;

                case UISWTViewEvent.TYPE_DATASOURCE_CHANGED:
                    System.out.println("TYPE_DATASOURCE_CHANGED Called");
                    break;

                case UISWTViewEvent.TYPE_FOCUSGAINED:
                    System.out.println("TYPE_FOCUSGAINED Called");
                    break;

                case UISWTViewEvent.TYPE_FOCUSLOST:
                    System.out.println("TYPE_FOCUSLOST Called");
                    break;

                case UISWTViewEvent.TYPE_LANGUAGEUPDATE:
                    System.out.println("TYPE_LANGUAGEUPDATE Called "
                            + Locale.getDefault().toString());
                    break;
            }
            return true;
        }

        private void initialize(Composite parent) {
            final Label lbl = new Label(parent, SWT.NONE);
            lbl.setText("Welcome to my View!");
            lbl.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));

            funLabel = new Label(parent, SWT.LEFT);
            funLabel.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));

            Button button = new Button(parent, SWT.PUSH);
            button.setText("Push me");
            button.addSelectionListener(new SelectionAdapter() {
                public void widgetSelected(SelectionEvent e) {
                    lbl.setText("My plugin ID is " + view.getViewID());
                }
            });
        }

        private void refresh() {
            funLabel.setText(String.valueOf(iIndex++));
        }
    }

Displaying your view[edit]

Now that we have an UISWTViewEventListener, we need to hook it to the plugin API. addView will add your view, but not open it. openMainView will open your view in Azureus' main window, but not add it to the menu. Both of these methods require you to supply an ID for your view. This ID is used to retrieve text from your resource bundle. "Views.plugins." + ID + ".title" will be used for the menu item and the tab display.


For this example, we want to add it to the menu, and open it immediately (on startup), so we call both with the same UISWTViewEventListener.

    private static final String VIEWID = "org.gudy.azureus2.SampleMainViewPlugin";

    private ViewListener viewListener = null;

    private UISWTInstance swtInstance = null;

    public void initialize(PluginInterface pluginInterface)
            throws PluginException {
        
        // Get notified when the UI is attached
        pluginInterface.getUIManager().addUIListener(new UIManagerListener() {
            public void UIAttached(UIInstance instance) {
                if (instance instanceof UISWTInstance) {
                    swtInstance = ((UISWTInstance) instance);

                    viewListener = new ViewListener();
                    if (viewListener != null) {
                        // Add it to the menu
                        swtInstance.addView(UISWTInstance.VIEW_MAIN, VIEWID, viewListener);
                        // Open it immediately
                        swtInstance.openMainView(VIEWID, viewListener, null);
                    }
                }
            }

            public void UIDetached(UIInstance instance) {
                if (instance instanceof UISWTInstance)
                    swtInstance = null;
            }
        });
    }

Closing, Cleaning up[edit]

Finally, since we've made this plugin unloadable, we want to close our view when unload is called. Calling UISWTInstance#removeViews will remove all instances of your view, and remove the menu item.

    public void unload() throws PluginException {
        if (swtInstance != null)
            swtInstance.removeViews(UISWTInstance.VIEW_MAIN, VIEWID);
    }

If you wanted to just close the view (say, with a button), use UISWTView#closeView(). For this example, we'd add the button inside the initialize method,:

            Button buttonClose = new Button(parent, SWT.PUSH);
            buttonClose.setText("Close");
            buttonClose.addSelectionListener(new SelectionAdapter() {
                public void widgetSelected(SelectionEvent e) {
                    view.closeView();
                }
            });

There is also a TYPE_DESTROY event, which is triggered when a view is closed (either by you, azureus, or the user). Put any variable cleanup code, such as releasing GUI resource, there.

The Final Product[edit]

You can download the plugin, with source, with the link provided below. It will only run with B45 and later.

Sample Main View Plugin Details Page

Why the Change from Previous Versions?[edit]

The way to create plugin views was changed from extending a class to listeners and interface classes. Listeners and Interface classes are prefered because functionality can be added to Interface classes without breaking any plugins. As well, listeners (or just new event types) can be added without breaking plugins.

The second reason was to get away from introducing plugins to the non-plugin class, AbstractIView, which introduced them to AEMonitor and IndentWriter, both of which are also non-plugin classes.