bluej-logo BlueJ
Search the BlueJ site

 
 
home
about BlueJ
download
documentation
extensions
help & Info
resources

WRITING EXTENSIONS FOR BLUEJ

Introduction

The BlueJ extensions mechanism is a way of adding new functionality to BlueJ as and when it is needed, avoiding user interface clutter and user confusion.

The BlueJ Extensions API provides access for extensions to the BlueJ application via a proxy object, and to the classes and objects which BlueJ is manipulating via a number of wrapper classes.

An extension can add a menu item to the BlueJ Tools menu, and to Class and Object menus, and add a preference panel to the Tools/Preferences/Extensions panel. The BlueJ proxy object generates events when the user performs significant actions within BlueJ, and provides access to its projects, packages, classes and objects via a set of wrapper classes which behave in a similar way to the java.lang.reflect classes.

A Simple Extension

The following example implements an extension which logs the name of every BlueJ project opened by the user to System.out. Once it is installed you should also see:

  • an entry for it in the "Installed Extensions" panel of the BlueJ help menu
  • (fairly useless) menu entries to BlueJ's Tools menu, and to the menus displayed for classes and objects
  • an entry in the Tools/Preferences/Extensions panel which prompts for (and stores) your favourite colour.

The full source code for the extension is here.

How to build and install the Simple Extension
  • Compile it, including lib/bluejext.jar from your BlueJ installation in the compiler classpath
  • Create a Jar file containing the classes of your extension, and add a Main-Class attribute to the Manifest of the Jar file. Note that this does not mean that the extension needs to have a main() method. The simplest way to do this is to create a file (called, say, manifest.txt) containing the single line:
    Main-Class: SimpleExtension
    This information can be included in the Jar file when it is created using the m option to the jar command, e.g.:
    jar cmf manifest.txt SimpleExtension.jar *.class
  • Install the extension by copying the Jar file either into the lib/extensions directory of your BlueJ installation, or into an extensions directory in your BlueJ user configuration directory (<USER_HOME>/.bluej/extensions or <USER_HOME>\bluej\extensions), or into an extensions directory in the single BlueJ project you wish the extension to be active for.
  • Run it. The extension should start when you next run BlueJ, or when you open the BlueJ project into which you installed the extension.
The main class of the Simple Extension

An object of this type will be constructed by the BlueJ extension manager, so this class must have a no-args constructor (which this one does by default). The class registers menu items and a Preference panel item, then registers itself as a listener for BlueJ package events.

import bluej.extensions.*;
import bluej.extensions.event.*;
import java.net.URL;
import javax.swing.*;
import java.awt.event.*;
/*
 * This is the starting point of a BlueJ Extension
 */
public class SimpleExtension extends Extension implements PackageListener                {
    /*
     * When this method is called, the extension may start its work.
     */
    public void startup (BlueJ bluej) {
        bluej.setMenuGenerator(new MenuBuilder());
        Preferences myPreferences = new Preferences(bluej);
        bluej.setPreferenceGenerator(myPreferences);
        bluej.addPackageListener(this);
    }
    /*
     * A package has been opened. Print the name of the project it is part of.
     */
    public void packageOpened ( PackageEvent ev ) {
        try {
            System.out.println ("Project " + ev.getPackage().getProject().getName()                + " opened.");
        } catch (ExtensionException e) {
            System.out.println("Project closed by BlueJ");
        }
    } 
               
    /*
     * A package is closing.
     */
    public void packageClosing ( PackageEvent ev ) {
    } 
               
    /*
     * This method must decide if this Extension is compatible with the                
     * current release of the BlueJ Extensions API
     */
    public boolean isCompatible () { 
        return true; 
    }
    /*
     * Returns the version number of this extension
     */
    public String getVersion () { 
        return ("2003.11"); 
    }
    /*
     * Returns the user-visible name of this extension
     */
    public String getName () { 
        return ("Simple Extension"); 
    }
    public void terminate() {
        System.out.println ("Simple extension terminates");
    }
               
    public String getDescription () {
        return ("A simple extension");
    }
    /*
     * Returns a URL where you can find info on this extension.
     * The real problem is making sure that the link will still be alive in three years...
     */
    public URL getURL () {
        try {
            return new URL("http://www.bluej.org/doc/writingextensions.html");
        } catch ( Exception eee ) {
            // There is no reason at all that this should trow exception...
            System.out.println ("Simple extension: getURL: Exception="+eee.getMessage());
            return null;
        }
    }
}
Adding menus to BlueJ's menus

Extensions which wish to add a menu item to BlueJ's menus should register an instance of MenuGenerator with the BlueJ proxy object. A MenuGenerator provides a set of functions which can be called back by BlueJ to request the actual menu items which will be displayed, and to indicate that a particular menu item is about to be displayed, so that an extension can (e.g.) enable or disable appropriate items. Note that the JMenuItem which is returned by the extension can itself be a JMenu, allowing extensions to build more complex menu structures, but that the "notify" methods below will only be called for the item which has actually been added, and not any subsidiary items. Below is a simple example which creates menus for Tools, Classes and Objects. To activate the menus you instantiate an object of the ExtensionMenu class and then register it with the BlueJ proxy object, e.g.:

    MenuBuilder myMenus = new MenuBuilder();
    bluej.setMenuGenerator(myMenus);


