#include "e_mod_main.h"
#include <X11/Xlib.h>

static int            _border_check          (Ngi_Box *box, E_Border *bd);
static Ngi_Item_Taskbar *_border_find        (Ngi_Box *box, E_Border *bd);

static Eina_Bool      _cb_border_event       (void *data, int type, void *event);
static Eina_Bool      _cb_desk_show          (void *data, int type, void *event);

static void           _item_new              (Ngi_Box *box, E_Border *bd);
static void           _item_cb_free          (Ngi_Item *it);
static void           _item_set_icon         (Ngi_Item_Taskbar *it);
static void           _item_set_label        (Ngi_Item_Taskbar *it);

static void           _item_cb_mouse_down    (Ngi_Item *it, Ecore_Event_Mouse_Button *ev);
static void           _item_cb_mouse_up      (Ngi_Item *it, Ecore_Event_Mouse_Button *ev);
static void           _item_cb_mouse_in      (Ngi_Item *it);
static void           _item_cb_mouse_out     (Ngi_Item *it);
static void           _item_cb_drag_start    (Ngi_Item *it);
static void           _item_cb_drag_end      (E_Drag *drag, int dropped);

static Eina_Bool      _cb_show_window        (void *data);
static void           _cb_drop_enter         (void *data, const char *type, void *event_info);
static void           _cb_drop_move          (void *data, const char *type, void *event_info);
static void           _cb_drop_end           (void *data, const char *type, void *event_info);
static void           _cb_drop_leave         (void *data, const char *type, void *event_info);


static Ecore_X_Atom ECOMORPH_ATOM_THUMBNAIL = 0;

void
ngi_taskbar_init(void)
{
   ECOMORPH_ATOM_THUMBNAIL = ecore_x_atom_get("__ECOMORPH_THUMBNAIL");
}

void
ngi_taskbar_new(Ng *ng, Config_Box *cfg)
{
   Ngi_Box *box = ngi_box_new(ng);
   E_Border *bd;
   E_Border_List *bl;
   Ecore_Event_Handler *h;
   
   if (!box) return;

   box->cfg = cfg;
   cfg->box = box;

   // "enlightenment/border", "enlightenment/desktop",
   const char *drop[] = {"text/uri-list","text/x-moz-url", "enlightenment/x-file"};
   box->drop_handler = e_drop_handler_add(ng->win->drop_win, box,
                                          _cb_drop_enter, _cb_drop_move,
                                          _cb_drop_leave, _cb_drop_end,
                                          drop, 3, 0, 0, 0, 0);

#define HANDLE(_event, _cb)						\
   box->handlers = eina_list_append(box->handlers, ecore_event_handler_add(_event, _cb, box));

   HANDLE(E_EVENT_BORDER_ADD,           _cb_border_event);
   HANDLE(E_EVENT_BORDER_REMOVE,        _cb_border_event);
   HANDLE(E_EVENT_BORDER_ICONIFY,       _cb_border_event);
   HANDLE(E_EVENT_BORDER_UNICONIFY,     _cb_border_event);
   HANDLE(E_EVENT_BORDER_ICON_CHANGE,   _cb_border_event);
   HANDLE(E_EVENT_BORDER_PROPERTY,      _cb_border_event);
   HANDLE(E_EVENT_BORDER_ZONE_SET,      _cb_border_event);
   HANDLE(E_EVENT_BORDER_URGENT_CHANGE, _cb_border_event);
   HANDLE(E_EVENT_BORDER_FOCUS_IN,      _cb_border_event);
   HANDLE(E_EVENT_BORDER_FOCUS_OUT,     _cb_border_event);
   HANDLE(E_EVENT_DESK_SHOW,            _cb_desk_show);
#undef HANDLE
   
   bl = e_container_border_list_first(box->ng->zone->container);

   while ((bd = e_container_border_list_next(bl)))
     {
        if (box->ng->zone == bd->zone)
	  _item_new(box, bd);
     }

   e_container_border_list_free(bl);
}

void
ngi_taskbar_remove(Ngi_Box *box)
{
   Ecore_Event_Handler *h;
   
   e_drop_handler_del(box->drop_handler);

   EINA_LIST_FREE(box->handlers, h)
     ecore_event_handler_del(h);

   ngi_box_free(box);
}

static int
_border_check(Ngi_Box *box, E_Border *bd)
{
   if (box->ng->zone != bd->zone)
      return 0;
   
   if (box->cfg->taskbar_show_iconified == 0 && bd->iconic)
      return 0;

   if (box->cfg->taskbar_show_iconified == 2 && !bd->iconic)
      return 0;

   if ((box->cfg->taskbar_skip_dialogs && bd->client.netwm.type == ECORE_X_WINDOW_TYPE_DIALOG)
       || (bd->user_skip_winlist)
       || (bd->client.netwm.state.skip_taskbar))
      return 0;

   /* XXX Xitk menu windows act like normal windows... */
   if (bd->client.icccm.transient_for &&
       bd->client.icccm.class &&
       !strcmp(bd->client.icccm.class, "Xitk"))
      return 0;

   return 1;
}

static Ngi_Item_Taskbar *
_border_find(Ngi_Box *box, E_Border *bd)
{
   Eina_List *l;
   Ngi_Item_Taskbar *it;

   EINA_LIST_FOREACH(box->items, l, it)
   if (it->border == bd) return it;

   return NULL;
}

/* *************************  DND  CALLBACKS  ************************** */

static void
_cb_drop_enter(void *data, const char *type, void *event_info)
{
   E_Event_Dnd_Enter *ev = (E_Event_Dnd_Enter *)event_info;
   Ngi_Box *box = (Ngi_Box *)data;
   Ng *ng = box->ng;

   ng->pos = (ng->horizontal ? ev->x : ev->y);

   ng->item_active = ngi_item_at_position_get(ng);

   if (!ng->item_active)
      return;  /* FIXME set edge_in before ?*/

   if (box->dnd_timer)
      ecore_timer_del(box->dnd_timer);

   box->dnd_timer = ecore_timer_add(0.5, _cb_show_window, ng->item_active);

   ng->dnd = 1;
   ngi_mouse_in(ng);
}

static void
_cb_drop_leave(void *data, const char *type, void *event_info)
{
   Ngi_Box *box = (Ngi_Box *)data;
   Ng *ng = box->ng;

   if (box->dnd_timer)
     {
	ecore_timer_del(box->dnd_timer);
	box->dnd_timer = NULL;
     }

   ng->item_active = NULL;
   ngi_mouse_out(ng);
}

static void
_cb_drop_move(void *data, const char *type, void *event_info)
{
   E_Event_Dnd_Move *ev = (E_Event_Dnd_Move *)event_info;
   Ngi_Box *box = (Ngi_Box *)data;
   Ng *ng = box->ng;
   Ngi_Item *it;

   ng->pos = (ng->horizontal ? ev->x : ev->y);

   it = ngi_item_at_position_get(ng);

   if (!it || !ng->item_active)
      return;

   if (ng->item_active != it)
     {
        if (box->dnd_timer)
           ecore_timer_del(box->dnd_timer);
	box->dnd_timer = NULL;

	if (it)
	  {
	     ngi_item_activate(ng);

	     box->dnd_timer = ecore_timer_add(0.5, _cb_show_window, it);
	  }
     }

   ngi_animate(ng);
}

static void
_cb_drop_end(void *data, const char *type, void *event_info)
{
   Ngi_Box *box = (Ngi_Box *)data;
   Ng *ng = box->ng;

   if (!ng->item_active)
      return;

   if (box->dnd_timer)
     {
	ecore_timer_del(box->dnd_timer);
	box->dnd_timer = NULL;
     }

   ng->item_active = NULL;
   ngi_mouse_out(ng);
}

static Eina_Bool
_cb_show_window(void *data)
{
   Ngi_Item_Taskbar *it = (Ngi_Item_Taskbar*) data;
   E_Desk *desk;

   if (!it) return EINA_FALSE;

   desk = e_desk_current_get(it->border->zone);

   if (desk != it->border->desk && !it->border->sticky)
     {
        e_desk_show(it->border->desk);
     }

   if (it->border->shaded)
     {
        e_border_unshade(it->border, E_DIRECTION_UP);
     }

   if (!it->border->visible)
     {
        e_border_show(it->border);
     }

   if (it->border->iconic)
     {
        e_border_uniconify(it->border);
     }
   else
     {
        e_border_raise(it->border);
     }

   e_border_focus_set(it->border, 1, 1);

   it->base.box->dnd_timer = NULL;

   return EINA_FALSE;
}

/* ************************** BORDER CALLBACKS ************************* */

static Eina_Bool
_cb_border_event(void *data, int type, void *event)
{
   E_Event_Border_Add *ev = event;
   Ngi_Box *box = data;
   Ngi_Item *it;
   E_Border *bd = ev->border;

   /* XXX workaround for e sending event after the
    * border_remove event */
   /* if (bd->already_unparented)
    *   return ECORE_CALLBACK_PASS_ON; */

   it = (Ngi_Item*)_border_find(box, bd);
   
   if (type == E_EVENT_BORDER_FOCUS_IN)
     {
	if (it) ngi_item_signal_emit(it, "e,state,taskbar,focus,on");
     }
   else if (type == E_EVENT_BORDER_FOCUS_OUT)
     {
	if (it) ngi_item_signal_emit(it, "e,state,taskbar,focus,off");
     }
   else if (type == E_EVENT_BORDER_ICONIFY)
     {
	if (it) ngi_item_signal_emit(it, "e,state,taskbar,iconic,on");
     }
   else if (type == E_EVENT_BORDER_UNICONIFY)
     {
	if (it) ngi_item_signal_emit(it, "e,state,taskbar,iconic,off");
     }
   else if (type == E_EVENT_BORDER_ICON_CHANGE)
     {
	if (it) _item_set_icon((Ngi_Item_Taskbar *)it);
     }
   else if (type == E_EVENT_BORDER_ADD)
     {
	_item_new(box, bd);
     }
   else if (type == E_EVENT_BORDER_REMOVE)
     {
	if (it) ngi_item_remove(it);
     }
   else if (type == E_EVENT_BORDER_ZONE_SET)
     {
	if (box->ng->zone == bd->zone)
	  _item_new(box, bd);
	else if (it)
	  ngi_item_remove(it);
     }
   else if ((type == E_EVENT_BORDER_URGENT_CHANGE) && (it))
     {
	 if (bd->client.icccm.urgent)
	   {
	      ((Ngi_Item_Taskbar *)it)->urgent = 1;
	      ngi_item_signal_emit(it, "e,state,taskbar,urgent,on");

	      if (box->ng->cfg->autohide_show_urgent)
		{
		   ngi_bar_lock(box->ng, 1);
		   ngi_animate(box->ng);
		}
	   }
	 else
	   {
	      ((Ngi_Item_Taskbar *)it)->urgent = 0;
	      ngi_item_signal_emit(it, "e,state,taskbar,urgent,off");

	      if (box->ng->cfg->autohide_show_urgent)
		{
		   ngi_bar_lock(box->ng, 0);
		   ngi_animate(box->ng);
		}
	   }
     }
   else if (type == E_EVENT_BORDER_PROPERTY)
     {
	if (it)
	  {
	     if (!_border_check(box, bd))
	       ngi_item_remove(it);
	     else
	       _item_set_label((Ngi_Item_Taskbar *)it);
	  }
	else
	  {
	     _item_new(box, ev->border);
	  }
     }

   return ECORE_CALLBACK_PASS_ON;
}

static Eina_Bool
_cb_desk_show(void *data, int type, void *event)
{
   E_Event_Desk_Show *ev = event;
   Ngi_Box *box = data;
   Ngi_Item_Taskbar *it;
   Eina_List *l;

   if (ev->desk->zone != box->ng->zone)
     return ECORE_CALLBACK_PASS_ON;

   if (!box->cfg->taskbar_show_desktop)
     return ECORE_CALLBACK_PASS_ON;
   
   EINA_LIST_FOREACH(box->items, l, it)
     {
	if ((it->border->desk == ev->desk) || (it->border->sticky))
	  {
	     evas_object_show(it->base.obj);
	     evas_object_show(it->base.over);	     
	     it->base.scale = 1.0;
	  }
	else
	  {
	     evas_object_hide(it->base.obj);
	     evas_object_hide(it->base.over);
	     it->base.scale = 0.0;
	  }
     }
   
   ngi_thaw(box->ng);

   return ECORE_CALLBACK_PASS_ON;
}

/* ***************************  TASKBAR  ITEM  *************************** */

static void
_item_new(Ngi_Box *box, E_Border *bd)
{
   Ngi_Item_Taskbar *it, *l_it = NULL, *ll_it = NULL;
   Ngi_Item *item;
   Eina_List *l;

   if (!_border_check(box, bd))
      return;

   if (_border_find(box, bd))
      return;

   it = E_NEW(Ngi_Item_Taskbar, 1);
   it->base.box = box;
   item = (Ngi_Item*) it;
   ngi_item_init_defaults(item);

   e_object_ref(E_OBJECT(bd));
   it->border = bd;

   it->base.cb_free       = _item_cb_free;
   it->base.cb_mouse_in   = _item_cb_mouse_in;
   it->base.cb_mouse_out  = _item_cb_mouse_out;
   it->base.cb_mouse_down = _item_cb_mouse_down;
   it->base.cb_mouse_up   = _item_cb_mouse_up;
   it->base.cb_drag_start = _item_cb_drag_start;

   _item_set_icon(it);
   _item_set_label(it);

   if (box->cfg->taskbar_group_apps && (bd->client.icccm.class && bd->client.icccm.class[0]))
     {
	it->class = eina_stringshare_add(bd->client.icccm.class);

	EINA_LIST_FOREACH (box->items, l, l_it)
	  {
	     if (box->cfg->taskbar_append_right)
	       {
		  if (ll_it && l_it->class && ll_it->class &&
		      ll_it->class == it->class &&
		      l_it->class != it->class)
		    break;
	       }
	     else
	       {
		  if (l_it->class && l_it->class == it->class)
		    break;

		  ll_it = l_it;
		  l_it = NULL;
	       }
	  }
     }

   if (l_it)
     {
        if (box->cfg->taskbar_append_right)
           box->items = eina_list_append_relative(box->items, it, l_it);
        else
           box->items = eina_list_prepend_relative(box->items, it, l_it);
     }
   else
     {
        if (box->cfg->taskbar_append_right)
           box->items = eina_list_append(box->items, it);
        else
           box->items = eina_list_prepend(box->items, it);
     }

   if ((box->cfg->taskbar_show_desktop) &&
       (bd->desk != e_desk_current_get(box->ng->zone)) &&
       (!bd->sticky))
     {
	ngi_item_show(item, 1);
	item->scale = 0.0;
     }
   else
     {
	ngi_item_show(item, 0);
     }
   
   if (bd->iconic)
     ngi_item_signal_emit(item, "e,state,taskbar,iconic,on");

   it->urgent = bd->client.icccm.urgent;
   if (it->urgent)
     ngi_item_signal_emit(item, "e,state,taskbar,urgent,on");
}

static void
_item_cb_free(Ngi_Item *item)
{
   Ngi_Item_Taskbar *it = (Ngi_Item_Taskbar *) item;

   if (it->urgent)
     ngi_bar_lock(item->box->ng, 0);

   if (it->border)
     e_object_unref(E_OBJECT(it->border));

   if (it->o_icon)
     evas_object_del(it->o_icon);

   if (it->o_proxy)
     edje_object_part_unswallow(it->base.obj, it->o_proxy);

   E_FREE(it);
}

Evas_Object *
_border_icon_add(E_Border *bd, Evas *evas)
{
   Evas_Object *o;

   E_OBJECT_CHECK_RETURN(bd, NULL);
   E_OBJECT_TYPE_CHECK_RETURN(bd, E_BORDER_TYPE, NULL);

   o = NULL;

   if (bd->internal)
     {
        o = edje_object_add(evas);
        if (!bd->internal_icon)
          e_util_edje_icon_set(o, "enlightenment");
        else
          {
             if (!bd->internal_icon_key)
               {
                  char *ext;

                  ext = strrchr(bd->internal_icon, '.');
                  if ((ext) && ((!strcmp(ext, ".edj"))))
                    {
                       if (!edje_object_file_set(o, bd->internal_icon, "icon"))
                         e_util_edje_icon_set(o, "enlightenment");
                    }
                  else if (ext)
                    {
                       evas_object_del(o);
                       o = e_icon_add(evas);
                       e_icon_file_set(o, bd->internal_icon);
                    }
                  else
                    {
                       evas_object_del(o);
                       o = e_icon_add(evas);
		       e_icon_scale_size_set(o, 128); 
                       if (!e_util_icon_theme_set(o, bd->internal_icon))
                         e_util_icon_theme_set(o, "enlightenment");
                    }
               }
             else
               {
                  edje_object_file_set(o, bd->internal_icon,
                                       bd->internal_icon_key);
               }
          }
        return o;
     }
   if ((bd->client.netwm.icons) &&
       ((e_config->use_app_icon) ||
        (bd->remember && (bd->remember->prop.icon_preference == E_ICON_PREF_NETWM))))
     {
        int i, size, tmp, found = 0;
        o = e_icon_add(evas);

        size = bd->client.netwm.icons[0].width;

        for (i = 1; i < bd->client.netwm.num_icons; i++)
          {
             if ((tmp = bd->client.netwm.icons[i].width) > size)
               {
                  size = tmp;
                  found = i;
               }
          }

        e_icon_data_set(o, bd->client.netwm.icons[found].data,
                        bd->client.netwm.icons[found].width,
                        bd->client.netwm.icons[found].height);
        e_icon_alpha_set(o, 1);

        return o;
     }

   if (bd->desktop)
     {
        o = e_util_desktop_icon_add(bd->desktop, 128, evas);
	if (o) return o;
     }

   if (bd->client.netwm.icons)
     {
        int i, size, tmp, found = 0;
        o = e_icon_add(evas);

        size = bd->client.netwm.icons[0].width;

        for (i = 1; i < bd->client.netwm.num_icons; i++)
          {
             if ((tmp = bd->client.netwm.icons[i].width) > size)
               {
                  size = tmp;
                  found = i;
               }
          }

        e_icon_data_set(o, bd->client.netwm.icons[found].data,
                        bd->client.netwm.icons[found].width,
                        bd->client.netwm.icons[found].height);
        e_icon_alpha_set(o, 1);
        return o;
     }

   o = e_border_icon_add(bd, evas);
   if (o) return o;

   o = edje_object_add(evas);
   e_util_edje_icon_set(o, "enlightenment/unknown");

   return o;
}

static void
_item_set_icon(Ngi_Item_Taskbar *it)
{
   Evas_Object *o, *oo;
   Evas *e = it->base.box->ng->evas;

   if (it->o_icon)
     {
	edje_object_part_unswallow(it->base.obj, it->o_icon);
	evas_object_del(it->o_icon);
     }
   if (it->o_proxy)
     {
	edje_object_part_unswallow(it->base.obj, it->o_proxy);
	evas_object_del(it->o_proxy);
     }

   o = _border_icon_add(it->border, e);
   edje_object_part_swallow(it->base.obj, "e.swallow.content", o);
   evas_object_show(o);
   it->o_icon = o;

   oo = evas_object_image_filled_add(e);
   evas_object_image_fill_set(oo, 0, 0, 1, 1);
   evas_object_image_source_set(oo, it->base.obj);
   edje_object_part_swallow(it->base.over, "e.swallow.content", oo);
   evas_object_show(oo);
   it->o_proxy = oo;

   if (it->border->iconic)
      ngi_item_signal_emit((Ngi_Item*)it, "e,state,taskbar,iconic,on");
   if (it->urgent)
     ngi_item_signal_emit((Ngi_Item*)it, "e,state,taskbar,urgent,on");
}

static void
_item_set_label(Ngi_Item_Taskbar *it)
{
   const char *title;
   unsigned int max_len = 36;

   title = e_border_name_get(it->border);

   if ((!title) || (!title[0]))
      title = D_("No name!");

   if (strlen(title) > max_len)
     {
        char *abbv;
        const char *left, *right;
   
        abbv = E_NEW(char, E_CLIENTLIST_MAX_CAPTION_LEN + 4);
        left = title;
        right = title + (strlen(title) - (max_len / 2));
   
        strncpy(abbv, left, max_len / 2);
        strncat(abbv, "...", 3);
        strncat(abbv, right, max_len / 2);
   
   	ngi_item_label_set((Ngi_Item*)it, abbv);
	E_FREE(abbv);
	
   	return;
     }

   ngi_item_label_set((Ngi_Item*)it, title);
}

/* **************************  ITEM  CALLBACKS  ************************** */

static void
_item_cb_mouse_down(Ngi_Item *item, Ecore_Event_Mouse_Button *ev)
{
   Ngi_Item_Taskbar *it = (Ngi_Item_Taskbar *) item;
   Evas_Coord x, y, w, h;
   E_Border *bd;
   Ng *ng;

   ng = item->box->ng;
   bd = it->border;

   if (ev->buttons == 3 && bd)
     {
        int dir = E_MENU_POP_DIRECTION_AUTO;
        evas_object_geometry_get(item->obj, &x, &y, &w, &h);

        x += ng->win->popup->x + ng->zone->x;
        y += ng->win->popup->y + ng->zone->y;

        switch(ng->cfg->orient)
          {
           case E_GADCON_ORIENT_TOP:
              dir = E_MENU_POP_DIRECTION_DOWN;
              y += h;
              break;

           case E_GADCON_ORIENT_BOTTOM:
              dir = E_MENU_POP_DIRECTION_UP;
              break;

           case E_GADCON_ORIENT_LEFT:
              dir = E_MENU_POP_DIRECTION_RIGHT;
              x += w;
              break;

           case E_GADCON_ORIENT_RIGHT:
              dir = E_MENU_POP_DIRECTION_LEFT;
              break;
          } /* switch */
        ngi_border_menu_show(item->box, it->border, x, y, dir, ev->timestamp);
     }
   else if (ev->buttons == 1 && ev->double_click && !it->border->iconic)
     {
        e_border_iconify(it->border);
     }
   else
      item->mouse_down = 1;
}

static void
_item_cb_mouse_up(Ngi_Item *item, Ecore_Event_Mouse_Button *ev)
{
   Ngi_Item_Taskbar *it = (Ngi_Item_Taskbar *) item;
   E_Border *bd = it->border;

   if (!item->mouse_down)
      return;

   item->mouse_down = 0;

   if (it->urgent)
     {
        it->urgent = 0;

	ngi_item_signal_emit((Ngi_Item*)it, "e,state,taskbar,urgent,off");
	ngi_bar_lock(item->box->ng, 0);
     }

   if (ev->buttons != 1)
     return;

   if (bd->shaded)
     e_border_unshade(bd, E_DIRECTION_UP);

   if (bd->shaded)
     e_border_unshade(bd, E_DIRECTION_UP);

   if ((e_desk_current_get(bd->zone) != bd->desk) && (!bd->sticky))
     e_desk_show(bd->desk);

   if (bd->iconic)
     {
	e_border_uniconify(bd);

	if (it->border_was_fullscreen)
	  e_border_fullscreen(bd, (E_Fullscreen)e_config->fullscreen_policy);

	it->border_was_fullscreen = 0;
     }
   else
     {
	e_border_raise(bd);
     }
   if (bd->focused)
     {
	char buf[1024];
	E_Action *act = e_action_find("scale-windows");

	if (!act) return;
	if (!bd->client.icccm.class) return;
	  
	snprintf(buf, 1024, "go_scale_class:%s", bd->client.icccm.class);
	act->func.go(NULL, buf);
     }
   else
     e_border_focus_set(bd, 1, 1);
}


static void
_item_cb_drag_del(void *data, Evas *e, Evas_Object *obj, void *event_info)
{
   Ng *ng = data;

   ngi_bar_lock(ng, 0);
   ngi_thaw(ng);
}

