Branding Support for TopComponents
- by Geertjan
In yesterday's blog entry, you saw how a menu item can be created, in this case with the label "Brand", especially for Java classes that extend TopComponent:
And, as you can see here, it's not about the name of the class, i.e., not because the class above is named "BlaTopComponent" because below the "Brand" men item is also available for the class named "Bla":
Both the files BlaTopComponent.java and Bla.java have the "Brand" menu item available, because both extend the "org.openide.windows.TopComponent" class, as shown yesterday.
Now we continue by creating a new JPanel, with checkboxes for each part of a TopComponent that we consider to be brandable. In my case, this is the end result, at deployment, when the Brand menu item is clicked for the Bla class:
When the user (who, in this case, is a developer) clicks OK, a constructor is created and the related client properties are added, depending on which of the checkboxes are clicked:
public Bla() {
putClientProperty(TopComponent.PROP_SLIDING_DISABLED, false);
putClientProperty(TopComponent.PROP_UNDOCKING_DISABLED, true);
putClientProperty(TopComponent.PROP_MAXIMIZATION_DISABLED, false);
putClientProperty(TopComponent.PROP_CLOSING_DISABLED, true);
putClientProperty(TopComponent.PROP_DRAGGING_DISABLED, false);
}
At this point, no check is done to see whether a constructor already exists, nor whether the client properties are already available. That's for an upcoming blog entry! Right now, the constructor is always created, regardless of whether it already exists, and the client properties are always added.
The key to all this is the 'actionPeformed' of the TopComponent, which was left empty yesterday. We start by creating a JDialog from the JPanel and we retrieve the selected state of the checkboxes defined in the JPanel:
@Override
public void actionPerformed(ActionEvent ev) {
String msg = dobj.getName() + " Branding";
final BrandTopComponentPanel brandTopComponentPanel = new BrandTopComponentPanel();
dd = new DialogDescriptor(brandTopComponentPanel, msg, true, new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
Object result = dd.getValue();
if (DialogDescriptor.OK_OPTION == result) {
isClosing = brandTopComponentPanel.getClosingCheckBox().isSelected();
isDragging = brandTopComponentPanel.getDraggingCheckBox().isSelected();
isMaximization = brandTopComponentPanel.getMaximizationCheckBox().isSelected();
isSliding = brandTopComponentPanel.getSlidingCheckBox().isSelected();
isUndocking = brandTopComponentPanel.getUndockingCheckBox().isSelected();
JavaSource javaSource = JavaSource.forFileObject(dobj.getPrimaryFile());
try {
javaSource.runUserActionTask(new ScanTask(javaSource), true);
} catch (IOException ex) {
Exceptions.printStackTrace(ex);
}
}
}
});
DialogDisplayer.getDefault().createDialog(dd).setVisible(true);
}
Then we start a scan process, which introduces the branding. We're already doing a scan process for identifying whether a class is a TopComponent. So, let's combine those two scans, branching out based on which one we're doing:
private class ScanTask implements Task<CompilationController> {
private BrandTopComponentAction action = null;
private JavaSource js = null;
private ScanTask(JavaSource js) {
this.js = js;
}
private ScanTask(BrandTopComponentAction action) {
this.action = action;
}
@Override
public void run(final CompilationController info) throws Exception {
info.toPhase(Phase.ELEMENTS_RESOLVED);
if (action != null) {
new EnableIfTopComponentScanner(info, action).scan(
info.getCompilationUnit(), null);
} else {
introduceBranding();
}
}
private void introduceBranding() throws IOException {
CancellableTask task = new CancellableTask<WorkingCopy>() {
@Override
public void run(WorkingCopy workingCopy) throws IOException {
workingCopy.toPhase(Phase.RESOLVED);
CompilationUnitTree cut = workingCopy.getCompilationUnit();
TreeMaker treeMaker = workingCopy.getTreeMaker();
for (Tree typeDecl : cut.getTypeDecls()) {
if (Tree.Kind.CLASS == typeDecl.getKind()) {
ClassTree clazz = (ClassTree) typeDecl;
ModifiersTree methodModifiers = treeMaker.Modifiers(Collections.<Modifier>singleton(Modifier.PUBLIC));
MethodTree newMethod =
treeMaker.Method(methodModifiers,
"<init>",
treeMaker.PrimitiveType(TypeKind.VOID),
Collections.<TypeParameterTree>emptyList(),
Collections.EMPTY_LIST,
Collections.<ExpressionTree>emptyList(),
"{ putClientProperty(TopComponent.PROP_SLIDING_DISABLED, " + isSliding + ");\n"+
" putClientProperty(TopComponent.PROP_UNDOCKING_DISABLED, " + isUndocking + ");\n"+
" putClientProperty(TopComponent.PROP_MAXIMIZATION_DISABLED, " + isMaximization + ");\n"+
" putClientProperty(TopComponent.PROP_CLOSING_DISABLED, " + isClosing + ");\n"+
" putClientProperty(TopComponent.PROP_DRAGGING_DISABLED, " + isDragging + "); }\n",
null);
ClassTree modifiedClazz = treeMaker.addClassMember(clazz, newMethod);
workingCopy.rewrite(clazz, modifiedClazz);
}
}
}
@Override
public void cancel() {
}
};
ModificationResult result = js.runModificationTask(task);
result.commit();
}
}
private static class EnableIfTopComponentScanner extends TreePathScanner<Void, Void> {
private CompilationInfo info;
private final AbstractAction action;
public EnableIfTopComponentScanner(CompilationInfo info, AbstractAction action) {
this.info = info;
this.action = action;
}
@Override
public Void visitClass(ClassTree t, Void v) {
Element el = info.getTrees().getElement(getCurrentPath());
if (el != null) {
TypeElement te = (TypeElement) el;
if (te.getSuperclass().toString().equals("org.openide.windows.TopComponent")) {
action.setEnabled(true);
} else {
action.setEnabled(false);
}
}
return null;
}
}