/* dock.c- built-in Dock module for WindowMaker
 * 
 *  WindowMaker window manager
 * 
 *  Copyright (c) 1997 Alfredo K. Kojima
 * 
 *  This program 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; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program 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 this program; if not, write to the Free Software
 *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */


#include "wconfig.h"

#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#include "generic/wwmlib.h"
#include "WindowMaker.h"
#include "wcore.h"
#include "window.h"
#include "pixmap.h"
#include "icon.h"
#include "appicon.h"
#include "actions.h"
#include "stacking.h"
#include "dock.h"
#include "dialog.h"
#include "funcs.h"
#include "properties.h"
#include "resource.h"

#ifdef SUPERFLUOUS
extern void DoKaboom();
#endif

/**** Global variables ****/
extern char *PixmapPath;
extern Cursor wCursor[WCUR_LAST];

extern WPreferences wPreferences;

extern WResource wResourceDefault;

extern WMethodTable wAppIconMethods;


static void iconMouseDown(WObjDescriptor *desc, XEvent *event);
static void iconDblClick(WObjDescriptor *desc, XEvent *event);

WMethodTable wDockIconMethods = {
    (WEventHandler)iconMouseDown,
    NULL,
    (WEventHandler)iconDblClick,
    NULL
};


static pid_t execCommand(char **argv, int argc);


static WAppIcon*
createDockIcon(WDock *dock, int y, char **argv, int argc, char *wm_class, 
	       char *wm_instance, int auto_launch)
{
    WScreen *scr = dock->screen_ptr;
    WAppIcon *btn, *tmp;

    btn = wAppIconCreateForCommand(scr, argv, argc, wm_class, wm_instance);
    btn->icon->core->descriptor.methods = &wDockIconMethods;
    btn->icon->core->descriptor.parent_type = WCLASS_DOCK_ICON;
    btn->icon->core->descriptor.parent = btn;
    if (dock->dock_level)
      ChangeStackingLevel(btn->icon->core, NSDockWindowLevel);
    else
      ChangeStackingLevel(btn->icon->core, NSNormalWindowLevel);
    XMapWindow(dpy, btn->icon->core->window);
    wRaiseFrame(btn->icon->core);
    btn->next = NULL;
    btn->prev = NULL;
    if (dock->on_right_side) {
	btn->x_pos = dock->x_pos = scr->scr_width - ICON_WIDTH
	  - DOCK_EXTRA_SPACE;
    } else {
	btn->x_pos = dock->x_pos = DOCK_EXTRA_SPACE;
    }

    btn->y_pos = (y*ICON_HEIGHT) + dock->y_pos;

    btn->running = 0;
    btn->docked = 1;
    btn->auto_launch = auto_launch;
    XMoveWindow(dpy, btn->icon->core->window, btn->x_pos, btn->y_pos);

    tmp = dock->first_icon;
    while (tmp) {
	if (btn->y_pos > tmp->y_pos) {
	    if (!tmp->next) {
		tmp->next = btn;
		btn->prev = tmp;
		btn->next = NULL;
		break;
	    } else if (tmp->next->y_pos >= btn->y_pos+ICON_HEIGHT) {
		tmp->next->prev = btn;
		btn->next = tmp->next;
		btn->prev = tmp;
		tmp->next = btn;
		break;
	    }
	}
	tmp = tmp->next;
    }
    dock->icon_count++;
    return btn;
}


WDock*
wDockCreate(WScreen *scr)
{
    WDock *dock;
    WAppIcon *btn;

    dock = wmalloc(sizeof(WDock));

    btn = wAppIconCreateForCommand(scr, NULL, 0, NULL, NULL);
    btn->icon->core->descriptor.methods = &wDockIconMethods;
    btn->icon->core->descriptor.parent_type = WCLASS_DOCK_ICON;
    btn->icon->core->descriptor.parent = btn;
    ChangeStackingLevel(btn->icon->core, NSDockWindowLevel);
    XMapWindow(dpy, btn->icon->core->window);
    wRaiseFrame(btn->icon->core);
    btn->next = NULL;
    btn->prev = NULL;
    dock->screen_ptr = scr;
    dock->icon_count = 1;
    dock->on_right_side = 1;
    dock->dock_level = 1;
    dock->first_icon = btn;
    btn->x_pos = dock->x_pos = scr->scr_width - ICON_WIDTH-DOCK_EXTRA_SPACE;
    btn->y_pos = dock->y_pos = 0;
    btn->running = 0;
    btn->docked = 1;
    XMoveWindow(dpy, btn->icon->core->window, btn->x_pos, btn->y_pos);

    return dock;
}


