view src/name/blackcap/imageprep/Menus.kt @ 29:e90d290a9a8d

Remove dependence on Nashorn.
author David Barts <n5jrn@me.com>
date Mon, 13 Jun 2022 09:21:24 -0700
parents 92afaa27f40a
children 098c4f5507c7
line wrap: on
line source

/*
 * Menus.
 */
package name.blackcap.imageprep

import java.awt.Graphics2D
import java.awt.RenderingHints
import java.awt.Toolkit
import java.awt.Window
import java.awt.event.ActionEvent
import java.awt.event.ActionListener
import java.awt.event.KeyEvent
import java.awt.image.BufferedImage
import java.io.File
import java.io.IOException
import java.util.logging.Level
import java.util.logging.Logger
import javax.imageio.IIOImage
import javax.imageio.ImageIO
import javax.imageio.ImageWriteParam
import javax.imageio.ImageWriter
import javax.imageio.stream.ImageOutputStream
import javax.swing.*
import javax.swing.filechooser.FileNameExtensionFilter

/**
 * Our menu bar. What we display depends somewhat on the system type, as
 * the Mac gives us a gratuitous menu bar entry for handling some stuff.
 */
class MyMenuBar: JMenuBar() {
    private var currentInputDirectory = File(System.getProperty("user.dir"))
    private var currentOutputDirectory = File(Application.settingsDialog.outputTo)

    init {
        add(JMenu("File").apply {
            add(JMenuItem("Open & Scale…").apply {
                addActionListener(ActionListener { doOpen() })
                makeShortcut(KeyEvent.VK_O)
            })
            add(JMenuItem("Discard").apply {
                addActionListener(ActionListener { doDiscard() })
            })
            add(JMenuItem("Save & Close…").apply {
                addActionListener(ActionListener { doClose() })
                makeShortcut(KeyEvent.VK_S)
            })
            if (OS.type != OS.MAC) {
                add(JMenuItem("Preferences…").apply {
                    addActionListener(ActionListener {
                        Application.settingsDialog.setVisible(true)
                    })
                    makeShortcut(KeyEvent.VK_COMMA)
                })
                add(JMenuItem("Quit").apply {
                    addActionListener(ActionListener {
                        LOGGER.log(Level.INFO, "execution complete")
                        System.exit(0)
                    })
                    makeShortcut(KeyEvent.VK_Q)
                })
            }
        })
        add(JMenu("Help").apply {
            if (OS.type != OS.MAC) {
                add(JMenuItem("About ${Application.MYNAME}…").apply {
                    addActionListener(ActionListener { showAboutDialog() })
                })
            }
            add(JMenuItem("${Application.MYNAME} Help…").apply {
                addActionListener(ActionListener { Application.helpDialog.setVisible(true) })
            })
        })
    }

    fun getMenu(name: String): JMenu? {
        subElements.forEach {
            val jmenu = it.component as? JMenu
            if (jmenu?.text == name) {
                return jmenu
            }
        }
        return null
    }

    private fun doOpen() {
        val maxDim = MaxDimSpinner(Application.settingsDialog.maxDimension).apply {
            alignmentX = LEFT_ALIGNMENT
        }
        val acc = JPanel().apply {
            layout = BoxLayout(this, BoxLayout.Y_AXIS)
            add(Box.createGlue())
            add(JLabel("Max. dim.:").apply { alignmentX = LEFT_ALIGNMENT })
            add(maxDim)
        }
        val chooser = JFileChooser().apply {
            accessory = acc
            currentDirectory = currentInputDirectory
            fileFilter = FileNameExtensionFilter("Image Files", *ImageIO.getReaderFileSuffixes())
        }
        if (chooser.showOpenDialog(Application.mainFrame) == JFileChooser.APPROVE_OPTION) {
            currentInputDirectory = chooser.selectedFile.canonicalFile.parentFile
            RotateDialog.makeDialog(chooser.selectedFile, maxDim.value as Int)
        }
    }

