Saturday, April 5, 2008

Dynamic Context Help

I admit that I've passed over this user assistance feature for the last few years. My reasons: it's fluff, the documentation is vague, and no one will use it. I couldn't have been more wrong.

We first wired up dynamic help to our job deck editor in January. Looking at the above image you will see that the *TFSUPPRESS tag NOISEDIP has been selected with the help view offered a link for more details. It is simple, it works, and it is not annoying.

The idea is to offer the user help on what has been selected in the active view or editor. The help appears in the Eclipse help view. I've put together an example plugin that shows how it works. It is a view listing three characters from the classic movie "The Good, the Bad and the Ugly". The view class is called DynamicHelpView.

Download the plugin source here from SourceForge.net. The code assumes you are using Eclipse 3.3 and at least Java 5.0. I'm assuming you have some experience in adding help to your plugin. If not, spend a few minutes looking at the plugin.xml, context.xml and reference/*.* files.

The Eclipse help view, if visible, will listen for view or editor activation and selection events. In our example, when it sees one of these events it calls DynamicHelpView.getAdapter(Class) with a request for an IContextProvider.

To get this to work, make a selection and request dynamic help by pressing the F1 key.

public Object getAdapter(Class adapter) {
  if (IContextProvider.class.equals(adapter)) {
    return new ContextProvider(Activator.PLUGIN_ID + ".context_dynamichelpview",
                               (IStructuredSelection) viewer.getSelection());
    }
  return super.getAdapter(adapter);
}

The ContextProvider retrieves the current table selection and returns a SelectionContext which provides the help view with help context resources to display. The help context resources can be related topics links or external links.

public class ContextProvider implements IContextProvider {
  public IContext getContext(Object target) {
    IContext context = HelpSystem.getContext(fContextId);
    if (!fSelected.isEmpty()) {
      context = new SelectionContext(context, fSelected);
    }
    return context;
  }

  public int getContextChangeMask() {
    return SELECTION;
  }
}

The SelectionContext creates a list of IHelpResources. The first is a a single external link to Wikipedia followed by some internal links to related static help. The static help was registered in plugin.xml.

public class SelectionContext implements IContext2 {
  private IHelpResource[] fHelpResources;
  private String fText;
  private String fTitle;

  public SelectionContext(IContext context, IStructuredSelection selection) {
    Assert.isNotNull(selection);
    if (context instanceof IContext2) {
      fTitle = ((IContext2) context).getTitle();
    }
    List helpResources = new ArrayList();
    String label = null;
    StringBuffer location = new StringBuffer("http://en.wikipedia.org/wiki/");
    if (selection.getFirstElement().equals("The Good")) {
      label = "Clint Eastwood " + selection.toString();
      location.append("Man_with_No_Name");
    } else if (selection.getFirstElement().equals("The Bad")) {
      label = "Lee Van Cleef " + selection.toString();
      location.append("Angel_Eyes_%28The_Good%2C_the_Bad_and_the_Ugly%29");
    } else if (selection.getFirstElement().equals("The Ugly")) {
      label = "Eli Wallach " + selection.toString();
      location.append("Tuco_%28The_Ugly%29");
    }
    helpResources.add(new SelectionHelpResource(label, location.toString()));
    // Add static help topics
    if (context != null) {
      IHelpResource[] resources = context.getRelatedTopics();
      if (resources != null) {
        for (int j = 0; j < resources.length; j++) {
          helpResources.add(resources[j]);
        }
      }
    }
    fHelpResources = helpResources.toArray(new IHelpResource[helpResources.size()]);
    if (context != null) {
      fText = context.getText();
    }
    if (fText == null) {
      fText = "";
    }
  }
}

Set a few break points in the plugin to learn how dynamic help works.

There is a small bug 173073 which requires you to hit a few keys to have the help view update its list of links. It has been fixed in Eclipse 3.4 (to be released in June 2008). Don't let this bug stop you from using the dynamic help.

Friday, February 15, 2008

My Favorite Breakpoints

I have been meaning to start listing the breakpoints I use to track down issues with Eclipse since the beginning of the year. Even when you know the problem is with your own code diving into the Eclipse code can greatly reduce your debug time. This is specific to the Eclipse 3.3 release. Please send me your favorite break points and I'll add them to the list.

User assistance
WorkbenchHelpListener.helpRequested(HelpEvent)
Prease F1 (or CTRL + F1 for GNOME) and you will end up here. A quick way to figure out which help context id is being requested.
WorkbenchHelpSystem.displayContext(IContext, int, int)
Most requests for help end up here.
StandaloneInfocenter.executeCommand(List)
Stand alone infocenter entry point.
Menu contribution (org.eclipse.ui.menus)

If you use commands and handlers with the new Eclipse 3.3 org.eclipse.ui.menus then these breakpoints are for you.

MenuPersistence.readAdditions()
See org.eclipse.ui.menus contributions being read from a plugin.xml file.
MenuAdditionCacheEntry.createContributionItems()
Most requests for help end up here.
PopupMenuExtender.menuAboutToShow(IMenuManager)
Called when MenuManager fires an about to show event