Merge " fix Klocwork warning of unintialized data members, unneeded assignment &...
[platform/core/uifw/dali-toolkit.git] / base / dali-toolkit / internal / builder / builder-signals.cpp
1 /*
2  * Copyright (c) 2014 Samsung Electronics Co., Ltd.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  *
16  */
17
18 // EXTERNAL INCLUDES
19 #include <dali/integration-api/debug.h>
20 #include <boost/function.hpp>
21
22 // INTERNAL INCLUDES
23 #include <dali-toolkit/internal/builder/builder-impl.h>
24 #include <dali-toolkit/internal/builder/builder-get-is.inl.h>
25
26 namespace Dali
27 {
28 namespace Toolkit
29 {
30 namespace Internal
31 {
32 extern Animation CreateAnimation( const TreeNode& child );
33 extern bool SetPropertyFromNode( const TreeNode& node, Property::Value& value );
34 }
35 }
36 }
37
38 namespace
39 {
40 using namespace Dali;
41
42 //
43 // Signal Actions
44 //
45
46 // Action on child actor. The child is found by alias so can be 'previous' etc.
47 struct ChildActorAction
48 {
49   std::string actorName;
50   std::string actionName;
51   std::string childAlias;
52   std::vector<Property::Value> parameters;
53
54   void operator()(void)
55   {
56     Actor actor = Stage::GetCurrent().GetRootLayer().FindChildByName(actorName);
57
58     if(actor)
59     {
60       Actor child_actor = actor.FindChildByAlias(childAlias);
61
62       if(child_actor)
63       {
64         child_actor.DoAction(actionName, parameters);
65       }
66       else
67       {
68         DALI_SCRIPT_WARNING("Could not find child by alias '%s'\n", childAlias.c_str());
69       }
70     }
71   };
72 };
73
74 // Action to set a property
75 struct PropertySetAction
76 {
77   std::string actorName;
78   std::string propertyName;
79   Property::Value value;
80
81   void operator()(void)
82   {
83     Actor actor = Stage::GetCurrent().GetRootLayer().FindChildByName(actorName);
84
85     if(actor)
86     {
87       Property::Index idx = actor.GetPropertyIndex(propertyName);
88
89       if( idx != Property::INVALID_INDEX )
90       {
91         if( actor.GetPropertyType(idx) != value.GetType() )
92         {
93           DALI_SCRIPT_WARNING("Set property action has different type for property '%s'\n", propertyName.c_str());
94         }
95         else
96         {
97           actor.SetProperty( idx, value );
98         }
99       }
100       else
101       {
102         DALI_SCRIPT_WARNING("Set property action cannot find property '%s'\n", propertyName.c_str());
103       }
104     }
105   };
106 };
107
108 // Generic action on a handle (Animation & Actor)
109 struct GenericAction
110 {
111   std::string actorName;
112   std::string actionName;
113   std::vector<Property::Value> parameters;
114
115   void operator()(void)
116   {
117     Actor actor = Stage::GetCurrent().GetRootLayer().FindChildByName(actorName);
118     if(actor)
119     {
120       actor.DoAction(actionName, parameters);
121     }
122
123   };
124 };
125
126 // Delay an animation play; ie wait as its not on stage yet
127 struct DelayedAnimationPlay
128 {
129   Toolkit::JsonParser memento;
130
131   void operator()(void)
132   {
133     Animation anim = Toolkit::Internal::CreateAnimation(*memento.GetRoot());
134     if(anim)
135     {
136       anim.Play();
137     }
138   };
139 };
140
141 /*
142  * Gets Property::Value from child
143  */
144 Property::Value GetPropertyValue(const TreeNode &child)
145 {
146   size_t nChildren = child.Size();
147
148   Property::Value ret;
149
150   if(0 == nChildren)
151   {
152     // cast away unused return for static analyzers
153     static_cast<void>( Dali::Toolkit::Internal::SetPropertyFromNode( child, ret ) );
154   }
155   else if(1 == nChildren)
156   {
157     // {"property": {"quaternion":[1,2,3,4]} }
158     // {"property": {"angle":22, "axis": [1,2,3]} }
159
160     OptionalChild quaternion  = IsChild(&child, "quaternion");
161     OptionalChild axis        = IsChild(&child, "axis");
162     OptionalChild angle       = IsChild(&child, "angle");
163
164     if(quaternion)
165     {
166       ret = Property::Value(Quaternion(GetVector4(*quaternion)));
167     }
168     else if(axis && angle)
169     {
170       ret = Property::Value(AngleAxis(Degree(GetFloat(*angle)), GetVector3(*axis)));
171     }
172   }
173   else if(2 == nChildren)
174   {
175     // {"property": [1,2]}
176     ret = Property::Value(GetVector2(child));
177   }
178   else if(3 == nChildren)
179   {
180     // {"property": [1,2,3]}
181     ret = Property::Value(GetVector3(child));
182   }
183   else if(4 == nChildren)
184   {
185     // {"property": [1,2,3,4]}
186     ret = Property::Value(GetVector4(child));
187   }
188
189   return ret;
190 }
191
192
193 /*
194  * Gets Parmeter list from child
195  * params is be cleared before insertion
196  */
197 void GetParameters(const TreeNode &child, std::vector<Property::Value> &params)
198 {
199   if( OptionalChild c = IsChild(child, "parameters") )
200   {
201     const TreeNode& node = *c;
202
203     if(0 == node.Size())
204     {
205       GetPropertyValue(node);
206     }
207     else
208     {
209       params.clear();
210       params.reserve(node.Size());
211
212       for(TreeNode::ConstIterator iter(node.CBegin()); iter != node.CEnd(); ++iter)
213       {
214         params.push_back( GetPropertyValue( (*iter).second ) );
215       }
216     }
217   }
218 }
219
220 void DoNothing(void) {};
221
222 /**
223  * Get an action as boost function callback
224  */
225 boost::function<void (void)> GetAction(const TreeNode &root, const TreeNode &child, Actor actor, boost::function<void (void)> quitAction)
226 {
227   OptionalString childActorName(IsString( IsChild(&child, "child-actor")) );
228   OptionalString actorName(IsString( IsChild(&child, "actor")) );
229   OptionalString propertyName(IsString( IsChild(&child, "property")) );
230   OptionalChild  valueChild( IsChild(&child, "value") );
231
232   OptionalString actionName = IsString( IsChild(&child, "action") );
233   DALI_ASSERT_ALWAYS(actionName && "Signal must have an action");
234
235   boost::function<void(void)> callback = DoNothing;
236
237   if(childActorName)
238   {
239     ChildActorAction action;
240     action.actorName       = *actorName;
241     action.childAlias      = *childActorName;
242     action.actionName      = *actionName;
243     GetParameters(child, action.parameters);
244     callback = action;
245   }
246   else if(actorName)
247   {
248     if(propertyName && valueChild && ("set" == *actionName) )
249     {
250       PropertySetAction action;
251       action.actorName       = *actorName;
252       action.propertyName    = *propertyName;
253       // actor may not exist yet so we can't check the property type
254       if( !Dali::Toolkit::Internal::SetPropertyFromNode( *valueChild, action.value ) )
255       {
256         DALI_SCRIPT_WARNING("Cannot set property for set property action\n");
257       }
258       callback = action;
259     }
260     else
261     {
262       GenericAction action;
263       action.actorName       = *actorName;
264       action.actionName      = *actionName;
265       GetParameters(child, action.parameters);
266       callback = action;
267     }
268   }
269   else if("quit" == *actionName)
270   {
271     callback = quitAction;
272   }
273   else if("play" == *actionName)
274   {
275     OptionalChild animations     = IsChild( root, "animations" );
276     OptionalString animationName = IsString( IsChild(child, "animation") );
277     if( animations && animationName )
278     {
279       if( OptionalChild animNode = IsChild(*animations, *animationName) )
280       {
281         DelayedAnimationPlay action;
282         action.memento = Toolkit::JsonParser::New(*animNode);
283         // @todo; put constants into the map
284         callback = action;
285       }
286       else
287       {
288         DALI_SCRIPT_WARNING("Cannot find animation '%s'\n", (*animationName).c_str());
289       }
290     }
291     else
292     {
293       DALI_SCRIPT_WARNING("Cannot find animations section\n");
294     }
295   }
296   else
297   {
298     // no named actor; presume self
299     GenericAction action;
300     action.actorName       = actor.GetName();
301     action.actionName      = *actionName;
302     GetParameters(child, action.parameters);
303     callback = action;
304   }
305
306   return callback;
307 }
308
309
310 /**
311  * Get a notification condition argument0 as 'arg0' 'value' or 'min'
312  */
313 float GetConditionArg0(const TreeNode &child)
314 {
315   OptionalFloat f = IsFloat( IsChild(child, "arg0") );
316   // allowing some human preferable alternatives
317   if(!f)
318   {
319     f = IsFloat( IsChild(child, "value") );
320   }
321   if(!f)
322   {
323     f = IsFloat( IsChild(child, "min") );
324   }
325
326   DALI_ASSERT_ALWAYS(f && "Notification condition for arg0 not specified");
327
328   return *f;
329 }
330
331 /**
332  * Get a notification condition argument1 as 'arg1' or 'max'
333  */
334 float GetConditionArg1(const TreeNode &child)
335 {
336   OptionalFloat f = IsFloat( IsChild(child, "arg1") );
337   // allowing some human preferable alternatives
338   if(!f)
339   {
340     f = IsFloat( IsChild(child, "max") );
341   }
342
343   DALI_ASSERT_ALWAYS(f && "Notification condition for arg1 not specified");
344
345   return *f;
346 }
347
348
349
350 }; // anon namespace
351
352 namespace Dali
353 {
354 namespace Toolkit
355 {
356 namespace Internal
357 {
358
359 Actor SetupSignalAction(const TreeNode &child, Actor actor);
360 Actor SetupPropertyNotification(const TreeNode &child, Actor actor);
361
362 /**
363  * Setup signals and actions on an actor
364  */
365 Actor SetupSignalAction(ConnectionTracker* tracker, const TreeNode &root, const TreeNode &child, Actor actor, boost::function<void (void)> quitAction)
366 {
367   DALI_ASSERT_ALWAYS(actor);
368
369   if(OptionalChild signalsChild = IsChild(child, "signals"))
370   {
371     const TreeNode& signalsNode = *signalsChild;
372     const TreeConstIter endIter = signalsNode.CEnd();
373     for( TreeConstIter iter = signalsNode.CBegin(); endIter != iter; ++iter )
374     {
375       const TreeNode::KeyNodePair& key_child = *iter;
376
377       DALI_SCRIPT_INFO("  Creating Signal for: %s\n", actor.GetName().c_str());
378
379       OptionalString name( IsString( IsChild( key_child.second, "name")) );
380       DALI_ASSERT_ALWAYS(name && "Signal must have a name");
381
382       boost::function<void (void)> callback = GetAction(root, key_child.second, actor, quitAction);
383
384       actor.ConnectSignal(tracker, *name, callback);
385     }
386   }
387
388   return actor;
389 }
390
391 /**
392  * Setup Property notifications for an actor
393  */
394 Actor SetupPropertyNotification(ConnectionTracker* tracker, const TreeNode &root, const TreeNode &child, Actor actor, boost::function<void (void)> quitAction)
395 {
396   DALI_ASSERT_ALWAYS(actor);
397
398   if(OptionalChild notificationsChild = IsChild(child,"notifications"))
399   {
400     const TreeNode& notificationsNode = *notificationsChild;
401     const TreeNode::ConstIterator endIter = notificationsNode.CEnd();
402     for( TreeNode::ConstIterator iter = notificationsNode.CBegin(); endIter != iter; ++iter )
403     {
404       const TreeNode::KeyNodePair& key_child = *iter;
405
406       // Actor actions reference by pointer because of circular reference actor->signal
407       // So this callback should only go onto the actor maintained list.
408       boost::function<void (void)> callback = GetAction(root, key_child.second, actor, quitAction);
409
410       OptionalString prop(IsString( IsChild(key_child.second, "property")) );
411       DALI_ASSERT_ALWAYS(prop && "Notification signal must specify a property");
412
413       Property::Index prop_index = actor.GetPropertyIndex(*prop);
414       DALI_ASSERT_ALWAYS(prop_index != Property::INVALID_INDEX && "Notification signal specifies an unknown property");
415
416       OptionalString cond(IsString( IsChild(key_child.second, "condition")));
417       DALI_ASSERT_ALWAYS(cond && "Notification signal must specify a condition");
418
419       if("False" == *cond)
420       {
421         PropertyNotification notification = actor.AddPropertyNotification( actor.GetPropertyIndex(*prop),
422                                                                            LessThanCondition(1.f) );
423         notification.NotifySignal().Connect( tracker, FunctorDelegate::New(callback) );
424       }
425       else if("LessThan" == *cond)
426       {
427         PropertyNotification notification = actor.AddPropertyNotification( actor.GetPropertyIndex(*prop),
428                                                                            LessThanCondition(GetConditionArg0(key_child.second)) );
429         notification.NotifySignal().Connect( tracker, FunctorDelegate::New(callback) );
430       }
431       else if("GreaterThan" == *cond)
432       {
433         PropertyNotification notification = actor.AddPropertyNotification( actor.GetPropertyIndex(*prop),
434                                                                            GreaterThanCondition(GetConditionArg0(key_child.second)) );
435         notification.NotifySignal().Connect( tracker, FunctorDelegate::New(callback) );
436       }
437       else if("Inside" == *cond)
438       {
439         PropertyNotification notification = actor.AddPropertyNotification( actor.GetPropertyIndex(*prop),
440                                                                            InsideCondition(GetConditionArg0(key_child.second),
441                                                                            GetConditionArg1(key_child.second)) );
442         notification.NotifySignal().Connect( tracker, FunctorDelegate::New(callback) );
443       }
444       else if("Outside" == *cond)
445       {
446         PropertyNotification notification = actor.AddPropertyNotification( actor.GetPropertyIndex(*prop),
447                                                                            OutsideCondition(GetConditionArg0(key_child.second),
448                                                                            GetConditionArg1(key_child.second)) );
449         notification.NotifySignal().Connect( tracker, FunctorDelegate::New(callback) );
450       }
451       else
452       {
453         DALI_ASSERT_ALWAYS(!"Unknown condition");
454       }
455     }
456   } // if notifications
457
458   return actor;
459
460 } // AddPropertyNotification
461
462
463 } // namespace Internal
464 } // namespace Toolkit
465 } // namespace Dali