Mercurial > cgi-bin > hgweb.cgi > ImagePrep
annotate src/name/blackcap/imageprep/Menus.kt @ 14:bed255e4c2dc
Fix file leak on save.
author | David Barts <n5jrn@me.com> |
---|---|
date | Sun, 19 Jul 2020 13:32:02 -0700 |
parents | 9e9fe34052a6 |
children | fad32eda667f |
rev | line source |
---|---|
0 | 1 /* |
2 * Menus. | |
3 */ | |
4 package name.blackcap.imageprep | |
5 | |
6 import java.awt.Graphics2D | |
7 import java.awt.RenderingHints | |
1 | 8 import java.awt.Toolkit |
0 | 9 import java.awt.Window |
10 import java.awt.event.ActionEvent | |
11 import java.awt.event.ActionListener | |
12 import java.awt.event.KeyEvent | |
13 import java.awt.image.BufferedImage | |
14 import java.io.File | |
1 | 15 import java.io.IOException |
0 | 16 import java.util.logging.Level |
17 import java.util.logging.Logger | |
18 import javax.imageio.IIOImage | |
19 import javax.imageio.ImageIO | |
20 import javax.imageio.ImageWriteParam | |
21 import javax.imageio.ImageWriter | |
22 import javax.imageio.stream.ImageOutputStream | |
23 import javax.swing.* | |
1 | 24 import javax.swing.filechooser.FileNameExtensionFilter |
0 | 25 |
26 /** | |
27 * Our menu bar. What we display depends somewhat on the system type, as | |
28 * the Mac gives us a gratuitous menu bar entry for handling some stuff. | |
29 */ | |
30 class MyMenuBar: JMenuBar() { | |
5
884f1415a330
Rationalized directory management.
David Barts <n5jrn@me.com>
parents:
3
diff
changeset
|
31 var currentInputDirectory = File(System.getProperty("user.dir")) |
884f1415a330
Rationalized directory management.
David Barts <n5jrn@me.com>
parents:
3
diff
changeset
|
32 var currentOutputDirectory = File(Settings.outputTo) |
884f1415a330
Rationalized directory management.
David Barts <n5jrn@me.com>
parents:
3
diff
changeset
|
33 |
0 | 34 init { |
35 add(JMenu("File").apply { | |
36 add(JMenuItem("Open & Scale…").apply { | |
37 addActionListener(ActionListener { doOpen() }) | |
38 makeShortcut(KeyEvent.VK_O) | |
39 }) | |
40 add(JMenuItem("Discard").apply { | |
41 addActionListener(ActionListener { doDiscard() }) | |
42 }) | |
43 add(JMenuItem("Save & Close…").apply { | |
44 addActionListener(ActionListener { doClose() }) | |
45 makeShortcut(KeyEvent.VK_S) | |
46 }) | |
47 if (OS.type != OS.MAC) { | |
48 add(JMenuItem("Quit").apply { | |
49 addActionListener(ActionListener { | |
50 LOGGER.log(Level.INFO, "execution complete") | |
51 System.exit(0) | |
52 }) | |
53 makeShortcut(KeyEvent.VK_Q) | |
54 }) | |
55 } | |
56 }) | |
57 add(JMenu("Help").apply { | |
58 if (OS.type != OS.MAC) { | |
59 add(JMenuItem("About ${Application.MYNAME}…").apply { | |
60 addActionListener(ActionListener { showAboutDialog() }) | |
61 }) | |
62 } | |
63 add(JMenuItem("${Application.MYNAME} Help…").apply { | |
64 addActionListener(ActionListener { Application.helpDialog.setVisible(true) }) | |
65 }) | |
66 }) | |
67 } | |
68 | |
69 fun getMenu(name: String): JMenu? { | |
70 subElements.forEach { | |
71 val jmenu = it.component as? JMenu | |
72 if (jmenu?.text == name) { | |
73 return jmenu | |
74 } | |
75 } | |
76 return null | |
77 } | |
78 | |
79 private fun doOpen() { | |
80 val chooser = JFileChooser().apply { | |
5
884f1415a330
Rationalized directory management.
David Barts <n5jrn@me.com>
parents:
3
diff
changeset
|
81 currentDirectory = currentInputDirectory |
3 | 82 fileFilter = FileNameExtensionFilter("Image Files", *ImageIO.getReaderFileSuffixes()) |
0 | 83 } |
84 if (chooser.showOpenDialog(Application.mainFrame) == JFileChooser.APPROVE_OPTION) { | |
5
884f1415a330
Rationalized directory management.
David Barts <n5jrn@me.com>
parents:
3
diff
changeset
|
85 currentInputDirectory = chooser.selectedFile.canonicalFile.parentFile |
0 | 86 RotateDialog.makeDialog(chooser.selectedFile) |
87 } | |
88 } | |
89 | |
90 private fun doDiscard() { | |
1 | 91 val w = FocusManager.getCurrentManager().activeWindow as? RotateDialog |
0 | 92 if (w == null) { |
1 | 93 Toolkit.getDefaultToolkit().beep() |
0 | 94 return |
95 } | |
9 | 96 w.close() |
0 | 97 } |
98 | |
7 | 99 /* xxx - ImageIO bug? */ |
100 private class NullOutputStream : java.io.OutputStream() { | |
101 override fun write(b: Int): Unit {} | |
102 } | |
103 | |
0 | 104 private fun doClose() { |
1 | 105 val w = FocusManager.getCurrentManager().activeWindow as? RotateDialog |
0 | 106 if (w == null) { |
7 | 107 LOGGER.log(Level.INFO, "beep!") |
1 | 108 Toolkit.getDefaultToolkit().beep() |
0 | 109 return |
110 } | |
111 val outName = splitext(w.file.name).first + Settings.outputSuffix + ".jpg" | |
1 | 112 val chooser = JFileChooser().apply { |
5
884f1415a330
Rationalized directory management.
David Barts <n5jrn@me.com>
parents:
3
diff
changeset
|
113 currentDirectory = currentOutputDirectory |
884f1415a330
Rationalized directory management.
David Barts <n5jrn@me.com>
parents:
3
diff
changeset
|
114 selectedFile = File(currentOutputDirectory, outName) |
0 | 115 } |
3 | 116 if (chooser.showSaveDialog(Application.mainFrame) != JFileChooser.APPROVE_OPTION) { |
0 | 117 return |
118 } | |
5
884f1415a330
Rationalized directory management.
David Barts <n5jrn@me.com>
parents:
3
diff
changeset
|
119 currentOutputDirectory = chooser.selectedFile.canonicalFile.parentFile |
0 | 120 val (name, ext) = splitext(chooser.selectedFile.name) |
121 val file = if (ext.toLowerCase() in setOf(".jpg", ".jpeg")) { | |
122 chooser.selectedFile | |
123 } else { | |
5
884f1415a330
Rationalized directory management.
David Barts <n5jrn@me.com>
parents:
3
diff
changeset
|
124 File(currentOutputDirectory, name + ".jpg") |
0 | 125 } |
126 if (file.exists() && JOptionPane.showConfirmDialog(w, "File ${file.name} already exists. Overwrite?", "Confirm Overwrite", JOptionPane.YES_NO_OPTION) != JOptionPane.YES_OPTION) { | |
127 return | |
128 } | |
129 w.useWaitCursor() | |
130 swingWorker<IOException?> { | |
131 inBackground { | |
132 try { | |
7 | 133 /* xxx - ImageIO bug? */ |
134 val devNull = java.io.PrintStream(NullOutputStream()) | |
135 val oldOut = System.out | |
136 System.setOut(devNull) | |
137 val oldErr = System.err | |
138 System.setErr(devNull) | |
139 val ios = try { | |
140 ImageIO.createImageOutputStream(file) | |
141 } finally { | |
142 System.setErr(oldErr) | |
143 System.setOut(oldOut) | |
144 } | |
145 if (ios == null) | |
146 throw IOException() | |
147 /* xxx - end workaround */ | |
14 | 148 ios.use { |
149 val writer = ImageIO.getImageWritersByFormatName("jpeg").next() | |
150 val iwp = writer.getDefaultWriteParam().apply { | |
151 setCompressionMode(ImageWriteParam.MODE_EXPLICIT) | |
152 setCompressionQuality(Settings.outputQuality.toFloat() / 100.0f) | |
153 } | |
154 writer.setOutput(it) | |
155 writer.write(null, IIOImage(w.image, null, null), iwp) | |
0 | 156 } |
157 null | |
158 } catch (e: IOException) { | |
159 e | |
160 } | |
161 } | |
162 whenDone { | |
163 w.useNormalCursor() | |
164 val error = get() | |
165 if (error == null) { | |
9 | 166 w.close() |
0 | 167 } else { |
1 | 168 ioExceptionDialog(w, file, "write", error) |
0 | 169 } |
170 } | |
171 } | |
172 } | |
173 | |
174 private fun splitext(s: String): Pair<String, String> { | |
175 val pos = s.lastIndexOf('.') | |
176 if (pos == -1) { | |
177 return Pair(s, "") | |
178 } | |
179 return Pair(s.substring(0, pos), s.substring(pos)) | |
180 } | |
181 } | |
182 | |
183 /** | |
184 * Show an About dialog. | |
185 */ | |
186 fun showAboutDialog() { | |
187 JOptionPane.showMessageDialog(Application.mainFrame, | |
188 "\nImagePrep\n" | |
189 + "\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0© MMXX, David W. Barts\n", | |
190 "About ImagePrep", | |
191 JOptionPane.PLAIN_MESSAGE) | |
192 } |