/*
 * client.cc
 * Copyright (C) 2000 Frank Hale
 * frankhale@yahoo.com
 * http://sapphire.sourceforge.net/
 *
 * Updated: 15 Jan 2002
 *
 * 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 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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 */
 
#include "aewm.hh"

using namespace std;

Client::Client(Display *d, Window new_client, list<Client *> *l)
{
	initialize(d);
	l->push_back(this);
	make_new_client(new_client);
}

Client::~Client()
{
	remove_client();
}

void Client::initialize(Display *d)
{
	dpy 			= d;

	name			= NULL;
	//icon_name		= NULL;
	    	
	window 			= None;
	frame  			= None;
	title			= None;
	trans  			= None;

	window_menu 		= NULL;
	
    	x      			= 1; 
	y      			= 1;
	width  			= 1;
	height 			= 1;
    	ignore_unmap		= 0;

	pointer_x		= 0;
	pointer_y		= 0;

	old_cx			= 0;
	old_cy			= 0;

	wire_move		= wm->getWireMove();

#ifdef SHAPE
   	has_been_shaped 	= false;
#endif
	
	has_title		= true;
	has_border		= true;
	has_focus 		= false;

	is_iconified 		= false;

	// Extra Window States
	is_shaded		= false;
	is_maximized		= false;
	is_maximized_vertical	= false;
	is_maximized_horizontal	= false;
	is_sticky		= false;
	is_always_on_top	= false;

	client_strut 		= new Strut;

	client_strut->east	= 0;
	client_strut->west	= 0;
	client_strut->north	= 0;
	client_strut->south	= 0;

	has_strut=false;

	has_extended_net_name	= false;
	skip_taskbar		= false;
	skip_pager		= false;

	last_button1_time 	= 0;
	old_x			= 0;
	old_y	   		= 0;
	old_width  		= 1;
	old_height 		= 1;

	direction		= 0;
	ascent			= 0;
	descent 		= 0;
	text_width 		= 0;
	text_justify		= 0;
	justify_style 		= wm->getTextJustify();

	screen 			= DefaultScreen(dpy);
	root			= wm->getRootWindow();

	xres			= wm->getXRes();
	yres			= wm->getYRes();
}

void Client::getXClientName()
{
	//if (name) XFree(name);

	name = new char[256];

	wm->getExtendedWMHintString(window, wm->atom_extended_net_wm_name, &name);
	
	
	// _NET_WM_NAME isn't set fallback to XA_WM_NAME
	if(name==NULL)
	{
    		XFetchName(dpy, window, &name);
		
		if(name==NULL) 
		{
			XStoreName(dpy, window, "no name");
			XFetchName(dpy, window, &name);
		}
	} 
	else 
		has_extended_net_name=true;

	if(name!=NULL)
	{
		XTextExtents(wm->getFont(), name , strlen(name), 
			&direction, &ascent, 
            		&descent, &overall);
	
		text_width = overall.width;
	}
}

/*
// We aren't using Icon name yet so its commented out!
void Client::getXIconName()
{
	if(icon_name) XFree(icon_name);
	
	XGetIconName(dpy, w, &icon_name);
	
	if(icon_name==NULL)
	{
		XSetIconName(dpy, w, "no name");
		XGetIconName(dpy, w, &icon_name);
	}
}
*/

// Set up a client structure for the new (not-yet-mapped) window. The
// confusing bit is that we have to ignore 2 unmap events if the
// client was already mapped but has IconicState set (for instance,
// when we are the second window manager in a session).  That's
// because there's one for the reparent (which happens on all viewable
// windows) and then another for the unmapping itself. 
//
void Client::make_new_client(Window w)
{
  	XWindowAttributes attr;
    	XWMHints *hints;
    	MwmHints *mhints;

    	long dummy;

    	XGrabServer(dpy);

	window = w;

	getXClientName();
	//getXIconName();

	XGetTransientForHint(dpy, window, &trans);

    	XGetWindowAttributes(dpy, window, &attr);

    	x = attr.x;
    	y = attr.y;
    	width = attr.width;
    	height = attr.height;
    	cmap = attr.colormap;
    	size = XAllocSizeHints();
    	XGetWMNormalHints(dpy, window, size, &dummy);

    	old_x		= x;
    	old_y		= y;
    	old_width 	= width;
    	old_height 	= height;

    	if ((mhints = get_mwm_hints(window))) 
	{
        	if (mhints->flags & MwmHintsDecorations && !(mhints->decorations & MwmDecorAll)) 
		{
            		has_title  = mhints->decorations & MwmDecorTitle;
            		has_border = mhints->decorations & MwmDecorBorder;
		}
        
		XFree(mhints);
    	}

    	if (attr.map_state == IsViewable) ignore_unmap++;

	init_position();

       	if ((hints = XGetWMHints(dpy, w))) 
	{
		if (hints->flags & StateHint) 
			set_wm_state(hints->initial_state);
		else
			set_wm_state(NormalState);

		XFree(hints);
	}

	window_menu = wm->getWindowMenu();

    	gravitate(APPLY_GRAVITY);
    	reparent();

	NetWMStates *win_states;
	win_states = wm->getExtendedNetWMStates(window);

	if(win_states->sticky) is_sticky=true;
	if(win_states->shaded) shade();
	if(win_states->max_vert && win_states->max_horz) maximize();
	if(win_states->skip_taskbar) skip_taskbar=true;
	if(win_states->skip_pager) skip_pager=true;
	
	delete win_states;

    	if(!trans) wm->updateClientList();

	belongsToDesktop = wm->findGnomeDesktopHint(window);
	
	if(belongsToDesktop == -1)
	{
		belongsToDesktop = wm->findExtendedDesktopHint(window);	
	}

	if(belongsToDesktop == -1)
        {
		belongsToDesktop = wm->getCurrentDesktop();
        }
	
	wm->setGnomeHint(window, wm->atom_gnome_win_workspace, belongsToDesktop);

	wm->setExtendedWMHint(window, wm->atom_extended_net_wm_desktop, belongsToDesktop);

    	if(wm->getHint(window, wm->atom_gnome_win_state)&WIN_STATE_STICKY) is_sticky=true;
	if(wm->getHint(window, wm->atom_gnome_win_hints)&WIN_HINTS_DO_NOT_COVER) is_always_on_top=true;

	// Do we want a strut?
	int num=0;
	CARD32 *temp_strut = NULL;
	temp_strut = (CARD32*) wm->getExtendedNetPropertyData(window, wm->atom_extended_net_wm_strut, XA_CARDINAL, &num);

	if(temp_strut) 
	{	
		client_strut->west = temp_strut[0];
		client_strut->east = temp_strut[1];
		client_strut->north = temp_strut[2];
		client_strut->south = temp_strut[3];
			
		wm->addStrut(client_strut);
			
		has_strut=true;									
	}
	
	// Actually updates both gnome and net client lists.
	update_net_wm_states();

	if (get_wm_state() == NormalState || get_wm_state() == WithdrawnState)
	{
		XMapWindow(dpy, window);
		XMapWindow(dpy, title);
		XMapWindow(dpy, frame);

		wm->restackOnTopWindows();

		is_iconified=false;
       	} 
	else if (get_wm_state() == IconicState) iconify();
	
	XSync(dpy, False);
    	XUngrabServer(dpy);
}

