Merge pull request #767 from sethtroll/addgepanel
Add Grand Exchange search feature
This commit is contained in:
@@ -25,9 +25,11 @@
|
||||
package net.runelite.http.api.item;
|
||||
|
||||
import com.google.gson.JsonParseException;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import javax.imageio.ImageIO;
|
||||
import net.runelite.http.api.RuneLiteAPI;
|
||||
import okhttp3.HttpUrl;
|
||||
import okhttp3.Request;
|
||||
@@ -70,6 +72,34 @@ public class ItemClient
|
||||
}
|
||||
}
|
||||
|
||||
public BufferedImage getIcon(int itemId) throws IOException
|
||||
{
|
||||
HttpUrl url = RuneLiteAPI.getApiBase().newBuilder()
|
||||
.addPathSegment("item")
|
||||
.addPathSegment("" + itemId)
|
||||
.addPathSegment("icon")
|
||||
.build();
|
||||
|
||||
logger.debug("Built URI: {}", url);
|
||||
|
||||
Request request = new Request.Builder()
|
||||
.url(url)
|
||||
.build();
|
||||
|
||||
try (Response response = RuneLiteAPI.CLIENT.newCall(request).execute())
|
||||
{
|
||||
if (!response.isSuccessful())
|
||||
{
|
||||
logger.debug("Error grabbing icon {}: {}", itemId, response.message());
|
||||
return null;
|
||||
}
|
||||
|
||||
InputStream in = response.body().byteStream();
|
||||
BufferedImage imageIcon = ImageIO.read(in);
|
||||
return imageIcon;
|
||||
}
|
||||
}
|
||||
|
||||
public SearchResult search(String itemName) throws IOException
|
||||
{
|
||||
HttpUrl url = RuneLiteAPI.getApiBase().newBuilder()
|
||||
|
||||
@@ -85,43 +85,29 @@ public class ItemController
|
||||
}
|
||||
|
||||
@RequestMapping(path = "/{itemId}/icon", produces = "image/gif")
|
||||
public byte[] getIcon(HttpServletResponse response, @PathVariable int itemId)
|
||||
public ResponseEntity<byte[]> getIcon(@PathVariable int itemId)
|
||||
{
|
||||
ItemEntry item = itemService.getItem(itemId);
|
||||
if (item != null && item.getIcon() != null)
|
||||
{
|
||||
response.setHeader(RUNELITE_CACHE, "HIT");
|
||||
return item.getIcon();
|
||||
return ResponseEntity.ok(item.getIcon());
|
||||
}
|
||||
|
||||
item = itemService.fetchItem(itemId);
|
||||
if (item != null)
|
||||
{
|
||||
response.setHeader(RUNELITE_CACHE, "MISS");
|
||||
return item.getIcon();
|
||||
}
|
||||
|
||||
return null;
|
||||
itemService.queueItem(itemId);
|
||||
return ResponseEntity.notFound().build();
|
||||
}
|
||||
|
||||
@RequestMapping(path = "/{itemId}/icon/large", produces = "image/gif")
|
||||
public byte[] getIconLarge(HttpServletResponse response, @PathVariable int itemId)
|
||||
public ResponseEntity<byte[]> getIconLarge(HttpServletResponse response, @PathVariable int itemId)
|
||||
{
|
||||
ItemEntry item = itemService.getItem(itemId);
|
||||
if (item != null && item.getIcon_large() != null)
|
||||
{
|
||||
response.setHeader(RUNELITE_CACHE, "HIT");
|
||||
return item.getIcon_large();
|
||||
return ResponseEntity.ok(item.getIcon_large());
|
||||
}
|
||||
|
||||
item = itemService.fetchItem(itemId);
|
||||
if (item != null)
|
||||
{
|
||||
response.setHeader(RUNELITE_CACHE, "MISS");
|
||||
return item.getIcon_large();
|
||||
}
|
||||
|
||||
return null;
|
||||
itemService.queueItem(itemId);
|
||||
return ResponseEntity.notFound().build();
|
||||
}
|
||||
|
||||
@RequestMapping("/{itemId}/price")
|
||||
@@ -195,7 +181,7 @@ public class ItemController
|
||||
if (priceEntry.getFetched_time().isBefore(cacheTime))
|
||||
{
|
||||
// Queue a check for the price
|
||||
itemService.queueLookup(itemId);
|
||||
itemService.queuePriceLookup(itemId);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -86,6 +86,7 @@ public class ItemService
|
||||
private final Sql2o sql2o;
|
||||
private final ConcurrentLinkedQueue<Integer> pendingPriceLookups = new ConcurrentLinkedQueue<>();
|
||||
private final ConcurrentLinkedQueue<String> pendingSearches = new ConcurrentLinkedQueue<>();
|
||||
private final ConcurrentLinkedQueue<Integer> pendingItems = new ConcurrentLinkedQueue<>();
|
||||
|
||||
@Autowired
|
||||
public ItemService(@Qualifier("Runelite SQL2O") Sql2o sql2o)
|
||||
@@ -362,7 +363,7 @@ public class ItemService
|
||||
}
|
||||
}
|
||||
|
||||
public void queueLookup(int itemId)
|
||||
public void queuePriceLookup(int itemId)
|
||||
{
|
||||
if (pendingPriceLookups.size() >= MAX_PENDING)
|
||||
{
|
||||
@@ -382,25 +383,48 @@ public class ItemService
|
||||
pendingSearches.add(search);
|
||||
}
|
||||
|
||||
@Scheduled(fixedDelay = 5000)
|
||||
public void checkPrices()
|
||||
public void queueItem(int itemId)
|
||||
{
|
||||
Integer itemId = pendingPriceLookups.poll();
|
||||
if (itemId == null)
|
||||
if (pendingItems.size() >= MAX_PENDING)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
fetchPrice(itemId);
|
||||
pendingItems.add(itemId);
|
||||
}
|
||||
|
||||
@Scheduled(fixedDelay = 5000)
|
||||
public void checkSearches()
|
||||
public void check()
|
||||
{
|
||||
if (checkPrices())
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (checkSearches())
|
||||
{
|
||||
return;
|
||||
}
|
||||
checkItems();
|
||||
}
|
||||
|
||||
private boolean checkPrices()
|
||||
{
|
||||
Integer itemId = pendingPriceLookups.poll();
|
||||
if (itemId == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
fetchPrice(itemId);
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean checkSearches()
|
||||
{
|
||||
String search = pendingSearches.poll();
|
||||
if (search == null)
|
||||
{
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
try
|
||||
@@ -413,5 +437,19 @@ public class ItemService
|
||||
{
|
||||
log.warn("error while searching items", ex);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean checkItems()
|
||||
{
|
||||
Integer itemId = pendingItems.poll();
|
||||
if (itemId == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
fetchItem(itemId);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,7 +36,10 @@ public class KeyManager
|
||||
|
||||
public void registerKeyListener(KeyListener keyListener)
|
||||
{
|
||||
keyListeners.add(keyListener);
|
||||
if (!keyListeners.contains(keyListener))
|
||||
{
|
||||
keyListeners.add(keyListener);
|
||||
}
|
||||
}
|
||||
|
||||
public void unregisterKeyListener(KeyListener keyListener)
|
||||
|
||||
@@ -38,7 +38,10 @@ public class MouseManager
|
||||
|
||||
public void registerMouseListener(MouseListener mouseListener)
|
||||
{
|
||||
mouseListeners.add(mouseListener);
|
||||
if (!mouseListeners.contains(mouseListener))
|
||||
{
|
||||
mouseListeners.add(mouseListener);
|
||||
}
|
||||
}
|
||||
|
||||
public void unregisterMouseListener(MouseListener mouseListener)
|
||||
@@ -48,7 +51,10 @@ public class MouseManager
|
||||
|
||||
public void registerMouseWheelListener(MouseWheelListener mouseWheelListener)
|
||||
{
|
||||
mouseWheelListeners.add(mouseWheelListener);
|
||||
if (!mouseWheelListeners.contains(mouseWheelListener))
|
||||
{
|
||||
mouseWheelListeners.add(mouseWheelListener);
|
||||
}
|
||||
}
|
||||
|
||||
public void unregisterMouseWheelListener(MouseWheelListener mouseWheelListener)
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* Copyright (c) 2018, Seth <https://github.com/sethtroll>
|
||||
* 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.grandexchange;
|
||||
|
||||
import net.runelite.client.config.Config;
|
||||
import net.runelite.client.config.ConfigGroup;
|
||||
import net.runelite.client.config.ConfigItem;
|
||||
|
||||
@ConfigGroup(
|
||||
keyName = "grandexchange",
|
||||
name = "Grand Exchange",
|
||||
description = "Configuration for the Grand Exchange"
|
||||
)
|
||||
public interface GrandExchangeConfig extends Config
|
||||
{
|
||||
@ConfigItem(
|
||||
keyName = "quickLookup",
|
||||
name = "Hotkey lookup (Alt + Left click)",
|
||||
description = "Configures whether to enable the hotkey lookup for ge searches"
|
||||
)
|
||||
default boolean quickLookup()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,142 @@
|
||||
/*
|
||||
* Copyright (c) 2018, Seth <https://github.com/sethtroll>
|
||||
* 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.grandexchange;
|
||||
|
||||
import java.awt.BorderLayout;
|
||||
import java.awt.Color;
|
||||
import java.awt.Desktop;
|
||||
import java.awt.GridLayout;
|
||||
import java.awt.event.MouseAdapter;
|
||||
import java.awt.event.MouseEvent;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.text.NumberFormat;
|
||||
import javax.swing.ImageIcon;
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.border.CompoundBorder;
|
||||
import javax.swing.border.EmptyBorder;
|
||||
import javax.swing.border.LineBorder;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
@Slf4j
|
||||
class GrandExchangeItemPanel extends JPanel
|
||||
{
|
||||
GrandExchangeItemPanel(BufferedImage icon, String name, int itemID, int gePrice, Double haPrice)
|
||||
{
|
||||
BorderLayout layout = new BorderLayout();
|
||||
layout.setHgap(5);
|
||||
setLayout(layout);
|
||||
setToolTipText(name);
|
||||
|
||||
Color background = getBackground();
|
||||
|
||||
addMouseListener(new MouseAdapter()
|
||||
{
|
||||
@Override
|
||||
public void mouseEntered(MouseEvent e)
|
||||
{
|
||||
setBackground(getBackground().darker().darker());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mouseExited(MouseEvent e)
|
||||
{
|
||||
setBackground(background);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mouseReleased(MouseEvent e)
|
||||
{
|
||||
geLink(name, itemID);
|
||||
}
|
||||
});
|
||||
|
||||
setBorder(new CompoundBorder
|
||||
(
|
||||
new LineBorder(getBackground().brighter(), 1),
|
||||
new EmptyBorder(5, 5, 5, 5)
|
||||
));
|
||||
|
||||
// Icon
|
||||
JLabel itemIcon = new JLabel();
|
||||
itemIcon.setIcon(new ImageIcon(icon));
|
||||
add(itemIcon, BorderLayout.LINE_START);
|
||||
|
||||
// Item details panel
|
||||
JPanel rightPanel = new JPanel(new GridLayout(3, 1));
|
||||
rightPanel.setOpaque(false);
|
||||
|
||||
// Item name
|
||||
JLabel itemName = new JLabel();
|
||||
itemName.setText(name);
|
||||
rightPanel.add(itemName);
|
||||
|
||||
// Ge price
|
||||
JLabel gePriceLabel = new JLabel();
|
||||
gePriceLabel.setText(NumberFormat.getInstance().format(gePrice) + " gp");
|
||||
gePriceLabel.setForeground(Color.GREEN);
|
||||
rightPanel.add(gePriceLabel);
|
||||
|
||||
// Alch price
|
||||
JLabel haPriceLabel = new JLabel();
|
||||
haPriceLabel.setText(NumberFormat.getInstance().format(haPrice.intValue()) + " alch");
|
||||
haPriceLabel.setForeground(Color.orange);
|
||||
rightPanel.add(haPriceLabel);
|
||||
|
||||
add(rightPanel, BorderLayout.CENTER);
|
||||
}
|
||||
|
||||
public void geLink(String name, int itemID)
|
||||
{
|
||||
String url = "http://services.runescape.com/m=itemdb_oldschool/" + name.replaceAll(" ", "_") + "/viewitem?obj=" + itemID;
|
||||
|
||||
if (!Desktop.isDesktopSupported())
|
||||
{
|
||||
log.info("Desktop is not supported. Visit {}", url);
|
||||
return;
|
||||
}
|
||||
|
||||
Desktop desktop = Desktop.getDesktop();
|
||||
if (!desktop.isSupported(Desktop.Action.BROWSE))
|
||||
{
|
||||
log.info("Desktop browser is not supported. Visit {}", url);
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
desktop.browse(new URI(url));
|
||||
|
||||
log.debug("Opened browser to {}", url);
|
||||
}
|
||||
catch (IOException | URISyntaxException ex)
|
||||
{
|
||||
log.warn("Unable to open grand exchange page", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* Copyright (c) 2018, Seth <https://github.com/sethtroll>
|
||||
* 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.grandexchange;
|
||||
|
||||
import java.awt.image.BufferedImage;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class GrandExchangeItems
|
||||
{
|
||||
private final BufferedImage icon;
|
||||
private final String name;
|
||||
private final int itemId;
|
||||
private final int gePrice;
|
||||
private final double haPrice;
|
||||
|
||||
GrandExchangeItems(BufferedImage icon, String name, int itemId, int gePrice, double haPrice)
|
||||
{
|
||||
this.icon = icon;
|
||||
this.name = name;
|
||||
this.itemId = itemId;
|
||||
this.gePrice = gePrice;
|
||||
this.haPrice = haPrice;
|
||||
}
|
||||
}
|
||||
@@ -25,9 +25,15 @@
|
||||
|
||||
package net.runelite.client.plugins.grandexchange;
|
||||
|
||||
import java.awt.BorderLayout;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import javax.inject.Inject;
|
||||
import javax.swing.BoxLayout;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.JTabbedPane;
|
||||
import lombok.Getter;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import net.runelite.api.Client;
|
||||
import net.runelite.api.GrandExchangeOffer;
|
||||
import net.runelite.client.game.ItemManager;
|
||||
import net.runelite.client.ui.PluginPanel;
|
||||
@@ -37,20 +43,34 @@ class GrandExchangePanel extends PluginPanel
|
||||
{
|
||||
private static final int MAX_OFFERS = 8;
|
||||
|
||||
@Getter
|
||||
private GrandExchangeSearchPanel searchPanel;
|
||||
|
||||
private GrandExchangeOfferSlot[] offerSlotPanels = new GrandExchangeOfferSlot[MAX_OFFERS];
|
||||
|
||||
@Inject
|
||||
GrandExchangePanel(ItemManager itemManager)
|
||||
{
|
||||
setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
|
||||
private JPanel offerPanel = new JPanel();
|
||||
|
||||
private JTabbedPane tabbedPane = new JTabbedPane();
|
||||
|
||||
@Inject
|
||||
GrandExchangePanel(Client client, ItemManager itemManager, ScheduledExecutorService executor)
|
||||
{
|
||||
setLayout(new BorderLayout());
|
||||
add(tabbedPane, BorderLayout.NORTH);
|
||||
|
||||
// Offer Panel
|
||||
offerPanel.setLayout(new BoxLayout(offerPanel, BoxLayout.Y_AXIS));
|
||||
for (int i = 0; i < offerSlotPanels.length; ++i)
|
||||
{
|
||||
offerSlotPanels[i] = new GrandExchangeOfferSlot(itemManager);
|
||||
add(offerSlotPanels[i]);
|
||||
offerPanel.add(offerSlotPanels[i]);
|
||||
}
|
||||
|
||||
setVisible(true);
|
||||
// Search Panel
|
||||
searchPanel = new GrandExchangeSearchPanel(client, itemManager, executor);
|
||||
|
||||
tabbedPane.addTab("Offers", offerPanel);
|
||||
tabbedPane.addTab("Search", searchPanel);
|
||||
}
|
||||
|
||||
void updateOffer(GrandExchangeOffer newOffer, int slot)
|
||||
@@ -58,4 +78,14 @@ class GrandExchangePanel extends PluginPanel
|
||||
offerSlotPanels[slot].updateOffer(newOffer);
|
||||
}
|
||||
|
||||
void showSearch()
|
||||
{
|
||||
if (searchPanel.isShowing())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
tabbedPane.setSelectedComponent(searchPanel);
|
||||
revalidate();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,12 +28,24 @@
|
||||
package net.runelite.client.plugins.grandexchange;
|
||||
|
||||
import com.google.common.eventbus.Subscribe;
|
||||
import com.google.inject.Provides;
|
||||
import java.awt.event.MouseEvent;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.IOException;
|
||||
import javax.imageio.ImageIO;
|
||||
import javax.inject.Inject;
|
||||
import javax.swing.SwingUtilities;
|
||||
import net.runelite.api.Client;
|
||||
import net.runelite.api.ItemComposition;
|
||||
import net.runelite.api.Point;
|
||||
import net.runelite.api.events.ConfigChanged;
|
||||
import net.runelite.api.events.GrandExchangeOfferChanged;
|
||||
import net.runelite.api.widgets.Widget;
|
||||
import net.runelite.api.widgets.WidgetInfo;
|
||||
import net.runelite.api.widgets.WidgetItem;
|
||||
import net.runelite.client.config.ConfigManager;
|
||||
import net.runelite.client.input.MouseListener;
|
||||
import net.runelite.client.input.MouseManager;
|
||||
import net.runelite.client.plugins.Plugin;
|
||||
import net.runelite.client.plugins.PluginDescriptor;
|
||||
import net.runelite.client.ui.ClientUI;
|
||||
@@ -48,9 +60,26 @@ public class GrandExchangePlugin extends Plugin
|
||||
|
||||
private GrandExchangePanel panel;
|
||||
|
||||
private MouseListener itemClick;
|
||||
|
||||
@Inject
|
||||
private MouseManager mouseManager;
|
||||
|
||||
@Inject
|
||||
private Client client;
|
||||
|
||||
@Inject
|
||||
private ClientUI ui;
|
||||
|
||||
@Inject
|
||||
private GrandExchangeConfig config;
|
||||
|
||||
@Provides
|
||||
GrandExchangeConfig provideConfig(ConfigManager configManager)
|
||||
{
|
||||
return configManager.getConfig(GrandExchangeConfig.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void startUp() throws IOException
|
||||
{
|
||||
@@ -58,12 +87,82 @@ public class GrandExchangePlugin extends Plugin
|
||||
BufferedImage icon = ImageIO.read(getClass().getResourceAsStream("ge_icon.png"));
|
||||
button = new NavigationButton("GE Offers", icon, () -> panel);
|
||||
ui.getPluginToolbar().addNavigation(button);
|
||||
|
||||
itemClick = new MouseListener()
|
||||
{
|
||||
@Override
|
||||
public MouseEvent mouseClicked(MouseEvent e)
|
||||
{
|
||||
// Check if left click + alt
|
||||
if (e.getButton() == MouseEvent.BUTTON1 && e.isAltDown())
|
||||
{
|
||||
Point mousePosition = client.getMouseCanvasPosition();
|
||||
Widget inventoryWidget = client.getWidget(WidgetInfo.INVENTORY);
|
||||
if (inventoryWidget != null && !inventoryWidget.isHidden())
|
||||
{
|
||||
for (WidgetItem item : inventoryWidget.getWidgetItems())
|
||||
{
|
||||
if (item.getCanvasBounds().contains(mousePosition.getX(), mousePosition.getY()))
|
||||
{
|
||||
ItemComposition itemComp = client.getItemDefinition(item.getId());
|
||||
if (itemComp != null)
|
||||
{
|
||||
e.consume();
|
||||
|
||||
SwingUtilities.invokeLater(() ->
|
||||
{
|
||||
panel.showSearch();
|
||||
|
||||
if (!button.isSelected())
|
||||
{
|
||||
button.doClick();
|
||||
}
|
||||
|
||||
panel.getSearchPanel().priceLookup(itemComp.getName());
|
||||
});
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return super.mouseClicked(e);
|
||||
}
|
||||
};
|
||||
|
||||
if (config.quickLookup())
|
||||
{
|
||||
mouseManager.registerMouseListener(itemClick);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void shutDown()
|
||||
{
|
||||
ui.getPluginToolbar().removeNavigation(button);
|
||||
|
||||
mouseManager.unregisterMouseListener(itemClick);
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void onConfigChange(ConfigChanged event)
|
||||
{
|
||||
if (event.getGroup().equals("grandexchange"))
|
||||
{
|
||||
if (event.getKey().equals("quickLookup"))
|
||||
{
|
||||
if (config.quickLookup())
|
||||
{
|
||||
mouseManager.registerMouseListener(itemClick);
|
||||
}
|
||||
else
|
||||
{
|
||||
mouseManager.unregisterMouseListener(itemClick);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
|
||||
@@ -0,0 +1,230 @@
|
||||
/*
|
||||
* Copyright (c) 2018, Seth <https://github.com/sethtroll>
|
||||
* 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.grandexchange;
|
||||
|
||||
import com.google.common.base.Strings;
|
||||
import java.awt.BorderLayout;
|
||||
import java.awt.Color;
|
||||
import java.awt.GridLayout;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import javax.imageio.ImageIO;
|
||||
import javax.swing.Icon;
|
||||
import javax.swing.ImageIcon;
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.SwingUtilities;
|
||||
import javax.swing.border.EmptyBorder;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import net.runelite.api.Client;
|
||||
import net.runelite.api.ItemComposition;
|
||||
import net.runelite.client.game.ItemManager;
|
||||
import net.runelite.client.plugins.hiscore.IconTextField;
|
||||
import net.runelite.http.api.item.Item;
|
||||
import net.runelite.http.api.item.ItemClient;
|
||||
import net.runelite.http.api.item.ItemPrice;
|
||||
import net.runelite.http.api.item.SearchResult;
|
||||
|
||||
@Slf4j
|
||||
class GrandExchangeSearchPanel extends JPanel
|
||||
{
|
||||
private static final List<GrandExchangeItems> ITEMS_LIST = new ArrayList<>();
|
||||
|
||||
private final Client client;
|
||||
private final ItemManager itemManager;
|
||||
private final ScheduledExecutorService executor;
|
||||
|
||||
private ItemClient itemClient;
|
||||
|
||||
private Icon search;
|
||||
|
||||
private IconTextField searchBox = new IconTextField();
|
||||
private JPanel container = new JPanel();
|
||||
private JPanel searchItemsPanel = new JPanel();
|
||||
private JLabel searchingLabel = new JLabel("Searching...");
|
||||
|
||||
GrandExchangeSearchPanel(Client client, ItemManager itemManager, ScheduledExecutorService executor)
|
||||
{
|
||||
this.client = client;
|
||||
this.itemManager = itemManager;
|
||||
this.executor = executor;
|
||||
init();
|
||||
}
|
||||
|
||||
void init()
|
||||
{
|
||||
setLayout(new BorderLayout());
|
||||
container.setLayout(new BorderLayout());
|
||||
|
||||
// Search Box
|
||||
try
|
||||
{
|
||||
search = new ImageIcon(ImageIO.read(GrandExchangePlugin.class.getResourceAsStream("search.png")));
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
log.warn("Failed to read icon", e);
|
||||
}
|
||||
|
||||
searchBox.setIcon(search);
|
||||
searchBox.addActionListener(e -> executor.execute(() -> priceLookup(false)));
|
||||
|
||||
container.add(searchBox, BorderLayout.NORTH);
|
||||
|
||||
// Searching label
|
||||
searchingLabel.setHorizontalAlignment(JLabel.CENTER);
|
||||
searchingLabel.setForeground(Color.YELLOW);
|
||||
|
||||
// Items Panel
|
||||
searchItemsPanel.setLayout(new GridLayout(0, 1, 0, 3));
|
||||
searchItemsPanel.setBorder(new EmptyBorder(3, 0, 0, 0));
|
||||
|
||||
container.add(searchItemsPanel, BorderLayout.SOUTH);
|
||||
add(container, BorderLayout.NORTH);
|
||||
}
|
||||
|
||||
void priceLookup(String item)
|
||||
{
|
||||
searchBox.setText(item);
|
||||
executor.execute(() -> priceLookup(true));
|
||||
}
|
||||
|
||||
private void priceLookup(boolean exactMatch)
|
||||
{
|
||||
String lookup = searchBox.getText();
|
||||
|
||||
if (Strings.isNullOrEmpty(lookup))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Input is not empty, add searching label
|
||||
searchItemsPanel.removeAll();
|
||||
showSearchString(true);
|
||||
|
||||
SearchResult result;
|
||||
|
||||
try
|
||||
{
|
||||
result = itemManager.searchForItem(lookup);
|
||||
}
|
||||
catch (ExecutionException ex)
|
||||
{
|
||||
log.warn("Unable to search for item {}", lookup, ex);
|
||||
showSearchString(false);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
if (result != null && !result.getItems().isEmpty())
|
||||
{
|
||||
itemClient = new ItemClient();
|
||||
|
||||
for (Item item : result.getItems())
|
||||
{
|
||||
int itemId = item.getId();
|
||||
|
||||
ItemComposition itemComp = client.getItemDefinition(itemId);
|
||||
if (itemComp == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
ItemPrice itemPrice;
|
||||
try
|
||||
{
|
||||
itemPrice = itemManager.getItemPrice(itemId);
|
||||
}
|
||||
catch (IOException ex)
|
||||
{
|
||||
log.warn("Unable to fetch item price for {}", itemId, ex);
|
||||
showSearchString(false);
|
||||
return;
|
||||
}
|
||||
|
||||
BufferedImage itemImage;
|
||||
try
|
||||
{
|
||||
itemImage = itemClient.getIcon(itemId);
|
||||
}
|
||||
catch (IOException ex)
|
||||
{
|
||||
log.warn("Unable to fetch item icon for {}", itemId, ex);
|
||||
showSearchString(false);
|
||||
return;
|
||||
}
|
||||
|
||||
if (itemImage == null)
|
||||
{
|
||||
log.warn("Unable to fetch item icon for {}", itemId);
|
||||
showSearchString(false);
|
||||
return;
|
||||
}
|
||||
|
||||
ITEMS_LIST.add(new GrandExchangeItems(itemImage, item.getName(), itemId, itemPrice.getPrice(), itemComp.getPrice() * 0.6));
|
||||
|
||||
// If using hotkey to lookup item, stop after finding match.
|
||||
if (exactMatch && item.getName().equalsIgnoreCase(lookup))
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SwingUtilities.invokeLater(() ->
|
||||
{
|
||||
for (GrandExchangeItems item : ITEMS_LIST)
|
||||
{
|
||||
GrandExchangeItemPanel panel = new GrandExchangeItemPanel(item.getIcon(), item.getName(), item.getItemId(), item.getGePrice(), item.getHaPrice());
|
||||
|
||||
searchItemsPanel.add(panel);
|
||||
}
|
||||
|
||||
ITEMS_LIST.clear();
|
||||
|
||||
// Remove searching label after search is complete
|
||||
showSearchString(false);
|
||||
});
|
||||
}
|
||||
|
||||
private void showSearchString(boolean shown)
|
||||
{
|
||||
if (shown)
|
||||
{
|
||||
add(searchingLabel, BorderLayout.CENTER);
|
||||
}
|
||||
else
|
||||
{
|
||||
remove(searchingLabel);
|
||||
}
|
||||
|
||||
revalidate();
|
||||
repaint();
|
||||
}
|
||||
}
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 388 B |
Reference in New Issue
Block a user