blob: f46e100cb08f1643302146758a5db9f6ed4b12c1 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2011, 2012 Red Hat, Inc.
* All rights reserved.
* This program is 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:
* Red Hat, Inc. - initial API and implementation
*
* @author Bob Brodt
******************************************************************************/
package org.eclipse.bpmn2.modeler.core.merrimac.dialogs;
import org.eclipse.bpmn2.modeler.core.Activator;
import org.eclipse.bpmn2.modeler.core.merrimac.clad.IPropertiesCompositeFactory;
import org.eclipse.bpmn2.modeler.core.validation.LiveValidationListener;
import org.eclipse.bpmn2.modeler.core.validation.ValidationErrorHandler;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.transaction.RecordingCommand;
import org.eclipse.emf.transaction.RollbackException;
import org.eclipse.emf.transaction.Transaction;
import org.eclipse.emf.transaction.TransactionChangeDescription;
import org.eclipse.emf.transaction.impl.InternalTransaction;
import org.eclipse.emf.transaction.impl.InternalTransactionalEditingDomain;
import org.eclipse.graphiti.ui.editor.DiagramEditor;
import org.eclipse.jface.dialogs.ErrorDialog;
import org.eclipse.jface.dialogs.IDialogConstants;
import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.jface.resource.StringConverter;
import org.eclipse.jface.window.Window;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.ControlEvent;
import org.eclipse.swt.events.ControlListener;
import org.eclipse.swt.events.DisposeEvent;
import org.eclipse.swt.events.DisposeListener;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.layout.FormAttachment;
import org.eclipse.swt.layout.FormData;
import org.eclipse.swt.layout.FormLayout;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Text;
import org.eclipse.swt.widgets.ToolBar;
import org.eclipse.ui.forms.FormDialog;
import org.eclipse.ui.forms.IManagedForm;
import org.eclipse.ui.forms.widgets.ScrolledForm;
import org.eclipse.ui.forms.widgets.Section;
import org.eclipse.ui.forms.widgets.Twistie;
public abstract class AbstractObjectEditingDialog extends FormDialog implements ValidationErrorHandler {
protected IPreferenceStore preferenceStore = Activator.getDefault().getPreferenceStore();
protected DiagramEditor editor;
protected String title = ""; //$NON-NLS-1$
protected EObject object;
protected boolean cancel = false;
protected boolean abortOnCancel = true;
protected Transaction transaction;
protected ScrolledForm form;
protected Composite dialogContent;
protected InternalTransactionalEditingDomain domain;
private Text errorMessageText;
private IPropertiesCompositeFactory compositeFactory = null;
// If this property is set on a Control, then don't try to
// adapt the Control's colors/fonts/etc. to dialog defaults
// This is used by the Description Styled Text widget.
public final static String DO_NOT_ADAPT = "do_not_adapt"; //$NON-NLS-1$
public AbstractObjectEditingDialog(DiagramEditor editor, EObject object) {
super(editor.getEditorSite().getShell());
setShellStyle(SWT.DIALOG_TRIM | SWT.APPLICATION_MODAL | SWT.MAX | SWT.RESIZE
| getDefaultOrientation());
this.editor = editor;
this.object = object;
domain = (InternalTransactionalEditingDomain) editor.getEditingDomain();
}
public void setCompositeFactory(IPropertiesCompositeFactory compositeFactory) {
this.compositeFactory = compositeFactory;
}
@Override
protected void createFormContent(IManagedForm mform) {
super.createFormContent(mform);
form = mform.getForm();
form.setExpandHorizontal(true);
form.setExpandVertical(true);
form.setText(null);
Composite body = form.getBody();
body.setBackground(form.getBackground());
FormData data = new FormData();
data.top = new FormAttachment(0, 0);
data.bottom = new FormAttachment(100, 0);
data.left = new FormAttachment(0, 0);
data.right = new FormAttachment(100, 0);
body.setLayoutData(data);
body.setLayout(new FormLayout());
dialogContent = createDialogContent(body);
if (compositeFactory!=null)
dialogContent.setData("factory", compositeFactory); //$NON-NLS-1$
data = new FormData();
data.top = new FormAttachment(0, 0);
data.bottom = new FormAttachment(100, 0);
data.left = new FormAttachment(0, 0);
data.right = new FormAttachment(100, 0);
dialogContent.setLayoutData(data);
form.setContent(body);
getShell().pack();
}
@Override
protected Control createDialogArea(Composite parent) {
Composite composite = (Composite) super.createDialogArea(parent);
errorMessageText = new Text(parent, SWT.READ_ONLY | SWT.WRAP | SWT.BORDER);
errorMessageText.setLayoutData(new GridData(GridData.GRAB_HORIZONTAL
| GridData.HORIZONTAL_ALIGN_FILL));
errorMessageText.setForeground(errorMessageText.getDisplay()
.getSystemColor(SWT.COLOR_RED));
errorMessageText.setBackground(errorMessageText.getDisplay()
.getSystemColor(SWT.COLOR_WIDGET_BACKGROUND));
return composite;
}
abstract protected Composite createDialogContent(Composite parent);
abstract protected String getPreferenceKey();
protected String getTitle() {
return title;
}
protected void addControlListener() {
final String key = getPreferenceKey();
Point p = getShell().getSize();
int width = preferenceStore.getInt("dialog."+key+".width"); //$NON-NLS-1$ //$NON-NLS-2$
if (width==0)
width = p.x;
int height = preferenceStore.getInt("dialog."+key+".height"); //$NON-NLS-1$ //$NON-NLS-2$
if (height==0)
height = p.y;
getShell().setSize(width,height);
p = getShell().getLocation();
int x = preferenceStore.getInt("dialog."+key+".x"); //$NON-NLS-1$ //$NON-NLS-2$
if (x==0)
x = p.x;
int y = preferenceStore.getInt("dialog."+key+".y"); //$NON-NLS-1$ //$NON-NLS-2$
if (y==0)
y = p.y;
getShell().setLocation(x,y);
getShell().addControlListener(new ControlListener() {
public void controlMoved(ControlEvent e)
{
Point p = getShell().getLocation();
preferenceStore.setValue("dialog."+key+".x", p.x); //$NON-NLS-1$ //$NON-NLS-2$
preferenceStore.setValue("dialog."+key+".y", p.y); //$NON-NLS-1$ //$NON-NLS-2$
}
public void controlResized(ControlEvent e)
{
Point p = getShell().getSize();
preferenceStore.setValue("dialog."+key+".width", p.x); //$NON-NLS-1$ //$NON-NLS-2$
preferenceStore.setValue("dialog."+key+".height", p.y); //$NON-NLS-1$ //$NON-NLS-2$
}
});
hookTransaction();
}
protected void aboutToOpen() {
dialogContent.setData(object);
}
@Override
public void create() {
super.create();
startTransaction();
}
public static int openWithTransaction(final AbstractObjectEditingDialog dialog) {
final int result[] = new int[1];
// wrap this dialog in a transaction
if (dialog.domain.getActiveTransaction()==null) {
dialog.domain.getCommandStack().execute(new RecordingCommand(dialog.domain) {
@Override
protected void doExecute() {
result[0] = openWithRollback(dialog);
}
});
}
else {
result[0] = openWithRollback(dialog);
}
return result[0];
}
private static int openWithRollback(final AbstractObjectEditingDialog dialog) {
int result = dialog.open();
InternalTransaction transaction = dialog.domain.getActiveTransaction();
if (transaction!=null) {
if (dialog.cancel) {
// roll back transaction if CANCEL button was pressed
IStatus status = new Status(Status.CANCEL, Activator.PLUGIN_ID, "cancel");
transaction.setStatus(status);
result = Window.CANCEL;
}
else {
TransactionChangeDescription cd = dialog.transaction.getChangeDescription();
if (cd.getObjectChanges().size()==0) {
// also roll back if there were no changes made even if
// OK button was pressed.
IStatus status = new Status(Status.CANCEL, Activator.PLUGIN_ID, "no changes");
transaction.setStatus(status);
result = Window.CANCEL;
}
}
}
return result;
}
public int open() {
if (getShell()==null)
create();
getShell().setText(getTitle());
getShell().setSize(600,400);
addControlListener();
// Tell the Live Validation Listener to report validation errors to us
// instead of the Workbench Status line.
LiveValidationListener.setValidationErrorHandler(this);
getShell().addDisposeListener(new DisposeListener() {
public void widgetDisposed(DisposeEvent event) {
LiveValidationListener.setValidationErrorHandler(null);
}
});
aboutToOpen();
adapt(dialogContent);
return super.open();
}
protected void adapt(Composite content) {
// The AbstractDetailComposite controls don't actually get constructed until
// setBusinessObject() is called - the business object determines which controls
// are required. So, this needs to happen very late in the dialog lifecycle.
// We can now safely set the background color of all controls to match
// the dialog.
content.setBackground(form.getBackground());
for (Control k : content.getChildren()) {
Object data = k.getData(AbstractObjectEditingDialog.DO_NOT_ADAPT);
if (data instanceof Boolean && (Boolean)data == true)
continue;
if (k instanceof Twistie || k instanceof ToolBar
|| (k instanceof Label && k.getParent() instanceof Section)) {
continue;
}
k.setBackground(form.getBackground());
if (k instanceof Composite) {
adapt((Composite)k);
}
}
}
@Override
public boolean close() {
if (getReturnCode() != OK)
cancel = true;
return super.close();
}
/**
* Return state of the "abortOnCancel transaction on cancel" flag
*
* @return true if the current transaction will be aborted if the dialog is canceled.
*/
public boolean isAbortOnCancel() {
return abortOnCancel;
}
/**
* Abort the currently active transaction if dialog is canceled either by clicking the "Cancel"
* button, pressing the ESCAPE key or closing the dialog with the Window Close button.
*
* @param abortOnCancel - if true, abortOnCancel the current transaction if dialog is canceled,
* otherwise allow transaction to commit.
*/
public void setAbortOnCancel(boolean abort) {
this.abortOnCancel = abort;
}
@Override
protected void cancelPressed() {
cancel = true;
dialogContent.dispose();
super.cancelPressed();
}
@Override
protected void okPressed() {
cancel = false;
dialogContent.dispose();
super.okPressed();
}
public boolean hasDoneChanges() {
return transaction==null || !transaction.getChangeDescription().isEmpty();
}
protected void startTransaction() {
if (transaction==null) {
try {
transaction = domain.startTransaction(false, null);
}
catch (InterruptedException e) {
e.printStackTrace();
}
}
}
private void hookTransaction() {
getShell().addDisposeListener(new DisposeListener() {
public void widgetDisposed(DisposeEvent event) {
if (transaction!=null && transaction.isActive()) {
if (cancel) {
transaction.rollback();
}
else {
try {
transaction.commit();
}
catch (RollbackException e) {
ErrorDialog.openError(getShell(), Messages.AbstractObjectEditingDialog_Commit_Error,
Messages.AbstractObjectEditingDialog_Commit_Error_Title, new Status(IStatus.ERROR,
Activator.PLUGIN_ID, e.getMessage(), e));
}
}
}
}
});
}
protected void rollbackTransaction() {
if (transaction!=null) {
transaction.rollback();
transaction = null;
}
}
public void reportError(IStatus s)
{
String errorMessage = (s==null) ? null : s.getMessage();
if (errorMessageText != null && !errorMessageText.isDisposed()) {
errorMessageText.setText(errorMessage == null ? "" : errorMessage); //$NON-NLS-1$
// Disable the error message text control if there is no error, or
// no error text (empty or whitespace only). Hide it also to avoid
// color change.
boolean hasError = errorMessage != null && (StringConverter.removeWhiteSpaces(errorMessage)).length() > 0;
errorMessageText.setEnabled(hasError);
errorMessageText.setVisible(hasError);
GridData gd = (GridData) errorMessageText.getLayoutData();
gd.exclude = !hasError;
if (dialogArea!=null)
dialogArea.getParent().layout();
if (s!=null && s.getSeverity()>=IStatus.ERROR) {
Control button = getButton(IDialogConstants.OK_ID);
if (button != null) {
button.setEnabled(hasError);
}
}
}
}
}