/*
 * Copyright (C) 2003 Sasha Vasko
 * Copyright (C) 1996 Frank Fejes
 * Copyright (C) 1996 Alfredo Kojima
 * Copyright (C) 1995 Bo Yang
 * Copyright (C) 1993 Robert Nation
 *
 * 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.
 *
 */

#define LOCAL_DEBUG

#include "../../configure.h"

#include "asinternals.h"
#include "../../libAfterConf/afterconf.h"

/********************************************************************/
/* ASWindow frame decorations :                                     */
/********************************************************************/
/* window frame decoration consists of :
  Top level window
	  4 canvases - one for each side :
		  Top or left canvas contains titlebar+ adjusen frame side+corners if any
		  Bottom or right canvas contains sidebar which is the same as south frame side with corners
		  Remaining two canvasses contain east and west frame sides only ( if any );
	  Canvasses surround main window and its sizes are actually the frame size.
 *    For each frame part and title we have separate TBarData structure. ( 9 total )
 *    In windows with vertical title we keep the orientation of canvases but turn
 *    tbars counterclockwise 90 degrees. As The result Title tbar will be shown on FR_W canvas,
 *    instead of FR_N canvas. Frame parts will be shown on the following canvases :
 *      Frame part  |   Canvas
 *    --------------+-----------
 *      FR_N            FR_W
 *      FR_NE           FR_W
 *      FR_E            FR_N
 *      FR_SE           FR_E
 *      FR_S            FR_E
 *      FR_SW           FR_E
 *      FR_W            FR_S
 *      FR_NW           FR_W
 *
 *      That has to be taken on account whenever we have to associate tbars and canvases,
 *      like in moving/resizing, redrawing, hiliting, pressing and context lookup
 *
 * Icon is drawn on one or two canvases. If separate button titles are selected and Icon title
 * is allowed for this style, then separate canvas will be created for the titlebar.
 * Respectively icon_title_canvas will be different then icon_canvas. In all other cases
 * Icon_title_canvas will be the same as icon_canvas.
 */

static int NormalX, NormalY;
static unsigned int NormalWidth, NormalHeight;
/* Mirror Note :
 *
 * For the purpose of sizing/placing left and right sides and corners - we employ somewhat
 * twisted logic - we mirror sides over lt2rb diagonal in case of
 * vertical title orientation. That allows us to apply simple x/y switching instead of complex
 * calculations. Note that we only do that for placement purposes. Contexts and images are
 * still taken from MyFrame parts as if it was rotated counterclockwise instead of mirrored.
 */

ASOrientation HorzOrientation = {
	{C_FrameN, C_FrameE, C_FrameS, C_FrameW, C_FrameNW, C_FrameNE, C_FrameSW,
	 C_FrameSE},
	{FR_N, FR_E, FR_S, FR_W, FR_NW, FR_NE, FR_SW, FR_SE},
/*      N     E     S     W     NW    NE    SW    SE    TITLE */
	{FR_N, FR_E, FR_S, FR_W, FR_N, FR_N, FR_S, FR_S, FR_N},
	FR_N,
	{FR_NW, FR_NE},
	{FR_NW, FR_NE},
	FR_S,
	{FR_SW, FR_SE},
	{FR_SW, FR_SE},
	FR_W, FR_E,
	FR_W, FR_E,
	MYFRAME_HOR_MASK,
	TBTN_ORDER_L2R, TBTN_ORDER_L2R,
	&NormalX, &NormalY, &NormalWidth, &NormalHeight,
	&NormalX, &NormalY, &NormalWidth, &NormalHeight,
	0,
	{0, 1, 2, 3, 4, 5, 6},
	{0, 0, 0, 0, 0, 0, 0}
};

ASOrientation VertOrientation = {
	{C_FrameW, C_FrameN, C_FrameE, C_FrameS, C_FrameSW, C_FrameNW, C_FrameSE,
	 C_FrameNE},
	{FR_W, FR_N, FR_E, FR_S, FR_SW, FR_NW, FR_SE, FR_NE},
/*      N     E     S     W     NW    NE    SW    SE    TITLE */
	{FR_N, FR_E, FR_S, FR_W, FR_W, FR_E, FR_W, FR_E, FR_W},
	FR_W,
	{FR_SW, FR_NW},
	{FR_NW, FR_SW},
	FR_E,
	{FR_SE, FR_NE},
	{FR_NE, FR_SE},
	FR_S, FR_N,
	FR_N, FR_S,
	MYFRAME_VERT_MASK,
	TBTN_ORDER_B2T, TBTN_ORDER_B2T,
	&NormalX, &NormalY, &NormalWidth, &NormalHeight,
	&NormalY, &NormalX, &NormalHeight, &NormalWidth,
	FLIP_VERTICAL,
	{0, 0, 0, 0, 0, 0, 0},
	{7, 6, 5, 4, 3, 2, 1}
};

ASOrientation *get_orientation_data (ASWindow * asw)
{
	if (asw && asw->magic == MAGIC_ASWINDOW && asw->hints)
		return ASWIN_HFLAGS (asw,
												 AS_VerticalTitle) ? &VertOrientation :
				&HorzOrientation;
	return &HorzOrientation;
}

void
compile_tbar_layout (unsigned int *tbar_layout,
										 unsigned int *default_tbar_layout,
										 unsigned long left_layout, unsigned long right_layout)
{
	int l, r;

	Bool used[MYFRAME_TITLE_BACKS] =
			{ False, False, False, False, False, False, False };

	LOCAL_DEBUG_OUT ("left_layout = %lX, right_layout = %lX", left_layout,
									 right_layout);
	for (l = 0; l < MYFRAME_TITLE_BACKS; ++l)
		tbar_layout[l] = -1;
	for (l = 0; l < MYFRAME_TITLE_SIDE_ELEMS; ++l) {
		int elem = MYFRAME_GetTbarLayoutElem (left_layout, l);

		if (elem != MYFRAME_TITLE_BACK_INVALID && !used[l]) {
			tbar_layout[elem] = default_tbar_layout[l];
			used[l] = True;
		}
	}
	for (r = MYFRAME_TITLE_BACKS; r > MYFRAME_TITLE_SIDE_ELEMS + 1;) {
		int elem =
				MYFRAME_GetTbarLayoutElem (right_layout,
																	 (MYFRAME_TITLE_BACKS - r));
		--r;
		LOCAL_DEBUG_OUT ("r = %d, elem = %d, used = %d", r, elem, used[r]);
		if (elem != MYFRAME_TITLE_BACK_INVALID && !used[r]) {
			/* right layout is in reverse order ! */
			elem = MYFRAME_TITLE_BACKS - MYFRAME_TITLE_SIDE_ELEMS + elem;
			tbar_layout[elem] = default_tbar_layout[r];
			LOCAL_DEBUG_OUT ("right_elem %d has slot = %d", elem,
											 tbar_layout[elem]);
			used[r] = True;
		}
	}

	tbar_layout[MYFRAME_TITLE_BACK_LBL] =
			default_tbar_layout[MYFRAME_TITLE_BACK_LBL];
	used[MYFRAME_TITLE_BACK_LBL] = True;
	for (l = 0; l < MYFRAME_TITLE_BACKS; ++l) {
		if (tbar_layout[l] == -1) {
			int i;
			int from = 0, to = MYFRAME_TITLE_SIDE_ELEMS;
			if (l >= MYFRAME_TITLE_SIDE_ELEMS) {
				from = MYFRAME_TITLE_BACK_LEFT2RIGHT (MYFRAME_TITLE_SIDE_ELEMS);
				to = MYFRAME_TITLE_BACKS;
			}
			LOCAL_DEBUG_OUT ("missing layout for elem %d, from = %d, to = %d", l,
											 from, to);
			for (i = from; i < to; ++i) {
				if (!used[i]) {
					LOCAL_DEBUG_OUT ("found vacant slot %d", default_tbar_layout[i]);
					tbar_layout[l] = default_tbar_layout[i];
					used[i] = True;
					break;
				}
			}
		}
	}
}

