/src/ftk_theme.c

http://ftk.googlecode.com/ · C · 608 lines · 481 code · 85 blank · 42 comment · 102 complexity · 8e19a613ab8cc200380360915ca08d44 MD5 · raw file

  1. /*
  2. * File: ftk_theme.c
  3. * Author: Li XianJing <xianjimli@hotmail.com>
  4. * Brief: theme
  5. *
  6. * Copyright (c) 2009 - 2010 Li XianJing <xianjimli@hotmail.com>
  7. *
  8. * Licensed under the Academic Free License version 2.1
  9. *
  10. * This program is free software; you can redistribute it and/or modify
  11. * it under the terms of the GNU General Public License as published by
  12. * the Free Software Foundation; either version 2 of the License, or
  13. * (at your option) any later version.
  14. *
  15. * This program is distributed in the hope that it will be useful,
  16. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  17. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  18. * GNU General Public License for more details.
  19. *
  20. * You should have received a copy of the GNU General Public License
  21. * along with this program; if not, write to the Free Software
  22. * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  23. */
  24. /*
  25. * History:
  26. * ================================================================
  27. * 2009-12-05 Li XianJing <xianjimli@hotmail.com> created
  28. *
  29. */
  30. #include "ftk_log.h"
  31. #include "ftk_util.h"
  32. #include "ftk_mmap.h"
  33. #include "ftk_theme.h"
  34. #include "ftk_globals.h"
  35. #include "ftk_icon_cache.h"
  36. #include "ftk_xml_parser.h"
  37. #include "ftk_animation_trigger_default.h"
  38. #include "ftk_animation_trigger_silence.h"
  39. static void ftk_init_default_path();
  40. typedef struct _FtkWidgetTheme
  41. {
  42. FtkColor bg[FTK_WIDGET_STATE_NR];
  43. FtkColor fg[FTK_WIDGET_STATE_NR];
  44. FtkColor border[FTK_WIDGET_STATE_NR];
  45. FtkBitmap* bg_image[FTK_WIDGET_STATE_NR];
  46. char bg_image_name[FTK_WIDGET_STATE_NR][32];
  47. FtkFontDesc* font_desc;
  48. }FtkWidgetTheme;
  49. struct _FtkTheme
  50. {
  51. char name[32];
  52. FtkIconCache* icon_cache;
  53. FtkFontDesc* default_font_desc;
  54. FtkAnimationTrigger* animation_trigger;
  55. FtkWidgetTheme widget_themes[FTK_WIDGET_TYPE_NR];
  56. };
  57. static const char* const s_default_theme = "\
  58. <theme name=\"default\">\
  59. <button bg_image[normal]=\"btn_default_normal"FTK_STOCK_IMG_SUFFIX"\" \
  60. bg_image[focused]=\"btn_default_selected"FTK_STOCK_IMG_SUFFIX"\" \
  61. bg_image[active]=\"btn_default_pressed"FTK_STOCK_IMG_SUFFIX"\" \
  62. bg_image[disable]=\"btn_default_normal_disable"FTK_STOCK_IMG_SUFFIX"\" \
  63. /> \
  64. <progress_bar \
  65. bg[normal]=\"ff9d9e9d\" fg[normal]=\"ffffd300\"\
  66. bg[focused]=\"ff9d9e9d\" fg[focused]=\"ffffd300\"\
  67. bg[active]=\"ff9d9e9d\" fg[active]=\"ffffd300\"\
  68. bg[disable]=\"ff9d9e9d\" fg[disable]=\"ffffd300\"\
  69. />\
  70. <entry \
  71. bg[normal]=\"ffffffff\" fg[normal]=\"ff000000\" bd[normal]=\"ffe2ceee\"\
  72. bg[focused]=\"ffffffff\" fg[focused]=\"ff000000\" bd[focused]=\"ffffbb00\"\
  73. bg[active]=\"ffffffff\" fg[active]=\"ff000000\" bd[active]=\"ffe2ceee\"\
  74. bg[disable]=\"ffffffff\" fg[disable]=\"ffaca899\" bd[disable]=\"ffe2ceee\"\
  75. />\
  76. <text_view \
  77. bg[normal]=\"ffffffff\" fg[normal]=\"ff000000\" bd[normal]=\"ffe2ceee\"\
  78. bg[focused]=\"ffffffff\" fg[focused]=\"ff000000\" bd[focused]=\"ffffbb00\"\
  79. bg[active]=\"ffffffff\" fg[active]=\"ff000000\" bd[active]=\"ffe2ceee\"\
  80. bg[disable]=\"ffffffff\" fg[disable]=\"ffaca899\" bd[disable]=\"ffe2ceee\"\
  81. />\
  82. <check_button \
  83. fg[normal]=\"ff000000\"\
  84. fg[focused]=\"ffffbb00\"\
  85. fg[active]=\"ffffbb00\"\
  86. fg[disable]=\"ffaca899\"\
  87. />\
  88. <radio_button \
  89. fg[normal]=\"ff000000\"\
  90. fg[focused]=\"ffffbb00\"\
  91. fg[active]=\"ffffbb00\"\
  92. fg[disable]=\"ffaca899\"\
  93. />\
  94. <menu_panel \
  95. bg[normal]=\"ffffffff\" fg[normal]=\"ffccc9b8\" bd[normal]=\"ffb0a080\"\
  96. />\
  97. </theme>";
  98. static Ret ftk_theme_init_default(FtkTheme* thiz)
  99. {
  100. #if defined(LINUX) || defined(WIN32)
  101. char filename[FTK_MAX_PATH + 1] = {0};
  102. ftk_strs_cat(filename, FTK_MAX_PATH, ftk_config_get_data_dir(ftk_default_config()),
  103. "/theme/default/theme.xml", NULL);
  104. ftk_normalize_path(filename);
  105. return ftk_theme_parse_file(thiz, filename);
  106. #else
  107. return ftk_theme_parse_data(thiz, s_default_theme, strlen(s_default_theme));
  108. #endif
  109. }
  110. FtkTheme* ftk_theme_create(int init_default)
  111. {
  112. FtkTheme* thiz = FTK_NEW(FtkTheme);
  113. ftk_init_default_path();
  114. if(thiz != NULL)
  115. {
  116. size_t i = 0;
  117. size_t j = 0;
  118. for(i = 0; i < FTK_WIDGET_TYPE_NR; i++)
  119. {
  120. for(j = 0; j < FTK_WIDGET_STATE_NR; j++)
  121. {
  122. /*init background color to white*/
  123. thiz->widget_themes[i].bg[j].a = 0xff;
  124. thiz->widget_themes[i].bg[j].r = 0xff;
  125. thiz->widget_themes[i].bg[j].g = 0xff;
  126. thiz->widget_themes[i].bg[j].b = 0xff;
  127. /*init foreground color to black*/
  128. thiz->widget_themes[i].fg[j].a = 0xff;
  129. thiz->widget_themes[i].fg[j].r = 0x00;
  130. thiz->widget_themes[i].fg[j].g = 0x00;
  131. thiz->widget_themes[i].fg[j].b = 0x00;
  132. }
  133. }
  134. if(init_default)
  135. {
  136. ftk_theme_init_default(thiz);
  137. }
  138. thiz->default_font_desc = ftk_font_desc_create(FTK_DEFAULT_FONT);
  139. }
  140. return thiz;
  141. }
  142. typedef struct _WidgetNameType
  143. {
  144. const char* name;
  145. FtkWidgetType type;
  146. }WidgetNameType;
  147. static const WidgetNameType s_widget_name_types[] =
  148. {
  149. {"label", FTK_LABEL},
  150. {"entry", FTK_ENTRY},
  151. {"text_view", FTK_TEXT_VIEW},
  152. {"image", FTK_IMAGE},
  153. {"button", FTK_BUTTON},
  154. {"wait_box", FTK_WAIT_BOX},
  155. {"group_box", FTK_GROUP_BOX},
  156. {"radio_button", FTK_RADIO_BUTTON},
  157. {"check_button", FTK_CHECK_BUTTON},
  158. {"progress_bar", FTK_PROGRESS_BAR},
  159. {"scroll_vbar", FTK_SCROLL_VBAR},
  160. {"scroll_hbar", FTK_SCROLL_HBAR},
  161. {"window", FTK_WINDOW},
  162. {"dialog", FTK_DIALOG},
  163. {"menu_item", FTK_MENU_ITEM},
  164. {"list_view", FTK_LIST_VIEW},
  165. {"status_item", FTK_STATUS_ITEM},
  166. {"status_panel", FTK_STATUS_PANEL},
  167. {"menu_panel", FTK_MENU_PANEL},
  168. {"icon_view", FTK_ICON_VIEW},
  169. {"combo_box", FTK_COMBO_BOX},
  170. {"tab_page", FTK_TAB_PAGE},
  171. {NULL, FTK_WIDGET_NONE},
  172. };
  173. static FtkWidgetType ftk_theme_get_widget_type(FtkTheme* thiz, const char* name)
  174. {
  175. size_t i = 0;
  176. return_val_if_fail(thiz != NULL && name != NULL, FTK_WIDGET_NONE);
  177. for(i = 0; s_widget_name_types[i].name != NULL; i++)
  178. {
  179. if(strcmp(name, s_widget_name_types[i].name) == 0)
  180. {
  181. return s_widget_name_types[i].type;
  182. }
  183. }
  184. return FTK_WIDGET_NONE;
  185. }
  186. typedef struct _ThemePrivInfo
  187. {
  188. FtkTheme* theme;
  189. }PrivInfo;
  190. #define TO_STATE(v)\
  191. switch(v)\
  192. {\
  193. case 'n':\
  194. {\
  195. state = FTK_WIDGET_NORMAL;\
  196. break;\
  197. }\
  198. case 'f':\
  199. {\
  200. state = FTK_WIDGET_FOCUSED;\
  201. break;\
  202. }\
  203. case 'a':\
  204. {\
  205. state = FTK_WIDGET_ACTIVE;\
  206. break;\
  207. }\
  208. case 'd':\
  209. {\
  210. state = FTK_WIDGET_INSENSITIVE;\
  211. break;\
  212. }\
  213. default:\
  214. {\
  215. return RET_FAIL;\
  216. }\
  217. }
  218. static Ret ftk_theme_parse_fg_color(FtkTheme* thiz, FtkWidgetType type, const char* name, const char* value)
  219. {
  220. FtkWidgetState state = FTK_WIDGET_NORMAL;
  221. FtkWidgetTheme* theme = thiz->widget_themes+type;
  222. TO_STATE(name[3]);
  223. theme->fg[state] = ftk_parse_color(value);
  224. // ftk_logd("fg type=%d state=%d (%02x %02x %2x %02x) \n", type, state,
  225. // theme->fg[state].a, theme->fg[state].r, theme->fg[state].g, theme->fg[state].b);
  226. return RET_OK;
  227. }
  228. static Ret ftk_theme_parse_bg_color(FtkTheme* thiz, FtkWidgetType type, const char* name, const char* value)
  229. {
  230. FtkWidgetState state = FTK_WIDGET_NORMAL;
  231. FtkWidgetTheme* theme = thiz->widget_themes+type;
  232. TO_STATE(name[3]);
  233. theme->bg[state] = ftk_parse_color(value);
  234. // ftk_logd("bg type=%d state=%d (%02x %02x %2x %02x) \n", type, state,
  235. // theme->bg[state].a, theme->bg[state].r, theme->bg[state].g, theme->bg[state].b);
  236. return RET_OK;
  237. }
  238. static Ret ftk_theme_parse_bd_color(FtkTheme* thiz, FtkWidgetType type, const char* name, const char* value)
  239. {
  240. FtkWidgetState state = FTK_WIDGET_NORMAL;
  241. FtkWidgetTheme* theme = thiz->widget_themes+type;
  242. TO_STATE(name[3]);
  243. theme->border[state] = ftk_parse_color(value);
  244. // ftk_logd("bd type=%d state=%d (%02x %02x %2x %02x) \n", type, state,
  245. // theme->border[state].a, theme->border[state].r, theme->border[state].g, theme->border[state].b);
  246. return RET_OK;
  247. }
  248. static Ret ftk_theme_parse_bg_image(FtkTheme* thiz, FtkWidgetType type, const char* name, const char* value)
  249. {
  250. FtkWidgetState state = FTK_WIDGET_NORMAL;
  251. FtkWidgetTheme* theme = thiz->widget_themes+type;
  252. TO_STATE(name[9]);
  253. ftk_strncpy(theme->bg_image_name[state], value, sizeof(theme->bg_image_name[state]));
  254. // ftk_logd("bg_image type=%d state=%d name=%s \n", type, state, theme->bg_image_name[state]);
  255. return RET_OK;
  256. }
  257. static void ftk_theme_builder_on_start(FtkXmlBuilder* thiz, const char* tag, const char** attrs)
  258. {
  259. size_t i = 0;
  260. DECL_PRIV(thiz, priv);
  261. const char* name = NULL;
  262. const char* value = NULL;
  263. FtkTheme* theme = priv->theme;
  264. FtkWidgetType type = FTK_WIDGET_NONE;
  265. return_if_fail(tag != NULL && attrs != NULL);
  266. if(strcmp(tag, "theme") == 0)
  267. {
  268. for(i = 0; attrs[i] != NULL; i += 2)
  269. {
  270. name = attrs[i];
  271. value = attrs[i+1];
  272. if(name[0] == 'n')
  273. {
  274. ftk_strncpy(theme->name, value, sizeof(theme->name));
  275. break;
  276. }
  277. }
  278. return;
  279. }
  280. else if(strcmp(tag, "animation_trigger") == 0)
  281. {
  282. const char* name = attrs[1];
  283. if(name != NULL && theme->animation_trigger == NULL)
  284. {
  285. if(strcmp(name, "silence") == 0)
  286. {
  287. theme->animation_trigger = ftk_animation_trigger_silence_create();
  288. }
  289. else
  290. {
  291. theme->animation_trigger = ftk_animation_trigger_default_create(theme->name, name);
  292. if(theme->animation_trigger == NULL)
  293. {
  294. ftk_logd("load animation %s failed.\n", name);
  295. theme->animation_trigger = ftk_animation_trigger_silence_create();
  296. }
  297. }
  298. }
  299. return;
  300. }
  301. type = ftk_theme_get_widget_type(theme, tag);
  302. return_if_fail(type != FTK_WIDGET_NONE);
  303. for(i = 0; attrs[i] != NULL; i += 2)
  304. {
  305. name = attrs[i];
  306. value = attrs[i+1];
  307. if(strlen(name) < 4)
  308. {
  309. ftk_logd("%s: unknow %s=%s\n", __func__, name, value);
  310. continue;
  311. }
  312. switch(name[0])
  313. {
  314. case 'f':
  315. {
  316. /*fg:forground color*/
  317. if(strstr(name, "fg[") != NULL)
  318. {
  319. ftk_theme_parse_fg_color(theme, type, name, value);
  320. }
  321. else if(strcmp(name, "font") == 0)
  322. {
  323. theme->widget_themes[type].font_desc = ftk_font_desc_create(value);
  324. }
  325. break;
  326. }
  327. case 'b':
  328. {
  329. /*bd:border color*/
  330. if(name[1] == 'd')
  331. {
  332. ftk_theme_parse_bd_color(theme, type, name, value);
  333. }
  334. else if(name[1] == 'g' && name[2] == '[')
  335. {
  336. /*bg:background color*/
  337. ftk_theme_parse_bg_color(theme, type, name, value);
  338. }
  339. else if(name[1] == 'g' && name[2] == '_')
  340. {
  341. /*bg_image:background image*/
  342. ftk_theme_parse_bg_image(theme, type, name, value);
  343. }
  344. else
  345. {
  346. ftk_logd("%s: unknow %s=%s\n", __func__, name, value);
  347. }
  348. break;
  349. }
  350. default:
  351. {
  352. ftk_logd("%s: unknow %s=%s\n", __func__, name, value);
  353. break;
  354. }
  355. }
  356. }
  357. return;
  358. }
  359. static void ftk_theme_builder_destroy(FtkXmlBuilder* thiz)
  360. {
  361. if(thiz != NULL)
  362. {
  363. FTK_ZFREE(thiz, sizeof(FtkXmlBuilder) + sizeof(PrivInfo));
  364. }
  365. return;
  366. }
  367. static FtkXmlBuilder* ftk_theme_builder_create(void)
  368. {
  369. FtkXmlBuilder* thiz = FTK_NEW_PRIV(FtkXmlBuilder);
  370. if(thiz != NULL)
  371. {
  372. thiz->on_start_element = ftk_theme_builder_on_start;
  373. thiz->destroy = ftk_theme_builder_destroy;
  374. }
  375. return thiz;
  376. }
  377. static const char* s_default_path[FTK_ICON_PATH_NR];
  378. static void ftk_init_default_path()
  379. {
  380. s_default_path[0] = ftk_config_get_data_dir(ftk_default_config());
  381. s_default_path[1] = ftk_config_get_data_root_dir(ftk_default_config());;
  382. s_default_path[2] = ftk_config_get_test_data_dir(ftk_default_config());
  383. return;
  384. }
  385. Ret ftk_theme_parse_data(FtkTheme* thiz, const char* xml, size_t length)
  386. {
  387. FtkXmlParser* parser = NULL;
  388. FtkXmlBuilder* builder = NULL;
  389. char icon_path[FTK_MAX_PATH] = {0};
  390. return_val_if_fail(xml != NULL, RET_FAIL);
  391. parser = ftk_xml_parser_create();
  392. return_val_if_fail(parser != NULL, RET_FAIL);
  393. builder = ftk_theme_builder_create();
  394. if(builder != NULL)
  395. {
  396. PrivInfo* priv = (PrivInfo*)builder->priv;
  397. priv->theme = thiz;
  398. ftk_xml_parser_set_builder(parser, builder);
  399. ftk_xml_parser_parse(parser, xml, length);
  400. }
  401. ftk_xml_builder_destroy(builder);
  402. ftk_xml_parser_destroy(parser);
  403. if(thiz->icon_cache != NULL)
  404. {
  405. ftk_icon_cache_destroy(thiz->icon_cache);
  406. thiz->icon_cache = NULL;
  407. }
  408. ftk_strs_cat(icon_path, FTK_MAX_PATH, "theme/", thiz->name, NULL);
  409. thiz->icon_cache = ftk_icon_cache_create(s_default_path, icon_path);
  410. if(thiz->animation_trigger == NULL)
  411. {
  412. thiz->animation_trigger = ftk_animation_trigger_silence_create();
  413. }
  414. return RET_OK;
  415. }
  416. Ret ftk_theme_parse_file(FtkTheme* thiz, const char* filename)
  417. {
  418. FtkMmap* m = NULL;
  419. Ret ret = RET_FAIL;
  420. return_val_if_fail(thiz != NULL && filename != NULL, RET_FAIL);
  421. m = ftk_mmap_create(filename, 0, -1);
  422. if(m == NULL)
  423. {
  424. ftk_logd("%s: mmap %s failed.\n", __func__, filename);
  425. }
  426. return_val_if_fail(m != NULL, RET_FAIL);
  427. ret = ftk_theme_parse_data(thiz, (const char*)ftk_mmap_data(m), ftk_mmap_length(m));
  428. ftk_mmap_destroy(m);
  429. return ret;
  430. }
  431. FtkBitmap* ftk_theme_get_bg(FtkTheme* thiz, FtkWidgetType type, FtkWidgetState state)
  432. {
  433. assert(type < FTK_WIDGET_TYPE_NR && state < FTK_WIDGET_STATE_NR);
  434. return_val_if_fail(thiz != NULL, NULL);
  435. if(thiz->widget_themes[type].bg_image[state] == NULL)
  436. {
  437. if(thiz->widget_themes[type].bg_image_name[state][0])
  438. {
  439. thiz->widget_themes[type].bg_image[state] = ftk_icon_cache_load(thiz->icon_cache,
  440. thiz->widget_themes[type].bg_image_name[state]);
  441. }
  442. }
  443. if(thiz->widget_themes[type].bg_image[state] != NULL)
  444. {
  445. ftk_bitmap_ref(thiz->widget_themes[type].bg_image[state]);
  446. }
  447. return thiz->widget_themes[type].bg_image[state];
  448. }
  449. FtkBitmap* ftk_theme_load_image(FtkTheme* thiz, const char* filename)
  450. {
  451. return_val_if_fail(thiz != NULL && filename != NULL, NULL);
  452. return ftk_icon_cache_load(thiz->icon_cache, filename);
  453. }
  454. FtkColor ftk_theme_get_bg_color(FtkTheme* thiz, FtkWidgetType type, FtkWidgetState state)
  455. {
  456. FtkColor c = {0};
  457. assert(type < FTK_WIDGET_TYPE_NR && state < FTK_WIDGET_STATE_NR);
  458. return_val_if_fail(thiz != NULL, c);
  459. return thiz->widget_themes[type].bg[state];
  460. }
  461. FtkColor ftk_theme_get_border_color(FtkTheme* thiz, FtkWidgetType type, FtkWidgetState state)
  462. {
  463. FtkColor c = {0};
  464. assert(type < FTK_WIDGET_TYPE_NR && state < FTK_WIDGET_STATE_NR);
  465. return_val_if_fail(thiz != NULL, c);
  466. return thiz->widget_themes[type].border[state];
  467. }
  468. FtkFontDesc* ftk_theme_get_font(FtkTheme* thiz, FtkWidgetType type)
  469. {
  470. FtkFontDesc* font_desc = NULL;
  471. assert(type < FTK_WIDGET_TYPE_NR);
  472. return_val_if_fail(thiz != NULL, NULL);
  473. if(thiz->widget_themes[type].font_desc != NULL)
  474. {
  475. font_desc = thiz->widget_themes[type].font_desc;
  476. }
  477. else
  478. {
  479. font_desc = thiz->default_font_desc;
  480. }
  481. ftk_font_desc_ref(font_desc);
  482. return font_desc;
  483. }
  484. FtkColor ftk_theme_get_fg_color(FtkTheme* thiz, FtkWidgetType type, FtkWidgetState state)
  485. {
  486. FtkColor c = {0};
  487. assert(type < FTK_WIDGET_TYPE_NR && state < FTK_WIDGET_STATE_NR);
  488. return_val_if_fail(thiz != NULL, c);
  489. return thiz->widget_themes[type].fg[state];
  490. }
  491. FtkAnimationTrigger* ftk_theme_get_animation_trigger(FtkTheme* thiz)
  492. {
  493. return_val_if_fail(thiz != NULL, NULL);
  494. return thiz->animation_trigger;
  495. }
  496. void ftk_theme_destroy(FtkTheme* thiz)
  497. {
  498. if(thiz != NULL)
  499. {
  500. size_t i = 0;
  501. size_t j = 0;
  502. ftk_font_desc_unref(thiz->default_font_desc);
  503. for(i = 0; i < FTK_WIDGET_TYPE_NR; i++)
  504. {
  505. for(j = 0; j < FTK_WIDGET_STATE_NR; j++)
  506. {
  507. if(thiz->widget_themes[i].bg_image[j] != NULL)
  508. {
  509. ftk_bitmap_unref(thiz->widget_themes[i].bg_image[j]);
  510. thiz->widget_themes[i].bg_image[j] = NULL;
  511. }
  512. }
  513. if(thiz->widget_themes[i].font_desc != NULL)
  514. {
  515. ftk_font_desc_unref(thiz->widget_themes[i].font_desc);
  516. }
  517. }
  518. ftk_icon_cache_destroy(thiz->icon_cache);
  519. ftk_animation_trigger_destroy(thiz->animation_trigger);
  520. FTK_ZFREE(thiz, sizeof(FtkTheme));
  521. }
  522. return;
  523. }