summaryrefslogtreecommitdiffstats
path: root/documentation/advanced/advanced-dragndrop.asciidoc
blob: 814883357f69a6e61903d2aefc58b9f9e3fb84e3 (plain)
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
---
title: Drag and Drop
order: 12
layout: page
---

[[advanced.dragndrop]]
= Drag and Drop

((("Drag and Drop", id="term.advanced.dragndrop", range="startofrange")))


Dragging an object from one location to another by grabbing it with mouse,
holding the mouse button pressed, and then releasing the button to "drop" it to
the other location is a common way to move, copy, or associate objects. For
example, most operating systems allow dragging and dropping files between
folders or dragging a document on a program to open it. In Vaadin, it is
possible to drag and drop components and parts of certain components.

Dragged objects, or __transferables__, are essentially data objects. You can
drag and drop rows in [classname]#Table# and nodes in [classname]#Tree#
components, either within or between the components. You can also drag entire
components by wrapping them inside [classname]#DragAndDropWrapper#.

Dragging starts from a __drag source__, which defines the transferable.
Transferables implement the [classname]#Transferable# interfaces. For trees and
tables, which are bound to [classname]#Container# data sources, a node or row
transferable is a reference to an [classname]#Item# in the Vaadin Data Model.
Dragged components are referenced with a [classname]#WrapperTransferable#.
Starting dragging does not require any client-server communication, you only
need to enable dragging. All drag and drop logic occurs in two operations:
determining ( __accepting__) where dropping is allowed and actually dropping.
Drops can be done on a __drop target__, which implements the
[classname]#DropTarget# interface. Three components implement the interface:
[classname]#Tree#, [classname]#Table#, and [classname]#DragAndDropWrapper#.
These accept and drop operations need to be provided in a __drop handler__.
Essentially all you need to do to enable drag and drop is to enable dragging in
the drag source and implement the [methodname]#getAcceptCriterion()# and
[methodname]#drop()# methods in the [classname]#DropHandler# interface.

The client-server architecture of Vaadin causes special requirements for the
drag and drop functionality. The logic for determining where a dragged object
can be dropped, that is, __accepting__ a drop, should normally be done on the
client-side, in the browser. Server communications are too slow to have much of
such logic on the server-side. The drag and drop feature therefore offers a
number of ways to avoid the server communications to ensure a good user
experience.

[[advanced.dragndrop.drophandler]]
== Handling Drops

Most of the user-defined drag and drop logic occurs in a __drop handler__, which
is provided by implementing the [methodname]#drop()# method in the
[classname]#DropHandler# interface. A closely related definition is the drop
accept criterion, which is defined in the [methodname]#getAcceptCriterion()#
method in the same interface. It is described in
<<advanced.dragndrop.acceptcriteria>> later.

The [methodname]#drop()# method gets a [classname]#DragAndDropEvent# as its
parameters. The event object provides references to two important object:
[classname]#Transferable# and [classname]#TargetDetails#.

A [classname]#Transferable# contains a reference to the object (component or
data item) that is being dragged. A tree or table item is represented as a
[classname]#TreeTransferable# or [classname]#TableTransferable# object, which
carries the item identifier of the dragged tree or table item. These special
transferables, which are bound to some data in a container, are
[classname]#DataBoundTransferable#. Dragged components are represented as
[classname]#WrapperTransferable# objects, as the components are wrapped in a
[classname]#DragAndDropWrapper#.

The [classname]#TargetDetails# object provides information about the exact
location where the transferable object is being dropped. The exact class of the
details object depends on the drop target and you need to cast it to the proper
subclass to get more detailed information. If the target is selection component,
essentially a tree or a table, the [classname]#AbstractSelectTargetDetails#
object tells the item on which the drop is being made. For trees, the
[classname]#TreeTargetDetails# gives some more details. For wrapped components,
the information is provided in a [classname]#WrapperDropDetails# object. In
addition to the target item or component, the details objects provide a __drop
location__. For selection components, the location can be obtained with the
[methodname]#getDropLocation()# and for wrapped components with
[methodname]#verticalDropLocation()# and [methodname]#horizontalDropLocation()#.
The locations are specified as either [classname]#VerticalDropLocation# or
[classname]#HorizontalDropLocation# objects. The drop location objects specify
whether the transferable is being dropped above, below, or directly on (at the
middle of) a component or item.

Dropping on a [classname]#Tree#, [classname]#Table#, and a wrapped component is
explained further in the following sections.


[[advanced.dragndrop.treedrop]]
== Dropping Items On a [classname]#Tree#

You can drag items from, to, or within a [classname]#Tree#. Making tree a drag
source requires simply setting the drag mode with [methodname]#setDragMode()#.
[classname]#Tree# currently supports only one drag mode,
[literal]#++TreeDragMode.NODE++#, which allows dragging single tree nodes. While
dragging, the dragged node is referenced with a [classname]#TreeTransferable#
object, which is a [classname]#DataBoundTransferable#. The tree node is
identified by the item ID of the container item.

When a transferable is dropped on a tree, the drop location is stored in a
[classname]#TreeTargetDetails# object, which identifies the target location by
item ID of the tree node on which the drop is made. You can get the item ID with
[methodname]#getItemIdOver()# method in
[classname]#AbstractSelectTargetDetails#, which the
[classname]#TreeTargetDetails# inherits. A drop can occur directly on or above
or below a node; the exact location is a [classname]#VerticalDropLocation#,
which you can get with the [methodname]#getDropLocation()# method.

In the example below, we have a [classname]#Tree# and we allow reordering the
tree items by drag and drop.


[source, java]
----
final Tree tree = new Tree("Inventory");
tree.setContainerDataSource(TreeExample.createTreeContent());
layout.addComponent(tree);
        
// Expand all items
for (Iterator<?> it = tree.rootItemIds().iterator(); it.hasNext();)
    tree.expandItemsRecursively(it.next());
        
// Set the tree in drag source mode
tree.setDragMode(TreeDragMode.NODE);
        
// Allow the tree to receive drag drops and handle them
tree.setDropHandler(new DropHandler() {
    public AcceptCriterion getAcceptCriterion() {
        return AcceptAll.get();
    }

    public void drop(DragAndDropEvent event) {
        // Wrapper for the object that is dragged
        Transferable t = event.getTransferable();
        
        // Make sure the drag source is the same tree
        if (t.getSourceComponent() != tree)
            return;
        
        TreeTargetDetails target = (TreeTargetDetails)
            event.getTargetDetails();

        // Get ids of the dragged item and the target item
        Object sourceItemId = t.getData("itemId");
        Object targetItemId = target.getItemIdOver();

        // On which side of the target the item was dropped 
        VerticalDropLocation location = target.getDropLocation();
        
        HierarchicalContainer container = (HierarchicalContainer)
        tree.getContainerDataSource();

        // Drop right on an item -> make it a child
        if (location == VerticalDropLocation.MIDDLE)
            tree.setParent(sourceItemId, targetItemId);

        // Drop at the top of a subtree -> make it previous
        else if (location == VerticalDropLocation.TOP) {
            Object parentId = container.getParent(targetItemId);
            container.setParent(sourceItemId, parentId);
            container.moveAfterSibling(sourceItemId, targetItemId);
            container.moveAfterSibling(targetItemId, sourceItemId);
        }
        
        // Drop below another item -> make it next 
        else if (location == VerticalDropLocation.BOTTOM) {
            Object parentId = container.getParent(targetItemId);
            container.setParent(sourceItemId, parentId);
            container.moveAfterSibling(sourceItemId, targetItemId);
        }
    }
});
----

[[advanced.dragndrop.treedrop.criteria]]
=== Accept Criteria for Trees

[classname]#Tree# defines some specialized accept criteria for trees.