void Client::remove_client()
{
    	XGrabServer(dpy);

	XUngrabButton(dpy, AnyButton, AnyModifier, frame);

	gravitate(REMOVE_GRAVITY);
	XReparentWindow(dpy, window, root, x, y);
	
	XRemoveFromSaveSet(dpy,window);
	
	XDestroyWindow(dpy, title);
	XDestroyWindow(dpy, frame);
	
	if (name) XFree(name);
	//if (icon_name) XFree(icon_name);
	if (size) XFree(size);

    	XSync(dpy, False);
    	XUngrabServer(dpy);

	if(has_strut)
		wm->removeStrut(client_strut);
			
	delete client_strut;

    	window_menu->hide();

    	wm->getClientList()->remove(this);
	wm->updateClientList();
}

/* For a regular window, trans is None (false), and we include
 * enough space to draw the title. For a transient window we just make
 * a tiny strip. */
int Client::theight()
{
    	if (!has_title) return 0;

    	return (trans ? 0 : wm->getFont()->ascent + wm->getFont()->descent) + 2*SPACE + BW;
}

/* Attempt to follow the ICCCM by explicity specifying 32 bits for
 * this property. Does this goof up on 64 bit systems? */
void Client::set_wm_state(int state)
{
    	CARD32 data[2];

    	data[0] = state;
    	data[1] = None; /* Icon? We don't need no steenking icon. */

    	XChangeProperty(dpy, window, wm->atom_wm_state, wm->atom_wm_state,
        	32, PropModeReplace, (unsigned char *)data, 2);
}

/* If we can't find a wm->wm_state we're going to have to assume
 * Withdrawn. This is not exactly optimal, since we can't really
 * distinguish between the case where no WM has run yet and when the
 * state was explicitly removed (Clients are allowed to either set the
 * atom to Withdrawn or just remove it... yuck.) */
long Client::get_wm_state()
{
    	Atom real_type; int real_format;
    	unsigned long items_read, items_left;
    	long *data, state = WithdrawnState;

    	if (XGetWindowProperty(dpy, window, wm->atom_wm_state, 0L, 2L, False,
            wm->atom_wm_state, &real_type, &real_format, &items_read, &items_left,
            (unsigned char **) &data) == Success && items_read) {
        	state = *data;
        	XFree(data);
    	}
    
    	return state;
}

// This is called whenever we update our Client stuff. 
void Client::send_config()
{
	XConfigureEvent ce;

	ce.type = ConfigureNotify;
	ce.event = window;
	ce.window = window;
	ce.x = x;
	ce.y = y;
	ce.width = width;
	ce.height = height;
	ce.border_width = 0;
	ce.above = (is_always_on_top) ? Above : Below;
	ce.override_redirect = False;

	XSendEvent(dpy, window, False, StructureNotifyMask, (XEvent *)&ce);
}

void Client::redraw()
{
	if (!has_title) return;

    	if( has_focus ) XSetForeground(dpy, wm->getStringGC(), wm->getFGColor().pixel);
	else XSetForeground(dpy, wm->getStringGC(), wm->getBDColor().pixel);
    
	XDrawLine(dpy, title, wm->getBorderGC(),
		0, theight() - BW + BW/2,
		width, theight() - BW + BW/2);

	XDrawLine(dpy, title, wm->getBorderGC(),
		width - theight()+ BW/2, 0,
		width - theight()+ BW/2, theight());

	XPoint points[4];
	points[0].x = width - 3*theight()/4 + BW;
	points[0].y = 2*theight()/5 - BW;
	points[1].x = width - theight()/4 - BW;
	points[1].y = points[0].y;
	points[2].x = width - theight()/2 - BW/2;
	points[2].y = 3*theight()/5 + BW;
	points[3].x = points[0].x;
	points[3].y = points[0].y;

	XFillPolygon(dpy, title, wm->getBorderGC(), points, 4, Convex, CoordModeOrigin);

    	if (!trans && name)
    	{
		switch(justify_style)
		{
			case LEFT_JUSTIFY:
				text_justify = SPACE;
			break;

			case CENTER_JUSTIFY:
				text_justify = ( (width / 2) - (text_width / 2) );
			break;

			case RIGHT_JUSTIFY:
				text_justify = width - text_width - 25;
			break;
		}

		if(name!=NULL)
		{
			XDrawString(dpy, title, wm->getStringGC(),
				text_justify, SPACE + wm->getFont()->ascent,
				name, strlen(name));
		}
	}
}

/* Window gravity is a mess to explain, but we don't need to do much
 * about it since we're using X borders. For NorthWest et al, the top
 * left corner of the window when there is no WM needs to match up
 * with the top left of our fram once we manage it, and likewise with
 * SouthWest and the bottom right (these are the only values I ever
 * use, but the others should be obvious.) Our titlebar is on the top
 * so we only have to adjust in the first case. */
void Client::gravitate(int multiplier)
{
    	int dy = 0;
    	int gravity = (size->flags & PWinGravity) ?
        	size->win_gravity : NorthWestGravity;

    	switch (gravity) {
        	case NorthWestGravity:
        	case NorthEastGravity:
        	case NorthGravity: dy = theight(); break;
        	case CenterGravity: dy = theight()/2; break;
    	}

   	y += multiplier * dy;
}

/* Well, the man pages for the shape extension say nothing, but I was
 * able to find a shape.PS.Z on the x.org FTP site. What we want to do
 * here is make the window shape be a boolean OR (or union, if you
 * prefer) of the client's shape and our titlebar. The titlebar
 * requires both a bound and a clip because it has a border -- the X
 * server will paint the border in the region between the two. (I knew
 * that using X borders would get me eventually... ;-)) */
#ifdef SHAPE
void Client::set_shape()
{
	int n, order;
    	XRectangle temp, *dummy;

    	dummy = XShapeGetRectangles(dpy, window, ShapeBounding, &n, &order);
	
    	if (n > 1) {
        	XShapeCombineShape(dpy, frame, ShapeBounding,
	            0, theight(), window, ShapeBounding, ShapeSet);

        	temp.x = -BW;
	        temp.y = -BW;
        	temp.width = width + 2*BW;
	        temp.height = theight() + BW;
        	
		XShapeCombineRectangles(dpy, frame, ShapeBounding,
	            0, 0, &temp, 1, ShapeUnion, YXBanded);
        	
		temp.x = 0;
	        temp.y = 0;
        	temp.width = width;
	        temp.height = theight() - BW;
        	
		XShapeCombineRectangles(dpy, frame, ShapeClip,
	            0, theight(), &temp, 1, ShapeUnion, YXBanded);
        	
		has_been_shaped = 1;
	} 
	else 
	if (has_been_shaped) 
	{
	        /* I can't find a 'remove all shaping' function... */
	        temp.x = -BW;
        	temp.y = -BW;
	        temp.width = width + 2*BW;
        	temp.height = height + theight() + 2*BW;
	        
		XShapeCombineRectangles(dpy, frame, ShapeBounding,
        	    0, 0, &temp, 1, ShapeSet, YXBanded);
    	}
    
    	XFree(dummy);
}
#endif

