/* -*- 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>
#include <launcher/launcher.h>

/* uncomment will display a 'quit' entry in sidebar */
#define SIDEBAR_QUIT

#define SIDEBAR_PREFS_DESKTOP "/usr/share/desktop-directories/System.directory"


static const char _EDJE_GROUP_LIST[] = "e/netbook/launcher/main/sidebar";
static const char _EDJE_GROUP_LIST_ITEM[] = "e/netbook/launcher/main/sidebar/item";
static const char _EDJE_PART_CONTENTS[] = "e.box.contents";
static const char _EDJE_PART_ICON[] = "e.swallow.icon";
static const char _EDJE_PART_LABEL[] = "e.text.label";
static const char _EDJE_PART_COMMENT[] = "e.text.comment";

static const char _EDJE_SIG_SRC[] = "e";
static const char _EDJE_SIG_FOCUS_OUT[] = "gui,action,focus,out";
static const char _EDJE_SIG_FOCUS_IN[] = "gui,action,focus,in";
static const char _EDJE_SIG_EXECUTED[] = "gui,action,executed";
static const char _EDJE_SIG_SELECT[] = "gui,action,select";
static const char _EDJE_SIG_UNSELECT[] = "gui,action,unselect";

static const char _ICON_FAVS[] = "hedley-home";
static const char _ICON_PLACES[] = "applications-places";
static const char _ICON_GADS[] = "unr-applications-widgets";
static const char _ICON_INFOS[] = "hedley-system-information";
static const char _ICON_PREFS[] = "hedley-settings";
#ifdef SIDEBAR_QUIT
static const char _ICON_QUIT[] = "hedley-power";
#endif

static const char _sidebar_key[] = "_nl_sidebar";

struct sidebar_context
{
  Evas_Object *scroller;
  Evas_Object *sidebar;
  Evas_Object *last;
  Eina_Bool first;
  LauncherMenu *menu;
  struct item_size item;
  struct kbdnav_pool *nav_pool;
  const char *favs_icon;
  const char *places_icon;
  const char *gads_icon;
  const char *infos_icon;
#ifdef SIDEBAR_QUIT
  const char *quit_icon;
#endif
#ifdef PREFS_COLLAPSE
  const char *prefs_icon;
  gchar *prefs_label;
  Eina_Bool prefs_show;
#endif
  struct
  {
    gulong changed;
  } notify;
};

static inline void
_sidebar_context_set(Evas_Object *obj, struct sidebar_context *ctxt)
{
  evas_object_data_set(obj, _sidebar_key, ctxt);
}

static inline struct sidebar_context *
_sidebar_context_get(Evas_Object *obj)
{
  return evas_object_data_get(obj, _sidebar_key);
}

static void
_sidebar_item_in(void *data, Evas_Object *obj, const char *signal __UNUSED__, const char *source __UNUSED__)
{
  const char *str = edje_object_data_get(obj, "selectraise");
  if (str && (strcmp(str, "on") == 0))
    evas_object_raise(data);
}

static void
_sidebar_item_out(void *data __UNUSED__, Evas_Object *obj __UNUSED__, const char *signal __UNUSED__, const char *source __UNUSED__)
{
}

struct sidebar_item_data
{
  struct sidebar_context *ctxt;
  const char *name; /* stringshared */
  Evas_Object *edje;
  void (*cb)(struct sidebar_item_data *id, Evas_Object *o);
};

static void
_sidebar_item_select(void *data, Evas_Object *obj, const char *signal __UNUSED__, const char *source __UNUSED__)
{
  struct sidebar_item_data *id = data;

  INF("item selected %s\n", id->name);

  if (id->ctxt->last && id->ctxt->last == obj)
    return;
  
  if (id->ctxt->last) {
    /* Only play sound if we're moving from something to another thing.  This
       avoids playing sound at startup */
    sound_play(id->edje, "button-pressed");
    sound_play(id->edje, "button-released");
    edje_object_signal_emit(id->ctxt->last, _EDJE_SIG_UNSELECT, _EDJE_SIG_SRC);
    edje_object_signal_emit(id->ctxt->last, _EDJE_SIG_FOCUS_OUT, _EDJE_SIG_SRC);
  }

  edje_object_signal_emit(obj, _EDJE_SIG_SELECT, _EDJE_SIG_SRC);

  kbdnav_pool_current_id_set(id->ctxt->nav_pool, id->name);

  id->cb(id, obj);

  id->ctxt->last = obj;
}

