com.publicobject.issuesbrowser.swing.StatusMatcherEditor Maven / Gradle / Ivy
/* Glazed Lists (c) 2003-2006 */
/* http://publicobject.com/glazedlists/ publicobject.com,*/
/* O'Dell Engineering Ltd.*/
package com.publicobject.issuesbrowser.swing;
import java.awt.GridLayout;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.text.MessageFormat;
import java.util.*;
import javax.swing.JCheckBox;
import javax.swing.JComponent;
import javax.swing.JPanel;
import ca.odell.glazedlists.EventList;
import ca.odell.glazedlists.GroupingList;
import ca.odell.glazedlists.event.ListEvent;
import ca.odell.glazedlists.event.ListEventListener;
import ca.odell.glazedlists.matchers.AbstractMatcherEditor;
import ca.odell.glazedlists.matchers.Matcher;
import ca.odell.glazedlists.matchers.MatcherEditor;
import ca.odell.glazedlists.swing.GlazedListsSwing;
import com.publicobject.issuesbrowser.Issue;
import com.publicobject.issuesbrowser.IssueStatusComparator;
import com.publicobject.issuesbrowser.Status;
/**
* A MatcherEditor that produces Matchers that filter the issues based on the
* selected statuses.
*/
class StatusMatcherEditor extends AbstractMatcherEditor implements ListEventListener>, ActionListener, FilterComponent {
/** A MessageFormat to generate pretty names for our CheckBoxes which include the number of bugs with that status. */
private static final MessageFormat checkboxFormat = new MessageFormat("{0} {1,choice,0#|0<({1})}");
/** A panel housing a checkbox for each status. */
private JPanel checkBoxPanel = new JPanel(new GridLayout(4, 2));
/** A checkbox for each displayed status. */
private final Map statusCheckBoxes = new LinkedHashMap();
/** Issues grouped together by status. */
private final GroupingList issuesByStatus;
private final EventList> issuesByStatusSwingThread;
/**
* A cache of the list of statuses that mirrors the statuses of the issuesByStatus List.
* It is used to determine which status is deleted when DELETE events arrive.
*/
private List statuses = new ArrayList();
public StatusMatcherEditor(EventList issues, Status[] stati) {
// group the issues according to their status
issuesByStatus = new GroupingList(issues, new IssueStatusComparator());
this.issuesByStatusSwingThread = GlazedListsSwing.swingThreadProxyList(issuesByStatus);
this.issuesByStatusSwingThread.addListEventListener(this);
for (Status status : stati) {
this.statusCheckBoxes.put(status.getId(), buildCheckBox(status.getName()));
}
this.checkBoxPanel.setOpaque(false);
// add each checkbox to the panel and start listening to selections
for (Iterator iter = statusCheckBoxes.values().iterator(); iter.hasNext();) {
JCheckBox checkBox = iter.next();
checkBox.addActionListener(this);
this.checkBoxPanel.add(checkBox);
}
}
/**
* Returns the component responsible for editing the status filter.
*/
@Override
public JComponent getComponent() {
return this.checkBoxPanel;
}
@Override
public String toString() {
return "Status";
}
@Override
public MatcherEditor getMatcherEditor() {
return this;
}
/**
* A convenience method to build a status checkbox with the given name.
*/
private static JCheckBox buildCheckBox(String name) {
final JCheckBox checkBox = new JCheckBox(name, true);
checkBox.setName(name);
checkBox.setOpaque(false);
checkBox.setFocusable(false);
checkBox.setMargin(new Insets(0, 0, 0, 0));
return checkBox;
}
/**
* Returns a StatusMatcher which matches Issues if their status is one
* of the selected statuses.
*/
private StatusMatcher buildMatcher() {
final Set allowedStates = new HashSet();
for (Iterator> iter = statusCheckBoxes.entrySet().iterator(); iter.hasNext();) {
Map.Entry entry = iter.next();
if (entry.getValue().isSelected())
allowedStates.add(entry.getKey());
}
return new StatusMatcher(allowedStates);
}
@Override
public void listChanged(ListEvent> listChanges) {
while (listChanges.next()) {
final int type = listChanges.getType();
final int index = listChanges.getIndex();
// determine the status which changed and the new number
// of bugs that match that status after the change
final String status;
final int count;
if (type == ListEvent.INSERT) {
List issuesOfThisStatus = issuesByStatusSwingThread.get(index);
status = issuesOfThisStatus.get(0).getStatus();
statuses.add(index, status);
count = issuesOfThisStatus.size();
} else if (type == ListEvent.UPDATE) {
List issuesOfThisStatus = issuesByStatusSwingThread.get(index);
status = statuses.get(index);
count = issuesOfThisStatus.size();
} else if (type == ListEvent.DELETE) {
status = statuses.remove(index);
count = 0;
} else {
throw new IllegalStateException();
}
final JCheckBox checkBox = statusCheckBoxes.get(status);
// update the text of the checkbox to reflect the new bug count for that status
checkBox.setText(checkboxFormat.format(new Object[] { checkBox.getName(), new Integer(count)}));
}
}
@Override
public void actionPerformed(ActionEvent e) {
// determine if the checkbox that generated this ActionEvent is freshly checked or freshly unchecked
// - we'll use that information to determine whether this is a constrainment or relaxation of the matcher
final boolean isCheckBoxSelected = ((JCheckBox) e.getSource()).isSelected();
// build a StatusMatcher
final StatusMatcher statusMatcher = this.buildMatcher();
// fire a MatcherEvent of the appropriate type
if (statusMatcher.getStateCount() == 0)
this.fireMatchNone();
else if (statusMatcher.getStateCount() == this.statusCheckBoxes.size())
this.fireMatchAll();
else if (isCheckBoxSelected)
this.fireRelaxed(statusMatcher);
else
this.fireConstrained(statusMatcher);
}
/**
* A StatusMatcher returns true if the status of the Issue is
* one of the viewable status selected by the user.
*/
private static class StatusMatcher implements Matcher {
private final Set allowedStatuses;
public StatusMatcher(Set allowedStatuses) {
this.allowedStatuses = allowedStatuses;
}
public int getStateCount() {
return this.allowedStatuses.size();
}
@Override
public boolean matches(Issue issue) {
return this.allowedStatuses.contains(issue.getStatus());
}
}
}