changeset 20:71029c9bf7cd

Commit overlooked files.
author David Barts <n5jrn@me.com>
date Sat, 21 Nov 2020 10:15:35 -0800
parents 5fa5d15b4a7b
children 0f3634a0816d
files src/name/blackcap/imageprep/MaxDimSpinner.kt src/name/blackcap/imageprep/Menus.kt src/name/blackcap/imageprep/OutQualSpinner.kt src/name/blackcap/imageprep/SettingsDialog.kt
diffstat 4 files changed, 295 insertions(+), 2 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/name/blackcap/imageprep/MaxDimSpinner.kt	Sat Nov 21 10:15:35 2020 -0800
@@ -0,0 +1,34 @@
+/*
+ * A standard spinner for inputting the maximum dimension of an image.
+ */
+package name.blackcap.imageprep
+
+import java.awt.Toolkit
+import javax.swing.*
+import javax.swing.event.ChangeEvent
+import javax.swing.event.ChangeListener
+import java.util.logging.Level
+import java.util.logging.Logger
+
+/* maximum allowable maximum dimension */
+private val MAXDIM = 16384
+
+/* preferred standard dimensions */
+private val STDDIMS = listOf<Int>(1600, 1280, 1024, 800, 640, 512, 400, 320)
+
+class MaxDimSpinner(val default: Int): JSpinner(SpinnerListModel(STDDIMS))
+{
+    init {
+        editor = JSpinner.ListEditor(this)
+        value = default
+        noTaller()
+        addChangeListener( ChangeListener {
+            val v = value as? Int ?: (value as? String)?.toIntOrNull()
+            if ( v == null || v < 1 || v > MAXDIM ) {
+                LOGGER.log(Level.INFO, "bad max dimension: $value")
+                value = default
+                Toolkit.getDefaultToolkit().beep()
+            }
+        })
+    }
+}
--- a/src/name/blackcap/imageprep/Menus.kt	Fri Nov 20 21:38:06 2020 -0800
+++ b/src/name/blackcap/imageprep/Menus.kt	Sat Nov 21 10:15:35 2020 -0800
@@ -85,7 +85,7 @@
     private fun doOpen() {
         val maxDim = MaxDimSpinner(Application.settingsDialog.maxDimension)
         val acc = JPanel().apply {
-            layout = BoxLayout(this, BoxLayout.X_AXIS)
+            layout = BoxLayout(this, BoxLayout.Y_AXIS)
             add(JLabel("Max. dimension:"))
             add(maxDim)
         }
@@ -123,7 +123,7 @@
         }
         val outQual = OutQualSpinner(Application.settingsDialog.outputQuality)
         val acc = JPanel().apply {
-            layout = BoxLayout(this, BoxLayout.X_AXIS)
+            layout = BoxLayout(this, BoxLayout.Y_AXIS)
             add(JLabel("Quality:"))
             add(outQual)
         }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/name/blackcap/imageprep/OutQualSpinner.kt	Sat Nov 21 10:15:35 2020 -0800
