Как сохранить согласованность результатов поиска в схеме качания netbeans (древовидной таблице) после ее сортировки? - PullRequest
0 голосов
/ 01 февраля 2019

Поиск, который я пытаюсь реализовать, представляет собой поиск в стиле google-chrome, в котором все совпадения выделены желтым, а текущее совпадение выделено оранжевым, и можно перемещаться вперед с помощью Enter или назад с помощью Shift + Enter.Используемый мной компонент Swing - это Swing Outline Netbeans, который в основном выглядит как JTree Table.Он работает, пока контур не будет отсортирован, нажав на любой из его столбцов.В этом примере есть только один столбец.

Чтобы этот пример работал, нам понадобится контурный файл из дистрибутива Netbeans, который находится здесь: netbeans \ platform \ modules \ org-netbeans-swing-outline.jar.Другой вариант - поместить этот файл сборки Gradle (build.gradle) в корень проекта Gradle:

plugins {
    id 'java-library'
}

dependencies {
    compile 'uk.gov.nationalarchives.thirdparty.netbeans:org-netbeans-swing-outline:7.2'
}

repositories {
    mavenCentral()
}

В древовидной модели есть функция поиска findNodesWithPattern, которая помещает поиск в список:

import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.regex.Pattern;

import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.TreeNode;

public class TestTreeModel extends DefaultTreeModel {

    private static final long serialVersionUID = 1L;

    public TestTreeModel() {
        super(buildModel());
    }

    private static TreeNode buildModel() {
        DefaultMutableTreeNode root = new DefaultMutableTreeNode("");
        for (int i = 0; i < 3; i++) {
            DefaultMutableTreeNode node = new DefaultMutableTreeNode("test" + i);
            for (int j = 0; j < 3; j++) {
                node.add(new DefaultMutableTreeNode("subtest" + j));
            }
            root.add(node);
        }
        return root;
    }

    @Override
    public DefaultMutableTreeNode getRoot() {
        return (DefaultMutableTreeNode) super.getRoot();
    }

    public List<TreeNode> findNodesMatchingPattern(Pattern pattern) {
        List<TreeNode> matchingNodes = new ArrayList<>();
        findNodesWithpattern(matchingNodes, getRoot(), pattern);
        return matchingNodes;
    }

    private void findNodesWithpattern(List<TreeNode> matchingNodes, TreeNode node, Pattern pattern) {
        if (pattern.matcher(node.toString()).find()) {
            matchingNodes.add(node);
        }
        Enumeration<DefaultMutableTreeNode> children = node.children();
        while (children.hasMoreElements()) {
            findNodesWithpattern(matchingNodes, children.nextElement(), pattern);
        }
    }

}

Существует также модель строки, которая определяет один столбец с именем Value с хэш-кодом объекта (только для демонстрационных целей)

import org.netbeans.swing.outline.RowModel;

public class TestRowModel implements RowModel {
    @Override
    public Class getColumnClass(int column) {
        return Integer.class;
    }

    @Override
    public int getColumnCount() {
        return 1;
    }

    @Override
    public String getColumnName(int column) {
        return "Value";
    }

    @Override
    public Object getValueFor(Object node, int column) {
        return node.hashCode();
    }

    @Override
    public boolean isCellEditable(Object node, int column) {
        return false;
    }

    @Override
    public void setValueFor(Object node, int column, Object value) {
        // do nothing for now
    }
}

Визуализация, отвечающая за выделение желтым цветом.или оранжевый:

import java.awt.Color;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;

import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.TreeNode;

import org.netbeans.swing.outline.RenderDataProvider;

public class TestRenderData implements RenderDataProvider {

    private String searchPattern;
    private TreeNode currentMatch;

    @Override
    public java.awt.Color getBackground(Object o) {
        return null;
    }

