001    package org.bukkit.event.inventory;
002    
003    import org.bukkit.inventory.Inventory;
004    import org.bukkit.inventory.InventoryView;
005    import org.bukkit.entity.HumanEntity;
006    import org.bukkit.entity.Player;
007    import org.bukkit.event.HandlerList;
008    import org.bukkit.event.inventory.InventoryType.SlotType;
009    import org.bukkit.Location;
010    import org.bukkit.inventory.ItemStack;
011    import org.bukkit.scheduler.BukkitScheduler;
012    import org.bukkit.plugin.Plugin;
013    
014    /**
015     * This event is called when a player clicks a slot in an inventory.
016     * <p>
017     * Because InventoryClickEvent occurs within a modification of the Inventory,
018     * not all Inventory related methods are safe to use.
019     * <p>
020     * The following should never be invoked by an EventHandler for
021     * InventoryClickEvent using the HumanEntity or InventoryView associated with
022     * this event:
023     * <ul>
024     * <li>{@link HumanEntity#closeInventory()}
025     * <li>{@link HumanEntity#openInventory(Inventory)}
026     * <li>{@link HumanEntity#openWorkbench(Location, boolean)}
027     * <li>{@link HumanEntity#openEnchanting(Location, boolean)}
028     * <li>{@link InventoryView#close()}
029     * </ul>
030     * To invoke one of these methods, schedule a task using 
031     * {@link BukkitScheduler#runTask(Plugin, Runnable)}, which will run the task
032     * on the next tick. Also be aware that this is not an exhaustive list, and
033     * other methods could potentially create issues as well.
034     * <p>
035     * Assuming the EntityHuman associated with this event is an instance of a
036     * Player, manipulating the MaxStackSize or contents of an Inventory will
037     * require an Invocation of {@link Player#updateInventory()}.
038     * <p>
039     * Modifications to slots that are modified by the results of this
040     * InventoryClickEvent can be overwritten. To change these slots, this event
041     * should be cancelled and all desired changes to the inventory applied.
042     * Alternatively, scheduling a task using {@link BukkitScheduler#runTask(
043     * Plugin, Runnable)}, which would execute the task on the next tick, would
044     * work as well.
045     */
046    public class InventoryClickEvent extends InventoryInteractEvent {
047        private static final HandlerList handlers = new HandlerList();
048        private final ClickType click;
049        private final InventoryAction action;
050        private SlotType slot_type;
051        private int whichSlot;
052        private int rawSlot;
053        private ItemStack current = null;
054        private int hotbarKey = -1;
055    
056        @Deprecated
057        public InventoryClickEvent(InventoryView view, SlotType type, int slot, boolean right, boolean shift) {
058            this(view, type, slot, right ? (shift ? ClickType.SHIFT_RIGHT : ClickType.RIGHT) : (shift ? ClickType.SHIFT_LEFT : ClickType.LEFT), InventoryAction.SWAP_WITH_CURSOR);
059        }
060    
061        public InventoryClickEvent(InventoryView view, SlotType type, int slot, ClickType click, InventoryAction action) {
062            super(view);
063            this.slot_type = type;
064            this.rawSlot = slot;
065            this.whichSlot = view.convertSlot(slot);
066            this.click = click;
067            this.action = action;
068        }
069    
070        public InventoryClickEvent(InventoryView view, SlotType type, int slot, ClickType click, InventoryAction action, int key) {
071            this(view, type, slot, click, action);
072            this.hotbarKey = key;
073        }
074    
075        /**
076         * Gets the type of slot that was clicked.
077         *
078         * @return the slot type
079         */
080        public SlotType getSlotType() {
081            return slot_type;
082        }
083    
084        /**
085         * Gets the current ItemStack on the cursor.
086         *
087         * @return the cursor ItemStack
088         */
089        public ItemStack getCursor() {
090            return getView().getCursor();
091        }
092    
093        /**
094         * Gets the ItemStack currently in the clicked slot.
095         *
096         * @return the item in the clicked
097         */
098        public ItemStack getCurrentItem() {
099            if (slot_type == SlotType.OUTSIDE) {
100                return current;
101            }
102            return getView().getItem(rawSlot);
103        }
104    
105        /**
106         * Gets whether or not the ClickType for this event represents a right
107         * click.
108         *
109         * @return true if the ClickType uses the right mouse button.
110         * @see ClickType#isRightClick()
111         */
112        public boolean isRightClick() {
113            return click.isRightClick();
114        }
115    
116        /**
117         * Gets whether or not the ClickType for this event represents a left
118         * click.
119         *
120         * @return true if the ClickType uses the left mouse button.
121         * @see ClickType#isLeftClick()
122         */
123        public boolean isLeftClick() {
124            return click.isLeftClick();
125        }
126    
127        /**
128         * Gets whether the ClickType for this event indicates that the key was
129         * pressed down when the click was made.
130         *
131         * @return true if the ClickType uses Shift or Ctrl.
132         * @see ClickType#isShiftClick()
133         */
134        public boolean isShiftClick() {
135            return click.isShiftClick();
136        }
137    
138        /**
139         * Sets the item on the cursor.
140         *
141         * @param stack the new cursor item
142         * @deprecated This changes the ItemStack in their hand before any
143         *     calculations are applied to the Inventory, which has a tendency to
144         *     create inconsistencies between the Player and the server, and to
145         *     make unexpected changes in the behavior of the clicked Inventory.
146         */
147        @Deprecated
148        public void setCursor(ItemStack stack) {
149            getView().setCursor(stack);
150        }
151    
152        /**
153         * Sets the ItemStack currently in the clicked slot.
154         *
155         * @param stack the item to be placed in the current slot
156         */
157        public void setCurrentItem(ItemStack stack) {
158            if (slot_type == SlotType.OUTSIDE) {
159                current = stack;
160            } else {
161                getView().setItem(rawSlot, stack);
162            }
163        }
164    
165        /**
166         * The slot number that was clicked, ready for passing to
167         * {@link Inventory#getItem(int)}. Note that there may be two slots with
168         * the same slot number, since a view links two different inventories.
169         *
170         * @return The slot number.
171         */
172        public int getSlot() {
173            return whichSlot;
174        }
175    
176        /**
177         * The raw slot number clicked, ready for passing to {@link InventoryView
178         * #getItem(int)} This slot number is unique for the view.
179         *
180         * @return the slot number
181         */
182        public int getRawSlot() {
183            return rawSlot;
184        }
185    
186        /**
187         * If the ClickType is NUMBER_KEY, this method will return the index of
188         * the pressed key (0-8).
189         *
190         * @return the number on the key minus 1 (range 0-8); or -1 if not
191         *     a NUMBER_KEY action
192         */
193        public int getHotbarButton() {
194            return hotbarKey;
195        }
196    
197        /**
198         * Gets the InventoryAction that triggered this event.
199         * <p>
200         * This action cannot be changed, and represents what the normal outcome
201         * of the event will be. To change the behavior of this
202         * InventoryClickEvent, changes must be manually applied.
203         *
204         * @return the InventoryAction that triggered this event.
205         */
206        public InventoryAction getAction() {
207            return action;
208        }
209    
210        /**
211         * Gets the ClickType for this event.
212         * <p>
213         * This is insulated against changes to the inventory by other plugins.
214         *
215         * @return the type of inventory click
216         */
217        public ClickType getClick() {
218            return click;
219        }
220    
221        @Override
222        public HandlerList getHandlers() {
223            return handlers;
224        }
225    
226        public static HandlerList getHandlerList() {
227            return handlers;
228        }
229    }