Swing
ToolbarBuilder – Use the builder pattern to create toolbars
Recently I discovered Jeremys uber-ButtonUI’s. He provides UI classes to mimic Mac OS Button looks (and more) including segmented Buttons. The UI’s are nice, but while playing around with, I thought a builder approach would fit to easily create toolbars of buttons and components. Basically the problem is that segmentation configuration depends on Button order (
), so changing the order of elements within a toolbar now also involves updating the segmentation configuration. Jeremy provides a nice installUI method that helps adding the appropriate button UIs, but I thought this could be improved further and I wanted to play with the builder pattern and find a way to make it extensible without copy/pasting the whole builder.
Appframework and accelerator keys
Today I re-researched on the topic of assigning accelerator keys to actions depending on the operating system using java appframework. The problem I have is simple. On Mac OS X the “meta” key maps to apples command key. On windows it maps to the “Alt” key. And thats is the beginning of all the trouble. Assume you want to define a “save” action. On Mac the shortcut should be “command-S”, on windows it should be “ctrl-S”. In terms of appframework (or java keystrokes) that means you need:
save.Action.accelerator=meta pressed S
if you are on Mac OS X and
save.Action.accelerator=ctrl pressed S
if you are on Windows. The simple way to to this is
save.Action.accelerator=shortcut S
and “shortcut” will map to the meta key on the mac and to the ctrl key on windows. Remember, this is appframework specific and not a real Keystroke definition. I didn’t find the documentation anywhere ( I am sure it somewhere
), but I found that while browsing the source code. It doesn’t solve all the problems related to accelerator/mnemonic/keystrokes on differen paltforms ! But, at least for me, it solved 90% of the problems, because we develope our app in english (so no other locales involved) and “ctrl” is also fine for the linux/unix/solaris platform. I you want to do more or just want to read more about the problem, there is a thread from the appframwork mailing list.
A simple AbstractBean
Just in case, here is a simple example of an AbstractBean class that can be used as a base class for JavaBeans if you want to use the Eclipse bouded setter plugin.
public abstract class AbstractBean {
/**
* Create a new change support
*/
protected PropertyChangeSupport change = new EDTSafePropertyChangeSupport(this);
/**
* @param listener
* @see java.beans.PropertyChangeSupport#addPropertyChangeListener(java.beans.PropertyChangeListener)
*/
public void addPropertyChangeListener(PropertyChangeListener listener) {
change.addPropertyChangeListener(listener);
}
/**
* @param propertyName
* @param listener
* @see java.beans.PropertyChangeSupport#addPropertyChangeListener(java.lang.String, java.beans.PropertyChangeListener)
*/
public void addPropertyChangeListener(String propertyName,
PropertyChangeListener listener) {
change.addPropertyChangeListener(propertyName, listener);
}
/**
* @param propertyName
* @param oldValue
* @param newValue
* @see java.beans.PropertyChangeSupport#firePropertyChange(java.lang.String, java.lang.Object, java.lang.Object)
*/
public void firePropertyChange(String propertyName, Object oldValue,
Object newValue) {
change.firePropertyChange(propertyName, oldValue, newValue);
}
/**
* @return
* @see java.beans.PropertyChangeSupport#getPropertyChangeListeners()
*/
public PropertyChangeListener[] getPropertyChangeListeners() {
return change.getPropertyChangeListeners();
}
/**
* @param propertyName
* @return
* @see java.beans.PropertyChangeSupport#getPropertyChangeListeners(java.lang.String)
*/
public PropertyChangeListener[] getPropertyChangeListeners(
String propertyName) {
return change.getPropertyChangeListeners(propertyName);
}
/**
* @param propertyName
* @return
* @see java.beans.PropertyChangeSupport#hasListeners(java.lang.String)
*/
public boolean hasListeners(String propertyName) {
return change.hasListeners(propertyName);
}
/**
* @param listener
* @see java.beans.PropertyChangeSupport#removePropertyChangeListener(java.beans.PropertyChangeListener)
*/
public void removePropertyChangeListener(PropertyChangeListener listener) {
change.removePropertyChangeListener(listener);
}
/**
* @param propertyName
* @param listener
* @see java.beans.PropertyChangeSupport#removePropertyChangeListener(java.lang.String, java.beans.PropertyChangeListener)
*/
public void removePropertyChangeListener(String propertyName,
PropertyChangeListener listener) {
change.removePropertyChangeListener(propertyName, listener);
}
/**
* Simply ensures that events are delivered through the event dispatching thread
*
*/
class EDTSafePropertyChangeSupport extends PropertyChangeSupport{
public EDTSafePropertyChangeSupport(Object sourceBean) {
super(sourceBean);
}
@Override
public void firePropertyChange(final PropertyChangeEvent evt) {
if(SwingUtilities.isEventDispatchThread()){
super.firePropertyChange(evt);
}else{
SwingUtilities.invokeLater(new Runnable() {
public void run() {
firePropertyChange(evt);
}
});
}
}
}
}
This implementation restricts itself to the firePropertyChange(String,Object,Object) method. It also provide a custom extension of PropertyChangeSupport that ensures that events are fired in the EventDispatchingThread.