# HG changeset patch # User Jaroslav Havlin # Parent fcfcef8316fff51999b43801c5b0a2ab0fac1249 #143367: Add option to disable Quicksearch provider diff --git a/spi.quicksearch/nbproject/project.xml b/spi.quicksearch/nbproject/project.xml --- a/spi.quicksearch/nbproject/project.xml +++ b/spi.quicksearch/nbproject/project.xml @@ -76,6 +76,10 @@ unit + org.netbeans.bootstrap + + + org.netbeans.core.startup @@ -87,6 +91,10 @@ + + org.openide.modules + + diff --git a/spi.quicksearch/src/org/netbeans/modules/quicksearch/AbstractQuickSearchComboBar.java b/spi.quicksearch/src/org/netbeans/modules/quicksearch/AbstractQuickSearchComboBar.java --- a/spi.quicksearch/src/org/netbeans/modules/quicksearch/AbstractQuickSearchComboBar.java +++ b/spi.quicksearch/src/org/netbeans/modules/quicksearch/AbstractQuickSearchComboBar.java @@ -43,6 +43,8 @@ package org.netbeans.modules.quicksearch; import java.awt.Color; +import java.awt.Component; +import java.awt.Container; import java.awt.FontMetrics; import java.awt.KeyboardFocusManager; import java.awt.event.ActionEvent; @@ -51,13 +53,23 @@ import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.lang.ref.WeakReference; +import java.util.Collections; +import java.util.HashSet; +import java.util.Iterator; +import java.util.MissingResourceException; +import java.util.Set; +import javax.swing.JCheckBoxMenuItem; import javax.swing.JComponent; import javax.swing.JList; +import javax.swing.JMenuItem; import javax.swing.JPopupMenu; -import javax.swing.JRadioButtonMenuItem; import javax.swing.KeyStroke; +import javax.swing.MenuElement; +import javax.swing.MenuSelectionManager; import javax.swing.SwingUtilities; import javax.swing.UIManager; +import javax.swing.event.ChangeEvent; +import javax.swing.event.ChangeListener; import javax.swing.event.DocumentEvent; import javax.swing.event.DocumentListener; import javax.swing.text.AbstractDocument; @@ -78,9 +90,7 @@ * Quick search toolbar component * @author Jan Becicka */ -public abstract class AbstractQuickSearchComboBar extends javax.swing.JPanel implements ActionListener { - - private static final String CATEGORY = "cat"; +public abstract class AbstractQuickSearchComboBar extends javax.swing.JPanel { QuickSearchPopup displayer = new QuickSearchPopup(this); WeakReference caller; @@ -253,7 +263,7 @@ setShowHint(false); if (CommandEvaluator.isCatTemporary()) { CommandEvaluator.setCatTemporary(false); - CommandEvaluator.setEvalCat(null); + CommandEvaluator.setEvalCats(null); } } @@ -263,21 +273,18 @@ } JPopupMenu pm = new JPopupMenu(); - ProviderModel.Category evalCat = null; + final Set evalCats = + new HashSet(); if (!CommandEvaluator.isCatTemporary()) { - evalCat = CommandEvaluator.getEvalCat(); + evalCats.addAll(CommandEvaluator.getEvalCats()); } - - JRadioButtonMenuItem allCats = new JRadioButtonMenuItem( - NbBundle.getMessage(getClass(), "LBL_AllCategories"), evalCat == null); - allCats.addActionListener(this); + JMenuItem allCats = new AllMenuItem(evalCats); pm.add(allCats); for (ProviderModel.Category cat : ProviderModel.getInstance().getCategories()) { if (!CommandEvaluator.RECENT.equals(cat.getName())) { - JRadioButtonMenuItem item = new JRadioButtonMenuItem(cat.getDisplayName(), cat == evalCat); - item.putClientProperty(CATEGORY, cat); - item.addActionListener(this); + JCheckBoxMenuItem item = new CategoryCheckBoxMenuItem(cat, + evalCats); pm.add(item); } } @@ -285,20 +292,30 @@ pm.show(getInnerComponent(), 0, getInnerComponent().getHeight() - 1); } - /** ActionListener implementation, reaction to popup menu item invocation */ - public void actionPerformed(ActionEvent e) { - JRadioButtonMenuItem item = (JRadioButtonMenuItem)e.getSource(); - CommandEvaluator.setEvalCat((Category) item.getClientProperty(CATEGORY)); + private void updateCats(Set evalCats) { + CommandEvaluator.setEvalCats(evalCats); CommandEvaluator.setCatTemporary(false); // refresh hint setShowHint(!command.isFocusOwner()); } + private void updateCheckBoxes(Container container, Set evalCats) { + Container parent = container.getParent(); + for (Component c : parent.getComponents()) { + if (c instanceof CategoryCheckBoxMenuItem) { + CategoryCheckBoxMenuItem ci = (CategoryCheckBoxMenuItem) c; + ci.setSelected(evalCats.contains(ci.category)); + ci.setTooltipText(); + } + } + } + /** Runs evaluation narrowed to specified category * */ public void evaluateCategory (Category cat, boolean temporary) { - CommandEvaluator.setEvalCat(cat); + CommandEvaluator.setEvalCats( + cat == null ? null : Collections.singleton(cat)); CommandEvaluator.setCatTemporary(temporary); displayer.maybeEvaluate(command.getText()); } @@ -322,9 +339,16 @@ } if (showHint) { command.setForeground(command.getDisabledTextColor()); - Category evalCat = CommandEvaluator.getEvalCat(); - if (evalCat != null && !CommandEvaluator.isCatTemporary()) { - command.setText(getHintText(evalCat)); + Set evalCats = CommandEvaluator.getEvalCats(); + if (evalCats.size() < 3 && !CommandEvaluator.isCatTemporary()) { + Category bestFound = null; + for (Category c : evalCats) { + if (bestFound == null || CommandEvaluator.RECENT.equals( + bestFound.getName())) { + bestFound = c; + } + } + command.setText(getHintText(bestFound)); } else { command.setText(getHintText(null)); } @@ -493,4 +517,162 @@ DialogDisplayer.getDefault().notifyLater(nd); } } + + /** + * Show and select menu at a given path. Used to restore a menu after click. + */ + private void showMenuPath(MenuElement[] selectedPath) { + if (selectedPath != null && selectedPath.length > 1) { + if (selectedPath[0] instanceof JPopupMenu) { + ((JPopupMenu) selectedPath[0]).setVisible(true); + MenuSelectionManager.defaultManager().setSelectedPath( + selectedPath); + } + } + } + + /** + * Menu item representing a single category. + */ + private class CategoryCheckBoxMenuItem extends JCheckBoxMenuItem + implements ActionListener { + + private MenuElement[] selectedPath = null; + private Category category; + private final Set evalCats; + + public CategoryCheckBoxMenuItem(final Category category, + final Set evalCats) { + super(category.getDisplayName(), evalCats.contains(category)); + this.category = category; + this.evalCats = evalCats; + setTooltipText(); + getModel().addChangeListener(new ChangeListener() { + @Override + public void stateChanged(ChangeEvent e) { + if (isShowing() && model.isArmed()) { + selectedPath = MenuSelectionManager.defaultManager() + .getSelectedPath(); + } + } + }); + addActionListener(this); + addMouseListener(new MouseAdapter() { + + @Override + public void mouseClicked(MouseEvent e) { + mouseClickedOnItem(e); + } + }); + } + + @Override + public void doClick(int pressTime) { + super.doClick(pressTime); + setTooltipText(); + showMenuPath(selectedPath); + } + + private void mouseClickedOnItem(MouseEvent e) { + if (SwingUtilities.isRightMouseButton(e)) { + e.consume(); + if (isSelected()) { + Iterator iterator = evalCats.iterator(); + while (iterator.hasNext()) { + Category c = iterator.next(); + if (!CommandEvaluator.RECENT.equals(c.getName())) { + iterator.remove(); + } + } + evalCats.add(category); + } else { + evalCats.addAll( + ProviderModel.getInstance().getCategories()); + evalCats.remove(category); + } + updateCheckBoxes(CategoryCheckBoxMenuItem.this, evalCats); + updateCats(evalCats); + } + } + + @Override + public void actionPerformed(ActionEvent e) { + if (this.isSelected()) { + evalCats.add(category); + } else { + evalCats.remove(category); + } + updateCats(evalCats); + } + + private void setTooltipText() throws MissingResourceException { + + boolean selected = evalCats.contains(category); + String bundleKey = selected + ? "MSG_RightClickEnablesAllOthers" //NOI18N + : "MSG_RightClickDisablesOthers"; //NOI18N + setToolTipText(NbBundle.getMessage( + AbstractQuickSearchComboBar.class, bundleKey)); + } + } + + /** + * Menu item for enabling or disabling all categories. + */ + private class AllMenuItem extends JMenuItem implements ActionListener { + + private Set evalCats; + private int totalCount; + private MenuElement[] selectedPath = null; + + public AllMenuItem(Set evalCats) { + this.evalCats = evalCats; + this.totalCount = ProviderModel.getInstance() + .getCategories().size(); + getModel().addChangeListener(new ChangeListener() { + @Override + public void stateChanged(ChangeEvent e) { + if (isShowing() && model.isArmed()) { + selectedPath = MenuSelectionManager.defaultManager() + .getSelectedPath(); + } + } + }); + addActionListener(this); + } + + @Override + public void actionPerformed(ActionEvent e) { + if (evalCats.size() == totalCount) { + Iterator iterator = evalCats.iterator(); + while (iterator.hasNext()) { + Category c = iterator.next(); + if (!CommandEvaluator.RECENT.equals(c.getName())) { + iterator.remove(); + } + } + } else { + evalCats.addAll(ProviderModel.getInstance().getCategories()); + } + updateCats(evalCats); + updateCheckBoxes(this, evalCats); + } + + @Override + public void doClick(int pressTime) { + super.doClick(pressTime); + showMenuPath(selectedPath); + } + + @Override + public String getText() { + if (evalCats == null || evalCats.size() != totalCount) { + return NbBundle.getMessage(getClass(), + "LBL_AllCategories"); //NOI18N + } else { + return NbBundle.getMessage(getClass(), + "LBL_NoCategory"); //NOI18N + } + } + } } diff --git a/spi.quicksearch/src/org/netbeans/modules/quicksearch/Bundle.properties b/spi.quicksearch/src/org/netbeans/modules/quicksearch/Bundle.properties --- a/spi.quicksearch/src/org/netbeans/modules/quicksearch/Bundle.properties +++ b/spi.quicksearch/src/org/netbeans/modules/quicksearch/Bundle.properties @@ -48,6 +48,9 @@ MSG_DiscoverabilityHint2=Search in {0} LBL_MoreResults=... LBL_AllCategories=All Categories +LBL_NoCategory=Deselect All +MSG_RightClickDisablesOthers=Right-click to de-select all other categories +MSG_RightClickEnablesAllOthers=Right-click to select all categories excluding this one QuickSearchPopup.searchingLabel.text=Searching ... QuickSearchPopup.noResultsLabel.text=( No Results ) QuickSearchPopup.hintLabel.text={0}; Press {1} for All Categories diff --git a/spi.quicksearch/src/org/netbeans/modules/quicksearch/CommandEvaluator.java b/spi.quicksearch/src/org/netbeans/modules/quicksearch/CommandEvaluator.java --- a/spi.quicksearch/src/org/netbeans/modules/quicksearch/CommandEvaluator.java +++ b/spi.quicksearch/src/org/netbeans/modules/quicksearch/CommandEvaluator.java @@ -43,13 +43,18 @@ package org.netbeans.modules.quicksearch; import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashSet; +import java.util.Iterator; import java.util.List; +import java.util.Set; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.netbeans.modules.quicksearch.ProviderModel.Category; import org.netbeans.spi.quicksearch.SearchProvider; import org.netbeans.spi.quicksearch.SearchRequest; import org.netbeans.spi.quicksearch.SearchResponse; +import org.openide.util.NbPreferences; import org.openide.util.RequestProcessor; import org.openide.util.RequestProcessor.Task; @@ -61,6 +66,8 @@ public class CommandEvaluator { final static String RECENT = "Recent"; + private static final String PROP_ENABLED_CATEGORIES = + "enabledCategories"; //NOI18N /** * command pattern is: @@ -68,15 +75,14 @@ */ private static Pattern COMMAND_PATTERN = Pattern.compile("(\\w+)(\\s+)(.+)"); - /** Narrow evaluation only to specified category if non null. - * Evaluate all categories otherwise - */ - private static ProviderModel.Category evalCat; - /** Temporary narrow evaluation to only specified category **/ private static boolean isCatTemporary; private static final RequestProcessor RP = new RequestProcessor("QuickSearch Command Evaluator", 10); // NOI18N + /** + * Narrow evaluation to specified set of categories. + */ + private static Set evalCats = loadEvalCats(); /** * Runs evaluation. @@ -113,12 +119,57 @@ return new Wait4AllTask(tasks); } - public static Category getEvalCat () { - return evalCat; + private static Set loadEvalCats() { + final Set cats = new HashSet( + ProviderModel.getInstance().getCategories()); + RP.post(new Runnable() { + @Override + public void run() { + String ec = NbPreferences.forModule(CommandEvaluator.class).get( + PROP_ENABLED_CATEGORIES, null); + if (ec != null) { + Set categoryNames = new HashSet(); + categoryNames.addAll(Arrays.asList(ec.split(":"))); //NOI18N + Iterator iterator = cats.iterator(); + while (iterator.hasNext()) { + Category category = iterator.next(); + if (!categoryNames.contains(category.getName()) + && !RECENT.equals(category.getName())) { + iterator.remove(); + } + } + } + } + }); + return cats; } - public static void setEvalCat (Category cat) { - CommandEvaluator.evalCat = cat; + private static void storeEvalCats() { + RP.post(new Runnable() { + @Override + public void run() { + StringBuilder sb = new StringBuilder(); + for (Category category : evalCats) { + if (!RECENT.equals(category.getName())) { + sb.append(category.getName()); + sb.append(':'); + } + } + NbPreferences.forModule(CommandEvaluator.class).put( + PROP_ENABLED_CATEGORIES, sb.toString()); + } + }); + } + + public static Set getEvalCats () { + return evalCats; + } + + public static void setEvalCats(Set cat) { + CommandEvaluator.evalCats = (cat == null) + ? new HashSet(ProviderModel.getInstance() + .getCategories()) + : cat; } public static boolean isCatTemporary () { @@ -127,6 +178,9 @@ public static void setCatTemporary (boolean isCatTemporary) { CommandEvaluator.isCatTemporary = isCatTemporary; + if (!isCatTemporary) { + storeEvalCats(); + } } private static String[] parseCommand (String command) { @@ -180,15 +234,8 @@ } } - // evaluation narrowed to category perhaps? - if (evalCat != null) { - result.add(evalCat); - return true; - } - - // no narrowing - result.clear(); - result.addAll(cats); + //no narrowing + result.addAll(evalCats); return false; } diff --git a/spi.quicksearch/src/org/netbeans/modules/quicksearch/QuickSearchPopup.java b/spi.quicksearch/src/org/netbeans/modules/quicksearch/QuickSearchPopup.java --- a/spi.quicksearch/src/org/netbeans/modules/quicksearch/QuickSearchPopup.java +++ b/spi.quicksearch/src/org/netbeans/modules/quicksearch/QuickSearchPopup.java @@ -55,6 +55,7 @@ import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.awt.event.MouseMotionAdapter; +import java.util.Set; import java.util.logging.Level; import java.util.logging.Logger; import java.util.prefs.Preferences; @@ -541,7 +542,7 @@ shouldBeVisible = shouldBeVisible || areNoResults; hintLabel.setText(getHintText()); - boolean isNarrowed = CommandEvaluator.getEvalCat() != null && searchedNotEmpty; + boolean isNarrowed = CommandEvaluator.getEvalCats().size() == 1 && searchedNotEmpty; hintSep.setVisible(isNarrowed); hintLabel.setVisible(isNarrowed); shouldBeVisible = shouldBeVisible || isNarrowed; @@ -550,12 +551,12 @@ } private String getHintText () { - ProviderModel.Category evalCat = CommandEvaluator.getEvalCat(); - if (evalCat == null) { + Set evalCats = CommandEvaluator.getEvalCats(); + if (evalCats.size() != 1) { return null; } return NbBundle.getMessage(QuickSearchPopup.class, "QuickSearchPopup.hintLabel.text", - evalCat.getDisplayName(), SearchResultRender.getKeyStrokeAsText( + evalCats.iterator().next().getDisplayName(), SearchResultRender.getKeyStrokeAsText( comboBar.getKeyStroke())); } diff --git a/spi.quicksearch/test/unit/src/org/netbeans/modules/quicksearch/CommandEvaluatorTest.java b/spi.quicksearch/test/unit/src/org/netbeans/modules/quicksearch/CommandEvaluatorTest.java new file mode 100644 --- /dev/null +++ b/spi.quicksearch/test/unit/src/org/netbeans/modules/quicksearch/CommandEvaluatorTest.java @@ -0,0 +1,75 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2013 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 2013 Sun Microsystems, Inc. + */ +package org.netbeans.modules.quicksearch; + +import java.util.HashSet; +import java.util.Set; +import static org.junit.Assert.*; +import org.junit.Test; +import org.netbeans.modules.quicksearch.ProviderModel.Category; + +/** + * + * @author jhavlin + */ +public class CommandEvaluatorTest { + + @Test + public void testGetSetEvalCats() { + Set evalCats = CommandEvaluator.getEvalCats(); + Category recent = null; + for (Category c : evalCats) { + if (CommandEvaluator.RECENT.equals(c.getName())) { + recent = c; + } + } + assertNotNull("Recent category should be enabled", recent); + CommandEvaluator.setEvalCats(new HashSet()); + assertEquals(0, CommandEvaluator.getEvalCats().size()); + CommandEvaluator.setEvalCats(null); + assertNotNull(ProviderModel.getInstance().getCategories()); + assertNotNull(CommandEvaluator.getEvalCats()); + assertEquals(ProviderModel.getInstance().getCategories().size(), + CommandEvaluator.getEvalCats().size()); + CommandEvaluator.setEvalCats(evalCats); + } +} \ No newline at end of file