Mercurial > cgi-bin > hgweb.cgi > ClipMan
diff src/name/blackcap/clipman/Pasteboard.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 | 9dd58db4d15a |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/name/blackcap/clipman/Pasteboard.kt Tue Jan 14 14:07:19 2020 -0800 @@ -0,0 +1,139 @@ +/* + * We call the clipboard a "pasteboard" for our internal class name, not + * because I prefer that term (I don't) but so as to not clash with the + * AWT's Clipboard class. + */ +package name.blackcap.clipman + +import java.awt.Toolkit +import java.awt.datatransfer.Clipboard +import java.awt.datatransfer.ClipboardOwner +import java.awt.datatransfer.DataFlavor +import java.awt.datatransfer.Transferable +import java.awt.datatransfer.UnsupportedFlavorException +import java.io.IOException +import java.io.InputStream +import java.nio.charset.Charset +import java.util.logging.Level +import java.util.logging.Logger +import kotlin.collections.HashMap + +/* Constants, etc. */ +val CHARSET_NAME = "UTF-8" + +/* + * Represents an error dealing with pasteboard items. + */ +class PasteboardError(): Exception() + +/** + * Represents an item of data in the clipboard and how to read and + * write it. + */ +sealed class PasteboardItem { + data class Plain(val plain: String): PasteboardItem() + data class HTML(val plain: String, val html: String): PasteboardItem() + + private class PasteboardData(val item: PasteboardItem): + Transferable, ClipboardOwner { + private val CHARSET = Charset.forName(CHARSET_NAME) + private val HTML_FLAVOR = DataFlavor("text/html; document=all; class=\"[B\"; charset=" + CHARSET_NAME) + private val _data: HashMap<DataFlavor, Any> + private val flavors: Array<DataFlavor> + + init { + _data = HashMap<DataFlavor, Any>().apply { + when (item) { + is Plain -> put(DataFlavor.stringFlavor, item.plain as Any) + is HTML -> { + put(DataFlavor.stringFlavor, item.plain as Any) + put(HTML_FLAVOR, item.html as Any) + } + } + } + _data.keys.asIterable().run { + flavors = Array<DataFlavor>(count()) { elementAt(it) } + } + } + + override fun getTransferData(flavor: DataFlavor): Any { + return _data.get(flavor) ?: throw UnsupportedFlavorException(flavor) + } + + override fun getTransferDataFlavors(): Array<DataFlavor> = flavors + override fun isDataFlavorSupported(flavor: DataFlavor) = _data.containsKey(flavor) + override fun lostOwnership(clipboard: Clipboard, contents: Transferable) {} + } + + companion object { + private val CLIPBOARD = Toolkit.getDefaultToolkit().systemClipboard + + /** + * Read the item in the pasteboard. + * @return a PasteboardItem? object, null if nothing could be read + */ + fun read() : PasteboardItem? { + check() + var plain = getClipboardData(DataFlavor.stringFlavor) + if (plain == null) { + return null + } + var html = getClipboardData(DataFlavor.allHtmlFlavor) + if (html == null) { + html = htmlFromRTF() + } + return if (html == null) { Plain(plain) } else { HTML(plain, html) } + } + + /** + * Write an item to the pasteboard. + * @param item a PasteboardItem to write + */ + fun write(item: PasteboardItem) { + check() + val pbdata = PasteboardData(item) + CLIPBOARD.setContents(pbdata, pbdata) + } + + private fun check() { + if (CLIPBOARD == null) { + throw RuntimeException("no clipboard available!") + } + } + + private fun getClipboardData(flavor: DataFlavor): String? { + try { + return CLIPBOARD.getData(flavor) as String? + } catch (e: IOException) { + return null + } catch (e: UnsupportedFlavorException) { + return null + } + } + + private fun htmlFromRTF(): String? { + /* see if there's an appropriate flavor */ + var rtf: DataFlavor? = null + for (flavor in CLIPBOARD.availableDataFlavors) { + if (flavor.isRepresentationClassInputStream() && + "text".equals(flavor.primaryType ?: "", ignoreCase=true) && + "rtf".equals(flavor.subType ?: "", ignoreCase=true)) { + rtf = flavor + break + } + } + if (rtf == null) { + return null + } + + (CLIPBOARD.getData(rtf) as InputStream).use { + val (html, errors) = rtfToHtml(it) + if (errors != null) { + LOGGER.log(Level.WARNING, errors) + return null + } + return html + } + } + } +}