@@ -0,0 +1,32 @@
+/*
+ * A standard spinner for inputting the output quality of a JPEG image.
+ */
+package name.blackcap.imageprep
+
+import java.awt.Toolkit
+import javax.swing.*
+import javax.swing.event.ChangeEvent
+import javax.swing.event.ChangeListener
+import java.util.logging.Level
+import java.util.logging.Logger
+
+/* allowable JPEG quality range */
+private val MINQUAL = 0
+private val MAXQUAL = 100
+
+class OutQualSpinner(val default: Int): JSpinner(SpinnerNumberModel(default, MINQUAL, MAXQUAL, 1))
+{
+    init {
+        editor = JSpinner.NumberEditor(this)
+        value = default
+        noTaller()
+        addChangeListener( ChangeListener {
+            val v = value as? Int
+            if (v == null || v < MINQUAL || v > MAXQUAL) {
+                LOGGER.log(Level.INFO, "bad output quality: $value")
+                value = default
+                Toolkit.getDefaultToolkit().beep()
+            }
+        })
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/name/blackcap/imageprep/SettingsDialog.kt	Sat Nov 21 10:15:35 2020 -0800
@@ -0,0 +1,227 @@
+/*
+ * The dialog that controls font corecion.
+ */
+package name.blackcap.imageprep
+
+import java.awt.Dimension
+import java.awt.event.ActionEvent
+import java.awt.event.ActionListener
+import java.io.BufferedWriter
+import java.io.File
+import java.io.FileOutputStream
+import java.io.IOException
+import java.io.OutputStreamWriter
+import java.util.Hashtable
+import java.util.Properties
+import java.util.logging.Level
+import java.util.logging.Logger
+import javax.swing.*
+import javax.swing.event.ChangeEvent
+import javax.swing.event.ChangeListener
+import kotlin.math.log10
+import kotlin.math.pow
+import kotlin.math.roundToInt
+import kotlin.text.toInt
+
+/* work around name shadowing */
+private val _PROPS = PROPERTIES
+
+class SettingsDialog: JDialog(Application.mainFrame) {
+    /* maximum allowed dimension in output */
+    private val _maxDimension = MaxDimSpinner(_PROPS.getInt("maxDimension"))
+    val maxDimension: Int
+    get() {
+        return _maxDimension.value as Int
+    }
+    
+    /* JPEG output quality */
+    private val _outputQuality = OutQualSpinner(_PROPS.getInt("outputQuality"))
+    val outputQuality: Int
+    get() {
+        return _outputQuality.value as Int
+    }
+    
+    /* file name output suffix */
+    private val _outputSuffix = JTextField(_PROPS.getProperty("outputSuffix"), 24).apply {
+        noTaller()
+    }
+    val outputSuffix: String
+    get() {
+        return _outputSuffix.text
+    }
+    
+    /* output to input dir? */
+    private val otid = _PROPS.getBoolean("outputToInputDir")
+    private val outputToInputButton = JRadioButton("Create output file in same folder as input.", otid)
+    val outputToInputDir: Boolean
+    get() {
+        return outputToInputButton.isSelected()
+    }
+    
+    /* output to some other directory */
+    private val outputToButton = JRadioButton("Create output file in:", !otid)
+    
+    /* name of the other directory */
+    private val oto = _getOutputTo()
+    protected val outputToText = JTextField(oto, 40).apply {
+        setEditable(false)
+        noTaller()
+    }
+    
+    /* chooser for other directory */
+    private val _outputTo = JFileChooser(oto).apply {
+        fileSelectionMode = JFileChooser.DIRECTORIES_ONLY
+        setMultiSelectionEnabled(false)
+    }
+    val outputTo: String
+    get() {
+        return _outputTo.selectedFile?.getCanonicalPath() ?: oto
+    }
+    
+    /* button to change that other directory */
+    protected val changeOutputTo = JButton("Change").also {
+        it.noTaller()
+        it.addActionListener(ActionListener {
+            val status = _outputTo.showOpenDialog(this)
+            if (status == JFileChooser.APPROVE_OPTION) {
+                val path = outputTo
+                outputToText.text = path
+            }
+        })
+        it.setEnabled(!otid)
+    }
+    
+    /* radio button group */
+    protected val buttonGroup = ButtonGroup().apply {
+        add(outputToButton)
+        add(outputToInputButton)
+    }
+    
+    /* standard spacing between elements (10 pixels ≅ 1/7") and half that */
+    private val BW = 5
+    private val BW2 = 10
+
+    /* buttons */
+    private val _ok = JButton("OK").also {
+        it.noTaller()
+        it.addActionListener(ActionListener {
+            writeProperties()
+            setVisible(false)
+        })
+    }
+
+    private val _cancel = JButton("Cancel").also {
+        it.noTaller()
+        it.addActionListener(ActionListener {
+            revertValues()
+            setVisible(false)
+        })
+    }
+
+    /* initializer */
+    init {
+        title = "Preferences"
+        contentPane.apply {
+            layout = BoxLayout(this, BoxLayout.Y_AXIS)
+            add(Box(BoxLayout.X_AXIS).apply {
+                border = BorderFactory.createEmptyBorder(BW2, BW2, BW, BW2)
+                add(leftLabel("Maximum dimension:"))
+                add(_maxDimension)
+                add(Box.createGlue())
+                add(leftLabel("Output quality:"))
+                add(_outputQuality)
+            })
+            add(Box(BoxLayout.X_AXIS).apply {
+                border = BorderFactory.createEmptyBorder(BW, BW2, BW, BW2)
+                add(leftLabel("Output filename suffix:"))
+                add(_outputSuffix)
+                add(Box.createGlue())
+            })
+            add(Box(BoxLayout.Y_AXIS).apply {
+                border = BorderFactory.createEmptyBorder(BW, BW2, BW2, BW2)
+                add(outputToInputButton)
+                add(outputToButton)
+                add(Box(BoxLayout.X_AXIS).apply {
+                    add(outputToText)
+                    add(changeOutputTo)
+                    add(Box.createGlue())
+                })
+            })
+            add(Box(BoxLayout.X_AXIS).apply {
+                add(_cancel)
+                add(Box.createGlue())
+                add(_ok)
+            })
+        }
+        rootPane.setDefaultButton(_ok)
+        pack()
+        setResizable(false)
+    }
+
+    private fun leftLabel(text: String) = JLabel(text).apply {
+        alignmentX = JLabel.LEFT_ALIGNMENT
+    }
+
+    private fun revertValues()
+    {
+        _maxDimension.value = _PROPS.getInt("maxDimension")
+        _outputQuality.value = _PROPS.getInt("outputQuality")
+        _outputSuffix.text = _PROPS.getProperty("outputSuffix")
+        val otid = _PROPS.getBoolean("outputToInputDir")
+        outputToInputButton.setSelected(otid)
+        outputToButton.setSelected(!otid)
+        val oto = _getOutputTo()
+        outputToText.text = oto
+        _outputTo.selectedFile = File(oto)
+    }
+
+    private fun writeProperties()
+    {
+        _PROPS.setInt("maxDimension", maxDimension)
+        _PROPS.setInt("outputQuality", outputQuality)
+        _PROPS.setProperty("outputSuffix", outputSuffix)
+        _PROPS.setBoolean("outputToInputDir", outputToInputDir)
+        _PROPS.setProperty("outputTo", outputTo)
+        try {
+            BufferedWriter(OutputStreamWriter(FileOutputStream(PROP_FILE), CHARSET)).use {
+                _PROPS.store(it, " -*- coding: ${CHARSET} -*-")
+            }
+        } catch (e: IOException) {
+            LOGGER.log(Level.WARNING, "IOException writing properties file")
+            val message = e.message
+            if (message != null && !message.isEmpty()) {
+                LOGGER.log(Level.WARNING, message)
+            }
+            JOptionPane.showMessageDialog(Application.mainFrame,
+                "Unable to write settings.",
+                "Error",
+                JOptionPane.ERROR_MESSAGE)
+        }
+    }
+    
+    private fun _getOutputTo(): String = _PROPS.getProperty("outputTo") ?: System.getProperty("user.dir")
+}
+
+fun Properties.getString(key: String): String = getProperty(key) as String
+
+fun Properties.getInt(key: String): Int = getString(key).toInt()
+
+fun Properties.setInt(key: String, value: Int): Unit {
+    setProperty(key, value.toString())
+}
+
+fun Properties.getBoolean(key: String): Boolean {
+    val raw = getProperty(key)
+    if (raw.isNullOrEmpty())
+        return false
+    val c1 = raw[0].toLowerCase()
+    return c1 == 't' || c1 == 'y'
+}
+
+fun Properties.setBoolean(key: String, value: Boolean): Unit {
+    setProperty(key, value.toString())
+}
+
+fun JComponent.noTaller() {
+    maximumSize = Dimension(maximumSize.width, preferredSize.height)
+}