comparison src/name/blackcap/exifwasher/SettingsDialog.kt @ 5:dc1f4359659d

Got it compiling.
author David Barts <n5jrn@me.com>
date Thu, 09 Apr 2020 18:20:34 -0700
parents 19c381c536ec
children aafc9c127c7b
comparison
equal deleted inserted replaced
4:ba5dc14652da 5:dc1f4359659d
4 package name.blackcap.exifwasher 4 package name.blackcap.exifwasher
5 5
6 import java.awt.Toolkit 6 import java.awt.Toolkit
7 import java.awt.event.ActionEvent 7 import java.awt.event.ActionEvent
8 import java.awt.event.ActionListener 8 import java.awt.event.ActionListener
9 import java.io.BufferedWriter
9 import java.io.File 10 import java.io.File
11 import java.io.FileOutputStream
10 import java.io.IOException 12 import java.io.IOException
13 import java.io.OutputStreamWriter
14 import java.util.Properties
11 import java.util.logging.Level 15 import java.util.logging.Level
12 import java.util.logging.Logger 16 import java.util.logging.Logger
13 import javax.swing.* 17 import javax.swing.*
14 import javax.swing.event.ListDataEvent 18 import javax.swing.event.ListDataEvent
15 import javax.swing.event.ListDataListener 19 import javax.swing.event.ListDataListener
20 import javax.swing.event.ListSelectionListener
16 import kotlin.text.toBoolean 21 import kotlin.text.toBoolean
17 22
18 import name.blackcap.exifwasher.exiv2.* 23 import name.blackcap.exifwasher.exiv2.*
19 24
25 /* work around name shadowing */
26 private val _PROPS = PROPERTIES
27
20 class SettingsDialog : JDialog(Application.mainFrame) { 28 class SettingsDialog : JDialog(Application.mainFrame) {
21 private val BW = 9 29 protected val BW = 9
22 private val BW2 = BW * 2 30 protected val BW2 = BW * 2
23 31
24 /* where to send output, if not outputToInputDir */ 32 /* where to send output, if not outputToInputDir */
25 private var _outputTo = PROPERTIES.getProperty("outputTo") 33 protected var _outputTo = _PROPS.getProperty("outputTo")
26 private var _dOutputTo = DPROPERTIES.getProperty("outputTo") 34 protected var _dOutputTo = DPROPERTIES.getProperty("outputTo")
27 val outputTo: String 35 val outputTo: String
28 get() = _outputTo 36 get() = _outputTo
29 37
30 /* make output where input was found */ 38 /* make output where input was found */
31 private var _outputToInputDir = (PROPERTIES.getProperty("outputToInputDir") ?: "false").toBoolean() 39 protected var _outputToInputDir = _PROPS.getProperty("outputToInputDir", "false").toBoolean()
32 private var _dOutputToInputDir = (DPROPERTIES.getProperty("outputToInputDir") ?: "false").toBoolean() 40 protected var _dOutputToInputDir = DPROPERTIES.getProperty("outputToInputDir", "false").toBoolean()
33 val outputToInputDir: Boolean 41 val outputToInputDir: Boolean
34 get() = _outputToInputDir 42 get() = _outputToInputDir
35 43
36 /* the whitelist of allowed Exif tags */ 44 /* the whitelist of allowed Exif tags */
37 private var _whitelist = Whitelist.parse(PROPERTIES.getProperty("whitelist") :? "") 45 protected var _whitelist = Whitelist.parse(_PROPS.getProperty("whitelist", ""))
38 val whitelist: Whitelist 46 val whitelist: Whitelist
39 get() = _whitelist 47 get() = _whitelist
40 48
41 /* the default whitelist, for factory resets */ 49 /* the default whitelist, for factory resets */
42 private val _oWhitelist = _whitelist.clone() 50 protected val _oWhitelist = _whitelist.clone()
43 private val _dWhitelist = Whitelist.parse(DPROPERTIES.getProperty("whitelist") :? "") 51 protected val _dWhitelist = Whitelist.parse(DPROPERTIES.getProperty("whitelist", ""))
44 52
45 /* radio buttons to choose output directory policy */ 53 /* radio buttons to choose output directory policy */
46 private val outputToButton = JRadioButton("Output to:", !outputToInputDir).apply { 54 protected val outputToButton = JRadioButton("Output to:", !outputToInputDir).apply {
47 addActionListener(ActionListener { setOutputOpts(isSelected()) }) 55 addActionListener(ActionListener { setOutputOpts(isSelected()) })
48 } 56 }
49 private val outputToInputButton = JRadioButton( 57 protected val outputToInputButton = JRadioButton(
50 "Output to directory containing input file.", outputToInputDir).apply { 58 "Output to directory containing input file.", outputToInputDir).apply {
51 addActionListener(ActionListener { setOutputOpts(!isSelected()) }) 59 addActionListener(ActionListener { setOutputOpts(!isSelected()) })
52 } 60 }
53 private val buttonGroup = ButtonGroup().apply { 61 protected val buttonGroup = ButtonGroup().apply {
54 add(outputToButton) 62 add(outputToButton)
55 add(outputToInputButton) 63 add(outputToInputButton)
56 } 64 }
57 private fun setOutputOpts(toSpecific: Boolean) { 65 protected fun setOutputOpts(toSpecific: Boolean) {
58 _outputToInputDir = !toSpecific 66 _outputToInputDir = !toSpecific
59 changeOutputTo.setEnabled(toSpecific) 67 changeOutputTo.setEnabled(toSpecific)
60 } 68 }
61 69
62 /* displays the OutputTo directory */ 70 /* displays the OutputTo directory */
63 private val outputToText = JTextField(outputTo, 50).apply { 71 protected val outputToText = JTextField(outputTo, 50).apply {
64 setEditable(false) 72 setEditable(false)
65 } 73 }
66 74
67 /* pops up to change the above directory */ 75 /* pops up to change the above directory */
68 private val outputToChooser = JFileChooser(outputToText.text).apply { 76 protected val outputToChooser = JFileChooser(outputToText.text).apply {
69 fileSelectionMode = DIRECTORIES_ONLY 77 fileSelectionMode = JFileChooser.DIRECTORIES_ONLY
70 } 78 }
71 79
72 /* requests the OutputTo directory be changed */ 80 /* requests the OutputTo directory be changed */
73 private val changeOutputTo = JButton("Change").also { 81 protected val changeOutputTo = JButton("Change").also {
74 it.addActionListener(ActionListener { 82 it.addActionListener(ActionListener {
75 val status = outputToChooser.showOpenDialog(this) 83 val status = outputToChooser.showOpenDialog(this)
76 if (status == JFileChooser.APPROVE_OPTION) { 84 if (status == JFileChooser.APPROVE_OPTION) {
77 _outputTo = outputToChooser.selectedFile.canonicalPath 85 _outputTo = outputToChooser.selectedFile.canonicalPath
78 outputToText.text = _outputTo 86 outputToText.text = _outputTo
80 }) 88 })
81 it.setEnabled(!outputToInputDir) 89 it.setEnabled(!outputToInputDir)
82 } 90 }
83 91
84 /* bottom buttons to restore defaults, cancel, save */ 92 /* bottom buttons to restore defaults, cancel, save */
85 private val restoreButton = JButton("Restore All Defaults").also { 93 protected val restoreButton = JButton("Restore All Defaults").apply {
86 addActionListener(ActionListener { 94 addActionListener(ActionListener {
87 restore(_dOutputToInputDir, _dOutputTo, _dWhitelist) 95 restore(_dOutputToInputDir, _dOutputTo, _dWhitelist)
88 } 96 })
89 } 97 }
90 private val cancelButton = JButton("Cancel").also { 98 protected val cancelButton = JButton("Cancel").apply {
91 addActionListener(ActionListener { 99 addActionListener(ActionListener {
92 setVisible(false) 100 setVisible(false)
93 restore(outputToInputDir, outputTo, whitelist) 101 restore(outputToInputDir, outputTo, whitelist)
94 }) 102 })
95 } 103 }
96 private val saveButton = JButton("Save").also { 104 protected val saveButton = JButton("Save").apply {
97 addActionListener(ActionListener { 105 addActionListener(ActionListener {
98 setVisible(false) 106 setVisible(false)
99 writeProperties() 107 writeProperties()
100 }) 108 })
101 } 109 }
102 110
103 private fun writeProperties() { 111 protected fun writeProperties() {
104 PROPERTIES.run { 112 _PROPS.run {
105 setProperty("outputTo", outputTo) 113 setProperty("outputTo", outputTo)
106 setProperty("outputToInputDir", outputToInputDir.toString()) 114 setProperty("outputToInputDir", outputToInputDir.toString())
107 setProperty("whitelist", whitelist.toString()) 115 setProperty("whitelist", whitelist.toString())
108 } 116 }
109 try { 117 try {
110 BufferedWriter(OutputStreamWriter(FileOutputStream(PROP_FILE), CHARSET)).use { 118 BufferedWriter(OutputStreamWriter(FileOutputStream(PROP_FILE), CHARSET)).use {
111 PROPERTIES.store(it, null) 119 _PROPS.store(it, null)
112 } 120 }
113 } catch (e: IOException) { 121 } catch (e: IOException) {
114 LOGGER.log(Level.SEVERE, "unable to write settings", e) 122 LOGGER.log(Level.SEVERE, "unable to write settings", e)
115 JOptionPane.showMessageDialog(Application.mainFrame, 123 JOptionPane.showMessageDialog(Application.mainFrame,
116 "Unable to write settings.", 124 "Unable to write settings.",
117 "Error", 125 "Error",
118 JOptionPane.ERROR_MESSAGE) 126 JOptionPane.ERROR_MESSAGE)
119 } 127 }
120 } 128 }
121 129
122 private fun restore(outputToInput: Boolean, output: String, wl: Whitelist) { 130 protected fun restore(outputToInput: Boolean, output: String, wl: Whitelist) {
123 outputToButton.setSelected(!outputToInput) 131 outputToButton.setSelected(!outputToInput)
124 changeOutputTo.setEnabled(!outputToInput) 132 changeOutputTo.setEnabled(!outputToInput)
125 outputToText.text = output 133 outputToText.text = output
126 outputToInputButton.setSelected(outputToInput) 134 outputToInputButton.setSelected(outputToInput)
127 wlSelectorModel.reset(wl.toList()) 135 wlSelectorModel.reset(wl.toList())
128 } 136 }
129 137
130 /* so we can present a list of strings that is always sorted */ 138 /* so we can present a list of strings that is always sorted */
131 private class WhiteListModel(basedOn: Collection<String>): ListModel<String> { 139 protected class WhiteListModel(basedOn: Collection<String>): ListModel<String> {
132 private val storage = ArrayList<String>(basedOn).apply { sort() } 140 private val storage = ArrayList<String>(basedOn).apply { sort() }
133 private val listeners = mutableListOf<ListDataListener>() 141 private val listeners = mutableListOf<ListDataListener>()
134 142
135 /* so we can mutate the list */ 143 /* so we can mutate the list */
136 144
138 var index = storage.binarySearch(newItem) 146 var index = storage.binarySearch(newItem)
139 if (index < 0) { 147 if (index < 0) {
140 index = -(index + 1) 148 index = -(index + 1)
141 } 149 }
142 storage.add(index, newItem) 150 storage.add(index, newItem)
143 val event = ListDataEvent(this, ListDataEvent.INTERVAL_ADDED, index, index) 151 notifyAll(ListDataEvent.INTERVAL_ADDED, index, index)
144 listeners.forEach { it.intervalAdded(event) }
145 } 152 }
146 153
147 fun removeAt(index: Int): Unit { 154 fun removeAt(index: Int): Unit {
148 if (storage.removeAt(index)) { 155 storage.removeAt(index)
149 val event = ListDataEvent(this, ListDataEvent.INTERVAL_REMOVED, index, index) 156 notifyAll(ListDataEvent.INTERVAL_REMOVED, index, index)
150 listeners.forEach { it.intervalRemoved(event) }
151 }
152 } 157 }
153 158
154 fun remove(oldItem: String): Unit { 159 fun remove(oldItem: String): Unit {
155 val index = basedOn.binarySearch(oldItem) 160 val index: Int = storage.binarySearch(oldItem)
156 if (index < 0) { 161 if (index < 0) {
157 return 162 return
158 } 163 }
159 var start = index 164 storage.removeAt(index)
160 while (start > 0 && storage[start] == oldItem) { 165 notifyAll(ListDataEvent.INTERVAL_REMOVED, index, index)
161 start -= 1
162 }
163 var end = index
164 var max = storage.size - 1
165 while (end < max && storage[end] == oldItem) {
166 end += 1
167 }
168 storage.removeRange(start, end+1)
169 val event = ListDataEvent(this, ListDataEvent.INTERVAL_REMOVED, start, end)
170 listeners.forEach { it.intervalRemoved(event) }
171 } 166 }
172 167
173 fun reset(basedOn: Collection<String>): Unit { 168 fun reset(basedOn: Collection<String>): Unit {
174 val removeEvent = ListDataEvent(this, ListDataEvent.INTERVAL_REMOVED, 0, storage.size) 169 val oldSize = storage.size
175 storage.clear() 170 storage.clear()
171 notifyAll(ListDataEvent.INTERVAL_REMOVED, 0, oldSize)
176 storage.addAll(basedOn) 172 storage.addAll(basedOn)
177 storage.sort() 173 storage.sort()
178 val addEvent = ListDataEvent(this, ListDataEvent.INTERVAL_ADDED, 0, storage.size) 174 notifyAll(ListDataEvent.INTERVAL_ADDED, 0, storage.size)
179 listeners.forEach { 175 }
180 it.contentsChanged(removeEvent) 176
181 it.contentsChanged(addEvent) 177 /* misc. */
182 }
183 }
184 178
185 fun toList(): List<String> = storage 179 fun toList(): List<String> = storage
180
181 private fun notifyAll(eType: Int, index0: Int, index1: Int): Unit {
182 val event = ListDataEvent(this, eType, index0, index1)
183 when (eType) {
184 ListDataEvent.CONTENTS_CHANGED -> listeners.forEach { it.contentsChanged(event) }
185 ListDataEvent.INTERVAL_ADDED -> listeners.forEach { it.intervalAdded(event) }
186 ListDataEvent.INTERVAL_REMOVED -> listeners.forEach { it.intervalRemoved(event) }
187 else -> throw RuntimeException("unexpected event type!")
188 }
189 }
186 190
187 /* so we are a proper ListModel */ 191 /* so we are a proper ListModel */
188 192
189 override fun addListDataListener(l: ListDataListener): Unit { 193 override fun addListDataListener(l: ListDataListener): Unit {
190 listeners.add(l) 194 listeners.add(l)
197 override fun getElementAt(index: Int): String = storage[index] 201 override fun getElementAt(index: Int): String = storage[index]
198 202
199 override fun getSize(): Int = storage.size 203 override fun getSize(): Int = storage.size
200 } 204 }
201 205
202 private class WLAddDialog(parent: SettingsDialog): JDialog(parent) { 206 protected class WLAddDialog(parent: SettingsDialog): JDialog(parent) {
203 JTextField text = JTextField(40).apply { 207 private val BW = parent.BW
208 private val BW2 = parent.BW2
209
210 private val toAdd = JTextField(40).apply {
204 alignmentX = CENTER_ALIGNMENT 211 alignmentX = CENTER_ALIGNMENT
205 border = BorderFactory.createEmptyBorder(BW, BW, BW, BW) 212 border = BorderFactory.createEmptyBorder(BW, BW, BW, BW)
206 } 213 }
207 214
208 JButton cancelButton = JButton("Cancel").apply { 215 private val cancelButton = JButton("Cancel").apply {
209 addActionListener(ActionListener { 216 addActionListener(ActionListener {
210 text.text = "" 217 toAdd.text = ""
211 setVisible(false) 218 setVisible(false)
212 }) 219 })
213 } 220 }
214 221
215 JButton addButton = JButton("Add").apply { 222 private val addButton = JButton("Add").apply {
216 addActionListener(ActionListener { 223 addActionListener(ActionListener {
217 val newItem = text.text?.trim() 224 val newItem = toAdd.text?.trim()
218 if (newItem.isNullOrEmpty()) { 225 if (newItem.isNullOrEmpty()) {
219 Toolkit.getDefaultToolkit().beep() 226 Toolkit.getDefaultToolkit().beep()
220 } else { 227 } else {
221 wlSelectorModel.add(newItem) 228 parent.wlSelectorModel.add(newItem)
222 } 229 }
223 text.text = "" 230 toAdd.text = ""
224 setVisible(false) 231 setVisible(false)
225 }
226 }) 232 })
227 } 233 }
228 234
229 init { 235 init {
230 title = "Add Item to Whitelist" 236 title = "Add Item to Whitelist"
231 contentPane.apply { 237 contentPane.apply {
232 layout = BoxLayout(this, BoxLayout.Y_AXIS) 238 layout = BoxLayout(this, BoxLayout.Y_AXIS)
233 add(text) 239 add(toAdd)
234 add(Box(BoxLayout.X_AXIS).apply { 240 add(Box(BoxLayout.X_AXIS).apply {
235 alignmentX = CENTER_ALIGNMENT 241 alignmentX = CENTER_ALIGNMENT
236 border = BorderFactory.createEmptyBorder(BW, BW, BW, BW) 242 border = BorderFactory.createEmptyBorder(BW, BW, BW, BW)
237 add(Box.createHorizontalGlue()) 243 add(Box.createHorizontalGlue())
238 add(cancelButton) 244 add(cancelButton)
245 setResizable(false) 251 setResizable(false)
246 } 252 }
247 } 253 }
248 254
249 /* the JList that holds our whitelist */ 255 /* the JList that holds our whitelist */
250 private val wlSelectorModel = WhiteListModel(whitelist.toList()) 256 protected val wlSelectorModel = WhiteListModel(whitelist.toList())
251 private val wlSelector = JList().apply { 257 protected val wlSelector: JList<String> = JList<String>().apply {
252 visibleRowCount = -1 258 visibleRowCount = -1
253 model = wlSelectorModel 259 model = wlSelectorModel
254 clearSelection() 260 clearSelection()
255 addListSelectionListener(ListSelectionListener { 261 addListSelectionListener(ListSelectionListener {
256 wlDeleteButton.setEnabled(!isSelectionEmpty()) 262 wlDeleteButton.setEnabled(!isSelectionEmpty())
257 } 263 })
258 } 264 }
259 265
260 /* buttons for managing the whitelist */ 266 /* buttons for managing the whitelist */
261 private val wlAddButton = JButton("Add").apply { 267 protected val wlAddButton = JButton("Add").apply {
262 addActionListener(ActionListener { wlAddDialog.setVisible(true) }) 268 addActionListener(ActionListener { wlAddDialog.setVisible(true) })
263 } 269 }
264 private val wlDeleteButton = JButton("Delete").apply { 270 protected val wlDeleteButton = JButton("Delete").apply {
265 addActionListener(ActionListener { 271 addActionListener(ActionListener {
266 wlSelector.selectedIndices.forEach { wlSelectorModel.removeAt(it) } 272 wlSelector.selectedIndices.forEach { wlSelectorModel.removeAt(it) }
273 setEnabled(false)
267 }) 274 })
268 setEnabled(false) 275 setEnabled(false)
269 } 276 }
270 277
271 /* the dialog that the Add button pops up */ 278 /* the dialog that the Add button pops up */
272 private val wlAddDialog = WLAddDialog(this) 279 protected val wlAddDialog = WLAddDialog(this)
273 280
274 init { 281 init {
282 val home = System.getProperty("user.dir")
275 if (_outputTo.isNullOrEmpty()) { 283 if (_outputTo.isNullOrEmpty()) {
276 _outputTo = System.getProperty("user.dir") 284 _outputTo = home
285 }
286 if (_dOutputTo.isNullOrEmpty()) {
287 _dOutputTo = home
277 } 288 }
278 title = "Settings" 289 title = "Settings"
279 contentPane.apply { 290 contentPane.apply {
280 layout = BoxLayout(this, BoxLayout.Y_AXIS) 291 layout = BoxLayout(this, BoxLayout.Y_AXIS)
281 add(JTabbedPane().apply { 292 add(JTabbedPane().apply {