void Client::resize()
{
    	sweep();
	
	XMoveResizeWindow(dpy, frame,
        	x, y - theight(), width, height + theight());

	XResizeWindow(dpy, title, width, theight());
    
    	XMoveResizeWindow(dpy, window,
        	0, theight(), width, height);
    
    	send_config();
}

void Client::iconify()
{
	if (!ignore_unmap) ignore_unmap++;

	if(has_focus) set_focus(false);

	XUnmapWindow(dpy, window);
	XUnmapWindow(dpy, title);
	XUnmapWindow(dpy, frame);

	is_iconified=true;

	set_wm_state(IconicState);

	if(!trans)
	{
		wm->addClientToIconMenu(this);
		wm->findTransientsToMapOrUnmap(window, true);
	}
}

void Client::hide()
{
	if (!ignore_unmap) ignore_unmap++; 

	if(has_focus) set_focus(false);

	if(window_menu->is_visible()) window_menu->hide();

	XUnmapWindow(dpy, window);
	XUnmapWindow(dpy, title);
	XUnmapWindow(dpy, frame);
	
	set_wm_state(WithdrawnState);
}

void Client::unhide()
{
	if(belongsToDesktop == wm->getCurrentDesktop())
	{
		XMapRaised(dpy, window);
		XMapRaised(dpy, title);
		XMapRaised(dpy, frame);

		if(is_iconified)
		{
			is_iconified=false;
			wm->removeClientFromIconMenu(this);
		} 

		set_wm_state(NormalState);

		if(!trans) wm->findTransientsToMapOrUnmap(window, false);

		wm->restackOnTopWindows();	
	}
}

/* The name of this function is a bit misleading: if the client
 * doesn't listen to WM_DELETE then we just terminate it with extreme
 * prejudice. */
void Client::send_wm_delete()
{
    	int i, n, found = 0;
    	Atom *protocols;

    	if (XGetWMProtocols(dpy, window, &protocols, &n)) {
        	for (i=0; i<n; i++) if (protocols[i] == wm->atom_wm_delete) found++;
        	XFree(protocols);
    	}
    	if (found) 
		send_xmessage(window, wm->atom_wm_protos, NoEventMask, wm->atom_wm_delete);
    	else XKillClient(dpy, window);
}

// This function sets up the initial position of windows when they are mapped
// for the first time. It still needs some work done to it to make it more 
// aware of _NET_WM_STRUT's
// 
//
//	XSizeHints structure definition
// 
// 	typedef struct {
//            long flags;
//            int x, y;
//            int width, height;
//            int min_width, min_height;
//            int max_width, max_height;
//            int width_inc, height_inc;
//            struct {
//                   int x;
//                   int y;
//            } min_aspect, max_aspect;
//            int base_width, base_height;
//            int win_gravity;
//
//       } XSizeHints;
//
void Client::init_position()
{
    	int mouse_x, mouse_y;

	Strut *temp_strut = wm->getMasterStrut();

	unsigned int w, h;
	unsigned int border_width, depth;

  	XWindowAttributes attr;

    	XGetWindowAttributes(dpy, window, &attr);

	// If the window is mapped already we want
	// to leave it alone!
	if (attr.map_state == IsViewable) return;

	XGetGeometry(dpy, window, &root, &x, &y, &w, &h, &border_width, &depth);

	width = (int)w;
	height = (int)h;

	if(x+width>xres) width  -= (width - xres);
	if(y+height>yres) { height -= (height - yres); }

	// Let's be strut concious here, hehe =)
	fixupPositionBasedOnStruts(temp_strut);

    	if (size->flags & PPosition)
	{
        	if(!x) x = size->x;
        	if(!y) y = size->y;

		// Let's be strut concious here, hehe =)
		fixupPositionBasedOnStruts(temp_strut);
    	}
	else
	{
		if (size->flags & USPosition)
		{
			if(!x) x = size->x;
            		if(!y) y = size->y;

			// Let's be strut concious here, hehe =)
			fixupPositionBasedOnStruts(temp_strut);
		}
		else
		if ( (x==0) || (y==0) )
    		{
			if( width>=xres && height>=yres	)
			{
				is_always_on_top=true;

				x=0;
				y=0;
				width=xres;
				height=yres-theight();

				if(!trans) wm->updateClientList();
			}
			else
			{
				wm->getMousePosition(&mouse_x, &mouse_y);

				if(mouse_x && mouse_y)
				{
					x = (int) (((long) (xres - width) 
		      				* (long) mouse_x) / (long) xres);
	 				y = (int) (((long) (yres - height - theight())
		      				* (long) mouse_y) / (long) yres);
	 				y = (y<theight()) ? theight() : y;

					// Let's be strut concious here, hehe =)
					// This isn't the end all and be all of
					// being strut conscious. This doesn't account
					// for some windows.

					fixupPositionBasedOnStruts(temp_strut);

	         			gravitate(REMOVE_GRAVITY);
				}
			}
    		}
    	}
}

void Client::fixupPositionBasedOnStruts(Strut* temp_strut)
{
	// This has been totally rewritten.  The basic aims are:-
	//   1. Avoid going under the north strut (else we lose the titlebar)
	//   2. Avoid going under struts
	// To do this we ensure 2, then ensure 1.
	// If ensuring 1 causes 2 to be broken, too bad!
	bool size_changed=false;

	x = ((unsigned int)x < temp_strut->west) ? (temp_strut->west) : x;
	x = (((unsigned int)x + (unsigned int)width) > ((unsigned int)xres - temp_strut->east)) ? (xres - (temp_strut->east) - width) : x;
	y = (((unsigned int)y + (unsigned int)height) > ((unsigned int)yres - temp_strut->south)) ? (yres - height - (temp_strut->south)) : y;
	y = (((unsigned int)y - (unsigned int)theight()) < temp_strut->north) ? ((temp_strut->north) + theight()) : y;

	if(width > xres)
	{
		width = xres - temp_strut->east - x;
		size_changed=true;
	}
	
	if(height > yres)
	{
		height = yres - temp_strut->south - y;
		size_changed=true;
	}
	
	if(size_changed) send_config();
}

