blob: 38282f0f1d9f6ca5a7fd3b8533ccbb0cbc942615 [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 Context;
modeltype widget_ uses "http://www.eclipse.org/gmf/2008/Widget";
modeltype context uses "http://www.eclipse.org/gmf/2008/Context";
modeltype binding uses "http://www.eclipse.org/gmf/2008/Binding";
modeltype ecore uses "http://www.eclipse.org/emf/2002/Ecore";
library Forms;
helper context::Context::isBoolean() : Boolean {
return false
}
helper context::FeatureContext::isBoolean() : Boolean {
return self.selector.eType.name = 'EBoolean' or self.selector.eType.name = 'EBooleanObject' or self.selector.eType.name = 'Boolean' /* UML. Though not 100% sure it's right place */or
// just in case, if user defined own primitive type with Java's implementation of a boolean kind
(self.selector.eType.instanceClassName = 'java.lang.Boolean' or self.selector.eType.instanceClassName = 'boolean')
}
helper hasReferenceBasedCheckBoxes(s : binding::Section, c : context::Condition) : Boolean {
return bindingsToRefresh(s, c)->exists(b | b.widget.oclIsKindOf(widget_::CheckBox) and not b.selector.isBoolean())
}
helper referenceBasedCheckBoxBindings(s : binding::Section, c : context::Condition) : Sequence(binding::Binding) {
return bindingsToRefresh(s, c)[widget.oclIsKindOf(widget_::CheckBox) and not selector.isBoolean()]
}
helper getConditionAccessors(e : binding::Section) : Sequence(context::FeatureContext) {
return (e.bindings->asSequence()->refreshCondition/*[accessor != null]*/.accessor->asOrderedSet()->asSequence())[context::FeatureContext]
}
helper needsCast(inputType : ecore::EClass, x : context::FeatureContext) : Boolean {
return (if x.chain = null then not x.selector.eContainingClass.isSuperTypeOf(inputType) else not x.selector.eContainingClass.isSuperTypeOf(x.chain.oclAsType(context::FeatureContext).selector.oclAsType(ecore::EReference).eReferenceType) endif)
}
// look for conditions that share model accessor with the binding of the widget
// - assume when binding's widget got changed, there's a need to trigger UI enablement actions
helper dependantActions(s : binding::Section, w : widget_::Widget) : Sequence(binding::Action) {
return triggeredConditions(s, associatedBinding(s, w))->collect(c | triggeredActions(s, c))
// FIXME replace with smth like: s.actions.select(a | !a.trigger.intersect(triggeredConditions).isEmpty())
}
helper associatedBinding(s : binding::Section, w : widget_::Widget) : binding::Binding {
return s.bindings[widget = w]->first()
}
helper triggeredActions(s : binding::Section, c : context::Condition) : Sequence(binding::Action) {
return (s.actions->asSequence())[trigger->includes(c)]
}
//
// XXX replace with EReference [0..*] from Binding to Condition
// for now, assume triggered condition share model accessor with binding
helper triggeredConditions(s : binding::Section, b : binding::Binding) : Sequence(context::Condition) {
return (s.conditions->asSequence())[accessor = b.selector]
}
//
// next two are analogous to triggeredCondition
// but for radio buttons case, where few conditions may share the same accessor,
// so, need to tell actions that became activated from those that became deactivated
// NOTE, use of commitCondition is nothing but an attempt to figure out which
// condition is associated with the activated binding, perhaps, need to check
// refreshCondition also/besides.
helper triggeredActions_activated(s : binding::Section, b : binding::Binding) : Sequence(binding::Action) {
//s.conditions.select(c | c.accessor == b.selector && b.commitCondition == c).actions
return (s.actions->asSequence())[trigger->exists(c | c.accessor = b.selector and b.commitCondition = c)]
}
helper triggeredActions_deactivated(s : binding::Section, b : binding::Binding) : Sequence(binding::Action) {
//s.conditions.select(c | c.accessor == b.selector && b.commitCondition != c).actions
return (s.actions->asSequence())[trigger->exists(c | c.accessor = b.selector and b.commitCondition <> c)]
}
helper triggeredBindingToRefreshBesidesTheOne(s : binding::Section, b : binding::Binding) : Sequence(binding::Binding) {
return let triggeredAndActivatedConditions = s.conditions->select(c | c.accessor = b.selector and b.commitCondition = c) in s.bindings->asSequence()->select(v | v <> b and triggeredAndActivatedConditions->includes(v.refreshCondition))
}
// XXX since we use this extension to perform refresh, do we really need to
// take commitCondition into account?
helper dependantBindings(s : binding::Section, w : widget_::Widget) : Sequence(binding::Binding) {
return let activeBinding = associatedBinding(s, w) in let triggered = triggeredConditions(s, activeBinding) in s.bindings->asSequence()->select(b | b <> activeBinding and (triggered->includes(b.refreshCondition) or triggered->includes(b.commitCondition)))
}
helper bindingsToRefresh(s : binding::Section, c : context::Condition) : Sequence(binding::Binding) {
return (s.bindings->asSequence())[refreshCondition = c]
}
// RadioButton in SWT doesn't deselect another active radiobutton
// in the composite on *programmatic* setSelection, hence
// need to manually deselect any leftover from previous input
helper needsRadioButtonWorkaround(s : binding::Section, c : context::Condition) : Boolean {
return bindingsToRefresh(s, c)->exists(b | b.widget.oclIsKindOf(widget_::RadioButton))
}
helper radioButtonToWorkaround(s : binding::Section, c : context::Condition) : Sequence(binding::Binding) {
return bindingsToRefresh(s, c)[widget.oclIsKindOf(widget_::RadioButton)]
}
// looks for contexts that are being used by bindings and may be null.
// NOTE: context directly used by bindings as their accessors are EXCLUDED
// from the return list even if they may be null (they are assumed to get created
// on appropriate ViewToModel call (which makes success of the view-2-model assignment dependent
// on order of binding processing)). Consider the example of radiobutton associated with a EClass:
// if (myFlowLayoutRadio.isSelected()) {
// /*begin: code that should not get generated*/
// if (getInput().getLayout() == null) {getInput().setLayout(new Layout());}
// /*end*/
// getInput().setLayout(new FlowLayout());
// }
helper needsCreation(bindings : Sequence(binding::Binding)) : Sequence(context::Context) {
// TODO bindings.select(b | needsNullCheck(b.selector)).selector.chain.purgeDups().typeSelect(Context).wholeChain().flatten().toSet().without(bindings.selector).typeSelect(Context).select(x | mayBeNull(x))
var res = bindings->selector[needsNullCheck()]->chain->asOrderedSet()->asSequence()->collect(ctx | wholeChain(ctx))[mayBeNull()];
bindings.selector->forEach(i) {
res := res->excluding(i);
};
return res->asOrderedSet()->asSequence();
}
helper deduceInputType(s : binding::Section) : ecore::EClass {
return deduceCommon(s.input[context::FeatureContext].selector.eContainingClass)
}
// XXX actually, the code below is not tested and
// apparently is not really working as intended, and may
// easily get to situations with no answer though common superclass exists
// Just didn't get enough time to implement [Head|Tail] logic to implement
// pair-by-pair comparison
helper deduceCommon(classes : Sequence(ecore::EClass)) : ecore::EClass {
return if classes->size() = 1
then classes->first()
else let supertypesOfTheRest = classes->select(c | classes->forAll(cc | c.isSuperTypeOf(cc)))
in if supertypesOfTheRest->isEmpty()
then let supertypesOfNone = classes->select(c | classes->forAll(cc | cc <> c and not c.isSuperTypeOf(cc))) /* no cycles in supertypes => this collection will not be empty*/
in if supertypesOfNone->isEmpty()
then deduceCommon(classes.eSuperTypes) /* [AS] Should never happen ?*/
else deduceCommon((supertypesOfNone.eSuperTypes->union(classes->asOrderedSet()->-(supertypesOfNone->asSet())->asSequence())->asOrderedSet()->asSequence())[ecore::EClass])
endif
else mostSpecific(supertypesOfTheRest) /* [AS] this collection contains only one EClass or several instances of same EClass (no cycles in supertypes) ?*/
endif
endif
}
helper mostSpecific(classes : Sequence(ecore::EClass)) : ecore::EClass {
return if classes->size() = 1
then classes->first()
else classes->reject(c | classes->exists(cc | cc <> c and c.isSuperTypeOf(cc)))->first() /* looks strange - should be "->select" instead of "->reject" */
endif
}