// Near Infinity - An Infinity Engine Browser and Editor // Copyright (C) 2001 - 2005 Jon Olav Hauglid // See LICENSE.txt for license information package org.infinity.gui; import java.awt.BorderLayout; import java.awt.Dimension; import java.awt.Font; import java.awt.FontMetrics; import java.awt.GridBagConstraints; import java.awt.GridBagLayout; import java.awt.Insets; import java.awt.Toolkit; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.InputEvent; import java.awt.event.ItemEvent; import java.awt.event.ItemListener; import java.awt.event.KeyEvent; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; import java.io.IOException; import java.nio.charset.Charset; import java.nio.file.Files; import java.nio.file.Path; import java.util.ArrayList; import java.util.EnumSet; import java.util.Enumeration; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.prefs.Preferences; import javax.swing.AbstractButton; import javax.swing.ButtonGroup; import javax.swing.Icon; import javax.swing.JCheckBoxMenuItem; import javax.swing.JFileChooser; import javax.swing.JLabel; import javax.swing.JMenu; import javax.swing.JMenuBar; import javax.swing.JMenuItem; import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JPopupMenu; import javax.swing.JRadioButtonMenuItem; import javax.swing.JScrollPane; import javax.swing.JTextPane; import javax.swing.KeyStroke; import javax.swing.UIManager; import javax.swing.UIManager.LookAndFeelInfo; import javax.swing.filechooser.FileNameExtensionFilter; import org.infinity.NearInfinity; import org.infinity.check.BCSIDSChecker; import org.infinity.check.CreInvChecker; import org.infinity.check.DialogChecker; import org.infinity.check.EffectsIndexChecker; import org.infinity.check.IDSRefChecker; import org.infinity.check.ResRefChecker; import org.infinity.check.ResourceUseChecker; import org.infinity.check.ScriptChecker; import org.infinity.check.StringUseChecker; import org.infinity.check.StrrefIndexChecker; import org.infinity.check.StructChecker; import org.infinity.gui.converter.ConvertToBam; import org.infinity.gui.converter.ConvertToBmp; import org.infinity.gui.converter.ConvertToMos; import org.infinity.gui.converter.ConvertToPvrz; import org.infinity.gui.converter.ConvertToTis; import org.infinity.icon.Icons; import org.infinity.resource.Profile; import org.infinity.resource.Resource; import org.infinity.resource.ResourceFactory; import org.infinity.resource.StructureFactory; import org.infinity.resource.key.FileResourceEntry; import org.infinity.resource.key.ResourceEntry; import org.infinity.search.DialogSearcher; import org.infinity.search.SearchFrame; import org.infinity.search.SearchResource; import org.infinity.search.TextResourceSearcher; import org.infinity.updater.UpdateCheck; import org.infinity.updater.UpdateInfo; import org.infinity.updater.Updater; import org.infinity.updater.UpdaterSettings; import org.infinity.util.MassExporter; import org.infinity.util.ObjectString; import org.infinity.util.Pair; import org.infinity.util.StringResource; import org.infinity.util.io.FileManager; public final class BrowserMenuBar extends JMenuBar { public static final String VERSION = "v2.0-20160408"; public static final int OVERRIDE_IN_THREE = 0, OVERRIDE_IN_OVERRIDE = 1, OVERRIDE_SPLIT = 2; public static final LookAndFeelInfo DEFAULT_LOOKFEEL = new LookAndFeelInfo("Metal", "javax.swing.plaf.metal.MetalLookAndFeel"); public static final int RESREF_ONLY = 0, RESREF_REF_NAME = 1, RESREF_NAME_REF = 2; public static final int DEFAULT_VIEW = 0, DEFAULT_EDIT = 1; // Defines platform-specific shortcut key (e.g. Ctrl on Win/Linux, Meta on Mac) private static final int CTRL_MASK = Toolkit.getDefaultToolkit().getMenuShortcutKeyMask(); // Name of the child node in the GUI preferences path private static final String PREFS_PROFILES_NODE = "Profiles"; private static BrowserMenuBar menuBar; private final EditMenu editMenu; private final FileMenu fileMenu; private final GameMenu gameMenu; private final OptionsMenu optionsMenu; private final SearchMenu searchMenu; private final ToolsMenu toolsMenu; private final HelpMenu helpMenu; private final Preferences prefsGui, prefsProfiles; public static BrowserMenuBar getInstance() { return menuBar; } private static JMenuItem makeMenuItem(String name, int menuKey, Icon icon, int shortKey, ActionListener listener) { JMenuItem item = new JMenuItem(name); if (menuKey != -1) item.setMnemonic(menuKey); if (icon != null) item.setIcon(icon); if (shortKey != -1) item.setAccelerator(KeyStroke.getKeyStroke(shortKey, CTRL_MASK)); if (listener != null) item.addActionListener(listener); return item; } // Returns the main Preferences instance private static Preferences getPrefs() { if (getInstance() != null) { return getInstance().prefsGui; } else { return null; } } // Returns the Preferences instance for profile-specific settings private static Preferences getPrefsProfiles() { if (getInstance() != null) { return getInstance().prefsProfiles; } else { return null; } } public BrowserMenuBar() { menuBar = this; prefsGui = Preferences.userNodeForPackage(getClass()); prefsProfiles = prefsGui.node(PREFS_PROFILES_NODE); gameMenu = new GameMenu(); fileMenu = new FileMenu(); editMenu = new EditMenu(); searchMenu = new SearchMenu(); toolsMenu = new ToolsMenu(); optionsMenu = new OptionsMenu(); helpMenu = new HelpMenu(); add(gameMenu); add(fileMenu); add(editMenu); add(searchMenu); add(toolsMenu); add(optionsMenu); add(helpMenu); } public boolean autocheckBCS() { return optionsMenu.optionAutocheckBCS.isSelected(); } public boolean checkScriptNames() { return optionsMenu.optionCheckScriptNames.isSelected(); } public boolean showStrrefs() { return optionsMenu.optionShowStrrefs.isSelected(); } public boolean showDlgTreeIcons() { return optionsMenu.optionDlgShowIcons.isSelected(); } public boolean getHexColorMapEnabled() { return optionsMenu.optionShowHexColored.isSelected(); } public boolean cacheOverride() { return optionsMenu.optionCacheOverride.isSelected(); } public void gameLoaded(Profile.Game oldGame, String oldFile) { gameMenu.gameLoaded(oldGame, oldFile); fileMenu.gameLoaded(); editMenu.gameLoaded(); searchMenu.gameLoaded(); optionsMenu.gameLoaded(); } /** Returns state of "Text: Show Highlight Current Line" */ public boolean getTextHighlightCurrentLine() { return optionsMenu.optionTextHightlightCurrent.isSelected(); } /** Returns state of "Text: Show Line Numbers" */ public boolean getTextLineNumbers() { return optionsMenu.optionTextLineNumbers.isSelected(); } /** Returns state of "Text: Show Whitespace and Tab" */ public boolean getTextWhitespaceVisible() { return optionsMenu.optionTextShowWhiteSpace.isSelected(); } /** Returns state of "Text: Show End of Line" */ public boolean getTextEOLVisible() { return optionsMenu.optionTextShowEOL.isSelected(); } /** Returns the selected BCS color scheme. */ public String getBcsColorScheme() { if (NearInfinity.isDebug() && optionsMenu.getDebugColorScheme() != null) { return optionsMenu.getDebugColorScheme(); } else { return optionsMenu.getBcsColorScheme(); } } /** Returns state of "BCS: Enable Syntax Highlighting" */ public boolean getBcsSyntaxHighlightingEnabled() { return optionsMenu.optionBCSEnableSyntax.isSelected(); } /** Returns state of "BCS: Enable Code Folding" */ public boolean getBcsCodeFoldingEnabled() { return optionsMenu.optionBCSEnableCodeFolding.isSelected(); } /** Returns state of "BCS: Enable Automatic Indentation" */ public boolean getBcsAutoIndentEnabled() { return optionsMenu.optionBCSEnableAutoIndent.isSelected(); } // /** Returns state of "BCS: Enable Auto-Completion" */ // public boolean getBcsAutoCompleteEnabled() // { // return optionsMenu.optionBCSEnableAutoComplete.isSelected(); // } /** Returns the selected GLSL color scheme. */ public String getGlslColorScheme() { if (NearInfinity.isDebug() && optionsMenu.getDebugColorScheme() != null) { return optionsMenu.getDebugColorScheme(); } else { return optionsMenu.getGlslColorScheme(); } } /** Returns the selected LUA color scheme. */ public String getLuaColorScheme() { if (NearInfinity.isDebug() && optionsMenu.getDebugColorScheme() != null) { return optionsMenu.getDebugColorScheme(); } else { return optionsMenu.getLuaColorScheme(); } } /** Returns the selected SQL color scheme. */ public String getSqlColorScheme() { if (NearInfinity.isDebug() && optionsMenu.getDebugColorScheme() != null) { return optionsMenu.getDebugColorScheme(); } else { return optionsMenu.getSqlColorScheme(); } } /** Returns state of "Enable Syntax Highlighting for GLSL" */ public boolean getGlslSyntaxHighlightingEnabled() { return optionsMenu.optionGLSLEnableSyntax.isSelected(); } /** Returns state of "Enable Syntax Highlighting for LUA" */ public boolean getLuaSyntaxHighlightingEnabled() { return optionsMenu.optionLUAEnableSyntax.isSelected(); } /** Returns state of "Enable Syntax Highlighting for SQL" */ public boolean getSqlSyntaxHighlightingEnabled() { return optionsMenu.optionSQLEnableSyntax.isSelected(); } /** Returns state of "Enable Code Folding for GLSL" */ public boolean getGlslCodeFoldingEnabled() { return optionsMenu.optionGLSLEnableCodeFolding.isSelected(); } /** Returns whether to emulate tabs by inserting spaces instead. */ public boolean isTextTabEmulated() { return optionsMenu.optionTextTabEmulate.isSelected(); } /** Returns the number of spaces used for (real or emulated) tabs. */ public int getTextTabSize() { return 1 << (optionsMenu.getTextIndentIndex()+1); } /** Returns the selected BCS indent. */ public String getBcsIndent() { return optionsMenu.getBcsIndent(); } public int getDefaultStructView() { return optionsMenu.getDefaultStructView(); } public LookAndFeelInfo getLookAndFeel() { return optionsMenu.getLookAndFeel(); } public int getOverrideMode() { return optionsMenu.getOverrideMode(); } public int getResRefMode() { return optionsMenu.getResRefMode(); } public Font getScriptFont() { for (int i = 0; i < OptionsMenu.FONTS.length; i++) if (optionsMenu.selectFont[i].isSelected()) return OptionsMenu.FONTS[i]; return OptionsMenu.FONTS[0]; } public String getSelectedCharset() { return optionsMenu.charsetName(optionsMenu.getSelectedButtonData()); } public boolean backupOnSave() { return optionsMenu.optionBackupOnSave.isSelected(); } public boolean ignoreOverrides() { return optionsMenu.optionIgnoreOverride.isSelected(); } public boolean ignoreReadErrors() { return optionsMenu.optionIgnoreReadErrors.isSelected(); } public void resourceEntrySelected(ResourceEntry entry) { fileMenu.resourceEntrySelected(entry); } public void resourceShown(Resource res) { fileMenu.resourceShown(res); } public boolean showOffsets() { return optionsMenu.optionShowOffset.isSelected(); } /** * Returns the language code of the selected game language for Enhanced Edition games. * Returns an empty string if autodetect is selected or game is not part of the Enhanced Edition. */ public String getSelectedGameLanguage() { return optionsMenu.getSelectedGameLanguage(); } /** * Attempts to find a matching bookmark and returns its name. * @param keyPath The path to the game's chitin.key. * @return The bookmark name of a matching game or {@code null} otherwise. */ public String getBookmarkName(Path keyFile) { Bookmark bookmark = gameMenu.getBookmarkOf(keyFile); return (bookmark != null) ? bookmark.getName() : null; } public void storePreferences() { optionsMenu.storePreferences(); gameMenu.storePreferences(); } // -------------------------- INNER CLASSES -------------------------- /////////////////////////////// // Game Menu /////////////////////////////// private static final class GameMenu extends JMenu implements ActionListener { private final JMenuItem gameOpenFile, gameOpenGame, gameRefresh, gameExit, gameCloseTLK, gameProperties, gameBookmarkAdd, gameBookmarkEdit, gameRecentClear; private final JMenu gameRecent = new JMenu("Recently opened games"); private final List<RecentGame> recentList = new ArrayList<RecentGame>(); private final JPopupMenu.Separator gameRecentSeparator = new JPopupMenu.Separator(); private final JMenu gameBookmarks = new JMenu("Bookmarked games"); private final List<Bookmark> bookmarkList = new ArrayList<Bookmark>(); private final JPopupMenu.Separator gameBookmarkSeparator = new JPopupMenu.Separator(); private GameMenu() { super("Game"); setMnemonic(KeyEvent.VK_G); gameOpenFile = makeMenuItem("Open File...", KeyEvent.VK_F, Icons.getIcon(Icons.ICON_OPEN_16), KeyEvent.VK_I, this); add(gameOpenFile); gameOpenGame = makeMenuItem("Open Game...", KeyEvent.VK_O, Icons.getIcon(Icons.ICON_OPEN_16), KeyEvent.VK_O, NearInfinity.getInstance()); gameOpenGame.setActionCommand("Open"); add(gameOpenGame); gameRefresh = makeMenuItem("Refresh Tree", KeyEvent.VK_R, Icons.getIcon(Icons.ICON_REFRESH_16), -1, NearInfinity.getInstance()); gameRefresh.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_F5, 0)); gameRefresh.setActionCommand("Refresh"); add(gameRefresh); gameCloseTLK = makeMenuItem("Release Dialog.tlk Lock", KeyEvent.VK_D, Icons.getIcon(Icons.ICON_RELEASE_16), -1, this); add(gameCloseTLK); gameProperties = makeMenuItem("Game Properties...", KeyEvent.VK_P, Icons.getIcon(Icons.ICON_EDIT_16), -1, this); add(gameProperties); addSeparator(); // adding bookmarked games list gameBookmarks.setMnemonic('b'); add(gameBookmarks); bookmarkList.clear(); int gameCount = getPrefsProfiles().getInt(Bookmark.getEntryCountKey(), 0); for (int i = 0; i < gameCount; i++) { Profile.Game game = Profile.gameFromString(getPrefsProfiles().get(Bookmark.getGameKey(i), Profile.Game.Unknown.toString())); String gamePath = getPrefsProfiles().get(Bookmark.getPathKey(i), null); String gameName = getPrefsProfiles().get(Bookmark.getNameKey(i), null); try { Bookmark b = new Bookmark(gameName, game, gamePath, this); addBookmarkedGame(bookmarkList.size(), b); } catch (NullPointerException e) { // skipping entry } } gameBookmarks.add(gameBookmarkSeparator); gameBookmarkAdd = new JMenuItem("Add current game..."); gameBookmarkAdd.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_B, CTRL_MASK | InputEvent.ALT_DOWN_MASK)); gameBookmarkAdd.addActionListener(this); gameBookmarks.add(gameBookmarkAdd); gameBookmarkEdit = new JMenuItem("Edit bookmarks..."); gameBookmarkEdit.addActionListener(this); gameBookmarks.add(gameBookmarkEdit); gameBookmarkSeparator.setVisible(!bookmarkList.isEmpty()); gameBookmarkEdit.setEnabled(!bookmarkList.isEmpty()); // adding recently opened games list gameRecent.setMnemonic('r'); add(gameRecent); recentList.clear(); for (int i = 0; i < RecentGame.getEntryCount(); i++) { Profile.Game game = Profile.gameFromString(getPrefsProfiles().get(RecentGame.getGameKey(i), Profile.Game.Unknown.toString())); String gamePath = getPrefsProfiles().get(RecentGame.getPathKey(i), null); try { RecentGame rg = new RecentGame(game, gamePath, recentList.size(), this); addLastGame(recentList.size(), rg); } catch (NullPointerException e) { // skipping entry } } gameRecent.add(gameRecentSeparator); gameRecentClear = new JMenuItem("Clear list of recent games"); gameRecentClear.addActionListener(this); gameRecent.add(gameRecentClear); gameRecent.setEnabled(!recentList.isEmpty()); gameRecentSeparator.setVisible(!recentList.isEmpty()); addSeparator(); gameExit = makeMenuItem("Quit", KeyEvent.VK_Q, Icons.getIcon(Icons.ICON_EXIT_16), KeyEvent.VK_Q, NearInfinity.getInstance()); gameExit.setActionCommand("Exit"); add(gameExit); } private void gameLoaded(Profile.Game oldGame, String oldFile) { // updating "Recently opened games" list for (int i = 0; i < recentList.size(); i++) { if (ResourceFactory.getKeyfile().toString().equalsIgnoreCase(recentList.get(i).getPath())) { removeLastGame(i); i--; } } if (oldGame != null && oldGame != Profile.Game.Unknown) { for (int i = 0; i < recentList.size(); i++) { if (oldFile.equalsIgnoreCase(recentList.get(i).getPath())) { removeLastGame(i); i--; } } addLastGame(0, new RecentGame(oldGame, oldFile, 0, this)); } while (recentList.size() > RecentGame.getEntryCount()) { removeLastGame(recentList.size() - 1); } } // Updates list of bookmark menu items private void updateBookmarkedGames() { // 1. remove old bookmark items from menu while (gameBookmarks.getPopupMenu().getComponentCount() > 0) { if (gameBookmarks.getPopupMenu().getComponent(0) != gameBookmarkSeparator) { gameBookmarks.getPopupMenu().remove(0); } else { break; } } // 2. add new bookmark items to menu for (int i = 0, size = bookmarkList.size(); i < size; i++) { gameBookmarks.insert(bookmarkList.get(i).getMenuItem(), i); } gameBookmarkSeparator.setVisible(!bookmarkList.isEmpty()); gameBookmarkEdit.setEnabled(!bookmarkList.isEmpty()); // Updating current game if needed Bookmark bookmark = getBookmarkOf(Profile.getChitinKey()); if (bookmark != null) { Profile.addProperty(Profile.Key.GET_GAME_DESC, Profile.Type.STRING, bookmark.getName()); NearInfinity.getInstance().updateWindowTitle(); } } // Removes the bookmark specified by item index from the list and associated menu private void removeBookmarkedGame(int idx) { if (idx >= 0 && idx < bookmarkList.size()) { Bookmark b = bookmarkList.remove(idx); if (b != null) { b.setActionListener(null); } if (gameBookmarks.getPopupMenu().getComponent(idx) == b.getMenuItem()) { gameBookmarks.getPopupMenu().remove(idx); } else { for (int i = 0, count = gameBookmarks.getPopupMenu().getComponentCount(); i < count; i++) { if (gameBookmarks.getPopupMenu().getComponent(i) == b.getMenuItem()) { gameBookmarks.getPopupMenu().remove(i); break; } } } Profile.addProperty(Profile.Key.GET_GAME_DESC, Profile.Type.STRING, null); NearInfinity.getInstance().updateWindowTitle(); } } // Adds the specified bookmark to the list and associated menu private void addBookmarkedGame(int idx, Bookmark bookmark) { if (idx < 0) { idx = 0; } else if (idx > bookmarkList.size()) { idx = bookmarkList.size(); } // use either separator item or menu item count as upper bounds for inserting new bookmark items int separatorIdx = gameBookmarks.getPopupMenu().getComponentIndex(gameBookmarkSeparator); if (separatorIdx < 0) { separatorIdx = gameBookmarks.getPopupMenu().getComponentCount(); } if (bookmark != null && idx <= separatorIdx) { bookmarkList.add(idx, bookmark); gameBookmarks.insert(bookmark.getMenuItem(), idx); gameBookmarkSeparator.setVisible(!bookmarkList.isEmpty()); gameBookmarkEdit.setEnabled(!bookmarkList.isEmpty()); Profile.addProperty(Profile.Key.GET_GAME_DESC, Profile.Type.STRING, bookmark.getName()); NearInfinity.getInstance().updateWindowTitle(); } } // Adds or replaces the current game to the bookmark section private void addNewBookmark(String name) { if (name != null) { name = name.trim(); if (name.isEmpty()) { name = Profile.getProperty(Profile.Key.GET_GAME_TITLE); } Profile.Game game = Profile.getGame(); String path = Profile.getChitinKey().toAbsolutePath().toString(); Bookmark b = new Bookmark(name, game, path, this); // check whether to replace existing bookmark Bookmark curBookmark = getBookmarkOf(Profile.getChitinKey()); int idx = (curBookmark != null) ? bookmarkList.indexOf(curBookmark) : -1; if (idx >= 0) { // replace existing bookmark removeBookmarkedGame(idx); addBookmarkedGame(idx, b); } else { // add new bookmark addBookmarkedGame(bookmarkList.size(), b); } } else { JOptionPane.showMessageDialog(NearInfinity.getInstance(), "No name specified.", "Error", JOptionPane.ERROR_MESSAGE); } } // Adds the specified last game entry to the list private void addLastGame(int idx, RecentGame rg) { if (rg != null) { if (idx < 0 || idx > recentList.size()) { idx = recentList.size(); } rg.setIndex(idx); recentList.add(idx, rg); gameRecent.insert(rg.getMenuItem(), idx); gameRecent.setEnabled(!recentList.isEmpty()); gameRecentSeparator.setVisible(!recentList.isEmpty()); for (int i = 0; i < recentList.size(); i++) { recentList.get(i).setIndex(i); } } } // Removes the specified last game entry from the list private void removeLastGame(int idx) { if (idx >= 0 && idx < recentList.size()) { recentList.get(idx).clear(); recentList.remove(idx); gameRecent.setEnabled(!recentList.isEmpty()); gameRecentSeparator.setVisible(!recentList.isEmpty()); for (int i = 0; i < recentList.size(); i++) { recentList.get(i).setIndex(i); } } } private void storePreferences() { // storing bookmarks // 1. removing excess bookmark entries from preferences int oldSize = getPrefsProfiles().getInt(Bookmark.getEntryCountKey(), 0); if (oldSize > bookmarkList.size()) { for (int i = bookmarkList.size(); i < oldSize; i++) { getPrefsProfiles().remove(Bookmark.getNameKey(i)); getPrefsProfiles().remove(Bookmark.getPathKey(i)); getPrefsProfiles().remove(Bookmark.getGameKey(i)); } } // 2. storing bookmarks in preferences getPrefsProfiles().putInt(Bookmark.getEntryCountKey(), bookmarkList.size()); for (int i = 0; i < bookmarkList.size(); i++) { Bookmark bookmark = bookmarkList.get(i); getPrefsProfiles().put(Bookmark.getNameKey(i), bookmark.getName()); getPrefsProfiles().put(Bookmark.getPathKey(i), bookmark.getPath()); getPrefsProfiles().put(Bookmark.getGameKey(i), bookmark.getGame().toString()); } // storing recently used games for (int i = 0; i < RecentGame.getEntryCount(); i++) { if (i < recentList.size()) { RecentGame rg = recentList.get(i); getPrefsProfiles().put(RecentGame.getGameKey(i), rg.getGame().toString()); getPrefsProfiles().put(RecentGame.getPathKey(i), rg.getPath()); } else { getPrefsProfiles().remove(RecentGame.getGameKey(i)); getPrefsProfiles().remove(RecentGame.getPathKey(i)); } } } /** Attempts to find a bookmarked game using specified key file path. */ public Bookmark getBookmarkOf(Path keyFile) { if (keyFile != null) { String path = keyFile.toAbsolutePath().toString(); for (Iterator<Bookmark> iter = bookmarkList.iterator(); iter.hasNext();) { Bookmark bookmark = iter.next(); if (bookmark.getPath().equalsIgnoreCase(path)) { return bookmark; } } } return null; } @Override public void actionPerformed(ActionEvent event) { if (event.getSource() == gameOpenFile) { OpenFileFrame openframe = (OpenFileFrame)ChildFrame.getFirstFrame(OpenFileFrame.class); if (openframe == null) { openframe = new OpenFileFrame(); } openframe.setVisible(true); } else if (event.getActionCommand().equals(Bookmark.getCommand())) { // Bookmark item selected int selected = -1; for (int i = 0; i < bookmarkList.size(); i++) { if (event.getSource() == bookmarkList.get(i).getMenuItem()) { selected = i; break; } } if (selected != -1) { Path keyFile = FileManager.resolve(bookmarkList.get(selected).getPath()); if (!Files.isRegularFile(keyFile)) { JOptionPane.showMessageDialog(NearInfinity.getInstance(), bookmarkList.get(selected).getPath() + " could not be found", "Open game failed", JOptionPane.ERROR_MESSAGE); } else { NearInfinity.getInstance().openGame(keyFile); } } } else if (event.getActionCommand().equals(RecentGame.getCommand())) { // Recently opened game item selected int selected = -1; for (int i = 0; i < recentList.size(); i++) { if (event.getSource() == recentList.get(i).getMenuItem()) { selected = i; break; } } if (selected != -1) { Path keyFile = FileManager.resolve(recentList.get(selected).getPath()); if (!Files.isRegularFile(keyFile)) { JOptionPane.showMessageDialog(NearInfinity.getInstance(), recentList.get(selected).getPath() + " could not be found", "Open game failed", JOptionPane.ERROR_MESSAGE); } else { NearInfinity.getInstance().openGame(keyFile); } } } else if (event.getSource() == gameCloseTLK) { StringResource.close(); JOptionPane.showMessageDialog(NearInfinity.getInstance(), "Read lock released", "Release Dialog.tlk", JOptionPane.INFORMATION_MESSAGE); } else if (event.getSource() == gameProperties) { new GameProperties(NearInfinity.getInstance()); } else if (event.getSource() == gameBookmarkAdd) { Object name = null; Bookmark bookmark = getBookmarkOf(Profile.getChitinKey()); if (bookmark != null) { int retVal = JOptionPane.showConfirmDialog(NearInfinity.getInstance(), "The game has already been bookmarked.\nDo you want to update it?", "Update bookmark", JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE); if (retVal == JOptionPane.YES_OPTION) { name = bookmark.getName(); } else { return; } } name = JOptionPane.showInputDialog(NearInfinity.getInstance(), "Enter bookmark name:", "Add game to bookmarks", JOptionPane.QUESTION_MESSAGE, null, null, name); if (name != null) { addNewBookmark(name.toString()); } } else if (event.getSource() == gameBookmarkEdit) { List<Bookmark> list = BookmarkEditor.editBookmarks(bookmarkList); if (list != null) { bookmarkList.clear(); bookmarkList.addAll(list); updateBookmarkedGames(); } } else if (event.getSource() == gameRecentClear) { while (!recentList.isEmpty()) { removeLastGame(0); } } } } /////////////////////////////// // File Menu /////////////////////////////// private static final class FileMenu extends JMenu implements ActionListener { private static final class ResInfo { public final String label; public final StructureFactory.ResType resId; private final EnumSet<Profile.Game> supportedGames = EnumSet.noneOf(Profile.Game.class); public ResInfo(StructureFactory.ResType id, String text) { this(id, text, new Profile.Game[]{Profile.Game.BG1, Profile.Game.BG1TotSC, Profile.Game.PST, Profile.Game.IWD, Profile.Game.IWDHoW, Profile.Game.IWDHowToTLM, Profile.Game.IWD2, Profile.Game.BG2SoA, Profile.Game.BG2ToB, Profile.Game.BG1EE, Profile.Game.BG1SoD, Profile.Game.BG2EE, Profile.Game.IWDEE, Profile.Game.EET}); } public ResInfo(StructureFactory.ResType id, String text, Profile.Game[] games) { resId = id; label = text; if (games != null) for (final Profile.Game g : games) { supportedGames.add(g); } } public boolean gameSupported(Profile.Game game) { return supportedGames.contains(game); } } private static final ResInfo RESOURCE[] = { new ResInfo(StructureFactory.ResType.RES_2DA, "2DA"), new ResInfo(StructureFactory.ResType.RES_ARE, "ARE"), new ResInfo(StructureFactory.ResType.RES_BAF, "BAF"), new ResInfo(StructureFactory.ResType.RES_BCS, "BCS"), new ResInfo(StructureFactory.ResType.RES_BIO, "BIO", new Profile.Game[]{Profile.Game.BG2SoA, Profile.Game.BG2ToB, Profile.Game.BG1EE, Profile.Game.BG1SoD, Profile.Game.BG2EE, Profile.Game.IWDEE, Profile.Game.EET}), new ResInfo(StructureFactory.ResType.RES_CHR, "CHR", new Profile.Game[]{Profile.Game.BG1, Profile.Game.BG1TotSC, Profile.Game.BG2SoA, Profile.Game.BG2ToB, Profile.Game.IWD, Profile.Game.IWDHoW, Profile.Game.IWDHowToTLM, Profile.Game.IWD2, Profile.Game.BG1EE, Profile.Game.BG1SoD, Profile.Game.BG2EE, Profile.Game.IWDEE, Profile.Game.EET}), new ResInfo(StructureFactory.ResType.RES_CRE, "CRE"), new ResInfo(StructureFactory.ResType.RES_EFF, "EFF", new Profile.Game[]{Profile.Game.BG1, Profile.Game.BG1TotSC, Profile.Game.BG2SoA, Profile.Game.BG2ToB, Profile.Game.BG1EE, Profile.Game.BG1SoD, Profile.Game.BG2EE, Profile.Game.IWDEE, Profile.Game.EET}), new ResInfo(StructureFactory.ResType.RES_IDS, "IDS"), new ResInfo(StructureFactory.ResType.RES_ITM, "ITM"), new ResInfo(StructureFactory.ResType.RES_INI, "INI", new Profile.Game[]{Profile.Game.PST, Profile.Game.IWD, Profile.Game.IWDHoW, Profile.Game.IWDHowToTLM, Profile.Game.IWD2, Profile.Game.BG1EE, Profile.Game.BG1SoD, Profile.Game.BG2EE, Profile.Game.IWDEE, Profile.Game.EET}), new ResInfo(StructureFactory.ResType.RES_PRO, "PRO", new Profile.Game[]{Profile.Game.BG2SoA, Profile.Game.BG2ToB, Profile.Game.BG1EE, Profile.Game.BG1SoD, Profile.Game.BG2EE, Profile.Game.IWDEE, Profile.Game.EET}), new ResInfo(StructureFactory.ResType.RES_RES, "RES", new Profile.Game[]{Profile.Game.IWD, Profile.Game.IWDHoW, Profile.Game.IWDHowToTLM, Profile.Game.IWD2}), new ResInfo(StructureFactory.ResType.RES_SPL, "SPL"), new ResInfo(StructureFactory.ResType.RES_SRC, "SRC", new Profile.Game[]{Profile.Game.PST, Profile.Game.IWD2}), new ResInfo(StructureFactory.ResType.RES_STO, "STO"), new ResInfo(StructureFactory.ResType.RES_VEF, "VEF", new Profile.Game[]{Profile.Game.BG2SoA, Profile.Game.BG2ToB, Profile.Game.BG1EE, Profile.Game.BG1SoD, Profile.Game.BG2EE, Profile.Game.IWDEE, Profile.Game.EET}), new ResInfo(StructureFactory.ResType.RES_VVC, "VVC", new Profile.Game[]{Profile.Game.BG2SoA, Profile.Game.BG2ToB, Profile.Game.BG1EE, Profile.Game.BG1SoD, Profile.Game.BG2EE, Profile.Game.IWDEE, Profile.Game.EET}), new ResInfo(StructureFactory.ResType.RES_WED, "WED"), new ResInfo(StructureFactory.ResType.RES_WFX, "WFX", new Profile.Game[]{Profile.Game.BG2SoA, Profile.Game.BG2ToB, Profile.Game.BG1EE, Profile.Game.BG1SoD, Profile.Game.BG2EE, Profile.Game.IWDEE, Profile.Game.EET}), new ResInfo(StructureFactory.ResType.RES_WMAP, "WMAP"), }; private final JMenu newFileMenu; private final JMenuItem fileOpenNew, fileExport, fileAddCopy, fileRename, fileDelete, fileRestore; private FileMenu() { super("File"); setMnemonic(KeyEvent.VK_F); newFileMenu = new JMenu("New Resource"); newFileMenu.setIcon(Icons.getIcon(Icons.ICON_NEW_16)); newFileMenu.setMnemonic(KeyEvent.VK_N); add(newFileMenu); fileOpenNew = makeMenuItem("Open in New Window", KeyEvent.VK_W, Icons.getIcon(Icons.ICON_OPEN_16), -1, this); fileOpenNew.setEnabled(false); add(fileOpenNew); fileExport = makeMenuItem("Export...", KeyEvent.VK_E, Icons.getIcon(Icons.ICON_EXPORT_16), -1, this); fileExport.setEnabled(false); add(fileExport); fileAddCopy = makeMenuItem("Add Copy Of...", KeyEvent.VK_A, Icons.getIcon(Icons.ICON_ADD_16), -1, this); fileAddCopy.setEnabled(false); add(fileAddCopy); fileRename = makeMenuItem("Rename...", KeyEvent.VK_R, Icons.getIcon(Icons.ICON_EDIT_16), -1, this); fileRename.setEnabled(false); add(fileRename); fileDelete = makeMenuItem("Delete", KeyEvent.VK_D, Icons.getIcon(Icons.ICON_DELETE_16), -1, this); fileDelete.setEnabled(false); add(fileDelete); fileRestore = makeMenuItem("Restore backup", KeyEvent.VK_B, Icons.getIcon(Icons.ICON_UNDO_16), -1, this); fileRestore.setEnabled(false); add(fileRestore); } private void gameLoaded() { if (newFileMenu != null) { newFileMenu.removeAll(); for (final ResInfo res : RESOURCE) { if (res.gameSupported(Profile.getGame())) { JMenuItem newFile = new JMenuItem(res.label); newFile.addActionListener(this); newFile.setActionCommand(res.label); newFile.setEnabled(true); newFileMenu.add(newFile); } } newFileMenu.setEnabled(newFileMenu.getItemCount() > 0); } } @Override public void actionPerformed(ActionEvent event) { if (event.getSource() == fileOpenNew) { Resource res = ResourceFactory.getResource( NearInfinity.getInstance().getResourceTree().getSelected()); if (res != null) new ViewFrame(NearInfinity.getInstance(), res); } else if (event.getSource() == fileExport) { ResourceFactory.exportResource(NearInfinity.getInstance().getResourceTree().getSelected(), NearInfinity.getInstance()); } else if (event.getSource() == fileAddCopy) { ResourceFactory.saveCopyOfResource(NearInfinity.getInstance().getResourceTree().getSelected()); } else if (event.getSource() == fileRename) { if (NearInfinity.getInstance().getResourceTree().getSelected() instanceof FileResourceEntry) { ResourceTree.renameResource((FileResourceEntry)NearInfinity.getInstance().getResourceTree().getSelected()); } } else if (event.getSource() == fileDelete) { ResourceTree.deleteResource(NearInfinity.getInstance().getResourceTree().getSelected()); } else if (event.getSource() == fileRestore) { ResourceTree.restoreResource(NearInfinity.getInstance().getResourceTree().getSelected()); } else { for (final ResInfo res : RESOURCE) { if (event.getActionCommand().equals(res.label)) { StructureFactory.getInstance().newResource(res.resId, NearInfinity.getInstance()); } } } } private void resourceEntrySelected(ResourceEntry entry) { fileOpenNew.setEnabled(entry != null); fileExport.setEnabled(entry != null); fileAddCopy.setEnabled(entry != null); fileRename.setEnabled(entry instanceof FileResourceEntry); fileDelete.setEnabled((entry != null && entry.hasOverride()) || entry instanceof FileResourceEntry); fileRestore.setEnabled(ResourceTree.isBackupAvailable(entry)); } private void resourceShown(Resource res) { // not used anymore } } /////////////////////////////// // Edit Menu /////////////////////////////// private static final class EditMenu extends JMenu implements ActionListener { private final JMenuItem editString, editString2, editBIFF, editVarVar, editIni; private EditMenu() { super("Edit"); setMnemonic(KeyEvent.VK_E); editString = makeMenuItem("Dialog.tlk", KeyEvent.VK_D, Icons.getIcon(Icons.ICON_EDIT_16), KeyEvent.VK_S, this); add(editString); editString2 = makeMenuItem("DialogF.tlk", KeyEvent.VK_F, Icons.getIcon(Icons.ICON_EDIT_16), -1, this); add(editString2); editIni = makeMenuItem("baldur.ini", KeyEvent.VK_I, Icons.getIcon(Icons.ICON_EDIT_16), -1, NearInfinity.getInstance()); editIni.setActionCommand("GameIni"); add(editIni); editVarVar = makeMenuItem("Var.var", KeyEvent.VK_V, Icons.getIcon(Icons.ICON_ROW_INSERT_AFTER_16), -1, this); add(editVarVar); // TODO: reactive when fixed editBIFF = makeMenuItem("BIFF", KeyEvent.VK_B, Icons.getIcon(Icons.ICON_EDIT_16), KeyEvent.VK_E, this); editBIFF.setToolTipText("Temporarily disabled"); editBIFF.setEnabled(false); add(editBIFF); } private void gameLoaded() { Path iniFile = Profile.getProperty(Profile.Key.GET_GAME_INI_FILE); if (iniFile != null && Files.isRegularFile(iniFile)) { editIni.setText(iniFile.getFileName().toString()); editIni.setEnabled(true); editIni.setToolTipText("Edit " + iniFile.toString()); } else { editIni.setText("baldur.ini"); editIni.setEnabled(false); editIni.setToolTipText("Ini file not available"); } editString2.setEnabled(Profile.getProperty(Profile.Key.GET_GAME_DIALOGF_FILE) != null); Path varFile = FileManager.query(Profile.getRootFolders(), "VAR.VAR"); editVarVar.setEnabled(varFile != null && Files.isRegularFile(varFile)); if (editString2.isEnabled()) { editString2.setToolTipText(""); } else { editString2.setToolTipText("DialogF.tlk not found"); } if (editVarVar.isEnabled()) { editVarVar.setToolTipText(""); } else { editVarVar.setToolTipText("Only available for Planescape: Torment"); } } @Override public void actionPerformed(ActionEvent event) { if (event.getSource() == editString) { StringEditor editor = null; List<ChildFrame> frames = ChildFrame.getFrames(StringEditor.class); for (int i = 0; i < frames.size(); i++) { StringEditor e = (StringEditor)frames.get(i); if (e.getPath().equals(StringResource.getPath())) { editor = e; } } if (editor == null) { new StringEditor(StringResource.getPath(), 0); } else { editor.setVisible(true); } } else if (event.getSource() == editString2) { StringEditor editor = null; Path file = Profile.getProperty(Profile.Key.GET_GAME_DIALOGF_FILE); List<ChildFrame> frames = ChildFrame.getFrames(StringEditor.class); for (int i = 0; i < frames.size(); i++) { StringEditor e = (StringEditor)frames.get(i); if (e.getPath().equals(file)) { editor = e; } } if (editor == null) { new StringEditor(file, 0); } else { editor.setVisible(true); } } else if (event.getSource() == editVarVar) { new ViewFrame(NearInfinity.getInstance(), ResourceFactory.getResource( new FileResourceEntry( FileManager.queryExisting(Profile.getRootFolders(), "VAR.VAR")))); } else if (event.getSource() == editBIFF) { // new BIFFEditor(); } } } /////////////////////////////// // Search Menu /////////////////////////////// private static final class SearchMenu extends JMenu implements ActionListener { private final String TEXTSEARCH[] = {"2DA", "BCS", "DLG", "IDS", "INI"}; private final JMenuItem searchString, searchFile, searchResource; private final JMenu textSearchMenu; private SearchMenu() { super("Search"); setMnemonic(KeyEvent.VK_S); searchString = makeMenuItem("StringRef...", KeyEvent.VK_S, Icons.getIcon(Icons.ICON_FIND_16), KeyEvent.VK_L, this); add(searchString); searchFile = makeMenuItem("CRE/ITM/SPL/STO...", KeyEvent.VK_C, Icons.getIcon(Icons.ICON_FIND_16), KeyEvent.VK_F, this); add(searchFile); searchResource = makeMenuItem("Extended search...", KeyEvent.VK_X, Icons.getIcon(Icons.ICON_FIND_16), -1, this); searchResource.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_X, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask() | ActionEvent.ALT_MASK)); add(searchResource); textSearchMenu = new JMenu("Text Search"); textSearchMenu.setIcon(Icons.getIcon(Icons.ICON_EDIT_16)); for (final String type : TEXTSEARCH) { JMenuItem textSearch = new JMenuItem(type); textSearch.addActionListener(this); textSearch.setActionCommand(type); textSearchMenu.add(textSearch); } add(textSearchMenu); } private void gameLoaded() { // Enable INI search only if the game is supporting it for (int i = 0, count = textSearchMenu.getMenuComponentCount(); i < count; i++) { if (textSearchMenu.getMenuComponent(i) instanceof JMenuItem) { JMenuItem mi = (JMenuItem)textSearchMenu.getMenuComponent(i); if ("INI".equals(mi.getText())) { if ((Boolean)Profile.getProperty(Profile.Key.IS_SUPPORTED_INI)) { mi.setEnabled(true); } else { mi.setEnabled(false); } } } } } @Override public void actionPerformed(ActionEvent event) { if (event.getSource() == searchString) { StringLookup lookup = (StringLookup)ChildFrame.getFirstFrame(StringLookup.class); if (lookup == null) lookup = new StringLookup(); lookup.setVisible(true); } else if (event.getSource() == searchFile) { SearchFrame search = (SearchFrame)ChildFrame.getFirstFrame(SearchFrame.class); if (search == null) search = new SearchFrame(); search.setVisible(true); } else if (event.getSource() == searchResource) { SearchResource resource = (SearchResource)ChildFrame.getFirstFrame(SearchResource.class); if (resource == null) { resource = new SearchResource(); } resource.setVisible(true); } else { for (final String type : TEXTSEARCH) { if (event.getActionCommand().equals(type)) { if (event.getActionCommand().equals("DLG")) { new DialogSearcher(ResourceFactory.getResources(type), getTopLevelAncestor()); } else { new TextResourceSearcher(ResourceFactory.getResources(type), getTopLevelAncestor()); } return; } } } } } /////////////////////////////// // Tools Menu /////////////////////////////// private static final class ToolsMenu extends JMenu implements ActionListener { private final JMenuItem toolInfinityAmp, toolCleanKeyfile, toolCheckAllDialog, toolCheckOverrideDialog; private final JMenuItem toolCheckResRef, toolIDSBrowser, toolDropZone, toolCheckCREInv; private final JMenuItem toolCheckIDSRef, toolCheckIDSBCSRef, toolCheckScripts, toolCheckStructs; private final JMenuItem toolCheckStringUse, toolCheckStringIndex, toolCheckFileUse, toolMassExport; private final JMenuItem toolCheckEffectsIndex; private final JMenuItem toolConvImageToBam, toolConvImageToBmp, toolConvImageToMos, toolConvImageToTis, toolConvImageToPvrz; private final JCheckBoxMenuItem toolConsole, toolClipBoard; private ToolsMenu() { super("Tools"); setMnemonic(KeyEvent.VK_T); toolInfinityAmp = makeMenuItem("InfinityAmp", KeyEvent.VK_I, Icons.getIcon(Icons.ICON_VOLUME_16), -1, this); add(toolInfinityAmp); addSeparator(); // TODO: reactivate when fixed toolCleanKeyfile = makeMenuItem("Keyfile Cleanup", KeyEvent.VK_K, Icons.getIcon(Icons.ICON_REFRESH_16), -1, this); toolCleanKeyfile.setToolTipText("Temporarily disabled"); toolCleanKeyfile.setEnabled(false); add(toolCleanKeyfile); addSeparator(); // *** Begin Check submenu *** JMenu checkMenu = new JMenu("Check"); checkMenu.setIcon(Icons.getIcon(Icons.ICON_FIND_16)); checkMenu.setMnemonic('c'); add(checkMenu); JMenu checkSubMenu = new JMenu("Triggers & Actions For"); checkSubMenu.setIcon(Icons.getIcon(Icons.ICON_REFRESH_16)); toolCheckAllDialog = new JMenuItem("All Dialogues"); toolCheckAllDialog.addActionListener(this); checkSubMenu.add(toolCheckAllDialog); toolCheckOverrideDialog = new JMenuItem("Override Dialogues Only"); toolCheckOverrideDialog.addActionListener(this); checkSubMenu.add(toolCheckOverrideDialog); checkMenu.add(checkSubMenu); toolCheckScripts = makeMenuItem("Scripts", KeyEvent.VK_S, Icons.getIcon(Icons.ICON_REFRESH_16), -1, this); checkMenu.add(toolCheckScripts); toolCheckCREInv = makeMenuItem("For CRE Items Not in Inventory", KeyEvent.VK_C, Icons.getIcon(Icons.ICON_REFRESH_16), -1, this); toolCheckCREInv.setToolTipText("Reports items present in the file but not in the inventory"); checkMenu.add(toolCheckCREInv); toolCheckResRef = makeMenuItem("For Illegal ResourceRefs...", KeyEvent.VK_R, Icons.getIcon(Icons.ICON_FIND_16), -1, this); toolCheckResRef.setToolTipText("Reports resource references pointing to nonexistent files"); checkMenu.add(toolCheckResRef); JMenu findMenu = new JMenu("For Unknown IDS References In"); findMenu.setIcon(Icons.getIcon(Icons.ICON_FIND_16)); toolCheckIDSBCSRef = new JMenuItem("BCS & BS Files"); toolCheckIDSBCSRef.addActionListener(this); findMenu.add(toolCheckIDSBCSRef); toolCheckIDSRef = new JMenuItem("Other Files..."); toolCheckIDSRef.addActionListener(this); findMenu.add(toolCheckIDSRef); checkMenu.add(findMenu); findMenu.setToolTipText("Reports IDS references to unknown IDS values"); toolCheckIDSBCSRef.setToolTipText("Note: GTimes, Time, Scroll, ShoutIDs, and Specific are ignored"); toolCheckIDSRef.setToolTipText("Note: \"0\" references are ignored"); toolCheckStructs = makeMenuItem("For Corrupted Files...", KeyEvent.VK_F, Icons.getIcon(Icons.ICON_FIND_16), -1, this); toolCheckStructs.setToolTipText("Reports structured files with partially overlapping subsections or resource-specific corruptions"); checkMenu.add(toolCheckStructs); toolCheckStringUse = makeMenuItem("For Unused Strings", KeyEvent.VK_U, Icons.getIcon(Icons.ICON_FIND_16), -1, this); checkMenu.add(toolCheckStringUse); toolCheckStringIndex = makeMenuItem("For Illegal Strrefs...", KeyEvent.VK_S, Icons.getIcon(Icons.ICON_FIND_16), -1, this); toolCheckStringIndex.setToolTipText("Reports resources with out-of-range string references"); checkMenu.add(toolCheckStringIndex); toolCheckFileUse = makeMenuItem("For Unused Files...", -1, Icons.getIcon(Icons.ICON_FIND_16), -1, this); checkMenu.add(toolCheckFileUse); toolCheckEffectsIndex = makeMenuItem("For Mis-indexed Effects", -1, Icons.getIcon(Icons.ICON_FIND_16), -1, this); checkMenu.add(toolCheckEffectsIndex); // *** End Check submenu *** // *** Begin Convert submenu *** JMenu convertMenu = new JMenu("Convert"); convertMenu.setIcon(Icons.getIcon(Icons.ICON_APPLICATION_16)); convertMenu.setMnemonic('v'); add(convertMenu); toolConvImageToBam = makeMenuItem("BAM Converter...", KeyEvent.VK_B, Icons.getIcon(Icons.ICON_APPLICATION_16), -1, this); convertMenu.add(toolConvImageToBam); toolConvImageToBmp = makeMenuItem("Image to BMP...", KeyEvent.VK_I, Icons.getIcon(Icons.ICON_APPLICATION_16), -1, this); convertMenu.add(toolConvImageToBmp); toolConvImageToMos = makeMenuItem("Image to MOS...", KeyEvent.VK_M, Icons.getIcon(Icons.ICON_APPLICATION_16), -1, this); convertMenu.add(toolConvImageToMos); toolConvImageToPvrz = makeMenuItem("Image to PVRZ...", KeyEvent.VK_P, Icons.getIcon(Icons.ICON_APPLICATION_16), -1, this); convertMenu.add(toolConvImageToPvrz); toolConvImageToTis = makeMenuItem("Image to TIS...", KeyEvent.VK_T, Icons.getIcon(Icons.ICON_APPLICATION_16), -1, this); convertMenu.add(toolConvImageToTis); // *** End Convert submenu *** addSeparator(); toolIDSBrowser = makeMenuItem("IDS Browser", KeyEvent.VK_B, Icons.getIcon(Icons.ICON_HISTORY_16), KeyEvent.VK_B, this); add(toolIDSBrowser); toolDropZone = makeMenuItem("Script Drop Zone", KeyEvent.VK_Z, Icons.getIcon(Icons.ICON_HISTORY_16), KeyEvent.VK_Z, this); add(toolDropZone); addSeparator(); toolMassExport = makeMenuItem("Mass Export...", KeyEvent.VK_M, Icons.getIcon(Icons.ICON_EXPORT_16), -1, this); add(toolMassExport); addSeparator(); toolClipBoard = new JCheckBoxMenuItem("Show Clipboard", Icons.getIcon(Icons.ICON_PASTE_16)); toolClipBoard.addActionListener(this); add(toolClipBoard); toolConsole = new JCheckBoxMenuItem("Show Debug Console", Icons.getIcon(Icons.ICON_PROPERTIES_16)); toolConsole.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_D, CTRL_MASK)); toolConsole.addActionListener(this); add(toolConsole); } // private static void cleanKeyfile() // { // JLabel infolabel = new JLabel("<html><center>This will delete empty BIFFs and remove<br>" + // "references to nonexistent BIFFs.<br><br>" + // "Warning: Your existing " + ResourceFactory.getKeyfile() + // " will be overwritten!<br><br>Continue?</center></html>"); // String options[] = {"Continue", "Cancel"}; // if (JOptionPane.showOptionDialog(NearInfinity.getInstance(), infolabel, // "Keyfile cleanup", JOptionPane.YES_NO_OPTION, // JOptionPane.WARNING_MESSAGE, null, options, options[0]) != 0) // return; // boolean updated = ResourceFactory.getKeyfile().cleanUp(); // if (!updated) // JOptionPane.showMessageDialog(NearInfinity.getInstance(), "No cleanup necessary", "Cleanup completed", // JOptionPane.INFORMATION_MESSAGE); // else { // try { // ResourceFactory.getKeyfile().write(); // JOptionPane.showMessageDialog(NearInfinity.getInstance(), "Operation completed successfully", "Cleanup completed", // JOptionPane.INFORMATION_MESSAGE); // } catch (IOException e) { // JOptionPane.showMessageDialog(NearInfinity.getInstance(), "Error writing keyfile", "Error", // JOptionPane.ERROR_MESSAGE); // e.printStackTrace(); // } // } // } @Override public void actionPerformed(ActionEvent event) { if (event.getSource() == toolInfinityAmp) { InfinityAmp infAmp = (InfinityAmp)ChildFrame.getFirstFrame(InfinityAmp.class); if (infAmp == null) infAmp = new InfinityAmp(); infAmp.setVisible(true); } else if (event.getSource() == toolIDSBrowser) { IdsBrowser browser = (IdsBrowser)ChildFrame.getFirstFrame(IdsBrowser.class); if (browser == null) browser = new IdsBrowser(); browser.setVisible(true); } else if (event.getSource() == toolClipBoard) { ClipboardViewer viewer = (ClipboardViewer)ChildFrame.getFirstFrame( ClipboardViewer.class); if (viewer == null) { viewer = new ClipboardViewer(); viewer.addWindowListener(new WindowAdapter() { @Override public void windowClosing(WindowEvent e) { toolClipBoard.setSelected(false); } }); } viewer.setVisible(toolClipBoard.isSelected()); } else if (event.getSource() == toolConsole) { DebugConsole console = (DebugConsole)ChildFrame.getFirstFrame(DebugConsole.class); if (console == null) { console = new DebugConsole(); console.addWindowListener(new WindowAdapter() { @Override public void windowClosing(WindowEvent e) { toolConsole.setSelected(false); } }); } console.setVisible(toolConsole.isSelected()); } else if (event.getSource() == toolCleanKeyfile) // cleanKeyfile(); ; else if (event.getSource() == toolDropZone) { BcsDropFrame bcsframe = (BcsDropFrame)ChildFrame.getFirstFrame(BcsDropFrame.class); if (bcsframe == null) bcsframe = new BcsDropFrame(); bcsframe.setVisible(true); } else if (event.getSource() == toolCheckAllDialog) new DialogChecker(false); else if (event.getSource() == toolCheckOverrideDialog) new DialogChecker(true); else if (event.getSource() == toolCheckResRef) new ResRefChecker(); else if (event.getSource() == toolCheckCREInv) new CreInvChecker(); else if (event.getSource() == toolCheckIDSRef) new IDSRefChecker(); else if (event.getSource() == toolCheckIDSBCSRef) new BCSIDSChecker(); else if (event.getSource() == toolCheckScripts) new ScriptChecker(); else if (event.getSource() == toolCheckStructs) new StructChecker(); else if (event.getSource() == toolCheckStringUse) new StringUseChecker(); else if (event.getSource() == toolCheckStringIndex) new StrrefIndexChecker(); else if (event.getSource() == toolCheckFileUse) new ResourceUseChecker(NearInfinity.getInstance()); else if (event.getSource() == toolMassExport) new MassExporter(); else if (event.getSource() == toolCheckEffectsIndex) new EffectsIndexChecker(); else if (event.getSource() == toolConvImageToPvrz) { ConvertToPvrz dlg = (ConvertToPvrz)ChildFrame.getFirstFrame(ConvertToPvrz.class); if (dlg == null) { dlg = new ConvertToPvrz(); } dlg.setVisible(true); } else if (event.getSource() == toolConvImageToTis) { ConvertToTis dlg = (ConvertToTis)ChildFrame.getFirstFrame(ConvertToTis.class); if (dlg == null) { dlg = new ConvertToTis(); } dlg.setVisible(true); } else if (event.getSource() == toolConvImageToMos) { ConvertToMos dlg = (ConvertToMos)ChildFrame.getFirstFrame(ConvertToMos.class); if (dlg == null) { dlg = new ConvertToMos(); } dlg.setVisible(true); } else if (event.getSource() == toolConvImageToBmp) { ConvertToBmp dlg = (ConvertToBmp)ChildFrame.getFirstFrame(ConvertToBmp.class); if (dlg == null) { dlg = new ConvertToBmp(); } dlg.setVisible(true); } else if (event.getSource() == toolConvImageToBam) { ConvertToBam dlg = (ConvertToBam)ChildFrame.getFirstFrame(ConvertToBam.class); if (dlg == null) { dlg = new ConvertToBam(); } dlg.setVisible(true); } } } /////////////////////////////// // Options Menu /////////////////////////////// private static final class OptionsMenu extends JMenu implements ActionListener, ItemListener { private static final Font[] FONTS = { new Font(Font.MONOSPACED, Font.PLAIN, 12), new Font(Font.SERIF, Font.PLAIN, 12), new Font(Font.SANS_SERIF, Font.PLAIN, 12), new Font(Font.DIALOG, Font.PLAIN, 12), null}; private static final String DefaultCharset = "Auto"; private static final List<String[]> CharsetsUsed = new ArrayList<String[]>(); // BCS indentations to use when decompiling (indent, title) private static final String[][] BCSINDENT = { {" ", "2 Spaces"}, {" ", "4 Spaces"}, {"\t", "Tab"} }; // Available color schemes for highlighted BCS format (scheme, title, description) private static final String[][] BCSCOLORSCHEME = { {InfinityTextArea.SchemeDefault, "Default", "A general-purpose default color scheme"}, {InfinityTextArea.SchemeDark, "Dark", "A dark scheme based off of Notepad++'s Obsidian theme"}, {InfinityTextArea.SchemeEclipse, "Eclipse", "Mimics Eclipse's default color scheme"}, {InfinityTextArea.SchemeIdea, "IntelliJ IDEA", "Mimics IntelliJ IDEA's default color scheme"}, {InfinityTextArea.SchemeVs, "Visual Studio", "Mimics Microsoft's Visual Studio color scheme"}, {InfinityTextArea.SchemeBCS, "BCS Light", "A color scheme which is loosely based on the WeiDU Syntax Highlighter for Notepad++"}, // {null, "External color scheme...", "Use an external color scheme definition file"}, }; // Available color schemes for remaining highlighted formats (scheme, title, description) private static final String[][] COLORSCHEME = { {InfinityTextArea.SchemeDefault, "Default", "A general-purpose default color scheme"}, {InfinityTextArea.SchemeDark, "Dark", "A dark scheme based off of Notepad++'s Obsidian theme"}, {InfinityTextArea.SchemeEclipse, "Eclipse", "Mimics Eclipse's default color scheme"}, {InfinityTextArea.SchemeIdea, "IntelliJ IDEA", "Mimics IntelliJ IDEA's default color scheme"}, {InfinityTextArea.SchemeVs, "Visual Studio", "Mimics Microsoft's Visual Studio color scheme"}, // {null, "External color scheme...", "Use an external color scheme definition file"}, }; static { // Order: Display name, Canonical charset name, Tooltip CharsetsUsed.add(new String[]{"UTF-8", "UTF-8", "The character set of choice for the Enhanced Editions of the Baldur's Gate games."}); CharsetsUsed.add(new String[]{"Windows-1252", "windows-1252", "Character set used in english and other latin-based languages, such as french, german, italian or spanish."}); CharsetsUsed.add(new String[]{"Windows-1251", "windows-1251", "Character set used in russian and other cyrillic-based languages."}); CharsetsUsed.add(new String[]{"Windows-1250", "windows-1250", "Character set used in central european and eastern european languages, such as polish or czech."}); CharsetsUsed.add(new String[]{"Windows-31J", "windows-31j", "Character set used in japanese localizations."}); CharsetsUsed.add(new String[]{"GBK", "GBK", "Character set for Simplified Chinese text."}); CharsetsUsed.add(new String[]{"Big5-HKSCS", "Big5-HKSCS", "Character set for Traditional Chinese text (may not be fully compatible)."}); CharsetsUsed.add(new String[]{"IBM-949", "x-IBM949", "Character set used in korean localizations."}); } private static final String OPTION_SHOWOFFSETS = "ShowOffsets"; private static final String OPTION_BACKUPONSAVE = "BackupOnSave"; private static final String OPTION_IGNOREOVERRIDE = "IgnoreOverride"; private static final String OPTION_IGNOREREADERRORS = "IgnoreReadErrors"; private static final String OPTION_AUTOCHECK_BCS = "AutocheckBCS"; private static final String OPTION_CACHEOVERRIDE = "CacheOverride"; private static final String OPTION_CHECKSCRIPTNAMES = "CheckScriptNames"; private static final String OPTION_SHOWSTRREFS = "ShowStrrefs"; private static final String OPTION_DLG_SHOWICONS = "DlgShowIcons"; private static final String OPTION_SHOWHEXCOLORED = "ShowHexColored"; private static final String OPTION_SHOWOVERRIDES = "ShowOverridesIn"; private static final String OPTION_SHOWRESREF = "ShowResRef"; private static final String OPTION_LOOKANDFEELCLASS = "LookAndFeelClass"; private static final String OPTION_VIEWOREDITSHOWN = "ViewOrEditShown"; private static final String OPTION_FONT = "Font"; private static final String OPTION_FONT_NAME = "FontName"; private static final String OPTION_FONT_STYLE = "FontStyle"; private static final String OPTION_FONT_SIZE = "FontSize"; private static final String OPTION_TLKCHARSET = "TLKCharsetType"; private static final String OPTION_LANGUAGE_GAMES = "GameLanguages"; private static final String OPTION_TEXT_SHOWCURRENTLINE = "TextShowCurrentLine"; private static final String OPTION_TEXT_SHOWLINENUMBERS = "TextShowLineNumbers"; private static final String OPTION_TEXT_SYMBOLWHITESPACE = "TextShowWhiteSpace"; private static final String OPTION_TEXT_SYMBOLEOL = "TextShowEOL"; private static final String OPTION_TEXT_TABSEMULATED = "TextTabsEmulated"; private static final String OPTION_TEXT_TABSIZE = "TextTabSize"; private static final String OPTION_BCS_SYNTAXHIGHLIGHTING = "BcsSyntaxHighlighting"; private static final String OPTION_BCS_COLORSCHEME = "BcsColorScheme"; private static final String OPTION_BCS_CODEFOLDING = "BcsCodeFolding"; private static final String OPTION_BCS_AUTO_INDENT = "BcsAutoIndent"; // private static final String OPTION_BCS_AUTOCOMPLETE = "BcsAutoComplete"; private static final String OPTION_BCS_INDENT = "BcsIndent"; private static final String OPTION_GLSL_SYNTAXHIGHLIGHTING = "GlslSyntaxHighlighting"; private static final String OPTION_GLSL_COLORSCHEME = "GlslColorScheme"; private static final String OPTION_GLSL_CODEFOLDING = "GlslCodeFolding"; private static final String OPTION_LUA_SYNTAXHIGHLIGHTING = "LuaSyntaxHighlighting"; private static final String OPTION_LUA_COLORSCHEME = "LuaColorScheme"; private static final String OPTION_SQL_SYNTAXHIGHLIGHTING = "SqlSyntaxHighlighting"; private static final String OPTION_SQL_COLORSCHEME = "SqlColorScheme"; private static final String OPTION_TEXT_DEBUG_ENABLECOLORSCHEME = "DebugColorSchemeEnabled"; private static final String OPTION_TEXT_DEBUG_COLORSCHEME = "DebugColorSchemeFile"; // this preferences key can be used internally to reset incorrectly set default values after a public release private static final String OPTION_OPTION_FIXED = "OptionFixedInternal"; // Mask used for one-time resets of options (kept track of in OPTION_OPTION_FIXED) private static final int MASK_OPTION_FIXED_AUTO_INDENT = 0x00000001; // Identifier for autodetected game language private static final String LANGUAGE_AUTODETECT = "Auto"; // For debugging purposes only private static String DEBUGCOLORSCHEME = ""; private final List<DataRadioButtonMenuItem> lookAndFeel = new ArrayList<DataRadioButtonMenuItem>(); private final JRadioButtonMenuItem[] showOverrides = new JRadioButtonMenuItem[3]; private final JRadioButtonMenuItem[] showResRef = new JRadioButtonMenuItem[3]; private final JRadioButtonMenuItem[] viewOrEditShown = new JRadioButtonMenuItem[3]; private final JRadioButtonMenuItem[] selectFont = new JRadioButtonMenuItem[FONTS.length]; private final JRadioButtonMenuItem[] selectTextTabSize = new JRadioButtonMenuItem[3]; private final JRadioButtonMenuItem[] selectBcsIndent = new JRadioButtonMenuItem[BCSINDENT.length]; private final JRadioButtonMenuItem[] selectBcsColorScheme = new JRadioButtonMenuItem[BCSCOLORSCHEME.length]; private final JRadioButtonMenuItem[] selectGlslColorScheme = new JRadioButtonMenuItem[COLORSCHEME.length]; private final JRadioButtonMenuItem[] selectLuaColorScheme = new JRadioButtonMenuItem[COLORSCHEME.length]; private final JRadioButtonMenuItem[] selectSqlColorScheme = new JRadioButtonMenuItem[COLORSCHEME.length]; private JCheckBoxMenuItem optionTextHightlightCurrent, optionTextLineNumbers, optionTextShowWhiteSpace, optionTextShowEOL, optionTextTabEmulate, optionBCSEnableSyntax, optionBCSEnableCodeFolding, optionBCSEnableAutoIndent, optionGLSLEnableSyntax, optionLUAEnableSyntax, optionSQLEnableSyntax, // optionBCSEnableAutoComplete, optionGLSLEnableCodeFolding, optionTextDebugColorSchemeEnabled; private JMenuItem optionTextDebugColorSchemeSelect; private JCheckBoxMenuItem optionBackupOnSave, optionShowOffset, optionIgnoreOverride; private JCheckBoxMenuItem optionIgnoreReadErrors, optionAutocheckBCS, optionCacheOverride; private JCheckBoxMenuItem optionCheckScriptNames, optionShowStrrefs, optionDlgShowIcons, optionShowHexColored; private final JMenu mCharsetMenu, mLanguageMenu; private ButtonGroup bgCharsetButtons; private String languageDefinition; private int optionFixedInternal; // Stores available languages in BG(2)EE private final HashMap<JRadioButtonMenuItem, String> gameLanguage = new HashMap<JRadioButtonMenuItem, String>(); private OptionsMenu() { super("Options"); setMnemonic(KeyEvent.VK_O); optionFixedInternal = getPrefs().getInt(OPTION_OPTION_FIXED, 0); // Options optionBackupOnSave = new JCheckBoxMenuItem("Backup on save", getPrefs().getBoolean(OPTION_BACKUPONSAVE, false)); optionBackupOnSave.setToolTipText("Enable this option to automatically create a backup " + "of the resource you want to save."); add(optionBackupOnSave); optionIgnoreOverride = new JCheckBoxMenuItem("Ignore Overrides", getPrefs().getBoolean(OPTION_IGNOREOVERRIDE, false)); add(optionIgnoreOverride); optionIgnoreReadErrors = new JCheckBoxMenuItem("Ignore Read Errors", getPrefs().getBoolean(OPTION_IGNOREREADERRORS, false)); add(optionIgnoreReadErrors); optionShowOffset = new JCheckBoxMenuItem("Show Hex Offsets", getPrefs().getBoolean(OPTION_SHOWOFFSETS, false)); add(optionShowOffset); optionAutocheckBCS = new JCheckBoxMenuItem("Autocheck BCS", getPrefs().getBoolean(OPTION_AUTOCHECK_BCS, true)); add(optionAutocheckBCS); optionCacheOverride = new JCheckBoxMenuItem("Autocheck for Overrides", getPrefs().getBoolean(OPTION_CACHEOVERRIDE, false)); optionCacheOverride.setToolTipText("Without this option selected, Refresh Tree is required " + "to discover new override files added while NI is open"); add(optionCacheOverride); optionCheckScriptNames = new JCheckBoxMenuItem("Interactive script names", getPrefs().getBoolean(OPTION_CHECKSCRIPTNAMES, true)); optionCheckScriptNames.setToolTipText("With this option disabled, performance may be boosted " + "but many features involving script names will be disabled."); add(optionCheckScriptNames); optionShowStrrefs = new JCheckBoxMenuItem("Show Strrefs in View tabs", getPrefs().getBoolean(OPTION_SHOWSTRREFS, false)); add(optionShowStrrefs); optionDlgShowIcons = new JCheckBoxMenuItem("Show icons in DLG tree viewer", getPrefs().getBoolean(OPTION_DLG_SHOWICONS, true)); add(optionDlgShowIcons); optionShowHexColored = new JCheckBoxMenuItem("Show colored blocks in Raw tabs", getPrefs().getBoolean(OPTION_SHOWHEXCOLORED, true)); add(optionShowHexColored); addSeparator(); // Options->Text Editor JMenu textMenu = new JMenu("Text Editor"); add(textMenu); // Options->Text Editor->Show Symbols JMenu textSymbols = new JMenu("Show Symbols"); textMenu.add(textSymbols); optionTextShowWhiteSpace = new JCheckBoxMenuItem("Show Spaces and Tabs", getPrefs().getBoolean(OPTION_TEXT_SYMBOLWHITESPACE, false)); textSymbols.add(optionTextShowWhiteSpace); optionTextShowEOL = new JCheckBoxMenuItem("Show End of Line", getPrefs().getBoolean(OPTION_TEXT_SYMBOLEOL, false)); textSymbols.add(optionTextShowEOL); // Options->Text Viewer/Editor->Tab Settings JMenu textTabs = new JMenu("Tab Settings"); textMenu.add(textTabs); optionTextTabEmulate = new JCheckBoxMenuItem("Emulate Tabs with Spaces", getPrefs().getBoolean(OPTION_TEXT_TABSEMULATED, false)); textTabs.add(optionTextTabEmulate); textTabs.addSeparator(); ButtonGroup bg = new ButtonGroup(); int selectedTextTabSize = getPrefs().getInt(OPTION_TEXT_TABSIZE, 1); selectTextTabSize[0] = new JRadioButtonMenuItem("Expand by 2 Spaces", selectedTextTabSize == 0); selectTextTabSize[1] = new JRadioButtonMenuItem("Expand by 4 Spaces", selectedTextTabSize == 1); selectTextTabSize[2] = new JRadioButtonMenuItem("Expand by 8 Spaces", selectedTextTabSize == 2); for (int i = 0; i < selectTextTabSize.length; i++) { int cnt = 1 << (i + 1); selectTextTabSize[i].setToolTipText(String.format("Each (real or emulated) tab will occupy %1$d spaces.", cnt)); textTabs.add(selectTextTabSize[i]); bg.add(selectTextTabSize[i]); } // Options->Text Editor->BCS and BAF JMenu textBCS = new JMenu("BCS and BAF"); textMenu.add(textBCS); JMenu textBCSIndent = new JMenu("BCS Indent"); textBCS.add(textBCSIndent); bg = new ButtonGroup(); int selectedBCSIndent = getPrefs().getInt(OPTION_BCS_INDENT, 2); if (selectedBCSIndent < 0 || selectedBCSIndent >= BCSINDENT.length) { selectedBCSIndent = 2; } for (int i = 0; i < BCSINDENT.length; i++) { selectBcsIndent[i] = new JRadioButtonMenuItem(BCSINDENT[i][1], selectedBCSIndent == i); textBCSIndent.add(selectBcsIndent[i]); bg.add(selectBcsIndent[i]); } JMenu textBCSColors = new JMenu("Color Scheme"); textBCS.add(textBCSColors); bg = new ButtonGroup(); int selectedBCSScheme = getPrefs().getInt(OPTION_BCS_COLORSCHEME, 5); if (selectedBCSScheme < 0 || selectedBCSScheme >= BCSCOLORSCHEME.length) { selectedBCSScheme = 5; } for (int i = 0; i < BCSCOLORSCHEME.length; i++) { selectBcsColorScheme[i] = new JRadioButtonMenuItem(BCSCOLORSCHEME[i][1], selectedBCSScheme == i); selectBcsColorScheme[i].setToolTipText(BCSCOLORSCHEME[i][2]); textBCSColors.add(selectBcsColorScheme[i]); bg.add(selectBcsColorScheme[i]); } optionBCSEnableSyntax = new JCheckBoxMenuItem("Enable Syntax Highlighting", getPrefs().getBoolean(OPTION_BCS_SYNTAXHIGHLIGHTING, true)); textBCS.add(optionBCSEnableSyntax); optionBCSEnableCodeFolding = new JCheckBoxMenuItem("Enable Code Folding", getPrefs().getBoolean(OPTION_BCS_CODEFOLDING, false)); textBCS.add(optionBCSEnableCodeFolding); // XXX: Work-around to fix a previously incorrectly defined option optionBCSEnableAutoIndent = new JCheckBoxMenuItem("Enable Automatic Indentation", fixOption(MASK_OPTION_FIXED_AUTO_INDENT, true, getPrefs().getBoolean(OPTION_BCS_AUTO_INDENT, false))); // optionBCSEnableAutoIndent = new JCheckBoxMenuItem("Enable Automatic Indentation", // getPrefs().getBoolean(OPTION_BCS_AUTO_INDENT, false)); textBCS.add(optionBCSEnableAutoIndent); // TODO: add auto-complete support // optionBCSEnableAutoComplete = new JCheckBoxMenuItem("Enable Auto-Completion", // getPrefs().getBoolean(OPTION_BCS_AUTOCOMPLETE, false)); // optionBCSEnableAutoComplete.setVisible(false); // textBCS.add(optionBCSEnableAutoComplete); // Options->Text Viewer/Editor->Misc. Resource Types JMenu textMisc = new JMenu("Misc. Resource Types"); textMenu.add(textMisc); JMenu textGLSLColors = new JMenu("Color Scheme for GLSL"); textMisc.add(textGLSLColors); bg = new ButtonGroup(); int selectedGLSLScheme = getPrefs().getInt(OPTION_GLSL_COLORSCHEME, 0); if (selectedGLSLScheme < 0 || selectedGLSLScheme >= COLORSCHEME.length) { selectedGLSLScheme = 0; } for (int i = 0; i < COLORSCHEME.length; i++) { selectGlslColorScheme[i] = new JRadioButtonMenuItem(COLORSCHEME[i][1], selectedGLSLScheme == i); selectGlslColorScheme[i].setToolTipText(COLORSCHEME[i][2]); textGLSLColors.add(selectGlslColorScheme[i]); bg.add(selectGlslColorScheme[i]); } JMenu textLUAColors = new JMenu("Color Scheme for LUA"); textMisc.add(textLUAColors); bg = new ButtonGroup(); int selectedLUAScheme = getPrefs().getInt(OPTION_LUA_COLORSCHEME, 0); if (selectedLUAScheme < 0 || selectedLUAScheme >= COLORSCHEME.length) { selectedLUAScheme = 0; } for (int i = 0; i < COLORSCHEME.length; i++) { selectLuaColorScheme[i] = new JRadioButtonMenuItem(COLORSCHEME[i][1], selectedLUAScheme == i); selectLuaColorScheme[i].setToolTipText(COLORSCHEME[i][2]); textLUAColors.add(selectLuaColorScheme[i]); bg.add(selectLuaColorScheme[i]); } JMenu textSQLColors = new JMenu("Color Scheme for SQL"); textMisc.add(textSQLColors); bg = new ButtonGroup(); int selectedSQLScheme = getPrefs().getInt(OPTION_SQL_COLORSCHEME, 0); if (selectedSQLScheme < 0 || selectedSQLScheme >= COLORSCHEME.length) { selectedSQLScheme = 0; } for (int i = 0; i < COLORSCHEME.length; i++) { selectSqlColorScheme[i] = new JRadioButtonMenuItem(COLORSCHEME[i][1], selectedSQLScheme == i); selectSqlColorScheme[i].setToolTipText(COLORSCHEME[i][2]); textSQLColors.add(selectSqlColorScheme[i]); bg.add(selectSqlColorScheme[i]); } optionGLSLEnableSyntax = new JCheckBoxMenuItem("Enable Syntax Highlighting for GLSL", getPrefs().getBoolean(OPTION_GLSL_SYNTAXHIGHLIGHTING, true)); textMisc.add(optionGLSLEnableSyntax); optionLUAEnableSyntax = new JCheckBoxMenuItem("Enable Syntax Highlighting for LUA", getPrefs().getBoolean(OPTION_LUA_SYNTAXHIGHLIGHTING, true)); textMisc.add(optionLUAEnableSyntax); optionSQLEnableSyntax = new JCheckBoxMenuItem("Enable Syntax Highlighting for SQL", getPrefs().getBoolean(OPTION_SQL_SYNTAXHIGHLIGHTING, true)); textMisc.add(optionSQLEnableSyntax); optionGLSLEnableCodeFolding = new JCheckBoxMenuItem("Enable Code Folding for GLSL", getPrefs().getBoolean(OPTION_GLSL_CODEFOLDING, false)); textMisc.add(optionGLSLEnableCodeFolding); // Options->Text Editor (continued) optionTextHightlightCurrent = new JCheckBoxMenuItem("Show Highlighted Current Line", getPrefs().getBoolean(OPTION_TEXT_SHOWCURRENTLINE, true)); textMenu.add(optionTextHightlightCurrent); optionTextLineNumbers = new JCheckBoxMenuItem("Show Line Numbers", getPrefs().getBoolean(OPTION_TEXT_SHOWLINENUMBERS, true)); textMenu.add(optionTextLineNumbers); // Options->Text Editor->Debug: External color scheme if (NearInfinity.isDebug()) { JMenu textDebug = new JMenu("Debug: External color scheme"); textMenu.add(textDebug); optionTextDebugColorSchemeEnabled = new JCheckBoxMenuItem("Color scheme enabled", getPrefs().getBoolean(OPTION_TEXT_DEBUG_ENABLECOLORSCHEME, true)); textDebug.add(optionTextDebugColorSchemeEnabled); textDebug.addSeparator(); optionTextDebugColorSchemeSelect = new JMenuItem("Select color scheme..."); optionTextDebugColorSchemeSelect.addActionListener(this); textDebug.add(optionTextDebugColorSchemeSelect); loadDebugColorScheme(getPrefs().get(OPTION_TEXT_DEBUG_COLORSCHEME, "")); } // Options->Show ResourceRefs As JMenu showresrefmenu = new JMenu("Show ResourceRefs As"); add(showresrefmenu); int selectedresref = getPrefs().getInt(OPTION_SHOWRESREF, RESREF_REF_NAME); showResRef[RESREF_ONLY] = new JRadioButtonMenuItem("Filename", selectedresref == RESREF_ONLY); showResRef[RESREF_ONLY].setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_1, CTRL_MASK)); showResRef[RESREF_REF_NAME] = new JRadioButtonMenuItem("Filename (Name)", selectedresref == RESREF_REF_NAME); showResRef[RESREF_REF_NAME].setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_2, CTRL_MASK)); showResRef[RESREF_NAME_REF] = new JRadioButtonMenuItem("Name (Filename)", selectedresref == RESREF_NAME_REF); showResRef[RESREF_NAME_REF].setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_3, CTRL_MASK)); bg = new ButtonGroup(); for (int i = RESREF_ONLY; i <= RESREF_NAME_REF; i++) { showresrefmenu.add(showResRef[i]); bg.add(showResRef[i]); } // Options->Show Override Files JMenu overridesubmenu = new JMenu("Show Override Files"); add(overridesubmenu); int selectedmode = getPrefs().getInt(OPTION_SHOWOVERRIDES, OVERRIDE_SPLIT); showOverrides[OVERRIDE_IN_THREE] = new JRadioButtonMenuItem("In ??? Folders (CRE, SPL, ...)", selectedmode == OVERRIDE_IN_THREE); showOverrides[OVERRIDE_IN_OVERRIDE] = new JRadioButtonMenuItem("In Override Folder", selectedmode == OVERRIDE_IN_OVERRIDE); showOverrides[OVERRIDE_SPLIT] = new JRadioButtonMenuItem("Split Between ??? and Override Folders", selectedmode == OVERRIDE_SPLIT); showOverrides[OVERRIDE_SPLIT].setToolTipText( "Indexed by Chitin.key => ??? folders; Not indexed => Override folder"); bg = new ButtonGroup(); for (int i = OVERRIDE_IN_THREE; i <= OVERRIDE_SPLIT; i++) { overridesubmenu.add(showOverrides[i]); bg.add(showOverrides[i]); showOverrides[i].setActionCommand("Refresh"); showOverrides[i].addActionListener(NearInfinity.getInstance()); } // Options->Default Structure Display JMenu vieworeditmenu = new JMenu("Default Structure Display"); add(vieworeditmenu); int selectedview = getPrefs().getInt(OPTION_VIEWOREDITSHOWN, DEFAULT_VIEW); viewOrEditShown[DEFAULT_VIEW] = new JRadioButtonMenuItem("View", selectedview == DEFAULT_VIEW); viewOrEditShown[DEFAULT_EDIT] = new JRadioButtonMenuItem("Edit", selectedview == DEFAULT_EDIT); bg = new ButtonGroup(); bg.add(viewOrEditShown[DEFAULT_VIEW]); bg.add(viewOrEditShown[DEFAULT_EDIT]); vieworeditmenu.add(viewOrEditShown[DEFAULT_VIEW]); vieworeditmenu.add(viewOrEditShown[DEFAULT_EDIT]); // Options->Look and Feel JMenu lookandfeelmenu = new JMenu("Look and Feel"); add(lookandfeelmenu); final String selectedLF = getPrefs().get(OPTION_LOOKANDFEELCLASS, DEFAULT_LOOKFEEL.getClassName()); LookAndFeelInfo[] info = UIManager.getInstalledLookAndFeels(); bg = new ButtonGroup(); if (info != null && info.length > 0) { // dynamically create a list of supported look&feel themes DataRadioButtonMenuItem dbmi; for (int i = 0; i < info.length; i++) { dbmi = new DataRadioButtonMenuItem(info[i].getName(), selectedLF.equalsIgnoreCase(info[i].getClassName()), info[i]); lookAndFeel.add(dbmi); bg.add(dbmi); } } else { // fallback solution: adding default look&feel theme DataRadioButtonMenuItem dbmi; dbmi = new DataRadioButtonMenuItem(DEFAULT_LOOKFEEL.getName(), true, DEFAULT_LOOKFEEL); lookAndFeel.add(dbmi); bg.add(dbmi); } if (bg.getSelection() == null) { lookAndFeel.get(0).setSelected(true); } for (final JRadioButtonMenuItem lf : lookAndFeel) { if (lf != null) { lookandfeelmenu.add(lf); lf.setActionCommand("ChangeLook"); lf.addActionListener(NearInfinity.getInstance()); } } // Options->Text Font JMenu scriptmenu = new JMenu("Text Font"); add(scriptmenu); bg = new ButtonGroup(); int selectedFont = getPrefs().getInt(OPTION_FONT, 0); selectedFont = Math.min(Math.max(selectedFont, 0), FONTS.length - 1); for (int i = 0; i < FONTS.length; i++) { if (FONTS[i] != null) { selectFont[i] = new JRadioButtonMenuItem(FONTS[i].getName() + ' ' + FONTS[i].getSize(), i == selectedFont); selectFont[i].setFont(FONTS[i]); } else { Font font = null; String fontName = getPrefs().get(OPTION_FONT_NAME, ""); if (!fontName.isEmpty()) { font = new Font(fontName, getPrefs().getInt(OPTION_FONT_STYLE, Font.PLAIN), getPrefs().getInt(OPTION_FONT_SIZE, 12)); } selectFont[i] = new JRadioButtonMenuItem("Select font...", i == selectedFont); selectFont[i].addActionListener(this); applyCustomFont(font); } scriptmenu.add(selectFont[i]); bg.add(selectFont[i]); } // Options->TLK Charset String charset = getPrefs().get(OPTION_TLKCHARSET, DefaultCharset); if (!charsetAvailable(charset)) { System.err.println(String.format("Charset \"%1$s\" not available.", charset)); charset = DefaultCharset; } if (!charsetName(charset).equals(StringResource.getCharset().name())) { StringResource.setCharset(charsetName(charset)); } mCharsetMenu = initCharsetMenu(charset); add(mCharsetMenu); // Options->TLK Language mLanguageMenu = new JMenu("TLK Language (EE only)"); add(mLanguageMenu); languageDefinition = getPrefs().get(OPTION_LANGUAGE_GAMES, ""); } // (Re-)creates a list of available TLK languages private void resetGameLanguage() { // removing old list of available game languages for (JRadioButtonMenuItem r: gameLanguage.keySet()) { r.removeActionListener(this); } mLanguageMenu.removeAll(); gameLanguage.clear(); // initializing new list of available game languages String selectedCode = getGameLanguage(languageDefinition, Profile.getGame()); ButtonGroup bg = new ButtonGroup(); JRadioButtonMenuItem rbmi; // adding "Autodetect" for all available game types rbmi = createLanguageMenuItem(LANGUAGE_AUTODETECT, "Autodetect", "Autodetect language from baldur.ini. " + "Defaults to english if not available.", bg, true); mLanguageMenu.add(rbmi); if (Profile.isEnhancedEdition()) { List<String> languages = Profile.getProperty(Profile.Key.GET_GAME_LANG_FOLDER_NAMES_AVAILABLE); for (final String lang: languages) { String langName = getDisplayLanguage(lang); if (!langName.equalsIgnoreCase(lang)) { rbmi = createLanguageMenuItem(lang, String.format("%1$s (%2$s)", langName, lang), null, bg, selectedCode.equalsIgnoreCase(lang)); mLanguageMenu.add(rbmi); } else { rbmi = createLanguageMenuItem(lang, lang, null, bg, selectedCode.equalsIgnoreCase(lang)); mLanguageMenu.add(rbmi); } } } else { rbmi.setEnabled(false); rbmi.setToolTipText(null); } } // Returns the name of the language specified by the given language code private String getDisplayLanguage(String langCode) { String retVal = langCode; String[] lang = langCode.split("_"); if (lang.length >= 2) { retVal = (new Locale(lang[0], lang[1])).getDisplayLanguage(); if (retVal == null || retVal.isEmpty()) { retVal = langCode; } } return retVal; } // Initializes and returns a radio button menuitem private JRadioButtonMenuItem createLanguageMenuItem(String code, String name, String tooltip, ButtonGroup bg, boolean selected) { JRadioButtonMenuItem rbmi = null; if (code == null) { code = ""; } if (name != null && !name.isEmpty()) { rbmi = new JRadioButtonMenuItem(name); if (tooltip != null && !tooltip.isEmpty()) { rbmi.setToolTipText(tooltip); } if (bg != null) { bg.add(rbmi); } rbmi.setSelected(selected); rbmi.addItemListener(this); gameLanguage.put(rbmi, code); } return rbmi; } private JMenu initCharsetMenu(String charset) { bgCharsetButtons = new ButtonGroup(); JMenu menu = new JMenu("TLK Charset"); DataRadioButtonMenuItem dmi = new DataRadioButtonMenuItem("Autodetect Charset", false, DefaultCharset); dmi.setToolTipText("Attempts to determine the correct character encoding automatically. " + "May not work reliably for non-english games."); dmi.addActionListener(this); bgCharsetButtons.add(dmi); menu.add(dmi); // creating primary list of charsets for (int i = 0; i < CharsetsUsed.size(); i++) { String[] info = CharsetsUsed.get(i); if (info != null && info.length > 2) { dmi = new DataRadioButtonMenuItem(info[0], false, info[1]); StringBuilder sb = new StringBuilder(); sb.append(info[2]); Charset cs = Charset.forName(info[1]); if (cs != null && !cs.aliases().isEmpty()) { sb.append(" Charset aliases: "); Iterator<String> iter = cs.aliases().iterator(); while (iter.hasNext()) { sb.append(iter.next()); if (iter.hasNext()) sb.append(", "); } } dmi.setToolTipText(sb.toString()); dmi.addActionListener(this); bgCharsetButtons.add(dmi); menu.add(dmi); } } int count = 0; JMenu menu2 = new JMenu("More character sets"); menu.add(menu2); // creating secondary list(s) of charsets Iterator<String> iter = Charset.availableCharsets().keySet().iterator(); if (iter != null) { while (iter.hasNext()) { String name= iter.next(); // check whether charset has already been added boolean match = false; for (int i = 0; i < CharsetsUsed.size(); i++) { String[] info = CharsetsUsed.get(i); if (info != null && info.length > 2) { if (name.equalsIgnoreCase(info[1])) { match = true; break; } } } if (match) { continue; } boolean official = !(name.startsWith("x-") || name.startsWith("X-")); String desc = official ? name : String.format("%1$s (unofficial)", name.substring(2)); dmi = new DataRadioButtonMenuItem(desc, false, name); Charset cs = Charset.forName(name); if (cs != null && !cs.aliases().isEmpty()) { StringBuilder sb = new StringBuilder("Charset aliases: "); Iterator<String> csIter = cs.aliases().iterator(); while (csIter.hasNext()) { sb.append(csIter.next()); if (csIter.hasNext()) sb.append(", "); } dmi.setToolTipText(sb.toString()); } dmi.addActionListener(this); bgCharsetButtons.add(dmi); menu2.add(dmi); count++; // splitting list of charsets into manageable segments if (count % 30 == 0) { JMenu tmpMenu = new JMenu("More character sets"); menu2.add(tmpMenu); menu2 = tmpMenu; } } } // Selecting specified menu item dmi = findCharsetButton(charset); if (dmi == null) { dmi = findCharsetButton(DefaultCharset); } if (dmi != null) { dmi.setSelected(true); } return menu; } // Returns the menuitem that is associated with the specified string private DataRadioButtonMenuItem findCharsetButton(String charset) { if (bgCharsetButtons != null && charset != null && !charset.isEmpty()) { Enumeration<AbstractButton> buttonSet = bgCharsetButtons.getElements(); while (buttonSet.hasMoreElements()) { AbstractButton b = buttonSet.nextElement(); if (b instanceof DataRadioButtonMenuItem) { Object data = ((DataRadioButtonMenuItem)b).getData(); if (data instanceof String) { if (charset.equalsIgnoreCase((String)data)) { return (DataRadioButtonMenuItem)b; } } } } } return null; } // Returns the charset string associated with the currently selected charset menuitem private String getSelectedButtonData() { Enumeration<AbstractButton> buttonSet = bgCharsetButtons.getElements(); if (buttonSet != null) { while (buttonSet.hasMoreElements()) { AbstractButton b = buttonSet.nextElement(); if (b instanceof DataRadioButtonMenuItem) { DataRadioButtonMenuItem dmi = (DataRadioButtonMenuItem)b; if (dmi.isSelected()) { return (dmi.getData() != null) ? (String)dmi.getData() : DefaultCharset; } } } } return DefaultCharset; } // Attempts to determine the correct charset for the current game private String charsetName(String charset) { // TODO: detect specific localizations if (DefaultCharset.equalsIgnoreCase(charset)) { if (Profile.isEnhancedEdition()) { return "UTF-8"; } else { return "windows-1252"; } } else { return charset; } } private boolean charsetAvailable(String charset) { if (charset != null && !charset.isEmpty()) { if (DefaultCharset.equalsIgnoreCase(charset)) { return true; } try { return (Charset.forName(charset) != null); } catch (Throwable t) { return false; } } return false; } private void gameLoaded() { // update charset selection StringResource.setCharset(charsetName(getSelectedButtonData())); // update language selection resetGameLanguage(); } private void storePreferences() { getPrefs().putBoolean(OPTION_SHOWOFFSETS, optionShowOffset.isSelected()); getPrefs().putBoolean(OPTION_BACKUPONSAVE, optionBackupOnSave.isSelected()); getPrefs().putBoolean(OPTION_IGNOREOVERRIDE, optionIgnoreOverride.isSelected()); getPrefs().putBoolean(OPTION_IGNOREREADERRORS, optionIgnoreReadErrors.isSelected()); getPrefs().putBoolean(OPTION_AUTOCHECK_BCS, optionAutocheckBCS.isSelected()); getPrefs().putBoolean(OPTION_CACHEOVERRIDE, optionCacheOverride.isSelected()); getPrefs().putBoolean(OPTION_CHECKSCRIPTNAMES, optionCheckScriptNames.isSelected()); getPrefs().putBoolean(OPTION_SHOWSTRREFS, optionShowStrrefs.isSelected()); getPrefs().putBoolean(OPTION_DLG_SHOWICONS, optionDlgShowIcons.isSelected()); getPrefs().putBoolean(OPTION_SHOWHEXCOLORED, optionShowHexColored.isSelected()); getPrefs().putInt(OPTION_SHOWRESREF, getResRefMode()); getPrefs().putInt(OPTION_SHOWOVERRIDES, getOverrideMode()); getPrefs().put(OPTION_LOOKANDFEELCLASS, getLookAndFeel().getClassName()); getPrefs().putInt(OPTION_VIEWOREDITSHOWN, getDefaultStructView()); int selectedFont = getSelectedButtonIndex(selectFont, 0); getPrefs().putInt(OPTION_FONT, selectedFont); Font font = FONTS[FONTS.length - 1]; if (font != null) { getPrefs().put(OPTION_FONT_NAME, font.getName()); getPrefs().putInt(OPTION_FONT_STYLE, font.getStyle()); getPrefs().putInt(OPTION_FONT_SIZE, font.getSize()); } int selectedIndent = getSelectedButtonIndex(selectBcsIndent, 0); getPrefs().putInt(OPTION_BCS_INDENT, selectedIndent); getPrefs().putBoolean(OPTION_TEXT_SHOWCURRENTLINE, optionTextHightlightCurrent.isSelected()); getPrefs().putBoolean(OPTION_TEXT_SHOWLINENUMBERS, optionTextLineNumbers.isSelected()); getPrefs().putBoolean(OPTION_TEXT_SYMBOLWHITESPACE, optionTextShowWhiteSpace.isSelected()); getPrefs().putBoolean(OPTION_TEXT_SYMBOLEOL, optionTextShowEOL.isSelected()); getPrefs().putBoolean(OPTION_TEXT_TABSEMULATED, optionTextTabEmulate.isSelected()); int selectTabSize = getSelectedButtonIndex(selectTextTabSize, 1); getPrefs().putInt(OPTION_TEXT_TABSIZE, selectTabSize); int selectColorScheme = getSelectedButtonIndex(selectBcsColorScheme, 5); getPrefs().putInt(OPTION_BCS_COLORSCHEME, selectColorScheme); getPrefs().putBoolean(OPTION_BCS_SYNTAXHIGHLIGHTING, optionBCSEnableSyntax.isSelected()); getPrefs().putBoolean(OPTION_BCS_CODEFOLDING, optionBCSEnableCodeFolding.isSelected()); getPrefs().putBoolean(OPTION_BCS_AUTO_INDENT, optionBCSEnableAutoIndent.isSelected()); // prefs.putBoolean(OPTION_BCS_AUTOCOMPLETE, optionBCSEnableAutoComplete.isSelected()); selectColorScheme = getSelectedButtonIndex(selectGlslColorScheme, 0); getPrefs().putInt(OPTION_GLSL_COLORSCHEME, selectColorScheme); selectColorScheme = getSelectedButtonIndex(selectLuaColorScheme, 0); getPrefs().putInt(OPTION_LUA_COLORSCHEME, selectColorScheme); selectColorScheme = getSelectedButtonIndex(selectSqlColorScheme, 0); getPrefs().putInt(OPTION_SQL_COLORSCHEME, selectColorScheme); getPrefs().putBoolean(OPTION_GLSL_SYNTAXHIGHLIGHTING, optionGLSLEnableSyntax.isSelected()); getPrefs().putBoolean(OPTION_LUA_SYNTAXHIGHLIGHTING, optionLUAEnableSyntax.isSelected()); getPrefs().putBoolean(OPTION_SQL_SYNTAXHIGHLIGHTING, optionSQLEnableSyntax.isSelected()); getPrefs().putBoolean(OPTION_GLSL_CODEFOLDING, optionGLSLEnableCodeFolding.isSelected()); getPrefs().putInt(OPTION_OPTION_FIXED, optionFixedInternal); if (NearInfinity.isDebug()) { getPrefs().putBoolean(OPTION_TEXT_DEBUG_ENABLECOLORSCHEME, optionTextDebugColorSchemeEnabled.isSelected()); getPrefs().put(OPTION_TEXT_DEBUG_COLORSCHEME, DEBUGCOLORSCHEME); } String charset = getSelectedButtonData(); getPrefs().put(OPTION_TLKCHARSET, charset); getPrefs().put(OPTION_LANGUAGE_GAMES, languageDefinition); } // Returns the (first) index of the selected AbstractButton array private int getSelectedButtonIndex(AbstractButton[] items, int defaultIndex) { int retVal = defaultIndex; if (items != null && items.length > 0) { for (int i = 0; i < items.length; i++) { if (items[i] != null && items[i].isSelected()) { retVal = i; break; } } } return retVal; } // Extracts entries of Game/Language pairs from the given argument private List<Pair<String>> extractGameLanguages(String definition) { List<Pair<String>> list = new ArrayList<Pair<String>>(); if (definition != null && !definition.isEmpty()) { String[] entries = definition.split(";"); if (entries != null) { for (final String entry: entries) { String[] elements = entry.split("="); if (elements != null && elements.length == 2) { Profile.Game game = Profile.gameFromString(elements[0]); if (game != Profile.Game.Unknown) { String lang = elements[1].trim(); Pair<String> pair = null; if (lang.equalsIgnoreCase(LANGUAGE_AUTODETECT)) { pair = new Pair<String>(); pair.setFirst(game.toString()); pair.setSecond(LANGUAGE_AUTODETECT); } else if (lang.matches("[a-z]{2}_[A-Z]{2}")) { pair = new Pair<String>(); pair.setFirst(game.toString()); pair.setSecond(lang); } // check if game/language pair is already in the list if (pair != null) { for (final Pair<String> curPair: list) { if (curPair.getFirst().equalsIgnoreCase(pair.getFirst())) { curPair.setSecond(pair.getSecond()); pair = null; break; } } } if (pair != null) { list.add(pair); } } } } } } return list; } // Creates a formatted string out of the Game/Language pairs included in the given list private String createGameLanguages(List<Pair<String>> list) { StringBuilder sb = new StringBuilder(); if (list != null) { for (Iterator<Pair<String>> iter = list.iterator(); iter.hasNext();) { Pair<String> pair = iter.next(); sb.append(String.format("%1$s=%2$s", pair.getFirst(), pair.getSecond())); if (iter.hasNext()) { sb.append(';'); } } } return sb.toString(); } // Adds or updates the Game/Language pair in the formatted "definition" string private String updateGameLanguages(String definition, Pair<String> pair) { List<Pair<String>> list = extractGameLanguages(definition); if (pair != null && pair.getFirst() != null && pair.getSecond() != null) { // attempt to update existing entry first for (final Pair<String> curPair: list) { if (curPair.getFirst().equalsIgnoreCase(pair.getFirst())) { curPair.setSecond(pair.getSecond()); pair = null; break; } } // add new entry if necessary if (pair != null) { list.add(pair); } return createGameLanguages(list); } return ""; } // Returns the language definition stored in "definition" for the specified game private String getGameLanguage(String definition, Profile.Game game) { if (game != null && game != Profile.Game.Unknown) { List<Pair<String>> list = extractGameLanguages(definition); for (Iterator<Pair<String>> iter = list.iterator(); iter.hasNext();) { Pair<String> pair = iter.next(); Profile.Game curGame = Profile.gameFromString(pair.getFirst()); if (curGame == game) { return pair.getSecond(); } } } return LANGUAGE_AUTODETECT; } // Returns the currently selected game language. Returns empty string on autodetect. private String getSelectedGameLanguage() { String lang = getGameLanguage(languageDefinition, Profile.getGame()); return lang.equalsIgnoreCase(LANGUAGE_AUTODETECT) ? "" : lang; } // Attempts to switch the game language in Enhanced Edition games private void switchGameLanguage(String newLanguage) { if (newLanguage != null) { // switch language and refresh resources String oldLanguage = Profile.getProperty(Profile.Key.GET_GAME_LANG_FOLDER_NAME); String oldLangName = getDisplayLanguage(oldLanguage); String newLanguageCode; if (newLanguage.equalsIgnoreCase(LANGUAGE_AUTODETECT)) { // "Autodetect" must be converted into an actual language code before proceeding newLanguageCode = ResourceFactory.autodetectGameLanguage(Profile.getProperty(Profile.Key.GET_GAME_INI_FILE)); } else { newLanguageCode = newLanguage; } String newLangName = getDisplayLanguage(newLanguageCode); boolean success = false, showErrorMsg = false; if (JOptionPane.showConfirmDialog(NearInfinity.getInstance(), String.format("Do you want to switch from \"%1$s\" to \"%2$s\"?", oldLangName, newLangName), "Switch game language", JOptionPane.YES_NO_OPTION) == JOptionPane.YES_OPTION) { if (Profile.updateGameLanguage(newLanguageCode)) { languageDefinition = updateGameLanguages(languageDefinition, new Pair<String>(Profile.getGame().toString(), newLanguage)); NearInfinity.getInstance().refreshGame(); success = true; } else { showErrorMsg = true; } } if (success == false) { if (showErrorMsg == true) { JOptionPane.showMessageDialog(NearInfinity.getInstance(), "Unable to set new language.", "Error", JOptionPane.ERROR_MESSAGE); } for (Iterator<Map.Entry<JRadioButtonMenuItem, String>> iter = gameLanguage.entrySet().iterator(); iter.hasNext();) { Map.Entry<JRadioButtonMenuItem, String> entry = iter.next(); if (oldLanguage.equalsIgnoreCase(entry.getValue())) { JRadioButtonMenuItem rbmi = entry.getKey(); // don't trigger item event rbmi.removeItemListener(this); entry.getKey().setSelected(true); rbmi.addItemListener(this); Profile.updateGameLanguage(oldLanguage); break; } } } } } // // Returns the path to a color scheme definition file // private String getColorSchemeFile(String rootPath) // { // if (rootPath == null) { // if (ResourceFactory.getInstance() != null) { // rootPath = ResourceFactory.getRootDir().toString(); // } else { // rootPath = "."; // } // } // JFileChooser fc = new JFileChooser(rootPath); // File file = new File(rootPath); // if (!file.isDirectory()) { // fc.setSelectedFile(file); // } // fc.setDialogTitle("Select color scheme definition file"); // fc.setDialogType(JFileChooser.OPEN_DIALOG); // fc.setFileSelectionMode(JFileChooser.FILES_ONLY); // fc.setMultiSelectionEnabled(false); // fc.addChoosableFileFilter(new FileNameExtensionFilter("Color Scheme Definitions (*.xml)", "xml")); // fc.setFileFilter(fc.getChoosableFileFilters()[0]); // if (fc.showOpenDialog(NearInfinity.getInstance()) == JFileChooser.APPROVE_OPTION) { // return fc.getSelectedFile().toString(); // } // return null; // } private void applyCustomFont(Font font) { int index = FONTS.length - 1; FONTS[index] = (font != null) ? font : UIManager.getFont("MenuItem.font").deriveFont(Font.PLAIN); selectFont[index].setText(String.format("Select font... (%1$s %2$d)", FONTS[index].getName(), FONTS[index].getSize())); selectFont[index].setFont(FONTS[index].deriveFont((float)12.0f)); } // Returns defValue if masked bit is clear or value if masked bit is already set private boolean fixOption(int mask, boolean defValue, boolean value) { boolean retVal = value; if ((optionFixedInternal & mask) == 0) { retVal = defValue; optionFixedInternal |= mask; } return retVal; } // Returns defValue if masked bit is clear or value if masked bit is already set // private int fixOption(int mask, int defValue, int value) // { // int retVal = value; // if ((optionFixedInternal & mask) == 0) { // retVal = defValue; // optionFixedInternal |= mask; // } // return retVal; // } // Returns defValue if masked bit is clear or value if masked bit is already set // private String fixOption(int mask, String defValue, String value) // { // String retVal = value; // if ((optionFixedInternal & mask) == 0) { // retVal = defValue; // optionFixedInternal |= mask; // } // return retVal; // } public int getTextIndentIndex() { for (int i = 0; i < selectTextTabSize.length; i++) { if (selectTextTabSize[i].isSelected()) { return i; } } return 1; // default } public String getBcsIndent() { int idx = getSelectedButtonIndex(selectBcsIndent, 2); return BCSINDENT[idx][0]; } public String getBcsColorScheme() { int idx = getSelectedButtonIndex(selectBcsColorScheme, 5); return BCSCOLORSCHEME[idx][0]; } public String getGlslColorScheme() { int idx = getSelectedButtonIndex(selectGlslColorScheme, 0); return COLORSCHEME[idx][0]; } public String getLuaColorScheme() { int idx = getSelectedButtonIndex(selectLuaColorScheme, 0); return COLORSCHEME[idx][0]; } public String getSqlColorScheme() { int idx = getSelectedButtonIndex(selectSqlColorScheme, 0); return COLORSCHEME[idx][0]; } // Registers the specified filename as an external color scheme public void loadDebugColorScheme(String fileName) { if (NearInfinity.isDebug()) { if (fileName != null && !fileName.isEmpty() && Files.isRegularFile(FileManager.resolve(fileName))) { // adding specified file reference to list DEBUGCOLORSCHEME = fileName; optionTextDebugColorSchemeSelect .setToolTipText(String.format("Color scheme \"%1$s\" selected", fileName)); } else { // removing file reference from list DEBUGCOLORSCHEME = ""; optionTextDebugColorSchemeSelect.setToolTipText("No color scheme selected"); } } } // Returns the selected external color scheme file (only if enabled and file exists) public String getDebugColorScheme() { if (NearInfinity.isDebug() && optionTextDebugColorSchemeEnabled.isSelected()) { if (DEBUGCOLORSCHEME != null && Files.isRegularFile(FileManager.resolve(DEBUGCOLORSCHEME))) { return DEBUGCOLORSCHEME; } } return null; } public int getResRefMode() { if (showResRef[RESREF_ONLY].isSelected()) return RESREF_ONLY; else if (showResRef[RESREF_NAME_REF].isSelected()) return RESREF_NAME_REF; return RESREF_REF_NAME; } public int getOverrideMode() { if (showOverrides[OVERRIDE_IN_THREE].isSelected()) return OVERRIDE_IN_THREE; else if (showOverrides[OVERRIDE_IN_OVERRIDE].isSelected()) return OVERRIDE_IN_OVERRIDE; return OVERRIDE_SPLIT; } public LookAndFeelInfo getLookAndFeel() { for (int i = 0; i < lookAndFeel.size(); i++) { if (lookAndFeel.get(i) != null && lookAndFeel.get(i).isSelected()) { return (LookAndFeelInfo)lookAndFeel.get(i).getData(); } } return DEFAULT_LOOKFEEL; } public int getDefaultStructView() { if (viewOrEditShown[DEFAULT_VIEW].isSelected()) return DEFAULT_VIEW; return DEFAULT_EDIT; } @Override public void actionPerformed(ActionEvent event) { if (event.getSource() == selectFont[selectFont.length - 1]) { int index = FONTS.length - 1; FontChooser fc = new FontChooser(); if (FONTS[index] != null) { fc.setSelectedFont(FONTS[index]); } if (fc.showDialog(NearInfinity.getInstance()) == FontChooser.OK_OPTION) { applyCustomFont(fc.getSelectedFont()); } } else if (event.getSource() instanceof DataRadioButtonMenuItem) { DataRadioButtonMenuItem dmi = (DataRadioButtonMenuItem)event.getSource(); String csName = (String)dmi.getData(); if (csName != null) { StringResource.setCharset(charsetName(csName)); // re-read strings ActionEvent refresh = new ActionEvent(dmi, 0, "Refresh"); NearInfinity.getInstance().actionPerformed(refresh); } } else if (event.getSource() == optionTextDebugColorSchemeSelect) { // Debug: loading external color scheme file Path file = null; if (DEBUGCOLORSCHEME != null) { file = FileManager.resolve(DEBUGCOLORSCHEME); if (!Files.isRegularFile(file)) { file = null; } } Path rootPath = (file != null) ? file.getParent() : Profile.getGameRoot(); JFileChooser fc = new JFileChooser(rootPath.toFile()); if (file != null) { fc.setSelectedFile(file.toFile()); } fc.setDialogTitle("Select color scheme file"); fc.setDialogType(JFileChooser.OPEN_DIALOG); fc.setFileSelectionMode(JFileChooser.FILES_ONLY); fc.setMultiSelectionEnabled(false); FileNameExtensionFilter filter = new FileNameExtensionFilter("Color Scheme files (*.xml)", "xml"); fc.setFileFilter(filter); if (fc.showOpenDialog(NearInfinity.getInstance()) == JFileChooser.APPROVE_OPTION) { loadDebugColorScheme(fc.getSelectedFile().toString()); } fc = null; } } @Override public void itemStateChanged(ItemEvent event) { if (event.getStateChange() == ItemEvent.SELECTED && event.getSource() instanceof JRadioButtonMenuItem && gameLanguage.containsKey(event.getSource())) { switchGameLanguage(gameLanguage.get(event.getSource())); } } } /////////////////////////////// // Help Menu /////////////////////////////// private static final class HelpMenu extends JMenu implements ActionListener { private static final String wikiUrl = "https://github.com/NearInfinityBrowser/NearInfinity/wiki"; private final JMenuItem helpAbout, helpWiki, helpLicense, helpJOrbisLicense, helpFifeLicense, helpJHexViewLicense, helpMonteMediaLicense, helpJFontChooserLicense, helpOracleLicense, helpUpdateSettings, helpUpdateCheck; private HelpMenu() { super("Help"); setMnemonic(KeyEvent.VK_H); helpAbout = makeMenuItem("About Near Infinity", KeyEvent.VK_A, Icons.getIcon(Icons.ICON_ABOUT_16), -1, this); add(helpAbout); helpWiki = makeMenuItem("Near Infinity Wiki", KeyEvent.VK_W, Icons.getIcon(Icons.ICON_HELP_16), -1, this); helpWiki.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_F1, 0)); add(helpWiki); helpLicense = makeMenuItem("Near Infinity License", KeyEvent.VK_N, Icons.getIcon(Icons.ICON_EDIT_16), -1, this); add(helpLicense); JMenu miscLicenses = new JMenu("Third-party licenses"); miscLicenses.setMnemonic(KeyEvent.VK_T); add(miscLicenses); helpFifeLicense = makeMenuItem("Fifesoft License", KeyEvent.VK_F, Icons.getIcon(Icons.ICON_EDIT_16), -1, this); miscLicenses.add(helpFifeLicense); helpJFontChooserLicense = makeMenuItem("JFontChooser License", KeyEvent.VK_C, Icons.getIcon(Icons.ICON_EDIT_16), -1, this); miscLicenses.add(helpJFontChooserLicense); helpJHexViewLicense = makeMenuItem("JHexView License", KeyEvent.VK_H, Icons.getIcon(Icons.ICON_EDIT_16), -1, this); miscLicenses.add(helpJHexViewLicense); helpJOrbisLicense = makeMenuItem("JOrbis License", KeyEvent.VK_J, Icons.getIcon(Icons.ICON_EDIT_16), -1, this); miscLicenses.add(helpJOrbisLicense); helpMonteMediaLicense = makeMenuItem("Monte Media License", KeyEvent.VK_M, Icons.getIcon(Icons.ICON_EDIT_16), -1, this); miscLicenses.add(helpMonteMediaLicense); helpOracleLicense = makeMenuItem("Oracle License", KeyEvent.VK_O, Icons.getIcon(Icons.ICON_EDIT_16), -1, this); miscLicenses.add(helpOracleLicense); addSeparator(); helpUpdateSettings = makeMenuItem("Update settings...", KeyEvent.VK_S, null, -1, this); add(helpUpdateSettings); helpUpdateCheck = makeMenuItem("Check for updates", KeyEvent.VK_U, Icons.getIcon(Icons.ICON_FIND_16), -1, this); add(helpUpdateCheck); } @Override public void actionPerformed(ActionEvent event) { if (event.getSource() == helpAbout) { displayAbout(); } else if (event.getSource() == helpWiki) { UrlBrowser.openUrl(wikiUrl); } else if (event.getSource() == helpLicense) { displayLicense("org/infinity/License.txt", "LGPL License"); } else if (event.getSource() == helpJOrbisLicense) { displayLicense("org/infinity/JOrbis.License.txt", "LGPL License"); } else if (event.getSource() == helpFifeLicense) { displayLicense("org/infinity/RSyntaxTextArea.License.txt", "BSD License"); } else if (event.getSource() == helpJHexViewLicense) { displayLicense("org/infinity/JHexView.License.txt", "GPL License"); } else if (event.getSource() == helpMonteMediaLicense) { displayLicense("org/infinity/MonteMedia.License.txt", "Creative Commons / LGPL License"); } else if (event.getSource() == helpJFontChooserLicense) { displayLicense("org/infinity/JFontChooser.License.txt", "MIT License"); } else if (event.getSource() == helpOracleLicense) { displayLicense("org/infinity/Oracle.License.txt", "BSD License"); } else if (event.getSource() == helpUpdateSettings) { UpdaterSettings.showDialog(NearInfinity.getInstance()); } else if (event.getSource() == helpUpdateCheck) { UpdateInfo info = null; try { WindowBlocker.blockWindow(NearInfinity.getInstance(), true); info = Updater.getInstance().loadUpdateInfo(); if (info == null) { final String msg = "Unable to find update information.\n" + "Please make sure that your Update Settings have been configured correctly."; JOptionPane.showMessageDialog(NearInfinity.getInstance(), msg, "Error", JOptionPane.ERROR_MESSAGE); return; } if (!Updater.isNewRelease(info.getRelease(), false)) { info = null; } } finally { WindowBlocker.blockWindow(NearInfinity.getInstance(), false); } UpdateCheck.showDialog(NearInfinity.getInstance(), info); } } private void displayAbout() { // title string final String versionString = "Near Infinity " + VERSION; // list of current links final ObjectString[] currentLinks = { new ObjectString("Active branch", "https://github.com/Argent77/NearInfinity/"), new ObjectString("Main branch", "https://github.com/NearInfinityBrowser/NearInfinity/"), new ObjectString("Wiki page", wikiUrl), }; // original author final String originalVersion = "From Near Infinity 1.32.1 beta 24"; final String originalCopyright = "Copyright (\u00A9) 2001-2005 - Jon Olav Hauglid"; final ObjectString originalLink = new ObjectString("Website", "http://www.idi.ntnu.no/~joh/ni/"); // List of contributors (ordered chronologically) final String[] contributors = { "devSin", "FredSRichardson", "Taimon", "Valerio Bigiani (aka The Bigg)", "Fredrik Lindgren (aka Wisp)", "Argent77", }; // More contributors, in separate block final String[] contributorsMisc = { "Near Infinity logo/icon by Cuv and Troodon80", }; // copyright message final String[] copyNearInfinityText = { "This program is free and may be distributed according to the terms of ", "the GNU Lesser General Public License." }; // Third-party copyright messages final String[] copyThirdPartyText = { "Most icons (\u00A9) eclipse.org - Common Public License.", "RSyntaxTextArea (\u00A9) Fifesoft - Berkeley Software Distribution License.", "Monte Media Library by Werner Randelshofer - GNU Lesser General Public License.", "JOrbis (\u00A9) JCraft Inc. - GNU Lesser General Public License.", "JHexView by Sebastian Porst - GNU General Public License.", }; // Fixed elements final Font defaultfont = UIManager.getFont("Label.font"); final Font font = defaultfont.deriveFont(13.0f); final Font bigFont = defaultfont.deriveFont(Font.BOLD, 20.0f); final Font smallFont = defaultfont.deriveFont(11.0f); GridBagConstraints gbc = new GridBagConstraints(); // version JLabel lVersion = new JLabel(versionString); lVersion.setFont(bigFont); JPanel pLinks = new JPanel(new GridBagLayout()); { int row = 0; // current links for (int i = 0; i < currentLinks.length; i++, row++) { int top = (i > 0) ? 4 : 0; JLabel lTitle = new JLabel(currentLinks[i].getString() + ":"); lTitle.setFont(font); String link = currentLinks[i].getObject(); JLabel lLink = ViewerUtil.createUrlLabel(link); lLink.setFont(font); gbc = ViewerUtil.setGBC(gbc, 0, row, 1, 1, 1.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.HORIZONTAL, new Insets(top, 0, 0, 0), 0, 0); pLinks.add(lTitle, gbc); gbc = ViewerUtil.setGBC(gbc, 1, row, 1, 1, 1.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.HORIZONTAL, new Insets(top, 4, 0, 0), 0, 0); pLinks.add(lLink, gbc); } // original author block JLabel label = new JLabel(originalVersion); label.setFont(font); gbc = ViewerUtil.setGBC(gbc, 0, row, 2, 1, 1.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.HORIZONTAL, new Insets(16, 0, 0, 0), 0, 0); pLinks.add(label, gbc); row++; label = new JLabel(originalCopyright); label.setFont(font); gbc = ViewerUtil.setGBC(gbc, 0, row, 2, 1, 1.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.HORIZONTAL, new Insets(0, 0, 0, 0), 0, 0); pLinks.add(label, gbc); row++; label = new JLabel(originalLink.getString() + ":"); label.setFont(font); gbc = ViewerUtil.setGBC(gbc, 0, row, 1, 1, 0.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.HORIZONTAL, new Insets(4, 0, 0, 0), 0, 0); pLinks.add(label, gbc); String link = originalLink.getObject(); label = ViewerUtil.createUrlLabel(link); label.setFont(font); gbc = ViewerUtil.setGBC(gbc, 1, row, 1, 1, 1.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.HORIZONTAL, new Insets(4, 8, 0, 0), 0, 0); pLinks.add(label, gbc); row++; } // contributors JPanel pContrib = new JPanel(new GridBagLayout()); { // trying to limit line width to a certain maximum FontMetrics fm = getFontMetrics(font); double maxWidth = 0.0; for (int i = 0; i < currentLinks.length; i++) { String s = currentLinks[i].getString() + ": " + currentLinks[i].getObject(); maxWidth = Math.max(maxWidth, fm.getStringBounds(s, getGraphics()).getWidth()); } // adding title int row = 0; JLabel label = new JLabel("Additional Contributors (in chronological order):"); label.setFont(smallFont.deriveFont(12.0f)); gbc = ViewerUtil.setGBC(gbc, 0, row, 1, 1, 1.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.HORIZONTAL, new Insets(0, 0, 2, 0), 0, 0); pContrib.add(label, gbc); row++; // adding names StringBuilder sb = new StringBuilder(); for (int i = 0; i < contributors.length; i++) { if (i > 0) { if (i+1 == contributors.length) { sb.append(" and "); } else { sb.append(", "); } } String s = sb.toString() + contributors[i]; if (fm.getStringBounds(s, getGraphics()).getWidth() > maxWidth) { label = new JLabel(sb.toString()); label.setFont(smallFont); gbc = ViewerUtil.setGBC(gbc, 0, row, 1, 1, 1.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.HORIZONTAL, new Insets(0, 0, 0, 0), 0, 0); pContrib.add(label, gbc); row++; sb = new StringBuilder(); } sb.append(contributors[i]); } label = new JLabel(sb.toString()); label.setFont(smallFont); gbc = ViewerUtil.setGBC(gbc, 0, row, 1, 1, 1.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.HORIZONTAL, new Insets(0, 0, 0, 0), 0, 0); pContrib.add(label, gbc); row++; // Adding misc. contributors for (int i = 0; i < contributorsMisc.length; i++) { label = new JLabel(contributorsMisc[i]); label.setFont(smallFont); gbc = ViewerUtil.setGBC(gbc, 0, row, 1, 1, 1.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.HORIZONTAL, new Insets(i == 0 ? 4 : 0, 0, 0, 0), 0, 0); pContrib.add(label, gbc); row++; } } // Near Infinity license JPanel pLicense = new JPanel(new GridBagLayout()); { int row = 0; JLabel label = new JLabel("Near Infinity license:"); label.setFont(smallFont.deriveFont(12.0f)); gbc = ViewerUtil.setGBC(gbc, 0, row, 1, 1, 1.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.HORIZONTAL, new Insets(0, 0, 2, 0), 0, 0); pLicense.add(label, gbc); row++; for (int i = 0; i < copyNearInfinityText.length; i++) { label = new JLabel(copyNearInfinityText[i]); label.setFont(smallFont); gbc = ViewerUtil.setGBC(gbc, 0, row, 1, 1, 1.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.HORIZONTAL, new Insets(0, 0, 0, 0), 0, 0); pLicense.add(label, gbc); row++; } } // Additional licenses JPanel pMiscLicenses = new JPanel(new GridBagLayout()); { int row = 0; JLabel label = new JLabel("Additional licenses:"); label.setFont(smallFont.deriveFont(12.0f)); gbc = ViewerUtil.setGBC(gbc, 0, row, 1, 1, 1.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.HORIZONTAL, new Insets(0, 0, 2, 0), 0, 0); pMiscLicenses.add(label, gbc); row++; for (int i = 0; i < copyThirdPartyText.length; i++) { label = new JLabel(copyThirdPartyText[i]); label.setFont(smallFont); gbc = ViewerUtil.setGBC(gbc, 0, row, 1, 1, 1.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.HORIZONTAL, new Insets(0, 0, 0, 0), 0, 0); pMiscLicenses.add(label, gbc); row++; } } // putting all together JPanel panel = new JPanel(new GridBagLayout()); gbc = ViewerUtil.setGBC(gbc, 0, 0, 1, 1, 0.0, 1.0, GridBagConstraints.FIRST_LINE_START, GridBagConstraints.HORIZONTAL, new Insets(0, 0, 0, 0), 0, 0); panel.add(lVersion, gbc); gbc = ViewerUtil.setGBC(gbc, 0, 1, 1, 1, 0.0, 1.0, GridBagConstraints.FIRST_LINE_START, GridBagConstraints.HORIZONTAL, new Insets(16, 0, 0, 0), 0, 0); panel.add(pLinks, gbc); gbc = ViewerUtil.setGBC(gbc, 0, 2, 1, 1, 0.0, 1.0, GridBagConstraints.FIRST_LINE_START, GridBagConstraints.HORIZONTAL, new Insets(16, 0, 0, 0), 0, 0); panel.add(pContrib, gbc); gbc = ViewerUtil.setGBC(gbc, 0, 3, 1, 1, 0.0, 1.0, GridBagConstraints.FIRST_LINE_START, GridBagConstraints.HORIZONTAL, new Insets(16, 0, 0, 0), 0, 0); panel.add(pLicense, gbc); gbc = ViewerUtil.setGBC(gbc, 0, 4, 1, 1, 0.0, 1.0, GridBagConstraints.FIRST_LINE_START, GridBagConstraints.HORIZONTAL, new Insets(16, 0, 0, 0), 0, 0); panel.add(pMiscLicenses, gbc); JOptionPane.showMessageDialog(NearInfinity.getInstance(), panel, "About Near Infinity", JOptionPane.INFORMATION_MESSAGE, Icons.getIcon(Icons.ICON_APP_128)); } private void displayLicense(String classPath, String title) { JPanel panel = new JPanel(new BorderLayout()); JTextPane tphelp = new JTextPane(); tphelp.setFont(new Font("Monospaced", Font.PLAIN, 12)); tphelp.setEditable(false); tphelp.setMargin(new Insets(3, 3, 3, 3)); panel.add(new JScrollPane(tphelp), BorderLayout.CENTER); panel.setPreferredSize(new Dimension(640, 480)); try { tphelp.setPage(ClassLoader.getSystemResource(classPath)); } catch (IOException e) { e.printStackTrace(); } JOptionPane.showMessageDialog(NearInfinity.getInstance(), panel, title, JOptionPane.PLAIN_MESSAGE); } } // Manages bookmarked game entries static final class Bookmark implements Cloneable { // "Bookmarks" preferences entries (numbers are 1-based) private static final String BOOKMARK_NUM_ENTRIES = "BookmarkEntries"; private static final String FMT_BOOKMARK_NAME = "BookmarkName%1$d"; private static final String FMT_BOOKMARK_ID = "BookmarkID%1$d"; private static final String FMT_BOOKMARK_PATH = "BookmarkPath%1$d"; private static final String MENUITEM_COMMAND = "OpenBookmark"; private final Profile.Game game; private final String path; private String name; private ActionListener listener; private JMenuItem item; public Bookmark(String name, Profile.Game game, String path, ActionListener listener) { if (game == null || game == Profile.Game.Unknown || path == null) { throw new NullPointerException(); } if (name == null || name.trim().isEmpty()) { name = Profile.getProperty(Profile.Key.GET_GLOBAL_GAME_TITLE, game); } this.name = name; this.game = game; this.path = path; this.listener = listener; updateMenuItem(); } @Override public String toString() { return getName(); } @Override public Object clone() throws CloneNotSupportedException { return new Bookmark(getName(), getGame(), getPath(), listener); } /** Returns user-defined game name. */ public String getName() { return name; } /** Sets a new name and returns the previous name (if available). */ public String setName(String newName) { String retVal = getName(); if (newName != null && !newName.trim().isEmpty()) { this.name = newName; updateMenuItem(); } return retVal; } /** Returns game type. */ public Profile.Game getGame() { return game; } /** Returns game path (i.e. full path to the chitin.key). */ public String getPath() { return path; } /** Returns associated menu item. */ public JMenuItem getMenuItem() { return item; } /** Returns whether the bookmark points to an existing game installation. */ public boolean isEnabled() { return (Files.isRegularFile(FileManager.resolve(path))); } /** Returns ActionListener used by the associated menu item. */ public ActionListener getActionListener() { return listener; } /** Assigns a new ActionListener object to the associated menu item. */ public void setActionListener(ActionListener listener) { if (item != null) { item.removeActionListener(this.listener); } this.listener = listener; if (listener != null && item != null) { item.addActionListener(this.listener); } } // Creates or updates associated menu item private void updateMenuItem() { if (item == null) { item = new JMenuItem(getName()); item.setToolTipText(path); item.setActionCommand(MENUITEM_COMMAND); if (listener != null) { item.addActionListener(listener); } } else { item.setText(getName()); } item.setEnabled(isEnabled()); } /** Returns the command string used for all menu items. */ public static String getCommand() { return MENUITEM_COMMAND; } /** Returns the Preferences key for the number of available Bookmark entries. */ public static String getEntryCountKey() { return BOOKMARK_NUM_ENTRIES; } /** Returns the Preferences key for a specific BookmarkID. */ public static String getGameKey(int idx) { if (idx >= 0) { return String.format(FMT_BOOKMARK_ID, idx+1); } else { return null; } } /** Returns the Preferences key for a specific BookmarkPath. */ public static String getPathKey(int idx) { if (idx >= 0) { return String.format(FMT_BOOKMARK_PATH, idx+1); } else { return null; } } /** Returns the Preferences key for a specific BookmarkName. */ public static String getNameKey(int idx) { if (idx >= 0) { return String.format(FMT_BOOKMARK_NAME, idx+1); } else { return null; } } } // Manages individual "Recently used games" entries static final class RecentGame implements Cloneable { // "Recently opened games" preferences entries (numbers are 1-based) private static final int MAX_LASTGAME_ENTRIES = 10; private static final String FMT_LASTGAME_IDS = "LastGameID%1$d"; private static final String FMT_LASTGAME_PATH = "LastGamePath%1$d"; private static final String MENUITEM_COMMAND = "OpenOldGame"; private final Profile.Game game; private final String path; private JMenuItem item; private ActionListener listener; private int index; public RecentGame(Profile.Game game, String path, int index, ActionListener listener) { if (game == null || game == Profile.Game.Unknown || path == null || !Files.isRegularFile(FileManager.resolve(path))) { throw new NullPointerException(); } this.game = game; this.path = path; this.index = -1; this.listener = listener; setIndex(index); } @Override public String toString() { if (index >= 0) { return String.format("%1$d %2$s", index+1, Profile.getProperty(Profile.Key.GET_GLOBAL_GAME_TITLE, game)); } else { return Profile.getProperty(Profile.Key.GET_GLOBAL_GAME_TITLE, game); } } @Override public Object clone() throws CloneNotSupportedException { return new RecentGame(getGame(), getPath(), getIndex(), getActionListener()); } /** Returns game type. */ public Profile.Game getGame() { return game; } /** Returns game path (i.e. full path to the chitin.key). */ public String getPath() { return path; } /** Returns associated menu item. */ public JMenuItem getMenuItem() { return item; } /** Returns current entry index. */ public int getIndex() { return index; } /** Updates existing menu item or creates a new one, based on the given index. */ public void setIndex(int index) { if (index >= 0 && index < getEntryCount() && index != this.index) { this.index = index; if (item == null) { item = new JMenuItem(toString()); item.setToolTipText(path); item.setActionCommand(MENUITEM_COMMAND); if (listener != null) { item.addActionListener(listener); } } else { item.setText(toString()); } } } /** Returns ActionListener used by the associated menu item. */ public ActionListener getActionListener() { return listener; } /** Assigns a new ActionListener object to the associated menu item. */ public void setActionListener(ActionListener listener) { if (item != null) { item.removeActionListener(this.listener); } this.listener = listener; if (listener != null && item != null) { item.addActionListener(this.listener); } } /** Removes the currently associated menu item. */ public void clear() { if (item != null) { if (listener != null) { item.removeActionListener(listener); item.setEnabled(false); if (item.getParent() != null) { item.getParent().remove(item); } item = null; } } } /** Returns the command string used for all menu items. */ public static String getCommand() { return MENUITEM_COMMAND; } /** Returns the max. number of supported last game entries. */ public static int getEntryCount() { return MAX_LASTGAME_ENTRIES; } /** Returns the Preferences key for a specific LastGameID. */ public static String getGameKey(int index) { if (index >= 0 && index < getEntryCount()) { return String.format(FMT_LASTGAME_IDS, index+1); } else { return null; } } /** Returns the Preferences key for a specific LastGamePath. */ public static String getPathKey(int index) { if (index >= 0 && index < getEntryCount()) { return String.format(FMT_LASTGAME_PATH, index+1); } else { return null; } } } }