#include "Macro.h"
#include "CObjectBase.h"
#include "container.h"

extern HINSTANCE hinst;
extern mmachine mm;
extern int OBJNODE;



//CObjectBase::CObjectBase(container *cont,Layer *layer,int xfat,int yfat,int w,int h,int flags,int contflags,int transp):CObjLink( (int (__cdecl *)(CObjLink *)) RedrawObjectBase )
CObjectBase::CObjectBase(container *cont,Layer *layer,int xfat,int yfat,int w,int h,int flags,int contflags,int transp):CObjLink()
{
//***********************************
#if DEBUG_LIB2DBASE
MMechostr (0, "\nCObjectBase::CObjectBase");
#endif
//***********************************

	ObjContainer = cont;
	ObjX=0;
	ObjY=0;
	ObjXfat=xfat;
	ObjYfat=yfat;
	ObjW=w;
	ObjH=h;
	ObjFlags=flags;
	ObjContainerFlags=contflags;
	ObjTransparency=transp;
	ObjLayer=layer;
	ObjToolTip=NULL;
	ObjResourceDest=0;

	if ((ObjFlags&(OBJ_VISIBLE|OBJ_HIDE))==0)
		ObjFlags|=OBJ_VISIBLE;
	if ((ObjFlags&(OBJ_DISABLE|OBJ_ENABLE))==0)
		ObjFlags|=OBJ_ENABLE;
	if ((ObjFlags&(OBJ_LH_FLEX|OBJ_MH_FLEX|OBJ_RH_FLEX))==0)
		ObjFlags|=OBJ_RH_FLEX;
	if ((ObjFlags&(OBJ_LW_FLEX|OBJ_MW_FLEX|OBJ_RW_FLEX))==0)
		ObjFlags|=OBJ_RW_FLEX;

	if (ObjFlags&OBJ_TABSTOP)
		cont->addTabStopObjects(this);
//***********************************
#if DEBUG_LIB2DBASE
MMechostr (0, "\nCObjectBase::CObjectBase end");
#endif
//***********************************
}




CObjectBase::~CObjectBase()
{
//***********************************
#if DEBUG_LIB2DBASE
MMechostr (0, "\nCObjectBase::~CObjectBase");
#endif
//***********************************
//MMechostr(0,"> ~CObjectBase\n");
	deleteLayers(ObjLayer);

//MMechostr(0,">\n");
	if (ObjToolTip!=NULL)
		delete(ObjToolTip);

	if (ObjFlags&OBJ_TABSTOP)
		Container()->remTabStopObjects(this);

//MMechostr(0,"< ~CObjectBase\n");
//***********************************
#if DEBUG_LIB2DBASE
MMechostr (0, "\nCObjectBase::~CObjectBase end");
#endif
//***********************************
}






Rect2D CObjectBase::RectangleIncludingObject()
{
	return (Rect2D (ObjX,ObjY,ObjX+ObjW,ObjY+ObjH));
};




void CObjectBase::ChangeCommonFlags(int newflags,int repaint)
{
//***********************************
#if DEBUG_LIB2DBASE
MMechostr (0, "\nCObjectBase::ChangeCommonFlags");
#endif
//***********************************
	
	int oldFlags=ObjFlags;

	ObjFlags&=((-1)<<(LAST_OBJ_VISI+1));
	ObjFlags |= newflags;

	// evalue 
	if ((oldFlags&OBJ_VISIBLE && newflags&OBJ_HIDE)||(oldFlags&OBJ_ENABLE && newflags&OBJ_DISABLE))
	{
		POINT pt;
		GetCursorPos(&pt);
		ClickOut(pt.x,pt.y,0,0,GetTab(mm,FindObjNodeFromHdlSys(mm,(int)this)),1);
	}

	/* prevent from destruction in user callback */
	container *cont;

	if ((cont=RetrieveContainerFromHdlSys((int)ObjContainer))!=NULL)
	{
		if (cont->CoActiveObject==this || cont->CoFocusedObject==this)
			cont->CoActiveObject=cont->CoFocusedObject=NULL;
		
		cont->RedrawArea(RectangleIncludingObject(),repaint);	
	}
//***********************************
#if DEBUG_LIB2DBASE
MMechostr (0, "\nCObjectBase::ChangeCommonFlags end");
#endif
//***********************************
}





void CObjectBase::Redraw()
{
	Container()->RedrawArea(IntersectionRectangle(Rect2D(0,0,Container()->GetXSize(),Container()->GetYSize()),RectangleIncludingObject()),1);
}

void CObjectBase::RedrawPart(Rect2D rect)
{
	Container()->RedrawArea(rect,1);
}




// fonction evaluant les nouvelles coordonnées et taille d'un objet
// en fonction de la nouvelle taille fournie
void EvalResizeAxis(int flags,int *left,int *middle,int oldw,int w)
{
//***********************************
#if DEBUG_LIB2DBASE
MMechostr (0, "\nEvalResizeAxis");
#endif
//***********************************
	
	
	int sold=oldw;
	int right=oldw-(*left)-(*middle);
	// evaluation des parties fixes
	if ((flags&1)==0) // left fixed
	{
		oldw-=*left;
		w-=*left;
	}
	if ((flags&2)==0) // middle fixed
	{
		oldw-=*middle;
		w-=*middle;
	}
	if ((flags&4)==0) // right fixed
	{
		oldw-=right;
		w-=right;
	}
	
	if (oldw==0)
		oldw=sold;

	// evaluation des parties proportionneles
  //$BB use float for very big container
  float tmpLeft = (float)*left;
  float tmpMiddle = (float)*middle;
  float tmpW = (float)w;
  float tmpOldw = (float)oldw;

	if (flags&1) // left flexible
  {
		*left=tmpLeft*tmpW/tmpOldw;
  }
	if (flags&2) // middle flexible
  {
		*middle=tmpMiddle*tmpW/tmpOldw;
  }

	if (*middle<=0) *middle=0;

//***********************************
#if DEBUG_LIB2DBASE
MMechostr (0, "\nEvalResizeAxis end");
#endif
//***********************************
}