/* this gets called when Look changes or hints changes : */
static ASCanvas *check_side_canvas (ASWindow * asw, FrameSide side,
																		Bool required)
{
	ASCanvas *canvas = asw->frame_sides[side];
	Window w;
	LOCAL_DEBUG_CALLER_OUT ("asw = %p, side = %d, required = %d", asw, side,
													required);
	if (required) {
		if (canvas == NULL) {				/* create canvas here */
			unsigned long valuemask;
			XSetWindowAttributes attributes;

			valuemask = CWBorderPixel | CWEventMask;
			attributes.border_pixel = Scr.asv->black_pixel;
			if (Scr.Feel.flags & BackingStore) {
				valuemask |= CWBackingStore;
				attributes.backing_store = WhenMapped;
			}
			attributes.event_mask = AS_CANVAS_EVENT_MASK;

			w = create_visual_window (Scr.asv, asw->frame,
																0, 0, 1, 1, 0, InputOutput,
																valuemask, &attributes);
			register_aswindow (w, asw);
			canvas = create_ascanvas (w);
			LOCAL_DEBUG_OUT
					("++CREAT Client(%lx(%s))->side(%d)->canvas(%p)->window(%lx)",
					 asw->w, ASWIN_NAME (asw) ? ASWIN_NAME (asw) : "noname", side,
					 canvas, canvas->w);
		} else
			invalidate_canvas_config (canvas);
	} else if (canvas != NULL) {	/* destroy canvas here */
		w = canvas->w;
		LOCAL_DEBUG_OUT
				("--DESTR Client(%lx(%s))->side(%d)->canvas(%p)->window(%lx)",
				 asw->w, ASWIN_NAME (asw) ? ASWIN_NAME (asw) : "noname", side,
				 canvas, canvas->w);
		destroy_ascanvas (&canvas);
		destroy_registered_window (w);
	}

	return (asw->frame_sides[side] = canvas);
}

/* creating/destroying our main frame window : */
static ASCanvas *check_frame_canvas (ASWindow * asw, Bool required)
{
	ASCanvas *canvas = asw->frame_canvas;
	Window w;

	if (required) {
		if (canvas == NULL) {				/* create canvas here */
			unsigned long valuemask;
			XSetWindowAttributes attributes;
			int bw = 0;

			/* create windows */
			valuemask = CWBorderPixel | CWCursor | CWEventMask;
			if (Scr.asv->visual_info.visual == DefaultVisual (dpy, Scr.screen)) {
				/* only if root has same depth and visual as us! */
				attributes.background_pixmap = ParentRelative;
				valuemask |= CWBackPixmap;
			}
			attributes.border_pixel = Scr.asv->black_pixel;
			attributes.cursor = Scr.Feel.cursors[ASCUR_Default];
			attributes.event_mask = AS_FRAME_EVENT_MASK;

			if (get_flags (Scr.Feel.flags, SaveUnders)) {
				valuemask |= CWSaveUnder;
				attributes.save_under = True;
			}
			if (asw->hints && get_flags (asw->hints->flags, AS_Border))
				bw = asw->hints->border_width;
			asw->status->frame_border_width = bw;
			w = create_visual_window (Scr.asv,
																(ASWIN_DESK (asw) ==
																 Scr.CurrentDesk) ? Scr.Root : Scr.
																ServiceWin, asw->status->x, asw->status->y,
																asw->status->width, asw->status->height,
																bw, InputOutput, valuemask, &attributes);
			XLowerWindow (dpy, w);
			asw->frame = w;
			register_aswindow (w, asw);
			canvas = create_ascanvas_container (w);
			LOCAL_DEBUG_OUT
					("++CREAT Client(%lx(%s))->FRAME->canvas(%p)->window(%lx)->parent(%lx)",
					 asw->w, ASWIN_NAME (asw) ? ASWIN_NAME (asw) : "noname", canvas,
					 canvas->w,
					 (ASWIN_DESK (asw) ==
						Scr.CurrentDesk) ? Scr.Root : Scr.ServiceWin);
		} else
			invalidate_canvas_config (canvas);
	} else if (canvas != NULL) {	/* destroy canvas here */
		w = canvas->w;
		LOCAL_DEBUG_OUT
				("--DESTR Client(%lx(%s))->FRAME->canvas(%p)->window(%lx)", asw->w,
				 ASWIN_NAME (asw) ? ASWIN_NAME (asw) : "noname", canvas,
				 canvas->w);
		destroy_ascanvas (&canvas);
		destroy_registered_window (w);
	}

	return (asw->frame_canvas = canvas);
}

/* creating/destroying container canvas for our client's window : */
static ASCanvas *check_client_canvas (ASWindow * asw, Bool required)
{
	ASCanvas *canvas = asw->client_canvas;
	Window w;

	if (required) {
		if (canvas == NULL) {				/* create canvas here */
			unsigned long valuemask;
			XSetWindowAttributes attributes;

			if (asw->frame == None || (w = asw->w) == None)
				return NULL;

			attributes.event_mask = AS_CLIENT_EVENT_MASK;
			if (asw->internal) {
				XWindowAttributes internal_attr;
				XGetWindowAttributes (dpy, w, &internal_attr);
				attributes.event_mask |= internal_attr.your_event_mask;
			}
			quietly_reparent_window (w, asw->frame, 0, 0, attributes.event_mask);

			valuemask = (CWEventMask | CWDontPropagate);
			attributes.do_not_propagate_mask =
					ButtonPressMask | ButtonReleaseMask;

			if (get_flags (Scr.Feel.flags, AppsBackingStore)) {
				valuemask |= CWBackingStore;
				attributes.backing_store = WhenMapped;
			}
			XChangeWindowAttributes (dpy, w, valuemask, &attributes);
			if (asw->internal == NULL)
				XAddToSaveSet (dpy, w);

			register_aswindow (w, asw);
			canvas = create_ascanvas_container (w);
			if (ASWIN_GET_FLAGS (asw, AS_Shaped))
				refresh_container_shape (canvas);
			LOCAL_DEBUG_OUT
					("++CREAT Client(%lx(%s))->CLIENT->canvas(%p)->window(%lx)",
					 asw->w, ASWIN_NAME (asw) ? ASWIN_NAME (asw) : "noname", canvas,
					 canvas->w);
		}
		/*else    invalidate_canvas_config( canvas ); */
	} else if (canvas != NULL) {	/* destroy canvas here */
		XWindowChanges xwc;					/* our withdrawn geometry */
		ASStatusHints withdrawn_status = { 0 };
		register int i = 0;

		w = canvas->w;

		/* calculating the withdrawn location */
		if (asw->status)
			withdrawn_status = *(asw->status);

		xwc.border_width = withdrawn_status.border_width;

		for (i = 0; i < FRAME_SIDES; ++i)
			withdrawn_status.frame_size[i] = 0;
		clear_flags (withdrawn_status.flags, AS_Shaded | AS_Iconic);
		anchor2status (&withdrawn_status, asw->hints, &(asw->anchor));
		xwc.x = withdrawn_status.x;
		xwc.y = withdrawn_status.y;
		xwc.width = withdrawn_status.width;
		xwc.height = withdrawn_status.height;

		LOCAL_DEBUG_OUT
				("--DESTR Client(%lx(%s))->CLIENT->canvas(%p)->window(%lx)",
				 asw->w, ASWIN_NAME (asw) ? ASWIN_NAME (asw) : "noname", canvas,
				 canvas->w);
		destroy_ascanvas (&canvas);

		/*
		 * Prevent the receipt of an UnmapNotify in case we are simply restarting,
		 * since that would cause a transition to the Withdrawn state.
		 */
#if 1
		if ((asw->status == NULL || !get_flags (asw->status->flags, AS_Dead))
				&& get_parent_window (w) == asw->frame) {
			LOCAL_DEBUG_OUT ("reparenting client window %lX", w);
			if (get_flags (AfterStepState, ASS_Shutdown))
				quietly_reparent_window (w, Scr.Root, xwc.x, xwc.y,
																 AS_CLIENT_EVENT_MASK);
			else
				XReparentWindow (dpy, w, Scr.Root, xwc.x, xwc.y);
			/* WE have to restore window's withdrawn location now. */
			XConfigureWindow (dpy, w,
												CWX | CWY | CWWidth | CWHeight | CWBorderWidth,
												&xwc);
		}
		XSync (dpy, 0);
#endif
	}

	asw->client_canvas = canvas;
	return canvas;
}

