diff --git a/src/main/java/codechicken/nei/BookmarkPanel.java b/src/main/java/codechicken/nei/BookmarkPanel.java index a22e78c7..e22cf986 100644 --- a/src/main/java/codechicken/nei/BookmarkPanel.java +++ b/src/main/java/codechicken/nei/BookmarkPanel.java @@ -21,7 +21,6 @@ import static codechicken.lib.gui.GuiDraw.drawRect; import java.awt.Point; import java.io.File; import java.io.FileReader; -import java.io.FileWriter; import java.io.InputStream; import java.io.OutputStream; import java.io.FileInputStream; @@ -450,6 +449,10 @@ public class BookmarkPanel extends ItemPanel public void saveBookmarks() { + if (bookmarkFile == null) { + return; + } + ArrayList strings = new ArrayList<>(); for (int grpIdx = 0; grpIdx < namespaces.size() - 1; grpIdx++) { @@ -487,12 +490,10 @@ public class BookmarkPanel extends ItemPanel } - if (bookmarkFile != null) { - try(FileWriter writer = new FileWriter(bookmarkFile)) { - IOUtils.writeLines(strings, "\n", writer); - } catch (IOException e) { - NEIClientConfig.logger.error("Filed to save bookmarks list to file {}", bookmarkFile, e); - } + try (FileOutputStream output = new FileOutputStream(bookmarkFile)) { + IOUtils.writeLines(strings, "\n", output, "UTF-8"); + } catch (IOException e) { + NEIClientConfig.logger.error("Filed to save bookmarks list to file {}", bookmarkFile, e); } } @@ -748,6 +749,7 @@ public class BookmarkPanel extends ItemPanel public void mouseUp(int mousex, int mousey, int button) { if (sortedStackIndex != -1) { + draggedStack = null; sortedNamespaceIndex = -1; sortedStackIndex = -1; mouseDownSlot = -1; diff --git a/src/main/java/codechicken/nei/ClientHandler.java b/src/main/java/codechicken/nei/ClientHandler.java index 9cfa5140..e8a17444 100644 --- a/src/main/java/codechicken/nei/ClientHandler.java +++ b/src/main/java/codechicken/nei/ClientHandler.java @@ -6,6 +6,8 @@ import codechicken.lib.packet.PacketCustom; import codechicken.nei.api.API; import codechicken.nei.api.ItemInfo; import codechicken.nei.recipe.GuiRecipeTab; +import codechicken.nei.recipe.StackInfo; + import com.google.common.collect.Lists; import cpw.mods.fml.client.CustomModLoadingErrorDisplayException; import cpw.mods.fml.common.FMLCommonHandler; @@ -160,6 +162,7 @@ public class ClientHandler loadSerialHandlers(); loadHeightHackHandlers(); ItemInfo.preInit(); + StackInfo.loadGuidFilters(); } public static void loadSerialHandlers() { diff --git a/src/main/java/codechicken/nei/ItemList.java b/src/main/java/codechicken/nei/ItemList.java index 8a2c3168..df25046f 100644 --- a/src/main/java/codechicken/nei/ItemList.java +++ b/src/main/java/codechicken/nei/ItemList.java @@ -252,6 +252,7 @@ public class ItemList try { filtered = ItemList.forkJoinPool.submit(() -> items.parallelStream() + .filter(PresetsWidget::matches) .filter(filter::matches) .collect(Collectors.toCollection(ArrayList::new))).get(); } catch (InterruptedException | ExecutionException e) { diff --git a/src/main/java/codechicken/nei/ItemPanel.java b/src/main/java/codechicken/nei/ItemPanel.java index 5c88bebe..b5395dc8 100644 --- a/src/main/java/codechicken/nei/ItemPanel.java +++ b/src/main/java/codechicken/nei/ItemPanel.java @@ -1,5 +1,6 @@ package codechicken.nei; +import codechicken.lib.vec.Rectangle4i; import codechicken.nei.api.GuiInfo; import codechicken.nei.api.INEIGuiHandler; import codechicken.nei.guihook.GuiContainerManager; @@ -14,6 +15,7 @@ import net.minecraft.item.ItemStack; import net.minecraftforge.fluids.FluidStack; import net.minecraftforge.fluids.IFluidContainerItem; +import static codechicken.lib.gui.GuiDraw.drawRect; import static codechicken.nei.LayoutManager.searchField; import java.util.ArrayList; @@ -82,6 +84,18 @@ public class ItemPanel extends Widget super.refresh(gui); } + @Override + protected void drawItem(Rectangle4i rect, int idx, ItemPanelSlot focus) + { + + if (PresetsWidget.inEditMode() && !PresetsWidget.isHidden(getItem(idx))) { + drawRect(rect.x, rect.y, rect.w, rect.h, 0xee555555); + } + + super.drawItem(rect, idx, PresetsWidget.inEditMode()? null: focus); + } + + } protected ItemsGrid grid = new ItemPanelGrid(); @@ -236,6 +250,7 @@ public class ItemPanel extends Widget if (mouseOverSlot == null || mouseOverSlot.slotIndex != mouseDownSlot || heldTime > 500) { draggedStack = getDraggedStackWithQuantity(mouseDownSlot); + mouseDownSlot = -1; } } @@ -482,7 +497,6 @@ public class ItemPanel extends Widget else if (button == 1) GuiUsageRecipe.openRecipeGui("item", item); - draggedStack = null; mouseDownSlot = -1; return; } diff --git a/src/main/java/codechicken/nei/ItemsGrid.java b/src/main/java/codechicken/nei/ItemsGrid.java index 06283688..7a6c2bb5 100644 --- a/src/main/java/codechicken/nei/ItemsGrid.java +++ b/src/main/java/codechicken/nei/ItemsGrid.java @@ -14,7 +14,7 @@ import static codechicken.lib.gui.GuiDraw.drawRect; public class ItemsGrid { - protected static final int SLOT_SIZE = 18; + public static final int SLOT_SIZE = 18; protected int width; protected int height; diff --git a/src/main/java/codechicken/nei/LayoutManager.java b/src/main/java/codechicken/nei/LayoutManager.java index 7c229c0e..771b7fb8 100644 --- a/src/main/java/codechicken/nei/LayoutManager.java +++ b/src/main/java/codechicken/nei/LayoutManager.java @@ -86,6 +86,7 @@ public class LayoutManager implements IContainerInputHandler, IContainerTooltipH public static ItemPanel itemPanel; public static BookmarkPanel bookmarkPanel; public static SubsetWidget dropDown; + public static PresetsWidget presetsPanel; public static TextField searchField; public static ButtonCycled options; @@ -177,7 +178,7 @@ public class LayoutManager implements IContainerInputHandler, IContainerTooltipH @Override public boolean objectUnderMouse(GuiContainer gui, int mousex, int mousey) { if (!isHidden() && isEnabled()) - for (Widget widget : controlWidgets) + for (Widget widget : drawWidgets) if (widget.contains(mousex, mousey)) return true; @@ -238,7 +239,7 @@ public class LayoutManager implements IContainerInputHandler, IContainerTooltipH @Override public ItemStack getStackUnderMouse(GuiContainer gui, int mousex, int mousey) { if (!isHidden() && isEnabled()) { - for (Widget widget : controlWidgets) { + for (Widget widget : drawWidgets) { ItemStack stack = widget.getStackMouseOver(mousex, mousey); if (stack != null) return stack; @@ -275,7 +276,7 @@ public class LayoutManager implements IContainerInputHandler, IContainerTooltipH @Override public List handleTooltip(GuiContainer gui, int mousex, int mousey, List currenttip) { if (!isHidden() && isEnabled() && GuiContainerManager.shouldShowTooltip(gui)) { - for (Widget widget : controlWidgets) + for (Widget widget : drawWidgets) currenttip = widget.handleTooltip(mousex, mousey, currenttip); } return currenttip; @@ -339,6 +340,7 @@ public class LayoutManager implements IContainerInputHandler, IContainerTooltipH bookmarkPanel.init(); dropDown = new SubsetWidget(); + presetsPanel = new PresetsWidget(); searchField = new SearchField("search"); options = new ButtonCycled(3) @@ -671,7 +673,13 @@ public class LayoutManager implements IContainerInputHandler, IContainerTooltipH addWidget(options); addWidget(bookmarksButton); if (visiblity.showItemPanel) { - addWidget(itemPanel); + + if (PresetsWidget.inEditMode()) { + drawWidgets.add(itemPanel); + } else { + addWidget(itemPanel); + } + itemPanel.setVisible(); addWidget(more); @@ -682,6 +690,8 @@ public class LayoutManager implements IContainerInputHandler, IContainerTooltipH if (visiblity.showBookmarkPanel) { addWidget(bookmarkPanel); bookmarkPanel.setVisible(); + + addWidget(presetsPanel); } if (visiblity.showSearchSection) { @@ -738,7 +748,7 @@ public class LayoutManager implements IContainerInputHandler, IContainerTooltipH if (!isEnabled()) return; - for (Widget widget : controlWidgets) + for (Widget widget : drawWidgets) widget.update(); } @@ -747,7 +757,7 @@ public class LayoutManager implements IContainerInputHandler, IContainerTooltipH if (isHidden() || !isEnabled()) return false; - for (Widget widget : controlWidgets) + for (Widget widget : drawWidgets) if (widget.onMouseWheel(scrolled, mousex, mousey)) return true; diff --git a/src/main/java/codechicken/nei/NEIClientConfig.java b/src/main/java/codechicken/nei/NEIClientConfig.java index 3d54c927..319c3e5e 100644 --- a/src/main/java/codechicken/nei/NEIClientConfig.java +++ b/src/main/java/codechicken/nei/NEIClientConfig.java @@ -190,6 +190,17 @@ public class NEIClientConfig { return true; } }); + + tag.getTag("inventory.worldSpecificPresets").setComment("Global or world specific presets").getBooleanValue(false); + API.addOption(new OptionToggleButton("inventory.worldSpecificPresets", true) { + @Override + public boolean onClick(int button) { + super.onClick(button); + initPresetsFile(worldPath); + return true; + } + }); + tag.getTag("inventory.bookmarksEnabled").setComment("Enable/disable bookmarks").getBooleanValue(true); API.addOption(new OptionToggleButton("inventory.bookmarksEnabled", true)); tag.getTag("inventory.saveCurrentRecipeInBookmarksEnabled").setComment("Save Current Recipe in Bookmarks").getBooleanValue(true); @@ -303,6 +314,7 @@ public class NEIClientConfig { } initBookmarkFile(worldPath); + initPresetsFile(worldPath); world = new ConfigSet(new File(specificDir, "NEI.dat"), new ConfigFile(new File(specificDir, "NEI.cfg"))); onWorldLoad(newWorld); } @@ -386,6 +398,16 @@ public class NEIClientConfig { ItemPanels.bookmarkPanel.setBookmarkFile(worldPath); } + private static void initPresetsFile(String worldPath) + { + + if (!global.config.getTag("inventory.worldSpecificPresets").getBooleanValue()) { + worldPath = "global"; + } + + PresetsWidget.loadPresets(worldPath); + } + public static boolean isWorldSpecific(String setting) { if(world == null) return false; ConfigTag tag = world.config.getTag(setting, false); diff --git a/src/main/java/codechicken/nei/PresetsWidget.java b/src/main/java/codechicken/nei/PresetsWidget.java new file mode 100644 index 00000000..6dbec7a6 --- /dev/null +++ b/src/main/java/codechicken/nei/PresetsWidget.java @@ -0,0 +1,1032 @@ +package codechicken.nei; + +import codechicken.core.CommonUtils; +import codechicken.core.gui.GuiScrollSlot; +import codechicken.lib.gui.GuiDraw; +import codechicken.lib.vec.Rectangle4i; +import codechicken.nei.ItemList.AllMultiItemFilter; +import codechicken.nei.ItemList.EverythingItemFilter; +import codechicken.nei.ItemPanel.ItemPanelSlot; +import codechicken.nei.api.API; +import codechicken.nei.api.ItemFilter.ItemFilterProvider; +import codechicken.nei.api.ItemFilter; +import codechicken.nei.recipe.GuiRecipe; +import codechicken.nei.recipe.StackInfo; +import codechicken.nei.util.NBTJson; +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; +import com.google.gson.JsonPrimitive; +import com.google.gson.JsonSyntaxException; +import cpw.mods.fml.common.registry.GameRegistry; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.concurrent.ExecutionException; +import java.util.stream.Collectors; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.FontRenderer; +import net.minecraft.item.ItemStack; +import org.apache.commons.io.IOUtils; +import org.lwjgl.input.Keyboard; +import static codechicken.nei.NEIClientUtils.translate; + +public class PresetsWidget extends Widget// implements ItemFilterProvider +{ + + public static class PresetTag implements ItemFilter + { + public String filename = null; + public String displayName = ""; + public boolean whitelist = true; + public HashSet items; + + public PresetTag(String displayName) + { + this(displayName, new HashSet<>(), false, null); + } + + public PresetTag(String displayName, HashSet items) + { + this(displayName, items, true, null); + } + + public PresetTag(String displayName, HashSet items, boolean whitelist, String filename) + { + this.displayName = displayName; + this.items = items != null? items: new HashSet<>(); + this.whitelist = whitelist; + this.filename = filename; + } + + public boolean matches(final ItemStack stack) + { + return items.contains(StackInfo.getItemStackGUID(stack)) == whitelist; + } + + public static PresetTag loadFromFile(File file) + { + if (!file.isFile() || !file.exists() || file.getName().equals("selected.ini")) { + return null; + } + + try { + + List itemStrings; + try (FileInputStream input = new FileInputStream(file)) { + NEIClientConfig.logger.info("Loading presets from file {}", file); + itemStrings = IOUtils.readLines(input, "UTF-8"); + } catch (IOException e) { + NEIClientConfig.logger.error("Failed to load presets from file {}", file, e); + return null; + } + + final JsonParser parser = new JsonParser(); + final JsonObject metaObject = parser.parse(itemStrings.remove(0)).getAsJsonObject(); + final String displayName = metaObject.get("displayName").getAsString(); + final boolean whitelist = metaObject.get("whitelist").getAsBoolean(); + final HashSet items = new HashSet<>(); + + for (String itemStr: itemStrings) { + items.add(itemStr); + } + + return new PresetTag(displayName, items, whitelist, file.getName()); + } catch (Throwable th) { + NEIClientConfig.logger.error("Failed to load presets ItemStack from file", file); + } + + return null; + } + + public void deleteFile() + { + if (presetsDir == null || filename == null) return; + final File tagFile = new File(presetsDir, filename); + + if (tagFile.exists()) { + tagFile.delete(); + } + + } + + public void saveToFile() + { + if (presetsDir == null) return; + + if (filename == null) { + final String sanitized = displayName.replaceAll("[^_\\-.0-9a-zA-Z]", "_"); + int index = 1; + + filename = sanitized + ".ini"; + + while ((new File(presetsDir, filename)).exists() || filename.equals("selected.ini")) { + filename = sanitized + " (" + index + ").ini"; + index++; + } + + } + + final List strings = new ArrayList<>(); + final JsonObject row = new JsonObject(); + + row.add("displayName", new JsonPrimitive(displayName)); + row.add("whitelist", new JsonPrimitive(whitelist)); + + strings.add(NBTJson.toJson(row)); + + try { + strings.addAll(items); + } catch (JsonSyntaxException e) { + NEIClientConfig.logger.error("Failed to stringify presets ItemStack to json string"); + } + + final File tagFile = new File(presetsDir, filename); + + try (FileOutputStream output = new FileOutputStream(tagFile)) { + IOUtils.writeLines(strings, "\n", output, "UTF-8"); + } catch (IOException e) { + NEIClientConfig.logger.error("Filed to save presets list to file {}", tagFile, e); + } + + } + + @SuppressWarnings("unchecked") + public PresetTag copy() + { + return new PresetTag(displayName, (HashSet) items.clone(), whitelist, null); + } + + } + protected static class SubsetListBox extends GuiScrollSlot + { + protected final List presets = new ArrayList<>(); + protected final List selected = new ArrayList<>(); + protected int lastSelectedIndex = 0; + + public SubsetListBox(File presetsDir) + { + super(0, 0, 0, 0); + setSmoothScroll(false); + + try { + + presets.addAll(ItemList.forkJoinPool + .submit(() -> Arrays.asList(presetsDir.listFiles()).parallelStream() + .map(f -> PresetTag.loadFromFile(f)) + .filter(p -> p != null) + .collect(Collectors.toCollection(ArrayList::new))).get()); + + } catch (InterruptedException | ExecutionException e) { + e.printStackTrace(); + return; + } + + selected.addAll(loadSelFromFile(presetsDir, presets)); + sort(); + } + + public SubsetListBox() + { + super(0, 0, 0, 0); + setSmoothScroll(false); + } + + public void addTag(final PresetTag tag, boolean sel) + { + presets.add(tag); + + if (sel) { + selected.add(tag); + } + } + + protected List getPresets() + { + return presets; + } + + protected List getSelected() + { + return selected; + } + + public void replaceSeleted(final PresetTag tag) + { + + if (selected.size() == 1) { + selected.get(0).deleteFile(); + presets.set(presets.indexOf(selected.get(0)), tag); + selected.set(0, tag); + } else { + presets.add(tag); + selected.add(tag); + } + + tag.saveToFile(); + saveSelToFile(); + sort(); + } + + public void sort() + { + presets.sort((o1, o2) -> o1.displayName.compareTo(o2.displayName)); + } + + public void clear() + { + presets.clear(); + selected.clear(); + } + + @Override + public int getSlotHeight(int slot) + { + return 18; + } + + @Override + protected int getNumSlots() + { + return presets.size(); + } + + @Override + protected void slotClicked(int slot, int button, int mx, int my, int count) + { + + if (slot >= presets.size()) { + return; + } + + final PresetTag tag = presets.get(slot); + final int width = windowBounds().width; + + final Rectangle4i direction = new Rectangle4i(0, 0, 18, 18); + final Rectangle4i delete = new Rectangle4i(width - 18, 0, 18, 18); + final Rectangle4i option = new Rectangle4i(18, 0, width - 18 - 18, 18); + + if (option.contains(mx, my)) { + final boolean sel = selected.contains(tag); + + if (NEIClientUtils.controlKey()) { + + if (sel) { + selected.remove(tag); + } else { + selected.add(tag); + } + + } else if (NEIClientUtils.shiftKey()) { + selected.clear(); + + for (int i = Math.min(lastSelectedIndex, slot); i <= Math.max(lastSelectedIndex, slot); i++) { + if (!selected.contains(presets.get(i))) { + selected.add(presets.get(i)); + } + } + + } else { + final int size = selected.size(); + selected.clear(); + + if (!sel || size > 1) { + selected.add(tag); + } + + } + + if (selected.isEmpty()) { + lastSelectedIndex = 0; + } else if (!NEIClientUtils.shiftKey()) { + lastSelectedIndex = slot; + } + + } else if (delete.contains(mx, my)) { + presets.remove(tag); + selected.remove(tag); + tag.deleteFile(); + } else if (direction.contains(mx, my)) { + tag.whitelist = !tag.whitelist; + tag.saveToFile(); + } + + saveSelToFile(); + PresetsWidget.edit = null; + PresetsWidget.openListBox = !presets.isEmpty() && (NEIClientUtils.controlKey() || NEIClientUtils.shiftKey() || !option.contains(mx, my)); + ItemList.updateFilter.restart(); + } + + @Override + protected void drawSlot(int slot, int x, int y, int mx, int my, float frame) + { + if (slot >= presets.size()) { + return; + } + + final int width = windowBounds().width; + final Rectangle4i direction = new Rectangle4i(x, y, 18, 18); + final Rectangle4i delete = new Rectangle4i(x + width - 18, y, 18, 18); + final Rectangle4i option = new Rectangle4i(x + 18, y, width - 18 - 18, 18); + final PresetTag tag = presets.get(slot); + + final int optionState = selected.contains(tag)? 0: option.contains(x + mx, y + my)? 1: 2; + final int directionState = direction.contains(x + mx, y + my)? 1: 2; + final int deleteState = delete.contains(x + mx, y + my)? 1: 2; + + final String displayName = cropText(tag.displayName, option.w - 6); + final String dirName = tag.whitelist? translate("presets.whitelist.label"): translate("presets.blacklist.label"); + + //blacklist or whitelist + LayoutManager.getLayoutStyle().drawSubsetTag(null, direction.x, direction.y, direction.w, direction.h, directionState, false); + GuiDraw.drawString(dirName, direction.x + 6, direction.y + 5, directionState == 2 ? 0xFFE0E0E0 : 0xFFFFA0); + + //option name + LayoutManager.getLayoutStyle().drawSubsetTag(null, option.x, option.y, option.w, option.h, optionState, false); + GuiDraw.drawString(displayName, option.x + 3, option.y + 5, optionState == 2 ? 0xFFE0E0E0 : 0xFFFFA0); + + //remove icon + LayoutManager.getLayoutStyle().drawSubsetTag(null, delete.x, delete.y, delete.w, delete.h, deleteState, false); + GuiDraw.drawString("✕", delete.x + 6, delete.y + 5, deleteState == 2 ? 0xFFE0E0E0 : 0x601010); + + } + + @Override + public void drawOverlay(float frame) + { + } + + @Override + public void drawBackground(float frame) + { + drawRect(x , y, x + width, y + height, 0xFF202020); + } + + @Override + public void drawScrollbar(float frame) + { + if (hasScrollbar()) { + super.drawScrollbar(frame); + } + } + + @Override + public int scrollbarGuideAlignment() + { + return 0; + } + + protected List loadSelFromFile(File presetsDir, List presets) + { + final File file = new File(presetsDir, "selected.ini"); + List selected = new ArrayList<>(); + + if (file.exists()) { + + try { + + List itemStrings; + try (FileInputStream input = new FileInputStream(file)) { + NEIClientConfig.logger.info("Loading presets selected from file {}", file); + itemStrings = IOUtils.readLines(input, "UTF-8"); + } catch (IOException e) { + NEIClientConfig.logger.error("Failed to load presets selected from file {}", file, e); + return selected; + } + + final HashMap map = new HashMap<>(); + + for (PresetTag tag: presets) { + map.put(tag.filename, tag); + } + + for (String filename: itemStrings) { + if (map.containsKey(filename)) { + selected.add(map.get(filename)); + } + } + + } catch (Throwable th) { + NEIClientConfig.logger.error("Failed to load presets selected from file", file); + } + + } + + return selected; + } + + public void saveSelToFile() + { + + final File file = new File(presetsDir, "selected.ini"); + final List list = new ArrayList<>(); + + for (final PresetTag tag: selected) { + list.add(tag.filename); + } + + try (FileOutputStream output = new FileOutputStream(file)) { + IOUtils.writeLines(list, "\n", output, "UTF-8"); + } catch (IOException e) { + NEIClientConfig.logger.error("Filed to save presets selected list to file {}", file, e); + } + + } + + } + protected static class MouseSelection + { + public int startX = -1; + public int startY = -1; + public int startIndex = -1; + public int endIndex = -1; + public HashSet items = new HashSet<>(); + public boolean append = true; + + public MouseSelection(int slotIndex, boolean append) + { + final ItemsGrid grid = ItemPanels.itemPanel.getGrid(); + final Rectangle4i rec = grid.getSlotRect(slotIndex - (grid.getPage() - 1) * grid.getPerPage()); + + this.append = append; + endIndex = slotIndex; + startIndex = slotIndex; + startX = rec.x; + startY = rec.y; + } + + } + + protected final static FontRenderer fontRenderer = Minecraft.getMinecraft().fontRenderer; + protected static SubsetListBox listbox = new SubsetListBox(); + protected static File presetsDir; + + protected static boolean openListBox = false; + protected static PresetTag edit; + protected long lastclicktime; + protected static MouseSelection mouseSelection; + + protected final static Button dropdown = new Button() + { + + public boolean onButtonPress(boolean rightclick) + { + if (!rightclick) { + openListBox = !openListBox; + return true; + } + + return false; + } + + @Override + public String getRenderLabel() + { + return "..."; + } + + }; + + protected final static Label selectedValue = new Label("", false) + { + + @Override + public void draw(int mousex, int mousey) + { + final String text = cropText(this.text, w); + + if (text.equals(this.text)) { + GuiDraw.drawString(text, x + w - fontRenderer.getStringWidth(text), y + (h - 8) / 2, colour); + } else { + GuiDraw.drawString(text, x, y + (h - 8) / 2, colour); + } + + } + + @Override + public List handleTooltip(int mx, int my, List tooltip) + { + if (contains(mx, my)) { + tooltip.add(translate("presets.label.tooltip")); + } + + return tooltip; + } + + }; + + protected final static TextField selectedDisplayName = new TextField("") + { + + public void onTextChange(String oldText) + { + if (edit != null) { + edit.displayName = text(); + } + } + + @Override + public void draw(int mousex, int mousey) + { + super.draw(mousex, mousey); + + if (text().isEmpty()) { + GuiDraw.drawGradientRect(x - 1, y - 1, 1, h + 2, 0xFFcc3300, 0xFFcc3300); //Left + GuiDraw.drawGradientRect(x - 1, y - 1, w + 2, 1, 0xFFcc3300, 0xFFcc3300); //Top + GuiDraw.drawGradientRect(x + w, y - 1, 1, h + 2, 0xFFcc3300, 0xFFcc3300); //Left + GuiDraw.drawGradientRect(x - 1, y + h, w + 2, 1, 0xFFcc3300, 0xFFcc3300); //Bottom + } + + } + + @Override + public List handleTooltip(int mx, int my, List tooltip) + { + if (contains(mx, my) && listbox.getSelected().size() <= 1) { + tooltip.add(translate("presets.textfield.tooltip.1")); + tooltip.add(translate("presets.textfield.tooltip.2")); + } + + return tooltip; + } + + }; + + protected final static Button selectedState = new Button("B") + { + + public boolean onButtonPress(boolean rightclick) + { + if (!rightclick && edit != null) { + edit.whitelist = !edit.whitelist; + return true; + } + + return false; + } + + @Override + public String getRenderLabel() + { + return edit != null && edit.whitelist? translate("presets.whitelist.label"): translate("presets.blacklist.label"); + } + + @Override + public void addTooltips(List tooltip) + { + if (edit != null) { + tooltip.add(edit.whitelist? translate("presets.whitelist.tooltip"): translate("presets.blacklist.tooltip")); + } + } + + }; + + protected static String cropText(String text, int containerWidth) + { + + int textWidth = fontRenderer.getStringWidth(text); + + if (textWidth > containerWidth) { + textWidth += fontRenderer.getStringWidth("..."); + + while (textWidth > containerWidth) { + textWidth -= fontRenderer.getCharWidth(text.charAt(text.length() - 1)); + text = text.substring(0, text.length() - 1); + } + + return text + "..."; + } + + return text; + } + + protected static void setEditedTag(final PresetTag tag) + { + edit = tag; + openListBox = false; + selectedDisplayName.setText(tag != null? tag.displayName: ""); + ItemList.updateFilter.restart(); + } + + public static void addTag(final PresetTag tag) + { + addTag(tag, false); + } + + public static void addTag(final PresetTag tag, boolean sel) + { + listbox.addTag(tag, sel); + } + + public static boolean inEditMode() + { + return edit != null; + } + + public static boolean isHidden(final ItemStack item) + { + + if (edit != null || listbox.getSelected().isEmpty()) { + + if (edit != null) { + + if (mouseSelection != null && mouseSelection.items.contains(item)) { + return mouseSelection.append != edit.whitelist; + } + + return !edit.matches(item); + } + + final List selected = listbox.getSelected(); + + for (int idx = 0; idx < selected.size(); idx++) { + if (!selected.get(idx).matches(item)) { + return true; + } + } + + } + + return false; + } + + public static void setHidden(final ItemStack stack, final boolean append) + { + if (edit == null) { + return; + } + + if (NEIClientUtils.shiftKey()) { + final ArrayList items = ItemPanels.itemPanel.getItems(); + + for (int i = 0; i < items.size(); i++) { + if (stack.getItem().equals(items.get(i).getItem())) { + hideItem(items.get(i), append); + } + } + + } else if (NEIClientUtils.controlKey()) { + final String modId = getModId(stack); + + if (modId == null) { + hideItem(stack, append); + } else { + final ArrayList items = ItemPanels.itemPanel.getItems(); + + for (int i = 0; i < items.size(); i++) { + final String mod = getModId(items.get(i)); + if (mod != null && mod.equals(modId)) { + hideItem(items.get(i), append); + } + } + + } + + } else { + hideItem(stack, append); + } + + } + + protected static void hideItem(final ItemStack stack, boolean append) + { + final String guid = StackInfo.getItemStackGUID(stack); + + if (append) { + edit.items.add(guid); + } else { + edit.items.remove(guid); + } + + } + + protected static String getModId(final ItemStack stack) + { + try { + return GameRegistry.findUniqueIdentifierFor(stack.getItem()).modId; + } catch (Exception ignored) {} + + return null; + } + + public static void loadPresets(final String worldPath) + { + final File dir = new File(CommonUtils.getMinecraftDir(), "saves/NEI/" + worldPath); + presetsDir = new File(CommonUtils.getMinecraftDir(), "saves/NEI/" + worldPath + "/presets"); + + if (!dir.exists()) { + dir.mkdirs(); + } + + if (!presetsDir.exists()) { + presetsDir.mkdirs(); + } + + listbox = new SubsetListBox(presetsDir); + + edit = null; + openListBox = false; + selectedDisplayName.setText(""); + + ItemList.updateFilter.restart(); + } + + // public PresetsWidget() + // { + // API.addItemFilter(this); + // } + + @Override + public void draw(int mx, int my) + { + final int panelHeight = 16; + final int panelWidth = 153; + + w = panelWidth; + h = panelHeight; + y = 2; + x = ItemPanels.itemPanel.x - 4 - w; + + if (edit != null) { + + selectedState.x = x; + selectedState.y = y; + selectedState.w = selectedState.h = panelHeight; + selectedState.draw(mx, my); + + selectedDisplayName.y = y; + selectedDisplayName.h = h; + selectedDisplayName.x = selectedState.x + selectedState.w + 1; + selectedDisplayName.w = w - panelHeight * 2 - 5; + selectedDisplayName.draw(mx, my); + + } else { + final List selected = listbox.getSelected(); + final int size = selected.size(); + + selectedValue.text = size> 1? translate("presets.label.selected", size): (size == 1? selected.get(0).displayName: ""); + selectedValue.y = y; + selectedValue.h = h; + selectedValue.w = w - panelHeight - 4; + selectedValue.x = x; + + selectedValue.draw(mx, my); + } + + + dropdown.y = y; + dropdown.w = dropdown.h = panelHeight; + dropdown.x = x + w - dropdown.w; + dropdown.state = edit != null || listbox.getNumSlots() == 0? 2: 0; + dropdown.draw(mx, my); + + if (openListBox) { + final int scrollbarWidth = listbox.hasScrollbar() ? listbox.scrollbarDim().width : 0; + + listbox.setSize( + x, + y + h + 1, + w, + Math.min(((LayoutManager.searchField.y - h - y) / 18) * 18, listbox.contentHeight()) + ); + + listbox.setMargins(0, 0, scrollbarWidth, 0); + listbox.draw(mx, my, 0); + } + + } + + @Override + public boolean contains(int px, int py) + { + return super.contains(px, py) || openListBox && listbox.contains(px, py); + } + + @Override + public boolean handleClick(int mx, int my, int button) + { + + if (openListBox && listbox.contains(mx, my)) { + listbox.mouseClicked(mx, my, button); + return true; + } + + if (edit != null) { + + if (selectedState.contains(mx, my)) { + selectedState.handleClick(mx, my, button); + return true; + } + + if (selectedDisplayName.contains(mx, my)) { + selectedDisplayName.handleClick(mx, my, button); + return true; + } + + } else { + + if (button == 0 && dropdown.contains(mx, my)) { + openListBox = !openListBox; + return true; + } + + openListBox = false; + + if (button == 0 && selectedValue.contains(mx, my)) { + + if (System.currentTimeMillis() - lastclicktime < 400) { //double click + final List selected = listbox.getSelected(); + final int size = selected.size(); + + if (size <= 1) { + setEditedTag(size == 1? selected.get(0).copy(): new PresetTag("")); + } + + } + + lastclicktime = System.currentTimeMillis(); + } + + } + + return contains(mx, my); + } + + @Override + public boolean handleClickExt(int mx, int my, int button) + { + + if (edit != null && mouseSelection == null) { + ItemPanelSlot slot = ItemPanels.itemPanel.getSlotMouseOver(mx, my); + + if (slot != null && button != 2) { + mouseSelection = new MouseSelection(slot.slotIndex, button == 0); + return true; + } + + } + + return false; + } + + @Override + public boolean handleKeyPress(int keyID, char keyChar) + { + if (keyID == Keyboard.KEY_ESCAPE) { + openListBox = false; + } + + if (edit == null) { + return false; + } + + if (selectedDisplayName.focused() && (keyID == Keyboard.KEY_RETURN || keyID == Keyboard.KEY_NUMPADENTER) && !selectedDisplayName.text().isEmpty()) { + selectedDisplayName.handleKeyPress(keyID, keyChar); + selectedDisplayName.setFocus(false); + listbox.replaceSeleted(edit); + setEditedTag(null); + return true; + } else if (selectedDisplayName.focused()) { + return selectedDisplayName.handleKeyPress(keyID, keyChar); + } else if (keyID == Keyboard.KEY_ESCAPE && !(NEIClientUtils.mc().currentScreen instanceof GuiRecipe)) { + setEditedTag(null); + return true; + } + + return false; + } + + @Override + public void mouseDragged(int mx, int my, int button, long heldTime) + { + + if (openListBox) { + listbox.mouseDragged(mx, my, button, heldTime); + } + + if (edit != null && button != 2 && mouseSelection != null) { + final ItemPanelSlot slot = ItemPanels.itemPanel.getSlotMouseOver(mx, my); + + if (slot != null && slot.slotIndex != mouseSelection.endIndex) { + final ItemsGrid grid = ItemPanels.itemPanel.getGrid(); + mouseSelection.endIndex = slot.slotIndex; + mouseSelection.items.clear(); + + final Rectangle4i rec = grid.getSlotRect(slot.slotIndex - (grid.getPage() - 1) * grid.getPerPage()); + final Rectangle4i sel = new Rectangle4i( + Math.min(rec.x, mouseSelection.startX), + Math.min(rec.y, mouseSelection.startY), + Math.max(rec.x, mouseSelection.startX) - Math.min(rec.x, mouseSelection.startX), + Math.max(rec.y, mouseSelection.startY) - Math.min(rec.y, mouseSelection.startY) + ); + + for (int x = sel.x; x <= sel.x + sel.w; x += ItemsGrid.SLOT_SIZE) { + for (int y = sel.y; y <= sel.y + sel.h; y += ItemsGrid.SLOT_SIZE) { + ItemStack stack = ItemPanels.itemPanel.getStackMouseOver(x, y); + + if (stack != null) { + mouseSelection.items.add(stack); + } + + } + } + + } + + } + + } + + @Override + public void mouseUp(int mx, int my, int button) + { + + if (openListBox) { + listbox.mouseMovedOrUp(mx, my, button); + } + + if (edit != null && mouseSelection != null) { + ItemPanelSlot hoverSlot = ItemPanels.itemPanel.getSlotMouseOver(mx, my); + + if (hoverSlot != null && hoverSlot.slotIndex == mouseSelection.startIndex) { + setHidden(hoverSlot.item, button == 0); + } else if (!mouseSelection.items.isEmpty()) { + + for (ItemStack stack : mouseSelection.items) { + hideItem(stack, mouseSelection.append); + } + + } + + mouseSelection = null; + } + + } + + @Override + public boolean onMouseWheel(int i, int mx, int my) + { + + if (openListBox && listbox.hasScrollbar() && listbox.contains(mx, my)) { + listbox.scroll(-i); + return true; + } + + return false; + } + + @Override + public void onGuiClick(int mx, int my) + { + + if (!contains(mx, my)) { + openListBox = false; + } + + selectedDisplayName.onGuiClick(mx, my); + } + + @Override + public List handleTooltip(int mx, int my, List tooltip) + { + + if (edit != null) { + + if (selectedState.contains(mx, my)) { + return selectedState.handleTooltip(mx, my, tooltip); + } + + if (selectedDisplayName.contains(mx, my)) { + return selectedDisplayName.handleTooltip(mx, my, tooltip); + } + + } else { + + if (selectedValue.contains(mx, my)) { + return selectedValue.handleTooltip(mx, my, tooltip); + } + + } + + return tooltip; + } + + public static boolean matches(ItemStack stack) + { + final List selected = listbox.getSelected(); + + for (int idx = 0; idx < selected.size(); idx++) { + if (!selected.get(idx).matches(stack)) { + return false; + } + } + + return true; + } + +} diff --git a/src/main/java/codechicken/nei/recipe/StackInfo.java b/src/main/java/codechicken/nei/recipe/StackInfo.java index 55db1bd5..535d8b2f 100644 --- a/src/main/java/codechicken/nei/recipe/StackInfo.java +++ b/src/main/java/codechicken/nei/recipe/StackInfo.java @@ -1,19 +1,35 @@ package codechicken.nei.recipe; +import codechicken.nei.NEIClientConfig; import codechicken.nei.api.IStackStringifyHandler; import codechicken.nei.recipe.stackinfo.DefaultStackStringifyHandler; import codechicken.nei.recipe.stackinfo.GTFluidStackStringifyHandler; import net.minecraft.item.ItemStack; +import net.minecraft.nbt.NBTBase; import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.nbt.NBTTagList; import net.minecraftforge.fluids.FluidStack; +import org.apache.commons.io.IOUtils; +import java.io.BufferedReader; +import java.io.File; +import java.io.FileReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.net.URL; import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.WeakHashMap; public class StackInfo { - public static final ArrayList stackStringifyHandlers = new ArrayList<>(); + public static final ArrayList stackStringifyHandlers = new ArrayList<>(); + private static final HashMap> guidfilters = new HashMap<>(); + private static final WeakHashMap guidcache = new WeakHashMap<>(); + static { stackStringifyHandlers.add(new DefaultStackStringifyHandler()); stackStringifyHandlers.add(new GTFluidStackStringifyHandler()); @@ -39,7 +55,7 @@ public class StackInfo { ItemStack stack = null; - for (int i = stackStringifyHandlers.size() - 1; i >= 0 && stack == null; i--) { + for (int i = stackStringifyHandlers.size() - 1; i >= 0 && nbtTag != null && stack == null; i--) { stack = stackStringifyHandlers.get(i).convertNBTToItemStack(nbtTag); } @@ -73,4 +89,119 @@ public class StackInfo return fluid; } + public static String getItemStackGUID(ItemStack stack) + { + if (!guidcache.containsKey(stack)) { + + final NBTTagCompound nbTag = itemStackToNBT(stack, false); + + if (nbTag == null) { + return null; + } + + nbTag.removeTag("Count"); + + if (nbTag.getShort("Damage") == 0) { + nbTag.removeTag("Damage"); + } + + if (nbTag.hasKey("tag") && nbTag.getCompoundTag("tag").hasNoTags()) { + nbTag.removeTag("tag"); + } + + if (nbTag.hasKey("strId") && guidfilters.containsKey(nbTag.getString("strId"))) { + final ArrayList keys = new ArrayList<>(); + final String strId = nbTag.getString("strId"); + + keys.add(strId); + + guidfilters.get(strId).forEach((key, rule) -> { + Object local = nbTag; + + for (int i = 0; i < rule.length; i++) { + + try { + + if (local instanceof NBTTagCompound) { + local = ((NBTTagCompound) local).getTag(rule[i]); + } else if (local instanceof NBTTagList) { + local = ((NBTTagList) local).tagList.get(Integer.parseInt(rule[i])); + } else { + break; + } + + } catch (Throwable e) { + break; + } + + } + + if (local instanceof NBTBase) { + keys.add(((NBTBase)local).toString()); + } else if (local != null) { + keys.add(String.valueOf(local)); + } + + }); + + synchronized (guidcache) { + guidcache.put(stack, keys.toString()); + } + } else { + synchronized (guidcache) { + guidcache.put(stack, nbTag.toString()); + } + } + + } + + return guidcache.get(stack); + } + + public static void loadGuidFilters() + { + + guidfilters.clear(); + + final File guidFlitersFile = new File(NEIClientConfig.configDir, "guidfilters.cfg"); + List itemStrings = new ArrayList<>(); + + if (guidFlitersFile.exists()) { + + try (FileReader reader = new FileReader(guidFlitersFile)) { + NEIClientConfig.logger.info("Loading guid filters from file {}", guidFlitersFile); + itemStrings = IOUtils.readLines(reader); + } catch (IOException e) { + NEIClientConfig.logger.error("Failed to load bookmarks from file {}", guidFlitersFile, e); + e.printStackTrace(); + } + + } else { + final URL filterUrl = Thread.currentThread().getContextClassLoader().getResource("assets/nei/guidfilters.cfg"); + + try (BufferedReader reader = new BufferedReader(new InputStreamReader(filterUrl.openStream()))) { + itemStrings = IOUtils.readLines(reader); + } catch (IOException e) { + NEIClientConfig.logger.info("Error parsing guid filters"); + e.printStackTrace(); + } catch (Exception e) { + NEIClientConfig.logger.info("Error parsing guid filters"); + e.printStackTrace(); + } + + } + + for (String guidStr: itemStrings) { + final String[] parts = guidStr.split(","); + final HashMap rules = new HashMap<>(); + + for (int j = 1; j < parts.length; j++) { + rules.put(parts[j], parts[j].split("\\.")); + } + + guidfilters.put(parts[0], rules); + } + + } + } diff --git a/src/main/java/codechicken/nei/util/NBTJson.java b/src/main/java/codechicken/nei/util/NBTJson.java index b734264d..64323e97 100644 --- a/src/main/java/codechicken/nei/util/NBTJson.java +++ b/src/main/java/codechicken/nei/util/NBTJson.java @@ -1,11 +1,8 @@ package codechicken.nei.util; -import com.google.gson.Gson; -import com.google.gson.GsonBuilder; import com.google.gson.JsonArray; import com.google.gson.JsonElement; import com.google.gson.JsonObject; -import com.google.gson.JsonParser; import com.google.gson.JsonPrimitive; import net.minecraft.nbt.NBTBase; import net.minecraft.nbt.NBTTagByte; @@ -20,26 +17,21 @@ import net.minecraft.nbt.NBTTagLong; import net.minecraft.nbt.NBTTagShort; import net.minecraft.nbt.NBTTagString; -import java.io.ByteArrayOutputStream; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; -import java.util.stream.IntStream; public class NBTJson { - private static final Pattern numberPattern = Pattern.compile("^([-+]?[\\d]+\\.?[0-9]*)([bBsSlLfFdD]?)$"); - private static final JsonParser parser = new JsonParser(); - private static final Gson gson = new GsonBuilder().create(); + private static final Pattern numberPattern = Pattern.compile("^([-+]?\\d+\\.?\\d*E*\\d*)([bBsSlLfFdD]?)$"); public static String toJson(NBTTagCompound tag) { return toJson(toJsonObject(tag)); } public static String toJson(JsonElement json) { - final String jsonString = json.toString(); - return gson.toJson(parser.parse(jsonString)); + return json.toString(); } @SuppressWarnings("unchecked") @@ -50,9 +42,11 @@ public class NBTJson { final Map tagMap = (Map)nbtTagCompound.tagMap; JsonObject root = new JsonObject(); - for (Map.Entry nbtEntry : tagMap.entrySet() ) { + + for (Map.Entry nbtEntry : tagMap.entrySet()) { root.add(nbtEntry.getKey(), toJsonObject(nbtEntry.getValue())); } + return root; } else if (nbt instanceof NBTTagByte) { // Number (byte) @@ -80,23 +74,48 @@ public class NBTJson { return new JsonPrimitive(((NBTTagString) nbt).func_150285_a_()); } else if (nbt instanceof NBTTagList) { // Tag List - JsonArray arr = new JsonArray(); - ((NBTTagList) nbt).tagList.forEach(c -> arr.add(toJsonObject((NBTBase) c))); - return arr; + final NBTTagList list = (NBTTagList) nbt; + + if (list.tagList.isEmpty()) { + return createEmptyList(list); + } else { + JsonArray arr = new JsonArray(); + list.tagList.forEach(c -> arr.add(toJsonObject((NBTBase) c))); + return arr; + } + } else if (nbt instanceof NBTTagIntArray) { // Int Array - JsonArray arr = new JsonArray(); - for(int i : ((NBTTagIntArray) nbt).func_150302_c()) { - arr.add(new JsonPrimitive(i)); + final NBTTagIntArray list = (NBTTagIntArray) nbt; + + if (list.func_150302_c().length == 0) { + return createEmptyList(list); + } else { + JsonArray arr = new JsonArray(); + + for (int i : list.func_150302_c()) { + arr.add(new JsonPrimitive(i)); + } + + return arr; } - return arr; + } else if (nbt instanceof NBTTagByteArray) { // Byte Array - JsonArray arr = new JsonArray(); - for(int i : ((NBTTagByteArray) nbt).func_150292_c()) { - arr.add(new JsonPrimitive(i)); + final NBTTagByteArray list = (NBTTagByteArray) nbt; + + if (list.func_150292_c().length == 0) { + return createEmptyList(list); + } else { + JsonArray arr = new JsonArray(); + + for (byte i : list.func_150292_c()) { + arr.add(new JsonPrimitive(Byte.toString(i) + 'B')); + } + + return arr; } - return arr; + } else { throw new IllegalArgumentException("Unsupported NBT Tag: " + NBTBase.NBTTypes[nbt.getId()] + " - " + nbt); } @@ -108,7 +127,7 @@ public class NBTJson { final JsonPrimitive jsonPrimitive = (JsonPrimitive) jsonElement; final String jsonString = jsonPrimitive.getAsString(); final Matcher m = numberPattern.matcher(jsonString); - if(m.find()) { + if (m.find()) { // Number final String numberString = m.group(1); if (m.groupCount() == 2 && m.group(2).length() > 0) { @@ -152,7 +171,13 @@ public class NBTJson { if (nbtList.stream().allMatch(n -> n instanceof NBTTagInt)) { return new NBTTagIntArray(nbtList.stream().mapToInt(i -> ((NBTTagInt)i).func_150287_d() ).toArray()); } else if (nbtList.stream().allMatch(n -> n instanceof NBTTagByte)) { - return new NBTTagByteArray(toByteArray(nbtList.stream().mapToInt(i -> ((NBTTagInt)i).func_150287_d() ))); + final byte[] abyte = new byte[nbtList.size()]; + + for (int i = 0; i < nbtList.size(); i++) { + abyte[i] = ((NBTTagByte) nbtList.get(i)).func_150290_f(); + } + + return new NBTTagByteArray(abyte); } else { NBTTagList nbtTagList = new NBTTagList(); nbtList.forEach(nbtTagList::appendTag); @@ -163,18 +188,43 @@ public class NBTJson { } else if (jsonElement instanceof JsonObject) { // NBTTagCompound final JsonObject jsonObject = (JsonObject) jsonElement; - NBTTagCompound nbtTagCompound = new NBTTagCompound(); - for (Map.Entry jsonEntry : jsonObject.entrySet()) { - nbtTagCompound.setTag(jsonEntry.getKey(), toNbt(jsonEntry.getValue())); + final NBTBase custom = restoreEmptyList(jsonObject); + + if (custom == null) { + NBTTagCompound nbtTagCompound = new NBTTagCompound(); + + for (Map.Entry jsonEntry : jsonObject.entrySet()) { + nbtTagCompound.setTag(jsonEntry.getKey(), toNbt(jsonEntry.getValue())); + } + + return nbtTagCompound; + } else { + return custom; } - return nbtTagCompound; + } throw new IllegalArgumentException("Unhandled element " + jsonElement); } - public static byte[] toByteArray(IntStream stream) { - return stream.collect(ByteArrayOutputStream::new, (baos, i) -> baos.write((byte) i), (baos1, baos2) -> baos1.write(baos2.toByteArray(), 0, baos2.size())) - .toByteArray(); + + protected static JsonObject createEmptyList(Object obj) + { + JsonObject empty = new JsonObject(); + empty.add("__custom_type", new JsonPrimitive(obj.getClass().getName())); + return empty; + } + + protected static NBTBase restoreEmptyList(JsonObject obj) + { + + if (obj.has("__custom_type")) { + try { + final String className = obj.get("__custom_type").getAsString(); + return (NBTBase)Class.forName(className).newInstance(); + } catch (ClassNotFoundException | InstantiationException | IllegalAccessException th) {} + } + + return null; } } diff --git a/src/main/resources/assets/nei/guidfilters.cfg b/src/main/resources/assets/nei/guidfilters.cfg new file mode 100644 index 00000000..c94f1605 --- /dev/null +++ b/src/main/resources/assets/nei/guidfilters.cfg @@ -0,0 +1,56 @@ +AdvancedSolarPanel:hybrid_solar_helmet,Damage +BuildCraft|Transport:pipeFacade +DraconicEvolution:draconicAxe,tag.Energy +DraconicEvolution:draconicPickaxe,tag.Energy +DraconicEvolution:wyvernPickaxe,tag.Energy +ExtraUtilities:microblocks,Damage +Forestry:beePrincessGE,tag.Genome.Chromosomes +ForgeMicroblock:microblock,Damage +Genetics:serumArray,tag.genes +GraviSuite:itemPlasmaCell,tag.Fluid.FluidName +IC2:itemCropSeed,tag.name +IC2:itemFluidCell,tag.Fluid.FluidName +NodalMechanics:item.nodalmechanics.matrix,tag.Aspects.0.key +TConstruct:ArrowAmmo,tag.InfiTool +TConstruct:BoltAmmo,tag.InfiTool +TConstruct:Crossbow,tag.InfiTool +TConstruct:Javelin,tag.InfiTool +TConstruct:LongBow,tag.InfiTool +TConstruct:ShortBow,tag.InfiTool +TConstruct:Shuriken,tag.InfiTool +TConstruct:ThrowingKnife,tag.InfiTool +TConstruct:battleaxe,tag.InfiTool +TConstruct:battlesign,tag.InfiTool +TConstruct:broadsword,tag.InfiTool +TConstruct:chisel,tag.InfiTool +TConstruct:cleaver,tag.InfiTool +TConstruct:cutlass,tag.InfiTool +TConstruct:dagger,tag.InfiTool +TConstruct:excavator,tag.InfiTool +TConstruct:frypan,tag.InfiTool +TConstruct:hammer,tag.InfiTool +TConstruct:hatchet,tag.InfiTool +TConstruct:longsword,tag.InfiTool +TConstruct:lumberaxe,tag.InfiTool +TConstruct:mattock,tag.InfiTool +TConstruct:pickaxe,tag.InfiTool +TConstruct:potionLauncher +TConstruct:rapier,tag.InfiTool +TConstruct:scythe,tag.InfiTool +TConstruct:shovel,tag.InfiTool +Thaumcraft:ItemEssence,tag.Aspects.0.key +Thaumcraft:ItemWispEssence,tag.Aspects.0.key +WitchingGadgets:item.WG_InfusedGem,tag.Aspect +adventurebackpack:adventureBackpack,Damage +appliedenergistics2:item.ItemFacade +bartworks:BioLabParts,Damage,tag.Name +battlegear2:knights_armour.boots +battlegear2:knights_armour.helmet +battlegear2:knights_armour.legs +battlegear2:knights_armour.plate +miscutils:itemBlueprint +miscutils:particleIon,Damage +miscutils:personalHealingDevice +tectech:item.em.debugContainer,tag.symbols +tinkersdefense:Heater Shield,tag.InfiTool +tinkersdefense:Round Shield,tag.InfiTool \ No newline at end of file diff --git a/src/main/resources/assets/nei/lang/en_US.lang b/src/main/resources/assets/nei/lang/en_US.lang index 510b388d..c48fc2eb 100644 --- a/src/main/resources/assets/nei/lang/en_US.lang +++ b/src/main/resources/assets/nei/lang/en_US.lang @@ -111,6 +111,9 @@ nei.options.inventory=Inventory nei.options.inventory.worldSpecificBookmarks=World Specific Bookmarks nei.options.inventory.worldSpecificBookmarks.true=Yes nei.options.inventory.worldSpecificBookmarks.false=No +nei.options.inventory.worldSpecificPresets=World Specific Presets +nei.options.inventory.worldSpecificPresets.true=Yes +nei.options.inventory.worldSpecificPresets.false=No nei.options.inventory.bookmarksEnabled=Bookmarks Panel Visibility nei.options.inventory.bookmarksEnabled.true=Visible nei.options.inventory.bookmarksEnabled.false=Hidden @@ -281,4 +284,13 @@ nei.subsets.Items.Tools.Shovels=Shovels nei.subsets.Items.Weapons=Weapons nei.subsets.Items.Weapons.Ranged=Ranged nei.subsets.Items.Weapons.Swords=Swords -nei.subsets.Mod=Mod \ No newline at end of file +nei.subsets.Mod=Mod + +nei.presets.blacklist.label=B +nei.presets.blacklist.tooltip=Blacklist +nei.presets.whitelist.label=W +nei.presets.whitelist.tooltip=Whitelist +nei.presets.textfield.tooltip.1=Enter - save +nei.presets.textfield.tooltip.2=Escape - cancel +nei.presets.label.tooltip=Double click to edit +nei.presets.label.selected=%s Presets \ No newline at end of file