void Client::drag()
{
	XEvent ev;
    	int nx=0, ny=0;
    	//old_cx = x;
    	//old_cy = y;
    
    	//XGrabServer(dpy);

    	draw_outline();

    	for (;;)
    	{
    		XMaskEvent(dpy, PointerMotionMask|ButtonReleaseMask, &ev);		
		draw_outline();
    		XUngrabServer(dpy);
		XSync(dpy, False);

		switch(ev.type)
		{
			case MotionNotify:
			{

				nx = old_cx + (ev.xmotion.x_root - pointer_x);
				ny = old_cy + (ev.xmotion.y_root - pointer_y);

				if(wm->getEdgeSnap())
				{
					// Move beyond edges of screen
					if(nx == xres - width) nx = xres - width + 1;
					else if(nx == 0) nx = -1;

					if(ny == yres - SNAP) ny = yres - SNAP - 1;
					else if(ny == theight()) ny = theight() - 1;

					// Snap to edges of screen
					if( (nx + width >= xres - SNAP) && (nx + width <= xres) ) nx = xres - width;
					else if( (nx <= SNAP) && (nx >= 0) ) nx = 0;

					if(is_shaded)
					{
				 		if( (ny  >= yres - SNAP) && (ny  <= yres) ) ny = yres;
				 		else if( (ny - theight() <= SNAP) && (ny - theight() >= 0)) ny = theight();
					}
					else
					{
						if( (ny + height >= yres - SNAP) && (ny + height <= yres) ) ny = yres - height;
						else if( (ny - theight() <= SNAP) && (ny - theight() >= 0)) ny = theight();
					}
				}

				x=nx; y=ny;

			}
			break;

			case ButtonRelease:
			{
				XMoveWindow(dpy, frame, x, y-theight());
				send_config();
				return;
			}
		}
    		//XGrabServer(dpy);
		draw_outline();
    	}
}

void Client::stick(int sticky_trans)
{
	if (sticky_trans == DISALLOW_STICKY_TRANS && trans)
		return;

	if(! is_sticky)
		is_sticky = true;
	else
		is_sticky = false;

	send_config();

	update_net_wm_states();

	// if we've unset the sticky hint, make sure the window and all
	// of its child windows are placed on the desktop it was unstuck
	// to (so we can move a window to a different workspace by making
	// it sticky, moving to that workspace, and making it unsticky)

	if(! is_sticky)
	{
		set_desktop(wm->getCurrentDesktop());
		wm->updateClientList();
		wm->setGnomeHint(window, wm->atom_gnome_win_workspace, belongsToDesktop);
		wm->setExtendedWMHint(window, wm->atom_extended_net_wm_desktop, belongsToDesktop);
	}

	if(!trans) wm->findTransientsToStickOrUnstick(window, is_sticky);
}

void Client::maximize()
{
	if(trans) return;

	if(is_shaded) 
		shade();

	if(is_maximized_vertical && ! is_maximized)
		maximize_vertical();

	if(is_maximized_horizontal && ! is_maximized)
		maximize_horizontal();

	if(! is_maximized)
	{
		old_x=x;
		old_y=y;
		old_width=width;
		old_height=height;

		// Check to see if this client sets
		// its max size property. If so don't
		// maximize it past that size.
		if (size->flags & PMaxSize) {

			width = size->max_width;
	        	height = size->max_height;

			XMoveResizeWindow(dpy, frame, x, y-theight(), width, height+theight());

	    	} else {

			x=0;
			y=0;
			width=xres;
			height=yres;

			// MAKE SURE WE DON'T MAXIMIZE OVER A STRUT
			Strut *temp_strut = wm->getMasterStrut();

			x = temp_strut->west;
			y = temp_strut->north;
			width = xres - temp_strut->east - x;
			height = yres - temp_strut->south - y;

			XMoveResizeWindow(dpy, frame, x, y, width, height);

			y = theight();
			height -= theight();
		}

		is_maximized=true;

		is_maximized_vertical=true;
		is_maximized_horizontal=true;

	} else {

		x=old_x;
		y=old_y;
		width=old_width;
		height=old_height;

		XMoveResizeWindow(dpy, frame,
        		old_x, old_y - theight(), old_width, old_height + theight());

		is_maximized=false;

		is_maximized_vertical=false;
		is_maximized_horizontal=false;

		if(is_shaded) is_shaded=false;
	}

	XResizeWindow(dpy, title, width, theight());
	XResizeWindow(dpy, window, width, height);

	send_config();

	update_net_wm_states();
}

void Client::maximize_vertical()
{
	if(trans) return;

	if(is_shaded) 
		shade();

	if(is_maximized)
		maximize();

	if(is_maximized_horizontal)
		maximize_horizontal();

	if(! is_maximized_vertical)
	{
		old_y=y;
		old_height=height;

		// Check to see if this client sets
		// its max size property. If so don't
		// maximize it past that size.
		if (size->flags & PMaxSize) {

	        	height = size->max_height;

			XMoveResizeWindow(dpy, frame, x, y-theight(), width, height+theight());

	    	} else {

			y=0;
			height=yres;

			// MAKE SURE WE DON'T MAXIMIZE OVER A STRUT
			Strut *temp_strut = wm->getMasterStrut();

			y = temp_strut->north;
			height = yres - temp_strut->south - y;

			XMoveResizeWindow(dpy, frame, x, y, width, height);

			y = theight();
			height -= theight();
		}

		is_maximized=false;

		is_maximized_vertical=true;
		is_maximized_horizontal=false;

	} else {

		y=old_y;
		height=old_height;

		XMoveResizeWindow(dpy, frame,
        		x, old_y - theight(), width, old_height + theight());

		is_maximized=false;

		is_maximized_vertical=false;
		is_maximized_horizontal=false;

		if(is_shaded) is_shaded=false;
	}

	XResizeWindow(dpy, title, width, theight());
	XResizeWindow(dpy, window, width, height);

	send_config();

	update_net_wm_states();
}

void Client::maximize_horizontal()
{
	if(trans) return;

	if(is_shaded) 
		shade();

	if(is_maximized)
		maximize();

	if(is_maximized_vertical)
		maximize_vertical();

	if(! is_maximized_horizontal)
	{
		old_x=x;
		old_width=width;

		// Check to see if this client sets
		// its max size property. If so don't
		// maximize it past that size.
		if (size->flags & PMaxSize) {

			width = size->max_width;

			XMoveResizeWindow(dpy, frame, x, y-theight(), width, height+theight());

	    	} else {

			x=0;
			width=xres;

			// MAKE SURE WE DON'T MAXIMIZE OVER A STRUT
			Strut *temp_strut = wm->getMasterStrut();

			x = temp_strut->west;
			width = xres - temp_strut->east - x;

			XMoveResizeWindow(dpy, frame, x, y-theight(), width, height+theight());
		}

		is_maximized=false;

		is_maximized_vertical=false;
		is_maximized_horizontal=true;

	} else {

		x=old_x;
		width=old_width;

		XMoveResizeWindow(dpy, frame,
        		old_x, y - theight(), old_width, height + theight());

		is_maximized=false;

		is_maximized_vertical=false;
		is_maximized_horizontal=false;

		if(is_shaded) is_shaded=false;
	}

	XResizeWindow(dpy, title, width, theight());
	XResizeWindow(dpy, window, width, height);

	send_config();

	update_net_wm_states();
}

