#include #include "e_mod_main.h" #include char *strptime(const char *s, const char *format, struct tm *tm); #define FORECASTS 2 #define KM_TO_MI 1.609344 #define MB_TO_IN 33.864 #define GOLDEN_RATIO 1.618033989 #define ENABLE_DEBUG 0 #define DEBUG(f, ...) if (ENABLE_DEBUG) \ printf("[forecasts] "f "\n", __VA_ARGS__) static E_Gadcon_Client *_gc_init(E_Gadcon *gc, const char *name, const char *id, const char *style); static void _gc_shutdown(E_Gadcon_Client *gcc); static void _gc_orient(E_Gadcon_Client *gcc, E_Gadcon_Orient orient); static const char *_gc_label(const E_Gadcon_Client_Class *client_class); static Evas_Object *_gc_icon(const E_Gadcon_Client_Class *client_class, Evas *evas); static const char *_gc_id_new(const E_Gadcon_Client_Class *client_class); static E_Config_DD *conf_edd = NULL; static E_Config_DD *conf_item_edd = NULL; Config *forecasts_config = NULL; static const E_Gadcon_Client_Class _gadcon_class = { GADCON_CLIENT_CLASS_VERSION, "forecasts", {_gc_init, _gc_shutdown, _gc_orient, _gc_label, _gc_icon, _gc_id_new, NULL, NULL}, E_GADCON_CLIENT_STYLE_PLAIN }; typedef struct _Instance Instance; typedef struct _Forecasts Forecasts; struct _Instance { E_Gadcon_Client *gcc; Evas_Object *forecasts_obj; Forecasts *forecasts; Ecore_Timer *check_timer; Ecore_Con_Url *url; Ecore_Event_Handler *url_data_handler; Ecore_Event_Handler *url_complete_handler; const char *update_time; struct { int temp; char update[52]; char desc[256]; char code[128]; } condition; struct { char temp, distance[3], pressure[3], speed[5]; } units; struct { struct { int chill, direction, speed; Eina_Bool can_chill; } wind; struct { int humidity; float pressure; } atmosphere; } details; struct { char day[4]; char date[12]; int low, high; char code[128]; char desc[256]; } forecast[FORECASTS]; Eina_Strbuf *buffer; const char *location; const char *area; E_Gadcon_Popup *popup; Config_Item *ci; }; struct _Forecasts { Instance *inst; Evas_Object *forecasts_obj; Evas_Object *icon_obj; }; /* Module Function Protos */ static void _forecasts_cb_mouse_up(void *data, Evas *e, Evas_Object *obj, void *event_info); static void _forecasts_menu_cb_configure(void *data, E_Menu *m, E_Menu_Item *mi); static void _forecasts_menu_cb_post(void *data, E_Menu *m); static Eina_Bool _forecasts_cb_check(void *data); static Config_Item *_forecasts_config_item_get(const char *id); static Forecasts *_forecasts_new(Evas *evas); static void _forecasts_free(Forecasts *w); static Eina_Bool _forecasts_url_data(void *data, int type, void *event); static Eina_Bool _forecasts_url_complete(void *data, int type, void *event); static int _forecasts_parse(void *data); static void _forecasts_converter(Instance *inst); static void _forecasts_convert_degrees(int *value, int dir); static void _forecasts_convert_distances(int *value, int dir); static void _forecasts_convert_pressures(float *value, int dir); static void _forecasts_display_set(Instance *inst, int ok); static void _forecasts_popup_content_create(Instance *inst); static void _cb_mouse_up(void *data, Evas *e, Evas_Object *obj, void *event_info); static Evas_Object *_forecasts_popup_icon_create(Evas *evas, const char *code); static void _forecasts_popup_destroy(Instance *inst); /* Gadcon Functions */ static E_Gadcon_Client * _gc_init(E_Gadcon *gc, const char *name, const char *id, const char *style) { Evas_Object *o; E_Gadcon_Client *gcc; Forecasts *w; Instance *inst; inst = E_NEW(Instance, 1); inst->ci = _forecasts_config_item_get(id); inst->area = eina_stringshare_add(inst->ci->location); inst->buffer = eina_strbuf_new(); w = _forecasts_new(gc->evas); w->inst = inst; inst->forecasts = w; o = w->forecasts_obj; gcc = e_gadcon_client_new(gc, name, id, style, o); gcc->data = inst; inst->gcc = gcc; inst->popup = NULL; inst->forecasts_obj = o; evas_object_event_callback_add(inst->forecasts_obj, EVAS_CALLBACK_MOUSE_UP, _cb_mouse_up, inst); if (!inst->url_data_handler) inst->url_data_handler = ecore_event_handler_add(ECORE_CON_EVENT_URL_DATA, _forecasts_url_data, inst); if (!inst->url_complete_handler) inst->url_complete_handler = ecore_event_handler_add(ECORE_CON_EVENT_URL_COMPLETE, _forecasts_url_complete, inst); evas_object_event_callback_add(w->forecasts_obj, EVAS_CALLBACK_MOUSE_UP, _forecasts_cb_mouse_up, inst); forecasts_config->instances = eina_list_append(forecasts_config->instances, inst); _forecasts_cb_check(inst); inst->check_timer = ecore_timer_add(inst->ci->poll_time, _forecasts_cb_check, inst); return gcc; } static void _gc_shutdown(E_Gadcon_Client *gcc) { Instance *inst; Forecasts *w; inst = gcc->data; w = inst->forecasts; if (inst->popup) _forecasts_popup_destroy(inst); if (inst->check_timer) ecore_timer_del(inst->check_timer); if (inst->url_data_handler) ecore_event_handler_del(inst->url_data_handler); if (inst->url_complete_handler) ecore_event_handler_del(inst->url_complete_handler); if (inst->url) ecore_con_url_free(inst->url); if (inst->area) eina_stringshare_del(inst->area); eina_strbuf_free(inst->buffer); if (inst->update_time) eina_stringshare_del(inst->update_time); inst->url = NULL; forecasts_config->instances = eina_list_remove(forecasts_config->instances, inst); evas_object_event_callback_del(w->forecasts_obj, EVAS_CALLBACK_MOUSE_UP, _forecasts_cb_mouse_up); _forecasts_free(w); E_FREE(inst); } static void _gc_orient(E_Gadcon_Client *gcc, E_Gadcon_Orient orient) { Instance *inst; inst = gcc->data; switch (orient) { case E_GADCON_ORIENT_FLOAT: edje_object_signal_emit(inst->forecasts_obj, "e,state,orientation,float", "e"); e_gadcon_client_aspect_set(gcc, 240, 120); e_gadcon_client_min_size_set(gcc, 240, 120); break; default: edje_object_signal_emit(inst->forecasts_obj, "e,state,orientation,default", "e"); e_gadcon_client_aspect_set(gcc, 16, 16); e_gadcon_client_min_size_set(gcc, 16, 16); break; } } static const char * _gc_label(const E_Gadcon_Client_Class *client_class EINA_UNUSED) { return D_("Forecasts"); } static Evas_Object * _gc_icon(const E_Gadcon_Client_Class *client_class EINA_UNUSED, Evas *evas) { Evas_Object *o; char buf[4096]; o = edje_object_add(evas); snprintf(buf, sizeof(buf), "%s/e-module-forecasts.edj", e_module_dir_get(forecasts_config->module)); edje_object_file_set(o, buf, "icon"); return o; } static const char * _gc_id_new(const E_Gadcon_Client_Class *client_class EINA_UNUSED) { Config_Item *ci; ci = _forecasts_config_item_get(NULL); return ci->id; } static void _forecasts_cb_mouse_up(void *data, Evas *e EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event_info) { Instance *inst = data; Evas_Event_Mouse_Up *ev = event_info; if (ev->event_flags & EVAS_EVENT_FLAG_ON_HOLD) return; if ((ev->button == 3) && (!forecasts_config->menu)) { E_Menu *m; E_Menu_Item *mi; int x, y, w, h; m = e_menu_new(); mi = e_menu_item_new(m); e_menu_item_label_set(mi, D_("Settings")); e_util_menu_item_theme_icon_set(mi, "preferences-system"); e_menu_item_callback_set(mi, _forecasts_menu_cb_configure, inst); m = e_gadcon_client_util_menu_items_append(inst->gcc, m, 0); e_menu_post_deactivate_callback_set(m, _forecasts_menu_cb_post, inst); forecasts_config->menu = m; e_gadcon_canvas_zone_geometry_get(inst->gcc->gadcon, &x, &y, &w, &h); e_menu_activate_mouse(m, e_zone_current_get(), x + ev->output.x, y + ev->output.y, 1, 1, E_MENU_POP_DIRECTION_DOWN, ev->timestamp); evas_event_feed_mouse_up(inst->gcc->gadcon->evas, ev->button, EVAS_BUTTON_NONE, ev->timestamp, NULL); } } static void _forecasts_menu_cb_post(void *data EINA_UNUSED, E_Menu *m EINA_UNUSED) { if (!forecasts_config->menu) return; e_object_del(E_OBJECT(forecasts_config->menu)); forecasts_config->menu = NULL; } static void _forecasts_menu_cb_configure(void *data, E_Menu *m EINA_UNUSED, E_Menu_Item *mi EINA_UNUSED) { Instance *inst; inst = data; _config_forecasts_module(inst->ci); } static Config_Item * _forecasts_config_item_get(const char *id) { Eina_List *l; Config_Item *ci; char buf[128]; if (!id) { int num = 0; /* Create id */ if (forecasts_config->items) { const char *p; ci = eina_list_last(forecasts_config->items)->data; p = strrchr(ci->id, '.'); if (p) num = strtol(p + 1, NULL, 10) + 1; } snprintf(buf, sizeof(buf), "%s.%d", _gadcon_class.name, num); id = buf; } else { for (l = forecasts_config->items; l; l = l->next) { ci = l->data; if (!ci->id) continue; if (!strcmp(ci->id, id)) return ci; } } ci = E_NEW(Config_Item, 1); ci->id = eina_stringshare_add(id); ci->poll_time = 60.0; ci->degrees = DEGREES_C; ci->lat = ci->lon = 0; ci->show_text = 1; ci->popup_on_hover = 1; forecasts_config->items = eina_list_append(forecasts_config->items, ci); return ci; } /* Gadman Module Setup */ E_API E_Module_Api e_modapi = { E_MODULE_API_VERSION, "Forecasts" }; E_API void * e_modapi_init(E_Module *m) { Eina_List *l; Config_Item *ci; ecore_con_url_init(); bindtextdomain(PACKAGE, LOCALE_DIR); bind_textdomain_codeset(PACKAGE, "UTF-8"); conf_item_edd = E_CONFIG_DD_NEW("Forecasts_Config_Item", Config_Item); #undef T #undef D #define T Config_Item #define D conf_item_edd E_CONFIG_VAL(D, T, id, STR); E_CONFIG_VAL(D, T, poll_time, DOUBLE); E_CONFIG_VAL(D, T, degrees, INT); E_CONFIG_VAL(D, T, show_text, INT); E_CONFIG_VAL(D, T, popup_on_hover, INT); E_CONFIG_VAL(D, T, location_id, INT); E_CONFIG_VAL(D, T, location, STR); E_CONFIG_VAL(D, T, lat, STR); E_CONFIG_VAL(D, T, lon, STR); conf_edd = E_CONFIG_DD_NEW("Forecasts_Config", Config); #undef T #undef D #define T Config #define D conf_edd E_CONFIG_LIST(D, T, items, conf_item_edd); forecasts_config = e_config_domain_load("module.forecasts", conf_edd); if (!forecasts_config) { forecasts_config = E_NEW(Config, 1); ci = E_NEW(Config_Item, 1); ci->poll_time = 60.0; ci->degrees = DEGREES_C; ci->lat = ci->lon = 0; ci->id = eina_stringshare_add("0"); ci->show_text = 1; ci->popup_on_hover = 1; forecasts_config->items = eina_list_append(forecasts_config->items, ci); } EINA_LIST_FOREACH(forecasts_config->items, l, ci) { if (ci->poll_time < 60.0) ci->poll_time = 60.0; else if (ci->poll_time > 3600.0) ci->poll_time = 3600.0; } forecasts_config->module = m; e_gadcon_provider_register(&_gadcon_class); return m; } E_API int e_modapi_shutdown(E_Module *m EINA_UNUSED) { forecasts_config->module = NULL; e_gadcon_provider_unregister(&_gadcon_class); if (forecasts_config->config_dialog) e_object_del(E_OBJECT(forecasts_config->config_dialog)); if (forecasts_config->menu) { e_menu_post_deactivate_callback_set(forecasts_config->menu, NULL, NULL); e_object_del(E_OBJECT(forecasts_config->menu)); forecasts_config->menu = NULL; } while (forecasts_config->items) { Config_Item *ci; ci = forecasts_config->items->data; if (ci->id) eina_stringshare_del(ci->id); if (ci->location) eina_stringshare_del(ci->location); forecasts_config->items = eina_list_remove_list(forecasts_config->items, forecasts_config->items); free(ci); ci = NULL; } E_FREE(forecasts_config); E_CONFIG_DD_FREE(conf_item_edd); E_CONFIG_DD_FREE(conf_edd); ecore_con_url_shutdown(); return 1; } E_API int e_modapi_save(E_Module *m EINA_UNUSED) { e_config_domain_save("module.forecasts", conf_edd, forecasts_config); return 1; } static Forecasts * _forecasts_new(Evas *evas) { Forecasts *w; char buf[4096]; w = E_NEW(Forecasts, 1); w->forecasts_obj = edje_object_add(evas); snprintf(buf, sizeof(buf), "%s/forecasts.edj", e_module_dir_get(forecasts_config->module)); if (!e_theme_edje_object_set(w->forecasts_obj, "base/theme/modules/forecasts", "modules/forecasts/main")) edje_object_file_set(w->forecasts_obj, buf, "modules/forecasts/main"); evas_object_show(w->forecasts_obj); w->icon_obj = edje_object_add(evas); if (!e_theme_edje_object_set(w->icon_obj, "base/theme/modules/forecasts/icons", "modules/forecasts/icons/3200")) edje_object_file_set(w->icon_obj, buf, "modules/forecasts/icons/3200"); edje_object_part_swallow(w->forecasts_obj, "icon", w->icon_obj); return w; } static void _forecasts_free(Forecasts *w) { char name[60]; int i; for (i = 0; i < FORECASTS; i++) { Evas_Object *swallow; snprintf(name, sizeof(name), "e.swallow.day%d.icon", i); swallow = edje_object_part_swallow_get(w->forecasts_obj, name); if (swallow) evas_object_del(swallow); } evas_object_del(w->forecasts_obj); evas_object_del(w->icon_obj); free(w); w = NULL; } static Eina_Bool _forecasts_cb_check(void *data) { Instance *inst = data; char buf[256]; /* if we have a previous server, delete it */ if (inst->url) ecore_con_url_free(inst->url); snprintf(buf, sizeof(buf), "https://www.enlightenment.org/weather.php?lat=%s&lon=%s", inst->ci->lat, inst->ci->lon); DEBUG("FC: URL: %s", buf); inst->url = ecore_con_url_new(buf); if (!inst->url) return EINA_FALSE; ecore_con_url_http_version_set(inst->url, ECORE_CON_URL_HTTP_VERSION_1_0); ecore_con_url_get(inst->url); return EINA_TRUE; } static Eina_Bool _forecasts_url_data(void *data, int type EINA_UNUSED, void *event) { Instance *inst = data; Ecore_Con_Event_Url_Data *ev = event; if ((!inst->url) || (inst->url != ev->url_con)) return EINA_TRUE; eina_strbuf_append_length(inst->buffer, (const char *)ev->data, ev->size); return EINA_FALSE; } static Eina_Bool _forecasts_url_complete(void *data, int type EINA_UNUSED, void *event) { Instance *inst = data; Ecore_Con_Event_Url_Complete *ev = event; int ret; if ((!inst->url) || (inst->url != ev->url_con)) return EINA_TRUE; DEBUG("FC: status: %i %s", ev->status, eina_strbuf_string_get(inst->buffer)); ecore_con_url_free(inst->url); inst->url = NULL; eina_stringshare_replace(&inst->location, inst->ci->location); ret = _forecasts_parse(inst); _forecasts_converter(inst); _forecasts_display_set(inst, ret); eina_strbuf_string_free(inst->buffer); return EINA_FALSE; } static void _timestamp_local(const char *timestamp, char *buf, size_t len) { struct tm tm_in; struct tm *tm_out; time_t t; memset(&tm_in, 0, sizeof(struct tm)); strptime(timestamp, "%Y-%m-%dT%H:%M:%SZ", &tm_in); t = mktime(&tm_in); tm_out = localtime(&t); strftime(buf, len - 1, "%Y-%m-%d %H:%M:%S", tm_out); } static struct tm * _timestamp_time(const char *timestamp) { struct tm tm_in; struct tm *tm_out; time_t t; memset(&tm_in, 0, sizeof(struct tm)); strptime(timestamp, "%Y-%m-%dT%H:%M:%SZ", &tm_in); t = mktime(&tm_in); tm_out = localtime(&t); return tm_out; } static int _epoch_days(struct tm *tm_in) { time_t t = mktime(tm_in); return t / 86400; } static int _forecasts_parse(void *data) { Instance *inst = data; const char *result; const char *timestamp, *code; time_t now; struct tm *tm_local, *tm_data; int i, len, days_prev, idx = 0; static char *weekdays[7] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", }; if (!inst->buffer) return 0; for (int i = 0; i < FORECASTS; i++) { inst->forecast[i].high = -237.15; inst->forecast[i].low = 190.0; } now = time(NULL); tm_local = localtime(&now); days_prev = _epoch_days(tm_local); result = eina_strbuf_string_get(inst->buffer); if (!result) goto error; json_object *o_root = json_tokener_parse(result); if (!o_root) goto error; json_object *o_properties = json_object_object_get(o_root, "properties"); if (!o_properties) goto error; json_object *o_meta = json_object_object_get(o_properties, "meta"); if (!o_meta) goto error; json_object *o_updated = json_object_object_get(o_meta, "updated_at"); if (!o_updated) goto error; timestamp = json_object_get_string(o_updated); if (!timestamp) goto error; eina_stringshare_replace(&inst->update_time, timestamp); json_object *o_timeseries = json_object_object_get(o_properties, "timeseries"); if (!o_timeseries) goto error; if (json_object_get_type(o_timeseries) != json_type_array) goto error; inst->units.temp = 'C'; snprintf(inst->units.distance, 3, "km"); snprintf(inst->units.pressure, 3, "mb"); snprintf(inst->units.speed, 5, "km/h"); len = json_object_array_length(o_timeseries); for (i = 0; i < len; i++) { json_object *o_index = json_object_array_get_idx(o_timeseries, i); if (!o_index) break; timestamp = json_object_get_string(json_object_object_get(o_index, "time")); tm_data = _timestamp_time(timestamp); int days = _epoch_days(tm_data); if ((days != days_prev) && days == (days_prev + 1)) idx++; if (idx > 1) break; else if (idx == 0) { strftime(inst->forecast[idx].date, 12, "%d %b %Y", tm_data); strncpy(inst->forecast[idx].day, weekdays[tm_data->tm_wday], 3); } else if (idx == 1) { strftime(inst->forecast[idx].date, 12, "%d %b %Y", tm_data); strncpy(inst->forecast[idx].day, weekdays[tm_data->tm_wday], 3); } json_object *o_data = json_object_object_get(o_index, "data"); if (!o_data) break; json_object *o_instant = json_object_object_get(o_data, "instant"); if (!o_instant) break; json_object *o_details = json_object_object_get(o_instant, "details"); if (!o_details) break; double v = json_object_get_double(json_object_object_get(o_details, "air_temperature")); if (v > inst->forecast[idx].high) inst->forecast[idx].high = v; if (v < inst->forecast[idx].low) inst->forecast[idx].low = v; if (i == 0) { inst->condition.temp = v; v = json_object_get_double(json_object_object_get(o_details, "air_pressure_at_sea_level")); inst->details.atmosphere.pressure = v; v = json_object_get_double(json_object_object_get(o_details, "relative_humidity")); inst->details.atmosphere.humidity = v; v = json_object_get_double(json_object_object_get(o_details, "wind_from_direction")); inst->details.wind.direction = v; v = json_object_get_double(json_object_object_get(o_details, "wind_speed")); v *= 3.6; inst->details.wind.speed = v; if (inst->condition.temp <= 10.0) { double t, vpow; t = inst->condition.temp; vpow = pow(inst->details.wind.speed, 0.16); inst->details.wind.chill = (13.12 + (0.6215 * t)) - (11.37 * (vpow)) + ((0.3965 * t) * (vpow)); inst->details.wind.can_chill = 1; } json_object *o_next = json_object_object_get(o_data, "next_1_hours"); json_object *o_summary = json_object_object_get(o_next, "summary"); code = json_object_get_string(json_object_object_get(o_summary, "symbol_code")); snprintf(inst->condition.code, sizeof(inst->condition.code), "%s", code); snprintf(inst->forecast[idx].code, sizeof(inst->condition.code), "%s", code); } if (tm_data->tm_hour == 8) { // XXX We need TZ data. json_object *o_next = json_object_object_get(o_data, "next_12_hours"); json_object *o_summary = json_object_object_get(o_next, "summary"); code = json_object_get_string(json_object_object_get(o_summary, "symbol_code")); snprintf(inst->forecast[idx].code, sizeof(inst->condition.code), "%s", code); } days_prev = days; } json_object_put(o_root); return 1; error: printf("FC: ERROR: Couldn't parse info\n"); return 0; } void _forecasts_converter(Instance *inst) { int i, dir = -1; if ((inst->units.temp == 'F') && (inst->ci->degrees == DEGREES_C)) { dir = DEGREES_C; inst->units.temp = 'C'; snprintf(inst->units.distance, 3, "km"); snprintf(inst->units.pressure, 3, "mb"); snprintf(inst->units.speed, 5, "km/h"); } else if ((inst->units.temp == 'C') && (inst->ci->degrees == DEGREES_F)) { dir = DEGREES_F; inst->units.temp = 'F'; snprintf(inst->units.distance, 3, "mi"); snprintf(inst->units.pressure, 3, "in"); snprintf(inst->units.speed, 4, "mph"); } if (dir == -1) return; _forecasts_convert_degrees(&inst->condition.temp, dir); _forecasts_convert_degrees(&inst->details.wind.chill, dir); _forecasts_convert_distances(&inst->details.wind.speed, dir); _forecasts_convert_pressures(&inst->details.atmosphere.pressure, dir); for (i = 0; i < FORECASTS; i++) { _forecasts_convert_degrees(&inst->forecast[i].low, dir); _forecasts_convert_degrees(&inst->forecast[i].high, dir); } } static void _forecasts_convert_degrees(int *value, int dir) { if ((dir == DEGREES_C)) *value = (*value - 32) * 5.0 / 9.0; else *value = (*value * 9.0 / 5.0) + 32; } static void _forecasts_convert_distances(int *value, int dir) { if ((dir == DEGREES_C)) *value = (*value) * KM_TO_MI; else *value = (*value) / KM_TO_MI; } static void _forecasts_convert_pressures(float *value, int dir) { if ((dir == DEGREES_C)) *value = (*value) * MB_TO_IN; else *value = (*value) / MB_TO_IN; } static void _forecasts_display_set(Instance *inst, int ok EINA_UNUSED) { char buf[4096]; char m[4096]; if (!inst) return; snprintf(m, sizeof(m), "%s/forecasts.edj", e_module_dir_get(forecasts_config->module)); snprintf(buf, sizeof(buf), "modules/forecasts/icons/%s", inst->condition.code); if (!e_theme_edje_object_set(inst->forecasts->icon_obj, "base/theme/modules/forecasts/icons", buf)) edje_object_file_set(inst->forecasts->icon_obj, m, buf); edje_object_part_swallow(inst->forecasts->forecasts_obj, "icon", inst->forecasts->icon_obj); if (!inst->ci->show_text) edje_object_signal_emit(inst->forecasts_obj, "e,state,description,hide", "e"); else edje_object_signal_emit(inst->forecasts_obj, "e,state,description,show", "e"); snprintf(buf, sizeof(buf), "%d°%c", inst->condition.temp, inst->units.temp); edje_object_part_text_set(inst->forecasts->forecasts_obj, "e.text.temp", buf); edje_object_part_text_set(inst->forecasts->forecasts_obj, "e.text.description", inst->condition.desc); edje_object_part_text_set(inst->forecasts->forecasts_obj, "e.text.location", inst->ci->location); if (inst->gcc->gadcon->orient == E_GADCON_ORIENT_FLOAT) { char buf[4096], name[60]; int i; for (i = 0; i < FORECASTS; i++) { Evas_Object *swallow; snprintf(name, sizeof(name), "e.text.day%d.date", i); edje_object_part_text_set(inst->forecasts->forecasts_obj, name, inst->forecast[i].date); snprintf(name, sizeof(name), "e.text.day%d.description", i); edje_object_part_text_set(inst->forecasts->forecasts_obj, name, inst->forecast[i].desc); snprintf(name, sizeof(name), "e.text.day%d.high", i); snprintf(buf, sizeof(buf), "%d°%c", inst->forecast[i].high, inst->units.temp); edje_object_part_text_set(inst->forecasts->forecasts_obj, name, buf); snprintf(name, sizeof(name), "e.text.day%d.low", i); snprintf(buf, sizeof(buf), "%d°%c", inst->forecast[i].low, inst->units.temp); edje_object_part_text_set(inst->forecasts->forecasts_obj, name, buf); snprintf(name, sizeof(name), "e.swallow.day%d.icon", i); swallow = edje_object_part_swallow_get(inst->forecasts->forecasts_obj, name); if (swallow) evas_object_del(swallow); edje_object_part_swallow(inst->forecasts->forecasts_obj, name, _forecasts_popup_icon_create(inst->gcc->gadcon->evas, inst->forecast[i].code)); } } if (inst->popup) _forecasts_popup_destroy(inst); inst->popup = NULL; } void _forecasts_config_updated(Config_Item *ci) { Eina_List *l; char buf[4096]; if (!forecasts_config) return; for (l = forecasts_config->instances; l; l = l->next) { Instance *inst; inst = l->data; if (inst->ci != ci) continue; int area_changed = 0; if ((!inst->area && inst->ci->location) || (inst->area && strcmp(inst->area, inst->ci->location))) area_changed = 1; if (inst->area) eina_stringshare_del(inst->area); inst->area = eina_stringshare_add(inst->ci->location); _forecasts_converter(inst); if (inst->popup) _forecasts_popup_destroy(inst); inst->popup = NULL; snprintf(buf, sizeof(buf), "%d°%c", inst->condition.temp, inst->units.temp); edje_object_part_text_set(inst->forecasts->forecasts_obj, "e.text.temp", buf); if (!inst->ci->show_text) edje_object_signal_emit(inst->forecasts_obj, "e,state,description,hide", "e"); else edje_object_signal_emit(inst->forecasts_obj, "e,state,description,show", "e"); if (area_changed) _forecasts_cb_check(inst); if (inst->check_timer) ecore_timer_del(inst->check_timer); inst->check_timer = ecore_timer_add(inst->ci->poll_time, _forecasts_cb_check, inst); } } static Evas_Object * _lb_add(Evas_Object *base, const char *txt) { Evas_Object *lb = elm_label_add(base); evas_object_size_hint_weight_set(lb, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND); elm_object_text_set(lb, txt); evas_object_show(lb); return lb; } static void _forecasts_popup_content_create(Instance *inst) { Evas_Object *base, *bx, *hbx, *fr, *tb, *lb; Evas_Object *rec, *ic, *im; char buf[2048], tmp[2048]; int row = 0; Evas_Coord w, h; if (!inst->location) return; inst->popup = e_gadcon_popup_new(inst->gcc, 0); base = e_comp->elm; bx = elm_box_add(base); evas_object_size_hint_weight_set(bx, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND); evas_object_size_hint_align_set(bx, EVAS_HINT_FILL, EVAS_HINT_FILL); evas_object_show(bx); fr = elm_frame_add(base); evas_object_size_hint_weight_set(fr, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND); evas_object_size_hint_align_set(fr, EVAS_HINT_FILL, EVAS_HINT_FILL); elm_object_text_set(fr, D_("Current Conditions")); evas_object_show(fr); elm_box_pack_end(bx, fr); tb = elm_table_add(base); evas_object_size_hint_weight_set(tb, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND); evas_object_size_hint_align_set(tb, EVAS_HINT_FILL, EVAS_HINT_FILL); elm_table_align_set(tb, 0.0, 0.0); elm_table_padding_set(tb, 2, 2); evas_object_show(tb); rec = evas_object_rectangle_add(e_comp->evas); evas_object_size_hint_min_set(rec, 240, 1); elm_table_pack(tb, rec, 0, 0, 2, 1); elm_object_content_set(fr, tb); ic = _forecasts_popup_icon_create(e_comp->evas, inst->condition.code); edje_object_size_max_get(ic, &w, &h); if (w > 160) w = 160; if (h > 160) h = 160; im = e_widget_image_add_from_object(e_comp->evas, ic, w, h); evas_object_show(im); elm_table_pack(tb, im, 0, row, 2, 1); _timestamp_local(inst->update_time, tmp, sizeof(tmp)); snprintf(buf, sizeof(buf), D_("Updated: %s"), tmp); lb = _lb_add(base, buf); evas_object_size_hint_align_set(lb, 1.0, 0.0); elm_table_pack(tb, lb, 0, row++, 2, 1); snprintf(buf, sizeof(buf), "%s", inst->location); lb = _lb_add(base, buf); evas_object_size_hint_align_set(lb, 0.5, 1.0); elm_table_pack(tb, lb, 0, row++, 2, 1); lb = _lb_add(base, D_("Temperature")); elm_table_pack(tb, lb, 0, row, 1, 1); snprintf(buf, sizeof(buf), "%d°%c", inst->condition.temp, inst->units.temp); lb = _lb_add(base, buf); elm_table_pack(tb, lb, 1, row++, 1, 1); lb = _lb_add(base, D_("Wind Chill")); elm_table_pack(tb, lb, 0, row, 1, 1); if (inst->details.wind.can_chill) snprintf(buf, sizeof(buf), "%d°%c", inst->details.wind.chill, inst->units.temp); else snprintf(buf, sizeof(buf), "N/A"); lb = _lb_add(base, buf); elm_table_pack(tb, lb, 1, row++, 1, 1); lb = _lb_add(base, D_("Wind Speed")); elm_table_pack(tb, lb, 0, row, 1, 1); snprintf(buf, sizeof(buf), "%d %s", inst->details.wind.speed, inst->units.speed); lb = _lb_add(base, buf); elm_table_pack(tb, lb, 1, row++, 1, 1); lb = _lb_add(base, D_("Humidity")); elm_table_pack(tb, lb, 0, row, 1, 1); snprintf(buf, sizeof(buf), "%d %%", inst->details.atmosphere.humidity); lb = _lb_add(base, buf); elm_table_pack(tb, lb, 1, row++, 1, 1); lb = _lb_add(base, D_("Pressure")); elm_table_pack(tb, lb, 0, row, 1, 1); snprintf(buf, sizeof(buf), "%.2f %s", inst->details.atmosphere.pressure, inst->units.pressure); lb = _lb_add(base, buf); elm_table_pack(tb, lb, 1, row++, 1, 1); hbx = elm_box_add(base); evas_object_size_hint_weight_set(hbx, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND); evas_object_size_hint_align_set(hbx, EVAS_HINT_FILL, EVAS_HINT_FILL); elm_box_horizontal_set(hbx, 1); evas_object_show(hbx); elm_box_pack_end(bx, hbx); fr = elm_frame_add(base); evas_object_size_hint_weight_set(fr, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND); evas_object_size_hint_align_set(fr, EVAS_HINT_FILL, EVAS_HINT_FILL); elm_object_text_set(fr, D_("Today")); evas_object_show(fr); tb = elm_table_add(base); evas_object_size_hint_weight_set(tb, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND); evas_object_size_hint_align_set(tb, EVAS_HINT_FILL, EVAS_HINT_FILL); evas_object_show(tb); elm_object_content_set(fr, tb); ic = _forecasts_popup_icon_create(e_comp->evas, inst->forecast[0].code); im = e_widget_image_add_from_object(e_comp->evas, ic, 24, 24); evas_object_size_hint_weight_set(im, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND); evas_object_show(im); elm_table_pack(tb, im, 0, 0, 2, 1); lb = _lb_add(base, D_("High")); elm_table_pack(tb, lb, 0, 1, 1, 1); snprintf(buf, sizeof(buf), "%d°%c", inst->forecast[0].high, inst->units.temp); lb = _lb_add(base, buf); elm_table_pack(tb, lb, 0, 2, 1, 1); lb = _lb_add(base, D_("Low")); elm_table_pack(tb, lb, 1, 1, 1, 1); snprintf(buf, sizeof(buf), "%d°%c", inst->forecast[0].low, inst->units.temp); lb = _lb_add(base, buf); elm_table_pack(tb, lb, 1, 2, 1, 1); elm_box_pack_end(hbx, fr); fr = elm_frame_add(base); evas_object_size_hint_weight_set(fr, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND); evas_object_size_hint_align_set(fr, EVAS_HINT_FILL, EVAS_HINT_FILL); elm_object_text_set(fr, D_("Tomorrow")); evas_object_show(fr); elm_box_pack_end(hbx, fr); tb = elm_table_add(base); evas_object_size_hint_weight_set(tb, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND); evas_object_size_hint_align_set(tb, EVAS_HINT_FILL, EVAS_HINT_FILL); evas_object_show(tb); elm_object_content_set(fr, tb); ic = _forecasts_popup_icon_create(e_comp->evas, inst->forecast[1].code); im = e_widget_image_add_from_object(e_comp->evas, ic, 24, 24); evas_object_size_hint_weight_set(im, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND); evas_object_show(im); elm_table_pack(tb, im, 0, 0, 2, 1); lb = _lb_add(base, D_("High")); elm_table_pack(tb, lb, 0, 1, 1, 1); snprintf(buf, sizeof(buf), "%d°%c", inst->forecast[1].high, inst->units.temp); lb = _lb_add(base, buf); elm_table_pack(tb, lb, 0, 2, 1, 1); lb = _lb_add(base, D_("Low")); elm_table_pack(tb, lb, 1, 1, 1, 1); snprintf(buf, sizeof(buf), "%d°%c", inst->forecast[1].low, inst->units.temp); lb = _lb_add(base, buf); elm_table_pack(tb, lb, 1, 2, 1, 1); e_gadcon_popup_content_set(inst->popup, bx); } static Evas_Object * _forecasts_popup_icon_create(Evas *evas, const char *code) { char buf[4096]; char m[4096]; Evas_Object *o; snprintf(m, sizeof(m), "%s/forecasts.edj", e_module_dir_get(forecasts_config->module)); o = edje_object_add(evas); snprintf(buf, sizeof(buf), "modules/forecasts/icons/%s", code); if (!e_theme_edje_object_set(o, "base/theme/modules/forecasts/icons", buf)) edje_object_file_set(o, m, buf); return o; } static void _forecasts_popup_destroy(Instance *inst) { if (!inst->popup) return; e_object_del(E_OBJECT(inst->popup)); } static void _cb_mouse_up(void *data, Evas *e EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event_info) { Instance *inst = data; Evas_Event_Mouse_Up *ev = event_info; if (ev->event_flags & EVAS_EVENT_FLAG_ON_HOLD) return; if (ev->button == 1) { if (!inst->popup) { _forecasts_popup_content_create(inst); e_gadcon_popup_show(inst->popup); e_gadcon_popup_toggle_pinned(inst->popup); } else { _forecasts_popup_destroy(inst); inst->popup = NULL; } } }