| /* |
| * 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 activated)«ENDREM» |
| } |
| «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 well?«ENDREM» |
| «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)».setText(«EXPAND 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)».setSelection(«IF b.selector.isBoolean()»«EXPAND modelAccessGet(b.selector) FOR b.section»«ELSE»true«ENDIF»); |
| «ENDDEFINE» |
| |
| «DEFINE ModelToView(b : binding::Binding) FOR widget::Spin-» |
| «fieldName(self)».setSelection(«EXPAND modelAccessGet(b.selector) FOR b.section»); |
| «ENDDEFINE» |
| |
| «DEFINE ModelToView(b : binding::Binding) FOR widget::Combo-» |
| «fieldName(self)».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::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» |