/* 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 "icon.h"
#include "appicon.h"
#include "actions.h"
#include "stacking.h"
#include "dock.h"
#include "dialog.h"
#include "funcs.h"
#include "properties.h"
#include "menu.h"
#include "client.h"
#include "defaults.h"

#include "framewin.h"

#ifdef SUPERFLUOUS
#include "superfluous.h"
#endif


#include <proplist.h>



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

extern WPreferences wPreferences;

extern XContext wWinContext;

extern Atom _XA_WM_DELETE_WINDOW;

#ifdef OFFIX_DND
extern Atom _XA_DND_PROTOCOL;
#endif


#define MOD_MASK wPreferences.modifier_mask

extern void appIconMouseDown(WObjDescriptor *desc, XEvent *event);

#define ICON_SIZE wPreferences.icon_size


/***** Local variables ****/

static proplist_t dCommand=NULL;
#ifdef OFFIX_DND
static proplist_t dDropCommand=NULL;
#endif
static proplist_t dAutoLaunch, dName, dForced, dYes, dNo;
static proplist_t dHost, dDock;

static proplist_t dPosition, dApplications, dLowered;

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

static pid_t execCommand(WScreen *scr, char *command, char *host);

static void trackDeadProcess(pid_t pid, unsigned char status, WDock *dock);

static void toggleLowered(WDock *dock);





#ifdef OFFIX_DND

#define DndNotDnd       -1
#define DndUnknown      0
#define DndRawData      1
#define DndFile         2
#define DndFiles        3
#define DndText         4
#define DndDir          5
#define DndLink         6
#define DndExe          7

#define DndEND          8

#endif /* OFFIX_DND */


static void
make_keys()
{
    if (dCommand!=NULL)
	return;
    
    dCommand = PLRetain(PLMakeString("Command"));
#ifdef OFFIX_DND
    dDropCommand = PLRetain(PLMakeString("DropCommand"));
#endif
    dAutoLaunch = PLRetain(PLMakeString("AutoLaunch"));
    dName = PLRetain(PLMakeString("Name"));
    dForced = PLRetain(PLMakeString("Forced"));
    dYes = PLRetain(PLMakeString("YES"));
    dNo = PLRetain(PLMakeString("NO"));
    dHost = PLRetain(PLMakeString("Host"));
    
    dPosition = PLMakeString("Position");
    dApplications = PLMakeString("Applications");
    dLowered = PLMakeString("Lowered");
    
    dDock = PLMakeString("Dock");
}


static void
toggleLoweredCallback(WMenu *menu, WMenuEntry *entry)
{
    assert(entry->clientdata!=NULL);
    

    toggleLowered(entry->clientdata);
    
    entry->flags.indicator_on = !((WDock*)entry->clientdata)->lowered;
	
    wMenuPaint(menu);
}

static void
toggleAutoLaunchCallback(WMenu *menu, WMenuEntry *entry)
{
    WAppIcon *btn = (WAppIcon*)entry->clientdata;
    
    assert(btn!=NULL);
    
    btn->auto_launch = !btn->auto_launch;
    entry->flags.indicator_on = btn->auto_launch;

    wMenuPaint(menu);
}



/*
 *----------------------------------------------------------------------
 * parse_command--
 * 	Divides a command line into a argv/argc pair.
 *---------------------------------------------------------------------- 
 */
#define PRC_ALPHA	0
#define PRC_BLANK	1
#define PRC_ESCAPE	2
#define PRC_DQUOTE	3
#define PRC_EOS		4
#define PRC_SQUOTE	5

typedef struct {
    short nstate;
    short output;
} DFA;


static DFA mtable[9][6] = {
    {{3,1},{0,0},{4,0},{1,0},{8,0},{6,0}},
    {{1,1},{1,1},{2,0},{3,0},{5,0},{1,1}},
    {{1,1},{1,1},{1,1},{1,1},{5,0},{1,1}},
    {{3,1},{5,0},{4,0},{1,0},{5,0},{6,0}},
    {{3,1},{3,1},{3,1},{3,1},{5,0},{3,1}},
    {{-1,-1},{0,0},{0,0},{0,0},{0,0},{0,0}}, /* final state */
    {{6,1},{6,1},{7,0},{6,1},{5,0},{3,0}},
    {{6,1},{6,1},{6,1},{6,1},{5,0},{6,1}},
    {{-1,-1},{0,0},{0,0},{0,0},{0,0},{0,0}}, /* final state */
};

char *next_token(char *word, char **next)
{
    char *ptr;
    char *ret, *t;
    int state, ctype;

    t = ret = wmalloc(strlen(word)+1);
    ptr = word;
    
    state = 0;
    *t = 0;
    while (1) {
	if (*ptr==0) 
	    ctype = PRC_EOS;
	else if (*ptr=='\\')
	    ctype = PRC_ESCAPE;
	else if (*ptr=='"')
	    ctype = PRC_DQUOTE;
	else if (*ptr=='\'')
	    ctype = PRC_SQUOTE;
	else if (*ptr==' ' || *ptr=='\t')
	    ctype = PRC_BLANK;
	else
	    ctype = PRC_ALPHA;

	if (mtable[state][ctype].output) {
	    *t = *ptr; t++;
	    *t = 0;
	}
	state = mtable[state][ctype].nstate;
	ptr++;
	if (mtable[state][0].output<0) {
	    break;
	}
    }

    if (*ret==0)
	t = NULL;
    else
	t = strdup(ret);

    free(ret);
    
    if (ctype==PRC_EOS)
	*next = NULL;
    else
	*next = ptr;
    
    return t;
}