    private fun doDiscard() {
        val w = FocusManager.getCurrentManager().activeWindow as? RotateDialog
        if (w == null) {
            Toolkit.getDefaultToolkit().beep()
            return
        }
        w.close()
    }

    /* xxx - ImageIO bug? */
    private class NullOutputStream : java.io.OutputStream() {
        override fun write(b: Int): Unit {}
    }

    private fun doClose() {
        val w = FocusManager.getCurrentManager().activeWindow as? RotateDialog
        if (w == null) {
            LOGGER.log(Level.INFO, "beep!")
            Toolkit.getDefaultToolkit().beep()
            return
        }
        val outQual = OutQualSpinner(Application.settingsDialog.outputQuality).apply {
            alignmentX = LEFT_ALIGNMENT
        }
        val acc = JPanel().apply {
            layout = BoxLayout(this, BoxLayout.Y_AXIS)
            add(Box.createGlue())
            add(JLabel("Quality:").apply { alignmentX = LEFT_ALIGNMENT })
            add(outQual)
        }
        val outName = splitext(w.file.name).first + Application.settingsDialog.outputSuffix + ".jpg"
        val chooser = JFileChooser().apply {
            accessory = acc
            currentDirectory = currentOutputDirectory
            selectedFile = File(currentOutputDirectory, outName)
        }
        if (chooser.showSaveDialog(Application.mainFrame) != JFileChooser.APPROVE_OPTION) {
            return
        }
        currentOutputDirectory = chooser.selectedFile.canonicalFile.parentFile
        val (name, ext) = splitext(chooser.selectedFile.name)
        val file = if (ext.toLowerCase() in setOf(".jpg", ".jpeg")) {
            chooser.selectedFile
        } else {
            File(currentOutputDirectory, name + ".jpg")
        }
        if (file.exists() && JOptionPane.showConfirmDialog(w, "File ${file.name} already exists. Overwrite?", "Confirm Overwrite", JOptionPane.YES_NO_OPTION) != JOptionPane.YES_OPTION) {
            return
        }
        w.useWaitCursor()
        swingWorker<IOException?> {
            inBackground {
                try {
                    /* xxx - ImageIO bug? */
                    val devNull = java.io.PrintStream(NullOutputStream())
                    val oldOut = System.out
                    System.setOut(devNull)
                    val oldErr = System.err
                    System.setErr(devNull)
                    val ios = try {
                        ImageIO.createImageOutputStream(file)
                    } finally {
                        System.setErr(oldErr)
                        System.setOut(oldOut)
                    }
                    if (ios == null)
                        throw IOException()
                    /* xxx - end workaround */
                    ios.use {
                        val writer = ImageIO.getImageWritersByFormatName("jpeg").next()
                        try {
                            val iwp = writer.getDefaultWriteParam().apply {
                                setCompressionMode(ImageWriteParam.MODE_EXPLICIT)
                                setCompressionQuality((outQual.value as Int).toFloat() / 100.0f)
                            }
                            writer.setOutput(it)
                            writer.write(null, IIOImage(w.image, null, null), iwp)
                        } finally {
                            writer.dispose()
                        }
                    }
                    null
                } catch (e: IOException) {
                    e
                }
            }
            whenDone {
                w.useNormalCursor()
                val error = get()
                if (error == null) {
                    w.close()
                } else {
                    ioExceptionDialog(w, file, "write", error)
                }
            }
        }
    }

    private fun splitext(s: String): Pair<String, String> {
        val pos = s.lastIndexOf('.')
        if (pos == -1) {
            return Pair(s, "")
        }
        return Pair(s.substring(0, pos), s.substring(pos))
    }
}

/**
 * Show an About dialog.
 */
fun showAboutDialog() {
    JOptionPane.showMessageDialog(Application.mainFrame,
        "\nImagePrep\n"
        + "\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0© MMXX, David W. Barts\n",
        "About ImagePrep",
        JOptionPane.PLAIN_MESSAGE)
}