/*
 * Copyright (C) 2002 Sasha Vasko <sasha at aftercode.net>
 *
 * 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.
 *
 */
#undef LOCAL_DEBUG
#undef DO_CLOCKING

#include "../configure.h"
#include "asapp.h"
#include "afterstep.h"
#include "mystyle.h"
#include "screen.h"
#include "../libAfterImage/afterimage.h"
#include "myicon.h"
#include "canvas.h"
#include "decor.h"
#include "event.h"
#include "balloon.h"
#include "shape.h"

static ASTBarData *FocusedBar = NULL;	/* currently focused bar with balloon shown for it */

/********************************************************************/
/* ASTBtnData :                                                     */
/********************************************************************/
ASTBtnData *create_astbtn ()
{
	return safecalloc (1, sizeof (ASTBtnData));
}

static void free_tbtn_images (ASTBtnData * btn)
{
	if (btn->pressed) {
/*        safe_asimage_destroy (btn->pressed); */
		btn->pressed = NULL;
	}

	if (btn->unpressed) {
/*        safe_asimage_destroy (btn->unpressed); */
		btn->unpressed = NULL;
	}
	btn->current = NULL;
}

void set_tbtn_images (ASTBtnData * btn, struct button_t *from)
{
	Bool pressed = False;

	if (AS_ASSERT (btn) || from == NULL)
		return;

	if (btn->current != NULL)
		pressed = (btn->current == btn->pressed);
	free_tbtn_images (btn);

	btn->pressed = from->pressed.image;
	btn->unpressed = from->unpressed.image;
	btn->current = (pressed && btn->pressed) ? btn->pressed : btn->unpressed;
	btn->width = from->width;
	btn->height = from->height;
}

ASTBtnData *make_tbtn (struct button_t *from)
{
	ASTBtnData *btn = NULL;

	if (from) {
		btn = safecalloc (1, sizeof (ASTBtnData));
		set_tbtn_images (btn, from);
	}
	return btn;
}

void destroy_astbtn (ASTBtnData ** ptbtn)
{
	if (!AS_ASSERT (ptbtn)) {
		ASTBtnData *btn = *ptbtn;

		if (btn) {
			free_tbtn_images (btn);
			if (btn->balloon)
				destroy_asballoon (&(btn->balloon));
			memset (btn, 0x00, sizeof (ASTBtnData));
			free (btn);
		}
		*ptbtn = NULL;
	}
}

/********************************************************************/
/* ASBtnBlock :                                                    */
/********************************************************************/
void free_asbtn_block (ASTile * tile)
{
	register ASBtnBlock *blk = &(tile->data.bblock);

	if (blk->buttons) {
		register int i = blk->buttons_num;

		while (--i >= 0) {
			free_tbtn_images (&(blk->buttons[i]));
			if (blk->buttons[i].balloon)
				destroy_asballoon (&(blk->buttons[i].balloon));
		}
		free (blk->buttons);
	}

	memset (blk, 0x00, sizeof (ASBtnBlock));
	ASSetTileType (tile, AS_TileFreed);
}

static void
build_btn_block (ASTile * tile,
								 struct button_t **from_list, ASFlagType context_mask,
								 unsigned int count, int left_margin, int top_margin,
								 int spacing, int order)
{

	unsigned int real_count = 0;
	unsigned short max_width = 0, max_height = 0;
	register int i = count;
	ASBtnBlock *blk = &(tile->data.bblock);

	LOCAL_DEBUG_CALLER_OUT ("count(%d),lm(%d),tm(%d),sp(%d),or(%d)",
													left_margin, top_margin, spacing, order);
	if (count > 0)
		if (!AS_ASSERT (from_list))
			while (--i >= 0)
				if (from_list[i] != NULL &&
						(context_mask & from_list[i]->context) != 0 &&
						(from_list[i]->unpressed.image || from_list[i]->pressed.image))
				{
					++real_count;
					if (from_list[i]->width > max_width)
						max_width = from_list[i]->width;
					if (from_list[i]->height > max_height)
						max_height = from_list[i]->height;
				}
	LOCAL_DEBUG_OUT ("realcount = %d, max_size = %dx%d", real_count,
									 max_width, max_height);
	if (real_count > 0) {
		int k = real_count - 1;
		int pos =
				get_flags (order, TBTN_ORDER_REVERSE) ? -left_margin : left_margin;

		blk->buttons = safecalloc (real_count, sizeof (ASTBtnData));
		blk->buttons_num = real_count;
		i = count - 1;
		while (i >= 0 && k >= 0) {
			if (from_list[i] != NULL &&
					(context_mask & from_list[i]->context) != 0 &&
					(from_list[i]->unpressed.image || from_list[i]->pressed.image)) {
				set_tbtn_images (&(blk->buttons[k]), from_list[i]);
				blk->buttons[k].context = from_list[i]->context;
				--k;
			}
			--i;
		}

		/* top_margin surrounds button block from both sides ! */
		if (get_flags (order, TBTN_ORDER_VERTICAL)) {
			tile->width = max_width + top_margin * 2;
			tile->height = left_margin * 2;
			/*pos = get_flags(order, TBTN_ORDER_REVERSE)?-top_margin:top_margin ; */
		} else {
			tile->width = left_margin * 2;
			tile->height = max_height + top_margin * 2;
		}

		k = 0;
		while (k < real_count) {
			if (get_flags (order, TBTN_ORDER_VERTICAL)) {
				blk->buttons[k].x =
						top_margin + ((max_width - blk->buttons[k].width) >> 1);
				tile->height += blk->buttons[k].height + spacing;
				if (get_flags (order, TBTN_ORDER_REVERSE)) {
					pos -= blk->buttons[k].height;
					blk->buttons[k].y = pos;
					pos -= spacing;
				} else {
					blk->buttons[k].y = pos;
					pos += blk->buttons[k].height;
					pos += spacing;
				}
			} else {
				blk->buttons[k].y =
						top_margin + ((max_height - blk->buttons[k].height) >> 1);
				tile->width += blk->buttons[k].width + spacing;
				if (get_flags (order, TBTN_ORDER_REVERSE)) {
					pos -= blk->buttons[k].width;
					blk->buttons[k].x = pos;
					pos -= spacing;
				} else {
					blk->buttons[k].x = pos;
					pos += blk->buttons[k].width;
					pos += spacing;
				}
			}
			++k;
		}
		if (get_flags (order, TBTN_ORDER_VERTICAL)) {
			if (tile->height > left_margin * 2)
				tile->height -= spacing;
		} else {
			if (tile->width > left_margin * 2)
				tile->width -= spacing;
		}
		if (get_flags (order, TBTN_ORDER_REVERSE)) {
			k = real_count;
			if (get_flags (order, TBTN_ORDER_VERTICAL))
				while (--k >= 0)
					blk->buttons[k].y += tile->height;
			else
				while (--k >= 0)
					blk->buttons[k].x += tile->width;
		}
		LOCAL_DEBUG_OUT ("real_count=%d", real_count);
	} else {
		tile->width = 1;
		tile->height = 1;
	}
	ASSetTileSublayers (*tile, real_count);
}

static int check_btn_point (ASTile * tile, int x, int y)
{
	ASBtnBlock *bb = &(tile->data.bblock);
	register int i = bb->buttons_num;

	while (--i >= 0) {
		register ASTBtnData *btn = &(bb->buttons[i]);
		int tmp = x - btn->x;

		if (tmp >= 0 && tmp < btn->width) {
			tmp = y - btn->y;
			if (tmp >= 0 && tmp < btn->height)
				return btn->context;
		}
	}
	return C_NO_CONTEXT;
}

Bool set_tbtn_pressed (ASBtnBlock * bb, int context)
{
	register int i = bb->buttons_num;
	Bool changed = False;

	while (--i >= 0) {
		register ASTBtnData *btn = &(bb->buttons[i]);
		ASImage *new_current;

		new_current =
				(context & (btn->context)) ? btn->pressed : btn->unpressed;
		if (new_current == NULL)
			new_current = (btn->pressed == NULL) ? btn->unpressed : btn->pressed;
		if (new_current != btn->current) {
			changed = True;
			btn->current = new_current;
		}
	}
	return changed;
}


static int
set_asbtn_block_layer (ASTile * tile, ASImageLayer * layer,
											 unsigned int state, ASImage ** scrap_images,
											 int max_width, int max_height)
{
	register ASBtnBlock *bb = &(tile->data.bblock);
	register int i = bb->buttons_num;

	while (--i >= 0) {
		register ASTBtnData *btn = &(bb->buttons[i]);

		if (btn && btn->current) {
			layer[i].im = btn->current;
			layer[i].dst_x = tile->x + btn->x;
			layer[i].dst_y = tile->y + btn->y;
			layer[i].clip_width = layer[i].im->width;
			layer[i].clip_height = layer[i].im->height;
		}
	}
	return bb->buttons_num;
}


/********************************************************************/
/* ASIcon :                                                         */
/********************************************************************/
static void free_asimage_tile (ASTile * tile)
{
	if (tile->data.image.im) {
		safe_asimage_destroy (tile->data.image.im);
		tile->data.image.im = NULL;
	}
	ASSetTileType (tile, AS_TileFreed);
}

static int
set_asimage_layer (ASTile * tile, ASImageLayer * layer, unsigned int state,
									 ASImage ** scrap_images, int max_width, int max_height)
{
	int dst_width, dst_height;
	ASImage *im = tile->data.image.im;
	unsigned int slice_x_start = tile->data.image.slice_x_start;
	unsigned int slice_x_end = tile->data.image.slice_x_end;
	unsigned int slice_y_start = tile->data.image.slice_y_start;
	unsigned int slice_y_end = tile->data.image.slice_y_end;
	Bool do_slice_x = ((slice_x_start != 0 || slice_x_end != 0)
										 && get_flags (tile->flags, AS_TileHResize));
	Bool do_slice_y = ((slice_y_start != 0 || slice_y_end != 0)
										 && get_flags (tile->flags, AS_TileVResize));

	if (im == NULL)
		return 0;

	dst_width = im->width;
	dst_height = im->height;

	/* if( ASTileResizeable( *tile ) ) */
	{
		if (get_flags (tile->flags, AS_TileHScale) || do_slice_x)
			dst_width = max_width;
		else if (get_flags (tile->flags, AS_TileHResize)
						 && max_width < im->width) {
			if (get_flags (tile->flags, AS_TilePadLeft)) {
				if (get_flags (tile->flags, AS_TilePadRight))
					layer->clip_x = ((int)im->width - max_width) / 2;
				else
					layer->clip_x = ((int)im->width - max_width);
			}
		}
		if (get_flags (tile->flags, AS_TileVScale) || do_slice_y)
			dst_height = max_height;
		else if (get_flags (tile->flags, AS_TileVResize)
						 && max_height < im->height) {
			if (get_flags (tile->flags, AS_TilePadTop)) {
				if (get_flags (tile->flags, AS_TilePadBottom))
					layer->clip_y = ((int)im->height - max_height) / 2;
				else
					layer->clip_y = ((int)im->height - max_height);
			}
		}
	}
	LOCAL_DEBUG_OUT
			("flags = %lX, dst_size = %dx%d, im_size = %dx%d, max_size = %dx%d, clip = %+d%+d",
			 tile->flags, dst_width, dst_height, im->width, im->height,
			 max_width, max_height, layer->clip_x, layer->clip_y);
	if (im->width != dst_width || im->height != dst_height) {
		if (do_slice_x || do_slice_y)
			im = slice_asimage2 (ASDefaultVisual, im, slice_x_start, slice_x_end,
													 slice_y_start, slice_y_end, dst_width,
													 dst_height, get_flags (tile->flags,
																									AS_TileHScale |
																									AS_TileVScale),
													 ASA_ASImage, 0, ASIMAGE_QUALITY_DEFAULT);
		else
			im = scale_asimage (ASDefaultVisual, im, dst_width, dst_height,
													ASA_ASImage, 0, ASIMAGE_QUALITY_DEFAULT);

		if (im == NULL)
			im = tile->data.image.im;
		else
			*scrap_images = im;
	}

	layer->im = im;
	layer->dst_x = tile->x - layer->clip_x;
	layer->dst_y = tile->y - layer->clip_y;
	if (ASTileHResizeable (*tile))
		layer->clip_width = max_width;
	else
		layer->clip_width = layer->im->width;

	if (ASTileVResizeable (*tile))
		layer->clip_height = max_height;
	else
		layer->clip_height = layer->im->height;

	return 1;
}

/********************************************************************/
/* ASLabel :                                                        */
/********************************************************************/
static void free_aslabel (ASTile * tile)
{
	register ASLabel *lbl = &(tile->data.label);
	register int i;

	for (i = 0; i < BAR_STATE_NUM; ++i) {
		if (lbl->rendered[i])
			safe_asimage_destroy (lbl->rendered[i]);
		lbl->rendered[i] = NULL;
	}
	if (lbl->text) {
		free (lbl->text);
		lbl->text = NULL;
	}
	ASSetTileType (tile, AS_TileFreed);
}

static void
aslabel_style_changed (ASTile * tile, MyStyle * style, unsigned int state)
{
	register ASLabel *lbl = &(tile->data.label);
	register int i;
	ASImage *im;
	int flip = ASTileFlip (*tile);

	if (lbl->rendered[state] != NULL) {
		safe_asimage_destroy (lbl->rendered[state]);
		lbl->rendered[state] = NULL;
	}

	im = mystyle_draw_text_image (style, lbl->text, lbl->encoding);
	LOCAL_DEBUG_OUT
			("state(%d)->style(\"%s\")->text(\"%s\")->image(%p)->flip(%d)",
			 state, style ? style->name : "none", lbl->text, im, flip);
	if (flip != 0) {
		int w = im->width;
		int h = im->height;

		if (get_flags (flip, FLIP_VERTICAL)) {
			w = im->height;
			h = im->width;
		}
		lbl->rendered[state] = flip_asimage (ASDefaultVisual,
																				 im, 0, 0, w, h, flip, ASA_ASImage,
																				 100, ASIMAGE_QUALITY_DEFAULT);
		safe_asimage_destroy (im);
	} else
		lbl->rendered[state] = im;

	tile->width = 0;
	tile->height = 0;
	for (i = 0; i < BAR_STATE_NUM; ++i)
		if (lbl->rendered[i]) {
			if (tile->width < lbl->rendered[i]->width)
				tile->width = lbl->rendered[i]->width;
			if (tile->height < lbl->rendered[i]->height)
				tile->height = lbl->rendered[i]->height;
		}
	if (get_flags (flip, FLIP_VERTICAL)) {
		tile->width += lbl->v_padding * 2;
		tile->height += lbl->h_padding * 2;
	} else {
		tile->width += lbl->h_padding * 2;
		tile->height += lbl->v_padding * 2;
	}
}

static int
set_aslabel_layer (ASTile * tile, ASImageLayer * layer, unsigned int state,
									 ASImage ** scrap_images, int max_width, int max_height)
{
	register ASLabel *lbl = &(tile->data.label);
	CARD32 alpha;
	short h_pad = lbl->h_padding;
	short v_pad = lbl->v_padding;

	layer->im = lbl->rendered[state];
	if (layer->im == NULL)
		if ((layer->im =
				 lbl->rendered[(~state) & BAR_STATE_FOCUS_MASK]) == NULL)
			return 0;

	if (get_flags (ASTileFlip (*tile), FLIP_VERTICAL)) {
		h_pad = lbl->v_padding;
		v_pad = lbl->h_padding;
	}

	layer->dst_x = tile->x + h_pad;
	layer->dst_y = tile->y + v_pad;
	if (layer->im->width < max_width) {
		int cell_size = tile->width - h_pad * 2;

		if (cell_size > max_width)
			cell_size = max_width;
		if (layer->im->width < tile->width)
			layer->dst_x +=
					make_tile_pad (get_flags (tile->flags, AS_TilePadLeft),
												 get_flags (tile->flags, AS_TilePadRight),
												 cell_size, layer->im->width);
		layer->clip_width = layer->im->width;
	} else
		layer->clip_width = (max_width > h_pad) ? max_width - h_pad : 1;
	if (layer->im->height < max_height) {
		int cell_size = tile->height - v_pad * 2;

		if (cell_size > max_height)
			cell_size = max_height;
		if (layer->im->height < tile->height)
			layer->dst_y +=
					make_tile_pad (get_flags (tile->flags, AS_TilePadTop),
												 get_flags (tile->flags, AS_TilePadBottom),
												 cell_size, layer->im->height);
		layer->clip_height = layer->im->height;
	} else
		layer->clip_height = (max_height > v_pad) ? max_height - v_pad : 1;

	layer->clip_height = min (layer->im->height, max_height);
	alpha = ARGB32_ALPHA8 (layer->im->back_color);
	if (alpha < 0x00FF && alpha > 1)
		layer->tint = MAKE_ARGB32 ((alpha >> 1), 0x007F, 0x007F, 0x007F);
	return 1;
}


/********************************************************************/
/* ASTBarData :                                                     */
/********************************************************************/
struct {
	char *name;
	void (*free_astile_handler) (ASTile * tile);
	int (*check_point_handler) (ASTile * tile, int x, int y);
	void (*on_style_changed_handler) (ASTile * tile, MyStyle * style,
																		unsigned int state);
	int (*set_layer_handler) (ASTile * tile, ASImageLayer * layer,
														unsigned int state, ASImage ** scrap_images,
														int max_width, int max_height);

} ASTileTypeHandlers[AS_TileTypes] = {
	{
	"Spacer", NULL, NULL, NULL}, {
	"Buttons", free_asbtn_block, check_btn_point, NULL,
				set_asbtn_block_layer}, {
	"Image", free_asimage_tile, NULL, NULL, set_asimage_layer}, {
	"Label", free_aslabel, NULL, aslabel_style_changed, set_aslabel_layer}, {
	"none", NULL, NULL, NULL}, {
	"none", NULL, NULL, NULL}, {
	"none", NULL, NULL, NULL}, {
	"freed", NULL, NULL, NULL}
};