    @Override
    public String getDisplayName(Object o) {
        DefaultMutableTreeNode node = (DefaultMutableTreeNode) o;
        if (searchPattern == null || searchPattern.length() == 0) {
            return node.toString();
        }
        try {
            Pattern pattern = Pattern.compile("(" + searchPattern + ")");
            Matcher matcher = pattern.matcher(node.toString());
            Color bgColor = Color.YELLOW;
            if (matcher.find()) {
                if (node == currentMatch) {
                    bgColor = Color.ORANGE;
                }
            }
            String bgHexColor = Integer.toHexString(bgColor.getRGB() & 0xffffff);
            String replacement = matcher.replaceAll("<span style=\"background-color: #" + bgHexColor + "\">$1</span>");
            return "<html>" + replacement + "</html>";
        } catch (PatternSyntaxException e) {
            return node.toString();
        }
    }

    @Override
    public java.awt.Color getForeground(Object o) {
        return null;
    }

    @Override
    public javax.swing.Icon getIcon(Object o) {
        return null;
    }

    @Override
    public String getTooltipText(Object o) {
        return null;
    }

    @Override
    public boolean isHtmlDisplayName(Object o) {
        return false;
    }

    public void setSearchPattern(String searchPattern) {
        this.searchPattern = searchPattern;
    }

    public void setCurrentMatch(TreeNode currentMatch) {
        this.currentMatch = currentMatch;
    }
}

Основной класс строит схему с помощью быстрого фильтра (несоответствующие узлы отфильтровываются) и ключевого прослушивателя, который запускает поиск.Когда вводится клавиша Enter, он увеличивает счетчик (nthMatch), и текущее совпадение с индексом nthMatch подсвечивается в Orange средством визуализации.

import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.util.Enumeration;
import java.util.List;
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;

import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JMenuBar;
import javax.swing.JScrollPane;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.TreeNode;
import javax.swing.tree.TreePath;

import org.netbeans.swing.etable.QuickFilter;
import org.netbeans.swing.outline.DefaultOutlineModel;
import org.netbeans.swing.outline.Outline;
import org.netbeans.swing.outline.OutlineModel;

public class MainFrame extends JFrame {
    private static final long serialVersionUID = 1L;

    public MainFrame() {
        TestTreeModel treeMdl = new TestTreeModel();
        OutlineModel mdl = DefaultOutlineModel.createOutlineModel(treeMdl, new TestRowModel(), true, "Test");
        Outline outline = new Outline();
        TestRenderData renderData = new TestRenderData();
        outline.setRenderDataProvider(renderData);
        outline.setRootVisible(false);
        outline.setModel(mdl);
        JScrollPane jScrollPane1 = new JScrollPane(outline);
        jScrollPane1.setViewportView(outline);
        JMenuBar mb = new JMenuBar();
        JLabel searchPatternLabel = new JLabel("  Search :  ");
        mb.add(searchPatternLabel);
        JTextField searchPatternTextField = new JTextField();
        searchPatternLabel.setLabelFor(searchPatternTextField);
        outline.setQuickFilter(0, new QuickFilter() {

            @Override
            public boolean accept(Object aValue) {
                if (searchPatternTextField.getText() == null || searchPatternTextField.getText().length() == 0) {
                    return true;
                }
                if (aValue instanceof DefaultMutableTreeNode) {
                    DefaultMutableTreeNode node = (DefaultMutableTreeNode) aValue;
                    Enumeration<DefaultMutableTreeNode> children = node.children();
                    while (children.hasMoreElements()) {
                        DefaultMutableTreeNode child = (DefaultMutableTreeNode) children.nextElement();
                        if (accept(child)) {
                            return true;
                        }
                    }
                    try {
                        Pattern searchPattern = Pattern.compile(searchPatternTextField.getText());
                        return searchPattern.matcher(node.toString()).find();
                    } catch (PatternSyntaxException ex) {
                        return true;
                    }
                }
                return false;
            }
        });
        mb.add(searchPatternTextField);
        setJMenuBar(mb);
        getContentPane().add(jScrollPane1);
        pack();
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setVisible(true);

        searchPatternTextField.addKeyListener(new KeyAdapter() {

            private int nthMatch = 0;

            @Override
            public void keyReleased(KeyEvent e) {
                if (e.getKeyCode() == KeyEvent.VK_ENTER) {
                    if (e.isShiftDown()) {
                        nthMatch--;
                    } else {
                        nthMatch++;
                    }
                }
                try {
                    Pattern searchPattern = Pattern.compile(searchPatternTextField.getText());
                    List<TreeNode> matchingNodes = treeMdl.findNodesMatchingPattern(searchPattern);
                    if (matchingNodes.size() > 0) {
                        if (nthMatch >= matchingNodes.size()) {
                            nthMatch = 0;
                        }
                        if (nthMatch < 0) {
                            nthMatch = 0;
                        }
                        TreeNode matchingNode = matchingNodes.get(nthMatch);
                        renderData.setCurrentMatch(matchingNode);
                        TreePath matchingNodePath = new TreePath(((DefaultMutableTreeNode) matchingNode).getPath());
                        outline.expandPath(matchingNodePath);
                        outline.scrollRectToVisible(outline.getPathBounds(matchingNodePath));
                        renderData.setSearchPattern(searchPatternTextField.getText());
                    }
                } catch (PatternSyntaxException ex) {

                }
                //outline.repaint();
                treeMdl.nodeChanged(treeMdl.getRoot());
            }

        });

    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {

            @Override
            public void run() {
                new MainFrame();
            }
        });

    }
}