void CObjectBase::EvalCoordsResize(int w,int h)
{

//***********************************
#if DEBUG_LIB2DBASE
MMechostr (0, "\nCObjectBase::EvalCoordsResize");
#endif
//***********************************

	int flags=ObjFlags>>(LAST_OBJ_COM+1);
	ObjX=ObjXi;
	ObjY=ObjYi;
	ObjW=ObjWi;
	ObjH=ObjHi;
	EvalResizeAxis(flags,&ObjX,&ObjW,ObjContWi,w);
	EvalResizeAxis(flags>>3,&ObjY,&ObjH,ObjContHi,h);

//***********************************
#if DEBUG_LIB2DBASE
MMechostr (0, "\nCObjectBase::EvalCoordsResize end");
#endif
//***********************************
}





void CObjectBase::SetIniResizeCoords()
{

//***********************************
#if DEBUG_LIB2DBASE
MMechostr (0, "\nCObjectBase::SetIniResizeCoords");
#endif
//***********************************
	ObjXi=ObjX;
	ObjYi=ObjY;
	ObjWi=ObjW;
	ObjHi=ObjH;
	ObjContWi=ObjContainer->GetXSize();
	ObjContHi=ObjContainer->GetYSize();

//***********************************
#if DEBUG_LIB2DBASE
MMechostr (0, "\nCObjectBase::SetIniResizeCoords end");
#endif
//***********************************
}





int CObjectBase::ResizeCont(int neww,int newh,int reflex)
{
//***********************************
#if DEBUG_LIB2DBASE
MMechostr (0, "\nCObjectBase::ResizeCont");
#endif
//***********************************

	int w=ObjW;
	int h=ObjH;
	int x=ObjX;
	int y=ObjY;
	// evaluation de la nouvelle taille et coordonnées
	EvalCoordsResize(neww,newh);
	
	// mise à jour des coordonées par rapport au pere
	int p_node=FindObjNodeFromHdlSys(mm,(int)this);
	if (x!=ObjX || y!=ObjY)
	{
		int p_nodefather=GetFather(mm,p_node);
		if (p_nodefather==NIL)
		{
			ObjXfat=ObjX;
			ObjYfat=ObjY;
		}
		else
		{
			CObjectBase *co_father=GetObjectBase(mm,p_nodefather);
			ObjXfat=ObjX-co_father->ObjX;
			ObjYfat=ObjY-co_father->ObjY;
		}
	}

	// redimensionnement de la ressource
	if (w!=ObjW || h!=ObjH)
	{
		DestroyAllLayers();
		if (ObjW!=0 && ObjH!=0)
			ResizeLayer(ObjW,ObjH,GetTab(mm,p_node));
		
		// execution de la callback de redimensionnement de l'objet
		int tmp_res;
		if (reflex && OBJbeginreflex(mm,OBJNODE,(int)this,RFLOBJNODE_RESIZE)==0)
		{
			CHECK(MMpush(mm,ITOM(ObjW)));
			CHECK(MMpush(mm,ITOM(ObjH)));
			return OBJcallreflex(mm,2);
		}
	}


//***********************************
#if DEBUG_LIB2DBASE
MMechostr (0, "\nCObjectBase::ResizeCont end");
#endif
//***********************************

	return 0;
}





int CObjectBase::Resize(int w,int h,int repaint)
{
//***********************************
#if DEBUG_LIB2DBASE
MMechostr (0, "\nCObjectBase::Resize");
#endif
//***********************************

	if (w!=ObjW || h!=ObjH)
	{
		Rect2D oldrect=RectangleIncludingObject();
		ObjWi=ObjW=w;
		ObjHi=ObjH=h;
	
		DestroyAllLayers();
		if (ObjW!=0 && ObjH!=0)
			ResizeLayer(ObjW,ObjH,GetTab(mm,FindObjNodeFromHdlSys(mm,(int)this)));
		
		/* prevent from destruction in user callback */
		container *cont;
		if ((cont=RetrieveContainerFromHdlSys((int)ObjContainer))!=NULL)
			cont->RedrawArea(oldrect,repaint);	
		
	}

//***********************************
#if DEBUG_LIB2DBASE
MMechostr (0, "\nCObjectBase::Resize end");
#endif
//***********************************

	return 0;

}



void CObjectBase::SetFocus()
{
//***********************************
#if DEBUG_LIB2DBASE
MMechostr (0, "\nCObjectBase::SetFocus");
#endif
//***********************************

	MMechostr(1,"CObjectBase::SetFocus\n");
	Container()->SetFocus(this);

//***********************************
#if DEBUG_LIB2DBASE
MMechostr (0, "\nCObjectBase::SetFocus end");
#endif
//***********************************
}





int CObjectBase::ChangeResource(mmachine m,int ndx,int p_new)
{
//***********************************
#if DEBUG_LIB2DBASE
MMechostr (0, "\nCObjectBase::ChangeResource");
#endif
//***********************************

	int tmp_res;
	int p_obj=FindObjNodeFromHdlSys(mm,(int)this);
	int p_old=MTOP(MMfetch(mm,GetTab(mm,p_obj),ndx));
	if (p_new!=p_old && ObjResourceDest)
	{
		// on epile l'objet et la resource
		CHECK(MMpush(m,PTOM(p_obj)));
		CHECK(MMpush(m,PTOM(p_new)));
		CHECK(dsAlphaBitmap(mm,PTOM(p_old)));
		p_new=MTOP(MMpull(m));
		p_obj=MTOP(MMpull(m));
	}
	ObjResourceDest=1;
	// remplacement de la valeur du pointeur de l'alphabitmap
	// dans le tab
	MMstore(mm,GetTab(mm,p_obj),ndx,PTOM(p_new));		
//***********************************
#if DEBUG_LIB2DBASE
MMechostr (0, "\nCObjectBase::ChangeResource end");
#endif
//***********************************
	return 0;
}