/* creating/destroying our icon window : */
static ASCanvas *check_icon_canvas (ASWindow * asw, Bool required)
{
	ASCanvas *canvas = asw->icon_canvas;
	Window w;

	if (required) {
		if (canvas == NULL) {				/* create canvas here */
			unsigned long valuemask;
			XSetWindowAttributes attributes;

			valuemask = CWBorderPixel | CWCursor | CWEventMask;
			attributes.border_pixel = Scr.asv->black_pixel;
			attributes.cursor = Scr.Feel.cursors[ASCUR_Default];

			if ((get_flags (asw->hints->client_icon_flags, AS_ClientIcon | AS_ClientIconPixmap) != AS_ClientIcon) || asw->hints == NULL || asw->hints->icon.window == None || !get_flags (Scr.Feel.flags, KeepIconWindows)) {	/* create windows */
				attributes.event_mask = AS_ICON_TITLE_EVENT_MASK;
				w = create_visual_window (Scr.asv,
																	(ASWIN_DESK (asw) ==
																	 Scr.CurrentDesk) ? Scr.Root : Scr.
																	ServiceWin, -9000, -9000, 1, 1, 0,
																	InputOutput, valuemask, &attributes);
				canvas = create_ascanvas (w);
			} else {									/* reuse client's provided window */
				attributes.event_mask = AS_ICON_EVENT_MASK;
				w = asw->hints->icon.window;
				XChangeWindowAttributes (dpy, w, valuemask, &attributes);
				canvas = create_ascanvas_container (w);
			}
			LOCAL_DEBUG_OUT
					("++CREAT Client(%lx(%s))->ICON->canvas(%p)->window(%lx)",
					 asw->w, ASWIN_NAME (asw) ? ASWIN_NAME (asw) : "noname", canvas,
					 canvas->w);
			register_aswindow (w, asw);
		} else
			invalidate_canvas_config (canvas);
	} else if (canvas != NULL) {	/* destroy canvas here */
		w = canvas->w;
		LOCAL_DEBUG_OUT
				("--DESTR Client(%lx(%s))->ICON->canvas(%p)->window(%lx)", asw->w,
				 ASWIN_NAME (asw) ? ASWIN_NAME (asw) : "noname", canvas,
				 canvas->w);
		if (asw->icon_title_canvas == canvas)
			asw->icon_title_canvas = NULL;
		destroy_ascanvas (&canvas);
		if (asw->hints && asw->hints->icon.window == w)
			unregister_aswindow (w);
		else
			destroy_registered_window (w);
	}
	asw->icon_canvas = canvas;

	return canvas;
}

/* creating/destroying our icon title window : */
static ASCanvas *check_icon_title_canvas (ASWindow * asw, Bool required,
																					Bool reuse_icon_canvas)
{
	ASCanvas *canvas = asw->icon_title_canvas;
	Window w;

	if (!required) {
		if (canvas && canvas != asw->icon_canvas) {
			w = canvas->w;
			LOCAL_DEBUG_OUT
					("--DESTR Client(%lx(%s))->ICONT->canvas(%p)->window(%lx)",
					 asw->w, ASWIN_NAME (asw) ? ASWIN_NAME (asw) : "noname", canvas,
					 canvas->w);
			destroy_ascanvas (&canvas);
			destroy_registered_window (w);
		}
	} else {											/* if( required ) */

		if (reuse_icon_canvas)
			canvas = asw->icon_canvas;
		else if (canvas == NULL) {	/* create canvas here */
			unsigned long valuemask;
			XSetWindowAttributes attributes;

			valuemask = CWBorderPixel | CWCursor | CWEventMask;
			attributes.border_pixel = Scr.asv->black_pixel;
			attributes.cursor = Scr.Feel.cursors[ASCUR_Default];
			attributes.event_mask = AS_ICON_TITLE_EVENT_MASK;
			/* create windows */
			w = create_visual_window (Scr.asv,
																(ASWIN_DESK (asw) ==
																 Scr.CurrentDesk) ? Scr.Root : Scr.
																ServiceWin, -9010, -9010, 1, 1, 0,
																InputOutput, valuemask, &attributes);

			register_aswindow (w, asw);
			canvas = create_ascanvas (w);
			LOCAL_DEBUG_OUT
					("++CREAT Client(%lx(%s))->ICONT->canvas(%p)->window(%lx)",
					 asw->w, ASWIN_NAME (asw) ? ASWIN_NAME (asw) : "noname", canvas,
					 canvas->w);
		} else
			invalidate_canvas_config (canvas);
	}

	asw->icon_title_canvas = canvas;

	return canvas;
}

/* pointers should point to valid values preset to 0, im.width, 0, im.height */
static void
geometry2slicing (ASGeometry g, int *pxs, int *pxe, int *pys, int *pye)
{
	if (get_flags (g.flags, XValue))
		*pxs = get_flags (g.flags, XNegative) ? *pxe + g.x : g.x;
	if (get_flags (g.flags, YValue))
		*pys = get_flags (g.flags, YNegative) ? *pye + g.y : g.y;

	if (get_flags (g.flags, WidthValue))
		*pxe = *pxs + g.width;
	if (get_flags (g.flags, HeightValue))
		*pye = *pys + g.height;
}


static ASTBarData *check_tbar (ASTBarData ** tbar, Bool required,
															 const char *mystyle_name, ASImage * img,
															 unsigned short back_w,
															 unsigned short back_h, int flip,
															 ASFlagType align, ASFlagType fbevel,
															 ASFlagType ubevel, unsigned char fcm,
															 unsigned char ucm, int context,
															 ASGeometry * slicing)
{
	if (required) {
		if (*tbar == NULL) {
			*tbar = create_astbar ();
			LOCAL_DEBUG_OUT ("++CREAT tbar(%p)->context(%s)", *tbar,
											 context2text (context));
		} else
			delete_astbar_tile (*tbar, -1);

		set_astbar_flip (*tbar, flip);

		invalidate_astbar_style (*tbar, -1);
		set_astbar_style (*tbar, BAR_STATE_FOCUSED, mystyle_name);
		set_astbar_style (*tbar, BAR_STATE_UNFOCUSED, "default");
		if (img) {
			LOCAL_DEBUG_OUT ("adding bar icon %p %ux%u", img, img->width,
											 img->height);
			if (slicing && slicing->flags) {
				int xs = 0, xe = 0, ys = img->width, ye = img->height;
				geometry2slicing (*slicing, &xs, &xe, &ys, &ye);
				add_astbar_image (*tbar, 0, 0, flip, align, img, xs, xe, ys, ye);
			} else
				add_astbar_icon (*tbar, 0, 0, flip, align, img);
			if (back_w == 0)
				back_w =
						get_flags (flip, FLIP_VERTICAL) ? img->height : img->width;
			if (back_h == 0)
				back_h =
						get_flags (flip, FLIP_VERTICAL) ? img->width : img->height;
		}

		set_astbar_hilite (*tbar, BAR_STATE_FOCUSED, fbevel);
		set_astbar_hilite (*tbar, BAR_STATE_UNFOCUSED, ubevel);
		set_astbar_composition_method (*tbar, BAR_STATE_FOCUSED, fcm);
		set_astbar_composition_method (*tbar, BAR_STATE_UNFOCUSED, ucm);
		set_astbar_size (*tbar, (back_w == 0) ? 1 : back_w,
										 (back_h == 0) ? 1 : back_h);
		(*tbar)->context = context;
	} else if (*tbar) {
		destroy_astbar (tbar);
	}
	return *tbar;
}

