view src/name/blackcap/clipman/SearchDialog.kt @ 27:8aa2dfac27eb

Big reorg; compiled but untested.
author David Barts <n5jrn@me.com>
date Wed, 29 Jan 2020 10:50:07 -0800
parents
children f1fcc1281dad
line wrap: on
line source

/*
 * The dialog that controls a search.
 */
package name.blackcap.clipman

import java.awt.Color
import java.awt.Toolkit
import java.awt.event.ActionEvent
import java.awt.event.ActionListener
import javax.swing.*
import javax.swing.border.*
import javax.swing.event.DocumentEvent
import javax.swing.event.DocumentListener

class SearchDialog: JDialog(frame.v), ActionListener, DocumentListener {
    /* the search term */
    private val _searchFor = JTextField(50).also {
        it.border = LineBorder(Color.GRAY, 1)
        it.horizontalAlignment = JTextField.LEFT
        it.alignmentX = JTextField.LEFT_ALIGNMENT
        it.text = ""
        it.document.addDocumentListener(this)
    }
    val searchFor: String
    get() {
        return _searchFor.text
    }

    /* whether or not we should ignore case */
    private val _ignoreCase = JCheckBox("Ignore case", true)
    val ignoreCase: Boolean
    get() {
        return _ignoreCase.isSelected()
    }

    /* whether or not searches should wrap around */
    private val _autoWrap = JCheckBox("Auto wrap", false)
    val autoWrap: Boolean
    get() {
        return _autoWrap.isSelected()
    }

    /* which direction to search */
    private val _forwards = JRadioButton("Forward", true)
    private val _backwards = JRadioButton("Backward", false)
    private val _direction = ButtonGroup().apply {
        add(_forwards)
        add(_backwards)
    }
    val direction: PasteboardQueue.Direction
    get() {
        if (_forwards.isSelected()) {
            return PasteboardQueue.Direction.FORWARDS
        }
        if (_backwards.isSelected()) {
            return PasteboardQueue.Direction.BACKWARDS
        }
        throw RuntimeException("impossible button state!")
    }

    /* where to begin searching from. unlike the other properties, this
       one is read/write. null means to start from the beginning on
       forward searches, and from the end on backward searches (i.e.
       search everything) */
    var origin: PasteboardQueue.Offset? = null

    private val _find = JButton("Find").also {
        it.actionCommand = "Find"
        it.addActionListener(this)
    }

    /* initializer */
    init {
        title = "Find"
        contentPane.apply {
            add(Box(BoxLayout.Y_AXIS).apply {
                add(Box(BoxLayout.Y_AXIS).apply {
                    add(JLabel("Search for").apply {
                        horizontalAlignment = JLabel.LEFT
                        alignmentX = JLabel.LEFT_ALIGNMENT
                    })
                    add(_searchFor)
                })
                add(Box(BoxLayout.X_AXIS).apply {
                    add(Box(BoxLayout.Y_AXIS).apply {
                        add(_ignoreCase)
                        add(_autoWrap)
                    })
                    add(Box(BoxLayout.Y_AXIS).apply {
                        add(_forwards)
                        add(_backwards)
                    })
                })
                add(_find)
            })
        }
        rootPane.setDefaultButton(_find)
    }

    override fun actionPerformed(e: ActionEvent) {
        if (e.actionCommand == "Find") {
            setVisible(false)
            find()
        }
    }

    override fun setVisible(visible: Boolean) {
        if (visible) {
            _searchFor.run {
                requestFocusInWindow()
                selectAll()
            }
        }
        super.setVisible(visible)
    }

    fun find(): Unit {
        fun doFind(o: PasteboardQueue.Offset?) = queue.v.find(searchFor,
            direction = direction, foldCase = ignoreCase, origin = o)
        var result = doFind(origin)
        if (result == null && origin != null && autoWrap) {
            result = doFind(null)
        }
        if (result == null) {
            Toolkit.getDefaultToolkit().beep()
        }
        origin = result
    }

    /* changing the search string resets the search origin */
    override fun changedUpdate(e: DocumentEvent) {
        if (e.document === _searchFor.document) {
            origin = null
        }
    }
    override fun insertUpdate(e: DocumentEvent) = changedUpdate(e)
    override fun removeUpdate(e: DocumentEvent) = changedUpdate(e)
}

val searchDialog = SearchDialog()