blob: 3a3d85690526b15cef3a17e9c7c1169493aafdfd [file] [log] [blame]
/*
* Copyright (c) 2008 Borland Software Corp.
*
* 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:
* Artem Tikhomirov (Borland) - initial API and implementation
*/
«IMPORT "http://www.eclipse.org/gmf/2008/Widget"»
«IMPORT "http://www.eclipse.org/gmf/2008/Context"»
«IMPORT "http://www.eclipse.org/gmf/2008/Binding"»
«IMPORT "http://www.eclipse.org/emf/2002/Ecore"»
«EXTENSION Forms»
«EXTENSION Widgets»
«EXTENSION Context»
//
//
//
«DEFINE registerListeners FOR Section»
«IF hasTextWidgets()-»
for (Text t : new Text[] FOREACH textWidgets() AS te SEPARATOR ", "»«te.fieldName()»«ENDFOREACH»}) {
t.addListener(SWT.Modify, this);
t.addListener(SWT.FocusOut, this);
t.addListener(SWT.KeyDown, this);
}
«ENDIF
«IF hasSpins()-»
for (Spinner s : new Spinner[] FOREACH spinWidgets() AS s SEPARATOR ", "»«s.fieldName()»«ENDFOREACH»}) {
s.addListener(SWT.Modify, this);
s.addListener(SWT.FocusOut, this);
}
«ENDIF
«IF hasRadioButtons() || hasCheckBoxes() || hasCombos()-»
for (Widget w : new Widget[] FOREACH (List[Widget])radioWidgets().union(checkboxWidgets()).union(comboWidgets()) AS r SEPARATOR ", "»«r.fieldName()»«ENDFOREACH»}) {
w.addListener(SWT.Selection, this);
}
«ENDIF
«ENDDEFINE»
//
//
//
«DEFINE handleEventMethodBody FOR Section»
«IF hasTextWidgets() || hasSpins() /*though, spinners may require more attention*/
if (event.type == SWT.Modify) {
// XXX also override isDirty to compare values if they
// match model's and to clear dirty state in case like aaa^H^H^H
markDirty();
} else if (event.type == SWT.FocusOut) {
applyChanges();
} else if (event.type == SWT.KeyDown) {
if (event.keyCode == SWT.ESC) {
discardChanges();
} else if (event.keyCode == SWT.CR) {
applyChanges();
}
}
«ENDIF
«IF hasRadioButtons() || hasCheckBoxes()-»
if (event.type == SWT.Selection) {
«LET checkboxWidgets().select(w | isBoolean(associatedBinding(w).selector)) AS booleanCheckBoxes
«IF !booleanCheckBoxes.isEmpty()-»
if «FOREACH booleanCheckBoxes AS r SEPARATOR " else if "»(«r.fieldName()» == event.widget) {
«IF !dependantActions(r).isEmpty()-»
if r.fieldName()».getSelection()) {
«EXPAND ApplyAction FOREACH dependantActions(r)-»
} else {
«EXPAND RevertAction FOREACH dependantActions(r)-»
}
«ENDIF
applyChanges(); // Commit; View to Model
// Optimization? Instead of full refresh, just dependant widgets should get updated
«EXPAND RefreshAssociated(r)-»
ENDFOREACH»
«ENDIF
«ENDLET
«LET checkboxWidgets().select(w | !isBoolean(associatedBinding(w).selector)) AS referenceBasedCheckBoxes
«IF hasRadioButtons() || !referenceBasedCheckBoxes.isEmpty()-»
if «FOREACH (List[Widget])radioWidgets().union(referenceBasedCheckBoxes) AS r SEPARATOR " else if "»(«r.fieldName()» == event.widget) {
«LET associatedBinding(r) AS activeBinding
if r.fieldName()».getSelection()) {
«EXPAND ApplyAction FOREACH triggeredActions_activated(activeBinding)-»
«EXPAND RevertAction FOREACH triggeredActions_deactivated(activeBinding)-»
applyChanges(); // Commit; View to Model
«EXPAND doRefresh(triggeredBindingToRefreshBesidesTheOne(activeBinding), activeBinding.refreshCondition.accessor)-»
IF !triggeredActions_activated(activeBinding).isEmpty()» else {
«EXPAND RevertAction FOREACH triggeredActions_activated(activeBinding)-»
«IF !{activeBinding.widget}.typeSelect(CheckBox).isEmpty()-»
applyChanges(); // Commit; View to Model
«ENDIF
ENDIF»
«ENDLET
ENDFOREACH»
«ENDIF
«ENDLET
«IF hasCombos()-»
if FOREACH comboWidgets() AS cb SEPARATOR " || "»«cb.fieldName()» == event.widget«ENDFOREACH») {
applyChanges();«REM»TODO need to process possible enablements here as well (actions for conditions with accessor equal to the activatedENDREM»
}
«ENDIF»
}
«ENDIF
«ENDDEFINE»
//
//
// for all bindings that share the same condition, refresh UI state
«DEFINE RefreshAssociated(Widget w) FOR Section
«EXPAND doRefresh(dependantBindings(w), null)-»
«ENDDEFINE»
/////////////////////////////////////////////////////////////////////////////////
«DEFINE Commit FOR Section
«REM»
FIXME
Need to create not only the those elements that would get committed at this time
e.g. is wizard/generatePerspective == false, do not create GenPerspective
«ENDREM»
«EXPAND createMissing(1, this) FOR needsCreation(bindings.select(b | b.commitCondition == null))-»
«FOREACH bindings.select(b | b.commitCondition == null) AS b»«EXPAND ViewToModel(b) FOR b.widget»«ENDFOREACH»
«FOREACH conditions.select(c | bindings.exists(b | c == b.commitCondition)) AS c
«IF triggeredActions(c).isEmpty()-»
«FOREACH bindings.select(b | b.commitCondition == c) AS b
if EXPAND uiConditionGet FOR b.widget») {
«EXPAND createMissing(1, this) FOR needsCreation({b})-»
«EXPAND ViewToModel(b) FOR b.widget
}
«ENDFOREACH
«ELSE
«LET bindings.select(b | b.commitCondition == c) AS bc
if EXPAND uiConditionGet FOREACH triggeredActions(c) SEPARATOR " || "») {
«LET needsCreation(bc) AS toCreate
«FOREACH bc.select(b | b.selector.wholeChain().intersect(toCreate).isEmpty()) AS b
«EXPAND ViewToModel(b) FOR b.widget
«ENDFOREACH
«EXPAND createMissing(1, this) FOR toCreate
«FOREACH bc.select(b | !b.selector.wholeChain().intersect(toCreate).isEmpty()) AS b
«EXPAND ViewToModel(b) FOR b.widget
«ENDFOREACH
«ENDLET
LET bc.select(b | !{b.widget}.typeSelect(CheckBox).isEmpty() && !isBoolean(b.selector)) AS referenceBasedCheckBoxBindings
«IF !referenceBasedCheckBoxBindings.isEmpty()» else {
«FOREACH referenceBasedCheckBoxBindings AS cb
«EXPAND modelAccessSet(cb.selector)»(null);
«ENDFOREACH
}
«ENDIF
«ENDLET
«ENDLET
«ENDIF
«ENDFOREACH
«ENDDEFINE»
// assume each element of the list is context that mayBeNull
«DEFINE createMissing(int level, Section s) FOR List[Context]-»
«IF exists(x | wholeChain(x).size() >= level)-»
«FOREACH select(x | wholeChain(x).size() == level).typeSelect(FeatureContext) AS x
if EXPAND modelAccessGet(x) FOR s» == null) {
«EXPAND modelAccessSet(x) FOR s»(«EXPAND MetaModel::factory FOR x.selector.eType»);
}
«ENDFOREACH
«EXPAND createMissing(level+1, s)-»
«ENDIF
«ENDDEFINE»
«DEFINE Refresh FOR Section»
«EXPAND doRefresh(bindings.select(b | b.refreshCondition == null), null)-»
«FOREACH conditions.select(c | bindings.exists(b | b.refreshCondition == c)) AS c
«REM»grouping bindings by condition - to generate only one check and to set dependant bindings/widgets inside the same if()«ENDREM»
if EXPAND modelConditionClause(c)») {
«EXPAND doRefresh(bindingsToRefresh(c), c.accessor)-»
«EXPAND ApplyAction FOREACH triggeredActions(c)-»
IF !triggeredActions(c).isEmpty() || needsRadioButtonWorkaround(c) || hasReferenceBasedCheckBoxes(c else {
«EXPAND deselectWidget FOREACH radioButtonToWorkaround(c)-»
«EXPAND deselectWidget FOREACH referenceBasedCheckBoxBindings(c)-»
«EXPAND RevertAction FOREACH triggeredActions(c)-»
ENDIF
«ENDFOREACH
«FOREACH conditions.select(c | bindings.exists(b | b.commitCondition == c && b.refreshCondition != c) && !triggeredActions(c).isEmpty()) AS c
if IF c.accessor.chain != null && needsNullCheck(c.accessor.chain)»«EXPAND nullModelConditionClause(this) FOR c.accessor.chain» && «ENDIF»«EXPAND modelConditionClause(c)») {
«EXPAND ApplyAction FOREACH triggeredActions(c)-»
} else REM»Shouldn't I add radiobutton workaround here as well?«ENDREM»
«EXPAND RevertAction FOREACH triggeredActions(c)-»
}
«ENDFOREACH-»
«ENDDEFINE»
«REM»
Performs a refresh taking into account possible null Contexts
In certain cases, there might be a null/whatever check for specific context,
(e.g. when refresh is conditioned) so that we may assume here the context is
already known to be non-null.
«ENDREM»
«DEFINE doRefresh(List[Binding] bnd, Context exclude) FOR Section-»
«FOREACH bnd.select(b | needsNullCheck(b.selector) && b.selector != exclude).selector.chain.purgeDups().typeSelect(Context) AS x-»
if («EXPAND nullModelConditionClause(this) FOR x») {
«FOREACH bnd.select(b | b.selector.chain == x) AS b-»
«EXPAND ModelToView(b) FOR b.widget-»
«ENDFOREACH-»
}
«ENDFOREACH-»
«FOREACH bnd.select(b | !needsNullCheck(b.selector) || b.selector == exclude) AS b-»
«EXPAND ModelToView(b) FOR b.widget-»
«ENDFOREACH-»
«ENDDEFINE»
/////////////////////////////////////////////////////////////////////////////////
// Model to View - Refresh/Update
«DEFINE ModelToView(Binding b) FOR Widget»«ERROR "abstract ModelToView(Binding) FOR Widget"»«ENDDEFINE»
«DEFINE ModelToView(Binding b) FOR TextEntry-»
«fieldName()».setText(«EXPAND modelAccessGet(b.selector) FOR b.section»);/*Bridge.fieldSet(«fieldName()», «EXPAND modelAccessGet(b.selector) FOR b.section»);*/
«ENDDEFINE»
// XXX NOTE: actual model value from b.selector is not used for now - not sure
// how to tell radiobuttons based on boolean values vs. radiobtns used to switch
// UI based on a condition
«DEFINE ModelToView(Binding b) FOR RadioButton-»
«fieldName()».setSelection(true);
«ENDDEFINE»
«DEFINE ModelToView(Binding b) FOR CheckBox-»
«fieldName()».setSelection(«IF isBoolean(b.selector)»«EXPAND modelAccessGet(b.selector) FOR b.section»«ELSE»true«ENDIF»);
«ENDDEFINE»
«DEFINE ModelToView(Binding b) FOR Spin-»
«fieldName()».setSelection(«EXPAND modelAccessGet(b.selector) FOR b.section»);
«ENDDEFINE»
«DEFINE ModelToView(Binding b) FOR Combo-»
«fieldName()».select(«EXPAND modelAccessGet(b.selector) FOR b.section».getValue());
«ENDDEFINE»
«REM»
XXX WORKAROUND. May be reasonable to hide that behind RadioGroupController
Programmatic radioButton.setSelection(true) doesn't deselect
any previously selected radiobutton within the same composite
(as it happens with user action/event). Hence, need to deselect
radiobuttons manually (leftover selection from previous input)
«ENDREM»
«DEFINE deselectWidget FOR Binding
«fieldName(widget)».setSelection(false);
«ENDDEFINE»
/////////////////////////////////////////////////////////////////////////////////
// View to Model - Commit
«DEFINE ViewToModel(Binding b) FOR Widget»«ERROR "abstract ViewToModel(Condition, Binding) FOR Widget"»«ENDDEFINE»
«DEFINE ViewToModel(Binding b) FOR TextEntry
«EXPAND modelAccessSet(b.selector) FOR b.section»(/*Bridge.fieldGet(«fieldName()»)*/«fieldName()».getText());
«ENDDEFINE»
«DEFINE ViewToModel(Binding b) FOR RadioButton
«EXPAND modelAccessSet(b.selector) FOR b.section»(«EXPAND NewValue FOR b.commitCondition»);
«ENDDEFINE»
«DEFINE ViewToModel(Binding b) FOR CheckBox
«EXPAND modelAccessSet(b.selector) FOR b.section»(«IF isBoolean(b.selector)»«fieldName()».getSelection()«ELSE»«EXPAND NewValue FOR b.commitCondition»«ENDIF»);
«ENDDEFINE»
«DEFINE ViewToModel(Binding b) FOR Spin
«EXPAND modelAccessSet(b.selector) FOR b.section»(«fieldName()».getSelection());
«ENDDEFINE»
«DEFINE ViewToModel(Binding b) FOR Combo
«EXPAND modelAccessSet(b.selector) FOR b.section»(«EXPAND enumComboModelClass FOR b».getfieldName()».getSelectionIndex()));
«ENDDEFINE»
«DEFINE NewValue FOR Condition»«ERROR "abstract NewValue FOR Condition"»«ENDDEFINE»
«DEFINE NewValue FOR InstanceCondition»«EXPAND MetaModel::factory FOR type»«ENDDEFINE»
«DEFINE NewValue FOR EqualsCondition»«value»«ENDDEFINE»
/////////////////////////////////////////////////////////////////////////////////
// Adapters
«DEFINE AdaptersField FOR Section
private org.eclipse.emf.common.notify.Adapter[] myModelListeners;
«ENDDEFINE»
«DEFINE AttachAdapters FOR Section
myModelListeners = new org.eclipse.emf.common.notify.Adapter[] {
«EXPAND Adapter::New(getConditionAccessors()) FOR input.typeSelect(FeatureContext
};
getInput().eAdapters().addAll(java.util.Arrays.asList(myModelListeners));
«ENDDEFINE»
«DEFINE DetachAdapters FOR Section
if (myModelListeners != null) {
getInput().eAdapters().removeAll(java.util.Arrays.asList(myModelListeners));
myModelListeners = null;
}
«ENDDEFINE»
«DEFINE modelAccessSet(Context c) FOR Section»«ENDDEFINE»
«DEFINE modelAccessGet(Context c) FOR Section»«IF c == null»getInput(/*not sure which modelAccesGet would get invoked when c==null*/ENDIF»«ENDDEFINE»
«DEFINE modelAccessSet(FeatureContext c) FOR Section
«IF needsCast(deduceInputType(), c)-»
((«c.selector.eContainingClass.name») «EXPAND modelAccessGet(c.chain)»)«ELSE
«EXPAND modelAccessGet(c.chain)-»
«ENDIF».set«c.selector.name.toFirstUpper()-»
«ENDDEFINE»
«DEFINE modelAccessGet(FeatureContext c) FOR Section
«IF c == null»getInput()«ELSE
«IF needsCast(deduceInputType(), c)-»
((«c.selector.eContainingClass.name») «EXPAND modelAccessGet (c.chain)»)«ELSE
«EXPAND modelAccessGet(c.chain)-»
«ENDIF
«IF "EBoolean" == c.selector.eType.name».is«ELSE».get«ENDIF»«c.selector.name.toFirstUpper()»()«ENDIF
«ENDDEFINE»
«DEFINE uiConditionGet FOR Action
«FOREACH widgets AS w SEPARATOR " && "»«IF kind.value == 1 || kind.value == 3»!«ENDIF»«w.fieldName()»«IF kind.value < 2».isVisible()«ELSE».isEnabled()«ENDIF»«ENDFOREACH
«ENDDEFINE»
// Sorta hack for radiobuttons. Unless Action(kind=SELECTION) is introduced
«DEFINE uiConditionGet FOR Widget»«ERROR "abstract uiConditionGet FOR Widget"»«ENDDEFINE»
«DEFINE uiConditionGet FOR RadioButton»«fieldName()».getSelection()«ENDDEFINE»
«DEFINE modelConditionClause(Condition c) FOR Section»«ERROR "abstract modelConditionClause"»«ENDDEFINE»
«DEFINE modelConditionClause(InstanceCondition c) FOR Section
«EXPAND modelAccessGet(c.accessor instanceof «c.type.name
«ENDDEFINE»
«DEFINE modelConditionClause(EqualsCondition c) FOR Section
«EXPAND modelAccessGet(c.accessor)»«IF c.value != null» == «c.value»«ENDIF
«ENDDEFINE»
«DEFINE modelConditionClause(AndCondition c) FOR Section
«FOREACH c.conjuncts AS cj SEPARATOR " && "»«EXPAND modelConditionClause(cj)»«ENDFOREACH»
«ENDDEFINE»
//uiConditionSet
«DEFINE ApplyAction FOR Action
«FOREACH widgets AS w»«w.fieldName()».«IF kind.value < 2»setVisible«ELSE»setEnabled«ENDIF»(«kind.value == 0 || kind.value == 2»);
«ENDFOREACH
«ENDDEFINE»
«DEFINE RevertAction FOR Action
«FOREACH widgets AS w»«w.fieldName()».«IF kind.value < 2»setVisible«ELSE»setEnabled«ENDIF»(«kind.value == 1 || kind.value == 3»);
«ENDFOREACH
«ENDDEFINE»
//
«DEFINE nullModelConditionClause(Section s) FOR Context»«ERROR "abstract nullModelConditionClause"»«ENDDEFINE»
«DEFINE nullModelConditionClause(Section s) FOR FeatureContext
«FOREACH wholeChain().select(x | mayBeNull(x)).typeSelect(FeatureContext) AS x SEPARATOR " && "»«EXPAND modelAccessGet(x) FOR s» != null«ENDFOREACH
«ENDDEFINE»
«DEFINE PopulateWidgets FOR Section
«FOREACH comboWidgets() AS wc
// TODO «wc.fieldName()».setItems(VALUES.toString().toArray());
for (org.eclipse.emf.common.util.Enumerator e : «EXPAND enumComboModelClass FOR associatedBinding(wc)».VALUES) {
«wc.fieldName()».add(e.getName());
}
«ENDFOREACH
«ENDDEFINE»
«REM»FIXME: hack/quickfix - always assume combos are associated with eenum«ENDREM»
«DEFINE enumComboModelClass FOR Binding»«((FeatureContext) selector).selector.eType.name»«ENDDEFINE»