Sunday, September 28, 2008

Template Tools Eclipse plug-in

UPDATE:
Template Tools Eclipse plug-in enhances the functionality of code templates:
  • ${clipboard} template variable - replaced by the contents of the clipboard.
  • ${prompt(input|file|directory|enumeration, value [, value]*|color|font)}
    • input - the user is prompted to enter a value
    • file - the user is prompted to select a file
    • directory - the user is prompted to select a directory
    • enumeration - the user is prompted to select a value from the list of values
    • color - the user is prompted to select a color using a color dialog. The color value can be formatted using java.util.Formatter strings. The parameters passed to the format are red, green or blue color component values. If no format is specified the value is formatted as the org.eclipse.swt.graphics.RGB.toString() value of the color.
    • font - the user is prompted to select a font using a FontDialog. The returned value is the org.eclipse.swt.graphics.FontData.toString() value returned by the font dialog.

This plug-in also supports extension-point promptProviders. Here is an example:

  <extension
point="TemplateTools.promptProviders">
<promptProvider
class="templatetools.InputPrompt"
type="input">
</promptProvider>
:
:
</extension>

Here is the implementation:


package templatetools;

import java.util.Formatter;
import java.util.List;

import org.eclipse.swt.graphics.RGB;
import org.eclipse.swt.widgets.ColorDialog;
import org.eclipse.ui.PlatformUI;

public class ColorPrompt implements IPrompt {

@SuppressWarnings("unchecked")
public String getValue(String name, List params) {
ColorDialog colorDilaog = new ColorDialog(PlatformUI.getWorkbench()
.getActiveWorkbenchWindow().getShell());
RGB color = colorDilaog.open();
if (color != null) {
if (params.size() > 0) {
Object format = params.get(0);
if (format instanceof String) {
StringBuffer formatBuffer = new StringBuffer();
new Formatter(formatBuffer).format((String) format,
color.red, color.green, color.blue);
return formatBuffer.toString();
}
} else {
return color.toString();
}
}
return null;
}

}

You can download the plug-in here.

DISCLAIMER: This plug-in is experimental. So no guarantees. Use the plug-in at your own risk.


Monday, September 15, 2008

Reorder Eclipse Plug-in : How does it work?

In the last entry I published the Reorder Eclipse plug-in. In this entry I will talk about how it works.

Basically, the Reorder plug-in uses the AST APIs to get parsed structure of the Java source code surrounding the caret in the Java editor.
  • It gets the ASTNode at caret position using NodeFinder API and starts traversing the parent ASTNodes until it find a Class Instance Creation, Method Invocation, Method Declaration or an Array initializer node.
  • Once found it gets the ASTNode's list of arguments, parameters or array initialization elements and stores the text of each node in a ordered list of items. While doing so, it also records the intervening white spaces as items.
  • It also records which item's extent surrounds the caret position. It records it as a current item.
  • Then, it swaps the current item with the following or preceding non-whitespace item in the list based on the action that was invoked - forward or backward swap.
  • Lastly it builds the string from the list of items and replaces the original text with the new string.
That's it.

Sunday, September 14, 2008

Reorder Eclipse Plug-in for Java Editor

The asymmetrical way in which the , (comma) is used to separate the items in parameter and arguments lists always causes problem when one wants to reorder that list in Java editor. Is that why Java allows trailing commas in array initializer? ;) may be. The Reorder plug-in supports single click swapping of a parameter, argument or array initializer expressions with previous or next item in the list. Each item of the sequence can be a simple literal, an identifier or a more complex function call expression. The comma delimiter is correctly handled.

This plug-in adds two toolbar buttons to the Java editor:

  • Swap backward
  • Swap forward


Usage

With the caret at | in:

void method(int iii, String |sss, boolean bbb){}
pressing the Swap forward button yields:
void method(int iii, boolean bbb, String |sss){}
or pressing Swap backward button with the original source yields:
void method(String |sss, int iii, boolean bbb){}


You can download the plug-in from here.


TODO

  • Key bindings for the actions
  • Generalization of the concept to other language editors

DISCLAIMER: This plug-in is experimental. So no guarantees. Use the plug-in at your own risk.

Saturday, September 13, 2008

Add pulldown actions to Eclipse View's Toolbars

The actions added to the org.eclipse.ui.actionSets extension point (which allows contribution to the Wokbench Window's toolbars) allow the pulldown style. However the actions added to the org.eclipse.ui.viewActions extension point do not allow the pulldown style. IMHO this is a annoying limitation. It may be that the original reason for this limitation was to avoid confusion with the drop down menu in the View toolbars. However the visual of a pulldown action down action is different enough that it is not confusing. Besides the pull down actions are used in View toolbars of many Eclipse provided Views e.g. Open Console action on the as Console view's toolbar. So how are such actions created? Well such actions are programatically created during the View's createPartControl() invocation. However that is not always the case. You may want to contribute a pulldown action to a View created elsewhere. In this entry I show you how to do exactly that programatically.

The basic idea behind this is to watch for opening of the view that you are interested in and programatically add the pulldown style action when the view is opened. Just put the following code in your plug-in's Activator. One thing to keep in mind is that your plug-in may get activated some time after the View that you are contributing a pull down action to - was already created. To handle that we process all the exiting windows that are already present when our code activates. We monitor the opening of the views in the Workbench windows by adding the IPartListener to the Window's PartService. What happens if the user creates more Workbench windows and then openes the View of interest in that Workbench window. Well, we monitor creation of new Workbench windows by adding the IWindowListener to the Workbench. We keep track of the fact that the action was added to the View's toolbar in a WeakHashMap (keyed by the IViewPart) so that we add the action only once. This is because the addActions() is called from multiple places in the following code. One more thing...the action you add must use SWT.AS_DROP_DOWN_MENU style and also implement the IMenuCreator intreface. That's it. Here is the code:
private static WeakHashMap actionsAddedToView = new WeakHashMap();
private static void addActions(IViewPart viewPart)
{
Boolean added = actionsAddedToView.get(viewPart);
if (added == null) {
// The actions were not added yet
final IToolBarManager toolBarManager = viewPart.getViewSite().getActionBars().getToolBarManager();

// SomePulldownAction extends Action implements IMenuCreator
// public SomePulldownAction() {
// super("Some Action...", IAction.AS_DROP_DOWN_MENU);
// }
// Also implement the getMenu() action.
// public Menu getMenu(Control parent) {
// create menu
// add menu items
// return the menu
// }
SomePulldownAction someDownloadAction = new SomePulldownAction();
someDownloadAction.init(viewPart);
toolBarManager.insertAfter("id-of-an-exiting-action-on-views-toolbar", someDownloadAction); //$NON-NLS-1$

// Add more actions
// ...
// ...
// ...

toolBarManager.update(true);
actionsAddedToView.put(viewPart, Boolean.TRUE);
}
}

// Listner for monitoring the view opening
private static final IPartListener partListener = new PartListenerAdapter()
{
@Override
public void partOpened(IWorkbenchPart part)
{
if (part instanceof IViewPart)
{
IViewPart viewPart = (IViewPart) part;
if ("-id-of-the-view-to-which-you-want-to-add-the-pulldown-action".equals(viewPart.getSite().getId()))
{
addActions(viewPart);
}
}
}

@Override
public void partClosed(IWorkbenchPart part)
{
if (part instanceof IViewPart)
{
IViewPart viewPart = (IViewPart) part;
if ("-id-of-the-view-to-which-you-want-to-add-the-pulldown-action".equals(viewPart.getSite().getId()))
{
// forget the view's actions added state
actionsAddedToView.remove(viewPart);
}
}
}
};

private static void processWindow(IWorkbenchWindow window)
{
IWorkbenchPage[] pages = window.getPages();
for (IWorkbenchPage workbenchPage : pages)
{
IViewPart viewPart = workbenchPage.findView("-id-of-the-view-to-which-you-want-to-add-the-pulldown-action");
if viewPart != null && viewPart.getSite() != null)
{
addActions(viewPart);
break;
}
}

// Monitor furture view creation in case the view
// is closed and reopened
window.getPartService().addPartListener(partListener);
}

static {
// Add pulldown action to the view that may be already present in
// exiting Workbench windows.
IWorkbenchWindow[] workbenchWindows = PlatformUI.getWorkbench().getWorkbenchWindows();
for (IWorkbenchWindow workbenchWindow : workbenchWindows)
{
processWindow(workbenchWindow);
}

// Monitor future Workbench windows
PlatformUI.getWorkbench().addWindowListener(new IWindowListener()
{
public void windowActivated(IWorkbenchWindow window) {}
public void windowClosed(IWorkbenchWindow window) {}
public void windowDeactivated(IWorkbenchWindow window) {}
public void windowOpened(IWorkbenchWindow window)
{
processWindow(window);
}
});
}

