Mercurial > cgi-bin > hgweb.cgi > JpegWasher
comparison src/name/blackcap/exifwasher/Misc.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 |
comparison
equal
deleted
inserted
replaced
2:efd9fe2d70d7 | 3:19c381c536ec |
---|---|
1 /* | |
2 * Miscellaneous utility stuff. | |
3 */ | |
4 package name.blackcap.exifwasher | |
5 | |
6 import java.awt.Component | |
7 import java.awt.Cursor | |
8 import java.awt.Toolkit | |
9 import javax.swing.* | |
10 import kotlin.annotation.* | |
11 import kotlin.properties.ReadWriteProperty | |
12 import kotlin.reflect.KProperty | |
13 | |
14 /** | |
15 * Delegate that makes a var that can only be set once. This is commonly | |
16 * needed in Swing, because some vars inevitably need to be declared at | |
17 * outer levels but initialized in the Swing event dispatch thread. | |
18 */ | |
19 class SetOnce<T: Any>: ReadWriteProperty<Any?,T> { | |
20 private var value: T? = null | |
21 | |
22 override operator fun getValue(thisRef: Any?, property: KProperty<*>): T { | |
23 if (value == null) { | |
24 throw RuntimeException("${property.name} has not been initialized") | |
25 } else { | |
26 return value!! | |
27 } | |
28 } | |
29 | |
30 @Synchronized | |
31 override operator fun setValue(thisRef: Any?, property: KProperty<*>, newValue: T): Unit { | |
32 if (value != null) { | |
33 throw RuntimeException("${property.name} has already been initialized") | |
34 } | |
35 value = newValue | |
36 } | |
37 } | |
38 | |
39 /** | |
40 * Run something in the Swing thread, asynchronously. | |
41 * @param block lambda containing code to run | |
42 */ | |
43 fun inSwingThread(block: () -> Unit) { | |
44 SwingUtilities.invokeLater(Runnable(block)) | |
45 } | |
46 | |
47 /** | |
48 * Run something in the Swing thread, synchronously. | |
49 * @param block lambda containing code to run | |
50 */ | |
51 fun inSynSwingThread(block: () -> Unit) { | |
52 SwingUtilities.invokeAndWait(Runnable(block)) | |
53 } | |
54 | |
55 /** | |
56 * Make a shortcut for a menu item, using the standard combining key | |
57 * (control, command, etc.) for the system we're on. | |
58 * @param key KeyEvent constant describing the key | |
59 */ | |
60 fun JMenuItem.makeShortcut(key: Int): Unit { | |
61 val SC_KEY_MASK = Toolkit.getDefaultToolkit().menuShortcutKeyMask | |
62 setAccelerator(KeyStroke.getKeyStroke(key, SC_KEY_MASK)) | |
63 } | |
64 | |
65 /** | |
66 * Given a MenuElement object, get the item whose text matches the | |
67 * specified text. | |
68 * @param text to match | |
69 * @return first matched element, null if no match found | |
70 */ | |
71 fun MenuElement.getItem(name: String) : JMenuItem? { | |
72 subElements.forEach { | |
73 val jMenuItem = it.component as? JMenuItem | |
74 if (jMenuItem?.text == name) { | |
75 return jMenuItem | |
76 } | |
77 } | |
78 return null | |
79 } | |
80 | |
81 /** | |
82 * Change to the standard wait cursor. | |
83 */ | |
84 fun Component.useWaitCursor() { | |
85 this.cursor = Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)) | |
86 } | |
87 | |
88 /** | |
89 * Return back to the normal cursor(). | |
90 */ | |
91 fun Component.useNormalCursor() { | |
92 this.cursor = Cursor.defaultCursor | |
93 } | |
94 | |
95 /** | |
96 * Thrown if the programmer botches something in our DSL. | |
97 */ | |
98 class SwingWorkerException(message: String): Exception(message) { } | |
99 | |
100 /** | |
101 * A simplified SwingWorker DSL. It does not support intermediate | |
102 * results. Just lets one define a background task and something | |
103 * to execute when complete. | |
104 * | |
105 * @param T Type returned by inBackground (Java doInBackground) task. | |
106 */ | |
107 class SwingWorkerBuilder<T>: SwingWorker<T,Unit>() { | |
108 private var inBackgroundLambda: (SwingWorkerBuilder.() -> T)? = null | |
109 private var whenDoneLambda: (SwingWorkerBuilder.() -> Unit)? = null | |
110 | |
111 private fun setOnce<U>(prop: KMutableProperty<(SwingWorkerBuilder.() -> U)?>, value: SwingWorkerBuilder.() -> U) { | |
112 if (prop.get() != null) { | |
113 throw SwingWorkerException(prop.name.removeSuffix("Lambda") + " already defined!") | |
114 } | |
115 prop.set(value) | |
116 } | |
117 | |
118 /** | |
119 * Define the inBackground task. | |
120 */ | |
121 fun inBackground(lambda: SwingWorkerBuilder.() -> T): Unit { | |
122 setOnce<T>(::inBackgroundLambda, lambda) | |
123 } | |
124 | |
125 /** | |
126 * Define the whenDone task. | |
127 */ | |
128 fun whenDone(lambda: SwingWorkerBuilder.() -> Unit): Unit { | |
129 setOnce<Unit>(::whenDoneLambda, lambda) | |
130 } | |
131 | |
132 /* standard overrides for SwingWorker follow */ | |
133 | |
134 override fun doInBackground(): T = inBackgroundLambda?.invoke(this) | |
135 | |
136 override fun done(): Unit = whenDoneLambda?.invoke(this) | |
137 | |
138 override fun execute(): Unit { | |
139 if (inBackgroundLambda == null) { | |
140 throw SwingWorkerException("inBackground not defined!") | |
141 } else { | |
142 super.execute() | |
143 } | |
144 } | |
145 } | |
146 | |
147 /** | |
148 * Provides for an outer swingWorker block to contain the DSL. | |
149 */ | |
150 fun swingWorker<T>(initializer: SwingWorkerBuilder.() -> Unit): Unit { | |
151 SwingWorkerBuilder<T>().run { | |
152 initializer() | |
153 execute() | |
154 } | |
155 } | |
156 | |
157 /** | |
158 * Close a dialog (don't just hide it). | |
159 */ | |
160 fun JDialog.close() { | |
161 setVisible(false) | |
162 dispose() | |
163 } |