static void
_sidebar_item_del(void *data, Evas *e __UNUSED__, Evas_Object *obj __UNUSED__, void *event_info __UNUSED__)
{
  struct sidebar_item_data *id = data;

  kbdnav_pool_item_del(id->ctxt->nav_pool, id);
  eina_stringshare_del(id->name);
  free(id);
}

static void
_sidebar_calc_sizes(struct sidebar_context *ctxt)
{
  ctxt->item.icon_size = 32;
  ctxt->item.w = 100;
  ctxt->item.h = 32;
  _nl_edje_item_size_calc(&ctxt->item, ctxt->sidebar, _EDJE_GROUP_LIST_ITEM);
}

static void
_sidebar_append_item(struct sidebar_context *ctxt, const char *name, const char *comment, const char *icon_name, void (*cb)(struct sidebar_item_data *id, Evas_Object *o))
{
  Evas_Object *ed, *item, *icon, *item_ed;
  struct sidebar_item_data *id;

  ed = elm_layout_edje_get(ctxt->sidebar);
  if (!ed)
  {
    ERR("could not get layout edje object\n");
    return;
  }

  id = malloc(sizeof(*id));
  if (!id)
  {
    ERR("could not allocate sidebar_item_data\n");
    return;
  }

  icon = _nl_icon_load_sized(ctxt->sidebar, icon_name, ctxt->item.icon_size);

  item = elm_layout_add(ctxt->sidebar);
  if (!item)
    goto error;
  if (!elm_layout_file_set(item, _nl_prefs.theme, _EDJE_GROUP_LIST_ITEM))
    goto error;

  if (icon)
    elm_layout_content_set(item, _EDJE_PART_ICON, icon);

  item_ed = elm_layout_edje_get(item);
  edje_object_part_text_set(item_ed, _EDJE_PART_LABEL, name);
  edje_object_part_text_set(item_ed, _EDJE_PART_COMMENT, comment);

  id->ctxt = ctxt;
  id->name = eina_stringshare_add(name);
  id->edje = item_ed;
  id->cb = cb;

  evas_object_event_callback_add
    (item, EVAS_CALLBACK_FREE, _sidebar_item_del, id);

  evas_object_resize(item, ctxt->item.w, ctxt->item.h);
  evas_object_show(item);

  edje_object_signal_callback_add
    (item_ed, _EDJE_SIG_FOCUS_IN, _EDJE_SIG_SRC, _sidebar_item_in, item);
  edje_object_signal_callback_add
    (item_ed, _EDJE_SIG_FOCUS_OUT, _EDJE_SIG_SRC, _sidebar_item_out, item);
  edje_object_signal_callback_add
    (item_ed, _EDJE_SIG_EXECUTED, _EDJE_SIG_SRC, _sidebar_item_select, id);

  edje_object_part_box_append(ed, _EDJE_PART_CONTENTS, item);
  kbdnav_pool_item_add(ctxt->nav_pool, id);
  return;

error:
  if (item)
    evas_object_del(item);
  if (icon)
    evas_object_del(icon);
  free(id);
}

static void
_sidebar_favs_select(struct sidebar_item_data *id, Evas_Object *o __UNUSED__)
{
  evas_object_smart_callback_call
    (id->ctxt->scroller, CALLBACK_SIDEBAR_FAVORITES_SELECTED, NULL);
}

static void
_sidebar_append_item_favs(struct sidebar_context *ctxt)
{
  _sidebar_append_item
    (ctxt, _("Favorites"), NULL, ctxt->favs_icon, _sidebar_favs_select);
}

static void
_sidebar_places_select(struct sidebar_item_data *id, Evas_Object *o __UNUSED__)
{
  evas_object_smart_callback_call
    (id->ctxt->scroller, CALLBACK_SIDEBAR_PLACES_SELECTED, NULL);
}

static void
_sidebar_append_item_places(struct sidebar_context *ctxt)
{
  _sidebar_append_item
    (ctxt, _("Files & Folders"), NULL, ctxt->places_icon,
     _sidebar_places_select);
}

