Copy and paste / src /
view.c
- /*
- * Copyright (c) 2016 Samsung Electronics Co., Ltd
- *
- * Licensed under the Flora License, Version 1.1 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://floralicense.org/license/
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- #include <efl_extension.h>
- #include "$(appName).h"
- #include "view.h"
- #include "view_defines.h"
- typedef enum { STATE_NORMAL = 0, STATE_PRESS, STATE_DRAG } state_t;
- static const char *source_text = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna " \
- "aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis" \
- " aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat" \
- " cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.";
- static struct view_info {
- Evas_Object *win;
- Evas_Object *conform;
- Evas_Object *main_layout;
- Evas_Object *source_label;
- Evas_Object *sink_entry;
- Evas_Object *hover;
- state_t state;
- int drag_start_x;
- int drag_start_y;
- } s_info = {
- .win = NULL,
- .conform = NULL,
- .main_layout = NULL,
- .source_label = NULL,
- .sink_entry = NULL,
- .hover = NULL,
- .state = STATE_NORMAL,
- .drag_start_x = 0,
- .drag_start_y = 0,
- };
- static void _delete_win_request_cb(void *data, Evas_Object *obj, void *event_info);
- static void _layout_back_cb(void *data, Evas_Object *obj, void *event_info);
- static Eina_Bool _press_cb(void *data, int type, void *event);
- static Eina_Bool _move_cb(void *data, int type, void *event);
- static Eina_Bool _unpress_cb(void *data, int type, void *event);
- static void _get_app_resource(const char *file_in, char *path_out);
- static Eina_Bool _create_source(void);
- static Eina_Bool _create_sink(void);
- static Eina_Bool _create_hover_window(void);
- static void _set_callbacks(void);
- static bool _source_hit_test(int x, int y);
- static bool _sink_hit_test(int x, int y);
- static void _paste_to_hover(void);
- static void _paste_to_sink(void);
- static void _set_hover_window_size(void);
- static void _set_hover_window_position(int x, int y);
- static void _hide_hover_window(void);
- static void _show_hover_window(void);
- /**
- * @brief Creates essential objects: window, conformant and layout.
- */
- Eina_Bool view_create(void)
- {
- /* Create the window */
- s_info.win = view_create_win(PACKAGE);
- if (s_info.win == NULL) {
- dlog_print(DLOG_ERROR, LOG_TAG, "failed to create a window.");
- return EINA_FALSE;
- }
- /* Create the conformant */
- s_info.conform = view_create_conformant_without_indicator(s_info.win);
- if (s_info.conform == NULL) {
- dlog_print(DLOG_ERROR, LOG_TAG, "failed to create a conformant");
- return EINA_FALSE;
- }
- s_info.main_layout = view_create_layout(s_info.conform, EDJ_MAIN, GRP_MAIN, NULL);
- if (!s_info.main_layout) {
- dlog_print(DLOG_ERROR, LOG_TAG, "view_create_layout() failed.");
- return EINA_FALSE;
- }
- if (!_create_source()) {
- dlog_print(DLOG_ERROR, LOG_TAG, "_create_source() failed.");
- return false;
- }
- if (!_create_sink()) {
- dlog_print(DLOG_ERROR, LOG_TAG, "_create_sink() failed.");
- return false;
- }
- if (!_create_hover_window()) {
- dlog_print(DLOG_ERROR, LOG_TAG, "_create_hover_window() failed.");
- return false;
- }
- _set_callbacks();
- /* Show the window after main view is set up */
- evas_object_show(s_info.win);
- return EINA_TRUE;
- }
- /**
- * @brief Creates a basic window named package.
- * @param[in] pkg_name Name of the window
- */
- Evas_Object *view_create_win(const char *pkg_name)
- {
- Evas_Object *win = NULL;
- /*
- * Window
- * Create and initialize elm_win.
- * elm_win is mandatory to manipulate the window.
- */
- win = elm_win_util_standard_add(pkg_name, pkg_name);
- elm_win_conformant_set(win, EINA_TRUE);
- elm_win_autodel_set(win, EINA_TRUE);
- evas_object_pass_events_set(win, EINA_FALSE);
- evas_object_smart_callback_add(win, "delete,request", _delete_win_request_cb, NULL);
- return win;
- }
- /**
- * @brief Creates a conformant without indicator for wearable app.
- * @param[in] win The object to which you want to set this conformant
- * Conformant is mandatory for base GUI to have proper size
- */
- Evas_Object *view_create_conformant_without_indicator(Evas_Object *win)
- {
- /*
- * Conformant
- * Create and initialize elm_conformant.
- * elm_conformant is mandatory for base GUI to have proper size
- * when indicator or virtual keypad is visible.
- */
- Evas_Object *conform = NULL;
- if (win == NULL) {
- dlog_print(DLOG_ERROR, LOG_TAG, "window is NULL.");
- return NULL;
- }
- conform = elm_conformant_add(win);
- evas_object_size_hint_weight_set(conform, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
- elm_win_resize_object_add(win, conform);
- evas_object_show(conform);
- return conform;
- }
- /**
- * @brief Creates a layout object for parent object based on provided EDJE script.
- * @param[in] parent The parent object for layout object.
- * @param[in] edj_file_name The relative path to the layout EDJE script.
- * @param[in] edj_group The name of the group to be loaded from the EDJE script.
- * @param[in] target_part_name The EDJE part's name where the layout is to be set.
- * @return The function returns layout object if it was created successfully,
- * otherwise 'NULL' is returned.
- */
- Evas_Object *view_create_layout(Evas_Object *parent, const char *edj_file_name, const char *edj_group, const char *target_part_name)
- {
- char edj_path[PATH_MAX] = {0, };
- Evas_Object *layout = NULL;
- if (!parent || !edj_file_name || !edj_group) {
- dlog_print(DLOG_ERROR, LOG_TAG, "Wrong input arguments.");
- return NULL;
- }
- _get_app_resource(edj_file_name, edj_path);
- layout = elm_layout_add(parent);
- if (!layout) {
- dlog_print(DLOG_ERROR, LOG_TAG, "Function elm_layout_add() failed.");
- return NULL;
- }
- if (!elm_layout_file_set(layout, edj_path, edj_group)) {
- dlog_print(DLOG_ERROR, LOG_TAG, "Function elm_layout_file_set() failed.");
- evas_object_del(layout);
- return NULL;
- }
- if (target_part_name)
- elm_object_part_content_set(parent, target_part_name, layout);
- else
- elm_object_content_set(parent, layout);
- evas_object_size_hint_weight_set(layout, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
- eext_object_event_callback_add(layout, EEXT_CALLBACK_BACK, _layout_back_cb, NULL);
- evas_object_show(layout);
- return layout;
- }
- /**
- * @brief Creates a hidden label object.
- * @param[in] parent The parent object for label object.
- * @param[in] caption The text to be displayed on a label.
- * @return The function returns label object if it was created successfully,
- * otherwise 'NULL' is returned.
- */
- Evas_Object *view_create_label(Evas_Object *parent, const char *caption)
- {
- char theme_ext_path[256] = {0,};
- Evas_Object *label;
- label = elm_label_add(parent);
- if (!label) {
- dlog_print(DLOG_ERROR, LOG_TAG, "elm_label_add() failed.");
- return NULL;
- }
- elm_label_line_wrap_set(label, ELM_WRAP_MIXED);
- elm_label_ellipsis_set(label, EINA_TRUE);
- evas_object_pass_events_set(label, EINA_TRUE);
- if (caption)
- elm_object_text_set(label, caption);
- _get_app_resource(EDJ_SOURCE_EXT, theme_ext_path);
- elm_theme_extension_add(NULL, theme_ext_path);
- elm_object_style_set(label, GRP_SOURCE_EXT);
- return label;
- }
- /**
- * @brief Function resets the application to normal state.
- */
- void view_set_normal_state(void)
- {
- s_info.state = STATE_NORMAL;
- }
- /**
- * @brief Destroys window and frees its resources.
- */
- void view_destroy(void)
- {
- ecore_event_handler_del((Ecore_Event_Handler *)_press_cb);
- ecore_event_handler_del((Ecore_Event_Handler *)_move_cb);
- ecore_event_handler_del((Ecore_Event_Handler *)_unpress_cb);
- if (s_info.win == NULL)
- return;
- evas_object_del(s_info.win);
- }
- /**
- * @brief Internal callback function invoked when the main window needs to be destroyed.
- * @param[in] data The user data passed to the evas_object_smart_callback_add() function.
- * @param[in] obj The object invoking this callback function.
- * @param[in] event_info The structure containing the information on this event.
- */
- static void _delete_win_request_cb(void *data, Evas_Object *obj, void *event_info)
- {
- ui_app_exit();
- }
- /**
- * @brief Internal callback function invoked on HW Back button press.
- * @param[in] data The user data passed to the eext_object_event_callback_add() function.
- * @param[in] obj The object invoking this callback function.
- * @param[in] event_info The structure containing the information on this event.
- */
- static void _layout_back_cb(void *data, Evas_Object *obj, void *event_info)
- {
- /* Let window go to hide state. */
- elm_win_lower(s_info.win);
- }
- /**
- * @brief Internal callback function invoked on text paste operation performed using
- * the elm_cnp_selection_set() / elm_cnp_selection_get() functions.
- * @param[in] data The user data passed to the eext_object_event_callback_add() function.
- * @param[in] obj The object invoking this callback function.
- * @param[in] event_info The structure containing the information on this event.
- * @return The function returns 'EINA_TRUE' if the text paste operation is successful,
- * otherwise 'EINA_FALSE' is returned.
- */
- static Eina_Bool _text_paste_cb(void *data, Evas_Object *obj, Elm_Selection_Data *ev)
- {
- if (!ev || ev->format != ELM_SEL_FORMAT_TEXT)
- return EINA_FALSE;
- elm_object_text_set(obj, (const char *)ev->data);
- return EINA_TRUE;
- }
- /**
- * @brief Internal callback function invoked on mouse button press event.
- * This function is responsible for copy the text from source widget and paste it
- * to the hover window. Afterward the hover window is displayed.
- * @param[in] data The user data passed to the ecore_event_handler_add() function.
- * @param[in] type The type of the event occurred .
- * @param[in] event The structure containing the information on this event.
- * @return The function returns 'EINA_TRUE' if the event is processed successfully,
- * otherwise 'EINA_FALSE' is returned.
- */
- static Eina_Bool _press_cb(void *data, int type, void *event)
- {
- Ecore_Event_Mouse_Button *press = (Ecore_Event_Mouse_Button *)event;
- if (s_info.state != STATE_NORMAL || !_source_hit_test(press->root.x, press->root.y))
- return EINA_TRUE;
- _paste_to_hover();
- s_info.state = STATE_PRESS;
- s_info.drag_start_x = press->root.x;
- s_info.drag_start_y = press->root.y;
- _set_hover_window_size();
- _set_hover_window_position(press->root.x, press->root.y);
- _show_hover_window();
- return EINA_TRUE;
- }
- /**
- * @brief Internal callback function invoked on mouse move event.
- * This function is responsible for hover window location update according to
- * the mouse position.
- * @param[in] data The user data passed to the ecore_event_handler_add() function.
- * @param[in] type The type of the event occurred .
- * @param[in] event The structure containing the information on this event.
- * @return The function returns 'EINA_TRUE' if the event is processed successfully,
- * otherwise 'EINA_FALSE' is returned.
- */
- static Eina_Bool _move_cb(void *data, int type, void *event)
- {
- Ecore_Event_Mouse_Move *move = (Ecore_Event_Mouse_Move *)event;
- if (s_info.state == STATE_PRESS)
- s_info.state = STATE_DRAG;
- if (s_info.state != STATE_DRAG)
- return EINA_TRUE;
- _set_hover_window_position(move->root.x, move->root.y);
- return EINA_TRUE;
- }
- /**
- * @brief Internal callback function invoked on mouse button unpressed event.
- * This function is responsible for copy the text from hover window and paste it
- * to the sink widget. Afterward the hover window is dismissed.
- * @param[in] data The user data passed to the ecore_event_handler_add() function.
- * @param[in] type The type of the event occurred .
- * @param[in] event The structure containing the information on this event.
- * @return The function returns 'EINA_TRUE' if the event is processed successfully,
- * otherwise 'EINA_FALSE' is returned.
- */
- static Eina_Bool _unpress_cb(void *data, int type, void *event)
- {
- Ecore_Event_Mouse_Button *unpress = (Ecore_Event_Mouse_Button *)event;
- _hide_hover_window();
- if (s_info.state == STATE_DRAG && _sink_hit_test(unpress->root.x, unpress->root.y))
- _paste_to_sink();
- s_info.state = STATE_NORMAL;
- return EINA_TRUE;
- }
- /**
- * @brief Internal function which creates fully qualified path to the provided resource file.
- * @param[in] file_in The file name.
- * @param[out] path_out The fully qualified path to the edj_file_in file.
- */
- static void _get_app_resource(const char *file_in, char *path_out)
- {
- char *res_path = app_get_resource_path();
- if (res_path) {
- snprintf(path_out, PATH_MAX, "%s%s", res_path, file_in);
- free(res_path);
- }
- }
- /**
- * @brief Internal function which creates the source label set to the top of the layout.
- * @return The function returns 'EINA_TRUE' if the source label was created successfully,
- * otherwise 'EINA_FALSE' is returned.
- */
- static Eina_Bool _create_source(void)
- {
- s_info.source_label = view_create_label(s_info.main_layout, source_text);
- if (!s_info.source_label) {
- dlog_print(DLOG_ERROR, LOG_TAG, "view_create_label() failed.");
- return EINA_FALSE;
- }
- elm_object_part_content_set(s_info.main_layout, PART_MAIN_SOURCE, s_info.source_label);
- evas_object_show(s_info.source_label);
- return EINA_TRUE;
- }
- /**
- * @brief Internal function which creates the sink entry set to the bottom of the layout.
- * @return The function returns 'EINA_TRUE' if the sink entry was created successfully,
- * otherwise 'EINA_FALSE' is returned.
- */
- static Eina_Bool _create_sink(void)
- {
- s_info.sink_entry = elm_entry_add(s_info.main_layout);
- if (!s_info.sink_entry) {
- dlog_print(DLOG_ERROR, LOG_TAG, "elm_entry_add() failed.");
- return EINA_FALSE;
- }
- elm_entry_input_panel_enabled_set(s_info.sink_entry, EINA_FALSE);
- elm_entry_scrollable_set(s_info.sink_entry, EINA_TRUE);
- elm_entry_text_style_user_push(s_info.sink_entry, "DEFAULT='font_size=24 color=#ff0000'");
- elm_object_part_content_set(s_info.main_layout, PART_MAIN_SINK, s_info.sink_entry);
- return EINA_TRUE;
- }
- /**
- * @brief Internal function which creates the label object which plays a role of a hoover window.
- * In order to display the window properly, its position must be set before
- * _show_hover_window() function is called. The hover window's position shall be set with
- * _set_hover_window_position() function.
- * @return The function returns 'EINA_TRUE' if the hover window was created successfully,
- * otherwise 'EINA_FALSE' is returned.
- */
- static Eina_Bool _create_hover_window(void)
- {
- s_info.hover = view_create_label(s_info.win, NULL);
- if (!s_info.hover) {
- dlog_print(DLOG_ERROR, LOG_TAG, "view_create_label() failed.");
- return EINA_FALSE;
- }
- evas_object_layer_set(s_info.hover, EVAS_LAYER_MAX);
- return EINA_TRUE;
- }
- /**
- * @brief Internal function for mouse events callbacks setting.
- * The following callbacks are set:
- * - mouse button down,
- * - mouse button up,
- * - mouse move.
- */
- static void _set_callbacks(void)
- {
- ecore_event_handler_add(ECORE_EVENT_MOUSE_BUTTON_DOWN, _press_cb, NULL);
- ecore_event_handler_add(ECORE_EVENT_MOUSE_MOVE, _move_cb, NULL);
- ecore_event_handler_add(ECORE_EVENT_MOUSE_BUTTON_UP, _unpress_cb, NULL);
- }
- /**
- * @brief Internal function which checks whether the given coordinates are contained
- * within a specified object's boundaries.
- * @param[in] obj The object which is subject for coordinates containment test.
- * @param[in] hit_x The X coordinate.
- * @param[in] hit_y The Y coordinate.
- * @return The function returns 'true' if the given coordinates are contained within
- * a specified object's boundaries.
- */
- static bool _hit_test(Evas_Object *obj, int hit_x, int hit_y)
- {
- Evas_Coord x = 0, y = 0, w = 0, h = 0;
- evas_object_geometry_get(obj, &x, &y, &w, &h);
- return hit_x >= x && hit_x < (x + w) && hit_y >= y && hit_y < (y + h);
- }
- /**
- * @brief Internal function which checks whether the given coordinates are contained
- * within a source widget boundaries.
- * @param[in] x The X coordinate.
- * @param[in] y The Y coordinate.
- * @return The function returns 'true' if the given coordinates are contained within
- * a source widget boundaries.
- */
- static bool _source_hit_test(int x, int y)
- {
- return _hit_test(s_info.source_label, x, y);
- }
- /**
- * @brief Internal function which checks whether the given coordinates are contained
- * within a sink widget boundaries.
- * @param[in] x The X coordinate.
- * @param[in] y The Y coordinate.
- * @return The function returns 'true' if the given coordinates are contained within
- * a sink widget boundaries.
- */
- static bool _sink_hit_test(int x, int y)
- {
- return _hit_test(s_info.sink_entry, x, y);
- }
- /**
- * @brief Internal function which takes the text from the source widget and pastes it
- * to the hover window. The paste operation is performed using 'cnp' mechanism.
- */
- static void _paste_to_hover(void)
- {
- const char *text_to_paste = elm_object_part_text_get(s_info.source_label, "default");
- /* We could just use elm_object_text_set, but for the exemplary purpose the cnp mechanism is used:
- * use copy mechanism, fill copy buffer associated with the label */
- elm_cnp_selection_set(s_info.hover, ELM_SEL_TYPE_PRIMARY, ELM_SEL_FORMAT_TEXT, text_to_paste, strlen(text_to_paste));
- /* get the text from the clipboard and since we use 'Label' widget type,
- * we have to use custom drop callback: _text_paste_cb */
- elm_cnp_selection_get(s_info.hover, ELM_SEL_TYPE_PRIMARY, ELM_SEL_FORMAT_TEXT, _text_paste_cb, NULL);
- }
- /**
- * @brief Internal function which takes the text from the source widget and pastes it
- * to the hover window. The paste operation is performed using 'cnp' mechanism.
- */
- static void _paste_to_sink(void)
- {
- /* use paste mechanism. */
- elm_cnp_selection_get(s_info.sink_entry, ELM_SEL_TYPE_PRIMARY, ELM_SEL_FORMAT_TEXT, NULL, NULL);
- /* after text has been copied, clear selection in clipboard. */
- elm_object_cnp_selection_clear(s_info.source_label, ELM_SEL_TYPE_CLIPBOARD);
- }
- /**
- * @brief Internal function which sets the hover window size based on the size
- * of the main window.
- */
- static void _set_hover_window_size(void)
- {
- Evas_Coord w = 0;
- Evas_Coord h = 0;
- evas_object_geometry_get(s_info.win, NULL, NULL, &w, &h);
- w = (Evas_Coord)(w * 0.8);
- h = (Evas_Coord)(h * 0.3);
- evas_object_resize(s_info.hover, w, h);
- }
- /**
- * @brief Internal function which sets the display position of the hover window.
- * The position is set so the mouse hit point lies in the hover window's center point.
- * @param[in] x The X coordinate of the mouse hit point.
- * @param[in] y The Y coordinate of the mouse hit point.
- */
- static void _set_hover_window_position(int x, int y)
- {
- Evas_Coord w = 0;
- Evas_Coord h = 0;
- evas_object_geometry_get(s_info.hover, NULL, NULL, &w, &h);
- evas_object_move(s_info.hover, (Evas_Coord)(x - (int)(w / 2)), (Evas_Coord)(y - (int)(h / 2)));
- }
- /**
- * @brief Internal function which shows the hover window.
- */
- static void _show_hover_window(void)
- {
- evas_object_show(s_info.hover);
- }
- /**
- * @brief Internal function which hides the hover window.
- */
- static void _hide_hover_window(void)
- {
- evas_object_hide(s_info.hover);
- }