package modmanager.gui;
import modmanager.gui.ModsTable.ViewType;
import org.jdesktop.application.Application;
import org.jdesktop.application.Task;
import org.apache.log4j.Logger;
import com.mallardsoft.tuple.Pair;
import com.mallardsoft.tuple.Tuple;
import modmanager.app.ManagerApp;
import modmanager.business.ManagerOptions;
import modmanager.business.Mod;
import modmanager.controller.Manager;
import modmanager.gui.l10n.L10n;
import modmanager.gui.views.DetailsView;
import modmanager.utility.FileDrop;
import modmanager.utility.OS;
import modmanager.utility.Game;
import modmanager.utility.ZIP;
import modmanager.utility.update.UpdateReturn;
import java.util.Observable;
import java.util.ResourceBundle;
import javax.swing.event.ChangeEvent;
import javax.swing.event.TableColumnModelEvent;
import javax.swing.event.TableColumnModelListener;
import javax.swing.JFileChooser;
import javax.swing.filechooser.FileFilter;
import javax.swing.JCheckBox;
import javax.swing.JOptionPane;
import javax.swing.JTable;
import javax.swing.SwingConstants;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.event.TableModelEvent;
import javax.swing.event.TableModelListener;
import javax.swing.table.JTableHeader;
import javax.swing.table.TableCellRenderer;
import javax.swing.table.TableColumnModel;
import javax.swing.JCheckBoxMenuItem;
import javax.swing.JList;
import javax.swing.filechooser.FileNameExtensionFilter;
import javax.swing.plaf.FontUIResource;
import java.awt.event.ComponentEvent;
import java.awt.Component;
import java.awt.event.ComponentListener;
import java.awt.event.ItemListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.Font;
import java.nio.channels.FileLockInterruptionException;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.UnsupportedEncodingException;
import java.io.IOException;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.Iterator;
import java.util.NoSuchElementException;
import java.util.Observer;
import java.util.Set;
import java.util.prefs.Preferences;
import modmanager.business.modactions.ActionEditFileActions;
import modmanager.business.modactions.ActionEditFileFind;
import modmanager.exceptions.ModConflictException;
import modmanager.exceptions.ModEnabledException;
import modmanager.exceptions.ModNotEnabledException;
import modmanager.exceptions.ModNotFoundException;
import modmanager.exceptions.ModSameNameDifferentVersionsException;
import modmanager.exceptions.ModStreamException;
import modmanager.exceptions.ModVersionMissmatchException;
import modmanager.exceptions.ModVersionUnsatisfiedException;
import modmanager.exceptions.InvalidModActionParameterException;
import modmanager.exceptions.ModDuplicateException;
import modmanager.exceptions.ModFileNotFoundException;
import modmanager.exceptions.ModZipException;
import modmanager.exceptions.NothingSelectedModActionException;
import modmanager.exceptions.StringNotFoundModActionException;
import modmanager.exceptions.UnknowModActionException;
import modmanager.utility.SplashScreenMain;
/**
* Controller for the ManagerGUI. This is the 'controller' part of the MVC framework
* used for handling UI in ModManager. Controller has access to both model and view
* and is responsible for handling events coming from view and calling appropriate
* implementations for these events.
*
* @author Kovo
*/
public class ManagerCtrl implements Observer {
Logger logger = Logger.getLogger(this.getClass().getPackage().getName());
Manager controller;
ManagerOptions model;
static ManagerGUI view;
ListSelectionListener lsl;
private Preferences prefs;
private static ManagerCtrl instance = null;
/**
* Initialize event listeners.
*
* @param model model of the MVC framework
* @param view view of the MVC framework
*/
public ManagerCtrl() {
this.model = ManagerOptions.getInstance();
this.controller = Manager.getInstance();
ManagerCtrl.view = ManagerGUI.getInstance();
this.controller.addObserver(this);
this.model.addObserver(this);
// Set up look and feel
loadLaf();
initViewComponents(view);
// For an unknown reason, if we load the mods after initializing the components our ModList will be correctly sorted.
loadMods();
if (model.getAppliedMods().isEmpty()) {
importModsFromOldModManager();
}
SplashScreenMain.getInstance().splashScreenDestruct();
// Display window
view.fullyLoaded = true;
view.setVisible(true);
try {
if (model.getLastHonVersion() != null && !model.getLastHonVersion().isEmpty() && !Game.getInstance().getVersion().equals(model.getLastHonVersion())) {
if (JOptionPane.showConfirmDialog(view, L10n.getString("message.update.suggest"), L10n.getString("message.update.suggest.title"), JOptionPane.OK_CANCEL_OPTION, JOptionPane.QUESTION_MESSAGE) == 0) {
Task task = new Task<Void, Void>(Application.getInstance()) {
@Override
protected Void doInBackground() throws Exception {
Thread.currentThread().setName("UpdateMods");
view.setInputEnabled(false);
view.setStatusMessage("Updating mods", true);
view.getProgressBar().setVisible(true);
view.getProgressBar().setStringPainted(true);
ArrayList<Mod> toUpdate = new ArrayList<Mod>();
Iterator<Mod> it = model.getMods().iterator();
while (it.hasNext()) {
Mod next = it.next();
if (next.getUpdateCheckUrl() != null && !next.getUpdateCheckUrl().trim().isEmpty()) {
toUpdate.add(next);
}
}
view.getProgressBar().setMaximum(toUpdate.size());
view.getProgressBar().paint(view.getProgressBar().getGraphics());
view.paint(view.getGraphics());
UpdateReturn things = null;
things = controller.updateMod(toUpdate);
it = things.getUpdatedModList().iterator();
String message = "";
if (it.hasNext()) {
message += L10n.getString("message.update.updatedmods") + " \n\n";
while (it.hasNext()) {
Mod mod = it.next();
message += L10n.getString("message.update.updated").replace("#mod#", mod.getName()).replace("#olderversion#", things.getOlderVersion(mod)).replace("#newversion#", mod.getVersion()) + "\n";
}
message += "\n\n";
} else {
message = L10n.getString("message.update.uptodate");
}
it = things.getFailedModList().iterator();
if (it.hasNext()) {
if (!message.isEmpty()) {
message += "\n\n";
}
message += L10n.getString("message.update.failed") + "\n";
while (it.hasNext()) {
Mod mod = it.next();
message += "- " + mod.getName() + " (" + things.getException(mod).getLocalizedMessage() + ")\n";
}
}
view.getProgressBar().setValue(0);
view.getProgressBar().setStringPainted(false);
view.updateModTable();
view.showMessage(message, L10n.getString("message.update.title"), JOptionPane.INFORMATION_MESSAGE);
view.setInputEnabled(true);
return null;
}
};
task.execute();
}
}
} catch (Exception ex) {
}
try {
model.setLastHonVersion(Game.getInstance().getVersion());
} catch (Exception ex) {
}
if (model.getNoOptionsFile()) {
if (model.getGamePath() == null || model.getModPath() == null || model.getGamePath().isEmpty() || model.getModPath().isEmpty()) {
view.showMessage(L10n.getString("error.honmodsfolder"), L10n.getString("error.honmodsfolder.title"), JOptionPane.ERROR_MESSAGE);
view.getItemOpenPreferences().doClick();
} else {
if (JOptionPane.showConfirmDialog(view, L10n.getString("message.importfromoldmodmanager"), L10n.getString("message.importfromoldmodmanager.title"), JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE) == 0) {
importModsFromOldModManager();
}
}
}
ManagerCtrl.instance = this;
logger.info("ManagerCtrl finished initialization.");
}
public static ManagerCtrl getInstance() {
return instance;
}
private void initViewComponents(ManagerGUI view) {
try {
changeFonts(new FontUIResource("Dialog", Font.PLAIN, 14));
} catch (Exception e) {
logger.warn("Font 'Dialog' doesn't exist in system fonts.");
}
// Set up last window position
if (model.getGuiRectangle() != null) {
view.setBounds(model.getGuiRectangle());
}
// Load last column order and widths for details view
DetailsView detailsView = (DetailsView) view.getModsTable().getView(ModsTable.ViewType.DETAILS);
if (detailsView != null && model.getColumnsOrder() != null && !model.getColumnsOrder().isEmpty()) {
detailsView.deserializeColumnOrder(model.getColumnsOrder());
}
if (detailsView != null && model.getColumnsWidth() != null) {
int i = 0;
Iterator<Integer> it = model.getColumnsWidth().iterator();
while (it.hasNext()) {
int width = it.next();
detailsView.setColumnWidth(i, width);
++i;
}
}
// Update table
view.getModsTable().getCurrentView().getComponent().repaint();
// Add listeners to view components
view.buttonAddModAddActionListener(new AddModListener());
view.buttonEnableModAddActionListener(new EnableModListener());
view.popupMenuItemEnableDisableModAddActionListener(new EnableModListener());
view.itemApplyModsAddActionListener(new ApplyModsListener());
view.itemApplyAndLaunchAddActionListener(new ApplyAndLaunchListener());
view.itemUnapplyAllModsAddActionListener(new UnapplyAllModsListener());
view.itemOpenModFolderAddActionListener(new OpenModFolderListener());
view.itemVisitForumThreadAddActionListener(new VisitForumThreadListener());
// ----- DetailsView bug Disable ------
view.itemViewDetailsAddActionListener(new ViewChangeListener(ModsTable.ViewType.DETAILS));
view.itemViewIconsAddActionListener(new ViewChangeListener(ModsTable.ViewType.ICONS));
view.itemViewTilesAddActionListener(new ViewChangeListener(ModsTable.ViewType.TILES));
view.itemViewDetailedIconsAddActionListener(new ViewChangeListener(ModsTable.ViewType.DETAILED_ICONS));
view.itemUseSmallIconsAddActionListener(new SmallIconsListener());
view.itemExportOverviewAddActionListener(new ExportOverviewListener());
view.itemExitAddActionListener(new ExitListener());
view.buttonVisitWebsiteAddActionListener(new VisitWebsiteListener());
view.popupMenuItemVisitWebsiteAddActionListener(new VisitWebsiteListener());
view.buttonApplyLafAddActionListener(new ApplyLafListener());
view.buttonApplyLanguageAddActionListener(new ApplyLanguageListener());
view.buttonOkAddActionListener(new PrefsOkListener());
view.buttonCancelAddActionListener(new PrefsCancelListener());
view.buttonHonFolderAddActionListener(new ChooseFolderHonListener());
view.buttonDevelopingModAddActionListener(new ChooseFolderDevelopingModListener());
view.buttonUpdateModActionListener(new UpdateModListener());
view.popupMenuItemUpdateModAddActionListener(new UpdateModListener());
view.buttonModsFolderAddActionListener(new ChooseFolderModsListener());
view.itemDownloadModUpdates(new DownloadModUpdatesListener());
// DetailsView bug Disable
if (detailsView != null) {
((JTable) detailsView.getComponent()).getColumnModel().addColumnModelListener(new Columns2Listener());
}
view.addComponentListener(new ComponentEventListener());
view.getItemRefreshManager().addActionListener(new RefreshManagerListener());
view.getButtonViewChagelog().addActionListener(new ButtonViewModChangelogListener());
view.popupMenuItemViewChangelogAddActionListener(new ButtonViewModChangelogListener());
view.popupItemMenuDeleteModAddActionListener(new DeleteModListener());
view.getButtonViewModDetails().addActionListener(new ButtonViewModChangelogListener());
view.getButtonLaunchHon().addActionListener(new ApplyAndLaunchListener());
view.itemImportFromOldModManagerAddActionListener(new ImportModsFromOldModManager());
view.getModsTable().addKeyListener(new ModTableKeyListener());
// Add file drop functionality
new FileDrop(view, new DropListener());
// Load the user's chosen view
if (model.getViewType().equals(ManagerOptions.ViewType.DETAILED_ICONS)) {
view.getItemViewDetailedIcons().doClick();
} else if (model.getViewType().equals(ManagerOptions.ViewType.DETAILS)) {
// Little hack for the L10n bug while developing
try {
view.getItemViewDetails().doClick();
} catch (NullPointerException e) {
view.getItemViewIcons().doClick();
}
} else if (model.getViewType().equals(ManagerOptions.ViewType.ICONS)) {
view.getItemViewIcons().doClick();
} else if (model.getViewType().equals(ManagerOptions.ViewType.TILES)) {
view.getItemViewTiles().doClick();
}
// Load the user's small icons preference.
if (model.usingSmallIcons()) {
view.getItemUseSmallIcons().doClick();
}
try {
view.setStatusMessage("<html><font color=#009900>" + (model.getAppliedMods().size()) + "</font>/<font color=#0033cc>" + (model.getMods().size()) + "</font> " + L10n.getString("status.modsenabled") + " - Version: " + Game.getInstance().getVersion() + "</html>", false);
} catch (Exception ex) {
view.setStatusMessage("<html><font color=#009900>" + (model.getAppliedMods().size()) + "</font>/<font color=#0033cc>" + (model.getMods().size()) + "</font> " + L10n.getString("status.modsenabled") + "</html>", false);
}
}
public void changeFonts(FontUIResource f) {
Enumeration<Object> keys = UIManager.getDefaults().keys();
while (keys.hasMoreElements()) {
Object key = keys.nextElement();
Object value = UIManager.get(key);
if (value instanceof javax.swing.plaf.FontUIResource) {
UIManager.put(key, f);
}
}
SwingUtilities.updateComponentTreeUI(view);
SwingUtilities.updateComponentTreeUI(view.getPrefsDialog());
}
/**
* @deprecated not currently used.
*/
public class CheckBoxHeader extends JCheckBox implements TableCellRenderer, MouseListener {
protected CheckBoxHeader rendererComponent;
protected int column;
protected boolean mousePressed = false;
public CheckBoxHeader(ItemListener itemListener) {
rendererComponent = this;
rendererComponent.addItemListener(itemListener);
}
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
if (table != null) {
JTableHeader header = table.getTableHeader();
if (header != null) {
rendererComponent.setForeground(header.getForeground());
rendererComponent.setBackground(header.getBackground());
rendererComponent.setFont(header.getFont());
header.addMouseListener(rendererComponent);
}
}
setColumn(column);
//setBorder(UIManager.getBorder("TableHeader.cellBorder"));
rendererComponent.setHorizontalAlignment(SwingConstants.CENTER);
return rendererComponent;
}
protected void setColumn(int column) {
this.column = column;
}
public int getColumn() {
return column;
}
protected void handleClickEvent(MouseEvent e) {
if (mousePressed && !totalDisable) {
mousePressed = false;
JTableHeader header = (JTableHeader) (e.getSource());
JTable tableView = header.getTable();
TableColumnModel columnModel = tableView.getColumnModel();
int viewColumn = columnModel.getColumnIndexAtX(e.getX());
int thecolumn = tableView.convertColumnIndexToModel(viewColumn);
boolean isToDisable = true;
if (viewColumn == this.column && e.getClickCount() == 1 && thecolumn != -1) {
Iterator<Mod> it = ManagerOptions.getInstance().getMods().iterator();
while (it.hasNext()) {
if (!it.next().isEnabled()) {
isToDisable = false;
}
}
for (int k = 0; k < 5; k++) {
for (int i = 0; i < ManagerOptions.getInstance().getMods().size(); i++) {
Mod m = ManagerOptions.getInstance().getMods().get(i);
if (!isToDisable) {
try {
controller.enableMod(m, ManagerOptions.getInstance().isIgnoreGameVersion());
//logger.info("Mod '" + m.getName() + "' has been ENABLED");
} catch (Exception ex) {
}
} else {
try {
controller.disableMod(m);
//logger.info("Mod '" + m.getName() + "' has been DISABLED");
} catch (Exception ex) {
}
}
}
view.updateModTable();
}
}
try {
this.finalize();
} catch (Throwable ex) {
}
}
}
boolean totalDisable = false;
@Override
protected void finalize() throws Throwable {
totalDisable = true;
super.finalize();
}
public void mouseClicked(MouseEvent e) {
handleClickEvent(e);
((JTableHeader) e.getSource()).repaint();
}
public void mousePressed(MouseEvent e) {
}
public void mouseReleased(MouseEvent e) {
mousePressed = true;
}
public void mouseEntered(MouseEvent e) {
}
public void mouseExited(MouseEvent e) {
}
}
/**
* This method is used to get the running instance of the ManagerGUI class.
* @return the instance.
* @see get()
* @deprecated currently not used.
*/
public static ManagerGUI getGUI() {
return view;
}
private void loadLaf() {
// Get selected LaF and apply it
String lafClass = model.getLaf();
try {
if (lafClass.equals("default") || lafClass.isEmpty()) {
logger.info("Setting LaF to Default");
if (OS.isWindows()) {
try {
UIManager.setLookAndFeel("com.jgoodies.looks.windows.WindowsLookAndFeel");
ManagerOptions.getInstance().setLaf("com.jgoodies.looks.windows.WindowsLookAndFeel");
} catch (Exception e) {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
ManagerOptions.getInstance().setLaf(UIManager.getSystemLookAndFeelClassName());
}
} else {
ManagerOptions.getInstance().setLaf(UIManager.getSystemLookAndFeelClassName());
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
}
SwingUtilities.updateComponentTreeUI(view);
SwingUtilities.updateComponentTreeUI(view.getPrefsDialog());
view.pack();
view.getPrefsDialog().pack();
} else {
logger.info("Changing LaF to " + lafClass);
UIManager.setLookAndFeel(lafClass);
}
// Update UI. Probally not all those methods should be called, but Swing is so complex, that this combo fix all the problems.
SwingUtilities.updateComponentTreeUI(view);
SwingUtilities.updateComponentTreeUI(view.getPrefsDialog());
view.pack();
view.getPrefsDialog().pack();
} catch (Exception ex) {
logger.warn("Unable to change Look and feel: " + ex.getMessage());
//TODO: some error message?
}
}
/**
* This method is called when the mods are being applied, to update the status bar fill percentage.
*/
public void update(Observable o, Object arg) {
if (o.getClass().equals(Manager.class)) {
if (arg.getClass().equals(String.class)) {
view.setStatusMessage(L10n.getString("status.applyingmods") + " - " + arg, true);
} else {
int[] ints = (int[]) arg;
view.getProgressBar().setValue(ints[0]);
}
}
}
private void loadMods() {
view.setInputEnabled(false);
try {
ArrayList<ArrayList<Pair<String, String>>> exs = controller.loadMods(model.isDeveloperMode());
controller.buildGraphs();
Set<Mod> newApplied = new HashSet<Mod>();
Iterator<Mod> applied = model.getAppliedMods().iterator();
while (applied.hasNext()) {
Mod appliedMod = applied.next();
Iterator<Mod> mods = model.getMods().iterator();
while (mods.hasNext()) {
Mod mod = mods.next();
if (appliedMod.equals(mod)) {
newApplied.add(mod);
mod.enable();
}
}
}
model.setAppliedMods(newApplied);
if (!exs.isEmpty()) {
Enumeration en = Collections.enumeration(exs);
String stream = "";
String notfound = "";
String zip = "";
String duplicate = "";
String duplicate2 = "";
boolean before = false;
boolean before2 = false;
boolean before3 = false;
boolean foundMod = false;
while (en.hasMoreElements()) {
ArrayList<Pair<String, String>> e = (ArrayList<Pair<String, String>>) en.nextElement();
Enumeration ex = Collections.enumeration(e);
while (ex.hasMoreElements()) {
Pair<String, String> item = (Pair<String, String>) ex.nextElement();
if (Tuple.get2(item).equalsIgnoreCase("stream")) {
logger.error("ModStreamException: mod:" + Tuple.get1(item));
if (before) {
stream += ", ";
}
stream += Tuple.get1(item);
before = true;
}
if (Tuple.get2(item).equalsIgnoreCase("notfound")) {
logger.error("ModNotFoundException: mods:" + Tuple.get1(item));
if (before) {
notfound += ", ";
}
notfound += Tuple.get1(item);
before = true;
}
if (Tuple.get2(item).equalsIgnoreCase("zip")) {
logger.error("ModZipException: mod:" + Tuple.get1(item));
if (before) {
zip += ", ";
}
zip += Tuple.get1(item);
before = true;
}
if (Tuple.get2(item).equalsIgnoreCase("duplicate")) {
logger.error("ModDuplicateException: mod:" + Tuple.get1(item));
if (!foundMod) {
Iterator<Mod> it = model.getMods().iterator();
while (it.hasNext() && !foundMod) {
Mod mod = it.next();
if (new File(mod.getPath()).getName().equals(Tuple.get1(item))) {
if (before2) {
duplicate += ", ";
}
duplicate += mod.getName() + " " + mod.getVersion();
before2 = true;
foundMod = true;
}
}
} else {
if (before3) {
duplicate2 += ", ";
}
duplicate2 += Tuple.get1(item);
before3 = true;
}
}
}
}
if (!stream.isEmpty() || !zip.isEmpty()) {
view.showMessage(L10n.getString("error.modcorrupt").replace("#mod#", "<strong>" + stream + zip + "</strong>"), L10n.getString("error.modcorrupt.title"), JOptionPane.ERROR_MESSAGE);
}
if (!notfound.isEmpty()) {
view.showMessage(L10n.getString("error.modsnotfound").replace("#mod#", notfound), L10n.getString("error.modsnotfound.title"), JOptionPane.ERROR_MESSAGE);
}
if (!duplicate.isEmpty() || !duplicate2.isEmpty()) {
view.showMessage("<html>" + L10n.getString("error.modduplicate").replace("#mod#", "<strong>" + duplicate + "</strong>").replace("#mod2#", "<strong>" + duplicate2 + "</strong>") + "<br/><br/><strong>" + L10n.getString("error.modduplicate.complement") + "</strong></html>", L10n.getString("error.modduplicate.title"), JOptionPane.ERROR_MESSAGE);
}
}
} catch (IOException ex) {
logger.error("IOException from loadMods()", ex);
view.showMessage(L10n.getString("error.loadmodfiles"), L10n.getString("error.loadmodfiles.title"), JOptionPane.ERROR_MESSAGE);
}
view.setInputEnabled(true);
}
/**
* Listener for detecting changes of selection in the mods table. This is
* used when user selects a row in the table.
*/
class ModTableSelectionListener implements ListSelectionListener {
JTable table;
ModTableSelectionListener(JTable table) {
this.table = table;
}
/**
* On selection change, display details of the newly selected mod
*/
public void valueChanged(ListSelectionEvent e) {
if (e.getSource() == table.getSelectionModel()
&& table.getRowSelectionAllowed()) {
view.displayModDetail();
}
if (e.getValueIsAdjusting()) {
// The mouse button has not yet been released
}
}
}
/**
* Listener for detecting changes of selection in the mods table. This is
* used when user selects a row in the table.
*/
class ModListSelectionListener implements ListSelectionListener {
JList list;
ModListSelectionListener(JList _list) {
this.list = _list;
}
/**
* On selection change, display details of the newly selected mod
*/
public void valueChanged(ListSelectionEvent e) {
if (e.getSource() == list.getSelectionModel()) {
view.displayModDetail();
}
}
}
/**
* File filter for JFileChooser. Only displays files ending with
* .honmod extension
*/
class ModFilter extends FileFilter {
public boolean accept(File f) {
if (f.isDirectory()) {
return true;
}
int dotIndex = f.getName().lastIndexOf(".");
if (dotIndex == -1) {
return false;
}
String extension = f.getName().substring(dotIndex);
if ((extension != null) && ((extension.equals(".honmod")) || (extension.equals(".zip")))) {
return true;
} else {
return false;
}
}
//The description of this filter
public String getDescription() {
return L10n.getString("chooser.filedescription");
}
}
/**
* File filter for JFileChooser. Only displays files ending with
* .honmod extension
*/
class HoNFilter extends FileFilter {
public boolean accept(File f) {
if (f.isDirectory()) {
return true;
}
int dotIndex = f.getName().lastIndexOf(".");
if (dotIndex == -1) {
return false;
}
String extension = f.getName().substring(dotIndex);
if ((extension != null) && (extension.equals(".app"))) {
return true;
} else {
return false;
}
}
//The description of this filter
public String getDescription() {
return L10n.getString("chooser.hondescription");
}
}
/**
* Listener for 'Add mod' button. Opens JFileChooser and lets user select
* one or more honmod files
*/
class AddModListener implements ActionListener {
private String lastfolder = null;
public void actionPerformed(ActionEvent e) {
JFileChooser fc = new JFileChooser(new File("."));
if (lastfolder == null) {
fc.setCurrentDirectory(new File("."));
} else {
fc.setCurrentDirectory(new File(lastfolder));
}
fc.setAcceptAllFileFilterUsed(false);
fc.setMultiSelectionEnabled(true);
ModFilter filter = new ModFilter();
fc.setFileFilter(filter);
int returnVal = fc.showOpenDialog(view);
if (returnVal == JFileChooser.APPROVE_OPTION) {
lastfolder = fc.getSelectedFiles()[0].getParent();
File[] files = fc.getSelectedFiles();
addHonmod(files);
loadMods();
view.getModsTable().redraw();
}
}
}
private void addHonmod(File[] files) {
for (int i = 0; i < files.length; i++) {
if (files[i] != null && (files[i].getName().endsWith(".honmod") || (files[i].getName().endsWith(".zip")))) {
try {
logger.info("Opening adding honmod: " + files[i].getAbsolutePath());
controller.addHonmod(files[i], true);
} catch (ModNotFoundException ex) {
Pair<String, String> item = ex.getMods().get(0);
view.showMessage(L10n.getString("error.modsnotfound").replace("#mod#", Tuple.get1(item)), L10n.getString("error.modsnotfound.title"), JOptionPane.ERROR_MESSAGE);
logger.error("Cannot open honmod file: " + Tuple.get1(item));
} catch (ModStreamException ex) {
Pair<String, String> item = ex.getMods().get(0);
view.showMessage(L10n.getString("error.modcorrupt").replace("#mod#", Tuple.get1(item)), L10n.getString("error.modcorrupt.title"), JOptionPane.ERROR_MESSAGE);
logger.error("Honmod file corrupt: " + Tuple.get1(item));
} catch (ModZipException ex) {
Pair<String, String> item = ex.getMods().get(0);
view.showMessage(L10n.getString("error.modcorrupt").replace("#mod#", Tuple.get1(item)), L10n.getString("error.modcorrupt.title"), JOptionPane.ERROR_MESSAGE);
logger.error("Honmod file corrupt: " + Tuple.get1(item));
} catch (ModDuplicateException ex) {
Pair<String, String> item = ex.getMods().get(0);
view.showMessage("<html>" + L10n.getString("error.modduplicate").replace("#mod#", "<strong>" + Tuple.get1(ex.getMods().get(0)) + "</strong>").replace("#mod2#", "<strong>" + Tuple.get1(ex.getMods().get(1)) + "</strong>") + "<br/><br/><strong>" + L10n.getString("error.modduplicate.complement") + "</strong></html>", L10n.getString("error.modduplicate.title"), JOptionPane.ERROR_MESSAGE);
logger.error("Mod duplicated: " + Tuple.get1(ex.getMods().get(0)) + " - " + Tuple.get1(ex.getMods().get(1)));
} catch (IOException e) {
}
}
}
}
/**
* Listener for changes in the leftmost column of the mods table containing
* checkboxes for enabling/disabling mods
*/
class TableEditListener implements TableModelListener {
public void tableChanged(TableModelEvent e) {
int row = e.getFirstRow();
int column = e.getColumn();
if ((row == -1) || (column == -1)) {
return;
}
Mod mod = view.getSelectedMod();
enableMod(mod);
view.getModsTable().redraw();
}
}
/**
* Listener for clicks on 'Visit website' label in mod details
*/
class VisitWebsiteListener implements ActionListener {
public void actionPerformed(ActionEvent ae) {
if (!controller.openWebsite(view.getModsTable().getSelectedMod().getWebLink())) {
view.showMessage(L10n.getString("error.websitenotsupported"),
L10n.getString("error.websitenotsupported.title"),
JOptionPane.ERROR_MESSAGE);
}
}
}
/**
* Listener for 'Visit website' menu item
*/
class VisitForumThreadListener implements ActionListener {
public void actionPerformed(ActionEvent e) {
controller.openWebsite(ManagerOptions.HOMEPAGE);
}
}
/**
* Listener for 'Apply mods' button/menu item
*/
class ApplyModsListener implements ActionListener {
public void actionPerformed(ActionEvent e) {
Task task = new Task<Void, Void>(Application.getInstance()) {
@Override
protected Void doInBackground() throws Exception {
Thread.currentThread().setName("ApplyMods");
applyMods();
return null;
}
};
task.execute();
}
}
/**
* Listener for 'Apply mods and launch HoN' menu item
*/
class ApplyAndLaunchListener implements ActionListener {
public void actionPerformed(ActionEvent e) {
logger.info("Applying mods and launching HoN...2");
Task task = new Task<Void, Void>(Application.getInstance()) {
@Override
protected Void doInBackground() throws Exception {
Thread.currentThread().setName("ApplyModsAndLaunch");
if (applyMods()) {
try {
Process game = Runtime.getRuntime().exec(new String[]{Game.getInstance().getHonExecutable().getAbsolutePath()});
game = null;
} catch (IOException ex) {
view.showMessage(L10n.getString("message.honnotfound"),
L10n.getString("message.honnotfound.title"),
JOptionPane.ERROR_MESSAGE);
logger.error("HoN couldn't be launched. Hon path=" + model.getGamePath(), ex);
}
if (!model.isDeveloperMode()) {
exit();
}
}
return null;
}
};
task.execute();
}
}
/**
* Listener for 'Unapply all mods' menu item
*/
class UnapplyAllModsListener implements ActionListener {
public void actionPerformed(ActionEvent e) {
view.setInputEnabled(false);
logger.info("Unapplying all mods...");
try {
controller.unapplyMods(model.isDeleteFolderTree());
} catch (SecurityException e1) {
} catch (IOException e1) {
}
view.setInputEnabled(true);
view.showMessage(L10n.getString("message.modsunapplied"),
L10n.getString("message.modsunapplied.title"),
JOptionPane.INFORMATION_MESSAGE);
}
}
/**
* Listener for 'Open mod folder' menu item
*/
class OpenModFolderListener implements ActionListener {
public void actionPerformed(ActionEvent e) {
logger.info("Opening mod folder...");
if (controller.openModFolder() == -1) {
view.showMessage(L10n.getString("error.openfoldernotsupported"),
L10n.getString("error.openfoldernotsupported.title"),
JOptionPane.ERROR_MESSAGE);
}
}
}
class ModTableKeyListener implements KeyListener {
// Don't change this
public void keyTyped(KeyEvent e) {
if (e.getKeyCode() == KeyEvent.VK_ENTER) {
e.consume();
}
}
// This can be edited
public void keyPressed(KeyEvent e) {
if (e.getKeyCode() == KeyEvent.VK_ENTER) {
enableMod(view.getSelectedMod());
e.consume();
} else if (e.getKeyCode() == KeyEvent.VK_DELETE) {
deleteSelectedMod();
}
}
// Don't change this
public void keyReleased(KeyEvent e) {
if (e.getKeyCode() == KeyEvent.VK_ENTER) {
e.consume();
}
}
}
class ModListKeyListener implements KeyListener {
// Don't change this
public void keyTyped(KeyEvent e) {
if (e.getKeyCode() == KeyEvent.VK_ENTER) {
e.consume();
}
}
// This can be edited
public void keyPressed(KeyEvent e) {
if (e.getKeyCode() == KeyEvent.VK_ENTER) {
enableMod(view.getSelectedMod());
view.getModsTable().redraw();
e.consume();
} else if (e.getKeyCode() == KeyEvent.VK_DELETE) {
deleteSelectedMod();
}
}
// Don't change this
public void keyReleased(KeyEvent e) {
if (e.getKeyCode() == KeyEvent.VK_ENTER) {
e.consume();
}
}
}
/**
* Listener for changes to the mod list view type preference.
*/
class ViewChangeListener implements ActionListener {
ModsTable.ViewType viewType;
public ViewChangeListener(ModsTable.ViewType _viewType) {
viewType = _viewType;
}
public void actionPerformed(ActionEvent e) {
if (view.getModsTable().getViewMode() != viewType) {
view.getModsTable().setViewMode(viewType);
if (viewType.equals(ViewType.DETAILED_ICONS)) {
model.setViewType(ManagerOptions.ViewType.DETAILED_ICONS);
} else if (viewType.equals(ViewType.DETAILS)) {
model.setViewType(ManagerOptions.ViewType.DETAILS);
} else if (viewType.equals(ViewType.ICONS)) {
model.setViewType(ManagerOptions.ViewType.ICONS);
} else if (viewType.equals(ViewType.TILES)) {
model.setViewType(ManagerOptions.ViewType.TILES);
}
}
}
}
/**
* Listener for changes to the icon size preference.
*/
class SmallIconsListener implements ActionListener {
public void actionPerformed(ActionEvent e) {
ManagerOptions.getInstance().setUseSmallIcons(((JCheckBoxMenuItem) e.getSource()).isSelected());
view.getModsTable().getCurrentView().applyOptions();
view.getModsTable().redraw();
}
}
/**
* Listener for exporting an overview
*/
class ExportOverviewListener implements ActionListener {
public void actionPerformed(ActionEvent e) {
JFileChooser saveDialog = new JFileChooser(".");
saveDialog.addChoosableFileFilter(new FileNameExtensionFilter("TXT file", "txt"));
if (saveDialog.showSaveDialog(view) == JFileChooser.APPROVE_OPTION) {
File outFile = saveDialog.getSelectedFile();
// TODO: Check if file already exists and warn user.
try {
BufferedWriter outStream = new BufferedWriter(new FileWriter(outFile));
for (Mod mod : model.getMods()) {
outStream.write(mod.getName());
outStream.newLine();
}
outStream.close();
} catch (IOException ex) {
logger.error("Could not open write to file.");
// TODO: Nicer message box here.
}
}
}
}
class UpdateModListener implements ActionListener {
public void actionPerformed(ActionEvent e) {
//view.getProgressBar().setStringPainted(true);
view.setInputEnabled(false);
logger.info("Ctrl: " + e.getActionCommand() + " is called to update");
Mod mod = view.getModsTable().getSelectedMod();
ArrayList<Mod> toUpdate = new ArrayList<Mod>();
toUpdate.add(mod);
UpdateReturn things = null;
things = controller.updateMod(toUpdate);
if (!things.getFailedModList().isEmpty()) {
view.showMessage(L10n.getString("message.update.failed.single").replace("#mod#", things.getFailedModList().get(0).getName()).replace("#reason#", things.getException(mod).getMessage()), L10n.getString("message.update.title"), JOptionPane.INFORMATION_MESSAGE);
} else if (!things.getUpToDateModList().isEmpty()) {
view.showMessage(L10n.getString("message.update.uptodate.single").replace("#mod#", things.getUpToDateModList().get(0).getName()), L10n.getString("message.update.title"), JOptionPane.INFORMATION_MESSAGE);
} else {
view.showMessage(L10n.getString("message.update.updated").replace("#mod#", things.getUpdatedModList().get(0).getName()).replace("#olderversion#", things.getOlderVersion(mod)).replace("#newversion#", mod.getVersion()), L10n.getString("message.update.title"), JOptionPane.INFORMATION_MESSAGE);
}
view.setInputEnabled(true);
view.getModsTable().redraw();
}
}
class ImportModsFromOldModManager implements ActionListener {
public void actionPerformed(ActionEvent e) {
importModsFromOldModManager();
}
}
private void importModsFromOldModManager() {
try {
String extractZipComment = ZIP.extractZipComment(model.getGamePath() + File.separator + "game" + File.separator + "resources999.s2z");
ArrayList<String> modArray = new ArrayList<String>();
ArrayList<String> versionArray = new ArrayList<String>();
BufferedReader br = new BufferedReader(new StringReader(extractZipComment));
String str = null;
boolean isMod = false;
try {
while ((str = br.readLine()) != null) {
if (str.length() > 0) {
if (isMod) {
int start = str.indexOf("(") + 2; // Jump the ( AND jump the 'v' [ModManager outputs in this format (v1.2.5) so we need to avoid that v also]
int end = str.indexOf(")");
versionArray.add(str.substring(start, end));
modArray.add(str.substring(0, start - 3).trim()); // -3 because 2 from the add up there, and 1 to avoid the (
} else if (str.contains("Applied Mods:")) {
isMod = true;
}
}
}
} catch (IOException ex) {
}
for (int i = 0; i < 5; i++) {
Iterator<String> mods = modArray.iterator();
Iterator<String> versions = versionArray.iterator();
while (mods.hasNext() && versions.hasNext()) {
String stringMod = mods.next();
String stringVersion = versions.next();
Mod mod = model.getMod(stringMod, stringVersion);
try {
controller.enableMod(mod, model.isIgnoreGameVersion());
mods.remove();
versions.remove();
} catch (Exception ex) {
}
}
}
} catch (FileNotFoundException e) {
} catch (Exception ex) {
}
view.getModsTable().redraw();
}
class ButtonViewModChangelogListener implements ActionListener {
public void actionPerformed(ActionEvent ae) {
if (ae.getActionCommand().equals("display changelog")) {
view.getPanelModChangelog().setVisible(true);
view.getPanelModDetails().setVisible(false);
} else {
view.getPanelModChangelog().setVisible(false);
view.getPanelModDetails().setVisible(true);
}
}
}
class DeleteModListener implements ActionListener {
public void actionPerformed(ActionEvent e) {
deleteSelectedMod();
}
}
public void deleteSelectedMod() {
Mod m = view.getModsTable().getSelectedMod();
if (JOptionPane.showConfirmDialog(view, L10n.getString("question.deletemod").replace("#mod#", m.getName()), L10n.getString("question.deletemod.title"), JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE) == 0) {
// Pressed Yes
view.deleteSelectedMod();
File f = new File(m.getPath());
if (!f.delete()) {
view.showMessage("Failed to delete file", "Failed", JOptionPane.ERROR_MESSAGE);
model.addMod(m, m.isEnabled());
view.updateModTable();
}
logger.info("Deleting mod " + m.getName());
}
}
class DownloadModUpdatesListener implements ActionListener {
public void actionPerformed(ActionEvent e) {
Task task = new Task<Void, Void>(Application.getInstance()) {
@Override
protected Void doInBackground() throws Exception {
Thread.currentThread().setName("UpdateMods");
view.setInputEnabled(false);
view.setStatusMessage("Updating mods", true);
view.getProgressBar().setVisible(true);
view.getProgressBar().setStringPainted(true);
ArrayList<Mod> toUpdate = new ArrayList<Mod>();
Iterator<Mod> it = model.getMods().iterator();
while (it.hasNext()) {
Mod next = it.next();
if (next.getUpdateCheckUrl() != null && !next.getUpdateCheckUrl().trim().isEmpty()) {
toUpdate.add(next);
}
}
view.getProgressBar().setMaximum(toUpdate.size());
view.getProgressBar().paint(view.getProgressBar().getGraphics());
view.paint(view.getGraphics());
UpdateReturn things = null;
things = controller.updateMod(toUpdate);
it = things.getUpdatedModList().iterator();
String message = "";
if (it.hasNext()) {
message += L10n.getString("message.update.updatedmods") + " \n\n";
while (it.hasNext()) {
Mod mod = it.next();
message += L10n.getString("message.update.updated").replace("#mod#", mod.getName()).replace("#olderversion#", things.getOlderVersion(mod)).replace("#newversion#", mod.getVersion()) + "\n";
//message += mod.getName() + " was updated from " + things.getOlderVersion(mod) + " to " + mod.getVersion() + "\n";
}
message += "\n\n";
} else {
message = L10n.getString("message.update.uptodate");
}
// it = things.getUpToDateModList().iterator();
// if (it.hasNext()) {
// message += L10n.getString("message.update.uptodate") + " \n\n";
// }
it = things.getFailedModList().iterator();
if (it.hasNext()) {
if (!message.isEmpty()) {
message += "\n\n";
}
message += L10n.getString("message.update.failed") + "\n";
while (it.hasNext()) {
Mod mod = it.next();
message += "- " + mod.getName() + " (" + things.getException(mod).getLocalizedMessage() + ")\n";
}
}
view.getProgressBar().setValue(0);
view.getProgressBar().setStringPainted(false);
view.updateModTable();
view.showMessage(message, L10n.getString("message.update.title"), JOptionPane.INFORMATION_MESSAGE);
view.setInputEnabled(true);
return null;
}
};
task.execute();
}
}
class MouseEnableModListener implements MouseListener {
public void mouseClicked(MouseEvent e) {
// Only if it's the main button!
if (view.getModsTable().isEnabled() && e.getClickCount() == 2 && e.getButton() == 1) {
enableMod(view.getSelectedMod());
view.getModsTable().redraw();
}
}
public void mousePressed(MouseEvent e) {
}
public void mouseReleased(MouseEvent e) {
}
public void mouseEntered(MouseEvent e) {
}
public void mouseExited(MouseEvent e) {
}
}
/**
* Listener for 'Enable/disable mod' button on mod details panel
*/
class EnableModListener implements ActionListener {
public void actionPerformed(ActionEvent e) {
if (view.getModsTable().isModSelected()) {
Mod mod = view.getModsTable().getSelectedMod();
enableMod(mod);
// When we click this button, the focus is taken from the
// ModsTable. Give it back!
view.getModsTable().grabFocus();
view.getModsTable().repaint();
view.displayModDetail(mod);
}
}
}
/**
* Listener for Drop action on the main form
*/
class DropListener implements FileDrop.Listener {
public void filesDropped(java.io.File[] files) {
logger.info("Files dropped: " + files.length);
addHonmod(files);
controller.buildGraphs();
view.getModsTable().redraw();
}
}
/**
* Listener for 'Apply LaF' button. Canges LaF of the application
*/
class ApplyLafListener implements ActionListener {
public void actionPerformed(ActionEvent e) {
// Get selected LaF and apply it
String lafClass = view.getSelectedLafClass();
try {
if (lafClass.equals("default")) {
logger.info("Changing LaF to Default");
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
model.setLaf(UIManager.getSystemLookAndFeelClassName());
} else {
logger.info("Changing LaF to " + lafClass);
UIManager.setLookAndFeel(lafClass);
model.setLaf(view.getSelectedLafClass());
}
// Update UI
SwingUtilities.updateComponentTreeUI(view);
SwingUtilities.updateComponentTreeUI(view.getPrefsDialog());
view.pack();
view.getPrefsDialog().pack();
} catch (Exception ex) {
logger.warn("Unable to change Look and feel: " + ex.getMessage());
//TODO: some error message?
}
}
}
/**
* Listener for 'Apply Language' button. Canges LaF of the application
*/
class ApplyLanguageListener implements ActionListener {
public void actionPerformed(ActionEvent e) {
String selectedLanguage = view.getSelectedLanguage();
ResourceBundle backup = L10n.getResource();
try {
if (L10n.load(selectedLanguage)) {
ManagerGUI newone = ManagerGUI.newInstance();
newone.pack();
newone.getPrefsDialog().pack();
initViewComponents(newone);
view.setVisible(false);
view.getPrefsDialog().setVisible(false);
newone.setVisible(true);
ManagerOptions.getInstance().setLanguage(selectedLanguage);
view = newone;
} else {
L10n.setResource(backup);
}
} catch (Exception ex) {
L10n.setResource(backup);
}
}
}
/**
* Listener for 'Ok' button on the preferences dialog
*/
class PrefsOkListener implements ActionListener {
public void actionPerformed(ActionEvent e) {
File f = new File(view.getTextFieldModsFolder());
if (!f.exists()) {
if (!f.mkdirs()) {
view.showMessage(L10n.getString("error.honmodsfolder"), L10n.getString("error.honmodsfolder.title"), JOptionPane.ERROR_MESSAGE);
return;
}
}
String oldModsFolder = model.getModPath();
model.setGamePath(view.getSelectedHonFolder());
model.setCLArgs(view.getCLArguments());
//model.setLaf(view.getSelectedLafClass()); This shall not be saved here, but when the user presses Apply button
//model.setLanguage(view.getSelectedLanguage()); This shall not be saved here, but when the user presses Apply button
model.setModPath(view.getTextFieldModsFolder());
model.setIgnoreGameVersion(view.getIgnoreGameVersion());
model.setAutoUpdate(view.getAutoUpdate());
model.setDeveloperMode(view.getDeveloperMode());
model.setDeleteFolderTree(view.getDeleteFolderTree());
try {
controller.saveOptions();
logger.info("---- Options Saved ----");
logger.info("HoN Folder=" + model.getGamePath());
logger.info("Mods Folder=" + model.getModPath());
logger.info("LaF=" + model.getLaf());
logger.info("Language=" + model.getLanguage());
logger.info("CL=" + model.getCLArgs());
logger.info("AutoUpdate=" + model.isAutoUpdate() + " - IgnoreGameVersion=" + model.isIgnoreGameVersion() + " - DeveloperMode=" + model.isDeveloperMode() + " - DeleteFolderTree=" + model.isDeleteFolderTree());
logger.info("----");
} catch (FileNotFoundException e1) {
} catch (UnsupportedEncodingException e1) {
} catch (IOException e1) {
}
String dest = null;
if (OS.isWindows() || OS.isLinux()) {
dest = ManagerOptions.getInstance().getGamePath() + File.separator + "game" + File.separator;
} else if (OS.isMac()) {
dest = System.getProperty("user.home") + "/Library/Application Support/Heroes of Newerth/game/";
}
for (String s : Manager.getInstance().getResources0FolderTree()) {
File warningFile = new File(dest + s, "! FILES AND FOLDERS HERE WILL BE DELETED ON NEXT APPLY");
try {
warningFile.createNewFile();
} catch (IOException ex) {
}
}
// Hide dialog
view.getPrefsDialog().setVisible(false);
if (!oldModsFolder.equals(model.getModPath())) {
loadMods();
view.updateModTable();
}
view.getModsTable().redraw();
}
}
class RefreshManagerListener implements ActionListener {
public void actionPerformed(ActionEvent e) {
loadMods();
view.getModsTable().redraw();
}
}
/**
* Listener for 'Choose HoN folder' button. Lets user navigate through
* filesystem and select folder where HoN is installed
*/
class ChooseFolderHonListener implements ActionListener {
public void actionPerformed(ActionEvent e) {
JFileChooser fc = new JFileChooser();
view.setTextFieldHonFolder(Game.findHonFolder());
fc.setAcceptAllFileFilterUsed(false);
if (OS.isMac()) {
HoNFilter filter = new HoNFilter();
fc.setFileFilter(filter);
fc.setCurrentDirectory(new File("/Applications"));
} else {
fc.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
}
if (OS.isLinux() || OS.isWindows()) {
if (model.getGamePath() != null && !model.getGamePath().isEmpty()) {
fc.setCurrentDirectory(new File(model.getGamePath()));
}
}
int returnVal = fc.showOpenDialog(view.getPrefsDialog());
if (returnVal == JFileChooser.APPROVE_OPTION) {
File directory = fc.getSelectedFile();
view.setTextFieldHonFolder(directory.getPath());
logger.info("Hon folder selected: " + directory.getPath());
}
if (model.getModPath() == null || model.getGamePath().isEmpty()) {
if (fc.getSelectedFile() != null) {
String modPath = Game.findModFolder(fc.getSelectedFile().getAbsolutePath());
if (modPath != null) {
view.setTextFieldModsFolder(modPath);
}
}
}
}
}
class Columns2Listener implements TableColumnModelListener {
public void columnAdded(TableColumnModelEvent e) {
}
public void columnRemoved(TableColumnModelEvent e) {
}
public void columnSelectionChanged(ListSelectionEvent e) {
}
public void columnMoved(TableColumnModelEvent e) {
saveColumnChanges();
}
public void columnMarginChanged(ChangeEvent e) {
saveColumnChanges();
}
private void saveColumnChanges() {
ArrayList<Integer> temp = new ArrayList<Integer>();
DetailsView detailsView = (DetailsView) view.getModsTable().getView(ModsTable.ViewType.DETAILS);
int lim = ((JTable) detailsView.getComponent()).getColumnCount();
for (int i = 0; i < lim; i++) {
temp.add(detailsView.getColumnWidth(i));
}
model.setColumnsWidth(temp);
model.setColumnsOrder(detailsView.serializeColumnOrder());
wantToSaveOptions();
}
}
class ComponentEventListener implements ComponentListener {
public void componentResized(ComponentEvent e) {
model.setGuiRectangle(view.getBounds());
wantToSaveOptions();
}
public void componentMoved(ComponentEvent e) {
model.setGuiRectangle(view.getBounds());
wantToSaveOptions();
}
public void componentShown(ComponentEvent e) {
}
public void componentHidden(ComponentEvent e) {
}
}
class ChooseFolderModsListener implements ActionListener {
public void actionPerformed(ActionEvent e) {
JFileChooser fc = new JFileChooser();
fc.setAcceptAllFileFilterUsed(false);
if (OS.isMac()) {
HoNFilter filter = new HoNFilter();
fc.setFileFilter(filter);
fc.setCurrentDirectory(new File("/Applications"));
} else {
fc.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
}
if (OS.isLinux() || OS.isWindows()) {
if (model.getModPath() != null && !model.getModPath().isEmpty()) {
fc.setCurrentDirectory(new File(model.getModPath()));
} else if (model.getGamePath() != null && !model.getGamePath().isEmpty()) {
fc.setCurrentDirectory(new File(model.getGamePath()));
}
}
int returnVal = fc.showOpenDialog(view.getPrefsDialog());
if (returnVal == JFileChooser.APPROVE_OPTION) {
File directory = fc.getSelectedFile();
view.setTextFieldModsFolder(directory.getPath());
logger.info("Mods folder selected: " + directory.getPath());
}
}
}
class ChooseFolderDevelopingModListener implements ActionListener {
public void actionPerformed(ActionEvent e) {
JFileChooser fc = new JFileChooser();
fc.setAcceptAllFileFilterUsed(false);
if (OS.isMac()) {
HoNFilter filter = new HoNFilter();
fc.setFileFilter(filter);
} else {
fc.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
}
if (OS.isLinux() || OS.isWindows()) {
if (model.getDevelopingMod() != null && !model.getDevelopingMod().isEmpty()) {
fc.setCurrentDirectory(new File(model.getDevelopingMod()));
} else if (model.getModPath() != null && !model.getModPath().isEmpty()) {
fc.setCurrentDirectory(new File(model.getModPath()));
} else if (model.getGamePath() != null && !model.getGamePath().isEmpty()) {
fc.setCurrentDirectory(new File(model.getGamePath()));
}
}
int returnVal = fc.showOpenDialog(view.getPrefsDialog());
if (returnVal == JFileChooser.APPROVE_OPTION) {
File directory = fc.getSelectedFile();
view.setTextFieldDevelopingMod(directory.getPath());
logger.info("Developing mod folder selected: " + directory.getPath());
}
}
}
private long date;
private void wantToSaveOptions() {
Date d = new Date();
if (date == 0) {
date = d.getTime() + 100;
}
if (date <= d.getTime()) {
try {
Manager.getInstance().saveOptionsNoLog();
date = d.getTime() + 100;
} catch (IOException ex) {
date = d.getTime() + 100;
}
}
}
public void enableMod(Mod mod) {
if (mod.isEnabled()) {
try {
controller.disableMod(mod);
logger.info("Mod '" + mod.getName() + "' " + mod.getVersion() + " has been DISABLED");
} catch (ModEnabledException ex) {
// TODO: Add auto-disable dependencies for at least 2 levels.
view.showMessage(L10n.getString("error.modenabled").replace("#mod#", mod.getName()).replace("#mod2#", ex.toString()), L10n.getString("error.modenabled.title"), JOptionPane.WARNING_MESSAGE);
logger.error("Error disabling mod: " + mod.getName() + " because: " + ex.toString() + " is/are enabled.", ex);
}
} else {
try {
String gameVersion = Game.getInstance().getVersion();
try {
controller.enableMod(mod, model.isIgnoreGameVersion());
logger.info("Mod '" + mod.getName() + "' " + mod.getVersion() + " has been ENABLED");
} catch (NoSuchElementException e1) {
view.showMessage(L10n.getString("error.modnotfound"), L10n.getString("error.modnotfound.title"), JOptionPane.WARNING_MESSAGE);
logger.error("Error enabling mod: " + mod.getName() + " NoSuchElementException", e1);
} catch (NullPointerException e1) {
view.showMessage(L10n.getString("error.pathnotset"), L10n.getString("error.pathnotset.title"), JOptionPane.WARNING_MESSAGE);
logger.error("Error enabling mod: " + mod.getName() + " NullPointerException", e1);
logger.error("Error enabling mod detail: " + e1.getCause().getMessage(), e1);
} catch (ModNotEnabledException e1) {
int response = view.confirmMessage(L10n.getString("error.modnotenabled").replace("#mod#", mod.getName()).replace("#mod2#", e1.toString()) + "\n" + L10n.getString("suggest.suggestmodenable"), L10n.getString("suggest.suggestmodenable.title"), JOptionPane.YES_NO_OPTION);
if (response == JOptionPane.YES_OPTION) {
HashSet<Pair<String, String>> enableMods = e1.getDeps();
Iterator it = enableMods.iterator();
String failed = "";
while (it.hasNext()) {
Pair<String, String> element = (Pair<String, String>) it.next();
Mod target = model.getMod(Tuple.get1(element), Tuple.get2(element));
if (target != null) {
enableMod(target);
} else {
if (!failed.isEmpty()) {
failed += ", ";
}
failed += Tuple.get1(element);
}
}
// Enable the mod (finally)
if (!failed.isEmpty()) {
enableMod(mod);
} else {
view.showMessage(L10n.getString("error.modsnotfound"), L10n.getString("error.modsnotfound.title"), JOptionPane.ERROR_MESSAGE);
}
view.getModsTable().redraw();
}
} catch (ModVersionMissmatchException e1) {
view.showMessage(L10n.getString("error.modversionmissmatch").replace("#mod#", mod.getName()), L10n.getString("error.modversionmissmatch.title"), JOptionPane.WARNING_MESSAGE);
logger.error("Error enabling mod: " + mod.getName() + " because: Game version = " + gameVersion + " - Mod app version = " + e1.getAppVersion() + " ModVersionMissmatchException", e1);
} catch (ModConflictException e1) {
view.showMessage(L10n.getString("error.modconflict").replace("#mod#", mod.getName()).replace("#mod2#", e1.toString()), L10n.getString("error.modconflict.title"), JOptionPane.WARNING_MESSAGE);
logger.error("Error enabling mod: " + mod.getName() + " because there are conflict mods (" + e1.toString() + ") enabled", e1);
} catch (ModVersionUnsatisfiedException e1) {
view.showMessage(L10n.getString("error.modversionunsatisfied").replace("#mod#", mod.getName()).replace("#mod2#", e1.toString()), L10n.getString("error.modversionunsatisfied.title"), JOptionPane.WARNING_MESSAGE);
logger.error("Error enabling mod: " + mod.getName() + " because some mods (" + e1.toString() + ") version(s) is/are not satisfied", e1);
} catch (IllegalArgumentException e1) {
e1.printStackTrace();
} catch (ModSameNameDifferentVersionsException e1) {
view.showMessage(L10n.getString("error.modsameversion").replace("#mod#", mod.getName()), L10n.getString("error.modsameversion.title"), JOptionPane.ERROR_MESSAGE);
logger.error("Error enabling mod: " + mod.getName() + " because another mod is already enabled.", e1);
}
} catch (FileNotFoundException e1) {
view.showMessage(L10n.getString("error.incorrectpath"), L10n.getString("error.incorrectpath.title"), JOptionPane.WARNING_MESSAGE);
logger.error("Error enabling mod: " + mod.getName() + " FileNotFoundException", e1);
} catch (IOException e1) {
}
}
}
public boolean applyMods() {
boolean sucess = false;
view.setInputEnabled(false);
try {
view.setStatusMessage(L10n.getString("status.applyingmods"), true);
view.getProgressBar().setStringPainted(true);
view.getProgressBar().setMaximum(controller.getApplyIterationsCount());
view.getProgressBar().paint(view.getProgressBar().getGraphics());
controller.applyMods(model.isDeveloperMode(), model.isDeleteFolderTree());
view.getModsTable().redraw();
sucess = true;
view.showMessage(L10n.getString("message.modsapplied"), L10n.getString("message.modsapplied.title"), JOptionPane.INFORMATION_MESSAGE);
} catch (FileLockInterruptionException ex) {
logger.error("Error applying mods. Can't write on the resources999.s2z file", ex);
view.showMessage(L10n.getString("error.cantacessfile").replace("#file#", model.getGamePath() + File.separator + "game" + File.separator + "resources999.s2z"), L10n.getString("error.cantacessfile"), JOptionPane.ERROR_MESSAGE);
} catch (NothingSelectedModActionException ex) {
logger.error("Error applying mods. Nothing was selected and a operation that needs something to be selected was called. Mod=" + ex.getName() + " | Version=" + ex.getVersion() + " | ActionClass=" + ex.getAction().getClass(), ex);
String s = L10n.getString("error.modcantapply").replace("#mod#", ex.getName());
if (model.isDeveloperMode()) {
s += "\n\n" + L10n.getString("error.atline").replace("#start#", ex.getAction().getLineStart()).replace("#end#", ex.getAction().getLineEnd());
ActionEditFileActions find = (ActionEditFileActions) ex.getAction();
s = s.replace("#content#", find.getContent());
}
view.showMessage(s, L10n.getString("error.modcantapply.title"), JOptionPane.ERROR_MESSAGE);
} catch (StringNotFoundModActionException ex) {
logger.error("Error applying mods. A find operation didn't find it's string. Mod=" + ex.getName() + " | Version=" + ex.getVersion() + " | String=" + ex.getString() + " | " + ex.getAction().getLineStart(), ex);
String s = L10n.getString("error.modcantapply").replace("#mod#", ex.getName());
if (model.isDeveloperMode()) {
s += "\n\n" + L10n.getString("error.atline").replace("#start#", ex.getAction().getLineStart()).replace("#end#", ex.getAction().getLineEnd());
ActionEditFileActions find = (ActionEditFileActions) ex.getAction();
s = s.replace("#content#", find.getContent());
}
view.showMessage(s, L10n.getString("error.modcantapply.title"), JOptionPane.ERROR_MESSAGE);
} catch (InvalidModActionParameterException ex) {
logger.error("Error applying mods. A operation had a invalid parameter. Mod=" + ex.getName() + " | Version=" + ex.getVersion() + " | ActionClass" + ex.getAction().getClass() + " | " + ex.getAction().getLineStart(), ex);
String s = L10n.getString("error.modcantapply").replace("#mod#", ex.getName());
if (model.isDeveloperMode()) {
s += "\n\n" + L10n.getString("error.atline").replace("#start#", ex.getAction().getLineStart()).replace("#end#", ex.getAction().getLineEnd());
}
view.showMessage(s, L10n.getString("error.modcantapply.title"), JOptionPane.ERROR_MESSAGE);
} catch (ModFileNotFoundException ex) {
logger.error("Error applying mods. A mod tried to load an inexistent file. Mod=" + ex.getName() + " | Version=" + ex.getVersion() + " | ActionClass" + ex.getAction().getClass() + " | File = " + ex.getFile(), ex);
view.showMessage(L10n.getString("error.modcantapply").replace("#mod#", ex.getName()), L10n.getString("error.modcantapply.title"), JOptionPane.ERROR_MESSAGE);
} catch (FileNotFoundException ex) {
logger.error("Error applying mods. A file wasn't found, so it failed to apply.", ex);
view.showMessage(L10n.getString("error.filenotfound").replace("#file#", ex.getMessage()), L10n.getString("error.filenotfound.title"), JOptionPane.ERROR_MESSAGE);
} catch (UnknowModActionException ex) {
logger.error("Error applying mods. A unknown action was found. This message should never be logged.", ex);
view.showMessage(L10n.getString("error.modcantapply").replace("#mod#", ex.getName()), L10n.getString("error.modcantapply.title"), JOptionPane.ERROR_MESSAGE);
} catch (SecurityException ex) {
logger.error("Error applying mods. Security exception found, couldn't do some operations that were needed. " + ex.getClass(), ex);
view.showMessage(L10n.getString("error.cantacessfile").replace("#file#", ex.getMessage()), L10n.getString("error.cantacessfile"), JOptionPane.ERROR_MESSAGE);
} catch (Exception ex) {
logger.error("Error applying mods. A random I/O exception was thrown, can't apply. " + ex.getClass(), ex);
view.showMessage(L10n.getString("error.randomerror"), L10n.getString("error.randomerror.title"), JOptionPane.ERROR_MESSAGE);
} finally {
view.getProgressBar().setValue(0);
view.getProgressBar().setStringPainted(false);
}
view.setInputEnabled(true);
return sucess;
}
/**
* Listener for 'Cancel' button on preferences dialog
*/
class PrefsCancelListener implements ActionListener {
public void actionPerformed(ActionEvent e) {
view.getPrefsDialog().setVisible(false);
}
}
/**
* Listener for 'Exit' menu item
*/
class ExitListener implements ActionListener {
public void actionPerformed(ActionEvent e) {
if (controller.hasUnappliedMods()) {
int option = JOptionPane.showConfirmDialog(view, L10n.getString("message.unappliedmods"), L10n.getString("message.unappliedmods.title"), JOptionPane.YES_NO_CANCEL_OPTION, JOptionPane.WARNING_MESSAGE);
if (option == 0) {
// Yes
// This is blocking... so the animations (progress bar and text)
// won't work. Big TODO
Task task = new Task<Void, Void>(Application.getInstance()) {
@Override
protected Void doInBackground() throws Exception {
Thread.currentThread().setName("Exit");
if (applyMods()) {
exit();
}
return null;
}
};
task.execute();
} else if (option == 1) {
// no
exit();
} else {
return;
}
} else {
exit();
}
}
}
public void exit() {
try {
Manager.getInstance().saveOptions();
} catch (IOException e1) {
logger.error("Unable to save options");
}
logger.info("Closing HonModManager...");
ManagerApp.requestShutdown();
}
}