Skip to main content
summaryrefslogblamecommitdiffstats
blob: cd30d2d23ea7278d153cc4f6ea194358132e4985 (plain) (tree)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620











































































































































































































































































































































































































































































































































































































































                                                                                                                          
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"><html><head>
  <meta http-equiv="Content-Type" content="text/html; charset=windows-1252">
  <title>Drag and Drop in the Eclipse UI</title>
  <meta name="Author" content="John Arthorne">
  <link rel="stylesheet" href="../default_style.css"></head>

<body link="#0000ff" vlink="#800080" bgcolor="#ffffff">
 
<div align="right"><font size="-2">Copyright © 2003 International Business Machines Corp.</font>    
<table border="0" cellspacing="0" cellpadding="2" width="100%">
     <tbody>
    <tr>
        <td align="left" valign="top" colspan="2" bgcolor="#0080c0"><b>
        <font face="Arial,Helvetica" color="#ffffff"> Eclipse Corner Article</font></b></td>
     </tr>
  </tbody>
</table>
 </div>
 
<div align="left">    
<h1><img src="images/Idea.jpg" height="86" width="120" align="middle">
</h1>
 </div>
 
<p> </p>
  
<h1 align="center">Drag and Drop in the Eclipse UI</h1>
 
<blockquote> <b>Summary</b>  <br>
  In this article, we discuss the drag and drop facilities provided by JFace and 
  the Eclipse platform UI. After reading this, you will know how to add drag and 
  drop support to your own Eclipse views, and how that support will interact with 
  the standard views in the Eclipse platform. Along the way, we'll also discuss 
  that keyboard relative of drag and drop: cut and paste. You'll learn that putting 
  your own custom objects on the clipboard is easy once you've figured out the 
  basics of drag and drop. This article is intended to be read as a companion 
  to the <a href="../Article-SWT-DND/DND-in-SWT.html">SWT 
  drag and drop article</a>.<br>
  <p><b> By John Arthorne, IBM OTI Labs</b><br>
    <font size="-1">August 25, 2003</font></p>
 </blockquote>
 
<hr width="100%">  <a name="1"> </a>
<h2>
Doing the drag and drop</h2>

<div align="right"><a name="1"><i>Never keep up with the Joneses. Drag them down to your level.</i> <br>
– Quentin Crisp</a></div>

