Tutorial: Upgrading to the new view API

From VuzeWiki
Jump to: navigation, search

This tutorial will go through how to convert your plugin from using the older PluginInterface.addView, UISWTManager.addView, and UISWTInstance.addView

Short History of addView[edit]

(Skip if you don't care about the history)

When Azureus' plugin interface was small, almost every function was directly in PluginInterface.

As the plugin interface became larger, functionality was split off into different manager. In 2102, the addView was moved to UISWTManager, along with all the other SWT UI Management utilities.

In 2305, UISWTManager was moved out of the org.gudy.azureus2.plugins structure and into org.gudy.azureus2.ui.swt.plugins. The idea was to seperate the UI plugin interface even further from the main plugin interfaces, making it easier for Azureus to be compiled without requiring SWT at all.

Moving the SWT UI interface to another package brought an unique opportunity to change its calling style (hopefully) for the better. In general, the plugin interface relies on listeners and manager classes. Plugins hook the listeners, wait for Azureus to trigger them, then get a manager class pertaining to the area they are listening to. The old way, making plugin developers extend Azureus classes is discouraged, because it runs the risk of the developer not calling super, or overriding tool methods. As well, structural changes are harder with extended classes.

(Side Note) Sometimes, you get the manager class before the trigger. This is generally just Azureus' developers programming style.

Changing Your Code[edit]

Step 1: Changing your view's class[edit]

In the old API, you had a class that extended either PluginView or UISWTPluginView. For this tutorial, we'll refer to it as "MyView".

MyView must be changed to implement from UISWTViewEventListener. Where your code says extends PluginView or extends UISWTPluginView, change it to implements UISWTViewEventListener.

You'll need to import the appropriate package - adding the line import org.gudy.azureus2.ui.swt.plugins.*; should do it.

Step 2: Implementing UISWTViewEventListener[edit]

UISWTViewEventListener has only one method, public boolean eventOccurred(UISWTViewEvent event). Instead of Azureus calling your view directly, it now triggers events for you to act on. So, within your eventOccurred implementation, you must trap the events you need and call the existing methods. A switch is best for this job.

  public boolean eventOccurred(UISWTViewEvent event) {
    switch (event.getType()) {
    }

    return true;
  }

The first event your want to trap is UISWTViewEvent.TYPE_CREATE. This is where you tell Azureus whether you want only 1 instance of your view, or many. Returning true in UISWTViewEvent.TYPE_CREATE tells Azureus to create the instance, and returning false tells Azureus to not create another instance, but to look for an existing one and give it the focus. An example of a plugin where you'd only want one instance is overall statistics page. An example of a plugin where you'd want many instances of your view is a browser view.

For this tutorial, we want only one instance of our view. To do this, we make a class boolean variable isCreated, and set it to true on the first UISWTViewEvent.TYPE_CREATE. Later calls to UISWTViewEvent.TYPE_CREATE will return false.

public class MyView implements UISWTViewEventListener {
  boolean isCreated = false;

and inside the switch statement:

      case UISWTViewEvent.TYPE_CREATE:
        if (isCreated)
          return false;

        isCreated = true;
        break;

The second most important event to trap when making a one instance view, is the TYPE_DESTROY. In it, we must set isCreated back to false, so that the window can be opened again. As well, we must call the old delete() method, if you implemented one:

      case UISWTViewEvent.TYPE_DESTROY:
        delete();
        isCreated = false;
        break;

The rest of the cases will call your existing code. Depending on which methods you overrode in *PluginView, you may not need all the case statements in the snippet below:

      case UISWTViewEvent.TYPE_INITIALIZE:
        initialize((Composite)event.getData());
        break;

      case UISWTViewEvent.TYPE_LANGUAGEUPDATE:
        updateLanguage();
        break;

      case UISWTViewEvent.TYPE_REFRESH:
        refresh();
        break;

There are other events, such when the focus is gained, or lost, and when the data source changed. These are out of the scope of this tutorial. However, if you find you require a new event trigger, please do not hesistate to contact the Azureus dev team.

So, if you are modifying existing code, the change to paste directly into your code is:

  
  boolean isCreated = false;
  public boolean eventOccurred(UISWTViewEvent event) {
    switch (event.getType()) {
      case UISWTViewEvent.TYPE_CREATE:
        if (isCreated) // Comment this out if you want to allow multiple views!
          return false;

        isCreated = true;
        break;

      case UISWTViewEvent.TYPE_DESTROY:
        delete(); // Remove if not defined
        isCreated = false;
        break;

      case UISWTViewEvent.TYPE_INITIALIZE:
        initialize((Composite)event.getData());
        break;

      case UISWTViewEvent.TYPE_LANGUAGEUPDATE:
        updateLanguage(); // Remove if not defined
        break;

      case UISWTViewEvent.TYPE_REFRESH:
        refresh(); // Remove if not defined
        break;
    }

    return true;
  }

Then modify the code to strip out those things which do not apply.

Step 3: Removing useless code[edit]

The following methods can be switched from public to private:

  • delete
  • initialize
  • refresh
  • updateLanguage

The line that calls the super implementations of these methods must be removed.

The following methods are not used anymore, and can be deleted:

  • generateDiagnostics(IndentWriter)
  • getComposite()
  • getData()
  • getFullTitle()
  • getPluginViewName()
  • getShortTitle()
  • isEnabled(String)
  • isSelected(String)
  • itemActivated(String)
  • setTabListener()

Setting your titles is addressed in Step 5.

Step 4: Changing to the new addView API[edit]

Let's start off with a snippet of code that opens a view.

Using PluginInterface.addView, you have:

01: public class MyPlugin implements UnloadablePlugin {
02:   PluginView myView = null;
03:   
04:   public void initialize(PluginInterface pluginInterface) {
05:     myView = new View(pluginInterface);
06:     pluginInterface.addView(myView);
07:   }
08: 
09:   public void unload() throws PluginException {
10:     // Unload code here
11:   }
12: }

Using UISWTManager.addView, you have something like this:

06:    pluginInterface.getUIManager().getSWTManager().addView(myView, true);

Using UISWTInstance.addView, you have something like:

public class MyPlugin implements UnloadablePlugin {
  UISWTInstance swtInstance = null;
  UISWTPluginView myView = null;
  
  public void initialize(PluginInterface pluginInterface) {
    pluginInterface.getUIManager().addUIListener(new UIManagerListener() {
      public void UIAttached(UIInstance instance) {
        if (instance instanceof UISWTInstance) {
          swtInstance = (UISWTInstance)instance;
          myView = new MyView(pluginInterface);

          swtInstance.addView(myView, true);
        }
      }

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

  public void unload() throws PluginException {
    if (swtInstance == null || myView == null)
      return;

    swtInstance.removeView(myView);

    myView = null;
  }
}

The new addView API is very similar to the last code snippet. You only need to change your "myView" variable from a "UISWTPluginView" to a "UISWTViewEventListener", and use the new addView/removeViews API.

  UISWTView myView = null;

to

  UISWTViewEventListener myView = null;
          swtInstance.addView(myView, true);

to

          swtInstance.addView(UISWTInstance.VIEW_MAIN, MyView.VIEWID, myView);
    swtInstance.removeView(myView);

to

    swtInstance.removeViews(UISWTInstance.VIEW_MAIN, MyView.VIEWID);

For plugins using the two older styles, you must add code to listen for the UIAttached trigger and create your view there. The snippet to use the new API is as follows:

public class MyPlugin implements UnloadablePlugin {
  UISWTInstance swtInstance = null;
  UISWTViewEventListener myView = null;
  
  public void initialize(PluginInterface pluginInterface) {
    pluginInterface.getUIManager().addUIListener(new UIManagerListener() {
      public void UIAttached(UIInstance instance) {
        if (instance instanceof UISWTInstance) {
          swtInstance = (UISWTInstance)instance;
          myView = new MyView(pluginInterface);
          swtInstance.addView(UISWTInstance.VIEW_MAIN, MyView.VIEWID, myView);
        }
      }

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

  public void unload() throws PluginException {
    if (swtInstance == null || myView == null)
      return;

    swtInstance.removeViews(UISWTInstance.VIEW_MAIN, MyView.VIEWID);

    myView = null;
  }
}

Finally, add a VIEWID constant to your MyView class.

public class MyView implements UISWTViewEventListener {
  public static final String VIEWID = "MyPluginID";

Step 5: Internationalization[edit]

In the previous addView APIs, you set name on the menu and tab using either getData(), getFullTitle(), or getShortTitle(). This has been replaced with a View ID you pass into the new addView API. The title is determined by a key in your language file that's based on the view id. To set the title that Azureus' uses, create a key in your language file of "Views.plugins." + <YourViewID> + ".title". For example, if your View ID is "MyViewID", your key/value pair may be

Views.plugins.MyViewID.title=My View Title

Final Comments[edit]

This tutorial is intended to get your plugin working quickly with the new API. Due to this, a lot of discussion on the new API has been left out. It is encouraged that you read the JavaDocs for the new API. You can find the CVS JavaDocs online. Here is a direct link to the new SWT UI Plugin Interfaces