Monday, December 17, 2007

Making Eclipse Newsgroups More Effective

As long as I've used Eclipse, I've found the newsgroups to be a great source of information and probably the best source of documentation outside the included help docs. However, I've always found it hard to find information in the platform newsgroup. The problem is that it has turned into a kind of catch-all for Eclipse users, plugin developers, jface developers, etc. This makes it really hard when you are searching for something on a topic from a particular point of view. A few months ago, I filed this bug to request a change. My proposal is to try to organize the information as follows:

platform.users
platform.pluginDev
platform.jface
...
In order to get some support for this change, I meant to post back then (really, I did). It was brought back to my attention by a comment on the bug, so I thought this would be a good time to see if any other in the community have the same feelings. If not, I'll just have to get better at searching;)

Read More...

Monday, November 26, 2007

When Display Decides to Wait...

Since its been a while since I've posted anything, I thought I would write about a weird problem I was having with one of my plugins. It initially presented as a failure to load certain files in my editor. The app simply seemed to hang forever. It only showed up when running a customer build, so I first needed to connect remotely. I quickly found out that it was a problem in my logger. Since there are many complicated plugins involved with the app, I was a little relieved to find this out since it is the smallest and easiest to follow plugin. My logger is setup to use log4j to direct log messages to the Eclipse console. The files that weren't loading were logging an enormous amount of data at load time. I was using MessageConsoleStream.println to log messages to the console. This filled a buffer in IOConsolePartitioner, which eventually called ArrayList.wait(). Since this was all happening in the display thread, Eclipse never finished loading.
The fix was pretty simple and only involved a few lines of code. I realized that I would need a reusable set of threads to do the work, so I first created a thread pool using the cool new Executors class introduced in Java 1.5 (it was really nice to have this handy and not have to implement it, again). I chose to create a single thread executor so my messages would be guaranteed in order (see Executors.newSingleThreadExecutor). Then I only needed to schedule the log messages by putting them in a runnable and passing it to the executor using the execute method. Besides fixing the hang problem, the performance of my app was increased since much of the logging work didn't need to be done in Display up until the string was shown on the console.

Read More...

Tuesday, July 24, 2007

JFace TableViewer Sorting using LabelProvider

While designing some code for an Eclipse plugin, I decided that I wanted to be able to provide the normal sort mechanism for the tables that is present in so many other applications. I was actually surprised to find that there wasn't a simple API to just turn this on. I sure there are good reasons for this and there are many bug reports in this area. It even looks like there are some fixes in progress.
Anyway, while looking at some examples, I came across this snippet by Tom Schindl. It was really close to what I was looking for, but didn't quite work because it assumed a simple model and didn't exercise the LabelProvider to get the text to sort.

Since I usually create the columns in a loop, it was easy to modify the compare method to look up the index of the column using a label provider.

int i = 0;
for (String name : colNames) {
final int colIdx = i;
TableColumn column = new TableColumn(viewer.getTable(), SWT.NONE);
column.setWidth(200);
column.setText(name);
column.setMoveable(true);

TableColumnSorter cSorter = new TableColumnSorter(viewer, column) {
protected int doCompare(Viewer v, Object e1, Object e2) {
ITableLabelProvider lp = ((ITableLabelProvider) viewer
.getLabelProvider());
String t1 = lp.getColumnText(e1, colIdx);
String t2 = lp.getColumnText(e2, colIdx);
return t1.compareTo(t2);
}
};

cSorter.setSorter(cSorter, TableColumnSorter.ASC);
i++;
}




The full text of the example with my changes are below the split.

package snippet;

/*******************************************************************************
* TableViewerSorting Example
*
* Adam Cabler
*
* revised example by Tom Schindl
* http://dev.eclipse.org/viewcvs/index.cgi/org.eclipse.jface.snippets/Eclipse%20JFace%20Snippets/org/eclipse/jface/snippets/viewers/Snippet040TableViewerSorting.java?view=markup
*
* removed ColumnViewer references
* added ITableLabelProvider
* used label provider for text compare
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
*
*******************************************************************************/


import org.eclipse.jface.viewers.ILabelProviderListener;
import org.eclipse.jface.viewers.IStructuredContentProvider;
import org.eclipse.jface.viewers.ITableLabelProvider;
import org.eclipse.jface.viewers.TableViewer;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.jface.viewers.ViewerComparator;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.layout.FillLayout;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.TableColumn;

