diff --git a/src/lib/elementary/efl_ui_calendar.c b/src/lib/elementary/efl_ui_calendar.c index cc2149f73c..e630b1dc22 100644 --- a/src/lib/elementary/efl_ui_calendar.c +++ b/src/lib/elementary/efl_ui_calendar.c @@ -1,1322 +1,1322 @@ #ifdef HAVE_CONFIG_H # include "elementary_config.h" #endif #define EFL_UI_FOCUS_COMPOSITION_PROTECTED #define EFL_UI_FOCUS_OBJECT_PROTECTED #define EFL_ACCESS_WIDGET_ACTION_PROTECTED #include #include "elm_priv.h" #include "efl_ui_calendar_private.h" #include "efl_ui_calendar_item.eo.h" #define MY_CLASS EFL_UI_CALENDAR_CLASS #define MY_CLASS_NAME "Efl.Ui.Calendar" #define MY_CLASS_PFX efl_ui_calendar #define EFL_UI_CALENDAR_BUTTON_LEFT "elm,calendar,button,left" #define EFL_UI_CALENDAR_BUTTON_RIGHT "elm,calendar,button,right" #define EFL_UI_CALENDAR_BUTTON_YEAR_LEFT "elm,calendar,button_year,left" #define EFL_UI_CALENDAR_BUTTON_YEAR_RIGHT "elm,calendar,button_year,right" static const char PART_NAME_DEC_BUTTON[] = "dec_button"; static const char PART_NAME_INC_BUTTON[] = "inc_button"; static const char SIG_CHANGED[] = "changed"; static const Evas_Smart_Cb_Description _smart_callbacks[] = { {SIG_CHANGED, ""}, {SIG_WIDGET_LANG_CHANGED, ""}, /**< handled by elm_widget */ {SIG_WIDGET_ACCESS_CHANGED, ""}, /**< handled by elm_widget */ {SIG_LAYOUT_FOCUSED, ""}, /**< handled by elm_layout */ {SIG_LAYOUT_UNFOCUSED, ""}, /**< handled by elm_layout */ {NULL, NULL} }; static void _button_widget_month_dec_start(void *data, const Efl_Event *ev EINA_UNUSED); static void _button_widget_month_dec_start_click(void *data, const Efl_Event *ev EINA_UNUSED); static void _button_widget_month_inc_start(void *data, const Efl_Event *ev EINA_UNUSED); static void _button_widget_month_inc_start_click(void *data, const Efl_Event *ev EINA_UNUSED); static Eina_Bool _key_action_activate(Evas_Object *obj, const char *params); static const Elm_Action key_actions[] = { {"activate", _key_action_activate}, {NULL, NULL} }; /* Should not be translated, it's used if we failed * getting from locale. */ static const char *_days_abbrev[] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" }; static int _days_in_month[2][12] = { {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}, {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31} }; static Eina_Bool _efl_ui_calendar_smart_focus_next_enable = EINA_FALSE; EOLIAN static void _efl_ui_calendar_elm_layout_sizing_eval(Eo *obj, Efl_Ui_Calendar_Data *_pd EINA_UNUSED) { Evas_Coord minw = -1, minh = -1; EFL_UI_CALENDAR_DATA_GET(obj, sd); ELM_WIDGET_DATA_GET_OR_RETURN(obj, wd); if (sd->filling) return; // 7x8 (1 month+year, days, 6 dates.) elm_coords_finger_size_adjust(7, &minw, 8, &minh); edje_object_size_min_restricted_calc (wd->resize_obj, &minw, &minh, minw, minh); evas_object_size_hint_min_set(obj, minw, minh); evas_object_size_hint_max_set(obj, -1, -1); } // Get the max day number for each month static inline int _maxdays_get(struct tm *date, int month_offset) { int month, year; month = (date->tm_mon + month_offset) % 12; year = date->tm_year + 1900; if (month < 0) month += 12; return _days_in_month [((!(year % 4)) && ((!(year % 400)) || (year % 100)))][month]; } static inline void _unselect(Evas_Object *obj, int selected) { char emission[32]; snprintf(emission, sizeof(emission), "cit_%d,unselected", selected); elm_layout_signal_emit(obj, emission, "elm"); } static inline void _select(Evas_Object *obj, int selected) { char emission[32]; EFL_UI_CALENDAR_DATA_GET(obj, sd); sd->focused_it = sd->selected_it = selected; snprintf(emission, sizeof(emission), "cit_%d,selected", selected); elm_layout_signal_emit(obj, emission, "elm"); } static inline void _not_today(Efl_Ui_Calendar_Data *sd) { char emission[32]; snprintf(emission, sizeof(emission), "cit_%d,not_today", sd->today_it); elm_layout_signal_emit(sd->obj, emission, "elm"); sd->today_it = -1; } static inline void _today(Efl_Ui_Calendar_Data *sd, int it) { char emission[32]; snprintf(emission, sizeof(emission), "cit_%d,today", it); elm_layout_signal_emit(sd->obj, emission, "elm"); sd->today_it = it; } static inline void _enable(Efl_Ui_Calendar_Data *sd, int it) { char emission[32]; snprintf(emission, sizeof(emission), "cit_%d,enable", it); elm_layout_signal_emit(sd->obj, emission, "elm"); } static inline void _disable(Efl_Ui_Calendar_Data *sd, int it) { char emission[32]; snprintf(emission, sizeof(emission), "cit_%d,disable", it); elm_layout_signal_emit(sd->obj, emission, "elm"); } static void _set_month_year(Efl_Ui_Calendar_Data *sd) { sd->filling = EINA_TRUE; if (sd->format_cb) { Eina_Value val; const char *buf; eina_value_setup(&val, EINA_VALUE_TYPE_TM); eina_value_set(&val, sd->shown_date); eina_strbuf_reset(sd->format_strbuf); sd->format_cb(sd->format_cb_data, sd->format_strbuf, val); buf = eina_strbuf_string_get(sd->format_strbuf); eina_value_flush(&val); if (buf) elm_layout_text_set(sd->obj, "month_text", buf); else elm_layout_text_set(sd->obj, "month_text", ""); } else { char *buf; buf = eina_strftime(E_("%B %Y"), &sd->shown_date); if (buf) { elm_layout_text_set(sd->obj, "month_text", buf); free(buf); } else elm_layout_text_set(sd->obj, "month_text", ""); } sd->filling = EINA_FALSE; } static char * _access_info_cb(void *data EINA_UNUSED, Evas_Object *obj) { char *ret; Eina_Strbuf *buf; buf = eina_strbuf_new(); eina_strbuf_append_printf(buf, "day %s", elm_widget_access_info_get(obj)); ret = eina_strbuf_string_steal(buf); eina_strbuf_free(buf); return ret; } static void _access_calendar_item_register(Evas_Object *obj) { unsigned int maxdays, i; char day_s[13], pname[14]; unsigned day = 0; Evas_Object *ao; EFL_UI_CALENDAR_DATA_GET(obj, sd); maxdays = _maxdays_get(&sd->shown_date, 0); for (i = 0; i < 42; i++) { if ((!day) && (i == sd->first_day_it)) day = 1; if ((day) && (day <= maxdays)) { snprintf(pname, sizeof(pname), "cit_%d.access", i); ao = _elm_access_edje_object_part_object_register (obj, elm_layout_edje_get(obj), pname); _elm_access_text_set(_elm_access_info_get(ao), ELM_ACCESS_TYPE, E_("calendar item")); _elm_access_callback_set(_elm_access_info_get(ao), ELM_ACCESS_INFO, _access_info_cb, NULL); snprintf(day_s, sizeof(day_s), "%d", (int) (day++)); elm_widget_access_info_set(ao, (const char*)day_s); } else { snprintf(pname, sizeof(pname), "cit_%d.access", i); _elm_access_edje_object_part_object_unregister (obj, elm_layout_edje_get(obj), pname); } } } static void _access_calendar_spinner_register(Evas_Object *obj) { Evas_Object *po; Elm_Access_Info *ai; EFL_UI_CALENDAR_DATA_GET(obj, sd); if (!sd->dec_btn_month) sd->dec_btn_month = _elm_access_edje_object_part_object_register (obj, elm_layout_edje_get(obj), "left_bt"); ai = _elm_access_info_get(sd->dec_btn_month); _elm_access_text_set(ai, ELM_ACCESS_TYPE, E_("calendar decrement month button")); if (!sd->inc_btn_month) sd->inc_btn_month = _elm_access_edje_object_part_object_register (obj, elm_layout_edje_get(obj), "right_bt"); ai = _elm_access_info_get(sd->inc_btn_month); _elm_access_text_set(ai, ELM_ACCESS_TYPE, E_("calendar increment month button")); sd->month_access = _elm_access_edje_object_part_object_register (obj, elm_layout_edje_get(obj), "text_month"); ai = _elm_access_info_get(sd->month_access); _elm_access_text_set(ai, ELM_ACCESS_TYPE, E_("calendar month")); ai = _elm_access_info_get(sd->year_access); _elm_access_text_set(ai, ELM_ACCESS_TYPE, E_("calendar year")); po = (Evas_Object *)edje_object_part_object_get (elm_layout_edje_get(obj), "month_text"); evas_object_pass_events_set(po, EINA_FALSE); } static void _access_calendar_register(Evas_Object *obj) { _access_calendar_spinner_register(obj); _access_calendar_item_register(obj); } static void _flush_calendar_composite_elements(Evas_Object *obj, Efl_Ui_Calendar_Data *sd) { Eina_List *items = NULL; int max_day = _maxdays_get(&sd->shown_date, 0); #define EXTEND(v) \ if (v) items = eina_list_append(items, v); \ EXTEND(sd->month_access); EXTEND(sd->dec_btn_month); EXTEND(sd->inc_btn_month); EXTEND(sd->year_access); #undef EXTEND for (int i = sd->first_day_it; i <= max_day; ++i) items = eina_list_append(items, sd->items[i]); efl_ui_focus_composition_elements_set(obj, items); } static void _populate(Evas_Object *obj) { int maxdays, prev_month_maxdays, day, mon, yr, i; char part[12], day_s[3]; struct tm first_day; EFL_UI_CALENDAR_DATA_GET(obj, sd); elm_layout_freeze(obj); sd->filling = EINA_FALSE; if (sd->today_it > 0) _not_today(sd); maxdays = _maxdays_get(&sd->shown_date, 0); prev_month_maxdays = _maxdays_get(&sd->shown_date, -1); mon = sd->shown_date.tm_mon; yr = sd->shown_date.tm_year; _set_month_year(sd); sd->filling = EINA_TRUE; /* Set days */ day = 0; first_day = sd->shown_date; first_day.tm_mday = 1; if (mktime(&first_day) == -1) { ERR("mktime can not give week day for this month properly. Please check year or month is proper."); return; } // Layout of the calendar is changed for removing the unfilled last row. if (first_day.tm_wday < (int)sd->first_week_day) sd->first_day_it = first_day.tm_wday + ELM_DAY_LAST - sd->first_week_day; else sd->first_day_it = first_day.tm_wday - sd->first_week_day; for (i = 0; i < 42; i++) { if ((!day) && (i == sd->first_day_it)) day = 1; if ((day == sd->current_date.tm_mday) && (mon == sd->current_date.tm_mon) && (yr == sd->current_date.tm_year)) _today(sd, i); if (day == sd->date.tm_mday) { if ((sd->selected_it > -1) && (sd->selected_it != i)) _unselect(obj, sd->selected_it); if ((mon == sd->date.tm_mon) && (yr == sd->date.tm_year)) _select(obj, i); } if ((day) && (day <= maxdays)) { if (((yr == sd->date_min.tm_year) && (mon == sd->date_min.tm_mon) && (day < sd->date_min.tm_mday)) || ((yr == sd->date_max.tm_year) && (mon == sd->date_max.tm_mon) && (day > sd->date_max.tm_mday))) _disable(sd, i); else _enable(sd, i); snprintf(day_s, sizeof(day_s), "%d", day++); } else { _disable(sd, i); if (day <= maxdays) snprintf(day_s, sizeof(day_s), "%d", prev_month_maxdays - sd->first_day_it + i + 1); else snprintf(day_s, sizeof(day_s), "%d", i - sd->first_day_it - maxdays + 1); } snprintf(part, sizeof(part), "cit_%d.text", i); elm_layout_text_set(obj, part, day_s); } // ACCESS if ((_elm_config->access_mode != ELM_ACCESS_MODE_OFF)) _access_calendar_item_register(obj); sd->filling = EINA_FALSE; elm_layout_thaw(obj); edje_object_message_signal_process(elm_layout_edje_get(obj)); _flush_calendar_composite_elements(obj, sd); } static void _set_headers(Evas_Object *obj) { static char part[] = "ch_0.text"; int i; struct tm *t; time_t temp = 259200; // the first sunday since epoch EFL_UI_CALENDAR_DATA_GET(obj, sd); elm_layout_freeze(obj); sd->filling = EINA_TRUE; t = gmtime(&temp); if (t) { t->tm_wday = 0; for (i = 0; i < ELM_DAY_LAST; i++) { char *buf; buf = eina_strftime("%a", t); if (buf) { sd->weekdays[i] = eina_stringshare_add(buf); free(buf); } else { /* If we failed getting day, get a default value */ sd->weekdays[i] = _days_abbrev[i]; WRN("Failed getting weekday name for '%s' from locale.", _days_abbrev[i]); } t->tm_wday++; } } for (i = 0; i < ELM_DAY_LAST; i++) { part[3] = i + '0'; elm_layout_text_set(obj, part, sd->weekdays[(i + sd->first_week_day) % ELM_DAY_LAST]); } sd->filling = EINA_FALSE; elm_layout_thaw(obj); } static void _spinner_buttons_add(Evas_Object *obj, Efl_Ui_Calendar_Data *sd) { ELM_WIDGET_DATA_GET_OR_RETURN(obj, wd); if (edje_object_part_exists(wd->resize_obj, EFL_UI_CALENDAR_BUTTON_LEFT)) { if (sd->dec_btn_month && efl_isa(sd->dec_btn_month, ELM_ACCESS_CLASS)) { _elm_access_edje_object_part_object_unregister (obj, elm_layout_edje_get(obj), "left_bt"); sd->dec_btn_month = NULL; } if (!sd->dec_btn_month) { sd->dec_btn_month = efl_add(EFL_UI_BUTTON_CLASS, obj, elm_widget_element_update(obj, efl_added, PART_NAME_DEC_BUTTON), efl_ui_autorepeat_enabled_set(efl_added, EINA_TRUE), efl_ui_autorepeat_initial_timeout_set(efl_added, 0.5), efl_ui_autorepeat_gap_timeout_set(efl_added, 0.2)); efl_event_callback_add(sd->dec_btn_month, EFL_UI_EVENT_CLICKED, _button_widget_month_dec_start_click, obj); efl_event_callback_add(sd->dec_btn_month, EFL_UI_EVENT_REPEATED, _button_widget_month_dec_start, obj); } elm_layout_content_set(obj, EFL_UI_CALENDAR_BUTTON_LEFT, sd->dec_btn_month); } else if (sd->dec_btn_month && !efl_isa(sd->dec_btn_month, ELM_ACCESS_CLASS)) { evas_object_del(sd->dec_btn_month); sd->dec_btn_month = NULL; } if (edje_object_part_exists(wd->resize_obj, EFL_UI_CALENDAR_BUTTON_RIGHT)) { if (sd->inc_btn_month && efl_isa(sd->inc_btn_month, ELM_ACCESS_CLASS)) { _elm_access_edje_object_part_object_unregister (obj, elm_layout_edje_get(obj), "right_bt"); sd->inc_btn_month = NULL; } if (!sd->inc_btn_month) { sd->inc_btn_month = efl_add(EFL_UI_BUTTON_CLASS, obj, elm_widget_element_update(obj, efl_added, PART_NAME_INC_BUTTON), efl_ui_autorepeat_enabled_set(efl_added, EINA_TRUE), efl_ui_autorepeat_initial_timeout_set(efl_added, 0.5), efl_ui_autorepeat_gap_timeout_set(efl_added, 0.2)); efl_event_callback_add(sd->inc_btn_month, EFL_UI_EVENT_CLICKED, _button_widget_month_inc_start_click, obj); efl_event_callback_add(sd->inc_btn_month, EFL_UI_EVENT_REPEATED, _button_widget_month_inc_start, obj); } elm_layout_content_set(obj, EFL_UI_CALENDAR_BUTTON_RIGHT, sd->inc_btn_month); } else if (sd->inc_btn_month && !efl_isa(sd->inc_btn_month, ELM_ACCESS_CLASS)) { evas_object_del(sd->inc_btn_month); sd->inc_btn_month = NULL; } } EOLIAN static Efl_Ui_Theme_Apply _efl_ui_calendar_efl_ui_widget_theme_apply(Eo *obj, Efl_Ui_Calendar_Data *sd) { Efl_Ui_Theme_Apply int_ret = EFL_UI_THEME_APPLY_FAILED; int_ret = efl_ui_widget_theme_apply(efl_super(obj, MY_CLASS)); if (!int_ret) return EFL_UI_THEME_APPLY_FAILED; _spinner_buttons_add(obj, sd); evas_object_smart_changed(obj); return int_ret; } /* Set correct tm_wday and tm_yday after other fields changes*/ static inline Eina_Bool _fix_date(Efl_Ui_Calendar_Data *sd) { Eina_Bool no_change = EINA_TRUE; if ((sd->date.tm_year < sd->date_min.tm_year) || ((sd->date.tm_year == sd->date_min.tm_year) && (sd->date.tm_mon < sd->date_min.tm_mon)) || ((sd->date.tm_year == sd->date_min.tm_year) && (sd->date.tm_mon == sd->date_min.tm_mon) && (sd->date.tm_mday < sd->date_min.tm_mday))) { sd->date.tm_year = sd->shown_date.tm_year = sd->date_min.tm_year; sd->date.tm_mon = sd->shown_date.tm_mon = sd->date_min.tm_mon; sd->date.tm_mday = sd->shown_date.tm_mday = sd->date_min.tm_mday; no_change = EINA_FALSE; } else if ((sd->date_max.tm_year != -1) && ((sd->date.tm_year > sd->date_max.tm_year) || ((sd->date.tm_year == sd->date_max.tm_year) && (sd->date.tm_mon > sd->date_max.tm_mon)) || ((sd->date.tm_year == sd->date_max.tm_year) && (sd->date.tm_mon == sd->date_max.tm_mon) && (sd->date.tm_mday > sd->date_max.tm_mday)))) { sd->date.tm_year = sd->shown_date.tm_year = sd->date_max.tm_year; sd->date.tm_mon = sd->shown_date.tm_mon = sd->date_max.tm_mon; sd->date.tm_mday = sd->shown_date.tm_mday = sd->date_max.tm_mday; no_change = EINA_FALSE; } else { if (sd->date.tm_mon != sd->shown_date.tm_mon) sd->date.tm_mon = sd->shown_date.tm_mon; if (sd->date.tm_year != sd->shown_date.tm_year) sd->date.tm_year = sd->shown_date.tm_year; } return no_change; } static Eina_Bool _update_data(Evas_Object *obj, int delta) { struct tm time_check; int maxdays; EFL_UI_CALENDAR_DATA_GET(obj, sd); /* check if it's a valid time. for 32 bits, year greater than 2037 is not */ time_check = sd->shown_date; time_check.tm_mon += delta; if (mktime(&time_check) == -1) { ERR("mktime can not give week day for the next month. Please check what is wrong with udpate date."); return EINA_FALSE; } sd->shown_date.tm_mon += delta; if (delta < 0) { if (sd->shown_date.tm_year == sd->date_min.tm_year) { if (sd->shown_date.tm_mon < sd->date_min.tm_mon) { sd->shown_date.tm_mon = sd->date_min.tm_mon; return EINA_FALSE; } } else if (sd->shown_date.tm_mon < 0) { sd->shown_date.tm_mon = 11; sd->shown_date.tm_year--; } } else { if (sd->shown_date.tm_year == sd->date_max.tm_year) { if (sd->shown_date.tm_mon > sd->date_max.tm_mon) { sd->shown_date.tm_mon = sd->date_max.tm_mon; return EINA_FALSE; } } else if (sd->shown_date.tm_mon > 11) { sd->shown_date.tm_mon = 0; sd->shown_date.tm_year++; } } maxdays = _maxdays_get(&sd->shown_date, 0); if (sd->date.tm_mday > maxdays) sd->date.tm_mday = maxdays; return EINA_TRUE; } static Eina_Bool _spin_month_value(void *data) { EFL_UI_CALENDAR_DATA_GET(data, sd); if (_update_data(data, sd->spin_speed)) evas_object_smart_changed(data); sd->interval = sd->interval / 1.05; ecore_timer_interval_set(sd->spin_month, sd->interval); return ECORE_CALLBACK_RENEW; } static void _button_widget_month_inc_start_click(void *data, const Efl_Event *ev EINA_UNUSED) { EFL_UI_CALENDAR_DATA_GET(data, sd); if (sd->month_repeated) { sd->month_repeated = EINA_FALSE; return; } sd->interval = sd->first_interval; sd->spin_speed = 1; _spin_month_value(data); } static void _button_widget_month_inc_start(void *data, const Efl_Event *ev EINA_UNUSED) { EFL_UI_CALENDAR_DATA_GET(data, sd); sd->spin_speed = 1; if (!sd->month_repeated) sd->interval = sd->first_interval; sd->month_repeated = EINA_TRUE; _spin_month_value(data); } static void _button_widget_month_dec_start_click(void *data, const Efl_Event *ev EINA_UNUSED) { EFL_UI_CALENDAR_DATA_GET(data, sd); if (sd->month_repeated) { sd->month_repeated = EINA_FALSE; return; } sd->interval = sd->first_interval; sd->spin_speed = -1; _spin_month_value(data); } static void _button_widget_month_dec_start(void *data, const Efl_Event *ev EINA_UNUSED) { EFL_UI_CALENDAR_DATA_GET(data, sd); sd->spin_speed = -1; if (!sd->month_repeated) sd->interval = sd->first_interval; sd->month_repeated = EINA_TRUE; _spin_month_value(data); } static int _get_item_day(Evas_Object *obj, int selected_it) { int day; EFL_UI_CALENDAR_DATA_GET(obj, sd); day = selected_it - sd->first_day_it + 1; if ((day < 0) || (day > _maxdays_get(&sd->shown_date, 0))) return 0; if ((sd->shown_date.tm_year == sd->date_min.tm_year) && (sd->shown_date.tm_mon == sd->date_min.tm_mon) && (day < sd->date_min.tm_mday)) { return 0; } else if ((sd->shown_date.tm_year == sd->date_max.tm_year) && (sd->shown_date.tm_mon == sd->date_max.tm_mon) && (day > sd->date_max.tm_mday)) { return 0; } return day; } static void _update_unfocused_it(Evas_Object *obj, int unfocused_it) { int day; char emission[32]; EFL_UI_CALENDAR_DATA_GET(obj, sd); day = _get_item_day(obj, unfocused_it); if (!day) return; sd->focused_it = -1; snprintf(emission, sizeof(emission), "cit_%d,unfocused", unfocused_it); elm_layout_signal_emit(obj, emission, "elm"); } static Eina_Bool _update_focused_it(Evas_Object *obj, int focused_it) { int day; char emission[32]; EFL_UI_CALENDAR_DATA_GET(obj, sd); day = _get_item_day(obj, focused_it); if (!day) return EINA_FALSE; snprintf(emission, sizeof(emission), "cit_%d,unfocused", sd->focused_it); elm_layout_signal_emit(obj, emission, "elm"); sd->focused_it = focused_it; snprintf(emission, sizeof(emission), "cit_%d,focused", sd->focused_it); elm_layout_signal_emit(obj, emission, "elm"); return EINA_TRUE; } static void _update_sel_it(Evas_Object *obj, int sel_it) { int day; EFL_UI_CALENDAR_DATA_GET(obj, sd); day = _get_item_day(obj, sel_it); if (!day) return; _unselect(obj, sd->selected_it); if (!sd->selected) sd->selected = EINA_TRUE; if (sd->focused_it) _update_unfocused_it(obj, sd->focused_it); sd->date.tm_mday = day; _fix_date(sd); _select(obj, sel_it); efl_event_callback_legacy_call(obj, EFL_UI_CALENDAR_EVENT_CHANGED, NULL); } static void _day_selected(void *data, Evas_Object *obj EINA_UNUSED, const char *emission EINA_UNUSED, const char *source) { int sel_it; sel_it = atoi(source); _update_sel_it(data, sel_it); } static inline int _time_to_next_day(struct tm *t) { return ((((24 - t->tm_hour) * 60) - t->tm_min) * 60) - t->tm_sec; } static Eina_Bool _update_cur_date(void *data) { time_t current_date; int t, day; EFL_UI_CALENDAR_DATA_GET(data, sd); if (sd->today_it > 0) _not_today(sd); current_date = time(NULL); localtime_r(¤t_date, &sd->current_date); t = _time_to_next_day(&sd->current_date); ecore_timer_interval_set(sd->update_timer, t); if ((sd->current_date.tm_mon != sd->shown_date.tm_mon) || (sd->current_date.tm_year != sd->shown_date.tm_year)) return ECORE_CALLBACK_RENEW; day = sd->current_date.tm_mday + sd->first_day_it - 1; _today(sd, day); return ECORE_CALLBACK_RENEW; } static Eina_Bool _key_action_activate(Evas_Object *obj, const char *params EINA_UNUSED) { EFL_UI_CALENDAR_DATA_GET(obj, sd); _update_sel_it(obj, sd->focused_it); return EINA_TRUE; } EOLIAN static Eina_Bool -_efl_ui_calendar_efl_ui_widget_on_focus_update(Eo *obj, Efl_Ui_Calendar_Data *sd) +_efl_ui_calendar_efl_ui_focus_object_on_focus_update(Eo *obj, Efl_Ui_Calendar_Data *sd) { Eina_Bool int_ret = EINA_FALSE; - int_ret = efl_ui_widget_on_focus_update(efl_super(obj, MY_CLASS)); + int_ret = efl_ui_focus_object_on_focus_update(efl_super(obj, MY_CLASS)); if (!int_ret) return EINA_FALSE; // FIXME : Currently, focused item is same with selected item. // After arranging focus logic in this widget, we need to make // focused item which is for indicating direction key input movement // on the calendar widget. if (efl_ui_focus_object_focus_get(obj)) _update_focused_it(obj, sd->selected_it); else _update_unfocused_it(obj, sd->focused_it); return EINA_TRUE; } EOLIAN static void _efl_ui_calendar_efl_canvas_group_group_calculate(Eo *obj, Efl_Ui_Calendar_Data *_pd EINA_UNUSED) { elm_layout_freeze(obj); _set_headers(obj); _populate(obj); elm_layout_thaw(obj); } EOLIAN static void _efl_ui_calendar_efl_object_destructor(Eo *obj, Efl_Ui_Calendar_Data *sd) { int i; ecore_timer_del(sd->spin_month); ecore_timer_del(sd->spin_year); ecore_timer_del(sd->update_timer); efl_ui_format_cb_set(obj, NULL, NULL, NULL); eina_strbuf_free(sd->format_strbuf); for (i = 0; i < ELM_DAY_LAST; i++) eina_stringshare_del(sd->weekdays[i]); efl_destructor(efl_super(obj, MY_CLASS)); } static void _access_obj_process(Evas_Object *obj, Eina_Bool is_access) { int maxdays, day, i; EFL_UI_CALENDAR_DATA_GET(obj, sd); ELM_WIDGET_DATA_GET_OR_RETURN(obj, wd); if (is_access) _access_calendar_register(obj); else { day = 0; maxdays = _maxdays_get(&sd->shown_date, 0); for (i = 0; i < 42; i++) { if ((!day) && (i == sd->first_day_it)) day = 1; if ((day) && (day <= maxdays)) { char pname[14]; snprintf(pname, sizeof(pname), "cit_%d.access", i); _elm_access_edje_object_part_object_unregister (obj, elm_layout_edje_get(obj), pname); } } if (sd->dec_btn_month && efl_isa(sd->dec_btn_month, ELM_ACCESS_CLASS)) { _elm_access_edje_object_part_object_unregister (obj, elm_layout_edje_get(obj), "left_bt"); sd->dec_btn_month = NULL; } if (sd->inc_btn_month && efl_isa(sd->inc_btn_month, ELM_ACCESS_CLASS)) { _elm_access_edje_object_part_object_unregister (obj, elm_layout_edje_get(obj), "right_bt"); sd->inc_btn_month = NULL; } if (sd->month_access) _elm_access_edje_object_part_object_unregister (obj, elm_layout_edje_get(obj), "month_text"); } } EOLIAN static void _efl_ui_calendar_efl_ui_widget_on_access_update(Eo *obj EINA_UNUSED, Efl_Ui_Calendar_Data *_pd EINA_UNUSED, Eina_Bool acs) { _efl_ui_calendar_smart_focus_next_enable = acs; _access_obj_process(obj, _efl_ui_calendar_smart_focus_next_enable); } static Eo * _efl_ui_calendar_constructor_internal(Eo *obj, Efl_Ui_Calendar_Data *priv) { time_t current_date; int t; ELM_WIDGET_DATA_GET_OR_RETURN(obj, wd, NULL); elm_widget_sub_object_parent_add(obj); priv->first_interval = 0.85; priv->date_min.tm_year = 2; priv->date_min.tm_mon = 0; priv->date_min.tm_mday = 1; priv->date_max.tm_year = -1; priv->date_max.tm_mon = 11; priv->date_max.tm_mday = 31; priv->today_it = -1; priv->selected_it = -1; priv->first_day_it = -1; priv->format_cb = NULL; edje_object_signal_callback_add (wd->resize_obj, "elm,action,selected", "*", _day_selected, obj); current_date = time(NULL); localtime_r(¤t_date, &priv->shown_date); priv->current_date = priv->shown_date; priv->date = priv->shown_date; t = _time_to_next_day(&priv->current_date); priv->update_timer = ecore_timer_add(t, _update_cur_date, obj); elm_widget_can_focus_set(obj, EINA_TRUE); if (!elm_widget_theme_klass_get(obj)) elm_widget_theme_klass_set(obj, "calendar"); if (!elm_widget_theme_object_set(obj, wd->resize_obj, elm_widget_theme_klass_get(obj), elm_widget_theme_element_get(obj), elm_widget_theme_style_get(obj))) CRI("Failed to set layout!"); evas_object_smart_changed(obj); // ACCESS if ((_elm_config->access_mode != ELM_ACCESS_MODE_OFF)) _access_calendar_spinner_register(obj); // Items for composition for (int i = 0; i < 42; ++i) { priv->items[i] = efl_add(EFL_UI_CALENDAR_ITEM_CLASS, obj, efl_ui_calendar_item_day_number_set(efl_added, i)); } return obj; } EOLIAN static Eo * _efl_ui_calendar_efl_object_constructor(Eo *obj, Efl_Ui_Calendar_Data *sd) { obj = efl_constructor(efl_super(obj, MY_CLASS)); sd->obj = obj; evas_object_smart_callbacks_descriptions_set(obj, _smart_callbacks); efl_access_role_set(obj, EFL_ACCESS_ROLE_DATE_EDITOR); obj = _efl_ui_calendar_constructor_internal(obj, sd); return obj; } EOLIAN static Eina_Bool _efl_ui_calendar_date_min_set(Eo *obj, Efl_Ui_Calendar_Data *sd, Efl_Time min) { Eina_Bool upper = EINA_FALSE; struct tm temp; temp = min; if (mktime(&temp) == -1) { ERR("mktime can not give week day for your minimum date. Please check the date."); return EINA_FALSE; } if ((sd->date_min.tm_year == min.tm_year) && (sd->date_min.tm_mon == min.tm_mon) && (sd->date_min.tm_mday == min.tm_mday)) return EINA_TRUE; if (min.tm_year < 2) { sd->date_min.tm_year = 2; sd->date_min.tm_mon = 0; sd->date_min.tm_mday = 1; } else { if (sd->date_max.tm_year != -1) { if (min.tm_year > sd->date_max.tm_year) { upper = EINA_TRUE; } else if (min.tm_year == sd->date_max.tm_year) { if (min.tm_mon > sd->date_max.tm_mon) upper = EINA_TRUE; else if ((min.tm_mon == sd->date_max.tm_mon) && (min.tm_mday > sd->date_max.tm_mday)) upper = EINA_TRUE; } } if (upper) { sd->date_min.tm_year = sd->date_max.tm_year; sd->date_min.tm_mon = sd->date_max.tm_mon; sd->date_min.tm_mday = sd->date_max.tm_mday; } else { sd->date_min.tm_year = min.tm_year; sd->date_min.tm_mon = min.tm_mon; sd->date_min.tm_mday = min.tm_mday; } } _fix_date(sd); evas_object_smart_changed(obj); if (upper) { ERR("Your minimum date is greater than current maximum date."); return EINA_FALSE; } return EINA_TRUE; } EOLIAN static Efl_Time _efl_ui_calendar_date_min_get(Eo *obj EINA_UNUSED, Efl_Ui_Calendar_Data *sd) { return sd->date_min; } EOLIAN static Eina_Bool _efl_ui_calendar_date_max_set(Eo *obj, Efl_Ui_Calendar_Data *sd, Efl_Time max) { Eina_Bool lower = EINA_FALSE; struct tm temp; temp = max; if (mktime(&temp) == -1) { ERR("mktime can not give week day for your maximum date. Please check the date."); return EINA_FALSE; } if ((sd->date_max.tm_year == max.tm_year) && (sd->date_max.tm_mon == max.tm_mon) && (sd->date_max.tm_mday == max.tm_mday)) return EINA_TRUE; if (max.tm_year < sd->date_min.tm_year) { lower = EINA_TRUE; } else if (max.tm_year == sd->date_min.tm_year) { if (max.tm_mon < sd->date_min.tm_mon) lower = EINA_TRUE; else if ((max.tm_mon == sd->date_min.tm_mon) && (max.tm_mday < sd->date_min.tm_mday)) lower = EINA_TRUE; } if (lower) { sd->date_max.tm_year = sd->date_min.tm_year; sd->date_max.tm_mon = sd->date_min.tm_mon; sd->date_max.tm_mday = sd->date_min.tm_mday; } else { sd->date_max.tm_year = max.tm_year; sd->date_max.tm_mon = max.tm_mon; sd->date_max.tm_mday = max.tm_mday; } _fix_date(sd); evas_object_smart_changed(obj); if (lower) { ERR("Your maximum date is less than current minimum date."); return EINA_FALSE; } return EINA_TRUE; } EOLIAN static Efl_Time _efl_ui_calendar_date_max_get(Eo *obj EINA_UNUSED, Efl_Ui_Calendar_Data *sd) { return sd->date_max; } EOLIAN static Eina_Bool _efl_ui_calendar_date_set(Eo *obj, Efl_Ui_Calendar_Data *sd, Efl_Time date) { Eina_Bool ret = EINA_TRUE; struct tm temp; temp = date; if (mktime(&temp) == -1) { ERR("mktime can not give week day for your new date. Please check the date."); return EINA_FALSE; } sd->date.tm_year = date.tm_year; sd->date.tm_mon = date.tm_mon; sd->date.tm_mday = date.tm_mday; if (!sd->selected) sd->selected = EINA_TRUE; if (sd->date.tm_year != sd->shown_date.tm_year) sd->shown_date.tm_year = sd->date.tm_year; if (sd->date.tm_mon != sd->shown_date.tm_mon) sd->shown_date.tm_mon = sd->date.tm_mon; ret = _fix_date(sd); evas_object_smart_changed(obj); if (!ret) ERR("The current date is greater than the maximum date or less than the minimum date."); return ret; } EOLIAN static Efl_Time _efl_ui_calendar_date_get(Eo *obj EINA_UNUSED, Efl_Ui_Calendar_Data *sd) { return sd->date; } EOLIAN static void _efl_ui_calendar_efl_ui_format_format_cb_set(Eo *obj, Efl_Ui_Calendar_Data *sd, void *func_data, Efl_Ui_Format_Func_Cb func, Eina_Free_Cb func_free_cb) { if ((sd->format_cb_data == func_data) && (sd->format_cb == func)) return; if (sd->format_cb_data && sd->format_free_cb) sd->format_free_cb(sd->format_cb_data); sd->format_cb = func; sd->format_cb_data = func_data; sd->format_free_cb = func_free_cb; if (!sd->format_strbuf) sd->format_strbuf = eina_strbuf_new(); evas_object_smart_changed(obj); } EOLIAN static void _efl_ui_calendar_first_day_of_week_set(Eo *obj, Efl_Ui_Calendar_Data *sd, Efl_Ui_Calendar_Weekday day) { if (day >= EFL_UI_CALENDAR_WEEKDAY_LAST) return; if (sd->first_week_day != day) { sd->first_week_day = day; evas_object_smart_changed(obj); } } EOLIAN static Efl_Ui_Calendar_Weekday _efl_ui_calendar_first_day_of_week_get(Eo *obj EINA_UNUSED, Efl_Ui_Calendar_Data *sd) { return sd->first_week_day; } static void _efl_ui_calendar_class_constructor(Efl_Class *klass) { evas_smart_legacy_type_register(MY_CLASS_NAME, klass); if (_elm_config->access_mode != ELM_ACCESS_MODE_OFF) _efl_ui_calendar_smart_focus_next_enable = EINA_TRUE; } EOLIAN static const Efl_Access_Action_Data* _efl_ui_calendar_efl_access_widget_action_elm_actions_get(Eo *obj EINA_UNUSED, Efl_Ui_Calendar_Data *sd EINA_UNUSED) { static Efl_Access_Action_Data atspi_actions[] = { { "activate", "activate", NULL, _key_action_activate}, { NULL, NULL, NULL, NULL } }; return &atspi_actions[0]; } /* Standard widget overrides */ ELM_WIDGET_KEY_DOWN_DEFAULT_IMPLEMENT(efl_ui_calendar, Efl_Ui_Calendar_Data) /* Internal EO APIs and hidden overrides */ #define EFL_UI_CALENDAR_EXTRA_OPS \ ELM_LAYOUT_SIZING_EVAL_OPS(efl_ui_calendar) #include "efl_ui_calendar.eo.c" typedef struct { int v; Evas_Object *part; } Efl_Ui_Calendar_Item_Data; EOLIAN static void _efl_ui_calendar_item_day_number_set(Eo *obj, Efl_Ui_Calendar_Item_Data *pd, int i) { char pname[14]; Evas_Object *po; pd->v = i; snprintf(pname, sizeof(pname), "cit_%i.access", i); po = (Evas_Object *)edje_object_part_object_get (elm_layout_edje_get(efl_parent_get(obj)), pname); if (_elm_config->access_mode != ELM_ACCESS_MODE_ON) pd->part = po; else pd->part = evas_object_data_get(po, "_part_access_obj"); EINA_SAFETY_ON_NULL_RETURN(pd->part); } EOLIAN static int _efl_ui_calendar_item_day_number_get(Eo *obj EINA_UNUSED, Efl_Ui_Calendar_Item_Data *pd) { return pd->v; } EOLIAN static void _efl_ui_calendar_item_efl_ui_focus_object_focus_set(Eo *obj, Efl_Ui_Calendar_Item_Data *pd, Eina_Bool focus) { efl_ui_focus_object_focus_set(efl_super(obj, EFL_UI_CALENDAR_ITEM_CLASS), focus); _update_focused_it(efl_parent_get(obj), pd->v); evas_object_focus_set(pd->part, efl_ui_focus_object_focus_get(obj)); } EOLIAN static Eina_Rect _efl_ui_calendar_item_efl_ui_focus_object_focus_geometry_get(Eo *obj EINA_UNUSED, Efl_Ui_Calendar_Item_Data *pd) { return efl_gfx_geometry_get(pd->part); } #include "efl_ui_calendar_item.eo.c" diff --git a/src/lib/elementary/efl_ui_calendar.eo b/src/lib/elementary/efl_ui_calendar.eo index 088d2e153e..c5df93758f 100644 --- a/src/lib/elementary/efl_ui_calendar.eo +++ b/src/lib/elementary/efl_ui_calendar.eo @@ -1,119 +1,119 @@ import efl_types; enum Efl.Ui.Calendar.Weekday { [[A weekday See also @Efl.Ui.Calendar.first_day_of_week.set. ]] sunday, [[Sunday weekday]] monday, [[Monday weekday]] tuesday, [[Tusday weekday]] wednesday, [[Wednesday weekday]] thursday, [[Thursday weekday]] friday, [[Friday weekday]] saturday, [[Saturday weekday]] last [[Sentinel value to indicate last enum field during iteration]] } class Efl.Ui.Calendar (Efl.Ui.Layout, Efl.Ui.Focus.Composition, Efl.Access.Widget.Action, Efl.Ui.Format) { [[Calendar widget It helps applications to flexibly display a calendar with day of the week, date, year and month. Applications are able to set specific dates to be reported back, when selected, in the smart callbacks of the calendar widget. ]] methods { @property first_day_of_week { [[The first day of week to use on calendar widgets. This is the day that will appear in the left-most column (eg. Monday in France or Sunday in the US). ]] values { day: Efl.Ui.Calendar.Weekday(Efl.Ui.Calendar.Weekday.sunday); [[The first day of the week.]] } } @property date_min { [[Minimum date on calendar.]] set { [[Set the minimum date on calendar. Set the minimum date, changing the displayed month or year if needed. Displayed day also to be disabled if it is smaller than minimum date. If the minimum date is greater than current maximum date, the minimum date would be changed to the maximum date with returning $false. ]] return: bool; [[$true, on success, $false otherwise]] } get { [[Get the minimum date. Default value is 1 JAN,1902. ]] } values { min: Efl.Time; [[Time structure containing the minmum date.]] } } @property date_max { [[Maximum date on calendar.]] set { [[Set the maximum date on calendar. Set the maximum date, changing the displayed month or year if needed. Displayed day also to be disabled if it is bigger than maximum date. If the maximum date is less than current minimum date, the maximum date would be changed to the minimum date with returning $false. ]] return: bool; [[$true, on success, $false otherwise]] } get { [[Get the maximum date. Default maximum year is -1. Default maximum day and month are 31 and DEC. If the maximum year is a negative value, it will be limited depending on the platform architecture (year 2037 for 32 bits); ]] } values { max: Efl.Time; [[Time structure containing the maximum date.]] } } @property date { [[The selected date on calendar.]] set { [[Set the selected date. If the date is greater than the maximum date, the date would be changed to the maximum date with returning $false. In the opposite case with the minimum date, this would give the same result. ]] return: bool; [[$true, on success, $false otherwise]] } get { } values { date: Efl.Time; [[Time structure containing the selected date.]] } } } implements { class.constructor; Efl.Object.constructor; Efl.Object.destructor; Efl.Canvas.Group.group_calculate; Efl.Ui.Widget.theme_apply; Efl.Ui.Widget.on_access_update; - Efl.Ui.Widget.on_focus_update; + Efl.Ui.Focus.Object.on_focus_update; Efl.Ui.Widget.widget_event; Efl.Access.Widget.Action.elm_actions { get; } Efl.Ui.Format.format_cb { set; } } events { changed; [[Emitted when the selected date in the calendar is changed]] } } diff --git a/src/lib/elementary/efl_ui_clock.c b/src/lib/elementary/efl_ui_clock.c index 885313a339..e133ee96e5 100644 --- a/src/lib/elementary/efl_ui_clock.c +++ b/src/lib/elementary/efl_ui_clock.c @@ -1,1140 +1,1140 @@ #ifdef HAVE_CONFIG_H # include "elementary_config.h" #endif #define EFL_ACCESS_PROTECTED #define EFL_UI_TRANSLATABLE_PROTECTED #include #include "elm_priv.h" #include #include #define MY_CLASS EFL_UI_CLOCK_CLASS #define MY_CLASS_NAME "Efl.Ui.Clock" #define MY_CLASS_NAME_LEGACY "efl_ui_clock" #ifdef HAVE_LOCALE_H # include #endif #ifdef HAVE_LANGINFO_H # include #endif #define MAX_SEPARATOR_LEN 6 #define MIN_DAYS_IN_MONTH 28 #define BUFFER_SIZE 1024 /* interface between EDC & C code (field & signal names). values 0 to * EFL_UI_CLOCK_TYPE_COUNT are in the valid range, and must get in the * place of "%d". */ #define EDC_PART_FIELD_STR "field%d" #define EDC_PART_SEPARATOR_STR "separator%d" #define EDC_PART_FIELD_ENABLE_SIG_STR "field%d,enable" #define EDC_PART_FIELD_DISABLE_SIG_STR "field%d,disable" /* struct tm does not define the fields in the order year, month, * date, hour, minute. values are reassigned to an array for easy * handling. */ #define CLOCK_TM_ARRAY(intptr, tmptr) \ int *intptr[] = { \ &(tmptr)->tm_year, \ &(tmptr)->tm_mon, \ &(tmptr)->tm_mday, \ &(tmptr)->tm_hour, \ &(tmptr)->tm_min, \ &(tmptr)->tm_sec, \ &(tmptr)->tm_wday, \ &(tmptr)->tm_hour} // default limits for individual fields static Format_Map mapping[EFL_UI_CLOCK_TYPE_COUNT] = { [EFL_UI_CLOCK_TYPE_YEAR] = { "Yy", -1, -1, "" }, [EFL_UI_CLOCK_TYPE_MONTH] = { "mbBh", 0, 11, "" }, [EFL_UI_CLOCK_TYPE_DATE] = { "de", 1, 31, "" }, [EFL_UI_CLOCK_TYPE_HOUR] = { "IHkl", 0, 23, "" }, [EFL_UI_CLOCK_TYPE_MINUTE] = { "M", 0, 59, "" }, [EFL_UI_CLOCK_TYPE_SECOND] = { "S", 0, 59, "" }, [EFL_UI_CLOCK_TYPE_DAY] = { "Aa", 0, 6, "" }, [EFL_UI_CLOCK_TYPE_AMPM] = { "pP", 0, 1, "" } }; static const char *multifield_formats = "cxXrRTDF"; static const char *ignore_separators = "()"; static const char *ignore_extensions = "E0_-O^#"; static Clock_Mod_Api *dt_mod = NULL; static const char SIG_CHANGED[] = "changed"; static const Evas_Smart_Cb_Description _smart_callbacks[] = { {SIG_CHANGED, ""}, {SIG_WIDGET_LANG_CHANGED, ""}, /**< handled by elm_widget */ {SIG_WIDGET_ACCESS_CHANGED, ""}, /**< handled by elm_widget */ {SIG_LAYOUT_FOCUSED, ""}, /**< handled by elm_layout */ {SIG_LAYOUT_UNFOCUSED, ""}, /**< handled by elm_layout */ {NULL, NULL} }; static Clock_Mod_Api * _dt_mod_init() { static int tried_fallback = 0; Elm_Module *mod = NULL; if (!(mod = _elm_module_find_as("clock/api"))) { if (!tried_fallback && (!_elm_config->modules || !strstr(_elm_config->modules, "clock/api"))) { // See also _config_update(): we hardcode here the default module ERR("Elementary config does not contain the required module " "name for the clock widget! Verify your installation."); _elm_module_add("clock_input_ctxpopup", "clock/api"); mod = _elm_module_find_as("clock/api"); tried_fallback = EINA_TRUE; } if (!mod) return NULL; } mod->api = malloc(sizeof(Clock_Mod_Api)); if (!mod->api) return NULL; ((Clock_Mod_Api *)(mod->api))->obj_hook = _elm_module_symbol_get(mod, "obj_hook"); ((Clock_Mod_Api *)(mod->api))->obj_unhook = _elm_module_symbol_get(mod, "obj_unhook"); ((Clock_Mod_Api *)(mod->api))->obj_hide = _elm_module_symbol_get(mod, "obj_hide"); ((Clock_Mod_Api *)(mod->api))->field_create = _elm_module_symbol_get(mod, "field_create"); ((Clock_Mod_Api *)(mod->api))->field_value_display = _elm_module_symbol_get(mod, "field_value_display"); return mod->api; } static void _field_list_display(Evas_Object *obj) { Clock_Field *field; unsigned int idx = 0; EFL_UI_CLOCK_DATA_GET(obj, sd); if (!dt_mod || !dt_mod->field_value_display) return; for (idx = 0; idx < EFL_UI_CLOCK_TYPE_COUNT; idx++) { field = sd->field_list + idx; if (field->fmt_exist && field->visible) dt_mod->field_value_display(sd->mod_data, field->item_obj); } } // FIXME: provide nl_langinfo on Windows if possible // returns expanded format string for corresponding multi-field format character static char * _expanded_fmt_str_get(char ch) { char *exp_fmt = ""; switch (ch) { case 'c': #if defined(HAVE_LANGINFO_H) || defined (_WIN32) exp_fmt = nl_langinfo(D_T_FMT); #else exp_fmt = ""; #endif break; case 'x': #if defined(HAVE_LANGINFO_H) || defined (_WIN32) exp_fmt = nl_langinfo(D_FMT); #else exp_fmt = ""; #endif break; case 'X': #if defined(HAVE_LANGINFO_H) || defined (_WIN32) exp_fmt = nl_langinfo(T_FMT); #else exp_fmt = ""; #endif break; case 'r': #if defined(HAVE_LANGINFO_H) || defined (_WIN32) exp_fmt = nl_langinfo(T_FMT_AMPM); #else exp_fmt = ""; #endif break; case 'R': exp_fmt = "%H:%M"; break; case 'T': exp_fmt = "%H:%M:%S"; break; case 'D': exp_fmt = "%m/%d/%y"; break; case 'F': exp_fmt = "%Y-%m-%d"; break; default: exp_fmt = ""; break; } return exp_fmt; } static void _expand_format(char *dt_fmt) { char *ptr, *expanded_fmt, ch; unsigned int idx, len = 0; char buf[EFL_UI_CLOCK_MAX_FORMAT_LEN] = {0, }; Eina_Bool fmt_char, fmt_expanded; do { idx = 0; fmt_char = EINA_FALSE; fmt_expanded = EINA_FALSE; ptr = dt_fmt; while ((ch = *ptr)) { if ((fmt_char) && (strchr(multifield_formats, ch))) { /* replace the multi-field format characters with * corresponding expanded format */ expanded_fmt = _expanded_fmt_str_get(ch); len = strlen(expanded_fmt); if (len > 0) fmt_expanded = EINA_TRUE; buf[--idx] = 0; strncat(buf, expanded_fmt, len); idx += len; } else buf[idx++] = ch; if (ch == '%') fmt_char = EINA_TRUE; else fmt_char = EINA_FALSE; ptr++; } buf[idx] = 0; strncpy(dt_fmt, buf, EFL_UI_CLOCK_MAX_FORMAT_LEN); } while (fmt_expanded); } static void _field_list_arrange(Evas_Object *obj) { Clock_Field *field; char buf[BUFFER_SIZE]; int idx; Eina_Bool freeze; EFL_UI_CLOCK_DATA_GET(obj, sd); freeze = sd->freeze_sizing; sd->freeze_sizing = EINA_TRUE; for (idx = 0; idx < EFL_UI_CLOCK_TYPE_COUNT; idx++) { field = sd->field_list + idx; snprintf(buf, sizeof(buf), EDC_PART_FIELD_STR, field->location); if (field->visible && field->fmt_exist) { evas_object_hide(elm_layout_content_unset(obj, buf)); elm_layout_content_set(obj, buf, field->item_obj); } else evas_object_hide(elm_layout_content_unset(obj, buf)); } sd->freeze_sizing = freeze; elm_layout_sizing_eval(obj); _field_list_display(obj); } static unsigned int _parse_format(Evas_Object *obj, char *fmt_ptr) { Eina_Bool fmt_parsing = EINA_FALSE, sep_parsing = EINA_FALSE, sep_lookup = EINA_FALSE; unsigned int len = 0, idx = 0, location = 0; char separator[MAX_SEPARATOR_LEN]; Clock_Field *field = NULL; char cur; EFL_UI_CLOCK_DATA_GET(obj, sd); while ((cur = *fmt_ptr)) { if (fmt_parsing) { if (strchr(ignore_extensions, cur)) { fmt_ptr++; continue; } fmt_parsing = EINA_FALSE; for (idx = 0; idx < EFL_UI_CLOCK_TYPE_COUNT; idx++) { if (strchr(mapping[idx].fmt_char, cur)) { field = sd->field_list + idx; /* ignore the fields already have or disabled * valid formats, means already parsed & * repeated, ignore. */ if (field->location != -1) break; field->fmt[1] = cur; field->fmt_exist = EINA_TRUE; field->location = location++; sep_lookup = EINA_TRUE; len = 0; break; } } } if (cur == '%') { fmt_parsing = EINA_TRUE; sep_parsing = EINA_FALSE; // set the separator to previous field separator[len] = 0; if (field) eina_stringshare_replace(&field->separator, separator); } // ignore the set of chars (global, field specific) as field separators if (sep_parsing && (len < MAX_SEPARATOR_LEN - 1) && (field->type != EFL_UI_CLOCK_TYPE_AMPM) && (!strchr(ignore_separators, cur)) && (!strchr(mapping[idx].ignore_sep, cur))) separator[len++] = cur; if (sep_lookup) sep_parsing = EINA_TRUE; sep_lookup = EINA_FALSE; fmt_ptr++; } // return the number of valid fields parsed. return location; } static void _reload_format(Evas_Object *obj) { unsigned int idx, field_count; Clock_Field *field; char buf[BUFFER_SIZE]; char *dt_fmt; EFL_UI_CLOCK_DATA_GET(obj, sd); ELM_WIDGET_DATA_GET_OR_RETURN(obj, wd); // FIXME: provide nl_langinfo on Windows if possible // fetch the default format from Libc. if (!sd->user_format) #if defined(HAVE_LANGINFO_H) || defined (_WIN32) strncpy(sd->format, nl_langinfo(D_T_FMT), EFL_UI_CLOCK_MAX_FORMAT_LEN); #else strncpy(sd->format, "", EFL_UI_CLOCK_MAX_FORMAT_LEN); #endif sd->format[EFL_UI_CLOCK_MAX_FORMAT_LEN - 1] = '\0'; dt_fmt = (char *)malloc(EFL_UI_CLOCK_MAX_FORMAT_LEN); if (!dt_fmt) return; strncpy(dt_fmt, sd->format, EFL_UI_CLOCK_MAX_FORMAT_LEN); _expand_format(dt_fmt); // reset all the fields to disable state sd->enabled_field_count = 0; for (idx = 0; idx < EFL_UI_CLOCK_TYPE_COUNT; idx++) { field = sd->field_list + idx; field->fmt_exist = EINA_FALSE; field->location = -1; } field_count = _parse_format(obj, dt_fmt); free(dt_fmt); for (idx = 0; idx < EFL_UI_CLOCK_TYPE_COUNT; idx++) { field = sd->field_list + idx; if (field->fmt_exist && field->visible) sd->enabled_field_count++; } // assign locations to disabled fields for uniform usage for (idx = 0; idx < EFL_UI_CLOCK_TYPE_COUNT; idx++) { field = sd->field_list + idx; if (field->location == -1) field->location = field_count++; if (field->fmt_exist && field->visible) { snprintf(buf, sizeof(buf), EDC_PART_FIELD_ENABLE_SIG_STR, field->location); elm_layout_signal_emit(obj, buf, "elm"); } else { snprintf(buf, sizeof(buf), EDC_PART_FIELD_DISABLE_SIG_STR, field->location); elm_layout_signal_emit(obj, buf, "elm"); } if (field->location + 1) { snprintf(buf, sizeof(buf), EDC_PART_SEPARATOR_STR, (field->location + 1)); elm_layout_text_set(obj, buf, field->separator); } } edje_object_message_signal_process(wd->resize_obj); _field_list_arrange(obj); } EOLIAN static void _efl_ui_clock_efl_ui_translatable_translation_update(Eo *obj, Efl_Ui_Clock_Data *sd) { if (!sd->user_format) _reload_format(obj); else _field_list_display(obj); efl_ui_translatable_translation_update(efl_super(obj, MY_CLASS)); } EOLIAN static void _efl_ui_clock_pause_set(Eo *obj EINA_UNUSED, Efl_Ui_Clock_Data *sd, Eina_Bool paused) { paused = !!paused; if (sd->paused == paused) return; sd->paused = paused; if (paused) ecore_timer_freeze(sd->ticker); else ecore_timer_thaw(sd->ticker); } EOLIAN static Eina_Bool _efl_ui_clock_pause_get(Eo *obj EINA_UNUSED, Efl_Ui_Clock_Data *sd) { return sd->paused; } EOLIAN static void _efl_ui_clock_edit_mode_set(Eo *obj EINA_UNUSED, Efl_Ui_Clock_Data *sd, Eina_Bool edit_mode) { sd->edit_mode = edit_mode; } EOLIAN static Eina_Bool _efl_ui_clock_edit_mode_get(Eo *obj EINA_UNUSED, Efl_Ui_Clock_Data *sd) { return sd->edit_mode; } EOLIAN static Eina_Bool -_efl_ui_clock_efl_ui_widget_on_focus_update(Eo *obj, Efl_Ui_Clock_Data *sd) +_efl_ui_clock_efl_ui_focus_object_on_focus_update(Eo *obj, Efl_Ui_Clock_Data *sd) { Eina_Bool int_ret = EINA_FALSE; - int_ret = efl_ui_widget_on_focus_update(efl_super(obj, MY_CLASS)); + int_ret = efl_ui_focus_object_on_focus_update(efl_super(obj, MY_CLASS)); if (!int_ret) return EINA_FALSE; if (!efl_ui_focus_object_focus_get(obj)) { if ((dt_mod) && (dt_mod->obj_hide)) dt_mod->obj_hide(sd->mod_data); } return EINA_TRUE; } EOLIAN static Eina_Bool _efl_ui_clock_efl_ui_widget_on_disabled_update(Eo *obj, Efl_Ui_Clock_Data *sd, Eina_Bool disabled) { Clock_Field *field; unsigned int idx = 0; if (!efl_ui_widget_on_disabled_update(efl_super(obj, MY_CLASS), disabled)) return EINA_FALSE; for (idx = 0; idx < EFL_UI_CLOCK_TYPE_COUNT; idx++) { field = sd->field_list + idx; elm_object_disabled_set(field->item_obj, disabled); } return EINA_TRUE; } EOLIAN static void _efl_ui_clock_elm_layout_sizing_eval(Eo *obj, Efl_Ui_Clock_Data *sd) { Evas_Coord minw = -1, minh = -1; if (sd->freeze_sizing) return; ELM_WIDGET_DATA_GET_OR_RETURN(obj, wd); if (sd->enabled_field_count) elm_coords_finger_size_adjust(sd->enabled_field_count, &minw, 1, &minh); edje_object_size_min_restricted_calc (wd->resize_obj, &minw, &minh, minw, minh); evas_object_size_hint_min_set(obj, minw, minh); evas_object_size_hint_max_set(obj, -1, -1); } EOLIAN static Efl_Ui_Theme_Apply _efl_ui_clock_efl_ui_widget_theme_apply(Eo *obj, Efl_Ui_Clock_Data *sd) { Efl_Ui_Theme_Apply int_ret = EFL_UI_THEME_APPLY_FAILED; Clock_Field *field; char buf[BUFFER_SIZE]; unsigned int idx; ELM_WIDGET_DATA_GET_OR_RETURN(obj, wd, EINA_FALSE); int_ret = efl_ui_widget_theme_apply(efl_super(obj, MY_CLASS)); if (!int_ret) return EFL_UI_THEME_APPLY_FAILED; if ((!dt_mod) || (!dt_mod->field_value_display)) return EINA_TRUE; for (idx = 0; idx < EFL_UI_CLOCK_TYPE_COUNT; idx++) { field = sd->field_list + idx; // TODO: Different group name for each field_obj may be needed. elm_widget_element_update(obj, field->item_obj, PART_NAME_ARRAY[idx]); if (field->fmt_exist && field->visible) { snprintf(buf, sizeof(buf), EDC_PART_FIELD_ENABLE_SIG_STR, field->location); elm_layout_signal_emit(obj, buf, "elm"); if (field->location) { snprintf(buf, sizeof(buf), EDC_PART_SEPARATOR_STR, (field->location)); elm_layout_text_set(obj, buf, field->separator); } dt_mod->field_value_display(sd->mod_data, field->item_obj); } else { snprintf(buf, sizeof(buf), EDC_PART_FIELD_DISABLE_SIG_STR, field->location); elm_layout_signal_emit(obj, buf, "elm"); } } edje_object_message_signal_process(wd->resize_obj); elm_layout_sizing_eval(obj); return int_ret; } static int _max_days_get(int year, int month) { struct tm time1; time_t t; int day; t = time(NULL); localtime_r(&t, &time1); time1.tm_year = year; time1.tm_mon = month; for (day = MIN_DAYS_IN_MONTH; day <= mapping[EFL_UI_CLOCK_TYPE_DATE].def_max; day++) { time1.tm_mday = day; mktime(&time1); /* To restrict month wrapping because of summer time in some locales, * ignore day light saving mode in mktime(). */ time1.tm_isdst = -1; if (time1.tm_mday == 1) break; } day--; return day; } static Eina_Bool _date_cmp(const struct tm *time1, const struct tm *time2) { unsigned int idx; const CLOCK_TM_ARRAY(timearr1, time1); const CLOCK_TM_ARRAY(timearr2, time2); for (idx = 0; idx < EFL_UI_CLOCK_TYPE_COUNT; idx++) { if (*timearr1[idx] != *timearr2[idx]) return EINA_FALSE; } return EINA_TRUE; } static Eina_Bool _field_cmp(Efl_Ui_Clock_Type field_type, struct tm *time1, struct tm *time2) { CLOCK_TM_ARRAY(timearr1, time1); CLOCK_TM_ARRAY(timearr2, time2); if (*timearr1[field_type] != *timearr2[field_type]) return EINA_FALSE; else return EINA_TRUE; } // validates curr_time/min_limt/max_limit according to the newly set value static void _validate_clock_limits(struct tm *time1, struct tm *time2, Eina_Bool swap) { struct tm *t1, *t2; unsigned int idx; if (!time1 || !time2) return; t1 = (swap) ? time2 : time1; t2 = (swap) ? time1 : time2; CLOCK_TM_ARRAY(timearr1, time1); CLOCK_TM_ARRAY(timearr2, time2); for (idx = 0; idx < EFL_UI_CLOCK_TYPE_COUNT - 1; idx++) { if (*timearr1[idx] < *timearr2[idx]) { memcpy(t1, t2, sizeof(struct tm)); break; } else if (*timearr1[idx] > *timearr2[idx]) break; } } static void _apply_field_limits(Evas_Object *obj) { Clock_Field *field; unsigned int idx = 0; int val; EFL_UI_CLOCK_DATA_GET(obj, sd); CLOCK_TM_ARRAY(timearr, &sd->curr_time); for (idx = 0; idx < EFL_UI_CLOCK_TYPE_COUNT - 1; idx++) { field = sd->field_list + idx; val = *timearr[idx]; if (val < field->min) *timearr[idx] = field->min; else if (val > field->max) *timearr[idx] = field->max; } _field_list_display(obj); } static void _apply_range_restrictions(struct tm *tim) { unsigned int idx; int val, min, max; if (!tim) return; CLOCK_TM_ARRAY(timearr, tim); for (idx = EFL_UI_CLOCK_TYPE_MONTH; idx < EFL_UI_CLOCK_TYPE_COUNT - 1; idx++) { val = *timearr[idx]; min = mapping[idx].def_min; max = mapping[idx].def_max; if (idx == EFL_UI_CLOCK_TYPE_DATE) max = _max_days_get(tim->tm_year, tim->tm_mon); if (val < min) *timearr[idx] = min; else if (val > max) *timearr[idx] = max; } } static const char * _field_format_get(Evas_Object *obj, Efl_Ui_Clock_Type field_type) { Clock_Field *field; if (field_type > EFL_UI_CLOCK_TYPE_DAY) return NULL; EFL_UI_CLOCK_DATA_GET(obj, sd); field = sd->field_list + field_type; return field->fmt; } static void _field_limit_get(Evas_Object *obj, Efl_Ui_Clock_Type field_type, int *range_min, int *range_max) { int min, max, max_days; Clock_Field *field; unsigned int idx; if (field_type > EFL_UI_CLOCK_TYPE_DAY) return; EFL_UI_CLOCK_DATA_GET(obj, sd); field = sd->field_list + field_type; min = field->min; max = field->max; CLOCK_TM_ARRAY(curr_timearr, &sd->curr_time); CLOCK_TM_ARRAY(min_timearr, &sd->min_limit); CLOCK_TM_ARRAY(max_timearr, &sd->max_limit); for (idx = 0; idx < field->type; idx++) if (*curr_timearr[idx] > *min_timearr[idx]) break; if ((idx == field_type) && (min < *min_timearr[field_type])) min = *min_timearr[field_type]; if (field_type == EFL_UI_CLOCK_TYPE_DATE) { max_days = _max_days_get(sd->curr_time.tm_year, sd->curr_time.tm_mon); if (max > max_days) max = max_days; } for (idx = 0; idx < field->type; idx++) if (*curr_timearr[idx] < *max_timearr[idx]) break; if ((idx == field_type) && (max > *max_timearr[field_type])) max = *max_timearr[field_type]; *range_min = min; *range_max = max; } static void _field_list_init(Evas_Object *obj) { Clock_Field *field; unsigned int idx; time_t t; EFL_UI_CLOCK_DATA_GET(obj, sd); t = time(NULL); localtime_r(&t, &sd->curr_time); mapping[EFL_UI_CLOCK_TYPE_YEAR].def_min = _elm_config->year_min; mapping[EFL_UI_CLOCK_TYPE_YEAR].def_max = _elm_config->year_max; for (idx = 0; idx < EFL_UI_CLOCK_TYPE_COUNT; idx++) { field = sd->field_list + idx; field->type = EFL_UI_CLOCK_TYPE_YEAR + idx; field->fmt[0] = '%'; field->fmt_exist = EINA_FALSE; field->visible = EINA_TRUE; field->min = mapping[idx].def_min; field->max = mapping[idx].def_max; } CLOCK_TM_ARRAY(min_timearr, &sd->min_limit); CLOCK_TM_ARRAY(max_timearr, &sd->max_limit); for (idx = 0; idx < EFL_UI_CLOCK_TYPE_COUNT - 1; idx++) { *min_timearr[idx] = mapping[idx].def_min; *max_timearr[idx] = mapping[idx].def_max; } } static char * _access_info_cb(void *data, Evas_Object *obj EINA_UNUSED) { char *ret; Eina_Strbuf *buf; buf = eina_strbuf_new(); EFL_UI_CLOCK_DATA_GET(data, sd); eina_strbuf_append_printf(buf, "%d year, %d month, %d date, %d hour, %d minute", sd->curr_time.tm_year, sd->curr_time.tm_mon + 1, sd->curr_time.tm_mday, sd->curr_time.tm_hour, sd->curr_time.tm_min); ret = eina_strbuf_string_steal(buf); eina_strbuf_free(buf); return ret; } static Eina_Bool _ticker(void *data) { double t; time_t tt; struct timeval timev; Clock_Field *field; EFL_UI_CLOCK_DATA_GET(data, sd); tt = time(NULL); localtime_r(&tt, &sd->curr_time); if (sd->curr_time.tm_sec > 0) { field = sd->field_list + EFL_UI_CLOCK_TYPE_SECOND; if (field->fmt_exist && field->visible && dt_mod && dt_mod->field_value_display) dt_mod->field_value_display(sd->mod_data, field->item_obj); } else _field_list_display(data); gettimeofday(&timev, NULL); t = ((double)(1000000 - timev.tv_usec)) / 1000000.0; sd->ticker = ecore_timer_add(t, _ticker, data); return ECORE_CALLBACK_CANCEL; } EOLIAN static void _efl_ui_clock_efl_canvas_group_group_add(Eo *obj, Efl_Ui_Clock_Data *priv) { Clock_Field *field; int idx; ELM_WIDGET_DATA_GET_OR_RETURN(obj, wd); if (!elm_widget_theme_klass_get(obj)) elm_widget_theme_klass_set(obj, "uiclock"); efl_canvas_group_add(efl_super(obj, MY_CLASS)); elm_widget_sub_object_parent_add(obj); if (!elm_widget_theme_object_set(obj, wd->resize_obj, elm_widget_theme_klass_get(obj), elm_widget_theme_element_get(obj), elm_widget_theme_style_get(obj))) CRI("Failed to set layout!"); // module - initialise module for clock if (!dt_mod) dt_mod = _dt_mod_init(); if (dt_mod) { if (dt_mod->obj_hook) { priv->mod_data = dt_mod->obj_hook(obj); // update module data if (priv->mod_data) { priv->mod_data->base = obj; priv->mod_data->field_limit_get = _field_limit_get; priv->mod_data->field_format_get = _field_format_get; } } if (dt_mod->field_create) { for (idx = 0; idx < EFL_UI_CLOCK_TYPE_COUNT; idx++) { field = priv->field_list + idx; field->item_obj = dt_mod->field_create(priv->mod_data, idx); } } } else WRN("Failed to load clock module, clock widget may not show properly!"); priv->freeze_sizing = EINA_TRUE; _field_list_init(obj); _reload_format(obj); _ticker(obj); elm_widget_can_focus_set(obj, EINA_TRUE); priv->freeze_sizing = EINA_FALSE; elm_layout_sizing_eval(obj); // ACCESS if (_elm_config->access_mode == ELM_ACCESS_MODE_ON) { Elm_Access_Info *ai; priv->access_obj = _elm_access_edje_object_part_object_register (obj, elm_layout_edje_get(obj), "elm.access"); if (!priv->access_obj) priv->access_obj = _elm_access_edje_object_part_object_register (obj, elm_layout_edje_get(obj), "access"); ai = _elm_access_info_get(priv->access_obj); _elm_access_text_set(ai, ELM_ACCESS_TYPE, "date time"); _elm_access_callback_set(ai, ELM_ACCESS_INFO, _access_info_cb, obj); } } EOLIAN static void _efl_ui_clock_efl_canvas_group_group_del(Eo *obj, Efl_Ui_Clock_Data *sd) { Clock_Field *tmp; unsigned int idx; ecore_timer_del(sd->ticker); for (idx = 0; idx < EFL_UI_CLOCK_TYPE_COUNT; idx++) { tmp = sd->field_list + idx; evas_object_del(tmp->item_obj); eina_stringshare_del(tmp->separator); } if ((dt_mod) && (dt_mod->obj_unhook)) dt_mod->obj_unhook(sd->mod_data); // module - unhook efl_canvas_group_del(efl_super(obj, MY_CLASS)); } EOLIAN static Eo * _efl_ui_clock_efl_object_constructor(Eo *obj, Efl_Ui_Clock_Data *_pd EINA_UNUSED) { obj = efl_constructor(efl_super(obj, MY_CLASS)); efl_canvas_object_type_set(obj, MY_CLASS_NAME_LEGACY); evas_object_smart_callbacks_descriptions_set(obj, _smart_callbacks); efl_access_role_set(obj, EFL_ACCESS_ROLE_DATE_EDITOR); return obj; } EOLIAN static const char* _efl_ui_clock_format_get(Eo *obj EINA_UNUSED, Efl_Ui_Clock_Data *sd) { return sd->format; } EOLIAN static void _efl_ui_clock_format_set(Eo *obj, Efl_Ui_Clock_Data *sd, const char *fmt) { if (fmt) { strncpy(sd->format, fmt, EFL_UI_CLOCK_MAX_FORMAT_LEN); sd->format[EFL_UI_CLOCK_MAX_FORMAT_LEN - 1] = '\0'; sd->user_format = EINA_TRUE; } else sd->user_format = EINA_FALSE; _reload_format(obj); } EOLIAN static Eina_Bool _efl_ui_clock_field_visible_get(Eo *obj EINA_UNUSED, Efl_Ui_Clock_Data *sd, Efl_Ui_Clock_Type fieldtype) { Clock_Field *field; if (fieldtype > EFL_UI_CLOCK_TYPE_DAY) return EINA_FALSE; field = sd->field_list + fieldtype; return field->visible; } EOLIAN static void _efl_ui_clock_field_visible_set(Eo *obj, Efl_Ui_Clock_Data *sd, Efl_Ui_Clock_Type fieldtype, Eina_Bool visible) { char buf[BUFFER_SIZE]; Clock_Field *field; if (fieldtype > EFL_UI_CLOCK_TYPE_DAY) return; field = sd->field_list + fieldtype; visible = !!visible; if (field->visible == visible) return; field->visible = visible; sd->freeze_sizing = EINA_TRUE; if (visible) { sd->enabled_field_count++; if (!field->fmt_exist) return; snprintf(buf, sizeof(buf), EDC_PART_FIELD_ENABLE_SIG_STR, field->location); elm_layout_signal_emit(obj, buf, "elm"); ELM_WIDGET_DATA_GET_OR_RETURN(obj, wd); edje_object_message_signal_process(wd->resize_obj); snprintf(buf, sizeof(buf), EDC_PART_FIELD_STR, field->location); elm_layout_content_unset(obj, buf); elm_layout_content_set(obj, buf, field->item_obj); } else { sd->enabled_field_count--; if (!field->fmt_exist) return; snprintf(buf, sizeof(buf), EDC_PART_FIELD_DISABLE_SIG_STR, field->location); elm_layout_signal_emit(obj, buf, "elm"); ELM_WIDGET_DATA_GET_OR_RETURN(obj, wd); edje_object_message_signal_process(wd->resize_obj); snprintf(buf, sizeof(buf), EDC_PART_FIELD_STR, field->location); evas_object_hide(elm_layout_content_unset(obj, buf)); } sd->freeze_sizing = EINA_FALSE; elm_layout_sizing_eval(obj); if (!visible) return; if (!dt_mod || !dt_mod->field_value_display) return; dt_mod->field_value_display(sd->mod_data, field->item_obj); } EOLIAN static void _efl_ui_clock_field_limit_get(Eo *obj EINA_UNUSED, Efl_Ui_Clock_Data *sd, Efl_Ui_Clock_Type fieldtype, int *min, int *max) { Clock_Field *field; if (fieldtype >= EFL_UI_CLOCK_TYPE_DAY) return; field = sd->field_list + fieldtype; if (min) *min = field->min; if (max) *max = field->max; } EOLIAN static void _efl_ui_clock_field_limit_set(Eo *obj, Efl_Ui_Clock_Data *sd, Efl_Ui_Clock_Type fieldtype, int min, int max) { Clock_Field *field; struct tm old_time; if (fieldtype >= EFL_UI_CLOCK_TYPE_DAY) return; if (min > max) return; old_time = sd->curr_time; field = sd->field_list + fieldtype; if (((min >= mapping[fieldtype].def_min) && (min <= mapping[fieldtype].def_max)) || (field->type == EFL_UI_CLOCK_TYPE_YEAR)) field->min = min; if (((max >= mapping[fieldtype].def_min) && (max <= mapping[fieldtype].def_max)) || (field->type == EFL_UI_CLOCK_TYPE_YEAR)) field->max = max; _apply_field_limits(obj); if (!_field_cmp(fieldtype, &old_time, &sd->curr_time)) efl_event_callback_legacy_call(obj, EFL_UI_CLOCK_EVENT_CHANGED, NULL); } EOLIAN static Efl_Time _efl_ui_clock_time_get(Eo *obj EINA_UNUSED, Efl_Ui_Clock_Data *sd) { return sd->curr_time; } EOLIAN static void _efl_ui_clock_time_set(Eo *obj, Efl_Ui_Clock_Data *sd, Efl_Time newtime) { if (_date_cmp(&sd->curr_time, &newtime)) return; sd->curr_time = newtime; // apply default field restrictions for curr_time _apply_range_restrictions(&sd->curr_time); // validate the curr_time according to the min_limt and max_limt _validate_clock_limits(&sd->curr_time, &sd->min_limit, EINA_FALSE); _validate_clock_limits(&sd->max_limit, &sd->curr_time, EINA_TRUE); _apply_field_limits(obj); efl_event_callback_legacy_call(obj, EFL_UI_CLOCK_EVENT_CHANGED, NULL); } EOLIAN static Efl_Time _efl_ui_clock_time_min_get(Eo *obj EINA_UNUSED, Efl_Ui_Clock_Data *sd) { return sd->min_limit; } EOLIAN static void _efl_ui_clock_time_min_set(Eo *obj, Efl_Ui_Clock_Data *sd, Efl_Time mintime) { struct tm old_time; if (_date_cmp(&sd->min_limit, &mintime)) return; sd->min_limit = mintime; old_time = sd->curr_time; // apply default field restrictions for min_limit _apply_range_restrictions(&sd->min_limit); // validate curr_time and max_limt according to the min_limit _validate_clock_limits(&sd->max_limit, &sd->min_limit, EINA_FALSE); _validate_clock_limits(&sd->curr_time, &sd->min_limit, EINA_FALSE); _apply_field_limits(obj); if (!_date_cmp(&old_time, &sd->curr_time)) efl_event_callback_legacy_call(obj, EFL_UI_CLOCK_EVENT_CHANGED, NULL); } EOLIAN static Efl_Time _efl_ui_clock_time_max_get(Eo *obj EINA_UNUSED, Efl_Ui_Clock_Data *sd) { return sd->max_limit; } EOLIAN static void _efl_ui_clock_time_max_set(Eo *obj, Efl_Ui_Clock_Data *sd, Efl_Time maxtime) { struct tm old_time; if (_date_cmp(&sd->max_limit, &maxtime)) return; sd->max_limit = maxtime; old_time = sd->curr_time; // apply default field restrictions for max_limit _apply_range_restrictions(&sd->max_limit); // validate curr_time and min_limt according to the max_limit _validate_clock_limits(&sd->max_limit, &sd->min_limit, EINA_TRUE); _validate_clock_limits(&sd->max_limit, &sd->curr_time, EINA_TRUE); _apply_field_limits(obj); if (!_date_cmp(&old_time, &sd->curr_time)) efl_event_callback_legacy_call(obj, EFL_UI_CLOCK_EVENT_CHANGED, NULL); } EOLIAN static void _efl_ui_clock_class_constructor(Efl_Class *klass) { evas_smart_legacy_type_register(MY_CLASS_NAME_LEGACY, klass); } /* Internal EO APIs and hidden overrides */ #define EFL_UI_CLOCK_EXTRA_OPS \ ELM_LAYOUT_SIZING_EVAL_OPS(efl_ui_clock), \ EFL_CANVAS_GROUP_ADD_DEL_OPS(efl_ui_clock) #include "efl_ui_clock.eo.c" diff --git a/src/lib/elementary/efl_ui_clock.eo b/src/lib/elementary/efl_ui_clock.eo index 3211962e84..795afd44dd 100644 --- a/src/lib/elementary/efl_ui_clock.eo +++ b/src/lib/elementary/efl_ui_clock.eo @@ -1,235 +1,235 @@ import efl_types; enum Efl.Ui.Clock.Type { [[Identifies a clock field, The widget supports 6 fields : Year, month, Date, Hour, Minute, AM/PM ]] year = 0, [[Indicates Year field.]] month = 1, [[Indicates Month field.]] date = 2, [[Indicates Date field.]] hour = 3, [[Indicates Hour field.]] minute = 4, [[Indicates Minute field.]] second = 5, [[Indicates Second field.]] day = 6, [[Indicated Day field.]] ampm = 7, [[Indicates AM/PM field .]] } class Efl.Ui.Clock (Efl.Ui.Layout) { [[Efl UI clock class]] methods { @property format { [[The current clock format. Format is a combination of allowed Libc date format specifiers like: "%b %d, %Y %I : %M %p". Maximum allowed format length is 64 chars. Format can include separators for each individual clock field except for AM/PM field. Each separator can be a maximum of 6 UTF-8 bytes. Space is also taken as a separator. These specifiers can be arranged in any order and the widget will display the fields accordingly. Default format is taken as per the system locale settings. ]] /* FIXME-doc Following are the allowed set of format specifiers for each clock field. @b %%Y : The year as a decimal number including the century. @b %%y : The year as a decimal number without a century (range 00 to 99). @b %%m : The month as a decimal number (range 01 to 12). @b %%b : The abbreviated month name according to the current locale. @b %%B : The full month name according to the current locale. @b %%h : The abbreviated month name according to the current locale(same as %%b). @b %%d : The day of the month as a decimal number (range 01 to 31). @b %%e : The day of the month as a decimal number (range 1 to 31). single digits are preceded by a blank. @b %%I : The hour as a decimal number using a 12-hour clock (range 01 to 12). @b %%H : The hour as a decimal number using a 24-hour clock (range 00 to 23). @b %%k : The hour (24-hour clock) as a decimal number (range 0 to 23). single digits are preceded by a blank. @b %%l : The hour (12-hour clock) as a decimal number (range 1 to 12); single digits are preceded by a blank. @b %%M : The minute as a decimal number (range 00 to 59). @b %%p : Either 'AM' or 'PM' according to the given time value, or the corresponding strings for the current locale. Noon is treated as 'PM' and midnight as 'AM'. @b %%P : Like %p but in lower case: 'am' or 'pm' or a corresponding string for the current locale. @b %%c : The preferred date and time representation for the current locale. @b %%x : The preferred date representation for the current locale without the time. @b %%X : The preferred time representation for the current locale without the date. @b %%r : The complete calendar time using the AM/PM format of the current locale. @b %%R : The hour and minute in decimal numbers using the format %H:%M. @b %%T : The time of day in decimal numbers using the format %H:%M:%S. @b %%D : The date using the format %%m/%%d/%%y. @b %%F : The date using the format %%Y-%%m-%%d. */ set {} get {} values { fmt: string @nullable; [[The clock format.]] } } @property pause { [[Whether the given clock widget should be paused or not. This function pauses or starts the clock widget. ]] set {} get {} values { paused: bool; [[$true to pause clock, $false otherwise]] } } @property edit_mode { [[Digits of the given clock widget should be editable when in editing mode.]] set {} get {} values { value: bool; [[$true to set edit mode, $false otherwise]] } } @property time_min { [[The lower boundary of a field. Year: years since 1900. Negative value represents year below 1900 (year value -30 represents 1870). Year default range is from 70 to 137. Month: default value range is from 0 to 11. Date: default value range is from 1 to 31 according to the month value. Hour: default value will be in terms of 24 hr format (0~23) Minute: default value range is from 0 to 59. ]] values { mintime: Efl.Time; [[Time structure containing the minimum time value.]] } } @property time_max { [[The upper boundary of a field. Year: years since 1900. Negative value represents year below 1900 (year value -30 represents 1870). Year default range is from 70 to 137. Month: default value range is from 0 to 11. Date: default value range is from 1 to 31 according to the month value. Hour: default value will be in terms of 24 hr format (0~23) Minute: default value range is from 0 to 59. ]] values { maxtime: Efl.Time; [[Time structure containing the maximum time value.]] } } @property time { [[The current value of a clock object. Year: years since 1900. Negative value represents year below 1900 (year value -30 represents 1870). Year default range is from 70 to 137. Month: default value range is from 0 to 11. Date: default value range is from 1 to 31 according to the month value. Hour: default value will be in terms of 24 hr format (0~23) Minute: default value range is from 0 to 59. ]] values { curtime: Efl.Time; [[Time structure containing the time value.]] } } @property field_visible { [[ The field to be visible/not.]] set{} get{} keys { fieldtype: Efl.Ui.Clock.Type; [[Type of the field. #EFL_UI_CLOCK_TYPE_YEAR etc.]] } values { visible: bool; [[$true field can be visible, $false otherwise.]] } } @property field_limit { set { [[Set a field to be visible or not. Setting this API to $true in itself doen't ensure that the field is visible. The field's format also must be present in the overall clock format. If a field's visibility is set to $false then it won't appear even though its format is present. In summary, if this API is set to true and the corresponding field's format is present in clock format, the field is visible. By default the field visibility is set to $true. ]] } get { [[ Get the field limits of a field. Limits can be set to individual fields, independently, except for the AM/PM field. Any field can display the values only in between these minimum and maximum limits unless the corresponding time value is restricted from MinTime to MaxTime. That is, min/max field limits always work under the limitations of mintime/maxtime. There is no provision to set the limits of AM/PM field. ]] } keys { fieldtype: Efl.Ui.Clock.Type; [[Type of the field. #EFL_UI_CLOCK_TYPE_YEAR etc.]] } values { min: int; [[Reference to field's minimum value.]] max: int; [[Reference to field's maximum value.]] } } } implements { class.constructor; Efl.Object.constructor; Efl.Ui.Widget.theme_apply; Efl.Ui.Widget.on_disabled_update; - Efl.Ui.Widget.on_focus_update; + Efl.Ui.Focus.Object.on_focus_update; Efl.Ui.Translatable.translation_update; } events { changed; [[Called when clock changed]] } } diff --git a/src/lib/elementary/efl_ui_focus_object.eo b/src/lib/elementary/efl_ui_focus_object.eo index 6842dfabc7..a8ce1130b8 100644 --- a/src/lib/elementary/efl_ui_focus_object.eo +++ b/src/lib/elementary/efl_ui_focus_object.eo @@ -1,78 +1,81 @@ import eina_types; mixin Efl.Ui.Focus.Object { [[Functions of focusable objects. @since 1.20 ]] methods { @property focus_geometry { [[The geometry used to calculate relationships between other objects.]] get { } values { rect : Eina.Rect; [[The geometry to use.]] } } @property focus { [[This is called by the manager and should never be called by anyone else. It can be used by configuring a focus object to adapt to any changes that are required. The function emits the focus state events, if focus is different to the previous state. ]] get { } set @protected { } values { focus : bool; [[The state in which the object should be put]] } } @property focus_manager { [[Describes which manager is used to register. If an instance of this interface is the root of a manager, this instance should not have a manager where as root of this property. The other manager in this instance will be set as focused in the corresponding manager. This instance should be registered with its own manager as redirect. ]] get {} values { manager : Efl.Ui.Focus.Manager; [[The manager object]] } } @property focus_parent { [[Describes which logical parent is used by this object.]] get {} values { logical_parent : Efl.Ui.Focus.Object; [[The focus parent.]] } } - prepare_logical { [[Tells the object that its children will be queried soon by the given manager. Deleting manager items in this call will result in undefied behaviour and may cause your system to crash. ]] } + on_focus_update @protected @pure_virtual { + [[Virtual function handling focus in/out events on the widget]] + return: bool; [[$true if this widget can handle focus, $false otherwise]] + } } implements { @empty .focus_geometry; @empty .prepare_logical; @empty .focus_manager; @empty .focus_parent; } events { focus,changed : bool; [[Emitted if the focus state has changed]] manager,changed: Efl.Ui.Focus.Manager; [[Emitted when a new manager is the parent for this object.]] logical,changed: Efl.Ui.Focus.Object; [[Emitted when a new logical parent should be used.]] } } diff --git a/src/lib/elementary/efl_ui_image_zoomable.c b/src/lib/elementary/efl_ui_image_zoomable.c index d4a184e2c7..c3d0f5e2d5 100644 --- a/src/lib/elementary/efl_ui_image_zoomable.c +++ b/src/lib/elementary/efl_ui_image_zoomable.c @@ -1,3235 +1,3235 @@ #ifdef HAVE_CONFIG_H # include "elementary_config.h" #endif #define EFL_ACCESS_PROTECTED #define EFL_ACCESS_WIDGET_ACTION_PROTECTED #define EFL_UI_SCROLL_MANAGER_PROTECTED #define EFL_UI_SCROLLBAR_PROTECTED #define EFL_UI_SCROLLBAR_BETA #include #include "elm_priv.h" #include "efl_ui_image_zoomable_private.h" #define MY_PAN_CLASS EFL_UI_IMAGE_ZOOMABLE_PAN_CLASS #define MY_PAN_CLASS_NAME "Efl.Ui.Image_Zoomable_Pan" #define MY_PAN_CLASS_NAME_LEGACY "elm_photocam_pan" #define MY_CLASS EFL_UI_IMAGE_ZOOMABLE_CLASS #define MY_CLASS_NAME "Efl.Ui.Image_Zoomable" #define MY_CLASS_NAME_LEGACY "elm_photocam" /* * TODO (maybe - optional future stuff): * * 1. wrap photo in theme edje so u can have styling around photo (like white * photo bordering). * 2. exif handling * 3. rotation flags in exif handling (nasty! should have rot in evas) */ static const char SIG_CLICKED[] = "clicked"; static const char SIG_PRESS[] = "press"; static const char SIG_LONGPRESSED[] = "longpressed"; static const char SIG_CLICKED_DOUBLE[] = "clicked,double"; static const char SIG_LOAD[] = "load"; static const char SIG_LOADED[] = "loaded"; static const char SIG_LOAD_DETAIL[] = "load,detail"; static const char SIG_LOADED_DETAIL[] = "loaded,detail"; static const char SIG_ZOOM_START[] = "zoom,start"; static const char SIG_ZOOM_STOP[] = "zoom,stop"; static const char SIG_ZOOM_CHANGE[] = "zoom,change"; static const char SIG_SCROLL[] = "scroll"; static const char SIG_SCROLL_ANIM_START[] = "scroll,anim,start"; static const char SIG_SCROLL_ANIM_STOP[] = "scroll,anim,stop"; static const char SIG_SCROLL_DRAG_START[] = "scroll,drag,start"; static const char SIG_SCROLL_DRAG_STOP[] = "scroll,drag,stop"; static const char SIG_DOWNLOAD_START[] = "download,start"; static const char SIG_DOWNLOAD_PROGRESS[] = "download,progress"; static const char SIG_DOWNLOAD_DONE[] = "download,done"; static const char SIG_DOWNLOAD_ERROR[] = "download,error"; static const Evas_Smart_Cb_Description _smart_callbacks[] = { {SIG_CLICKED, ""}, {SIG_PRESS, ""}, {SIG_LONGPRESSED, ""}, {SIG_CLICKED_DOUBLE, ""}, {SIG_LOAD, ""}, {SIG_LOADED, ""}, {SIG_LOAD_DETAIL, ""}, {SIG_LOADED_DETAIL, ""}, {SIG_ZOOM_START, ""}, {SIG_ZOOM_STOP, ""}, {SIG_ZOOM_CHANGE, ""}, {SIG_SCROLL, ""}, {SIG_SCROLL_ANIM_START, ""}, {SIG_SCROLL_ANIM_STOP, ""}, {SIG_SCROLL_DRAG_START, ""}, {SIG_SCROLL_DRAG_STOP, ""}, {SIG_DOWNLOAD_START, ""}, {SIG_DOWNLOAD_PROGRESS, ""}, {SIG_DOWNLOAD_DONE, ""}, {SIG_DOWNLOAD_ERROR, ""}, {SIG_WIDGET_FOCUSED, ""}, /**< handled by elm_widget */ {SIG_WIDGET_UNFOCUSED, ""}, /**< handled by elm_widget */ {NULL, NULL} }; static Eina_Error PHOTO_FILE_LOAD_ERROR_GENERIC; static Eina_Error PHOTO_FILE_LOAD_ERROR_DOES_NOT_EXIST; static Eina_Error PHOTO_FILE_LOAD_ERROR_PERMISSION_DENIED; static Eina_Error PHOTO_FILE_LOAD_ERROR_RESOURCE_ALLOCATION_FAILED; static Eina_Error PHOTO_FILE_LOAD_ERROR_CORRUPT_FILE; static Eina_Error PHOTO_FILE_LOAD_ERROR_UNKNOWN_FORMAT; static Eina_Bool _key_action_move(Evas_Object *obj, const char *params); static Eina_Bool _key_action_zoom(Evas_Object *obj, const char *params); static void _efl_ui_image_zoomable_remote_copier_cancel(Eo *obj, Efl_Ui_Image_Zoomable_Data *sd); static Eina_Bool _internal_efl_ui_image_zoomable_icon_set(Evas_Object *obj, const char *name, Eina_Bool *fdo, Eina_Bool resize); static void _min_obj_size_get(Evas_Object *o, int *w, int *h); static const Elm_Action key_actions[] = { {"move", _key_action_move}, {"zoom", _key_action_zoom}, {NULL, NULL} }; static const char *icon_theme = NULL; #define NON_EXISTING (void *)-1 static inline void _photocam_image_file_set(Evas_Object *obj, Efl_Ui_Image_Zoomable_Data *sd) { if (sd->f) evas_object_image_mmap_set(obj, sd->f, NULL); else evas_object_image_file_set(obj, sd->file, NULL); } static void _sizing_eval(Evas_Object *obj) { Evas_Coord minw = 0, minh = 0, maxw = -1, maxh = -1; ELM_WIDGET_DATA_GET_OR_RETURN(obj, wd); evas_object_size_hint_max_get (wd->resize_obj, &maxw, &maxh); evas_object_size_hint_min_set(obj, minw, minh); evas_object_size_hint_max_set(obj, maxw, maxh); } static void _calc_job_cb(void *data) { Evas_Object *obj = data; EFL_UI_IMAGE_ZOOMABLE_DATA_GET(obj, sd); Evas_Coord minw, minh; minw = sd->size.w; minh = sd->size.h; if (sd->resized) { sd->resized = EINA_FALSE; if (sd->mode != ELM_PHOTOCAM_ZOOM_MODE_MANUAL) { double tz = sd->zoom; sd->zoom = 0.0; elm_photocam_zoom_set(obj, tz); } } if ((minw != sd->minw) || (minh != sd->minh)) { sd->minw = minw; sd->minh = minh; efl_event_callback_call(sd->pan_obj, EFL_UI_PAN_EVENT_CONTENT_CHANGED, NULL); _sizing_eval(obj); } sd->calc_job = NULL; evas_object_smart_changed(sd->pan_obj); } EOLIAN static void _efl_ui_image_zoomable_pan_efl_gfx_position_set(Eo *obj, Efl_Ui_Image_Zoomable_Pan_Data *psd, Eina_Position2D pos) { if (_evas_object_intercept_call(obj, EVAS_OBJECT_INTERCEPT_CB_MOVE, 0, pos.x, pos.y)) return; efl_gfx_position_set(efl_super(obj, MY_PAN_CLASS), pos); ecore_job_del(psd->wsd->calc_job); psd->wsd->calc_job = ecore_job_add(_calc_job_cb, psd->wobj); } EOLIAN static void _efl_ui_image_zoomable_pan_efl_gfx_size_set(Eo *obj, Efl_Ui_Image_Zoomable_Pan_Data *psd, Eina_Size2D sz) { if (_evas_object_intercept_call(obj, EVAS_OBJECT_INTERCEPT_CB_RESIZE, 0, sz.w, sz.h)) return; efl_gfx_size_set(efl_super(obj, MY_PAN_CLASS), sz); psd->wsd->resized = EINA_TRUE; ecore_job_del(psd->wsd->calc_job); psd->wsd->calc_job = ecore_job_add(_calc_job_cb, psd->wobj); } static void _image_place(Evas_Object *obj, Evas_Coord px, Evas_Coord py, Evas_Coord ox, Evas_Coord oy, Evas_Coord ow, Evas_Coord oh) { Evas_Coord ax, ay, gw, gh; EFL_UI_IMAGE_ZOOMABLE_DATA_GET(obj, sd); ax = 0; ay = 0; gw = sd->size.w; gh = sd->size.h; if (!sd->zoom_g_layer) { if (ow > gw) ax = (ow - gw) / 2; if (oh > gh) ay = (oh - gh) / 2; } evas_object_move(sd->img, ox + 0 - px + ax, oy + 0 - py + ay); evas_object_resize(sd->img, gw, gh); if (sd->show_item) { sd->show_item = EINA_FALSE; efl_ui_scrollable_scroll (sd->smanager, sd->show, EINA_FALSE); } } static void _grid_load(Evas_Object *obj, Efl_Ui_Image_Zoomable_Grid *g) { int x, y; Evas_Coord ox, oy, ow, oh, cvx, cvy, cvw, cvh, gw, gh, tx, ty; EFL_UI_IMAGE_ZOOMABLE_DATA_GET(obj, sd); ELM_WIDGET_DATA_GET_OR_RETURN(obj, wd); evas_object_geometry_get(sd->pan_obj, &ox, &oy, &ow, &oh); evas_output_viewport_get(evas_object_evas_get(obj), &cvx, &cvy, &cvw, &cvh); gw = sd->size.w; gh = sd->size.h; for (y = 0; y < g->gh; y++) { for (x = 0; x < g->gw; x++) { int tn, xx, yy, ww, hh; Eina_Bool visible = EINA_FALSE; tn = (y * g->gw) + x; xx = g->grid[tn].out.x; yy = g->grid[tn].out.y; ww = g->grid[tn].out.w; hh = g->grid[tn].out.h; if ((gw != g->w) && (g->w > 0)) { tx = xx; xx = (gw * xx) / g->w; ww = ((gw * (tx + ww)) / g->w) - xx; } if ((gh != g->h) && (g->h > 0)) { ty = yy; yy = (gh * yy) / g->h; hh = ((gh * (ty + hh)) / g->h) - yy; } if (ELM_RECTS_INTERSECT(xx - sd->pan_x + ox, yy - sd->pan_y + oy, ww, hh, cvx, cvy, cvw, cvh)) visible = EINA_TRUE; if ((visible) && (!g->grid[tn].have) && (!g->grid[tn].want)) { g->grid[tn].want = 1; evas_object_hide(g->grid[tn].img); evas_object_image_file_set(g->grid[tn].img, NULL, NULL); evas_object_image_load_scale_down_set (g->grid[tn].img, g->zoom); evas_object_image_load_region_set (g->grid[tn].img, g->grid[tn].src.x, g->grid[tn].src.y, g->grid[tn].src.w, g->grid[tn].src.h); _photocam_image_file_set(g->grid[tn].img, sd); evas_object_image_preload(g->grid[tn].img, 0); sd->preload_num++; if (sd->preload_num == 1) { edje_object_signal_emit (wd->resize_obj, "elm,state,busy,start", "elm"); efl_event_callback_legacy_call (obj, EFL_UI_IMAGE_ZOOMABLE_EVENT_LOAD_DETAIL, NULL); } } else if ((g->grid[tn].want) && (!visible)) { sd->preload_num--; if (!sd->preload_num) { edje_object_signal_emit (wd->resize_obj, "elm,state,busy,stop", "elm"); efl_event_callback_legacy_call (obj, EFL_UI_IMAGE_ZOOMABLE_EVENT_LOADED_DETAIL, NULL); } g->grid[tn].want = 0; evas_object_hide(g->grid[tn].img); evas_object_image_preload(g->grid[tn].img, 1); evas_object_image_file_set(g->grid[tn].img, NULL, NULL); } else if ((g->grid[tn].have) && (!visible)) { g->grid[tn].have = 0; evas_object_hide(g->grid[tn].img); evas_object_image_preload(g->grid[tn].img, 1); evas_object_image_file_set(g->grid[tn].img, NULL, NULL); } } } } static void _grid_place(Evas_Object *obj, Efl_Ui_Image_Zoomable_Grid *g, Evas_Coord px, Evas_Coord py, Evas_Coord ox, Evas_Coord oy, Evas_Coord ow, Evas_Coord oh) { Evas_Coord ax, ay, gw, gh, tx, ty; int x, y; EFL_UI_IMAGE_ZOOMABLE_DATA_GET(obj, sd); ax = 0; ay = 0; gw = sd->size.w; gh = sd->size.h; if (!sd->zoom_g_layer) { if (ow > gw) ax = (ow - gw) / 2; if (oh > gh) ay = (oh - gh) / 2; } for (y = 0; y < g->gh; y++) { for (x = 0; x < g->gw; x++) { int tn, xx, yy, ww, hh; tn = (y * g->gw) + x; xx = g->grid[tn].out.x; yy = g->grid[tn].out.y; ww = g->grid[tn].out.w; hh = g->grid[tn].out.h; if ((gw != g->w) && (g->w > 0)) { tx = xx; xx = (gw * xx) / g->w; ww = ((gw * (tx + ww)) / g->w) - xx; } if ((gh != g->h) && (g->h > 0)) { ty = yy; yy = (gh * yy) / g->h; hh = ((gh * (ty + hh)) / g->h) - yy; } evas_object_move(g->grid[tn].img, ox + xx - px + ax, oy + yy - py + ay); evas_object_resize(g->grid[tn].img, ww, hh); } } } EOLIAN static void _efl_ui_image_zoomable_pan_efl_canvas_group_group_calculate(Eo *obj, Efl_Ui_Image_Zoomable_Pan_Data *psd) { Efl_Ui_Image_Zoomable_Grid *g; Eina_List *l; Evas_Coord ox, oy, ow, oh; ELM_WIDGET_DATA_GET_OR_RETURN(psd->wobj, wd); evas_object_geometry_get(obj, &ox, &oy, &ow, &oh); _image_place( wd->obj, psd->wsd->pan_x, psd->wsd->pan_y, ox - psd->wsd->g_layer_zoom.imx, oy - psd->wsd->g_layer_zoom.imy, ow, oh); EINA_LIST_FOREACH(psd->wsd->grids, l, g) { _grid_load(wd->obj, g); _grid_place( wd->obj, g, psd->wsd->pan_x, psd->wsd->pan_y, ox - psd->wsd->g_layer_zoom.imx, oy - psd->wsd->g_layer_zoom.imy, ow, oh); } } EOLIAN static void _efl_ui_image_zoomable_pan_efl_ui_pan_pan_position_set(Eo *obj, Efl_Ui_Image_Zoomable_Pan_Data *psd, Eina_Position2D pos) { if ((pos.x == psd->wsd->pan_x) && (pos.y == psd->wsd->pan_y)) return; psd->wsd->pan_x = pos.x; psd->wsd->pan_y = pos.y; evas_object_smart_changed(obj); efl_event_callback_call(obj, EFL_UI_PAN_EVENT_POSITION_CHANGED, NULL); } EOLIAN static Eina_Position2D _efl_ui_image_zoomable_pan_efl_ui_pan_pan_position_get(Eo *obj EINA_UNUSED, Efl_Ui_Image_Zoomable_Pan_Data *psd) { return EINA_POSITION2D(psd->wsd->pan_x, psd->wsd->pan_y); } EOLIAN static Eina_Position2D _efl_ui_image_zoomable_pan_efl_ui_pan_pan_position_max_get(Eo *obj, Efl_Ui_Image_Zoomable_Pan_Data *psd) { Evas_Coord ow, oh; evas_object_geometry_get(obj, NULL, NULL, &ow, &oh); ow = psd->wsd->minw - ow; if (ow < 0) ow = 0; oh = psd->wsd->minh - oh; if (oh < 0) oh = 0; return EINA_POSITION2D(ow, oh); } EOLIAN static Eina_Position2D _efl_ui_image_zoomable_pan_efl_ui_pan_pan_position_min_get(Eo *obj EINA_UNUSED, Efl_Ui_Image_Zoomable_Pan_Data *_pd EINA_UNUSED) { return EINA_POSITION2D(0, 0); } EOLIAN static Eina_Size2D _efl_ui_image_zoomable_pan_efl_ui_pan_content_size_get(Eo *obj EINA_UNUSED, Efl_Ui_Image_Zoomable_Pan_Data *psd) { return EINA_SIZE2D(psd->wsd->minw, psd->wsd->minh); } EOLIAN static void _efl_ui_image_zoomable_pan_efl_object_destructor(Eo *obj, Efl_Ui_Image_Zoomable_Pan_Data *psd) { efl_data_unref(psd->wobj, psd->wsd); efl_destructor(efl_super(obj, MY_PAN_CLASS)); } EOLIAN static void _efl_ui_image_zoomable_pan_class_constructor(Efl_Class *klass) { evas_smart_legacy_type_register(MY_PAN_CLASS_NAME_LEGACY, klass); } #include "efl_ui_image_zoomable_pan.eo.c" static int _nearest_pow2_get(int num) { unsigned int n = num - 1; n |= n >> 1; n |= n >> 2; n |= n >> 4; n |= n >> 8; n |= n >> 16; return n + 1; } static void _grid_clear(Evas_Object *obj, Efl_Ui_Image_Zoomable_Grid *g) { int x, y; EFL_UI_IMAGE_ZOOMABLE_DATA_GET(obj, sd); ELM_WIDGET_DATA_GET_OR_RETURN(obj, wd); if (!g->grid) return; for (y = 0; y < g->gh; y++) { for (x = 0; x < g->gw; x++) { int tn; tn = (y * g->gw) + x; evas_object_del(g->grid[tn].img); if (g->grid[tn].want) { sd->preload_num--; if (!sd->preload_num) { edje_object_signal_emit (wd->resize_obj, "elm,state,busy,stop", "elm"); efl_event_callback_legacy_call (obj, EFL_UI_IMAGE_ZOOMABLE_EVENT_LOAD_DETAIL, NULL); } } } } ELM_SAFE_FREE(g->grid, free); g->gw = 0; g->gh = 0; } static void _tile_preloaded_cb(void *data, Evas *e EINA_UNUSED, Evas_Object *o EINA_UNUSED, void *event_info EINA_UNUSED) { Efl_Ui_Image_Zoomable_Grid_Item *git = data; EFL_UI_IMAGE_ZOOMABLE_DATA_GET(git->obj, sd); ELM_WIDGET_DATA_GET_OR_RETURN(git->obj, wd); if (git->want) { git->want = 0; evas_object_show(git->img); git->have = 1; sd->preload_num--; if (!sd->preload_num) { edje_object_signal_emit (wd->resize_obj, "elm,state,busy,stop", "elm"); efl_event_callback_legacy_call (wd->obj, EFL_UI_IMAGE_ZOOMABLE_EVENT_LOADED_DETAIL, NULL); } } } static int _grid_zoom_calc(double zoom) { int z = zoom; if (z < 1) z = 1; return _nearest_pow2_get(z); } static Efl_Ui_Image_Zoomable_Grid * _grid_create(Evas_Object *obj) { int x, y; Efl_Ui_Image_Zoomable_Grid *g; EFL_UI_IMAGE_ZOOMABLE_DATA_GET(obj, sd); g = calloc(1, sizeof(Efl_Ui_Image_Zoomable_Grid)); if (!g) return NULL; g->zoom = _grid_zoom_calc(sd->zoom); g->tsize = sd->tsize; g->iw = sd->size.imw; g->ih = sd->size.imh; g->w = g->iw / g->zoom; g->h = g->ih / g->zoom; if (g->zoom >= 8) { free(g); return NULL; } if (sd->do_region) { g->gw = (g->w + g->tsize - 1) / g->tsize; g->gh = (g->h + g->tsize - 1) / g->tsize; } else { g->gw = 1; g->gh = 1; } g->grid = calloc(1, sizeof(Efl_Ui_Image_Zoomable_Grid_Item) * g->gw * g->gh); if (!g->grid) { g->gw = 0; g->gh = 0; return g; } for (y = 0; y < g->gh; y++) { for (x = 0; x < g->gw; x++) { int tn; tn = (y * g->gw) + x; g->grid[tn].out.x = x * g->tsize; if (x == (g->gw - 1)) g->grid[tn].out.w = g->w - ((g->gw - 1) * g->tsize); else g->grid[tn].out.w = g->tsize; g->grid[tn].out.y = y * g->tsize; if (y == (g->gh - 1)) g->grid[tn].out.h = g->h - ((g->gh - 1) * g->tsize); else g->grid[tn].out.h = g->tsize; if (g->zoom <= 0) g->zoom = 1; g->grid[tn].src.x = g->grid[tn].out.x * g->zoom; g->grid[tn].src.y = g->grid[tn].out.y * g->zoom; g->grid[tn].src.w = g->grid[tn].out.w * g->zoom; g->grid[tn].src.h = g->grid[tn].out.h * g->zoom; g->grid[tn].obj = obj; g->grid[tn].img = evas_object_image_add(evas_object_evas_get(obj)); evas_object_image_load_orientation_set(g->grid[tn].img, EINA_TRUE); efl_orientation_set(g->grid[tn].img, sd->orient); efl_orientation_flip_set(g->grid[tn].img, sd->flip); evas_object_image_scale_hint_set (g->grid[tn].img, EVAS_IMAGE_SCALE_HINT_DYNAMIC); evas_object_pass_events_set(g->grid[tn].img, EINA_TRUE); /* XXX: check this */ evas_object_smart_member_add(g->grid[tn].img, sd->pan_obj); elm_widget_sub_object_add(obj, g->grid[tn].img); evas_object_image_filled_set(g->grid[tn].img, 1); evas_object_event_callback_add (g->grid[tn].img, EVAS_CALLBACK_IMAGE_PRELOADED, _tile_preloaded_cb, &(g->grid[tn])); } } return g; } static void _grid_clear_all(Evas_Object *obj) { Efl_Ui_Image_Zoomable_Grid *g; EFL_UI_IMAGE_ZOOMABLE_DATA_GET(obj, sd); EINA_LIST_FREE(sd->grids, g) { _grid_clear(obj, g); free(g); } } static void _smooth_update(Evas_Object *obj) { Efl_Ui_Image_Zoomable_Grid *g; int x, y; Eina_List *l; EFL_UI_IMAGE_ZOOMABLE_DATA_GET(obj, sd); EINA_LIST_FOREACH(sd->grids, l, g) { for (y = 0; y < g->gh; y++) { for (x = 0; x < g->gw; x++) { int tn; tn = (y * g->gw) + x; evas_object_image_smooth_scale_set (g->grid[tn].img, (!sd->no_smooth)); } } } evas_object_image_smooth_scale_set(sd->img, (!sd->no_smooth)); } static void _grid_raise(Efl_Ui_Image_Zoomable_Grid *g) { int x, y; for (y = 0; y < g->gh; y++) { for (x = 0; x < g->gw; x++) { int tn; tn = (y * g->gw) + x; evas_object_raise(g->grid[tn].img); } } } static Eina_Bool _scroll_timeout_cb(void *data) { EFL_UI_IMAGE_ZOOMABLE_DATA_GET(data, sd); sd->no_smooth--; if (!sd->no_smooth) _smooth_update(data); sd->scr_timer = NULL; return ECORE_CALLBACK_CANCEL; } static void _main_img_preloaded_cb(void *data, Evas *e EINA_UNUSED, Evas_Object *o EINA_UNUSED, void *event_info EINA_UNUSED) { Evas_Object *obj = data; Efl_Ui_Image_Zoomable_Grid *g; EFL_UI_IMAGE_ZOOMABLE_DATA_GET(data, sd); ELM_WIDGET_DATA_GET_OR_RETURN(data, wd); evas_object_show(sd->img); sd->main_load_pending = 0; g = _grid_create(obj); if (g) { sd->grids = eina_list_prepend(sd->grids, g); _grid_load(obj, g); } ecore_job_del(sd->calc_job); sd->calc_job = ecore_job_add(_calc_job_cb, data); efl_event_callback_legacy_call(data, EFL_UI_IMAGE_ZOOMABLE_EVENT_LOADED, NULL); sd->preload_num--; if (!sd->preload_num) { edje_object_signal_emit (wd->resize_obj, "elm,state,busy,stop", "elm"); efl_event_callback_legacy_call (obj, EFL_UI_IMAGE_ZOOMABLE_EVENT_LOADED_DETAIL, NULL); } } static Eina_Bool _zoom_do(Evas_Object *obj, double t) { Evas_Coord xx, yy; Eina_Rect view = {}; EFL_UI_IMAGE_ZOOMABLE_DATA_GET(obj, sd); sd->size.w = (sd->size.ow * (1.0 - t)) + (sd->size.nw * t); sd->size.h = (sd->size.oh * (1.0 - t)) + (sd->size.nh * t); view = efl_ui_scrollable_viewport_geometry_get(sd->smanager); xx = (sd->size.spos.x * sd->size.w) - (view.w / 2); yy = (sd->size.spos.y * sd->size.h) - (view.h / 2); if (xx < 0) xx = 0; else if (xx > (sd->size.w - view.w)) xx = sd->size.w - view.w; if (yy < 0) yy = 0; else if (yy > (sd->size.h - view.h)) yy = sd->size.h - view.h; sd->show_item = EINA_TRUE; sd->show.x = xx; sd->show.y = yy; sd->show.w = view.w; sd->show.h = view.h; if (sd->orientation_changed) { evas_object_smart_member_del(sd->img); elm_widget_sub_object_del(obj, sd->img); evas_object_smart_member_add(sd->img, sd->pan_obj); elm_widget_sub_object_add(obj, sd->img); } ecore_job_del(sd->calc_job); sd->calc_job = ecore_job_add(_calc_job_cb, obj); if (t >= 1.0) { Eina_List *l, *l_next; Efl_Ui_Image_Zoomable_Grid *g; EINA_LIST_FOREACH_SAFE(sd->grids, l, l_next, g) { if (g->dead) { sd->grids = eina_list_remove_list(sd->grids, l); _grid_clear(obj, g); free(g); } } return EINA_FALSE; } return EINA_TRUE; } static void _zoom_anim_cb(void *data, const Efl_Event *event EINA_UNUSED) { double t; Evas_Object *obj = data; EFL_UI_IMAGE_ZOOMABLE_DATA_GET(obj, sd); t = ecore_loop_time_get(); if (t >= sd->t_end) t = 1.0; else if (sd->t_end > sd->t_start) t = (t - sd->t_start) / (sd->t_end - sd->t_start); else t = 1.0; t = 1.0 - t; t = 1.0 - (t * t); if (!_zoom_do(obj, t)) { sd->no_smooth--; if (!sd->no_smooth) _smooth_update(data); efl_event_callback_del(obj, EFL_EVENT_ANIMATOR_TICK, _zoom_anim_cb, obj); efl_event_callback_legacy_call(obj, EFL_UI_EVENT_ZOOM_STOP, NULL); } } static Eina_Bool _long_press_cb(void *data) { EFL_UI_IMAGE_ZOOMABLE_DATA_GET(data, sd); sd->long_timer = NULL; sd->longpressed = EINA_TRUE; efl_event_callback_legacy_call (data, EFL_UI_EVENT_LONGPRESSED, NULL); return ECORE_CALLBACK_CANCEL; } static void _mouse_down_cb(void *data, Evas *evas EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event_info) { Evas_Event_Mouse_Down *ev = event_info; EFL_UI_IMAGE_ZOOMABLE_DATA_GET(data, sd); if (ev->button != 1) return; if (ev->event_flags & EVAS_EVENT_FLAG_ON_HOLD) sd->on_hold = EINA_TRUE; else sd->on_hold = EINA_FALSE; if (ev->flags & EVAS_BUTTON_DOUBLE_CLICK) efl_event_callback_legacy_call (data, EFL_UI_EVENT_CLICKED_DOUBLE, NULL); else efl_event_callback_legacy_call(data, EFL_UI_IMAGE_ZOOMABLE_EVENT_PRESS, NULL); sd->longpressed = EINA_FALSE; ecore_timer_del(sd->long_timer); sd->long_timer = ecore_timer_add (_elm_config->longpress_timeout, _long_press_cb, data); } static void _mouse_up_cb(void *data, Evas *evas EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event_info) { Evas_Event_Mouse_Up *ev = event_info; EFL_UI_IMAGE_ZOOMABLE_DATA_GET(data, sd); if (ev->button != 1) return; if (ev->event_flags & EVAS_EVENT_FLAG_ON_HOLD) sd->on_hold = EINA_TRUE; else sd->on_hold = EINA_FALSE; ELM_SAFE_FREE(sd->long_timer, ecore_timer_del); if (!sd->on_hold) efl_event_callback_legacy_call (data, EFL_UI_EVENT_CLICKED, NULL); sd->on_hold = EINA_FALSE; } EOLIAN static Eina_Bool -_efl_ui_image_zoomable_efl_ui_widget_on_focus_update(Eo *obj, Efl_Ui_Image_Zoomable_Data *_pd EINA_UNUSED) +_efl_ui_image_zoomable_efl_ui_focus_object_on_focus_update(Eo *obj, Efl_Ui_Image_Zoomable_Data *_pd EINA_UNUSED) { ELM_WIDGET_DATA_GET_OR_RETURN(obj, wd, EINA_FALSE); Eina_Bool int_ret = EINA_FALSE; - int_ret = efl_ui_widget_on_focus_update(efl_super(obj, MY_CLASS)); + int_ret = efl_ui_focus_object_on_focus_update(efl_super(obj, MY_CLASS)); if (!int_ret) return EINA_FALSE; if (efl_ui_focus_object_focus_get(obj)) { edje_object_signal_emit (wd->resize_obj, "elm,action,focus", "elm"); evas_object_focus_set(wd->resize_obj, EINA_TRUE); } else { edje_object_signal_emit (wd->resize_obj, "elm,action,unfocus", "elm"); evas_object_focus_set(wd->resize_obj, EINA_FALSE); } return EINA_TRUE; } EOLIAN static Efl_Ui_Theme_Apply _efl_ui_image_zoomable_efl_ui_widget_theme_apply(Eo *obj, Efl_Ui_Image_Zoomable_Data *sd) { Efl_Ui_Theme_Apply int_ret = EFL_UI_THEME_APPLY_FAILED; Eina_Bool fdo = EINA_FALSE; if (sd->stdicon) _internal_efl_ui_image_zoomable_icon_set(obj, sd->stdicon, &fdo, EINA_TRUE); int_ret = efl_ui_widget_theme_apply(efl_super(obj, MY_CLASS)); if (!int_ret) return EFL_UI_THEME_APPLY_FAILED; efl_ui_mirrored_set(sd->smanager, efl_ui_mirrored_get(obj)); _sizing_eval(obj); return int_ret; } static void _scroll_cb(void * data, const Efl_Event *event EINA_UNUSED) { Evas_Object *obj = data; EFL_UI_IMAGE_ZOOMABLE_DATA_GET(obj, sd); if (!sd->scr_timer) { sd->no_smooth++; if (sd->no_smooth == 1) _smooth_update(obj); } ecore_timer_del(sd->scr_timer); sd->scr_timer = ecore_timer_add(0.5, _scroll_timeout_cb, obj); } static Eina_Bool _key_action_move(Evas_Object *obj, const char *params) { Eina_Rect view = {}; Eina_Position2D pos = {}; const char *dir = params; EFL_UI_IMAGE_ZOOMABLE_DATA_GET(obj, sd); Evas_Coord step_x = 0; Evas_Coord step_y = 0; Evas_Coord page_y = 0; pos = efl_ui_scrollable_content_pos_get(sd->smanager); view = efl_ui_scrollable_viewport_geometry_get(sd->smanager); if (!strcmp(dir, "left")) { pos.x -= step_x; } else if (!strcmp(dir, "right")) { pos.x += step_x; } else if (!strcmp(dir, "up")) { pos.y -= step_y; } else if (!strcmp(dir, "down")) { pos.y += step_y; } else if (!strcmp(dir, "prior")) { if (page_y < 0) pos.y -= -(page_y * view.h) / 100; else pos.y -= page_y; } else if (!strcmp(dir, "next")) { if (page_y < 0) pos.y += -(page_y * view.h) / 100; else pos.y += page_y; } else return EINA_FALSE; efl_ui_scrollable_content_pos_set(sd->smanager, pos); return EINA_TRUE; } static Eina_Bool _key_action_zoom(Evas_Object *obj, const char *params) { const char *dir = params; double zoom; if (!strcmp(dir, "in")) { zoom = elm_photocam_zoom_get(obj); zoom /= 1.5; elm_photocam_zoom_mode_set(obj, ELM_PHOTOCAM_ZOOM_MODE_MANUAL); elm_photocam_zoom_set(obj, zoom); } else if (!strcmp(dir, "out")) { zoom = elm_photocam_zoom_get(obj); zoom *= 1.5; elm_photocam_zoom_mode_set(obj, ELM_PHOTOCAM_ZOOM_MODE_MANUAL); elm_photocam_zoom_set(obj, zoom); } else return EINA_FALSE; return EINA_TRUE; } static void _bounce_eval(void *data, const Efl_Event *event EINA_UNUSED) { Evas_Object *obj = data; EFL_UI_IMAGE_ZOOMABLE_DATA_GET(obj, sd); double t, tt; if ((sd->g_layer_zoom.imx == sd->g_layer_zoom.bounce.x_end) && (sd->g_layer_zoom.imy == sd->g_layer_zoom.bounce.y_end)) goto on_end; t = ecore_loop_time_get(); tt = (t - sd->g_layer_zoom.bounce.t_start) / (sd->g_layer_zoom.bounce.t_end - sd->g_layer_zoom.bounce.t_start); tt = 1.0 - tt; tt = 1.0 - (tt * tt); if (t > sd->g_layer_zoom.bounce.t_end) { _zoom_do(obj, 1.0); goto on_end; } if (sd->g_layer_zoom.imx != sd->g_layer_zoom.bounce.x_end) sd->g_layer_zoom.imx = sd->g_layer_zoom.bounce.x_start * (1.0 - tt) + sd->g_layer_zoom.bounce.x_end * tt; if (sd->g_layer_zoom.imy != sd->g_layer_zoom.bounce.y_end) sd->g_layer_zoom.imy = sd->g_layer_zoom.bounce.y_start * (1.0 - tt) + sd->g_layer_zoom.bounce.y_end * tt; _zoom_do(obj, 1.0 - (1.0 - tt)); return; on_end: sd->g_layer_zoom.imx = 0; sd->g_layer_zoom.imy = 0; sd->zoom_g_layer = EINA_FALSE; efl_ui_scrollable_scroll_freeze_set(sd->smanager, EINA_FALSE); efl_event_callback_del(obj, EFL_EVENT_ANIMATOR_TICK, _bounce_eval, obj); } static void _efl_ui_image_zoomable_bounce_reset(Eo *obj, Efl_Ui_Image_Zoomable_Data *sd EINA_UNUSED) { Eina_Bool r; r = efl_event_callback_del(obj, EFL_EVENT_ANIMATOR_TICK, _bounce_eval, obj); if (r) _zoom_do(obj, 1.0); } static void _efl_ui_image_zoomable_zoom_reset(Eo *obj, Efl_Ui_Image_Zoomable_Data *sd) { Eina_Bool r; r = efl_event_callback_del(obj, EFL_EVENT_ANIMATOR_TICK, _zoom_anim_cb, obj); if (r) { sd->no_smooth--; if (!sd->no_smooth) _smooth_update(obj); _zoom_do(obj, 1.0); efl_event_callback_legacy_call(obj, EFL_UI_EVENT_ZOOM_STOP, NULL); } } static void _g_layer_zoom_do(Evas_Object *obj, Evas_Coord px, Evas_Coord py, Elm_Gesture_Zoom_Info *g_layer) { int regx, regy, regw, regh, ix, iy, iw, ih; int xx, yy; Eina_Rect view = {}; EFL_UI_IMAGE_ZOOMABLE_DATA_GET(obj, sd); sd->mode = ELM_PHOTOCAM_ZOOM_MODE_MANUAL; sd->zoom = sd->g_layer_start / g_layer->zoom; sd->size.ow = sd->size.w; sd->size.oh = sd->size.h; view = efl_ui_scrollable_viewport_geometry_get(sd->smanager); if ((view.w <= 0) || (view.h <= 0)) return; sd->size.nw = (double)sd->size.imw / sd->zoom; sd->size.nh = (double)sd->size.imh / sd->zoom; elm_photocam_image_region_get(obj, ®x, ®y, ®w, ®h); evas_object_geometry_get(sd->img, &ix, &iy, &iw, &ih); sd->pvx = g_layer->x; sd->pvy = g_layer->y; xx = (px / sd->zoom) - sd->pvx; yy = (py / sd->zoom) - sd->pvy; sd->g_layer_zoom.imx = 0; sd->g_layer_zoom.imy = 0; if ((xx < 0) || (view.w > sd->size.nw)) { sd->g_layer_zoom.imx = xx; xx = 0; } else if ((xx + view.w) > sd->size.nw) { sd->g_layer_zoom.imx = xx + view.w - sd->size.nw; xx = sd->size.nw - view.w; } if ((yy < 0) || (view.h > sd->size.nh)) { sd->g_layer_zoom.imy = yy; yy = 0; } else if ((yy + view.h) > sd->size.nh) { sd->g_layer_zoom.imy = yy + view.h - sd->size.nh; yy = sd->size.nh - view.h; } sd->size.spos.x = (double)(xx + (view.w / 2)) / (double)(sd->size.nw); sd->size.spos.y = (double)(yy + (view.h / 2)) / (double)(sd->size.nh); _zoom_do(obj, 1.0); } static Evas_Event_Flags _g_layer_zoom_start_cb(void *data, void *event_info) { Evas_Object *obj = data; Elm_Gesture_Zoom_Info *p = event_info; EFL_UI_IMAGE_ZOOMABLE_DATA_GET(obj, sd); double marginx = 0, marginy = 0; int x, y, w, h; Eina_Rect view = {}; _efl_ui_image_zoomable_bounce_reset(obj, sd); sd->zoom_g_layer = EINA_TRUE; efl_ui_scrollable_scroll_freeze_set(sd->smanager, EINA_TRUE); elm_photocam_image_region_get(obj, &x, &y, &w, &h); view = efl_ui_scrollable_viewport_geometry_get(sd->smanager); if (view.w > sd->size.nw) marginx = (view.w - sd->size.nw) / 2; if (view.h > sd->size.nh) marginy = (view.h - sd->size.nh) / 2; sd->g_layer_start = sd->zoom; sd->zoom_point_x = x + ((p->x - marginx) * sd->zoom) + sd->g_layer_zoom.imx; sd->zoom_point_y = y + ((p->y - marginy) * sd->zoom) + sd->g_layer_zoom.imy; return EVAS_EVENT_FLAG_NONE; } static Evas_Event_Flags _g_layer_zoom_move_cb(void *data, void *event_info) { Efl_Ui_Image_Zoomable_Data *sd = efl_data_scope_get(data, MY_CLASS); Elm_Gesture_Zoom_Info *p = event_info; _g_layer_zoom_do(data, sd->zoom_point_x, sd->zoom_point_y, p); return EVAS_EVENT_FLAG_NONE; } static Evas_Event_Flags _g_layer_zoom_end_cb(void *data, void *event_info EINA_UNUSED) { Evas_Object *obj = data; EFL_UI_IMAGE_ZOOMABLE_DATA_GET(obj, sd); Eina_Rect view = efl_ui_scrollable_viewport_geometry_get(sd->smanager); sd->g_layer_start = 1.0; if (sd->g_layer_zoom.imx || sd->g_layer_zoom.imy) { double t; t = ecore_loop_time_get(); sd->g_layer_zoom.bounce.x_start = sd->g_layer_zoom.imx; sd->g_layer_zoom.bounce.y_start = sd->g_layer_zoom.imy; sd->g_layer_zoom.bounce.x_end = 0; sd->g_layer_zoom.bounce.y_end = 0; if (view.w > sd->size.nw && view.h > sd->size.nh) { Evas_Coord pw, ph; double z; if ((sd->size.imw < view.w) && (sd->size.imh < view.h)) { sd->zoom = 1; sd->size.nw = sd->size.imw; sd->size.nh = sd->size.imh; } else { ph = (sd->size.imh * view.w) / sd->size.imw; if (ph > view.h) { pw = (sd->size.imw * view.h) / sd->size.imh; ph = view.h; } else { pw = view.w; } if (sd->size.imw > sd->size.imh) z = (double)sd->size.imw / pw; else z = (double)sd->size.imh / ph; sd->zoom = z; sd->size.nw = pw; sd->size.nh = ph; } sd->g_layer_zoom.bounce.x_end = (sd->size.nw - view.w) / 2; sd->g_layer_zoom.bounce.y_end = (sd->size.nh - view.h) / 2; } else { int xx, yy; xx = (sd->zoom_point_x / sd->zoom) - sd->pvx; yy = (sd->zoom_point_y / sd->zoom) - sd->pvy; if (xx < 0) xx = 0; if (yy < 0) yy = 0; if (view.w > sd->size.nw) sd->g_layer_zoom.bounce.x_end = (sd->size.nw - view.w) / 2; if ((xx + view.w) > sd->size.nw) xx = sd->size.nw - view.w; if (view.h > sd->size.nh) sd->g_layer_zoom.bounce.y_end = (sd->size.nh - view.h) / 2; if ((yy + view.h) > sd->size.nh) yy = sd->size.nh - view.h; sd->size.spos.x = (double)(xx + (view.w / 2)) / (double)(sd->size.nw); sd->size.spos.y = (double)(yy + (view.h / 2)) / (double)(sd->size.nh); } sd->g_layer_zoom.bounce.t_start = t; sd->g_layer_zoom.bounce.t_end = t + _elm_config->page_scroll_friction; efl_event_callback_add(obj, EFL_EVENT_ANIMATOR_TICK, _bounce_eval, obj); } else { efl_ui_scrollable_scroll_freeze_set(obj, EINA_FALSE); sd->zoom_g_layer = EINA_FALSE; } return EVAS_EVENT_FLAG_NONE; } static void _orient_do(Evas_Object *obj, Efl_Ui_Image_Zoomable_Data *sd) { evas_object_smart_member_del(sd->img); elm_widget_sub_object_del(obj, sd->img); evas_object_smart_member_add(sd->img, sd->pan_obj); elm_widget_sub_object_add(obj, sd->img); ecore_job_del(sd->calc_job); sd->calc_job = ecore_job_add(_calc_job_cb, obj); } static void _orient_apply(Eo *obj, Efl_Ui_Image_Zoomable_Data *sd) { int iw, ih; Eina_List *l; Efl_Ui_Image_Zoomable_Grid *g, *g_orient = NULL; // Note: This is based on legacy code. Separating flip & orient in eo api // means we need to do the apply twice. This could be delayed as a job. sd->orientation_changed = EINA_TRUE; g = _grid_create(obj); if (g) { if (eina_list_count(sd->grids) > 1) { g_orient = eina_list_last(sd->grids)->data; sd->grids = eina_list_remove(sd->grids, g_orient); _grid_clear(obj, g_orient); free(g_orient); EINA_LIST_FOREACH(sd->grids, l, g_orient) { g_orient->dead = 1; } } sd->grids = eina_list_prepend(sd->grids, g); } else { EINA_LIST_FREE(sd->grids, g) { _grid_clear(obj, g); free(g); } } efl_orientation_set(sd->img, sd->orient); efl_orientation_flip_set(sd->img, sd->flip); evas_object_image_size_get(sd->img, &iw, &ih); sd->size.imw = iw; sd->size.imh = ih; sd->size.w = iw / sd->zoom; sd->size.h = ih / sd->zoom; _orient_do(obj, sd); } EOLIAN static void _efl_ui_image_zoomable_efl_orientation_orientation_set(Eo *obj, Efl_Ui_Image_Zoomable_Data *sd, Efl_Orient orient) { if (sd->orient == orient) return; sd->orient = orient; _orient_apply(obj, sd); } EOLIAN static Efl_Orient _efl_ui_image_zoomable_efl_orientation_orientation_get(Eo *obj EINA_UNUSED, Efl_Ui_Image_Zoomable_Data *sd) { return sd->orient; } EOLIAN static void _efl_ui_image_zoomable_efl_orientation_flip_set(Eo *obj, Efl_Ui_Image_Zoomable_Data *sd, Efl_Flip flip) { if (sd->flip == flip) return; sd->flip = flip; _orient_apply(obj, sd); } EOLIAN static Efl_Flip _efl_ui_image_zoomable_efl_orientation_flip_get(Eo *obj EINA_UNUSED, Efl_Ui_Image_Zoomable_Data *sd) { return sd->flip; } static void _efl_ui_image_zoomable_bar_read_and_update(Eo *obj) { EFL_UI_IMAGE_ZOOMABLE_DATA_GET(obj, sd); ELM_WIDGET_DATA_GET_OR_RETURN(obj, wd); double vx, vy; edje_object_part_drag_value_get (wd->resize_obj, "elm.dragable.vbar", NULL, &vy); edje_object_part_drag_value_get (wd->resize_obj, "elm.dragable.hbar", &vx, NULL); efl_ui_scrollbar_bar_position_set(sd->smanager, vx, vy); } static void _efl_ui_image_zoomable_reload_cb(void *data, Evas_Object *obj EINA_UNUSED, const char *emission EINA_UNUSED, const char *source EINA_UNUSED) { EFL_UI_IMAGE_ZOOMABLE_DATA_GET(data, sd); efl_ui_scrollbar_bar_visibility_update(sd->smanager); } static void _efl_ui_image_zoomable_vbar_drag_cb(void *data, Evas_Object *obj EINA_UNUSED, const char *emission EINA_UNUSED, const char *source EINA_UNUSED) { _efl_ui_image_zoomable_bar_read_and_update(data); Efl_Ui_Scrollbar_Direction type = EFL_UI_SCROLLBAR_DIRECTION_VERTICAL; efl_event_callback_call(data, EFL_UI_SCROLLBAR_EVENT_BAR_DRAG, &type); } static void _efl_ui_image_zoomable_vbar_press_cb(void *data, Evas_Object *obj EINA_UNUSED, const char *emission EINA_UNUSED, const char *source EINA_UNUSED) { Efl_Ui_Scrollbar_Direction type = EFL_UI_SCROLLBAR_DIRECTION_VERTICAL; efl_event_callback_call(data, EFL_UI_SCROLLBAR_EVENT_BAR_PRESS, &type); } static void _efl_ui_image_zoomable_vbar_unpress_cb(void *data, Evas_Object *obj EINA_UNUSED, const char *emission EINA_UNUSED, const char *source EINA_UNUSED) { Efl_Ui_Scrollbar_Direction type = EFL_UI_SCROLLBAR_DIRECTION_VERTICAL; efl_event_callback_call(data, EFL_UI_SCROLLBAR_EVENT_BAR_UNPRESS, &type); } static void _efl_ui_image_zoomable_edje_drag_start_cb(void *data, Evas_Object *obj EINA_UNUSED, const char *emission EINA_UNUSED, const char *source EINA_UNUSED) { EFL_UI_IMAGE_ZOOMABLE_DATA_GET(data, sd); _efl_ui_image_zoomable_bar_read_and_update(data); sd->freeze_want = efl_ui_scrollable_scroll_freeze_get(sd->smanager); efl_ui_scrollable_scroll_freeze_set(sd->smanager, EINA_TRUE); efl_event_callback_call(data, EFL_UI_EVENT_SCROLL_DRAG_START, NULL); } static void _efl_ui_image_zoomable_edje_drag_stop_cb(void *data, Evas_Object *obj EINA_UNUSED, const char *emission EINA_UNUSED, const char *source EINA_UNUSED) { EFL_UI_IMAGE_ZOOMABLE_DATA_GET(data, sd); _efl_ui_image_zoomable_bar_read_and_update(data); efl_ui_scrollable_scroll_freeze_set(sd->smanager, sd->freeze_want); efl_event_callback_call(data, EFL_UI_EVENT_SCROLL_DRAG_STOP, NULL); } static void _efl_ui_image_zoomable_edje_drag_cb(void *data, Evas_Object *obj EINA_UNUSED, const char *emission EINA_UNUSED, const char *source EINA_UNUSED) { _efl_ui_image_zoomable_bar_read_and_update(data); } static void _efl_ui_image_zoomable_hbar_drag_cb(void *data, Evas_Object *obj EINA_UNUSED, const char *emission EINA_UNUSED, const char *source EINA_UNUSED) { _efl_ui_image_zoomable_bar_read_and_update(data); Efl_Ui_Scrollbar_Direction type = EFL_UI_SCROLLBAR_DIRECTION_HORIZONTAL; efl_event_callback_call(data, EFL_UI_SCROLLBAR_EVENT_BAR_DRAG, &type); } static void _efl_ui_image_zoomable_hbar_press_cb(void *data, Evas_Object *obj EINA_UNUSED, const char *emission EINA_UNUSED, const char *source EINA_UNUSED) { Efl_Ui_Scrollbar_Direction type = EFL_UI_SCROLLBAR_DIRECTION_HORIZONTAL; efl_event_callback_call(data, EFL_UI_SCROLLBAR_EVENT_BAR_PRESS, &type); } static void _efl_ui_image_zoomable_hbar_unpress_cb(void *data, Evas_Object *obj EINA_UNUSED, const char *emission EINA_UNUSED, const char *source EINA_UNUSED) { Efl_Ui_Scrollbar_Direction type = EFL_UI_SCROLLBAR_DIRECTION_HORIZONTAL; efl_event_callback_call(data, EFL_UI_SCROLLBAR_EVENT_BAR_UNPRESS, &type); } static void _efl_ui_image_zoomable_bar_size_changed_cb(void *data, const Efl_Event *event EINA_UNUSED) { Eo *obj = data; EFL_UI_IMAGE_ZOOMABLE_DATA_GET(obj, sd); ELM_WIDGET_DATA_GET_OR_RETURN(obj, wd); double width = 0.0, height = 0.0; efl_ui_scrollbar_bar_size_get(sd->smanager, &width, &height); edje_object_part_drag_size_set(wd->resize_obj, "elm.dragable.hbar", width, 1.0); edje_object_part_drag_size_set(wd->resize_obj, "elm.dragable.vbar", 1.0, height); } static void _efl_ui_image_zoomable_bar_pos_changed_cb(void *data, const Efl_Event *event EINA_UNUSED) { Eo *obj = data; EFL_UI_IMAGE_ZOOMABLE_DATA_GET(obj, sd); ELM_WIDGET_DATA_GET_OR_RETURN(obj, wd); double posx = 0.0, posy = 0.0; efl_ui_scrollbar_bar_position_get(sd->smanager, &posx, &posy); edje_object_part_drag_value_set(wd->resize_obj, "elm.dragable.hbar", posx, 0.0); edje_object_part_drag_value_set(wd->resize_obj, "elm.dragable.vbar", 0.0, posy); } static void _efl_ui_image_zoomable_bar_show_cb(void *data, const Efl_Event *event) { Eo *obj = data; ELM_WIDGET_DATA_GET_OR_RETURN(obj, wd); Efl_Ui_Scrollbar_Direction type = *(Efl_Ui_Scrollbar_Direction *)(event->info); if (type == EFL_UI_SCROLLBAR_DIRECTION_HORIZONTAL) edje_object_signal_emit(wd->resize_obj, "elm,action,show,hbar", "elm"); else if (type == EFL_UI_SCROLLBAR_DIRECTION_VERTICAL) edje_object_signal_emit(wd->resize_obj, "elm,action,show,vbar", "elm"); } static void _efl_ui_image_zoomable_bar_hide_cb(void *data, const Efl_Event *event) { Eo *obj = data; ELM_WIDGET_DATA_GET_OR_RETURN(obj, wd); Efl_Ui_Scrollbar_Direction type = *(Efl_Ui_Scrollbar_Direction *)(event->info); if (type == EFL_UI_SCROLLBAR_DIRECTION_HORIZONTAL) edje_object_signal_emit(wd->resize_obj, "elm,action,hide,hbar", "elm"); else if (type == EFL_UI_SCROLLBAR_DIRECTION_VERTICAL) edje_object_signal_emit(wd->resize_obj, "elm,action,hide,vbar", "elm"); } static void _efl_ui_image_zoomable_edje_object_attach(Eo *obj) { efl_layout_signal_callback_add (obj, "reload", "elm", _efl_ui_image_zoomable_reload_cb, obj); efl_layout_signal_callback_add (obj, "drag", "elm.dragable.vbar", _efl_ui_image_zoomable_vbar_drag_cb, obj); efl_layout_signal_callback_add (obj, "drag,set", "elm.dragable.vbar", _efl_ui_image_zoomable_edje_drag_cb, obj); efl_layout_signal_callback_add (obj, "drag,start", "elm.dragable.vbar", _efl_ui_image_zoomable_edje_drag_start_cb, obj); efl_layout_signal_callback_add (obj, "drag,stop", "elm.dragable.vbar", _efl_ui_image_zoomable_edje_drag_stop_cb, obj); efl_layout_signal_callback_add (obj, "drag,step", "elm.dragable.vbar", _efl_ui_image_zoomable_edje_drag_cb, obj); efl_layout_signal_callback_add (obj, "drag,page", "elm.dragable.vbar", _efl_ui_image_zoomable_edje_drag_cb, obj); efl_layout_signal_callback_add (obj, "elm,vbar,press", "elm", _efl_ui_image_zoomable_vbar_press_cb, obj); efl_layout_signal_callback_add (obj, "elm,vbar,unpress", "elm", _efl_ui_image_zoomable_vbar_unpress_cb, obj); efl_layout_signal_callback_add (obj, "drag", "elm.dragable.hbar", _efl_ui_image_zoomable_hbar_drag_cb, obj); efl_layout_signal_callback_add (obj, "drag,set", "elm.dragable.hbar", _efl_ui_image_zoomable_edje_drag_cb, obj); efl_layout_signal_callback_add (obj, "drag,start", "elm.dragable.hbar", _efl_ui_image_zoomable_edje_drag_start_cb, obj); efl_layout_signal_callback_add (obj, "drag,stop", "elm.dragable.hbar", _efl_ui_image_zoomable_edje_drag_stop_cb, obj); efl_layout_signal_callback_add (obj, "drag,step", "elm.dragable.hbar", _efl_ui_image_zoomable_edje_drag_cb, obj); efl_layout_signal_callback_add (obj, "drag,page", "elm.dragable.hbar", _efl_ui_image_zoomable_edje_drag_cb, obj); efl_layout_signal_callback_add (obj, "elm,hbar,press", "elm", _efl_ui_image_zoomable_hbar_press_cb, obj); efl_layout_signal_callback_add (obj, "elm,hbar,unpress", "elm", _efl_ui_image_zoomable_hbar_unpress_cb, obj); } static void _efl_ui_image_zoomable_edje_object_detach(Evas_Object *obj) { efl_layout_signal_callback_del (obj, "reload", "elm", _efl_ui_image_zoomable_reload_cb, obj); efl_layout_signal_callback_del (obj, "drag", "elm.dragable.vbar", _efl_ui_image_zoomable_vbar_drag_cb, obj); efl_layout_signal_callback_del (obj, "drag,set", "elm.dragable.vbar", _efl_ui_image_zoomable_edje_drag_cb, obj); efl_layout_signal_callback_del (obj, "drag,start", "elm.dragable.vbar", _efl_ui_image_zoomable_edje_drag_start_cb, obj); efl_layout_signal_callback_del (obj, "drag,stop", "elm.dragable.vbar", _efl_ui_image_zoomable_edje_drag_stop_cb, obj); efl_layout_signal_callback_del (obj, "drag,step", "elm.dragable.vbar", _efl_ui_image_zoomable_edje_drag_cb, obj); efl_layout_signal_callback_del (obj, "drag,page", "elm.dragable.vbar", _efl_ui_image_zoomable_edje_drag_cb, obj); efl_layout_signal_callback_del (obj, "elm,vbar,press", "elm", _efl_ui_image_zoomable_vbar_press_cb, obj); efl_layout_signal_callback_del (obj, "elm,vbar,unpress", "elm", _efl_ui_image_zoomable_vbar_unpress_cb, obj); efl_layout_signal_callback_del (obj, "drag", "elm.dragable.hbar", _efl_ui_image_zoomable_hbar_drag_cb, obj); efl_layout_signal_callback_del (obj, "drag,set", "elm.dragable.hbar", _efl_ui_image_zoomable_edje_drag_cb, obj); efl_layout_signal_callback_del (obj, "drag,start", "elm.dragable.hbar", _efl_ui_image_zoomable_edje_drag_start_cb, obj); efl_layout_signal_callback_del (obj, "drag,stop", "elm.dragable.hbar", _efl_ui_image_zoomable_edje_drag_stop_cb, obj); efl_layout_signal_callback_del (obj, "drag,step", "elm.dragable.hbar", _efl_ui_image_zoomable_edje_drag_cb, obj); efl_layout_signal_callback_del (obj, "drag,page", "elm.dragable.hbar", _efl_ui_image_zoomable_edje_drag_cb, obj); efl_layout_signal_callback_del (obj, "elm,hbar,press", "elm", _efl_ui_image_zoomable_hbar_press_cb, obj); efl_layout_signal_callback_del (obj, "elm,hbar,unpress", "elm", _efl_ui_image_zoomable_hbar_unpress_cb, obj); } EOLIAN static void _efl_ui_image_zoomable_efl_canvas_group_group_add(Eo *obj, Efl_Ui_Image_Zoomable_Data *priv) { Eina_Bool bounce = _elm_config->thumbscroll_bounce_enable; Efl_Ui_Image_Zoomable_Pan_Data *pan_data; Evas_Object *edje; Evas_Coord minw, minh; elm_widget_sub_object_parent_add(obj); edje = edje_object_add(evas_object_evas_get(obj)); elm_widget_resize_object_set(obj, edje); efl_canvas_group_add(efl_super(obj, MY_CLASS)); elm_widget_theme_object_set (obj, edje, "photocam", "base", elm_widget_style_get(obj)); elm_widget_can_focus_set(obj, EINA_TRUE); priv->smanager = efl_add(EFL_UI_SCROLL_MANAGER_CLASS, obj); efl_ui_mirrored_set(priv->smanager, efl_ui_mirrored_get(obj)); efl_ui_scrollable_bounce_enabled_set(priv->smanager, bounce, bounce); priv->pan_obj = efl_add(MY_PAN_CLASS, obj); efl_ui_scroll_manager_pan_set(priv->smanager, priv->pan_obj); edje_object_part_swallow(edje, "elm.swallow.content", priv->pan_obj); pan_data = efl_data_scope_get(priv->pan_obj, MY_PAN_CLASS); efl_data_ref(obj, MY_CLASS); pan_data->wobj = obj; pan_data->wsd = priv; efl_event_callback_add(obj, EFL_UI_EVENT_SCROLL, _scroll_cb, obj); priv->g_layer_start = 1.0; priv->zoom = 1; priv->mode = ELM_PHOTOCAM_ZOOM_MODE_MANUAL; priv->tsize = 512; priv->img = evas_object_image_add(evas_object_evas_get(obj)); evas_object_image_load_orientation_set(priv->img, EINA_TRUE); evas_object_image_scale_hint_set(priv->img, EVAS_IMAGE_SCALE_HINT_DYNAMIC); evas_object_event_callback_add (priv->img, EVAS_CALLBACK_MOUSE_DOWN, _mouse_down_cb, obj); evas_object_event_callback_add (priv->img, EVAS_CALLBACK_MOUSE_UP, _mouse_up_cb, obj); evas_object_image_scale_hint_set(priv->img, EVAS_IMAGE_SCALE_HINT_STATIC); /* XXX: mmm... */ evas_object_smart_member_add(priv->img, priv->pan_obj); elm_widget_sub_object_add(obj, priv->img); evas_object_image_filled_set(priv->img, EINA_TRUE); evas_object_event_callback_add (priv->img, EVAS_CALLBACK_IMAGE_PRELOADED, _main_img_preloaded_cb, obj); edje_object_size_min_calc(edje, &minw, &minh); evas_object_size_hint_min_set(obj, minw, minh); _efl_ui_image_zoomable_edje_object_attach(obj); efl_event_callback_add(obj, EFL_UI_SCROLLBAR_EVENT_BAR_SIZE_CHANGED, _efl_ui_image_zoomable_bar_size_changed_cb, obj); efl_event_callback_add(obj, EFL_UI_SCROLLBAR_EVENT_BAR_POS_CHANGED, _efl_ui_image_zoomable_bar_pos_changed_cb, obj); efl_event_callback_add(obj, EFL_UI_SCROLLBAR_EVENT_BAR_SHOW, _efl_ui_image_zoomable_bar_show_cb, obj); efl_event_callback_add(obj, EFL_UI_SCROLLBAR_EVENT_BAR_HIDE, _efl_ui_image_zoomable_bar_hide_cb, obj); _sizing_eval(obj); } EOLIAN static void _efl_ui_image_zoomable_efl_canvas_group_group_del(Eo *obj, Efl_Ui_Image_Zoomable_Data *sd) { Efl_Ui_Image_Zoomable_Grid *g; ecore_timer_del(sd->anim_timer); ELM_SAFE_FREE(sd->edje, evas_object_del); EINA_LIST_FREE(sd->grids, g) { _grid_clear(obj, g); free(g->grid); free(g); } ELM_SAFE_FREE(sd->pan_obj, evas_object_del); if (sd->f) eina_file_close(sd->f); if (sd->remote.copier) _efl_ui_image_zoomable_remote_copier_cancel(obj, sd); if (sd->remote.binbuf) ELM_SAFE_FREE(sd->remote.binbuf, eina_binbuf_free); eina_stringshare_del(sd->file); ecore_job_del(sd->calc_job); ecore_timer_del(sd->scr_timer); ecore_timer_del(sd->long_timer); efl_event_callback_del(obj, EFL_EVENT_ANIMATOR_TICK, _zoom_anim_cb, obj); efl_event_callback_del(obj, EFL_EVENT_ANIMATOR_TICK, _bounce_eval, obj); efl_event_callback_del(obj, EFL_UI_EVENT_SCROLL, _scroll_cb, obj); _efl_ui_image_zoomable_edje_object_detach(obj); efl_del(sd->pan_obj); sd->pan_obj = NULL; efl_del(sd->smanager); sd->smanager = NULL; efl_canvas_group_del(efl_super(obj, MY_CLASS)); } EOLIAN static void _efl_ui_image_zoomable_efl_gfx_position_set(Eo *obj, Efl_Ui_Image_Zoomable_Data *sdi EINA_UNUSED, Eina_Position2D pos) { if (_evas_object_intercept_call(obj, EVAS_OBJECT_INTERCEPT_CB_MOVE, 0, pos.x, pos.y)) return; efl_gfx_position_set(efl_super(obj, MY_CLASS), pos); } EOLIAN static void _efl_ui_image_zoomable_efl_gfx_size_set(Eo *obj, Efl_Ui_Image_Zoomable_Data *sd EINA_UNUSED, Eina_Size2D sz) { if (_evas_object_intercept_call(obj, EVAS_OBJECT_INTERCEPT_CB_RESIZE, 0, sz.w, sz.h)) return; efl_gfx_size_set(efl_super(obj, MY_CLASS), sz); } EOLIAN static void _efl_ui_image_zoomable_efl_canvas_group_group_member_add(Eo *obj, Efl_Ui_Image_Zoomable_Data *sd EINA_UNUSED, Evas_Object *member) { efl_canvas_group_member_add(efl_super(obj, MY_CLASS), member); } EOLIAN static Eo * _efl_ui_image_zoomable_efl_object_constructor(Eo *obj, Efl_Ui_Image_Zoomable_Data *_pd EINA_UNUSED) { obj = efl_constructor(efl_super(obj, MY_CLASS)); efl_canvas_object_type_set(obj, MY_CLASS_NAME_LEGACY); evas_object_smart_callbacks_descriptions_set(obj, _smart_callbacks); efl_access_role_set(obj, EFL_ACCESS_ROLE_IMAGE); return obj; } EOLIAN static Eina_Size2D _efl_ui_image_zoomable_efl_image_image_size_get(Eo *obj EINA_UNUSED, Efl_Ui_Image_Zoomable_Data *pd) { return EINA_SIZE2D(pd->size.imw, pd->size.imh); } EOLIAN static Eina_Size2D _efl_ui_image_zoomable_efl_layout_group_group_size_min_get(Eo *obj EINA_UNUSED, Efl_Ui_Image_Zoomable_Data *sd) { if (sd->edje) return efl_layout_group_size_min_get(sd->edje); else return EINA_SIZE2D(0, 0); } EOLIAN static Eina_Size2D _efl_ui_image_zoomable_efl_layout_group_group_size_max_get(Eo *obj EINA_UNUSED, Efl_Ui_Image_Zoomable_Data *sd) { if (sd->edje) return efl_layout_group_size_max_get(sd->edje); else return EINA_SIZE2D(0, 0); } EOLIAN static Eina_Bool _efl_ui_image_zoomable_efl_layout_signal_signal_callback_add(Eo *obj EINA_UNUSED, Efl_Ui_Image_Zoomable_Data *sd EINA_UNUSED, const char *emission, const char *source, Edje_Signal_Cb func_cb, void *data) { Eina_Bool ok; ELM_WIDGET_DATA_GET_OR_RETURN(obj, wd, EINA_FALSE); ok = efl_layout_signal_callback_add(wd->resize_obj, emission, source, func_cb, data); return ok; } EOLIAN static Eina_Bool _efl_ui_image_zoomable_efl_layout_signal_signal_callback_del(Eo *obj EINA_UNUSED, Efl_Ui_Image_Zoomable_Data *sd EINA_UNUSED, const char *emission, const char *source, Edje_Signal_Cb func_cb, void *data) { Eina_Bool ok; ELM_WIDGET_DATA_GET_OR_RETURN(obj, wd, EINA_FALSE); ok = efl_layout_signal_callback_del(wd->resize_obj, emission, source, func_cb, data); return ok; } static Eina_Bool _img_proxy_set(Evas_Object *obj, Efl_Ui_Image_Zoomable_Data *sd, const char *file, const Eina_File *f, const char *group, Eina_Bool resize) { ELM_WIDGET_DATA_GET_OR_RETURN(obj, wd, EINA_FALSE); double tz; int w, h; sd->zoom = 1.0; evas_object_image_smooth_scale_set(sd->img, (sd->no_smooth == 0)); evas_object_image_file_set(sd->img, NULL, NULL); evas_object_image_source_set(sd->img, NULL); evas_object_image_load_scale_down_set(sd->img, 0); if (!sd->edje) sd->edje = edje_object_add(evas_object_evas_get(obj)); if (!resize) { if (f) { if (!edje_object_mmap_set(sd->edje, f, group)) { ERR("failed to set edje file '%s', group '%s': %s", sd->file, group, edje_load_error_str(edje_object_load_error_get(sd->edje))); return EINA_FALSE; } } else if (file) { if (!edje_object_file_set(sd->edje, file, group)) { ERR("failed to set edje file '%s', group '%s': %s", file, group, edje_load_error_str(edje_object_load_error_get(sd->edje))); return EINA_FALSE; } } } _min_obj_size_get(obj, &w, &h); evas_object_resize(sd->edje, w, h); evas_object_image_source_set(sd->img, sd->edje); evas_object_image_source_visible_set(sd->img, EINA_FALSE); evas_object_size_hint_min_set(sd->img, w, h); evas_object_show(sd->img); evas_object_show(sd->edje); sd->do_region = 0; sd->size.imw = w; sd->size.imh = h; sd->size.w = sd->size.imw / sd->zoom; sd->size.h = sd->size.imh / sd->zoom; evas_object_image_preload(sd->img, 0); sd->main_load_pending = EINA_TRUE; sd->calc_job = ecore_job_add(_calc_job_cb, obj); efl_event_callback_legacy_call(obj, EFL_UI_IMAGE_ZOOMABLE_EVENT_LOAD, NULL); sd->preload_num++; if (sd->preload_num == 1) { edje_object_signal_emit (wd->resize_obj, "elm,state,busy,start", "elm"); efl_event_callback_legacy_call(obj, EFL_UI_IMAGE_ZOOMABLE_EVENT_LOAD_DETAIL, NULL); } tz = sd->zoom; sd->zoom = 0.0; elm_photocam_zoom_set(obj, tz); sd->orient = EFL_ORIENT_NONE; sd->flip = EFL_FLIP_NONE; sd->orientation_changed = EINA_FALSE; return EINA_TRUE; } static Eina_Bool _image_zoomable_edje_file_set(Evas_Object *obj, const char *file, const char *group) { EFL_UI_IMAGE_ZOOMABLE_DATA_GET(obj, sd); ELM_WIDGET_DATA_GET_OR_RETURN(obj, wd, EINA_FALSE); if (file) eina_stringshare_replace(&sd->file, file); return _img_proxy_set(obj, sd, file, NULL, group, EINA_FALSE); } static void _internal_file_set(Eo *obj, Efl_Ui_Image_Zoomable_Data *sd, const char *file, Eina_File *f, const char *key, Evas_Load_Error *ret) { ELM_WIDGET_DATA_GET_OR_RETURN(obj, wd); Evas_Load_Error err; int w, h; double tz; if (eina_str_has_extension(file, ".edj")) { _image_zoomable_edje_file_set(obj, file, key); return; } // It is actually to late, we have lost the reference to the previous // file descriptor already, so we can't know if the file changed. To // be safe we do for now just force a full reload on file_set and hope // on evas to catch it, if there is no change. eina_stringshare_replace(&sd->file, file); sd->f = eina_file_dup(f); evas_object_image_smooth_scale_set(sd->img, (sd->no_smooth == 0)); evas_object_image_file_set(sd->img, NULL, NULL); evas_object_image_load_scale_down_set(sd->img, 0); _photocam_image_file_set(sd->img, sd); err = evas_object_image_load_error_get(sd->img); if (err != EVAS_LOAD_ERROR_NONE) { ERR("Things are going bad for '%s' (%p) : %i", file, sd->img, err); if (ret) *ret = err; return; } evas_object_image_size_get(sd->img, &w, &h); sd->do_region = evas_object_image_region_support_get(sd->img); sd->size.imw = w; sd->size.imh = h; sd->size.w = sd->size.imw / sd->zoom; sd->size.h = sd->size.imh / sd->zoom; evas_object_image_file_set(sd->img, NULL, NULL); _photocam_image_file_set(sd->img, sd); err = evas_object_image_load_error_get(sd->img); if (err != EVAS_LOAD_ERROR_NONE) { ERR("Things are going bad for '%s' (%p)", file, sd->img); if (ret) *ret = err; return; } evas_object_image_preload(sd->img, 0); sd->main_load_pending = EINA_TRUE; sd->calc_job = ecore_job_add(_calc_job_cb, obj); efl_event_callback_legacy_call(obj, EFL_UI_IMAGE_ZOOMABLE_EVENT_LOAD, NULL); sd->preload_num++; if (sd->preload_num == 1) { edje_object_signal_emit (wd->resize_obj, "elm,state,busy,start", "elm"); efl_event_callback_legacy_call(obj, EFL_UI_IMAGE_ZOOMABLE_EVENT_LOAD_DETAIL, NULL); } tz = sd->zoom; sd->zoom = 0.0; elm_photocam_zoom_set(obj, tz); sd->orient = EFL_ORIENT_NONE; sd->flip = EFL_FLIP_NONE; sd->orientation_changed = EINA_FALSE; if (ret) *ret = evas_object_image_load_error_get(sd->img); } static void _efl_ui_image_zoomable_remote_copier_del(void *data EINA_UNUSED, const Efl_Event *event) { Eo *dialer = efl_io_copier_source_get(event->object); efl_del(dialer); } static void _efl_ui_image_zoomable_remote_copier_cancel(Eo *obj EINA_UNUSED, Efl_Ui_Image_Zoomable_Data *sd) { Eo *copier = sd->remote.copier; if (!copier) return; /* copier is flagged as close_on_destructor, thus: * efl_del() * -> efl_io_closer_close() * -> "done" event * -> _efl_ui_image_zoomable_remote_copier_done() * * flag sd->remote.copier = NULL so _efl_ui_image_zoomable_remote_copier_done() * knows about it. */ sd->remote.copier = NULL; efl_del(copier); } static void _efl_ui_image_zoomable_remote_copier_done(void *data, const Efl_Event *event EINA_UNUSED) { Eo *obj = data; Efl_Ui_Image_Zoomable_Data *sd = efl_data_scope_get(obj, MY_CLASS); Eina_File *f; Eo *dialer; const char *url; Evas_Load_Error ret = EVAS_LOAD_ERROR_NONE; /* we're called from _efl_ui_image_zoomable_remote_copier_cancel() */ if (!sd->remote.copier) return; if (sd->remote.binbuf) eina_binbuf_free(sd->remote.binbuf); sd->remote.binbuf = efl_io_copier_binbuf_steal(sd->remote.copier); dialer = efl_io_copier_source_get(sd->remote.copier); url = efl_net_dialer_address_dial_get(dialer); f = eina_file_virtualize(url, eina_binbuf_string_get(sd->remote.binbuf), eina_binbuf_length_get(sd->remote.binbuf), EINA_FALSE); _internal_file_set(obj, sd, url, f, NULL, &ret); eina_file_close(f); if (ret != EVAS_LOAD_ERROR_NONE) { Elm_Photocam_Error err = { 0, EINA_TRUE }; ELM_SAFE_FREE(sd->remote.binbuf, eina_binbuf_free); efl_event_callback_legacy_call (obj, EFL_UI_IMAGE_ZOOMABLE_EVENT_DOWNLOAD_ERROR, &err); } else { efl_event_callback_legacy_call (obj, EFL_UI_IMAGE_ZOOMABLE_EVENT_DOWNLOAD_DONE, NULL); } ELM_SAFE_FREE(sd->remote.copier, efl_del); } static void _efl_ui_image_zoomable_remote_copier_error(void *data, const Efl_Event *event) { Eo *obj = data; Efl_Ui_Image_Zoomable_Data *sd = efl_data_scope_get(obj, MY_CLASS); Eina_Error *perr = event->info; Elm_Photocam_Error err = { *perr, EINA_FALSE }; efl_event_callback_legacy_call(obj, EFL_UI_IMAGE_ZOOMABLE_EVENT_DOWNLOAD_ERROR, &err); _efl_ui_image_zoomable_remote_copier_cancel(obj, sd); } static void _efl_ui_image_zoomable_remote_copier_progress(void *data, const Efl_Event *event) { Eo *obj = data; Elm_Photocam_Progress progress; uint64_t now, total; efl_io_copier_progress_get(event->object, &now, NULL, &total); progress.now = now; progress.total = total; efl_event_callback_legacy_call (obj, EFL_UI_IMAGE_ZOOMABLE_EVENT_DOWNLOAD_PROGRESS, &progress); } EFL_CALLBACKS_ARRAY_DEFINE(_efl_ui_image_zoomable_remote_copier_cbs, { EFL_EVENT_DEL, _efl_ui_image_zoomable_remote_copier_del }, { EFL_IO_COPIER_EVENT_DONE, _efl_ui_image_zoomable_remote_copier_done }, { EFL_IO_COPIER_EVENT_ERROR, _efl_ui_image_zoomable_remote_copier_error }, { EFL_IO_COPIER_EVENT_PROGRESS, _efl_ui_image_zoomable_remote_copier_progress }); static Eina_Bool _efl_ui_image_zoomable_download(Eo *obj, Efl_Ui_Image_Zoomable_Data *sd, const char *url) { Eo *dialer; Elm_Photocam_Error img_err = { ENOSYS, EINA_FALSE }; Eina_Error err; dialer = efl_add(EFL_NET_DIALER_HTTP_CLASS, obj, efl_net_dialer_http_allow_redirects_set(efl_added, EINA_TRUE)); EINA_SAFETY_ON_NULL_GOTO(dialer, error_dialer); sd->remote.copier = efl_add(EFL_IO_COPIER_CLASS, obj, efl_io_copier_source_set(efl_added, dialer), efl_io_closer_close_on_destructor_set(efl_added, EINA_TRUE), efl_event_callback_array_add(efl_added, _efl_ui_image_zoomable_remote_copier_cbs(), obj)); EINA_SAFETY_ON_NULL_GOTO(sd->remote.copier, error_copier); err = efl_net_dialer_dial(dialer, url); if (err) { img_err.status = err; ERR("Could not download %s: %s", url, eina_error_msg_get(err)); evas_object_smart_callback_call(obj, SIG_DOWNLOAD_ERROR, &img_err); goto error_dial; } return EINA_TRUE; error_dial: evas_object_smart_callback_call(obj, SIG_DOWNLOAD_ERROR, &img_err); _efl_ui_image_zoomable_remote_copier_cancel(obj, sd); return EINA_FALSE; error_copier: efl_del(dialer); error_dialer: evas_object_smart_callback_call(obj, SIG_DOWNLOAD_ERROR, &img_err); return EINA_FALSE; } static const Eina_Slice remote_uri[] = { EINA_SLICE_STR_LITERAL("http://"), EINA_SLICE_STR_LITERAL("https://"), EINA_SLICE_STR_LITERAL("ftp://"), { } }; static inline Eina_Bool _efl_ui_image_zoomable_is_remote(const char *file) { Eina_Slice s = EINA_SLICE_STR(file); const Eina_Slice *itr; for (itr = remote_uri; itr->mem; itr++) if (eina_slice_startswith(s, *itr)) return EINA_TRUE; return EINA_FALSE; } static Evas_Load_Error _efl_ui_image_zoomable_file_set_internal(Eo *obj, Efl_Ui_Image_Zoomable_Data *sd, const char *file, const char *key) { Evas_Load_Error ret = EVAS_LOAD_ERROR_NONE; ELM_SAFE_FREE(sd->edje, evas_object_del); eina_stringshare_replace(&sd->stdicon, NULL); _grid_clear_all(obj); _efl_ui_image_zoomable_zoom_reset(obj, sd); _efl_ui_image_zoomable_bounce_reset(obj, sd); sd->no_smooth--; if (sd->no_smooth == 0) _smooth_update(obj); ecore_job_del(sd->calc_job); evas_object_hide(sd->img); if (sd->f) eina_file_close(sd->f); sd->f = NULL; if (sd->remote.copier) _efl_ui_image_zoomable_remote_copier_cancel(obj, sd); if (sd->remote.binbuf) ELM_SAFE_FREE(sd->remote.binbuf, eina_binbuf_free); sd->preload_num = 0; if (_efl_ui_image_zoomable_is_remote(file)) { if (_efl_ui_image_zoomable_download(obj, sd, file)) { efl_event_callback_legacy_call (obj, EFL_UI_IMAGE_ZOOMABLE_EVENT_DOWNLOAD_START, NULL); return ret; } } _internal_file_set(obj, sd, file, NULL, key, &ret); return ret; } EOLIAN static Eina_Bool _efl_ui_image_zoomable_efl_file_file_set(Eo *obj, Efl_Ui_Image_Zoomable_Data *sd, const char *file, const char *key) { Evas_Load_Error ret = _efl_ui_image_zoomable_file_set_internal(obj, sd, file, key); if (ret == EVAS_LOAD_ERROR_NONE) return EINA_TRUE; eina_error_set( ret == EVAS_LOAD_ERROR_DOES_NOT_EXIST ? PHOTO_FILE_LOAD_ERROR_DOES_NOT_EXIST : ret == EVAS_LOAD_ERROR_PERMISSION_DENIED ? PHOTO_FILE_LOAD_ERROR_PERMISSION_DENIED : ret == EVAS_LOAD_ERROR_RESOURCE_ALLOCATION_FAILED ? PHOTO_FILE_LOAD_ERROR_RESOURCE_ALLOCATION_FAILED : ret == EVAS_LOAD_ERROR_CORRUPT_FILE ? PHOTO_FILE_LOAD_ERROR_CORRUPT_FILE : ret == EVAS_LOAD_ERROR_UNKNOWN_FORMAT ? PHOTO_FILE_LOAD_ERROR_UNKNOWN_FORMAT : PHOTO_FILE_LOAD_ERROR_GENERIC ); return EINA_FALSE; } EOLIAN static void _efl_ui_image_zoomable_efl_file_file_get(Eo *obj EINA_UNUSED, Efl_Ui_Image_Zoomable_Data *sd, const char **file, const char **key) { if (file) *file = sd->file; if (key) *key = NULL; } EOLIAN static void _efl_ui_image_zoomable_efl_ui_zoom_zoom_set(Eo *obj, Efl_Ui_Image_Zoomable_Data *sd, double zoom) { double z; Eina_List *l; Efl_Ui_Image_Zoomable_Grid *g, *g_zoom = NULL; Evas_Coord pw, ph; int zoom_changed = 0, started = 0; Eina_Bool an = EINA_FALSE; Eina_Rect view = {}; Eina_Position2D pos = {}; if (zoom <= (1.0 / 256.0)) zoom = (1.0 / 256.0); if (EINA_DBL_EQ(zoom, sd->zoom)) return; sd->zoom = zoom; sd->size.ow = sd->size.w; sd->size.oh = sd->size.h; pos = efl_ui_scrollable_content_pos_get(sd->smanager); view = efl_ui_scrollable_viewport_geometry_get(sd->smanager); if ((view.w <= 0) || (view.h <= 0)) return; if (sd->mode == ELM_PHOTOCAM_ZOOM_MODE_MANUAL) { sd->size.nw = (double)sd->size.imw / sd->zoom; sd->size.nh = (double)sd->size.imh / sd->zoom; } else if (sd->mode == ELM_PHOTOCAM_ZOOM_MODE_AUTO_FIT) { if ((sd->size.imw < 1) || (sd->size.imh < 1)) { sd->size.nw = 0; sd->size.nh = 0; } else { ph = (sd->size.imh * view.w) / sd->size.imw; if (ph > view.h) { pw = (sd->size.imw * view.h) / sd->size.imh; ph = view.h; } else { pw = view.w; } if (sd->size.imw > sd->size.imh) z = (double)sd->size.imw / pw; else z = (double)sd->size.imh / ph; if (!EINA_DBL_EQ(z, sd->zoom)) zoom_changed = 1; sd->zoom = z; sd->size.nw = pw; sd->size.nh = ph; } } else if (sd->mode == ELM_PHOTOCAM_ZOOM_MODE_AUTO_FILL) { if ((sd->size.imw < 1) || (sd->size.imh < 1)) { sd->size.nw = 0; sd->size.nh = 0; } else { ph = (sd->size.imh * view.w) / sd->size.imw; if (ph < view.h) { pw = (sd->size.imw * view.h) / sd->size.imh; ph = view.h; } else { pw = view.w; } if (sd->size.imw > sd->size.imh) z = (double)sd->size.imw / pw; else z = (double)sd->size.imh / ph; if (z != sd->zoom) zoom_changed = 1; sd->zoom = z; sd->size.nw = pw; sd->size.nh = ph; } } else if (sd->mode == ELM_PHOTOCAM_ZOOM_MODE_AUTO_FIT_IN) { if ((sd->size.imw < 1) || (sd->size.imh < 1)) { sd->size.nw = 0; sd->size.nh = 0; } else if ((sd->size.imw < view.w) && (sd->size.imh < view.h)) { if (!EINA_DBL_EQ(sd->zoom, 1)) zoom_changed = 1; sd->zoom = 1; sd->size.nw = sd->size.imw; sd->size.nh = sd->size.imh; } else { ph = (sd->size.imh * view.w) / sd->size.imw; if (ph > view.h) { pw = (sd->size.imw * view.h) / sd->size.imh; ph = view.h; } else pw = view.w; if (sd->size.imw > sd->size.imh) z = (double)sd->size.imw / pw; else z = (double)sd->size.imh / ph; if (EINA_DBL_EQ(z, sd->zoom)) zoom_changed = 1; sd->zoom = z; sd->size.nw = pw; sd->size.nh = ph; } } if (sd->main_load_pending) { sd->size.w = sd->size.nw; sd->size.h = sd->size.nh; goto done; } EINA_LIST_FOREACH(sd->grids, l, g) { if (g->zoom == _grid_zoom_calc(sd->zoom)) { sd->grids = eina_list_remove(sd->grids, g); sd->grids = eina_list_prepend(sd->grids, g); _grid_raise(g); goto done; } } g = _grid_create(obj); if (g) { if (eina_list_count(sd->grids) > 1) { g_zoom = eina_list_last(sd->grids)->data; sd->grids = eina_list_remove(sd->grids, g_zoom); _grid_clear(obj, g_zoom); free(g_zoom); EINA_LIST_FOREACH(sd->grids, l, g_zoom) { g_zoom->dead = 1; } } sd->grids = eina_list_prepend(sd->grids, g); } else { EINA_LIST_FREE(sd->grids, g) { _grid_clear(obj, g); free(g); } } done: sd->t_start = ecore_loop_time_get(); sd->t_end = sd->t_start + _elm_config->zoom_friction; if ((sd->size.w > 0) && (sd->size.h > 0)) { sd->size.spos.x = (double)(pos.x + (view.w / 2)) / (double)sd->size.w; sd->size.spos.y = (double)(pos.y + (view.h / 2)) / (double)sd->size.h; } else { sd->size.spos.x = 0.5; sd->size.spos.y = 0.5; } if (view.w > sd->size.w) sd->size.spos.x = 0.5; if (view.h > sd->size.h) sd->size.spos.y = 0.5; if (sd->size.spos.x > 1.0) sd->size.spos.x = 1.0; if (sd->size.spos.y > 1.0) sd->size.spos.y = 1.0; if (sd->paused) { _zoom_do(obj, 1.0); } else { an = efl_event_callback_del(obj, EFL_EVENT_ANIMATOR_TICK, _zoom_anim_cb, obj); efl_event_callback_add(obj, EFL_EVENT_ANIMATOR_TICK, _zoom_anim_cb, obj); if (!an) { sd->no_smooth++; if (sd->no_smooth == 1) _smooth_update(obj); started = 1; } } if (an) { // FIXME: If one day we do support partial animator in photocam, this would require change Efl_Event event = {}; event.object = evas_object_evas_get(obj); _zoom_anim_cb(obj, &event); // FIXME: Unhandled. if (0) { _efl_ui_image_zoomable_bounce_reset(obj, sd); an = 0; } } ecore_job_del(sd->calc_job); sd->calc_job = ecore_job_add(_calc_job_cb, obj); if (!sd->paused) { if (started) efl_event_callback_legacy_call(obj, EFL_UI_EVENT_ZOOM_START, NULL); if (!an) efl_event_callback_legacy_call(obj, EFL_UI_EVENT_ZOOM_STOP, NULL); } if (zoom_changed) efl_event_callback_legacy_call(obj, EFL_UI_EVENT_ZOOM_CHANGE, NULL); } EOLIAN static double _efl_ui_image_zoomable_efl_ui_zoom_zoom_get(Eo *obj EINA_UNUSED, Efl_Ui_Image_Zoomable_Data *sd) { return sd->zoom; } EOLIAN static void _efl_ui_image_zoomable_efl_ui_zoom_zoom_mode_set(Eo *obj, Efl_Ui_Image_Zoomable_Data *sd, Elm_Photocam_Zoom_Mode mode) { double tz; if (sd->mode == mode) return; sd->mode = mode; tz = sd->zoom; sd->zoom = 0.0; elm_photocam_zoom_set(obj, tz); } EOLIAN static Elm_Photocam_Zoom_Mode _efl_ui_image_zoomable_efl_ui_zoom_zoom_mode_get(Eo *obj EINA_UNUSED, Efl_Ui_Image_Zoomable_Data *sd) { return sd->mode; } EOLIAN static Eina_Size2D _efl_ui_image_zoomable_efl_gfx_view_view_size_get(Eo *obj EINA_UNUSED, Efl_Ui_Image_Zoomable_Data *pd) { return EINA_SIZE2D(pd->size.imw, pd->size.imh); } EOLIAN static Eina_Rect _efl_ui_image_zoomable_image_region_get(Eo *obj EINA_UNUSED, Efl_Ui_Image_Zoomable_Data *sd) { Eina_Rect region = {}; Eina_Position2D pos = efl_ui_scrollable_content_pos_get(sd->smanager); Eina_Rect view = efl_ui_scrollable_viewport_geometry_get(sd->smanager); if (sd->size.w > 0) { region.x = (sd->size.imw * pos.x) / sd->size.w; if (region.x > sd->size.imw) region.x = sd->size.imw; region.w = (sd->size.imw * view.w) / sd->size.w; if (region.w > sd->size.imw) region.w = sd->size.imw; else if (region.w < 0) region.w = 0; } if (sd->size.h > 0) { region.y = (sd->size.imh * pos.y) / sd->size.h; if (region.y > sd->size.imh) region.y = sd->size.imh; region.h = (sd->size.imh * view.h) / sd->size.h; if (region.h > sd->size.imh) region.h = sd->size.imh; else if (region.h < 0) region.h = 0; } return region; } EOLIAN static void _efl_ui_image_zoomable_image_region_set(Eo *obj, Efl_Ui_Image_Zoomable_Data *sd, Eina_Rect region) { int rx, ry, rw, rh; if ((sd->size.imw < 1) || (sd->size.imh < 1)) return; rx = (region.x * sd->size.w) / sd->size.imw; ry = (region.y * sd->size.h) / sd->size.imh; rw = (region.w * sd->size.w) / sd->size.imw; rh = (region.h * sd->size.h) / sd->size.imh; if (rw < 1) rw = 1; if (rh < 1) rh = 1; if ((rx + rw) > sd->size.w) rx = sd->size.w - rw; if ((ry + rh) > sd->size.h) ry = sd->size.h - rh; _efl_ui_image_zoomable_bounce_reset(obj, sd); _efl_ui_image_zoomable_zoom_reset(obj, sd); efl_ui_scrollable_scroll(sd->smanager, EINA_RECT(rx, ry, rw, rh), EINA_FALSE); } EOLIAN static void _efl_ui_image_zoomable_efl_ui_scrollable_interactive_scroll(Eo *obj, Efl_Ui_Image_Zoomable_Data *sd, Eina_Rect rc, Eina_Bool animation) { int rx, ry, rw, rh; if ((sd->size.imw < 1) || (sd->size.imh < 1)) return; rx = (rc.x * sd->size.w) / sd->size.imw; ry = (rc.y * sd->size.h) / sd->size.imh; rw = (rc.w * sd->size.w) / sd->size.imw; rh = (rc.h * sd->size.h) / sd->size.imh; if (rw < 1) rw = 1; if (rh < 1) rh = 1; if ((rx + rw) > sd->size.w) rx = sd->size.w - rw; if ((ry + rh) > sd->size.h) ry = sd->size.h - rh; _efl_ui_image_zoomable_bounce_reset(obj, sd); _efl_ui_image_zoomable_zoom_reset(obj, sd); efl_ui_scrollable_scroll(sd->smanager, EINA_RECT(rx, ry, rw, rh), animation); } EOLIAN static void _efl_ui_image_zoomable_efl_ui_zoom_zoom_animation_set(Eo *obj, Efl_Ui_Image_Zoomable_Data *sd, Eina_Bool paused) { paused = !!paused; if (sd->paused == paused) return; sd->paused = paused; if (!sd->paused) return; _efl_ui_image_zoomable_bounce_reset(obj, sd); _efl_ui_image_zoomable_zoom_reset(obj, sd); } EOLIAN static Eina_Bool _efl_ui_image_zoomable_efl_ui_zoom_zoom_animation_get(Eo *obj EINA_UNUSED, Efl_Ui_Image_Zoomable_Data *sd) { return sd->paused; } EOLIAN static void _efl_ui_image_zoomable_gesture_enabled_set(Eo *obj, Efl_Ui_Image_Zoomable_Data *sd, Eina_Bool gesture) { gesture = !!gesture; if (sd->do_gesture == gesture) return; sd->do_gesture = gesture; ELM_SAFE_FREE(sd->g_layer, evas_object_del); if (!gesture) return; sd->g_layer = elm_gesture_layer_add(obj); if (!sd->g_layer) return; elm_gesture_layer_attach(sd->g_layer, obj); elm_gesture_layer_cb_set (sd->g_layer, ELM_GESTURE_ZOOM, ELM_GESTURE_STATE_START, _g_layer_zoom_start_cb, obj); elm_gesture_layer_cb_set (sd->g_layer, ELM_GESTURE_ZOOM, ELM_GESTURE_STATE_MOVE, _g_layer_zoom_move_cb, obj); elm_gesture_layer_cb_set (sd->g_layer, ELM_GESTURE_ZOOM, ELM_GESTURE_STATE_END, _g_layer_zoom_end_cb, obj); elm_gesture_layer_cb_set (sd->g_layer, ELM_GESTURE_ZOOM, ELM_GESTURE_STATE_ABORT, _g_layer_zoom_end_cb, obj); } static void _min_obj_size_get(Evas_Object *o, int *w, int *h) { EFL_UI_IMAGE_ZOOMABLE_DATA_GET(o, sd); *w = sd->size.imw; *h = sd->size.imh; if (*w == 0 || *h == 0) { evas_object_geometry_get(o, NULL, NULL, w, h); if (*w == 0 || *h == 0) { edje_object_size_min_get(sd->edje, w, h); } } } static Eina_Bool _image_zoomable_object_icon_set(Evas_Object *o, const char *group, char *style, Eina_Bool resize) { Elm_Theme *th = elm_widget_theme_get(o); EFL_UI_IMAGE_ZOOMABLE_DATA_GET(o, sd); char buf[1024]; Eina_File *f; if (!th) th = elm_theme_default_get(); snprintf(buf, sizeof(buf), "elm/icon/%s/%s", group, style); f = _elm_theme_group_file_find(th, buf); if (f) { if (sd->f) eina_file_close(sd->f); eina_stringshare_replace(&sd->file, eina_file_filename_get(f)); sd->f = f; return _img_proxy_set(o, sd, NULL, f, buf, resize); } ELM_SAFE_FREE(sd->edje, evas_object_del); _sizing_eval(o); WRN("Failed to set icon '%s'. Icon theme '%s' not found", group, buf); ELM_SAFE_FREE(sd->f, eina_file_close); return EINA_FALSE; } static Eina_Bool _icon_standard_set(Evas_Object *obj, const char *name, Eina_Bool resize) { EFL_UI_IMAGE_ZOOMABLE_DATA_GET(obj, sd); if (_image_zoomable_object_icon_set(obj, name, "default", resize)) { /* TODO: elm_unneed_efreet() */ sd->freedesktop.use = EINA_FALSE; return EINA_TRUE; } return EINA_FALSE; } static Eina_Bool _icon_freedesktop_set(Evas_Object *obj, const char *name, int size) { const char *path; EFL_UI_IMAGE_ZOOMABLE_DATA_GET(obj, sd); elm_need_efreet(); if (icon_theme == NON_EXISTING) return EINA_FALSE; if (!icon_theme) { Efreet_Icon_Theme *theme; /* TODO: Listen for EFREET_EVENT_ICON_CACHE_UPDATE */ theme = efreet_icon_theme_find(elm_config_icon_theme_get()); if (!theme) { const char **itr; static const char *themes[] = { "gnome", "Human", "oxygen", "hicolor", NULL }; for (itr = themes; *itr; itr++) { theme = efreet_icon_theme_find(*itr); if (theme) break; } } if (!theme) { icon_theme = NON_EXISTING; return EINA_FALSE; } else icon_theme = eina_stringshare_add(theme->name.internal); } path = efreet_icon_path_find(icon_theme, name, size); sd->freedesktop.use = !!path; if (sd->freedesktop.use) { sd->freedesktop.requested_size = size; efl_file_set(obj, path, NULL); return EINA_TRUE; } return EINA_FALSE; } static inline int _icon_size_min_get(Evas_Object *image) { int w, h; evas_object_geometry_get(image, NULL, NULL, &w, &h); return MAX(16, MIN(w, h)); } /* FIXME: move this code to ecore */ #ifdef _WIN32 static Eina_Bool _path_is_absolute(const char *path) { //TODO: Check if this works with all absolute paths in windows return (isalpha(*path)) && (*(path + 1) == ':') && ((*(path + 2) == '\\') || (*(path + 2) == '/')); } #else static Eina_Bool _path_is_absolute(const char *path) { return *path == '/'; } #endif static Eina_Bool _internal_efl_ui_image_zoomable_icon_set(Evas_Object *obj, const char *name, Eina_Bool *fdo, Eina_Bool resize) { char *tmp; Eina_Bool ret = EINA_FALSE; EFL_UI_IMAGE_ZOOMABLE_DATA_GET(obj, sd); /* try locating the icon using the specified theme */ if (!strcmp(ELM_CONFIG_ICON_THEME_ELEMENTARY, elm_config_icon_theme_get())) { ret = _icon_standard_set(obj, name, resize); if (ret && fdo) *fdo = EINA_FALSE; } else { ret = _icon_freedesktop_set(obj, name, _icon_size_min_get(obj)); if (ret && fdo) *fdo = EINA_TRUE; } if (ret) { eina_stringshare_replace(&sd->stdicon, name); _sizing_eval(obj); return EINA_TRUE; } else eina_stringshare_replace(&sd->stdicon, NULL); if (_path_is_absolute(name)) { if (fdo) *fdo = EINA_FALSE; return efl_file_set(obj, name, NULL); } /* if that fails, see if icon name is in the format size/name. if so, try locating a fallback without the size specification */ if (!(tmp = strchr(name, '/'))) return EINA_FALSE; ++tmp; if (*tmp) return _internal_efl_ui_image_zoomable_icon_set(obj, tmp, fdo, resize); /* give up */ return EINA_FALSE; } static void _efl_ui_image_zoomable_icon_resize_cb(void *data, Evas *e EINA_UNUSED, Evas_Object *obj, void *event_info EINA_UNUSED) { EFL_UI_IMAGE_ZOOMABLE_DATA_GET(data, sd); const char *refup = eina_stringshare_ref(sd->stdicon); Eina_Bool fdo = EINA_FALSE; if (!_internal_efl_ui_image_zoomable_icon_set(obj, sd->stdicon, &fdo, EINA_TRUE) || (!fdo)) evas_object_event_callback_del_full (obj, EVAS_CALLBACK_RESIZE, _efl_ui_image_zoomable_icon_resize_cb, data); eina_stringshare_del(refup); } EOLIAN static Eina_Bool _efl_ui_image_zoomable_efl_ui_image_icon_set(Eo *obj, Efl_Ui_Image_Zoomable_Data *pd EINA_UNUSED, const char *name) { Eina_Bool fdo = EINA_FALSE; if (!name) return EINA_FALSE; evas_object_event_callback_del_full (obj, EVAS_CALLBACK_RESIZE, _efl_ui_image_zoomable_icon_resize_cb, obj); Eina_Bool int_ret = _internal_efl_ui_image_zoomable_icon_set(obj, name, &fdo, EINA_FALSE); if (fdo) evas_object_event_callback_add (obj, EVAS_CALLBACK_RESIZE, _efl_ui_image_zoomable_icon_resize_cb, obj); return int_ret; } EOLIAN static const char * _efl_ui_image_zoomable_efl_ui_image_icon_get(Eo *obj EINA_UNUSED, Efl_Ui_Image_Zoomable_Data *pd) { return pd->stdicon; } EOLIAN static Eina_Bool _efl_ui_image_zoomable_gesture_enabled_get(Eo *obj EINA_UNUSED, Efl_Ui_Image_Zoomable_Data *sd) { return sd->do_gesture; } EOLIAN static Eina_Bool _efl_ui_image_zoomable_efl_player_playable_get(Eo *obj EINA_UNUSED, Efl_Ui_Image_Zoomable_Data *sd) { if (sd->edje) return EINA_TRUE; return evas_object_image_animated_get(sd->img); } static Eina_Bool _efl_ui_image_zoomable_animated_get_internal(const Eo *obj EINA_UNUSED, Efl_Ui_Image_Zoomable_Data *sd) { if (sd->edje) return edje_object_animation_get(sd->edje); return sd->anim; } static void _efl_ui_image_zoomable_animated_set_internal(Eo *obj EINA_UNUSED, Efl_Ui_Image_Zoomable_Data *sd, Eina_Bool anim) { anim = !!anim; if (sd->anim == anim) return; sd->anim = anim; if (sd->edje) { edje_object_animation_set(sd->edje, anim); return; } if (!evas_object_image_animated_get(sd->img)) return; if (anim) { sd->frame_count = evas_object_image_animated_frame_count_get(sd->img); sd->cur_frame = 1; sd->frame_duration = evas_object_image_animated_frame_duration_get (sd->img, sd->cur_frame, 0); evas_object_image_animated_frame_set(sd->img, sd->cur_frame); } else { sd->frame_count = -1; sd->cur_frame = -1; sd->frame_duration = -1; } } static Eina_Bool _efl_ui_image_zoomable_animate_cb(void *data) { EFL_UI_IMAGE_ZOOMABLE_DATA_GET(data, sd); _grid_clear_all(data); if (!sd->anim) return ECORE_CALLBACK_CANCEL; sd->cur_frame++; if ((sd->frame_count > 0) && (sd->cur_frame > sd->frame_count)) sd->cur_frame = sd->cur_frame % sd->frame_count; evas_object_image_animated_frame_set(sd->img, sd->cur_frame); sd->frame_duration = evas_object_image_animated_frame_duration_get (sd->img, sd->cur_frame, 0); if (sd->frame_duration > 0) ecore_timer_interval_set(sd->anim_timer, sd->frame_duration); return ECORE_CALLBACK_RENEW; } static void _efl_ui_image_zoomable_animated_play_set_internal(Eo *obj, Efl_Ui_Image_Zoomable_Data *sd, Eina_Bool play) { if (!sd->anim) return; if (sd->play == play) return; sd->play = play; if (sd->edje) { edje_object_play_set(sd->edje, play); return; } if (play) { sd->anim_timer = ecore_timer_add (sd->frame_duration, _efl_ui_image_zoomable_animate_cb, obj); } else { ELM_SAFE_FREE(sd->anim_timer, ecore_timer_del); } } EOLIAN static void _efl_ui_image_zoomable_efl_player_play_set(Eo *obj, Efl_Ui_Image_Zoomable_Data *sd, Eina_Bool play) { evas_object_image_preload(sd->img, EINA_FALSE); if (play && !_efl_ui_image_zoomable_animated_get_internal(obj, sd)) _efl_ui_image_zoomable_animated_set_internal(obj, sd, play); _efl_ui_image_zoomable_animated_play_set_internal(obj, sd, play); } static Eina_Bool _efl_ui_image_zoomable_animated_play_get_internal(const Eo *obj EINA_UNUSED, Efl_Ui_Image_Zoomable_Data *sd) { if (sd->edje) return edje_object_play_get(sd->edje); return sd->play; } EOLIAN static Eina_Bool _efl_ui_image_zoomable_efl_player_play_get(Eo *obj, Efl_Ui_Image_Zoomable_Data *sd) { return _efl_ui_image_zoomable_animated_play_get_internal(obj, sd); } EOLIAN static void _efl_ui_image_zoomable_class_constructor(Efl_Class *klass) { evas_smart_legacy_type_register(MY_CLASS_NAME_LEGACY, klass); PHOTO_FILE_LOAD_ERROR_GENERIC = eina_error_msg_static_register("Generic load error"); PHOTO_FILE_LOAD_ERROR_DOES_NOT_EXIST = eina_error_msg_static_register("File does not exist"); PHOTO_FILE_LOAD_ERROR_PERMISSION_DENIED = eina_error_msg_static_register("Permission denied to an existing file"); PHOTO_FILE_LOAD_ERROR_RESOURCE_ALLOCATION_FAILED = eina_error_msg_static_register("Allocation of resources failure prevented load"); PHOTO_FILE_LOAD_ERROR_CORRUPT_FILE = eina_error_msg_static_register("File corrupt (but was detected as a known format)"); PHOTO_FILE_LOAD_ERROR_UNKNOWN_FORMAT = eina_error_msg_static_register("File is not a known format"); } EOLIAN const Efl_Access_Action_Data * _efl_ui_image_zoomable_efl_access_widget_action_elm_actions_get(Eo *obj EINA_UNUSED, Efl_Ui_Image_Zoomable_Data *pd EINA_UNUSED) { static Efl_Access_Action_Data atspi_actions[] = { { "move,prior", "move", "prior", _key_action_move}, { "move,next", "move", "next", _key_action_move}, { "move,left", "move", "left", _key_action_move}, { "move,right", "move", "right", _key_action_move}, { "move,up", "move", "up", _key_action_move}, { "move,down", "move", "down", _key_action_move}, { "zoom,in", "zoom", "in", _key_action_zoom}, { "zoom,out", "zoom", "out", _key_action_zoom}, { NULL, NULL, NULL, NULL } }; return &atspi_actions[0]; } /* Legacy APIs */ EAPI Evas_Object * elm_photocam_add(Evas_Object *parent) { EINA_SAFETY_ON_NULL_RETURN_VAL(parent, NULL); return elm_legacy_add(MY_CLASS, parent); } static inline void _evas_orient_to_eo_orient_flip(const Evas_Image_Orient evas_orient, Efl_Orient *orient, Efl_Flip *flip) { switch (evas_orient) { case EVAS_IMAGE_ORIENT_NONE: *orient = EFL_ORIENT_NONE; *flip = EFL_FLIP_NONE; break; case EVAS_IMAGE_ORIENT_90: *orient = EFL_ORIENT_90; *flip = EFL_FLIP_NONE; break; case EVAS_IMAGE_ORIENT_180: *orient = EFL_ORIENT_180; *flip = EFL_FLIP_NONE; break; case EVAS_IMAGE_ORIENT_270: *orient = EFL_ORIENT_270; *flip = EFL_FLIP_NONE; break; case EVAS_IMAGE_FLIP_HORIZONTAL: *orient = EFL_ORIENT_NONE; *flip = EFL_FLIP_HORIZONTAL; break; case EVAS_IMAGE_FLIP_VERTICAL: *orient = EFL_ORIENT_NONE; *flip = EFL_FLIP_VERTICAL; break; case EVAS_IMAGE_FLIP_TRANSVERSE: *orient = EFL_ORIENT_270; *flip = EFL_FLIP_HORIZONTAL; break; case EVAS_IMAGE_FLIP_TRANSPOSE: *orient = EFL_ORIENT_270; *flip = EFL_FLIP_VERTICAL; break; default: *orient = EFL_ORIENT_NONE; *flip = EFL_FLIP_NONE; break; } } static inline Evas_Image_Orient _eo_orient_flip_to_evas_orient(Efl_Orient orient, Efl_Flip flip) { switch (flip) { default: case EFL_FLIP_NONE: switch (orient) { default: case EFL_ORIENT_0: return EVAS_IMAGE_ORIENT_0; case EFL_ORIENT_90: return EVAS_IMAGE_ORIENT_90; case EFL_ORIENT_180: return EVAS_IMAGE_ORIENT_180; case EFL_ORIENT_270: return EVAS_IMAGE_ORIENT_270; } case EFL_FLIP_HORIZONTAL: switch (orient) { default: case EFL_ORIENT_0: return EVAS_IMAGE_FLIP_HORIZONTAL; case EFL_ORIENT_90: return EVAS_IMAGE_FLIP_TRANSPOSE; case EFL_ORIENT_180: return EVAS_IMAGE_FLIP_VERTICAL; case EFL_ORIENT_270: return EVAS_IMAGE_FLIP_TRANSVERSE; } case EFL_FLIP_VERTICAL: switch (orient) { default: case EFL_ORIENT_0: return EVAS_IMAGE_FLIP_VERTICAL; case EFL_ORIENT_90: return EVAS_IMAGE_FLIP_TRANSVERSE; case EFL_ORIENT_180: return EVAS_IMAGE_FLIP_HORIZONTAL; case EFL_ORIENT_270: return EVAS_IMAGE_FLIP_TRANSPOSE; } } } EAPI void elm_photocam_image_orient_set(Eo *obj, Evas_Image_Orient evas_orient) { Efl_Orient orient; Efl_Flip flip; _evas_orient_to_eo_orient_flip(evas_orient, &orient, &flip); efl_orientation_set(obj, orient); efl_orientation_flip_set(obj, flip); } EAPI Evas_Image_Orient elm_photocam_image_orient_get(const Eo *obj) { ELM_PHOTOCAM_CHECK(obj) EVAS_IMAGE_ORIENT_NONE; EFL_UI_IMAGE_ZOOMABLE_DATA_GET(obj, sd); return _eo_orient_flip_to_evas_orient(sd->orient, sd->flip); } EAPI Evas_Object* elm_photocam_internal_image_get(const Evas_Object *obj) { EFL_UI_IMAGE_ZOOMABLE_DATA_GET_OR_RETURN_VAL(obj, sd, NULL); return sd->img; } EAPI void elm_photocam_image_size_get(const Evas_Object *obj, int *w, int *h) { Eina_Size2D sz; sz = efl_gfx_view_size_get(obj); if (w) *w = sz.w; if (h) *h = sz.h; } EAPI Eina_Bool elm_photocam_paused_get(const Evas_Object *obj) { return efl_ui_zoom_animation_get(obj); } EAPI void elm_photocam_paused_set(Evas_Object *obj, Eina_Bool paused) { efl_ui_zoom_animation_set(obj, paused); } EAPI void elm_photocam_zoom_set(Evas_Object *obj, double zoom) { efl_ui_zoom_set(obj, zoom); } EAPI double elm_photocam_zoom_get(const Evas_Object *obj) { return efl_ui_zoom_get(obj); } EAPI void elm_photocam_zoom_mode_set(Evas_Object *obj, Elm_Photocam_Zoom_Mode mode) { efl_ui_zoom_mode_set(obj, mode); } EAPI Elm_Photocam_Zoom_Mode elm_photocam_zoom_mode_get(const Evas_Object *obj) { return efl_ui_zoom_mode_get(obj); } EAPI Evas_Load_Error elm_photocam_file_set(Evas_Object *obj, const char *file) { ELM_PHOTOCAM_CHECK(obj) EVAS_LOAD_ERROR_NONE; EINA_SAFETY_ON_NULL_RETURN_VAL(file, EVAS_LOAD_ERROR_NONE); if (efl_file_set(obj, file, NULL)) return EVAS_LOAD_ERROR_NONE; Eina_Error err = eina_error_get(); return err == PHOTO_FILE_LOAD_ERROR_DOES_NOT_EXIST ? EVAS_LOAD_ERROR_DOES_NOT_EXIST : err == PHOTO_FILE_LOAD_ERROR_PERMISSION_DENIED ? EVAS_LOAD_ERROR_PERMISSION_DENIED : err == PHOTO_FILE_LOAD_ERROR_RESOURCE_ALLOCATION_FAILED ? EVAS_LOAD_ERROR_RESOURCE_ALLOCATION_FAILED : err == PHOTO_FILE_LOAD_ERROR_CORRUPT_FILE ? EVAS_LOAD_ERROR_CORRUPT_FILE : err == PHOTO_FILE_LOAD_ERROR_UNKNOWN_FORMAT ? EVAS_LOAD_ERROR_UNKNOWN_FORMAT : EVAS_LOAD_ERROR_GENERIC; } EAPI const char* elm_photocam_file_get(const Evas_Object *obj) { const char *ret = NULL; efl_file_get(obj, &ret, NULL); return ret; } EAPI void elm_photocam_image_region_show(Evas_Object *obj, int x, int y, int w, int h) { efl_ui_image_zoomable_image_region_set(obj, EINA_RECT(x, y, w, h)); } EAPI void elm_photocam_image_region_bring_in(Evas_Object *obj, int x, int y, int w, int h EINA_UNUSED) { ELM_PHOTOCAM_CHECK(obj); EFL_UI_IMAGE_ZOOMABLE_DATA_GET(obj, sd); efl_ui_scrollable_scroll(sd->smanager, EINA_RECT(x, y, w, h), EINA_TRUE); } EAPI void elm_photocam_bounce_set(Evas_Object *obj, Eina_Bool h_bounce, Eina_Bool v_bounce) { ELM_PHOTOCAM_CHECK(obj); efl_ui_scrollable_bounce_enabled_set(obj, h_bounce, v_bounce); } EAPI void elm_photocam_bounce_get(const Evas_Object *obj, Eina_Bool *h_bounce, Eina_Bool *v_bounce) { ELM_PHOTOCAM_CHECK(obj); efl_ui_scrollable_bounce_enabled_get((Eo *)obj, h_bounce, v_bounce); } EAPI void elm_photocam_image_region_get(const Efl_Ui_Image_Zoomable *obj, int *x, int *y, int *w, int *h) { Eina_Rect r; r = efl_ui_image_zoomable_image_region_get(obj); if (x) *x = r.x; if (y) *y = r.y; if (w) *w = r.w; if (h) *h = r.h; } /* Standard widget overrides */ ELM_WIDGET_KEY_DOWN_DEFAULT_IMPLEMENT(efl_ui_image_zoomable, Efl_Ui_Image_Zoomable_Data) /* Internal EO APIs and hidden overrides */ #define EFL_UI_IMAGE_ZOOMABLE_EXTRA_OPS \ EFL_CANVAS_GROUP_ADD_DEL_OPS(efl_ui_image_zoomable) #include "efl_ui_image_zoomable.eo.c" diff --git a/src/lib/elementary/efl_ui_image_zoomable.eo b/src/lib/elementary/efl_ui_image_zoomable.eo index bbe5124552..789457cf07 100644 --- a/src/lib/elementary/efl_ui_image_zoomable.eo +++ b/src/lib/elementary/efl_ui_image_zoomable.eo @@ -1,86 +1,86 @@ class Efl.Ui.Image_Zoomable (Efl.Ui.Widget, Efl.Ui.Image, Efl.Ui.Zoom, Efl.Ui.Scrollable.Interactive, Efl.Ui.Scrollbar) { [[Elementary Image Zoomable class]] legacy_prefix: elm_photocam; methods { @property gesture_enabled { set { [[Set the gesture state for photocam. This sets the gesture state to on or off for photocam. The default is off. This will start multi touch zooming. ]] } get { [[Get the gesture state for photocam. This gets the current gesture state for the photocam object. ]] } values { gesture: bool; [[The gesture state.]] } } @property image_region { get { [[Get the region of the image that is currently shown See also @.image_region.set. ]] legacy: null; } set { [[Set the viewed region of the image This shows the region of the image without using animation. @since 1.20 ]] legacy: null; } values { region: Eina.Rect; [[The region in the original image pixels.]] } } } implements { class.constructor; Efl.Object.constructor; Efl.Gfx.position { set; } Efl.Gfx.size { set; } Efl.Gfx.View.view_size { get; } Efl.Image.image_size { get; } Efl.Ui.Image.icon { set; get; } Efl.Player.playable { get; } Efl.Player.play { get; set; } Efl.Ui.Zoom.zoom_animation { set; get; } Efl.Ui.Zoom.zoom { set; get; } Efl.Ui.Zoom.zoom_mode { set; get; } Efl.Canvas.Group.group_member_add; Efl.Ui.Widget.theme_apply; - Efl.Ui.Widget.on_focus_update; + Efl.Ui.Focus.Object.on_focus_update; Efl.Ui.Widget.widget_event; Efl.Ui.Scrollable.Interactive.scroll; Efl.Access.Widget.Action.elm_actions { get; } Efl.File.file { get; set; } Efl.Orientation.orientation { get; set; } Efl.Orientation.flip { get; set; } Efl.Layout.Group.group_size_min { get; } Efl.Layout.Group.group_size_max { get; } Efl.Layout.Signal.signal_callback_add; Efl.Layout.Signal.signal_callback_del; //Efl.Canvas.Layout_Group.group_data { get; } } events { press; [[Called when photocam got pressed]] load; [[Called when photocam loading started]] loaded; [[Called when photocam loading finished]] load,detail; [[Called when photocal detail loading started]] loaded,detail; [[Called when photocam detail loading finished]] download,start; [[Called when photocam download started]] download,progress; [[Called when photocam download progress updated]] download,done; [[Called when photocam download finished]] download,error; [[Called when photocam download failed]] } } diff --git a/src/lib/elementary/efl_ui_layout.c b/src/lib/elementary/efl_ui_layout.c index 96d0b09ba7..102077f331 100644 --- a/src/lib/elementary/efl_ui_layout.c +++ b/src/lib/elementary/efl_ui_layout.c @@ -1,2660 +1,2660 @@ #ifdef HAVE_CONFIG_H # include "elementary_config.h" #endif #define EFL_ACCESS_PROTECTED #define ELM_LAYOUT_PROTECTED #define EFL_UI_WIDGET_PART_BG_PROTECTED #include #include "elm_priv.h" #include "elm_widget_layout.h" #include "elm_part_helper.h" #include "elm_entry.eo.h" #define MY_CLASS EFL_UI_LAYOUT_CLASS #define MY_CLASS_PFX efl_ui_layout #define MY_CLASS_NAME "Efl.Ui.Layout" #define MY_CLASS_NAME_LEGACY "elm_layout" Eo *_efl_ui_layout_pack_proxy_get(Efl_Ui_Layout *obj, Edje_Part_Type type, const char *part); static void _efl_model_properties_changed_cb(void *, const Efl_Event *); static Eina_Bool _efl_ui_layout_part_cursor_unset(Efl_Ui_Layout_Data *sd, const char *part_name); static const char SIG_THEME_CHANGED[] = "theme,changed"; const char SIG_LAYOUT_FOCUSED[] = "focused"; const char SIG_LAYOUT_UNFOCUSED[] = "unfocused"; const char SIGNAL_PREFIX[] = "signal/"; /* smart callbacks coming from elm layout objects: */ static const Evas_Smart_Cb_Description _smart_callbacks[] = { {SIG_THEME_CHANGED, ""}, {SIG_LAYOUT_FOCUSED, ""}, {SIG_LAYOUT_UNFOCUSED, ""}, {SIG_WIDGET_LANG_CHANGED, ""}, /**< handled by elm_widget */ {SIG_WIDGET_ACCESS_CHANGED, ""}, /**< handled by elm_widget */ {NULL, NULL} }; static const Elm_Layout_Part_Alias_Description _text_aliases[] = { {"default", "elm.text"}, {NULL, NULL} }; static const Elm_Layout_Part_Alias_Description _content_aliases[] = { {"default", "elm.swallow.content"}, {NULL, NULL} }; static const char *_efl_ui_layout_swallow_parts[] = { "elm.swallow.icon", "elm.swallow.end", "elm.swallow.background", NULL }; /* these are data operated by layout's class functions internally, and * should not be messed up by inhering classes */ typedef struct _Efl_Ui_Layout_Sub_Object_Data Efl_Ui_Layout_Sub_Object_Data; typedef struct _Efl_Ui_Layout_Sub_Object_Cursor Efl_Ui_Layout_Sub_Object_Cursor; typedef struct _Efl_Ui_Layout_Sub_Iterator Efl_Ui_Layout_Sub_Iterator; typedef struct _Efl_Ui_Layout_Sub_Connect Efl_Ui_Layout_Sub_Connect; struct _Efl_Ui_Layout_Sub_Iterator { Eina_Iterator iterator; Eina_Iterator *real_iterator; Efl_Ui_Layout *object; }; struct _Efl_Ui_Layout_Sub_Object_Data { const char *part; Evas_Object *obj; enum { SWALLOW, BOX_APPEND, BOX_PREPEND, BOX_INSERT_BEFORE, BOX_INSERT_AT, TABLE_PACK, TEXT } type; union { union { const Evas_Object *reference; unsigned int pos; } box; struct { unsigned short col, row, colspan, rowspan; } table; } p; }; struct _Efl_Ui_Layout_Sub_Object_Cursor { Evas_Object *obj; const char *part; const char *cursor; const char *style; Eina_Bool engine_only : 1; }; struct _Efl_Ui_Layout_Sub_Connect { Eina_Stringshare *name; Eina_Stringshare *property; Eina_Bool is_signal; Eo *obj; Efl_Future *future; }; static void _on_sub_object_size_hint_change(void *data, Evas *e EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED) { ELM_WIDGET_DATA_GET_OR_RETURN(data, wd); elm_layout_sizing_eval(data); } static void _part_cursor_free(Efl_Ui_Layout_Sub_Object_Cursor *pc) { eina_stringshare_del(pc->part); eina_stringshare_del(pc->style); eina_stringshare_del(pc->cursor); free(pc); } static void _sizing_eval(Evas_Object *obj, Efl_Ui_Layout_Data *sd) { Evas_Coord minh = -1, minw = -1; Evas_Coord rest_w = 0, rest_h = 0; ELM_WIDGET_DATA_GET_OR_RETURN(sd->obj, wd); if (sd->restricted_calc_w) rest_w = wd->w; if (sd->restricted_calc_h) rest_h = wd->h; edje_object_size_min_restricted_calc(wd->resize_obj, &minw, &minh, rest_w, rest_h); evas_object_size_hint_min_set(obj, minw, minh); sd->restricted_calc_w = sd->restricted_calc_h = EINA_FALSE; } /* common content cases for layout objects: icon and text */ static inline void _icon_signal_emit(Efl_Ui_Layout_Data *sd, Efl_Ui_Layout_Sub_Object_Data *sub_d, Eina_Bool visible) { char buf[1024]; const char *type; Eo *edje; int i; edje = elm_widget_resize_object_get(sd->obj); if (!edje) return; //FIXME: Don't limit to the icon and end here. // send signals for all contents after elm 2.0 if (sub_d->type != SWALLOW) return; for (i = 0;; i++) { if (!_efl_ui_layout_swallow_parts[i]) return; if (!strcmp(sub_d->part, _efl_ui_layout_swallow_parts[i])) break; } if (!strncmp(sub_d->part, "elm.swallow.", strlen("elm.swallow."))) type = sub_d->part + strlen("elm.swallow."); else type = sub_d->part; snprintf(buf, sizeof(buf), "elm,state,%s,%s", type, visible ? "visible" : "hidden"); edje_object_signal_emit(edje, buf, "elm"); /* themes might need immediate action here */ edje_object_message_signal_process(edje); } static inline void _text_signal_emit(Efl_Ui_Layout_Data *sd, Efl_Ui_Layout_Sub_Object_Data *sub_d, Eina_Bool visible) { char buf[1024]; const char *type; //FIXME: Don't limit to "elm.text" prefix. //Send signals for all text parts after elm 2.0 if ((sub_d->type != TEXT) || (!((!strcmp("elm.text", sub_d->part)) || (!strncmp("elm.text.", sub_d->part, 9))))) return; ELM_WIDGET_DATA_GET_OR_RETURN(sd->obj, wd); if (!strncmp(sub_d->part, "elm.text.", strlen("elm.text."))) type = sub_d->part + strlen("elm.text."); else type = sub_d->part; snprintf(buf, sizeof(buf), "elm,state,%s,%s", type, visible ? "visible" : "hidden"); edje_object_signal_emit(wd->resize_obj, buf, "elm"); /* TODO: is this right? It was like that, but IMO it should be removed: */ snprintf(buf, sizeof(buf), visible ? "elm,state,text,visible" : "elm,state,text,hidden"); edje_object_signal_emit(wd->resize_obj, buf, "elm"); /* themes might need immediate action here */ edje_object_message_signal_process(wd->resize_obj); } static void _parts_signals_emit(Efl_Ui_Layout_Data *sd) { const Eina_List *l; Efl_Ui_Layout_Sub_Object_Data *sub_d; EINA_LIST_FOREACH(sd->subs, l, sub_d) { _icon_signal_emit(sd, sub_d, EINA_TRUE); _text_signal_emit(sd, sub_d, EINA_TRUE); } } static void _part_cursor_part_apply(const Efl_Ui_Layout_Sub_Object_Cursor *pc) { elm_object_cursor_set(pc->obj, pc->cursor); elm_object_cursor_style_set(pc->obj, pc->style); elm_object_cursor_theme_search_enabled_set(pc->obj, !pc->engine_only); } static void _parts_cursors_apply(Efl_Ui_Layout_Data *sd) { const Eina_List *l; const char *file, *group; Efl_Ui_Layout_Sub_Object_Cursor *pc; ELM_WIDGET_DATA_GET_OR_RETURN(sd->obj, wd); edje_object_file_get(wd->resize_obj, &file, &group); EINA_LIST_FOREACH(sd->parts_cursors, l, pc) { Evas_Object *obj = (Evas_Object *)edje_object_part_object_get (wd->resize_obj, pc->part); if (!obj) { pc->obj = NULL; WRN("no part '%s' in group '%s' of file '%s'. " "Cannot set cursor '%s'", pc->part, group, file, pc->cursor); continue; } else if (evas_object_pass_events_get(obj)) { pc->obj = NULL; WRN("part '%s' in group '%s' of file '%s' has mouse_events: 0. " "Cannot set cursor '%s'", pc->part, group, file, pc->cursor); continue; } pc->obj = obj; _part_cursor_part_apply(pc); } } static void _efl_ui_layout_highlight_in_theme(Evas_Object *obj) { const char *fh; ELM_WIDGET_DATA_GET_OR_RETURN(obj, wd); fh = edje_object_data_get (wd->resize_obj, "focus_highlight"); if ((fh) && (!strcmp(fh, "on"))) elm_widget_highlight_in_theme_set(obj, EINA_TRUE); else elm_widget_highlight_in_theme_set(obj, EINA_FALSE); fh = edje_object_data_get (wd->resize_obj, "access_highlight"); if ((fh) && (!strcmp(fh, "on"))) elm_widget_access_highlight_in_theme_set(obj, EINA_TRUE); else elm_widget_access_highlight_in_theme_set(obj, EINA_FALSE); } static Eina_Bool _visuals_refresh(Evas_Object *obj, Efl_Ui_Layout_Data *sd) { Eina_Bool ret = EINA_FALSE; ELM_WIDGET_DATA_GET_OR_RETURN(obj, wd, EINA_FALSE); _parts_signals_emit(sd); _parts_cursors_apply(sd); edje_object_mirrored_set (wd->resize_obj, efl_ui_mirrored_get(obj)); edje_object_scale_set (wd->resize_obj, efl_gfx_scale_get(obj) * elm_config_scale_get()); _efl_ui_layout_highlight_in_theme(obj); ret = efl_ui_widget_on_disabled_update(obj, elm_widget_disabled_get(obj)); elm_layout_sizing_eval(obj); return ret; } EOLIAN static Eina_Bool _efl_ui_layout_efl_ui_widget_on_disabled_update(Eo *obj, Efl_Ui_Layout_Data *_pd EINA_UNUSED, Eina_Bool disabled) { ELM_WIDGET_DATA_GET_OR_RETURN(obj, wd, EINA_FALSE); // Not calling efl_super here: Elm.Widget simply returns false. if (disabled) edje_object_signal_emit (wd->resize_obj, "elm,state,disabled", "elm"); else edje_object_signal_emit (wd->resize_obj, "elm,state,enabled", "elm"); return EINA_TRUE; } static Efl_Ui_Theme_Apply _efl_ui_layout_theme_internal(Eo *obj, Efl_Ui_Layout_Data *sd) { Efl_Ui_Theme_Apply ret = EFL_UI_THEME_APPLY_FAILED; ELM_WIDGET_DATA_GET_OR_RETURN(obj, wd, EFL_UI_THEME_APPLY_FAILED); /* function already prints error messages, if any */ if (!sd->file_set) { ret = elm_widget_theme_object_set (obj, wd->resize_obj, elm_widget_theme_klass_get(obj), elm_widget_theme_element_get(obj), elm_widget_theme_style_get(obj)); } if (ret) efl_event_callback_legacy_call(obj, EFL_UI_LAYOUT_EVENT_THEME_CHANGED, NULL); if (!_visuals_refresh(obj, sd)) ret = EFL_UI_THEME_APPLY_FAILED; return ret; } EOLIAN static Efl_Ui_Theme_Apply _efl_ui_layout_efl_ui_widget_theme_apply(Eo *obj, Efl_Ui_Layout_Data *sd) { Efl_Ui_Theme_Apply theme_apply = EFL_UI_THEME_APPLY_FAILED; theme_apply = efl_ui_widget_theme_apply(efl_super(obj, MY_CLASS)); if (!theme_apply) return EFL_UI_THEME_APPLY_FAILED; theme_apply &= _efl_ui_layout_theme_internal(obj, sd); return theme_apply; } EOLIAN static Eina_Bool -_efl_ui_layout_efl_ui_widget_on_focus_update(Eo *obj, Efl_Ui_Layout_Data *_pd EINA_UNUSED) +_efl_ui_layout_efl_ui_focus_object_on_focus_update(Eo *obj, Efl_Ui_Layout_Data *_pd EINA_UNUSED) { ELM_WIDGET_DATA_GET_OR_RETURN(obj, wd, EINA_FALSE); if (!elm_widget_can_focus_get(obj)) return EINA_FALSE; if (efl_ui_focus_object_focus_get(obj)) { elm_layout_signal_emit(obj, "elm,action,focus", "elm"); evas_object_focus_set(wd->resize_obj, EINA_TRUE); } else { elm_layout_signal_emit(obj, "elm,action,unfocus", "elm"); evas_object_focus_set(wd->resize_obj, EINA_FALSE); } - efl_ui_widget_on_focus_update(efl_super(obj, MY_CLASS)); + efl_ui_focus_object_on_focus_update(efl_super(obj, MY_CLASS)); if (efl_isa(wd->resize_obj, EFL_CANVAS_LAYOUT_CLASS)) edje_object_message_signal_process(wd->resize_obj); return EINA_TRUE; } EOLIAN static Eina_Bool _efl_ui_layout_efl_ui_widget_widget_sub_object_add(Eo *obj, Efl_Ui_Layout_Data *_pd EINA_UNUSED, Evas_Object *sobj) { Eina_Bool int_ret = EINA_FALSE; if (evas_object_data_get(sobj, "elm-parent") == obj) return EINA_TRUE; int_ret = elm_widget_sub_object_add(efl_super(obj, MY_CLASS), sobj); if (!int_ret) return EINA_FALSE; evas_object_event_callback_add (sobj, EVAS_CALLBACK_CHANGED_SIZE_HINTS, _on_sub_object_size_hint_change, obj); return EINA_TRUE; } EOLIAN static Eina_Bool _efl_ui_layout_efl_ui_widget_widget_sub_object_del(Eo *obj, Efl_Ui_Layout_Data *sd, Evas_Object *sobj) { Eina_List *l; Efl_Ui_Layout_Sub_Object_Data *sub_d; Eina_Bool int_ret = EINA_FALSE; ELM_WIDGET_DATA_GET_OR_RETURN(obj, wd, EINA_FALSE); evas_object_event_callback_del_full (sobj, EVAS_CALLBACK_CHANGED_SIZE_HINTS, _on_sub_object_size_hint_change, obj); int_ret = elm_widget_sub_object_del(efl_super(obj, MY_CLASS), sobj); if (!int_ret) return EINA_FALSE; if (sd->destructed_is) return EINA_TRUE; EINA_LIST_FOREACH(sd->subs, l, sub_d) { if (sub_d->obj != sobj) continue; sd->subs = eina_list_remove_list(sd->subs, l); _icon_signal_emit(sd, sub_d, EINA_FALSE); eina_stringshare_del(sub_d->part); free(sub_d); break; } elm_layout_sizing_eval(obj); return EINA_TRUE; } static void _edje_signal_callback(void *data, Evas_Object *obj EINA_UNUSED, const char *emission, const char *source) { Edje_Signal_Data *esd = data; esd->func(esd->data, esd->obj, emission, source); } EAPI Eina_Bool _elm_layout_part_aliasing_eval(const Evas_Object *obj, const char **part, Eina_Bool is_text) { const Elm_Layout_Part_Alias_Description *aliases = NULL; if (is_text) aliases = elm_layout_text_aliases_get(obj); else aliases = elm_layout_content_aliases_get(obj); while (aliases && aliases->alias && aliases->real_part) { /* NULL matches the 1st */ if ((!*part) || (!strcmp(*part, aliases->alias))) { *part = aliases->real_part; break; } aliases++; } if (!*part) { ERR("no default content part set for object %p -- " "part must not be NULL", obj); return EINA_FALSE; } /* if no match, part goes on with the same value */ return EINA_TRUE; } static void _eo_unparent_helper(Eo *child, Eo *parent) { if (efl_parent_get(child) == parent) { efl_parent_set(child, evas_object_evas_get(parent)); } } static void _box_reference_del(void *data, Evas *e EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED) { Efl_Ui_Layout_Sub_Object_Data *sub_d = data; sub_d->p.box.reference = NULL; } static Evas_Object * _sub_box_remove(Evas_Object *obj, Efl_Ui_Layout_Data *sd, Efl_Ui_Layout_Sub_Object_Data *sub_d) { Evas_Object *child = sub_d->obj; /* sub_d will die in * _efl_ui_layout_smart_widget_sub_object_del */ if (sub_d->type == BOX_INSERT_BEFORE) evas_object_event_callback_del_full ((Evas_Object *)sub_d->p.box.reference, EVAS_CALLBACK_DEL, _box_reference_del, sub_d); ELM_WIDGET_DATA_GET_OR_RETURN(sd->obj, wd, NULL); edje_object_part_box_remove (wd->resize_obj, sub_d->part, child); _eo_unparent_helper(child, obj); if (!_elm_widget_sub_object_redirect_to_top(obj, child)) { ERR("could not remove sub object %p from %p", child, obj); return NULL; } return child; } static Eina_Bool _sub_box_is(const Efl_Ui_Layout_Sub_Object_Data *sub_d) { switch (sub_d->type) { case BOX_APPEND: case BOX_PREPEND: case BOX_INSERT_BEFORE: case BOX_INSERT_AT: return EINA_TRUE; default: return EINA_FALSE; } } static Evas_Object * _sub_table_remove(Evas_Object *obj, Efl_Ui_Layout_Data *sd, Efl_Ui_Layout_Sub_Object_Data *sub_d) { Evas_Object *child; ELM_WIDGET_DATA_GET_OR_RETURN(sd->obj, wd, NULL); child = sub_d->obj; /* sub_d will die in _efl_ui_layout_smart_widget_sub_object_del */ edje_object_part_table_unpack (wd->resize_obj, sub_d->part, child); _eo_unparent_helper(child, obj); if (!_elm_widget_sub_object_redirect_to_top(obj, child)) { ERR("could not remove sub object %p from %p", child, obj); return NULL; } return child; } static void _on_size_evaluate_signal(void *data, Evas_Object *obj EINA_UNUSED, const char *emission EINA_UNUSED, const char *source EINA_UNUSED) { elm_layout_sizing_eval(data); } EOLIAN static void _efl_ui_layout_efl_canvas_group_group_add(Eo *obj, Efl_Ui_Layout_Data *_pd EINA_UNUSED) { Evas_Object *edje; elm_widget_sub_object_parent_add(obj); /* has to be there *before* parent's smart_add() */ edje = edje_object_add(evas_object_evas_get(obj)); elm_widget_resize_object_set(obj, edje); efl_canvas_group_add(efl_super(obj, MY_CLASS)); elm_widget_can_focus_set(obj, EINA_FALSE); edje_object_signal_callback_add (edje, "size,eval", "elm", _on_size_evaluate_signal, obj); elm_layout_sizing_eval(obj); } EOLIAN static void _efl_ui_layout_efl_canvas_group_group_del(Eo *obj, Efl_Ui_Layout_Data *sd) { Efl_Ui_Layout_Sub_Object_Data *sub_d; Efl_Ui_Layout_Sub_Object_Cursor *pc; Efl_Ui_Layout_Sub_Connect *sc; Edje_Signal_Data *esd; Evas_Object *child; Eina_List *l, *ll; ELM_WIDGET_DATA_GET_OR_RETURN(obj, wd); elm_layout_freeze(obj); EINA_LIST_FOREACH_SAFE(sd->subs, l, ll, sub_d) efl_ui_widget_sub_object_del(obj, sub_d->obj); EINA_LIST_FREE(sd->parts_cursors, pc) _part_cursor_free(pc); EINA_LIST_FREE(sd->edje_signals, esd) { edje_object_signal_callback_del_full (wd->resize_obj, esd->emission, esd->source, _edje_signal_callback, esd); eina_stringshare_del(esd->emission); eina_stringshare_del(esd->source); free(esd); } if(sd->model) { efl_event_callback_del(sd->model, EFL_MODEL_EVENT_PROPERTIES_CHANGED, _efl_model_properties_changed_cb, sd); efl_unref(sd->model); sd->model = NULL; } EINA_LIST_FREE(sd->prop_connect, sc) { if (sc->future) efl_future_cancel(sc->future); sc->future = NULL; eina_stringshare_del(sc->name); eina_stringshare_del(sc->property); free(sc); } sd->prop_connect = NULL; eina_hash_free(sd->factories); sd->factories = NULL; /* let's make our Edje object the *last* to be processed, since it * may (smart) parent other sub objects here */ EINA_LIST_FOREACH(wd->subobjs, l, child) { if (child == wd->resize_obj) { wd->subobjs = eina_list_demote_list(wd->subobjs, l); break; } } sd->destructed_is = EINA_TRUE; efl_canvas_group_del(efl_super(obj, MY_CLASS)); } /* rewrite or extend this one on your derived class as to suit your * needs */ EOLIAN static void _efl_ui_layout_efl_canvas_group_group_calculate(Eo *obj, Efl_Ui_Layout_Data *sd) { if (sd->needs_size_calc) { _sizing_eval(obj, sd); sd->needs_size_calc = EINA_FALSE; } } static Efl_Ui_Layout_Sub_Object_Cursor * _parts_cursors_find(Efl_Ui_Layout_Data *sd, const char *part) { const Eina_List *l; Efl_Ui_Layout_Sub_Object_Cursor *pc; EINA_LIST_FOREACH(sd->parts_cursors, l, pc) { if (!strcmp(pc->part, part)) return pc; } return NULL; } /* The public functions down here are meant to operate on whichever * widget inheriting from elm_layout */ EOLIAN static Eina_Bool _efl_ui_layout_efl_file_file_set(Eo *obj, Efl_Ui_Layout_Data *sd, const char *file, const char *group) { Eina_Bool int_ret = EINA_FALSE; ELM_WIDGET_DATA_GET_OR_RETURN(obj, wd, EINA_FALSE); int_ret = edje_object_file_set(wd->resize_obj, file, group); if (int_ret) { sd->file_set = EINA_TRUE; _visuals_refresh(obj, sd); } else ERR("failed to set edje file '%s', group '%s': %s", file, group, edje_load_error_str (edje_object_load_error_get(wd->resize_obj))); return int_ret; } EOLIAN static void _efl_ui_layout_efl_file_file_get(Eo *obj, Efl_Ui_Layout_Data *sd EINA_UNUSED, const char **file, const char **group) { ELM_WIDGET_DATA_GET_OR_RETURN(obj, wd); edje_object_file_get(wd->resize_obj, file, group); } EOLIAN static Eina_Bool _efl_ui_layout_efl_file_mmap_set(Eo *obj, Efl_Ui_Layout_Data *sd, const Eina_File *file, const char *group) { Eina_Bool int_ret = EINA_FALSE; ELM_WIDGET_DATA_GET_OR_RETURN(obj, wd, EINA_FALSE); int_ret = edje_object_mmap_set(wd->resize_obj, file, group); if (int_ret) { sd->file_set = EINA_TRUE; _visuals_refresh(obj, sd); } else ERR("failed to set edje mmap file %p, group '%s': %s", file, group, edje_load_error_str (edje_object_load_error_get(wd->resize_obj))); return int_ret; } EOLIAN static void _efl_ui_layout_efl_file_mmap_get(Eo *obj, Efl_Ui_Layout_Data *sd EINA_UNUSED, const Eina_File **file, const char **group) { ELM_WIDGET_DATA_GET_OR_RETURN(obj, wd); efl_file_mmap_get(wd->resize_obj, file, group); } EOLIAN static Efl_Ui_Theme_Apply _efl_ui_layout_theme_set(Eo *obj, Efl_Ui_Layout_Data *sd, const char *klass, const char *group, const char *style) { Eina_Bool changed = EINA_FALSE; ELM_WIDGET_DATA_GET_OR_RETURN(obj, wd, EINA_FALSE); if (!wd->legacy && efl_finalized_get(obj)) { ERR("Efl.Ui.Layout.theme can only be set before finalize!"); return EFL_UI_THEME_APPLY_FAILED; } if (sd->file_set) sd->file_set = EINA_FALSE; changed |= elm_widget_theme_klass_set(obj, klass); changed |= elm_widget_theme_element_set(obj, group); changed |= elm_widget_theme_style_set(obj, style); if (changed) return efl_ui_widget_theme_apply(obj); return EFL_UI_THEME_APPLY_SUCCESS; } EOLIAN static void _efl_ui_layout_efl_layout_signal_signal_emit(Eo *obj, Efl_Ui_Layout_Data *_pd EINA_UNUSED, const char *emission, const char *source) { // Don't do anything else than call forward here ELM_WIDGET_DATA_GET_OR_RETURN(obj, wd); efl_layout_signal_emit(wd->resize_obj, emission, source); } EOLIAN static Eina_Bool _efl_ui_layout_efl_layout_signal_signal_callback_add(Eo *obj, Efl_Ui_Layout_Data *_pd EINA_UNUSED, const char *emission, const char *source, Efl_Signal_Cb func, void *data) { // Don't do anything else than call forward here ELM_WIDGET_DATA_GET_OR_RETURN(obj, wd, EINA_FALSE); return efl_layout_signal_callback_add(wd->resize_obj, emission, source, func, data); } EOLIAN static Eina_Bool _efl_ui_layout_efl_layout_signal_signal_callback_del(Eo *obj, Efl_Ui_Layout_Data *_pd EINA_UNUSED, const char *emission, const char *source, Edje_Signal_Cb func, void *data) { // Don't do anything else than call forward here ELM_WIDGET_DATA_GET_OR_RETURN(obj, wd, EINA_FALSE); return efl_layout_signal_callback_add(wd->resize_obj, emission, source, func, data); } // TODO: // - message_send // - message_signal_process // and also message handler (not implemented yet as an EO interface!) EAPI Eina_Bool elm_layout_content_set(Evas_Object *obj, const char *swallow, Evas_Object *content) { EFL_UI_LAYOUT_CHECK(obj) EINA_FALSE; if (!swallow) { swallow = efl_ui_widget_default_content_part_get(obj); if (!swallow) return EINA_FALSE; } else if (!_elm_layout_part_aliasing_eval(obj, &swallow, EINA_FALSE)) return EINA_FALSE; return efl_content_set(efl_part(obj, swallow), content); } static Eina_Bool _efl_ui_layout_content_set(Eo *obj, Efl_Ui_Layout_Data *sd, const char *part, Evas_Object *content) { Efl_Ui_Layout_Sub_Object_Data *sub_d; Eina_List *l; ELM_WIDGET_DATA_GET_OR_RETURN(obj, wd, EINA_FALSE); EINA_LIST_FOREACH(sd->subs, l, sub_d) { if (sub_d->type == SWALLOW) { if (!strcmp(part, sub_d->part)) { if (content == sub_d->obj) goto end; _eo_unparent_helper(sub_d->obj, obj); evas_object_del(sub_d->obj); break; } /* was previously swallowed at another part -- mimic * edje_object_part_swallow()'s behavior, then */ else if (content == sub_d->obj) { sd->subs = eina_list_remove_list(sd->subs, l); _icon_signal_emit(sd, sub_d, EINA_FALSE); eina_stringshare_del(sub_d->part); free(sub_d); _elm_widget_sub_object_redirect_to_top(obj, content); break; } } } if (content) { if (!elm_widget_sub_object_add(obj, content)) return EINA_FALSE; if (!edje_object_part_swallow (wd->resize_obj, part, content)) { ERR("could not swallow %p into part '%s'", content, part); _elm_widget_sub_object_redirect_to_top(obj, content); return EINA_FALSE; } sub_d = ELM_NEW(Efl_Ui_Layout_Sub_Object_Data); if (!sub_d) { ERR("failed to allocate memory!"); edje_object_part_unswallow(wd->resize_obj, content); _elm_widget_sub_object_redirect_to_top(obj, content); return EINA_FALSE; } sub_d->type = SWALLOW; sub_d->part = eina_stringshare_add(part); sub_d->obj = content; sd->subs = eina_list_append(sd->subs, sub_d); efl_parent_set(content, obj); _icon_signal_emit(sd, sub_d, EINA_TRUE); } elm_layout_sizing_eval(obj); end: return EINA_TRUE; } EAPI Evas_Object * elm_layout_content_get(const Evas_Object *obj, const char *swallow) { EFL_UI_LAYOUT_CHECK(obj) NULL; if (!swallow) { swallow = efl_ui_widget_default_content_part_get(obj); if (!swallow) return NULL; } else if (!_elm_layout_part_aliasing_eval(obj, &swallow, EINA_FALSE)) return NULL; return efl_content_get(efl_part(obj, swallow)); } static Evas_Object* _efl_ui_layout_content_get(Eo *obj, Efl_Ui_Layout_Data *sd, const char *part) { const Eina_List *l; Efl_Ui_Layout_Sub_Object_Data *sub_d; EINA_LIST_FOREACH(sd->subs, l, sub_d) { if ((sub_d->type != TEXT) && !strcmp(part, sub_d->part)) { if (sub_d->type == SWALLOW) return sub_d->obj; } } ELM_WIDGET_DATA_GET_OR_RETURN(obj, wd, NULL); return efl_content_get(efl_part(wd->resize_obj, part)); } EAPI Evas_Object * elm_layout_content_unset(Evas_Object *obj, const char *swallow) { EFL_UI_LAYOUT_CHECK(obj) NULL; if (!swallow) { swallow = efl_ui_widget_default_content_part_get(obj); if (!swallow) return NULL; } else if (!_elm_layout_part_aliasing_eval(obj, &swallow, EINA_FALSE)) return NULL; return efl_content_unset(efl_part(obj, swallow)); } static Evas_Object* _efl_ui_layout_content_unset(Eo *obj, Efl_Ui_Layout_Data *sd, const char *part) { Efl_Ui_Layout_Sub_Object_Data *sub_d; Eina_List *l; ELM_WIDGET_DATA_GET_OR_RETURN(obj, wd, NULL); EINA_LIST_FOREACH(sd->subs, l, sub_d) { if ((sub_d->type == SWALLOW) && (!strcmp(part, sub_d->part))) { Evas_Object *content; if (!sub_d->obj) return NULL; content = sub_d->obj; /* sub_d will die in * _efl_ui_layout_smart_widget_sub_object_del */ if (!_elm_widget_sub_object_redirect_to_top(obj, content)) { ERR("could not remove sub object %p from %p", content, obj); return NULL; } edje_object_part_unswallow (wd->resize_obj, content); EINA_LIST_FOREACH(sd->subs, l, sub_d) { if (sub_d->obj == content) { sd->subs = eina_list_remove_list(sd->subs, l); _icon_signal_emit(sd, sub_d, EINA_FALSE); eina_stringshare_del(sub_d->part); free(sub_d); break; } } _eo_unparent_helper(content, obj); return content; } } return NULL; } EOLIAN static Eina_Bool _efl_ui_layout_efl_container_content_remove(Eo *obj, Efl_Ui_Layout_Data *sd EINA_UNUSED, Efl_Gfx *content) { ELM_WIDGET_DATA_GET_OR_RETURN(obj, wd, EINA_FALSE); if (!_elm_widget_sub_object_redirect_to_top(obj, content)) { ERR("could not remove sub object %p from %p", content, obj); return EINA_FALSE; } edje_object_part_unswallow(wd->resize_obj, content); _eo_unparent_helper(content, obj); return EINA_TRUE; } /* legacy only - eo is iterator */ EAPI Eina_List * elm_layout_content_swallow_list_get(const Evas_Object *obj) { EFL_UI_LAYOUT_CHECK(obj) NULL; Eina_List *ret = NULL; Efl_Ui_Layout_Sub_Object_Data *sub_d = NULL; Efl_Ui_Layout_Data *sd = efl_data_scope_get(obj, MY_CLASS); Eina_List *l = NULL; EINA_LIST_FOREACH(sd->subs, l, sub_d) { if (sub_d->type == SWALLOW) ret = eina_list_append(ret, sub_d->obj); } return ret; } static Eina_Bool _sub_iterator_next(Efl_Ui_Layout_Sub_Iterator *it, void **data) { Efl_Ui_Layout_Sub_Object_Data *sub; if (!eina_iterator_next(it->real_iterator, (void **)&sub)) return EINA_FALSE; if (data) *data = sub->obj; return EINA_TRUE; } static Efl_Ui_Layout * _sub_iterator_get_container(Efl_Ui_Layout_Sub_Iterator *it) { return it->object; } static void _sub_iterator_free(Efl_Ui_Layout_Sub_Iterator *it) { eina_iterator_free(it->real_iterator); free(it); } static Eina_Iterator * _sub_iterator_create(Eo *eo_obj, Efl_Ui_Layout_Data *sd) { Efl_Ui_Layout_Sub_Iterator *it; it = calloc(1, sizeof(*it)); if (!it) return NULL; EINA_MAGIC_SET(&it->iterator, EINA_MAGIC_ITERATOR); it->real_iterator = eina_list_iterator_new(sd->subs); it->iterator.version = EINA_ITERATOR_VERSION; it->iterator.next = FUNC_ITERATOR_NEXT(_sub_iterator_next); it->iterator.get_container = FUNC_ITERATOR_GET_CONTAINER(_sub_iterator_get_container); it->iterator.free = FUNC_ITERATOR_FREE(_sub_iterator_free); it->object = eo_obj; return &it->iterator; } EOLIAN static Eina_Iterator * _efl_ui_layout_efl_container_content_iterate(Eo *eo_obj EINA_UNUSED, Efl_Ui_Layout_Data *sd) { return _sub_iterator_create(eo_obj, sd); } EOLIAN static int _efl_ui_layout_efl_container_content_count(Eo *eo_obj EINA_UNUSED, Efl_Ui_Layout_Data *sd) { return eina_list_count(sd->subs); } static Eina_Bool _efl_ui_layout_text_generic_set(Eo *obj, Efl_Ui_Layout_Data *sd, const char *part, const char *text, Eina_Bool is_markup) { ELM_WIDGET_DATA_GET_OR_RETURN(obj, wd, EINA_FALSE); Eina_List *l; Efl_Ui_Layout_Sub_Object_Data *sub_d = NULL; Efl_Ui_Layout_Sub_Connect *sc; EINA_LIST_FOREACH(sd->subs, l, sub_d) { if ((sub_d->type == TEXT) && (!strcmp(part, sub_d->part))) { if (!text) { _text_signal_emit(sd, sub_d, EINA_FALSE); eina_stringshare_del(sub_d->part); free(sub_d); edje_object_part_text_escaped_set (wd->resize_obj, part, NULL); sd->subs = eina_list_remove_list(sd->subs, l); elm_layout_sizing_eval(obj); return EINA_TRUE; } else break; } } if (!text) return EINA_TRUE; if (wd->legacy) { if (!edje_object_part_text_escaped_set (wd->resize_obj, part, text)) return EINA_FALSE; } else if (is_markup) { efl_text_markup_set(efl_part(wd->resize_obj, part), text); } else { efl_text_set(efl_part(wd->resize_obj, part), text); } if (!sub_d) { sub_d = ELM_NEW(Efl_Ui_Layout_Sub_Object_Data); if (!sub_d) return EINA_FALSE; sub_d->type = TEXT; sub_d->part = eina_stringshare_add(part); sd->subs = eina_list_append(sd->subs, sub_d); } _text_signal_emit(sd, sub_d, EINA_TRUE); elm_layout_sizing_eval(obj); if (_elm_config->access_mode == ELM_ACCESS_MODE_ON && sd->can_access && !(sub_d->obj)) sub_d->obj = _elm_access_edje_object_part_object_register (obj, elm_layout_edje_get(obj), part); if (sd->model) { EINA_LIST_FOREACH(sd->prop_connect, l, sc) { if (sc->name == sub_d->part && !sd->view_updated) { Eina_Value v; eina_value_setup(&v, EINA_VALUE_TYPE_STRING); eina_value_set(&v, text); efl_model_property_set(sd->model, sc->property, &v); eina_value_flush(&v); break; } } } return EINA_TRUE; } static Eina_Bool _efl_ui_layout_text_set(Eo *obj, Efl_Ui_Layout_Data *sd, const char *part, const char *text) { return _efl_ui_layout_text_generic_set(obj, sd, part, text, EINA_FALSE); } static const char* _efl_ui_layout_text_get(Eo *obj, Efl_Ui_Layout_Data *sd EINA_UNUSED, const char *part) { ELM_WIDGET_DATA_GET_OR_RETURN(obj, wd, NULL); return efl_text_get(efl_part(wd->resize_obj, part)); } static const char* _efl_ui_layout_text_markup_get(Eo *obj, Efl_Ui_Layout_Data *sd EINA_UNUSED, const char *part) { ELM_WIDGET_DATA_GET_OR_RETURN(obj, wd, NULL); return efl_text_markup_get(efl_part(wd->resize_obj, part)); } static Eina_Bool _efl_ui_layout_text_markup_set(Eo *obj, Efl_Ui_Layout_Data *sd, const char *part, const char *text) { return _efl_ui_layout_text_generic_set(obj, sd, part, text, EINA_TRUE); } static void _layout_box_subobj_init(Efl_Ui_Layout_Data *sd, Efl_Ui_Layout_Sub_Object_Data *sub_d, const char *part, Evas_Object *child) { sub_d->part = eina_stringshare_add(part); sub_d->obj = child; sd->subs = eina_list_append(sd->subs, sub_d); efl_parent_set(child, sd->obj); } Eina_Bool _efl_ui_layout_box_append(Eo *obj, Efl_Ui_Layout_Data *sd, const char *part, Evas_Object *child) { Efl_Ui_Layout_Sub_Object_Data *sub_d; ELM_WIDGET_DATA_GET_OR_RETURN(obj, wd, EINA_FALSE); if (!edje_object_part_box_append (wd->resize_obj, part, child)) { ERR("child %p could not be appended to box part '%s'", child, part); return EINA_FALSE; } if (!elm_widget_sub_object_add(obj, child)) { edje_object_part_box_remove (wd->resize_obj, part, child); return EINA_FALSE; } sub_d = ELM_NEW(Efl_Ui_Layout_Sub_Object_Data); if (!sub_d) { ERR("failed to allocate memory!"); _elm_widget_sub_object_redirect_to_top(obj, child); edje_object_part_box_remove(wd->resize_obj, part, child); return EINA_FALSE; } sub_d->type = BOX_APPEND; _layout_box_subobj_init(sd, sub_d, part, child); elm_layout_sizing_eval(obj); return EINA_TRUE; } Eina_Bool _efl_ui_layout_box_prepend(Eo *obj, Efl_Ui_Layout_Data *sd, const char *part, Evas_Object *child) { Efl_Ui_Layout_Sub_Object_Data *sub_d; ELM_WIDGET_DATA_GET_OR_RETURN(obj, wd, EINA_FALSE); if (!edje_object_part_box_prepend (wd->resize_obj, part, child)) { ERR("child %p could not be prepended to box part '%s'", child, part); return EINA_FALSE; } if (!elm_widget_sub_object_add(obj, child)) { edje_object_part_box_remove (wd->resize_obj, part, child); return EINA_FALSE; } sub_d = ELM_NEW(Efl_Ui_Layout_Sub_Object_Data); if (!sub_d) { ERR("failed to allocate memory!"); _elm_widget_sub_object_redirect_to_top(obj, child); edje_object_part_box_remove(wd->resize_obj, part, child); return EINA_FALSE; } sub_d->type = BOX_PREPEND; _layout_box_subobj_init(sd, sub_d, part, child); elm_layout_sizing_eval(obj); return EINA_TRUE; } Eina_Bool _efl_ui_layout_box_insert_before(Eo *obj, Efl_Ui_Layout_Data *sd, const char *part, Evas_Object *child, const Evas_Object *reference) { Efl_Ui_Layout_Sub_Object_Data *sub_d; ELM_WIDGET_DATA_GET_OR_RETURN(obj, wd, EINA_FALSE); if (!edje_object_part_box_insert_before (wd->resize_obj, part, child, reference)) { ERR("child %p could not be inserted before %p inf box part '%s'", child, reference, part); return EINA_FALSE; } if (!elm_widget_sub_object_add(obj, child)) { edje_object_part_box_remove (wd->resize_obj, part, child); return EINA_FALSE; } sub_d = ELM_NEW(Efl_Ui_Layout_Sub_Object_Data); if (!sub_d) { ERR("failed to allocate memory!"); _elm_widget_sub_object_redirect_to_top(obj, child); edje_object_part_box_remove(wd->resize_obj, part, child); return EINA_FALSE; } sub_d->type = BOX_INSERT_BEFORE; sub_d->p.box.reference = reference; _layout_box_subobj_init(sd, sub_d, part, child); evas_object_event_callback_add ((Evas_Object *)reference, EVAS_CALLBACK_DEL, _box_reference_del, sub_d); elm_layout_sizing_eval(obj); return EINA_TRUE; } Eina_Bool _efl_ui_layout_box_insert_at(Eo *obj, Efl_Ui_Layout_Data *sd, const char *part, Evas_Object *child, unsigned int pos) { Efl_Ui_Layout_Sub_Object_Data *sub_d; ELM_WIDGET_DATA_GET_OR_RETURN(obj, wd, EINA_FALSE); if (!edje_object_part_box_insert_at (wd->resize_obj, part, child, pos)) { ERR("child %p could not be inserted at %u to box part '%s'", child, pos, part); return EINA_FALSE; } if (!elm_widget_sub_object_add(obj, child)) { edje_object_part_box_remove (wd->resize_obj, part, child); return EINA_FALSE; } sub_d = ELM_NEW(Efl_Ui_Layout_Sub_Object_Data); if (!sub_d) { ERR("failed to allocate memory!"); _elm_widget_sub_object_redirect_to_top(obj, child); edje_object_part_box_remove(wd->resize_obj, part, child); return EINA_FALSE; } sub_d->type = BOX_INSERT_AT; sub_d->p.box.pos = pos; _layout_box_subobj_init(sd, sub_d, part, child); elm_layout_sizing_eval(obj); return EINA_TRUE; } Evas_Object * _efl_ui_layout_box_remove(Eo *obj, Efl_Ui_Layout_Data *sd, const char *part, Evas_Object *child) { EINA_SAFETY_ON_NULL_RETURN_VAL(part, NULL); EINA_SAFETY_ON_NULL_RETURN_VAL(child, NULL); const Eina_List *l; Efl_Ui_Layout_Sub_Object_Data *sub_d; EINA_LIST_FOREACH(sd->subs, l, sub_d) { if (!_sub_box_is(sub_d)) continue; if ((sub_d->obj == child) && (!strcmp(sub_d->part, part))) return _sub_box_remove(obj, sd, sub_d); } return NULL; } Eina_Bool _efl_ui_layout_box_remove_all(Eo *obj, Efl_Ui_Layout_Data *sd, const char *part, Eina_Bool clear) { EINA_SAFETY_ON_NULL_RETURN_VAL(part, EINA_FALSE); ELM_WIDGET_DATA_GET_OR_RETURN(obj, wd, EINA_FALSE); Efl_Ui_Layout_Sub_Object_Data *sub_d; Eina_List *lst; lst = eina_list_clone(sd->subs); EINA_LIST_FREE(lst, sub_d) { if (!_sub_box_is(sub_d)) continue; if (!strcmp(sub_d->part, part)) { /* original item's deletion handled at sub-obj-del */ Evas_Object *child = _sub_box_remove(obj, sd, sub_d); if ((clear) && (child)) evas_object_del(child); } } /* eventually something may not be added with elm_layout, delete them * as well */ edje_object_part_box_remove_all (wd->resize_obj, part, clear); return EINA_TRUE; } Eina_Bool _efl_ui_layout_table_pack(Eo *obj, Efl_Ui_Layout_Data *sd, const char *part, Evas_Object *child, unsigned short col, unsigned short row, unsigned short colspan, unsigned short rowspan) { Efl_Ui_Layout_Sub_Object_Data *sub_d; ELM_WIDGET_DATA_GET_OR_RETURN(obj, wd, EINA_FALSE); if (!edje_object_part_table_pack (wd->resize_obj, part, child, col, row, colspan, rowspan)) { ERR("child %p could not be packed into table part '%s' col=%uh, row=%hu," " colspan=%hu, rowspan=%hu", child, part, col, row, colspan, rowspan); return EINA_FALSE; } if (!elm_widget_sub_object_add(obj, child)) { edje_object_part_table_unpack (wd->resize_obj, part, child); return EINA_FALSE; } sub_d = ELM_NEW(Efl_Ui_Layout_Sub_Object_Data); if (!sub_d) { ERR("failed to allocate memory!"); _elm_widget_sub_object_redirect_to_top(obj, child); edje_object_part_table_unpack(wd->resize_obj, part, child); return EINA_FALSE; } sub_d->type = TABLE_PACK; sub_d->part = eina_stringshare_add(part); sub_d->obj = child; sub_d->p.table.col = col; sub_d->p.table.row = row; sub_d->p.table.colspan = colspan; sub_d->p.table.rowspan = rowspan; sd->subs = eina_list_append(sd->subs, sub_d); efl_parent_set(child, obj); elm_layout_sizing_eval(obj); return EINA_TRUE; } Evas_Object * _efl_ui_layout_table_unpack(Eo *obj, Efl_Ui_Layout_Data *sd, const char *part, Evas_Object *child) { EINA_SAFETY_ON_NULL_RETURN_VAL(part, NULL); EINA_SAFETY_ON_NULL_RETURN_VAL(child, NULL); const Eina_List *l; Efl_Ui_Layout_Sub_Object_Data *sub_d; EINA_LIST_FOREACH(sd->subs, l, sub_d) { if (sub_d->type != TABLE_PACK) continue; if ((sub_d->obj == child) && (!strcmp(sub_d->part, part))) { return _sub_table_remove(obj, sd, sub_d); } } return NULL; } Eina_Bool _efl_ui_layout_table_clear(Eo *obj, Efl_Ui_Layout_Data *sd, const char *part, Eina_Bool clear) { EINA_SAFETY_ON_NULL_RETURN_VAL(part, EINA_FALSE); ELM_WIDGET_DATA_GET_OR_RETURN(obj, wd, EINA_FALSE); Efl_Ui_Layout_Sub_Object_Data *sub_d; Eina_List *lst; lst = eina_list_clone(sd->subs); EINA_LIST_FREE(lst, sub_d) { if (sub_d->type != TABLE_PACK) continue; if (!strcmp(sub_d->part, part)) { /* original item's deletion handled at sub-obj-del */ Evas_Object *child = _sub_table_remove(obj, sd, sub_d); if ((clear) && (child)) evas_object_del(child); } } /* eventually something may not be added with elm_layout, delete them * as well */ edje_object_part_table_clear(wd->resize_obj, part, clear); return EINA_TRUE; } EAPI Evas_Object* elm_layout_edje_get(const Eo *obj) { EINA_SAFETY_ON_FALSE_RETURN_VAL(efl_isa(obj, MY_CLASS), NULL); ELM_WIDGET_DATA_GET_OR_RETURN(obj, wd, NULL); return wd->resize_obj; } EOLIAN static const char * _efl_ui_layout_efl_layout_group_group_data_get(Eo *obj, Efl_Ui_Layout_Data *_pd EINA_UNUSED, const char *key) { ELM_WIDGET_DATA_GET_OR_RETURN(obj, wd, NULL); return efl_layout_group_data_get(wd->resize_obj, key); } EOLIAN static Eina_Size2D _efl_ui_layout_efl_layout_group_group_size_min_get(Eo *obj, Efl_Ui_Layout_Data *_pd EINA_UNUSED) { ELM_WIDGET_DATA_GET_OR_RETURN(obj, wd, EINA_SIZE2D(0, 0)); return efl_layout_group_size_min_get(wd->resize_obj); } EOLIAN static Eina_Size2D _efl_ui_layout_efl_layout_group_group_size_max_get(Eo *obj, Efl_Ui_Layout_Data *_pd EINA_UNUSED) { ELM_WIDGET_DATA_GET_OR_RETURN(obj, wd, EINA_SIZE2D(0, 0)); return efl_layout_group_size_max_get(wd->resize_obj); } EOLIAN static Eina_Bool _efl_ui_layout_efl_layout_group_part_exist_get(Eo *obj, Efl_Ui_Layout_Data *_pd EINA_UNUSED, const char *part) { ELM_WIDGET_DATA_GET_OR_RETURN(obj, wd, EINA_FALSE); return efl_layout_group_part_exist_get(wd->resize_obj, part); } /* layout's sizing evaluation is deferred. evaluation requests are * queued up and only flag the object as 'changed'. when it comes to * Evas's rendering phase, it will be addressed, finally (see * _efl_ui_layout_smart_calculate()). */ static void _elm_layout_sizing_eval(Eo *obj, Efl_Ui_Layout_Data *sd) { if (sd->frozen) return; if (sd->needs_size_calc) return; sd->needs_size_calc = EINA_TRUE; evas_object_smart_changed(obj); } EAPI void elm_layout_sizing_restricted_eval(Eo *obj, Eina_Bool w, Eina_Bool h) { Efl_Ui_Layout_Data *sd = efl_data_scope_safe_get(obj, MY_CLASS); if (!sd) return; sd->restricted_calc_w = !!w; sd->restricted_calc_h = !!h; evas_object_smart_changed(obj); } EOLIAN static int _efl_ui_layout_efl_layout_calc_calc_freeze(Eo *obj, Efl_Ui_Layout_Data *sd) { ELM_WIDGET_DATA_GET_OR_RETURN(obj, wd, 0); if ((sd->frozen)++ != 0) return sd->frozen; edje_object_freeze(wd->resize_obj); return 1; } EOLIAN static int _efl_ui_layout_efl_layout_calc_calc_thaw(Eo *obj, Efl_Ui_Layout_Data *sd) { ELM_WIDGET_DATA_GET_OR_RETURN(obj, wd, 0); if (--(sd->frozen) != 0) return sd->frozen; edje_object_thaw(wd->resize_obj); elm_layout_sizing_eval(obj); return 0; } static Eina_Bool _efl_ui_layout_part_cursor_set(Efl_Ui_Layout_Data *sd, const char *part_name, const char *cursor) { Evas_Object *part_obj; Efl_Ui_Layout_Sub_Object_Cursor *pc; Eo *obj = sd->obj; ELM_WIDGET_DATA_GET_OR_RETURN(obj, wd, EINA_FALSE); EINA_SAFETY_ON_NULL_RETURN_VAL(part_name, EINA_FALSE); if (!cursor) return _efl_ui_layout_part_cursor_unset(sd, part_name); part_obj = (Evas_Object *)edje_object_part_object_get (wd->resize_obj, part_name); if (!part_obj) { const char *group, *file; edje_object_file_get(wd->resize_obj, &file, &group); ERR("no part '%s' in group '%s' of file '%s'. Cannot set cursor '%s'", part_name, group, file, cursor); return EINA_FALSE; } if (evas_object_pass_events_get(part_obj)) { const char *group, *file; edje_object_file_get(wd->resize_obj, &file, &group); ERR("part '%s' in group '%s' of file '%s' has mouse_events: 0. " "Cannot set cursor '%s'", part_name, group, file, cursor); return EINA_FALSE; } pc = _parts_cursors_find(sd, part_name); if (pc) eina_stringshare_replace(&pc->cursor, cursor); else { pc = calloc(1, sizeof(*pc)); if (!pc) { ERR("failed to allocate memory!"); return EINA_FALSE; } pc->part = eina_stringshare_add(part_name); pc->cursor = eina_stringshare_add(cursor); pc->style = eina_stringshare_add("default"); sd->parts_cursors = eina_list_append(sd->parts_cursors, pc); } pc->obj = part_obj; elm_object_sub_cursor_set(part_obj, obj, pc->cursor); return EINA_TRUE; } static const char * _efl_ui_layout_part_cursor_get(Efl_Ui_Layout_Data *sd, const char *part_name) { EINA_SAFETY_ON_NULL_RETURN_VAL(part_name, NULL); Efl_Ui_Layout_Sub_Object_Cursor *pc = _parts_cursors_find(sd, part_name); EINA_SAFETY_ON_NULL_RETURN_VAL(pc, NULL); EINA_SAFETY_ON_NULL_RETURN_VAL(pc->obj, NULL); return elm_object_cursor_get(pc->obj); } static Eina_Bool _efl_ui_layout_part_cursor_unset(Efl_Ui_Layout_Data *sd, const char *part_name) { EINA_SAFETY_ON_NULL_RETURN_VAL(part_name, EINA_FALSE); Eina_List *l; Efl_Ui_Layout_Sub_Object_Cursor *pc; EINA_LIST_FOREACH(sd->parts_cursors, l, pc) { if (!strcmp(part_name, pc->part)) { if (pc->obj) elm_object_cursor_unset(pc->obj); _part_cursor_free(pc); sd->parts_cursors = eina_list_remove_list(sd->parts_cursors, l); return EINA_TRUE; } } return EINA_FALSE; } static Eina_Bool _efl_ui_layout_part_cursor_style_set(Efl_Ui_Layout_Data *sd, const char *part_name, const char *style) { EINA_SAFETY_ON_NULL_RETURN_VAL(part_name, EINA_FALSE); Efl_Ui_Layout_Sub_Object_Cursor *pc = _parts_cursors_find(sd, part_name); EINA_SAFETY_ON_NULL_RETURN_VAL(pc, EINA_FALSE); EINA_SAFETY_ON_NULL_RETURN_VAL(pc->obj, EINA_FALSE); eina_stringshare_replace(&pc->style, style); elm_object_cursor_style_set(pc->obj, pc->style); return EINA_TRUE; } static const char* _efl_ui_layout_part_cursor_style_get(Efl_Ui_Layout_Data *sd, const char *part_name) { EINA_SAFETY_ON_NULL_RETURN_VAL(part_name, NULL); Efl_Ui_Layout_Sub_Object_Cursor *pc = _parts_cursors_find(sd, part_name); EINA_SAFETY_ON_NULL_RETURN_VAL(pc, NULL); EINA_SAFETY_ON_NULL_RETURN_VAL(pc->obj, NULL); return elm_object_cursor_style_get(pc->obj); } static Eina_Bool _efl_ui_layout_part_cursor_engine_only_set(Efl_Ui_Layout_Data *sd, const char *part_name, Eina_Bool engine_only) { EINA_SAFETY_ON_NULL_RETURN_VAL(part_name, EINA_FALSE); Efl_Ui_Layout_Sub_Object_Cursor *pc = _parts_cursors_find(sd, part_name); EINA_SAFETY_ON_NULL_RETURN_VAL(pc, EINA_FALSE); EINA_SAFETY_ON_NULL_RETURN_VAL(pc->obj, EINA_FALSE); pc->engine_only = !!engine_only; elm_object_cursor_theme_search_enabled_set(pc->obj, !pc->engine_only); return EINA_TRUE; } static Eina_Bool _efl_ui_layout_part_cursor_engine_only_get(Efl_Ui_Layout_Data *sd, const char *part_name) { EINA_SAFETY_ON_NULL_RETURN_VAL(part_name, EINA_FALSE); Efl_Ui_Layout_Sub_Object_Cursor *pc = _parts_cursors_find(sd, part_name); EINA_SAFETY_ON_NULL_RETURN_VAL(pc, EINA_FALSE); EINA_SAFETY_ON_NULL_RETURN_VAL(pc->obj, EINA_FALSE); return !elm_object_cursor_theme_search_enabled_get(pc->obj); } EAPI Eina_Bool elm_layout_edje_object_can_access_set(Eo *obj, Eina_Bool can_access) { Efl_Ui_Layout_Data *sd = efl_data_scope_safe_get(obj, MY_CLASS); EINA_SAFETY_ON_NULL_RETURN_VAL(sd, EINA_FALSE); sd->can_access = !!can_access; return EINA_TRUE; } EAPI Eina_Bool elm_layout_edje_object_can_access_get(const Eo *obj) { Efl_Ui_Layout_Data *sd = efl_data_scope_safe_get(obj, MY_CLASS); EINA_SAFETY_ON_NULL_RETURN_VAL(sd, EINA_FALSE); return sd->can_access; } EOLIAN static void _efl_ui_layout_efl_object_dbg_info_get(Eo *eo_obj, Efl_Ui_Layout_Data *_pd EINA_UNUSED, Efl_Dbg_Info *root) { efl_dbg_info_get(efl_super(eo_obj, MY_CLASS), root); ELM_WIDGET_DATA_GET_OR_RETURN(eo_obj, wd); if (wd->resize_obj && efl_isa(wd->resize_obj, EFL_CANVAS_LAYOUT_CLASS)) { Efl_Dbg_Info *group = EFL_DBG_INFO_LIST_APPEND(root, MY_CLASS_NAME); const char *file, *edje_group; Evas_Object *edje_obj = wd->resize_obj; Edje_Load_Error error; efl_file_get(edje_obj, &file, &edje_group); EFL_DBG_INFO_APPEND(group, "File", EINA_VALUE_TYPE_STRING, file); EFL_DBG_INFO_APPEND(group, "Group", EINA_VALUE_TYPE_STRING, edje_group); error = edje_object_load_error_get(edje_obj); if (error != EDJE_LOAD_ERROR_NONE) { EFL_DBG_INFO_APPEND(group, "Error", EINA_VALUE_TYPE_STRING, edje_load_error_str(error)); } } } static void _prop_future_error_cb(void* data, Efl_Event const*event EINA_UNUSED) { Efl_Ui_Layout_Sub_Connect *sc = data; sc->future = NULL; } static void _view_update(Efl_Ui_Layout_Sub_Connect *sc, const char *property) { Eina_Strbuf *buf; if (sc->is_signal == EINA_FALSE) { EFL_UI_LAYOUT_DATA_GET(sc->obj, pd); pd->view_updated = EINA_TRUE; elm_layout_text_set(sc->obj, sc->name, property); pd->view_updated = EINA_FALSE; return; } buf = eina_strbuf_new(); eina_strbuf_append(buf, sc->name); eina_strbuf_replace_all(buf, "%v", property); elm_layout_signal_emit(sc->obj, eina_strbuf_string_get(buf), "elm"); eina_strbuf_free(buf); } static void _prop_future_then_cb(void* data, Efl_Event const*event) { Efl_Ui_Layout_Sub_Connect *sc = data; const Eina_Value_Type *vtype; Eina_Value *value = (Eina_Value *)((Efl_Future_Event_Success*)event->info)->value; Eina_Stringshare *selected; char *text; sc->future = NULL; vtype= eina_value_type_get(value); if (vtype == EINA_VALUE_TYPE_STRING || vtype == EINA_VALUE_TYPE_STRINGSHARE) { eina_value_get(value, &text); _view_update(sc, text); return; } selected = eina_stringshare_add("selected"); if (vtype == EINA_VALUE_TYPE_UCHAR && sc->property == selected) { Eina_Bool sb = EINA_FALSE; eina_value_get(value, &sb); if (sb) _view_update(sc, "selected"); else _view_update(sc, "unselected"); } else { text = eina_value_to_string(value); _view_update(sc, text); free(text); } eina_stringshare_del(selected); } static void _efl_ui_layout_view_model_update(Efl_Ui_Layout_Data *pd) { Efl_Ui_Layout_Sub_Connect *sc; Eina_List *l; if (!pd->prop_connect || !pd->model) return; EINA_LIST_FOREACH(pd->prop_connect, l, sc) { if (sc->future) efl_future_cancel(sc->future); sc->future = efl_model_property_get(pd->model, sc->property); efl_future_then(sc->future, &_prop_future_then_cb, &_prop_future_error_cb, NULL, sc); } } static void _efl_model_properties_changed_cb(void *data, const Efl_Event *event) { Efl_Ui_Layout_Data *pd = data; Efl_Model_Property_Event *evt = event->info; Eina_Stringshare *ss_prop; Efl_Ui_Layout_Sub_Connect *sc; const char *prop; Eina_Array_Iterator it; unsigned int i; Eina_List *l; if (!evt->changed_properties || !pd->prop_connect) return; EINA_ARRAY_ITER_NEXT(evt->changed_properties, i, prop, it) { ss_prop = eina_stringshare_add(prop); EINA_LIST_FOREACH(pd->prop_connect, l, sc) { if (sc->property == ss_prop) { if (sc->future) efl_future_cancel(sc->future); sc->future = efl_model_property_get(pd->model, sc->property); efl_future_then(sc->future, &_prop_future_then_cb, &_prop_future_error_cb, NULL, sc); } } eina_stringshare_del(ss_prop); } } EOLIAN static void _efl_ui_layout_efl_ui_view_model_set(Eo *obj EINA_UNUSED, Efl_Ui_Layout_Data *pd, Efl_Model *model) { Efl_Ui_Layout_Sub_Connect *sc; Eina_List *l; if (pd->model) { efl_event_callback_del(pd->model, EFL_MODEL_EVENT_PROPERTIES_CHANGED, _efl_model_properties_changed_cb, pd); EINA_LIST_FOREACH(pd->prop_connect, l, sc) if (sc->future) efl_future_cancel(sc->future); efl_unref(pd->model); pd->model = NULL; } if (model) { pd->model = model; efl_ref(pd->model); efl_event_callback_add(pd->model, EFL_MODEL_EVENT_PROPERTIES_CHANGED, _efl_model_properties_changed_cb, pd); } else { EINA_LIST_FOREACH(pd->prop_connect, l, sc) { if (!sc->is_signal) elm_layout_text_set(obj, sc->name, NULL); } return; } if (pd->prop_connect) _efl_ui_layout_view_model_update(pd); if (pd->factories) { Eina_Hash_Tuple *tuple; Eina_Stringshare *name; Efl_Ui_Factory *factory; Efl_Gfx *content; Eina_Iterator *it_p = eina_hash_iterator_tuple_new(pd->factories); while (eina_iterator_next(it_p, (void **)&tuple)) { name = tuple->key; factory = tuple->data; content = elm_layout_content_get(pd->obj, name); if (content && efl_isa(content, EFL_UI_VIEW_INTERFACE)) { efl_ui_view_model_set(content, pd->model); } else { efl_ui_factory_release(factory, content); content = efl_ui_factory_create(factory, pd->model, pd->obj); elm_layout_content_set(pd->obj, name, content); } } eina_iterator_free(it_p); } } EOLIAN static Efl_Model * _efl_ui_layout_efl_ui_view_model_get(Eo *obj EINA_UNUSED, Efl_Ui_Layout_Data *pd) { return pd->model; } EOLIAN static void _efl_ui_layout_efl_ui_model_connect_connect(Eo *obj EINA_UNUSED, Efl_Ui_Layout_Data *pd, const char *name, const char *property) { EINA_SAFETY_ON_NULL_RETURN(name); EINA_SAFETY_ON_NULL_RETURN(property); Efl_Ui_Layout_Sub_Connect *sc, *fsc; Eina_List *l; if (!_elm_layout_part_aliasing_eval(obj, &name, EINA_TRUE)) return; sc = calloc(1, sizeof(*sc)); sc->obj = obj; sc->property = eina_stringshare_add(property); if (strncmp(SIGNAL_PREFIX, name, sizeof(SIGNAL_PREFIX) -1) == 0) { sc->name = eina_stringshare_add(name+sizeof(SIGNAL_PREFIX) -1); sc->is_signal = EINA_TRUE; } else { sc->name = eina_stringshare_add(name); sc->is_signal = EINA_FALSE; } EINA_LIST_FOREACH(pd->prop_connect, l, fsc) { if (fsc->name == sc->name && fsc->property == sc->property) { eina_stringshare_del(sc->name); eina_stringshare_del(sc->property); free(sc); return; } } pd->prop_connect = eina_list_append(pd->prop_connect, sc); if (pd->model) { sc->future = efl_model_property_get(pd->model, sc->property); efl_future_then(sc->future, &_prop_future_then_cb, &_prop_future_error_cb, NULL, sc); } } EOLIAN static void _efl_ui_layout_efl_ui_model_factory_connect_connect(Eo *obj EINA_UNUSED, Efl_Ui_Layout_Data *pd, const char *name, Efl_Ui_Factory *factory) { EINA_SAFETY_ON_NULL_RETURN(name); Eina_Stringshare *ss_name; Efl_Ui_Factory *old_factory; Evas_Object *new_ev, *old_ev; if (!_elm_layout_part_aliasing_eval(obj, &name, EINA_TRUE)) return; ss_name = eina_stringshare_add(name); if (!pd->factories) pd->factories = eina_hash_stringshared_new(EINA_FREE_CB(efl_unref)); new_ev = efl_ui_factory_create(factory, pd->model, obj); EINA_SAFETY_ON_NULL_RETURN(new_ev); old_factory = eina_hash_set(pd->factories, ss_name, efl_ref(factory)); if (old_factory) { old_ev = elm_layout_content_get(obj, name); if (old_ev) efl_ui_factory_release(old_factory, old_ev); efl_unref(old_factory); } elm_layout_content_set(obj, name, new_ev); } EAPI Evas_Object * elm_layout_add(Evas_Object *parent) { EINA_SAFETY_ON_NULL_RETURN_VAL(parent, NULL); return elm_legacy_add(MY_CLASS, parent); } EOLIAN static Eo * _efl_ui_layout_efl_object_constructor(Eo *obj, Efl_Ui_Layout_Data *sd) { sd->obj = obj; obj = efl_constructor(efl_super(obj, MY_CLASS)); efl_canvas_object_type_set(obj, MY_CLASS_NAME_LEGACY); evas_object_smart_callbacks_descriptions_set(obj, _smart_callbacks); efl_access_role_set(obj, EFL_ACCESS_ROLE_FILLER); return obj; } EOLIAN static void _efl_ui_layout_class_constructor(Efl_Class *klass) { evas_smart_legacy_type_register(MY_CLASS_NAME_LEGACY, klass); } EOLIAN static Efl_Object* _efl_ui_layout_efl_object_finalize(Eo *obj, Efl_Ui_Layout_Data *pd EINA_UNUSED) { Eo *eo; eo = efl_finalize(efl_super(obj, MY_CLASS)); efl_ui_widget_theme_apply(eo); return eo; } EOLIAN static void _efl_ui_layout_efl_layout_signal_message_send(Eo *obj, Efl_Ui_Layout_Data *pd EINA_UNUSED, int id, const Eina_Value msg) { ELM_WIDGET_DATA_GET_OR_RETURN(obj, wd); efl_layout_signal_message_send(wd->resize_obj, id, msg); } EOLIAN static void _efl_ui_layout_efl_layout_signal_signal_process(Eo *obj, Efl_Ui_Layout_Data *pd EINA_UNUSED, Eina_Bool recurse) { ELM_WIDGET_DATA_GET_OR_RETURN(obj, wd); efl_layout_signal_process(wd->resize_obj, recurse); } /* Legacy APIs */ EAPI Eina_Bool elm_layout_file_set(Eo *obj, const char *file, const char *group) { return efl_file_set((Eo *) obj, file, group); } EAPI void elm_layout_file_get(Eo *obj, const char **file, const char **group) { efl_file_get((Eo *) obj, file, group); } EAPI Eina_Bool elm_layout_mmap_set(Eo *obj, const Eina_File *file, const char *group) { return efl_file_mmap_set((Eo *) obj, file, group); } EAPI void elm_layout_mmap_get(Eo *obj, const Eina_File **file, const char **group) { efl_file_mmap_get((Eo *) obj, file, group); } EAPI Eina_Bool elm_layout_box_append(Eo *obj, const char *part, Evas_Object *child) { return efl_pack(efl_part(obj, part), child); } EAPI Eina_Bool elm_layout_box_prepend(Eo *obj, const char *part, Evas_Object *child) { return efl_pack_begin(efl_part(obj, part), child); } EAPI Eina_Bool elm_layout_box_insert_before(Eo *obj, const char *part, Evas_Object *child, const Evas_Object *reference) { return efl_pack_before(efl_part(obj, part), child, reference); } EAPI Eina_Bool elm_layout_box_insert_at(Eo *obj, const char *part, Evas_Object *child, unsigned int pos) { return efl_pack_at(efl_part(obj, part), child, pos); } EAPI Evas_Object * elm_layout_box_remove(Eo *obj, const char *part, Evas_Object *child) { if (!efl_pack_unpack(efl_part(obj, part), child)) return NULL; return child; } EAPI Eina_Bool elm_layout_box_remove_all(Eo *obj, const char *part, Eina_Bool clear) { if (clear) return efl_pack_clear(efl_part(obj, part)); else return efl_pack_unpack_all(efl_part(obj, part)); } EAPI Eina_Bool elm_layout_table_pack(Eo *obj, const char *part, Evas_Object *child, unsigned short col, unsigned short row, unsigned short colspan, unsigned short rowspan) { return efl_pack_table(efl_part(obj, part), child, col, row, colspan, rowspan); } EAPI Evas_Object * elm_layout_table_unpack(Eo *obj, const char *part, Evas_Object *child) { if (!efl_pack_unpack(efl_part(obj, part), child)) return NULL; return child; } EAPI Eina_Bool elm_layout_table_clear(Eo *obj, const char *part, Eina_Bool clear) { if (clear) return efl_pack_clear(efl_part(obj, part)); else return efl_pack_unpack_all(efl_part(obj, part)); } EAPI Eina_Bool elm_layout_text_set(Eo *obj, const char *part, const char *text) { if (!part) { part = efl_ui_widget_default_text_part_get(obj); if (!part) return EINA_FALSE; } else if (!_elm_layout_part_aliasing_eval(obj, &part, EINA_TRUE)) return EINA_FALSE; efl_text_set(efl_part(obj, part), text); return EINA_TRUE; } EAPI const char * elm_layout_text_get(const Eo *obj, const char *part) { if (!part) { part = efl_ui_widget_default_text_part_get(obj); if (!part) return NULL; } else if (!_elm_layout_part_aliasing_eval(obj, &part, EINA_TRUE)) return NULL; return efl_text_get(efl_part(obj, part)); } EAPI Eina_Bool elm_layout_part_cursor_engine_only_set(Eo *obj, const char *part, Eina_Bool engine_only) { return efl_ui_cursor_theme_search_enabled_set(efl_part(obj, part), !engine_only); } EAPI Eina_Bool elm_layout_part_cursor_engine_only_get(const Eo *obj, const char *part) { return !efl_ui_cursor_theme_search_enabled_get(efl_part(obj, part)); } EAPI Eina_Bool elm_layout_part_cursor_set(Eo *obj, const char *part, const char *cursor) { return efl_ui_cursor_set(efl_part(obj, part), cursor); } EAPI const char * elm_layout_part_cursor_get(const Eo *obj, const char *part) { return efl_ui_cursor_get(efl_part(obj, part)); } EAPI Eina_Bool elm_layout_part_cursor_style_set(Eo *obj, const char *part, const char *style) { return efl_ui_cursor_style_set(efl_part(obj, part), style); } EAPI const char * elm_layout_part_cursor_style_get(const Eo *obj, const char *part) { return efl_ui_cursor_style_get(efl_part(obj, part)); } EAPI Eina_Bool elm_layout_part_cursor_unset(Eo *obj, const char *part) { return efl_ui_cursor_set(efl_part(obj, part), NULL); } EAPI int elm_layout_freeze(Evas_Object *obj) { return efl_layout_calc_freeze(obj); } EAPI int elm_layout_thaw(Evas_Object *obj) { return efl_layout_calc_thaw(obj); } void _elm_layout_signal_callback_add_legacy(Eo *obj, Eo *edje, Eina_List **p_edje_signals, const char *emission, const char *source, Edje_Signal_Cb func, void *data) { Edje_Signal_Data *esd; esd = ELM_NEW(Edje_Signal_Data); if (!esd) return; esd->obj = obj; esd->func = func; esd->emission = eina_stringshare_add(emission); esd->source = eina_stringshare_add(source); esd->data = data; *p_edje_signals = eina_list_append(*p_edje_signals, esd); efl_layout_signal_callback_add(edje, emission, source, _edje_signal_callback, esd); } EAPI void elm_layout_signal_callback_add(Eo *obj, const char *emission, const char *source, Edje_Signal_Cb func, void *data) { Efl_Ui_Layout_Data *sd; if (!emission || !source) return; if (efl_isa(obj, ELM_ENTRY_CLASS)) { _elm_entry_signal_callback_add_legacy(obj, emission, source, func, data); return; } sd = efl_data_scope_safe_get(obj, MY_CLASS); if (!sd) return; ELM_WIDGET_DATA_GET_OR_RETURN(obj, wd); _elm_layout_signal_callback_add_legacy(obj, wd->resize_obj, &sd->edje_signals, emission, source, func, data); } void * _elm_layout_signal_callback_del_legacy(Eo *obj EINA_UNUSED, Eo *edje, Eina_List **p_edje_signals, const char *emission, const char *source, Edje_Signal_Cb func) { Edje_Signal_Data *esd = NULL; void *data = NULL; Eina_List *l; if (!emission || !source) return NULL; EINA_LIST_FOREACH(*p_edje_signals, l, esd) { if ((esd->func == func) && (!strcmp(esd->emission, emission)) && (!strcmp(esd->source, source))) { *p_edje_signals = eina_list_remove_list(*p_edje_signals, l); efl_layout_signal_callback_del(edje, emission, source, _edje_signal_callback, esd); eina_stringshare_del(esd->emission); eina_stringshare_del(esd->source); data = esd->data; free(esd); return data; /* stop at 1st match */ } } return NULL; } EAPI void * elm_layout_signal_callback_del(Eo *obj, const char *emission, const char *source, Edje_Signal_Cb func) { Efl_Ui_Layout_Data *sd; if (!emission || !source) return NULL; if (efl_isa(obj, ELM_ENTRY_CLASS)) return _elm_entry_signal_callback_del_legacy(obj, emission, source, func); sd = efl_data_scope_safe_get(obj, MY_CLASS); if (!sd) return NULL; ELM_WIDGET_DATA_GET_OR_RETURN(obj, wd, NULL); return _elm_layout_signal_callback_del_legacy(obj, wd->resize_obj, &sd->edje_signals, emission, source, func); } EAPI void elm_layout_signal_emit(Eo *obj, const char *emission, const char *source) { efl_layout_signal_emit(obj, emission, source); } EAPI const char * elm_layout_data_get(const Evas_Object *obj, const char *key) { return efl_layout_group_data_get(obj, key); } EAPI Eina_Bool elm_layout_theme_set(Evas_Object *obj, const char *klass, const char *group, const char *style) { Efl_Ui_Theme_Apply ta; ta = efl_ui_layout_theme_set(obj, klass, group, style); return (ta != EFL_UI_THEME_APPLY_FAILED); } /* End of legacy only */ /* Efl.Part implementation */ EOLIAN static Eo * _efl_ui_layout_efl_part_part(const Eo *obj, Efl_Ui_Layout_Data *sd EINA_UNUSED, const char *part) { Efl_Canvas_Layout_Part_Type type = EFL_CANVAS_LAYOUT_PART_TYPE_NONE; EINA_SAFETY_ON_NULL_RETURN_VAL(part, NULL); ELM_WIDGET_DATA_GET_OR_RETURN((Eo *) obj, wd, NULL); // Check part type without using edje_object_part_object_get(), as this // can cause recalc, which has side effects... and could be slow. if (eina_streq(part, "background")) { if (efl_layout_group_part_exist_get(wd->resize_obj, part)) type = efl_canvas_layout_part_type_get(efl_part(wd->resize_obj, part)); if (type != EFL_CANVAS_LAYOUT_PART_TYPE_SWALLOW) { if (type < EFL_CANVAS_LAYOUT_PART_TYPE_LAST && type > EFL_CANVAS_LAYOUT_PART_TYPE_NONE) { const char *file = NULL, *key = NULL; efl_file_get(wd->resize_obj, &file, &key); WRN("Layout has a background but it's not a swallow: '%s'", elm_widget_theme_element_get(obj)); } return efl_part(efl_super(obj, MY_CLASS), part); } return ELM_PART_IMPLEMENT(EFL_UI_LAYOUT_PART_BG_CLASS, obj, part); } else if (eina_streq(part, "shadow")) return efl_part(efl_super(obj, MY_CLASS), part); if (!efl_layout_group_part_exist_get(wd->resize_obj, part)) { // edje part will handle the error message return efl_part(wd->resize_obj, part); } type = efl_canvas_layout_part_type_get(efl_part(wd->resize_obj, part)); if (type >= EFL_CANVAS_LAYOUT_PART_TYPE_LAST) { ERR("Invalid type found for part '%s' in group '%s'", part, elm_widget_theme_element_get(obj)); return NULL; } switch (type) { case EFL_CANVAS_LAYOUT_PART_TYPE_BOX: case EFL_CANVAS_LAYOUT_PART_TYPE_TABLE: return _efl_ui_layout_pack_proxy_get((Eo *) obj, type, part); case EFL_CANVAS_LAYOUT_PART_TYPE_TEXT: case EFL_CANVAS_LAYOUT_PART_TYPE_TEXTBLOCK: return ELM_PART_IMPLEMENT(EFL_UI_LAYOUT_PART_TEXT_CLASS, obj, part); case EFL_CANVAS_LAYOUT_PART_TYPE_SWALLOW: return ELM_PART_IMPLEMENT(EFL_UI_LAYOUT_PART_CONTENT_CLASS, obj, part); default: return ELM_PART_IMPLEMENT(EFL_UI_LAYOUT_PART_CLASS, obj, part); } } static const char * _efl_ui_layout_default_content_part_get(const Eo *obj, Efl_Ui_Layout_Data *sd EINA_UNUSED) { const char *part = NULL; if (!_elm_layout_part_aliasing_eval(obj, &part, EINA_FALSE)) return NULL; return part; } static const char * _efl_ui_layout_default_text_part_get(const Eo *obj, Efl_Ui_Layout_Data *sd EINA_UNUSED) { const char *part = NULL; if (!_elm_layout_part_aliasing_eval(obj, &part, EINA_TRUE)) return NULL; return part; } /* Efl.Ui.Layout.Part (common) */ EOLIAN static Eina_Bool _efl_ui_layout_part_efl_ui_cursor_cursor_set(Eo *obj, void *_pd EINA_UNUSED, const char *cursor) { Elm_Part_Data *pd = efl_data_scope_get(obj, EFL_UI_WIDGET_PART_CLASS); Efl_Ui_Layout_Data *sd = efl_data_scope_get(pd->obj, MY_CLASS); return _efl_ui_layout_part_cursor_set(sd, pd->part, cursor); } EOLIAN static const char * _efl_ui_layout_part_efl_ui_cursor_cursor_get(Eo *obj, void *_pd EINA_UNUSED) { Elm_Part_Data *pd = efl_data_scope_get(obj, EFL_UI_WIDGET_PART_CLASS); Efl_Ui_Layout_Data *sd = efl_data_scope_get(pd->obj, MY_CLASS); return _efl_ui_layout_part_cursor_get(sd, pd->part); } EOLIAN static Eina_Bool _efl_ui_layout_part_efl_ui_cursor_cursor_style_set(Eo *obj, void *_pd EINA_UNUSED, const char *style) { Elm_Part_Data *pd = efl_data_scope_get(obj, EFL_UI_WIDGET_PART_CLASS); Efl_Ui_Layout_Data *sd = efl_data_scope_get(pd->obj, MY_CLASS); return _efl_ui_layout_part_cursor_style_set(sd, pd->part, style); } EOLIAN static const char * _efl_ui_layout_part_efl_ui_cursor_cursor_style_get(Eo *obj, void *_pd EINA_UNUSED) { Elm_Part_Data *pd = efl_data_scope_get(obj, EFL_UI_WIDGET_PART_CLASS); Efl_Ui_Layout_Data *sd = efl_data_scope_get(pd->obj, MY_CLASS); return _efl_ui_layout_part_cursor_style_get(sd, pd->part); } EOLIAN static Eina_Bool _efl_ui_layout_part_efl_ui_cursor_cursor_theme_search_enabled_set(Eo *obj, void *_pd EINA_UNUSED, Eina_Bool allow) { Elm_Part_Data *pd = efl_data_scope_get(obj, EFL_UI_WIDGET_PART_CLASS); Efl_Ui_Layout_Data *sd = efl_data_scope_get(pd->obj, MY_CLASS); return _efl_ui_layout_part_cursor_engine_only_set(sd, pd->part, !allow); } EOLIAN static Eina_Bool _efl_ui_layout_part_efl_ui_cursor_cursor_theme_search_enabled_get(Eo *obj, void *_pd EINA_UNUSED) { Elm_Part_Data *pd = efl_data_scope_get(obj, EFL_UI_WIDGET_PART_CLASS); Efl_Ui_Layout_Data *sd = efl_data_scope_get(pd->obj, MY_CLASS); return !_efl_ui_layout_part_cursor_engine_only_get(sd, pd->part); } /* Efl.Ui.Layout.Part_Content */ ELM_PART_OVERRIDE_CONTENT_GET_FULL(efl_ui_layout_part_content, efl_ui_layout, EFL_UI_LAYOUT, Efl_Ui_Layout_Data) ELM_PART_OVERRIDE_CONTENT_SET_FULL(efl_ui_layout_part_content, efl_ui_layout, EFL_UI_LAYOUT, Efl_Ui_Layout_Data) ELM_PART_OVERRIDE_CONTENT_UNSET_FULL(efl_ui_layout_part_content, efl_ui_layout, EFL_UI_LAYOUT, Efl_Ui_Layout_Data) /* Efl.Ui.Layout.Part_Text */ ELM_PART_OVERRIDE_TEXT_GET_FULL(efl_ui_layout_part_text, efl_ui_layout, EFL_UI_LAYOUT, Efl_Ui_Layout_Data) ELM_PART_OVERRIDE_TEXT_SET_FULL(efl_ui_layout_part_text, efl_ui_layout, EFL_UI_LAYOUT, Efl_Ui_Layout_Data) ELM_PART_OVERRIDE_TEXT_MARKUP_GET_FULL(efl_ui_layout_part_text, efl_ui_layout, EFL_UI_LAYOUT, Efl_Ui_Layout_Data) ELM_PART_OVERRIDE_TEXT_MARKUP_SET_FULL(efl_ui_layout_part_text, efl_ui_layout, EFL_UI_LAYOUT, Efl_Ui_Layout_Data) EOLIAN static const char * _efl_ui_layout_part_text_efl_ui_translatable_translatable_text_get(Eo *obj, void *_pd EINA_UNUSED, const char **domain) { Elm_Part_Data *pd = efl_data_scope_get(obj, EFL_UI_WIDGET_PART_CLASS); return elm_widget_part_translatable_text_get(pd->obj, pd->part, domain); } EOLIAN static void _efl_ui_layout_part_text_efl_ui_translatable_translatable_text_set(Eo *obj, void *_pd EINA_UNUSED, const char *label, const char *domain) { Elm_Part_Data *pd = efl_data_scope_get(obj, EFL_UI_WIDGET_PART_CLASS); elm_widget_part_translatable_text_set(pd->obj, pd->part, label, domain); } /* Efl.Ui.Layout.Part_Legacy */ ELM_PART_OVERRIDE_CONTENT_GET_FULL(efl_ui_layout_part_legacy, efl_ui_layout, EFL_UI_LAYOUT, Efl_Ui_Layout_Data) ELM_PART_OVERRIDE_CONTENT_SET_FULL(efl_ui_layout_part_legacy, efl_ui_layout, EFL_UI_LAYOUT, Efl_Ui_Layout_Data) ELM_PART_OVERRIDE_CONTENT_UNSET_FULL(efl_ui_layout_part_legacy, efl_ui_layout, EFL_UI_LAYOUT, Efl_Ui_Layout_Data) ELM_PART_OVERRIDE_TEXT_GET_FULL(efl_ui_layout_part_legacy, efl_ui_layout, EFL_UI_LAYOUT, Efl_Ui_Layout_Data) ELM_PART_OVERRIDE_TEXT_SET_FULL(efl_ui_layout_part_legacy, efl_ui_layout, EFL_UI_LAYOUT, Efl_Ui_Layout_Data) ELM_PART_OVERRIDE_TEXT_MARKUP_GET_FULL(efl_ui_layout_part_legacy, efl_ui_layout, EFL_UI_LAYOUT, Efl_Ui_Layout_Data) ELM_PART_OVERRIDE_TEXT_MARKUP_SET_FULL(efl_ui_layout_part_legacy, efl_ui_layout, EFL_UI_LAYOUT, Efl_Ui_Layout_Data) EOLIAN static const char * _efl_ui_layout_part_legacy_efl_ui_translatable_translatable_text_get(Eo *obj, void *_pd EINA_UNUSED, const char **domain) { Elm_Part_Data *pd = efl_data_scope_get(obj, EFL_UI_WIDGET_PART_CLASS); return elm_widget_part_translatable_text_get(pd->obj, pd->part, domain); } EOLIAN static void _efl_ui_layout_part_legacy_efl_ui_translatable_translatable_text_set(Eo *obj, void *_pd EINA_UNUSED, const char *label, const char *domain) { Elm_Part_Data *pd = efl_data_scope_get(obj, EFL_UI_WIDGET_PART_CLASS); elm_widget_part_translatable_text_set(pd->obj, pd->part, label, domain); } /* Efl.Ui.Layout.Part_Bg (common) */ EOLIAN static Efl_Object * _efl_ui_layout_part_bg_efl_object_finalize(Eo *obj, void *_pd EINA_UNUSED) { Efl_Ui_Layout_Data *sd; Elm_Part_Data *pd; Eo *bg; obj = efl_finalize(efl_super(obj, EFL_UI_LAYOUT_PART_BG_CLASS)); if (!obj) return NULL; pd = efl_data_scope_get(obj, EFL_UI_WIDGET_PART_CLASS); sd = efl_data_scope_get(pd->obj, MY_CLASS); bg = _efl_ui_widget_bg_get(pd->obj); if (!_efl_ui_layout_content_set(pd->obj, sd, "background", bg)) { ERR("Failed to swallow new background object!"); // Shouldn't happen. What now? del bg? call super? return null? } return obj; } /* Efl.Ui.Layout.Part_Xxx includes */ #include "efl_ui_layout_part.eo.c" #include "efl_ui_layout_part_content.eo.c" #include "efl_ui_layout_part_bg.eo.c" #include "efl_ui_layout_part_text.eo.c" #include "efl_ui_layout_part_legacy.eo.c" /* Efl.Part end */ /* Internal EO APIs and hidden overrides */ EAPI EFL_VOID_FUNC_BODY(elm_layout_sizing_eval) EFL_FUNC_BODY_CONST(elm_layout_text_aliases_get, const Elm_Layout_Part_Alias_Description *, NULL) EFL_FUNC_BODY_CONST(elm_layout_content_aliases_get, const Elm_Layout_Part_Alias_Description *, NULL) ELM_LAYOUT_CONTENT_ALIASES_IMPLEMENT(MY_CLASS_PFX) ELM_LAYOUT_TEXT_ALIASES_IMPLEMENT(MY_CLASS_PFX) #define EFL_UI_LAYOUT_EXTRA_OPS \ EFL_CANVAS_GROUP_ADD_DEL_OPS(efl_ui_layout), \ ELM_PART_CONTENT_DEFAULT_OPS(efl_ui_layout), \ ELM_PART_TEXT_DEFAULT_OPS(efl_ui_layout), \ ELM_LAYOUT_CONTENT_ALIASES_OPS(MY_CLASS_PFX), \ ELM_LAYOUT_TEXT_ALIASES_OPS(MY_CLASS_PFX), \ EFL_OBJECT_OP_FUNC(elm_layout_sizing_eval, _elm_layout_sizing_eval), \ EFL_OBJECT_OP_FUNC(efl_dbg_info_get, _efl_ui_layout_efl_object_dbg_info_get) #include "efl_ui_layout.eo.c" diff --git a/src/lib/elementary/efl_ui_layout.eo b/src/lib/elementary/efl_ui_layout.eo index 877e3d0bcb..1667b0dbd2 100644 --- a/src/lib/elementary/efl_ui_layout.eo +++ b/src/lib/elementary/efl_ui_layout.eo @@ -1,79 +1,79 @@ class Efl.Ui.Layout (Efl.Ui.Widget, Efl.Part, Efl.Container, Efl.File, Efl.Ui.View, Efl.Ui.Model.Connect, Efl.Ui.Model.Factory.Connect, Efl.Layout.Calc, Efl.Layout.Signal, Efl.Layout.Group) { [[Elementary layout class]] methods { @property theme { [[The theme of this widget, defines which edje group will be used. Based on the type of widget ($klass), a given $group and a $style (usually "default"), the edje group name will be formed for this object. Widgets that inherit from this class will call this function automatically so it should not be called by applications, unless you are dealing directly with a @Efl.Ui.Layout object. ]] set { [[Sets the edje group from the elementary theme that will be used as layout. Note that $style will be the new style of this object, as in an @Efl.Ui.Widget.style. As a consequence this function can only be called during construction of the object, before finalize. If this returns $false the widget is very likely to become non-functioning. ]] return: Efl.Ui.Theme.Apply(0); [[Whether the style was successfully applied or not, see the values of @Efl.Ui.Theme.Apply for more information.]] } /* TODO: get { [[Get information about the current theme in use.]] } */ values { klass: string; [[The class of the group, eg. "button".]] group: string("base"); [[The group, eg. "base".]] style: string("default"); [[The style to use, eg "default".]] } } } implements { class.constructor; Efl.Object.constructor; Efl.Object.finalize; Efl.File.file { get; set; } Efl.File.mmap { get; set; } Efl.Canvas.Group.group_calculate; Efl.Layout.Calc.calc_freeze; Efl.Layout.Calc.calc_thaw; Efl.Layout.Signal.signal_callback_add; Efl.Layout.Signal.signal_callback_del; Efl.Layout.Signal.signal_emit; Efl.Layout.Signal.message_send; Efl.Layout.Signal.signal_process; Efl.Layout.Group.group_data { get; } Efl.Layout.Group.group_size_min { get; } Efl.Layout.Group.group_size_max { get; } Efl.Layout.Group.part_exist { get; } Efl.Ui.Widget.widget_sub_object_add; Efl.Ui.Widget.theme_apply; Efl.Ui.Widget.on_disabled_update; Efl.Ui.Widget.widget_sub_object_del; - Efl.Ui.Widget.on_focus_update; + Efl.Ui.Focus.Object.on_focus_update; Efl.Container.content_count; Efl.Container.content_remove; Efl.Container.content_iterate; Efl.Part.part; Efl.Ui.View.model { get; set; } Efl.Ui.Model.Connect.connect; Efl.Ui.Model.Factory.Connect.connect; } events { theme,changed; [[Called when theme changed]] } } diff --git a/src/lib/elementary/efl_ui_slider.c b/src/lib/elementary/efl_ui_slider.c index 789a43e686..be7232bcab 100644 --- a/src/lib/elementary/efl_ui_slider.c +++ b/src/lib/elementary/efl_ui_slider.c @@ -1,1833 +1,1833 @@ #ifdef HAVE_CONFIG_H # include "elementary_config.h" #endif #define EFL_ACCESS_PROTECTED #define EFL_ACCESS_WIDGET_ACTION_PROTECTED #define EFL_ACCESS_VALUE_PROTECTED #define ELM_LAYOUT_PROTECTED #include #include "elm_priv.h" #include "efl_ui_slider_private.h" #include "elm_widget_layout.h" #include "elm_part_helper.h" #define MY_CLASS EFL_UI_SLIDER_CLASS #define MY_CLASS_PFX efl_ui_slider #define MY_CLASS_NAME "Efl.Ui.Slider" #define MY_CLASS_NAME_LEGACY "elm_slider" #define SLIDER_DELAY_CHANGED_INTERVAL 0.2 #define SLIDER_STEP 0.05 static const Elm_Layout_Part_Alias_Description _content_aliases[] = { {"icon", "elm.swallow.icon"}, {"end", "elm.swallow.end"}, {NULL, NULL} }; static const Elm_Layout_Part_Alias_Description _text_aliases[] = { {"default", "elm.text"}, {NULL, NULL} }; static const char SIG_CHANGED[] = "changed"; static const char SIG_DELAY_CHANGED[] = "delay,changed"; static const char SIG_DRAG_START[] = "slider,drag,start"; static const char SIG_DRAG_STOP[] = "slider,drag,stop"; static const char PART_NAME_POPUP[] = "popup"; static const Evas_Smart_Cb_Description _smart_callbacks[] = { {SIG_CHANGED, ""}, {SIG_DELAY_CHANGED, ""}, {SIG_DRAG_START, ""}, {SIG_DRAG_STOP, ""}, {SIG_WIDGET_LANG_CHANGED, ""}, /**< handled by elm_widget */ {SIG_WIDGET_ACCESS_CHANGED, ""}, /**< handled by elm_widget */ {SIG_LAYOUT_FOCUSED, ""}, /**< handled by elm_layout */ {SIG_LAYOUT_UNFOCUSED, ""}, /**< handled by elm_layout */ {NULL, NULL} }; static void _min_max_set(Evas_Object *obj); static Eina_Bool _key_action_drag(Evas_Object *obj, const char *params); static const Elm_Action key_actions[] = { {"drag", _key_action_drag}, {NULL, NULL} }; static Eina_Bool _delay_change(void *data) { EFL_UI_SLIDER_DATA_GET(data, sd); sd->delay = NULL; efl_event_callback_legacy_call(data, EFL_UI_SLIDER_EVENT_DELAY_CHANGED, NULL); if (_elm_config->atspi_mode) efl_access_value_changed_signal_emit(data); return ECORE_CALLBACK_CANCEL; } static inline Eina_Bool _is_horizontal(Efl_Ui_Dir dir) { return efl_ui_dir_is_horizontal(dir, EINA_TRUE); } static inline Eina_Bool _is_inverted(Efl_Ui_Dir dir) { if ((dir == EFL_UI_DIR_LEFT) || (dir == EFL_UI_DIR_UP)) return EINA_TRUE; return EINA_FALSE; } static Efl_Ui_Dir _direction_get(Eina_Bool horizontal, Eina_Bool inverted) { if (horizontal) { if (inverted) return EFL_UI_DIR_LEFT; else return EFL_UI_DIR_RIGHT; } else { if (inverted) return EFL_UI_DIR_UP; else return EFL_UI_DIR_DOWN; } } static void _val_fetch(Evas_Object *obj, Eina_Bool user_event) { Eina_Bool rtl; double posx = 0.0, posy = 0.0, pos = 0.0, val; double posx2 = 0.0, posy2 = 0.0, pos2 = 0.0, val2; EFL_UI_SLIDER_DATA_GET(obj, sd); ELM_WIDGET_DATA_GET_OR_RETURN(obj, wd); edje_object_part_drag_value_get (wd->resize_obj, "elm.dragable.slider", &posx, &posy); if (_is_horizontal(sd->dir)) pos = posx; else pos = posy; edje_object_part_drag_value_get (wd->resize_obj, "elm.dragable2.slider", &posx2, &posy2); if (_is_horizontal(sd->dir)) pos2 = posx2; else pos2 = posy2; rtl = efl_ui_mirrored_get(obj); if ((!rtl && _is_inverted(sd->dir)) || (rtl && ((sd->dir == EFL_UI_DIR_UP) || (sd->dir == EFL_UI_DIR_RIGHT)))) { pos = 1.0 - pos; pos2 = 1.0 - pos2; } val = (pos * (sd->val_max - sd->val_min)) + sd->val_min; val2 = (pos2 * (sd->val_max - sd->val_min)) + sd->val_min; if (fabs(val - sd->val) > DBL_EPSILON) { sd->val = val; sd->intvl_from = val; if (user_event) { efl_event_callback_legacy_call(obj, EFL_UI_SLIDER_EVENT_CHANGED, NULL); ecore_timer_del(sd->delay); sd->delay = ecore_timer_add(SLIDER_DELAY_CHANGED_INTERVAL, _delay_change, obj); } } if (fabs(val2 - sd->intvl_to) > DBL_EPSILON) { sd->intvl_to = val2; if (user_event) { efl_event_callback_legacy_call(obj, EFL_UI_SLIDER_EVENT_CHANGED, NULL); ecore_timer_del(sd->delay); sd->delay = ecore_timer_add(SLIDER_DELAY_CHANGED_INTERVAL, _delay_change, obj); } } } static void _val_set(Evas_Object *obj) { Eina_Bool rtl; double pos, pos2; EFL_UI_SLIDER_DATA_GET(obj, sd); if (sd->val_max > sd->val_min) { pos = (sd->val - sd->val_min) / (sd->val_max - sd->val_min); pos2 = (sd->intvl_to - sd->val_min) / (sd->val_max - sd->val_min); } else { pos = 0.0; pos2 = 0.0; } if (pos < 0.0) pos = 0.0; else if (pos > 1.0) pos = 1.0; if (pos2 < 0.0) pos2 = 0.0; else if (pos2 > 1.0) pos2 = 1.0; rtl = efl_ui_mirrored_get(obj); if ((!rtl && _is_inverted(sd->dir)) || (rtl && ((sd->dir == EFL_UI_DIR_UP) || (sd->dir == EFL_UI_DIR_RIGHT)))) { pos = 1.0 - pos; pos2 = 1.0 - pos2; } ELM_WIDGET_DATA_GET_OR_RETURN(obj, wd); edje_object_part_drag_value_set (wd->resize_obj, "elm.dragable.slider", pos, pos); if (sd->intvl_enable) edje_object_part_drag_value_set (wd->resize_obj, "elm.dragable2.slider", pos2, pos2); // emit accessibility event also if value was changed by API if (_elm_config->atspi_mode) efl_access_value_changed_signal_emit(obj); } static void _units_set(Evas_Object *obj) { EFL_UI_SLIDER_DATA_GET(obj, sd); if (sd->format_cb) { Eina_Value val; eina_value_setup(&val, EINA_VALUE_TYPE_DOUBLE); eina_strbuf_reset(sd->format_strbuf); if (!sd->intvl_enable) eina_value_set(&val, sd->val); else { double v1, v2; elm_slider_range_get(obj, &v1, &v2); eina_value_set(&val, v2 - v1); } sd->format_cb(sd->format_cb_data, sd->format_strbuf, val); elm_layout_text_set(obj, "elm.units", eina_strbuf_string_get(sd->format_strbuf)); if (!sd->units_show) { elm_layout_signal_emit(obj, "elm,state,units,visible", "elm"); sd->units_show = EINA_TRUE; } eina_value_flush(&val); } else { elm_layout_text_set(obj, "elm.units", NULL); if (sd->units_show) { elm_layout_signal_emit(obj, "elm,state,units,hidden", "elm"); sd->units_show = EINA_FALSE; } } } static void _indicator_set(Evas_Object *obj) { EFL_UI_SLIDER_DATA_GET(obj, sd); Eina_Value val; const char *str; if (!sd->indi_format_cb) return; eina_value_setup(&val, EINA_VALUE_TYPE_DOUBLE); eina_strbuf_reset(sd->indi_format_strbuf); eina_value_set(&val, sd->val); sd->indi_format_cb(sd->indi_format_cb_data, sd->indi_format_strbuf, val); str = eina_strbuf_string_get(sd->indi_format_strbuf); elm_layout_text_set(obj, "elm.indicator", str); elm_layout_text_set(obj, "elm.dragable.slider:elm.indicator", str); if (sd->popup) edje_object_part_text_set(sd->popup, "elm.indicator", str); if (sd->popup2) { eina_strbuf_reset(sd->indi_format_strbuf); eina_value_set(&val, sd->intvl_to); sd->indi_format_cb(sd->indi_format_cb_data, sd->indi_format_strbuf, val); str = eina_strbuf_string_get(sd->indi_format_strbuf); elm_layout_text_set(obj, "elm.dragable2.slider:elm.indicator", str); edje_object_part_text_set(sd->popup2, "elm.indicator", str); } eina_value_flush(&val); } static void _slider_update(Evas_Object *obj, Eina_Bool user_event) { _val_fetch(obj, user_event); evas_object_smart_changed(obj); } static void _drag(void *data, Evas_Object *obj EINA_UNUSED, const char *emission EINA_UNUSED, const char *source EINA_UNUSED) { _slider_update(data, EINA_TRUE); } static void _drag_start(void *data, Evas_Object *obj EINA_UNUSED, const char *emission EINA_UNUSED, const char *source EINA_UNUSED) { if (!efl_ui_focus_object_focus_get(data)) elm_object_focus_set(data, EINA_TRUE); _slider_update(data, EINA_TRUE); efl_event_callback_legacy_call(data, EFL_UI_SLIDER_EVENT_SLIDER_DRAG_START, NULL); elm_widget_scroll_freeze_push(data); } static void _drag_stop(void *data, Evas_Object *obj EINA_UNUSED, const char *emission EINA_UNUSED, const char *source EINA_UNUSED) { _slider_update(data, EINA_TRUE); efl_event_callback_legacy_call(data, EFL_UI_SLIDER_EVENT_SLIDER_DRAG_STOP, NULL); elm_widget_scroll_freeze_pop(data); } static void _drag_step(void *data, Evas_Object *obj EINA_UNUSED, const char *emission EINA_UNUSED, const char *source EINA_UNUSED) { _slider_update(data, EINA_TRUE); } static void _drag_up(void *data, Evas_Object *obj EINA_UNUSED, const char *emission EINA_UNUSED, const char *source EINA_UNUSED) { double step; EFL_UI_SLIDER_DATA_GET(data, sd); step = sd->step; if (_is_inverted(sd->dir)) step *= -1.0; ELM_WIDGET_DATA_GET_OR_RETURN(data, wd); edje_object_part_drag_step (wd->resize_obj, "elm.dragable.slider", step, step); } static void _drag_down(void *data, Evas_Object *obj EINA_UNUSED, const char *emission EINA_UNUSED, const char *source EINA_UNUSED) { double step; EFL_UI_SLIDER_DATA_GET(data, sd); step = -sd->step; if (_is_inverted(sd->dir)) step *= -1.0; ELM_WIDGET_DATA_GET_OR_RETURN(data, wd); edje_object_part_drag_step (wd->resize_obj, "elm.dragable.slider", step, step); } static void _popup_show(void *data, Evas_Object *obj EINA_UNUSED, const char *emission EINA_UNUSED, const char *source EINA_UNUSED) { EFL_UI_SLIDER_DATA_GET(data, sd); if (sd->popup && (sd->indicator_visible_mode != ELM_SLIDER_INDICATOR_VISIBLE_MODE_NONE)) { evas_object_raise(sd->popup); evas_object_show(sd->popup); sd->popup_visible = EINA_TRUE; edje_object_signal_emit(sd->popup, "popup,show", "elm"); // XXX: for compat edje_object_signal_emit(sd->popup, "elm,popup,show", "elm"); } if (sd->popup2 && (sd->indicator_visible_mode != ELM_SLIDER_INDICATOR_VISIBLE_MODE_NONE)) { evas_object_raise(sd->popup2); evas_object_show(sd->popup2); edje_object_signal_emit(sd->popup2, "popup,show", "elm"); // XXX: for compat edje_object_signal_emit(sd->popup2, "elm,popup,show", "elm"); } ELM_SAFE_FREE(sd->wheel_indicator_timer, ecore_timer_del); } static void _popup_hide(void *data, Evas_Object *obj EINA_UNUSED, const char *emission EINA_UNUSED, const char *source EINA_UNUSED) { EFL_UI_SLIDER_DATA_GET(data, sd); if (!sd->popup_visible || !sd->popup) return; if (sd->indicator_visible_mode == ELM_SLIDER_INDICATOR_VISIBLE_MODE_ALWAYS) return; if ((sd->indicator_visible_mode == ELM_SLIDER_INDICATOR_VISIBLE_MODE_ON_FOCUS) && efl_ui_focus_object_focus_get(data)) return; edje_object_signal_emit(sd->popup, "popup,hide", "elm"); // XXX: for compat edje_object_signal_emit(sd->popup, "elm,popup,hide", "elm"); if (sd->popup2) { edje_object_signal_emit(sd->popup2, "popup,hide", "elm"); // XXX: for compat edje_object_signal_emit(sd->popup2, "elm,popup,hide", "elm"); } } static void _popup_hide_done(void *data, Evas_Object *obj EINA_UNUSED, const char *emission EINA_UNUSED, const char *source EINA_UNUSED) { EFL_UI_SLIDER_DATA_GET(data, sd); if (sd->popup) { if (!((efl_ui_focus_object_focus_get(data)) && (sd->indicator_visible_mode == ELM_SLIDER_INDICATOR_VISIBLE_MODE_ON_FOCUS))) { evas_object_hide(sd->popup); sd->popup_visible = EINA_FALSE; } } if (sd->popup2) { if (!((efl_ui_focus_object_focus_get(data)) && (sd->indicator_visible_mode == ELM_SLIDER_INDICATOR_VISIBLE_MODE_ON_FOCUS))) { evas_object_hide(sd->popup2); } } } static void _popup_emit(void *data, Evas_Object *obj EINA_UNUSED, const char *emission, const char *source) { EFL_UI_SLIDER_DATA_GET(data, sd); if (sd->popup) { edje_object_signal_emit(sd->popup, emission, source); } if (sd->popup2) { edje_object_signal_emit(sd->popup2, emission, source); } } static Eina_Bool _key_action_drag(Evas_Object *obj, const char *params) { EFL_UI_SLIDER_DATA_GET(obj, sd); const char *dir = params; if (!strcmp(dir, "left")) { if (!_is_horizontal(sd->dir)) return EINA_FALSE; if (!_is_inverted(sd->dir)) _drag_down(obj, NULL, NULL, NULL); else _drag_up(obj, NULL, NULL, NULL); } else if (!strcmp(dir, "right")) { if (!_is_horizontal(sd->dir)) return EINA_FALSE; if (!_is_inverted(sd->dir)) _drag_up(obj, NULL, NULL, NULL); else _drag_down(obj, NULL, NULL, NULL); } else if (!strcmp(dir, "up")) { if (_is_horizontal(sd->dir)) return EINA_FALSE; if (_is_inverted(sd->dir)) _drag_up(obj, NULL, NULL, NULL); else _drag_down(obj, NULL, NULL, NULL); } else if (!strcmp(dir, "down")) { if (_is_horizontal(sd->dir)) return EINA_FALSE; if (_is_inverted(sd->dir)) _drag_down(obj, NULL, NULL, NULL); else _drag_up(obj, NULL, NULL, NULL); } else return EINA_FALSE; return EINA_TRUE; } static Eina_Bool _wheel_indicator_timer_cb(void *data) { EFL_UI_SLIDER_DATA_GET(data, sd); sd->wheel_indicator_timer = NULL; _popup_hide(data, NULL, NULL, NULL); return ECORE_CALLBACK_CANCEL; } // _slider_efl_ui_widget_widget_event ELM_WIDGET_KEY_DOWN_DEFAULT_IMPLEMENT(slider, Efl_Ui_Slider_Data) EOLIAN static Eina_Bool _efl_ui_slider_efl_ui_widget_widget_event(Eo *obj, Efl_Ui_Slider_Data *sd, const Efl_Event *eo_event, Evas_Object *src) { Eo *ev = eo_event->info; if (eo_event->desc == EFL_EVENT_KEY_DOWN) { if (!_slider_efl_ui_widget_widget_event(obj, sd, eo_event, src)) return EINA_FALSE; } else if (eo_event->desc == EFL_EVENT_KEY_UP) { _popup_hide(obj, NULL, NULL, NULL); return EINA_FALSE; } else if (eo_event->desc == EFL_EVENT_POINTER_WHEEL) { if (efl_input_processed_get(ev)) return EINA_FALSE; if (efl_input_pointer_wheel_delta_get(ev) < 0) _drag_up(obj, NULL, NULL, NULL); else _drag_down(obj, NULL, NULL, NULL); efl_input_processed_set(ev, EINA_TRUE); ELM_SAFE_FREE(sd->wheel_indicator_timer, ecore_timer_del); sd->wheel_indicator_timer = ecore_timer_add(0.5, _wheel_indicator_timer_cb, obj); } else return EINA_FALSE; _popup_show(obj, NULL, NULL, NULL); _slider_update(obj, EINA_TRUE); return EINA_TRUE; } EOLIAN static Eina_Bool _efl_ui_slider_efl_ui_widget_on_access_activate(Eo *obj, Efl_Ui_Slider_Data *sd, Efl_Ui_Activate act) { if (elm_widget_disabled_get(obj)) return EINA_FALSE; if (act == EFL_UI_ACTIVATE_DEFAULT) return EINA_FALSE; if ((act == EFL_UI_ACTIVATE_UP) || (act == EFL_UI_ACTIVATE_RIGHT)) { if (!_is_inverted(sd->dir)) _drag_up(obj, NULL, NULL, NULL); else _drag_down(obj, NULL, NULL, NULL); } else if ((act == EFL_UI_ACTIVATE_DOWN) || (act == EFL_UI_ACTIVATE_LEFT)) { if (!_is_inverted(sd->dir)) _drag_down(obj, NULL, NULL, NULL); else _drag_up(obj, NULL, NULL, NULL); } _slider_update(obj, EINA_TRUE); return EINA_TRUE; } static void _visuals_refresh(Evas_Object *obj) { _val_set(obj); evas_object_smart_changed(obj); } static void _track_move_cb(void *data, Evas *e EINA_UNUSED, Evas_Object *obj, void *event_info EINA_UNUSED) { Evas_Coord x, y; EFL_UI_SLIDER_DATA_GET(data, sd); evas_object_geometry_get(obj, &x, &y, NULL, NULL); evas_object_move(sd->popup, x, y); } static void _track2_move_cb(void *data, Evas *e EINA_UNUSED, Evas_Object *obj, void *event_info EINA_UNUSED) { Evas_Coord x, y; EFL_UI_SLIDER_DATA_GET(data, sd); evas_object_geometry_get(obj, &x, &y, NULL, NULL); evas_object_move(sd->popup2, x, y); } static void _track_resize_cb(void *data, Evas *e EINA_UNUSED, Evas_Object *obj, void *event_info EINA_UNUSED) { Evas_Coord w, h; EFL_UI_SLIDER_DATA_GET(data, sd); evas_object_geometry_get(obj, NULL, NULL, &w, &h); evas_object_resize(sd->popup, w, h); } static void _track2_resize_cb(void *data, Evas *e EINA_UNUSED, Evas_Object *obj, void *event_info EINA_UNUSED) { Evas_Coord w, h; EFL_UI_SLIDER_DATA_GET(data, sd); evas_object_geometry_get(obj, NULL, NULL, &w, &h); evas_object_resize(sd->popup2, w, h); } static void _popup_update(Evas_Object *obj, Efl_Ui_Slider_Data *sd, Evas_Object *popup) { if (elm_widget_is_legacy(obj)) { if (_is_horizontal(sd->dir)) elm_widget_theme_object_set(obj, popup, "slider", "horizontal/popup", elm_widget_style_get(obj)); else elm_widget_theme_object_set(obj, popup, "slider", "vertical/popup", elm_widget_style_get(obj)); } else elm_widget_element_update(obj, popup, PART_NAME_POPUP); edje_object_scale_set(popup, efl_gfx_scale_get(obj) * elm_config_scale_get()); if (!_is_inverted(sd->dir)) edje_object_signal_emit(popup, "elm,state,inverted,off", "elm"); else edje_object_signal_emit(popup, "elm,state,inverted,on", "elm"); if (sd->indicator_show) edje_object_signal_emit(popup, "elm,state,val,show", "elm"); else edje_object_signal_emit(popup, "elm,state,val,hide", "elm"); } static void _popup_add(Efl_Ui_Slider_Data *sd, Eo *obj, Evas_Object **popup, Evas_Object **track, Eina_Bool is_range) { /* if theme has an overlayed slider mode, then lets support it */ if (!is_range && !edje_object_part_exists(elm_layout_edje_get(obj), "elm.track.slider")) return; else if (is_range && !edje_object_part_exists(elm_layout_edje_get(obj), "elm.track2.slider")) return; // XXX popup needs to adapt to theme etc. *popup = edje_object_add(evas_object_evas_get(obj)); evas_object_smart_member_add(*popup, obj); edje_object_signal_callback_add(*popup, "popup,hide,done", "elm", // XXX: for compat _popup_hide_done, obj); edje_object_signal_callback_add(*popup, "elm,popup,hide,done", "elm", _popup_hide_done, obj); _popup_update(obj, sd, *popup); /* create a rectangle to track position+size of the dragable */ *track = evas_object_rectangle_add(evas_object_evas_get(obj)); evas_object_color_set(*track, 0, 0, 0, 0); evas_object_pass_events_set(*track, EINA_TRUE); if (!is_range) { evas_object_event_callback_add (*track, EVAS_CALLBACK_MOVE, _track_move_cb, obj); evas_object_event_callback_add (*track, EVAS_CALLBACK_RESIZE, _track_resize_cb, obj); elm_layout_content_set(obj, "elm.track.slider", *track); } else { evas_object_event_callback_add (*track, EVAS_CALLBACK_MOVE, _track2_move_cb, obj); evas_object_event_callback_add (*track, EVAS_CALLBACK_RESIZE, _track2_resize_cb, obj); elm_layout_content_set(obj, "elm.track2.slider", *track); } } static const char * _theme_group_modify_pos_get(const char *cur_group, const char *search, size_t len, Eina_Bool is_legacy) { const char *pos = NULL; const char *temp_str = NULL; if (is_legacy) return cur_group; temp_str = cur_group + len - strlen(search); if (temp_str >= cur_group) { if (!strcmp(temp_str, search)) pos = temp_str; } return pos; } static char * _efl_ui_slider_theme_group_get(Evas_Object *obj, Efl_Ui_Slider_Data *sd) { const char *pos = NULL; const char *cur_group = elm_widget_theme_element_get(obj); Eina_Strbuf *new_group = eina_strbuf_new(); Eina_Bool is_legacy = elm_widget_is_legacy(obj); size_t len = 0; if (cur_group) { len = strlen(cur_group); pos = _theme_group_modify_pos_get(cur_group, "horizontal", len, is_legacy); if (!pos) pos = _theme_group_modify_pos_get(cur_group, "vertical", len, is_legacy); // TODO: change separator when it is decided. // can skip when prev_group == cur_group if (!pos) { eina_strbuf_append(new_group, cur_group); eina_strbuf_append(new_group, "/"); } else { eina_strbuf_append_length(new_group, cur_group, pos - cur_group); } } if (is_legacy && sd->intvl_enable) eina_strbuf_append(new_group, "range/"); if (_is_horizontal(sd->dir)) eina_strbuf_append(new_group, "horizontal"); else eina_strbuf_append(new_group, "vertical"); return eina_strbuf_release(new_group); } EOLIAN static Efl_Ui_Theme_Apply _efl_ui_slider_efl_ui_widget_theme_apply(Eo *obj, Efl_Ui_Slider_Data *sd) { Efl_Ui_Theme_Apply int_ret = EFL_UI_THEME_APPLY_FAILED; ELM_WIDGET_DATA_GET_OR_RETURN(obj, wd, EFL_UI_THEME_APPLY_FAILED); char *group; group = _efl_ui_slider_theme_group_get(obj, sd); if (group) { elm_widget_theme_element_set(obj, group); free(group); } int_ret = efl_ui_widget_theme_apply(efl_super(obj, MY_CLASS)); if (!int_ret) return EFL_UI_THEME_APPLY_FAILED; if (_is_horizontal(sd->dir)) evas_object_size_hint_min_set (sd->spacer, (double)sd->size * efl_gfx_scale_get(obj) * elm_config_scale_get(), 1); else evas_object_size_hint_min_set (sd->spacer, 1, (double)sd->size * efl_gfx_scale_get(obj) * elm_config_scale_get()); if (sd->intvl_enable) elm_layout_signal_emit(obj, "elm,slider,range,enable", "elm"); else elm_layout_signal_emit(obj, "elm,slider,range,disable", "elm"); if (_is_inverted(sd->dir)) elm_layout_signal_emit(obj, "elm,state,inverted,on", "elm"); else elm_layout_signal_emit(obj, "elm,state,inverted,off", "elm"); if (sd->indicator_show) elm_layout_signal_emit(obj, "elm,state,val,show", "elm"); else elm_layout_signal_emit(obj, "elm,state,val,hide", "elm"); if (!sd->popup) _popup_add(sd, obj, &sd->popup, &sd->track, sd->intvl_enable); else _popup_update(obj, sd, sd->popup); if (sd->intvl_enable) { if (!sd->popup2) _popup_add(sd, obj, &sd->popup2, &sd->track2, EINA_TRUE); else _popup_update(obj, sd, sd->popup2); } _min_max_set(obj); _units_set(obj); _indicator_set(obj); _visuals_refresh(obj); edje_object_message_signal_process(wd->resize_obj); if (sd->popup) edje_object_message_signal_process(sd->popup); if (sd->popup2) edje_object_message_signal_process(sd->popup2); evas_object_smart_changed(obj); return int_ret; } EOLIAN static void _efl_ui_slider_elm_layout_sizing_eval(Eo *obj, Efl_Ui_Slider_Data *_pd EINA_UNUSED) { ELM_WIDGET_DATA_GET_OR_RETURN(obj, wd); Evas_Coord minw = -1, minh = -1; elm_coords_finger_size_adjust(1, &minw, 1, &minh); edje_object_size_min_restricted_calc (wd->resize_obj, &minw, &minh, minw, minh); evas_object_size_hint_min_set(obj, minw, minh); evas_object_size_hint_max_set(obj, -1, -1); } static void _move_knob_on_mouse(Evas_Object *obj, double button_x, double button_y) { EFL_UI_SLIDER_DATA_GET(obj, sd); ELM_WIDGET_DATA_GET_OR_RETURN(obj, wd); if (!sd->intvl_enable) { edje_object_part_drag_value_set (wd->resize_obj, "elm.dragable.slider", button_x, button_y); } else { double posx = 0.0, posy = 0.0, posx2 = 0.0, posy2 = 0.0, diff1, diff2; edje_object_part_drag_value_get (wd->resize_obj, "elm.dragable.slider", &posx, &posy); edje_object_part_drag_value_get (wd->resize_obj, "elm.dragable2.slider", &posx2, &posy2); if (_is_horizontal(sd->dir)) { diff1 = fabs(button_x - posx); diff2 = fabs(button_x - posx2); } else { diff1 = fabs(button_y - posy); diff2 = fabs(button_y - posy2); } if (diff1 < diff2) { edje_object_part_drag_value_set (wd->resize_obj, "elm.dragable.slider", button_x, button_y); } else { edje_object_part_drag_value_set (wd->resize_obj, "elm.dragable2.slider", button_x, button_y); } } } static void _spacer_down_cb(void *data, Evas *e EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event_info) { EFL_UI_SLIDER_DATA_GET(data, sd); Evas_Event_Mouse_Down *ev = event_info; Evas_Coord x, y, w, h; double button_x = 0.0, button_y = 0.0; sd->spacer_down = EINA_TRUE; sd->val2 = sd->val; evas_object_geometry_get(sd->spacer, &x, &y, &w, &h); sd->downx = ev->canvas.x - x; sd->downy = ev->canvas.y - y; if (_is_horizontal(sd->dir)) { button_x = ((double)ev->canvas.x - (double)x) / (double)w; if (button_x > 1) button_x = 1; if (button_x < 0) button_x = 0; } else { button_y = ((double)ev->canvas.y - (double)y) / (double)h; if (button_y > 1) button_y = 1; if (button_y < 0) button_y = 0; } _move_knob_on_mouse(data, button_x, button_y); if (!efl_ui_focus_object_focus_get(data)) elm_object_focus_set(data, EINA_TRUE); _slider_update(data, EINA_TRUE); efl_event_callback_legacy_call(data, EFL_UI_SLIDER_EVENT_SLIDER_DRAG_START, NULL); elm_layout_signal_emit(data, "elm,state,indicator,show", "elm"); } static void _spacer_move_cb(void *data, Evas *e EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event_info) { EFL_UI_SLIDER_DATA_GET(data, sd); Evas_Coord x, y, w, h; double button_x = 0.0, button_y = 0.0; Evas_Event_Mouse_Move *ev = event_info; if (sd->spacer_down) { Evas_Coord d = 0; evas_object_geometry_get(sd->spacer, &x, &y, &w, &h); if (_is_horizontal(sd->dir)) d = abs(ev->cur.canvas.x - x - sd->downx); else d = abs(ev->cur.canvas.y - y - sd->downy); if (d > (_elm_config->thumbscroll_threshold - 1)) { if (!sd->frozen) { elm_widget_scroll_freeze_push(data); sd->frozen = EINA_TRUE; } ev->event_flags &= ~EVAS_EVENT_FLAG_ON_HOLD; } if (ev->event_flags & EVAS_EVENT_FLAG_ON_HOLD) { if (sd->spacer_down) sd->spacer_down = EINA_FALSE; _slider_update(data, EINA_TRUE); efl_event_callback_legacy_call (data, EFL_UI_SLIDER_EVENT_SLIDER_DRAG_STOP, NULL); if (sd->frozen) { elm_widget_scroll_freeze_pop(data); sd->frozen = EINA_FALSE; } elm_layout_signal_emit(data, "elm,state,indicator,hide", "elm"); elm_slider_value_set(data, sd->val2); return; } if (_is_horizontal(sd->dir)) { button_x = ((double)ev->cur.canvas.x - (double)x) / (double)w; if (button_x > 1) button_x = 1; if (button_x < 0) button_x = 0; } else { button_y = ((double)ev->cur.canvas.y - (double)y) / (double)h; if (button_y > 1) button_y = 1; if (button_y < 0) button_y = 0; } _move_knob_on_mouse(data, button_x, button_y); _slider_update(data, EINA_TRUE); } } static void _spacer_up_cb(void *data, Evas *e EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED) { EFL_UI_SLIDER_DATA_GET(data, sd); if (!sd->spacer_down) return; if (sd->spacer_down) sd->spacer_down = EINA_FALSE; _slider_update(data, EINA_TRUE); efl_event_callback_legacy_call(data, EFL_UI_SLIDER_EVENT_SLIDER_DRAG_STOP, NULL); if (sd->frozen) { elm_widget_scroll_freeze_pop(data); sd->frozen = EINA_FALSE; } elm_layout_signal_emit(data, "elm,state,indicator,hide", "elm"); } static void _min_max_set(Evas_Object *obj) { EFL_UI_SLIDER_DATA_GET(obj, sd); Eina_Strbuf *str; Eina_Value val; if (!sd->format_cb) return; eina_value_setup(&val, EINA_VALUE_TYPE_DOUBLE); str = eina_strbuf_new(); eina_value_set(&val, sd->val_max); sd->format_cb(sd->format_cb_data, str, val); elm_layout_text_set(obj, "elm.units.min", eina_strbuf_string_get(str)); eina_strbuf_reset(str); eina_value_set(&val, sd->val_min); sd->format_cb(sd->format_cb_data, str, val); elm_layout_text_set(obj, "elm.units.max", eina_strbuf_string_get(str)); eina_strbuf_free(str); eina_value_flush(&val); } EOLIAN static void _efl_ui_slider_efl_canvas_group_group_calculate(Eo *obj, Efl_Ui_Slider_Data *sd) { elm_layout_freeze(obj); if (_is_horizontal(sd->dir)) evas_object_size_hint_min_set (sd->spacer, (double)sd->size * efl_gfx_scale_get(obj) * elm_config_scale_get(), 1); else evas_object_size_hint_min_set (sd->spacer, 1, (double)sd->size * efl_gfx_scale_get(obj) * elm_config_scale_get()); _val_fetch(obj, EINA_FALSE); _min_max_set(obj); _units_set(obj); _indicator_set(obj); elm_layout_thaw(obj); } static char * _access_info_cb(void *data EINA_UNUSED, Evas_Object *obj) { const char *txt = elm_widget_access_info_get(obj); if (!txt) txt = elm_layout_text_get(obj, NULL); if (txt) return strdup(txt); return NULL; } static char * _access_state_cb(void *data EINA_UNUSED, Evas_Object *obj) { char *ret; Eina_Strbuf *buf = eina_strbuf_new(); const char *txt = elm_layout_text_get(obj, "elm.units"); if (txt) eina_strbuf_append(buf, txt); if (elm_widget_disabled_get(obj)) eina_strbuf_append(buf, " state: disabled"); if (eina_strbuf_length_get(buf)) { ret = eina_strbuf_string_steal(buf); eina_strbuf_free(buf); return ret; } eina_strbuf_free(buf); return NULL; } static void _on_show(void *data EINA_UNUSED, Evas *e EINA_UNUSED, Evas_Object *obj, void *event_info EINA_UNUSED) { EFL_UI_SLIDER_DATA_GET(obj, sd); if (sd->indicator_visible_mode == ELM_SLIDER_INDICATOR_VISIBLE_MODE_ALWAYS) _popup_show(obj, NULL, NULL, NULL); } EOLIAN static void _efl_ui_slider_efl_canvas_group_group_add(Eo *obj, Efl_Ui_Slider_Data *priv) { ELM_WIDGET_DATA_GET_OR_RETURN(obj, wd); char *group; if (!elm_widget_theme_klass_get(obj)) elm_widget_theme_klass_set(obj, "slider"); efl_canvas_group_add(efl_super(obj, MY_CLASS)); elm_widget_sub_object_parent_add(obj); priv->dir = EFL_UI_DIR_RIGHT; priv->indicator_show = EINA_TRUE; priv->indicator_visible_mode = elm_config_slider_indicator_visible_mode_get(); priv->val_max = 1.0; //TODO: customize this time duration from api or theme data. priv->wheel_indicator_duration = 0.25; priv->step = SLIDER_STEP; group = _efl_ui_slider_theme_group_get(obj, priv); if (!elm_widget_theme_object_set(obj, wd->resize_obj, elm_widget_theme_klass_get(obj), group, elm_widget_theme_style_get(obj))) CRI("Failed to set layout!"); free(group); elm_layout_signal_callback_add(obj, "drag", "*", _drag, obj); elm_layout_signal_callback_add(obj, "drag,start", "*", _drag_start, obj); elm_layout_signal_callback_add(obj, "drag,stop", "*", _drag_stop, obj); elm_layout_signal_callback_add(obj, "drag,step", "*", _drag_step, obj); elm_layout_signal_callback_add(obj, "drag,page", "*", _drag_stop, obj); elm_layout_signal_callback_add(obj, "popup,show", "elm", _popup_show, obj); // XXX: for compat elm_layout_signal_callback_add(obj, "popup,hide", "elm", _popup_hide, obj); // XXX: for compat elm_layout_signal_callback_add(obj, "elm,popup,show", "elm", _popup_show, obj); elm_layout_signal_callback_add(obj, "elm,popup,hide", "elm", _popup_hide, obj); elm_layout_signal_callback_add(obj, "*", "popup,emit", _popup_emit, obj); edje_object_part_drag_value_set (wd->resize_obj, "elm.dragable.slider", 0.0, 0.0); priv->spacer = evas_object_rectangle_add(evas_object_evas_get(obj)); evas_object_color_set(priv->spacer, 0, 0, 0, 0); evas_object_pass_events_set(priv->spacer, EINA_TRUE); elm_layout_content_set(obj, "elm.swallow.bar", priv->spacer); if (!priv->intvl_enable) _popup_add(priv, obj, &priv->popup, &priv->track, priv->intvl_enable); else _popup_add(priv, obj, &priv->popup2, &priv->track2, priv->intvl_enable); evas_object_event_callback_add (priv->spacer, EVAS_CALLBACK_MOUSE_DOWN, _spacer_down_cb, obj); evas_object_event_callback_add (priv->spacer, EVAS_CALLBACK_MOUSE_MOVE, _spacer_move_cb, obj); evas_object_event_callback_add (priv->spacer, EVAS_CALLBACK_MOUSE_UP, _spacer_up_cb, obj); evas_object_event_callback_add(obj, EVAS_CALLBACK_SHOW, _on_show, NULL); elm_widget_can_focus_set(obj, EINA_TRUE); _elm_access_object_register(obj, wd->resize_obj); _elm_access_text_set (_elm_access_info_get(obj), ELM_ACCESS_TYPE, E_("slider")); _elm_access_callback_set (_elm_access_info_get(obj), ELM_ACCESS_INFO, _access_info_cb, NULL); _elm_access_callback_set (_elm_access_info_get(obj), ELM_ACCESS_STATE, _access_state_cb, NULL); efl_ui_format_string_set(efl_part(obj, "indicator"), "%0.2f"); evas_object_smart_changed(obj); } EOLIAN static void _efl_ui_slider_efl_canvas_group_group_del(Eo *obj, Efl_Ui_Slider_Data *sd) { ecore_timer_del(sd->delay); ecore_timer_del(sd->wheel_indicator_timer); evas_object_del(sd->popup); evas_object_del(sd->popup2); ELM_SAFE_FREE(sd->indi_template, eina_stringshare_del); eina_strbuf_free(sd->indi_format_strbuf); efl_ui_format_cb_set(obj, NULL, NULL, NULL); eina_strbuf_free(sd->format_strbuf); efl_canvas_group_del(efl_super(obj, MY_CLASS)); } EOLIAN static Eo * _efl_ui_slider_efl_object_constructor(Eo *obj, Efl_Ui_Slider_Data *_pd EINA_UNUSED) { obj = efl_constructor(efl_super(obj, MY_CLASS)); efl_canvas_object_type_set(obj, MY_CLASS_NAME_LEGACY); evas_object_smart_callbacks_descriptions_set(obj, _smart_callbacks); efl_access_role_set(obj, EFL_ACCESS_ROLE_SLIDER); return obj; } EOLIAN static void _efl_ui_slider_efl_ui_direction_direction_set(Eo *obj, Efl_Ui_Slider_Data *sd, Efl_Ui_Dir dir) { sd->dir = dir; efl_ui_widget_theme_apply(obj); } EOLIAN static Efl_Ui_Dir _efl_ui_slider_efl_ui_direction_direction_get(Eo *obj EINA_UNUSED, Efl_Ui_Slider_Data *sd) { return sd->dir; } EOLIAN static void _efl_ui_slider_efl_ui_range_range_min_max_set(Eo *obj, Efl_Ui_Slider_Data *sd, double min, double max) { if ((sd->val_min == min) && (sd->val_max == max)) return; sd->val_min = min; sd->val_max = max; if (sd->val < sd->val_min) sd->val = sd->val_min; if (sd->val > sd->val_max) sd->val = sd->val_max; _visuals_refresh(obj); } EOLIAN static void _efl_ui_slider_efl_ui_range_range_min_max_get(Eo *obj EINA_UNUSED, Efl_Ui_Slider_Data *sd, double *min, double *max) { if (min) *min = sd->val_min; if (max) *max = sd->val_max; } EOLIAN static void _efl_ui_slider_efl_ui_range_range_value_set(Eo *obj, Efl_Ui_Slider_Data *sd, double val) { if (sd->val == val) return; sd->val = val; sd->intvl_from = val; if (sd->val < sd->val_min) sd->val = sd->val_min; if (sd->val > sd->val_max) sd->val = sd->val_max; _visuals_refresh(obj); } EOLIAN static double _efl_ui_slider_efl_ui_range_range_value_get(Eo *obj EINA_UNUSED, Efl_Ui_Slider_Data *sd) { return sd->val; } EOLIAN static void _efl_ui_slider_step_set(Eo *obj EINA_UNUSED, Efl_Ui_Slider_Data *sd, double step) { if (sd->step == step) return; if (step < 0.0) step = 0.0; else if (step > 1.0) step = 1.0; sd->step = step; } EOLIAN static double _efl_ui_slider_step_get(Eo *obj EINA_UNUSED, Efl_Ui_Slider_Data *sd) { return sd->step; } EOLIAN static Eina_Bool -_efl_ui_slider_efl_ui_widget_on_focus_update(Eo *obj, Efl_Ui_Slider_Data *sd EINA_UNUSED) +_efl_ui_slider_efl_ui_focus_object_on_focus_update(Eo *obj, Efl_Ui_Slider_Data *sd EINA_UNUSED) { Eina_Bool int_ret = EINA_FALSE; - int_ret = efl_ui_widget_on_focus_update(efl_super(obj, MY_CLASS)); + int_ret = efl_ui_focus_object_on_focus_update(efl_super(obj, MY_CLASS)); if ((sd->indicator_visible_mode == ELM_SLIDER_INDICATOR_VISIBLE_MODE_ON_FOCUS) && efl_ui_focus_object_focus_get(obj)) _popup_show(obj, NULL, NULL, NULL); else if (!efl_ui_focus_object_focus_get(obj)) _popup_hide(obj, NULL, NULL, NULL); return int_ret; } EOLIAN static void _efl_ui_slider_class_constructor(Efl_Class *klass) { evas_smart_legacy_type_register(MY_CLASS_NAME_LEGACY, klass); } // A11Y Accessibility EOLIAN static void _efl_ui_slider_efl_access_value_value_and_text_get(Eo *obj EINA_UNUSED, Efl_Ui_Slider_Data *sd, double *value, const char **text) { if (value) *value = sd->val; if (text) *text = NULL; } EOLIAN static Eina_Bool _efl_ui_slider_efl_access_value_value_and_text_set(Eo *obj, Efl_Ui_Slider_Data *sd, double value, const char *text EINA_UNUSED) { double oldval = sd->val; if (value < sd->val_min) value = sd->val_min; if (value > sd->val_max) value = sd->val_max; efl_event_callback_legacy_call(obj, EFL_UI_SLIDER_EVENT_SLIDER_DRAG_START, NULL); sd->val = value; _visuals_refresh(obj); sd->val = oldval; _slider_update(obj, EINA_TRUE); efl_event_callback_legacy_call(obj, EFL_UI_SLIDER_EVENT_SLIDER_DRAG_STOP, NULL); return EINA_TRUE; } EOLIAN static void _efl_ui_slider_efl_access_value_range_get(Eo *obj EINA_UNUSED, Efl_Ui_Slider_Data *sd, double *lower, double *upper, const char **descr) { if (lower) *lower = sd->val_min; if (upper) *upper = sd->val_max; if (descr) *descr = NULL; } EOLIAN static double _efl_ui_slider_efl_access_value_increment_get(Eo *obj EINA_UNUSED, Efl_Ui_Slider_Data *sd) { return sd->step; } EOLIAN static void _efl_ui_slider_efl_ui_format_format_cb_set(Eo *obj, Efl_Ui_Slider_Data *sd, void *func_data, Efl_Ui_Format_Func_Cb func, Eina_Free_Cb func_free_cb) { ELM_WIDGET_DATA_GET_OR_RETURN(obj, wd); if (sd->format_cb_data == func_data && sd->format_cb == func) return; if (sd->format_cb_data && sd->format_free_cb) sd->format_free_cb(sd->format_cb_data); sd->format_cb = func; sd->format_cb_data = func_data; sd->format_free_cb = func_free_cb; if (!sd->format_strbuf) sd->format_strbuf = eina_strbuf_new(); elm_layout_signal_emit(obj, "elm,state,units,visible", "elm"); edje_object_message_signal_process(wd->resize_obj); if (sd->popup) edje_object_signal_emit(sd->popup, "elm,state,units,visible", "elm"); if (sd->popup2) edje_object_signal_emit(sd->popup2, "elm,state,units,visible", "elm"); efl_canvas_group_change(obj); } EOLIAN const Efl_Access_Action_Data * _efl_ui_slider_efl_access_widget_action_elm_actions_get(Eo *obj EINA_UNUSED, Efl_Ui_Slider_Data *pd EINA_UNUSED) { static Efl_Access_Action_Data atspi_actions[] = { { "drag,left", "drag", "left", _key_action_drag}, { "drag,right", "drag", "right", _key_action_drag}, { "drag,up", "drag", "up", _key_action_drag}, { "drag,down", "drag", "down", _key_action_drag}, { NULL, NULL, NULL, NULL} }; return &atspi_actions[0]; } // A11Y Accessibility - END /* Standard widget overrides */ ELM_PART_TEXT_DEFAULT_IMPLEMENT(efl_ui_slider, Efl_Ui_Slider_Data) ELM_PART_MARKUP_DEFAULT_IMPLEMENT(efl_ui_slider, Efl_Ui_Slider_Data) ELM_PART_CONTENT_DEFAULT_GET(efl_ui_slider, _content_aliases[0].real_part) ELM_PART_CONTENT_DEFAULT_IMPLEMENT(efl_ui_slider, Efl_Ui_Slider_Data) static void _slider_span_size_set(Eo *obj, Efl_Ui_Slider_Data *sd, int size) { if (sd->size == size) return; sd->size = size; if (sd->indicator_show) { elm_layout_signal_emit(obj, "elm,state,val,show", "elm"); if (sd->popup) edje_object_signal_emit(sd->popup, "elm,state,val,show", "elm"); if (sd->popup2) edje_object_signal_emit(sd->popup2, "elm,state,val,show", "elm"); } else { elm_layout_signal_emit(obj, "elm,state,val,hide", "elm"); if (sd->popup) edje_object_signal_emit(sd->popup, "elm,state,val,hide", "elm"); if (sd->popup2) edje_object_signal_emit(sd->popup2, "elm,state,val,hide", "elm"); } evas_object_smart_changed(obj); } /* Efl.Part begin */ EOLIAN static Eo * _efl_ui_slider_efl_part_part(const Eo *obj, Efl_Ui_Slider_Data *sd EINA_UNUSED, const char *part) { EINA_SAFETY_ON_NULL_RETURN_VAL(part, NULL); if (eina_streq(part, "indicator")) return ELM_PART_IMPLEMENT(EFL_UI_SLIDER_PART_INDICATOR_CLASS, obj, part); return efl_part(efl_super(obj, MY_CLASS), part); } EOLIAN static void _efl_ui_slider_part_indicator_efl_ui_format_format_cb_set(Eo *obj, void *_pd EINA_UNUSED, void *func_data, Efl_Ui_Format_Func_Cb func, Eina_Free_Cb func_free_cb) { Elm_Part_Data *pd = efl_data_scope_get(obj, EFL_UI_WIDGET_PART_CLASS); Efl_Ui_Slider_Data *sd = efl_data_scope_get(pd->obj, EFL_UI_SLIDER_CLASS); if (sd->indi_format_cb_data == func_data && sd->indi_format_cb == func) return; if (sd->indi_format_cb_data && sd->indi_format_free_cb) sd->indi_format_free_cb(sd->format_cb_data); sd->indi_format_cb = func; sd->indi_format_cb_data = func_data; sd->indi_format_free_cb = func_free_cb; if (!sd->indi_format_strbuf) sd->indi_format_strbuf = eina_strbuf_new(); efl_canvas_group_change(pd->obj); } static void _indi_default_format_cb(void *data, Eina_Strbuf *str, const Eina_Value value) { const Eina_Value_Type *type = eina_value_type_get(&value); Efl_Ui_Slider_Data *sd = efl_data_scope_get(data, EFL_UI_SLIDER_CLASS); double v; if (type != EINA_VALUE_TYPE_DOUBLE) return; eina_value_get(&value, &v); eina_strbuf_append_printf(str, sd->indi_template, v); } static void _indi_default_format_free_cb(void *data) { Efl_Ui_Slider_Data *sd = efl_data_scope_get(data, EFL_UI_SLIDER_CLASS); if (sd && sd->indi_template) { eina_stringshare_del(sd->indi_template); sd->indi_template = NULL; } } EOLIAN static void _efl_ui_slider_part_indicator_efl_ui_format_format_string_set(Eo *obj, void *_pd EINA_UNUSED, const char *template) { Elm_Part_Data *pd = efl_data_scope_get(obj, EFL_UI_WIDGET_PART_CLASS); Efl_Ui_Slider_Data *sd = efl_data_scope_get(pd->obj, EFL_UI_SLIDER_CLASS); if (!template) return; eina_stringshare_replace(&sd->indi_template, template); efl_ui_format_cb_set(efl_part(pd->obj, "indicator"), pd->obj, _indi_default_format_cb, _indi_default_format_free_cb); } EOLIAN static const char * _efl_ui_slider_part_indicator_efl_ui_format_format_string_get(Eo *obj, void *_pd EINA_UNUSED) { Elm_Part_Data *pd = efl_data_scope_get(obj, EFL_UI_WIDGET_PART_CLASS); Efl_Ui_Slider_Data *sd = efl_data_scope_get(pd->obj, EFL_UI_SLIDER_CLASS); return sd->indi_template; } EOLIAN static void _efl_ui_slider_part_indicator_visible_mode_set(Eo *obj, void *_pd EINA_UNUSED, Efl_Ui_Slider_Indicator_Visible_Mode indicator_visible_mode) { Elm_Part_Data *pd = efl_data_scope_get(obj, EFL_UI_WIDGET_PART_CLASS); Efl_Ui_Slider_Data *sd = efl_data_scope_get(pd->obj, EFL_UI_SLIDER_CLASS); if (!eina_streq(pd->part, "indicator")) return; if (sd->indicator_visible_mode == indicator_visible_mode) return; sd->indicator_visible_mode = indicator_visible_mode; } EOLIAN static Efl_Ui_Slider_Indicator_Visible_Mode _efl_ui_slider_part_indicator_visible_mode_get(Eo *obj, void *_pd EINA_UNUSED) { Elm_Part_Data *pd = efl_data_scope_get(obj, EFL_UI_WIDGET_PART_CLASS); Efl_Ui_Slider_Data *sd = efl_data_scope_get(pd->obj, EFL_UI_SLIDER_CLASS); if (!eina_streq(pd->part, "indicator")) return EFL_UI_SLIDER_INDICATOR_VISIBLE_MODE_NONE; return sd->indicator_visible_mode; } #include "efl_ui_slider_part_indicator.eo.c" /* Efl.Part end */ /* Legacy APIs */ EAPI Evas_Object * elm_slider_add(Evas_Object *parent) { EINA_SAFETY_ON_NULL_RETURN_VAL(parent, NULL); return elm_legacy_add(MY_CLASS, parent); } EAPI void elm_slider_span_size_set(Evas_Object *obj, Evas_Coord size) { EFL_UI_SLIDER_DATA_GET_OR_RETURN(obj, sd); _slider_span_size_set(obj, sd, size); } EAPI Evas_Coord elm_slider_span_size_get(const Evas_Object *obj) { EFL_UI_SLIDER_DATA_GET_OR_RETURN(obj, sd, 0); return sd->size; } EAPI void elm_slider_unit_format_set(Evas_Object *obj, const char *units) { efl_ui_format_string_set(obj, units); } EAPI const char * elm_slider_unit_format_get(const Evas_Object *obj) { return efl_ui_format_string_get(obj); } EAPI void elm_slider_horizontal_set(Evas_Object *obj, Eina_Bool horizontal) { Efl_Ui_Dir dir; EFL_UI_SLIDER_DATA_GET_OR_RETURN(obj, sd); dir = _direction_get(horizontal, _is_inverted(sd->dir)); efl_ui_direction_set(obj, dir); } EAPI Eina_Bool elm_slider_horizontal_get(const Evas_Object *obj) { Efl_Ui_Dir dir; dir = efl_ui_direction_get(obj); return _is_horizontal(dir); } EAPI void elm_slider_value_set(Evas_Object *obj, double val) { efl_ui_range_value_set(obj, val); } EAPI double elm_slider_value_get(const Evas_Object *obj) { return efl_ui_range_value_get(obj); } EAPI void elm_slider_inverted_set(Evas_Object *obj, Eina_Bool inverted) { Efl_Ui_Dir dir; EFL_UI_SLIDER_DATA_GET_OR_RETURN(obj, sd); dir = _direction_get(_is_horizontal(sd->dir), inverted); efl_ui_direction_set(obj, dir); } EAPI Eina_Bool elm_slider_inverted_get(const Evas_Object *obj) { Efl_Ui_Dir dir; dir = efl_ui_direction_get(obj); return _is_inverted(dir); } typedef struct { slider_func_type format_cb; slider_freefunc_type format_free_cb; } Slider_Format_Wrapper_Data; static void _format_legacy_to_format_eo_cb(void *data, Eina_Strbuf *str, const Eina_Value value) { Slider_Format_Wrapper_Data *sfwd = data; char *buf = NULL; double val = 0; const Eina_Value_Type *type = eina_value_type_get(&value); if (type == EINA_VALUE_TYPE_DOUBLE) eina_value_get(&value, &val); if (sfwd->format_cb) buf = sfwd->format_cb(val); if (buf) eina_strbuf_append(str, buf); if (sfwd->format_free_cb) sfwd->format_free_cb(buf); } static void _format_legacy_to_format_eo_free_cb(void *data) { Slider_Format_Wrapper_Data *sfwd = data; free(sfwd); } EAPI void elm_slider_units_format_function_set(Evas_Object *obj, slider_func_type func, slider_freefunc_type free_func) { Slider_Format_Wrapper_Data *sfwd = malloc(sizeof(Slider_Format_Wrapper_Data)); sfwd->format_cb = func; sfwd->format_free_cb = free_func; efl_ui_format_cb_set(obj, sfwd, _format_legacy_to_format_eo_cb, _format_legacy_to_format_eo_free_cb); } EAPI void elm_slider_range_enabled_set(Evas_Object *obj, Eina_Bool enable) { EFL_UI_SLIDER_DATA_GET_OR_RETURN(obj, sd); if (sd->intvl_enable == enable) return; sd->intvl_enable = enable; efl_ui_widget_theme_apply(obj); if (sd->intvl_enable) { elm_layout_signal_emit(obj, "elm,slider,range,enable", "elm"); if (sd->indicator_show) edje_object_signal_emit(sd->popup2, "elm,state,val,show", "elm"); } else { elm_layout_signal_emit(obj, "elm,slider,range,disable", "elm"); ELM_SAFE_FREE(sd->popup2, evas_object_del); } } EAPI Eina_Bool elm_slider_range_enabled_get(const Evas_Object *obj) { EFL_UI_SLIDER_DATA_GET_OR_RETURN(obj, pd, EINA_FALSE); return pd->intvl_enable; } EAPI void elm_slider_range_set(Evas_Object *obj, double from, double to) { EFL_UI_SLIDER_DATA_GET_OR_RETURN(obj, pd); pd->intvl_from = from; pd->val = from; pd->intvl_to = to; if (pd->intvl_from < pd->val_min) pd->intvl_from = pd->val_min; if (pd->intvl_to > pd->val_max) pd->intvl_to = pd->val_max; _visuals_refresh(obj); } EAPI void elm_slider_range_get(const Evas_Object *obj, double *from, double *to) { EFL_UI_SLIDER_DATA_GET_OR_RETURN(obj, pd); if (from) *from = fmin(pd->intvl_from, pd->intvl_to); if (to) *to = fmax(pd->intvl_from, pd->intvl_to); } EAPI void elm_slider_min_max_set(Evas_Object *obj, double min, double max) { efl_ui_range_min_max_set(obj, min, max); } EAPI void elm_slider_min_max_get(const Evas_Object *obj, double *min, double *max) { efl_ui_range_min_max_get(obj, min, max); } EAPI void elm_slider_indicator_format_set(Evas_Object *obj, const char *indicator) { efl_ui_format_string_set(efl_part(obj, "indicator"), indicator); } EAPI const char * elm_slider_indicator_format_get(const Evas *obj) { return efl_ui_format_string_get(efl_part(obj, "indicator")); } EAPI void elm_slider_indicator_format_function_set(Evas_Object *obj, slider_func_type func, slider_freefunc_type free_func) { Slider_Format_Wrapper_Data *sfwd = malloc(sizeof(Slider_Format_Wrapper_Data)); sfwd->format_cb = func; sfwd->format_free_cb = free_func; efl_ui_format_cb_set(efl_part(obj, "indicator"), sfwd, _format_legacy_to_format_eo_cb, _format_legacy_to_format_eo_free_cb); } EAPI void elm_slider_indicator_show_on_focus_set(Evas_Object *obj, Eina_Bool flag) { efl_ui_slider_part_indicator_visible_mode_set(efl_part(obj, "indicator"), flag ? ELM_SLIDER_INDICATOR_VISIBLE_MODE_ON_FOCUS : ELM_SLIDER_INDICATOR_VISIBLE_MODE_DEFAULT); } EAPI Eina_Bool elm_slider_indicator_show_on_focus_get(const Evas_Object *obj) { return (efl_ui_slider_part_indicator_visible_mode_get(efl_part(obj, "indicator")) == EFL_UI_SLIDER_INDICATOR_VISIBLE_MODE_ON_FOCUS); } EAPI void elm_slider_indicator_show_set(Evas_Object *obj, Eina_Bool show) { EFL_UI_SLIDER_DATA_GET_OR_RETURN(obj, sd); if (show) { sd->indicator_show = EINA_TRUE; elm_layout_signal_emit(obj, "elm,state,val,show", "elm"); if (sd->popup) edje_object_signal_emit(sd->popup, "elm,state,val,show", "elm"); if (sd->popup2) edje_object_signal_emit(sd->popup2, "elm,state,val,show", "elm"); } else { sd->indicator_show = EINA_FALSE; elm_layout_signal_emit(obj, "elm,state,val,hide", "elm"); if (sd->popup) edje_object_signal_emit(sd->popup, "elm,state,val,hide", "elm"); if (sd->popup2) edje_object_signal_emit(sd->popup2, "elm,state,val,hide", "elm"); } evas_object_smart_changed(obj); } EAPI Eina_Bool elm_slider_indicator_show_get(const Evas_Object *obj) { EFL_UI_SLIDER_DATA_GET_OR_RETURN(obj, sd, EINA_FALSE); return sd->indicator_show; } EAPI void elm_slider_indicator_visible_mode_set(Evas_Object *obj, Elm_Slider_Indicator_Visible_Mode indicator_visible_mode) { efl_ui_slider_part_indicator_visible_mode_set(efl_part(obj, "indicator"), indicator_visible_mode); } EAPI Elm_Slider_Indicator_Visible_Mode elm_slider_indicator_visible_mode_get(const Evas_Object *obj) { return efl_ui_slider_part_indicator_visible_mode_get(efl_part(obj, "indicator")); } /* Internal EO APIs and hidden overrides */ ELM_LAYOUT_CONTENT_ALIASES_IMPLEMENT(efl_ui_slider) ELM_LAYOUT_TEXT_ALIASES_IMPLEMENT(efl_ui_slider) #define EFL_UI_SLIDER_EXTRA_OPS \ ELM_LAYOUT_CONTENT_ALIASES_OPS(efl_ui_slider), \ ELM_LAYOUT_TEXT_ALIASES_OPS(efl_ui_slider), \ ELM_LAYOUT_SIZING_EVAL_OPS(efl_ui_slider), \ EFL_CANVAS_GROUP_ADD_DEL_OPS(efl_ui_slider) #include "efl_ui_slider.eo.c" diff --git a/src/lib/elementary/efl_ui_slider.eo b/src/lib/elementary/efl_ui_slider.eo index 55007f3e24..50092849f0 100644 --- a/src/lib/elementary/efl_ui_slider.eo +++ b/src/lib/elementary/efl_ui_slider.eo @@ -1,65 +1,65 @@ class Efl.Ui.Slider (Efl.Ui.Layout, Efl.Ui.Range, Efl.Ui.Direction, Efl.Access.Value, Efl.Text, Efl.Text.Markup, Efl.Ui.Format, Efl.Ui.Translatable, Efl.Access.Widget.Action, Efl.Content) { [[Elementary slider class]] legacy_prefix: elm_slider; methods { @property step { set { [[Set the step by which slider indicator will move. This value is used when a draggable object is moved automatically such as when key events like the up/down/left/right key are pressed or in case accessibility is set and flick event is used to inc/dec slider values. By default step value is 0.05. @since 1.8 ]] } get { [[Get the step by which slider indicator moves. @since 1.8 ]] } values { step: double; [[The step value.]] } } } parts { indicator: Efl.Ui.Slider.Part_Indicator; [[A floating indicator above the slider.]] } implements { class.constructor; Efl.Object.constructor; Efl.Canvas.Group.group_calculate; Efl.Ui.Widget.theme_apply; Efl.Ui.Widget.on_access_activate; - Efl.Ui.Widget.on_focus_update; + Efl.Ui.Focus.Object.on_focus_update; Efl.Ui.Widget.widget_event; Efl.Ui.Range.range_value { get; set; } Efl.Ui.Range.range_min_max {get; set; } Efl.Ui.Direction.direction { get; set; } Efl.Access.Value.value_and_text { get; set; } Efl.Access.Value.range { get; } Efl.Access.Value.increment { get; } Efl.Access.Widget.Action.elm_actions { get; } Efl.Text.text { get; set; } Efl.Text.Markup.markup { get; set; } Efl.Ui.Format.format_cb { set; } Efl.Ui.Translatable.translatable_text { get; set; } Efl.Content.content { get; set; } Efl.Content.content_unset; Efl.Part.part; } events { changed; [[Called when slider changed]] delay,changed; [[Called when delay changed]] slider,drag,start; [[Called when slider drag started]] slider,drag,stop; [[Called when slider drag stopped]] } } diff --git a/src/lib/elementary/efl_ui_spin_button.c b/src/lib/elementary/efl_ui_spin_button.c index 1c97c84382..34d969d4ae 100644 --- a/src/lib/elementary/efl_ui_spin_button.c +++ b/src/lib/elementary/efl_ui_spin_button.c @@ -1,899 +1,899 @@ #ifdef HAVE_CONFIG_H # include "elementary_config.h" #endif #define EFL_ACCESS_PROTECTED #define EFL_ACCESS_VALUE_PROTECTED #define EFL_ACCESS_WIDGET_ACTION_PROTECTED #define EFL_UI_FOCUS_COMPOSITION_PROTECTED #include #include "elm_priv.h" #include "efl_ui_spin_button_private.h" #include "efl_ui_spin_private.h" #include "elm_entry.eo.h" #define MY_CLASS EFL_UI_SPIN_BUTTON_CLASS #define MY_CLASS_NAME "Efl.Ui.Spin_Button" #define EFL_UI_SPIN_BUTTON_DELAY_CHANGE_TIME 0.2 static const char PART_NAME_DEC_BUTTON[] = "dec_button"; static const char PART_NAME_TEXT_BUTTON[] = "text_button"; static const char PART_NAME_INC_BUTTON[] = "inc_button"; static void _inc_dec_button_clicked_cb(void *data, const Efl_Event *event); static void _inc_dec_button_pressed_cb(void *data, const Efl_Event *event); static void _inc_dec_button_unpressed_cb(void *data, const Efl_Event *event); static void _inc_dec_button_mouse_move_cb(void *data, const Efl_Event *event); static void _entry_activated_cb(void *data, const Efl_Event *event); static void _entry_focus_changed_cb(void *data, const Efl_Event *event); static void _access_increment_decrement_info_say(Evas_Object *obj, Eina_Bool is_incremented); EFL_CALLBACKS_ARRAY_DEFINE(_inc_dec_button_cb, { EFL_UI_EVENT_CLICKED, _inc_dec_button_clicked_cb}, { EFL_UI_EVENT_PRESSED, _inc_dec_button_pressed_cb}, { EFL_UI_EVENT_UNPRESSED, _inc_dec_button_unpressed_cb}, { EFL_EVENT_POINTER_MOVE, _inc_dec_button_mouse_move_cb } ); static void _entry_show(Evas_Object *obj) { Efl_Ui_Spin_Button_Data *sd = efl_data_scope_get(obj, MY_CLASS); Efl_Ui_Spin_Data *pd = efl_data_scope_get(obj, EFL_UI_SPIN_CLASS); char buf[32], fmt[32] = "%0.f"; /* try to construct just the format from given label * completely ignoring pre/post words */ if (pd->templates) { const char *start = strchr(pd->templates, '%'); while (start) { /* handle %% */ if (start[1] != '%') break; else start = strchr(start + 2, '%'); } if (start) { const char *itr, *end = NULL; for (itr = start + 1; *itr != '\0'; itr++) { if ((*itr == 'd') || (*itr == 'u') || (*itr == 'i') || (*itr == 'o') || (*itr == 'x') || (*itr == 'X') || (*itr == 'f') || (*itr == 'F')) { end = itr + 1; break; } } if ((end) && ((size_t)(end - start + 1) < sizeof(fmt))) { memcpy(fmt, start, end - start); fmt[end - start] = '\0'; } } } if (pd->format_type == SPIN_FORMAT_INT) snprintf(buf, sizeof(buf), fmt, (int)pd->val); else snprintf(buf, sizeof(buf), fmt, pd->val); elm_object_text_set(sd->ent, buf); } static void _label_write(Evas_Object *obj) { Efl_Ui_Spin_Button_Data *sd = efl_data_scope_get(obj, MY_CLASS); Efl_Ui_Spin_Data *pd = efl_data_scope_get(obj, EFL_UI_SPIN_CLASS); if (pd->templates) { efl_text_set(sd->text_button, pd->templates); } else { char buf[1024]; snprintf(buf, sizeof(buf), "%.0f", pd->val); efl_text_set(sd->text_button, buf); } } static Eina_Bool _delay_change_timer_cb(void *data) { Efl_Ui_Spin_Button_Data *sd = efl_data_scope_get(data, MY_CLASS); sd->delay_change_timer = NULL; efl_event_callback_call(data, EFL_UI_SPIN_BUTTON_EVENT_DELAY_CHANGED, NULL); return ECORE_CALLBACK_CANCEL; } static Eina_Bool _value_set(Evas_Object *obj, double new_val) { Efl_Ui_Spin_Button_Data *sd = efl_data_scope_get(obj, MY_CLASS); Efl_Ui_Spin_Data *pd = efl_data_scope_get(obj, EFL_UI_SPIN_CLASS); if (sd->circulate) { if (new_val < pd->val_min) new_val = pd->val_max; else if (new_val > pd->val_max) new_val = pd->val_min; } efl_ui_range_value_set(obj, new_val); ecore_timer_del(sd->delay_change_timer); sd->delay_change_timer = ecore_timer_add(EFL_UI_SPIN_BUTTON_DELAY_CHANGE_TIME, _delay_change_timer_cb, obj); return EINA_TRUE; } static void _entry_hide(Evas_Object *obj) { Efl_Ui_Spin_Button_Data *sd = efl_data_scope_get(obj, MY_CLASS); elm_layout_signal_emit(obj, "elm,state,button,active", "elm"); elm_layout_signal_emit(obj, "elm,state,entry,inactive", "elm"); if (sd->entry_visible && !evas_focus_state_get(evas_object_evas_get(obj))) sd->entry_reactivate = EINA_TRUE; sd->entry_visible = EINA_FALSE; } static void _entry_value_apply(Evas_Object *obj) { const char *str; double val; char *end; Efl_Ui_Spin_Button_Data *sd = efl_data_scope_get(obj, MY_CLASS); Efl_Ui_Spin_Data *pd = efl_data_scope_get(obj, EFL_UI_SPIN_CLASS); if (!sd->entry_visible) return; efl_event_callback_del(sd->ent, EFL_UI_FOCUS_OBJECT_EVENT_FOCUS_CHANGED, _entry_focus_changed_cb, obj); _entry_hide(obj); str = elm_object_text_get(sd->ent); if (!str) return; val = strtod(str, &end); if (((*end != '\0') && (!isspace(*end))) || (fabs(val - pd->val) < DBL_EPSILON)) return; efl_ui_range_value_set(obj, val); efl_event_callback_call(obj, EFL_UI_SPIN_EVENT_CHANGED, NULL); ecore_timer_del(sd->delay_change_timer); sd->delay_change_timer = ecore_timer_add(EFL_UI_SPIN_BUTTON_DELAY_CHANGE_TIME, _delay_change_timer_cb, obj); } static void _invalid_input_validity_filter(void *data EINA_UNUSED, Evas_Object *obj, char **text) { char *insert = NULL; const char *str = NULL; int cursor_pos = 0; int read_idx = 0, read_char, cmp_char; EINA_SAFETY_ON_NULL_RETURN(obj); EINA_SAFETY_ON_NULL_RETURN(text); insert = *text; str = elm_object_text_get(obj); evas_string_char_next_get(*text, 0, &read_char); cursor_pos = elm_entry_cursor_pos_get(obj); if (read_char) { if (read_char == '-') { if (cursor_pos != 0) { goto invalid_input; } } if (read_char == '.') { read_idx = evas_string_char_next_get(str, 0, &cmp_char); while (cmp_char) { if (read_char == cmp_char) { goto invalid_input; } read_idx = evas_string_char_next_get(str, read_idx, &cmp_char); } } read_idx = evas_string_char_next_get(str, 0, &cmp_char); if ((cmp_char == '-') && (cursor_pos == 0)) { goto invalid_input; } } return; invalid_input: *insert = 0; } static void _entry_accept_filter_add(Evas_Object *obj) { Efl_Ui_Spin_Button_Data *sd = efl_data_scope_get(obj, MY_CLASS); Efl_Ui_Spin_Data *pd = efl_data_scope_get(obj, EFL_UI_SPIN_CLASS); static Elm_Entry_Filter_Accept_Set digits_filter_data; if (!sd->ent) return; elm_entry_markup_filter_remove(sd->ent, elm_entry_filter_accept_set, &digits_filter_data); if (pd->decimal_points > 0) digits_filter_data.accepted = "-.0123456789"; else digits_filter_data.accepted = "-0123456789"; elm_entry_markup_filter_prepend(sd->ent, elm_entry_filter_accept_set, &digits_filter_data); } static char * _text_insert(const char *text, const char *input, int pos) { char *result = NULL; int text_len, input_len; text_len = evas_string_char_len_get(text); input_len = evas_string_char_len_get(input); result = (char *)calloc(text_len + input_len + 1, sizeof(char)); if (!result) return NULL; strncpy(result, text, pos); strcpy(result + pos, input); strcpy(result + pos + input_len, text + pos); return result; } static void _min_max_validity_filter(void *data, Evas_Object *obj, char **text) { const char *str, *point; char *insert, *new_str = NULL; double val; int max_len = 0, len; EINA_SAFETY_ON_NULL_RETURN(data); EINA_SAFETY_ON_NULL_RETURN(obj); EINA_SAFETY_ON_NULL_RETURN(text); Efl_Ui_Spin_Data *pd = efl_data_scope_get(data, EFL_UI_SPIN_CLASS); str = elm_object_text_get(obj); if (!str) return; insert = *text; new_str = _text_insert(str, insert, elm_entry_cursor_pos_get(obj)); if (!new_str) return; if (strchr(new_str, '-')) max_len++; if (pd->format_type == SPIN_FORMAT_FLOAT) { point = strchr(new_str, '.'); if (point) { if ((int) strlen(point + 1) > pd->decimal_points) { *insert = 0; goto end; } } } max_len += (fabs(pd->val_max) > fabs(pd->val_min)) ? (log10(fabs(pd->val_max)) + 1) : (log10(fabs(pd->val_min)) + 1); len = strlen(new_str); if (len < max_len) goto end; val = strtod(new_str, NULL); if ((val < pd->val_min) || (val > pd->val_max)) *insert = 0; end: free(new_str); } static void _entry_show_cb(void *data, Evas *e EINA_UNUSED, Evas_Object *obj, void *event_info EINA_UNUSED) { Efl_Ui_Spin_Button_Data *sd = efl_data_scope_get(data, MY_CLASS); _entry_show(data); elm_object_focus_set(obj, EINA_TRUE); elm_entry_select_all(obj); sd->entry_visible = EINA_TRUE; elm_layout_signal_emit(data, "elm,state,button,inactive", "elm"); } static void _toggle_entry(Evas_Object *obj) { Efl_Ui_Spin_Button_Data *sd = efl_data_scope_get(obj, MY_CLASS); if (elm_widget_disabled_get(obj)) return; if (!sd->editable) return; if (sd->entry_visible) _entry_value_apply(obj); else { if (!sd->ent) { //FIXME: elm_entry will be changed to efl_ui_text after // filter feature implemented. // (Current efl_ui_text has missed filter feature.) sd->ent = elm_entry_add(obj); Eina_Strbuf *buf = eina_strbuf_new(); eina_strbuf_append_printf(buf, "spinner/%s", elm_widget_style_get(obj)); elm_widget_style_set(sd->ent, eina_strbuf_string_get(buf)); eina_strbuf_free(buf); evas_object_event_callback_add (sd->ent, EVAS_CALLBACK_SHOW, _entry_show_cb, obj); elm_entry_single_line_set(sd->ent, EINA_TRUE); elm_layout_content_set(obj, "elm.swallow.entry", sd->ent); _entry_accept_filter_add(obj); elm_entry_markup_filter_append(sd->ent, _invalid_input_validity_filter, NULL); if (_elm_config->spinner_min_max_filter_enable) elm_entry_markup_filter_append(sd->ent, _min_max_validity_filter, obj); efl_event_callback_add(sd->ent, ELM_ENTRY_EVENT_ACTIVATED, _entry_activated_cb, obj); } efl_event_callback_add(sd->ent, EFL_UI_FOCUS_OBJECT_EVENT_FOCUS_CHANGED, _entry_focus_changed_cb, obj); sd->entry_visible = EINA_TRUE; elm_layout_signal_emit(obj, "elm,state,entry,active", "elm"); { Eina_List *items = NULL; items = eina_list_append(items, sd->dec_button); items = eina_list_append(items, sd->text_button); items = eina_list_append(items, sd->ent); items = eina_list_append(items, sd->inc_button); efl_ui_focus_composition_elements_set(obj, items); } } } static void _entry_toggle_cb(void *data EINA_UNUSED, Evas_Object *obj, const char *emission EINA_UNUSED, const char *source EINA_UNUSED) { _toggle_entry(obj); } static Eina_Bool _spin_value(void *data) { Efl_Ui_Spin_Button_Data *sd = efl_data_scope_get(data, MY_CLASS); Efl_Ui_Spin_Data *pd = efl_data_scope_get(data, EFL_UI_SPIN_CLASS); if (_value_set(data, pd->val + (sd->inc_val ? pd->step : -pd->step))) _label_write(data); return ECORE_CALLBACK_RENEW; } static void _spin_stop(Evas_Object *obj) { Efl_Ui_Spin_Button_Data *sd = efl_data_scope_get(obj, MY_CLASS); ELM_SAFE_FREE(sd->spin_timer, ecore_timer_del); elm_widget_scroll_freeze_pop(obj); } static Eina_Bool _inc_dec_button_press_start(void *data) { Efl_Ui_Spin_Button_Data *sd = efl_data_scope_get(data, MY_CLASS); sd->interval = sd->first_interval; sd->longpress_timer = NULL; ecore_timer_del(sd->spin_timer); sd->spin_timer = ecore_timer_add(sd->interval, _spin_value, data); _spin_value(data); elm_widget_scroll_freeze_push(data); return ECORE_CALLBACK_CANCEL; } static void _inc_dec_button_clicked_cb(void *data, const Efl_Event *event) { Efl_Ui_Spin_Button_Data *sd = efl_data_scope_get(data, MY_CLASS); _spin_stop(data); sd->inc_val = sd->inc_button == event->object ? EINA_TRUE : EINA_FALSE; _spin_value(data); if (_elm_config->access_mode) _access_increment_decrement_info_say(data, EINA_TRUE); } static void _inc_dec_button_pressed_cb(void *data, const Efl_Event *event) { Efl_Ui_Spin_Button_Data *sd = efl_data_scope_get(data, MY_CLASS); sd->inc_val = sd->inc_button == event->object ? EINA_TRUE : EINA_FALSE; if (sd->longpress_timer) ecore_timer_del(sd->longpress_timer); sd->longpress_timer = ecore_timer_add (_elm_config->longpress_timeout, _inc_dec_button_press_start, data); if (sd->entry_visible) _entry_value_apply(data); } static void _inc_dec_button_unpressed_cb(void *data, const Efl_Event *event EINA_UNUSED) { Efl_Ui_Spin_Button_Data *sd = efl_data_scope_get(data, MY_CLASS); if (sd->longpress_timer) { ecore_timer_del(sd->longpress_timer); sd->longpress_timer = NULL; } _spin_stop(data); } static void _inc_dec_button_mouse_move_cb(void *data, const Efl_Event *event) { Efl_Input_Pointer *ev = event->info; Efl_Ui_Spin_Button_Data *sd = efl_data_scope_get(data, MY_CLASS); if (efl_input_processed_get(ev) && sd->longpress_timer) { ecore_timer_del(sd->longpress_timer); sd->longpress_timer = NULL; } } static void _text_button_focus_changed_cb(void *data, const Efl_Event *event) { if (efl_ui_focus_object_focus_get(event->object)) _toggle_entry(data); } static void _entry_activated_cb(void *data, const Efl_Event *event EINA_UNUSED) { _toggle_entry(data); } static void _entry_focus_changed_cb(void *data, const Efl_Event *event) { if (!efl_ui_focus_object_focus_get(event->object)) _toggle_entry(data); } static void _text_button_clicked_cb(void *data, const Efl_Event *event EINA_UNUSED) { Efl_Ui_Spin_Button_Data *sd = efl_data_scope_get(data, MY_CLASS); if (sd->entry_visible) return; _toggle_entry(data); } static Eina_Bool _key_action_toggle(Evas_Object *obj, const char *params EINA_UNUSED) { Efl_Ui_Spin_Button_Data *sd = efl_data_scope_get(obj, MY_CLASS); if (sd->spin_timer) _spin_stop(obj); else if (sd->entry_visible) _entry_toggle_cb(NULL, obj, NULL, NULL); return EINA_FALSE; } EOLIAN static Eina_Bool _efl_ui_spin_button_efl_ui_widget_widget_event(Eo *obj, Efl_Ui_Spin_Button_Data *sd EINA_UNUSED, const Efl_Event *eo_event, Evas_Object *src EINA_UNUSED) { Eo *ev = eo_event->info; if (efl_input_processed_get(ev)) return EINA_FALSE; if (eo_event->desc == EFL_EVENT_KEY_DOWN) { if (sd->spin_timer) _spin_stop(obj); else return EINA_FALSE; } else if (eo_event->desc == EFL_EVENT_KEY_UP) { if (sd->spin_timer) _spin_stop(obj); else return EINA_FALSE; } else if (eo_event->desc == EFL_EVENT_POINTER_WHEEL) { sd->interval = sd->first_interval; if (efl_input_pointer_wheel_delta_get(ev) < 0) sd->inc_val = EINA_TRUE; else sd->inc_val = EINA_FALSE; _spin_value(obj); } else return EINA_FALSE; efl_input_processed_set(ev, EINA_TRUE); return EINA_TRUE; } EOLIAN static Eina_Bool -_efl_ui_spin_button_efl_ui_widget_on_focus_update(Eo *obj, Efl_Ui_Spin_Button_Data *sd) +_efl_ui_spin_button_efl_ui_focus_object_on_focus_update(Eo *obj, Efl_Ui_Spin_Button_Data *sd) { Eina_Bool int_ret = EINA_FALSE; - int_ret = efl_ui_widget_on_focus_update(efl_super(obj, MY_CLASS)); + int_ret = efl_ui_focus_object_on_focus_update(efl_super(obj, MY_CLASS)); if (!int_ret) return EINA_FALSE; if (!efl_ui_focus_object_focus_get(obj)) { ELM_SAFE_FREE(sd->delay_change_timer, ecore_timer_del); ELM_SAFE_FREE(sd->spin_timer, ecore_timer_del); } else { if (sd->entry_reactivate) { _toggle_entry(obj); sd->entry_reactivate = EINA_FALSE; } } return EINA_TRUE; } EOLIAN static void _efl_ui_spin_button_elm_layout_sizing_eval(Eo *obj, Efl_Ui_Spin_Button_Data *_pd EINA_UNUSED) { Evas_Coord minw = -1, minh = -1; ELM_WIDGET_DATA_GET_OR_RETURN(obj, wd); elm_coords_finger_size_adjust(1, &minw, 1, &minh); edje_object_size_min_restricted_calc (wd->resize_obj, &minw, &minh, minw, minh); elm_coords_finger_size_adjust(1, &minw, 1, &minh); evas_object_size_hint_min_set(obj, minw, minh); evas_object_size_hint_max_set(obj, -1, -1); } static char * _access_info_cb(void *data, Evas_Object *obj EINA_UNUSED) { const char *txt = NULL; Efl_Ui_Spin_Button_Data *sd = efl_data_scope_get(data, MY_CLASS); if (sd->entry_visible) txt = elm_object_text_get(sd->ent); else txt = efl_text_get(sd->text_button); if (txt) return strdup(txt); return NULL; } static char * _access_state_cb(void *data, Evas_Object *obj EINA_UNUSED) { if (elm_widget_disabled_get(data)) return strdup(E_("State: Disabled")); return NULL; } static void _access_activate_spin_button_cb(void *data, Evas_Object *part_obj EINA_UNUSED, Elm_Object_Item *item EINA_UNUSED) { Efl_Ui_Spin_Button_Data *sd = efl_data_scope_get(data, MY_CLASS); if (elm_widget_disabled_get(data)) return; if (!sd->entry_visible) _toggle_entry(data); } static void _access_increment_decrement_info_say(Evas_Object *obj, Eina_Bool is_incremented) { Eina_Strbuf *buf; Efl_Ui_Spin_Button_Data *sd = efl_data_scope_get(obj, MY_CLASS); buf = eina_strbuf_new(); if (is_incremented) { elm_object_signal_emit (sd->inc_button, "elm,action,anim,activate", "elm"); eina_strbuf_append(buf, E_("incremented")); } else { elm_object_signal_emit (sd->dec_button, "elm,action,anim,activate", "elm"); eina_strbuf_append(buf, E_("decremented")); } eina_strbuf_append_printf (buf, "%s", elm_object_text_get(sd->text_button)); _elm_access_say(eina_strbuf_string_get(buf)); eina_strbuf_free(buf); } static void _access_spinner_register(Evas_Object *obj, Eina_Bool is_access) { Evas_Object *ao; Elm_Access_Info *ai; Efl_Ui_Spin_Button_Data *sd = efl_data_scope_get(obj, MY_CLASS); if (!is_access) { /* unregister access */ _elm_access_edje_object_part_object_unregister (obj, elm_layout_edje_get(obj), "access"); elm_layout_signal_emit(obj, "elm,state,access,inactive", "elm"); return; } elm_layout_signal_emit(obj, "elm,state,access,active", "elm"); ao = _elm_access_edje_object_part_object_register (obj, elm_layout_edje_get(obj), "access"); ai = _elm_access_info_get(ao); _elm_access_text_set(ai, ELM_ACCESS_TYPE, E_("spinner")); _elm_access_callback_set(ai, ELM_ACCESS_STATE, _access_state_cb, obj); _elm_access_activate_callback_set(ai, _access_activate_spin_button_cb, obj); /*Do not register spinner buttons if widget is disabled*/ if (!elm_widget_disabled_get(obj)) { ai = _elm_access_info_get(sd->inc_button); _elm_access_text_set(ai, ELM_ACCESS_TYPE, E_("spinner increment button")); ai = _elm_access_info_get(sd->dec_button); _elm_access_text_set(ai, ELM_ACCESS_TYPE, E_("spinner decrement button")); ai = _elm_access_info_get(sd->text_button); _elm_access_text_set(ai, ELM_ACCESS_TYPE, E_("spinner text")); _elm_access_callback_set(ai, ELM_ACCESS_INFO, _access_info_cb, obj); } } EOLIAN static Efl_Ui_Theme_Apply _efl_ui_spin_button_efl_ui_widget_theme_apply(Eo *obj, Efl_Ui_Spin_Button_Data *sd EINA_UNUSED) { Efl_Ui_Theme_Apply int_ret = EFL_UI_THEME_APPLY_FAILED; int_ret = efl_ui_widget_theme_apply(efl_super(obj, MY_CLASS)); if (!int_ret) return EFL_UI_THEME_APPLY_FAILED; if (sd->ent) { //elm_widget_element_update(obj, sd->ent, PART_NAME_TEXT); Eina_Strbuf *buf = eina_strbuf_new(); eina_strbuf_append_printf(buf, "spin_button/%s", elm_widget_style_get(obj)); elm_widget_style_set(sd->ent, eina_strbuf_string_get(buf)); eina_strbuf_free(buf); } if (sd->inc_button) elm_widget_element_update(obj, sd->inc_button, PART_NAME_INC_BUTTON); if (sd->text_button) elm_widget_element_update(obj, sd->text_button, PART_NAME_TEXT_BUTTON); if (sd->dec_button) elm_widget_element_update(obj, sd->dec_button, PART_NAME_DEC_BUTTON); if (_elm_config->access_mode) _access_spinner_register(obj, EINA_TRUE); _label_write(obj); elm_layout_sizing_eval(obj); return EFL_UI_THEME_APPLY_SUCCESS; } EOLIAN static Eo * _efl_ui_spin_button_efl_object_constructor(Eo *obj, Efl_Ui_Spin_Button_Data *sd) { if (!elm_widget_theme_klass_get(obj)) elm_widget_theme_klass_set(obj, "spin_button"); obj = efl_constructor(efl_super(obj, MY_CLASS)); elm_widget_sub_object_parent_add(obj); sd->first_interval = 0.85; sd->inc_button = efl_add(EFL_UI_BUTTON_CLASS, obj, elm_widget_element_update(obj, efl_added, PART_NAME_INC_BUTTON), efl_event_callback_array_add(efl_added, _inc_dec_button_cb(), obj), efl_content_set(efl_part(obj, "elm.swallow.inc_button"), efl_added)); sd->text_button = efl_add(EFL_UI_BUTTON_CLASS, obj, elm_widget_element_update(obj, efl_added, PART_NAME_TEXT_BUTTON), efl_event_callback_add(efl_added, EFL_UI_EVENT_CLICKED, _text_button_clicked_cb, obj), efl_event_callback_add(efl_added, EFL_UI_FOCUS_OBJECT_EVENT_FOCUS_CHANGED, _text_button_focus_changed_cb, obj), efl_content_set(efl_part(obj, "elm.swallow.text_button"), efl_added)); sd->dec_button = efl_add(EFL_UI_BUTTON_CLASS, obj, elm_widget_element_update(obj, efl_added, PART_NAME_DEC_BUTTON), efl_event_callback_array_add(efl_added, _inc_dec_button_cb(), obj), efl_content_set(efl_part(obj, "elm.swallow.dec_button"), efl_added)); { Eina_List *items = NULL; items = eina_list_append(items, sd->dec_button); items = eina_list_append(items, sd->text_button); items = eina_list_append(items, sd->inc_button); efl_ui_focus_composition_elements_set(obj, items); } elm_layout_signal_callback_add (obj, "elm,action,entry,toggle", "*", _entry_toggle_cb, NULL); elm_widget_can_focus_set(obj, EINA_TRUE); efl_access_role_set(obj, EFL_ACCESS_ROLE_SPIN_BUTTON); return obj; } EOLIAN static void _efl_ui_spin_button_efl_ui_range_range_value_set(Eo *obj, Efl_Ui_Spin_Button_Data *sd EINA_UNUSED, double val) { efl_ui_range_value_set(efl_super(obj, MY_CLASS), val); _label_write(obj); } EOLIAN static void _efl_ui_spin_button_editable_set(Eo *obj EINA_UNUSED, Efl_Ui_Spin_Button_Data *sd, Eina_Bool editable) { sd->editable = editable; } EOLIAN static Eina_Bool _efl_ui_spin_button_editable_get(Eo *obj EINA_UNUSED, Efl_Ui_Spin_Button_Data *sd) { return sd->editable; } EOLIAN static void _efl_ui_spin_button_circulate_set(Eo *obj EINA_UNUSED, Efl_Ui_Spin_Button_Data *sd, Eina_Bool circulate) { sd->circulate = circulate; } EOLIAN static Eina_Bool _efl_ui_spin_button_circulate_get(Eo *obj EINA_UNUSED, Efl_Ui_Spin_Button_Data *sd) { return sd->circulate; } EOLIAN static const Efl_Access_Action_Data * _efl_ui_spin_button_efl_access_widget_action_elm_actions_get(Eo *obj EINA_UNUSED, Efl_Ui_Spin_Button_Data *sd EINA_UNUSED) { static Efl_Access_Action_Data atspi_actions[] = { { "toggle", "toggle", NULL, _key_action_toggle}, { NULL, NULL, NULL, NULL } }; return &atspi_actions[0]; } // A11Y Accessibility EOLIAN static void _efl_ui_spin_button_efl_access_value_value_and_text_get(Eo *obj EINA_UNUSED, Efl_Ui_Spin_Button_Data *sd EINA_UNUSED, double *value, const char **text) { Efl_Ui_Spin_Data *pd = efl_data_scope_get(obj, EFL_UI_SPIN_CLASS); if (value) *value = pd->val; if (text) *text = NULL; } EOLIAN static Eina_Bool _efl_ui_spin_button_efl_access_value_value_and_text_set(Eo *obj, Efl_Ui_Spin_Button_Data *sd EINA_UNUSED, double value, const char *text EINA_UNUSED) { Efl_Ui_Spin_Data *pd = efl_data_scope_get(obj, EFL_UI_SPIN_CLASS); if (pd->val_min > value) return EINA_FALSE; if (pd->val_max < value) return EINA_FALSE; pd->val = value; efl_ui_range_value_set(efl_super(obj, MY_CLASS), value); return EINA_TRUE; } EOLIAN static void _efl_ui_spin_button_efl_access_value_range_get(Eo *obj EINA_UNUSED, Efl_Ui_Spin_Button_Data *sd EINA_UNUSED, double *lower, double *upper, const char **descr) { Efl_Ui_Spin_Data *pd = efl_data_scope_get(obj, EFL_UI_SPIN_CLASS); if (lower) *lower = pd->val_min; if (upper) *upper = pd->val_max; if (descr) *descr = NULL; } EOLIAN static double _efl_ui_spin_button_efl_access_value_increment_get(Eo *obj EINA_UNUSED, Efl_Ui_Spin_Button_Data *sd EINA_UNUSED) { Efl_Ui_Spin_Data *pd = efl_data_scope_get(obj, EFL_UI_SPIN_CLASS); return pd->step; } EOLIAN static const char* _efl_ui_spin_button_efl_access_name_get(Eo *obj, Efl_Ui_Spin_Button_Data *sd EINA_UNUSED) { const char *name; name = efl_access_name_get(efl_super(obj, EFL_UI_SPIN_BUTTON_CLASS)); if (name) return name; const char *ret = elm_layout_text_get(obj, "elm.text"); return ret; } // A11Y Accessibility - END #define EFL_UI_SPIN_BUTTON_EXTRA_OPS \ ELM_LAYOUT_SIZING_EVAL_OPS(efl_ui_spin_button), \ #include "efl_ui_spin_button.eo.c" diff --git a/src/lib/elementary/efl_ui_spin_button.eo b/src/lib/elementary/efl_ui_spin_button.eo index 581e0b493f..1bf2f2ac0c 100644 --- a/src/lib/elementary/efl_ui_spin_button.eo +++ b/src/lib/elementary/efl_ui_spin_button.eo @@ -1,80 +1,80 @@ class Efl.Ui.Spin_Button (Efl.Ui.Spin, Efl.Ui.Focus.Composition, Efl.Access.Value, Efl.Access.Widget.Action) { [[A Button Spin. This is a widget which allows the user to increase or decrease numeric values using the arrow buttons or to edit values directly by clicking over them and inputting new ones. @since 1.21 ]] methods { @property circulate { [[Control whether the spin should circulate value when it reaches its minimum or maximum value. Disabled by default. If disabled, when the user tries to increment the value but displayed value plus step value is bigger than maximum value, the new value will be the maximum value. The same happens when the user tries to decrement it but the value less step is less than minimum value. In this case, the new displayed value will be the minimum value. If enabled, when the user tries to increment the value but displayed value plus step value is bigger than maximum value, the new value will become the minimum value. When the the user tries to decrement it, if the value minus step is less than minimum value, the new displayed value will be the maximum value. E.g.: $min = 10 $max = 50 $step = 20 $displayed = 20 When the user decrements the value (using left or bottom arrow), it will display $50.]] set { } get { } values { circulate: bool(false); [[$true to enable circulate or $false to disable it.]] } } @property editable { [[Control whether the spin can be directly edited by the user. Spin objects can have editing disabled, in which case they can only be changed by using arrows. This is useful for situations where you don't want your users to write their own value. It's especially useful when using special values. The user can see the real values instead of special label when editing.]] set { } get { } values { editable: bool(false); [[$true to allow users to edit it or $false to don't allow users to edit it directly.]] } } } implements { Efl.Object.constructor; Efl.Ui.Widget.theme_apply; Efl.Ui.Widget.widget_event; - Efl.Ui.Widget.on_focus_update; + Efl.Ui.Focus.Object.on_focus_update; Efl.Ui.Range.range_value { set; } Efl.Access.name { get; } Efl.Access.Value.value_and_text { get; set; } Efl.Access.Value.range { get; } Efl.Access.Value.increment { get; } Efl.Access.Widget.Action.elm_actions { get; } } events { delay,changed; [[Called when spin delay is changed.]] } } diff --git a/src/lib/elementary/efl_ui_text.c b/src/lib/elementary/efl_ui_text.c index 8102b4576f..ca76023171 100644 --- a/src/lib/elementary/efl_ui_text.c +++ b/src/lib/elementary/efl_ui_text.c @@ -1,5298 +1,5298 @@ #ifdef HAVE_CONFIG_H # include "elementary_config.h" #endif #define EFL_ACCESS_PROTECTED #define EFL_ACCESS_TEXT_PROTECTED #define EFL_ACCESS_EDITABLE_TEXT_PROTECTED #define ELM_LAYOUT_PROTECTED #include #include #include "elm_priv.h" //#include "elm_entry_part.eo.h" //#include "elm_part_helper.h" // #include "elm_interface_scrollable.h" #include "elm_widget_layout.h" #include "elm_entry_common.h" #include "elm_widget_entry.h" #include "efl_ui_text.eo.h" #include "elm_hoversel.eo.h" typedef struct _Efl_Ui_Text_Data Efl_Ui_Text_Data; typedef struct _Efl_Ui_Text_Rectangle Efl_Ui_Text_Rectangle; typedef struct _Anchor Anchor; typedef struct _Item_Obj Item_Obj; /** * Base widget smart data extended with entry instance data. */ struct _Efl_Ui_Text_Data { Evas_Object *hit_rect, *entry_edje, *scr_edje; Evas_Object *hoversel; Evas_Object *mgf_bg; Evas_Object *mgf_clip; Evas_Object *mgf_proxy; Eo *text_obj; Eo *cursor; Eo *cursor_bidi; Evas_Object *start_handler; Evas_Object *end_handler; Ecore_Job *deferred_decoration_job; Ecore_Timer *longpress_timer; Ecore_Timer *delay_write; /* for deferred appending */ Ecore_Idler *append_text_idler; char *append_text_left; int append_text_position; int append_text_len; /* Only for clipboard */ const char *cut_sel; const char *text; const char *file; Elm_Text_Format format; Evas_Coord ent_w, ent_h; Evas_Coord downx, downy; Evas_Coord ox, oy; Eina_List *anchors; Eina_List *item_anchors; Eina_List *sel; Eina_List *items; /** context menu item list */ Item_Obj *item_objs; Efl_Canvas_Text_Factory *item_factory; Efl_Canvas_Text_Factory *item_fallback_factory; Eina_List *markup_filters; Ecore_Job *hov_deljob; Mod_Api *api; // module api if supplied int cursor_pos; Elm_Scroller_Policy policy_h, policy_v; Elm_Wrap_Type line_wrap; Elm_Input_Panel_Layout input_panel_layout; Elm_Autocapital_Type autocapital_type; Elm_Input_Panel_Lang input_panel_lang; Elm_Input_Panel_Return_Key_Type input_panel_return_key_type; Elm_Input_Hints input_hints; Efl_Text_Cursor_Cursor *sel_handler_cursor; void *input_panel_imdata; int input_panel_imdata_len; int input_panel_layout_variation; int validators; struct { Evas_Object *hover_parent; /**< hover parent object. entry is a hover parent object by default */ Evas_Object *pop; /**< hidden icon for hover target */ Evas_Object *hover; /**< hover object */ const char *hover_style; /**< style of a hover object */ } anchor_hover; Elm_Cnp_Mode cnp_mode; Elm_Sel_Format drop_format; struct { char *text; Eina_Bool enabled; } async; Eina_Bool input_panel_return_key_disabled : 1; Eina_Bool drag_selection_asked : 1; Eina_Bool sel_handler_disabled : 1; Eina_Bool start_handler_down : 1; Eina_Bool start_handler_shown : 1; Eina_Bool end_handler_down : 1; Eina_Bool end_handler_shown : 1; Eina_Bool input_panel_enable : 1; Eina_Bool prediction_allow : 1; Eina_Bool selection_asked : 1; Eina_Bool auto_return_key : 1; Eina_Bool have_selection : 1; Eina_Bool deferred_cur : 1; Eina_Bool deferred_decoration_selection : 1; Eina_Bool deferred_decoration_cursor : 1; Eina_Bool deferred_decoration_anchor : 1; Eina_Bool context_menu : 1; Eina_Bool long_pressed : 1; Eina_Bool cur_changed : 1; Eina_Bool single_line : 1; Eina_Bool can_write : 1; Eina_Bool auto_save : 1; Eina_Bool password : 1; Eina_Bool editable : 1; // FIXME: This is redundant because of text interactive and should be removed Eina_Bool disabled : 1; Eina_Bool h_bounce : 1; Eina_Bool v_bounce : 1; Eina_Bool has_text : 1; Eina_Bool use_down : 1; Eina_Bool sel_mode : 1; Eina_Bool sel_allow : 1; Eina_Bool changed : 1; Eina_Bool scroll : 1; Eina_Bool input_panel_show_on_demand : 1; Eina_Bool anchors_updated : 1; Eina_Bool fallback_item_provider_disabled : 1; }; struct _Anchor { Eo *obj; char *name; Efl_Text_Annotate_Annotation *annotation; Eina_List *sel; Eina_Bool item : 1; }; struct _Item_Obj { EINA_INLIST; Anchor *an; char *name; Evas_Object *obj; }; #define EFL_UI_TEXT_DATA_GET(o, sd) \ Efl_Ui_Text_Data * sd = efl_data_scope_get(o, EFL_UI_TEXT_CLASS) #define EFL_UI_TEXT_DATA_GET_OR_RETURN(o, ptr) \ EFL_UI_TEXT_DATA_GET(o, ptr); \ if (EINA_UNLIKELY(!ptr)) \ { \ CRI("No widget data for object %p (%s)", \ o, evas_object_type_get(o)); \ return; \ } #define EFL_UI_TEXT_DATA_GET_OR_RETURN_VAL(o, ptr, val) \ EFL_UI_TEXT_DATA_GET(o, ptr); \ if (EINA_UNLIKELY(!ptr)) \ { \ CRI("No widget data for object %p (%s)", \ o, evas_object_type_get(o)); \ return val; \ } #define EFL_UI_TEXT_CHECK(obj) \ if (EINA_UNLIKELY(!efl_isa((obj), EFL_UI_TEXT_CLASS))) \ return struct _Efl_Ui_Text_Rectangle { Evas_Object *obj_bg, *obj_fg, *obj; }; #define MY_CLASS EFL_UI_TEXT_CLASS #define MY_CLASS_PFX efl_ui_text #define MY_CLASS_NAME "Efl.Ui.Text" #define MY_CLASS_NAME_LEGACY "elm_entry" #include "efl_ui_internal_text_interactive.h" /* Maximum chunk size to be inserted to the entry at once * FIXME: This size is arbitrary, should probably choose a better size. * Possibly also find a way to set it to a low value for weak computers, * and to a big value for better computers. */ #define EFL_UI_TEXT_CHUNK_SIZE 10000 #define EFL_UI_TEXT_DELAY_WRITE_TIME 2.0 #define ELM_PRIV_ENTRY_SIGNALS(cmd) \ cmd(SIG_ABORTED, "aborted", "") \ cmd(SIG_ACTIVATED, "activated", "") \ cmd(SIG_ANCHOR_CLICKED, "anchor,clicked", "") \ cmd(SIG_ANCHOR_DOWN, "anchor,down", "") \ cmd(SIG_ANCHOR_HOVER_OPENED, "anchor,hover,opened", "") \ cmd(SIG_ANCHOR_IN, "anchor,in", "") \ cmd(SIG_ANCHOR_OUT, "anchor,out", "") \ cmd(SIG_ANCHOR_UP, "anchor,up", "") \ cmd(SIG_CHANGED, "changed", "") \ cmd(SIG_CHANGED_USER, "changed,user", "") \ cmd(SIG_CLICKED, "clicked", "") \ cmd(SIG_CLICKED_DOUBLE, "clicked,double", "") \ cmd(SIG_CLICKED_TRIPLE, "clicked,triple", "") \ cmd(SIG_CURSOR_CHANGED, "cursor,changed", "") \ cmd(SIG_CURSOR_CHANGED_MANUAL, "cursor,changed,manual", "") \ cmd(SIG_FOCUSED, "focused", "") \ cmd(SIG_UNFOCUSED, "unfocused", "") \ cmd(SIG_LONGPRESSED, "longpressed", "") \ cmd(SIG_MAX_LENGTH, "maxlength,reached", "") \ cmd(SIG_PREEDIT_CHANGED, "preedit,changed", "") \ cmd(SIG_PRESS, "press", "") \ cmd(SIG_REDO_REQUEST, "redo,request", "") \ cmd(SIG_SELECTION_CHANGED, "selection,changed", "") \ cmd(SIG_SELECTION_CLEARED, "selection,cleared", "") \ cmd(SIG_SELECTION_COPY, "selection,copy", "") \ cmd(SIG_SELECTION_CUT, "selection,cut", "") \ cmd(SIG_SELECTION_PASTE, "selection,paste", "") \ cmd(SIG_SELECTION_START, "selection,start", "") \ cmd(SIG_TEXT_SET_DONE, "text,set,done", "") \ cmd(SIG_THEME_CHANGED, "theme,changed", "") \ cmd(SIG_UNDO_REQUEST, "undo,request", "") \ cmd(SIG_REJECTED, "rejected", "") ELM_PRIV_ENTRY_SIGNALS(ELM_PRIV_STATIC_VARIABLE_DECLARE); #define ENTRY_PASSWORD_MASK_CHARACTER 0x002A static const Evas_Smart_Cb_Description _smart_callbacks[] = { ELM_PRIV_ENTRY_SIGNALS(ELM_PRIV_SMART_CALLBACKS_DESC) {SIG_WIDGET_LANG_CHANGED, ""}, /**< handled by elm_widget */ {SIG_WIDGET_ACCESS_CHANGED, ""}, /**< handled by elm_widget */ {NULL, NULL} }; #undef ELM_PRIV_ENTRY_SIGNALS /* static const Elm_Layout_Part_Alias_Description _content_aliases[] = */ /* { */ /* {"icon", "elm.swallow.icon"}, */ /* {"end", "elm.swallow.end"}, */ /* {NULL, NULL} */ /* }; */ static Eina_List *entries = NULL; struct _Mod_Api { void (*obj_hook)(Evas_Object *obj); void (*obj_unhook)(Evas_Object *obj); void (*obj_longpress)(Evas_Object *obj); }; static const char PART_NAME_SCROLLER[] = "scroller"; static const char PART_NAME_HANDLER_START[] = "handler/start"; static const char PART_NAME_HANDLER_END[] = "handler/end"; static const char PART_NAME_MAGNIFIER[] = "magnifier"; static const char PART_NAME_CURSOR[] = "cursor"; static const char PART_NAME_SELECTION[] = "selection"; static const char PART_NAME_ANCHOR[] = "anchor"; static void _create_selection_handlers(Evas_Object *obj, Efl_Ui_Text_Data *sd); static void _magnifier_move(void *data); static void _update_decorations(Eo *obj); static void _create_text_cursors(Eo *obj, Efl_Ui_Text_Data *sd); static void _efl_ui_text_changed_cb(void *data, const Efl_Event *event); static void _efl_ui_text_changed_user_cb(void *data, const Efl_Event *event); static void _efl_ui_text_selection_changed_cb(void *data, const Efl_Event *event); static void _efl_ui_text_cursor_changed_cb(void *data, const Efl_Event *event); static void _efl_ui_text_move_cb(void *data, Evas *e, Evas_Object *obj, void *event_info); static void _efl_ui_text_select_none(Eo *obj, Efl_Ui_Text_Data *sd); static void _efl_ui_text_anchor_hover_end(Eo *obj, Efl_Ui_Text_Data *sd); static void _efl_ui_text_anchor_hover_parent_set(Eo *obj, Efl_Ui_Text_Data *sd, Evas_Object *parent); static const char* _efl_ui_text_selection_get(Eo *obj, Efl_Ui_Text_Data *sd); static void _edje_signal_emit(Efl_Ui_Text_Data *obj, const char *sig, const char *src); static void _decoration_defer_all(Eo *obj); static inline Eo * _decoration_create(Eo *obj, Efl_Ui_Text_Data *sd, const char *source, Eina_Bool above); static void _decoration_defer(Eo *obj); static void _anchors_clear_all(Evas_Object *o, Efl_Ui_Text_Data *sd); static void _unused_item_objs_free(Efl_Ui_Text_Data *sd); static void _clear_text_selection(Efl_Ui_Text_Data *sd); static Mod_Api * _module_find(Evas_Object *obj EINA_UNUSED) { static Elm_Module *m = NULL; if (m) goto ok; // already found - just use if (!(m = _elm_module_find_as("entry/api"))) return NULL; // get module api m->api = malloc(sizeof(Mod_Api)); if (!m->api) return NULL; ((Mod_Api *)(m->api))->obj_hook = // called on creation _elm_module_symbol_get(m, "obj_hook"); ((Mod_Api *)(m->api))->obj_unhook = // called on deletion _elm_module_symbol_get(m, "obj_unhook"); ((Mod_Api *)(m->api))->obj_longpress = // called on long press menu _elm_module_symbol_get(m, "obj_longpress"); ok: // ok - return api return m->api; } static char * _file_load(const char *file) { Eina_File *f; char *text = NULL; void *tmp = NULL; f = eina_file_open(file, EINA_FALSE); if (!f) return NULL; tmp = eina_file_map_all(f, EINA_FILE_SEQUENTIAL); if (!tmp) goto on_error; text = malloc(eina_file_size_get(f) + 1); if (!text) goto on_error; memcpy(text, tmp, eina_file_size_get(f)); text[eina_file_size_get(f)] = 0; if (eina_file_map_faulted(f, tmp)) { ELM_SAFE_FREE(text, free); } on_error: if (tmp) eina_file_map_free(f, tmp); eina_file_close(f); return text; } static char * _plain_load(const char *file) { return _file_load(file); } static Eina_Bool _load_do(Evas_Object *obj) { char *text; EFL_UI_TEXT_DATA_GET(obj, sd); if (!sd->file) { elm_object_text_set(obj, ""); return EINA_TRUE; } switch (sd->format) { /* Only available format */ case ELM_TEXT_FORMAT_PLAIN_UTF8: text = _plain_load(sd->file); break; default: text = NULL; break; } if (text) { efl_text_set(obj, text); free(text); return EINA_TRUE; } else { efl_text_set(obj, ""); return EINA_FALSE; } } static void _text_save(const char *file, const char *text) { FILE *f; if (!text) { ecore_file_unlink(file); return; } f = fopen(file, "wb"); if (!f) { ERR("Failed to open %s for writing", file); return; } if (fputs(text, f) == EOF) ERR("Failed to write text to file %s", file); fclose(f); } static void _save_do(Evas_Object *obj) { EFL_UI_TEXT_DATA_GET(obj, sd); if (!sd->file) return; switch (sd->format) { /* Only supported format */ case ELM_TEXT_FORMAT_PLAIN_UTF8: _text_save(sd->file, efl_text_get(obj)); break; case ELM_TEXT_FORMAT_MARKUP_UTF8: default: break; } } static Eina_Bool _delay_write(void *data) { EFL_UI_TEXT_DATA_GET(data, sd); _save_do(data); sd->delay_write = NULL; return ECORE_CALLBACK_CANCEL; } static void _efl_ui_text_guide_update(Evas_Object *obj, Eina_Bool has_text) { EFL_UI_TEXT_DATA_GET(obj, sd); if ((has_text) && (!sd->has_text)) edje_object_signal_emit(sd->entry_edje, "elm,guide,disabled", "elm"); else if ((!has_text) && (sd->has_text)) edje_object_signal_emit(sd->entry_edje, "elm,guide,enabled", "elm"); sd->has_text = has_text; } static void _validate(Evas_Object *obj) { EFL_UI_TEXT_DATA_GET(obj, sd); Eina_Bool res; Elm_Validate_Content vc; Eina_Strbuf *buf; if (sd->validators == 0) return; vc.text = edje_object_part_text_get(sd->entry_edje, "elm.text"); res = efl_event_callback_call(obj, EFL_UI_TEXT_EVENT_VALIDATE, (void *)&vc); buf = eina_strbuf_new(); eina_strbuf_append_printf(buf, "validation,%s,%s", vc.signal, res == EINA_FALSE ? "fail" : "pass"); edje_object_signal_emit(sd->scr_edje, eina_strbuf_string_get(buf), "elm"); eina_tmpstr_del(vc.signal); eina_strbuf_free(buf); } static void _filter_free(Elm_Entry_Markup_Filter *tf) { if (tf->func == elm_entry_filter_limit_size) { Elm_Entry_Filter_Limit_Size *lim = tf->data; free(lim); } else if (tf->func == elm_entry_filter_accept_set) { Elm_Entry_Filter_Accept_Set *as = tf->data; if (as) { eina_stringshare_del(as->accepted); eina_stringshare_del(as->rejected); free(as); } } free(tf); } static void _mirrored_set(Evas_Object *obj, Eina_Bool rtl) { EFL_UI_TEXT_DATA_GET(obj, sd); edje_object_mirrored_set(sd->entry_edje, rtl); if (sd->anchor_hover.hover) efl_ui_mirrored_set(sd->anchor_hover.hover, rtl); } static void _hide_selection_handler(Evas_Object *obj) { EFL_UI_TEXT_DATA_GET(obj, sd); if (!sd->start_handler) return; if (sd->start_handler_shown) { edje_object_signal_emit(sd->start_handler, "elm,handler,hide", "elm"); sd->start_handler_shown = EINA_FALSE; } if (sd->end_handler_shown) { edje_object_signal_emit(sd->end_handler, "elm,handler,hide", "elm"); sd->end_handler_shown = EINA_FALSE; } } static Eina_Rectangle * _viewport_region_get(Evas_Object *obj) { EFL_UI_TEXT_DATA_GET(obj, sd); Eina_Rectangle *rect = eina_rectangle_new(0, 0, 0, 0); Evas_Object *parent; if (!rect) return NULL; if (sd->scroll) elm_interface_scrollable_content_viewport_geometry_get (obj, &rect->x, &rect->y, &rect->w, &rect->h); else evas_object_geometry_get(sd->entry_edje, &rect->x, &rect->y, &rect->w, &rect->h); parent = elm_widget_parent_get(obj); while (parent) { if (efl_isa(parent, ELM_INTERFACE_SCROLLABLE_MIXIN)) { Eina_Rectangle r; EINA_RECTANGLE_SET(&r, 0, 0, 0, 0); evas_object_geometry_get(parent, &r.x, &r.y, &r.w, &r.h); if (!eina_rectangle_intersection(rect, &r)) { rect->x = rect->y = rect->w = rect->h = 0; break; } } parent = elm_widget_parent_get(parent); } return rect; } static void _update_selection_handler(Eo *obj) { EFL_UI_TEXT_DATA_GET(obj, sd); Evas_Coord sx, sy, sh; Evas_Coord ex, ey, eh; Evas_Coord ent_x, ent_y; if (!sd->have_selection) { _hide_selection_handler(obj); return; } if (!sd->sel_handler_disabled) { Eina_Rectangle *rect; Evas_Coord hx, hy; Eina_Bool hidden = EINA_FALSE; Efl_Text_Cursor_Cursor *sel_start, *sel_end; efl_ui_text_interactive_selection_cursors_get(obj, &sel_start, &sel_end); if (!sd->start_handler) _create_selection_handlers(obj, sd); rect = _viewport_region_get(obj); evas_object_geometry_get(sd->entry_edje, &ent_x, &ent_y, NULL, NULL); efl_text_cursor_geometry_get(obj, sel_start, EFL_TEXT_CURSOR_TYPE_BEFORE, &sx, &sy, NULL, &sh, NULL, NULL, NULL, NULL); hx = ent_x + sx; hy = ent_y + sy + sh; evas_object_move(sd->start_handler, hx, hy); if (!eina_rectangle_xcoord_inside(rect, hx) || !eina_rectangle_ycoord_inside(rect, hy)) { hidden = EINA_TRUE; } if (!sd->start_handler_shown && !hidden) { edje_object_signal_emit(sd->start_handler, "elm,handler,show", "elm"); sd->start_handler_shown = EINA_TRUE; } else if (sd->start_handler_shown && hidden) { edje_object_signal_emit(sd->start_handler, "elm,handler,hide", "elm"); sd->start_handler_shown = EINA_FALSE; } efl_text_cursor_geometry_get(obj, sel_end, EFL_TEXT_CURSOR_TYPE_BEFORE, &ex, &ey, NULL, &eh, NULL, NULL, NULL, NULL); hx = ent_x + ex; hy = ent_y + ey + eh; evas_object_move(sd->end_handler, hx, hy); if (!eina_rectangle_xcoord_inside(rect, hx) || !eina_rectangle_ycoord_inside(rect, hy)) { hidden = EINA_TRUE; } if (!sd->end_handler_shown && !hidden) { edje_object_signal_emit(sd->end_handler, "elm,handler,show", "elm"); sd->end_handler_shown = EINA_TRUE; } else if (sd->end_handler_shown && hidden) { edje_object_signal_emit(sd->end_handler, "elm,handler,hide", "elm"); sd->end_handler_shown = EINA_FALSE; } eina_rectangle_free(rect); } else { if (sd->start_handler_shown) { edje_object_signal_emit(sd->start_handler, "elm,handler,hide", "elm"); sd->start_handler_shown = EINA_FALSE; } if (sd->end_handler_shown) { edje_object_signal_emit(sd->end_handler, "elm,handler,hide", "elm"); sd->end_handler_shown = EINA_FALSE; } } } static void _edje_entry_user_insert(Evas_Object *obj, const char *data) { if (!data) return; EFL_UI_TEXT_DATA_GET(obj, sd); sd->changed = EINA_TRUE; edje_object_part_text_user_insert(sd->entry_edje, "elm.text", data); elm_layout_sizing_eval(obj); } static Eina_Bool _selection_data_cb(void *data EINA_UNUSED, Evas_Object *obj, Elm_Selection_Data *sel_data) { char *buf; if (!sel_data->data) return EINA_FALSE; EFL_UI_TEXT_DATA_GET(obj, sd); buf = malloc(sel_data->len + 1); if (!buf) { ERR("Failed to allocate memory, obj: %p", obj); return EINA_FALSE; } memcpy(buf, sel_data->data, sel_data->len); buf[sel_data->len] = '\0'; if ((sel_data->format & ELM_SEL_FORMAT_IMAGE) && (sd->cnp_mode != ELM_CNP_MODE_NO_IMAGE)) { char *entry_tag; int len; static const char *tag_string = ""; len = strlen(tag_string) + strlen(buf); entry_tag = alloca(len + 1); snprintf(entry_tag, len + 1, tag_string, buf); _edje_entry_user_insert(obj, entry_tag); } else if (sd->cnp_mode == ELM_CNP_MODE_PLAINTEXT) { Efl_Text_Cursor_Cursor *cur, *start, *end; efl_ui_text_interactive_selection_cursors_get(obj, &start, &end); if (!efl_text_cursor_equal(obj, start, end)) { efl_canvas_text_range_delete(obj, start, end); } cur = efl_text_cursor_get(obj, EFL_TEXT_CURSOR_GET_MAIN); efl_text_cursor_text_insert(obj, cur, buf); } else { _edje_entry_user_insert(obj, buf); } free(buf); return EINA_TRUE; } static void _dnd_enter_cb(void *data EINA_UNUSED, Evas_Object *obj) { elm_object_focus_set(obj, EINA_TRUE); } static void _dnd_leave_cb(void *data EINA_UNUSED, Evas_Object *obj) { if (_elm_config->desktop_entry) elm_object_focus_set(obj, EINA_FALSE); } static void _dnd_pos_cb(void *data EINA_UNUSED, Evas_Object *obj, Evas_Coord x, Evas_Coord y, Elm_Xdnd_Action action EINA_UNUSED) { int pos; Evas_Coord ox, oy, ex, ey; EFL_UI_TEXT_DATA_GET(obj, sd); evas_object_geometry_get(obj, &ox, &oy, NULL, NULL); evas_object_geometry_get(sd->entry_edje, &ex, &ey, NULL, NULL); x = x + ox - ex; y = y + oy - ey; edje_object_part_text_cursor_coord_set (sd->entry_edje, "elm.text", EDJE_CURSOR_USER, x, y); pos = edje_object_part_text_cursor_pos_get (sd->entry_edje, "elm.text", EDJE_CURSOR_USER); edje_object_part_text_cursor_pos_set(sd->entry_edje, "elm.text", EDJE_CURSOR_MAIN, pos); } static Eina_Bool _dnd_drop_cb(void *data EINA_UNUSED, Evas_Object *obj, Elm_Selection_Data *drop) { Eina_Bool rv; EFL_UI_TEXT_DATA_GET(obj, sd); rv = edje_object_part_text_cursor_coord_set (sd->entry_edje, "elm.text", EDJE_CURSOR_MAIN, drop->x, drop->y); if (!rv) WRN("Warning: Failed to position cursor: paste anyway"); rv = _selection_data_cb(NULL, obj, drop); return rv; } static Elm_Sel_Format _get_drop_format(Evas_Object *obj) { EFL_UI_TEXT_DATA_GET(obj, sd); if ((sd->editable) && (!sd->single_line) && (!sd->password) && (!sd->disabled)) return ELM_SEL_FORMAT_MARKUP | ELM_SEL_FORMAT_IMAGE; return ELM_SEL_FORMAT_MARKUP; } /* we can't reuse layout's here, because it's on entry_edje only */ EOLIAN static Eina_Bool _efl_ui_text_efl_ui_widget_on_disabled_update(Eo *obj, Efl_Ui_Text_Data *sd, Eina_Bool disabled) { const char *emission ; elm_drop_target_del(obj, sd->drop_format, _dnd_enter_cb, NULL, _dnd_leave_cb, NULL, _dnd_pos_cb, NULL, _dnd_drop_cb, NULL); emission = disabled ? "elm,state,disabled" : "elm,state,enabled"; edje_object_signal_emit(sd->entry_edje, emission, "elm"); if (sd->scroll) { edje_object_signal_emit(sd->scr_edje, emission, "elm"); elm_interface_scrollable_freeze_set(obj, disabled); } sd->disabled = disabled; if (!disabled) { sd->drop_format = _get_drop_format(obj); elm_drop_target_add(obj, sd->drop_format, _dnd_enter_cb, NULL, _dnd_leave_cb, NULL, _dnd_pos_cb, NULL, _dnd_drop_cb, NULL); } return EINA_TRUE; } /* It gets the background object from from_edje object and * sets the background object to to_edje object. * The background object has to be moved to proper Edje object * when scrollable status is changed. */ static void _efl_ui_text_background_switch(Evas_Object *from_edje, Evas_Object *to_edje) { Evas_Object *bg_obj; if (!from_edje || !to_edje) return; if (edje_object_part_exists(from_edje, "elm.swallow.background") && edje_object_part_exists(to_edje, "elm.swallow.background") && !edje_object_part_swallow_get(to_edje, "elm.swallow.background")) { bg_obj = edje_object_part_swallow_get(from_edje, "elm.swallow.background"); if (bg_obj) { edje_object_part_unswallow(from_edje, bg_obj); edje_object_part_swallow(to_edje, "elm.swallow.background", bg_obj); } } } /* we can't issue the layout's theming code here, cause it assumes an * unique edje object, always */ EOLIAN static Efl_Ui_Theme_Apply _efl_ui_text_efl_ui_widget_theme_apply(Eo *obj, Efl_Ui_Text_Data *sd) { const char *str; Efl_Ui_Theme_Apply theme_apply; ELM_WIDGET_DATA_GET_OR_RETURN(obj, wd, EINA_FALSE); // Note: We are skipping elm_layout here! This is by design. // This assumes the following inheritance: my_class -> layout -> widget ... theme_apply = efl_ui_widget_theme_apply(efl_cast(obj, EFL_UI_WIDGET_CLASS)); if (!theme_apply) return EFL_UI_THEME_APPLY_FAILED; efl_event_freeze(obj); edje_object_mirrored_set (wd->resize_obj, efl_ui_mirrored_get(obj)); edje_object_scale_set (wd->resize_obj, efl_gfx_scale_get(obj) * elm_config_scale_get()); _mirrored_set(obj, efl_ui_mirrored_get(obj)); elm_widget_element_update(obj, sd->entry_edje, elm_widget_theme_element_get(obj)); if (elm_widget_disabled_get(obj)) edje_object_signal_emit(sd->entry_edje, "elm,state,disabled", "elm"); edje_object_part_text_input_panel_layout_set (sd->entry_edje, "elm.text", (Edje_Input_Panel_Layout)sd->input_panel_layout); edje_object_part_text_input_panel_layout_variation_set (sd->entry_edje, "elm.text", sd->input_panel_layout_variation); edje_object_part_text_autocapital_type_set (sd->entry_edje, "elm.text", (Edje_Text_Autocapital_Type)sd->autocapital_type); edje_object_part_text_prediction_allow_set (sd->entry_edje, "elm.text", sd->prediction_allow); edje_object_part_text_input_hint_set (sd->entry_edje, "elm.text", (Edje_Input_Hints)sd->input_hints); edje_object_part_text_input_panel_enabled_set (sd->entry_edje, "elm.text", sd->input_panel_enable); edje_object_part_text_input_panel_imdata_set (sd->entry_edje, "elm.text", sd->input_panel_imdata, sd->input_panel_imdata_len); edje_object_part_text_input_panel_return_key_type_set (sd->entry_edje, "elm.text", (Edje_Input_Panel_Return_Key_Type)sd->input_panel_return_key_type); edje_object_part_text_input_panel_return_key_disabled_set (sd->entry_edje, "elm.text", sd->input_panel_return_key_disabled); edje_object_part_text_input_panel_show_on_demand_set (sd->entry_edje, "elm.text", sd->input_panel_show_on_demand); // elm_entry_cursor_pos_set -> cursor,changed -> widget_show_region_set // -> smart_objects_calculate will call all smart calculate functions, // and one of them can delete elm_entry. evas_object_ref(obj); if (efl_ui_focus_object_focus_get(obj)) { edje_object_signal_emit(sd->entry_edje, "elm,action,focus", "elm"); if (sd->scroll) edje_object_signal_emit(sd->scr_edje, "elm,action,focus", "elm"); } edje_object_message_signal_process(sd->entry_edje); Evas_Object* clip = evas_object_clip_get(sd->entry_edje); evas_object_clip_set(sd->hit_rect, clip); if (sd->scroll) { elm_interface_scrollable_mirrored_set(obj, efl_ui_mirrored_get(obj)); if (sd->single_line) elm_widget_element_update(obj, sd->scr_edje, PART_NAME_SCROLLER); _efl_ui_text_background_switch(sd->entry_edje, sd->scr_edje); str = edje_object_data_get(sd->scr_edje, "focus_highlight"); } else { _efl_ui_text_background_switch(sd->scr_edje, sd->entry_edje); str = edje_object_data_get(sd->entry_edje, "focus_highlight"); } if ((str) && (!strcmp(str, "on"))) elm_widget_highlight_in_theme_set(obj, EINA_TRUE); else elm_widget_highlight_in_theme_set(obj, EINA_FALSE); if (sd->start_handler) { elm_widget_element_update(obj, sd->start_handler, PART_NAME_HANDLER_START); elm_widget_element_update(obj, sd->end_handler, PART_NAME_HANDLER_END); } sd->changed = EINA_TRUE; sd->has_text = !sd->has_text; _efl_ui_text_guide_update(obj, !sd->has_text); efl_event_thaw(obj); efl_event_callback_call(obj, EFL_UI_LAYOUT_EVENT_THEME_CHANGED, NULL); evas_object_unref(obj); return theme_apply; } static void _cursor_geometry_recalc(Evas_Object *obj) { EFL_UI_TEXT_DATA_GET(obj, sd); Evas_Coord x, y, w, h; Evas_Coord x2, y2, w2, h2; Evas_Coord cx, cy, cw, ch; if (!sd->editable) return; cx = cy = cw = ch = 0; x2 = y2 = w2 = h2 = 0; x = y = w = h = 0; Efl_Text_Cursor_Cursor *main_cur = efl_text_cursor_get(obj, EFL_TEXT_CURSOR_GET_MAIN); efl_text_cursor_geometry_get(obj, main_cur, EFL_TEXT_CURSOR_TYPE_BEFORE, &cx, &cy, &cw, &ch, NULL, NULL, NULL, NULL); edje_object_size_min_restricted_calc(sd->cursor, &cw, NULL, cw, 0); if (cw < 1) cw = 1; if (ch < 1) ch = 1; edje_object_size_min_restricted_calc(sd->cursor, &cw, NULL, cw, 0); evas_object_geometry_get(sd->entry_edje, &x, &y, &w, &h); evas_object_geometry_get( edje_object_part_swallow_get(sd->entry_edje, "elm.text"), &x2, &y2, &w2, &h2); cx = cx + x - x2; cy = cy + y - y2; elm_widget_show_region_set(obj, EINA_RECT(cx, cy, cw, ch), EINA_FALSE); } static void _layout_text_sizing_eval(Eo *obj, Evas_Coord tw, Evas_Coord th) { Eo *sw; Evas_Coord minw, minh; efl_event_freeze(obj); EFL_UI_TEXT_DATA_GET(obj, sd); sw = edje_object_part_swallow_get(sd->entry_edje, "elm.text"); if (sd->scroll) { Evas_Coord vw = 0, vh = 0; elm_interface_scrollable_content_viewport_geometry_get(obj, NULL, NULL, &vw, &vh); evas_object_size_hint_min_set(sw, tw, th); edje_object_size_min_calc(sd->entry_edje, &minw, &minh); evas_object_size_hint_min_set(sw, -1, -1); if (vw > minw) minw = vw; efl_gfx_size_set(sd->entry_edje, EINA_SIZE2D(minw, minh)); if (!efl_text_multiline_get(sw)) { evas_object_size_hint_min_set(obj, -1, minh); } } else { Eina_Bool wrap; wrap = efl_text_wrap_get(sw); evas_object_size_hint_min_set(sw, tw, th); edje_object_size_min_calc(sd->entry_edje, &minw, &minh); evas_object_size_hint_min_set(sw, -1, -1); if (wrap == EFL_TEXT_FORMAT_WRAP_NONE) { _decoration_defer_all(obj); efl_event_thaw(obj); evas_object_size_hint_min_set(obj, minw, minh); return; } } _decoration_defer_all(obj); efl_event_thaw(obj); } typedef struct _Layout_Ctx Layout_Ctx; struct _Layout_Ctx { Eo *obj; }; static Eina_Value _on_layout_complete(void *data, const Eina_Value v) { Layout_Ctx *c = data; Eina_Rectangle r; if (eina_value_get(&v, &r)) { _layout_text_sizing_eval(c->obj, r.w, r.h); } free(c); return v; } EOLIAN static void _efl_ui_text_elm_layout_sizing_eval(Eo *obj, Efl_Ui_Text_Data *sd) { Evas_Coord resw, resh; Eo *sw; Eina_Bool can_async; evas_object_geometry_get(obj, NULL, NULL, &resw, &resh); sw = edje_object_part_swallow_get(sd->entry_edje, "elm.text"); if (!sw) return; if (!sd->changed && (resw == sd->ent_w) && (resh == sd->ent_h)) return; sd->changed = EINA_FALSE; sd->ent_w = resw; sd->ent_h = resh; can_async = !sd->editable && sd->async.enabled; efl_event_freeze(obj); if (sd->scroll) { Evas_Coord vw = 0, vh = 0; elm_interface_scrollable_content_viewport_geometry_get(obj, NULL, NULL, &vw, &vh); efl_gfx_size_set(sd->entry_edje, EINA_SIZE2D(vw, vh)); } if (can_async) { Eina_Future *f; Layout_Ctx *c = calloc(1, sizeof(*c)); c->obj = obj; efl_event_thaw(obj); f = efl_canvas_text_async_layout(sw); eina_future_then_easy(f, _on_layout_complete, NULL, NULL, NULL, c); } else { /* Don't defer - complete the sizing evaluation now */ Evas_Coord fw, fh; efl_canvas_text_size_formatted_get(obj, &fw, &fh); efl_event_thaw(obj); _layout_text_sizing_eval(obj, fw, fh); sd->deferred_decoration_cursor = EINA_TRUE; _decoration_defer(obj); } } static void _return_key_enabled_check(Evas_Object *obj) { Eina_Bool return_key_disabled = EINA_FALSE; EFL_UI_TEXT_DATA_GET(obj, sd); if (!sd->auto_return_key) return; if (efl_canvas_text_is_empty_get(obj) == EINA_TRUE) return_key_disabled = EINA_TRUE; efl_ui_text_input_panel_return_key_disabled_set(obj, return_key_disabled); } EOLIAN static Eina_Bool -_efl_ui_text_efl_ui_widget_on_focus_update(Eo *obj, Efl_Ui_Text_Data *sd) +_efl_ui_text_efl_ui_focus_object_on_focus_update(Eo *obj, Efl_Ui_Text_Data *sd) { Evas_Object *top; Eina_Bool top_is_win = EINA_FALSE; if (!sd->editable) return EINA_FALSE; top = elm_widget_top_get(obj); if (top && efl_isa(top, EFL_UI_WIN_CLASS)) top_is_win = EINA_TRUE; if (efl_ui_focus_object_focus_get(obj)) { Eo *sw = edje_object_part_swallow_get(sd->entry_edje, "elm.text"); evas_object_focus_set(sw, EINA_TRUE); _edje_signal_emit(sd, "elm,action,focus", "elm"); if (sd->scroll) edje_object_signal_emit(sd->scr_edje, "elm,action,focus", "elm"); if (top && top_is_win && sd->input_panel_enable && !sd->input_panel_show_on_demand) elm_win_keyboard_mode_set(top, ELM_WIN_KEYBOARD_ON); if (_elm_config->atspi_mode) efl_access_state_changed_signal_emit(obj, EFL_ACCESS_STATE_FOCUSED, EINA_TRUE); _return_key_enabled_check(obj); _validate(obj); } else { Eo *sw = edje_object_part_swallow_get(sd->entry_edje, "elm.text"); _edje_signal_emit(sd, "elm,action,unfocus", "elm"); if (sd->scroll) edje_object_signal_emit(sd->scr_edje, "elm,action,unfocus", "elm"); evas_object_focus_set(sw, EINA_FALSE); if (top && top_is_win && sd->input_panel_enable) elm_win_keyboard_mode_set(top, ELM_WIN_KEYBOARD_OFF); if (_elm_config->atspi_mode) efl_access_state_changed_signal_emit(obj, EFL_ACCESS_STATE_FOCUSED, EINA_FALSE); if (_elm_config->selection_clear_enable) { if ((sd->have_selection) && (!sd->hoversel)) { sd->sel_mode = EINA_FALSE; elm_widget_scroll_hold_pop(obj); edje_object_part_text_select_allow_set(sd->entry_edje, "elm.text", EINA_FALSE); edje_object_signal_emit(sd->entry_edje, "elm,state,select,off", "elm"); edje_object_part_text_select_none(sd->entry_edje, "elm.text"); } } edje_object_signal_emit(sd->scr_edje, "validation,default", "elm"); } return EINA_TRUE; } EOLIAN static Eina_Rect _efl_ui_text_efl_ui_widget_interest_region_get(Eo *obj EINA_UNUSED, Efl_Ui_Text_Data *sd) { Evas_Coord edje_x, edje_y, elm_x, elm_y; Eina_Rect r = {}; efl_text_cursor_geometry_get(obj, efl_text_cursor_get(obj, EFL_TEXT_CURSOR_GET_MAIN), EFL_TEXT_CURSOR_TYPE_BEFORE, &r.x, &r.y, &r.w, &r.h, NULL, NULL, NULL, NULL); if (sd->single_line) { evas_object_geometry_get(sd->entry_edje, NULL, NULL, NULL, &r.h); r.y = 0; } evas_object_geometry_get(sd->entry_edje, &edje_x, &edje_y, NULL, NULL); evas_object_geometry_get(obj, &elm_x, &elm_y, NULL, NULL); r.x += edje_x - elm_x; r.y += edje_y - elm_y; if (r.w < 1) r.w = 1; if (r.h < 1) r.h = 1; return r; } static void _show_region_hook(void *data EINA_UNUSED, Evas_Object *obj, Eina_Rect r) { elm_interface_scrollable_content_region_show(obj, r.x, r.y, r.w, r.h); } EOLIAN static Eina_Bool _efl_ui_text_efl_ui_widget_widget_sub_object_del(Eo *obj, Efl_Ui_Text_Data *_pd EINA_UNUSED, Evas_Object *sobj) { Eina_Bool ret = EINA_FALSE; /* unfortunately entry doesn't follow the signal pattern * elm,state,icon,{visible,hidden}, so we have to replicate this * smart function */ if (sobj == elm_layout_content_get(obj, "elm.swallow.icon")) { elm_layout_signal_emit(obj, "elm,action,hide,icon", "elm"); } else if (sobj == elm_layout_content_get(obj, "elm.swallow.end")) { elm_layout_signal_emit(obj, "elm,action,hide,end", "elm"); } ret = elm_widget_sub_object_del(efl_super(obj, MY_CLASS), sobj); if (!ret) return EINA_FALSE; return EINA_TRUE; } static void _hoversel_position(Evas_Object *obj) { Evas_Coord cx, cy, cw, ch, x, y, mw, mh, w, h; EFL_UI_TEXT_DATA_GET(obj, sd); cx = cy = 0; cw = ch = 1; evas_object_geometry_get(sd->entry_edje, &x, &y, &w, &h); if (sd->use_down) { cx = sd->downx - x; cy = sd->downy - y; cw = 1; ch = 1; } else edje_object_part_text_cursor_geometry_get (sd->entry_edje, "elm.text", &cx, &cy, &cw, &ch); evas_object_size_hint_min_get(sd->hoversel, &mw, &mh); if (cx + mw > w) cx = w - mw; if (cy + mh > h) cy = h - mh; evas_object_move(sd->hoversel, x + cx, y + cy); evas_object_resize(sd->hoversel, mw, mh); } static void _hover_del_job(void *data) { EFL_UI_TEXT_DATA_GET(data, sd); ELM_SAFE_FREE(sd->hoversel, evas_object_del); sd->hov_deljob = NULL; } static void _hover_dismissed_cb(void *data, const Efl_Event *event EINA_UNUSED) { EFL_UI_TEXT_DATA_GET(data, sd); sd->use_down = 0; if (sd->hoversel) evas_object_hide(sd->hoversel); if (sd->sel_mode) { if (!_elm_config->desktop_entry) { if (!sd->password) edje_object_part_text_select_allow_set (sd->entry_edje, "elm.text", EINA_TRUE); } } elm_widget_scroll_freeze_pop(data); ecore_job_del(sd->hov_deljob); sd->hov_deljob = ecore_job_add(_hover_del_job, data); } static void _hover_selected_cb(void *data, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED) { EFL_UI_TEXT_DATA_GET(data, sd); if (!sd->sel_allow) return; sd->sel_mode = EINA_TRUE; edje_object_part_text_select_none(sd->entry_edje, "elm.text"); if (!_elm_config->desktop_entry) { if (!sd->password) edje_object_part_text_select_allow_set (sd->entry_edje, "elm.text", EINA_TRUE); } edje_object_signal_emit(sd->entry_edje, "elm,state,select,on", "elm"); if (!_elm_config->desktop_entry) elm_widget_scroll_hold_push(data); } static char * _item_tags_remove(const char *str) { char *ret; Eina_Strbuf *buf; if (!str) return NULL; buf = eina_strbuf_new(); if (!buf) return NULL; if (!eina_strbuf_append(buf, str)) { eina_strbuf_free(buf); return NULL; } while (EINA_TRUE) { const char *temp = eina_strbuf_string_get(buf); char *start_tag = NULL; char *end_tag = NULL; size_t sindex; size_t eindex; start_tag = strstr(temp, ""); else break; if (!end_tag || start_tag > end_tag) break; sindex = start_tag - temp; eindex = end_tag - temp + 1; if (!eina_strbuf_remove(buf, sindex, eindex)) break; } ret = eina_strbuf_string_steal(buf); eina_strbuf_free(buf); return ret; } void _efl_ui_text_entry_paste(Evas_Object *obj, const char *entry) { char *str = NULL; if (!entry) return; EFL_UI_TEXT_CHECK(obj); EFL_UI_TEXT_DATA_GET(obj, sd); if (sd->cnp_mode == ELM_CNP_MODE_NO_IMAGE) { str = _item_tags_remove(entry); if (!str) str = strdup(entry); } else str = strdup(entry); if (!str) str = (char *)entry; _edje_entry_user_insert(obj, str); if (str != entry) free(str); } static void _paste_cb(Eo *obj) { Elm_Sel_Format formats = ELM_SEL_FORMAT_MARKUP; EFL_UI_TEXT_DATA_GET(obj, sd); efl_event_callback_call(obj, EFL_UI_EVENT_SELECTION_PASTE, NULL); sd->selection_asked = EINA_TRUE; if (sd->cnp_mode == ELM_CNP_MODE_PLAINTEXT) formats = ELM_SEL_FORMAT_TEXT; else if (sd->cnp_mode != ELM_CNP_MODE_NO_IMAGE) formats |= ELM_SEL_FORMAT_IMAGE; elm_cnp_selection_get (obj, ELM_SEL_TYPE_CLIPBOARD, formats, _selection_data_cb, NULL); } static void _hoversel_item_paste_cb(void *data, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED) { _paste_cb(data); } static void _selection_clear(void *data, Elm_Sel_Type selection) { EFL_UI_TEXT_DATA_GET(data, sd); if (!sd->have_selection) return; if ((selection == ELM_SEL_TYPE_CLIPBOARD) || (selection == ELM_SEL_TYPE_PRIMARY)) { _efl_ui_text_select_none(data, sd); } } static void _selection_store(Elm_Sel_Type seltype, Evas_Object *obj) { char *sel; Efl_Text_Cursor_Cursor *start, *end; Elm_Sel_Format format = ELM_SEL_FORMAT_TEXT; EFL_UI_TEXT_DATA_GET(obj, sd); efl_ui_text_interactive_selection_cursors_get(obj, &start, &end); sel = efl_canvas_text_range_text_get(obj, start, end); if ((!sel) || (!sel[0])) return; /* avoid deleting our own selection */ elm_cnp_selection_set (obj, seltype, format, sel, strlen(sel)); elm_cnp_selection_loss_callback_set(obj, seltype, _selection_clear, obj); if (seltype == ELM_SEL_TYPE_CLIPBOARD) eina_stringshare_replace(&sd->cut_sel, sel); free(sel); } static void _cut_cb(Eo *obj) { Efl_Text_Cursor_Cursor *start, *end; EFL_UI_TEXT_DATA_GET(obj, sd); efl_event_callback_call(obj, EFL_UI_EVENT_SELECTION_CUT, NULL); /* Store it */ sd->sel_mode = EINA_FALSE; if (!_elm_config->desktop_entry) edje_object_part_text_select_allow_set (sd->entry_edje, "elm.text", EINA_FALSE); edje_object_signal_emit(sd->entry_edje, "elm,state,select,off", "elm"); if (!_elm_config->desktop_entry) elm_widget_scroll_hold_pop(obj); _selection_store(ELM_SEL_TYPE_CLIPBOARD, obj); efl_ui_text_interactive_selection_cursors_get(obj, &start, &end); efl_canvas_text_range_delete(obj, start, end); } static void _hoversel_item_cut_cb(void *data, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED) { _cut_cb(data); } static void _copy_cb(Eo *obj) { EFL_UI_TEXT_DATA_GET(obj, sd); efl_event_callback_call(obj, EFL_UI_EVENT_SELECTION_COPY, NULL); sd->sel_mode = EINA_FALSE; if (!_elm_config->desktop_entry) { edje_object_part_text_select_allow_set (sd->entry_edje, "elm.text", EINA_FALSE); edje_object_signal_emit(sd->entry_edje, "elm,state,select,off", "elm"); elm_widget_scroll_hold_pop(obj); } _selection_store(ELM_SEL_TYPE_CLIPBOARD, obj); } static void _hoversel_item_copy_cb(void *data, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED) { _copy_cb(data); } static void _hover_cancel_cb(void *data, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED) { EFL_UI_TEXT_DATA_GET(data, sd); sd->sel_mode = EINA_FALSE; if (!_elm_config->desktop_entry) edje_object_part_text_select_allow_set (sd->entry_edje, "elm.text", EINA_FALSE); edje_object_signal_emit(sd->entry_edje, "elm,state,select,off", "elm"); if (!_elm_config->desktop_entry) elm_widget_scroll_hold_pop(data); edje_object_part_text_select_none(sd->entry_edje, "elm.text"); } static void _hover_item_clicked_cb(void *data, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED) { Elm_Entry_Context_Menu_Item *it = data; if (!it) return; if (it->func) it->func(it->data, it->obj, it); } static void _menu_call(Evas_Object *obj) { Evas_Object *top; const Eina_List *l; const Elm_Entry_Context_Menu_Item *it; EFL_UI_TEXT_DATA_GET(obj, sd); if (sd->anchor_hover.hover) return; efl_event_callback_call(obj, EFL_UI_TEXT_EVENT_CONTEXT_OPEN, NULL); if ((sd->api) && (sd->api->obj_longpress)) { sd->api->obj_longpress(obj); } else if (sd->context_menu) { const char *context_menu_orientation; Eina_Bool ownersel; ownersel = elm_selection_selection_has_owner(obj); if (!sd->items) { /* prevent stupid blank hoversel */ if (sd->have_selection && sd->password) return; if (_elm_config->desktop_entry && (!sd->have_selection) && ((!sd->editable) || (!ownersel))) return; } if (sd->hoversel) evas_object_del(sd->hoversel); else elm_widget_scroll_freeze_push(obj); sd->hoversel = elm_hoversel_add(obj); context_menu_orientation = edje_object_data_get (sd->entry_edje, "context_menu_orientation"); if ((context_menu_orientation) && (!strcmp(context_menu_orientation, "horizontal"))) elm_hoversel_horizontal_set(sd->hoversel, EINA_TRUE); elm_object_style_set(sd->hoversel, "entry"); elm_widget_sub_object_add(obj, sd->hoversel); elm_object_text_set(sd->hoversel, "Text"); top = elm_widget_top_get(obj); if (top) elm_hoversel_hover_parent_set(sd->hoversel, top); efl_event_callback_add (sd->hoversel, ELM_HOVERSEL_EVENT_DISMISSED, _hover_dismissed_cb, obj); if (sd->have_selection) { if (!sd->password) { if (sd->editable) elm_hoversel_item_add (sd->hoversel, E_("Cut"), NULL, ELM_ICON_NONE, _hoversel_item_cut_cb, obj); elm_hoversel_item_add (sd->hoversel, E_("Copy"), NULL, ELM_ICON_NONE, _hoversel_item_copy_cb, obj); if (sd->editable && ownersel) elm_hoversel_item_add (sd->hoversel, E_("Paste"), NULL, ELM_ICON_NONE, _hoversel_item_paste_cb, obj); elm_hoversel_item_add (sd->hoversel, E_("Cancel"), NULL, ELM_ICON_NONE, _hover_cancel_cb, obj); } } else { if (!sd->sel_mode) { if (sd->sel_allow && !_elm_config->desktop_entry) { if (!sd->password) elm_hoversel_item_add (sd->hoversel, E_("Select"), NULL, ELM_ICON_NONE, _hover_selected_cb, obj); } if (ownersel) { if (sd->editable) elm_hoversel_item_add (sd->hoversel, E_("Paste"), NULL, ELM_ICON_NONE, _hoversel_item_paste_cb, obj); } } else elm_hoversel_item_add (sd->hoversel, E_("Cancel"), NULL, ELM_ICON_NONE, _hover_cancel_cb, obj); } EINA_LIST_FOREACH(sd->items, l, it) { elm_hoversel_item_add(sd->hoversel, it->label, it->icon_file, it->icon_type, _hover_item_clicked_cb, it); } if (sd->hoversel) { _hoversel_position(obj); evas_object_show(sd->hoversel); elm_hoversel_hover_begin(sd->hoversel); } if (!_elm_config->desktop_entry) { edje_object_part_text_select_allow_set (sd->entry_edje, "elm.text", EINA_FALSE); edje_object_part_text_select_abort(sd->entry_edje, "elm.text"); } } } static void _magnifier_proxy_update(void *data, Evas *e EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED) { _magnifier_move(data); } static void _magnifier_create(void *data) { EFL_UI_TEXT_DATA_GET(data, sd); double scale = _elm_config->magnifier_scale; Evas *e; Evas_Coord w, h, mw, mh; evas_object_del(sd->mgf_proxy); evas_object_del(sd->mgf_bg); evas_object_del(sd->mgf_clip); e = evas_object_evas_get(data); //Bg sd->mgf_bg = edje_object_add(e); elm_widget_element_update(data, sd->mgf_bg, PART_NAME_MAGNIFIER); evas_object_show(sd->mgf_bg); //Proxy sd->mgf_proxy = evas_object_image_add(e); evas_object_event_callback_add(sd->mgf_proxy, EVAS_CALLBACK_RESIZE, _magnifier_proxy_update, data); evas_object_event_callback_add(sd->mgf_proxy, EVAS_CALLBACK_MOVE, _magnifier_proxy_update, data); edje_object_part_swallow(sd->mgf_bg, "elm.swallow.content", sd->mgf_proxy); evas_object_image_source_set(sd->mgf_proxy, data); evas_object_geometry_get(data, NULL, NULL, &w, &h); //Clipper sd->mgf_clip = evas_object_rectangle_add(e); evas_object_color_set(sd->mgf_clip, 0, 0, 0, 0); evas_object_show(sd->mgf_clip); evas_object_clip_set(sd->mgf_proxy, sd->mgf_clip); mw = (Evas_Coord)(scale * (float) w); mh = (Evas_Coord)(scale * (float) h); if ((mw <= 0) || (mh <= 0)) return; evas_object_layer_set(sd->mgf_bg, EVAS_LAYER_MAX); evas_object_layer_set(sd->mgf_proxy, EVAS_LAYER_MAX); } static void _magnifier_move(void *data) { EFL_UI_TEXT_DATA_GET(data, sd); Evas_Coord x, y, w, h; Evas_Coord px, py, pw, ph; Evas_Coord cx, cy, ch; Evas_Coord ex, ey; Evas_Coord mx, my, mw, mh; Evas_Coord diffx = 0; Evas_Object *top; double fx, fy, fw, fh; double scale = _elm_config->magnifier_scale; edje_object_part_text_cursor_geometry_get(sd->entry_edje, "elm.text", &cx, &cy, NULL, &ch); if (sd->scroll) { Evas_Coord ox, oy; evas_object_geometry_get(sd->scr_edje, &ex, &ey, NULL, NULL); elm_interface_scrollable_content_pos_get(data, &ox, &oy); ex -= ox; ey -= oy; } else { evas_object_geometry_get(data, &ex, &ey, NULL, NULL); } cx += ex; cy += ey; //Move the Magnifier edje_object_parts_extends_calc(sd->mgf_bg, &x, &y, &w, &h); evas_object_move(sd->mgf_bg, cx - x - (w / 2), cy - y - h); mx = cx - x - (w / 2); my = cy - y - h; mw = w; mh = h; // keep magnifier inside window top = elm_widget_top_get(data); if (top && efl_isa(top, EFL_UI_WIN_CLASS)) { Evas_Coord wh, ww; evas_object_geometry_get(top, NULL, NULL, &ww, &wh); if (mx < 0) { diffx = mx; mx = 0; } if (mx + mw > ww) { diffx = - (ww - (mx + mw)); mx = ww - mw; } if (my < 0) my = 0; if (my + mh > wh) my = wh - mh; evas_object_move(sd->mgf_bg, mx, my); } //Set the Proxy Render Area evas_object_geometry_get(data, &x, &y, &w, &h); evas_object_geometry_get(sd->mgf_proxy, &px, &py, &pw, &ph); fx = -((cx - x) * scale) + (pw * 0.5) + diffx; fy = -((cy - y) * scale) + (ph * 0.5) - (ch * 0.5 * scale); fw = w * scale; fh = h * scale; evas_object_image_fill_set(sd->mgf_proxy, fx, fy, fw, fh); //Update Clipper Area int tx = fx; int ty = fy; int tw = fw; int th = fh; if (tx > 0) px += tx; if (ty > 0) py += ty; if (-(tx - pw) > tw) pw -= (-((tx - pw) + tw)); if (-(ty - ph) > th) ph -= (-((ty - ph) + th)); evas_object_move(sd->mgf_clip, px, py); evas_object_resize(sd->mgf_clip, pw, ph); } static void _magnifier_hide(void *data) { EFL_UI_TEXT_DATA_GET(data, sd); edje_object_signal_emit(sd->mgf_bg, "elm,action,hide,magnifier", "elm"); elm_widget_scroll_freeze_pop(data); evas_object_hide(sd->mgf_clip); } static void _magnifier_show(void *data) { EFL_UI_TEXT_DATA_GET(data, sd); edje_object_signal_emit(sd->mgf_bg, "elm,action,show,magnifier", "elm"); elm_widget_scroll_freeze_push(data); evas_object_show(sd->mgf_clip); } static Eina_Bool _long_press_cb(void *data) { EFL_UI_TEXT_DATA_GET(data, sd); if (_elm_config->magnifier_enable) { _magnifier_create(data); _magnifier_show(data); _magnifier_move(data); } /* Context menu will not appear if context menu disabled is set * as false on a long press callback */ else if (!_elm_config->context_menu_disabled && (!_elm_config->desktop_entry)) _menu_call(data); sd->long_pressed = EINA_TRUE; sd->longpress_timer = NULL; efl_event_callback_call(data, EFL_UI_EVENT_LONGPRESSED, NULL); return ECORE_CALLBACK_CANCEL; } static void _key_down_cb(void *data, Evas *evas EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event_info) { Evas_Event_Key_Down *ev = event_info; Eina_Bool on_hold = EINA_FALSE; /* First check if context menu disabled is false or not, and * then check for key id */ if ((!_elm_config->context_menu_disabled) && !strcmp(ev->key, "Menu")) { _menu_call(data); on_hold = EINA_TRUE; } else { #if defined(__APPLE__) && defined(__MACH__) Eina_Bool control = evas_key_modifier_is_set(ev->modifiers, "Super"); #else Eina_Bool control = evas_key_modifier_is_set(ev->modifiers, "Control"); #endif /* Ctrl operations */ if (control) { if (!strncmp(ev->key, "c", 1)) { _copy_cb(data); on_hold = EINA_TRUE; } else if (!strncmp(ev->key, "x", 1)) { _cut_cb(data); on_hold = EINA_TRUE; } else if (!strncmp(ev->key, "v", 1)) { _paste_cb(data); on_hold = EINA_TRUE; } } } if (on_hold) ev->event_flags |= EVAS_EVENT_FLAG_ON_HOLD; } static void _mouse_down_cb(void *data, Evas *evas EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event_info) { Evas_Event_Mouse_Down *ev = event_info; EFL_UI_TEXT_DATA_GET(data, sd); if (sd->disabled) return; if (ev->event_flags & EVAS_EVENT_FLAG_ON_HOLD) return; sd->downx = ev->canvas.x; sd->downy = ev->canvas.y; sd->long_pressed = EINA_FALSE; if (ev->button == 1) { ELM_SAFE_FREE(sd->longpress_timer, ecore_timer_del); sd->longpress_timer = ecore_timer_add (_elm_config->longpress_timeout, _long_press_cb, data); } /* If right button is pressed and context menu disabled is true, * then only context menu will appear */ else if (ev->button == 3 && (!_elm_config->context_menu_disabled)) { if (_elm_config->desktop_entry) { sd->use_down = 1; _menu_call(data); } } } static void _mouse_up_cb(void *data, Evas *evas EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event_info) { Evas_Event_Mouse_Up *ev = event_info; Eina_Bool top_is_win = EINA_FALSE; Evas_Object *top; EFL_UI_TEXT_DATA_GET(data, sd); if (sd->disabled) return; if (ev->button == 1) { ELM_SAFE_FREE(sd->longpress_timer, ecore_timer_del); /* Since context menu disabled flag was checked at long press start while mouse * down, hence the same should be checked at mouse up from a long press * as well */ if ((sd->long_pressed) && (_elm_config->magnifier_enable)) { _magnifier_hide(data); if (!_elm_config->context_menu_disabled) { _menu_call(data); } } else { top = elm_widget_top_get(data); if (top) { if (efl_isa(top, EFL_UI_WIN_CLASS)) top_is_win = EINA_TRUE; if (top_is_win && sd->input_panel_enable && sd->input_panel_show_on_demand) elm_win_keyboard_mode_set(top, ELM_WIN_KEYBOARD_ON); } } } /* Since context menu disabled flag was checked at mouse right key down, * hence the same should be stopped at mouse up of right key as well */ else if ((ev->button == 3) && (!_elm_config->context_menu_disabled) && (!_elm_config->desktop_entry)) { sd->use_down = 1; _menu_call(data); } } static void _mouse_move_cb(void *data, Evas *evas EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event_info) { Evas_Event_Mouse_Move *ev = event_info; EFL_UI_TEXT_DATA_GET(data, sd); if (sd->disabled) return; if (ev->buttons == 1) { if ((sd->long_pressed) && (_elm_config->magnifier_enable)) { Evas_Coord x, y; Eina_Bool rv; evas_object_geometry_get(sd->entry_edje, &x, &y, NULL, NULL); rv = edje_object_part_text_cursor_coord_set (sd->entry_edje, "elm.text", EDJE_CURSOR_USER, ev->cur.canvas.x - x, ev->cur.canvas.y - y); if (rv) { edje_object_part_text_cursor_copy (sd->entry_edje, "elm.text", EDJE_CURSOR_USER, EDJE_CURSOR_MAIN); } else WRN("Warning: Cannot move cursor"); _magnifier_move(data); } } if (!sd->sel_mode) { if (ev->event_flags & EVAS_EVENT_FLAG_ON_HOLD) { ELM_SAFE_FREE(sd->longpress_timer, ecore_timer_del); } else if (sd->longpress_timer) { Evas_Coord dx, dy; dx = sd->downx - ev->cur.canvas.x; dx *= dx; dy = sd->downy - ev->cur.canvas.y; dy *= dy; if ((dx + dy) > ((_elm_config->finger_size / 2) * (_elm_config->finger_size / 2))) { ELM_SAFE_FREE(sd->longpress_timer, ecore_timer_del); } } } else if (sd->longpress_timer) { Evas_Coord dx, dy; dx = sd->downx - ev->cur.canvas.x; dx *= dx; dy = sd->downy - ev->cur.canvas.y; dy *= dy; if ((dx + dy) > ((_elm_config->finger_size / 2) * (_elm_config->finger_size / 2))) { ELM_SAFE_FREE(sd->longpress_timer, ecore_timer_del); } } } static void _entry_changed_handle(void *data, const Efl_Event_Description* event, Efl_Ui_Text_Change_Info *info) { Evas_Coord minh; const char *text; Eina_Bool single_line; Eo *obj = data; EFL_UI_TEXT_DATA_GET(data, sd); single_line = !efl_text_multiline_get(obj); efl_event_freeze(obj); sd->changed = EINA_TRUE; /* Reset the size hints which are no more relevant. Keep the * height, this is a hack, but doesn't really matter cause we'll * re-eval in a moment. */ evas_object_size_hint_min_get(data, NULL, &minh); evas_object_size_hint_min_set(data, -1, minh); if (sd->single_line != single_line) { sd->single_line = single_line; efl_ui_widget_theme_apply(data); } elm_layout_sizing_eval(data); ELM_SAFE_FREE(sd->text, eina_stringshare_del); ELM_SAFE_FREE(sd->delay_write, ecore_timer_del); efl_event_thaw(obj); if ((sd->auto_save) && (sd->file)) sd->delay_write = ecore_timer_add(EFL_UI_TEXT_DELAY_WRITE_TIME, _delay_write, data); _return_key_enabled_check(data); text = efl_text_get(data); if (text) { if (text[0]) _efl_ui_text_guide_update(data, EINA_TRUE); else _efl_ui_text_guide_update(data, EINA_FALSE); } _validate(data); /* callback - this could call callbacks that delete the * entry... thus... any access to sd after this could be * invalid */ efl_event_callback_call(data, event, info); } static void _entry_changed_user_signal_cb(void *data, Evas_Object *obj EINA_UNUSED, const char *emission EINA_UNUSED, const char *source EINA_UNUSED) { Elm_Entry_Change_Info info; Edje_Entry_Change_Info *edje_info = (Edje_Entry_Change_Info *) edje_object_signal_callback_extra_data_get(); if (edje_info) { memcpy(&info, edje_info, sizeof(info)); efl_event_callback_call(data, EFL_UI_TEXT_EVENT_CHANGED_USER, &info); } else { efl_event_callback_call(data, EFL_UI_TEXT_EVENT_CHANGED_USER, NULL); } if (_elm_config->atspi_mode) { Efl_Access_Text_Change_Info atspi_info; if (edje_info && edje_info->insert) { atspi_info.content = edje_info->change.insert.content; atspi_info.pos = edje_info->change.insert.pos; atspi_info.len = edje_info->change.insert.plain_length; efl_access_event_emit(EFL_ACCESS_MIXIN, data, EFL_ACCESS_TEXT_EVENT_ACCESS_TEXT_INSERTED, &atspi_info); } else if (edje_info && !edje_info->insert) { atspi_info.content = edje_info->change.del.content; atspi_info.pos = MIN(edje_info->change.del.start, edje_info->change.del.end); atspi_info.len = MAX(edje_info->change.del.start, edje_info->change.del.end) - atspi_info.pos; efl_access_event_emit(EFL_ACCESS_MIXIN, data, EFL_ACCESS_TEXT_EVENT_ACCESS_TEXT_REMOVED, &atspi_info); } } } static void _entry_preedit_changed_signal_cb(void *data, Evas_Object *obj EINA_UNUSED, const char *emission EINA_UNUSED, const char *source EINA_UNUSED) { _entry_changed_handle(data, EFL_UI_TEXT_EVENT_PREEDIT_CHANGED, NULL); } static void _entry_undo_request_signal_cb(void *data, Evas_Object *obj EINA_UNUSED, const char *emission EINA_UNUSED, const char *source EINA_UNUSED) { efl_event_callback_call(data, EFL_UI_TEXT_EVENT_UNDO_REQUEST, NULL); } static void _entry_redo_request_signal_cb(void *data, Evas_Object *obj EINA_UNUSED, const char *emission EINA_UNUSED, const char *source EINA_UNUSED) { efl_event_callback_call(data, EFL_UI_TEXT_EVENT_REDO_REQUEST, NULL); } static void _entry_cursor_changed_signal_cb(void *data, Evas_Object *obj EINA_UNUSED, const char *emission EINA_UNUSED, const char *source EINA_UNUSED) { EFL_UI_TEXT_DATA_GET(data, sd); sd->cursor_pos = edje_object_part_text_cursor_pos_get (sd->entry_edje, "elm.text", EDJE_CURSOR_MAIN); sd->cur_changed = EINA_TRUE; if (efl_ui_focus_object_focus_get(data)) { edje_object_signal_emit(sd->entry_edje, "elm,action,show,cursor", "elm"); } else { edje_object_signal_emit(sd->entry_edje, "elm,action,hide,cursor", "elm"); } sd->deferred_decoration_cursor = EINA_TRUE; _decoration_defer(obj); if (_elm_config->atspi_mode) efl_access_event_emit(EFL_ACCESS_MIXIN, data, EFL_ACCESS_TEXT_EVENT_ACCESS_TEXT_CARET_MOVED, NULL); } static void _entry_cursor_changed_manual_signal_cb(void *data, Evas_Object *obj EINA_UNUSED, const char *emission EINA_UNUSED, const char *source EINA_UNUSED) { efl_event_callback_call(data, EFL_UI_TEXT_EVENT_CURSOR_CHANGED_MANUAL, NULL); if (_elm_config->atspi_mode) efl_access_event_emit(EFL_ACCESS_MIXIN, data, EFL_ACCESS_TEXT_EVENT_ACCESS_TEXT_CARET_MOVED, NULL); } static void _signal_anchor_geoms_do_things_with_lol(Efl_Ui_Text_Data *sd, Elm_Entry_Anchor_Info *ei) { Evas_Textblock_Rectangle *r; const Eina_List *geoms, *l; Evas_Coord px, py, x, y; geoms = edje_object_part_text_anchor_geometry_get (sd->entry_edje, "elm.text", ei->name); if (!geoms) return; evas_object_geometry_get( edje_object_part_object_get(sd->entry_edje, "elm.text"), &x, &y, NULL, NULL); evas_pointer_canvas_xy_get (evas_object_evas_get(sd->entry_edje), &px, &py); EINA_LIST_FOREACH(geoms, l, r) { if (((r->x + x) <= px) && ((r->y + y) <= py) && ((r->x + x + r->w) > px) && ((r->y + y + r->h) > py)) { ei->x = r->x + x; ei->y = r->y + y; ei->w = r->w; ei->h = r->h; break; } } } static void _entry_anchor_down_signal_cb(void *data, Evas_Object *obj EINA_UNUSED, const char *emission EINA_UNUSED, const char *source EINA_UNUSED) { Elm_Entry_Anchor_Info ei; const char *p; char *p2; EFL_UI_TEXT_DATA_GET(data, sd); p = emission + sizeof("nchor,mouse,down,"); ei.button = strtol(p, &p2, 10); ei.name = p2 + 1; ei.x = ei.y = ei.w = ei.h = 0; _signal_anchor_geoms_do_things_with_lol(sd, &ei); if (!sd->disabled) efl_event_callback_call(data, EFL_UI_TEXT_EVENT_ANCHOR_DOWN, &ei); } static void _entry_anchor_up_signal_cb(void *data, Evas_Object *obj EINA_UNUSED, const char *emission EINA_UNUSED, const char *source EINA_UNUSED) { Elm_Entry_Anchor_Info ei; const char *p; char *p2; EFL_UI_TEXT_DATA_GET(data, sd); p = emission + sizeof("nchor,mouse,up,"); ei.button = strtol(p, &p2, 10); ei.name = p2 + 1; ei.x = ei.y = ei.w = ei.h = 0; _signal_anchor_geoms_do_things_with_lol(sd, &ei); if (!sd->disabled) efl_event_callback_call(data, EFL_UI_TEXT_EVENT_ANCHOR_UP, &ei); } static void _anchor_hover_del_cb(void *data, Evas *e EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED) { EFL_UI_TEXT_DATA_GET(data, sd); ELM_SAFE_FREE(sd->anchor_hover.pop, evas_object_del); evas_object_event_callback_del_full (sd->anchor_hover.hover, EVAS_CALLBACK_DEL, _anchor_hover_del_cb, obj); } static void _anchor_hover_clicked_cb(void *data, const Efl_Event *event EINA_UNUSED) { EFL_UI_TEXT_DATA_GET(data, sd); _efl_ui_text_anchor_hover_end(data, sd); } static void _entry_hover_anchor_clicked_do(Evas_Object *obj, Elm_Entry_Anchor_Info *info) { Evas_Object *hover_parent; Evas_Coord x, w, y, h, px, py; Elm_Entry_Anchor_Hover_Info ei; EFL_UI_TEXT_DATA_GET(obj, sd); if (sd->hoversel) return; ei.anchor_info = info; sd->anchor_hover.pop = elm_icon_add(obj); evas_object_move(sd->anchor_hover.pop, info->x, info->y); evas_object_resize(sd->anchor_hover.pop, info->w, info->h); sd->anchor_hover.hover = elm_hover_add(obj); evas_object_event_callback_add (sd->anchor_hover.hover, EVAS_CALLBACK_DEL, _anchor_hover_del_cb, obj); efl_ui_mirrored_set (sd->anchor_hover.hover, efl_ui_mirrored_get(obj)); if (sd->anchor_hover.hover_style) elm_object_style_set (sd->anchor_hover.hover, sd->anchor_hover.hover_style); hover_parent = sd->anchor_hover.hover_parent; if (!hover_parent) hover_parent = obj; elm_hover_parent_set(sd->anchor_hover.hover, hover_parent); elm_hover_target_set(sd->anchor_hover.hover, sd->anchor_hover.pop); ei.hover = sd->anchor_hover.hover; evas_object_geometry_get(hover_parent, &x, &y, &w, &h); ei.hover_parent.x = x; ei.hover_parent.y = y; ei.hover_parent.w = w; ei.hover_parent.h = h; px = info->x + (info->w / 2); py = info->y + (info->h / 2); ei.hover_left = 1; if (px < (x + (w / 3))) ei.hover_left = 0; ei.hover_right = 1; if (px > (x + ((w * 2) / 3))) ei.hover_right = 0; ei.hover_top = 1; if (py < (y + (h / 3))) ei.hover_top = 0; ei.hover_bottom = 1; if (py > (y + ((h * 2) / 3))) ei.hover_bottom = 0; /* Swap right and left because they switch sides in RTL */ if (efl_ui_mirrored_get(sd->anchor_hover.hover)) { Eina_Bool tmp = ei.hover_left; ei.hover_left = ei.hover_right; ei.hover_right = tmp; } efl_event_callback_call(obj, EFL_UI_TEXT_EVENT_ANCHOR_HOVER_OPENED, &ei); efl_event_callback_add (sd->anchor_hover.hover, EFL_UI_EVENT_CLICKED, _anchor_hover_clicked_cb, obj); /* FIXME: Should just check if there's any callback registered to * the smart events instead. This is used to determine if anyone * cares about the hover or not. */ if (!elm_layout_content_get(sd->anchor_hover.hover, "middle") && !elm_layout_content_get(sd->anchor_hover.hover, "left") && !elm_layout_content_get(sd->anchor_hover.hover, "right") && !elm_layout_content_get(sd->anchor_hover.hover, "top") && !elm_layout_content_get(sd->anchor_hover.hover, "bottom")) { ELM_SAFE_FREE(sd->anchor_hover.hover, evas_object_del); } else evas_object_show(sd->anchor_hover.hover); } static void _entry_anchor_move_signal_cb(void *data EINA_UNUSED, Evas_Object *obj EINA_UNUSED, const char *emission EINA_UNUSED, const char *source EINA_UNUSED) { } static void _entry_anchor_in_signal_cb(void *data, Evas_Object *obj EINA_UNUSED, const char *emission EINA_UNUSED, const char *source EINA_UNUSED) { Elm_Entry_Anchor_Info ei; EFL_UI_TEXT_DATA_GET(data, sd); ei.name = emission + sizeof("nchor,mouse,in,"); ei.button = 0; ei.x = ei.y = ei.w = ei.h = 0; _signal_anchor_geoms_do_things_with_lol(sd, &ei); if (!sd->disabled) efl_event_callback_call(data, EFL_UI_TEXT_EVENT_ANCHOR_IN, &ei); } static void _entry_anchor_out_signal_cb(void *data, Evas_Object *obj EINA_UNUSED, const char *emission EINA_UNUSED, const char *source EINA_UNUSED) { Elm_Entry_Anchor_Info ei; EFL_UI_TEXT_DATA_GET(data, sd); ei.name = emission + sizeof("nchor,mouse,out,"); ei.button = 0; ei.x = ei.y = ei.w = ei.h = 0; _signal_anchor_geoms_do_things_with_lol(sd, &ei); if (!sd->disabled) efl_event_callback_call(data, EFL_UI_TEXT_EVENT_ANCHOR_OUT, &ei); } static void _entry_key_enter_signal_cb(void *data, Evas_Object *obj EINA_UNUSED, const char *emission EINA_UNUSED, const char *source EINA_UNUSED) { efl_event_callback_call(data, EFL_UI_TEXT_EVENT_ACTIVATED, NULL); } static void _entry_key_escape_signal_cb(void *data, Evas_Object *obj EINA_UNUSED, const char *emission EINA_UNUSED, const char *source EINA_UNUSED) { efl_event_callback_call(data, EFL_UI_TEXT_EVENT_ABORTED, NULL); } static void _entry_mouse_down_signal_cb(void *data, Evas_Object *obj EINA_UNUSED, const char *emission EINA_UNUSED, const char *source EINA_UNUSED) { efl_event_callback_call(data, EFL_UI_TEXT_EVENT_PRESS, NULL); } static void _entry_mouse_clicked_signal_cb(void *data, Evas_Object *obj EINA_UNUSED, const char *emission EINA_UNUSED, const char *source EINA_UNUSED) { efl_event_callback_call(data, EFL_UI_EVENT_CLICKED, NULL); } static void _entry_mouse_double_signal_cb(void *data, Evas_Object *obj EINA_UNUSED, const char *emission EINA_UNUSED, const char *source EINA_UNUSED) { efl_event_callback_call(data, EFL_UI_EVENT_CLICKED_DOUBLE, NULL); } static void _entry_mouse_triple_signal_cb(void *data, Evas_Object *obj EINA_UNUSED, const char *emission EINA_UNUSED, const char *source EINA_UNUSED) { efl_event_callback_call(data, EFL_UI_EVENT_CLICKED_TRIPLE, NULL); } static Evas_Object * _item_get(void *data, const char *item) { Evas_Object *o = NULL; EFL_UI_TEXT_DATA_GET(data, sd); if (item) { if (sd->item_factory) { o = efl_canvas_text_factory_create(sd->item_factory, data, item); } else if (sd->item_fallback_factory) { o = efl_canvas_text_factory_create(sd->item_fallback_factory, data, item); } } return o; } static void _markup_filter_cb(void *data, Evas_Object *edje EINA_UNUSED, const char *part EINA_UNUSED, char **text) { Eina_List *l; Elm_Entry_Markup_Filter *tf; EFL_UI_TEXT_DATA_GET(data, sd); EINA_LIST_FOREACH(sd->markup_filters, l, tf) { tf->func(tf->data, data, text); if (!*text) break; } } EOLIAN static void _efl_ui_text_efl_layout_signal_signal_emit(Eo *obj EINA_UNUSED, Efl_Ui_Text_Data *sd, const char *emission, const char *source) { /* always pass to both edje objs */ efl_layout_signal_emit(sd->entry_edje, emission, source); // FIXME: This should not be here! efl_layout_signal_process(sd->entry_edje, EINA_TRUE); if (sd->scr_edje) { efl_layout_signal_emit(sd->scr_edje, emission, source); efl_layout_signal_process(sd->scr_edje, EINA_TRUE); // FIXME } } EOLIAN static Eina_Bool _efl_ui_text_efl_layout_signal_signal_callback_add(Eo *obj EINA_UNUSED, Efl_Ui_Text_Data *sd, const char *emission, const char *source, Edje_Signal_Cb func_cb, void *data) { Eina_Bool ok; ok = efl_layout_signal_callback_add(sd->entry_edje, emission, source, func_cb, data); if (sd->scr_edje) ok = efl_layout_signal_callback_add(sd->scr_edje, emission, source, func_cb, data); return ok; } EOLIAN static Eina_Bool _efl_ui_text_efl_layout_signal_signal_callback_del(Eo *obj EINA_UNUSED, Efl_Ui_Text_Data *sd, const char *emission, const char *source, Edje_Signal_Cb func_cb, void *data) { Eina_Bool ok; ok = efl_layout_signal_callback_del(sd->entry_edje, emission, source, func_cb, data); if (sd->scr_edje) ok = efl_layout_signal_callback_del(sd->scr_edje, emission, source, func_cb, data); return ok; } #if 0 static Eina_Bool _efl_ui_text_content_set(Eo *obj, Efl_Ui_Text_Data *_pd EINA_UNUSED, const char *part, Evas_Object *content) { Eina_Bool int_ret = EINA_FALSE; int_ret = efl_content_set(efl_part(efl_super(obj, MY_CLASS), part), content); if (!int_ret) return EINA_FALSE; /* too bad entry does not follow the pattern * "elm,state,{icon,end},visible", we have to repeat ourselves */ if (!part || !strcmp(part, "icon") || !strcmp(part, "elm.swallow.icon")) efl_ui_text_icon_visible_set(obj, EINA_TRUE); if (!part || !strcmp(part, "end") || !strcmp(part, "elm.swallow.end")) efl_ui_text_end_visible_set(obj, EINA_TRUE); return EINA_TRUE; } static Evas_Object* _efl_ui_text_content_unset(Eo *obj, Efl_Ui_Text_Data *_pd EINA_UNUSED, const char *part) { Evas_Object *ret = NULL; ret = efl_content_unset(efl_part(efl_super(obj, MY_CLASS), part)); if (!ret) return NULL; /* too bad entry does not follow the pattern * "elm,state,{icon,end},hidden", we have to repeat ourselves */ if (!part || !strcmp(part, "icon") || !strcmp(part, "elm.swallow.icon")) efl_ui_text_icon_visible_set(obj, EINA_FALSE); if (!part || !strcmp(part, "end") || !strcmp(part, "elm.swallow.end")) efl_ui_text_end_visible_set(obj, EINA_FALSE); return ret; } #endif static char * _access_info_cb(void *data EINA_UNUSED, Evas_Object *obj) { #if 0 const char *txt; EFL_UI_TEXT_DATA_GET(obj, sd); if (sd->password) return NULL; txt = elm_widget_access_info_get(obj); if (!txt) return _elm_util_mkup_to_text(efl_ui_text_entry_get(obj)); else return strdup(txt); #else (void) data; (void) obj; return NULL; #endif } static char * _access_state_cb(void *data EINA_UNUSED, Evas_Object *obj) { Eina_Strbuf *buf; char *ret; EFL_UI_TEXT_DATA_GET(obj, sd); ret = NULL; buf = eina_strbuf_new(); if (elm_widget_disabled_get(obj)) eina_strbuf_append(buf, "State: Disabled"); if (!sd->editable) { if (!eina_strbuf_length_get(buf)) eina_strbuf_append(buf, "State: Not Editable"); else eina_strbuf_append(buf, ", Not Editable"); } if (sd->password) { if (!eina_strbuf_length_get(buf)) eina_strbuf_append(buf, "State: Password"); else eina_strbuf_append(buf, ", Password"); } if (!eina_strbuf_length_get(buf)) goto buf_free; ret = eina_strbuf_string_steal(buf); buf_free: eina_strbuf_free(buf); return ret; } static void _efl_ui_text_resize_internal(Evas_Object *obj) { EFL_UI_TEXT_DATA_GET(obj, sd); if (sd->line_wrap) { elm_layout_sizing_eval(obj); } else if (sd->scroll) { Evas_Coord vw = 0, vh = 0; elm_interface_scrollable_content_viewport_geometry_get (obj, NULL, NULL, &vw, &vh); evas_object_resize(sd->entry_edje, vw, vh); } if (sd->hoversel) _hoversel_position(obj); _decoration_defer_all(obj); } static void _resize_cb(void *data, Evas *e EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED) { _efl_ui_text_resize_internal(data); } static void _selection_handlers_offset_calc(Evas_Object *obj, Evas_Object *handler, Evas_Coord canvasx, Evas_Coord canvasy) { Evas_Coord ex, ey; Evas_Coord cx, cy, cw, ch; Evas_Coord hh; EFL_UI_TEXT_DATA_GET(obj, sd); evas_object_geometry_get(sd->entry_edje, &ex, &ey, NULL, NULL); efl_text_cursor_geometry_get(obj, efl_text_cursor_get(obj, EFL_TEXT_CURSOR_GET_MAIN), EFL_TEXT_CURSOR_TYPE_BEFORE, &cx, &cy, &cw, &ch, NULL, NULL, NULL, NULL); edje_object_size_min_calc(handler, NULL, &hh); sd->ox = canvasx - (ex + cx + (cw / 2)); if (ch > hh) sd->oy = canvasy - (ey + cy + ch); else sd->oy = canvasy - (ey + cy + (ch / 2)); ELM_SAFE_FREE(sd->longpress_timer, ecore_timer_del); sd->long_pressed = EINA_FALSE; if (_elm_config->magnifier_enable) { _magnifier_create(obj); _magnifier_show(obj); _magnifier_move(obj); } } static void _start_handler_mouse_down_cb(void *data, Evas *e EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event_info) { EFL_UI_TEXT_DATA_GET(data, sd); Evas_Event_Mouse_Down *ev = event_info; int start_pos, end_pos, pos; Efl_Text_Cursor_Cursor *sel_start, *sel_end; Efl_Text_Cursor_Cursor *main_cur; Eo *text_obj = edje_object_part_swallow_get(sd->entry_edje, "elm.text"); sd->start_handler_down = EINA_TRUE; /* Get the cursors */ efl_ui_text_interactive_selection_cursors_get(text_obj, &sel_start, &sel_end); main_cur = efl_text_cursor_get(text_obj, EFL_TEXT_CURSOR_GET_MAIN); start_pos = efl_text_cursor_position_get(obj, sel_start); end_pos = efl_text_cursor_position_get(obj, sel_end); if (start_pos <= end_pos) { pos = start_pos; sd->sel_handler_cursor = sel_start; } else { pos = end_pos; sd->sel_handler_cursor = sel_end; } efl_text_cursor_position_set(obj, main_cur, pos); _selection_handlers_offset_calc(data, sd->start_handler, ev->canvas.x, ev->canvas.y); } static void _start_handler_mouse_up_cb(void *data, Evas *e EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED) { EFL_UI_TEXT_DATA_GET(data, sd); sd->start_handler_down = EINA_FALSE; if (_elm_config->magnifier_enable) _magnifier_hide(data); /* Context menu should not appear, even in case of selector mode, if the * flag is false (disabled) */ if ((!_elm_config->context_menu_disabled) && (!_elm_config->desktop_entry) && (sd->long_pressed)) _menu_call(data); } static void _start_handler_mouse_move_cb(void *data, Evas *e EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event_info) { EFL_UI_TEXT_DATA_GET(data, sd); if (!sd->start_handler_down) return; Evas_Event_Mouse_Move *ev = event_info; Evas_Coord ex, ey; Evas_Coord cx, cy; int pos; evas_object_geometry_get(sd->entry_edje, &ex, &ey, NULL, NULL); cx = ev->cur.canvas.x - sd->ox - ex; cy = ev->cur.canvas.y - sd->oy - ey; if (cx <= 0) cx = 1; efl_text_cursor_coord_set(obj, sd->sel_handler_cursor, cx, cy); pos = efl_text_cursor_position_get(obj, sd->sel_handler_cursor); /* Set the main cursor. */ efl_text_cursor_position_set(obj, efl_text_cursor_get(data, EFL_TEXT_CURSOR_GET_MAIN), pos); ELM_SAFE_FREE(sd->longpress_timer, ecore_timer_del); sd->long_pressed = EINA_FALSE; if (_elm_config->magnifier_enable) _magnifier_move(data); } static void _end_handler_mouse_down_cb(void *data, Evas *e EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event_info) { EFL_UI_TEXT_DATA_GET(data, sd); Evas_Event_Mouse_Down *ev = event_info; Efl_Text_Cursor_Cursor *sel_start, *sel_end; Efl_Text_Cursor_Cursor *main_cur; int pos, start_pos, end_pos; sd->end_handler_down = EINA_TRUE; Eo *text_obj = edje_object_part_swallow_get(sd->entry_edje, "elm.text"); efl_ui_text_interactive_selection_cursors_get(text_obj, &sel_start, &sel_end); main_cur = efl_text_cursor_get(text_obj, EFL_TEXT_CURSOR_GET_MAIN); start_pos = efl_text_cursor_position_get(obj, sel_start); end_pos = efl_text_cursor_position_get(obj, sel_end); if (start_pos < end_pos) { pos = end_pos; sd->sel_handler_cursor = sel_end; } else { pos = start_pos; sd->sel_handler_cursor = sel_start; } efl_text_cursor_position_set(obj, main_cur, pos); _selection_handlers_offset_calc(data, sd->end_handler, ev->canvas.x, ev->canvas.y); } static void _end_handler_mouse_up_cb(void *data, Evas *e EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED) { EFL_UI_TEXT_DATA_GET(data, sd); sd->end_handler_down = EINA_FALSE; if (_elm_config->magnifier_enable) _magnifier_hide(data); /* Context menu appear was checked in case of selector start, and hence * the same should be checked at selector end as well */ if ((!_elm_config->context_menu_disabled) && (!_elm_config->desktop_entry) && (sd->long_pressed)) _menu_call(data); } static void _end_handler_mouse_move_cb(void *data, Evas *e EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event_info) { EFL_UI_TEXT_DATA_GET(data, sd); if (!sd->end_handler_down) return; Evas_Event_Mouse_Move *ev = event_info; Evas_Coord ex, ey; Evas_Coord cx, cy; int pos; evas_object_geometry_get(sd->entry_edje, &ex, &ey, NULL, NULL); cx = ev->cur.canvas.x - sd->ox - ex; cy = ev->cur.canvas.y - sd->oy - ey; if (cx <= 0) cx = 1; efl_text_cursor_coord_set(obj, sd->sel_handler_cursor, cx, cy); pos = efl_text_cursor_position_get(obj, sd->sel_handler_cursor); /* Set the main cursor. */ efl_text_cursor_position_set(obj, efl_text_cursor_get(data, EFL_TEXT_CURSOR_GET_MAIN), pos); ELM_SAFE_FREE(sd->longpress_timer, ecore_timer_del); sd->long_pressed = EINA_FALSE; if (_elm_config->magnifier_enable) _magnifier_move(data); } static void _create_selection_handlers(Evas_Object *obj, Efl_Ui_Text_Data *sd) { Evas_Object *handle; handle = _decoration_create(obj, sd, PART_NAME_HANDLER_START, EINA_TRUE); evas_object_pass_events_set(handle, EINA_FALSE); sd->start_handler = handle; evas_object_event_callback_add(handle, EVAS_CALLBACK_MOUSE_DOWN, _start_handler_mouse_down_cb, obj); evas_object_event_callback_add(handle, EVAS_CALLBACK_MOUSE_MOVE, _start_handler_mouse_move_cb, obj); evas_object_event_callback_add(handle, EVAS_CALLBACK_MOUSE_UP, _start_handler_mouse_up_cb, obj); evas_object_show(handle); handle = _decoration_create(obj, sd, PART_NAME_HANDLER_END, EINA_TRUE); evas_object_pass_events_set(handle, EINA_FALSE); sd->end_handler = handle; evas_object_event_callback_add(handle, EVAS_CALLBACK_MOUSE_DOWN, _end_handler_mouse_down_cb, obj); evas_object_event_callback_add(handle, EVAS_CALLBACK_MOUSE_MOVE, _end_handler_mouse_move_cb, obj); evas_object_event_callback_add(handle, EVAS_CALLBACK_MOUSE_UP, _end_handler_mouse_up_cb, obj); evas_object_show(handle); } EOLIAN static void _efl_ui_text_efl_gfx_position_set(Eo *obj, Efl_Ui_Text_Data *sd, Eina_Position2D pos) { if (_evas_object_intercept_call(obj, EVAS_OBJECT_INTERCEPT_CB_MOVE, 0, pos.x, pos.y)) return; efl_gfx_position_set(efl_super(obj, MY_CLASS), pos); efl_gfx_position_set(sd->hit_rect, pos); if (sd->hoversel) _hoversel_position(obj); _update_selection_handler(obj); } EOLIAN static void _efl_ui_text_efl_gfx_size_set(Eo *obj, Efl_Ui_Text_Data *sd, Eina_Size2D sz) { if (_evas_object_intercept_call(obj, EVAS_OBJECT_INTERCEPT_CB_RESIZE, 0, sz.w, sz.h)) return; efl_gfx_size_set(sd->hit_rect, sz); _update_selection_handler(obj); efl_gfx_size_set(efl_super(obj, MY_CLASS), sz); } EOLIAN static void _efl_ui_text_efl_gfx_visible_set(Eo *obj, Efl_Ui_Text_Data *sd EINA_UNUSED, Eina_Bool vis) { if (_evas_object_intercept_call(obj, EVAS_OBJECT_INTERCEPT_CB_VISIBLE, 0, vis)) return; efl_gfx_visible_set(efl_super(obj, MY_CLASS), vis); if (vis) _update_selection_handler(obj); } EOLIAN static void _efl_ui_text_efl_canvas_group_group_member_add(Eo *obj, Efl_Ui_Text_Data *sd, Evas_Object *member) { efl_canvas_group_member_add(efl_super(obj, MY_CLASS), member); if (sd->hit_rect) evas_object_raise(sd->hit_rect); } static void _cb_added(void *data EINA_UNUSED, const Efl_Event *ev) { const Efl_Callback_Array_Item *event = ev->info; EFL_UI_TEXT_DATA_GET(ev->object, sd); if (event->desc == EFL_UI_TEXT_EVENT_VALIDATE) sd->validators++; } static void _cb_deleted(void *data EINA_UNUSED, const Efl_Event *ev) { const Efl_Callback_Array_Item *event = ev->info; EFL_UI_TEXT_DATA_GET(ev->object, sd); if (event->desc == EFL_UI_TEXT_EVENT_VALIDATE) sd->validators--; return; } EOLIAN static Eo * _efl_ui_text_efl_object_constructor(Eo *obj, Efl_Ui_Text_Data *sd) { Eo *text_obj; ELM_WIDGET_DATA_GET_OR_RETURN(obj, wd, NULL); if (!elm_widget_theme_klass_get(obj)) elm_widget_theme_klass_set(obj, "text"); obj = efl_constructor(efl_super(obj, MY_CLASS)); elm_widget_sub_object_parent_add(obj); evas_object_smart_callbacks_descriptions_set(obj, _smart_callbacks); efl_access_role_set(obj, EFL_ACCESS_ROLE_ENTRY); efl_event_callback_add(obj, EFL_EVENT_CALLBACK_ADD, _cb_added, NULL); efl_event_callback_add(obj, EFL_EVENT_CALLBACK_DEL, _cb_deleted, NULL); sd->entry_edje = wd->resize_obj; sd->cnp_mode = ELM_CNP_MODE_PLAINTEXT; sd->line_wrap = ELM_WRAP_WORD; sd->context_menu = EINA_TRUE; sd->auto_save = EINA_TRUE; sd->editable = EINA_TRUE; sd->sel_allow = EINA_TRUE; sd->drop_format = ELM_SEL_FORMAT_MARKUP | ELM_SEL_FORMAT_IMAGE; elm_drop_target_add(obj, sd->drop_format, _dnd_enter_cb, NULL, _dnd_leave_cb, NULL, _dnd_pos_cb, NULL, _dnd_drop_cb, NULL); if (!elm_widget_theme_object_set(obj, wd->resize_obj, elm_widget_theme_klass_get(obj), elm_widget_theme_element_get(obj), elm_widget_theme_style_get(obj))) CRI("Failed tp set layout!"); text_obj = efl_add(EFL_UI_INTERNAL_TEXT_INTERACTIVE_CLASS, obj); efl_composite_attach(obj, text_obj); sd->text_obj = text_obj; // FIXME: use the theme, when a proper theming option is available // (possibly, text_classes). // For now, set this for easier setup efl_text_font_set(text_obj, "Sans", 12); efl_text_normal_color_set(text_obj, 255, 255, 255, 255); efl_ui_text_interactive_editable_set(obj, EINA_FALSE); sd->single_line = !efl_text_multiline_get(text_obj); edje_object_part_swallow(sd->entry_edje, "elm.text", text_obj); evas_object_size_hint_weight_set (sd->entry_edje, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND); evas_object_size_hint_align_set (sd->entry_edje, EVAS_HINT_FILL, EVAS_HINT_FILL); efl_event_callback_add(text_obj, EFL_UI_TEXT_EVENT_CHANGED_USER, _efl_ui_text_changed_user_cb, obj); efl_event_callback_add(text_obj, EFL_CANVAS_TEXT_EVENT_CHANGED, _efl_ui_text_changed_cb, obj); efl_event_callback_add(text_obj, EFL_UI_TEXT_INTERACTIVE_EVENT_SELECTION_CHANGED, _efl_ui_text_selection_changed_cb, obj); efl_event_callback_add(text_obj, EFL_CANVAS_TEXT_EVENT_CURSOR_CHANGED, _efl_ui_text_cursor_changed_cb, obj); evas_object_event_callback_add(sd->entry_edje, EVAS_CALLBACK_MOVE, _efl_ui_text_move_cb, obj); /* Async layout */ if (sd->async.enabled) { #if 0 efl_event_callback_add(text_obj, EFL_CANVAS_TEXT_EVENT_ASYNC_LAYOUT_COMPLETE, _on_layout_complete, obj); #endif } sd->hit_rect = evas_object_rectangle_add(evas_object_evas_get(obj)); evas_object_data_set(sd->hit_rect, "_elm_leaveme", obj); Evas_Object* clip = evas_object_clip_get(sd->entry_edje); evas_object_clip_set(sd->hit_rect, clip); evas_object_smart_member_add(sd->hit_rect, obj); elm_widget_sub_object_add(obj, sd->hit_rect); /* common scroller hit rectangle setup */ evas_object_color_set(sd->hit_rect, 0, 0, 0, 0); evas_object_show(sd->hit_rect); evas_object_repeat_events_set(sd->hit_rect, EINA_TRUE); elm_interface_scrollable_objects_set(obj, sd->entry_edje, sd->hit_rect); edje_object_text_markup_filter_callback_add (sd->entry_edje, "elm.text", _markup_filter_cb, obj); evas_object_event_callback_add (sd->entry_edje, EVAS_CALLBACK_KEY_DOWN, _key_down_cb, obj); evas_object_event_callback_add (sd->entry_edje, EVAS_CALLBACK_MOUSE_DOWN, _mouse_down_cb, obj); evas_object_event_callback_add (sd->entry_edje, EVAS_CALLBACK_MOUSE_UP, _mouse_up_cb, obj); evas_object_event_callback_add (sd->entry_edje, EVAS_CALLBACK_MOUSE_MOVE, _mouse_move_cb, obj); /* this code can't go in smart_resize. sizing gets wrong */ evas_object_event_callback_add(obj, EVAS_CALLBACK_RESIZE, _resize_cb, obj); edje_object_signal_callback_add (sd->entry_edje, "entry,changed,user", "elm.text", _entry_changed_user_signal_cb, obj); edje_object_signal_callback_add (sd->entry_edje, "preedit,changed", "elm.text", _entry_preedit_changed_signal_cb, obj); edje_object_signal_callback_add (sd->entry_edje, "cursor,changed", "elm.text", _entry_cursor_changed_signal_cb, obj); edje_object_signal_callback_add (sd->entry_edje, "cursor,changed,manual", "elm.text", _entry_cursor_changed_manual_signal_cb, obj); edje_object_signal_callback_add (sd->entry_edje, "anchor,mouse,down,*", "elm.text", _entry_anchor_down_signal_cb, obj); edje_object_signal_callback_add (sd->entry_edje, "anchor,mouse,up,*", "elm.text", _entry_anchor_up_signal_cb, obj); edje_object_signal_callback_add (sd->entry_edje, "anchor,mouse,move,*", "elm.text", _entry_anchor_move_signal_cb, obj); edje_object_signal_callback_add (sd->entry_edje, "anchor,mouse,in,*", "elm.text", _entry_anchor_in_signal_cb, obj); edje_object_signal_callback_add (sd->entry_edje, "anchor,mouse,out,*", "elm.text", _entry_anchor_out_signal_cb, obj); edje_object_signal_callback_add (sd->entry_edje, "entry,key,enter", "elm.text", _entry_key_enter_signal_cb, obj); edje_object_signal_callback_add (sd->entry_edje, "entry,key,escape", "elm.text", _entry_key_escape_signal_cb, obj); edje_object_signal_callback_add (sd->entry_edje, "mouse,down,1", "elm.text", _entry_mouse_down_signal_cb, obj); edje_object_signal_callback_add (sd->entry_edje, "mouse,clicked,1", "elm.text", _entry_mouse_clicked_signal_cb, obj); edje_object_signal_callback_add (sd->entry_edje, "mouse,down,1,double", "elm.text", _entry_mouse_double_signal_cb, obj); edje_object_signal_callback_add (sd->entry_edje, "mouse,down,1,triple", "elm.text", _entry_mouse_triple_signal_cb, obj); edje_object_signal_callback_add (sd->entry_edje, "entry,undo,request", "elm.text", _entry_undo_request_signal_cb, obj); edje_object_signal_callback_add (sd->entry_edje, "entry,redo,request", "elm.text", _entry_redo_request_signal_cb, obj); efl_text_set(sd->text_obj, ""); elm_object_sub_cursor_set (wd->resize_obj, obj, ELM_CURSOR_XTERM); elm_widget_can_focus_set(obj, EINA_TRUE); if (_elm_config->desktop_entry) edje_object_part_text_select_allow_set (sd->entry_edje, "elm.text", EINA_TRUE); efl_ui_text_input_panel_layout_set(obj, ELM_INPUT_PANEL_LAYOUT_NORMAL); efl_ui_text_input_panel_enabled_set(obj, EINA_TRUE); efl_ui_text_prediction_allow_set(obj, EINA_TRUE); efl_ui_text_input_hint_set(obj, ELM_INPUT_HINT_AUTO_COMPLETE); sd->autocapital_type = (Elm_Autocapital_Type)edje_object_part_text_autocapital_type_get (sd->entry_edje, "elm.text"); entries = eina_list_prepend(entries, obj); // module - find module for entry sd->api = _module_find(obj); // if found - hook in if ((sd->api) && (sd->api->obj_hook)) sd->api->obj_hook(obj); _mirrored_set(obj, efl_ui_mirrored_get(obj)); // access _elm_access_object_register(obj, sd->entry_edje); _elm_access_text_set (_elm_access_info_get(obj), ELM_ACCESS_TYPE, E_("Entry")); _elm_access_callback_set (_elm_access_info_get(obj), ELM_ACCESS_INFO, _access_info_cb, NULL); _elm_access_callback_set (_elm_access_info_get(obj), ELM_ACCESS_STATE, _access_state_cb, NULL); if (_elm_config->desktop_entry) sd->sel_handler_disabled = EINA_TRUE; sd->item_fallback_factory = efl_add(EFL_UI_TEXT_FACTORY_FALLBACK_CLASS, obj); _create_text_cursors(obj, sd); return obj; } EOLIAN static void _efl_ui_text_efl_object_destructor(Eo *obj, Efl_Ui_Text_Data *sd) { Elm_Entry_Context_Menu_Item *it; Elm_Entry_Markup_Filter *tf; Eo *text_obj; if (sd->delay_write) { ELM_SAFE_FREE(sd->delay_write, ecore_timer_del); if (sd->auto_save) _save_do(obj); } if (sd->scroll) elm_interface_scrollable_content_viewport_resize_cb_set(obj, NULL); _efl_ui_text_anchor_hover_end(obj, sd); _efl_ui_text_anchor_hover_parent_set(obj, sd, NULL); efl_event_freeze(obj); eina_stringshare_del(sd->file); ecore_job_del(sd->hov_deljob); if ((sd->api) && (sd->api->obj_unhook)) sd->api->obj_unhook(obj); // module - unhook evas_object_del(sd->mgf_proxy); evas_object_del(sd->mgf_bg); evas_object_del(sd->mgf_clip); entries = eina_list_remove(entries, obj); eina_stringshare_del(sd->cut_sel); eina_stringshare_del(sd->text); ecore_job_del(sd->deferred_decoration_job); if (sd->append_text_idler) { ecore_idler_del(sd->append_text_idler); ELM_SAFE_FREE(sd->append_text_left, free); sd->append_text_idler = NULL; } ecore_timer_del(sd->longpress_timer); EINA_LIST_FREE(sd->items, it) { eina_stringshare_del(it->label); eina_stringshare_del(it->icon_file); eina_stringshare_del(it->icon_group); free(it); } EINA_LIST_FREE(sd->markup_filters, tf) { _filter_free(tf); } ELM_SAFE_FREE(sd->delay_write, ecore_timer_del); free(sd->input_panel_imdata); eina_stringshare_del(sd->anchor_hover.hover_style); efl_event_thaw(obj); if (sd->start_handler) { evas_object_del(sd->start_handler); evas_object_del(sd->end_handler); } _anchors_clear_all(obj, sd); _unused_item_objs_free(sd); _clear_text_selection(sd); text_obj = edje_object_part_swallow_get(sd->entry_edje, "elm.text"); efl_event_callback_del(text_obj, EFL_UI_TEXT_EVENT_CHANGED_USER, _efl_ui_text_changed_user_cb, obj); efl_event_callback_del(text_obj, EFL_CANVAS_TEXT_EVENT_CHANGED, _efl_ui_text_changed_cb, obj); efl_event_callback_del(text_obj, EFL_UI_TEXT_INTERACTIVE_EVENT_SELECTION_CHANGED, _efl_ui_text_selection_changed_cb, obj); efl_event_callback_del(text_obj, EFL_CANVAS_TEXT_EVENT_CURSOR_CHANGED, _efl_ui_text_cursor_changed_cb, obj); evas_object_event_callback_del_full(sd->entry_edje, EVAS_CALLBACK_MOVE, _efl_ui_text_move_cb, obj); // XXX: explicitly delete the object, as it's been reparented to the canvas, due to // a specific behavior of SWALLOW parts. efl_del(sd->text_obj); sd->text_obj = NULL; ecore_job_del(sd->deferred_decoration_job); sd->deferred_decoration_job = NULL; if (sd->item_factory) efl_unref(sd->item_factory); if (sd->item_fallback_factory) efl_del(sd->item_fallback_factory); efl_destructor(efl_super(obj, MY_CLASS)); } EOLIAN static void _efl_ui_text_password_set(Eo *obj, Efl_Ui_Text_Data *sd, Eina_Bool password) { password = !!password; if (sd->password == password) return; sd->password = password; elm_drop_target_del(obj, sd->drop_format, _dnd_enter_cb, NULL, _dnd_leave_cb, NULL, _dnd_pos_cb, NULL, _dnd_drop_cb, NULL); if (password) { sd->single_line = EINA_TRUE; sd->line_wrap = ELM_WRAP_NONE; efl_ui_text_input_hint_set(obj, ((sd->input_hints & ~ELM_INPUT_HINT_AUTO_COMPLETE) | ELM_INPUT_HINT_SENSITIVE_DATA)); efl_access_role_set(obj, EFL_ACCESS_ROLE_PASSWORD_TEXT); } else { sd->drop_format = _get_drop_format(obj); elm_drop_target_add(obj, sd->drop_format, _dnd_enter_cb, NULL, _dnd_leave_cb, NULL, _dnd_pos_cb, NULL, _dnd_drop_cb, NULL); efl_ui_text_input_hint_set(obj, ((sd->input_hints | ELM_INPUT_HINT_AUTO_COMPLETE) & ~ELM_INPUT_HINT_SENSITIVE_DATA)); efl_access_role_set(obj, EFL_ACCESS_ROLE_ENTRY); } efl_ui_widget_theme_apply(obj); } EOLIAN static Eina_Bool _efl_ui_text_password_get(Eo *obj EINA_UNUSED, Efl_Ui_Text_Data *sd) { return sd->password; } static void _efl_ui_text_calc_force(Eo *obj, Efl_Ui_Text_Data *sd) { edje_object_calc_force(sd->entry_edje); sd->changed = EINA_TRUE; elm_layout_sizing_eval(obj); } static const char* _efl_ui_text_selection_get(Eo *obj EINA_UNUSED, Efl_Ui_Text_Data *sd) { Efl_Text_Cursor_Cursor *start_obj, *end_obj; if ((sd->password)) return NULL; efl_ui_text_interactive_selection_cursors_get(obj, &start_obj, &end_obj); return efl_canvas_text_range_text_get(obj, start_obj, end_obj); } EOLIAN static void _efl_ui_text_selection_handler_disabled_set(Eo *obj EINA_UNUSED, Efl_Ui_Text_Data *sd, Eina_Bool disabled) { if (sd->sel_handler_disabled == disabled) return; sd->sel_handler_disabled = disabled; } EOLIAN static Eina_Bool _efl_ui_text_selection_handler_disabled_get(Eo *obj EINA_UNUSED, Efl_Ui_Text_Data *sd) { return sd->sel_handler_disabled; } static void _efl_ui_text_entry_insert(Eo *obj, Efl_Ui_Text_Data *sd, const char *entry) { Efl_Text_Cursor_Cursor *cur_obj = efl_text_cursor_get(obj, EFL_TEXT_CURSOR_GET_MAIN); efl_text_cursor_text_insert(obj, cur_obj, entry); sd->changed = EINA_TRUE; elm_layout_sizing_eval(obj); } EOLIAN static void _efl_ui_text_efl_ui_text_interactive_editable_set(Eo *obj, Efl_Ui_Text_Data *sd, Eina_Bool editable) { efl_ui_text_interactive_editable_set(efl_super(obj, MY_CLASS), editable); if (sd->editable == editable) return; sd->editable = editable; efl_ui_widget_theme_apply(obj); elm_drop_target_del(obj, sd->drop_format, _dnd_enter_cb, NULL, _dnd_leave_cb, NULL, _dnd_pos_cb, NULL, _dnd_drop_cb, NULL); if (editable) { sd->drop_format = _get_drop_format(obj); elm_drop_target_add(obj, sd->drop_format, _dnd_enter_cb, NULL, _dnd_leave_cb, NULL, _dnd_pos_cb, NULL, _dnd_drop_cb, NULL); if (sd->cursor) { evas_object_show(sd->cursor); evas_object_show(sd->cursor_bidi); } } if (!editable && sd->cursor) { evas_object_hide(sd->cursor); evas_object_hide(sd->cursor_bidi); } } static void _efl_ui_text_select_none(Eo *obj EINA_UNUSED, Efl_Ui_Text_Data *sd) { if ((sd->password)) return; if (sd->sel_mode) { sd->sel_mode = EINA_FALSE; if (!_elm_config->desktop_entry) edje_object_part_text_select_allow_set (sd->entry_edje, "elm.text", EINA_FALSE); edje_object_signal_emit(sd->entry_edje, "elm,state,select,off", "elm"); } if (sd->have_selection) efl_event_callback_call(obj, EFL_UI_EVENT_SELECTION_CLEARED, NULL); sd->have_selection = EINA_FALSE; edje_object_part_text_select_none(sd->entry_edje, "elm.text"); _hide_selection_handler(obj); } static void _efl_ui_text_select_region_set(Eo *obj, Efl_Ui_Text_Data *sd, int start, int end) { Efl_Text_Cursor_Cursor *sel_start, *sel_end; if ((sd->password)) return; efl_ui_text_interactive_selection_cursors_get(obj, &sel_start, &sel_end); efl_text_cursor_position_set(obj, sel_start, start); efl_text_cursor_position_set(obj, sel_end, end); } EOLIAN static void _efl_ui_text_cursor_selection_end(Eo *obj EINA_UNUSED, Efl_Ui_Text_Data *sd) { edje_object_part_text_select_extend(sd->entry_edje, "elm.text"); } EOLIAN static void _efl_ui_text_selection_cut(Eo *obj, Efl_Ui_Text_Data *sd) { if ((sd->password)) return; _cut_cb(obj); } EOLIAN static void _efl_ui_text_selection_copy(Eo *obj, Efl_Ui_Text_Data *sd) { if ((sd->password)) return; _copy_cb(obj); } EOLIAN static void _efl_ui_text_selection_paste(Eo *obj, Efl_Ui_Text_Data *sd) { if ((sd->password)) return; _paste_cb(obj); } EOLIAN static void _efl_ui_text_context_menu_clear(Eo *obj EINA_UNUSED, Efl_Ui_Text_Data *sd) { Elm_Entry_Context_Menu_Item *it; EINA_LIST_FREE(sd->items, it) { eina_stringshare_del(it->label); eina_stringshare_del(it->icon_file); eina_stringshare_del(it->icon_group); free(it); } } EOLIAN static void _efl_ui_text_context_menu_item_add(Eo *obj, Efl_Ui_Text_Data *sd, const char *label, const char *icon_file, Elm_Icon_Type icon_type, Evas_Smart_Cb func, const void *data) { Elm_Entry_Context_Menu_Item *it; it = calloc(1, sizeof(Elm_Entry_Context_Menu_Item)); if (!it) return; sd->items = eina_list_append(sd->items, it); it->obj = obj; it->label = eina_stringshare_add(label); it->icon_file = eina_stringshare_add(icon_file); it->icon_type = icon_type; it->func = func; it->data = (void *)data; } EOLIAN static void _efl_ui_text_context_menu_disabled_set(Eo *obj EINA_UNUSED, Efl_Ui_Text_Data *sd, Eina_Bool disabled) { if (sd->context_menu == !disabled) return; sd->context_menu = !disabled; } EOLIAN static Eina_Bool _efl_ui_text_context_menu_disabled_get(Eo *obj EINA_UNUSED, Efl_Ui_Text_Data *sd) { return !sd->context_menu; } EOLIAN static Eina_Bool _efl_ui_text_efl_file_file_set(Eo *obj, Efl_Ui_Text_Data *sd, const char *file, const char *group EINA_UNUSED) { ELM_SAFE_FREE(sd->delay_write, ecore_timer_del); if (sd->auto_save) _save_do(obj); eina_stringshare_replace(&sd->file, file); Eina_Bool int_ret = _load_do(obj); return int_ret; } EOLIAN static void _efl_ui_text_efl_file_file_get(Eo *obj EINA_UNUSED, Efl_Ui_Text_Data *sd, const char **file, const char **group) { if (file) *file = sd->file; if (group) *group = NULL; } EOLIAN static void _efl_ui_text_cnp_mode_set(Eo *obj, Efl_Ui_Text_Data *sd, Efl_Selection_Format cnp_mode) { /* FIXME: CnP format handling really odd... */ Elm_Sel_Format dnd_format = ELM_SEL_FORMAT_MARKUP; Elm_Sel_Format cnp_format = cnp_mode; if (cnp_mode != EFL_SELECTION_FORMAT_TARGETS) { if (cnp_mode & EFL_SELECTION_FORMAT_VCARD) ERR("VCARD format not supported for copy & paste!"); else if (cnp_mode & EFL_SELECTION_FORMAT_HTML) ERR("HTML format not supported for copy & paste!"); cnp_mode &= ~EFL_SELECTION_FORMAT_VCARD; cnp_mode &= ~EFL_SELECTION_FORMAT_HTML; } if (sd->cnp_mode == cnp_format) return; sd->cnp_mode = cnp_format; if (sd->cnp_mode == EFL_SELECTION_FORMAT_TEXT) dnd_format = ELM_SEL_FORMAT_TEXT; else if (cnp_mode == EFL_SELECTION_FORMAT_IMAGE) dnd_format |= ELM_SEL_FORMAT_IMAGE; elm_drop_target_del(obj, sd->drop_format, _dnd_enter_cb, NULL, _dnd_leave_cb, NULL, _dnd_pos_cb, NULL, _dnd_drop_cb, NULL); sd->drop_format = dnd_format; elm_drop_target_add(obj, sd->drop_format, _dnd_enter_cb, NULL, _dnd_leave_cb, NULL, _dnd_pos_cb, NULL, _dnd_drop_cb, NULL); } EOLIAN static Efl_Selection_Format _efl_ui_text_cnp_mode_get(Eo *obj EINA_UNUSED, Efl_Ui_Text_Data *sd) { return sd->cnp_mode; } static void _efl_ui_text_content_viewport_resize_cb(Evas_Object *obj, Evas_Coord w EINA_UNUSED, Evas_Coord h EINA_UNUSED) { _efl_ui_text_resize_internal(obj); } static void _scroll_cb(Evas_Object *obj, void *data EINA_UNUSED) { EFL_UI_TEXT_DATA_GET(obj, sd); /* here we need to emit the signal that the elm_scroller would have done */ efl_event_callback_call(obj, EFL_UI_EVENT_SCROLL, NULL); if (sd->have_selection) _update_selection_handler(obj); } EOLIAN static void _efl_ui_text_scrollable_set(Eo *obj, Efl_Ui_Text_Data *sd, Eina_Bool scroll) { scroll = !!scroll; if (sd->scroll == scroll) return; sd->scroll = scroll; if (sd->scroll) { /* we now must re-theme ourselves to a scroller decoration * and move the entry looking object to be the content of the * scrollable view */ elm_widget_resize_object_set(obj, NULL); elm_widget_sub_object_add(obj, sd->entry_edje); if (!sd->scr_edje) { sd->scr_edje = edje_object_add(evas_object_evas_get(obj)); elm_widget_element_update(obj, sd->scr_edje, PART_NAME_SCROLLER); evas_object_size_hint_weight_set (sd->scr_edje, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND); evas_object_size_hint_align_set (sd->scr_edje, EVAS_HINT_FILL, EVAS_HINT_FILL); evas_object_propagate_events_set(sd->scr_edje, EINA_TRUE); } elm_widget_resize_object_set(obj, sd->scr_edje); elm_interface_scrollable_objects_set(obj, sd->scr_edje, sd->hit_rect); elm_interface_scrollable_scroll_cb_set(obj, _scroll_cb); elm_interface_scrollable_bounce_allow_set(obj, sd->h_bounce, sd->v_bounce); if (sd->single_line) elm_interface_scrollable_policy_set(obj, ELM_SCROLLER_POLICY_OFF, ELM_SCROLLER_POLICY_OFF); else elm_interface_scrollable_policy_set(obj, sd->policy_h, sd->policy_v); elm_interface_scrollable_content_set(obj, sd->entry_edje); elm_interface_scrollable_content_viewport_resize_cb_set(obj, _efl_ui_text_content_viewport_resize_cb); elm_widget_on_show_region_hook_set(obj, NULL, _show_region_hook, NULL); } else { if (sd->scr_edje) { elm_interface_scrollable_content_set(obj, NULL); evas_object_hide(sd->scr_edje); } elm_widget_resize_object_set(obj, sd->entry_edje); if (sd->scr_edje) elm_widget_sub_object_add(obj, sd->scr_edje); elm_interface_scrollable_objects_set(obj, sd->entry_edje, sd->hit_rect); elm_widget_on_show_region_hook_set(obj, NULL, NULL, NULL); } _update_decorations(obj); efl_ui_widget_theme_apply(obj); } EOLIAN static Eina_Bool _efl_ui_text_scrollable_get(Eo *obj EINA_UNUSED, Efl_Ui_Text_Data *sd) { return sd->scroll; } EOLIAN static void _efl_ui_text_elm_interface_scrollable_policy_set(Eo *obj, Efl_Ui_Text_Data *sd, Elm_Scroller_Policy h, Elm_Scroller_Policy v) { sd->policy_h = h; sd->policy_v = v; elm_interface_scrollable_policy_set(efl_super(obj, MY_CLASS), sd->policy_h, sd->policy_v); } EOLIAN static void _efl_ui_text_elm_interface_scrollable_bounce_allow_set(Eo *obj, Efl_Ui_Text_Data *sd, Eina_Bool h_bounce, Eina_Bool v_bounce) { sd->h_bounce = h_bounce; sd->v_bounce = v_bounce; elm_interface_scrollable_bounce_allow_set(efl_super(obj, MY_CLASS), h_bounce, v_bounce); } EOLIAN static void _efl_ui_text_input_panel_layout_set(Eo *obj EINA_UNUSED, Efl_Ui_Text_Data *sd, Elm_Input_Panel_Layout layout) { sd->input_panel_layout = layout; edje_object_part_text_input_panel_layout_set (sd->entry_edje, "elm.text", (Edje_Input_Panel_Layout)layout); if (layout == ELM_INPUT_PANEL_LAYOUT_PASSWORD) efl_ui_text_input_hint_set(obj, ((sd->input_hints & ~ELM_INPUT_HINT_AUTO_COMPLETE) | ELM_INPUT_HINT_SENSITIVE_DATA)); else if (layout == ELM_INPUT_PANEL_LAYOUT_TERMINAL) efl_ui_text_input_hint_set(obj, (sd->input_hints & ~ELM_INPUT_HINT_AUTO_COMPLETE)); } EOLIAN static Elm_Input_Panel_Layout _efl_ui_text_input_panel_layout_get(Eo *obj EINA_UNUSED, Efl_Ui_Text_Data *sd) { return sd->input_panel_layout; } EOLIAN static void _efl_ui_text_input_panel_layout_variation_set(Eo *obj EINA_UNUSED, Efl_Ui_Text_Data *sd, int variation) { sd->input_panel_layout_variation = variation; edje_object_part_text_input_panel_layout_variation_set (sd->entry_edje, "elm.text", variation); } EOLIAN static int _efl_ui_text_input_panel_layout_variation_get(Eo *obj EINA_UNUSED, Efl_Ui_Text_Data *sd) { return sd->input_panel_layout_variation; } EOLIAN static void _efl_ui_text_autocapital_type_set(Eo *obj EINA_UNUSED, Efl_Ui_Text_Data *sd, Elm_Autocapital_Type autocapital_type) { sd->autocapital_type = autocapital_type; edje_object_part_text_autocapital_type_set (sd->entry_edje, "elm.text", (Edje_Text_Autocapital_Type)autocapital_type); } EOLIAN static Elm_Autocapital_Type _efl_ui_text_autocapital_type_get(Eo *obj EINA_UNUSED, Efl_Ui_Text_Data *sd) { return sd->autocapital_type; } EOLIAN static void _efl_ui_text_prediction_allow_set(Eo *obj EINA_UNUSED, Efl_Ui_Text_Data *sd, Eina_Bool prediction) { sd->prediction_allow = prediction; edje_object_part_text_prediction_allow_set (sd->entry_edje, "elm.text", prediction); } EOLIAN static Eina_Bool _efl_ui_text_prediction_allow_get(Eo *obj EINA_UNUSED, Efl_Ui_Text_Data *sd) { return sd->prediction_allow; } EOLIAN static void _efl_ui_text_input_hint_set(Eo *obj EINA_UNUSED, Efl_Ui_Text_Data *sd, Elm_Input_Hints hints) { sd->input_hints = hints; edje_object_part_text_input_hint_set (sd->entry_edje, "elm.text", (Edje_Input_Hints)hints); } EOLIAN static Elm_Input_Hints _efl_ui_text_input_hint_get(Eo *obj EINA_UNUSED, Efl_Ui_Text_Data *sd) { return sd->input_hints; } EOLIAN static void _efl_ui_text_input_panel_enabled_set(Eo *obj EINA_UNUSED, Efl_Ui_Text_Data *sd, Eina_Bool enabled) { sd->input_panel_enable = enabled; edje_object_part_text_input_panel_enabled_set (sd->entry_edje, "elm.text", enabled); } EOLIAN static Eina_Bool _efl_ui_text_input_panel_enabled_get(Eo *obj EINA_UNUSED, Efl_Ui_Text_Data *sd) { return sd->input_panel_enable; } EOLIAN static void _efl_ui_text_input_panel_show(Eo *obj EINA_UNUSED, Efl_Ui_Text_Data *sd) { edje_object_part_text_input_panel_show(sd->entry_edje, "elm.text"); } EOLIAN static void _efl_ui_text_input_panel_hide(Eo *obj EINA_UNUSED, Efl_Ui_Text_Data *sd) { edje_object_part_text_input_panel_hide(sd->entry_edje, "elm.text"); } EOLIAN static void _efl_ui_text_input_panel_language_set(Eo *obj EINA_UNUSED, Efl_Ui_Text_Data *sd, Elm_Input_Panel_Lang lang) { sd->input_panel_lang = lang; edje_object_part_text_input_panel_language_set (sd->entry_edje, "elm.text", (Edje_Input_Panel_Lang)lang); } EOLIAN static Elm_Input_Panel_Lang _efl_ui_text_input_panel_language_get(Eo *obj EINA_UNUSED, Efl_Ui_Text_Data *sd) { return sd->input_panel_lang; } EOLIAN static void _efl_ui_text_input_panel_imdata_set(Eo *obj EINA_UNUSED, Efl_Ui_Text_Data *sd, const void *data, int len) { free(sd->input_panel_imdata); sd->input_panel_imdata = calloc(1, len); sd->input_panel_imdata_len = len; memcpy(sd->input_panel_imdata, data, len); edje_object_part_text_input_panel_imdata_set (sd->entry_edje, "elm.text", sd->input_panel_imdata, sd->input_panel_imdata_len); } EOLIAN static void _efl_ui_text_input_panel_imdata_get(const Eo *obj EINA_UNUSED, Efl_Ui_Text_Data *sd, void *data, int *len) { edje_object_part_text_input_panel_imdata_get (sd->entry_edje, "elm.text", data, len); } EOLIAN static void _efl_ui_text_input_panel_return_key_type_set(Eo *obj EINA_UNUSED, Efl_Ui_Text_Data *sd, Elm_Input_Panel_Return_Key_Type return_key_type) { sd->input_panel_return_key_type = return_key_type; edje_object_part_text_input_panel_return_key_type_set (sd->entry_edje, "elm.text", (Edje_Input_Panel_Return_Key_Type)return_key_type); } EOLIAN static Elm_Input_Panel_Return_Key_Type _efl_ui_text_input_panel_return_key_type_get(Eo *obj EINA_UNUSED, Efl_Ui_Text_Data *sd) { return sd->input_panel_return_key_type; } EOLIAN static void _efl_ui_text_input_panel_return_key_disabled_set(Eo *obj EINA_UNUSED, Efl_Ui_Text_Data *sd, Eina_Bool disabled) { sd->input_panel_return_key_disabled = disabled; edje_object_part_text_input_panel_return_key_disabled_set (sd->entry_edje, "elm.text", disabled); } EOLIAN static Eina_Bool _efl_ui_text_input_panel_return_key_disabled_get(Eo *obj EINA_UNUSED, Efl_Ui_Text_Data *sd) { return sd->input_panel_return_key_disabled; } EOLIAN static void _efl_ui_text_input_panel_return_key_autoenabled_set(Eo *obj, Efl_Ui_Text_Data *sd, Eina_Bool enabled) { sd->auto_return_key = enabled; _return_key_enabled_check(obj); } EOLIAN static void _efl_ui_text_input_panel_show_on_demand_set(Eo *obj EINA_UNUSED, Efl_Ui_Text_Data *sd, Eina_Bool ondemand) { sd->input_panel_show_on_demand = ondemand; edje_object_part_text_input_panel_show_on_demand_set (sd->entry_edje, "elm.text", ondemand); } EOLIAN static Eina_Bool _efl_ui_text_input_panel_show_on_demand_get(Eo *obj EINA_UNUSED, Efl_Ui_Text_Data *sd) { return sd->input_panel_show_on_demand; } /* START - ANCHOR HOVER */ static void _anchor_parent_del_cb(void *data, Evas *e EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED) { EFL_UI_TEXT_DATA_GET(data, sd); sd->anchor_hover.hover_parent = NULL; } static void _efl_ui_text_anchor_hover_parent_set(Eo *obj, Efl_Ui_Text_Data *sd, Evas_Object *parent) { if (sd->anchor_hover.hover_parent) evas_object_event_callback_del_full (sd->anchor_hover.hover_parent, EVAS_CALLBACK_DEL, _anchor_parent_del_cb, obj); sd->anchor_hover.hover_parent = parent; if (sd->anchor_hover.hover_parent) evas_object_event_callback_add (sd->anchor_hover.hover_parent, EVAS_CALLBACK_DEL, _anchor_parent_del_cb, obj); } static void _efl_ui_text_anchor_hover_end(Eo *obj EINA_UNUSED, Efl_Ui_Text_Data *sd) { ELM_SAFE_FREE(sd->anchor_hover.hover, evas_object_del); ELM_SAFE_FREE(sd->anchor_hover.pop, evas_object_del); } /* END - ANCHOR HOVER */ EOLIAN static Eina_Bool _efl_ui_text_efl_ui_widget_on_access_activate(Eo *obj, Efl_Ui_Text_Data *_pd EINA_UNUSED, Efl_Ui_Activate act) { if (act != EFL_UI_ACTIVATE_DEFAULT) return EINA_FALSE; EFL_UI_TEXT_DATA_GET(obj, sd); if (!elm_widget_disabled_get(obj) && !evas_object_freeze_events_get(obj)) { efl_event_callback_call(obj, EFL_UI_EVENT_CLICKED, NULL); if (sd->editable && sd->input_panel_enable) edje_object_part_text_input_panel_show(sd->entry_edje, "elm.text"); } return EINA_TRUE; } // ATSPI Accessibility EOLIAN static Eina_Unicode _efl_ui_text_efl_access_text_character_get(Eo *obj, Efl_Ui_Text_Data *_pd EINA_UNUSED, int offset) { const char *txt; int idx = 0; Eina_Unicode ret = 0; if (offset < 0) return ret; if (_pd->password) return ENTRY_PASSWORD_MASK_CHARACTER; txt = efl_text_get(obj); if (!txt) return ret; ret = eina_unicode_utf8_next_get(txt, &idx); while (offset--) ret = eina_unicode_utf8_next_get(txt, &idx); return ret; } EOLIAN static int _efl_ui_text_efl_access_text_character_count_get(Eo *obj, Efl_Ui_Text_Data *_pd EINA_UNUSED) { const char *txt; txt = efl_text_get(obj); if (!txt) return -1; return eina_unicode_utf8_get_len(txt); } EOLIAN static char* _efl_ui_text_efl_access_text_string_get(Eo *obj EINA_UNUSED, Efl_Ui_Text_Data *pd, Efl_Access_Text_Granularity granularity, int *start_offset, int *end_offset) { Evas_Textblock_Cursor *cur = NULL, *cur2 = NULL; char *ret = NULL; Eo *text_obj = edje_object_part_swallow_get(pd->entry_edje, "elm.text"); cur = evas_object_textblock_cursor_new(text_obj); cur2 = evas_object_textblock_cursor_new(text_obj); if (!cur || !cur2) goto fail; evas_textblock_cursor_pos_set(cur, *start_offset); if (evas_textblock_cursor_pos_get(cur) != *start_offset) goto fail; switch (granularity) { case EFL_ACCESS_TEXT_GRANULARITY_CHAR: break; case EFL_ACCESS_TEXT_GRANULARITY_WORD: evas_textblock_cursor_word_start(cur); break; case EFL_ACCESS_TEXT_GRANULARITY_SENTENCE: // TODO - add sentence support in textblock first break; case EFL_ACCESS_TEXT_GRANULARITY_LINE: evas_textblock_cursor_line_char_first(cur); break; case EFL_ACCESS_TEXT_GRANULARITY_PARAGRAPH: evas_textblock_cursor_paragraph_char_first(cur); break; } *start_offset = evas_textblock_cursor_pos_get(cur); evas_textblock_cursor_copy(cur, cur2); switch (granularity) { case EFL_ACCESS_TEXT_GRANULARITY_CHAR: evas_textblock_cursor_char_next(cur2); break; case EFL_ACCESS_TEXT_GRANULARITY_WORD: evas_textblock_cursor_word_end(cur2); // since word_end sets cursor position ON (before) last // char of word, we need to manually advance cursor to get // proper string from function range_text_get evas_textblock_cursor_char_next(cur2); break; case EFL_ACCESS_TEXT_GRANULARITY_SENTENCE: // TODO - add sentence support in textblock first break; case EFL_ACCESS_TEXT_GRANULARITY_LINE: evas_textblock_cursor_line_char_last(cur2); break; case EFL_ACCESS_TEXT_GRANULARITY_PARAGRAPH: evas_textblock_cursor_paragraph_char_last(cur2); break; } if (end_offset) *end_offset = evas_textblock_cursor_pos_get(cur2); ret = evas_textblock_cursor_range_text_get(cur, cur2, EVAS_TEXTBLOCK_TEXT_PLAIN); evas_textblock_cursor_free(cur); evas_textblock_cursor_free(cur2); if (ret && pd->password) { int i = 0; while (ret[i] != '\0') ret[i++] = ENTRY_PASSWORD_MASK_CHARACTER; } return ret; fail: if (start_offset) *start_offset = -1; if (end_offset) *end_offset = -1; if (cur) evas_textblock_cursor_free(cur); if (cur2) evas_textblock_cursor_free(cur2); return NULL; } EOLIAN static char* _efl_ui_text_efl_access_text_text_get(Eo *obj EINA_UNUSED, Efl_Ui_Text_Data *pd EINA_UNUSED, int start_offset, int end_offset) { Evas_Textblock_Cursor *cur = NULL, *cur2 = NULL; char *ret = NULL; Eo *text_obj = edje_object_part_swallow_get(pd->entry_edje, "elm.text"); cur = evas_object_textblock_cursor_new(text_obj); cur2 = evas_object_textblock_cursor_new(text_obj); if (!cur || !cur2) goto fail; evas_textblock_cursor_pos_set(cur, start_offset); if (evas_textblock_cursor_pos_get(cur) != start_offset) goto fail; evas_textblock_cursor_pos_set(cur2, end_offset); if (evas_textblock_cursor_pos_get(cur2) != end_offset) goto fail; ret = evas_textblock_cursor_range_text_get(cur, cur2, EVAS_TEXTBLOCK_TEXT_PLAIN); evas_textblock_cursor_free(cur); evas_textblock_cursor_free(cur2); if (ret && pd->password) { int i = 0; while (ret[i] != '\0') ret[i++] = ENTRY_PASSWORD_MASK_CHARACTER; } return ret; fail: if (cur) evas_textblock_cursor_free(cur); if (cur2) evas_textblock_cursor_free(cur2); return NULL; } EOLIAN static int _efl_ui_text_efl_access_text_caret_offset_get(Eo *obj, Efl_Ui_Text_Data *_pd EINA_UNUSED) { return efl_text_cursor_position_get(obj, efl_text_cursor_get(obj, EFL_TEXT_CURSOR_GET_MAIN)); } EOLIAN static Eina_Bool _efl_ui_text_efl_access_text_caret_offset_set(Eo *obj, Efl_Ui_Text_Data *_pd EINA_UNUSED, int offset) { efl_text_cursor_position_set(obj, efl_text_cursor_get(obj, EFL_TEXT_CURSOR_GET_MAIN), offset); return EINA_TRUE; } EOLIAN static int _efl_ui_text_efl_access_text_selections_count_get(Eo *obj, Efl_Ui_Text_Data *_pd EINA_UNUSED) { return _efl_ui_text_selection_get(obj, _pd) ? 1 : 0; } EOLIAN static void _efl_ui_text_efl_access_text_selection_get(Eo *obj, Efl_Ui_Text_Data *_pd EINA_UNUSED, int selection_number, int *start_offset, int *end_offset) { if (selection_number != 0) return; elm_obj_entry_select_region_get(obj, start_offset, end_offset); } EOLIAN static Eina_Bool _efl_ui_text_efl_access_text_selection_set(Eo *obj, Efl_Ui_Text_Data *_pd EINA_UNUSED, int selection_number, int start_offset, int end_offset) { if (selection_number != 0) return EINA_FALSE; _efl_ui_text_select_region_set(obj, _pd, start_offset, end_offset); return EINA_TRUE; } EOLIAN static Eina_Bool _efl_ui_text_efl_access_text_selection_remove(Eo *obj, Efl_Ui_Text_Data *pd EINA_UNUSED, int selection_number) { if (selection_number != 0) return EINA_FALSE; _efl_ui_text_select_none(obj, pd); return EINA_TRUE; } EOLIAN static Eina_Bool _efl_ui_text_efl_access_text_selection_add(Eo *obj, Efl_Ui_Text_Data *pd EINA_UNUSED, int start_offset, int end_offset) { _efl_ui_text_select_region_set(obj, pd, start_offset, end_offset); return EINA_TRUE; } EOLIAN static Eina_List* _efl_ui_text_efl_access_text_bounded_ranges_get(Eo *obj EINA_UNUSED, Efl_Ui_Text_Data *_pd EINA_UNUSED, Eina_Bool screen_coods EINA_UNUSED, Eina_Rect rect EINA_UNUSED, Efl_Access_Text_Clip_Type xclip EINA_UNUSED, Efl_Access_Text_Clip_Type yclip EINA_UNUSED) { return NULL; } EOLIAN static int _efl_ui_text_efl_access_text_offset_at_point_get(Eo *obj, Efl_Ui_Text_Data *pd EINA_UNUSED, Eina_Bool screen_coods, int x, int y) { Evas_Textblock_Cursor *cur; int ret; Eo *text_obj = edje_object_part_swallow_get(pd->entry_edje, "elm.text"); if (!text_obj) return -1; cur = evas_object_textblock_cursor_new(text_obj); if (!cur) return -1; if (screen_coods) { int ee_x, ee_y; Ecore_Evas *ee= ecore_evas_ecore_evas_get(evas_object_evas_get(obj)); ecore_evas_geometry_get(ee, &ee_x, &ee_y, NULL, NULL); x -= ee_x; y -= ee_y; } if (!evas_textblock_cursor_char_coord_set(cur, x, y)) { evas_textblock_cursor_free(cur); return -1; } ret = evas_textblock_cursor_pos_get(cur); evas_textblock_cursor_free(cur); return ret; } EOLIAN static Eina_Bool _efl_ui_text_efl_access_text_character_extents_get(Eo *obj, Efl_Ui_Text_Data *pd EINA_UNUSED, int offset, Eina_Bool screen_coods, Eina_Rect *rect) { Evas_Textblock_Cursor *cur; int ret; Eo *text_obj = edje_object_part_swallow_get(pd->entry_edje, "elm.text"); if (!text_obj) return EINA_FALSE; cur = evas_object_textblock_cursor_new(text_obj); if (!cur) return EINA_FALSE; evas_textblock_cursor_pos_set(cur, offset); ret = evas_textblock_cursor_char_geometry_get(cur, &rect->x, &rect->y, &rect->w, &rect->h); evas_textblock_cursor_free(cur); if (ret == -1) return EINA_FALSE; if (screen_coods) { int ee_x, ee_y; Ecore_Evas *ee= ecore_evas_ecore_evas_get(evas_object_evas_get(obj)); ecore_evas_geometry_get(ee, &ee_x, &ee_y, NULL, NULL); rect->x += ee_x; rect->y += ee_y; } return EINA_TRUE; } EOLIAN static Eina_Bool _efl_ui_text_efl_access_text_range_extents_get(Eo *obj, Efl_Ui_Text_Data *pd EINA_UNUSED, Eina_Bool screen_coods, int start_offset, int end_offset, Eina_Rect *rect) { Evas_Textblock_Cursor *cur1, *cur2; int ret; int x, xx, y, yy; Eo *text_obj = edje_object_part_swallow_get(pd->entry_edje, "elm.text"); if (!text_obj) return EINA_FALSE; cur1 = evas_object_textblock_cursor_new(text_obj); if (!cur1) return EINA_FALSE; cur2 = evas_object_textblock_cursor_new(text_obj); if (!cur2) { evas_textblock_cursor_free(cur1); return EINA_FALSE; } evas_textblock_cursor_pos_set(cur1, start_offset); evas_textblock_cursor_pos_set(cur2, end_offset); ret = evas_textblock_cursor_char_geometry_get(cur1, &x, &y, NULL, NULL); ret += evas_textblock_cursor_char_geometry_get(cur2, &xx, &yy, NULL, NULL); evas_textblock_cursor_free(cur1); evas_textblock_cursor_free(cur2); if (ret != 0) return EINA_FALSE; rect->x = x < xx ? x : xx; rect->y = y < yy ? y : yy; rect->w = abs(x - xx); rect->h = abs(y - yy); if (screen_coods) { int ee_x, ee_y; Ecore_Evas *ee= ecore_evas_ecore_evas_get(evas_object_evas_get(obj)); ecore_evas_geometry_get(ee, &ee_x, &ee_y, NULL, NULL); rect->x += ee_x; rect->y += ee_y; } return EINA_TRUE; } static Efl_Access_Text_Attribute* _textblock_node_format_to_atspi_text_attr(Eo *obj, Efl_Text_Annotate_Annotation *annotation) { Efl_Access_Text_Attribute *ret; const char *txt; txt = efl_text_annotation_get(obj, annotation); if (!txt) return NULL; ret = calloc(1, sizeof(Efl_Access_Text_Attribute)); if (!ret) return NULL; ret->value = eina_stringshare_add(txt); int size = strlen(txt); ret->name = eina_stringshare_add_length(txt, size); return ret; } EOLIAN static Eina_Bool _efl_ui_text_efl_access_text_attribute_get(Eo *obj, Efl_Ui_Text_Data *_pd EINA_UNUSED, const char *attr_name EINA_UNUSED, int *start_offset, int *end_offset, char **value) { Evas_Textblock_Cursor *cur1, *cur2; Efl_Access_Text_Attribute *attr; Eina_Iterator *annotations; Efl_Text_Annotate_Annotation *an; cur1 = evas_object_textblock_cursor_new(obj); if (!cur1) return EINA_FALSE; cur2 = evas_object_textblock_cursor_new(obj); if (!cur2) { evas_textblock_cursor_free(cur1); return EINA_FALSE; } evas_textblock_cursor_pos_set(cur1, *start_offset); evas_textblock_cursor_pos_set(cur2, *end_offset); annotations = efl_text_range_annotations_get(obj, cur1, cur2); evas_textblock_cursor_free(cur1); evas_textblock_cursor_free(cur2); if (!annotations) return EINA_FALSE; EINA_ITERATOR_FOREACH(annotations, an) { attr = _textblock_node_format_to_atspi_text_attr(obj, an); if (!attr) continue; if (!strcmp(attr->name, attr_name)) { *value = attr->value ? strdup(attr->value) : NULL; elm_atspi_text_text_attribute_free(attr); return EINA_TRUE; } elm_atspi_text_text_attribute_free(attr); } eina_iterator_free(annotations); return EINA_FALSE; } EOLIAN static Eina_List* _efl_ui_text_efl_access_text_attributes_get(Eo *obj, Efl_Ui_Text_Data *pd EINA_UNUSED, int *start_offset, int *end_offset) { Evas_Textblock_Cursor *cur1, *cur2; Eina_List *ret = NULL; Efl_Access_Text_Attribute *attr; Eina_Iterator *annotations; Efl_Text_Annotate_Annotation *an; cur1 = evas_object_textblock_cursor_new(obj); if (!cur1) return NULL; cur2 = evas_object_textblock_cursor_new(obj); if (!cur2) { evas_textblock_cursor_free(cur1); return NULL; } evas_textblock_cursor_pos_set(cur1, *start_offset); evas_textblock_cursor_pos_set(cur2, *end_offset); annotations = efl_text_range_annotations_get(obj, cur1, cur2); evas_textblock_cursor_free(cur1); evas_textblock_cursor_free(cur2); if (!annotations) return NULL; EINA_ITERATOR_FOREACH(annotations, an) { attr = _textblock_node_format_to_atspi_text_attr(obj, an); if (!attr) continue; ret = eina_list_append(ret, attr); } eina_iterator_free(annotations); return ret; } EOLIAN static Eina_List* _efl_ui_text_efl_access_text_default_attributes_get(Eo *obj, Efl_Ui_Text_Data *_pd EINA_UNUSED) { Eina_List *ret = NULL; Efl_Access_Text_Attribute *attr; Efl_Text_Cursor_Cursor *start, *end; Eina_Iterator *annotations; Efl_Text_Annotate_Annotation *an; /* Retrieve all annotations in the text. */ start = efl_text_cursor_new(obj); end = efl_text_cursor_new(obj); efl_text_cursor_paragraph_first(obj, start); efl_text_cursor_paragraph_last(obj, end); annotations = efl_text_range_annotations_get(obj, start, end); EINA_ITERATOR_FOREACH(annotations, an) { attr = _textblock_node_format_to_atspi_text_attr(obj, an); if (!attr) continue; ret = eina_list_append(ret, attr); } eina_iterator_free(annotations); return ret; } EOLIAN static Eina_Bool _efl_ui_text_efl_access_editable_text_content_set(Eo *obj, Efl_Ui_Text_Data *pd EINA_UNUSED, const char *content) { efl_text_set(obj, content); return EINA_TRUE; } EOLIAN static Eina_Bool _efl_ui_text_efl_access_editable_text_insert(Eo *obj, Efl_Ui_Text_Data *pd, const char *string, int position) { Efl_Text_Cursor_Cursor *cur_obj = efl_text_cursor_get(obj, EFL_TEXT_CURSOR_GET_MAIN); efl_text_cursor_position_set(obj, cur_obj, position); _efl_ui_text_entry_insert(obj, pd, string); return EINA_TRUE; } EOLIAN static Eina_Bool _efl_ui_text_efl_access_editable_text_copy(Eo *obj, Efl_Ui_Text_Data *pd, int start, int end) { _efl_ui_text_select_region_set(obj, pd, start, end); efl_ui_text_selection_copy(obj); return EINA_TRUE; } EOLIAN static Eina_Bool _efl_ui_text_efl_access_editable_text_delete(Eo *obj, Efl_Ui_Text_Data *pd, int start_offset, int end_offset) { Evas_Textblock_Cursor *cur1, *cur2; Eo *text_obj = edje_object_part_swallow_get(pd->entry_edje, "elm.text"); if (!text_obj) return EINA_FALSE; cur1 = evas_object_textblock_cursor_new(text_obj); if (!cur1) return EINA_FALSE; cur2 = evas_object_textblock_cursor_new(text_obj); if (!cur2) { evas_textblock_cursor_free(cur1); return EINA_FALSE; } evas_textblock_cursor_pos_set(cur1, start_offset); evas_textblock_cursor_pos_set(cur2, end_offset); evas_textblock_cursor_range_delete(cur1, cur2); evas_textblock_cursor_free(cur1); evas_textblock_cursor_free(cur2); _efl_ui_text_calc_force(obj, pd); return EINA_TRUE; } EOLIAN static Eina_Bool _efl_ui_text_efl_access_editable_text_paste(Eo *obj, Efl_Ui_Text_Data *_pd EINA_UNUSED, int position) { Efl_Text_Cursor_Cursor *cur_obj = efl_text_cursor_get(obj, EFL_TEXT_CURSOR_GET_MAIN); efl_text_cursor_position_set(obj, cur_obj, position); efl_ui_text_selection_paste(obj); return EINA_TRUE; } EOLIAN static Eina_Bool _efl_ui_text_efl_access_editable_text_cut(Eo *obj, Efl_Ui_Text_Data *pd EINA_UNUSED, int start, int end) { _efl_ui_text_select_region_set(obj, pd, start, end); efl_ui_text_selection_cut(obj); return EINA_TRUE; } EOLIAN static Efl_Access_State_Set _efl_ui_text_efl_access_state_set_get(Eo *obj, Efl_Ui_Text_Data *_pd EINA_UNUSED) { Efl_Access_State_Set ret; ret = efl_access_state_set_get(efl_super(obj, EFL_UI_TEXT_CLASS)); if (efl_ui_text_interactive_editable_get(obj)) STATE_TYPE_SET(ret, EFL_ACCESS_STATE_EDITABLE); return ret; } EOLIAN static const char* _efl_ui_text_efl_access_name_get(Eo *obj, Efl_Ui_Text_Data *pd) { const char *name; name = efl_access_name_get(efl_super(obj, EFL_UI_TEXT_CLASS)); if (name && strncmp("", name, 1)) return name; const char *ret = edje_object_part_text_get(pd->entry_edje, "elm.guide"); return ret; } EOLIAN static Efl_Text_Cursor_Cursor * _efl_ui_text_cursor_new(Eo *obj EINA_UNUSED, Efl_Ui_Text_Data *sd EINA_UNUSED) { Eo *text_obj = edje_object_part_swallow_get(sd->entry_edje, "elm.text"); return efl_text_cursor_new(text_obj); } static void _edje_signal_emit(Efl_Ui_Text_Data *sd, const char *sig, const char *src) { edje_object_signal_emit(sd->entry_edje, sig, src); edje_object_signal_emit(sd->cursor, sig, src); edje_object_signal_emit(sd->cursor_bidi, sig, src); } static inline Eo * _decoration_create(Eo *obj, Efl_Ui_Text_Data *sd, const char *group_name, Eina_Bool above) { Eo *ret = efl_add(EFL_CANVAS_LAYOUT_CLASS, sd->entry_edje); Eo *text_obj = edje_object_part_swallow_get(sd->entry_edje, "elm.text"); elm_widget_element_update(obj, ret, group_name); evas_object_smart_member_add(ret, sd->entry_edje); if (above) { evas_object_stack_above(ret, text_obj); } else { evas_object_stack_below(ret, text_obj); } evas_object_clip_set(ret, evas_object_clip_get(text_obj)); evas_object_pass_events_set(ret, EINA_TRUE); return ret; } /** * Creates the cursors, if not created. */ static void _create_text_cursors(Eo *obj, Efl_Ui_Text_Data *sd) { sd->cursor = _decoration_create(obj, sd, PART_NAME_CURSOR, EINA_TRUE); sd->cursor_bidi = _decoration_create(obj, sd, PART_NAME_CURSOR, EINA_TRUE); if (!efl_ui_text_interactive_editable_get(obj)) { evas_object_hide(sd->cursor); evas_object_hide(sd->cursor_bidi); } } static void _decoration_calc_offset(Efl_Ui_Text_Data *sd, Evas_Coord *_x, Evas_Coord *_y) { Evas_Coord x, y; Eo *text_obj = edje_object_part_swallow_get(sd->entry_edje, "elm.text"); evas_object_geometry_get(text_obj, &x, &y, NULL, NULL); if (_x) *_x = x; if (_y) *_y = y; } static void _update_text_cursors(Eo *obj) { Evas_Coord x, y, w, h, xx, yy, ww, hh; Evas_Coord xx2, yy2; Eina_Bool bidi_cursor; Eo *text_obj; EFL_UI_TEXT_DATA_GET(obj, sd); if (!sd->deferred_decoration_cursor) return; sd->deferred_decoration_cursor = EINA_FALSE; text_obj = edje_object_part_swallow_get(sd->entry_edje, "elm.text"); x = y = w = h = -1; xx = yy = ww = hh = -1; _decoration_calc_offset(sd, &x, &y); bidi_cursor = efl_text_cursor_geometry_get(obj, efl_text_cursor_get(text_obj, EFL_TEXT_CURSOR_GET_MAIN), EFL_TEXT_CURSOR_TYPE_BEFORE, &xx, &yy, &ww, &hh, &xx2, &yy2, NULL, NULL); if (ww < 1) ww = 1; if (hh < 1) hh = 1; if (sd->cursor) { evas_object_move(sd->cursor, x + xx, y + yy); evas_object_resize(sd->cursor, ww, hh); } if (sd->cursor_bidi) { if (bidi_cursor) { evas_object_move(sd->cursor_bidi, x + xx2, y + yy2 + (hh / 2)); evas_object_resize(sd->cursor, ww, hh / 2); evas_object_resize(sd->cursor_bidi, ww, hh / 2); evas_object_show(sd->cursor_bidi); } else { evas_object_hide(sd->cursor_bidi); } } _cursor_geometry_recalc(obj); } static void _clear_text_selection(Efl_Ui_Text_Data *sd) { Efl_Ui_Text_Rectangle *r; EINA_LIST_FREE(sd->sel, r) { free(r); } } static void _update_text_selection(Eo *obj, Eo *text_obj) { Evas_Coord x, y; Efl_Text_Cursor_Cursor *sel_start, *sel_end; Eina_List *l; Eina_Iterator *range; Efl_Ui_Text_Rectangle *rect; Eina_Rectangle *r; EFL_UI_TEXT_DATA_GET(obj, sd); if (!sd->deferred_decoration_selection) return; sd->deferred_decoration_selection = EINA_FALSE; _decoration_calc_offset(sd, &x, &y); efl_ui_text_interactive_selection_cursors_get(text_obj, &sel_start, &sel_end); range = efl_canvas_text_range_simple_geometry_get(text_obj, sel_start, sel_end); l = sd->sel; EINA_ITERATOR_FOREACH(range, r) { /* Create if there isn't a rectangle to populate. */ if (!l) { rect = calloc(1, sizeof(Efl_Ui_Text_Rectangle)); sd->sel = eina_list_append(sd->sel, rect); rect->obj_bg = _decoration_create(obj, sd, PART_NAME_SELECTION, EINA_FALSE); evas_object_show(rect->obj_bg); } else { rect = eina_list_data_get(l); l = l->next; } if (rect->obj_bg) { evas_object_move(rect->obj_bg, x + r->x, y + r->y); evas_object_resize(rect->obj_bg, r->w, r->h); } } eina_iterator_free(range); /* delete redundant rectection rects */ while (l) { Eina_List *temp = l->next; rect = eina_list_data_get(l); if (rect) { if (rect->obj_bg) efl_del(rect->obj_bg); free(rect); } sd->sel = eina_list_remove_list(sd->sel, l); l = temp; } /* Update selection handlers */ _update_selection_handler(obj); } static void _item_obj_del_cb(void *data, Evas *e EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED) { Item_Obj *io = data; Anchor *an = io->an; if (!an) { ERR("Failed to free item object struct. Anchor is NULL!"); return; } EFL_UI_TEXT_DATA_GET(an->obj, sd); sd->item_objs = (Item_Obj *)eina_inlist_remove(EINA_INLIST_GET(sd->item_objs), EINA_INLIST_GET(io)); io->an = NULL; free(io->name); free(io); } static Evas_Object * _item_obj_get(Anchor *an, Evas_Object *o, Evas_Object *smart, Evas_Object *clip) { Item_Obj *io; Eo *obj = an->obj; Evas_Object *item_obj; if (!an->name) return NULL; EFL_UI_TEXT_DATA_GET(obj, sd); EINA_INLIST_FOREACH(sd->item_objs, io) { if (!io->an && io->name && !strcmp(an->name, io->name)) { io->an = an; return io->obj; } } io = calloc(1, sizeof(Item_Obj)); item_obj = _item_get(obj, an->name); evas_object_event_callback_add(item_obj, EVAS_CALLBACK_DEL, _item_obj_del_cb, io); evas_object_smart_member_add(item_obj, smart); evas_object_stack_above(item_obj, o); evas_object_clip_set(item_obj, clip); evas_object_pass_events_set(item_obj, EINA_TRUE); evas_object_show(item_obj); io->an = an; io->name = strdup(an->name); io->obj = item_obj; sd->item_objs = (Item_Obj *)eina_inlist_append(EINA_INLIST_GET(sd->item_objs), EINA_INLIST_GET(io)); return io->obj; } static void _unused_item_objs_free(Efl_Ui_Text_Data *sd) { Item_Obj *io; Eina_Inlist *l; EINA_INLIST_FOREACH_SAFE(sd->item_objs, l, io) { if (!io->an) { if (io->obj) { evas_object_event_callback_del_full(io->obj, EVAS_CALLBACK_DEL, _item_obj_del_cb, io); evas_object_del(io->obj); } sd->item_objs = (Item_Obj *)eina_inlist_remove(EINA_INLIST_GET(sd->item_objs), EINA_INLIST_GET(io)); free(io->name); free(io); } } } static void _anchors_clear(Eina_List **_list) { Eina_List *list = *_list; while (list) { Anchor *an = list->data; while (an->sel) { Efl_Ui_Text_Rectangle *sel = an->sel->data; if (sel->obj_bg) evas_object_del(sel->obj_bg); if (sel->obj_fg) evas_object_del(sel->obj_fg); if (!an->item && sel->obj) evas_object_del(sel->obj); free(sel); an->sel = eina_list_remove_list(an->sel, an->sel); } free(an->name); free(an); list = eina_list_remove_list(list, list); } *_list = list; } static void _anchors_clear_all(Evas_Object *o EINA_UNUSED, Efl_Ui_Text_Data *sd) { Item_Obj *io; _anchors_clear(&sd->anchors); _anchors_clear(&sd->item_anchors); /* Detach anchors from cached objects */ EINA_INLIST_FOREACH(sd->item_objs, io) io->an = NULL; } static char * _anchor_format_parse(const char *item) { const char *start, *end; char *tmp; size_t len; start = strchr(item, '='); if (!start) return NULL; start++; /* Advance after the '=' */ /* If we can find a quote as the first non-space char, * our new delimiter is a quote, not a space. */ while (*start == ' ') start++; if (*start == '\'') { start++; end = strchr(start, '\''); while ((end) && (end > start) && (end[-1] == '\\')) end = strchr(end + 1, '\''); } else { end = strchr(start, ' '); while ((end) && (end > start) && (end[-1] == '\\')) end = strchr(end + 1, ' '); } /* Null terminate before the spaces */ if (end) len = end - start; else len = strlen(start); tmp = malloc(len + 1); strncpy(tmp, start, len); tmp[len] = '\0'; return tmp; } /* Recreates the anchors in the text. */ static void _anchors_create(Eo *obj, Efl_Ui_Text_Data *sd) { Eina_Iterator *it; Anchor *an = NULL; Efl_Text_Cursor_Cursor *start, *end; Efl_Text_Annotate_Annotation *anchor; Eo *text_obj = edje_object_part_swallow_get(sd->entry_edje, "elm.text"); _anchors_clear_all(obj, sd); start = efl_text_cursor_new(text_obj); end = efl_text_cursor_new(text_obj); /* Retrieve all annotations in the text. */ efl_text_cursor_paragraph_first(obj, start); efl_text_cursor_paragraph_last(obj, end); it = efl_text_range_annotations_get(obj, start, end); efl_text_cursor_free(text_obj, start); efl_text_cursor_free(text_obj, end); EINA_ITERATOR_FOREACH(it, anchor) { Eina_Bool is_anchor = EINA_FALSE; Eina_Bool is_item = EINA_FALSE; if (efl_text_annotation_is_item(obj, anchor)) { is_anchor = EINA_TRUE; is_item = EINA_TRUE; } else if (!strncmp(efl_text_annotation_get(obj, anchor), "a ", 2)) { is_anchor = EINA_TRUE; } if (is_anchor) { const char *p; const char *item_str = efl_text_annotation_get(obj, anchor); an = calloc(1, sizeof(Anchor)); if (!an) break; an->obj = obj; an->annotation = anchor; an->item = is_item; p = strstr(item_str, "href="); if (p) { an->name = _anchor_format_parse(p); } sd->anchors = eina_list_append(sd->anchors, an); } } eina_iterator_free(it); } #if 0 static Eina_Bool _is_anchors_outside_viewport(Evas_Coord oxy, Evas_Coord axy, Evas_Coord awh, Evas_Coord vxy, Evas_Coord vwh) { if (((oxy + axy + awh) < vxy) || ((oxy + axy) > vwh)) { return EINA_TRUE; } return EINA_FALSE; } #endif static void _text_anchor_mouse_down_cb(void *data, Evas *e EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED) { EFL_UI_TEXT_DATA_GET(obj, pd); Anchor *an = data; Elm_Entry_Anchor_Info ei; ei.x = ei.y = ei.w = ei.h = 0; ei.name = an->name; evas_object_geometry_get(obj, &ei.x, &ei.y, &ei.w, &ei.h); if (!pd->disabled) efl_event_callback_call(an->obj, EFL_UI_TEXT_EVENT_ANCHOR_DOWN, &ei); } static void _text_anchor_mouse_up_cb(void *data, Evas *e EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED) { EFL_UI_TEXT_DATA_GET(obj, pd); Elm_Entry_Anchor_Info ei; Anchor *an = data; ei.x = ei.y = ei.w = ei.h = 0; evas_object_geometry_get(obj, &ei.x, &ei.y, &ei.w, &ei.h); ei.name = an->name; _entry_hover_anchor_clicked_do(an->obj, &ei); if (!pd->disabled) efl_event_callback_call(an->obj, EFL_UI_TEXT_EVENT_ANCHOR_UP, &ei); } static void _anchors_update(Eo *o, Efl_Ui_Text_Data *sd) { Eina_List *l, *ll, *rl; Evas_Coord x, y; Evas_Object *smart, *clip; Efl_Ui_Text_Rectangle *rect; Anchor *an; Eo *sw; sw = edje_object_part_swallow_get(sd->entry_edje, "elm.text"); if (!sd->deferred_decoration_anchor) return; sd->deferred_decoration_anchor = EINA_FALSE; /* Better not to update anchors outside the view port. */ if (sd->anchors_updated) return; efl_event_freeze(sw); _anchors_create(o, sd); smart = evas_object_smart_parent_get(o); clip = evas_object_clip_get( edje_object_part_swallow_get(sd->entry_edje, "elm.text")); _decoration_calc_offset(sd, &x, &y); EINA_LIST_FOREACH(sd->anchors, l, an) { // for item anchors if (an->item) { Evas_Object *ob; Evas_Coord cx, cy, cw, ch; if (!an->sel) { rect = calloc(1, sizeof(Efl_Ui_Text_Rectangle)); an->sel = eina_list_append(an->sel, rect); ob = _item_obj_get(an, o, smart, clip); if (ob) { rect->obj = ob; efl_text_item_geometry_get(an->obj, an->annotation, &cx, &cy, &cw, &ch); evas_object_move(rect->obj, x + cx, y + cy); evas_object_resize(rect->obj, cw, ch); } } } // for link anchors else { Eina_Iterator *range; Efl_Text_Cursor_Cursor *start, *end; Eina_List *range_list; Eina_Rectangle *r; start = efl_text_cursor_new(o); end = efl_text_cursor_new(o); efl_text_annotation_positions_get(o, an->annotation, start, end); range = efl_canvas_text_range_geometry_get(o, start, end); range_list = eina_iterator_container_get(range); if (eina_list_count(range_list) != eina_list_count(an->sel)) { while (an->sel) { rect = an->sel->data; if (rect->obj_bg) evas_object_del(rect->obj_bg); if (rect->obj) evas_object_del(rect->obj); free(rect); an->sel = eina_list_remove_list(an->sel, an->sel); } r = range_list->data; #if 0 Eina_Rectangle *r_last; r_last = eina_list_last_data_get(range_list); if (r->y != r_last->y) { /* For multiple range */ r->h = r->y + r_last->y + r_last->h; } #endif /* For vertically layout entry */ #if 0 if (_is_anchors_outside_viewport(y, r->y, r->h, vy, tvh)) { EINA_LIST_FREE(range, r) free(r); continue; } else { /* XXX: Should consider for horizontal entry but has * very minimal usage. Probably we should get the min x * and max w for range and then decide whether it is in * the viewport or not. Unnecessary calculation for this * minimal usage. Please test with large number of anchors * after implementing it, if its needed to be. */ } #endif /* XXX: the iterator isn't powerful enought to iterate more * than once on the list. We have to resort to this workaround * since for this optimization port to work, we need to * have another go on the list. */ EINA_LIST_FOREACH(range_list, ll, r) { Evas_Object *ob; rect = calloc(1, sizeof(Efl_Ui_Text_Rectangle)); an->sel = eina_list_append(an->sel, rect); ob = _decoration_create(o, sd, PART_NAME_ANCHOR, EINA_TRUE); rect->obj_fg = ob; /* Create hit rectangle to catch events */ ob = evas_object_rectangle_add(o); evas_object_color_set(ob, 0, 0, 0, 0); evas_object_smart_member_add(ob, smart); evas_object_stack_above(ob, o); evas_object_clip_set(ob, clip); evas_object_repeat_events_set(ob, EINA_TRUE); rect->obj = ob; evas_object_event_callback_add(ob, EVAS_CALLBACK_MOUSE_DOWN, _text_anchor_mouse_down_cb, an); evas_object_event_callback_add(ob, EVAS_CALLBACK_MOUSE_UP, _text_anchor_mouse_up_cb, an); #if 0 evas_object_event_callback_add(ob, EVAS_CALLBACK_MOUSE_MOVE, _text_anchor_mouse_move_cb, an); evas_object_event_callback_add(ob, EVAS_CALLBACK_MOUSE_IN, _text_anchor_mouse_in_cb, an); evas_object_event_callback_add(ob, EVAS_CALLBACK_MOUSE_OUT, _text_anchor_mouse_out_cb, an); #endif } } ll = an->sel; EINA_LIST_FOREACH(range_list, rl, r) { rect = ll->data; #if 0 if (_is_anchors_outside_viewport(y, r->y, r->h, vy, tvh) || _is_anchors_outside_viewport(x, r->x, r->w, vx, tvw)) { range = eina_list_remove_list(range, range); free(r); evas_object_hide(sel->obj_bg); evas_object_hide(sel->obj_fg); evas_object_hide(sel->obj); continue; } #endif if (rect->obj_bg) { evas_object_move(rect->obj_bg, x + r->x, y + r->y); evas_object_resize(rect->obj_bg, r->w, r->h); evas_object_show(rect->obj_bg); } if (rect->obj_fg) { evas_object_move(rect->obj_fg, x + r->x, y + r->y); evas_object_resize(rect->obj_fg, r->w, r->h); evas_object_show(rect->obj_fg); } if (rect->obj) { evas_object_move(rect->obj, x + r->x, y + r->y); evas_object_resize(rect->obj, r->w, r->h); evas_object_show(rect->obj); } ll = ll->next; } eina_iterator_free(range); } } _unused_item_objs_free(sd); efl_event_thaw(sw); } static void _update_decorations(Eo *obj) { EFL_UI_TEXT_DATA_GET(obj, sd); Eo *text_obj = edje_object_part_swallow_get(sd->entry_edje, "elm.text"); _update_text_cursors(obj); _update_text_selection(obj, text_obj); _anchors_update(obj, sd); //_update_text_hover(obj, sd); } static void _deferred_decoration_job(void *data) { EFL_UI_TEXT_DATA_GET(data, sd); _update_decorations(data); sd->deferred_decoration_job = NULL; } static void _decoration_defer(Eo *obj) { EFL_UI_TEXT_DATA_GET(obj, sd); if (!sd->deferred_decoration_job) { sd->deferred_decoration_job = ecore_job_add(_deferred_decoration_job, obj); } } static void _decoration_defer_all(Eo *obj) { EFL_UI_TEXT_DATA_GET(obj, sd); sd->deferred_decoration_anchor = EINA_TRUE; sd->deferred_decoration_cursor = EINA_TRUE; sd->deferred_decoration_selection = EINA_TRUE; _decoration_defer(obj); } static void _efl_ui_text_changed_cb(void *data, const Efl_Event *event EINA_UNUSED) { _decoration_defer_all(data); _entry_changed_handle(data, EFL_UI_TEXT_EVENT_CHANGED, NULL); } static void _efl_ui_text_changed_user_cb(void *data, const Efl_Event *event) { _decoration_defer_all(data); _entry_changed_handle(data, EFL_UI_TEXT_EVENT_CHANGED_USER, event->info); } static void _efl_ui_text_cursor_changed_cb(void *data, const Efl_Event *event EINA_UNUSED) { EFL_UI_TEXT_DATA_GET(data, sd); sd->cur_changed = EINA_TRUE; sd->deferred_decoration_cursor = EINA_TRUE; _decoration_defer(data); } static void _efl_ui_text_selection_changed_cb(void *data, const Efl_Event *event EINA_UNUSED) { Eo *obj = data; Efl_Text_Cursor_Cursor *start, *end; char *text; EFL_UI_TEXT_DATA_GET(obj, sd); efl_ui_text_interactive_selection_cursors_get(obj, &start, &end); text = efl_canvas_text_range_text_get(obj, start, end); if (!text || (text[0] == '\0')) { _edje_signal_emit(sd, "selection,cleared", "elm.text"); sd->have_selection = EINA_FALSE; } else { if (!sd->have_selection) { _edje_signal_emit(sd, "selection,start", "elm.text"); } _edje_signal_emit(sd, "selection,changed", "elm.text"); sd->have_selection = EINA_TRUE; } if (text) free(text); sd->deferred_decoration_selection = EINA_TRUE; _decoration_defer(data); } static void _efl_ui_text_move_cb(void *data, Evas *e EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED) { _decoration_defer_all(data); } static void _efl_ui_text_item_factory_set(Eo *obj EINA_UNUSED, Efl_Ui_Text_Data *pd, Efl_Canvas_Text_Factory *item_factory) { if (pd->item_factory) efl_unref(pd->item_factory); pd->item_factory = efl_ref(item_factory); } static Eo * _efl_ui_text_item_factory_get(Eo *obj EINA_UNUSED, Efl_Ui_Text_Data *pd) { return pd->item_factory; } #if 0 /* Efl.Part begin */ ELM_PART_OVERRIDE(elm_entry, EFL_UI_TEXT, Efl_Ui_Text_Data) ELM_PART_OVERRIDE_CONTENT_SET(elm_entry, EFL_UI_TEXT, Efl_Ui_Text_Data) ELM_PART_OVERRIDE_CONTENT_UNSET(elm_entry, EFL_UI_TEXT, Efl_Ui_Text_Data) #include "elm_entry_part.eo.c" /* Efl.Part end */ #endif /* Internal EO APIs and hidden overrides */ //ELM_LAYOUT_CONTENT_ALIASES_IMPLEMENT(MY_CLASS_PFX) #define EFL_UI_TEXT_EXTRA_OPS \ ELM_LAYOUT_SIZING_EVAL_OPS(efl_ui_text), \ #include "efl_ui_text.eo.c" EOLIAN static Eo * _efl_ui_text_async_efl_object_constructor(Eo *obj, void *_pd EINA_UNUSED) { EFL_UI_TEXT_DATA_GET(obj, sd); sd->async.enabled = EINA_TRUE; // FIXME: should we have to keep this efl_ui_text_xxx classes? // Then, going to make new theme for these classes? ex) efl/text_async? if (!elm_widget_theme_klass_get(obj)) elm_widget_theme_klass_set(obj, "text"); obj = efl_constructor(efl_super(obj, EFL_UI_TEXT_ASYNC_CLASS)); return obj; } #include "efl_ui_text_async.eo.c" #undef MY_CLASS #define MY_CLASS EFL_UI_TEXT_EDITABLE_CLASS EOLIAN static Eo * _efl_ui_text_editable_efl_object_constructor(Eo *obj, void *_pd EINA_UNUSED) { // FIXME: should we have to keep this efl_ui_text_xxx classes? // Then, going to make new theme for these classes? ex) efl/text_editable? if (!elm_widget_theme_klass_get(obj)) elm_widget_theme_klass_set(obj, "text"); obj = efl_constructor(efl_super(obj, MY_CLASS)); efl_ui_text_interactive_editable_set(obj, EINA_TRUE); return obj; } #include "efl_ui_text_editable.eo.c" diff --git a/src/lib/elementary/efl_ui_text.eo b/src/lib/elementary/efl_ui_text.eo index 515fc0dfda..5146d2ce8e 100644 --- a/src/lib/elementary/efl_ui_text.eo +++ b/src/lib/elementary/efl_ui_text.eo @@ -1,391 +1,391 @@ import elm_general; import elm_entry; class Efl.Ui.Text (Efl.Ui.Layout, Elm.Interface_Scrollable, Efl.Ui.Clickable, Efl.Access.Text, Efl.Access.Editable.Text, Efl.File, Efl.Ui.Selectable, Efl.Ui.Scrollable, Efl.Ui.Text.Interactive) { [[Efl UI text class]] methods { @property scrollable { set { [[Enable or disable scrolling in entry Normally the entry is not scrollable unless you enable it with this call. ]] } get { [[Get the scrollable state of the entry Normally the entry is not scrollable. This gets the scrollable state of the entry. ]] } values { scroll: bool; [[$true if it is to be scrollable, $false otherwise.]] } } @property input_panel_show_on_demand { set { [[Set the attribute to show the input panel in case of only a user's explicit Mouse Up event. It doesn't request to show the input panel even though it has focus. @since 1.9 ]] } get { [[Get the attribute to show the input panel in case of only an user's explicit Mouse Up event. @since 1.9 ]] } values { ondemand: bool; [[If $true, the input panel will be shown in case of only Mouse up event. (Focus event will be ignored.) ]] } } @property context_menu_disabled { set { [[This disables the entry's contextual (longpress) menu.]] } get { [[This returns whether the entry's contextual (longpress) menu is disabled. ]] } values { disabled: bool; [[If $true, the menu is disabled.]] } } @property cnp_mode { /* FIXME: Efl.Selection.Format does not allow markup without images! */ set { [[Control pasting of text and images for the widget. Normally the entry allows both text and images to be pasted. By setting cnp_mode to be #ELM_CNP_MODE_NO_IMAGE this prevents images from being copied or pasted. By setting cnp_mode to be #ELM_CNP_MODE_PLAINTEXT this remove all tags in text . Note: This only changes the behaviour of text. ]] } get { [[Getting elm_entry text paste/drop mode. Normally the entry allows both text and images to be pasted. This gets the copy & paste mode of the entry. ]] } values { format: Efl.Selection.Format; [[Format for copy & paste.]] } } @property input_panel_language { set { [[Set the language mode of the input panel. This API can be used if you want to show the alphabet keyboard mode. ]] } get { [[Get the language mode of the input panel.]] } values { lang: Elm.Input.Panel.Lang; [[Language to be set to the input panel.]] } } @property selection_handler_disabled { set { [[This disables the entry's selection handlers.]] } get { [[This returns whether the entry's selection handlers are disabled.]] legacy: null; } values { disabled: bool; [[If $true, the selection handlers are disabled.]] } } @property input_panel_layout_variation { set { [[Set the input panel layout variation of the entry @since 1.8 ]] } get { [[Get the input panel layout variation of the entry @since 1.8 ]] } values { variation: int; [[Layout variation type.]] } } @property autocapital_type { set { [[Set the autocapitalization type on the immodule.]] } get { [[Get the autocapitalization type on the immodule.]] } values { autocapital_type: Elm.Autocapital.Type; [[The type of autocapitalization.]] } } @property password { set { [[Sets the entry to password mode. In password mode entries are implicitly single line and the display of any text inside them is replaced with asterisks (*). ]] } get { [[Get whether the entry is set to password mode.]] } values { password: bool; [[If true, password mode is enabled.]] } } @property input_panel_return_key_disabled { set { [[Set the return key on the input panel to be disabled.]] } get { [[Get whether the return key on the input panel should be disabled or not.]] } values { disabled: bool; [[The state to put in in: $true for disabled, $false for enabled.]] } } @property prediction_allow { set { [[Set whether the entry should allow predictive text.]] } get { [[Get whether the entry allows predictive text.]] } values { prediction: bool; [[Whether the entry should allow predictive text.]] } } @property input_hint { set { [[Sets the input hint which allows input methods to fine-tune their behavior.]] } get { [[Gets the value of input hint.]] } values { hints: Elm.Input.Hints; [[Input hint.]] } } @property input_panel_layout { set { [[Set the input panel layout of the entry.]] } get { [[Get the input panel layout of the entry.]] } values { layout: Elm.Input.Panel.Layout(Elm.Input.Panel.Layout.invalid); [[Layout type.]] } } @property input_panel_return_key_type { set { [[Set the "return" key type. This type is used to set string or icon on the "return" key of the input panel. An input panel displays the string or icon associated with this type. ]] } get { [[Get the "return" key type.]] } values { return_key_type: Elm.Input.Panel.Return_Key.Type; [[The type of "return" key on the input panel.]] } } @property input_panel_enabled { set { [[Sets the attribute to show the input panel automatically.]] } get { [[Get the attribute to show the input panel automatically.]] } values { enabled: bool; [[If $true, the input panel is appeared when entry is clicked or has a focus.]] } } @property input_panel_return_key_autoenabled { set { [[Set whether the return key on the input panel is disabled automatically when entry has no text. If $enabled is $true, the return key on input panel is disabled when the entry has no text. The return key on the input panel is automatically enabled when the entry has text. The default value is $false. ]] } values { enabled: bool; [[If $enabled is $true, the return key is automatically disabled when the entry has no text.]] } } @property item_factory { [[The factory that provides item in the text e.g. "emoticon/happy" or "href=file://image.jpg" etc. ]] values { item_factory: Efl.Canvas.Text.Factory; [[Factory to create items]] } } input_panel_show { [[Show the input panel (virtual keyboard) based on the input panel property of entry such as layout, autocapital types and so on. Note that input panel is shown or hidden automatically according to the focus state of entry widget. This API can be used in the case of manually controlling by using @.input_panel_enabled.set(en, $false). ]] } selection_copy { [[This executes a "copy" action on the selected text in the entry.]] } context_menu_clear { [[This clears and frees the items in a entry's contextual (longpress) menu. See also @.context_menu_item_add. ]] } input_panel_imdata_set { [[Set the input panel-specific data to deliver to the input panel. This API is used by applications to deliver specific data to the input panel. The data format MUST be negotiated by both application and the input panel. The size and format of data are defined by the input panel. ]] params { @in data: const(void_ptr); [[The specific data to be set to the input panel.]] @in len: int; [[The length of data, in bytes, to send to the input panel.]] } } input_panel_imdata_get @const { [[Get the specific data of the current input panel.]] params { @inout data: void; [[The specific data to be obtained from the input panel.]] @out len: int; [[The length of data.]] } } selection_paste { [[This executes a "paste" action in the entry.]] } input_panel_hide { [[Hide the input panel (virtual keyboard). Note that input panel is shown or hidden automatically according to the focus state of entry widget. This API can be used in the case of manually controlling by using @.input_panel_enabled.set(en, $false) ]] } cursor_selection_end { [[This ends a selection within the entry as though the user had just released the mouse button while making a selection.]] } selection_cut { [[This executes a "cut" action on the selected text in the entry.]] } context_menu_item_add { [[This adds an item to the entry's contextual menu. A longpress on an entry will make the contextual menu show up unless this has been disabled with @.context_menu_disabled.set. By default this menu provides a few options like enabling selection mode, which is useful on embedded devices that need to be explicit about it. When a selection exists it also shows the copy and cut actions. With this function, developers can add other options to this menu to perform any action they deem necessary. ]] params { @in label: string @optional; [[The item's text label.]] @in icon_file: string @optional; [[The item's icon file.]] @in icon_type: Elm.Icon.Type; [[The item's icon type.]] @in func: Evas_Smart_Cb @optional; [[The callback to execute when the item is clicked.]] @in data: const(void_ptr) @optional; [[The data to associate with the item for related functions.]] } } cursor_new { [[Creates and returns a new cursor for the text.]] return: ptr(Efl.Text.Cursor.Cursor); [[Text cursor]] } } implements { Efl.Object.constructor; Efl.Object.destructor; Efl.Gfx.visible { set; } Efl.Gfx.position { set; } Efl.Gfx.size { set; } Efl.Canvas.Group.group_member_add; Efl.Layout.Signal.signal_callback_add; Efl.Layout.Signal.signal_callback_del; Efl.Layout.Signal.signal_emit; Efl.Ui.Widget.on_access_activate; Efl.Ui.Widget.theme_apply; - Efl.Ui.Widget.on_focus_update; + Efl.Ui.Focus.Object.on_focus_update; Efl.Ui.Widget.interest_region { get; } Efl.Ui.Widget.on_disabled_update; Efl.Ui.Widget.widget_sub_object_del; Elm.Interface_Scrollable.policy { set; } Elm.Interface_Scrollable.bounce_allow { set; } Efl.Access.state_set { get; } Efl.Access.name { get; } Efl.Access.Text.text { get; } Efl.Access.Text.string { get; } Efl.Access.Text.attribute { get; } Efl.Access.Text.attributes { get; } Efl.Access.Text.default_attributes { get; } Efl.Access.Text.caret_offset { get; set; } Efl.Access.Text.character { get; } Efl.Access.Text.character_extents { get; } Efl.Access.Text.character_count { get; } Efl.Access.Text.offset_at_point { get; } Efl.Access.Text.bounded_ranges { get; } Efl.Access.Text.range_extents { get; } Efl.Access.Text.selection { get; set; } Efl.Access.Text.selections_count { get; } Efl.Access.Text.selection_add; Efl.Access.Text.selection_remove; Efl.Access.Editable.Text.content { set; } Efl.Access.Editable.Text.insert; Efl.Access.Editable.Text.copy; Efl.Access.Editable.Text.cut; Efl.Access.Editable.Text.delete; Efl.Access.Editable.Text.paste; Efl.File.file { get; set; } Efl.Ui.Text.Interactive.editable { set; } } events { activated; [[Called when entry is activated]] changed; [[Called when entry changes]] changed,user: Efl.Ui.Text.Change_Info; [[The text object has changed due to user interaction]] validate; [[Called when validating]] context,open; [[Called when context menu was opened]] anchor,clicked; [[Called when anchor is clicked]] rejected; [[Called when entry is rejected]] maxlength,reached; [[Called when maximum entry length has been reached]] preedit,changed; [[Called when entry preedit changed]] press; [[Called when entry pressed]] redo,request; [[Called when redo is requested]] undo,request; [[Called when undo is requested]] text,set,done; [[Called when text set finished]] aborted; [[Called when entry is aborted]] anchor,down; [[Called on anchor down]] anchor,hover,opened; [[Called when hover opened]] anchor,in; [[Called on anchor in]] anchor,out; [[Called on anchor out]] anchor,up; [[called on anchor up]] cursor,changed; [[Called on cursor changed]] cursor,changed,manual; [[Called on manual cursor change]] } } diff --git a/src/lib/elementary/efl_ui_widget.c b/src/lib/elementary/efl_ui_widget.c index 6b822e0586..2816397da4 100644 --- a/src/lib/elementary/efl_ui_widget.c +++ b/src/lib/elementary/efl_ui_widget.c @@ -1,6019 +1,6019 @@ #ifdef HAVE_CONFIG_H # include "elementary_config.h" #endif #define EFL_ACCESS_PROTECTED #define EFL_ACCESS_COMPONENT_PROTECTED #define ELM_WIDGET_PROTECTED #define ELM_WIDGET_ITEM_PROTECTED #define EFL_CANVAS_OBJECT_BETA #define EFL_INPUT_EVENT_PROTECTED #define EFL_UI_TRANSLATABLE_PROTECTED #define EFL_UI_FOCUS_OBJECT_PROTECTED #define EFL_UI_WIDGET_PART_BG_PROTECTED #include #include "elm_priv.h" #include "elm_widget_container.h" #include "elm_interface_scrollable.h" #include "elm_part_helper.h" #include "elm_combobox.eo.h" /* FIXME: remove this when we don't rely on evas event structs anymore */ #define EFL_INTERNAL_UNSTABLE #include "interfaces/efl_common_internal.h" #define MY_CLASS EFL_UI_WIDGET_CLASS #define MY_CLASS_NAME "Efl_Ui_Widget" #define MY_CLASS_NAME_LEGACY "elm_widget" #define ELM_WIDGET_DATA_GET(o, wd) \ Elm_Widget_Smart_Data *wd = efl_data_scope_get(o, MY_CLASS) #define API_ENTRY \ Elm_Widget_Smart_Data *sd = NULL; \ if (!_elm_widget_is(obj) || \ (!(sd = efl_data_scope_get(obj, MY_CLASS)))) #define INTERNAL_ENTRY \ ELM_WIDGET_DATA_GET(obj, sd); \ if (!sd) return #define ELM_WIDGET_FOCUS_GET(obj) \ (efl_isa(obj, EFL_UI_WIDGET_CLASS) && \ ((_elm_access_auto_highlight_get()) ? (elm_widget_highlight_get(obj)) : \ (efl_ui_focus_object_focus_get(obj)))) const char SIG_WIDGET_FOCUSED[] = "focused"; const char SIG_WIDGET_UNFOCUSED[] = "unfocused"; const char SIG_WIDGET_LANG_CHANGED[] = "language,changed"; const char SIG_WIDGET_ACCESS_CHANGED[] = "access,changed"; typedef struct _Elm_Event_Cb_Data Elm_Event_Cb_Data; typedef struct _Elm_Label_Data Elm_Label_Data; typedef struct _Elm_Translate_String_Data Elm_Translate_String_Data; /* FIXME: EAPI because of elm_code_widget test case */ EAPI Eina_Bool _elm_legacy_add = EINA_FALSE; struct _Elm_Event_Cb_Data { Elm_Event_Cb func; const void *data; }; struct _Elm_Label_Data { const char *part; const char *text; }; struct _Elm_Translate_String_Data { EINA_INLIST; Eina_Stringshare *id; Eina_Stringshare *domain; Eina_Stringshare *string; Eina_Bool preset : 1; }; /* local subsystem globals */ static inline Eina_Bool _elm_widget_is(const Evas_Object *obj) { return efl_isa(obj, MY_CLASS); } static inline Eina_Bool _is_focusable(Evas_Object *obj) { API_ENTRY return EINA_FALSE; return sd->can_focus || (sd->child_can_focus); } static inline Eina_Bool _is_focused(Evas_Object *obj) { API_ENTRY return EINA_FALSE; return sd->focused; } static inline Eina_Bool _elm_scrollable_is(const Evas_Object *obj) { INTERNAL_ENTRY EINA_FALSE; return efl_isa(obj, ELM_INTERFACE_SCROLLABLE_MIXIN); } static void elm_widget_disabled_internal(Eo *obj, Eina_Bool disabled); static void _on_sub_obj_del(void *data, const Efl_Event *event); static void _propagate_event(void *data, const Efl_Event *eo_event); static void _elm_widget_focus_tree_unfocusable_handle(Eo *obj); static void _elm_widget_shadow_update(Efl_Ui_Widget *obj); EFL_CALLBACKS_ARRAY_DEFINE(elm_widget_subitems_callbacks, { EFL_EVENT_DEL, _on_sub_obj_del }); EFL_CALLBACKS_ARRAY_DEFINE(efl_subitems_callbacks, { EFL_EVENT_DEL, _on_sub_obj_del }); EFL_CALLBACKS_ARRAY_DEFINE(focus_callbacks, { EFL_EVENT_KEY_DOWN, _propagate_event }, { EFL_EVENT_KEY_UP, _propagate_event }, { EFL_EVENT_POINTER_WHEEL, _propagate_event }); static inline void _callbacks_add(Eo *widget, void *data) { if (_elm_widget_is(widget)) { efl_event_callback_array_add(widget, elm_widget_subitems_callbacks(), data); } else { efl_event_callback_array_add(widget, efl_subitems_callbacks(), data); } } static inline void _callbacks_del(Eo *widget, void *data) { if (_elm_widget_is(widget)) { efl_event_callback_array_del(widget, elm_widget_subitems_callbacks(), data); } else { efl_event_callback_array_del(widget, efl_subitems_callbacks(), data); } } void _elm_widget_item_highlight_in_theme(Evas_Object *obj, Elm_Object_Item *eo_it) { const char *str; if (!eo_it) return; if (efl_isa(eo_it, ELM_WIDGET_ITEM_CLASS)) { Elm_Widget_Item_Data *it = efl_data_scope_get(eo_it, ELM_WIDGET_ITEM_CLASS); if (efl_isa(it->view, EFL_UI_LAYOUT_CLASS)) str = edje_object_data_get(elm_layout_edje_get(it->view), "focus_highlight"); else str = edje_object_data_get(it->view, "focus_highlight"); } else str = edje_object_data_get(((Elm_Widget_Item_Data *)eo_it)->view, "focus_highlight"); if ((str) && (!strcmp(str, "on"))) elm_widget_highlight_in_theme_set(obj, EINA_TRUE); else elm_widget_highlight_in_theme_set(obj, EINA_FALSE); } void _elm_widget_focus_highlight_start(const Evas_Object *obj) { Evas_Object *top = elm_widget_top_get(obj); if (top && efl_isa(top, EFL_UI_WIN_CLASS)) _elm_win_focus_highlight_start(top); } Evas_Object * _efl_ui_widget_focus_highlight_object_get(const Evas_Object *obj) { Evas_Object *top = elm_widget_top_get(obj); if (top && efl_isa(top, EFL_UI_WIN_CLASS)) return _elm_win_focus_highlight_object_get(top); return NULL; } EOLIAN static Eina_Bool _efl_ui_widget_focus_highlight_enabled_get(Eo *obj, Elm_Widget_Smart_Data *sd EINA_UNUSED) { // Forward to closest parent Window const Evas_Object *win = elm_widget_top_get(obj); if (win && efl_isa(win, EFL_UI_WIN_CLASS)) return elm_win_focus_highlight_enabled_get(win); return EINA_FALSE; } EOLIAN static void _efl_ui_widget_focus_highlight_enabled_set(Eo *obj, Elm_Widget_Smart_Data *sd EINA_UNUSED, Eina_Bool enable) { // Forward to closest parent Window Evas_Object *win = elm_widget_top_get(obj); if (win && efl_isa(win, EFL_UI_WIN_CLASS)) elm_win_focus_highlight_enabled_set(win, enable); } EOLIAN static Eina_Bool _efl_ui_widget_focus_highlight_animate_get(Eo *obj, Elm_Widget_Smart_Data *sd EINA_UNUSED) { // Forward to closest parent Window const Evas_Object *win = elm_widget_top_get(obj); if (win && efl_isa(win, EFL_UI_WIN_CLASS)) return elm_win_focus_highlight_animate_get(win); return EINA_FALSE; } EOLIAN static void _efl_ui_widget_focus_highlight_animate_set(Eo *obj, Elm_Widget_Smart_Data *sd EINA_UNUSED, Eina_Bool enable) { // Forward to closest parent Window Evas_Object *win = elm_widget_top_get(obj); if (win && efl_isa(win, EFL_UI_WIN_CLASS)) elm_win_focus_highlight_animate_set(win, enable); } EOLIAN static Eina_Bool _efl_ui_widget_focus_highlight_style_set(Eo *obj, Elm_Widget_Smart_Data *sd EINA_UNUSED, const char *style) { // Forward to closest parent Window Evas_Object *win = elm_widget_top_get(obj); if (win && efl_isa(win, EFL_UI_WIN_CLASS)) return efl_ui_widget_focus_highlight_style_set(win, style); return EINA_FALSE; } EOLIAN static const char * _efl_ui_widget_focus_highlight_style_get(Eo *obj, Elm_Widget_Smart_Data *sd EINA_UNUSED) { // Forward to closest parent Window Evas_Object *win = elm_widget_top_get(obj); if (win && efl_isa(win, EFL_UI_WIN_CLASS)) return elm_win_focus_highlight_style_get(win); return NULL; } static Eina_Bool _tree_unfocusable(Eo *obj) { Efl_Ui_Widget *wid = obj; do { ELM_WIDGET_DATA_GET(wid, wid_pd); if (wid_pd->tree_unfocusable) return EINA_TRUE; } while((wid = elm_widget_parent_widget_get(wid))); return EINA_FALSE; } static Eina_Bool _tree_disabled(Eo *obj) { Efl_Ui_Widget *wid = obj; do { ELM_WIDGET_DATA_GET(wid, wid_pd); if (wid_pd->disabled) return EINA_TRUE; } while((wid = elm_widget_parent_widget_get(wid))); return EINA_FALSE; } static void _full_eval(Eo *obj, Elm_Widget_Smart_Data *pd); static void _manager_changed_cb(void *data, const Efl_Event *event EINA_UNUSED) { ELM_WIDGET_DATA_GET(data, pd); _full_eval(data, pd); } static Efl_Ui_Focus_Object* _focus_manager_eval(Eo *obj, Elm_Widget_Smart_Data *pd) { Evas_Object *provider = NULL; Evas_Object *parent; Efl_Ui_Focus_Manager *new = NULL, *old = NULL; parent = elm_widget_parent_get(obj); if (efl_isa(parent, EFL_UI_FOCUS_MANAGER_INTERFACE)) { new = parent; } else if (parent) { new = efl_ui_focus_object_focus_manager_get(parent); provider = parent; } if (new != pd->manager.manager ) { old = pd->manager.manager; if (pd->manager.provider) efl_event_callback_del(pd->manager.provider, EFL_UI_FOCUS_OBJECT_EVENT_MANAGER_CHANGED, _manager_changed_cb, obj); pd->manager.manager = new; pd->manager.provider = provider; if (pd->manager.provider) efl_event_callback_add(pd->manager.provider, EFL_UI_FOCUS_OBJECT_EVENT_MANAGER_CHANGED, _manager_changed_cb, obj); } return old; } EOLIAN static Eina_Bool _efl_ui_widget_focus_state_apply(Eo *obj, Elm_Widget_Smart_Data *pd EINA_UNUSED, Efl_Ui_Widget_Focus_State current_state, Efl_Ui_Widget_Focus_State *configured_state, Efl_Ui_Widget *redirect) { Eina_Bool registered = EINA_TRUE; //shortcut for having the same configurations if (current_state.manager == configured_state->manager && !current_state.manager) return !!current_state.manager; if (configured_state->logical == current_state.logical && configured_state->manager == current_state.manager && configured_state->parent == current_state.parent) return !!current_state.manager; //this thing doesnt want to be registered, but it is ... if (!configured_state->manager && current_state.manager) { efl_ui_focus_manager_calc_unregister(current_state.manager, obj); return EINA_FALSE; } //by that point we have always a configured manager if (!current_state.manager) registered = EINA_FALSE; if ((//check if we have changed the manager (current_state.manager != configured_state->manager) || //check if we are already registered but in a different state (current_state.logical != configured_state->logical)) && registered) { //we need to unregister here efl_ui_focus_manager_calc_unregister(current_state.manager, obj); registered = EINA_FALSE; } //the parent may has changed if (current_state.parent != configured_state->parent && registered) { return efl_ui_focus_manager_calc_update_parent(current_state.manager, obj, configured_state->parent); } if (!registered) { if (configured_state->logical) return efl_ui_focus_manager_calc_register_logical(configured_state->manager, obj, configured_state->parent, redirect); else return efl_ui_focus_manager_calc_register(configured_state->manager, obj, configured_state->parent, redirect); } ERR("Uncaught focus state consider this as unregistered (%d) \n (%p,%p,%d) \n (%p,%p,%d) ", registered, configured_state->manager, configured_state->parent, configured_state->logical, current_state.manager, current_state.parent, current_state.logical ); return EINA_FALSE; } static void _eval_registration_candidate(Eo *obj, Elm_Widget_Smart_Data *pd, Eina_Bool *should, Eina_Bool *want_full) { *should = *want_full = EINA_FALSE; //can focus can be overridden by the following properties if (!efl_isa(elm_widget_top_get(obj), EFL_UI_WIN_CLASS) || (!pd->parent_obj) || (_tree_unfocusable(obj)) || (_tree_disabled(obj)) || (!evas_object_visible_get(obj))) return; if (pd->can_focus) { *should = *want_full = EINA_TRUE; } else if (pd->logical.child_count > 0) { *should = EINA_TRUE; } } static void _focus_state_eval(Eo *obj, Elm_Widget_Smart_Data *pd, Eina_Bool should, Eina_Bool want_full) { Efl_Ui_Widget_Focus_State configuration; //this would mean we are registering again the root, we dont want that if (pd->manager.manager == obj) return; //there are two reasons to be registered, the child count is bigger than 0, or the widget is flagged to be able to handle focus if (should) { configuration.parent = pd->logical.parent; configuration.manager = pd->manager.manager; configuration.logical = !want_full; } else { configuration.parent = NULL; configuration.manager = NULL; configuration.logical = EINA_FALSE; } if (!efl_ui_widget_focus_state_apply(obj, pd->focus, &configuration, NULL)) { //things went wrong or this thing is unregistered. Purge the current configuration. pd->focus.manager = NULL; pd->focus.parent = NULL; pd->focus.logical = EINA_FALSE; } else { pd->focus.parent = configuration.parent; pd->focus.manager = configuration.manager; pd->focus.logical = configuration.logical; } } static Efl_Ui_Focus_Object* _logical_parent_eval(Eo *obj EINA_UNUSED, Elm_Widget_Smart_Data *pd, Eina_Bool should) { Efl_Ui_Widget *parent; Efl_Ui_Focus_Parent_Provider *provider; if (should) { provider = efl_provider_find(obj, EFL_UI_FOCUS_PARENT_PROVIDER_INTERFACE); EINA_SAFETY_ON_NULL_RETURN_VAL(provider, NULL); parent = efl_ui_focus_parent_provider_find_logical_parent(provider, obj); } else parent = NULL; if (pd->logical.parent != parent) { Efl_Ui_Focus_Object *old = NULL; //update old logical parent; if (pd->logical.parent) { if (efl_isa(pd->logical.parent, EFL_UI_WIDGET_CLASS)) { ELM_WIDGET_DATA_GET(pd->logical.parent, logical_wd); if (!logical_wd) { ERR("Widget parent has the wrong type!"); return NULL; } logical_wd->logical.child_count --; } old = pd->logical.parent; efl_weak_unref(&pd->logical.parent); pd->logical.parent = NULL; } if (parent) { if (efl_isa(parent, EFL_UI_WIDGET_CLASS)) { ELM_WIDGET_DATA_GET(parent, parent_wd); if (!parent_wd) { ERR("Widget parent has the wrong type!"); return NULL; } parent_wd->logical.child_count ++; } pd->logical.parent = parent; efl_weak_ref(&pd->logical.parent); } return old; } return NULL; } static void _full_eval(Eo *obj, Elm_Widget_Smart_Data *pd) { Efl_Ui_Focus_Object *old_parent; Efl_Ui_Focus_Object *old_registered_parent, *old_registered_manager; Eina_Bool should, want_full; _eval_registration_candidate(obj, pd, &should, &want_full); old_parent = _logical_parent_eval(obj, pd, should); if (efl_isa(old_parent, EFL_UI_WIDGET_CLASS)) { //emit signal and focus eval old and new ELM_WIDGET_DATA_GET(old_parent, old_pd); _full_eval(old_parent, old_pd); } if (efl_isa(pd->logical.parent, EFL_UI_WIDGET_CLASS)) { ELM_WIDGET_DATA_GET(pd->logical.parent, new_pd); _full_eval(pd->logical.parent, new_pd); } _focus_manager_eval(obj, pd); old_registered_parent = pd->focus.parent; old_registered_manager = pd->focus.manager; _focus_state_eval(obj, pd, should, want_full); if (old_registered_parent != pd->focus.parent) { efl_event_callback_call(obj, EFL_UI_FOCUS_OBJECT_EVENT_LOGICAL_CHANGED, old_registered_parent); } if (old_registered_manager != pd->focus.manager) { efl_event_callback_call(obj, EFL_UI_FOCUS_OBJECT_EVENT_MANAGER_CHANGED, old_registered_manager); } } void _elm_widget_full_eval(Eo *obj) { ELM_WIDGET_DATA_GET(obj, pd); _full_eval(obj, pd); } /** * @internal * * Resets the mirrored mode from the system mirror mode for widgets that are in * automatic mirroring mode. This function does not call elm_widget_theme. * * @param obj The widget. * @param mirrored EINA_TRUE to set mirrored mode. EINA_FALSE to unset. */ static void _elm_widget_mirrored_reload(Evas_Object *obj) { API_ENTRY return; Eina_Bool mirrored = elm_config_mirrored_get(); if (efl_ui_mirrored_automatic_get(obj) && (sd->is_mirrored != mirrored)) { sd->is_mirrored = mirrored; } } static void _parents_focus(Evas_Object *obj) { for (; obj; obj = elm_widget_parent_get(obj)) { INTERNAL_ENTRY; if (sd->focused) return; sd->focused = 1; } } static void _parents_unfocus(Evas_Object *obj) { for (; obj; obj = elm_widget_parent_get(obj)) { INTERNAL_ENTRY; if (!sd->focused) return; sd->focused = 0; } } static void _on_sub_obj_del(void *data, const Efl_Event *event) { ELM_WIDGET_DATA_GET_OR_RETURN(data, sd); if (_elm_widget_is(event->object)) { if (_is_focused(event->object)) _parents_unfocus(sd->obj); } if (event->object == sd->resize_obj) { /* already dels sub object */ elm_widget_resize_object_set(sd->obj, NULL); } else if (event->object == sd->hover_obj) { sd->hover_obj = NULL; } else { if (!elm_widget_sub_object_del(sd->obj, event->object)) ERR("failed to remove sub object %p from %p\n", event->object, sd->obj); } } static const Evas_Smart_Cb_Description _smart_callbacks[] = { {SIG_WIDGET_FOCUSED, ""}, {SIG_WIDGET_UNFOCUSED, ""}, {SIG_WIDGET_LANG_CHANGED, ""}, {SIG_WIDGET_ACCESS_CHANGED, ""}, {NULL, NULL} }; static void _obj_mouse_down(void *data, Evas *e EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event_info) { Evas_Object *top; ELM_WIDGET_DATA_GET(data, sd); Evas_Event_Mouse_Down *ev = event_info; if (ev->event_flags & EVAS_EVENT_FLAG_ON_HOLD) return; top = elm_widget_top_get(data); if (top && efl_isa(top, EFL_UI_WIN_CLASS)) _elm_win_focus_auto_hide(top); sd->still_in = EINA_TRUE; } static void _obj_mouse_move(void *data, Evas *e EINA_UNUSED, Evas_Object *obj, void *event_info) { ELM_WIDGET_DATA_GET(data, sd); Evas_Event_Mouse_Move *ev = event_info; if (!sd->still_in) return; if (ev->event_flags & EVAS_EVENT_FLAG_ON_HOLD) sd->still_in = EINA_FALSE; else { Evas_Coord x, y, w, h; evas_object_geometry_get(obj, &x, &y, &w, &h); if (ELM_RECTS_POINT_OUT(x, y, w, h, ev->cur.canvas.x, ev->cur.canvas.y)) sd->still_in = EINA_FALSE; } } static void _obj_mouse_up(void *data, Evas *e EINA_UNUSED, Evas_Object *obj, void *event_info) { ELM_WIDGET_DATA_GET(data, sd); Evas_Event_Mouse_Up *ev = event_info; if (sd->still_in && (ev->flags == EVAS_BUTTON_NONE) && (sd->focus_move_policy == ELM_FOCUS_MOVE_POLICY_CLICK)) elm_widget_focus_mouse_up_handle(evas_object_widget_parent_find(obj)); sd->still_in = EINA_FALSE; } static void _obj_mouse_in(void *data, Evas *e EINA_UNUSED, Evas_Object *obj, void *event_info EINA_UNUSED) { ELM_WIDGET_DATA_GET(data, sd); if (sd->focus_move_policy == ELM_FOCUS_MOVE_POLICY_IN) elm_widget_focus_mouse_up_handle(evas_object_widget_parent_find(obj)); } EOLIAN static void _efl_ui_widget_efl_canvas_group_group_add(Eo *obj, Elm_Widget_Smart_Data *priv) { priv->obj = obj; priv->mirrored_auto_mode = EINA_TRUE; /* will follow system locale * settings */ priv->focus_move_policy_auto_mode = EINA_TRUE; priv->focus_region_show_mode = ELM_FOCUS_REGION_SHOW_WIDGET; elm_widget_can_focus_set(obj, EINA_TRUE); priv->is_mirrored = elm_config_mirrored_get(); priv->focus_move_policy = _elm_config->focus_move_policy; evas_object_event_callback_add(obj, EVAS_CALLBACK_MOUSE_DOWN, _obj_mouse_down, obj); evas_object_event_callback_add(obj, EVAS_CALLBACK_MOUSE_MOVE, _obj_mouse_move, obj); evas_object_event_callback_add(obj, EVAS_CALLBACK_MOUSE_UP, _obj_mouse_up, obj); evas_object_event_callback_add(obj, EVAS_CALLBACK_MOUSE_IN, _obj_mouse_in, obj); } EOLIAN static void _efl_ui_widget_efl_canvas_group_group_del(Eo *obj, Elm_Widget_Smart_Data *sd) { Evas_Object *sobj; Elm_Translate_String_Data *ts; Elm_Event_Cb_Data *ecb; if (sd->hover_obj) { /* detach it from us */ _callbacks_del(sd->hover_obj, obj); sd->hover_obj = NULL; } while (sd->subobjs) { sobj = eina_list_data_get(sd->subobjs); /* let the objects clean-up themselves and get rid of this list */ if (!elm_widget_sub_object_del(obj, sobj)) { ERR("failed to remove sub object %p from %p\n", sobj, obj); sd->subobjs = eina_list_remove_list (sd->subobjs, sd->subobjs); } evas_object_del(sobj); } sd->tooltips = eina_list_free(sd->tooltips); /* should be empty anyway */ sd->cursors = eina_list_free(sd->cursors); /* should be empty anyway */ while (sd->translate_strings) { ts = EINA_INLIST_CONTAINER_GET(sd->translate_strings, Elm_Translate_String_Data); eina_stringshare_del(ts->id); eina_stringshare_del(ts->domain); eina_stringshare_del(ts->string); sd->translate_strings = eina_inlist_remove(sd->translate_strings, sd->translate_strings); free(ts); } EINA_LIST_FREE(sd->event_cb, ecb) free(ecb); eina_stringshare_del(sd->klass); eina_stringshare_del(sd->group); eina_stringshare_del(sd->style); if (sd->theme) elm_theme_free(sd->theme); eina_stringshare_del(sd->access_info); eina_stringshare_del(sd->accessible_name); evas_object_smart_data_set(obj, NULL); efl_canvas_group_del(efl_super(obj, MY_CLASS)); } static void _smart_reconfigure(Elm_Widget_Smart_Data *sd) { if (sd->resize_obj) { evas_object_move(sd->resize_obj, sd->x, sd->y); evas_object_resize(sd->resize_obj, sd->w, sd->h); } if (sd->hover_obj) { evas_object_move(sd->hover_obj, sd->x, sd->y); evas_object_resize(sd->hover_obj, sd->w, sd->h); } if (sd->bg) { evas_object_move(sd->bg, sd->x, sd->y); evas_object_resize(sd->bg, sd->w, sd->h); } if (sd->has_shadow) _elm_widget_shadow_update(sd->obj); } EOLIAN static void _efl_ui_widget_efl_gfx_position_set(Eo *obj EINA_UNUSED, Elm_Widget_Smart_Data *sd, Eina_Position2D pos) { if (_evas_object_intercept_call(obj, EVAS_OBJECT_INTERCEPT_CB_MOVE, 0, pos.x, pos.y)) return; sd->x = pos.x; sd->y = pos.y; _smart_reconfigure(sd); efl_gfx_position_set(efl_super(obj, MY_CLASS), pos); } EOLIAN static void _efl_ui_widget_efl_gfx_size_set(Eo *obj EINA_UNUSED, Elm_Widget_Smart_Data *sd, Eina_Size2D sz) { if (_evas_object_intercept_call(obj, EVAS_OBJECT_INTERCEPT_CB_RESIZE, 0, sz.w, sz.h)) return; sd->w = sz.w; sd->h = sz.h; _smart_reconfigure(sd); efl_gfx_size_set(efl_super(obj, MY_CLASS), sz); } static void _full_eval_children(Eo *obj, Elm_Widget_Smart_Data *sd) { Eina_List *l; Eo *child; _full_eval(obj, sd); EINA_LIST_FOREACH(sd->subobjs , l, child) { Elm_Widget_Smart_Data *sd_child; if (!efl_isa(child, EFL_UI_WIDGET_CLASS)) continue; sd_child = efl_data_scope_get(child, EFL_UI_WIDGET_CLASS); _full_eval_children(child, sd_child); } } EOLIAN static void _efl_ui_widget_efl_gfx_visible_set(Eo *obj, Elm_Widget_Smart_Data *pd, Eina_Bool vis) { Eina_Iterator *it; Evas_Object *o; if (_evas_object_intercept_call(obj, EVAS_OBJECT_INTERCEPT_CB_VISIBLE, 0, vis)) { _full_eval_children(obj, pd); return; } efl_gfx_visible_set(efl_super(obj, MY_CLASS), vis); _full_eval_children(obj, pd); it = evas_object_smart_iterator_new(obj); EINA_ITERATOR_FOREACH(it, o) { if (evas_object_data_get(o, "_elm_leaveme")) continue; efl_gfx_visible_set(o, vis); } eina_iterator_free(it); if (!_elm_config->atspi_mode || pd->on_destroy) return; if (vis) { efl_access_added(obj); if (_elm_widget_onscreen_is(obj)) efl_access_state_changed_signal_emit(obj, EFL_ACCESS_STATE_SHOWING, EINA_TRUE); } else { efl_access_state_changed_signal_emit(obj, EFL_ACCESS_STATE_SHOWING, EINA_FALSE); } } EOLIAN static void _efl_ui_widget_efl_gfx_color_color_set(Eo *obj, Elm_Widget_Smart_Data *pd, int r, int g, int b, int a) { Eina_Iterator *it; Evas_Object *o; if (_evas_object_intercept_call(obj, EVAS_OBJECT_INTERCEPT_CB_COLOR_SET, 0, r, g, b, a)) return; it = evas_object_smart_iterator_new(obj); EINA_ITERATOR_FOREACH(it, o) { if (pd->bg == o) continue; if (evas_object_data_get(o, "_elm_leaveme")) continue; evas_object_color_set(o, r, g, b, a); } eina_iterator_free(it); } EOLIAN static void _efl_ui_widget_efl_canvas_object_no_render_set(Eo *obj, Elm_Widget_Smart_Data *_pd EINA_UNUSED, Eina_Bool hide) { Eina_Iterator *it; Evas_Object *o; hide = !!hide; if (efl_canvas_object_no_render_get(obj) == hide) return; it = evas_object_smart_iterator_new(obj); EINA_ITERATOR_FOREACH(it, o) { if (evas_object_data_get(o, "_elm_leaveme")) continue; efl_canvas_object_no_render_set(o, hide); } eina_iterator_free(it); // bypass implementation in Efl.Canvas.Group efl_canvas_object_no_render_set(efl_super(obj, EFL_CANVAS_GROUP_CLASS), hide); } EOLIAN static void _efl_ui_widget_efl_canvas_object_is_frame_object_set(Eo *obj, Elm_Widget_Smart_Data *pd, Eina_Bool frame) { Evas_Object *o; Eina_List *li; frame = !!frame; efl_canvas_object_is_frame_object_set(efl_super(obj, MY_CLASS), frame); EINA_LIST_FOREACH(pd->subobjs, li, o) { if (evas_object_data_get(o, "_elm_leaveme")) continue; efl_canvas_object_is_frame_object_set(o, frame); } } EOLIAN static void _efl_ui_widget_efl_canvas_object_clip_set(Eo *obj, Elm_Widget_Smart_Data *_pd EINA_UNUSED, Evas_Object *clip) { Eina_Iterator *it; Evas_Object *o; if (_evas_object_intercept_call(obj, EVAS_OBJECT_INTERCEPT_CB_CLIP_SET, 0, clip)) return; efl_canvas_object_clip_set(efl_super(obj, MY_CLASS), clip); it = evas_object_smart_iterator_new(obj); EINA_ITERATOR_FOREACH(it, o) { if (evas_object_data_get(o, "_elm_leaveme")) continue; evas_object_clip_set(o, clip); } eina_iterator_free(it); } EOLIAN static void _efl_ui_widget_efl_canvas_group_group_calculate(Eo *obj EINA_UNUSED, Elm_Widget_Smart_Data *_pd EINA_UNUSED) { /* a NO-OP, on the base */ } EOLIAN static void _efl_ui_widget_efl_canvas_group_group_member_add(Eo *obj, Elm_Widget_Smart_Data *pd, Evas_Object *child) { int r, g, b, a; efl_canvas_group_member_add(efl_super(obj, MY_CLASS), child); if (evas_object_data_get(child, "_elm_leaveme")) return; if (pd->bg != child) { evas_object_color_get(obj, &r, &g, &b, &a); evas_object_color_set(child, r, g, b, a); } efl_canvas_object_no_render_set(child, efl_canvas_object_no_render_get(obj)); evas_object_clip_set(child, evas_object_clip_get(obj)); if (evas_object_visible_get(obj)) evas_object_show(child); else evas_object_hide(child); } EOLIAN static void _efl_ui_widget_efl_canvas_group_group_member_del(Eo *obj EINA_UNUSED, Elm_Widget_Smart_Data *_pd EINA_UNUSED, Evas_Object *child) { if (!evas_object_data_get(child, "_elm_leaveme")) evas_object_clip_unset(child); efl_canvas_group_member_del(efl_super(obj, MY_CLASS), child); } // internal funcs static void _propagate_x_drag_lock(Evas_Object *obj, int dir) { INTERNAL_ENTRY; if (sd->parent_obj) { ELM_WIDGET_DATA_GET(sd->parent_obj, sd2); if (sd2) { sd2->child_drag_x_locked += dir; _propagate_x_drag_lock(sd->parent_obj, dir); } } } static void _propagate_y_drag_lock(Evas_Object *obj, int dir) { INTERNAL_ENTRY; if (sd->parent_obj) { ELM_WIDGET_DATA_GET(sd->parent_obj, sd2); if (sd2) { sd2->child_drag_y_locked += dir; _propagate_y_drag_lock(sd->parent_obj, dir); } } } static Eina_Bool _propagate_event_legacy(Eo *parent, const Efl_Event *event, Eo *obj, Elm_Event_Cb_Data *ecd) { Evas_Callback_Type type; Evas_Event_Flags *event_flags, prev_flags; union { Evas_Event_Key_Down *down; Evas_Event_Key_Up *up; Evas_Event_Mouse_Wheel *wheel; void *any; } event_info; if (event->desc == EFL_EVENT_KEY_DOWN) { event_info.down = efl_input_legacy_info_get(event->info); EINA_SAFETY_ON_NULL_RETURN_VAL(event_info.down, EINA_FALSE); type = EVAS_CALLBACK_KEY_DOWN; event_flags = &event_info.down->event_flags; } else if (event->desc == EFL_EVENT_KEY_UP) { event_info.up = efl_input_legacy_info_get(event->info); EINA_SAFETY_ON_NULL_RETURN_VAL(event_info.up, EINA_FALSE); type = EVAS_CALLBACK_KEY_UP; event_flags = &event_info.up->event_flags; } else if (event->desc == EFL_EVENT_POINTER_WHEEL) { event_info.wheel = efl_input_legacy_info_get(event->info); EINA_SAFETY_ON_NULL_RETURN_VAL(event_info.wheel, EINA_FALSE); type = EVAS_CALLBACK_MOUSE_WHEEL; event_flags = &event_info.wheel->event_flags; } else return EINA_FALSE; prev_flags = *event_flags; if (ecd->func((void *)ecd->data, parent, obj, type, event_info.any) || ((*event_flags) & EVAS_EVENT_FLAG_ON_HOLD)) { if (prev_flags != *event_flags) efl_input_event_flags_set(event->info, *event_flags); return EINA_TRUE; } return EINA_FALSE; } /** * @internal * * If elm_widget_focus_region_get() returns an empty rect (w or h <= 0), * this function will ignore region show action. */ EAPI void elm_widget_focus_region_show(Eo *obj) { Evas_Coord ox, oy; Eina_Rect r; Evas_Object *o; o = elm_widget_parent_get(obj); if (!o) return; r = elm_widget_focus_region_get(obj); if (eina_rectangle_is_empty(&r.rect)) return; evas_object_geometry_get(obj, &ox, &oy, NULL, NULL); while (o) { Evas_Coord px, py; evas_object_geometry_get(o, &px, &py, NULL, NULL); if (_elm_scrollable_is(o) && !elm_widget_disabled_get(o)) { Evas_Coord sx, sy; elm_interface_scrollable_content_region_get(o, &sx, &sy, NULL, NULL); // Get the object's on_focus_region position relative to the scroller. Evas_Coord rx, ry; rx = ox + r.x - px + sx; ry = oy + r.y - py + sy; switch (_elm_config->focus_autoscroll_mode) { case ELM_FOCUS_AUTOSCROLL_MODE_SHOW: elm_interface_scrollable_content_region_show(o, rx, ry, r.w, r.h); break; case ELM_FOCUS_AUTOSCROLL_MODE_BRING_IN: elm_interface_scrollable_region_bring_in(o, rx, ry, r.w, r.h); break; default: break; } r = elm_widget_focus_region_get(o); evas_object_geometry_get(o, &ox, &oy, NULL, NULL); } else { r.x += ox - px; r.y += oy - py; ox = px; oy = py; } o = elm_widget_parent_get(o); } } EOLIAN static void _efl_ui_widget_widget_parent_set(Eo *obj EINA_UNUSED, Elm_Widget_Smart_Data *_pd EINA_UNUSED, Evas_Object *parent EINA_UNUSED) { } EAPI Eina_Bool elm_widget_api_check(int ver) { if (ver != ELM_INTERNAL_API_VERSION) { CRI("Elementary widget api versions do not match"); return EINA_FALSE; } return EINA_TRUE; } EAPI Eina_Bool elm_widget_access(Evas_Object *obj, Eina_Bool is_access) { const Eina_List *l; Evas_Object *child; Eina_Bool ret = EINA_TRUE; API_ENTRY return EINA_FALSE; EINA_LIST_FOREACH(sd->subobjs, l, child) { if (elm_widget_is(child)) ret &= elm_widget_access(child, is_access); } efl_ui_widget_on_access_update(obj, is_access); efl_event_callback_legacy_call(obj, EFL_UI_WIDGET_EVENT_ACCESS_CHANGED, NULL); return ret; } EOLIAN static void _efl_ui_widget_on_access_update(Eo *obj EINA_UNUSED, Elm_Widget_Smart_Data *_pd EINA_UNUSED, Eina_Bool is_access EINA_UNUSED) { } EAPI Efl_Ui_Theme_Apply elm_widget_theme(Evas_Object *obj) { const Eina_List *l; Evas_Object *child; Elm_Tooltip *tt; Elm_Cursor *cur; Efl_Ui_Theme_Apply ret = EFL_UI_THEME_APPLY_SUCCESS; API_ENTRY return EFL_UI_THEME_APPLY_FAILED; EINA_LIST_FOREACH(sd->subobjs, l, child) if (_elm_widget_is(child)) ret &= elm_widget_theme(child); if (sd->hover_obj) ret &= elm_widget_theme(sd->hover_obj); EINA_LIST_FOREACH(sd->tooltips, l, tt) elm_tooltip_theme(tt); EINA_LIST_FOREACH(sd->cursors, l, cur) elm_cursor_theme(cur); ret &= efl_ui_widget_theme_apply(obj); return ret; } EAPI void elm_widget_theme_specific(Evas_Object *obj, Elm_Theme *th, Eina_Bool force) { const Eina_List *l; Evas_Object *child; Elm_Tooltip *tt; Elm_Cursor *cur; Elm_Theme *th2, *thdef; API_ENTRY return; thdef = elm_theme_default_get(); if (!th) th = thdef; if (!force) { th2 = sd->theme; if (!th2) th2 = thdef; while (th2) { if (th2 == th) { force = EINA_TRUE; break; } if (th2 == thdef) break; th2 = th2->ref_theme; if (!th2) th2 = thdef; } } if (!force) return; EINA_LIST_FOREACH(sd->subobjs, l, child) { if (elm_widget_is(child)) elm_widget_theme_specific(child, th, force); } if (sd->hover_obj) elm_widget_theme(sd->hover_obj); EINA_LIST_FOREACH(sd->tooltips, l, tt) elm_tooltip_theme(tt); EINA_LIST_FOREACH(sd->cursors, l, cur) elm_cursor_theme(cur); efl_ui_widget_theme_apply(obj); } EOLIAN static Efl_Ui_Theme_Apply _efl_ui_widget_theme_apply(Eo *obj, Elm_Widget_Smart_Data *_pd EINA_UNUSED) { _elm_widget_mirrored_reload(obj); if (elm_widget_disabled_get(obj)) elm_widget_disabled_internal(obj, elm_widget_disabled_get(obj)); return EFL_UI_THEME_APPLY_SUCCESS; } /** * @internal * * Returns the widget's mirrored mode. * * @param obj The widget. * @return mirrored mode of the object. * **/ EOLIAN static Eina_Bool _efl_ui_widget_efl_ui_base_mirrored_get(Eo *obj EINA_UNUSED, Elm_Widget_Smart_Data *sd) { return sd->is_mirrored; } /** * @internal * * Sets the widget's mirrored mode. * * @param obj The widget. * @param mirrored EINA_TRUE to set mirrored mode. EINA_FALSE to unset. */ EOLIAN static void _efl_ui_widget_efl_ui_base_mirrored_set(Eo *obj, Elm_Widget_Smart_Data *sd, Eina_Bool mirrored) { mirrored = !!mirrored; if (sd->is_mirrored == mirrored) return; sd->is_mirrored = mirrored; elm_widget_theme(obj); } /** * Returns the widget's mirrored mode setting. * * @param obj The widget. * @return mirrored mode setting of the object. * **/ EOLIAN static Eina_Bool _efl_ui_widget_efl_ui_base_mirrored_automatic_get(Eo *obj EINA_UNUSED, Elm_Widget_Smart_Data *sd) { return sd->mirrored_auto_mode; } /** * @internal * * Sets the widget's mirrored mode setting. * When widget in automatic mode, it follows the system mirrored mode set by * elm_mirrored_set(). * @param obj The widget. * @param automatic EINA_TRUE for auto mirrored mode. EINA_FALSE for manual. */ EOLIAN static void _efl_ui_widget_efl_ui_base_mirrored_automatic_set(Eo *obj, Elm_Widget_Smart_Data *sd, Eina_Bool automatic) { if (sd->mirrored_auto_mode != automatic) { sd->mirrored_auto_mode = automatic; if (automatic) { efl_ui_mirrored_set(obj, elm_config_mirrored_get()); } } } EOLIAN static void _efl_ui_widget_on_show_region_hook_set(Eo *obj EINA_UNUSED, Elm_Widget_Smart_Data *sd, void *data, Efl_Ui_Scrollable_On_Show_Region func, Eina_Free_Cb func_free_cb) { if ((sd->on_show_region_data == data) && (sd->on_show_region == func)) return; if (sd->on_show_region_data && sd->on_show_region_data_free) sd->on_show_region_data_free(sd->on_show_region_data); sd->on_show_region = func; sd->on_show_region_data = data; sd->on_show_region_data_free = func_free_cb; } /* * @internal * * Add myself as a sub object of parent object * * @see elm_widget_sub_object_add() */ EAPI Eina_Bool elm_widget_sub_object_parent_add(Evas_Object *sobj) { Eo *parent = NULL; parent = efl_parent_get(sobj); if (!efl_isa(parent, EFL_UI_WIDGET_CLASS)) { ERR("You passed a wrong parent parameter (%p %s). " "Elementary widget's parent should be an elementary widget.", parent, evas_object_type_get(parent)); return EINA_FALSE; } return elm_widget_sub_object_add(parent, sobj); } /* * @internal * * Add sobj to obj's sub object. * * What does elementary sub object mean? This is unique in elementary, it * handles overall elementary policies between parent and sub objects. * focus, access, deletion, theme, scale, mirror, scrollable child get, * translate, name find, display mode set, orientation set, tree dump * AUTOMATICALLY. * * @see elm_widget_sub_object_parent_add() */ EOLIAN static Eina_Bool _efl_ui_widget_widget_sub_object_add(Eo *obj, Elm_Widget_Smart_Data *sd, Evas_Object *sobj) { Eina_Bool mirrored, pmirrored = efl_ui_mirrored_get(obj); EINA_SAFETY_ON_TRUE_RETURN_VAL(obj == sobj, EINA_FALSE); if (sobj == sd->parent_obj) { /* in this case, sobj must be an elm widget, or something * very wrong is happening */ if (!_elm_widget_is(sobj)) return EINA_FALSE; if (!elm_widget_sub_object_del(sobj, obj)) return EINA_FALSE; WRN("You passed a parent object of obj = %p as the sub object = %p!", obj, sobj); } if (_elm_widget_is(sobj)) { ELM_WIDGET_DATA_GET(sobj, sdc); if (sdc->parent_obj == obj) goto end; if (sdc->parent_obj) { if (!elm_widget_sub_object_del(sdc->parent_obj, sobj)) return EINA_FALSE; } sdc->parent_obj = obj; _full_eval(sobj, sdc); if (!sdc->on_create) efl_ui_widget_on_orientation_update(sobj, sd->orient_mode); else sdc->orient_mode = sd->orient_mode; if (!sdc->on_create) { if (!sdc->disabled && (elm_widget_disabled_get(obj))) { efl_ui_widget_on_disabled_update(sobj, EINA_TRUE); } } _elm_widget_top_win_focused_set(sobj, sd->top_win_focused); /* update child focusable-ness on self and parents, now that a * focusable child got in */ if (!sd->child_can_focus && (_is_focusable(sobj))) { Elm_Widget_Smart_Data *sdp = sd; sdp->child_can_focus = EINA_TRUE; while (sdp->parent_obj) { sdp = efl_data_scope_get(sdp->parent_obj, MY_CLASS); if (sdp->child_can_focus) break; sdp->child_can_focus = EINA_TRUE; } } } else { void *data = evas_object_data_get(sobj, "elm-parent"); if (data) { if (data == obj) goto end; if (!elm_widget_sub_object_del(data, sobj)) return EINA_FALSE; } } sd->subobjs = eina_list_append(sd->subobjs, sobj); evas_object_data_set(sobj, "elm-parent", obj); _callbacks_add(sobj, obj); if (_elm_widget_is(sobj)) { ELM_WIDGET_DATA_GET(sobj, sdc); /* NOTE: In the following two lines, 'sobj' is correct. Do not change it. * Due to elementary's scale policy, scale and pscale can be different in * some cases. This happens when sobj's previous parent and new parent have * different scale value. * For example, if sobj's previous parent's scale is 5 and new parent's scale * is 2 while sobj's scale is 0. Then 'pscale' is 5 and 'scale' is 2. So we * need to reset sobj's scale to 5. * Note that each widget's scale is 0 by default. */ double scale, pscale = efl_gfx_scale_get(sobj); Elm_Theme *th, *pth = elm_widget_theme_get(sobj); scale = efl_gfx_scale_get(sobj); th = elm_widget_theme_get(sobj); mirrored = efl_ui_mirrored_get(sobj); if (!sdc->on_create) { if ((scale != pscale) || (th != pth) || (pmirrored != mirrored)) elm_widget_theme(sobj); } if (_is_focused(sobj)) _parents_focus(obj); elm_widget_display_mode_set(sobj, evas_object_size_hint_display_mode_get(obj)); if (_elm_config->atspi_mode && !sd->on_create) { Efl_Access *aparent; aparent = efl_access_parent_get(sobj); if (aparent) efl_access_children_changed_added_signal_emit(aparent, sobj); } } end: return EINA_TRUE; } EOLIAN static Eina_Bool _efl_ui_widget_widget_sub_object_del(Eo *obj, Elm_Widget_Smart_Data *sd, Evas_Object *sobj) { Evas_Object *sobj_parent; if (!sobj) return EINA_FALSE; EINA_SAFETY_ON_TRUE_RETURN_VAL(obj == sobj, EINA_FALSE); sobj_parent = evas_object_data_del(sobj, "elm-parent"); if (sobj_parent && sobj_parent != obj) { static int abort_on_warn = -1; ERR("removing sub object %p (%s) from parent %p (%s), " "but elm-parent is different %p (%s)!", sobj, elm_widget_type_get(sobj), obj, elm_widget_type_get(obj), sobj_parent, elm_widget_type_get(sobj_parent)); if (EINA_UNLIKELY(abort_on_warn == -1)) { if (getenv("ELM_ERROR_ABORT")) abort_on_warn = 1; else abort_on_warn = 0; } if (abort_on_warn == 1) abort(); return EINA_FALSE; } if (_elm_widget_is(sobj)) { if (_is_focused(sobj)) { elm_widget_tree_unfocusable_set(sobj, EINA_TRUE); elm_widget_tree_unfocusable_set(sobj, EINA_FALSE); } if ((sd->child_can_focus) && (_is_focusable(sobj))) { Evas_Object *parent = obj; /* update child focusable-ness on self and parents, now that a * focusable child is gone */ while (parent) { const Eina_List *l; Evas_Object *subobj; ELM_WIDGET_DATA_GET(parent, sdp); sdp->child_can_focus = EINA_FALSE; EINA_LIST_FOREACH(sdp->subobjs, l, subobj) { if ((subobj != sobj) && (_is_focusable(subobj))) { sdp->child_can_focus = EINA_TRUE; break; } } /* break again, child_can_focus went back to * original value */ if (sdp->child_can_focus) break; parent = sdp->parent_obj; } } if (_elm_config->atspi_mode && !sd->on_destroy) { Efl_Access *aparent; aparent = efl_access_parent_get(sobj); if (aparent) efl_access_children_changed_del_signal_emit(aparent, sobj); } ELM_WIDGET_DATA_GET(sobj, sdc); sdc->parent_obj = NULL; _full_eval(sobj, sdc); } if (sd->resize_obj == sobj) sd->resize_obj = NULL; sd->subobjs = eina_list_remove(sd->subobjs, sobj); _callbacks_del(sobj, obj); return EINA_TRUE; } /* protected function - for widget developers only */ EOLIAN static void _efl_ui_widget_resize_object_set(Eo *obj, Elm_Widget_Smart_Data *sd, Eo *sobj) { Evas_Object *parent; if (sd->resize_obj == sobj) return; EINA_SAFETY_ON_TRUE_RETURN(sobj && !efl_isa(sobj, EFL_CANVAS_OBJECT_CLASS)); // orphan previous resize obj if (sd->resize_obj) { evas_object_clip_unset(sd->resize_obj); evas_object_smart_member_del(sd->resize_obj); if (_elm_widget_is(sd->resize_obj)) { if (_is_focused(sd->resize_obj)) _parents_unfocus(obj); } elm_widget_sub_object_del(obj, sd->resize_obj); } sd->resize_obj = sobj; if (!sobj) return; // orphan new resize obj parent = evas_object_data_get(sobj, "elm-parent"); if (parent && parent != obj) { ELM_WIDGET_DATA_GET(parent, sdp); /* should be there, just being paranoid */ if (sdp) { if (sdp->resize_obj == sobj) elm_widget_resize_object_set(parent, NULL); else elm_widget_sub_object_del(parent, sobj); } } elm_widget_sub_object_add(obj, sobj); evas_object_smart_member_add(sobj, obj); _smart_reconfigure(sd); } /* * @internal * * WARNING: the programmer is responsible, in the scenario of * exchanging a hover object, of cleaning the old hover "target" * before */ EAPI void elm_widget_hover_object_set(Eo *obj, Evas_Object *sobj) { Elm_Widget_Smart_Data *sd = efl_data_scope_safe_get(obj, MY_CLASS); if (!sd) return; if (sd->hover_obj) { _callbacks_del(sd->hover_obj, obj); } sd->hover_obj = sobj; if (sd->hover_obj) { _callbacks_add(sobj, obj); _smart_reconfigure(sd); } } EOLIAN static void _efl_ui_widget_focus_allow_set(Eo *obj, Elm_Widget_Smart_Data *sd, Eina_Bool can_focus) { can_focus = !!can_focus; if (sd->can_focus == can_focus) return; sd->can_focus = can_focus; if (sd->can_focus) { /* update child_can_focus of parents */ Evas_Object *o = obj; for (;;) { o = elm_widget_parent_get(o); if (!o) break; ELM_WIDGET_DATA_GET(o, sdp); if (!sdp || sdp->child_can_focus) break; sdp->child_can_focus = EINA_TRUE; } efl_event_callback_array_add(obj, focus_callbacks(), NULL); } else { // update child_can_focus of parents */ Evas_Object *parent = elm_widget_parent_get(obj); while (parent) { const Eina_List *l; Evas_Object *subobj; ELM_WIDGET_DATA_GET(parent, sdp); sdp->child_can_focus = EINA_FALSE; EINA_LIST_FOREACH(sdp->subobjs, l, subobj) { if (_is_focusable(subobj)) { sdp->child_can_focus = EINA_TRUE; break; } } /* break again, child_can_focus went back to * original value */ if (sdp->child_can_focus) break; parent = sdp->parent_obj; } efl_event_callback_array_del(obj, focus_callbacks(), NULL); } if (efl_finalized_get(obj)) _full_eval(obj, sd); } EOLIAN static Eina_Bool _efl_ui_widget_focus_allow_get(Eo *obj EINA_UNUSED, Elm_Widget_Smart_Data *sd) { return sd->can_focus; } EAPI Eina_Bool elm_widget_child_can_focus_get(const Eo *obj) { Elm_Widget_Smart_Data *sd = efl_data_scope_safe_get(obj, MY_CLASS); if (!sd) return EINA_FALSE; return sd->child_can_focus; } /** * @internal * * This API makes the widget object and its children to be unfocusable. * * This API can be helpful for an object to be deleted. * When an object will be deleted soon, it and its children may not * want to get focus (by focus reverting or by other focus controls). * Then, just use this API before deleting. * * @param obj The widget root of sub-tree * @param tree_unfocusable If true, set the object sub-tree as unfocusable * * @ingroup Widget */ EAPI void elm_widget_tree_unfocusable_set(Eo *obj, Eina_Bool tree_unfocusable) { Elm_Widget_Smart_Data *sd = efl_data_scope_safe_get(obj, MY_CLASS); if (!sd) return; tree_unfocusable = !!tree_unfocusable; if (sd->tree_unfocusable == tree_unfocusable) return; sd->tree_unfocusable = tree_unfocusable; _elm_widget_focus_tree_unfocusable_handle(obj); //focus state eval on all children _full_eval_children(obj, sd); } /** * @internal * * This returns true, if the object sub-tree is unfocusable. * * @param obj The widget root of sub-tree * @return EINA_TRUE if the object sub-tree is unfocusable * * @ingroup Widget */ EAPI Eina_Bool elm_widget_tree_unfocusable_get(const Eo *obj) { Elm_Widget_Smart_Data *sd = efl_data_scope_safe_get(obj, MY_CLASS); if (!sd) return EINA_FALSE; return sd->tree_unfocusable; } /** * @internal * * Get the list of focusable child objects. * * This function retruns list of child objects which can get focus. * * @param obj The parent widget * @retrun list of focusable child objects. * * @ingroup Widget */ EAPI Eina_List* elm_widget_can_focus_child_list_get(const Eo *obj) { Elm_Widget_Smart_Data *sd = efl_data_scope_safe_get(obj, MY_CLASS); const Eina_List *l; Eina_List *child_list = NULL; Evas_Object *child; if (!sd) return NULL; EINA_LIST_FOREACH(sd->subobjs, l, child) { if (!_elm_widget_is(child)) continue; if ((elm_widget_can_focus_get(child)) && (evas_object_visible_get(child)) && (!elm_widget_disabled_get(child))) child_list = eina_list_append(child_list, child); else { Eina_List *can_focus_list; can_focus_list = elm_widget_can_focus_child_list_get(child); if (can_focus_list) child_list = eina_list_merge(child_list, can_focus_list); } } return child_list; } /** @internal */ EAPI void elm_widget_highlight_ignore_set(Eo *obj, Eina_Bool ignore) { Elm_Widget_Smart_Data *sd = efl_data_scope_safe_get(obj, MY_CLASS); if (!sd) return; sd->highlight_ignore = !!ignore; } /** @internal */ EAPI Eina_Bool elm_widget_highlight_ignore_get(const Eo *obj) { Elm_Widget_Smart_Data *sd = efl_data_scope_safe_get(obj, MY_CLASS); if (!sd) return EINA_FALSE; return sd->highlight_ignore; } /** @internal */ EAPI void elm_widget_highlight_in_theme_set(Eo *obj, Eina_Bool highlight) { Elm_Widget_Smart_Data *sd = efl_data_scope_safe_get(obj, MY_CLASS); if (!sd) return; sd->highlight_in_theme = !!highlight; /* FIXME: if focused, it should switch from one mode to the other */ } void _elm_widget_highlight_in_theme_update(Eo *obj) { Evas_Object *top = elm_widget_top_get(obj); if (top && efl_isa(top, EFL_UI_WIN_CLASS)) { _elm_win_focus_highlight_in_theme_update( top, elm_widget_highlight_in_theme_get(obj)); } } /** @internal */ EAPI Eina_Bool elm_widget_highlight_in_theme_get(const Eo *obj) { Elm_Widget_Smart_Data *sd = efl_data_scope_safe_get(obj, MY_CLASS); if (!sd) return EINA_FALSE; return sd->highlight_in_theme; } /** @internal */ EAPI void elm_widget_access_highlight_in_theme_set(Eo *obj, Eina_Bool highlight) { Elm_Widget_Smart_Data *sd = efl_data_scope_safe_get(obj, MY_CLASS); if (!sd) return; sd->access_highlight_in_theme = !!highlight; } /** @internal */ EAPI Eina_Bool elm_widget_access_highlight_in_theme_get(const Eo *obj) { Elm_Widget_Smart_Data *sd = efl_data_scope_safe_get(obj, MY_CLASS); if (!sd) return EINA_FALSE; return sd->access_highlight_in_theme; } /** @internal */ EAPI Eina_Bool elm_widget_highlight_get(const Eo *obj) { Elm_Widget_Smart_Data *sd = efl_data_scope_safe_get(obj, MY_CLASS); if (!sd) return EINA_FALSE; return sd->highlighted; } EOLIAN static Evas_Object* _efl_ui_widget_widget_top_get(Eo *obj, Elm_Widget_Smart_Data *sd EINA_UNUSED) { Efl_Ui_Widget *parent = elm_widget_parent_get(obj); if (parent) { if (!efl_isa(parent, EFL_UI_WIDGET_CLASS)) return NULL; return efl_ui_widget_top_get(parent); } return obj; } EAPI Eina_Bool elm_widget_is(const Evas_Object *obj) { return _elm_widget_is(obj); } EAPI Evas_Object * elm_widget_parent_widget_get(const Evas_Object *obj) { Evas_Object *parent; if (_elm_widget_is(obj)) { ELM_WIDGET_DATA_GET(obj, sd); if (!sd) return NULL; parent = sd->parent_obj; } else { parent = evas_object_data_get(obj, "elm-parent"); if (!parent) parent = evas_object_smart_parent_get(obj); } while (parent) { Evas_Object *elm_parent; if (_elm_widget_is(parent)) break; elm_parent = evas_object_data_get(parent, "elm-parent"); if (elm_parent) parent = elm_parent; else parent = evas_object_smart_parent_get(parent); } return parent; } EAPI void elm_widget_event_callback_add(Eo *obj, Elm_Event_Cb func, const void *data) { API_ENTRY return; EINA_SAFETY_ON_NULL_RETURN(func); Elm_Event_Cb_Data *ecb = ELM_NEW(Elm_Event_Cb_Data); if (!ecb) { ERR("Failed to allocate memory"); return; } ecb->func = func; ecb->data = data; sd->event_cb = eina_list_append(sd->event_cb, ecb); } EAPI void * elm_widget_event_callback_del(Eo *obj, Elm_Event_Cb func, const void *data) { API_ENTRY return NULL; EINA_SAFETY_ON_NULL_RETURN_VAL(func, NULL); Eina_List *l; Elm_Event_Cb_Data *ecd; EINA_LIST_FOREACH(sd->event_cb, l, ecd) if ((ecd->func == func) && (ecd->data == data)) { free(ecd); sd->event_cb = eina_list_remove_list(sd->event_cb, l); return (void *)data; } return NULL; } static void _propagate_event(void *data EINA_UNUSED, const Efl_Event *eo_event) { Evas_Object *obj = eo_event->object; Evas_Object *parent = obj; Elm_Event_Cb_Data *ecd; Eina_List *l, *l_prev; if ((evas_focus_get(evas_object_evas_get(obj)) != elm_widget_top_get(obj)) && efl_isa(obj, EFL_UI_WIN_CLASS)) return; while (parent && !efl_input_processed_get(eo_event->info)) { Elm_Widget_Smart_Data *sd = efl_data_scope_safe_get(parent, MY_CLASS); if (!sd) return; if (elm_widget_disabled_get(obj)) { parent = sd->parent_obj; continue; } if (efl_ui_widget_event(parent, eo_event, obj)) return; EINA_LIST_FOREACH_SAFE(sd->event_cb, l, l_prev, ecd) { if (_propagate_event_legacy(parent, eo_event, obj, ecd)) return; } parent = sd->parent_obj; } } double _elm_widget_focus_direction_weight_get(const Evas_Object *obj1, const Evas_Object *obj2, double degree) { Evas_Coord obj_x1, obj_y1, w1, h1, obj_x2, obj_y2, w2, h2; double x1, yy1, x2, yy2, xx1, yyy1, xx2, yyy2; double ax, ay, cx, cy; double weight = -1.0, g = 0.0; if (obj1 == obj2) return 0.0; degree -= 90.0; while (degree >= 360.0) degree -= 360.0; while (degree < 0.0) degree += 360.0; evas_object_geometry_get(obj1, &obj_x1, &obj_y1, &w1, &h1); cx = obj_x1 + (w1 / 2.0); cy = obj_y1 + (h1 / 2.0); evas_object_geometry_get(obj2, &obj_x2, &obj_y2, &w2, &h2); /* For overlapping cases. */ if (ELM_RECTS_INTERSECT(obj_x1, obj_y1, w1, h1, obj_x2, obj_y2, w2, h2)) return 0.0; /* Change all points to relative one. */ x1 = obj_x1 - cx; xx1 = x1 + w1; yy1 = obj_y1 - cy; yyy1 = yy1 + h1; x2 = obj_x2 - cx; xx2 = x2 + w2; yy2 = obj_y2 - cy; yyy2 = yy2 + h2; /* Get crossing points (ax, ay) between obj1 and a line extending * to the direction of current degree. */ if (degree == 0.0) { ax = xx1; ay = 0.0; } else if (degree == 90.0) { ax = 0.0; ay = yyy1; } else if (degree == 180.0) { ax = x1; ay = 0.0; } else if (degree == 270.0) { ax = 0.0; ay = yy1; } else { g = tan(degree * (M_PI / 180.0)); if ((degree > 0.0) && (degree < 90.0)) { ay = g * xx1; if (ay <= yyy1) ax = xx1; else { ax = yyy1 / g; ay = yyy1; } } else if ((degree > 90.0) && (degree < 180.0)) { ay = g * x1; if (ay <= yyy1) ax = x1; else { ax = yyy1 / g; ay = yyy1; } } else if ((degree > 180.0) && (degree < 270.0)) { ay = g * x1; if (ay >= yy1) ax = x1; else { ax = yy1 / g; ay = yy1; } } else { ay = g * xx1; if (ay >= yy1) ax = xx1; else { ax = yy1 / g; ay = yy1; } } } /* Filter obj2, if it is not in the specific derection. */ int i = 0; double rx[4] = {0.0, 0.0, 0.0, 0.0}, ry[4] = {0.0, 0.0, 0.0, 0.0}; double t1, t2, u1, v1, u2, v2; if ((degree == 45.0) || (degree == 225.0) || (degree == 135.0) || (degree == 315.0)) { u1 = 1.0; v1 = 0.0; u2 = 0.0; v2 = 1.0; } else { double g2 = tan((degree + 45.0) * (M_PI / 180.0)); u1 = (-1.0 * g2); u2 = (1.0 / g2); v1 = v2 = 1.0; } t1 = (u1 * ax) + (v1 * ay); t2 = (u2 * ax) + (v2 * ay); #define _R(x) (int)((x + 0.05) * 10.0) if ((_R(t1 * ((u1 * x2) + (v1 * yy2))) > 0) && (_R(t2 * ((u2 * x2) + (v2 * yy2))) > 0)) { rx[i] = x2; ry[i++] = yy2; } if ((_R(t1 * ((u1 * x2) + (v1 * yyy2))) > 0) && (_R(t2 * ((u2 * x2) + (v2 * yyy2))) > 0)) { rx[i] = x2; ry[i++] = yyy2; } if ((_R(t1 * ((u1 * xx2) + (v1 * yy2))) > 0) && (_R(t2 * ((u2 * xx2) + (v2 * yy2))) > 0)) { rx[i] = xx2; ry[i++] = yy2; } if ((_R(t1 * ((u1 * xx2) + (v1 * yyy2))) > 0) && (_R(t2 * ((u2 * xx2) + (v2 * yyy2))) > 0)) { rx[i] = xx2; ry[i++] = yyy2; } if (i == 0) { if (degree == 0.0) { if ((_R(xx2) < 0) || (_R(yy2) > 0) || (_R(yyy2) < 0)) return 0.0; } else if (degree == 90.0) { if ((_R(yyy2) < 0) || (_R(x2) > 0) || (_R(xx2) < 0)) return 0.0; } else if (degree == 180.0) { if ((_R(x2) > 0) || (_R(yy2) > 0) || (_R(yyy2) < 0)) return 0.0; } else if (degree == 270.0) { if ((_R(yy2) > 0) || (_R(x2) > 0) || (_R(xx2) < 0)) return 0.0; } else { if ((_R(g * x2) >= _R(yy2)) && (_R((g * x2)) <= _R(yyy2))) { if (!((_R(ax * x2) > 0) && (_R(ay * (g * x2)) > 0))) return 0.0; } else if ((_R(g * xx2) >= _R(yy2)) && (_R((g * xx2)) <= _R(yyy2))) { if (!((_R(ax * xx2) > 0) && (_R(ay * (g * xx2)) > 0))) return 0.0; } else if ((_R((1.0 / g) * yy2) >= _R(xx2)) && (_R((1.0 / g) * yy2) <= _R(xx2))) { if (!((_R(ax * ((1.0 / g) * yy2)) > 0) && (_R(ay * yy2) > 0))) return 0.0; } else if ((_R((1.0 / g) * yyy2) >= _R(xx2)) && (_R((1.0 / g) * yyy2) <= _R(xx2))) { if (!((_R(ax * ((1.0 / g) * yyy2)) > 0) && (_R(ay * yyy2) > 0))) return 0.0; } else return 0.0; } } /* Calculate the weight for obj2. */ if (degree == 0.0) { if (_R(xx1) > _R(x2)) weight = -1.0; else if ((_R(yy2) >= _R(yy1)) && (_R(yyy2) <= _R(yyy1))) weight = (x2 - xx1) * (x2 - xx1); else if (_R(yy2) > 0) weight = ((x2 - xx1) * (x2 - xx1)) + (yy2 * yy2); else if (_R(yyy2) < 0) weight = ((x2 - xx1) * (x2 - xx1)) + (yyy2 * yyy2); else weight = (x2 - xx1) * (x2 - xx1); } else if (degree == 90.0) { if (_R(yyy1) > _R(yy2)) weight = -1.0; else if ((_R(x2) >= _R(x1)) && (_R(xx2) <= _R(xx1))) weight = (yy2 - yyy1) * (yy2 - yyy1); else if (_R(x2) > 0) weight = (x2 * x2) + ((yy2 - yyy1) * (yy2 - yyy1)); else if (_R(xx2) < 0) weight = (xx2 * xx2) + ((yy2 - yyy1) * (yy2 - yyy1)); else weight = (yy2 - yyy1) * (yy2 - yyy1); } else if (degree == 180.0) { if (_R(x1) < _R(xx2)) weight = -1.0; else if ((_R(yy2) >= _R(yy1)) && (_R(yyy2) <= _R(yyy1))) weight = (x1 - xx2) * (x1 - xx2); else if (_R(yy2) > 0) weight = ((x1 - xx2) * (x1 - xx2)) + (yy2 * yy2); else if (_R(yyy2) < 0) weight = ((x1 - xx2) * (x1 - xx2)) + (yyy2 * yyy2); else weight = (x1 - xx2) * (x1 - xx2); } else if (degree == 270.0) { if (_R(yy1) < _R(yyy2)) weight = -1.0; else if ((_R(x2) >= _R(x1)) && (_R(xx2) <= _R(xx1))) weight = (yy1 - yyy2) * (yy1 - yyy2); else if (_R(x2) > 0) weight = (x2 * x2) + ((yy1 - yyy2) * (yy1 - yyy2)); else if (_R(xx2) < 0) weight = (xx2 * xx2) + ((yy1 - yyy2) * (yy1 - yyy2)); else weight = (yy1 - yyy2) * (yy1 - yyy2); } else { int j = 0, k = 0; double sx[4] = {0.0, 0.0, 0.0, 0.0}, sy[4] = {0.0, 0.0, 0.0, 0.0}; double t_weight[4] = {-1.0, -1.0, -1.0, -1.0}; if ((_R(g * x2) >= _R(yy2)) && (_R(g * x2) <= _R(yyy2))) { sx[j] = x2; sy[j] = g * x2; t_weight[j++] = ((ax - x2) * (ax - x2)) + ((ay - (g * x2)) * (ay - (g * x2))); } if ((_R(g * xx2) >= _R(yy2)) && (_R(g * xx2) <= _R(yyy2))) { sx[j] = xx2; sy[j] = g * xx2; t_weight[j++] = ((ax - xx2) * (ax - xx2)) + ((ay - (g * xx2)) * (ay - (g * xx2))); } if ((_R((1.0 / g) * yy2) >= _R(x2)) && (_R((1.0 / g) * yy2) <= _R(xx2))) { sx[j] = (1.0 / g) * yy2; sy[j] = yy2; t_weight[j++] = ((ax - ((1.0 / g) * yy2)) * (ax - ((1.0 / g) * yy2))) + ((ay - yy2) * (ay - yy2)); } if ((_R((1.0 / g) * yyy2) >= _R(x2)) && (_R((1.0 / g) * yyy2) <= _R(xx2))) { sx[j] = (1.0 / g) * yyy2; sy[j] = yyy2; t_weight[j++] = ((ax - ((1.0 / g) * yyy2)) * (ax - ((1.0 / g) * yyy2))) + ((ay - yyy2) * (ay - yyy2)); } if ((j > 2) || ((j == 2) && ((_R(sx[0]) != _R(sx[1])) || (_R(sy[0]) != _R(sy[1]))))) { for (; k < j; k++) { if (_R(t_weight[k]) == 0) return -1.0; if ((1 / weight) < (1 / t_weight[k])) weight = t_weight[k]; } } else { for (; k < i; k++) { double ccx, ccy, t1_weight, x_diff, y_diff; ccx = ((1.0 / g) * rx[k] + ry[k]) / (g + (1.0 / g)); ccy = g * ccx; x_diff = rx[k] - ccx; if (x_diff < 0) x_diff *= -1.0; y_diff = ry[k] - ccy; if (y_diff < 0) y_diff *= -1.0; t1_weight = (((ax - ccx) * (ax - ccx)) + ((ay - ccy) * (ay - ccy))) + ((x_diff * x_diff * x_diff) + (y_diff * y_diff * y_diff)); if ((_R(t1_weight) != 0) && ((1 / weight) < (1 / t1_weight))) weight = t1_weight; } } } /* Return the current object's weight. */ if (weight == -1.0) return 0.0; if (_R(weight) == 0) return -1.0; #undef _R return 1.0 / weight; } /** @internal */ EAPI void elm_widget_parent_highlight_set(Eo *obj, Eina_Bool highlighted) { Elm_Widget_Smart_Data *sd =efl_data_scope_safe_get(obj, MY_CLASS); if (!sd) return; highlighted = !!highlighted; Evas_Object *o = elm_widget_parent_get(obj); if (o) elm_widget_parent_highlight_set(o, highlighted); sd->highlighted = highlighted; } EOLIAN static Evas_Object* _efl_ui_widget_widget_parent_get(Eo *obj EINA_UNUSED, Elm_Widget_Smart_Data *sd) { return sd->parent_obj; } void _elm_widget_focus_auto_show(Evas_Object *obj) { Evas_Object *top = elm_widget_top_get(obj); if (top && efl_isa(top, EFL_UI_WIN_CLASS)) _elm_win_focus_auto_show(top); } void _elm_widget_top_win_focused_set(Evas_Object *obj, Eina_Bool top_win_focused) { const Eina_List *l; Evas_Object *child; API_ENTRY return; if (sd->top_win_focused == top_win_focused) return; EINA_LIST_FOREACH(sd->subobjs, l, child) { if (elm_widget_is(child)) _elm_widget_top_win_focused_set(child, top_win_focused); } sd->top_win_focused = top_win_focused; if (sd->focused && !sd->top_win_focused) - efl_ui_widget_on_focus_update(obj); + efl_ui_focus_object_on_focus_update(obj); } Eina_Bool _elm_widget_top_win_focused_get(const Evas_Object *obj) { API_ENTRY return EINA_FALSE; return sd->top_win_focused; } static void _elm_widget_disabled_eval(const Evas_Object *obj, Eina_Bool disabled) { const Eina_List *l; Evas_Object *child; ELM_WIDGET_DATA_GET(obj, sd); EINA_LIST_FOREACH(sd->subobjs, l, child) { if (elm_widget_is(child)) { efl_ui_widget_on_disabled_update(child, disabled); _elm_widget_disabled_eval(child, disabled); } } } static void elm_widget_disabled_internal(Eo *obj, Eina_Bool disabled) { if (!disabled && elm_widget_disabled_get(elm_widget_parent_get(obj))) return; efl_ui_widget_on_disabled_update(obj, disabled); _elm_widget_disabled_eval(obj, disabled); } EOLIAN static void _efl_ui_widget_disabled_set(Eo *obj, Elm_Widget_Smart_Data *sd, Eina_Bool disabled) { if (sd->disabled == disabled) return; sd->disabled = !!disabled; elm_widget_disabled_internal(obj, disabled); if (efl_finalized_get(obj)) _full_eval_children(obj, sd); } EOLIAN static Eina_Bool _efl_ui_widget_disabled_get(Eo *obj, Elm_Widget_Smart_Data *sd) { Eo *parent; if (sd->disabled) return EINA_TRUE; if ((parent = elm_widget_parent_get(obj)) != NULL) return elm_widget_disabled_get(parent); return EINA_FALSE; } EOLIAN static void _efl_ui_widget_show_region_set(Eo *obj, Elm_Widget_Smart_Data *sd, Eina_Rect sr, Eina_Bool forceshow) { Evas_Object *parent_obj, *child_obj; Evas_Coord px, py, cx, cy, nx = 0, ny = 0; evas_smart_objects_calculate(evas_object_evas_get(obj)); if (!forceshow && eina_rectangle_equal(&sr.rect, &sd->show_region.rect)) return; sd->show_region = sr; if (sd->on_show_region) { sd->on_show_region(sd->on_show_region_data, obj, sr); if (_elm_scrollable_is(obj)) { elm_interface_scrollable_content_pos_get(obj, &nx, &ny); sr.x -= nx; sr.y -= ny; } } do { parent_obj = sd->parent_obj; child_obj = sd->obj; if ((!parent_obj) || (!_elm_widget_is(parent_obj))) break; sd = efl_data_scope_get(parent_obj, MY_CLASS); if (!sd) break; evas_object_geometry_get(parent_obj, &px, &py, NULL, NULL); evas_object_geometry_get(child_obj, &cx, &cy, NULL, NULL); sr.x += (cx - px); sr.y += (cy - py); sd->show_region = sr; if (sd->on_show_region) sd->on_show_region(sd->on_show_region_data, parent_obj, sr); } while (parent_obj); } EOLIAN static Eina_Rect _efl_ui_widget_show_region_get(Eo *obj EINA_UNUSED, Elm_Widget_Smart_Data *sd) { return (Eina_Rect) sd->show_region; } /** * @internal * * Get the focus region of the given widget. * * @return The region to show. If it's not a valid rectangle it will not show. * * The focus region is the area of a widget that should brought into the * visible area when the widget is focused. Mostly used to show the part of * an entry where the cursor is, for example. The area returned is relative * to the object @p obj. * * @param obj The widget object * @return The region to show, in relative coordinates. If it's not a valid * rectangle (i.e. w or h <= 0) it will be ignored. * * @ingroup Widget */ EOLIAN static Eina_Rect _efl_ui_widget_interest_region_get(Eo *obj, Elm_Widget_Smart_Data *_pd EINA_UNUSED) { Eina_Rect r = {}; r.size = efl_gfx_size_get(obj); return r; } EOLIAN static void _efl_ui_widget_scroll_hold_push(Eo *obj, Elm_Widget_Smart_Data *sd) { sd->scroll_hold++; if (sd->scroll_hold == 1) { if (_elm_scrollable_is(obj)) elm_interface_scrollable_hold_set(obj, EINA_TRUE); else { Evas_Object *child; Eina_List *l; EINA_LIST_FOREACH(sd->subobjs, l, child) { if (elm_widget_is(child) && _elm_scrollable_is(child)) elm_interface_scrollable_hold_set(child, EINA_TRUE); } } } if (sd->parent_obj) efl_ui_widget_scroll_hold_push(sd->parent_obj); // FIXME: on delete/reparent hold pop } EOLIAN static void _efl_ui_widget_scroll_hold_pop(Eo *obj, Elm_Widget_Smart_Data *sd) { sd->scroll_hold--; if (!sd->scroll_hold) { if (_elm_scrollable_is(obj)) elm_interface_scrollable_hold_set(obj, EINA_FALSE); else { Evas_Object *child; Eina_List *l; EINA_LIST_FOREACH(sd->subobjs, l, child) { if (elm_widget_is(child) && _elm_scrollable_is(child)) elm_interface_scrollable_hold_set(child, EINA_FALSE); } } } if (sd->parent_obj) efl_ui_widget_scroll_hold_pop(sd->parent_obj); if (sd->scroll_hold < 0) sd->scroll_hold = 0; } EAPI int elm_widget_scroll_hold_get(const Eo *obj) { Elm_Widget_Smart_Data *sd = efl_data_scope_safe_get(obj, MY_CLASS); if (!sd) return 0; return sd->scroll_hold; } EOLIAN static void _efl_ui_widget_scroll_freeze_push(Eo *obj, Elm_Widget_Smart_Data *sd) { sd->scroll_freeze++; if (sd->scroll_freeze == 1) { if (_elm_scrollable_is(obj)) elm_interface_scrollable_freeze_set(obj, EINA_TRUE); else { Evas_Object *child; Eina_List *l; EINA_LIST_FOREACH(sd->subobjs, l, child) { if (elm_widget_is(child) && _elm_scrollable_is(child)) elm_interface_scrollable_freeze_set(child, EINA_TRUE); } } } if (sd->parent_obj) efl_ui_widget_scroll_freeze_push(sd->parent_obj); // FIXME: on delete/reparent freeze pop } EOLIAN static void _efl_ui_widget_scroll_freeze_pop(Eo *obj, Elm_Widget_Smart_Data *sd) { sd->scroll_freeze--; if (!sd->scroll_freeze) { if (_elm_scrollable_is(obj)) elm_interface_scrollable_freeze_set(obj, EINA_FALSE); else { Evas_Object *child; Eina_List *l; EINA_LIST_FOREACH(sd->subobjs, l, child) { if (elm_widget_is(child) && _elm_scrollable_is(child)) elm_interface_scrollable_freeze_set(child, EINA_FALSE); } } } if (sd->parent_obj) efl_ui_widget_scroll_freeze_pop(sd->parent_obj); if (sd->scroll_freeze < 0) sd->scroll_freeze = 0; } EAPI int elm_widget_scroll_freeze_get(const Eo *obj) { Elm_Widget_Smart_Data *sd = efl_data_scope_safe_get(obj, MY_CLASS); if (!sd) return 0; return sd->scroll_freeze; } EOLIAN static void _efl_ui_widget_efl_gfx_scale_set(Eo *obj, Elm_Widget_Smart_Data *sd, double scale) { if (scale < 0.0) scale = 0.0; if (sd->scale != scale) { sd->scale = scale; elm_widget_theme(obj); } } EOLIAN static double _efl_ui_widget_efl_gfx_scale_get(Eo *obj EINA_UNUSED, Elm_Widget_Smart_Data *sd) { // FIXME: save walking up the tree by storing/caching parent scale if (sd->scale == 0.0) { if (sd->parent_obj && elm_widget_is(sd->parent_obj)) { return efl_gfx_scale_get(sd->parent_obj); } else { return 1.0; } } return sd->scale; } EAPI void elm_widget_theme_set(Evas_Object *obj, Elm_Theme *th) { ELM_WIDGET_DATA_GET_OR_RETURN(obj, sd); Eina_Bool apply = EINA_FALSE; if (sd->theme != th) { if (elm_widget_theme_get(obj) != th) apply = EINA_TRUE; if (sd->theme) elm_theme_free(sd->theme); sd->theme = th; if (th) th->ref++; if (apply) elm_widget_theme(obj); } } EAPI void elm_widget_part_text_set(Eo *obj, const char *part, const char *label) { /* legacy support: combobox was special (internal entry is text object). */ if (efl_isa(obj, ELM_COMBOBOX_CLASS)) _elm_combobox_part_text_set(obj, part, label); else if (efl_isa(obj, EFL_UI_LAYOUT_CLASS)) elm_layout_text_set(obj, part, label); } EAPI const char* elm_widget_part_text_get(const Eo *obj, const char *part) { /* legacy support: combobox was special (internal entry is text object). */ if (efl_isa(obj, ELM_COMBOBOX_CLASS)) return _elm_combobox_part_text_get(obj, part); else if (efl_isa(obj, EFL_UI_LAYOUT_CLASS)) return elm_layout_text_get(obj, part); return NULL; } static Elm_Translate_String_Data * _translate_string_data_get(Eina_Inlist *translate_strings, const char *part) { Elm_Translate_String_Data *ts; Eina_Stringshare *str; if (!translate_strings) return NULL; str = eina_stringshare_add(part); EINA_INLIST_FOREACH(translate_strings, ts) { if (ts->id == str) break; } eina_stringshare_del(str); return ts; } static Elm_Translate_String_Data * _part_text_translatable_set(Eina_Inlist **translate_strings, const char *part, Eina_Bool translatable, Eina_Bool preset) { Eina_Inlist *t; Elm_Translate_String_Data *ts; t = *translate_strings; ts = _translate_string_data_get(t, part); if (translatable) { if (!ts) { ts = ELM_NEW(Elm_Translate_String_Data); if (!ts) return NULL; ts->id = eina_stringshare_add(part); t = eina_inlist_append(t, (Eina_Inlist*) ts); } if (preset) ts->preset = EINA_TRUE; } //Delete this exist one if this part has been not preset. //see elm_widget_part_text_translatable_set() else if (ts && ((preset) || (!ts->preset))) { t = eina_inlist_remove(t, EINA_INLIST_GET(ts)); eina_stringshare_del(ts->id); eina_stringshare_del(ts->domain); eina_stringshare_del(ts->string); ELM_SAFE_FREE(ts, free); } *translate_strings = t; return ts; } /* internal */ void elm_widget_part_translatable_text_set(Eo *obj, const char *part, const char *label, const char *domain) { Elm_Translate_String_Data *ts; Elm_Widget_Smart_Data *sd; sd = efl_data_scope_safe_get(obj, MY_CLASS); if (!sd) return; if (!label) { _part_text_translatable_set(&sd->translate_strings, part, EINA_FALSE, EINA_FALSE); } else { ts = _part_text_translatable_set(&sd->translate_strings, part, EINA_TRUE, EINA_FALSE); if (!ts) return; if (!ts->string) ts->string = eina_stringshare_add(label); else eina_stringshare_replace(&ts->string, label); if (!ts->domain) ts->domain = eina_stringshare_add(domain); else eina_stringshare_replace(&ts->domain, domain); #ifdef HAVE_GETTEXT if (label[0]) label = dgettext(domain, label); #endif } sd->on_translate = EINA_TRUE; elm_widget_part_text_set(obj, part, label); sd->on_translate = EINA_FALSE; } /* legacy only */ EAPI void elm_widget_domain_part_text_translatable_set(Eo *obj, const char *part, const char *domain, Eina_Bool translatable) { Elm_Translate_String_Data *ts; Elm_Widget_Smart_Data *sd; const char *text = NULL; sd = efl_data_scope_safe_get(obj, MY_CLASS); if (!sd) return; ts = _part_text_translatable_set(&sd->translate_strings, part, translatable, EINA_TRUE); if (!ts) return; if (!ts->domain) ts->domain = eina_stringshare_add(domain); else eina_stringshare_replace(&ts->domain, domain); text = elm_widget_part_text_get(obj, part); if (!text || !text[0]) return; if (!ts->string) ts->string = eina_stringshare_add(text); //Try to translate text since we don't know the text is already translated. #ifdef HAVE_GETTEXT text = dgettext(domain, text); #endif sd->on_translate = EINA_TRUE; elm_widget_part_text_set(obj, part, text); sd->on_translate = EINA_FALSE; } /* internal */ const char * elm_widget_part_translatable_text_get(const Eo *obj, const char *part, const char **domain) { Elm_Widget_Smart_Data *sd; Elm_Translate_String_Data *ts; if (domain) *domain = NULL; sd = efl_data_scope_safe_get(obj, MY_CLASS); if (!sd) return NULL; ts = _translate_string_data_get(sd->translate_strings, part); if (!ts) return NULL; if (domain) *domain = ts->domain; return ts->string; } EOLIAN static void _efl_ui_widget_efl_ui_translatable_translation_update(Eo *obj EINA_UNUSED, Elm_Widget_Smart_Data *sd) { const Eina_List *l; Evas_Object *child; EINA_LIST_FOREACH(sd->subobjs, l, child) { if (elm_widget_is(child)) efl_ui_translatable_translation_update(child); } if (sd->hover_obj) efl_ui_translatable_translation_update(sd->hover_obj); #ifdef HAVE_GETTEXT Elm_Translate_String_Data *ts; EINA_INLIST_FOREACH(sd->translate_strings, ts) { if (!ts->string) continue; const char *s = dgettext(ts->domain, ts->string); sd->on_translate = EINA_TRUE; elm_widget_part_text_set(obj, ts->id, s); sd->on_translate = EINA_FALSE; } #endif efl_event_callback_legacy_call(obj, EFL_UI_WIDGET_EVENT_LANGUAGE_CHANGED, NULL); } EOLIAN static void _efl_ui_widget_access_info_set(Eo *obj EINA_UNUSED, Elm_Widget_Smart_Data *sd, const char *txt) { eina_stringshare_replace(&sd->access_info, txt); } EOLIAN static const char* _efl_ui_widget_access_info_get(Eo *obj EINA_UNUSED, Elm_Widget_Smart_Data *sd) { return sd->access_info; } EAPI Elm_Theme * elm_widget_theme_get(const Evas_Object *obj) { ELM_WIDGET_DATA_GET_OR_RETURN(obj, sd, NULL); if (!sd->theme) { if (sd->parent_obj && elm_widget_is(sd->parent_obj)) return elm_widget_theme_get(sd->parent_obj); else return NULL; } return sd->theme; } EOLIAN static Efl_Ui_Theme_Apply _efl_ui_widget_style_set(Eo *obj, Elm_Widget_Smart_Data *sd, const char *style) { if (!elm_widget_is_legacy(obj) && efl_finalized_get(obj)) { ERR("Efl.Ui.Widget.style can only be set before finalize!"); return EFL_UI_THEME_APPLY_FAILED; } if (eina_stringshare_replace(&sd->style, style)) return elm_widget_theme(obj); return EFL_UI_THEME_APPLY_SUCCESS; } EOLIAN static const char* _efl_ui_widget_style_get(Eo *obj EINA_UNUSED, Elm_Widget_Smart_Data *sd) { const char *ret; ret = "default"; if (sd->style) ret = sd->style; return ret; } EAPI void elm_widget_tooltip_add(Eo *obj, Elm_Tooltip *tt) { Elm_Widget_Smart_Data *sd = efl_data_scope_safe_get(obj, MY_CLASS); if (!sd) return; sd->tooltips = eina_list_append(sd->tooltips, tt); } EAPI void elm_widget_tooltip_del(Eo *obj, Elm_Tooltip *tt) { Elm_Widget_Smart_Data *sd = efl_data_scope_safe_get(obj, MY_CLASS); if (!sd) return; sd->tooltips = eina_list_remove(sd->tooltips, tt); } EAPI void elm_widget_cursor_add(Eo *obj, Elm_Cursor *cur) { Elm_Widget_Smart_Data *sd = efl_data_scope_safe_get(obj, MY_CLASS); if (!sd) return; sd->cursors = eina_list_append(sd->cursors, cur); } EAPI void elm_widget_cursor_del(Eo *obj, Elm_Cursor *cur) { Elm_Widget_Smart_Data *sd = efl_data_scope_safe_get(obj, MY_CLASS); if (!sd) return; sd->cursors = eina_list_remove(sd->cursors, cur); } EAPI void elm_widget_scroll_lock_set(Eo *obj, Efl_Ui_Scroll_Block block) { Elm_Widget_Smart_Data *sd = efl_data_scope_safe_get(obj, MY_CLASS); Eina_Bool lx, ly; if (!sd) return; lx = !!(block & EFL_UI_SCROLL_BLOCK_HORIZONTAL); ly = !!(block & EFL_UI_SCROLL_BLOCK_VERTICAL); if (sd->scroll_x_locked != lx) { sd->scroll_x_locked = lx; _propagate_x_drag_lock(obj, lx ? 1 : -1); } if (sd->scroll_y_locked != ly) { sd->scroll_y_locked = ly; _propagate_y_drag_lock(obj, ly ? 1 : -1); } } EAPI Efl_Ui_Scroll_Block elm_widget_scroll_lock_get(const Eo *obj) { Elm_Widget_Smart_Data *sd = efl_data_scope_safe_get(obj, MY_CLASS); Efl_Ui_Scroll_Block block = EFL_UI_SCROLL_BLOCK_NONE; if (!sd) return block; if (sd->scroll_x_locked) block |= EFL_UI_SCROLL_BLOCK_HORIZONTAL; if (sd->scroll_y_locked) block |= EFL_UI_SCROLL_BLOCK_VERTICAL; return block; } EAPI int elm_widget_scroll_child_locked_x_get(const Eo *obj) { Elm_Widget_Smart_Data *sd = efl_data_scope_safe_get(obj, MY_CLASS); if (!sd) return EINA_FALSE; return sd->child_drag_x_locked; } EAPI int elm_widget_scroll_child_locked_y_get(const Eo *obj) { Elm_Widget_Smart_Data *sd = efl_data_scope_safe_get(obj, MY_CLASS); if (!sd) return EINA_FALSE; return sd->child_drag_y_locked; } EAPI Efl_Ui_Theme_Apply elm_widget_theme_object_set(Evas_Object *obj, Evas_Object *edj, const char *wname, const char *welement, const char *wstyle) { ELM_WIDGET_DATA_GET_OR_RETURN(obj, sd, EFL_UI_THEME_APPLY_FAILED); if (eina_streq(welement, "base")) welement = NULL; if (eina_streq(wstyle, "default")) wstyle = NULL; Efl_Ui_Theme_Apply ret = _elm_theme_object_set(obj, edj, wname, welement, wstyle); if (!ret) { return EFL_UI_THEME_APPLY_FAILED; } if (sd->orient_mode != -1) { char buf[128]; snprintf(buf, sizeof(buf), "elm,state,orient,%d", sd->orient_mode); elm_widget_signal_emit(obj, buf, "elm"); } return ret; } static void _convert(Efl_Dbg_Info *info, Eina_Iterator *ptr_list) { void *p; int i = 0; EINA_ITERATOR_FOREACH(ptr_list, p) { char name[100]; snprintf(name, sizeof(name), "Candidate %d", i); EFL_DBG_INFO_APPEND(info, name, EINA_VALUE_TYPE_UINT64, p); i++; } eina_iterator_free(ptr_list); } EOLIAN static void _efl_ui_widget_efl_object_dbg_info_get(Eo *eo_obj, Elm_Widget_Smart_Data *_pd EINA_UNUSED, Efl_Dbg_Info *root) { efl_dbg_info_get(efl_super(eo_obj, MY_CLASS), root); Efl_Ui_Focus_Relations *rel = NULL; Efl_Dbg_Info *focus, *group = EFL_DBG_INFO_LIST_APPEND(root, MY_CLASS_NAME); EFL_DBG_INFO_APPEND(group, "Wid-Type", EINA_VALUE_TYPE_STRING, elm_widget_type_get(eo_obj)); EFL_DBG_INFO_APPEND(group, "Style", EINA_VALUE_TYPE_STRING, elm_widget_style_get(eo_obj)); EFL_DBG_INFO_APPEND(group, "Layer", EINA_VALUE_TYPE_INT, (int) evas_object_layer_get(eo_obj)); EFL_DBG_INFO_APPEND(group, "Scale", EINA_VALUE_TYPE_DOUBLE, evas_object_scale_get(eo_obj)); EFL_DBG_INFO_APPEND(group, "Has focus", EINA_VALUE_TYPE_CHAR, elm_object_focus_get(eo_obj)); EFL_DBG_INFO_APPEND(group, "Can focus", EINA_VALUE_TYPE_CHAR, elm_widget_can_focus_get(eo_obj)); EFL_DBG_INFO_APPEND(group, "Disabled", EINA_VALUE_TYPE_CHAR, elm_widget_disabled_get(eo_obj)); EFL_DBG_INFO_APPEND(group, "Mirrored", EINA_VALUE_TYPE_CHAR, efl_ui_mirrored_get(eo_obj)); EFL_DBG_INFO_APPEND(group, "Tree Unfocusable", EINA_VALUE_TYPE_CHAR, elm_widget_tree_unfocusable_get(eo_obj)); EFL_DBG_INFO_APPEND(group, "Automatic mirroring", EINA_VALUE_TYPE_CHAR, efl_ui_mirrored_automatic_get(eo_obj)); rel = efl_ui_focus_manager_fetch(_pd->focus.manager, eo_obj); if (rel) { focus = EFL_DBG_INFO_LIST_APPEND(group, "Focus"); EFL_DBG_INFO_APPEND(focus, "logical", EINA_VALUE_TYPE_CHAR, rel->logical ); EFL_DBG_INFO_APPEND(focus, "manager", EINA_VALUE_TYPE_UINT64, _pd->focus.manager); EFL_DBG_INFO_APPEND(focus, "parent", EINA_VALUE_TYPE_UINT64, rel->parent); EFL_DBG_INFO_APPEND(focus, "next", EINA_VALUE_TYPE_UINT64 , rel->next); EFL_DBG_INFO_APPEND(focus, "prev", EINA_VALUE_TYPE_UINT64 , rel->prev); EFL_DBG_INFO_APPEND(focus, "redirect", EINA_VALUE_TYPE_UINT64 , rel->redirect); #define ADD_PTR_LIST(name) \ Efl_Dbg_Info* name = EFL_DBG_INFO_LIST_APPEND(focus, ""#name""); \ _convert(name, eina_list_iterator_new(rel->name)); ADD_PTR_LIST(top) ADD_PTR_LIST(down) ADD_PTR_LIST(right) ADD_PTR_LIST(left) #undef ADD_PTR_LIST } //if thats a focus manager, give useful information like the border elements if (efl_isa(eo_obj, EFL_UI_FOCUS_MANAGER_INTERFACE)) { Efl_Dbg_Info *border; focus = EFL_DBG_INFO_LIST_APPEND(group, "Focus Manager"); border = EFL_DBG_INFO_LIST_APPEND(focus, "Border Elements"); _convert(border, efl_ui_focus_manager_border_elements_get(eo_obj) ); EFL_DBG_INFO_APPEND(focus, "redirect", EINA_VALUE_TYPE_UINT64, efl_ui_focus_manager_redirect_get(eo_obj)); } } EAPI Eina_Bool elm_widget_is_check(const Evas_Object *obj) { static int abort_on_warn = -1; if (elm_widget_is(obj)) return EINA_TRUE; ERR("Passing Object: %p.", obj); if (abort_on_warn == -1) { if (getenv("ELM_ERROR_ABORT")) abort_on_warn = 1; else abort_on_warn = 0; } if (abort_on_warn == 1) abort(); return EINA_FALSE; } EAPI const char * elm_widget_type_get(const Evas_Object *obj) { API_ENTRY return NULL; return efl_class_name_get(efl_class_get(obj)); } EAPI Eina_Bool elm_widget_type_check(const Evas_Object *obj, const char *type, const char *func) { const char *provided, *expected = "(unknown)"; static int abort_on_warn = -1; provided = elm_widget_type_get(obj); /* TODO: eventually migrate to check_ptr version */ if (evas_object_smart_type_check(obj, type)) return EINA_TRUE; if (type) expected = type; if ((!provided) || (!provided[0])) { provided = evas_object_type_get(obj); if ((!provided) || (!provided[0])) provided = "(unknown)"; } ERR("Passing Object: %p in function: %s, of type: '%s' when expecting" " type: '%s'", obj, func, provided, expected); if (abort_on_warn == -1) { if (getenv("ELM_ERROR_ABORT")) abort_on_warn = 1; else abort_on_warn = 0; } if (abort_on_warn == 1) abort(); return EINA_FALSE; } /** @internal */ EAPI Evas_Object * elm_widget_name_find(const Eo *obj, const char *name, int recurse) { Eina_List *l; Evas_Object *child; const char *s; INTERNAL_ENTRY NULL; if (!name) return NULL; if (!_elm_widget_is(obj)) return NULL; EINA_LIST_FOREACH(sd->subobjs, l, child) { s = evas_object_name_get(child); if ((s) && (!strcmp(s, name))) return child; if ((recurse != 0) && ((child = elm_widget_name_find(child, name, recurse - 1)))) return child; } if (sd->hover_obj) { s = evas_object_name_get(sd->hover_obj); if ((s) && (!strcmp(s, name))) return sd->hover_obj; if ((recurse != 0) && ((child = elm_widget_name_find(sd->hover_obj, name, recurse - 1)))) return child; } return NULL; } /** * @internal * * Split string in words * * @param str Source string * @return List of const words * * @see elm_widget_stringlist_free() * @ingroup Widget */ EAPI Eina_List * elm_widget_stringlist_get(const char *str) { Eina_List *list = NULL; const char *s, *b; if (!str) return NULL; for (b = s = str; 1; s++) { if ((*s == ' ') || (!*s)) { char *t = malloc(s - b + 1); if (t) { strncpy(t, b, s - b); t[s - b] = 0; list = eina_list_append(list, eina_stringshare_add(t)); free(t); } b = s + 1; } if (!*s) break; } return list; } EAPI void elm_widget_stringlist_free(Eina_List *list) { const char *s; EINA_LIST_FREE(list, s) eina_stringshare_del(s); } /* internal */ EAPI void elm_widget_focus_mouse_up_handle(Eo *obj) { Elm_Widget_Smart_Data *pd = efl_data_scope_get(obj, MY_CLASS); if (!_is_focusable(obj)) return; if (pd->focus.manager && !pd->focus.logical) { efl_ui_focus_util_focus(EFL_UI_FOCUS_UTIL_CLASS, obj); } } static void _elm_widget_focus_tree_unfocusable_handle(Eo *obj EINA_UNUSED) { //FIXME } /* * @internal * * Get the focus highlight geometry of a widget. * * @param obj Widget object for the focus highlight * @param x Focus highlight x coordinate * @param y Focus highlight y coordinate * @param w Focus highlight object width * @param h Focus highlight object height * @param is_next @c EINA_TRUE if this request is for the new focused object, * @c EINA_FALSE if this request is for the previously focused object. This * information becomes important when the focus highlight is changed inside one * widget. * * @ingroup Widget */ /* * @internal * * Get the 'focus_part' geometry if there is any * * This queries if there is a 'focus_part' request from the edc style. If edc * style offers 'focus_part' edje data item, this function requests for the * geometry of a specific part which is described in 'focus_part' edje data. * * @param obj Widget object for the focus highlight * @param x Focus highlight x coordinate * @param y Focus highlight y coordinate * @param w Focus highlight object width * @param h Focus highlight object height * * x, y, w, h already contain the object's geometry. If there is a 'focus_part' * support, these values will be updated accordingly or the values will be * remained as they were. * * @ingroup Widget */ EAPI void elm_widget_focus_highlight_focus_part_geometry_get(const Evas_Object *obj, Evas_Coord *x, Evas_Coord *y, Evas_Coord *w, Evas_Coord *h) { Evas_Coord tx = 0, ty = 0, tw = 0, th = 0; const char *target_hl_part = NULL; const Evas_Object *edje_obj = NULL; if (obj && efl_isa(obj, EFL_CANVAS_LAYOUT_CLASS)) { edje_obj = obj; if (!(target_hl_part = edje_object_data_get(edje_obj, "focus_part"))) return; } else if (obj && efl_isa(obj, EFL_UI_LAYOUT_CLASS)) { edje_obj = elm_layout_edje_get(obj); if (!(target_hl_part = elm_layout_data_get(obj, "focus_part"))) return; } else return; edje_object_part_geometry_get(edje_obj, target_hl_part, &tx, &ty, &tw, &th); *x += tx; *y += ty; if (tw != *w) *w = tw; if (th != *h) *h = th; } EOLIAN static Eina_Rect _efl_ui_widget_focus_highlight_geometry_get(Eo *obj, Elm_Widget_Smart_Data *sd) { Evas_Coord ox = 0, oy = 0, ow = 0, oh = 0; Evas_Object *scroller = (Evas_Object *)obj; Eina_Rect r = {}; evas_object_geometry_get(obj, &r.x, &r.y, &r.w, &r.h); elm_widget_focus_highlight_focus_part_geometry_get(sd->resize_obj, &r.x, &r.y, &r.w, &r.h); if (_elm_config->focus_autoscroll_mode != ELM_FOCUS_AUTOSCROLL_MODE_BRING_IN) return r; while (scroller) { if (_elm_scrollable_is(scroller)) { elm_interface_scrollable_content_viewport_geometry_get(scroller, &ox, &oy, &ow, &oh); if (r.y < oy) r.y = oy; else if ((oy + oh) < (r.y + r.h)) r.y = (oy + oh - r.h); else if (r.x < ox) r.x = ox; else if ((ox + ow) < (r.x + r.w)) r.x = (ox + ow - r.w); break; } scroller = elm_widget_parent_get(scroller); } return r; } EOLIAN static Elm_Object_Item* _efl_ui_widget_focused_item_get(Eo *obj EINA_UNUSED, Elm_Widget_Smart_Data *_pd EINA_UNUSED) { return NULL; } EOLIAN static void _efl_ui_widget_interest_region_mode_set(Eo *obj EINA_UNUSED, Elm_Widget_Smart_Data *_pd, Elm_Focus_Region_Show_Mode mode) { _pd->focus_region_show_mode = mode; } EOLIAN static Elm_Focus_Region_Show_Mode _efl_ui_widget_interest_region_mode_get(Eo *obj EINA_UNUSED, Elm_Widget_Smart_Data *_pd) { return _pd->focus_region_show_mode; } EAPI void elm_widget_activate(Evas_Object *obj, Efl_Ui_Activate act) { Evas_Object *parent; Eina_Bool ret; ELM_WIDGET_CHECK(obj); ret = EINA_FALSE; ret = efl_ui_widget_on_access_activate(obj, act); if (ret) return; parent = elm_widget_parent_get(obj); if (parent) elm_widget_activate(parent, act); return; } /** * @internal * * Sets the widget and child widget's Evas_Display_Mode. * * @param obj The widget. * @param dispmode Evas_Display_Mode to set widget's mode. * * Widgets are resized by several reasons. * Evas_Display_Mode can help for widgets to get one more reason of resize. * For example, elm conform widget resizes it's contents when keypad state changed. * After keypad showing, conform widget can change child's Evas_Display_Mode. * @ingroup Widget */ /* Legacy only */ EAPI void elm_widget_display_mode_set(Evas_Object *obj, Evas_Display_Mode dispmode) { Evas_Display_Mode prev_dispmode; Evas_Object *child; Eina_List *l; API_ENTRY return; prev_dispmode = evas_object_size_hint_display_mode_get(obj); if ((prev_dispmode == dispmode) || (prev_dispmode == EVAS_DISPLAY_MODE_DONT_CHANGE)) return; evas_object_size_hint_display_mode_set(obj, dispmode); EINA_LIST_FOREACH (sd->subobjs, l, child) { if (elm_widget_is(child)) elm_widget_display_mode_set(child, dispmode); } } EOLIAN static void _efl_ui_widget_orientation_mode_set(Eo *obj, Elm_Widget_Smart_Data *sd, Efl_Ui_Widget_Orientation_Mode mode) { int rotation = -1; if (mode != EFL_UI_WIDGET_ORIENTATION_MODE_DISABLED) { //Get current orient mode from it's parent otherwise, 0. sd->orient_mode = 0; ELM_WIDGET_DATA_GET(sd->parent_obj, sd_parent); if (!sd_parent) rotation = 0; else rotation = sd_parent->orient_mode; } efl_ui_widget_on_orientation_update(obj, rotation); } EOLIAN static Efl_Ui_Widget_Orientation_Mode _efl_ui_widget_orientation_mode_get(Eo *obj EINA_UNUSED, Elm_Widget_Smart_Data *sd) { if (sd->orient_mode == -1) return EFL_UI_WIDGET_ORIENTATION_MODE_DISABLED; else return EFL_UI_WIDGET_ORIENTATION_MODE_DEFAULT; } EOLIAN static void _efl_ui_widget_on_orientation_update(Eo *obj, Elm_Widget_Smart_Data *sd, int orient_mode) { Evas_Object *child; Eina_List *l; sd->orient_mode = orient_mode; EINA_LIST_FOREACH (sd->subobjs, l, child) { if (elm_widget_is(child)) efl_ui_widget_on_orientation_update(child, orient_mode); } if (orient_mode != -1) { char buf[128]; snprintf(buf, sizeof(buf), "elm,state,orient,%d", orient_mode); elm_widget_signal_emit(obj, buf, "elm"); } } /** * @internal * * Returns the widget's focus move policy. * * @param obj The widget. * @return focus move policy of the object. * **/ EOLIAN static Elm_Focus_Move_Policy _efl_ui_widget_focus_move_policy_get(Eo *obj EINA_UNUSED, Elm_Widget_Smart_Data *sd) { return sd->focus_move_policy; } /** * @internal * * Sets the widget's focus move policy. * * @param obj The widget. * @param policy Elm_Focus_Move_Policy to set object's focus move policy. */ EOLIAN static void _efl_ui_widget_focus_move_policy_set(Eo *obj EINA_UNUSED, Elm_Widget_Smart_Data *sd, Elm_Focus_Move_Policy policy) { if (sd->focus_move_policy == policy) return; sd->focus_move_policy = policy; } /** * Returns the widget's focus_move_policy mode setting. * * @param obj The widget. * @return focus_move_policy mode setting of the object. * **/ EOLIAN static Eina_Bool _efl_ui_widget_focus_move_policy_automatic_get(Eo *obj EINA_UNUSED, Elm_Widget_Smart_Data *sd) { return sd->focus_move_policy_auto_mode; } /** * @internal * * Sets the widget's focus_move_policy mode setting. * When widget in automatic mode, it follows the system focus_move_policy mode set by * elm_config_focus_move_policy_set(). * @param obj The widget. * @param automatic EINA_TRUE for auto focus_move_policy mode. EINA_FALSE for manual. */ EOLIAN static void _efl_ui_widget_focus_move_policy_automatic_set(Eo *obj, Elm_Widget_Smart_Data *sd, Eina_Bool automatic) { if (sd->focus_move_policy_auto_mode != automatic) { sd->focus_move_policy_auto_mode = automatic; if (automatic) { efl_ui_widget_focus_move_policy_set(obj, elm_config_focus_move_policy_get()); } } } /** * @internal * * Sets the klass name of a widget. * @param obj The widget. * @param name Name of the klass to use. * @return Whether the name was different and thus replaced. */ EAPI Eina_Bool elm_widget_theme_klass_set(Evas_Object *obj, const char *name) { ELM_WIDGET_DATA_GET_OR_RETURN(obj, pd, EINA_FALSE); return eina_stringshare_replace(&(pd->klass), name); } /** * @internal * * Gets the klass name of a widget. * @param obj The widget. * @return The current klass name of internal canvas object. */ EAPI const char * elm_widget_theme_klass_get(const Evas_Object *obj) { ELM_WIDGET_DATA_GET_OR_RETURN(obj, pd, NULL); return (const char *)pd->klass; } /** * @internal * * Sets the element name of a widget. * * @param obj The widget. * @param name Name of the element to use. * @return Whether the name was different and thus replaced. */ EAPI Eina_Bool elm_widget_theme_element_set(Evas_Object *obj, const char *name) { ELM_WIDGET_DATA_GET_OR_RETURN(obj, pd, EINA_FALSE); if (eina_streq(name, "base")) name = NULL; return eina_stringshare_replace(&(pd->group), name); } /** * @internal * * Gets the element name of a widget. * @param obj The widget. * @return The current element name of internal canvas object. */ EAPI const char * elm_widget_theme_element_get(const Evas_Object *obj) { ELM_WIDGET_DATA_GET_OR_RETURN(obj, pd, NULL); return (const char *)pd->group; } /** * @internal * * Sets the style name of a widget. * * @param obj The widget. * @param name Name of the style to use. * @return Whether the name was different and thus replaced. */ EAPI Eina_Bool elm_widget_theme_style_set(Evas_Object *obj, const char *name) { ELM_WIDGET_DATA_GET_OR_RETURN(obj, pd, EINA_FALSE); if (eina_streq(name, "default")) name = NULL; return eina_stringshare_replace(&(pd->style), name); } /** * @internal * * Gets the style name of a widget. * @param obj The widget. * @return The current style name of internal canvas object. */ EAPI const char * elm_widget_theme_style_get(const Evas_Object *obj) { ELM_WIDGET_DATA_GET_OR_RETURN(obj, pd, NULL); return (const char *)pd->style; } /** * @internal * * Register sub object as a group of a widget and re-apply its theme. * @param obj The widget. * @param component A sub object to be added as an element of the widget. * @param name An element name of sub object. * @return Whether the style was successfully applied or not. */ EAPI Efl_Ui_Theme_Apply elm_widget_element_update(Evas_Object *obj, Evas_Object *component, const char *name) { Efl_Ui_Theme_Apply ret = EFL_UI_THEME_APPLY_SUCCESS; Eina_Bool changed = EINA_FALSE; const char *obj_group; Eina_Stringshare *group; obj_group = elm_widget_theme_element_get(obj); if (!obj_group) group = eina_stringshare_add(name); else group = eina_stringshare_printf("%s/%s", elm_widget_theme_element_get(obj), name); if (efl_isa(component, EFL_UI_WIDGET_CLASS)) { changed |= elm_widget_theme_klass_set(component, elm_widget_theme_klass_get(obj)); changed |= elm_widget_theme_element_set(component, (const char *)group); changed |= elm_widget_theme_style_set(component, elm_widget_theme_style_get(obj)); if (changed) ret = efl_ui_widget_theme_apply(component); } else { ret = elm_widget_theme_object_set(obj, component, elm_widget_theme_klass_get(obj), (const char *)group, elm_widget_theme_style_get(obj)); } eina_stringshare_del(group); return ret; } static void _track_obj_del(void *data, Evas *e, Evas_Object *obj, void *event_info); static void _track_obj_update(Evas_Object *track, Evas_Object *obj) { //Geometry Evas_Coord x, y, w, h; evas_object_geometry_get(obj, &x, &y, &w, &h); evas_object_move(track, x, y); evas_object_resize(track, w, h); //Visibility if (evas_object_visible_get(obj)) evas_object_show(track); else evas_object_hide(track); } static void _track_obj_view_update(void *data, const Efl_Event *event) { Elm_Widget_Item_Data *item = data; _track_obj_update(item->track_obj, event->object); } static void _track_obj_view_del(void *data, const Efl_Event *event); EFL_CALLBACKS_ARRAY_DEFINE(tracker_callbacks, { EFL_GFX_EVENT_RESIZE, _track_obj_view_update }, { EFL_GFX_EVENT_MOVE, _track_obj_view_update }, { EFL_GFX_EVENT_SHOW, _track_obj_view_update }, { EFL_GFX_EVENT_HIDE, _track_obj_view_update }, { EFL_EVENT_DEL, _track_obj_view_del }); static void _track_obj_view_del(void *data, const Efl_Event *event EINA_UNUSED) { Elm_Widget_Item_Data *item = data; while (evas_object_ref_get(item->track_obj) > 0) evas_object_unref(item->track_obj); evas_object_event_callback_del(item->track_obj, EVAS_CALLBACK_DEL, _track_obj_del); evas_object_del(item->track_obj); item->track_obj = NULL; } static void _track_obj_del(void *data, Evas *e EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED) { Elm_Widget_Item_Data *item = data; item->track_obj = NULL; if (!item->view) return; efl_event_callback_array_del(item->view, tracker_callbacks(), item); } static void _elm_widget_item_signal_cb(void *data, Evas_Object *obj EINA_UNUSED, const char *emission, const char *source) { Elm_Widget_Item_Signal_Data *wisd = data; wisd->func(wisd->data, wisd->item, emission, source); } static void * _elm_widget_item_signal_callback_list_get(Elm_Widget_Item_Data *item, Eina_List *position) { Elm_Widget_Item_Signal_Data *wisd = eina_list_data_get(position); void *data; item->signals = eina_list_remove_list(item->signals, position); data = wisd->data; if (_elm_widget_is(item->view)) elm_object_signal_callback_del(item->view, wisd->emission, wisd->source, _elm_widget_item_signal_cb); else if (efl_isa(item->view, EFL_CANVAS_LAYOUT_CLASS)) edje_object_signal_callback_del_full(item->view, wisd->emission, wisd->source, _elm_widget_item_signal_cb, wisd); eina_stringshare_del(wisd->emission); eina_stringshare_del(wisd->source); free(wisd); return data; } #define ERR_NOT_SUPPORTED(item, method) ERR("%s does not support %s API.", elm_widget_type_get(item->widget), method); static void _efl_del_cb(void *data EINA_UNUSED, const Efl_Event *event) { Elm_Widget_Item_Data *item = efl_data_scope_get(event->object, ELM_WIDGET_ITEM_CLASS); ELM_WIDGET_ITEM_CHECK_OR_RETURN(item); if (item->del_func) item->del_func((void *) WIDGET_ITEM_DATA_GET(event->object), item->widget, item->eo_obj); } /** * @internal * * Allocate a new Elm_Widget_Item-derived structure. * * The goal of this structure is to provide common ground for actions * that a widget item have, such as the owner widget, callback to * notify deletion, data pointer and maybe more. * * @param widget the owner widget that holds this item, must be an elm_widget! * @param alloc_size any number greater than sizeof(Elm_Widget_Item) that will * be used to allocate memory. * * @return allocated memory that is already zeroed out, or NULL on errors. * * @see elm_widget_item_new() convenience macro. * @see elm_widget_item_del() to release memory. * @ingroup Widget */ EOLIAN static Eo * _elm_widget_item_efl_object_constructor(Eo *eo_item, Elm_Widget_Item_Data *item) { Evas_Object *widget; widget = efl_parent_get(eo_item); if (!_elm_widget_is(widget)) { ERR("Failed"); return NULL; } eo_item = efl_constructor(efl_super(eo_item, ELM_WIDGET_ITEM_CLASS)); EINA_MAGIC_SET(item, ELM_WIDGET_ITEM_MAGIC); item->widget = widget; item->eo_obj = eo_item; efl_event_callback_add(eo_item, EFL_EVENT_DEL, _efl_del_cb, NULL); return eo_item; } EOLIAN static void _elm_widget_item_efl_object_destructor(Eo *eo_item, Elm_Widget_Item_Data *item) { Elm_Translate_String_Data *ts; ELM_WIDGET_ITEM_CHECK_OR_RETURN(item); evas_object_del(item->view); eina_stringshare_del(item->access_info); eina_stringshare_del(item->accessible_name); while (item->signals) _elm_widget_item_signal_callback_list_get(item, item->signals); while (item->translate_strings) { ts = EINA_INLIST_CONTAINER_GET(item->translate_strings, Elm_Translate_String_Data); eina_stringshare_del(ts->id); eina_stringshare_del(ts->domain); eina_stringshare_del(ts->string); item->translate_strings = eina_inlist_remove(item->translate_strings, item->translate_strings); free(ts); } eina_hash_free(item->labels); efl_access_attributes_clear(eo_item); efl_access_removed(eo_item); EINA_MAGIC_SET(item, EINA_MAGIC_NONE); efl_destructor(efl_super(eo_item, ELM_WIDGET_ITEM_CLASS)); } /** * @internal * * Releases widget item memory, calling back item_del_pre_hook() and * item_del_cb() if they exist. * * @param item a valid #Elm_Widget_Item to be deleted. * * If there is an Elm_Widget_Item::del_cb, then it will be called prior * to memory release. Note that elm_widget_item_pre_notify_del() calls * this function and then unset it, thus being useful for 2 step * cleanup whenever the del_cb may use any of the data that must be * deleted from item. * * The Elm_Widget_Item::view will be deleted (evas_object_del()) if it * is presented! * * Note that if item_del_pre_hook() returns @c EINA_TRUE, item free will be * deferred, or item will be freed here if it returns @c EINA_FALSE. * * @see elm_widget_item_del() convenience macro. * @ingroup Widget */ EOLIAN static void _elm_widget_item_del(Eo *eo_item EINA_UNUSED, Elm_Widget_Item_Data *item) { ELM_WIDGET_ITEM_CHECK_OR_RETURN(item); ELM_WIDGET_ITEM_RETURN_IF_ONDEL(item); item->on_deletion = EINA_TRUE; //Widget item delete callback Eina_Bool del_ok; del_ok = elm_wdg_item_del_pre(item->eo_obj); if (del_ok) efl_del(item->eo_obj); return; } EOLIAN static Eina_Bool _elm_widget_item_del_pre(Eo *eo_item EINA_UNUSED, Elm_Widget_Item_Data *item EINA_UNUSED) { return EINA_TRUE; } /** * @internal * * Notify object will be deleted without actually deleting it. * * This function will callback Elm_Widget_Item::del_cb if it is set * and then unset it so it is not called twice (ie: from * elm_widget_item_del()). * * @param item a valid #Elm_Widget_Item to be notified * @see elm_widget_item_pre_notify_del() convenience macro. * @ingroup Widget */ EOLIAN static void _elm_widget_item_pre_notify_del(Eo *eo_item, Elm_Widget_Item_Data *item) { ELM_WIDGET_ITEM_CHECK_OR_RETURN(item); if (!item->del_func) return; item->del_func((void *)WIDGET_ITEM_DATA_GET(eo_item), item->widget, item->eo_obj); item->del_func = NULL; } /** * @internal * * Set the function to notify when item is being deleted. * * This function will complain if there was a callback set already, * however it will set the new one. * * The callback will be called from elm_widget_item_pre_notify_del() * or elm_widget_item_del() will be called with: * - data: the Elm_Widget_Item::data value. * - obj: the Elm_Widget_Item::widget evas object. * - event_info: the item being deleted. * * @param item a valid #Elm_Widget_Item to be notified * @see elm_widget_item_del_cb_set() convenience macro. * @ingroup Widget */ EOLIAN static void _elm_widget_item_del_cb_set(Eo *eo_item EINA_UNUSED, Elm_Widget_Item_Data *item, Evas_Smart_Cb func) { ELM_WIDGET_ITEM_CHECK_OR_RETURN(item); ELM_WIDGET_ITEM_RETURN_IF_ONDEL(item); if ((item->del_func) && (item->del_func != func)) WRN("You're replacing a previously set del_cb %p of item %p with %p", item->del_func, item->eo_obj, func); item->del_func = func; } /** * @internal * * Get owner widget of this item. * * @param item a valid #Elm_Widget_Item to get data from. * @return owner widget of this item. * @ingroup Widget */ EOLIAN static Evas_Object * _elm_widget_item_widget_get(const Eo *eo_item EINA_UNUSED, Elm_Widget_Item_Data *item) { ELM_WIDGET_ITEM_CHECK_OR_RETURN(item, NULL); ELM_WIDGET_ITEM_RETURN_IF_ONDEL(item, NULL); return item->widget; } EAPI Eina_Bool _elm_widget_onscreen_is(Evas_Object *widget) { Evas_Object *parent = widget; Eina_Rectangle r1, r2; Evas *evas = evas_object_evas_get(widget); if (!evas) return EINA_FALSE; evas_object_geometry_get(widget, &r1.x, &r1.y, &r1.w, &r1.h); if (eina_rectangle_is_empty(&r1)) return EINA_FALSE; // window does not have to check viewport and geometry if (efl_isa(widget, EFL_UI_WIN_CLASS)) return EINA_TRUE; // check if on canvas evas_output_viewport_get(evas, &r2.x, &r2.y, &r2.w, &r2.h); if (!eina_rectangles_intersect(&r1, &r2)) return EINA_FALSE; // check if inside scrollable parent viewport do { parent = elm_widget_parent_get(parent); if (parent && !evas_object_visible_get(parent)) return EINA_FALSE; if (parent && efl_isa(parent, ELM_INTERFACE_SCROLLABLE_MIXIN)) { evas_object_geometry_get(parent, &r2.x, &r2.y, &r2.w, &r2.h); if (!eina_rectangles_intersect(&r1, &r2)) return EINA_FALSE; } } while (parent && (parent != elm_widget_top_get(widget))); return EINA_TRUE; } EAPI Eina_Bool _elm_widget_item_onscreen_is(Elm_Object_Item *item) { Eina_Rectangle r1, r2; Elm_Widget_Item_Data *id = efl_data_scope_get(item, ELM_WIDGET_ITEM_CLASS); if (!id || !id->view) return EINA_FALSE; if (!evas_object_visible_get(id->view)) return EINA_FALSE; if (!_elm_widget_onscreen_is(id->widget)) return EINA_FALSE; evas_object_geometry_get(id->view, &r1.x, &r1.y, &r1.w, &r1.h); if (eina_rectangle_is_empty(&r1)) return EINA_FALSE; evas_object_geometry_get(id->widget, &r2.x, &r2.y, &r2.w, &r2.h); if (!eina_rectangles_intersect(&r1, &r2)) return EINA_FALSE; return EINA_TRUE; } const char* _elm_widget_accessible_plain_name_get(Evas_Object *obj, const char* name) { char *accessible_plain_name; API_ENTRY return NULL; accessible_plain_name = _elm_util_mkup_to_text(name); eina_stringshare_del(sd->accessible_name); sd->accessible_name = eina_stringshare_add(accessible_plain_name); free(accessible_plain_name); return sd->accessible_name; } const char* _elm_widget_item_accessible_plain_name_get(Elm_Object_Item *item, const char* name) { char *accessible_plain_name; Elm_Widget_Item_Data *id = efl_data_scope_get(item, ELM_WIDGET_ITEM_CLASS); if (!id) return NULL; accessible_plain_name = _elm_util_mkup_to_text(name); eina_stringshare_del(id->accessible_name); id->accessible_name = eina_stringshare_add(accessible_plain_name); free(accessible_plain_name); return id->accessible_name; } EOLIAN static Efl_Access_State_Set _elm_widget_item_efl_access_state_set_get(Eo *eo_item, Elm_Widget_Item_Data *item EINA_UNUSED) { Efl_Access_State_Set states = 0; STATE_TYPE_SET(states, EFL_ACCESS_STATE_FOCUSABLE); if (elm_object_item_focus_get(eo_item)) STATE_TYPE_SET(states, EFL_ACCESS_STATE_FOCUSED); if (!elm_object_item_disabled_get(eo_item)) { STATE_TYPE_SET(states, EFL_ACCESS_STATE_ENABLED); STATE_TYPE_SET(states, EFL_ACCESS_STATE_SENSITIVE); STATE_TYPE_SET(states, EFL_ACCESS_STATE_VISIBLE); } if (_elm_widget_item_onscreen_is(eo_item)) STATE_TYPE_SET(states, EFL_ACCESS_STATE_SHOWING); return states; } EAPI void elm_object_item_data_set(Elm_Object_Item *it, void *data) { WIDGET_ITEM_DATA_SET(it, data); } EAPI void * elm_object_item_data_get(const Elm_Object_Item *it) { return (void *) WIDGET_ITEM_DATA_GET(it); } EOLIAN static void _elm_widget_item_disabled_set(Eo *eo_item EINA_UNUSED, Elm_Widget_Item_Data *item, Eina_Bool disabled) { ELM_WIDGET_ITEM_CHECK_OR_RETURN(item); ELM_WIDGET_ITEM_RETURN_IF_ONDEL(item); if (item->disabled == disabled) return; item->disabled = !!disabled; elm_wdg_item_disable(item->eo_obj); } EOLIAN static Eina_Bool _elm_widget_item_disabled_get(Eo *eo_item EINA_UNUSED, Elm_Widget_Item_Data *item) { ELM_WIDGET_ITEM_CHECK_OR_RETURN(item, EINA_FALSE); return item->disabled; } EOLIAN static void _elm_widget_item_style_set(Eo *eo_item EINA_UNUSED, Elm_Widget_Item_Data *item, const char *style EINA_UNUSED) { ERR_NOT_SUPPORTED(item, "elm_object_style_set()"); } EOLIAN static const char * _elm_widget_item_style_get(Eo *eo_item EINA_UNUSED, Elm_Widget_Item_Data *item) { ERR_NOT_SUPPORTED(item, "elm_object_style_get()"); return NULL; } EOLIAN static void _elm_widget_item_disable(Eo *eo_item EINA_UNUSED, Elm_Widget_Item_Data *item EINA_UNUSED) { } EOLIAN static void _elm_widget_item_focus_set(Eo *eo_item EINA_UNUSED, Elm_Widget_Item_Data *item, Eina_Bool focused EINA_UNUSED) { ERR_NOT_SUPPORTED(item, "elm_object_item_focus_set"); } EOLIAN static Eina_Bool _elm_widget_item_focus_get(Eo *eo_item EINA_UNUSED, Elm_Widget_Item_Data *item) { ERR_NOT_SUPPORTED(item, "elm_object_item_focus_get"); return EINA_FALSE; } EOLIAN static void _elm_widget_item_domain_translatable_part_text_set(Eo *eo_item EINA_UNUSED, Elm_Widget_Item_Data *item, const char *part, const char *domain, const char *label) { ELM_WIDGET_ITEM_CHECK_OR_RETURN(item); ELM_WIDGET_ITEM_RETURN_IF_ONDEL(item); Elm_Translate_String_Data *ts; if (!label) { _part_text_translatable_set(&item->translate_strings, part, EINA_FALSE, EINA_FALSE); } else { ts = _part_text_translatable_set(&item->translate_strings, part, EINA_TRUE, EINA_FALSE); if (!ts) return; if (!ts->string) ts->string = eina_stringshare_add(label); else eina_stringshare_replace(&ts->string, label); if (!ts->domain) ts->domain = eina_stringshare_add(domain); else eina_stringshare_replace(&ts->domain, domain); #ifdef HAVE_GETTEXT if (label[0]) label = dgettext(domain, label); #endif } item->on_translate = EINA_TRUE; elm_wdg_item_part_text_set(item->eo_obj, part, label); item->on_translate = EINA_FALSE; } EOLIAN static const char * _elm_widget_item_translatable_part_text_get(const Eo *eo_item EINA_UNUSED, Elm_Widget_Item_Data *item, const char *part) { ELM_WIDGET_ITEM_CHECK_OR_RETURN(item, NULL); ELM_WIDGET_ITEM_RETURN_IF_ONDEL(item, NULL); Elm_Translate_String_Data *ts; ts = _translate_string_data_get(item->translate_strings, part); if (ts) return ts->string; return NULL; } EOLIAN static void _elm_widget_item_domain_part_text_translatable_set(Eo *eo_item EINA_UNUSED, Elm_Widget_Item_Data *item, const char *part, const char *domain, Eina_Bool translatable) { ELM_WIDGET_ITEM_CHECK_OR_RETURN(item); ELM_WIDGET_ITEM_RETURN_IF_ONDEL(item); Elm_Translate_String_Data *ts; const char *text; ts = _part_text_translatable_set(&item->translate_strings, part, translatable, EINA_TRUE); if (!ts) return; if (!ts->domain) ts->domain = eina_stringshare_add(domain); else eina_stringshare_replace(&ts->domain, domain); text = elm_wdg_item_part_text_get(item->eo_obj, part); if (!text || !text[0]) return; if (!ts->string) ts->string = eina_stringshare_add(text); //Try to translate text since we don't know the text is already translated. #ifdef HAVE_GETTEXT text = dgettext(domain, text); #endif item->on_translate = EINA_TRUE; elm_wdg_item_part_text_set(item->eo_obj, part, text); item->on_translate = EINA_FALSE; } EOLIAN static void _elm_widget_item_track_cancel(Eo *eo_item EINA_UNUSED, Elm_Widget_Item_Data *item) { ELM_WIDGET_ITEM_CHECK_OR_RETURN(item); ELM_WIDGET_ITEM_RETURN_IF_ONDEL(item); if (!item->track_obj) return; while (evas_object_ref_get(item->track_obj) > 0) evas_object_unref(item->track_obj); evas_object_del(item->track_obj); } EOLIAN static Evas_Object * _elm_widget_item_track(Eo *eo_item EINA_UNUSED, Elm_Widget_Item_Data *item) { ELM_WIDGET_ITEM_CHECK_OR_RETURN(item, NULL); ELM_WIDGET_ITEM_RETURN_IF_ONDEL(item, NULL); if (item->track_obj) { evas_object_ref(item->track_obj); return item->track_obj; } if (!item->view) { WRN("view obj of the item(%p) is invalid. Please make sure the view obj is created!", item); return NULL; } Evas_Object *track = evas_object_rectangle_add(evas_object_evas_get(item->widget)); evas_object_color_set(track, 0, 0, 0, 0); evas_object_pass_events_set(track, EINA_TRUE); _track_obj_update(track, item->view); evas_object_event_callback_add(track, EVAS_CALLBACK_DEL, _track_obj_del, item); efl_event_callback_array_add(item->view, tracker_callbacks(), item); evas_object_ref(track); item->track_obj = track; return track; } EOLIAN static void _elm_widget_item_untrack(Eo *eo_item EINA_UNUSED, Elm_Widget_Item_Data *item) { ELM_WIDGET_ITEM_CHECK_OR_RETURN(item); ELM_WIDGET_ITEM_RETURN_IF_ONDEL(item); if (!item->track_obj) return; evas_object_unref(item->track_obj); if (evas_object_ref_get(item->track_obj) == 0) evas_object_del(item->track_obj); } EOLIAN static int _elm_widget_item_track_get(const Eo *eo_item EINA_UNUSED, Elm_Widget_Item_Data *item) { ELM_WIDGET_ITEM_CHECK_OR_RETURN(item, 0); ELM_WIDGET_ITEM_RETURN_IF_ONDEL(item, 0); if (!item->track_obj) return 0; return evas_object_ref_get(item->track_obj); } typedef struct _Elm_Widget_Item_Tooltip Elm_Widget_Item_Tooltip; struct _Elm_Widget_Item_Tooltip { Elm_Widget_Item_Data *item; Elm_Tooltip_Item_Content_Cb func; Evas_Smart_Cb del_cb; const void *data; }; static Evas_Object * _elm_widget_item_tooltip_label_create(void *data, Evas_Object *obj EINA_UNUSED, Evas_Object *tooltip, void *item EINA_UNUSED) { Evas_Object *label = elm_label_add(tooltip); if (!label) return NULL; elm_object_style_set(label, "tooltip"); elm_object_text_set(label, data); return label; } static Evas_Object * _elm_widget_item_tooltip_trans_label_create(void *data, Evas_Object *obj EINA_UNUSED, Evas_Object *tooltip, void *item EINA_UNUSED) { Evas_Object *label = elm_label_add(tooltip); if (!label) return NULL; elm_object_style_set(label, "tooltip"); elm_object_translatable_text_set(label, data); return label; } static void _elm_widget_item_tooltip_label_del_cb(void *data, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED) { eina_stringshare_del(data); } /** * @internal * * Set the text to be shown in the widget item. * * @param item Target item * @param text The text to set in the content * * Setup the text as tooltip to object. The item can have only one tooltip, * so any previous tooltip data is removed. * * @ingroup Widget */ EOLIAN static void _elm_widget_item_tooltip_text_set(Eo *eo_item EINA_UNUSED, Elm_Widget_Item_Data *item EINA_UNUSED, const char *text) { ELM_WIDGET_ITEM_CHECK_OR_RETURN(item); ELM_WIDGET_ITEM_RETURN_IF_ONDEL(item); EINA_SAFETY_ON_NULL_RETURN(text); text = eina_stringshare_add(text); elm_wdg_item_tooltip_content_cb_set(item->eo_obj, _elm_widget_item_tooltip_label_create, text, _elm_widget_item_tooltip_label_del_cb); } EOLIAN static void _elm_widget_item_tooltip_translatable_text_set(Eo *eo_item EINA_UNUSED, Elm_Widget_Item_Data *item EINA_UNUSED, const char *text) { ELM_WIDGET_ITEM_CHECK_OR_RETURN(item); ELM_WIDGET_ITEM_RETURN_IF_ONDEL(item); EINA_SAFETY_ON_NULL_RETURN(text); text = eina_stringshare_add(text); elm_wdg_item_tooltip_content_cb_set(item->eo_obj, _elm_widget_item_tooltip_trans_label_create, text, _elm_widget_item_tooltip_label_del_cb); } static Evas_Object * _elm_widget_item_tooltip_create(void *data, Evas_Object *obj, Evas_Object *tooltip) { Elm_Widget_Item_Tooltip *wit = data; return wit->func((void *)wit->data, obj, tooltip, wit->item->eo_obj); } static void _elm_widget_item_tooltip_del_cb(void *data, Evas_Object *obj, void *event_info EINA_UNUSED) { Elm_Widget_Item_Tooltip *wit = data; if (wit->del_cb) wit->del_cb((void *)wit->data, obj, wit->item->eo_obj); free(wit); } /** * @internal * * Set the content to be shown in the tooltip item * * Setup the tooltip to item. The item can have only one tooltip, * so any previous tooltip data is removed. @p func(with @p data) will * be called every time that need show the tooltip and it should * return a valid Evas_Object. This object is then managed fully by * tooltip system and is deleted when the tooltip is gone. * * @param item the widget item being attached a tooltip. * @param func the function used to create the tooltip contents. * @param data what to provide to @a func as callback data/context. * @param del_cb called when data is not needed anymore, either when * another callback replaces @func, the tooltip is unset with * elm_widget_item_tooltip_unset() or the owner @a item * dies. This callback receives as the first parameter the * given @a data, and @c event_info is the item. * * @ingroup Widget */ EOLIAN static void _elm_widget_item_tooltip_content_cb_set(Eo *eo_item EINA_UNUSED, Elm_Widget_Item_Data *item, Elm_Tooltip_Item_Content_Cb func, const void *data, Evas_Smart_Cb del_cb) { Elm_Widget_Item_Tooltip *wit; ELM_WIDGET_ITEM_CHECK_OR_GOTO(item, error_noitem); //ELM_WIDGET_ITEM_RETURN_IF_GOTO(item, error_noitem); if (!func) { elm_wdg_item_tooltip_unset(item->eo_obj); return; } wit = ELM_NEW(Elm_Widget_Item_Tooltip); if (!wit) goto error; wit->item = item; wit->func = func; wit->data = data; wit->del_cb = del_cb; elm_object_sub_tooltip_content_cb_set (item->view, item->widget, _elm_widget_item_tooltip_create, wit, _elm_widget_item_tooltip_del_cb); return; error_noitem: if (del_cb) del_cb((void *)data, NULL, item); return; error: if (del_cb) del_cb((void *)data, item->widget, item); } /** * @internal * * Unset tooltip from item * * @param item widget item to remove previously set tooltip. * * Remove tooltip from item. The callback provided as del_cb to * elm_widget_item_tooltip_content_cb_set() will be called to notify * it is not used anymore. * * @see elm_widget_item_tooltip_content_cb_set() * * @ingroup Widget */ EOLIAN static void _elm_widget_item_tooltip_unset(Eo *eo_item EINA_UNUSED, Elm_Widget_Item_Data *item) { ELM_WIDGET_ITEM_CHECK_OR_RETURN(item); ELM_WIDGET_ITEM_RETURN_IF_ONDEL(item); elm_object_tooltip_unset(item->view); } /** * @internal * * Sets a different style for this item tooltip. * * @note before you set a style you should define a tooltip with * elm_widget_item_tooltip_content_cb_set() or * elm_widget_item_tooltip_text_set() * * @param item widget item with tooltip already set. * @param style the theme style to use (default, transparent, ...) * * @ingroup Widget */ EOLIAN static void _elm_widget_item_tooltip_style_set(Eo *eo_item EINA_UNUSED, Elm_Widget_Item_Data *item, const char *style) { ELM_WIDGET_ITEM_CHECK_OR_RETURN(item); ELM_WIDGET_ITEM_RETURN_IF_ONDEL(item); elm_object_tooltip_style_set(item->view, style); } EOLIAN static Eina_Bool _elm_widget_item_tooltip_window_mode_set(Eo *eo_item EINA_UNUSED, Elm_Widget_Item_Data *item, Eina_Bool disable) { ELM_WIDGET_ITEM_CHECK_OR_RETURN(item, EINA_FALSE); ELM_WIDGET_ITEM_RETURN_IF_ONDEL(item, EINA_FALSE); return elm_object_tooltip_window_mode_set(item->view, disable); } EOLIAN static Eina_Bool _elm_widget_item_tooltip_window_mode_get(Eo *eo_item EINA_UNUSED, Elm_Widget_Item_Data *item) { ELM_WIDGET_ITEM_CHECK_OR_RETURN(item, EINA_FALSE); ELM_WIDGET_ITEM_RETURN_IF_ONDEL(item, EINA_FALSE); return elm_object_tooltip_window_mode_get(item->view); } /** * @internal * * Get the style for this item tooltip. * * @param item widget item with tooltip already set. * @return style the theme style in use, defaults to "default". If the * object does not have a tooltip set, then NULL is returned. * * @ingroup Widget */ EOLIAN static const char * _elm_widget_item_tooltip_style_get(Eo *eo_item EINA_UNUSED, Elm_Widget_Item_Data *item) { ELM_WIDGET_ITEM_CHECK_OR_RETURN(item, NULL); return elm_object_tooltip_style_get(item->view); } EOLIAN static void _elm_widget_item_cursor_set(Eo *eo_item EINA_UNUSED, Elm_Widget_Item_Data *item, const char *cursor) { ELM_WIDGET_ITEM_CHECK_OR_RETURN(item); ELM_WIDGET_ITEM_RETURN_IF_ONDEL(item); elm_object_sub_cursor_set(item->view, item->widget, cursor); } EOLIAN static const char * _elm_widget_item_cursor_get(Eo *eo_item EINA_UNUSED, Elm_Widget_Item_Data *item) { ELM_WIDGET_ITEM_CHECK_OR_RETURN(item, NULL); return elm_object_cursor_get(item->view); } EOLIAN static void _elm_widget_item_cursor_unset(Eo *eo_item EINA_UNUSED, Elm_Widget_Item_Data *item) { ELM_WIDGET_ITEM_CHECK_OR_RETURN(item); ELM_WIDGET_ITEM_RETURN_IF_ONDEL(item); elm_object_cursor_unset(item->view); } /** * @internal * * Sets a different style for this item cursor. * * @note before you set a style you should define a cursor with * elm_widget_item_cursor_set() * * @param item widget item with cursor already set. * @param style the theme style to use (default, transparent, ...) * * @ingroup Widget */ EOLIAN static void _elm_widget_item_cursor_style_set(Eo *eo_item EINA_UNUSED, Elm_Widget_Item_Data *item, const char *style) { ELM_WIDGET_ITEM_CHECK_OR_RETURN(item); ELM_WIDGET_ITEM_RETURN_IF_ONDEL(item); elm_object_cursor_style_set(item->view, style); } /** * @internal * * Get the style for this item cursor. * * @param item widget item with cursor already set. * @return style the theme style in use, defaults to "default". If the * object does not have a cursor set, then NULL is returned. * * @ingroup Widget */ EOLIAN static const char * _elm_widget_item_cursor_style_get(Eo *eo_item EINA_UNUSED, Elm_Widget_Item_Data *item) { ELM_WIDGET_ITEM_CHECK_OR_RETURN(item, NULL); return elm_object_cursor_style_get(item->view); } /** * @internal * * Set if the cursor set should be searched on the theme or should use * the provided by the engine, only. * * @note before you set if should look on theme you should define a cursor * with elm_object_cursor_set(). By default it will only look for cursors * provided by the engine. * * @param item widget item with cursor already set. * @param engine_only boolean to define it cursors should be looked only * between the provided by the engine or searched on widget's theme as well. * * @ingroup Widget */ EOLIAN static void _elm_widget_item_cursor_engine_only_set(Eo *eo_item EINA_UNUSED, Elm_Widget_Item_Data *item, Eina_Bool engine_only) { ELM_WIDGET_ITEM_CHECK_OR_RETURN(item); ELM_WIDGET_ITEM_RETURN_IF_ONDEL(item); elm_object_cursor_theme_search_enabled_set(item->view, !engine_only); } /** * @internal * * Get the cursor engine only usage for this item cursor. * * @param item widget item with cursor already set. * @return engine_only boolean to define it cursors should be looked only * between the provided by the engine or searched on widget's theme as well. If * the object does not have a cursor set, then EINA_FALSE is returned. * * @ingroup Widget */ EOLIAN static Eina_Bool _elm_widget_item_cursor_engine_only_get(Eo *eo_item EINA_UNUSED, Elm_Widget_Item_Data *item) { ELM_WIDGET_ITEM_CHECK_OR_RETURN(item, EINA_FALSE); return !elm_object_cursor_theme_search_enabled_get(item->view); } EOLIAN static void _elm_widget_item_part_content_set(Eo *eo_item EINA_UNUSED, Elm_Widget_Item_Data *item, const char *part EINA_UNUSED, Evas_Object *content EINA_UNUSED) { ERR_NOT_SUPPORTED(item, "elm_object_part_content_set()"); } EOLIAN static Evas_Object * _elm_widget_item_part_content_get(Eo *eo_item EINA_UNUSED, Elm_Widget_Item_Data *item, const char *part EINA_UNUSED) { ERR_NOT_SUPPORTED(item, "elm_object_part_content_get()"); return NULL; } EOLIAN static Evas_Object * _elm_widget_item_part_content_unset(Eo *eo_item EINA_UNUSED, Elm_Widget_Item_Data *item, const char *part EINA_UNUSED) { ERR_NOT_SUPPORTED(item, "elm_object_part_content_unset()"); return NULL; } EOLIAN static void _elm_widget_item_part_text_set(Eo *eo_item EINA_UNUSED, Elm_Widget_Item_Data *item, const char *part EINA_UNUSED, const char *label EINA_UNUSED) { ERR_NOT_SUPPORTED(item, "elm_object_part_text_set()"); } EOLIAN static const char * _elm_widget_item_part_text_get(Eo *eo_item EINA_UNUSED, Elm_Widget_Item_Data *item, const char *part EINA_UNUSED) { ERR_NOT_SUPPORTED(item, "elm_object_part_text_get()"); return NULL; } static void _elm_widget_item_part_text_custom_free(void *data) { Elm_Label_Data *label; label = data; eina_stringshare_del(label->part); eina_stringshare_del(label->text); free(label); } EOLIAN static void _elm_widget_item_part_text_custom_set(Eo *eo_item EINA_UNUSED, Elm_Widget_Item_Data *item, const char *part, const char *text) { Elm_Label_Data *label; ELM_WIDGET_ITEM_CHECK_OR_RETURN(item); ELM_WIDGET_ITEM_RETURN_IF_ONDEL(item); if (!item->labels) item->labels = eina_hash_stringshared_new(_elm_widget_item_part_text_custom_free); label = eina_hash_find(item->labels, part); if (!label) { label = malloc(sizeof(Elm_Label_Data)); if (!label) { ERR("Failed to allocate memory"); return; } label->part = eina_stringshare_add(part); label->text = eina_stringshare_add(text); eina_hash_add(item->labels, part, label); } else eina_stringshare_replace(&label->text, text); } EOLIAN static const char * _elm_widget_item_part_text_custom_get(Eo *eo_item EINA_UNUSED, Elm_Widget_Item_Data *item, const char *part) { Elm_Label_Data *label; ELM_WIDGET_ITEM_CHECK_OR_RETURN(item, NULL); label = eina_hash_find(item->labels, part); return label ? label->text : NULL; } static Eina_Bool _elm_widget_item_part_text_custom_foreach(const Eina_Hash *labels EINA_UNUSED, const void *key EINA_UNUSED, void *data, void *func_data) { Elm_Label_Data *label; Elm_Widget_Item_Data *item; label = data; item = func_data; elm_wdg_item_part_text_set(item->eo_obj, label->part, label->text); return EINA_TRUE; } EOLIAN static void _elm_widget_item_part_text_custom_update(Eo *eo_item EINA_UNUSED, Elm_Widget_Item_Data *item) { ELM_WIDGET_ITEM_CHECK_OR_RETURN(item); ELM_WIDGET_ITEM_RETURN_IF_ONDEL(item); if (item->labels) eina_hash_foreach(item->labels, _elm_widget_item_part_text_custom_foreach, item); } EOLIAN static void _elm_widget_item_signal_emit(Eo *eo_item EINA_UNUSED, Elm_Widget_Item_Data *item EINA_UNUSED, const char *emission EINA_UNUSED, const char *source EINA_UNUSED) { } EOLIAN static void _elm_widget_item_signal_callback_add(Eo *eo_item, Elm_Widget_Item_Data *item, const char *emission, const char *source, Elm_Object_Item_Signal_Cb func, void *data) { ELM_WIDGET_ITEM_CHECK_OR_RETURN(item); ELM_WIDGET_ITEM_RETURN_IF_ONDEL(item); EINA_SAFETY_ON_NULL_RETURN(func); Elm_Widget_Item_Signal_Data *wisd; wisd = malloc(sizeof(Elm_Widget_Item_Signal_Data)); if (!wisd) return; wisd->item = eo_item; wisd->func = (Elm_Widget_Item_Signal_Cb)func; wisd->data = data; wisd->emission = eina_stringshare_add(emission); wisd->source = eina_stringshare_add(source); if (_elm_widget_is(item->view)) elm_object_signal_callback_add(item->view, emission, source, _elm_widget_item_signal_cb, wisd); else if (efl_isa(item->view, EFL_CANVAS_LAYOUT_CLASS)) edje_object_signal_callback_add(item->view, emission, source, _elm_widget_item_signal_cb, wisd); else { WRN("The %s widget item doesn't support signal callback add!", efl_class_name_get(efl_class_get(item->widget))); free(wisd); return; } item->signals = eina_list_append(item->signals, wisd); } EOLIAN static void * _elm_widget_item_signal_callback_del(Eo *eo_item EINA_UNUSED, Elm_Widget_Item_Data *item, const char *emission, const char *source, Elm_Object_Item_Signal_Cb func) { Elm_Widget_Item_Signal_Data *wisd; Eina_List *l; ELM_WIDGET_ITEM_CHECK_OR_RETURN(item, NULL); ELM_WIDGET_ITEM_RETURN_IF_ONDEL(item, NULL); EINA_SAFETY_ON_NULL_RETURN_VAL(func, NULL); EINA_LIST_FOREACH(item->signals, l, wisd) { if ((wisd->func == (Elm_Widget_Item_Signal_Cb)func) && !strcmp(wisd->emission, emission) && !strcmp(wisd->source, source)) return _elm_widget_item_signal_callback_list_get(item, l); } return NULL; } EOLIAN static void _elm_widget_item_access_info_set(Eo *eo_item EINA_UNUSED, Elm_Widget_Item_Data *item, const char *txt) { ELM_WIDGET_ITEM_CHECK_OR_RETURN(item); ELM_WIDGET_ITEM_RETURN_IF_ONDEL(item); eina_stringshare_del(item->access_info); if (!txt) item->access_info = NULL; else item->access_info = eina_stringshare_add(txt); } EOLIAN static void _elm_widget_item_translate(Eo *eo_item EINA_UNUSED, Elm_Widget_Item_Data *item) { ELM_WIDGET_ITEM_CHECK_OR_RETURN(item); ELM_WIDGET_ITEM_RETURN_IF_ONDEL(item); #ifdef HAVE_GETTEXT Elm_Translate_String_Data *ts; EINA_INLIST_FOREACH(item->translate_strings, ts) { if (!ts->string) continue; const char *s = dgettext(ts->domain, ts->string); item->on_translate = EINA_TRUE; elm_wdg_item_part_text_set(item->eo_obj, ts->id, s); item->on_translate = EINA_FALSE; } #endif } EOLIAN static void _elm_widget_item_access_order_set(Eo *eo_item EINA_UNUSED, Elm_Widget_Item_Data *item, Eina_List *objs) { _elm_access_widget_item_access_order_set(item, objs); } EOLIAN static const Eina_List * _elm_widget_item_access_order_get(Eo *eo_item EINA_UNUSED, Elm_Widget_Item_Data *item) { return _elm_access_widget_item_access_order_get(item); } EOLIAN static void _elm_widget_item_access_order_unset(Eo *eo_item EINA_UNUSED, Elm_Widget_Item_Data *item) { _elm_access_widget_item_access_order_unset(item); } EOLIAN static Evas_Object* _elm_widget_item_access_register(Eo *eo_item EINA_UNUSED, Elm_Widget_Item_Data *item) { _elm_access_widget_item_register(item); return item->access_obj; } EOLIAN static void _elm_widget_item_access_unregister(Eo *eo_item EINA_UNUSED, Elm_Widget_Item_Data *item) { _elm_access_widget_item_unregister(item); } EOLIAN static Evas_Object* _elm_widget_item_access_object_get(const Eo *eo_item EINA_UNUSED, Elm_Widget_Item_Data *item) { return item->access_obj; } EOLIAN static Evas_Object * _elm_widget_item_focus_next_object_get(const Eo *eo_item EINA_UNUSED, Elm_Widget_Item_Data *item, Elm_Focus_Direction dir) { Evas_Object *ret = NULL; if (dir == ELM_FOCUS_PREVIOUS) ret = item->focus_previous; else if (dir == ELM_FOCUS_NEXT) ret = item->focus_next; else if (dir == ELM_FOCUS_UP) ret = item->focus_up; else if (dir == ELM_FOCUS_DOWN) ret = item->focus_down; else if (dir == ELM_FOCUS_RIGHT) ret = item->focus_right; else if (dir == ELM_FOCUS_LEFT) ret = item->focus_left; return ret; } EOLIAN static void _elm_widget_item_focus_next_object_set(Eo *eo_item EINA_UNUSED, Elm_Widget_Item_Data *item, Evas_Object *next, Elm_Focus_Direction dir) { if (dir == ELM_FOCUS_PREVIOUS) item->focus_previous = next; else if (dir == ELM_FOCUS_NEXT) item->focus_next = next; else if (dir == ELM_FOCUS_UP) item->focus_up = next; else if (dir == ELM_FOCUS_DOWN) item->focus_down = next; else if (dir == ELM_FOCUS_RIGHT) item->focus_right = next; else if (dir == ELM_FOCUS_LEFT) item->focus_left = next; } EOLIAN static Elm_Object_Item* _elm_widget_item_focus_next_item_get(const Eo *eo_item EINA_UNUSED, Elm_Widget_Item_Data *item, Elm_Focus_Direction dir) { Elm_Object_Item *ret = NULL; if (dir == ELM_FOCUS_PREVIOUS) ret = item->item_focus_previous; else if (dir == ELM_FOCUS_NEXT) ret = item->item_focus_next; else if (dir == ELM_FOCUS_UP) ret = item->item_focus_up; else if (dir == ELM_FOCUS_DOWN) ret = item->item_focus_down; else if (dir == ELM_FOCUS_RIGHT) ret = item->item_focus_right; else if (dir == ELM_FOCUS_LEFT) ret = item->item_focus_left; return ret; } EOLIAN static void _elm_widget_item_focus_next_item_set(Eo *eo_item EINA_UNUSED, Elm_Widget_Item_Data *item, Elm_Object_Item *next_item, Elm_Focus_Direction dir) { if (dir == ELM_FOCUS_PREVIOUS) item->item_focus_previous = next_item; else if (dir == ELM_FOCUS_NEXT) item->item_focus_next = next_item; else if (dir == ELM_FOCUS_UP) item->item_focus_up = next_item; else if (dir == ELM_FOCUS_DOWN) item->item_focus_down = next_item; else if (dir == ELM_FOCUS_RIGHT) item->item_focus_right = next_item; else if (dir == ELM_FOCUS_LEFT) item->item_focus_left = next_item; } /* happy debug functions */ #ifdef ELM_DEBUG static void _sub_obj_tree_dump(const Evas_Object *obj, int lvl) { int i; for (i = 0; i < lvl * 3; i++) putchar(' '); if (_elm_widget_is(obj)) { Eina_List *l; INTERNAL_ENTRY; DBG("+ %s(%p)\n", elm_widget_type_get(obj), obj); EINA_LIST_FOREACH(sd->subobjs, l, obj) _sub_obj_tree_dump(obj, lvl + 1); } else DBG("+ %s(%p)\n", evas_object_type_get(obj), obj); } static void _sub_obj_tree_dot_dump(const Evas_Object *obj, FILE *output) { if (!_elm_widget_is(obj)) return; INTERNAL_ENTRY; Eina_Bool visible = evas_object_visible_get(obj); Eina_Bool disabled = elm_widget_disabled_get(obj); Eina_Bool focused = efl_ui_focus_object_focus_get(obj); Eina_Bool can_focus = elm_widget_can_focus_get(obj); if (sd->parent_obj) { fprintf(output, "\"%p\" -- \"%p\" [ color=black", sd->parent_obj, obj); if (focused) fprintf(output, ", style=bold"); if (!visible) fprintf(output, ", color=gray28"); fprintf(output, " ];\n"); } fprintf(output, "\"%p\" [ label = \"{%p|%s|%s|visible: %d|" "disabled: %d|focused: %d/%d|focus order:%d}\"", obj, obj, elm_widget_type_get(obj), evas_object_name_get(obj), visible, disabled, focused, can_focus, sd->focus_order); if (focused) fprintf(output, ", style=bold"); if (!visible) fprintf(output, ", fontcolor=gray28"); if ((disabled) || (!visible)) fprintf(output, ", color=gray"); fprintf(output, " ];\n"); Eina_List *l; Evas_Object *o; EINA_LIST_FOREACH(sd->subobjs, l, o) _sub_obj_tree_dot_dump(o, output); } #endif EAPI void elm_widget_tree_dump(const Evas_Object *top) { #ifdef ELM_DEBUG if (!_elm_widget_is(top)) return; _sub_obj_tree_dump(top, 0); #else (void)top; return; #endif } EAPI void elm_widget_tree_dot_dump(const Evas_Object *top, FILE *output) { #ifdef ELM_DEBUG if (!_elm_widget_is(top)) return; fprintf(output, "graph " " { node [shape=record];\n"); _sub_obj_tree_dot_dump(top, output); fprintf(output, "}\n"); #else (void)top; (void)output; return; #endif } static void _focus_event_changed(void *data EINA_UNUSED, const Efl_Event *event) { if (efl_ui_focus_object_focus_get(event->object)) evas_object_smart_callback_call(event->object, "focused", NULL); else evas_object_smart_callback_call(event->object, "unfocused", NULL); } EOLIAN static Eo * _efl_ui_widget_efl_object_constructor(Eo *obj, Elm_Widget_Smart_Data *sd EINA_UNUSED) { Eo *parent = NULL; sd->on_create = EINA_TRUE; if (_elm_legacy_add) { sd->legacy = _elm_legacy_add; _elm_legacy_add = EINA_FALSE; } efl_canvas_group_clipped_set(obj, EINA_FALSE); obj = efl_constructor(efl_super(obj, MY_CLASS)); efl_canvas_object_type_set(obj, MY_CLASS_NAME_LEGACY); evas_object_smart_callbacks_descriptions_set(obj, _smart_callbacks); parent = efl_parent_get(obj); efl_ui_widget_parent_set(obj, parent); sd->on_create = EINA_FALSE; efl_access_role_set(obj, EFL_ACCESS_ROLE_UNKNOWN); efl_event_callback_add(obj, EFL_UI_FOCUS_OBJECT_EVENT_FOCUS_CHANGED, _focus_event_changed, NULL); return obj; } EOLIAN static Efl_Object* _efl_ui_widget_efl_object_finalize(Eo *obj, Elm_Widget_Smart_Data *pd) { Eo *eo; eo = efl_finalize(efl_super(obj, MY_CLASS)); _full_eval(obj, pd); return eo; } EOLIAN static void _efl_ui_widget_efl_object_destructor(Eo *obj, Elm_Widget_Smart_Data *sd) { if (sd->manager.provider) { efl_event_callback_del(sd->manager.provider, EFL_UI_FOCUS_OBJECT_EVENT_MANAGER_CHANGED, _manager_changed_cb, obj); sd->manager.provider = NULL; } efl_access_attributes_clear(obj); efl_access_removed(obj); if (sd->logical.parent) { efl_weak_unref(&sd->logical.parent); sd->logical.parent = NULL; } sd->on_destroy = EINA_TRUE; efl_destructor(efl_super(obj, EFL_UI_WIDGET_CLASS)); sd->on_destroy = EINA_FALSE; } /* internal eo */ static void _efl_ui_widget_legacy_ctor(Eo *obj, Elm_Widget_Smart_Data *sd) { efl_canvas_object_legacy_ctor(efl_super(obj, MY_CLASS)); sd->legacy = EINA_TRUE; _elm_legacy_add = EINA_FALSE; } EOLIAN static void _efl_ui_widget_efl_object_debug_name_override(Eo *obj, Elm_Widget_Smart_Data *sd EINA_UNUSED, Eina_Strbuf *sb) { const char *focus = ""; if (efl_ui_focus_object_focus_get(obj)) focus = ":focused"; efl_debug_name_override(efl_super(obj, MY_CLASS), sb); eina_strbuf_append_printf(sb, "%s", focus); } EOLIAN static Eina_Bool -_efl_ui_widget_on_focus_update(Eo *obj, Elm_Widget_Smart_Data *sd) +_efl_ui_widget_efl_ui_focus_object_on_focus_update(Eo *obj, Elm_Widget_Smart_Data *sd) { Eina_Bool focused; if (!elm_widget_can_focus_get(obj)) return EINA_FALSE; focused = efl_ui_focus_object_focus_get(obj); if (!sd->resize_obj) evas_object_focus_set(obj, focused); if (_elm_config->atspi_mode && !elm_widget_child_can_focus_get(obj)) efl_access_state_changed_signal_emit(obj, EFL_ACCESS_STATE_FOCUSED, focused); return EINA_TRUE; } EOLIAN static Eina_Bool _efl_ui_widget_on_disabled_update(Eo *obj EINA_UNUSED, Elm_Widget_Smart_Data *_pd EINA_UNUSED, Eina_Bool disabled EINA_UNUSED) { return EINA_FALSE; } EOLIAN static Eina_Bool _efl_ui_widget_widget_event(Eo *obj EINA_UNUSED, Elm_Widget_Smart_Data *_pd EINA_UNUSED, const Efl_Event *eo_event EINA_UNUSED, Evas_Object *source EINA_UNUSED) { return EINA_FALSE; } EOLIAN static Eina_Bool _efl_ui_widget_on_access_activate(Eo *obj EINA_UNUSED, Elm_Widget_Smart_Data *_pd EINA_UNUSED, Efl_Ui_Activate act EINA_UNUSED) { WRN("The %s widget does not implement the \"activate\" functions.", efl_class_name_get(efl_class_get(obj))); return EINA_TRUE; } EOLIAN static void _efl_ui_widget_class_constructor(Efl_Class *klass) { evas_smart_legacy_type_register(MY_CLASS_NAME_LEGACY, klass); } EOLIAN static Eina_Bool _efl_ui_widget_efl_access_component_focus_grab(Eo *obj, Elm_Widget_Smart_Data *pd EINA_UNUSED) { if (elm_object_focus_allow_get(obj)) { Ecore_Evas *ee = ecore_evas_ecore_evas_get(evas_object_evas_get(obj)); if (!ee) return EINA_FALSE; ecore_evas_activate(ee); elm_object_focus_set(obj, EINA_TRUE); return EINA_TRUE; } return EINA_FALSE; } EOLIAN static const char* _efl_ui_widget_efl_access_name_get(Eo *obj, Elm_Widget_Smart_Data *_pd EINA_UNUSED) { const char *ret, *name; name = efl_access_name_get(efl_super(obj, EFL_UI_WIDGET_CLASS)); if (name) return name; ret = elm_object_text_get(obj); if (!ret) return NULL; return _elm_widget_accessible_plain_name_get(obj, ret); } EOLIAN static Eina_List* _efl_ui_widget_efl_access_children_get(Eo *obj EINA_UNUSED, Elm_Widget_Smart_Data *pd) { Eina_List *l, *accs = NULL; Evas_Object *widget; Efl_Access_Type type; EINA_LIST_FOREACH(pd->subobjs, l, widget) { if (!elm_object_widget_check(widget)) continue; if (!efl_isa(widget, EFL_ACCESS_MIXIN)) continue; type = efl_access_type_get(widget); if (type == EFL_ACCESS_TYPE_DISABLED) continue; if (type == EFL_ACCESS_TYPE_SKIPPED) { Eina_List *children; children = efl_access_children_get(widget); accs = eina_list_merge(accs, children); continue; } accs = eina_list_append(accs, widget); } return accs; } EOLIAN static Eo* _efl_ui_widget_efl_access_parent_get(Eo *obj, Elm_Widget_Smart_Data *pd EINA_UNUSED) { Efl_Access_Type type; Efl_Access *parent = obj; do { ELM_WIDGET_DATA_GET_OR_RETURN(parent, wd, NULL); parent = wd->parent_obj; type = efl_access_type_get(parent); } while (parent && (type == EFL_ACCESS_TYPE_SKIPPED)); return efl_isa(parent, EFL_ACCESS_MIXIN) ? parent : NULL; } EOLIAN static Efl_Access_State_Set _efl_ui_widget_efl_access_state_set_get(Eo *obj, Elm_Widget_Smart_Data *pd EINA_UNUSED) { Efl_Access_State_Set states = 0; states = efl_access_state_set_get(efl_super(obj, EFL_UI_WIDGET_CLASS)); if (evas_object_visible_get(obj)) { STATE_TYPE_SET(states, EFL_ACCESS_STATE_VISIBLE); if (_elm_widget_onscreen_is(obj)) STATE_TYPE_SET(states, EFL_ACCESS_STATE_SHOWING); } if (!elm_widget_child_can_focus_get(obj)) { if (elm_object_focus_allow_get(obj)) STATE_TYPE_SET(states, EFL_ACCESS_STATE_FOCUSABLE); if (elm_object_focus_get(obj)) STATE_TYPE_SET(states, EFL_ACCESS_STATE_FOCUSED); } if (!elm_object_disabled_get(obj)) { STATE_TYPE_SET(states, EFL_ACCESS_STATE_ENABLED); STATE_TYPE_SET(states, EFL_ACCESS_STATE_SENSITIVE); } return states; } EOLIAN static Eina_List* _efl_ui_widget_efl_access_attributes_get(Eo *obj, Elm_Widget_Smart_Data *pd EINA_UNUSED) { Eina_List *attr_list = NULL; attr_list = efl_access_attributes_get(efl_super(obj, EFL_UI_WIDGET_CLASS)); //Add type and style information in addition. Efl_Access_Attribute *attr = NULL; attr = calloc(1, sizeof(Efl_Access_Attribute)); if (attr) { attr->key = eina_stringshare_add("type"); attr->value = eina_stringshare_add(elm_widget_type_get(obj)); attr_list = eina_list_append(attr_list, attr); } attr = calloc(1, sizeof(Efl_Access_Attribute)); if (attr) { attr->key = eina_stringshare_add("style"); attr->value = eina_stringshare_add(elm_widget_style_get(obj)); attr_list = eina_list_append(attr_list, attr); } return attr_list; } EOLIAN static Eina_List * _elm_widget_item_efl_access_attributes_get(Eo *eo_item, Elm_Widget_Item_Data *pd EINA_UNUSED) { Eina_List *attr_list = NULL; attr_list = efl_access_attributes_get(efl_super(eo_item, ELM_WIDGET_ITEM_CLASS)); Efl_Access_Attribute *attr = NULL; attr = calloc(1, sizeof(Efl_Access_Attribute)); if (attr) { attr->key = eina_stringshare_add("style"); attr->value = eina_stringshare_add(elm_object_item_style_get(eo_item)); attr_list = eina_list_append(attr_list, attr); } return attr_list; } EOLIAN static Eina_Rect _elm_widget_item_efl_access_component_extents_get(Eo *obj EINA_UNUSED, Elm_Widget_Item_Data *sd EINA_UNUSED, Eina_Bool screen_coords) { Eina_Rect r = EINA_RECT(-1, -1, -1, -1); int ee_x, ee_y; if (!sd->view) return r; r = efl_gfx_geometry_get(sd->view); if (screen_coords) { Ecore_Evas *ee = ecore_evas_ecore_evas_get(evas_object_evas_get(sd->view)); if (ee) { ecore_evas_geometry_get(ee, &ee_x, &ee_y, NULL, NULL); r.x += ee_x; r.y += ee_y; } } return r; } EOLIAN static Eina_Bool _elm_widget_item_efl_access_component_extents_set(Eo *obj EINA_UNUSED, Elm_Widget_Item_Data *sd EINA_UNUSED, Eina_Bool screen_coords EINA_UNUSED, Eina_Rect r EINA_UNUSED) { return EINA_FALSE; } EOLIAN static int _elm_widget_item_efl_access_component_layer_get(Eo *obj EINA_UNUSED, Elm_Widget_Item_Data *sd EINA_UNUSED) { if (!sd->view) return -1; return evas_object_layer_get(sd->view); } EOLIAN static Eina_Bool _elm_widget_item_efl_access_component_focus_grab(Eo *obj EINA_UNUSED, Elm_Widget_Item_Data *_pd EINA_UNUSED) { elm_object_item_focus_set(obj, EINA_TRUE); return elm_object_item_focus_get(obj); } EOLIAN static double _elm_widget_item_efl_access_component_alpha_get(Eo *obj EINA_UNUSED, Elm_Widget_Item_Data *sd EINA_UNUSED) { int alpha; if (!sd->view) return -1.0; evas_object_color_get(sd->view, NULL, NULL, NULL, &alpha); return (double)alpha / 255.0; } EOLIAN static Efl_Object * _efl_ui_widget_efl_object_provider_find(const Eo *obj, Elm_Widget_Smart_Data *pd, const Efl_Object *klass) { Efl_Object *lookup = NULL; if ((klass == EFL_CONFIG_INTERFACE) || (klass == EFL_CONFIG_GLOBAL_CLASS)) return _efl_config_obj; if (pd->provider_lookup) return NULL; pd->provider_lookup = EINA_TRUE; if (pd->parent_obj) lookup = efl_provider_find(pd->parent_obj, klass); if (!lookup) lookup = efl_provider_find(efl_super(obj, MY_CLASS), klass); pd->provider_lookup = EINA_FALSE; return lookup; } EOLIAN static Efl_Ui_Focus_Manager* _efl_ui_widget_efl_ui_focus_object_focus_parent_get(Eo *obj EINA_UNUSED, Elm_Widget_Smart_Data *pd EINA_UNUSED) { return pd->focus.parent; } EOLIAN static Efl_Ui_Focus_Manager* _efl_ui_widget_efl_ui_focus_object_focus_manager_get(Eo *obj EINA_UNUSED, Elm_Widget_Smart_Data *pd EINA_UNUSED) { return pd->focus.manager; } EOLIAN static Eina_Rect _efl_ui_widget_efl_ui_focus_object_focus_geometry_get(Eo *obj, Elm_Widget_Smart_Data *pd EINA_UNUSED) { return efl_gfx_geometry_get(obj); } EOLIAN static void _efl_ui_widget_efl_ui_focus_object_focus_set(Eo *obj, Elm_Widget_Smart_Data *pd, Eina_Bool focus) { pd->focused = focus; efl_ui_focus_object_focus_set(efl_super(obj, MY_CLASS), focus); - efl_ui_widget_on_focus_update(obj); + efl_ui_focus_object_on_focus_update(obj); } EOLIAN static Efl_Ui_Focus_Manager* _efl_ui_widget_focus_manager_create(Eo *obj EINA_UNUSED, Elm_Widget_Smart_Data *pd EINA_UNUSED, Efl_Ui_Focus_Object *root EINA_UNUSED) { ERR("No manager presented"); return NULL; } /* Legacy APIs */ /* elm_object_content_xxx APIs are supposed to work on all objects for which * elm_object_widget_check() returns true. The below checks avoid printing out * undesired ERR messages. */ EAPI void elm_widget_content_part_set(Evas_Object *obj, const char *part, Evas_Object *content) { ELM_WIDGET_CHECK(obj); if (efl_isa(obj, EFL_UI_LAYOUT_CLASS)) { elm_layout_content_set(obj, part, content); return; } if (!efl_isa(obj, EFL_PART_INTERFACE)) return; if (!part) { part = efl_ui_widget_default_content_part_get(obj); if (!part) return; } efl_content_set(efl_part(obj, part), content); } EAPI Evas_Object * elm_widget_content_part_get(const Evas_Object *obj, const char *part) { ELM_WIDGET_CHECK(obj) NULL; if (efl_isa(obj, EFL_UI_LAYOUT_CLASS)) return elm_layout_content_get(obj, part); if (!efl_isa(obj, EFL_PART_INTERFACE)) return NULL; if (!part) { part = efl_ui_widget_default_content_part_get(obj); if (!part) return NULL; } return efl_content_get(efl_part(obj, part)); } EAPI Evas_Object * elm_widget_content_part_unset(Evas_Object *obj, const char *part) { ELM_WIDGET_CHECK(obj) NULL; if (efl_isa(obj, EFL_UI_LAYOUT_CLASS)) return elm_layout_content_unset(obj, part); if (!efl_isa(obj, EFL_PART_INTERFACE)) return NULL; if (!part) { part = efl_ui_widget_default_content_part_get(obj); if (!part) return NULL; } return efl_content_unset(efl_part(obj, part)); } EAPI void elm_widget_signal_emit(Eo *obj, const char *emission, const char *source) { ELM_WIDGET_CHECK(obj); if (evas_object_smart_type_check(obj, "elm_layout")) elm_layout_signal_emit(obj, emission, source); else if (evas_object_smart_type_check(obj, "elm_icon")) { WRN("Deprecated function. This functionality on icon objects" " will be dropped on a next release."); _elm_icon_signal_emit(obj, emission, source); } } EAPI void elm_widget_signal_callback_add(Eo *obj, const char *emission, const char *source, Edje_Signal_Cb func, void *data) { ELM_WIDGET_CHECK(obj); EINA_SAFETY_ON_NULL_RETURN(func); if (evas_object_smart_type_check(obj, "elm_layout")) elm_layout_signal_callback_add(obj, emission, source, func, data); else if (evas_object_smart_type_check(obj, "elm_icon")) { WRN("Deprecated function. This functionality on icon objects" " will be dropped on a next release."); _elm_icon_signal_callback_add(obj, emission, source, func, data); } } EAPI void * elm_widget_signal_callback_del(Eo *obj, const char *emission, const char *source, Edje_Signal_Cb func) { void *data = NULL; ELM_WIDGET_CHECK(obj) NULL; EINA_SAFETY_ON_NULL_RETURN_VAL(func, NULL); if (evas_object_smart_type_check(obj, "elm_layout")) data = elm_layout_signal_callback_del(obj, emission, source, func); else if (evas_object_smart_type_check(obj, "elm_icon")) { WRN("Deprecated function. This functionality on icon objects" " will be dropped on a next release."); data = _elm_icon_signal_callback_del(obj, emission, source, func); } return data; } /* Widget Shadow Begin */ typedef struct _Widget_Shadow { Eo *widget; Eo *surface; struct { double rx, ry, ox, oy, grow; int r, g, b, a; } props; Eina_Stringshare *code, *name; } Widget_Shadow; static void _widget_shadow_update(Widget_Shadow *shadow); static void _widget_shadow_del_cb(void *data, const Efl_Event *ev EINA_UNUSED) { Widget_Shadow *shadow = data; efl_del(shadow->surface); free(shadow); } static void _widget_shadow_event_cb(void *data, const Efl_Event *ev EINA_UNUSED) { Widget_Shadow *shadow = data; _widget_shadow_update(shadow); } EFL_CALLBACKS_ARRAY_DEFINE(widget_shadow_cb, { EFL_EVENT_DEL, _widget_shadow_del_cb }, { EFL_GFX_EVENT_MOVE, _widget_shadow_event_cb }, { EFL_GFX_EVENT_RESIZE, _widget_shadow_event_cb }, { EFL_GFX_EVENT_RESTACK, _widget_shadow_event_cb }, { EFL_GFX_EVENT_HIDE, _widget_shadow_event_cb }, { EFL_GFX_EVENT_SHOW, _widget_shadow_event_cb }) static Widget_Shadow * _widget_shadow_part_get(Eo *part_obj) { Elm_Part_Data *pd = efl_data_scope_get(part_obj, EFL_UI_WIDGET_PART_CLASS); Widget_Shadow *shadow; Eo *widget = pd->obj; shadow = efl_key_data_get(widget, "__elm_shadow"); if (!shadow) { shadow = calloc(1, sizeof(*shadow)); shadow->widget = pd->obj; efl_key_data_set(widget, "__elm_shadow", shadow); efl_event_callback_array_add(widget, widget_shadow_cb(), shadow); } return shadow; } static void _widget_shadow_update(Widget_Shadow *ws) { int l = 0, r = 0, t = 0, b = 0; Eina_Rect srect, wrect; char filter[1024]; #define FILTER_FMT \ "a = buffer { 'alpha' }" \ "grow { %f, dst = a, alphaonly = true }" \ "blur { src = a, rx = %f, ry = %f, color = color(%d,%d,%d,%d) }" if (!ws->surface) { ws->surface = efl_add(EFL_CANVAS_PROXY_CLASS, ws->widget); efl_gfx_fill_auto_set(ws->surface, 1); efl_canvas_proxy_source_clip_set(ws->surface, EINA_FALSE); efl_canvas_proxy_source_events_set(ws->surface, EINA_FALSE); efl_canvas_proxy_source_set(ws->surface, ws->widget); } if (!ws->code) { snprintf(filter, sizeof(filter), FILTER_FMT, ws->props.grow, ws->props.rx, ws->props.ry, ws->props.r, ws->props.g, ws->props.b, ws->props.a); } efl_gfx_filter_program_set(ws->surface, ws->code ? ws->code : filter, ws->name ? ws->name : "shadow"); efl_gfx_filter_padding_get(ws->surface, &l, &r, &t, &b); wrect = efl_gfx_geometry_get(ws->widget); srect.x = wrect.x + (int) (-l + ws->props.ox); srect.y = wrect.y + (int) (-t + ws->props.oy); srect.w = wrect.w + (int) (l + r); srect.h = wrect.h + (int) (t + b); if ((!ws->props.a && !ws->code) || !efl_gfx_visible_get(ws->widget)) { efl_gfx_visible_set(ws->surface, EINA_FALSE); return; } efl_canvas_object_clip_set(ws->surface, efl_canvas_object_clip_get(ws->widget)); efl_canvas_group_member_add(efl_canvas_object_render_parent_get(ws->widget), ws->surface); efl_gfx_geometry_set(ws->surface, srect); efl_gfx_stack_below(ws->surface, ws->widget); efl_gfx_visible_set(ws->surface, EINA_TRUE); } static void _elm_widget_shadow_update(Efl_Ui_Widget *obj) { Widget_Shadow *shadow = _widget_shadow_part_get(obj); _widget_shadow_update(shadow); } EOLIAN static void _efl_ui_widget_part_shadow_efl_gfx_blur_offset_set(Eo *obj, void *_pd EINA_UNUSED, double ox, double oy) { Widget_Shadow *shadow = _widget_shadow_part_get(obj); shadow->props.ox = ox; shadow->props.oy = oy; _widget_shadow_update(shadow); } EOLIAN static void _efl_ui_widget_part_shadow_efl_gfx_blur_offset_get(Eo *obj, void *_pd EINA_UNUSED, double *ox, double *oy) { Widget_Shadow *shadow = _widget_shadow_part_get(obj); if (ox) *ox = shadow->props.ox; if (oy) *oy = shadow->props.oy; } EOLIAN static void _efl_ui_widget_part_shadow_efl_gfx_blur_radius_set(Eo *obj, void *_pd EINA_UNUSED, double rx, double ry) { Widget_Shadow *shadow = _widget_shadow_part_get(obj); shadow->props.rx = rx; shadow->props.ry = ry; _widget_shadow_update(shadow); } EOLIAN static void _efl_ui_widget_part_shadow_efl_gfx_blur_radius_get(Eo *obj, void *_pd EINA_UNUSED, double *rx, double *ry) { Widget_Shadow *shadow = _widget_shadow_part_get(obj); if (rx) *rx = shadow->props.rx; if (ry) *ry = shadow->props.ry; } EOLIAN static void _efl_ui_widget_part_shadow_efl_gfx_color_color_set(Eo *obj, void *_pd EINA_UNUSED, int r, int g, int b, int a) { Widget_Shadow *shadow = _widget_shadow_part_get(obj); shadow->props.r = r; shadow->props.g = g; shadow->props.b = b; shadow->props.a = a; _widget_shadow_update(shadow); } EOLIAN static void _efl_ui_widget_part_shadow_efl_gfx_color_color_get(Eo *obj, void *_pd EINA_UNUSED, int *r, int *g, int *b, int *a) { Widget_Shadow *shadow = _widget_shadow_part_get(obj); if (r) *r = shadow->props.r; if (g) *g = shadow->props.g; if (b) *b = shadow->props.b; if (a) *a = shadow->props.a; } EOLIAN static void _efl_ui_widget_part_shadow_efl_gfx_blur_grow_set(Eo *obj, void *_pd EINA_UNUSED, double radius) { Widget_Shadow *shadow = _widget_shadow_part_get(obj); shadow->props.grow = radius; _widget_shadow_update(shadow); } EOLIAN static double _efl_ui_widget_part_shadow_efl_gfx_blur_grow_get(Eo *obj, void *_pd EINA_UNUSED) { Widget_Shadow *shadow = _widget_shadow_part_get(obj); return shadow->props.grow; } EOLIAN static void _efl_ui_widget_part_shadow_efl_gfx_filter_filter_program_set(Eo *obj, void *_pd EINA_UNUSED, const char *code, const char *name) { Widget_Shadow *ws = _widget_shadow_part_get(obj); eina_stringshare_replace(&ws->code, code); eina_stringshare_replace(&ws->name, name); _widget_shadow_update(ws); } EOLIAN static void _efl_ui_widget_part_shadow_efl_gfx_filter_filter_program_get(Eo *obj, void *_pd EINA_UNUSED, const char **code, const char **name) { Widget_Shadow *ws = _widget_shadow_part_get(obj); efl_gfx_filter_program_get(ws->surface, code, name); } EOLIAN static void _efl_ui_widget_part_shadow_efl_gfx_filter_filter_source_set(Eo *obj, void *_pd EINA_UNUSED, const char *name, Efl_Gfx *source) { Widget_Shadow *ws = _widget_shadow_part_get(obj); _widget_shadow_update(ws); efl_gfx_filter_source_set(ws->surface, name, source); } EOLIAN static Efl_Gfx * _efl_ui_widget_part_shadow_efl_gfx_filter_filter_source_get(Eo *obj, void *_pd EINA_UNUSED, const char *name) { Widget_Shadow *ws = _widget_shadow_part_get(obj); return efl_gfx_filter_source_get(ws->surface, name); } EOLIAN static void _efl_ui_widget_part_shadow_efl_gfx_filter_filter_data_set(Eo *obj, void *_pd EINA_UNUSED, const char *name, const char *value, Eina_Bool execute) { Widget_Shadow *ws = _widget_shadow_part_get(obj); _widget_shadow_update(ws); efl_gfx_filter_data_set(ws->surface, name, value, execute); } EOLIAN static void _efl_ui_widget_part_shadow_efl_gfx_filter_filter_data_get(Eo *obj, void *_pd EINA_UNUSED, const char *name, const char **value, Eina_Bool *execute) { Widget_Shadow *ws = _widget_shadow_part_get(obj); efl_gfx_filter_data_get(ws->surface, name, value, execute); } EOLIAN static void _efl_ui_widget_part_shadow_efl_gfx_filter_filter_padding_get(Eo *obj, void *_pd EINA_UNUSED, int *l, int *r, int *t, int *b) { Widget_Shadow *ws = _widget_shadow_part_get(obj); efl_gfx_filter_padding_get(ws->surface, l, r, t, b); } EOLIAN static void _efl_ui_widget_part_shadow_efl_gfx_filter_filter_state_set(Eo *obj, void *_pd EINA_UNUSED, const char *cur_state, double cur_val, const char *next_state, double next_val, double pos) { Widget_Shadow *ws = _widget_shadow_part_get(obj); efl_gfx_filter_state_set(ws->surface, cur_state, cur_val, next_state, next_val, pos); } EOLIAN static void _efl_ui_widget_part_shadow_efl_gfx_filter_filter_state_get(Eo *obj, void *_pd EINA_UNUSED, const char **cur_state, double *cur_val, const char **next_state, double *next_val, double *pos) { Widget_Shadow *ws = _widget_shadow_part_get(obj); efl_gfx_filter_state_get(ws->surface, cur_state, cur_val, next_state, next_val, pos); } #include "efl_ui_widget_part_shadow.eo.c" /* Widget Shadow End */ /* Efl.Part implementation */ EOLIAN static Efl_Object * _efl_ui_widget_efl_part_part(const Eo *obj, Elm_Widget_Smart_Data *wd EINA_UNUSED, const char *part) { if (eina_streq(part, "background")) return ELM_PART_IMPLEMENT(EFL_UI_WIDGET_PART_BG_CLASS, obj, part); else if (eina_streq(part, "shadow")) return ELM_PART_IMPLEMENT(EFL_UI_WIDGET_PART_SHADOW_CLASS, obj, part); return ELM_PART_IMPLEMENT(EFL_UI_WIDGET_PART_CLASS, obj, part); } EOLIAN static void \ _efl_ui_widget_part_efl_object_destructor(Eo *obj, Elm_Part_Data *pd) { ELM_PART_HOOK; eina_tmpstr_del(pd->part); efl_destructor(efl_super(obj, EFL_UI_WIDGET_PART_CLASS)); } #include "efl_ui_widget_part.eo.c" /* Efl.Part end */ /* Efl.Part Bg implementation */ Efl_Canvas_Object * _efl_ui_widget_bg_get(Efl_Ui_Widget *obj) { Elm_Widget_Smart_Data *sd = efl_data_scope_get(obj, MY_CLASS); Evas_Object *bg_obj = sd->bg; if (!bg_obj) { bg_obj = efl_add(EFL_UI_BG_WIDGET_CLASS, obj); EINA_SAFETY_ON_NULL_RETURN_VAL(bg_obj, NULL); sd->bg = bg_obj; efl_canvas_group_member_add(obj, sd->bg); evas_object_stack_below(sd->bg, sd->resize_obj); _smart_reconfigure(sd); } return bg_obj; } static inline Efl_Canvas_Object * efl_ui_widget_part_bg_get(Eo *part_obj) { Elm_Part_Data *pd = efl_data_scope_get(part_obj, EFL_UI_WIDGET_PART_CLASS); return _efl_ui_widget_bg_get(pd->obj); } EOLIAN static Eina_Bool _efl_ui_widget_part_bg_efl_file_file_set(Eo *obj, void *pd EINA_UNUSED, const char *file, const char *key) { Evas_Object *bg_obj = efl_ui_widget_part_bg_get(obj); return efl_file_set(bg_obj, file, key); } EOLIAN static void _efl_ui_widget_part_bg_efl_file_file_get(Eo *obj, void *pd EINA_UNUSED, const char **file, const char **key) { Evas_Object *bg_obj = efl_ui_widget_part_bg_get(obj); efl_file_get(bg_obj, file, key); } EOLIAN static void _efl_ui_widget_part_bg_efl_gfx_color_color_set(Eo *obj, void *pd EINA_UNUSED, int r, int g, int b, int a) { Evas_Object *bg_obj = efl_ui_widget_part_bg_get(obj); efl_gfx_color_set(bg_obj, r, g, b, a); } EOLIAN static void _efl_ui_widget_part_bg_efl_gfx_color_color_get(Eo *obj, void *pd EINA_UNUSED, int *r, int *g, int *b, int *a) { Evas_Object *bg_obj = efl_ui_widget_part_bg_get(obj); efl_gfx_color_get(bg_obj, r, g, b, a); } EOLIAN static void _efl_ui_widget_part_bg_efl_image_scale_type_set(Eo *obj, void *pd EINA_UNUSED, Efl_Image_Scale_Type scale_type) { Evas_Object *bg_obj = efl_ui_widget_part_bg_get(obj); efl_image_scale_type_set(bg_obj, scale_type); } EOLIAN static Efl_Image_Scale_Type _efl_ui_widget_part_bg_efl_image_scale_type_get(Eo *obj, void *pd EINA_UNUSED) { Evas_Object *bg_obj = efl_ui_widget_part_bg_get(obj); return efl_image_scale_type_get(bg_obj); } #include "efl_ui_widget_part_bg.eo.c" /* Efl.Part Bg end */ /* Internal EO APIs and hidden overrides */ EFL_FUNC_BODY_CONST(efl_ui_widget_default_content_part_get, const char *, NULL) EFL_FUNC_BODY_CONST(efl_ui_widget_default_text_part_get, const char *, NULL) ELM_PART_CONTENT_DEFAULT_GET(efl_ui_widget, NULL) ELM_PART_TEXT_DEFAULT_GET(efl_ui_widget, NULL) #define EFL_UI_WIDGET_EXTRA_OPS \ EFL_CANVAS_GROUP_ADD_DEL_OPS(efl_ui_widget), \ ELM_PART_CONTENT_DEFAULT_OPS(efl_ui_widget), \ ELM_PART_TEXT_DEFAULT_OPS(efl_ui_widget), \ EFL_OBJECT_OP_FUNC(efl_canvas_object_legacy_ctor, _efl_ui_widget_legacy_ctor), \ EFL_OBJECT_OP_FUNC(efl_dbg_info_get, _efl_ui_widget_efl_object_dbg_info_get) #include "elm_widget_item.eo.c" #include "efl_ui_widget.eo.c" /* Others */ #include "efl_ui_translatable.eo.c" diff --git a/src/lib/elementary/efl_ui_widget.eo b/src/lib/elementary/efl_ui_widget.eo index 0566407c9e..340ed501b7 100644 --- a/src/lib/elementary/efl_ui_widget.eo +++ b/src/lib/elementary/efl_ui_widget.eo @@ -1,615 +1,611 @@ function Efl.Ui.Scrollable_On_Show_Region { [[Function pointer for on show region hook]] params { @in obj: Efl.Canvas.Object; [[Canvas object]] @in region: Eina.Rect; [[Showed region]] } }; struct Efl.Ui.Widget.Focus_State { [[All relevant fields needed for the current state of focus registeration]] manager : Efl.Ui.Focus.Manager; [[The manager where the widget is registered in]] parent : Efl.Ui.Focus.Object; [[The parent the widget is using as logical parent]] logical : bool; [[$true if this is registered as logical currently]] } abstract Efl.Ui.Widget (Efl.Canvas.Group, Efl.Access, Efl.Access.Component, Efl.Part, Efl.Ui.Focus.Object, Efl.Ui.Base, Efl.Ui.Cursor, Efl.Ui.Translatable, Efl.Selection, Efl.Ui.Dnd) { [[Efl UI widget abstract class]] legacy_prefix: elm_widget; //eo_prefix: efl_ui_widget; event_prefix: efl_ui_widget; data: Efl_Ui_Widget_Data; methods { @property resize_object @protected { [[This is the internal canvas object managed by a widget. This property is protected as it is meant for widget implementations only, to set and access the internal canvas object. Do use this function unless you're implementing a widget. ]] set { [[Sets the new resize object for this widget.]] } values { sobj: Efl.Canvas.Object @nullable; [[A canvas object (often a @Efl.Canvas.Layout object).]] } } @property disabled { [[Whether the widget is enabled (accepts and reacts to user inputs). Each widget may handle the disabled state differently, but overall disabled widgets shall not respond to any input events. This is $false by default, meaning the widget is enabled. ]] set { [[Enables or disables this widget. Disabling a widget will disable all its children recursively, but only this widget will be marked as disabled internally. ]] } get { [[Returns whether the widget is disabled. This will return $true if any widget in the parent hierarchy is disabled. Re-enabling that parent may in turn change the disabled state of this widget. ]] } values { disabled: bool(false); [[$true if the widget is disabled.]] } } @property style { [[The widget style to use. Styles define different look and feel for widgets, and may provide different parts for layout-based widgets. Styles vary from widget to widget and may be defined by other themes by means of extensions and overlays. The style can only be set before @Efl.Object.finalize, which means at construction time of the object (inside $efl_add in C). ]] set @protected { [[Can only be called during construction, before finalize.]] return: Efl.Ui.Theme.Apply(0); [[Whether the style was successfully applied or not, see the values of @Efl.Ui.Theme.Apply for more information.]] } get { [[Returns the current style of a widget.]] } values { style: string; [[Name of the style to use. Refer to each widget's documentation for the available style names, or to the themes in use.]] } } widget_event @protected { [[Virtual function handling input events on the widget. This method should return $true if the event has been processed. Only key down, key up and pointer wheel events will be propagated through this function. It is common for the event to be also marked as processed as in @Efl.Input.Event.processed, if this operation was successful. This makes sure other widgets will not also process this input event. ]] params { @cref eo_event: Efl.Event; [[EO event struct with an Efl.Input.Event as info.]] @in source: Efl.Canvas.Object; [[Source object where the event originated. Often same as this.]] } return: bool; [[$true on success, $false otherwise]] legacy: null; } // FIXME: focus_allow? can_focus? focusable? @property focus_allow { [[The ability for a widget to be focused. Unfocusable objects do nothing when programmatically focused. The nearest focusable parent object the one really getting focus. Also, when they receive mouse input, they will get the event, but not take away the focus from where it was previously. Note: Objects which are meant to be interacted with by input events are created able to be focused, by default. All the others are not. This property's default value depends on the widget (eg. a box is not focusable, but a button is). ]] set { legacy: elm_widget_can_focus_set; } get { legacy: elm_widget_can_focus_get; } values { can_focus: bool; [[Whether the object is focusable.]] } } @property widget_parent @protected { [[The internal parent of this widget. @Efl.Ui.Widget objects have a parent hierarchy that may differ slightly from their @Efl.Object or @Efl.Canvas.Object hierarchy. This is meant for internal handling. See also @.widget_top. ]] set {} get { legacy: elm_widget_parent_get; /* internal in legacy */ } values { parent: Efl.Ui.Widget @nullable; [[Widget parent object]] } } @property widget_top { [[Root widget in the widget hierarchy. This returns the top widget, in terms of widget hierarchy. This is usually a window (@Efl.Ui.Win). This function walks the list of @.widget_parent. If this widget has no parent (in terms of widget hierarchy) this will return $null. Note: This may not be a display manager window in case of nested canvases. If a "real" window is required, then you might want to verify that the returned object is a $Efl.Ui.Win_Inlined, and then get $Efl.Ui.Win_Inlined.inlined_parent to find an object in the master window. See also @.widget_parent. ]] get { legacy: elm_widget_top_get; /* internal in legacy */ } values { top: Efl.Ui.Widget; [[Top widget, usually a window.]] } } /* Accessibility */ @property access_info { [[Accessibility information. This is a replacement string to be read by the accessibility text-to-speech engine, if accessibility is enabled by configuration. This will take precedence over the default text for this object, which means for instance that the label of a button won't be read out loud, instead $txt will be read out. ]] values { txt: string @nullable; [[Accessibility text description.]] } } on_access_activate @protected @beta { [[Hook function called when widget is activated through accessibility. This meant to be overridden by subclasses to support accessibility. This is an unstable API. ]] params { @in act: Efl.Ui.Activate; [[Type of activation.]] } return: bool; [[$true on success, $false otherwise]] legacy: null; /* FIXME: legacy API does extra work */ } on_access_update @protected @beta { [[Hook function called when accessibility is changed on the widget. This meant to be overridden by subclasses to support accessibility. This is an unstable API. ]] params { @in enable: bool; [[$true if accessibility is enabled.]] } legacy: null; /* FIXME: legacy API does extra work */ } /* Internal hooks. */ widget_sub_object_add @protected { [[Virtual function handling sub objects being added. Sub objects can be any canvas object, not necessarily widgets. See also @.widget_parent. ]] params { @in sub_obj: Efl.Canvas.Object; [[Sub object to be added. Not necessarily a widget itself.]] } return: bool; [[Indicates if the operation succeeded.]] legacy: elm_widget_sub_object_add; } widget_sub_object_del @protected { [[Virtual function handling sub objects being removed. Sub objects can be any canvas object, not necessarily widgets. See also @.widget_parent. ]] params { @in sub_obj: Efl.Canvas.Object; [[Sub object to be removed. Should be a child of this widget.]] } return: bool; [[Indicates if the operation succeeded.]] legacy: elm_widget_sub_object_del; } @property orientation_mode { [[Whether the widget's automatic orientation is enabled or not. Orientation mode is used for widgets to change their style or send signals based on the canvas rotation (i.e. the window orientation). If the orientation mode is enabled, the widget will emit signals such as "elm,state,orient,N" where $N is one of 0, 90, 180, 270, depending on the window orientation. Such signals may be handled by the theme in order to provide a different look for the widget based on the canvas orientation. By default orientation mode is enabled. See also @.on_orientation_update. ]] values { mode: Efl.Ui.Widget.Orientation_Mode(Efl.Ui.Widget.Orientation_Mode.default); [[How window orientation should affect this widget.]] } } on_orientation_update @protected { [[Virtual function handling canvas orientation changes. This method will be called recursively from the top widget (the window) to all the children objects whenever the window rotation is changed. The given $rotation will be one of 0, 90, 180, 270 or the special value -1 if @.orientation_mode is $disabled. If @.orientation_mode is $default, the widget implementation will emit the signal "elm,state,orient,$R" will be emitted (where $R is the rotation angle in degrees). Note: This function may be called even if the orientation has not actually changed, like when a widget needs to be reconfigured. See also @.orientation_mode. ]] params { rotation: int; [[Orientation in degrees: 0, 90, 180, 270 or -1 if @.orientation_mode is $disabled.]] } } on_disabled_update @protected { [[Virtual function called when the widget becomes disabled. This may be triggered even if this widget is not disabled, as the parent widget could be disabled and propagate its state. ]] params { disabled: bool; [[The new value of @.disabled.]] } return: bool; [[Indicates if the operation succeeded.]] } theme_apply @protected { [[Virtual function called when the widget needs to re-apply its theme. This may be called when the object is first created, or whenever the widget is modified in any way that may require a reload of the theme. This may include but is not limited to scale, theme, or mirrored mode changes. Note: even widgets not based on layouts may override this method to handle widget updates (scale, mirrored mode, etc...). ]] return: Efl.Ui.Theme.Apply; [[Indicates success, and if the current theme or default theme was used.]] } - on_focus_update @protected { - [[Virtual function handling focus in/out events on the widget]] - return: bool; [[$true if this widget can handle focus, $false otherwise]] - } /* Scroll API. */ @property on_show_region_hook @protected { [[Hook function called when the @.show_region is changed. See also @.show_region. ]] set {} values { func: Efl.Ui.Scrollable_On_Show_Region @nullable; [[Region hook function]] } } @property show_region @protected { [[Region inside the widget to show. See also @.on_show_region_hook. ]] set { [[Request parent scrollers to pan around so that this region of the widget becomes visible. If $force is $true this will trigger scroller changes and the @.on_show_region_hook to be called even if the region is unchanged. ]] values { region: Eina.Rect; [[The region to show.]] force: bool; [[Set to $true to force show even if unchanged.]] } } get { [[Returns the current region to show.]] values { region: Eina.Rect; [[The region to show.]] } } } /* FIXME: Scroll API. Not sure how those APIs should be exposed with * the new scrollable API. */ scroll_hold_push { [[Push scroll hold]] } scroll_hold_pop { [[Pop scroller hold]] } scroll_freeze_push { [[Push scroller freeze]] } scroll_freeze_pop { [[Pop scroller freeze]] } /* Region of interest */ @property interest_region @protected { [[Region of interest inside this widget, that should be given priority to be visible inside a scroller. When this widget or one of its subwidgets is given focus, this region should be shown, which means any parent scroller should attempt to display the given area of this widget. For instance, an entry given focus should scroll to show the text cursor if that cursor moves. In this example, this region defines the relative geometry of the cursor within the widget. Note: The region is relative to the top-left corner of the widget, i.e. X,Y start from 0,0 to indicate the top-left corner of the widget. W,H must be greater or equal to 1 for this region to be taken into account, otherwise it is ignored. See also @.interest_region_mode ]] get { legacy: elm_widget_focus_region_get; } values { region: Eina.Rect; [[The relative region to show. If width or height is <= 0 it will be ignored, and no action will be taken.]] } } @property interest_region_mode { [[Defines how @.show_region should be calculated. It is up to the implementation of @.show_region.get to take this value into account (or ignore it). ]] get { legacy: elm_widget_focus_region_show_mode_get; } set { legacy: elm_widget_focus_region_show_mode_set; } values { mode: Efl.Ui.Interest_Region_Mode; [[Whether to show a specific item or the widget as a whole.]] } } /* Focus highlight (visual focus rectangle) properties */ @property focus_highlight_geometry @protected { [[The rectangle region to be highlighted on focus. This is a rectangle region where the focus highlight should be displayed. ]] get { [[This is a read-only property.]] } values { region: Eina.Rect; [[The rectangle area.]] } } @property focus_highlight_enabled { [[Whether focus highlight is enabled or not. As of EFL 1.21 focus highlight properties apply to a single window, not a single widget. As a consequence, calls to this function may be forwarded to the parent window. Future versions of EFL may implement widget-specific focus highlight properties. See also @.widget_top. See also @.focus_highlight_style. See also @.focus_highlight_animate. ]] set { [[Set the enabled status for the focus highlight in a window. This function will enable or disable the focus highlight, regardless of the global setting for it. ]] } get { [[Get the enabled value of the focus highlight for this window.]] } values { enabled: bool; [[The enabled value for the highlight.]] } } @property focus_highlight_style { [[Control the widget focus highlight style. If $style is $null, the default will be used. As of EFL 1.21 focus highlight properties apply to a single window, not a single widget. As a consequence, calls to this function may be forwarded to the parent window. Future versions of EFL may implement widget-specific focus highlight properties. See also @.widget_top. See also @.focus_highlight_enabled. See also @.focus_highlight_animate. ]] set { /* FIXME: This is async... success here means nothing. */ return: bool; [[$true on success, $false otherwise.]] } get { } values { style: string @nullable; [[The name of the focus highlight style.]] } } @property focus_highlight_animate { [[Whether focus highlight should animate or not. As of EFL 1.21 focus highlight properties apply to a single window, not a single widget. As a consequence, calls to this function may be forwarded to the parent window. Future versions of EFL may implement widget-specific focus highlight properties. See also @.widget_top. See also @.focus_highlight_style. See also @.focus_highlight_enabled. ]] set { [[Set the animate status for the focus highlight for this window. This function will enable or disable the animation of focus highlight. ]] } get { [[Get the animate value of the focus highlight for this window.]] } values { animate: bool; [[The enabled value for the highlight animation.]] } } @property focus_move_policy @beta { [[The widget's focus move policy.]] values { policy: Efl.Ui.Focus.Move_Policy; [[Focus move policy]] } } @property focus_move_policy_automatic @beta { [[Control the widget's focus_move_policy mode setting. @since 1.18]] values { automatic: bool; [[$true to follow system focus move policy change, $false otherwise]] } } /* FIXME: No more items in EO API */ @property focused_item @beta { get { [[Get the focused widget item.]] return: Elm.Widget.Item; [[Focused item]] } } /* Focus Manager API */ focus_state_apply @protected { [[Register focus with the given configuration. The implementation can feel free to change the logical flag as it wants, but other than that it should strictly keep the configuration. The implementation in elm.widget updates the current state into what is passed as configured state, respecting manager changes, registeration and unregistration based on if it should be registered or unregistered. A manager field that is $null means that the widget should not or was not registered. ]] params { @in current_state : Efl.Ui.Widget.Focus_State; [[The focus manager to register with.]] @inout configured_state : Efl.Ui.Widget.Focus_State; [[The evalulated Focus state that should be used.]] @in redirect : Efl.Ui.Widget; [[A redirect that will be set by the elm.widget implementation.]] } return: bool; [[Returns whether the widget is registered or not.]] } focus_manager_create @protected { [[If the widget needs a focus manager, this function will be called. It can be used and overriden to inject your own manager or set custom options on the focus manager. ]] params { @in root: Efl.Ui.Focus.Object; [[The logical root object for focus.]] } return: Efl.Ui.Focus.Manager; [[The focus manager.]] } } parts { shadow: Efl.Ui.Widget.Part_Shadow; background: Efl.Ui.Widget.Part_Bg; } implements { class.constructor; Efl.Object.constructor; Efl.Object.finalize; Efl.Object.destructor; Efl.Object.provider_find; Efl.Object.debug_name_override; Efl.Gfx.Color.color { set; } Efl.Gfx.visible { set; } Efl.Gfx.position { set; } Efl.Gfx.size { set; } Efl.Gfx.scale { set; get; } Efl.Canvas.Object.clip { set; } Efl.Canvas.Object.no_render { set; } Efl.Canvas.Object.is_frame_object { set; } Efl.Canvas.Group.group_calculate; Efl.Canvas.Group.group_member_del; Efl.Canvas.Group.group_member_add; Efl.Access.name { get; } Efl.Access.state_set { get; } Efl.Access.children { get; } Efl.Access.parent { get; } Efl.Access.attributes { get; } Efl.Access.Component.focus_grab; Efl.Ui.Focus.Object.focus_manager { get; } Efl.Ui.Focus.Object.focus_parent { get; } Efl.Ui.Focus.Object.focus_geometry { get; } Efl.Ui.Focus.Object.focus { set; } Efl.Ui.Base.mirrored { get; set; } Efl.Ui.Base.mirrored_automatic { get; set; } Efl.Ui.Cursor.cursor { get; set; } Efl.Ui.Cursor.cursor_style { get; set; } Efl.Ui.Cursor.cursor_theme_search_enabled { get; set; } Efl.Ui.Translatable.translation_update; [[This implements the calls to $gettext() and $text_set().]] Efl.Part.part; [[Returns @Efl.Ui.Widget.Part.]] } events { moved; [[Called when widget moved]] language,changed; [[Called when widget language changed]] access,changed; [[Called when accessibility changed]] } } diff --git a/src/lib/elementary/efl_ui_win.c b/src/lib/elementary/efl_ui_win.c index a838dba11a..fd2eea0427 100644 --- a/src/lib/elementary/efl_ui_win.c +++ b/src/lib/elementary/efl_ui_win.c @@ -1,8686 +1,8686 @@ #ifdef HAVE_CONFIG_H # include "elementary_config.h" #endif #define EFL_ACCESS_PROTECTED #define EFL_ACCESS_COMPONENT_PROTECTED #define EFL_ACCESS_WIDGET_ACTION_PROTECTED #define EFL_INPUT_EVENT_PROTECTED #define EFL_GFX_SIZE_HINT_PROTECTED #define EFL_CANVAS_OBJECT_BETA #define EFL_CANVAS_OBJECT_PROTECTED #define EFL_UI_TRANSLATABLE_PROTECTED #define EFL_UI_WIN_INLINED_PROTECTED #define EFL_UI_FOCUS_OBJECT_PROTECTED #define EFL_UI_WIN_BETA #define EFL_CANVAS_BETA #include #include #include "elm_priv.h" #include "elm_widget_menu.h" #ifdef HAVE_ELEMENTARY_WL2 # include "ecore_evas_wayland_private.h" #endif #include "../evas/canvas/evas_box.eo.h" #define EFL_INTERNAL_UNSTABLE #include "interfaces/efl_common_internal.h" #include "elm_part_helper.h" #include "efl_ui_win_part.eo.h" #include "elm_plug.eo.h" #define MY_CLASS EFL_UI_WIN_CLASS #define MY_CLASS_NAME "Efl.Ui.Win" #define MY_CLASS_NAME_LEGACY "elm_win" #define FRAME_OBJ_THEME_MIN_VERSION 119 static const Elm_Win_Trap *trap = NULL; static int _paused_windows = 0; #define TRAP(sd, name, ...) \ do \ { \ if (sd->type != ELM_WIN_FAKE) \ if ((!trap) || (!trap->name) || \ ((trap->name) && \ (trap->name(sd->trap_data, sd->obj, ## __VA_ARGS__)))) \ ecore_evas_##name(sd->ee, ##__VA_ARGS__); \ } \ while (0) #define ELM_WIN_DATA_GET(o, sd) \ Efl_Ui_Win_Data * sd = efl_data_scope_get(o, MY_CLASS) #define ELM_WIN_DATA_GET_OR_RETURN(o, ptr, ...) \ ELM_WIN_DATA_GET(o, ptr); \ if (!ptr) \ { \ CRI("No widget data for object %p (%s)", \ o, efl_class_name_get(o)); \ return __VA_ARGS__; \ } // Ecore_Evas callbacks are unsafe unlike EO calls. As a consequence a user // callback (eg evas cb, efl event cb, ...) could be triggered that deletes the // object. This macro ensures the sd data is still valid after a foreign call. #define ELM_WIN_DATA_ALIVE_CHECK(_obj, _sd, ...) do { \ _sd = efl_data_scope_safe_get(_obj, MY_CLASS); \ if (EINA_UNLIKELY(!(_sd))) { return __VA_ARGS__; } \ } while (0) #define ENGINE_GET() (_elm_preferred_engine ? _elm_preferred_engine : _elm_config->engine) typedef struct _Efl_Ui_Win_Data Efl_Ui_Win_Data; typedef struct _Input_Pointer_Iterator Input_Pointer_Iterator; struct _Efl_Ui_Win_Data { Ecore_Evas *ee; Evas *evas; Evas_Object *parent; /* parent *window* object*/ Evas_Object *img_obj, *frame_obj; Eo /* wref */ *bg, *content; Evas_Object *obj; /* The object itself */ Evas_Object *indicator; #ifdef HAVE_ELEMENTARY_X struct { Ecore_X_Window xwin; Ecore_Event_Handler *client_message_handler; Ecore_Event_Handler *property_handler; } x; #endif #ifdef HAVE_ELEMENTARY_WL2 struct { Ecore_Wl2_Window *win; Ecore_Event_Handler *configure_handler; Eina_Bool opaque_dirty : 1; } wl; #endif #ifdef HAVE_ELEMENTARY_COCOA struct { Ecore_Cocoa_Window *win; } cocoa; #endif #ifdef HAVE_ELEMENTARY_WIN32 struct { Ecore_Win32_Window *win; Ecore_Event_Handler *key_down_handler; } win32; #endif unsigned /* Efl_Ui_Win_Type */ type; Efl_Ui_Win_Keyboard_Mode kbdmode; Efl_Ui_Win_Indicator_Mode indimode; struct { const char *info; Ecore_Timer *timer; int repeat_count; int shot_counter; } shot; int *autodel_clear, rot; struct { int x, y; } screen; struct { #ifdef HAVE_ELEMENTARY_WL2 Ecore_Wl2_Window *win; struct wl_surface *surf; #endif Ecore_Evas *ee; Evas_Object *obj, *hot_obj; int hot_x, hot_y; Eina_Bool visible : 1; } pointer; struct { Evas_Object *fobj; /* focus highlight edje object */ struct { Evas_Object *target; Eina_Bool visible : 1; Eina_Bool in_theme: 1; /**< focus highlight is handled by theme. this is set true if edc data item "focus_highlight" is set to "on" during focus in callback. */ } cur, prev; const char *style; Ecore_Job *reconf_job; Eina_Bool enabled : 1; Eina_Bool theme_changed : 1; /* set true when the focus theme is changed */ Eina_Bool animate : 1; /* set true when the focus highlight animate is enabled */ Eina_Bool animate_supported : 1; /* set true when the focus highlight animate is supported by theme */ Eina_Bool geometry_changed : 1; Eina_Bool auto_enabled : 1; Eina_Bool auto_animate : 1; } focus_highlight; Evas_Object *icon; const char *title; const char *icon_name; const char *role; const char *stack_id; const char *stack_master_id; Eina_Stringshare *name; Eina_Stringshare *accel_pref; Evas_Object *main_menu; Efl_Ui_Focus_Manager *manager; Efl_Ui_Focus_Parent_Provider_Standard *provider; struct { Eina_Stringshare *name; /* Current profile in use */ Eina_Array *available; /* Never NULL, contains Eina_Stringshare */ } profile; struct { int preferred_rot; /* indicates preferred rotation value, -1 means invalid. */ int *rots; /* indicates available rotations */ unsigned int count; /* number of elements in available rotations */ Eina_Bool wm_supported : 1; /* set true when the window manager support window rotation */ Eina_Bool use : 1; /* set ture when application use window manager rotation. */ } wm_rot; void *trap_data; double aspect; /* defined as w/h or 0 */ int size_base_w, size_base_h; int size_step_w, size_step_h; int req_x, req_y, req_w, req_h; int max_w, max_h; int norender; int modal_count; int response; Eina_Bool req_wh : 1; Eina_Bool req_xy : 1; struct { short pointer_move; short pointer_down; short pointer_up; short pointer_in; short pointer_out; short pointer_cancel; short pointer_wheel; short finger_move; short finger_down; short finger_up; short key_down; short key_up; short render_pre; short render_post; short focus_in; short focus_out; short object_focus_in; short object_focus_out; short device_changed; } event_forward; struct { /* frame_obj is always used except for FAKE */ Eina_Bool need : 1; /**< if true, application draws its own csd */ Eina_Bool need_shadow : 1; /**< if true, application draws its csd and shadow */ Eina_Bool need_borderless : 1; Eina_Bool need_bg_solid : 1; Eina_Bool need_bg_standard : 1; Eina_Bool need_menu : 1; Eina_Bool need_unresizable : 1; Eina_Bool need_indicator : 1; Eina_Bool cur_borderless : 1; Eina_Bool cur_shadow : 1; Eina_Bool cur_focus : 1; Eina_Bool cur_maximized : 1; Eina_Bool cur_bg_solid : 1; Eina_Bool cur_bg_standard : 1; Eina_Bool cur_menu : 1; Eina_Bool cur_unresizable : 1; Eina_Bool cur_indicator : 1; Eina_Bool wayland : 1; } csd; struct { Evas_Object *box, *edje; Elm_Win_Indicator_Mode indmode; Elm_Win_Indicator_Opacity_Mode ind_o_mode; Eina_Bool forbidden : 1; /**< Marks some legacy APIs as not allowed. */ Eina_Bool bg_must_swallow : 1; /**< Legacy theme compatibility (elm_bg for standard window) */ Eina_Bool bg_must_swallow_init : 1; Eina_Bool ctor : 1; /**< legacy constructor: elm_win~add */ } legacy; Eina_Bool first_draw : 1; Eina_Bool deferred_resize_job : 1; Eina_Bool urgent : 1; Eina_Bool modal : 1; Eina_Bool demand_attention : 1; Eina_Bool autodel : 1; Eina_Bool autohide : 1; Eina_Bool constrain : 1; Eina_Bool resizing : 1; Eina_Bool iconified : 1; Eina_Bool withdrawn : 1; Eina_Bool sticky : 1; Eina_Bool fullscreen : 1; Eina_Bool maximized : 1; Eina_Bool skip_focus : 1; Eina_Bool floating : 1; Eina_Bool noblank : 1; Eina_Bool theme_alpha : 1; /**< alpha value fetched by a theme. this has higher priority than application_alpha */ Eina_Bool application_alpha : 1; /**< alpha value set by an elm_win_alpha_set() api. this has lower priority than theme_alpha */ Eina_Bool tmp_updating_hints : 1; Eina_Bool single_edje_content: 1; /* hack for E */ Eina_Bool shown : 1; Eina_Bool stack_base : 1; Eina_Bool paused : 1; }; struct _Input_Pointer_Iterator { Eina_Iterator iterator; Eina_List *list; Eina_Iterator *real_iterator; const Eo *object; }; static const char SIG_DELETE_REQUEST[] = "delete,request"; static const char SIG_FOCUS_OUT[] = "focus,out"; // deprecated. use "unfocused" instead. static const char SIG_FOCUS_IN[] = "focus,in"; // deprecated. use "focused" instead. static const char SIG_MOVED[] = "moved"; static const char SIG_WITHDRAWN[] = "withdrawn"; static const char SIG_ICONIFIED[] = "iconified"; static const char SIG_NORMAL[] = "normal"; static const char SIG_STICK[] = "stick"; static const char SIG_UNSTICK[] = "unstick"; static const char SIG_FULLSCREEN[] = "fullscreen"; static const char SIG_UNFULLSCREEN[] = "unfullscreen"; static const char SIG_MAXIMIZED[] = "maximized"; static const char SIG_UNMAXIMIZED[] = "unmaximized"; static const char SIG_IOERR[] = "ioerr"; static const char SIG_INDICATOR_PROP_CHANGED[] = "indicator,prop,changed"; static const char SIG_ROTATION_CHANGED[] = "rotation,changed"; static const char SIG_PROFILE_CHANGED[] = "profile,changed"; static const char SIG_WM_ROTATION_CHANGED[] = "wm,rotation,changed"; static const Evas_Smart_Cb_Description _smart_callbacks[] = { {SIG_DELETE_REQUEST, ""}, {SIG_FOCUS_OUT, ""}, {SIG_FOCUS_IN, ""}, {SIG_MOVED, ""}, {SIG_WITHDRAWN, ""}, {SIG_ICONIFIED, ""}, {SIG_NORMAL, ""}, {SIG_STICK, ""}, {SIG_UNSTICK, ""}, {SIG_FULLSCREEN, ""}, {SIG_UNFULLSCREEN, ""}, {SIG_MAXIMIZED, ""}, {SIG_UNMAXIMIZED, ""}, {SIG_IOERR, ""}, {SIG_INDICATOR_PROP_CHANGED, ""}, {SIG_ROTATION_CHANGED, ""}, {SIG_PROFILE_CHANGED, ""}, {SIG_WM_ROTATION_CHANGED, ""}, {SIG_WIDGET_FOCUSED, ""}, /**< handled by elm_widget */ {SIG_WIDGET_UNFOCUSED, ""}, /**< handled by elm_widget */ {NULL, NULL} }; static Eina_Bool _key_action_return(Evas_Object *obj, const char *params); static Eina_Bool _key_action_move(Evas_Object *obj, const char *params); static const Elm_Action key_actions[] = { {"return", _key_action_return}, {"move", _key_action_move}, {NULL, NULL} }; Eina_List *_elm_win_list = NULL; int _elm_win_deferred_free = 0; static Eina_Bool _elm_win_throttle_ok = EINA_FALSE; static int _elm_win_count = 0; static Eina_Bool _elm_win_auto_throttled = EINA_FALSE; static Ecore_Timer *_elm_win_state_eval_timer = NULL; static void _elm_win_legacy_init(Efl_Ui_Win_Data *sd); static void _elm_win_on_resize_obj_changed_size_hints(void *data, Evas *e, Evas_Object *obj, void *event_info); static void _elm_win_img_callbacks_del(Evas_Object *obj, Evas_Object *imgobj); static Efl_Ui_Theme_Apply _elm_win_theme_internal(Eo *obj, Efl_Ui_Win_Data *sd); static void _elm_win_frame_add(Efl_Ui_Win_Data *sd, const char *element, const char *style); static void _elm_win_frame_style_update(Efl_Ui_Win_Data *sd, Eina_Bool force_emit, Eina_Bool calc); static inline void _elm_win_need_frame_adjust(Efl_Ui_Win_Data *sd, const char *engine); static void _elm_win_resize_objects_eval(Evas_Object *obj, Eina_Bool force_resize); static void _elm_win_opaque_update(Efl_Ui_Win_Data *sd, Eina_Bool force_alpha); static void _elm_win_frame_obj_update(Efl_Ui_Win_Data *sd); #ifdef HAVE_ELEMENTARY_X static void _elm_win_xwin_update(Efl_Ui_Win_Data *sd); #endif EAPI double _elm_startup_time = 0; static void _elm_win_first_frame_do(void *data, Evas *e EINA_UNUSED, void *event_info EINA_UNUSED) { double end = ecore_time_unix_get(); char *first = data; switch (*first) { case 'A': abort(); case 'E': case 'D': exit(-1); case 'T': fprintf(stderr, "Startup time: '%f' - '%f' = '%f' sec\n", end, _elm_startup_time, end - _elm_startup_time); break; } evas_event_callback_del_full(e, EVAS_CALLBACK_RENDER_POST, _elm_win_first_frame_do, data); } static void _win_noblank_eval(void) { #ifdef HAVE_ELEMENTARY_X Eina_List *l; Evas_Object *obj; int noblanks = 0; Eina_Bool change = EINA_FALSE; EINA_LIST_FOREACH(_elm_win_list, l, obj) { ELM_WIN_DATA_GET(obj, sd); if (sd->x.xwin) { if ((sd->noblank) && (!sd->iconified) && (!sd->withdrawn) && evas_object_visible_get(obj)) noblanks++; change = EINA_TRUE; } } if (!change) return; if (noblanks > 0) ecore_x_screensaver_suspend(); else ecore_x_screensaver_resume(); #endif #ifdef HAVE_ELEMENTARY_WL2 // XXX: no wl implementation of this yet - maybe higher up at prop level #endif } static Elm_Process_State _elm_process_state = ELM_PROCESS_STATE_FOREGROUND; EAPI Elm_Process_State elm_process_state_get(void) { return _elm_process_state; } static void _elm_win_apply_alpha(Eo *obj, Efl_Ui_Win_Data *sd) { Eina_Bool enabled; if (!sd->ee) return; enabled = sd->theme_alpha | sd->application_alpha; _elm_win_opaque_update(sd, EINA_TRUE); if (sd->img_obj) { evas_object_image_alpha_set(sd->img_obj, enabled); ecore_evas_alpha_set(sd->ee, enabled); } else { #ifdef HAVE_ELEMENTARY_X if (sd->x.xwin) { enabled |= (sd->csd.need && !sd->fullscreen); if (enabled) { if (!ecore_x_screen_is_composited(0)) elm_win_shaped_set(obj, enabled); else TRAP(sd, alpha_set, enabled); } else TRAP(sd, alpha_set, enabled); _elm_win_xwin_update(sd); } else #else (void)obj; #endif TRAP(sd, alpha_set, enabled); } } static Eina_Bool _elm_win_state_eval(void *data EINA_UNUSED) { Eina_List *l; Evas_Object *obj; int _elm_win_count_shown = 0; int _elm_win_count_iconified = 0; int _elm_win_count_withdrawn = 0; Eina_Bool throttle = EINA_FALSE; _elm_win_state_eval_timer = NULL; EINA_LIST_FOREACH(_elm_win_list, l, obj) { if (_elm_config->auto_norender_withdrawn) { if ((elm_win_withdrawn_get(obj)) || ((elm_win_iconified_get(obj) && (_elm_config->auto_norender_iconified_same_as_withdrawn)))) { if (!evas_object_data_get(obj, "__win_auto_norender")) { Evas *evas = evas_object_evas_get(obj); elm_win_norender_push(obj); evas_object_data_set(obj, "__win_auto_norender", obj); if (_elm_config->auto_flush_withdrawn) { edje_file_cache_flush(); edje_collection_cache_flush(); evas_image_cache_flush(evas); evas_font_cache_flush(evas); } if (_elm_config->auto_dump_withdrawn) { evas_render_dump(evas); } } if (elm_win_iconified_get(obj)) efl_event_callback_call(obj, EFL_UI_WIN_EVENT_PAUSE, NULL); continue; } } if (evas_object_data_get(obj, "__win_auto_norender")) { elm_win_norender_pop(obj); evas_object_data_del(obj, "__win_auto_norender"); } } if (((_elm_config->auto_throttle) && (elm_policy_get(ELM_POLICY_THROTTLE) != ELM_POLICY_THROTTLE_NEVER)) || (elm_policy_get(ELM_POLICY_THROTTLE) == ELM_POLICY_THROTTLE_HIDDEN_ALWAYS)) throttle = EINA_TRUE; if (_elm_win_count == 0) { if ((_elm_win_throttle_ok) && (_elm_win_auto_throttled)) { _elm_process_state = ELM_PROCESS_STATE_FOREGROUND; ecore_event_add(ELM_EVENT_PROCESS_FOREGROUND, NULL, NULL, NULL); if (throttle) ecore_throttle_adjust(-_elm_config->auto_throttle_amount); _elm_win_auto_throttled = EINA_FALSE; } } else { EINA_LIST_FOREACH(_elm_win_list, l, obj) { if (elm_win_withdrawn_get(obj)) _elm_win_count_withdrawn++; else if (elm_win_iconified_get(obj)) _elm_win_count_iconified++; else if (evas_object_visible_get(obj)) _elm_win_count_shown++; } if (_elm_win_count_shown <= 0) { if ((_elm_win_throttle_ok) && (!_elm_win_auto_throttled)) { _elm_process_state = ELM_PROCESS_STATE_BACKGROUND; ecore_event_add(ELM_EVENT_PROCESS_BACKGROUND, NULL, NULL, NULL); if (throttle) ecore_throttle_adjust(_elm_config->auto_throttle_amount); _elm_win_auto_throttled = EINA_TRUE; } } else { if ((_elm_win_throttle_ok) && (_elm_win_auto_throttled)) { _elm_process_state = ELM_PROCESS_STATE_FOREGROUND; ecore_event_add(ELM_EVENT_PROCESS_FOREGROUND, NULL, NULL, NULL); if (throttle) ecore_throttle_adjust(-_elm_config->auto_throttle_amount); _elm_win_auto_throttled = EINA_FALSE; } } } _win_noblank_eval(); return EINA_FALSE; } static Eina_Bool _elm_win_policy_quit_triggered(Eo* triggering_obj) { if ((!_elm_win_list) && (elm_policy_get(ELM_POLICY_QUIT) == ELM_POLICY_QUIT_LAST_WINDOW_CLOSED)) { return EINA_TRUE; } if (elm_policy_get(ELM_POLICY_QUIT) == ELM_POLICY_QUIT_LAST_WINDOW_HIDDEN) { Eina_List *l; Evas_Object *win; EINA_LIST_FOREACH(_elm_win_list, l, win) if (win != triggering_obj && evas_object_visible_get(win) == EINA_TRUE) { return EINA_FALSE; } return EINA_TRUE; } return EINA_FALSE; } static void _elm_win_flush_cache_and_exit(Eo *obj) { edje_file_cache_flush(); edje_collection_cache_flush(); evas_image_cache_flush(evas_object_evas_get(obj)); evas_font_cache_flush(evas_object_evas_get(obj)); elm_exit(); } static void _elm_win_state_eval_queue(void) { if (_elm_win_state_eval_timer) ecore_timer_del(_elm_win_state_eval_timer); _elm_win_state_eval_timer = ecore_timer_add(0.5, _elm_win_state_eval, NULL); } // example shot spec (wait 0.1 sec then save as my-window.png): // ELM_ENGINE="shot:delay=0.1:file=my-window.png" static double _shot_delay_get(Efl_Ui_Win_Data *sd) { char *p, *pd; char *d = strdup(sd->shot.info); if (!d) return 0.5; for (p = (char *)sd->shot.info; *p; p++) { if (!strncmp(p, "delay=", 6)) { double v; for (pd = d, p += 6; (*p) && (*p != ':'); p++, pd++) { *pd = *p; } *pd = 0; v = _elm_atof(d); free(d); return v; } } free(d); return 0.5; } static char * _shot_file_get(Efl_Ui_Win_Data *sd) { char *p; char *tmp = strdup(sd->shot.info); char *repname = NULL; if (!tmp) return NULL; for (p = (char *)sd->shot.info; *p; p++) { if (!strncmp(p, "file=", 5)) { strcpy(tmp, p + 5); if (!sd->shot.repeat_count) return tmp; else { char *dotptr = strrchr(tmp, '.'); if (dotptr) { size_t size = sizeof(char) * (strlen(tmp) + 16); repname = malloc(size); if (repname) { strncpy(repname, tmp, dotptr - tmp); snprintf(repname + (dotptr - tmp), size - (dotptr - tmp), "%03i", sd->shot.shot_counter + 1); strcat(repname, dotptr); } free(tmp); return repname; } } } } free(tmp); if (!sd->shot.repeat_count) return strdup("out.png"); repname = malloc(sizeof(char) * 24); if (!repname) return NULL; snprintf(repname, sizeof(char) * 24, "out%03i.png", sd->shot.shot_counter + 1); return repname; } static int _shot_repeat_count_get(Efl_Ui_Win_Data *sd) { char *p, *pd; char *d = strdup(sd->shot.info); if (!d) return 0; for (p = (char *)sd->shot.info; *p; p++) { if (!strncmp(p, "repeat=", 7)) { int v; for (pd = d, p += 7; (*p) && (*p != ':'); p++, pd++) { *pd = *p; } *pd = 0; v = atoi(d); if (v < 0) v = 0; if (v > 1000) v = 999; free(d); return v; } } free(d); return 0; } static char * _shot_key_get(Efl_Ui_Win_Data *sd EINA_UNUSED) { return NULL; } static char * _shot_flags_get(Efl_Ui_Win_Data *sd EINA_UNUSED) { return NULL; } static void _shot_do(Efl_Ui_Win_Data *sd) { Ecore_Evas *ee; Evas_Object *o; unsigned int *pixels; int w, h; char *file, *key, *flags; ecore_evas_manual_render(sd->ee); pixels = (void *)ecore_evas_buffer_pixels_get(sd->ee); if (!pixels) return; ecore_evas_geometry_get(sd->ee, NULL, NULL, &w, &h); if ((w < 1) || (h < 1)) return; file = _shot_file_get(sd); if (!file) return; key = _shot_key_get(sd); flags = _shot_flags_get(sd); ee = ecore_evas_buffer_new(1, 1); o = evas_object_image_add(ecore_evas_get(ee)); evas_object_image_alpha_set(o, sd->theme_alpha | sd->application_alpha); evas_object_image_size_set(o, w, h); evas_object_image_data_set(o, pixels); if (!evas_object_image_save(o, file, key, flags)) { ERR("Cannot save window to '%s' (key '%s', flags '%s')", file, key, flags); } free(file); free(key); free(flags); ecore_evas_free(ee); if (sd->shot.repeat_count) sd->shot.shot_counter++; } static Eina_Bool _shot_delay(void *data) { ELM_WIN_DATA_GET(data, sd); _shot_do(sd); if (sd->shot.repeat_count) { int remainshot = (sd->shot.repeat_count - sd->shot.shot_counter); if (remainshot > 0) return EINA_TRUE; } sd->shot.timer = NULL; elm_exit(); return EINA_FALSE; } static void _shot_init(Efl_Ui_Win_Data *sd) { if (!sd->shot.info) return; sd->shot.repeat_count = _shot_repeat_count_get(sd); sd->shot.shot_counter = 0; } static void _shot_handle(Efl_Ui_Win_Data *sd) { if (!sd->shot.info) return; if (!sd->shot.timer) sd->shot.timer = ecore_timer_add(_shot_delay_get(sd), _shot_delay, sd->obj); } /* elm-win specific associate, does the trap while ecore_evas_object_associate() * does not. */ static Efl_Ui_Win_Data * _elm_win_associate_get(const Ecore_Evas *ee) { Evas_Object *obj = ecore_evas_data_get(ee, "elm_win"); return efl_data_scope_safe_get(obj, MY_CLASS); } /* Interceptors Callbacks */ static void _elm_win_obj_intercept_raise(void *data, Evas_Object *obj EINA_UNUSED) { // Note: This is probably not necessary anymore (Win implements raise) ELM_WIN_DATA_GET(data, sd); TRAP(sd, raise); } static void _elm_win_obj_intercept_lower(void *data, Evas_Object *obj EINA_UNUSED) { // Note: This is probably not necessary anymore (Win ignores lower) ELM_WIN_DATA_GET(data, sd); TRAP(sd, lower); } static void _elm_win_obj_intercept_stack_above(void *data EINA_UNUSED, Evas_Object *obj EINA_UNUSED, Evas_Object *above EINA_UNUSED) { INF("TODO: %s", __FUNCTION__); } static void _elm_win_obj_intercept_stack_below(void *data EINA_UNUSED, Evas_Object *obj EINA_UNUSED, Evas_Object *below EINA_UNUSED) { INF("TODO: %s", __FUNCTION__); } static void _elm_win_obj_intercept_layer_set(void *data, Evas_Object *obj EINA_UNUSED, int l) { ELM_WIN_DATA_GET(data, sd); TRAP(sd, layer_set, l); } /* Event Callbacks */ static void _elm_win_size_hints_update(Efl_Ui_Win *win, Efl_Ui_Win_Data *sd) { Eina_Size2D min, max; min = efl_gfx_size_hint_combined_min_get(win); max = efl_gfx_size_hint_max_get(win); if (max.w < 1) max.w = -1; if (max.h < 1) max.h = -1; TRAP(sd, size_min_set, min.w, min.h); TRAP(sd, size_max_set, max.w, max.h); } static void _elm_win_obj_callback_changed_size_hints(void *data EINA_UNUSED, Evas *e EINA_UNUSED, Evas_Object *obj, void *event_info EINA_UNUSED) { ELM_WIN_DATA_GET(obj, sd); if (sd->tmp_updating_hints) return; _elm_win_size_hints_update(obj, sd); } /* end of elm-win specific associate */ static void _elm_win_move(Ecore_Evas *ee) { Efl_Ui_Win_Data *sd = _elm_win_associate_get(ee); int x, y; Eo *obj; if (!sd) return; obj = sd->obj; ecore_evas_geometry_get(ee, &x, &y, NULL, NULL); sd->screen.x = x; sd->screen.y = y; efl_event_callback_legacy_call(sd->obj, EFL_GFX_EVENT_MOVE, NULL); ELM_WIN_DATA_ALIVE_CHECK(obj, sd); evas_nochange_push(evas_object_evas_get(sd->obj)); sd->response++; sd->req_xy = EINA_FALSE; evas_object_move(sd->obj, x, y); ELM_WIN_DATA_ALIVE_CHECK(obj, sd); sd->response--; evas_nochange_pop(evas_object_evas_get(sd->obj)); } static void _elm_win_resize_job(void *data) { ELM_WIN_DATA_GET(data, sd); int w, h; sd->deferred_resize_job = EINA_FALSE; ecore_evas_geometry_get(sd->ee, NULL, NULL, &w, &h); if (sd->constrain) { int sw, sh; ecore_evas_screen_geometry_get(sd->ee, NULL, NULL, &sw, &sh); w = MIN(w, sw); h = MIN(h, sh); } if (sd->frame_obj) { int fx, fy, fw, fh; evas_output_framespace_get(sd->evas, &fx, &fy, &fw, &fh); evas_object_move(sd->frame_obj, -fx, -fy); evas_object_resize(sd->frame_obj, w + fw, h + fh); } else _elm_win_opaque_update(sd, 0); if (sd->main_menu) { Eina_Position2D pos; pos = efl_gfx_position_get(sd->main_menu); elm_menu_move(sd->main_menu, pos.x, pos.y); } sd->response++; sd->req_wh = EINA_FALSE; evas_object_resize(sd->obj, w, h); evas_object_resize(sd->legacy.edje, w, h); sd->response--; } static void _elm_win_pre_render(Ecore_Evas *ee) { Efl_Ui_Win_Data *sd = _elm_win_associate_get(ee); Eo *obj; if (!sd) return; obj = sd->obj; _elm_win_throttle_ok = EINA_TRUE; if (!sd->first_draw) { sd->first_draw = EINA_TRUE; _elm_win_frame_obj_update(sd); ELM_WIN_DATA_ALIVE_CHECK(obj, sd); } if (sd->deferred_resize_job) _elm_win_resize_job(sd->obj); } static void _elm_win_resize(Ecore_Evas *ee) { Efl_Ui_Win_Data *sd = _elm_win_associate_get(ee); if (!sd) return; sd->deferred_resize_job = EINA_TRUE; } static void _elm_win_mouse_in(Ecore_Evas *ee) { Efl_Ui_Win_Data *sd = _elm_win_associate_get(ee); if (!sd) return; _elm_win_throttle_ok = EINA_TRUE; sd->resizing = EINA_FALSE; #ifdef HAVE_ELEMENTARY_WL2 if ((sd->wl.win) && (sd->pointer.ee)) { sd->pointer.visible = EINA_TRUE; sd->pointer.surf = ecore_wl2_window_surface_get(sd->pointer.win); _elm_win_wl_cursor_set(sd->obj, NULL); //ELM_WIN_DATA_ALIVE_CHECK(obj, sd); ecore_evas_show(sd->pointer.ee); } #endif } static void _elm_win_mouse_out(Ecore_Evas *ee) { Efl_Ui_Win_Data *sd = _elm_win_associate_get(ee); if (!sd) return; #ifdef HAVE_ELEMENTARY_WL2 if ((sd->wl.win) && (sd->pointer.ee)) sd->pointer.visible = EINA_FALSE; #endif } static void _elm_win_focus_highlight_reconfigure_job_stop(Efl_Ui_Win_Data *sd) { ELM_SAFE_FREE(sd->focus_highlight.reconf_job, ecore_job_del); } static void _elm_win_focus_highlight_visible_set(Efl_Ui_Win_Data *sd, Eina_Bool visible) { Evas_Object *fobj = sd->focus_highlight.fobj; if (!fobj) return; if (visible) { evas_object_show(fobj); edje_object_signal_emit(fobj, "elm,action,focus,show", "elm"); } else { edje_object_signal_emit(fobj, "elm,action,focus,hide", "elm"); } } Evas_Object * _elm_win_focus_highlight_object_get(Evas_Object *obj) { ELM_WIN_DATA_GET(obj, sd); return sd->focus_highlight.fobj; } static void _elm_win_focus_highlight_anim_setup(Efl_Ui_Win_Data *sd, Evas_Object *obj) { Eina_Rect rt, rp; Edje_Message_Int_Set *m; Evas_Object *target = sd->focus_highlight.cur.target; rp = efl_gfx_geometry_get(obj); rt = elm_widget_focus_highlight_geometry_get(target); efl_gfx_geometry_set(obj, rt); if (eina_rectangle_equal(&rp.rect, &rt.rect)) return; if (!_elm_config->focus_highlight_clip_disable) evas_object_clip_unset(obj); m = alloca(sizeof(*m) + (sizeof(int) * 8)); m->count = 8; m->val[0] = rp.x - rt.x; m->val[1] = rp.y - rt.y; m->val[2] = rp.w; m->val[3] = rp.h; m->val[4] = 0; m->val[5] = 0; m->val[6] = rt.w; m->val[7] = rt.h; edje_object_message_send(obj, EDJE_MESSAGE_INT_SET, 1, m); } static void _elm_win_focus_highlight_simple_setup(Efl_Ui_Win_Data *sd, Evas_Object *obj) { Evas_Object *clip, *target = sd->focus_highlight.cur.target; efl_gfx_geometry_set(obj, elm_widget_focus_highlight_geometry_get(target)); if (!_elm_config->focus_highlight_clip_disable) { clip = evas_object_clip_get(target); if (clip) evas_object_clip_set(obj, clip); } edje_object_signal_emit(obj, "elm,state,anim,stop", "elm"); } static void _elm_win_focus_prev_target_del(void *data, Evas *e EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED) { ELM_WIN_DATA_GET(data, sd); sd->focus_highlight.prev.target = NULL; } static void _elm_win_focus_highlight_reconfigure_job(void *data) { ELM_WIN_DATA_GET(data, sd); Evas_Object *target = sd->focus_highlight.cur.target; Evas_Object *previous = sd->focus_highlight.prev.target; Evas_Object *fobj = sd->focus_highlight.fobj; Eina_Bool visible_changed; Eina_Bool common_visible; const char *sig = NULL; const char *focus_style_target = NULL; const char *focus_style_previous = NULL; _elm_win_focus_highlight_reconfigure_job_stop(sd); visible_changed = (sd->focus_highlight.cur.visible != sd->focus_highlight.prev.visible); if ((target == previous) && (!visible_changed) && (!sd->focus_highlight.geometry_changed) && (!sd->focus_highlight.theme_changed)) return; if (previous) { evas_object_event_callback_del_full (previous, EVAS_CALLBACK_DEL, _elm_win_focus_prev_target_del, data); if (sd->focus_highlight.prev.in_theme) elm_widget_signal_emit (previous, "elm,action,focus_highlight,hide", "elm"); } if (!target) common_visible = EINA_FALSE; else if (sd->focus_highlight.cur.in_theme) { common_visible = EINA_FALSE; if (sd->focus_highlight.cur.visible) sig = "elm,action,focus_highlight,show"; else sig = "elm,action,focus_highlight,hide"; } else common_visible = sd->focus_highlight.cur.visible; if (sig) elm_widget_signal_emit(target, sig, "elm"); if ((!target) || (!common_visible) || (sd->focus_highlight.cur.in_theme)) { if (target) _elm_win_focus_highlight_simple_setup(sd, fobj); goto the_end; } if (previous) focus_style_previous = elm_widget_focus_highlight_style_get(previous); focus_style_target = elm_widget_focus_highlight_style_get(target); if (sd->focus_highlight.theme_changed || (focus_style_target != focus_style_previous)) { const char *str; if (focus_style_target) str = focus_style_target; else if (sd->focus_highlight.style) str = sd->focus_highlight.style; else str = "default"; elm_widget_theme_object_set (sd->obj, fobj, "focus_highlight", "top", str); sd->focus_highlight.theme_changed = EINA_FALSE; if ((sd->focus_highlight.animate) || (sd->focus_highlight.auto_animate)) { str = edje_object_data_get(sd->focus_highlight.fobj, "animate"); sd->focus_highlight.animate_supported = ((str) && (!strcmp(str, "on"))); } else sd->focus_highlight.animate_supported = EINA_FALSE; } if ((sd->focus_highlight.animate_supported) && (previous) && (!sd->focus_highlight.prev.in_theme)) _elm_win_focus_highlight_anim_setup(sd, fobj); else _elm_win_focus_highlight_simple_setup(sd, fobj); evas_object_raise(fobj); the_end: _elm_win_focus_highlight_visible_set(sd, common_visible); sd->focus_highlight.geometry_changed = EINA_FALSE; sd->focus_highlight.prev = sd->focus_highlight.cur; if (sd->focus_highlight.prev.target) { evas_object_event_callback_add (sd->focus_highlight.prev.target, EVAS_CALLBACK_DEL, _elm_win_focus_prev_target_del, data); } } static void _elm_win_focus_highlight_reconfigure_job_start(Efl_Ui_Win_Data *sd) { ecore_job_del(sd->focus_highlight.reconf_job); sd->focus_highlight.reconf_job = ecore_job_add( _elm_win_focus_highlight_reconfigure_job, sd->obj); } static void _elm_win_focus_in(Ecore_Evas *ee) { Efl_Ui_Win_Data *sd = _elm_win_associate_get(ee); Evas_Object *obj; if ((!sd) || (sd->modal_count)) return; _elm_win_throttle_ok = EINA_TRUE; obj = sd->obj; _elm_widget_top_win_focused_set(obj, EINA_TRUE); ELM_WIN_DATA_ALIVE_CHECK(obj, sd); if (sd->type != ELM_WIN_FAKE) { Efl_Ui_Focus_Manager *man = sd->obj; while(efl_ui_focus_manager_redirect_get(man)) { man = efl_ui_focus_manager_redirect_get(man); } Evas_Object *focused = efl_ui_focus_manager_focus_get(man); efl_ui_focus_object_focus_set(focused, EINA_TRUE); } evas_object_smart_callback_call(obj, SIG_FOCUS_IN, NULL); ELM_WIN_DATA_ALIVE_CHECK(obj, sd); sd->focus_highlight.cur.visible = EINA_TRUE; _elm_win_focus_highlight_reconfigure_job_start(sd); _elm_win_frame_style_update(sd, 0, 1); if (_elm_config->atspi_mode) { efl_access_window_activated_signal_emit(obj); efl_access_state_changed_signal_emit(obj, EFL_ACCESS_STATE_ACTIVE, EINA_TRUE); } /* do nothing */ /* else if (sd->img_obj) */ /* { */ /* } */ } static void _elm_win_focus_out(Ecore_Evas *ee) { Efl_Ui_Win_Data *sd = _elm_win_associate_get(ee); Evas_Object *obj; if (!sd) return; obj = sd->obj; _elm_widget_top_win_focused_set(obj, EINA_FALSE); ELM_WIN_DATA_ALIVE_CHECK(obj, sd); evas_object_smart_callback_call(obj, SIG_FOCUS_OUT, NULL); ELM_WIN_DATA_ALIVE_CHECK(obj, sd); sd->focus_highlight.cur.visible = EINA_FALSE; _elm_win_focus_highlight_reconfigure_job_start(sd); if (!sd->resizing) _elm_win_frame_style_update(sd, 0, 1); /* access */ _elm_access_object_highlight_disable(evas_object_evas_get(obj)); if (_elm_config->atspi_mode) { efl_access_window_deactivated_signal_emit(obj); efl_access_state_changed_signal_emit(obj, EFL_ACCESS_STATE_ACTIVE, EINA_FALSE); } if (sd->type != ELM_WIN_FAKE) { Efl_Ui_Focus_Manager *man = sd->obj; while(efl_ui_focus_manager_redirect_get(man)) { man = efl_ui_focus_manager_redirect_get(man); } Evas_Object *focused = efl_ui_focus_manager_focus_get(man); efl_ui_focus_object_focus_set(focused, EINA_FALSE); } /* do nothing */ /* if (sd->img_obj) */ /* { */ /* } */ } static void _elm_win_available_profiles_del(Efl_Ui_Win_Data *sd) { Eina_Stringshare *prof; Eina_Iterator *it; it = eina_array_iterator_new(sd->profile.available); EINA_ITERATOR_FOREACH(it, prof) eina_stringshare_del(prof); eina_iterator_free(it); eina_array_flush(sd->profile.available); } static void _elm_win_profile_del(Efl_Ui_Win_Data *sd) { ELM_SAFE_FREE(sd->profile.name, eina_stringshare_del); } static Eina_Bool _internal_elm_win_profile_set(Efl_Ui_Win_Data *sd, const char *profile) { Eina_Bool changed = EINA_FALSE; if (profile == sd->profile.name) return EINA_FALSE; if (profile) { if (eina_stringshare_replace(&sd->profile.name, profile)) changed = EINA_TRUE; } else _elm_win_profile_del(sd); return changed; } static inline Eina_Bool _profile_exists(Efl_Ui_Win_Data *sd, const char *profile) { Eina_Bool found = EINA_FALSE; Eina_Stringshare *prof; Eina_Iterator *it; if (!profile) return EINA_FALSE; it = eina_array_iterator_new(sd->profile.available); EINA_ITERATOR_FOREACH(it, prof) if (!strcmp(profile, prof)) { found = EINA_TRUE; break; } eina_iterator_free(it); return found; } static void _elm_win_profile_update(Efl_Ui_Win_Data *sd) { if (getenv("ELM_PROFILE")) return; if (eina_array_count(sd->profile.available)) { Eina_Bool found = _profile_exists(sd, sd->profile.name); /* If current profile is not present in an available profiles, * change current profile to the 1st element of an array. */ if (!found) _internal_elm_win_profile_set(sd, eina_array_data_get(sd->profile.available, 0)); } _config_profile_lock = EINA_TRUE; _elm_config_profile_set(sd->profile.name); /* update sub ee */ Ecore_Evas *ee2; Eina_List *sub, *l = NULL; sub = ecore_evas_sub_ecore_evas_list_get(sd->ee); EINA_LIST_FOREACH(sub, l, ee2) ecore_evas_window_profile_set(ee2, sd->profile.name); efl_event_callback_legacy_call(sd->obj, EFL_UI_WIN_EVENT_PROFILE_CHANGED, NULL); } static inline void _elm_win_opaque_dirty(Efl_Ui_Win_Data *sd) { #ifdef HAVE_ELEMENTARY_WL2 sd->wl.opaque_dirty = 1; #else (void)sd; #endif } static void _elm_win_opaque_update(Efl_Ui_Win_Data *sd, Eina_Bool force_alpha) { #ifdef HAVE_ELEMENTARY_WL2 int ox, oy, ow, oh; Eina_Bool alpha; Ecore_Evas_Engine_Wl_Data *wdata; const char *engine_name; if (!sd->wl.win) return; if (!sd->shown) return; /* If this isn't a wayland window, BAIL now to avoid destroying * non-wayland engine data structures... */ engine_name = ecore_evas_engine_name_get(sd->ee); if (strncmp(engine_name, "wayland", sizeof("wayland") - 1)) return; wdata = sd->ee->engine.data; alpha = ecore_evas_alpha_get(sd->ee) || force_alpha; if (sd->fullscreen || !sd->frame_obj) { ecore_evas_geometry_get(sd->ee, NULL, NULL, &ow, &oh); if (!alpha) ecore_wl2_window_opaque_region_set(sd->wl.win, 0, 0, ow, oh); else ecore_wl2_window_opaque_region_set(sd->wl.win, 0, 0, 0, 0); wdata->content.x = wdata->content.y = 0; wdata->content.w = ow; wdata->content.h = oh; ecore_wl2_window_geometry_set(sd->wl.win, 0, 0, ow, oh); ecore_wl2_window_input_region_set(sd->wl.win, 0, 0, ow, oh); return; } edje_object_part_geometry_get(sd->frame_obj, "elm.spacer.opaque", &ox, &oy, &ow, &oh); edje_object_part_geometry_get(sd->frame_obj, "elm.swallow.client", &wdata->content.x, &wdata->content.y, &wdata->content.w, &wdata->content.h); if (!alpha) ecore_wl2_window_opaque_region_set(sd->wl.win, ox, oy, ow, oh); else ecore_wl2_window_opaque_region_set(sd->wl.win, 0, 0, 0, 0); /* FIXME: Replace with call to ecore_evas_shadow_geometry_set(). */ ecore_wl2_window_geometry_set(sd->wl.win, ox, oy, ow, oh); ecore_wl2_window_input_region_set(sd->wl.win, ox, oy, ow, oh); #else (void)sd; (void)force_alpha; #endif } static inline void _elm_win_frame_geometry_adjust(Efl_Ui_Win_Data *sd) { int l = 0, t = 0, r = 0, b = 0; if (sd->frame_obj && sd->csd.need && !sd->fullscreen) { int fw, fh, ox, oy, ow, oh; evas_object_geometry_get(sd->frame_obj, NULL, NULL, &fw, &fh); edje_object_part_geometry_get(sd->frame_obj, "elm.spacer.opaque", &ox, &oy, &ow, &oh); l = ox; t = oy; r = fw - ow - l; b = fh - oh - t; } ecore_evas_shadow_geometry_set(sd->ee, l, r, t, b); } static inline Eina_Bool _elm_win_framespace_set(Efl_Ui_Win_Data *sd, int x, int y, int w, int h) { int fx, fy, fw, fh; evas_output_framespace_get(sd->evas, &fx, &fy, &fw, &fh); evas_output_framespace_set(sd->evas, x, y, w, h); // return true if framespace geometry changed return ((fx != x) || (fy != y) || (fw != w) || (fh != h)); } static void _elm_win_frame_obj_update(Efl_Ui_Win_Data *sd) { int ox, oy, ow, oh; int cx, cy, cw, ch; int w, h; if (!sd->frame_obj) return; _elm_win_opaque_dirty(sd); _elm_win_frame_geometry_adjust(sd); evas_object_geometry_get(sd->frame_obj, &ox, &oy, &ow, &oh); edje_object_part_geometry_get(sd->frame_obj, "elm.spacer.content", &cx, &cy, &cw, &ch); if (!_elm_win_framespace_set(sd, cx, cy, ow - cw, oh - ch)) return; _elm_win_frame_geometry_adjust(sd); evas_object_geometry_get(sd->obj, NULL, NULL, &w, &h); TRAP(sd, resize, w, h); } static void _elm_win_state_change(Ecore_Evas *ee) { Efl_Ui_Win_Data *sd = _elm_win_associate_get(ee); Evas_Object *obj; Eina_Bool ch_withdrawn = EINA_FALSE; Eina_Bool ch_sticky = EINA_FALSE; Eina_Bool ch_iconified = EINA_FALSE; Eina_Bool ch_fullscreen = EINA_FALSE; Eina_Bool ch_maximized = EINA_FALSE; Eina_Bool ch_profile = EINA_FALSE; Eina_Bool ch_wm_rotation = EINA_FALSE; const char *profile; if (!sd) return; obj = sd->obj; if (sd->withdrawn != ecore_evas_withdrawn_get(sd->ee)) { sd->withdrawn = ecore_evas_withdrawn_get(sd->ee); ch_withdrawn = EINA_TRUE; } if (sd->sticky != ecore_evas_sticky_get(sd->ee)) { sd->sticky = ecore_evas_sticky_get(sd->ee); ch_sticky = EINA_TRUE; } if (sd->iconified != ecore_evas_iconified_get(sd->ee)) { sd->iconified = ecore_evas_iconified_get(sd->ee); ch_iconified = EINA_TRUE; } if (sd->fullscreen != ecore_evas_fullscreen_get(sd->ee)) { sd->fullscreen = ecore_evas_fullscreen_get(sd->ee); ch_fullscreen = EINA_TRUE; } if (sd->maximized != ecore_evas_maximized_get(sd->ee)) { sd->maximized = ecore_evas_maximized_get(sd->ee); ch_maximized = EINA_TRUE; } if (ecore_evas_window_profile_supported_get(sd->ee)) { profile = ecore_evas_window_profile_get(sd->ee); ch_profile = _internal_elm_win_profile_set(sd, profile); } ELM_WIN_DATA_ALIVE_CHECK(obj, sd); if (sd->wm_rot.use) { if (sd->rot != ecore_evas_rotation_get(sd->ee)) { sd->rot = ecore_evas_rotation_get(sd->ee); ch_wm_rotation = EINA_TRUE; } } _elm_win_state_eval_queue(); if ((ch_withdrawn) || (ch_iconified)) { ELM_WIN_DATA_ALIVE_CHECK(obj, sd); if (sd->withdrawn) efl_event_callback_legacy_call(obj, EFL_UI_WIN_EVENT_WITHDRAWN, NULL); else if (sd->iconified) { efl_event_callback_legacy_call(obj, EFL_UI_WIN_EVENT_ICONIFIED, NULL); if (_elm_config->atspi_mode) efl_access_window_minimized_signal_emit(obj); } else { efl_event_callback_legacy_call(obj, EFL_UI_WIN_EVENT_NORMAL, NULL); if (_elm_config->atspi_mode) efl_access_window_restored_signal_emit(obj); } } if (ch_sticky) { if (sd->sticky) efl_event_callback_legacy_call(obj, EFL_UI_WIN_EVENT_STICK, NULL); else efl_event_callback_legacy_call(obj, EFL_UI_WIN_EVENT_UNSTICK, NULL); } #ifdef HAVE_ELEMENTARY_WL2 if (sd->wl.win) { if (sd->csd.cur_focus != ecore_wl2_window_activated_get(sd->wl.win)) _elm_win_frame_style_update(sd, 0, 1); } #endif if (ch_fullscreen) { ELM_WIN_DATA_ALIVE_CHECK(obj, sd); _elm_win_frame_style_update(sd, 0, 1); if (sd->fullscreen) { _elm_win_opaque_dirty(sd); efl_event_callback_legacy_call (obj, EFL_UI_WIN_EVENT_FULLSCREEN, NULL); } else { efl_event_callback_legacy_call (obj, EFL_UI_WIN_EVENT_UNFULLSCREEN, NULL); } } if (ch_maximized) { ELM_WIN_DATA_ALIVE_CHECK(obj, sd); _elm_win_frame_style_update(sd, 0, 1); if (sd->maximized) { efl_event_callback_legacy_call(obj, EFL_UI_WIN_EVENT_MAXIMIZED, NULL); if (_elm_config->atspi_mode) efl_access_window_maximized_signal_emit(obj); } else { efl_event_callback_legacy_call(obj, EFL_UI_WIN_EVENT_UNMAXIMIZED, NULL); if (_elm_config->atspi_mode) efl_access_window_restored_signal_emit(obj); } } if (ch_profile) { ELM_WIN_DATA_ALIVE_CHECK(obj, sd); _elm_win_profile_update(sd); } if (ch_wm_rotation) { efl_gfx_size_hint_restricted_min_set(obj, EINA_SIZE2D(-1, -1)); efl_gfx_size_hint_max_set(obj, EINA_SIZE2D(-1, -1)); #ifdef HAVE_ELEMENTARY_X ELM_WIN_DATA_ALIVE_CHECK(obj, sd); _elm_win_xwin_update(sd); #endif ELM_WIN_DATA_ALIVE_CHECK(obj, sd); efl_ui_widget_on_orientation_update(obj, sd->rot); efl_event_callback_legacy_call (obj, EFL_UI_WIN_EVENT_ROTATION_CHANGED, NULL); efl_event_callback_legacy_call (obj, EFL_UI_WIN_EVENT_WM_ROTATION_CHANGED, NULL); if (_elm_config->atspi_mode) { Evas_Coord x = 0, y = 0, width = 0, height = 0; elm_win_screen_size_get(obj, &x, &y, &width, &height); if ((sd->rot == 0) || (sd->rot == 180)) { efl_access_bounds_changed_signal_emit(obj, x, y, width, height); } else { efl_access_bounds_changed_signal_emit(obj, x, y, height, width); } } } } EOLIAN static Eina_Bool -_efl_ui_win_efl_ui_widget_on_focus_update(Eo *obj, Efl_Ui_Win_Data *sd) +_efl_ui_win_efl_ui_focus_object_on_focus_update(Eo *obj, Efl_Ui_Win_Data *sd) { - if (!efl_ui_widget_on_focus_update(efl_super(obj, MY_CLASS))) + if (!efl_ui_focus_object_on_focus_update(efl_super(obj, MY_CLASS))) return EINA_TRUE; if (sd->img_obj) evas_object_focus_set(sd->img_obj, efl_ui_focus_object_focus_get(obj)); else evas_object_focus_set(obj, efl_ui_focus_object_focus_get(obj)); return EINA_TRUE; } static Eina_Bool _key_action_return(Evas_Object *obj EINA_UNUSED, const char *params EINA_UNUSED) { return EINA_FALSE; } static Eina_Bool _key_action_move(Evas_Object *obj, const char *params) { const char *dir = params; _elm_widget_focus_auto_show(obj); Efl_Ui_Focus_Direction focus_dir; Efl_Ui_Focus_Object *o; if (!strcmp(dir, "previous")) focus_dir = EFL_UI_FOCUS_DIRECTION_PREVIOUS; else if (!strcmp(dir, "next")) focus_dir = EFL_UI_FOCUS_DIRECTION_NEXT; else if (!strcmp(dir, "left")) focus_dir = EFL_UI_FOCUS_DIRECTION_LEFT; else if (!strcmp(dir, "right")) focus_dir = EFL_UI_FOCUS_DIRECTION_RIGHT; else if (!strcmp(dir, "up")) focus_dir = EFL_UI_FOCUS_DIRECTION_UP; else if (!strcmp(dir, "down")) focus_dir = EFL_UI_FOCUS_DIRECTION_DOWN; else return EINA_FALSE; o = efl_ui_focus_manager_move(obj, focus_dir); if (!o) { if (focus_dir == EFL_UI_FOCUS_DIRECTION_NEXT || focus_dir == EFL_UI_FOCUS_DIRECTION_PREVIOUS) { Efl_Ui_Focus_Object *root; root = efl_ui_focus_manager_root_get(obj); efl_ui_focus_manager_setup_on_first_touch(obj, focus_dir, root); } } return EINA_TRUE; } /* forward events sent to evas to the window */ static void _evas_event_key_cb(void *data, const Efl_Event *ev) { Eo *win = data; Eo *evt = ev->info; Efl_Input_Key_Data *evdata; evdata = efl_data_scope_get(evt, EFL_INPUT_KEY_CLASS); if (!evdata || evdata->win_fed) return; // evas_callbacks will send the event to the focussed object (ie. this win) if (evas_focus_get(evas_object_evas_get(win)) == win) return; efl_event_callback_call(win, ev->desc, evt); } static void _evas_event_pointer_cb(void *data, const Efl_Event *ev) { Eo *win = data; Eo *evt = ev->info; Efl_Input_Pointer_Data *evdata; evdata = efl_data_scope_get(evt, EFL_INPUT_POINTER_CLASS); if (!evdata || evdata->win_fed) return; efl_event_callback_call(win, ev->desc, evt); } /* feed events from the window to evas - for fake inputs */ static void _evas_event_key_feed_fake_cb(void *data, const Efl_Event *ev) { Eo *evas = data; Efl_Input_Event *evt = ev->info; Efl_Input_Key_Data *evdata; if (!efl_input_fake_get(evt)) return; evdata = efl_data_scope_get(evt, EFL_INPUT_KEY_CLASS); if (!evdata || evdata->win_fed) return; evdata->win_fed = EINA_TRUE; efl_event_callback_call(evas, ev->desc, evt); evdata->win_fed = EINA_FALSE; evdata->evas_done = EINA_FALSE; } static void _evas_event_pointer_feed_fake_cb(void *data, const Efl_Event *ev) { Eo *evas = data; Efl_Input_Event *evt = ev->info; Efl_Input_Pointer_Data *evdata; if (!efl_input_fake_get(evt)) return; evdata = efl_data_scope_get(evt, EFL_INPUT_POINTER_CLASS); if (!evdata || evdata->win_fed) return; evdata->win_fed = EINA_TRUE; efl_event_callback_call(evas, ev->desc, evt); evdata->win_fed = EINA_FALSE; evdata->evas_done = EINA_FALSE; } EFL_CALLBACKS_ARRAY_DEFINE(_elm_win_evas_feed_fake_callbacks, { EFL_EVENT_POINTER_MOVE, _evas_event_pointer_feed_fake_cb }, { EFL_EVENT_POINTER_DOWN, _evas_event_pointer_feed_fake_cb }, { EFL_EVENT_POINTER_UP, _evas_event_pointer_feed_fake_cb }, { EFL_EVENT_POINTER_IN, _evas_event_pointer_feed_fake_cb }, { EFL_EVENT_POINTER_OUT, _evas_event_pointer_feed_fake_cb }, { EFL_EVENT_POINTER_CANCEL, _evas_event_pointer_feed_fake_cb }, { EFL_EVENT_POINTER_WHEEL, _evas_event_pointer_feed_fake_cb }, { EFL_EVENT_FINGER_MOVE, _evas_event_pointer_feed_fake_cb }, { EFL_EVENT_FINGER_DOWN, _evas_event_pointer_feed_fake_cb }, { EFL_EVENT_FINGER_UP, _evas_event_pointer_feed_fake_cb }, { EFL_EVENT_KEY_DOWN, _evas_event_key_feed_fake_cb }, { EFL_EVENT_KEY_UP, _evas_event_key_feed_fake_cb }) static void _elm_win_evas_render_post(void *data, Evas *e EINA_UNUSED, void *event_info) { Efl_Gfx_Event_Render_Post *ev = event_info; Eo *win = data; efl_event_callback_legacy_call(win, EFL_CANVAS_EVENT_RENDER_POST, ev); } static void _elm_win_evas_render_pre(void *data, Evas *e EINA_UNUSED, void *event_info EINA_UNUSED) { Eo *win = data; _elm_win_throttle_ok = EINA_TRUE; efl_event_callback_legacy_call(win, EFL_CANVAS_EVENT_RENDER_PRE, NULL); } static void _elm_win_evas_focus_in(void *data, Evas *e EINA_UNUSED, void *event_info EINA_UNUSED) { Eo *win = data; _elm_win_throttle_ok = EINA_TRUE; efl_event_callback_legacy_call(win, EFL_CANVAS_EVENT_FOCUS_IN, NULL); } static void _elm_win_evas_focus_out(void *data, Evas *e EINA_UNUSED, void *event_info EINA_UNUSED) { Eo *win = data; efl_event_callback_legacy_call(win, EFL_CANVAS_EVENT_FOCUS_OUT, NULL); } static void _elm_win_evas_object_focus_in(void *data, Evas *e EINA_UNUSED, void *event_info) { Eo *object = event_info; Eo *win = data; _elm_win_throttle_ok = EINA_TRUE; efl_event_callback_legacy_call(win, EFL_CANVAS_EVENT_OBJECT_FOCUS_IN, object); } static void _elm_win_evas_object_focus_out(void *data, Evas *e EINA_UNUSED, void *event_info) { Eo *object = event_info; Eo *win = data; efl_event_callback_legacy_call(win, EFL_CANVAS_EVENT_OBJECT_FOCUS_OUT, object); } static void _elm_win_evas_device_changed(void *data, Evas *e EINA_UNUSED, void *event_info) { Eo *device = event_info; Eo *win = data; efl_event_callback_legacy_call(win, EFL_CANVAS_EVENT_DEVICE_CHANGED, device); } static void _win_event_add_cb(void *data, const Efl_Event *ev) { const Efl_Callback_Array_Item *array = ev->info; Efl_Ui_Win_Data *sd = data; Efl_Ui_Win *win = ev->object; int i; for (i = 0; array[i].desc; i++) { if (array[i].desc == EFL_EVENT_POINTER_MOVE) { if (!(sd->event_forward.pointer_move++)) efl_event_callback_add(sd->evas, array[i].desc, _evas_event_pointer_cb, win); } else if (array[i].desc == EFL_EVENT_POINTER_DOWN) { if (!(sd->event_forward.pointer_down++)) efl_event_callback_add(sd->evas, array[i].desc, _evas_event_pointer_cb, win); } else if (array[i].desc == EFL_EVENT_POINTER_UP) { if (!(sd->event_forward.pointer_up++)) efl_event_callback_add(sd->evas, array[i].desc, _evas_event_pointer_cb, win); } else if (array[i].desc == EFL_EVENT_POINTER_IN) { if (!(sd->event_forward.pointer_in++)) efl_event_callback_add(sd->evas, array[i].desc, _evas_event_pointer_cb, win); } else if (array[i].desc == EFL_EVENT_POINTER_OUT) { if (!(sd->event_forward.pointer_out++)) efl_event_callback_add(sd->evas, array[i].desc, _evas_event_pointer_cb, win); } else if (array[i].desc == EFL_EVENT_POINTER_CANCEL) { if (!(sd->event_forward.pointer_cancel++)) efl_event_callback_add(sd->evas, array[i].desc, _evas_event_pointer_cb, win); } else if (array[i].desc == EFL_EVENT_POINTER_WHEEL) { if (!(sd->event_forward.pointer_wheel++)) efl_event_callback_add(sd->evas, array[i].desc, _evas_event_pointer_cb, win); } else if (array[i].desc == EFL_EVENT_FINGER_MOVE) { if (!(sd->event_forward.finger_move++)) efl_event_callback_add(sd->evas, array[i].desc, _evas_event_pointer_cb, win); } else if (array[i].desc == EFL_EVENT_FINGER_DOWN) { if (!(sd->event_forward.finger_down++)) efl_event_callback_add(sd->evas, array[i].desc, _evas_event_pointer_cb, win); } else if (array[i].desc == EFL_EVENT_FINGER_UP) { if (!(sd->event_forward.finger_up++)) efl_event_callback_add(sd->evas, array[i].desc, _evas_event_pointer_cb, win); } else if (array[i].desc == EFL_EVENT_KEY_DOWN) { // Legacy API: Must grab key if (elm_widget_is_legacy(win)) return; if (!(sd->event_forward.key_down++)) efl_event_callback_add(sd->evas, array[i].desc, _evas_event_key_cb, win); } else if (array[i].desc == EFL_EVENT_KEY_UP) { // Legacy API: Must grab key if (elm_widget_is_legacy(win)) return; if (!(sd->event_forward.key_up++)) efl_event_callback_add(sd->evas, array[i].desc, _evas_event_key_cb, win); } else if (array[i].desc == EFL_CANVAS_EVENT_RENDER_POST) { if (!(sd->event_forward.render_post++)) evas_event_callback_add(sd->evas, EVAS_CALLBACK_RENDER_POST, _elm_win_evas_render_post, win); } else if (array[i].desc == EFL_CANVAS_EVENT_RENDER_PRE) { if (!(sd->event_forward.render_pre++)) evas_event_callback_add(sd->evas, EVAS_CALLBACK_RENDER_PRE, _elm_win_evas_render_pre, win); } else if (array[i].desc == EFL_CANVAS_EVENT_FOCUS_IN) { if (!(sd->event_forward.focus_in++)) evas_event_callback_add(sd->evas, EVAS_CALLBACK_FOCUS_IN, _elm_win_evas_focus_in, win); } else if (array[i].desc == EFL_CANVAS_EVENT_FOCUS_OUT) { if (!(sd->event_forward.focus_out++)) evas_event_callback_add(sd->evas, EVAS_CALLBACK_FOCUS_OUT, _elm_win_evas_focus_out, win); } else if (array[i].desc == EFL_CANVAS_EVENT_OBJECT_FOCUS_IN) { if (!(sd->event_forward.object_focus_in++)) evas_event_callback_add(sd->evas, EVAS_CALLBACK_CANVAS_OBJECT_FOCUS_IN, _elm_win_evas_object_focus_in, win); } else if (array[i].desc == EFL_CANVAS_EVENT_OBJECT_FOCUS_OUT) { if (!(sd->event_forward.object_focus_out++)) evas_event_callback_add(sd->evas, EVAS_CALLBACK_CANVAS_OBJECT_FOCUS_OUT, _elm_win_evas_object_focus_out, win); } else if (array[i].desc == EFL_CANVAS_EVENT_DEVICE_CHANGED) { if (!(sd->event_forward.device_changed++)) evas_event_callback_add(sd->evas, EVAS_CALLBACK_DEVICE_CHANGED, _elm_win_evas_device_changed, win); } } } static void _win_event_del_cb(void *data, const Efl_Event *ev) { const Efl_Callback_Array_Item *array = ev->info; Efl_Ui_Win_Data *sd = data; Efl_Ui_Win *win = ev->object; int i; for (i = 0; array[i].desc; i++) { if (array[i].desc == EFL_EVENT_POINTER_MOVE) { if (!(--sd->event_forward.pointer_move)) efl_event_callback_del(sd->evas, array[i].desc, _evas_event_pointer_cb, win); } else if (array[i].desc == EFL_EVENT_POINTER_DOWN) { if (!(--sd->event_forward.pointer_down)) efl_event_callback_del(sd->evas, array[i].desc, _evas_event_pointer_cb, win); } else if (array[i].desc == EFL_EVENT_POINTER_UP) { if (!(--sd->event_forward.pointer_up)) efl_event_callback_del(sd->evas, array[i].desc, _evas_event_pointer_cb, win); } else if (array[i].desc == EFL_EVENT_POINTER_IN) { if (!(--sd->event_forward.pointer_in)) efl_event_callback_del(sd->evas, array[i].desc, _evas_event_pointer_cb, win); } else if (array[i].desc == EFL_EVENT_POINTER_OUT) { if (!(--sd->event_forward.pointer_out)) efl_event_callback_del(sd->evas, array[i].desc, _evas_event_pointer_cb, win); } else if (array[i].desc == EFL_EVENT_POINTER_CANCEL) { if (!(--sd->event_forward.pointer_cancel)) efl_event_callback_del(sd->evas, array[i].desc, _evas_event_pointer_cb, win); } else if (array[i].desc == EFL_EVENT_POINTER_WHEEL) { if (!(--sd->event_forward.pointer_wheel)) efl_event_callback_del(sd->evas, array[i].desc, _evas_event_pointer_cb, win); } else if (array[i].desc == EFL_EVENT_FINGER_MOVE) { if (!(--sd->event_forward.finger_move)) efl_event_callback_del(sd->evas, array[i].desc, _evas_event_pointer_cb, win); } else if (array[i].desc == EFL_EVENT_FINGER_DOWN) { if (!(--sd->event_forward.finger_down)) efl_event_callback_del(sd->evas, array[i].desc, _evas_event_pointer_cb, win); } else if (array[i].desc == EFL_EVENT_FINGER_UP) { if (!(--sd->event_forward.finger_up)) efl_event_callback_del(sd->evas, array[i].desc, _evas_event_pointer_cb, win); } else if (array[i].desc == EFL_EVENT_KEY_DOWN) { // Legacy API: Must grab key if (elm_widget_is_legacy(win)) return; if (!(--sd->event_forward.key_down)) efl_event_callback_del(sd->evas, array[i].desc, _evas_event_key_cb, win); } else if (array[i].desc == EFL_EVENT_KEY_UP) { // Legacy API: Must grab key if (elm_widget_is_legacy(win)) return; if (!(--sd->event_forward.key_up)) efl_event_callback_del(sd->evas, array[i].desc, _evas_event_key_cb, win); } else if (array[i].desc == EFL_CANVAS_EVENT_RENDER_POST) { if (!(--sd->event_forward.render_post)) evas_event_callback_del_full(sd->evas, EVAS_CALLBACK_RENDER_POST, _elm_win_evas_render_post, win); } else if (array[i].desc == EFL_CANVAS_EVENT_RENDER_PRE) { if (!(--sd->event_forward.render_pre)) evas_event_callback_del_full(sd->evas, EVAS_CALLBACK_RENDER_PRE, _elm_win_evas_render_pre, win); } else if (array[i].desc == EFL_CANVAS_EVENT_FOCUS_IN) { if (!(--sd->event_forward.focus_in)) evas_event_callback_del_full(sd->evas, EVAS_CALLBACK_FOCUS_IN, _elm_win_evas_focus_in, win); } else if (array[i].desc == EFL_CANVAS_EVENT_FOCUS_OUT) { if (!(--sd->event_forward.focus_out)) evas_event_callback_del_full(sd->evas, EVAS_CALLBACK_FOCUS_OUT, _elm_win_evas_focus_out, win); } else if (array[i].desc == EFL_CANVAS_EVENT_OBJECT_FOCUS_IN) { if (!(--sd->event_forward.object_focus_in)) evas_event_callback_del_full(sd->evas, EVAS_CALLBACK_CANVAS_OBJECT_FOCUS_IN, _elm_win_evas_object_focus_in, win); } else if (array[i].desc == EFL_CANVAS_EVENT_OBJECT_FOCUS_OUT) { if (!(--sd->event_forward.object_focus_out)) evas_event_callback_del_full(sd->evas, EVAS_CALLBACK_CANVAS_OBJECT_FOCUS_OUT, _elm_win_evas_object_focus_out, win); } else if (array[i].desc == EFL_CANVAS_EVENT_DEVICE_CHANGED) { if (!(--sd->event_forward.device_changed)) evas_event_callback_del_full(sd->evas, EVAS_CALLBACK_DEVICE_CHANGED, _elm_win_evas_device_changed, win); } } } static void _win_paused(void *data, const Efl_Event *ev) { Efl_Ui_Win_Data *sd = data; if (sd->paused) { ERR("A window did receive a pause event while still paused. Dismissing."); return ; } sd->paused = EINA_TRUE; _paused_windows++; if (_elm_win_count == _paused_windows) efl_event_callback_call(efl_loop_get(ev->object), EFL_LOOP_EVENT_PAUSE, NULL); } EFL_CALLBACKS_ARRAY_DEFINE(_elm_win_tracking, { EFL_EVENT_CALLBACK_ADD, _win_event_add_cb }, { EFL_EVENT_CALLBACK_DEL, _win_event_del_cb }, { EFL_UI_WIN_EVENT_PAUSE, _win_paused }) static void _elm_win_cb_mouse_up(void *data, const Efl_Event *ev EINA_UNUSED) { DBG("Evas mouse up event"); /*Currently wayland server didn't send mouse up event after resize the window*/ Efl_Ui_Win_Data *sd = data; if(sd->resizing) sd->resizing = EINA_FALSE; } static void _elm_win_resume(void *data, const Efl_Event *ev) { Efl_Ui_Win_Data *sd = data; if (!sd->paused) return ; efl_event_callback_call(sd->obj, EFL_UI_WIN_EVENT_RESUME, NULL); sd->paused = EINA_FALSE; if (_elm_win_count == _paused_windows) efl_event_callback_call(efl_loop_get(ev->object), EFL_LOOP_EVENT_RESUME, NULL); _paused_windows--; } EFL_CALLBACKS_ARRAY_DEFINE(_elm_evas_tracking, { EFL_EVENT_POINTER_UP, _elm_win_cb_mouse_up }, { EFL_CANVAS_EVENT_RENDER_PRE, _elm_win_resume }) static void _deferred_ecore_evas_free(void *data) { ecore_evas_free(data); _elm_win_deferred_free--; } static inline Edje_Object * _elm_win_modal_blocker_edje_get(Efl_Ui_Win_Data *sd) { /* Legacy theme compatibility */ const char *version = edje_object_data_get(sd->legacy.edje, "version"); int v = version ? atoi(version) : 0; if (v < FRAME_OBJ_THEME_MIN_VERSION) { DBG("Detected legacy theme (<1.19) for modal window blocker."); return sd->legacy.edje; } return sd->frame_obj; } static void _elm_win_modality_increment(Efl_Ui_Win_Data *modalsd) { Efl_Ui_Win *current; Eina_List *l; EINA_LIST_FOREACH(_elm_win_list, l, current) { ELM_WIN_DATA_GET_OR_RETURN(current, cursd); if (modalsd != cursd) cursd->modal_count++; if (cursd->modal_count > 0) { Edje_Object *ed = _elm_win_modal_blocker_edje_get(cursd); edje_object_signal_emit(ed, "elm,action,show_blocker", "elm"); efl_event_callback_legacy_call (cursd->main_menu, EFL_UI_WIN_EVENT_ELM_ACTION_BLOCK_MENU, NULL); _elm_win_frame_style_update(cursd, 0, 1); } } } static void _elm_win_modality_decrement(Efl_Ui_Win_Data *modalsd) { Efl_Ui_Win *current; Eina_List *l; EINA_LIST_FOREACH(_elm_win_list, l, current) { ELM_WIN_DATA_GET_OR_RETURN(current, cursd); if ((modalsd != cursd) && (cursd->modal_count > 0)) cursd->modal_count--; if (cursd->modal_count == 0) { Edje_Object *ed = _elm_win_modal_blocker_edje_get(cursd); edje_object_signal_emit(ed, "elm,action,hide_blocker", "elm"); efl_event_callback_legacy_call (cursd->main_menu, ELM_MENU_EVENT_ELM_ACTION_UNBLOCK_MENU, NULL); _elm_win_frame_style_update(cursd, 0, 1); } } } static void _efl_ui_win_show(Eo *obj, Efl_Ui_Win_Data *sd) { Eina_Bool do_eval = EINA_FALSE; sd->shown = EINA_TRUE; if (sd->modal_count) { /* FIXME FIXME FIXME * Ugly code flow: legacy code had an early return in smart_show, ie. * evas object show would be processed but smart object show would be * aborted. This super call tries to simulate that. */ efl_gfx_visible_set(efl_super(obj, EFL_CANVAS_GROUP_CLASS), EINA_TRUE); return; } if ((sd->modal) && (!evas_object_visible_get(obj))) _elm_win_modality_increment(sd); if (!evas_object_visible_get(obj)) do_eval = EINA_TRUE; efl_gfx_visible_set(efl_super(obj, MY_CLASS), EINA_TRUE); if (sd->deferred_resize_job) _elm_win_resize_job(sd->obj); evas_smart_objects_calculate(evas_object_evas_get(obj)); TRAP(sd, show); if (_elm_config->atspi_mode) { Eo *root; efl_access_window_created_signal_emit(obj); root = efl_access_root_get(EFL_ACCESS_MIXIN); if (root) efl_access_children_changed_added_signal_emit(root, obj); } if (do_eval) { if (_elm_win_state_eval_timer) { ecore_timer_del(_elm_win_state_eval_timer); _elm_win_state_eval_timer = NULL; } _elm_win_state_eval(NULL); } if (sd->shot.info) _shot_handle(sd); } static void _efl_ui_win_hide(Eo *obj, Efl_Ui_Win_Data *sd) { if (sd->modal_count) { /* FIXME FIXME FIXME * Ugly code flow: legacy code had an early return in smart_show, ie. * evas object show would be processed but smart object show would be * aborted. This super call tries to simulate that. */ efl_gfx_visible_set(efl_super(obj, EFL_CANVAS_GROUP_CLASS), EINA_FALSE); return; } _elm_win_state_eval_queue(); if ((sd->modal) && (evas_object_visible_get(obj))) _elm_win_modality_decrement(sd); efl_gfx_visible_set(efl_super(obj, MY_CLASS), EINA_FALSE); TRAP(sd, hide); if (sd->frame_obj) { evas_object_hide(sd->frame_obj); } if (sd->img_obj) { evas_object_hide(sd->img_obj); } if (sd->pointer.obj) { evas_object_hide(sd->pointer.obj); } #ifdef HAVE_ELEMENTARY_WL2 if (sd->pointer.ee) { ecore_evas_hide(sd->pointer.ee); } #endif if (_elm_config->atspi_mode) { Eo *root; root = efl_access_root_get(EFL_ACCESS_MIXIN); efl_access_window_destroyed_signal_emit(obj); if (root) efl_access_children_changed_del_signal_emit(root, obj); } if (_elm_win_policy_quit_triggered(obj)) _elm_win_flush_cache_and_exit(obj); } EOLIAN static void _efl_ui_win_efl_gfx_visible_set(Eo *obj, Efl_Ui_Win_Data *sd, Eina_Bool vis) { if (_evas_object_intercept_call(obj, EVAS_OBJECT_INTERCEPT_CB_VISIBLE, 0, vis)) return; if (vis) _efl_ui_win_show(obj, sd); else _efl_ui_win_hide(obj, sd); } EOLIAN static Eina_Position2D _efl_ui_win_efl_canvas_pointer_position_get(Eo *obj EINA_UNUSED, Efl_Ui_Win_Data *sd) { Eina_Position2D pos; evas_pointer_canvas_xy_get(sd->evas, &pos.x, &pos.y); return pos; } EOLIAN static Eina_Bool _efl_ui_win_efl_canvas_pointer_pointer_inside_get(Eo *obj EINA_UNUSED, Efl_Ui_Win_Data *sd, Eo *dev) { return efl_canvas_pointer_inside_get(sd->evas, dev); } /* multi touch support */ static Eina_Bool _input_pointer_iterator_next(Input_Pointer_Iterator *it, void **data) { Eo *sub; if (!eina_iterator_next(it->real_iterator, (void **) &sub)) return EINA_FALSE; if (data) *data = sub; return EINA_TRUE; } static Eo * _input_pointer_iterator_get_container(Input_Pointer_Iterator *it) { return (Eo *) it->object; } static void _input_pointer_iterator_free(Input_Pointer_Iterator *it) { Efl_Input_Pointer *ptr; EINA_LIST_FREE(it->list, ptr) efl_del(ptr); eina_iterator_free(it->real_iterator); free(it); } EOLIAN static Eina_Iterator * _efl_ui_win_efl_canvas_pointer_iterate(const Eo *obj, Efl_Ui_Win_Data *sd, Eina_Bool hover EINA_UNUSED) { Input_Pointer_Iterator *it; Eina_List *list = NULL; int i, cnt; // Note: "hover" is here as a possible extension to this API. At the moment // I don't have any device that could track the position of hovering fingers // and Evas also wouldn't track those. ELM_WIDGET_DATA_GET_OR_RETURN(obj, wd, NULL); cnt = evas_touch_point_list_count(sd->evas); if (!cnt) return NULL; it = calloc(1, sizeof(*it)); if (!it) return NULL; EINA_MAGIC_SET(&it->iterator, EINA_MAGIC_ITERATOR); for (i = 0; i < cnt; i++) { Efl_Input_Pointer_Data *ptrdata; Evas_Touch_Point_State state; Efl_Input_Pointer *ptr; double x, y; ptr = efl_input_instance_get(EFL_INPUT_POINTER_CLASS, (Eo *) obj, (void **) &ptrdata); if (!ptrdata) break; ptrdata->tool = evas_touch_point_list_nth_id_get(sd->evas, i); _efl_input_value_mark(ptrdata, EFL_INPUT_VALUE_TOOL); // Note that "still" maps to "down" here. state = evas_touch_point_list_nth_state_get(sd->evas, i); switch (state) { case EVAS_TOUCH_POINT_DOWN: ptrdata->action = EFL_POINTER_ACTION_DOWN; break; case EVAS_TOUCH_POINT_UP: ptrdata->action = EFL_POINTER_ACTION_UP; break; case EVAS_TOUCH_POINT_MOVE: ptrdata->action = EFL_POINTER_ACTION_MOVE; break; case EVAS_TOUCH_POINT_STILL: ptrdata->action = EFL_POINTER_ACTION_DOWN; break; case EVAS_TOUCH_POINT_CANCEL: ptrdata->action = EFL_POINTER_ACTION_CANCEL; break; default: ptrdata->action = EFL_POINTER_ACTION_NONE; break; } evas_canvas_touch_point_list_nth_xy_get(sd->evas, i, &x, &y); _efl_input_value_mark(ptrdata, EFL_INPUT_VALUE_X); _efl_input_value_mark(ptrdata, EFL_INPUT_VALUE_Y); ptrdata->cur.x = x; ptrdata->cur.y = y; ptrdata->prev = ptrdata->cur; list = eina_list_append(list, ptr); } it->list = list; it->real_iterator = eina_list_iterator_new(it->list); it->iterator.version = EINA_ITERATOR_VERSION; it->iterator.next = FUNC_ITERATOR_NEXT(_input_pointer_iterator_next); it->iterator.get_container = FUNC_ITERATOR_GET_CONTAINER(_input_pointer_iterator_get_container); it->iterator.free = FUNC_ITERATOR_FREE(_input_pointer_iterator_free); it->object = obj; return &it->iterator; } EOLIAN static Eina_Bool _efl_ui_win_efl_canvas_image_max_size_get(Eo *obj EINA_UNUSED, Efl_Ui_Win_Data *sd, Eina_Size2D *max) { return efl_canvas_image_max_size_get(sd->evas, max); } EOLIAN static void _efl_ui_win_efl_canvas_smart_objects_calculate(Eo *obj EINA_UNUSED, Efl_Ui_Win_Data *sd) { evas_smart_objects_calculate(sd->evas); } EOLIAN static Eina_Iterator * _efl_ui_win_efl_canvas_objects_at_xy_get(const Eo *obj EINA_UNUSED, Efl_Ui_Win_Data *sd, Eina_Position2D pos, Eina_Bool include_pass_events_objects, Eina_Bool include_hidden_objects) { Eina_List *objs = NULL; objs = evas_objects_at_xy_get(sd->evas, pos.x, pos.y, include_pass_events_objects, include_hidden_objects); return eina_list_iterator_new(objs); // FIXME: This leaks the list! } EOLIAN static Efl_Gfx * _efl_ui_win_efl_canvas_object_top_at_xy_get(const Eo *obj EINA_UNUSED, Efl_Ui_Win_Data *sd, Eina_Position2D pos, Eina_Bool include_pass_events_objects, Eina_Bool include_hidden_objects) { return evas_object_top_at_xy_get(sd->evas, pos.x, pos.y, include_pass_events_objects, include_hidden_objects); } EOLIAN static Eina_Iterator * _efl_ui_win_efl_canvas_objects_in_rectangle_get(const Eo *obj EINA_UNUSED, Efl_Ui_Win_Data *sd, Eina_Rect r, Eina_Bool include_pass_events_objects, Eina_Bool include_hidden_objects) { Eina_List *objs = NULL; objs = evas_objects_in_rectangle_get(sd->evas, r.x, r.y, r.w, r.h, include_pass_events_objects, include_hidden_objects); return eina_list_iterator_new(objs); // FIXME: This leaks the list! } EOLIAN static Efl_Gfx * _efl_ui_win_efl_canvas_object_top_in_rectangle_get(const Eo *obj EINA_UNUSED, Efl_Ui_Win_Data *sd, Eina_Rect r, Eina_Bool include_pass_events_objects, Eina_Bool include_hidden_objects) { return evas_object_top_in_rectangle_get(sd->evas, r.x, r.y, r.w, r.h, include_pass_events_objects, include_hidden_objects); } EOLIAN static Efl_Input_Device * _efl_ui_win_efl_canvas_device_get(Eo *obj EINA_UNUSED, Efl_Ui_Win_Data *sd, const char *name) { return efl_canvas_device_get(sd->evas, name); } EOLIAN static Efl_Input_Device * _efl_ui_win_efl_canvas_seat_get(Eo *obj EINA_UNUSED, Efl_Ui_Win_Data *sd, int id) { return efl_canvas_seat_get(sd->evas, id); } static void _elm_win_on_parent_del(void *data, Evas *e EINA_UNUSED, Evas_Object *obj, void *event_info EINA_UNUSED) { ELM_WIN_DATA_GET(data, sd); if (obj == sd->parent) sd->parent = NULL; } static void _elm_win_focus_target_move(void *data, Evas *e EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED) { ELM_WIN_DATA_GET(data, sd); sd->focus_highlight.geometry_changed = EINA_TRUE; _elm_win_focus_highlight_reconfigure_job_start(sd); } static void _elm_win_focus_target_resize(void *data, Evas *e EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED) { ELM_WIN_DATA_GET(data, sd); sd->focus_highlight.geometry_changed = EINA_TRUE; _elm_win_focus_highlight_reconfigure_job_start(sd); } static void _elm_win_focus_target_del(void *data, Evas *e EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED) { ELM_WIN_DATA_GET(data, sd); sd->focus_highlight.cur.target = NULL; _elm_win_focus_highlight_reconfigure_job_start(sd); } static Evas_Object * _elm_win_focus_target_get(Evas_Object *obj) { Evas_Object *o = obj; do { if (elm_widget_is(o)) { if (!elm_widget_highlight_ignore_get(o)) break; o = elm_widget_parent_get(o); if (!o) o = evas_object_smart_parent_get(o); } else { o = elm_widget_parent_widget_get(o); if (!o) o = evas_object_smart_parent_get(o); } } while (o); return o; } static void _elm_win_focus_target_callbacks_add(Efl_Ui_Win_Data *sd) { Evas_Object *obj = sd->focus_highlight.cur.target; if (!obj) return; evas_object_event_callback_add (obj, EVAS_CALLBACK_MOVE, _elm_win_focus_target_move, sd->obj); evas_object_event_callback_add (obj, EVAS_CALLBACK_RESIZE, _elm_win_focus_target_resize, sd->obj); } static void _elm_win_focus_target_callbacks_del(Efl_Ui_Win_Data *sd) { Evas_Object *obj = sd->focus_highlight.cur.target; evas_object_event_callback_del_full (obj, EVAS_CALLBACK_MOVE, _elm_win_focus_target_move, sd->obj); evas_object_event_callback_del_full (obj, EVAS_CALLBACK_RESIZE, _elm_win_focus_target_resize, sd->obj); } static void _elm_win_object_focus_in(void *data, Evas *e EINA_UNUSED, void *event_info) { Evas_Object *obj = event_info, *target; ELM_WIN_DATA_GET(data, sd); if (sd->focus_highlight.cur.target == obj) return; target = _elm_win_focus_target_get(obj); sd->focus_highlight.cur.target = target; if (target) { if (elm_widget_highlight_in_theme_get(target)) sd->focus_highlight.cur.in_theme = EINA_TRUE; else _elm_win_focus_target_callbacks_add(sd); evas_object_event_callback_add (target, EVAS_CALLBACK_DEL, _elm_win_focus_target_del, sd->obj); } _elm_win_focus_highlight_reconfigure_job_start(sd); } static void _elm_win_object_focus_out(void *data, Evas *e EINA_UNUSED, void *event_info EINA_UNUSED) { ELM_WIN_DATA_GET(data, sd); if (!sd->focus_highlight.cur.target) return; if (!sd->focus_highlight.cur.in_theme) _elm_win_focus_target_callbacks_del(sd); evas_object_event_callback_del_full (sd->focus_highlight.cur.target, EVAS_CALLBACK_DEL, _elm_win_focus_target_del, sd->obj); sd->focus_highlight.cur.target = NULL; sd->focus_highlight.cur.in_theme = EINA_FALSE; _elm_win_focus_highlight_reconfigure_job_start(sd); } static void _elm_win_focus_highlight_shutdown(Efl_Ui_Win_Data *sd) { _elm_win_focus_highlight_reconfigure_job_stop(sd); if (sd->focus_highlight.cur.target) { elm_widget_signal_emit(sd->focus_highlight.cur.target, "elm,action,focus_highlight,hide", "elm"); _elm_win_focus_target_callbacks_del(sd); evas_object_event_callback_del_full (sd->focus_highlight.cur.target, EVAS_CALLBACK_DEL, _elm_win_focus_target_del, sd->obj); sd->focus_highlight.cur.target = NULL; } ELM_SAFE_FREE(sd->focus_highlight.fobj, evas_object_del); evas_event_callback_del_full (sd->evas, EVAS_CALLBACK_CANVAS_OBJECT_FOCUS_IN, _elm_win_object_focus_in, sd->obj); evas_event_callback_del_full (sd->evas, EVAS_CALLBACK_CANVAS_OBJECT_FOCUS_OUT, _elm_win_object_focus_out, sd->obj); } static void _win_img_hide(void *data, Evas *e EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED) { Efl_Ui_Win *real_win = elm_widget_top_get(data); efl_ui_focus_manager_redirect_set(real_win, NULL); } static void _win_img_mouse_up(void *data, Evas *e EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event_info) { Evas_Event_Mouse_Up *ev = event_info; if (!(ev->event_flags & EVAS_EVENT_FLAG_ON_HOLD)) elm_widget_focus_mouse_up_handle(evas_object_widget_parent_find(data)); } static void _win_img_focus_in(void *data, Evas *e EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED) { Efl_Ui_Win *real_win = elm_widget_top_get(data); efl_ui_focus_manager_redirect_set(real_win, data); efl_ui_focus_manager_focus_set(data, efl_ui_focus_manager_root_get(data)); } static void _win_img_focus_out(void *data, Evas *e EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED) { Efl_Ui_Win *real_win = elm_widget_top_get(data); efl_ui_focus_manager_redirect_set(real_win, NULL); } static void _elm_win_on_img_obj_del(void *data, Evas *e EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED) { ELM_WIN_DATA_GET(data, sd); _elm_win_img_callbacks_del(sd->obj, sd->img_obj); sd->img_obj = NULL; } static void _elm_win_img_callbacks_del(Evas_Object *obj, Evas_Object *imgobj) { if (!imgobj) return; evas_object_event_callback_del_full (imgobj, EVAS_CALLBACK_DEL, _elm_win_on_img_obj_del, obj); evas_object_event_callback_del_full (imgobj, EVAS_CALLBACK_HIDE, _win_img_hide, obj); evas_object_event_callback_del_full (imgobj, EVAS_CALLBACK_MOUSE_UP, _win_img_mouse_up, obj); evas_object_event_callback_del_full (imgobj, EVAS_CALLBACK_FOCUS_IN, _win_img_focus_in, obj); evas_object_event_callback_del_full (imgobj, EVAS_CALLBACK_FOCUS_OUT, _win_img_focus_out, obj); } EOLIAN static void _efl_ui_win_efl_canvas_group_group_del(Eo *obj, Efl_Ui_Win_Data *sd) { if ((sd->modal) && (evas_object_visible_get(obj))) _elm_win_modality_decrement(sd); if ((sd->modal) && (sd->modal_count > 0)) ERR("Deleted modal win was blocked by another modal win which was created after creation of that win."); evas_object_event_callback_del_full(sd->legacy.edje, EVAS_CALLBACK_CHANGED_SIZE_HINTS, _elm_win_on_resize_obj_changed_size_hints, obj); efl_event_callback_array_del(sd->evas, _elm_evas_tracking(), sd); efl_event_callback_array_del(obj, _elm_win_evas_feed_fake_callbacks(), sd->evas); efl_event_callback_array_del(obj, _elm_win_tracking(), sd); evas_object_del(sd->legacy.box); evas_object_del(sd->legacy.edje); /* NB: child deletion handled by parent's smart del */ if ((sd->type != ELM_WIN_FAKE) && (trap) && (trap->del)) trap->del(sd->trap_data, obj); if (sd->parent) { evas_object_event_callback_del_full (sd->parent, EVAS_CALLBACK_DEL, _elm_win_on_parent_del, obj); sd->parent = NULL; } if (sd->autodel_clear) *(sd->autodel_clear) = -1; if (_elm_config->atspi_mode) efl_access_window_destroyed_signal_emit(obj); _elm_win_list = eina_list_remove(_elm_win_list, obj); _elm_win_count--; _elm_win_state_eval_queue(); if (_elm_win_count == _paused_windows) efl_event_callback_call(efl_loop_get(obj), EFL_LOOP_EVENT_PAUSE, NULL); if (sd->ee) { ecore_evas_callback_delete_request_set(sd->ee, NULL); ecore_evas_callback_resize_set(sd->ee, NULL); } eina_stringshare_del(sd->shot.info); ecore_timer_del(sd->shot.timer); #ifdef HAVE_ELEMENTARY_X ecore_event_handler_del(sd->x.client_message_handler); ecore_event_handler_del(sd->x.property_handler); #endif #ifdef HAVE_ELEMENTARY_WL2 ecore_event_handler_del(sd->wl.configure_handler); if (sd->pointer.obj) evas_object_del(sd->pointer.obj); if (sd->pointer.ee) ecore_evas_free(sd->pointer.ee); sd->pointer.surf = NULL; #endif #ifdef HAVE_ELEMENTARY_WIN32 ecore_event_handler_del(sd->win32.key_down_handler); #endif if (sd->type == ELM_WIN_INLINED_IMAGE) { _elm_win_img_callbacks_del(obj, sd->img_obj); sd->img_obj = NULL; } else { if (sd->ee && (sd->type != ELM_WIN_FAKE)) { ecore_job_add(_deferred_ecore_evas_free, sd->ee); _elm_win_deferred_free++; } } _elm_win_focus_highlight_shutdown(sd); eina_stringshare_del(sd->focus_highlight.style); eina_stringshare_del(sd->title); eina_stringshare_del(sd->icon_name); eina_stringshare_del(sd->role); eina_stringshare_del(sd->name); eina_stringshare_del(sd->accel_pref); eina_stringshare_del(sd->stack_id); eina_stringshare_del(sd->stack_master_id); evas_object_del(sd->icon); evas_object_del(sd->main_menu); evas_object_del(sd->indicator); sd->focus_highlight.style = NULL; sd->title = NULL; sd->icon_name = NULL; sd->role = NULL; sd->name = NULL; sd->icon = NULL; sd->main_menu = NULL; _elm_win_profile_del(sd); _elm_win_available_profiles_del(sd); eina_array_free(sd->profile.available); sd->profile.available = NULL; free(sd->wm_rot.rots); sd->wm_rot.rots = NULL; /* Don't let callback in the air that point to sd */ if (sd->ee) { ecore_evas_callback_mouse_in_set(sd->ee, NULL); ecore_evas_callback_focus_in_set(sd->ee, NULL); ecore_evas_callback_focus_out_set(sd->ee, NULL); ecore_evas_callback_move_set(sd->ee, NULL); ecore_evas_callback_state_change_set(sd->ee, NULL); ecore_evas_callback_pre_render_set(sd->ee, NULL); } efl_canvas_group_del(efl_super(obj, MY_CLASS)); if (_elm_win_policy_quit_triggered(obj)) { _elm_win_flush_cache_and_exit(obj); } } static void _elm_win_obj_intercept_show(void *data, Evas_Object *obj) { ELM_WIN_DATA_GET(data, sd); /* FIXME: this intercept needs to be implemented in proper EO */ // this is called to make sure all smart containers have calculated their // sizes BEFORE we show the window to make sure it initially appears at // our desired size (ie min size is known first) evas_smart_objects_calculate(evas_object_evas_get(obj)); if (sd->frame_obj) { evas_object_show(sd->frame_obj); } if (sd->img_obj) { evas_object_show(sd->img_obj); } if (sd->pointer.obj) evas_object_show(sd->pointer.obj); evas_object_show(obj); #ifdef ELEMENTARY_X if (sd->type == ELM_WIN_TOOLTIP) ecore_x_window_shape_input_rectangle_set(sd->x.xwin, 0, 0, 0, 0); #endif } EOLIAN static void _efl_ui_win_efl_gfx_position_set(Eo *obj, Efl_Ui_Win_Data *sd, Eina_Position2D pos) { if (_evas_object_intercept_call(obj, EVAS_OBJECT_INTERCEPT_CB_MOVE, 0, pos.x, pos.y)) return; if (sd->img_obj) { if ((pos.x != sd->screen.x) || (pos.y != sd->screen.y)) { sd->screen.x = pos.x; sd->screen.y = pos.y; efl_event_callback_legacy_call(obj, EFL_GFX_EVENT_MOVE, NULL); } goto super_skip; } else { if (!sd->response) { sd->req_xy = EINA_TRUE; sd->req_x = pos.x; sd->req_y = pos.y; TRAP(sd, move, pos.x, pos.y); } if (!ecore_evas_override_get(sd->ee)) goto super_skip; } efl_gfx_position_set(efl_super(obj, MY_CLASS), pos); if (ecore_evas_override_get(sd->ee)) { sd->screen.x = pos.x; sd->screen.y = pos.y; efl_event_callback_legacy_call(obj, EFL_GFX_EVENT_MOVE, NULL); } if (sd->frame_obj) { #ifdef HAVE_ELEMENTARY_WL2 /* TODO */ /* ecore_wl_window_update_location(sd->wl.win, x, y); */ #endif sd->screen.x = pos.x; sd->screen.y = pos.y; } if (sd->img_obj) { sd->screen.x = pos.x; sd->screen.y = pos.y; } return; super_skip: /* FIXME FIXME FIXME * Ugly code flow: legacy code had an early return in smart_move, ie. * evas object move would be processed but smart object move would be * aborted. This super call tries to simulate that. */ efl_gfx_position_set(efl_super(obj, EFL_CANVAS_GROUP_CLASS), pos); } EOLIAN static void _efl_ui_win_efl_gfx_size_set(Eo *obj, Efl_Ui_Win_Data *sd, Eina_Size2D sz) { if (_evas_object_intercept_call(obj, EVAS_OBJECT_INTERCEPT_CB_RESIZE, 0, sz.w, sz.h)) return; if (sd->img_obj) { if (sd->constrain) { int sw, sh; ecore_evas_screen_geometry_get(sd->ee, NULL, NULL, &sw, &sh); sz.w = MIN(sz.w, sw); sz.h = MIN(sz.h, sh); } if (sz.w < 1) sz.w = 1; if (sz.h < 1) sz.h = 1; evas_object_image_size_set(sd->img_obj, sz.w, sz.h); } _elm_win_frame_geometry_adjust(sd); if (!sd->response) { sd->req_wh = EINA_TRUE; sd->req_w = sz.w; sd->req_h = sz.h; TRAP(sd, resize, sz.w, sz.h); } efl_gfx_size_set(efl_super(obj, MY_CLASS), sz); } static void _elm_win_delete_request(Ecore_Evas *ee) { Efl_Ui_Win_Data *sd = _elm_win_associate_get(ee); Evas_Object *obj; if (!sd) return; obj = sd->obj; int autodel = sd->autodel; sd->autodel_clear = &autodel; evas_object_ref(obj); efl_event_callback_legacy_call(obj, EFL_UI_WIN_EVENT_DELETE_REQUEST, NULL); ELM_WIN_DATA_ALIVE_CHECK(obj, sd); if (sd->autohide) evas_object_hide(obj); ELM_WIN_DATA_ALIVE_CHECK(obj, sd); if (_elm_config->atspi_mode) efl_access_window_destroyed_signal_emit(obj); ELM_WIN_DATA_ALIVE_CHECK(obj, sd); if (autodel) evas_object_del(obj); else sd->autodel_clear = NULL; evas_object_unref(obj); } Ecore_X_Window _elm_ee_xwin_get(const Ecore_Evas *ee) { #ifdef HAVE_ELEMENTARY_X const char *engine_name; if (!ee) return 0; engine_name = ecore_evas_engine_name_get(ee); if (EINA_UNLIKELY(!engine_name)) return 0; if (!strcmp(engine_name, ELM_SOFTWARE_X11)) { return ecore_evas_software_x11_window_get(ee); } else if (!strcmp(engine_name, ELM_OPENGL_X11)) { return ecore_evas_gl_x11_window_get(ee); } #else (void)ee; #endif return 0; } #ifdef HAVE_ELEMENTARY_X static void _internal_elm_win_xwindow_get(Efl_Ui_Win_Data *sd) { Ecore_X_Window pwin = sd->x.xwin; sd->x.xwin = _elm_ee_xwin_get(sd->ee); if (sd->x.xwin != pwin) { char buf[128]; snprintf(buf, sizeof(buf), "%x", sd->x.xwin); eina_stringshare_del(sd->stack_id); sd->stack_id = eina_stringshare_add(buf); } } #endif Ecore_Wl2_Window * _elm_ee_wlwin_get(const Ecore_Evas *ee) { #ifdef HAVE_ELEMENTARY_WL2 const char *engine_name; if (!ee) return NULL; engine_name = ecore_evas_engine_name_get(ee); if (EINA_UNLIKELY(!engine_name)) return NULL; if ((!strcmp(engine_name, ELM_WAYLAND_SHM)) || (!strcmp(engine_name, ELM_WAYLAND_EGL))) { return ecore_evas_wayland2_window_get(ee); } #else (void)ee; #endif return NULL; } #ifdef HAVE_ELEMENTARY_WL2 static void _elm_win_wlwindow_get(Efl_Ui_Win_Data *sd) { Ecore_Wl2_Window *pwin = sd->wl.win; sd->wl.win = _elm_ee_wlwin_get(sd->ee); if (sd->wl.win != pwin) { char buf[128]; int id; snprintf(buf, sizeof(buf), "%u||%p", getpid(), sd->wl.win); eina_stringshare_replace(&sd->stack_id, buf); id = ecore_evas_aux_hint_id_get(sd->ee, "stack_id"); if (id >= 0) ecore_evas_aux_hint_val_set(sd->ee, id, sd->stack_id); else ecore_evas_aux_hint_add(sd->ee, "stack_id", sd->stack_id); } } void _elm_win_wl_cursor_set(Evas_Object *obj, const char *cursor) { ELM_WIN_DATA_GET(obj, sd); if (!sd) return; if (sd->pointer.obj) { Evas_Coord mw = 1, mh = 1, hx = 0, hy = 0; if (cursor) { if (!elm_widget_theme_object_set(sd->obj, sd->pointer.obj, "cursor", cursor, "default")) { elm_widget_theme_object_set(sd->obj, sd->pointer.obj, "pointer", "base", "default"); } } else elm_widget_theme_object_set(sd->obj, sd->pointer.obj, "pointer", "base", "default"); edje_object_size_min_get(sd->pointer.obj, &mw, &mh); edje_object_size_min_restricted_calc(sd->pointer.obj, &mw, &mh, mw, mh); if ((mw < 32) || (mh < 32)) { mw = 32; mh = 32; } evas_object_move(sd->pointer.obj, 0, 0); evas_object_resize(sd->pointer.obj, mw, mh); edje_object_part_geometry_get(sd->pointer.obj, "elm.swallow.hotspot", &hx, &hy, NULL, NULL); sd->pointer.hot_x = hx; sd->pointer.hot_y = hy; ecore_evas_resize(sd->pointer.ee, mw, mh); } if ((sd->wl.win) && (sd->pointer.surf) && (sd->pointer.visible)) { /* FIXME: multiseat */ Ecore_Wl2_Input *input; Eina_Iterator *it; it = ecore_wl2_display_inputs_get(ecore_wl2_window_display_get(sd->wl.win)); EINA_ITERATOR_FOREACH(it, input) ecore_wl2_input_pointer_set(input, sd->pointer.surf, sd->pointer.hot_x, sd->pointer.hot_y); eina_iterator_free(it); } } #endif Ecore_Cocoa_Window * _elm_ee_cocoa_win_get(const Ecore_Evas *ee) { #ifdef HAVE_ELEMENTARY_COCOA const char *engine_name; if (!ee) return NULL; engine_name = ecore_evas_engine_name_get(ee); if (EINA_UNLIKELY(!engine_name)) return NULL; if (!strcmp(engine_name, "opengl_cocoa") || !strcmp(engine_name, "gl_cocoa")) return ecore_evas_cocoa_window_get(ee); #else (void)ee; #endif return NULL; } Ecore_Win32_Window * _elm_ee_win32win_get(const Ecore_Evas *ee) { #ifdef HAVE_ELEMENTARY_WIN32 const char *engine_name; if (!ee) return NULL; engine_name = ecore_evas_engine_name_get(ee); if (EINA_UNLIKELY(!engine_name)) return NULL; if ((!strcmp(engine_name, ELM_SOFTWARE_WIN32)) || (!strcmp(engine_name, ELM_SOFTWARE_DDRAW))) { return ecore_evas_win32_window_get(ee); } #else (void)ee; #endif return NULL; } #ifdef HAVE_ELEMENTARY_COCOA static void _elm_win_cocoawindow_get(Efl_Ui_Win_Data *sd) { sd->cocoa.win = _elm_ee_cocoa_win_get(sd->ee); } #endif #ifdef HAVE_ELEMENTARY_WIN32 static void _internal_elm_win_win32window_get(Efl_Ui_Win_Data *sd) { sd->win32.win = _elm_ee_win32win_get(sd->ee); } #endif #ifdef HAVE_ELEMENTARY_X static void _elm_win_xwin_update(Efl_Ui_Win_Data *sd) { const char *s; if (sd->type == ELM_WIN_FAKE) return; _internal_elm_win_xwindow_get(sd); if (!sd->x.xwin) return; /* nothing more to do */ if (sd->stack_master_id) { Ecore_X_Window win = strtol(sd->stack_master_id, NULL, 16); if (win) { ecore_x_icccm_transient_for_set(sd->x.xwin, win); if (sd->stack_base) ecore_x_e_stack_type_set(sd->x.xwin, ECORE_X_STACK_BASE); else ecore_x_e_stack_type_set(sd->x.xwin, ECORE_X_STACK_STANDARD); } } else { if (sd->parent) { ELM_WIN_DATA_GET(sd->parent, sdp); if (sdp) ecore_x_icccm_transient_for_set(sd->x.xwin, sdp->x.xwin); } } s = sd->title; if (!s) s = _elm_appname; if (!s) s = ""; if (sd->icon_name) s = sd->icon_name; ecore_x_icccm_icon_name_set(sd->x.xwin, s); ecore_x_netwm_icon_name_set(sd->x.xwin, s); s = sd->role; if (s) ecore_x_icccm_window_role_set(sd->x.xwin, s); // set window icon if (sd->icon) { Eo *image = NULL; if (efl_isa(sd->icon, EFL_CANVAS_IMAGE_INTERNAL_CLASS)) image = sd->icon; else if (efl_isa(sd->icon, EFL_UI_IMAGE_CLASS)) image = elm_image_object_get(sd->icon); if (image) { int w = 0, h = 0, stride, x, y; Eina_Bool unmap = EINA_FALSE; Eina_Rw_Slice sl = {}; if (efl_isa(image, EFL_CANVAS_IMAGE_CLASS)) { Eina_Rect rect = {}; unmap = EINA_TRUE; rect.size = efl_gfx_buffer_size_get(image); sl = efl_gfx_buffer_map(image, EFL_GFX_BUFFER_ACCESS_MODE_READ, &rect, EFL_GFX_COLORSPACE_ARGB8888, 0, &stride); w = rect.w; h = rect.h; } else { evas_object_image_size_get(image, &w, &h); stride = evas_object_image_stride_get(image); sl.mem = evas_object_image_data_get(image, EINA_FALSE); } if (sl.mem) { Ecore_X_Icon ic; ic.width = w; ic.height = h; if ((w > 0) && (h > 0) && (stride >= (int)(w * sizeof(unsigned int)))) { if (stride == (int)(w * sizeof(unsigned int))) { ic.data = sl.mem; ecore_x_netwm_icons_set(sd->x.xwin, &ic, 1); } else { ic.data = malloc(w * h * sizeof(unsigned int)); if (ic.data) { unsigned char *p = sl.mem; unsigned int *p2 = ic.data; for (y = 0; y < h; y++) { for (x = 0; x < w; x++) { *p2 = *((unsigned int *)p); p += sizeof(unsigned int); p2++; } p += (stride - (w * sizeof(unsigned int))); } ecore_x_netwm_icons_set(sd->x.xwin, &ic, 1); free(ic.data); } } } if (unmap) efl_gfx_buffer_unmap(image, sl); else evas_object_image_data_set(image, sl.mem); } } } switch (sd->type) { case ELM_WIN_BASIC: ecore_x_netwm_window_type_set(sd->x.xwin, ECORE_X_WINDOW_TYPE_NORMAL); break; case ELM_WIN_DIALOG_BASIC: ecore_x_netwm_window_type_set(sd->x.xwin, ECORE_X_WINDOW_TYPE_DIALOG); break; case ELM_WIN_DESKTOP: ecore_x_netwm_window_type_set(sd->x.xwin, ECORE_X_WINDOW_TYPE_DESKTOP); break; case ELM_WIN_DOCK: ecore_x_netwm_window_type_set(sd->x.xwin, ECORE_X_WINDOW_TYPE_DOCK); break; case ELM_WIN_TOOLBAR: ecore_x_netwm_window_type_set(sd->x.xwin, ECORE_X_WINDOW_TYPE_TOOLBAR); break; case ELM_WIN_MENU: ecore_x_netwm_window_type_set(sd->x.xwin, ECORE_X_WINDOW_TYPE_MENU); break; case ELM_WIN_UTILITY: ecore_x_netwm_window_type_set(sd->x.xwin, ECORE_X_WINDOW_TYPE_UTILITY); break; case ELM_WIN_SPLASH: ecore_x_netwm_window_type_set(sd->x.xwin, ECORE_X_WINDOW_TYPE_SPLASH); break; case ELM_WIN_DROPDOWN_MENU: ecore_x_netwm_window_type_set (sd->x.xwin, ECORE_X_WINDOW_TYPE_DROPDOWN_MENU); break; case ELM_WIN_POPUP_MENU: ecore_x_netwm_window_type_set (sd->x.xwin, ECORE_X_WINDOW_TYPE_POPUP_MENU); break; case ELM_WIN_TOOLTIP: ecore_x_netwm_window_type_set(sd->x.xwin, ECORE_X_WINDOW_TYPE_TOOLTIP); ecore_x_window_shape_input_rectangle_set(sd->x.xwin, 0, 0, 0, 0); break; case ELM_WIN_NOTIFICATION: ecore_x_netwm_window_type_set (sd->x.xwin, ECORE_X_WINDOW_TYPE_NOTIFICATION); break; case ELM_WIN_COMBO: ecore_x_netwm_window_type_set(sd->x.xwin, ECORE_X_WINDOW_TYPE_COMBO); break; case ELM_WIN_DND: ecore_x_netwm_window_type_set(sd->x.xwin, ECORE_X_WINDOW_TYPE_DND); break; default: break; } ecore_x_e_virtual_keyboard_state_set (sd->x.xwin, (Ecore_X_Virtual_Keyboard_State)sd->kbdmode); if (sd->legacy.indmode == ELM_WIN_INDICATOR_SHOW) ecore_x_e_illume_indicator_state_set (sd->x.xwin, ECORE_X_ILLUME_INDICATOR_STATE_ON); else if (sd->legacy.indmode == ELM_WIN_INDICATOR_HIDE) ecore_x_e_illume_indicator_state_set (sd->x.xwin, ECORE_X_ILLUME_INDICATOR_STATE_OFF); if ((sd->wm_rot.count) && (sd->wm_rot.rots)) ecore_evas_wm_rotation_available_rotations_set(sd->ee, sd->wm_rot.rots, sd->wm_rot.count); if (sd->wm_rot.preferred_rot != -1) ecore_evas_wm_rotation_preferred_rotation_set(sd->ee, sd->wm_rot.preferred_rot); #ifdef HAVE_ELEMENTARY_X if (sd->csd.need && sd->x.xwin) TRAP(sd, borderless_set, EINA_TRUE); #endif } #endif /** * @internal * * Resize the window according to window layout's min and weight. * If the window layout's weight is 0.0, the window max is limited to layout's * min size. * * This is called when the window layout's weight hint is changed or when the * window is rotated. * * @param obj window object */ static void _elm_win_resize_objects_eval(Evas_Object *obj, Eina_Bool force_resize) { Efl_Ui_Win_Data *sd = efl_data_scope_get(obj, MY_CLASS); Evas_Coord w, h, minw, minh, maxw, maxh, ow, oh; Eina_Bool unresizable; double wx, wy; evas_object_size_hint_combined_min_get(sd->legacy.edje, &minw, &minh); if ((!minw) && (!minh)) return; // If content has a weight, make resizable efl_gfx_size_hint_weight_get(sd->legacy.edje, &wx, &wy); // Content max hint is ignored maxw = sd->max_w; maxh = sd->max_h; // Compatibility hack (for E) if (sd->single_edje_content && !wx && !wy) wx = wy = 1; if (!wx) maxw = minw; else if (maxw < 1) maxw = 32767; if (!wy) maxh = minh; else if (maxh < 1) maxh = 32767; if (maxw < minw) maxw = minw; if (maxh < minh) maxh = minh; if (maxw > 32767) maxw = 32767; if (maxh > 32767) maxh = 32767; unresizable = ((minw == maxw) && (minh == maxh)); if (sd->csd.need_unresizable != unresizable) { sd->csd.need_unresizable = unresizable; _elm_win_frame_style_update(sd, 0, 1); } if (sd->frame_obj) { int fw, fh; evas_output_framespace_get(sd->evas, NULL, NULL, &fw, &fh); minw += fw; minh += fh; maxw += fw; maxh += fh; } sd->tmp_updating_hints = 1; efl_gfx_size_hint_restricted_min_set(obj, EINA_SIZE2D(minw, minh)); efl_gfx_size_hint_max_set(obj, EINA_SIZE2D(maxw, maxh)); sd->tmp_updating_hints = 0; _elm_win_size_hints_update(obj, sd); evas_object_geometry_get(obj, NULL, NULL, &ow, &oh); w = ow; h = oh; if (w < minw) w = minw; if (h < minh) h = minh; if (w > maxw) w = maxw; if (h > maxh) h = maxh; if (!force_resize && (w == ow) && (h == oh)) return; sd->req_wh = EINA_FALSE; if (sd->img_obj) evas_object_resize(obj, w, h); else { _elm_win_frame_geometry_adjust(sd); if (!sd->response) { sd->req_wh = EINA_TRUE; sd->req_w = w; sd->req_h = h; TRAP(sd, resize, w, h); } } } static void _elm_win_on_resize_obj_changed_size_hints(void *data, Evas *e EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED) { _elm_win_resize_objects_eval(data, EINA_FALSE); } void _elm_win_shutdown(void) { while (_elm_win_list) { Eina_List *itr = _elm_win_list; evas_object_del(itr->data); if (_elm_win_list == itr) { _elm_win_list = eina_list_remove_list(_elm_win_list, _elm_win_list); } } ELM_SAFE_FREE(_elm_win_state_eval_timer, ecore_timer_del); } void _elm_win_rescale(Elm_Theme *th, Eina_Bool use_theme) { const Eina_List *l; Evas_Object *obj; if (!use_theme) { EINA_LIST_FOREACH(_elm_win_list, l, obj) elm_widget_theme(obj); } else { EINA_LIST_FOREACH(_elm_win_list, l, obj) elm_widget_theme_specific(obj, th, EINA_FALSE); } } void _elm_win_access(Eina_Bool is_access) { Evas *evas; const Eina_List *l; Evas_Object *obj; Evas_Object *fobj; EINA_LIST_FOREACH(_elm_win_list, l, obj) { elm_widget_access(obj, is_access); /* floating orphan object. if there are A, B, C objects and user does as below, then there would be floating orphan objects. 1. elm_object_content_set(layout, A); 2. elm_object_content_set(layout, B); 3. elm_object_content_set(layout, C); now, the object A and B are floating orphan objects */ fobj = obj; for (;;) { fobj = evas_object_below_get(fobj); if (!fobj) break; if (elm_widget_is(fobj) && !elm_widget_parent_get(fobj)) { elm_widget_access(fobj, is_access); } } if (!is_access) { evas = evas_object_evas_get(obj); if (evas) _elm_access_object_highlight_disable(evas); } } } void _elm_win_translate(void) { const Eina_List *l; Evas_Object *obj; EINA_LIST_FOREACH(_elm_win_list, l, obj) efl_ui_translatable_translation_update(obj); } void _elm_win_focus_reconfigure(void) { /* FOCUS-FIXME */ } #ifdef HAVE_ELEMENTARY_X static Eina_Bool _elm_win_client_message(void *data, int type EINA_UNUSED, void *event) { ELM_WIN_DATA_GET(data, sd); Ecore_X_Event_Client_Message *e = event; if (e->format != 32) return ECORE_CALLBACK_PASS_ON; if (e->message_type == ECORE_X_ATOM_E_COMP_FLUSH) { if ((unsigned int)e->data.l[0] == sd->x.xwin) { Evas *evas = evas_object_evas_get(sd->obj); if (evas) { edje_file_cache_flush(); edje_collection_cache_flush(); evas_image_cache_flush(evas); evas_font_cache_flush(evas); } } } else if (e->message_type == ECORE_X_ATOM_E_COMP_DUMP) { if ((unsigned int)e->data.l[0] == sd->x.xwin) { Evas *evas = evas_object_evas_get(sd->obj); if (evas) { edje_file_cache_flush(); edje_collection_cache_flush(); evas_image_cache_flush(evas); evas_font_cache_flush(evas); evas_render_dump(evas); } } } else if (e->message_type == ECORE_X_ATOM_E_ILLUME_ACCESS_CONTROL) { if ((unsigned int)e->data.l[0] == sd->x.xwin) { if ((unsigned int)e->data.l[1] == ECORE_X_ATOM_E_ILLUME_ACCESS_ACTION_NEXT) { // XXX: call right access func } else if ((unsigned int)e->data.l[1] == ECORE_X_ATOM_E_ILLUME_ACCESS_ACTION_PREV) { // XXX: call right access func } else if ((unsigned int)e->data.l[1] == ECORE_X_ATOM_E_ILLUME_ACCESS_ACTION_ACTIVATE) { _elm_access_highlight_object_activate (sd->obj, EFL_UI_ACTIVATE_DEFAULT); } else if ((unsigned int)e->data.l[1] == ECORE_X_ATOM_E_ILLUME_ACCESS_ACTION_READ) { /* there would be better way to read highlight object */ Evas *evas; evas = evas_object_evas_get(sd->obj); if (!evas) return ECORE_CALLBACK_PASS_ON; _elm_access_mouse_event_enabled_set(EINA_TRUE); evas_event_feed_mouse_in(evas, 0, NULL); evas_event_feed_mouse_move (evas, e->data.l[2], e->data.l[3], 0, NULL); _elm_access_mouse_event_enabled_set(EINA_FALSE); } else if ((unsigned int)e->data.l[1] == ECORE_X_ATOM_E_ILLUME_ACCESS_ACTION_READ_NEXT) { _elm_access_highlight_cycle(sd->obj, ELM_FOCUS_NEXT); } else if ((unsigned int)e->data.l[1] == ECORE_X_ATOM_E_ILLUME_ACCESS_ACTION_READ_PREV) { _elm_access_highlight_cycle(sd->obj, ELM_FOCUS_PREVIOUS); } else if ((unsigned int)e->data.l[1] == ECORE_X_ATOM_E_ILLUME_ACCESS_ACTION_UP) { _elm_access_highlight_object_activate (sd->obj, EFL_UI_ACTIVATE_UP); } else if ((unsigned int)e->data.l[1] == ECORE_X_ATOM_E_ILLUME_ACCESS_ACTION_DOWN) { _elm_access_highlight_object_activate (sd->obj, EFL_UI_ACTIVATE_DOWN); } } } return ECORE_CALLBACK_PASS_ON; } static Eina_Bool _elm_win_property_change(void *data, int type EINA_UNUSED, void *event) { ELM_WIN_DATA_GET(data, sd); Ecore_X_Event_Window_Property *e = event; if (e->atom == ECORE_X_ATOM_E_ILLUME_INDICATOR_STATE) { if (e->win == sd->x.xwin) { sd->legacy.indmode = (Elm_Win_Indicator_Mode)ecore_x_e_illume_indicator_state_get(e->win); efl_event_callback_legacy_call (sd->obj, EFL_UI_WIN_EVENT_INDICATOR_PROP_CHANGED, NULL); } } return ECORE_CALLBACK_PASS_ON; } #endif #ifdef HAVE_ELEMENTARY_WIN32 static Eina_Bool _elm_win_key_down(void *data, int type EINA_UNUSED, void *event) { ELM_WIN_DATA_GET(data, sd); Ecore_Event_Key *e = event; if ((e->modifiers & ECORE_EVENT_MODIFIER_ALT) && (strcmp(e->key, "F4") == 0)) _elm_win_delete_request(sd->ee); return ECORE_CALLBACK_PASS_ON; } #endif static void _elm_win_focus_highlight_hide(void *data EINA_UNUSED, Evas_Object *obj, const char *emission EINA_UNUSED, const char *source EINA_UNUSED) { evas_object_hide(obj); } static void _elm_win_focus_highlight_anim_end(void *data, Evas_Object *obj, const char *emission EINA_UNUSED, const char *source EINA_UNUSED) { ELM_WIN_DATA_GET(data, sd); _elm_win_focus_highlight_simple_setup(sd, obj); } static void _elm_win_focus_highlight_init(Efl_Ui_Win_Data *sd) { evas_event_callback_add(sd->evas, EVAS_CALLBACK_CANVAS_OBJECT_FOCUS_IN, _elm_win_object_focus_in, sd->obj); evas_event_callback_add(sd->evas, EVAS_CALLBACK_CANVAS_OBJECT_FOCUS_OUT, _elm_win_object_focus_out, sd->obj); sd->focus_highlight.cur.target = _elm_win_focus_target_get(evas_focus_get(sd->evas)); if (sd->focus_highlight.cur.target) { if (elm_widget_highlight_in_theme_get(sd->focus_highlight.cur.target)) sd->focus_highlight.cur.in_theme = EINA_TRUE; else _elm_win_focus_target_callbacks_add(sd); evas_object_event_callback_add (sd->focus_highlight.cur.target, EVAS_CALLBACK_DEL, _elm_win_focus_target_del, sd->obj); } sd->focus_highlight.prev.target = NULL; sd->focus_highlight.theme_changed = EINA_TRUE; if (!sd->focus_highlight.fobj) { sd->focus_highlight.fobj = edje_object_add(sd->evas); edje_object_signal_callback_add(sd->focus_highlight.fobj, "elm,action,focus,hide,end", "*", _elm_win_focus_highlight_hide, NULL); edje_object_signal_callback_add(sd->focus_highlight.fobj, "elm,action,focus,anim,end", "*", _elm_win_focus_highlight_anim_end, sd->obj); } _elm_win_focus_highlight_reconfigure_job_start(sd); } typedef struct _resize_info { const char *source; const char *cursor; Efl_Ui_Win_Move_Resize_Mode mode; int wl_location; #ifdef HAVE_ELEMENTARY_X #define XDIR(d) , ECORE_X_NETWM_DIRECTION_##d Ecore_X_Netwm_Direction x_dir; #else # define XDIR(d) #endif } resize_info; static const resize_info _resize_infos[8] = { { "elm.event.resize.t", ELM_CURSOR_TOP_SIDE, EFL_UI_WIN_MOVE_RESIZE_TOP, 1 XDIR(SIZE_T) }, { "elm.event.resize.b", ELM_CURSOR_BOTTOM_SIDE, EFL_UI_WIN_MOVE_RESIZE_BOTTOM, 2 XDIR(SIZE_B) }, { "elm.event.resize.l", ELM_CURSOR_LEFT_SIDE, EFL_UI_WIN_MOVE_RESIZE_LEFT, 4 XDIR(SIZE_L) }, { "elm.event.resize.r", ELM_CURSOR_RIGHT_SIDE, EFL_UI_WIN_MOVE_RESIZE_RIGHT, 8 XDIR(SIZE_R) }, { "elm.event.resize.tl", ELM_CURSOR_TOP_LEFT_CORNER, EFL_UI_WIN_MOVE_RESIZE_TOP | EFL_UI_WIN_MOVE_RESIZE_LEFT, 5 XDIR(SIZE_TL) }, { "elm.event.resize.bl", ELM_CURSOR_BOTTOM_LEFT_CORNER, EFL_UI_WIN_MOVE_RESIZE_BOTTOM | EFL_UI_WIN_MOVE_RESIZE_LEFT, 6 XDIR(SIZE_BL) }, { "elm.event.resize.br", ELM_CURSOR_BOTTOM_RIGHT_CORNER, EFL_UI_WIN_MOVE_RESIZE_BOTTOM | EFL_UI_WIN_MOVE_RESIZE_RIGHT, 10 XDIR(SIZE_BR) }, { "elm.event.resize.tr", ELM_CURSOR_TOP_RIGHT_CORNER, EFL_UI_WIN_MOVE_RESIZE_TOP | EFL_UI_WIN_MOVE_RESIZE_RIGHT, 9 XDIR(SIZE_TR) }, }; static inline Efl_Ui_Win_Move_Resize_Mode _move_resize_mode_rotate(int rotation, Efl_Ui_Win_Move_Resize_Mode mode) { const Efl_Ui_Win_Move_Resize_Mode edges[4] = { EFL_UI_WIN_MOVE_RESIZE_TOP, EFL_UI_WIN_MOVE_RESIZE_LEFT, EFL_UI_WIN_MOVE_RESIZE_BOTTOM, EFL_UI_WIN_MOVE_RESIZE_RIGHT }; const Efl_Ui_Win_Move_Resize_Mode corners[4] = { EFL_UI_WIN_MOVE_RESIZE_TOP | EFL_UI_WIN_MOVE_RESIZE_LEFT, EFL_UI_WIN_MOVE_RESIZE_BOTTOM | EFL_UI_WIN_MOVE_RESIZE_LEFT, EFL_UI_WIN_MOVE_RESIZE_BOTTOM | EFL_UI_WIN_MOVE_RESIZE_RIGHT, EFL_UI_WIN_MOVE_RESIZE_TOP | EFL_UI_WIN_MOVE_RESIZE_RIGHT, }; const int i = rotation / 90; int k; for (k = 0; k < 4; k++) if (mode == edges[k]) return edges[(k + i) % 4]; for (k = 0; k < 4; k++) if (mode == corners[k]) return corners[(k + i) % 4]; return EFL_UI_WIN_MOVE_RESIZE_MOVE; } static const resize_info * _resize_info_get(int rotation, Efl_Ui_Win_Move_Resize_Mode mode) { if (rotation) return _resize_info_get(0, _move_resize_mode_rotate(rotation, mode)); for (size_t k = 0; k < EINA_C_ARRAY_LENGTH(_resize_infos); k++) { if (_resize_infos[k].mode == mode) return &_resize_infos[k]; } return NULL; } static Efl_Ui_Win_Move_Resize_Mode _move_resize_mode_get(const char *source) { for (size_t k = 0; k < EINA_C_ARRAY_LENGTH(_resize_infos); k++) if (!strcmp(source, _resize_infos[k].source)) return _resize_infos[k].mode; return EFL_UI_WIN_MOVE_RESIZE_MOVE; } static void _elm_win_frame_obj_move(void *data, Evas *e EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED) { Efl_Ui_Win_Data *sd; if (!(sd = data)) return; if (!sd->legacy.edje) return; _elm_win_frame_obj_update(sd); } static void _elm_win_frame_obj_resize(void *data, Evas *e EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED) { Efl_Ui_Win_Data *sd; if (!(sd = data)) return; if (!sd->legacy.edje) return; _elm_win_frame_obj_update(sd); } static void _elm_win_frame_cb_resize_show(void *data, Evas_Object *obj EINA_UNUSED, const char *sig EINA_UNUSED, const char *source) { ELM_WIN_DATA_GET(data, sd); if (!sd) return; if (sd->resizing) return; #ifdef HAVE_ELEMENTARY_WL2 if (sd->pointer.obj) { Efl_Ui_Win_Move_Resize_Mode mode = _move_resize_mode_get(source); const resize_info *ri = _resize_info_get(sd->rot, mode); if (ri) elm_widget_theme_object_set(sd->obj, sd->pointer.obj, "pointer", "base", ri->cursor); } #else (void)source; #endif } static void _elm_win_frame_cb_resize_hide(void *data, Evas_Object *obj EINA_UNUSED, const char *sig EINA_UNUSED, const char *source EINA_UNUSED) { ELM_WIN_DATA_GET(data, sd); if (!sd) return; if (sd->resizing) return; #ifdef HAVE_ELEMENTARY_WL2 if (sd->pointer.obj) elm_widget_theme_object_set(sd->obj, sd->pointer.obj, "pointer", "base", "default"); #endif } static inline Eina_Bool _win_move_start(Efl_Ui_Win_Data *sd) { #ifdef HAVE_ELEMENTARY_WL2 if (sd->wl.win) { /* Note: Not passing any X,Y position as those don't make sense, only * the compositor can really handle the window & pointer position. */ ecore_evas_wayland_move(sd->ee, 0, 0); return EINA_TRUE; } #endif #ifdef HAVE_ELEMENTARY_X if (sd->x.xwin) { int x, y; sd->resizing = EINA_TRUE; ecore_x_pointer_ungrab(); ecore_x_pointer_root_xy_get(&x, &y); ecore_x_netwm_moveresize_request_send(sd->x.xwin, x, y, ECORE_X_NETWM_DIRECTION_MOVE, 1); return EINA_TRUE; } #endif INF("Window move request not supported for this window!"); return EINA_FALSE; } static Eina_Bool _win_move_resize_start(Efl_Ui_Win_Data *sd, Efl_Ui_Win_Move_Resize_Mode mode) { EINA_SAFETY_ON_NULL_RETURN_VAL(sd, EINA_FALSE); const resize_info *ri; // 2. check move_resize already started if (sd->resizing) { ERR("Window is already being resized."); return EINA_FALSE; } if (mode == EFL_UI_WIN_MOVE_RESIZE_MOVE) return _win_move_start(sd); ri = _resize_info_get(sd->rot, mode); if (!ri) { ERR("Unsupported move_resize mode %#x", (int) mode); return EINA_FALSE; } sd->resizing = EINA_TRUE; #ifdef HAVE_ELEMENTARY_WL2 if (sd->wl.win) { ecore_evas_wayland_resize(sd->ee, ri->wl_location); return EINA_TRUE; } #endif #ifdef HAVE_ELEMENTARY_X if (sd->x.xwin) { int x, y; ecore_x_pointer_ungrab(); ecore_x_pointer_root_xy_get(&x, &y); ecore_x_netwm_moveresize_request_send(sd->x.xwin, x, y, ri->x_dir, 1); return EINA_TRUE; } #endif INF("Window resize request not supported for this window!"); return EINA_FALSE; } static void _elm_win_frame_cb_move_start(void *data, Evas_Object *obj EINA_UNUSED, const char *sig EINA_UNUSED, const char *source EINA_UNUSED) { ELM_WIN_DATA_GET_OR_RETURN(data, sd); _win_move_resize_start(sd, EFL_UI_WIN_MOVE_RESIZE_MOVE); } static void _elm_win_frame_cb_move_stop(void *data, Evas_Object *obj EINA_UNUSED, const char *sig EINA_UNUSED, const char *source EINA_UNUSED) { ELM_WIN_DATA_GET_OR_RETURN(data, sd); #ifdef HAVE_ELEMENTARY_WL2 _elm_win_wl_cursor_set(sd->obj, NULL); #endif } static void _elm_win_frame_cb_resize_start(void *data, Evas_Object *obj EINA_UNUSED, const char *sig EINA_UNUSED, const char *source) { ELM_WIN_DATA_GET_OR_RETURN(data, sd); Efl_Ui_Win_Move_Resize_Mode mode; mode = _move_resize_mode_get(source); if (mode == EFL_UI_WIN_MOVE_RESIZE_MOVE) return; _win_move_resize_start(sd, mode); } static void _elm_win_frame_cb_minimize(void *data, Evas_Object *obj EINA_UNUSED, const char *sig EINA_UNUSED, const char *source EINA_UNUSED) { ELM_WIN_DATA_GET(data, sd); if (!sd) return; // sd->iconified = EINA_TRUE; TRAP(sd, iconified_set, EINA_TRUE); } static void _elm_win_frame_cb_maximize(void *data, Evas_Object *obj EINA_UNUSED, const char *sig EINA_UNUSED, const char *source EINA_UNUSED) { Eina_Bool value; ELM_WIN_DATA_GET(data, sd); if (!sd) return; if (sd->maximized) value = EINA_FALSE; else value = EINA_TRUE; efl_ui_win_maximized_set(sd->obj, value); } static void _elm_win_frame_cb_menu(void *data, Evas_Object *obj EINA_UNUSED, const char *sig EINA_UNUSED, const char *source EINA_UNUSED) { ELM_WIN_DATA_GET(data, sd); #ifdef HAVE_ELEMENTARY_WL2 Ecore_Wl2_Input *input; int x, y, wx, wy; if (!sd->wl.win) return; evas_pointer_canvas_xy_get(sd->evas, &x, &y); ecore_wl2_window_geometry_get(sd->wl.win, &wx, &wy, NULL, NULL); if (x < 0) x += wx; if (y < 0) y += wy; { Eina_Iterator *it; Ecore_Wl2_Display *display = ecore_wl2_window_display_get(sd->wl.win); it = ecore_wl2_display_inputs_get(display); EINA_ITERATOR_FOREACH(it, input) break; eina_iterator_free(it); } if (sd->wl.win->xdg_toplevel) { xdg_toplevel_show_window_menu(sd->wl.win->xdg_toplevel, ecore_wl2_input_seat_get(input), 0, x, y); ecore_wl2_display_flush(input->display); } else if (sd->wl.win->zxdg_toplevel) { zxdg_toplevel_v6_show_window_menu(sd->wl.win->zxdg_toplevel, ecore_wl2_input_seat_get(input), 0, x, y); ecore_wl2_display_flush(input->display); } #else (void)sd; #endif } static void _elm_win_frame_cb_close(void *data, Evas_Object *obj EINA_UNUSED, const char *sig EINA_UNUSED, const char *source EINA_UNUSED) { ELM_WIN_DATA_GET(data, sd); Evas_Object *win; /* FIXME: After the current freeze, this should be handled differently. * * Ideally, we would want to mimic the X11 backend and use something * like ECORE_WL_EVENT_WINDOW_DELETE and handle the delete_request * inside of ecore_evas. That would be the 'proper' way, but since we are * in a freeze right now, I cannot add a new event value, or a new * event structure to ecore_wayland. * * So yes, this is a temporary 'stop-gap' solution which will be fixed * when the freeze is over, but it does fix a trac bug for now, and in a * way which does not break API or the freeze. - dh */ if (!sd) return; win = sd->obj; int autodel = sd->autodel; sd->autodel_clear = &autodel; evas_object_ref(win); efl_event_callback_legacy_call(win, EFL_UI_WIN_EVENT_DELETE_REQUEST, NULL); if (sd->autohide) evas_object_hide(win); // FIXME: if above callback deletes - then the below will be invalid if (autodel) evas_object_del(win); else sd->autodel_clear = NULL; evas_object_unref(win); } #ifdef HAVE_ELEMENTARY_WL2 static Eina_Bool _elm_win_wl_configure(void *data, int t EINA_UNUSED, void *event) { Ecore_Wl2_Event_Window_Configure *ev = event; ELM_WIN_DATA_GET(data, sd); if (ecore_wl2_window_id_get(sd->wl.win) != (int)ev->win) return ECORE_CALLBACK_RENEW; if (sd->resizing && (!ev->edges)) sd->resizing = EINA_FALSE; return ECORE_CALLBACK_RENEW; } static void _elm_win_frame_pre_render(void *data, Evas *e EINA_UNUSED, void *ev EINA_UNUSED) { Efl_Ui_Win_Data *sd = data; if (sd->wl.opaque_dirty) _elm_win_opaque_update(sd, EINA_FALSE); sd->wl.opaque_dirty = 0; } #endif static inline void _elm_object_part_cursor_set(Evas_Object *obj, Evas_Object *edj, const char *part, const char *cursor) { Evas_Object *sub = (Evas_Object *) edje_object_part_object_get(edj, part); if (!sub) return; elm_object_sub_cursor_set(sub, obj, cursor); } static char * _efl_system_theme_path_get(void) { // Find the default theme from EFL install. Quite ugly. const char *sysdir; char *version; char path[PATH_MAX]; int v; sysdir = elm_theme_system_dir_get(); if (!sysdir) return NULL; eina_file_path_join(path, PATH_MAX, sysdir, "default.edj"); version = edje_file_data_get(path, "version"); v = version ? atoi(version) : 0; free(version); if (v < FRAME_OBJ_THEME_MIN_VERSION) { ERR("Default system theme is too old, something is wrong with your installation of EFL."); return NULL; } return strdup(path); } static void _elm_win_frame_add(Efl_Ui_Win_Data *sd, const char *element, const char *style) { Evas_Object *obj = sd->obj; int w, h, mw, mh, v; const char *version; if (sd->frame_obj) return; sd->frame_obj = edje_object_add(sd->evas); // Verify theme version. Border requires an exact theme API. version = elm_theme_data_get(elm_widget_theme_get(sd->obj), "version"); v = version ? atoi(version) : 0; if (EINA_LIKELY(v >= FRAME_OBJ_THEME_MIN_VERSION)) { if (!elm_widget_theme_object_set (sd->obj, sd->frame_obj, "border", element, style)) { ERR("Failed to set main border theme for the window."); ELM_SAFE_FREE(sd->frame_obj, evas_object_del); return; } // Verify border.edc version as well version = edje_object_data_get(sd->frame_obj, "version"); v = version ? atoi(version) : 0; } if (v < FRAME_OBJ_THEME_MIN_VERSION) { // Theme compatibility const char *key = "elm/border/base/default"; // FIXME? char *sys_theme; WRN("Selected theme does not support the required border theme API " "(version = %d, requires >= %d).", v, FRAME_OBJ_THEME_MIN_VERSION); sys_theme = _efl_system_theme_path_get(); if (!sys_theme || !edje_object_file_set(sd->frame_obj, sys_theme, key)) { ERR("Failed to set main border theme for the window."); ELM_SAFE_FREE(sd->frame_obj, evas_object_del); free(sys_theme); return; } free(sys_theme); } /* Small hack: The special value 2 means this is the top frame object. * We propagate to the children now (the edc group contents), but subsequent * calls to smart_member_add will not propagate the flag further. Note that * this little hack will fall apart if edje creates and destroys objects on * the fly. */ efl_canvas_object_is_frame_object_set(sd->frame_obj, 2); edje_object_part_swallow(sd->frame_obj, "elm.swallow.client", sd->legacy.edje); if (sd->icon) evas_object_show(sd->icon); else { Eina_Bool set = EINA_FALSE; sd->icon = elm_icon_add(sd->obj); if (sd->icon_name) set = elm_icon_standard_set(sd->icon, sd->icon_name); if (((!sd->icon_name) || (!set)) && _elm_appname) { Efreet_Desktop *d; d = efreet_util_desktop_exec_find(_elm_appname); if (d) { elm_icon_standard_set(sd->icon, d->icon); efreet_desktop_free(d); } } efl_access_type_set(sd->icon, EFL_ACCESS_TYPE_DISABLED); } edje_object_part_swallow(sd->frame_obj, "elm.swallow.icon", sd->icon); efl_canvas_object_is_frame_object_set(sd->icon, EINA_TRUE); evas_object_event_callback_add (sd->frame_obj, EVAS_CALLBACK_MOVE, _elm_win_frame_obj_move, sd); evas_object_event_callback_add (sd->frame_obj, EVAS_CALLBACK_RESIZE, _elm_win_frame_obj_resize, sd); _elm_win_frame_style_update(sd, 1, 0); /* NB: Do NOT remove these calls !! Needed to calculate proper * framespace on initial show of the window */ edje_object_size_min_calc(sd->frame_obj, &mw, &mh); evas_object_move(sd->frame_obj, 0, 0); evas_object_resize(sd->frame_obj, mw, mh); evas_object_smart_calculate(sd->frame_obj); edje_object_signal_callback_add (sd->frame_obj, "elm,action,move,start", "elm", _elm_win_frame_cb_move_start, obj); edje_object_signal_callback_add (sd->frame_obj, "elm,action,move,stop", "elm", _elm_win_frame_cb_move_stop, obj); edje_object_signal_callback_add (sd->frame_obj, "elm,action,resize,show", "*", _elm_win_frame_cb_resize_show, obj); edje_object_signal_callback_add (sd->frame_obj, "elm,action,resize,hide", "*", _elm_win_frame_cb_resize_hide, obj); edje_object_signal_callback_add (sd->frame_obj, "elm,action,resize,start", "*", _elm_win_frame_cb_resize_start, obj); edje_object_signal_callback_add (sd->frame_obj, "elm,action,minimize", "elm", _elm_win_frame_cb_minimize, obj); edje_object_signal_callback_add (sd->frame_obj, "elm,action,maximize", "elm", _elm_win_frame_cb_maximize, obj); edje_object_signal_callback_add (sd->frame_obj, "elm,action,close", "elm", _elm_win_frame_cb_close, obj); edje_object_signal_callback_add (sd->frame_obj, "elm,action,menu", "elm", _elm_win_frame_cb_menu, obj); if (!sd->pointer.obj) { for (size_t k = 0; k < EINA_C_ARRAY_LENGTH(_resize_infos); k++) { const resize_info *ri = &_resize_infos[k]; _elm_object_part_cursor_set(obj, sd->frame_obj, ri->source, ri->cursor); } } if (sd->title) { edje_object_part_text_escaped_set (sd->frame_obj, "elm.text.title", sd->title); } { // HACK: Force render mode of bg rect to COPY. This avoids render garbage. Eo *bgrect = (Eo *) edje_object_part_object_get(sd->frame_obj, "elm.rect.background"); efl_canvas_object_render_op_set(bgrect, EFL_GFX_RENDER_OP_COPY); } _elm_win_frame_style_update(sd, 1, 1); _elm_win_frame_geometry_adjust(sd); ecore_evas_geometry_get(sd->ee, NULL, NULL, &w, &h); if ((w > 1) && (h > 1)) ecore_evas_resize(sd->ee, w, h); } static void _elm_win_frame_style_update(Efl_Ui_Win_Data *sd, Eina_Bool force_emit, Eina_Bool calc) { Eina_Bool borderless, maximized, shadow, focus, bg_solid, menu, unresizable, alpha, bg_standard, indicator; Eina_Bool changed = EINA_FALSE; if (!sd->frame_obj) { if (!efl_finalized_get(sd->obj)) return; if (EINA_LIKELY(sd->type == ELM_WIN_FAKE)) { _elm_win_opaque_update(sd, 0); return; } CRI("Window has no frame object!"); return; } if ((sd->type == ELM_WIN_INLINED_IMAGE) || (sd->type == ELM_WIN_SOCKET_IMAGE) || (sd->type == ELM_WIN_TOOLTIP) || (sd->type == ELM_WIN_COMBO) || (sd->type == ELM_WIN_MENU) || (sd->type == ELM_WIN_POPUP_MENU)) { sd->csd.need_shadow = EINA_FALSE; sd->csd.need_borderless = EINA_TRUE; sd->csd.need_unresizable = EINA_TRUE; sd->csd.need_menu = EINA_FALSE; sd->csd.need_indicator = EINA_FALSE; } else { sd->csd.need_shadow = sd->csd.need && (!sd->maximized); } alpha = sd->application_alpha || sd->theme_alpha; borderless = sd->csd.need_borderless || (!sd->csd.need) || sd->fullscreen; maximized = sd->maximized; shadow = sd->csd.need_shadow && (!sd->fullscreen) && (!sd->maximized) && (!borderless); if (alpha && borderless) shadow = 0; #ifdef HAVE_ELEMENTARY_WL2 if (sd->wl.win) focus = ecore_wl2_window_activated_get(sd->wl.win); else #endif focus = ecore_evas_focus_get(sd->ee); bg_solid = sd->csd.need_bg_solid; bg_standard = sd->csd.need_bg_standard; unresizable = sd->csd.need_unresizable; menu = sd->csd.need_menu; indicator = sd->csd.need_indicator; /* FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME * At the moment, E Wayland uses SSD for its internal windows. Which means * we must hide the shadow if the borderless flag is set. "trap" here means * we are likely to be running inside E compositor. * FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME */ if (trap && sd->csd.wayland && sd->csd.need_borderless) shadow = 0; #define STATE_SET(state, s1, s2) do { \ if (force_emit || (state != sd->csd.cur_##state)) \ { \ const char *sig = state ? s1 : s2; \ edje_object_signal_emit(sd->frame_obj, sig, "elm"); \ DBG("frame style emit: %p %s", sd->obj, sig); \ sd->csd.cur_##state = state; \ changed = EINA_TRUE; \ } } while (0) STATE_SET(borderless, "elm,state,borderless,on", "elm,state,borderless,off"); STATE_SET(shadow, "elm,state,shadow,on", "elm,state,shadow,off"); STATE_SET(maximized, "elm,state,maximized", "elm,state,unmaximized"); STATE_SET(focus, "elm,action,focus", "elm,action,unfocus"); STATE_SET(bg_solid, "elm,state,background,solid,on", "elm,state,background,solid,off"); STATE_SET(bg_standard, "elm,state,background,standard,on", "elm,state,background,standard,off"); STATE_SET(unresizable, "elm,state,unresizable,on", "elm,state,unresizable,off"); STATE_SET(menu, "elm,action,show_menu", "elm,action,hide_menu"); STATE_SET(indicator, "elm,action,show_indicator", "elm,action,hide_indicator"); #undef STATE_SET if (changed) { if (calc || force_emit) edje_object_message_signal_process(sd->frame_obj); if (calc) evas_object_smart_calculate(sd->frame_obj); _elm_win_frame_obj_update(sd); _elm_win_opaque_update(sd, EINA_FALSE); } } #ifdef ELM_DEBUG static void _debug_key_down(void *data EINA_UNUSED, Evas *e EINA_UNUSED, Evas_Object *obj, void *event_info) { Evas_Event_Key_Down *ev = event_info; if (ev->event_flags & EVAS_EVENT_FLAG_ON_HOLD) return; if ((strcmp(ev->key, "F12")) || (!evas_key_modifier_is_set(ev->modifiers, "Control"))) return; INF("Tree graph generated."); elm_object_tree_dot_dump(obj, "./dump.dot"); } #endif static void _win_inlined_image_set(Efl_Ui_Win_Data *sd) { evas_object_image_alpha_set(sd->img_obj, EINA_FALSE); evas_object_image_filled_set(sd->img_obj, EINA_TRUE); evas_object_event_callback_add (sd->img_obj, EVAS_CALLBACK_DEL, _elm_win_on_img_obj_del, sd->obj); evas_object_event_callback_add (sd->img_obj, EVAS_CALLBACK_HIDE, _win_img_hide, sd->obj); evas_object_event_callback_add (sd->img_obj, EVAS_CALLBACK_MOUSE_UP, _win_img_mouse_up, sd->obj); evas_object_event_callback_add (sd->img_obj, EVAS_CALLBACK_FOCUS_IN, _win_img_focus_in, sd->obj); evas_object_event_callback_add (sd->img_obj, EVAS_CALLBACK_FOCUS_OUT, _win_img_focus_out, sd->obj); } static void _elm_win_on_icon_del(void *data, const Efl_Event *ev) { ELM_WIN_DATA_GET(data, sd); if (sd->icon == ev->object) sd->icon = NULL; } EOLIAN static void _efl_ui_win_efl_canvas_group_group_add(Eo *obj, Efl_Ui_Win_Data *_pd EINA_UNUSED) { efl_canvas_group_add(efl_super(obj, MY_CLASS)); elm_widget_theme_klass_set(obj, "win"); elm_widget_can_focus_set(obj, EINA_TRUE); elm_widget_highlight_ignore_set(obj, EINA_TRUE); } #ifdef HAVE_ELEMENTARY_X static void _elm_x_io_err(void *data EINA_UNUSED) { Eina_List *l; Evas_Object *obj; EINA_LIST_FOREACH(_elm_win_list, l, obj) evas_object_smart_callback_call(obj, "ioerr", NULL); elm_exit(); } #endif static void _elm_win_cb_hide(void *data EINA_UNUSED, Evas *e EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED) { _elm_win_state_eval_queue(); } static void _elm_win_cb_show(void *data EINA_UNUSED, Evas *e EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED) { _elm_win_state_eval_queue(); } static inline const char * _efl_ui_win_accel(Efl_Ui_Win_Data *sd) { const char *str = sd->accel_pref; const char *env; /* current elm config */ if (!str) { if (_elm_config->accel) str = _elm_config->accel; if (_elm_accel_preference) str = _elm_accel_preference; } /* global overrides */ if ((_elm_config->accel_override) && (_elm_config->accel)) str = _elm_config->accel; /* env var wins */ env = getenv("ELM_ACCEL"); if (env) str = env; return str; } static inline void _elm_win_need_frame_adjust(Efl_Ui_Win_Data *sd, const char *engine) { const char *s; /* this is for debug only - don't keep forever, it's not an api! */ s = getenv("EFL_WIN_FRAME_MODE"); sd->csd.wayland = (eina_streq(engine, ELM_WAYLAND_SHM) || eina_streq(engine, ELM_WAYLAND_EGL)); if (sd->type == ELM_WIN_FAKE) sd->csd.need = EINA_FALSE; else if (eina_streq(s, "on")) sd->csd.need = EINA_TRUE; else if (eina_streq(s, "off")) sd->csd.need = EINA_FALSE; else sd->csd.need = sd->csd.wayland; } static void _indicator_resized(void *data, const Efl_Event *event) { ELM_WIN_DATA_GET_OR_RETURN(data, sd); Evas_Object *indicator = event->object; Evas_Coord_Size *size = (Evas_Coord_Size *)event->info; efl_gfx_size_hint_restricted_min_set(indicator, EINA_SIZE2D(size->w, size->h)); _elm_win_frame_obj_update(sd); } static Evas_Object* _create_indicator(Evas_Object *obj) { Evas_Object *indicator = NULL; const char *indicator_serv_name; indicator_serv_name = "elm_indicator_portrait"; if (!indicator_serv_name) { ERR("Conformant cannot get portrait indicator service name"); return NULL; } indicator = elm_plug_add(obj); if (!indicator) { ERR("Conformant cannot create plug to server[%s]", indicator_serv_name); return NULL; } if (!elm_plug_connect(indicator, indicator_serv_name, 0, EINA_FALSE)) { ERR("Conformant cannot connect to server[%s]", indicator_serv_name); return NULL; } return indicator; } static void _indicator_add(Efl_Ui_Win_Data *sd) { Eo *obj = sd->obj; sd->indicator = _create_indicator(obj); if ((!sd->indicator) || (!edje_object_part_swallow(sd->frame_obj, "elm.swallow.indicator", sd->indicator))) return; efl_event_callback_add (sd->indicator, ELM_PLUG_EVENT_IMAGE_RESIZED, _indicator_resized, obj); efl_canvas_object_is_frame_object_set(sd->indicator, EINA_TRUE); sd->csd.need_indicator = EINA_TRUE; _elm_win_frame_style_update(sd, 0, 1); } static void _indicator_del(Efl_Ui_Win_Data *sd) { Eo *obj = sd->obj; efl_event_callback_del (sd->indicator, ELM_PLUG_EVENT_IMAGE_RESIZED, _indicator_resized, obj); efl_del(sd->indicator); sd->indicator = NULL; sd->csd.need_indicator = EINA_FALSE; _elm_win_frame_style_update(sd, 0, 1); } static Eina_Value _win_finalize_job_cb(void *data, const Eina_Value value) { Evas *eo_e = evas_object_evas_get(data); evas_render_pending_objects_flush(eo_e); return value; } static Eo * _elm_win_finalize_internal(Eo *obj, Efl_Ui_Win_Data *sd, const char *name, Efl_Ui_Win_Type type) { Evas_Object *parent = NULL; Evas *e; const Eina_List *l; const char *fontpath, *engine = NULL, *enginelist[32], *disp; int gl_depth = _elm_config->gl_depth; int gl_stencil = _elm_config->gl_stencil; int gl_msaa = _elm_config->gl_msaa; Eina_Stringshare *accel = NULL; Eina_Bool is_gl_accel; int i, p = 0; int parent_id = 0; Efl_Ui_Win_Data tmp_sd; if ((sd->type == ELM_WIN_INLINED_IMAGE) && !efl_isa(obj, EFL_UI_WIN_INLINED_CLASS)) { ERR("Failed to create an inlined window!"); return NULL; } parent = efl_parent_get(obj); /* just to store some data while trying out to create a canvas */ memset(&tmp_sd, 0, sizeof(Efl_Ui_Win_Data)); is_gl_accel = _elm_config_accel_preference_parse (_efl_ui_win_accel(sd), &accel, &gl_depth, &gl_stencil, &gl_msaa); switch ((int) type) { case ELM_WIN_FAKE: tmp_sd.ee = sd->ee; break; case ELM_WIN_INLINED_IMAGE: if (!parent) break; { Ecore_Evas *ee; e = evas_object_evas_get(parent); if (!e) break; ee = ecore_evas_ecore_evas_get(e); if (!ee) break; tmp_sd.img_obj = ecore_evas_object_image_new(ee); if (!tmp_sd.img_obj) break; tmp_sd.ee = ecore_evas_object_ecore_evas_get(tmp_sd.img_obj); if (!tmp_sd.ee) { ELM_SAFE_FREE(tmp_sd.img_obj, evas_object_del); } } break; case ELM_WIN_SOCKET_IMAGE: tmp_sd.ee = ecore_evas_extn_socket_new(1, 1); break; default: disp = getenv("ELM_DISPLAY"); if ((disp) && (!strcmp(disp, "ews"))) { enginelist[p++] = ELM_EWS; } else if ((disp) && (!strcmp(disp, "buffer"))) { enginelist[p++] = ELM_BUFFER; } else if ((disp) && (!strcmp(disp, "shot"))) { enginelist[p++] = ENGINE_GET(); } // welcome to ifdef hell! :) #ifdef HAVE_ELEMENTARY_X else if ((disp) && (!strcmp(disp, "x11"))) { if (is_gl_accel) { enginelist[p++] = ELM_OPENGL_X11; enginelist[p++] = ELM_SOFTWARE_X11; } else { enginelist[p++] = ELM_SOFTWARE_X11; enginelist[p++] = ELM_OPENGL_X11; } } #endif #ifdef HAVE_ELEMENTARY_WL2 else if ((disp) && (!strcmp(disp, "wl"))) { if (is_gl_accel) { enginelist[p++] = ELM_WAYLAND_EGL; enginelist[p++] = ELM_WAYLAND_SHM; } else { enginelist[p++] = ELM_WAYLAND_SHM; enginelist[p++] = ELM_WAYLAND_EGL; } } #endif #ifdef HAVE_ELEMENTARY_WIN32 else if ((disp) && (!strcmp(disp, "win"))) { enginelist[p++] = ELM_SOFTWARE_WIN32; enginelist[p++] = ELM_SOFTWARE_DDRAW; } #endif #ifdef HAVE_ELEMENTARY_SDL else if ((disp) && (!strcmp(disp, "sdl"))) { if (is_gl_accel) { enginelist[p++] = ELM_OPENGL_SDL; enginelist[p++] = ELM_SOFTWARE_SDL; } else { enginelist[p++] = ELM_SOFTWARE_SDL; enginelist[p++] = ELM_OPENGL_SDL; } } #endif #ifdef HAVE_ELEMENTARY_COCOA else if ((disp) && (!strcmp(disp, "mac"))) { enginelist[p++] = ELM_OPENGL_COCOA; } #endif #if defined(HAVE_ELEMENTARY_DRM) || defined(HAVE_ELEMENTARY_FB) else if ((disp) && (!strcmp(disp, "fb"))) { #ifdef HAVE_ELEMENTARY_DRM enginelist[p++] = ELM_DRM; #endif #ifdef HAVE_ELEMENTARY_FB enginelist[p++] = ELM_SOFTWARE_FB; #endif } #endif #ifdef HAVE_ELEMENTARY_X else if (!_elm_preferred_engine && getenv("DISPLAY") && !getenv("ELM_ENGINE")) { if (is_gl_accel) { enginelist[p++] = ELM_OPENGL_X11; enginelist[p++] = ELM_SOFTWARE_X11; } else { enginelist[p++] = ELM_SOFTWARE_X11; enginelist[p++] = ELM_OPENGL_X11; } } #endif #ifdef HAVE_ELEMENTARY_WL2 else if (!_elm_preferred_engine && getenv("WAYLAND_DISPLAY") && !getenv("ELM_ENGINE")) { if (is_gl_accel) { enginelist[p++] = ELM_WAYLAND_EGL; enginelist[p++] = ELM_WAYLAND_SHM; } else { enginelist[p++] = ELM_WAYLAND_SHM; enginelist[p++] = ELM_WAYLAND_EGL; } } #endif else { if (is_gl_accel) { // add all engines with selected engine first - if any if (ENGINE_GET()) enginelist[p++] = ENGINE_GET(); // add all engines with gl/accelerated ones first - only engines compiled #ifdef HAVE_ELEMENTARY_X enginelist[p++] = ELM_OPENGL_X11; #endif #ifdef HAVE_ELEMENTARY_WL2 enginelist[p++] = ELM_WAYLAND_EGL; #endif #ifdef HAVE_ELEMENTARY_DRM enginelist[p++] = ELM_DRM; #endif #ifdef HAVE_ELEMENTARY_FB enginelist[p++] = ELM_SOFTWARE_FB; #endif #ifdef HAVE_ELEMENTARY_COCOA enginelist[p++] = ELM_OPENGL_COCOA; #endif #ifdef HAVE_ELEMENTARY_SDL enginelist[p++] = ELM_OPENGL_SDL; #endif #ifdef HAVE_ELEMENTARY_X enginelist[p++] = ELM_SOFTWARE_X11; #endif #ifdef HAVE_ELEMENTARY_WL2 enginelist[p++] = ELM_WAYLAND_SHM; #endif #ifdef HAVE_ELEMENTARY_WIN32 enginelist[p++] = ELM_SOFTWARE_WIN32; enginelist[p++] = ELM_SOFTWARE_DDRAW; #endif #ifdef HAVE_ELEMENTARY_SDL enginelist[p++] = ELM_SOFTWARE_SDL; #endif } else { // add all engines with selected engine first - if any if (elm_config_preferred_engine_get()) enginelist[p++] = elm_config_preferred_engine_get(); // add check _elm_gl_preference whether "none" or not else if (_elm_config->engine && ((elm_config_accel_preference_get() && !strcmp(elm_config_accel_preference_get(), "none")) || (accel && !strcmp(accel, "none")))) enginelist[p++] = _elm_config->engine; // add all engines with gl/accelerated ones first - only engines compiled #ifdef HAVE_ELEMENTARY_X enginelist[p++] = ELM_SOFTWARE_X11; #endif #ifdef HAVE_ELEMENTARY_WL2 enginelist[p++] = ELM_WAYLAND_SHM; #endif #ifdef HAVE_ELEMENTARY_DRM enginelist[p++] = ELM_DRM; #endif #ifdef HAVE_ELEMENTARY_FB enginelist[p++] = ELM_SOFTWARE_FB; #endif #ifdef HAVE_ELEMENTARY_COCOA enginelist[p++] = ELM_OPENGL_COCOA; #endif #ifdef HAVE_ELEMENTARY_WIN32 enginelist[p++] = ELM_SOFTWARE_WIN32; enginelist[p++] = ELM_SOFTWARE_DDRAW; #endif #ifdef HAVE_ELEMENTARY_SDL enginelist[p++] = ELM_SOFTWARE_SDL; #endif #ifdef HAVE_ELEMENTARY_X enginelist[p++] = ELM_OPENGL_X11; #endif #ifdef HAVE_ELEMENTARY_WL2 enginelist[p++] = ELM_WAYLAND_EGL; #endif #ifdef HAVE_ELEMENTARY_DRM enginelist[p++] = ELM_DRM; #endif #ifdef HAVE_ELEMENTARY_SDL enginelist[p++] = ELM_OPENGL_SDL; #endif } } if (parent) parent_id = elm_win_window_id_get(parent); for (i = 0; i < p; i++) { if (!strcmp(enginelist[i], ELM_SOFTWARE_X11)) tmp_sd.ee = ecore_evas_software_x11_new(NULL, 0, 0, 0, 0, 0); else if (!strcmp(enginelist[i], ELM_OPENGL_X11)) { int opt[20], opt_i = 0; if (_elm_config->vsync) { opt[opt_i++] = ECORE_EVAS_GL_X11_OPT_VSYNC; opt[opt_i++] = 1; } if (gl_depth) { opt[opt_i++] = ECORE_EVAS_GL_X11_OPT_GL_DEPTH; opt[opt_i++] = gl_depth; } if (gl_stencil) { opt[opt_i++] = ECORE_EVAS_GL_X11_OPT_GL_STENCIL; opt[opt_i++] = gl_stencil; } if (gl_msaa) { opt[opt_i++] = ECORE_EVAS_GL_X11_OPT_GL_MSAA; opt[opt_i++] = gl_msaa; } opt[opt_i] = 0; if (opt_i > 0) tmp_sd.ee = ecore_evas_gl_x11_options_new(NULL, 0, 0, 0, 0, 0, opt); else tmp_sd.ee = ecore_evas_gl_x11_new(NULL, 0, 0, 0, 0, 0); } else if (!strcmp(enginelist[i], ELM_WAYLAND_SHM)) tmp_sd.ee = ecore_evas_wayland_shm_new(NULL, parent_id, 0, 0, 0, 0, 0); else if (!strcmp(enginelist[i], ELM_WAYLAND_EGL)) tmp_sd.ee = ecore_evas_wayland_egl_new(NULL, parent_id, 0, 0, 0, 0, 0); else if (!strcmp(enginelist[i], ELM_SOFTWARE_WIN32)) tmp_sd.ee = ecore_evas_software_gdi_new(NULL, 0, 0, 1, 1); else if (!strcmp(enginelist[i], ELM_SOFTWARE_DDRAW)) tmp_sd.ee = ecore_evas_software_ddraw_new(NULL, 0, 0, 1, 1); else if (!strcmp(enginelist[i], ELM_SOFTWARE_SDL)) tmp_sd.ee = ecore_evas_sdl_new(NULL, 0, 0, 0, 0, 0, 1); else if (!strcmp(enginelist[i], ELM_OPENGL_SDL)) tmp_sd.ee = ecore_evas_gl_sdl_new(NULL, 1, 1, 0, 0); else if (!strcmp(enginelist[i], ELM_OPENGL_COCOA)) tmp_sd.ee = ecore_evas_cocoa_new(NULL, 1, 1, 0, 0); else if (!strcmp(enginelist[i], ELM_EWS)) tmp_sd.ee = ecore_evas_ews_new(0, 0, 1, 1); else if (!strcmp(enginelist[i], ELM_SOFTWARE_FB)) tmp_sd.ee = ecore_evas_fb_new(NULL, 0, 1, 1); else if (!strcmp(enginelist[i], ELM_BUFFER)) tmp_sd.ee = ecore_evas_buffer_new(1, 1); else if (!strcmp(enginelist[i], ELM_DRM)) tmp_sd.ee = ecore_evas_drm_new(NULL, 0, 0, 0, 1, 1); else if (!strncmp(enginelist[i], "shot:", 5)) { tmp_sd.ee = ecore_evas_buffer_new(1, 1); ecore_evas_manual_render_set(tmp_sd.ee, EINA_TRUE); tmp_sd.shot.info = eina_stringshare_add(ENGINE_GET() + 5); } engine = enginelist[i]; if (tmp_sd.ee) break; } break; } eina_stringshare_del(accel); if (!tmp_sd.ee) { ERR("Cannot create window."); return NULL; } if (!sd->accel_pref) eina_stringshare_replace(&sd->accel_pref, elm_config_accel_preference_get()); efl_parent_set(obj, ecore_evas_get(tmp_sd.ee)); /* FIXME: Major hack: calling the constructor in the middle of finalize. */ efl_constructor(efl_super(obj, MY_CLASS)); efl_canvas_object_type_set(obj, MY_CLASS_NAME_LEGACY); evas_object_smart_callbacks_descriptions_set(obj, _smart_callbacks); evas_object_focus_set(obj, EINA_TRUE); if (getenv("ELM_FIRST_FRAME")) evas_event_callback_add(ecore_evas_get(tmp_sd.ee), EVAS_CALLBACK_RENDER_POST, _elm_win_first_frame_do, getenv("ELM_FIRST_FRAME")); /* copying possibly altered fields back */ #define SD_CPY(_field) \ do \ { \ sd->_field = tmp_sd._field; \ } while (0) SD_CPY(ee); SD_CPY(img_obj); SD_CPY(shot.info); #undef SD_CPY if ((type != ELM_WIN_FAKE) && (trap) && (trap->add)) sd->trap_data = trap->add(obj); /* complementary actions, which depend on final smart data * pointer */ if (type == ELM_WIN_INLINED_IMAGE) _win_inlined_image_set(sd); #ifdef HAVE_ELEMENTARY_X else if ((engine) && ((!strcmp(engine, ELM_SOFTWARE_X11)) || (!strcmp(engine, ELM_OPENGL_X11)))) { sd->x.client_message_handler = ecore_event_handler_add (ECORE_X_EVENT_CLIENT_MESSAGE, _elm_win_client_message, obj); sd->x.property_handler = ecore_event_handler_add (ECORE_X_EVENT_WINDOW_PROPERTY, _elm_win_property_change, obj); } #endif else if ((engine) && (!strncmp(engine, "shot:", 5))) _shot_init(sd); sd->kbdmode = ELM_WIN_KEYBOARD_UNKNOWN; sd->legacy.indmode = ELM_WIN_INDICATOR_UNKNOWN; sd->indimode = EFL_UI_WIN_INDICATOR_OFF; #ifdef HAVE_ELEMENTARY_X _internal_elm_win_xwindow_get(sd); if (sd->x.xwin) ecore_x_io_error_handler_set(_elm_x_io_err, NULL); #endif #ifdef HAVE_ELEMENTARY_WL2 _elm_win_wlwindow_get(sd); if (sd->wl.win) { Ecore_Wl2_Window_Type wtype; sd->wl.configure_handler = ecore_event_handler_add(ECORE_WL2_EVENT_WINDOW_CONFIGURE, _elm_win_wl_configure, obj); switch (sd->type) { case ELM_WIN_BASIC: case ELM_WIN_DIALOG_BASIC: case ELM_WIN_NAVIFRAME_BASIC: case ELM_WIN_SPLASH: case ELM_WIN_TOOLBAR: case ELM_WIN_UTILITY: case ELM_WIN_DOCK: case ELM_WIN_DESKTOP: wtype = ECORE_WL2_WINDOW_TYPE_TOPLEVEL; break; case ELM_WIN_TOOLTIP: case ELM_WIN_COMBO: case ELM_WIN_MENU: case ELM_WIN_POPUP_MENU: wtype = ECORE_WL2_WINDOW_TYPE_MENU; break; case ELM_WIN_DND: wtype = ECORE_WL2_WINDOW_TYPE_DND; break; default: wtype = ECORE_WL2_WINDOW_TYPE_NONE; } ecore_wl2_window_type_set(sd->wl.win, wtype); } else if (sd->type == ELM_WIN_FAKE) { const char *env = getenv("WAYLAND_DISPLAY"); if (env) { Ecore_Wl2_Display *d = ecore_wl2_display_connect(env); sd->wl.win = ecore_wl2_window_new(d, NULL, 0, 0, 1, 1); ecore_wl2_display_disconnect(d); } } #endif #ifdef HAVE_ELEMENTARY_COCOA _elm_win_cocoawindow_get(sd); #endif #ifdef HAVE_ELEMENTARY_WIN32 sd->win32.key_down_handler = ecore_event_handler_add(ECORE_EVENT_KEY_DOWN, _elm_win_key_down, obj); _internal_elm_win_win32window_get(sd); #endif if ((_elm_config->bgpixmap) #ifdef HAVE_ELEMENTARY_X && (((sd->x.xwin) && (!ecore_x_screen_is_composited(0))) || (!sd->x.xwin))) #else ) #endif TRAP(sd, avoid_damage_set, ECORE_EVAS_AVOID_DAMAGE_EXPOSE); // bg pixmap done by x - has other issues like can be redrawn by x before it // is filled/ready by app // TRAP(sd, avoid_damage_set, ECORE_EVAS_AVOID_DAMAGE_BUILT_IN); sd->type = type; sd->parent = parent; sd->modal_count = 0; sd->withdrawn = ecore_evas_withdrawn_get(sd->ee); if (sd->parent) evas_object_event_callback_add (sd->parent, EVAS_CALLBACK_DEL, _elm_win_on_parent_del, obj); sd->evas = ecore_evas_get(sd->ee); evas_object_color_set(obj, 0, 0, 0, 0); evas_object_pass_events_set(obj, EINA_TRUE); if (type == ELM_WIN_INLINED_IMAGE) efl_ui_win_inlined_parent_set(obj, parent); /* use own version of ecore_evas_object_associate() that does TRAP() */ ecore_evas_data_set(sd->ee, "elm_win", obj); if (type != ELM_WIN_FAKE) { evas_object_event_callback_add(obj, EVAS_CALLBACK_CHANGED_SIZE_HINTS, _elm_win_obj_callback_changed_size_hints, NULL); evas_object_intercept_raise_callback_add (obj, _elm_win_obj_intercept_raise, obj); evas_object_intercept_lower_callback_add (obj, _elm_win_obj_intercept_lower, obj); evas_object_intercept_stack_above_callback_add (obj, _elm_win_obj_intercept_stack_above, obj); evas_object_intercept_stack_below_callback_add (obj, _elm_win_obj_intercept_stack_below, obj); evas_object_intercept_layer_set_callback_add (obj, _elm_win_obj_intercept_layer_set, obj); evas_object_intercept_show_callback_add (obj, _elm_win_obj_intercept_show, obj); } TRAP(sd, name_class_set, name, _elm_appname); TRAP(sd, title_set, sd->title); ecore_evas_callback_delete_request_set(sd->ee, _elm_win_delete_request); ecore_evas_callback_state_change_set(sd->ee, _elm_win_state_change); ecore_evas_callback_focus_in_set(sd->ee, _elm_win_focus_in); ecore_evas_callback_focus_out_set(sd->ee, _elm_win_focus_out); ecore_evas_callback_resize_set(sd->ee, _elm_win_resize); ecore_evas_callback_move_set(sd->ee, _elm_win_move); ecore_evas_callback_pre_render_set(sd->ee, _elm_win_pre_render); if (type != ELM_WIN_FAKE) { ecore_evas_callback_mouse_in_set(sd->ee, _elm_win_mouse_in); ecore_evas_callback_mouse_out_set(sd->ee, _elm_win_mouse_out); } evas_object_event_callback_add(obj, EVAS_CALLBACK_HIDE, _elm_win_cb_hide, NULL); evas_object_event_callback_add(obj, EVAS_CALLBACK_SHOW, _elm_win_cb_show, NULL); evas_image_cache_set(sd->evas, (_elm_config->image_cache * 1024)); evas_font_cache_set(sd->evas, (_elm_config->font_cache * 1024)); EINA_LIST_FOREACH(_elm_config->font_dirs, l, fontpath) evas_font_path_append(sd->evas, fontpath); if (!_elm_config->font_hinting) evas_font_hinting_set(sd->evas, EVAS_FONT_HINTING_NONE); else if (_elm_config->font_hinting == 1) evas_font_hinting_set(sd->evas, EVAS_FONT_HINTING_AUTO); else if (_elm_config->font_hinting == 2) evas_font_hinting_set(sd->evas, EVAS_FONT_HINTING_BYTECODE); sd->wm_rot.wm_supported = ecore_evas_wm_rotation_supported_get(sd->ee); sd->wm_rot.preferred_rot = -1; // it means that elm_win doesn't use preferred rotation. #ifdef HAVE_ELEMENTARY_X _elm_win_xwin_update(sd); #endif #ifdef HAVE_ELEMENTARY_WL2 if (eina_streq(engine, ELM_WAYLAND_SHM) || eina_streq(engine, ELM_WAYLAND_EGL)) evas_event_callback_add(sd->evas, EVAS_CALLBACK_RENDER_FLUSH_PRE, _elm_win_frame_pre_render, sd); #endif //Prohibiting auto-rendering, until elm_win is shown. if (_elm_config->auto_norender_withdrawn) { if (elm_win_withdrawn_get(obj)) { if (!evas_object_data_get(obj, "__win_auto_norender")) { elm_win_norender_push(obj); evas_object_data_set(obj, "__win_auto_norender", obj); } } } #ifdef ELM_DEBUG Evas_Modifier_Mask mask = evas_key_modifier_mask_get(sd->evas, "Control"); evas_object_event_callback_add (obj, EVAS_CALLBACK_KEY_DOWN, _debug_key_down, NULL); if (evas_object_key_grab(obj, "F12", mask, 0, EINA_TRUE)) INF("Ctrl+F12 key combination exclusive for dot tree generation\n"); else ERR("failed to grab F12 key to elm widgets (dot) tree generation"); #endif if (type != ELM_WIN_FAKE) { if ((_elm_config->softcursor_mode == ELM_SOFTCURSOR_MODE_ON) || ((_elm_config->softcursor_mode == ELM_SOFTCURSOR_MODE_AUTO) && ((engine) && ((!strcmp(engine, ELM_SOFTWARE_FB)) || (!strcmp(engine, ELM_DRM)))))) { Evas_Object *o; Evas_Coord mw = 1, mh = 1, hx = 0, hy = 0; sd->pointer.obj = o = edje_object_add(ecore_evas_get(tmp_sd.ee)); elm_widget_theme_object_set(obj, o, "pointer", "base", "default"); edje_object_size_min_calc(o, &mw, &mh); evas_object_resize(o, mw, mh); edje_object_part_geometry_get(o, "elm.swallow.hotspot", &hx, &hy, NULL, NULL); sd->pointer.hot_x = hx; sd->pointer.hot_y = hy; evas_object_show(o); ecore_evas_object_cursor_set(tmp_sd.ee, o, EVAS_LAYER_MAX, hx, hy); } else if (_elm_config->softcursor_mode == ELM_SOFTCURSOR_MODE_OFF) { // do nothing } } _elm_win_legacy_init(sd); _elm_win_need_frame_adjust(sd, engine); _elm_win_apply_alpha(obj, sd); #ifdef HAVE_ELEMENTARY_WL2 if ((type != ELM_WIN_FAKE) && (type != ELM_WIN_INLINED_IMAGE)) { if ((engine) && ((!strcmp(engine, ELM_WAYLAND_SHM) || (!strcmp(engine, ELM_WAYLAND_EGL))))) { Evas *pevas; if (!strcmp(engine, ELM_WAYLAND_SHM)) sd->pointer.ee = ecore_evas_wayland_shm_new(NULL, 0, 0, 0, 0, 0, 0); else if (!strcmp(engine, ELM_WAYLAND_EGL)) sd->pointer.ee = ecore_evas_wayland_egl_new(NULL, 0, 0, 0, 0, 0, 0); pevas = ecore_evas_get(sd->pointer.ee); sd->pointer.obj = edje_object_add(pevas); sd->pointer.win = ecore_evas_wayland2_window_get(sd->pointer.ee); ecore_wl2_window_type_set(sd->pointer.win, ECORE_WL2_WINDOW_TYPE_NONE); } } #endif /* do not append to list; all windows render as black rects */ if (type != ELM_WIN_FAKE) { const char *element = "base"; const char *style; _elm_win_list = eina_list_append(_elm_win_list, obj); _elm_win_count++; if ((engine) && ((!strcmp(engine, ELM_SOFTWARE_FB)) || (!strcmp(engine, ELM_DRM)))) { TRAP(sd, fullscreen_set, 1); } style = elm_widget_style_get(obj); if (!style) style = "default"; switch (type) { case EFL_UI_WIN_DIALOG_BASIC: element = "dialog"; break; case EFL_UI_WIN_NAVIFRAME_BASIC: element = "naviframe"; break; default: break; } _elm_win_frame_add(sd, element, style); if (sd->indimode != EFL_UI_WIN_INDICATOR_OFF) _indicator_add(sd); if (_elm_config->focus_highlight_enable) elm_win_focus_highlight_enabled_set(obj, EINA_TRUE); if (_elm_config->focus_highlight_animate) elm_win_focus_highlight_animate_set(obj, EINA_TRUE); } efl_access_role_set(obj, EFL_ACCESS_ROLE_WINDOW); if (_elm_config->atspi_mode) efl_access_window_created_signal_emit(obj); // attach config API efl_composite_attach(obj, efl_provider_find(efl_main_loop_get(), EFL_CONFIG_GLOBAL_CLASS)); efl_event_callback_array_add(sd->evas, _elm_evas_tracking(), sd); efl_event_callback_array_add(obj, _elm_win_evas_feed_fake_callbacks(), sd->evas); efl_event_callback_array_add(obj, _elm_win_tracking(), sd); evas_object_show(sd->legacy.edje); if (type == ELM_WIN_FAKE) { _elm_win_resize_job(obj); _elm_win_move(sd->ee); } else { eina_future_then_easy(efl_loop_job(efl_loop_get(obj)), .success = _win_finalize_job_cb, .data = obj); } // All normal windows are "standard" windows with EO API if (!sd->legacy.ctor) { switch (type) { case EFL_UI_WIN_UNKNOWN: case EFL_UI_WIN_BASIC: case EFL_UI_WIN_DIALOG_BASIC: _elm_win_standard_init(obj); break; default: break; } } return obj; } EOLIAN static Eo * _efl_ui_win_efl_object_finalize(Eo *obj, Efl_Ui_Win_Data *sd) { Eina_Bool resume = !_elm_win_count; obj = _elm_win_finalize_internal(obj, sd, sd->name, sd->type); if (!obj) return NULL; obj = efl_finalize(efl_super(obj, MY_CLASS)); if (obj && resume) efl_event_callback_call(efl_loop_get(obj), EFL_LOOP_EVENT_RESUME, NULL); return obj; } EOLIAN static void _efl_ui_win_efl_canvas_object_legacy_ctor(Eo *obj, Efl_Ui_Win_Data *sd) { efl_canvas_object_legacy_ctor(efl_super(obj, MY_CLASS)); sd->legacy.ctor = EINA_TRUE; } EOLIAN static Efl_Ui_Focus_Manager* _efl_ui_win_efl_ui_widget_focus_manager_create(Eo *obj EINA_UNUSED, Efl_Ui_Win_Data *pd EINA_UNUSED, Efl_Ui_Focus_Object *root) { Efl_Ui_Focus_Manager *manager; manager = efl_add(EFL_UI_FOCUS_MANAGER_CALC_CLASS, obj, efl_ui_focus_manager_root_set(efl_added, root) ); return manager; } EOLIAN static void _efl_ui_win_efl_object_destructor(Eo *obj, Efl_Ui_Win_Data *pd EINA_UNUSED) { #ifdef HAVE_ELEMENTARY_WL2 if (pd->type == ELM_WIN_FAKE) { if (pd->wl.win) ecore_wl2_window_free(pd->wl.win); } #endif efl_destructor(efl_super(obj, MY_CLASS)); } EOLIAN static Eo * _efl_ui_win_efl_object_constructor(Eo *obj, Efl_Ui_Win_Data *pd) { /* UGLY HACK: Do (almost) nothing here: * We are calling the constructor chain from the finalizer. It's * really bad and hacky. Needs fixing. */ pd->obj = obj; pd->manager = efl_ui_widget_focus_manager_create(obj, obj); pd->provider = efl_add(EFL_UI_FOCUS_PARENT_PROVIDER_STANDARD_CLASS, obj); pd->profile.available = eina_array_new(4); // For bindings: if no parent, allow simple unref if (!efl_parent_get(obj)) efl_allow_parent_unref_set(obj, EINA_TRUE); efl_composite_attach(obj, pd->manager); _efl_ui_focus_manager_redirect_events_add(pd->manager, obj); return obj; } EOLIAN static void _efl_ui_win_efl_text_text_set(Eo *obj EINA_UNUSED, Efl_Ui_Win_Data *sd, const char *title) { if (!title) return; eina_stringshare_replace(&(sd->title), title); if (sd->ee) TRAP(sd, title_set, sd->title); if (sd->frame_obj) edje_object_part_text_escaped_set (sd->frame_obj, "elm.text.title", sd->title); } EOLIAN static const char* _efl_ui_win_efl_text_text_get(Eo *obj EINA_UNUSED, Efl_Ui_Win_Data *sd) { return sd->title; } EOLIAN static void _efl_ui_win_win_type_set(Eo *obj, Efl_Ui_Win_Data *sd, Efl_Ui_Win_Type type) { if (efl_finalized_get(obj)) { ERR("This function is only allowed during construction."); return; } sd->type = type; } EOLIAN static Efl_Ui_Win_Type _efl_ui_win_win_type_get(Eo *obj EINA_UNUSED, Efl_Ui_Win_Data *sd) { return sd->type; } EOLIAN static void _efl_ui_win_win_name_set(Eo *obj, Efl_Ui_Win_Data *sd, const char *name) { if (efl_finalized_get(obj)) { ERR("This function is only allowed during construction."); return; } sd->name = eina_stringshare_add(name); } EOLIAN static const char * _efl_ui_win_win_name_get(Eo *obj EINA_UNUSED, Efl_Ui_Win_Data *sd) { return sd->name; } EOLIAN static void _efl_ui_win_accel_preference_set(Eo *obj, Efl_Ui_Win_Data *pd, const char *accel) { if (efl_finalized_get(obj)) { ERR("This function is only allowed during construction."); return; } eina_stringshare_replace(&pd->accel_pref, accel); } EOLIAN static const char * _efl_ui_win_accel_preference_get(Eo *obj EINA_UNUSED, Efl_Ui_Win_Data *pd) { return pd->accel_pref; } EOLIAN static void _efl_ui_win_noblank_set(Eo *obj EINA_UNUSED, Efl_Ui_Win_Data *pd, Eina_Bool noblank) { noblank = !!noblank; if (pd->noblank == noblank) return; pd->noblank = noblank; _win_noblank_eval(); } EOLIAN static Eina_Bool _efl_ui_win_noblank_get(Eo *obj EINA_UNUSED, Efl_Ui_Win_Data *pd) { return pd->noblank; } EOLIAN static void _efl_ui_win_win_role_set(Eo *obj EINA_UNUSED, Efl_Ui_Win_Data *sd, const char *role) { if (!role) return; eina_stringshare_replace(&(sd->role), role); #ifdef HAVE_ELEMENTARY_X _elm_win_xwin_update(sd); #endif } EOLIAN static const char* _efl_ui_win_win_role_get(Eo *obj EINA_UNUSED, Efl_Ui_Win_Data *sd) { return sd->role; } EOLIAN static void _efl_ui_win_icon_object_set(Eo *obj, Efl_Ui_Win_Data *sd, Evas_Object *icon) { if (icon && (!efl_isa(sd->icon, EFL_CANVAS_IMAGE_INTERNAL_CLASS) && !efl_isa(sd->icon, EFL_UI_IMAGE_CLASS))) { ERR("Icon object type is not supported!"); efl_del(icon); return; } if (sd->icon) { efl_event_callback_del(sd->icon, EFL_EVENT_DEL, _elm_win_on_icon_del, obj); efl_del(sd->icon); } sd->icon = icon; if (sd->icon) { efl_event_callback_add(sd->icon, EFL_EVENT_DEL, _elm_win_on_icon_del, obj); if (sd->frame_obj) { edje_object_part_swallow(sd->frame_obj, "elm.swallow.icon", sd->icon); evas_object_is_frame_object_set(sd->icon, EINA_TRUE); } } #ifdef HAVE_ELEMENTARY_X _elm_win_xwin_update(sd); #endif } EOLIAN static const Evas_Object* _efl_ui_win_icon_object_get(Eo *obj EINA_UNUSED, Efl_Ui_Win_Data *sd) { return sd->icon; } /* Only for C API */ EAPI void elm_win_autodel_set(Eo *obj, Eina_Bool autodel) { ELM_WIN_DATA_GET_OR_RETURN(obj, sd); sd->autodel = autodel; } EAPI Eina_Bool elm_win_autodel_get(const Eo *obj) { Efl_Ui_Win_Data *sd = efl_data_scope_safe_get(obj, MY_CLASS); if (!sd) return EINA_FALSE; return sd->autodel; } EOLIAN static void _efl_ui_win_autohide_set(Eo *obj EINA_UNUSED, Efl_Ui_Win_Data *sd, Eina_Bool autohide) { sd->autohide = autohide; } EOLIAN static Eina_Bool _efl_ui_win_autohide_get(Eo *obj EINA_UNUSED, Efl_Ui_Win_Data *sd) { return sd->autohide; } EOLIAN static void _efl_ui_win_activate(Eo *obj EINA_UNUSED, Efl_Ui_Win_Data *sd) { TRAP(sd, activate); } EOLIAN static void _efl_ui_win_efl_gfx_stack_raise(Eo *obj EINA_UNUSED, Efl_Ui_Win_Data *pd) { TRAP(pd, raise); } EAPI void elm_win_raise(Eo *obj) { efl_gfx_stack_raise(obj); } EOLIAN static void _efl_ui_win_efl_gfx_stack_lower(Eo *obj, Efl_Ui_Win_Data *pd EINA_UNUSED) { // Do nothing: in X we could request to stack lower but that has been abused // and transformed into a kind of "iconify". As a consequence, lower is // not allowed in EO land. if (!elm_widget_is_legacy(obj)) return; // Legacy support... elm_win_lower(obj); } EOLIAN static void _efl_ui_win_center(Eo *obj, Efl_Ui_Win_Data *sd, Eina_Bool h, Eina_Bool v) { int win_w, win_h, screen_x, screen_y, screen_w, screen_h, nx, ny; if (sd->deferred_resize_job) _elm_win_resize_job(sd->obj); if (sd->frame_obj) edje_object_message_signal_process(sd->frame_obj); evas_smart_objects_calculate(evas_object_evas_get(obj)); _elm_win_resize_objects_eval(obj, EINA_FALSE); if ((trap) && (trap->center) && (!trap->center(sd->trap_data, obj, h, v))) return; if (!efl_gfx_visible_get(obj)) { // Chose to use env var so this will also translate more easily // to wayland. yes - we can get x atoms to figure out if wm is // enlightenment, but this works just as well. for wl we'd need // an alternate wl specific way... this below works better IMHO const char *s = getenv("DESKTOP"); if ((s) && (!strcasecmp(s, "Enlightenment"))) { #ifdef HAVE_ELEMENTARY_X if (sd->x.xwin) { static Ecore_X_Atom state = 0; static Ecore_X_Atom centered = 0; if (!centered) centered = ecore_x_atom_get ("__E_ATOM_WINDOW_STATE_CENTERED"); if (!state) state = ecore_x_atom_get ("__E_ATOM_WINDOW_STATE"); ecore_x_window_prop_card32_set(sd->x.xwin, state, ¢ered, 1); } #endif // XXX: what to do with wayland? // XXX: what to do with win32? // XXX: what to do with osx/coca? // etc. return; } // not e - fall back to manually placing on what we think the screen // is/will be... to do this move window to where pointer is first #ifdef HAVE_ELEMENTARY_X if (sd->x.xwin) { int x = 0, y = 0; if (sd->req_wh) { win_w = sd->req_w; win_h = sd->req_h; } else evas_object_geometry_get(obj, NULL, NULL, &win_w, &win_h); ecore_x_pointer_root_xy_get(&x, &y); ecore_evas_move(sd->ee, x - (win_w / 2), y - (win_h / 2)); } #endif // XXX: what to do with wayland? // XXX: what to do with win32? // XXX: what to do with osx/coca? // etc. } ecore_evas_screen_geometry_get(sd->ee, &screen_x, &screen_y, &screen_w, &screen_h); if ((!screen_w) || (!screen_h)) return; if (sd->req_wh) { win_w = sd->req_w; win_h = sd->req_h; } else evas_object_geometry_get(obj, NULL, NULL, &win_w, &win_h); if (sd->req_xy) { nx = sd->req_x; ny = sd->req_y; } else evas_object_geometry_get(obj, &nx, &ny, NULL, NULL); if ((!win_w) || (!win_h)) return; if (h) nx = win_w >= screen_w ? 0 : (screen_w / 2) - (win_w / 2); if (v) ny = win_h >= screen_h ? 0 : (screen_h / 2) - (win_h / 2); sd->req_xy = EINA_TRUE; sd->req_x = screen_x + nx; sd->req_y = screen_y + ny; evas_object_move(obj, screen_x + nx, screen_y + ny); } EOLIAN static void _efl_ui_win_borderless_set(Eo *obj, Efl_Ui_Win_Data *sd, Eina_Bool borderless) { sd->csd.need_borderless = borderless ? 1 : 0; _elm_win_frame_style_update(sd, 0, 1); #ifdef HAVE_ELEMENTARY_X if (!sd->x.xwin || !sd->csd.need) #endif TRAP(sd, borderless_set, borderless); _elm_win_resize_objects_eval(obj, EINA_FALSE); #ifdef HAVE_ELEMENTARY_X _elm_win_xwin_update(sd); #endif } EOLIAN static Eina_Bool _efl_ui_win_borderless_get(Eo *obj EINA_UNUSED, Efl_Ui_Win_Data *sd) { if (!sd->csd.need) return ecore_evas_borderless_get(sd->ee); return sd->csd.need_borderless; } EOLIAN static void _efl_ui_win_alpha_set(Eo *obj, Efl_Ui_Win_Data *sd, Eina_Bool enabled) { sd->application_alpha = enabled; _elm_win_apply_alpha(obj, sd); _elm_win_frame_style_update(sd, 0, 1); } EOLIAN static Eina_Bool _efl_ui_win_alpha_get(Eo *obj EINA_UNUSED, Efl_Ui_Win_Data *sd) { if (sd->img_obj) { return evas_object_image_alpha_get(sd->img_obj); } else { return ecore_evas_alpha_get(sd->ee); } return EINA_FALSE; } EOLIAN static void _efl_ui_win_fullscreen_set(Eo *obj EINA_UNUSED, Efl_Ui_Win_Data *sd, Eina_Bool fullscreen) { const char *engine_name = ecore_evas_engine_name_get(sd->ee); // YYY: handle if sd->img_obj if (engine_name && ((!strcmp(engine_name, ELM_SOFTWARE_FB)) || (!strcmp(engine_name, ELM_DRM)))) { // these engines... can ONLY be fullscreen return; } else if (sd->type == ELM_WIN_FAKE) sd->fullscreen = !!fullscreen; else { // sd->fullscreen = fullscreen; TRAP(sd, fullscreen_set, fullscreen); #ifdef HAVE_ELEMENTARY_X _elm_win_xwin_update(sd); #endif } } EOLIAN static Eina_Bool _efl_ui_win_fullscreen_get(Eo *obj EINA_UNUSED, Efl_Ui_Win_Data *sd) { const char *engine_name = ecore_evas_engine_name_get(sd->ee); if (engine_name && ((!strcmp(engine_name, ELM_SOFTWARE_FB)) || (!strcmp(engine_name, ELM_DRM)))) { // these engines... can ONLY be fullscreen return EINA_TRUE; } return sd->fullscreen; } static inline Eo * _main_menu_swallow_get(Efl_Ui_Win_Data *sd) { Eina_Bool legacy_menu_swallow = EINA_TRUE; const char *data; int version; data = edje_object_data_get(sd->legacy.edje, "version"); version = data ? atoi(data) : 0; if (version >= 119) legacy_menu_swallow = EINA_FALSE; #ifdef HAVE_ELEMENTARY_COCOA if (sd->cocoa.win) legacy_menu_swallow = EINA_TRUE; #endif if (legacy_menu_swallow) { DBG("Detected legacy theme, using legacy swallows."); return sd->legacy.edje; } return sd->frame_obj; } static void _main_menu_resize_cb(void *data EINA_UNUSED, const Efl_Event *ev) { // After resize, the framespace size has changed, so update the win geometry _elm_win_resize_objects_eval(ev->object, EINA_FALSE); efl_event_callback_del(ev->object, EFL_GFX_EVENT_RESIZE, _main_menu_resize_cb, NULL); } static void _dbus_menu_set(Eina_Bool dbus_connect, void *data) { ELM_WIN_DATA_GET_OR_RETURN(data, sd); Eo *swallow = _main_menu_swallow_get(sd); if (dbus_connect) { DBG("Setting menu to D-Bus"); edje_object_part_unswallow(swallow, sd->main_menu); sd->csd.need_menu = EINA_FALSE; _elm_menu_menu_bar_hide(sd->main_menu); _elm_win_resize_objects_eval(sd->obj, EINA_FALSE); if (swallow != sd->frame_obj) { // Note: Based on EFL 1.18 the signal was "elm,action,hide" // and not "elm,action,hide_menu" as expected. edje_object_signal_emit(swallow, "elm,action,hide", "elm"); edje_object_message_signal_recursive_process(swallow); } } else { DBG("Setting menu to local mode"); efl_event_callback_add(sd->obj, EFL_GFX_EVENT_RESIZE, _main_menu_resize_cb, NULL); edje_object_part_swallow(swallow, "elm.swallow.menu", sd->main_menu); evas_object_show(sd->main_menu); if (swallow == sd->frame_obj) { efl_canvas_object_is_frame_object_set(sd->main_menu, EINA_TRUE); sd->csd.need_menu = EINA_TRUE; } else { edje_object_signal_emit(swallow, "elm,action,show_menu", "elm"); edje_object_message_signal_recursive_process(swallow); } } _elm_win_frame_style_update(sd, 0, 1); //sd->deferred_resize_job = EINA_TRUE; } EOLIAN static void _efl_ui_win_maximized_set(Eo *obj EINA_UNUSED, Efl_Ui_Win_Data *sd, Eina_Bool maximized) { _elm_win_frame_style_update(sd, 0, 1); // YYY: handle if sd->img_obj TRAP(sd, maximized_set, maximized); #ifdef HAVE_ELEMENTARY_X _elm_win_xwin_update(sd); #endif } EOLIAN static Eina_Bool _efl_ui_win_maximized_get(Eo *obj EINA_UNUSED, Efl_Ui_Win_Data *sd) { return sd->maximized; } EOLIAN static void _efl_ui_win_iconified_set(Eo *obj EINA_UNUSED, Efl_Ui_Win_Data *sd, Eina_Bool iconified) { // sd->iconified = iconified; TRAP(sd, iconified_set, iconified); #ifdef HAVE_ELEMENTARY_X _elm_win_xwin_update(sd); #endif } EOLIAN static Eina_Bool _efl_ui_win_iconified_get(Eo *obj EINA_UNUSED, Efl_Ui_Win_Data *sd) { return sd->iconified; } EOLIAN static void _efl_ui_win_wm_available_profiles_set(Eo *obj EINA_UNUSED, Efl_Ui_Win_Data *sd, const Eina_Array *profiles) { Eina_Bool found = EINA_FALSE; _elm_win_available_profiles_del(sd); if (profiles && eina_array_count(profiles)) { Eina_Iterator *it; const char *prof; it = eina_array_iterator_new(profiles); EINA_ITERATOR_FOREACH(it, prof) { Eina_Stringshare *str = eina_stringshare_add(prof); if (!str) continue; eina_array_push(sd->profile.available, str); /* check to see if a given array has a current profile of elm_win */ if (str == sd->profile.name) found = EINA_TRUE; } eina_iterator_free(it); } if (ecore_evas_window_profile_supported_get(sd->ee)) { ecore_evas_window_available_profiles_set(sd->ee, (const char **) sd->profile.available->data, eina_array_count(sd->profile.available)); /* current profile of elm_win is wrong, change profile */ if (!found && eina_array_count(sd->profile.available)) { eina_stringshare_replace(&(sd->profile.name), eina_array_data_get(sd->profile.available, 0)); ecore_evas_window_profile_set(sd->ee, sd->profile.name); } } else { if (eina_array_count(sd->profile.available)) _elm_win_profile_update(sd); } } EOLIAN static const Eina_Array * _efl_ui_win_wm_available_profiles_get(Eo *obj EINA_UNUSED, Efl_Ui_Win_Data *sd) { if (ecore_evas_window_profile_supported_get(sd->ee)) { char **profiles = NULL; // all const unsigned int count, i; Eina_Bool ok; ok = ecore_evas_window_available_profiles_get(sd->ee, &profiles, &count); if (!ok) return NULL; if (count == eina_array_count(sd->profile.available)) { for (i = 0; ok && (i < count); i++) { if (!eina_streq(profiles[i], eina_array_data_get(sd->profile.available, i))) ok = EINA_FALSE; } if (ok) return sd->profile.available; } // Oops! What is going on here? Can this happen? INF("Available profile list has changed in ecore evas!"); _elm_win_available_profiles_del(sd); for (i = 0; i < count; i++) { Eina_Stringshare *str = eina_stringshare_add(profiles[i]); if (str) eina_array_push(sd->profile.available, str); } } return sd->profile.available; } EOLIAN static void _efl_ui_win_urgent_set(Eo *obj EINA_UNUSED, Efl_Ui_Win_Data *sd, Efl_Ui_Win_Urgent_Mode urgent) { Eina_Bool urgent_tmp = !!urgent; if (sd->urgent == urgent_tmp) return; sd->urgent = urgent_tmp; TRAP(sd, urgent_set, urgent_tmp); #ifdef HAVE_ELEMENTARY_X _elm_win_xwin_update(sd); #endif } EOLIAN static Efl_Ui_Win_Urgent_Mode _efl_ui_win_urgent_get(Eo *obj EINA_UNUSED, Efl_Ui_Win_Data *sd) { if (sd->urgent) return EFL_UI_WIN_URGENT_URGENT; return EFL_UI_WIN_URGENT_NONE; } EOLIAN static void _efl_ui_win_modal_set(Eo *obj, Efl_Ui_Win_Data *sd, Efl_Ui_Win_Modal_Mode modal) { Eina_Bool modal_tmp = !!modal; if (sd->modal_count) return; if ((modal_tmp) && (!sd->modal) && (evas_object_visible_get(obj))) _elm_win_modality_increment(sd); else if ((!modal_tmp) && (sd->modal) && (evas_object_visible_get(obj))) _elm_win_modality_decrement(sd); sd->modal = modal_tmp; TRAP(sd, modal_set, modal_tmp); #ifdef HAVE_ELEMENTARY_X _elm_win_xwin_update(sd); #endif } EOLIAN static Efl_Ui_Win_Modal_Mode _efl_ui_win_modal_get(Eo *obj EINA_UNUSED, Efl_Ui_Win_Data *sd) { if (sd->modal) return EFL_UI_WIN_MODAL_MODAL; return EFL_UI_WIN_MODAL_NONE; } static void _win_aspect_set(Efl_Ui_Win_Data *sd, double aspect) { sd->aspect = aspect; TRAP(sd, aspect_set, aspect); #ifdef HAVE_ELEMENTARY_X _elm_win_xwin_update(sd); #endif } static double _win_aspect_get(Efl_Ui_Win_Data *sd) { return sd->aspect; } EOLIAN static void _efl_ui_win_efl_gfx_size_hint_hint_aspect_set(Eo *obj EINA_UNUSED, Efl_Ui_Win_Data *pd, Efl_Gfx_Size_Hint_Aspect mode, Eina_Size2D sz) { if (sz.h) _win_aspect_set(pd, (double) sz.w / (double) sz.h); else _win_aspect_set(pd, 0.0); efl_gfx_size_hint_aspect_set(efl_super(obj, MY_CLASS), mode, sz); #ifdef HAVE_ELEMENTARY_WL2 if (pd->wl.win) ecore_wl2_window_aspect_set(pd->wl.win, sz.w, sz.h, mode); #endif } EOLIAN static void _efl_ui_win_efl_gfx_size_hint_hint_weight_set(Eo *obj EINA_UNUSED, Efl_Ui_Win_Data *pd EINA_UNUSED, double w, double h) { efl_gfx_size_hint_weight_set(efl_super(obj, MY_CLASS), w, h); #ifdef HAVE_ELEMENTARY_WL2 if (pd->wl.win) ecore_wl2_window_weight_set(pd->wl.win, w, h); #endif } EOLIAN static void _efl_ui_win_efl_gfx_size_hint_hint_base_set(Eo *obj EINA_UNUSED, Efl_Ui_Win_Data *sd, Eina_Size2D sz) { sd->size_base_w = sz.w; sd->size_base_h = sz.h; TRAP(sd, size_base_set, sz.w, sz.h); #ifdef HAVE_ELEMENTARY_X _elm_win_xwin_update(sd); #endif } EOLIAN static Eina_Size2D _efl_ui_win_efl_gfx_size_hint_hint_base_get(Eo *obj EINA_UNUSED, Efl_Ui_Win_Data *sd) { return EINA_SIZE2D(sd->size_base_w, sd->size_base_h); } EOLIAN static void _efl_ui_win_efl_gfx_size_hint_hint_step_set(Eo *obj EINA_UNUSED, Efl_Ui_Win_Data *sd, Eina_Size2D sz) { sd->size_step_w = sz.w; sd->size_step_h = sz.h; TRAP(sd, size_step_set, sz.w, sz.h); #ifdef HAVE_ELEMENTARY_X _elm_win_xwin_update(sd); #endif } EOLIAN static void _efl_ui_win_efl_gfx_size_hint_hint_max_set(Eo *obj, Efl_Ui_Win_Data *sd, Eina_Size2D sz) { if (sd->tmp_updating_hints) { efl_gfx_size_hint_max_set(efl_super(obj, MY_CLASS), sz); } else { if (sz.w < 1) sz.w = -1; if (sz.h < 1) sz.h = -1; sd->max_w = sz.w; sd->max_h = sz.h; _elm_win_resize_objects_eval(obj, EINA_FALSE); } } EOLIAN static Eina_Size2D _efl_ui_win_efl_gfx_size_hint_hint_step_get(Eo *obj EINA_UNUSED, Efl_Ui_Win_Data *sd) { return EINA_SIZE2D(sd->size_step_w, sd->size_step_h); } EAPI void elm_win_norender_push(Evas_Object *obj) { Efl_Ui_Win_Data *sd = efl_data_scope_safe_get(obj, MY_CLASS); if (!sd) return; sd->norender++; if (sd->norender == 1) ecore_evas_manual_render_set(sd->ee, EINA_TRUE); } EAPI void elm_win_norender_pop(Evas_Object *obj) { Efl_Ui_Win_Data *sd = efl_data_scope_safe_get(obj, MY_CLASS); if (!sd) return; if (sd->norender <= 0) return; sd->norender--; if (sd->norender == 0) ecore_evas_manual_render_set(sd->ee, EINA_FALSE); } EAPI int elm_win_norender_get(const Evas_Object *obj) { Efl_Ui_Win_Data *sd = efl_data_scope_safe_get(obj, MY_CLASS); if (!sd) return -1; return sd->norender; } EAPI void elm_win_render(Evas_Object *obj) { Efl_Ui_Win_Data *sd = efl_data_scope_safe_get(obj, MY_CLASS); if (!sd) return; ecore_evas_manual_render(sd->ee); } static int _win_rotation_degree_check(int rotation) { if ((rotation > 360) || (rotation < 0)) { WRN("Rotation degree should be 0 ~ 360 (passed degree: %d)", rotation); rotation %= 360; if (rotation < 0) rotation += 360; } return rotation; } /* * This API resizes the internal window(ex: X window) and evas_output. * But this does not resize the elm window object and its contents. */ static void _win_rotate(Evas_Object *obj, Efl_Ui_Win_Data *sd, int rotation, Eina_Bool resize) { rotation = _win_rotation_degree_check(rotation); if (sd->rot == rotation) return; sd->rot = rotation; if (resize) TRAP(sd, rotation_with_resize_set, rotation); else TRAP(sd, rotation_set, rotation); efl_gfx_size_hint_restricted_min_set(obj, EINA_SIZE2D(-1, -1)); efl_gfx_size_hint_max_set(obj, EINA_SIZE2D(-1, -1)); _elm_win_resize_objects_eval(obj, EINA_FALSE); #ifdef HAVE_ELEMENTARY_X _elm_win_xwin_update(sd); #endif _elm_win_frame_obj_update(sd); efl_ui_widget_on_orientation_update(obj, rotation); efl_event_callback_legacy_call (obj, EFL_UI_WIN_EVENT_ROTATION_CHANGED, NULL); if (_elm_config->atspi_mode) { Evas_Coord x = 0, y = 0, width = 0, height = 0; elm_win_screen_size_get(obj, &x, &y, &width, &height); if ((sd->rot == 0) || (sd->rot == 180)) { efl_access_bounds_changed_signal_emit(obj, x, y, width, height); } else { efl_access_bounds_changed_signal_emit(obj, x, y, height, width); } } } EOLIAN static void _efl_ui_win_wm_available_rotations_set(Eo *obj EINA_UNUSED, Efl_Ui_Win_Data *sd, Eina_Bool allow_0, Eina_Bool allow_90, Eina_Bool allow_180, Eina_Bool allow_270) { unsigned cnt = 0; int rots[4]; if (allow_0) rots[cnt++] = 0; if (allow_90) rots[cnt++] = 90; if (allow_180) rots[cnt++] = 180; if (allow_270) rots[cnt++] = 270; sd->wm_rot.use = EINA_TRUE; ELM_SAFE_FREE(sd->wm_rot.rots, free); sd->wm_rot.count = 0; if (cnt) { sd->wm_rot.rots = malloc(sizeof(int) * cnt); if (!sd->wm_rot.rots) return; memcpy(sd->wm_rot.rots, rots, cnt * sizeof(int)); sd->wm_rot.count = cnt; } ecore_evas_wm_rotation_available_rotations_set(sd->ee, sd->wm_rot.rots, sd->wm_rot.count); } EOLIAN static Eina_Bool _efl_ui_win_wm_available_rotations_get(Eo *obj EINA_UNUSED, Efl_Ui_Win_Data *sd, Eina_Bool *allow_0, Eina_Bool *allow_90, Eina_Bool *allow_180, Eina_Bool *allow_270) { if (!sd->wm_rot.use) goto end; if (allow_0) *allow_0 = EINA_FALSE; if (allow_90) *allow_90 = EINA_FALSE; if (allow_180) *allow_180 = EINA_FALSE; if (allow_270) *allow_270 = EINA_FALSE; for (unsigned k = 0; k < sd->wm_rot.count; k++) { switch (sd->wm_rot.rots[k]) { case 0: if (allow_0) *allow_0 = EINA_TRUE; break; case 90: if (allow_90) *allow_90 = EINA_TRUE; break; case 180: if (allow_180) *allow_180 = EINA_TRUE; break; case 270: if (allow_270) *allow_270 = EINA_TRUE; break; default: ERR("Unsupported rotation %d", sd->wm_rot.rots[k]); break; } } end: return !!sd->wm_rot.use; } EAPI void elm_win_wm_rotation_available_rotations_set(Elm_Win *obj, const int *rotations, unsigned int count) { Eina_Bool allow[4] = { 0, }; int found = 0; if (!rotations || !count) goto end; for (unsigned k = 0; (k < count) && (found < 4); k++) { int rot = (((rotations[k] % 360) + 360) % 360) / 90; if (!allow[rot]) { allow[rot] = EINA_TRUE; found++; } } end: efl_ui_win_wm_available_rotations_set(obj, allow[0], allow[1], allow[2], allow[3]); } EAPI Eina_Bool elm_win_wm_rotation_available_rotations_get(const Elm_Win *obj, int **rotations, unsigned int *count) { int rots[4] = { 0, }; Eina_Bool allow[4] = { 0, }; unsigned cnt = 0; if (!efl_ui_win_wm_available_rotations_get(obj, &allow[0], &allow[1], &allow[2], &allow[3])) goto none; for (int k = 0; k < 4; k++) if (allow[k]) rots[cnt++] = k * 90; if (!cnt) goto none; if (rotations) { *rotations = malloc(sizeof(int) * cnt); if (!*rotations) goto none; memcpy(*rotations, rots, cnt * sizeof(int)); } if (count) *count = cnt; return EINA_TRUE; none: if (rotations) *rotations = NULL; if (count) *count = 0; return EINA_FALSE; } EOLIAN static void _efl_ui_win_sticky_set(Eo *obj EINA_UNUSED, Efl_Ui_Win_Data *sd, Eina_Bool sticky) { // sd->sticky = sticky; TRAP(sd, sticky_set, sticky); #ifdef HAVE_ELEMENTARY_X _elm_win_xwin_update(sd); #endif } EOLIAN static Eina_Bool _efl_ui_win_sticky_get(Eo *obj EINA_UNUSED, Efl_Ui_Win_Data *sd) { return sd->sticky; } EOLIAN static void _efl_ui_win_keyboard_mode_set(Eo *obj EINA_UNUSED, Efl_Ui_Win_Data *sd, Elm_Win_Keyboard_Mode mode) { if (mode == sd->kbdmode) return; #ifdef HAVE_ELEMENTARY_X _internal_elm_win_xwindow_get(sd); #endif sd->kbdmode = mode; #ifdef HAVE_ELEMENTARY_X if (sd->x.xwin) ecore_x_e_virtual_keyboard_state_set (sd->x.xwin, (Ecore_X_Virtual_Keyboard_State)sd->kbdmode); #endif } EOLIAN static Elm_Win_Keyboard_Mode _efl_ui_win_keyboard_mode_get(Eo *obj EINA_UNUSED, Efl_Ui_Win_Data *sd) { return sd->kbdmode; } EOLIAN static void _efl_ui_win_indicator_mode_set(Eo *obj EINA_UNUSED, Efl_Ui_Win_Data *sd, Efl_Ui_Win_Indicator_Mode mode) { sd->legacy.forbidden = EINA_TRUE; if (sd->indimode == mode) return; sd->indimode = mode; if (sd->indimode == EFL_UI_WIN_INDICATOR_OFF) { _indicator_del(sd); return; } if (!sd->indicator) _indicator_add(sd); if (sd->indimode == EFL_UI_WIN_INDICATOR_BG_OPAQUE) edje_object_signal_emit(sd->frame_obj, "elm,action,indicator,bg_opaque", "elm"); else if (sd->indimode == EFL_UI_WIN_INDICATOR_BG_TRANSPARENT) edje_object_signal_emit(sd->frame_obj, "elm,action,indicator,bg_transparent", "elm"); else if (sd->indimode == EFL_UI_WIN_INDICATOR_HIDDEN) edje_object_signal_emit(sd->frame_obj, "elm,action,indicator,hidden", "elm"); edje_object_message_signal_process(sd->frame_obj); evas_object_smart_calculate(sd->frame_obj); _elm_win_frame_obj_update(sd); } EOLIAN static Efl_Ui_Win_Indicator_Mode _efl_ui_win_indicator_mode_get(Eo *obj EINA_UNUSED, Efl_Ui_Win_Data *sd EINA_UNUSED) { sd->legacy.forbidden = EINA_TRUE; return sd->indimode; } EOLIAN static Eina_Bool _efl_ui_win_efl_ui_focus_object_focus_get(Eo *obj EINA_UNUSED, Efl_Ui_Win_Data *sd) { // Bypass widget implementation here. return ecore_evas_focus_get(sd->ee); } EOLIAN static void _efl_ui_win_screen_constrain_set(Eo *obj EINA_UNUSED, Efl_Ui_Win_Data *sd, Eina_Bool constrain) { sd->constrain = !!constrain; } EOLIAN static Eina_Bool _efl_ui_win_screen_constrain_get(Eo *obj EINA_UNUSED, Efl_Ui_Win_Data *sd) { return sd->constrain; } EOLIAN static Eina_Size2D _efl_ui_win_efl_screen_screen_size_get(Eo *obj EINA_UNUSED, Efl_Ui_Win_Data *sd) { Eina_Size2D sz; ecore_evas_screen_geometry_get(sd->ee, NULL, NULL, &sz.w, &sz.h); return sz; } EOLIAN static void _efl_ui_win_efl_screen_screen_dpi_get(Eo *obj EINA_UNUSED, Efl_Ui_Win_Data *sd, int *xdpi, int *ydpi) { ecore_evas_screen_dpi_get(sd->ee, xdpi, ydpi); } EOLIAN static int _efl_ui_win_efl_screen_screen_rotation_get(Eo *obj EINA_UNUSED, Efl_Ui_Win_Data *sd) { //TODO: query to wm about device's rotation (void)sd; return 0; } EOLIAN static void _efl_ui_win_prop_focus_skip_set(Eo *obj EINA_UNUSED, Efl_Ui_Win_Data *sd, Eina_Bool skip) { sd->skip_focus = skip; TRAP(sd, focus_skip_set, skip); } EOLIAN static Eina_Bool _efl_ui_win_keygrab_set(Eo *obj EINA_UNUSED, Efl_Ui_Win_Data *sd, const char *key, Efl_Input_Modifier modifiers EINA_UNUSED, Efl_Input_Modifier not_modifiers EINA_UNUSED, int priority EINA_UNUSED, Efl_Ui_Win_Keygrab_Mode grab_mode) { Eina_Bool ret = EINA_FALSE; #ifdef HAVE_ELEMENTARY_X _internal_elm_win_xwindow_get(sd); if (sd->x.xwin) { Ecore_X_Win_Keygrab_Mode x_grab_mode; switch (grab_mode) { case ELM_WIN_KEYGRAB_SHARED: x_grab_mode = ECORE_X_WIN_KEYGRAB_SHARED; break; case ELM_WIN_KEYGRAB_TOPMOST: x_grab_mode = ECORE_X_WIN_KEYGRAB_TOPMOST; break; case ELM_WIN_KEYGRAB_EXCLUSIVE: x_grab_mode = ECORE_X_WIN_KEYGRAB_EXCLUSIVE; break; case ELM_WIN_KEYGRAB_OVERRIDE_EXCLUSIVE: x_grab_mode = ECORE_X_WIN_KEYGRAB_OVERRIDE_EXCLUSIVE; break; default: return ret; } ret = ecore_x_window_keygrab_set(sd->x.xwin, key, 0, 0, 0, x_grab_mode); } #else (void)sd; (void)key; (void)grab_mode; #endif return ret; } EOLIAN static Eina_Bool _efl_ui_win_keygrab_unset(Eo *obj EINA_UNUSED, Efl_Ui_Win_Data *sd, const char *key, Efl_Input_Modifier modifiers EINA_UNUSED, Efl_Input_Modifier not_modifiers EINA_UNUSED) { Eina_Bool ret = EINA_FALSE; #ifdef HAVE_ELEMENTARY_X _internal_elm_win_xwindow_get(sd); if (sd->x.xwin) ret = ecore_x_window_keygrab_unset(sd->x.xwin, key, 0, 0); #else (void)sd; (void)key; #endif return ret; } EOLIAN static void _efl_ui_win_efl_ui_widget_focus_highlight_enabled_set(Eo *obj EINA_UNUSED, Efl_Ui_Win_Data *sd, Eina_Bool enabled) { // Do not call efl_super() here. Only Win handles this property. enabled = !!enabled; if (sd->focus_highlight.enabled == enabled) return; sd->focus_highlight.enabled = enabled; if ((sd->focus_highlight.enabled) || (sd->focus_highlight.auto_enabled)) _elm_win_focus_highlight_init(sd); else _elm_win_focus_highlight_shutdown(sd); } EOLIAN static Eina_Bool _efl_ui_win_efl_ui_widget_focus_highlight_enabled_get(Eo *obj EINA_UNUSED, Efl_Ui_Win_Data *sd) { // Do not call efl_super() here. Only Win handles this property. return sd->focus_highlight.enabled; } static Efl_Ui_Theme_Apply _elm_win_theme_internal(Eo *obj, Efl_Ui_Win_Data *sd) { Efl_Ui_Theme_Apply int_ret = EFL_UI_THEME_APPLY_FAILED; Eina_Bool ret = EINA_FALSE, prev_alpha; const char *s; int_ret = elm_widget_theme_object_set(obj, sd->legacy.edje, "win", "base", elm_widget_style_get(obj)); if (!int_ret) return EFL_UI_THEME_APPLY_FAILED; edje_object_mirrored_set(sd->legacy.edje, efl_ui_mirrored_get(obj)); edje_object_scale_set(sd->legacy.edje, efl_gfx_scale_get(obj) * elm_config_scale_get()); efl_event_callback_legacy_call(obj, EFL_UI_WIN_EVENT_THEME_CHANGED, NULL); ret = efl_ui_widget_on_disabled_update(obj, elm_widget_disabled_get(obj)); if (!ret) int_ret = EFL_UI_THEME_APPLY_FAILED; prev_alpha = sd->theme_alpha; s = edje_object_data_get(sd->legacy.edje, "alpha"); sd->theme_alpha = (eina_streq(s, "1") || eina_streq(s, "true")); if (sd->theme_alpha != prev_alpha) _elm_win_apply_alpha(obj, sd); return int_ret; } EOLIAN static Efl_Ui_Theme_Apply _efl_ui_win_efl_ui_widget_theme_apply(Eo *obj, Efl_Ui_Win_Data *sd) { Efl_Ui_Theme_Apply int_ret = EFL_UI_THEME_APPLY_FAILED; int_ret = efl_ui_widget_theme_apply(efl_super(obj, MY_CLASS)); if (!int_ret) return EFL_UI_THEME_APPLY_FAILED; sd->focus_highlight.theme_changed = EINA_TRUE; int_ret = _elm_win_theme_internal(obj, sd) & int_ret; if (!int_ret) return EFL_UI_THEME_APPLY_FAILED; _elm_win_focus_highlight_reconfigure_job_start(sd); return int_ret; } EOLIAN static Eina_Bool _efl_ui_win_efl_ui_widget_focus_highlight_style_set(Eo *obj EINA_UNUSED, Efl_Ui_Win_Data *sd, const char *style) { // Do not call efl_super() here. Only Win handles this property. if (!eina_stringshare_replace(&sd->focus_highlight.style, style)) return EINA_TRUE; sd->focus_highlight.theme_changed = EINA_TRUE; _elm_win_focus_highlight_reconfigure_job_start(sd); return EINA_TRUE; } EOLIAN static const char* _efl_ui_win_efl_ui_widget_focus_highlight_style_get(Eo *obj EINA_UNUSED, Efl_Ui_Win_Data *sd) { // Do not call efl_super() here. Only Win handles this property. return sd->focus_highlight.style; } EOLIAN static void _efl_ui_win_efl_ui_widget_focus_highlight_animate_set(Eo *obj EINA_UNUSED, Efl_Ui_Win_Data *sd, Eina_Bool animate) { // Do not call efl_super() here. Only Win handles this property. animate = !!animate; if (sd->focus_highlight.animate == animate) return; sd->focus_highlight.animate = animate; sd->focus_highlight.theme_changed = EINA_TRUE; _elm_win_focus_highlight_reconfigure_job_start(sd); } EOLIAN static Eina_Bool _efl_ui_win_efl_ui_widget_focus_highlight_animate_get(Eo *obj EINA_UNUSED, Efl_Ui_Win_Data *sd) { // Do not call efl_super() here. Only Win handles this property. return sd->focus_highlight.animate; } EOLIAN static const char * _efl_ui_win_stack_id_get(Eo *obj EINA_UNUSED, Efl_Ui_Win_Data *sd) { return sd->stack_id; } EOLIAN static void _efl_ui_win_stack_master_id_set(Eo *obj EINA_UNUSED, Efl_Ui_Win_Data *sd, const char *id) { if (sd->shown) return; eina_stringshare_replace(&(sd->stack_master_id), id); #ifdef HAVE_ELEMENTARY_X if (sd->x.xwin) _elm_win_xwin_update(sd); else #endif { int num = ecore_evas_aux_hint_id_get(sd->ee, "stack_master_id"); if (num >= 0) ecore_evas_aux_hint_val_set(sd->ee, num, id); else ecore_evas_aux_hint_add(sd->ee, "stack_master_id", id); } } EOLIAN static const char * _efl_ui_win_stack_master_id_get(Eo *obj EINA_UNUSED, Efl_Ui_Win_Data *sd) { return sd->stack_master_id; } EOLIAN static void _efl_ui_win_stack_base_set(Eo *obj EINA_UNUSED, Efl_Ui_Win_Data *sd, Eina_Bool base) { int num; if (sd->shown) return; sd->stack_base = !!base; num = ecore_evas_aux_hint_id_get(sd->ee, "stack_base"); if (num >= 0) ecore_evas_aux_hint_val_set(sd->ee, num, sd->stack_base ? "1" : "0"); else ecore_evas_aux_hint_add(sd->ee, "stack_base", sd->stack_base ? "1" : "0"); } EOLIAN static Eina_Bool _efl_ui_win_stack_base_get(Eo *obj EINA_UNUSED, Efl_Ui_Win_Data *sd) { return sd->stack_base; } #ifdef HAVE_ELEMENTARY_X // list transient from bottom to top by recursive walking static void _x_transients_for_list(Ecore_X_Window base, Ecore_X_Window transient, Ecore_X_Window **wins, int *num) { Ecore_X_Window t, *children, *w; int n, i; children = ecore_x_window_children_get(base, &n); if (children) { for (i = 0; i < n; i++) { if (children[i] != transient) { t = ecore_x_icccm_transient_for_get(children[i]); if (t == transient) { (*num)++; w = realloc(*wins, *num * sizeof(Ecore_X_Window)); if (w) { *wins = w; (*wins)[*num - 1] = children[i]; } } _x_transients_for_list(children[i], transient, wins, num); } } free(children); } } #endif EOLIAN static void _efl_ui_win_stack_pop_to(Eo *obj EINA_UNUSED, Efl_Ui_Win_Data *sd) { if (!sd->stack_master_id) return; #ifdef HAVE_ELEMENTARY_X if (sd->x.xwin) { Ecore_X_Window *wins = NULL; int i, num = 0; Eina_Bool del = EINA_FALSE; ecore_x_grab(); _x_transients_for_list (ecore_x_window_root_get(sd->x.xwin), ecore_x_icccm_transient_for_get(sd->x.xwin), &wins, &num); if (wins) { for (i = 0; i < num; i++) { if (del) ecore_x_window_delete_request_send(wins[i]); if (wins[i] == sd->x.xwin) del = EINA_TRUE; } free(wins); } ecore_x_ungrab(); } else #endif { int num = ecore_evas_aux_hint_id_get(sd->ee, "stack_pop_to"); if (num >= 0) ecore_evas_aux_hint_val_set(sd->ee, num, "1"); else ecore_evas_aux_hint_add(sd->ee, "stack_pop_to", "1"); } // win32/osx ? } EAPI Eina_Bool elm_win_trap_set(const Elm_Win_Trap *t) { DBG("old %p, new %p", trap, t); if ((t) && (t->version != ELM_WIN_TRAP_VERSION)) { CRI("trying to set a trap version %lu while %lu was expected!", t->version, ELM_WIN_TRAP_VERSION); return EINA_FALSE; } trap = t; return EINA_TRUE; } EAPI void elm_win_floating_mode_set(Evas_Object *obj, Eina_Bool floating) { Efl_Ui_Win_Data *sd = efl_data_scope_safe_get(obj, MY_CLASS); if (!sd) return; floating = !!floating; if (floating == sd->floating) return; sd->floating = floating; #ifdef HAVE_ELEMENTARY_X _internal_elm_win_xwindow_get(sd); if (sd->x.xwin) { if (sd->floating) ecore_x_e_illume_window_state_set (sd->x.xwin, ECORE_X_ILLUME_WINDOW_STATE_FLOATING); else ecore_x_e_illume_window_state_set (sd->x.xwin, ECORE_X_ILLUME_WINDOW_STATE_NORMAL); } #endif } EAPI Eina_Bool elm_win_floating_mode_get(const Evas_Object *obj) { Efl_Ui_Win_Data *sd = efl_data_scope_safe_get(obj, MY_CLASS); if (!sd) return EINA_FALSE; return sd->floating; } void _elm_win_focus_highlight_in_theme_update(Evas_Object *obj, Eina_Bool in_theme) { ELM_WIN_DATA_GET(obj, sd); sd->focus_highlight.cur.in_theme = !!in_theme; } void _elm_win_focus_highlight_start(Evas_Object *obj) { ELM_WIN_DATA_GET(obj, sd); if (!(sd->focus_highlight.enabled) && !(sd->focus_highlight.auto_enabled)) return; sd->focus_highlight.cur.visible = EINA_TRUE; sd->focus_highlight.geometry_changed = EINA_TRUE; _elm_win_focus_highlight_reconfigure_job(obj); } void _elm_win_focus_auto_show(Evas_Object *obj) { ELM_WIN_DATA_GET(obj, sd); Eina_Bool pfocus = (sd->focus_highlight.enabled) || (sd->focus_highlight.auto_enabled); sd->focus_highlight.auto_enabled = _elm_config->win_auto_focus_enable; sd->focus_highlight.auto_animate = _elm_config->win_auto_focus_animate; if (pfocus != ((sd->focus_highlight.enabled) || (sd->focus_highlight.auto_enabled))) { if ((sd->focus_highlight.enabled) || (sd->focus_highlight.auto_enabled)) _elm_win_focus_highlight_init(sd); } } void _elm_win_focus_auto_hide(Evas_Object *obj) { ELM_WIN_DATA_GET(obj, sd); Eina_Bool pfocus = (sd->focus_highlight.enabled) || (sd->focus_highlight.auto_enabled); sd->focus_highlight.auto_enabled = EINA_FALSE; sd->focus_highlight.auto_animate = EINA_FALSE; if (pfocus != ((sd->focus_highlight.enabled) || (sd->focus_highlight.auto_enabled))) { if (!((sd->focus_highlight.enabled) || (sd->focus_highlight.auto_enabled))) _elm_win_focus_highlight_shutdown(sd); } } static void _on_atspi_bus_connected(void *data EINA_UNUSED, const Efl_Event *event EINA_UNUSED) { Evas_Object *win; Eina_List *l; EINA_LIST_FOREACH(_elm_win_list, l, win) { /** * Reemit accessibility events when AT-SPI2 connection is begin * established. This assures that Assistive Technology clients will * receive all org.a11y.window events and could keep track of active * windows whithin system. */ efl_access_window_created_signal_emit(win); if (elm_win_focus_get(win)) { Evas_Object *target; efl_access_window_activated_signal_emit(win); /** Reemit focused event to inform atspi clients about currently * focused object **/ { Efl_Ui_Focus_Manager *m; m = win; while (efl_ui_focus_manager_redirect_get(m)) m = efl_ui_focus_manager_redirect_get(m); target = efl_ui_focus_manager_focus_get(m); } if (target) efl_access_state_changed_signal_emit(target, EFL_ACCESS_STATE_FOCUSED, EINA_TRUE); } else efl_access_window_deactivated_signal_emit(win); } } EOLIAN static void _efl_ui_win_class_constructor(Efl_Class *klass) { evas_smart_legacy_type_register(MY_CLASS_NAME_LEGACY, klass); if (_elm_config->atspi_mode) { Eo *bridge = _elm_atspi_bridge_get(); if (bridge) efl_event_callback_add(bridge, ELM_ATSPI_BRIDGE_EVENT_CONNECTED, _on_atspi_bus_connected, NULL); } } EOLIAN static void _efl_ui_win_efl_object_debug_name_override(Eo *obj, Efl_Ui_Win_Data *pd, Eina_Strbuf *sb) { efl_debug_name_override(efl_super(obj, MY_CLASS), sb); eina_strbuf_append_printf(sb, ":'%s':'%s'", pd->name, pd->title); } EOLIAN static Eo* _efl_ui_win_efl_access_parent_get(Eo *obj EINA_UNUSED, Efl_Ui_Win_Data *sd EINA_UNUSED) { // attach all kinds of windows directly to ATSPI application root object Eo *root; root = efl_access_root_get(EFL_ACCESS_MIXIN); return root; } EOLIAN static const Efl_Access_Action_Data* _efl_ui_win_efl_access_widget_action_elm_actions_get(Eo *obj EINA_UNUSED, Efl_Ui_Win_Data *sd EINA_UNUSED) { static Efl_Access_Action_Data atspi_actions[] = { { "move,previous", "move", "previous", _key_action_move}, { "move,next", "move", "next", _key_action_move}, { "move,left", "move", "left", _key_action_move}, { "move,right", "move", "right", _key_action_move}, { "move,up", "move", "up", _key_action_move}, { "move,down", "move", "down", _key_action_move}, { NULL, NULL, NULL, NULL } }; return &atspi_actions[0]; } EOLIAN static Efl_Access_State_Set _efl_ui_win_efl_access_state_set_get(Eo *obj, Efl_Ui_Win_Data *sd EINA_UNUSED) { Efl_Access_State_Set ret; ret = efl_access_state_set_get(efl_super(obj, MY_CLASS)); if (elm_win_focus_get(obj)) STATE_TYPE_SET(ret, EFL_ACCESS_STATE_ACTIVE); return ret; } EOLIAN static const char* _efl_ui_win_efl_access_name_get(Eo *obj, Efl_Ui_Win_Data *sd EINA_UNUSED) { const char *ret; ret = efl_access_name_get(efl_super(obj, EFL_UI_WIN_CLASS)); if (ret) return ret; const char *name = elm_win_title_get(obj); return name; } EOLIAN static Eina_Rect _efl_ui_win_efl_access_component_extents_get(Eo *obj, Efl_Ui_Win_Data *_pd EINA_UNUSED, Eina_Bool screen_coords) { Eina_Rect r; int ee_x, ee_y; r = efl_gfx_geometry_get(obj); r.x = r.y = 0; if (screen_coords) { Ecore_Evas *ee = ecore_evas_ecore_evas_get(evas_object_evas_get(obj)); if (ee) { ecore_evas_geometry_get(ee, &ee_x, &ee_y, NULL, NULL); r.x += ee_x; r.y += ee_y; } } return r; } EOLIAN static Eina_Bool _efl_ui_win_efl_input_state_modifier_enabled_get(Eo *obj EINA_UNUSED, Efl_Ui_Win_Data *pd, Efl_Input_Modifier mod, const Efl_Input_Device *seat) { const Evas_Modifier *m = evas_key_modifier_get(pd->evas); const char *name = _efl_input_modifier_to_string(mod); return evas_seat_key_modifier_is_set(m, name, seat); } EOLIAN static Eina_Bool _efl_ui_win_efl_input_state_lock_enabled_get(Eo *obj EINA_UNUSED, Efl_Ui_Win_Data *pd, Efl_Input_Lock lock, const Efl_Input_Device *seat) { const Evas_Lock *m = evas_key_lock_get(pd->evas); const char *name = _efl_input_lock_to_string(lock); return evas_seat_key_lock_is_set(m, name, seat); } EOLIAN static Efl_Object * _efl_ui_win_efl_object_provider_find(const Eo *obj, Efl_Ui_Win_Data *pd EINA_UNUSED, const Efl_Object *klass) { if (klass == EFL_UI_WIN_CLASS) return (Eo *)obj; if (klass == EFL_UI_FOCUS_PARENT_PROVIDER_INTERFACE) return pd->provider; return efl_provider_find(efl_super(obj, MY_CLASS), klass); } // See evas_inline.x #define _EVAS_COLOR_CLAMP(x, y) do { \ if (x > y) { x = y; bad = 1; } \ if (x < 0) { x = 0; bad = 1; } } while (0) #define EVAS_COLOR_SANITIZE(r, g, b, a) \ ({ int bad = 0; \ _EVAS_COLOR_CLAMP(a, 255); \ _EVAS_COLOR_CLAMP(r, a); \ _EVAS_COLOR_CLAMP(g, a); \ _EVAS_COLOR_CLAMP(b, a); \ bad; }) /* Efl.Part APIs */ #define WIN_PART_ERR(part) ERR("No such part in window: '%s'. Supported parts are: 'content' and 'background'.", part); static Eina_Bool _elm_win_bg_set(Efl_Ui_Win_Data *sd, Eo *bg) { ELM_SAFE_DEL(sd->bg); if (!bg) return EINA_TRUE; if (!elm_widget_sub_object_add(sd->obj, bg)) return EINA_FALSE; if (!edje_object_part_swallow(sd->frame_obj, "elm.swallow.background", bg)) return EINA_FALSE; efl_gfx_visible_set(bg, 1); efl_gfx_size_hint_align_set(bg, -1, -1); efl_gfx_size_hint_weight_set(bg, 1, 1); efl_wref_add(bg, &sd->bg); return EINA_TRUE; } /* Legacy theme compatibility */ static Eina_Bool _elm_win_bg_must_swallow(Efl_Ui_Win_Data *sd) { if (EINA_UNLIKELY(!sd->legacy.bg_must_swallow_init)) { /* Overkill: check which theme version the standard elm_bg uses */ Elm_Widget_Smart_Data *wd; const char *version; Eo *bg; int v; sd->legacy.bg_must_swallow = 1; sd->legacy.bg_must_swallow_init = 1; bg = elm_bg_add(sd->obj); wd = efl_data_scope_get(bg, EFL_UI_WIDGET_CLASS); if (wd) { version = edje_object_data_get(wd->resize_obj, "version"); v = version ? atoi(version) : 0; if (v >= FRAME_OBJ_THEME_MIN_VERSION) sd->legacy.bg_must_swallow = 0; } evas_object_del(bg); } return sd->legacy.bg_must_swallow; } void _elm_win_standard_init(Eo *obj) { /* Support for elm_util_win_standard_add() and Efl.Ui.Win.Standard */ Efl_Ui_Win_Data *sd = efl_data_scope_get(obj, MY_CLASS); ELM_SAFE_DEL(sd->bg); sd->csd.need_bg_standard = 1; if (!_elm_win_bg_must_swallow(sd)) { sd->csd.need_bg_solid = EINA_TRUE; } else { Eo *bg; /* Legacy theme compatibility */ DBG("Detected legacy theme used for elm_bg. Swallowing object."); sd->csd.need_bg_solid = EINA_FALSE; if (sd->legacy.ctor) bg = elm_bg_add(obj); else { // Note: This code path is probably not necessary (custom legacy // theme but efl_add'ed window -- all efl_add'ed widgets would // use default theme) bg = efl_add(EFL_UI_BG_WIDGET_CLASS, obj); } _elm_win_bg_set(sd, bg); } _elm_win_frame_style_update(sd, 0, 1); } static Eina_Bool _efl_ui_win_content_set(Eo *obj, Efl_Ui_Win_Data *sd, const char *part, Eo *content) { sd->legacy.forbidden = EINA_TRUE; if (eina_streq(part, "content")) { if (sd->content == content) return EINA_TRUE; if (!elm_widget_sub_object_add(obj, content)) goto err; /* FIXME: Switch to swallow inside the frame if (!edje_object_part_swallow(sd->frame_obj, "elm.swallow.client", content)) goto err; */ evas_object_box_append(sd->legacy.box, content); evas_object_show(content); efl_wref_add(content, &sd->content); return EINA_TRUE; } else if (eina_streq(part, "background")) { sd->csd.need_bg_standard = 0; if (sd->bg == content) return EINA_TRUE; if (!_elm_win_bg_set(sd, content)) goto err; return EINA_TRUE; } WIN_PART_ERR(part); return EINA_FALSE; err: ERR("Failed to set object %p as %s for window %p", content, part, obj); return EINA_FALSE; } static Efl_Canvas_Object * _efl_ui_win_content_get(Eo *obj EINA_UNUSED, Efl_Ui_Win_Data *sd, const char *part) { sd->legacy.forbidden = EINA_TRUE; if (eina_streq(part, "content")) return sd->content; else if (eina_streq(part, "background")) return sd->bg; WIN_PART_ERR(part); return NULL; } static Efl_Canvas_Object * _efl_ui_win_content_unset(Eo *obj, Efl_Ui_Win_Data *sd, const char *part) { Eo *content; sd->legacy.forbidden = EINA_TRUE; content = _efl_ui_win_content_get(obj, sd, part); if (!content) return NULL; efl_ref(content); _efl_ui_win_content_set(obj, sd, part, NULL); return content; } static Eina_Bool _efl_ui_win_part_color_set(Eo *obj EINA_UNUSED, Efl_Ui_Win_Data *sd, const char *part, int r, int g, int b, int a) { sd->legacy.forbidden = EINA_TRUE; if (eina_streq(part, "background")) { sd->csd.need_bg_solid = EINA_TRUE; edje_object_color_class_set(sd->frame_obj, "elm/win/background", r, g, b, a, 0, 0, 0, 0, 0, 0, 0, 0); _elm_win_frame_style_update(sd, 0, 1); return EINA_TRUE; } WIN_PART_ERR(part); return EINA_FALSE; } static Eina_Bool _efl_ui_win_part_color_get(Eo *obj EINA_UNUSED, Efl_Ui_Win_Data *sd, const char *part, int *r, int *g, int *b, int *a) { sd->legacy.forbidden = EINA_TRUE; if (eina_streq(part, "background")) { edje_object_color_class_get(sd->frame_obj, "elm/win/background", r, g, b, a, 0, 0, 0, 0, 0, 0, 0, 0); return EINA_TRUE; } WIN_PART_ERR(part); return EINA_FALSE; } static Eina_Bool _efl_ui_win_part_file_set(Eo *obj, Efl_Ui_Win_Data *sd, const char *part, const char *file, const char *key) { sd->legacy.forbidden = EINA_TRUE; if (eina_streq(part, "background")) { Eina_Bool ok = EINA_TRUE; Eo *bg = NULL; if (file) { bg = efl_add(EFL_UI_IMAGE_CLASS, obj); efl_image_scale_type_set(bg, EFL_IMAGE_SCALE_TYPE_FIT_OUTSIDE); ok = efl_file_set(bg, file, key); if (!ok) ELM_SAFE_DEL(bg); _elm_win_bg_set(sd, bg); } else { _elm_win_standard_init(obj); } return ok; } WIN_PART_ERR(part); return EINA_FALSE; } static Eina_Bool _efl_ui_win_part_file_get(Eo *obj, Efl_Ui_Win_Data *sd, const char *part, const char **file, const char **key) { sd->legacy.forbidden = EINA_TRUE; if (file) *file = NULL; if (key) *key = NULL; if (eina_streq(part, "background")) { const Eo *bg = _efl_ui_win_content_get(obj, sd, "background"); efl_file_get(bg, file, key); return EINA_TRUE; } WIN_PART_ERR(part); return EINA_FALSE; } /* Efl.Part begin */ static void _efl_ui_win_part_efl_gfx_color_color_set(Eo *obj, void *_pd EINA_UNUSED, int r, int g, int b, int a) { Elm_Part_Data *pd = efl_data_scope_get(obj, EFL_UI_WIDGET_PART_CLASS); Efl_Ui_Win_Data *sd = efl_data_scope_get(pd->obj, MY_CLASS); if (EVAS_COLOR_SANITIZE(r, g, b, a)) ERR("Evas only handles premultiplied colors (0 <= R,G,B <= A <= 255)"); _efl_ui_win_part_color_set(pd->obj, sd, pd->part, r, g, b, a); } static void _efl_ui_win_part_efl_gfx_color_color_get(Eo *obj, void *_pd EINA_UNUSED, int *r, int *g, int *b, int *a) { Elm_Part_Data *pd = efl_data_scope_get(obj, EFL_UI_WIDGET_PART_CLASS); Efl_Ui_Win_Data *sd = efl_data_scope_get(pd->obj, MY_CLASS); _efl_ui_win_part_color_get(pd->obj, sd, pd->part, r, g, b, a); } static Eina_Bool _efl_ui_win_part_efl_file_file_set(Eo *obj, void *_pd EINA_UNUSED, const char *file, const char *key) { Elm_Part_Data *pd = efl_data_scope_get(obj, EFL_UI_WIDGET_PART_CLASS); Efl_Ui_Win_Data *sd = efl_data_scope_get(pd->obj, MY_CLASS); return _efl_ui_win_part_file_set(pd->obj, sd, pd->part, file, key); } static void _efl_ui_win_part_efl_file_file_get(Eo *obj, void *_pd EINA_UNUSED, const char **file, const char **key) { Elm_Part_Data *pd = efl_data_scope_get(obj, EFL_UI_WIDGET_PART_CLASS); Efl_Ui_Win_Data *sd = efl_data_scope_get(pd->obj, MY_CLASS); _efl_ui_win_part_file_get(pd->obj, sd, pd->part, file, key); } ELM_PART_OVERRIDE(efl_ui_win, EFL_UI_WIN, Efl_Ui_Win_Data) ELM_PART_OVERRIDE_CONTENT_SET(efl_ui_win, EFL_UI_WIN, Efl_Ui_Win_Data) ELM_PART_OVERRIDE_CONTENT_GET(efl_ui_win, EFL_UI_WIN, Efl_Ui_Win_Data) ELM_PART_OVERRIDE_CONTENT_UNSET(efl_ui_win, EFL_UI_WIN, Efl_Ui_Win_Data) ELM_PART_CONTENT_DEFAULT_GET(efl_ui_win, "content") ELM_PART_CONTENT_DEFAULT_IMPLEMENT(efl_ui_win, Efl_Ui_Win_Data) #include "efl_ui_win_part.eo.c" /* Efl.Part end */ EOLIAN static Eina_Bool _efl_ui_win_move_resize_start(Eo *obj EINA_UNUSED, Efl_Ui_Win_Data *sd, Efl_Ui_Win_Move_Resize_Mode mode) { // 1. move_resize can only be started after mouse down event if (evas_event_down_count_get(sd->evas) <= 0) { ERR("move_resize_start can only be called when a pointer is pressed."); return EINA_FALSE; } return _win_move_resize_start(sd, mode); } EAPI Evas_Object * elm_win_get(Evas_Object *obj) { EINA_SAFETY_ON_NULL_RETURN_VAL(obj, NULL); return ecore_evas_data_get(ecore_evas_ecore_evas_get(evas_object_evas_get(obj)), "elm_win"); } /* windowing specific calls - shall we do this differently? */ EAPI Ecore_X_Window elm_win_xwindow_get(const Evas_Object *obj) { Efl_Ui_Win_Data *sd = efl_data_scope_safe_get(obj, MY_CLASS); if (!sd) return 0; #ifdef HAVE_ELEMENTARY_X if (sd->x.xwin) return sd->x.xwin; if (sd->parent) return elm_win_xwindow_get(sd->parent); #endif return 0; } EAPI Ecore_Wl2_Window * elm_win_wl_window_get(const Evas_Object *obj) { Efl_Ui_Win_Data *sd = efl_data_scope_safe_get(obj, MY_CLASS); if (!sd) return NULL; if (!evas_object_smart_type_check_ptr(obj, MY_CLASS_NAME_LEGACY)) { Ecore_Evas *ee = ecore_evas_ecore_evas_get(evas_object_evas_get(obj)); return _elm_ee_wlwin_get(ee); } #if HAVE_ELEMENTARY_WL2 if (sd->wl.win) return sd->wl.win; if (sd->parent) return elm_win_wl_window_get(sd->parent); #endif return NULL; } EAPI Ecore_Cocoa_Window * elm_win_cocoa_window_get(const Evas_Object *obj) { Efl_Ui_Win_Data *sd = efl_data_scope_safe_get(obj, MY_CLASS); if (!sd) return NULL; Ecore_Evas *ee = ecore_evas_ecore_evas_get(evas_object_evas_get(obj)); return _elm_ee_cocoa_win_get(ee); } EAPI Ecore_Win32_Window * elm_win_win32_window_get(const Evas_Object *obj) { Efl_Ui_Win_Data *sd = efl_data_scope_safe_get(obj, MY_CLASS); const char *engine_name; if (!sd) return NULL; engine_name = ecore_evas_engine_name_get(sd->ee); if (!(engine_name && ((!strcmp(engine_name, ELM_SOFTWARE_WIN32)) || (!strcmp(engine_name, ELM_SOFTWARE_DDRAW))))) return NULL; if (!evas_object_smart_type_check_ptr(obj, MY_CLASS_NAME_LEGACY)) { Ecore_Evas *ee = ecore_evas_ecore_evas_get(evas_object_evas_get(obj)); return _elm_ee_win32win_get(ee); } Ecore_Win32_Window *ret = NULL; #if HAVE_ELEMENTARY_WIN32 if (sd->win32.win) ret = sd->win32.win; if (sd->parent) ret = elm_win_win32_window_get(sd->parent); #endif return ret; } EAPI void * elm_win_trap_data_get(const Evas_Object *obj) { Efl_Ui_Win_Data *sd = efl_data_scope_safe_get(obj, MY_CLASS); if (!sd) return NULL; return sd->trap_data; } EAPI void elm_win_override_set(Evas_Object *obj, Eina_Bool override) { Efl_Ui_Win_Data *sd = efl_data_scope_safe_get(obj, MY_CLASS); if (!sd) return; TRAP(sd, override_set, override); #ifdef HAVE_ELEMENTARY_X _elm_win_xwin_update(sd); #endif } EAPI Eina_Bool elm_win_override_get(const Evas_Object *obj) { Efl_Ui_Win_Data *sd = efl_data_scope_safe_get(obj, MY_CLASS); if (!sd) return EINA_FALSE; return ecore_evas_override_get(sd->ee); } EAPI void elm_win_lower(Evas_Object *obj) { Efl_Ui_Win_Data *sd = efl_data_scope_safe_get(obj, MY_CLASS); if (!sd) return; TRAP(sd, lower); } EAPI void elm_win_quickpanel_set(Evas_Object *obj, Eina_Bool quickpanel) { Efl_Ui_Win_Data *sd = efl_data_scope_safe_get(obj, MY_CLASS); if (!sd) return; #ifdef HAVE_ELEMENTARY_X _internal_elm_win_xwindow_get(sd); if (sd->x.xwin) { ecore_x_e_illume_quickpanel_set(sd->x.xwin, quickpanel); if (quickpanel) { Ecore_X_Window_State states[2]; states[0] = ECORE_X_WINDOW_STATE_SKIP_TASKBAR; states[1] = ECORE_X_WINDOW_STATE_SKIP_PAGER; ecore_x_netwm_window_state_set(sd->x.xwin, states, 2); ecore_x_icccm_hints_set(sd->x.xwin, 0, 0, 0, 0, 0, 0, 0); } } #else (void)sd; (void)quickpanel; #endif } EAPI Eina_Bool elm_win_quickpanel_get(const Evas_Object *obj) { Efl_Ui_Win_Data *sd = efl_data_scope_safe_get(obj, MY_CLASS); if (!sd) return EINA_FALSE; #ifdef HAVE_ELEMENTARY_X _internal_elm_win_xwindow_get(sd); if (sd->x.xwin) return ecore_x_e_illume_quickpanel_get(sd->x.xwin); #else (void)sd; #endif return EINA_FALSE; } EAPI void elm_win_quickpanel_priority_major_set(Evas_Object *obj, int priority) { Efl_Ui_Win_Data *sd = efl_data_scope_safe_get(obj, MY_CLASS); if (!sd) return; #ifdef HAVE_ELEMENTARY_X _internal_elm_win_xwindow_get(sd); if (sd->x.xwin) ecore_x_e_illume_quickpanel_priority_major_set(sd->x.xwin, priority); #else (void)sd; (void)priority; #endif } EAPI int elm_win_quickpanel_priority_major_get(const Evas_Object *obj) { Efl_Ui_Win_Data *sd = efl_data_scope_safe_get(obj, MY_CLASS); if (!sd) return -1; #ifdef HAVE_ELEMENTARY_X _internal_elm_win_xwindow_get(sd); if (sd->x.xwin) return ecore_x_e_illume_quickpanel_priority_major_get(sd->x.xwin); #else (void)sd; #endif return -1; } EAPI void elm_win_quickpanel_priority_minor_set(Evas_Object *obj, int priority) { Efl_Ui_Win_Data *sd = efl_data_scope_safe_get(obj, MY_CLASS); if (!sd) return; #ifdef HAVE_ELEMENTARY_X _internal_elm_win_xwindow_get(sd); if (sd->x.xwin) ecore_x_e_illume_quickpanel_priority_minor_set(sd->x.xwin, priority); #else (void)sd; (void)priority; #endif } EAPI int elm_win_quickpanel_priority_minor_get(const Evas_Object *obj) { Efl_Ui_Win_Data *sd = efl_data_scope_safe_get(obj, MY_CLASS); if (!sd) return -1; #ifdef HAVE_ELEMENTARY_X _internal_elm_win_xwindow_get(sd); if (sd->x.xwin) return ecore_x_e_illume_quickpanel_priority_minor_get(sd->x.xwin); #else (void)sd; #endif return -1; } EAPI void elm_win_quickpanel_zone_set(Evas_Object *obj, int zone) { Efl_Ui_Win_Data *sd = efl_data_scope_safe_get(obj, MY_CLASS); if (!sd) return; #ifdef HAVE_ELEMENTARY_X _internal_elm_win_xwindow_get(sd); if (sd->x.xwin) ecore_x_e_illume_quickpanel_zone_set(sd->x.xwin, zone); #else (void)sd; (void)zone; #endif } EAPI int elm_win_quickpanel_zone_get(const Evas_Object *obj) { Efl_Ui_Win_Data *sd = efl_data_scope_safe_get(obj, MY_CLASS); if (!sd) return 0; #ifdef HAVE_ELEMENTARY_X _internal_elm_win_xwindow_get(sd); if (sd->x.xwin) return ecore_x_e_illume_quickpanel_zone_get(sd->x.xwin); #else (void)sd; #endif return 0; } EAPI void elm_win_indicator_mode_set(Evas_Object *obj, Elm_Win_Indicator_Mode mode) { Efl_Ui_Win_Data *sd = efl_data_scope_safe_get(obj, MY_CLASS); if (!sd) return; if (sd->legacy.forbidden) { CRI("Use of this API is forbidden after calling an EO API on this " "window. Fix your code!"); return; } if (mode == sd->legacy.indmode) return; #ifdef HAVE_ELEMENTARY_X _internal_elm_win_xwindow_get(sd); #endif sd->legacy.indmode = mode; #ifdef HAVE_ELEMENTARY_X if (sd->x.xwin) { if (sd->legacy.indmode == ELM_WIN_INDICATOR_SHOW) ecore_x_e_illume_indicator_state_set (sd->x.xwin, ECORE_X_ILLUME_INDICATOR_STATE_ON); else if (sd->legacy.indmode == ELM_WIN_INDICATOR_HIDE) ecore_x_e_illume_indicator_state_set (sd->x.xwin, ECORE_X_ILLUME_INDICATOR_STATE_OFF); } #endif efl_event_callback_legacy_call (obj, EFL_UI_WIN_EVENT_INDICATOR_PROP_CHANGED, NULL); } EAPI Elm_Win_Indicator_Mode elm_win_indicator_mode_get(const Evas_Object *obj) { Efl_Ui_Win_Data *sd = efl_data_scope_safe_get(obj, MY_CLASS); if (!sd) return ELM_WIN_INDICATOR_UNKNOWN; if (sd->legacy.forbidden) { CRI("Use of this API is forbidden after calling an EO API on this " "window. Fix your code!"); return ELM_WIN_INDICATOR_UNKNOWN; } return sd->legacy.indmode; } EAPI void elm_win_indicator_opacity_set(Evas_Object *obj, Elm_Win_Indicator_Opacity_Mode mode) { Efl_Ui_Win_Data *sd = efl_data_scope_safe_get(obj, MY_CLASS); if (!sd) return; if (sd->legacy.forbidden) { CRI("Use of this API is forbidden after calling an EO API on this " "window. Fix your code!"); return; } if (mode == sd->legacy.ind_o_mode) return; sd->legacy.ind_o_mode = mode; #ifdef HAVE_ELEMENTARY_X _internal_elm_win_xwindow_get(sd); if (sd->x.xwin) { if (sd->legacy.ind_o_mode == ELM_WIN_INDICATOR_OPAQUE) ecore_x_e_illume_indicator_opacity_set (sd->x.xwin, ECORE_X_ILLUME_INDICATOR_OPAQUE); else if (sd->legacy.ind_o_mode == ELM_WIN_INDICATOR_TRANSLUCENT) ecore_x_e_illume_indicator_opacity_set (sd->x.xwin, ECORE_X_ILLUME_INDICATOR_TRANSLUCENT); else if (sd->legacy.ind_o_mode == ELM_WIN_INDICATOR_TRANSPARENT) ecore_x_e_illume_indicator_opacity_set (sd->x.xwin, ECORE_X_ILLUME_INDICATOR_TRANSPARENT); } #endif efl_event_callback_legacy_call (obj, EFL_UI_WIN_EVENT_INDICATOR_PROP_CHANGED, NULL); } EAPI Elm_Win_Indicator_Opacity_Mode elm_win_indicator_opacity_get(const Evas_Object *obj) { Efl_Ui_Win_Data *sd = efl_data_scope_safe_get(obj, MY_CLASS); if (!sd) return ELM_WIN_INDICATOR_OPACITY_UNKNOWN; if (sd->legacy.forbidden) { CRI("Use of this API is forbidden after calling an EO API on this " "window. Fix your code!"); return ELM_WIN_INDICATOR_OPACITY_UNKNOWN; } return sd->legacy.ind_o_mode; } EAPI void elm_win_keyboard_win_set(Evas_Object *obj, Eina_Bool is_keyboard) { Efl_Ui_Win_Data *sd = efl_data_scope_safe_get(obj, MY_CLASS); if (!sd) return; #ifdef HAVE_ELEMENTARY_X _internal_elm_win_xwindow_get(sd); if (sd->x.xwin) ecore_x_e_virtual_keyboard_set(sd->x.xwin, is_keyboard); #else (void)sd; (void)is_keyboard; #endif } EAPI Eina_Bool elm_win_keyboard_win_get(const Evas_Object *obj) { Efl_Ui_Win_Data *sd = efl_data_scope_safe_get(obj, MY_CLASS); if (!sd) return EINA_FALSE; #ifdef HAVE_ELEMENTARY_X _internal_elm_win_xwindow_get(sd); if (sd->x.xwin) return ecore_x_e_virtual_keyboard_get(sd->x.xwin); #else (void)sd; #endif return EINA_FALSE; } EAPI void elm_win_conformant_set(Evas_Object *obj, Eina_Bool conformant) { Efl_Ui_Win_Data *sd = efl_data_scope_safe_get(obj, MY_CLASS); if (!sd) return; #ifdef HAVE_ELEMENTARY_X _internal_elm_win_xwindow_get(sd); if (sd->x.xwin) ecore_x_e_illume_conformant_set(sd->x.xwin, conformant); #else (void)sd; (void)conformant; #endif } EAPI Eina_Bool elm_win_conformant_get(const Evas_Object *obj) { Efl_Ui_Win_Data *sd = efl_data_scope_safe_get(obj, MY_CLASS); if (!sd) return EINA_FALSE; #ifdef HAVE_ELEMENTARY_X _internal_elm_win_xwindow_get(sd); if (sd->x.xwin) return ecore_x_e_illume_conformant_get(sd->x.xwin); #else (void)sd; #endif return EINA_FALSE; } EAPI void elm_win_wm_rotation_manual_rotation_done_set(Evas_Object *obj, Eina_Bool set) { Efl_Ui_Win_Data *sd = efl_data_scope_safe_get(obj, MY_CLASS); if (!sd) return; if (!sd->wm_rot.use) return; ecore_evas_wm_rotation_manual_rotation_done_set(sd->ee, set); } EAPI Eina_Bool elm_win_wm_rotation_manual_rotation_done_get(const Evas_Object *obj) { Efl_Ui_Win_Data *sd = efl_data_scope_safe_get(obj, MY_CLASS); if (!sd) return EINA_FALSE; if (!sd->wm_rot.use) return EINA_FALSE; return ecore_evas_wm_rotation_manual_rotation_done_get(sd->ee); } EAPI void elm_win_wm_rotation_manual_rotation_done(Evas_Object *obj) { Efl_Ui_Win_Data *sd = efl_data_scope_safe_get(obj, MY_CLASS); if (!sd) return; if (!sd->wm_rot.use) return; ecore_evas_wm_rotation_manual_rotation_done(sd->ee); } EAPI void elm_win_rotation_set(Evas_Object *obj, int rotation) { Efl_Ui_Win_Data *sd = efl_data_scope_safe_get(obj, MY_CLASS); if (!sd) return; _win_rotate(obj, sd, rotation, EINA_FALSE); } /* * This API does not resize the internal window (ex: X window). * But this resizes evas_output, elm window, and its contents. */ EAPI void elm_win_rotation_with_resize_set(Evas_Object *obj, int rotation) { Efl_Ui_Win_Data *sd = efl_data_scope_safe_get(obj, MY_CLASS); if (!sd) return; _win_rotate(obj, sd, rotation, EINA_TRUE); } EAPI int elm_win_rotation_get(const Evas_Object *obj) { Efl_Ui_Win_Data *sd = efl_data_scope_safe_get(obj, MY_CLASS); if (!sd) return -1; return sd->rot; } EAPI int elm_win_wm_rotation_preferred_rotation_get(const Evas_Object *obj) { Efl_Ui_Win_Data *sd = efl_data_scope_safe_get(obj, MY_CLASS); if (!sd) return -1; return sd->wm_rot.preferred_rot; } EAPI Eina_Bool elm_win_wm_rotation_supported_get(const Evas_Object *obj) { Efl_Ui_Win_Data *sd = efl_data_scope_safe_get(obj, MY_CLASS); if (!sd) return EINA_FALSE; return sd->wm_rot.wm_supported; } EAPI void elm_win_wm_rotation_preferred_rotation_set(Evas_Object *obj, int rotation) { Efl_Ui_Win_Data *sd = efl_data_scope_safe_get(obj, MY_CLASS); int rot; if (!sd) return; if (!sd->wm_rot.use) sd->wm_rot.use = EINA_TRUE; // '-1' means that elm_win doesn't use preferred rotation. if (rotation == -1) rot = -1; else rot = _win_rotation_degree_check(rotation); if (sd->wm_rot.preferred_rot == rot) return; sd->wm_rot.preferred_rot = rot; ecore_evas_wm_rotation_preferred_rotation_set(sd->ee, rot); } EAPI void elm_win_screen_size_get(const Evas_Object *obj, int *x, int *y, int *w, int *h) { Efl_Ui_Win_Data *sd = efl_data_scope_safe_get(obj, MY_CLASS); if (!sd) return; ecore_evas_screen_geometry_get(sd->ee, x, y, w, h); } EAPI void elm_win_screen_position_get(const Evas_Object *obj, int *x, int *y) { Efl_Ui_Win_Data *sd = efl_data_scope_safe_get(obj, MY_CLASS); if (!sd) return; if (x) *x = sd->screen.x; if (y) *y = sd->screen.y; } EAPI void elm_win_screen_dpi_get(const Evas_Object *obj, int *xdpi, int *ydpi) { Efl_Ui_Win_Data *sd = efl_data_scope_safe_get(obj, MY_CLASS); if (!sd) return; ecore_evas_screen_dpi_get(sd->ee, xdpi, ydpi); } EAPI void elm_win_icon_name_set(Evas_Object *obj, const char *icon_name) { Efl_Ui_Win_Data *sd = efl_data_scope_safe_get(obj, MY_CLASS); if (!sd) return; if (!icon_name) return; eina_stringshare_replace(&(sd->icon_name), icon_name); #ifdef HAVE_ELEMENTARY_X _elm_win_xwin_update(sd); #endif } EAPI const char* elm_win_icon_name_get(const Evas_Object *obj) { Efl_Ui_Win_Data *sd = efl_data_scope_safe_get(obj, MY_CLASS); if (!sd) return NULL; return sd->icon_name; } EAPI void elm_win_withdrawn_set(Evas_Object *obj, Eina_Bool withdrawn) { Efl_Ui_Win_Data *sd = efl_data_scope_safe_get(obj, MY_CLASS); if (!sd) return; // sd->withdrawn = withdrawn; TRAP(sd, withdrawn_set, withdrawn); #ifdef HAVE_ELEMENTARY_X _elm_win_xwin_update(sd); #endif } EAPI Eina_Bool elm_win_withdrawn_get(const Evas_Object *obj) { Efl_Ui_Win_Data *sd = efl_data_scope_safe_get(obj, MY_CLASS); if (!sd) return EINA_FALSE; return sd->withdrawn; } EAPI void elm_win_urgent_set(Evas_Object *obj, Eina_Bool urgent) { Efl_Ui_Win_Data *sd = efl_data_scope_safe_get(obj, MY_CLASS); if (!sd) return; if (sd->urgent == urgent) return; sd->urgent = urgent; TRAP(sd, urgent_set, urgent); #ifdef HAVE_ELEMENTARY_X _elm_win_xwin_update(sd); #endif } EAPI Eina_Bool elm_win_urgent_get(const Evas_Object *obj) { Efl_Ui_Win_Data *sd = efl_data_scope_safe_get(obj, MY_CLASS); if (!sd) return EINA_FALSE; return sd->urgent; } EAPI void elm_win_demand_attention_set(Evas_Object *obj, Eina_Bool demand_attention) { Efl_Ui_Win_Data *sd = efl_data_scope_safe_get(obj, MY_CLASS); if (!sd) return; sd->demand_attention = demand_attention; TRAP(sd, demand_attention_set, demand_attention); #ifdef HAVE_ELEMENTARY_X _elm_win_xwin_update(sd); #endif } EAPI Eina_Bool elm_win_demand_attention_get(const Evas_Object *obj) { Efl_Ui_Win_Data *sd = efl_data_scope_safe_get(obj, MY_CLASS); if (!sd) return EINA_FALSE; return sd->demand_attention; } EAPI void elm_win_modal_set(Evas_Object *obj, Eina_Bool modal) { Efl_Ui_Win_Modal_Mode modality; modality = modal ? EFL_UI_WIN_MODAL_MODAL : EFL_UI_WIN_MODAL_NONE; efl_ui_win_modal_set(obj, modality); } EAPI Eina_Bool elm_win_modal_get(const Evas_Object *obj) { Efl_Ui_Win_Modal_Mode modality; modality = efl_ui_win_modal_get(obj); return (modality != EFL_UI_WIN_MODAL_NONE); } EAPI void elm_win_shaped_set(Evas_Object *obj, Eina_Bool shaped) { Efl_Ui_Win_Data *sd = efl_data_scope_safe_get(obj, MY_CLASS); if (!sd) return; TRAP(sd, shaped_set, shaped); #ifdef HAVE_ELEMENTARY_X _elm_win_xwin_update(sd); #endif } EAPI Eina_Bool elm_win_shaped_get(const Evas_Object *obj) { Efl_Ui_Win_Data *sd = efl_data_scope_safe_get(obj, MY_CLASS); if (!sd) return EINA_FALSE; return ecore_evas_shaped_get(sd->ee); } EAPI void elm_win_title_set(Evas_Object *obj, const char *title) { efl_text_set(obj, title); } EAPI const char* elm_win_title_get(const Evas_Object *obj) { return efl_text_get(obj); } EAPI void elm_win_size_base_set(Evas_Object *obj, int w, int h) { efl_gfx_size_hint_base_set(obj, EINA_SIZE2D(w, h)); } EAPI void elm_win_size_base_get(const Evas_Object *obj, int *w, int *h) { Eina_Size2D sz; sz = efl_gfx_size_hint_base_get(obj); if (w) *w = sz.w; if (h) *h = sz.h; } EAPI void elm_win_size_step_set(Evas_Object *obj, int w, int h) { efl_gfx_size_hint_step_set(obj, EINA_SIZE2D(w, h)); } EAPI void elm_win_size_step_get(const Evas_Object *obj, int *w, int *h) { Eina_Size2D sz; sz = efl_gfx_size_hint_step_get(obj); if (w) *w = sz.w; if (h) *h = sz.h; } EAPI void elm_win_illume_command_send(Evas_Object *obj, Elm_Illume_Command command, void *params EINA_UNUSED) { Efl_Ui_Win_Data *sd = efl_data_scope_safe_get(obj, MY_CLASS); if (!sd) return; #ifdef HAVE_ELEMENTARY_X _internal_elm_win_xwindow_get(sd); if (sd->x.xwin) { switch (command) { case ELM_ILLUME_COMMAND_FOCUS_BACK: ecore_x_e_illume_focus_back_send(sd->x.xwin); break; case ELM_ILLUME_COMMAND_FOCUS_FORWARD: ecore_x_e_illume_focus_forward_send(sd->x.xwin); break; case ELM_ILLUME_COMMAND_FOCUS_HOME: ecore_x_e_illume_focus_home_send(sd->x.xwin); break; case ELM_ILLUME_COMMAND_CLOSE: ecore_x_e_illume_close_send(sd->x.xwin); break; default: break; } } #else (void)sd; (void)command; #endif } EAPI void elm_win_profile_set(Evas_Object *obj, const char *profile) { Efl_Ui_Win_Data *sd = efl_data_scope_safe_get(obj, MY_CLASS); if (!sd) return; /* check to see if a given profile is present in an available profiles */ if (profile && eina_array_count(sd->profile.available)) { if (!_profile_exists(sd, profile)) return; } if (ecore_evas_window_profile_supported_get(sd->ee)) { if (!profile) _elm_win_profile_del(sd); ecore_evas_window_profile_set(sd->ee, profile); } else { if (_internal_elm_win_profile_set(sd, profile)) _elm_win_profile_update(sd); } } EAPI const char* elm_win_profile_get(const Evas_Object *obj) { Efl_Ui_Win_Data *sd = efl_data_scope_safe_get(obj, MY_CLASS); if (!sd) return NULL; return sd->profile.name; } EAPI void elm_win_layer_set(Evas_Object *obj, int layer) { Efl_Ui_Win_Data *sd = efl_data_scope_safe_get(obj, MY_CLASS); if (!sd) return; TRAP(sd, layer_set, layer); #ifdef HAVE_ELEMENTARY_X _elm_win_xwin_update(sd); #endif } EAPI int elm_win_layer_get(const Evas_Object *obj) { Efl_Ui_Win_Data *sd = efl_data_scope_safe_get(obj, MY_CLASS); if (!sd) return 0; return ecore_evas_layer_get(sd->ee); } EAPI Evas_Object* elm_win_inlined_image_object_get(const Evas_Object *obj) { Efl_Ui_Win_Data *sd = efl_data_scope_safe_get(obj, MY_CLASS); if (!sd) return NULL; return sd->img_obj; } static Ecore_Window _elm_win_window_id_get(Efl_Ui_Win_Data *sd) { #if HAVE_ELEMENTARY_WL2 if (sd->wl.win) return (Ecore_Window)ecore_wl2_window_id_get(sd->wl.win); if (sd->parent) { Ecore_Wl2_Window *parent; parent = elm_win_wl_window_get(sd->parent); if (parent) return (Ecore_Window)ecore_wl2_window_id_get(parent); } #endif #ifdef HAVE_ELEMENTARY_X _internal_elm_win_xwindow_get(sd); if (sd->x.xwin) return (Ecore_Window)sd->x.xwin; if (sd->parent) { Ecore_Window xwin = elm_win_xwindow_get(sd->parent); if (xwin) return xwin; } #endif #ifdef HAVE_ELEMENTARY_COCOA if (sd->cocoa.win) return (Ecore_Window)(sd->cocoa.win); if (sd->parent) { Ecore_Cocoa_Window *pwin; pwin = elm_win_cocoa_window_get(sd->parent); if (pwin) return (Ecore_Window)pwin; } #endif #ifdef HAVE_ELEMENTARY_WIN32 _internal_elm_win_win32window_get(sd); if (sd->win32.win) return (Ecore_Window)sd->win32.win; if (sd->parent) { Ecore_Window wwin = (Ecore_Window)elm_win_win32_window_get(sd->parent); if (wwin) return wwin; } #endif return 0; } EAPI Ecore_Window elm_win_window_id_get(const Evas_Object *obj) { Efl_Ui_Win_Data *sd; if (!obj) return 0; if (!evas_object_smart_type_check_ptr(obj, MY_CLASS_NAME_LEGACY)) { Ecore_Evas *ee = ecore_evas_ecore_evas_get(evas_object_evas_get(obj)); return ecore_evas_window_get(ee); } sd = efl_data_scope_safe_get(obj, MY_CLASS); if (!sd) return 0; return _elm_win_window_id_get(sd); } EAPI Evas_Object * elm_win_main_menu_get(Evas_Object *obj) { Efl_Ui_Win_Data *sd = efl_data_scope_safe_get(obj, MY_CLASS); if (!sd) return NULL; #ifdef HAVE_ELEMENTARY_X Eina_Bool use_dbus = EINA_FALSE; #endif if (sd->main_menu) goto end; sd->main_menu = elm_menu_add(obj); _elm_menu_menu_bar_set(sd->main_menu, EINA_TRUE); #ifdef HAVE_ELEMENTARY_X if (!_elm_config->disable_external_menu && sd->x.xwin) use_dbus = EINA_TRUE; #endif #ifdef HAVE_ELEMENTARY_X if (use_dbus && _elm_dbus_menu_register(sd->main_menu)) { _elm_dbus_menu_app_menu_register(sd->x.xwin, sd->main_menu, _dbus_menu_set, obj); } else #endif _dbus_menu_set(EINA_FALSE, obj); end: return sd->main_menu; } EAPI void elm_win_aspect_set(Eo *obj, double aspect) { Eina_Size2D sz = { 0, 0 }; if (aspect > DBL_EPSILON) sz = EINA_SIZE2D(1000 * aspect, 1000); efl_gfx_size_hint_aspect_set(obj, EFL_GFX_SIZE_HINT_ASPECT_NONE, sz); } EAPI double elm_win_aspect_get(const Eo *obj) { Efl_Ui_Win_Data *sd = efl_data_scope_safe_get(obj, MY_CLASS); if (!sd) return 0.0; return _win_aspect_get(sd); } /* legacy APIs */ static void _fake_canvas_set(Evas_Object *obj, Ecore_Evas *oee) { Efl_Ui_Win_Data *sd = efl_data_scope_safe_get(obj, MY_CLASS); if (!sd) return; sd->ee = oee; _elm_win_need_frame_adjust(sd, ecore_evas_engine_name_get(oee)); } EAPI Evas_Object * elm_win_add(Evas_Object *parent, const char *name, Efl_Ui_Win_Type type) { const Efl_Class *klass = MY_CLASS; switch ((int) type) { case ELM_WIN_INLINED_IMAGE: klass = EFL_UI_WIN_INLINED_CLASS; break; case ELM_WIN_SOCKET_IMAGE: klass = EFL_UI_WIN_SOCKET_CLASS; break; default: break; } return elm_legacy_add(klass, parent, efl_ui_win_name_set(efl_added, name), efl_ui_win_type_set(efl_added, type)); } EAPI Evas_Object * elm_win_fake_add(Ecore_Evas *ee) { return elm_legacy_add(MY_CLASS, NULL, _fake_canvas_set(efl_added, ee), efl_ui_win_name_set(efl_added, NULL), efl_ui_win_type_set(efl_added, ELM_WIN_FAKE)); } EAPI Evas_Object * elm_win_util_standard_add(const char *name, const char *title) { Evas_Object *win; win = elm_legacy_add(EFL_UI_WIN_CLASS, NULL, efl_text_set(efl_added, title), efl_ui_win_name_set(efl_added, name), efl_ui_win_type_set(efl_added, EFL_UI_WIN_BASIC)); if (!win) return NULL; _elm_win_standard_init(win); return win; } EAPI Evas_Object * elm_win_util_dialog_add(Evas_Object *parent, const char *name, const char *title) { Evas_Object *win; win = elm_legacy_add(EFL_UI_WIN_CLASS, parent, efl_text_set(efl_added, title), efl_ui_win_name_set(efl_added, name), efl_ui_win_type_set(efl_added, EFL_UI_WIN_DIALOG_BASIC)); if (!win) return NULL; _elm_win_standard_init(win); return win; } /** * @internal * * Recalculate the size of window considering its resize objects' weight and * min size. If any of its resize objects' weight equals to 0.0, window * layout's weight will be set to 0.0. * * @param o box object * @param p box's private data * @param data window object */ static void _window_layout_stack(Evas_Object *o, Evas_Object_Box_Data *p, void *data) { const Eina_List *l; Evas_Object *child; Evas_Object_Box_Option *opt; Evas_Coord x, y, w, h, menuw = 0; double wx, wy; Evas_Coord minw = -1, minh = -1; double weight_x = EVAS_HINT_EXPAND; double weight_y = EVAS_HINT_EXPAND; ELM_WIN_DATA_GET(data, sd); if (sd->main_menu && efl_gfx_visible_get(sd->main_menu)) evas_object_size_hint_combined_min_get(sd->main_menu, &menuw, NULL); EINA_LIST_FOREACH(p->children, l, opt) { child = opt->obj; efl_gfx_size_hint_weight_get(child, &wx, &wy); if (EINA_DBL_EQ(wx, 0.0)) weight_x = 0; if (EINA_DBL_EQ(wy, 0.0)) weight_y = 0; evas_object_size_hint_combined_min_get(child, &w, &h); if (w > minw) minw = w; if (h > minh) minh = h; } if (minw < menuw) minw = menuw; efl_gfx_size_hint_restricted_min_set(o, EINA_SIZE2D(minw, minh)); evas_object_geometry_get(o, &x, &y, &w, &h); if (w < minw) w = minw; if (h < minh) h = minh; evas_object_resize(o, w, h); EINA_LIST_FOREACH(p->children, l, opt) { child = opt->obj; evas_object_move(child, x, y); evas_object_resize(child, w, h); } efl_gfx_size_hint_weight_set(sd->legacy.edje, weight_x, weight_y); //evas_object_smart_changed(sd->legacy.edje); } static void _elm_win_legacy_init(Efl_Ui_Win_Data *sd) { sd->legacy.edje = edje_object_add(sd->evas); _elm_win_theme_internal(sd->obj, sd); sd->legacy.box = evas_object_box_add(sd->evas); evas_object_box_layout_set(sd->legacy.box, _window_layout_stack, sd->obj, NULL); edje_object_part_swallow(sd->legacy.edje, "elm.swallow.contents", sd->legacy.box); evas_object_move(sd->legacy.edje, 0, 0); evas_object_resize(sd->legacy.edje, 1, 1); if (sd->type != ELM_WIN_FAKE) { edje_object_update_hints_set(sd->legacy.edje, EINA_TRUE); evas_object_event_callback_add(sd->legacy.edje, EVAS_CALLBACK_CHANGED_SIZE_HINTS, _elm_win_on_resize_obj_changed_size_hints, sd->obj); } } EAPI void elm_win_resize_object_add(Eo *obj, Evas_Object *subobj) { Efl_Ui_Win_Data *sd = efl_data_scope_safe_get(obj, MY_CLASS); Eina_Bool ret; if (!sd) return; if (sd->legacy.forbidden) { CRI("Use of this API is forbidden after calling an EO API on this " "window. Fix your code!"); return; } // Little hack for E if (evas_obj_box_count(sd->legacy.box) > 0) sd->single_edje_content = 0; else if (efl_isa(subobj, EFL_CANVAS_LAYOUT_CLASS)) sd->single_edje_content = 1; ret = elm_widget_sub_object_add(obj, subobj); ret &= (evas_object_box_append(sd->legacy.box, subobj) != NULL); if (!ret) ERR("could not add sub object %p to window %p", subobj, obj); } EAPI void elm_win_resize_object_del(Eo *obj, Evas_Object *subobj) { Efl_Ui_Win_Data *sd = efl_data_scope_safe_get(obj, MY_CLASS); Eina_Bool ret; if (!sd) return; if (sd->legacy.forbidden) { CRI("Use of this API is forbidden after calling an EO API on this " "window. Fix your code!"); return; } ret = elm_widget_sub_object_del(obj, subobj); ret &= evas_object_box_remove(sd->legacy.box, subobj); if (!ret) ERR("could not remove sub object %p from window %p", subobj, obj); } EAPI Eina_Bool elm_win_keygrab_set(Elm_Win *obj, const char *key, Evas_Modifier_Mask modifiers EINA_UNUSED, Evas_Modifier_Mask not_modifiers EINA_UNUSED, int priority, Elm_Win_Keygrab_Mode grab_mode) { // Note: Not converting modifiers as they are not used in the implementation return efl_ui_win_keygrab_set(obj, key, EFL_INPUT_MODIFIER_NONE, EFL_INPUT_MODIFIER_NONE, priority, grab_mode); } EAPI Eina_Bool elm_win_keygrab_unset(Elm_Win *obj, const char *key, Evas_Modifier_Mask modifiers EINA_UNUSED, Evas_Modifier_Mask not_modifiers EINA_UNUSED) { // Note: Not converting modifiers as they are not used in the implementation return efl_ui_win_keygrab_unset(obj, key, EFL_INPUT_MODIFIER_NONE, EFL_INPUT_MODIFIER_NONE); } EAPI Eina_Bool elm_win_socket_listen(Efl_Ui_Win *obj, const char *svcname, int svcnum, Eina_Bool svcsys) { return efl_ui_win_socket_listen(obj, svcname, svcnum, svcsys); } EAPI Eina_Bool elm_win_focus_get(const Efl_Ui_Win *obj) { return efl_ui_focus_object_focus_get(obj); } EAPI void elm_win_focus_highlight_style_set(Elm_Win *obj, const char *style) { elm_widget_focus_highlight_style_set(obj, style); } EAPI const char * elm_win_focus_highlight_style_get(const Elm_Win *obj) { return elm_widget_focus_highlight_style_get(obj); } EAPI Eina_Bool elm_win_focus_highlight_enabled_get(const Efl_Ui_Win *obj) { return elm_widget_focus_highlight_enabled_get(obj); } EAPI void elm_win_focus_highlight_enabled_set(Efl_Ui_Win *obj, Eina_Bool enabled) { efl_ui_widget_focus_highlight_enabled_set(obj, enabled); } EAPI void elm_win_focus_highlight_animate_set(Elm_Win *obj, Eina_Bool animate) { efl_ui_widget_focus_highlight_animate_set(obj, animate); } EAPI Eina_Bool elm_win_focus_highlight_animate_get(const Elm_Win *obj) { return efl_ui_widget_focus_highlight_animate_get(obj); } EAPI Eina_Bool elm_win_available_profiles_get(const Elm_Win *obj, char ***profiles, unsigned int *count) { const Eina_Array *ar; if (!efl_isa(obj, MY_CLASS)) return EINA_FALSE; ar = efl_ui_win_wm_available_profiles_get(obj); if (!ar) { if (profiles) *profiles = NULL; if (count) *count = 0; return EINA_FALSE; } if (profiles) *profiles = (char **) ar->data; if (count) *count = ar->count; return EINA_TRUE; } EAPI void elm_win_available_profiles_set(Elm_Win *obj, const char **profiles, unsigned int count) { if (!efl_isa(obj, MY_CLASS)) return; if ((count > 0) && (profiles)) { Eina_Array *ar; unsigned int i; ar = eina_array_new(count); if (ar) { for (i = 0; i < count; i++) eina_array_push(ar, profiles[i]); efl_ui_win_wm_available_profiles_set(obj, ar); eina_array_free(ar); } } else efl_ui_win_wm_available_profiles_set(obj, NULL); } // deprecated EAPI void elm_win_fake_canvas_set(Evas_Object *obj EINA_UNUSED, Ecore_Evas *oee EINA_UNUSED) { ERR("Calling deprecrated function '%s'", __FUNCTION__); } EAPI void elm_win_name_set(Evas_Object *obj, const char *name) { ERR("Calling deprecrated function '%s'", __FUNCTION__); efl_ui_win_name_set(obj, name); } EAPI void elm_win_type_set(Evas_Object *obj, Elm_Win_Type type) { ERR("Calling deprecrated function '%s'", __FUNCTION__); efl_ui_win_type_set(obj, type); } EAPI void elm_win_teamwork_uri_preload(Efl_Ui_Win *obj EINA_UNUSED, const char *uri EINA_UNUSED) { ERR("Calling deprecrated function '%s'", __FUNCTION__); } EAPI void elm_win_teamwork_uri_show(Efl_Ui_Win *obj EINA_UNUSED, const char *uri EINA_UNUSED) { ERR("Calling deprecrated function '%s'", __FUNCTION__); } EAPI void elm_win_teamwork_uri_hide(Efl_Ui_Win *obj EINA_UNUSED) { ERR("Calling deprecrated function '%s'", __FUNCTION__); } EAPI void elm_win_teamwork_uri_open(Efl_Ui_Win *obj EINA_UNUSED, const char *uri EINA_UNUSED) { ERR("Calling deprecrated function '%s'", __FUNCTION__); } /* Internal EO APIs and hidden overrides */ ELM_WIDGET_KEY_DOWN_DEFAULT_IMPLEMENT(efl_ui_win, Efl_Ui_Win_Data) /* Internal EO APIs and hidden overrides */ #define EFL_UI_WIN_EXTRA_OPS \ EFL_CANVAS_GROUP_ADD_DEL_OPS(efl_ui_win), \ ELM_PART_CONTENT_DEFAULT_OPS(efl_ui_win), \ EFL_OBJECT_OP_FUNC(efl_canvas_object_legacy_ctor, _efl_ui_win_efl_canvas_object_legacy_ctor) #include "efl_ui_win.eo.c" diff --git a/src/lib/elementary/efl_ui_win.eo b/src/lib/elementary/efl_ui_win.eo index 462d38e070..26264d84d7 100644 --- a/src/lib/elementary/efl_ui_win.eo +++ b/src/lib/elementary/efl_ui_win.eo @@ -1,848 +1,848 @@ enum Efl.Ui.Win.Type { [[Defines the types of window that can be created These are hints set on a window so that a running Window Manager knows how the window should be handled and/or what kind of decorations it should have. Currently, only the X11 backed engines use them. ]] legacy: efl_ui_win; unknown = -1, [[Default, unknown, type]] basic, [[A normal window. Indicates a normal, top-level window. Almost every window will be created with this type.]] dialog_basic, [[Used for simple dialog windows.]] desktop, [[For special desktop windows, like a background window holding desktop icons.]] dock, [[The window is used as a dock or panel. Usually would be kept on top of any other window by the Window Manager.]] toolbar, [[The window is used to hold a floating toolbar, or similar.]] menu, [[Similar to @.toolbar.]] utility, [[A persistent utility window, like a toolbox or palette.]] splash, [[Splash window for a starting up application.]] dropdown_menu, [[The window is a dropdown menu, as when an entry in a menubar is clicked. This hint exists for completion only, as the EFL way of implementing a menu would not normally use a separate window for its contents.]] popup_menu, [[Like @.dropdown_menu, but for the menu triggered by right-clicking an object.]] tooltip, [[The window is a tooltip. A short piece of explanatory text that typically appear after the mouse cursor hovers over an object for a while. Typically not very commonly used in the EFL.]] notification, [[A notification window, like a warning about battery life or a new E-Mail received.]] combo, [[A window holding the contents of a combo box. Not usually used in the EFL.]] /* Some types have been removed from EO. Skipping to 17. */ naviframe_basic = 17 [[Used for naviframe style replacement with a back button instead of a close button. @since 1.19 ]] } enum Efl.Ui.Win.Keyboard_Mode { [[The different layouts that can be requested for the virtual keyboard. When the application window is being managed by Illume it may request any of the following layouts for the virtual keyboard. ]] legacy: efl_ui_win_keyboard; unknown, [[Unknown keyboard state]] off, [[Request to deactivate the keyboard]] on, [[Enable keyboard with default layout]] alpha, [[Alpha (a-z) keyboard layout]] numeric, [[Numeric keyboard layout]] pin, [[PIN keyboard layout]] phone_number, [[Phone keyboard layout]] hex, [[Hexadecimal numeric keyboard layout]] terminal, [[Full (QWERTY) keyboard layout]] password, [[Password keyboard layout]] ip, [[IP keyboard layout]] host, [[Host keyboard layout]] file, [[File keyboard layout]] url, [[URL keyboard layout]] keypad, [[Keypad layout]] j2me [[J2ME keyboard layout]] } enum Efl.Ui.Win.Indicator_Mode { [[Defines the type indicator that can be shown]] legacy: efl_ui_win_indicator; off, [[Request to deactivate the indicator]] bg_opaque, [[The indicator icon is opaque, as is the indicator background. The content of window is located at the end of the indicator. The area of indicator and window content are not overlapped]] bg_transparent, [[The icon of indicator is opaque, but the background is transparent. The content of window is located under the indicator in Z-order. The area of indicator and window content are overlapped]] hidden [[The indicator is hidden so user can see only the content of window such as in video mode. If user flicks the upper side of window, the indicator is shown temporarily.]] } enum Efl.Ui.Win.Keygrab_Mode { [[Define the keygrab modes of window. A window may send commands to the Keyrouter according this mode, and perform different actions.]] legacy: efl_ui_win_keygrab; unknown = 0, [[Unknown keygrab mode]] shared = (1 << 8), [[Get the grabbed-key together with the other client windows]] topmost = (1 << 9), [[Get the grabbed-key only when window is top of the stack]] exclusive = (1 << 10), [[Get the grabbed-key exclusively regardless of window's position]] override_exclusive = (1 << 11) [[Get the grabbed-key exclusively regardless of window's position. This is overrided by grabs from the other client window]] } enum Efl.Ui.Win.Modal_Mode { [[Defines the mode of a modal window]] legacy: efl_ui_win_modal; none, [[The window is not modal window.]] modal [[The window is modal window.]] } enum Efl.Ui.Win.Urgent_Mode { [[Defines the mode of a urgent window.]] legacy: efl_ui_win_urgent; none, [[The window is not a urgent window.]] urgent [[The window is a urgent window.]] } enum Efl.Ui.Win.Move_Resize_Mode { [[Define the move or resize mode of window. The user can request the display server to start moving or resizing the window by combining these modes. However only limited combinations are allowed. Currently, only the following 9 combinations are permitted. More combinations may be added in future: 1. move, 2. top, 3. bottom, 4. left, 5. right, 6. top | left, 7. top | right, 8. bottom | left, 9. bottom | right. @since 1.19 ]] legacy: efl_ui_win_move_resize; move = 1, [[Start moving window]] top = (1 << 1), [[Start resizing window to the top]] bottom = (1 << 2), [[Start resizing window to the bottom]] left = (1 << 3), [[Start resizing window to the left]] right = (1 << 4) [[Start resizing window to the right]] } class Efl.Ui.Win (Efl.Ui.Widget, Efl.Canvas, Efl.Canvas.Pointer, Efl.Access.Window, Efl.Access.Component, Efl.Access.Widget.Action, Efl.Content, Efl.Input.State, Efl.Input.Interface, Efl.Screen, Efl.Gfx.Size.Hint, Efl.Text, Efl.Config.Global, Efl.Part, Efl.Ui.Focus.Manager ) { [[Efl UI window class]] legacy_prefix: elm_win; methods { @property indicator_mode { [[In some environments you may have an indicator that shows battery status, reception, time etc. This is the indicator. Sometimes you don't want this because you provide the same functionality inside your app, so this will request that the indicator is disabled in such circumstances. The default settings depends on the environment. For example, on phones, the default is to enable the indicator. The indicator is disabled on devices like televisions however. @since 1.18 ]] set { legacy: null; } get { legacy: null; } values { type: Efl.Ui.Win.Indicator_Mode; [[The mype, one of #Efl_Ui_Win_Indicator_Mode.]] } } @property keyboard_mode { set { [[Sets the keyboard mode of the window.]] } get { [[Get the keyboard mode of the window.]] } values { mode: Efl.Ui.Win.Keyboard_Mode; [[The mode, one of #Efl_Ui_Win_Keyboard_Mode.]] } } @property wm_available_rotations { [[Defines which rotations this window supports. The window manager will refer to these hints and rotate the window accordingly, depending on the device orientation, for instance. ]] values { allow_0: bool; [[Normal orientation.]] allow_90: bool; [[Rotated 90 degrees CCW.]] allow_180: bool; [[Rotated 180 degrees.]] allow_270: bool; [[Rotated 270 degrees CCW (i.e. 90 CW).]] } set { legacy: null; } get { legacy: null; return: bool; [[Returns $false if available rotations were not specified.]] } } @property wm_available_profiles { [[Available profiles on a window.]] set { legacy: null; } get { legacy: null; } values { profiles: const(array); [[A list of profiles.]] } } @property screen_constrain { set { [[Constrain the maximum width and height of a window to the width and height of the screen. When $constrain is $true, $obj will never resize larger than the screen. ]] } get { [[Get the constraints on the maximum width and height of a window relative to the width and height of the screen. When this function returns $true, $obj will never resize larger than the screen. ]] } values { constrain: bool; [[$true to restrict the window's maximum size.]] } } @property prop_focus_skip { set { [[Set the window to be skipped by keyboard focus. This sets the window to be skipped by normal keyboard input. This means a window manager will be asked not to focus this window as well as omit it from things like the taskbar, pager, "alt-tab" list etc. etc. Call this and enable it on a window BEFORE you show it for the first time, otherwise it may have no effect. Use this for windows that have only output information or might only be interacted with by the mouse or touchscreen, never for typing. This may have side-effects like making the window non-accessible in some cases unless the window is specially handled. Use this with care. ]] } values { skip: bool; [[The skip flag state ($true if it is to be skipped).]] } } @property autohide { [[Window's autohide state. When closing the window in any way outside of the program control, like pressing the X button in the titlebar or using a command from the Window Manager, a "delete,request" signal is emitted to indicate that this event occurred and the developer can take any action, which may include, or not, destroying the window object. When this property is set to $true, the window will be automatically hidden when this event occurs, after the signal is emitted. If this property is $false nothing will happen, beyond the event emission. C applications can use this option along with the quit policy $ELM_POLICY_QUIT_LAST_WINDOW_HIDDEN which allows exiting EFL's main loop when all the windows are hidden. Note: $autodel and $autohide are not mutually exclusive. The window will be deleted if both are set to $true. ]] values { autohide: bool; [[If $true, the window will automatically hide itself when closed.]] } } @property icon_object { set { [[Set a window object's icon. This sets an image to be used as the icon for the given window, in the window manager decoration part. The exact pixel dimensions of the object (not object size) will be used and the image pixels will be used as-is when this function is called. If the image object has been updated, then call this function again to source the image pixels and place them in the window's icon. Note that only objects of type @Efl.Canvas.Image or @Efl.Ui.Image are allowed. ]] values { icon: Efl.Canvas.Object @nullable; [[The image object to use for an icon.]] } } get { [[Get the icon object used for the window. The object returns is the one marked by @.icon_object.set as the object to use for the window icon. ]] values { icon: const(Efl.Canvas.Object) @nullable; [[The Evas image object to use for an icon.]] } } } @property iconified { set { [[Set the iconified state of a window.]] } get { [[Get the iconified state of a window.]] } values { iconified: bool; [[If $true, the window is iconified.]] } } @property maximized { set { [[Set the maximized state of a window.]] } get { [[Get the maximized state of a window.]] } values { maximized: bool; [[If $true, the window is maximized.]] } } @property fullscreen { set { [[Set the fullscreen state of a window.]] } get { [[Get the fullscreen state of a window.]] } values { fullscreen: bool; [[If $true, the window is fullscreen.]] } } @property sticky { set { [[Set the sticky state of the window. Hints the Window Manager that the window in $obj should be left fixed at its position even when the virtual desktop it's on moves or changes. ]] } get { [[Get the sticky state of the window.]] } values { sticky: bool; [[If $true, the window's sticky state is enabled.]] } } @property urgent { set { [[Set the urgent state of a window.]] legacy: null; } get { [[Get the urgent state of a window.]] legacy: null; } values { urgent: Efl.Ui.Win.Urgent_Mode; [[The mode of a urgent window, one of #Efl_Ui_Win_Urgent_Mode.]] } } @property modal { set { [[Set the modal state of a window.]] legacy: null; } get { [[Get the modal state of a window.]] legacy: null; } values { modal: Efl.Ui.Win.Modal_Mode; [[The mode of a window, one of #Efl_Ui_Win_Modal_Mode.]] } } @property noblank { set { [[Set the noblank property of a window. The "noblank" property is a way to request the display on which the window is shown does not blank, go to screensaver or otherwise hide or obscure the window. It is intended for uses such as media playback on a television where a user may not want to be interrupted by an idle screen. The noblank property may have no effect if the window is iconified/minimized or hidden. @since 1.11 ]] } get { [[Get the noblank property of a window. @since 1.11 ]] } values { noblank: bool; [[If $true, the window is set to noblank.]] } } @property borderless { set { [[Set the borderless state of a window. This function requests the Window Manager not to draw any decoration around the window. ]] } get { [[Get the borderless state of a window.]] } values { borderless: bool; [[If $true, the window is borderless.]] } } @property win_role { /* FIXME: Do we actually need this? There is only support in X. */ [[The role of the window. It is a hint of how the Window Manager should handle it. Unlike @.win_type and @.win_name this can be changed at runtime. ]] set { [[Set the role of the window.]] legacy: elm_win_role_set; } get { [[Get the role of the window. The returned string is an internal one and should not be freed or modified. It will also be invalid if a new role is set or if the window is destroyed. ]] legacy: elm_win_role_get; } values { role: string; [[The role to set.]] } } @property win_name { [[The window name. The meaning of name depends on the underlying windowing system. The window name is a construction property that can only be set at creation time, before finalize. In C this means inside $efl_add(). Note: Once set, it cannot be modified afterwards. ]] set { [[Name can only be set before finalize.]] legacy: null; } get { [[ @since 1.18 ]] legacy: elm_win_name_get; } values { name: string @nullable; [[Window name]] } } @property win_type { [[The type of the window. It is a hint of how the Window Manager should handle it. The window type is a construction property that can only be set at creation time, before finalize. In C this means inside $efl_add(). Note: Once set, it cannot be modified afterward. ]] set { [[Type can on be set before finalize.]] legacy: null; } get { [[If the object is not window object, returns $unknown.]] legacy: elm_win_type_get; } values { type: Efl.Ui.Win.Type(Efl.Ui.Win.Type.unknown); [[Window type]] } } @property accel_preference { [[The hardware acceleration preference for this window. This is a constructor function and can only be called before @Efl.Object.finalize. This property overrides the global EFL configuration option "accel_preference" for this single window, and accepts the same syntax. The $accel string is a freeform C string that indicates what kind of acceleration is preferred. Here "acceleration" generally refers to rendering and the hardware with which the unit application renders GUIs. This may or may not be honored but a best attempt will be made. Known strings are as follows: "gl", "opengl" - try use OpenGL. "3d" - try to use a 3d acceleration unit. "hw", "hardware", "accel" - try any acceleration unit (best possible) "none" - use no acceleration but software instead (since 1.16) Since 1.14, it is also possible to specify some GL properties for the GL window surface. This allows applications to use GLView with depth, stencil and MSAA buffers with direct rendering. The new accel preference string format is thus "{HW Accel}[:depth{value}[:stencil{value}[:msaa{str}$]$]$]". Accepted values for depth are for instance "depth", "depth16", "depth24". Accepted values for stencil are "stencil", "stencil1", "stencil8". For MSAA, only predefined strings are accepted: "msaa", "msaa_low", "msaa_mid" and "msaa_high". The selected configuration is not guaranteed and is only valid in case of GL acceleration. Only the base acceleration string will be saved (e.g. "gl" or "hw"). Full examples include: "gl", - try to use OpenGL "hw:depth:stencil", - use HW acceleration with default depth and stencil buffers "opengl:depth24:stencil8:msaa_mid" - use OpenGL with 24-bit depth, 8-bit stencil and a medium number of MSAA samples in the backbuffer. Note that this option may be overriden by environment variables or the configuration option "accel_preference_override". @since 1.18 ]] values { accel: string; [[Acceleration]] } set { legacy: null; } get { [[This will return the value of "accel_preference" when the window was created.]] } } @property alpha { set { [[Set the alpha channel state of a window. If $alpha is true, the alpha channel of the canvas will be enabled possibly making parts of the window completely or partially transparent. This is also subject to the underlying system supporting it, for example a system using a compositing manager. Note: Alpha window can be enabled automatically by window theme style's property. If "alpha" data.item is "1" or "true" in window style(eg. elm/win/base/default), the window is switched to alpha automatically without the explicit api call. ]] } get { [[Get the alpha channel state of a window.]] } values { alpha: bool; [[$true if the window alpha channel is enabled, $false otherwise.]] } } @property stack_id @beta { get { [[Get the stack ID string of the window as an opaque string. This ID is immutable and can never be modified. It will be an opaque string that has no specific defined format or content other than being a string (no character with a value of 0). This string is intended for use as a stack master ID to be use by other windows to make this window part of a stack of windows to be placed on top of each other as if they are a series of dialogs or questions one after the other, allowing you to go back through history.]] } values { id: string; [[An opaque string that has no specific format but identifies a specific unique window on the display.]] } } @property stack_master_id @beta { set { [[Set the window stack ID to use as the master top-level. This sets the ID string to be used as the master top-level window as the base of a stack of windows. This must be set before the first time the window is shown and should never be changed afterwards.]] } get { [[Get the stack master Id that has been set.]] } values { id: string; [[An opaque string that has no specific format, but identifies a specific unique window on the display.]] } } @property stack_base @beta { [[The stack base state of this window This is a boolean flag that determines if this window will become the base of a stack at all. You must enable this on a base (the bottom of a window stack) for things to work correctly. This state should be set before a window is shown for the first time and never changed afterwards.]] set {} get {} values { base: bool; [[$true if this is a stack base window, $false otherwise.]] } } stack_pop_to @beta { [[Pop (delete) all windows in the stack above this window. This will try and delete all the windows in the stack that are above the window.]] } activate { [[Activate a window object. This function sends a request to the Window Manager to activate the window pointed by $obj. If honored by the WM, the window will receive the keyboard focus. Note: This is just a request that a Window Manager may ignore, so calling this function does not ensure in any way that the window will be the active one afterwards. ]] } center { [[Center a window on the screen. This function centers window $obj horizontally and/or vertically based on the values of $h and $v. Note: This is just a request that a Window Manager may ignore, so calling this function does not ensure in any way that the window will be centered afterwards. ]] params { @in h: bool; [[If $true, center horizontally. If $false, do not change horizontal location.]] @in v: bool; [[If $true, center vertically. If $false, do not change vertical location.]] } } keygrab_set { [[Set keygrab value of the window This function grabs the $key of window using $grab_mode. ]] return: bool; [[$true on success, $false otherwise]] params { @in key: string; [[This string is the keyname to grab.]] @in modifiers: Efl.Input.Modifier; [[A combinaison of modifier keys that must be present to trigger the event. Not supported yet.]] @in not_modifiers: Efl.Input.Modifier; [[A combinaison of modifier keys that must not be present to trigger the event. Not supported yet.]] @in priority: int; [[Not supported yet.]] @in grab_mode: Efl.Ui.Win.Keygrab_Mode; [[Describes how the key should be grabbed, wrt. focus and stacking.]] } legacy: null; } keygrab_unset { [[Unset keygrab value of the window This function unset keygrab value. Ungrab $key of window. ]] return: bool; [[$true on success, $false otherwise]] params { @in key: string; [[This string is the keyname to grab.]] @in modifiers: Efl.Input.Modifier; [[A combinaison of modifier keys that must be present to trigger the event. Not supported yet.]] @in not_modifiers: Efl.Input.Modifier; [[A combinaison of modifier keys that must not be present to trigger the event. Not supported yet.]] } legacy: null; } move_resize_start { [[Start moving or resizing the window. The user can request the display server to start moving or resizing the window by combining modes from @Efl.Ui.Win.Move_Resize_Mode. This API can only be called if none of the following conditions is true: 1. Called in the absence of a pointer down event, 2. Called more than once before a pointer up event, 3. Called when the window is already being resized or moved, 4. Called with an unsupported combination of modes. Right usage: 1. Pointer (mouse or touch) down event, 2. @.move_resize_start called only once with a supported mode, 3. Pointer (mouse or touch) up event. If a pointer up event occurs after calling the function, it automatically ends the window move and resize operation. Currently, only the following 9 combinations are allowed, and possibly more combinations may be added in the future: 1. @Efl.Ui.Win.Move_Resize_Mode.move 2. @Efl.Ui.Win.Move_Resize_Mode.top 3. @Efl.Ui.Win.Move_Resize_Mode.bottom 4. @Efl.Ui.Win.Move_Resize_Mode.left 5. @Efl.Ui.Win.Move_Resize_Mode.right 6. @Efl.Ui.Win.Move_Resize_Mode.top | @Efl.Ui.Win.Move_Resize_Mode.left 7. @Efl.Ui.Win.Move_Resize_Mode.top | @Efl.Ui.Win.Move_Resize_Mode.right 8. @Efl.Ui.Win.Move_Resize_Mode.bottom | @Efl.Ui.Win.Move_Resize_Mode.left 9. @Efl.Ui.Win.Move_Resize_Mode.bottom | @Efl.Ui.Win.Move_Resize_Mode.right In particular move and resize cannot happen simultaneously. Note: the result of this API can only guarantee that the request has been forwarded to the server, but there is no guarantee that the request can be processed by the display server. @since 1.19 ]] return: bool; [[$true if the request was successfully sent to the display server, $false in case of error.]] params { @in mode: Efl.Ui.Win.Move_Resize_Mode; [[The requested move or resize mode.]] } } } implements { class.constructor; Efl.Object.constructor; Efl.Object.destructor; Efl.Object.finalize; Efl.Object.debug_name_override; Efl.Gfx.visible { set; } Efl.Gfx.position { set; } Efl.Gfx.size { set; } Efl.Gfx.Stack.raise; [[Places the window at the top of the stack, so that it's not covered by any other window.]] Efl.Gfx.Stack.lower; [[This action is ignored by the Window.]] Efl.Ui.Widget.theme_apply; Efl.Ui.Focus.Object.focus { get; } Efl.Ui.Widget.focus_highlight_style { get; set; } Efl.Ui.Widget.focus_highlight_enabled { get; set; } Efl.Ui.Widget.focus_highlight_animate { get; set; } - Efl.Ui.Widget.on_focus_update; + Efl.Ui.Focus.Object.on_focus_update;