# HG changeset patch # User David Barts # Date 1647756280 25200 # Node ID 22725d4d78490cef8ec89eadb88ac27698e2d0b7 # Parent 7ad2b29a7f60bddc0c382a4b884ae2c9db320c52 An attempt to get it to troff-ize styled text. diff -r 7ad2b29a7f60 -r 22725d4d7849 setup.sh --- a/setup.sh Tue Apr 13 10:34:51 2021 -0700 +++ b/setup.sh Sat Mar 19 23:04:40 2022 -0700 @@ -1,7 +1,11 @@ #!/bin/bash export JRE_HOME="$(/usr/libexec/java_home)" -export KOTLIN_HOME="/usr/local/Cellar/kotlin/1.3.71/libexec" +export KOTLIN_HOME="/Users/davidb/kotlin/1.6.10/kotlinc" +if [[ "$PATH" != *$KOTLIN_HOME/bin* ]] +then + export PATH="$KOTLIN_HOME/bin:$PATH" +fi export ANT_HOME="$HOME/java/apache-ant-1.10.1" if [[ "$PATH" != *$ANT_HOME/bin* ]] diff -r 7ad2b29a7f60 -r 22725d4d7849 src/name/blackcap/clipman/CoerceDialog.kt --- a/src/name/blackcap/clipman/CoerceDialog.kt Tue Apr 13 10:34:51 2021 -0700 +++ b/src/name/blackcap/clipman/CoerceDialog.kt Sat Mar 19 23:04:40 2022 -0700 @@ -162,36 +162,28 @@ private fun coerce() { val selected = Application.queue.getSelected() + if (!suitedForCoercing(selected)) { + return + } if (selected == null) { - JOptionPane.showMessageDialog(Application.frame, - "No item selected.", - "Error", - JOptionPane.ERROR_MESSAGE) - } else { - val (plain, html) = when (selected.contents) { - is PasteboardItem.Plain -> - Pair(selected.contents.plain, null) - is PasteboardItem.HTML -> - Pair(selected.contents.plain, selected.contents.html) - is PasteboardItem.RTF -> - Pair(selected.contents.plain, selected.contents.html) - } - if (html == null) { - JOptionPane.showMessageDialog(Application.frame, - "Only styled texts may be coerced.", - "Error", - JOptionPane.ERROR_MESSAGE) - } else { - if (badSize(_pSize, PROP_SIZE, "proportionally-spaced") || badSize(_mSize, MONO_SIZE, "monospaced")) { - return - } - PasteboardItem.write( - PasteboardItem.HTML( - plain, - coerceHTML(html, normalizeFont(pFamily), pSize, - normalizeFont(mFamily), mSize))) - } + return /* redundant, but makes kotlinc happy */ + } + if (badSize(_pSize, PROP_SIZE, "proportionally-spaced") || badSize(_mSize, MONO_SIZE, "monospaced")) { + return } + val (plain, html) = when (selected.contents) { + is PasteboardItem.Plain -> + Pair(selected.contents.plain, null) + is PasteboardItem.HTML -> + Pair(selected.contents.plain, selected.contents.html) + is PasteboardItem.RTF -> + Pair(selected.contents.plain, selected.contents.html) + } + PasteboardItem.write( + PasteboardItem.HTML( + plain, + coerceHTML(html!!, normalizeFont(pFamily), pSize, + normalizeFont(mFamily), mSize))) } private fun badSize(control: JComboBox, default: Int, fontType: String): Boolean { @@ -226,3 +218,25 @@ } } } + +/** + * See if the selected pasteboard item is suitable for coercing. If not, + * issue an error dialog. + */ +fun suitedForCoercing(selected: QueueItem?): Boolean { + if (selected == null) { + JOptionPane.showMessageDialog(Application.frame, + "No item selected.", + "Error", + JOptionPane.ERROR_MESSAGE) + return false + } + if (selected.contents is PasteboardItem.Plain) { + JOptionPane.showMessageDialog(Application.frame, + "Only styled texts may be coerced.", + "Error", + JOptionPane.ERROR_MESSAGE) + return false + } + return true +} diff -r 7ad2b29a7f60 -r 22725d4d7849 src/name/blackcap/clipman/Menus.kt --- a/src/name/blackcap/clipman/Menus.kt Tue Apr 13 10:34:51 2021 -0700 +++ b/src/name/blackcap/clipman/Menus.kt Sat Mar 19 23:04:40 2022 -0700 @@ -28,6 +28,7 @@ "Edit.Coerce" -> onlyIfSelected { Application.coerceDialog.setVisible(true) } "Edit.Find" -> Application.searchDialog.setVisible(true) "Edit.FindAgain" -> Application.searchDialog.find() + "Edit.Troff" -> onlyIfSelected { if (suitedForCoercing(it)) { troffize(it.contents) } } "Help.About" -> showAboutDialog() else -> throw RuntimeException("unexpected actionCommand!") } @@ -102,6 +103,12 @@ addActionListener(Application.menuItemListener) makeShortcut(KeyEvent.VK_K) })) + add(Application.styledRequired.add(JMenuItem("Convert to Troff").apply { + setEnabled(false) + actionCommand = "Edit.Troff" + addActionListener(Application.menuItemListener) + makeShortcut(KeyEvent.VK_T) + })) add(JMenuItem("Find…").apply { actionCommand = "Edit.Find" addActionListener(Application.menuItemListener) diff -r 7ad2b29a7f60 -r 22725d4d7849 src/name/blackcap/clipman/Troff.kt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/name/blackcap/clipman/Troff.kt Sat Mar 19 23:04:40 2022 -0700 @@ -0,0 +1,78 @@ +/* + * Coercion to troff input. + */ +package name.blackcap.clipman + +import org.jsoup.Jsoup +import org.jsoup.nodes.* +import org.jsoup.select.NodeVisitor + +class Troffizer: NodeVisitor { + private enum class Typeface(val pos: Int) { + ROMAN(1), + ITALIC(2), + BOLD(3) + } + + private val tfStack = mutableListOf(Typeface.ROMAN); + + private val TF_TAGS = mapOf( + "em" to Typeface.ITALIC, + "i" to Typeface.ITALIC, + "b" to Typeface.BOLD, + "strong" to Typeface.BOLD) + + private val accum = StringBuilder(); + + override fun head(node: Node, depth: Int): Unit { + when (node) { + is TextNode -> accum.append(node.text()) + is Element -> enterElement(node) + } + } + + override fun tail(node: Node, depth: Int): Unit { + if (node is Element) { + leaveElement(node) + } + } + + private fun enterElement(element: Element) { + var newFace = TF_TAGS[element.normalName()] + if (newFace != null) { + tfStack.add(newFace) + accum.append("\\f") + accum.append(newFace.pos) + } + } + + private fun leaveElement(element: Element) { + if (element.normalName() in TF_TAGS) { + tfStack.removeLast() + accum.append("\\f") + accum.append(tfStack.lastOrNull()?.pos ?: Typeface.ROMAN.pos) + } + } + + fun getTroff(): String = accum.toString() +} + +private fun _troffize(html: String): String { + val troffizer = Troffizer() + Jsoup.parse(html).traverse(troffizer) + return troffizer.getTroff() +} + +fun troffize(item: PasteboardItem): Unit { + val (plain, html) = when (item) { + is PasteboardItem.Plain -> + Pair(item.plain, null) + is PasteboardItem.HTML -> + Pair(item.plain, item.html) + is PasteboardItem.RTF -> + Pair(item.plain, item.html) + } + PasteboardItem.write( + PasteboardItem.Plain( + if (html == null) { plain } else { _troffize(html) })) +}