/******************************************************************************/
/*                     OPERATIONS SUR L'OBJNODE                               */
/******************************************************************************/

#define FATHER		0
#define SON			1
#define BROTHER		2
#define VALUE		3
#define TAB			4

#define BMP			0
#define ABMP		1







/**************************************************************************/
/*                    OPERATIONS PRIMAIRES SUR l'OBJNODE                  */
/**************************************************************************/
// creation du root
// les deux parametres en entree sont des pointeurs et non des mots scols
// obj  : pointeur vers le CObjectBase
int CrRoot(mmachine m,CObjectBase *obj,int ndxchannel)
{
	int p_node_add,p_base,tmp_res;

	
//***********************************
#if DEBUG_LIB2DBASE
MMechostr (0, "\nCrRoot");
#endif
//***********************************

	// on recopie le canal dans la pile
	CHECK(MMpush( m, MMget(m,ndxchannel)));
	
	// allocation du buffer contenant le pointeur vers l'objet
	if ((p_base=MMmalloc(m,1,TYPEBUF))<0) return p_base;
	MMstore(m,p_base,0,(int)obj);
	CHECK(MMpush( m, PTOM(p_base)));
	
	// creation du node
	if ((p_node_add = MMmalloc( m, 5, TYPETAB ))<0) return p_node_add;
	MMstore( m, p_node_add, FATHER  , NIL         );
	MMstore( m, p_node_add, SON     , NIL         );
	MMstore( m, p_node_add, BROTHER , NIL		  );
	MMstore( m, p_node_add, VALUE   , MMpull(m)   );
	MMstore( m, p_node_add, TAB		, NIL		  );
	
	CHECK(MMpush( m, PTOM(p_node_add)));
	CHECK(OBJcreate(m,OBJNODE,(int)obj,0,-1));
#if DEBUG_OBJNODE
	MMechostr(MSKTRACE,"Root:%d\n",MMget(m,0));
#endif
//***********************************
#if DEBUG_LIB2DBASE
MMechostr (0, "\nCrRoot end");
#endif
//***********************************

	return 0;
}



// Calcule les coordonées globale de l'OBJNODE node par rapport au container
int ComputeCoordinates(mmachine m,int node,va_list l)
{
//***********************************
#if DEBUG_LIB2DBASE
MMechostr (0, "\nComputeCoordinates");
#endif
//***********************************
	if (node!=NIL)
	{
		int p_nodefather=GetFather(m,node);
		CObjectBase * co_node=GetObjectBase(m,node);
		if (p_nodefather==NIL)
		{
			co_node->ObjX=co_node->ObjXfat;
			co_node->ObjY=co_node->ObjYfat;
		}
		else
		{
			CObjectBase *co_father=GetObjectBase(m,p_nodefather);
			co_node->ObjX=co_father->ObjX+co_node->ObjXfat;
			co_node->ObjY=co_father->ObjY+co_node->ObjYfat;
		}
		co_node->SetIniResizeCoords();
	}

//***********************************
#if DEBUG_LIB2DBASE
MMechostr (0, "\nComputeCoordinates end");
#endif
//***********************************
	return node;
}

// ajoute un node à un node pere
// si father==NULL alors ajoute le node au root
// les trois parametres en entree sont des pointeurs et non des mots scols
int AddNode(mmachine m,CObjectBase *root,CObjectBase * father,CObjectBase * obj, int nbparam)
{
	int p_node_add,p_base,tmp_res;
	int p_nodepere,p_lastson;

//***********************************
#if DEBUG_LIB2DBASE
MMechostr (0, "\nAddNode");
#endif
//***********************************

	if ( nbparam != 0 )
	{
		CHECK(MMpush(m,ITOM(nbparam)));
		CHECK(MBdeftab(m));
	}
	else
	{
		CHECK(MMpush(m,ITOM(NIL)));
	}

	// allocation du buffer contenant le pointeur vers l'objet
	if ((p_base=MMmalloc(m,1,TYPEBUF))<0) return p_base;
	MMstore(m,p_base,0,(int)obj);
	CHECK(MMpush( m, PTOM(p_base)));
	
	// creation du node
	if ((p_node_add = MMmalloc( m, 5, TYPETAB ))<0) return p_node_add;
	MMstore( m, p_node_add, FATHER , NIL			);
	MMstore( m, p_node_add, SON	   , NIL            );
	MMstore( m, p_node_add, BROTHER, NIL			);
	tmp_res = MMpull( m );
	MMstore( m, p_node_add, VALUE  , tmp_res        );
	tmp_res = MMpull( m );
	MMstore( m, p_node_add, TAB    , tmp_res		);
	
	// creation de l'objet
	CHECK(MMpush( m, PTOM(p_node_add)));
	CHECK(OBJcreate(m,OBJNODE,(int)obj,OBJNODE,(father==NULL)?(int)root:(int)father));
	
	// on recupere l'ObjNode du pere et du noeud à ajouter
	p_node_add=MTOP(MMget(m,0));
	p_nodepere=FindObjNodeFromHdlSys(m,(father==NULL)?(int)root:(int)father);

	//on recherche le dernier fils du pere et on ajoute le nouveau node
	if ((p_lastson=GetSon(m,p_nodepere))==NIL)
		MMstore( m, p_nodepere, SON, PTOM(p_node_add) );
	else
	{
		int find=0;
		while (!find)
		{
			if (GetBrother(m,p_lastson)==NIL) find=1;
			else p_lastson=GetBrother(m,p_lastson);
		}
		MMstore( m, p_lastson, BROTHER , PTOM(p_node_add) );
	}
	MMstore( m, p_node_add, FATHER , PTOM(p_nodepere));	
	ApplyOnTree(0,m,FindObjNodeFromHdlSys(m,(int)root),&ComputeCoordinates);

//***********************************
#if DEBUG_LIB2DBASE
MMechostr (0, "\nAddNode end");
#endif
//***********************************

	return 0;
}





