001    package org.bukkit.event.inventory;
002    
003    import java.util.Collections;
004    import java.util.Map;
005    import java.util.Set;
006    
007    import org.apache.commons.lang.Validate;
008    import org.bukkit.Location;
009    import org.bukkit.entity.HumanEntity;
010    import org.bukkit.entity.Player;
011    import org.bukkit.event.HandlerList;
012    import org.bukkit.inventory.Inventory;
013    import org.bukkit.inventory.InventoryView;
014    import org.bukkit.inventory.ItemStack;
015    import org.bukkit.plugin.Plugin;
016    import org.bukkit.scheduler.BukkitScheduler;
017    
018    import com.google.common.collect.ImmutableSet;
019    
020    /**
021     * This event is called when the player drags an item in their cursor across
022     * the inventory. The ItemStack is distributed across the slots the
023     * HumanEntity dragged over. The method of distribution is described by the 
024     * DragType returned by {@link #getType()}.
025     * <p>
026     * Canceling this event will result in none of the changes described in
027     * {@link #getNewItems()} being applied to the Inventory.
028     * <p>
029     * Because InventoryDragEvent occurs within a modification of the Inventory,
030     * not all Inventory related methods are safe to use.
031     * <p>
032     * The following should never be invoked by an EventHandler for
033     * InventoryDragEvent using the HumanEntity or InventoryView associated with
034     * this event.
035     * <ul>
036     * <li>{@link HumanEntity#closeInventory()}
037     * <li>{@link HumanEntity#openInventory(Inventory)}
038     * <li>{@link HumanEntity#openWorkbench(Location, boolean)}
039     * <li>{@link HumanEntity#openEnchanting(Location, boolean)}
040     * <li>{@link InventoryView#close()}
041     * </ul>
042     * To invoke one of these methods, schedule a task using 
043     * {@link BukkitScheduler#runTask(Plugin, Runnable)}, which will run the task
044     * on the next tick.  Also be aware that this is not an exhaustive list, and
045     * other methods could potentially create issues as well.
046     * <p>
047     * Assuming the EntityHuman associated with this event is an instance of a
048     * Player, manipulating the MaxStackSize or contents of an Inventory will
049     * require an Invocation of {@link Player#updateInventory()}.
050     * <p>
051     * Any modifications to slots that are modified by the results of this
052     * InventoryDragEvent will be overwritten. To change these slots, this event
053     * should be cancelled and the changes applied. Alternatively, scheduling a
054     * task using {@link BukkitScheduler#runTask(Plugin, Runnable)}, which would
055     * execute the task on the next tick, would work as well.
056     */
057    public class InventoryDragEvent extends InventoryInteractEvent {
058        private static final HandlerList handlers = new HandlerList();
059        private final DragType type;
060        private final Map<Integer, ItemStack> addedItems;
061        private final Set<Integer> containerSlots;
062        private final ItemStack oldCursor;
063        private ItemStack newCursor;
064    
065        public InventoryDragEvent(InventoryView what, ItemStack newCursor, ItemStack oldCursor, boolean right, Map<Integer, ItemStack> slots) {
066            super(what);
067    
068            Validate.notNull(oldCursor);
069            Validate.notNull(slots);
070    
071            type = right ? DragType.SINGLE : DragType.EVEN;
072            this.newCursor = newCursor;
073            this.oldCursor = oldCursor;
074            this.addedItems = slots;
075            ImmutableSet.Builder<Integer> b = ImmutableSet.builder();
076            for (Integer slot : slots.keySet()) {
077                b.add(what.convertSlot(slot));
078            }
079            this.containerSlots = b.build();
080        }
081    
082        /**
083         * Gets all items to be added to the inventory in this drag.
084         *
085         * @return map from raw slot id to new ItemStack
086         */
087        public Map<Integer, ItemStack> getNewItems() {
088            return Collections.unmodifiableMap(addedItems);
089        }
090    
091        /**
092         * Gets the raw slot ids to be changed in this drag.
093         *
094         * @return list of raw slot ids, suitable for getView().getItem(int)
095         */
096        public Set<Integer> getRawSlots() {
097            return addedItems.keySet();
098        }
099    
100        /**
101         * Gets the slots to be changed in this drag.
102         *
103         * @return list of converted slot ids, suitable for {@link
104         *     org.bukkit.inventory.Inventory#getItem(int)}.
105         */
106        public Set<Integer> getInventorySlots() {
107            return containerSlots;
108        }
109    
110        /**
111         * Gets the result cursor after the drag is done. The returned value is
112         * mutable.
113         *
114         * @return the result cursor
115         */
116        public ItemStack getCursor() {
117            return newCursor;
118        }
119    
120        /**
121         * Sets the result cursor after the drag is done.
122         * <p>
123         * Changing this item stack changes the cursor item. Note that changing
124         * the affected "dragged" slots does not change this ItemStack, nor does
125         * changing this ItemStack affect the "dragged" slots.
126         *
127         * @param newCursor the new cursor ItemStack
128         */
129        public void setCursor(ItemStack newCursor) {
130            this.newCursor = newCursor;
131        }
132    
133        /**
134         * Gets an ItemStack representing the cursor prior to any modifications
135         * as a result of this drag.
136         *
137         * @return the original cursor
138         */
139        public ItemStack getOldCursor() {
140            return oldCursor.clone();
141        }
142    
143        /**
144         * Gets the DragType that describes the behavior of ItemStacks placed
145         * after this InventoryDragEvent.
146         * <p>
147         * The ItemStacks and the raw slots that they're being applied to can be
148         * found using {@link #getNewItems()}.
149         *
150         * @return the DragType of this InventoryDragEvent
151         */
152        public DragType getType() {
153            return type;
154        }
155    
156        @Override
157        public HandlerList getHandlers() {
158            return handlers;
159        }
160    
161        public static HandlerList getHandlerList() {
162            return handlers;
163        }
164    }