static void
_sidebar_menu_select(struct sidebar_item_data *id, Evas_Object *o __UNUSED__)
{
  evas_object_smart_callback_call
    (id->ctxt->scroller, CALLBACK_SIDEBAR_MENU_SELECTED, (void*)id->name);
}

static void
_sidebar_append_item_menu(struct sidebar_context *ctxt, const char *name, const char *comment, const char *icon_name)
{
  _sidebar_append_item
    (ctxt, name, comment, icon_name, _sidebar_menu_select);
}

#ifdef HAVE_GOOGLE_GADGETS
static void
_sidebar_append_item_gadgets(struct sidebar_context *ctxt)
{
  _sidebar_append_item
    (ctxt, _("Widgets"), NULL, ctxt->gads_icon, _sidebar_menu_select);
}
#endif

static void
_sidebar_infos_select(struct sidebar_item_data *id, Evas_Object *o)
{
}

static void
_sidebar_append_item_infos(struct sidebar_context *ctxt)
{
  _sidebar_append_item
    (ctxt, _("Informations"), NULL, ctxt->infos_icon, _sidebar_infos_select);
}

#ifdef SIDEBAR_QUIT
static void
_sidebar_quit_select(struct sidebar_item_data *id, Evas_Object *o)
{
  evas_object_smart_callback_call
    (id->ctxt->scroller, CALLBACK_SIDEBAR_QUIT_SELECTED, NULL);
}

static void
_sidebar_append_item_quit(struct sidebar_context *ctxt)
{
  _sidebar_append_item
    (ctxt, _("Quit"), NULL, ctxt->quit_icon, _sidebar_quit_select);
}
#endif

#ifdef PREFS_COLLAPSE
static void
_sidebar_prefs_select(struct sidebar_item_data *id, Evas_Object *o)
{
  evas_object_smart_callback_call
    (id->ctxt->scroller, CALLBACK_SIDEBAR_PREFERENCES_SELECTED, NULL);
}

static void
_sidebar_append_item_prefs(struct sidebar_context *ctxt)
{
  _sidebar_append_item
    (ctxt, ctxt->prefs_label, NULL, ctxt->prefs_icon, _sidebar_prefs_select);
}
#endif

extern const char *elm_widget_type_get(Evas_Object *o);
static void
_sidebar_ecore_menu_changed(void *data, unsigned int len, const char **strings)
{
  struct sidebar_context *ctxt = data;
  Evas_Object *ed;
  unsigned int i;

  ed = elm_layout_edje_get(ctxt->sidebar);
  if (!ed)
  {
    ERR("could not get layout edje object\n");
    return;
  }
  edje_object_part_box_remove_all(ed, _EDJE_PART_CONTENTS, 1);

  /* Favorites must be first (or adjust logic in sidebar_favorites_activate) */
  _sidebar_append_item_favs(ctxt);
#ifdef HAVE_GOOGLE_GADGETS
  _sidebar_append_item_gadgets(ctxt);
#endif

#ifdef PREFS_COLLAPSE
  if (ctxt->prefs_show)
    _sidebar_append_item_prefs(ctxt);
#endif

  _sidebar_append_item_infos(ctxt);

#ifdef SIDEBAR_QUIT
  _sidebar_append_item_quit(ctxt);
#endif

  if (ctxt->first)
  {
    ctxt->first = 0;
    sidebar_favorites_activate(ctxt->scroller);
  }
}

