/*
* The FML Forge Mod Loader suite.
* Copyright (C) 2012 cpw
*
* This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free
* Software Foundation; either version 2.1 of the License, or any later version.
*
* This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
* A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51
* Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package net.minecraftforge.fml.client;
import java.awt.Dimension;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import javax.imageio.ImageIO;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.FontRenderer;
import net.minecraft.client.gui.GuiButton;
import net.minecraft.client.gui.GuiScreen;
import net.minecraft.client.gui.GuiTextField;
import net.minecraft.client.renderer.Tessellator;
import net.minecraft.client.renderer.WorldRenderer;
import net.minecraft.client.renderer.texture.DynamicTexture;
import net.minecraft.client.renderer.texture.TextureManager;
import net.minecraft.client.resources.I18n;
import net.minecraft.client.resources.IResourcePack;
import net.minecraft.util.ResourceLocation;
import net.minecraft.util.StringUtils;
import net.minecraftforge.fml.common.FMLLog;
import net.minecraftforge.fml.common.Loader;
import net.minecraftforge.fml.common.ModContainer;
import net.minecraftforge.fml.common.ModContainer.Disableable;
import org.apache.logging.log4j.Level;
import org.lwjgl.opengl.GL11;
import com.google.common.base.Strings;
/**
* @author cpw
*
*/
public class GuiModList extends GuiScreen
{
private enum SortType implements Comparator<ModContainer>
{
NORMAL(24),
A_TO_Z(25){ @Override protected int compare(String name1, String name2){ return name1.compareTo(name2); }},
Z_TO_A(26){ @Override protected int compare(String name1, String name2){ return name2.compareTo(name1); }};
private int buttonID;
private SortType(int buttonID)
{
this.buttonID = buttonID;
}
public static SortType getTypeForButton(GuiButton button)
{
for (SortType t : values())
{
if (t.buttonID == button.id)
{
return t;
}
}
return null;
}
protected int compare(String name1, String name2){ return 0; }
@Override
public int compare(ModContainer o1, ModContainer o2)
{
String name1 = StringUtils.stripControlCodes(o1.getName()).toLowerCase();
String name2 = StringUtils.stripControlCodes(o2.getName()).toLowerCase();
return compare(name1, name2);
}
}
private GuiScreen mainMenu;
private GuiSlotModList modList;
private int selected = -1;
private ModContainer selectedMod;
private int listWidth;
private ArrayList<ModContainer> mods;
private GuiButton configModButton;
private GuiButton disableModButton;
private ResourceLocation cachedLogo;
private Dimension cachedLogoDimensions;
private int buttonMargin = 1;
private int numButtons = SortType.values().length;
private String lastFilterText = "";
private GuiTextField search;
private boolean sorted = false;
private SortType sortType = SortType.NORMAL;
/**
* @param mainMenu
*/
public GuiModList(GuiScreen mainMenu)
{
this.mainMenu = mainMenu;
this.mods = new ArrayList<ModContainer>();
FMLClientHandler.instance().addSpecialModEntries(mods);
// Add child mods to their parent's list
for (ModContainer mod : Loader.instance().getModList())
{
if (mod.getMetadata() != null && mod.getMetadata().parentMod == null && !Strings.isNullOrEmpty(mod.getMetadata().parent))
{
String parentMod = mod.getMetadata().parent;
ModContainer parentContainer = Loader.instance().getIndexedModList().get(parentMod);
if (parentContainer != null)
{
mod.getMetadata().parentMod = parentContainer;
parentContainer.getMetadata().childMods.add(mod);
continue;
}
}
else if (mod.getMetadata() != null && mod.getMetadata().parentMod != null)
{
continue;
}
mods.add(mod);
}
}
@SuppressWarnings("unchecked")
@Override
public void initGui()
{
for (ModContainer mod : mods)
{
listWidth = Math.max(listWidth,getFontRenderer().getStringWidth(mod.getName()) + 10);
listWidth = Math.max(listWidth,getFontRenderer().getStringWidth(mod.getVersion()) + 10);
}
listWidth = Math.min(listWidth, 150);
this.modList = new GuiSlotModList(this, mods, listWidth);
this.modList.registerScrollButtons(this.buttonList, 7, 8);
this.buttonList.add(new GuiButton(6, ((modList.right + this.width) / 2) - 100, this.height - 38, I18n.format("gui.done")));
configModButton = new GuiButton(20, 10, this.height - 49, this.listWidth, 20, "Config");
disableModButton = new GuiButton(21, 10, this.height - 27, this.listWidth, 20, "Disable");
this.buttonList.add(configModButton);
this.buttonList.add(disableModButton);
search = new GuiTextField(0, getFontRenderer(), 12, modList.bottom + 17, modList.listWidth - 4, 14);
search.setFocused(true);
search.setCanLoseFocus(true);
int width = (modList.listWidth / numButtons);
int x = 10, y = 10;
GuiButton normalSort = new GuiButton(SortType.NORMAL.buttonID, x, y, width - buttonMargin, 20, I18n.format("fml.menu.mods.normal"));
normalSort.enabled = false;
buttonList.add(normalSort);
x += width + buttonMargin;
buttonList.add(new GuiButton(SortType.A_TO_Z.buttonID, x, y, width - buttonMargin, 20, "A-Z"));
x += width + buttonMargin;
buttonList.add(new GuiButton(SortType.Z_TO_A.buttonID, x, y, width - buttonMargin, 20, "Z-A"));
}
@Override
protected void mouseClicked(int x, int y, int button) throws IOException
{
super.mouseClicked(x, y, button);
search.mouseClicked(x, y, button);
if (button == 1 && x >= search.xPosition && x < search.xPosition + search.width && y >= search.yPosition && y < search.yPosition + search.height) {
search.setText("");
}
}
@Override
protected void keyTyped(char c, int keyCode) throws IOException
{
super.keyTyped(c, keyCode);
search.textboxKeyTyped(c, keyCode);
}
@Override
public void updateScreen()
{
super.updateScreen();
search.updateCursorCounter();
if (!search.getText().equals(lastFilterText))
{
reloadMods();
sorted = false;
}
if (!sorted)
{
reloadMods();
Collections.sort(mods, sortType);
selected = modList.selectedIndex = mods.indexOf(selectedMod);
sorted = true;
}
}
private void reloadMods()
{
ArrayList<ModContainer> mods = modList.getMods();
mods.clear();
for (ModContainer m : Loader.instance().getActiveModList())
{
// If it passes the filter, and is not a child mod
if (m.getName().toLowerCase().contains(search.getText().toLowerCase()) && m.getMetadata().parentMod == null)
{
mods.add(m);
}
}
this.mods = mods;
lastFilterText = search.getText();
}
@Override
protected void actionPerformed(GuiButton button) throws IOException {
if (button.enabled)
{
SortType type = SortType.getTypeForButton(button);
if (type != null)
{
for (GuiButton b : (List<GuiButton>) buttonList)
{
if (SortType.getTypeForButton(b) != null)
{
b.enabled = true;
}
}
button.enabled = false;
sorted = false;
sortType = type;
this.mods = modList.getMods();
}
else
{
switch (button.id)
{
case 6:
this.mc.displayGuiScreen(this.mainMenu);
return;
case 20:
try
{
IModGuiFactory guiFactory = FMLClientHandler.instance().getGuiFactoryFor(selectedMod);
GuiScreen newScreen = guiFactory.mainConfigGuiClass().getConstructor(GuiScreen.class).newInstance(this);
this.mc.displayGuiScreen(newScreen);
}
catch (Exception e)
{
FMLLog.log(Level.ERROR, e, "There was a critical issue trying to build the config GUI for %s", selectedMod.getModId());
}
return;
}
}
}
super.actionPerformed(button);
}
public int drawLine(String line, int offset, int shifty)
{
this.fontRendererObj.drawString(line, offset, shifty, 0xd7edea);
return shifty + 10;
}
@Override
public void drawScreen(int p_571_1_, int p_571_2_, float p_571_3_)
{
this.modList.drawScreen(p_571_1_, p_571_2_, p_571_3_);
this.drawCenteredString(this.fontRendererObj, "Mod List", this.width / 2, 16, 0xFFFFFF);
int offset = this.listWidth + 20;
if (selectedMod != null)
{
GL11.glEnable(GL11.GL_BLEND);
if (!selectedMod.getMetadata().autogenerated)
{
configModButton.visible = true;
disableModButton.visible = true;
disableModButton.packedFGColour = 0xFF3377;
configModButton.enabled = false;
int shifty = 35;
String logoFile = selectedMod.getMetadata().logoFile;
if (!logoFile.isEmpty())
{
GL11.glColor4f(1.0F, 1.0F, 1.0F, 1.0F);
TextureManager tm = mc.getTextureManager();
IResourcePack pack = FMLClientHandler.instance().getResourcePackFor(selectedMod.getModId());
try
{
if (cachedLogo == null)
{
BufferedImage logo = null;
if (pack!=null)
{
logo = pack.getPackImage();
}
else
{
InputStream logoResource = getClass().getResourceAsStream(logoFile);
if (logoResource != null)
{
logo = ImageIO.read(logoResource);
}
}
if (logo != null)
{
cachedLogo = tm.getDynamicTextureLocation("modlogo", new DynamicTexture(logo));
cachedLogoDimensions = new Dimension(logo.getWidth(), logo.getHeight());
}
}
if (cachedLogo != null)
{
this.mc.renderEngine.bindTexture(cachedLogo);
double scaleX = cachedLogoDimensions.width / 200.0;
double scaleY = cachedLogoDimensions.height / 65.0;
double scale = 1.0;
if (scaleX > 1 || scaleY > 1)
{
scale = 1.0 / Math.max(scaleX, scaleY);
}
cachedLogoDimensions.width *= scale;
cachedLogoDimensions.height *= scale;
int top = 32;
Tessellator tess = Tessellator.getInstance();
WorldRenderer world = tess.getWorldRenderer();
world.startDrawingQuads();
world.addVertexWithUV(offset, top + cachedLogoDimensions.height, zLevel, 0, 1);
world.addVertexWithUV(offset + cachedLogoDimensions.width, top + cachedLogoDimensions.height, zLevel, 1, 1);
world.addVertexWithUV(offset + cachedLogoDimensions.width, top, zLevel, 1, 0);
world.addVertexWithUV(offset, top, zLevel, 0, 0);
tess.draw();
shifty += 65;
}
}
catch (IOException e)
{
;
}
}
this.fontRendererObj.drawStringWithShadow(selectedMod.getMetadata().name, offset, shifty, 0xFFFFFF);
shifty += 12;
shifty = drawLine(String.format("Version: %s (%s)", selectedMod.getDisplayVersion(), selectedMod.getVersion()), offset, shifty);
shifty = drawLine(String.format("Mod ID: '%s' Mod State: %s", selectedMod.getModId(), Loader.instance().getModState(selectedMod)), offset, shifty);
if (!selectedMod.getMetadata().credits.isEmpty())
{
shifty = drawLine(String.format("Credits: %s", selectedMod.getMetadata().credits), offset, shifty);
}
shifty = drawLine(String.format("Authors: %s", selectedMod.getMetadata().getAuthorList()), offset, shifty);
shifty = drawLine(String.format("URL: %s", selectedMod.getMetadata().url), offset, shifty);
shifty = drawLine(selectedMod.getMetadata().childMods.isEmpty() ? "No child mods for this mod" : String.format("Child mods: %s", selectedMod.getMetadata().getChildModList()), offset, shifty);
int rightSide = this.width - offset - 20;
if (rightSide > 20)
{
this.getFontRenderer().drawSplitString(selectedMod.getMetadata().description, offset, shifty + 10, rightSide, 0xDDDDDD);
}
Disableable disableable = selectedMod.canBeDisabled();
if (disableable == Disableable.RESTART)
{
disableModButton.enabled = true;
disableModButton.visible = true;
disableModButton.packedFGColour = 0xFF3377;
}
else if (disableable == Disableable.YES)
{
disableModButton.enabled = true;
disableModButton.visible = true;
disableModButton.packedFGColour = 0;
}
else
{
disableModButton.packedFGColour = 0;
disableModButton.visible = true;
disableModButton.enabled = false;
}
IModGuiFactory guiFactory = FMLClientHandler.instance().getGuiFactoryFor(selectedMod);
if (guiFactory == null || guiFactory.mainConfigGuiClass() == null)
{
configModButton.visible = true;
configModButton.enabled = false;
}
else
{
configModButton.visible = true;
configModButton.enabled = true;
}
}
else
{
offset = ( this.listWidth + this.width ) / 2;
this.drawCenteredString(this.fontRendererObj, selectedMod.getName(), offset, 35, 0xFFFFFF);
this.drawCenteredString(this.fontRendererObj, String.format("Version: %s",selectedMod.getVersion()), offset, 45, 0xFFFFFF);
this.drawCenteredString(this.fontRendererObj, String.format("Mod State: %s",Loader.instance().getModState(selectedMod)), offset, 55, 0xFFFFFF);
this.drawCenteredString(this.fontRendererObj, "No mod information found", offset, 65, 0xDDDDDD);
this.drawCenteredString(this.fontRendererObj, "Ask your mod author to provide a mod mcmod.info file", offset, 75, 0xDDDDDD);
configModButton.visible = false;
disableModButton.visible = false;
}
GL11.glDisable(GL11.GL_BLEND);
}
else
{
configModButton.visible = false;
disableModButton.visible = false;
}
super.drawScreen(p_571_1_, p_571_2_, p_571_3_);
String text = I18n.format("fml.menu.mods.search");
int x = ((10 + modList.right) / 2) - (getFontRenderer().getStringWidth(text) / 2);
getFontRenderer().drawString(text, x, modList.bottom + 5, 0xFFFFFF);
search.drawTextBox();
}
Minecraft getMinecraftInstance()
{
return mc;
}
FontRenderer getFontRenderer()
{
return fontRendererObj;
}
public void selectModIndex(int index)
{
this.selected = index;
this.selectedMod = (index >= 0 && index <= mods.size()) ? mods.get(selected) : null;
cachedLogo = null;
}
public boolean modIndexSelected(int index)
{
return index == selected;
}
}