void print_astbar_tiles (ASTBarData * tbar)
{
	register int l;

	show_progress ("tbar %p has %d tiles :", tbar, tbar->tiles_num);
	for (l = 0; l < tbar->tiles_num; ++l) {
		show_progress
				("\t %3.3d: [%2.2d][%2.2d] %s flags(%X) %ux%u%+d%+d data.raw = (0x%lx, 0x%lx, 0x%lx)",
				 l, ASTileCol (tbar->tiles[l]), ASTileRow (tbar->tiles[l]),
				 ASTileTypeHandlers[ASTileType (tbar->tiles[l])].name,
				 tbar->tiles[l].flags, tbar->tiles[l].width, tbar->tiles[l].height,
				 tbar->tiles[l].x, tbar->tiles[l].y, tbar->tiles[l].data.raw[0],
				 tbar->tiles[l].data.raw[1], tbar->tiles[l].data.raw[2]);
	}
}

ASTBarData *create_astbar ()
{
	ASTBarData *tbar = safecalloc (1, sizeof (ASTBarData));

	set_flags (tbar->state, BAR_FLAGS_REND_PENDING);
	LOCAL_DEBUG_CALLER_OUT ("<<#########>>created tbar %p", tbar);
	tbar->rendered_root_x = tbar->rendered_root_y = 0xFFFF;
	tbar->composition_method[0] = TEXTURE_TRANSPIXMAP_ALPHA;
	tbar->composition_method[1] = TEXTURE_TRANSPIXMAP_ALPHA;
	/* since saturation is allowed to take values from 0 to 100 
	   inclusive - we use -1 to indicate absence : */
	tbar->sat[0] = -1;
	tbar->sat[1] = -1;
	tbar->hue[0] = -1;
	tbar->hue[1] = -1;
	return tbar;
}

static inline void flush_tbar_backs (ASTBarData * tbar)
{
	register int i;

	for (i = 0; i < BAR_STATE_NUM; ++i)
		if (tbar->back[i]) {
			LOCAL_DEBUG_OUT ("tbar %p destroy back %d, %p", tbar, i,
											 tbar->back[i]);
			destroy_asimage (&(tbar->back[i]));
		}
	set_flags (tbar->state, BAR_FLAGS_REND_PENDING);
}

static inline void flush_tbar_state_backs (ASTBarData * tbar, int state)
{
	if (state < 0 || state > BAR_STATE_NUM)
		flush_tbar_backs (tbar);
	else {
		if (tbar->back[state])
			destroy_asimage (&(tbar->back[state]));
		set_flags (tbar->state, BAR_FLAGS_REND_PENDING);
	}
}

void destroy_astbar (ASTBarData ** ptbar)
{
	if (ptbar)
		if (*ptbar) {
			ASTBarData *tbar = *ptbar;
			register int i;

			LOCAL_DEBUG_CALLER_OUT ("<<#########>>destroying tbar %p", tbar);
			if (tbar->tiles) {
				for (i = 0; i < tbar->tiles_num; ++i) {
					int type = ASTileType (tbar->tiles[i]);

					if (ASTileTypeHandlers[type].free_astile_handler)
						ASTileTypeHandlers[type].free_astile_handler (&
																													(tbar->
																													 tiles[i]));
				}
				free (tbar->tiles);
			}

			LOCAL_DEBUG_CALLER_OUT ("<<#########>>flashing tbar %p backs", tbar);
			flush_tbar_backs (tbar);

			if (tbar == FocusedBar) {
				LOCAL_DEBUG_CALLER_OUT
						("<<#########>>withdrawing tbar balloon  %p", tbar->balloon);
				withdraw_balloon (NULL);
				FocusedBar = NULL;
			}
			if (tbar->balloon) {
				LOCAL_DEBUG_CALLER_OUT ("<<#########>>freeing tbar balloon  %p",
																tbar->balloon);
				destroy_asballoon (&(tbar->balloon));
			}

			memset (tbar, 0x00, sizeof (ASTBarData));
			LOCAL_DEBUG_CALLER_OUT ("<<#########>>freeing tbar %p memory", tbar);
			free (tbar);
			LOCAL_DEBUG_CALLER_OUT ("<<#########>>all done for tbar %p", tbar);
			*ptbar = NULL;
		}
}

unsigned int calculate_astbar_height (ASTBarData * tbar)
{
	int height = 0;
	int row_height[AS_TileRows] = { 0 };

	if (tbar) {
		register int i = tbar->tiles_num;

#if	defined(LOCAL_DEBUG) && !defined(NO_DEBUG_OUTPUT)
		print_astbar_tiles (tbar);
#endif
		while (--i >= 0)
			if (ASTileType (tbar->tiles[i]) != AS_TileFreed
					&& !ASTileIgnoreHeight (tbar->tiles[i])) {
				register int row = ASTileRow (tbar->tiles[i]);

				if (row_height[row] < tbar->tiles[i].height)
					row_height[row] = tbar->tiles[i].height;
			}

		for (i = 0; i < AS_TileRows; ++i)
			if (row_height[i] > 0) {
				if (height > 0)
					height += tbar->v_spacing;
				height += row_height[i];
			}
		if (height > 0)
			height += tbar->v_border << 1;

		height += tbar->top_bevel + tbar->bottom_bevel;
	}
	return height;
}

unsigned int calculate_astbar_width (ASTBarData * tbar)
{
	int width = 0;
	int col_width[AS_TileColumns] = { 0 };

	if (tbar) {
		register int i = tbar->tiles_num;

#if	defined(LOCAL_DEBUG) && !defined(NO_DEBUG_OUTPUT)
		print_astbar_tiles (tbar);
#endif
		while (--i >= 0)
			if (ASTileType (tbar->tiles[i]) != AS_TileFreed
					&& !ASTileIgnoreWidth (tbar->tiles[i])) {
				register int col = ASTileCol (tbar->tiles[i]);

				if (col_width[col] < tbar->tiles[i].width)
					col_width[col] = tbar->tiles[i].width;
			}

		for (i = 0; i < AS_TileColumns; ++i)
			if (col_width[i] > 0) {
				if (width > 0)
					width += tbar->h_spacing;
				width += col_width[i];
			}
		if (width > 0)
			width += tbar->h_border << 1;

		width += tbar->left_bevel + tbar->right_bevel;
	}
	return width;
}

Bool
set_astbar_size (ASTBarData * tbar, unsigned int width,
								 unsigned int height)
{
	Bool changed = False;

	if (tbar) {
		unsigned int w = width;
		unsigned int h = height;

		if (w >= MAX_POSITION)
			w = tbar->width;
		else if (w == 0)
			w = 1;
		if (h >= MAX_POSITION)
			h = tbar->height;
		else if (h == 0)
			h = 1;

		changed = (w != tbar->width || h != tbar->height);
		LOCAL_DEBUG_CALLER_OUT ("resizing TBAR %p from %dx%d to %dx%d", tbar,
														tbar->width, tbar->height, w, h);
		tbar->width = w;
		tbar->height = h;
		if (changed)
			flush_tbar_backs (tbar);
	}
	return changed;
}

static void update_astbar_bevel_size (ASTBarData * tbar)
{
	ASImageBevel bevel;
	int i;

	tbar->left_bevel = tbar->top_bevel = tbar->right_bevel =
			tbar->bottom_bevel = 0;
	for (i = 0; i < BAR_STATE_NUM; ++i)
		if (tbar->style[i]) {
			mystyle_make_bevel (tbar->style[i], &bevel, tbar->hilite[i], False);
			if (tbar->left_bevel < bevel.left_outline + bevel.left_inline)
				tbar->left_bevel = bevel.left_outline + bevel.left_inline;
			if (tbar->top_bevel < bevel.top_outline + bevel.top_inline)
				tbar->top_bevel = bevel.top_outline + bevel.top_inline;
			if (tbar->right_bevel < bevel.right_outline + bevel.right_inline)
				tbar->right_bevel = bevel.right_outline + bevel.right_inline;
			if (tbar->bottom_bevel < bevel.bottom_outline + bevel.bottom_inline)
				tbar->bottom_bevel = bevel.bottom_outline + bevel.bottom_inline;
		}
}

Bool
set_astbar_hilite (ASTBarData * tbar, unsigned int state,
									 ASFlagType hilite)
{
	Bool changed = False;

	LOCAL_DEBUG_CALLER_OUT ("%p,%d,0x%lX", tbar, state, hilite);
	if (tbar && state < BAR_STATE_NUM) {
		changed = (tbar->hilite[state] != (hilite & HILITE_MASK));
		tbar->hilite[state] = (hilite & HILITE_MASK);
		if (changed) {
			update_astbar_bevel_size (tbar);
			set_flags (tbar->state, BAR_FLAGS_REND_PENDING);
		}
	}
	return changed;
}

