/apps/designer/ftk_app_designer.c

http://ftk.googlecode.com/ · C · 676 lines · 524 code · 121 blank · 31 comment · 112 complexity · 52c3162198ba0aebe602da722605f2b9 MD5 · raw file

  1. /*
  2. * File: ftk_app_designer.c
  3. * Author: Li XianJing <xianjimli@hotmail.com>
  4. * Brief: ui designer app.
  5. *
  6. * Copyright (c) 2009 - 2011 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. * 2011-09-27 Li XianJing <xianjimli@hotmail.com> created
  28. *
  29. */
  30. #include "open.h"
  31. #include "save.h"
  32. #include "widgets_info.h"
  33. #include "widget_editor.h"
  34. #include "ftk_popup_menu.h"
  35. #include "ftk_app_designer.h"
  36. typedef struct _PrivInfo
  37. {
  38. FtkBitmap* icon;
  39. }PrivInfo;
  40. typedef struct _Info
  41. {
  42. int last_x;
  43. int last_y;
  44. int ctrl_down;
  45. int alt_down;
  46. int mouse_down;
  47. FtkWidget* selected_widget;
  48. }Info;
  49. static Ret designer_on_event(void* ctx, void* data);
  50. static Ret designer_on_prepare_options_menu(void* ctx, FtkWidget* menu_panel);
  51. static Info* info_create(void)
  52. {
  53. Info* info = FTK_NEW(Info);
  54. return info;
  55. }
  56. static void info_destroy(void* data)
  57. {
  58. FTK_FREE(data);
  59. return;
  60. }
  61. static FtkBitmap* ftk_app_designer_get_icon(FtkApp* thiz)
  62. {
  63. DECL_PRIV(thiz, priv);
  64. return_val_if_fail(priv != NULL, NULL);
  65. if(priv->icon != NULL) return priv->icon;
  66. priv->icon = ftk_app_load_bitmap(thiz, "designer", "designer");
  67. return priv->icon;
  68. }
  69. static const char* ftk_app_designer_get_name(FtkApp* thiz)
  70. {
  71. return _("Designer");
  72. }
  73. static Ret designer_init(FtkWidget* win)
  74. {
  75. ftk_widget_set_user_data(win, info_destroy, info_create());
  76. ftk_widget_set_event_listener(win, designer_on_event, win);
  77. ftk_app_window_set_on_prepare_options_menu(win, designer_on_prepare_options_menu, win);
  78. ftk_widget_show(win, 1);
  79. return RET_OK;
  80. }
  81. static Ret designer_on_new(void* ctx, void* item)
  82. {
  83. FtkWidget* win = NULL;
  84. FtkWidget* old_win = (FtkWidget*)ctx;
  85. win = ftk_app_window_create();
  86. ftk_widget_set_text(win, _("FTK UI Designer"));
  87. ftk_window_set_animation_hint(win, "app_main_window");
  88. designer_init(win);
  89. if(old_win != NULL)
  90. {
  91. ftk_widget_unref(old_win);
  92. }
  93. return RET_OK;
  94. }
  95. static Ret designer_on_open_ok(void* ctx, FtkWidget* win)
  96. {
  97. FtkWidget* old_win = (FtkWidget*)ctx;
  98. if(win != NULL)
  99. {
  100. designer_init(win);
  101. }
  102. ftk_widget_unref(old_win);
  103. return RET_OK;
  104. }
  105. static Ret designer_on_open(void* ctx, void* item)
  106. {
  107. ftk_widget_editor_open(ctx, designer_on_open_ok);
  108. return RET_OK;
  109. }
  110. static Ret designer_on_save(void* ctx, void* item)
  111. {
  112. FtkWidget* win = (FtkWidget*)ctx;
  113. ftk_widget_editor_save(win);
  114. return RET_OK;
  115. }
  116. static Ret designer_on_help(void* ctx, void* item)
  117. {
  118. ftk_tips("Ctrl + UP|DOWN|LEFT|RIGHT: move widget.\n Alt + UP|DOWN|LEFT|RIGHT: resize widget\n <F5>: popup menu.\n");
  119. return RET_OK;
  120. }
  121. static Ret designer_on_quit(void* ctx, void* item)
  122. {
  123. FtkWidget* win = (FtkWidget*)ctx;
  124. ftk_widget_unref(win);
  125. return RET_OK;
  126. }
  127. static int designer_has_selected_widget(FtkWidget* win)
  128. {
  129. Info* info = (Info*)ftk_widget_user_data(win);
  130. return (info->selected_widget != NULL && info->selected_widget != win);
  131. }
  132. static Ret designer_on_duplicate(void* ctx, void* item)
  133. {
  134. FtkWidget* widget = NULL;
  135. FtkWidget* win = (FtkWidget*)ctx;
  136. const WidgetInfo* widget_info = NULL;
  137. Info* info = (Info*)ftk_widget_user_data(win);
  138. if(!designer_has_selected_widget(win))
  139. {
  140. return RET_OK;
  141. }
  142. widget_info = widgets_info_find_by_type(ftk_widget_type(info->selected_widget));
  143. return_val_if_fail(widget_info != NULL, RET_FAIL);
  144. if(widget_info->is_leaf_widget)
  145. {
  146. widget = widget_info->create(
  147. ftk_widget_parent(info->selected_widget),
  148. ftk_widget_left(info->selected_widget),
  149. ftk_widget_top(info->selected_widget) + ftk_widget_height(info->selected_widget),
  150. ftk_widget_width(info->selected_widget),
  151. ftk_widget_height(info->selected_widget));
  152. ftk_widget_set_text(widget, ftk_widget_get_text(info->selected_widget));
  153. ftk_widget_show(widget, 1);
  154. ftk_widget_ref(widget);
  155. /*TODO: duplicate listview/combobox/iconview*/
  156. }
  157. return RET_OK;
  158. }
  159. static Ret designer_on_insert(void* ctx, void* item)
  160. {
  161. int x = 0;
  162. int y = 0;
  163. FtkWidget* parent = NULL;
  164. FtkWidget* win = (FtkWidget*)ctx;
  165. const WidgetInfo* widget_info = NULL;
  166. Info* info = (Info*)ftk_widget_user_data(win);
  167. parent = win;
  168. if(designer_has_selected_widget(win))
  169. {
  170. widget_info = widgets_info_find_by_type(ftk_widget_type(info->selected_widget));
  171. parent = widget_info->is_leaf_widget ? ftk_widget_parent(info->selected_widget) : info->selected_widget;
  172. }
  173. x = info->last_x - ftk_widget_left_abs(parent);
  174. y = info->last_y - ftk_widget_top_abs(parent);
  175. ftk_widget_editor_new(parent, x, y);
  176. return RET_OK;
  177. }
  178. static Ret designer_on_delete(void* ctx, void* item)
  179. {
  180. FtkWidget* win = (FtkWidget*)ctx;
  181. Info* info = (Info*)ftk_widget_user_data(win);
  182. if(designer_has_selected_widget(win))
  183. {
  184. ftk_widget_remove_child(ftk_widget_parent(info->selected_widget), info->selected_widget);
  185. info->selected_widget = NULL;
  186. ftk_widget_invalidate(win);
  187. }
  188. return RET_OK;
  189. }
  190. static Ret designer_on_prop(void* ctx, void* item)
  191. {
  192. FtkWidget* parent = NULL;
  193. FtkWidget* widget = NULL;
  194. FtkWidget* win = (FtkWidget*)ctx;
  195. Info* info = (Info*)ftk_widget_user_data(win);
  196. if(designer_has_selected_widget(win))
  197. {
  198. widget = info->selected_widget;
  199. parent = ftk_widget_parent(info->selected_widget);
  200. }
  201. else
  202. {
  203. widget = win;
  204. parent = win;
  205. }
  206. ftk_widget_editor_edit(parent, widget);
  207. return RET_OK;
  208. }
  209. static Ret designer_on_special_prop(void* ctx, void* item)
  210. {
  211. const WidgetInfo* widget_info = NULL;
  212. FtkWidget* win = (FtkWidget*)ctx;
  213. Info* info = (Info*)ftk_widget_user_data(win);
  214. if(designer_has_selected_widget(win))
  215. {
  216. widget_info = widgets_info_find_by_type(ftk_widget_type(info->selected_widget));
  217. if(widget_info != NULL && widget_info->edit != NULL)
  218. {
  219. widget_info->edit(info->selected_widget);
  220. }
  221. }
  222. return RET_OK;
  223. }
  224. typedef struct _MenuItem
  225. {
  226. int need_selected_widget;
  227. char* name;
  228. const char* icon_file_name;
  229. FtkListener on_clicked;
  230. }MenuItem;
  231. static const MenuItem s_menu_items[] =
  232. {
  233. {0, "New", NULL, designer_on_new},
  234. {0, "Open", NULL, designer_on_open},
  235. {0, "Save", NULL, designer_on_save},
  236. {0, "Help", NULL, designer_on_help},
  237. {0, "Quit", NULL, designer_on_quit}
  238. };
  239. static const MenuItem s_popup_menu_items[] =
  240. {
  241. {0, "Insert", NULL, designer_on_insert},
  242. {1, "Duplicate", NULL, designer_on_duplicate},
  243. {1, "Delete", NULL, designer_on_delete},
  244. {0, "General property", NULL, designer_on_prop},
  245. {1, "Special property", NULL, designer_on_special_prop},
  246. {0, "Cancel", NULL, NULL}
  247. };
  248. static Ret designer_on_popup_menu_item_clicked(void* ctx, void* data)
  249. {
  250. FtkListItemInfo* info = data;
  251. FtkListener on_clicked = (FtkListener)info->extra_user_data;
  252. if(on_clicked != NULL)
  253. {
  254. on_clicked(ctx, NULL);
  255. }
  256. return RET_OK;
  257. }
  258. static Ret designer_on_popup_menu(void* ctx, void* obj)
  259. {
  260. size_t i = 0;
  261. int nr = 0;
  262. int height = 0;
  263. FtkBitmap* icon = NULL;
  264. FtkWidget* popup = NULL;
  265. FtkListItemInfo infos;
  266. FtkWidget* win = (FtkWidget*)ctx;
  267. memset(&infos, 0x00, sizeof(infos));
  268. icon = ftk_theme_load_image(ftk_default_theme(), "info"FTK_STOCK_IMG_SUFFIX);
  269. for(i = 0; i < FTK_ARRAY_SIZE(s_popup_menu_items); i++)
  270. {
  271. if(s_popup_menu_items[i].need_selected_widget && !designer_has_selected_widget(win))
  272. {
  273. continue;
  274. }
  275. nr++;
  276. }
  277. height = (nr + 1) * FTK_POPUP_MENU_ITEM_HEIGHT;
  278. height = height < ftk_widget_height(win) ? height : ftk_widget_height(win);
  279. popup = ftk_popup_menu_create(0, 0, 0, height, icon, _("Edit"));
  280. infos.state = 0;
  281. infos.type = FTK_LIST_ITEM_NORMAL;
  282. for(i = 0; i < FTK_ARRAY_SIZE(s_popup_menu_items); i++)
  283. {
  284. if(s_popup_menu_items[i].need_selected_widget && !designer_has_selected_widget(win))
  285. {
  286. continue;
  287. }
  288. infos.value = i;
  289. infos.text = s_popup_menu_items[i].name;
  290. infos.extra_user_data = s_popup_menu_items[i].on_clicked;
  291. ftk_popup_menu_add(popup, &infos);
  292. }
  293. ftk_bitmap_unref(icon);
  294. ftk_popup_menu_set_clicked_listener(popup, designer_on_popup_menu_item_clicked, ctx);
  295. ftk_widget_show_all(popup, 1);
  296. return RET_OK;
  297. }
  298. static Ret designer_on_prepare_options_menu(void* ctx, FtkWidget* menu_panel)
  299. {
  300. size_t i = 0;
  301. FtkWidget* item = NULL;
  302. FtkWidget* win = (FtkWidget*)ctx;
  303. for(i = 0; i < FTK_ARRAY_SIZE(s_menu_items); i++)
  304. {
  305. item = ftk_menu_item_create(menu_panel);
  306. ftk_widget_set_text(item, _(s_menu_items[i].name));
  307. ftk_menu_item_set_clicked_listener(item, s_menu_items[i].on_clicked, win);
  308. ftk_widget_show(item, 1);
  309. }
  310. return RET_OK;
  311. }
  312. static Ret designer_move_widget(FtkWidget* widget, int x, int y, int w, int h)
  313. {
  314. int parent_w = ftk_widget_width(ftk_widget_parent(widget));
  315. int parent_h = ftk_widget_height(ftk_widget_parent(widget));
  316. const WidgetInfo* widget_info = widgets_info_find_by_type(ftk_widget_type(widget));
  317. return_val_if_fail(widget_info != NULL, RET_OK);
  318. x = x >= 0 ? x : 0;
  319. y = y >= 0 ? y : 0;
  320. w = FTK_MAX(w, widget_info->min_width);
  321. h = FTK_MAX(h, widget_info->min_height);
  322. if((x + w) < parent_w && (y + h) < parent_h)
  323. {
  324. ftk_widget_move_resize(widget, x, y, w, h);
  325. }
  326. return RET_OK;
  327. }
  328. static Ret designer_handle_direction_key(FtkWidget* win, int press, int code)
  329. {
  330. int x = 0;
  331. int y = 0;
  332. int w = 0;
  333. int h = 0;
  334. Ret ret = RET_OK;
  335. Info* info = (Info*)ftk_widget_user_data(win);
  336. x = ftk_widget_left(info->selected_widget);
  337. y = ftk_widget_top(info->selected_widget);
  338. w = ftk_widget_width(info->selected_widget);
  339. h = ftk_widget_height(info->selected_widget);
  340. switch(code)
  341. {
  342. case FTK_KEY_UP:
  343. {
  344. if(info->ctrl_down)
  345. {
  346. y--;
  347. }
  348. if(info->alt_down)
  349. {
  350. h--;
  351. }
  352. break;
  353. }
  354. case FTK_KEY_DOWN:
  355. {
  356. if(info->ctrl_down)
  357. {
  358. y++;
  359. }
  360. if(info->alt_down)
  361. {
  362. h++;
  363. }
  364. break;
  365. }
  366. case FTK_KEY_LEFT:
  367. {
  368. if(info->ctrl_down)
  369. {
  370. x--;
  371. }
  372. if(info->alt_down)
  373. {
  374. w--;
  375. }
  376. break;
  377. }
  378. case FTK_KEY_RIGHT:
  379. {
  380. if(info->ctrl_down)
  381. {
  382. x++;
  383. }
  384. if(info->alt_down)
  385. {
  386. w++;
  387. }
  388. break;
  389. }
  390. default:break;
  391. }
  392. if(info->ctrl_down || info->alt_down)
  393. {
  394. designer_move_widget(info->selected_widget, x, y, w, h);
  395. ret = RET_REMOVE;
  396. }
  397. return ret;
  398. }
  399. static Ret designer_on_key_event(FtkWidget* win, int press, int code)
  400. {
  401. Ret ret = RET_OK;
  402. Info* info = (Info*)ftk_widget_user_data(win);
  403. /*XXX: Li XianJing 37 is the key code of left ctrl on my thinkpad, I dont know why.*/
  404. if(code == FTK_KEY_LEFTCTRL || code == FTK_KEY_RIGHTCTRL || code == 37)
  405. {
  406. info->ctrl_down = press;
  407. return RET_OK;
  408. }
  409. if(code == FTK_KEY_LEFTALT || code == FTK_KEY_RIGHTALT
  410. || code == FTK_KEY_LEFTSHIFT || code == FTK_KEY_RIGHTSHIFT)
  411. {
  412. info->alt_down = press;
  413. return RET_OK;
  414. }
  415. if(press)
  416. {
  417. if((info->ctrl_down || info->alt_down))
  418. {
  419. return RET_REMOVE;
  420. }
  421. else
  422. {
  423. return RET_OK;
  424. }
  425. }
  426. else
  427. {
  428. if(code == FTK_KEY_F5)
  429. {
  430. designer_on_popup_menu(win, NULL);
  431. }
  432. else if(code == FTK_KEY_INSERT)
  433. {
  434. designer_on_insert(win, NULL);
  435. }
  436. else if(code == FTK_KEY_DELETE)
  437. {
  438. designer_on_delete(win, NULL);
  439. }
  440. if(!info->ctrl_down && !info->alt_down)
  441. {
  442. if(code == FTK_KEY_TAB || code == FTK_KEY_LEFT || code == FTK_KEY_UP || code == FTK_KEY_DOWN || code == FTK_KEY_RIGHT)
  443. {
  444. info->selected_widget = ftk_window_get_focus(win);
  445. }
  446. return RET_OK;
  447. }
  448. }
  449. if(!designer_has_selected_widget(win))
  450. {
  451. if(info->ctrl_down || info->alt_down)
  452. {
  453. return RET_REMOVE;
  454. }
  455. else
  456. {
  457. return RET_OK;
  458. }
  459. }
  460. ret = designer_handle_direction_key(win, press, code);
  461. return ret;
  462. }
  463. static Ret designer_on_mouse_event(FtkWidget* win, int press, int x, int y)
  464. {
  465. Info* info = (Info*)ftk_widget_user_data(win);
  466. if(press == 1)
  467. {
  468. info->last_x = x;
  469. info->last_y = y;
  470. info->mouse_down = 1;
  471. info->selected_widget = ftk_widget_find_target(win, info->last_x, info->last_y, 0);
  472. }
  473. else if(press == 0)
  474. {
  475. info->mouse_down = 0;
  476. }
  477. else
  478. {
  479. int w = 0;
  480. int h = 0;
  481. int x_offset = x - info->last_x;
  482. int y_offset = y - info->last_y;
  483. if(!designer_has_selected_widget(win) || !info->mouse_down)
  484. {
  485. return RET_OK;
  486. }
  487. info->last_x = x;
  488. info->last_y = y;
  489. x = ftk_widget_left(info->selected_widget) + x_offset;
  490. y = ftk_widget_top(info->selected_widget) + y_offset;
  491. w = ftk_widget_width(info->selected_widget);
  492. h = ftk_widget_height(info->selected_widget);
  493. designer_move_widget(info->selected_widget, x, y, w, h);
  494. }
  495. return RET_OK;
  496. }
  497. static Ret designer_on_event(void* ctx, void* data)
  498. {
  499. Ret ret = RET_OK;
  500. FtkWidget* win = (FtkWidget*)ctx;
  501. FtkEvent* event = (FtkEvent*)data;
  502. if(event->type == FTK_EVT_MOUSE_LONG_PRESS)
  503. {
  504. ret = RET_REMOVE;
  505. designer_on_popup_menu(win, NULL);
  506. }
  507. else if(event->type == FTK_EVT_KEY_DOWN)
  508. {
  509. ret = designer_on_key_event(win, 1, event->u.key.code);
  510. }
  511. else if(event->type == FTK_EVT_KEY_UP)
  512. {
  513. ret = designer_on_key_event(win, 0, event->u.key.code);
  514. }
  515. else if(event->type == FTK_EVT_MOUSE_DOWN)
  516. {
  517. ret = designer_on_mouse_event(win, 1, event->u.mouse.x, event->u.mouse.y);
  518. }
  519. else if(event->type == FTK_EVT_MOUSE_MOVE)
  520. {
  521. ret = designer_on_mouse_event(win, -1, event->u.mouse.x, event->u.mouse.y);
  522. }
  523. else if(event->type == FTK_EVT_MOUSE_UP)
  524. {
  525. ret = designer_on_mouse_event(win, 0, event->u.mouse.x, event->u.mouse.y);
  526. }
  527. return ret;
  528. }
  529. static Ret ftk_app_designer_run(FtkApp* thiz, int argc, char* argv[])
  530. {
  531. designer_on_new(NULL, NULL);
  532. return RET_OK;
  533. }
  534. static void ftk_app_designer_destroy(FtkApp* thiz)
  535. {
  536. if(thiz != NULL)
  537. {
  538. DECL_PRIV(thiz, priv);
  539. ftk_bitmap_unref(priv->icon);
  540. FTK_FREE(thiz);
  541. }
  542. return;
  543. }
  544. FtkApp* ftk_app_designer_create(void)
  545. {
  546. FtkApp* thiz = FTK_ZALLOC(sizeof(FtkApp) + sizeof(PrivInfo));
  547. if(thiz != NULL)
  548. {
  549. thiz->run = ftk_app_designer_run;
  550. thiz->get_icon = ftk_app_designer_get_icon;
  551. thiz->get_name = ftk_app_designer_get_name;
  552. thiz->destroy = ftk_app_designer_destroy;
  553. }
  554. return thiz;
  555. }