diff --git a/src/bin/e_notification.c b/src/bin/e_notification.c index b185f395b..8ddac7c0f 100644 --- a/src/bin/e_notification.c +++ b/src/bin/e_notification.c @@ -2,8 +2,8 @@ typedef struct _Notification_Data { - Eldbus_Connection *conn; - Eldbus_Service_Interface *iface; + Eldbus_Connection *conn; + Eldbus_Service_Interface *iface; E_Notification_Notify_Cb notify_cb; E_Notification_Close_Cb close_cb; void *data; @@ -15,15 +15,28 @@ static Notification_Data *n_data = NULL; static void _notification_free(E_Notification_Notify *notify) { + int i; + EINA_SAFETY_ON_NULL_RETURN(notify); - eina_stringshare_del(notify->app_name); - eina_stringshare_del(notify->body); - eina_stringshare_del(notify->icon.icon); - if (notify->icon.icon_path) - eina_stringshare_del(notify->icon.icon_path); - eina_stringshare_del(notify->summary); - if (notify->icon.raw.data) - free(notify->icon.raw.data); + if (notify->app_name) eina_stringshare_del(notify->app_name); + if (notify->body) eina_stringshare_del(notify->body); + if (notify->icon.icon) eina_stringshare_del(notify->icon.icon); + if (notify->icon.icon_path) eina_stringshare_del(notify->icon.icon_path); + if (notify->summary) eina_stringshare_del(notify->summary); + if (notify->icon.raw.data) free(notify->icon.raw.data); + if (notify->category) eina_stringshare_del(notify->category); + if (notify->desktop_entry) eina_stringshare_del(notify->desktop_entry); + if (notify->sound_file) eina_stringshare_del(notify->sound_file); + if (notify->sound_name) eina_stringshare_del(notify->sound_name); + if (notify->actions) + { + for (i = 0; notify->actions[i].action; i++) + { + eina_stringshare_del(notify->actions[i].action); + eina_stringshare_del(notify->actions[i].label); + } + free(notify->actions); + } free(notify); } @@ -31,12 +44,15 @@ static void hints_dict_iter(void *data, const void *key, Eldbus_Message_Iter *var) { E_Notification_Notify *n = data; - if (!strcmp(key, "image-data") || !strcmp(key, "image_data")) + + if ((!strcmp(key, "image-data")) || (!strcmp(key, "image_data")) || + (!strcmp(key, "icon_data"))) { Eldbus_Message_Iter *st, *data_iter; int w, h, r, bits, channels; Eina_Bool alpha; unsigned char *raw_data; + if (!eldbus_message_iter_arguments_get(var, "(iiibiiay)", &st)) return; if (!eldbus_message_iter_arguments_get(st, "iiibiiay", &w, &h, &r, @@ -54,22 +70,136 @@ hints_dict_iter(void *data, const void *key, Eldbus_Message_Iter *var) n->icon.raw.data = malloc(sizeof(char) * n->icon.raw.data_size); EINA_SAFETY_ON_NULL_RETURN(n->icon.raw.data); memcpy(n->icon.raw.data, raw_data, sizeof(char) * n->icon.raw.data_size); - } - else if (!strcmp(key, "urgency")) - { - unsigned char urgency; - eldbus_message_iter_arguments_get(var, "y", &urgency); - if (urgency < 3) - n->urgency = urgency; + printf("NOT: image-data=%ix%i,a=%i\n", w, h, alpha); } else if (!strcmp(key, "image-path") || !strcmp(key, "image_path")) { eldbus_message_iter_arguments_get(var, "s", &n->icon.icon_path); n->icon.icon_path = eina_stringshare_add(n->icon.icon_path); + printf("NOT: image-path=[%s]\n", n->icon.icon_path); + // path to image file + } + else if (!strcmp(key, "urgency")) + { + unsigned char urgency; + + eldbus_message_iter_arguments_get(var, "y", &urgency); + if (urgency < 3) n->urgency = urgency; + printf("NOT: urgency=%i\n", n->urgency); + // 0=low, 1=normal, 2=critical + } + else if (!strcmp(key, "category")) + { // XXX: store category + const char *val = NULL; + + eldbus_message_iter_arguments_get(var, "s", &val); + printf("NOT: category=[%s]\n", val); + // "device" A generic device-related notification that doesn't fit into any other category. + // "device.added" A device, such as a USB device, was added to the system. + // "device.error" A device had some kind of error. + // "device.removed" A device, such as a USB device, was removed from the system. + // "email" A generic e-mail-related notification that doesn't fit into any other category. + // "email.arrived" A new e-mail notification. + // "email.bounced" A notification stating that an e-mail has bounced. + // "im" A generic instant message-related notification that doesn't fit into any other category. + // "im.error" An instant message error notification. + // "im.received" A received instant message notification. + // "network" A generic network notification that doesn't fit into any other category. + // "network.connected" A network connection notification, such as successful sign-on to a network service. This should not be confused with device.added for new network devices. + // "network.disconnected" A network disconnected notification. This should not be confused with device.removed for disconnected network devices. + // "network.error" A network-related or connection-related error. + // "presence" A generic presence change notification that doesn't fit into any other category, such as going away or idle. + // "presence.offline" An offline presence change notification. + // "presence.online" An online presence change notification. + // "transfer" A generic file transfer or download notification that doesn't fit into any other category. + // "transfer.complete" A file transfer or download complete notification. + // "transfer.error" A file transfer or download error. + if (val) n->category = eina_stringshare_add(val); + } + else if (!strcmp(key, "desktop-entry")) + { + const char *val = NULL; + + eldbus_message_iter_arguments_get(var, "s", &val); + printf("NOT: desktop-entry=[%s]\n", val); + // if rage.desktop -> "rage" + // if terminology.desktop -> "terminology" + if (val) n->desktop_entry = eina_stringshare_add(val); + } + else if (!strcmp(key, "icon-actions")) + { + Eina_Bool val = 0; + + eldbus_message_iter_arguments_get(var, "b", &val); + printf("NOT: icon-actions=%i\n", val); + // 1 == interpret action identifier == named icon in icon naming standards + n->icon_actions = val; + } + else if (!strcmp(key, "resident")) + { + Eina_Bool val = 0; + + eldbus_message_iter_arguments_get(var, "b", &val); + printf("NOT: resident=%i\n", val); + // 1== remove notification when action invoked - no timeout + n->resident = val; + } + else if (!strcmp(key, "supress-sound")) + { + Eina_Bool val = 0; + + eldbus_message_iter_arguments_get(var, "b", &val); + printf("NOT: supress-sound=%i\n", val); + // 1== remove notification when action invoked - no timeout + n->suppress_sound = val; + } + else if (!strcmp(key, "sound-file")) + { + const char *val = NULL; + + eldbus_message_iter_arguments_get(var, "s", &val); + printf("NOT: sound-file=[%s]\n", val); + // path to sound file to play + if (val) n->sound_file = eina_stringshare_add(val); + } + else if (!strcmp(key, "sound-name")) + { + const char *val = NULL; + + eldbus_message_iter_arguments_get(var, "s", &val); + printf("NOT: sound-file=[%s]\n", val); + // sound naming spec to play + // http://0pointer.de/public/sound-naming-spec.html + if (val) n->sound_name = eina_stringshare_add(val); + } + else if (!strcmp(key, "transient")) + { + Eina_Bool val = 0; + + eldbus_message_iter_arguments_get(var, "b", &val); + printf("NOT: transient=%i\n", val); + n->transient = val; + } + else if (!strcmp(key, "x")) + { + int val = 0; + + eldbus_message_iter_arguments_get(var, "i", &val); + printf("NOT: x=%i\n", val); + n->x = val; + n->have_xy = EINA_TRUE; + } + else if (!strcmp(key, "y")) + { + int val = 0; + + eldbus_message_iter_arguments_get(var, "i", &val); + printf("NOT: y=%i\n", val); + n->y = val; + n->have_xy = EINA_TRUE; } } - /* this function should be external in edje for use in cases such as this module. * * happily, it was decided that the function would not be external so that it could @@ -93,134 +223,188 @@ _text_escape(Eina_Strbuf *txt, const char *text) return advance; } -/* hardcoded list of allowed tags based on - * https://people.gnome.org/~mccann/docs/notification-spec/notification-spec-latest.html#markup - */ -static const char *tags[] = +static int +_tag_len(const char *txt) { - "') + { + s++; + break; + } + else if (*s == '\'') inquote = EINA_TRUE; + else if (*s == '\"') indblquote = EINA_TRUE; + } + } + } + else backslash = EINA_FALSE; + } + return s - txt; } -char * +static char * +_path_get(const char *txt) +{ + Eina_Strbuf *buf; + char *ret; + const char *s; + Eina_Bool backslash = EINA_FALSE; + Eina_Bool inquote = EINA_FALSE, indblquote = EINA_FALSE; + + if (txt[0] == '>') return NULL; + + buf = eina_strbuf_new(); + if (!buf) return NULL; + + for (s = txt; *s; s++) + { + if (!backslash) + { + if (*s == '\\') backslash = EINA_TRUE; + else + { + if (inquote) + { + if (*s == '\'') inquote = EINA_FALSE; + else eina_strbuf_append_char(buf, *s); + } + else if (indblquote) + { + if (*s == '"') indblquote = EINA_FALSE; + else eina_strbuf_append_char(buf, *s); + } + else + { + if (*s == '>') break; + else if (*s == ' ') break; + else if (*s == '\'') inquote = EINA_TRUE; + else if (*s == '\"') indblquote = EINA_TRUE; + else eina_strbuf_append_char(buf, *s); + } + } + } + else + { + eina_strbuf_append_char(buf, *s); + backslash = EINA_FALSE; + } + } + ret = eina_strbuf_string_steal(buf); + eina_strbuf_free(buf); + return ret; +} + +static char * _nedje_text_escape(const char *text) { Eina_Strbuf *txt; char *ret; const char *text_end; - size_t text_len; - Eina_Array *arr; - const char *cur_tag = NULL; + int taglen; if (!text) return NULL; txt = eina_strbuf_new(); - text_len = strlen(text); - arr = eina_array_new(3); + if (!txt) return NULL; + + text_end = text + strlen(text); - text_end = text + text_len; while (text < text_end) { - int advance; - - if ((text[0] == '<') && text[1]) + taglen = _tag_len(text); + if (taglen == 0) { - const char *tag, *popped; - Eina_Bool closing = EINA_FALSE; - - if (text[1] == '/') //closing tag - { - closing = EINA_TRUE; - tag = _get_tag(text + 2); - } - else - tag = _get_tag(text + 1); - if (closing) - { - if (cur_tag && (tag != cur_tag)) - { - /* tag mismatch: autoclose all failure tags - * not technically required by the spec, - * but it makes me feel better about myself - */ - do - { - popped = eina_array_pop(arr); - if (eina_array_count(arr)) - cur_tag = eina_array_data_get(arr, eina_array_count(arr) - 1); - else - cur_tag = NULL; - eina_strbuf_append_printf(txt, "", popped[1]); - } while (cur_tag && (popped != tag)); - advance = 4; - } - else if (cur_tag) - { - /* tag match: just pop */ - popped = eina_array_pop(arr); - if (eina_array_count(arr)) - cur_tag = eina_array_data_get(arr, eina_array_count(arr) - 1); - else - cur_tag = NULL; - eina_strbuf_append_printf(txt, "", popped[1]); - advance = 4; - } - else - { - /* no current tag: escape */ - advance = _text_escape(txt, text); - } - } - else - { - if (tag) - { - cur_tag = tag; - eina_array_push(arr, tag); - eina_strbuf_append_printf(txt, "<%c>", tag[1]); - advance = 3; - } - else - advance = _text_escape(txt, text); - } - } - else if (text[0] == '&') - { - const char *s; - - s = strchr(text, ';'); - if (s) - s = evas_textblock_escape_string_range_get(text, s + 1); - if (s) - { - eina_strbuf_append_char(txt, text[0]); - advance = 1; - } - else - advance = _text_escape(txt, text); + eina_strbuf_append_char(txt, text[0]); + text++; } else - advance = _text_escape(txt, text); + { + if (!strncmp(text, "", 3)) eina_strbuf_append(txt, ""); + else if (!strncmp(text, "", 4)) eina_strbuf_append(txt, ""); + else if (!strncmp(text, "", 3)) eina_strbuf_append(txt, ""); + else if (!strncmp(text, "", 4)) eina_strbuf_append(txt, ""); + else if (!strncmp(text, "", 3)) eina_strbuf_append(txt, ""); + else if (!strncmp(text, "", 4)) eina_strbuf_append(txt, ""); + else if (!strncmp(text, ""); + eina_strbuf_append_n(txt, text, taglen); + } + else if (!strncmp(text, "", 3)) + { + eina_strbuf_append(txt, ""); + } + else if (!strncmp(text, " 0) && (h > 0)) + { + double neww = w, newh = h; + + if (neww > 200.0) + { + double oldw = neww; + + neww = 200.0; + newh = (newh * neww) / oldw; + } + if (newh > 100.0) + { + double oldh = newh; + + newh = 100.0; + neww = (neww * newh) / oldh; + } + neww *= e_scale; + newh *= e_scale; + w = neww + 0.5; + h = newh + 0.5; + eina_strbuf_append_printf + (txt, ""); + } + evas_object_del(o); + } + free(path); + } + text += taglen; + } } - eina_array_free(arr); ret = eina_strbuf_string_steal(txt); + printf("NOT: body -> [%s]\n", ret); eina_strbuf_free(txt); return ret; } @@ -231,28 +415,30 @@ notify_cb(const Eldbus_Service_Interface *iface EINA_UNUSED, const Eldbus_Messag E_Notification_Notify *n; Eldbus_Message_Iter *actions_iter, *hints_iter; Eldbus_Message *reply; - char *txt; + char *txt, *txt2; + int num; - if (!n_data->notify_cb) - return NULL; + if (!n_data->notify_cb) return NULL; n = E_OBJECT_ALLOC(E_Notification_Notify, E_NOTIFICATION_TYPE, _notification_free); n->urgency = E_NOTIFICATION_NOTIFY_URGENCY_NORMAL; - if (!eldbus_message_arguments_get(msg, "susssasa{sv}i", &n->app_name, - &n->replaces_id, &n->icon.icon, &n->summary, - &n->body, &actions_iter, &hints_iter, - &n->timeout)) + if (!eldbus_message_arguments_get(msg, "susssasa{sv}i", + &n->app_name, &n->replaces_id, + &n->icon.icon, &n->summary, &n->body, + &actions_iter, &hints_iter, &n->timeout)) { ERR("Reading message."); e_object_del(E_OBJECT(n)); return NULL; } if (e_screensaver_on_get() && e_config->screensaver_wake_on_notify) - { + { // XXX: this is an attempt to wake the screen? should be an option int x, y; + ecore_evas_pointer_xy_get(e_comp->ee, &x, &y); ecore_evas_pointer_warp(e_comp->ee, x, y); } + // walk hints eldbus_message_iter_dict_iterate(hints_iter, "sv", hints_dict_iter, n); n->app_name = eina_stringshare_add(n->app_name); n->icon.icon = eina_stringshare_add(n->icon.icon); @@ -261,22 +447,43 @@ notify_cb(const Eldbus_Service_Interface *iface EINA_UNUSED, const Eldbus_Messag n->body = eina_stringshare_add(txt); free(txt); + num = 0; + while (eldbus_message_iter_get_and_next(actions_iter, 's', &txt)) + { + if (eldbus_message_iter_get_and_next(actions_iter, 's', &txt2)) + { // XXX: add actions to notification + E_Notification_Notify_Action *actions; + + printf("NOT: act=[%s] [%s]\n", txt, txt2); + num++; + actions = realloc(n->actions, (num + 1) * sizeof(E_Notification_Notify)); + if (actions) + { + n->actions = actions; + n->actions[num - 1].action = eina_stringshare_add(txt); + n->actions[num - 1].label = eina_stringshare_add(txt2); + n->actions[num].action = NULL; + n->actions[num].label = NULL; + } + } + } + e_object_ref(E_OBJECT(n)); n->id = n_data->notify_cb(n_data->data, n); reply = eldbus_message_method_return_new(msg); eldbus_message_arguments_append(reply, "u", n->id); e_object_unref(E_OBJECT(n)); + return reply; } static Eldbus_Message * close_notification_cb(const Eldbus_Service_Interface *iface EINA_UNUSED, const Eldbus_Message *msg) { - unsigned id; - if (!eldbus_message_arguments_get(msg, "u", &id)) - return NULL; - if (n_data->close_cb) - n_data->close_cb(n_data->data, id); + unsigned int id; + + if (!eldbus_message_arguments_get(msg, "u", &id)) return NULL; + if (n_data->close_cb) n_data->close_cb(n_data->data, id); return eldbus_message_method_return_new(msg); } @@ -291,8 +498,10 @@ capabilities_cb(const Eldbus_Service_Interface *iface EINA_UNUSED, const Eldbus_ eldbus_message_iter_arguments_append(main_iter, "as", &array); for (i = 0; n_data->server_info->capabilities[i]; i++) - eldbus_message_iter_arguments_append(array, "s", - n_data->server_info->capabilities[i]); + { + eldbus_message_iter_arguments_append + (array, "s", n_data->server_info->capabilities[i]); + } eldbus_message_iter_container_close(main_iter, array); return reply; } @@ -301,23 +510,32 @@ static Eldbus_Message * server_info_cb(const Eldbus_Service_Interface *iface EINA_UNUSED, const Eldbus_Message *msg) { Eldbus_Message *reply = eldbus_message_method_return_new(msg); - eldbus_message_arguments_append(reply, "ssss", n_data->server_info->name, - n_data->server_info->vendor, - n_data->server_info->version, - n_data->server_info->spec_version); + eldbus_message_arguments_append(reply, "ssss", + n_data->server_info->name, + n_data->server_info->vendor, + n_data->server_info->version, + n_data->server_info->spec_version); return reply; } static const Eldbus_Method methods[] = { { "Notify", - ELDBUS_ARGS({"s", "app_name"}, {"u", "replaces_id"}, {"s", "app_icon"}, {"s", "summary"}, {"s", "body"}, {"as", "actions"}, {"a{sv}", "hints"}, {"i", "expire_timeout"}), - ELDBUS_ARGS({"u", "id"}), notify_cb, 0 }, - { "CloseNotification", ELDBUS_ARGS({"u", "id"}), NULL, close_notification_cb, 0 }, - { "GetCapabilities", NULL, ELDBUS_ARGS({"as", "capabilities"}), - capabilities_cb, 0 }, - { "GetServerInformation", NULL, - ELDBUS_ARGS({"s", "name"}, {"s", "vendor"}, {"s", "version"}, {"s", "spec_version"}), - server_info_cb, 0 }, + ELDBUS_ARGS({"s", "app_name"}, {"u", "replaces_id"}, {"s", "app_icon"}, {"s", "summary"}, {"s", "body"}, {"as", "actions"}, {"a{sv}", "hints"}, {"i", "expire_timeout"}), + ELDBUS_ARGS({"u", "id"}), + notify_cb, 0 }, + { "CloseNotification", + ELDBUS_ARGS({"u", "id"}), + NULL, + close_notification_cb, 0 }, + { "GetCapabilities", + NULL, + ELDBUS_ARGS({"as", "capabilities"}), + capabilities_cb, 0 }, + { "GetServerInformation", + NULL, + ELDBUS_ARGS({"s", "name"}, {"s", "vendor"}, {"s", "version"}, {"s", "spec_version"}), + server_info_cb, 0 }, + { NULL, NULL, NULL, NULL, 0 } }; @@ -332,6 +550,7 @@ static const Eldbus_Signal signals[] = { { "NotificationClosed", ELDBUS_ARGS({"u", "id"}, {"u", "reason"}), 0 }, [SIGNAL_ACTION_INVOKED] = { "ActionInvoked", ELDBUS_ARGS({"u", "id"}, {"s", "action_key"}), 0 }, + { NULL, NULL, 0} }; @@ -347,8 +566,8 @@ E_API Eina_Bool e_notification_server_register(const E_Notification_Server_Info *server_info, E_Notification_Notify_Cb n_cb, E_Notification_Close_Cb close_cb, const void *data) { EINA_SAFETY_ON_NULL_RETURN_VAL(server_info, EINA_FALSE); - if (n_data) - return EINA_FALSE; + + if (n_data) return EINA_FALSE; n_data = calloc(1, sizeof(Notification_Data)); EINA_SAFETY_ON_NULL_RETURN_VAL(n_data, EINA_FALSE); @@ -359,8 +578,7 @@ e_notification_server_register(const E_Notification_Server_Info *server_info, E_ n_data->data = (void *)data; n_data->server_info = server_info; eldbus_name_request(n_data->conn, BUS, - ELDBUS_NAME_REQUEST_FLAG_REPLACE_EXISTING, NULL, NULL); - + ELDBUS_NAME_REQUEST_FLAG_REPLACE_EXISTING, NULL, NULL); return EINA_TRUE; } @@ -381,7 +599,17 @@ e_notification_notify_close(E_Notification_Notify *notify, E_Notification_Notify EINA_SAFETY_ON_NULL_RETURN(notify); EINA_SAFETY_ON_FALSE_RETURN(reason <= E_NOTIFICATION_NOTIFY_CLOSED_REASON_UNDEFINED); eldbus_service_signal_emit(n_data->iface, SIGNAL_NOTIFICATION_CLOSED, - notify->id, reason); + notify->id, reason); +} + +E_API void +e_notification_notify_action(E_Notification_Notify *notify, const char *action) +{ + EINA_SAFETY_ON_NULL_RETURN(n_data); + EINA_SAFETY_ON_NULL_RETURN(notify); + if (!action) action = ""; + eldbus_service_signal_emit(n_data->iface, SIGNAL_ACTION_INVOKED, + notify->id, action); } E_API Evas_Object * @@ -491,6 +719,7 @@ notification_client_dbus_send(E_Notification_Notify *notify, E_Notification_Clie { Eldbus_Message_Iter *st, *data_iter; int i; + eldbus_message_iter_arguments_append(hints, "{sv}", &entry); eldbus_message_iter_arguments_append(entry, "s", "image-data"); var = eldbus_message_iter_container_new(entry, 'v', "(iiibiiay)"); @@ -533,8 +762,7 @@ notification_client_dbus_send(E_Notification_Notify *notify, E_Notification_Clie p = eldbus_connection_send(conn, msg, client_notify_cb, data, 5000); EINA_SAFETY_ON_NULL_GOTO(p, error); - if (cb) - eldbus_pending_data_set(p, "cb", cb); + if (cb) eldbus_pending_data_set(p, "cb", cb); eldbus_pending_data_set(p, "conn", conn); return EINA_TRUE; @@ -547,8 +775,7 @@ error: static void normalize_notify(E_Notification_Notify *notify) { - if (!notify->timeout) - notify->timeout = -1; + if (!notify->timeout) notify->timeout = -1; } E_API Eina_Bool @@ -562,11 +789,12 @@ e_notification_client_send(E_Notification_Notify *notify, E_Notification_Client_ if (!n_data) { - fprintf(stderr, "UNHANDLED NOTIFICATION:\nSummary: %s\nBody: %s\n", notify->summary, notify->body); + fprintf(stderr, "UNHANDLED NOTIFICATION:\nSummary: %s\nBody: %s\n", + notify->summary, notify->body); return notification_client_dbus_send(notify, cb, data); } - //local + // local copy = malloc(sizeof(E_Notification_Notify)); EINA_SAFETY_ON_NULL_RETURN_VAL(copy, EINA_FALSE); memcpy(copy, notify, sizeof(E_Notification_Notify)); @@ -579,8 +807,7 @@ e_notification_client_send(E_Notification_Notify *notify, E_Notification_Client_ copy->icon.icon_path = eina_stringshare_add(notify->icon.icon_path); id = n_data->notify_cb(n_data->data, copy); - if (cb) - cb((void *)data, id); + if (cb) cb((void *)data, id); return EINA_TRUE; } diff --git a/src/bin/e_notification.h b/src/bin/e_notification.h index 46228982d..c93d553f4 100644 --- a/src/bin/e_notification.h +++ b/src/bin/e_notification.h @@ -7,35 +7,39 @@ typedef enum _E_Notification_Notify_Urgency { - E_NOTIFICATION_NOTIFY_URGENCY_LOW, - E_NOTIFICATION_NOTIFY_URGENCY_NORMAL, - E_NOTIFICATION_NOTIFY_URGENCY_CRITICAL + E_NOTIFICATION_NOTIFY_URGENCY_LOW = 0, + E_NOTIFICATION_NOTIFY_URGENCY_NORMAL = 1, + E_NOTIFICATION_NOTIFY_URGENCY_CRITICAL = 2 } E_Notification_Notify_Urgency; typedef enum _E_Notification_Notify_Closed_Reason { - E_NOTIFICATION_NOTIFY_CLOSED_REASON_EXPIRED, /** The notification expired. */ - E_NOTIFICATION_NOTIFY_CLOSED_REASON_DISMISSED, /** The notification was dismissed by the user. */ - E_NOTIFICATION_NOTIFY_CLOSED_REASON_REQUESTED, /** The notification was closed by a call to CloseNotification method. */ - E_NOTIFICATION_NOTIFY_CLOSED_REASON_UNDEFINED /** Undefined/reserved reasons. */ + E_NOTIFICATION_NOTIFY_CLOSED_REASON_EXPIRED = 1, + E_NOTIFICATION_NOTIFY_CLOSED_REASON_DISMISSED = 2, + E_NOTIFICATION_NOTIFY_CLOSED_REASON_REQUESTED = 3, + E_NOTIFICATION_NOTIFY_CLOSED_REASON_UNDEFINED = 4 } E_Notification_Notify_Closed_Reason; +typedef struct _E_Notification_Notify_Action +{ + const char *action; + const char *label; +} E_Notification_Notify_Action; + typedef struct _E_Notification_Notify { E_Object e_obj_inherit; unsigned int id; const char *app_name; - unsigned replaces_id; + unsigned int replaces_id; const char *summary; const char *body; - int timeout; + int timeout; // time in ms E_Notification_Notify_Urgency urgency; - struct - { + struct { const char *icon; const char *icon_path; - struct - { + struct { int width; int height; int rowstride; @@ -46,6 +50,17 @@ typedef struct _E_Notification_Notify int data_size; } raw; } icon; + const char *category; + const char *desktop_entry; + const char *sound_file; + const char *sound_name; + int x, y; + Eina_Bool have_xy; + Eina_Bool icon_actions; + Eina_Bool resident; + Eina_Bool suppress_sound; + Eina_Bool transient; + E_Notification_Notify_Action *actions; } E_Notification_Notify; typedef unsigned int (*E_Notification_Notify_Cb)(void *data, E_Notification_Notify *n); @@ -80,7 +95,9 @@ E_API Evas_Object *e_notification_notify_raw_image_get(E_Notification_Notify *no //client typedef void (*E_Notification_Client_Send_Cb)(void *data, unsigned int id); + E_API Eina_Bool e_notification_client_send(E_Notification_Notify *notify, E_Notification_Client_Send_Cb cb, const void *data); +E_API void e_notification_notify_action(E_Notification_Notify *notify, const char *action); E_API Eina_Bool e_notification_util_send(const char *summary, const char *body); #endif diff --git a/src/modules/notification/e_mod_main.c b/src/modules/notification/e_mod_main.c index e8afe13b4..ec3c9eb8c 100644 --- a/src/modules/notification/e_mod_main.c +++ b/src/modules/notification/e_mod_main.c @@ -28,11 +28,20 @@ _notification_notify(E_Notification_Notify *n) E_API E_Module_Api e_modapi = {E_MODULE_API_VERSION, "Notification"}; static const E_Notification_Server_Info server_info = { - .name = "e17", - .vendor = "enlightenment.org", - .version = "0.17", + .name = "Notification Service", + .vendor = "Enlightenment", + .version = PACKAGE_VERSION, .spec_version = "1.2", - .capabilities = { "body", "body-markup", NULL } + .capabilities = { + "body", "body-markup", + "body-hyperlinks", "body-images", + "actions", "action-icons", +// "icon-multi", +// or +// "icon-static", + "persistence", +// "sound", + NULL } }; /* Callbacks */ diff --git a/src/modules/notification/e_mod_main.h b/src/modules/notification/e_mod_main.h index 714448166..094e23847 100644 --- a/src/modules/notification/e_mod_main.h +++ b/src/modules/notification/e_mod_main.h @@ -31,34 +31,37 @@ typedef enum struct _Config { - E_Config_Dialog *cfd; + E_Config_Dialog *cfd; - int version; - int show_low; - int show_normal; - int show_critical; - int force_timeout; - int ignore_replacement; - Popup_Display_Policy dual_screen; - float timeout; - Popup_Corner corner; + int version; + int show_low; + int show_normal; + int show_critical; + int force_timeout; + int ignore_replacement; + Popup_Display_Policy dual_screen; + float timeout; + Popup_Corner corner; - Eina_List *popups; - unsigned int next_id; + Eina_List *popups; + unsigned int next_id; }; struct _Popup_Data { - unsigned id; - E_Notification_Notify *notif; - Evas_Object *win; - Eina_List *mirrors; - Evas *e; - Evas_Object *theme; - const char *app_name; - Evas_Object *app_icon; - Ecore_Timer *timer; - Eina_Bool pending E_BITFIELD; + unsigned id; + E_Notification_Notify *notif; + Evas_Object *win; + Eina_List *mirrors; + Evas *e; + Evas_Object *theme; + const char *app_name; + Evas_Object *app_icon; + Evas_Object *desktop_icon; + Evas_Object *action_box; + Eina_List *actions; + Ecore_Timer *timer; + Eina_Bool pending E_BITFIELD; }; diff --git a/src/modules/notification/e_mod_popup.c b/src/modules/notification/e_mod_popup.c index 444bc551f..6cf2ccc54 100644 --- a/src/modules/notification/e_mod_popup.c +++ b/src/modules/notification/e_mod_popup.c @@ -239,6 +239,61 @@ _notification_theme_cb_find(Popup_Data *popup, } } +static void +_notification_theme_cb_anchor(Popup_Data *popup EINA_UNUSED, + Evas_Object *obj EINA_UNUSED, + const char *emission, + const char *source EINA_UNUSED) +{ + if (!strncmp(emission, "anchor,mouse,clicked,1,", + strlen("anchor,mouse,clicked,1,"))) + { + const char *href = emission + strlen("anchor,mouse,clicked,1,"); + Eina_Strbuf *buf = eina_strbuf_new(); + + if (buf) + { + const char *s; + + eina_strbuf_append(buf, href); + s = eina_strbuf_string_get(buf); + if ((s) && (*s == '"')) + { + eina_strbuf_remove(buf, 0, 1); + s = eina_strbuf_string_get(buf); + if ((s) && (strlen(s) > 0) && (s[strlen(s) - 1] == '"')) + eina_strbuf_replace_last(buf, "\"", ""); + } + if ((s) && (*s == '\'')) + { + eina_strbuf_remove(buf, 0, 1); + s = eina_strbuf_string_get(buf); + if ((s) && (strlen(s) > 0) && (s[strlen(s) - 1] == '\'')) + eina_strbuf_replace_last(buf, "'", ""); + } + printf("NOT: clicked=[%s]\n", eina_strbuf_string_get(buf)); + e_util_open(eina_strbuf_string_get(buf), NULL); + eina_strbuf_free(buf); + } + } +} + +static void +_notification_theme_cb_action(Popup_Data *popup, + Evas_Object *obj, + const char *emission EINA_UNUSED, + const char *source EINA_UNUSED) +{ + const char *action = evas_object_data_get(obj, "action"); + + if (action) + { + printf("NOT: action=[%s]\n", action); + e_notification_notify_action(popup->notif, action); + } +} + + static void _notification_popup_place_coords_get(int zw, int zh, int ow, int oh, int pos, int *x, int *y) { @@ -269,6 +324,22 @@ _notification_popup_del_cb(void *data, Evas *e EINA_UNUSED, Evas_Object *obj EIN popup->win = NULL; } +static Evas_Object * +_cb_item_provider(void *data, Evas_Object *obj EINA_UNUSED, const char *part, const char *item) +{ + Popup_Data *popup = data; + + printf("NOT: PROVIDER.... [%s] item: [%s]\n", part, item); +// if (!strcmp(part, "notification.textblock.message")) + { + Evas_Object *o = e_icon_add(popup->e); + + e_icon_file_set(o, item); + return o; + } + return NULL; +} + static Popup_Data * _notification_popup_new(E_Notification_Notify *n, unsigned id) { @@ -307,6 +378,7 @@ _notification_popup_new(E_Notification_Notify *n, unsigned id) /* Setup the theme */ popup->theme = edje_object_add(popup->e); + edje_object_item_provider_set(popup->theme, _cb_item_provider, popup); e_theme_edje_object_set(popup->theme, "base/theme/modules/notification", "e/modules/notification/main"); @@ -318,14 +390,17 @@ _notification_popup_new(E_Notification_Notify *n, unsigned id) evas_object_event_callback_add(popup->win, EVAS_CALLBACK_DEL, _notification_popup_del_cb, popup); edje_object_signal_callback_add - (popup->theme, "notification,deleted", "theme", + (popup->theme, "notification,deleted", "*", (Edje_Signal_Cb)_notification_theme_cb_deleted, popup); edje_object_signal_callback_add - (popup->theme, "notification,close", "theme", + (popup->theme, "notification,close", "*", (Edje_Signal_Cb)_notification_theme_cb_close, popup); edje_object_signal_callback_add - (popup->theme, "notification,find", "theme", + (popup->theme, "notification,find", "*", (Edje_Signal_Cb)_notification_theme_cb_find, popup); + edje_object_signal_callback_add + (popup->theme, "anchor,mouse,clicked,1,*", "notification.textblock.message", + (Edje_Signal_Cb)_notification_theme_cb_anchor, popup); _notification_popup_refresh(popup); next_pos = _notification_popup_place(popup, next_pos); @@ -386,17 +461,35 @@ _notification_popup_refresh(Popup_Data *popup) const char *app_icon_max; int w, h, width = 80, height = 80; E_Zone *zone; + Evas_Object *o; if (!popup) return; popup->app_name = popup->notif->app_name; + EINA_LIST_FREE(popup->actions, o) + { + evas_object_del(o); + } + if (popup->action_box) + { + e_comp_object_util_del_list_remove(popup->win, popup->action_box); + E_FREE_FUNC(popup->action_box, evas_object_del); + edje_object_signal_emit(popup->theme, "e,state,actions,hide", "e"); + } + if (popup->app_icon) { e_comp_object_util_del_list_remove(popup->win, popup->app_icon); E_FREE_FUNC(popup->app_icon, evas_object_del); } + if (popup->desktop_icon) + { + e_comp_object_util_del_list_remove(popup->win, popup->desktop_icon); + E_FREE_FUNC(popup->desktop_icon, evas_object_del); + } + app_icon_max = edje_object_data_get(popup->theme, "app_icon_max"); if (app_icon_max) { @@ -452,7 +545,7 @@ _notification_popup_refresh(Popup_Data *popup) icon_path = new_path; else { - Evas_Object *o = e_icon_add(popup->e); + o = e_icon_add(popup->e); if (!e_util_icon_theme_set(o, icon_path)) evas_object_del(o); else @@ -510,12 +603,79 @@ _notification_popup_refresh(Popup_Data *popup) popup->app_icon); edje_object_signal_emit(popup->theme, "notification,icon", "notification"); + if ((popup->notif->desktop_entry) && + (edje_object_part_exists(popup->theme, "notification.swallow.desktop_icon"))) + { + Efreet_Desktop *desktop; + unsigned int size; + const char *icon_path; + char buf[1024]; + + snprintf(buf, sizeof(buf), "%s.desktop", popup->notif->desktop_entry); + desktop = efreet_util_desktop_file_id_find(buf); + if ((desktop) && (desktop->icon)) + { + size = e_util_icon_size_normalize(width * e_scale); + icon_path = efreet_icon_path_find(e_config->icon_theme, + desktop->icon, size); + efreet_desktop_free(desktop); + + o = e_icon_add(popup->e); + if (!e_util_icon_theme_set(o, icon_path)) + evas_object_del(o); + else + { + popup->desktop_icon = o; + edje_object_part_swallow(popup->theme, + "notification.swallow.desktop_icon", + popup->desktop_icon); + evas_object_show(o); + e_comp_object_util_del_list_append(popup->win, o); + } + } + } /* Fill up the event message */ _notification_format_message(popup); + if (popup->notif->actions) + { + int i; + + o = popup->action_box = elm_box_add(e_comp->elm); + elm_box_homogeneous_set(o, EINA_TRUE); + elm_box_horizontal_set(o, EINA_TRUE); + e_comp_object_util_del_list_append(popup->win, o); + for (i = 0; popup->notif->actions[i].action; i++) + { + o = edje_object_add(popup->e); + e_theme_edje_object_set(o, + "base/theme/modules/notification", + "e/modules/notification/action"); + evas_object_data_set(o, "action", popup->notif->actions[i].action); + edje_object_part_text_unescaped_set(o, "e.text.label", + popup->notif->actions[i].label); + edje_object_signal_callback_add + (o, "e,action,clicked", "e", + (Edje_Signal_Cb)_notification_theme_cb_action, popup); + edje_object_size_min_calc(o, &w, &h); + evas_object_size_hint_min_set(o, w, h); + printf("NOT: act %ix%i\n", w, h); + elm_box_pack_end(popup->action_box, o); + evas_object_show(o); + } +// evas_smart_objects_calculate(popup->e); +// edje_message_signal_process(); + evas_smart_objects_calculate(popup->e); + evas_object_size_hint_min_get(popup->action_box, &w, &h); + printf("NOT: actbox %ix%i\n", w, h); + edje_object_part_swallow(popup->theme, "notification.swallow.actions", popup->action_box); + edje_object_signal_emit(popup->theme, "e,state,actions,show", "e"); + } + /* Compute the new size of the popup */ edje_object_calc_force(popup->theme); edje_object_size_min_calc(popup->theme, &w, &h); + printf("NOT: min %ix%i\n", w, h); if ((zone = e_comp_object_util_zone_get(popup->win))) { w = MIN(w, zone->w / 2); @@ -585,13 +745,18 @@ _notification_format_message(Popup_Data *popup) { Evas_Object *o = popup->theme; Eina_Strbuf *buf = eina_strbuf_new(); + + printf("NOT: set message... [%s]\n", popup->notif->body); edje_object_part_text_unescaped_set(o, "notification.text.title", - popup->notif->summary); + popup->notif->summary); /* FIXME: Filter to only include allowed markup? */ /* We need to replace \n with . FIXME: We need to handle all the * newline kinds, and paragraph separator. ATM this will suffice. */ eina_strbuf_append(buf, popup->notif->body); eina_strbuf_replace_all(buf, "\n", "
"); + // message is thge shadow sizer part + edje_object_part_text_set(o, "message", + eina_strbuf_string_get(buf)); edje_object_part_text_set(o, "notification.textblock.message", eina_strbuf_string_get(buf)); eina_strbuf_free(buf);