diff src/name/blackcap/clipman/Main.kt @ 27:8aa2dfac27eb

Big reorg; compiled but untested.
author David Barts <n5jrn@me.com>
date Wed, 29 Jan 2020 10:50:07 -0800
parents dac8dfb4b549
children f1fcc1281dad
line wrap: on
line diff
--- a/src/name/blackcap/clipman/Main.kt	Wed Jan 29 10:47:46 2020 -0800
+++ b/src/name/blackcap/clipman/Main.kt	Wed Jan 29 10:50:07 2020 -0800
@@ -4,25 +4,19 @@
 package name.blackcap.clipman
 
 import java.awt.BorderLayout
-import java.awt.Color
 import java.awt.Container
 import java.awt.Dimension
 import java.awt.Font
-import java.awt.Toolkit;
 import java.awt.datatransfer.*
-import java.awt.event.ActionEvent
-import java.awt.event.ActionListener
-import java.awt.event.KeyEvent
+import java.awt.event.MouseEvent
+import java.awt.event.MouseListener
 import java.awt.event.WindowEvent
 import java.awt.event.WindowListener
 import java.util.Date
-import java.util.concurrent.Semaphore
 import java.util.logging.Level
 import java.util.logging.Logger
 import javax.swing.*
 import javax.swing.border.*
-import javax.swing.text.JTextComponent
-import javax.swing.text.html.HTMLEditorKit
 import javax.swing.text.html.StyleSheet
 import kotlin.concurrent.thread
 import org.jsoup.Jsoup
@@ -35,19 +29,19 @@
 val CPWIDTH = 640
 val CPHEIGHT = 480
 
-/* border widths */
+/* width of main panel border */
 val PANEL_BORDER = 9
-val OUTER_BORDER_TOP = 3
-val OUTER_BORDER = 9
-val INNER_BORDER = 1
-val MARGIN_BORDER = 3
 
 /* default font sizes in the text-display panes */
 val MONO_SIZE = 14
 val PROP_SIZE = 16
 
+/* the queue of data we deal with and the main application frame */
+val queue = LateInit<PasteboardQueue>()
+val frame = LateInit<JFrame>()
+
 /* kills the updating thread (and does a system exit) when needed */