В качестве базового сценария я неоднократно ищу "test"нажав Enter.Порядок результатов поиска портится при щелчке столбца для сортировки.В Outline есть 2 унаследованных метода convertRowIndexToModel и convertRowIndexToView, которые, похоже, выполняют отображение индексов, но я не знаю, как их использовать.

1 Ответ

0 голосов
/ 02 февраля 2019

Всякий раз, когда меняется поле поиска, просто сообщите рендереру новый шаблон поиска.Это позаботится о выделении желтого цвета при вводе текста:

searchPatternTextField.getDocument().addDocumentListener(new DocumentListener() {

    @Override
    public void removeUpdate(DocumentEvent e) {
        renderData.setSearchPattern(searchPatternTextField.getText());
        treeMdl.nodeChanged(treeMdl.getRoot());
    }

    @Override
    public void insertUpdate(DocumentEvent e) {
        renderData.setSearchPattern(searchPatternTextField.getText());
        treeMdl.nodeChanged(treeMdl.getRoot());
    }

    @Override
    public void changedUpdate(DocumentEvent e) {
        renderData.setSearchPattern(searchPatternTextField.getText());
        treeMdl.nodeChanged(treeMdl.getRoot());
    }
});

Затем прослушайте Enter и Shift + Enter.Просто найдите предыдущий, текущий и следующий соответствующий узел в представлении:

searchPatternTextField.addKeyListener(new KeyAdapter() {

    private void showMatchingNode(TreeNode node) {
        renderData.setCurrentMatch(node);
        TreePath matchingNodePath = new TreePath(((DefaultMutableTreeNode) node).getPath());
        outline.expandPath(matchingNodePath);
        outline.scrollRectToVisible(outline.getPathBounds(matchingNodePath));
    }

    public TreeNode findNextMatchingNode(boolean lookForPrevious) {
        try {
            Pattern searchPattern = Pattern.compile(searchPatternTextField.getText());
            boolean currentMatchFound = false;
            TreeNode previousMatch = null;
            for (int i = 0; i < outline.getRowCount(); i++) {
                TreeNode node = (TreeNode) outline.getValueAt(i, 0);
                if (node == renderData.getCurrentMatch()) {
                    currentMatchFound = true;
                    if (previousMatch != null && lookForPrevious) {
                        return previousMatch;
                    }
                } else if (searchPattern.matcher(node.toString()).find()) {
                    if (renderData.getCurrentMatch() == null || (currentMatchFound && !lookForPrevious)) {
                        return node;
                    }
                    previousMatch = node;
                }
            }
        } catch (PatternSyntaxException ex) {

        }
        return null;
    }

    @Override
    public void keyReleased(KeyEvent e) {
        if (e.getKeyCode() == KeyEvent.VK_ENTER) {
            TreeNode node = findNextMatchingNode(e.isShiftDown());
            if (node != null) {
                showMatchingNode(node);
            }
            treeMdl.nodeChanged(treeMdl.getRoot());
        }
    }

});
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...