Showing posts with label eclipse application. Show all posts
Showing posts with label eclipse application. Show all posts

SWT File Dialog

During the development of an Rich Client Platform (RCP) I needed to perform a file browse, here's how to show a file dialog in SWT:

import org.eclipse.swt.widgets.FileDialog;

FileDialog dialog = new FileDialog(this.getShell(), SWT.NULL);
dialog.setFilterExtensions(new String[] { "*.txt", "*.*" });
dialog.setFilterNames(new String[] { "Text files", "All files" });
String path = dialog.open();
if (path != null) {
File file = new File(path);
if (file.isFile()) {
System.out.println(file.toString());
}
}

The snippet above filters by text files (*.txt) and all files (*.*) only, and it can be easily applied to any button or file menu option click event.

./M6

JFace TreeView with Manual Expand of TreeItems

I have a FilteredTree with check items and I was trying to expand the tree whenever the user checks an item on it. By default, the tree does only expand a TreeItem when the user clicks on the + icon and retracts when the user clicks on the - icon.
Since this tree has check items, I wanted the tree to expand when the user clicks on the check box.

This proved to be a lot harder than I expected, partially because I was using the wrong object, partially because it is lazy, and partially because it was hard to find an answer for what I was doing wrong.
So, I'm posting here how to manually expand a TreeView, which is part of a FilteredTree.

To begin with, the TreeView requires the following classes to be defined:

1. Your tree node that will hold the data of each tree item:
private class Node {

private final Node parent;
private final String word;
private List children = new ArrayList();

Node(Node parent, String word) {
this.parent = parent;
this.word = word;
if(parent != null) {
parent.children.add(this);
}
}

public Node getParent() {
return parent;
}

public Collection getChildren() {
return new ArrayList(children);
}

public boolean hasChildren() {
return children.size() > 0;
}

public void setChildren(Collection newChildren) {
if(newChildren != null) {
children = new ArrayList(newChildren);
}
}

public String getWord() {
return word;
}

}
Please note that this class can be whatever class you require, this is only an example.

2. A tree content provider that will provide the content for the tree:
private class NodeContentProvider implements ITreeContentProvider {

@Override
public Object[] getChildren(Object element) {
return ((Node)element).getChildren().toArray();
}

@Override
public Object getParent(Object element) {
return ((Node)element).getParent();
}

@Override
public boolean hasChildren(Object element) {
return ((Node)element).hasChildren();
}

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

@Override
public void dispose() {
// unused
}

@Override
public void inputChanged(Viewer viewer, Object oldInput,
Object newInput) {
// unused
}

}

3. The label provider that will provide the label for each item in the tree:
private class NodeLabelProvider extends LabelProvider {

private final TreeViewer viewer;

NodeLabelProvider(TreeViewer viewer) {
this.viewer = viewer;
}

@Override
public String getText(Object element) {
Node node = (Node) element;
return node.getWord();
}
}

I'll create a FilteredTree, but since it makes use of TreeView, using only a TreeView is trivial:

PatternFilter patternFilter = new PatternFilter();
FilteredTree filteredTree = new FilteredTree(cTree,
SWT.BORDER | SWT.H_SCROLL | SWT.V_SCROLL | SWT.CHECK,
this.patternFilter);
filteredTree.setInitialText("type your filter text here");
TreeViewer treeViewer = filteredTree.getViewer();

// Assign the label provider
treeViewer.setLabelProvider(new NodeLabelProvider());

// Assign the content provider
treeViewer.setContentProvider(new NodeContentProvider());

// Populate the tree with elements
Node ex1= new Node(null, "Example 1");
new Node(ex1, "Example 1.1");
new Node(ex1, "Example 1.2");
new Node(ex1, "Example 1.3");
Node ex2= new Node(null, "Example 2");
new Node(ex2, "Example 2.1");
Node[] nodes = Node[] {ex1, ex2}
viewer.setInput(nodes);

// Event for item selection
treeViewer.getTree().addListener(SWT.Selection, new Listener() {
public void handleEvent(Event event) {
TreeItem item = (TreeItem) event.item;
Node node = (Node)item.getData();
// Manually Expand
treeViewer.expandToLevel(node, 1);
treeViewer.update(node, null);
}
});

Please note that the tree selection event always expands the selected node, you may wish to retract it or do another operation, like check all its descendants.
This is actually quite simple, but I was calling treeViewer.expandToLevel with the TreeItem instead of using the Node object, and that was the cause of my problems.

If you're looking for a similar example but using custom images, check Eclipse Nuggets.
./M6

RCP save workbench status

In Rich Client Platform (RCP) application, the workbench status can be easily saved and restored by simply including the snippet bellow in the ApplicationWorkbenchAdvisor class, that extends WorkbenchAdvisor.

@Override
public void initialize(IWorkbenchConfigurer configurer) {
super.initialize(configurer);

// tell eclipse to save workbench state when it quits
// (things like window size, view layout, etc.)
configurer.setSaveAndRestore(true);
}

