Archive

Archive for the ‘Quick Search’ Category

Recipe 15: How to add a toggle action

February 2, 2015 Leave a comment

Problem: How can I add a toggle button as an action? I tried to add a JToggleButton but it doesn’t work.

Solution: JToggleButton doesn’t really work with the NetBeans toolbar, instead use BooleanStateAction. Here are the steps to follow:

  1. Create a new Action, e.g.
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import org.openide.awt.ActionRegistration;
import org.openide.awt.ActionReference;
import org.openide.awt.ActionReferences;
import org.openide.awt.ActionID;
import org.openide.util.NbBundle.Messages;

@ActionID(category = "Options",
id = "todo.controller.options.QuickFilterAction")
@ActionRegistration(iconBase = "todo/controller/options/find16.png",
displayName = "#CTL_QuickFilterAction")
@ActionReferences({
    @ActionReference(path = "Menu/Options", position = 50),
    @ActionReference(path = "Toolbars/Options", position = 50),
    @ActionReference(path = "Shortcuts", name = "F3")
})
@Messages("CTL_QuickFilterAction=Quick Filter")
public final class QuickFilterAction implements ActionListener {

    public void actionPerformed(ActionEvent e) {
        // TODO implement action body
    }
}

2.  Extend BooleanStateAction and since it implements ActionListener this can be deleted from our action:

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import org.openide.awt.ActionRegistration;
import org.openide.awt.ActionReference;
import org.openide.awt.ActionReferences;
import org.openide.awt.ActionID;
import org.openide.util.NbBundle.Messages;

@ActionID(category = "Options",
id = "todo.controller.options.QuickFilterAction")
@ActionRegistration(iconBase = "todo/controller/options/find16.png",
displayName = "#CTL_QuickFilterAction")
@ActionReferences({
    @ActionReference(path = "Menu/Options", position = 50),
    @ActionReference(path = "Toolbars/Options", position = 50),
    @ActionReference(path = "Shortcuts", name = "F3")
})
@Messages("CTL_QuickFilterAction=Quick Filter")
public final class QuickFilterAction extends BooleanStateAction {

 @Override
 protected void initialize() {
   super.initialize();
   setBooleanState(Settings.isSearchVisible());
 }

 @Override
 public void actionPerformed(ActionEvent e) {
   super.actionPerformed(e);
   TasksTopComponent tasksTopComponent = (TasksTopComponent) WindowManager.getDefault().findTopComponent("TasksTopComponent");
   tasksTopComponent.setSearchVisible(getBooleanState());
 }

 @Override
 public String getName() {
   return "Quick Filter";
 }

 @Override
 public HelpCtx getHelpCtx() {
   return HelpCtx.DEFAULT_HELP;
 }

 @Override
 protected String iconResource() {
   return "todo/controller/options/find16.png";
 }
}

Since, due to lazy initialization, it doesn’t recognize the values passed in the annotations, we need to override the iconResource() method. Because, by default, it is selected, we need to set it to unselected in the initialize() method.

Advertisements
Categories: Action, Filter, Quick Search

Recipe 14: How to add a Quick Filter

February 1, 2015 Leave a comment

Problem: How can I display a Quick Filter button with a Quick Filter text field to filter my OutlineView?

Solution:  In Recipe 9 we showed how to create a QuickFilter in order to filter an OutlineView. NetBeans RCP allows you to create a graphical filter text field in order to filter your OutlineView dynamically based on what you are typing (display the Notifications window by clicking on menu Window | IDE Tools | Notifications for an example — you need to have NetBeans version 7.3 or later).

notifications

In the class that contains your TopComponent or your panel you need to define the following:

/** Quick Search filter */
private final QuickSearch quickSearch;
/** Callback */
private final QuickSearch.Callback quickFilterCallback = new QuickFilterCallback();

and in the constructor (you need to create an empty JPanel — here called pnlQuickSearchFilter — that will host the quick filter text box):

