Mercurial > cgi-bin > hgweb.cgi > ClipMan
diff src/name/blackcap/clipman/Main.kt @ 0:be282c48010a
Incomplete; checking it in as a backup.
author | David Barts <n5jrn@me.com> |
---|---|
date | Tue, 14 Jan 2020 14:07:19 -0800 |
parents | |
children | 70caa73e32f7 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/name/blackcap/clipman/Main.kt Tue Jan 14 14:07:19 2020 -0800 @@ -0,0 +1,147 @@ +/* + * The entry point and most of the view logic is here. + */ +package name.blackcap.clipman + +import java.awt.BorderLayout +import java.awt.Color +import java.awt.Container +import java.awt.Dimension +import java.awt.Font +import java.awt.Toolkit; +import java.awt.datatransfer.* +import java.awt.event.WindowEvent +import java.awt.event.WindowListener +import java.util.Date +import java.util.logging.Level +import java.util.logging.Logger +import javax.swing.* +import javax.swing.border.CompoundBorder +import javax.swing.border.EmptyBorder +import javax.swing.border.LineBorder +import javax.swing.text.JTextComponent +import kotlin.concurrent.thread +import org.jsoup.Jsoup +import org.jsoup.nodes.* + + +/* kills the updating thread (and does a system exit) when needed */ +class KillIt(val thr: Thread) : WindowListener { + // events we don't care about + override fun windowActivated(e: WindowEvent) {} + override fun windowClosed(e: WindowEvent) {} + override fun windowDeactivated(e: WindowEvent) {} + override fun windowDeiconified(e: WindowEvent) {} + override fun windowIconified(e: WindowEvent) {} + override fun windowOpened(e: WindowEvent) {} + + // and the one we do + override fun windowClosing(e: WindowEvent) { + thr.run { interrupt(); join() } + System.exit(0) + } +} + +/* the updating thread */ +class UpdateIt(val queue: PasteboardQueue, val interval: Int): Thread() { + @Volatile var enabled = true + private val stdBorder = + CompoundBorder(EmptyBorder(5, 10, 5, 10), LineBorder(Color.GRAY, 1)) + + override fun run() { + var oldContents = "" + var newContents = "" + while (true) { + if (enabled) { + var contents = PasteboardItem.read() + if (contents == null) { + LOGGER.log(Level.WARNING, "unable to read clipboard") + continue + } + newContents = when (contents) { + is PasteboardItem.Plain -> contents.plain + is PasteboardItem.HTML -> contents.plain + } + if (oldContents != newContents) { + var widget: JComponent = when(contents) { + is PasteboardItem.Plain -> JTextPane().apply { + contentType = "text/plain" + toolTipText = "Plain text" + text = contents.plain + font = Font(Font.MONOSPACED, Font.PLAIN, 14) + border = stdBorder + autoSize(600) + setEditable(false) + } + is PasteboardItem.HTML -> JTextPane().apply { + contentType = "text/html" + toolTipText = "Styled text" + text = scrub(contents.html) + border = stdBorder + autoSize(600) + setEditable(false) + } + } + queue.add(QueueItem(widget, contents)) + oldContents = newContents + } + } + if (Thread.interrupted()) { + return + } + try { + Thread.sleep(interval - System.currentTimeMillis() % interval) + } catch (e: InterruptedException) { + return + } + } + } + + private fun scrub(html: String): String { + return Jsoup.parse(html).run { + select(":root>head>meta").remove() + outputSettings() + .charset(CHARSET_NAME) + .syntax(Document.OutputSettings.Syntax.xml) + outerHtml() + } + } +} + +fun main(args: Array<String>) { + LOGGER.log(Level.INFO, "beginning execution") + val con = Container().apply { + layout = BoxLayout(this, BoxLayout.Y_AXIS) + } + var frame: JFrame? = null + inSwingThread { + frame = JFrame("ClipMan").apply { + preferredSize = Dimension(640, 480) + contentPane.add( + JScrollPane(con).apply { + verticalScrollBarPolicy = ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED + horizontalScrollBarPolicy = ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER + preferredSize = Dimension(640, 480) + }, BorderLayout.CENTER) + pack() + setVisible(true) + } + } + val queue = PasteboardQueue(con, 10) + val updater = UpdateIt(queue, 1000).apply { start() } + inSwingThread { frame?.addWindowListener(KillIt(updater)) } + LOGGER.log(Level.INFO, "execution complete") +} + +fun inSwingThread(block: () -> Unit) { + SwingUtilities.invokeLater(Runnable(block)) +} + +fun JTextComponent.autoSize(width: Int): Unit { + val SLOP = 10 + val dim = Dimension(width, width) + preferredSize = dim + size = dim + val r = modelToView(document.length) + preferredSize = Dimension(width, r.y + r.height + SLOP) +}