blob: 9ecc9113b68c9ec986fcc5a5b299f31eaa0fad7f [file] [log] [blame]
/**
* Copyright (c) 2015 Codetrails GmbH.
* 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
*/
package org.eclipse.epp.internal.logging.aeri.ide.dialogs;
import static com.google.common.base.Preconditions.checkNotNull;
import static org.apache.commons.lang3.StringUtils.*;
import static org.eclipse.e4.ui.services.IServiceConstants.ACTIVE_SHELL;
import static org.eclipse.emf.databinding.EMFProperties.value;
import static org.eclipse.emf.databinding.FeaturePath.fromList;
import static org.eclipse.epp.internal.logging.aeri.ide.IDEWorkflow.*;
import static org.eclipse.epp.internal.logging.aeri.ide.IIdePackage.Literals.LOG_EVENT__OPTIONS;
import static org.eclipse.epp.internal.logging.aeri.ide.di.ImageRegistryCreationFunction.*;
import static org.eclipse.epp.internal.logging.aeri.ide.dialogs.ReviewDialog.ToolItemProperty.toolItemSelection;
import static org.eclipse.epp.internal.logging.aeri.ide.dialogs.UI.*;
import static org.eclipse.epp.internal.logging.aeri.ide.utils.IDEConstants.BUNDLE_ID;
import static org.eclipse.epp.logging.aeri.core.IModelPackage.Literals.*;
import static org.eclipse.jface.databinding.swt.WidgetProperties.text;
import java.util.List;
import javax.inject.Inject;
import javax.inject.Named;
import org.eclipse.core.databinding.Binding;
import org.eclipse.core.databinding.DataBindingContext;
import org.eclipse.core.databinding.observable.ChangeEvent;
import org.eclipse.core.databinding.observable.IChangeListener;
import org.eclipse.core.databinding.observable.list.IObservableList;
import org.eclipse.core.databinding.observable.value.IObservableValue;
import org.eclipse.core.runtime.preferences.IEclipsePreferences;
import org.eclipse.e4.core.contexts.Active;
import org.eclipse.e4.core.contexts.IEclipseContext;
import org.eclipse.e4.core.di.annotations.Optional;
import org.eclipse.e4.core.di.extensions.Preference;
import org.eclipse.e4.core.services.events.IEventBroker;
import org.eclipse.e4.ui.di.UISynchronize;
import org.eclipse.emf.common.notify.Notification;
import org.eclipse.emf.common.notify.impl.AdapterImpl;
import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.databinding.EMFProperties;
import org.eclipse.emf.databinding.FeaturePath;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.util.EcoreUtil;
import org.eclipse.epp.internal.logging.aeri.ide.IIdeFactory;
import org.eclipse.epp.internal.logging.aeri.ide.IIdePackage;
import org.eclipse.epp.internal.logging.aeri.ide.IInternalInput;
import org.eclipse.epp.internal.logging.aeri.ide.ILogEvent;
import org.eclipse.epp.internal.logging.aeri.ide.ILogEventGroup;
import org.eclipse.epp.internal.logging.aeri.ide.ILogEventsQueue;
import org.eclipse.epp.internal.logging.aeri.ide.IServerDescriptor;
import org.eclipse.epp.internal.logging.aeri.ide.l10n.Messages;
import org.eclipse.epp.internal.logging.aeri.ide.util.IdeSwitch;
import org.eclipse.epp.internal.logging.aeri.ide.utils.Formats;
import org.eclipse.epp.internal.logging.aeri.ide.utils.IDEConstants;
import org.eclipse.epp.logging.aeri.core.IReport;
import org.eclipse.epp.logging.aeri.core.Severity;
import org.eclipse.epp.logging.aeri.core.util.Reports;
import org.eclipse.jdt.annotation.Nullable;
import org.eclipse.jface.databinding.swt.ISWTObservableValue;
import org.eclipse.jface.databinding.swt.WidgetValueProperty;
import org.eclipse.jface.databinding.viewers.IViewerObservableValue;
import org.eclipse.jface.databinding.viewers.ObservableListContentProvider;
import org.eclipse.jface.databinding.viewers.ViewerProperties;
import org.eclipse.jface.dialogs.IDialogConstants;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.layout.GridDataFactory;
import org.eclipse.jface.preference.PreferenceDialog;
import org.eclipse.jface.resource.ImageRegistry;
import org.eclipse.jface.resource.JFaceResources;
import org.eclipse.jface.viewers.ArrayContentProvider;
import org.eclipse.jface.viewers.ComboViewer;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.LabelProvider;
import org.eclipse.jface.viewers.TableViewer;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.SashForm;
import org.eclipse.swt.custom.StyledText;
import org.eclipse.swt.events.ControlAdapter;
import org.eclipse.swt.events.ControlEvent;
import org.eclipse.swt.events.KeyAdapter;
import org.eclipse.swt.events.KeyEvent;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Link;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.ToolBar;
import org.eclipse.swt.widgets.ToolItem;
import org.eclipse.ui.dialogs.PreferencesUtil;
import org.eclipse.ui.forms.events.ExpansionAdapter;
import org.eclipse.ui.forms.events.ExpansionEvent;
import org.eclipse.ui.forms.widgets.ExpandableComposite;
@SuppressWarnings({ "null", "restriction" })
public class ReviewDialog extends MessageDialog {
public static final String CTX_STATE_REVIEW_IN_PROGRESS = BUNDLE_ID + ".di.review-in-progress"; //$NON-NLS-1$
private static final String PREF_REVIEW_DIALOG_WIDTH = "review-dialog-width"; //$NON-NLS-1$
private static final int DEFAULT_DIALOG_WIDTH = 600;
private static final int DEFAULT_DIALOG_HEIGHT = 320;
private SashForm reportsAndDetailsSash;
private TableViewer reportsViewer;
private SashForm commentAndPreviewSash;
private ComboViewer severity;
private StyledText comments;
private Composite previewArea;
private StyledText preview;
private ToolItem anonymizeMessagesOption;
private ToolItem anonymizeStackTraceOption;
private Composite detailsArea;
private ExpandableComposite previewTwistie;
private ToolBar addonsBar;
private ExpandableComposite commentTwistie;
private ShowPreviewListener showPreviewListener;
private IEclipseContext context;
private IEclipsePreferences prefs;
private IEventBroker broker;
private ImageRegistry registry;
@Nullable
private ILogEventGroup active;
private ILogEventsQueue queue;
private DataBindingContext dbContext;
private IViewerObservableValue selectedLogEvent;
private IObservableList events;
private IncomingEventsCopier incomingEventsCopier;
private UISynchronize uiSynchronize;
@Inject
public ReviewDialog(@Active @Optional ILogEventGroup active, ILogEventsQueue queue, ImageRegistry registry,
@Named(ACTIVE_SHELL) @Optional Shell parentShell, @Preference IEclipsePreferences prefs, UISynchronize uiSynchronize,
IEclipseContext context, IEventBroker broker) {
super(parentShell, Messages.DIALOG_TITLE_REVIEW, null, Messages.DIALOG_MESSAGE_REVIEW, MessageDialog.WARNING,
new String[] { Messages.BUTTON_TEXT_SEND, Messages.BUTTON_TEXT_DONT_SEND }, 0);
this.active = active;
this.queue = queue;
this.uiSynchronize = uiSynchronize;
this.registry = checkNotNull(registry);
this.prefs = prefs;
this.context = context;
this.broker = broker;
setShellStyle(SWT.MODELESS | SWT.DIALOG_TRIM | SWT.RESIZE | SWT.MAX);
setBlockOnOpen(false);
setupObservableList();
context.modify(CTX_STATE_REVIEW_IN_PROGRESS, true);
}
private void setupObservableList() {
final IInternalInput input = IIdeFactory.eINSTANCE.createInternalInput();
this.events = EMFProperties.list(IIdePackage.Literals.INTERNAL_INPUT__INPUT).observe(input);
for (ILogEventGroup group : queue.getGroups()) {
input.getInput().addAll(group.getEvents());
}
incomingEventsCopier = new IncomingEventsCopier(input);
queue.eAdapters().add(incomingEventsCopier);
}
@Override
protected void configureShell(Shell shell) {
super.configureShell(shell);
shell.setSize(prefs.getInt(PREF_REVIEW_DIALOG_WIDTH, DEFAULT_DIALOG_WIDTH), DEFAULT_DIALOG_HEIGHT);
}
@Override
public void create() {
super.create();
}
@Override
protected boolean customShouldTakeFocus() {
return false;
}
@Override
public Composite createCustomArea(final Composite parent) {
createReportsAndDetailsSash(parent);
createEventsTree(reportsAndDetailsSash);
createDetailsArea(reportsAndDetailsSash);
reportsAndDetailsSash.setWeights(new int[] { 1, 3 });
createPreferencesLink(parent);
createDatabindings();
if (active != null) {
EList<ILogEvent> events = active.getEvents();
ILogEvent event = events.get(0);
selectedLogEvent.setValue(event);
}
gdGrabHV().indent(0, 10).applyTo(reportsAndDetailsSash);
return reportsAndDetailsSash;
}
private void createDetailsArea(Composite parent) {
detailsArea = new Composite(reportsAndDetailsSash, SWT.NONE);
// detailsArea.setBackground(parent.getDisplay().getSystemColor(SWT.COLOR_DARK_RED));
{
commentTwistie = new ExpandableComposite(detailsArea, SWT.NONE);
commentTwistie.setText(Messages.TWISTIE_TEXT_COMMENT);
commentTwistie.setExpanded(true);
commentTwistie.addExpansionListener(new ExpansionAdapter() {
@Override
public void expansionStateChanged(ExpansionEvent e) {
if (e.getState()) {
commentAndPreviewSash.setMaximizedControl(null);
} else {
commentAndPreviewSash.setMaximizedControl(previewArea);
if (!previewTwistie.isExpanded()) {
previewTwistie.setExpanded(true);
showPreviewListener.showPreview();
}
}
}
});
}
{
severity = new ComboViewer(detailsArea);
severity.setContentProvider(new ArrayContentProvider());
severity.setInput(Severity.VALUES);
severity.setLabelProvider(new LabelProvider() {
@Override
public String getText(Object element) {
return element == Severity.UNKNOWN ? Messages.COMBO_TEXT_SEVERITY_UNKNOWN
: capitalize(lowerCase(replaceChars(element.toString(), '_', ' ')));
}
});
}
{
commentAndPreviewSash = new SashForm(detailsArea, SWT.VERTICAL);
commentAndPreviewSash.setSashWidth(10);
createCommentArea(commentAndPreviewSash);
createPreviewArea(commentAndPreviewSash);
final int lineHeight = Math.max(16 + 3, parent.getFont().getFontData()[0].getHeight());
commentAndPreviewSash.addControlListener(new ControlAdapter() {
private int preferredCommentHeight = -1;
private int autoExpandPoint;
@Override
public void controlResized(ControlEvent e) {
int sashHeight = commentAndPreviewSash.getClientArea().height;
if (preferredCommentHeight < 0) {
// Calculate weights and expand point based on initial window
preferredCommentHeight = sashHeight - lineHeight - commentAndPreviewSash.getSashWidth();
autoExpandPoint = (int) (sashHeight * 1.35);
}
if (preview.isVisible()) {
if (sashHeight < autoExpandPoint && commentTwistie.isExpanded()) {
previewTwistie.setExpanded(false);
preview.setVisible(false);
}
} else if (sashHeight >= autoExpandPoint) {
previewTwistie.setExpanded(true);
preview.setVisible(true);
}
int preferredPreviewHeight = sashHeight - preferredCommentHeight;
if (preferredPreviewHeight < lineHeight) {
commentAndPreviewSash.setWeights(new int[] { 3, 1 });
} else {
commentAndPreviewSash.setWeights(new int[] { preferredCommentHeight, preferredPreviewHeight });
}
}
});
}
gl().margins(0, 0).spacing(0, 0).numColumns(2).applyTo(detailsArea);
gdGrabHV().span(2, 1).applyTo(commentAndPreviewSash);
GridDataFactory.fillDefaults().align(SWT.END, SWT.CENTER).applyTo(commentTwistie);
GridDataFactory.fillDefaults().align(SWT.END, SWT.CENTER).applyTo(severity.getControl());
}
private void createReportsAndDetailsSash(final Composite parent) {
reportsAndDetailsSash = new SashForm(parent, SWT.HORIZONTAL);
reportsAndDetailsSash.setSashWidth(10);
gdGrabHV().applyTo(reportsAndDetailsSash);
}
private void createEventsTree(Composite parent) {
Composite container = new Composite(parent, SWT.NONE);
Label label = new Label(container, SWT.NONE);
label.setText(Messages.LABEL_TEXT_EVENTS);
reportsViewer = new TableViewer(container);
reportsViewer.setContentProvider(new ObservableListContentProvider());
reportsViewer.setLabelProvider(new ReportsViewerLabelprovider());
reportsViewer.getControl().addKeyListener(new ReportsViewerDeleteListener());
gl().applyTo(container);
gdGrabH().align(SWT.BEGINNING, SWT.END).applyTo(label);
gdGrabHV().applyTo(container);
gdGrabHV().applyTo(reportsViewer.getControl());
}
private void createCommentArea(Composite parent) {
comments = new StyledText(parent, SWT.BORDER | SWT.MULTI | SWT.WRAP | SWT.V_SCROLL);
comments.setFont(JFaceResources.getFont(JFaceResources.TEXT_FONT));
comments.setToolTipText(Messages.TOOLTIP_COMMENTS);
comments.setAlwaysShowScrollBars(false);
gdGrabHV().applyTo(comments);
}
private void createPreviewArea(Composite parent) {
previewArea = new Composite(parent, SWT.NONE);
{
previewTwistie = new ExpandableComposite(previewArea, SWT.NONE);
previewTwistie.setText(Messages.TWISTIE_TEXT_PREVIEW);
showPreviewListener = new ShowPreviewListener();
previewTwistie.addExpansionListener(showPreviewListener);
}
{
addonsBar = new ToolBar(previewArea, SWT.FLAT);
{
anonymizeMessagesOption = new ToolItem(addonsBar, SWT.CHECK);
Image icon3 = registry.get(ICO_ANONMYIZE_MESSAGE);
anonymizeMessagesOption.setImage(icon3);
anonymizeMessagesOption.setToolTipText(Messages.TOGGLE_TOOLTIP_ANONYMIZE_MESSAGES);
}
{
Image icon4 = registry.get(ICO_ANONMYIZE_STACKTRACE);
anonymizeStackTraceOption = new ToolItem(addonsBar, SWT.CHECK);
anonymizeStackTraceOption.setImage(icon4);
anonymizeStackTraceOption.setToolTipText(Messages.TOGGLE_TOOLTIP_ANONYMIZE_STACKTRACE);
}
}
{
preview = new StyledText(previewArea, SWT.V_SCROLL | SWT.H_SCROLL | SWT.BORDER);
preview.setEditable(false);
preview.setMargins(2, 2, 2, 2);
preview.setFont(JFaceResources.getFont(JFaceResources.TEXT_FONT));
preview.setForeground(parent.getDisplay().getSystemColor(SWT.COLOR_DARK_GRAY));
preview.setVisible(false);
}
gl().numColumns(2).applyTo(previewArea);
gdGrabH().align(SWT.END, SWT.CENTER).applyTo(addonsBar);
gdGrabHV().span(2, 1).hint(SWT.DEFAULT, DEFAULT_DIALOG_HEIGHT).applyTo(preview);
}
protected void createPreferencesLink(final Composite parent) {
Link link = new Link(parent, SWT.NONE);
link.setText(Messages.LINK_TEXT_SENDING_PREFERENCES);
link.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
uiSynchronize.asyncExec(new Runnable() {
@Override
public void run() {
PreferenceDialog dialog = PreferencesUtil.createPreferenceDialogOn(null, IDEConstants.PREFERENCE_PAGE_ID, null,
null);
dialog.open();
}
});
}
});
gdGrabH().indent(0, 10).minSize(SWT.DEFAULT, 20).applyTo(link);
}
private void createDatabindings() {
dbContext = new DataBindingContext();
{
selectedLogEvent = ViewerProperties.singlePostSelection().observe(reportsViewer);
reportsViewer.setInput(events);
}
{
IObservableValue emf = value(fromList(LOG_EVENT__OPTIONS, SEND_OPTIONS__COMMENT)).observeDetail(selectedLogEvent);
ISWTObservableValue swt = text(SWT.Modify).observeDelayed(300, comments);
dbContext.bindValue(swt, emf);
}
{
IObservableValue emf = value(fromList(LOG_EVENT__OPTIONS, USER_SETTINGS__ANONYMIZE_STACK_TRACES))
.observeDetail(selectedLogEvent);
ISWTObservableValue swt = toolItemSelection().observe(anonymizeStackTraceOption);
dbContext.bindValue(swt, emf);
}
{
IObservableValue emf = value(FeaturePath.fromList(LOG_EVENT__OPTIONS, USER_SETTINGS__ANONYMIZE_MESSAGES))
.observeDetail(selectedLogEvent);
ISWTObservableValue swt = toolItemSelection().observe(anonymizeMessagesOption);
dbContext.bindValue(swt, emf);
}
{
IObservableValue emf = value(fromList(LOG_EVENT__OPTIONS, SEND_OPTIONS__SEVERITY)).observeDetail(selectedLogEvent);
IObservableValue jface = ViewerProperties.singleSelection().observe(severity);
dbContext.bindValue(jface, emf);
}
addChangeListenerToBindings(dbContext);
}
private void addChangeListenerToBindings(DataBindingContext context) {
for (Object o : context.getBindings()) {
Binding b = (Binding) o;
b.getModel().addChangeListener(new IChangeListener() {
@Override
public void handleChange(ChangeEvent event) {
ILogEvent logEvent = (ILogEvent) selectedLogEvent.getValue();
if (logEvent != null) {
IServerDescriptor server = logEvent.getServer();
IReport report = server.getConnection().transform(logEvent.getStatus(), logEvent.getContext());
preview.setText(Formats.format(Messages.STYLED_TEXT_PREVIEW, server.getName(), Reports.toPrettyString(report)));
}
}
});
}
}
@Override
protected void buttonPressed(int buttonId) {
// MessageDialogs have a null implementation of this method. Need to re-implement it.
if (IDialogConstants.OK_ID == buttonId) {
okPressed();
} else if (IDialogConstants.CANCEL_ID == buttonId) {
cancelPressed();
}
}
@Override
public boolean close() {
context.modify(CTX_STATE_REVIEW_IN_PROGRESS, false);
dbContext.dispose();
prefs.putInt(PREF_REVIEW_DIALOG_WIDTH, getShell().getSize().x);
queue.eAdapters().remove(incomingEventsCopier);
return super.close();
}
@Override
protected void okPressed() {
super.okPressed();
broker.post(TOPIC_USER_REQUESTS_SEND, queue);
}
@Override
protected void cancelPressed() {
super.cancelPressed();
broker.post(TOPIC_USER_REQUESTS_CLEAR_QUEUE, queue);
}
private final class IncomingEventsCopier extends AdapterImpl {
private final IInternalInput input;
private IncomingEventsCopier(IInternalInput input) {
this.input = input;
}
@Override
public void notifyChanged(Notification msg) {
EList<ILogEvent> input2 = input.getInput();
switch (msg.getEventType()) {
case Notification.ADD: {
ILogEventGroup group = (ILogEventGroup) msg.getNewValue();
for (ILogEvent event : group.getEvents()) {
// TODO me need a better solution than this for final version:
IServerDescriptor server = event.getServer();
if (server.isConfigured()) {
input2.add(event);
}
}
break;
}
case Notification.REMOVE:
ILogEventGroup group = (ILogEventGroup) msg.getOldValue();
input2.removeAll(group.getEvents());
break;
}
}
}
private final class ReportsViewerDeleteListener extends KeyAdapter {
@Override
public void keyPressed(KeyEvent e) {
if (e.keyCode == SWT.DEL || e.keyCode == SWT.BS) {
deleteSelection();
}
}
private void deleteSelection() {
IStructuredSelection selection = (IStructuredSelection) reportsViewer.getSelection();
@SuppressWarnings("unchecked")
List<ILogEvent> elements = selection.toList();
events.removeAll(elements);
for (ILogEvent event : elements) {
ILogEventGroup group = (ILogEventGroup) event.eContainer();
EcoreUtil.delete(event);
if (group.getEvents().isEmpty()) {
EcoreUtil.remove(group);
}
}
}
}
private final class ShowPreviewListener extends ExpansionAdapter {
private Point collapsedShellSize;
@Override
public void expansionStateChanged(ExpansionEvent e) {
if (e.getState()) {
showPreview();
} else {
hidePreview();
}
}
public void hidePreview() {
Shell shell = getShell();
Point current = shell.getSize();
preview.setVisible(false);
shell.setSize(current.x, DEFAULT_DIALOG_HEIGHT);
}
public void showPreview() {
Shell shell = getShell();
preview.setVisible(true);
collapsedShellSize = shell.getSize();
shell.setSize(collapsedShellSize.x, collapsedShellSize.y + 300);
}
}
private final class ReportsViewerLabelprovider extends LabelProvider {
@Override
public String getText(Object element) {
return new IdeSwitch<String>() {
@Override
public String caseLogEvent(ILogEvent object) {
return object.getLabel();
}
}.doSwitch((EObject) element);
}
@Override
public Image getImage(Object element) {
return new IdeSwitch<Image>() {
@Override
public Image caseLogEvent(ILogEvent object) {
return UIUtils.decorate(object.getServer(), object.getStatus(), registry);
}
}.doSwitch((EObject) element);
}
}
public static class ToolItemProperty extends WidgetValueProperty {
public static ToolItemProperty toolItemSelection() {
return new ToolItemProperty();
}
public ToolItemProperty() {
super(SWT.Selection);
}
@Override
public Object getValueType() {
return Boolean.TYPE;
}
@Override
protected Object doGetValue(Object source) {
ToolItem item = (ToolItem) source;
boolean value = item.getSelection();
return value;
}
@Override
protected void doSetValue(Object source, @Nullable Object value) {
if (value == null) {
value = Boolean.FALSE;
}
ToolItem item = (ToolItem) source;
item.setSelection((Boolean) value);
}
}
}