Puzzle / src /

view.c

  1. /*
  2. * Copyright (c) 2016 Samsung Electronics Co., Ltd
  3. *
  4. * Licensed under the Flora License, Version 1.1 (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://floralicense.org/license/
  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. #include <Elementary.h>
  18. #include <efl_extension.h>
  19. #include "view.h"
  20. #include "view_defines.h"
  21. #include "$(appName).h"
  22.  
  23. #define IMG_SMILE "smile.jpg"
  24. #define IMG_CORRECT "correct.jpg"
  25.  
  26. #define PUZZLE_RATIO 0.6
  27. #define MENU_RATIO 0.2
  28. #define PUZZLE_SIZE_LEVEL_4 4
  29. #define PUZZLE_SIZE_LEVEL_5 5
  30. #define IMAGE_PREVIEW_SIZE 0.5
  31. #define MAX_SHUFFLE_COUNT 70
  32. #define PIECE_COUNT PUZZLE_SIZE_LEVEL_5 * PUZZLE_SIZE_LEVEL_5 + 1
  33.  
  34. typedef enum {NB_UP, NB_DOWN, NB_LEFT, NB_RIGHT} navi_button_e;
  35.  
  36. static struct view_info {
  37. Evas_Object *win;
  38. Evas_Object *conform;
  39. Evas_Object *radio_group;
  40. int radio_selector;
  41. Evas_Object *piece[PIECE_COUNT];
  42. int piece_pos[PIECE_COUNT];
  43. Evas* e;
  44. int full_image_width;
  45. int full_image_height;
  46. int origin_image_width;
  47. int origin_image_height;
  48. int puzzle_start_x;
  49. int puzzle_start_y;
  50. int white_piece;
  51. int size;
  52. int screen_width;
  53. int screen_height;
  54. int r;
  55. int g;
  56. int b;
  57. int a;
  58. int start;
  59. bool shuffling;
  60. int shuffle_count;
  61. int level;
  62. } s_info = {
  63. .win = NULL,
  64. .conform = NULL,
  65. .radio_group = NULL,
  66. .radio_selector = 0,
  67. .piece = {NULL,},
  68. .piece_pos = {0,},
  69. .e = NULL,
  70. .full_image_width = 0,
  71. .full_image_height = 0,
  72. .origin_image_width = 0,
  73. .origin_image_height = 0,
  74. .puzzle_start_x = 0,
  75. .puzzle_start_y = 0,
  76. .white_piece = 0,
  77. .size = 0,
  78. .screen_width = 0,
  79. .screen_height = 0,
  80. .r = 0,
  81. .g = 0,
  82. .b = 0,
  83. .a = 0,
  84. .start = 0,
  85. .shuffling = 0,
  86. .shuffle_count = 0,
  87. .level = 0,
  88. };
  89.  
  90. static void _delete_win_request_cb(void *data, Evas_Object *obj, void *event_info);
  91. static void _layout_back_cb(void *data, Evas_Object *obj, void *event_info);
  92. static void _popup_close_cb(void *data, Evas_Object *obj, void *event_info);
  93. static void _popup_level_accept_cb(void *data, Evas_Object *obj, void *event_info);
  94. static void _answer_icon_click_cb(void *data, Evas *e, Evas_Object *obj, void *event_info);
  95. static void _level_icon_click_cb(void* data, Evas *e, Evas_Object *obj, void *event_info);
  96. static void _shuffle_icon_click_cb(void *data, Evas *e, Evas_Object *obj, void *event_info);
  97. static void _navigation_button_cb(void *data, Evas_Object *obj, void *event_info);
  98. static Eina_Bool _shuffle_cb(void *data);
  99. static void _get_app_resource(const char *edj_file_in, char *edj_path_out);
  100. static bool _create_main_layout(void);
  101. static bool _create_menu(Evas_Object *parent);
  102. static bool _create_navigation(Evas_Object *parent);
  103. static void _create_puzzle(int level);
  104. static void _move_puzzle(int offset);
  105. static bool _check_puzzle_move(navi_button_e nb_type);
  106. static void _change_size(int level);
  107. static void _finished(void);
  108.  
  109.  
  110. /**
  111. * @brief Creates essential objects: window, conformant and layout.
  112. */
  113. Eina_Bool view_create(void *user_data)
  114. {
  115. /* Create the window */
  116. s_info.win = view_create_win(PACKAGE);
  117. if (s_info.win == NULL) {
  118. dlog_print(DLOG_ERROR, LOG_TAG, "failed to create a window.");
  119. return EINA_FALSE;
  120. }
  121.  
  122. elm_win_screen_size_get(s_info.win, NULL, NULL, &s_info.screen_width, &s_info.screen_height);
  123.  
  124. /* Create the conformant */
  125. s_info.conform = view_create_conformant_without_indicator(s_info.win);
  126. if (s_info.conform == NULL) {
  127. dlog_print(DLOG_ERROR, LOG_TAG, "failed to create a conformant");
  128. return EINA_FALSE;
  129. }
  130.  
  131. if (!_create_main_layout())
  132. return EINA_FALSE;
  133.  
  134. /* Show the window after main view is set up */
  135. evas_object_show(s_info.win);
  136.  
  137. return EINA_TRUE;
  138. }
  139.  
  140. /**
  141. * @brief Creates a basic window named package.
  142. * @param[in] pkg_name Name of the window
  143. */
  144. Evas_Object *view_create_win(const char *pkg_name)
  145. {
  146. Evas_Object *win = NULL;
  147.  
  148. /*
  149. * Window
  150. * Create and initialize elm_win.
  151. * elm_win is mandatory to manipulate the window.
  152. */
  153. win = elm_win_util_standard_add(pkg_name, pkg_name);
  154. elm_win_conformant_set(win, EINA_TRUE);
  155. elm_win_autodel_set(win, EINA_TRUE);
  156. elm_win_indicator_mode_set(win, ELM_WIN_INDICATOR_HIDE);
  157. elm_win_indicator_opacity_set(win, ELM_WIN_INDICATOR_OPAQUE);
  158.  
  159. evas_object_smart_callback_add(win, "delete,request", _delete_win_request_cb, NULL);
  160.  
  161. return win;
  162. }
  163.  
  164. /**
  165. * @brief Creates a layout object for parent object based on provided EDJE script.
  166. * @param[in] parent The parent object for layout object.
  167. * @param[in] edj_file_name The relative path to the layout EDJE script.
  168. * @param[in] edj_group The name of the group to be loaded from the EDJE script.
  169. * @param[in] part_name The EDJE part's name where the layout is to be set.
  170. * @return The function returns layout object if it was created successfully,
  171. * otherwise 'NULL' is returned.
  172. */
  173. Evas_Object *view_create_layout(Evas_Object *parent, const char *edj_file_name, const char *group_name, const char *part_name)
  174. {
  175. char edj_path[PATH_MAX] = {0, };
  176. Evas_Object *layout = NULL;
  177.  
  178. _get_app_resource(edj_file_name, edj_path);
  179.  
  180. layout = elm_layout_add(parent);
  181. if (!layout) {
  182. dlog_print(DLOG_ERROR, LOG_TAG, "elm_layout_add() failed.");
  183. return NULL;
  184. }
  185.  
  186. if (!elm_layout_file_set(layout, edj_path, group_name)) {
  187. dlog_print(DLOG_ERROR, LOG_TAG, "elm_layout_file_set() failed.");
  188. return NULL;
  189. }
  190.  
  191. evas_object_size_hint_weight_set(layout, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
  192. eext_object_event_callback_add(layout, EEXT_CALLBACK_BACK, _layout_back_cb, NULL);
  193.  
  194. if (!part_name)
  195. elm_object_content_set(parent, layout);
  196. else
  197. elm_object_part_content_set(parent, part_name, layout);
  198.  
  199. evas_object_show(layout);
  200.  
  201. return layout;
  202. }
  203.  
  204. /**
  205. * @brief Creates a conformant without indicator for wearable app.
  206. * @param[in] win The object to which you want to set this conformant
  207. * Conformant is mandatory for base GUI to have proper size
  208. */
  209. Evas_Object *view_create_conformant_without_indicator(Evas_Object *win)
  210. {
  211. /*
  212. * Conformant
  213. * Create and initialize elm_conformant.
  214. * elm_conformant is mandatory for base GUI to have proper size
  215. * when indicator or virtual keypad is visible.
  216. */
  217. Evas_Object *conform = NULL;
  218.  
  219. if (win == NULL) {
  220. dlog_print(DLOG_ERROR, LOG_TAG, "window is NULL.");
  221. return NULL;
  222. }
  223.  
  224. conform = elm_conformant_add(win);
  225. evas_object_size_hint_weight_set(conform, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
  226. elm_win_resize_object_add(win, conform);
  227.  
  228. evas_object_show(conform);
  229.  
  230. return conform;
  231. }
  232.  
  233. /**
  234. * @brief Creates an icon object and sets it to the parent object.
  235. * The icon displays an standard image specified by icon_type.
  236. * @param[in] parent The parent object for icon object.
  237. * @param[in] icon_type The icon type name.
  238. * @param[in] part_name The EDJE part's name where the icon is to be set.
  239. * @return The function returns icon object if it was created successfully,
  240. * otherwise 'NULL' is returned.
  241. */
  242. Evas_Object *view_create_icon(Evas_Object *parent, const char *icon_type, const char *part_name)
  243. {
  244. Evas_Object *icon = elm_icon_add(parent);
  245. if (!icon) {
  246. dlog_print(DLOG_ERROR, LOG_TAG, "elm_icon_add() failed.");
  247. return NULL;
  248. };
  249.  
  250. if (!elm_icon_standard_set(icon, icon_type)) {
  251. dlog_print(DLOG_ERROR, LOG_TAG, "elm_icon_standard_set() failed. No '%s' icon.", icon_type);
  252. return NULL;
  253. }
  254.  
  255. elm_object_part_content_set(parent, part_name, icon);
  256. evas_object_show(icon);
  257.  
  258. return icon;
  259. }
  260.  
  261. /**
  262. * @brief Creates an image object and sets it to the parent object.
  263. * @param[in] parent The parent object for image object.
  264. * @param[in] file_name The name of the image file to be displayed.
  265. * @param[in] part_name The EDJE part's name where the image is to be set.
  266. * @param[in] width The width of the image in pixels.
  267. * @param[in] height The height of the image in pixels.
  268. * @return The function returns image object if it was created successfully,
  269. * otherwise 'NULL' is returned.
  270. */
  271. Evas_Object *view_create_image(Evas_Object *parent, const char *file_name, const char *part_name, int width, int height)
  272. {
  273. char img_path[PATH_MAX] = {0, };
  274. Evas_Object *image = elm_image_add(parent);
  275. if (!image) {
  276. dlog_print(DLOG_ERROR, LOG_TAG, "elm_image_add() failed.");
  277. return NULL;
  278. };
  279.  
  280. _get_app_resource(file_name, img_path);
  281.  
  282. if (!elm_image_file_set(image, img_path, NULL)) {
  283. dlog_print(DLOG_ERROR, LOG_TAG, "elm_image_file_set() failed.");
  284. return NULL;
  285. }
  286.  
  287. elm_image_aspect_fixed_set(image, EINA_TRUE);
  288.  
  289. evas_object_size_hint_min_set(image, width, height);
  290. evas_object_size_hint_max_set(image, width, height);
  291.  
  292. elm_object_part_content_set(parent, part_name, image);
  293. evas_object_show(image);
  294.  
  295. return image;
  296. }
  297.  
  298. /**
  299. * @brief Creates a button object for the parent object.
  300. * @param[in] parent The parent object for button object.
  301. * @param[in] caption The caption to be displayed on the button.
  302. * @param[in] part_name The part name where the button is to be set.
  303. * @param[in] on_click_cb The callback function's handler to be invoked on button click.
  304. * @param[in] data The data to be passed to the callback function.
  305. * @return The function returns button object if it was created successfully,
  306. * otherwise 'NULL' is returned.
  307. */
  308. Evas_Object *view_create_button(Evas_Object *parent, const char *caption, const char *part_name, Evas_Smart_Cb on_click_cb, void *data)
  309. {
  310. Evas_Object *button = elm_button_add(parent);
  311. if (!button) {
  312. dlog_print(DLOG_ERROR, LOG_TAG, "elm_button_add() failed.");
  313. return NULL;
  314. }
  315.  
  316. evas_object_size_hint_weight_set(button, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
  317. evas_object_size_hint_align_set(button, EVAS_HINT_FILL, EVAS_HINT_FILL);
  318. elm_object_text_set(button, caption);
  319. elm_object_part_content_set(parent, part_name, button);
  320. evas_object_smart_callback_add(button, "clicked", on_click_cb, data);
  321. evas_object_show(button);
  322.  
  323. return button;
  324. }
  325.  
  326. /**
  327. * @brief Creates a popup window with 'OK' button set.
  328. * @param[in] title The popup window title.
  329. * @param[in] button1 The first button caption.
  330. * @param[in] on_click1_cb The first button's callback function invoked on click event.
  331. * @param[in] button2 The second button caption.
  332. * @param[in] on_click2_cb The second button's callback function invoked on click event.
  333. * @return This function returns popup window object if it was successfully created,
  334. * otherwise 'false' is returned.
  335. */
  336. Evas_Object *view_create_popup(const char *title, const char *button1, Evas_Smart_Cb on_click1_cb, const char *button2, Evas_Smart_Cb on_click2_cb)
  337. {
  338. Evas_Object *button;
  339. Evas_Object *popup = elm_popup_add(s_info.win);
  340. if (!popup) {
  341. dlog_print(DLOG_ERROR, LOG_TAG, "elm_popup_add() failed.");
  342. return NULL;
  343. }
  344.  
  345. elm_popup_align_set(popup, ELM_NOTIFY_ALIGN_FILL, 1.0);
  346. elm_object_part_text_set(popup, "title,text", title);
  347.  
  348. if (button1) {
  349. button = view_create_button(popup, button1, "button1", on_click1_cb, (void *)popup);
  350. if (!button) {
  351. evas_object_del(popup);
  352. return NULL;
  353. }
  354. }
  355.  
  356. if (button2) {
  357. button = view_create_button(popup, button2, "button2", on_click2_cb, (void *)popup);
  358. if (!button) {
  359. evas_object_del(popup);
  360. return NULL;
  361. }
  362. }
  363.  
  364. evas_object_show(popup);
  365.  
  366. return popup;
  367. }
  368.  
  369. /**
  370. * @brief Creates a radio button within a group of radio buttons and sets it to the parent object.
  371. * @param[in] parent The parent object for radio button.
  372. * @param[in] part_name The part name where the radio is to be set.
  373. * @param[in] v_pointer The pointer to the selected item's id.
  374. * @return This function returns radio button object if it was successfully created,
  375. * otherwise 'false' is returned.
  376. */
  377. Evas_Object *view_create_radio(Evas_Object *parent, const char *part_name, int *v_pointer)
  378. {
  379. static int radio_id = 0;
  380. Evas_Object *radio = elm_radio_add(parent);
  381. if (!radio) {
  382. dlog_print(DLOG_ERROR, LOG_TAG, "elm_radio_add() failed.");
  383. return NULL;
  384. }
  385.  
  386. if (!s_info.radio_group) {
  387. radio_id = 0;
  388. s_info.radio_group = radio;
  389. elm_radio_value_pointer_set(radio, v_pointer);
  390. } else {
  391. elm_radio_group_add(radio, s_info.radio_group);
  392. }
  393.  
  394. elm_radio_state_value_set(radio, radio_id++);
  395. evas_object_size_hint_align_set(radio, EVAS_HINT_FILL, EVAS_HINT_FILL);
  396. evas_object_size_hint_weight_set(radio, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
  397. elm_object_part_content_set(parent, part_name, radio);
  398. evas_object_show(radio);
  399.  
  400. return radio;
  401. }
  402.  
  403. /**
  404. * @brief Destroys window and frees its resources.
  405. */
  406. void view_destroy(void)
  407. {
  408. if (s_info.win == NULL)
  409. return;
  410.  
  411. evas_object_del(s_info.win);
  412. }
  413.  
  414. /**
  415. * @brief Internal callback function invoked when the main window needs to be destroyed.
  416. * @param[in] data The user data passed to the evas_object_smart_callback_add() function.
  417. * @param[in] obj The object invoking this callback function.
  418. * @param[in] event_info The structure containing the information on this event.
  419. */
  420. static void _delete_win_request_cb(void *data, Evas_Object *obj, void *event_info)
  421. {
  422. ui_app_exit();
  423. }
  424.  
  425. /**
  426. * @brief Internal callback function invoked when the HW Back button is pressed.
  427. * @param[in] data The user data passed to the eext_object_event_callback_add() function.
  428. * @param[in] obj The object invoking this callback function.
  429. * @param[in] event_info The structure containing the information on this event.
  430. */
  431. static void _layout_back_cb(void *data, Evas_Object *obj, void *event_info)
  432. {
  433. /* Let window go to hide state. */
  434. elm_win_lower(s_info.win);
  435. }
  436.  
  437. /**
  438. * @brief Internal callback function invoked when the 'Close' button is pressed
  439. * while popup window is visible.
  440. * @param[in] data The user data passed to the callback attachment function.
  441. * @param[in] obj The object invoking this callback function.
  442. * @param[in] event_info The structure containing the information on this event.
  443. */
  444. static void _popup_close_cb(void *data, Evas_Object *obj, void *event_info)
  445. {
  446. s_info.radio_group = NULL;
  447. evas_object_del((Evas_Object *)data);
  448. }
  449.  
  450. /**
  451. * @brief Internal callback function invoked when the 'OK' button is pressed
  452. * while popup window is visible.
  453. * @param[in] data The user data passed to the callback attachment function.
  454. * @param[in] obj The object invoking this callback function.
  455. * @param[in] event_info The structure containing the information on this event.
  456. */
  457. static void _popup_level_accept_cb(void *data, Evas_Object *obj, void *event_info)
  458. {
  459. if (s_info.level != s_info.radio_selector) {
  460. if (s_info.radio_selector == 0)
  461. _change_size(PUZZLE_SIZE_LEVEL_4);
  462. else if (s_info.radio_selector == 1)
  463. _change_size(PUZZLE_SIZE_LEVEL_5);
  464.  
  465. s_info.level = s_info.radio_selector;
  466. }
  467.  
  468. _popup_close_cb(data, obj, event_info);
  469. }
  470.  
  471. /**
  472. * @brief Internal callback function invoked when the 'answer' icon is pressed.
  473. * @param[in] data The user data passed to the callback attachment function.
  474. * @param[in] e The evas surface of the object.
  475. * @param[in] obj The object invoking this callback function.
  476. * @param[in] event_info The structure containing the information on this event.
  477. */
  478. static void _answer_icon_click_cb(void *data, Evas *e, Evas_Object *obj, void *event_info)
  479. {
  480. Evas_Object *popup;
  481. int width = 0;
  482.  
  483. popup = view_create_popup("Answer", "Close", _popup_close_cb, NULL, NULL);
  484. if (!popup)
  485. return;
  486.  
  487. elm_win_screen_size_get(s_info.win, NULL, NULL, &width, NULL);
  488.  
  489. if (!view_create_image(popup, IMG_SMILE, "default", width * IMAGE_PREVIEW_SIZE, width * IMAGE_PREVIEW_SIZE)) {
  490. evas_object_del(popup);
  491. return;
  492. }
  493.  
  494. evas_object_show(popup);
  495. }
  496.  
  497. /**
  498. * @brief Internal callback function invoked when the 'level' icon is pressed.
  499. * @param[in] data The user data passed to the callback attachment function.
  500. * @param[in] e The evas surface of the object.
  501. * @param[in] obj The object invoking this callback function.
  502. * @param[in] event_info The structure containing the information on this event.
  503. */
  504. static void _level_icon_click_cb(void* data, Evas *e, Evas_Object *obj, void *event_info)
  505. {
  506. Evas_Object *popup;
  507. Evas_Object *layout;
  508.  
  509. popup = view_create_popup("Level", "OK", _popup_level_accept_cb, "Close", _popup_close_cb);
  510. if (!popup)
  511. return;
  512.  
  513. layout = view_create_layout(popup, EDJ_MAIN, GRP_POPUP, "default");
  514. if (!layout) {
  515. evas_object_del(popup);
  516. return;
  517. }
  518.  
  519. view_create_radio(layout, PART_POPUP_ITEM_1, &s_info.radio_selector);
  520. view_create_radio(layout, PART_POPUP_ITEM_2, &s_info.radio_selector);
  521.  
  522. evas_object_show(popup);
  523. }
  524.  
  525. /**
  526. * @brief Internal callback function invoked when the 'shuffle' icon is pressed.
  527. * @param[in] data The user data passed to the callback attachment function.
  528. * @param[in] e The evas surface of the object.
  529. * @param[in] obj The object invoking this callback function.
  530. * @param[in] event_info The structure containing the information on this event.
  531. */
  532. static void _shuffle_icon_click_cb(void *data, Evas *e, Evas_Object *obj, void *event_info)
  533. {
  534. Ecore_Animator *animator;
  535.  
  536. if (s_info.shuffling)
  537. return;
  538.  
  539. s_info.shuffle_count = 0;
  540. s_info.shuffling = true;
  541.  
  542. animator = ecore_animator_add(_shuffle_cb, NULL);
  543. }
  544.  
  545. /**
  546. * @brief Internal callback function invoked when one of the navigation buttons is pressed.
  547. * @param[in] data The user data passed to the callback attachment function.
  548. * @param[in] obj The object invoking this callback function.
  549. * @param[in] event_info The structure containing the information on this event.
  550. */
  551. static void _navigation_button_cb(void *data, Evas_Object *obj, void *event_info)
  552. {
  553. navi_button_e nb_type = (navi_button_e)data;
  554.  
  555. if (!_check_puzzle_move(nb_type))
  556. return;
  557.  
  558. switch (nb_type) {
  559. case NB_UP:
  560. _move_puzzle(s_info.size);
  561. break;
  562. case NB_DOWN:
  563. _move_puzzle(-s_info.size);
  564. break;
  565. case NB_LEFT:
  566. _move_puzzle(1);
  567. break;
  568. case NB_RIGHT:
  569. _move_puzzle(-1);
  570. break;
  571. default:
  572. dlog_print(DLOG_WARN, LOG_TAG, "Unknown navigation button type.");
  573. }
  574. }
  575.  
  576. /**
  577. * @brief Internal callback function invoked when the mouse button is pressed
  578. * over the puzzle piece.
  579. * This function performs selected piece highlighting.
  580. * @param[in] data The user data passed to the callback attachment function.
  581. * @param[in] e The evas surface of the object.
  582. * @param[in] obj The object invoking this callback function.
  583. * @param[in] event_info The structure containing the information on this event.
  584. */
  585. void _mouse_down_cb(void *data, Evas *e, Evas_Object *obj, void *event_info)
  586. {
  587. Evas_Object *piece = obj;
  588. int pos = (int)evas_object_data_get(piece, "position");
  589. int r, g, b, a;
  590.  
  591. if (pos == (int)evas_object_data_get(s_info.piece[s_info.white_piece], "position"))
  592. return;
  593.  
  594. evas_object_color_get(piece, &r, &g, &b, &a);
  595. evas_object_color_set(piece, r, g, b, 200);
  596.  
  597. evas_object_show(piece);
  598. }
  599.  
  600. /**
  601. * @brief Internal callback function invoked when the mouse button is released
  602. * from the puzzle piece.
  603. * This function performs selected piece movement to the white space.
  604. * @param[in] data The user data passed to the callback attachment function.
  605. * @param[in] e The evas surface of the object.
  606. * @param[in] obj The object invoking this callback function.
  607. * @param[in] event_info The structure containing the information on this event.
  608. */
  609. void _mouse_up_cb(void *data, Evas *e, Evas_Object *obj, void *event_info)
  610. {
  611. Evas_Object *piece = obj;
  612. int pos = (int)evas_object_data_get(piece, "position");
  613.  
  614. if (pos == (int)evas_object_data_get(s_info.piece[s_info.white_piece], "position"))
  615. return;
  616.  
  617. evas_object_color_set(piece, 255, 255, 255, 255);
  618.  
  619. if (pos - s_info.size == s_info.white_piece)
  620. _navigation_button_cb((void *)NB_UP, NULL, NULL);
  621. else if (pos - 1 == s_info.white_piece && ((s_info.white_piece + 1) % s_info.size))
  622. _navigation_button_cb((void *)NB_LEFT, NULL, NULL);
  623. else if (pos + 1 == s_info.white_piece && (s_info.white_piece % s_info.size))
  624. _navigation_button_cb((void *)NB_RIGHT, NULL, NULL);
  625. else if (pos + s_info.size == s_info.white_piece)
  626. _navigation_button_cb((void *)NB_DOWN, NULL, NULL);
  627.  
  628. evas_object_show(piece);
  629. }
  630.  
  631. /**
  632. * @brief Internal callback function invoked on pieces shuffling animation.
  633. * @param[in] data The user data passed to the callback attachment function.
  634. */
  635. static Eina_Bool _shuffle_cb(void *data)
  636. {
  637. navi_button_e nb_type = (navi_button_e)(rand() % 4);
  638. s_info.shuffle_count++;
  639.  
  640. if (!_check_puzzle_move(nb_type))
  641. s_info.shuffle_count--;
  642. else
  643. _navigation_button_cb((void *)nb_type, NULL, NULL);
  644.  
  645. if (s_info.shuffle_count < MAX_SHUFFLE_COUNT) {
  646. return ECORE_CALLBACK_RENEW;
  647. } else {
  648. s_info.shuffling = false;
  649. return ECORE_CALLBACK_CANCEL;
  650. }
  651. }
  652.  
  653. /**
  654. * @brief Internal function which creates fully qualified path to the provided resource file.
  655. * @param[in] edj_file_in The file name.
  656. * @param[out] edj_path_out The fully qualified path to the edj_file_in file.
  657. */
  658. static void _get_app_resource(const char *edj_file_in, char *edj_path_out)
  659. {
  660. char *res_path = app_get_resource_path();
  661. if (res_path) {
  662. snprintf(edj_path_out, PATH_MAX, "%s%s", res_path, edj_file_in);
  663. free(res_path);
  664. }
  665. }
  666.  
  667. /**
  668. * @brief Internal function which creates the main layout.
  669. * @return This function returns 'true' if the main layout was created successfully,
  670. * otherwise 'false' is returned.
  671. */
  672. static bool _create_main_layout(void)
  673. {
  674. Evas_Object *layout = NULL;
  675.  
  676. layout = view_create_layout(s_info.conform, EDJ_MAIN, GRP_MAIN, "elm.swallow.content");
  677. if (!layout)
  678. return false;
  679.  
  680. s_info.e = evas_object_evas_get(layout);
  681. if (!s_info.e)
  682. return false;
  683.  
  684. if (!_create_menu(layout))
  685. return false;
  686.  
  687. if (!_create_navigation(layout))
  688. return false;
  689.  
  690. _create_puzzle(PUZZLE_SIZE_LEVEL_4);
  691.  
  692. return true;
  693. }
  694.  
  695. /**
  696. * @brief Internal function which creates the top menu layout.
  697. * The menu consists of three icons (buttons): answer preview, level selection, puzzle shuffle.
  698. * @return This function returns 'true' if the menu layout was created successfully,
  699. * otherwise 'false' is returned.
  700. */
  701. static bool _create_menu(Evas_Object *parent)
  702. {
  703. Evas_Object *layout;
  704. Evas_Object *answer;
  705. Evas_Object *level;
  706. Evas_Object *shuffle;
  707.  
  708. layout = view_create_layout(parent, EDJ_MAIN, GRP_MENU, PART_MAIN_MENU);
  709. if (!layout)
  710. return false;
  711.  
  712. answer = view_create_icon(layout, "no_photo", PART_MENU_ANSWER);
  713. if (!answer)
  714. return false;
  715.  
  716. level = view_create_icon(layout, "file", PART_MENU_LEVEL);
  717. if (!level)
  718. return false;
  719.  
  720. shuffle = view_create_icon(layout, "refresh", PART_MENU_SHUFFLE);
  721. if (!shuffle)
  722. return false;
  723.  
  724. evas_object_event_callback_add(answer, EVAS_CALLBACK_MOUSE_UP, _answer_icon_click_cb, NULL);
  725. evas_object_event_callback_add(level, EVAS_CALLBACK_MOUSE_UP, _level_icon_click_cb, NULL);
  726. evas_object_event_callback_add(shuffle, EVAS_CALLBACK_MOUSE_UP, _shuffle_icon_click_cb, NULL);
  727.  
  728. return true;
  729. }
  730.  
  731. /**
  732. * @brief Internal function which creates the navigation layout.
  733. * The navigation consists of four buttons for puzzle pieces moving in four directions.
  734. * @return This function returns 'true' if the navigation layout was created successfully,
  735. * otherwise 'false' is returned.
  736. */
  737. static bool _create_navigation(Evas_Object *parent)
  738. {
  739. Evas_Object *layout = view_create_layout(parent, EDJ_MAIN, GRP_NAVI, PART_MAIN_NAVI);
  740. if (!layout)
  741. return false;
  742.  
  743. return (view_create_button(layout, "UP", PART_NAVI_UP, _navigation_button_cb, (void *)NB_UP) &&
  744. view_create_button(layout, "DOWN", PART_NAVI_DOWN, _navigation_button_cb, (void *)NB_DOWN) &&
  745. view_create_button(layout, "LEFT", PART_NAVI_LEFT, _navigation_button_cb, (void *)NB_LEFT) &&
  746. view_create_button(layout, "RIGHT", PART_NAVI_RIGHT, _navigation_button_cb, (void *)NB_RIGHT));
  747. }
  748.  
  749. /**
  750. * @brief Internal function which creates the puzzle image area.
  751. */
  752. static void _create_puzzle(int level)
  753. {
  754. char file_path[PATH_MAX] = {0,};
  755. int x, y;
  756. int extract_region_width, extract_region_height;
  757. int puzzle_width, puzzle_height;
  758. int i;
  759. int ret;
  760.  
  761. _get_app_resource(IMG_SMILE, file_path);
  762.  
  763. s_info.full_image_width = s_info.screen_width - (6 + 6);
  764. s_info.full_image_height = (s_info.screen_height * PUZZLE_RATIO) - (6 + 6);
  765. s_info.puzzle_start_x = 6;
  766. s_info.puzzle_start_y = (s_info.screen_height * MENU_RATIO) + 6;
  767.  
  768. for (i = 0; i < PIECE_COUNT; i++) {
  769. s_info.piece[i] = evas_object_image_filled_add(s_info.e);
  770. s_info.piece_pos[i] = i;
  771. evas_object_image_file_set(s_info.piece[i], file_path, NULL);
  772. ret = evas_object_image_load_error_get(s_info.piece[i]);
  773. if (ret != EVAS_LOAD_ERROR_NONE)
  774. dlog_print(DLOG_ERROR, LOG_TAG, "Failed to load image");
  775.  
  776. evas_object_data_set(s_info.piece[i], "position", (void *)i);
  777. }
  778.  
  779. evas_object_image_size_get(s_info.piece[PIECE_COUNT - 1], &s_info.origin_image_width, &s_info.origin_image_height);
  780.  
  781. extract_region_width = s_info.origin_image_width / level;
  782. extract_region_height = s_info.origin_image_height / level;
  783. puzzle_width = s_info.full_image_width / level;
  784. puzzle_height = s_info.full_image_height / level;
  785.  
  786. for (y = 0; y < level; y++)
  787. for (x = 0; x < level; x++) {
  788. evas_object_move(s_info.piece[y * level + x], s_info.puzzle_start_x + x * (2 + puzzle_width), s_info.puzzle_start_y + y * (2 + puzzle_height));
  789. evas_object_image_load_region_set(s_info.piece[y * level + x], x * extract_region_width, y * extract_region_height, extract_region_width, extract_region_height);
  790. evas_object_resize(s_info.piece[y * level + x], puzzle_width, puzzle_height);
  791.  
  792. evas_object_event_callback_add(s_info.piece[y * level + x], EVAS_CALLBACK_MOUSE_DOWN, _mouse_down_cb, NULL);
  793. evas_object_event_callback_add(s_info.piece[y * level + x], EVAS_CALLBACK_MOUSE_UP, _mouse_up_cb, NULL);
  794.  
  795. if (y == level - 1 && x == level - 1) {
  796. evas_object_color_get(s_info.piece[y * level + x], &s_info.r, &s_info.g, &s_info.b, &s_info.a);
  797. evas_object_color_set(s_info.piece[y * level + x], s_info.r, s_info.g, s_info.b, 0);
  798.  
  799. s_info.white_piece = y * level + x;
  800. }
  801.  
  802. evas_object_show(s_info.piece[y * level + x]);
  803. }
  804.  
  805. s_info.size = level;
  806. s_info.start = 0;
  807. }
  808.  
  809. /**
  810. * @brief Internal function which moves the puzzle piece.
  811. * @param[in] offset The number of cells to move the current piece. In order to
  812. * specify a valid value, the puzzle image must be considered as a continuous
  813. * list (array) of cells. To move the current cell:
  814. * - down/up - the offset equals to +/-row_length;
  815. * - right/left - the offset equals to +/-1.
  816. */
  817. static void _move_puzzle(int offset)
  818. {
  819. int x1, y1, w1, h1;
  820. int x2, y2, w2, h2;
  821. int temp;
  822. int white_piece = s_info.white_piece;
  823.  
  824. evas_object_image_load_region_get(s_info.piece[white_piece + offset], &x1, &y1, &w1, &h1);
  825. evas_object_image_load_region_get(s_info.piece[white_piece], &x2, &y2, &w2, &h2);
  826.  
  827. evas_object_image_load_region_set(s_info.piece[white_piece + offset], x2, y2, w2, h2);
  828. evas_object_image_load_region_set(s_info.piece[white_piece], x1, y1, w1, h1);
  829.  
  830. evas_object_color_set(s_info.piece[white_piece], s_info.r, s_info.g, s_info.b, s_info.a);
  831. evas_object_color_set(s_info.piece[white_piece + offset], s_info.r, s_info.g, s_info.b, 0);
  832.  
  833. evas_object_show(s_info.piece[white_piece]);
  834. evas_object_show(s_info.piece[white_piece + offset]);
  835.  
  836. temp = s_info.piece_pos[white_piece];
  837. s_info.piece_pos[white_piece] = s_info.piece_pos[white_piece + offset];
  838. s_info.piece_pos[white_piece + offset] = temp;
  839.  
  840. s_info.white_piece = white_piece + offset;
  841.  
  842. if (s_info.piece_pos[s_info.white_piece] == s_info.size * s_info.size - 1 && s_info.start == 1)
  843. _finished();
  844. }
  845.  
  846. /**
  847. * @brief Internal function which checks whether the current puzzle can be moved
  848. * with the provided button type.
  849. * @param[in] nb_type The type of the navigation button to check against.
  850. * @return This function returns 'true' if the current piece can be moved using
  851. * the provided button type, otherwise 'false' is returned.
  852. */
  853. static bool _check_puzzle_move(navi_button_e nb_type)
  854. {
  855. switch (nb_type) {
  856. case NB_UP:
  857. return (s_info.white_piece + s_info.size < s_info.size * s_info.size);
  858. case NB_DOWN:
  859. return (s_info.white_piece - s_info.size >= 0);
  860. case NB_LEFT:
  861. return ((s_info.white_piece + 1) % s_info.size > 0);
  862. case NB_RIGHT:
  863. return (s_info.white_piece % s_info.size > 0);
  864. default:
  865. return false;
  866. }
  867. }
  868.  
  869. /**
  870. * @brief Internal function which changes the puzzle size and recalculates all the
  871. * parameters and coordinates of each puzzle piece.
  872. * @param[in] level The number of pieces along one edge.
  873. */
  874. static void _change_size(int level)
  875. {
  876. char file_path[PATH_MAX] = {0,};
  877. int x, y;
  878. int extract_region_width, extract_region_height;
  879. int puzzle_width, puzzle_height;
  880. int i;
  881.  
  882. _get_app_resource(IMG_SMILE, file_path);
  883.  
  884. for (i = 0; i < PIECE_COUNT; i++) {
  885. evas_object_color_set(s_info.piece[i], s_info.r, s_info.g, s_info.b, 0);
  886. evas_object_show(s_info.piece[i]);
  887. evas_object_move(s_info.piece[i], 0, 0);
  888. }
  889.  
  890. extract_region_width = s_info.origin_image_width / level;
  891. extract_region_height = s_info.origin_image_height / level;
  892. puzzle_width = s_info.full_image_width / level;
  893. puzzle_height = s_info.full_image_height / level;
  894.  
  895. for (y = 0; y < level; y++)
  896. for (x = 0; x < level; x++) {
  897. evas_object_color_set(s_info.piece[y * level + x], s_info.r, s_info.g, s_info.b, 255);
  898. evas_object_image_file_set(s_info.piece[y * level + x], file_path, NULL);
  899.  
  900. evas_object_move(s_info.piece[y * level + x], s_info.puzzle_start_x + x * (2 + puzzle_width), s_info.puzzle_start_y + y * (2 + puzzle_height));
  901. evas_object_image_load_region_set(s_info.piece[y * level + x], x * extract_region_width, y * extract_region_height, extract_region_width, extract_region_height);
  902. evas_object_resize(s_info.piece[y * level + x], puzzle_width, puzzle_height);
  903.  
  904. evas_object_event_callback_add(s_info.piece[y * level + x], EVAS_CALLBACK_MOUSE_DOWN, _mouse_down_cb, NULL);
  905. evas_object_event_callback_add(s_info.piece[y * level + x], EVAS_CALLBACK_MOUSE_UP, _mouse_up_cb, NULL);
  906.  
  907. if (y == (level - 1) && x == (level - 1)) {
  908. evas_object_color_get(s_info.piece[y * level + x], &s_info.r, &s_info.g, &s_info.b, &s_info.a);
  909. evas_object_color_set(s_info.piece[y * level + x], s_info.r, s_info.g, s_info.b, 0);
  910.  
  911. s_info.white_piece = y * level + x;
  912. }
  913.  
  914. evas_object_show(s_info.piece[y * level + x]);
  915. }
  916.  
  917. s_info.size = level;
  918. s_info.start = 0;
  919. }
  920.  
  921. /**
  922. * @brief Internal function which validates the pieces arrangement correctness
  923. * and displays the final popup window if the validation is passed.
  924. */
  925. static void _finished(void)
  926. {
  927. Evas_Object *popup = NULL;
  928. int width = 0;
  929. int i;
  930.  
  931. for (i = 0; i < s_info.size * s_info.size; i++)
  932. if (s_info.piece_pos[i] != i)
  933. return;
  934.  
  935. popup = view_create_popup("Correct !!!", "OK", _popup_close_cb, NULL, NULL);
  936. if (!popup)
  937. return;
  938.  
  939. elm_win_screen_size_get(s_info.win, NULL, NULL, &width, NULL);
  940.  
  941. if (!view_create_image(popup, IMG_CORRECT, "default", width * IMAGE_PREVIEW_SIZE, width * IMAGE_PREVIEW_SIZE)) {
  942. evas_object_del(popup);
  943. return;
  944. }
  945.  
  946. evas_object_show(popup);
  947. s_info.start = 0;
  948. }