#include "apricot.h"
#include "Application.h"
#include "Icon.h"
#include "Popup.h"
#include "Widget.h"
#include "Window.h"
#include <Widget.inc>

#ifdef __cplusplus
extern "C" {
#endif


#undef  my
#define inherited CDrawable
#define enter_method PWidget_vmt selfvmt = ((( PWidget) self)-> self)
#define my  selfvmt
#define var (( PWidget) self)

typedef Bool ActionProc ( Handle self, Handle item, void * params);
typedef ActionProc *PActionProc;
#define his (( PWidget) child)

/* local defines */
typedef struct _SingleColor
{
	Color color;
	int   index;
} SingleColor, *PSingleColor;

static Bool find_dup_msg( PEvent event, int * cmd);
static Bool pquery ( Handle window, Handle self, void * v);
static Bool get_top_current( Handle self);
static Bool sptr( Handle window, Handle self, void * v);
static Handle find_tabfoc( Handle self);
static Bool showhint_notify ( Handle self, Handle child, void * data);
static Bool hint_notify ( Handle self, Handle child, SV * hint);

extern void Widget_pack_slaves( Handle self); 
extern void Widget_place_slaves( Handle self); 
extern Bool Widget_size_notify( Handle self, Handle child, const Rect* metrix);
extern Bool Widget_move_notify( Handle self, Handle child, Point * moveTo);

/* init, done & update_sys_handle */
void
Widget_init( Handle self, HV * profile)
{
	dPROFILE;
	enter_method;
	SV * sv;
	int geometry;

	inherited-> init( self, profile);

	list_create( &var-> widgets, 0, 8);
	var-> tabOrder = -1;

	var-> geomInfo. side = 3; /* default pack side is 'top', anchor ='center' */
	var-> geomInfo. anchorx = var-> geomInfo. anchory = 1;

	my-> update_sys_handle( self, profile);
	/* props init */
	/* font and colors */
	SvHV_Font( pget_sv( font), &Font_buffer, "Widget::init");
	my-> set_widgetClass        ( self, pget_i( widgetClass  ));
	my-> set_color              ( self, pget_i( color        ));
	my-> set_backColor          ( self, pget_i( backColor    ));
	my-> set_font               ( self, Font_buffer);
	opt_assign( optOwnerBackColor, pget_B( ownerBackColor));
	opt_assign( optOwnerColor    , pget_B( ownerColor));
	opt_assign( optOwnerFont     , pget_B( ownerFont));
	opt_assign( optOwnerHint     , pget_B( ownerHint));
	opt_assign( optOwnerShowHint , pget_B( ownerShowHint));
	opt_assign( optOwnerPalette  , pget_B( ownerPalette));
	my-> colorIndex( self, true,  ciHiliteText,   pget_i( hiliteColor)      );
	my-> colorIndex( self, true,  ciHilite,       pget_i( hiliteBackColor)  );
	my-> colorIndex( self, true,  ciDisabledText, pget_i( disabledColor)    );
	my-> colorIndex( self, true,  ciDisabled,     pget_i( disabledBackColor));
	my-> colorIndex( self, true,  ciLight3DColor, pget_i( light3DColor)     );
	my-> colorIndex( self, true,  ciDark3DColor,  pget_i( dark3DColor)      );
	my-> set_palette( self, pget_sv( palette));

	/* light props */
	my-> set_autoEnableChildren ( self, pget_B( autoEnableChildren));
	my-> set_briefKeys          ( self, pget_B( briefKeys));
	my-> set_buffered           ( self, pget_B( buffered));
	my-> set_cursorVisible      ( self, pget_B( cursorVisible));
	my-> set_growMode           ( self, pget_i( growMode));
	my-> set_helpContext        ( self, pget_sv( helpContext));
	my-> set_hint               ( self, pget_sv( hint));
	my-> set_firstClick         ( self, pget_B( firstClick));
	{
		Point hotSpot;
		Handle icon = pget_H( pointerIcon);
		prima_read_point( pget_sv( pointerHotSpot), (int*)&hotSpot, 2, "Array panic on 'pointerHotSpot'");
		if ( icon != nilHandle && !kind_of( icon, CIcon)) {
			warn("Illegal object reference passed to Widget::pointerIcon");
			icon = nilHandle;
		}
		apc_pointer_set_user( self, icon, hotSpot);
	}
	my-> set_pointerType        ( self, pget_i( pointerType));
	my-> set_selectingButtons   ( self, pget_i( selectingButtons));
	my-> set_selectable         ( self, pget_B( selectable));
	my-> set_showHint           ( self, pget_B( showHint));
	my-> set_tabOrder           ( self, pget_i( tabOrder));
	my-> set_tabStop            ( self, pget_B( tabStop));
	my-> set_text               ( self, pget_sv( text));

	opt_assign( optScaleChildren, pget_B( scaleChildren));

	/* subcomponents props */
	my-> popupColorIndex( self, true, ciFore,         pget_i( popupColor)              );
	my-> popupColorIndex( self, true, ciBack,         pget_i( popupBackColor)          );
	my-> popupColorIndex( self, true, ciHiliteText,   pget_i( popupHiliteColor)        );
	my-> popupColorIndex( self, true, ciHilite,       pget_i( popupHiliteBackColor)    );
	my-> popupColorIndex( self, true, ciDisabledText, pget_i( popupDisabledColor)      );
	my-> popupColorIndex( self, true, ciDisabled,     pget_i( popupDisabledBackColor)  );
	my-> popupColorIndex( self, true, ciLight3DColor, pget_i( popupLight3DColor)       );
	my-> popupColorIndex( self, true, ciDark3DColor,  pget_i( popupDark3DColor)        );
	SvHV_Font( pget_sv( popupFont), &Font_buffer, "Widget::init");
	my-> set_popup_font  ( self, Font_buffer);
	if ( SvTYPE( sv = pget_sv( popupItems)) != SVt_NULL)
		my-> set_popupItems( self, sv);
	if ( SvTYPE( sv = pget_sv( accelItems)) != SVt_NULL)
		my-> set_accelItems( self, sv);

	/* size, position, enabling, visibliity etc. runtime */
	{
		Point set, set2;
		AV * av;
		SV ** holder;
		NPoint ds = {1,1};

		prima_read_point( pget_sv( sizeMin), (int*)&set, 2, "Array panic on 'sizeMin'");
		prima_read_point( pget_sv( sizeMax), (int*)&set2, 2, "Array panic on 'sizeMax'");
		var-> sizeMax = set2;
		my-> set_sizeMin( self, set);
		my-> set_sizeMax( self, set2);
		prima_read_point( pget_sv( cursorSize), (int*)&set, 2, "Array panic on 'cursorSize'");
		my-> set_cursorSize( self, set);
		prima_read_point( pget_sv( cursorPos), (int*)&set, 2, "Array panic on 'cursorPos'");
		my-> set_cursorPos( self, set);

		av = ( AV *) SvRV( pget_sv( designScale));
		holder = av_fetch( av, 0, 0);
		ds. x = holder ? SvNV( *holder) : 1;
		if ( !holder) warn("Array panic on 'designScale'");
		holder = av_fetch( av, 1, 0);
		ds. y = holder ? SvNV( *holder) : 1;
		if ( !holder) warn("Array panic on 'designScale'");
		my-> set_designScale( self, ds);
	}
	my-> set_enabled     ( self, pget_B( enabled));
	if ( !pexist( originDontCare) || !pget_B( originDontCare)) {
		Point pos;
		pos. x = pget_i( left);
		pos. y = pget_i( bottom);
		my-> set_origin( self, pos);
	} else
		var-> pos = my-> get_origin( self);

	if ( !pexist( sizeDontCare  ) || !pget_B( sizeDontCare  )) {
		Point size;
		size. x = pget_i( width);
		size. y = pget_i( height);
		my-> set_size( self, size);
	} else
		var-> virtualSize = my-> get_size( self);
	var-> geomSize = var-> virtualSize;

	geometry = pget_i(geometry);
	if ( geometry == gtGrowMode ) {
		Bool x = 0, y = 0;
		if ( pget_B( centered)) { x = 1; y = 1; };
		if ( pget_B( x_centered) || ( var-> growMode & gmXCenter)) x = 1;
		if ( pget_B( y_centered) || ( var-> growMode & gmYCenter)) y = 1;
		if ( x || y) my-> set_centered( self, x, y);
	}

	opt_assign( optPackPropagate, pget_B( packPropagate));
	my-> set_packInfo( self, pget_sv( packInfo));
	my-> set_placeInfo( self, pget_sv( placeInfo));
	my-> set_geometry( self, geometry);
	
	my-> set_shape       ( self, pget_H(  shape));
	my-> set_visible     ( self, pget_B( visible));
	if ( pget_B( capture)) my-> set_capture( self, 1, nilHandle);
	if ( pget_B( current)) my-> set_current( self, 1);
	CORE_INIT_TRANSIENT(Widget);

	{
		SV * widgets = pget_sv( widgets);
		if ( SvTYPE( widgets) != SVt_NULL) {
			dSP;
			ENTER;
			SAVETMPS;
			PUSHMARK( sp);
			XPUSHs( var-> mate);
			XPUSHs( sv_2mortal( newSVsv( widgets)));
			PUTBACK;
			perl_call_method( "widgets", G_DISCARD);
			SPAGAIN;
			PUTBACK;
			FREETMPS;
			LEAVE;
		}
	}
}

void
Widget_update_sys_handle( Handle self, HV * profile)
{
	dPROFILE;
	enter_method;
	Handle    owner;
	Bool      clipOwner, layered, syncPaint, transparent;
	ApiHandle parentHandle;
	if (!(
		pexist( owner) ||
		pexist( syncPaint) ||
		pexist( clipOwner) ||
		pexist( layered) ||
		pexist( transparent)
	)) return;
	
	owner        = pexist( owner)        ? pget_H( owner)        : var-> owner;
	clipOwner    = pexist( clipOwner)    ? pget_B( clipOwner)    : my-> get_clipOwner( self);
	parentHandle = pexist( parentHandle) ? pget_i( parentHandle) : apc_widget_get_parent_handle( self);
	layered      = pexist( layered)      ? pget_B( layered)      : my-> get_layered(self);
	syncPaint    = pexist( syncPaint)    ? pget_B( syncPaint)    : my-> get_syncPaint( self);
	transparent  = pexist( transparent)  ? pget_B( transparent)  : my-> get_transparent( self);

	if ( parentHandle) {
		if (( owner != application) && clipOwner) 
			croak("Cannot accept 'parentHandle' for non-application child and clip-owner widget");
	}
	
	if ( !apc_widget_create( self, owner, syncPaint, clipOwner, transparent, parentHandle, layered))
		croak( "Cannot create widget");

	pdelete( transparent);
	pdelete( syncPaint);
	pdelete( clipOwner);
	pdelete( parentHandle);
	pdelete( layered);
}

void
Widget_done( Handle self)
{
	if ( var-> text ) SvREFCNT_dec( var-> text );
	var-> text = nil;
	apc_widget_destroy( self);
	free( var-> helpContext);
	if ( var-> hint) SvREFCNT_dec( var-> hint );
	var-> helpContext = nil;
	var-> hint = nil;

	if ( var-> owner) {
		Handle * enum_lists = PWidget( var-> owner)-> enum_lists;
		while ( enum_lists) {
			unsigned int i, count;
			count = (unsigned int) enum_lists[1];
			for ( i = 2; i < count + 2; i++) {
				if ( self == enum_lists[i]) 
					enum_lists[i] = nilHandle;
			}
			enum_lists = ( Handle*) enum_lists[0];
		}
	}

	list_destroy( &var-> widgets);
	inherited-> done( self);
}

/* ::a */
void
Widget_attach( Handle self, Handle objectHandle)
{
	if ( objectHandle == nilHandle) return;
	if ( var-> stage > csNormal) return;
	if ( kind_of( objectHandle, CWidget)) {
		if ( list_index_of( &var-> widgets, objectHandle) >= 0) {
			warn( "Object attach failed");
			return;
		}
		list_add( &var-> widgets, objectHandle);
	}
	inherited-> attach( self, objectHandle);
}

/*::b */
Bool
Widget_begin_paint( Handle self)
{
	Bool ok;
	if ( !inherited-> begin_paint( self))
		return false;
	if ( !( ok = apc_widget_begin_paint( self, false))) {
		inherited-> end_paint( self);
		perl_error();
	}
	return ok;
}

Bool
Widget_begin_paint_info( Handle self)
{
	Bool ok;
	if ( is_opt( optInDraw))     return true;
	if ( !inherited-> begin_paint_info( self))
		return false;
	if ( !( ok = apc_widget_begin_paint_info( self))) {
		inherited-> end_paint_info( self);
		perl_error();
	}
	return ok;
}


void
Widget_bring_to_front( Handle self)
{
	if ( opt_InPaint) return;
	apc_widget_set_z_order( self, nilHandle, true);
}

/*::c */
Bool
Widget_can_close( Handle self)
{
	enter_method;
	Event ev = { cmClose};
	return ( var-> stage <= csNormal) ? my-> message( self, &ev) : true;
}

Bool
Widget_can_propagate_key( Handle self)
{
	return true;
}

void
Widget_cleanup( Handle self)
{
	Handle ptr;
	enter_method;

	/* disconnect all geometry slaves */
	ptr = var-> packSlaves;
	while ( ptr) {
		PWidget( ptr)-> geometry = gtDefault;
		ptr = PWidget( ptr)-> geomInfo. next;
	}
	var-> packSlaves = nilHandle;
	ptr = var-> placeSlaves;
	while ( ptr) {
		PWidget( ptr)-> geometry = gtDefault;
		ptr = PWidget( ptr)-> geomInfo. next;
	}
	var-> placeSlaves = nilHandle;
	
	my-> set_geometry( self, gtDefault);
	
	if ( application && (( PApplication) application)-> hintUnder == self)
		my-> set_hintVisible( self, 0);

	{
		int i;
		for ( i = 0; i < var-> widgets. count; i++)
			Object_destroy( var-> widgets. items[i]);
	}

	my-> detach( self, var-> accelTable, true);
	var-> accelTable = nilHandle;

	my-> detach( self, var-> popupMenu, true);
	var-> popupMenu = nilHandle;

	inherited-> cleanup( self);
}


Bool
Widget_close( Handle self)
{
	Bool canClose;
	enter_method;
	if ( var-> stage > csNormal) return true;
	if (( canClose = my-> can_close( self))) {
		Object_destroy( self);
	}
	return canClose;
}

Bool
Widget_custom_paint( Handle self)
{
	PList  list;
	void * ret;
	enter_method;
	if ( my-> on_paint != Widget_on_paint) return true;
	if ( var-> eventIDs == nil) return false;
	ret = hash_fetch( var-> eventIDs, "Paint", 5);
	if ( ret == nil) return false;
	list = var-> events + PTR2UV( ret) - 1;
	return list-> count > 0;
}

/*::d */
void
Widget_detach( Handle self, Handle objectHandle, Bool kill)
{
	enter_method;
	if ( kind_of( objectHandle, CWidget)) {
		list_delete( &var-> widgets, objectHandle);
		if ( var-> currentWidget == objectHandle && objectHandle != nilHandle)
			my-> set_currentWidget( self, nilHandle);
	}
	inherited-> detach( self, objectHandle, kill);
}

/*::e */
void
Widget_end_paint( Handle self)
{
	if ( !is_opt( optInDraw)) return;
	apc_widget_end_paint( self);
	inherited-> end_paint( self);
}

void
Widget_end_paint_info( Handle self)
{
	if ( !is_opt( optInDrawInfo)) return;
	apc_widget_end_paint_info( self);
	inherited-> end_paint_info( self);
}


/*::f */

SV*
Widget_fetch_resource( char *className, char *name, char *classRes, char *res, Handle owner, int resType)
{
	char *str = nil;
	Color clr;
	void *parm;
	Font font;
	SV * ret = nilSV;
	char *r_className, *r_name, *r_classRes, *r_res;

	switch ( resType) {
	case frColor:
		parm = &clr; break;
	case frFont:
		parm = &font;
		bzero( &font, sizeof( font));
		break;
	default:
		parm = &str;
		resType = frString;
	}

	r_className = duplicate_string(className);
	r_name      = duplicate_string(name);
	r_classRes  = duplicate_string(classRes);
	r_res       = duplicate_string(res);

	if ( !apc_fetch_resource(
		prima_normalize_resource_string( r_className, true),
		prima_normalize_resource_string( r_name, false),
		prima_normalize_resource_string( r_classRes, true),
		prima_normalize_resource_string( r_res, false),
		owner, resType, parm))
		goto FAIL;

	switch ( resType) {
	case frColor:
		ret = newSViv( clr);
		break;
	case frFont:
		ret = sv_Font2HV( &font);
		break;
	default:
		ret = str ? newSVpv( str, 0) : nilSV;
		free( str);
	}

FAIL:
	free(r_className);
	free(r_name);
	free(r_classRes);
	free(r_res);

	return ret;
}

Handle
Widget_first( Handle self)
{
	return apc_widget_get_z_order( self, zoFirst);
}

Handle
Widget_first_that( Handle self, void * actionProc, void * params)
{
	Handle child  = nilHandle;
	int i, count  = var-> widgets. count;
	Handle * list;
	if ( actionProc == nil || count == 0) return nilHandle;
	if (!(list = allocn( Handle, count + 2))) return nilHandle;

	list[0] = (Handle)( var-> enum_lists);
	list[1] = (Handle)( count);
	var-> enum_lists = list;
	memcpy( list + 2, var-> widgets. items, sizeof( Handle) * count);

	for ( i = 2; i < count + 2; i++)
	{
		if ( list[i] && (( PActionProc) actionProc)( self, list[ i], params))
		{
			child = list[ i];
			break;
		}
	}
	var-> enum_lists = (Handle*)(*list);
	free( list);
	return child;
}

/*::g */

/*::h */

void Widget_handle_event( Handle self, PEvent event)
{
	enter_method;
#define evOK ( var-> evStack[ var-> evPtr - 1])
#define objCheck if ( var-> stage > csNormal) return
	inherited-> handle_event ( self, event);
	objCheck;
	switch ( event-> cmd)
	{
		case cmCalcBounds:
		{
			Point min, max;
			min = var-> sizeMin;
			max = var-> sizeMax;
			if (( min. x > 0) && ( min. x > event-> gen. R. right  )) event-> gen. R. right  = min. x;
			if (( min. y > 0) && ( min. y > event-> gen. R. top    )) event-> gen. R. top    = min. y;
			if (( max. x > 0) && ( max. x < event-> gen. R. right  )) event-> gen. R. right  = max. x;
			if (( max. y > 0) && ( max. y < event-> gen. R. top    )) event-> gen. R. top    = max. y;
		}
		break;
		case cmSetup:
			if ( !is_opt( optSetupComplete)) {
				opt_set( optSetupComplete);
				my-> notify( self, "<s", "Setup");
			}
			break;
		case cmRepaint:
			my-> repaint( self);
			break;
		case cmPaint        :
			if ( !opt_InPaint && !my-> get_locked( self))
				if ( inherited-> begin_paint( self)) {
					if ( apc_widget_begin_paint( self, true)) {
						Bool flag = exception_block(true);
						my-> notify( self, "<sH", "Paint", self);
						exception_block(flag);
						if ( var-> stage == csNormal ) apc_widget_end_paint( self);
						EXCEPTION_CHECK_RAISE;
						objCheck;
						inherited-> end_paint( self);
					} else
						inherited-> end_paint( self);
				}
			break;
		case cmEnable       :
			my-> notify( self, "<s", "Enable");
			break;
		case cmDisable      :
			my-> notify( self, "<s", "Disable");
			break;
		case cmReceiveFocus :
			my-> notify( self, "<s", "Enter");
			break;
		case cmReleaseFocus :
			my-> notify( self, "<s", "Leave");
			break;
		case cmShow         :
			my-> notify( self, "<s", "Show");
			break;
		case cmHide         :
			my-> notify( self, "<s", "Hide");
			break;
		case cmHint:
			my-> notify( self, "<si", "Hint", event-> gen. B);
			break;
		case cmClose        :
			if ( my-> first_that( self, (void*)pquery, nil)) {
				my-> clear_event( self);
				return;
			}
			objCheck;
			my-> notify( self, "<s", "Close");
			break;
		case cmZOrderChanged:
			my-> notify( self, "<s", "ZOrderChanged");
			break;
		case cmClick:
			my-> notify( self, "<s", "Click");
			break;
		case cmColorChanged:
			if ( !kind_of( event-> gen. source, CPopup))
				my-> notify( self, "<si", "ColorChanged", event-> gen. i);
			else
				var-> popupColor[ event-> gen. i] =
					apc_menu_get_color( event-> gen. source, event-> gen. i);
			break;
		case cmFontChanged:
			if ( !kind_of( event-> gen. source, CPopup))
				my-> notify( self, "<s", "FontChanged");
			else
				apc_menu_get_font( event-> gen. source, &var-> popupFont);
			break;
		case cmMenu:
			if ( event-> gen. H) {
				char buffer[16], *context;
				context = ((( PAbstractMenu) event-> gen. H)-> self)-> make_id_context( event-> gen. H, event-> gen. i, buffer);
				my-> notify( self, "<sHs", "Menu", event-> gen. H, context);
			}
			break;
		case cmMouseClick:
			my-> notify( self, "<siiPi", "MouseClick",
				event-> pos. button, event-> pos. mod, event-> pos. where, event-> pos. dblclk);
			break;
		case cmMouseDown:
			if ((( PApplication) application)-> hintUnder == self)
				my-> set_hintVisible( self, 0);
			objCheck;
			if (((event-> pos. button & var-> selectingButtons) != 0) && my-> get_selectable( self))
				my-> set_selected( self, true);
			objCheck;
			my-> notify( self, "<siiP", "MouseDown",
				event-> pos. button, event-> pos. mod, event-> pos. where);
			break;
		case cmMouseUp:
			my-> notify( self, "<siiP", "MouseUp",
				event-> pos. button, event-> pos. mod, event-> pos. where);
			break;
		case cmMouseMove:
			if ((( PApplication) application)-> hintUnder == self)
				my-> set_hintVisible( self, -1);
			objCheck;
			my-> notify( self, "<siP", "MouseMove", event-> pos. mod, event -> pos. where);
			break;
		case cmMouseWheel:
			my-> notify( self, "<siPi", "MouseWheel",
				event-> pos. mod, event -> pos. where,
				event-> pos. button); /* +n*delta == up, -n*delta == down */
			break;
		case cmMouseEnter:
			my-> notify( self, "<siP", "MouseEnter", event-> pos. mod, event -> pos. where);
			objCheck;
			if ( application && is_opt( optShowHint) && ((( PApplication) application)-> options. optShowHint) && var-> hint && SvCUR(var-> hint))
			{
				PApplication app = ( PApplication) application;
				app-> self-> set_hint_action( application, self, true, true);
			}
			break;
		case cmMouseLeave:
			if ( application && is_opt( optShowHint)) {
				PApplication app = ( PApplication) application;
				app-> self-> set_hint_action( application, self, false, true);
			}
			my-> notify( self, "<s", "MouseLeave");
			break;
		case cmKeyDown:
		{
			int i;
			int rep = event-> key. repeat;
			Handle next = nilHandle;
			if ( is_opt( optBriefKeys))
				rep = 1;
			else
				event-> key. repeat = 1;
			for ( i = 0; i < rep; i++) {
				my-> notify( self, "<siiii", "KeyDown",
					event-> key.code, event-> key. key, event-> key. mod, event-> key. repeat);
				objCheck;
				if ( evOK) {
					Event ev = *event;
					ev. key. source = self;
					ev. cmd         = var-> owner ? cmDelegateKey : cmTranslateAccel;
					ev. key. subcmd = 0;
					if ( !my-> message( self, &ev)) {
						my-> clear_event( self);
						return;
					}
					objCheck;
				}
				if ( !evOK) break;

				switch( event-> key. key) {
				case kbF1:
				case kbHelp:
					my-> help( self);
					my-> clear_event( self);
					return;
				case kbLeft: 
					next = my-> next_positional( self, -1, 0);
					break;
				case kbRight: 
					next = my-> next_positional( self, 1, 0);
					break;
				case kbUp: 
					next = my-> next_positional( self, 0, 1);
					break;
				case kbDown: 
					next = my-> next_positional( self, 0, -1);
					break;
				case kbTab:
					next = my-> next_tab( self, true);
					break;
				case kbBackTab:
					next = my-> next_tab( self, false);
					break;
				default:;   
				}   
				if ( next) {
					CWidget( next)-> set_selected( next, true);
					objCheck;
					my-> clear_event( self);
					return;
				}
			}
		}
		break;
		case cmDelegateKey:
		switch ( event-> key. subcmd)
		{
			case 0: {
				Event ev = *event;
				ev. cmd         = cmTranslateAccel;
				if ( !my-> message( self, &ev)) {
					my-> clear_event( self);
					return;
				}
				objCheck;

				if ( my-> first_that( self, (void*)accel_notify, &ev)) {
					my-> clear_event( self);
					return;
				}
				objCheck;
				ev. cmd         = cmDelegateKey;
				ev. key. subcmd = 1;
				if ( my-> first_that( self, (void*)accel_notify, &ev)) {
					my-> clear_event( self);
					return;
				}
				if ( var-> owner && my->can_propagate_key(self))
				{
					if ( var-> owner == application)
						ev. cmd = cmTranslateAccel;
					else
						ev. key. subcmd = 0;
					ev. key. source = self;
					if (!(((( PWidget) var-> owner)-> self)-> message( var-> owner, &ev))) {
						objCheck;
						my-> clear_event( self);
						return;
					}
				}
			}
			break;
			case 1: {
				Event ev = *event;
				ev. cmd  = cmTranslateAccel;
				if ( my-> first_that( self, (void*)accel_notify, &ev)) {
					my-> clear_event( self);
					return;
				}
				objCheck;
				ev = *event;
				if ( my-> first_that( self, (void*)accel_notify, &ev)) {
					my-> clear_event( self);
					return;
				}
			}
			break;
		}
		break;
		case cmTranslateAccel:
		{
			int key = CAbstractMenu-> translate_key( nilHandle, event-> key. code, event-> key. key, event-> key. mod);
			if ( my-> first_that_component( self, (void*)find_accel, &key)) {
				my-> clear_event( self);
				return;
			}
			objCheck;
		}
		my-> notify( self, "<siii", "TranslateAccel",
			event-> key.code, event-> key. key, event-> key. mod);
		break;
		case cmKeyUp:
		my-> notify( self, "<siii", "KeyUp",
			event-> key.code, event-> key. key, event-> key. mod);
		break;
		case cmMenuCmd:
		if ( event-> gen. source)
			((( PAbstractMenu) event-> gen. source)-> self)-> sub_call_id( event-> gen. source, event-> gen. i);
		break;
		case cmMove:
			{
				Bool doNotify = false;
				Point oldP;
				if ( var-> stage == csNormal && var-> evQueue == nil) {
					doNotify = true;
				} else if ( var-> stage > csNormal) {
					break;
				} else if ( var-> evQueue != nil) {
				int i = list_first_that( var-> evQueue, (void*)find_dup_msg, &event-> cmd);
				PEvent n;
				if ( i < 0) {
					if ( !( n = alloc1( Event))) goto MOVE_EVENT;
					memcpy( n, event, sizeof( Event));
					n-> gen. B = 1;
					n-> gen. R. left = n-> gen. R. bottom = 0;
					list_add( var-> evQueue, ( Handle) n);
				} else
					n = ( PEvent) list_at( var-> evQueue, i);
				n-> gen. P = event-> gen. P;
				}
			MOVE_EVENT:;
				if ( !event-> gen. B)
					my-> first_that( self, (void*) Widget_move_notify, &event-> gen. P);
				if ( doNotify) oldP = var-> pos;
				var-> pos = event-> gen. P;
				if ( doNotify && 
					(oldP. x != event-> gen. P. x || 
						oldP. y != event-> gen. P. y)) {
					my-> notify( self, "<sPP", "Move", oldP, event-> gen. P);
					objCheck;
					if ( var->geometry == gtGrowMode && var-> growMode & gmCenter) 
						my-> set_centered( self, var-> growMode & gmXCenter, var-> growMode & gmYCenter);
				}
			}
			break;
		case cmPopup:
		{
			Handle org = self;
			my-> notify( self, "<siP", "Popup", event-> gen. B, event-> gen. P. x, event-> gen. P. y);
			objCheck;
			if ( evOK) {
				while ( self) {
					PPopup p = ( PPopup) CWidget( self)-> get_popup( self);
					if ( p && p-> self-> get_autoPopup(( Handle) p)) {
						Point px = event-> gen. P;
						apc_widget_map_points( org,  true,  1, &px);
						apc_widget_map_points( self, false, 1, &px);
						p-> self-> popup(( Handle) p, px. x, px. y ,0,0,0,0);
						CWidget( org)-> clear_event( org);
						return;
					}
					self = var-> owner;
				}
			}
			break;
		}
		case cmSize:
		/* expecting new size in P, old & new size in R. */
		{
			Bool doNotify = false;
			if ( var-> stage == csNormal && var-> evQueue == nil) {
				doNotify = true;
			} else if ( var-> stage > csNormal) {
				break;
			} else if ( var-> evQueue != nil) {
				int i = list_first_that( var-> evQueue, (void*)find_dup_msg, &event-> cmd);
				PEvent n;
				if ( i < 0) {
					if ( !( n = alloc1( Event))) goto SIZE_EVENT;
					memcpy( n, event, sizeof( Event));
					n-> gen. B = 1;
					n-> gen. R. left = n-> gen. R. bottom = 0;
					list_add( var-> evQueue, ( Handle) n);
				} else
					n = ( PEvent) list_at( var-> evQueue, i);
				n-> gen. P. x = n-> gen. R. right  = event-> gen. P. x;
				n-> gen. P. y = n-> gen. R. top    = event-> gen. P. y;
			}
		SIZE_EVENT:;  
			if ( var->geometry == gtGrowMode && var-> growMode & gmCenter) 
				my-> set_centered( self, var-> growMode & gmXCenter, var-> growMode & gmYCenter);
			if ( !event-> gen. B)
				my-> first_that( self, (void*) Widget_size_notify, &event-> gen. R);
			if ( doNotify) {
				Point oldSize;
				oldSize. x = event-> gen. R. left;
				oldSize. y = event-> gen. R. bottom;
				my-> notify( self, "<sPP", "Size", oldSize, event-> gen. P);
			}
			Widget_pack_slaves( self);
			Widget_place_slaves( self);
		}
		break;
	}
}

void
Widget_hide( Handle self)
{
	enter_method;
	my-> set_visible( self, false);
}

void
Widget_hide_cursor( Handle self)
{
	enter_method;
	if ( my-> get_cursorVisible( self))
		my-> set_cursorVisible( self, false);
	else
		var-> cursorLock++;
}

/*::i */
void
Widget_insert_behind ( Handle self, Handle widget)
{
	apc_widget_set_z_order( self, widget, 0);
}

void
Widget_invalidate_rect( Handle self, Rect rect)
{
	enter_method;
	if ( !opt_InPaint && ( var-> stage == csNormal) && !my-> get_locked( self))
		apc_widget_invalidate_rect( self, &rect);
}


Bool
Widget_is_child( Handle self, Handle owner)
{
	if ( !owner)
		return false;
	while ( self) {
		if ( self == owner)
			return true;
		self = var-> owner;
	}
	return false;
}

/*::j */
/*::k */
void
Widget_key_event( Handle self, int command, int code, int key, int mod, int repeat, Bool post)
{
	Event ev;
	if ( command != cmKeyDown && command != cmKeyUp) return;
	memset( &ev, 0, sizeof( ev));
	if ( repeat <= 0) repeat = 1;
	ev. cmd = command;
	ev. key. code   = code;
	ev. key. key    = key;
	ev. key. mod    = mod;
	ev. key. repeat = repeat;
	apc_message( self, &ev, post);
}

/*::l */
Handle
Widget_last( Handle self)
{
	return apc_widget_get_z_order( self, zoLast);
}

Bool
Widget_lock( Handle self)
{
	var-> lockCount++;
	return true;
}

/*::m */
void
Widget_mouse_event( Handle self, int command, int button, int mod, int x, int y, Bool dbl, Bool post)
{
	Event ev;
	if ( command != cmMouseDown
		&& command != cmMouseUp
		&& command != cmMouseClick
		&& command != cmMouseMove
		&& command != cmMouseWheel
		&& command != cmMouseEnter
		&& command != cmMouseLeave
	) return;
	
	memset( &ev, 0, sizeof( ev));
	ev. cmd = command;
	ev. pos. where. x = x;
	ev. pos. where. y = y;
	ev. pos. mod    = mod;
	ev. pos. button = button;
	if ( command == cmMouseClick) ev. pos. dblclk = dbl;
	apc_message( self, &ev, post);
}

/*::n */
Handle
Widget_next( Handle self)
{
	return apc_widget_get_z_order( self, zoNext);
}

static void 
fill_tab_candidates( PList list, Handle level)
{
	int i;
	PList w = &(PWidget( level)-> widgets);
	for ( i = 0; i < w-> count; i++) {
		Handle x = w-> items[i];
		if ( CWidget( x)-> get_visible( x) && CWidget( x)-> get_enabled( x)) {
			if ( CWidget( x)-> get_selectable( x) && CWidget( x)-> get_tabStop( x))
				list_add( list, x);
			fill_tab_candidates( list, x);
		}
	}   
}

Handle 
Widget_next_positional( Handle self, int dx, int dy)
{
	Handle horizon = self;
	
	int i, maxDiff = INT_MAX;
	Handle max = nilHandle;
	List candidates;
	Point p[2];
	
	int minor[2], major[2], axis, extraDiff, ir[4];

	/*
		In order to compute positional difference, using four penalties.
		To simplify algorithm, Rect will be translated to int[4] and
		minor, major and extraDiff assigned to array indices for those 
		steps - minor for first and third, major for second and extraDiff for last one.
	*/
	
	axis = ( dx == 0) ? dy : dx;
	minor[0] = ( dx == 0) ? 0 : 1;
	minor[1] = minor[0] + 2;
	extraDiff = major[(axis < 0) ? 0 : 1] = ( dx == 0) ? 1 : 0;
	major[(axis < 0) ? 1 : 0] = extraDiff + 2;
	extraDiff = ( dx == 0) ? (( axis < 0) ? 0 : 2) : (( axis < 0) ? 1 : 3);
	
	while ( PWidget( horizon)-> owner) {
		if (
			( PWidget( horizon)-> options. optSystemSelectable) || /* fast check for CWindow */
			( PWidget( horizon)-> options. optModalHorizon) 
			) break; 
		horizon = PWidget( horizon)-> owner;
	}

	if ( !CWidget( horizon)-> get_visible( horizon) ||
		!CWidget( horizon)-> get_enabled( horizon)) return nilHandle;

	list_create( &candidates, 64, 64);
	fill_tab_candidates( &candidates, horizon);
	
	p[0].x = p[0].y = 0;
	p[1] = CWidget( self)-> get_size( self);
	apc_widget_map_points( self, true, 2, p);
	apc_widget_map_points( horizon, false, 2, p);
	ir[0] = p[0].x; ir[1] = p[0].y; ir[2] = p[1].x; ir[3] = p[1].y;

	for ( i = 0; i < candidates. count; i++) {
		int    diff, ix[4];
		Handle x = candidates. items[i];

		if ( x == self) continue;
		
		p[0].x = p[0].y = 0;
		p[1] = CWidget( x)-> get_size( x);
		apc_widget_map_points( x, true, 2, p);
		apc_widget_map_points( horizon, false, 2, p);
		ix[0] = p[0].x; ix[1] = p[0].y; ix[2] = p[1].x; ix[3] = p[1].y;

		/* First step - checking if the widget is subject to comparison. It is not,
			if it's minor axis is not contiguous with self's */

		if ( ix[ minor[0]] > ir[ minor[1]] || ix[ minor[1]] < ir[ minor[0]]) 
			continue;

		/* Using x100 penalty for distance in major axis - and discarding those that 
			of different sign */
		diff = ( ix[ major[ 1]] - ir[ major[0]]) * 100 * axis;
		if ( diff < 0) 
			continue;

		/* Adding x10 penalty for incomplete minor axis congruence. Addition goes in tenths,
			in a way to not allow congruence overweight major axis distance */
		if ( ix[ minor[0]] > ir[ minor[0]])
			diff += ( ix[ minor[0]] - ir[ minor[0]]) * 100 / ( ir[ minor[1]] - ir[ minor[0]]);
		if ( ix[ minor[1]] < ir[ minor[1]])
			diff += ( ir[ minor[1]] - ix[ minor[1]]) * 100 / ( ir[ minor[1]] - ir[ minor[0]]);

		/* Adding 'distance from level' x1 penalty */
		if (( ix[ extraDiff] - ir[ extraDiff]) * axis < 0) 
			diff += abs( ix[ extraDiff] - ir[ extraDiff]);

		if ( diff < maxDiff) {
			max = x;
			maxDiff = diff;
		}   
	}   
	
	list_destroy( &candidates);

	return max;
}

static int compare_taborders_forward( const void *a, const void *b)
{
	if ((*(PWidget*) a)-> tabOrder < (*(PWidget*) b)-> tabOrder)
		return -1; else
	if ((*(PWidget*) a)-> tabOrder > (*(PWidget*) b)-> tabOrder)
		return 1; 
	else
		return 0;
}   

static int compare_taborders_backward( const void *a, const void *b)
{
	if ((*(PWidget*) a)-> tabOrder < (*(PWidget*) b)-> tabOrder)
		return 1; else
	if ((*(PWidget*) a)-> tabOrder > (*(PWidget*) b)-> tabOrder)
		return -1; 
	else
		return 0;
}   

static int
do_taborder_candidates( Handle level, Handle who, 
	int (*compareProc)(const void *, const void *), 
	int * stage, Handle * result)
{
	int i, fsel = -1;
	PList w = &(PWidget( level)-> widgets);
	Handle * ordered;

	if ( w-> count == 0) return true;
		
	ordered = ( Handle *) malloc( w-> count * sizeof( Handle));
	if ( !ordered) return true;
	
	memcpy( ordered, w-> items, w-> count * sizeof( Handle));
	qsort( ordered, w-> count, sizeof( Handle), compareProc);

	/* finding current widget in the group */
	for ( i = 0; i < w-> count; i++) {
		Handle x = ordered[i];
		if ( CWidget( x)-> get_current( x)) {
			fsel = i;
			break;
		}   
	}   
	if ( fsel < 0) fsel = 0;
	
	for ( i = 0; i < w-> count; i++) {
		int j;
		Handle x;

		j = i + fsel;
		if ( j >= w-> count) j -= w-> count;
		
		x = ordered[j];
		if ( CWidget( x)-> get_visible( x) && CWidget( x)-> get_enabled( x)) {
			if ( CWidget( x)-> get_selectable( x) && CWidget( x)-> get_tabStop( x)) {
				if ( *result == nilHandle) *result = x; 
				switch( *stage) {
				case 0: /* nothing found yet */
					if ( x == who) *stage = 1;
					break;
				default:
					/* next widget after 'who' is ours */
					*result = x;
					free( ordered);
					return false;
				}   
			}   
			if ( !do_taborder_candidates( x, who, compareProc, stage, result)) {
				free( ordered);
				return false; /* fall through */
			}   
		}
	}   
	free( ordered);
	return true;
}

Handle 
Widget_next_tab( Handle self, Bool forward)
{
	Handle horizon = self, result = nilHandle;
	int stage = 0;

	while ( PWidget( horizon)-> owner) {
		if (
			( PWidget( horizon)-> options. optSystemSelectable) || /* fast check for CWindow */
			( PWidget( horizon)-> options. optModalHorizon) 
			) break; 
		horizon = PWidget( horizon)-> owner;
	}

	if ( !CWidget( horizon)-> get_visible( horizon) ||
		!CWidget( horizon)-> get_enabled( horizon)) return nilHandle;
	
	do_taborder_candidates( horizon, self, 
		forward ? compare_taborders_forward : compare_taborders_backward, 
		&stage, &result);
	if ( result == self) result = nilHandle;
	return result;
}

/*::o */
/*::p */


void
Widget_post_message( Handle self, SV * info1, SV * info2)
{
	PPostMsg p;
	Event ev = { cmPost};
	if ( var-> stage > csNormal) return;
	if (!( p = alloc1( PostMsg))) return;
	p-> info1  = newSVsv( info1);
	p-> info2  = newSVsv( info2);
	p-> h = self;
	if ( var-> postList == nil)
		var-> postList = plist_create( 8, 8);
	list_add( var-> postList, ( Handle) p);
	ev. gen. p = p;
	ev. gen. source = ev. gen. H = self;
	apc_message( self, &ev, true);
}

Handle
Widget_prev( Handle self)
{
	return apc_widget_get_z_order( self, zoPrev);
}

Bool
Widget_process_accel( Handle self, int key)
{
	enter_method;
	if ( my-> first_that_component( self, (void*)find_accel, &key)) return true;
	return kind_of( var-> owner, CWidget) ?
			((( PWidget) var-> owner)-> self)->process_accel( var-> owner, key) : false;
}

/*::q */
/*::r */
void
Widget_repaint( Handle self)
{
	enter_method;
	if ( !opt_InPaint && ( var-> stage == csNormal) && !my-> get_locked( self))
		apc_widget_invalidate_rect( self, nil);
}

/*::s */
int
Widget_scroll( Handle self, int dx, int dy, Rect *confine, Rect *clip, Bool withChildren)
{
	enter_method;
	if ( !opt_InPaint && ( var-> stage == csNormal) && !my-> get_locked( self))
		return apc_widget_scroll( self, dx, dy, confine, clip, withChildren);
	return scrError;
}

int
Widget_scroll_REDEFINED( Handle self, int dx, int dy, Rect *confine, Rect *clip, Bool withChildren)
{
	warn("Invalid call of Widget::scroll");
	return scrError;
}

XS( Widget_scroll_FROMPERL)
{
	dPROFILE;
	dXSARGS;
	Handle self;
	int dx, dy, ret;
	Rect *confine = nil;
	Rect *clip = nil;
	Rect confine_rect, clip_rect;
	Bool withChildren = false;
	HV *profile;
	int rect[4];

	if ( items < 3 || (items - 3) % 2) goto invalid_usage;
	if (!( self = gimme_the_mate( ST(0)))) goto invalid_usage;
	dx = SvIV( ST(1));
	dy = SvIV( ST(2));
	profile = parse_hv( ax, sp, items, mark, 3, "Widget::scroll");
	if ( pexist( confineRect)) {
		prima_read_point( pget_sv( confineRect), rect, 4, "Array panic on 'confineRect'");
		confine = &confine_rect;
		confine-> left = rect[0];
		confine-> bottom = rect[1];
		confine-> right = rect[2];
		confine-> top = rect[3];
	}
	if ( pexist( clipRect)) {
		prima_read_point( pget_sv( clipRect), rect, 4, "Array panic on 'clipRect'");
		clip = &clip_rect;
		clip-> left = rect[0];
		clip-> bottom = rect[1];
		clip-> right = rect[2];
		clip-> top = rect[3];
	}
	if ( pexist( withChildren)) withChildren = pget_B( withChildren);
	sv_free((SV*)profile);
	ret = Widget_scroll( self, dx, dy, confine, clip, withChildren);
	SPAGAIN;
	SP -= items;
	XPUSHs( sv_2mortal( newSViv( ret)));
	PUTBACK;
	return;
invalid_usage:
	croak ("Invalid usage of %s", "Widget::scroll");
}

void
Widget_send_to_back( Handle self)
{
	apc_widget_set_z_order( self, nilHandle, false);
}

void
Widget_set( Handle self, HV * profile)
{
	dPROFILE;
	enter_method;
	Handle postOwner = nilHandle;
	AV *order = nil;
	int geometry = gtDefault;

	if ( pexist(__ORDER__)) order = (AV*)SvRV(pget_sv( __ORDER__));

	if ( pexist( owner)) {
		if ( !my-> validate_owner( self, &postOwner, profile))
			croak( "Illegal 'owner' reference passed to %s::%s", my-> className, "set");
		if ( postOwner != var-> owner) {
			if ( is_opt( optOwnerColor)) {
				my-> set_color( self, CWidget( postOwner)-> get_color( postOwner));
				opt_set( optOwnerColor);
			}
			if ( is_opt( optOwnerBackColor)) {
				my-> set_backColor( self, CWidget( postOwner)-> get_backColor( postOwner));
				opt_set( optOwnerBackColor);
			}
			if ( is_opt( optOwnerShowHint)) {
				Bool newSH = ( postOwner == application) ? 1 :
					CWidget( postOwner)-> get_showHint( postOwner);
				my-> set_showHint( self, newSH);
				opt_set( optOwnerShowHint);
			}
			if ( is_opt( optOwnerHint)) {
				my-> set_hint( self, CWidget( postOwner)-> get_hint( postOwner));
				opt_set( optOwnerHint);
			}
			if ( is_opt( optOwnerFont)) {
				my-> set_font ( self, CWidget( postOwner)-> get_font( postOwner));
				opt_set( optOwnerFont);
			}
		}
		if ( var-> geometry != gtDefault) {
			geometry = var-> geometry;
			my-> set_geometry( self, gtDefault);
		}
	}

	/* geometry manipulations */
	{
#define iLEFT   0      
#define iRIGHT  1
#define iTOP    2
#define iBOTTOM 3
#define iWIDTH  4
#define iHEIGHT 5
		int i, count;
		Bool exists[ 6];
		int  values[ 6];

		bzero( values, sizeof(values));
		if ( pexist( origin))
		{
			int set[2];
			if (order && !pexist(left))   av_push( order, newSVpv("left",0));
			if (order && !pexist(bottom)) av_push( order, newSVpv("bottom",0));
			prima_read_point( pget_sv( origin), set, 2, "Array panic on 'origin'");
			pset_i( left,   set[0]);
			pset_i( bottom, set[1]);
			pdelete( origin);
		}
		if ( pexist( rect))
		{
			int rect[4];
			if (order && !pexist(left)) av_push( order, newSVpv("left",0));
			if (order && !pexist(bottom)) av_push( order, newSVpv("bottom",0));
			if (order && !pexist(width)) av_push( order, newSVpv("width",0));
			if (order && !pexist(height)) av_push( order, newSVpv("height",0));
			prima_read_point( pget_sv( rect), rect, 4, "Array panic on 'rect'");
			pset_i( left,   rect[0]);
			pset_i( bottom, rect[1]);
			pset_i( width,  rect[2] - rect[0]);
			pset_i( height, rect[3] - rect[1]);
			pdelete( rect);
		}
		if ( pexist( size))
		{
			int set[2];
			if (order && !pexist(width)) av_push( order, newSVpv("width",0));
			if (order && !pexist(height)) av_push( order, newSVpv("height",0));
			prima_read_point( pget_sv( size), set, 2, "Array panic on 'size'");
			pset_i( width,  set[0]);
			pset_i( height, set[1]);
			pdelete( size);
		}

		if (( exists[ iLEFT]   = pexist( left)))    values[ iLEFT]   = pget_i( left);
		if (( exists[ iRIGHT]  = pexist( right)))   values[ iRIGHT]  = pget_i( right);
		if (( exists[ iTOP]    = pexist( top)))     values[ iTOP]    = pget_i( top);
		if (( exists[ iBOTTOM] = pexist( bottom ))) values[ iBOTTOM] = pget_i( bottom);
		if (( exists[ iWIDTH]  = pexist( width)))   values[ iWIDTH]  = pget_i( width);
		if (( exists[ iHEIGHT] = pexist( height)))  values[ iHEIGHT] = pget_i( height);

		count = 0;
		for ( i = 0; i < 6; i++) if ( exists[ i]) count++;

		if ( count > 1) {
			if ( exists[ iWIDTH] && exists[ iRIGHT] && exists[ iLEFT]) {
				exists[ iRIGHT] = 0;
				count--;
			}
			if ( exists[ iHEIGHT] && exists[ iTOP] && exists[ iBOTTOM]) {
				exists[ iTOP] = 0;
				count--;
			}
			if ( exists[ iRIGHT] && exists[ iLEFT]) {
				exists[ iWIDTH] = 1;
				values[ iWIDTH] = values[ iRIGHT] - values[ iLEFT];
				exists[ iRIGHT] = 0;
			}
			if ( exists[ iTOP] && exists[ iBOTTOM]) {
				exists[ iHEIGHT] = 1;
				values[ iHEIGHT] = values[ iTOP] - values[ iBOTTOM];
				exists[ iTOP]    = 0;
			}

			if (
				( count == 2) &&
				(
					( exists[ iLEFT]  && exists[ iBOTTOM]) ||
					( exists[ iWIDTH] && exists[ iHEIGHT])
				)
				) {
				Point p;
				if ( exists[ iLEFT]) {
					p. x = values[ iLEFT];
					p. y = values[ iBOTTOM];
					my-> set_origin( self, p);
				} else {
					p. x = values[ iWIDTH];
					p. y = values[ iHEIGHT];
					my-> set_size( self, p);
				}
			} else {
				Rect r;
				if ( !exists[ iWIDTH] || !exists[ iHEIGHT]) {
					Point sz;
					sz = my-> get_size( self);
					if ( !exists[ iWIDTH])  values[ iWIDTH]  = sz. x;
					if ( !exists[ iHEIGHT]) values[ iHEIGHT] = sz. y;
					exists[ iWIDTH] = exists[ iHEIGHT] = 1; 
				}
				if ( ( !exists[ iLEFT]   && !exists[ iRIGHT]) ||
					( !exists[ iBOTTOM] && !exists[ iTOP])) {
					Point pos;
					pos = my-> get_origin( self);
					if ( !exists[ iLEFT])   values[ iLEFT]   = pos. x;
					if ( !exists[ iBOTTOM]) values[ iBOTTOM] = pos. y;
					exists[ iLEFT] = exists[ iBOTTOM] = 1; 
				}
				if ( !exists[ iLEFT]) {
					exists[ iLEFT] = 1;
					values[ iLEFT] = values[ iRIGHT] - values[ iWIDTH];
				}
				if ( !exists[ iBOTTOM]) {
					exists[ iBOTTOM] = 1;
					values[ iBOTTOM] = values[ iTOP] - values[ iHEIGHT];
				}
				r. left   = values[ iLEFT];
				r. bottom = values[ iBOTTOM];
				r. right  = values[ iLEFT] + values[ iWIDTH];
				r. top    = values[ iBOTTOM] + values[ iHEIGHT];
				my-> set_rect( self, r);
			}
			pdelete( left);
			pdelete( right);
			pdelete( top);
			pdelete( bottom);
			pdelete( width);
			pdelete( height);
		} /* count > 1 */
	}
	if ( pexist( popupFont))
	{
		SvHV_Font( pget_sv( popupFont), &Font_buffer, "Widget::set");
		my-> set_popup_font( self, Font_buffer);
		pdelete( popupFont);
	}
	if ( pexist( pointerIcon) && pexist( pointerHotSpot))
	{
		Point hotSpot;
		Handle icon = pget_H( pointerIcon);
		prima_read_point( pget_sv( pointerHotSpot), (int*)&hotSpot, 2, "Array panic on 'pointerHotSpot'");
		if ( icon != nilHandle && !kind_of( icon, CIcon)) {
			warn("Illegal object reference passed to Widget.set_pointer_icon");
			icon = nilHandle;
		}
		apc_pointer_set_user( self, icon, hotSpot);
		if ( var-> pointerType == crUser) my-> first_that( self, (void*)sptr, nil);
		pdelete( pointerIcon);
		pdelete( pointerHotSpot);
	}
	if ( pexist( designScale))
	{
		AV * av = ( AV *) SvRV( pget_sv( designScale));
		SV ** holder = av_fetch( av, 0, 0);
		NPoint ds = {1,1};
		ds. x = holder ? SvNV( *holder) : 1;
		if ( !holder) warn("Array panic on 'designScale'");
		holder = av_fetch( av, 1, 0);
		ds. y = holder ? SvNV( *holder) : 1;
		if ( !holder) warn("Array panic on 'designScale'");
		my-> set_designScale( self, ds);
		pdelete( designScale);
	}
	if ( pexist( sizeMin)) {
		Point set;
		prima_read_point( pget_sv( sizeMin), (int*)&set, 2, "Array panic on 'sizeMin'");
		my-> set_sizeMin( self, set);
		pdelete( sizeMin);
	}
	if ( pexist( sizeMax)) {
		Point set;
		prima_read_point( pget_sv( sizeMax), (int*)&set, 2, "Array panic on 'sizeMax'");
		my-> set_sizeMax( self, set);
		pdelete( sizeMax);
	}
	if ( pexist( cursorSize)) {
		Point set;
		prima_read_point( pget_sv( cursorSize), (int*)&set, 2, "Array panic on 'cursorSize'");
		my-> set_cursorSize( self, set);
		pdelete( cursorSize);
	}
	if ( pexist( cursorPos)) {
		Point set;
		prima_read_point( pget_sv( cursorPos), (int*)&set, 2, "Array panic on 'cursorPos'");
		my-> set_cursorPos( self, set);
		pdelete( cursorPos);
	}
	if ( pexist( geomSize))
	{
		Point set;
		prima_read_point( pget_sv( geomSize), (int*)&set, 2, "Array panic on 'geomSize'");
		my-> set_geomSize( self, set);
		pdelete( geomSize);
	}

	inherited-> set( self, profile);
	if ( postOwner) {
		my-> set_tabOrder( self, var-> tabOrder);
		my-> set_geometry( self, geometry);
	}
}

void
Widget_setup( Handle self)
{
	enter_method;
	if ( get_top_current( self) &&
		my-> get_enabled( self) &&
		my-> get_visible( self))
		my-> set_selected( self, true);
	inherited-> setup( self);
}

void
Widget_show( Handle self)
{
	enter_method;
	my-> set_visible( self, true);
}

void
Widget_show_cursor( Handle self)
{
	enter_method;
	if ( var-> cursorLock-- <= 0) {
		my-> set_cursorVisible( self, true);
		var-> cursorLock = 0;
	}
}

/*::t */
/*::u */

static Bool
repaint_all( Handle owner, Handle self, void * dummy)
{
	enter_method;
	my-> repaint( self);
	my-> first_that( self, (void*)repaint_all, nil);
	return false;
}

Bool
Widget_unlock( Handle self)
{
	if ( --var-> lockCount <= 0) {
		var-> lockCount = 0;
		repaint_all( var-> owner, self, nil);
	}
	return true;
}

void
Widget_update_view( Handle self)
{
	if ( !opt_InPaint) apc_widget_update( self);
}

/*::v */

Bool
Widget_validate_owner( Handle self, Handle * owner, HV * profile)
{
	dPROFILE;
	*owner = pget_H( owner);
	if ( !kind_of( *owner, CWidget)) return false;
	return inherited-> validate_owner( self, owner, profile);
}

/*::w */
/*::x */
/*::y */
/*::z */

/* get_props() */

Font
Widget_get_default_font( char * dummy)
{
	Font font;
	apc_widget_default_font( &font);
	return font;
}

Font
Widget_get_default_popup_font( char * dummy)
{
	Font f;
	apc_popup_default_font( &f);
	return f;
}


NPoint
Widget_designScale( Handle self, Bool set, NPoint designScale)
{
	if ( !set)
		return var-> designScale;
	if ( designScale. x < 0) designScale. x = 0;
	if ( designScale. y < 0) designScale. y = 0;
	var-> designScale = designScale;
	return designScale;
}

int
Widget_growMode( Handle self, Bool set, int growMode)
{
	enter_method;
	Bool x = false, y = false;
	if ( !set)
		return var-> growMode;
	var-> growMode = growMode;
	if ( var-> growMode & gmXCenter) x = true;
	if ( var-> growMode & gmYCenter) y = true;
	if ( var-> geometry == gtGrowMode && (x || y)) my-> set_centered( self, x, y);
	return var-> growMode;
}

SV *
Widget_get_handle( Handle self)
{
	char buf[ 256];
	snprintf( buf, 256, "0x%08lx", apc_widget_get_handle( self));
	return newSVpv( buf, 0);
}


SV *
Widget_get_parent_handle( Handle self)
{
	char buf[ 256];
	snprintf( buf, 256, "0x%08lx", apc_widget_get_parent_handle( self));
	return newSVpv( buf, 0);
}

int
Widget_hintVisible( Handle self, Bool set, int hintVisible)
{
	Bool wantVisible;
	if ( !set)
		return PApplication( application)-> hintVisible;
	if ( var-> stage >= csDead) return false;
	wantVisible = ( hintVisible != 0);
	if ( wantVisible == PApplication( application)-> hintVisible) return false;
	if ( wantVisible) {
		if ( SvCUR( var-> hint) == 0) return false;
		if ( hintVisible > 0) PApplication(application)-> hintActive = -1; /* immediate */
	}
	CApplication( application)-> set_hint_action( application, self, wantVisible, false);
	return false;
}

Bool
Widget_get_locked( Handle self)
{
	while ( self) {
		if ( var-> lockCount != 0) return true;
		self = var-> owner;
	}
	return false;
}


Handle
Widget_get_parent( Handle self)
{
	enter_method;
	return my-> get_clipOwner( self) ? var-> owner : application;
}

Point
Widget_get_pointer_size( char*dummy)
{
	return apc_pointer_get_size( nilHandle);
}

Font
Widget_get_popup_font( Handle self)
{
	return var-> popupFont;
}

Handle
Widget_get_selectee( Handle self)
{
	if ( var-> stage > csFrozen) return nilHandle;
	if ( is_opt( optSelectable))
		return self;
	else if ( var-> currentWidget) {
		PWidget w = ( PWidget) var-> currentWidget;
		if ( w-> options. optSystemSelectable && !w-> self-> get_clipOwner(( Handle) w))
			return ( Handle) w;
		else
			return w-> self-> get_selectee(( Handle) w);
	} else if ( is_opt( optSystemSelectable))
		return self;
	else
		return find_tabfoc( self);
}

Point
Widget_get_virtual_size( Handle self)
{
	return var-> virtualSize;
}

/* set_props() */

Bool
Widget_set_capture( Handle self, Bool capture, Handle confineTo)
{
	if ( opt_InPaint) return false;
	return apc_widget_set_capture( self, capture, confineTo);
}

void
Widget_set_centered( Handle self, Bool x, Bool y)
{
	enter_method;
	Handle parent = my-> get_parent( self);
	Point size    = CWidget( parent)-> get_size( parent);
	Point mysize  = my-> get_size ( self);
	Point mypos   = my-> get_origin( self);
	Point delta   = {0,0};

	if ( !x && !y ) return;

	if ( parent == application ) {
		int i, nrects = 0;
		Rect2 *best = nil, *rects = apc_application_get_monitor_rects( application, &nrects);
		for ( i = 0; i < nrects; i++) {
			Rect2 * curr = rects + i;
			if ( best == nil || best-> x > curr->x || best->y > curr->y)
				best = curr;
		}
		if ( best ) {
			delta.x = best->x;
			delta.y = best->y;
			size.x  = best->width;
			size.y  = best->height;
		}
	}
	if ( x) mypos. x = ( size. x - mysize. x) / 2 + delta.x;
	if ( y) mypos. y = ( size. y - mysize. y) / 2 + delta.y;
	my-> set_origin( self, mypos);
}

void
Widget_set_font( Handle self, Font font)
{
	enter_method;
	if ( var-> stage > csFrozen) return;
	if ( !opt_InPaint) my-> first_that( self, (void*)font_notify, &font);
	if ( var-> handle == nilHandle) return; /* aware of call from Drawable::init */
	if ( opt_InPaint) {
		inherited->set_font(self, font);
	}
	else {
		apc_font_pick( self, &font, & var-> font);
		opt_clear( optOwnerFont);
		apc_widget_set_font( self, & var-> font);
		my-> repaint( self);
	}
}

void
Widget_set_popup_font( Handle self, Font font)
{
	apc_font_pick( self, &font, &var-> popupFont);
}

/* event handlers */

void
Widget_on_paint( Handle self, SV * canvas)
{
	int i;
	dSP;
	ENTER;
	SAVETMPS;
	PUSHMARK( sp);
	XPUSHs( canvas);
	for ( i = 0; i < 4; i++)
		XPUSHs( sv_2mortal( newSViv( -1)));
	PUTBACK;
	PERL_CALL_METHOD( "clear", G_DISCARD);
	SPAGAIN;
	PUTBACK;
	FREETMPS;
	LEAVE;
}

/*
void Widget_on_click( Handle self) {}
void Widget_on_change( Handle self) {}
void Widget_on_close( Handle self) {}
void Widget_on_colorchanged( Handle self, int colorIndex){}
void Widget_on_disable( Handle self) {}
void Widget_on_dragdrop( Handle self, Handle source , int x , int y ) {}
void Widget_on_dragover( Handle self, Handle source , int x , int y , int state ) {}
void Widget_on_enable( Handle self) {}
void Widget_on_enddrag( Handle self, Handle target , int x , int y ) {}
void Widget_on_fontchanged( Handle self) {}
void Widget_on_enter( Handle self) {}
void Widget_on_keydown( Handle self, int code , int key , int shiftState, int repeat ) {}
void Widget_on_keyup( Handle self, int code , int key , int shiftState ) {}
void Widget_on_menu( Handle self, Handle menu, char * variable) {}
void Widget_on_setup( Handle self) {}
void Widget_on_size( Handle self, Point oldSize, Point newSize) {}
void Widget_on_move( Handle self, Point oldPos, Point newPos) {}
void Widget_on_show( Handle self) {}
void Widget_on_hide( Handle self) {}
void Widget_on_hint( Handle self, Bool show) {}
void Widget_on_translateaccel( Handle self, int code , int key , int shiftState ) {}
void Widget_on_zorderchanged( Handle self) {}
void Widget_on_popup( Handle self, Bool mouseDriven, int x, int y) {}
void Widget_on_mouseclick( Handle self, int button , int shiftState , int x , int y , Bool dbl ) {}
void Widget_on_mousedown( Handle self, int button , int shiftState , int x , int y ) {}
void Widget_on_mouseup( Handle self, int button , int shiftState , int x , int y ) {}
void Widget_on_mousemove( Handle self, int shiftState , int x , int y ) {}
void Widget_on_mousewheel( Handle self, int shiftState , int x , int y, int z ) {}
void Widget_on_mouseenter( Handle self, int shiftState , int x , int y ) {}
void Widget_on_mouseleave( Handle self ) {}
void Widget_on_leave( Handle self) {}
*/


/* static iterators */
Bool kill_all( Handle self, Handle child, void * dummy)
{
	Object_destroy( child); return 0;
}

static Bool find_dup_msg( PEvent event, int * cmd) { return event-> cmd == *cmd; }

Bool
accel_notify ( Handle group, Handle self, PEvent event)
{
	enter_method;
	if (( self != event-> key. source) && my-> get_enabled( self))
		return ( var-> stage <= csNormal) ? !my-> message( self, event) : false;
	else
		return false;
}

static Bool
pquery ( Handle window, Handle self, void * v)
{
	enter_method;
	Event ev = {cmClose};
	return ( var-> stage <= csNormal) ? !my-> message( self, &ev) : false;
}

Bool
find_accel( Handle self, Handle item, int * key)
{
	return ( kind_of( item, CAbstractMenu)
				&& CAbstractMenu(item)-> sub_call_key( item, *key));
}

static Handle
find_tabfoc( Handle self)
{
	int i;
	Handle toRet;
	for ( i = 0; i < var-> widgets. count; i++) {
		PWidget w = ( PWidget)( var-> widgets. items[ i]);
		if (
			w-> self-> get_selectable(( Handle) w) &&
			w-> self-> get_enabled(( Handle) w)
		)
			return ( Handle) w;
	}
	for ( i = 0; i < var-> widgets. count; i++)
		if (( toRet = find_tabfoc( var-> widgets. items[ i])))
			return toRet;
	return nilHandle;
}


static Bool
get_top_current( Handle self)
{
	PWidget o  = ( PWidget) var-> owner;
	Handle  me = self;
	while ( o) {
		if ( o-> currentWidget != me)
			return false;
		me = ( Handle) o;
		o  = ( PWidget) o-> owner;
	}
	return true;
}

static Bool
sptr( Handle window, Handle self, void * v)
{
	enter_method;
	/* does nothing but refreshes system pointer */
	if ( var-> pointerType == crDefault)
		my-> set_pointerType( self, crDefault);
	return false;
}

/* static iterators for ownership notifications */

Bool
font_notify ( Handle self, Handle child, void * font)
{
	if ( his-> options. optOwnerFont) {
		his-> self-> set_font ( child, *(( PFont) font));
		his-> options. optOwnerFont = 1;
	}
	return false;
}

static Bool
showhint_notify ( Handle self, Handle child, void * data)
{
	if ( his-> options. optOwnerShowHint) {
		his-> self-> set_showHint ( child, *(( Bool *) data));
		his-> options. optOwnerShowHint = 1;
	}
	return false;
}

static Bool
hint_notify ( Handle self, Handle child, SV * hint)
{
	if ( his-> options. optOwnerHint) {
		his-> self-> set_hint( child, hint);
		his-> options. optOwnerHint = 1;
	}
	return false;
}

Bool
single_color_notify ( Handle self, Handle child, void * color)
{
	PSingleColor s = ( PSingleColor) color;
	if ( his-> options. optOwnerColor && ( s-> index == ciFore))
	{
		his-> self-> colorIndex ( child, true, s-> index, s-> color);
		his-> options. optOwnerColor = 1;
	} else if (( his-> options. optOwnerBackColor) && ( s-> index == ciBack))
	{
		his-> self-> colorIndex ( child, true, s-> index, s-> color);
		his-> options. optOwnerBackColor = 1;
	} else if ( s-> index > ciBack)
		his-> self-> colorIndex ( child, true, s-> index, s-> color);
	return false;
}

Bool
prima_read_point( SV *rv_av, int * pt, int number, char * error)
{
	SV ** holder;
	int i;
	AV *av;
	Bool result = true;

	if ( !rv_av || !SvROK( rv_av) || ( SvTYPE( SvRV( rv_av)) != SVt_PVAV)) {
		result = false;
		if ( error) croak( "%s", error);
	} else {
		av = (AV*)SvRV(rv_av);
		for ( i = 0; i < number; i++) {
			holder = av_fetch( av, i, 0);
			if ( holder)
				pt[i] = SvIV( *holder);
			else {
				pt[i] = 0;
				result = false;
				if ( error) croak( "%s", error);
			}
		}
	}
	return result;
}

static Bool
auto_enable_children( Handle self, Handle child, void * enable)
{
	apc_widget_set_enabled( child, PTR2UV( enable) != 0);
	return false;
}
/* properties section */

SV *
Widget_accelItems( Handle self, Bool set, SV * accelItems)
{
	dPROFILE;
	enter_method;
	if ( var-> stage > csFrozen) return nilSV;
	if ( !set)
		return var-> accelTable ?
			CAbstractMenu( var-> accelTable)-> get_items( var-> accelTable, "") : nilSV;
	if ( var-> accelTable == nilHandle) {
		HV * profile = newHV();
		if ( SvTYPE( accelItems)) pset_sv( items, accelItems);
		pset_H ( owner, self);
		my-> set_accelTable( self, create_instance( "Prima::AccelTable"));
		sv_free(( SV *) profile);
	} else
		CAbstractMenu( var-> accelTable)-> set_items( var-> accelTable, accelItems);
	return nilSV;
}

Handle
Widget_accelTable( Handle self, Bool set, Handle accelTable)
{
	enter_method;
	if ( var-> stage > csFrozen) return nilHandle;
	if ( !set)
		return var-> accelTable;
	if ( accelTable && !kind_of( accelTable, CAbstractMenu)) return nilHandle;
	if ( accelTable && (( PAbstractMenu) accelTable)-> owner != self)
		my-> set_accelItems( self, CAbstractMenu( accelTable)-> get_items( accelTable, ""));
	else
		var-> accelTable = accelTable;
	return accelTable;
}

Color
Widget_backColor( Handle self, Bool set, Color color)
{
	enter_method;
	if (!set) return my-> colorIndex( self, false, ciBack, 0);
	my-> colorIndex( self, true, ciBack, color);
	return color;
}

int
Widget_bottom( Handle self, Bool set, int bottom)
{
	enter_method;
	Point p = my-> get_origin( self);
	if ( !set)
		return p. y;
	p. y = bottom;
	my-> set_origin( self, p);
	return 0;
}

Bool
Widget_autoEnableChildren( Handle self, Bool set, Bool autoEnableChildren)
{
	if ( !set)
		return is_opt( optAutoEnableChildren);
	opt_assign( optAutoEnableChildren, autoEnableChildren);
	return false;
}

Bool
Widget_briefKeys( Handle self, Bool set, Bool briefKeys)
{
	if ( !set)
		return is_opt( optBriefKeys);
	opt_assign( optBriefKeys, briefKeys);
	return false;
}

Bool
Widget_buffered( Handle self, Bool set, Bool buffered)
{
	if ( !set)
		return is_opt( optBuffered);
	if ( !opt_InPaint)
		opt_assign( optBuffered, buffered);
	return false;
}

Bool
Widget_clipOwner( Handle self, Bool set, Bool clipOwner)
{
	HV * profile;
	enter_method;
	if ( !set)
		return apc_widget_get_clip_owner( self);
	profile = newHV();
	pset_i( clipOwner, clipOwner);
	my-> set( self, profile);
	sv_free(( SV *) profile);
	return false;
}

Color
Widget_color( Handle self, Bool set, Color color)
{
	enter_method;
	if (!set)
		return my-> colorIndex( self, false, ciFore, 0);
	return my-> colorIndex( self, true, ciFore, color);
}

Color
Widget_colorIndex( Handle self, Bool set, int index, Color color)
{
	if ( !set) {
		if ( index < 0 || index > ciMaxId) return clInvalid;
		switch ( index) {
		case ciFore:
			return opt_InPaint ? inherited-> get_color ( self) : apc_widget_get_color( self, ciFore);
		case ciBack:
			return opt_InPaint ? inherited-> get_backColor ( self) : apc_widget_get_color( self, ciBack);
		default:
			return apc_widget_get_color( self, index);
		}
	} else {
		enter_method;
		SingleColor s;
		s. color = color;
		s. index = index;
		if (( index < 0) || ( index > ciMaxId)) return clInvalid;
		if ( !opt_InPaint) my-> first_that( self, (void*)single_color_notify, &s);

		if ( var-> handle == nilHandle) return clInvalid; /* aware of call from Drawable::init */
		if ((( color & clSysFlag) != 0) && (( color & wcMask) == 0))
			color |= var-> widgetClass;
		if ( opt_InPaint) {
			switch ( index) {
				case ciFore:
					inherited-> set_color ( self, color);
					break;
				case ciBack:
					inherited-> set_backColor ( self, color);
					break;
				default:
					apc_widget_set_color ( self, color, index);
			}
		} else {
			switch ( index) {
				case ciFore:
					opt_clear( optOwnerColor);
					break;
				case ciBack:
					opt_clear( optOwnerBackColor);
					break;
			}
			apc_widget_set_color( self, color, index);
			my-> repaint( self);
		}
	}
	return 0;
}

Bool
Widget_current( Handle self, Bool set, Bool current)
{
	PWidget o;
	if ( var-> stage > csFrozen) return false;
	if ( !set)
		return var-> owner && ( PWidget( var-> owner)-> currentWidget == self);
	o = ( PWidget) var-> owner;
	if ( o == nil) return false;
	if ( current)
		o-> self-> set_currentWidget( var-> owner, self);
	else
		if ( o-> currentWidget == self)
			o-> self-> set_currentWidget( var-> owner, nilHandle);
	return current;
}

Handle
Widget_currentWidget( Handle self, Bool set, Handle widget)
{
	enter_method;
	if ( var-> stage > csFrozen) return nilHandle;
	if ( !set)
		return var-> currentWidget;
	if ( widget) {
		if ( !widget || ( PWidget( widget)-> stage > csFrozen) ||
			( PWidget( widget)-> owner != self)
		) return nilHandle;
		var-> currentWidget = widget;
	} else
		var-> currentWidget = nilHandle;

	/* adjust selection if we're in currently selected chain */
	if ( my-> get_selected( self))
		my-> set_selectedWidget( self, widget);
	return nilHandle;
}

Point
Widget_cursorPos( Handle self, Bool set, Point cursorPos)
{
	if ( !set)
		return apc_cursor_get_pos( self);
	apc_cursor_set_pos( self, cursorPos. x, cursorPos. y);
	return cursorPos;
}

Point
Widget_cursorSize( Handle self, Bool set, Point cursorSize)
{
	if ( !set)
		return apc_cursor_get_size( self);
	apc_cursor_set_size( self, cursorSize. x, cursorSize. y);
	return cursorSize;
}

Bool
Widget_cursorVisible( Handle self, Bool set, Bool cursorVisible)
{
	if ( !set)
		return apc_cursor_get_visible( self);
	return apc_cursor_set_visible( self, cursorVisible);
}

Bool
Widget_enabled( Handle self, Bool set, Bool enabled)
{
	if ( !set) return apc_widget_is_enabled( self);
	if ( !apc_widget_set_enabled( self, enabled)) 
		return false;
	if ( is_opt( optAutoEnableChildren)) 
		CWidget(self)-> first_that( self, (void*)auto_enable_children, INT2PTR(void*,enabled));
	return true;
}

Bool
Widget_firstClick( Handle self, Bool set, Bool firstClick)
{
	return set ?
		apc_widget_set_first_click( self, firstClick) :
		apc_widget_get_first_click( self);
}

Bool
Widget_focused( Handle self, Bool set, Bool focused)
{
	enter_method;
	if ( var-> stage > csNormal) return false;
	if ( !set)
		return apc_widget_is_focused( self);

	if ( focused) {
		PWidget x = ( PWidget)( var-> owner);
		Handle current = self;
		while ( x) {
			x-> currentWidget = current;
			current = ( Handle) x;
			x = ( PWidget) x-> owner;
		}
		var-> currentWidget = nilHandle;
		if ( var-> stage == csNormal)
			apc_widget_set_focused( self);
	} else
		if ( var-> stage == csNormal && my-> get_selected( self))
			apc_widget_set_focused( nilHandle);
	return focused;
}

SV *
Widget_helpContext( Handle self, Bool set, SV *helpContext)
{
	if ( set) {
		if ( var-> stage > csFrozen) return nilSV;
		free( var-> helpContext);
		var-> helpContext = nil;
		var-> helpContext = duplicate_string( SvPV_nolen( helpContext));
		opt_assign( optUTF8_helpContext, prima_is_utf8_sv(helpContext));
	} else {
		helpContext = newSVpv( var-> helpContext ? var-> helpContext : "", 0);
		if ( is_opt( optUTF8_helpContext)) SvUTF8_on( helpContext);
		return helpContext;
	}
	return nilSV;
}

SV *
Widget_hint( Handle self, Bool set, SV *hint)
{
	enter_method;
	if ( set) {
		if ( var-> stage > csFrozen) return nilSV;
		my-> first_that( self, (void*)hint_notify, (void*)hint);
		if ( var-> hint ) SvREFCNT_dec( var-> hint );
		var-> hint = hint;
		if ( var-> hint ) SvREFCNT_inc( var-> hint );
		if ( application && (( PApplication) application)-> hintVisible &&
			(( PApplication) application)-> hintUnder == self)
		{
			Handle hintWidget = (( PApplication) application)-> hintWidget;
			if ( SvLEN( var-> hint) == 0) 
				my-> set_hintVisible( self, 0);
			if ( hintWidget) 
				CWidget(hintWidget)-> set_text( hintWidget, my-> get_hint( self));
		}
		opt_clear( optOwnerHint);
	} else {
		return newSVsv(var->hint);
	}
	return nilSV;
}

Bool
Widget_layered( Handle self, Bool set, Bool layered)
{
	HV * profile;
	enter_method;
	if ( !set)
		return apc_widget_get_layered_request( self);
	profile = newHV();
	pset_i( layered, layered);
	my-> set( self, profile);
	sv_free(( SV *) profile);
	return false;
}

int
Widget_left( Handle self, Bool set, int left)
{
	enter_method;
	Point p = my-> get_origin( self);
	if ( !set)
		return p. x;
	p. x = left;
	my-> set_origin( self, p);
	return 0;
}

Point
Widget_origin( Handle self, Bool set, Point origin)
{
	if ( !set)
		return apc_widget_get_pos( self);
	apc_widget_set_pos( self, origin.x, origin.y);
	return origin;
}

Bool
Widget_ownerBackColor( Handle self, Bool set, Bool ownerBackColor)
{
	enter_method;
	if ( !set)
		return is_opt( optOwnerBackColor);
	opt_assign( optOwnerBackColor, ownerBackColor);
	if ( is_opt( optOwnerBackColor) && var-> owner)
	{
		my-> set_backColor( self, ((( PWidget) var-> owner)-> self)-> get_backColor( var-> owner));
		opt_set( optOwnerBackColor);
		my-> repaint ( self);
	}
	return false;
}

Bool
Widget_ownerColor( Handle self, Bool set, Bool ownerColor)
{
	enter_method;
	if ( !set)
		return is_opt( optOwnerColor);
	opt_assign( optOwnerColor, ownerColor);
	if ( is_opt( optOwnerColor) && var-> owner)
	{
		my-> set_color( self, ((( PWidget) var-> owner)-> self)-> get_color( var-> owner));
		opt_set( optOwnerColor);
		my-> repaint( self);
	}
	return false;
}

Bool
Widget_ownerFont( Handle self, Bool set, Bool ownerFont )
{
	enter_method;
	if ( !set)
		return is_opt( optOwnerFont);
	opt_assign( optOwnerFont, ownerFont);
	if ( is_opt( optOwnerFont) && var-> owner)
	{
		my-> set_font ( self, ((( PWidget) var-> owner)-> self)-> get_font ( var-> owner));
		opt_set( optOwnerFont);
		my-> repaint ( self);
	}
	return false;
}

Bool
Widget_ownerHint( Handle self, Bool set, Bool ownerHint )
{
	enter_method;
	if ( !set)
		return is_opt( optOwnerHint);
	opt_assign( optOwnerHint, ownerHint);
	if ( is_opt( optOwnerHint) && var-> owner)
	{
		my-> set_hint( self, ((( PWidget) var-> owner)-> self)-> get_hint ( var-> owner));
		opt_set( optOwnerHint);
	}
	return false;
}

Bool
Widget_ownerPalette( Handle self, Bool set, Bool ownerPalette)
{
	enter_method;
	if ( !set)
		return is_opt( optOwnerPalette);
	if ( ownerPalette) my-> set_palette( self, nilSV);
	opt_assign( optOwnerPalette, ownerPalette);
	return false;
}

Bool
Widget_ownerShowHint( Handle self, Bool set, Bool ownerShowHint )
{
	enter_method;
	if ( !set)
		return is_opt( optOwnerShowHint);
	opt_assign( optOwnerShowHint, ownerShowHint);
	if ( is_opt( optOwnerShowHint) && var-> owner)
	{
		my-> set_showHint( self, CWidget( var-> owner)-> get_showHint ( var-> owner));
		opt_set( optOwnerShowHint);
	}
	return false;
}

SV *
Widget_palette( Handle self, Bool set, SV * palette)
{
	int colors;
	if ( !set)
		return inherited-> palette( self, set, palette);

	if ( var-> stage > csFrozen) return nilSV;
	if ( var-> handle == nilHandle) return nilSV; /* aware of call from Drawable::init */

	colors = var-> palSize;
	free( var-> palette);
	var-> palette = read_palette( &var-> palSize, palette);
	opt_clear( optOwnerPalette);
	if ( colors == 0 && var-> palSize == 0)
		return nilSV; /* do not bother apc */
	if ( opt_InPaint)
		apc_gp_set_palette( self);
	else
		apc_widget_set_palette( self);
	return nilSV;
}

Handle
Widget_pointerIcon( Handle self, Bool set, Handle icon)
{
	enter_method;
	Point hotSpot;

	if ( var-> stage > csFrozen) return nilHandle;

	if ( !set) {
		HV * profile = newHV();
		Handle icon = Object_create( "Prima::Icon", profile);
		sv_free(( SV *) profile);
		apc_pointer_get_bitmap( self, icon);
		--SvREFCNT( SvRV((( PAnyObject) icon)-> mate));
		return icon;
	}

	if ( icon != nilHandle && !kind_of( icon, CIcon)) {
		warn("Illegal object reference passed to Widget::pointerIcon");
		return nilHandle;
	}
	hotSpot = my-> get_pointerHotSpot( self);
	apc_pointer_set_user( self, icon, hotSpot);
	if ( var-> pointerType == crUser) my-> first_that( self, (void*)sptr, nil);
	return nilHandle;
}

Point
Widget_pointerHotSpot( Handle self, Bool set, Point hotSpot)
{
	enter_method;
	Handle icon;
	if ( !set)
		return apc_pointer_get_hot_spot( self);
	if ( var-> stage > csFrozen) return hotSpot;
	icon = my-> get_pointerIcon( self);
	apc_pointer_set_user( self, icon, hotSpot);
	if ( var-> pointerType == crUser) my-> first_that( self, (void*)sptr, nil);
	return hotSpot;
}

int
Widget_pointerType( Handle self, Bool set, int type)
{
	enter_method;
	if ( var-> stage > csFrozen) return 0;
	if ( !set)
		return var-> pointerType;
	var-> pointerType = type;
	apc_pointer_set_shape( self, type);
	my-> first_that( self, (void*)sptr, nil);
	return type;
}

Point
Widget_pointerPos( Handle self, Bool set, Point p)
{
	if ( !set) {
		p = apc_pointer_get_pos( self);
		apc_widget_map_points( self, false, 1, &p);
		return p;
	}
	apc_widget_map_points( self, true, 1, &p);
	apc_pointer_set_pos( self, p. x, p. y);
	return p;
}

Handle
Widget_popup( Handle self, Bool set, Handle popup)
{
	enter_method;
	if ( var-> stage > csFrozen) return nilHandle;
	if ( !set)
		return var-> popupMenu;

	if ( popup && !kind_of( popup, CPopup)) return nilHandle;
	if ( popup && PAbstractMenu( popup)-> owner != self)
		my-> set_popupItems( self, CAbstractMenu( popup)-> get_items( popup, ""));
	else
		var-> popupMenu = popup;
	return nilHandle;
}

Color
Widget_popupColorIndex( Handle self, Bool set, int index, Color color)
{
	if (( index < 0) || ( index > ciMaxId)) return clInvalid;
	if ( !set)
		return var-> popupColor[ index];
	if ((( color & clSysFlag) != 0) && (( color & wcMask) == 0)) color |= wcPopup;
	var-> popupColor[ index] = color;
	return color;
}

SV *
Widget_popupItems( Handle self, Bool set, SV * popupItems)
{
	dPROFILE;
	enter_method;
	if ( var-> stage > csFrozen) return nilSV;
	if ( !set)
		return var-> popupMenu ?
			CAbstractMenu( var-> popupMenu)-> get_items( var-> popupMenu, "") : nilSV;

	if ( var-> popupMenu == nilHandle) {
	if ( SvTYPE( popupItems)) {
			HV * profile = newHV();
			pset_sv( items, popupItems);
			pset_H ( owner, self);
			my-> set_popup( self, create_instance( "Prima::Popup"));
			sv_free(( SV *) profile);
		}
	}
	else
		CAbstractMenu( var-> popupMenu)-> set_items( var-> popupMenu, popupItems);
	return popupItems;
}


Rect
Widget_rect( Handle self, Bool set, Rect r)
{
	enter_method;
	if ( !set) {
		Point p   = my-> get_origin( self);
		Point s   = my-> get_size( self);
		r. left   = p. x;
		r. bottom = p. y;
		r. right  = p. x + s. x;
		r. top    = p. y + s. y;
	} else 
		apc_widget_set_rect( self, r. left, r. bottom, r. right - r. left, r. top - r. bottom);
	return r;
}

int
Widget_right( Handle self, Bool set, int right)
{
	enter_method;
	Point p;
	Rect r = my-> get_rect( self);
	if ( !set)
		return r. right;
	p. x = r. left - r. right + right;
	p. y = r. bottom;
	my-> set_origin( self, p);
	return 0;
}

Bool
Widget_scaleChildren( Handle self, Bool set, Bool scaleChildren)
{
	if ( !set)
		return is_opt( optScaleChildren);
	opt_assign( optScaleChildren, scaleChildren);
	return false;
}

Bool
Widget_selectable( Handle self, Bool set, Bool selectable)
{
	if ( !set)
		return is_opt( optSelectable);
	opt_assign( optSelectable, selectable);
	return false;
}

Bool
Widget_selected( Handle self, Bool set, Bool selected)
{
	enter_method;
	if ( !set)
		return my-> get_selectedWidget( self) != nilHandle;

	if ( var-> stage > csFrozen) return selected;
	if ( selected) {
		if ( is_opt( optSelectable) && !is_opt( optSystemSelectable)) {
			my-> set_focused( self, true);
		} else
		if ( var-> currentWidget) {
			PWidget w = ( PWidget) var-> currentWidget;
			if ( w-> options. optSystemSelectable && !w-> self-> get_clipOwner(( Handle) w))
				w-> self-> bring_to_front(( Handle) w); /* <- very uncertain !!!! */
			else
				w-> self-> set_selected(( Handle) w, true);
		} else
		if ( is_opt( optSystemSelectable)) {
			/* nothing to do with Widget, reserved for Window */
		}
		else {
			PWidget toFocus = ( PWidget) find_tabfoc( self);
			if ( toFocus)
				toFocus-> self-> set_selected(( Handle) toFocus, 1);
			else {
			/* if group has no selectable widgets and cannot be selected by itself, */
			/* process chain of bring_to_front(), followed by set_focused(1) call, if available */
				PWidget x = ( PWidget) var-> owner;
				List  lst;
				int i;

				list_create( &lst, 8, 8);
				while ( x) {
					if ( !toFocus && x-> options. optSelectable) {
						toFocus = x;  /* choose closest owner to focus */
						break;
					}
					if (( Handle) x != application && !kind_of(( Handle) x, CWindow))
						list_insert_at( &lst, ( Handle) x, 0);
					x = ( PWidget) x-> owner;
				}

				if ( toFocus)
					toFocus-> self-> set_focused(( Handle) toFocus, 1);

				for ( i = 0; i < lst. count; i++) {
					PWidget v = ( PWidget) list_at( &lst, i);
					v-> self-> bring_to_front(( Handle) v);
				}
				list_destroy( &lst);
			}
		} /* end set_selected( true); */
	} else
		my-> set_focused( self, false);
	return selected;
}

Handle
Widget_selectedWidget( Handle self, Bool set, Handle widget)
{
	if ( var-> stage > csFrozen) return nilHandle;

	if ( !set) {
		if ( var-> stage <= csNormal) {
			Handle foc = apc_widget_get_focused();
			PWidget  f = ( PWidget) foc;
			while( f) {
				if (( Handle) f == self) return foc;
				f = ( PWidget) f-> owner;
			}
		}
		return nilHandle;

		/* classic solution should be recursive and inheritant call */
		/* of get_selected() here, when Widget would return state of */
		/* child-group selected state until Widget::selected() called; */
		/* thus, each of them would call apc_widget_get_focused - that's expensive, */
		/* so that's the reason not to use classic object model here. */
	}

	if ( widget) {
		if ( PWidget( widget)-> owner == self)
			CWidget( widget)-> set_selected( widget, true);
	} else {
		/* give selection up to hierarchy chain */
		Handle s = self;
		while ( s) {
			if ( CWidget( s)-> get_selectable( s)) {
				CWidget( s)-> set_selected( s, true);
				break;
			}
			s = PWidget( s)-> owner;
		}
	}
	return nilHandle;
}

int
Widget_selectingButtons( Handle self, Bool set, int sb)
{
	if ( !set)
		return var-> selectingButtons;
	return var-> selectingButtons = sb;
}

Handle
Widget_shape( Handle self, Bool set, Handle mask)
{
	if ( var-> stage > csFrozen) return nilHandle;

	if ( !set) {
		if ( apc_widget_get_shape( self, nilHandle)) {
			HV * profile = newHV();
			Handle i = Object_create( "Prima::Image", profile);
			sv_free(( SV *) profile);
			apc_widget_get_shape( self, i);
			--SvREFCNT( SvRV((( PAnyObject) i)-> mate));
			return i;
		} else
			return nilHandle;
	}

	if ( mask && !kind_of( mask, CImage)) {
		warn("Illegal object reference passed to Widget::shape");
		return nilHandle;
	}

	if ( mask && (( PImage( mask)-> type & imBPP) != imbpp1)) {
		Handle i = CImage( mask)-> dup( mask);
		++SvREFCNT( SvRV( PImage( i)-> mate));
		CImage( i)-> set_conversion( i, ictNone);
		CImage( i)-> set_type( i, imBW);
		apc_widget_set_shape( self, i);
		--SvREFCNT( SvRV( PImage( i)-> mate));
		Object_destroy( i);
	} else
		apc_widget_set_shape( self, mask);

	return nilHandle;
}

Bool
Widget_showHint( Handle self, Bool set, Bool showHint )
{
	enter_method;
	Bool oldShowHint = is_opt( optShowHint);
	if ( !set)
		return oldShowHint;
	my-> first_that( self, (void*)showhint_notify, &showHint);
	opt_clear( optOwnerShowHint);
	opt_assign( optShowHint, showHint);
	if ( application && !is_opt( optShowHint) && oldShowHint) my-> set_hintVisible( self, 0);
	return false;
}

Point
Widget_size( Handle self, Bool set, Point size)
{
	if ( !set)
		return apc_widget_get_size( self);
	apc_widget_set_size( self, size.x, size.y);
	return size;
}

Bool
Widget_syncPaint( Handle self, Bool set, Bool syncPaint)
{
	HV * profile;
	enter_method;
	if ( !set)
		return apc_widget_get_sync_paint( self);
	profile = newHV();
	pset_i( syncPaint, syncPaint);
	my-> set( self, profile);
	sv_free(( SV *) profile);
	return false;
}

int
Widget_tabOrder( Handle self, Bool set, int tabOrder)
{
	int count;
	PWidget owner;

	if ( var-> stage > csFrozen) return 0;
	if ( !set)
		return var-> tabOrder;

	owner = ( PWidget) var-> owner;
	count = owner-> widgets. count;
	if ( tabOrder < 0) {
		int i, maxOrder = -1;
		/* finding maximal tabOrder value among the siblings */
		for ( i = 0; i < count; i++) {
			PWidget ctrl = ( PWidget) owner-> widgets. items[ i];
			if ( self == ( Handle) ctrl) continue;
			if ( maxOrder < ctrl-> tabOrder) maxOrder = ctrl-> tabOrder;
		}
		if ( maxOrder < INT_MAX) {
			var-> tabOrder = maxOrder + 1;
			return 0;
		}
		/* maximal value found, but has no use; finding gaps */
		{
			int j = 0;
			Bool match = 1;
			while ( !match) {
				for ( i = 0; i < count; i++) {
					PWidget ctrl = ( PWidget) owner-> widgets. items[ i];
					if ( self == ( Handle) ctrl) continue;
					if ( ctrl-> tabOrder == j) {
						match = 1;
						break;
					}
				}
				j++;
			}
			var-> tabOrder = j - 1;
		}
	} else {
		int i;
		Bool match = 0;
		/* finding exact match among the siblings */
		for ( i = 0; i < count; i++) {
			PWidget ctrl = ( PWidget) owner-> widgets. items[ i];
			if ( self == ( Handle) ctrl) continue;
			if ( ctrl-> tabOrder == tabOrder) {
				match = 1;
				break;
			}
		}
		if ( match)
			/* incrementing all tabOrders that greater than ours */
			for ( i = 0; i < count; i++) {
				PWidget ctrl = ( PWidget) owner-> widgets. items[ i];
				if ( self == ( Handle) ctrl) continue;
				if ( ctrl-> tabOrder >= tabOrder) ctrl-> tabOrder++;
			}
		var-> tabOrder = tabOrder;
	}
	return 0;
}

Bool
Widget_tabStop( Handle self, Bool set, Bool stop)
{
	if ( !set)
		return is_opt( optTabStop);
	opt_assign( optTabStop, stop);
	return false;
}

Bool
Widget_transparent( Handle self, Bool set, Bool transparent)
{
	HV * profile;
	enter_method;
	if ( !set)
		return apc_widget_get_transparent( self);
	profile = newHV();
	pset_i( transparent, transparent);
	my-> set( self, profile);
	sv_free(( SV *) profile);
	return false;
}

SV *
Widget_text( Handle self, Bool set, SV *text)
{
	if ( set) {
		if ( var-> stage > csFrozen) return nilSV;
		if ( var-> text ) SvREFCNT_dec( var-> text );
		var-> text = text;
		if ( var-> text ) SvREFCNT_inc( var-> text );
		return nilSV;
	} else {
		return newSVsv(var->text);
	}
}

int
Widget_top( Handle self, Bool set, int top)
{
	enter_method;
	Point p;
	Rect  r   = my-> get_rect( self);
	if ( !set)
		return r. top;
	p. x = r. left;
	p. y = r. bottom - r. top + top;
	my-> set_origin( self, p);
	return 0;
}

Bool
Widget_visible( Handle self, Bool set, Bool visible)
{
	return set ?
		apc_widget_set_visible( self, visible) :
		apc_widget_is_visible( self);
}

int
Widget_widgetClass( Handle self, Bool set, int widgetClass)
{
	enter_method;
	if ( !set)
		return var-> widgetClass;
	var-> widgetClass = widgetClass;
	my-> repaint( self);
	return 0;
}

/* XS section */
XS( Widget_client_to_screen_FROMPERL)
{
	dXSARGS;
	Handle self;
	int i, count;
	Point * points;

	if (( items % 2) != 1)
		croak ("Invalid usage of Widget::client_to_screen");
	SP -= items;
	self = gimme_the_mate( ST( 0));
	if ( self == nilHandle)
		croak( "Illegal object reference passed to Widget::client_to_screen");
	count  = ( items - 1) / 2;
	if ( !( points = allocn( Point, count))) {
		PUTBACK;
		return;
	}
	for ( i = 0; i < count; i++) {
		points[i]. x = SvIV( ST( i * 2 + 1));
		points[i]. y = SvIV( ST( i * 2 + 2));
	}
	apc_widget_map_points( self, true, count, points);
	EXTEND( sp, count * 2);
	for ( i = 0; i < count; i++) {
		PUSHs( sv_2mortal( newSViv( points[i].x)));
		PUSHs( sv_2mortal( newSViv( points[i].y)));
	}
	PUTBACK;
	free( points);
	return;
}

XS( Widget_screen_to_client_FROMPERL)
{
	dXSARGS;
	Handle self;
	int i, count;
	Point * points;

	if (( items % 2) != 1)
		croak ("Invalid usage of Widget::screen_to_client");
	SP -= items;
	self = gimme_the_mate( ST( 0));
	if ( self == nilHandle)
		croak( "Illegal object reference passed to Widget::screen_to_client");
	count  = ( items - 1) / 2;
	if ( !( points = allocn( Point, count))) {
		PUTBACK;
		return;
	}
	for ( i = 0; i < count; i++) {
		points[i]. x = SvIV( ST( i * 2 + 1));
		points[i]. y = SvIV( ST( i * 2 + 2));
	}
	apc_widget_map_points( self, false, count, points);
	EXTEND( sp, count * 2);
	for ( i = 0; i < count; i++) {
		PUSHs( sv_2mortal( newSViv( points[i].x)));
		PUSHs( sv_2mortal( newSViv( points[i].y)));
	}
	PUTBACK;
	free( points);
	return;
}


XS( Widget_get_widgets_FROMPERL)
{
	dXSARGS;
	Handle self;
	Handle * list;
	int i, count;

	if ( items != 1)
		croak ("Invalid usage of Widget.get_widgets");
	SP -= items;
	self = gimme_the_mate( ST( 0));
	if ( self == nilHandle)
		croak( "Illegal object reference passed to Widget.get_widgets");
	count = var-> widgets. count;
	list  = var-> widgets. items;
	EXTEND( sp, count);
	for ( i = 0; i < count; i++)
		PUSHs( sv_2mortal( newSVsv((( PAnyObject) list[ i])-> mate)));
	PUTBACK;
	return;
}

void Widget_get_widgets          ( Handle self) { warn("Invalid call of Widget::get_widgets"); }
void Widget_get_widgets_REDEFINED( Handle self) { warn("Invalid call of Widget::get_widgets"); }
void Widget_screen_to_client ( Handle self) { warn("Invalid call of Widget::screen_to_client"); }
void Widget_screen_to_client_REDEFINED ( Handle self) { warn("Invalid call of Widget::screen_to_client"); }
void Widget_client_to_screen ( Handle self) { warn("Invalid call of Widget::screen_to_client"); }
void Widget_client_to_screen_REDEFINED ( Handle self) { warn("Invalid call of Widget::screen_to_client"); }

#ifdef __cplusplus
}
#endif
