All Downloads are FREE. Search and download functionalities are using the official Maven repository.

xdev.ui.XdevQuickTreeFilterField Maven / Gradle / Ivy

There is a newer version: 6.0.2
Show newest version
package xdev.ui;

/*-
 * #%L
 * XDEV Component Suite
 * %%
 * Copyright (C) 2011 - 2021 XDEV Software
 * %%
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Lesser Public License for more details.
 * 
 * You should have received a copy of the GNU General Lesser Public
 * License along with this program.  If not, see
 * .
 * #L%
 */


import java.awt.Color;
import java.awt.Font;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.Enumeration;
import java.util.List;

import javax.swing.Icon;
import javax.swing.JTextField;
import javax.swing.JTree;
import javax.swing.event.CaretListener;
import javax.swing.event.DocumentListener;
import javax.swing.text.JTextComponent;
import javax.swing.tree.TreeModel;
import javax.swing.tree.TreeNode;
import javax.swing.tree.TreePath;

import com.jidesoft.combobox.QuickComboBoxFilterField;
import com.jidesoft.swing.JidePopupMenu;
import com.jidesoft.tree.QuickTreeFilterField;

import xdev.ui.persistence.Persistable;
import xdev.ui.quickfilter.QuickFilterSearchMode;
import xdev.ui.quickfilter.QuickFilterSearchOption;
import xdev.ui.tree.XdevTreeNode;
import xdev.util.XdevList;


