Search code examples
javadrag-and-dropswtrcp

Java/SWT - Want to detect the Drop area in a SWT DnD operation


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:

enter image description here

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 :

  • boolean isEmpty ( check if the label or square already has a milestone on it )
  • int nbMilestone ( number of milestone on a label/square )
  • int id ( the number of the label or square from 0 to 35 )

The Milestone class features :

  • year ( gets the current year )
  • positionId ( on which label or square the milestone has been dropped )

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


Solution

  • 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 Maps.

    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.