/*=============================================================================# # Copyright (c) 2015-2016 Stephan Wahlbrink (WalWare.de) 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: # Stephan Wahlbrink - initial API and implementation #=============================================================================*/ package de.walware.docmlet.base.ui.processing; import java.util.HashMap; import java.util.List; import java.util.Map; import org.eclipse.core.databinding.DataBindingContext; import org.eclipse.core.databinding.observable.Realm; import org.eclipse.core.databinding.observable.value.IValueChangeListener; import org.eclipse.core.databinding.observable.value.ValueChangeEvent; import org.eclipse.core.databinding.observable.value.WritableValue; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Status; import org.eclipse.core.variables.IDynamicVariable; import org.eclipse.core.variables.IStringVariable; import org.eclipse.debug.core.ILaunchConfiguration; import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy; import org.eclipse.debug.ui.ILaunchConfigurationDialog; import org.eclipse.jface.databinding.swt.WidgetProperties; import org.eclipse.jface.databinding.viewers.ViewerProperties; import org.eclipse.jface.dialogs.Dialog; import org.eclipse.jface.viewers.ArrayContentProvider; import org.eclipse.jface.viewers.ComboViewer; import org.eclipse.jface.viewers.LabelProvider; import org.eclipse.osgi.util.NLS; import org.eclipse.swt.SWT; import org.eclipse.swt.custom.StackLayout; import org.eclipse.swt.layout.GridData; import org.eclipse.swt.widgets.Button; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Group; import org.eclipse.swt.widgets.Label; import org.eclipse.ui.statushandlers.StatusManager; import de.walware.jcommons.collections.CopyOnWriteIdentityListSet; import de.walware.jcommons.collections.ImCollections; import de.walware.jcommons.collections.ImList; import de.walware.ecommons.debug.core.variables.ResourceVariables; import de.walware.ecommons.debug.ui.config.LaunchConfigTabWithDbc; import de.walware.ecommons.ui.util.LayoutUtil; import de.walware.ecommons.ui.util.UIAccess; import de.walware.docmlet.base.internal.ui.DocBaseUIPlugin; import de.walware.docmlet.base.internal.ui.processing.Messages; import de.walware.docmlet.base.ui.DocBaseUI; public abstract class DocProcessingConfigStepTab extends LaunchConfigTabWithDbc implements IValueChangeListener { public static interface Listener { void changed(DocProcessingConfigStepTab source); } private class OperationItem { private static final byte S_INITIALIZED= 0b0_00000001; private static final byte S_CONTROL_FAILED= 0b0_00100000; private byte state; private DocProcessingOperationSettings operation; private Composite detailControl; public void init(final DocProcessingOperationSettings operation) { operation.init(DocProcessingConfigStepTab.this); this.state|= S_INITIALIZED; this.operation= operation; } public void dipose() { if (this.operation != null && (this.state & S_INITIALIZED) != 0) { this.operation.dispose(); } } public String getId() { return this.operation.getId(); } public DocProcessingOperationSettings getOperation() { return this.operation; } @Override // for LabelProvider public String toString() { return this.operation.getLabel(); } public Composite enable() { if (this.operation != null) { try { this.operation.setSelected(true); } catch (final Exception e) { DocBaseUIPlugin.log(new Status(IStatus.ERROR, DocBaseUI.PLUGIN_ID, 0, NLS.bind("An error occurred when enabling settings for document processing operation ''{0}''.", //$NON-NLS-1$ getId() ), e )); } if (this.detailControl == null && (this.state & S_CONTROL_FAILED) == 0) { try { this.detailControl= this.operation.createDetailControl( DocProcessingConfigStepTab.this.operationDetailControl ); } catch (final Exception e) { this.state|= S_CONTROL_FAILED; StatusManager.getManager().handle(new Status(IStatus.ERROR, DocBaseUI.PLUGIN_ID, 0, NLS.bind("An error occurred when creating GUI for document processing operation ''{0}''.", getId() ), e ), (StatusManager.LOG | StatusManager.SHOW) ); } } } return this.detailControl; } public void disable() { if (this.operation != null) { try { this.operation.setSelected(false); } catch (final Exception e) { DocBaseUIPlugin.log(new Status(IStatus.ERROR, DocBaseUI.PLUGIN_ID, 0, NLS.bind("An error occurred when disabling settings for document processing operation ''{0}''.", //$NON-NLS-1$ getId() ), e )); } } } } protected static final ImList<IDynamicVariable> INPUT_RESOURCE_VAR_DEFS= ResourceVariables .createSingleResourceVarDefs(Messages.Variable_InFileResourceVars_description_Resource_term); private final OperationItem nullOperationItem= new OperationItem() { @Override public String getId() { return ""; //$NON-NLS-1$ } @Override public String toString() { return ""; //$NON-NLS-1$ } }; private final DocProcessingConfigMainTab mainTab; private final int num; private final CopyOnWriteIdentityListSet<Listener> listeners= new CopyOnWriteIdentityListSet<>(); private boolean isNotifyListenerScheduled; private final String attrQualifier; private final String stepEnabledAttrName; private final String operationIdAttrName; private final String operationSettingsAttrName; private Map<String, String> operationSettings; private final WritableValue stepEnabledValue; private final WritableValue operationValue; private ImList<OperationItem> operations; private Button stepEnabledControl; private final Map<String, IStringVariable> stepVariables= new HashMap<>(); private ComboViewer operationSelectionViewer; private StackLayout operationDetailLayout; private Composite operationDetailControl; private final StringBuilder sBuilder= new StringBuilder(32); public DocProcessingConfigStepTab(final DocProcessingConfigMainTab mainTab, final String attrQualifier) { this.mainTab= mainTab; this.num= mainTab.addStep(this); this.attrQualifier= attrQualifier; this.stepEnabledAttrName= getAttrQualifier() + '/' + DocProcessingConfig.STEP_ENABLED_ATTR_KEY; this.operationIdAttrName= getAttrQualifier() + '/' + DocProcessingConfig.STEP_OPERATION_ID_ATTR_KEY; this.operationSettingsAttrName= getAttrQualifier() + '/' + DocProcessingConfig.STEP_OPERATION_SETTINGS_ATTR_KEY; final Realm realm= getRealm(); this.stepEnabledValue= new WritableValue(realm, true, Boolean.TYPE); this.stepEnabledValue.addValueChangeListener(this); this.operationValue= new WritableValue(realm, this.nullOperationItem, OperationItem.class); this.operationValue.addValueChangeListener(this); } @Override protected Realm getRealm() { return super.getRealm(); } @Override protected DataBindingContext getDataBindingContext() { return super.getDataBindingContext(); } @Override public void setLaunchConfigurationDialog(final ILaunchConfigurationDialog dialog) { super.setLaunchConfigurationDialog(dialog); initVariables(this.stepVariables); } @Override protected ILaunchConfigurationDialog getLaunchConfigurationDialog() { return super.getLaunchConfigurationDialog(); } @Override public void dispose() { if (this.operations != null) { for (final OperationItem item : this.operations) { item.dipose(); } } super.dispose(); } protected final StringBuilder getStringBuilder() { this.sBuilder.setLength(0); return this.sBuilder; } public final DocProcessingConfigMainTab getMainTab() { return this.mainTab; } public final int getNum() { return this.num; } protected final String getAttrQualifier() { return this.attrQualifier; } protected String createName(final String text) { return "&" + getNum() + ") " + text; //$NON-NLS-1$ //$NON-NLS-2$ } public abstract String getLabel(); public boolean isEnabled() { return (Boolean) this.stepEnabledValue.getValue(); } public abstract String getInfo(); public void addListener(final Listener listener) { this.listeners.add(listener); } public void removeListener(final Listener listener) { this.listeners.remove(listener); } protected void scheduleNotifyListeners() { if (!this.isNotifyListenerScheduled) { this.isNotifyListenerScheduled= true; UIAccess.getDisplay().asyncExec(new Runnable() { @Override public void run() { DocProcessingConfigStepTab.this.isNotifyListenerScheduled= false; notifyListeners(); } }); } } protected void notifyListeners() { for (final Listener listener : this.listeners) { listener.changed(this); } } protected abstract void initVariables(Map<String, IStringVariable> variables); public Map<String, IStringVariable> getStepVariables() { return this.stepVariables; } @Override public void createControl(final Composite parent) { final Composite mainComposite= new Composite(parent, SWT.NONE); setControl(mainComposite); mainComposite.setLayout(LayoutUtil.createTabGrid(1)); final Composite composite= new Composite(mainComposite, SWT.NONE); composite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false)); composite.setLayout(LayoutUtil.createCompositeGrid(2)); { final Label label= new Label(composite, SWT.NONE); label.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false)); label.setText(getLabel() + ':'); } { final Button button= new Button(composite, SWT.CHECK); button.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, false, false)); button.setText(Messages.StepTab_Enabled_label); this.stepEnabledControl= button; } addControls(mainComposite); Dialog.applyDialogFont(parent); initBindings(); } protected abstract void addControls(Composite parent); protected void setAvailableOperations(final List<DocProcessingOperationSettings> operations) { if (this.operations != null) { throw new IllegalStateException(); } final OperationItem[] items= new OperationItem[operations.size()]; for (int i= 0; i < items.length; i++) { items[i]= new OperationItem(); items[i].init(operations.get(i)); } this.operations= ImCollections.newList(items); } private OperationItem getOperationItem(final String id) { for (final OperationItem item : this.operations) { if (item.getId() == id) { return item; } } return this.nullOperationItem; } public DocProcessingOperationSettings getOperation() { final OperationItem item= (OperationItem) this.operationValue.getValue(); return (item != null) ? item.getOperation() : null; } protected String getOperationsLabel() { return Messages.StepTab_Operations_label; } protected Composite createOperationGroup(final Composite parent) { if (this.operations == null) { throw new UnsupportedOperationException(); } final Group group= new Group(parent, SWT.NONE); group.setLayout(LayoutUtil.createGroupGrid(1)); group.setText(getOperationsLabel()); { final ComboViewer viewer= new ComboViewer(group); viewer.setLabelProvider(new LabelProvider()); viewer.setContentProvider(new ArrayContentProvider()); final OperationItem[] input= this.operations.toArray(new OperationItem[this.operations.size() + 1]); input[input.length - 1]= this.nullOperationItem; viewer.setInput(input); viewer.getControl().setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false)); this.operationSelectionViewer= viewer; } { final Composite composite= new Composite(group, SWT.NONE); this.operationDetailLayout= new StackLayout(); composite.setLayout(this.operationDetailLayout); composite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); this.operationDetailControl= composite; } return group; } protected Composite createPostGroup(final Composite parent) { final Group group= new Group(parent, SWT.NONE); group.setText(Messages.StepTab_PostActions_label); return group; } @Override protected void addBindings(final DataBindingContext dbc) { dbc.bindValue(WidgetProperties.selection().observe(this.stepEnabledControl), this.stepEnabledValue ); dbc.bindValue(ViewerProperties.singleSelection().observe(this.operationSelectionViewer), this.operationValue ); } @Override public void handleValueChange(final ValueChangeEvent event) { if (event.getObservable() == this.operationValue) { final OperationItem oldItem= (OperationItem) event.diff.getOldValue(); if (oldItem != null) { oldItem.disable(); } final OperationItem newItem= (OperationItem) event.diff.getNewValue(); if (newItem != null) { this.operationDetailLayout.topControl= newItem.enable(); } else { this.operationDetailLayout.topControl= null; } this.operationDetailControl.layout(); } scheduleNotifyListeners(); } protected String getDefaultOperationId() { return null; } @Override public void setDefaults(final ILaunchConfigurationWorkingCopy configuration) { configuration.setAttribute(this.stepEnabledAttrName, true); configuration.setAttribute(this.operationIdAttrName, getDefaultOperationId()); } @Override protected void doInitialize(final ILaunchConfiguration configuration) { { boolean enabled= false; try { enabled= configuration.getAttribute(this.stepEnabledAttrName, enabled); } catch (final CoreException e) { logReadingError(e); } this.stepEnabledValue.setValue(enabled); } { String id= ""; //$NON-NLS-1$ try { id= configuration.getAttribute(this.operationIdAttrName, id); } catch (final CoreException e) { logReadingError(e); } id= id.intern(); final OperationItem item= getOperationItem(id); this.operationValue.setValue(item); Map<String, String> settings= null; try { settings= configuration.getAttribute(this.operationSettingsAttrName, (Map<String, String>) null ); } catch (final CoreException e) { logReadingError(e); } if (settings == null) { settings= new HashMap<>(); } this.operationSettings= settings; if (item.getOperation() != null) { item.getOperation().load(settings); } } } @Override protected void doSave(final ILaunchConfigurationWorkingCopy configuration) { { final boolean enabled= (Boolean) this.stepEnabledValue.getValue(); configuration.setAttribute(this.stepEnabledAttrName, enabled); } { final OperationItem item= (OperationItem) this.operationValue.getValue(); configuration.setAttribute(this.operationIdAttrName, item.getId()); Map<String, String> settings= this.operationSettings; if (item.getOperation() != null) { settings= new HashMap<>(settings); item.getOperation().save(settings); } configuration.setAttribute(this.operationSettingsAttrName, settings); } } }