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 }