[dali_1.4.26] Merge branch 'devel/master'
[platform/core/uifw/dali-toolkit.git] / plugins / dali-script-v8 / src / controls / item-factory-wrapper.cpp
1 /*
2  * Copyright (c) 2017 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 // CLASS HEADER
19 #include "item-factory-wrapper.h"
20
21 // EXTERNAL INCLUDES
22 #include <dali/public-api/common/vector-wrapper.h>
23 #include <dali/public-api/object/weak-handle.h>
24 #include <dali-toolkit/devel-api/builder/builder.h>
25 #include <dali-toolkit/public-api/controls/scrollable/item-view/item-view.h>
26 #include <dali-toolkit/public-api/controls/scrollable/item-view/item-layout.h>
27
28 // INTERNAL INCLUDES
29 #include <v8-utils.h>
30 #include <dali-wrapper.h>
31 #include <shared/api-function.h>
32 #include <shared/object-template-helper.h>
33
34 namespace Dali
35 {
36
37 namespace V8Plugin
38 {
39
40 v8::Persistent<v8::ObjectTemplate> ItemFactoryWrapper::mItemFactoryTemplate;
41
42 namespace
43 {
44
45 typedef std::vector< Property::Map > ItemDataContainer;
46
47 // Implementation of ItemFactory for providing actors to ItemView
48 class ItemFactory : public Toolkit::ItemFactory
49 {
50 public:
51
52   /**
53    * Constructor
54    * @param application class, stored as reference
55    */
56   ItemFactory()
57   : mJsonFileLoaded(false),
58     mNumberOfItems(0)
59   {
60     mBuilder = Toolkit::Builder::New();
61   }
62
63   /**
64    * Set the name of the JSON file which defines the templates of items.
65    * @param jsonFile The JSON file
66    */
67   void SetJsonTemplateFile(std::string jsonFile)
68   {
69     if(mJsonFile != jsonFile)
70     {
71       mJsonFile = jsonFile;
72       LoadJsonFile(mJsonFile);
73
74       // Check whether any layout activated in ItemView
75       Toolkit::ItemView itemView = mItemView.GetHandle();
76       if(itemView && itemView.GetActiveLayout() != NULL)
77       {
78         // Refresh ItemView if item templates are changed
79         itemView.Refresh();
80       }
81     }
82   }
83
84   /**
85    * Returns the name of the JSON file.
86    * @return The JSON file name
87    */
88   std::string GetJsonTemplate()
89   {
90     return mJsonFile;
91   }
92
93   /**
94    * Set the data to be used to create new items.
95    *
96    * If ItemView is already created, this will immediately update ItemView with the
97    * new data.
98    *
99    * The data is an array of property maps in which each map contains the data for
100    * each item, including the template to be used to build the actor and the pairs
101    * of key/value to be used to replace the constants defined in the template.
102    * The order of property maps in the array represents the actual order of items
103    * in ItemView.
104    *
105    * @param data The array of property maps
106    */
107   void SetData(ItemDataContainer data)
108   {
109     ItemDataContainer currentData = mData;
110     mData = data;
111     mNumberOfItems = mData.size();
112
113     // Check whether any layout activated in ItemView
114     Toolkit::ItemView itemView = mItemView.GetHandle();
115     if(itemView && itemView.GetActiveLayout() != NULL)
116     {
117       unsigned int currentNumberOfItems = currentData.size();
118       unsigned int newNumberOfItems = data.size();
119
120       // Check whether any items added or deleted from the data
121       // which requires ItemView to be refreshed with the new data
122       if(currentNumberOfItems != newNumberOfItems)
123       {
124         itemView.Refresh();
125       }
126       else
127       {
128         for( unsigned int itemId = 0; itemId < newNumberOfItems; itemId++)
129         {
130           // Check whether the item is already built in ItemView
131           Actor itemActor = itemView.GetItem(itemId);
132           if(itemActor)
133           {
134             // Check if the item needs to be rebuilt
135             if( !V8Utils::IsPropertyMapIdentical(currentData[itemId], data[itemId]) )
136             {
137               // Rebuild the item with the new data
138               Actor newItemActor = NewItem(itemId);
139
140               // Replace the old item with the new one
141               itemView.ReplaceItem( Toolkit::Item( itemId, newItemActor ), 0.0f );
142             }
143           }
144         }
145       }
146     }
147   }
148
149   /**
150    * Retrieve the data.
151    * @return the data.
152    */
153   ItemDataContainer GetData()
154   {
155     return mData;
156   }
157
158   /**
159    * Store a weak handle of ItemView in order to access ItemView APIs
160    * from this ItemFactory implementation
161    * @return the data.
162    */
163   void SetItemView(Toolkit::ItemView itemView)
164   {
165     mItemView = itemView;
166   }
167
168 public: // From Toolkit::ItemFactory
169
170   /**
171    * Query the number of items available from the factory.
172    * The maximum available item has an ID of GetNumberOfItems() - 1.
173    */
174   virtual unsigned int GetNumberOfItems()
175   {
176     return mJsonFileLoaded ? mNumberOfItems : 0;
177   }
178
179   /**
180    * Create an Actor to represent a visible item.
181    * @param itemId
182    * @return the created actor.
183    */
184   virtual Actor NewItem(unsigned int itemId)
185   {
186     std::string itemTemplate;
187
188     Property::Map constantsMap = mData[itemId];
189     for ( unsigned int i = 0, count = constantsMap.Count(); i < count; ++i )
190     {
191       Property::Value& constantValue = constantsMap.GetValue(i);
192       if(constantsMap.GetKey(i) == "template")
193       {
194         constantValue.Get(itemTemplate);
195       }
196       else
197       {
198         mBuilder.AddConstant( constantsMap.GetKey(i), constantValue );
199       }
200     }
201
202     Actor item = Actor::DownCast( mBuilder.Create(itemTemplate) );
203     return item;
204   }
205
206 private:
207
208   /**
209    * Load the JSON file.
210    * @param The JSON file name
211    */
212   void LoadJsonFile(std::string jsonFile)
213   {
214     try
215     {
216       std::string data;
217       V8Utils::GetFileContents(jsonFile, data);
218
219       mBuilder.LoadFromString(data);
220
221       mJsonFileLoaded = true;
222     }
223     catch(...)
224     {
225 //      printf("invalid JSON data\n");
226       mJsonFileLoaded = false;
227     }
228   }
229
230 private:
231
232   std::string mJsonFile;
233   bool mJsonFileLoaded;
234   Toolkit::Builder mBuilder;
235   unsigned int mNumberOfItems;
236   ItemDataContainer mData;
237   WeakHandle< Toolkit::ItemView > mItemView;
238 };
239
240 } //un-named space
241
242 ItemFactoryWrapper::ItemFactoryWrapper( Toolkit::ItemFactory& factory, GarbageCollectorInterface& gc )
243 : BaseWrappedObject( BaseWrappedObject::ITEMFACTORY , gc ),
244   mItemFactory( factory )
245 {
246 }
247
248 ItemFactoryWrapper::~ItemFactoryWrapper()
249 {
250 }
251
252 v8::Handle<v8::Object> ItemFactoryWrapper::WrapItemFactory(v8::Isolate* isolate, Toolkit::ItemFactory& factory )
253 {
254   v8::EscapableHandleScope handleScope( isolate );
255   v8::Local<v8::ObjectTemplate> objectTemplate;
256
257   objectTemplate = GetItemFactoryTemplate( isolate );
258
259   // create an instance of the template
260   v8::Local<v8::Object> localObject = objectTemplate->NewInstance();
261
262   // create the ItemFactory wrapper
263   ItemFactoryWrapper* pointer =  new ItemFactoryWrapper( factory, Dali::V8Plugin::DaliWrapper::Get().GetDaliGarbageCollector() );
264
265   // assign the JavaScript object to the wrapper.
266   pointer->SetJavascriptObject( isolate, localObject );
267
268   return handleScope.Escape( localObject );
269 }
270
271 v8::Local<v8::ObjectTemplate> ItemFactoryWrapper::GetItemFactoryTemplate( v8::Isolate* isolate)
272 {
273   v8::EscapableHandleScope handleScope( isolate );
274   v8::Local<v8::ObjectTemplate> objectTemplate;
275
276   if( mItemFactoryTemplate.IsEmpty() )
277   {
278     objectTemplate = MakeItemFactoryTemplate( isolate );
279     mItemFactoryTemplate.Reset( isolate, objectTemplate );
280   }
281   else
282   {
283     // get the object template
284     objectTemplate = v8::Local<v8::ObjectTemplate>::New( isolate, mItemFactoryTemplate );
285   }
286   return handleScope.Escape( objectTemplate );
287 }
288
289 v8::Handle<v8::ObjectTemplate> ItemFactoryWrapper::MakeItemFactoryTemplate( v8::Isolate* isolate )
290 {
291   v8::EscapableHandleScope handleScope( isolate );
292
293   v8::Local<v8::ObjectTemplate> objTemplate = v8::ObjectTemplate::New();
294
295   objTemplate->SetInternalFieldCount( BaseWrappedObject::FIELD_COUNT );
296
297   // set property setter and getter
298   objTemplate->SetNamedPropertyHandler( ItemFactoryWrapper::PropertyGet, ItemFactoryWrapper::PropertySet);
299
300   return handleScope.Escape( objTemplate );
301 }
302
303 void ItemFactoryWrapper::NewItemFactory( const v8::FunctionCallbackInfo< v8::Value >& args)
304 {
305   v8::Isolate* isolate = args.GetIsolate();
306   v8::HandleScope handleScope( isolate);
307
308   if( !args.IsConstructCall() )
309   {
310     DALI_SCRIPT_EXCEPTION( isolate, "ItemFactory constructor called without 'new'" );
311     return;
312   }
313
314   Toolkit::ItemFactory* factory = new ItemFactory();
315
316   v8::Local<v8::Object> localObject = WrapItemFactory( isolate, *factory );
317   args.GetReturnValue().Set( localObject );
318 }
319
320 Toolkit::ItemFactory& ItemFactoryWrapper::GetItemFactoryFromParams( int paramIndex,
321                                              bool& found,
322                                              v8::Isolate* isolate,
323                                              const v8::FunctionCallbackInfo< v8::Value >& args )
324 {
325   found = false;
326
327   v8::HandleScope handleScope( isolate );
328   BaseWrappedObject* wrappedObject = V8Utils::GetWrappedDaliObjectParameter( paramIndex, BaseWrappedObject::ITEMFACTORY, isolate, args );
329   if( wrappedObject )
330   {
331     found = true;
332     ItemFactoryWrapper* wrapper = static_cast< ItemFactoryWrapper *>(wrappedObject);
333     return wrapper->GetItemFactory();
334   }
335   else
336   {
337     DALI_SCRIPT_EXCEPTION( isolate, "no valid ItemFactory parameter" );
338     Toolkit::ItemFactory* dummyFactory = new ItemFactory();
339     return *dummyFactory; // avoid build error
340   }
341 }
342
343 ItemFactoryWrapper* ItemFactoryWrapper::Unwrap( v8::Isolate* isolate, v8::Handle< v8::Object> obj)
344 {
345   v8::HandleScope handleScope( isolate );
346
347   v8::Local<v8::External> field = v8::Local<v8::External>::Cast( obj->GetInternalField(0) );
348   void* ptr = field->Value();
349   return static_cast< ItemFactoryWrapper *>(ptr);
350 }
351
352 void ItemFactoryWrapper::PropertyGet( v8::Local<v8::String> propertyName,
353                                         const v8::PropertyCallbackInfo<v8::Value>& info)
354 {
355   v8::Isolate* isolate = info.GetIsolate();
356   v8::HandleScope handleScope( isolate );
357
358   // get the property name
359   std::string name = V8Utils::v8StringToStdString( propertyName );
360
361   if( std::isupper( name[0] ))
362   {
363     return;
364   }
365
366   // unwrap the object
367   ItemFactoryWrapper* itemFactoryWrapper = Unwrap( isolate, info.This() );
368   if( !itemFactoryWrapper )
369   {
370     return;
371   }
372
373   ItemFactory& factory = static_cast<ItemFactory&>( itemFactoryWrapper->GetItemFactory() );
374
375   if( name == "jsonTemplateFile" )
376   {
377     std::string jsonTemplateFile = factory.GetJsonTemplate();
378     info.GetReturnValue().Set(v8::String::NewFromUtf8(isolate, jsonTemplateFile.c_str()));
379   }
380   else if( name == "data" )
381   {
382     ItemDataContainer data = factory.GetData();
383     unsigned int itemCount = data.size();
384
385     v8::Local<v8::Array> array= v8::Array::New( isolate, itemCount );
386     for( unsigned int i = 0; i < itemCount; i++)
387     {
388       v8::Local<v8::Object> mapObject = v8::Object::New( isolate );
389       V8Utils::CreatePropertyMap( isolate, data[i], mapObject );
390
391       array->Set( i, mapObject);
392     }
393
394     info.GetReturnValue().Set(array);
395   }
396   else
397   {
398     std::string error="Invalid property Get for "+name + "\n";
399     DALI_SCRIPT_EXCEPTION( isolate, error );
400   }
401 }
402
403 void ItemFactoryWrapper::PropertySet( v8::Local<v8::String> propertyName,
404                   v8::Local<v8::Value> javaScriptValue,
405                   const v8::PropertyCallbackInfo<v8::Value>& info)
406 {
407
408   v8::Isolate* isolate = info.GetIsolate();
409   v8::HandleScope handleScope( isolate );
410
411   // get the property name
412   std::string name = V8Utils::v8StringToStdString( propertyName );
413
414   // unwrap the object
415   ItemFactoryWrapper* itemFactoryWrapper = Unwrap( isolate, info.This() );
416   if( !itemFactoryWrapper )
417   {
418     return;
419   }
420
421   ItemFactory& factory = static_cast<ItemFactory&>( itemFactoryWrapper->GetItemFactory() );
422
423   if( name == "jsonTemplateFile" && javaScriptValue->IsString() )
424   {
425     std::string jsonTemplateFile = V8Utils::v8StringToStdString( javaScriptValue );
426     factory.SetJsonTemplateFile(jsonTemplateFile);
427   }
428   else if( name == "data" && javaScriptValue->IsArray() )
429   {
430     v8::Local<v8::Array> array = v8::Local<v8::Array>::Cast(javaScriptValue);
431
432     ItemDataContainer data;
433
434     for( unsigned int i = 0; i < array->Length(); ++i )
435     {
436       v8::Local<v8::Value> itemData = array->Get(i);
437
438       if( itemData->IsObject() )
439       {
440         Dali::Property::Map map = V8Utils::GetPropertyMapFromObject( isolate, itemData->ToObject() );
441         data.push_back(map);
442       }
443     }
444
445     factory.SetData(data);
446   }
447   else
448   {
449     std::string error = "Invalid property Set for " + name + "\n";
450     DALI_SCRIPT_EXCEPTION( isolate, error );
451   }
452 }
453
454 Toolkit::ItemFactory& ItemFactoryWrapper::GetItemFactory()
455 {
456   return mItemFactory;
457 }
458
459 void ItemFactoryWrapper::SetItemView(Toolkit::ItemFactory& itemFactory, Toolkit::ItemView itemView)
460 {
461   ItemFactory& factory = static_cast<ItemFactory&>( itemFactory );
462   factory.SetItemView(itemView);
463 }
464
465 } // namespace V8Plugin
466
467 } // namespace Dali