/* -*- Mode: C; indent-tabs-mode: nil; tab-width: 2 -*- * * Copyright (C) 2009,2010 Canonical Ltd. * Authors: * Gustavo Sverzut Barbieri <gustavo.barbieri@canonical.com> * Michael Terry <michael.terry@canonical.com> * * This file is part of Netbook Launcher EFL. * * Netbook Launcher EFL is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3 of the License. * * Netbook Launcher EFL is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Netbook Launcher EFL. If not, see <http://www.gnu.org/licenses/>. */ #include "netbook-launcher.h" #include <Elementary.h> #include <glib/gi18n.h> #ifdef HAVE_GOOGLE_GADGETS #include <libgadget/libgadget.h> #endif #define LINK_WATCH_DIR "/tmp/webfav" static const char _EDJE_GROUP_LAYOUT[] = "e/netbook/launcher/main/apps/layout"; static const char _EDJE_GROUP_GRID[] = "e/netbook/launcher/main/apps/grid"; static const char _EDJE_GROUP_ITEM[] = "e/netbook/launcher/main/apps/item"; static const char _EDJE_PART_CONTENTS[] = "e.box.contents"; static const char _EDJE_PART_SCROLLER[] = "e.swallow.scroller"; static const char _EDJE_PART_QUIT[] = "e.swallow.quit"; static const char _EDJE_SIG_SRC[] = "e"; static const char _EDJE_SIG_FOCUS_IN[] = "gui,action,focus,in"; static const char _EDJE_SIG_ENTER[] = "gui,action,enter"; static const char _EDJE_SIG_EXIT[] = "gui,action,exit"; static const char _EDJE_SIG_QUIT_EXECUTED[] = "gui,action,quit,executed"; static const char _EDJE_SIG_ENTER_END[] = "gui,action,enter,end"; static const char _EDJE_SIG_EXIT_END[] = "gui,action,exit,end"; static const char _EDJE_SIG_FAV_ADDED[] = "gui,state,favorite,added"; static const char _EDJE_SIG_QUIT_FAV_ENTER[] = "gui,action,quit,favorites,enter"; static const char _EDJE_SIG_QUIT_FAV_EXIT[] = "gui,action,quit,favorites,exit"; static const char _EDJE_SIG_QUIT_CAT_ENTER[] = "gui,action,quit,categories,enter"; static const char _EDJE_SIG_QUIT_CAT_EXIT[] = "gui,action,quit,categories,exit"; static const char _favorites_id[] = "_nl_apps_favorites"; static const char _gadgets_id[] = "Widgets"; static const char _apps_key[] = "_nl_apps"; static const char _ICON_QUIT[] = "gnome-logout"; int FAVORITES_EVENT_CHANGED = 0; struct apps_context { Evas_Object *apps; Evas_Object *edje; Evas_Object *scroller; Evas_Object *grid; Evas_Object *quit; const char *favorites_id; const char *gadgets_id; const char *category; const char *pending_category; const char *quit_icon; void (*pending_end_callback)(void *data, Evas_Object *apps); void *pending_end_callback_data; GFile *watch_dir; GFileMonitor *dir_monitor; struct { Ecore_Event_Handler *favorites_changed; } handler; struct app_item_common item; }; static inline void _apps_context_set(Evas_Object *obj, struct apps_context *ctxt) { evas_object_data_set(obj, _apps_key, ctxt); } static inline struct apps_context * _apps_context_get(Evas_Object *obj) { return evas_object_data_get(obj, _apps_key); } static void _apps_quit_executed(void *data, Evas_Object *o, const char *signal __UNUSED__, const char *source __UNUSED__) { Evas_Object *apps = data; evas_object_smart_callback_call(apps, CALLBACK_APPS_QUIT_SELECTED, NULL); sound_play(o, "button-pressed"); sound_play(o, "button-released"); } static void _apps_ecore_quit_show(struct apps_context *ctxt, Eina_Bool is_favorite) { #if USE_QUIT const char *sig_enter, *sig_exit; Evas_Object *ed; if (!ctxt->quit_icon) return; if (!ctxt->quit) { ctxt->quit = _nl_icon_load_sized (ctxt->apps, ctxt->quit_icon, ctxt->item.item.icon_size); if (!ctxt->quit) return; elm_layout_content_set(ctxt->apps, _EDJE_PART_QUIT, ctxt->quit); } if (is_favorite) { sig_enter = _EDJE_SIG_QUIT_FAV_ENTER; sig_exit = _EDJE_SIG_QUIT_CAT_EXIT; } else { sig_enter = _EDJE_SIG_QUIT_CAT_ENTER; sig_exit = _EDJE_SIG_QUIT_FAV_EXIT; } ed = elm_layout_edje_get(ctxt->apps); edje_object_signal_emit(ed, sig_exit, _EDJE_SIG_SRC); edje_object_signal_emit(ed, sig_enter, _EDJE_SIG_SRC); #endif } static LauncherCategory * _apps_find_category(const struct apps_context *ctxt, const char *category) { const GSList *n = launcher_menu_get_categories(ctxt->item.menu); for (; n != NULL; n = n->next) { LauncherCategory *cat = n->data; const char *cur = launcher_category_get_name(cat); if (cur && strcmp(cur, category) == 0) return cat; } return NULL; } static void _apps_glib_load_category(void *data, const char *category) { struct apps_context *ctxt = data; /* do not use ctxt->category here! */ LauncherCategory *cat; GSList *lst, *n; unsigned int i, len; Evas_Object *ed; cat = _apps_find_category(ctxt, category); if (!cat) { g_warning("could not find category '%s' in menu.", category); goto error; } ed = elm_layout_edje_get(ctxt->grid); if (!edje_object_part_box_remove_all(ed, _EDJE_PART_CONTENTS, 1)) { ERR("could not clear box\n"); return; } evas_object_size_hint_min_set(ctxt->grid, 0, 0); lst = launcher_category_get_applications(cat); len = g_slist_length(lst); Eina_Bool is_favorite = EINA_FALSE; for (i = 1, n = lst; n != NULL; n = n->next, i += 5) { Evas_Object *item; LauncherApplication *app = n->data; const char *icon_name, *name, *comment, *app_icon, *match_name, *dfile; name = launcher_application_get_name(app); name = !name ? "" : name; comment = launcher_application_get_comment(app); comment = !comment ? "" : comment; app_icon = launcher_application_get_icon_name(app); match_name = launcher_application_get_unique_string(app); match_name = !match_name ? "" : match_name; dfile = launcher_application_get_desktop_file(app); dfile = !dfile ? "" : dfile; icon_name = icon_path_get(app_icon, ctxt->item.item.icon_size); if (icon_name) app_icon = icon_name; app_icon = !app_icon ? "" : app_icon; item = app_item_add (&ctxt->item, ctxt->category, name, comment, app_icon, match_name, dfile, is_favorite); if (!item) continue; edje_object_part_box_append(ed, _EDJE_PART_CONTENTS, item); } _apps_ecore_quit_show(ctxt, is_favorite); return; error: _apps_ecore_quit_show(ctxt, ctxt->category == ctxt->favorites_id); } static LauncherApplication * _apps_glib_favorite_app_find(const struct apps_context *ctxt, const char *uid) { LauncherApplication *app; gchar *type, *desktop_file; type = launcher_favorites_get_string(ctxt->item.favs, uid, "type"); if (!type) { g_warning("no type for favorite id '%s'", uid); return NULL; } if (g_strcmp0(type, "application") != 0) { g_debug("favorite type '%s' not supported for id '%s'", type, uid); g_free(type); return NULL; } g_free(type); desktop_file = launcher_favorites_get_string (ctxt->item.favs, uid, "desktop_file"); if (!desktop_file || !g_file_test(desktop_file, G_FILE_TEST_EXISTS)) { g_warning("no desktop file for favorite id '%s'", uid); g_free(desktop_file); return NULL; } app = launcher_application_new_from_desktop_file(desktop_file); if (!app) { g_warning("could not create launcher app for desktop file '%s', favorite id '%s'", desktop_file, uid); g_free(desktop_file); return NULL; } /* If loading desktop failed, sometimes we'll get a valid, but empty LauncherApplication. Check for that here. */ if (!launcher_application_get_desktop_file(app)) { g_warning("could not create launcher app for desktop file '%s', favorite id '%s'", desktop_file, uid); g_free(desktop_file); return NULL; } g_free(desktop_file); return app; } static gboolean _apps_glib_gadget_uri_find(const struct apps_context *ctxt, const char *uid) { gchar *type; type = launcher_favorites_get_string(ctxt->item.favs, uid, "type"); if (!type) { g_warning("no type for favorite id '%s'", uid); return FALSE; } if (g_strcmp0(type, "libgadget-ggadget") != 0) { g_debug("favorite type '%s' not supported for id '%s'", type, uid); g_free(type); return FALSE; } g_free(type); return TRUE; } static gboolean _apps_glib_favorite_uri_find(const struct apps_context *ctxt, const char *uid) { gchar *type; type = launcher_favorites_get_string(ctxt->item.favs, uid, "type"); if (!type) { g_warning("no type for favorite id '%s'", uid); return FALSE; } if (g_strcmp0(type, "uri") != 0) { g_debug("favorite type '%s' not supported for id '%s'", type, uid); g_free(type); return FALSE; } g_free(type); return TRUE; } #ifdef HAVE_GOOGLE_GADGETS static gint _sort_gadget(GadgetInfo *a, GadgetInfo *b) { if (!a) return -1; if (!b) return 1; if (G_UNLIKELY(a == b)) return 0; return g_strcmp0(a->name, b->name); } static gchar * _get_icon(const char *icon_name) { gchar *icon, *escaped; if (!icon_name) return NULL; escaped = g_uri_escape_string(icon_name, NULL, TRUE); icon = g_build_filename(DATADIR, PACKAGE, "icons", escaped, NULL); if (!g_file_test(icon, G_FILE_TEST_EXISTS)) { g_free(icon); icon = g_build_filename(DATADIR, "netbook-launcher", "icons", escaped, NULL); if (!g_file_test(icon, G_FILE_TEST_EXISTS)) { g_free(icon); icon = g_build_filename(g_get_user_cache_dir(), PACKAGE, "icons", escaped, NULL); if (!g_file_test(icon, G_FILE_TEST_EXISTS)) { // download icon GFile *remote = g_file_new_for_uri(icon_name); GFile *local = g_file_new_for_path(icon); GFile *dir = g_file_get_parent(local); g_file_make_directory_with_parents(dir, NULL, NULL); if (!g_file_copy(remote, local, G_FILE_COPY_NONE, NULL, NULL, NULL, NULL)) { g_free(icon); icon = NULL; } g_object_unref(remote); g_object_unref(local); g_object_unref(dir); } } } g_free(escaped); return icon; } static void _apps_glib_load_gadgets(void *data) { struct apps_context *ctxt = data; GList *gadgets, *g; Evas_Object *ed; GadgetManager *manager; ed = elm_layout_edje_get(ctxt->grid); if (!edje_object_part_box_remove_all(ed, _EDJE_PART_CONTENTS, 1)) { ERR("could not clear box\n"); return; } evas_object_size_hint_min_set(ctxt->grid, 0, 0); manager = gadget_manager_get_default(); if (!manager) { ERR("No gadget manager\n"); return; } /* Just in case the sources haven`t been loaded before */ gadget_manager_load_sources(manager); gadgets = gadget_manager_get_available_gadgets(manager); gadgets = g_list_sort(gadgets, (GCompareFunc)_sort_gadget); for (g = gadgets; g; g = g->next) { GadgetInfo *info = g->data; Evas_Object *item; char *gadget_name = NULL; char *icon = NULL; char *uid; if (!info) continue; uid = efl_gadget_uid_make(); /* First check if there is an icon based on the name of the gadget file. This might happen if we pre-ship some icons. */ gadget_name = g_path_get_basename(info->path); icon = _get_icon(gadget_name); if (!icon && info->icon_name) icon = _get_icon(info->icon_name); if (!icon && info->icon) { char *escaped = g_uri_escape_string(gadget_name, NULL, TRUE); icon = gstuff_pixbuf_to_file(info->icon, escaped); g_free(escaped); } g_free(gadget_name); item = app_gadget_add (&ctxt->item, ctxt->category, info->name, info->description, icon, info->path, uid, EINA_FALSE); g_free(uid); g_free(icon); if (!item) continue; edje_object_part_box_append(ed, _EDJE_PART_CONTENTS, item); } g_list_foreach(gadgets, (GFunc)gadget_info_free, NULL); g_list_free(gadgets); g_object_unref(manager); } #endif static void _apps_glib_load_favorites(void *data) { struct apps_context *ctxt = data; /* do not use ctxt->category here! */ GSList *lst, *n; unsigned int len; gboolean fav_uri, is_gadget; Evas_Object *ed; Eina_Bool is_favorite; ed = elm_layout_edje_get(ctxt->grid); if (!edje_object_part_box_remove_all(ed, _EDJE_PART_CONTENTS, 1)) { ERR("could not clear box\n"); return; } evas_object_size_hint_min_set(ctxt->grid, 0, 0); lst = launcher_favorites_get_favorites(ctxt->item.favs); len = g_slist_length(lst); is_favorite = EINA_TRUE; for (n = lst; n != NULL; n = n->next) { Evas_Object *item; gchar *uid = n->data; is_gadget = _apps_glib_gadget_uri_find(ctxt, uid); if (is_gadget) { #ifdef HAVE_GOOGLE_GADGETS gchar *path = launcher_favorites_get_string(ctxt->item.favs, uid, "path"); g_debug("load gadget '%s' '%s'", uid, path); item = app_gadget_add(&ctxt->item, NULL, NULL, NULL, NULL, path, uid, is_favorite); evas_object_show(item); #else item = NULL; #endif } else { LauncherApplication *app; const char *icon_name, *name, *comment, *app_icon_name, *match_name; fav_uri = FALSE; app = _apps_glib_favorite_app_find(ctxt, uid); if (!app) { fav_uri = _apps_glib_favorite_uri_find(ctxt, uid); if (!fav_uri) continue; } name = fav_uri ? launcher_favorites_get_string(ctxt->item.favs, uid, "name") : launcher_application_get_name(app); name = !name ? "" : name; comment = fav_uri ? launcher_favorites_get_string(ctxt->item.favs, uid, "comment") : launcher_application_get_comment(app); comment = !comment ? "" : comment; app_icon_name = fav_uri ? launcher_favorites_get_string(ctxt->item.favs, uid, "icon") : launcher_application_get_icon_name(app); match_name = fav_uri ? "firefox" : launcher_application_get_unique_string(app); match_name = !match_name ? "" : match_name; icon_name = icon_path_get(app_icon_name, ctxt->item.item.icon_size); if (icon_name) app_icon_name = icon_name; if (!g_file_test(app_icon_name, G_FILE_TEST_EXISTS)) { /* Provide some sort of backup icon */ const char *backup_name; if (strcmp(launcher_favorites_get_string(ctxt->item.favs, uid, "type"), "uri") == 0) backup_name = "applications-internet"; else backup_name = "applications-other"; app_icon_name = icon_path_get(backup_name, ctxt->item.item.icon_size); } g_debug("load favorite '%s' '%s' '%s' '%s' '%s'", name, comment, app_icon_name, match_name, uid); item = app_item_add (&ctxt->item, ctxt->category, name, comment, app_icon_name, match_name, uid, is_favorite); } if (!item) continue; edje_object_part_box_append(ed, _EDJE_PART_CONTENTS, item); } while (lst) { g_free(lst->data); lst = g_slist_delete_link(lst, lst); } _apps_ecore_quit_show(ctxt, is_favorite); } static Eina_Bool _apps_load(struct apps_context *ctxt) { /* use given parameter (copy) and not the ctxt->category from inside glib! */ app_item_common_navigation_id_set(&ctxt->item, NULL); #ifdef HAVE_GOOGLE_GADGETS if (ctxt->category == ctxt->gadgets_id) return gstuff_glib_run_simple(_apps_glib_load_gadgets, ctxt); else if (ctxt->category != ctxt->favorites_id) #else if (ctxt->category != ctxt->favorites_id) #endif return gstuff_glib_run_ptr_string (_apps_glib_load_category, ctxt, ctxt->category); else return gstuff_glib_run_simple(_apps_glib_load_favorites, ctxt); } static Eina_Bool _apps_load_internal(struct apps_context *ctxt, const char *category, Eina_Bool animate, void (*end_callback)(void *data, Evas_Object *apps), const void *end_callback_data) { category = eina_stringshare_ref(category); if (!animate) { if (category == ctxt->category) { eina_stringshare_del(category); return 1; } DBG("immediate replace category '%s' with '%s'\n", ctxt->category, category); eina_stringshare_del(ctxt->category); ctxt->category = category; return _apps_load(ctxt); } if (category == ctxt->pending_category) { eina_stringshare_del(category); return 1; } else if (ctxt->pending_category) WRN("loading apps category '%s' while other '%s' was pending.\n", category, ctxt->pending_category); if (ctxt->pending_end_callback) ctxt->pending_end_callback(ctxt->pending_end_callback_data, ctxt->apps); ctxt->pending_end_callback = end_callback; ctxt->pending_end_callback_data = (void *)end_callback_data; eina_stringshare_del(ctxt->pending_category); ctxt->pending_category = category; DBG("animate replace category '%s' with '%s'\n", ctxt->category, category); edje_object_signal_emit(ctxt->edje, _EDJE_SIG_EXIT, _EDJE_SIG_SRC); return 1; } Eina_Bool apps_load_category(Evas_Object *apps, const char *category, Eina_Bool animate, void (*end_callback)(void *data, Evas_Object *apps), const void *end_callback_data) { struct apps_context *ctxt = _apps_context_get(apps); Eina_Bool ret; category = eina_stringshare_add(category); ret = _apps_load_internal (ctxt, category, animate, end_callback, end_callback_data); eina_stringshare_del(category); return ret; } Eina_Bool apps_load_favorites(Evas_Object *apps, Eina_Bool animate, void (*end_callback)(void *data, Evas_Object *apps), const void *end_callback_data) { struct apps_context *ctxt = _apps_context_get(apps); return _apps_load_internal (ctxt, ctxt->favorites_id, animate, end_callback, end_callback_data); } static void _apps_box_size_hints_changed(void *data, Evas *e __UNUSED__, Evas_Object *o, void *event_info __UNUSED__) { Evas_Object *grid = data; Evas_Coord w, h; evas_object_size_hint_min_get(o, &w, &h); evas_object_size_hint_min_set(grid, w, h); evas_object_resize(grid, w, h); } static void _apps_glib_stop(void *data) { struct apps_context *ctxt = data; if (ctxt->watch_dir) { g_object_unref(ctxt->watch_dir); ctxt->watch_dir = NULL; } if (ctxt->dir_monitor) { g_object_unref(ctxt->dir_monitor); ctxt->dir_monitor = NULL; } app_item_common_glib_stop(&ctxt->item); free(ctxt); } static void _app_ecore_favs_changed(void) { ecore_event_add(FAVORITES_EVENT_CHANGED, NULL, NULL, NULL); } static void _app_ecore_favs_added(void *data) { struct apps_context *ctxt = data; edje_object_signal_emit(ctxt->edje, _EDJE_SIG_FAV_ADDED, _EDJE_SIG_SRC); _app_ecore_favs_changed(); } static gchar * _make_thumbnail_hash(const gchar *name) { gchar *temp; gchar *ret; temp = g_compute_checksum_for_string(G_CHECKSUM_MD5, name, -1); ret = g_strconcat(temp, ".png", NULL); g_free (temp); return ret; } static gchar * _apps_glib_create_uri_icon(struct apps_context *ctxt, gchar *uid, gchar *name, gchar *uri) { gchar *thumbhash; gchar *thumbfile; gchar *escaped; gchar *cmd; gchar *ret; gchar *dirpath; LauncherFavorites *favs = launcher_favorites_get_default(); if (!g_str_has_prefix(uri, "http")) { g_warning("Unable to create thumbnail for a non-http page.\n"); return NULL; } escaped = g_markup_escape_text(name, -1); thumbhash = _make_thumbnail_hash(escaped); dirpath = g_build_filename(g_get_user_cache_dir(), PACKAGE, "webicons", NULL); g_mkdir_with_parents(dirpath, 0700); thumbfile = g_build_filename(dirpath, thumbhash, NULL); cmd = g_strdup_printf("gnome-web-photo --mode=thumbnail \"%s\" %s", uri, thumbfile); g_print("cmd: %s\n", cmd); if (g_spawn_command_line_sync(cmd, NULL, NULL, NULL, NULL)) ret = g_strdup(thumbfile); else ret = NULL; g_free(thumbhash); g_free(escaped); g_free(cmd); g_free(dirpath); g_free(thumbfile); return ret; } static void _apps_glib_on_watch_dir_changed(GFileMonitor *monitor, GFile *file_changed, GFile *other_file, GFileMonitorEvent event, struct apps_context *ctxt) { LauncherFavorites *favs; GKeyFile *file; gchar *link_path, *name, *url, *uid, *comment, *icon; if (event != G_FILE_MONITOR_EVENT_CREATED) return; link_path = g_file_get_path (file_changed); if (!(link_path && g_str_has_suffix(link_path, ".desktop"))) return; file = g_key_file_new(); if (!g_key_file_load_from_file(file, link_path, 0, NULL)) { g_warning("Unable to open %s, cannot add web favorite", link_path); return; } name = g_key_file_get_string(file, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_NAME, NULL); url = g_key_file_get_string(file, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_URL, NULL); comment = g_key_file_get_string(file, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_COMMENT, NULL); uid = g_strdup_printf("uri-%d", (gint)time(NULL)); if (!name) name = g_strdup(url); icon = _apps_glib_create_uri_icon(ctxt, uid, name, url); favs = launcher_favorites_get_default(); launcher_favorites_set_string(favs, uid, "type", "uri"); launcher_favorites_set_string(favs, uid, "uri", url); launcher_favorites_set_string(favs, uid, "name", name); launcher_favorites_set_string(favs, uid, "comment", comment); launcher_favorites_set_string(favs, uid, "icon", icon); launcher_favorites_add_favorite(favs, uid); gstuff_ecore_run_simple(_app_ecore_favs_added, ctxt); g_object_unref(favs); g_free(link_path); g_free(name); g_free(url); g_free(comment); g_free(uid); g_free(icon); } static void _apps_watch_links(struct apps_context *ctxt) { GFile *dir; g_mkdir_with_parents(LINK_WATCH_DIR, 0700); dir = ctxt->watch_dir = g_file_new_for_path(LINK_WATCH_DIR); ctxt->dir_monitor = g_file_monitor_directory(dir, 0, NULL, NULL); g_signal_connect(ctxt->dir_monitor, "changed", G_CALLBACK(_apps_glib_on_watch_dir_changed), ctxt); } static void _apps_glib_start(void *data) { struct apps_context *ctxt = data; app_item_common_glib_start(&ctxt->item); ctxt->quit_icon = icon_unr_path_get(_ICON_QUIT, ctxt->item.item.icon_size); _apps_watch_links(ctxt); } static void _apps_del(void *data, Evas *e __UNUSED__, Evas_Object *o __UNUSED__, void *event_info __UNUSED__) { struct apps_context *ctxt = data; eina_stringshare_del(ctxt->favorites_id); eina_stringshare_del(ctxt->gadgets_id); eina_stringshare_del(ctxt->category); eina_stringshare_del(ctxt->pending_category); if (ctxt->handler.favorites_changed) ecore_event_handler_del(ctxt->handler.favorites_changed); app_item_common_ecore_stop(&ctxt->item); gstuff_glib_run_simple(_apps_glib_stop, ctxt); } static void _apps_enter_end(void *data, Evas_Object *o __UNUSED__, const char *emission __UNUSED__, const char *source __UNUSED__) { struct apps_context *ctxt = data; DBG("apps finish enter animation, ready!\n"); if (ctxt->pending_end_callback) ctxt->pending_end_callback(ctxt->pending_end_callback_data, ctxt->apps); ctxt->pending_end_callback = NULL; ctxt->pending_end_callback_data = NULL; eina_stringshare_del(ctxt->pending_category); ctxt->pending_category = NULL; } static void _apps_exit_end(void *data, Evas_Object *o __UNUSED__, const char *emission __UNUSED__, const char *source __UNUSED__) { struct apps_context *ctxt = data; DBG("apps finish exit animation, load '%s'!\n", ctxt->pending_category); eina_stringshare_del(ctxt->category); ctxt->category = eina_stringshare_ref(ctxt->pending_category); _apps_load(ctxt); DBG("apps enter animation...\n"); edje_object_signal_emit(ctxt->edje, _EDJE_SIG_ENTER, _EDJE_SIG_SRC); } static Eina_Bool _apps_favorites_changed(void *data, int type __UNUSED__, void *event __UNUSED__) { struct apps_context *ctxt = data; if (ctxt->category == ctxt->favorites_id) gstuff_glib_run_simple(_apps_glib_load_favorites, ctxt); return ECORE_CALLBACK_RENEW; } static void _apps_focus_key_down(void *data, Evas *e __UNUSED__, Evas_Object *o __UNUSED__, void *event_info __UNUSED__) { struct apps_context *ctxt = data; edje_object_signal_emit(ctxt->edje, _EDJE_SIG_FOCUS_IN, _EDJE_SIG_SRC); } Evas_Object * apps_add(Evas_Object *parent) { struct apps_context *ctxt; Evas_Object *apps, *scroller, *grid, *ed, *box; const char *s; Eina_Bool w = 1, h = 1; if (FAVORITES_EVENT_CHANGED == 0) FAVORITES_EVENT_CHANGED = ecore_event_type_new(); apps = elm_layout_add(parent); if (!apps) { ERR("could not create apps layout widget.\n"); return NULL; } ed = elm_layout_edje_get(apps); if (!elm_layout_file_set(apps, _nl_prefs.theme, _EDJE_GROUP_LAYOUT)) { int err = edje_object_load_error_get(ed); const char *errmsg = edje_load_error_str(err); ERR("cannot load theme '%s', group '%s': %s\n", _nl_prefs.theme, _EDJE_GROUP_LAYOUT, errmsg); goto error; } edje_object_signal_callback_add (ed, _EDJE_SIG_QUIT_EXECUTED, _EDJE_SIG_SRC, _apps_quit_executed, apps); scroller = elm_scroller_add(apps); if (!scroller) { ERR("could not createapps scroller.\n"); goto error; } elm_object_style_set(scroller, "apps"); elm_layout_content_set(apps, _EDJE_PART_SCROLLER, scroller); grid = elm_layout_add(apps); if (!grid) { ERR("could not create apps layout widget.\n"); goto error; } ed = elm_layout_edje_get(grid); if (!elm_layout_file_set(grid, _nl_prefs.theme, _EDJE_GROUP_GRID)) { int err = edje_object_load_error_get(ed); const char *errmsg = edje_load_error_str(err); ERR("cannot load theme '%s', group '%s': %s\n", _nl_prefs.theme, _EDJE_GROUP_GRID, errmsg); goto error; } box = (Evas_Object *)edje_object_part_object_get(ed, _EDJE_PART_CONTENTS); evas_object_event_callback_add (box, EVAS_CALLBACK_CHANGED_SIZE_HINTS, _apps_box_size_hints_changed, grid); elm_scroller_content_set(scroller, grid); ctxt = calloc(1, sizeof(*ctxt)); if (!ctxt) { ERR("could not allocate apps context.\n"); goto error; } ctxt->apps = apps; ctxt->edje = elm_layout_edje_get(apps); ctxt->scroller = scroller; ctxt->grid = grid; ctxt->favorites_id = eina_stringshare_add(_favorites_id); ctxt->gadgets_id = eina_stringshare_add(_(_gadgets_id)); _apps_context_set(apps, ctxt); app_item_common_ecore_start(&ctxt->item, apps, scroller, grid); edje_object_signal_callback_add (ctxt->edje, _EDJE_SIG_ENTER_END, _EDJE_SIG_SRC, _apps_enter_end, ctxt); edje_object_signal_callback_add (ctxt->edje, _EDJE_SIG_EXIT_END, _EDJE_SIG_SRC, _apps_exit_end, ctxt); evas_object_event_callback_add(apps, EVAS_CALLBACK_FREE, _apps_del, ctxt); evas_object_event_callback_add (apps, EVAS_CALLBACK_KEY_DOWN, _apps_focus_key_down, ctxt); s = edje_object_data_get(ed, "scroll_horizontal"); if (s && (strcmp(s, "off") == 0)) w = 0; s = edje_object_data_get(ed, "scroll_vertical"); if (s && (strcmp(s, "off") == 0)) h = 0; elm_scroller_bounce_set(scroller, w, h); ctxt->handler.favorites_changed = ecore_event_handler_add (FAVORITES_EVENT_CHANGED, _apps_favorites_changed, ctxt); gstuff_glib_run_simple(_apps_glib_start, ctxt); return apps; error: evas_object_del(apps); return NULL; }