-class KillIt(val thr: Thread) : WindowListener {
+class KillIt() : WindowListener {
     // events we don't care about
     override fun windowActivated(e: WindowEvent) {}
     override fun windowClosed(e: WindowEvent) {}
@@ -58,41 +52,14 @@
 
     // and the one we do
     override fun windowClosing(e: WindowEvent) {
-        thr.run { interrupt(); join() }
         LOGGER.log(Level.INFO, "execution complete")
         System.exit(0)
     }
 }
 
-class ClipText: JTextPane() {
-    override fun getMaximumSize(): Dimension {
-        return Dimension(Int.MAX_VALUE, preferredSize.height)
-    }
-}
-
-/* HTMLEditorKit shares all stylesheets. How unbelievably braindamaged. */
-class MyEditorKit: HTMLEditorKit() {
-    private var _styleSheet = defaultStyleSheet
-    override fun getStyleSheet() = _styleSheet
-    override fun setStyleSheet(value: StyleSheet) {
-        _styleSheet = value
-    }
-
-    val defaultStyleSheet: StyleSheet
-    get() {
-        return super.getStyleSheet()
-    }
-}
-
 /* the updating thread */
-class UpdateIt(val queue: PasteboardQueue, val interval: Int): Thread() {
+class UpdateIt(val interval: Int): Thread(), MouseListener {
     @Volatile var enabled = true
-    private val outerBorder =
-        MatteBorder(OUTER_BORDER_TOP, OUTER_BORDER, OUTER_BORDER, OUTER_BORDER,
-            queue.parent.background)
-    private val stdBorder =
-        CompoundBorder(LineBorder(Color.GRAY, INNER_BORDER),
-            EmptyBorder(MARGIN_BORDER, MARGIN_BORDER, MARGIN_BORDER, MARGIN_BORDER))
 
     override fun run() {
         var oldContents: PasteboardItem? = null
@@ -100,52 +67,30 @@
             if (enabled) {
                 var contents = PasteboardItem.read()
                 if ((contents != null) && (contents != oldContents)) {
-                    val stdWidth = queue.parent.size.width - 2 * (PANEL_BORDER+OUTER_BORDER+INNER_BORDER+MARGIN_BORDER)
-                    val widget = JPanel().apply {
-                        layout = BoxLayout(this, BoxLayout.Y_AXIS)
-                        background = queue.parent.background
-                        border = outerBorder
-                    }
                     val (plain, html) = when(contents) {
                         is PasteboardItem.Plain -> Pair(contents.plain, null)
                         is PasteboardItem.HTML -> Pair(null, contents.html)
                         is PasteboardItem.RTF -> Pair(contents.plain, contents.html)
                     }
-                    var searchable: JTextComponent? = null
-                    if (html == null) {
-                        widget.run {
-                            add(stdLabel("Plain text"))
-                            searchable = ClipText().apply {
-                                contentType = "text/plain"
-                                text = plain
-                                font = Font(Font.MONOSPACED, Font.PLAIN, MONO_SIZE)
-                                border = stdBorder
-                                autoSize(stdWidth)
-                                setEditable(false)
-                                alignmentX = JTextPane.LEFT_ALIGNMENT
-                            }
-                            add(searchable)
-                        }
+                    val piv = if (html == null) {
+                        PasteboardItemView("Plain text", ClipText().apply {
+                            contentType = "text/plain"
+                            text = plain
+                            font = Font(Font.MONOSPACED, Font.PLAIN, MONO_SIZE)
+                        })
                     } else {
-                        widget.run {
-                            add(stdLabel("Styled text"))
-                            val (dhtml, style) = preproc(html)
-                            val hek = MyEditorKit().apply {
-                                style.addStyleSheet(defaultStyleSheet)
-                                styleSheet = style
-                            }
-                            searchable = ClipText().apply {
-                                editorKit = hek
-                                text = dhtml
-                                border = stdBorder
-                                autoSize(stdWidth)
-                                setEditable(false)
-                                alignmentX = JTextPane.LEFT_ALIGNMENT
-                            }
-                            add(searchable)
+                        val (dhtml, style) = preproc(html)
+                        val hek = MyEditorKit().apply {
+                            style.addStyleSheet(defaultStyleSheet)
+                            styleSheet = style
                         }
+                        PasteboardItemView("Styled text", ClipText().apply {
+                            editorKit = hek
+                            text = dhtml
+                        })
                     }
-                    queue.add(QueueItem(widget, searchable!!, contents))
+                    piv.searchable.addMouseListener(this)
+                    queue.v.add(QueueItem(contents, piv))
                     oldContents = contents
                 }
             }
@@ -160,15 +105,10 @@
         }
     }
 
-    private fun stdLabel(text: String) = JLabel(text).apply {
-        horizontalAlignment = JLabel.LEFT
-        alignmentX = JLabel.LEFT_ALIGNMENT
-    }
-
     private fun preproc(html: String): Pair<String, StyleSheet> {
         val sty = StyleSheet().apply {
-            addRule("body { font-family: serif; font-size: %d; }".format(PROP_SIZE))
-            addRule("code, kbd, pre, samp, tt { font-family: monospace; font-size: %d; }".format(MONO_SIZE))
+            addRule("body { font-family: serif; font-size: ${PROP_SIZE}; }")
+            addRule("code, kbd, pre, samp, tt { font-family: monospace; font-size: ${MONO_SIZE}; }")
         }
         val scrubbed = Jsoup.parse(html).run {
             select("style").forEach {
@@ -182,113 +122,67 @@
         }
         return Pair(scrubbed, sty)
     }
+
+    /* MouseListener methods */
+
+    override fun mouseClicked(e: MouseEvent) {
+        val source = e.getSource() as? ClipText
+        if (source == null) {
+            return
+        }
+        queue.v.deselectAll()
+        source.selected = true
+        source.validate()
+        SelectionRequired.enable()
+    }
+
+ 	override fun mousePressed(e: MouseEvent) {
+ 	    maybeShowPopup(e)
+ 	}
+
+ 	override fun mouseReleased(e: MouseEvent) {
+ 	    maybeShowPopup(e)
+ 	}
+
+ 	private fun maybeShowPopup(e: MouseEvent) {
+ 	    if (e.isPopupTrigger()) {
+ 	        popupMenu.show(e.component, e.x, e.y)
+ 	    }
+ 	}
+
+ 	override fun mouseEntered(e: MouseEvent) { }
+ 	override fun mouseExited(e: MouseEvent) { }
 }
 
+/* entry point */
 fun main(args: Array<String>) {
     LOGGER.log(Level.INFO, "beginning execution")
     if (OS.type == OS.MAC) {
         System.setProperty("apple.laf.useScreenMenuBar", "true")
     }
-    var frame: JFrame? = null
-    var con: JPanel? = null
+    lateinit var con: JPanel
     inSynSwingThread {
         UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
-        frame = JFrame(MYNAME)
+        frame.v = JFrame(MYNAME)
         con = JPanel().apply {
             layout = BoxLayout(this, BoxLayout.Y_AXIS)
             border = EmptyBorder(PANEL_BORDER, PANEL_BORDER, PANEL_BORDER, PANEL_BORDER)
-            background = frame!!.background
+            background = frame.v.background
         }
-        frame!!.apply {
-            jMenuBar = makeMenuBar()
+        frame.v.jMenuBar = menuBar
+        frame.v.apply {
             contentPane.add(
-                JScrollPane(con!!).apply {
+                JScrollPane(con).apply {
                     verticalScrollBarPolicy = ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS
                     horizontalScrollBarPolicy = ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER
                     preferredSize = Dimension(CPWIDTH, CPHEIGHT)
-                    background = frame!!.background
+                    background = frame.v.background
                 }, BorderLayout.CENTER)
             pack()
             setVisible(true)
+            addWindowListener(KillIt())
         }
     }
-    val queue = PasteboardQueue(con!!, 10)
-    val updater = UpdateIt(queue, 1000).apply { start() }
-    inSwingThread { frame!!.addWindowListener(KillIt(updater)) }
-}
-
-class MenuItemListener: ActionListener {
-    override fun actionPerformed(e: ActionEvent) {
-        println(e.actionCommand + " selected")
-    }
+    queue.v = PasteboardQueue(con, 10)
+    UpdateIt(1000).apply { start() }
 }
-
-fun makeMenuBar() = JMenuBar().apply {
-    val al: ActionListener = MenuItemListener()
-    if (OS.type != OS.MAC) {
-        add(JMenu("File").apply {
-            add(JMenuItem("Quit").apply {
-                actionCommand = "File.Quit"
-                addActionListener(al)
-                makeShortcut(KeyEvent.VK_Q)
-            })
-        })
-    }
-    add(JMenu("Edit").apply {
-        add(JMenuItem("Clone").apply {
-            actionCommand = "Edit.Clone"
-            addActionListener(al)
-            makeShortcut(KeyEvent.VK_C)
-        })
-        add(JMenuItem("Coerce…").apply {
-            actionCommand = "Edit.Coerce"
-            addActionListener(al)
-            makeShortcut(KeyEvent.VK_K)
-        })
-        add(JMenuItem("Delete").apply {
-            actionCommand = "Edit.Delete"
-            addActionListener(al)
-            setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_DELETE, 0))
-        })
-        add(JMenuItem("Find…").apply {
-            actionCommand = "Edit.Find"
-            addActionListener(al)
-            makeShortcut(KeyEvent.VK_F)
-        })
-    })
-    if (OS.type != OS.MAC) {
-        add(JMenu("Help").apply {
-            add(JMenuItem("About ClipMan…").apply {
-                actionCommand = "Help.About"
-                addActionListener(al)
-            })
-        })
-    }
-}
-
-fun inSwingThread(block: () -> Unit) {
-    SwingUtilities.invokeLater(Runnable(block))
-}
-
-fun inSynSwingThread(block: () -> Unit) {
-    val ready = Semaphore(0)
-    inSwingThread {
-        block()
-        ready.release()
-    }
-    ready.acquire()
-}
-
-fun JTextComponent.autoSize(width: Int): Unit {
-    val SLOP = 10
-    val dim = Dimension(width, width)
-    preferredSize = dim
-    size = dim
-    val r = modelToView(document.length)
-    preferredSize = Dimension(width, r.y + r.height + SLOP)
-}
-
-val SC_KEY_MASK = Toolkit.getDefaultToolkit().menuShortcutKeyMask
-fun JMenuItem.makeShortcut(key: Int): Unit {
-    setAccelerator(KeyStroke.getKeyStroke(key, SC_KEY_MASK))
-}