Tuesday, March 20, 2007

NetBeans Groovy console

Alright, I didn't think I'd be putting it out there, but since
Geertjan blogged about it, here is the source to what I was doing with
the Groovy console in NetBeans.. I didn't think I'd be putting it
somewhere, since it's a bit of a hack (a proper solution to the
problem would probably use GroovyShell to interpret things and use
purely NetBeans for the creation of the UI end of things). But, since
it's out there in the wild, here is my "New and Improved" NetBeans groovy console.
One cool thing to note is that it picks up the Groovy Console key mappings, thus all shortcuts work exactly the same.



package com.troymaxventures.nbgroovyconsole;

import groovy.lang.Binding;
import java.awt.BorderLayout;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import javax.swing.Action;
import javax.swing.ActionMap;
import javax.swing.InputMap;
import javax.swing.JComponent;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.KeyStroke;
import javax.swing.MenuElement;
import org.openide.ErrorManager;
import org.openide.explorer.ExplorerManager;
import org.openide.explorer.ExplorerUtils;
import org.openide.util.Lookup;
import org.openide.util.NbBundle;
import org.openide.windows.TopComponent;
import org.openide.windows.WindowManager;

/**
* Top component which displays something.
*/
final class GroovyConsoleTopComponent extends TopComponent implements
ExplorerManager.Provider, Lookup.Provider {

private static GroovyConsoleTopComponent instance;
/** path to the icon used by the component and its open action */
// static final String ICON_PATH = "SET/PATH/TO/ICON/HERE";

private static final String PREFERRED_ID = "GroovyConsoleTopComponent";
private ExplorerManager manager;
private Lookup lookup;

private GroovyConsoleTopComponent() {
initComponents();
manager = new ExplorerManager();
setName(NbBundle.getMessage(GroovyConsoleTopComponent.class,
"CTL_GroovyConsoleTopComponent"));
setToolTipText(NbBundle.getMessage(GroovyConsoleTopComponent.class,
"HINT_GroovyConsoleTopComponent"));
Binding bind = new Binding();
groovy.ui.Console console = new
groovy.ui.Console(this.getClass().getClassLoader(),bind);
bind.setProperty("console",console);
try {
console.run();
add(console.getFrame().getJMenuBar(),BorderLayout.NORTH);
add(console.getFrame().getContentPane(),BorderLayout.CENTER);
ActionMap am = this.getActionMap();
InputMap im =
this.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
setShortcutMaps(console.getFrame().getJMenuBar(),am,im);
console.getFrame().setVisible(false);
this.revalidate();
lookup = ExplorerUtils.createLookup(manager, am);
} catch (Throwable e) {
e.printStackTrace();
ErrorManager.getDefault().notify(e);
}


// setIcon(Utilities.loadImage(ICON_PATH, true));
}

private void setShortcutMaps(JMenuBar jmb, ActionMap am, InputMap im) {

for (JMenuItem me3 : getMenuItems(jmb)) {
Action a = ((JMenuItem)me3).getAction();
KeyStroke k = ((JMenuItem)me3).getAccelerator();
im.put(k,a.getValue(Action.NAME));
am.put(a.getValue(Action.NAME),a);
}

}

private List getMenuItems(MenuElement me) {
List thisLevelMenuItems = new ArrayList();
if (me!=null && me.getSubElements()!=null &&
me.getSubElements().length>0) {
for (MenuElement me1 : me.getSubElements()) {
if (me1 instanceof JMenuItem && !(me1 instanceof JMenu)) {
thisLevelMenuItems.add((JMenuItem)me1);
} else {
thisLevelMenuItems.addAll(getMenuItems(me1));
}
}
}
return thisLevelMenuItems;
}

public ExplorerManager getExplorerManager() {
return manager;
}
public Lookup getLookup() {
return lookup;
}
// ...methods as before, but replace componentActivated and
// componentDeactivated with e.g.:
public void addNotify() {
super.addNotify();
ExplorerUtils.activateActions(manager, true);
}
public void removeNotify() {
ExplorerUtils.activateActions(manager, false);
super.removeNotify();
}

/** This method is called from within the constructor to
* initialize the form.
* WARNING: Do NOT modify this code. The content of this method is
* always regenerated by the Form Editor.
*/
//
private void initComponents() {
pnlButtons = new javax.swing.JPanel();
pnlMain = new javax.swing.JPanel();

setLayout(new java.awt.BorderLayout());

}//


// Variables declaration - do not modify
private javax.swing.JPanel pnlButtons;
private javax.swing.JPanel pnlMain;
// End of variables declaration

/**
* Gets default instance. Do not use directly: reserved for
*.settings files only,
* i.e. deserialization routines; otherwise you could get a
non-deserialized instance.
* To obtain the singleton instance, use {@link findInstance}.
*/
public static synchronized GroovyConsoleTopComponent getDefault() {
if (instance == null) {
instance = new GroovyConsoleTopComponent();
}
return instance;
}

/**
* Obtain the GroovyConsoleTopComponent instance. Never call
{@link #getDefault} directly!
*/
public static synchronized GroovyConsoleTopComponent findInstance() {
TopComponent win =
WindowManager.getDefault().findTopComponent(PREFERRED_ID);
if (win == null) {
ErrorManager.getDefault().log(ErrorManager.WARNING,
"Cannot find GroovyConsole component. It will not be located properly
in the window system.");
return getDefault();
}
if (win instanceof GroovyConsoleTopComponent) {
return (GroovyConsoleTopComponent)win;
}
ErrorManager.getDefault().log(ErrorManager.WARNING, "There
seem to be multiple components with the '" + PREFERRED_ID + "' ID.
That is a potential source of errors and unexpected behavior.");
return getDefault();
}

public int getPersistenceType() {
return TopComponent.PERSISTENCE_ALWAYS;
}

public void componentOpened() {
// TODO add custom code on component opening
}

public void componentClosed() {
// TODO add custom code on component closing
}

/** replaces this in object stream */
public Object writeReplace() {
return new ResolvableHelper();
}

protected String preferredID() {
return PREFERRED_ID;
}

final static class ResolvableHelper implements Serializable {
private static final long serialVersionUID = 1L;
public Object readResolve() {
return GroovyConsoleTopComponent.getDefault();
}
}

}