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

Big reorg; compiled but untested.
author David Barts <n5jrn@me.com>
date Wed, 29 Jan 2020 10:50:07 -0800
parents c10a447b9e1b
children c4f53bc01732
line wrap: on
line diff
--- a/src/name/blackcap/clipman/PasteboardQueue.kt	Wed Jan 29 10:47:46 2020 -0800
+++ b/src/name/blackcap/clipman/PasteboardQueue.kt	Wed Jan 29 10:50:07 2020 -0800
@@ -11,7 +11,7 @@
 import java.util.logging.Level
 import java.util.logging.Logger
 import javax.swing.*
-import javax.swing.text.JTextComponent
+import javax.swing.text.DefaultHighlighter
 
 /**
  * A queue that tracks the data we display and the widgets used to
@@ -56,7 +56,7 @@
      */
     @Synchronized fun add(item: QueueItem) {
         inSwingThread {
-            parent.add(item.component)
+            parent.add(item.view.contents)
             scrollPane?.run {
                 validate()
                 verticalScrollBar.run { value = maximum + 1 }
@@ -67,6 +67,16 @@
     }
 
     /**
+     * Delete something from the queue.
+     * @param object to delete
+     */
+    @Synchronized fun delete(item: QueueItem) {
+        if (queue.remove(item)) {
+            parent.validate()
+        }
+    }
+
+    /**
      * Find and highlight the next occurrence of the specified string
      * @param string to search
      * @param whether to search backwards (default forwards)
@@ -77,18 +87,68 @@
     fun find(needle: String, direction: Direction = Direction.FORWARDS,
         foldCase: Boolean = true, origin: Offset? = null): Offset?
     {
-        /* canonicalize the origin */
-        val norigin = if (origin == null) {
-            if (direction == Direction.FORWARDS) {
-                Offset(0, 0)
-            } else {
-                Offset(queue.size - 1, queue.last.searchable.document.length)
+        /* clean up any old highlights */
+        queue.forEach {
+            val hiliter = it.view.searchable.highlighter
+            hiliter.highlights.forEach {
+                hiliter.removeHighlight(it)
             }
-        } else {
-            origin
+        }
+
+        /* get starting item index */
+        val qMax = queue.size
+        var norigin = origin ?: when (direction) {
+            Direction.FORWARDS -> Offset(0, 0)
+            Direction.BACKWARDS -> Offset(qMax - 1, -1)
         }
 
-        /* XXX - not finished */
+        /* loop initialization */
+        val (start, incr, search) = if (direction == Direction.FORWARDS) {
+            Triple( 0,  1, { n: String, h: String, o: Int -> h.indexOf(n, o, foldCase) })
+        } else {
+            Triple(-1, -1, { n: String, h: String, o: Int -> h.lastIndexOf(n, o, foldCase) })
+        }
+        val painter = DefaultHighlighter.DefaultHighlightPainter(null);
+        var pos = -1
+
+        /* try and find it */
+        while (norigin.inQueue >= 0 && norigin.inQueue < qMax) {
+            val si = queue.get(norigin.inQueue).view.searchable
+            val doc = si.document
+            val text = doc.getText(0, doc.length)
+            pos = if (norigin.inItem >= 0) norigin.inItem else text.length - 1
+            pos = search(needle, text, pos)
+            if (pos >= 0) {
+                si.highlighter.addHighlight(pos, pos+needle.length, painter)
+                break
+            }
+            norigin = Offset(norigin.inQueue + incr, start)
+        }
+        return if (pos >= 0) Offset(norigin.inQueue, pos) else null
+    }
+
+    /**
+     * Ensure none of the searchables in this queue are selected.
+     */
+    fun deselectAll() {
+        queue.forEach {
+            val s = it.view.searchable as? ClipText
+            if (s != null && s.selected) {
+                s.selected = false
+                s.validate()
+            }
+        }
+    }
+
+    /**
+     * Return the selected item, or null if nothing has been selected
+     */
+    fun getSelected(): QueueItem? {
+        queue.forEach {
+            if ((it.view.searchable as? ClipText)?.selected ?: false) {
+                return it
+            }
+        }
         return null
     }
 
@@ -97,8 +157,13 @@
             var size = queue.size
             var dirty = false
             while (size > _maxSize) {
-                var extra = queue.removeFirst().component
-                inSwingThread { parent.remove(extra) }
+                var extra = queue.removeFirst().view
+                inSwingThread {
+                    if (extra.searchable.selected) {
+                        SelectionRequired.disable()
+                    }
+                    parent.remove(extra.contents)
+                }
                 dirty = true
                 size -= 1
             }
@@ -110,9 +175,9 @@
 }
 
 /**
- * An item in the above queue.
+ * An item in the above queue. Linking model to view here sorta violates
+ * MVC principles, but rules are sometimes best broken. Doing it this way
+ * makes it impossible for the view queue to fail to follow the data
+ * queue.
  */
-data class QueueItem(
-    val component: JComponent,
-    val searchable: JTextComponent,
-    val contents: PasteboardItem)
+data class QueueItem(val contents: PasteboardItem, val view: PasteboardItemView)