123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447 |
- /*
- * Copyright 2000-2016 Vaadin Ltd.
- *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not
- * use this file except in compliance with the License. You may obtain a copy of
- * the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations under
- * the License.
- */
- package com.vaadin.ui.dnd;
-
- import java.util.Collections;
- import java.util.LinkedHashMap;
- import java.util.Map;
- import java.util.Objects;
-
- import com.vaadin.server.AbstractExtension;
- import com.vaadin.server.Resource;
- import com.vaadin.shared.Registration;
- import com.vaadin.shared.ui.dnd.criteria.ComparisonOperator;
- import com.vaadin.shared.ui.dnd.DragSourceRpc;
- import com.vaadin.shared.ui.dnd.DragSourceState;
- import com.vaadin.shared.ui.dnd.DropEffect;
- import com.vaadin.shared.ui.dnd.EffectAllowed;
- import com.vaadin.shared.ui.dnd.criteria.Payload;
- import com.vaadin.ui.AbstractComponent;
- import com.vaadin.ui.dnd.event.DragEndEvent;
- import com.vaadin.ui.dnd.event.DragEndListener;
- import com.vaadin.ui.dnd.event.DragStartEvent;
- import com.vaadin.ui.dnd.event.DragStartListener;
-
- /**
- * Extension to make a component drag source for HTML5 drag and drop
- * functionality.
- *
- * @param <T>
- * Type of the component to be extended.
- * @author Vaadin Ltd
- * @since 8.1
- */
- public class DragSourceExtension<T extends AbstractComponent>
- extends AbstractExtension {
-
- private Registration dragStartListenerHandle;
- private Registration dragEndListenerHandle;
-
- /**
- * Stores the server side drag data that is available for the drop target if
- * it is in the same UI.
- */
- private Object dragData;
-
- /**
- * Extends {@code target} component and makes it a drag source.
- *
- * @param target
- * Component to be extended.
- */
- public DragSourceExtension(T target) {
- super.extend(target);
-
- initListeners();
- }
-
- /**
- * Initializes dragstart and -end event listeners for this drag source to
- * capture the active drag source for the UI.
- */
- private void initListeners() {
- // Set current extension as active drag source in the UI
- dragStartListenerHandle = addDragStartListener(
- event -> getUI().setActiveDragSource(this));
-
- // Remove current extension as active drag source from the UI
- dragEndListenerHandle = addDragEndListener(
- event -> getUI().setActiveDragSource(null));
- }
-
- @Override
- public void attach() {
- super.attach();
-
- registerDragSourceRpc();
- }
-
- /**
- * Registers the server side RPC methods invoked from client side on
- * <code>dragstart</code> and <code>dragend</code> events.
- * <p>
- * Override this method if you have custom RPC interface for transmitting
- * those events with more data. If just need to do additional things before
- * firing the events, then you should override {@link #onDragStart()} and
- * {@link #onDragEnd(DropEffect)} instead.
- */
- protected void registerDragSourceRpc() {
- registerRpc(new DragSourceRpc() {
- @Override
- public void dragStart() {
- onDragStart();
- }
-
- @Override
- public void dragEnd(DropEffect dropEffect) {
- onDragEnd(dropEffect);
- }
- });
- }
-
- /**
- * Method invoked when a <code>dragstart</code> has been sent from client
- * side. Fires the {@link DragStartEvent}.
- */
- protected void onDragStart() {
- DragStartEvent<T> event = new DragStartEvent<>(getParent(),
- getState(false).effectAllowed);
- fireEvent(event);
- }
-
- /**
- * Method invoked when a <code>dragend</code> has been sent from client
- * side. Fires the {@link DragEndEvent}.
- *
- * @param dropEffect
- * the drop effect on the dragend
- */
- protected void onDragEnd(DropEffect dropEffect) {
- DragEndEvent<T> event = new DragEndEvent<>(getParent(), dropEffect);
- fireEvent(event);
- }
-
- @Override
- public void remove() {
- super.remove();
-
- // Remove listeners attached on construction
- dragStartListenerHandle.remove();
- dragEndListenerHandle.remove();
- }
-
- /**
- * Sets the allowed effects for the current drag source element. Used for
- * setting client side {@code DataTransfer.effectAllowed} parameter for the
- * drag event.
- * <p>
- * By default the value is {@link EffectAllowed#UNINITIALIZED} which is
- * equivalent to {@link EffectAllowed#ALL}.
- *
- * @param effect
- * Effects to allow for this draggable element. Cannot be {@code
- * null}.
- */
- public void setEffectAllowed(EffectAllowed effect) {
- if (effect == null) {
- throw new IllegalArgumentException("Allowed effect cannot be null");
- }
- if (!Objects.equals(getState(false).effectAllowed, effect)) {
- getState().effectAllowed = effect;
- }
- }
-
- /**
- * Returns the allowed effects for the current drag source element. Used to
- * set client side {@code DataTransfer.effectAllowed} parameter for the drag
- * event.
- * <p>
- * You can use different types of data to support dragging to different
- * targets. Accepted types depend on the drop target and those can be
- * platform specific. See https://developer.mozilla.org/en-US/docs/Web/API/HTML_Drag_and_Drop_API/Recommended_drag_types
- * for examples on different types.
- * <p>
- * <em>NOTE: IE11 only supports type ' text', which can be set using {@link
- * #setDataTransferText(String data)}</em>
- *
- * @return Effects that are allowed for this draggable element.
- */
- public EffectAllowed getEffectAllowed() {
- return getState(false).effectAllowed;
- }
-
- /**
- * Sets data for this drag source element with the given type. The data is
- * set for the client side draggable element using {@code
- * DataTransfer.setData(type, data)} method.
- * <p>
- * Note that {@code "text"} is the only cross browser supported data type.
- * Use {@link #setDataTransferText(String)} method instead if your
- * application supports IE11.
- *
- * @param type
- * Type of the data to be set for the client side draggable element,
- * e.g. {@code text/plain}. Cannot be {@code null}.
- * @param data
- * Data to be set for the client side draggable element. Cannot be
- * {@code null}.
- */
- public void setDataTransferData(String type, String data) {
- if (type == null) {
- throw new IllegalArgumentException("Data type cannot be null");
- }
-
- if (data == null) {
- throw new IllegalArgumentException("Data cannot be null");
- }
-
- if (!getState(false).types.contains(type)) {
- getState().types.add(type);
- }
- getState().data.put(type, data);
- }
-
- /**
- * Returns the data stored with type {@code type} in this drag source
- * element.
- *
- * @param type
- * Type of the requested data, e.g. {@code text/plain}.
- * @return Data of type {@code type} stored in this drag source element.
- */
- public String getDataTransferData(String type) {
- return getState(false).data.get(type);
- }
-
- /**
- * Returns the map of data stored in this drag source element. The returned
- * map preserves the order of storage and is unmodifiable.
- *
- * @return Unmodifiable copy of the map of data in the order the data was
- * stored.
- */
- public Map<String, String> getDataTransferData() {
- Map<String, String> data = getState(false).data;
-
- // Create a map of data that preserves the order of types
- LinkedHashMap<String, String> orderedData = new LinkedHashMap<>(
- data.size());
- getState(false).types
- .forEach(type -> orderedData.put(type, data.get(type)));
-
- return Collections.unmodifiableMap(orderedData);
- }
-
- /**
- * Sets data of type {@code "text"} for this drag source element. The data
- * is set for the client side draggable element using the {@code
- * DataTransfer.setData("text", data)} method.
- * <p>
- * Note that {@code "text"} is the only cross browser supported data type.
- * Use this method if your application supports IE11.
- *
- * @param data
- * Data to be set for the client side draggable element.
- * @see #setDataTransferData(String, String)
- */
- public void setDataTransferText(String data) {
- setDataTransferData(DragSourceState.DATA_TYPE_TEXT, data);
- }
-
- /**
- * Returns the data stored with type {@code "text"} in this drag source
- * element.
- *
- * @return Data of type {@code "text"} stored in this drag source element.
- */
- public String getDataTransferText() {
- return getDataTransferData(DragSourceState.DATA_TYPE_TEXT);
- }
-
- /**
- * Clears data with the given type for this drag source element when
- * present.
- *
- * @param type
- * Type of data to be cleared. Cannot be {@code null}.
- */
- public void clearDataTransferData(String type) {
- if (type == null) {
- throw new IllegalArgumentException("Data type cannot be null");
- }
-
- getState().types.remove(type);
- getState().data.remove(type);
- }
-
- /**
- * Clears all data for this drag source element.
- */
- public void clearDataTransferData() {
- getState().types.clear();
- getState().data.clear();
- }
-
- /**
- * Sets payload for this drag source to use with acceptance criterion. The
- * payload is transferred as data type in the data transfer object in the
- * following format: {@code "v-item:string:key:value"}. The given value is
- * compared to the criterion value when the drag source is dragged on top of
- * a drop target that has the suitable criterion.
- * <p>
- * Note that setting payload in Internet Explorer 11 is not possible due to
- * the browser's limitations.
- *
- * @param key
- * key of the payload to be transferred
- * @param value
- * value of the payload to be transferred
- * @see DropTargetExtension#setDropCriterion(String, String)
- */
- public void setPayload(String key, String value) {
- setPayload(key, String.valueOf(value), Payload.ValueType.STRING);
- }
-
- /**
- * Sets payload for this drag source to use with acceptance criterion. The
- * payload is transferred as data type in the data transfer object in the
- * following format: {@code "v-item:integer:key:value"}. The given value is
- * compared to the criterion value when the drag source is dragged on top of
- * a drop target that has the suitable criterion.
- * <p>
- * Note that setting payload in Internet Explorer 11 is not possible due to
- * the browser's limitations.
- *
- * @param key
- * key of the payload to be transferred
- * @param value
- * value of the payload to be transferred
- * @see DropTargetExtension#setDropCriterion(String, ComparisonOperator,
- * int)
- */
- public void setPayload(String key, int value) {
- setPayload(key, String.valueOf(value), Payload.ValueType.INTEGER);
- }
-
- /**
- * Sets payload for this drag source to use with acceptance criterion. The
- * payload is transferred as data type in the data transfer object in the
- * following format: {@code "v-item:double:key:value"}. The given value is
- * compared to the criterion value when the drag source is dragged on top of
- * a drop target that has the suitable criterion.
- * <p>
- * Note that setting payload in Internet Explorer 11 is not possible due to
- * the browser's limitations.
- *
- * @param key
- * key of the payload to be transferred
- * @param value
- * value of the payload to be transferred
- * @see DropTargetExtension#setDropCriterion(String, ComparisonOperator,
- * double)
- */
- public void setPayload(String key, double value) {
- setPayload(key, String.valueOf(value), Payload.ValueType.DOUBLE);
- }
-
- private void setPayload(String key, String value,
- Payload.ValueType valueType) {
- getState().payload.put(key, new Payload(key, value, valueType));
- }
-
- /**
- * Set server side drag data. This data is available in the drop event and
- * can be used to transfer data between drag source and drop target if they
- * are in the same UI.
- *
- * @param data
- * Data to transfer to drop event.
- */
- public void setDragData(Object data) {
- dragData = data;
- }
-
- /**
- * Get server side drag data. This data is available in the drop event and
- * can be used to transfer data between drag source and drop target if they
- * are in the same UI.
- *
- * @return Server side drag data if set, otherwise {@literal null}.
- */
- public Object getDragData() {
- return dragData;
- }
-
- /**
- * Attaches dragstart listener for the current drag source.
- * {@link DragStartListener#dragStart(DragStartEvent)} is called when
- * dragstart event happens on the client side.
- *
- * @param listener
- * Listener to handle dragstart event.
- * @return Handle to be used to remove this listener.
- */
- public Registration addDragStartListener(DragStartListener<T> listener) {
- return addListener(DragSourceState.EVENT_DRAGSTART,
- DragStartEvent.class, listener,
- DragStartListener.DRAGSTART_METHOD);
- }
-
- /**
- * Attaches dragend listener for the current drag source.
- * {@link DragEndListener#dragEnd(DragEndEvent)} is called when dragend
- * event happens on the client side.
- *
- * @param listener
- * Listener to handle dragend event.
- * @return Handle to be used to remove this listener.
- */
- public Registration addDragEndListener(DragEndListener<T> listener) {
- return addListener(DragSourceState.EVENT_DRAGEND, DragEndEvent.class,
- listener, DragEndListener.DRAGEND_METHOD);
- }
-
- /**
- * Set a custom drag image for the current drag source.
- *
- * @param imageResource
- * Resource of the image to be displayed as drag image.
- */
- public void setDragImage(Resource imageResource) {
- setResource(DragSourceState.RESOURCE_DRAG_IMAGE, imageResource);
- }
-
- @Override
- protected DragSourceState getState() {
- return (DragSourceState) super.getState();
- }
-
- @Override
- protected DragSourceState getState(boolean markAsDirty) {
- return (DragSourceState) super.getState(markAsDirty);
- }
-
- /**
- * Returns the component this extension is attached to.
- *
- * @return Extended component.
- */
- @Override
- @SuppressWarnings("unchecked")
- public T getParent() {
- return (T) super.getParent();
- }
- }
|