Archive

Archive for the ‘Filter’ 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.

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 10: How to remove a filter that returned no rows from an OutlineView?

March 2, 2014 Leave a comment

Problem

By right-clicking inside an outline view, a popup menu is displayed where you can select the action Show only rows where you can define your criteria depending under which column you clicked. However, if the filter resulted in no rows, how can you display all rows again since the action is displayed only when there are visible rows in the outline view?

Solution

An easy solution is to add a toolbar next to your outline view inside the top component and add the following button:

public class RemoveFilterAction extends AbstractAction {
  /** The outline view to sort. */
  private final OutlineView outlineView;
  /** PropertyChangeListener to set the enabled state. */
  private final PropertyChangeListener pcl = new PropertyChangeListener() {
     @Override
     public void propertyChange(PropertyChangeEvent evt) {
       setEnabled(RemoveFilterAction.this.outlineView.getOutline().getQuickFilterColumn() != -1);
     }
  };

  /**
    *
    * @param view the outline view to remove filter from.
    */
  public RemoveFilterAction(OutlineView view) {
     super(NbBundle.getMessage(RemoveFilterAction.class, "HINT_RemoveFilter"),
           ImageUtilities.loadImageIcon("deleteFilter.png", false));
     this.outlineView = view;
     view.getOutline().addPropertyChangeListener(pcl);
     pcl.propertyChange(null);
     putValue(SHORT_DESCRIPTION, org.openide.util.NbBundle.getMessage(RemoveFilterAction.class, "HINT_RemoveFilter"));
  }

  @Override
  public void actionPerformed(ActionEvent e) {
     outlineView.getOutline().unsetQuickFilter();
     pcl.propertyChange(null);
  }
}

where

HINT_RemoveFilter=Remove the applied filter from view

Whenever a filter is applied to the outline view, the button becomes enabled, and by clicking on it the filter is removed.

Categories: Filter, OutlineView

Recipe 9: How to filter an OutlineView?

February 26, 2014 1 comment

Problem

How can I filter an OutlineView programmatically?

Solution

By right-clicking inside an outline view, a popup menu is displayed where you can select the action Show only rows where you can define your criteria depending under which column you clicked.

To do filtering programmatically, ETable defines two methods:

outlineView.getOutline().setQuickFilter(column, filter);
outlineView.getOutline().unsetQuickFilter();

where column is 1 for first column, 2 for second etc. and filter is of type Object and can be one of two types:

  • a value that is matched directly to the values of the column, e.g.
outlineView.getOutline().setQuickFilter(3, Boolean.TRUE);
  • a QuickFilter e.g.
private final QuickFilter filter = new QuickFilter() {
   @Override
   public boolean accept(Object aValue) {
      if (aValue instanceof TaskNode) {
         TaskNode taskNode = (TaskNode) aValue;
         Task task = taskNode.getLookup().lookup(Task.class);
         return task.isCompleted();
      }
     return true;
   }
};
outlineView.getOutline().setQuickFilter(0, filter);

In the previous example we pass the filter to column 0. This is the node column which gives us access to all fields of the node, i.e. the bean that is wrapped, which allows to filter the outline view even for fields that are not displayed in the outline view. E.g. you have a class :

public class Task implements Serializable {
  private int id;
  private String description;
  private int priority;
  private Date dueDate;
  private boolean alert = false;
  private int daysBefore;
  private String obs;
  private boolean completed = false;

but the outline view displays only: priority, description, alert and due date and you want to filter the tasks that are completed, then the above filter will do the job.

Categories: Filter, OutlineView