--- a/spi.quicksearch/nbproject/project.xml
+++ a/spi.quicksearch/nbproject/project.xml
@@ -76,6 +76,10 @@
unit
+ org.netbeans.bootstrap
+
+
+
org.netbeans.core.startup
@@ -87,6 +91,10 @@
+
+ org.openide.modules
+
+
--- a/spi.quicksearch/src/org/netbeans/modules/quicksearch/AbstractQuickSearchComboBar.java
+++ a/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,22 @@
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.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 +89,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 +262,7 @@
setShowHint(false);
if (CommandEvaluator.isCatTemporary()) {
CommandEvaluator.setCatTemporary(false);
- CommandEvaluator.setEvalCat(null);
+ CommandEvaluator.setEvalCats(null);
}
}
@@ -263,21 +272,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 +291,29 @@
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));
+ }
+ }
+ }
+
/** 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 +337,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 +515,151 @@
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;
+ setOpaque(false);
+ 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);
+ 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);
+ }
+ }
+
+ /**
+ * 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
+ }
+ }
+ }
}
--- a/spi.quicksearch/src/org/netbeans/modules/quicksearch/Bundle.properties
+++ a/spi.quicksearch/src/org/netbeans/modules/quicksearch/Bundle.properties
@@ -48,6 +48,7 @@
MSG_DiscoverabilityHint2=Search in {0}
LBL_MoreResults=...
LBL_AllCategories=All Categories
+LBL_NoCategory=Deselect All
QuickSearchPopup.searchingLabel.text=Searching ...
QuickSearchPopup.noResultsLabel.text=( No Results )
QuickSearchPopup.hintLabel.text={0}; Press {1} for All Categories
--- a/spi.quicksearch/src/org/netbeans/modules/quicksearch/CommandEvaluator.java
+++ a/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;
}
--- a/spi.quicksearch/src/org/netbeans/modules/quicksearch/QuickSearchPopup.java
+++ a/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()));
}
--- a/spi.quicksearch/test/unit/src/org/netbeans/modules/quicksearch/CommandEvaluatorTest.java
+++ a/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);
+ }
+}