Home > Eclipse development, Software Development > Writing an Eclipse Plug-in (Part 19): A Quick Display Fix

Writing an Eclipse Plug-in (Part 19): A Quick Display Fix


[I am deep into solving a CNF issue, but since I haven’t solved it yet you will have to settle for a bug fix.]

Random bug: when the cursor hovers over the custom navigator title bar a tooltip opens letting us know that the navigator can’t find a label for the root node. The full error message is Error: no label provider for R/. Tells you everything you need to know. Except what the problem is. Or how to fix it. Or, for Eclipse novices, what R/ means.

Luckily this is something that we are not afflicted with here at Hidden Clause. The message did tell us everything we needed to know. The Custom Navigator label provider is ignoring the root node used by the navigator (the R/ referred to in the error) and returning an empty string. The code for LabelProvider.getText() is:

   public String getText(Object element) {
        String text = ""; //$NON-NLS-1$
        if (ICustomProjectElement.class.isInstance(element)) {
            text = ((ICustomProjectElement)element).getText();
        }
        // else ignore the element

        return text;
    }

(Notice how it so brilliantly ignores everything except elements of type ICustomProjectElement.)

What the message also tells us, by not telling us, is that our zero-length string appears to be causing consternation in the navigator. It is causing so much consternation that the navigator thinks no label provider is available to supply it with a default label for the root node.

That something is easily fixed in the LabelProvider. I’m not sure why Eclipse does not default to no string for the root (damn, those double negatives!), but it does not so we have to assign something to it. Since the standard behavior for other navigator views is to use the name of the view, in this case Custom Plug-in Navigator, that is what we will do.

  1. Open LabelProvider.java
  2. Change getText() to include an else if:
       public String getText(Object element) {
            String text = ""; //$NON-NLS-1$
            if (ICustomProjectElement.class.isInstance(element)) {
                text = ((ICustomProjectElement)element).getText();
            } else if (IWorkspaceRoot.class.isInstance(element)) {
                text = "Custom Plug-in Navigator";
            }
            // else ignore the element
    
            return text;
        }
    
  3. Start the runtime workbench and behold the beauty of our new string.

For Those of You Who Care

I discovered the solution to the above by putting a breakpoint in LabelProvider.getText() and walking the call tree. NavigatorContentServiceLabelProvider.findStyledText() quite explicitly changed the original empty string returned by LabelProvider.getText() into a null which caused NavigatorContentServiceLabelProvider.getStyledText() to assign the error message to the navigator view. It makes sense, it just wasn’t what I wanted.

Extra credit: Run the Externalize String Wizard on LabelProvider.java and add the new string to the message.properties file.

What Just Happened?

Fixed a bug. Contain your excitement.

The cat was not impressed and refused to come out.

Code

messages.properties

CustomProjectParent_Project_Folder=icons/project-folder.png
CustomProjectSchema_Project_Schema=icons/project-schema.png
CustomProjectSchemaFilters_Project_Schema_Filters=icons/project-schema-filters.png
CustomProjectSchemaTables_Project_Schema_Tables=icons/project-schema-tables.png
CustomProjectSchemaViews_Project_Schema_Views=icons/project-schema-views.png
CustomProjectStoredProcedures_Project_Stored_Procedures=icons/project-stored-procedures.png
LabelProvider_Custom_Plugin_Navigator=Custom Plug-in Navigator

LabelProvider.java

/**
 * Coder beware: this code is not warranted to do anything.
 *
 * Copyright Oct 17, 2009 Carlos Valcarcel
 */
package customnavigator.navigator;

import org.eclipse.core.resources.IWorkspaceRoot;
import org.eclipse.jface.viewers.ILabelProvider;
import org.eclipse.jface.viewers.ILabelProviderListener;
import org.eclipse.swt.graphics.Image;

/**
 * @author carlos
 *
 */
public class LabelProvider implements ILabelProvider {

    /* (non-Javadoc)
     * @see org.eclipse.jface.viewers.ILabelProvider#getImage(java.lang.Object)
     */
    @Override
    public Image getImage(Object element) {
        Image image = null;
        
        if (ICustomProjectElement.class.isInstance(element)) {
            image = ((ICustomProjectElement)element).getImage();
        }
        // else ignore the element
        
        return image;
    }

    /* (non-Javadoc)
     * @see org.eclipse.jface.viewers.ILabelProvider#getText(java.lang.Object)
     */
    @Override
    public String getText(Object element) {
        String text = ""; //$NON-NLS-1$
        if (ICustomProjectElement.class.isInstance(element)) {
            text = ((ICustomProjectElement)element).getText();
        } else if (IWorkspaceRoot.class.isInstance(element)) {
            text = Messages.LabelProvider_Custom_Plugin_Navigator;
        }
        // else ignore the element
        
        return text;
    }