[classname]#TargetInSubtree#(client-side):: Accepts if the target item is in the specified sub-tree. The sub-tree is specified by the item ID of the root of the sub-tree in the constructor. The second constructor includes a depth parameter, which specifies how deep from the given root node are drops accepted. Value [literal]#++-1++# means infinite, that is, the entire sub-tree, and is therefore the same as the simpler constructor.
[classname]#TargetItemAllowsChildren#(client-side):: Accepts a drop if the tree has [methodname]#setChildrenAllowed()# enabled for the target item. The criterion does not require parameters, so the class is a singleton and can be acquired with [methodname]#Tree.TargetItemAllowsChildren.get()#. For example, the following composite criterion accepts drops only on nodes that allow children, but between all nodes: 
+
[source, java]
----
return new Or (Tree.TargetItemAllowsChildren.get(), new Not(VerticalLocationIs.MIDDLE));
----

[classname]#TreeDropCriterion#(server-side):: Accepts drops on only some items, which as specified by a set of item IDs. You must extend the abstract class and implement the [methodname]#getAllowedItemIds()# to return the set. While the criterion is server-side, it is lazy-loading, so that the list of accepted target nodes is loaded only once from the server for each drag operation. See <<advanced.dragndrop.acceptcriteria>> for an example.


In addition, the accept criteria defined in [classname]#AbstractSelect# are
available for a [classname]#Tree#, as listed in
<<advanced.dragndrop.acceptcriteria>>.



[[advanced.dragndrop.tabledrop]]
== Dropping Items On a [classname]#Table#

You can drag items from, to, or within a [classname]#Table#. Making table a drag
source requires simply setting the drag mode with [methodname]#setDragMode()#.
[classname]#Table# supports dragging both single rows, with
[literal]#++TableDragMode.ROW++#, and multiple rows, with
[literal]#++TableDragMode.MULTIROW++#. While dragging, the dragged node or nodes
are referenced with a [classname]#TreeTransferable# object, which is a
[classname]#DataBoundTransferable#. Tree nodes are identified by the item IDs of
the container items.

When a transferable is dropped on a table, the drop location is stored in a
[classname]#AbstractSelectTargetDetails# object, which identifies the target row
by its item ID. You can get the item ID with [methodname]#getItemIdOver()#
method. A drop can occur directly on or above or below a row; the exact location
is a [classname]#VerticalDropLocation#, which you can get with the
[methodname]#getDropLocation()# method from the details object.

[[advanced.dragndrop.tabledrop.criteria]]
=== Accept Criteria for Tables

[classname]#Table# defines one specialized accept criterion for tables.

[classname]#TableDropCriterion#(server-side):: Accepts drops only on (or above or below) items that are specified by a set of item IDs. You must extend the abstract class and implement the [methodname]#getAllowedItemIds()# to return the set. While the criterion is server-side, it is lazy-loading, so that the list of accepted target items is loaded only once from the server for each drag operation.




[[advanced.dragndrop.acceptcriteria]]
== Accepting Drops

((("Drag and Drop", "Accept Criteria", id="term.advanced.dragndrop.acceptcriteria", range="startofrange")))


You can not drop the objects you are dragging around just anywhere. Before a
drop is possible, the specific drop location on which the mouse hovers must be
__accepted__. Hovering a dragged object over an accepted location displays an
__accept indicator__, which allows the user to position the drop properly. As
such checks have to be done all the time when the mouse pointer moves around the
drop targets, it is not feasible to send the accept requests to the server-side,
so drops on a target are normally accepted by a client-side __accept
criterion__.

A drop handler must define the criterion on the objects which it accepts to be
dropped on the target. The criterion needs to be provided in the
[classname]#getAcceptCriterion()# method of the [classname]#DropHandler#
interface. A criterion is represented in an [classname]#AcceptCriterion# object,
which can be a composite of multiple criteria that are evaluated using logical
operations. There are two basic types of criteria: __client-side__ and
__server-side criteria__. The various built-in criteria allow accepting drops
based on the identity of the source and target components, and on the __data
flavor__ of the dragged objects.

To allow dropping any transferable objects, you can return a universal accept
criterion, which you can get with [methodname]#AcceptAll.get()#.


[source, java]
----
tree.setDropHandler(new DropHandler() {
    public AcceptCriterion getAcceptCriterion() {
        return AcceptAll.get();
    }
    ...
----

[[advanced.dragndrop.acceptcriteria.client-side]]
=== Client-Side Criteria

The __client-side criteria__, which inherit the
[classname]#ClientSideCriterion#, are verified on the client-side, so server
requests are not needed for verifying whether each component on which the mouse
pointer hovers would accept a certain object.

The following client-side criteria are define in
[package]#com.vaadin.event.dd.acceptcriterion#:

[classname]#AcceptAll#:: Accepts all transferables and targets.
[classname]#And#:: Performs the logical AND operation on two or more client-side criteria; accepts the transferable if all the given sub-criteria accept it.
[classname]#ContainsDataFlavour#:: The transferable must contain the defined data flavour.
[classname]#Not#:: Performs the logical NOT operation on a client-side criterion; accepts the transferable if and only if the sub-criterion does not accept it.
[classname]#Or#:: Performs the logical OR operation on two or more client-side criteria; accepts the transferable if any of the given sub-criteria accepts it.
[classname]#SourceIs#:: Accepts all transferables from any of the given source components
[classname]#SourceIsTarget#:: Accepts the transferable only if the source component is the same as the target. This criterion is useful for ensuring that items are dragged only within a tree or a table, and not from outside it.
[classname]#TargetDetailIs#:: Accepts any transferable if the target detail, such as the item of a tree node or table row, is of the given data flavor and has the given value.


In addition, target components such as [classname]#Tree# and [classname]#Table#
define some component-specific client-side accept criteria. See
<<advanced.dragndrop.treedrop>> for more details.

[classname]#AbstractSelect# defines the following criteria for all selection
components, including [classname]#Tree# and [classname]#Table#.

[classname]#AcceptItem#:: Accepts only specific items from a specific selection component. The selection component, which must inherit [classname]#AbstractSelect#, is given as the first parameter for the constructor. It is followed by a list of allowed item identifiers in the drag source.
[classname]#AcceptItem.ALL#:: Accepts all transferables as long as they are items.
[classname]#TargetItemIs#:: Accepts all drops on the specified target items. The constructor requires the target component ( [classname]#AbstractSelect#) followed by a list of allowed item identifiers.
[classname]#VerticalLocationIs.MIDDLE#, [classname]#TOP#, and [classname]#BOTTOM#:: The three static criteria accepts drops on, above, or below an item. For example, you could accept drops only in between items with the following: 
[source, java]
----
public AcceptCriterion getAcceptCriterion() {
    return new Not(VerticalLocationIs.MIDDLE);
}
----





[[advanced.dragndrop.acceptcriteria.server-side]]
=== Server-Side Criteria

The __server-side criteria__ are verified on the server-side with the
[methodname]#accept()# method of the [classname]#ServerSideCriterion# class.
This allows fully programmable logic for accepting drops, but the negative side
is that it causes a very large amount of server requests. A request is made for
every target position on which the pointer hovers. This problem is eased in many
cases by the component-specific lazy loading criteria
[classname]#TableDropCriterion# and [classname]#TreeDropCriterion#. They do the
server visit once for each drag and drop operation and return all accepted rows
or nodes for current [classname]#Transferable# at once.

The [methodname]#accept()# method gets the drag event as a parameter so it can
perform its logic much like in [methodname]#drop()#.


[source, java]
----
public AcceptCriterion getAcceptCriterion() {
    // Server-side accept criterion that allows drops on any other
    // location except on nodes that may not have children
    ServerSideCriterion criterion = new ServerSideCriterion() {
        public boolean accept(DragAndDropEvent dragEvent) {
            TreeTargetDetails target = (TreeTargetDetails)
                dragEvent.getTargetDetails();

            // The tree item on which the load hovers
            Object targetItemId = target.getItemIdOver();

            // On which side of the target the item is hovered
            VerticalDropLocation location = target.getDropLocation();
            if (location == VerticalDropLocation.MIDDLE)
                if (! tree.areChildrenAllowed(targetItemId))
                    return false; // Not accepted

            return true; // Accept everything else
        }
    };
    return criterion;
}
----

The server-side criteria base class [classname]#ServerSideCriterion# provides a
generic [methodname]#accept()# method. The more specific
[classname]#TableDropCriterion# and [classname]#TreeDropCriterion# are
conveniency extensions that allow definiting allowed drop targets as a set of
items. They also provide some optimization by lazy loading, which reduces server
communications significantly.


[source, java]
----
public AcceptCriterion getAcceptCriterion() {
    // Server-side accept criterion that allows drops on any
    // other tree node except on node that may not have children
    TreeDropCriterion criterion = new TreeDropCriterion() {
        @Override
        protected Set<Object> getAllowedItemIds(
                DragAndDropEvent dragEvent, Tree tree) {
            HashSet<Object> allowed = new HashSet<Object>();
            for (Iterator<Object> i =
                   tree.getItemIds().iterator(); i.hasNext();) {
                Object itemId = i.next();
                if (tree.hasChildren(itemId))
                    allowed.add(itemId);
            }
            return allowed;
        }
    };
    return criterion;
}
----


[[advanced.dragndrop.acceptcriteria.indicators]]
=== Accept Indicators

When a dragged object hovers on a drop target, an __accept indicator__ is
displayed to show whether or not the location is accepted. For
[parameter]#MIDDLE# location, the indicator is a box around the target (tree
node, table row, or component). For vertical drop locations, the accepted
locations are shown as horizontal lines, and for horizontal drop locations as
vertical lines.

For [classname]#DragAndDropWrapper# drop targets, you can disable the accept
indicators or __drag hints__ with the [parameter]#no-vertical-drag-hints#,
[parameter]#no-horizontal-drag-hints#, and [parameter]#no-box-drag-hints#
styles. You need to add the styles to the __layout that contains__ the wrapper,
not to the wrapper itself.


[source, java]
----
// Have a wrapper
DragAndDropWrapper wrapper = new DragAndDropWrapper(c);
layout.addComponent(wrapper);

// Disable the hints
layout.addStyleName("no-vertical-drag-hints");
layout.addStyleName("no-horizontal-drag-hints");
layout.addStyleName("no-box-drag-hints");
----


(((range="endofrange", startref="term.advanced.dragndrop.acceptcriteria")))

[[advanced.dragndrop.dragging]]
== Dragging Components

Dragging a component requires wrapping the source component within a
[classname]#DragAndDropWrapper#. You can then allow dragging by putting the
wrapper (and the component) in drag mode with [methodname]#setDragStartMode()#.
The method supports two drag modes: [parameter]#DragStartMode.WRAPPER# and
[parameter]#DragStartMode.COMPONENT#, which defines whether the entire wrapper
is shown as the drag image while dragging or just the wrapped component.


[source, java]
----
// Have a component to drag
final Button button = new Button("An Absolute Button");

// Put the component in a D&D wrapper and allow dragging it
final DragAndDropWrapper buttonWrap = new DragAndDropWrapper(button);
buttonWrap.setDragStartMode(DragStartMode.COMPONENT);

// Set the wrapper to wrap tightly around the component
buttonWrap.setSizeUndefined();
        
// Add the wrapper, not the component, to the layout
layout.addComponent(buttonWrap, "left: 50px; top: 50px;");
----

The default height of [classname]#DragAndDropWrapper# is undefined, but the
default width is 100%. If you want to ensure that the wrapper fits tightly
around the wrapped component, you should call [methodname]#setSizeUndefined()#
for the wrapper. Doing so, you should make sure that the wrapped component does
not have a relative size, which would cause a paradox.

Dragged components are referenced in the [classname]#WrapperTransferable#. You
can get the reference to the dragged component with
[methodname]#getDraggedComponent()#. The method will return [literal]#++null++#
if the transferable is not a component. Also HTML 5 drags (see later) are held
in wrapper transferables.


[[advanced.dragndrop.drop-on-component]]
== Dropping on a Component

Drops on a component are enabled by wrapping the component in a
[classname]#DragAndDropWrapper#. The wrapper is an ordinary component; the
constructor takes the wrapped component as a parameter. You just need to define
the [classname]#DropHandler# for the wrapper with
[methodname]#setDropHandler()#.

In the following example, we allow moving components in an absolute layout.
Details on the drop handler are given later.


[source, java]
----
// A layout that allows moving its contained components
// by dragging and dropping them
final AbsoluteLayout absLayout = new AbsoluteLayout();
absLayout.setWidth("100%");
absLayout.setHeight("400px");

... put some (wrapped) components in the layout ...

// Wrap the layout to allow handling drops
DragAndDropWrapper layoutWrapper =
        new DragAndDropWrapper(absLayout);

// Handle moving components within the AbsoluteLayout
layoutWrapper.setDropHandler(new DropHandler() {
    public AcceptCriterion getAcceptCriterion() {
        return AcceptAll.get();
    }
    
    public void drop(DragAndDropEvent event) {
        ...        
    }
});
----

[[advanced.dragndrop.drop-on-component.details]]
=== Target Details for Wrapped Components

The drop handler receives the drop target details in a
[classname]#WrapperTargetDetails# object, which implements the
[classname]#TargetDetails# interface.


[source, java]
----
public void drop(DragAndDropEvent event) {
    WrapperTransferable t =
        (WrapperTransferable) event.getTransferable();
    WrapperTargetDetails details =
        (WrapperTargetDetails) event.getTargetDetails();
----

The wrapper target details include a [classname]#MouseEventDetails# object,
which you can get with [methodname]#getMouseEvent()#. You can use it to get the
mouse coordinates for the position where the mouse button was released and the
drag ended. Similarly, you can find out the drag start position from the
transferable object (if it is a [classname]#WrapperTransferable#) with
[methodname]#getMouseDownEvent()#.


[source, java]
----
// Calculate the drag coordinate difference
int xChange = details.getMouseEvent().getClientX()
              - t.getMouseDownEvent().getClientX();
int yChange = details.getMouseEvent().getClientY()
              - t.getMouseDownEvent().getClientY();

// Move the component in the absolute layout
ComponentPosition pos =
    absLayout.getPosition(t.getSourceComponent());
pos.setLeftValue(pos.getLeftValue() + xChange);
pos.setTopValue(pos.getTopValue() + yChange);
----

You can get the absolute x and y coordinates of the target wrapper with
[methodname]#getAbsoluteLeft()# and [methodname]#getAbsoluteTop()#, which allows
you to translate the absolute mouse coordinates to coordinates relative to the
wrapper. Notice that the coordinates are really the position of the wrapper, not
the wrapped component; the wrapper reserves some space for the accept
indicators.

The [methodname]#verticalDropLocation()# and
[methodname]#horizontalDropLocation()# return the more detailed drop location in
the target.



[[advanced.dragndrop.external]]
== Dragging Files from Outside the Browser

The [classname]#DragAndDropWrapper# allows dragging files from outside the
browser and dropping them on a component wrapped in the wrapper. Dropped files
are automatically uploaded to the application and can be acquired from the
wrapper with [methodname]#getFiles()#. The files are represented as
[classname]#Html5File# objects as defined in the inner class. You can define an
upload [classname]#Receiver# to receive the content of a file to an
[classname]#OutputStream#.

Dragging and dropping files to browser is supported in HTML 5 and requires a
compatible browser, such as Mozilla Firefox 3.6 or newer.


(((range="endofrange", startref="term.advanced.dragndrop")))