<p>In keeping with the general philosophy of JFace, the JFace drag and drop 
adds a layer of functionality on top of the SWT drag and drop support.  This layer
allows the developer to deal directly with domain objects (such as resources,
tasks, etc), without having to worry too much about the underlying window
controls.  Rather than concealing or replacing the drag and drop support in SWT,
the JFace drag and drop support works as an extension to the same concepts
found in SWT drag and drop.
</p>
<h3>Transfer types</h3>
As you'll know from the <a href="../Article-SWT-DND/DND-in-SWT.html">SWT drag and drop article</a>,
the notion of transfer types is central to the drag and drop support in Eclipse-based UIs.
To recap, transfer types allow drag sources to specify what kinds of object they allow to be 
dragged out of their widget, and they allow drop targets to specify what kinds of 
objects they are willing to receive.  For each transfer type, there is a subclass of 
<code>org.eclipse.swt.dnd.Transfer</code>.  These subclasses implement the marshaling 
behavior that converts between objects and bytes, allowing drag and drop 
transfers between applications. The following table summarizes the transfer types 
provided by the basic Eclipse platform, along with the object they are capable of transferring:
</p>
<p>
<table BORDER COLS=2 WIDTH="90%" >
<tr>
<td><b>Transfer class</b></td>
<td><b>Object it transfers</b></td>
</tr>
<tr>
<td><code>org.eclipse.swt.dnd.FileTransfer</code></td>
<td><code>java.lang.String[]</code> (list of absolute file paths)</td>
</tr>
<tr>
<td><code>org.eclipse.swt.dnd.RTFTransfer</code></td>
<td><code>java.lang.String</code> (may contain RTF formatting characters)</td>
</tr>
<tr>
<td><code>org.eclipse.swt.dnd.TextTransfer</code></td>
<td><code>java.lang.String</code></td>
</tr>
<tr>
<td><code>org.eclipse.ui.part.MarkerTransfer</code></td>
<td><code>org.eclipse.core.resources.IMarker[]</code></td>
</tr>
<tr>
<td><code>org.eclipse.ui.part.ResourceTransfer</code></td>
<td><code>org.eclipse.core.resources.IResource[]</code></td>
</tr>
<tr>
<td><code>org.eclipse.ui.part.EditorInputTransfer</code></td>
<td><code>org.eclipse.ui.part.EditorInputTransfer.EditorInputData[]</code></td>
</tr>
<tr>
<td><code>org.eclipse.ui.part.PluginTransfer</code></td>
<td><code>org.eclipse.ui.part.PluginTransferData</code></td>
</tr>
</table>
</p>
<p>
The set of transfer types is open ended, because third party
tool writers can implement their own transfer types for their domain objects.
To implement your own transfer type, it is recommended that you subclass
<code>org.eclipse.swt.dnd.ByteArrayTransfer</code>.
See the <a href="../Article-SWT-DND/DND-in-SWT.html">SWT drag and drop article</a> for more information 
on defining your own transfer types.
</p>
<p>
<p><img src="images/tip.gif">
An important point about transfer types is that they don't necessarily need to store 
the entire object as serialized bytes.  In most cases it is simpler and more 
efficient to just store enough information about where the object is found.  For 
example, <code>FileTransfer</code> simply encodes a string which represents the 
absolute path of the file in the file system.  It does not store the entire file in the 
transfer object.
</p>
<h3>Transfer types supported by the standard views</h3>
<p>
Many of the basic views you see in Eclipse already support various
transfer types. It is important to understand what transfer types are supported by
each view, because this dictates how the drag and drop support in your view will
interact with other basic views found in the Eclipse platform UI.
</p>
<p>
The Navigator view supports dragging and dropping files (<code>FileTransfer</code>),
and resources (<code>ResourceTransfer</code>). For example, you can drag a file 
from the Navigator view into Windows Explorer or the Windows Desktop. Similarly, 
you can import resources into Eclipse simply by dragging them from Windows into 
the Navigator view of your workbench. You can also drag files between two instances
of Eclipse, or drag within a single Navigator to copy and move files within
your workspace.  If your view supports either <code>FileTransfer</code> or
<code>ResourceTransfer</code>, then users will be able to transfer resources between
your view and the Navigator view.
</p>
<p>
The Tasks and Bookmarks views support dragging of markers (<code>MarkerTransfer</code>). 
Dragging a selection of tasks from the Tasks view into an application such as MS 
Word will generate a textual marker report (<code>TextTransfer</code>). You can also drag markers 
out of the Tasks and Bookmarks views into other parts of the workbench, such as 
the editor area. Dragging a marker to the editor area will open the associated 
resource in the editor and jump to that marker location in the editor. 
</p>
<p>
Finally, the editor area supports dropping of editor inputs (<code>EditorInputTransfer</code>),
resources, or markers.  Dragging these objects to the editor will cause it to locate and
open an appropriate editor for the given resource, editor input or marker.  In the case 
of markers, it will also jump to that marker location in the editor.
</p>
<h3>A running example: go go gadgets!</h3>
<div align="right"><a name="1"><i>"Gosh, Scotland is beautiful, Uncle Gadget."<br>
"It certainly is, Penny. This is where they make Scotch tape, ya know."</i> <br>
– Inspector Gadget</a></div>

