From c7a0699a4186df98080e3fb9876b2e18ceffd66a Mon Sep 17 00:00:00 2001 From: Toocanzs Date: Thu, 16 Nov 2017 20:11:19 -0500 Subject: [PATCH] menu manager: add custom widget menu capability --- .../net/runelite/api/widgets/WidgetInfo.java | 7 ++ .../net/runelite/client/callback/Hooks.java | 18 ++- .../client/events/MenuEntryAdded.java | 75 +++++++++++++ .../client/events/MenuOptionClicked.java | 11 ++ .../events/WidgetMenuOptionClicked.java | 64 +++++++++++ .../runelite/client/menus/MenuManager.java | 90 +++++++++++++++ .../client/menus/WidgetMenuOption.java | 105 ++++++++++++++++++ 7 files changed, 368 insertions(+), 2 deletions(-) create mode 100644 runelite-client/src/main/java/net/runelite/client/events/MenuEntryAdded.java create mode 100644 runelite-client/src/main/java/net/runelite/client/events/WidgetMenuOptionClicked.java create mode 100644 runelite-client/src/main/java/net/runelite/client/menus/WidgetMenuOption.java 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 5533ae61b1..ba6fe27ed5 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 @@ -28,6 +28,8 @@ public enum WidgetInfo { INVENTORY(WidgetID.INVENTORY_GROUP_ID, 0), + WORLD_MAP(WidgetID.WORLD_MAP_MENU_GROUP_ID, WidgetID.WorldMap.OPTION), + CLUE_SCROLL_TEXT(WidgetID.CLUE_SCROLL_GROUP_ID, WidgetID.Cluescroll.CLUE_TEXT), EQUIPMENT(WidgetID.EQUIPMENT_GROUP_ID, 0), @@ -106,6 +108,11 @@ public enum WidgetInfo this.childId = childId; } + public int getId() + { + return groupId << 16 | childId; + } + public int getGroupId() { return groupId; diff --git a/runelite-client/src/main/java/net/runelite/client/callback/Hooks.java b/runelite-client/src/main/java/net/runelite/client/callback/Hooks.java index 8806a2f3c0..325c9ced50 100644 --- a/runelite-client/src/main/java/net/runelite/client/callback/Hooks.java +++ b/runelite-client/src/main/java/net/runelite/client/callback/Hooks.java @@ -176,7 +176,7 @@ public class Hooks } } - public static void menuActionHook(int var0, int var1, int menuAction, int id, String menuOption, String menuTarget, int var6, int var7) + public static void menuActionHook(int var0, int widgetId, int menuAction, int id, String menuOption, String menuTarget, int var6, int var7) { /* Along the way, the RuneScape client may change a menuAction by incrementing it with 2000. * I have no idea why, but it does. Their code contains the same conditional statement. @@ -186,17 +186,31 @@ public class Hooks menuAction -= 2000; } - logger.debug("Menu action clicked: {} ({}) on {} ({})", menuOption, menuAction, menuTarget.isEmpty() ? "" : menuTarget, id); + logger.debug("Menu action clicked: {} ({}) on {} ({} widget: {})", + menuOption, menuAction, menuTarget.isEmpty() ? "" : menuTarget, id, var0, widgetId); MenuOptionClicked menuOptionClicked = new MenuOptionClicked(); menuOptionClicked.setMenuOption(menuOption); menuOptionClicked.setMenuTarget(menuTarget); menuOptionClicked.setMenuAction(MenuAction.of(menuAction)); menuOptionClicked.setId(id); + menuOptionClicked.setWidgetId(widgetId); eventBus.post(menuOptionClicked); } + public static void addMenuEntry(String option, String target, int type, int identifier, int param0, int param1) + { + if (logger.isTraceEnabled()) + { + logger.trace("Menu entry added {} {}", option, target); + } + + MenuEntryAdded menuEntry = new MenuEntryAdded(option, target, type, identifier, param0, param1); + + eventBus.post(menuEntry); + } + public static void addChatMessage(int type, String sender, String message, String clan) { if (logger.isDebugEnabled()) diff --git a/runelite-client/src/main/java/net/runelite/client/events/MenuEntryAdded.java b/runelite-client/src/main/java/net/runelite/client/events/MenuEntryAdded.java new file mode 100644 index 0000000000..6e8e225cfa --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/events/MenuEntryAdded.java @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2017, Adam + * 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.events; + +public class MenuEntryAdded +{ + private String option; + private String target; + private int type; + private int identifier; + private int actionParam0; + private int actionParam1; + + public MenuEntryAdded(String option, String target, int type, int identifier, int actionParam0, int param1) + { + this.option = option; + this.target = target; + this.type = type; + this.identifier = identifier; + this.actionParam0 = actionParam0; + this.actionParam1 = param1; + } + + public String getOption() + { + return option; + } + + public String getTarget() + { + return target; + } + + public int getType() + { + return type; + } + + public int getIdentifier() + { + return identifier; + } + + public int getActionParam0() + { + return actionParam0; + } + + public int getActionParam1() + { + return actionParam1; + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/events/MenuOptionClicked.java b/runelite-client/src/main/java/net/runelite/client/events/MenuOptionClicked.java index 24a277a70c..b228954f20 100644 --- a/runelite-client/src/main/java/net/runelite/client/events/MenuOptionClicked.java +++ b/runelite-client/src/main/java/net/runelite/client/events/MenuOptionClicked.java @@ -36,6 +36,7 @@ public class MenuOptionClicked private String menuTarget; private MenuAction menuAction; private int id; + private int widgetId; public String getMenuOption() { @@ -77,4 +78,14 @@ public class MenuOptionClicked this.id = id; } + public int getWidgetId() + { + return widgetId; + } + + public void setWidgetId(int widgetId) + { + this.widgetId = widgetId; + } + } diff --git a/runelite-client/src/main/java/net/runelite/client/events/WidgetMenuOptionClicked.java b/runelite-client/src/main/java/net/runelite/client/events/WidgetMenuOptionClicked.java new file mode 100644 index 0000000000..2ab2ebc860 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/events/WidgetMenuOptionClicked.java @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2017, Adam + * 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.events; + +import net.runelite.api.widgets.WidgetInfo; + +public class WidgetMenuOptionClicked +{ + private String menuOption; + private String menuTarget; + private WidgetInfo widget; + + public String getMenuOption() + { + return menuOption; + } + + public void setMenuOption(String menuOption) + { + this.menuOption = menuOption; + } + + public String getMenuTarget() + { + return menuTarget; + } + + public void setMenuTarget(String menuTarget) + { + this.menuTarget = menuTarget; + } + + public WidgetInfo getWidget() + { + return widget; + } + + public void setWidget(WidgetInfo widget) + { + this.widget = widget; + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/menus/MenuManager.java b/runelite-client/src/main/java/net/runelite/client/menus/MenuManager.java index ec78f7224b..4e64361c20 100644 --- a/runelite-client/src/main/java/net/runelite/client/menus/MenuManager.java +++ b/runelite-client/src/main/java/net/runelite/client/menus/MenuManager.java @@ -25,8 +25,12 @@ package net.runelite.client.menus; import com.google.common.base.Preconditions; +import com.google.common.collect.HashMultimap; +import com.google.common.collect.Multimap; import com.google.common.eventbus.EventBus; import com.google.common.eventbus.Subscribe; +import java.util.Arrays; +import java.util.Collection; import java.util.HashMap; import java.util.Map; import javax.inject.Inject; @@ -34,6 +38,10 @@ import javax.inject.Provider; import javax.inject.Singleton; import net.runelite.api.Client; import net.runelite.api.MenuAction; +import net.runelite.api.MenuEntry; +import net.runelite.api.widgets.WidgetInfo; +import net.runelite.client.events.WidgetMenuOptionClicked; +import net.runelite.client.events.MenuEntryAdded; import net.runelite.client.events.MenuOptionClicked; import net.runelite.client.events.PlayerMenuOptionClicked; import net.runelite.client.events.PlayerMenuOptionsChanged; @@ -56,6 +64,8 @@ public class MenuManager //Maps the indexes that are being used to the menu option. private final Map playerMenuIndexMap = new HashMap<>(); + //Used to manage custom non-player menu options + private final Multimap managedMenuOptions = HashMultimap.create(); @Inject public MenuManager(Provider clientProvider, EventBus eventBus) @@ -64,6 +74,69 @@ public class MenuManager this.eventBus = eventBus; } + /** + * Adds a CustomMenuOption to the list of managed menu options. + * + * @param customMenuOption The custom menu to add + */ + public void addManagedCustomMenu(WidgetMenuOption customMenuOption) + { + WidgetInfo widget = customMenuOption.getWidget(); + managedMenuOptions.put(widget.getId(), customMenuOption); + } + + /** + * Removes a CustomMenuOption from the list of managed menu options. + * + * @param customMenuOption The custom menu to add + */ + public void removeManagedCustomMenu(WidgetMenuOption customMenuOption) + { + WidgetInfo widget = customMenuOption.getWidget(); + managedMenuOptions.remove(widget.getId(), customMenuOption); + } + + private boolean menuContainsCustomMenu(WidgetMenuOption customMenuOption) + { + Client client = clientProvider.get(); + for (MenuEntry menuEntry : client.getMenuEntries()) + { + String option = menuEntry.getOption(); + String target = menuEntry.getTarget(); + + if (option.equals(customMenuOption.getMenuOption()) && target.equals(customMenuOption.getMenuTarget())) + { + return true; + } + } + return false; + } + + @Subscribe + public void onMenuEntryAdded(MenuEntryAdded event) + { + int widgetId = event.getActionParam1(); + Collection options = managedMenuOptions.get(widgetId); + for (WidgetMenuOption currentMenu : options) + { + Client client = clientProvider.get(); + + if (!menuContainsCustomMenu(currentMenu))//Don't add if we have already added it to this widget + { + MenuEntry[] menuEntries = client.getMenuEntries(); + menuEntries = Arrays.copyOf(menuEntries, menuEntries.length + 1); + + MenuEntry menuEntry = menuEntries[menuEntries.length - 1] = new MenuEntry(); + menuEntry.setOption(currentMenu.getMenuOption()); + menuEntry.setParam1(widgetId); + menuEntry.setTarget(currentMenu.getMenuTarget()); + menuEntry.setType(MenuAction.RUNELITE); + + client.setMenuEntries(menuEntries); + } + } + } + public void addPlayerMenuItem(String menuText) { Preconditions.checkNotNull(menuText); @@ -110,6 +183,23 @@ public class MenuManager return; // not a player menu } + int widgetId = event.getWidgetId(); + Collection options = managedMenuOptions.get(widgetId); + + for (WidgetMenuOption curMenuOption : options) + { + if (curMenuOption.getMenuTarget().equals(event.getMenuTarget()) + && curMenuOption.getMenuOption().equals(event.getMenuOption())) + { + WidgetMenuOptionClicked customMenu = new WidgetMenuOptionClicked(); + customMenu.setMenuOption(event.getMenuOption()); + customMenu.setMenuTarget(event.getMenuTarget()); + customMenu.setWidget(curMenuOption.getWidget()); + eventBus.post(customMenu); + return; // don't continue because it's not a player option + } + } + String target = event.getMenuTarget(); String username = target.split("[<>]")[2]; // username (level-42) diff --git a/runelite-client/src/main/java/net/runelite/client/menus/WidgetMenuOption.java b/runelite-client/src/main/java/net/runelite/client/menus/WidgetMenuOption.java new file mode 100644 index 0000000000..3f87509c83 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/menus/WidgetMenuOption.java @@ -0,0 +1,105 @@ +/* + * Copyright (c) 2017, Robin + * 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.menus; + +import net.runelite.api.widgets.WidgetInfo; + +import java.awt.Color; + +public final class WidgetMenuOption +{ + /** + * The left hand text to be displayed on the menu option. Ex. the menuOption of "Drop Bones" is "Drop" + */ + private String menuOption; + /** + * The right hand text to be displayed on the menu option Ex. the menuTarget of "Drop Bones" is "Bones" + */ + private String menuTarget; + /** + * The color that the menuTarget should be. Defaults to the brownish color that most menu options have. + */ + private Color color = Color.decode("#ff9040"); + + /** + * The widget to add the option to + */ + private final WidgetInfo widget; + + /** + * Creates a menu to be added to right click menus. The menu will only be added if match is found within the menu options + * + * @param menuOption Option text of this right click option + * @param menuTarget Target text of this right click option + * @param widget The widget to attach this option to + */ + public WidgetMenuOption(String menuOption, String menuTarget, WidgetInfo widget) + { + this.menuOption = menuOption; + setMenuTarget(menuTarget); + this.widget = widget; + } + + public void setMenuOption(String option) + { + menuOption = option; + } + + /** + * Sets the target of the menu option. Color code will be added on to target + * + * @param target The target text without color code. + */ + public void setMenuTarget(String target) + { + menuTarget = String.format("%s", + color.getRed(), color.getGreen(), color.getBlue(), target); + } + + public String getMenuOption() + { + return menuOption; + } + + public String getMenuTarget() + { + return menuTarget; + } + + public WidgetInfo getWidget() + { + return widget; + } + + public Color getColor() + { + return color; + } + + public void setColor(Color col) + { + color = col; + } +}