Mercurial > cgi-bin > hgweb.cgi > JpegWasher
view src/name/blackcap/exifwasher/WashDialog.kt @ 3:19c381c536ec
Code to make it a proper Mac GUI app. Untested!
author | David Barts <n5jrn@me.com> |
---|---|
date | Wed, 08 Apr 2020 20:29:12 -0700 |
parents | |
children | dc1f4359659d |
line wrap: on
line source
/* * The dialog that controls washing a single file. */ package name.blackcap.exifwasher import java.awt.event.ActionEvent import java.awt.event.ActionListener import java.io.File import java.io.FileInputStream import java.io.FileOutputStream import java.io.IOException import java.util.logging.Level import java.util.logging.Logger import javax.swing.* import javax.swing.table.DefaultTableModel import javax.swing.table.TableColumn import javax.swing.table.TableColumnModel import name.blackcap.exifwasher.exiv2.* class WashDialog : JDialog(Application.mainFrame) { private val BW = 9 private val BW2 = BW * 2 private val WIDTH = 640 private val HEIGHT = 480 private val myTable = JTable().apply { autoCreateRowSorter = false rowSorter = null columnModel.run { getColumn(0).preferredWidth = 10 /* checkbox */ getColumn(1).preferredWidth = 25 /* key name */ getColumn(2).preferredWidth = 15 /* type */ getColumn(3).preferredWidth = 100 /* value */ } } private val selectAll = JCheckBox("Select all for deletion", false) private val resetButton = JButton("Reset").also { it.addActionListener(ActionListener { doReset() }) it.border = BorderFactory.createEmptyBorder(0, BW, 0, BW) } private val cancelButton = JButton("Cancel").also { it.addActionListener(ActionListener { close() }) it.border = BorderFactory.createEmptyBorder(0, BW, 0, BW) } /* deliberately not the default action, because it changes a file */ private val washButton = JButton("Wash").also { it.addActionListener(ActionListener { doWash() }) it.border = BorderFactory.createEmptyBorder(0, BW, 0, BW) } private lateinit var washing: File /* initiates the washing of the Exif data */ fun wash(dirty: File) { title = "Washing: ${image.name}" selectAll.setSelected(false) washing = dirty useWaitCursor() swingWorker<Array<Array<Any>>?> { inBackground { try { val image = Image(dirty.canonicalPath) val meta = image.meta val keys = meta.keys keys.sort() Array<Array<String>>(keys.size) { val key = keys[it] val value = meta[key] arrayOf(!settingsDialog.whitelist.contains(key), key, value.type, value.value) } } catch (e: Exiv2Exception) { LOGGER.log(Level.SEVERE, "unable to read metadata", e) null } } whenDone { useNormalCursor() val tableData = get() if (tableData == null) { JOptionPane.showMessageDialog(Application.mainFrame, "Unable to read metadata.", "Error", JOptionPane.ERROR_MESSAGE) } else { val colNames = arrayOf("Delete?", "Key", "Type", "Value") myTable.apply { model = MyTableModel(tableData, colNames) validate() } setVisible(true) } } } } private class MyTableModel : DefaultTableModel { override fun isCellEditable(row: Int, col: Int) = col == 0 override fun getColumnClass(col: Int) = if (col == 0) { Boolean } else { String } } private fun doReset() { myTable.model.run { for (i in 0 .. rowCount - 1) { val key = getValueAt(i, 1) as String setValueAt(!settingsDialog.whitelist.contains(key), i, 0) } } myTable.validate() } private fun doWash() { setVisible(false) /* get path to the directory we create */ val outDir = if (settingsDialog.outputToInputDir) { washing.canonicalFile.parent } else { settingsDialog.outputTo } /* get new file name */ val (name, ext) = splitext(washing.name) var newFile = File(outDir, "${name}_washed${ext}") /* copy the file, then edit the Exif in the copy */ useWaitCursor() swingWorker<Boolean> { inBackground { try { FileInputStream(washing).use { source -> FileOutputStream(newFile).use { target -> source.copyTo(target) } } val image = Image(newFile.canonicalPath) val meta = image.meta meta.keys.forEach { if (!settingsDialog.whitelist.contains(it)) { meta.erase(it) } } image.store() true } catch (e: IOException) { LOGGER.log(Level.SEVERE, "unable to copy input", e) false } catch (e: Exiv2Exception) { LOGGER.log(Level.SEVERE, "unable to edit metadata", e) false } } whenDone { useNormalCursor() close() /* if all went well, show the Exif in the new file */ if (get()) { ShowDialog().show(newFile) } else { try { if (newFile.exists()) { newFile.delete() } } catch (e: IOException) { LOGGER.log(Level.SEVERE, "unable to delete", e) } JOptionPane.showMessageDialog(Application.mainFrame, "Error\nUnable to wash: ${washing.canonicalPath}\nTo: ${newFile.canonicalPath}", "Error", JOptionPane.ERROR_MESSAGE) } } } } 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)) } init { defaultCloseOperation = JDialog.DISPOSE_ON_CLOSE /* delete if reusing */ title = "Untitled" contentPane.apply { layout = BoxLayout(this, BoxLayout.Y_AXIS) add(Box(BoxLayout.Y_AXIS).apply { alignmentX = Box.CENTER_ALIGNMENT border = BorderFactory.createEmptyBorder(BW, BW, BW, BW) add(JScrollPane(myTable).apply { alignmentX = JScrollPane.LEFT_ALIGNMENT border = BorderFactory.createEmptyBorder(BW, BW, BW, BW) verticalScrollBarPolicy = ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS horizontalScrollBarPolicy = ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER preferredSize = Dimension(WIDTH, HEIGHT) background = Application.mainFrame.background }) add(selectAll.apply { alignmentX = JCheckBox.LEFT_ALIGNMENT border = BorderFactory.createEmptyBorder(BW, BW, 0, BW) }) }) add(Box(BoxLayout.X_AXIS).apply { alignmentX = Box.CENTER_ALIGNMENT border = BorderFactory.createEmptyBorder(BW, BW, BW2, BW) add(resetButton) add(Box.createHorizontalGlue()) add(cancelButton) add(washButton) }) } pack() } }