Bool
set_astbar_composition_method (ASTBarData * tbar, unsigned int state,
															 unsigned char method)
{
	Bool changed = False;

	if (tbar && state < BAR_STATE_NUM) {
		changed = (tbar->composition_method[state] != method);
		tbar->composition_method[state] = method;
		if (changed)
			set_flags (tbar->state, BAR_FLAGS_REND_PENDING);
	}
	return changed;
}

Bool
set_astbar_huesat (ASTBarData * tbar, unsigned int state, int hue, int sat)
{
	Bool changed = False;

	if (tbar && state < BAR_STATE_NUM) {
		changed = (tbar->hue[state] != hue || tbar->sat[state] != sat);
		tbar->hue[state] = hue;
		tbar->sat[state] = sat;
		if (changed)
			set_flags (tbar->state, BAR_FLAGS_REND_PENDING);
	}
	return changed;
}


static inline void
set_astile_styles (ASTBarData * tbar, ASTile * tile, int state)
{
	register int i;

	for (i = 0; i < BAR_STATE_NUM; ++i)
		if ((i == state || state == -1) && tbar->style[i])
			if (ASTileTypeHandlers[ASTileType (*tile)].on_style_changed_handler
					!= NULL)
				ASTileTypeHandlers[ASTileType (*tile)].on_style_changed_handler
						(tile, tbar->style[i], i);
}

Bool set_astbar_style_ptr (ASTBarData * tbar, int state, MyStyle * style)
{
	Bool changed = False;

	LOCAL_DEBUG_OUT ("bar(%p)->state(%d)->style_name(\"%s\")", tbar, state,
									 style ? style->name : "");
	if (tbar && state < BAR_STATE_NUM) {
		register int i;

		for (i = 0; i < BAR_STATE_NUM; ++i)
			if (i == state || state == -1) {
				changed = changed || (style != tbar->style[i]);
				tbar->style[i] = style;
			}
/*LOCAL_DEBUG_OUT( "style(%p)->changed(%d)->tiles_num(%d)", style, changed, tbar->tiles_num );*/
		if (changed) {
			register int i = tbar->tiles_num;

			while (--i >= 0)
				set_astile_styles (tbar, &(tbar->tiles[i]), state);

			flush_tbar_state_backs (tbar, state);
			update_astbar_bevel_size (tbar);
		}
	}
	return changed;
}


Bool set_astbar_flip (ASTBarData * tbar, int flip)
{
	Bool changed = get_flags (flip, FLIP_VERTICAL);

	if (tbar) {
		ASFlagType vert_flag = changed ? BAR_FLAGS_VERTICAL : 0;

		if (get_flags (tbar->state, BAR_FLAGS_VERTICAL) == vert_flag)
			changed = False;
/*LOCAL_DEBUG_OUT( "style(%p)->changed(%d)->tiles_num(%d)", style, changed, tbar->tiles_num );*/
		if (changed) {
			flush_tbar_backs (tbar);
			set_flags (tbar->state, vert_flag);
		}
	}
	return changed;
}

Bool
set_astbar_style (ASTBarData * tbar, unsigned int state,
									const char *style_name)
{
	Bool changed = False;

	if (tbar && state < BAR_STATE_NUM)
		return set_astbar_style_ptr (tbar, state,
																 mystyle_find_or_default (style_name));
	return changed;
}

Bool invalidate_astbar_style (ASTBarData * tbar, int state)
{
	Bool changed = False;

	if (tbar && state < BAR_STATE_NUM) {
		if (state < 0) {
			int i;

			for (i = 0; i < BAR_STATE_NUM; ++i)
				if (tbar->back[i])
					changed = invalidate_astbar_style (tbar, i);
		} else if (tbar->style[state] != NULL) {
			changed = True;
			tbar->style[state] = NULL;
			flush_tbar_state_backs (tbar, state);
		}
	}
	return changed;
}


static int
add_astbar_tile (ASTBarData * tbar, int type, unsigned char col,
								 unsigned char row, int flip, int align)
{
	int new_idx = -1;
	ASFlagType align_flags = align;

	/* try 1: see if we have any tiles that has been freed */
	while (++new_idx < tbar->tiles_num)
		if (ASTileType (tbar->tiles[new_idx]) == AS_TileFreed)
			break;

	/* try 2: allocate new memory : */
	/* allocating memory if 4 tiles increments for more efficient memory management: */
	if (new_idx == tbar->tiles_num) {
		if ((tbar->tiles_num & 0x0003) == 0)
			tbar->tiles =
					realloc (tbar->tiles,
									 (((tbar->tiles_num >> 2) + 1) << 2) * sizeof (ASTile));
		++(tbar->tiles_num);
	}
	LOCAL_DEBUG_CALLER_OUT
			("type = %d, col = %d, row = %d, flip = %d, align_flags = 0x%lX, new_idx = %d",
			 type, col, row, flip, align_flags, new_idx);

	if (get_flags (flip, FLIP_VERTICAL) && get_flags (flip, FLIP_UPSIDEDOWN)) {
		clear_flags (align_flags, PAD_MASK);
		if (get_flags (align, PAD_LEFT))
			set_flags (align_flags, PAD_TOP);
		if (get_flags (align, PAD_RIGHT))
			set_flags (align_flags, PAD_BOTTOM);
		if (get_flags (align, PAD_TOP))
			set_flags (align_flags, PAD_RIGHT);
		if (get_flags (align, PAD_BOTTOM))
			set_flags (align_flags, PAD_LEFT);
	} else if (get_flags (flip, FLIP_VERTICAL)) {
		clear_flags (align_flags, PAD_MASK);
		if (get_flags (align, PAD_LEFT))
			set_flags (align_flags, PAD_BOTTOM);
		if (get_flags (align, PAD_RIGHT))
			set_flags (align_flags, PAD_TOP);
		if (get_flags (align, PAD_TOP))
			set_flags (align_flags, PAD_LEFT);
		if (get_flags (align, PAD_BOTTOM))
			set_flags (align_flags, PAD_RIGHT);
	} else if (get_flags (flip, FLIP_UPSIDEDOWN)) {
		clear_flags (align_flags, PAD_MASK);
		if (get_flags (align, PAD_LEFT))
			set_flags (align_flags, PAD_RIGHT);
		if (get_flags (align, PAD_RIGHT))
			set_flags (align_flags, PAD_LEFT);
		if (get_flags (align, PAD_TOP))
			set_flags (align_flags, PAD_BOTTOM);
		if (get_flags (align, PAD_BOTTOM))
			set_flags (align_flags, PAD_TOP);
	}

	if (get_flags (flip, FLIP_VERTICAL)) {
		clear_flags (align_flags, RESIZE_MASK | FIT_LABEL_SIZE);
		if (get_flags (align, RESIZE_H))
			set_flags (align_flags, RESIZE_V);
		if (get_flags (align, RESIZE_V))
			set_flags (align_flags, RESIZE_H);
		if (get_flags (align, RESIZE_H_SCALE))
			set_flags (align_flags, RESIZE_V_SCALE);
		if (get_flags (align, RESIZE_V_SCALE))
			set_flags (align_flags, RESIZE_H_SCALE);
		if (get_flags (align, FIT_LABEL_WIDTH))
			set_flags (align_flags, FIT_LABEL_HEIGHT);
		if (get_flags (align, FIT_LABEL_HEIGHT))
			set_flags (align_flags, FIT_LABEL_WIDTH);
	}

	LOCAL_DEBUG_OUT
			("type = %d, flip = %d, align = 0x%X, align_flags = 0x%lX, tbar->tiles = %p",
			 type, flip, align, align_flags, tbar->tiles);
	align_flags &= (PAD_MASK | RESIZE_MASK | FIT_LABEL_SIZE);

	memset (&(tbar->tiles[new_idx]), 0x00, sizeof (ASTile));
	tbar->tiles[new_idx].flags = (type & AS_TileTypeMask) |
			((col << AS_TileColOffset) & AS_TileColMask) |
			((row << AS_TileRowOffset) & AS_TileRowMask) |
			((flip << AS_TileFlipOffset) & AS_TileFlipMask) |
			((align_flags << AS_TileFloatingOffset));
	set_flags (tbar->state, BAR_FLAGS_REND_PENDING);
	return new_idx;
}

Bool delete_astbar_tile (ASTBarData * tbar, int idx)
{

	if (tbar != NULL && idx < (int)(tbar->tiles_num)) {
		register int i;

		for (i = 0; i < tbar->tiles_num; ++i)
			if (i == idx || idx < 0) {
				int type = ASTileType (tbar->tiles[i]);

				if (ASTileTypeHandlers[type].free_astile_handler)
					ASTileTypeHandlers[type].free_astile_handler (&(tbar->tiles[i]));
				else if (type != AS_TileFreed) {
					memset (&(tbar->tiles[i]), 0x00, sizeof (ASTile));
					tbar->tiles[i].flags = AS_TileFreed;
				}
			}
		set_flags (tbar->state, BAR_FLAGS_REND_PENDING);
		return True;
	}
	return False;
}

