500 NetBeans Users…
The NetBeans User Group in XING has almost 497 members now. Will you be member number 500?
http://www.xing.com/group-20148.82db20
The NetBeans User Group in XING has almost 497 members now. Will you be member number 500?
http://www.xing.com/group-20148.82db20
In NetBeans there are some useful Code Templates, like “sout” which, when you type it in the editor and hit “Tab” will be expanded to “System.out.println();”. But you’re not limited to the predefined ones, you can also create your own templates. I’ve been playing around with Code Templates recently and was a bit disappointed, because the avaiable set of default parameters is quite limited. E.g. I tried to add some nice templates for logging, and I was missing a way to get hold of the class name of the current file. As a workaround I did something like:
java.util.logging.Logger.getLogger(getClass()”getName()).log(java.util.logging.Level.INFO}, “${cursor}”);
Today I asked on the list if there is no other way, and I received an answer by Michel Graciano that there are a couple of Templates related to Logging that add the class name. I replied that I had looked at every single Template and none of them is related to Logging, when he said he uses 6.9 M1. And there it is, shortcut “log”:
${loggerType type=”java.util.logging.Logger” default=”Logger” editable=”false”}.getLogger(${classVar editable=”false” currClassName default=”getClass()”}.class.getName()).log(${levelType type=”java.util.logging.Level” editable=”false” default=”Level”}.${logLevel default=”INFO” completionInvoke}, “${message}”);
“currClassname” adds the functionality I’ve been missing. ( Actually I think default=”getClass()” doesn’t make much sense, because it would be expanded to “…getClass().class.getName()..” (?) ). There’s also “loge” for logging errors and “logp” for logging with parameters… Thanks Michel!
For the first time the fotos are available before the course ended. And for the first time we had to do a Panorama shot to get all people on the picture
Thanks to Jacek for creating these great pictures!
Hurray! The NetBeans Platform has it’s own Refcard. It’s full of very useful tips. You can get it here:
http://refcardz.dzone.com/refcardz/essential-netbeans-platform
When using OutlineView or other ExplorerView a tooltip text is displayed when hovering over a value. In some cases you want to tweak this or not display tooltips for a certain value at all. In Quick Tip 39 I’ve described how you can display an Icon. That’s a good example. By default a long and useless String is displayed here (toString of the displayed Image). To fix that simply override getAsText:
@Override
public String getAsText() {
// we don’t want a tooltiptext…
return “”;
}
When I’m using OutlineView to show a table, most of the time I have read only properties. By default OutlineView uses the normal foreground (usually black) and brightens it in order to make clear that this is not editable. That’s nice for some usecases, but usually not for mine. To change this you can create avery simple PropertyEditorSupport like this:
public class DisabledStringPropertyEditorSupport extends PropertyEditorSupport {
JLabel renderer = new JLabel();
@Override
public boolean isPaintable() {
return true;
}
@Override
public void paintValue(Graphics g, Rectangle r) {
renderer.setText(getAsText());
renderer.setBounds(r);
renderer.paint(g);
}
}
and set it either on a PropertySupport.Reflection:
PropertySupport.Reflection p = new PropertySupport.Reflection( o, int.class, Test.class.getMethod(”getCount”, null), null);
p.setPropertyEditorClass(DisabledStringPropertyEditorSupport.class);
or in your Property override:
DisabledStringPropertyEditorSupport d = new DisabledStringPropertyEditorSupport();
@Override
public PropertyEditor getPropertyEditor() {
return d;
}
and you’ve got a nice black foreground…
Beware: needs tweaking for non-String values
I’m moving. Initially I was sorry, because I liked my old office a lot, but since the new one is a lot bigger and in the same street, I’m quite happy now. There’s only one very annoying thing about moving and that’s my DSL provider. I’m with m-net because they have a very fine grained collection of optional features (like fixed IP address). So yesterday I called them to talk about moving my phone and DSL connection to the new location, because even though I’m moving I’m not allowed to cancel my contract. That’s already a little bit annoying, because if e.g. a couple decides to move in together, they’ll end up with two phone lines + internet connection, but OK. The really annoying thing is, that when reading the form for moving I found that the contract starts again, so in my case I’ll again be bound 24 months to this provider.
I called them again to find out if I misinterpreted the form, and the (very nice) person on the customer service told me that I was right. So I’m not allowed to cancel my contract, and I’m not allowed to keep it, I must make a new contract over the full period again. Obiously I started arguing and asked for the rationale behind that, and the guy told me that even though I was actually paying an additional fee for moving my phone number and IP address to the new location they’ve got enormous costs of hundreds of Euros for setting things up. After a while the guy on the line said “that’s the official version I have to tell customers, my personal opinion is different”. So while being sure that this is only crap, I’ll have to bite the bullet.
Update: it seems, that what they (and other providers) are doing is against German law ( Paragraf 313 BGB ):
http://www.geldundverbraucher.de/index.php?node=1593
There are already a lot of nice explorerviews around, but the widget set is missing some commonly used views, like e.g. TaskPanes. They can be a good replacement for ListView to show a flat list of Nodes. Actions can then be displayed in a hyperlinked list instead of a context menu. It’s quite simple to create a simple view with e.g. the JXTaskPane from SwingX.
For a very simple view (beware, no dynamic updates), you only need to put your JXTaskPane under control of an ExplorerManager:
public class TaskPaneView extends JScrollPane {
private transient ExplorerManager manager;
// create a taskpanecontainer
JXTaskPaneContainer taskpanecontainer = new JXTaskPaneContainer();
/** Listener to nearly everything */
transient Listener managerListener;
/** weak variation of the listener for property change on the explorer manager */
transient PropertyChangeListener wlpc;
/** True, if the selection listener is attached. */
transient boolean listenerActive;
// UI Settings:
Font labelFont = new Font("Segoe UI", Font.BOLD, 14);
Painter backgroundPainter = new MattePainter(Color.white);
public TaskPaneView() {
setViewportView(taskpanecontainer);
}
public void setBackground(Painter background) {
this.backgroundPainter = background;
}
public void setLabelFont(Font labelFont) {
this.labelFont = labelFont;
}
@Override
public void addNotify() {
super.addNotify();
ExplorerManager em = ExplorerManager.find(this);
if (em != manager) {
if (manager != null) {
manager.removePropertyChangeListener(wlpc);
}
manager = em;
manager.addPropertyChangeListener(wlpc = WeakListeners.propertyChange(managerListener, manager));
Node root = manager.getExploredContext();
setRootNode(root);
} else {
// bugfix #23509, the listener were removed --> add it again
if (!listenerActive && (manager != null)) {
manager.addPropertyChangeListener(wlpc = WeakListeners.propertyChange(managerListener, manager));
}
}
}
/** Removes listeners.
*/
@Override
public void removeNotify() {
super.removeNotify();
listenerActive = false;
// bugfix #23509, remove useless listeners
if (manager != null) {
manager.removePropertyChangeListener(wlpc);
}
}
private void setRootNode(Node root) {
//throw new UnsupportedOperationException("Not yet implemented");
taskpanecontainer.removeAll();
System.out.println("root node set " + root);
Node[] children = root.getChildren().getNodes();
for (int i = 0; i < children.length; i++) {
Node node = children[i];
JXTaskPane taskPane = new JXTaskPane();
taskPane.setName(node.getName());
taskPane.setCollapsed(true);
taskPane.setTitle(node.getDisplayName());
taskPane.setIcon(new ImageIcon(node.getIcon(BeanInfo.ICON_COLOR_16x16)));
Action [] actions = node.getActions(true);
for (int j = 0; j < actions.length; j++) {
Action action = actions[j];
taskPane.add(action);
}
taskpanecontainer.add(taskPane);
}
}
private final class Listener implements PropertyChangeListener {
public void propertyChange(PropertyChangeEvent evt) {
if (ExplorerManager.PROP_EXPLORED_CONTEXT.equals(evt.getPropertyName())) {
setRootNode(manager.getExploredContext());
return;
}
}
}
}
Admittedly it’s a very basic implementation and not dynamic, but I guess it illustrates that there’s no magic in creating Explorer Views…
…and start by simplifying NetBeans.
I’ve just returned from a training at skyguide in Geneva. We where invited there by a group of enthusiastic Java pros who are thinking about porting some of their applications to NetBeans. When teaching the NetBeans Platform some APIs are harder to understand for new users than others - especially the Nodes API, System FileSystem and Lookups. If you’ve been using these APIs for a while they actually seem very simple. So when it’s hard to explain an API which is actually quite elegant and simple, there must be something wrong. The main issues seem to be strange naming on the one side and mixing of concerns on the other side. Often it’s only small things that get into your way when learning the platform. Here are some suggestions, what could be improved. I’m starting to collect these ideas, so feel free to add your own ideas in the comments section:
Let’s start with Lookup API. There are several problems with this API for beginners. The first problem is, that the term “Lookup” is used for a lot of different things. First it is used for a generic API, the abstract Lookup class and it’s companions, like Lookup.Template, Lookup.Result and LookupListener. But the same term is also used synonymously for some very specific uses of this API, namely as a Service loader (Lookup.getDefault()) and for global selection management (Utilities.actionsGlobalContext()). Somehow it’s also an unfortunate mixture that Lookup.getDefault() is a method in the Lookup class, because I think it binds the data structure to close to one of it’s uses.
Just imagine a less elaborate and powerful mechanism for service loading using a HashMap with Class objects as keys and Collections of instances as values. You probably wouldn’t expect something like HashMap.getDefault() to return a ServiceLoader. For a new user the situation is similar when they first see Lookup. If ServiceLoading were the only purpose of Lookup it would be OK, but since Lookup is used pervasively and for totally different purposes it’s more like a data structure. In the case of Utilities.actionsGlobalContext() I think it’s bad naming that get’s into the way of an easy understanding.
So the first thing that could be done to simplify the API would be to create a ServiceLoader class with a user friendly name as a wrapper for Lookup.getDefault():
public class ServiceLoader{
public static <T> Collection<? extends T> loadServices(Class<T> clazz) { return Lookup.getDefault().lookupAll(clazz);}
public static void addServiceListener(Class clazz, LookupListener listener) {Lookup.Result result = Lookup.getDefault().lookupResult(clazz); ...}
public static void removeServiceListener(Class clazz, LookupListener listener){...}}
This would serve several purposes.
So instead of this:
Lookup.getDefault().lookupAll(ServiceProvider.class);
We would write:
ServiceLoader.loadServices(ServiceProvider.class);
and instead of:
Lookup lookup = Lookup.getDefault(); Lookup.Result result = lookup.lookupResult(ServiceProvider.class); result.addLookupListener(listener); result.allInstances();
It would be:
ServiceLoader.addServiceListener(ServiceProvider.class, listener);
It would be even nicer if the LookupListener could be wrapped in a ServiceListener with convenience methods that totally hide the Lookup.Result. Another extension might be to have a method getRegisteredServices() that returns the fully qualified name of all class objects that are registered as keys.
The same could be done for Selection Management with a SelectionManager that gives a nicer interface to Utilities.actionsGlobalContext().
public class SelectionManager{
public static void addSelectionListener(Class clazz, LookupListener listener) {...}
public static void removeSelectionListener(Class clazz, LookupListener listener){...}
}
Again a user friendly name and hiding the initialization of the Lookup.Result would simplify the usage a lot. So when you want to listen for selection instead of:
Lookup lookup = Utilities.actionsGlobalContext(); Lookup.Result result = lookup.lookupResult(MyInteresting.class); result.addLookupListener(listener); result.allInstances();
It would be:
SelectionManager.addSelectionListener(MyInteresting.class, listener);
This way the naming reflects what the user is using it for, so it’s much easier to remember. The main benefit when explaining Lookup is the separation of concerns, and since all the changes could be done by simple wrappers it would be fairly easy to implement it in a fully backward compatible way.
FileSystems again have a similar problem. First it’s an API for accessing structured data, very simple and powerful. But again the name and concept is overloaded with the DefaultFileSystem or System FileSystem. Although it’s basically only a usecase for FileSystem API to create a central registry for intermodule communication it’s very often confused with the API itself. And have a look at the code:
FileSystem filesystem = Repository.getDefault().getDefaultFileSystem();
Who would guess that we’re accessing the Registry here? It would be much nicer like this:
Registry registry = Registry.getInstance();
Even a simpe name change like this would help:
FileSystem registry = Registry.getInstance();
Another thing that would be useful would be to mark Extension points in the layer. I’ve recently filed an issue for this, and again in Geneva I was asked by a attendee of the course, how he can easily find available extension points from inside the IDE. Other Platforms have this feature, and I think it’s very useful for learning more about the platform.
The main problem in understanding the Nodes API is the use of Children. I’m not saying something is wrong about using Children ( now that sounded really weird! ), but when first learning the API they get into the way of understanding. Users who are starting with the platform are usually Swing developers looking for more. As Swing developers they know JTree and DefaultMutableTreeNode. So when looking at the Nodes hierarchy they are looking for something similar. Instead the first thing they learn is they need to create a “Children Object” or a ChildFactory with a setKeys method and a createNodeForKey method that seem to be magically called from somewhere.
Here we would benefit from a SimpleNode, maybe like this:
public class SimpleNode extends AbstractNode{
private Children.Array childrenArray ;
private InstanceContent content;
public SimpleNode() {
this(new Children.Array(), new InstanceContent());
}
public SimpleNode(String name){
this(name, name);
}
public SimpleNode(String name, Object userObject){
this();
content.add(userObject);
super.setName(name);
}
private SimpleNode( Children.Array childrenArray, InstanceContent ic){
super(childrenArray, new AbstractLookup(ic));
this.childrenArray = childrenArray;
this.content = ic;
}
public void addChildNode(Node node){
addChildNodes(new Node[]{node});
}
public void addChildNodes(Node [] nodes){
childrenArray.add(nodes);
}
public void removeChildNode(Node nodes){
removeChildNodes(new Node [] {nodes});
}
public void removeChildNodes(Node [] nodes){
childrenArray.remove(nodes);
}
}
So instead of having to implement a ChildFactory a user could simply generate SimleNodes and put his hierarchy together in a very simple way. For many usecases this will be fine and in case advanced node creation is required the Children concept can be used.
The “Hello World!” example would now look like this:
SimpleNode root = new SimpleNode("I'm a maid I should mary any:");
root.addChildNode(new SimpleNode("Tom"));
root.addChildNode(new SimpleNode("Dick"));
root.addChildNode(new SimpleNode("or Harry."));
explorermanager.setRootContext(root);
Much simpler and easier to grasp, than having to create a ChildFactory and still fully compatible.
I guess with those little changes it would be easier to understand these APIs. What do you think?
As some of you might have noticed, the plugin that integrates the (kirill)cool Substance look & feel with NetBeans has been discontinued, because it’s so complicated to write all the UIDelegates:
substance-netbeans: Project Home Page.
One of the reasons, why I created NBTabbedPane was, to bring Substance back without the need of creating custom UI Delegates. But it was harder than I thought, because substance checks for violations of the new EDT rules, and in NetBeans there are some.
So I was glad to see that Josh took care of this:
JNBB: Plugin: NetBeans Substance 5.2 Look and Feel.
Now here’s a screenshot of NetBeans with my latest changes to the Tab Component:

..and here’s how it looks without:

Here’s another screenshot with a different Substance theme and tablayout policy set to scroll:

Hello again Substance Look & Feel!