static void
_item_cb_drag_start(Ngi_Item *item)
{
   Ngi_Item_Taskbar *it = (Ngi_Item_Taskbar *) item;
   E_Drag *d;
   Evas_Object *o;
   Evas_Coord x, y, w, h, px, py;
   Ng *ng = item->box->ng;

   if (!it->border)
      return;

   edje_object_signal_emit(ng->o_label, "e,state,label,hide", "e");

   evas_object_geometry_get(it->o_icon, &x, &y, &w, &h);

   if (!ngi_config->use_composite)
     {
	x -= ng->win->rect.x;
	y -= ng->win->rect.y;
     }

   const char *drag_types[] = { "enlightenment/border" };

   d = e_drag_new(ng->zone->container, x, y, drag_types, 1,
                  it->border, -1, NULL, _item_cb_drag_end);

   o = _border_icon_add(it->border,e_drag_evas_get(d));
   e_object_ref(E_OBJECT(it->border));
   e_drag_object_set(d, o);
   e_drag_resize(d, w, h);
   evas_object_event_callback_add(o, EVAS_CALLBACK_DEL, _item_cb_drag_del, ng);

   ecore_x_pointer_xy_get(ng->win->input, &px, &py);
   e_drag_start(d, px, py);

   ngi_bar_lock(ng, 1);
}

static void
_item_cb_drag_end(E_Drag *drag, int dropped)
{
   E_Border *bd = (E_Border *)drag->data;

   if (dropped)
     {
        e_object_unref(E_OBJECT(bd));
        return;
     }

   E_Desk *desk = e_desk_current_get(bd->zone);
   int w = bd->zone->w;
   int h = bd->zone->h;

   if (desk != bd->desk)
      e_border_desk_set(bd, desk);

   if ((bd->w < w) && (bd->h < w))
     {
	int dx = abs(drag->x - w/2);
	int dy = abs(drag->y - h/2);

	if (sqrt(dx*dx + dy*dy) < w/8)
	  {
	     drag->x = w/2;
	     drag->y = h/2;
	  }

	int x = drag->x - bd->w/2;

	if (bd->w < w - 50)
	  {
	     if (x < 50) x = 50;
	     if (x + bd->w > w) x = w - bd->w - 50;
	  }
	else x = bd->x;

	int y = drag->y - bd->h/2;

	if (bd->h < h - 50)
	  {
	     if (y < 50) y = 50;
	     if (y + bd->h > h) y = h - bd->h - 50;
	  }
	else y = bd->y;

	e_border_move(bd, x, y);
   }

   if (bd->iconic)
      e_border_uniconify(bd);
   else
      e_border_raise(bd);

   if (bd->shaded)
      e_border_unshade(bd, E_DIRECTION_UP);

   e_object_unref(E_OBJECT(bd));
}

static Eina_Bool
_cb_preview_hide(void *data)
{
   Ng *ng = data;

   ng->effect_timer = NULL;

   ecore_x_client_message32_send(e_manager_current_get()->root,
                                 ECORE_X_ATOM_NET_WM_ICON_GEOMETRY,
                                 SubstructureNotifyMask,
                                 0, 0, 0, 0, 0);
   return EINA_FALSE;
}

static void
_item_cb_mouse_in(Ngi_Item *item)
{
   Ngi_Item_Taskbar *it = (Ngi_Item_Taskbar *) item;

   Ng *ng = item->box->ng;

   if (ng->cfg->ecomorph_features)
     {
        if (ng->effect_timer)
           ecore_timer_del(ng->effect_timer);

        ng->effect_timer = NULL;

        ecore_x_client_message32_send(e_manager_current_get()->root,
                                      ECORE_X_ATOM_NET_WM_ICON_GEOMETRY,
                                      SubstructureNotifyMask,
                                      it->border->win, item->pos,
                                      (double)ng->size * item->scale,
                                      ng->cfg->orient,0);
     }
}

static void
_item_cb_mouse_out(Ngi_Item *it)
{
   Ng *ng = it->box->ng;
   if (ng->cfg->ecomorph_features)
     {
        if (ng->effect_timer)
           ecore_timer_del(ng->effect_timer);

        ng->effect_timer = ecore_timer_add(0.2, _cb_preview_hide, ng);
     }
}