GridBagConstraints searchConstrains = new GridBagConstraints(0, 0, 1, 1, 0.0, 0.0, GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0);
quickSearch = QuickSearch.attach(pnlQuickSearchFilter, searchConstrains, quickFilterCallback, true);
FilterRepository.getInstance().addPropertyChangeListener(pcl);
setSearchVisible(Settings.isSearchVisible());

QuickFilterCallback is called when the user types something in the quick filter text field:

private class QuickFilterCallback implements QuickSearch.Callback {

    @Override
    public void quickSearchUpdate(String searchText) {
        FilterRepository.getInstance().setQuickSearchFilter(searchText);
        if (quickSearch != null && !quickSearch.isAlwaysShown()) {
            setSearchVisible(true);
        }
    }

    @Override
    public void showNextSelection(boolean forward) {
    }

    @Override
    public String findMaxPrefix(String prefix) {
        return prefix;
    }

    @Override
    public void quickSearchConfirmed() {
    }

    @Override
    public void quickSearchCanceled() {
        FilterRepository.getInstance().setQuickSearchFilter(null);
    }
}

FilterRepository handles the quick filter:

package org.netbeans.filters.filter.checklist;

import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;

public final class FilterRepository {

    /** Property when a new quick filter is applied. */
    public static final String PROP_QUICK_FILTER = "QUICK-FILTER";
    /** Observable. */
    private PropertyChangeSupport pcs = null;
    /** A quick search filter. */
    private String quickSearchFilter;

    /** Constructor, default. */
    private FilterRepository() {
    }

    /**
     * Singleton.
     *
     * @return the single instance of this class.
     */
    public static FilterRepository getInstance() {
        return SingletonHolder.INSTANCE;
    }

    /** SingletonHolder. */
    private static final class SingletonHolder {

        /** The only INSTANCE. */
        private static final FilterRepository INSTANCE = new FilterRepository();

        /** Private. */
        private SingletonHolder() {
        }
    }

    /**
     * @return a thread-safe PropertyChangeSupport
     */
    private PropertyChangeSupport getPropertyChangeSupport() {
        if (pcs == null) {
            pcs = new PropertyChangeSupport(this);
        }
        return pcs;
    }

    public void addPropertyChangeListener(PropertyChangeListener pcl) {
        getPropertyChangeSupport().addPropertyChangeListener(pcl);
    }

    public void removePropertyChangeListener(PropertyChangeListener pcl) {
        getPropertyChangeSupport().removePropertyChangeListener(pcl);
    }

    public void addPropertyChangeListener(String name, PropertyChangeListener pcl) {
        getPropertyChangeSupport().addPropertyChangeListener(name, pcl);
    }

    public void removePropertyChangeListener(String name, PropertyChangeListener pcl) {
        getPropertyChangeSupport().removePropertyChangeListener(name, pcl);
    }

    /**
     * Quick search filter is used by bottom panel.
     *
     * @param searchText
     * text to filter
     */
    public void setQuickSearchFilter(String searchText) {
        String oldFilter = quickSearchFilter;
        quickSearchFilter = (searchText == null || searchText.isEmpty()) ? null : searchText;
        getPropertyChangeSupport().firePropertyChange(PROP_QUICK_FILTER, oldFilter, quickSearchFilter);
    }

    public boolean isQuickSearchFilter() {
        return quickSearchFilter != null;
    }

    public String getQuickSearchFilter() {
        return quickSearchFilter;
    }
}

Settings class (copied from NetBeans source code):

import java.util.prefs.Preferences;
import org.openide.util.NbPreferences;

/**
 * @author jpeska
 */
public class Settings {

   private static final String SEARCH_VISIBLE = "searchVisible"; //NOI18N
   private static final boolean DEFAULT_SEARCH_VISIBLE = true;

   private Settings() {
   }

   public static boolean isSearchVisible() {
      return getPreferences().getBoolean(SEARCH_VISIBLE, DEFAULT_SEARCH_VISIBLE);
   }