Note that the MenuGenerator's get*MenuItem() methods:

  • may be called more than once during a BlueJ session, they should return a new set of MenuItems for each invocation. This is a restriction required by the Swing implementation, which does not allow sharing of MenuItems between menus. You can, of course, share MenuActions between all of the appropriate MenuItems.
  • may not be called between the registration of a new MenuGenerator and the display of a menu. That is to say old menu items may still be active for previously registered menus, despite the registration of a new MenuGenerator.
  • will be called at least once for every menu which is displayed.

The source code for this example MenuGenerator is:

class MenuBuilder extends MenuGenerator {
private ToolsAction aToolsAction;
private ClassAction aClassAction;
private ObjectAction aObjectAction;
private BPackage curPackage;
private BClass curClass;
private BObject curObject;
    MenuBuilder() {
        aToolsAction = new ToolsAction("Click Tools");
        aClassAction = new ClassAction("Click Class");
        aObjectAction = new ObjectAction("Click Object");
    }
    public JMenuItem getToolsMenuItem(BPackage aPackage) {
        return new JMenuItem(aToolsAction);
    }
    public JMenuItem getClassMenuItem(BClass aClass) {
        return new JMenuItem(aClassAction);
    }
    public JMenuItem getObjectMenuItem(BObject anObject) {
        return new JMenuItem(aObjectAction);
    }
    // A utility method which prints the objects involved in the current
    // menu invocation.
    private void printCurrentStatus(String header) {
        try {
            if (curObject != null)
               curClass = curObject.getBClass();
            if (curClass != null)
               curPackage = curClass.getPackage();
               
            System.out.println(header);
            if (curPackage != null)
                System.out.println(" Current Package=" + curPackage);
            if (curClass != null)
                System.out.println(" Current Class=" + curClass);
            if (curObject != null)
                System.out.println(" Current Object=" + curObject);
            } catch (Exception exc) { }
        }
    // Now the nested classes that instantiate the different menus.
    class ToolsAction extends AbstractAction {
        public ToolsAction(String menuName) {
            putValue(AbstractAction.NAME, menuName);
        }
        public void actionPerformed(ActionEvent anEvent) {
            printCurrentStatus("Tools menu:");
        }
    }
    class ClassAction extends AbstractAction {
        public ClassAction(String menuName) {
            putValue(AbstractAction.NAME, menuName);
        }
        public void actionPerformed(ActionEvent anEvent) {
            printCurrentStatus("Class menu:");
        }
    }
    class ObjectAction extends AbstractAction {
        public ObjectAction(String menuName) {
            putValue(AbstractAction.NAME, menuName);
        }
        public void actionPerformed(ActionEvent anEvent) {
            printCurrentStatus("Object menu:");
        }
    }
               
    // and the methods which will be called in the main class when
    // each of the different menus are about to be invoked.
    public void notifyPostToolsMenu(BPackage bp, JMenuItem jmi) {
        System.out.println("Post on Tools menu");
        curPackage = bp ; curClass = null ; curObject = null;
    }
               
    public void notifyPostClassMenu(BClass bc, JMenuItem jmi) {
        System.out.println("Post on Class menu");
        curPackage = null ; curClass = bc ; curObject = null;
    }
               
    public void notifyPostObjectMenu(BObject bo, JMenuItem jmi) {
        System.out.println("Post on Object menu");
        curPackage = null ; curClass = null ; curObject = bo;
    }
}
Adding items to BlueJ's Preferences panel

Extensions which wish to add preference items to BlueJ's Tools/Preferences/Extensions panel should register an instance of PreferenceGenerator with the BlueJ proxy object. The PreferenceGenerator allows the creation of a Panel to contain preference data, and the loading and saving of that data. Below is a simple example to create a preference panel with a single text item to record a user's favourite colour. To activate the preference panel you instantiate an object of the Preferences class and then register it with the BlueJ proxy object (see above).

class Preferences implements PreferenceGenerator {
private JPanel myPanel;
private JTextField color;
private BlueJ bluej;
public static final String PROFILE_LABEL="Favorite-Colour";
    // Construct the panel, and initialise it from any stored values
    public Preferences(BlueJ bluej) {
        this.bluej = bluej;
        myPanel = new JPanel();
        myPanel.add (new JLabel ("Favorite Colour"));
        color = new JTextField (40);
        myPanel.add (color);
        // Load the default value
        loadValues();
    }
    public JPanel getPanel () { return myPanel; }
    public void saveValues () {
        // Save the preference value in the BlueJ properties file
        bluej.setExtensionPropertyString(PROFILE_LABEL, color.getText());
    }
    public void loadValues () {
        // Load the property value from the BlueJ proerties file, default to an empty string
        color.setText(bluej.getExtensionPropertyString(PROFILE_LABEL,""));
    }
}
 
 

Discussion Group

bluej-discuss

Announcements

For notificaton of new releases, subscribe to: BlueJ-announce

 
Bug Parade
View or submit bugs
maintained by
Michael Kölling
.
supported by Oracle logo