Mercurial > cgi-bin > hgweb.cgi > JpegWasher
diff 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 diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/name/blackcap/exifwasher/WashDialog.kt Wed Apr 08 20:29:12 2020 -0700 @@ -0,0 +1,220 @@ +/* + * 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() + } +}