// detruit un node
// p_obj: pointeur de l'objet à détruire (OBJNODE)
int DsNode(mmachine m,int p_obj)
{
	
	CObjectBase *obj=GetObjectBase(m,p_obj);
//***********************************
#if DEBUG_LIB2DBASE
MMechostr (0, "\nDsNode");
#endif
//***********************************
#if DEBUG_OBJNODE
	MMechostr(MSKTRACE,"Destruction node principale:%d %d\n",p_obj,(int)obj); 
#endif
	if ((p_obj==NIL)||(obj==NULL)) return NIL;
	else
	{
		// destruction du tooltip 
		if (obj->ObjToolTip!=NULL)
		{
			delete(obj->ObjToolTip);
			obj->ObjToolTip=NULL;
		}
		OBJdelTH(m,OBJNODE,(int)obj);
	}

//***********************************
#if DEBUG_LIB2DBASE
MMechostr (0, "\nDsNode end");
#endif
//***********************************
	return 0;
}





int DsNode2(mmachine m,int handsys,int objm)
{
//***********************************
#if DEBUG_LIB2DBASE
MMechostr (0, "\nDsNode2");
#endif
//***********************************

	CObjectBase * obj=(CObjectBase *)handsys;
	container *cont=obj->Container();
	int p_obj=MTOP(objm);
	
#if DEBUG_OBJNODE
	MMechostr(MSKTRACE,"Destruction node secondaire:%d %d\n",p_obj,handsys); 
#endif

	// mise à jour des objets handlés par le container
	if (cont->CoHandledObject==obj)
		cont->CoHandledObject=NULL;
	if (cont->CoFocusedObject==obj)
		cont->CoFocusedObject=NULL;
	if (cont->CoActiveObject==obj)
		cont->CoActiveObject=NULL;
	if (cont->CoActiveToolTipObject==obj)
		cont->CoActiveToolTipObject=NULL;
	if (cont->CoNewHandledObject==obj)
		cont->CoNewHandledObject=NULL;

	// mise à jour de l'arborescence et de la pile scol
	int father=MTOP(MMfetch(m,p_obj,FATHER));
	if (father!=NIL)
	{
		if (MTOP(MMfetch(m,father,SON))==p_obj)
			MMstore(m,father,SON,MMfetch(m,p_obj, BROTHER));
		else
		{
			int tmp;
			int prev_brother = MTOP(MMfetch(m,father,SON));
			while ( (tmp=MTOP(MMfetch(m,prev_brother,BROTHER))) != p_obj )
				prev_brother = tmp;
			MMstore (m,prev_brother,BROTHER,MMfetch(m, p_obj, BROTHER ) );
		}
	}
	int son=MTOP(MMfetch(m,p_obj,SON));
	while (son!=NIL)
	{
		MMstore(m,son,FATHER,NIL);
		son=MTOP(MMfetch(m,son,BROTHER));
	}
	
	delete(obj);
	

	// destruction de l'objet au niveau de scol
	MMstore(m,p_obj,FATHER,NIL);
	MMstore(m,p_obj,BROTHER,NIL);
	MMstore(m,p_obj,SON,NIL);
	MMstore(m,p_obj,TAB,NIL);
	MMstore(m,p_obj,VALUE,NIL);


//***********************************
#if DEBUG_LIB2DBASE
MMechostr (0, "\nDsNode2 end");
#endif
//***********************************
	return 0;
}





void ApplyOnTreeArgs(mmachine m,int node,int (*func)(mmachine,int,va_list),va_list l)
{

//***********************************
#if DEBUG_LIB2DBASE
MMechostr (0, "\nApplyOnTreeArgs");
#endif
//***********************************
	if (node!=NIL)
	{
		(*func)(m,node,l);
		ApplyOnTreeArgs(m,GetSon(m,node),func,l);
		ApplyOnTreeArgs(m,GetBrother(m,node),func,l);
	}
//***********************************
#if DEBUG_LIB2DBASE
MMechostr (0, "\nApplyOnTreeArgs end");
#endif
//***********************************
}




void ApplyOnTreeArgsGarbage(mmachine m,int node,int (*func)(mmachine,int,va_list),va_list l)
{
//***********************************
#if DEBUG_LIB2DBASE
MMechostr (0, "\nApplyOnTreeArgsGarbage");
#endif
//***********************************
	if (node!=NIL)
	{
		CObjectBase *obj=GetObjectBase(m,node);
		
		(*func)(m,node,l);
		
		// possibilité de destruction du container et/ou des objets
		// on doit recuperer le node
		node=FindObjNodeFromHdlSys(m,(int)obj);
		ApplyOnTreeArgsGarbage(m,GetSon(m,node),func,l);
		
		// possibilité de destruction du container et/ou des objets
		// on doit recuperer le node
		node=FindObjNodeFromHdlSys(m,(int)obj);
		ApplyOnTreeArgsGarbage(m,GetBrother(m,node),func,l);
	}
//***********************************
#if DEBUG_LIB2DBASE
MMechostr (0, "\nApplyOnTreeArgsGarbage end");
#endif
//***********************************
}