/******************************************************************************/
/* now externally available interfaces to the above functions :               */
/******************************************************************************/
void invalidate_window_icon (ASWindow * asw)
{
	if (asw)
		check_icon_canvas (asw, False);
}

void invalidate_window_mystyles (ASWindow * asw)
{
	int i;

	for (i = 0; i < FRAME_PARTS; ++i)
		if (asw->frame_bars[i])
			invalidate_astbar_style (asw->frame_bars[i], -1);
	if (asw->tbar)
		invalidate_astbar_style (asw->tbar, -1);
	if (asw->icon_button)
		invalidate_astbar_style (asw->icon_button, -1);
	if (asw->icon_title)
		invalidate_astbar_style (asw->icon_title, -1);
}

/***********************************************************************
 *  grab_aswindow_buttons - grab needed buttons for all of the windows
 *  for specified client
 ***********************************************************************/
void grab_aswindow_buttons (ASWindow * asw, Bool focused)
{
	if (asw) {
		Bool do_focus_grab = (get_flags (Scr.Feel.flags, ClickToFocus));
		if (do_focus_grab) {
			if (focused)
				ungrab_focus_click (asw->frame);
			else
				grab_focus_click (asw->frame);
		} else
			ungrab_window_buttons (asw->frame);

		if (!ASWIN_GET_FLAGS (asw, AS_Dead)) {
			ungrab_window_buttons (asw->w);
			grab_window_buttons (asw->w, C_WINDOW);
		}

		if (asw->icon_canvas && !ASWIN_GET_FLAGS (asw, AS_Dead)
				&& validate_drawable (asw->icon_canvas->w, NULL, NULL) != None) {
			ungrab_window_buttons (asw->icon_canvas->w);
			if (do_focus_grab) {
				if (focused)
					ungrab_focus_click (asw->icon_canvas->w);
				else
					grab_focus_click (asw->icon_canvas->w);
			}
			grab_window_buttons (asw->icon_canvas->w, C_ICON);
		}

		if (asw->icon_title_canvas
				&& asw->icon_title_canvas != asw->icon_canvas) {
			ungrab_window_buttons (asw->icon_title_canvas->w);
			if (do_focus_grab) {
				if (focused)
					ungrab_focus_click (asw->icon_title_canvas->w);
				else
					grab_focus_click (asw->icon_title_canvas->w);
			}
			grab_window_buttons (asw->icon_title_canvas->w, C_ICON);

		}
	}
	XSync (dpy, False);
}

/***********************************************************************
 *  grab_aswindow_keys - grab needed keys for the window
 ***********************************************************************/
void grab_aswindow_keys (ASWindow * asw)
{
	if (AS_ASSERT (asw))
		return;

	ungrab_window_keys (asw->frame);
	grab_window_keys (asw->frame,
										(C_WINDOW | C_TITLE | C_TButtonAll | C_FRAME));

	if (asw->icon_canvas) {
		ungrab_window_keys (asw->icon_canvas->w);
		grab_window_keys (asw->icon_canvas->w, (C_ICON));
	}
	if (asw->icon_title_canvas && asw->icon_title_canvas != asw->icon_canvas) {
		ungrab_window_keys (asw->icon_title_canvas->w);
		grab_window_keys (asw->icon_title_canvas->w, (C_ICON));
	}
	XSync (dpy, False);
}


/* this should set up proper feel settings - grab keyboard and mouse buttons :
 * Note: must be called after redecorate_window, since some of windows may have
 * been created at that time.
 */
void grab_window_input (ASWindow * asw, Bool release_grab)
{
	if (asw) {
		if (release_grab) {
			ungrab_window_keys (asw->frame);
			ungrab_window_buttons (asw->frame);
			if (asw->icon_canvas) {
				ungrab_window_keys (asw->icon_canvas->w);
				ungrab_window_buttons (asw->icon_canvas->w);
			}
			if (asw->icon_title_canvas
					&& asw->icon_title_canvas != asw->icon_canvas) {
				ungrab_window_keys (asw->icon_title_canvas->w);
				ungrab_window_buttons (asw->icon_title_canvas->w);
			}
			XSync (dpy, False);
		} else {
			grab_aswindow_buttons (asw, (Scr.Windows->focused == asw));
			grab_aswindow_keys (asw);
		}
	}
}

/*
 ** returns a newline delimited list of the Mouse functions bound to a
 ** given context, in human readable form
 */
char *list_functions_by_context (int context, ASHints * hints)
{
	MouseButton *btn;
	char *str = NULL;
	int allocated_bytes = 0;
	if (hints == NULL)
		return NULL;
	for (btn = Scr.Feel.MouseButtonRoot; btn != NULL; btn = btn->NextButton)
		if ((btn->Context & context)
				&& check_allowed_function (btn->fdata, hints)) {
			TermDef *fterm;

			fterm = func2fterm (btn->fdata->func, True);
			if (fterm != NULL) {
				char *ptr;

				if (str) {
					str =
							realloc (str, allocated_bytes + 1 + fterm->keyword_len + 32);
					ptr = str + allocated_bytes;
					*(ptr++) = '\n';
				} else
					ptr = str = safemalloc (fterm->keyword_len + 32);

				sprintf (ptr, "%s: ", fterm->keyword);
				ptr += fterm->keyword_len + 2;
				if (btn->Modifier & ShiftMask) {
					strcat (ptr, "Shift+");
					ptr += 8;
				}
				if (btn->Modifier & ControlMask) {
					strcat (ptr, "Ctrl+");
					ptr += 7;
				}
				if (btn->Modifier & Mod1Mask) {
					strcat (ptr, "Meta+");
					ptr += 7;
				}
				sprintf (ptr, "Button %d", btn->Button);
				allocated_bytes = strlen (str);
			}
		}
	return str;
}



/****************************************************************************
 *
 * Checks the function "function", and sees if it
 * is an allowed function for window t,  according to the motif way of life.
 * This routine is used to decide if we should refuse to perform a function.
 *
 ****************************************************************************/
int check_allowed_function2 (int func, ASHints * hints)
{
	if (func == F_NOP)
		return 0;

	if (hints) {
		int mask = function2mask (func);
		if (func == F_SHADE)
			return get_flags (hints->flags, AS_Titlebar);
		else if (mask != 0)
			return get_flags (hints->function_mask, mask);
	}
	return 1;
}

/****************************************************************************
 *
 * Checks the function described in menuItem mi, and sees if it
 * is an allowed function for window Tmp_Win,
 * according to the motif way of life.
 *
 * This routine is used to determine whether or not to grey out menu items.
 *
 ****************************************************************************/
int check_allowed_function (FunctionData * fdata, ASHints * hints)
{
	int func = fdata->func;
	int i;
	ComplexFunction *cfunc;

	if (func != F_FUNCTION && func != F_CATEGORY)
		return check_allowed_function2 (func, hints);

	if (func == F_FUNCTION) {
		if ((cfunc = get_complex_function (fdata->name)) == NULL)
			return 0;

		for (i = 0; i < cfunc->items_num; ++i)
			if (cfunc->items[i].func == F_FUNCTION
					|| cfunc->items[i].func == F_CATEGORY) {
				if (check_allowed_function (&(cfunc->items[i]), hints) == 0)
					return 0;
			} else if (check_allowed_function2 (cfunc->items[i].func, hints) ==
								 0)
				return 0;
	} else if (func == F_CATEGORY) {
		if (name2desktop_category
				(fdata->text ? fdata->text : fdata->name, NULL) == NULL)
			return 0;
	}
	return 1;
}