/*
 * wm_instance
 * wm_class 
 * vertical_position  argc  
 * argv[0]
 * argv[1]
 * ...
 * argv[argc-1]
 */
void
wDockSaveState(WDock *dock, char *path)
{
    FILE *file;
    WAppIcon *btn;
    int i;

    file = fopen(path, "w+");
    if (!file) {
	wWarning(_("could not open file \"%s\" to save Dock state"), path);
	return;
    }
    fprintf(file, "##Automatically generated by WindowMaker. Do not edit it\n");
    fprintf(file, "%i %i\n", dock->on_right_side, dock->dock_level);
    btn = dock->first_icon;
    while (btn) {
	if (btn->wm_instance)
	  fprintf(file, "%s\n", btn->wm_instance);
	else
	  fprintf(file, ".\n");

	if (btn->wm_class)
	  fprintf(file, "%s\n", btn->wm_class);
	else
	  fprintf(file, ".\n");
	
	fprintf(file, "%i %i %i %i\n", (btn->y_pos-dock->y_pos)/ICON_HEIGHT,
		btn->argc, btn->auto_launch, btn->forced_dock);
	for (i=0; i<btn->argc; i++) {
	    fprintf(file, "%s\n", btn->argv[i]);
	}
	btn = btn->next;
    }
    fclose(file);
}

void
wDockRestoreState(WDock *dock, char *path)
{
    FILE *file;
    int i, j, y, auto_launch, forced, count;
    int argc;
    char **argv;
    char line[MAXLINE];
    char *lin;
    char *wm_class=NULL, *wm_instance=NULL;

    file = fopen(path, "r");
    if (!file) {
	return;
    }
    fgets(line, MAXLINE, file);
    /* dirty kludge to find dockfile version */
    if (line[1]!=' ') {
	fgets(line, MAXLINE, file);
	if (sscanf(line, "%i %i", &i, &j)!=2) {
	    j = 1;
	}
	dock->on_right_side = i;
	dock->dock_level = j;
    }
    i=0;
    count = 0;
    while (!feof(file)) {
	if (!fgets(line, MAXLINE, file))
	  goto error;
	lin=cropline(line);
	if (lin[0]=='.' && lin[1]==0)
	  wm_instance=NULL;
	else
	  wm_instance=strdup(lin);

	if (!fgets(line, MAXLINE, file))
	  goto error;
	lin=cropline(line);
	if (lin[0]=='.' && lin[1]==0)
	  wm_class=NULL;
	else
	  wm_class=strdup(lin);
	
	forced = 0;
	if (!fgets(line, MAXLINE, file))
	  goto error;
	i=sscanf(line, "%i %i %i %i", &y, &argc, &auto_launch, &forced);
	if (i==2)
	  auto_launch = 0;
	else if (i<2)
	  goto error;

	if (argc==0)
	  argv=NULL;
	else {
	    argv = malloc(sizeof(char*)*argc);
	    if (!argv) 
	      goto error;
	    for (i=0; i<argc; i++) {
		if (!fgets(line, MAXLINE, file))
		  goto error;
		argv[i] = strdup(cropline(line));
	    }
	}
	if (y==0) {
	    if (wm_instance)
	      dock->first_icon->wm_instance = strdup(wm_instance);
	    else
	      dock->first_icon->wm_instance = NULL;
	    if (wm_class) 
	      dock->first_icon->wm_class = strdup(wm_class);
	    else
	      dock->first_icon->wm_class = NULL;
	    dock->first_icon->argc = argc;
	    dock->first_icon->argv = argv;
	    dock->first_icon->auto_launch = auto_launch;
	    dock->first_icon->forced_dock = forced;
	    if (!dock->on_right_side) {
		dock->first_icon->x_pos = dock->x_pos = DOCK_EXTRA_SPACE;
	    }
	    dock->first_icon->y_pos = dock->y_pos;
	    XMoveWindow(dpy, dock->first_icon->icon->core->window,
			dock->x_pos, dock->y_pos);
	    if (!dock->dock_level)
	      ChangeStackingLevel(dock->first_icon->icon->core, 
				  NSNormalWindowLevel);
	} else {
	    WAppIcon *icon;
	    
	    if (y<0)
	      y = count+1;
	    icon = createDockIcon(dock, y, argv, argc, wm_class, wm_instance,
				  auto_launch);
	    icon->forced_dock = forced;
	}
	if (wm_class)
	  free(wm_class);
	if (wm_instance)
	  free(wm_instance);
	wm_class=NULL;
	wm_instance=NULL;
	count=y;
    }
    fclose(file);    
    return;

    error:
    if (wm_class)
      free(wm_class);
    if (wm_instance)
      free(wm_instance);
    fclose(file);
}


