60fa86800cd7239c86ef689ec3b79e4af57fbda3
[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   // Convert control name to lower case
252   std::string styleName = control.GetTypeName();
253   std::transform( styleName.begin(), styleName.end(), styleName.begin(), ::tolower );
254
255   // Apply the style after choosing the correct actual style (e.g. landscape or portrait)
256   StringList qualifiers;
257   CollectQualifiers( qualifiers );
258
259   while( true )
260   {
261     std::string qualifiedStyleName;
262     BuildQualifiedStyleName( styleName, qualifiers, qualifiedStyleName );
263
264     // Break if style found or we have tried the root style name (qualifiers is empty)
265     if( builder.ApplyStyle( qualifiedStyleName, control ) || qualifiers.size() == 0 )
266     {
267       break;
268     }
269
270     // Remove the last qualifier in an attempt to find a style that is valid
271     qualifiers.pop_back();
272   }
273
274   if( mDefaultFontSize >= 0 )
275   {
276     // Apply the style for logical font size
277     std::stringstream fontSizeQualifier;
278     fontSizeQualifier << styleName << "-" << FONT_SIZE_QUALIFIER << mDefaultFontSize;
279     builder.ApplyStyle( fontSizeQualifier.str(), control );
280   }
281 }
282
283 void StyleManager::ApplyThemeStyle( Toolkit::Control control )
284 {
285   if( mThemeBuilder )
286   {
287     ApplyStyle( mThemeBuilder, control );
288   }
289 }
290
291 void StyleManager::ApplyStyle( Toolkit::Control control, const std::string& jsonFileName, const std::string& styleName )
292 {
293   bool builderReady = false;
294
295   // First look in the cache
296   Toolkit::Builder builder = FindCachedBuilder( jsonFileName );
297   if( builder )
298   {
299     builderReady = true;
300   }
301   else
302   {
303     // Merge theme and style constants
304     Property::Map constants( mThemeBuilderConstants );
305     constants.Merge( mStyleBuilderConstants );
306
307     // Create it
308     builder = CreateBuilder( constants );
309
310     if( LoadJSON( builder, jsonFileName ) )
311     {
312       CacheBuilder( builder, jsonFileName );
313       builderReady = true;
314     }
315   }
316
317   // Apply the style to the control
318   if( builderReady )
319   {
320     builder.ApplyStyle( styleName, control );
321   }
322 }
323
324 bool StyleManager::LoadFile( const std::string& filename, std::string& stringOut )
325 {
326   DALI_ASSERT_DEBUG( 0 != filename.length());
327
328   std::ifstream in( filename.c_str(), std::ios::in );
329   if( in )
330   {
331     std::stringstream buffer;
332     buffer << in.rdbuf();
333
334     stringOut = buffer.str();
335
336     in.close();
337
338     return true;
339   }
340
341   return false;
342 }
343
344 Toolkit::StyleManager::StyleChangeSignalType& StyleManager::StyleChangeSignal()
345 {
346   return mStyleChangeSignal;
347 }
348
349 void StyleManager::RequestThemeChange( const std::string& themeFile )
350 {
351   mThemeFile = themeFile;
352
353   // need to do style change synchronously as app might create a UI control on the next line
354   SetTheme();
355 }
356
357 void StyleManager::RequestDefaultTheme()
358 {
359   RequestThemeChange( DEFAULT_THEME );
360 }
361
362 void StyleManager::SetTheme()
363 {
364   mThemeBuilder = CreateBuilder( mThemeBuilderConstants );
365   if ( LoadJSON( mThemeBuilder, mThemeFile ) )
366   {
367     StyleChange change;
368     change.themeChange = true;
369     mStyleChangeSignal.Emit( Toolkit::StyleManager::Get(), change );
370   }
371   else
372   {
373     mThemeBuilder.Reset();
374   }
375 }
376
377 Toolkit::Builder StyleManager::FindCachedBuilder( const std::string& key )
378 {
379   BuilderMap::iterator builderIt = mBuilderCache.find( key );
380   if( builderIt != mBuilderCache.end() )
381   {
382     return builderIt->second;
383   }
384
385   return Toolkit::Builder();
386 }
387
388 void StyleManager::CacheBuilder( Toolkit::Builder builder, const std::string& key )
389 {
390   mBuilderCache[ key ] = builder;
391 }
392
393 void StyleManager::StyleMonitorChange( StyleMonitor styleMonitor, StyleChange styleChange )
394 {
395   if( styleChange.defaultFontSizeChange )
396   {
397     mDefaultFontSize = styleMonitor.GetDefaultFontSize();
398   }
399
400   mStyleChangeSignal.Emit( Toolkit::StyleManager::Get(), styleChange );
401 }
402
403 } // namespace Internal
404
405 } // namespace Toolkit
406
407 } // namespace Dali