Mercurial > cgi-bin > hgweb.cgi > JpegWasher
diff 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 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/name/blackcap/exifwasher/Misc.kt Wed Apr 08 20:29:12 2020 -0700 @@ -0,0 +1,163 @@ +/* + * Miscellaneous utility stuff. + */ +package name.blackcap.exifwasher + +import java.awt.Component +import java.awt.Cursor +import java.awt.Toolkit +import javax.swing.* +import kotlin.annotation.* +import kotlin.properties.ReadWriteProperty +import kotlin.reflect.KProperty + +/** + * Delegate that makes a var that can only be set once. This is commonly + * needed in Swing, because some vars inevitably need to be declared at + * outer levels but initialized in the Swing event dispatch thread. + */ +class SetOnce<T: Any>: ReadWriteProperty<Any?,T> { + private var value: T? = null + + override operator fun getValue(thisRef: Any?, property: KProperty<*>): T { + if (value == null) { + throw RuntimeException("${property.name} has not been initialized") + } else { + return value!! + } + } + + @Synchronized + override operator fun setValue(thisRef: Any?, property: KProperty<*>, newValue: T): Unit { + if (value != null) { + throw RuntimeException("${property.name} has already been initialized") + } + value = newValue + } +} + +/** + * Run something in the Swing thread, asynchronously. + * @param block lambda containing code to run + */ +fun inSwingThread(block: () -> Unit) { + SwingUtilities.invokeLater(Runnable(block)) +} + +/** + * Run something in the Swing thread, synchronously. + * @param block lambda containing code to run + */ +fun inSynSwingThread(block: () -> Unit) { + SwingUtilities.invokeAndWait(Runnable(block)) +} + +/** + * Make a shortcut for a menu item, using the standard combining key + * (control, command, etc.) for the system we're on. + * @param key KeyEvent constant describing the key + */ +fun JMenuItem.makeShortcut(key: Int): Unit { + val SC_KEY_MASK = Toolkit.getDefaultToolkit().menuShortcutKeyMask + setAccelerator(KeyStroke.getKeyStroke(key, SC_KEY_MASK)) +} + +/** + * Given a MenuElement object, get the item whose text matches the + * specified text. + * @param text to match + * @return first matched element, null if no match found + */ +fun MenuElement.getItem(name: String) : JMenuItem? { + subElements.forEach { + val jMenuItem = it.component as? JMenuItem + if (jMenuItem?.text == name) { + return jMenuItem + } + } + return null +} + +/** + * Change to the standard wait cursor. + */ +fun Component.useWaitCursor() { + this.cursor = Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)) +} + +/** + * Return back to the normal cursor(). + */ +fun Component.useNormalCursor() { + this.cursor = Cursor.defaultCursor +} + +/** + * Thrown if the programmer botches something in our DSL. + */ +class SwingWorkerException(message: String): Exception(message) { } + +/** + * A simplified SwingWorker DSL. It does not support intermediate + * results. Just lets one define a background task and something + * to execute when complete. + * + * @param T Type returned by inBackground (Java doInBackground) task. + */ +class SwingWorkerBuilder<T>: SwingWorker<T,Unit>() { + private var inBackgroundLambda: (SwingWorkerBuilder.() -> T)? = null + private var whenDoneLambda: (SwingWorkerBuilder.() -> Unit)? = null + + private fun setOnce<U>(prop: KMutableProperty<(SwingWorkerBuilder.() -> U)?>, value: SwingWorkerBuilder.() -> U) { + if (prop.get() != null) { + throw SwingWorkerException(prop.name.removeSuffix("Lambda") + " already defined!") + } + prop.set(value) + } + + /** + * Define the inBackground task. + */ + fun inBackground(lambda: SwingWorkerBuilder.() -> T): Unit { + setOnce<T>(::inBackgroundLambda, lambda) + } + + /** + * Define the whenDone task. + */ + fun whenDone(lambda: SwingWorkerBuilder.() -> Unit): Unit { + setOnce<Unit>(::whenDoneLambda, lambda) + } + + /* standard overrides for SwingWorker follow */ + + override fun doInBackground(): T = inBackgroundLambda?.invoke(this) + + override fun done(): Unit = whenDoneLambda?.invoke(this) + + override fun execute(): Unit { + if (inBackgroundLambda == null) { + throw SwingWorkerException("inBackground not defined!") + } else { + super.execute() + } + } +} + +/** + * Provides for an outer swingWorker block to contain the DSL. + */ +fun swingWorker<T>(initializer: SwingWorkerBuilder.() -> Unit): Unit { + SwingWorkerBuilder<T>().run { + initializer() + execute() + } +} + +/** + * Close a dialog (don't just hide it). + */ +fun JDialog.close() { + setVisible(false) + dispose() +}