void Client::handle_motion_notify_event(XMotionEvent *ev)
{
	int nx=0, ny=0;

	if(! wire_move)
	{	
		if(ev->state & Button1Mask)
		{
			nx = old_cx + (ev->x_root - pointer_x);
			ny = old_cy + (ev->y_root - pointer_y);
		
			if(wm->getEdgeSnap())
			{
				// Move beyond edges of screen
				if(nx == xres - width) nx = xres - width + 1;		
				else if(nx == 0) nx = -1;
		
				if(ny == yres - SNAP) ny = yres - SNAP - 1;
				else if(ny == theight()) ny = theight() - 1;
				
				// Snap to edges of screen
				if( (nx + width >= xres - SNAP) && (nx + width <= xres) ) nx = xres - width;
				else if( (nx <= SNAP) && (nx >= 0) ) nx = 0;

				if(is_shaded)
				{
			 		if( (ny  >= yres - SNAP) && (ny  <= yres) ) ny = yres;
			 		else if( (ny - theight() <= SNAP) && (ny - theight() >= 0)) ny = theight();
				}
				else
				{
					if( (ny + height >= yres - SNAP) && (ny + height <= yres) ) ny = yres - height;
					else if( (ny - theight() <= SNAP) && (ny - theight() >= 0)) ny = theight();
				}
			}
		
			x=nx; y=ny;
		
			XMoveWindow(dpy, frame, nx, ny-theight());
			//send_config();
		}
		
	} else {
		
		if(ev->state & Button1Mask)
		{
 			XEvent ev;
		
			// Only call the drag function if we are 
			// actually dragging the window.
			while(1)	
			{
				XNextEvent(dpy,&ev);
			
				if(ev.type==Expose)
				{
					while(XCheckTypedEvent(dpy, Expose, &ev));
					
					// Make sure our titlebar gets repainted
					// if it needs to be.
					if(ev.xexpose.count == 0) redraw();
				}
				else										
				if(ev.type==MotionNotify)
				{
					drag();
					break;
				}
				else 
				{
					// Put all events we don't 
					// want to handle back so 
					// that the main window manager
					// event loop can handle them.
					XPutBackEvent(dpy,&ev);	
					return;
				}
			}

		}
	}
}

void Client::sweep()
{
    	XEvent ev;
    	old_cx = x;
    	old_cy = y;

    	if (!Grab(root, MouseMask, wm->getResizeCursor())) return;

    	draw_outline();
   
    	setmouse(window, width, height);
    	
	//XGrabServer(dpy);
	
	for (;;) 
	{
		XMaskEvent(dpy, MouseMask, &ev);
		draw_outline(); /* clear */
		XUngrabServer(dpy);
		switch (ev.type) 
		{
            		case MotionNotify:
				recalc_sweep(old_cx, old_cy, ev.xmotion.x, ev.xmotion.y);
                	break;

	            	case ButtonRelease:
        	        	Ungrab();
			return;
		}
		XSync(dpy,False);
		//XGrabServer(dpy);
        	draw_outline();
    	}
}

void Client::recalc_sweep(int x1, int y1, int x2, int y2)
{
	if (is_resizable())
	{
	    	width = abs(x1 - x2) - BW;
    		height = abs(y1 - y2) - BW;

	    	if(width > wm->getXRes()) width = wm->getXRes();
    		if(height > wm->getYRes()) height = wm->getYRes();

	    	get_incsize(&width, &height, PIXELS);

		if (size->flags & PMinSize) {
			if (width < size->min_width) width = size->min_width;
			if (height < size->min_height) height = size->min_height;
		}

		if (size->flags & PMaxSize) {
			if (width > size->max_width) width = size->max_width;
			if (height > size->max_height) height = size->max_height;
		}

		x = (x1 <= x2) ? x1 : x1 - width;
		y = (y1 <= y2) ? y1 : y1 - height;
	}
}

void Client::draw_outline()
{
    	char buf[32];
    	int w, h;

   	if (!get_incsize(&w, &h, INCREMENTS)) 
	{
       		w = width;
       		h = height;
	}
 	
    	if(! is_shaded)
    	{
    		XDrawRectangle(dpy, root, wm->getInvertGC(),
        		x + BW/2, y - theight() + BW/2,
        		width + BW, height + theight() + BW);

    		XDrawLine(dpy, root, wm->getInvertGC(), x + BW, y + BW/2,
        		x + width + BW, y + BW/2);

    		gravitate(REMOVE_GRAVITY);
    		snprintf(buf, sizeof buf, "%dx%d+%d+%d", width, height, x, y);
		gravitate(APPLY_GRAVITY);
    		XDrawString(dpy, root, wm->getInvertGC(),
	        	x + width - XTextWidth(wm->getFont(), buf, strlen(buf)) - SPACE,
	        	y + height - SPACE,
        		buf, strlen(buf));
    	} 
	else 
	{
		XDrawRectangle(dpy, root, wm->getInvertGC(),
        			x + BW/2, 
				y - theight() + BW/2,
        			width + BW, 
				theight() + BW);
    	}
}

int Client::is_resizable()
{
	if ((size->flags & PMinSize) && (size->flags & PMaxSize) &&
	     size->min_width == size->max_width && size->min_height ==
	     size->max_height)
		return 0;
	if ((size->flags & PResizeInc) && size->width_inc < 2 &&
	     size->height_inc < 2)
		return 0;
	return 1;
}

/* If the window in question has a ResizeInc int, then it wants to be
 * resized in multiples of some (x,y). Here we set x_ret and y_ret to
 * the number of multiples (if mode == INCREMENTS) or the correct size
 * in pixels for said multiples (if mode == PIXELS). */
int Client::get_incsize(int *x_ret, int *y_ret, int mode)
{
	int basex, basey;

    	if (size->flags & PResizeInc)
	{
		basex = (size->flags & PBaseSize) ? size->base_width :
	       	(size->flags & PMinSize) ? size->min_width : 0;

		basey = (size->flags & PBaseSize) ? size->base_height :
	       	(size->flags & PMinSize) ? size->min_height : 0;

		if (mode == PIXELS) 
		{
			*x_ret = width - ((width - basex) % ((size->width_inc == 0) ? 1 : size->width_inc));
			*y_ret = height - ((height - basey) % ((size->height_inc == 0) ? 1 : size->height_inc));
        	} 
		else /* INCREMENTS */ 
		{
			*x_ret = (width - basex) / ((size->width_inc == 0) ? 1 : size->width_inc);
			*y_ret = (width - basey) / ((size->height_inc == 0) ? 1 : size->height_inc);
		}

		return 1;
	}

	return 0;
}

/* This one does -not- free the data coming back from Xlib; it just
 * sends back the pointer to what was allocated. */
MwmHints* Client::get_mwm_hints(Window w)
{
    	Atom real_type; int real_format;
    	unsigned long items_read, items_left;
    	MwmHints *data;

    	if (XGetWindowProperty(dpy, w, wm->atom_mwm_hints, 0L, 20L, False,
            wm->atom_mwm_hints, &real_type, &real_format, &items_read, &items_left,
            (unsigned char **) &data) == Success && items_read >= PropMwmHintsElements) 
	    	{
        		return data;
		} 
		else return NULL;
}