// applique la fonction func sur tous les nodes 
void ApplyOnTree(int garb,mmachine m,int node,int (*func)(mmachine,int,va_list),...)
{
//***********************************
#if DEBUG_LIB2DBASE
MMechostr (0, "\nApplyOnTree");
#endif
//***********************************
	if (node!=NIL)
	{
		va_list l;
		va_start(l,func);
		if (garb)
			ApplyOnTreeArgsGarbage(m,node,func,l);
		else
			ApplyOnTreeArgs(m,node,func,l);
		va_end(l);
	}
//***********************************
#if DEBUG_LIB2DBASE
MMechostr (0, "\nApplyOnTree end");
#endif
//***********************************
}
 



// fonction redimensionnant un node en fonction de
// la nouvelle taille du container passé dans le
// va_list
int ComputeResize(mmachine m,int node,va_list l)
{
//***********************************
#if DEBUG_LIB2DBASE
MMechostr (0, "\nComputeResize");
#endif
//***********************************	
	
	int neww=va_arg(l,int);
	int newh=va_arg(l,int);
	CObjectBase *obj=GetObjectBase(m,node);
	if (obj!=NULL)
		obj->ResizeCont(neww,newh,1);


//***********************************
#if DEBUG_LIB2DBASE
MMechostr (0, "\nComputeResize end");
#endif
//***********************************
	return node;
}
	


	
// recupere le pere
int GetFather(mmachine m,int p_obj)
{
//***********************************
#if DEBUG_LIB2DBASE
MMechostr (0, "\nGetFather");
#endif
//***********************************

	if ( p_obj == NIL )
		return NIL;
	else
		return MTOP(MMfetch(m,p_obj,FATHER));
}



// recupere le frere
int GetBrother(mmachine m,int p_obj)
{
//***********************************
#if DEBUG_LIB2DBASE
MMechostr (0, "\nGetBrother");
#endif
//***********************************

	if ( p_obj == NIL )
		return NIL;
	else
		return MTOP(MMfetch(m,p_obj,BROTHER));
}




// recupere le fils
int GetSon(mmachine m,int p_obj)
{
//***********************************
#if DEBUG_LIB2DBASE
MMechostr (0, "\nGetSon");
#endif
//***********************************

	if ( p_obj == NIL )
		return NIL;
	else
		return MTOP(MMfetch(m,p_obj,SON));
}




// recupere le frere precedent
int GetPreviousBrother(mmachine m, int p_obj)
{
	int p_current;
	
//***********************************
#if DEBUG_LIB2DBASE
MMechostr (0, "\nGetPreviousBrother");
#endif
//***********************************

	if ( p_obj == NIL )
		return NIL;
	else
	{
		p_current = GetSon( m, GetFather( m, p_obj ) );

		while ( p_current != NIL && GetBrother( m, p_current ) != p_obj )
			p_current = GetBrother( m, p_current );

		return p_current;
	}
}

// recupere le dernier frere
int GetLastBrother(mmachine m, int p_obj )
{
//***********************************
#if DEBUG_LIB2DBASE
MMechostr (0, "\nGetLastBrother");
#endif
//***********************************

	if ( p_obj == NIL )
		return NIL;
	else
	{
		while ( GetBrother( m, p_obj ) != NIL )
			p_obj = GetBrother( m, p_obj );

		return p_obj;
	}
}




// recupere la valeur
CObjectBase *GetObjectBase(mmachine m,int p_obj)
{
	int p_value;

//***********************************
#if DEBUG_LIB2DBASE
MMechostr (0, "\nGetObjectBase");
#endif
//***********************************

	if ( p_obj == NIL || (p_value=MMfetch(m,p_obj,VALUE)) == NIL )
		return NULL;
	else
		/*
		return (CObjectBase *)MMfetch(
											m,
											MTOP(MMfetch(m,p_obj,VALUE)),
											0
							         );*/
	    return (CObjectBase *)MMfetch(
											m,
											MTOP(p_value),
											0
									);
		
}



int GetTab(mmachine m,int p_obj)
{
	if ( p_obj == NIL )
		return NIL;
	else
		return MTOP(MMfetch(m,p_obj,TAB));
}

int  GetResizeFlags(int flags)
{
	return ((flags>>(LAST_OBJ_COM+1))&(~((-1)<<(LAST_OBJ_COM+1))))<<(LAST_OBJ_COM+1);
}


int _CHANGEobjNodeFlags(mmachine m)
{
	int p_obj,newflags,repaint;
	CObjectBase * obj;

//***********************************
#if DEBUG_LIB2DBASE
MMechostr (0, "\n_CHANGEobjNodeFlags");
#endif
//***********************************


	if ((p_obj=MTOP(MMget(m,2)))==NIL)
	{
		MMpull(m);
		MMpull(m);
		MMechostr(MSKTRACE,"_CHANGEobjNodeFlags: ObjNode is nil !\n");
	}
	else
	{	
		if ((obj=GetObjectBase(m,p_obj))==NULL)
		{
			MMpull(m);
			MMpull(m);
			MMechostr(MSKTRACE,"_CHANGEobjNodeFlags: ObjNode already destroyed !\n");
		}
		else
		{
			int decal,t;
			// doit-on repeindre l'objet
			repaint=MTOI(MMpull(m));

			// calcul des nouveaux flags du node
			// 15 = 2^0 + 2^1 + 2^2 + 2^3 = 1 + 2 + 4 + 8
			// 31 = 2^0 + 2^1 + 2^2 + 2^3 +2^4 = 1 + 2 + 4 + 8 + 16
			decal = ((1<<(LAST_OBJ_FLAG+1))-1);
			// pour ne pas perdre les flags specifiques a l'objet
			// si les nouveaux flags vallent nil -> objet en enable/visible
			newflags = (
							(t=MTOI(MMpull(m)))==NIL?OBJ_ENABLE|OBJ_VISIBLE:t 
					   )
					   & decal;
			
			obj->ChangeCommonFlags(newflags,repaint);

		}
	}

//***********************************
#if DEBUG_LIB2DBASE
MMechostr (0, "\n_CHANGEobjNodeFlags end");
#endif
//***********************************

	return 0;
}




