#include "luncher.h"
static Eina_List *handlers;
static Eina_Bool _bar_icon_preview_show(void *data);
static Eina_Bool _bar_icon_preview_hide(void *data);
static void      _bar_icon_del(Instance *inst, Icon *ic);
static void _bar_exec_new_show(void *data, Evas *e, Evas_Object *obj, void *event_data);
static void _bar_icon_preview_menu_hide(void *data, Evas *e EINA_UNUSED, Evas_Object *obj, void *event_data EINA_UNUSED);

static void
_bar_aspect(Instance *inst)
{
   Evas_Coord w, h;

   switch (e_gadget_site_orient_get(e_gadget_site_get(inst->o_main)))
     {
      case E_GADGET_SITE_ORIENT_VERTICAL:
        h = eina_list_count(inst->icons);
        w = 1;
        break;
      default:
        w = eina_list_count(inst->icons);
        h = 1;
     }
   evas_object_size_hint_aspect_set(inst->o_main, EVAS_ASPECT_CONTROL_BOTH, w, h);
}

static Eina_Bool
_bar_check_for_duplicates(Icon *ic, E_Client *dupe)
{
   Eina_List *l;
   E_Exec_Instance *ex;

   EINA_LIST_FOREACH(ic->execs, l, ex)
     if (eina_list_data_find(ex->clients, dupe)) return EINA_TRUE;
   return !!eina_list_data_find(ic->clients, dupe);
}

static Eina_Bool
_bar_check_modifiers(Evas_Modifier *modifiers)
{
   if ((evas_key_modifier_is_set(modifiers, "Alt")) ||
       (evas_key_modifier_is_set(modifiers, "Control")) ||
       (evas_key_modifier_is_set(modifiers, "Shift")))
     return EINA_TRUE;
   return EINA_FALSE;
}

static Evas_Object *
_bar_gadget_configure(Evas_Object *g)
{
   if (!luncher_config) return NULL;
   if (luncher_config->config_dialog) return NULL;
   Instance *inst = evas_object_data_get(g, "instance");
   return config_luncher(e_zone_current_get(), inst, EINA_TRUE);
}

static void
_bar_popup_dismissed(void *data EINA_UNUSED, Evas_Object *obj, void *event_info EINA_UNUSED)
{
   E_FREE_FUNC(obj, evas_object_del);
}

static const char *
_bar_location_get(Instance *inst)
{
   const char *s = "float";

   E_Gadget_Site_Orient orient = e_gadget_site_orient_get(e_gadget_site_get(inst->o_main));
   E_Gadget_Site_Anchor anchor = e_gadget_site_anchor_get(e_gadget_site_get(inst->o_main));

   if (anchor & E_GADGET_SITE_ANCHOR_LEFT)
     {
        if (anchor & E_GADGET_SITE_ANCHOR_TOP)
          {
             switch (orient)
               {
                case E_GADGET_SITE_ORIENT_HORIZONTAL:
                  s = "top";
                  break;
                case E_GADGET_SITE_ORIENT_VERTICAL:
                  s = "left";
                  break;
                case E_GADGET_SITE_ORIENT_NONE:
                  s = "left";
                  break;
               }
          }
        else if (anchor & E_GADGET_SITE_ANCHOR_BOTTOM)
          {
             switch (orient)
               {
                case E_GADGET_SITE_ORIENT_HORIZONTAL:
                  s = "bottom";
                  break;
                case E_GADGET_SITE_ORIENT_VERTICAL:
                  s = "left";
                  break;
                case E_GADGET_SITE_ORIENT_NONE:
                  s = "left";
                  break;
               }
          }
        else
          s = "left";
     }
   else if (anchor & E_GADGET_SITE_ANCHOR_RIGHT)
     {
        if (anchor & E_GADGET_SITE_ANCHOR_TOP)
          {
             switch (orient)
               {
                case E_GADGET_SITE_ORIENT_HORIZONTAL:
                  s = "top";
                  break;
                case E_GADGET_SITE_ORIENT_VERTICAL:
                  s = "right";
                  break;
                case E_GADGET_SITE_ORIENT_NONE:
                  s = "right";
                  break;
               }
          }
        else if (anchor & E_GADGET_SITE_ANCHOR_BOTTOM)
          {
             switch (orient)
               {
                case E_GADGET_SITE_ORIENT_HORIZONTAL:
                  s = "bottom";
                  break;
                case E_GADGET_SITE_ORIENT_VERTICAL:
                  s = "right";
                  break;
                case E_GADGET_SITE_ORIENT_NONE:
                  s = "right";
                  break;
               }
          }
        else
          s = "right";
     }
   else if (anchor & E_GADGET_SITE_ANCHOR_TOP)
     s = "top";
   else if (anchor & E_GADGET_SITE_ANCHOR_BOTTOM)
     s = "bottom";
   else
     {
        switch (orient)
          {
           case E_GADGET_SITE_ORIENT_HORIZONTAL:
             s = "bottom";
             break;
           case E_GADGET_SITE_ORIENT_VERTICAL:
             s = "left";
             break;
           default:
             s = "bottom";
             break;
          }
     }
   return s;
}

static Icon *
_bar_icon_match(Instance *inst, E_Client *ec)
{
   Icon *ic = NULL, *ic2 = NULL;
   Eina_Bool has_desktop = EINA_FALSE;
   char ori[32];

   if (ec->exe_inst)
     {
        if (ec->exe_inst->desktop)
          has_desktop = EINA_TRUE;
     }
   if (has_desktop)
     {
        ic = eina_hash_find(inst->icons_desktop_hash, ec->exe_inst->desktop->orig_path);
        if ((ic) && (ic2 = eina_hash_find(inst->icons_clients_hash, &ec)))
          {
             ic2->execs = eina_list_remove(ic2->execs, ec->exe_inst);
             ic2->clients = eina_list_remove(ic2->clients, ec);
             if (ic2->client_cbs)
               {
                  ic2->client_cbs = eina_list_remove(ic2->client_cbs, ec);
                  evas_object_event_callback_del_full(ec->frame, EVAS_CALLBACK_SHOW, _bar_exec_new_show, ic2);
               }
             if (!eina_list_count(ic2->execs) && !eina_list_count(ic2->clients))
               {
                  eina_hash_del(inst->icons_clients_hash, ec, ic2);
                  snprintf(ori, sizeof(ori), "e,state,off,%s", _bar_location_get(inst));
                  elm_layout_signal_emit(ic2->o_layout, ori, "e");
                  if (!ic2->in_order)
                    _bar_icon_del(inst, ic2);
               }
          }
     }
   if (has_desktop && !ic)
     ic = eina_hash_find(inst->icons_clients_hash, &ec);
   if (!ic)
     ic = eina_hash_find(inst->icons_clients_hash, &ec);

   return ic;
}

static void
_bar_instance_watch(void *data, E_Exec_Instance *ex, E_Exec_Watch_Type type)
{
   Icon *ic = data;
   char ori[32];

   switch (type)
     {
      case E_EXEC_WATCH_STARTED:
        if (ic->starting) elm_layout_signal_emit(ic->o_layout, "e,state,started", "e");
        ic->starting = EINA_FALSE;
        if (!ic->execs && ic->inst->cfg->type != E_LUNCHER_MODULE_LAUNCH_ONLY)
          {
             snprintf(ori, sizeof(ori), "e,state,on,%s", _bar_location_get(ic->inst));
             elm_layout_signal_emit(ic->o_layout, ori, "e");
          }
        if (!eina_list_data_find(ic->execs, ex) && ic->inst->cfg->type != E_LUNCHER_MODULE_LAUNCH_ONLY)
          ic->execs = eina_list_append(ic->execs, ex);
        break;
      case E_EXEC_WATCH_TIMEOUT:
      case E_EXEC_WATCH_STOPPED:
        ic->exec = NULL;
        break;
      default:
        break;
     }
}

static void
_bar_icon_del(Instance *inst, Icon *ic)
{
   E_Client *ec;

   inst->icons = eina_list_remove(inst->icons, ic);
   if (ic->client_menu)
     evas_object_event_callback_del_full(ic->client_menu, EVAS_CALLBACK_HIDE, _bar_icon_preview_menu_hide, ic);
   if (ic->preview)
     _bar_icon_preview_hide(ic);
   if (!inst->main_del)
     _bar_aspect(inst);
   evas_object_del(ic->o_icon);
   evas_object_del(ic->o_layout);
   eina_hash_del_by_data(inst->icons_desktop_hash, ic);
   eina_hash_del_by_data(inst->icons_clients_hash, ic);
   if (ic->desktop)
     efreet_desktop_unref(ic->desktop);
   eina_list_free(ic->execs);
   eina_list_free(ic->clients);
   EINA_LIST_FREE(ic->client_cbs, ec)
     evas_object_event_callback_del_full(ec->frame, EVAS_CALLBACK_SHOW, _bar_exec_new_show, ic);
   eina_stringshare_del(ic->icon);
   eina_stringshare_del(ic->key);
   if (ic->exec)
     e_exec_instance_watcher_del(ic->exec, _bar_instance_watch, ic);
   ic->exec = NULL;
   E_FREE_FUNC(ic->mouse_in_timer, ecore_timer_del);
   E_FREE_FUNC(ic->mouse_out_timer, ecore_timer_del);
   E_FREE(ic);
}

#if 0
static void
_bar_icon_menu_icon_mouse_out(void *data EINA_UNUSED, Evas *e EINA_UNUSED, Evas_Object *obj, void *event_data EINA_UNUSED)
{
   elm_layout_signal_emit(obj, "e,state,unfocused", "e");
}