/**
* Table sorting example using label provider
*
* @author cabler
*
*/

public class TableViewerSortingExample {

private class MyContentProvider implements IStructuredContentProvider {

public Object[] getElements(Object inputElement) {
return (Person[]) inputElement;
}

public void dispose() {
}

public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
}
}

private class MyLabelProvider implements ITableLabelProvider {

@Override
public Image getColumnImage(Object element, int columnIndex) {
// TODO Auto-generated method stub
return null;
}

@Override
public String getColumnText(Object element, int columnIndex) {
if (!(element instanceof Person)) {
return null;
}
Person p = (Person) element;
switch (columnIndex) {
case 0:
return p.givenname;
case 1:
return p.surname;
case 2:
return p.email;
}
return "Error";
}

@Override
public void addListener(ILabelProviderListener listener) {
// TODO Auto-generated method stub

}

@Override
public void dispose() {
// TODO Auto-generated method stub

}

@Override
public boolean isLabelProperty(Object element, String property) {
// TODO Auto-generated method stub
return false;
}

@Override
public void removeListener(ILabelProviderListener listener) {
// TODO Auto-generated method stub

}

}

public class Person {
public String givenname;
public String surname;
public String email;

public Person(String givenname, String surname, String email) {
this.givenname = givenname;
this.surname = surname;
this.email = email;
}

}

public TableViewerSortingExample(Shell shell) {
final TableViewer viewer = new TableViewer(shell, SWT.BORDER | SWT.FULL_SELECTION);
viewer.setContentProvider(new MyContentProvider());
viewer.setLabelProvider(new MyLabelProvider());
String[] colNames = new String[] { "Givenname", "Surname", "Email" };

int i = 0;
for (String name : colNames) {
final int colIdx = i;
TableColumn column = new TableColumn(viewer.getTable(), SWT.NONE);
column.setWidth(200);
column.setText(name);
column.setMoveable(true);

TableColumnSorter cSorter = new TableColumnSorter(viewer, column) {
protected int doCompare(Viewer v, Object e1, Object e2) {
ITableLabelProvider lp = ((ITableLabelProvider) viewer
.getLabelProvider());
String t1 = lp.getColumnText(e1, colIdx);
String t2 = lp.getColumnText(e2, colIdx);
return t1.compareTo(t2);
}
};

cSorter.setSorter(cSorter, TableColumnSorter.ASC);
i++;
}

Person[] model = createModel();
viewer.setInput(model);
viewer.getTable().setLinesVisible(true);
viewer.getTable().setHeaderVisible(true);

}

private Person[] createModel() {
Person[] elements = new Person[4];
elements[0] = new Person("Tom", "Schindl",
"tom.schindl@bestsolution.at");
elements[1] = new Person("Boris", "Bokowski",
"Boris_Bokowski@ca.ibm.com");
elements[2] = new Person("Tod", "Creasey", "Tod_Creasey@ca.ibm.com");
elements[3] = new Person("Wayne", "Beaton", "wayne@eclipse.org");

return elements;
}

private static abstract class TableColumnSorter extends ViewerComparator {
public static final int ASC = 1;

public static final int NONE = 0;

public static final int DESC = -1;

private int direction = 0;

private TableColumn column;

private TableViewer viewer;

public TableColumnSorter(TableViewer viewer, TableColumn column) {
this.column = column;
this.viewer = viewer;
this.column.addSelectionListener(new SelectionAdapter() {

public void widgetSelected(SelectionEvent e) {
if (TableColumnSorter.this.viewer.getComparator() != null) {
if (TableColumnSorter.this.viewer.getComparator() == TableColumnSorter.this) {
int tdirection = TableColumnSorter.this.direction;

if (tdirection == ASC) {
setSorter(TableColumnSorter.this, DESC);
} else if (tdirection == DESC) {
setSorter(TableColumnSorter.this, NONE);
}
} else {
setSorter(TableColumnSorter.this, ASC);
}
} else {
setSorter(TableColumnSorter.this, ASC);
}
}
});
}

public void setSorter(TableColumnSorter sorter, int direction) {
if (direction == NONE) {
column.getParent().setSortColumn(null);
column.getParent().setSortDirection(SWT.NONE);
viewer.setComparator(null);
} else {
column.getParent().setSortColumn(column);
sorter.direction = direction;

if (direction == ASC) {
column.getParent().setSortDirection(SWT.DOWN);
} else {
column.getParent().setSortDirection(SWT.UP);
}

if (viewer.getComparator() == sorter) {
viewer.refresh();
} else {
viewer.setComparator(sorter);
}

}
}

public int compare(Viewer viewer, Object e1, Object e2) {
return direction * doCompare(viewer, e1, e2);
}

protected abstract int doCompare(Viewer TableViewer, Object e1, Object e2);
}