static void
parse_command(WScreen *scr, char *command, char ***argv, int *argc)
{
    LinkedList *list = NULL;
    char *token, *line;
    int count, i;

    line = command;
    do {
	token = next_token(line, &line);
	if (token) {	    
	    list = list_cons(token, list);
	}
    } while (token!=NULL && line!=NULL);

    count = list_length(list);
    *argv = wmalloc(sizeof(char*)*count);
    i = count;
    while (list!=NULL) {
	(*argv)[--i] = list->head;
	list_remove_head(&list);
    }
    *argc = count;
}


static void
setCommandCallback(WMenu *menu, WMenuEntry *entry)
{
    WAppIcon *icon;
    char *command;
    
    assert(entry->clientdata!=NULL);
    
    icon = (WAppIcon*)entry->clientdata;
    if (icon->editing_command)
	return;
    icon->editing_command = 1;

    if (icon->command)
	command = strdup(icon->command);
    else
	command = NULL;
    if (wInputDialog(icon->icon->core->screen_ptr, _("Dock Icon"),
		     _("Enter command for the docked application:"), &command)==WDB_OK) {
	if (icon->command)
	    free(icon->command);
	icon->command = command;
    } else {
	free(command);
    }
    icon->editing_command = 0;

    if (!icon->wm_class && !icon->wm_instance &&
	icon->command && strlen(icon->command)>0) {
      icon->forced_dock = 1;
    }
}


#ifdef OFFIX_DND
static void
setDropCommandCallback(WMenu *menu, WMenuEntry *entry)
{
    WAppIcon *icon;
    char *command;
    
    assert(entry->clientdata!=NULL);
    
    icon = (WAppIcon*)entry->clientdata;
    if (icon->editing_command)
	return;
    icon->editing_command = 1;

    if (icon->dnd_command)
	command = strdup(icon->dnd_command);
    else
	command = icon->command ? strdup(icon->command) : NULL;
    if (wInputDialog(icon->icon->core->screen_ptr, _("Dock Icon"),
		     _("Enter command for dropped objects:"), &command)==WDB_OK) {
	if (icon->dnd_command)
	    free(icon->dnd_command);
	icon->dnd_command = command;
    } else {
	free(command);
    }
    icon->editing_command = 0;
}
#endif /* OFFIX_DND */

static void
killCallback(WMenu *menu, WMenuEntry *entry)
{
    WAppIcon *icon;

    assert(entry->clientdata!=NULL);
    
    icon = (WAppIcon*)entry->clientdata;
 
    if (icon->icon && icon->icon->owner) {
	if (icon->icon->owner->protocols.DELETE_WINDOW) {
	    /* send delete message */
	    wClientSendProtocol(icon->icon->owner, _XA_WM_DELETE_WINDOW, 
				CurrentTime);
	} else {
	    wClientKill(icon->icon->owner);
	}
    }
}


static void
launchCallback(WMenu *menu, WMenuEntry *entry)
{
    WAppIcon *btn;
    
    btn = (WAppIcon*)entry->clientdata;

    if (!btn->launching && btn->command!=NULL) {
	if (!btn->forced_dock) {
	    btn->relaunching = btn->running;
	    btn->running = 1;
	}
	if (btn->wm_instance || btn->wm_class) {
	    WWindowAttributes attr;
	    
	    wDefaultFillAttributes(btn->icon->core->screen_ptr, 
				   btn->wm_instance, btn->wm_class, &attr);
	    
	    if (attr.application)
		btn->launching = 1;
	    else 
		btn->running = 0;
	}
	btn->drop_launch = 0;
	btn->pid = execCommand(btn->icon->core->screen_ptr,
			       btn->command, btn->client_machine);
	if (btn->pid>0) {
	    wAddDeathHandler(btn->pid, (WDeathHandler*)trackDeadProcess, 
			     btn->icon->core->screen_ptr->dock);
	    wAppIconPaint(btn);
	} else {
	    btn->launching = 0;
	    if (!btn->relaunching)
		btn->running = 0;
	}
    }
}


WDock*
wDockCreate(WScreen *scr)
{
    WDock *dock;
    WAppIcon *btn;
    WMenu *menu;
    WMenuEntry *entry;
    int icon_count;

    make_keys();

        
    dock = wmalloc(sizeof(WDock));
    /*
    if (scr->scr_width > scr->scr_height) 
	icon_count = scr->scr_width/wPreferences.icon_size;
    else
     */
	icon_count = scr->scr_height/wPreferences.icon_size;

    dock->icon_array = wmalloc(sizeof(WAppIcon*)*icon_count);
    memset(dock->icon_array, 0, sizeof(WAppIcon*)*icon_count);
    
    dock->max_icons = icon_count;

    btn = wAppIconCreateForCommand(scr, NULL, "WMDock", "Logo");

    btn->yindex = 0;
    btn->xindex = 0;
    
    btn->icon->core->descriptor.handle_mousedown = iconMouseDown;
    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);
    dock->screen_ptr = scr;
    dock->icon_count = 1;
    dock->on_right_side = 1;
    dock->icon_array[0] = btn;
    btn->x_pos = dock->x_pos = scr->scr_width - ICON_SIZE-DOCK_EXTRA_SPACE;
    btn->y_pos = dock->y_pos = 0;
    btn->docked = 1;
    XMoveWindow(dpy, btn->icon->core->window, btn->x_pos, btn->y_pos);

    /* create dock menu */
    menu = wMenuCreate(scr, NULL, False);
    entry = wMenuAddCallback(menu, _("Floating Dock"), toggleLoweredCallback, 
			     NULL);
    entry->flags.indicator = 1;
    entry->flags.indicator_on = 1;
    entry->flags.check = 1;

    entry = wMenuAddCallback(menu, _("Launch"), launchCallback, NULL);

    entry = wMenuAddCallback(menu, _("Autolaunch"), toggleAutoLaunchCallback, 
			     NULL);
    entry->flags.indicator = 1;
    entry->flags.indicator_on = 1;
    entry->flags.check = 1;    
    
    wMenuAddCallback(menu, _("Set arguments..."), setCommandCallback, NULL);

#ifdef OFFIX_DND
    wMenuAddCallback(menu, _("Set drop arguments..."), setDropCommandCallback, 
		     NULL);
#endif
    
    wMenuAddCallback(menu, _("Kill"), killCallback, NULL);

    dock->menu = menu;
    
    return dock;
}


static proplist_t
make_icon_state(WAppIcon *btn)
{
    proplist_t node;
    proplist_t command, autolaunch, name, forced, host;
    char buffer[256];


    if (btn) {
	if (!btn->command)
	    command = PLMakeString("-");
	else
	    command = PLMakeString(btn->command);

	autolaunch = PLRetain(btn->auto_launch ? dYes : dNo);
	
	if (btn->wm_class && btn->wm_instance)
	    sprintf(buffer, "%s.%s", btn->wm_instance, btn->wm_class);
	else if (btn->wm_instance)
	    sprintf(buffer, "%s", btn->wm_instance);
	else if (btn->wm_class)
	    sprintf(buffer, ".%s", btn->wm_class);
	else
	    sprintf(buffer, ".");
	
	name = PLMakeString(buffer);

	forced = PLRetain(btn->forced_dock ? dYes : dNo);

	node = PLMakeDictionaryFromEntries(PLRetain(dCommand), command,
					   PLRetain(dName), name,
					   PLRetain(dAutoLaunch), autolaunch,
					   PLRetain(dForced), forced,
					   NULL);
#ifdef OFFIX_DND
	if (btn->dnd_command) {
	    PLInsertDictionaryEntry(node, PLRetain(dDropCommand),
				    PLMakeString(btn->dnd_command));
	}
#endif /* OFFIX_DND */
 
	if (btn->client_machine && btn->remote_start) {
	    host = PLMakeString(btn->client_machine);
	    PLInsertDictionaryEntry(node, dHost, host);
	    PLRelease(host);
	}
    } else {
	command = PLMakeArrayFromElements(NULL);
	node = PLMakeDictionaryFromEntries(PLRetain(dCommand), command, 
					   NULL);
    }

    return node;
}


void
wDockSaveState(WScreen *scr)
{
    WDock *dock = scr->dock;
    WAppIcon *btn;
    int i, c;
    proplist_t icon_info;
    proplist_t list=NULL, dock_state=NULL;
    proplist_t value;
    char buffer[256];

    list = PLMakeArrayFromElements(NULL);

    c = 0;
    for (i=0; (i < dock->max_icons) && (c < dock->icon_count) ; i++) {
	btn = dock->icon_array[i];
	if (btn!=0)
	    c++;
	icon_info = make_icon_state(btn);
	list = PLAppendArrayElement(list, icon_info);
    }

    dock_state = PLMakeDictionaryFromEntries(dApplications, list, NULL);

    if (dock->on_right_side)
	sprintf(buffer, "%i,%i", -ICON_SIZE, dock->y_pos);
    else
	sprintf(buffer, "%i,%i", 0, dock->y_pos);
    value = PLMakeString(buffer);
    PLInsertDictionaryEntry(dock_state, dPosition, value);
    PLRelease(value);
    
    value = (dock->lowered ? dYes : dNo);
    PLInsertDictionaryEntry(dock_state, dLowered, value);
    PLRelease(value);
    
    PLInsertDictionaryEntry(scr->session_state, dDock, dock_state);
    
}


static void
parse_window_name(proplist_t value, char **winstance, char **wclass)
{
    char *name;
    char *dot;

    if (!PLIsString(value)) {
	wWarning(_("bad window name value in dock state info"));
	return;
    }

    name = PLGetString(value);
    if (!name || strlen(name)==0) {
	wWarning(_("bad window name value in dock state info"));
	return;
    }
    
    dot = strchr(name, '.');
    if (dot==name) {
	*wclass = strdup(&name[1]);
	*winstance = NULL;
    } else if (!dot) {
	*winstance = strdup(name);
	*wclass = NULL;
    } else {
	*dot = 0;
	*winstance = strdup(name);
	*dot = '.'; /* restore old string */
	*wclass = strdup(&dot[1]);
    }
}


static WAppIcon*
restore_icon_state(WScreen *scr, proplist_t info)
{
    WAppIcon *aicon;
    char *wclass, *winstance;
    proplist_t cmd, value;
    char *command;
    

    cmd = PLGetDictionaryEntry(info, dCommand);
    if (!cmd || !PLIsString(cmd)) {
	return NULL;
    }

    /* parse window name */
    value = PLGetDictionaryEntry(info, dName);
    if (!value)
	return NULL;
    
    parse_window_name(value, &winstance, &wclass);
    
    if (!winstance && !wclass) {
	return NULL;
    }

    /* get commands */

    command = PLGetString(cmd);
    
    if (!command || strcmp(command, "-")==0) {
	if (command)
	    free(command);
	if (wclass)
	    free(wclass);
	if (winstance)
	    free(winstance);

	return NULL;
    }

    aicon = wAppIconCreateForCommand(scr, command, wclass, winstance);
    if (wclass)
	free(wclass);
    if (winstance)
	free(winstance);
    
    aicon->icon->core->descriptor.handle_mousedown = iconMouseDown;
    aicon->icon->core->descriptor.parent_type = WCLASS_DOCK_ICON;
    aicon->icon->core->descriptor.parent = aicon;

    
#ifdef OFFIX_DND
    cmd = PLGetDictionaryEntry(info, dDropCommand);
    if (cmd)
	aicon->dnd_command = PLGetString(cmd);
#endif

    /* check auto launch */
    value = PLGetDictionaryEntry(info, dAutoLaunch);

    aicon->auto_launch = 0;
    if (value && PLIsString(value)) {
	if (strcasecmp(PLGetString(value), "YES")==0)
	    aicon->auto_launch = 1;
    } else {
	wWarning(_("bad value in docked icon state info %s"),
		 PLGetString(dAutoLaunch));
    }

    /* check if it wasn't normally docked */
    value = PLGetDictionaryEntry(info, dForced);

    aicon->forced_dock = 0;
    if (value && PLIsString(value)) {
	if (strcasecmp(PLGetString(value), "YES")==0)
	    aicon->forced_dock = 1;
    } else {
	wWarning(_("bad value in docked icon state info %s"),
		 PLGetString(dForced));
    }

    aicon->running = 0;
    aicon->docked = 1;

    return aicon;
}


WDock*
wDockRestoreState(WScreen *scr)
{
    WDock *dock;
    proplist_t apps;
    proplist_t value;
    proplist_t dock_state;
    WAppIcon *aicon, *old_top;
    int count, i;


#define COMPLAIN(key) wWarning(_("bad value in dock state info:%s"), key)

    dock = wDockCreate(scr);
    
    dock_state = PLGetDictionaryEntry(scr->session_state, dDock);

    if (!dock_state)
	return dock;

    if (dock_state)
	PLRetain(dock_state);


    /* restore position */
        
    dock->x_pos = scr->scr_width - ICON_SIZE - DOCK_EXTRA_SPACE;
    dock->y_pos = 0;
    
    value = PLGetDictionaryEntry(dock_state, dPosition);
    
    if (value) {
	if (!PLIsString(value))
	    COMPLAIN("Position");
	else {
	    if (sscanf(PLGetString(value), "%i,%i", &dock->x_pos, 
		       &dock->y_pos)!=2)
		COMPLAIN("Position");
	    
	    /* check position sanity */
	    if (dock->y_pos < 0) 
		dock->y_pos = 0;
	    else if (dock->y_pos > scr->scr_height-ICON_SIZE)
		dock->y_pos = scr->scr_height-ICON_SIZE;
	    
	    if (dock->x_pos >= 0) {
		dock->x_pos = DOCK_EXTRA_SPACE;
		dock->on_right_side = 0;
	    } else {
		dock->x_pos = scr->scr_width - DOCK_EXTRA_SPACE - ICON_SIZE;
		dock->on_right_side = 1;
	    }
	}
    }
    
    /* restore lowered/raised state */
        
    dock->lowered = 0;
    
    value = PLGetDictionaryEntry(dock_state, dLowered);
    
    if (value) {
	if (!PLIsString(value))
	    COMPLAIN("Lowered");
	else {
	    if (strcasecmp(PLGetString(value), "YES")==0)
		dock->lowered = 1;
	}
    }
    
    
    /* application list */
    
    apps = PLGetDictionaryEntry(dock_state, dApplications);
 
    if (!apps) {
	goto finish;
    }

    count = PLGetNumberOfElements(apps);
    
    if (count==0) 
	goto finish;

    old_top = dock->icon_array[0];
    
    dock->icon_count = 0;
    for (i=0; i<count; i++) {
	value = PLGetArrayElement(apps, i);
	aicon = restore_icon_state(scr, value);

	if (i >= dock->max_icons) {
	    wWarning(_("there are too many icons stored in dock. Ignoring what doesn't fit"));
	    break;
	}
	
	dock->icon_array[i] = aicon;

	if (aicon) {
	    aicon->yindex = i;
	    aicon->xindex = 0;
	    
	    aicon->x_pos = dock->x_pos;
	    aicon->y_pos = (i*ICON_SIZE) + dock->y_pos;

	    if (dock->lowered)
		ChangeStackingLevel(aicon->icon->core, NSNormalWindowLevel);
	    else
		ChangeStackingLevel(aicon->icon->core, NSDockWindowLevel);

	    wCoreConfigure(aicon->icon->core, aicon->x_pos, aicon->y_pos,
			   0, 0);

	    XMapWindow(dpy, aicon->icon->core->window);
	    wRaiseFrame(aicon->icon->core);

	    dock->icon_count++;
	}
    }

    /* if the first icon is not defined, use the default */
    if (dock->icon_array[0]==NULL) {
	/* update default icon */
	old_top->x_pos = dock->x_pos;
	old_top->y_pos = dock->y_pos;
	if (dock->lowered)         
            ChangeStackingLevel(old_top->icon->core, NSNormalWindowLevel);
	dock->icon_array[0] = old_top;
	XMoveWindow(dpy, old_top->icon->core->window, dock->x_pos, dock->y_pos);
	dock->icon_count++;
    } else {
	/* otherwise destroy default */
	wAppIconDestroy(old_top);
    }

  finish:
    if (dock_state)
	PLRelease(dock_state);

    return dock;
}



void
wDockDoAutoLaunch(WDock *dock)
{
    WAppIcon *btn;
    int i;

    for (i=0; i < dock->max_icons; i++) {
	btn = dock->icon_array[i];
	if (btn && btn->auto_launch && btn->command && !btn->running) {
	    
	    btn->drop_launch = 0;
	    
	    btn->pid = execCommand(dock->screen_ptr, btn->command, 
				   btn->client_machine);
	    
	    if (btn->pid>0) {
		wAddDeathHandler(btn->pid, (WDeathHandler*)trackDeadProcess, 
				 dock);
			     
		if (!btn->forced_dock) {
		    btn->launching = 1;
		    wAppIconPaint(btn);
		}
	    }
	}
    }
}



#ifdef OFFIX_DND
int
wDockReceiveDNDDrop(WScreen *scr, XEvent *event)
{
    WDock *dock = scr->dock;
    WAppIcon *btn;
    int i;
    
    if (!dock)
	return False;
    	
    /* find which icon the thing was dropped on */
    for (i=0; i<dock->max_icons; i++) {
	if (dock->icon_array[i]
	    && dock->icon_array[i]->icon->core->window==event->xclient.window) {
	    
	    /*
	     * Return True if the drop was on an application icon window.
	     * In this case, let the ClientMessage handler redirect the
	     * message to the app.
	     */
	    if (dock->icon_array[i]->icon->icon_win!=None)
		return True;

	    if (dock->icon_array[i]->dnd_command!=NULL) {
		scr->flags.dnd_data_convertion_status = 0;
		
		btn = dock->icon_array[i];
		
		if (!btn->forced_dock) {
		    btn->relaunching = btn->running;
		    btn->running = 1;
		}
		if (btn->wm_instance || btn->wm_class) {
		    WWindowAttributes attr;
			
		    wDefaultFillAttributes(btn->icon->core->screen_ptr, 
					   btn->wm_instance, 
					   btn->wm_class, &attr);
			
		    if (attr.application)
			btn->launching = 1;
		    else 
			btn->running = 0;
		}

		btn->drop_launch = 1;
		btn->pid = execCommand(scr, btn->dnd_command, 
				       btn->client_machine);
		if (btn->pid>0) {
		    wAddDeathHandler(btn->pid, (WDeathHandler*)trackDeadProcess, 
				     btn->icon->core->screen_ptr->dock);
		    wAppIconPaint(btn);
		} else {
		    if (btn->relaunching) {
			btn->running = 0;
		    }
		    btn->launching = 0;
		}
	    }
	} 
    }
    return False;
}
#endif /* OFFIX_DND */



void
wDockAttachIcon(WDock *dock, WAppIcon *icon, int x, int y)
{
    WWindow *wwin;
    char **argv;
    int argc;

    assert(dock->icon_array[y]==NULL);
    
    dock->icon_array[y] = icon;
    icon->yindex = y;
    icon->xindex = x;

    if (dock->on_right_side) {
	icon->x_pos = dock->x_pos + x*ICON_SIZE;
    } else {
	icon->x_pos = dock->x_pos - x*ICON_SIZE;
    }
    
    icon->y_pos = dock->y_pos + y*ICON_SIZE;

    dock->icon_array[y] = icon;
    dock->icon_count++;
    

    icon->running = 1;
    icon->launching = 0;
    icon->docked = 1;
    icon->icon->core->descriptor.handle_mousedown = iconMouseDown;
    icon->icon->core->descriptor.parent_type = WCLASS_DOCK_ICON;
    icon->icon->core->descriptor.parent = icon;
    if (!dock->lowered)
	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;

    if (icon->command==NULL) {
	if (XGetCommand(dpy, wwin->client_win, &argv, &argc) && argc>0) {
	    
	    icon->command = FlattenStringList(argv, argc);
	    XFreeStringList(argv);
	} else {
	    char *command=NULL;
	    
	    icon->forced_dock = 1;
	    if (wInputDialog(icon->icon->core->screen_ptr, _("Dock Icon"), 
			     _("Command:"), &command)==WDB_OK) {
		icon->command = command;
	    } else {
		if (command)
		    free(command);
	    }
	}
    }
}


void
reattachIcon(WDock *dock, WAppIcon *icon, int x, int y)
{
    assert(dock->icon_array[icon->yindex]!=NULL);
    dock->icon_array[icon->yindex]=NULL;
    
    icon->yindex = y;
    icon->xindex = x;

    dock->icon_array[y] = icon;
    
    if (dock->on_right_side) {
	icon->x_pos = dock->x_pos + x*ICON_SIZE;
    } else {
	icon->x_pos = dock->x_pos - x*ICON_SIZE;
    }
    
    icon->y_pos = dock->y_pos + y*ICON_SIZE;
}


void
wDockDetach(WDock *dock, WAppIcon *icon)
{
    icon->docked = 0;
    
    if (icon->command) {
	free(icon->command);
	icon->command = NULL;
    }

    assert(dock->icon_array[icon->yindex]!=NULL);
    dock->icon_array[icon->yindex] = NULL;
    icon->yindex = -1;
    icon->xindex = -1;
    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.handle_mousedown = appIconMouseDown;
	icon->icon->core->descriptor.parent_type = WCLASS_APPICON;
	icon->icon->core->descriptor.parent = icon;
	ChangeStackingLevel(icon->icon->core, NSNormalWindowLevel);
	wAppIconPaint(icon);
#ifdef AUTOARRANGE_ICON
	wArrangeIcons(dock->screen_ptr);
#endif
    }
}


/*
 * returns the closest Dock slot index 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 dx, dy;
/*    WScreen *scr = dock->screen_ptr;
 */
    int ex_x, ex_y;

    dy = dock->y_pos;
    dx = dock->x_pos;

    /* if it's before or over the first icon or the dock is full */
    if (!redocking &&
	(dock->icon_count >= dock->max_icons || req_y + ICON_SIZE < dy)) {
	return False;
    }
    
    /* exact position */
    ex_y = (req_y + ICON_SIZE/2 - dy)/ICON_SIZE;
    
    if (dock->on_right_side)
	ex_x = (req_x - dx)/ICON_SIZE;
    else
	ex_x = (req_x - dx)/ICON_SIZE;
    
    if ((!redocking && ex_x == 0 && dock->icon_array[ex_y]==NULL)
	 || (redocking && abs(ex_x) <= 1
	     && (dock->icon_array[ex_y]==icon||!dock->icon_array[ex_y]))) {
	*ret_x = 0;
	*ret_y = ex_y;
	return True;
    }


    if (ex_x > 0 && dock->icon_array[ex_y]==NULL) {
	return False;
    }
    
    return False;
}


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

    dock->y_pos = new_y;
    for (i=0; i<dock->max_icons; i++) {
	btn = dock->icon_array[i];
	if (btn) {
	    btn->y_pos = new_y + i*ICON_SIZE;
	    XMoveWindow(dpy, btn->icon->core->window, btn->x_pos, btn->y_pos);
	}
    }
}


static void
swapDock(WDock *dock)
{
   WScreen *scr = dock->screen_ptr;
   WAppIcon *btn;
   int x, i;


    if (dock->on_right_side) {
	x = dock->x_pos = scr->scr_width - ICON_SIZE - DOCK_EXTRA_SPACE;
    } else {
	x = dock->x_pos = DOCK_EXTRA_SPACE;
    }

    for (i=0; i<dock->max_icons; i++) {
	btn = dock->icon_array[i];
	if (btn) {
	    btn->x_pos = x;
	    XMoveWindow(dpy, btn->icon->core->window, btn->x_pos, btn->y_pos);
	}
    }
}


static pid_t
execCommand(WScreen *scr, char *command, char *host)
{
    pid_t pid;
    char **argv;
    int argc;
    char *cmdline;

    
    cmdline = ExpandOptions(scr, command);

    if (scr->flags.dnd_data_convertion_status || !cmdline) {
	if (cmdline)
	    free(cmdline);
	return 0;
    }

    parse_command(scr, cmdline, &argv, &argc);
    free(cmdline);
    
    if (argv==NULL) {
	return 0;
    }
    
    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;
    int i;
    
    for (i=0; i<dock->max_icons; i++) {
	icon = dock->icon_array[i];
	if (icon && icon->main_window == window)
	    return icon;
    }
    return NULL;
}


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

    if (!PropGetWMClass(window, &wm_class, &wm_instance) ||
	(!wm_class && !wm_instance))
	return;
    
    for (i=0; i<dock->max_icons; i++) {
	icon = dock->icon_array[i];
	if (!icon)
	    continue;
	
	/* kluge. If this does not exist, some windows attach themselves
	 * to more than one icon. Find out why */
	if (icon->main_window == window)
	    return;

	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) {
		continue;
	    }
	    if (icon->wm_class && wm_class && 
		strcmp(icon->wm_class, wm_class)!=0) {
		continue;
	    }
#ifdef notdef
	    if (!icon->forced_dock) {
		if (XGetCommand(dpy, window, &argv, &argc)) {
		    if (argc!=icon->argc) {
			XFreeStringList(argv);
			continue;
		    }
		    for (j=0; j<argc; j++) {
			if (strcmp(icon->argv[i], argv[j])!=0) {
			    XFreeStringList(argv);
			    continue;
			}
		    }
		}/* else if (icon->argc!=0) {
		    continue;
		}*/
	    }
#endif
	    if (!icon->relaunching)
		icon->main_window = window;
	    wDockFinishLaunch(dock, icon);
	    break;
	}
    }
    if (wm_class)
	XFree(wm_class);
    if (wm_instance)
	XFree(wm_instance);
}



static void
trackDeadProcess(pid_t pid, unsigned char status, WDock *dock)
{
    WAppIcon *icon;
    int i;
    
    for (i=0; i<dock->max_icons; i++) {
	icon = dock->icon_array[i];
	if (!icon)
	    continue;

	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];
#ifdef OFFIX_DND
		sprintf(msg, _("Could not execute command \"%s\""),
			icon->drop_launch && icon->dnd_command 
			? icon->dnd_command : icon->command);
#else
		sprintf(msg, _("Could not execute command \"%s\""),
			icon->command);
#endif
		wMessageDialog(dock->screen_ptr, _("Error"), msg, WD_ERROR);
	    }
	    break;
	}
    }
}


static void
toggleLowered(WDock *dock)
{
    WAppIcon *tmp;
    int newlevel, i;

    /* lower/raise Dock */
    if (!dock->lowered) {
	newlevel = NSNormalWindowLevel;
	dock->lowered = 1;
    } else {
	newlevel = NSDockWindowLevel;
	dock->lowered = 0;
    }
    
    for (i=0; i<dock->max_icons; i++) {
	tmp = dock->icon_array[i];
	if (!tmp)
	    continue;
	
	if (!dock->lowered)
	    wRaiseFrame(tmp->icon->core);
	ChangeStackingLevel(tmp->icon->core, newlevel);
	if (dock->lowered)
	    wLowerFrame(tmp->icon->core);
    }
}

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


    if (btn->icon->owner && !(event->xbutton.state & ControlMask)) {
	wapp = wApplicationOf(btn->icon->owner->main_window);

	assert(wapp!=NULL);

	wUnhideApplication(wapp, event->xbutton.button==Button2);

	if (event->xbutton.state & MOD_MASK) {
	    wHideOtherApplications(btn->icon->owner);
	}
    } else {
	if (event->xbutton.button==wLeftBtn) {
	
	    if (event->xbutton.state & MOD_MASK) {
		if (btn->prev==NULL) {
		    /* raise/lower dock */
		    toggleLowered(btn->icon->core->screen_ptr->dock);
		}
	    } else if (btn->command) {
		btn->drop_launch = 0;
		
		if (!btn->launching &&
		    (!btn->running || (event->xbutton.state & ControlMask))) {
		    if (!btn->forced_dock) {
			btn->relaunching = btn->running;
			btn->running = 1;
		    }
		    if (btn->wm_instance || btn->wm_class) {
			WWindowAttributes attr;
			
			wDefaultFillAttributes(btn->icon->core->screen_ptr, 
					       btn->wm_instance, 
					       btn->wm_class, &attr);
			
			if (attr.application)
			    btn->launching = 1;
			else 
			    btn->running = 0;
		    }
		    btn->pid = execCommand(btn->icon->core->screen_ptr,
					   btn->command, btn->client_machine);
		    if (btn->pid>0) {
			wAddDeathHandler(btn->pid, (WDeathHandler*)trackDeadProcess,
					 btn->icon->core->screen_ptr->dock);
			wAppIconPaint(btn);
		    } else {
			btn->launching = 0;
			if (!btn->relaunching)
			    btn->running = 0;
		    }
		}
	    }
	}
    }
}


static void
handleDockMove(WDock *dock, WAppIcon *aicon, XEvent *event)
{
    WScreen *scr = dock->screen_ptr;
    int ofs_x=event->xbutton.x, ofs_y=event->xbutton.y;
    int x, y;
    XEvent ev;
    int grabbed = 0, swapped = 0, done;
#ifdef SUPERFLUOUS
    Pixmap ghost = None;
#endif

#ifdef DEBUG
    puts("moving dock");
#endif
    if (XGrabPointer(dpy, aicon->icon->core->window, True, ButtonMotionMask
		     |ButtonReleaseMask, GrabModeAsync, GrabModeAsync, None, 
		     None, CurrentTime) !=GrabSuccess) {
	wWarning("pointer grab failed for dock move");
    }
    x = dock->icon_count;
    y = 0;
    while (x > 0) {
	if (dock->icon_array[y++]!=NULL)
	    x--;
    }
    XResizeWindow(dpy, scr->dock_shadow, ICON_SIZE, ICON_SIZE*y);
    
    done = 0;
    while (!done) {
	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;
		}
		break;
	    }
	    /* move vertically if pointer is inside the dock*/
	    if ((dock->on_right_side &&
		 ev.xmotion.x_root >= dock->x_pos - ICON_SIZE)
		|| (!dock->on_right_side &&
		    ev.xmotion.x_root <= dock->x_pos + ICON_SIZE*2)) {
		
		if (ev.xmotion.y_root - ofs_y < 0) {
		    y = 0;
		    moveDock(dock, y);
		} else if (ev.xmotion.y_root - ofs_y + ICON_SIZE >
			   scr->scr_height) {
		    y = scr->scr_height - ICON_SIZE;
		    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) {
		
		/* is on left */
		
		if (ev.xmotion.x_root > dock->x_pos + ICON_SIZE*2) {
		    XMoveWindow(dpy, scr->dock_shadow, scr->scr_width-ICON_SIZE
				      -DOCK_EXTRA_SPACE, dock->y_pos);
#ifdef SUPERFLUOUS
		    if (ghost==None) {
			ghost = MakeGhostDock(dock, dock->x_pos,
					      scr->scr_width-ICON_SIZE
					      -DOCK_EXTRA_SPACE, dock->y_pos);
			XSetWindowBackgroundPixmap(dpy, scr->dock_shadow,
						   ghost);
			XClearWindow(dpy, scr->dock_shadow);
		    }
#endif
		    XMapRaised(dpy, scr->dock_shadow);
		    swapped = 1;
		} else {
#ifdef SUPERFLUOUS
		    if (ghost!=None) {
			XFreePixmap(dpy, ghost);
			ghost = None;
		    }
#endif
		    XUnmapWindow(dpy, scr->dock_shadow);
		    swapped = 0;
		}
	    } else {
		/* is on right */
		if (ev.xmotion.x_root < dock->x_pos - ICON_SIZE) {
		    XMoveWindow(dpy, scr->dock_shadow, 
			      DOCK_EXTRA_SPACE, dock->y_pos);
#ifdef SUPERFLUOUS
		    if (ghost==None) {
			ghost = MakeGhostDock(dock, dock->x_pos,
					      DOCK_EXTRA_SPACE, dock->y_pos);
			XSetWindowBackgroundPixmap(dpy, scr->dock_shadow,
						   ghost);
			XClearWindow(dpy, scr->dock_shadow);
		    }
#endif
		    XMapRaised(dpy, scr->dock_shadow);
		    swapped = -1;
		} else {
		    XUnmapWindow(dpy, scr->dock_shadow);
		    swapped = 0;
#ifdef SUPERFLUOUS
		    if (ghost!=None) {
			XFreePixmap(dpy, ghost);
			ghost = None;
		    }
#endif
		}
	    }
	    break;
	    
	 case ButtonRelease:
	    XUngrabPointer(dpy, CurrentTime);
	    XUnmapWindow(dpy, scr->dock_shadow);
	    XResizeWindow(dpy, scr->dock_shadow, ICON_SIZE, ICON_SIZE);
	    if (swapped!=0) {
		if (swapped>0)
		    dock->on_right_side = 1;
		else
		    dock->on_right_side = 0;
		swapDock(dock);
	    }
	    done = 1;
	    break;
	}
    }
#ifdef SUPERFLUOUS
    if (ghost!=None)
	XFreePixmap(dpy, ghost);
    XSetWindowBackground(dpy, scr->dock_shadow, scr->white_pixel);
#endif
#ifdef DEBUG
    puts("End dock move");
#endif
}