void
wDockDoAutoLaunch(WDock *dock)
{
    WAppIcon *btn = dock->first_icon;

    while (btn) {
	if (btn->auto_launch && btn->argv && !btn->running) {
	    btn->pid = execCommand(btn->argv, btn->argc);
	    if (!btn->forced_dock) {
		btn->launching = 1;
		wAppIconPaint(btn);
	    }
	}
	btn=btn->next;
    }
}


void
wDockAttachIcon(WDock *dock, WAppIcon *icon)
{
    WAppIcon *btn = dock->first_icon;
    WWindow *wwin;
    char **argv;
    int argc;

    while (btn) {
	if (icon->y_pos > btn->y_pos) {
	    if (!btn->next) {
		btn->next = icon;
		icon->prev = btn;
		icon->next = NULL;
		break;
	    } else if (btn->next->y_pos >= icon->y_pos+ICON_HEIGHT) {
		btn->next->prev = icon;
		icon->next = btn->next;
		icon->prev = btn;
		btn->next = icon;
		break;
	    }
	}
	btn = btn->next;
    }
    dock->icon_count++;
    icon->running = 1;
    icon->launching = 0;
    icon->docked = 1;
    icon->icon->core->descriptor.methods = &wDockIconMethods;
    icon->icon->core->descriptor.parent_type = WCLASS_DOCK_ICON;
    icon->icon->core->descriptor.parent = icon;
    if (dock->dock_level)
      ChangeStackingLevel(icon->icon->core, NSDockWindowLevel);
    else {
	ChangeStackingLevel(icon->icon->core, NSNormalWindowLevel);
	if (icon->prev) {
	    Window win[2];
	    win[0] = icon->prev->icon->core->window;
	    win[1] = icon->icon->core->window;
	    XRestackWindows(dpy, win, 2);
	}
    }
    wAppIconPaint(icon);

    wwin = icon->icon->owner;
    if (wwin->wm_class)
      icon->wm_class = strdup(wwin->wm_class);
    else
      icon->wm_class = NULL;
    if (wwin->wm_instance)
      icon->wm_instance = strdup(wwin->wm_instance);
    else
      icon->wm_instance = NULL;
    
    XGetCommand(dpy, wwin->client_win, &argv, &argc);
    icon->argv = argv;
    icon->argc = argc;
}


void
reattachIcon(WDock *dock, WAppIcon *icon)
{
    WAppIcon *btn = dock->first_icon;
    
    if (icon->prev)
      icon->prev->next = icon->next;
    if (icon->next)
      icon->next->prev = icon->prev;

    while (btn) {
	if (icon->y_pos > btn->y_pos) {
	    if (!btn->next) {
		btn->next = icon;
		icon->prev = btn;
		icon->next = NULL;
		break;
	    } else if (btn->next->y_pos >= icon->y_pos+ICON_HEIGHT) {
		btn->next->prev = icon;
		icon->next = btn->next;
		icon->prev = btn;
		btn->next = icon;
		break;
	    }
	}
	btn = btn->next;
    }
}