/**
* @param args
*/

public static void main(String[] args) {
Display display = new Display();

Shell shell = new Shell(display);
shell.setLayout(new FillLayout());
new TableViewerSortingExample(shell);
shell.open();

while (!shell.isDisposed()) {
if (!display.readAndDispatch())
display.sleep();
}

display.dispose();

}

}

Read More...

Thursday, June 28, 2007

Cross Project Contributions

In my work with GEF I usually check out the newsgroups when looking for a solution to my current problem. I have noticed more than once that the solution exists in GMF, but wasn't contributed back to GEF and instead exists only for GMF users. I am really curious as to how this happens - especially when one of the stated goals of GMF is to contribute to GEF where possible. It seems that contributing fixes back to the source plugin would have several obvious benefits. Besides making these things available to a larger audience, this would make for more modular source trees and easier code maintenance.
I'm wondering if this exists in other projects as well and what can be done to help this problem (or if this isn't really a problem). I'm sure part of the issue has to do with the usual, time constraints and lack of resources, but here it would seem that it could actually be more efficient to back-contribute rather than trying to copy a chunk of code across projects and modify it to fix a bug or add a feature.
It is really not my intent to pick on specific projects - I only use GEF/GMF as an example because its one with which I'm familiar. And I really appreciate all the work that's been done there to date.

Read More...

Monday, June 4, 2007

Anti-Alias bug in 3.3 with GEF

I've been fighting a particularly nasty bug in Eclipse 3.3 lately and since I finally found the culprit, I wanted to post it in case other GEF developers encounter it before it's fixed.

Symptoms:

  1. Thumbnail in outline view won't show some edit parts
  2. Connection parts don't show until after editor is reopened in same session