This will automatically save the status of the workbench when the application is closed and will automatically restore that status when the application is executed again. The settings are saved on the [runtime-{your-product}]/.metadata/.plugins/org.eclipse.ui.workbench/workbench.xml file.

./M6

Selecting a value from a SWT CCombo on RCP

It looks like the CCombo custom SWT object lacks some selection functionality. I'm using a CCombo as a table cell editor on my Rich Client Platform (RCP) application and I've found out that it is (almost) impossible to detect the user selection with both the keyboard and the mouse.

The selection listener does not work as expected. Documentation says:
  • the widgetSelected method is triggered whenever a selection occurs in the control, i.e. when the user browses the option list this event is triggered;
  • the widgetDefaultSelected method is triggered when default selection occurs in the control, i.e. when the user selects an option from the list this event is triggered.
One might think, as I thought, that all one has to do is to catch the widgetDefaultSelected event and one would know which option the user has selected from the list.
This is only true when the user performs the selection using the keyboard, i.e. after browsing through the options list the Enter/Return key is pressed.
If the user decides to use the mouse, the widgetDefaultSelected event is not triggered at all, but widgetSelected is.

I though I could detect the user selection with the mouse listener. I was wrong. After trying a couple more possibilities, it turns out there's no (easy) way to detect if the user has performed a selection using the mouse...

So I had to do a workaround for it. Since the widgetSelected is triggered by the mouse clicks, I tried to use that event. Unfortunately the event has no real useful information, like if it was triggered by a right button mouse click. But fortunately, the CCombo object does have a couple of methods that allowed to infer that a selection has been made. In particular, it has a method to check if the options list is visible or not. Since a selection with the mouse makes the options list disappear, I've used it.

Here's the snippet:

// Selections events
combo.addSelectionListener(new SelectionAdapter() {
// Selection changed, check if the options are still visible
public void widgetSelected(SelectionEvent e) {
// If list is not visible assume that the user has
// performed selection with the mouse
if (!combo.getListVisible()) {
setEditionValue(combo);
}
}

// Option selected
public void widgetDefaultSelected(SelectionEvent e) {
// User performed a selection with the keyboard
setEditionValue(combo);
}
});
This is not a perfect solution, it's a workaround, but it's working just fine for me.

./M6

RCP Message Dialog

It looks like every time I need a message dialog in an Rich Client Platform (RCP) application, it takes me too much time finding which JFace dialog class is better for me...

This time I decided to post it here so that the next time I won't waste time.
All the methods needed are statically available in the org.eclipse.jface.dialogs.MessageDialog class:
  • MessageDialog.openConfirm, for a confirmation dialog with an Ok/Cancel button set.
  • MessageDialog.openError, for an error dialog with an Ok button.
  • MessageDialog.openInformation, for an information dialog with an Ok button.
  • MessageDialog.openQuestion, for a question dialog with and Yes/No button set.
  • MessageDialog.openWarning, for warning dialog with an Ok button.
The org.eclipse.jface.dialogs.MessageDialogWithToggle is similar,but allows the user to adjust a toggle setting, like Yes Always/Yes/No or Yes/No/Never.

You can use org.eclipse.jface.dialogs.DialogSettings for a dialog setting, supporting loading and saving of properties in an XML file.

You can use org.eclipse.jface.dialogs.ProgressMonitorDialog to display progress during a long running operation.

And you can design your own dialog windows, just extend the org.eclipse.jface.dialogs.IconAndMessageDialog class.


Note: in RCP, you can get the shell using PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell();.

./M6

RCP Open and Save File Dialogs

I'm currently developing an Eclipse application, and since some of the usual functionalities is the open and save file, or project, I'm posting here a nice code snippet for using open and save dialogs in Rich Client Platform (RCP) applications.

Both the FileOpen and FileSave classes bellow are ready for usage as a command default handler for the extension org.eclipse.ui.commands in the plugin.xml file. Don't forget to create the menu entry to use this command.

Here's the open file snippet (don't forget to update the package)
package your.package.in.here;

import org.eclipse.core.commands.AbstractHandler;
import org.eclipse.core.commands.ExecutionEvent;
import org.eclipse.core.commands.ExecutionException;
import org.eclipse.core.commands.IHandler;
import org.eclipse.swt.SWT;
import org.eclipse.swt.widgets.FileDialog;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.ui.PlatformUI;

/**
* Opens a file
*/
public class FileOpen extends AbstractHandler implements IHandler {

@Override
public Object execute(ExecutionEvent event)
throws ExecutionException {

Shell shell = PlatformUI.getWorkbench().
getActiveWorkbenchWindow().getShell();

FileDialog dialog = new FileDialog(shell, SWT.OPEN);
dialog.setFilterExtensions(new String[] {"*.txt", "*.*"});
dialog.setFilterNames(new String[] {"Text File", "All Files"});
String fileSelected = dialog.open();

if (fileSelected != null) {
// Perform Action, like open the file.
System.out.println("Selected file: " + fileSelected);
}
return null;
}
}


And here's the save file snippet (don't forget to update the package):
package your.package.in.here;

