Copy and paste / 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 <efl_extension.h>
  18. #include "$(appName).h"
  19. #include "view.h"
  20. #include "view_defines.h"
  21.  
  22. typedef enum { STATE_NORMAL = 0, STATE_PRESS, STATE_DRAG } state_t;
  23.  
  24. static const char *source_text = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna " \
  25. "aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis" \
  26. " aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat" \
  27. " cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.";
  28.  
  29. static struct view_info {
  30. Evas_Object *win;
  31. Evas_Object *conform;
  32. Evas_Object *main_layout;
  33. Evas_Object *source_label;
  34. Evas_Object *sink_entry;
  35. Evas_Object *hover;
  36. state_t state;
  37. int drag_start_x;
  38. int drag_start_y;
  39. } s_info = {
  40. .win = NULL,
  41. .conform = NULL,
  42. .main_layout = NULL,
  43. .source_label = NULL,
  44. .sink_entry = NULL,
  45. .hover = NULL,
  46. .state = STATE_NORMAL,
  47. .drag_start_x = 0,
  48. .drag_start_y = 0,
  49. };
  50.  
  51. static void _delete_win_request_cb(void *data, Evas_Object *obj, void *event_info);
  52. static void _layout_back_cb(void *data, Evas_Object *obj, void *event_info);
  53. static Eina_Bool _press_cb(void *data, int type, void *event);
  54. static Eina_Bool _move_cb(void *data, int type, void *event);
  55. static Eina_Bool _unpress_cb(void *data, int type, void *event);
  56. static void _get_app_resource(const char *file_in, char *path_out);
  57. static Eina_Bool _create_source(void);
  58. static Eina_Bool _create_sink(void);
  59. static Eina_Bool _create_hover_window(void);
  60. static void _set_callbacks(void);
  61. static bool _source_hit_test(int x, int y);
  62. static bool _sink_hit_test(int x, int y);
  63. static void _paste_to_hover(void);
  64. static void _paste_to_sink(void);
  65. static void _set_hover_window_size(void);
  66. static void _set_hover_window_position(int x, int y);
  67. static void _hide_hover_window(void);
  68. static void _show_hover_window(void);
  69.  
  70. /**
  71. * @brief Creates essential objects: window, conformant and layout.
  72. */
  73. Eina_Bool view_create(void)
  74. {
  75. /* Create the window */
  76. s_info.win = view_create_win(PACKAGE);
  77. if (s_info.win == NULL) {
  78. dlog_print(DLOG_ERROR, LOG_TAG, "failed to create a window.");
  79. return EINA_FALSE;
  80. }
  81.  
  82. /* Create the conformant */
  83. s_info.conform = view_create_conformant_without_indicator(s_info.win);
  84. if (s_info.conform == NULL) {
  85. dlog_print(DLOG_ERROR, LOG_TAG, "failed to create a conformant");
  86. return EINA_FALSE;
  87. }
  88.  
  89. s_info.main_layout = view_create_layout(s_info.conform, EDJ_MAIN, GRP_MAIN, NULL);
  90. if (!s_info.main_layout) {
  91. dlog_print(DLOG_ERROR, LOG_TAG, "view_create_layout() failed.");
  92. return EINA_FALSE;
  93. }
  94.  
  95. if (!_create_source()) {
  96. dlog_print(DLOG_ERROR, LOG_TAG, "_create_source() failed.");
  97. return false;
  98. }
  99.  
  100. if (!_create_sink()) {
  101. dlog_print(DLOG_ERROR, LOG_TAG, "_create_sink() failed.");
  102. return false;
  103. }
  104.  
  105. if (!_create_hover_window()) {
  106. dlog_print(DLOG_ERROR, LOG_TAG, "_create_hover_window() failed.");
  107. return false;
  108. }
  109.  
  110. _set_callbacks();
  111.  
  112. /* Show the window after main view is set up */
  113. evas_object_show(s_info.win);
  114.  
  115. return EINA_TRUE;
  116. }
  117.  
  118. /**
  119. * @brief Creates a basic window named package.
  120. * @param[in] pkg_name Name of the window
  121. */
  122. Evas_Object *view_create_win(const char *pkg_name)
  123. {
  124. Evas_Object *win = NULL;
  125.  
  126. /*
  127. * Window
  128. * Create and initialize elm_win.
  129. * elm_win is mandatory to manipulate the window.
  130. */
  131. win = elm_win_util_standard_add(pkg_name, pkg_name);
  132. elm_win_conformant_set(win, EINA_TRUE);
  133. elm_win_autodel_set(win, EINA_TRUE);
  134.  
  135. evas_object_pass_events_set(win, EINA_FALSE);
  136. evas_object_smart_callback_add(win, "delete,request", _delete_win_request_cb, NULL);
  137.  
  138. return win;
  139. }
  140.  
  141. /**
  142. * @brief Creates a conformant without indicator for wearable app.
  143. * @param[in] win The object to which you want to set this conformant
  144. * Conformant is mandatory for base GUI to have proper size
  145. */
  146. Evas_Object *view_create_conformant_without_indicator(Evas_Object *win)
  147. {
  148. /*
  149. * Conformant
  150. * Create and initialize elm_conformant.
  151. * elm_conformant is mandatory for base GUI to have proper size
  152. * when indicator or virtual keypad is visible.
  153. */
  154. Evas_Object *conform = NULL;
  155.  
  156. if (win == NULL) {
  157. dlog_print(DLOG_ERROR, LOG_TAG, "window is NULL.");
  158. return NULL;
  159. }
  160.  
  161. conform = elm_conformant_add(win);
  162. evas_object_size_hint_weight_set(conform, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
  163. elm_win_resize_object_add(win, conform);
  164.  
  165. evas_object_show(conform);
  166.  
  167. return conform;
  168. }
  169.  
  170. /**
  171. * @brief Creates a layout object for parent object based on provided EDJE script.
  172. * @param[in] parent The parent object for layout object.
  173. * @param[in] edj_file_name The relative path to the layout EDJE script.
  174. * @param[in] edj_group The name of the group to be loaded from the EDJE script.
  175. * @param[in] target_part_name The EDJE part's name where the layout is to be set.
  176. * @return The function returns layout object if it was created successfully,
  177. * otherwise 'NULL' is returned.
  178. */
  179. Evas_Object *view_create_layout(Evas_Object *parent, const char *edj_file_name, const char *edj_group, const char *target_part_name)
  180. {
  181. char edj_path[PATH_MAX] = {0, };
  182. Evas_Object *layout = NULL;
  183.  
  184. if (!parent || !edj_file_name || !edj_group) {
  185. dlog_print(DLOG_ERROR, LOG_TAG, "Wrong input arguments.");
  186. return NULL;
  187. }
  188.  
  189. _get_app_resource(edj_file_name, edj_path);
  190.  
  191. layout = elm_layout_add(parent);
  192. if (!layout) {
  193. dlog_print(DLOG_ERROR, LOG_TAG, "Function elm_layout_add() failed.");
  194. return NULL;
  195. }
  196.  
  197. if (!elm_layout_file_set(layout, edj_path, edj_group)) {
  198. dlog_print(DLOG_ERROR, LOG_TAG, "Function elm_layout_file_set() failed.");
  199. evas_object_del(layout);
  200. return NULL;
  201. }
  202.  
  203. if (target_part_name)
  204. elm_object_part_content_set(parent, target_part_name, layout);
  205. else
  206. elm_object_content_set(parent, layout);
  207.  
  208. evas_object_size_hint_weight_set(layout, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
  209. eext_object_event_callback_add(layout, EEXT_CALLBACK_BACK, _layout_back_cb, NULL);
  210. evas_object_show(layout);
  211.  
  212. return layout;
  213. }
  214.  
  215. /**
  216. * @brief Creates a hidden label object.
  217. * @param[in] parent The parent object for label object.
  218. * @param[in] caption The text to be displayed on a label.
  219. * @return The function returns label object if it was created successfully,
  220. * otherwise 'NULL' is returned.
  221. */
  222. Evas_Object *view_create_label(Evas_Object *parent, const char *caption)
  223. {
  224. char theme_ext_path[256] = {0,};
  225. Evas_Object *label;
  226.  
  227. label = elm_label_add(parent);
  228. if (!label) {
  229. dlog_print(DLOG_ERROR, LOG_TAG, "elm_label_add() failed.");
  230. return NULL;
  231. }
  232.  
  233. elm_label_line_wrap_set(label, ELM_WRAP_MIXED);
  234. elm_label_ellipsis_set(label, EINA_TRUE);
  235. evas_object_pass_events_set(label, EINA_TRUE);
  236.  
  237. if (caption)
  238. elm_object_text_set(label, caption);
  239.  
  240. _get_app_resource(EDJ_SOURCE_EXT, theme_ext_path);
  241. elm_theme_extension_add(NULL, theme_ext_path);
  242. elm_object_style_set(label, GRP_SOURCE_EXT);
  243.  
  244. return label;
  245. }
  246.  
  247. /**
  248. * @brief Function resets the application to normal state.
  249. */
  250. void view_set_normal_state(void)
  251. {
  252. s_info.state = STATE_NORMAL;
  253. }
  254.  
  255. /**
  256. * @brief Destroys window and frees its resources.
  257. */
  258. void view_destroy(void)
  259. {
  260. ecore_event_handler_del((Ecore_Event_Handler *)_press_cb);
  261. ecore_event_handler_del((Ecore_Event_Handler *)_move_cb);
  262. ecore_event_handler_del((Ecore_Event_Handler *)_unpress_cb);
  263.  
  264. if (s_info.win == NULL)
  265. return;
  266.  
  267. evas_object_del(s_info.win);
  268. }
  269.  
  270. /**
  271. * @brief Internal callback function invoked when the main window needs to be destroyed.
  272. * @param[in] data The user data passed to the evas_object_smart_callback_add() function.
  273. * @param[in] obj The object invoking this callback function.
  274. * @param[in] event_info The structure containing the information on this event.
  275. */
  276. static void _delete_win_request_cb(void *data, Evas_Object *obj, void *event_info)
  277. {
  278. ui_app_exit();
  279. }
  280.  
  281. /**
  282. * @brief Internal callback function invoked on HW Back button press.
  283. * @param[in] data The user data passed to the eext_object_event_callback_add() function.
  284. * @param[in] obj The object invoking this callback function.
  285. * @param[in] event_info The structure containing the information on this event.
  286. */
  287. static void _layout_back_cb(void *data, Evas_Object *obj, void *event_info)
  288. {
  289. /* Let window go to hide state. */
  290. elm_win_lower(s_info.win);
  291. }
  292.  
  293. /**
  294. * @brief Internal callback function invoked on text paste operation performed using
  295. * the elm_cnp_selection_set() / elm_cnp_selection_get() functions.
  296. * @param[in] data The user data passed to the eext_object_event_callback_add() function.
  297. * @param[in] obj The object invoking this callback function.
  298. * @param[in] event_info The structure containing the information on this event.
  299. * @return The function returns 'EINA_TRUE' if the text paste operation is successful,
  300. * otherwise 'EINA_FALSE' is returned.
  301. */
  302. static Eina_Bool _text_paste_cb(void *data, Evas_Object *obj, Elm_Selection_Data *ev)
  303. {
  304. if (!ev || ev->format != ELM_SEL_FORMAT_TEXT)
  305. return EINA_FALSE;
  306.  
  307. elm_object_text_set(obj, (const char *)ev->data);
  308.  
  309. return EINA_TRUE;
  310. }
  311.  
  312. /**
  313. * @brief Internal callback function invoked on mouse button press event.
  314. * This function is responsible for copy the text from source widget and paste it
  315. * to the hover window. Afterward the hover window is displayed.
  316. * @param[in] data The user data passed to the ecore_event_handler_add() function.
  317. * @param[in] type The type of the event occurred .
  318. * @param[in] event The structure containing the information on this event.
  319. * @return The function returns 'EINA_TRUE' if the event is processed successfully,
  320. * otherwise 'EINA_FALSE' is returned.
  321. */
  322. static Eina_Bool _press_cb(void *data, int type, void *event)
  323. {
  324. Ecore_Event_Mouse_Button *press = (Ecore_Event_Mouse_Button *)event;
  325.  
  326. if (s_info.state != STATE_NORMAL || !_source_hit_test(press->root.x, press->root.y))
  327. return EINA_TRUE;
  328.  
  329. _paste_to_hover();
  330.  
  331. s_info.state = STATE_PRESS;
  332. s_info.drag_start_x = press->root.x;
  333. s_info.drag_start_y = press->root.y;
  334.  
  335. _set_hover_window_size();
  336. _set_hover_window_position(press->root.x, press->root.y);
  337. _show_hover_window();
  338.  
  339. return EINA_TRUE;
  340. }
  341.  
  342. /**
  343. * @brief Internal callback function invoked on mouse move event.
  344. * This function is responsible for hover window location update according to
  345. * the mouse position.
  346. * @param[in] data The user data passed to the ecore_event_handler_add() function.
  347. * @param[in] type The type of the event occurred .
  348. * @param[in] event The structure containing the information on this event.
  349. * @return The function returns 'EINA_TRUE' if the event is processed successfully,
  350. * otherwise 'EINA_FALSE' is returned.
  351. */
  352. static Eina_Bool _move_cb(void *data, int type, void *event)
  353. {
  354. Ecore_Event_Mouse_Move *move = (Ecore_Event_Mouse_Move *)event;
  355.  
  356. if (s_info.state == STATE_PRESS)
  357. s_info.state = STATE_DRAG;
  358.  
  359. if (s_info.state != STATE_DRAG)
  360. return EINA_TRUE;
  361.  
  362. _set_hover_window_position(move->root.x, move->root.y);
  363.  
  364. return EINA_TRUE;
  365. }
  366.  
  367. /**
  368. * @brief Internal callback function invoked on mouse button unpressed event.
  369. * This function is responsible for copy the text from hover window and paste it
  370. * to the sink widget. Afterward the hover window is dismissed.
  371. * @param[in] data The user data passed to the ecore_event_handler_add() function.
  372. * @param[in] type The type of the event occurred .
  373. * @param[in] event The structure containing the information on this event.
  374. * @return The function returns 'EINA_TRUE' if the event is processed successfully,
  375. * otherwise 'EINA_FALSE' is returned.
  376. */
  377. static Eina_Bool _unpress_cb(void *data, int type, void *event)
  378. {
  379. Ecore_Event_Mouse_Button *unpress = (Ecore_Event_Mouse_Button *)event;
  380.  
  381. _hide_hover_window();
  382.  
  383. if (s_info.state == STATE_DRAG && _sink_hit_test(unpress->root.x, unpress->root.y))
  384. _paste_to_sink();
  385.  
  386. s_info.state = STATE_NORMAL;
  387.  
  388. return EINA_TRUE;
  389. }
  390.  
  391. /**
  392. * @brief Internal function which creates fully qualified path to the provided resource file.
  393. * @param[in] file_in The file name.
  394. * @param[out] path_out The fully qualified path to the edj_file_in file.
  395. */
  396. static void _get_app_resource(const char *file_in, char *path_out)
  397. {
  398. char *res_path = app_get_resource_path();
  399. if (res_path) {
  400. snprintf(path_out, PATH_MAX, "%s%s", res_path, file_in);
  401. free(res_path);
  402. }
  403. }
  404.  
  405. /**
  406. * @brief Internal function which creates the source label set to the top of the layout.
  407. * @return The function returns 'EINA_TRUE' if the source label was created successfully,
  408. * otherwise 'EINA_FALSE' is returned.
  409. */
  410. static Eina_Bool _create_source(void)
  411. {
  412. s_info.source_label = view_create_label(s_info.main_layout, source_text);
  413. if (!s_info.source_label) {
  414. dlog_print(DLOG_ERROR, LOG_TAG, "view_create_label() failed.");
  415. return EINA_FALSE;
  416. }
  417.  
  418. elm_object_part_content_set(s_info.main_layout, PART_MAIN_SOURCE, s_info.source_label);
  419. evas_object_show(s_info.source_label);
  420.  
  421. return EINA_TRUE;
  422. }
  423.  
  424. /**
  425. * @brief Internal function which creates the sink entry set to the bottom of the layout.
  426. * @return The function returns 'EINA_TRUE' if the sink entry was created successfully,
  427. * otherwise 'EINA_FALSE' is returned.
  428. */
  429. static Eina_Bool _create_sink(void)
  430. {
  431. s_info.sink_entry = elm_entry_add(s_info.main_layout);
  432. if (!s_info.sink_entry) {
  433. dlog_print(DLOG_ERROR, LOG_TAG, "elm_entry_add() failed.");
  434. return EINA_FALSE;
  435. }
  436.  
  437. elm_entry_input_panel_enabled_set(s_info.sink_entry, EINA_FALSE);
  438. elm_entry_scrollable_set(s_info.sink_entry, EINA_TRUE);
  439.  
  440. elm_entry_text_style_user_push(s_info.sink_entry, "DEFAULT='font_size=24 color=#ff0000'");
  441.  
  442. elm_object_part_content_set(s_info.main_layout, PART_MAIN_SINK, s_info.sink_entry);
  443.  
  444. return EINA_TRUE;
  445. }
  446.  
  447. /**
  448. * @brief Internal function which creates the label object which plays a role of a hoover window.
  449. * In order to display the window properly, its position must be set before
  450. * _show_hover_window() function is called. The hover window's position shall be set with
  451. * _set_hover_window_position() function.
  452. * @return The function returns 'EINA_TRUE' if the hover window was created successfully,
  453. * otherwise 'EINA_FALSE' is returned.
  454. */
  455. static Eina_Bool _create_hover_window(void)
  456. {
  457. s_info.hover = view_create_label(s_info.win, NULL);
  458. if (!s_info.hover) {
  459. dlog_print(DLOG_ERROR, LOG_TAG, "view_create_label() failed.");
  460. return EINA_FALSE;
  461. }
  462.  
  463. evas_object_layer_set(s_info.hover, EVAS_LAYER_MAX);
  464.  
  465. return EINA_TRUE;
  466. }
  467.  
  468. /**
  469. * @brief Internal function for mouse events callbacks setting.
  470. * The following callbacks are set:
  471. * - mouse button down,
  472. * - mouse button up,
  473. * - mouse move.
  474. */
  475. static void _set_callbacks(void)
  476. {
  477. ecore_event_handler_add(ECORE_EVENT_MOUSE_BUTTON_DOWN, _press_cb, NULL);
  478. ecore_event_handler_add(ECORE_EVENT_MOUSE_MOVE, _move_cb, NULL);
  479. ecore_event_handler_add(ECORE_EVENT_MOUSE_BUTTON_UP, _unpress_cb, NULL);
  480. }
  481.  
  482. /**
  483. * @brief Internal function which checks whether the given coordinates are contained
  484. * within a specified object's boundaries.
  485. * @param[in] obj The object which is subject for coordinates containment test.
  486. * @param[in] hit_x The X coordinate.
  487. * @param[in] hit_y The Y coordinate.
  488. * @return The function returns 'true' if the given coordinates are contained within
  489. * a specified object's boundaries.
  490. */
  491. static bool _hit_test(Evas_Object *obj, int hit_x, int hit_y)
  492. {
  493. Evas_Coord x = 0, y = 0, w = 0, h = 0;
  494. evas_object_geometry_get(obj, &x, &y, &w, &h);
  495.  
  496. return hit_x >= x && hit_x < (x + w) && hit_y >= y && hit_y < (y + h);
  497. }
  498.  
  499. /**
  500. * @brief Internal function which checks whether the given coordinates are contained
  501. * within a source widget boundaries.
  502. * @param[in] x The X coordinate.
  503. * @param[in] y The Y coordinate.
  504. * @return The function returns 'true' if the given coordinates are contained within
  505. * a source widget boundaries.
  506. */
  507. static bool _source_hit_test(int x, int y)
  508. {
  509. return _hit_test(s_info.source_label, x, y);
  510. }
  511.  
  512. /**
  513. * @brief Internal function which checks whether the given coordinates are contained
  514. * within a sink widget boundaries.
  515. * @param[in] x The X coordinate.
  516. * @param[in] y The Y coordinate.
  517. * @return The function returns 'true' if the given coordinates are contained within
  518. * a sink widget boundaries.
  519. */
  520. static bool _sink_hit_test(int x, int y)
  521. {
  522. return _hit_test(s_info.sink_entry, x, y);
  523. }
  524.  
  525. /**
  526. * @brief Internal function which takes the text from the source widget and pastes it
  527. * to the hover window. The paste operation is performed using 'cnp' mechanism.
  528. */
  529. static void _paste_to_hover(void)
  530. {
  531. const char *text_to_paste = elm_object_part_text_get(s_info.source_label, "default");
  532.  
  533. /* We could just use elm_object_text_set, but for the exemplary purpose the cnp mechanism is used:
  534. * use copy mechanism, fill copy buffer associated with the label */
  535. elm_cnp_selection_set(s_info.hover, ELM_SEL_TYPE_PRIMARY, ELM_SEL_FORMAT_TEXT, text_to_paste, strlen(text_to_paste));
  536.  
  537. /* get the text from the clipboard and since we use 'Label' widget type,
  538. * we have to use custom drop callback: _text_paste_cb */
  539. elm_cnp_selection_get(s_info.hover, ELM_SEL_TYPE_PRIMARY, ELM_SEL_FORMAT_TEXT, _text_paste_cb, NULL);
  540. }
  541.  
  542. /**
  543. * @brief Internal function which takes the text from the source widget and pastes it
  544. * to the hover window. The paste operation is performed using 'cnp' mechanism.
  545. */
  546. static void _paste_to_sink(void)
  547. {
  548. /* use paste mechanism. */
  549. elm_cnp_selection_get(s_info.sink_entry, ELM_SEL_TYPE_PRIMARY, ELM_SEL_FORMAT_TEXT, NULL, NULL);
  550.  
  551. /* after text has been copied, clear selection in clipboard. */
  552. elm_object_cnp_selection_clear(s_info.source_label, ELM_SEL_TYPE_CLIPBOARD);
  553. }
  554.  
  555. /**
  556. * @brief Internal function which sets the hover window size based on the size
  557. * of the main window.
  558. */
  559. static void _set_hover_window_size(void)
  560. {
  561. Evas_Coord w = 0;
  562. Evas_Coord h = 0;
  563.  
  564. evas_object_geometry_get(s_info.win, NULL, NULL, &w, &h);
  565.  
  566. w = (Evas_Coord)(w * 0.8);
  567. h = (Evas_Coord)(h * 0.3);
  568.  
  569. evas_object_resize(s_info.hover, w, h);
  570. }
  571.  
  572. /**
  573. * @brief Internal function which sets the display position of the hover window.
  574. * The position is set so the mouse hit point lies in the hover window's center point.
  575. * @param[in] x The X coordinate of the mouse hit point.
  576. * @param[in] y The Y coordinate of the mouse hit point.
  577. */
  578. static void _set_hover_window_position(int x, int y)
  579. {
  580. Evas_Coord w = 0;
  581. Evas_Coord h = 0;
  582.  
  583. evas_object_geometry_get(s_info.hover, NULL, NULL, &w, &h);
  584. evas_object_move(s_info.hover, (Evas_Coord)(x - (int)(w / 2)), (Evas_Coord)(y - (int)(h / 2)));
  585. }
  586.  
  587. /**
  588. * @brief Internal function which shows the hover window.
  589. */
  590. static void _show_hover_window(void)
  591. {
  592. evas_object_show(s_info.hover);
  593. }
  594.  
  595. /**
  596. * @brief Internal function which hides the hover window.
  597. */
  598. static void _hide_hover_window(void)
  599. {
  600. evas_object_hide(s_info.hover);
  601. }