diff --git a/openide.explorer/apichanges.xml b/openide.explorer/apichanges.xml
--- a/openide.explorer/apichanges.xml
+++ b/openide.explorer/apichanges.xml
@@ -50,6 +50,20 @@
Explorer API
+
+
+ QuickSearch attached to OutlineView
+
+
+
+
+
+ Added OutlineView.getQuickSearch()
+ method to get and control quick search functionality on OutlineView.
+
+
+
+
API method for creating a PropertyEnv instance
diff --git a/openide.explorer/manifest.mf b/openide.explorer/manifest.mf
--- a/openide.explorer/manifest.mf
+++ b/openide.explorer/manifest.mf
@@ -2,5 +2,5 @@
OpenIDE-Module: org.openide.explorer
OpenIDE-Module-Localizing-Bundle: org/openide/explorer/Bundle.properties
AutoUpdate-Essential-Module: true
-OpenIDE-Module-Specification-Version: 6.42
+OpenIDE-Module-Specification-Version: 6.43
diff --git a/openide.explorer/nbproject/project.xml b/openide.explorer/nbproject/project.xml
--- a/openide.explorer/nbproject/project.xml
+++ b/openide.explorer/nbproject/project.xml
@@ -70,7 +70,7 @@
- 6.2
+ 7.43
diff --git a/openide.explorer/src/org/openide/explorer/view/OutlineView.java b/openide.explorer/src/org/openide/explorer/view/OutlineView.java
--- a/openide.explorer/src/org/openide/explorer/view/OutlineView.java
+++ b/openide.explorer/src/org/openide/explorer/view/OutlineView.java
@@ -132,6 +132,7 @@
import org.netbeans.swing.outline.TreePathSupport;
import org.openide.awt.Mnemonics;
import org.openide.awt.MouseUtils;
+import org.openide.awt.QuickSearch;
import org.openide.explorer.ExplorerManager;
import org.openide.explorer.ExplorerUtils;
import org.openide.explorer.propertysheet.PropertyPanel;
@@ -681,24 +682,14 @@
}
/**
- * Test whether the quick search feature is enabled or not.
- * Default is enabled (true).
- * @since
- * @return true
if quick search feature is enabled, false
otherwise.
+ * Get the quick search support, that is attached to this view.
+ * @return The quick search support.
+ * @since 6.43
*/
- /*public*/ boolean isQuickSearchAllowed() {
- return quickSearch.isEnabled();
+ public final QuickSearch getQuickSearch() {
+ return quickSearch;
}
- /**
- * Set whether the quick search feature is enabled or not.
- * @since
- * @param allowedQuickSearch true
if quick search shall be enabled
- */
- /*public*/ void setQuickSearchAllowed(boolean allowedQuickSearch) {
- quickSearch.setEnabled(allowedQuickSearch);
- }
-
/** Initializes the component and lookup explorer manager.
*/
@Override
diff --git a/openide.explorer/src/org/openide/explorer/view/QuickSearch.java b/openide.explorer/src/org/openide/explorer/view/QuickSearch.java
deleted file mode 100644
--- a/openide.explorer/src/org/openide/explorer/view/QuickSearch.java
+++ /dev/null
@@ -1,514 +0,0 @@
-/*
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
- *
- * Copyright 2012 Oracle and/or its affiliates. All rights reserved.
- *
- * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
- * Other names may be trademarks of their respective owners.
- *
- * The contents of this file are subject to the terms of either the GNU
- * General Public License Version 2 only ("GPL") or the Common
- * Development and Distribution License("CDDL") (collectively, the
- * "License"). You may not use this file except in compliance with the
- * License. You can obtain a copy of the License at
- * http://www.netbeans.org/cddl-gplv2.html
- * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
- * specific language governing permissions and limitations under the
- * License. When distributing the software, include this License Header
- * Notice in each file and include the License file at
- * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this
- * particular file as subject to the "Classpath" exception as provided
- * by Oracle in the GPL Version 2 section of the License file that
- * accompanied this code. If applicable, add the following below the
- * License Header, with the fields enclosed by brackets [] replaced by
- * your own identifying information:
- * "Portions Copyrighted [year] [name of copyright owner]"
- *
- * If you wish your version of this file to be governed by only the CDDL
- * or only the GPL Version 2, indicate your decision by adding
- * "[Contributor] elects to include this software in this distribution
- * under the [CDDL or GPL Version 2] license." If you do not indicate a
- * single choice of license, a recipient has the option to distribute
- * your version of this file under either the CDDL, the GPL Version 2 or
- * to extend the choice of license to its licensees as provided above.
- * However, if you add GPL Version 2 code and therefore, elected the GPL
- * Version 2 license, then the option applies only if the new code is
- * made subject to such option by the copyright holder.
- *
- * Contributor(s):
- *
- * Portions Copyrighted 2012 Sun Microsystems, Inc.
- */
-package org.openide.explorer.view;
-
-import java.awt.*;
-import java.awt.event.*;
-import java.lang.ref.WeakReference;
-import java.util.LinkedList;
-import java.util.List;
-import javax.swing.*;
-import javax.swing.event.DocumentEvent;
-import javax.swing.event.DocumentListener;
-import javax.swing.text.Position.Bias;
-
-/**
- * Quick search infrastructure
- *
- * @author Martin Entlicher
- */
-class QuickSearch {
-
- private static final String ICON_FIND = "org/openide/explorer/view/find.png";
- private static final String ICON_FIND_WITH_MENU = "org/openide/explorer/view/findMenu.png";
-
- private final JComponent component;
- private final Object constraints;
- private boolean enabled = true;
- private final List listeners = new LinkedList();
- private SearchTextField searchTextField;
- private KeyAdapter quickSearchKeyAdapter;
- private JPanel searchPanel;
- private JMenu popupMenu;
-
- private QuickSearch(JComponent component, Object constraints) {
- this.component = component;
- this.constraints = constraints;
- setUpSearch();
- }
-
- public static QuickSearch attach(JComponent component, Object constraints) {
- Object qso = component.getClientProperty(QuickSearch.class.getName());
- if (qso instanceof QuickSearch) {
- return (QuickSearch) qso;
- } else {
- QuickSearch qs = new QuickSearch(component, constraints);
- component.putClientProperty(QuickSearch.class.getName(), qs);
- return qs;
- }
- }
-
- public void detach() {
- setEnabled(false);
- component.putClientProperty(QuickSearch.class.getName(), null);
- }
-
- public boolean isEnabled() {
- return enabled;
- }
-
- public void setEnabled(boolean enabled) {
- if (this.enabled == enabled) {
- return ;
- }
- this.enabled = enabled;
- if (enabled) {
- component.addKeyListener(quickSearchKeyAdapter);
- } else {
- component.removeKeyListener(quickSearchKeyAdapter);
- }
- }
-
- public void addQuickSearchListener(QuickSearchListener qsl) {
- synchronized (listeners) {
- listeners.add(qsl);
- }
- }
-
- public void removeQuickSearchListener(QuickSearchListener qsl) {
- synchronized (listeners) {
- listeners.remove(qsl);
- }
- }
-
- public void setPopupMenu(JMenu popupMenu) {
- this.popupMenu = popupMenu;
- }
-
- public void processKeyEvent(KeyEvent ke) {
- switch(ke.getID()) {
- case KeyEvent.KEY_PRESSED:
- quickSearchKeyAdapter.keyPressed(ke);
- break;
- case KeyEvent.KEY_RELEASED:
- quickSearchKeyAdapter.keyReleased(ke);
- break;
- case KeyEvent.KEY_TYPED:
- quickSearchKeyAdapter.keyTyped(ke);
- break;
- }
- }
-
- private QuickSearchListener[] getQuickSearchListeners() {
- QuickSearchListener[] qsls;
- synchronized (listeners) {
- qsls = listeners.toArray(new QuickSearchListener[] {});
- }
- return qsls;
- }
-
- private void fireQuickSearchUpdate(String searchText) {
- for (QuickSearchListener qsl : getQuickSearchListeners()) {
- qsl.quickSearchUpdate(searchText);
- }
- }
-
- private void fireShowNextSelection(Bias bias) {
- for (QuickSearchListener qsl : getQuickSearchListeners()) {
- qsl.showNextSelection(bias);
- }
- }
-
- private String findMaxPrefix(String prefix) {
- for (QuickSearchListener qsl : getQuickSearchListeners()) {
- prefix = qsl.findMaxPrefix(prefix);
- }
- return prefix;
- }
-
- private void fireQuickSearchConfirmed() {
- for (QuickSearchListener qsl : getQuickSearchListeners()) {
- qsl.quickSearchConfirmed();
- }
- }
-
- private void fireQuickSearchCanceled() {
- for (QuickSearchListener qsl : getQuickSearchListeners()) {
- qsl.quickSearchCanceled();
- }
- }
-
- private void setUpSearch() {
- searchTextField = new SearchTextField();
- // create new key listeners
- quickSearchKeyAdapter = (
- new KeyAdapter() {
- @Override
- public void keyTyped(KeyEvent e) {
- int modifiers = e.getModifiers();
- int keyCode = e.getKeyCode();
- char c = e.getKeyChar();
-
- //#43617 - don't eat + and -
- //#98634 - and all its duplicates dont't react to space
- if ((c == '+') || (c == '-') || (c==' ')) return; // NOI18N
-
- if (((modifiers > 0) && (modifiers != KeyEvent.SHIFT_MASK)) || e.isActionKey()) {
- return;
- }
-
- if (Character.isISOControl(c) ||
- (keyCode == KeyEvent.VK_SHIFT) ||
- (keyCode == KeyEvent.VK_ESCAPE)) return;
-
- final KeyStroke stroke = KeyStroke.getKeyStrokeForEvent(e);
- searchTextField.setText(String.valueOf(stroke.getKeyChar()));
-
- displaySearchField();
- e.consume();
- }
- }
- );
- if(isEnabled()){
- component.addKeyListener(quickSearchKeyAdapter);
- }
- // Create a the "multi-event" listener for the text field. Instead of
- // adding separate instances of each needed listener, we're using a
- // class which implements them all. This approach is used in order
- // to avoid the creation of 4 instances which takes some time
- SearchFieldListener searchFieldListener = new SearchFieldListener();
- searchTextField.addKeyListener(searchFieldListener);
- searchTextField.addFocusListener(searchFieldListener);
- searchTextField.getDocument().addDocumentListener(searchFieldListener);
-
- }
-
- private void displaySearchField() {
- if (searchPanel != null || !isEnabled()) {
- return;
- }
- /*
- TreeView previousSearchField = lastSearchField.get();
- if (previousSearchField != null && previousSearchField != this) {
- previousSearchField.removeSearchField();
- }
- */
- //JViewport vp = getViewport();
- //originalScrollMode = vp.getScrollMode();
- //vp.setScrollMode(JViewport.SIMPLE_SCROLL_MODE);
- searchTextField.setOriginalFocusOwner();
- searchTextField.setFont(component.getFont());
- searchPanel = new SearchPanel();
- //JLabel lbl = new JLabel(NbBundle.getMessage(TreeView.class, "LBL_QUICKSEARCH")); //NOI18N
- final JLabel lbl;
- if (popupMenu != null) {
- lbl = new JLabel(org.openide.util.ImageUtilities.loadImageIcon(ICON_FIND_WITH_MENU, false));
- lbl.addMouseListener(new MouseAdapter() {
- @Override
- public void mousePressed(MouseEvent e) {
- if (e != null && !SwingUtilities.isLeftMouseButton(e)) {
- return;
- }
- JPopupMenu pm = popupMenu.getPopupMenu();
- pm.show(lbl, 0, lbl.getHeight() - 1);
- }
- });
- } else {
- lbl = new JLabel(org.openide.util.ImageUtilities.loadImageIcon(ICON_FIND, false));
- }
- searchPanel.setLayout(new BoxLayout(searchPanel, BoxLayout.X_AXIS));
- searchPanel.add(lbl);
- searchPanel.add(searchTextField);
- lbl.setLabelFor(searchTextField);
- searchTextField.setColumns(10);
- searchTextField.setMaximumSize(searchTextField.getPreferredSize());
- searchTextField.putClientProperty("JTextField.variant", "search"); //NOI18N
- lbl.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 5));
- //JToggleButton matchCaseButton = new JToggleButton("aA");
- //matchCaseButton.setBorder(BorderFactory.createEmptyBorder(0, 5, 0, 5));
- //searchPanel.add(matchCaseButton);
- if (component instanceof JScrollPane) {
- // ((JScrollPane) component).getViewport().setScrollMode(JViewport.SIMPLE_SCROLL_MODE);
- }
- if (constraints == null) {
- component.add(searchPanel);
- } else {
- component.add(searchPanel, constraints);
- }
- component.invalidate();
- component.revalidate();
- component.repaint();
- searchTextField.requestFocus();
- }
-
- private void removeSearchField() {
- if (searchPanel == null) {
- return;
- }
- component.remove(searchPanel);
- searchPanel = null;
- //getViewport().setScrollMode(originalScrollMode);
- component.invalidate();
- component.revalidate();
- component.repaint();
- }
-
- public static String findMaxCommonSubstring(String str1, String str2, boolean ignoreCase) {
- int n1 = str1.length();
- int n2 = str2.length();
- int i = 0;
- if (ignoreCase) {
- for ( ; i < n1 && i < n2; i++) {
- char c1 = Character.toUpperCase(str1.charAt(i));
- char c2 = Character.toUpperCase(str2.charAt(i));
- if (c1 != c2) {
- break;
- }
- }
- } else {
- for ( ; i < n1 && i < n2; i++) {
- char c1 = str1.charAt(i);
- char c2 = str2.charAt(i);
- if (c1 != c2) {
- break;
- }
- }
- }
- return str1.substring(0, i);
- }
-
- public static interface QuickSearchListener {
-
- void quickSearchUpdate(String searchText);
-
- void showNextSelection(Bias bias);
-
- String findMaxPrefix(String prefix);
-
- void quickSearchConfirmed();
-
- void quickSearchCanceled();
-
- }
-
- private static class SearchPanel extends JPanel {
-
- public SearchPanel() {
- if (ViewUtil.isAquaLaF) {
- setBorder(BorderFactory.createEmptyBorder(9,6,8,2));
- } else {
- setBorder(BorderFactory.createEmptyBorder(2,6,2,2));
- }
- setOpaque(true);
- }
-
- @Override
- protected void paintComponent(Graphics g) {
- if (ViewUtil.isAquaLaF && g instanceof Graphics2D) {
- Graphics2D g2d = (Graphics2D) g;
- g2d.setPaint(new GradientPaint(0, 0, UIManager.getColor("NbExplorerView.quicksearch.background.top"),
- 0, getHeight(), UIManager.getColor("NbExplorerView.quicksearch.background.bottom")));//NOI18N
- g2d.fillRect(0, 0, getWidth(), getHeight());
- g2d.setColor(UIManager.getColor("NbExplorerView.quicksearch.border")); //NOI18N
- g2d.drawLine(0, 0, getWidth(), 0);
- } else {
- super.paintComponent(g);
- }
- }
- }
-
- /** searchTextField manages focus because it handles VK_ESCAPE key */
- private class SearchTextField extends JTextField {
-
- private WeakReference originalFocusOwner = new WeakReference(null);
-
- public SearchTextField() {
- }
-
- void setOriginalFocusOwner() {
- Component focusOwner = KeyboardFocusManager.getCurrentKeyboardFocusManager().getFocusOwner();
- if (focusOwner != null && component.isAncestorOf(focusOwner)) {
- originalFocusOwner = new WeakReference(focusOwner);
- } else {
- originalFocusOwner = new WeakReference(component);
- }
- }
-
- void requestOriginalFocusOwner() {
- SwingUtilities.invokeLater(
- new Runnable() {
- //additional bugfix - do focus change later or removing
- //the component while it's focused will cause focus to
- //get transferred to the next component in the
- //parent focusTraversalPolicy *after* our request
- //focus completes, so focus goes into a black hole - Tim
- @Override
- public void run() {
- Component fo = originalFocusOwner.get();
- if (fo != null) {
- fo.requestFocusInWindow();
- }
- }
- }
- );
- }
-
- @Override
- public boolean isManagingFocus() {
- return true;
- }
-
- @Override
- public void processKeyEvent(KeyEvent ke) {
- //override the default handling so that
- //the parent will never receive the escape key and
- //close a modal dialog
- if (ke.getKeyCode() == KeyEvent.VK_ESCAPE) {
- removeSearchField();
- ke.consume();
- // bugfix #32909, reqest focus when search field is removed
- requestOriginalFocusOwner();
- fireQuickSearchCanceled();
- } else {
- super.processKeyEvent(ke);
- }
- }
- };
-
- private class SearchFieldListener extends KeyAdapter implements DocumentListener, FocusListener {
-
- private boolean ignoreEvents;
-
- SearchFieldListener() {
- }
-
- @Override
- public void changedUpdate(DocumentEvent e) {
- if (ignoreEvents) return;
- searchForNode();
- }
-
- @Override
- public void insertUpdate(DocumentEvent e) {
- if (ignoreEvents) return;
- searchForNode();
- }
-
- @Override
- public void removeUpdate(DocumentEvent e) {
- if (ignoreEvents) return;
- searchForNode();
- }
-
- @Override
- public void keyPressed(KeyEvent e) {
- int keyCode = e.getKeyCode();
-
- if (keyCode == KeyEvent.VK_ESCAPE) {
- removeSearchField();
- searchTextField.requestOriginalFocusOwner();
- fireQuickSearchCanceled();
- e.consume();
- } else if (keyCode == KeyEvent.VK_UP || (keyCode == KeyEvent.VK_F3 && e.isShiftDown())) {
- fireShowNextSelection(Bias.Backward);
- // Stop processing the event here. Otherwise it's dispatched
- // to the tree too (which scrolls)
- e.consume();
- } else if (keyCode == KeyEvent.VK_DOWN || keyCode == KeyEvent.VK_F3) {
- fireShowNextSelection(Bias.Forward);
- // Stop processing the event here. Otherwise it's dispatched
- // to the tree too (which scrolls)
- e.consume();
- } else if (keyCode == KeyEvent.VK_TAB) {
- String maxPrefix = findMaxPrefix(searchTextField.getText());
- ignoreEvents = true;
- try {
- searchTextField.setText(maxPrefix);
- } finally {
- ignoreEvents = false;
- }
-
- e.consume();
- } else if (keyCode == KeyEvent.VK_ENTER) {
- removeSearchField();
- fireQuickSearchConfirmed();
-
- component.requestFocusInWindow();
- e.consume();
- }
- }
-
- /** Searches for a node in the tree. */
- private void searchForNode() {
- String text = searchTextField.getText();
- fireQuickSearchUpdate(text);
- }
-
- @Override
- public void focusGained(FocusEvent e) {
- if (e.getSource() == searchTextField) {
- // make sure nothing is selected
- int n = searchTextField.getText().length();
- searchTextField.select(n, n);
- }
- }
-
- @Override
- public void focusLost(FocusEvent e) {
- if (e.isTemporary()) return ;
- Component oppositeComponent = e.getOppositeComponent();
- if (e.getSource() != searchTextField) {
- ((Component) e.getSource()).removeFocusListener(this);
- }
- if (oppositeComponent instanceof JMenuItem || oppositeComponent instanceof JPopupMenu) {
- oppositeComponent.addFocusListener(this);
- return ;
- }
- if (oppositeComponent == searchTextField) {
- return ;
- }
- removeSearchField();
- fireQuickSearchConfirmed();
- }
- }
-
-}
diff --git a/openide.explorer/src/org/openide/explorer/view/TableQuickSearchSupport.java b/openide.explorer/src/org/openide/explorer/view/TableQuickSearchSupport.java
--- a/openide.explorer/src/org/openide/explorer/view/TableQuickSearchSupport.java
+++ b/openide.explorer/src/org/openide/explorer/view/TableQuickSearchSupport.java
@@ -61,7 +61,8 @@
import javax.swing.event.ChangeListener;
import javax.swing.table.TableColumn;
import javax.swing.table.TableColumnModel;
-import javax.swing.text.Position;
+import org.openide.awt.QuickSearch;
+import org.openide.awt.QuickSearchListener;
import org.openide.util.NbBundle;
import org.openide.util.NbPreferences;
@@ -70,7 +71,7 @@
*
* @author Martin Entlicher
*/
-class TableQuickSearchSupport implements QuickSearch.QuickSearchListener {
+class TableQuickSearchSupport implements QuickSearchListener {
private int quickSearchInitialRow = -1; // The search was initiated here
private int quickSearchInitialColumn = -1; // The search was initiated here
@@ -103,12 +104,12 @@
}
quickSearchLastRow = quickSearchInitialRow;
quickSearchLastColumn = quickSearchInitialColumn;
- doSearch(searchText, Position.Bias.Forward);
+ doSearch(searchText, true);
}
@Override
- public void showNextSelection(Position.Bias bias) {
- if (bias == Position.Bias.Forward) {
+ public void showNextSelection(boolean forward) {
+ if (forward) {
if (++quickSearchLastColumn >= table.getColumnCount()) {
quickSearchLastColumn = 0;
if (++quickSearchLastRow >= table.getRowCount()) {
@@ -116,7 +117,7 @@
}
}
}
- doSearch(lastSearchText, bias);
+ doSearch(lastSearchText, forward);
}
@Override
@@ -145,7 +146,7 @@
if (maxPrefix == null) {
maxPrefix = str;
} else {
- maxPrefix = QuickSearch.findMaxCommonSubstring(maxPrefix, str, !qss.isMatchCase());
+ maxPrefix = QuickSearch.findMaxPrefix(maxPrefix, str, !qss.isMatchCase());
}
}
}
@@ -170,21 +171,21 @@
quickSearchInitialColumn = -1;
}
- private void doSearch(String searchText, Position.Bias bias) {
+ private void doSearch(String searchText, boolean forward) {
if (!qss.isMatchCase()) {
searchText = searchText.toUpperCase();
}
int n = table.getRowCount();
- boolean backward = bias == Position.Bias.Backward;
+ //boolean backward = bias == Position.Bias.Backward;
int row1 = quickSearchLastRow;
int row2 = quickSearchLastRow + n;
boolean lineStartSearch = true;
Set columnsIgnoredToSearch = qss.getColumnsIgnoredToSearch();
do {
int col1 = quickSearchLastColumn;
- int col2 = (backward) ? 0 : table.getColumnCount();
- for (int row = (backward) ? (row2 - 1) : row1; (backward) ? row >= row1 : row < row2; row = (backward) ? --row : ++row) {
- for (int col = col1; (backward) ? col >= col2 : col < col2; col = (backward) ? --col : ++col) {
+ int col2 = (forward) ? table.getColumnCount() : 0;
+ for (int row = (forward) ? row1 : (row2 - 1); (forward) ? row < row2 : row >= row1; row = (forward) ? ++row : --row) {
+ for (int col = col1; (forward) ? col < col2 : col >= col2; col = (forward) ? ++col : --col) {
String cName = table.getColumnName(col);
if (columnsIgnoredToSearch.contains(cName)) {
continue;
@@ -208,7 +209,7 @@
}
}
}
- col1 = (backward) ? table.getColumnCount() - 1 : 0;
+ col1 = (forward) ? 0 : table.getColumnCount() - 1;
}
lineStartSearch = !lineStartSearch;
} while (!lineStartSearch);
@@ -234,7 +235,7 @@
return TableQuickSearchSupport.getSearchPopupMenu(qss, table.getColumnModel(), new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
- doSearch(lastSearchText, Position.Bias.Forward);
+ doSearch(lastSearchText, true);
}
});
}
diff --git a/openide.explorer/src/org/openide/explorer/view/TreeTableView.java b/openide.explorer/src/org/openide/explorer/view/TreeTableView.java
--- a/openide.explorer/src/org/openide/explorer/view/TreeTableView.java
+++ b/openide.explorer/src/org/openide/explorer/view/TreeTableView.java
@@ -77,6 +77,7 @@
import javax.swing.plaf.metal.MetalScrollBarUI;
import javax.swing.table.*;
import javax.swing.tree.*;
+import org.openide.awt.QuickSearch;
import org.openide.explorer.view.TreeView.PopupAdapter;
import org.openide.explorer.view.TreeView.PopupSupport;
import org.openide.explorer.view.TreeView.TreePropertyListener;
diff --git a/openide.explorer/src/org/openide/explorer/view/TreeView.java b/openide.explorer/src/org/openide/explorer/view/TreeView.java
--- a/openide.explorer/src/org/openide/explorer/view/TreeView.java
+++ b/openide.explorer/src/org/openide/explorer/view/TreeView.java
@@ -133,7 +133,6 @@
import javax.swing.event.TreeWillExpandListener;
import javax.swing.plaf.TreeUI;
import javax.swing.plaf.UIResource;
-import javax.swing.text.Position;
import javax.swing.tree.ExpandVetoException;
import javax.swing.tree.RowMapper;
import javax.swing.tree.TreeCellEditor;
@@ -141,6 +140,8 @@
import javax.swing.tree.TreeNode;
import javax.swing.tree.TreePath;
import javax.swing.tree.TreeSelectionModel;
+import org.openide.awt.QuickSearch;
+import org.openide.awt.QuickSearchListener;
/**
@@ -224,8 +225,7 @@
transient private int allowedDropActions = DnDConstants.ACTION_COPY_OR_MOVE | DnDConstants.ACTION_REFERENCE;
/** Quick Search support */
- transient private boolean allowedQuickSearch = true;
- transient private KeyAdapter quickSearchKeyAdapter;
+ transient private QuickSearch qs;
/** wait cursor is shown automatically during expanding */
transient private boolean autoWaitCursor = true;
@@ -454,7 +454,7 @@
* @return true if quick search feature is enabled, false otherwise.
*/
public boolean isQuickSearchAllowed() {
- return allowedQuickSearch;
+ return qs.isEnabled();
}
/**
@@ -463,15 +463,7 @@
* @param allowedQuickSearch true
if quick search shall be enabled
*/
public void setQuickSearchAllowed(boolean allowedQuickSearch) {
- this.allowedQuickSearch = allowedQuickSearch;
- if (quickSearchKeyAdapter != null && tree != null) {
- if (allowedQuickSearch) {
- tree.addKeyListener(quickSearchKeyAdapter);
- } else {
- removeSearchField();
- tree.removeKeyListener(quickSearchKeyAdapter);
- }
- }
+ qs.setEnabled(allowedQuickSearch);
}
@@ -1683,11 +1675,28 @@
}
@Override
+ public void add(Component comp, Object constraints) {
+ if (constraints == searchConstraints) {
+ searchPanel = comp;
+ constraints = null;
+ }
+ super.add(comp, constraints);
+ }
+
+ @Override
+ public void remove(Component comp) {
+ if (comp == searchPanel) {
+ searchPanel = null;
+ }
+ super.remove(comp);
+ }
+
+ @Override
public Insets getInsets() {
Insets res = getInnerInsets();
res = new Insets(res.top, res.left, res.bottom, res.right);
- if( null != searchpanel && searchpanel.isVisible() ) {
- res.bottom += searchpanel.getPreferredSize().height;
+ if( null != searchPanel && searchPanel.isVisible() ) {
+ res.bottom += searchPanel.getPreferredSize().height;
}
return res;
}
@@ -1701,130 +1710,12 @@
}
TreePath[] origSelectionPaths = null;
- JPanel searchpanel = null;
- // searchTextField manages focus because it handles VK_TAB key
- private JTextField searchTextField = new JTextField() {
- @Override
- public boolean isManagingFocus() {
- return true;
- }
-
- @Override
- public void processKeyEvent(KeyEvent ke) {
- //override the default handling so that
- //the parent will never receive the escape key and
- //close a modal dialog
- if (ke.getKeyCode() == KeyEvent.VK_ESCAPE) {
- removeSearchField();
- ke.consume();
-
- // bugfix #32909, reqest focus when search field is removed
- SwingUtilities.invokeLater(
- new Runnable() {
- //additional bugfix - do focus change later or removing
- //the component while it's focused will cause focus to
- //get transferred to the next component in the
- //parent focusTraversalPolicy *after* our request
- //focus completes, so focus goes into a black hole - Tim
- @Override
- public void run() {
- if( null != tree )
- tree.requestFocus();
- }
- }
- );
- } else {
- super.processKeyEvent(ke);
- }
- }
- };
- private int originalScrollMode;
-
- private static class SearchPanel extends JPanel {
- public SearchPanel() {
- if( ViewUtil.isAquaLaF )
- setBorder(BorderFactory.createEmptyBorder(9,6,8,2));
- else
- setBorder(BorderFactory.createEmptyBorder(2,6,2,2));
- setOpaque( true );
- }
-
- @Override
- protected void paintComponent(Graphics g) {
- if( ViewUtil.isAquaLaF && g instanceof Graphics2D ) {
- Graphics2D g2d = (Graphics2D) g;
- g2d.setPaint( new GradientPaint(0, 0, UIManager.getColor("NbExplorerView.quicksearch.background.top"),
- 0, getHeight(), UIManager.getColor("NbExplorerView.quicksearch.background.bottom")));//NOI18N
- g2d.fillRect(0, 0, getWidth(), getHeight());
- g2d.setColor( UIManager.getColor("NbExplorerView.quicksearch.border") ); //NOI18N
- g2d.drawLine(0, 0, getWidth(), 0);
- } else {
- super.paintComponent(g);
- }
- }
- }
-
-
- private void prepareSearchPanel() {
- if( searchpanel == null ) {
- searchpanel = new SearchPanel();
-
- JLabel lbl = new JLabel(NbBundle.getMessage(TreeView.class, "LBL_QUICKSEARCH")); //NOI18N
- searchpanel.setLayout(new BoxLayout(searchpanel, BoxLayout.X_AXIS));
- searchpanel.add(lbl);
- searchpanel.add(searchTextField);
- lbl.setLabelFor(searchTextField);
- searchTextField.setColumns(10);
- searchTextField.setMaximumSize(searchTextField.getPreferredSize());
- searchTextField.putClientProperty("JTextField.variant", "search"); //NOI18N
- lbl.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 5));
- }
- }
-
- /**
- * Adds the search field to the tree.
- */
- private void displaySearchField() {
- if( null != searchpanel || !isQuickSearchAllowed())
- return;
-
- TreeView previousSearchField = lastSearchField.get();
- if (previousSearchField != null && previousSearchField != this) {
- previousSearchField.removeSearchField();
- }
-
- JViewport vp = getViewport();
- originalScrollMode = vp.getScrollMode();
- vp.setScrollMode(JViewport.SIMPLE_SCROLL_MODE);
- searchTextField.setFont(tree.getFont());
- prepareSearchPanel();
- add(searchpanel);
- invalidate();
- revalidate();
- repaint();
- searchTextField.requestFocus();
-
- lastSearchField = new WeakReference(this);
- }
-
- /**
- * Removes the search field from the tree.
- */
- private void removeSearchField() {
- if( null == searchpanel )
- return;
-
- if (tree.getSelectionPaths() == null && origSelectionPaths != null) {
- tree.setSelectionPaths(origSelectionPaths);
- }
-
- remove(searchpanel);
- searchpanel = null;
- origSelectionPaths = null;
- getViewport().setScrollMode(originalScrollMode);
- invalidate();
- revalidate();
- repaint();
+ private Component searchPanel = null;
+ private final Object searchConstraints = new Object();
+
+ /** Called from tests */
+ Component getSearchPanel() {
+ return searchPanel;
}
private class ExplorerScrollPaneLayout extends ScrollPaneLayout {
@@ -1832,20 +1723,25 @@
@Override
public void layoutContainer( Container parent ) {
super.layoutContainer(parent);
- if( null != searchpanel && searchpanel.isVisible() ) {
+ if( null != searchPanel && searchPanel.isVisible() ) {
Insets innerInsets = getInnerInsets();
- Dimension prefSize = searchpanel.getPreferredSize();
- searchpanel.setBounds(innerInsets.left, parent.getHeight()-innerInsets.bottom-prefSize.height,
+ Dimension prefSize = searchPanel.getPreferredSize();
+ searchPanel.setBounds(innerInsets.left, parent.getHeight()-innerInsets.bottom-prefSize.height,
parent.getWidth()-innerInsets.left-innerInsets.right, prefSize.height);
}
}
}
- private final class ExplorerTree extends JTree implements Autoscroll {
+ private final class ExplorerTree extends JTree implements Autoscroll, QuickSearchListener {
AutoscrollSupport support;
private String maxPrefix;
int SEARCH_FIELD_SPACE = 3;
private boolean firstPaint = true;
+ /** The last search searchResults */
+ private List searchResults = new ArrayList();
+ /** The last selected index from the search searchResults. */
+ private int currentSelectionIndex;
+ private String lastSearchText;
ExplorerTree(TreeModel model) {
@@ -2008,6 +1904,14 @@
new GuardedActions(3, fe);
}
+ @Override
+ protected void processKeyEvent(KeyEvent e) {
+ qs.processKeyEvent(e);
+ if (!e.isConsumed()) {
+ super.processKeyEvent(e);
+ }
+ }
+
private void repaintSelection() {
int first = getSelectionModel().getMinSelectionRow();
int last = getSelectionModel().getMaxSelectionRow();
@@ -2039,58 +1943,66 @@
private void setupSearch() {
// Remove the default key listeners
- KeyListener[] keyListeners = getListeners(KeyListener.class);
-
- for (int i = 0; i < keyListeners.length; i++) {
- removeKeyListener(keyListeners[i]);
- }
-
- // create new key listeners
- quickSearchKeyAdapter = (
- new KeyAdapter() {
- @Override
- public void keyTyped(KeyEvent e) {
- int modifiers = e.getModifiers();
- int keyCode = e.getKeyCode();
- char c = e.getKeyChar();
-
- //#43617 - don't eat + and -
- //#98634 - and all its duplicates dont't react to space
- if ((c == '+') || (c == '-') || (c==' ')) return; // NOI18N
-
- if (((modifiers > 0) && (modifiers != KeyEvent.SHIFT_MASK)) || e.isActionKey()) {
- return;
- }
-
- if (Character.isISOControl(c) ||
- (keyCode == KeyEvent.VK_SHIFT) ||
- (keyCode == KeyEvent.VK_ESCAPE)) return;
-
- final KeyStroke stroke = KeyStroke.getKeyStrokeForEvent(e);
- origSelectionPaths = getSelectionPaths();
- if (origSelectionPaths != null && origSelectionPaths.length == 0) {
- origSelectionPaths = null;
- }
- searchTextField.setText(String.valueOf(stroke.getKeyChar()));
-
- displaySearchField();
- e.consume();
- }
- }
- );
- if(isQuickSearchAllowed()){
- addKeyListener(quickSearchKeyAdapter);
- }
- // Create a the "multi-event" listener for the text field. Instead of
- // adding separate instances of each needed listener, we're using a
- // class which implements them all. This approach is used in order
- // to avoid the creation of 4 instances which takes some time
- SearchFieldListener searchFieldListener = new SearchFieldListener();
- searchTextField.addKeyListener(searchFieldListener);
- searchTextField.addFocusListener(searchFieldListener);
- searchTextField.getDocument().addDocumentListener(searchFieldListener);
+// KeyListener[] keyListeners = getListeners(KeyListener.class);
+//
+// for (int i = 0; i < keyListeners.length; i++) {
+// removeKeyListener(keyListeners[i]);
+// }
+
+ qs = QuickSearch.attach(TreeView.this, searchConstraints);
+ qs.addQuickSearchListener(this);
}
+ @Override
+ public void quickSearchUpdate(String searchText) {
+ lastSearchText = searchText;
+ currentSelectionIndex = 0;
+ searchResults.clear();
+ maxPrefix = null;
+
+ String text = searchText.toUpperCase();
+
+ if (text.length() > 0) {
+ searchResults = doSearch(text);
+ }
+ displaySearchResult();
+ }
+
+ @Override
+ public void showNextSelection(boolean forward) {
+ if (forward) {
+ currentSelectionIndex++;
+ } else {
+ currentSelectionIndex--;
+ }
+ displaySearchResult();
+ }
+
+ @Override
+ public String findMaxPrefix(String prefix) {
+ return maxPrefix;
+ }
+
+ @Override
+ public void quickSearchConfirmed() {
+ TreePath selectedTPath = getSelectionPath();
+ if (selectedTPath != null) {
+ TreeNode selectedTNode = (TreeNode) selectedTPath.getLastPathComponent();
+ Node selectedNode = Visualizer.findNode(selectedTNode);
+ performPreferredActionOnNodes(new Node[] { selectedNode });
+ }
+ origSelectionPaths = null;
+ searchResults.clear();
+ lastSearchText = null;
+ }
+
+ @Override
+ public void quickSearchCanceled() {
+ origSelectionPaths = null;
+ searchResults.clear();
+ lastSearchText = null;
+ }
+
private List doSearch(String prefix) {
List results = new ArrayList();
Set resSet = new HashSet();
@@ -2109,7 +2021,7 @@
while (true) {
startIndex = startIndex % size;
- SubstringSearchResult substringSearchResult = getNextSubstringMatch(prefix, startIndex, Position.Bias.Forward);
+ SubstringSearchResult substringSearchResult = getNextSubstringMatch(prefix, startIndex, true);
TreePath path = substringSearchResult != null? substringSearchResult.treePath: null;
if ((path != null) && !resSet.contains(path)) {
@@ -2134,7 +2046,7 @@
maxPrefix = elementName;
}
- maxPrefix = findMaxPrefix(maxPrefix, elementName);
+ maxPrefix = QuickSearch.findMaxPrefix(maxPrefix, elementName, true);
}
// try next element
startIndex++;
@@ -2146,16 +2058,6 @@
return results;
}
- private String findMaxPrefix(String str1, String str2) {
- String res = null;
-
- for (int i = 0; str1.regionMatches(true, 0, str2, 0, i); i++) {
- res = str1.substring(0, i);
- }
-
- return res;
- }
-
/**
* Copied and adapted from JTree.getNextMatch(...).
*
@@ -2163,7 +2065,7 @@
* and the index of the first occurrence of the substring in TreePath.
*/
private SubstringSearchResult getNextSubstringMatch(
- String substring, int startingRow, Position.Bias bias) {
+ String substring, int startingRow, boolean forward) {
int max = getRowCount();
if (substring == null) {
@@ -2176,7 +2078,7 @@
// start search from the next/previous element froom the
// selected element
- int increment = (bias == Position.Bias.Forward) ? 1 : -1;
+ int increment = (forward) ? 1 : -1;
int row = startingRow;
do {
TreePath path = getPathForRow(row);
@@ -2193,6 +2095,29 @@
return null;
}
+ private void displaySearchResult() {
+ int sz = searchResults.size();
+
+ if (sz > 0) {
+ if (currentSelectionIndex < 0) {
+ currentSelectionIndex = sz - 1;
+ } else if (currentSelectionIndex >= sz) {
+ currentSelectionIndex = 0;
+ }
+
+ TreePath path = searchResults.get(currentSelectionIndex);
+ setSelectionPath(path);
+ scrollPathToVisible(path);
+ } else {
+ if (lastSearchText.isEmpty() && origSelectionPaths != null) {
+ setSelectionPaths(origSelectionPaths);
+ scrollPathToVisible(origSelectionPaths[0]);
+ } else {
+ clearSelection();
+ }
+ }
+ }
+
/** notify the Component to autoscroll */
@Override
public void autoscroll(Point cursorLoc) {
@@ -2295,131 +2220,6 @@
}
}
- private class SearchFieldListener extends KeyAdapter implements DocumentListener, FocusListener {
- /** The last search results */
- private List results = new ArrayList();
-
- /** The last selected index from the search results. */
- private int currentSelectionIndex;
-
- SearchFieldListener() {
- }
-
- @Override
- public void changedUpdate(DocumentEvent e) {
- searchForNode();
- }
-
- @Override
- public void insertUpdate(DocumentEvent e) {
- searchForNode();
- }
-
- @Override
- public void removeUpdate(DocumentEvent e) {
- searchForNode();
- }
-
- @Override
- public void keyPressed(KeyEvent e) {
- int keyCode = e.getKeyCode();
-
- if (keyCode == KeyEvent.VK_ESCAPE) {
- removeSearchField();
- ExplorerTree.this.requestFocus();
- } else if (keyCode == KeyEvent.VK_UP || (keyCode == KeyEvent.VK_F3 && e.isShiftDown())) {
- currentSelectionIndex--;
- displaySearchResult();
-
- // Stop processing the event here. Otherwise it's dispatched
- // to the tree too (which scrolls)
- e.consume();
- } else if (keyCode == KeyEvent.VK_DOWN || keyCode == KeyEvent.VK_F3) {
- currentSelectionIndex++;
- displaySearchResult();
-
- // Stop processing the event here. Otherwise it's dispatched
- // to the tree too (which scrolls)
- e.consume();
- } else if (keyCode == KeyEvent.VK_TAB) {
- if (maxPrefix != null) {
- searchTextField.setText(maxPrefix);
- }
-
- e.consume();
- } else if (keyCode == KeyEvent.VK_ENTER) {
- removeSearchField();
-
- // bugfix #39607, don't expand selected node when default action invoked
- TreePath selectedTPath = getSelectionPath();
-
- if (selectedTPath != null) {
- TreeNode selectedTNode = (TreeNode) selectedTPath.getLastPathComponent();
- Node selectedNode = Visualizer.findNode(selectedTNode);
-
- if (
- (selectedNode.getPreferredAction() == null) ||
- !selectedNode.getPreferredAction().isEnabled()
- ) {
- expandPath(getSelectionPath());
- }
- }
-
- ExplorerTree.this.requestFocus();
- ExplorerTree.this.dispatchEvent(e);
- }
- }
-
- /** Searches for a node in the tree. */
- private void searchForNode() {
- currentSelectionIndex = 0;
- results.clear();
- maxPrefix = null;
-
- String text = searchTextField.getText().toUpperCase();
-
- if (text.length() > 0) {
- results = doSearch(text);
- }
- displaySearchResult();
- }
-
- private void displaySearchResult() {
- int sz = results.size();
-
- if (sz > 0) {
- if (currentSelectionIndex < 0) {
- currentSelectionIndex = sz - 1;
- } else if (currentSelectionIndex >= sz) {
- currentSelectionIndex = 0;
- }
-
- TreePath path = results.get(currentSelectionIndex);
- setSelectionPath(path);
- scrollPathToVisible(path);
- } else {
- if (searchTextField.getText().length() == 0 && origSelectionPaths != null) {
- setSelectionPaths(origSelectionPaths);
- scrollPathToVisible(origSelectionPaths[0]);
- } else {
- clearSelection();
- }
- }
- }
-
- @Override
- public void focusGained(FocusEvent e) {
- // make sure nothing is selected
- searchTextField.select(1, 1);
- }
-
- @Override
- public void focusLost(FocusEvent e) {
- results.clear();
- removeSearchField();
- }
- }
-
private class AccessibleExplorerTree extends JTree.AccessibleJTree {
AccessibleExplorerTree() {
}
diff --git a/openide.explorer/test/unit/src/org/openide/explorer/view/TreeViewQuickSearchTest.java b/openide.explorer/test/unit/src/org/openide/explorer/view/TreeViewQuickSearchTest.java
--- a/openide.explorer/test/unit/src/org/openide/explorer/view/TreeViewQuickSearchTest.java
+++ b/openide.explorer/test/unit/src/org/openide/explorer/view/TreeViewQuickSearchTest.java
@@ -219,9 +219,9 @@
if (phase[0] != 0) {
if (btv.isQuickSearchAllowed()) {
- assertNotNull("Quick Search enabled ", btv.searchpanel);
+ assertNotNull("Quick Search enabled ", btv.getSearchPanel());
} else {
- assertNull("Quick Search disable", btv.searchpanel);
+ assertNull("Quick Search disable", btv.getSearchPanel());
}
}
}