static void
_bar_icon_menu_icon_mouse_in(void *data EINA_UNUSED, Evas *e EINA_UNUSED, Evas_Object *obj, void *event_data)
{
   Evas_Event_Mouse_In *ev = event_data;

   if (ev->event_flags & EVAS_EVENT_FLAG_ON_HOLD) return;
   if (_bar_check_modifiers(ev->modifiers)) return;

   elm_layout_signal_emit(obj, "e,state,focused", "e");
}

static Evas_Object *
_bar_icon_menu_item_new(Icon *ic, Evas_Object *popup, Evas_Object *parent, const char *name, const char *icon)
{
   const char *path = NULL, *k = NULL;
   char buf[4096];
   int len = 0;
   Evas_Object *layout, *label, *img;

   layout = elm_layout_add(parent);
   e_theme_edje_object_set(layout, "e/gadget/luncher/preview",
       "e/gadget/luncher/preview");
   evas_object_size_hint_weight_set(layout, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
   evas_object_event_callback_add(layout, EVAS_CALLBACK_MOUSE_IN, _bar_icon_menu_icon_mouse_in, NULL);
   evas_object_event_callback_add(layout, EVAS_CALLBACK_MOUSE_OUT, _bar_icon_menu_icon_mouse_out, NULL);
   elm_layout_signal_emit(layout, "e,state,icon,menu", "e");
   elm_box_pack_end(parent, layout);
   evas_object_data_set(layout, "popup", popup);
   evas_object_data_set(layout, "content", parent);
   evas_object_show(layout);

   label = elm_label_add(layout);
   elm_object_style_set(label, "luncher_preview");
   elm_label_ellipsis_set(label, EINA_TRUE);
   elm_object_text_set(label, name);
   evas_object_size_hint_weight_set(label, EVAS_HINT_EXPAND, EVAS_HINT_FILL);
   elm_layout_content_set(layout, "e.swallow.title", label);
   evas_object_show(label);

   img = elm_icon_add(layout);
   evas_object_size_hint_aspect_set(img, EVAS_ASPECT_CONTROL_BOTH, 1, 1);
   if (!icon)
     path = NULL;
   else if (strncmp(icon, "/", 1) && !ecore_file_exists(icon))
     {
        path = efreet_icon_path_find(e_config->icon_theme, icon, ic->inst->size);
        if (!path)
          {
             if (e_util_strcmp(e_config->icon_theme, "hicolor"))
             path = efreet_icon_path_find("hicolor", icon, ic->inst->size);
          }
     }
   else if (ecore_file_exists(icon))
     {
        path = icon;
     }
   if (!path && icon)
     {
        snprintf(buf, sizeof(buf), "e/icons/%s", icon);
        if (eina_list_count(e_theme_collection_items_find("base/theme/icons", buf)))
          {
             path = e_theme_edje_file_get("base/theme/icons", buf);
             k = buf;
          }
        else
          {
             path = e_theme_edje_file_get("base/theme/icons", "e/icons/unknown");
             k =  "e/icons/unknown";
          }
     }
   else if (!path && !icon)
     {
        path = e_theme_edje_file_get("base/theme/icons", "e/icons/unknown");
        k =  "e/icons/unknown";
     }
   if (path && icon)
     {
        len = strlen(icon);
        if ((len > 4) && (!strcasecmp(icon + len - 4, ".edj")))
        k = "icon";
     }
   elm_image_file_set(img, path, k);
   elm_layout_content_set(layout, "e.swallow.icon", img);
   evas_object_show(img);

   elm_layout_sizing_eval(layout);

   return layout;
}
#endif

static void
_bar_icon_drag_done(E_Drag *drag, int dropped)
{
   Instance *inst = e_object_data_get(E_OBJECT(drag));

   efreet_desktop_unref(drag->data);
   if (!inst) return;
   evas_object_smart_callback_call(e_gadget_site_get(inst->o_main), "gadget_site_unlocked", NULL);
   if (!dropped)
     bar_recalculate(inst);
}

static Eina_Bool
_bar_icon_preview_hide(void *data)
{
   Icon *ic = data;

   if (!ic) return EINA_FALSE;

   ic->mouse_out_timer = NULL;

   if (!ic->preview || ic->preview_dismissed)
     {
        ic->active = EINA_FALSE;
        return EINA_FALSE;
     }
   E_FREE_FUNC(ic->preview_box, evas_object_del);
   E_FREE_FUNC(ic->preview_scroller, evas_object_del);
   elm_ctxpopup_dismiss(ic->preview);
   ic->preview_dismissed = EINA_TRUE;
   ic->inst->current_preview = NULL;
   ic->inst->current_preview_menu = EINA_FALSE;
   ic->active = EINA_FALSE;

   return EINA_FALSE;
}

static void
_bar_icon_mouse_out(void *data, Evas *e EINA_UNUSED, Evas_Object *obj, void *event_data EINA_UNUSED)
{
   Icon *ic = data;

   elm_layout_signal_emit(ic->o_layout, "e,state,default", "e");
   elm_layout_signal_emit(ic->o_layout, "e,state,unfocused", "e");
   ic->active = EINA_FALSE;
   if (!ic->inst->cfg->hide_tooltips)
     elm_object_tooltip_hide(obj);
   E_FREE_FUNC(ic->mouse_in_timer, ecore_timer_del);
   E_FREE_FUNC(ic->mouse_out_timer, ecore_timer_del);
   ic->mouse_out_timer = ecore_timer_loop_add(.25, _bar_icon_preview_hide, ic);
}

static void
_bar_icon_mouse_move(void *data, Evas *e EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event_data)
{
   Icon *ic = data;
   Evas_Event_Mouse_Move *ev = event_data;
   int dx, dy;

   if (ev->event_flags & EVAS_EVENT_FLAG_ON_HOLD) return;
   if (_bar_check_modifiers(ev->modifiers)) return;

   if (!ic->drag.start)
     return;

   dx = ev->cur.output.x - ic->drag.x;
   dy = ev->cur.output.y - ic->drag.y;
   if (((dx * dx) + (dy * dy)) >
       (e_config->drag_resist * e_config->drag_resist))
     {
        E_Drag *d;
        Evas_Object *o;
        Evas_Coord x, y, w, h;
        unsigned int size;
        const char *drag_types[] = { "enlightenment/desktop" };

        _bar_icon_preview_hide(ic);
        ic->drag.dnd = 1;
        ic->drag.start = 0;

        evas_object_geometry_get(ic->o_icon, &x, &y, &w, &h);
        d = e_drag_new(x, y, drag_types, 1,
                       ic->desktop, -1, NULL, _bar_icon_drag_done);
        d->button_mask = evas_pointer_button_down_mask_get(e_comp->evas);
        efreet_desktop_ref(ic->desktop);
        size = MAX(w, h);
        o = e_util_desktop_icon_add(ic->desktop, size, e_drag_evas_get(d));
        e_drag_object_set(d, o);

        e_drag_resize(d, w, h);
        e_drag_start(d, ic->drag.x, ic->drag.y);
        e_object_data_set(E_OBJECT(d), ic->inst);
        if (ic->in_order)
          e_order_remove(ic->inst->order, ic->desktop);
     }
}

static Eina_Bool
_bar_drag_timer(void *data)
{
   Icon *ic = data;

   ic->drag_timer = NULL;
   ic->drag.start = 1;
   evas_object_smart_callback_call(e_gadget_site_get(ic->inst->o_main), "gadget_site_locked", NULL);
   return EINA_FALSE;
}

static void
_bar_icon_menu_action_cb(void *data, E_Menu *m EINA_UNUSED, E_Menu_Item *mi EINA_UNUSED)
{
   Efreet_Desktop_Action *action = (Efreet_Desktop_Action*)data;

   e_exec(e_zone_current_get(), NULL, action->exec, NULL, "luncher");
}

static void
_bar_icon_menu_add_cb(void *data, E_Menu *m EINA_UNUSED, E_Menu_Item *mi EINA_UNUSED)
{
   Icon *ic = data;

   if (ic->desktop)
     e_order_append(ic->inst->order, ic->desktop);
}

static void
_bar_icon_menu_remove_cb(void *data, E_Menu *m EINA_UNUSED, E_Menu_Item *mi EINA_UNUSED)
{
   Icon *ic = data;

   if (ic->desktop)
     e_order_remove(ic->inst->order, ic->desktop);
}

static void
_bar_icon_menu_properties_cb(void *data, E_Menu *m EINA_UNUSED, E_Menu_Item *mi EINA_UNUSED)
{
   Icon *ic = data;

   if (ic->desktop)
     e_desktop_edit(ic->desktop);
}

static void
_bar_gadget_menu_populate(Evas_Object *g, E_Menu *m)
{
   Instance *inst = evas_object_data_get(g, "instance");
   Evas *e = evas_object_evas_get(g);
   Eina_List *l;
   Icon *ic = NULL;
   Evas_Coord px, py;
   E_Menu_Item *mi;
   Efreet_Desktop_Action *action;

   evas_pointer_canvas_xy_get(e, &px, &py);
   EINA_LIST_FOREACH(inst->icons, l, ic)
     {
        Evas_Coord x, y, w, h;

        evas_object_geometry_get(ic->o_icon, &x, &y, &w, &h);
        if (E_INSIDE(px, py, x, y, w, h)) break;
        ic = NULL;
     }
   if (!ic) return;

   mi = e_menu_item_new(m);
   e_menu_item_separator_set(mi, 1);

   if (ic->desktop)
     {
        if (ic->desktop->actions)
          {
             EINA_LIST_FOREACH(ic->desktop->actions, l, action)
               {
                  mi = e_menu_item_new(m);
                  e_menu_item_label_set(mi, action->name);
                  e_util_menu_item_theme_icon_set(mi, action->icon);
                  e_menu_item_callback_set(mi, _bar_icon_menu_action_cb, action);
               }
             mi = e_menu_item_new(m);
             e_menu_item_separator_set(mi, 1);
          }
     }
   if (ic->desktop)
     {
        mi = e_menu_item_new(m);
        e_menu_item_label_set(mi, _("Icon Properties"));
        e_util_menu_item_theme_icon_set(mi, "preferences-applications");
        e_menu_item_callback_set(mi, _bar_icon_menu_properties_cb, ic);
     }
   if (ic->in_order)
     {
        mi = e_menu_item_new(m);
        e_menu_item_label_set(mi, _("Remove Icon"));
        e_util_menu_item_theme_icon_set(mi, "list-remove");
        e_menu_item_callback_set(mi, _bar_icon_menu_remove_cb, ic);
     }
   else if ((!ic->in_order) && (ic->desktop))
     {
        mi = e_menu_item_new(m);
        e_menu_item_label_set(mi, _("Add Icon"));
        e_util_menu_item_theme_icon_set(mi, "list-add");
        e_menu_item_callback_set(mi, _bar_icon_menu_add_cb, ic);
     }
}

static void
_bar_icon_mouse_down(void *data, Evas *e EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event_data)
{
   Icon *ic = data;
   Evas_Event_Mouse_Up *ev = event_data;

   if (ev->event_flags & EVAS_EVENT_FLAG_ON_HOLD) return;
   if (_bar_check_modifiers(ev->modifiers)) return;

   if (ev->button == 1)
     {
        E_FREE_FUNC(ic->mouse_in_timer, ecore_timer_del);
        E_FREE_FUNC(ic->mouse_out_timer, ecore_timer_del);
        ic->drag.x = ev->output.x;
        ic->drag.y = ev->output.y;
        E_FREE_FUNC(ic->drag_timer, ecore_timer_del);
        ic->drag_timer = ecore_timer_loop_add(0.35, _bar_drag_timer, ic);
     }
}

static void
_bar_icon_mouse_up(void *data, Evas *e EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event_data)
{
   Icon *ic = data;
   Evas_Event_Mouse_Up *ev = event_data;

   if (ev->event_flags & EVAS_EVENT_FLAG_ON_HOLD)
     {
        if (ev->button == 1)
          {
             ic->drag.start = 0;
             ic->drag.dnd = 0;
             E_FREE_FUNC(ic->mouse_in_timer, ecore_timer_del);
             E_FREE_FUNC(ic->mouse_out_timer, ecore_timer_del);
             E_FREE_FUNC(ic->drag_timer, ecore_timer_del);
          }
        return;
     }
   if (_bar_check_modifiers(ev->modifiers)) return;

   if (ev->button == 1)
     {
        E_FREE_FUNC(ic->drag_timer, ecore_timer_del);
        if (ic->drag.dnd)
          {
             ic->drag.start = 0;
             ic->drag.dnd = 0;
             return;
          }
     }
   if (ev->button == 1 && ic->desktop)
     {
        if (ic->desktop->type == EFREET_DESKTOP_TYPE_APPLICATION)
          {
             E_Exec_Instance *ex;

             ex = e_exec(e_zone_current_get(), ic->desktop, NULL, NULL, "luncher");
             ic->exec = ex;
             e_exec_instance_watcher_add(ex, _bar_instance_watch, ic);
             if (!ic->starting) elm_layout_signal_emit(ic->o_layout, "e,state,starting", "e");
             ic->starting = EINA_TRUE;
          }
        else if (ic->desktop->type == EFREET_DESKTOP_TYPE_LINK)
          {
             if (!strncasecmp(ic->desktop->url, "file:", 5))
               {
                  E_Action *act;

                  act = e_action_find("fileman");
                  if (act)
                    act->func.go(NULL, ic->desktop->url + 5);
               }
          }
     }
   else if (ev->button == 1 && !ic->in_order)
     {
        _bar_icon_preview_show(ic);
     }
}

static void
_bar_icon_preview_item_mouse_out(void *data EINA_UNUSED, Evas *e EINA_UNUSED, Evas_Object *obj, void *event_data EINA_UNUSED)
{
   elm_layout_signal_emit(obj, "e,state,unfocused", "e");
}

static void
_bar_icon_preview_item_mouse_in(void *data EINA_UNUSED, Evas *e EINA_UNUSED, Evas_Object *obj, void *event_data)
{
   Evas_Event_Mouse_In *ev = event_data;

   if (ev->event_flags & EVAS_EVENT_FLAG_ON_HOLD) return;
   if (_bar_check_modifiers(ev->modifiers)) return;

   elm_layout_signal_emit(obj, "e,state,focused", "e");
}

static void
_bar_icon_preview_mouse_out(void *data, Evas *e EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event_data EINA_UNUSED)
{
   Icon *ic = data;

   if (ic->inst->current_preview_menu)
     return;
   E_FREE_FUNC(ic->mouse_out_timer, ecore_timer_del);
   ic->mouse_out_timer = ecore_timer_loop_add(0.25, _bar_icon_preview_hide, ic);
}

static void
_bar_icon_preview_mouse_in(void *data, Evas *e EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event_data)
{
   Icon *ic = data;
   Evas_Event_Mouse_In *ev = event_data;

   if (ev->event_flags & EVAS_EVENT_FLAG_ON_HOLD) return;
   if (_bar_check_modifiers(ev->modifiers)) return;

   E_FREE_FUNC(ic->mouse_out_timer, ecore_timer_del);
}

static void
_bar_icon_preview_menu_hide(void *data, Evas *e EINA_UNUSED, Evas_Object *obj, void *event_data EINA_UNUSED)
{
   Icon *ic = data;

   evas_object_event_callback_del_full(obj, EVAS_CALLBACK_HIDE, _bar_icon_preview_menu_hide, ic);
   ic->client_menu = NULL;
   if (ic->preview)
     {
        ic->inst->current_preview_menu = EINA_FALSE;
        _bar_icon_preview_mouse_out(ic, NULL, NULL, NULL);
     }
}

static void
_bar_icon_preview_mouse_up(void *data, Evas *e EINA_UNUSED, Evas_Object *obj, void *event_data)
{
   E_Client *ec = data;
   Evas_Event_Mouse_Up *ev = event_data;
   Icon *ic = evas_object_data_get(obj, "icon");

   if (ev->event_flags & EVAS_EVENT_FLAG_ON_HOLD) return;
   if (_bar_check_modifiers(ev->modifiers)) return;

   if (ev->button == 3)
     {
        e_int_client_menu_show(ec, ev->canvas.x, ev->canvas.y, 0, ev->timestamp);
        evas_object_event_callback_add(ec->border_menu->comp_object, EVAS_CALLBACK_HIDE,
            _bar_icon_preview_menu_hide, ic);
        ic->client_menu = ec->border_menu->comp_object;
        ic->inst->current_preview_menu = EINA_TRUE;
        return;
     }
   e_client_activate(ec, 1);
   _bar_icon_preview_hide(ic);
}

static void
_bar_icon_preview_del(void *data, Evas *e EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event_data EINA_UNUSED)
{
   Evas_Object *img = data;

   evas_object_del(img);
}

static void
_bar_icon_preview_client_add(Icon *ic, E_Client *ec)
{
   Evas_Object *layout, *label, *img;

   layout = elm_layout_add(ic->preview_box);
   edje_object_update_hints_set(elm_layout_edje_get(layout), EINA_TRUE);
   evas_object_data_set(layout, "icon", ic);
   e_theme_edje_object_set(layout, "e/gadget/luncher/preview",
       "e/gadget/luncher/preview");
   evas_object_size_hint_weight_set(layout, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
   evas_object_size_hint_align_set(layout, EVAS_HINT_FILL, EVAS_HINT_FILL);
   evas_object_event_callback_add(layout, EVAS_CALLBACK_MOUSE_IN,
       _bar_icon_preview_item_mouse_in, ic);
   evas_object_event_callback_add(layout, EVAS_CALLBACK_MOUSE_OUT,
       _bar_icon_preview_item_mouse_out, ic);
   elm_box_pack_end(ic->preview_box, layout);
   evas_object_show(layout);

   label = elm_label_add(layout);
   elm_object_style_set(label, "luncher_preview");
   elm_label_ellipsis_set(label, EINA_TRUE);
   elm_object_text_set(label, e_client_util_name_get(ec));
   evas_object_size_hint_weight_set(label, EVAS_HINT_EXPAND, EVAS_HINT_FILL);
   elm_layout_content_set(layout, "e.swallow.title", label);
   evas_object_show(label);

   img = e_comp_object_util_mirror_add(ec->frame);
   evas_object_size_hint_aspect_set(img, EVAS_ASPECT_CONTROL_BOTH, ec->client.w, ec->client.h);
   evas_object_size_hint_min_set(img, ic->inst->cfg->preview_size, ic->inst->cfg->preview_size);
   elm_layout_content_set(layout, "e.swallow.icon", img);
   if (evas_object_image_alpha_get(img))
     elm_layout_signal_emit(layout, "e,state,icon,alpha", "e");
   evas_object_show(img);

   if (!(ec->desk->visible) || (ec->iconic))
     elm_layout_signal_emit(layout, "e,state,invisible", "e");

   evas_object_event_callback_add(layout, EVAS_CALLBACK_MOUSE_UP,
       _bar_icon_preview_mouse_up, ec);
   evas_object_event_callback_add(layout, EVAS_CALLBACK_DEL,
       _bar_icon_preview_del, img);
   elm_layout_sizing_eval(layout);
}

static Eina_Bool
_bar_icon_preview_show(void *data)
{
   Icon *ic = data;
   Eina_List *l, *ll;
   E_Client *ec;
   E_Exec_Instance *ex;
   Eina_List *clients = NULL;
   E_Gadget_Site_Orient orient;
   E_Zone *zone = e_zone_current_get();
   int count = 0;

   if (!ic)
     return EINA_FALSE;
   ic->mouse_in_timer = NULL;
   if (ic->drag.dnd)
     return EINA_FALSE;
   if (!ic->inst)
     return EINA_FALSE;
   if (!ic->inst->o_icon_con)
     return EINA_FALSE;

   orient = e_gadget_site_orient_get(e_gadget_site_get(ic->inst->o_main));

   if (ic->inst->current_preview && (ic->inst->current_preview != ic->preview))
     _bar_icon_preview_hide(evas_object_data_get(ic->inst->current_preview, "icon"));
   if (ic->preview && !ic->preview_dismissed)
     _bar_icon_preview_hide(ic);
   ic->preview_dismissed = EINA_FALSE;
   if (!eina_list_count(ic->execs) && !eina_list_count(ic->clients))
     return EINA_FALSE;

   ic->preview = elm_ctxpopup_add(e_comp->elm);
   elm_object_style_set(ic->preview, "noblock");
   evas_object_smart_callback_add(ic->preview, "dismissed", _bar_popup_dismissed, ic);
   evas_object_event_callback_add(ic->preview, EVAS_CALLBACK_MOUSE_IN,
       _bar_icon_preview_mouse_in, ic);
   evas_object_event_callback_add(ic->preview, EVAS_CALLBACK_MOUSE_OUT,
       _bar_icon_preview_mouse_out, ic);

   ic->preview_scroller = elm_scroller_add(ic->preview);
   elm_scroller_content_min_limit(ic->preview_scroller, EINA_TRUE, EINA_TRUE);
   evas_object_size_hint_max_set(ic->preview_scroller, zone->w - 15, zone->h - 15);
   elm_object_style_set(ic->preview_scroller, "no_inset_shadow");
   E_EXPAND(ic->preview_scroller);

   ic->preview_box = elm_box_add(ic->preview);
   evas_object_size_hint_align_set(ic->preview_box, 0, 0);
   switch (orient)
     {
        case E_GADGET_SITE_ORIENT_HORIZONTAL:
          elm_box_horizontal_set(ic->preview_box, EINA_TRUE);
          elm_box_padding_set(ic->preview_box, 2, 0);
	  break;
        case E_GADGET_SITE_ORIENT_VERTICAL:
          elm_box_horizontal_set(ic->preview_box, EINA_FALSE);
          elm_box_padding_set(ic->preview_box, 0, 2);
	  break;
        default:
          elm_box_horizontal_set(ic->preview_box, EINA_TRUE);
          elm_box_padding_set(ic->preview_box, 2, 0);
     }
   EINA_LIST_FOREACH(ic->execs, l, ex)
     {
        EINA_LIST_FOREACH(ex->clients, ll, ec)
          clients = eina_list_append(clients, ec);
     }
   EINA_LIST_FOREACH(ic->clients, l, ec)
     clients = eina_list_append(clients, ec);

   EINA_LIST_FREE(clients, ec)
     {
        if (ec && !ec->netwm.state.skip_taskbar && !e_client_util_ignored_get(ec))
          {
             if (!e_client_util_is_popup(ec))
               {
                  _bar_icon_preview_client_add(ic, ec);
                  count++;
               }
          }
     }

   if (!count)
     {
        _bar_icon_preview_hide(ic);
        return EINA_FALSE;
     }
   elm_object_content_set(ic->preview_scroller, ic->preview_box);
   elm_object_content_set(ic->preview, ic->preview_scroller);
   evas_object_show(ic->preview_box);

   e_gadget_util_ctxpopup_place(ic->inst->o_main, ic->preview, ic->o_icon);
   evas_object_layer_set(ic->preview, E_LAYER_POPUP);

   evas_object_data_del(ic->preview, "icon");
   evas_object_data_set(ic->preview, "icon", ic);
   evas_object_show(ic->preview);
   ic->inst->current_preview = ic->preview;

   return EINA_FALSE;
}

static void
_bar_icon_mouse_in(void *data, Evas *e EINA_UNUSED, Evas_Object *obj, void *event_data)
{
   Icon *ic = data;
   Eina_Bool clients = EINA_FALSE;
   Evas_Event_Mouse_In *ev = event_data;

   if (ev->event_flags & EVAS_EVENT_FLAG_ON_HOLD) return;
   if (_bar_check_modifiers(ev->modifiers)) return;

   if (!ic->active)
     {
        if (!ic->inst->cfg->hide_tooltips)
          elm_object_tooltip_show(obj);
        ic->active = EINA_TRUE;
        elm_layout_signal_emit(ic->o_layout, "e,state,focused", "e");
     }
   E_FREE_FUNC(ic->mouse_out_timer, ecore_timer_del);
   E_FREE_FUNC(ic->mouse_in_timer, ecore_timer_del);
   if (ic->inst->current_preview && (ic->preview == ic->inst->current_preview)) return;
   if (eina_list_count(ic->execs) || eina_list_count(ic->clients))
     clients = EINA_TRUE;
   if (clients && ic->inst->current_preview && !ic->inst->current_preview_menu)
     ic->mouse_in_timer = ecore_timer_loop_add(0.2, _bar_icon_preview_show, ic);
   else if (clients && !ic->inst->current_preview)
     ic->mouse_in_timer = ecore_timer_loop_add(0.3, _bar_icon_preview_show, ic);
}

static void
_bar_exec_new_show(void *data, Evas *e EINA_UNUSED, Evas_Object *obj, void *event_data EINA_UNUSED)
{
   Icon *ic = data;
   E_Client *ec = e_comp_object_client_get(obj);

   if (ic->preview && (ic->inst->current_preview == ic->preview))
     {
        _bar_icon_preview_client_add(ic, ec);
     }
   else
     {
        if (ic->active)
          {
             E_FREE_FUNC(ic->mouse_out_timer, ecore_timer_del);
             E_FREE_FUNC(ic->mouse_in_timer, ecore_timer_del);
             if (ic->inst->current_preview && !ic->inst->current_preview_menu)
               _bar_icon_preview_show(ic);
             else if (!ic->inst->current_preview)
               ic->mouse_in_timer = ecore_timer_loop_add(0.3, _bar_icon_preview_show, ic);
          }
     }
   ic->client_cbs = eina_list_remove(ic->client_cbs, ec);
   evas_object_event_callback_del_full(ec->frame, EVAS_CALLBACK_SHOW, _bar_exec_new_show, ic);
}

static void
_bar_icon_file_set(Icon *ic, Efreet_Desktop *desktop, E_Client *non_desktop_client)
{
   const char *path = NULL, *k = NULL;
   char buf[4096];
   int len = 0, clamp = 0;
   if (desktop)
     {
        if (!desktop->icon)
          path = NULL;
        else if (strncmp(desktop->icon, "/", 1) && !ecore_file_exists(desktop->icon))
          {
             clamp = MAX((4 * round((double)ic->inst->size/4)), 8);
             path = efreet_icon_path_find(e_config->icon_theme, desktop->icon, clamp);
             if (!path)
               {
                  if (e_util_strcmp(e_config->icon_theme, "hicolor"))
                    path = efreet_icon_path_find("hicolor", desktop->icon, clamp);
               }
          }
        else if (ecore_file_exists(desktop->icon))
          {
             path = desktop->icon;
          }
        if (!path && desktop->icon)
          {
             snprintf(buf, sizeof(buf), "e/icons/%s", desktop->icon);
             if (eina_list_count(e_theme_collection_items_find("base/theme/icons", buf)))
               {
                  path = e_theme_edje_file_get("base/theme/icons", buf);
                  k = buf;
               }
             else
               {
                  path = e_theme_edje_file_get("base/theme/icons", "e/icons/unknown");
                  k =  "e/icons/unknown";
               }
          }
        else if (!path && !desktop->icon)
          {
             path = e_theme_edje_file_get("base/theme/icons", "e/icons/unknown");
             k = "e/icons/unknown";
          }
        if (path && desktop->icon && !k)
          {
             len = strlen(desktop->icon);
             if ((len > 4) && (!strcasecmp(desktop->icon + len - 4, ".edj")))
               k = "icon";
          }
     }
   else if (non_desktop_client)
     {
        Evas_Object *tmp;
        const char *file, *group;
        Eina_Bool ret = EINA_FALSE;

        tmp = e_client_icon_add(non_desktop_client, evas_object_evas_get(ic->o_layout));
        if (isedje(tmp))
          {
             edje_object_file_get(tmp, &file, &group);
             if (file && group)
               ret = EINA_TRUE;
          }
        else
          ret = e_icon_file_get(tmp, &file, &group);
        if (ret)
          {
             eina_stringshare_replace(&ic->icon, file);
             eina_stringshare_replace(&ic->key, group);
             path = ic->icon;
             k = ic->key;
          }
        evas_object_del(tmp);
     }
   else if (ic->icon)
     {
        if (strncmp(ic->icon, "/", 1) && !ecore_file_exists(ic->icon))
          {
             clamp = (4 * round((double)ic->inst->size/4));
             path = efreet_icon_path_find(e_config->icon_theme, ic->icon, clamp);
             if (!path)
               {
                  if (e_util_strcmp(e_config->icon_theme, "hicolor"))
                    path = efreet_icon_path_find("hicolor", ic->icon, clamp);
               }
          }
        else if (ecore_file_exists(ic->icon))
          {
             path = ic->icon;
             k = ic->key;
          }
        if (!path)
          {
             path = e_theme_edje_file_get("base/theme/icons", "e/icons/unknown");
             k = "e/icons/unknown";
          }
     }
   else
     {
        path = e_theme_edje_file_get("base/theme/icons", "e/icons/unknown");
        k = "e/icons/unknown";
     }
   elm_image_file_set(ic->o_icon, path, k);
}

static void
_bar_resized_cb(void *data, Evas_Object *obj EINA_UNUSED, const char *emission EINA_UNUSED, const char *source EINA_UNUSED)
{
   Instance *inst = data;

   _bar_aspect(inst);
}

static Icon *
_bar_icon_add(Instance *inst, Efreet_Desktop *desktop, E_Client *non_desktop_client)
{
   char ori[32];
   Icon *ic;
   const Eina_List *l;

   ic = E_NEW(Icon, 1);
   if (desktop)
     efreet_desktop_ref(desktop);
   ic->desktop = desktop;
   ic->inst = inst;
   ic->preview = NULL;
   ic->preview_box = NULL;
   ic->preview_scroller = NULL;
   ic->client_menu = NULL;
   ic->mouse_in_timer = NULL;
   ic->mouse_out_timer = NULL;
   ic->active = EINA_FALSE;
   ic->starting = EINA_FALSE;
   ic->preview_dismissed = EINA_FALSE;
   ic->exec = NULL;

   ic->o_layout = elm_layout_add(inst->o_icon_con);
   edje_object_update_hints_set(elm_layout_edje_get(ic->o_layout), EINA_TRUE);
   e_theme_edje_object_set(ic->o_layout, "e/gadget/luncher/icon",
       "e/gadget/luncher/icon");
   E_EXPAND(ic->o_layout);
   E_FILL(ic->o_layout);
   edje_object_signal_callback_add(elm_layout_edje_get(ic->o_layout), "e,state,resized", "e", _bar_resized_cb, inst);
   elm_box_pack_end(inst->o_icon_con, ic->o_layout);
   evas_object_show(ic->o_layout);

   snprintf(ori, sizeof(ori), "e,state,off,%s", _bar_location_get(inst));
   elm_layout_signal_emit(ic->o_layout, ori, "e");

   ic->o_icon = elm_icon_add(ic->o_layout);
   E_EXPAND(ic->o_icon);


   _bar_icon_file_set(ic, desktop, non_desktop_client);

   if (!inst->cfg->hide_tooltips)
     {
        if (desktop)
          elm_object_tooltip_text_set(ic->o_icon, desktop->name);
        else if (non_desktop_client && non_desktop_client->icccm.class)
          elm_object_tooltip_text_set(ic->o_icon, non_desktop_client->icccm.class);
        else if (non_desktop_client && non_desktop_client->icccm.name)
          elm_object_tooltip_text_set(ic->o_icon, non_desktop_client->icccm.name);
        else if (non_desktop_client && non_desktop_client->icccm.title)
          elm_object_tooltip_text_set(ic->o_icon, non_desktop_client->icccm.title);
        else if (non_desktop_client && non_desktop_client->netwm.name)
          elm_object_tooltip_text_set(ic->o_icon, non_desktop_client->netwm.name);
        else
          elm_object_tooltip_text_set(ic->o_icon, _("Unknown"));
        elm_object_tooltip_orient_set(ic->o_icon, ELM_TOOLTIP_ORIENT_CENTER);
        elm_object_tooltip_style_set(ic->o_icon, "luncher");
     }
   evas_object_size_hint_aspect_set(ic->o_icon, EVAS_ASPECT_CONTROL_BOTH, 1, 1);
   elm_layout_content_set(ic->o_layout, "e.swallow.icon", ic->o_icon);
   evas_object_event_callback_add(ic->o_icon, EVAS_CALLBACK_MOUSE_UP,
       _bar_icon_mouse_up, ic);
   evas_object_event_callback_priority_add(ic->o_icon, EVAS_CALLBACK_MOUSE_DOWN, 0,
       _bar_icon_mouse_down, ic);
   evas_object_event_callback_add(ic->o_icon, EVAS_CALLBACK_MOUSE_MOVE,
       _bar_icon_mouse_move, ic);
   evas_object_event_callback_add(ic->o_icon, EVAS_CALLBACK_MOUSE_IN,
       _bar_icon_mouse_in, ic);
   evas_object_event_callback_add(ic->o_icon, EVAS_CALLBACK_MOUSE_OUT,
       _bar_icon_mouse_out, ic);
   evas_object_show(ic->o_icon);

   if (desktop)
     eina_hash_add(inst->icons_desktop_hash, eina_stringshare_add(desktop->orig_path), ic);
   else
     eina_hash_add(inst->icons_clients_hash, &non_desktop_client, ic);

   if (desktop)
     {
        if (inst->cfg->type != E_LUNCHER_MODULE_LAUNCH_ONLY)
          {
             l = e_exec_desktop_instances_find(desktop);
             if (l)
               {
                  snprintf(ori, sizeof(ori), "e,state,on,%s", _bar_location_get(inst));
                  elm_layout_signal_emit(ic->o_layout, ori, "e");
                  ic->execs = eina_list_clone(l);
               }
          }
     }
   else
     {
        if (!_bar_check_for_duplicates(ic, non_desktop_client))
          ic->clients = eina_list_append(ic->clients, non_desktop_client);
     }
   elm_layout_sizing_eval(ic->o_layout);
   _bar_aspect(inst);
   return ic;
}

static Eina_Bool
_bar_cb_desk_switch(void *data EINA_UNUSED, int type EINA_UNUSED, E_Event_Client *ev EINA_UNUSED)
{
   Instance *inst = NULL;
   Icon *ic = NULL;
   Eina_List *l = NULL, *ll = NULL;


   EINA_LIST_FOREACH(luncher_instances, l, inst)
     {
        if (inst->current_preview)
          {
             EINA_LIST_FOREACH(inst->icons, ll, ic)
               {
                  if (inst->current_preview && (inst->current_preview == ic->preview))
                    {
                       _bar_icon_preview_show(ic);
                       return ECORE_CALLBACK_RENEW;
                    }
               }
          }
     }
   return ECORE_CALLBACK_RENEW;
}

static Eina_Bool
_bar_cb_client_remove(void *data EINA_UNUSED, int type EINA_UNUSED, E_Event_Client *ev)
{
   Instance *inst = NULL;
   Eina_List *l = NULL;
   char ori[32];

   EINA_LIST_FOREACH(luncher_instances, l, inst)
     {
        Icon *ic = NULL;

        if (!inst->bar) continue;
        if (inst->cfg->type == E_LUNCHER_MODULE_LAUNCH_ONLY) continue;
        ic = _bar_icon_match(inst, ev->ec);
        if (ic)
          {
             if (ic->starting) elm_layout_signal_emit(ic->o_layout, "e,state,started", "e");
             ic->starting = EINA_FALSE;
             ic->clients = eina_list_remove(ic->clients, ev->ec);
             if (ic->client_cbs)
               {
                  ic->client_cbs = eina_list_remove(ic->client_cbs, ev->ec);
                  evas_object_event_callback_del_full(ev->ec->frame, EVAS_CALLBACK_SHOW, _bar_exec_new_show, ic);
               }
             if (ev->ec->exe_inst)
               ic->execs = eina_list_remove(ic->execs, ev->ec->exe_inst);
             if (!eina_list_count(ic->execs) && !eina_list_count(ic->clients))
               {
                  snprintf(ori, sizeof(ori), "e,state,off,%s", _bar_location_get(inst));
                  elm_layout_signal_emit(ic->o_layout, ori, "e");
                  if (ic->preview)
                    _bar_icon_preview_hide(ic);
                  if (!ic->in_order)
                    _bar_icon_del(inst, ic);
               }
             else if (ic->preview && (inst->current_preview == ic->preview))
               {
                  _bar_icon_preview_show(ic);
               }
          }
     }
   return ECORE_CALLBACK_RENEW;
}

static Eina_Bool
_bar_cb_exec_del(void *data EINA_UNUSED, int type EINA_UNUSED, E_Exec_Instance *ex)
{
   Instance *inst = NULL;
   Eina_List *l = NULL;
   E_Client *ec = NULL;
   char ori[32];

   EINA_LIST_FOREACH(ex->clients, l, ec)
     {
        if (!ec->netwm.state.skip_taskbar && !e_client_util_is_popup(ec))
          {
             break;
          }
     }

   EINA_LIST_FOREACH(luncher_instances, l, inst)
     {
        Icon *ic = NULL;

        if (!inst->bar) continue;
        if (inst->cfg->type == E_LUNCHER_MODULE_LAUNCH_ONLY) continue;
	       if (ex->desktop)
          {
             ic = eina_hash_find(inst->icons_desktop_hash, ex->desktop->orig_path);
          }
        if (ic)
          {
             if (ic->starting) elm_layout_signal_emit(ic->o_layout, "e,state,started", "e");
             ic->starting = EINA_FALSE;
             ic->execs = eina_list_remove(ic->execs, ex);
             if (ec) ic->clients = eina_list_remove(ic->clients, ec);
             if (ec && ic->client_cbs)
               {
                  ic->client_cbs = eina_list_remove(ic->client_cbs, ec);
                  evas_object_event_callback_del_full(ec->frame, EVAS_CALLBACK_SHOW, _bar_exec_new_show, ic);
               }
             if (!eina_list_count(ic->execs) && !eina_list_count(ic->clients))
               {
                  snprintf(ori, sizeof(ori), "e,state,off,%s", _bar_location_get(inst));
                  elm_layout_signal_emit(ic->o_layout, ori, "e");
                  if (ic->preview)
                    _bar_icon_preview_hide(ic);
                  if (!ic->in_order)
                    _bar_icon_del(inst, ic);
               }
             else if (ic->preview && (inst->current_preview == ic->preview))
               {
                  _bar_icon_preview_show(ic);
               }
          }
     }
   return ECORE_CALLBACK_RENEW;
}

static Eina_Bool
_bar_cb_exec_client_prop(void *data EINA_UNUSED, int type EINA_UNUSED, E_Event_Client_Property *ev)
{
   Instance *inst = NULL;
   E_Client *ec = NULL;
   Eina_List *l = NULL;
   Eina_Bool skip;
   Eina_Bool has_desktop = EINA_TRUE;

   if (e_client_util_ignored_get(ev->ec))
     return ECORE_CALLBACK_RENEW;
   if ((!(ev->property & E_CLIENT_PROPERTY_NETWM_STATE)) && (!(ev->property & E_CLIENT_PROPERTY_ICON))
       && (!(ev->property & E_CLIENT_PROPERTY_URGENCY)))
     return ECORE_CALLBACK_RENEW;

   if (!ev->ec->exe_inst)
     has_desktop = EINA_FALSE;
   if (ev->ec->exe_inst)
     {
        if (!ev->ec->exe_inst->desktop)
          has_desktop = EINA_FALSE;
     }
   if (has_desktop)
     {
        skip = EINA_TRUE;
        EINA_LIST_FOREACH(ev->ec->exe_inst->clients, l, ec)
          {
             if (!ec->netwm.state.skip_taskbar && !e_client_util_is_popup(ec))
               {
                  skip = EINA_FALSE;
                  break;
               }
          }
     }
   else
     {
        if (ev->ec->netwm.state.skip_taskbar || e_client_util_is_popup(ev->ec))
          skip = EINA_TRUE;
        else
          skip = EINA_FALSE;
     }
   EINA_LIST_FOREACH(luncher_instances, l, inst)
     {
        Icon *ic = NULL;
        char ori[32];

        if (!inst->bar) continue;
        if (inst->cfg->type == E_LUNCHER_MODULE_LAUNCH_ONLY) continue;
	ic = _bar_icon_match(inst, ev->ec);
        if (skip && !ic) continue;
        if (!skip)
          {
             if (ic)
               {
                  if ((ev->property & E_CLIENT_PROPERTY_URGENCY))
                    {
                       if (ev->ec->urgent)
                         elm_layout_signal_emit(ic->o_layout, "e,state,urgent", "e");
                       else
                         elm_layout_signal_emit(ic->o_layout, "e,state,normal", "e");
                       return ECORE_CALLBACK_RENEW;
                    }
                  if (ic->starting) elm_layout_signal_emit(ic->o_layout, "e,state,started", "e");
                  ic->starting = EINA_FALSE;
                  snprintf(ori, sizeof(ori), "e,state,on,%s", _bar_location_get(inst));
                  elm_layout_signal_emit(ic->o_layout, ori, "e");
                  if (has_desktop)
                    {
                       if (!(_bar_check_for_duplicates(ic, ev->ec)))
                         ic->execs = eina_list_append(ic->execs, ev->ec->exe_inst);
                    }
                  else
                    {
                       if (!(_bar_check_for_duplicates(ic, ev->ec)))
                         ic->clients = eina_list_append(ic->clients, ev->ec);
                    }
               }
             else
               {
                  if (has_desktop && !ev->ec->internal_elm_win)
                    ic = _bar_icon_add(inst, ev->ec->exe_inst->desktop, NULL);
                  else
                    ic = _bar_icon_add(inst, NULL, ev->ec);
                  snprintf(ori, sizeof(ori), "e,state,on,%s", _bar_location_get(inst));
                  elm_layout_signal_emit(ic->o_layout, ori, "e");
                  ic->in_order = EINA_FALSE;
                  inst->icons = eina_list_append(inst->icons, ic);
                  _bar_aspect(inst);
               }
          }
        else
          {
             if (has_desktop)
               ic->execs = eina_list_remove(ic->execs, ev->ec->exe_inst);
             else
               ic->clients = eina_list_remove(ic->clients, ev->ec);
             if (ic->client_cbs)
               {
                  ic->client_cbs = eina_list_remove(ic->client_cbs, ev->ec);
                  evas_object_event_callback_del_full(ev->ec->frame, EVAS_CALLBACK_SHOW, _bar_exec_new_show, ic);
               }
             if (!eina_list_count(ic->execs) && !eina_list_count(ic->clients))
               {
                  if (ic->preview)
                    _bar_icon_preview_hide(ic);
                  if (!ic->in_order)
                    _bar_icon_del(inst, ic);
                  else
                    {
                       snprintf(ori, sizeof(ori), "e,state,off,%s", _bar_location_get(inst));
                       elm_layout_signal_emit(ic->o_layout, ori, "e");
                    }
               }
             else if (ic->preview && (inst->current_preview == ic->preview))
               {
                  _bar_icon_preview_show(ic);
               }
          }
     }
   return ECORE_CALLBACK_RENEW;
}

static Eina_Bool
_bar_cb_exec_new(void *data EINA_UNUSED, int type, E_Exec_Instance *ex)
{
   Instance *inst = NULL;
   E_Client *ec = NULL;
   Eina_List *l = NULL;
   Eina_Bool skip;

   if (type == E_EVENT_EXEC_NEW_CLIENT)
     {
        ec = eina_list_last_data_get(ex->clients);
        if (ec->netwm.state.skip_taskbar || e_client_util_is_popup(ec))
          skip = EINA_TRUE;
        else
          skip = EINA_FALSE;
     }
   else
     {
        skip = EINA_TRUE;
        EINA_LIST_FOREACH(ex->clients, l, ec)
          {
             if (!ec->netwm.state.skip_taskbar && !e_client_util_is_popup(ec))
               {
                  skip = EINA_FALSE;
                  break;
               }
          }
     }
   EINA_LIST_FOREACH(luncher_instances, l, inst)
     {
        Icon *ic = NULL;
        char ori[32];

        if (!inst->bar) continue;
        if (inst->cfg->type == E_LUNCHER_MODULE_LAUNCH_ONLY) continue;
        if (ec) ic = _bar_icon_match(inst, ec);
        if (ic)
          {
             if (skip) continue;
             if (ic->starting) elm_layout_signal_emit(ic->o_layout, "e,state,started", "e");
             ic->starting = EINA_FALSE;
             snprintf(ori, sizeof(ori), "e,state,on,%s", _bar_location_get(inst));
             elm_layout_signal_emit(ic->o_layout, ori, "e");
             if (!(_bar_check_for_duplicates(ic, ec)))
               {
                  if (ex->desktop)
                    ic->execs = eina_list_append(ic->execs, ex);
                  else
                    ic->clients = eina_list_append(ic->clients, ec);
               }
             if (evas_object_visible_get(ec->frame))
               _bar_exec_new_show(ic, NULL, ec->frame, NULL);
             else
               {
                  ic->client_cbs = eina_list_append(ic->client_cbs, ec);
                  evas_object_event_callback_add(ec->frame, EVAS_CALLBACK_SHOW,
                    _bar_exec_new_show, ic);
               }
          }
        else
          {
             if (skip) continue;
             if (ex->desktop && !ec->internal_elm_win)
               ic = _bar_icon_add(inst, ex->desktop, NULL);
             else
               ic = _bar_icon_add(inst, NULL, ec);
             snprintf(ori, sizeof(ori), "e,state,on,%s", _bar_location_get(inst));
             elm_layout_signal_emit(ic->o_layout, ori, "e");
             ic->in_order = EINA_FALSE;
             inst->icons = eina_list_append(inst->icons, ic);
             _bar_aspect(inst);
          }
     }
   return ECORE_CALLBACK_RENEW;
}

static void
_bar_empty(Instance *inst)
{
   if (inst->icons)
     {
        elm_box_clear(inst->o_icon_con);
        while (inst->icons)
          _bar_icon_del(inst, eina_list_data_get(inst->icons));
        inst->icons = NULL;
     }
}

static void
_bar_fill(Instance *inst)
{
   const Eina_Hash *execs = e_exec_instances_get();
   Eina_Iterator *it;
   Eina_List *l, *ll, *lll;
   E_Exec_Instance *ex;
   E_Client *ec;
   Icon *ic;
   char ori[32];

   if (inst->order && inst->cfg->type != E_LUNCHER_MODULE_TASKS_ONLY)
     {
        Efreet_Desktop *desktop;
        Eina_List *list;

        EINA_LIST_FOREACH(inst->order->desktops, list, desktop)
          {
             ic = _bar_icon_add(inst, desktop, NULL);
             ic->in_order = EINA_TRUE;
             inst->icons = eina_list_append(inst->icons, ic);
          }
     }
   if (inst->cfg->type == E_LUNCHER_MODULE_LAUNCH_ONLY)
     {
        _bar_aspect(inst);
        return;
     }
   it = eina_hash_iterator_data_new(execs);
   EINA_ITERATOR_FOREACH(it, l)
     {
        EINA_LIST_FOREACH(l, ll, ex)
          {
             Eina_Bool skip = EINA_TRUE;

             EINA_LIST_FOREACH(ex->clients, lll, ec)
               {
                  if (!ec->netwm.state.skip_taskbar && !e_client_util_is_popup(ec))
                    {
                       skip = EINA_FALSE;
                    }
                  if (skip) continue;
                  ic = _bar_icon_match(inst, ec);
                  if (ic)
                    {
                       if (!(_bar_check_for_duplicates(ic, ec)))
                         ic->execs = eina_list_append(ic->execs, ex);
                       continue;
                    }
                  if (!ec->internal_elm_win)
                    ic = _bar_icon_add(inst, ex->desktop, NULL);
                  else
                    ic = _bar_icon_add(inst, NULL, ec);
                  snprintf(ori, sizeof(ori), "e,state,on,%s", _bar_location_get(inst));
                  elm_layout_signal_emit(ic->o_layout, ori, "e");
                  ic->in_order = EINA_FALSE;
                  inst->icons = eina_list_append(inst->icons, ic);
               }
          }
     }
   eina_iterator_free(it);
   E_CLIENT_FOREACH(ec)
     {
        if (e_client_util_ignored_get(ec)) continue;
        if (ec->netwm.state.skip_taskbar) continue;
        if (e_client_util_is_popup(ec)) continue;
        ic = _bar_icon_match(inst, ec);
        if (!ic)
          {
             if (ec->exe_inst && ec->exe_inst->desktop && !ec->internal_elm_win)
               ic = _bar_icon_add(inst, ec->exe_inst->desktop, NULL);
             else
               ic = _bar_icon_add(inst, NULL, ec);
             snprintf(ori, sizeof(ori), "e,state,on,%s", _bar_location_get(inst));
             elm_layout_signal_emit(ic->o_layout, ori, "e");
             ic->in_order = EINA_FALSE;
             inst->icons = eina_list_append(inst->icons, ic);
          }
     }
   _bar_aspect(inst);
}

static void
_bar_resize_job(void *data)
{
   Instance *inst = data;
   Eina_List *l;
   Icon *ic;
   E_Gadget_Site_Orient orient;
   Evas_Coord x, y, w, h, size;

   if (inst)
     {
        orient = e_gadget_site_orient_get(e_gadget_site_get(inst->o_main));
        elm_layout_sizing_eval(inst->o_main);
        evas_object_geometry_get(inst->o_main, &x, &y, &w, &h);
        switch (orient)
          {
             case E_GADGET_SITE_ORIENT_HORIZONTAL:
               size = h;
               break;
             case E_GADGET_SITE_ORIENT_VERTICAL:
               size = w;
               break;
             default:
               size = h;
          }
        if (inst->size == size)
          {
             inst->resize_job = NULL;
             return;
          }
        inst->size = size;
        EINA_LIST_FOREACH(inst->icons, l, ic)
          _bar_icon_file_set(ic, ic->desktop, NULL);
        inst->resize_job = NULL;
     }
}

static Eina_Bool
_bar_cb_update_icons(EINA_UNUSED void *data, EINA_UNUSED int ev_type, EINA_UNUSED void *ev)
{
   Instance *inst = NULL;
   Eina_List *l, *ll;
   Icon *ic;

   EINA_LIST_FOREACH(luncher_instances, l, inst)
     {
        if (!inst->bar) continue;
        EINA_LIST_FOREACH(inst->icons, ll, ic)
          _bar_icon_file_set(ic, ic->desktop, eina_list_data_get(ic->clients));
     }
   return ECORE_CALLBACK_PASS_ON;
}

static void
_bar_resize(void *data, Evas *e EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event_data EINA_UNUSED)
{
   Instance *inst = data;

   if (inst->resize_job) return;
   inst->resize_job = ecore_job_add(_bar_resize_job, inst);
}

static void
_bar_mouse_out(void *data, Evas *e EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event_data EINA_UNUSED)
{
   Instance *inst = data;
   Evas_Coord px, py, x, y, w, h;
   Eina_List *l = NULL;
   Icon *ic = NULL;

   evas_pointer_canvas_xy_get(evas_object_evas_get(inst->o_main), &px, &py);
   evas_object_geometry_get(inst->o_main, &x, &y, &w, &h);

   if (E_INSIDE(px, py, x, y, w, h)) return;

   EINA_LIST_FOREACH(inst->icons, l, ic)
     {
        elm_layout_signal_emit(ic->o_layout, "e,state,default", "e");
        elm_layout_signal_emit(ic->o_layout, "e,state,unfocused", "e");
        _bar_icon_mouse_out(ic, NULL, ic->o_icon, NULL);
     }
   _bar_aspect(inst);
}

static void
_bar_removed_cb(void *data, Evas_Object *obj EINA_UNUSED, void *event_data)
{
   Instance *inst = data;
   char buf[4096];

   if (inst->o_main != event_data) return;
   if (e_user_dir_snprintf(buf, sizeof(buf), "applications/bar/%s", inst->cfg->dir) >= sizeof(buf))
     return;

   luncher_config->items = eina_list_remove(luncher_config->items, inst->cfg);
   eina_stringshare_del(inst->cfg->dir);
   E_FREE(inst->cfg);
}

static void
_bar_del(void *data, Evas *e EINA_UNUSED, Evas_Object *obj, void *event_data EINA_UNUSED)
{
   Instance *inst = data;
   Ecore_Event_Handler *handler;

   evas_object_smart_callback_del_full(e_gadget_site_get(obj), "gadget_removed", _bar_removed_cb, inst);
   E_FREE_FUNC(inst->iconify_provider, e_comp_object_effect_mover_del);
   inst->main_del = EINA_TRUE;
   _bar_empty(inst);
   e_object_del(E_OBJECT(inst->order));
   E_FREE_FUNC(inst->drop_handler, evas_object_del);
   eina_hash_free(inst->icons_desktop_hash);
   eina_hash_free(inst->icons_clients_hash);
   luncher_instances = eina_list_remove(luncher_instances, inst);
   free(inst);
   if (!eina_list_count(luncher_instances))
     {
        EINA_LIST_FREE(handlers, handler)
          E_FREE_FUNC(handler, ecore_event_handler_del);
     }
}

static void
_bar_drop_drop(void *data, const char *type, void *event_data)
{
   Instance *inst = data;
   E_Event_Dnd_Drop *ev = event_data;
   Efreet_Desktop *desktop = NULL;
   Eina_List *l = NULL;
   Icon *ic = NULL;

   evas_object_del(inst->place_holder);
   inst->place_holder = NULL;
   if (!strcmp(type, "enlightenment/desktop"))
     desktop = ev->data;
   else if (!strcmp(type, "enlightenment/border"))
     {
        E_Client *ec;

        ec = ev->data;
        desktop = ec->desktop;
        if (!desktop)
          {
             desktop = e_desktop_client_create(ec);
             efreet_desktop_save(desktop);
             e_desktop_edit(desktop);
          }
     }
   else if (!strcmp(type, "text/uri-list"))
     l = ev->data;

   ic = inst->drop_before;
   if (ic)
     {
        if (desktop)
          e_order_prepend_relative(inst->order, desktop, ic->desktop);
        else
          e_order_files_prepend_relative(inst->order, l, ic->desktop);
     }
   else
     {
        if (desktop)
          e_order_append(inst->order, desktop);
        else
          e_order_files_append(inst->order, l);
     }
}

static void
_bar_drop_leave(void *data, const char *type EINA_UNUSED, void *event_data EINA_UNUSED)
{
   Instance *inst = data;

   inst->inside = EINA_FALSE;
   evas_object_del(inst->place_holder);
   inst->place_holder = NULL;
}

static void
_bar_drop_move(void *data, const char *type EINA_UNUSED, void *event_data)
{
   Instance *inst = data;
   E_Event_Dnd_Move *ev = event_data;
   Evas_Coord x = ev->x, y = ev->y;
   Eina_List *l;
   Icon *ic;

   if (!inst->inside)
     return;

   EINA_LIST_FOREACH(inst->icons, l, ic)
     {
        Evas_Coord dx, dy, dw, dh;

        if (!ic->in_order) continue;
        evas_object_geometry_get(ic->o_layout, &dx, &dy, &dw, &dh);
        if (E_INSIDE(x, y, dx, dy, dw, dh))
          inst->drop_before = ic;
     }
   elm_box_unpack(inst->o_icon_con, inst->place_holder);
   if (inst->drop_before)
     elm_box_pack_before(inst->o_icon_con, inst->place_holder, inst->drop_before->o_layout);
}

static void
_bar_drop_enter(void *data, const char *type EINA_UNUSED, void *event_data EINA_UNUSED)
{
   Instance *inst = data;

   inst->inside = EINA_TRUE;
   inst->place_holder = evas_object_rectangle_add(evas_object_evas_get(inst->o_icon_con));
   evas_object_color_set(inst->place_holder, 0, 0, 0, 0);
   evas_object_size_hint_min_set(inst->place_holder, inst->size, inst->size);
   evas_object_size_hint_max_set(inst->place_holder, inst->size, inst->size);
   evas_object_show(inst->place_holder);
}

static void
_bar_iconify_end(void *data, Evas_Object *obj EINA_UNUSED, const char *sig EINA_UNUSED, const char *src EINA_UNUSED)
{
   E_Client *ec = data;

   evas_object_layer_set(ec->frame, ec->layer);
   ec->layer_block = 0;
   if (ec->iconic)
     evas_object_hide(ec->frame);
}

static Eina_Bool
_bar_iconify_start(void *data, Evas_Object *obj, const char *signal EINA_UNUSED)
{
   Instance *inst = data;
   Icon *ic = NULL;
   E_Client *ec;
   int ox, oy, ow, oh;

   ec = e_comp_object_client_get(obj);

   if (ec)
     ic = _bar_icon_match(inst, ec);

   if (!ic) return EINA_FALSE;

   ec->layer_block = 1;
   evas_object_layer_set(ec->frame, E_LAYER_CLIENT_PRIO);
   evas_object_geometry_get(ic->o_layout, &ox, &oy, &ow, &oh);
   e_comp_object_effect_set(ec->frame, "iconify/luncher");
   e_comp_object_effect_params_set(ec->frame, 1, (int[]){ec->x, ec->y, ec->w, ec->h, ox, oy, ow, oh}, 8);
   e_comp_object_effect_params_set(ec->frame, 0, (int[]){!!strcmp(signal, "e,action,iconify")}, 1);
   e_comp_object_effect_start(ec->frame, _bar_iconify_end, ec);
   return EINA_TRUE;
}

static void
_bar_anchor_changed_cb(void *data, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED)
{
   Instance *inst = data;

   if (inst && inst->o_icon_con)
     {
        bar_recalculate(inst);
     }
}

static void
_bar_order_update(void *data, E_Order *eo EINA_UNUSED)
{
   Instance *inst = data;

   if (inst && inst->o_icon_con)
     {
        bar_recalculate(inst);
     }
}

static void
_bar_created_cb(void *data, Evas_Object *obj, void *event_data EINA_UNUSED)
{
   Instance *inst = data;
   char buf[4096];
   const char *drop[] = { "enlightenment/desktop", "enlightenment/border", "text/uri-list" };
   E_Gadget_Site_Orient orient = e_gadget_site_orient_get(e_gadget_site_get(inst->o_main));

   inst->o_icon_con = elm_box_add(inst->o_main);
   E_EXPAND(inst->o_icon_con);
   switch (orient)
     {
        case E_GADGET_SITE_ORIENT_HORIZONTAL:
           elm_box_horizontal_set(inst->o_icon_con, EINA_TRUE);
           break;
        case E_GADGET_SITE_ORIENT_VERTICAL:
           elm_box_horizontal_set(inst->o_icon_con, EINA_FALSE);
           break;
         default:
           elm_box_horizontal_set(inst->o_icon_con, EINA_TRUE);
     }
   elm_layout_content_set(inst->o_main, "e.swallow.bar", inst->o_icon_con);
   evas_object_show(inst->o_icon_con);

   evas_object_data_set(inst->o_main, "instance", inst);
   evas_object_data_set(inst->o_icon_con, "instance", inst);

   e_gadget_configure_cb_set(inst->o_main, _bar_gadget_configure);
   e_gadget_menu_populate_cb_set(inst->o_main, _bar_gadget_menu_populate);
   evas_object_smart_callback_del_full(obj, "gadget_created", _bar_created_cb, data);

   if (!inst->cfg->dir)
     inst->cfg->dir = eina_stringshare_add("default");
   if (inst->cfg->dir[0] != '/')
     e_user_dir_snprintf(buf, sizeof(buf), "applications/bar/%s/.order",
                         inst->cfg->dir);
   else
     eina_strlcpy(buf, inst->cfg->dir, sizeof(buf));

   inst->order = e_order_new(buf);
   e_order_update_callback_set(inst->order, _bar_order_update, inst);

   if (inst->cfg->type != E_LUNCHER_MODULE_LAUNCH_ONLY)
     {
        if (inst->cfg->id != -1)
          inst->iconify_provider = e_comp_object_effect_mover_add(80, "e,action,*iconify",
              _bar_iconify_start, inst);
     }
   _bar_fill(inst);

   if (inst->cfg->type != E_LUNCHER_MODULE_TASKS_ONLY)
     {
        inst->drop_handler =
          e_gadget_drop_handler_add(inst->o_main, inst,
                             _bar_drop_enter, _bar_drop_move,
                             _bar_drop_leave, _bar_drop_drop,
                             drop, 3);
        elm_layout_content_set(inst->o_main, "e.swallow.drop", inst->drop_handler);
        evas_object_show(inst->drop_handler);
     }
   evas_object_event_callback_add(inst->o_main, EVAS_CALLBACK_RESIZE, _bar_resize, inst);
}

static Config_Item *
_conf_item_get(int *id)
{
   Config_Item *ci;
   Eina_List *l;

   if (*id > 0)
     {
        EINA_LIST_FOREACH(luncher_config->items, l, ci)
          if (*id == ci->id) return ci;
     }

   ci = E_NEW(Config_Item, 1);

   if (*id != -1)
     ci->id = eina_list_count(luncher_config->items)+1;
   else
     ci->id = -1;
   ci->preview_size = 64;
   ci->dir = eina_stringshare_add("default");
   ci->type = E_LUNCHER_MODULE_FULL;
   luncher_config->items = eina_list_append(luncher_config->items, ci);

   return ci;
}

static void
_bar_recalculate_job(void *data)
{
   Instance *inst = data;

   if (inst)
     {
        if (inst->o_icon_con)
          {
             _bar_empty(inst);
             _bar_fill(inst);
          }
        inst->recalc_job = NULL;
     }
}

EINTERN void
bar_recalculate(Instance *inst)
{
   E_FREE_FUNC(inst->recalc_job, ecore_job_del);
   inst->recalc_job = ecore_job_add(_bar_recalculate_job, inst);
}

EINTERN void
bar_reorder(Instance *inst)
{
   char buf[4096];

   if (inst)
     {
        E_FREE_FUNC(inst->recalc_job, ecore_job_del);
        _bar_empty(inst);
        if (!inst->cfg->dir)
          inst->cfg->dir = eina_stringshare_add("default");
        if (inst->cfg->dir[0] != '/')
          e_user_dir_snprintf(buf, sizeof(buf), "applications/bar/%s/.order",
                              inst->cfg->dir);
        else
          eina_strlcpy(buf, inst->cfg->dir, sizeof(buf));
        e_object_del(E_OBJECT(inst->order));
        inst->order = e_order_new(buf);
        e_order_update_callback_set(inst->order, _bar_order_update, inst);
        _bar_fill(inst);
     }
}

EINTERN void
bar_config_updated(Instance *inst)
{
   const char *drop[] = { "enlightenment/desktop", "enlightenment/border", "text/uri-list" };

   if (!inst->iconify_provider && inst->cfg->type != E_LUNCHER_MODULE_LAUNCH_ONLY)
     {
        inst->iconify_provider = e_comp_object_effect_mover_add(80, "e,action,*iconify",
            _bar_iconify_start, inst);
     }
   else if (inst->iconify_provider && inst->cfg->type == E_LUNCHER_MODULE_LAUNCH_ONLY)
     {
        E_FREE_FUNC(inst->iconify_provider, e_comp_object_effect_mover_del);
     }

   bar_reorder(inst);

   if (!inst->drop_handler && inst->cfg->type != E_LUNCHER_MODULE_TASKS_ONLY)
     {
        inst->drop_handler =
          e_gadget_drop_handler_add(inst->o_main, inst,
                             _bar_drop_enter, _bar_drop_move,
                             _bar_drop_leave, _bar_drop_drop,
                             drop, 3);
        elm_layout_content_set(inst->o_main, "e.swallow.drop", inst->drop_handler);
        evas_object_show(inst->drop_handler);
     }
   else if (inst->drop_handler && inst->cfg->type == E_LUNCHER_MODULE_TASKS_ONLY)
     {
        E_FREE_FUNC(inst->drop_handler, evas_object_del);
     }
}

EINTERN Evas_Object *
bar_create(Evas_Object *parent, int *id, E_Gadget_Site_Orient orient EINA_UNUSED)
{
   Instance *inst;

   if (e_gadget_site_is_desklock(parent)) return NULL;
   inst = E_NEW(Instance, 1);
   inst->size = 0;
   inst->resize_job = NULL;
   inst->cfg = _conf_item_get(id);
   *id = inst->cfg->id;
   inst->inside = EINA_FALSE;
   inst->bar = EINA_TRUE;
   inst->main_del = EINA_FALSE;
   inst->icons_desktop_hash = eina_hash_string_superfast_new(NULL);
   inst->icons_clients_hash = eina_hash_pointer_new(NULL);

   if (!inst->cfg->preview_size)
     inst->cfg->preview_size = 64;

   inst->o_main = elm_layout_add(parent);
   e_theme_edje_object_set(inst->o_main, "e/gadget/luncher/bar",
       "e/gadget/luncher/bar");
   edje_object_update_hints_set(elm_layout_edje_get(inst->o_main), EINA_TRUE);
   evas_object_event_callback_add(inst->o_main, EVAS_CALLBACK_MOUSE_OUT, _bar_mouse_out, inst);
   evas_object_event_callback_add(inst->o_main, EVAS_CALLBACK_DEL, _bar_del, inst);
   evas_object_smart_callback_add(parent, "gadget_created", _bar_created_cb, inst);
   evas_object_smart_callback_add(parent, "gadget_site_anchor", _bar_anchor_changed_cb, inst);
   evas_object_smart_callback_add(parent, "gadget_removed", _bar_removed_cb, inst);
   evas_object_show(inst->o_main);

   if (!eina_list_count(handlers))
     {
        E_LIST_HANDLER_APPEND(handlers, E_EVENT_CONFIG_ICON_THEME,
                              _bar_cb_update_icons, NULL);
        E_LIST_HANDLER_APPEND(handlers, EFREET_EVENT_ICON_CACHE_UPDATE,
                              _bar_cb_update_icons, NULL);
        E_LIST_HANDLER_APPEND(handlers, E_EVENT_EXEC_NEW,
                              _bar_cb_exec_new, NULL);
        E_LIST_HANDLER_APPEND(handlers, E_EVENT_EXEC_NEW_CLIENT,
                              _bar_cb_exec_new, NULL);
        E_LIST_HANDLER_APPEND(handlers, E_EVENT_CLIENT_PROPERTY,
                              _bar_cb_exec_client_prop, NULL);
        E_LIST_HANDLER_APPEND(handlers, E_EVENT_EXEC_DEL,
                              _bar_cb_exec_del, NULL);
        E_LIST_HANDLER_APPEND(handlers, E_EVENT_CLIENT_REMOVE,
                              _bar_cb_client_remove, NULL);
        E_LIST_HANDLER_APPEND(handlers, E_EVENT_DESK_SHOW,
                              _bar_cb_desk_switch, NULL);
     }
   if (inst->cfg->id < 0) return inst->o_main;
   luncher_instances = eina_list_append(luncher_instances, inst);

   inst->current_preview = NULL;
   inst->current_preview_menu = EINA_FALSE;

   return inst->o_main;
}