int
add_astbar_spacer (ASTBarData * tbar, unsigned char col, unsigned char row,
									 int flip, int align, unsigned short width,
									 unsigned short height)
{
	if (tbar) {
		int idx = add_astbar_tile (tbar, AS_TileSpacer, col, row, flip, align);
		ASTile *tile = &(tbar->tiles[idx]);

		tile->width = width;
		tile->height = height;
		return idx;
	}
	return -1;
}

int
add_astbar_btnblock (ASTBarData * tbar, unsigned char col,
										 unsigned char row, int flip, int align,
										 struct button_t **from_list, ASFlagType context_mask,
										 unsigned int count, int left_margin, int top_margin,
										 int spacing, int order)
{
	if (tbar) {
		int idx =
				add_astbar_tile (tbar, AS_TileBtnBlock, col, row, flip, align);
		ASTile *tile = &(tbar->tiles[idx]);

/*      if( get_flags( flip, FLIP_VERTICAL ) )
		{
			int tmp = top_margin ; 
			top_margin = left_margin ; 
			left_margin = tmp ;    
	   }    
 */
		build_btn_block (tile, from_list, context_mask, count, left_margin,
										 top_margin, spacing, order);
		return idx;
	}
	return -1;
}

int
add_astbar_icon (ASTBarData * tbar, unsigned char col, unsigned char row,
								 int flip, int align, ASImage * icon)
{
	if (tbar && icon) {
		int idx = add_astbar_tile (tbar, AS_TileIcon, col, row, flip, align);
		ASTile *tile = &(tbar->tiles[idx]);

		LOCAL_DEBUG_CALLER_OUT
				("col = %d, row = %d, flip = %d, align = 0x%X, im = %p, size = %dx%d",
				 col, row, flip, align, icon, icon->width, icon->height);
		if (flip == 0) {
			if (icon->imageman == NULL
					|| (tile->data.image.im = dup_asimage (icon)) == NULL)
				tile->data.image.im = clone_asimage (icon, 0xFFFFFFFF);
		} else {
			int dst_width = icon->width, dst_height = icon->height;

			if (get_flags (flip, FLIP_VERTICAL)) {
				dst_width = icon->height;
				dst_height = icon->width;
			}
			tile->data.image.im =
					flip_asimage (ASDefaultVisual, icon, 0, 0, dst_width, dst_height,
												flip, ASA_ASImage, 100, ASIMAGE_QUALITY_DEFAULT);
		}
		tile->width = tile->data.image.im->width;
		tile->height = tile->data.image.im->height;
		ASSetTileSublayers (*tile, 1);
		return idx;
	}
	return -1;
}

int
add_astbar_image (ASTBarData * tbar, unsigned char col, unsigned char row,
									int flip, int align, ASImage * im,
									unsigned short slice_x_start, unsigned short slice_x_end,
									unsigned short slice_y_start, unsigned short slice_y_end)
{
	int idx = add_astbar_icon (tbar, col, row, flip, align, im);

	if (idx >= 0) {
		ASImageTile *imtile = &(tbar->tiles[idx].data.image);

		imtile->slice_x_start = slice_x_start;
		imtile->slice_x_end = slice_x_end;
		imtile->slice_y_start = slice_y_start;
		imtile->slice_y_end = slice_y_end;

		return idx;
	}
	return -1;
}


int
add_astbar_label (ASTBarData * tbar, unsigned char col, unsigned char row,
									int flip, int align, short h_padding, short v_padding,
									const char *text, unsigned long encoding)
{
	LOCAL_DEBUG_CALLER_OUT ("encoding = %ld, label \"%s\"", encoding,
													text ? text : "null");
	if (tbar) {

		int idx = add_astbar_tile (tbar, AS_TileLabel, col, row, flip, align);
		ASTile *tile = &(tbar->tiles[idx]);

		ASLabel *lbl = &(tile->data.label);

		lbl->text = mystrdup (text);
		lbl->encoding = encoding;
		lbl->h_padding = h_padding;
		lbl->v_padding = v_padding;
		set_astile_styles (tbar, tile, -1);

		ASSetTileSublayers (*tile, 1);
		return idx;
	}
	return -1;
}

Bool
change_astbar_label (ASTBarData * tbar, int index, const char *label,
										 unsigned long encoding)
{
	Bool changed = False;

	LOCAL_DEBUG_CALLER_OUT
			("tbar(%p)->index(%d)->label(ENC = %ld, TXT=\"%s\")", tbar, index,
			 encoding, label ? label : "null");
	if (tbar && tbar->tiles) {
		ASLabel *lbl = &(tbar->tiles[index].data.label);

		if (ASTileType (tbar->tiles[index]) != AS_TileLabel)
			return False;
		LOCAL_DEBUG_OUT ("old_label(ENC = %ld, TXT=\"%s\" )", lbl->encoding,
										 lbl->text ? lbl->text : "null");


		if (label == NULL) {
			if ((changed = (lbl->text != NULL))) {
				free (lbl->text);
				lbl->text = NULL;
			}
		} else if (lbl->text == NULL) {
			changed = True;
			lbl->text = mystrdup (label);
		} else if ((changed = (strcmp ((char *)(lbl->text), label) != 0))) {
			free (lbl->text);
			lbl->text = mystrdup (label);
		}
		if (!changed && lbl->encoding != encoding)
			changed = True;
		lbl->encoding = encoding;
		if (changed) {
			set_astile_styles (tbar, &(tbar->tiles[index]), -1);
			set_flags (tbar->state, BAR_FLAGS_REND_PENDING);
		}
	}
	return changed;
}

Bool
change_astbar_first_label (ASTBarData * tbar, const char *label,
													 unsigned long encoding)
{
	if (tbar) {
		register int i;

		for (i = 0; i < tbar->tiles_num; ++i)
			if (ASTileType (tbar->tiles[i]) == AS_TileLabel)
				return change_astbar_label (tbar, i, label, encoding);
	}
	return False;
}

Bool move_astbar (ASTBarData * tbar, ASCanvas * pc, int win_x, int win_y)
{
	Bool changed = False;

	if (tbar && pc) {
		int root_x, root_y;

		if (win_x >= MAX_POSITION || win_x <= -MAX_POSITION)
			win_x = tbar->win_x;
		if (win_y >= MAX_POSITION || win_y <= -MAX_POSITION)
			win_y = tbar->win_y;

		root_x = pc->root_x + (int)pc->bw + win_x;
		root_y = pc->root_y + (int)pc->bw + win_y;

		changed = (root_x != tbar->root_x || root_y != tbar->root_y);
		tbar->root_x = root_x;
		tbar->root_y = root_y;
		if (changed) {
			register int i = BAR_STATE_NUM;

			while (--i >= 0) {
				if (tbar->style[i] && TransparentMS (tbar->style[i]))
					flush_tbar_state_backs (tbar, i);
			}
		}
		changed = changed || (win_x != tbar->win_x || win_y != tbar->win_y);
		tbar->win_x = win_x;
		tbar->win_y = win_y;
		if (changed)
			set_flags (tbar->state, BAR_FLAGS_REND_PENDING);
		LOCAL_DEBUG_OUT
				("tbar(%p)->root_geom(%ux%u%+d%+d)->win_pos(%+d%+d)->changed(%x)",
				 tbar, tbar->width, tbar->height, root_x, root_y, win_x, win_y,
				 changed);
	}
	return changed;
}

int
make_tile_pad (Bool pad_before, Bool pad_after, int cell_size,
							 int tile_size)
{
	if (cell_size <= tile_size)
		return 0;
	if (!pad_before)
		return 0;
	if (!pad_after)
		return cell_size - tile_size;
	return (cell_size - tile_size) >> 1;
}

Bool set_astbar_focused (ASTBarData * tbar, ASCanvas * pc, Bool focused)
{
	if (tbar) {
		int old_focused =
				get_flags (tbar->state, BAR_STATE_FOCUS_MASK) ? 1 : 0;
		int new_focused = focused ? 1 : 0;

		if (focused)
			set_flags (tbar->state, BAR_STATE_FOCUSED);
		else
			clear_flags (tbar->state, BAR_STATE_FOCUSED);

		if (old_focused != new_focused)
			set_flags (tbar->state, BAR_FLAGS_REND_PENDING);
		if (get_flags (tbar->state, BAR_FLAGS_REND_PENDING) && pc != NULL)
			render_astbar (tbar, pc);
		return (new_focused != old_focused);
	}
	return False;
}

Bool set_astbar_pressed (ASTBarData * tbar, ASCanvas * pc, Bool pressed)
{
	if (tbar) {
		Bool old_pressed =
				get_flags (tbar->state, BAR_STATE_PRESSED_MASK) ? 1 : 0;

		if (pressed)
			set_flags (tbar->state, BAR_STATE_PRESSED);
		else
			clear_flags (tbar->state, BAR_STATE_PRESSED);


		if (old_pressed != pressed)
			set_flags (tbar->state, BAR_FLAGS_REND_PENDING);
		if (get_flags (tbar->state, BAR_FLAGS_REND_PENDING) && pc != NULL)
			render_astbar (tbar, pc);
		return ((pressed ? 1 : 0) != old_pressed);
	}
	return False;
}