    /* (non-Javadoc)
     * @see org.eclipse.jface.viewers.IBaseLabelProvider#addListener(org.eclipse.jface.viewers.ILabelProviderListener)
     */
    @Override
    public void addListener(ILabelProviderListener listener) {
        // TODO Auto-generated method stub

    }

    /* (non-Javadoc)
     * @see org.eclipse.jface.viewers.IBaseLabelProvider#dispose()
     */
    @Override
    public void dispose() {
        // TODO Auto-generated method stub

    }

    /* (non-Javadoc)
     * @see org.eclipse.jface.viewers.IBaseLabelProvider#isLabelProperty(java.lang.Object, java.lang.String)
     */
    @Override
    public boolean isLabelProperty(Object element, String property) {
        // TODO Auto-generated method stub
        return false;
    }

    /* (non-Javadoc)
     * @see org.eclipse.jface.viewers.IBaseLabelProvider#removeListener(org.eclipse.jface.viewers.ILabelProviderListener)
     */
    @Override
    public void removeListener(ILabelProviderListener listener) {
        // TODO Auto-generated method stub

    }

}
Advertisements
  1. bwgz57
    March 5, 2010 at 2:31 am

    This multi-part thread on Eclipse Plug-ins has been very helpful. About a month ago I started a similar exploration and reading these parts has filled in several holes. I stumbled across your posts about a week ago. I wished I had found it sooner.

    One area that was a bit discouraging when using Eclipse was the integration of standard actions (open, delete, copy, paste). Ultimately it required me writing my own handlers. For expediency I reused the existing internal Eclipse code and modified it as needed. Still it’s a lot of code there and that code dependent on the resource centric classes such as DeleteResourceAction. While I persist my underlying model as files the user only sees the objects persisted in those files. Right now when deleting one of those objects by deleting the file via DeleteResourceAction the messages presented to the user refer to the file. So eventually I’ll have to redo all the handling code.

    Eclipse’s migration from actions to commands also makes thing harder to understand. It’s hard to know sometimes when to use a standard command vs. an action. You have to weed through things to know what’s been converted and what’s not. Getting my menu’s to behave correctly took a lot of trial and error.

    But overall it’s been worthwhile. Once you have the recipe, creating an application can be a pretty rapid exercise.

    I look forward to your next post.

    PS: I’ve yet to see this random bug you’ve written about.

  2. cvalcarcel
    March 6, 2010 at 12:26 am

    Sad to say there are a number of areas of Eclipse that are discouraging. It is a platform, not a framework, and with that comes a level of complexity that is still quite daunting even after all these years. With all of the examples available it is easy to find simple tasks that have no documentation or samples to work from. Also, because there are so many ways to do things in Eclipse, it can be difficult to choose the most straightforward implementation of a standard extension (just look at the Common Navigator Framework!).

    There are many in the Eclipse community who feel your pain (myself included). While the platform does get better every day it is also obvious that the platform will always be complex. I started blogging in an attempt to add to the community folklore and point out that simple things really can be simple, but I have also discovered that you have to dig to find those gems. I much prefer configuration to code mainly out of the fear that I may have missed a simple way to do something. When I find myself doing plug-in coding I suspect that I may not be implementing the code in the most optimal way for Eclipse; unfortunately, while the existing code can be of use, it is not always the most optimal way to learn either.

    A possible-maybe-someday-but-I’m-not-sure project I have been considering is the creation of a wizard to create a plug-in front-to-back. Many of the things I have done so far are not rocket science (in fact, none of them are). Why can’t we just walk a wizard with the information it needs to create a new perspective with one or more basic views (with hooks in place to custom them), and one or more editors (with hooks in place to custom them)?

    Templates sounds like a starting point to make this kind of thing easier. In fact, why can’t we customize a plug-in while it is running? The typical user would not do that, but the advanced ones might: allow them to add/move/remove items from menus, change labels, add behavior to views and editors, etc. It would be great if we could do that from the runtime workbench; imagine how much time that could save instead of coding, running the runtime workbench, coding some more, running the runtime workbench, etc. TDD helps to some extent, but the time from implementation to actually checking something, especially debugging, does seem rather long.

    But that is just me.

    And about the random bug: I saw it almost from day one and just ignored it. As you see it wasn’t hard to fix, but I am also using Eclipse 3.6M4 so who knows if it is a transient problem will disappear in the final release.

    Glad you enjoy the posts!

  1. No trackbacks yet.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: