/**
* <copyright>
*
* Copyright (c) 2016 Thales Global Services S.A.S.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Thales Global Services S.A.S. - initial API and implementation
*
* </copyright>
*/
package org.eclipse.emf.diffmerge.ui.viewers;
import static org.eclipse.emf.diffmerge.ui.viewers.CategoryViewer.CategoryState.FILTERED;
import static org.eclipse.emf.diffmerge.ui.viewers.CategoryViewer.CategoryState.FOCUSED;
import static org.eclipse.emf.diffmerge.ui.viewers.CategoryViewer.CategoryState.NORMAL;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.eclipse.compare.IPropertyChangeNotifier;
import org.eclipse.emf.diffmerge.ui.EMFDiffMergeUIPlugin;
import org.eclipse.emf.diffmerge.ui.EMFDiffMergeUIPlugin.ImageID;
import org.eclipse.emf.diffmerge.ui.Messages;
import org.eclipse.jface.layout.TreeColumnLayout;
import org.eclipse.jface.util.IPropertyChangeListener;
import org.eclipse.jface.util.PropertyChangeEvent;
import org.eclipse.jface.viewers.AbstractTreeViewer;
import org.eclipse.jface.viewers.CellEditor;
import org.eclipse.jface.viewers.ColumnLabelProvider;
import org.eclipse.jface.viewers.ColumnViewer;
import org.eclipse.jface.viewers.ColumnViewerEditorActivationEvent;
import org.eclipse.jface.viewers.ColumnViewerToolTipSupport;
import org.eclipse.jface.viewers.ColumnWeightData;
import org.eclipse.jface.viewers.EditingSupport;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.ITreeContentProvider;
import org.eclipse.jface.viewers.TreeViewer;
import org.eclipse.jface.viewers.TreeViewerColumn;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Shell;
/**
* A viewer for setting up difference categories.
* Input: CategoryViewer.Input.
* @author Olivier Constant
*/
public class CategoryViewer extends Viewer {
/**
* The UI states of difference categories.
*/
public static enum CategoryState {
/** The 'normal', i.e. non-active, state */
NORMAL,
/** The 'active and filtering' state */
FILTERED,
/** The 'active and focusing' state */
FOCUSED
}
/** The non-null main sub-viewer */
protected TreeViewer _viewer;
/** The current input (initially null) */
private Input _input;
/** A non-null listener for configuration changes */
private final IPropertyChangeListener _configUpdater;
/**
* Constructor
* @param parent_p a non-null composite
*/
public CategoryViewer(Composite parent_p) {
_input = null;
_configUpdater = new IPropertyChangeListener() {
/**
* @see org.eclipse.jface.util.IPropertyChangeListener#propertyChange(org.eclipse.jface.util.PropertyChangeEvent)
*/
public void propertyChange(PropertyChangeEvent event_p) {
if (Input.PROPERTY_CONFIGURATION.equals(event_p.getProperty()))
refresh();
}
};
createControls(parent_p);
}
/**
* Create the controls for this viewer and return the main control
* @param parent_p a non-null composite
*/
protected void createControls(Composite parent_p) {
// Wrapper composite, required for TreeColumnLayout
Composite wrapper = new Composite(parent_p, SWT.NONE);
GridData wrapperData = new GridData(SWT.FILL, SWT.FILL, true, true);
wrapper.setLayoutData(wrapperData);
TreeColumnLayout layout = new TreeColumnLayout();
wrapper.setLayout(layout);
// Main
_viewer = new TreeViewer(wrapper, SWT.SINGLE | SWT.BORDER | SWT.NO_SCROLL | SWT.V_SCROLL |
SWT.FULL_SELECTION | SWT.HIDE_SELECTION);
_viewer.getTree().setHeaderVisible(true);
_viewer.getTree().setLinesVisible(true);
_viewer.setAutoExpandLevel(AbstractTreeViewer.ALL_LEVELS);
// Column 1: Hierarchy
TreeViewerColumn catItemColumn = new TreeViewerColumn(_viewer, SWT.LEFT);
catItemColumn.getColumn().setText(Messages.CategoryViewer_CategoryHeader);
catItemColumn.setLabelProvider(new HierarchyLabelProvider());
// Column 2: Normal
TreeViewerColumn normalStateColumn = new TreeViewerColumn(_viewer, SWT.CENTER);
normalStateColumn.getColumn().setText(Messages.CategoryViewer_NormalStateHeader);
normalStateColumn.getColumn().setToolTipText(
Messages.CategoryViewer_NormalStateTooltip);
normalStateColumn.setLabelProvider(new StateLabelProvider(NORMAL));
normalStateColumn.setEditingSupport(new StateEditingSupport(_viewer, NORMAL));
normalStateColumn.getColumn().addSelectionListener(new SelectionAdapter() {
/**
* @see org.eclipse.swt.events.SelectionAdapter#widgetSelected(org.eclipse.swt.events.SelectionEvent)
*/
@Override
public void widgetSelected(SelectionEvent e_p) {
Input input = getInput();
if (input != null)
input.setAll(NORMAL);
}
});
// Column 3: Filtered
TreeViewerColumn filteredStateColumn = new TreeViewerColumn(_viewer, SWT.CENTER);
filteredStateColumn.getColumn().setText(Messages.CategoryViewer_FilteredStateHeader);
filteredStateColumn.getColumn().setToolTipText(
Messages.CategoryViewer_FilteredStateTooltip);
filteredStateColumn.setLabelProvider(new StateLabelProvider(FILTERED));
filteredStateColumn.setEditingSupport(new StateEditingSupport(_viewer, FILTERED));
filteredStateColumn.getColumn().addSelectionListener(new SelectionAdapter() {
/**
* @see org.eclipse.swt.events.SelectionAdapter#widgetSelected(org.eclipse.swt.events.SelectionEvent)
*/
@Override
public void widgetSelected(SelectionEvent e_p) {
Input input = getInput();
if (input != null)
input.setAll(FILTERED);
}
});
// Column 4: Focused
TreeViewerColumn focusedStateColumn = new TreeViewerColumn(_viewer, SWT.CENTER);
focusedStateColumn.getColumn().setText(Messages.CategoryViewer_FocusedStateHeader);
focusedStateColumn.getColumn().setToolTipText(
Messages.CategoryViewer_FocusedStateTooltip);
focusedStateColumn.setLabelProvider(new StateLabelProvider(FOCUSED));
focusedStateColumn.setEditingSupport(new StateEditingSupport(_viewer, FOCUSED));
focusedStateColumn.getColumn().addSelectionListener(new SelectionAdapter() {
/**
* @see org.eclipse.swt.events.SelectionAdapter#widgetSelected(org.eclipse.swt.events.SelectionEvent)
*/
@Override
public void widgetSelected(SelectionEvent e_p) {
Input input = getInput();
if (input != null)
input.setAll(FOCUSED);
}
});
// Overall
layout.setColumnData(catItemColumn.getColumn(), new ColumnWeightData(1, 300, true));
final int STATE_COLUMN_WIDTH = 70;
layout.setColumnData(
normalStateColumn.getColumn(), new ColumnWeightData(0, STATE_COLUMN_WIDTH, false));
layout.setColumnData(
filteredStateColumn.getColumn(), new ColumnWeightData(0, STATE_COLUMN_WIDTH, false));
layout.setColumnData(
focusedStateColumn.getColumn(), new ColumnWeightData(0, STATE_COLUMN_WIDTH, false));
ColumnViewerToolTipSupport.enableFor(_viewer);
_viewer.setContentProvider(new ContentProvider());
}
/**
* @see org.eclipse.jface.viewers.Viewer#getControl()
*/
@Override
public Control getControl() {
return _viewer.getControl();
}
/**
* @see org.eclipse.jface.viewers.Viewer#getInput()
*/
@Override
public Input getInput() {
return _input;
}
/**
* @see org.eclipse.jface.viewers.Viewer#getSelection()
*/
@Override
public ISelection getSelection() {
return _viewer.getSelection();
}
/**
* Return the shell of this viewer
* @return a non-null shell
*/
protected Shell getShell() {
return getControl().getShell();
}
/**
* @see org.eclipse.jface.viewers.Viewer#inputChanged(java.lang.Object, java.lang.Object)
*/
@Override
protected void inputChanged(Object input_p, Object oldInput_p) {
if (oldInput_p instanceof Input)
((Input)oldInput_p).removePropertyChangeListener(_configUpdater);
if (input_p instanceof Input)
((Input)input_p).addPropertyChangeListener(_configUpdater);
}
/**
* @see org.eclipse.jface.viewers.Viewer#refresh()
*/
@Override
public void refresh() {
_viewer.refresh(true);
}
/**
* @see org.eclipse.jface.viewers.Viewer#setInput(java.lang.Object)
*/
@Override
public void setInput(Object input_p) {
if (input_p instanceof Input) {
Object oldInput = getInput();
_input = (Input)input_p;
inputChanged(_input, oldInput);
_viewer.setInput(input_p);
}
}
/**
* @see org.eclipse.jface.viewers.Viewer#setSelection(org.eclipse.jface.viewers.ISelection, boolean)
*/
@Override
public void setSelection(ISelection selection_p, boolean reveal_p) {
_viewer.setSelection(selection_p, reveal_p);
}
/**
* The input for the viewer.
*/
public static class Input implements IPropertyChangeNotifier {
/** The name of the "has changes" property */
public static final String PROPERTY_HAS_CHANGES = "PROPERTY_CATEGORIES_HAS_CHANGES"; //$NON-NLS-1$
/** The name of the "configuration" property */
public static final String PROPERTY_CONFIGURATION = "PROPERTY_CATEGORIES_CONFIGURATION_CHANGES"; //$NON-NLS-1$
/** The non-null wrapped node */
private final EMFDiffNode _node;
/** The non-null set of property change listeners */
private final Set<IPropertyChangeListener> _changeListeners;
/** The non-null set of modified categories along with their new state */
private final Map<IDifferenceCategory, CategoryState> _changedCategories;
/**
* Constructor
* @param node_p a non-null node
*/
public Input(EMFDiffNode node_p) {
_node = node_p;
_changedCategories = new HashMap<IDifferenceCategory, CategoryViewer.CategoryState>();
_changeListeners = new HashSet<IPropertyChangeListener>(1);
}
/**
* Register the given state for the given category as a change
* @param category_p a non-null category
* @param state_p a non-null state
* @return whether this 'change' had any impact
*/
public boolean addChange(IDifferenceCategory category_p, CategoryState state_p) {
boolean hadChanges = hasChanges();
boolean result = addSilentChange(category_p, state_p);
boolean hasChanges = hasChanges();
if (hasChanges != hadChanges) {
// Lost its last change or gained its first change
firePropertyChangeEvent(PROPERTY_HAS_CHANGES, Boolean.valueOf(hasChanges));
}
return result;
}
/**
* Register the given state for the given category as a change without
* notifying listeners
* @param category_p a non-null category
* @param state_p a non-null state
* @return whether this 'change' had any impact
*/
protected boolean addSilentChange(IDifferenceCategory category_p, CategoryState state_p) {
boolean result;
CategoryState currentState = getActualState(category_p);
if (currentState == state_p) {
// Re-set state to the original one
CategoryState previousChange = _changedCategories.remove(category_p);
result = previousChange != null;
} else {
// Actual change
CategoryState previousState = _changedCategories.put(category_p, state_p);
result = previousState != state_p;
}
return result;
}
/**
* @see org.eclipse.compare.IPropertyChangeNotifier#addPropertyChangeListener(org.eclipse.jface.util.IPropertyChangeListener)
*/
public void addPropertyChangeListener(IPropertyChangeListener listener_p) {
_changeListeners.add(listener_p);
}
/**
* Apply the current changes
*/
public void applyChanges() {
if (hasChanges()) {
for (Map.Entry<IDifferenceCategory, CategoryState> change : _changedCategories.entrySet()) {
applyChange(change.getKey(), change.getValue());
}
_changedCategories.clear();
_node.updateDifferenceNumbers();
firePropertyChangeEvent(PROPERTY_HAS_CHANGES, Boolean.FALSE);
}
}
/**
* Set the given category to the given state
* @param category_p a non-null category
* @param state_p a non-null state
*/
protected void applyChange(IDifferenceCategory category_p, CategoryState state_p) {
switch (state_p) {
case FILTERED:
category_p.setInFocusMode(false);
category_p.setActive(true);
break;
case FOCUSED:
category_p.setInFocusMode(true);
category_p.setActive(true);
break;
default: //NORMAL
category_p.setActive(false);
}
}
/**
* Notify listeners of a property change event
* @param propertyName_p the non-null name of the property
* @param newValue_p the potentially null, new value of the property
*/
protected void firePropertyChangeEvent(String propertyName_p, Object newValue_p) {
PropertyChangeEvent event = new PropertyChangeEvent(
this, propertyName_p, null, newValue_p);
for (IPropertyChangeListener listener : _changeListeners) {
listener.propertyChange(event);
}
}
/**
* Return the actual state of the given category, ignoring current changes
* @param category_p a non-null difference category
* @return a non-null object
*/
protected CategoryState getActualState(IDifferenceCategory category_p) {
return !category_p.isActive()? NORMAL: category_p.isInFocusMode()? FOCUSED: FILTERED;
}
/**
* Return the node wrapped by this input
* @return a non-null object
*/
public EMFDiffNode getNode() {
return _node;
}
/**
* Return the state of the given category, taking into account current changes
* @param category_p a non-null category
* @return a non-null state
*/
public CategoryState getStateWithChanges(IDifferenceCategory category_p) {
CategoryState result = _changedCategories.get(category_p);
if (result == null)
result = getActualState(category_p);
return result;
}
/**
* Return whether there are non-applied changes
*/
public boolean hasChanges() {
return !_changedCategories.isEmpty();
}
/**
* @see org.eclipse.compare.IPropertyChangeNotifier#removePropertyChangeListener(org.eclipse.jface.util.IPropertyChangeListener)
*/
public void removePropertyChangeListener(IPropertyChangeListener listener_p) {
_changeListeners.remove(listener_p);
}
/**
* Remove all listeners
*/
public void removePropertyChangeListeners() {
_changeListeners.clear();
}
/**
* Reset the states of the current categories to match the default ones
*/
public void resetToDefault() {
boolean hadChanges = hasChanges();
boolean hadImpact = false;
CategoryManager manager = _node.getCategoryManager();
Collection<IDifferenceCategory> defaultConfig = manager.getDefaultConfiguration();
for (IDifferenceCategory defaultCat : defaultConfig) {
String id = defaultCat.getID();
IDifferenceCategory actualCat = manager.getCategory(id);
if (actualCat != null) {
CategoryState defaultState = getActualState(defaultCat);
boolean hasImpact = addSilentChange(actualCat, defaultState);
hadImpact = hadImpact || hasImpact;
}
}
boolean hasChanges = hasChanges();
if (hasChanges != hadChanges) {
// Lost its last change or gained its first change
firePropertyChangeEvent(PROPERTY_HAS_CHANGES, Boolean.valueOf(hasChanges));
}
if (hadImpact)
firePropertyChangeEvent(PROPERTY_CONFIGURATION, null);
}
/**
* Set the state of all categories to the given one, when possible
* @param state_p a non-null state
*/
public void setAll(CategoryState state_p) {
boolean hadImpact = false;
boolean hadChanges = hasChanges();
for (IDifferenceCategory cat : getNode().getCategoryManager().getCategories()) {
if (cat.isVisible() && cat.isModifiable()) {
boolean hasImpact = addSilentChange(cat, state_p);
hadImpact = hadImpact || hasImpact;
}
}
boolean hasChanges = hasChanges();
if (hasChanges != hadChanges) {
// Lost its last change or gained its first change
firePropertyChangeEvent(PROPERTY_HAS_CHANGES, Boolean.valueOf(hasChanges));
}
if (hadImpact)
firePropertyChangeEvent(PROPERTY_CONFIGURATION, null);
}
}
/**
* The content provider for the viewer.
*/
protected static class ContentProvider implements ITreeContentProvider {
/** The current input */
private Input _input = null;
/**
* @see org.eclipse.jface.viewers.IContentProvider#dispose()
*/
public void dispose() {
_input = null;
}
/**
* @see org.eclipse.jface.viewers.ITreeContentProvider#getChildren(java.lang.Object)
*/
public Object[] getChildren(Object parentElement_p) {
Object[] result;
if (parentElement_p instanceof IDifferenceCategorySet) {
IDifferenceCategorySet catSet = (IDifferenceCategorySet)parentElement_p;
List<IDifferenceCategoryItem> listResult;
if (_input == null)
listResult = catSet.getChildren();
else
listResult = _input.getNode().getCategoryManager().getUIChildrenItems(catSet);
result = listResult.toArray();
} else {
result = new Object[0];
}
return result;
}
/**
* @see org.eclipse.jface.viewers.IStructuredContentProvider#getElements(java.lang.Object)
*/
public Object[] getElements(Object inputElement_p) {
EMFDiffNode node = ((Input)inputElement_p).getNode();
return node.getCategoryManager().getUIRootItems().toArray();
}
/**
* @see org.eclipse.jface.viewers.ITreeContentProvider#getParent(java.lang.Object)
*/
public Object getParent(Object element_p) {
Object result = null;
if (element_p instanceof IDifferenceCategoryItem)
result = ((IDifferenceCategoryItem)element_p).getParent();
return result;
}
/**
* @see org.eclipse.jface.viewers.ITreeContentProvider#hasChildren(java.lang.Object)
*/
public boolean hasChildren(Object element_p) {
return getChildren(element_p).length > 0;
}
/**
* @see org.eclipse.jface.viewers.IContentProvider#inputChanged(org.eclipse.jface.viewers.Viewer, java.lang.Object, java.lang.Object)
*/
public void inputChanged(Viewer viewer_p, Object oldInput_p,
Object newInput_p) {
if (newInput_p instanceof Input)
_input = (Input)newInput_p;
}
}
/**
* The label provider for the hierarchical column.
*/
protected class HierarchyLabelProvider extends ColumnLabelProvider {
/**
* @see org.eclipse.jface.viewers.ColumnLabelProvider#getImage(java.lang.Object)
*/
@Override
public Image getImage(Object element_p) {
Image result = null;
if (element_p instanceof IDifferenceCategoryItem) {
IDifferenceCategoryItem catItem = (IDifferenceCategoryItem)element_p;
EMFDiffNode node = CategoryViewer.this.getInput().getNode();
result = catItem.getImage(node);
}
return result;
}
/**
* @see org.eclipse.jface.viewers.ColumnLabelProvider#getText(java.lang.Object)
*/
@Override
public String getText(Object element_p) {
String result;
if (element_p instanceof IDifferenceCategoryItem) {
IDifferenceCategoryItem catItem = (IDifferenceCategoryItem)element_p;
EMFDiffNode node = CategoryViewer.this.getInput().getNode();
result = catItem.getText(node);
} else {
result = super.getText(element_p);
}
return result;
}
/**
* @see org.eclipse.jface.viewers.CellLabelProvider#getToolTipText(java.lang.Object)
*/
@Override
public String getToolTipText(Object element_p) {
String result = null;
if (element_p instanceof IDifferenceCategoryItem) {
IDifferenceCategoryItem catItem = (IDifferenceCategoryItem)element_p;
EMFDiffNode node = CategoryViewer.this.getInput().getNode();
result = catItem.getDescription(node);
}
return result;
}
}
/**
* The label provider for the state columns.
*/
protected class StateLabelProvider extends ColumnLabelProvider {
/** The non-null state being handled */
protected final CategoryState _state;
/**
* Constructor
* @param state_p the non-null state to handle
*/
public StateLabelProvider(CategoryState state_p) {
_state = state_p;
}
/**
* @see org.eclipse.jface.viewers.ColumnLabelProvider#getImage(java.lang.Object)
*/
@Override
public Image getImage(Object element_p) {
Image result = null;
if (element_p instanceof IDifferenceCategory) {
IDifferenceCategory cat = (IDifferenceCategory)element_p;
boolean selected = isSelected(cat);
ImageID imageId;
if (cat.isModifiable())
imageId = selected? ImageID.CHECKED: ImageID.UNCHECKED;
else
imageId = selected? ImageID.CHECKED_DISABLED: ImageID.UNCHECKED_DISABLED;
result = EMFDiffMergeUIPlugin.getDefault().getImage(imageId);
}
return result;
}
/**
* @see org.eclipse.jface.viewers.ColumnLabelProvider#getText(java.lang.Object)
*/
@Override
public String getText(Object element_p) {
return null;
}
/**
* @see org.eclipse.jface.viewers.CellLabelProvider#getToolTipText(java.lang.Object)
*/
@Override
public String getToolTipText(Object element_p) {
String result;
switch (_state) {
case FILTERED:
result = Messages.CategoryViewer_FilteredStateTooltip;
break;
case FOCUSED:
result = Messages.CategoryViewer_FocusedStateTooltip;
break;
default: //NORMAL
result = Messages.CategoryViewer_NormalStateTooltip;
}
return result;
}
/**
* Specify whether the given category is in the state represented by this label provider
* @param category_p a non-null difference category
*/
protected boolean isSelected(IDifferenceCategory category_p) {
return _state == CategoryViewer.this.getInput().getStateWithChanges(category_p);
}
}
/**
* Editing support for the state cells.
*/
protected class StateEditingSupport extends EditingSupport {
/** The non-null state being handled */
protected final CategoryState _state;
/**
* Constructor
* @param viewer_p the non-null column viewer this editing support is for
* @param state_p the non-null state to handle
*/
public StateEditingSupport(ColumnViewer viewer_p, CategoryState state_p) {
super(viewer_p);
_state = state_p;
}
/**
* @see org.eclipse.jface.viewers.EditingSupport#getCellEditor(java.lang.Object)
*/
@Override
protected CellEditor getCellEditor(Object element_p) {
return new CellEditor() {
// Inspired by EGit's ClickableCellEditor
/**
* @see org.eclipse.jface.viewers.CellEditor#activate
*/
@Override
public void activate() {
// Trigger setValue on editing support
fireApplyEditorValue();
}
/**
* @see org.eclipse.jface.viewers.CellEditor#activate(org.eclipse.jface.viewers.ColumnViewerEditorActivationEvent)
*/
@Override
public void activate(ColumnViewerEditorActivationEvent activationEvent_p) {
if (activationEvent_p.eventType != ColumnViewerEditorActivationEvent.TRAVERSAL)
// All mouse, key and programmatic events, excluding mouse traversal events
super.activate(activationEvent_p);
}
/**
* @see org.eclipse.jface.viewers.CellEditor#createControl(org.eclipse.swt.widgets.Composite)
*/
@Override
protected Control createControl(Composite parent_p) {
return null;
}
/**
* @see org.eclipse.jface.viewers.CellEditor#doGetValue()
*/
@Override
protected Object doGetValue() {
return null;
}
/**
* @see org.eclipse.jface.viewers.CellEditor#doSetFocus()
*/
@Override
protected void doSetFocus() {
// Nothing needed
}
/**
* @see org.eclipse.jface.viewers.CellEditor#doSetValue(java.lang.Object)
*/
@Override
protected void doSetValue(Object value_p) {
// Nothing needed
}
};
}
/**
* @see org.eclipse.jface.viewers.EditingSupport#canEdit(java.lang.Object)
*/
@Override
protected boolean canEdit(Object element_p) {
boolean result = false;
if (element_p instanceof IDifferenceCategory) {
IDifferenceCategory cat = (IDifferenceCategory)element_p;
result = cat.isModifiable();
}
return result;
}
/**
* @see org.eclipse.jface.viewers.EditingSupport#getValue(java.lang.Object)
*/
@Override
protected Object getValue(Object element_p) {
return null;
}
/**
* @see org.eclipse.jface.viewers.EditingSupport#setValue(java.lang.Object, java.lang.Object)
*/
@Override
protected void setValue(Object element_p, Object value_p) {
if (element_p instanceof IDifferenceCategory) {
IDifferenceCategory cat = (IDifferenceCategory)element_p;
selectState(cat);
getViewer().update(cat, null);
}
}
/**
* Set the given category in the state handled by this editing support
* @param category_p a non-null difference category
*/
protected void selectState(IDifferenceCategory category_p) {
if (category_p.isModifiable())
CategoryViewer.this.getInput().addChange(category_p, _state);
}
}
}