// This function makes it so only the titlebar shows.
void Client::shade()
{
	raise();
	
	wm->restackOnTopWindows();	

	if(! is_shaded)
	{
		XResizeWindow(dpy, frame, width, theight() - 1);

		is_shaded=true;
	} else {
		
		XResizeWindow(dpy, frame, width, height + theight());
		
		is_shaded=false;
	}
	
	update_net_wm_states();
}

/* Because we are redirecting the root window, we get ConfigureRequest
 * events from both clients we're handling and ones that we aren't.
 * For clients we manage, we need to fiddle with the frame and the
 * client window, and for unmanaged windows we have to pass along
 * everything unchanged. Thankfully, we can reuse (a) the
 * XWindowChanges struct and () the code to configure the client
 * window in both cases.
 *
 * Most of the assignments here are going to be garbage, but only the
 * ones that are masked in by e->value_mask will be looked at by the X
 * server. */
void Client::handle_configure_request(XConfigureRequestEvent *e)
{
      	XWindowChanges wc;

        gravitate(REMOVE_GRAVITY);
        if (e->value_mask & CWX) x = e->x;
        if (e->value_mask & CWY) y = e->y;
        if (e->value_mask & CWWidth) width = e->width;
        if (e->value_mask & CWHeight) height = e->height;
        gravitate(APPLY_GRAVITY);

	Strut *temp_strut = wm->getMasterStrut();
		
	if((! has_strut) && (has_title))
	{
		fixupPositionBasedOnStruts(temp_strut);
	}
	
	wc.x = x;
        wc.y = y - theight();
        wc.width = width;
        wc.height = height + theight();
        wc.border_width = BW;
        wc.sibling = e->above;
        wc.stack_mode = e->detail;
        XConfigureWindow(dpy, frame, e->value_mask, &wc);

	if(! is_shaded) 
	{
		XMoveResizeWindow(dpy, frame,x,y-theight(), width, height+theight());
		XResizeWindow(dpy, title, width, theight());
		XMoveResizeWindow(dpy, window,0,theight(),width,height);
	}

	// If an app wants to place his window in a bogus position
	// like offscreen then fix its position. Lets see if this
	// works out okay. Warez and Porn sites have a bad habit
	// of trying to place the Mozilla browser window titlebar
	// just offscreen so you can't close the window. 
	// The bastards!
	if( (x + width > xres) 		|| 
	    (height + theight() > yres) ||
	    (x > xres)	 		|| 
	    (y > yres)			||
	    (x < 0)			||
	    (y < 0) 
	    )
		init_position();
	
#ifdef SHAPE
        if (e->value_mask & (CWWidth|CWHeight)) set_shape();
#endif

	send_config();

	if (e->value_mask & CWY) wc.x = 0;
	if (e->value_mask & CWY) wc.y = theight();
	if (e->value_mask & CWWidth) wc.width = e->width;
	if (e->value_mask & CWHeight) wc.height = e->height;

    	wc.sibling = e->above;
    	wc.stack_mode = e->detail;
    	XConfigureWindow(dpy, e->window, e->value_mask, &wc);

	wm->restackOnTopWindows();	
}

// Two possiblilies if a client is asking to be mapped. One is that
// it's a new window, so we handle that if it isn't in our clients
// list anywhere. The other is that it already exists and wants to
// de-iconify, which is simple to take care of. 
void Client::handle_map_request(XMapRequestEvent *e)
{
	unhide();
}

void Client::handle_unmap_event(XUnmapEvent *e)
{
	if (! ignore_unmap)
	{
		delete this;
	}
}

// This happens when a window is iconified and destroys itself. An
// Unmap event wouldn't happen in that case because the window is
// already unmapped. 
void Client::handle_destroy_event(XDestroyWindowEvent *e)
{
	delete this;
}

// If a client wants to iconify itself (boo! hiss!) it must send a
// special kind of ClientMessage. We might set up other handlers here
// but there's nothing else required by the ICCCM. 
void Client::handle_client_message(XClientMessageEvent *e)
{
	bool state_remove=false;
	bool state_add=false;
	bool state_toggle=false;

	if(e->message_type == wm->atom_extended_net_wm_state)
	{
		if(e->data.l[0]==_NET_WM_STATE_REMOVE) 	state_remove=true;
		if(e->data.l[0]==_NET_WM_STATE_ADD)	state_add=true;
		if(e->data.l[0]==_NET_WM_STATE_TOGGLE)	state_toggle=true;

		// There is no modal support in aewm++ yet
		
		//if(
		//	(e->data.l[1] == wm->atom_extended_net_wm_state_modal)
		//	||
		//    	(e->data.l[2] == wm->atom_extended_net_wm_state_modal)
		//)
		//	is_modal=true;
		
		if(
			(e->data.l[1] == (long) wm->atom_extended_net_wm_state_sticky)
			||
		    	(e->data.l[2] == (long) wm->atom_extended_net_wm_state_sticky)
		)
		{
			if(state_add) 		is_sticky=true;
			if(state_remove)	is_sticky=false;
			if(state_toggle) 	is_sticky = (is_sticky) ? true : false;
		}

		if(
			(e->data.l[1] == (long) wm->atom_extended_net_wm_state_maximized_vert)
			||
		    	(e->data.l[2] == (long) wm->atom_extended_net_wm_state_maximized_vert)
		)
		{
			if(state_add) 		is_maximized_vertical=true;
			if(state_remove)	is_maximized_vertical=false;
			if(state_toggle) 	is_maximized_vertical = (is_maximized_vertical) ? true : false;
		}

		if(
			(e->data.l[1] == (long) wm->atom_extended_net_wm_state_maximized_horz)
			||
		    	(e->data.l[2] == (long) wm->atom_extended_net_wm_state_maximized_horz)
		)
		{
			if(state_add) 		is_maximized_horizontal=true;
			if(state_remove)	is_maximized_horizontal=false;
			if(state_toggle) 	is_maximized_horizontal = (is_maximized_horizontal) ? true : false;
		}

		if(is_maximized_vertical || is_maximized_horizontal) maximize();

		if(
			(e->data.l[1] == (long) wm->atom_extended_net_wm_state_shaded)
			||
		    	(e->data.l[2] == (long) wm->atom_extended_net_wm_state_shaded)
		)
		{
			if(state_add) 		is_maximized_horizontal=true;
			if(state_remove)	is_maximized_horizontal=false;
			if(state_toggle) 	shade();
		}

		if(
			(e->data.l[1] == (long) wm->atom_extended_net_wm_state_skip_taskbar)
			||
		    	(e->data.l[2] == (long) wm->atom_extended_net_wm_state_skip_taskbar)
		)
		{
			if(state_add) 		skip_taskbar=true;
			if(state_remove)	skip_taskbar=false;
			if(state_toggle) 	skip_taskbar = (skip_taskbar) ? true : false;
		}

		if(
			(e->data.l[1] == (long) wm->atom_extended_net_wm_state_skip_pager)
			||
		    	(e->data.l[2] == (long) wm->atom_extended_net_wm_state_skip_pager)
		)
		{
			if(state_add) 		skip_pager=true;
			if(state_remove)	skip_pager=false;
			if(state_toggle) 	skip_pager = (skip_pager) ? true : false;
		}

		update_net_wm_states();
	}

	if(e->message_type == wm->atom_extended_net_active_window &&
		e->data.l[0] == 0)
	{
		send_xmessage(window, wm->atom_wm_protos, SubstructureRedirectMask, wm->atom_wm_takefocus );
		wm->setFocusedClient(this);
		XInstallColormap(dpy, cmap);
	}

	if(e->message_type == wm->atom_extended_net_close_window &&
		e->data.l[0] == 0)
			delete this;

	if (e->message_type == wm->atom_wm_change_state &&
        	e->format == 32 && e->data.l[0] == IconicState)
			iconify();
}