void
wDockDetach(WDock *dock, WAppIcon *icon)
{
    if (icon->prev)
      icon->prev->next = icon->next;
    if (icon->next)
      icon->next->prev = icon->prev;
    
    icon->next = NULL;
    icon->prev = NULL;
    icon->docked = 0;
    
    if (icon->argv) {
	XFreeStringList(icon->argv);
	icon->argv = NULL;
	icon->argc = 0;
    }
    dock->icon_count--;
    
    /* if the dock is not attached to an application or
     * the the application did not set the approriate hints yet,
     * destroy the icon */
    if (!icon->running || !wApplicationOf(icon->main_window)) {
	wAppIconDestroy(icon);
    } else {
	icon->icon->core->descriptor.methods = &wAppIconMethods;
	icon->icon->core->descriptor.parent_type = WCLASS_APPICON;
	icon->icon->core->descriptor.parent = icon;
	ChangeStackingLevel(icon->icon->core, NSNormalWindowLevel);
	wAppIconPaint(icon);
    }
}


/*
 * returns the closest Dock slot position for the passed 
 * coordinates.
 * 
 * Returns False if icon can't be docked.
 */
int
wDockSnapIcon(WDock *dock, WAppIcon *icon, int req_x, int req_y, 
	      int *ret_x, int *ret_y, int redocking)
{
    int dy, dy2;
    WAppIcon *btn, *btn2;
    WScreen *scr = dock->screen_ptr;

    dy = dock->y_pos;
    
    /* if it's before or over the first icon or the dock is full */
    if (req_y + ICON_HEIGHT < dy
	|| (dock->icon_count+(redocking==0))*ICON_HEIGHT > scr->scr_height) {
	return False;
    }

    /* "round" position */
    if ((req_y-dy)%ICON_HEIGHT > ICON_HEIGHT/2) {
	req_y -= (req_y - dy)%ICON_HEIGHT - ICON_HEIGHT;
    } else {
	req_y -= (req_y - dy)%ICON_HEIGHT;
    }
    if (req_y <= dy)
      req_y = dy + ICON_HEIGHT;
    else if (req_y - dy > scr->scr_height - ICON_HEIGHT)
      req_y = scr->scr_height - ICON_HEIGHT;
    btn = dock->first_icon;
    while (btn) {
	if (btn->y_pos == req_y) {
	    if (btn == icon) {
		break;
	    }
	    /* the requested slot is already taken. Try to find
	     * a vacant one above or below it */

	    /* search below */
	    btn2 = btn->next;
	    dy = req_y + ICON_HEIGHT;
	    while (btn2) {
		if (btn2->y_pos == dy && btn2!=icon) {
		    dy += ICON_HEIGHT;
		    btn2 = btn2->next;
		} else {
		    break;
		}
	    }
	    /* search above */
	    if (btn!=dock->first_icon && abs(dy - req_y) > ICON_HEIGHT) {
		btn2 = btn->prev;
		dy2 = req_y - ICON_HEIGHT;
		while (btn2) {
		    if (btn2==dock->first_icon) {
			/* no more space above */
			dy2 = dy;
		    }
		    if (btn2->y_pos == dy2) {
			dy2 -= ICON_HEIGHT;
			btn2 = btn2->prev;
		    } else {
			break;
		    }
		}
		/* get the closest one */
		if (abs(dy - req_y) > abs(dy2 - req_y)) {
		    req_y = dy2;
		} else {
		    req_y = dy;
		}
	    } else {
		req_y = dy;
	    }
	    break;
	}
	btn = btn->next;
    }
    *ret_y = req_y;
    if (dock->on_right_side) {
      *ret_x = scr->scr_width - ICON_WIDTH - DOCK_EXTRA_SPACE;
    } else {
      *ret_x = DOCK_EXTRA_SPACE;
    }  
    return True;
}


static void
moveDock(WDock *dock, int new_y)
{
    WAppIcon *btn;
    int ofs;

    dock->y_pos = new_y;
    btn = dock->first_icon;
    ofs = new_y - btn->y_pos;
    while (btn) {
	btn->y_pos += ofs;
	XMoveWindow(dpy, btn->icon->core->window, btn->x_pos, btn->y_pos);
	btn = btn->next;
    }
}


