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 }