I started having these problems in M7. Since GEF hasn't changed much, I thought the problem had to be somewhere in the Platform. However, this only seemed to affect GEF editors - and I couldn't even find anyone else with the same problem. After analyzing the sample GEF apps (which don't have this problem) and comparing them to mine, I found the problem:

graphics.setAntialias(SWT.ON)

Commenting out this call fixed all the problems I was having. Now my edit parts don't look quite as good, but since I'm counting on being able to use 3.3, I'm just happy there is a workaround. I'm hopeful that this can be fixed by the final Europa release.

Read More...

Thursday, April 26, 2007

Synchronizing Views with a GEF Editor

View synchronizing is one of the fundamental features for most Eclipse workbench apps. When using a GEF editor, you can use additional views to provide more targeted information about the selection, or even allow editing. There is a lot of good information about this topic - both in the eclipse docs and in the newsgroups. However, I think I can augment that with a concise set of steps for getting this task done quickly.

In this post, I'll talk about how to easily allow views to respond to selection events in an editor.

  1. First, you will need to make sure events are being properly fired from your editor. Since a default selection provider is provided by the GEF Graphical Editor, this is handled for you if you are extending it.

  2. Next, you need to watch for these selection events in your views. This is done by simply adding a postSelectionListener to the page during createPartControl in the view:

  3. getSite().getPage().addPostSelectionListener(yourPageSelectionListener);

    It is important to note here that you can't use the other add method that allows filtering by workbench part. This does not work for editors. I have filed an enhancement request to get this added, but it wouldn't be until 3.4 at the earliest since 3.3 is at API freeze.

  4. If all you wanted was to get selection events of your edit parts, you're done. However, in my designs, I usually like to hide the edit parts from view outside the editor. For this, you will need to use the Eclipse adapter mechanism. In your edit part getAdapter method, you have the opportunity to do any pre-processing.

    Lets say that your edit parts are shapes and you want to find out the color, and size of them without exposing the edit parts to the view:
    1. First, put an interface in a shared plugin called: IShapeInfo with getters for color and size
    2. In Shape.getAdapter, when IShapeInfo is passed in, return an implementation that provides size and color. (You can implement IShapeInfo in Shape, create an anonymous inner class, etc.)
    3. In your view's pageSelectionListener:


//Get the selection as a list
StructuredSelection ss = (StructuredSelection) selection;
List sList = ss.toList();
/**
* Create a list of objects to show
*/
for (Iterator iterator = sList.iterator(); iterator.hasNext();) {
Object name = iterator.next();
//See if it implements IAdaptable
if (name instanceof IAdaptable) {
//See if the IShapeInfo is available as an adapter
Object o = ((IAdaptable)name).getAdapter(IShapeInfo.class);
if(o != null) {
IShapeInfo info = (IShapeInfo)o;
/** Now you can get size and color from the selected Object
}
}
}

Thats it. All you have to do is customize the interface and you can get custom information back from your edit parts - or any other selection - without exposing class details.

Read More...

Thursday, April 12, 2007

Current GEF Activity

Since I have been watching GEF progress closely, I got a nice surprise lately with a lot of Bugzilla activity. Even though the project plan still doesn't really contain any real detail, its nice to see bugs targeted at 3.3 milestone builds, as well as the final release. While I realize that this doesn't mean they will get fixed, its definitely a start. Thanks to the GEF Lead for taking the time to put this in!

For some reason, the GEF project seems to have really dropped in popularity over the last year - at least judging by the newsgroup activity and lack of project activity. As part of the community, I can take some blame for this since I haven't submitted any fixes. My 3.3 resolution is to find at least 1 bug for which I can provide a patch. I strongly encourage anyone interested in GEF to do the same!

Here is the list of bugs currently targeted at 3.3 if you're interested in helping.

Read More...

Thursday, March 15, 2007

GEF Updates and the Eclipse Project Info Process

Over the last few months, I have been getting increasingly frustrated at the lack of info I get about an Eclipse project that is really important to my work [GEF]. There is nothing in the project plan and nothing much targeted at 3.3. Even most things targeted at 3.2.2 weren't resolved or even updated as of the release. After getting little on the project newsgroup, I decided to post in the foundation newsgroup. I was a little apprehensive because I wasn't completely sure if this was appropriate, but soon after, I got a great response from David Williams:


I'll let GEF folks talk about GEF, but I'll speak to process. First, you did the right thing, just keep asking until you get an answer. First start with newsgroups, then a polite question on dev lists (if not response on newsgroups). And, if no response from dev list, then a not to a PMC member (or, the pmc mailing list) would probably be in order. I believe GEF is in the Tools project. Then, if no resposne from the PMC, the Eclipse foundation (this mailing list) is a good place. If no response there, you can bet the project will be "retired" as an inactive project soon -- which is _NOT_ the case for GEF. I know it is activly maintained, thought not sure they have lots of feature enhancements planned.
This stuff may seem like common sense to long time Eclipse developers, but at least for me, its nice to see it written down so others can follow it.

Read More...

Tuesday, March 13, 2007

Couple of Vista Utils

I found a couple of things that make life with Vista easier. The first is Start++, made by one of the MS devs. It adds some nice shortcut functionality the the start menu. The second is already built-in, but I was waiting on a Powertoy to do it. If you like the "Command Prompt From Here" powertoy, all you have to do to duplicate this functionality in Vista is Shift-Right_Click on a directory and select it from the context menu. Note that this only works in the right pane.

Read More...

Saturday, February 10, 2007

Eclipse 3.3M5 on Vista

So I just finished installing Vista on my work laptop (mostly so I could see the improvements Eclipse made for Vista support) and I have to say I'm really impressed with both. First, there are tons of new things in M5, like the new forms, splash, etc, that are really nice. As for Vista - it installed in about 30 minutes with no problems and all my programs, shortcuts still work. I'm still checking the performance, but so far, I haven't noticed any problems.

Read More...

Thursday, February 8, 2007

Finally - A Great UML Tool

Over the course of my career, I have tried to use UML several times. The one problem I kept having was that the tools did more to get in my way than ease documentation and development. Since starting a new job with a bigger team, I started feeling [again] that UML would really ease the development of the new products.
Since I work for a small startup - I evaluated all of the OSS tools I could find. I ended up with all of the old problems. Then, I was taking a look at the latest edition of my favorite java book and the author mentions this one. I have been trying it on an eval basis and so far, I'm really impressed. Now if I can only get the budget for some licenses...

Read More...

About

Since I've been using Eclipse for a good while now to develop software products, I decided that its time I had some space to keep track of stuff I want to remember. If anyone else reads this and it causes some healthy debate, that will definitely be a great side benefit. If that happens once I get some useful material here, I may ask to be added to the roll over at Planet Eclipse.

Read More...