static void
swapDock(WDock *dock)
{
   WScreen *scr = dock->screen_ptr;
   WAppIcon *btn;
   int x;
   btn = dock->first_icon;
   if (dock->on_right_side) {
     x = dock->x_pos = scr->scr_width - ICON_WIDTH - DOCK_EXTRA_SPACE;
   } else {
     x = dock->x_pos = DOCK_EXTRA_SPACE;
   }
   while (btn) {
        btn->x_pos = x;
        XMoveWindow(dpy, btn->icon->core->window, btn->x_pos, btn->y_pos);
        btn = btn->next;
   }
}


static pid_t
execCommand(char **argv, int argc)
{
    pid_t pid;

    if ((pid=fork())==0) {
	char **args;
	int i;
	
	args = malloc(sizeof(char*)*(argc+1));
	if (!args)
	  exit(111);
	for (i=0; i<argc; i++) {
	    args[i] = argv[i];
	}
	args[argc] = NULL;
	execvp(argv[0], args);
	exit(111);
    }
    return pid;
}


void
wDockFinishLaunch(WDock *dock, WAppIcon *icon)
{
    icon->launching = 0;
    icon->relaunching = 0;
    wAppIconPaint(icon);
}


WAppIcon*
wDockFindIconFor(WDock *dock, Window window)
{
    WAppIcon *icon;
    
    icon = dock->first_icon;
    while (icon) {
	if (icon->main_window == window)
	  return icon;
	icon=icon->next;
    }
    return NULL;
}


void
wDockTrackWindowLaunch(WDock *dock, Window window)
{
    WAppIcon *icon;
    char *wm_class, *wm_instance;
#ifdef notdef
    char **argv;
    int argc, i;
#endif

    if (!PropGetWMClass(window, &wm_class, &wm_instance) ||
	(!wm_class && !wm_instance))
      return;

    icon = dock->first_icon;
    while (icon) {
	if ((icon->wm_instance || icon->wm_class) 
	    && (icon->launching
		|| (dock->screen_ptr->flags.startup&&!icon->running))) {
	    
	    if (icon->wm_instance && wm_instance && 
		strcmp(icon->wm_instance, wm_instance)!=0) {
		goto next;
	    }
	    if (icon->wm_class && wm_class && 
		strcmp(icon->wm_class, wm_class)!=0) {
		goto next;
	    }
#ifdef notdef
	    if (!icon->forced_dock) {
		if (XGetCommand(dpy, window, &argv, &argc)) {
		    if (argc!=icon->argc) {
			XFreeStringList(argv);
			goto next;
		    }
		    for (i=0; i<argc; i++) {
			if (strcmp(icon->argv[i], argv[i])!=0) {
			    XFreeStringList(argv);
			    goto next;
			}
		    }
		}/* else if (icon->argc!=0) {
		    goto next;
		}*/
	    }
#endif
	    if (!icon->relaunching)
	      icon->main_window = window;
	    wDockFinishLaunch(dock, icon);
	    break;
	}
	next:
	icon = icon->next;
    }
    if (wm_class)
      XFree(wm_class);
    if (wm_instance)
      XFree(wm_instance);
}