ASFlagType compile_titlebuttons_mask (ASHints * hints)
{
	ASFlagType disabled_mask = hints->disabled_buttons;
	ASFlagType enabled_mask = 0;
	int i;

	LOCAL_DEBUG_OUT ("disabled mask from hints is 0x%lX", disabled_mask);

	for (i = 0; i < TITLE_BUTTONS; i++) {
		if (Scr.Look.buttons[i].unpressed.image != NULL) {
			int context = Scr.Look.buttons[i].context;
			MouseButton *func;
			Bool at_least_one_enabled = False;

			enabled_mask |= context;
			for (func = Scr.Feel.MouseButtonRoot; func != NULL;
					 func = (*func).NextButton)
				if ((func->Context & context) != 0)
					if (check_allowed_function (func->fdata, hints)) {
						at_least_one_enabled = True;
						break;
					}
			if (!at_least_one_enabled)
				disabled_mask |= Scr.Look.buttons[i].context;
		}
	}
	LOCAL_DEBUG_OUT
			("disabled mask(0x%lX)->enabled_mask(0x%lX)->button_mask(0x%lX)",
			 disabled_mask, enabled_mask, enabled_mask & (~disabled_mask));


	return enabled_mask & (~disabled_mask);
}

void
estimate_titlebar_size (ASHints * hints, unsigned int *width_ret,
												unsigned int *height_ret)
{
	unsigned int width = 0, height = 0;
	if (hints) {
		ASTBarData *tbar = create_astbar ();
		ASFlagType btn_mask;

		btn_mask = compile_titlebuttons_mask (hints);
		tbar->h_spacing = DEFAULT_TBAR_HSPACING;
		tbar->v_spacing = DEFAULT_TBAR_VSPACING;
		/* left buttons : */
		add_astbar_btnblock (tbar, 0, 0, 0, NO_ALIGN,
												 &(Scr.Look.ordered_buttons[0]), btn_mask,
												 Scr.Look.button_first_right,
												 Scr.Look.TitleButtonXOffset[0],
												 Scr.Look.TitleButtonYOffset[0],
												 Scr.Look.TitleButtonSpacing[0], TBTN_ORDER_L2R);

		/* label */
		add_astbar_label (tbar, 1, 0, 0, ALIGN_LEFT, DEFAULT_TBAR_HSPACING,
											DEFAULT_TBAR_VSPACING, hints->names[0],
											hints->names_encoding[0]);
		/* right buttons : */
		add_astbar_btnblock (tbar, 2, 0, 0, NO_ALIGN,
												 &(Scr.Look.
													 ordered_buttons[Scr.Look.button_first_right]),
												 btn_mask,
												 TITLE_BUTTONS - Scr.Look.button_first_right,
												 Scr.Look.TitleButtonXOffset[1],
												 Scr.Look.TitleButtonYOffset[1],
												 Scr.Look.TitleButtonSpacing[1], TBTN_ORDER_R2L);

		set_astbar_style_ptr (tbar, BAR_STATE_UNFOCUSED,
													Scr.Look.MSMenu[MENU_BACK_TITLE]);
		set_astbar_style_ptr (tbar, BAR_STATE_FOCUSED,
													Scr.Look.MSMenu[MENU_BACK_HITITLE]);
		width = calculate_astbar_width (tbar);
		height = calculate_astbar_height (tbar);
		destroy_astbar (&tbar);
	}
	if (width_ret)
		*width_ret = width;
	if (height_ret)
		*height_ret = height;
}

inline static ASFlagType fix_background_align (ASFlagType align)
{
	ASFlagType new_align = align;
	/* don't ask why - simply magic :)
	 * Well not really - we want to disregard background size in calculations of overall
	 * titlebar size. Overall titlebar size is determined only by text size and button sizes.
	 * At the same time we would like to be able to scale backgrounds to the size of the
	 * titlebar
	 */
	/* clear_flags(new_align , (RESIZE_H|RESIZE_V)); */
	if (get_flags (new_align, RESIZE_H_SCALE))
		set_flags (new_align, FIT_LABEL_WIDTH);
	if (get_flags (new_align, RESIZE_V_SCALE))
		set_flags (new_align, FIT_LABEL_HEIGHT);
	LOCAL_DEBUG_OUT ("Fixed align from 0x%lx to 0x%lx", align, new_align);
	return new_align;
}

