Merge branch 'new_text' into tizen
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / internal / styling / style-manager-impl.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 // CLASS HEADER
18 #include "style-manager-impl.h"
19
20 // EXTERNAL INCLUDES
21 #include <fstream>
22 #include <iostream>
23 #include <sstream>
24 #include <dali/public-api/adaptor-framework/singleton-service.h>
25 #include <dali/public-api/object/type-registry.h>
26 #include <dali/public-api/object/type-registry-helper.h>
27 #include <dali/integration-api/debug.h>
28
29 // INTERNAL INCLUDES
30 #include <dali-toolkit/public-api/controls/control.h>
31 #include <dali-toolkit/public-api/controls/control-impl.h>
32 #include <dali-toolkit/public-api/styling/style-manager.h>
33
34 namespace
35 {
36
37 const char* LANDSCAPE_QUALIFIER = "landscape";
38 const char* PORTRAIT_QUALIFIER  = "portrait";
39 const char* FONT_SIZE_QUALIFIER = "font-size-";
40
41 const char* DEFAULT_THEME = DALI_STYLE_DIR "tizen-default-theme.json";
42
43 const char* PACKAGE_PATH_KEY = "PACKAGE_PATH";
44 const char* DEFAULT_PACKAGE_PATH = DALI_DATA_READ_ONLY_DIR "/toolkit/";
45
46 } // namespace
47
48 namespace Dali
49 {
50
51 namespace Toolkit
52 {
53
54 namespace Internal
55 {
56
57 namespace
58 {
59
60 BaseHandle Create()
61 {
62   BaseHandle handle = StyleManager::Get();
63
64   if ( !handle )
65   {
66     SingletonService singletonService( SingletonService::Get() );
67     if ( singletonService )
68     {
69       Toolkit::StyleManager manager = Toolkit::StyleManager( new Internal::StyleManager() );
70       singletonService.Register( typeid( manager ), manager );
71       handle = manager;
72     }
73   }
74
75   return handle;
76 }
77
78 DALI_TYPE_REGISTRATION_BEGIN_CREATE( Toolkit::StyleManager, Dali::BaseHandle, Create, true )
79 DALI_TYPE_REGISTRATION_END()
80
81 } // namespace
82
83 Toolkit::StyleManager StyleManager::Get()
84 {
85   Toolkit::StyleManager manager;
86
87   SingletonService singletonService( SingletonService::Get() );
88   if ( singletonService )
89   {
90     // Check whether the style manager is already created
91     Dali::BaseHandle handle = singletonService.GetSingleton( typeid( Toolkit::StyleManager ) );
92     if( handle )
93     {
94       // If so, downcast the handle of singleton
95       manager = Toolkit::StyleManager( dynamic_cast< StyleManager* >( handle.GetObjectPtr() ) );
96     }
97   }
98
99   return manager;
100 }
101
102 StyleManager::StyleManager()
103 : mOrientationDegrees( 0 ),  // Portrait
104   mDefaultFontSize( -1 )
105 {
106   // Add theme builder constants
107   mThemeBuilderConstants[ PACKAGE_PATH_KEY ] = DEFAULT_PACKAGE_PATH;
108
109   RequestDefaultTheme();
110
111   StyleMonitor styleMonitor( StyleMonitor::Get() );
112   if( styleMonitor )
113   {
114     styleMonitor.StyleChangeSignal().Connect( this, &StyleManager::StyleMonitorChange );
115
116     mDefaultFontSize = styleMonitor.GetDefaultFontSize();
117   }
118 }
119
120 StyleManager::~StyleManager()
121 {
122 }
123
124 void StyleManager::SetOrientationValue( int orientation )
125 {
126   if( orientation !=  mOrientationDegrees )
127   {
128     mOrientationDegrees = orientation;
129     // TODO: if orientation changed, apply the new style to all controls
130     // dont want to really do the whole load from file again if the bundle contains both portrait & landscape
131     SetTheme();
132   }
133 }
134
135 int StyleManager::GetOrientationValue()
136 {
137   return mOrientationDegrees;
138 }
139
140 void StyleManager::SetOrientation( Orientation orientation )
141 {
142   if( mOrientation )
143   {
144     mOrientation.ChangedSignal().Disconnect( this, &StyleManager::OnOrientationChanged );
145   }
146
147   OnOrientationChanged( orientation );
148
149   if( mOrientation )
150   {
151     mOrientation.ChangedSignal().Connect( this, &StyleManager::OnOrientationChanged );
152   }
153 }
154
155 Orientation StyleManager::GetOrientation()
156 {
157   return mOrientation;
158 }
159
160 void StyleManager::SetStyleConstant( const std::string& key, const Property::Value& value )
161 {
162   mStyleBuilderConstants[ key ] = value;
163 }
164
165 bool StyleManager::GetStyleConstant( const std::string& key, Property::Value& valueOut )
166 {
167   Property::Value* value = mStyleBuilderConstants.Find( key );
168   if( value )
169   {
170     valueOut = *value;
171     return true;
172   }
173
174   return false;
175 }
176
177 void StyleManager::OnOrientationChanged( Orientation orientation )
178 {
179   mOrientation = orientation;
180   // TODO: if orientation changed, apply the new style to all controls
181   // dont want to really do the whole load from file again if the bundle contains both portrait & landscape
182   SetTheme();
183 }
184
185 Toolkit::Builder StyleManager::CreateBuilder( const Property::Map& constants )
186 {
187   Toolkit::Builder builder = Toolkit::Builder::New();
188   builder.AddConstants( constants );
189
190   return builder;
191 }
192
193 bool StyleManager::LoadJSON( Toolkit::Builder builder, const std::string& jsonFilePath )
194 {
195   std::string fileString;
196   if( LoadFile( jsonFilePath, fileString ) )
197   {
198     builder.LoadFromString( fileString );
199     return true;
200   }
201   else
202   {
203     DALI_LOG_WARNING("Error loading file '%s'\n", jsonFilePath.c_str());
204     return false;
205   }
206 }
207
208 void StyleManager::CollectQualifiers( StringList& qualifiersOut )
209 {
210   // Append the relevant qualifier for orientation
211   int orientation = mOrientationDegrees;
212
213   if( mOrientation )
214   {
215     orientation = mOrientation.GetDegrees();
216   }
217
218   switch( orientation )
219   {
220     case 90:
221     case 270:
222     {
223       qualifiersOut.push_back( std::string( LANDSCAPE_QUALIFIER ) );
224       break;
225     }
226     case 180:
227     case 0: // fall through
228     default:
229     {
230       qualifiersOut.push_back( std::string( PORTRAIT_QUALIFIER ) );
231       break;
232     }
233   }
234 }
235
236 void StyleManager::BuildQualifiedStyleName( const std::string& styleName, const StringList& qualifiers, std::string& qualifiedStyleOut )
237 {
238   qualifiedStyleOut.append( styleName );
239
240   for( StringList::const_iterator it = qualifiers.begin(), itEnd = qualifiers.end(); it != itEnd; ++it )
241   {
242     const std::string& str = *it;
243
244     qualifiedStyleOut.append( "-" );
245     qualifiedStyleOut.append( str );
246   }
247 }
248
249 void StyleManager::ApplyStyle( Toolkit::Builder builder, Toolkit::Control control )
250 {
251   std::string styleName = control.GetStyleName();
252   if( styleName.empty() )
253   {
254     // Convert control name to lower case
255     styleName = control.GetTypeName();
256     std::transform( styleName.begin(), styleName.end(), styleName.begin(), ::tolower );
257   }
258
259   // Apply the style after choosing the correct actual style (e.g. landscape or portrait)
260   StringList qualifiers;
261   CollectQualifiers( qualifiers );
262
263   while( true )
264   {
265     std::string qualifiedStyleName;
266     BuildQualifiedStyleName( styleName, qualifiers, qualifiedStyleName );
267
268     // Break if style found or we have tried the root style name (qualifiers is empty)
269     if( builder.ApplyStyle( qualifiedStyleName, control ) || qualifiers.size() == 0 )
270     {
271       break;
272     }
273
274     // Remove the last qualifier in an attempt to find a style that is valid
275     qualifiers.pop_back();
276   }
277
278   if( mDefaultFontSize >= 0 )
279   {
280     // Apply the style for logical font size
281     std::stringstream fontSizeQualifier;
282     fontSizeQualifier << styleName << "-" << FONT_SIZE_QUALIFIER << mDefaultFontSize;
283     builder.ApplyStyle( fontSizeQualifier.str(), control );
284   }
285 }
286
287 void StyleManager::ApplyThemeStyle( Toolkit::Control control )
288 {
289   if( mThemeBuilder )
290   {
291     ApplyStyle( mThemeBuilder, control );
292   }
293 }
294
295 void StyleManager::ApplyStyle( Toolkit::Control control, const std::string& jsonFileName, const std::string& styleName )
296 {
297   bool builderReady = false;
298
299   // First look in the cache
300   Toolkit::Builder builder = FindCachedBuilder( jsonFileName );
301   if( builder )
302   {
303     builderReady = true;
304   }
305   else
306   {
307     // Merge theme and style constants
308     Property::Map constants( mThemeBuilderConstants );
309     constants.Merge( mStyleBuilderConstants );
310
311     // Create it
312     builder = CreateBuilder( constants );
313
314     if( LoadJSON( builder, jsonFileName ) )
315     {
316       CacheBuilder( builder, jsonFileName );
317       builderReady = true;
318     }
319   }
320
321   // Apply the style to the control
322   if( builderReady )
323   {
324     builder.ApplyStyle( styleName, control );
325   }
326 }
327
328 bool StyleManager::LoadFile( const std::string& filename, std::string& stringOut )
329 {
330   DALI_ASSERT_DEBUG( 0 != filename.length());
331
332   std::ifstream in( filename.c_str(), std::ios::in );
333   if( in )
334   {
335     std::stringstream buffer;
336     buffer << in.rdbuf();
337
338     stringOut = buffer.str();
339
340     in.close();
341
342     return true;
343   }
344
345   return false;
346 }
347
348 Toolkit::StyleManager::StyleChangeSignalType& StyleManager::StyleChangeSignal()
349 {
350   return mStyleChangeSignal;
351 }
352
353 void StyleManager::RequestThemeChange( const std::string& themeFile )
354 {
355   mThemeFile = themeFile;
356
357   // need to do style change synchronously as app might create a UI control on the next line
358   SetTheme();
359 }
360
361 void StyleManager::RequestDefaultTheme()
362 {
363   RequestThemeChange( DEFAULT_THEME );
364 }
365
366 void StyleManager::SetTheme()
367 {
368   mThemeBuilder = CreateBuilder( mThemeBuilderConstants );
369   if ( LoadJSON( mThemeBuilder, mThemeFile ) )
370   {
371     StyleChange change;
372     change.themeChange = true;
373     mStyleChangeSignal.Emit( Toolkit::StyleManager::Get(), change );
374   }
375   else
376   {
377     mThemeBuilder.Reset();
378   }
379 }
380
381 Toolkit::Builder StyleManager::FindCachedBuilder( const std::string& key )
382 {
383   BuilderMap::iterator builderIt = mBuilderCache.find( key );
384   if( builderIt != mBuilderCache.end() )
385   {
386     return builderIt->second;
387   }
388
389   return Toolkit::Builder();
390 }
391
392 void StyleManager::CacheBuilder( Toolkit::Builder builder, const std::string& key )
393 {
394   mBuilderCache[ key ] = builder;
395 }
396
397 void StyleManager::StyleMonitorChange( StyleMonitor styleMonitor, StyleChange styleChange )
398 {
399   if( styleChange.defaultFontSizeChange )
400   {
401     mDefaultFontSize = styleMonitor.GetDefaultFontSize();
402   }
403
404   mStyleChangeSignal.Emit( Toolkit::StyleManager::Get(), styleChange );
405 }
406
407 } // namespace Internal
408
409 } // namespace Toolkit
410
411 } // namespace Dali