void
wDockTrackDeadProcess(WDock *dock, pid_t pid, unsigned char status)
{
    WAppIcon *icon;
    
    icon = dock->first_icon;
    while (icon) {
	if (icon->launching && icon->pid == pid) {
	    if (!icon->relaunching) {
		icon->running = 0;
		icon->main_window = None;
	    }
	    wDockFinishLaunch(dock, icon);
	    icon->pid = 0;
	    if (status==111) {
		char msg[MAXPATH];
		sprintf(msg, _("Could not execute program \"%s\""),
			icon->argv[0]);
		wMessageDialog(dock->screen_ptr, _("Error"), msg, WD_ERROR);
	    }
	    break;
	}
	icon = icon->next;
    }
}

		
/******************************************************************/
static void 
iconDblClick(WObjDescriptor *desc, XEvent *event)
{
    WAppIcon *btn = desc->parent;
    WApplication *wapp;

    if (btn->icon->owner && !(event->xbutton.state & Mod1Mask)) {
	wapp = wApplicationOf(btn->icon->owner->main_window);
#ifdef DEBUG0
	if (!wapp) {
	    wWarning("could not find application descriptor for app icon!!");
	    return;
	}
#endif
	wUnhideApplication(wapp);
    } else if (event->xbutton.button==Button1) {
	if (btn->prev==NULL && event->xbutton.state & Mod1Mask) {	    
	    WDock *dock = btn->icon->core->screen_ptr->dock;
	    WAppIcon *tmp = dock->first_icon;
	    int newlevel;
	    
	    /* lower/raise Dock */
	    if (dock->dock_level) {
		newlevel = NSNormalWindowLevel;
		dock->dock_level = 0;
	    } else {
		newlevel = NSDockWindowLevel;
		dock->dock_level = 1;
	    }
	    while (tmp) {
		if (dock->dock_level)
		  wRaiseFrame(tmp->icon->core);
		ChangeStackingLevel(tmp->icon->core, newlevel);
		if (!dock->dock_level)
		  wLowerFrame(tmp->icon->core);
		tmp = tmp->next;
	    }
	} else if (btn->argv) {
	    WResource *res;
	    if (!btn->launching &&
		 (!btn->running || event->xbutton.state & Mod1Mask)) {
		if (!btn->forced_dock) {
		    btn->relaunching = btn->running;
		    btn->running = 1;
		}
		if (btn->wm_instance || btn->wm_class) {
		    res = wResourceGet(NULL, btn->wm_instance, btn->wm_class);
		    if (!res)
		      res = &wResourceDefault;
		      
		    if (!(res->valid & WA_NOT_APPLICATION)
			|| !(res->value & WA_NOT_APPLICATION))
		      btn->launching = 1;
		    else 
		      btn->running = 0;
		}
		btn->pid = execCommand(btn->argv, btn->argc);
		wAppIconPaint(btn);
	    }
	}
    }
    if (btn->argv && event->xbutton.button==Button3 && !btn->forced_dock) {
	if (btn->auto_launch) {
	    btn->auto_launch = 0;
	} else {
	    btn->auto_launch = 1;
	}
	wAppIconPaint(btn);
    }
}