<p>
For the remainder of this article, we'll make use of a running example.  This example
is an Eclipse plug-in for manipulating a simple object model of <i>gadgets</i>.  Gadgets 
are simply named objects that can be formed into trees.  Each gadget knows its parent 
and its children.  The example includes two views, one containing a table viewer, and 
the other containing a tree viewer.  Drag and drop can be used to copy or move 
gadgets between these views, and to rearrange the order or hierarchy of gadgets 
within a view.  There is a <code>GadgetTransfer</code> class for encoding an array
of gadgets to and from a byte array.  Complete source code for the example is found 
<a href="gadgetsrc.zip">here</a>.
</p>
<h3>Adding JFace viewer drag support</h3>
Adding drag support to a viewer means that it enables the user to select
any item in the viewer with the mouse, and drag it into another viewer
or another application. Drag support can be added to any subclass of 
<code>org.eclipse.jface.viewers.StructuredViewer</code>
using the <code>addDragSupport(int, Transfer[], DragSourceListener)</code>
method. From our gadget example, here is the code for associating a drag listener
with a tree viewer:
</p>
<font color="#4444cc">
<pre>
   TreeViewer gadgetViewer = new TreeViewer(...);
   int ops = DND.DROP_COPY | DND.DROP_MOVE;
   Transfer[] transfers = new Transfer[] { GadgetTransfer.getInstance()};
   viewer.addDragSupport(ops, transfers, new GadgetDragListener(viewer));
</pre>
</font> 
<br>
<p>
<code>GadgetDragListener</code> is an implementation of the SWT interface
<code>org.eclipse.swt.dnd.DragSourceListener</code>.  There is nothing specific
to JFace about the implementation of <code>DragSourceListener</code>, so you
can learn more about its implementation in the <a href="../Article-SWT-DND/DND-in-SWT.html">SWT drag and drop article</a>.
</p>

<h3>Adding viewer drop support</h3>
<p>
Drop support can be added to viewers using the <code>StructuredViewer.addDropSupport(int,
Transfer[], DropTargetListener)</code> method. This allows your viewer to
be the target of a drop operation. The code for adding drop support is almost the
same as for adding drag support:
</p>
<p>
<font color="#4444cc">
<pre>
   TreeViewer gadgetViewer = new TreeViewer(...);
   int ops = DND.DROP_COPY | DND.DROP_MOVE;
   Transfer[] transfers = new Transfer[] { GadgetTransfer.getInstance()};
   viewer.addDropSupport(ops, transfers, new GadgetTreeDropAdapter(viewer));
</pre>
</font> 
</p>
<p>
JFace provides a standard implementation of <code>DropTargetListener</code> 
called <code>org.eclipse.jface.viewers.ViewerDropAdapter</code>.
This adapter makes it easy to add drop support for simple cases.  If you have more
complex requirements, you can always override the SWT <code>DropTargetListener</code>
interface directly for ultimate flexibility.
When sub-classing <code>ViewerDropAdapter</code>, simply implement its two 
abstract methods: <code>validateDrop(Object target, int operation, TransferData transferType)</code>,
and <code>performDrop(Object data)</code>.
</p>
<p>
<code>validateDrop</code> is called whenever the user moves over a new item
in your viewer, or changes the drop type with one of the modifier keys.
The method provides the current drop target, operation, and transfer type.
The return value of this method indicates whether a drop at the current location is
valid or not. A return value of false will change the drag icon to indicate
to the user that it is illegal to drop what they are dragging at the current
location.  In our gadget example, it is always legal to drop a gadget, so the 
<code>validateDrop</code> implementation simply looks like this:
<font color="#4444cc">
<pre>
   public boolean validateDrop(Object target, int op, TransferData type) {
      return GadgetTransfer.getInstance().isSupportedType(type);
   }
</pre>
</font>
<p>
This code just makes sure that the user is indeed dropping a gadget, and not
some other object such as a resource or marker. If you have more complex
validation requirements based on the target object, you can do that here.  For example,
in a file navigator, you may want to allow dropping on top of directories, but not on top of
files.
</p>
<p>
<code>performDrop</code> is called when the user lets go of the mouse button,
indicating that they want the drop to occur. Your implementation should
accordingly perform the expected behavior for that drop. Context for the
drop is provided by the methods <code>getCurrentTarget</code>, <code>getCurrentOperation</code>,
and <code>getCurrentLocation</code> on <code>ViewerDropAdapter</code>. Most
importantly, at the time when <code>performDrop </code>is called, <code>getCurrentTarget</code>
will provide the object in your viewer that is currently under the mouse.  Here is the 
<code>performDrop</code> method from our gadget example:
</p>
<p>
<font color="#4444cc">
<pre>
   public boolean performDrop(Object data) {
1     Gadget target = (Gadget)getCurrentTarget();
2     if (target == null)
3        target = (Gadget)getViewer().getInput();
4     Gadget[] toDrop = (Gadget[])data;
5     TreeViewer viewer = (TreeViewer)getViewer();
6     //cannot drop a gadget onto itself or a child
7     for (int i = 0; i < toDrop.length; i++)
8        if (toDrop[i].equals(target) || target.hasParent(toDrop[i]))
9           return false;
10    for (int i = 0; i < toDrop.length; i++) {
11       toDrop[i].setParent(target);
12       viewer.add(target, toDrop[i]);
13       viewer.reveal(toDrop[i]);
14    }
15    return true;
   }
