You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

1119 lines
35 KiB

#include <e.h>
#include "e_mod_main.h"
#include <json-c/json.h>
#define _XOPEN_SOURCE
#include <time.h>
#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__)
/* Gadcon Function Protos */
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;
/* Define Gadcon Class */
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
};
/* Module specifics */
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, code;
char update[52];
char desc[256];
} condition;
struct
{
char temp, distance[3], pressure[3], speed[5];
} units;
struct
{
struct
{
int chill, direction, speed;
} wind;
struct
{
int humidity, rising;
float pressure, visibility;
} atmosphere;
struct
{
char sunrise[9], sunset[9];
} astronomy;
} details;
struct
{
char day[4];
char date[12];
int low, high, code;
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_distances_float(float *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, int 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->code);
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.yahoo.com");
ci->code = eina_stringshare_add("839722");
ci->show_text = 1;
ci->popup_on_hover = 1;
forecasts_config->items = eina_list_append(forecasts_config->items, ci);
return ci;
}
/* Gadman Module Setup */
EAPI E_Module_Api e_modapi = {
E_MODULE_API_VERSION,
"Forecasts"
};
EAPI 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, code, STR);
E_CONFIG_VAL(D, T, show_text, INT);
E_CONFIG_VAL(D, T, popup_on_hover, INT);
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.yahoo.com");
ci->code = eina_stringshare_add("839722");
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;
}
EAPI 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->code)
eina_stringshare_del(ci->code);
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;
}
EAPI 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), "/news/_tdnews/api/resource/WeatherService;woeids=%s", inst->ci->code);
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;
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 int
_forecasts_parse(void *data)
{
Instance *inst;
char *needle;
int secs, i;
inst = data;
if (!inst)
return 0;
if (!inst->buffer)
return 0;
/* get rid of HTTP headers */
needle = strstr(eina_strbuf_string_get(inst->buffer), "\r\n\r\n");
if (!needle) goto error;
needle += 4;
/* parse data to json object */
json_object *obj_root = json_tokener_parse(needle);
json_object *obj_weathers = json_object_object_get(obj_root, "weathers");
if (json_object_get_type(obj_weathers) != json_type_array || json_object_array_length(obj_weathers) != 1)
goto error;
json_object *obj_main = json_object_array_get_idx(obj_weathers, 0);
/* we have data in imperial units by default, so set it accordingly */
inst->units.temp = 'F';
snprintf(inst->units.distance, 3, "mi");
snprintf(inst->units.pressure, 3, "in");
snprintf(inst->units.speed, 4, "mph");
json_object *obj_location = json_object_object_get(obj_main, "location");
eina_stringshare_replace(&inst->location, json_object_get_string(json_object_object_get(obj_location, "displayName")));
json_object *obj_sunmoon = json_object_object_get(obj_main, "sunAndMoon");
secs = json_object_get_int(json_object_object_get(obj_sunmoon, "sunrise"));
snprintf(inst->details.astronomy.sunrise, 7, "%02d:%02d", secs/3600, (secs%3600)/60);
secs = json_object_get_int(json_object_object_get(obj_sunmoon, "sunset"));
snprintf(inst->details.astronomy.sunset, 7, "%02d:%02d", secs/3600, (secs%3600)/60);
/* Current conditions */
json_object *obj_observation = json_object_object_get(obj_main, "observation");
inst->details.atmosphere.pressure = json_object_get_double(json_object_object_get(obj_observation, "barometricPressure"));
inst->details.atmosphere.humidity = json_object_get_int(json_object_object_get(obj_observation, "humidity"));
inst->details.atmosphere.visibility = json_object_get_double(json_object_object_get(obj_observation, "visibility"));
inst->condition.code = json_object_get_int(json_object_object_get(obj_observation, "conditionCode"));
strncpy(inst->condition.desc, json_object_get_string(json_object_object_get(obj_observation, "conditionDescription")), 255);
inst->details.wind.direction = json_object_get_int(json_object_object_get(obj_observation, "windDirection"));
inst->details.wind.speed = json_object_get_int(json_object_object_get(obj_observation, "windSpeed"));
json_object *obj_observation_temp = json_object_object_get(obj_observation, "temperature");
inst->condition.temp = json_object_get_int(json_object_object_get(obj_observation_temp, "now"));
inst->details.wind.chill = json_object_get_int(json_object_object_get(obj_observation_temp, "feelsLike"));
json_object *obj_observation_time = json_object_object_get(obj_observation, "observationTime");
strncpy(inst->condition.update, json_object_get_string(json_object_object_get(obj_observation_time, "timestamp")), 51);
/* Forecasts */
json_object *obj_forecasts = json_object_object_get(obj_main, "forecasts");
json_object *obj_forecasts_daily = json_object_object_get(obj_forecasts, "daily");
for (i = 0; i < FORECASTS; i++) {
json_object *obj_forecast = json_object_array_get_idx(obj_forecasts_daily, i);
inst->forecast[i].code = json_object_get_int(json_object_object_get(obj_forecast, "conditionCode"));
strncpy(inst->forecast[i].desc, json_object_get_string(json_object_object_get(obj_forecast, "conditionDescription")), 255);
json_object *obj_observation_time = json_object_object_get(obj_forecast, "observationTime");
strncpy(inst->forecast[i].day, json_object_get_string(json_object_object_get(obj_observation_time, "day")), 3);
struct tm tm;
memset(&tm, 0, sizeof(struct tm));
strptime(json_object_get_string(json_object_object_get(obj_observation_time, "timestamp")), "%Y-%m-%dT%H:%M:%S", &tm);
strftime(inst->forecast[i].date, 12, "%d %b %Y", &tm);
json_object *obj_observation_temp = json_object_object_get(obj_forecast, "temperature");
inst->forecast[i].high = json_object_get_int(json_object_object_get(obj_observation_temp, "high"));
inst->forecast[i].low = json_object_get_int(json_object_object_get(obj_observation_temp, "low"));
}
json_object_put(obj_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_distances_float(&inst->details.atmosphere.visibility, 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_distances_float(float *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/%d", 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->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->code))
area_changed = 1;
if (inst->area) eina_stringshare_del(inst->area);
inst->area = eina_stringshare_add(inst->ci->code);
_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 void
_forecasts_popup_content_create(Instance *inst)
{
Evas_Object *o, *ol, *of, *ob, *oi;
Evas *evas;
char buf[4096];
int row = 0, i;
Evas_Coord w, h, mw, mh;
if (!inst->location) return;
inst->popup = e_gadcon_popup_new(inst->gcc, 0);
evas = e_comp->evas;
o = e_widget_list_add(evas, 0, 0);
snprintf(buf, sizeof(buf), D_("%s: Current Conditions"), inst->location);
of = e_widget_frametable_add(evas, buf, 0);
snprintf(buf, sizeof(buf), "%s: %d°%c", inst->condition.desc, inst->condition.temp, inst->units.temp);
ob = e_widget_label_add(evas, buf);
e_widget_frametable_object_append(of, ob, 0, row, 2, 1, 0, 0, 1, 0);
oi = _forecasts_popup_icon_create(e_comp->evas,
inst->condition.code);
edje_object_size_max_get(oi, &w, &h);
DEBUG("Icon size %dx%d", w, h);
if (w > 160) w = 160; /* For now there is a limit to how big the icon should be */
if (h > 160) h = 160; /* In the future, the icon should be set from the theme, not part of the table */
ob = e_widget_image_add_from_object(evas, oi, w, h);
e_widget_frametable_object_append(of, ob, 2, row, 1, 4, 1, 1, 1, 1);
ob = e_widget_label_add(evas, D_("Wind Chill"));
e_widget_frametable_object_append(of, ob, 0, ++row, 1, 1, 1, 0, 0, 0);
snprintf(buf, sizeof(buf), "%d°%c", inst->details.wind.chill, inst->units.temp);
ob = e_widget_label_add(evas, buf);
e_widget_frametable_object_append(of, ob, 1, row, 1, 1, 1, 0, 0, 0);
ob = e_widget_label_add(evas, D_("Wind Speed"));
e_widget_frametable_object_append(of, ob, 0, ++row, 1, 1, 1, 0, 0, 0);
snprintf(buf, sizeof(buf), "%d %s", inst->details.wind.speed, inst->units.speed);
ob = e_widget_label_add(evas, buf);
e_widget_frametable_object_append(of, ob, 1, row, 1, 1, 1, 0, 0, 0);
ob = e_widget_label_add(evas, D_("Humidity"));
e_widget_frametable_object_append(of, ob, 0, ++row, 1, 1, 1, 0, 0, 0);
snprintf(buf, sizeof(buf), "%d %%", inst->details.atmosphere.humidity);
ob = e_widget_label_add(evas, buf);
e_widget_frametable_object_append(of, ob, 1, row, 1, 1, 1, 0, 0, 0);
ob = e_widget_label_add(evas, D_("Visibility"));
e_widget_frametable_object_append(of, ob, 0, ++row, 1, 1, 1, 0, 0, 0);
snprintf(buf, sizeof(buf), "%.2f %s", inst->details.atmosphere.visibility, inst->units.distance);
ob = e_widget_label_add(evas, buf);
e_widget_frametable_object_append(of, ob, 1, row, 1, 1, 1, 0, 0, 0);
ob = e_widget_label_add(evas, D_("Pressure"));
e_widget_frametable_object_append(of, ob, 0, ++row, 1, 1, 1, 0, 0, 0);
snprintf(buf, sizeof(buf), "%.2f %s", inst->details.atmosphere.pressure, inst->units.pressure);
ob = e_widget_label_add(evas, buf);
e_widget_frametable_object_append(of, ob, 1, row, 1, 1, 1, 0, 0, 0);
if (inst->details.atmosphere.rising == 1)
snprintf(buf, sizeof(buf), D_("Rising"));
else if (inst->details.atmosphere.rising == 2)
snprintf(buf, sizeof(buf), D_("Falling"));
else
snprintf(buf, sizeof(buf), D_("Steady"));
ob = e_widget_label_add(evas, buf);
e_widget_frametable_object_append(of, ob, 2, row, 1, 1, 1, 0, 1, 0);
ob = e_widget_label_add(evas, D_("Sunrise / Sunset"));
e_widget_frametable_object_append(of, ob, 0, ++row, 1, 1, 1, 0, 0, 0);
snprintf(buf, sizeof(buf), "%s", inst->details.astronomy.sunrise);
ob = e_widget_label_add(evas, buf);
e_widget_frametable_object_append(of, ob, 1, row, 1, 1, 1, 0, 1, 0);
snprintf(buf, sizeof(buf), "%s", inst->details.astronomy.sunset);
ob = e_widget_label_add(evas, buf);
e_widget_frametable_object_append(of, ob, 2, row, 1, 1, 1, 0, 1, 0);
e_widget_list_object_append(o, of, 1, 1, 0.5);
ol = e_widget_list_add(evas, 1, 1);
for (i = 0; i < FORECASTS; i++)
{
int row = 0;
if (!i)
snprintf(buf, sizeof(buf), D_("Today"));
else if (i == 1)
snprintf(buf, sizeof(buf), D_("Tomorrow"));
else
snprintf(buf, sizeof(buf), "%s", inst->forecast[i].date);
of = e_widget_frametable_add(evas, buf, 0);
ob = e_widget_label_add(evas, inst->forecast[i].desc);
e_widget_frametable_object_append(of, ob, 0, row, 3, 1, 0, 0, 1, 0);
ob = e_widget_label_add(evas, D_("High"));
e_widget_frametable_object_append(of, ob, 0, ++row, 1, 1, 1, 0, 1, 0);
snprintf(buf, sizeof(buf), "%d°%c", inst->forecast[i].high, inst->units.temp);
ob = e_widget_label_add(evas, buf);
e_widget_frametable_object_append(of, ob, 1, row, 1, 1, 1, 0, 1, 0);
ob = e_widget_image_add_from_object(evas,
_forecasts_popup_icon_create(e_comp->evas,
inst->forecast[i].code), 0, 0);
e_widget_frametable_object_append(of, ob, 2, row, 1, 2, 1, 1, 0, 0);
ob = e_widget_label_add(evas, D_("Low"));
e_widget_frametable_object_append(of, ob, 0, ++row, 1, 1, 1, 0, 1, 0);
snprintf(buf, sizeof(buf), "%d°%c", inst->forecast[i].low, inst->units.temp);
ob = e_widget_label_add(evas, buf);
e_widget_frametable_object_append(of, ob, 1, row, 1, 1, 1, 0, 1, 0);
e_widget_list_object_append(ol, of, 1, 1, 0.5);
}
e_widget_list_object_append(o, ol, 1, 1, 0.5);
e_widget_size_min_get(o, &mw, &mh);
if ((double)mw / mh > GOLDEN_RATIO)
mh = mw / GOLDEN_RATIO;
else if ((double)mw / mh < GOLDEN_RATIO - (double)1)
mw = mh * (GOLDEN_RATIO - (double)1);
e_widget_size_min_set(o, mw, mh);
e_gadcon_popup_content_set(inst->popup, o);
}
static Evas_Object *
_forecasts_popup_icon_create(Evas *evas, int 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/%d", 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);
}