/******************************************************************************* * Copyright (c) 2010, 2015 Tasktop Technologies 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: * Tasktop Technologies - initial API and implementation * Itema AS - bug 329897 select event type on open if available * Itema AS - bug 330064 notification filtering and model persistence * Itema AS - bug 331424 handle default event-sink action associations *******************************************************************************/ package org.eclipse.mylyn.internal.commons.notifications.ui; import java.util.Collection; import java.util.List; import org.eclipse.jface.dialogs.Dialog; import org.eclipse.jface.layout.GridDataFactory; import org.eclipse.jface.preference.IPreferenceStore; import org.eclipse.jface.preference.PreferencePage; import org.eclipse.jface.resource.ImageDescriptor; import org.eclipse.jface.viewers.CheckStateChangedEvent; import org.eclipse.jface.viewers.CheckboxTableViewer; import org.eclipse.jface.viewers.ICheckStateListener; import org.eclipse.jface.viewers.ICheckStateProvider; import org.eclipse.jface.viewers.IElementComparer; import org.eclipse.jface.viewers.ISelectionChangedListener; import org.eclipse.jface.viewers.IStructuredContentProvider; import org.eclipse.jface.viewers.IStructuredSelection; import org.eclipse.jface.viewers.ITreeContentProvider; import org.eclipse.jface.viewers.LabelProvider; import org.eclipse.jface.viewers.SelectionChangedEvent; import org.eclipse.jface.viewers.StructuredSelection; import org.eclipse.jface.viewers.TreeViewer; import org.eclipse.jface.viewers.Viewer; import org.eclipse.mylyn.commons.ui.CommonImages; import org.eclipse.mylyn.commons.workbench.SubstringPatternFilter; 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.FillLayout; import org.eclipse.swt.layout.GridLayout; import org.eclipse.swt.widgets.Button; 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.Text; import org.eclipse.ui.IWorkbench; import org.eclipse.ui.IWorkbenchPreferencePage; import org.eclipse.ui.dialogs.FilteredTree; /** * @author Steffen Pingel * @author Torkild Ulvøy Resheim */ public class NotificationsPreferencesPage extends PreferencePage implements IWorkbenchPreferencePage { /** * We need this in order to make sure that the correct element is selected in the {@link TreeViewer} when the * selection is set. * * @author Torkild Ulvøy Resheim */ public class NotificationEventComparer implements IElementComparer { public boolean equals(Object a, Object b) { if (a instanceof NotificationEvent && b instanceof NotificationEvent) { String idA = ((NotificationEvent) a).getId(); String idB = ((NotificationEvent) b).getId(); return (idA.equals(idB)); } return a.equals(b); } public int hashCode(Object element) { return element.hashCode(); } } private static final Object[] EMPTY = new Object[0]; private final class EventContentProvider implements ITreeContentProvider { public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { // ignore } public void dispose() { // ignore } public boolean hasChildren(Object element) { if (element instanceof NotificationCategory) { return ((NotificationCategory) element).getEvents().size() > 0; } return false; } public Object getParent(Object element) { if (element instanceof NotificationEvent) { return ((NotificationEvent) element).getCategory(); } return null; } public Object[] getElements(Object inputElement) { if (inputElement instanceof Object[]) { return (Object[]) inputElement; } else { return EMPTY; } } public Object[] getChildren(Object parentElement) { if (parentElement instanceof NotificationCategory) { return ((NotificationCategory) parentElement).getEvents().toArray(); } return EMPTY; } } private final class NotifiersContentProvider implements IStructuredContentProvider { private NotificationHandler handler; public void dispose() { // ignore } public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { if (newInput instanceof NotificationHandler) { handler = (NotificationHandler) newInput; } else { handler = null; } } public Object[] getElements(Object inputElement) { if (handler != null) { return handler.getActions().toArray(); } else { return EMPTY; } } } public final class EventLabelProvider extends LabelProvider { @Override public String getText(Object element) { if (element instanceof NotificationElement) { NotificationElement item = (NotificationElement) element; return item.getLabel(); } return super.getText(element); } @Override public Image getImage(Object element) { if (element instanceof NotificationEvent) { NotificationEvent item = (NotificationEvent) element; if (model.isSelected(item)) { return CommonImages.getImage(CommonImages.CHECKED); } else { return null; } } if (element instanceof NotificationElement) { NotificationElement item = (NotificationElement) element; ImageDescriptor imageDescriptor = item.getImageDescriptor(); if (imageDescriptor != null) { return CommonImages.getImage(imageDescriptor); } } return super.getImage(element); } } private TreeViewer eventsViewer; private CheckboxTableViewer notifiersViewer; private Button enableNotificationsButton; private NotificationModel model; private Text descriptionText; public NotificationsPreferencesPage() { } @Override public IPreferenceStore getPreferenceStore() { return NotificationsPlugin.getDefault().getPreferenceStore(); } @Override protected Control createContents(Composite parent) { model = NotificationsPlugin.getDefault().createModelWorkingCopy(); Composite composite = new Composite(parent, SWT.NONE); composite.setLayout(new GridLayout(2, false)); enableNotificationsButton = new Button(composite, SWT.CHECK); GridDataFactory.fillDefaults().span(2, 1).applyTo(enableNotificationsButton); enableNotificationsButton.setText(Messages.NotificationsPreferencesPage_Enable_Notifications_Text); enableNotificationsButton.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { updateEnablement(); } }); Label label = new Label(composite, SWT.NONE); label.setText(" "); //$NON-NLS-1$ GridDataFactory.fillDefaults().span(2, 1).applyTo(label); label = new Label(composite, SWT.NONE); label.setText(Messages.NotificationsPreferencesPage_Events_Label); label = new Label(composite, SWT.NONE); label.setText(Messages.NotificationsPreferencesPage_Notifiers_Label); // Create the tree showing all the various notification types FilteredTree tree = new FilteredTree(composite, SWT.BORDER, new SubstringPatternFilter(), true); eventsViewer = tree.getViewer(); GridDataFactory.fillDefaults().span(1, 2).grab(false, true).applyTo(tree); eventsViewer.setComparer(new NotificationEventComparer()); eventsViewer.setContentProvider(new EventContentProvider()); eventsViewer.setLabelProvider(new EventLabelProvider()); eventsViewer.setInput(model.getCategories().toArray()); eventsViewer.expandAll(); eventsViewer.addSelectionChangedListener(new ISelectionChangedListener() { public void selectionChanged(SelectionChangedEvent event) { Object input = getDetailsInput((IStructuredSelection) event.getSelection()); notifiersViewer.setInput(input); Object item = ((IStructuredSelection) event.getSelection()).getFirstElement(); if (item instanceof NotificationEvent) { descriptionText.setText(((NotificationEvent) item).getDescription()); notifiersViewer.getControl().setEnabled(true); } else { descriptionText.setText(" "); //$NON-NLS-1$ notifiersViewer.getControl().setEnabled(false); } } private Object getDetailsInput(IStructuredSelection selection) { Object item = selection.getFirstElement(); if (item instanceof NotificationEvent) { return model.getOrCreateNotificationHandler((NotificationEvent) item); } return null; } }); // Create the table listing all notification sinks available for the selected event type. notifiersViewer = CheckboxTableViewer.newCheckList(composite, SWT.BORDER); GridDataFactory.fillDefaults().grab(true, true).applyTo(notifiersViewer.getControl()); notifiersViewer.setContentProvider(new NotifiersContentProvider()); notifiersViewer.setLabelProvider(new EventLabelProvider()); notifiersViewer.addCheckStateListener(new ICheckStateListener() { public void checkStateChanged(CheckStateChangedEvent event) { NotificationAction action = (NotificationAction) event.getElement(); action.setSelected(event.getChecked()); model.setDirty(true); eventsViewer.refresh(); } }); notifiersViewer.setCheckStateProvider(new ICheckStateProvider() { public boolean isChecked(Object element) { return ((NotificationAction) element).isSelected(); } public boolean isGrayed(Object element) { return false; } }); notifiersViewer.addSelectionChangedListener(new ISelectionChangedListener() { public void selectionChanged(SelectionChangedEvent event) { Object item = ((IStructuredSelection) event.getSelection()).getFirstElement(); if (item instanceof NotificationAction) { // TODO show configuration pane } } }); Group group = new Group(composite, SWT.NONE); GridDataFactory.fillDefaults().hint(150, SWT.DEFAULT).grab(true, true).applyTo(group); group.setText(Messages.NotificationsPreferencesPage_Descriptions_Label); FillLayout layout = new FillLayout(); layout.marginHeight = 5; layout.marginWidth = 5; group.setLayout(layout); descriptionText = new Text(group, SWT.WRAP); descriptionText.setBackground(group.getBackground()); // Button testButton = new Button(composite, SWT.NONE); // testButton.setText("Test"); // testButton.addSelectionListener(new SelectionAdapter() { // @Override // public void widgetSelected(SelectionEvent e) { // ISelection selection = eventsViewer.getSelection(); // if (selection instanceof IStructuredSelection) { // Object object = ((IStructuredSelection) selection).getFirstElement(); // if (object instanceof NotificationEvent) { // final NotificationEvent event = (NotificationEvent) object; // getControl().getDisplay().asyncExec(new Runnable() { // public void run() { // Notifications.getService().notify( // Collections.singletonList(new TestNotification(event))); // } // }); // } // } // } // // }); reset(); Dialog.applyDialogFont(composite); return composite; } @Override public void applyData(Object data) { // We may or may not have a NotificationEvent supplied when this // preference dialog is opened. If we do have this data we want to // highlight the appropriate instance. if (data instanceof String && model != null) { String selectedEventId = (String) data; Collection<NotificationCategory> items = model.getCategories(); NotificationEvent selectedEvent = null; for (NotificationCategory notificationCategory : items) { List<NotificationEvent> event = notificationCategory.getEvents(); for (NotificationEvent notificationEvent : event) { if (notificationEvent.getId().equals(selectedEventId)) { selectedEvent = notificationEvent; break; } } } if (selectedEvent != null) { eventsViewer.setSelection(new StructuredSelection(selectedEvent), true); } } } private void updateEnablement() { boolean enabled = enableNotificationsButton.getSelection(); eventsViewer.getControl().setEnabled(enabled); notifiersViewer.getControl().setEnabled(enabled);// FIXME enabled && notifiersViewer.getInput() != null); descriptionText.setEnabled(enabled); if (!enabled) { eventsViewer.setSelection(StructuredSelection.EMPTY); } } public void init(IWorkbench workbench) { // ignore } public void reset() { enableNotificationsButton.setSelection(getPreferenceStore().getBoolean( NotificationsPlugin.PREF_NOTICATIONS_ENABLED)); updateEnablement(); } @Override public boolean performOk() { if (model != null) { getPreferenceStore().setValue(NotificationsPlugin.PREF_NOTICATIONS_ENABLED, enableNotificationsButton.getSelection()); if (model.isDirty()) { NotificationsPlugin.getDefault().saveWorkingCopy(model); model.setDirty(false); } } return super.performOk(); } @Override protected void performDefaults() { enableNotificationsButton.setSelection(getPreferenceStore().getDefaultBoolean( NotificationsPlugin.PREF_NOTICATIONS_ENABLED)); for (NotificationCategory category : model.getCategories()) { for (NotificationEvent event : category.getEvents()) { NotificationHandler handler = model.getOrCreateNotificationHandler(event); for (NotificationAction action : handler.getActions()) { action.setSelected(event.defaultHandledBySink(action.getSinkDescriptor().getId())); } } } // assume that the model has become dirty model.setDirty(true); // refresh UI eventsViewer.refresh(); notifiersViewer.refresh(); updateEnablement(); } }