   public static void setSearchVisible(boolean searchVisible) {
      getPreferences().putBoolean(SEARCH_VISIBLE, searchVisible);
   }

   private static Preferences getPreferences() {
      return NbPreferences.forModule(Settings.class);
   }
}

setSearchVisible() method does the actual filtering of the OutlineView using the setQuickFilter() and unsetQuickFilter() methods explained in Recipe 9.

    /**
     * @param visible if {@code true} then display the quick search panel
     */
    public void setSearchVisible(boolean visible) {
        quickSearch.setAlwaysShown(visible);
        if (!visible) {
            unsetQuickFilter();
        } else if (FilterRepository.getInstance().isQuickSearchFilter()) {
            setQuickFilter(0, getQuickFilter(FilterRepository.getInstance().getQuickSearchFilter()));
        }
        Settings.setSearchVisible(visible);
    }

    /**
     * Create a QuickFilter from the given String.
     *
     * @param qsFilter filter as String
     * @return a QuickFilter from the given String
     */
    private QuickFilter getQuickFilter(String qsFilter) {
        return (Object aValue) -> {
            if (aValue instanceof TaskNode) {
                Task task = ((TaskNode) aValue).getLookup().lookup(Task.class);
                return task.getDescription().contains(qsFilter)
                        || String.valueOf(task.getPriority()).contains(qsFilter)
                        || task.getDueDate().toString().contains(qsFilter);
            } else {
                return false;
            }
        };
    }

In the above example we show how to filter an OutlineView that contains TaskNodes. The filter is trying to match the Task‘s description, priority or due date.

The last remaining change is the property change listener which listens to changes in FilterRepository.

    /** Listen to changes in the FilterRepository and filter the outline view
     * accordingly. */
    private final PropertyChangeListener pcl = (final PropertyChangeEvent evt) -> {
        SwingUtilities.invokeLater(() -> {
            // filter outline view on QuickSearchFilter input
            if (FilterRepository.PROP_QUICK_FILTER.equals(evt.getPropertyName())) {
                final String newQuickSearchFilter = (String) evt.getNewValue();
                if (newQuickSearchFilter != null && !newQuickSearchFilter.isEmpty()) {
                    unsetQuickFilter();   // should run in EDT
                    if (!newQuickSearchFilter.isEmpty()) { // should run in EDT
                        setQuickFilter(0, getQuickFilter(newQuickSearchFilter));
                    }
                } else {
                    unsetQuickFilter();  // should run in EDT
                }
            }
        });
    };
Categories: Filter, Quick Search

Recipe 4: QuickSearch

October 8, 2011 Leave a comment

Problem:

How do I display a QuickSearch box?

Solution:

A QuickSearch box searches for keywords you type in its text box. To add this functionality to your NetBeans RCP application you need to implement the SearchProvider service like so:

package my.package.quicksearch;
@ServiceProvider(service = SearchProvider.class)
public class MyQuickSearch implements SearchProvider {
   @Override
   public void evaluate(SearchRequest request, SearchResponse response) {
      String req = request.getText();
      // response.addResult(Runnable Action, String htmlDisplayName);
   }
}

You need to declare it in layer.xml:

<filesystem>
  <folder name="QuickSearch">
    <folder name="MyFolder">
       <file name="my-package-quicksearch-MyQuickSearch.instance"/>
       <attr name="position" intvalue="300"/>
    </folder>
  </folder>
</filesystem>

If you need to add the quicksearch filter tool to your own panel/toolbar, then follow the trick found here and here.

This article provides an example on how to use QuickSearch. In short:

  1. Right-click on your project and select New -> Other.
  2. In the New File dialog, choose Module Development -> Quick Search Provider.
  3. In the Quick Search Provider panel, set the provider class name etc.
  4. Click on Finish.

The Java stub MyQuickSearch shown above is created as well as the entry in layer.xml.

Categories: Quick Search