diff src/name/blackcap/clipman/Pasteboard.kt @ 17:9dd58db4d15a

Only convert RTF to HTML if needed. Much more efficient.
author David Barts <n5jrn@me.com>
date Tue, 21 Jan 2020 16:24:18 -0800
parents be282c48010a
children 96cc73ae2904
line wrap: on
line diff
--- a/src/name/blackcap/clipman/Pasteboard.kt	Tue Jan 21 13:07:35 2020 -0800
+++ b/src/name/blackcap/clipman/Pasteboard.kt	Tue Jan 21 16:24:18 2020 -0800
@@ -12,7 +12,7 @@
 import java.awt.datatransfer.Transferable
 import java.awt.datatransfer.UnsupportedFlavorException
 import java.io.IOException
-import java.io.InputStream
+import java.io.ByteArrayInputStream
 import java.nio.charset.Charset
 import java.util.logging.Level
 import java.util.logging.Logger
@@ -31,8 +31,40 @@
  * write it.
  */
 sealed class PasteboardItem {
-    data class Plain(val plain: String): PasteboardItem()
-    data class HTML(val plain: String, val html: String): 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 {
@@ -44,10 +76,16 @@
         init {
             _data = HashMap<DataFlavor, Any>().apply {
                 when (item) {
-                    is Plain ->  put(DataFlavor.stringFlavor, item.plain as Any)
+                    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)
+                        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)
+                        }
                     }
                 }
             }
@@ -65,7 +103,16 @@
         override fun lostOwnership(clipboard: Clipboard, contents: Transferable) {}
     }
 
+    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
 
         /**
@@ -74,15 +121,17 @@
          */
         fun read() : PasteboardItem? {
             check()
-            var plain = getClipboardData(DataFlavor.stringFlavor)
+            val plain = getClipboardData(DataFlavor.stringFlavor) as String?
             if (plain == null) {
                 return null
             }
-            var html = getClipboardData(DataFlavor.allHtmlFlavor)
+            val html = getClipboardData(DataFlavor.allHtmlFlavor) as String?
             if (html == null) {
-                html = htmlFromRTF()
+                val rtf = getClipboardData(RTF_FLAVOR) as ByteArray?
+                return if (rtf == null) { Plain(plain) } else { RTF(plain, rtf) }
+            } else {
+                return HTML(plain, html)
             }
-            return if (html == null) { Plain(plain) } else { HTML(plain, html) }
         }
 
         /**
@@ -101,39 +150,14 @@
             }
         }
 
-        private fun getClipboardData(flavor: DataFlavor): String? {
+        private fun getClipboardData(flavor: DataFlavor): Any? {
             try {
-                return CLIPBOARD.getData(flavor) as String?
+                return CLIPBOARD.getData(flavor)
             } 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
-            }
-        }
     }
 }