blob: 105f3264d2f389d5b52afdc4444232deecf33cda [file] [log] [blame]
/*
* Copyright (c) 2008, 2009 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'»
«EXTENSION Forms»
«EXTENSION Widgets»
«EXTENSION Context»
//
//
//
«DEFINE registerListeners FOR binding::Section»
«IF self.hasTextWidgets()-»
for (Text t : new Text[] FOREACH self.textWidgets() AS te SEPARATOR ', '»«fieldName(te)»«ENDFOREACH»}) {
t.addListener(SWT.Modify, this);
t.addListener(SWT.FocusOut, this);
t.addListener(SWT.KeyDown, this);
}
«ENDIF
«IF self.hasSpins()-»
for (Spinner s : new Spinner[] FOREACH self.spinWidgets() AS s SEPARATOR ', '»«fieldName(s)»«ENDFOREACH»}) {
s.addListener(SWT.Modify, this);
s.addListener(SWT.FocusOut, this);
}
«ENDIF
«IF self.hasRadioButtons() or self.hasCheckBoxes() or self.hasCombos()-»
for (Widget w : new Widget[] FOREACH self.radioWidgets()[widget::Widget]->union(self.checkboxWidgets())->union(self.comboWidgets()) AS r SEPARATOR ', '»«fieldName(r)»«ENDFOREACH»}) {
w.addListener(SWT.Selection, this);
}
«ENDIF
«ENDDEFINE»
//
//
//
«DEFINE handleEventMethodBody FOR binding::Section»
«IF self.hasTextWidgets() or self.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 self.hasRadioButtons() or self.hasCheckBoxes()-»
if (event.type == SWT.Selection) {
«LET self.checkboxWidgets()->select(w | associatedBinding(self, w).selector.isBoolean()) AS booleanCheckBoxes
«IF not booleanCheckBoxes->isEmpty()-»
if «FOREACH booleanCheckBoxes AS r SEPARATOR ' else if '»(«fieldName(r == event.widget) {
«IF not dependantActions(self, r)->isEmpty()-»
if fieldName(r)».getSelection()) {
«EXPAND ApplyAction FOREACH dependantActions(self, r)-»
} else {
«EXPAND RevertAction FOREACH dependantActions(self, 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 self.checkboxWidgets()->select(w | not associatedBinding(self, w).selector.isBoolean()) AS referenceBasedCheckBoxes
«IF self.hasRadioButtons() or not referenceBasedCheckBoxes->isEmpty()-»
if «FOREACH (self.radioWidgets())[widget::Widget]->union(referenceBasedCheckBoxes)->asOrderedSet()->asSequence() AS r SEPARATOR ' else if '»(«fieldName(r == event.widget) {
«LET associatedBinding(self, r) AS activeBinding
if fieldName(r)».getSelection()) {
«EXPAND ApplyAction FOREACH triggeredActions_activated(self, activeBinding)-»
«EXPAND RevertAction FOREACH triggeredActions_deactivated(self, activeBinding)-»
applyChanges(); // Commit; View to Model
«EXPAND doRefresh(triggeredBindingToRefreshBesidesTheOne(self, activeBinding), activeBinding.refreshCondition.accessor)-»
IF not triggeredActions_activated(self, activeBinding)->isEmpty()» else {
«EXPAND RevertAction FOREACH triggeredActions_activated(self, activeBinding)-»
«IF activeBinding.widget.oclIsKindOf(widget::CheckBox)-»
applyChanges(); // Commit; View to Model
«ENDIF
ENDIF»
«ENDLET
ENDFOREACH»
«ENDIF
«ENDLET
«IF self.hasCombos()-»
if FOREACH self.comboWidgets() AS cb SEPARATOR ' || '»«fieldName(cb == 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(w : widget::Widget) FOR binding::Section
«EXPAND doRefresh(dependantBindings(self, w), null)-»
«ENDDEFINE»
/////////////////////////////////////////////////////////////////////////////////
«DEFINE Commit FOR binding::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, self) FOR needsCreation(bindings->asSequence()->select(b | b.commitCondition = null))-»
«FOREACH bindings->asSequence()->select(b | b.commitCondition = null) AS b»«EXPAND ViewToModel(b) FOR b.widget»«ENDFOREACH»
«FOREACH conditions->asSequence()->select(c | bindings->exists(b | c = b.commitCondition)) AS c
«IF triggeredActions(self, c)->isEmpty()-»
«FOREACH bindings->asSequence()->select(b | b.commitCondition = c) AS b
if EXPAND uiConditionGet FOR b.widget») {
«EXPAND createMissing(1, self) FOR needsCreation(Sequence { b })-»
«EXPAND ViewToModel(b) FOR b.widget
}
«ENDFOREACH
«ELSE
«LET bindings->asSequence()->select(b | b.commitCondition = c) AS bc
if EXPAND uiConditionGet FOREACH triggeredActions(self, c) SEPARATOR ' || '») {
«LET needsCreation(bc) AS toCreate
«FOREACH bc->select(b | wholeChain(b.selector)->asOrderedSet()->intersection(toCreate->asSet())->isEmpty()) AS b
«EXPAND ViewToModel(b) FOR b.widget
«ENDFOREACH
«EXPAND createMissing(1, self) FOR toCreate
«FOREACH bc->select(b | not wholeChain(b.selector)->asOrderedSet()->intersection(toCreate->asSet())->isEmpty()) AS b
«EXPAND ViewToModel(b) FOR b.widget
«ENDFOREACH
«ENDLET
LET bc->select(b | b.widget.oclIsKindOf(widget::CheckBox) and not b.selector.isBoolean()) AS referenceBasedCheckBoxBindings
«IF not 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(level : Integer, s : binding::Section) FOR Sequence(context::Context)-»
«IF self->exists(x | wholeChain(x)->size() >= level)-»
«FOREACH (self->select(x | wholeChain(x)->size() = level))[context::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 binding::Section»
«EXPAND doRefresh((bindings->asSequence())[refreshCondition = null], null)-»
«REM»Shouldn't we modify select like: "bindings->exists(b | b.refreshCondition = c) or actions->exists(a | a.trigger->includes(c))" ?«ENDREM»
«FOREACH conditions->asSequence()->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(self, c), c.accessor)-»
«EXPAND ApplyAction FOREACH triggeredActions(self, c)-»
}«IF not triggeredActions(self, c)->isEmpty() or (needsRadioButtonWorkaround(self, c) or hasReferenceBasedCheckBoxes(self, c))» else {
«EXPAND deselectWidget FOREACH radioButtonToWorkaround(self, c)-»
«EXPAND deselectWidget FOREACH referenceBasedCheckBoxBindings(self, c)-»
«EXPAND RevertAction FOREACH triggeredActions(self, c)-»
}«ENDIF-»
«ENDFOREACH-»
«FOREACH conditions->asSequence()->select(c | bindings->exists(b | b.commitCondition = c and b.refreshCondition <> c) and not triggeredActions(self, c)->isEmpty()) AS c-»
if («IF c.accessor.chain <> null and c.accessor.chain.needsNullCheck()»«EXPAND nullModelConditionClause(self) FOR c.accessor.chain» && «ENDIF»«EXPAND modelConditionClause(c)») {
«EXPAND ApplyAction FOREACH triggeredActions(self, c)-»
} else {«REM»Shouldn't I add radiobutton workaround here as wellENDREM»
«EXPAND RevertAction FOREACH triggeredActions(self, 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(bnd : Sequence(binding::Binding), exclude : context::Context) FOR binding::Section
«FOREACH bnd->selector[ctx | ctx.needsNullCheck() and ctx <> exclude]->chain->asOrderedSet() AS x
if EXPAND nullModelConditionClause(self) FOR x») {
«FOREACH bnd[selector.chain = x] AS b
«EXPAND ModelToView(b) FOR b.widget
«ENDFOREACH
}
«ENDFOREACH
«FOREACH bnd[not selector.needsNullCheck() or selector = exclude] AS b
«EXPAND ModelToView(b) FOR b.widget
«ENDFOREACH
«ENDDEFINE»
/////////////////////////////////////////////////////////////////////////////////
// Model to View - Refresh/Update
«DEFINE ModelToView(b : binding::Binding) FOR widget::Widget»«ERROR 'abstract ModelToView(Binding) FOR Widget'»«ENDDEFINE»
«DEFINE ModelToView(b : binding::Binding) FOR widget::TextEntry
«fieldName(self)».setTextEXPAND modelAccessGet(b.selector) FOR b.section»);/*Bridge.fieldSet(«fieldName(self)», «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(b : binding::Binding) FOR widget::RadioButton
«fieldName(self)».setSelection(true);
«ENDDEFINE»
«DEFINE ModelToView(b : binding::Binding) FOR widget::CheckBox
«fieldName(self)».setSelectionIF b.selector.isBoolean()»«EXPAND modelAccessGet(b.selector) FOR b.section»«ELSE»true«ENDIF»);
«ENDDEFINE»
«DEFINE ModelToView(b : binding::Binding) FOR widget::Spin
«fieldName(self)».setSelectionEXPAND modelAccessGet(b.selector) FOR b.section»);
«ENDDEFINE»
«DEFINE ModelToView(b : binding::Binding) FOR widget::Combo
«fieldName(self)».selectEXPAND 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::Binding-»
«fieldName(widget)».setSelection(false);
«ENDDEFINE»
/////////////////////////////////////////////////////////////////////////////////
// View to Model - Commit
«DEFINE ViewToModel(b : binding::Binding) FOR widget::Widget»«ERROR 'abstract ViewToModel(Condition, Binding) FOR Widget'»«ENDDEFINE»
«DEFINE ViewToModel(b : binding::Binding) FOR widget::TextEntry-»
«EXPAND modelAccessSet(b.selector) FOR b.section»(/*Bridge.fieldGet(«fieldName(self)»)*/«fieldName(self)».getText());
«ENDDEFINE»
«DEFINE ViewToModel(b : binding::Binding) FOR widget::RadioButton-»
«EXPAND modelAccessSet(b.selector) FOR b.section»(«EXPAND NewValue FOR b.commitCondition»);
«ENDDEFINE»
«DEFINE ViewToModel(b : binding::Binding) FOR widget::CheckBox-»
«EXPAND modelAccessSet(b.selector) FOR b.section»(«IF b.selector.isBoolean()»«fieldName(self)».getSelection()«ELSE»«EXPAND NewValue FOR b.commitCondition»«ENDIF»);
«ENDDEFINE»
«DEFINE ViewToModel(b : binding::Binding) FOR widget::Spin-»
«EXPAND modelAccessSet(b.selector) FOR b.section»(«fieldName(self)».getSelection());
«ENDDEFINE»
«DEFINE ViewToModel(b : binding::Binding) FOR widget::Combo-»
«EXPAND modelAccessSet(b.selector) FOR b.section»(«EXPAND enumComboModelClass FOR b».get(«fieldName(self)».getSelectionIndex()));
«ENDDEFINE»
«DEFINE NewValue FOR context::Condition»«ERROR 'abstract NewValue FOR Condition'»«ENDDEFINE»
«DEFINE NewValue FOR context::InstanceCondition»«EXPAND MetaModel::factory FOR type»«ENDDEFINE»
«DEFINE NewValue FOR context::EqualsCondition»«value»«ENDDEFINE»
/////////////////////////////////////////////////////////////////////////////////
// Adapters
«DEFINE AdaptersField FOR binding::Section-»
private org.eclipse.emf.common.notify.Adapter[] myModelListeners;
«ENDDEFINE»
«DEFINE AttachAdapters FOR binding::Section-»
myModelListeners = new org.eclipse.emf.common.notify.Adapter[] {
«EXPAND Adapter::New(getConditionAccessors(self)) FOR (input)[context::FeatureContext]->asSequence()»
};
getInput().eAdapters().addAll(java.util.Arrays.asList(myModelListeners));
«ENDDEFINE»
«DEFINE DetachAdapters FOR binding::Section-»
if (myModelListeners != null) {
getInput().eAdapters().removeAll(java.util.Arrays.asList(myModelListeners));
myModelListeners = null;
}
«ENDDEFINE»
«DEFINE modelAccessSet(c : context::Context) FOR binding::Section»«ENDDEFINE»
«DEFINE modelAccessGet(c : context::Context) FOR binding::Section»«IF c = null»getInput(/*not sure which modelAccesGet would get invoked when c==null*/)«ENDIF»«ENDDEFINE»
«DEFINE modelAccessSet(c : context::FeatureContext) FOR binding::Section-»
«IF needsCast(deduceInputType(self), c)-»
((«c.selector.eContainingClass.name») «EXPAND modelAccessGet(c.chain)»)«ELSE-»
«EXPAND modelAccessGet(c.chain)-»
«ENDIF».set«c.selector.name.firstToUpper()-»
«ENDDEFINE»
«DEFINE modelAccessGet(c : context::FeatureContext) FOR binding::Section-»
«IF c = null»getInput()«ELSE-»
«IF needsCast(deduceInputType(self), 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.firstToUpper()»()«ENDIF-»
«ENDDEFINE»
«DEFINE uiConditionGet FOR binding::Action-»
«FOREACH widgets->asSequence() AS w SEPARATOR ' && '»«IF kind = binding::ActionKind::HIDE or kind = binding::ActionKind::_DISABLE»!«ENDIF»«fieldName(w)»«IF kind = binding::ActionKind::HIDE or kind = binding::ActionKind::SHOW».isVisible()«ELSE».isEnabled()«ENDIF»«ENDFOREACH-»
«ENDDEFINE»
// Sorta hack for radiobuttons. Unless Action(kind=SELECTION) is introduced
«DEFINE uiConditionGet FOR widget::Widget»«ERROR 'abstract uiConditionGet FOR Widget'»«ENDDEFINE»
«DEFINE uiConditionGet FOR widget::RadioButton»«fieldName(self)».getSelection()«ENDDEFINE»
«DEFINE modelConditionClause(c : context::Condition) FOR binding::Section»«ERROR 'abstract modelConditionClause'»«ENDDEFINE»
«DEFINE modelConditionClause(c : context::InstanceCondition) FOR binding::Section-»
«EXPAND modelAccessGet(c.accessor)» instanceof «c.type.name-»
«ENDDEFINE»
«DEFINE modelConditionClause(c : context::EqualsCondition) FOR binding::Section-»
«EXPAND modelAccessGet(c.accessor)»«IF c.value <> null» == «c.value»«ENDIF-»
«ENDDEFINE»
«DEFINE modelConditionClause(c : context::AndCondition) FOR binding::Section-»
«FOREACH c.conjuncts->asSequence() AS cj SEPARATOR ' && '»«EXPAND modelConditionClause(cj)»«ENDFOREACH»
«ENDDEFINE»
//uiConditionSet
«DEFINE ApplyAction FOR binding::Action-»
«FOREACH widgets AS w»«fieldName(w)».«IF kind = binding::ActionKind::HIDE or kind = binding::ActionKind::SHOW»setVisible«ELSE»setEnabled«ENDIF»(«kind = binding::ActionKind::SHOW or kind = binding::ActionKind::ENABLE»);
«ENDFOREACH-»
«ENDDEFINE»
«DEFINE RevertAction FOR binding::Action-»
«FOREACH widgets AS w»«fieldName(w)».«IF kind = binding::ActionKind::HIDE or kind = binding::ActionKind::SHOW»setVisible«ELSE»setEnabled«ENDIF»(«kind = binding::ActionKind::HIDE or kind = binding::ActionKind::_DISABLE»);
«ENDFOREACH-»
«ENDDEFINE»
//
«DEFINE nullModelConditionClause(s : binding::Section) FOR context::Context»«ERROR 'abstract nullModelConditionClause'»«ENDDEFINE»
«DEFINE nullModelConditionClause(s : binding::Section) FOR context::FeatureContext-»
«FOREACH wholeChain(self)[mayBeNull()][context::FeatureContext] AS x SEPARATOR ' && '»«EXPAND modelAccessGet(x) FOR s» != null«ENDFOREACH-»
«ENDDEFINE»
«DEFINE PopulateWidgets FOR binding::Section-»
«FOREACH self.comboWidgets() AS wc-»
// TODO «fieldName(wc)».setItems(VALUES.toString().toArray());
for (org.eclipse.emf.common.util.Enumerator e : «EXPAND enumComboModelClass FOR associatedBinding(self, wc)».VALUES) {
«fieldName(wc)».add(e.getName());
}
«ENDFOREACH-»
«ENDDEFINE»
«REM»FIXME: hack/quickfix - always assume combos are associated with eenum«ENDREM»
«DEFINE enumComboModelClass FOR binding::Binding»«selector.oclAsType(context::FeatureContext).selector.eType.name»«ENDDEFINE»