</pre>
</font>
</p>
<p>
In lines 1-3, it is determining what the target gadget is.  The target is the item that
is currently under the mouse when the drop occurs.  If there is no item under the mouse, it
takes the viewer's input element as the target.  On lines 7-9, it is making sure
that the user is not dropping an item onto itself, or onto a child of itself.  You may 
be wondering why this validation was not done in the <code>validateDrop</code> 
method.  In SWT, the transfer of data from the source to the target is done lazily
when the drop is initiated.  So, as the user is dragging, the destination has no
way of finding out what source object is being dragged until the drop is performed.
Returning <code>false</code> from the <code>performDrop</code> method will
cancel the drop.  On lines 10-13, it is performing the actual drop.  This involves updating
the gadget with its new parent (line 11), and then adding and revealing the new item
in the viewer (lines 12-13).  Finally, the method returns true on line 15 to indicate
that the drop was successful.
</p>
<p>
<code>ViewerDropAdapter</code> has two more interesting methods for controlling
drag feedback effects. The method <code>setFeedbackEnabled</code> is used to turn
insertion feedback on or off.  If insertion feedback is on, the cursor will change when
the mouse is hovering between two items to indicate where the insertion will occur.
Using this effect, you can allow the user to sort items in a tree using drag and drop.
In your drop adapter, use the method <code>getCurrentLocation</code> to determine
if the cursor is currently directly on, before, or after the current target object.  Here
is how the gadget drop example can be updated to make use of this location 
information:
<p>
<font color="#4444cc">
<pre>
   public boolean performDrop(Object data) {
      //set the target gadget according to current cursor location
      Gadget target = (Gadget)getCurrentTarget();
      if (target != null) {
         int loc = getCurrentLocation();
         if (loc == LOCATION_BEFORE || loc == LOCATION_AFTER)
            target = target.getParent();
      }
      if (target == null)
         target = (Gadget)getViewer().getInput();
      Gadget[] toDrop = (Gadget[])data;
      TreeViewer viewer = (TreeViewer)getViewer();
      // ... remainder of method is the same as previous example ...
</pre>
</font>
</p>
<p>
In this snippet, it looks at the cursor location to see if it is before or after an
item in the tree.  When a gadget is dropped between other gadgets, it sets the
parent to be the parent of the neighboring gadget.  Said another way, the
dropped gadget will become a sibling of the gadget it is dropped next to.
</p>
<p>
The method <code>ViewerDropAdapter.setScrollExpandEnabled</code> is used to
turn scroll and expansion effects on or off.  When this is turned on, hovering
near the bottom of a tree or table will cause the widget to automatically scroll in
that direction.  Hovering over a collapsed tree item for a sufficient amount of time
will cause the item to be expanded.  In most cases you should leave these effects
on, otherwise users will not able to use drag and drop effectively when your tree
or table contains many items.
</p>

<h3>Plugin drop handling</h3>
<div align="right"><a name="1"><i>When I can't handle events, I let them handle themselves.</i> <br>
– Henry Ford</a></div>
<p>
Due to the UI layering imposed by the plug-in mechanism, viewers are often
not aware of the content and nature of other viewers. This can make drag
and drop operations between plug-ins difficult. For example, our gadget
plug-in may want to allow the user to drop gadget objects into the Navigator
view.  Since the Navigator view doesn't know anything about gadget objects
(the Navigator only displays <code>org.eclipse.core.resources.IResource</code>
objects), it would not be able to support this.  Similarly, another plug-in may want
to drop some other kind of objects into the views from the gadget example. 
To address this problem, a plug-in drop support mechanism
is provided by the workbench. This mechanism essentially delegates the
drop behavior back to the originator of the drag operation. In the gadget example,
we use this extension point to drop gadgets into the Navigator view, and create
files containing descriptions of the gadgets that were dropped. Here are the
steps required to add drag and drop behavior using this mechanism:
<p>
Step 1) In your plugin.xml, define an extension on the "org.eclipse.ui.dropActions"
extension point. Here is an example XML declaration:
<p>
<font color="#4444cc">
<pre>
   &lt;extension
         id=&quot;gadgetDrop&quot;
         name=&quot;Gadget Resource Drop&quot;
         point=&quot;org.eclipse.ui.dropActions&quot;&gt;
      &lt;action
            class=&quot;org.eclipse.ui.examples.gdt.dnd.GadgetPluginDropAdapter&quot;
            id=&quot;org.eclipse.ui.examples.gdt.gadgetDrop&quot;&gt;
      &lt;/action&gt;
   &lt;/extension&gt;
