diff -r 6bce66b50582 openide.explorer/apichanges.xml --- a/openide.explorer/apichanges.xml Tue Mar 06 10:49:56 2012 +0100 +++ b/openide.explorer/apichanges.xml Wed Mar 07 14:40:19 2012 +0100 @@ -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 -r 6bce66b50582 openide.explorer/manifest.mf --- a/openide.explorer/manifest.mf Tue Mar 06 10:49:56 2012 +0100 +++ b/openide.explorer/manifest.mf Wed Mar 07 14:40:19 2012 +0100 @@ -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 -r 6bce66b50582 openide.explorer/nbproject/project.xml --- a/openide.explorer/nbproject/project.xml Tue Mar 06 10:49:56 2012 +0100 +++ b/openide.explorer/nbproject/project.xml Wed Mar 07 14:40:19 2012 +0100 @@ -70,7 +70,7 @@ - 6.2 + 7.43 diff -r 6bce66b50582 openide.explorer/src/org/openide/explorer/view/OutlineView.java --- a/openide.explorer/src/org/openide/explorer/view/OutlineView.java Tue Mar 06 10:49:56 2012 +0100 +++ b/openide.explorer/src/org/openide/explorer/view/OutlineView.java Wed Mar 07 14:40:19 2012 +0100 @@ -88,6 +88,7 @@ import javax.swing.BorderFactory; import javax.swing.InputMap; import javax.swing.JComponent; +import javax.swing.JMenu; import javax.swing.JPopupMenu; import javax.swing.JScrollBar; import javax.swing.JScrollPane; @@ -132,6 +133,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; @@ -233,9 +235,8 @@ rowModel = new PropertiesRowModel(); model = createOutlineModel(treeModel, rowModel, nodesColumnLabel); outline = new OutlineViewOutline(model, rowModel); - quickSearch = QuickSearch.attach(this, searchConstraints); - TableQuickSearchSupport tqss = new TableQuickSearchSupport(outline, outline, outline.qss); - quickSearch.addQuickSearchListener(tqss); + TableQuickSearchSupport tqss = outline.createDefaultTableQuickSearchSupport(); + attachQuickSearch(tqss, false, tqss.createSearchPopupMenu()); outline.addKeyListener(new KeyListener() { @Override public void keyTyped(KeyEvent e) { @@ -250,7 +251,6 @@ quickSearch.processKeyEvent(e); } }); - quickSearch.setPopupMenu(tqss.createSearchPopupMenu()); rowModel.setOutline(outline); outline.setRenderDataProvider(new NodeRenderDataProvider(outline)); SheetCell tableCell = new SheetCell.OutlineSheetCell(outline); @@ -316,6 +316,13 @@ initializeTreeScrollSupport(); } + + private void attachQuickSearch(QuickSearch.Callback callback, boolean asynchronous, JMenu popup) { + if (quickSearch != null) { + quickSearch.detach(); + } + quickSearch = QuickSearch.attach(this, searchConstraints, callback, asynchronous, popup); + } @Override public void add(Component comp, Object constraints) { @@ -683,22 +690,33 @@ /** * 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. + * @since 6.43 + * @return true if quick search feature is enabled, false otherwise. */ - /*public*/ boolean isQuickSearchAllowed() { + public boolean isQuickSearchAllowed() { return quickSearch.isEnabled(); } /** * Set whether the quick search feature is enabled or not. - * @since + * @since 6.43 * @param allowedQuickSearch true if quick search shall be enabled */ - /*public*/ void setQuickSearchAllowed(boolean allowedQuickSearch) { + public void setQuickSearchAllowed(boolean allowedQuickSearch) { quickSearch.setEnabled(allowedQuickSearch); } + /** + * Set a quick search filter. + * @param quickSearchTableFilter The quick search filter + * @param asynchronous + * @since 6.43 + */ + public void setQuickSearchTableFilter(QuickSearchTableFilter quickSearchTableFilter, boolean asynchronous) { + TableQuickSearchSupport tqss = outline.createTableQuickSearchSupport(quickSearchTableFilter); + attachQuickSearch(tqss, asynchronous, tqss.createSearchPopupMenu()); + } + /** Initializes the component and lookup explorer manager. */ @Override @@ -1327,7 +1345,7 @@ * Extension of the ETable that allows adding a special comparator * for sorting the rows. */ - static class OutlineViewOutline extends Outline implements TableQuickSearchSupport.StringValuedTable { + static class OutlineViewOutline extends Outline { private final PropertiesRowModel rowModel; private static final String COLUMNS_SELECTOR_HINT = "ColumnsSelectorHint"; // NOI18N private static final String COPY_ACTION_DELEGATE = "Outline Copy Action Delegate "; // NOI18N @@ -1406,34 +1424,46 @@ } return null; } + + private TableQuickSearchSupport createDefaultTableQuickSearchSupport() { + return new TableQuickSearchSupport(this, new DefaultQuickSearchTableFilter(), qss); + } - @Override - public String getStringValueAt(int row, int col) { - Object value = transformValue(getValueAt(row, col)); - String str; - if (value instanceof Property) { - Property p = (Property) value; - Object v = null; - try { - v = p.getValue(); - } catch (IllegalAccessException ex) { - } catch (InvocationTargetException ex) { - } - if (v instanceof String) { - str = (String) v; + private TableQuickSearchSupport createTableQuickSearchSupport(QuickSearchTableFilter quickSearchTableFilter) { + return new TableQuickSearchSupport(this, quickSearchTableFilter, qss); + } + + private final class DefaultQuickSearchTableFilter implements QuickSearchTableFilter { + + @Override + public String getStringValueAt(int row, int col) { + Object value = transformValue(getValueAt(row, col)); + String str; + if (value instanceof Property) { + Property p = (Property) value; + Object v = null; + try { + v = p.getValue(); + } catch (IllegalAccessException ex) { + } catch (InvocationTargetException ex) { + } + if (v instanceof String) { + str = (String) v; + } else { + str = null; + } + } else if (value instanceof VisualizerNode) { + str = ((VisualizerNode) value).getDisplayName(); + } else if (value instanceof Node) { + str = ((Node) value).getDisplayName(); + } else if (value instanceof String) { + str = (String) value; } else { str = null; } - } else if (value instanceof VisualizerNode) { - str = ((VisualizerNode) value).getDisplayName(); - } else if (value instanceof Node) { - str = ((Node) value).getDisplayName(); - } else if (value instanceof String) { - str = (String) value; - } else { - str = null; + return str; } - return str; + } private class CopyToClipboardAction implements Action { diff -r 6bce66b50582 openide.explorer/src/org/openide/explorer/view/QuickSearchTableFilter.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/openide.explorer/src/org/openide/explorer/view/QuickSearchTableFilter.java Wed Mar 07 14:40:19 2012 +0100 @@ -0,0 +1,52 @@ +/* + * 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; + +/** + * + * @author Martin Entlicher + */ +public interface QuickSearchTableFilter { + + String getStringValueAt(int row, int col); + +} diff -r 6bce66b50582 openide.explorer/src/org/openide/explorer/view/TableQuickSearchSupport.java --- a/openide.explorer/src/org/openide/explorer/view/TableQuickSearchSupport.java Tue Mar 06 10:49:56 2012 +0100 +++ b/openide.explorer/src/org/openide/explorer/view/TableQuickSearchSupport.java Wed Mar 07 14:40:19 2012 +0100 @@ -61,7 +61,7 @@ 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.util.NbBundle; import org.openide.util.NbPreferences; @@ -70,7 +70,7 @@ * * @author Martin Entlicher */ -class TableQuickSearchSupport implements QuickSearch.QuickSearchListener { +class TableQuickSearchSupport implements QuickSearch.Callback { private int quickSearchInitialRow = -1; // The search was initiated here private int quickSearchInitialColumn = -1; // The search was initiated here @@ -79,15 +79,20 @@ private String lastSearchText; private JTable table; - private StringValuedTable svTable; + //private StringValuedTable svTable; + private QuickSearchTableFilter quickSearchTableFilter; private QuickSearchSettings qss; - TableQuickSearchSupport(JTable table, StringValuedTable svTable, QuickSearchSettings qss) { + TableQuickSearchSupport(JTable table, QuickSearchTableFilter quickSearchTableFilter, QuickSearchSettings qss) { this.table = table; - this.svTable = svTable; + this.quickSearchTableFilter = quickSearchTableFilter; this.qss = qss; } + public void setQuickSearchTableFilter(QuickSearchTableFilter quickSearchTableFilter, boolean asynchronous) { + this.quickSearchTableFilter = quickSearchTableFilter; + } + @Override public void quickSearchUpdate(String searchText) { lastSearchText = searchText; @@ -103,12 +108,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 +121,7 @@ } } } - doSearch(lastSearchText, bias); + doSearch(lastSearchText, forward); } @Override @@ -134,7 +139,7 @@ String maxPrefix = null; for (int row = row1; row < row2; row++) { for (int col = col1; col < col2; col++) { - String str = svTable.getStringValueAt(row, col); + String str = quickSearchTableFilter.getStringValueAt(row, col); String strUp; if (qss.isMatchCase()) { strUp = str; @@ -145,7 +150,7 @@ if (maxPrefix == null) { maxPrefix = str; } else { - maxPrefix = QuickSearch.findMaxCommonSubstring(maxPrefix, str, !qss.isMatchCase()); + maxPrefix = QuickSearch.findMaxPrefix(maxPrefix, str, !qss.isMatchCase()); } } } @@ -170,26 +175,26 @@ 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; } - String str = svTable.getStringValueAt(row % n, col); + String str = quickSearchTableFilter.getStringValueAt(row % n, col); if (str == null) { continue; } @@ -208,7 +213,7 @@ } } } - col1 = (backward) ? table.getColumnCount() - 1 : 0; + col1 = (forward) ? 0 : table.getColumnCount() - 1; } lineStartSearch = !lineStartSearch; } while (!lineStartSearch); @@ -234,7 +239,7 @@ return TableQuickSearchSupport.getSearchPopupMenu(qss, table.getColumnModel(), new ActionListener() { @Override public void actionPerformed(ActionEvent e) { - doSearch(lastSearchText, Position.Bias.Forward); + doSearch(lastSearchText, true); } }); } diff -r 6bce66b50582 openide.explorer/src/org/openide/explorer/view/TreeTable.java --- a/openide.explorer/src/org/openide/explorer/view/TreeTable.java Tue Mar 06 10:49:56 2012 +0100 +++ b/openide.explorer/src/org/openide/explorer/view/TreeTable.java Wed Mar 07 14:40:19 2012 +0100 @@ -87,7 +87,7 @@ * * @author Jan Rojcek */ -class TreeTable extends JTable implements Runnable, TableQuickSearchSupport.StringValuedTable { +class TreeTable extends JTable implements Runnable { /** Action key for up/down focus action */ private static final String ACTION_FOCUS_NEXT = "focusNext"; //NOI18N private static Color unfocusedSelBg = null; @@ -337,31 +337,41 @@ QuickSearchSettings getQuickSearchSettings() { return qss; } + + private QuickSearchTableFilter qstf = new DefaultQuickSearchTableFilter(); + + QuickSearchTableFilter getQuickSearchTableFilter() { + return qstf; + } - @Override - public String getStringValueAt(int row, int col) { - Object value = getValueAt(row, col); - String str; - if (value instanceof Property) { - Property p = (Property) value; - Object v = null; - try { - v = p.getValue(); - } catch (IllegalAccessException ex) { - } catch (InvocationTargetException ex) { - } - if (v instanceof String) { - str = (String) v; + private final class DefaultQuickSearchTableFilter implements QuickSearchTableFilter { + + @Override + public String getStringValueAt(int row, int col) { + Object value = getValueAt(row, col); + String str; + if (value instanceof Property) { + Property p = (Property) value; + Object v = null; + try { + v = p.getValue(); + } catch (IllegalAccessException ex) { + } catch (InvocationTargetException ex) { + } + if (v instanceof String) { + str = (String) v; + } else { + str = null; + } + } else if (value instanceof VisualizerNode) { + str = ((VisualizerNode) value).getDisplayName(); + //str = Visualizer.findNode(value).getDisplayName(); } else { str = null; } - } else if (value instanceof VisualizerNode) { - str = ((VisualizerNode) value).getDisplayName(); - //str = Visualizer.findNode(value).getDisplayName(); - } else { - str = null; + return str; } - return str; + } private class GuardedActions implements Mutex.Action { diff -r 6bce66b50582 openide.explorer/src/org/openide/explorer/view/TreeTableView.java --- a/openide.explorer/src/org/openide/explorer/view/TreeTableView.java Tue Mar 06 10:49:56 2012 +0100 +++ b/openide.explorer/src/org/openide/explorer/view/TreeTableView.java Wed Mar 07 14:40:19 2012 +0100 @@ -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; @@ -500,9 +501,8 @@ TreeTable tt = new TreeTable(treeModel, tableModel); treeTable = tt; tree = ((TreeTable) treeTable).getTree(); - quickSearch = QuickSearch.attach(this, searchConstraints); - TableQuickSearchSupport tqss = new TableQuickSearchSupport(tt, tt, tt.getQuickSearchSettings()); - quickSearch.addQuickSearchListener(tqss); + TableQuickSearchSupport tqss = new TableQuickSearchSupport(tt, tt.getQuickSearchTableFilter(), tt.getQuickSearchSettings()); + quickSearch = QuickSearch.attach(this, searchConstraints, tqss, tqss.createSearchPopupMenu()); tt.addKeyListener(new KeyListener() { @Override public void keyTyped(KeyEvent e) { @@ -517,7 +517,6 @@ quickSearch.processKeyEvent(e); } }); - quickSearch.setPopupMenu(tqss.createSearchPopupMenu()); defaultHeaderRenderer = treeTable.getTableHeader().getDefaultRenderer(); treeTable.getTableHeader().setDefaultRenderer(new SortingHeaderRenderer()); diff -r 6bce66b50582 openide.explorer/src/org/openide/explorer/view/TreeView.java --- a/openide.explorer/src/org/openide/explorer/view/TreeView.java Tue Mar 06 10:49:56 2012 +0100 +++ b/openide.explorer/src/org/openide/explorer/view/TreeView.java Wed Mar 07 14:40:19 2012 +0100 @@ -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,7 @@ import javax.swing.tree.TreeNode; import javax.swing.tree.TreePath; import javax.swing.tree.TreeSelectionModel; +import org.openide.awt.QuickSearch; /** @@ -224,8 +224,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 +453,7 @@ * @return true if quick search feature is enabled, false otherwise. */ public boolean isQuickSearchAllowed() { - return allowedQuickSearch; + return qs.isEnabled(); } /** @@ -463,15 +462,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 +1674,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 +1709,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 +1722,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, QuickSearch.Callback { 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 +1903,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 +1942,65 @@ private void setupSearch() { // Remove the default key listeners - KeyListener[] keyListeners = getListeners(KeyListener.class); +// KeyListener[] keyListeners = getListeners(KeyListener.class); +// +// for (int i = 0; i < keyListeners.length; i++) { +// removeKeyListener(keyListeners[i]); +// } + + qs = QuickSearch.attach(TreeView.this, searchConstraints, this); + } - for (int i = 0; i < keyListeners.length; i++) { - removeKeyListener(keyListeners[i]); + @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(); + } - // 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(); + @Override + public void showNextSelection(boolean forward) { + if (forward) { + currentSelectionIndex++; + } else { + currentSelectionIndex--; + } + displaySearchResult(); + } - //#43617 - don't eat + and - - //#98634 - and all its duplicates dont't react to space - if ((c == '+') || (c == '-') || (c==' ')) return; // NOI18N + @Override + public String findMaxPrefix(String prefix) { + return maxPrefix; + } - if (((modifiers > 0) && (modifiers != KeyEvent.SHIFT_MASK)) || e.isActionKey()) { - return; - } + @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; + } - if (Character.isISOControl(c) || - (keyCode == KeyEvent.VK_SHIFT) || - (keyCode == KeyEvent.VK_ESCAPE)) return; + @Override + public void quickSearchCanceled() { + origSelectionPaths = null; + searchResults.clear(); + lastSearchText = null; + } - 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); - } - private List doSearch(String prefix) { List results = new ArrayList(); Set resSet = new HashSet(); @@ -2109,7 +2019,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 +2044,7 @@ maxPrefix = elementName; } - maxPrefix = findMaxPrefix(maxPrefix, elementName); + maxPrefix = QuickSearch.findMaxPrefix(maxPrefix, elementName, true); } // try next element startIndex++; @@ -2146,16 +2056,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 +2063,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 +2076,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 +2093,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 +2218,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 -r 6bce66b50582 openide.explorer/test/unit/src/org/openide/explorer/view/TreeViewQuickSearchTest.java --- a/openide.explorer/test/unit/src/org/openide/explorer/view/TreeViewQuickSearchTest.java Tue Mar 06 10:49:56 2012 +0100 +++ b/openide.explorer/test/unit/src/org/openide/explorer/view/TreeViewQuickSearchTest.java Wed Mar 07 14:40:19 2012 +0100 @@ -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()); } } }