static void
_sidebar_glib_menu_changed(LauncherMenu *menu, struct sidebar_context *ctxt)
{
  GSList *lst, *n;
  unsigned int len, i;
  const char **strings;

  lst = launcher_menu_get_categories(menu);
  len = g_slist_length(lst);

  strings = alloca((len * 3 + 1) * sizeof(char *));
  if (!strings)
  {
    g_critical("could not allocate strings array on stack.");
    return;
  }

#ifdef PREFS_COLLAPSE
  ctxt->prefs_show = 0;
#endif
  for (i = 0, n = lst; n != NULL; n = n->next)
  {
    LauncherCategory *cat = n->data;
    const char *icon_name;
    unsigned int j;

    icon_name = launcher_category_get_icon_name(cat);
#ifdef PREFS_COLLAPSE
    if (icon_name && strstr(icon_name, PREFS_MATCH_STR))
    {
      g_debug("category '%s' (%s) matches '%s', "
              "collapse into singe preferences entry.",
              launcher_category_get_name(cat), icon_name, PREFS_MATCH_STR);
      ctxt->prefs_show = 1;
      continue;
    }
#endif

    strings[i + 0] = launcher_category_get_name(cat);
    strings[i + 1] = launcher_category_get_comment(cat);

    if (icon_name)
      strings[i + 2] = icon_unr_path_get(icon_name, ctxt->item.icon_size);
    else
      strings[i + 2] = "";

    for (j = 0; j < 2; j++)
      if (!strings[i + j])
        strings[i + j] = "";

    i += 3;
  }
  strings[i] = NULL;

  gstuff_ecore_run_ptr_string_array
    (_sidebar_ecore_menu_changed, ctxt, i, strings);
}

static void
_sidebar_size_hints_changed(void *data, Evas *e __UNUSED__, Evas_Object *o, void *event_info __UNUSED__)
{
  Evas_Object *sidebar = data;
  Evas_Coord w, h;
  evas_object_size_hint_min_get(o, &w, &h);
  evas_object_size_hint_min_set(sidebar, w, h);
  evas_object_resize(sidebar, w, h);
}

#ifdef PREFS_COLLAPSE
static void
_sidebar_prefs_start(struct sidebar_context *ctxt)
{
  GKeyFile *file;
  GError *err = NULL;
  gchar *icon;

  file = g_key_file_new();
  if (!file)
  {
    g_critical("could not create GKeyFile\n");
    goto end;
  }

  if (!g_key_file_load_from_file
      (file, SIDEBAR_PREFS_DESKTOP, G_KEY_FILE_NONE, &err))
  {
    g_critical("could not open '%s': %s",
               SIDEBAR_PREFS_DESKTOP, err ? err->message : "");
    if (err)
      g_error_free(err);
    g_key_file_free(file);
    goto end;
  }

  ctxt->prefs_label = g_key_file_get_locale_string
    (file, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_NAME, NULL, &err);
  if (err)
  {
    g_warning("could not get locale string from "
              "key '%s' from group '%s' of file '%s': %s",
              G_KEY_FILE_DESKTOP_KEY_NAME, G_KEY_FILE_DESKTOP_GROUP,
              SIDEBAR_PREFS_DESKTOP, err->message);
    g_error_free(err);
  }

  icon = g_key_file_get_string
    (file, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_ICON, &err);
  if (err)
  {
    g_warning("could not get string from "
              "key '%s' from group '%s' of file '%s': %s",
              G_KEY_FILE_DESKTOP_KEY_NAME, G_KEY_FILE_DESKTOP_GROUP,
              SIDEBAR_PREFS_DESKTOP, err->message);
    g_error_free(err);
  }

  g_key_file_free(file);

end:
  if (icon)
  {
    ctxt->prefs_icon = icon_unr_path_get(icon, ctxt->item.icon_size);
    g_free(icon);
  }

  if (!ctxt->prefs_icon)
    ctxt->prefs_icon = icon_unr_path_get
      ("preferences-desktop", ctxt->item.icon_size);

  if (!ctxt->prefs_label)
    ctxt->prefs_label = g_strdup(_("Settings"));
}

static void
_sidebar_prefs_stop(struct sidebar_context *ctxt)
{
  g_free(ctxt->prefs_label);
}
#endif

static void
_sidebar_glib_stop(void *data)
{
  struct sidebar_context *ctxt = data;

#ifdef PREFS_COLLAPSE
  _sidebar_prefs_stop(ctxt);
#endif

  if (ctxt->notify.changed)
    g_signal_handler_disconnect(ctxt->menu, ctxt->notify.changed);

  if (ctxt->menu)
    g_object_unref(ctxt->menu);

  free(ctxt);
}