</pre>
</font> 
</p>
<p>
Step 2) Implement the code that will perform the drop.  This work is done by
the class defined in the extension markup above, which must implement
<code>org.eclipse.ui.part.IDropActionDelegate</code>.
This interface defines a single run() method that gets called when the
drop occurs.  The run method is supplied with the object being dragged, as well as the
object under the cursor when the drop occurs.  Here is an example implementation of
the drop delegate from the gadget example:
</p>
<p>
<font color="#4444cc">
<pre>
   public boolean run(Object source, Object target) {
1     if (target instanceof IContainer) {
2        GadgetTransfer transfer = GadgetTransfer.getInstance();
3        Gadget[] gadgets = transfer.fromByteArray((byte[])source);
4        IContainer parent = (IContainer)target;
5        for (int i = 0; i < gadgets.length; i++) {
6           writeGadgetFile(parent, gadgets[i]);
7        }
8        return true;
9     }
10    //drop was not successful so return false
11    return false;
   }
</pre>
</font>
</p>
<p>
On line 1, it ensures that the gadget is being dropped on some kind of container.
In practice, your drop delegate may support being dropped on several different
types of objects, with different behavior for each type.  On lines 2-3, it makes use
of a convenience method to extract the transferred gadgets from the byte array in the
transfer data. Since the plug-in transfer data contains a byte array, this is the exact 
same code that typically exists in your custom <code>Transfer</code> subclass.
It then iterates over the transferred gadgets, and creates a file in the target container
for that gadget.  Finally, it returns true if the drop was successful (line 8), or false if the drop
occurred on a target object that was not supported (line 11).
</p>

<p>
Step 3) In the viewer that will be the source of the drag and drop,
add drag support using the <code>StructuredViewer.addDragSupport()</code> method
described earlier. In the array of supported transfer types, include the
singleton instance of <code>org.eclipse.ui.part.PluginTransfer</code>. In your
implementation of <code>DragSourceListener</code>, the <code>dragSetData</code>
method must set the data to be an instance of <code>org.eclipse.ui.part.PluginTransferData</code>.
This object consists of the id of your drop action,
along with the data being transferred.  Here is the code in the drag listener from the
gadget example:
</p>
<p>
<font color="#4444cc">
<pre>
   public void dragSetData(DragSourceEvent event) {
1     IStructuredSelection selection = (IStructuredSelection)viewer.getSelection();
2     Gadget[] gadgets = (Gadget[])selection.toList().toArray(new Gadget[selection.size()]);
3     if (GadgetTransfer.getInstance().isSupportedType(event.dataType)) {
4        event.data = gadgets;
5     } else if (PluginTransfer.getInstance().isSupportedType(event.dataType)) {
6        byte[] data = GadgetTransfer.getInstance().toByteArray(gadgets);
7        event.data = new PluginTransferData("org.eclipse.ui.examples.gdt.gadgetDrop", data);
8     }
9  }
   }