Bool set_astbar_btn_pressed (ASTBarData * tbar, int context)
{
	if (tbar) {
		int i;
		Bool changed = False;

		for (i = 0; i < tbar->tiles_num; ++i)
			if (ASTileType (tbar->tiles[i]) == AS_TileBtnBlock)
				if (set_tbtn_pressed (&(tbar->tiles[i].data.bblock), context))
					changed = True;
		if (changed)
			set_flags (tbar->state, BAR_FLAGS_REND_PENDING);
		return changed;
	}
	return False;
}


Bool
update_astbar_transparency (ASTBarData * tbar, ASCanvas * pc, Bool force)
{
	int root_x, root_y;
	Bool changed = False;

	if (tbar == NULL || pc == NULL)
		return False;;

	root_x = pc->root_x + (int)pc->bw + tbar->win_x;
	root_y = pc->root_y + (int)pc->bw + tbar->win_y;
	if ((changed =
			 (root_x != tbar->root_x || root_y != tbar->root_y
				|| ASDefaultScr->RootImage == NULL || force))) {
		register int i = BAR_STATE_NUM;

		tbar->root_x = root_x;
		tbar->root_y = root_y;

		while (--i >= 0)
			if (tbar->style[i] && TransparentMS (tbar->style[i]))
				flush_tbar_state_backs (tbar, i);
	}
	return get_flags (tbar->state, BAR_FLAGS_REND_PENDING);
}

Bool is_astbar_shaped (ASTBarData * tbar, int state)
{
	Bool shaped = False;
	MyStyle *style;

	if (state < 0 || state == BAR_STATE_UNFOCUSED) {
		if ((style = tbar->style[BAR_STATE_UNFOCUSED]) != NULL)
			if (style->texture_type == TEXTURE_SHAPED_PIXMAP
					|| style->texture_type == TEXTURE_SHAPED_SCALED_PIXMAP)
				shaped = True;
	}
	if (state < 0 || state == BAR_STATE_FOCUSED) {
		if ((style = tbar->style[BAR_STATE_FOCUSED]) != NULL)
			if (style->texture_type == TEXTURE_SHAPED_PIXMAP
					|| style->texture_type == TEXTURE_SHAPED_SCALED_PIXMAP)
				shaped = True;
	}
	return shaped;
}


static inline int
trim_astbar_grid_dim (short *dim, int size, int space_left)
{
	int l = 0;
	int changed = 0;

	while (space_left < -1) {			/* ohh, no, we need to trim rows to fit */
		if (dim[l] > 0) {
			int to_trim = (-space_left * dim[l]) / size;

			if (dim[l] < to_trim)
				to_trim = dim[l] - 1;
			if (to_trim == 0)
				to_trim = 1;
			dim[l] -= to_trim;
			space_left += to_trim;
			++changed;
		}
		if (++l >= AS_TileRows) {
			if (changed == 0)
				break;
			l = 0;
			changed = 0;
		}
	}
	return space_left;
}


