view src/name/blackcap/clipman/SearchDialog.kt @ 38:08eaae2aaf76

Stop dialog resizing, fix detection of bad font sizes.
author David Barts <n5jrn@me.com>
date Thu, 30 Jan 2020 22:06:37 -0800
parents 0c6c18a733b7
children 19d9da731c43
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.event.DocumentEvent
import javax.swing.event.DocumentListener

class SearchDialog: JDialog(frame.v), ActionListener, DocumentListener {
    /* the search term */
    private val _searchFor = JTextField(25).also {
        it.border = BorderFactory.createLineBorder(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!")
    }

    /* standard spacing between elements (10 pixels ≅ 1/7") and half that */
    private val BW = 5
    private val BW2 = 10

    /* 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)
    }

    private val _cancel = JButton("Cancel").also {
        it.actionCommand = "Cancel"
        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)
                    alignmentX = Box.CENTER_ALIGNMENT
                    border = BorderFactory.createEmptyBorder(BW2, BW2, BW, BW2)
                })
                add(Box(BoxLayout.X_AXIS).apply {
                    add(Box.createGlue())
                    add(Box(BoxLayout.Y_AXIS).apply {
                        add(JLabel("Settings:").apply {
                            horizontalAlignment = JLabel.CENTER
                            alignmentX = JLabel.LEFT_ALIGNMENT
                        })
                        add(_ignoreCase)
                        add(_autoWrap)
                    })
                    add(Box.createGlue())
                    add(Box(BoxLayout.Y_AXIS).apply {
                        add(JLabel("Direction:").apply {
                            horizontalAlignment = JLabel.CENTER
                            alignmentX = JLabel.LEFT_ALIGNMENT
                        })
                        add(_forwards)
                        add(_backwards)
                    })
                    add(Box.createGlue())
                    alignmentX = Box.CENTER_ALIGNMENT
                    border = BorderFactory.createEmptyBorder(BW, BW2, BW, BW2)
                })
                add(Box(BoxLayout.X_AXIS).apply {
                    add(Box.createGlue())
                    add(_cancel)
                    add(Box.createGlue())
                    add(_find)
                    add(Box.createGlue())
                    border = BorderFactory.createEmptyBorder(BW, BW2, BW2, BW2)
                })
            })
        }
        rootPane.setDefaultButton(_find)
        pack()
        setResizable(false)
    }

    override fun actionPerformed(e: ActionEvent) {
        when (e.actionCommand) {
            "Find" -> {
                setVisible(false)
                find()
            }
            "Cancel" -> setVisible(false)
        }
    }

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

    fun find(): Unit {
        if (searchFor.isEmpty()) {
            Toolkit.getDefaultToolkit().beep()
            origin = null
            return
        }
        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 = null
        } else {
            origin = when(direction) {
                PasteboardQueue.Direction.FORWARDS ->
                    PasteboardQueue.Offset(result.inQueue, result.inItem + 1)
                PasteboardQueue.Direction.BACKWARDS ->
                    if (result.inItem == 0) {
                        if (result.inQueue == 0) null else PasteboardQueue.Offset(result.inQueue - 1, -1)
                    } else {
                        PasteboardQueue.Offset(result.inQueue, result.inItem - 1)
                    }
            }
        }
    }

    /* 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()