Bool hints2decorations (ASWindow * asw, ASHints * old_hints)
{
	int i;
	MyFrame *frame = NULL, *old_frame = NULL;
	Bool has_tbar = False, had_tbar = False;
	Bool icon_image_changed = True;
	ASOrientation *od = get_orientation_data (asw);
	char *mystyle_name = Scr.Look.MSWindow[BACK_FOCUSED]->name;
	char *frame_mystyle_name = NULL;
	unsigned int *frame_contexts = &(od->frame_contexts[0]);
	Bool tbar_created = False;
	Bool status_changed = False;

	LOCAL_DEBUG_CALLER_OUT ("asw(%p)->client(%lX)", asw, asw ? asw->w : 0);

	/* 3) we need to prepare icon window : */
	check_icon_canvas (asw,
										 (ASWIN_HFLAGS (asw, AS_Icon)
											&& !get_flags (Scr.Feel.flags, SuppressIcons)));

	/* 4) we need to prepare icon title window : */
	LOCAL_DEBUG_CALLER_OUT ("checking icon title canvas(%p)",
													asw->icon_title_canvas);
	check_icon_title_canvas (asw,
													 (ASWIN_HFLAGS (asw, AS_IconTitle)
														&& get_flags (Scr.Feel.flags, IconTitle)
														&& !get_flags (Scr.Feel.flags, SuppressIcons)),
													 !get_flags (Scr.Look.flags, SeparateButtonTitle)
													 && (asw->icon_canvas != NULL));

	has_tbar = (ASWIN_HFLAGS (asw, AS_Titlebar) != 0);
	frame =
			myframe_find (ASWIN_HFLAGS (asw, AS_Frame) ? asw->hints->
										frame_name : NULL);
	if (old_hints == NULL) {
		had_tbar = !has_tbar;
		old_frame = NULL;
	} else {
		had_tbar = (get_flags (old_hints->flags, AS_Titlebar) != 0);
		old_frame =
				myframe_find (get_flags (old_hints->flags, AS_Frame) ? old_hints->
											frame_name : NULL);
	}

	asw->frame_data = frame;

	/* 5) we need to prepare windows for 4 frame decoration sides : */
	if (has_tbar != had_tbar || frame != old_frame) {
		LOCAL_DEBUG_OUT ("has_tbar = %d, has_top_parts = %d, ", has_tbar,
										 myframe_has_parts (frame, FRAME_TOP_MASK));
		check_side_canvas (asw, od->tbar_side, has_tbar
											 || myframe_has_parts (frame, FRAME_TOP_MASK));
	}
	if (!ASWIN_HFLAGS (asw, AS_Handles)) {
		check_side_canvas (asw, od->sbar_side, False);
		check_side_canvas (asw, od->left_side, False);
		check_side_canvas (asw, od->right_side, False);
	} else if (frame != old_frame
						 || (old_hints
								 && get_flags (old_hints->flags,
															 AS_Handles) != ASWIN_HFLAGS (asw,
																														AS_Handles))) {
		check_side_canvas (asw, od->sbar_side,
											 myframe_has_parts (frame, FRAME_BTM_MASK));
		check_side_canvas (asw, od->left_side,
											 myframe_has_parts (frame, FRAME_LEFT_MASK));
		check_side_canvas (asw, od->right_side,
											 myframe_has_parts (frame, FRAME_RIGHT_MASK));
	}

	if (ASWIN_HFLAGS (asw, AS_Handles)
			&& get_flags (frame->flags, MYFRAME_NOBORDER)) {
		XSetWindowBorderWidth (dpy, asw->frame, 0);
		asw->status->frame_border_width = 0;
	} else if (asw->hints && get_flags (asw->hints->flags, AS_Border)) {
		XSetWindowBorderWidth (dpy, asw->frame, asw->hints->border_width);
		asw->status->frame_border_width = asw->hints->border_width;
	}

	/* make sure all our decoration windows are mapped and in proper order: */
	if (!ASWIN_GET_FLAGS (asw, AS_Dead))
		XRaiseWindow (dpy, asw->w);

	/* 6) now we have to create bar for icon - if it is not client's animated icon */
	if (asw->icon_canvas) {
		if (old_hints) {
			icon_image_changed =
					(get_flags
					 (asw->hints->client_icon_flags,
						AS_ClientIcon | AS_ClientIconPixmap) !=
					 get_flags (old_hints->client_icon_flags,
											AS_ClientIcon | AS_ClientIconPixmap));
			if (!icon_image_changed)
				icon_image_changed =
						(asw->hints->icon.pixmap != old_hints->icon.pixmap);
			if (!icon_image_changed)
				icon_image_changed =
						(mystrcmp (asw->hints->icon_file, old_hints->icon_file) != 0);
		}

		if (icon_image_changed) {
			ASImage *icon_image =	get_client_icon_image (ASDefaultScr, asw->hints, 128);
			check_tbar (&(asw->icon_button), (asw->icon_canvas != NULL), AS_ICON_MYSTYLE, icon_image, Scr.Look.ButtonWidth, Scr.Look.ButtonHeight,	/* scaling icon image */
									0, Scr.Look.ButtonAlign,
									Scr.Look.ButtonBevel, Scr.Look.ButtonBevel,
									TEXTURE_TRANSPIXMAP_ALPHA, TEXTURE_TRANSPIXMAP_ALPHA,
									C_IconButton, False);

			if (icon_image)
				safe_asimage_destroy (icon_image);
		}
	}
	if (asw->icon_button && old_hints == NULL) {
		set_astbar_style (asw->icon_button, BAR_STATE_UNFOCUSED,
											AS_ICON_MYSTYLE);
		set_astbar_style (asw->icon_button, BAR_STATE_FOCUSED,
											AS_ICON_MYSTYLE);
		asw->icon_button->h_border = asw->icon_button->v_border =
				Scr.Look.ButtonIconSpacing;
	}

	/* 7) now we have to create bar for icon title (optional) */
	check_tbar (&(asw->icon_title),
							get_flags (Scr.Feel.flags, IconTitle)
							&&
							((asw->icon_canvas != NULL
								&& !get_flags (Scr.Look.flags, SeparateButtonTitle))
							 || asw->icon_title_canvas != NULL), AS_ICON_TITLE_MYSTYLE,
							NULL, 0, 0, 0, ALIGN_CENTER, DEFAULT_TBAR_HILITE,
							DEFAULT_TBAR_HILITE, TEXTURE_TRANSPIXMAP_ALPHA,
							TEXTURE_TRANSPIXMAP_ALPHA, C_IconTitle, False);

	if (asw->icon_title) {
		LOCAL_DEBUG_OUT ("setting icon label to %s", ASWIN_ICON_NAME (asw));
		add_astbar_label (asw->icon_title, 0, 0, 0, frame->title_align,
											DEFAULT_TBAR_HSPACING, DEFAULT_TBAR_VSPACING,
											ASWIN_ICON_NAME (asw), ASWIN_ICON_NAME_ENC (asw));
		set_astbar_style (asw->icon_title, BAR_STATE_FOCUSED,
											AS_ICON_TITLE_MYSTYLE);
		set_astbar_style (asw->icon_title, BAR_STATE_UNFOCUSED,
											AS_ICON_TITLE_UNFOCUS_MYSTYLE);
	}

	if (asw->hints->mystyle_names[BACK_FOCUSED])
		mystyle_name = asw->hints->mystyle_names[BACK_FOCUSED];
	if (frame->title_style_names[BACK_FOCUSED])
		mystyle_name = frame->title_style_names[BACK_FOCUSED];
	frame_mystyle_name = frame->frame_style_names[BACK_FOCUSED];

	/* 8) now we have to create actuall bars - for each frame element plus one for the titlebar */
	for (i = 0; i < FRAME_SIDES; ++i)
		clear_flags (asw->internal_flags,
								 ASWF_FirstCornerFollowsTbarSize << i);
	if (old_hints == NULL
			|| (get_flags (old_hints->flags, AS_Handles) !=
					ASWIN_HFLAGS (asw, AS_Handles)) || old_frame != frame) {
		status_changed = True;
		if (ASWIN_HFLAGS (asw, AS_Handles)) {
			for (i = 0; i < FRAME_PARTS; ++i) {
				ASImage *img = NULL;
				unsigned int real_part = od->frame_rotation[i];
				int part_width = frame->part_width[i];
				int part_length = frame->part_length[i];

				img = frame->parts[i] ? frame->parts[i]->image : NULL;

				if (img == NULL) {
					if (i < FRAME_SIDES) {
						if (part_width == 0)
							part_width = BOUNDARY_WIDTH;
						if (part_length == 0)
							part_length = 1;

					} else {
						if (part_width == 0) {
							part_width = CORNER_WIDTH;
							set_flags (asw->internal_flags,
												 ASWF_FirstCornerFollowsTbarSize << (i -
																														 FRAME_SIDES));
						}
						if (part_length == 0)
							part_length = BOUNDARY_WIDTH;
					}
				}

				if ((0x01 << i) & MYFRAME_HOR_MASK) {
					*(od->in_width) = part_length;
					*(od->in_height) = part_width;
				} else {
					*(od->in_width) = part_width;
					*(od->in_height) = part_length;
				}
				LOCAL_DEBUG_OUT
						("part(%d)->real_part(%d)->from_size(%ux%u)->in_size(%ux%u)->out_size(%ux%u)",
						 i, real_part, frame->part_width[i], frame->part_length[i],
						 *(od->in_width), *(od->in_height), *(od->out_width),
						 *(od->out_height));
				check_tbar (&(asw->frame_bars[real_part]), IsFramePart (frame, i),
										frame_mystyle_name ? frame_mystyle_name : mystyle_name,
										img, *(od->out_width), *(od->out_height), od->flip,
										frame->part_align[i], frame->part_fbevel[i],
										frame->part_ubevel[i], TEXTURE_TRANSPIXMAP_ALPHA,
										TEXTURE_TRANSPIXMAP_ALPHA, frame_contexts[i],
										(i < FRAME_SIDES) ? &(frame->part_slicing[i]) : NULL);
			}
		} else
			for (i = 0; i < FRAME_PARTS; ++i)
				check_tbar (&(asw->frame_bars[i]), False, NULL, NULL, 0, 0, 0, 0,
										0, 0, 0, 0, C_NO_CONTEXT, NULL);

		check_tbar (&(asw->tbar), has_tbar, mystyle_name, NULL, 0, 0,
								od->flip,
								frame->title_align,
								frame->title_fbevel, frame->title_ubevel,
								frame->title_fcm, frame->title_ucm, C_TITLE, NULL);
		if (asw->tbar) {
			int fhue = -1, fsat = -1, uhue = -1, usat = -1;
			if (get_flags (frame->set_title_attr, MYFRAME_TitleFHueSet))
				fhue = frame->title_fhue;
			if (get_flags (frame->set_title_attr, MYFRAME_TitleFSatSet))
				fsat = frame->title_fsat;
			if (get_flags (frame->set_title_attr, MYFRAME_TitleUHueSet))
				uhue = frame->title_fhue;
			if (get_flags (frame->set_title_attr, MYFRAME_TitleUSatSet))
				usat = frame->title_fsat;

			set_astbar_huesat (asw->tbar, BAR_STATE_FOCUSED, fhue, fsat);
			set_astbar_huesat (asw->tbar, BAR_STATE_UNFOCUSED, uhue, usat);
		}

		tbar_created = (asw->tbar != NULL);
	}

	if (asw->tbar) {							/* 9) now we have to setup titlebar buttons */
		ASFlagType title_align = frame->title_align;
		ASFlagType btn_mask = compile_titlebuttons_mask (asw->hints);
#ifdef SHAPE
		if (get_flags (frame->condense_titlebar, ALIGN_LEFT | ALIGN_RIGHT))
			ASWIN_SET_FLAGS (asw, AS_ShapedDecor);
#endif

		if (old_hints == NULL ||
				get_flags (old_hints->flags,
									 AS_VerticalTitle) != ASWIN_HFLAGS (asw,
																											AS_VerticalTitle)) {
			set_astbar_flip (asw->tbar,
											 ASWIN_HFLAGS (asw,
																		 AS_VerticalTitle) ? FLIP_VERTICAL :
											 0);
			if (!tbar_created) {
				tbar_created = True;
				delete_astbar_tile (asw->tbar, -1);
			}
		}
		/* need to add some titlebuttons */
		/* Don't need to do this as we already have padding set for label
		 * if( tbar_created )
		 *  {
		 *        asw->tbar->h_spacing = DEFAULT_TBAR_SPACING ;
		 *        asw->tbar->v_spacing = DEFAULT_TBAR_SPACING ;
		 *  }
		 */

		if (!tbar_created && old_hints != NULL) {
			ASFlagType old_btn_mask = compile_titlebuttons_mask (old_hints);
			if (old_btn_mask != btn_mask)
				tbar_created = True;
			else if (frame != old_frame)
				tbar_created = True;
			if (tbar_created)
				delete_astbar_tile (asw->tbar, -1);
		}

		if (!tbar_created) {
			if (old_hints == NULL ||
					mystrcmp (asw->hints->names[0], old_hints->names[0]) != 0) {
				ASCanvas *canvas =
						ASWIN_HFLAGS (asw,
													AS_VerticalTitle) ? asw->
						frame_sides[FR_W] : asw->frame_sides[FR_N];
				/* label ( goes on top of above pixmap ) */
				if (change_astbar_first_label
						(asw->tbar, ASWIN_NAME (asw), ASWIN_NAME_ENCODING (asw)))
					if (canvas) {
						render_astbar (asw->tbar, canvas);
						invalidate_canvas_save (canvas);
						update_canvas_display (canvas);
					}
			}
		} else {
			Bool rtitle_spacer_added = False;
			Bool ltitle_spacer_added = False;
			unsigned int tbar_layout_row[MYFRAME_TITLE_BACKS];
			unsigned int tbar_layout_col[MYFRAME_TITLE_BACKS];

			compile_tbar_layout (tbar_layout_row, od->default_tbar_elem_row,
													 frame->left_layout, frame->right_layout);
			compile_tbar_layout (tbar_layout_col, od->default_tbar_elem_col,
													 frame->left_layout, frame->right_layout);

#if defined(LOCAL_DEBUG) && !defined(NO_DEBUG_OUTPU)
			for (i = 0; i < MYFRAME_TITLE_BACKS; ++i) {
				LOCAL_DEBUG_OUT ("TitlebarLayout col[%d] = %d", i,
												 tbar_layout_col[i]);
				LOCAL_DEBUG_OUT ("TitlebarLayout row[%d] = %d", i,
												 tbar_layout_row[i]);
			}
#endif

			for (i = MYFRAME_TITLE_BACK_LBTN; i < MYFRAME_TITLE_BACKS; ++i)
				if (frame->title_backs[i] && frame->title_backs[i]->image) {
					LOCAL_DEBUG_OUT ("Adding Title Back #%d", i);
					if (frame->title_backs_slicing[i].flags != 0) {
						ASImage *im = frame->title_backs[i]->image;
						int xs = 0, xe = 0, ys = im->width, ye = im->height;
						geometry2slicing (frame->title_backs_slicing[i], &xs, &xe, &ys,
															&ye);
						add_astbar_image (asw->tbar,
															tbar_layout_col[ASO_TBAR_ELEM_LBTN + i],
															tbar_layout_row[ASO_TBAR_ELEM_LBTN + i],
															od->flip,
															fix_background_align (frame->
																										title_backs_align[i]),
															im, xs, xe, ys, ye);
					} else
						add_astbar_icon (asw->tbar,
														 tbar_layout_col[ASO_TBAR_ELEM_LBTN + i],
														 tbar_layout_row[ASO_TBAR_ELEM_LBTN + i],
														 od->flip,
														 fix_background_align (frame->
																									 title_backs_align[i]),
														 frame->title_backs[i]->image);
					if (i == MYFRAME_TITLE_BACK_RTITLE_SPACER)
						rtitle_spacer_added = True;
					else if (i == MYFRAME_TITLE_BACK_LTITLE_SPACER)
						ltitle_spacer_added = True;

				}
#if 1
			/* special handling to accomodate FIT_LABEL_SIZE alignment of the title */
			if (frame->title_backs[MYFRAME_TITLE_BACK_LBL] &&
					frame->title_backs[MYFRAME_TITLE_BACK_LBL]->image) {
				LOCAL_DEBUG_OUT ("title_back_align = 0x%lX",
												 frame->title_backs_align[MYFRAME_TITLE_BACK_LBL]);
				if (get_flags
						(frame->title_backs_align[MYFRAME_TITLE_BACK_LBL],
						 FIT_LABEL_SIZE)) {
					/* left spacer  - if we have an icon to go under the label if align is right or center */
					if (get_flags (frame->title_align, ALIGN_RIGHT)
							&& !ltitle_spacer_added)
						add_astbar_spacer (asw->tbar,
															 tbar_layout_col
															 [ASO_TBAR_ELEM_LTITLE_SPACER],
															 tbar_layout_row
															 [ASO_TBAR_ELEM_LTITLE_SPACER], od->flip,
															 PAD_LEFT, 1, 1);

					/* right spacer - if we have an icon to go under the label and align is left or center */
					if (get_flags (frame->title_align, ALIGN_LEFT)
							&& !rtitle_spacer_added)
						add_astbar_spacer (asw->tbar,
															 tbar_layout_col
															 [ASO_TBAR_ELEM_RTITLE_SPACER],
															 tbar_layout_row
															 [ASO_TBAR_ELEM_RTITLE_SPACER], od->flip,
															 PAD_RIGHT, 1, 1);
					title_align = 0;
				}
			}
#endif

			/* label ( goes on top of above pixmap ) */
			add_astbar_label (asw->tbar,
												tbar_layout_col[ASO_TBAR_ELEM_LBL],
												tbar_layout_row[ASO_TBAR_ELEM_LBL],
												od->flip,
												title_align, frame->title_h_spacing,
												frame->title_v_spacing, ASWIN_NAME (asw),
												ASWIN_NAME_ENCODING (asw));

			/* all the buttons go after the label to be rendered over it */
			/* left buttons : */
			add_astbar_btnblock (asw->tbar,
													 tbar_layout_col[ASO_TBAR_ELEM_LBTN],
													 tbar_layout_row[ASO_TBAR_ELEM_LBTN],
													 od->flip, frame->left_btn_align,
													 &(Scr.Look.ordered_buttons[0]), btn_mask,
													 Scr.Look.button_first_right,
													 Scr.Look.TitleButtonXOffset[0],
													 Scr.Look.TitleButtonYOffset[0],
													 Scr.Look.TitleButtonSpacing[0],
													 od->left_btn_order);

			/* right buttons : */
			add_astbar_btnblock (asw->tbar,
													 tbar_layout_col[ASO_TBAR_ELEM_RBTN],
													 tbar_layout_row[ASO_TBAR_ELEM_RBTN],
													 od->flip, frame->right_btn_align,
													 &(Scr.Look.
														 ordered_buttons[Scr.Look.button_first_right]),
													 btn_mask,
													 TITLE_BUTTONS - Scr.Look.button_first_right,
													 Scr.Look.TitleButtonXOffset[1],
													 Scr.Look.TitleButtonYOffset[1],
													 Scr.Look.TitleButtonSpacing[1],
													 od->right_btn_order);
			/* titlebar balloons */
			for (i = 0; i < TITLE_BUTTONS; ++i) {
				if (get_flags (btn_mask, C_TButton0 << i)) {
					char *str =
							list_functions_by_context (C_TButton0 << i, asw->hints);
					LOCAL_DEBUG_OUT ("balloon text will be \"%s\"",
													 str ? str : "none");
					set_astbar_balloon2 (asw->tbar, TitlebarBalloons,
															 C_TButton0 << i, str, AS_Text_ASCII);
					if (str)
						free (str);
				}
			}
		}
	}

	if (ASWIN_HFLAGS (asw, AS_WindowOpacity)) {
		set_32bit_property (asw->frame, _XA_NET_WM_WINDOW_OPACITY, XA_CARDINAL,
												asw->hints->window_opacity);
	}


	/* we also need to setup label, unfocused/sticky style and tbar sizes -
	 * it all is done when we change windows state, or move/resize it */
	/*since we might have destroyed/created some windows - we have to refresh grabs : */
	grab_window_input (asw, False);
	if (get_flags (asw->internal_flags, ASWF_WindowComplete)) {
		/* we need to map all the possibly created subwindows if window is not iconic */
		if (!ASWIN_GET_FLAGS (asw, AS_Iconic)) {
			for (i = 0; i < FRAME_SIDES; ++i)
				map_canvas_window (asw->frame_sides[i], False);

/*			LOCAL_DEBUG_OUT("mapping frame subwindows for client %lX, frame canvas = %p", asw->w, asw->frame_canvas );
			XMapSubwindows(dpy, asw->frame); */
		} else {
			map_canvas_window (asw->icon_canvas, False);
			map_canvas_window (asw->icon_title_canvas, False);
		}
	}

	return status_changed;
}