import org.eclipse.core.commands.AbstractHandler;
import org.eclipse.core.commands.ExecutionEvent;
import org.eclipse.core.commands.ExecutionException;
import org.eclipse.core.commands.IHandler;
import org.eclipse.swt.SWT;
import org.eclipse.swt.widgets.FileDialog;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.ui.PlatformUI;

/**
* Save file as...
*/
public class ProjectFileAs extends AbstractHandler implements IHandler {

@Override
public Object execute(ExecutionEvent event)
throws ExecutionException {
Shell shell = PlatformUI.getWorkbench().
getActiveWorkbenchWindow().getShell();

FileDialog dialog = new FileDialog(shell, SWT.SAVE);
dialog.setFilterExtensions(new String[] {"*.txt", "*.*"});
dialog.setFilterNames(new String[] {"Text File", "All Files"});
String fileSelected = dialog.open();

if (fileSelected != null) {
// Perform Action, like save the file.
System.out.println("Selected file: " + fileSelected);
}
return null;
}
}

./M6

Talend Open Studio

Since one of my professional interests is ETL/Data Migration, I'm evaluating Talend Open Studio, version 3.1 RC1, since it's an open source solution.

I've downloaded the product, installed it and when I opened it, I had to read and accept the license, and then I got a dialog box that was asking for a connection and a project. Obviously I had none of those so I tried to create one... That proved to be a not so easy task! I was not understanding what should I do, so I pressed F1 for help and... No luck... I had to figure out what the hell I was supposed to do to be able to create a project. It as not that hard to find it out, but still, the first impression was not a very positive one.
I had to register, or at least so it seemed since I had to insert my email address, and the I was able to import a Java demo project, which I did.
Then, I opened the project and, finally, I've arrived to what I was expecting to be the real first Open Studio window, the Welcome page! Talend is an RCP application, and in RCP applications, the welcome page is the first thing that the user sees, after the traditional splash screen.

Finally, on the welcome page, I got a register pop-up, where I should write my email address and state my location... I really don't get it! If registration is optional, why the hell did I had to write my email address to create a new repository and then a project on that repository?
All Open Studio does with this awkward interface is confusing its users, since it is using hiding, on a very confusing way, the Eclipse workspace and projects.
From the starting page I went to the, previously desired but inaccessible, help page from where I could watch a, also desired, kick start tutorial where the workspace and project creations were visible. Unfortunately, it was totally time dislocated, since it was now totally irrelevant.
I know I'm using a RC, but this kind of issues are not RC bugs, they are design faults!

Since I'm a technical guy, unfortunately I'm used to bad user interfaces, so I focused on the juicy stuff, its features, performance and transformations.

I started to explore the application and I got into one ugly dialog box! I haven't seen a dialog box so ugly for a long time. And it is so big that I almost felt that if I was not using an wide screen (1280x800) I would be unable to see the dialog box. The dialog box rules are also a bit confusing, for instance, I was forced to choose a week day, Monday was my choice, even after I had chosen an month day, day 1 was my choice. I wonder what will happen if the first day of the next month is not a Monday...

Talend Open Studio ugly "Add a task" dialog box.

Definitely, Open Studio interface has a long way to go before becoming really user friendly.

After that shocking moment, I continue to explore the product.

There's a business model area, where it is possible to specify very simple business diagrams. My first impression about this is that I have doubts about the real value and usefulness of this feature. I'll have to explore it more to know if it is really useful or not.
Open Studio has some simple data quality components, including a fuzzy one. Talend already has a data cleaning tool, Talend Data Quality.
It supports a variety of file formats, including Excel, XML and EBCDIC. EBCDIC in particular is extremely useful when it involves files from IBM mainframes.
There's a nice set of connections, including a connection for AS/400 and SAP.
It supports orchestration through a set of iterative and job execution components.
There's a set of SQL templates, some of them are not really that useful. There are templates what just have COMMIT; or DROP TABLE <%= __DATABASE_NAME__ %>.<%=__TABLE_NAME_TARGET__%>;.

Almost all components and processes have history, which is a very nice feature. It looks like that there's no version control implemented, just history, but that is a good first step into a control versioning.
The same applies to documentation, almost all components and processes seem to have documentation associated, this is not just an interesting feature, it's a must have on such a tool.

Since it is possible to document the components, the processes and the business rules through diagrams, I look around for a way to export the project documentation, but I was unable to find such feature.
There's an Documentation area, but it's not what I was expected. It seems to be just a file link interface, where documentation files, like spreadsheets, can be accessed from.
And there's a javadoc export functionality, which also does not do what I expected, apparently it exports Talend components documentation.
There's no really usefulness for documentation when it is not easily accessible. It's like having a jar library all documented but no javadoc to build its documentation, forcing anyone who needs to read the documentation to open the source code and read it from there. It does not make much sense.

Finally, one of the most interesting features is the real time debug. I still haven't got the opportunity to try it out, but for what I could see, that is the ETL developer best friend Open Studio feature.

I've already watched some videos of how easy ETL is with Open Studio, dragging and dropping and graphically connecting the components and all that. In the next days, I'll try it for myself.

./M6