//$BLG - v5.2.06: Add (Added before but forgot to document it)
int _GETobjNodeFlags(mmachine m)
{
	int p_obj,tmp_res;

//***********************************
#if DEBUG_LIB2DBASE
MMechostr (0, "\n_GETobjNodeFlags");
#endif
//***********************************

	if ((p_obj=MTOP(MMpull(m)))==NIL) return MMpush(m,NIL);
	else
	{
		// on recupere l'objet
		CObjectBase * obj;
		if ((obj=GetObjectBase(m,p_obj))==NULL)
		{
			MMechostr(MSKTRACE,"_GETobjNodeFlags: ObjNode already destoyed\n");
			MMpush(m,NIL);
		}
		else
			CHECK(MMpush(m,ITOM(obj->getFlags()&
								(OBJ_ENABLE|OBJ_DISABLE|OBJ_VISIBLE|OBJ_HIDE)
							   )
						));
	}

//***********************************
#if DEBUG_LIB2DBASE
MMechostr (0, "\n_GETobjNodeFlags end");
#endif
//***********************************

	return 0;
}




int _CHANGEobjNodeCoordinates(mmachine m)
{
	int p_obj,p_coord,repaint;
	CObjectBase * obj;

	//***********************************
#if DEBUG_LIB2DBASE
MMechostr (0, "\n_CHANGEobjNodeCoordinates");
#endif
//***********************************


	if ((p_obj=MTOP(MMget(m,2)))==NIL)
	{
		MMpull(m);
		MMpull(m);
		MMechostr(MSKTRACE,"_CHANGEobjNodeCoordinates: ObjNode is nil !\n");
	}
	else
	{	
		// on recupere l'objet
		if ((obj=GetObjectBase(m,p_obj))==NULL)
		{
			MMpull(m);
			MMpull(m);
			MMechostr(MSKTRACE,"_CHANGEobjNodeCoordinates: ObjNode is already destroyed.\n");
		}
		else
		{
		
			repaint=MTOI(MMpull(m));
			if ((p_coord=MTOP(MMpull(m)))==NIL)
			{
				obj->ObjXfat=0;
				obj->ObjYfat=0;
			}
			else
			{
				obj->ObjXfat=MTOI(MMfetch(m,p_coord,0));
				obj->ObjYfat=MTOI(MMfetch(m,p_coord,1));
			}
			ComputeCoordinates(m,p_obj,NULL);
			ApplyOnTree(0,m,GetSon(m,p_obj),&ComputeCoordinates);

			// on cherche si un objet est sous la souris

			
			if (repaint)
				obj->Container()->Redraw(1);
		}
	}

//***********************************
#if DEBUG_LIB2DBASE
MMechostr (0, "\n_CHANGEobjNodeCoordinates");
#endif
//***********************************

	return 0;
}




int _GETobjNodePositionSizeInFatherRef(mmachine m)
{

//***********************************
#if DEBUG_LIB2DBASE
MMechostr (0, "\n_GETobjNodePositionSizeInFatherRef");
#endif
//***********************************

	int p_obj,tmp_res;
	if ((p_obj=MTOP(MMpull(m)))==NIL) return MMpush(m,NIL);
	else
	{
		CObjectBase * obj;
		// on recupere l'objet
		if ((obj=GetObjectBase(m,p_obj))==NULL)
		{
			MMechostr(MSKTRACE,"_GETobjNodePositionSizeInFatherRef: ObjNode already destroyed\n");
			CHECK(MMpush(m,NIL));
		}
		else
		{
			CHECK(MMpush(m,ITOM(obj->ObjXfat)));
			CHECK(MMpush(m,ITOM(obj->ObjYfat)));
			CHECK(MMpush(m,ITOM(obj->ObjW)));
			CHECK(MMpush(m,ITOM(obj->ObjH)));
			CHECK(MMpush(m,ITOM(4)));
			CHECK(MBdeftab(m));
		}
	}

//***********************************
#if DEBUG_LIB2DBASE
MMechostr (0, "\n_GETobjNodePositionSizeInFatherRef end");
#endif
//***********************************

	return 0;
}





int _SIZEobjNode(mmachine m)
{
	int w,h,p_obj,repaint;

	//***********************************
#if DEBUG_LIB2DBASE
MMechostr (0, "\n_SIZEobjNode");
#endif
//***********************************


	if ((repaint=MTOI(MMpull(m)))==NIL) repaint=0;
	if ((h=MTOI(MMpull(m)))==NIL) h=0;
	if ((w=MTOI(MMpull(m)))==NIL) w=0;
	if ((p_obj=MTOP(MMget(m,0)))==NIL)
	{
		MMechostr(MSKTRACE,"_SIZEobjNode: ObjNode is nil !\n");
		return 0;
	}
	else
	{
		CObjectBase * obj;
		// on recupere l'objet
		if ((obj=GetObjectBase(m,p_obj))==NULL)
			MMechostr(MSKTRACE,"_SIZEobjNode: ObjNode already destroyed\n");
		else
			obj->Resize(w,h,repaint);
	}

//***********************************
#if DEBUG_LIB2DBASE
MMechostr (0, "\n_SIZEobjNode end");
#endif
//***********************************

	return 0;
}





