#include #include "e_mod_main.h" #include #define _XOPEN_SOURCE #include #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_Server *server; Ecore_Event_Handler *add_handler; Ecore_Event_Handler *del_handler; Ecore_Event_Handler *data_handler; 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; } 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; }; struct { const char *host; int port; } proxy = { NULL, 0 }; /* Module Function Protos */ static void _forecasts_cb_mouse_down(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 void _forecasts_get_proxy(void); static Eina_Bool _forecasts_server_add(void *data, int type, void *event); static Eina_Bool _forecasts_server_del(void *data, int type, void *event); static Eina_Bool _forecasts_server_data(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_down(void *data, Evas *e, Evas_Object *obj, void *event_info); static void _cb_mouse_in(void *data, Evas *e, Evas_Object *obj, void *event_info); static void _cb_mouse_out(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_DOWN, _cb_mouse_down, inst); evas_object_event_callback_add(inst->forecasts_obj, EVAS_CALLBACK_MOUSE_IN, _cb_mouse_in, inst); evas_object_event_callback_add(inst->forecasts_obj, EVAS_CALLBACK_MOUSE_OUT, _cb_mouse_out, inst); if (!inst->add_handler) inst->add_handler = ecore_event_handler_add(ECORE_CON_EVENT_SERVER_ADD, _forecasts_server_add, inst); if (!inst->del_handler) inst->del_handler = ecore_event_handler_add(ECORE_CON_EVENT_SERVER_DEL, _forecasts_server_del, inst); if (!inst->data_handler) inst->data_handler = ecore_event_handler_add(ECORE_CON_EVENT_SERVER_DATA, _forecasts_server_data, inst); evas_object_event_callback_add(w->forecasts_obj, EVAS_CALLBACK_MOUSE_DOWN, _forecasts_cb_mouse_down, 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->add_handler) ecore_event_handler_del(inst->add_handler); if (inst->data_handler) ecore_event_handler_del(inst->data_handler); if (inst->del_handler) ecore_event_handler_del(inst->del_handler); if (inst->server) ecore_con_server_del(inst->server); if (inst->area) eina_stringshare_del(inst->area); eina_strbuf_free(inst->buffer); inst->server = NULL; forecasts_config->instances = eina_list_remove(forecasts_config->instances, inst); evas_object_event_callback_del(w->forecasts_obj, EVAS_CALLBACK_MOUSE_DOWN, _forecasts_cb_mouse_down); _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) { return D_("Forecasts"); } static Evas_Object * _gc_icon(const E_Gadcon_Client_Class *client_class, 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) { Config_Item *ci; ci = _forecasts_config_item_get(NULL); return ci->id; } static void _forecasts_cb_mouse_down(void *data, Evas *e, Evas_Object *obj, void *event_info) { Instance *inst; Evas_Event_Mouse_Down *ev; inst = data; ev = event_info; 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, E_Menu *m) { 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, E_Menu_Item *mi) { 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->host = eina_stringshare_add("www.enlightenment.org"); 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) { 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, host, STR); 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) { Config_Item *ci; forecasts_config = E_NEW(Config, 1); ci = E_NEW(Config_Item, 1); ci->poll_time = 60.0; ci->degrees = DEGREES_C; ci->host = eina_stringshare_add("www.enlightenment.org"); 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); } _forecasts_get_proxy(); forecasts_config->module = m; e_gadcon_provider_register(&_gadcon_class); return m; } E_API int e_modapi_shutdown(E_Module *m) { 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->host) eina_stringshare_del(ci->host); 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); return 1; } E_API int e_modapi_save(E_Module *m) { 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 void _forecasts_get_proxy(void) { const char *env; const char *host = NULL; const char *p; int port = 0; env = getenv("http_proxy"); if ((!env) || (!*env)) env = getenv("HTTP_PROXY"); if ((!env) || (!*env)) return; if (strncmp(env, "http://", 7)) return; host = strchr(env, ':'); host += 3; p = strchr(host, ':'); if (p) { if (sscanf(p + 1, "%d", &port) != 1) port = 0; } if ((host) && (port)) { if (proxy.host) eina_stringshare_del(proxy.host); proxy.host = eina_stringshare_add_length(host, p - host); proxy.port = port; } } static Eina_Bool _forecasts_cb_check(void *data) { Instance *inst; /* check that data is valid */ if (!(inst = data)) return EINA_FALSE; /* if we have a previous server, delete it */ if (inst->server) ecore_con_server_del(inst->server); /* server deleted, set variable to NULL */ inst->server = NULL; if (proxy.port != 0) inst->server = ecore_con_server_connect(ECORE_CON_REMOTE_NODELAY, proxy.host, proxy.port, inst); else inst->server = ecore_con_server_connect(ECORE_CON_REMOTE_NODELAY | ECORE_CON_USE_MIXED, inst->ci->host, 443, inst); if (!inst->server) return EINA_FALSE; return EINA_TRUE; } static Eina_Bool _forecasts_server_add(void *data, int type, void *event) { Instance *inst; Ecore_Con_Event_Server_Add *ev; char buf[1024]; char forecast[1024]; inst = data; if (!inst) return EINA_TRUE; ev = event; if ((!inst->server) || (inst->server != ev->server)) return EINA_TRUE; snprintf(forecast, sizeof(forecast), "/weather.php?lat=%s&lon=%s", inst->ci->lat, inst->ci->lon); snprintf(buf, sizeof(buf)-strlen(forecast)-(strlen(inst->ci->host)*2), "GET https://%s%s HTTP/1.1\r\n" "Host: %s\r\n" "Connection: close\r\n\r\n", inst->ci->host, forecast, inst->ci->host); DEBUG("Server: %s", buf); ecore_con_server_send(inst->server, buf, strlen(buf)); return EINA_FALSE; } static Eina_Bool _forecasts_server_del(void *data, int type, void *event) { Instance *inst; Ecore_Con_Event_Server_Del *ev; int ret; inst = data; ev = event; if ((!inst->server) || (inst->server != ev->server)) return EINA_TRUE; ecore_con_server_del(inst->server); inst->server = 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 Eina_Bool _forecasts_server_data(void *data, int type, void *event) { Instance *inst; Ecore_Con_Event_Server_Data *ev; inst = data; ev = event; if ((!inst->server) || (inst->server != ev->server)) return EINA_TRUE; eina_strbuf_append_length(inst->buffer, ev->data, ev->size); return EINA_FALSE; } 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; char *needle; 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", }; inst = data; if (!inst) return 0; 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); needle = strstr(eina_strbuf_string_get(inst->buffer), "\r\n\r\n"); if (!needle) goto error; needle += 4; json_object *o_root = json_tokener_parse(needle); if (!o_root) goto error; json_object *o_properties = json_object_object_get(o_root, "properties"); if (!o_properties) goto error; 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; 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)); 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: fprintf(stderr, "ERROR: Couldn't parse info from %s\n", inst->ci->host); 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) { 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 && 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) inst->check_timer = ecore_timer_add(inst->ci->poll_time, _forecasts_cb_check, inst); else ecore_timer_interval_set(inst->check_timer, inst->ci->poll_time); } } 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[4096]; 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); 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); snprintf(buf, sizeof(buf), "%d°%c", inst->details.wind.chill, inst->units.temp); 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_down(void *data, Evas *e, Evas_Object *obj, void *event_info) { Instance *inst; Evas_Event_Mouse_Down *ev; if (!(inst = data)) return; if (!inst->ci->popup_on_hover) { if (!inst->popup) _forecasts_popup_content_create(inst); e_gadcon_popup_show(inst->popup); return; } ev = event_info; if (ev->button == 1) { e_gadcon_popup_toggle_pinned(inst->popup); } } static void _cb_mouse_in(void *data, Evas *e, Evas_Object *obj, void *event_info) { Instance *inst; if (!(inst = data)) return; if (!inst->ci->popup_on_hover) return; if (!inst->popup) _forecasts_popup_content_create(inst); e_gadcon_popup_show(inst->popup); } static void _cb_mouse_out(void *data, Evas *e, Evas_Object *obj, void *event_info) { Instance *inst; if (!(inst = data)) return; if (!(inst->popup)) return; if (inst->popup->pinned) return; e_gadcon_popup_hide(inst->popup); }