</pre>
</font> 
</p>
<p>
Lines 1-2 are the familiar code for creating an array of gadgets from the viewer's
selection.  Line 4 handles the old case of a gadget transfer by simply setting the
event data to be the correct type for <code>GadgetTransfer</code>.  Lines 6-7
are the interesting new code for handling a plug-in drag event.  First, it uses a convenience
method on the <code>GadgetTransfer</code> class for converting the array of gadgets
into a byte array.  Next, it creates a <code>PluginTransferData</code> object, passing
in the id of the plug-in drop action that was declared in the plugin.xml in step 1), along
with the serialized gadget array.
</p>

<p>Step 4) In the viewer that will receive the drop, the drop listener
for that viewer must subclass <code>org.eclipse.ui.part.PluginDropAdapter</code>,
which in turn subclasses <code>ViewerDropAdapter</code> as described earlier.
Be sure to invoke the super methods for validateDrop and performDrop in
cases where your adapter does not understand the transfer type. Following
from our earlier example, the viewer’s declaration would look
like this:
</p>
<p>
<font color="#4444cc">
<pre>
   TreeViewer gadgetViewer = new TreeViewer(...);
   int ops = DND.DROP_COPY | DND.DROP_MOVE;
   Transfer[] transfers = new Transfer[] {
      GadgetTransfer.getInstance(), PluginTransfer.getInstance()};
   viewer.addDropSupport(ops, transfers, new GadgetTreeDropAdapter(viewer));