/**
 * {@code XdevQuickTreeFilterField} can be used to dynamically filter the rows
 * of a table.
 * 

* It takes any {@link TreeModel} as input. *

* * @author XDEV Software * @see QuickComboBoxFilterField */ public class XdevQuickTreeFilterField extends QuickTreeFilterField implements XdevFocusCycleComponent, Persistable, TextComponentOwner { /** * serialVersionUID. */ private static final long serialVersionUID = 1L; /** * Should the gui state be persisted? Defaults to {@code true}. */ private boolean persistenceEnabled = true; /** * Flag for disabling the context menu. */ private boolean contextMenuDisabled = false; /** * Flag for dynamic tree expansion for filtered leaf nodes. */ private boolean expandFilteredLeafNodes = false; /** * @see QuickTreeFilterField#QuickTreeFilterField() */ public XdevQuickTreeFilterField() { super(); initDefaults(); } /** * @param treeModel * the TreeModel * @see QuickTreeFilterField#QuickTreeFilterField(TreeModel) */ public XdevQuickTreeFilterField(TreeModel treeModel) { super(treeModel); initDefaults(); } /** * Initializes the default values for search mode (wildcard) and search * option (match anywhere). */ protected void initDefaults() { setSearchMode(QuickFilterSearchMode.WILDCARD); setSearchOption(QuickFilterSearchOption.MATCH_ANYWHERE); } /** * Connects the filter field with a tree, meaning it always uses the tree's * model. * * @param tree * the tree to connect with. */ public void setFilterFor(final JTree tree) { setTree(tree); setTreeModel(tree.getModel()); tree.setModel(getDisplayTreeModel()); } private PropertyChangeListener treeModelChangeListener; /** * {@inheritDoc} */ @Override public void setTree(JTree newTree) { JTree oldTable = getTree(); if(oldTable != newTree) { if(treeModelChangeListener == null) { treeModelChangeListener = new PropertyChangeListener() { boolean fireChanged = true; @Override public void propertyChange(PropertyChangeEvent evt) { if(fireChanged) { try { fireChanged = false; JTree tree = (JTree)evt.getSource(); if(tree.getModel() != getDisplayTreeModel()) { setTreeModel(tree.getModel()); tree.setModel(getDisplayTreeModel()); } } finally { fireChanged = true; } } } }; } if(oldTable != null) { oldTable.removePropertyChangeListener("model",treeModelChangeListener); } super.setTree(newTree); newTree.addPropertyChangeListener("model",treeModelChangeListener); } } /** * Updates the {@code XdevQuickFilterPane} instance. *

* Should be called, if the default constructor was used to create the * instance. *

* * @param treeModel * the model object of the tree to filter */ public void setModel(TreeModel treeModel) { this.setTreeModel(treeModel); } // // XdevFocusCycleComponent impl - start // /** * the initial tabIndex. */ private int tabIndex = -1; /** * {@inheritDoc} */ @Override public int getTabIndex() { return tabIndex; } /** * {@inheritDoc} */ @Override public void setTabIndex(int tabIndex) { if(this.tabIndex != tabIndex) { int oldValue = this.tabIndex; this.tabIndex = tabIndex; firePropertyChange(TAB_INDEX_PROPERTY,oldValue,tabIndex); } } // // XdevFocusCycleComponent impl - end // /** * {@inheritDoc} * * @see #savePersistentState() */ @Override public void loadPersistentState(String persistentState) { String[] persistentValues = persistentState.split(Persistable.VALUE_SEPARATOR); int index = 0; try { this.setCaseSensitive(Boolean.parseBoolean(persistentValues[index++])); this.setRegexEnabled(Boolean.parseBoolean(persistentValues[index++])); this.setWildcardEnabled(Boolean.parseBoolean(persistentValues[index++])); this.setFromStart(Boolean.parseBoolean(persistentValues[index++])); this.setFromEnd(Boolean.parseBoolean(persistentValues[index++])); // tree specific flags this.setHideEmptyParentNode(Boolean.parseBoolean(persistentValues[index++])); this.setKeepAllChildren(Boolean.parseBoolean(persistentValues[index++])); this.setMatchesLeafNodeOnly(Boolean.parseBoolean(persistentValues[index++])); } catch(Exception e) { // do nothing her, if persistent value can't be retrieved... } } /** * {@inheritDoc}. *

* Persisted properties: *

    *
  • Case Sensitivity
  • *
  • FilterMode (Regex or Wildcard used?)
  • *
  • MatchMode (exact, from start, any)
  • *
*

*/ @Override public String savePersistentState() { StringBuilder persistentState = new StringBuilder(); persistentState.append(Boolean.toString(this.isCaseSensitive())); persistentState.append(Persistable.VALUE_SEPARATOR); persistentState.append(Boolean.toString(this.isRegexEnabled())); persistentState.append(Persistable.VALUE_SEPARATOR); persistentState.append(Boolean.toString(this.isWildcardEnabled())); persistentState.append(Persistable.VALUE_SEPARATOR); persistentState.append(Boolean.toString(this.isFromStart())); persistentState.append(Persistable.VALUE_SEPARATOR); persistentState.append(Boolean.toString(this.isFromEnd())); // tree specific flags persistentState.append(Persistable.VALUE_SEPARATOR); persistentState.append(Boolean.toString(this.isHideEmptyParentNode())); persistentState.append(Persistable.VALUE_SEPARATOR); persistentState.append(Boolean.toString(this.isKeepAllChildren())); persistentState.append(Persistable.VALUE_SEPARATOR); persistentState.append(Boolean.toString(this.isMatchesLeafNodeOnly())); return persistentState.toString(); } /** * {@inheritDoc} */ @Override public String getPersistentId() { return (this.getName() != null) ? this.getName() : this.getClass().getSimpleName(); } /** * {@inheritDoc} */ @Override public boolean isPersistenceEnabled() { return this.persistenceEnabled; } /** * Enables / Disables the persisting of the GuiState for this component. * * @param persistenceEnabled * if {@code true} gui persistence is enabled. */ public void setPersistenceEnabled(boolean persistenceEnabled) { this.persistenceEnabled = persistenceEnabled; } /** * {@inheritDoc}. * *

* To set the filter icon you may use {@link #setFilterIcon(Icon)} *

* * @see #setFilterIcon(Icon) */ @Override public void setIcon(Icon icon) { super.setIcon(icon); } /** * Sets the {@link QuickFilterSearchMode} for this QuickFilterField. * * @param searchMode * {@link QuickFilterSearchMode} to used by this QuickFilterField * * @see QuickFilterSearchMode */ public void setSearchMode(final QuickFilterSearchMode searchMode) { QuickFilterSearchMode.setSearchMode(this,searchMode); } /** * Returns the {@link QuickFilterSearchMode} of this QuickFilterField. * * @return the {@link QuickFilterSearchMode} of this QuickFilterField. * @since 1.1 */ public QuickFilterSearchMode getSearchMode() { return QuickFilterSearchMode.getSearchMode(this); } /** * Returns the {@link QuickFilterSearchOption} of this QuickFilterField. * * @return the {@link QuickFilterSearchOption} of this QuickFilterField. * @since 1.1 */ public QuickFilterSearchOption getSearchOption() { return QuickFilterSearchOption.getSearchOption(this); } /** * Sets the {@link QuickFilterSearchOption} for this QuickFilterField. * * @param searchOption * {@link QuickFilterSearchOption} to used by this * QuickFilterField * * @see QuickFilterSearchOption */ public void setSearchOption(final QuickFilterSearchOption searchOption) { QuickFilterSearchOption.setSearchOption(this,searchOption); } /** * Overriding this method here to make the display of the context menu * configurable. * * @return a {@link JidePopupMenu} containing the context menu. */ @Override protected JidePopupMenu createContextMenu() { JidePopupMenu popupMenu = super.createContextMenu(); if(isContextMenuDisabled()) { popupMenu.removeAll(); } return popupMenu; } /** * Is the context menu currently disabled? * * @return {@code true}, if context menu is disabled */ public boolean isContextMenuDisabled() { return contextMenuDisabled; } /** * En- or disables the context menu (defaults to enabled). * * @param contextMenuDisabled * if set to {@code true}, context menu gets disabled */ public void setContextMenuDisabled(boolean contextMenuDisabled) { this.contextMenuDisabled = contextMenuDisabled; } /** * {@inheritDoc} */ @Override public JTextComponent getTextComponent() { return this.getTextField(); } /** * Registers a {@link DocumentListener} on the contained {@link JTextField}. * * @param documentListener * a {@link DocumentListener} to register */ public void addDocumentListener(DocumentListener documentListener) { this.getTextField().getDocument().addDocumentListener(documentListener); } /** * Deregisters a {@link DocumentListener} on the contained * {@link JTextField}. * * @param documentListener * a {@link DocumentListener} to deregister */ public void removeDocumentListener(DocumentListener documentListener) { this.getTextField().getDocument().removeDocumentListener(documentListener); } /** * Registers a {@link CaretListener} on the contained {@link JTextField}. * * @param caretListener * a {@link CaretListener} to register */ public void addCaretListener(CaretListener caretListener) { this.getTextField().addCaretListener(caretListener); } /** * Deregisters a {@link CaretListener} on the contained {@link JTextField}. * * @param caretListener * a {@link CaretListener} to deregister */ public void removeCaretListener(CaretListener caretListener) { this.getTextField().removeCaretListener(caretListener); } /** * {@inheritDoc} */ @Override public void applyFilter(String filterText) { super.applyFilter(filterText); /* * #12407 - if expandFilteredLeafNodes is true, the tree will * automatically be expanded on to the leaf node(s), matching the * current filter condition. (works only for instances of XdevTree) */ if(isExpandFilteredLeafNodes()) { if(this.getTree() instanceof XdevTree) { XdevTree xdevTree = (XdevTree)this.getTree(); xdevTree.collapseAll(); List searchNodes = searchNodesMatchingFilter(filterText); if(searchNodes != null) { for(XdevTreeNode xdevTreeNode : searchNodes) { XdevTreeNode parentOfSearchNode = (XdevTreeNode)xdevTreeNode.getParent(); if(parentOfSearchNode != null) { TreePath pathToExpand = new TreePath(parentOfSearchNode.getPath()); xdevTree.expandPath(pathToExpand); } } } } } } /** * Returns a list of XdevTreeNode instances, which captions match the * current filter condition. current filter condition * * @param filterText * String containing the current filter text * @return a list of XdevTreeNode instances */ @SuppressWarnings("unchecked") private List searchNodesMatchingFilter(String filterText) { // (19.10.2020 TB)FIXME: cast to xdevtreenode? List list = null; final XdevTree xdevTree = (XdevTree)this.getTree(); final XdevTreeNode rootNode = xdevTree.getRoot(); final Enumeration e = rootNode.breadthFirstEnumeration(); while(e.hasMoreElements()) { final TreeNode node = e.nextElement(); if(filterText == null || filterText.isEmpty() ? false : !this.getFilter() .isValueFiltered(filterText)) { if(list == null) { list = new XdevList<>(); } list.add((XdevTreeNode)node); } } return list; } /** * Is tree currently expanded on filtered leaf nodes automatically? * * @return {@code true} if expanded automatically */ public boolean isExpandFilteredLeafNodes() { return expandFilteredLeafNodes; } /** * En- or disables the automatic expansion on filtered leaf nodes. Defaults * to {@code false}. *

* May have an negative effect on the performance when enabled, as the * expansion logic is triggered on every single key press. *

* to {@code false} * * @param expandFilteredLeafNodes * if {@code true} expands on filtered leaf nodes */ public void setExpandFilteredLeafNodes(boolean expandFilteredLeafNodes) { this.expandFilteredLeafNodes = expandFilteredLeafNodes; } // //////////////////////////////////////////////////////////// // / Following properties are now shown on development time/// // /////////////////////////////////////////////////////////// /** * {@inheritDoc} */ @BeanProperty(category = DefaultBeanCategories.MISC) @Override public void setFont(Font paramFont) { super.setFont(paramFont); } /** * {@inheritDoc} */ @BeanProperty(category = DefaultBeanCategories.MISC) @Override public void setForeground(Color paramColor) { super.setForeground(paramColor); } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy