/*-
 * Copyright (c) 2004 Bruce Ashfield
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. Neither the name of the author nor the names of contributors may be
 *    used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */
#include "tasklist.h"
#include "main.h"

/* per-screen structure */
static struct tasklistscr
{
	GC	tasklistgc;
} *tasklistscr;

/* font to use */
#ifdef I18N
static XFontSet	tasklistfont;
static XFontSetExtents *tasklistfont_extents;
#else
static XFontStruct	*tasklistfont	= NULL;
#endif

/* decoration group */
static dgroup_t		*tasklist_dgroup	= NULL;

/* pixmaps */
static pixmap_t		*subtasklist_bullet	= NULL;

/* stacking layer */
int tasklist_stacklayer;

/* XContext to get tasklists */
XContext tasklist_context;

XColor bclr, fclr;

/* initialize the tasklist system */
int tasklist2_init( char *fontname,
                    char *back_colour,
                    char *fore_colour,
                    dgroup_t *dgroup,
                    pixmap_t *bullet)
{
	XGCValues gcvalues;
	int i, cnt;

	/* this is used to get tasklist_t's from windows */
	tasklist_context = XUniqueContext();

	/*
	 * get the font structure, fall back on a semi-decent
	 * font, and if we can't even get that just use 'fixed',
	 * which should always be there.  If we can't use 'fixed'
	 * we give up.
	 */
	if (fontname)
   {
#ifdef I18N
		tasklistfont = XLoadQueryFontSet(display, fontname);
#else
		tasklistfont = XLoadQueryFont(display, fontname);
#endif
		if (tasklistfont)
			goto gotfont;
		PWARN("unable to get requested tasklist_font, trying default");
	}
	PWARN("using default font");
#ifdef I18N
	tasklistfont = XLoadQueryFontSet(display,
                                    "-*-helvetica-medium-r-normal-*-12-*-*-*-*-*-*-*");
#else
	tasklistfont = XLoadQueryFont(display,
                                 "-*-helvetica-medium-r-normal-*-12-*-*-*-*-*-*-*");
#endif
	if (tasklistfont)
		goto gotfont;
   PWARN("failed to load default font; trying 'fixed' as last resort");
#ifdef I18N
	tasklistfont = XLoadQueryFontSet(display, "fixed");
#else
	tasklistfont = XLoadQueryFont(display, "fixed");
#endif
	if (tasklistfont)
		goto gotfont;
	PWARN("failed to load font 'fixed', giving up on tasklists");
	return -1;
  gotfont:

#ifdef I18N
   tasklistfont_extents = XExtentsOfFontSet(tasklistfont);
#endif

   /* get the per-screen structure */
	cnt = ScreenCount(display);
	tasklistscr = calloc(cnt, sizeof(struct tasklistscr));
	if (!tasklistscr)
   {
		return -1;
   }

	/* fill in the structure */
	for (i = 0; i < cnt; i++)
   {
      if( !XParseColor( display,
                        DefaultColormap( display, i ),
                        fore_colour,
                        &fclr) )
      {
         gcvalues.foreground = WhitePixel(display, i);
      }
      else
      {
         if( !XAllocColor(display, DefaultColormap(display, i), &fclr) )
         {
            gcvalues.foreground = WhitePixel(display, i);
         }
         else
         {
            gcvalues.foreground = fclr.pixel;
         }
      }
      if( !XParseColor( display,
                        DefaultColormap( display, i ),
                        back_colour,
                        &bclr) )
      {
         gcvalues.background = BlackPixel(display, i);
      }
      else
      {
         if( !XAllocColor(display, DefaultColormap(display, i), &bclr) )
         {
            gcvalues.background = BlackPixel(display, i);
         }
         else
         {
            gcvalues.background = bclr.pixel;
         }
      }

#ifdef I18N
		tasklistscr[i].tasklistgc = XCreateGC( display, RootWindow(display, i),
                                             GCForeground | GCBackground, &gcvalues);
#else
		gcvalues.font = tasklistfont->fid;
		tasklistscr[i].tasklistgc = XCreateGC( display, RootWindow(display, i),
                                             GCFont | GCForeground | GCBackground,
                                             &gcvalues);
#endif
	}

	tasklist_dgroup = dgroup;
	subtasklist_bullet = bullet;

	return 0;
}

/* shutdown the tasklist system */
void tasklist2_shutdown()
{
	int i, cnt;

	cnt = ScreenCount(display);
	if (tasklistscr)
   {
		for (i = 0; i < cnt; i++)
      {
			if (tasklistscr[i].tasklistgc)
         {
				XFreeGC(display, tasklistscr[i].tasklistgc);
         }
      }
		free(tasklistscr);
	}

	if (tasklistfont)
   {
#ifdef I18N
		XFreeFontSet(display, tasklistfont);
#else
		XFreeFont(display, tasklistfont);
#endif
   }
}

/* create a tasklist */
tasklist_t *tasklist_create()
{
	tasklist_t *tasklist;

	tasklist = calloc(1, sizeof(tasklist_t));

	if (!tasklist)
		return NULL;

	tasklist->forefather = tasklist;

	return tasklist;
}

/* add an entry to a tasklist, pos is the position to add it at */
tasklistent_t *tasklist_addent( tasklist_t *tasklist,
                                int pos,
                                int type,
                                char *text,
                                void *dat )
{
	tasklistent_t *ent;
	tasklistent_t **tmp;
   char *text_store;

	ent = calloc(1, sizeof(tasklistent_t));
	if (!ent)
   {
		return NULL; // ------>
   }

	ent->type = type;

   if( text )
   {
      text_store = (char*) malloc( strlen( text ) + 1 );
      strcpy( text_store, text );
   }
   else
   {
      text_store = (char *) malloc( strlen( "unknown window" ) + 1 );
      strcpy( text_store, "unknown window" );
   }
	ent->text = text_store;

	/* get space for the new entry */
	tmp = realloc(tasklist->entries, (tasklist->entry_count + 1) * sizeof(tasklistent_t *));
	if (!tmp)
		goto free1;
	tasklist->entries = tmp;
	tasklist->entry_count++;

	/* subtasklists need some special attention */
	if (type == ET_SUBTASKLIST)
   {
      /*	tmpsubtasklists = realloc(tasklist->subtasklists, (tasklist->subtasklist_count + 1) * sizeof(tasklist_t *)); */
/*		if (!tmpsubtasklists) */
/*			goto free2; */
/*		tasklist->subtasklists = tmpsubtasklists; */
/*		tasklist->subtasklists[tasklist->subtasklist_count] = (tasklist_t *) dat; */
/*		ent->dat.subtasklist = tasklist->subtasklist_count++; */
/*		/\* set the forefather pointer, and set it on all subwindows of this one *\/ */
/*		((tasklist_t *) dat)->forefather = tasklist->forefather; */
/*		for (i = 0; i < ((tasklist_t *) dat)->subtasklist_count; i++) */
/*			((tasklist_t *) dat)->subtasklists[i]->forefather = tasklist->forefather; */
	}
   else
   {
      // bva. right here ... this is causing the problem
      // well actually it seem to be freeent deallocating improperly ...
		ent->dat.dat = dat;
   }

	/* move to make space if necessary, and put it in */
	if (pos == POS_LAST || pos >= tasklist->entry_count)
   {
		pos = tasklist->entry_count - 1;
   }
	else
   {
		memmove(&tasklist->entries[pos + 1], &tasklist->entries[pos],
              (tasklist->entry_count - pos) * sizeof(tasklistent_t *));
   }

	tasklist->entries[pos] = ent;

	return ent;
#if 0
  free2:
	tasklist->entry_count--;
#endif
  free1:
	free(ent);
	return NULL;
}

void tasklist_remove( tasklist_t *tasklist,
                      void *key )
{
   int i;
   int match_index = -1;
   tasklistent_t *ent, *match;


   for( i = 0; i < tasklist->entry_count; i++ )
   {
      ent = tasklist->entries[i];
      if( ent && ent->dat.dat == key )
      {
         match_index = i;
         match = ent;
      }
   }

   if( match_index != -1 )
   {
      for( i = match_index; i < tasklist->entry_count - 1; i++ )
      {
         tasklist->entries[i] = tasklist->entries[ i + 1 ];
      }

      tasklist->entry_count--;
      free(match);
   }
}

/* dealloc a tasklist entry */
void tasklist_freeent( tasklistent_t *ent )
{
	free(ent->text );

	/* free entry-type specific data */
	switch ( ent->type )
   {
      case ET_SUBTASKLIST:
         if (ent->dat.cmd)
            // nothing currently
            break;
      default:
         break;
	}

	free(ent);
}

/* resize a tasklist */
void tasklist_size(tasklist_t *tasklist)
{
	screen_t *screen;
	int width, height;
	int strwidth, i;

   if( tasklist == NULL )
   {
      return; /* ---------> */
   }

	width = TASKLIST_MINWIDTH;
	height = TASKLIST_YBORDER * 2;
	for (i = 0; i < tasklist->entry_count; i++)
   {
#ifdef I18N
		height += tasklistfont_extents->max_ink_extent.height;
		strwidth = XmbTextEscapement(tasklistfont, tasklist->entries[i]->text,
                                   strlen(tasklist->entries[i]->text));
#else
		height += tasklistfont->ascent + tasklistfont->descent;
		strwidth = XTextWidth(tasklistfont,
                            tasklist->entries[i]->text,
                            strlen(tasklist->entries[i]->text));
#endif
		if (subtasklist_bullet && tasklist->entries[i]->type == ET_SUBTASKLIST)
      {
			strwidth += subtasklist_bullet->width;
      }
		if (width < strwidth)
      {
			width = strwidth;
      }
	}
	width += TASKLIST_XBORDER * 2;

   width = width > TASKLIST_WIDTH ? width : TASKLIST_WIDTH;
   height = height > TASKLIST_HEIGHT ? height : TASKLIST_HEIGHT;

	/* fill in the client_t for each screen */
	TAILQ_FOREACH(screen, &screen_list, s_list)
   {
      tasklist->client[screen->num]->width = width;
      tasklist->client[screen->num]->height = height;

      client_sizeframe(tasklist->client[screen->num]);
   }
}

/* realize a tasklist (called from post-init, i.e. start) */
int tasklist_realize(tasklist_t *tasklist)
{
	XSetWindowAttributes attr;
	clientflags_t flags;
	screen_t *screen;
	Window wnd;
	int i;
   //desktop_t *desktop;
   //char label[200];

	tasklist->client = calloc(screen_count, sizeof(client_t *));
	if (!tasklist->client)
		return -1;

	tasklist->active_sub = calloc(screen_count, sizeof(tasklist_t *));
	if (!tasklist->active_sub)
		goto free;

	bzero(&flags, sizeof(clientflags_t));
	flags.internal = 1;
	flags.nofocus = 1;

	flags.noresize = 0;
   //flags.noresize = 0;
	flags.noiconify = 1;
	flags.nodelete = 0;
	flags.sticky = 1;

	TAILQ_FOREACH(screen, &screen_list, s_list)
       {
          attr.background_pixel = bclr.pixel;

          wnd = XCreateWindow( display, RootWindow(display, screen->num),
                               0, 0, TASKLIST_WIDTH, TASKLIST_HEIGHT, 0,
                               CopyFromParent, CopyFromParent,
                               CopyFromParent, CWBackPixel, &attr );

          // bva
          //         myatoms[0] = WM_DELETE_WINDOW;
          //          XSetWMProtocols( display, wnd, myatoms, 1 );
          // end bva

          tasklist->client[screen->num] = client_add( screen, wnd,
                                                      &flags, tasklist_dgroup );
          if (!tasklist->client[screen->num])
          {
             exit(1);
             return -1;
          }

          /* set layer */
          tasklist->client[screen->num]->stacklayer = tasklist_stacklayer;

          XSaveContext( display, tasklist->client[screen->num]->frame,
                        tasklist_context, (XPointer) tasklist);
          XSelectInput( display, tasklist->client[screen->num]->window,
                        ExposureMask | ButtonPressMask | ButtonReleaseMask | EnterWindowMask );

          plugin_setcontext(plugin_this, tasklist->client[screen->num]->window);

          XMapWindow(display, tasklist->client[screen->num]->window);

          // bva
          //myatoms[0] = WM_DELETE_WINDOW;
          // XSetWMProtocols( display, tasklist->client[screen->num]->window, myatoms, 1 );
          // end bva

/* #warning trying this ... */
/*       TAILQ_FOREACH(desktop, &screen->s_desklist, d_list) */
/*       { */
/*          sprintf( label, "desktop %d", desktop->num ); */
/*          printf( "adding desktop %d. tsk %d\n", tasklist, desktop->num ); */
/*          tasklist_addent( tasklist, POS_LAST, ET_LABEL, label, NULL ); */
/*       } */
       }

	tasklist_size(tasklist);

	/* realize the subtasklists */
	for (i = 0; i < tasklist->subtasklist_count; i++)
   {
		// tasklist_realize(tasklist->subtasklists[i]);
   }

	return 0;

  free:
	free(tasklist->client);
	return -1;
}

/* kill a tasklist, this may be before it's realized, so a lot is conditional */
void tasklist_delete(tasklist_t *tasklist)
{
	int cnt, i;


	/* kill subtasklists */
	for (i = 0; i < tasklist->subtasklist_count; i++)
   {
		//tasklist_delete(tasklist->subtasklists[i]);
   }
	if (tasklist->subtasklists)
		free(tasklist->subtasklists);

	/* get rid of the tasklist entries */
	for (i = 0; i < tasklist->entry_count; i++)
   {
		if (tasklist->entries[i])
      {
         // bva. april 23. right here. causing the crash on exit.
			tasklist_freeent( tasklist->entries[i] );
      }
   }

	if (tasklist->entries)
   {
		free(tasklist->entries);
   }

	/* free created clients */
	if (tasklist->client)
   {
		cnt = ScreenCount(display);
		for (i = 0; i < cnt; i++)
      {
			plugin_rmcontext(tasklist->client[i]->window);
			XDeleteContext(display, tasklist->client[i]->frame, tasklist_context);
			if (tasklist->client[i])
         {
				client_rm(tasklist->client[i]);
         }
		}
		free(tasklist->client);
	}

	/* free the active subtasklist pointers */
	if (tasklist->active_sub)
		free(tasklist->active_sub);

	free(tasklist);
}

/* draw a tasklist entry */
static void tasklist_drawent(tasklist_t *tasklist, client_t *client, int i, int y) {
	int tmpx, tmpy;

	if (subtasklist_bullet && tasklist->entries[i]->type == ET_SUBTASKLIST)
   {
		tmpx = client->width - subtasklist_bullet->width;
#ifdef I18N
		tmpy = y + (((tasklistfont_extents->max_ink_extent.height) / 2) - subtasklist_bullet->height / 2);
#else
		tmpy = y + (((tasklistfont->ascent + tasklistfont->descent) / 2) - subtasklist_bullet->height / 2);
#endif

		XSetClipMask(display, tasklistscr[client->screen->num].tasklistgc,
                   subtasklist_bullet->shapemasks[client->screen->num]);
		XSetClipOrigin(display, tasklistscr[client->screen->num].tasklistgc, tmpx, tmpy);
		XCopyArea(display, subtasklist_bullet->pixmaps[client->screen->num], client->window,
                tasklistscr[client->screen->num].tasklistgc, 0, 0, subtasklist_bullet->width,
                subtasklist_bullet->height, tmpx, tmpy);
		XSetClipMask(display, tasklistscr[client->screen->num].tasklistgc, None);
	}
#ifdef I18N
	XmbDrawString(display, client->window, tasklistfont, tasklistscr[client->screen->num].tasklistgc,
                 TASKLIST_XBORDER, y + tasklistfont_extents->max_logical_extent.height * 4 / 5, tasklist->entries[i]->text, strlen(tasklist->entries[i]->text));
#else
	XDrawString( display, client->window, tasklistscr[client->screen->num].tasklistgc,
                TASKLIST_XBORDER, y + tasklistfont->ascent, tasklist->entries[i]->text, strlen(tasklist->entries[i]->text));
#endif
}

/* draw a tasklist; entidx is for use during tasklist_interact */
void tasklist_expose(tasklist_t *tasklist, client_t *client, XExposeEvent *e)
{
	int i, y, first, last;

	/*
	 * be slightly intelligent about what lines to draw; it's
	 * probably not worth the time to worry about the width
	 * of the line getting drawn...
	 */
	y = TASKLIST_YBORDER;
	last = first = -1;
	for (i = 0; i < tasklist->entry_count; i++) {
#ifdef I18N
		y += tasklistfont_extents->max_ink_extent.height;
#else
		y += tasklistfont->ascent + tasklistfont->descent;
#endif
		if (y > e->y && first == -1)
			first = i - 1;
		if (y > e->y + e->height && last == -1)
			last = i;
	}

	/* now we draw the lines that need it */
	if (first < 0) first = 0;
	if (last == -1) last = tasklist->entry_count - 1;
#ifdef I18N
	y = TASKLIST_YBORDER + (tasklistfont_extents->max_ink_extent.height) * first;
#else
	y = TASKLIST_YBORDER + (tasklistfont->ascent + tasklistfont->descent) * first;
#endif
	for (i = first; i < last + 1; i++) {
		tasklist_drawent(tasklist, client, i, y);
#ifdef I18N
		y += tasklistfont_extents->max_ink_extent.height;
#else
		y += tasklistfont->ascent + tasklistfont->descent;
#endif
	}
}

/* perform an action for a tasklist entry */
static void tasklist_action( tasklist_t *tasklist,
                             tasklistent_t *ent,
                             int snum )
{
   client_t * client;

	switch (ent->type)
   {
      case ET_TASK:
         client = (client_t *) ent->dat.dat;
         if (client->state == NormalState)
         {
            // make this configurable ...
            //client->x = 100;
            //client->y = 100;

            if( client->x < 0 )
            {
               while( client->x < 0 )
               {
                  client->x = client->x + client->screen->width;
               }
            }
            if( client->y < 0 )
            {
               while( client->y < 0 )
               {
                  client->y = client->y + client->screen->height;
               }
            }
            if( client->x > client->screen->width )
            {
               while( client->x > client->screen->width )
               {
                  client->x =  client->x - client->screen->width;
               }
            }
            if( client->y > client->screen->height )
            {
               while( client->y > client->screen->height )
               {
                  client->y = client->y -  client->screen->height;
               }
            }

            client_sizeframe(client);
            action_send_config(client);


            workspace_add_bypos( client->screen->desktop, client );

            log( 2, "client is now on desk: %d", client->workspace->desktop->num );
            log( 2, "client x,y: %d,%d\n", client->x, client->y );

            XMapWindow(display, client->frame);

            stacking_raise(client);
            focus_setfocused(client);
            plugin_geometry_change( client );
         }
         else
         {
            action_restore( client );
         }

         break;
	}
}

/* open a tasklist */
static void tasklist_open(client_t *client, int x, int y)
{
	client->x = x;
	client->y = y;
	XMoveWindow(display, client->frame, client->x, client->y);
	workspace_add_client(client->screen->desktop->current_space, client);
	desktop_add_client(client);
	XMapWindow(display, client->frame);
	stacking_raise(client);
	client->state = NormalState;
}

/* close a tasklist */
static void tasklist_close(tasklist_t *tasklist, client_t *client)
{
	int snum = client->screen->num;

	/* close any open subtasklists */
	if (tasklist->active_sub[snum])
   {
		tasklist_close(tasklist->active_sub[snum], tasklist->active_sub[snum]->client[snum]);
		tasklist->active_sub[snum] = NULL;
	}
	if (client->workspace)
   {
		desktop_rm_client(client);
		workspace_rm_client(client);
	}
	XUnmapWindow(display, client->frame);
	client->state = WithdrawnState;
}

/* draw (or erase) a highlight */
static void drawhighlight(client_t *client, int idx)
{
#ifdef I18N
	XFillRectangle( display, client->window, client->screen->xorgc, 2,
                   idx * (tasklistfont_extents->max_ink_extent.height) + TASKLIST_YBORDER,
                   client->width - 5, tasklistfont_extents->max_ink_extent.height );
#else
	XFillRectangle( display, client->window, client->screen->xorgc, 2,
                   idx * (tasklistfont->ascent + tasklistfont->descent) + TASKLIST_YBORDER,
                   client->width - 5, tasklistfont->ascent + tasklistfont->descent);
#endif
}

/* open subtasklists or close them as neccessary while dragging across */
static void passopen(tasklist_t *tasklist, client_t *client, int entidx, int *oldentidx, int snum)
{
   int xopen, yopen;
   client_t *tclient;


	if( entidx < tasklist->entry_count &&
       tasklist->entries[entidx]->type == ET_SUBTASKLIST )
   {
		if (tasklist->active_sub[snum] != tasklist->subtasklists[tasklist->entries[entidx]->dat.subtasklist]) {
			if (tasklist->active_sub[snum])
				tasklist_close(tasklist, tasklist->active_sub[snum]->client[snum]);
			tasklist->active_sub[snum] = tasklist->subtasklists[tasklist->entries[entidx]->dat.subtasklist];
#ifdef I18N
			tasklist_open(tasklist->active_sub[snum]->client[snum],
                       client->x + FULLWIDTH(client),
                       client->y + TASKLIST_YBORDER + (entidx * (tasklistfont_extents->max_ink_extent.height)));
#else

         tclient = tasklist->active_sub[snum]->client[snum];

         xopen = client->x + FULLWIDTH(client);
         yopen = client->y + TASKLIST_YBORDER + (entidx * (tasklistfont->ascent + tasklistfont->descent));

         // NOTE: all this magic should be an option like: "keep on screen" AND put in a
         //  function!

         if( yopen + FULLHEIGHT(tclient) > client->screen->height )
         {
            yopen -= (yopen + FULLHEIGHT(tclient)) - client->screen->height;
         }

         if( xopen + FULLWIDTH(client) > client->screen->width )
         {
            log( 4, "We would be off the screen in the x direction" );
            xopen -= (xopen + FULLWIDTH(tclient)) - client->screen->width;
         }
         if( xopen < 0 )
         {
            log( 4, "We would be off the screen in the negative x direction" );
            xopen = 0;
         }

         tasklist_open( tasklist->active_sub[snum]->client[snum],
                        xopen,
                        yopen );

			/* tasklist_open( tasklist->active_sub[snum]->client[snum], */
/*                     client->x + FULLWIDTH(client), */
/*                     client->y + TASKLIST_YBORDER + (entidx * (tasklistfont->ascent + tasklistfont->descent))); */
#endif
		}
		*oldentidx = -1;
	}
   else
   {
		drawhighlight(client, entidx);
		if (tasklist->active_sub[snum])
      {
			tasklist_close(tasklist, tasklist->active_sub[snum]->client[snum]);
			tasklist->active_sub[snum] = NULL;
		}
		*oldentidx = entidx;
	}
}

/* main tasklist operation routine, e == NULL if not from a keypress */
static void tasklist_interact(tasklist_t *tasklist, client_t *client, XButtonEvent *butev)
{
	XEvent event;
	XMotionEvent *e;
	tasklist_t *tmptasklist;
	Window lastsubwin, dumwin;
	int xpos, ypos, snum;
	int oldentidx, entidx, mask;
	int moving;

	/* grab the pointer */
	mask = ButtonReleaseMask | PointerMotionMask | ButtonMotionMask;
	if (XGrabPointer(display, client->screen->root, 0, mask, GrabModeAsync,
                    GrabModeAsync, client->screen->root, None, CurrentTime) != GrabSuccess)
		return;
	stacking_raise(client);
	snum = client->screen->num;

	/* set the entry index var if we got a event structure */
	if (butev) {
#ifdef I18N
		oldentidx = entidx = (butev->y - TASKLIST_YBORDER)
          / (tasklistfont_extents->max_ink_extent.height);
#else
		oldentidx = entidx = (butev->y - TASKLIST_YBORDER)
          / (tasklistfont->ascent + tasklistfont->descent);
#endif
		if (entidx >= 0 && entidx < tasklist->entry_count) {
			XSync(display, 0);
			while (XCheckMaskEvent(display, ExposureMask, &event))
         {
				event_handle(&event);
         }

         passopen(tasklist, client, entidx, &oldentidx, snum);

		} else {
			oldentidx = entidx = -1;
		}
	} else {
		oldentidx = entidx = -1;
	}

	moving = 0;
	lastsubwin = -1;
	mask |= ExposureMask;
	while (1) {
		XMaskEvent(display, mask, &event);

		switch (event.type) {
         case MotionNotify:
            e = &event.xmotion;
            moving = 1;

            /* see if we moved to another window */
            if (e->subwindow != lastsubwin && e->subwindow != client->frame) {
               lastsubwin = e->subwindow;
               if (oldentidx != -1)
                  drawhighlight(client, oldentidx);
               if (e->subwindow == None
                   || XFindContext(display, e->subwindow, tasklist_context, (XPointer *) &tmptasklist) != 0) {
                  oldentidx = entidx = -1;
                  break;
               }
               if (tasklist->active_sub[snum] && tasklist->active_sub[snum]->client[snum]->frame != e->subwindow)
                  tasklist_close(tasklist, tasklist->active_sub[snum]->client[snum]);
               tasklist = tmptasklist;
               client = tasklist->client[snum];
               oldentidx = -1;
            }

            /* get cordinates and erase old highlights, draw new if needed */
            XTranslateCoordinates(display, e->root, client->window, e->x_root, e->y_root,
                                  &xpos, &ypos, &dumwin);
            if (ypos >= client->height - TASKLIST_YBORDER || ypos <= TASKLIST_YBORDER || xpos <= TASKLIST_XBORDER
                || xpos >= client->width - TASKLIST_XBORDER) {
               if (oldentidx != -1)
                  drawhighlight(client, oldentidx);
               entidx = oldentidx = -1;
               break;
            }

#ifdef I18N
            entidx = (ypos - TASKLIST_YBORDER) / (tasklistfont_extents->max_ink_extent.height);
#else
            entidx = (ypos - TASKLIST_YBORDER) / (tasklistfont->ascent + tasklistfont->descent);
#endif
            if (entidx != oldentidx) {
               if (oldentidx != -1)
                  drawhighlight(client, oldentidx);
               passopen(tasklist, client, entidx, &oldentidx, snum);
            } else
               oldentidx = entidx;
            break;
         case ButtonRelease:
            XUngrabPointer(display, CurrentTime);
            if (oldentidx != -1)
               drawhighlight(client, oldentidx);
            if (entidx != -1) {
               if (tasklist->entries[entidx]->type == ET_SUBTASKLIST)
                  return;
               tasklist_action(tasklist, tasklist->entries[entidx], snum);

               /*
                * for now, always close when doing an action.  at some point
                * I may do break-off-able tasklists and such
                */
               tasklist_close(tasklist->forefather, tasklist->forefather->client[snum]);
               return;
            }
            if (moving)
               tasklist_close(tasklist->forefather, tasklist->forefather->client[snum]);
            return;
         case Expose:
            event_handle(&event);
            break;
		}
	}
}

/* open or close a tasklist */
void tasklist_use(tasklist_t *tasklist, screen_t *screen)
{
	Window dumwin;
	client_t *client;
	int dumint, x, y;
   unsigned int dumuint;
   int yopen, xopen;

   log( 2, "tasklist_use" );

	client = tasklist->client[screen->num];

	if (client->state == WithdrawnState)
   {
		XQueryPointer( display, screen->root, &dumwin, &dumwin, &x, &y, &dumint, &dumint,
                     &dumuint );

      // x,y tell us where we are with the pointer
      // screen->width, screen->height tells us how big things are ...
      // client->width, client->height tells us how big the tasklist is ...
      log( 4, "we are at: %d,%d", x, y );
      log( 4, "opening a tasklist .....");

//		tasklist_open( client, x - client->width / 2, y);


      // 1) we can adjust where this beast opens ...
      xopen = x - client->width / 2;
      yopen = y;

      // NOTE: all this magic should be an option like: "keep on screen"
      if( y + client->height > screen->height )
      {
         log( 4, "We would be off the screen in the y direction" );
         yopen = y - ((y + client->height + 10) - screen->height);
      }
      if( x + client->width > screen->width )
      {
         log( 4, "We would be off the screen in the x direction" );
         xopen = x - ((x + client->width + 10) - screen->width);
      }
      if( xopen < 0 )
      {
         log( 4, "We would be off the screen in the negative x direction" );
         xopen = 0;
      }

      tasklist_open( client, xopen, yopen );

      // 2) we can warp the tasklist ala windowmaker in here
		tasklist_interact( tasklist, client, NULL );
	}
   else
   {
		/* need an if it's moved thing */
		tasklist_close(tasklist, client);
	}
}

/* a click on a tasklist, go into interaction with the tasklist */
void tasklist_click(tasklist_t *tasklist, client_t *client, XButtonEvent *e)
{
	tasklist_interact(tasklist, client, e);
}