void Client::handle_property_change(XPropertyEvent *e)
{
	long dummy;

	if(e->atom == wm->atom_gnome_win_state)
	{
		if(wm->getHint(window, wm->atom_gnome_win_state)&WIN_STATE_STICKY) is_sticky=true;
		else is_sticky=false;
	}

	if(e->atom == wm->atom_gnome_win_hints)
	{
		if(wm->getHint(window, wm->atom_gnome_win_hints)&WIN_HINTS_DO_NOT_COVER) is_always_on_top=true;
		else is_always_on_top=false;
	}

	if(e->atom == wm->atom_extended_net_wm_desktop)
	{
			belongsToDesktop = wm->findExtendedDesktopHint(window);
			if(belongsToDesktop != wm->getCurrentDesktop()) hide();
	}

	if(e->atom == wm->atom_extended_net_wm_strut)
	{
		// Do we want to update our strut?
		int num=0;
		CARD32 *temp_strut = NULL;
		temp_strut = (CARD32*) wm->getExtendedNetPropertyData(window, wm->atom_extended_net_wm_strut, XA_CARDINAL, &num);

		if(has_strut)
		{
			wm->removeStrut(client_strut);
			has_strut=false;
		}

		if(temp_strut)
		{
			client_strut->west = temp_strut[0];
			client_strut->east = temp_strut[1];
			client_strut->north = temp_strut[2];
			client_strut->south = temp_strut[3];

			wm->addStrut(client_strut);

			has_strut=true;
		}
	}

	if(has_extended_net_name)
	{
		if(e->atom == wm->atom_extended_net_wm_name)
		{
			getXClientName();

			// Yup we totally ignore UTF-8 strings here. Don't
			// know how to implement it. And I can't find any
			// docs on how to implement it with respect to window
			// managers.

			XClearWindow(dpy, title);
 			redraw();
		}
	}

	switch (e->atom) 
	{
        	case XA_WM_NAME:
			if(! has_extended_net_name)
			{
				getXClientName();

       				XClearWindow(dpy, title);
       				redraw();
			}
            	break;

        	case XA_WM_NORMAL_HINTS:
            		XGetWMNormalHints(dpy, window, size, &dummy);
	    	break;

		case XA_WM_TRANSIENT_FOR:
			if(!trans) XGetTransientForHint(dpy, window, &trans);
		break;
	}
}

void Client::reparent()
{
    	XSetWindowAttributes pattr;

	//MJR: Don't need this one because the caller has already grabbed
	//XGrabServer(dpy);

    	pattr.background_pixel = wm->getFCColor().pixel;
    	pattr.border_pixel = wm->getBDColor().pixel;
	pattr.do_not_propagate_mask = ButtonPressMask|ButtonReleaseMask|ButtonMotionMask;
	pattr.override_redirect=False;
	pattr.event_mask = ButtonMotionMask	|
			   ChildMask		|
			   ButtonMask		|
			   ExposureMask		|
			   EnterWindowMask 	|
			   LeaveWindowMask	;
    
    	frame = XCreateWindow(dpy, 
				root,
        			x, 
				y - theight(), 
				width, 
				height + theight(), 
				BW,
        			DefaultDepth(dpy, wm->getScreen()), 
				CopyFromParent, 
				DefaultVisual(dpy, wm->getScreen()),
        			CWOverrideRedirect|CWDontPropagate|CWBackPixel|CWBorderPixel|CWEventMask, 
				&pattr);

	// This window is used to house the window title and title bar button
	title = XCreateWindow(dpy, 
				frame,
        			0, 
				0, 
				width, 
				theight(), 
				0,
        			DefaultDepth(dpy, wm->getScreen()), 
				CopyFromParent, 
				DefaultVisual(dpy, wm->getScreen()),
				CWOverrideRedirect|CWDontPropagate|CWBackPixel|CWBorderPixel|CWEventMask, 
				&pattr);

	#ifdef SHAPE
    	if (wm->getShape()) {
        	XShapeSelectInput(dpy, window, ShapeNotifyMask);
        	set_shape();
    	}
	#endif

	// We don't want these masks to be propagated down to the frame
	XChangeWindowAttributes(dpy, window, CWDontPropagate, &pattr);

	XSelectInput(dpy, window, FocusChangeMask|PropertyChangeMask);

    	XAddToSaveSet(dpy, window);
    	XSetWindowBorderWidth(dpy, window, 0);

    	XReparentWindow(dpy, window, frame, 0, theight());

	XResizeWindow(dpy,window,width,height);
	
	send_config();

	XGrabButton(dpy, 
		Button1, 
		AnyModifier, 
		frame,
		1, 
		ButtonPressMask,
		GrabModeSync, 
		GrabModeAsync, None, None);	

	//XSync(dpy, false);
	//XUngrabServer(dpy);
}


// This function handles the button events.
// Remember this is designed for a 3 button mouse and to the way I like
// the buttons laid out. If you want something different then edit
// this until your heart is content.
void Client::handle_button_event(XButtonEvent *e)
{
	int in_box;
	
	// Formula to tell if the pointer is in the little
	// box on the right edge of the window. This box is
	// the iconify button, maximize button and close button.
     	in_box = (e->x >= width - theight()) && (e->y <= theight());

	// Used to compute the pointer position on click
	// used in the motion handler when doing a window move.
	old_cx = x;
	old_cy = y;
	pointer_x = e->x_root;
	pointer_y = e->y_root;

	// Allow us to get clicks from anywhere on the window
	// so click to raise works.
	XAllowEvents(dpy, ReplayPointer, CurrentTime);

	window_menu->hide();

	switch (e->button) 
	{
	    case Button1:
	    {
		if (e->type == ButtonPress) 
		{		
			if(e->window == window || e->subwindow == window)
			{
				// I always use these two functions
				// together because I want to raise
				// the current client to the top but
				// then have the windows that always
				// want to be ontop raised above the
				// current client.
				
				raise();

				wm->restackOnTopWindows();
			}
			
			if (e->window == title) 
			{
				window_menu->hideAllVisibleSubmenus();

				if (in_box) 
				{
					if(!trans)
					{
						window_menu->hide();
						iconify();
					}
				}
				else
				{
					raise();
					wm->restackOnTopWindows();
	      			}
			}			
     		}

		if (e->type == ButtonRelease) 
		{
			// Check for a double click then maximize 
			// the window.
			if(e->time-last_button1_time<250) 
			{
				maximize();
			
				last_button1_time=0;
				
				return;
			} else 
				last_button1_time=e->time;
		}
	      
	    }
	    break;
	      
            case Button2:
	    {	
			if(e->window == title)
			{
				if(! in_box) 
				{
					if(e->type == ButtonRelease) shade();
				} else {
					
					if(e->type == ButtonPress) 
					{
						if(is_shaded) shade();

						raise();

						wm->restackOnTopWindows();

						resize();
					}
				}
	      		}
	    } 
	    break;
	      
		case Button3:
		{
			if(e->window == title)
			{
				if (e->type == ButtonRelease)
				{
					if (in_box) 
						send_wm_delete();
					else {
						if(!trans)
						{
							window_menu->setThisClient(this);
							window_menu->show();
						}
					}
				}
			}
	      }
	      break;
	  }
}