/*************************************************************************/
/*   Main window decoration routine                                      */
/*************************************************************************/
void redecorate_window (ASWindow * asw, Bool free_resources)
{
	int i;

	LOCAL_DEBUG_OUT ("as?w(%p)->free_res(%d)", asw, free_resources);
	if (AS_ASSERT (asw))
		return;

	if (free_resources || asw->hints == NULL || asw->status == NULL) {	/* destroy window decorations here : */
		/* destruction goes in reverese order ! */
		check_tbar (&(asw->icon_title), False, NULL, NULL, 0, 0, 0, 0, 0, 0, 0,
								0, C_NO_CONTEXT, NULL);
		check_tbar (&(asw->icon_button), False, NULL, NULL, 0, 0, 0, 0, 0, 0,
								0, 0, C_NO_CONTEXT, NULL);
		check_tbar (&(asw->tbar), False, NULL, NULL, 0, 0, 0, 0, 0, 0, 0, 0,
								C_NO_CONTEXT, NULL);
		i = FRAME_PARTS;
		while (--i >= 0)
			check_tbar (&(asw->frame_bars[i]), False, NULL, NULL, 0, 0, 0, 0, 0,
									0, 0, 0, C_NO_CONTEXT, NULL);

		check_side_canvas (asw, FR_W, False);
		check_side_canvas (asw, FR_E, False);
		check_side_canvas (asw, FR_S, False);
		check_side_canvas (asw, FR_N, False);

		if (asw->icon_canvas == asw->icon_title_canvas)
			asw->icon_title_canvas = NULL;
		if (asw->icon_title_canvas && asw->icon_title_canvas->w)
			check_icon_title_canvas (asw, False, False);
		if (asw->icon_canvas && asw->icon_canvas->w)
			check_icon_canvas (asw, False);
		check_client_canvas (asw, False);
		check_frame_canvas (asw, False);

		return;
	}

	/* 1) we need to create our frame window : */
	if (check_frame_canvas (asw, True) == NULL)
		return;

	/* 2) we need to reparent our title window : */
	if (check_client_canvas (asw, True) == NULL)
		return;

	hints2decorations (asw, NULL);

}