Tuesday, September 09, 2008

An enhanced ListEditor implementation

As discussed in this article on Eclipse.org, the Eclipse FieldEditors make it easy to implement the preferences pages. The ListEditor is one of the many imlementations provided by Eclipse which allows editing of list like values. However it has a serious limitation. It only allows addition, deletion and reordering of the values. It does not allow editing of the values already in the list. The following subclass of ListEditor overcomes that limitation by providing the functionality of editing any one value in the list of values. To use it you have to subclass and implement the following additional abstract method:

/**
* The subclasses must override this to return the modified entry.
*
* @param original the new entry
* @return the modified entry. Return null to prevent modification.
*/
protected abstract String getModifiedEntry(String original);

One possible implementation could look something like this:

@Override
protected String getModifiedEntry(String original) {
InputDialog entryDialog = new InputDialog(
PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell(),
"Edit entry", "Edit entry:", original, null);
if (entryDialog.open() == InputDialog.OK) {
return entryDialog.getValue();
}
return null;
}

In fact, I make use of this in my Path Tools Eclipse Plug-in to edit the custom commands for folders and files. Here is the screenshot:


Here is the full code:

package somepackage;

import org.eclipse.jface.dialogs.IDialogConstants;
import org.eclipse.jface.preference.ListEditor;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.DisposeEvent;
import org.eclipse.swt.events.DisposeListener;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.List;

/**
* This class extends {@link ListEditor} to enable editing of entries.
*
* @author Sandip V. Chitale
*
*/
public abstract class EntryModifiableListEditor extends ListEditor {

public EntryModifiableListEditor(String name, String labelText, Composite parent) {
super(name, labelText, parent);
}

/**
* The subclasses must override this to return the modified entry.
*
* @param original the new entry
* @return the modified entry. Return null to prevent modification.
*/
protected abstract String getModifiedEntry(String original);

private Button editButton;
private List commandListControl;

@Override
public Composite getButtonBoxControl(Composite parent) {
Composite buttonBoxControl = super.getButtonBoxControl(parent);
if (editButton == null) {
editButton = createPushButton(buttonBoxControl, "Edit..."); // TODO I18N
editButton.setEnabled(false);
editButton.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
if (commandListControl.getSelectionCount() == 1) {
String modified = getModifiedEntry(commandListControl.getSelection()[0]);
if (modified != null) {
int selectedIndex = commandListControl.getSelectionIndex();
commandListControl.remove(selectedIndex);
commandListControl.add(modified, selectedIndex);
}
}
}
});
buttonBoxControl.addDisposeListener(new DisposeListener() {
public void widgetDisposed(DisposeEvent event) {
editButton = null;
}
});
}
return buttonBoxControl;
}

/**
* Helper method to create a push button.
*
* @param parent the parent control
* @param key the resource name used to supply the button's label text
* @return Button
*/
private Button createPushButton(Composite parent, String key) {
Button button = new Button(parent, SWT.PUSH);
button.setText(key);
button.setFont(parent.getFont());
GridData data = new GridData(GridData.FILL_HORIZONTAL);
int widthHint = convertHorizontalDLUsToPixels(button,
IDialogConstants.BUTTON_WIDTH);
data.widthHint = Math.max(widthHint, button.computeSize(SWT.DEFAULT,
SWT.DEFAULT, true).x);
button.setLayoutData(data);
return button;
}

@Override
public List getListControl(Composite parent) {
List listControl = super.getListControl(parent);
if (commandListControl == null) {
commandListControl = listControl;
commandListControl.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
editButton.setEnabled(commandListControl.getSelectionCount() == 1);
}
});
}
return listControl;
}

}

Sunday, September 07, 2008

Perspectives and Views Toolbar Plugin for Eclipse

In Eclipse IDE, I find it very inconvenient to go to the the Window:Open Perspective or Window:Show View submenu, and then select the one I want in dialog that is shown. I was inconvenienced enough that I wrote a simple Eclipse Plug-in called - Perspectives and Views Toolbar. The basic idea is that it adds two drop down menus to the toolbar. Using the drop down you can directly select the Perspective or the View that you want to show.












Get the Eclipse 3.4 compatible Perspectives and Views Toolbar Plug-in here.

DISCLAIMER: This plug-in is experimental. So no guarantees. Use the plug-in at your own risk.