Mercurial > cgi-bin > hgweb.cgi > ClipMan
comparison 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 |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:be282c48010a |
---|---|
1 /* | |
2 * We call the clipboard a "pasteboard" for our internal class name, not | |
3 * because I prefer that term (I don't) but so as to not clash with the | |
4 * AWT's Clipboard class. | |
5 */ | |
6 package name.blackcap.clipman | |
7 | |
8 import java.awt.Toolkit | |
9 import java.awt.datatransfer.Clipboard | |
10 import java.awt.datatransfer.ClipboardOwner | |
11 import java.awt.datatransfer.DataFlavor | |
12 import java.awt.datatransfer.Transferable | |
13 import java.awt.datatransfer.UnsupportedFlavorException | |
14 import java.io.IOException | |
15 import java.io.InputStream | |
16 import java.nio.charset.Charset | |
17 import java.util.logging.Level | |
18 import java.util.logging.Logger | |
19 import kotlin.collections.HashMap | |
20 | |
21 /* Constants, etc. */ | |
22 val CHARSET_NAME = "UTF-8" | |
23 | |
24 /* | |
25 * Represents an error dealing with pasteboard items. | |
26 */ | |
27 class PasteboardError(): Exception() | |
28 | |
29 /** | |
30 * Represents an item of data in the clipboard and how to read and | |
31 * write it. | |
32 */ | |
33 sealed class PasteboardItem { | |
34 data class Plain(val plain: String): PasteboardItem() | |
35 data class HTML(val plain: String, val html: String): PasteboardItem() | |
36 | |
37 private class PasteboardData(val item: PasteboardItem): | |
38 Transferable, ClipboardOwner { | |
39 private val CHARSET = Charset.forName(CHARSET_NAME) | |
40 private val HTML_FLAVOR = DataFlavor("text/html; document=all; class=\"[B\"; charset=" + CHARSET_NAME) | |
41 private val _data: HashMap<DataFlavor, Any> | |
42 private val flavors: Array<DataFlavor> | |
43 | |
44 init { | |
45 _data = HashMap<DataFlavor, Any>().apply { | |
46 when (item) { | |
47 is Plain -> put(DataFlavor.stringFlavor, item.plain as Any) | |
48 is HTML -> { | |
49 put(DataFlavor.stringFlavor, item.plain as Any) | |
50 put(HTML_FLAVOR, item.html as Any) | |
51 } | |
52 } | |
53 } | |
54 _data.keys.asIterable().run { | |
55 flavors = Array<DataFlavor>(count()) { elementAt(it) } | |
56 } | |
57 } | |
58 | |
59 override fun getTransferData(flavor: DataFlavor): Any { | |
60 return _data.get(flavor) ?: throw UnsupportedFlavorException(flavor) | |
61 } | |
62 | |
63 override fun getTransferDataFlavors(): Array<DataFlavor> = flavors | |
64 override fun isDataFlavorSupported(flavor: DataFlavor) = _data.containsKey(flavor) | |
65 override fun lostOwnership(clipboard: Clipboard, contents: Transferable) {} | |
66 } | |
67 | |
68 companion object { | |
69 private val CLIPBOARD = Toolkit.getDefaultToolkit().systemClipboard | |
70 | |
71 /** | |
72 * Read the item in the pasteboard. | |
73 * @return a PasteboardItem? object, null if nothing could be read | |
74 */ | |
75 fun read() : PasteboardItem? { | |
76 check() | |
77 var plain = getClipboardData(DataFlavor.stringFlavor) | |
78 if (plain == null) { | |
79 return null | |
80 } | |
81 var html = getClipboardData(DataFlavor.allHtmlFlavor) | |
82 if (html == null) { | |
83 html = htmlFromRTF() | |
84 } | |
85 return if (html == null) { Plain(plain) } else { HTML(plain, html) } | |
86 } | |
87 | |
88 /** | |
89 * Write an item to the pasteboard. | |
90 * @param item a PasteboardItem to write | |
91 */ | |
92 fun write(item: PasteboardItem) { | |
93 check() | |
94 val pbdata = PasteboardData(item) | |
95 CLIPBOARD.setContents(pbdata, pbdata) | |
96 } | |
97 | |
98 private fun check() { | |
99 if (CLIPBOARD == null) { | |
100 throw RuntimeException("no clipboard available!") | |
101 } | |
102 } | |
103 | |
104 private fun getClipboardData(flavor: DataFlavor): String? { | |
105 try { | |
106 return CLIPBOARD.getData(flavor) as String? | |
107 } catch (e: IOException) { | |
108 return null | |
109 } catch (e: UnsupportedFlavorException) { | |
110 return null | |
111 } | |
112 } | |
113 | |
114 private fun htmlFromRTF(): String? { | |
115 /* see if there's an appropriate flavor */ | |
116 var rtf: DataFlavor? = null | |
117 for (flavor in CLIPBOARD.availableDataFlavors) { | |
118 if (flavor.isRepresentationClassInputStream() && | |
119 "text".equals(flavor.primaryType ?: "", ignoreCase=true) && | |
120 "rtf".equals(flavor.subType ?: "", ignoreCase=true)) { | |
121 rtf = flavor | |
122 break | |
123 } | |
124 } | |
125 if (rtf == null) { | |
126 return null | |
127 } | |
128 | |
129 (CLIPBOARD.getData(rtf) as InputStream).use { | |
130 val (html, errors) = rtfToHtml(it) | |
131 if (errors != null) { | |
132 LOGGER.log(Level.WARNING, errors) | |
133 return null | |
134 } | |
135 return html | |
136 } | |
137 } | |
138 } | |
139 } |