diff --git a/runelite-api/src/main/java/net/runelite/api/ItemComposition.java b/runelite-api/src/main/java/net/runelite/api/ItemComposition.java
index f4ee757ad6..b314cd7dac 100644
--- a/runelite-api/src/main/java/net/runelite/api/ItemComposition.java
+++ b/runelite-api/src/main/java/net/runelite/api/ItemComposition.java
@@ -81,4 +81,22 @@ public interface ItemComposition
* @return true if stackable, false otherwise
*/
boolean isStackable();
+
+ /**
+ * Returns the menu actions the item has in a players' inventory
+ *
+ * @return the inventory menu actions
+ */
+ String[] getInventoryActions();
+
+ /**
+ * Returns the menu action index of the shift-click action
+ *
+ * @return menu index of the shift-click action
+ */
+ int getShiftClickActionIndex();
+
+ void setShiftClickActionIndex(int shiftclickActionIndex);
+
+ void resetShiftClickActionIndex();
}
diff --git a/runelite-api/src/main/java/net/runelite/api/widgets/WidgetID.java b/runelite-api/src/main/java/net/runelite/api/widgets/WidgetID.java
index f46a3607bf..d1b3a34b05 100644
--- a/runelite-api/src/main/java/net/runelite/api/widgets/WidgetID.java
+++ b/runelite-api/src/main/java/net/runelite/api/widgets/WidgetID.java
@@ -207,16 +207,19 @@ public class WidgetID
static class FixedViewport
{
+ static final int INVENTORY_TAB = 51;
static final int PRAYER_TAB = 53;
}
static class ResizableViewport
{
+ static final int INVENTORY_TAB = 54;
static final int PRAYER_TAB = 56;
}
static class ResizableViewportBottomLine
{
+ static final int INVENTORY_TAB = 51;
static final int PRAYER_TAB = 53;
}
diff --git a/runelite-api/src/main/java/net/runelite/api/widgets/WidgetInfo.java b/runelite-api/src/main/java/net/runelite/api/widgets/WidgetInfo.java
index 112b63ebc5..d40c6680b5 100644
--- a/runelite-api/src/main/java/net/runelite/api/widgets/WidgetInfo.java
+++ b/runelite-api/src/main/java/net/runelite/api/widgets/WidgetInfo.java
@@ -114,11 +114,14 @@ public enum WidgetInfo
LOGIN_CLICK_TO_PLAY_SCREEN(WidgetID.LOGIN_CLICK_TO_PLAY_GROUP_ID, 0),
FIXED_VIEWPORT(WidgetID.FIXED_VIEWPORT_GROUP_ID, WidgetID.Viewport.FIXED_VIEWPORT),
+ FIXED_VIEWPORT_INVENTORY_TAB(WidgetID.FIXED_VIEWPORT_GROUP_ID, WidgetID.FixedViewport.INVENTORY_TAB),
FIXED_VIEWPORT_PRAYER_TAB(WidgetID.FIXED_VIEWPORT_GROUP_ID, WidgetID.FixedViewport.PRAYER_TAB),
MINIMAP_WIDGET(WidgetID.RESIZABLE_VIEWPORT_OLD_SCHOOL_BOX_GROUP_ID, WidgetID.Viewport.MINIMAP_WIDGET),
RESIZABLE_VIEWPORT_OLD_SCHOOL_BOX(WidgetID.RESIZABLE_VIEWPORT_OLD_SCHOOL_BOX_GROUP_ID, WidgetID.Viewport.RESIZABLE_VIEWPORT_OLD_SCHOOL_BOX),
+ RESIZABLE_VIEWPORT_INVENTORY_TAB(WidgetID.RESIZABLE_VIEWPORT_OLD_SCHOOL_BOX_GROUP_ID, WidgetID.ResizableViewport.INVENTORY_TAB),
RESIZABLE_VIEWPORT_PRAYER_TAB(WidgetID.RESIZABLE_VIEWPORT_OLD_SCHOOL_BOX_GROUP_ID, WidgetID.ResizableViewport.PRAYER_TAB),
RESIZABLE_VIEWPORT_BOTTOM_LINE(WidgetID.RESIZABLE_VIEWPORT_BOTTOM_LINE_GROUP_ID, WidgetID.Viewport.RESIZABLE_VIEWPORT_BOTTOM_LINE),
+ RESIZABLE_VIEWPORT_BOTTOM_LINE_INVENTORY_TAB(WidgetID.RESIZABLE_VIEWPORT_BOTTOM_LINE_GROUP_ID, WidgetID.ResizableViewportBottomLine.INVENTORY_TAB),
RESIZABLE_VIEWPORT_BOTTOM_LINE_PRAYER_TAB(WidgetID.RESIZABLE_VIEWPORT_BOTTOM_LINE_GROUP_ID, WidgetID.ResizableViewportBottomLine.PRAYER_TAB),
PRAYER_THICK_SKIN(WidgetID.PRAYER_GROUP_ID, WidgetID.Prayer.THICK_SKIN),
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/menuentryswapper/MenuEntrySwapperConfig.java b/runelite-client/src/main/java/net/runelite/client/plugins/menuentryswapper/MenuEntrySwapperConfig.java
index c80beeaee3..9d8436535a 100644
--- a/runelite-client/src/main/java/net/runelite/client/plugins/menuentryswapper/MenuEntrySwapperConfig.java
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/menuentryswapper/MenuEntrySwapperConfig.java
@@ -37,6 +37,17 @@ public interface MenuEntrySwapperConfig extends Config
{
@ConfigItem(
position = 0,
+ keyName = "shiftClickCustomization",
+ name = "Customizable shift-click",
+ description = "Allows customization of shift-clicks on items"
+ )
+ default boolean shiftClickCustomization()
+ {
+ return true;
+ }
+
+ @ConfigItem(
+ position = 1,
keyName = "swapPickpocket",
name = "Pickpocket",
description = "Swap Talk-to with Pickpocket on NPC
Example: Man, Woman"
@@ -47,7 +58,7 @@ public interface MenuEntrySwapperConfig extends Config
}
@ConfigItem(
- position = 1,
+ position = 2,
keyName = "swapBanker",
name = "Bank",
description = "Swap Talk-to with Bank on Bank NPC
Example: Banker"
@@ -58,7 +69,7 @@ public interface MenuEntrySwapperConfig extends Config
}
@ConfigItem(
- position = 2,
+ position = 3,
keyName = "swapExchange",
name = "Exchange",
description = "Swap Talk-to with Exchange on NPC
Example: Grand Exchange Clerk, Tool Leprechaun, Void Knight"
@@ -69,7 +80,7 @@ public interface MenuEntrySwapperConfig extends Config
}
@ConfigItem(
- position = 3,
+ position = 4,
keyName = "swapHarpoon",
name = "Harpoon",
description = "Swap Cage, Big Net with Harpoon on Fishing spot"
@@ -80,7 +91,7 @@ public interface MenuEntrySwapperConfig extends Config
}
@ConfigItem(
- position = 4,
+ position = 5,
keyName = "swapTrade",
name = "Trade",
description = "Swap Talk-to with Trade on NPC
Example: Shop keeper, Shop assistant"
@@ -91,7 +102,7 @@ public interface MenuEntrySwapperConfig extends Config
}
@ConfigItem(
- position = 5,
+ position = 6,
keyName = "swapTravel",
name = "Travel",
description = "Swap Talk-to with Travel, Take-boat, Pay-fare, Charter on NPC
Example: Squire, Monk of Entrana, Customs officer, Trader Crewmember"
@@ -102,7 +113,7 @@ public interface MenuEntrySwapperConfig extends Config
}
@ConfigItem(
- position = 6,
+ position = 7,
keyName = "swapPay",
name = "Pay",
description = "Swap Talk-to with Pay on NPC
Example: Elstan, Heskel, Fayeth"
@@ -113,7 +124,7 @@ public interface MenuEntrySwapperConfig extends Config
}
@ConfigItem(
- position = 7,
+ position = 8,
keyName = "swapHome",
name = "Home",
description = "Swap Enter with Home on Portal"
@@ -124,7 +135,7 @@ public interface MenuEntrySwapperConfig extends Config
}
@ConfigItem(
- position = 8,
+ position = 9,
keyName = "swapLastDestination",
name = "Last-destination (XXX)",
description = "Swap Zanaris with Last-destination on Fairy ring"
@@ -135,7 +146,7 @@ public interface MenuEntrySwapperConfig extends Config
}
@ConfigItem(
- position = 9,
+ position = 10,
keyName = "swapBoxTrap",
name = "Reset",
description = "Swap Check with Reset on box trap"
@@ -146,7 +157,7 @@ public interface MenuEntrySwapperConfig extends Config
}
@ConfigItem(
- position = 10,
+ position = 11,
keyName = "swapCatacombEntrance",
name = "Catacomb entrance",
description = "Swap Read with Investigate on Catacombs of Kourend entrance"
@@ -157,7 +168,7 @@ public interface MenuEntrySwapperConfig extends Config
}
@ConfigItem(
- position = 11,
+ position = 12,
keyName = "swapTeleportItem",
name = "Teleport item",
description = "Swap Wear, Wield with Rub, Teleport on teleport item
Example: Amulet of glory, Ardougne cloak, Chronicle"
@@ -168,7 +179,7 @@ public interface MenuEntrySwapperConfig extends Config
}
@ConfigItem(
- position = 12,
+ position = 13,
keyName = "swapSilverSickle",
name = "Silver sickle(b)",
description = "Swap Wield with Cast Bloom on Silver sickle(b)"
@@ -179,7 +190,7 @@ public interface MenuEntrySwapperConfig extends Config
}
@ConfigItem(
- position = 13,
+ position = 14,
keyName = "swapBones",
name = "Bury",
description = "Swap Bury with Use on Bones"
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/menuentryswapper/MenuEntrySwapperPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/menuentryswapper/MenuEntrySwapperPlugin.java
index fc30693030..9701049eb6 100644
--- a/runelite-client/src/main/java/net/runelite/client/plugins/menuentryswapper/MenuEntrySwapperPlugin.java
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/menuentryswapper/MenuEntrySwapperPlugin.java
@@ -1,5 +1,6 @@
/*
* Copyright (c) 2018, Adam
+ * Copyright (c) 2018, Kamiel
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -26,14 +27,32 @@ package net.runelite.client.plugins.menuentryswapper;
import com.google.common.eventbus.Subscribe;
import com.google.inject.Provides;
+import java.awt.event.MouseEvent;
+import java.util.Collection;
+import java.util.Collections;
import javax.inject.Inject;
+import lombok.Getter;
+import lombok.Setter;
import net.runelite.api.Client;
import net.runelite.api.GameState;
+import net.runelite.api.ItemComposition;
import net.runelite.api.MenuEntry;
+import net.runelite.api.events.ConfigChanged;
import net.runelite.api.events.MenuEntryAdded;
+import net.runelite.api.events.PostItemComposition;
+import net.runelite.api.events.WidgetMenuOptionClicked;
+import net.runelite.api.widgets.WidgetInfo;
+import net.runelite.api.widgets.WidgetItem;
import net.runelite.client.config.ConfigManager;
+import net.runelite.client.game.ItemManager;
+import net.runelite.client.input.KeyManager;
+import net.runelite.client.input.MouseManager;
+import net.runelite.client.menus.MenuManager;
+import net.runelite.client.menus.WidgetMenuOption;
import net.runelite.client.plugins.Plugin;
import net.runelite.client.plugins.PluginDescriptor;
+import net.runelite.client.ui.overlay.Overlay;
+import net.runelite.client.util.Text;
@PluginDescriptor(
name = "Menu Entry Swapper",
@@ -41,18 +60,163 @@ import net.runelite.client.plugins.PluginDescriptor;
)
public class MenuEntrySwapperPlugin extends Plugin
{
+ private static final String CONFIGURE = "Configure";
+ private static final String SAVE = "Save";
+ private static final String MENU_TARGET = "Shift-click";
+
+ private static final String CONFIG_GROUP = "shiftclick";
+ private static final String ITEM_KEY_PREFIX = "item_";
+
+ private static final WidgetMenuOption FIXED_INVENTORY_TAB_CONFIGURE = new WidgetMenuOption(CONFIGURE,
+ MENU_TARGET, WidgetInfo.FIXED_VIEWPORT_INVENTORY_TAB);
+
+ private static final WidgetMenuOption FIXED_INVENTORY_TAB_SAVE = new WidgetMenuOption(SAVE,
+ MENU_TARGET, WidgetInfo.FIXED_VIEWPORT_INVENTORY_TAB);
+
+ private static final WidgetMenuOption RESIZABLE_INVENTORY_TAB_CONFIGURE = new WidgetMenuOption(CONFIGURE,
+ MENU_TARGET, WidgetInfo.RESIZABLE_VIEWPORT_INVENTORY_TAB);
+
+ private static final WidgetMenuOption RESIZABLE_INVENTORY_TAB_SAVE = new WidgetMenuOption(SAVE,
+ MENU_TARGET, WidgetInfo.RESIZABLE_VIEWPORT_INVENTORY_TAB);
+
+ private static final WidgetMenuOption RESIZABLE_BOTTOM_LINE_INVENTORY_TAB_CONFIGURE = new WidgetMenuOption(CONFIGURE,
+ MENU_TARGET, WidgetInfo.RESIZABLE_VIEWPORT_BOTTOM_LINE_INVENTORY_TAB);
+
+ private static final WidgetMenuOption RESIZABLE_BOTTOM_LINE_INVENTORY_TAB_SAVE = new WidgetMenuOption(SAVE,
+ MENU_TARGET, WidgetInfo.RESIZABLE_VIEWPORT_BOTTOM_LINE_INVENTORY_TAB);
+
@Inject
private Client client;
@Inject
private MenuEntrySwapperConfig config;
+ @Inject
+ private ShiftClickConfigurationOverlay overlay;
+
+ @Inject
+ private ShiftClickInputListener inputListener;
+
+ @Inject
+ private ConfigManager configManager;
+
+ @Inject
+ private ItemManager itemManager;
+
+ @Inject
+ private MouseManager mouseManager;
+
+ @Inject
+ private KeyManager keyManager;
+
+ @Inject
+ private MenuManager menuManager;
+
+ @Getter
+ private boolean configuringShiftClick = false;
+
+ @Setter
+ private boolean shiftModifier = false;
+
@Provides
MenuEntrySwapperConfig provideConfig(ConfigManager configManager)
{
return configManager.getConfig(MenuEntrySwapperConfig.class);
}
+ @Override
+ public Overlay getOverlay()
+ {
+ return overlay;
+ }
+
+ @Override
+ public void startUp()
+ {
+ if (config.shiftClickCustomization())
+ {
+ enableCustomization();
+ }
+ }
+
+ @Override
+ public void shutDown()
+ {
+ disableCustomization();
+ }
+
+ @Subscribe
+ public void onConfigChanged(ConfigChanged event)
+ {
+ if (event.getKey().equals("shiftClickCustomization"))
+ {
+ if (config.shiftClickCustomization())
+ {
+ enableCustomization();
+ }
+ else
+ {
+ disableCustomization();
+ }
+ }
+ }
+
+ private Integer getSwapConfig(int itemId)
+ {
+ String config = configManager.getConfiguration(CONFIG_GROUP, ITEM_KEY_PREFIX + itemId);
+ if (config == null || config.isEmpty())
+ {
+ return null;
+ }
+
+ return Integer.parseInt(config);
+ }
+
+ private void setSwapConfig(int itemId, int index)
+ {
+ configManager.setConfiguration(CONFIG_GROUP, ITEM_KEY_PREFIX + itemId, index);
+ }
+
+ private void unsetSwapConfig(int itemId)
+ {
+ configManager.unsetConfiguration(CONFIG_GROUP, ITEM_KEY_PREFIX + itemId);
+ }
+
+ private void enableCustomization()
+ {
+ keyManager.registerKeyListener(inputListener);
+ refreshShiftClickCustomizationMenus();
+ }
+
+ private void disableCustomization()
+ {
+ keyManager.unregisterKeyListener(inputListener);
+ mouseManager.unregisterMouseListener(inputListener);
+ removeShiftClickCustomizationMenus();
+ configuringShiftClick = false;
+ }
+
+ @Subscribe
+ public void onWidgetMenuOptionClicked(WidgetMenuOptionClicked event)
+ {
+ if (event.getWidget() == WidgetInfo.FIXED_VIEWPORT_INVENTORY_TAB
+ || event.getWidget() == WidgetInfo.RESIZABLE_VIEWPORT_INVENTORY_TAB
+ || event.getWidget() == WidgetInfo.RESIZABLE_VIEWPORT_BOTTOM_LINE_INVENTORY_TAB)
+ {
+ configuringShiftClick = event.getMenuOption().equals(CONFIGURE);
+ refreshShiftClickCustomizationMenus();
+
+ if (configuringShiftClick)
+ {
+ mouseManager.registerMouseListener(inputListener);
+ }
+ else
+ {
+ mouseManager.unregisterMouseListener(inputListener);
+ }
+ }
+ }
+
@Subscribe
public void onMenuEntryAdded(MenuEntryAdded event)
{
@@ -61,8 +225,9 @@ public class MenuEntrySwapperPlugin extends Plugin
return;
}
- String option = event.getOption().toLowerCase();
- String target = event.getTarget();
+ int itemId = event.getIdentifier();
+ String option = Text.removeTags(event.getOption()).toLowerCase();
+ String target = Text.removeTags(event.getTarget()).toLowerCase();
if (option.equals("talk-to"))
{
@@ -144,6 +309,31 @@ public class MenuEntrySwapperPlugin extends Plugin
{
swap("use", option, target, true);
}
+ else if (config.shiftClickCustomization() && shiftModifier && !option.equals("use"))
+ {
+ Integer customOption = getSwapConfig(itemId);
+
+ if (customOption != null && customOption == -1)
+ {
+ swap("use", option, target, true);
+ }
+ }
+ }
+
+ @Subscribe
+ public void onPostItemComposition(PostItemComposition event)
+ {
+ ItemComposition itemComposition = event.getItemComposition();
+ Integer option = getSwapConfig(itemComposition.getId());
+
+ if (option != null)
+ {
+ itemComposition.setShiftClickActionIndex(option);
+
+ // Update our cached item composition too
+ ItemComposition ourItemComposition = itemManager.getItemComposition(itemComposition.getId());
+ ourItemComposition.setShiftClickActionIndex(option);
+ }
}
private int searchIndex(MenuEntry[] entries, String option, String target, boolean strict)
@@ -151,21 +341,25 @@ public class MenuEntrySwapperPlugin extends Plugin
for (int i = entries.length - 1; i >= 0; i--)
{
MenuEntry entry = entries[i];
+ String entryOption = Text.removeTags(entry.getOption()).toLowerCase();
+ String entryTarget = Text.removeTags(entry.getTarget()).toLowerCase();
+
if (strict)
{
- if (entry.getOption().toLowerCase().equals(option) && entry.getTarget().equals(target))
+ if (entryOption.equals(option) && entryTarget.equals(target))
{
return i;
}
}
else
{
- if (entry.getOption().toLowerCase().contains(option) && entry.getTarget().equals(target))
+ if (entryOption.contains(option.toLowerCase()) && entryTarget.equals(target))
{
return i;
}
}
}
+
return -1;
}
@@ -185,4 +379,81 @@ public class MenuEntrySwapperPlugin extends Plugin
client.setMenuEntries(entries);
}
}
+
+ public void cycleItemShiftClickAction(WidgetItem item, int mouseClick)
+ {
+ ItemComposition itemComposition = client.getItemDefinition(item.getId());
+
+ if (mouseClick == MouseEvent.BUTTON1)
+ {
+ String[] actions = itemComposition.getInventoryActions();
+ int shiftClickActionIndex = itemComposition.getShiftClickActionIndex();
+ int nextActionIndex = getNextActionIndex(actions, shiftClickActionIndex);
+ setSwapConfig(item.getId(), nextActionIndex);
+ itemComposition.setShiftClickActionIndex(nextActionIndex);
+ }
+ else if (mouseClick == MouseEvent.BUTTON3)
+ {
+ unsetSwapConfig(item.getId());
+ itemComposition.resetShiftClickActionIndex();
+ }
+ }
+
+ private int getNextActionIndex(String[] actions, int currentIndex)
+ {
+ int size = actions.length;
+ int index = currentIndex + 1;
+
+ for (int i = index; i < index + size; i++)
+ {
+ if (i == size)
+ {
+ // -1 is used for "Use" which is not in actions
+ return -1;
+ }
+
+ index = i % size;
+
+ if (actions[index] == null)
+ {
+ continue;
+ }
+
+ break;
+ }
+
+ return index;
+ }
+
+ private void removeShiftClickCustomizationMenus()
+ {
+ menuManager.removeManagedCustomMenu(FIXED_INVENTORY_TAB_CONFIGURE);
+ menuManager.removeManagedCustomMenu(FIXED_INVENTORY_TAB_SAVE);
+ menuManager.removeManagedCustomMenu(RESIZABLE_BOTTOM_LINE_INVENTORY_TAB_CONFIGURE);
+ menuManager.removeManagedCustomMenu(RESIZABLE_BOTTOM_LINE_INVENTORY_TAB_SAVE);
+ menuManager.removeManagedCustomMenu(RESIZABLE_INVENTORY_TAB_CONFIGURE);
+ menuManager.removeManagedCustomMenu(RESIZABLE_INVENTORY_TAB_SAVE);
+ }
+
+ private void refreshShiftClickCustomizationMenus()
+ {
+ removeShiftClickCustomizationMenus();
+ if (configuringShiftClick)
+ {
+ menuManager.addManagedCustomMenu(FIXED_INVENTORY_TAB_SAVE);
+ menuManager.addManagedCustomMenu(RESIZABLE_BOTTOM_LINE_INVENTORY_TAB_SAVE);
+ menuManager.addManagedCustomMenu(RESIZABLE_INVENTORY_TAB_SAVE);
+ }
+ else
+ {
+ menuManager.addManagedCustomMenu(FIXED_INVENTORY_TAB_CONFIGURE);
+ menuManager.addManagedCustomMenu(RESIZABLE_BOTTOM_LINE_INVENTORY_TAB_CONFIGURE);
+ menuManager.addManagedCustomMenu(RESIZABLE_INVENTORY_TAB_CONFIGURE);
+ }
+ }
+
+ Collection getInventoryItems()
+ {
+ return Collections.unmodifiableCollection(client.getWidget(WidgetInfo.INVENTORY).getWidgetItems());
+ }
}
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/menuentryswapper/ShiftClickConfigurationOverlay.java b/runelite-client/src/main/java/net/runelite/client/plugins/menuentryswapper/ShiftClickConfigurationOverlay.java
new file mode 100644
index 0000000000..ec9db0e938
--- /dev/null
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/menuentryswapper/ShiftClickConfigurationOverlay.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright (c) 2018, Kamiel
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package net.runelite.client.plugins.menuentryswapper;
+
+import java.awt.Dimension;
+import java.awt.Font;
+import java.awt.Graphics2D;
+import java.awt.Point;
+import java.awt.Rectangle;
+import javax.inject.Inject;
+import net.runelite.api.Client;
+import net.runelite.api.ItemComposition;
+import net.runelite.api.widgets.WidgetInfo;
+import net.runelite.api.widgets.WidgetItem;
+import net.runelite.client.ui.FontManager;
+import net.runelite.client.ui.overlay.Overlay;
+import net.runelite.client.ui.overlay.OverlayLayer;
+import net.runelite.client.ui.overlay.OverlayPosition;
+import net.runelite.client.ui.overlay.components.TextComponent;
+
+public class ShiftClickConfigurationOverlay extends Overlay
+{
+ @Inject
+ private Client client;
+
+ @Inject
+ private MenuEntrySwapperPlugin plugin;
+
+ @Inject
+ public ShiftClickConfigurationOverlay()
+ {
+ setPosition(OverlayPosition.DYNAMIC);
+ setLayer(OverlayLayer.ABOVE_WIDGETS);
+ }
+
+ @Override
+ public Dimension render(Graphics2D graphics)
+ {
+ if (!plugin.isConfiguringShiftClick() || client.isMenuOpen() || client.getWidget(WidgetInfo.INVENTORY).isHidden())
+ {
+ return null;
+ }
+
+ Font font = FontManager.getRunescapeSmallFont();
+ graphics.setFont(font);
+
+ net.runelite.api.Point mouseCanvasPosition = client.getMouseCanvasPosition();
+ Point mousePoint = new Point(mouseCanvasPosition.getX(), mouseCanvasPosition.getY());
+
+ for (WidgetItem item : plugin.getInventoryItems())
+ {
+ final Rectangle bounds = item.getCanvasBounds();
+
+ if (!bounds.contains(mousePoint))
+ {
+ continue;
+ }
+
+ ItemComposition itemComposition = client.getItemDefinition(item.getId());
+ String[] actions = itemComposition.getInventoryActions();
+ int index = itemComposition.getShiftClickActionIndex();
+
+ if (index >= 0 && actions[index] == null)
+ {
+ continue;
+ }
+
+ String action = index == -1 ? "Use" : actions[index];
+ int textWidth = graphics.getFontMetrics().stringWidth(action);
+ int textLocationX = (int) (bounds.x + bounds.getWidth() / 2 - textWidth / 2);
+ int textLocationY = bounds.y + 28;
+ final TextComponent textComponent = new TextComponent();
+ textComponent.setPosition(new Point(textLocationX, textLocationY));
+ textComponent.setText(action);
+ textComponent.render(graphics);
+ }
+
+ return null;
+ }
+}
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/menuentryswapper/ShiftClickInputListener.java b/runelite-client/src/main/java/net/runelite/client/plugins/menuentryswapper/ShiftClickInputListener.java
new file mode 100644
index 0000000000..dddf78b696
--- /dev/null
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/menuentryswapper/ShiftClickInputListener.java
@@ -0,0 +1,127 @@
+/*
+ * Copyright (c) 2018, Kamiel
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package net.runelite.client.plugins.menuentryswapper;
+
+import java.awt.Point;
+import java.awt.event.KeyEvent;
+import java.awt.event.MouseEvent;
+import javax.inject.Inject;
+import net.runelite.api.Client;
+import net.runelite.api.widgets.Widget;
+import net.runelite.api.widgets.WidgetInfo;
+import net.runelite.api.widgets.WidgetItem;
+import net.runelite.client.callback.ClientThread;
+import net.runelite.client.input.KeyListener;
+import net.runelite.client.input.MouseListener;
+
+public class ShiftClickInputListener extends MouseListener implements KeyListener
+{
+ @Inject
+ private ClientThread clientThread;
+
+ @Inject
+ private Client client;
+
+ @Inject
+ private MenuEntrySwapperPlugin plugin;
+
+ @Inject
+ private MenuEntrySwapperConfig config;
+
+ private WidgetItem getClickedItem(Point point)
+ {
+ for (WidgetItem item : plugin.getInventoryItems())
+ {
+ if (item.getCanvasBounds().contains(point))
+ {
+ return item;
+ }
+ }
+
+ return null;
+ }
+
+ @Override
+ public MouseEvent mouseClicked(MouseEvent event)
+ {
+ if (!config.shiftClickCustomization() || !isValidInventoryClick(event.getPoint()))
+ {
+ return event;
+ }
+
+ final WidgetItem item = getClickedItem(event.getPoint());
+ final int button = event.getButton();
+
+ if (item != null)
+ {
+ clientThread.invokeLater(() -> plugin.cycleItemShiftClickAction(item, button));
+ event.consume();
+ }
+
+ return event;
+ }
+
+ @Override
+ public MouseEvent mousePressed(MouseEvent event)
+ {
+ if (!config.shiftClickCustomization() || !isValidInventoryClick(event.getPoint()))
+ {
+ return event;
+ }
+
+ event.consume();
+ return event;
+ }
+
+ private boolean isValidInventoryClick(Point point)
+ {
+ Widget widget = client.getWidget(WidgetInfo.INVENTORY).getParent();
+ return !widget.isHidden() && !client.isMenuOpen() && widget.getBounds().contains(point);
+ }
+
+ @Override
+ public void keyTyped(KeyEvent event)
+ {
+
+ }
+
+ @Override
+ public void keyPressed(KeyEvent event)
+ {
+ if (event.getKeyCode() == KeyEvent.VK_SHIFT)
+ {
+ plugin.setShiftModifier(true);
+ }
+ }
+
+ @Override
+ public void keyReleased(KeyEvent event)
+ {
+ if (event.getKeyCode() == KeyEvent.VK_SHIFT)
+ {
+ plugin.setShiftModifier(false);
+ }
+ }
+}
diff --git a/runelite-mixins/src/main/java/net/runelite/mixins/RSItemCompositionMixin.java b/runelite-mixins/src/main/java/net/runelite/mixins/RSItemCompositionMixin.java
index 766fb30735..6d8a7a6d61 100644
--- a/runelite-mixins/src/main/java/net/runelite/mixins/RSItemCompositionMixin.java
+++ b/runelite-mixins/src/main/java/net/runelite/mixins/RSItemCompositionMixin.java
@@ -25,17 +25,52 @@
package net.runelite.mixins;
+import net.runelite.api.mixins.Copy;
import net.runelite.api.mixins.Inject;
import net.runelite.api.mixins.Mixin;
+import net.runelite.api.mixins.Replace;
import net.runelite.rs.api.RSItemComposition;
@Mixin(RSItemComposition.class)
public abstract class RSItemCompositionMixin implements RSItemComposition
{
+ private static final int DEFAULT_CUSTOM_SHIFT_CLICK_INDEX = -2;
+
+ @Inject
+ private int shiftClickActionIndex = DEFAULT_CUSTOM_SHIFT_CLICK_INDEX;
+
+ @Inject
+ RSItemCompositionMixin()
+ {
+ }
+
@Inject
@Override
public boolean isStackable()
{
return getIsStackable() != 0;
}
+
+ @Inject
+ @Override
+ public void setShiftClickActionIndex(int shiftClickActionIndex)
+ {
+ this.shiftClickActionIndex = shiftClickActionIndex;
+ }
+
+ @Copy("getShiftClickActionIndex")
+ abstract int rs$getShiftClickActionIndex();
+
+ @Replace("getShiftClickActionIndex")
+ public int getShiftClickActionIndex()
+ {
+ return shiftClickActionIndex == DEFAULT_CUSTOM_SHIFT_CLICK_INDEX ? rs$getShiftClickActionIndex() : shiftClickActionIndex;
+ }
+
+ @Inject
+ @Override
+ public void resetShiftClickActionIndex()
+ {
+ shiftClickActionIndex = DEFAULT_CUSTOM_SHIFT_CLICK_INDEX;
+ }
}
diff --git a/runescape-api/src/main/java/net/runelite/rs/api/RSItemComposition.java b/runescape-api/src/main/java/net/runelite/rs/api/RSItemComposition.java
index 7a738f671f..8d48957b1a 100644
--- a/runescape-api/src/main/java/net/runelite/rs/api/RSItemComposition.java
+++ b/runescape-api/src/main/java/net/runelite/rs/api/RSItemComposition.java
@@ -69,4 +69,12 @@ public interface RSItemComposition extends ItemComposition
@Import("maleModel")
int getMaleModel();
+
+ @Import("inventoryActions")
+ @Override
+ String[] getInventoryActions();
+
+ @Import("getShiftClickActionIndex")
+ @Override
+ int getShiftClickActionIndex();
}