static void 
iconMouseDown(WObjDescriptor *desc, XEvent *event)
{
    WAppIcon *aicon = desc->parent, *tmp;
    WIcon *icon = aicon->icon;
    WScreen *scr = icon->core->screen_ptr;
    WDock *dock = scr->dock;
    XEvent ev;
    int x=aicon->x_pos, y=aicon->y_pos;
    int ofs_x=event->xbutton.x, ofs_y=event->xbutton.y;
    int grabbed=0, ondock, shad_x=x, shad_y=y, swapped=0;
    Window wins[2];

#ifdef DEBUG
    puts("Moving dock");
#endif
    tmp = dock->first_icon;
    if (event->xbutton.button == Button1) {
	if (event->xbutton.state & Mod1Mask) {
	    while (tmp) {
		wLowerFrame(tmp->icon->core);
		tmp = tmp->next;
	    }
	} else {
	    while (tmp) {
		wRaiseFrame(tmp->icon->core);
		tmp = tmp->next;
	    }
	    wRaiseFrame(icon->core);
	}
    }

    /* TODO: remove this send event and make event be passed directly
     * to the client */
    if (icon->owner && icon->client_window)
      XSendEvent(dpy, icon->icon_win, False, ButtonPressMask, event);
    
    if (XGrabPointer(dpy, icon->core->window, True, ButtonMotionMask
		     |ButtonReleaseMask, GrabModeAsync, GrabModeAsync, None, 
		     None, CurrentTime) !=GrabSuccess) {
#ifdef DEBUG0
	wWarning("pointer grab failed for icon move");
#endif
    }

    if (aicon!=dock->first_icon) {
	wins[0] = icon->core->window;
	wins[1] = scr->dock_shadow;
	XRestackWindows(dpy, wins, 2);
	XMoveWindow(dpy, scr->dock_shadow, aicon->x_pos, aicon->y_pos);
	XMapWindow(dpy, scr->dock_shadow);
    }
    ondock = 1;
    while(1) {
	XMaskEvent(dpy, PointerMotionMask|ButtonReleaseMask|ButtonMotionMask
		   |ExposureMask, &ev);
	switch (ev.type) {
	 case Expose:
	    DispatchEvent(&ev);
	    break;

	 case MotionNotify:
	    if (!grabbed) {
		if (abs(ofs_x-ev.xmotion.x)>=MOVE_THRESHOLD
		    || abs(ofs_y-ev.xmotion.y)>=MOVE_THRESHOLD) {
		    XChangeActivePointerGrab(dpy, ButtonMotionMask
					     |ButtonReleaseMask,
					     wCursor[WCUR_MOVE], CurrentTime);
		    grabbed=1;
		} else {
		    break;
		}
	    }
	    if (dock->first_icon == aicon) {
		/* move vertically */
		if (ev.xmotion.y_root - ofs_y < 0) {
		    y = 0;
		    moveDock(dock, y);
		} else if (ev.xmotion.y_root - ofs_y + ICON_HEIGHT >
			   scr->scr_height) {
		    y = scr->scr_height - ICON_HEIGHT;
		    moveDock(dock, y);
		} else {
		    y = ev.xmotion.y_root - ofs_y;
		    moveDock(dock, ev.xmotion.y_root - ofs_y);
		}
		/* move horizontally to change sides */
		x = ev.xmotion.x_root - ofs_x;
		if (!dock->on_right_side) {
		    if (x > ICON_WIDTH*3/2) {
			XMoveResizeWindow(dpy, scr->dock_shadow, 
					  scr->scr_width-ICON_WIDTH
					  +DOCK_EXTRA_SPACE, 
					  dock->y_pos, ICON_WIDTH,
					  ICON_HEIGHT*dock->icon_count);
			XMapRaised(dpy, scr->dock_shadow);
			swapped = 1;
		    } else {
			XUnmapWindow(dpy, scr->dock_shadow);
			swapped = 0;
		    }
                } else {
		    if (x < (scr->scr_width - ICON_WIDTH*5/2)) {
			XMoveResizeWindow(dpy, scr->dock_shadow, 
					  DOCK_EXTRA_SPACE, dock->y_pos,
					  ICON_WIDTH,
					  ICON_HEIGHT*dock->icon_count);
			XMapRaised(dpy, scr->dock_shadow);
			swapped = -1;
		    } else {
			XUnmapWindow(dpy, scr->dock_shadow);
			swapped = 0;
		    }
                }
		
	    } else {
		
		x = ev.xmotion.x_root - ofs_x;
		y = ev.xmotion.y_root - ofs_y;
		if ((x > scr->scr_width - ICON_WIDTH*5/2 &&
		     dock->on_right_side) 
		    || (x < ICON_WIDTH*3/2 && !dock->on_right_side) 
		    || aicon->launching) {
		    if (wDockSnapIcon(scr->dock, aicon, x, y, 
				      &shad_x, &shad_y, True)) {
			XMoveWindow(dpy, scr->dock_shadow, shad_x, shad_y);
			if (!ondock) {
			    XMapWindow(dpy, scr->dock_shadow);
			}
			ondock = 1;
		    }
		} else {
		    if (ondock)
		      XUnmapWindow(dpy, scr->dock_shadow);
		    ondock = 0;
		}
		XMoveWindow(dpy, icon->core->window, x, y);
	    }
	    break;

	 case ButtonRelease:
	    XUngrabPointer(dpy, CurrentTime);
	    if (dock->first_icon != aicon) {
		if (ondock) {
		    SlideWindow(icon->core->window, x, y, shad_x, shad_y,
				DOCK_SPRING_STEPS, DOCK_SPRING_DELAY);
		    XUnmapWindow(dpy, scr->dock_shadow);
		    aicon->x_pos = shad_x;
		    aicon->y_pos = shad_y;
		    reattachIcon(dock, aicon);
		} else {
		    aicon->x_pos = x;
		    aicon->y_pos = y;
#ifdef SUPERFLUOUS
		    if (!aicon->running && wPreferences.animations)
		      DoKaboom(scr,aicon->icon->core->window,x,y);
#endif
		    wDockDetach(dock, aicon);
		}
	    } else {
		XUnmapWindow(dpy, scr->dock_shadow);
		XResizeWindow(dpy, scr->dock_shadow, ICON_WIDTH, ICON_HEIGHT);
		if (swapped!=0) {
		    if (swapped>0)
		      dock->on_right_side = 1;
		    else
		      dock->on_right_side = 0;
		    swapDock(dock);
		}
	    }
#ifdef DEBUG
	    puts("End dock move");
#endif
	    return;
	}
    }
}
    