void Client::handle_focus_in_event(XFocusChangeEvent *e)
{
	send_xmessage(window, wm->atom_wm_protos, SubstructureRedirectMask, wm->atom_wm_takefocus );
	XInstallColormap(dpy, cmap);
	wm->setExtendedNetActiveWindow(window);
}

void Client::handle_focus_out_event(XFocusChangeEvent *e)
{
	if(e->window == window)
		wm->setExtendedNetActiveWindow(None);
}

void Client::set_focus(bool focus)
{
	has_focus=focus;
	
	if (has_title)
	{
		if(has_focus)
			XSetWindowBackground(dpy, title, wm->getBGColor().pixel);
		else 
			XSetWindowBackground(dpy, title, wm->getFCColor().pixel);
		
		XClearWindow(dpy, title);
		redraw();
	} 		
}

void Client::warpToTitlebarCenter()
{
	if (is_shaded)
		setmouse(root, x + (width / 2), y);
	else
		setmouse(root, x + (width / 2), y + 1);
}

void Client::moveToCorner(int whichCorner)
{
	Strut *temp_strut = wm->getMasterStrut();
	int old_height = height;
	switch (whichCorner)
	{
		case TOP_LEFT:
			x = 0;
			y = theight() - 1;
			break;
		case TOP_RIGHT:
			x = xres - width;
			y = theight() - 1;
			break;
		case BOTTOM_LEFT:
			x = 0;
			if (is_shaded)
				y = yres;
			else
				y = (yres - height) + (theight() - 1);
			break;
		case BOTTOM_RIGHT:
			x = xres - width;
			if (is_shaded)
				y = yres;
			else
				y = (yres - height) + (theight() - 1);
			break;
		default:
			return;
	}

	if (is_shaded)
	{
		height = 0;
		fixupPositionBasedOnStruts(temp_strut);
		height = old_height;
		XMoveResizeWindow(dpy, frame, x, y - theight(), width, theight() - 1);
	}
	else
	{
		fixupPositionBasedOnStruts(temp_strut);
		XMoveResizeWindow(dpy, frame, x, y - theight(), width, height + (theight() - 1));
	}
	raise();
	send_config();
}

void Client::moveInIncrement(int increment, int direction)
{
	switch (direction)
	{
		case LEFT:
			if ((x + width - increment) >= 0)
				x -= increment;
			break;
		case DOWN:
			if ((y - theight() + increment) <= yres)
				y += increment;
			break;
		case UP:
			if ((y + height - increment) >= 0)
				y -= increment;
			break;
		case RIGHT:
			if ((x + increment) <= xres)
				x += increment;
			break;
		default:
			return;
	}
	if (is_shaded)
		XMoveResizeWindow(dpy, frame, x, y - theight(), width, theight() - 1);
	else
		XMoveResizeWindow(dpy, frame, x, y - theight(), width, height + (theight() - 1));
	raise();
	send_config();
}

// Here's part 2 of our colormap policy: when a client installs a new
// colormap on itself, set the display's colormap to that. Arguably,
// this is bad, because we should only set the colormap if that client
// has the focus. However, clients don't usually set colormaps at
// random when you're not interacting with them, so I think we're
// safe. If you have an 8-bit display and this doesn't work for you,
// by all means yell at me, but very few people have 8-bit displays
// these days. 
void Client::handle_colormap_change(XColormapEvent *e)
{
    	if (e->c_new) {
        	cmap = e->colormap;
        	XInstallColormap(dpy, cmap);
    	}
}

// If we were covered by multiple windows, we will usually get
// multiple expose events, so ignore them unless e->count (the number
// of outstanding exposes) is zero.
void Client::handle_expose_event(XExposeEvent *e)
{
    	if (e->count == 0) redraw();
	if(!trans) wm->findTransientsToRaiseOrLower(window, false);
}

#ifdef SHAPE
void Client::handle_shape_change(XShapeEvent *e)
{
    	set_shape();
}
#endif

// Currently, only send_wm_delete uses this one...
int Client::send_xmessage(Window w, Atom a, long mask, long x)
{
    	XEvent e;

    	e.type = ClientMessage;
    	e.xclient.window = w;
    	e.xclient.message_type = a;
    	e.xclient.format = 32;
    	e.xclient.data.l[0] = x;
    	e.xclient.data.l[1] = CurrentTime;

    	return XSendEvent(dpy, w, False, mask, &e);
}

void Client::set_desktop(int desk)
{
	belongsToDesktop=desk;
	
	if(belongsToDesktop != wm->getCurrentDesktop())
	{
		hide();
		
		wm->setExtendedWMHint(window, wm->atom_extended_net_wm_desktop, belongsToDesktop);
		
		wm->setGnomeHint(window, wm->atom_gnome_win_workspace, belongsToDesktop);
	}
}

void Client::raise()
{
	XWindowChanges wc;
	wc.stack_mode = Above;

	XConfigureWindow(dpy, frame, CWStackMode, &wc);
	if(!trans) wm->findTransientsToRaiseOrLower(window, false);
}

void Client::lower()
{
	XWindowChanges wc;
	wc.stack_mode = Below;

	XConfigureWindow(dpy, window, CWStackMode, &wc);
	if(!trans) wm->findTransientsToRaiseOrLower(window, true);
}

void Client::update_net_wm_states()
{
	// Clients will always have a false modal property set
	// since there is no support for modal windows in 
	// aewm++.
	wm->setExtendedNetWMState(
				window,
				false, 			/* modal*/
				is_sticky, 		/* sticky */
				is_maximized_vertical, 	/* max_vert */
				is_maximized_horizontal,/* max_horz */
				is_shaded,		/* shaded */
				skip_taskbar, 		/* skip_taskbar */
				skip_pager  		/* skip_pager */
				);
}


syntax highlighted by Code2HTML, v. 0.9.1