static inline Bool
render_astbar_int (ASTBarData * tbar, ASCanvas * pc, ASImage ** pcache,
									 ASCanvas * origin_canvas)
{
	START_TIME (started);
	ASImage *back = NULL;
	MyStyle *style;
	ASImageBevel bevel;
	ASImageLayer *layers;
	ASImage **scrap_images = NULL;
	ASImage *merged_im = NULL;
	int state;
	ASAltImFormats fmt = ASA_ScratchXImageAndAlpha;
	int l;
	short col_width[AS_TileColumns] = { 0 };
	short row_height[AS_TileRows] = { 0 };
	short col_x[AS_TileColumns] = { 0 };
	short row_y[AS_TileRows] = { 0 };
	short floating_cols[AS_TileColumns] = { 0 };
	short floating_rows[AS_TileRows] = { 0 };
	int floating_cols_count = 0, floating_rows_count = 0;
	short space_left_x, space_left_y;
	int x = 0, y = 0;
	int good_layers = 0;
	Bool res = False;
	Bool render_mask = False;
	merge_scanlines_func merge_func = alphablend_scanlines;
	int h_bevel_size = 0, v_bevel_size = 0;

	/* input control : */
	LOCAL_DEBUG_CALLER_OUT ("tbar(%p)->pc(%p)", tbar, pc);
	if (tbar == NULL || pc == NULL || pc->w == None)
		return -3;
	state = get_flags (tbar->state, BAR_STATE_FOCUS_MASK);
	style = tbar->style[state];
	LOCAL_DEBUG_OUT ("style(%p)->geom(%ux%u%+d%+d)->hilite(0x%X)", style,
									 tbar->width, tbar->height, tbar->root_x, tbar->root_y,
									 tbar->hilite[state]);
	if (tbar->width == 0 || tbar->height == 0)
		return True;								/* nothing to draw anyways */

	if (style == NULL)
		return -2;

	if (origin_canvas == NULL)
		origin_canvas = pc;

	mystyle_make_bevel (style, &bevel, tbar->hilite[state],
											get_flags (tbar->state, BAR_STATE_PRESSED_MASK));
	h_bevel_size = bevel.left_outline + bevel.right_outline;
	v_bevel_size = bevel.top_outline + bevel.bottom_outline;

	/* validating our images : */
	if (tbar->back[state] != NULL) {
		if (tbar->root_x != pc->root_x + (int)pc->bw + tbar->win_x ||
				tbar->root_y != pc->root_y + (int)pc->bw + tbar->win_y) {
			update_astbar_transparency (tbar, pc, False);
		}
	}
	if ((back = tbar->back[state]) != NULL) {
		if (back->width != tbar->width || back->height != tbar->height ||
				((tbar->rendered_root_x != tbar->root_x
					|| tbar->rendered_root_y != tbar->root_y)
				 && style->texture_type > TEXTURE_PIXMAP)) {
			flush_tbar_state_backs (tbar, state);
			back = NULL;
		}
	}
	LOCAL_DEBUG_OUT ("back(%p), vertical?%s", back,
									 get_flags (tbar->state,
															BAR_FLAGS_VERTICAL) ? "Yes" : "No");
	if (back == NULL) {
		if ((get_flags (tbar->state, BAR_FLAGS_CROP_UNFOCUSED_BACK)
				 && !IsASTBarFocused (tbar))
				|| (get_flags (tbar->state, BAR_FLAGS_CROP_FOCUSED_BACK)
						&& IsASTBarFocused (tbar))) {
			if (pcache) {
				if (*pcache == NULL) {
					*pcache = mystyle_make_image (style,
																				origin_canvas->root_x,
																				origin_canvas->root_y,
																				origin_canvas->width +
																				origin_canvas->bw,
																				origin_canvas->height +
																				origin_canvas->bw,
																				get_flags (tbar->state,
																									 BAR_FLAGS_VERTICAL)
																				? FLIP_VERTICAL : 0);
				}
				if (*pcache)
					back = tile_asimage (ASDefaultVisual, *pcache,
															 tbar->root_x - origin_canvas->root_x +
															 bevel.top_outline,
															 tbar->root_y - origin_canvas->root_y +
															 bevel.left_outline, tbar->width,
															 tbar->height, TINT_LEAVE_SAME, ASA_ASImage,
															 0, ASIMAGE_QUALITY_DEFAULT);
			}

			if (back == NULL)
				back = mystyle_crop_image (style,
																	 pc->root_x,
																	 pc->root_y,
																	 tbar->root_x - origin_canvas->root_x +
																	 bevel.top_outline,
																	 tbar->root_y - origin_canvas->root_y +
																	 bevel.left_outline, tbar->width,
																	 tbar->height,
																	 origin_canvas->width +
																	 (int)origin_canvas->bw,
																	 origin_canvas->height +
																	 (int)origin_canvas->bw,
																	 get_flags (tbar->state,
																							BAR_FLAGS_VERTICAL) ?
																	 FLIP_VERTICAL : 0);
		} else
			back = mystyle_make_image (style,
																 tbar->root_x + bevel.left_outline,
																 tbar->root_y + bevel.top_outline,
																 tbar->width, tbar->height,
																 get_flags (tbar->state,
																						BAR_FLAGS_VERTICAL) ?
																 FLIP_VERTICAL : 0);
		tbar->back[state] = back;
		LOCAL_DEBUG_OUT ("back-try2(%p)", back);
		if (back == NULL)
			return -1;
	}

	/*mystyle_make_bevel (style, &bevel, 0, get_flags (tbar->state, BAR_STATE_PRESSED_MASK));
	 * in unfocused and unpressed state we render pixmap and set
	 * window's background to it
	 * in focused state or in pressed state we render to
	 * the window directly, and we'll need to be handling the expose
	 * events
	 */
	/* very complicated layout code : */
	/* pass 1: first we determine width/height of each row/column, as well as count of layers : */
	for (l = 0; l < tbar->tiles_num; ++l)
		if (ASTileType (tbar->tiles[l]) != AS_TileFreed) {
			register int pos = ASTileCol (tbar->tiles[l]);

			good_layers += ASTileSublayers (tbar->tiles[l]);
			if (!ASTileIgnoreWidth (tbar->tiles[l])) {
				int w = tbar->tiles[l].width;

				if (w > tbar->width)
					w = tbar->width;
				if (col_width[pos] < w)
					col_width[pos] = w;
				/* floating column has at least one element padded/resizable in horizontal dir,
				 * and no fixed elements */
				if (!ASTileHFloating (tbar->tiles[l]))
					floating_cols[pos] = -1;
				else if (floating_cols[pos] >= 0)
					++floating_cols[pos];
			}

			pos = ASTileRow (tbar->tiles[l]);
			if (!ASTileIgnoreHeight (tbar->tiles[l])) {
				int h = tbar->tiles[l].height;

				if (h > tbar->height)
					h = tbar->height;
				if (row_height[pos] < h)
					row_height[pos] = h;
				/* floating row has at least one element padded/resizable in vertical dir,
				 * and no fixed elements */
				if (!ASTileVFloating (tbar->tiles[l]))
					floating_rows[pos] = -1;
				else if (floating_rows[pos] >= 0)
					++floating_rows[pos];
			}
		}
	/* pass 2: see how much space we have left that needs to be floating to some rows/columns : */
	LOCAL_DEBUG_OUT ("bar_size = %dx%d", tbar->width, tbar->height);

	space_left_x =
			tbar->width - (h_bevel_size + tbar->h_border * 2 +
										 bevel.left_inline + bevel.right_inline);
	space_left_y =
			tbar->height - (v_bevel_size + tbar->v_border * 2 +
											bevel.top_inline + bevel.bottom_inline);
	LOCAL_DEBUG_OUT ("from: space_left_x = %d, space_left_y = %d",
									 space_left_x, space_left_y);
	for (l = 0; l < AS_TileColumns; ++l) {
		if (col_width[l] > 0)
			space_left_x -= col_width[l] + tbar->h_spacing;
		if (row_height[l] > 0)
			space_left_y -= row_height[l] + tbar->v_spacing;
		if (floating_cols[l] > 0)
			++floating_cols_count;
		else
			floating_cols[l] = 0;
		if (floating_rows[l] > 0)
			++floating_rows_count;
		else
			floating_rows[l] = 0;
	}
	space_left_x += tbar->h_spacing;
	space_left_y += tbar->v_spacing;
	LOCAL_DEBUG_OUT
			("to  : space_left_x = %d, space_left_y = %d, floating_cols = %d, floating_rows = %d",
			 space_left_x, space_left_y, floating_cols_count,
			 floating_rows_count);
	LOCAL_DEBUG_OUT ("h_spacing = %d, v_spacing = %d", tbar->h_spacing,
									 tbar->v_spacing);
	/* pass 3: now we determine spread padding among affected cols : */
	if (floating_cols_count > 0 && space_left_x != 0)
		for (l = 0; l < AS_TileColumns; ++l) {
			if (floating_cols[l] > 0) {
				register int change = space_left_x / floating_cols_count;

				if (change == 0)
					change = (space_left_x > 0) ? 1 : -1;
				col_width[l] += change;
				if (col_width[l] < 0) {
					change -= col_width[l];
					col_width[l] = 0;
				}
				--floating_cols_count;
				space_left_x -= change;
				if (space_left_x == 0)
					break;
			}
		}
	if (space_left_x < -1)
		space_left_x =
				trim_astbar_grid_dim (&(col_width[0]), tbar->width, space_left_x);

	/* pass 4: now we determine spread padding among affected rows : */
	if (floating_rows_count > 0 && space_left_y != 0)
		for (l = 0; l < AS_TileRows; ++l) {
			if (floating_rows[l] > 0) {
				register int change = space_left_y / floating_rows_count;

				if (change == 0)
					change = (space_left_y > 0) ? 1 : -1;
				row_height[l] += change;
				if (row_height[l] < 0) {
					change -= row_height[l];
					row_height[l] = 0;
				}
				--floating_rows_count;
				space_left_y -= change;
				if (space_left_y == 0)
					break;
			}
		}
	if (space_left_y < -1)
		space_left_y =
				trim_astbar_grid_dim (&(row_height[0]), tbar->height,
															space_left_y);


	/* pass 5: now we determine offset of each row/column : */
	x = bevel.left_outline + bevel.left_inline + tbar->h_border;
	y = bevel.top_outline + bevel.top_inline + tbar->v_border;
	for (l = 0; l < AS_TileColumns; ++l) {
		col_x[l] = x;
		row_y[l] = y;
		if (col_width[l] > 0)
			x += col_width[l] + tbar->h_spacing;
		if (row_height[l] > 0)
			y += row_height[l] + tbar->v_spacing;
	}
#if defined(LOCAL_DEBUG) && !defined(NO_DEBUG_OUTPUT)
	for (l = 0; l < AS_TileColumns; ++l)
		show_progress ("\tcolumn[%d] = %d%+d floating?%d", l, col_width[l],
									 col_x[l], floating_cols[l]);
	for (l = 0; l < AS_TileRows; ++l)
		show_progress ("\trow[%d] = x%d%+d floating?%d", l, row_height[l],
									 row_y[l], floating_rows[l]);
#endif
	/* Done with layout */

	layers = create_image_layers (good_layers + 1);
	scrap_images = safecalloc (good_layers + 1, sizeof (ASImage *));
	layers[0].im = back;
	layers[0].bevel = &bevel;
	if (tbar->width > h_bevel_size)
		layers[0].clip_width = tbar->width - h_bevel_size;
	else
		layers[0].clip_width = 1;

	if (tbar->height > v_bevel_size)
		layers[0].clip_height = tbar->height - v_bevel_size;
	else
		layers[0].clip_height = 1;

	/* now we need to loop through tiles and add them to the layers list at correct locations */
	good_layers = 1;
	for (l = 0; l < tbar->tiles_num; ++l) {
		int type = ASTileType (tbar->tiles[l]);

		if (ASTileTypeHandlers[type].set_layer_handler) {
			int row = ASTileRow (tbar->tiles[l]);
			int col = ASTileCol (tbar->tiles[l]);
			int pad_x = 0, pad_y = 0;

			if (!ASTileHResizeable (tbar->tiles[l]))
				pad_x =
						make_tile_pad (get_flags
													 (tbar->tiles[l].flags, AS_TilePadLeft),
													 get_flags (tbar->tiles[l].flags,
																			AS_TilePadRight), col_width[col],
													 tbar->tiles[l].width);
			tbar->tiles[l].x = col_x[col] + pad_x;

			if (!ASTileVResizeable (tbar->tiles[l]))
				pad_y =
						make_tile_pad (get_flags (tbar->tiles[l].flags, AS_TilePadTop),
													 get_flags (tbar->tiles[l].flags,
																			AS_TilePadBottom), row_height[row],
													 tbar->tiles[l].height);
			tbar->tiles[l].y = row_y[row] + pad_y;
			good_layers +=
					ASTileTypeHandlers[type].set_layer_handler (&(tbar->tiles[l]),
																											&(layers
																												[good_layers]),
																											state,
																											&(scrap_images
																												[good_layers]),
																											col_width[col] -
																											pad_x,
																											row_height[row] -
																											pad_y);
		}
	}
	merge_func =
			mystyle_translate_texture_type (tbar->composition_method[state]);
	for (l = 1; l < good_layers; ++l)
		layers[l].merge_scanlines = merge_func;

#if defined(LOCAL_DEBUG) && !defined(NO_DEBUG_OUTPUT)
	show_progress ("MERGING TBAR %p image %dx%d using merge_func %d FROM:",
								 tbar, tbar->width, tbar->height,
								 tbar->composition_method[state]);
	print_astbar_tiles (tbar);
	show_progress ("USING %d layers:", good_layers);
	for (l = 0; l < good_layers; ++l) {
		show_progress ("\t %3.3d: %p %+d%+d %ux%u%+d%+d", l, layers[l].im,
									 layers[l].dst_x, layers[l].dst_y,
									 layers[l].clip_width, layers[l].clip_height,
									 layers[l].clip_x, layers[l].clip_y);
	}
#endif
	render_mask = (style->texture_type == TEXTURE_SHAPED_PIXMAP ||
								 style->texture_type == TEXTURE_SHAPED_SCALED_PIXMAP
								 || get_flags (pc->state, CANVAS_FORCE_MASK));
#ifdef SHAPE
	LOCAL_DEBUG_OUT ("render_mask = %d, shape = %p", render_mask, pc->shape);
	if (render_mask) {
		/*     fmt = ASA_ASImage; */
	} else if (pc->shape)
		fill_canvas_mask (pc, tbar->win_x, tbar->win_y, tbar->width,
											tbar->height);
#endif
	if (get_flags (ASDefaultVisual->glx_support, ASGLX_UseForImageTx))
		fmt = ASA_ASImage;

	LOCAL_DEBUG_OUT ("fmt = %d, hue = %d, sat = %d", fmt, tbar->hue[state],
									 tbar->sat[state]);
	if (tbar->hue[state] > 0 || tbar->sat[state] >= 0) {
		ASImage *tmp_im =
				merge_layers (ASDefaultVisual, &layers[0], good_layers,
											tbar->width, tbar->height, ASA_ASImage, 0,
											ASIMAGE_QUALITY_DEFAULT);
		if (tmp_im) {
			merged_im = adjust_asimage_hsv (ASDefaultVisual, tmp_im,
																			0, 0,
																			tmp_im->width, tmp_im->height,
																			0, 360,
																			tbar->hue[state] <
																			0 ? 0 : tbar->hue[state],
																			tbar->sat[state] <
																			0 ? 0 : tbar->sat[state], 0, fmt, 0,
																			ASIMAGE_QUALITY_DEFAULT);
			destroy_asimage (&tmp_im);
		}
	} else
		merged_im =
				merge_layers (ASDefaultVisual, &layers[0], good_layers,
											tbar->width, tbar->height, fmt, 0,
											ASIMAGE_QUALITY_DEFAULT);
	for (l = 0; l < good_layers; ++l)
		if (scrap_images[l])
			safe_asimage_destroy (scrap_images[l]);
	free (scrap_images);
	free (layers);

	if (merged_im) {
		res = draw_canvas_image (pc, merged_im, tbar->win_x, tbar->win_y);

#ifdef SHAPE
		if (render_mask)
			draw_canvas_mask (pc, merged_im, tbar->win_x, tbar->win_y);
#endif
		destroy_asimage (&merged_im);
		if (res)
			clear_flags (tbar->state, BAR_FLAGS_REND_PENDING);
	}
	SHOW_TIME ("rendering", started);
	return res;
}

