Mercurial > cgi-bin > hgweb.cgi > ClipMan
view src/name/blackcap/clipman/Pasteboard.kt @ 47:19d9da731c43
Recoded; cleaned up root namespace, removed race conditions.
author | David Barts <n5jrn@me.com> |
---|---|
date | Sun, 12 Apr 2020 14:31:06 -0700 |
parents | 33fbe3a78d84 |
children |
line wrap: on
line source
/* * 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.ByteArrayInputStream import java.nio.charset.Charset import java.util.logging.Level import java.util.logging.Logger import kotlin.collections.HashMap /* Kotlin bug: compaion class fails to see these unless they are out here. */ private val HTML_FLAVOR = DataFlavor("text/html; document=all; class=\"[B\"; charset=" + CHARSET_NAME) /** * Represents an item of data in the clipboard and how to read and * write it. */ sealed class PasteboardItem { /* the three possibilities for this class */ class Plain(val plain: String): PasteboardItem() class HTML(val plain: String, val html: String): PasteboardItem() class RTF(val plain: String, val rtf: ByteArray): PasteboardItem() { private var failed = false /* lazy conversion to HTML */ private var _html: String? = null val html: String? get() { if (failed || _html != null) { return _html } _html = htmlFromRTF() failed = _html == null return _html } private fun htmlFromRTF(): String? { ByteArrayInputStream(rtf).use { val (html, errors) = rtfToHtml(it) if (errors != null) { LOGGER.log(Level.WARNING, errors) return null } return html } } } /* we use this when writing data back to the clipboard */ private class PasteboardData(val item: PasteboardItem): Transferable, ClipboardOwner { 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.toByteArray(CHARSET) as Any) } is RTF -> { put(DataFlavor.stringFlavor, item.plain as Any) if (item.html != null) { put(HTML_FLAVOR, item.html!!.toByteArray(CHARSET) 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) {} } /** * Compare this PasteboardItem with another object. * @param other object * @return true iff this item's type and native content match the other's */ override operator fun equals(other: Any?): Boolean { return when (this) { is Plain -> (other is Plain) && (this.plain == other.plain) is HTML -> (other is HTML) && (this.html == other.html) is RTF -> (other is RTF) && (this.rtf contentEquals other.rtf) } } companion object { private val RTF_FLAVOR = DataFlavor("text/rtf; class=\"[B\"") 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() val plain = getClipboardData(DataFlavor.stringFlavor) as String? if (plain == null) { return null } val html = getClipboardData(HTML_FLAVOR) as ByteArray? if (html == null) { val rtf = getClipboardData(RTF_FLAVOR) as ByteArray? return if (rtf == null) { Plain(plain) } else { RTF(plain, rtf) } } else { return HTML(plain, html.toString(CHARSET)) } } /** * 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): Any? { try { return CLIPBOARD.getData(flavor) } catch (e: IOException) { return null } catch (e: UnsupportedFlavorException) { return null } } } }