static void
_sidebar_glib_start(void *data)
{
  struct sidebar_context *ctxt = data;
  unsigned int icon_size;

  ctxt->menu = launcher_menu_get_default();
  if (!ctxt->menu)
  {
    g_critical("could not get launcher menu");
    return;
  }

  ctxt->notify.changed = g_signal_connect
    (ctxt->menu, "menu-changed", G_CALLBACK(_sidebar_glib_menu_changed), ctxt);

  icon_size = ctxt->item.icon_size;
  ctxt->favs_icon = icon_unr_path_get(_ICON_FAVS, icon_size);
  ctxt->places_icon = icon_unr_path_get(_ICON_PLACES, icon_size);
  ctxt->gads_icon = icon_unr_path_get(_ICON_GADS, icon_size);
  ctxt->infos_icon = icon_unr_path_get(_ICON_INFOS, icon_size);

  /* XXX REMOVE THESE HACKS ONCE THEY SET A FIXED NAME */
  if (!ctxt->favs_icon || (strcmp(ctxt->favs_icon, _ICON_FAVS) == 0))
    ctxt->favs_icon = icon_unr_path_get("emblem-favorite", icon_size);
  if (!ctxt->places_icon || (strcmp(ctxt->places_icon, _ICON_PLACES) == 0))
    ctxt->places_icon = icon_unr_path_get("user-home", icon_size);
  if (!ctxt->gads_icon || (strcmp(ctxt->gads_icon, _ICON_GADS) == 0))
    ctxt->gads_icon = icon_unr_path_get("weather-few-clouds", icon_size);

#ifdef SIDEBAR_QUIT
  ctxt->quit_icon = icon_unr_path_get(_ICON_QUIT, icon_size);
#endif

#ifdef PREFS_COLLAPSE
  _sidebar_prefs_start(ctxt);
  /* Override all smart icon retrieval from _sidebar_prefs_start */
  ctxt->prefs_icon = icon_unr_path_get(_ICON_PREFS, icon_size);
#endif

  _sidebar_glib_menu_changed(ctxt->menu, ctxt);
}

static void
_sidebar_del(void *data, Evas *e __UNUSED__, Evas_Object *o __UNUSED__, void *event_info __UNUSED__)
{
  struct sidebar_context *ctxt = data;
  kbdnav_pool_free(ctxt->nav_pool);
  gstuff_glib_run_simple(_sidebar_glib_stop, ctxt);
}

static Evas_Object *
_sidebar_item_data_object_get(void *user_data __UNUSED__, void *item_data)
{
  const struct sidebar_item_data *id = item_data;
  return id->edje;
}

static const char *
_sidebar_item_data_id_get(void *user_data __UNUSED__, void *item_data)
{
  const struct sidebar_item_data *id = item_data;
  return id->name;
}

static void
_sidebar_item_data_select(void *user_data __UNUSED__, void *item_data)
{
  const struct sidebar_item_data *id = item_data;
  Evas_Coord ix, iy, iw, ih, sx, sy;
  edje_object_signal_emit(id->edje, _EDJE_SIG_FOCUS_IN, _EDJE_SIG_SRC);
  evas_object_geometry_get(id->edje, &ix, &iy, &iw, &ih);
  evas_object_geometry_get(id->ctxt->sidebar, &sx, &sy, NULL, NULL);
  ix -= sx;
  iy -= sy;
  elm_scroller_region_show(id->ctxt->scroller, ix, iy, iw, ih);
  sound_play(id->edje, "item-selected");
}

static void
_sidebar_item_data_unselect(void *user_data __UNUSED__, void *item_data)
{
  const struct sidebar_item_data *id = item_data;
  edje_object_signal_emit(id->edje, _EDJE_SIG_FOCUS_OUT, _EDJE_SIG_SRC);
}

static void
_sidebar_focus_in(void *data, Evas *e __UNUSED__, Evas_Object *o __UNUSED__, void *event_info __UNUSED__)
{
  struct sidebar_context *ctxt = data;
  kbdnav_pool_current_select(ctxt->nav_pool);
}

static void
_sidebar_focus_out(void *data, Evas *e __UNUSED__, Evas_Object *o __UNUSED__, void *event_info __UNUSED__)
{
  struct sidebar_context *ctxt = data;
  kbdnav_pool_current_unselect(ctxt->nav_pool);
}