int _GETobjNodePositionSizeInContainerRef(mmachine m)
{
	int p_obj,tmp_res;

//***********************************
#if DEBUG_LIB2DBASE
MMechostr (0, "\n_GETobjNodePositionSizeInContainerRef");
#endif
//***********************************

	if ((p_obj=MTOP(MMpull(m)))==NIL) return MMpush(m,NIL);
	else
	{
		// on recupere l'objet
		CObjectBase * obj;
		if ((obj=GetObjectBase(m,p_obj))==NULL)
		{
			MMechostr(MSKTRACE,"_GETobjNodePositionSizeInContainerRef: ObjNode already destoyed\n");
			MMpush(m,NIL);
		}
		else
		{
			CHECK(MMpush(m,ITOM(obj->ObjX)));
			CHECK(MMpush(m,ITOM(obj->ObjY)));
			CHECK(MMpush(m,ITOM(obj->ObjW)));
			CHECK(MMpush(m,ITOM(obj->ObjH)));
			CHECK(MMpush(m,ITOM(4)));
			CHECK(MBdeftab(m));
		}
	}

//***********************************
#if DEBUG_LIB2DBASE
MMechostr (0, "\n_GETobjNodePositionSizeInContainerRef end");
#endif
//***********************************

	return 0;
}




int _PAINTobjNode( mmachine m )
{
	int p_obj;

//***********************************
#if DEBUG_LIB2DBASE
MMechostr (0, "\n_PAINTobjNode");
#endif
//***********************************

	if ((p_obj=MTOP(MMget(m,0)))==NIL) return 0;
	else
	{
		// on recupere l'objet
		CObjectBase * obj;
		if ((obj=GetObjectBase(m,p_obj))==NULL)
		{
			MMechostr(MSKTRACE,"_PAINTobjNode: ObjNode already destoyed\n");
			MMset(m,0,NIL);
		}
		else
			obj->Redraw();
	}

//***********************************
#if DEBUG_LIB2DBASE
MMechostr (0, "\n_PAINTobjNode end");
#endif
//***********************************

	return 0;
}




/* place un ObjNode en tant que dernier fils */
int _TOPobjNode(mmachine m)
{
	int p_obj;

//***********************************
#if DEBUG_LIB2DBASE
MMechostr (0, "\n_TOPobjNode");
#endif
//***********************************

	if ((p_obj=MTOP(MMget(m,0)))==NIL) return 0;
	else
	{
		int p_next = GetBrother( m, p_obj );

		if ( p_next != NIL ) // le noeud a au moins un frere, sinon le travail est deja fait
		{
			int p_father = GetFather( m, p_obj );

			if ( GetSon( m, p_father ) == p_obj ) // le noeud est en premier fils
				MMstore( m, p_father, SON, PTOM( p_next ) );
			else
				MMstore( m, GetPreviousBrother( m, p_obj ), BROTHER, PTOM( p_next ) );
			MMstore( m, GetLastBrother( m, p_obj ), BROTHER, PTOM( p_obj ) );
			MMstore( m, p_obj, BROTHER, NIL );
		}
	}


//***********************************
#if DEBUG_LIB2DBASE
MMechostr (0, "\n_TOPobjNode end");
#endif
//***********************************

	return 0;
}




int _SETobjNodeFocus(mmachine m)
{
	int p_obj;

//***********************************
#if DEBUG_LIB2DBASE
MMechostr (0, "\n_SETobjNodeFocus");
#endif
//***********************************

	if ((p_obj=MTOP(MMget(m,0)))==NIL) return 0;
	else
	{
		// on recupere l'objet
		CObjectBase * obj;
		if ((obj=GetObjectBase(m,p_obj))==NULL)
		{
			MMechostr(MSKTRACE,"_SETobjNodeFocus: ObjNode already destoyed\n");
			MMset(m,0,NIL);
		}
		else
			obj->SetFocus();
	}

//***********************************
#if DEBUG_LIB2DBASE
MMechostr (0, "\n_SETobjNodeFocus end");
#endif
//***********************************

	return 0;
}




int _SETobjNodeTabStopOrder(mmachine m)
{
	int p_cont;
	container * cont;

//***********************************
#if DEBUG_LIB2DBASE
MMechostr (0, "\n_SETobjNodeTabStopOrder");
#endif
//***********************************

	if ((p_cont=MMget(m,1))==NIL)
	{
		m->pp+=1;
		MMset(m,0,ITOM(0));
		return 0;
	}
	if ((cont=RetrievePtrContainer(m,MTOP(p_cont)))==NULL)
	{		
		MMechostr(MSKTRACE,"_SETobjNodeTabStopOrder: container already destroyed\n");
		m->pp+=1;
		MMset(m,0,ITOM(0));
		return 0;
	}
	// on reset le tableau des tabsstop
	cont->resetTabStop();
	
	// on ajoute les nouveaux objets au tableau des tabstops
	int p_listobj,p_obj;
	CObjectBase* obj;
	if ((p_listobj=MTOP(MMpull(m)))!=NIL)
		do
		{
			
			if ((p_obj=MTOP(MMfetch(m,p_listobj,0)))==NIL)
				MMechostr(1,"_SETobjNodeTabStopOrder: ObjNode is nil\n");
			else if ((obj=GetObjectBase(m,p_obj))==NULL)
				MMechostr(1,"_SETobjNodeTabStopOrder: ObjNode already destroyed\n");
			else if (!cont->OwnsObject(obj))
				MMechostr(1,"_SETobjNodeTabStopOrder: ObjNode was not created in this container\n");
			else
				cont->addTabStopObjects(obj);
		}
		while ((p_listobj=MTOP(MMfetch(m,p_listobj,1)))!=NIL);
	MMset(m,0,ITOM(1));

//***********************************
#if DEBUG_LIB2DBASE
MMechostr (0, "\n_SETobjNodeTabStopOrder end");
#endif
//***********************************

	return 0;
}






/**************************************************************************/
/*                   OPERATIONS EVOLUEE SUR l'OBJNODE                     */
/**************************************************************************/

// recupere l'OBJNODE dont le handler system est hdlsys
int FindObjNodeFromHdlSys(mmachine m,int hdlsys)
{
	int p;
	if ((p = OBJfindTH( mm, OBJNODE,hdlsys ))==NIL) return NIL;
	return  MTOP(MMfetch(m,p,OFFOBJMAG ) );
}

