/******************************************************************************* * Copyright (c) 2014, 2016 Obeo and others. * 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: * Obeo - initial API and implementation * Simon Delisle - bug 495753 *******************************************************************************/ package org.eclipse.emf.compare.rcp.ui.internal.preferences; import static org.eclipse.jface.dialogs.MessageDialogWithToggle.ALWAYS; import static org.eclipse.jface.dialogs.MessageDialogWithToggle.NEVER; import static org.eclipse.jface.dialogs.MessageDialogWithToggle.PROMPT; import com.google.common.collect.ImmutableList; import com.google.common.collect.Lists; import com.google.common.collect.Sets; import com.google.common.collect.Sets.SetView; import java.util.Collection; import java.util.Collections; import java.util.Comparator; import java.util.List; import java.util.Set; import org.eclipse.core.databinding.DataBindingContext; import org.eclipse.core.databinding.beans.PojoProperties; import org.eclipse.core.databinding.observable.set.IObservableSet; import org.eclipse.core.runtime.IStatus; import org.eclipse.emf.compare.rcp.internal.extension.IItemDescriptor; import org.eclipse.emf.compare.rcp.internal.tracer.TracingConstant; import org.eclipse.emf.compare.rcp.ui.EMFCompareRCPUIPlugin; import org.eclipse.emf.compare.rcp.ui.internal.EMFCompareRCPUIMessages; import org.eclipse.emf.compare.rcp.ui.internal.structuremergeviewer.filters.impl.DifferenceFilterManager; import org.eclipse.emf.compare.rcp.ui.structuremergeviewer.filters.IDifferenceFilter; import org.eclipse.jface.databinding.viewers.IViewerObservableSet; import org.eclipse.jface.databinding.viewers.ViewersObservables; import org.eclipse.jface.dialogs.MessageDialogWithToggle; import org.eclipse.jface.layout.GridDataFactory; import org.eclipse.jface.layout.GridLayoutFactory; import org.eclipse.jface.preference.PreferencePage; import org.eclipse.jface.viewers.ArrayContentProvider; import org.eclipse.jface.viewers.CheckboxTableViewer; import org.eclipse.jface.viewers.ISelection; import org.eclipse.jface.viewers.ISelectionChangedListener; import org.eclipse.jface.viewers.IStructuredSelection; import org.eclipse.jface.viewers.LabelProvider; import org.eclipse.jface.viewers.SelectionChangedEvent; import org.eclipse.jface.viewers.StructuredSelection; import org.eclipse.jface.viewers.StructuredViewer; import org.eclipse.swt.SWT; import org.eclipse.swt.events.SelectionAdapter; import org.eclipse.swt.events.SelectionEvent; import org.eclipse.swt.layout.GridData; import org.eclipse.swt.layout.GridLayout; import org.eclipse.swt.widgets.Combo; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Control; import org.eclipse.swt.widgets.Group; import org.eclipse.swt.widgets.Label; import org.eclipse.swt.widgets.TabFolder; import org.eclipse.swt.widgets.TabItem; import org.eclipse.ui.IWorkbench; import org.eclipse.ui.IWorkbenchPreferencePage; /** * Preference page for {@link IDifferenceFilter}. * * @author <a href="mailto:arthur.daussy@obeo.fr">Arthur Daussy</a> */ public class FiltersPreferencePage extends PreferencePage implements IWorkbenchPreferencePage { /** Preference page ID. */ public static final String PAGE_ID = "org.eclipse.emf.compare.rcp.ui.preferencePage.filters"; //$NON-NLS-1$ /** Preference key holding synchronization behavior value. */ public static final String SYNCHRONIZATION_BEHAVIOR = "org.eclipse.emf.compare.rcp.ui.filters.syncbehavior"; //$NON-NLS-1$ /** Width hint for introduction label. */ private static final int INTRO_TEXT_WIDTH_HINT = 400; /** Values used for the combobox. */ private static final List<String> SYNC_VALUES = ImmutableList.of(ALWAYS, NEVER, PROMPT); /** Filter manager. Used to retrieve current and default configuration. */ private DifferenceFilterManager filterManager = null; /** Interactive content holding UI components for enabled/disabled filters. */ private InteractiveFilterUIContent defaultFilterInteractiveContent; /** Interactive content holding UI components for activated/deactivated filters. */ private InteractiveFilterUIContent activateFilterInteractiveContent; /** The tab used to choose enabled filters. */ private Composite enabledFilterTabComposite; /** The tab used to choose active filters. */ private Composite activateFilterTabComposite; /** Combo holding synchronization behavior preferences. */ private Combo combo; /** Field holding {@link SynchronizationBehavior} */ private String synchronizationBehaviorValue; public void init(IWorkbench workbench) { setPreferenceStore(EMFCompareRCPUIPlugin.getDefault().getPreferenceStore()); } @Override protected Control createContents(Composite parent) { Composite container = new Composite(parent, SWT.NONE); GridLayoutFactory.fillDefaults().applyTo(container); GridDataFactory.fillDefaults().grab(true, true).applyTo(container); createSynchronizationBehaviorContent(container); setComboInput(getCurrentSynchronizationBehavior()); TabFolder tabFolder = new TabFolder(container, SWT.NONE); GridDataFactory.fillDefaults().grab(true, true).applyTo(tabFolder); // Create tab to choose filters that are enabled by default createDefaultEnabledFilterTab(tabFolder); // Create tab to activate or deactivate totally filters createActivateFilterTab(tabFolder); return container; } /** * Create a tab to choose which filters to enable by default. * * @param tabFolder */ private void createDefaultEnabledFilterTab(TabFolder tabFolder) { enabledFilterTabComposite = createTabSkeleton(tabFolder, EMFCompareRCPUIMessages.getString("FiltersPreferencePage.select.tab.label"), //$NON-NLS-1$ EMFCompareRCPUIMessages.getString("FiltersPreferencePage.selectIntro.text")); //$NON-NLS-1$ if (filterManager == null) { filterManager = EMFCompareRCPUIPlugin.getDefault().getDifferenceFilterManager(); } defaultFilterInteractiveContent = new InteractiveFilterUIContent(enabledFilterTabComposite, filterManager.getAllFilters(), filterManager.getCurrentByDefaultFilters(), false); } /** * Create a tab to select which filters to activate or deactivate. * * @param tabFolder */ private void createActivateFilterTab(TabFolder tabFolder) { activateFilterTabComposite = createTabSkeleton(tabFolder, EMFCompareRCPUIMessages.getString("FiltersPreferencePage.activate.tab.label"), //$NON-NLS-1$ EMFCompareRCPUIMessages.getString("FiltersPreferencePage.activateIntro.text")); //$NON-NLS-1$ if (filterManager == null) { filterManager = EMFCompareRCPUIPlugin.getDefault().getDifferenceFilterManager(); } activateFilterInteractiveContent = new InteractiveFilterUIContent(activateFilterTabComposite, filterManager.getAllFilters(), filterManager.getCurrentInactiveFilters(), true); } /** * Create skeleton of a tab. * * @param tabFolder * @param tabLabel * @param introText * Text use as description a tab * @return Main composite of the tab */ private Composite createTabSkeleton(TabFolder tabFolder, String tabLabel, String introText) { TabItem tbtmMain = new TabItem(tabFolder, SWT.NONE); tbtmMain.setText(tabLabel); Composite tabComposite = new Composite(tabFolder, SWT.NONE); tbtmMain.setControl(tabComposite); GridLayout layout = new GridLayout(1, true); tabComposite.setLayout(layout); GridData layoutData = new GridData(SWT.FILL, SWT.FILL, false, false, 1, 1); tabComposite.setLayoutData(layoutData); // Description text Label introductionText = new Label(tabComposite, SWT.WRAP); GridDataFactory.fillDefaults().grab(true, false).hint(INTRO_TEXT_WIDTH_HINT, SWT.DEFAULT) .applyTo(introductionText); introductionText.setText(introText); return tabComposite; } /** * Content for synchronization behavior preferences. * * @param parent */ private void createSynchronizationBehaviorContent(Composite parent) { Composite synchronizationComposite = new Composite(parent, SWT.NONE); GridLayoutFactory.fillDefaults().numColumns(2).applyTo(synchronizationComposite); Label label = new Label(synchronizationComposite, SWT.WRAP); label.setText(EMFCompareRCPUIMessages.getString("InteractiveFilterUIContent.sync.behavior.label")); //$NON-NLS-1$ label.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, false, false)); combo = new Combo(synchronizationComposite, SWT.DROP_DOWN | SWT.READ_ONLY); for (String comboLabel : SYNC_VALUES) { combo.add(comboLabel); } combo.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, false, true)); combo.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { if (combo.equals(e.getSource())) { synchronizationBehaviorValue = combo.getItem(combo.getSelectionIndex()); } } }); } /** * Select the correct behavior in the interactive UI. * * @param behavior */ public void setComboInput(String behavior) { int index = 0; for (String value : SYNC_VALUES) { if (value.equals(behavior)) { combo.select(index); synchronizationBehaviorValue = behavior; } index++; } } /** * Gets the current value of the filter synchronization behavior. * <p> * This value can only be one of the following: * <ul> * <li>{@link MessageDialogWithToggle#PROMPT}</li> * <li>{@link MessageDialogWithToggle#ALWAYS}</li> * <li>{@link MessageDialogWithToggle#NEVER}</li> * </ul> * </p> * * @return String. */ public String getCurrentSynchronizationBehavior() { String value = getPreferenceStore().getString(SYNCHRONIZATION_BEHAVIOR); if (value == null || !SYNC_VALUES.contains(value)) { value = getDefaultSynchronizationBehavior(); } return value; } /** * @return The default value of filter synchronization behavior. */ public String getDefaultSynchronizationBehavior() { return MessageDialogWithToggle.PROMPT; } /** * Set the current value of the filter synchronization behavior. * * @param newBehavior * New value. */ public void setCurrentSynchronizationBehavior(String newBehavior) { if (getDefaultSynchronizationBehavior().equals(newBehavior)) { getPreferenceStore().setToDefault(SYNCHRONIZATION_BEHAVIOR); } else { getPreferenceStore().setValue(SYNCHRONIZATION_BEHAVIOR, newBehavior); } // Trace preferences values if (TracingConstant.CONFIGURATION_TRACING_ACTIVATED) { StringBuilder builder = new StringBuilder(); // Print each preferences builder.append("Preference ").append(SYNCHRONIZATION_BEHAVIOR).append(":\n"); //$NON-NLS-1$ //$NON-NLS-2$ String preferenceValue = getPreferenceStore().getString(SYNCHRONIZATION_BEHAVIOR); builder.append(preferenceValue); EMFCompareRCPUIPlugin.getDefault().log(IStatus.INFO, builder.toString()); } } /** * {@inheritDoc} */ @Override public boolean performOk() { filterManager.setCurrentByDefaultFilters(defaultFilterInteractiveContent.getCheckedFilter()); filterManager.setCurrentActiveFilters(activateFilterInteractiveContent.getCheckedFilter()); setCurrentSynchronizationBehavior(synchronizationBehaviorValue); return super.performOk(); } /** * {@inheritDoc} */ @Override protected void performDefaults() { if (activateFilterTabComposite.isVisible()) { activateFilterInteractiveContent.checkElements(filterManager.getAllFilters()); } if (enabledFilterTabComposite.isVisible()) { defaultFilterInteractiveContent.checkElements(filterManager.getInitialByDefaultFilters()); setComboInput(getDefaultSynchronizationBehavior()); } super.performDefaults(); } /** * Interactive UI for filter preference page. * * @author <a href="mailto:arthur.daussy@obeo.fr">Arthur Daussy</a> */ private static class InteractiveFilterUIContent { /** Height hint for the description label. */ private static final int DESCRIPTION_LABEL_HEIGHT_HINT = 50; /** Width hint for configuration composite. */ private static final int DESCRIPTION_LABEL_WIDTH_HINT = 400; /** Text that will be updated with the description of the viewer. */ private final Label descriptionText; /** Viewer of {@link IDifferenceFilter}. */ private CheckboxTableViewer viewer; /** DataHolder for enabled/disabled {@link IDifferenceFilter}. */ private FilterDataHolder dataHolder = new FilterDataHolder(); /** DataHolder for activated/deactivated {@link IDifferenceFilter}. */ private FilterDataHolder allFilters = new FilterDataHolder(); private InteractiveFilterUIContent(Composite parent, Collection<? extends IDifferenceFilter> filters, Collection<? extends IDifferenceFilter> defaultCheck, boolean isDeactivateTab) { super(); Composite contentComposite = new Composite(parent, SWT.NONE); GridLayoutFactory.fillDefaults().extendedMargins(0, 0, 10, 0).applyTo(contentComposite); contentComposite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 1, 1)); Label introductionText = new Label(contentComposite, SWT.WRAP); if (isDeactivateTab) { introductionText.setText( EMFCompareRCPUIMessages.getString("FiltersPreferencePage.INTRO_DEACTIVATE_TEXT")); //$NON-NLS-1$ } else { introductionText.setText( EMFCompareRCPUIMessages.getString("FiltersPreferencePage.INTRO_SELECT_TEXT")); //$NON-NLS-1$ } introductionText.setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, false, 1, 1)); // Engine chooser composite Composite viewerComposite = new Composite(contentComposite, SWT.NONE); GridLayoutFactory.fillDefaults().applyTo(viewerComposite); viewerComposite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 1, 1)); viewer = createViewer(viewerComposite); // Descriptor engine Text this.descriptionText = createDescriptionComposite(contentComposite); setViewerInput(Lists.newArrayList(filters)); if (isDeactivateTab) { SetView<IDifferenceFilter> activatedFilters = Sets.difference(Sets.newLinkedHashSet(filters), Sets.newLinkedHashSet(defaultCheck)); bindAndInit(activatedFilters); } else { bindAndInit(Sets.newLinkedHashSet(defaultCheck)); } allFilters.setFilters(Sets.newLinkedHashSet(filters)); } /** * @return All checked {@link IDifferenceFilter}. */ public Set<IDifferenceFilter> getCheckedFilter() { return dataHolder.getFilters(); } private void setViewerInput(List<IDifferenceFilter> filters) { Collections.sort(filters, new Comparator<IDifferenceFilter>() { public int compare(IDifferenceFilter o1, IDifferenceFilter o2) { if (o1 == o2) { return 0; } else if (o1 == null || o1.getLabel() == null) { return -1; } else if (o2 == null || o2.getLabel() == null) { return 1; } return o1.getLabel().compareTo(o2.getLabel()); } }); viewer.setInput(filters); select(filters.iterator().next()); } private CheckboxTableViewer createViewer(Composite viewerCompsite) { CheckboxTableViewer descriptorViewer = CheckboxTableViewer.newCheckList(viewerCompsite, SWT.BORDER | SWT.V_SCROLL | SWT.FULL_SELECTION); descriptorViewer.setContentProvider(ArrayContentProvider.getInstance()); descriptorViewer.setLabelProvider(new FilterLabelProvider()); GridData gd = new GridData(SWT.FILL, SWT.FILL, true, true, 1, 1); descriptorViewer.getControl().setLayoutData(gd); setViewer(descriptorViewer); return descriptorViewer; } private void bindAndInit(Set<IDifferenceFilter> defaultCheck) { if (dataHolder != null) { if (defaultCheck != null) { dataHolder.setFilters(defaultCheck); } // Bind data bindMultipleData(viewer, dataHolder); } } /** * Bind UI to data object. * * @param engineBindingProperty * @param descriptorViewer * @param dataObject */ private void bindMultipleData(CheckboxTableViewer descriptorViewer, FilterDataHolder dataObject) { DataBindingContext ctx = new DataBindingContext(); // Bind the button with the corresponding field in data IViewerObservableSet target = ViewersObservables.observeCheckedElements(descriptorViewer, IDifferenceFilter.class); IObservableSet model = PojoProperties.set(FilterDataHolder.class, FilterDataHolder.FIELD_NAME) .observe(dataObject); ctx.bindSet(target, model); } /** * Check element in the viewer. * * @param checkedFilter * Element to check. */ public void checkElements(Set<IDifferenceFilter> checkedFilter) { viewer.setCheckedElements(checkedFilter.toArray()); dataHolder.setFilters(checkedFilter); } /** * Composite for description. This composite hold the text widget that will update with the current * selection * * @param composite * @return */ private Label createDescriptionComposite(Composite composite) { Group descriptionComposite = new Group(composite, SWT.NONE); descriptionComposite.setText( EMFCompareRCPUIMessages.getString("InteractiveUIContent.descriptionComposite.label")); //$NON-NLS-1$ GridLayoutFactory.swtDefaults().applyTo(descriptionComposite); descriptionComposite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false, 1, 1)); Label engineDescriptionLabel = new Label(descriptionComposite, SWT.WRAP); GridDataFactory.fillDefaults().grab(true, false) .hint(DESCRIPTION_LABEL_WIDTH_HINT, DESCRIPTION_LABEL_HEIGHT_HINT) .applyTo(engineDescriptionLabel); return engineDescriptionLabel; } /** * Handle a selection in the viewer. Update related components. * * @param descriptor */ public void select(IDifferenceFilter descriptor) { // Update viewer viewer.setSelection(new StructuredSelection(descriptor), true); String description = descriptor.getDescription(); if (description != null) { descriptionText.setText(description); } else { descriptionText.setText(""); //$NON-NLS-1$ } } /** * @param viewer * A {@link StructuredViewer} of {@link IItemDescriptor} */ public void setViewer(CheckboxTableViewer inputViewer) { this.viewer = inputViewer; viewer.addSelectionChangedListener(new DescriptionListener()); } /** * Listener to update description text * * @author <a href="mailto:arthur.daussy@obeo.fr">Arthur Daussy</a> */ private final class DescriptionListener implements ISelectionChangedListener { public void selectionChanged(SelectionChangedEvent event) { ISelection selection = event.getSelection(); if (selection instanceof IStructuredSelection) { IStructuredSelection structSelection = (IStructuredSelection)selection; Object selected = structSelection.getFirstElement(); if (selected instanceof IDifferenceFilter) { IDifferenceFilter desc = (IDifferenceFilter)selected; String description = desc.getDescription(); if (description != null) { descriptionText.setText(description); } else { descriptionText.setText(""); //$NON-NLS-1$ } } } } } /** * Label provider for {@link IDifferenceFilter}. * * @author <a href="mailto:arthur.daussy@obeo.fr">Arthur Daussy</a> */ private static final class FilterLabelProvider extends LabelProvider { /** * {@inheritDoc} */ @Override public String getText(Object element) { if (element instanceof IDifferenceFilter) { return ((IDifferenceFilter)element).getLabel(); } return super.getText(element); } } /** * Data holder for checked {@link IDifferenceFilter}. * * @author <a href="mailto:arthur.daussy@obeo.fr">Arthur Daussy</a> */ private static final class FilterDataHolder { private static final String FIELD_NAME = "filters"; //$NON-NLS-1$ private Set<IDifferenceFilter> filters; public Set<IDifferenceFilter> getFilters() { return filters; } public void setFilters(Set<IDifferenceFilter> filters) { this.filters = filters; } } } }