static void
_sidebar_selected_activate(struct sidebar_context *ctxt)
{
  struct sidebar_item_data *id = kbdnav_pool_current_item_get(ctxt->nav_pool);
  if (!id)
    return;
  edje_object_signal_emit(id->edje, _EDJE_SIG_EXECUTED, _EDJE_SIG_SRC);
}

static void
_sidebar_key_down(void *data, Evas *e __UNUSED__, Evas_Object *o __UNUSED__, void *event_info)
{
  Evas_Event_Key_Down *ev = event_info;
  struct sidebar_context *ctxt = data;

  if (strcmp(ev->keyname, "Up") == 0)
    kbdnav_pool_navigate_up(ctxt->nav_pool);
  else if (strcmp(ev->keyname, "Down") == 0)
    kbdnav_pool_navigate_down(ctxt->nav_pool);
  else if (strcmp(ev->keyname, "Left") == 0)
    kbdnav_pool_navigate_left(ctxt->nav_pool);
  else if (strcmp(ev->keyname, "Right") == 0)
    kbdnav_pool_navigate_right(ctxt->nav_pool);
  else if (strcmp(ev->keyname, "Return") == 0)
    _sidebar_selected_activate(ctxt);
}

Evas_Object *
sidebar_add(Evas_Object *parent)
{
  Evas_Object *sidebar, *scroller, *ed, *box;
  struct sidebar_context *ctxt;
  const char *s;
  Eina_Bool w = 1, h = 1;

  sidebar = elm_layout_add(parent);
  if (!sidebar)
  {
    ERR("could not create sidebar list widget.\n");
    return NULL;
  }

  ed = elm_layout_edje_get(sidebar);
  if (!elm_layout_file_set(sidebar, _nl_prefs.theme, _EDJE_GROUP_LIST))
  {
    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_LIST, errmsg);
    evas_object_del(sidebar);
    return NULL;
  }
  evas_object_show(sidebar);

  box = (Evas_Object *)edje_object_part_object_get(ed, _EDJE_PART_CONTENTS);
  evas_object_event_callback_add
    (box, EVAS_CALLBACK_CHANGED_SIZE_HINTS, _sidebar_size_hints_changed,
     sidebar);

  scroller = elm_scroller_add(parent);
  elm_object_style_set(scroller, "sidebar");
  elm_scroller_content_set(scroller, sidebar);
  evas_object_size_hint_weight_set(sidebar, 1.0, 1.0);
  evas_object_size_hint_align_set(sidebar, -1.0, -1.0);

  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 = calloc(1, sizeof(*ctxt));
  if (!ctxt)
  {
    ERR("could not allocate sidebar context.\n");
    evas_object_del(scroller);
    return NULL;
  }
  ctxt->sidebar = sidebar;
  ctxt->scroller = scroller;
  ctxt->first = 1;

  ctxt->nav_pool = kbdnav_pool_new
    (_sidebar_item_data_object_get, _sidebar_item_data_id_get,
     _sidebar_item_data_select, _sidebar_item_data_unselect, NULL);
  if (!ctxt->nav_pool)
  {
    ERR("could not allocate sidebar navigation pool.\n");
    evas_object_del(scroller);
    free(ctxt);
    return NULL;
  }

  evas_object_event_callback_add
    (scroller, EVAS_CALLBACK_FREE, _sidebar_del, ctxt);
  evas_object_event_callback_add
    (scroller, EVAS_CALLBACK_FOCUS_IN, _sidebar_focus_in, ctxt);
  evas_object_event_callback_add
    (scroller, EVAS_CALLBACK_FOCUS_OUT, _sidebar_focus_out, ctxt);
  evas_object_event_callback_add
    (scroller, EVAS_CALLBACK_KEY_DOWN, _sidebar_key_down, ctxt);

  _sidebar_context_set(scroller, ctxt);
  _sidebar_context_set(sidebar, ctxt);

  _sidebar_calc_sizes(ctxt);

  gstuff_glib_run_simple(_sidebar_glib_start, ctxt);

  return scroller;
}

void
sidebar_favorites_activate(Evas_Object *sidebar)
{
  struct sidebar_context *ctxt = _sidebar_context_get(sidebar);
  while (kbdnav_pool_navigate_up(ctxt->nav_pool));
  _sidebar_selected_activate(ctxt);
}

