Hi Stackoverflow people !
I'm implementing a Java RCP plugin for a project management tool, using SWT and close to a MVC logic.
I need to do a kind of chronologic timeline in which milestones (some kind of deadline event in project management) can be drag and dropped.
I realized it using several SWT labels (basically, an array of 36 labels in a horizontal line, 3 per month of the year).
Here is a screenshot of the actual GUI:
The user will have to drag a milestone from the drag label (the (X) on the left), and drop it on the desired label/part of the month. Then, for now the only effect it has is to set the text field of the drop target label to (X).
I created 2 "Model" classes : a class Milestone (that will later be improved with others elements, such as informations about what has to be done at this date regarding of the project for example) and a class Square (square has to be linked with the drop labels). The Square class features :
The Milestone class features :
What I want is when a user drags and drops a milestone on a label, it updates the Milestone and the Square class, at least with the position information. I have difficulties to get the information On which label did the user drop the milestone ?.
The 36 labels are created in a for(;;)
loop, with some drag and drop listeners inside this loop.
EDIT #2 : here is my actual code :
// Drag source label in order to create milestones by a Drag'n'Drop
// operation
final Label milestone_drag_label = new Label(compA, SWT.NONE);
milestone_drag_label.setText("(X)");
milestone_drag_label.setData("milestone", milestone);
// Setting up the Drag Listener for the milestone label
DragSource ds_ms_label = new DragSource(milestone_drag_label,
DND.DROP_MOVE);
ds_ms_label.setTransfer(new Transfer[] { TextTransfer.getInstance() });
ds_ms_label.addDragListener(new DragSourceAdapter() {
public void dragSetData(DragSourceEvent event) {
// event.data = milestone_drag_label.getText();
event.data = (Milestone) event.widget.getData("milestone");
from_ms_label = true;
}
});
// Display configuration for the labels (3 labels per months, so they're
// called
// third, as third of a month)
GridData gd_third = new GridData();
gd_third.grabExcessHorizontalSpace = true;
gd_third.widthHint = 15;
for (w = 0; w < 36; w++) {
final Label third = new Label(compA, SWT.BORDER);
third.setLayoutData(gd_third);
board[w] = third;
square[w] = new Square();
square[w].setId(w);
third.setData("square", square);
DropTarget dt = new DropTarget(board[w], DND.DROP_MOVE);
// DropTarget dt = new DropTarget(third, DND.DROP_MOVE);
dt.setTransfer(new Transfer[] { TextTransfer.getInstance() });
dt.addDropListener(new DropTargetAdapter() {
public void drop(DropTargetEvent event) {
if (event.data == null) {
event.detail = DND.DROP_NONE;
} else {
// third.setText((String) event.data);
Milestone milestone = (Milestone) event.data;
Square square = event.widget.getData("square");
milestone.setPositionId(square.getId());
printMilestone(milestone);
}
}
});
The printMilestone()
method in the end just writes the attributes year and positionId in a .txt file.
I got an error at the line : Square square = event.widget.getData("square");
so I added a cast : Square square = (Square) event.widget.getData("square");
But when I run my RichClient app, I got this error :
org.eclipse.swt.SWTException: Data does not have correct format for type
From what I understand: the user should be able to drag a milestone label that serves as a drag source and drop it over one of the 36 non-milestone labels which serve soley as drop targets. As a result a model element that is associated to the drop-target label should be updated.
All of the milestone labels should have a drag-listener, no need for a drop-listener as they never serve as drop-targets. All of the 36 labels should have assigned a drop-listener, they don't serve as a drag source, hence there is no need for a drag-listener. To see how the basic drag-and-drop functionality works, I think this snippet will help.
To associate the labels with their respective model elements, you can either use a Map
or use setData()
The code to create the milestone label would look like this:
Labe label = new Label( ... );
label.setData( "milesone", milestone );
label.addDragListener ( new DragSourceListener () {
@Override
public void dragSetData( DragSourceEvent event ) {
event.data = ( Milestone )event.widget.getData( "milestone" );
}
@Override
public void dragFinished( DragSourceEvent event ) {
// mark the milestone label as 'assigned' if desired
}
} );
The code for each square label would look like this:
Label label = new Label( ... );
label.setData( "square", square );
label.addDropListener ( new DropTargetAdapter() {
@Override
public void drop( DropTargetEvent event ) {
if( event.data == null ) {
event.detail = DND.DROP_NONE;
} else {
Milestone milestone = ( Milestone )event.data;
Square square = event.widget.getData( "square" );
milestone.setPositionId( square.getId() ); // or whatever it takes to update the model
// update label text if necessary
}
}
The above snippets use setData()
to associate model elements and widgets, the same would also work with Map
s.
There are also more DnD snippets an article and an excellent DnD Example that lets you follow the DnD mechanics for all widgets in great detail and experiment with different settings.