// 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.converter; import java.awt.BorderLayout; import java.awt.Dialog; import java.awt.GridBagConstraints; import java.awt.GridBagLayout; import java.awt.Insets; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.FocusEvent; import java.awt.event.FocusListener; import java.awt.event.KeyEvent; import java.nio.file.Files; import java.nio.file.Path; import java.util.prefs.Preferences; import javax.swing.AbstractAction; import javax.swing.BorderFactory; import javax.swing.JButton; import javax.swing.JCheckBox; import javax.swing.JComboBox; import javax.swing.JComponent; import javax.swing.JDialog; import javax.swing.JLabel; import javax.swing.JMenuItem; import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JSpinner; import javax.swing.JTextField; import javax.swing.KeyStroke; import javax.swing.SpinnerNumberModel; import javax.swing.WindowConstants; import org.infinity.gui.ButtonPopupMenu; import org.infinity.gui.ViewerUtil; import org.infinity.resource.Profile; import org.infinity.util.io.FileManager; /** * An options dialog for the BAM converter. */ class BamOptionsDialog extends JDialog implements ActionListener, FocusListener { private static final String PREFS_BAMVERSION = "BCBamVersion"; private static final String PREFS_PATH = "BCPath"; private static final String PREFS_AUTOCLEAR = "BCAutoClear"; private static final String PREFS_CLOSEONEXIT = "BCCloseOnExit"; private static final String PREFS_TRANSPARENCY = "BCTransparencyThreshold"; private static final String PREFS_COMPRESSBAM = "BCCompressBam"; private static final String PREFS_COMPRESSTYPE = "BCCompressionType"; private static final String PREFS_PVRZINDEX = "BCPvrzIndex"; // Default settings private static final int DEFAULT_BAM_VERSION = ConvertToBam.VERSION_BAMV1; private static final String DEFAULT_PATH = ""; private static final boolean DEFAULT_AUTO_CLEAR = true; private static final boolean DEFAULT_CLOSE_ON_EXIT = false; private static final int DEFAULT_TRANSPARENCY_THRESHOLD = 5; // in percent private static final boolean DEFAULT_COMPRESS_BAM = false; private static final int DEFAULT_COMPRESSION_TYPE = ConvertToBam.COMPRESSION_AUTO; private static final int DEFAULT_PVRZ_INDEX = 1000; // Current settings private static boolean settingsLoaded = false; private static int bamVersion = DEFAULT_BAM_VERSION; private static String path = DEFAULT_PATH; private static boolean autoClear = DEFAULT_AUTO_CLEAR; private static boolean closeOnExit = DEFAULT_CLOSE_ON_EXIT; private static int transparencyThreshold = DEFAULT_TRANSPARENCY_THRESHOLD; private static boolean compressBam = DEFAULT_COMPRESS_BAM; private static int compressionType = DEFAULT_COMPRESSION_TYPE; private static int pvrzIndex = DEFAULT_PVRZ_INDEX; private JButton bOK, bCancel, bDefaults, bTransparencyHelp; private JComboBox<String> cbBamVersion, cbCompressionType; private JCheckBox cbCloseOnExit, cbAutoClear, cbCompressBam; private JSpinner sTransparency, sPvrzIndex; private JTextField tfPath; private JMenuItem miPathSet, miPathClear; private ButtonPopupMenu bpmPath; /** Attempts to load stored settings from disk. Falls back to default values. */ public static void loadSettings(boolean force) { if (!settingsLoaded || force) { Preferences prefs = Preferences.userNodeForPackage(ConvertToBam.class); bamVersion = prefs.getInt(PREFS_BAMVERSION, DEFAULT_BAM_VERSION); path = prefs.get(PREFS_PATH, DEFAULT_PATH); autoClear = prefs.getBoolean(PREFS_AUTOCLEAR, DEFAULT_AUTO_CLEAR); closeOnExit = prefs.getBoolean(PREFS_CLOSEONEXIT, DEFAULT_CLOSE_ON_EXIT); transparencyThreshold = prefs.getInt(PREFS_TRANSPARENCY, DEFAULT_TRANSPARENCY_THRESHOLD); compressBam = prefs.getBoolean(PREFS_COMPRESSBAM, DEFAULT_COMPRESS_BAM); compressionType = prefs.getInt(PREFS_COMPRESSTYPE, DEFAULT_COMPRESSION_TYPE); pvrzIndex = prefs.getInt(PREFS_PVRZINDEX, DEFAULT_PVRZ_INDEX); validateSettings(); settingsLoaded = true; } } /** Stores the current settings on disk. */ public static void saveSettings() { validateSettings(); Preferences prefs = Preferences.userNodeForPackage(ConvertToBam.class); prefs.putInt(PREFS_BAMVERSION, bamVersion); prefs.put(PREFS_PATH, path); prefs.putBoolean(PREFS_AUTOCLEAR, autoClear); prefs.putBoolean(PREFS_CLOSEONEXIT, closeOnExit); prefs.putInt(PREFS_TRANSPARENCY, transparencyThreshold); prefs.putBoolean(PREFS_COMPRESSBAM, compressBam); prefs.putInt(PREFS_COMPRESSTYPE, compressionType); prefs.putInt(PREFS_PVRZINDEX, pvrzIndex); } // Makes sure that all settings are valid. private static void validateSettings() { bamVersion = Math.min(Math.max(bamVersion, ConvertToBam.VERSION_BAMV1), ConvertToBam.VERSION_BAMV2); if (path == null) path = DEFAULT_PATH; if (!path.isEmpty() && !(Files.isDirectory(FileManager.resolve(path)))) path = DEFAULT_PATH; transparencyThreshold = Math.min(Math.max(transparencyThreshold, 0), 100); compressionType = Math.min(Math.max(compressionType, ConvertToBam.COMPRESSION_AUTO), ConvertToBam.COMPRESSION_DXT5); pvrzIndex = Math.min(Math.max(pvrzIndex, 0), 99999); } /** Returns the default BAM version index. */ public static int getBamVersion() { return bamVersion; } /** Returns the default path. */ public static String getPath() { return path; } /** Returns whether to automatically clear the current BAM after a successful conversion. */ public static boolean getAutoClear() { return autoClear; } /** Returns the default state for the "Close On Exit" checkbox. */ public static boolean getCloseOnExit() { return closeOnExit; } /** Returns the transparency threshold in percent. */ public static int getTransparencyThreshold() { return transparencyThreshold; } /** Returns the default state for "Compress BAM" checkbox (BAM v1). */ public static boolean getCompressBam() { return compressBam; } /** Returns the default compression type (BAM v2). */ public static int getCompressionType() { return compressionType; } /** Returns the default PVRZ index (BAM v2). */ public static int getPvrzIndex() { return pvrzIndex; } public BamOptionsDialog(ConvertToBam parent) { super(parent, "Options", Dialog.ModalityType.DOCUMENT_MODAL); if (parent == null) { throw new NullPointerException(); } init(); } //--------------------- Begin Interface ActionListener --------------------- @Override public void actionPerformed(ActionEvent event) { if (event.getSource() == miPathSet) { Path path = FileManager.resolve(tfPath.getText()); if (!Files.isDirectory(path)) { path = Profile.getGameRoot(); } Path rootPath = ConvertToBam.getOpenPathName(this, "Select initial directory", path); if (rootPath != null) { tfPath.setText(rootPath.toString()); } } else if (event.getSource() == miPathClear) { tfPath.setText(""); } else if (event.getSource() == bTransparencyHelp) { String msg = "Defines a threshold that is used to determine whether a color is considered \"transparent\".\n\n" + "Example:\n" + "A value of 5% will treat any pixels with a transparency of 5% or higher as fully transparent.\n" + "Pixels with less than 5% transparency will be treated as fully opaque.\n\n" + "Note: This setting only affects Legacy BAM (v1) conversions."; JOptionPane.showMessageDialog(this, msg, "Help", JOptionPane.INFORMATION_MESSAGE); } else if (event.getSource() == bDefaults) { setDefaults(); } else if (event.getSource() == bOK) { updateSettings(); saveSettings(); setVisible(false); } else if (event.getSource() == bCancel) { setVisible(false); } } //--------------------- End Interface ActionListener --------------------- //--------------------- Begin Interface FocusListener --------------------- @Override public void focusGained(FocusEvent event) { } @Override public void focusLost(FocusEvent event) { if (event.getSource() == tfPath) { String path = tfPath.getText(); if (!path.isEmpty() && !Files.isDirectory(FileManager.resolve(path))) { tfPath.setText(path); } } } //--------------------- End Interface FocusListener --------------------- private void init() { loadSettings(false); GridBagConstraints c = new GridBagConstraints(); // initializing "General" panel JLabel l1 = new JLabel("Default BAM version:"); JLabel l2 = new JLabel("Default root path:"); cbBamVersion = new JComboBox<>(ConvertToBam.BamVersionItems); cbBamVersion.setSelectedIndex(getBamVersion()); tfPath = new JTextField(); tfPath.setText(getPath()); tfPath.addFocusListener(this); miPathSet = new JMenuItem("Set..."); miPathSet.addActionListener(this); miPathClear = new JMenuItem("Clear"); miPathClear.addActionListener(this); bpmPath = new ButtonPopupMenu("...", new JMenuItem[]{miPathSet, miPathClear}); cbCloseOnExit = new JCheckBox("Select \"Close dialog after conversion\" by default", getCloseOnExit()); cbAutoClear = new JCheckBox("Automatically clear frames and cycles after conversion", getAutoClear()); JPanel pGeneral = new JPanel(new GridBagLayout()); pGeneral.setBorder(BorderFactory.createTitledBorder("General ")); c = ViewerUtil.setGBC(c, 0, 0, 1, 1, 0.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.NONE, new Insets(0, 4, 0, 0), 0, 0); pGeneral.add(l1, c); c = ViewerUtil.setGBC(c, 1, 0, 2, 1, 1.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.NONE, new Insets(0, 4, 0, 4), 0, 0); pGeneral.add(cbBamVersion, c); c = ViewerUtil.setGBC(c, 0, 1, 1, 1, 0.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.NONE, new Insets(4, 4, 0, 0), 0, 0); pGeneral.add(l2, c); c = ViewerUtil.setGBC(c, 1, 1, 1, 1, 1.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.HORIZONTAL, new Insets(4, 4, 0, 0), 0, 0); pGeneral.add(tfPath, c); c = ViewerUtil.setGBC(c, 2, 1, 1, 1, 0.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.NONE, new Insets(4, 4, 0, 4), 0, 0); pGeneral.add(bpmPath, c); c = ViewerUtil.setGBC(c, 0, 2, 3, 1, 1.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.NONE, new Insets(4, 4, 0, 4), 0, 0); pGeneral.add(cbAutoClear, c); c = ViewerUtil.setGBC(c, 0, 3, 3, 1, 1.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.NONE, new Insets(4, 4, 4, 4), 0, 0); pGeneral.add(cbCloseOnExit, c); // initializing "Legacy BAM" panel l1 = new JLabel("Transparency threshold:"); SpinnerNumberModel model = new SpinnerNumberModel(getTransparencyThreshold(), 0, 100, 1); sTransparency = new JSpinner(model); sTransparency.setEditor(new JSpinner.NumberEditor(sTransparency, "#'%'")); bTransparencyHelp = new JButton("?"); bTransparencyHelp.setMargin(new Insets(2, 4, 2, 4)); bTransparencyHelp.setToolTipText("About transparency threshold"); bTransparencyHelp.addActionListener(this); cbCompressBam = new JCheckBox("Select \"Compress BAM\" by default", getCompressBam()); JPanel pBamV1 = new JPanel(new GridBagLayout()); pBamV1.setBorder(BorderFactory.createTitledBorder("Legacy BAM ")); c = ViewerUtil.setGBC(c, 0, 0, 1, 1, 0.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.NONE, new Insets(0, 4, 0, 0), 0, 0); pBamV1.add(l1, c); c = ViewerUtil.setGBC(c, 1, 0, 1, 1, 1.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.HORIZONTAL, new Insets(0, 4, 0, 0), 0, 0); pBamV1.add(sTransparency, c); c = ViewerUtil.setGBC(c, 2, 0, 1, 1, 0.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.NONE, new Insets(0, 4, 0, 4), 0, 0); pBamV1.add(bTransparencyHelp, c); c = ViewerUtil.setGBC(c, 0, 1, 3, 1, 1.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.NONE, new Insets(4, 4, 4, 4), 0, 0); pBamV1.add(cbCompressBam, c); // initializing "PVRZ-based BAM" panel l1 = new JLabel("Default compression:"); l2 = new JLabel("Default PVRZ index:"); cbCompressionType = new JComboBox<>(ConvertToBam.CompressionItems); cbCompressionType.setSelectedIndex(getCompressionType()); model = new SpinnerNumberModel(getPvrzIndex(), 0, 99999, 1); sPvrzIndex = new JSpinner(model); JPanel pBamV2 = new JPanel(new GridBagLayout()); pBamV2.setBorder(BorderFactory.createTitledBorder("PVRZ-based BAM ")); c = ViewerUtil.setGBC(c, 0, 0, 1, 1, 0.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.NONE, new Insets(0, 4, 0, 0), 0, 0); pBamV2.add(l1, c); c = ViewerUtil.setGBC(c, 1, 0, 1, 1, 1.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.HORIZONTAL, new Insets(0, 4, 0, 0), 0, 0); pBamV2.add(cbCompressionType, c); c = ViewerUtil.setGBC(c, 0, 1, 1, 1, 0.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.NONE, new Insets(4, 4, 4, 0), 0, 0); pBamV2.add(l2, c); c = ViewerUtil.setGBC(c, 1, 1, 1, 1, 1.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.HORIZONTAL, new Insets(4, 4, 4, 4), 0, 0); pBamV2.add(sPvrzIndex, c); // initializing bottom button bar bDefaults = new JButton("Set defaults"); bDefaults.addActionListener(this); bDefaults.setMargin(new Insets(4, bDefaults.getInsets().left, 4, bDefaults.getInsets().right)); bCancel = new JButton("Cancel"); bCancel.addActionListener(this); bCancel.setMargin(new Insets(4, bCancel.getInsets().left, 4, bCancel.getInsets().right)); bOK = new JButton("OK"); bOK.addActionListener(this); bOK.setMargin(new Insets(4, bOK.getInsets().left, 4, bOK.getInsets().right)); bOK.setPreferredSize(bCancel.getPreferredSize()); JPanel pButtons = new JPanel(new GridBagLayout()); c = ViewerUtil.setGBC(c, 0, 0, 1, 1, 1.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0); pButtons.add(bDefaults, c); c = ViewerUtil.setGBC(c, 1, 0, 1, 1, 0.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.NONE, new Insets(0, 4, 0, 0), 0, 0); pButtons.add(bOK, c); c = ViewerUtil.setGBC(c, 2, 0, 1, 1, 0.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.NONE, new Insets(0, 4, 0, 0), 0, 0); pButtons.add(bCancel, c); // putting all together JPanel pAll = new JPanel(new GridBagLayout()); c = ViewerUtil.setGBC(c, 0, 0, 2, 1, 1.0, 1.0, GridBagConstraints.FIRST_LINE_START, GridBagConstraints.BOTH, new Insets(8, 8, 0, 8), 0, 0); pAll.add(pGeneral, c); c = ViewerUtil.setGBC(c, 0, 1, 1, 1, 1.0, 1.0, GridBagConstraints.FIRST_LINE_START, GridBagConstraints.BOTH, new Insets(8, 8, 4, 0), 0, 0); pAll.add(pBamV1, c); c = ViewerUtil.setGBC(c, 1, 1, 1, 1, 1.0, 1.0, GridBagConstraints.FIRST_LINE_START, GridBagConstraints.BOTH, new Insets(8, 4, 4, 8), 0, 0); pAll.add(pBamV2, c); c = ViewerUtil.setGBC(c, 0, 2, 2, 1, 1.0, 0.0, GridBagConstraints.FIRST_LINE_START, GridBagConstraints.HORIZONTAL, new Insets(8, 8, 8, 8), 0, 0); pAll.add(pButtons, c); getContentPane().setLayout(new BorderLayout()); getContentPane().add(pAll, BorderLayout.CENTER); pack(); setResizable(false); setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE); getRootPane().getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW) .put(KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), getRootPane()); getRootPane().getActionMap().put(getRootPane(), new AbstractAction() { @Override public void actionPerformed(ActionEvent event) { setVisible(false); } }); setLocationRelativeTo(getOwner()); setVisible(true); } // Applies the default settings to the dialog controls private void setDefaults() { cbBamVersion.setSelectedIndex(DEFAULT_BAM_VERSION); tfPath.setText(DEFAULT_PATH); cbAutoClear.setSelected(DEFAULT_AUTO_CLEAR); cbCloseOnExit.setSelected(DEFAULT_CLOSE_ON_EXIT); sTransparency.setValue(Integer.valueOf(DEFAULT_TRANSPARENCY_THRESHOLD)); cbCompressBam.setSelected(DEFAULT_COMPRESS_BAM); cbCompressionType.setSelectedIndex(DEFAULT_COMPRESSION_TYPE); sPvrzIndex.setValue(Integer.valueOf(DEFAULT_PVRZ_INDEX)); } // Fetches the values from the dialog controls private void updateSettings() { bamVersion = cbBamVersion.getSelectedIndex(); path = tfPath.getText(); autoClear = cbAutoClear.isSelected(); closeOnExit = cbCloseOnExit.isSelected(); transparencyThreshold = (Integer)sTransparency.getValue(); compressBam = cbCompressBam.isSelected(); compressionType = cbCompressionType.getSelectedIndex(); pvrzIndex = (Integer)sPvrzIndex.getValue(); validateSettings(); } }