#ifdef TRACE_render_astbar
#undef render_astbar
Bool
trace_render_astbar (ASTBarData * tbar, ASCanvas * pc, const char *file,
										 int line)
{
	Bool res;

	fprintf (stderr, "D>%s(%d):render_astbar(%p,%p)\n", file, line, tbar,
					 pc);
	res = render_astbar_int (tbar, pc, NULL, NULL);
	fprintf (stderr, "D>%s(%d):render_astbar(%p,%p) returned %d\n", file,
					 line, tbar, pc, res);
	if (tbar && res <= 0)
		fprintf (stderr,
						 "D>%s(%d):render_astbar tbar data: state %lX, %ux%u%+d%+d, root %+d%+d, styles %p,%p, tiles_num %d, tiles %p, canvas %X\n",
						 file, line, tbar->state, tbar->width, tbar->height,
						 tbar->win_x, tbar->win_y, tbar->root_x, tbar->root_y,
						 tbar->style[0], tbar->style[1], tbar->tiles_num, tbar->tiles,
						 pc->canvas);
	return res;
}
#else
Bool render_astbar (ASTBarData * tbar, ASCanvas * pc)
{
	return render_astbar_int (tbar, pc, NULL, NULL);
}
#endif

Bool
render_astbar_cached_back (ASTBarData * tbar, ASCanvas * pc,
													 ASImage ** cache, ASCanvas * origin_canvas)
{
	return render_astbar_int (tbar, pc, cache, origin_canvas);
}


int check_astbar_point (ASTBarData * tbar, int root_x, int root_y)
{
	int context = C_NO_CONTEXT;

	if (tbar) {
		LOCAL_DEBUG_OUT
				("bar's geometry = %dx%d%+d%+d, pointer posish = %+d%+d",
				 tbar->width, tbar->height, tbar->root_x, tbar->root_y, root_x,
				 root_y);
		root_x -= tbar->root_x;
		root_y -= tbar->root_y;
		if (0 <= root_x && tbar->width > root_x && 0 <= root_y
				&& tbar->height > root_y) {
			int tmp_context;
			int i = tbar->tiles_num;

			context = tbar->context;
			while (--i >= 0) {
				int type = ASTileType (tbar->tiles[i]);

				if (ASTileTypeHandlers[type].check_point_handler) {
					int tile_x = root_x - tbar->tiles[i].x;
					int tile_y = root_y - tbar->tiles[i].y;

					LOCAL_DEBUG_OUT ("checking tile %d, %dx%d%+d%+d", i,
													 tbar->tiles[i].width, tbar->tiles[i].height,
													 tbar->tiles[i].x, tbar->tiles[i].y);
					if (tile_x >= 0 && tile_y >= 0 && tile_x < tbar->tiles[i].width
							&& tile_y < tbar->tiles[i].height)
						if ((tmp_context =
								 ASTileTypeHandlers[type].check_point_handler (&
																															 (tbar->
																																tiles[i]),
																															 tile_x,
																															 tile_y)) !=
								C_NO_CONTEXT) {
							context = tmp_context;
							break;
						}
				}
			}
		}
	}
	return context;
}

void
on_astbar_pointer_action (ASTBarData * tbar, int context, Bool leave,
													Bool pointer_moved)
{
	static ASBalloon *last_balloon = NULL;

	LOCAL_DEBUG_CALLER_OUT ("%p, %s, %d", tbar, context2text (context),
													leave);

	if (tbar == NULL) {
		tbar = FocusedBar;
		leave = True;
	}
	if (pointer_moved)
		last_balloon = NULL;
	LOCAL_DEBUG_OUT ("%p, %s, %d", tbar, context2text (context), leave);
	if (tbar) {
		ASBalloon *balloon = tbar->balloon;

		if (context != 0 && context != C_TITLE && context != tbar->context) {
			int i = tbar->tiles_num;

			LOCAL_DEBUG_OUT
					("looking for a tile with context %X in the set of %d tiles",
					 context, i);
			while (--i >= 0) {
				if (ASTileType (tbar->tiles[i]) == AS_TileBtnBlock) {
					ASBtnBlock *bb = (ASBtnBlock *) & (tbar->tiles[i].data.bblock);
					int k = bb->buttons_num;

					LOCAL_DEBUG_OUT
							("tile %d is a button block - lets see if any of %d buttons have context",
							 i, k);
					while (--k >= 0)
						if (bb->buttons[k].context == context) {
							balloon = bb->buttons[k].balloon;
							LOCAL_DEBUG_OUT
									("button %d has required contex. balloon = %p", k,
									 balloon);
							break;
						}
					if (k >= 0)
						break;
				}
			}
		}
		if (leave || balloon == NULL) {
			withdraw_balloon (balloon);
			if (tbar == FocusedBar)
				FocusedBar = NULL;
		} else if (balloon != last_balloon) {
			display_balloon (balloon);
			FocusedBar = tbar;
		}
		last_balloon = balloon;
	}
}

void
set_astbar_balloon2 (ASTBarData * tbar, ASBalloonState * balloon_state,
										 int context, const char *text, unsigned long encoding)
{
	if (tbar != NULL) {
		if (context != 0 && context != C_TITLE && context != tbar->context) {
			int i = tbar->tiles_num;

			LOCAL_DEBUG_OUT
					("looking for a tile with context %X in the set of %d tiles",
					 context, i);
			while (--i >= 0) {
				if (ASTileType (tbar->tiles[i]) == AS_TileBtnBlock) {
					ASBtnBlock *bb = (ASBtnBlock *) & (tbar->tiles[i].data.bblock);
					int k = bb->buttons_num;

					LOCAL_DEBUG_OUT
							("tile %d is a button block - lets see if any of %d buttons have context",
							 i, k);
					while (--k >= 0)
						if (bb->buttons[k].context == context) {
							if (bb->buttons[k].balloon != NULL) {
								balloon_set_text (bb->buttons[k].balloon, text, encoding);
								LOCAL_DEBUG_OUT
										("changed balloon for tbar(%p)->context(0x%X)->button(%d)->encoding(%ld)->text(%s)->balloon(%p)",
										 tbar, context, k, encoding, text,
										 bb->buttons[k].balloon);
							} else {
								bb->buttons[k].balloon =
										create_asballoon_with_text_for_state (balloon_state,
																													tbar, text,
																													encoding);
								LOCAL_DEBUG_OUT
										("created balloon for tbar(%p)->ct(0x%X)->btn(%d)->enc(%ld)->text(%s)->balloon(%p)",
										 tbar, context, k, encoding, text,
										 bb->buttons[k].balloon);
							}
							return;
						}
				}
			}
		} else {
			if (tbar->balloon != NULL) {
				balloon_set_text (tbar->balloon, text, encoding);
				LOCAL_DEBUG_OUT
						("changed tbar balloon for tbar(%p)->context(0x%X)->text(%s)->balloon(%p)",
						 tbar, context, text, tbar->balloon);
			} else {
				tbar->balloon =
						create_asballoon_with_text_for_state (balloon_state, tbar,
																									text, encoding);
				LOCAL_DEBUG_OUT
						("created tbar balloon for tbar(%p)->context(0x%X)->text(%s)->balloon(%p)",
						 tbar, context, text, tbar->balloon);
			}

		}
	}
}

void
set_astbar_balloon (ASTBarData * tbar, int context, const char *text,
										unsigned long encoding)
{
	if (tbar != NULL)
		set_astbar_balloon2 (tbar, NULL, context, text, encoding);
}