static void
handleIconMove(WDock *dock, WAppIcon *aicon, XEvent *event)
{
    WScreen *scr = dock->screen_ptr;
    Window wins[2];
    WIcon *icon = aicon->icon;
    int ondock, grabbed = 0;
    XEvent ev;
    int x = aicon->x_pos, y = aicon->y_pos;
    int ofs_x = event->xbutton.x, ofs_y = event->xbutton.y;
    int shad_x = x, shad_y = y;
    int ix = aicon->xindex, iy = aicon->yindex;
    int tmp;
#ifdef SUPERFLUOUS
    Pixmap ghost = None;
#endif

    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
    }

    wins[0] = icon->core->window;
    wins[1] = scr->dock_shadow;
    XRestackWindows(dpy, wins, 2);
    XMoveResizeWindow(dpy, scr->dock_shadow, aicon->x_pos, aicon->y_pos,
		      ICON_SIZE, ICON_SIZE);
#ifdef SUPERFLUOUS
    if (icon->pixmap!=None)
	ghost = MakeGhostIcon(scr, icon->pixmap);
    else
	ghost = MakeGhostIcon(scr, icon->core->window);

    XSetWindowBackgroundPixmap(dpy, scr->dock_shadow, ghost);
    XClearWindow(dpy, scr->dock_shadow);
#endif
    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;
		}
	    }
		
	    x = ev.xmotion.x_root - ofs_x;
	    y = ev.xmotion.y_root - ofs_y;
	    tmp = wDockSnapIcon(dock, aicon, x, y, &ix, &iy, True);
	    if (aicon->launching
		|| (aicon->running && !(ev.xmotion.state & MOD_MASK))
		|| (!aicon->running && tmp)) {
		if (dock->on_right_side)
		    shad_x = dock->x_pos + ix*wPreferences.icon_size;
		else
		    shad_x = dock->x_pos - ix*wPreferences.icon_size;
		shad_y = dock->y_pos + iy*wPreferences.icon_size;
		
		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 (ondock) {
		SlideWindow(icon->core->window, x, y, shad_x, shad_y);
		XUnmapWindow(dpy, scr->dock_shadow);
		reattachIcon(dock, aicon, ix, iy);
	    } else {
		aicon->x_pos = x;
		aicon->y_pos = y;
#ifdef SUPERFLUOUS
		if (!aicon->running && !wPreferences.no_animations)
		    DoKaboom(scr,aicon->icon->core->window,x,y);
#endif
		wDockDetach(dock, aicon);
	    }
#ifdef SUPERFLUOUS
	    if (ghost!=None)
		XFreePixmap(dpy, ghost);
	    XSetWindowBackground(dpy, scr->dock_shadow, scr->white_pixel);
#endif
#ifdef DEBUG
	    puts("End icon move");
#endif
	    return;
	}
    }
}


static void
handleDrawerMove(WDock *dock, WAppIcon *aicon, XEvent *event)
{
}


static void 
iconMouseDown(WObjDescriptor *desc, XEvent *event)
{
    WAppIcon *aicon = desc->parent;
    WScreen *scr = aicon->icon->core->screen_ptr;
    WDock *dock = scr->dock;
    int i;

    if (dock->menu->flags.mapped)
	wMenuUnmap(dock->menu);

    
    if ((desc->click_timestamp>0) 
	&& (event->xbutton.time - desc->click_timestamp <= DEF_DBLCLICK_TIME)) {
	desc->click_timestamp = -1;
	
	iconDblClick(desc, event);
	
	return;
    }
    desc->click_timestamp = event->xbutton.time;
    
#ifdef DEBUG
    puts("handling dock");
#endif
    if (event->xbutton.button == wLeftBtn) {
	if (event->xbutton.state & MOD_MASK) {
	    for (i=0; i<dock->max_icons; i++) {
		if (dock->icon_array[i])
		    wLowerFrame(dock->icon_array[i]->icon->core);
	    }
	} else {
	    for (i=0; i<dock->max_icons; i++) {
		if (dock->icon_array[i])
		    wRaiseFrame(dock->icon_array[i]->icon->core);
	    }
	    wRaiseFrame(aicon->icon->core);
	}
	if (aicon->yindex==0)
	    handleDockMove(dock, aicon, event);
	else
	    handleIconMove(dock, aicon, event);
	
    } else if (event->xbutton.button == wRightBtn) {
	WObjDescriptor *desc;
	WMenuEntry *entry;
	int index = 0;
	
	/* floating */
	entry = dock->menu->entries[index];
	entry->flags.indicator_on = !dock->lowered;
	entry->clientdata = dock;

	/* launch */
	entry = dock->menu->entries[++index];
	entry->clientdata = aicon;
	wMenuSetEnabled(dock->menu, index, aicon->command!=NULL);

	/* autolaunch */
	entry = dock->menu->entries[++index];
	entry->flags.indicator_on = aicon->auto_launch;
	entry->clientdata = aicon;
	wMenuSetEnabled(dock->menu, index, aicon->command!=NULL);

	/* set command */
	entry = dock->menu->entries[++index];
	entry->clientdata = aicon;

	/* set drop command */
#ifdef OFFIX_DND
	entry = dock->menu->entries[++index];
	entry->clientdata = aicon;
#endif /* OFFIX_DND */
	
	/* kill */
	entry = dock->menu->entries[++index];
	entry->clientdata = aicon;
	wMenuSetEnabled(dock->menu, index, aicon->running && aicon->icon
			&& aicon->icon->owner);
	
	if (!dock->menu->flags.realized)
	    wMenuRealize(dock->menu);
	
	wMenuMapAt(dock->menu, dock->on_right_side ? scr->scr_width 
		       - dock->menu->frame->core->width - 2 : 0,
		       event->xbutton.y_root+2, False);
	/* allow drag select */
	event->xany.send_event = True;
	desc = &dock->menu->menu->descriptor;
	(*desc->handle_mousedown)(desc, event);

    } else if (event->xbutton.button == Button2) {
	handleDrawerMove(dock, aicon, event);
    }
}