/****************************************************************************
 *
 * Sets up the shaped window borders
 *
 ****************************************************************************/
void SetShape (ASWindow * asw, int w)
{
#ifdef SHAPE
	LOCAL_DEBUG_CALLER_OUT ("asw(%p)->client(%lX)", asw, asw ? asw->w : 0);

	if (asw) {

		if (ASWIN_GET_FLAGS (asw, AS_Dead)) {
			clear_canvas_shape (asw->frame_canvas, True);
			return;
		}

		if (ASWIN_GET_FLAGS (asw, AS_Iconic)) {	/* todo: update icon's shape */

		} else {
			int i, tbar_side = -1;
			int child_x = 0;
			int child_y = 0;
			unsigned int width, height, bw;
			XRectangle rect;

			/* starting with an empty list of rectangles */
			if (asw->frame_canvas->shape)
				flush_vector (asw->frame_canvas->shape);
			else
				asw->frame_canvas->shape = create_shape ();

			get_current_canvas_geometry (asw->client_canvas, &child_x, &child_y,
																	 &width, &height, &bw);
			rect.x = 0;
			rect.y = 0;
			rect.width = width;
			rect.height = height;

			/* adding rectangles for all the frame decorations */
			if (asw->shading_steps == 0 && ASWIN_GET_FLAGS (asw, AS_Shaded)) {
				ASOrientation *od = get_orientation_data (asw);
				tbar_side = od->tbar_side;
			}
			for (i = 0; i < FRAME_SIDES; ++i)
				if (asw->frame_sides[i] && (i == tbar_side || tbar_side == -1)) {
					int x, y;
					unsigned int s_width, s_height, bw;
					LOCAL_DEBUG_OUT (" Frame side %d", i);
					get_current_canvas_geometry (asw->frame_sides[i], &x, &y,
																			 &s_width, &s_height, &bw);

					combine_canvas_shape_at_geom (asw->frame_canvas,
																				asw->frame_sides[i], x, y, s_width,
																				s_height, bw);
/*                  	combine_canvas_shape( asw->frame_canvas, asw->frame_sides[i] ); */
				}

			/* subtract client rectangle, before adding clients shape */
			subtract_shape_rectangle (asw->frame_canvas->shape, &rect, 1,
																child_x, child_y, asw->frame_canvas->width,
																asw->frame_canvas->height);
			/* add clients shape */
			combine_canvas_shape_at_geom (asw->frame_canvas, asw->client_canvas,
																		child_x, child_y, width, height, bw);

#ifndef NO_DEBUG_OUTPUT
			print_shape (asw->frame_canvas->shape);
#endif
			/* apply shape */
			update_canvas_display_mask (asw->frame_canvas, True);
			set_flags (asw->internal_flags, ASWF_PendingShapeRemoval);

		}
	}
#endif													/* SHAPE */
}

void ClearShape (ASWindow * asw)
{
#ifdef SHAPE
	if (asw && asw->frame_canvas) {
		clear_canvas_shape (asw->frame_canvas, True);
		clear_flags (asw->internal_flags, ASWF_PendingShapeRemoval);
	}
#endif													/* SHAPE */
}