</pre>
</font> 
</p>
<p>
The basic Workbench views such as the Navigator view 
already have this support added. It is recommended that anyone defining
their own views should add the plug-in support, in anticipation of future
third party plug-ins wanting to drop content into their views. For more information
about this advanced drag and drop mechanism, refer to the documentation
for the <code>org.eclipse.ui.dropActions</code> extension point.
</p>
<h2>Cut, copy and paste</h2>
<div align="right"><a name="1"><i>"I'll get you next time Gadget... Next time!" </i> <br>
– Dr. Claw </a></div>
<p>
Cut and paste can be thought of as the keyboard equivalent of drag and drop.  Once you've
mastered drag and drop support, you'll find that cut and paste is a snap.  Once
again, the gadgets example provides a complete implementation of cut and paste
support.  Here is code for adding cut and paste support from within an Eclipse view
(this code goes in the view's <code>createPartControl</code> method):
</p>
<p>
<font color="#4444cc">
<pre>
   public void createPartControl(Composite parent) {
      viewer = new TreeViewer(parent, SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL);
      
      //... initialize viewer's content and label providers ...
      
      clipboard = new Clipboard(getSite().getShell().getDisplay());
      IActionBars bars = getViewSite().getActionBars();
      bars.setGlobalActionHandler(
         IWorkbenchActionConstants.CUT, 
         new CutGadgetAction(viewer, clipboard));
      bars.setGlobalActionHandler(
         IWorkbenchActionConstants.COPY, 
         new CopyGadgetAction(viewer, clipboard));
      bars.setGlobalActionHandler(
         IWorkbenchActionConstants.PASTE, 
         new PasteTreeGadgetAction(viewer, clipboard));
   }
</pre>
</font> 
</p>
<p>
This code simply creates a new SWT clipboard, and then defines global actions
for cut, copy, and paste using that clipboard.  The <code>IActionBars</code>
interface is used for hooking into global actions.  <b>Note:</b> SWT clipboard
objects are operating system resources that must be disposed when no longer
needed.  In the gadget example, we <code>dispose()</code> the clipboard
when the view is disposed. Disposing of an SWT clipboard instance does not remove 
the data from the operating system's clipboard.
</p>
<p>
The actions that are provided as global action handlers should be subclasses of the 
<code>org.eclipse.jface.action.Action</code> class.  The code for these actions is 
similar to the drag and drop code, except that they use the clipboard as the transfer 
mechanism, rather than the drag and drop event handlers.  Here is the code for the 
run method of the cut action:
</p>
<p>
<font color="#4444cc">
<pre>
   public void run() {
1     IStructuredSelection selection = (IStructuredSelection)viewer.getSelection();
2     Gadget[] gadgets = (Gadget[])selection.toList().toArray(new Gadget[selection.size()]);
3     clipboard.setContents(
4        new Object[] { gadgets },
5        new Transfer[] { GadgetTransfer.getInstance()});
6     for (int i = 0; i < gadgets.length; i++) {
7        gadgets[i].setParent(null);
8     }
9     viewer.refresh();
   }
</pre>
</font> 
</p>
<p>
On lines 3-5, the gadgets are placed on the clipboard, along with the <code>
Transfer</code> object that will be used for serializing them.  Lines 6-8 remove
the gadgets from the source view (since they are being cut, not copied), and line 9
refreshes the view to update it with the new contents.  The copy action is almost
identical, except lines 6-9 are removed, since we don't want to remove the gadgets
from the source view on copy.  The paste action for pasting into a tree looks like this:
</p>
<p>
<font color="#4444cc">
<pre>
   public void run() {
1     IStructuredSelection sel = (IStructuredSelection)viewer.getSelection();
2     Gadget parent = (Gadget)sel.getFirstElement();
3     if (parent == null)
4        parent = (Gadget)viewer.getInput();
5     Gadget[] gadgets = (Gadget[])clipboard.getContents(GadgetTransfer.getInstance());
6     if (gadgets == null)
7        return;
8     //cannot drop a gadget onto itself or a child
9     for (int i = 0; i < gadgets.length; i++)
10       if (gadgets[i].equals(parent) || parent.hasParent(gadgets[i]))
11          return;
12    for (int i = 0; i < gadgets.length; i++) {
13       gadgets[i].setParent(parent);
14    }
15    viewer.refresh();
   }
</pre>
</font> 
</p>
<p>
Lines 1-4 compute the parent gadget for the gadgets that are about to be pasted.
If there is a gadget currently selected, it will become the new parent, otherwise the root
gadget is used.  Line 5 is the crucial step that takes the gadget objects off the clipboard.
You should always check that the returned value is not null, since there may not be
an object of the requested type on the clipboard.  Lines 9-15 are the familiar code
that we had in the drop event handler, first ensuring that we're not dropping a gadget
onto itself or a child of itself, and then setting the parent elements for the dropped
gadgets.  

<p><img src="images/tip.gif">
In your implementation, you will probably want to factor out the paste and drop
code into a common place, since they both do the same thing.  Likewise, the code for
the cut action is similar to the code in <code>dragFinished</code> in the drag action 
handler.  For clarity, the gadget example duplicates the code in both places, but you'll
make your code easier to maintain if you keep it all in one place.
</p>

<h3>Summary and further information</h3>
<p>
You now have all the information you need for adding drag/drop and cut/paste support 
in your own JFace viewers.  You should also know all about the standard transfer types
supplied by the platform, and what transfers are supported by the standard views
in the platform.
</p>
<p>
For more information, browse through the complete source from the 
<a href="gadgetsrc.zip">gadgets example</a>.  The UI readme 
example, available from the eclipse.org 
<a href="http://www.eclipse.org/downloads/index.php">downloads page</a>, 
also implements some drag and drop support.  For more in depth examples, you 
can look at the drag and drop support implemented within the UI plug-in itself. For 
example, see <code>org.eclipse.ui.views.navigator.ResourceNavigator </code>to 
see how the Navigator view implements its drag and drop behavior. The Navigator 
uses two helper classes, <code>NavigatorDragAdapter</code> and 
<code>NavigatorDropAdapter</code>, which are also instructive to look at.
</p>
<h3>Acknowledgements</h3>
<p> Thanks to Knut Radloff from the Eclipse Platform UI Team and Jim des Rivi&egrave;res 
  at OTI Labs for proof reading and providing feedback for this article. </p>
<p><small>Java and all Java-based trademarks and logos are trademarks or registered trademarks of Sun 
Microsystems, Inc. in the United States, other countries, or both.</small></p>
</body>
</html>

Back to the top