// Renvoie l'OBJNODE en dessous de la souris
int Search_ObjNode_Under_Mouse(mmachine m,int p_objnode,int MouseCol,int MouseLgn)
{
	int res;

//***********************************
#if DEBUG_LIB2DBASE
MMechostr (0, "\nSearch_ObjNode_Under_Mouse");
#endif
//***********************************

	if (p_objnode==NIL) res=NIL;
	else
	{
		int BrotherResult=Search_ObjNode_Under_Mouse(m,GetBrother(m,p_objnode),MouseCol,MouseLgn);
		if (BrotherResult==NIL)
		{
			CObjectBase * obj=GetObjectBase(m,p_objnode);
			int ChildResult=((obj!=NULL)&&(obj->IsObjectVisible()))?Search_ObjNode_Under_Mouse(m,GetSon(m,p_objnode),MouseCol,MouseLgn):NIL;
			if (ChildResult==NIL)
			{
				if ((obj!=NULL) &&
					( IsPointInRectangle(Point2D(MouseCol,MouseLgn),obj->RectangleIncludingObject()) )
				   )
				{
					int p_tab=GetTab(m,p_objnode);
					
					if ((p_tab!=NIL) &&
						(obj->IsObjectVisible())&&
						(obj->IsMouseOnObject(MouseCol, MouseLgn, p_tab)) )
						res=p_objnode;
					else
						res=NIL;

				}
				else
					res=NIL;
			}
			else
				res=ChildResult;
		}
		else
			res=BrotherResult;
	}

//***********************************
#if DEBUG_LIB2DBASE
MMechostr (0, "\nSearch_ObjNode_Under_Mouse end");
#endif
//***********************************

	return res;
}




// Redessine tous les OBJNODE de l'arbre dans le buffer offscreen
void PaintObjNodes(mmachine m,CObjBuffer *blit,int node)
{

//***********************************
#if DEBUG_LIB2DBASE
MMechostr (0, "\nPaintObjNodes");
#endif
//***********************************

  if (node!=NIL)
  {
    CObjectBase * o=GetObjectBase(m,node);
	if (o->IsObjectVisible())
	{
		Layer *source;
		int p_tab;
		if ((p_tab=GetTab(m,node))!=NIL)
		{
			source = o->GetLayer(m, p_tab);

			// on blit dans le buffer offscreen
			while ( source != NULL )
			{
				blit->Blit(o->ObjX+source->Xdecal,o->ObjY+source->Ydecal,source);
				source = source->nextLayer();
			}
		}
		PaintObjNodes(m,blit,GetSon(m,node));
	}
	PaintObjNodes(m,blit,GetBrother(m,node));
  }

//***********************************
#if DEBUG_LIB2DBASE
MMechostr (0, "\nPaintObjNodes end");
#endif
//***********************************

}




// Redessine tous les objets node de l'arbre dans une zone du buffer offscreen
void PaintPartObjNodes(mmachine m,PtrObjBuffer blit,int node,Rect2D area2redraw)
{
	Rect2D rectintersect,partobject2redraw;

//***********************************
#if DEBUG_LIB2DBASE
MMechostr (0, "\nPaintPartObjNodes");
#endif
//***********************************
	if (node!=NIL)
	{
		CObjectBase * o=GetObjectBase(m,node);
		if (o->IsObjectVisible())
		{
			if (!(rectintersect=IntersectionRectangle(o->RectangleIncludingObject(),area2redraw)).IsRectEmpty())
			{
				int p_tab;
				Layer *source;
				partobject2redraw=MoveRectangleByVecteur(rectintersect,Point2D(-o->ObjX,-o->ObjY));
				
				if ((p_tab=GetTab(m,node))!=NIL)
				{
					source = o->GetLayerPart(m,&partobject2redraw,p_tab);			
					// on blit dans le buffer offscreen
					while ( source != NULL )
					{
						blit->Blit(rectintersect.RctHG.iptX+source->Xdecal,rectintersect.RctHG.iptY+source->Ydecal,source);
						source = source->nextLayer();
					}
				}
			}
			PaintPartObjNodes(m,blit,GetSon(m,node),area2redraw);
		}
		PaintPartObjNodes(m,blit,GetBrother(m,node),area2redraw);
	}

//***********************************
#if DEBUG_LIB2DBASE
MMechostr (0, "\nPaintPartObjNodes end");
#endif
//***********************************

}





void PaintToolTip(CObjectBase *o,PtrObjBuffer blit)
{

//***********************************
#if DEBUG_LIB2DBASE
MMechostr (0, "\nPaintToolTip");
#endif
//***********************************

	if (o!=NULL && o->ObjToolTip!=NULL)
	{
		Layer * source=o->ObjToolTip->GetLayerToolTip();
		while ( source != NULL )
		{
			blit->Blit(o->ObjX+source->Xdecal,o->ObjY+source->Ydecal,source);
			source = source->nextLayer();
		}
	}

//***********************************
#if DEBUG_LIB2DBASE
MMechostr (0, "\nPaintToolTip end");
#endif
//***********************************
}




#if DEBUG_OBJNODE
void PrintTree(mmachine m,int prof,int node)
{
	char *str;
	int i;
	
	str=(char *)malloc(sizeof(char)*(prof+3));
	for (i=0;i<=prof;i++)
	{
		str[i]='-';
	}
	str[i++]='>';
	str[i]=0;

	if (node==NIL) MMechostr(MSKTRACE,"%s NIL\n",str);
	else
	{
		MMechostr(MSKTRACE,"%s %d Val:%d\n",str,node,GetObjectBase(m,node));
		PrintTree(m,prof+1,GetSon(m,node));
		PrintTree(m,prof,GetBrother(m,node));
	}
	free(str);
}

#endif //DEBUG_OBJNODE