//
// Modifications History
//
//$LB (20/12/2002) : 16bits to 24bits
//


#include "Macro.h"
#include "CObjectTree.h"
#include "../x/Objstr.h"
#include "utils.h"
#include "container.h"
#include "CObjectSlideBar.h"
#include "CObjMessageResize.h"			//pour la classe CObjMessageResize et les directions prédéfinies
#include "CObjMessageMove.h"			//pour la classe CObjMessageMove et les directions prédéfinies
#include "bitmap.h"

extern int OBJNODE;
extern mmachine mm;



// les references stockes dans le p_tab

#define ALPHA		  0
#define CONTENT       1
#define FIRST_ITEM    2
#define CLICKED_ITEM  3
#define FONT          4 // non utilisé, conservé uniquement pour la référence
#define SLIDE_V		  5
#define SLIDE_H		  6

// les references stockees dans le p_content (CONTENT ci-dessus)
#define ELEMENT          0  // [S AlphaBitmap]
#define STATE            1  // TRE_SHRINK ou TRE_EXPAND
#define BROTHER          2
#define SON              3
#define PREVIOUS_BROTHER 4
#define FATHER           5

// les differentes positions possibles
#define ROOT_NO_SON              0
#define ROOT_SHRINK              1
#define ROOT_EXPAND              2
#define NODE_NO_BROTHER_SHRINK   3
#define NODE_NO_BROTHER_EXPAND   4
#define NODE_BROTHER_SHRINK      5
#define NODE_BROTHER_EXPAND      6
#define LEAF_NO_BROTHER          7
#define LEAF_BROTHER             8
#define NODE_UNCLE_PENDING       9
#define POSITIONS               10


/*
int DEBUGnode( mmachine m, int p_content )
{
	char * str = MMstartstr( m, MTOP( MMfetch( m, MTOP( MMfetch( m, p_content, ELEMENT ) ), 0 ) ) );
	MMechostr(MSKTRACE,"Valeur du noeud: %s.\n", str);
	return 0;
}
*/



// ne gere pas la racine
int CObjectTree::GetColumnIndexBitmap( mmachine m, int p_content, int isRoot )
{
	if ( isRoot )
	{
		if ( MMfetch( m, p_content, SON ) == NIL ) // pas de fils
			return ROOT_NO_SON;
		else
		{
			if ( MTOI( MMfetch( m, p_content, STATE ) ) == TRE_EXPAND ) // le noeud est deploye
				return ROOT_EXPAND;
			else
				return ROOT_SHRINK;
		}
	}
	else
	{
		if ( MMfetch( m, p_content, SON ) == NIL ) // pas de fils
		{
			if ( MMfetch( m, p_content, BROTHER ) == NIL ) // pas de frere
				return LEAF_NO_BROTHER;
			else
				return LEAF_BROTHER;
		}
		else // un fils
		{
			if ( MTOI( MMfetch( m, p_content, STATE ) ) == TRE_EXPAND ) // le noeud est deploye
			{
				if ( MMfetch( m, p_content, BROTHER ) == NIL ) // pas de frere
					return NODE_NO_BROTHER_EXPAND;
				else
					return NODE_BROTHER_EXPAND;
			}
			else // noeud replie
			{
				if ( MMfetch( m, p_content, BROTHER ) == NIL ) // pas de frere
					return NODE_NO_BROTHER_SHRINK;
				else
					return NODE_BROTHER_SHRINK;
			}
		}
	}

	return 0;
}


CObjectTree::CObjectTree(container *cont,Layer *layer, Layer *graphics,
						 int x,int y,int w,int h, int flags,int contflags, int transp,int text_transp,
						 int text_color, int shadow_color, int shadow_decal,
						 int select_color, int select_transparency_coeff,
						 int visibleItemsCount, int itemSize, int reservedSpace, int levelShift,
						 PtrObjFont font):CObjectBase(cont,layer,x,y,w,h,flags,contflags,transp)
{
	this->TREtextTransp=text_transp;
	this->TREtransp=transp;
	this->TREtextColor = text_color;
	this->TREshadowColor = shadow_color;
	this->TREshadowDecal = shadow_decal;
	this->TREselectColor = select_color;
	this->TREselectTansparencyCoeff = select_transparency_coeff;
	this->itemSize = max( 1, itemSize );
	this->visibleItemsCount = visibleItemsCount;
	this->itemSelected = NO_ITEM_SELECTED;
//	if (this->hiddenRoot())
//		this->itemsCount = 0;
//	else
	this->itemsCount = 0;
	if ( reservedSpace == NIL )
		this->reservedSpace = 0;
	else
		this->reservedSpace = reservedSpace;
	if ( levelShift == NIL )
		this->levelShift = 0;
	else
		this->levelShift = levelShift;

	this->TRElayerGraphics = graphics;

	// MAT ICI non utilise !!
//	this->TRElayersBitmaps = NULL;
//	this->TRElayerSelect = NULL;
//	this->TRElayerClicked = NULL;

	this->TREfont = Lib2dFontService.getLayerFont(font);
}

CObjectTree::~CObjectTree()
{
	// les layers pour les images des elements
//	deleteLayers ( this->TRElayersBitmaps );

	Lib2dFontService.delLayerFont(this->TREfont);
	delete ( this->TRElayerGraphics );
}

int CObjectTree::getVisibleItemsCount()
{
	return this->visibleItemsCount;
}

// Fonctions de recherches et de manipulation des noeuds du contenu de l'arbre
// en memoire Scol !

int GetLevel( mmachine m, int p_content )
{
	if ( p_content == NIL )
		return 0;
	else
		return 1 + GetLevel( m, MTOP( MMfetch( m, p_content, FATHER ) ) );
}

// on recherche dans l'ordre :
// le frere du pere
// le frere du pere du pere
// etc...
int FindUpperNextElement( mmachine m, int p_content )
{
	if ( (p_content == NIL) || (MMfetch( m, p_content, FATHER ) == NIL) )
		return NIL;

	if ( MMfetch( m, MTOP( MMfetch( m, p_content, FATHER ) ), BROTHER ) != NIL )
		return MTOP( MMfetch( m, MTOP( MMfetch( m, p_content, FATHER ) ), BROTHER ) );
	else
		return FindUpperNextElement( m, MTOP( MMfetch( m, p_content, FATHER ) ) );
}

int FindFirstUncle( mmachine m, int p_content )
{
	if ( p_content == NIL )
		return NIL;

	if ( MMfetch( m, p_content, FATHER ) == NIL )
		return NIL;

	if ( MMfetch( m, MTOP( MMfetch( m, p_content, FATHER ) ), BROTHER ) != NIL )
		return MTOP( MMfetch( m, MTOP( MMfetch( m, p_content, FATHER ) ), BROTHER ) );

	return FindFirstUncle( m, MTOP( MMfetch( m, p_content, FATHER ) ) );
}

int FindNextElement( mmachine m, int p_content )
{
	if ( p_content == NIL )
		return NIL;

	if ( (MMfetch( m, p_content, SON ) != NIL) && (MTOI( MMfetch( m, p_content, STATE ) ) == TRE_EXPAND) )
		return MTOP( MMfetch( mm, p_content, SON ) );

	if ( MMfetch( m, p_content, BROTHER ) != NIL )
		return MTOP( MMfetch( m, p_content, BROTHER ) );

	return FindFirstUncle( m, p_content );
}

// recherche le dernier fils du dernier fils du dernier fils du ....
int FindLastSon( mmachine m, int p_content )
{
//	MMechostr(MSKTRACE,"FindLastSon");
	if ( (p_content == NIL) || (MMfetch( m, p_content, SON ) == NIL) || (MTOI( MMfetch( mm, p_content, STATE ) ) == TRE_SHRINK) )
	{
//		DEBUGnode( m, p_content );
		return p_content;
	}

	p_content = MTOP( MMfetch( m, p_content, SON ) );

	while ( MMfetch( m, p_content, BROTHER ) != NIL )
		p_content = MTOP( MMfetch( m, p_content, BROTHER ) );

	return FindLastSon( m, p_content );
}

// recherche dans l'ordre :
// le dernier fils du frere precedent du pere
// le frere precedent du pere
// le dernier fils du frere precedent du pere du pere
// etc...
int FindPreviousElement( mmachine m, int p_content )
{
	if ( p_content == NIL )
		return NIL;

//	DEBUGnode( m, p_content );

	if ( MMfetch( m, p_content, PREVIOUS_BROTHER ) != NIL )
	{
		int previous = FindLastSon( m, MTOP( MMfetch( m, p_content, PREVIOUS_BROTHER ) ) );

//		DEBUGnode( m, previous );
//		if ( previous != NIL )
			return previous;
//		else
//			return MTOP( MMfetch( m, p_content, PREVIOUS_BROTHER ) );
	}

	if ( MMfetch( m, p_content, FATHER ) == NIL )
		return NIL;
	else
		return MTOP( MMfetch( m, p_content, FATHER ) );
}

int FindFirstVisibleAscendantNode( mmachine m, int p_content, int p_betterfound )
{
	int father;

	if ( p_content == NIL )
		return NIL;

	father = MTOP( MMfetch( m, p_content, FATHER ) );
	if ( father == NIL )
		return p_betterfound;

	if ( MTOI( MMfetch( m, father, STATE ) ) == TRE_SHRINK )
		return FindFirstVisibleAscendantNode( m, father, father );
	else
		return FindFirstVisibleAscendantNode( m, father, p_betterfound );
}

// indique si un noeud est 'visible' i.e. si ses noeuds ascendants sont ouverts
int IsNodeVisible( mmachine m, int p_content )
{
	int father = MTOP( MMfetch( m, p_content, FATHER ) );

	if ( father == NIL )
		return 1;

	if ( MTOI( MMfetch( m, father, STATE ) ) == TRE_SHRINK )
		return 0;

	return IsNodeVisible( m, father );
}

// calcule le nbre de noeuds visibles (TRE_EXPAND) sous un certain noeud (non compris)
// le parametre p_content est le premier fils du noeud concerné
int ComputeVisibleNodesUnder( mmachine m, int p_content )
{
	int count = 0;

	if ( p_content != NIL )
	{
		count += 1;

		if ( MTOI( MMfetch( m, p_content, STATE ) ) == TRE_EXPAND )
			count += ComputeVisibleNodesUnder( m, MTOP( MMfetch( m, p_content, SON ) ) );

		count += ComputeVisibleNodesUnder( m, MTOP( MMfetch( m, p_content, BROTHER ) ) );
	}

	return count;
}

// calcule le nbre de noeuds visibles (TRE_EXPAND) sous un certain noeud (non compris)
// attention ! le certain noeud en question n'est pas teste comme etant ouvert ou ferme !
int VisibleNodesUnder( mmachine m, int p_content )
{
	int pipo = ComputeVisibleNodesUnder( m, MTOP( MMfetch( m, p_content, SON ) ) );
//MMechostr(MSKTRACE,"VisibleNodesUnder : %d\n", pipo);
	return pipo;
//	return ComputeVisibleNodesUnder( m, MTOP( MMfetch( m, p_content, SON ) ) );
}

// calcule le nbre d'elements visibles entre 2 noeuds (ordonnés !)
int VisibleNodesBetween( mmachine m, int p_first, int p_last )
{
//MMechostr(MSKTRACE,"VisibleNodesBetween\n");
	if ( p_first == NIL || p_last == NIL )
		return -1;
	else if ( p_first == p_last )
		return 0;
	else
	{
		int result = VisibleNodesBetween( m, FindNextElement( m, p_first ), p_last );

		if ( result == -1 )
			return -1;
		else
			return 1 + result;
	}
}

// indique si un noeud se trouve entre la racine et le premier element visible
int IsNodeSignificant( mmachine m, int p_tab, int p_content )
{
	int first = MTOP( MMfetch( m, p_tab, FIRST_ITEM ) );

	if ( p_content == NIL )
		return 0;
	else if ( first == NIL )
		return 1;
	else 
		return (VisibleNodesBetween( m, p_content, first ) > 0);
}

// verifie si le FIRST_ITEM concerne n'est pas trop bas (il reste des lignes vides en bas de l'arbre)
// et, s'il l'est, essaye de remonter jusqu'a ce que le dernier element de l'arbre soit tt en bas
int CheckFirstNode( mmachine m, int p_content, int visibleItemsCount, int hiddenRoot )
{
	int p_test, p_previous;
	int i = 0;

	p_test = p_content;

	while ( p_test != NIL && i < visibleItemsCount )
	{
		p_test = FindNextElement( m, p_test );
		i++;
	}

	if ( i == visibleItemsCount )
		return p_content; // le noeud est bien :)
	else
	{
		p_previous = FindPreviousElement( m, p_content );
		if ( hiddenRoot && MMfetch( m, p_previous, FATHER ) == NIL )
			p_previous = NIL;

		if ( p_previous == NIL )
			return p_content; // on ne peut remonter plus haut
		else
			return CheckFirstNode( m, p_previous, visibleItemsCount, hiddenRoot );
	}

}

int SetFirstItemForwards( mmachine m, int p_tab, int visibleItemsCount, int hiddenRoot, int value )
{
	int i = 0;
	int p_next, p_next_ok;

	p_next = MTOP( MMfetch( m, p_tab, FIRST_ITEM ) );
	p_next_ok = p_next;

	while ( i < value && p_next == p_next_ok )
	{
		p_next = FindNextElement( m, p_next );
		if ( p_next != NIL )
			p_next_ok = CheckFirstNode( m, p_next, visibleItemsCount, hiddenRoot );
		i++;
	}

	MMstore( m, p_tab, FIRST_ITEM, PTOM( p_next_ok ) );
//	DEBUGnode( m, p_next_ok );

	if ( p_next != p_next_ok )
		i--;

//	MMechostr(MSKTRACE,"SetFirstItemForwards: %d.\n", i);

	return i;
}

int SetFirstItemBackwards( mmachine m, int p_tab, int hiddenRoot, int value )
{
	int i = 0;
	int p_previous, p_previous_ok;
	
	p_previous = MTOP( MMfetch( m, p_tab, FIRST_ITEM ) );
	p_previous_ok = p_previous;

	while ( i < value && p_previous != NIL )
	{
		p_previous_ok = p_previous;
		p_previous = FindPreviousElement( m, p_previous );
		if ( hiddenRoot && p_previous == MTOP( MMfetch( m, p_tab, CONTENT ) ) )
			p_previous = NIL;
		i++;
	}

	if ( p_previous == NIL )
		MMstore( m, p_tab, FIRST_ITEM, PTOM( p_previous_ok ) );
	else
		MMstore( m, p_tab, FIRST_ITEM, PTOM( p_previous ) );

	if ( p_previous == NIL )
		i--;

//	MMechostr(MSKTRACE,"SetFirstItemBackwards: %d.\n", i);

	return i;
}

// indique si node2 se trouve parmi la descendance de node1
int IsDescendantNode( mmachine m, int node1, int node2 )
{
	if ( node1 == NIL || node2 == NIL )
		return 0;
	else if ( node1 == node2 )
		return 1;
	else
		return IsDescendantNode( m, node1, MTOP( MMfetch( m, node2, FATHER ) ) );
}

// indique la position de p_content_seek parmi les freres de p_content
// ex: 5e fils
int GetPositionAmongBrothers( mmachine m, int p_content_seek, int p_content, int count )
{
	if ( p_content == NIL || p_content_seek == NIL || MTOP( MMfetch( m, p_content, BROTHER ) ) == NIL )
		return 0;

	if ( MTOP( MMfetch( m, p_content, BROTHER ) ) == p_content_seek )
		return count;

	return GetPositionAmongBrothers( m, p_content_seek, MTOP( MMfetch( m, p_content, BROTHER ) ), count + 1 );
}

// etat de la pile a l'origine: ... p_content NIL
// resultat fournit ds la pile: ... p_root [I r1]
int BuildPositionFromNode( mmachine m )
{
	int p_content = MTOP( MMget( m, 1 ) );
	int position;
	int tmp_res;

	// pile: p_content p_position
	if ( p_content != NIL && MMfetch( m, p_content, FATHER ) != NIL )
	{
		position = GetPositionAmongBrothers( m, p_content, MTOP( MMfetch( m, MTOP( MMfetch( m, p_content, FATHER ) ), SON ) ), 1 );

//		MMechostr(MSKTRACE,"BuildPositionFromNode position: %d\n", position);

//		if ( position == 0 )
//			MMechostr(MSKTRACE,"CompTree: internal fatal error in building position from node !\n");

		// on empile la position
		CHECK( MMpush( m, ITOM( position ) ) );
		// on place ds le bon ordre pour un maillon de liste: position p_position
		INVERT( m, 0, 1 );
		// pile: p_content position p_position
		// on construit un maillon de liste
		CHECK( MMpush( m, ITOM( 2 ) ) );
		CHECK( MBdeftab( m ) );
		// pile: p_content p_position

		// on remplace p_content par son pere
		MMset( m, 1, MMfetch( m, MTOP( MMget( m, 1 ) ), FATHER ) );

		// on continue avec le niveau superieur
		CHECK( BuildPositionFromNode( m ) );
	}

	return 0;
}


// Methodes de l'objet

int CObjectTree::IsMouseOnObject(int x,int y, int p_tab)
{
	// on est toujours sur l'objet !
	return 1;
}

int CObjectTree::CursorMove(int x,int y,int keyFlags, int p_tab,int redrawobject)
{
	if ( this->IsObjectEnabled() )
	{
		x = x - ObjX;
		y = y - ObjY;
			
		if ( this->itemSelected != y / this->itemSize )
		{
			this->itemSelected = y / this->itemSize;
			if (this->highlightSelected())
				Redraw();
		}
	}

	return 0;
}

int CObjectTree::CursorMoveIn(int x,int y,int keyFlags, int p_tab,int redrawobject)
{
	return CursorMove( x, y, keyFlags, p_tab, redrawobject );
}

int CObjectTree::CursorMoveInWithBtnPushed(int x,int y,int keyFlags, int p_tab,int redrawobject)
{
	return CursorMoveIn( x, y, keyFlags, p_tab, redrawobject);
}

int CObjectTree::CursorMoveOut(int x,int y,int keyFlags, int p_tab,int redrawobject)
{
	if ( this->itemSelected != NO_ITEM_SELECTED )
	{
		this->itemSelected = NO_ITEM_SELECTED;
		Redraw();
	}

	return 0;
}

int CObjectTree::CursorMoveOutWithBtnPushed(int x,int y,int keyFlags, int p_tab,int redrawobject)
{
	return CursorMoveOut( x, y, keyFlags, p_tab, redrawobject);
}

int CObjectTree::CursorMoveOutsideWithBtnPushed(int x,int y,int keyFlags, int p_tab,int redrawobject)
{
	return 0;
}

Rect2D CObjectTree::GetClickableRect(mmachine m, int p_content, int itemPosition)
{
	int level = 0;
	int w = 0;

	if ( (p_content == NIL) )
		return (Rect2D());

	// on a besoin de la largeur de la ressource graphique
	w = this->TRElayerGraphics->RGBbitmap->TailleW;

	// on a besoin du niveau du noeud
	while ( MMfetch( mm, p_content, FATHER ) != NIL )
	{
		level++;
		p_content = MTOP( MMfetch( mm, p_content, FATHER ) );
	}
	if ( fixedRoot() )
		level--;
	if ( hiddenRoot() )
		level--;

	return (Rect2D( level * this->levelShift, itemPosition * this->itemSize, level * this->levelShift + w, (itemPosition + 1) * this->itemSize));
}

// gestion du clic sur les + et - des noeuds
// effectue le changement d'etat d'un noeud
// retourne p_content si un chgt d'etat a eu lieu sur un noeud, 0 si un element est selectionnable et -1 si le clic est trop a gauche
int CObjectTree::ClickInShrinkExpand(int x, int y, int p_content, int itemPosition)
{
	int w = 0;

	if ( itemPosition < this->visibleItemsCount )
	{
		Point2D mouse = Point2D( x, y );
		Rect2D clickableRect = GetClickableRect( mm, p_content, itemPosition );

		if ( p_content == NIL )
			return -1;

//		MMechostr(MSKTRACE,"testing %d item : (%d,%d) in ((%d,%d),(%d,%d))\n",itemPosition,x, y,tmpRect.RctHG.iptX, tmpRect.RctHG.iptY, tmpRect.RctBD.iptX, tmpRect.RctBD.iptY);
		if ( IsPointInRectangle( mouse, clickableRect ) )
		{
			if ( (MMfetch( mm, p_content, SON ) == NIL) || 
				 (fixedRoot() && MMfetch( mm, p_content, FATHER ) == NIL) ||
				 (fixedRoot() && hiddenRoot() && MMfetch( mm, MTOP( MMfetch( mm, p_content, FATHER ) ), FATHER ) == NIL )
				)
				return -1;
			else
			{
				int new_state = 1 - MTOI( MMfetch( mm, p_content, STATE ) );
//MMechostr(MSKTRACE,"ClickInShrinkExpand new_state : %d\n", new_state);
				MMstore( mm, p_content, STATE, ITOM( new_state ) );
				if ( new_state == TRE_EXPAND )
					addItem( VisibleNodesUnder( mm, p_content ), 1, 1 );
				else
					delItem( VisibleNodesUnder( mm, p_content ), 1, 1 );
				return p_content;
			}
		}

		if ( y < (itemPosition + 1) * this->itemSize )
		{
			// on a besoin de la largeur de la ressource graphique
			w = this->TRElayerGraphics->RGBbitmap->TailleW;

			if ( (clickableRect.IsRectEmpty() && x < w + GetLevel( mm, p_content ) * this->levelShift ) ||
				 (!clickableRect.IsRectEmpty() && x < clickableRect.RctBD.iptX) )
				return -1;
			else
				return 0;
		}
		else
			return ClickInShrinkExpand( x, y, FindNextElement( mm, p_content ), itemPosition + 1 );
	}

//	MMechostr(MSKTRACE,"ClickInShrinkExpand : no match\n");
	return -1;
}

int CObjectTree::ClickIn(int x,int y,int btn,int keyFlags, int p_tab,int redrawobject)
{
	int clicktest;

	x = x - ObjX;
	y = y - ObjY;

	this->TRElayerGraphics->RGBbitmap   = GET_PTR_OBJ_BITMAP(GET_BMP(MTOP(MMfetch(mm,p_tab,ALPHA))));
	this->TRElayerGraphics->AlphaBitmap = GET_PTR_OBJ_BITMAP(GET_ABMP(MTOP(MMfetch(mm,p_tab,ALPHA))));

	clicktest = ClickInShrinkExpand( x, y, MTOP( MMfetch( mm, p_tab, FIRST_ITEM ) ), 0 );

	if ( clicktest > 0 )
	{
		// on verifie que la liste est bien en bas
		int p_first = CheckFirstNode( mm, MTOP( MMfetch( mm, p_tab, FIRST_ITEM ) ), this->visibleItemsCount, this->hiddenRoot() );
		if ( p_first != MTOP( MMfetch( mm, p_tab, FIRST_ITEM ) ) )
			MMstore( mm, p_tab, FIRST_ITEM, PTOM( p_first ) );

		int tmp_res, k = OBJbeginreflex(mm,OBJNODE,(int)this,RFLOBJNODE_CHANGE);
		if (k>0) return 1; 
		if (k==0)
		{
//			MMechostr(MSKTRACE,"clicktest: %d\n", clicktest );

			// on empile clicktest
			CHECK( MMpush( mm, PTOM( clicktest ) ) );
			CHECK( MMpush( mm, PTOM( clicktest ) ) );
			CHECK( MMpush( mm, NIL ) );
			CHECK( BuildPositionFromNode( mm ) );
			// on retire clicktest
			INVERT( mm, 0, 1 );
			MMpull( mm );
			INVERT( mm, 0, 1 );
			clicktest = MTOP( MMpull( mm ) );
			// on empile l'état
			CHECK( MMpush( mm, MMfetch( mm, clicktest, STATE ) ) );
//			MMechostr(MSKTRACE,"STATE IS: %d\n", MTOI( MMfetch( mm, clicktest, STATE ) ) );
//			MMechostr(MSKTRACE,"clicktest: %d\n", clicktest );

			if ( redrawobject && IsObjectRepaintBeforeCallback() )
				Redraw();

			return OBJcallreflex(mm,2);
		}
		else if ( redrawobject )
			Redraw();
	}
	else if ( clicktest == 0 )// clic sur un element ?
	{
		int i = this->itemSelected - 1;
		int p_content = MTOP( MMfetch( mm, p_tab, FIRST_ITEM ) );

		if ( this->itemSelected != NO_ITEM_SELECTED )
		{
			while ( i >= 0 && p_content != NIL )
			{
				p_content = FindNextElement( mm, p_content );
				i--;
			}

			MMstore( mm, p_tab, CLICKED_ITEM, PTOM( p_content ) );
		}
		else
			MMstore( mm, p_tab, CLICKED_ITEM, NIL );

		if ( MMfetch( mm, p_tab, CLICKED_ITEM ) != NIL )
		{
			int tmp_res, k = OBJbeginreflex(mm,OBJNODE,(int)this,RFLOBJNODE_CLICK);
			if (k>0) return 1; 
			if (k==0)
			{
				int mask=keyFlags&(MK_CONTROL|MK_LBUTTON|MK_MBUTTON|MK_RBUTTON|MK_SHIFT);

				if ( redrawobject && IsObjectRepaintBeforeCallback() )
					Redraw();

				// on empile CLICKED_ITEM
				CHECK( MMpush( mm, MMfetch( mm, p_tab, CLICKED_ITEM ) ) );
				CHECK( MMpush( mm, NIL ) );
				CHECK( BuildPositionFromNode( mm ) );
				// on retire CLICKED_ITEM
				INVERT( mm, 0, 1 );
				MMpull( mm );
				CHECK(MMpush(mm,ITOM(btn)));
				CHECK(MMpush(mm,ITOM(mask)));

				return OBJcallreflex(mm,3);
			}
			else if ( redrawobject )
				Redraw();
		}
		else if ( redrawobject )
			Redraw();
	}
	else
	{
		MMstore( mm, p_tab, CLICKED_ITEM, NIL );
		if ( redrawobject )
			Redraw();
	}

	return 0;
}

int CObjectTree::UnClickIn(int x,int y,int btn,int keyFlags, int p_tab,int redrawobject)
{
	return CursorMove( x, y, keyFlags, p_tab, redrawobject );
}
	
int CObjectTree::ClickOut(int x,int y,int btn,int keyFlags, int p_tab,int redrawobject)
{
	return 0;
}

int CObjectTree::UnClickOut(int x,int y,int btn,int keyFlags, int p_tab,int redrawobject)
{
	return 0;
}

int CObjectTree::DblClickIn(int x,int y,int btn,int keyFlags, int p_tab,int redrawobject)
{
	int i = this->itemSelected - 1;
	int p_content = MTOP( MMfetch( mm, p_tab, FIRST_ITEM ) );

	x = x - ObjX;
	y = y - ObjY;

	if ( this->itemSelected != NO_ITEM_SELECTED )
	{
		Rect2D rect;
		int w = 0, h = 0;

		this->TRElayerGraphics->RGBbitmap   = GET_PTR_OBJ_BITMAP(GET_BMP(MTOP(MMfetch(mm,p_tab,ALPHA))));
		this->TRElayerGraphics->AlphaBitmap = GET_PTR_OBJ_BITMAP(GET_ABMP(MTOP(MMfetch(mm,p_tab,ALPHA))));

		// on a besoin de la largeur et de la hauteur d'une portion de la ressource graphique
		w = this->TRElayerGraphics->RGBbitmap->TailleW;

		while ( i >= 0 && p_content != NIL )
		{
			p_content = FindNextElement( mm, p_content );
			i--;
		}

		rect = GetClickableRect( mm, p_content, this->itemSelected );

		if ( (rect.IsRectEmpty() && x < w + GetLevel( mm, p_content ) * this->levelShift ) ||
			 (!rect.IsRectEmpty() && x < rect.RctBD.iptX) )
			MMstore( mm, p_tab, CLICKED_ITEM, NIL );
		else
			MMstore( mm, p_tab, CLICKED_ITEM, PTOM( p_content ) );
	}
	else
		MMstore( mm, p_tab, CLICKED_ITEM, NIL );

	if ( MMfetch( mm, p_tab, CLICKED_ITEM ) != NIL )
	{
		int tmp_res, k = OBJbeginreflex(mm,OBJNODE,(int)this,RFLOBJNODE_DBLCLICK);
		if (k>0) return 1; 
		if (k==0)
		{
			int mask=keyFlags&(MK_CONTROL|MK_LBUTTON|MK_MBUTTON|MK_RBUTTON|MK_SHIFT);

			if ( redrawobject && IsObjectRepaintBeforeCallback() )
				Redraw();
			// on empile CLICKED_ITEM
			CHECK( MMpush( mm, MMfetch( mm, p_tab, CLICKED_ITEM ) ) );
			CHECK( MMpush( mm, NIL ) );
			CHECK( BuildPositionFromNode( mm ) );
			// on retire CLICKED_ITEM
			INVERT( mm, 0, 1 );
			MMpull( mm );
			CHECK(MMpush(mm,ITOM(btn)));
			CHECK(MMpush(mm,ITOM(mask)));

			return OBJcallreflex(mm,3);
		}
		else if ( redrawobject )
			Redraw();
	}
	else if ( redrawobject )
		Redraw();

	return 0;
}

int CObjectTree::MouseWheel(int delta,int x,int y,int keyFlags,int p_tab,int redrawobject)
{
	int realDelta = 0;

	if ( delta < 0 ) // vers le bas
		realDelta = SetFirstItemForwards( mm, p_tab, this->visibleItemsCount, this->hiddenRoot(), -delta );
	else // vers le haut
		realDelta = - SetFirstItemBackwards( mm, p_tab, this->hiddenRoot(), delta );

	send_notification_value( LINK_MOVE, DIR_VERTICAL, realDelta, redrawobject );

	if ( redrawobject )
//	{
		Redraw();
//		this->repaint( 2 );
//	}

	return 0;
}

int CObjectTree::KeyUp(UINT vk,int cRepeat, UINT flags, int p_tab)
{
	return 0;
}

int CObjectTree::KeyDown(UINT vk,int keysys,int cRepeat, UINT flags, int p_tab)
{
	return 0;
}

int CObjectTree::SetFocus(int reset,int p_tab,int redraw)
{
	return 0;
}

int CObjectTree::KillFocus(int p_tab,int redraw)
{
	return 0;
}

int CObjectTree::Timer( int timerID )
{
	return 0;
}

int CObjectTree::setItemsCount( int value )
{
	this->itemsCount = value;
//	MMechostr(MSKTRACE,"this->itemsCount: %d\n", this->itemsCount);

	return this->itemsCount;
}

int CObjectTree::setFirstItem( int value, int notifylinks, int redrawobject )
{
	if ( notifylinks )
		send_notification_value( LINK_MOVE, DIR_VERTICAL, value, redrawobject );

	if ( redrawobject )
		Redraw();
	
	return 0;
}

int CObjectTree::addItem( int value, int notifylinks, int redrawobject )
{
	int differenceValue = 0;
	
//	MMechostr(MSKTRACE,"addItem\n");

	if ( this->itemsCount + value > this->visibleItemsCount )
		differenceValue = max( 0, min( this->itemsCount + value - this->visibleItemsCount, value ) );

	setItemsCount( this->itemsCount + value );
//	MMechostr(MSKTRACE,"visibleItemsCount: %d\n", this->visibleItemsCount);
//	MMechostr(MSKTRACE,"value: %d\n", value);
//	MMechostr(MSKTRACE,"differenceValue: %d\n", differenceValue);

	if ( notifylinks && differenceValue != 0 )
		send_notification_size( LINK_RESIZE, DIR_VERTICAL, differenceValue, redrawobject );

	if ( redrawobject )
		Redraw();

	return 0;
}

int CObjectTree::delItem( int value, int notifylinks, int redrawobject )
{
	int differenceValue = 0;
	
//	MMechostr(MSKTRACE,"delItem\n");

	if ( this->itemsCount > this->visibleItemsCount )
		differenceValue = max( 0, min( this->itemsCount + value - this->visibleItemsCount, value ) );

	setItemsCount( this->itemsCount - value );
//	MMechostr(MSKTRACE,"value: %d\n", -value);
//	MMechostr(MSKTRACE,"differenceValue: %d\n", -differenceValue);

	if ( notifylinks && differenceValue != 0 )
		send_notification_size( LINK_RESIZE, DIR_VERTICAL, -differenceValue, redrawobject );

	if ( redrawobject )
		Redraw();

	return 0;
}

// un noeud de l'arbre de description du contenu est composé de: [[S AlphaBitmap] I p_brother p_son p_previous_brother p_father], le I indique l'état (TRE_SHRINK ou TRE_EXPAND)
//
// creation de layers pour les + et les - des noeuds
//
Layer *CObjectTree::DrawTreeElementsDownwards( mmachine m, int p_tab, int *handledElements, int p_content, int level, Layer *upperLevelsLayers, Layer *graphics, Rect2D paintrect )
{
	if (ObjLayer==NULL) return NULL;

	int position = *handledElements;
	Layer *tmpLayer, *nodeState = NULL;
	int w = graphics->RGBbitmap->TailleW;
	int h = graphics->RGBbitmap->TailleH / POSITIONS;

	if ( (p_content != NIL) && (position < this->visibleItemsCount) )
	{
		int next_p_content;
		int isRoot = (MMfetch( m, p_content, FATHER ) == NIL) || (hiddenRoot() && MMfetch( m, MTOP( MMfetch( m, p_content, FATHER ) ), FATHER ) == NIL );
		int index = GetColumnIndexBitmap( m, p_content, isRoot );
		Layer *tmpUpperLevels, *upperLevels = upperLevelsLayers;
		char *text = NULL;

		if ( MMfetch( m, MTOP( MMfetch( m, p_content, ELEMENT ) ), 0 ) != NIL )
			text = MMstartstr( m, MTOP( MMfetch( m, MTOP( MMfetch( m, p_content, ELEMENT ) ), 0 ) ) );
//				MMechostr(MSKTRACE,"DrawTreeElementsDownwards: dessin de '%s' en (%d, %d)\n", text, level * this->levelShift, elementsHandled * this->itemSize );

		// on dessine l'element
		DrawText( text, w + level * this->levelShift, position * this->itemSize, MMfetch( m, MTOP( MMfetch( m, p_content, ELEMENT ) ), 1 ) != NIL );

		// le layer de selection
		if ( this->highlightSelected() && this->itemSelected == position )
		{
			Layer *selection = new Layer( ObjW - w - level * this->levelShift, this->itemSize, ObjTransparency, 1 );

			selection->fillLayer( this->TREselectColor );
			selection->fillAlphaLayer( this->TREselectTansparencyCoeff );

			selection->Ydecal = position * this->itemSize;
			selection->Xdecal = w + level * this->levelShift;

			ObjLayer->addLayer( selection );
		}

		// le layer de clic
		if ( this->highlightClicked() && p_content == MTOP( MMfetch( m, p_tab, CLICKED_ITEM ) ) )
		{
			Layer *selection = new Layer( ObjW - w - level * this->levelShift, this->itemSize, ObjTransparency, 1 );

			selection->fillLayer( this->TREselectColor );
			selection->fillAlphaLayer( this->TREselectTansparencyCoeff );

			selection->Ydecal = position * this->itemSize;
			selection->Xdecal = w + level * this->levelShift;

			ObjLayer->addLayer( selection );
		}

		// on dessine son image
		if ( MMfetch( m, MTOP( MMfetch( m, p_content, ELEMENT ) ), 1 ) != NIL )
			ObjLayer->addLayer( DrawBitmap( MTOP( MMfetch( m, MTOP( MMfetch( m, p_content, ELEMENT ) ), 1 ) ), w + level * this->levelShift, position * this->itemSize ) );

		// on ajoute un layer des + et -
		if ( (!fixedRoot() || MMfetch( m, p_content, FATHER ) != NIL) && // pas de layer + et - pour la racine en fixedRoot
			 (!hiddenRoot() || MMfetch( m, MTOP( MMfetch( m, p_content, FATHER ) ), FATHER ) != NIL) )
		{
			nodeState = new Layer( graphics->RGBbitmap, graphics->AlphaBitmap, graphics->Transparency );
			nodeState->Xdecal = level * this->levelShift;
			nodeState->Ydecal = position * this->itemSize;
			nodeState->sourceRect = Rect2D(0,index*h,min( w, this->ObjW - nodeState->Xdecal ),(index+1)*h);
		}

//			MMechostr(MSKTRACE,"nodeState created !\n");

		// on duplique les layers des niveaux superieurs
		while ( upperLevels != NULL )
		{
			tmpUpperLevels = new Layer( upperLevels->RGBbitmap, upperLevels->AlphaBitmap, upperLevels->Transparency );
			tmpUpperLevels->sourceRect = upperLevels->sourceRect;
			tmpUpperLevels->Xdecal = upperLevels->Xdecal;
			tmpUpperLevels->Ydecal = position * this->itemSize;

			if ( nodeState != NULL )
				nodeState->addLayer( tmpUpperLevels );
			else
				nodeState = tmpUpperLevels;

			upperLevels = upperLevels->nextLayer();

//				MMechostr(MSKTRACE,"upperLevels created !\n");
		}

		position++;

		if ( (MTOI( MMfetch( m, p_content, STATE ) ) == TRE_EXPAND) && (MMfetch( m, p_content, SON ) != NIL) ) // le noeud est deploye et il a un fils
		{
			Layer *uncle_pending = NULL;
			next_p_content = MTOP( MMfetch( m, p_content, SON ) ); // le fils suivant

			// si le noeud a aussi un frere on construit un layer pour NODE_UNCLE_PENDING
			if ( MMfetch( m, p_content, BROTHER ) != NIL && (!hiddenRoot() || MMfetch( m, MTOP( MMfetch( m, p_content, FATHER ) ), FATHER ) != NIL) )
			{
				uncle_pending = new Layer( graphics->RGBbitmap, graphics->AlphaBitmap, graphics->Transparency );
				uncle_pending->sourceRect = Rect2D(0,NODE_UNCLE_PENDING*h,w,(NODE_UNCLE_PENDING+1)*h);
				uncle_pending->Xdecal = level * this->levelShift;
				// Ydecal sera a positionner par le fils
				uncle_pending->addLayer( upperLevelsLayers );

				tmpLayer = DrawTreeElementsDownwards( m, p_tab, &position, next_p_content, level + 1, uncle_pending, graphics, paintrect );

				delete uncle_pending;
			}
			else
				tmpLayer = DrawTreeElementsDownwards( m, p_tab, &position, next_p_content, level + 1, upperLevelsLayers, graphics, paintrect );

			if ( nodeState != NULL )
				nodeState->addLayer( tmpLayer );
			else
				nodeState = tmpLayer;
		}

		next_p_content = MTOP( MMfetch( m, p_content, BROTHER ) ); // le frere suivant
		tmpLayer = DrawTreeElementsDownwards( m, p_tab, &position, next_p_content, level, upperLevelsLayers, graphics, paintrect );
		if ( nodeState != NULL )
			nodeState->addLayer( tmpLayer );
		else
			nodeState = tmpLayer;
	}

//	MMechostr(MSKTRACE,"elementsHandled: %d\n", elementsHandled);
	*handledElements = position;

	return nodeState;
}

Layer *CObjectTree::CreateUpperLevelsLayers( mmachine m, int p_content, int level, Layer *previousUpperLevels, Layer *graphics, Rect2D paintrect )
{
	int w = graphics->RGBbitmap->TailleW;
	int h = graphics->RGBbitmap->TailleH / POSITIONS;

	if ( MMfetch( m, p_content, FATHER ) == NIL || (hiddenRoot() && MMfetch( m, MTOP( MMfetch( m, MTOP( MMfetch( m, p_content, FATHER ) ), FATHER ) ), FATHER ) == NIL) )
		return previousUpperLevels;

	level--;

	if ( MMfetch( m, MTOP( MMfetch( m, p_content, FATHER ) ), BROTHER ) != NIL )
	{
//		MMechostr(MSKTRACE,"CreateUpperLevelsLayers : lvl %d\n", level);
		// on cree un layer pour le trait descendant
		Layer *uncle_pending = new Layer( graphics->RGBbitmap, graphics->AlphaBitmap, graphics->Transparency );
		uncle_pending->Xdecal = level * this->levelShift;
		// Ydecal sera a positionner par le fils
		uncle_pending->sourceRect = Rect2D(0,NODE_UNCLE_PENDING*h,min( w, this->ObjW - uncle_pending->Xdecal ),(NODE_UNCLE_PENDING+1)*h);
		
		uncle_pending->addLayer( previousUpperLevels );
		previousUpperLevels = uncle_pending;
	}

	return CreateUpperLevelsLayers( m, MTOP( MMfetch( m, p_content, FATHER ) ), level, previousUpperLevels, graphics, paintrect );
}

/*
int
DEBUG_countLayers( Layer *layers )
{
	int count = 0;

	while ( layers != NULL )
	{
		count++;
		layers = layers->nextLayer();
	}

	return count;
}
*/

Layer *CObjectTree::DrawTreeElements( mmachine m, int p_tab, int *handledElements, int p_content, Layer *graphics, Rect2D paintrect )
{
	Layer *upperLevelsLayers = NULL;
	Layer *result = NULL;
	int level;

	if ( p_content != NIL && *handledElements < this->visibleItemsCount )
	{
		if ( fixedRoot() )
		{
			if ( hiddenRoot() )
				level = GetLevel( m, p_content ) - 3;
			else
				level = GetLevel( m, p_content ) - 2;
		}
		else
		{
			if ( hiddenRoot() )
				level = GetLevel( m, p_content ) - 2;
			else
				level = GetLevel( m, p_content ) - 1;
		}

		upperLevelsLayers = CreateUpperLevelsLayers( m, p_content, level, NULL, graphics, paintrect );

		Layer *downlayers = DrawTreeElementsDownwards( m, p_tab, handledElements, p_content, level, upperLevelsLayers, graphics, paintrect );
		while ( upperLevelsLayers != NULL )
		{
			Layer *tmp = upperLevelsLayers->nextLayer();
			delete upperLevelsLayers;
			upperLevelsLayers = tmp;
		}

		result = downlayers;

//		MMechostr(MSKTRACE,"downlayers : %d\n", DEBUG_countLayers( downlayers ));
		
		if ( *handledElements < this->visibleItemsCount )
		{
			Layer *uplayers = NULL;

			p_content = FindUpperNextElement( m, p_content );

			uplayers = DrawTreeElements( m, p_tab, handledElements, p_content, graphics, paintrect );

//			MMechostr(MSKTRACE,"uplayers : %d\n", DEBUG_countLayers( uplayers ));
		
			if ( downlayers != NULL )
				result = downlayers->addLayer( uplayers );
			else
				result = uplayers;
		}

//		MMechostr(MSKTRACE,"result : %d\n", DEBUG_countLayers( result ));

	}

	return result;
}

void CObjectTree::DrawText( char *text, int x_position, int y_position, int bitmapPresent )
{
	int x1, y1, x2, y2;

	if ( text != NULL )
	{
		if ( bitmapPresent )
			x1 = this->reservedSpace + x_position;
		else
			x1 = x_position;
		y1 = y_position;
		x2 = ObjW;
		y2 = y_position + this->itemSize;

		// gestion de l'ombre du texte
		if ( this->TREshadowDecal != 0 )
			this->TREfont->CopyStringOnRectLayer( ObjLayer,
												  Rect2D( x1 + this->TREshadowDecal, 
														  y1 + this->TREshadowDecal,
														  x2 + this->TREshadowDecal,
														  y2 + this->TREshadowDecal ),
												  ObjFlags,
												  text,
												  strlen(text),
												  this->TREshadowColor,
												  255,
												  0 );
		this->TREfont->CopyStringOnRectLayer( ObjLayer,
											  Rect2D( x1, y1, x2, y2 ),
											  ObjFlags,
											  text,
											  strlen(text),
											  this->TREtextColor,
											  255,
											  0 );
	}
}

Layer *CObjectTree::DrawBitmap( int p_alphabmp, int positionX, int positionY )
{
	PtrObjBitmap src_rgb, src_alpha;
	Layer *bitmapLayer;
	int transparency;
	int taillew, tailleh;

	src_rgb		= GET_PTR_OBJ_BITMAP(GET_BMP(p_alphabmp));
	src_alpha	= GET_PTR_OBJ_BITMAP(GET_ABMP(p_alphabmp));
	transparency =GET_TRANSP(p_alphabmp);

	taillew = src_rgb->TailleW;
	tailleh = src_rgb->TailleH;

	bitmapLayer = new Layer( src_rgb, src_alpha, transparency );

	bitmapLayer->Xdecal = positionX + (this->reservedSpace - taillew) / 2;
	bitmapLayer->Ydecal = positionY + (this->itemSize - tailleh) / 2;
	bitmapLayer->sourceRect = Rect2D( 0, 0, min( taillew, this->ObjW - bitmapLayer->Xdecal ), tailleh );

	return bitmapLayer;
}

Layer *CObjectTree::GetLayer(mmachine m,int p_tab)
{
	if (ObjLayer==NULL) return NULL;

	int p_content;
	Layer *next_tmp, *tmp = ObjLayer->nextLayer();

	this->itemsDisplayed = 0;

	while ( tmp != NULL )
	{
		next_tmp = tmp->nextLayer();
		delete tmp;
		tmp = next_tmp;
	}
	ObjLayer->nulifyNext();

	ObjLayer->sourceRect = Rect2D( 0, 0, ObjW, ObjH );
	ObjLayer->fillLayer( ObjTransparency );
	ObjLayer->sourceRect = Rect2D( 0, 0, ObjW, ObjH );

	p_content = MTOP( MMfetch( m, p_tab, FIRST_ITEM ) );

	this->TRElayerGraphics->RGBbitmap   = GET_PTR_OBJ_BITMAP(GET_BMP(MTOP(MMfetch(mm,p_tab,ALPHA))));
	this->TRElayerGraphics->AlphaBitmap = GET_PTR_OBJ_BITMAP(GET_ABMP(MTOP(MMfetch(mm,p_tab,ALPHA))));

	if ( p_content != NIL )
		ObjLayer->addLayer( DrawTreeElements( m, p_tab, &this->itemsDisplayed, p_content, this->TRElayerGraphics, Rect2D( 0, 0, ObjW, ObjH ) ) );

	return ObjLayer;
}

Layer *CObjectTree::GetLayerPart(mmachine m,Rect2D *paintrect,int p_tab)
{
	if (ObjLayer==NULL) return NULL;
  
  Layer *tmp = GetLayer( m, p_tab )->nextLayer();
	
	ObjLayer->sourceRect = IntersectionRectangle( Rect2D( 0, 0, ObjW, ObjH ), *paintrect );

	// on clip au paintrect
	while ( tmp != NULL )
	{
		tmp->sourceRect = IntersectionRectangle( 
								tmp->sourceRect,
								MoveRectangleByVecteur( *paintrect, Point2D( tmp->sourceRect.RctHG.iptX - tmp->Xdecal, tmp->sourceRect.RctHG.iptY - tmp->Ydecal ) ) );
		if ( (*paintrect).RctHG.iptX > tmp->Xdecal )
			tmp->Xdecal = 0;
		else
			tmp->Xdecal = tmp->Xdecal - (*paintrect).RctHG.iptX;
		if ( (*paintrect).RctHG.iptY > tmp->Ydecal )
			tmp->Ydecal = 0;
		else
		if ( (*paintrect).RctHG.iptY != 0 )
			tmp->Ydecal = tmp->Ydecal - (*paintrect).RctHG.iptY;

		tmp = tmp->nextLayer();
	}

	return ObjLayer;
}

// gestion des liens entre objets

// supporte le LINK_RESIZE mais ne fait rien
int CObjectTree::supports( int type )
{
	return ( type == LINK_MOVE || type == LINK_RESIZE );
}

int CObjectTree::handle(CObjMessage* msg)
{
	int direction, value, redrawobject;
	int p_tab;

	switch(msg->GetType())
	{
		case LINK_MOVE:
			p_tab = GetTab(mm,FindObjNodeFromHdlSys(mm,(int)this));
			//recherche les informations du message
			direction = (int)(static_cast <CObjMessageMove*> (msg))->GetDirection();
			value =		(int)(static_cast <CObjMessageMove*> (msg))->GetOffset();
			redrawobject = msg->GetRedrawObject();

			//traite les informations du message
			if ( direction == DIR_VERTICAL )
			{
				if ( value > 0 )
					SetFirstItemForwards( mm, p_tab, this->visibleItemsCount, this->hiddenRoot(), value );
				else
					SetFirstItemBackwards( mm, p_tab, hiddenRoot(), -value );

				if ( redrawobject )
					Redraw();
			}

			break;

		default:
			break;
	}

	return 0;
}

int CObjectTree::send_notification_value( int type, int direction, int value, int redrawobject )
{
	CObjMessageMove *msg = new CObjMessageMove(direction, value, redrawobject);

	//propage le message aux objets liés
	this->notify(msg);
	delete msg;

	return 0;
}

int CObjectTree::send_notification_size( int type, int direction, int value, int redrawobject )
{
	CObjMessageResize *msg = new CObjMessageResize(direction, 0, value, redrawobject);

	//propage le message aux objets liés
	this->notify(msg);
	delete msg;

	return 0;
}

int CObjectTree::DestroyAllLayers()
{
	if (ObjLayer!=NULL)	
		deleteLayers(ObjLayer);
	ObjLayer=NULL;
	return 0;
}

int CObjectTree::ResizeLayer(int w,int h,int p_tab)
{
	ObjLayer=new Layer( w, h, TREtransp, TREtextTransp!=255 );
	int old=visibleItemsCount;
	visibleItemsCount=h/itemSize;
	send_notification_size(LINK_RESIZE,DIR_VERTICAL,old-visibleItemsCount, 1 );
	
	int p_first = MTOP( MMfetch( mm, p_tab, FIRST_ITEM ) );
	p_first = CheckFirstNode(mm,p_first,visibleItemsCount, this->hiddenRoot());
	MMstore( mm, p_tab, FIRST_ITEM, PTOM(p_first) );
	return 0;
}

// FONCTIONS SCOL ASSOCIEES

// fonctions de recherche de noeud
//
// positionne p_content sur le noeud precedent la position
// et renvoit 0 (respectivement 1) si la position designe le fils (respectivement le frere)
//
// si la position ne peut etre atteinte, p_content vaudra NIL et le retour -1
// complete_path a 1 indique si sur le dernier niveau on souhaite atteindre la position exacte
// complete_path a 0 indique qu'on se contente du dernier element si la position depasse le nbre d'elements
// un noeud de l'arbre de description du contenu est composé de: [[S AlphaBitmap] I p_brother p_son p_previous_brother p_father], le I indique l'état (TRE_SHRINK ou TRE_EXPAND)

int compTreeFindPreviousNode( mmachine m, int *p_content, int p_position, int complete_path )
{
	int p_current = MTOP( MMfetch( m, *p_content, SON ) ); // *p_content != NIL
	int position = MTOI( MMfetch( m, p_position, 0 ) );  // p_position != NIL
	int i = 0;

	if ( position < 0 )
		return NIL;

	if ( position == i )
	{
		// est-ce la fin du chemin a resoudre ?
		if ( MMfetch( m, p_position, 1 ) == NIL )
			return 0; // la position designe le fils
		else
			// y a t'il un fils ?
			if ( p_current != NIL )
			{
				*p_content = p_current;
//				MMechostr(MSKTRACE,"FindNode recursivity 1 !!!\n");

				return compTreeFindPreviousNode( m, p_content, MTOP( MMfetch( m, p_position, 1 ) ), complete_path );
			}
			else // chemin insolvable
			{
				*p_content = NIL;
//				MMechostr(MSKTRACE,"FindNode fatal error 1 !!!\n");
				return -1;
			}
	}

	if ( p_current == NIL )
	{
		if ( complete_path )
		{
			*p_content = NIL;
			return -1;
		}
		else
			return 0;
	}


	// parcours sur les freres du fils
	while ( (i < position - 1) && (MMfetch( m, p_current, BROTHER ) != NIL) )
	{
		p_current = MTOP( MMfetch( m, p_current, BROTHER ) );
		i++;
	}

	// est-ce la fin du chemin a resoudre ?
	if ( MMfetch( m, p_position, 1 ) == NIL )
	{
		*p_content = p_current;
		return 1; // la position designe le frere
	}

	if ( position - 1 != i || MMfetch( m, p_current, BROTHER ) == NIL ) // chemin insolvable
	{
		*p_content = NIL;
//		MMechostr(MSKTRACE,"FindNode fatal error 2 (%d/%d) !!!\n", position - 1, i);
		return -1;
	}

	p_current = MTOP( MMfetch( m, p_current, BROTHER ) );

	// y a t'il un fils ?
	if ( complete_path && MMfetch( m, p_current, SON ) == NIL )
	{
		*p_content = NIL;
//		MMechostr(MSKTRACE,"FindNode fatal error 3 !!!\n");
		return -1;
	}

	*p_content = p_current;
//	MMechostr(MSKTRACE,"FindNode recursivity 2 !!!\n");
	return compTreeFindPreviousNode( m, p_content, MTOP( MMfetch( m, p_position, 1 ) ), complete_path );
}

int compTreeFindNode( mmachine m, int p_content, int p_position )
{
	int found_as_brother;
	
	if ( p_content == NIL )
		return NIL;

	found_as_brother = compTreeFindPreviousNode( m, &p_content, p_position, 1 );

	if ( p_content == NIL )
		return NIL;
	else
		if ( found_as_brother )
			return MTOP( MMfetch( m, p_content, BROTHER ) );
		else
			return MTOP( MMfetch( m, p_content, SON ) );
}

// fonctions de recherche de noeud
//
// positionne p_content sur le noeud precedent la chaine de caracteres
// et renvoit 0 (respectivement 1) si la position designe le fils (respectivement le frere)
// seekMode: TRE_PRE_ORDER ou TRE_LEVEL_ORDER
int compTreeSeekNodePreOrder( mmachine m, int p_content, char *seekString )
{
	char *tgt_txt = NULL;
	int result;

	if ( p_content == NIL )
		return NIL;

	if ( MMfetch( m, p_content, ELEMENT ) != NIL )
		tgt_txt = MMstartstr( m, MTOP( MMfetch( m, MTOP( MMfetch( m, p_content, ELEMENT ) ), 0 ) ) );
	if ( !MYstrcmp( seekString, tgt_txt ) )
		return p_content;

	// sur les fils
	result = compTreeSeekNodePreOrder( m, MTOP( MMfetch( m, p_content, SON ) ), seekString );
	if ( result != NIL )
		return result;

	// sur les freres
	result = compTreeSeekNodePreOrder( m, MTOP( MMfetch( m, p_content, BROTHER ) ), seekString );
	if ( result != NIL )
		return result;

	return NIL;
}

int compTreeFindFirstNephew( mmachine m, int p_content )
{
	if ( p_content == NIL )
		return NIL;

	p_content = MTOP( MMfetch( m, p_content, BROTHER ) );
	if ( p_content == NIL )
		return NIL;
	else if ( MMfetch( m, p_content, SON ) == NIL )
		return compTreeFindFirstNephew( m, p_content );
	else
		return MTOP( MMfetch( m, p_content, SON ) );
}

int compTreeFindSonOrNephew( mmachine m, int p_content )
{
	if ( p_content == NIL )
		return NIL;

	if ( MMfetch( m, p_content, SON ) == NIL )
		return compTreeFindFirstNephew( m, p_content );
	else
		return MTOP( MMfetch( m, p_content, SON ) );
}

int compTreeFindBrotherOrCousin( mmachine m, int p_content )
{
	if ( p_content == NIL )
		return NIL;

	if ( MMfetch( m, p_content, BROTHER ) == NIL )
		return compTreeFindFirstNephew( m, MTOP( MMfetch( m, p_content, FATHER ) ) );
	else
		return MTOP( MMfetch( m, p_content, BROTHER ) );
}

int compTreeSeekNodeLevelOrderLevelSeek( mmachine m, int p_content, char *seekString )
{
	char *tgt_txt = NULL;

	if ( p_content == NIL )
		return NIL;

	if ( MMfetch( m, p_content, ELEMENT ) != NIL )
		tgt_txt = MMstartstr( m, MTOP( MMfetch( m, MTOP( MMfetch( m, p_content, ELEMENT ) ), 0 ) ) );
	if ( !MYstrcmp( seekString, tgt_txt ) )
		return p_content;

	return compTreeSeekNodeLevelOrderLevelSeek( m, compTreeFindBrotherOrCousin( m, p_content ), seekString );
}

int compTreeSeekNodeLevelOrder( mmachine m, int p_content, char *seekString )
{
	int result;

	if ( p_content == NIL )
		return NIL;

	result = compTreeSeekNodeLevelOrderLevelSeek( m, p_content, seekString );
	if ( result != NIL )
		return result;

	return compTreeSeekNodeLevelOrder( m, compTreeFindSonOrNephew( m, p_content ), seekString );

}

int compTreeSeekNode( mmachine m, int p_content, char *seekString, int seekMode )
{
	if ( seekMode == TRE_PRE_ORDER )
		return compTreeSeekNodePreOrder( m, p_content, seekString );
	else
		return compTreeSeekNodeLevelOrder( m, p_content, seekString );
}

int compTreeSeekPreviousNode( mmachine m, int *p_content, char *seekString, int seekMode )
{
	*p_content = compTreeSeekNode( m, *p_content, seekString, seekMode );

	if ( *p_content == NIL )
		return NIL;

	if ( MMfetch( m, *p_content, PREVIOUS_BROTHER ) != NIL )
	{
		*p_content = MTOP( MMfetch( m, *p_content, PREVIOUS_BROTHER ) );
		return 1;
	}
	else
	{
		*p_content = MTOP( MMfetch( m, *p_content, FATHER ) );
		return 0;
	}
}

// fonctions de decompte de noeuds
//
// MAT ICI cela sert-il vraiment ?
/*
int compTreeCountPreviousNode( mmachine m, int p_content, int p_content_seek, int *found )
{
	int p_son = MTOP( MMfetch( m, p_content, SON ) ); // *p_content != NIL;
	int p_brother = MTOP( MMfetch( m, p_content, BROTHER ) );
	int count = 1;

	// y a t'il un fils ?
	if ( p_son != NIL ) //MAT ICI: gerer les noeuds fermes && MTOI( MMfetch( m, *p_content, STATE ) ) != TRE_SHRINK )
	{
		if ( p_content_seek == p_son )
		{
			*found = 1;
			return count + 1; // le fils correspond
		}
		else // on descend dans les fils suivants
			count = count + compTreeCountPreviousNode( m, p_son, p_content_seek, found );
	}

	if ( (p_brother != NIL) && !(*found) )
	{
		if ( p_content_seek == p_brother )
		{
			*found = 1;
			return count + 1; // le frere correspond
		}
		else // on enchaine sur les freres suivants
			count = count + compTreeCountPreviousNode( m, p_brother, p_content_seek, found );
	}

	return count;
}

int compTreeCountNode( mmachine m, int p_content, int p_content_seek )
{
	int found = 0;
	int count = compTreeCountPreviousNode( m, p_content, p_content_seek, &found);

	// si count > itemsCount alors return 0
	return count - 1;
}
*/

// fonctions utilisateurs

int _CRcompTree( mmachine m )
{

	int tmp_res,i;
	int p_text_settings, p_select_settings, shadowflag, shadowcolor, textcolor, coeff_transp, select_color, text_transp;
	int p_font, p_coordinates, p_objpere, p_container, p_alphabmp;
	int flags_creation, flags_handler, graphicsTransparency;
	int w, h, x, y, itemsvisible, bitmaps_reserved_space, space_per_level_shift;
	CObjectBase *obj_root,*obj_father,* new_object;
	container *co;
	//$ LB (13/06/2002)
	PtrObjBitmap bmp, abmp;

	// pile: Chn ObjContainer ObjNode [I I] I I I I I I ObjFont I I [I I I I] [I I] [[I I] I AlphaBitmap [I I I]] [[I I] I AlphaBitmap [I I I]] AlphaBitmap // 18 elts

	// les differents tests de non validité
	if ((MMget(m,17))==NIL)
	{
		MMechostr(MSKTRACE,"_CRcompTree: channel is nil\n");
		m->pp += 17;
		MMset(m,0,NIL);
		return 0;	
	}
	if ((MMget(m,16))==NIL)
	{
		MMechostr(MSKTRACE,"_CRcompTree: container is nil\n");
		m->pp += 17;
		MMset(m,0,NIL);
		return 0;
	}
	if ((MMget(m,9))==NIL)
	{
		MMechostr(MSKTRACE,"_CRcompTree: number of visible items is nil\n");
		m->pp += 17;
		MMset(m,0,NIL);
		return 0;
	}
	else if (MTOI(MMget(m,9))<=0)
	{
		MMechostr(MSKTRACE,"_CRcompTree: number of visible items is <= 0\n");
		m->pp += 17;
		MMset(m,0,NIL);
		return 0;
	}
	if ((MMget(m,7))==NIL)
	{
		MMechostr(MSKTRACE,"_CRcompTree: font is nil\n");
		m->pp += 17;
		MMset(m,0,NIL);
		return 0;
	}
	if ((MMget(m,0))==NIL)
	{
		MMechostr(MSKTRACE,"_CRcompTree: resource bitmap is nil\n");
		m->pp += 17;
		MMset(m,0,NIL);
		return 0;
	}


#if DEBUG_OBJNODE
		MMechostr(MSKTRACE,"DEBUG_OBJNODE _CRcompTree\n");
#endif

	// on inverse le bitmap de resource et les coordonnees
	INVERT( m, 0, 14 );
	// pile: Chn ObjContainer ObjNode AlphaBitmap I I I I I I ObjFont I I [I I I I] [I I] [[I I] I AlphaBitmap [I I I]] [[I I] I AlphaBitmap [I I I]] [I I] // 18 elts

	// les coordonnées
	if ((p_coordinates=MTOP(MMpull(m)))==NIL)
	{
		x=0;
		y=0;
	}
	else
	{
		x=MTOI(MMfetch(m,p_coordinates,0));
		y=MTOI(MMfetch(m,p_coordinates,1));
	}

	// on inverse les 2 infos de slidebar et les 2 infos de flags
	INVERT( m, 1, 12 );
	INVERT( m, 0, 11 );

	if ( (flags_handler=MTOI( MMpull(m) )) == NIL ) flags_handler = 0;

	if ( (flags_creation=MTOI( MMpull(m) )) == NIL ) flags_creation=OBJ_ENABLE|OBJ_VISIBLE;

	// pile: Chn ObjContainer ObjNode AlphaBitmap [[I I] I AlphaBitmap [I I I]] [[I I] I AlphaBitmap [I I I]] I I I I ObjFont I I [I I I I] [I I] // 15 elts
	
	p_select_settings = MTOP( MMpull( m ) );
	if ( p_select_settings == NIL )
	{
		coeff_transp = 50;
		select_color = 0 + (0 << 8) + (255 << 16); // ndMat: ????? 0 + 0 ?????
	}
	else
	{
		if ( (coeff_transp = MTOI( MMfetch( m, p_select_settings, 1 ) )) == NIL )
			coeff_transp = 50;
		if ( coeff_transp < 0 || coeff_transp > 100 )
		{
			MMechostr(MSKTRACE,"_CRcompTree: selection transparency coefficient must be in [0;100] range\n");
			m->pp += 11;
			MMset(m,0,NIL);
			return 0;
		}
		else
			coeff_transp = 255 * ( 100 - coeff_transp ) / 100;
		if ( (select_color = MTOI( MMfetch( m, p_select_settings, 0 ) )) == NIL)
			select_color = 0 + (0 << 8) + (255 << 16); // ndMat: ????? 0 + 0 ?????
	}

	p_text_settings = MTOP( MMpull( m ) );
	if ( p_text_settings == NIL )
	{
		shadowcolor = 0;
		shadowflag = 0;
		textcolor = 255+(255<<8)+(255<<16);
		text_transp = 255;
	}
	else
	{
		if ( (shadowcolor = MTOI( MMfetch( m, p_text_settings, 3 ) )) == NIL )
			shadowcolor = 0;
		if ( (shadowflag = MTOI( MMfetch( m, p_text_settings, 2 ) )) == NIL )
			shadowflag = 0;
		if ( (text_transp = MTOI( MMfetch( m, p_text_settings, 1 ) )) == NIL )
			text_transp = 0;
		if ( text_transp < 0 || text_transp > 100 )
		{
			MMechostr(MSKTRACE,"_CRcompTree: text transparency coefficient must be in [0;100] range\n");
			m->pp += 10;
			MMset(m,0,NIL);
			return 0;
		}
		else
			text_transp = 255 * ( 100 - text_transp ) / 100;
		if ( (textcolor = MTOI( MMfetch( m, p_text_settings, 0 ) )) == NIL )
			textcolor = 0;
	}

	// pile: Chn ObjContainer ObjNode AlphaBitmap [[I I] I AlphaBitmap [I I I]] [[I I] I AlphaBitmap [I I I]] I I I I ObjFont I I // 13 elts
	space_per_level_shift = MTOI( MMpull( m ) );
	bitmaps_reserved_space = MTOI( MMpull( m ) );

	// pile: Chn ObjContainer ObjNode AlphaBitmap [[I I] I AlphaBitmap [I I I]] [[I I] I AlphaBitmap [I I I]] I I I I ObjFont // 11 elts
	// on inverse width et font
	INVERT( m, 0, 4 );
	if ( (w = MTOI( MMpull( m )) ) == NIL ) w = 0;

	// direction: not used
	MMpull( m );
	itemsvisible = abs( MTOI(MMpull(m)) );

	if ( (h = MTOI( MMpull( m )) ) == NIL ) h = 0;

	// pile: Chn ObjContainer ObjNode AlphaBitmap infos_slidebar_v infos_slidebar_h ObjFont // 7 elts

	// le Chn
	CHECK( MMpush( m, MMget( m, 6 ) ) );
	// on inverse Chn et ObjFont
	INVERT( m, 0, 1 );
	// pile: Chn ObjContainer ObjNode AlphaBitmap infos_slidebar_v infos_slidebar_h Chn ObjFont // 8 elts

	// pour le futur pointeur sur la liste des elements contenus
	CHECK(MMpush(m,NIL));
	// pile: Chn ObjContainer ObjNode AlphaBitmap infos_slidebar_v infos_slidebar_h Chn ObjFont NIL // 9 elts	
	
	for (i=5;i>0;i--)
		INVERT(m,i-1,i);
	// pile: Chn ObjContainer ObjNode infos_slidebar_v infos_slidebar_h Chn ObjFont NIL AlphaBitmap // 9 elts
	INVERT(m,0,2);
	// pile: Chn ObjContainer ObjNode infos_slidebar_v infos_slidebar_h Chn AlphaBitmap NIL ObjFont // 9 elts

	// le container et le pere
	p_objpere=MTOP(MMget(m,6));
	p_container=MTOP(MMget(m,7));
	obj_father=GetObjectBase(m,p_objpere);
	if ((p_objpere!=NIL)&&(GetObjectBase(m,p_objpere)==NULL))
	{
		MMechostr(MSKTRACE,"_CRcompTree: object father already destroyed\n");
		m->pp += 8;
		MMset(m,0,NIL);
		return 0;
	}
	else if ((co=RetrievePtrContainer(m,p_container))==NULL)
	{
		MMechostr(MSKTRACE,"_CRcompTree: container already destroyed\n");
		m->pp += 8;
		MMset(m,0,NIL);
		return 0;
	}
	else if (!co->OwnsObject(obj_father))
	{
		MMechostr(MSKTRACE,"_CRcompTree: father object was not created in this container!\n");
		m->pp += 8;
		MMset(m,0,NIL);
		return 0;
	}
	else
	{
		// evaluation de l'Image
		int transparency = getDifferentColor( 3, select_color, textcolor, shadowcolor );
		Layer *layer = new Layer( w, h, transparency, text_transp != 255 );
		Layer *graphics;
		int elementSize; // espace disponible pour chaque element du contenu

		// pile: Chn ObjContainer ObjNode infos_slidebar_v infos_slidebar_h Chn AlphaBitmap NIL ObjFont // 9 elts	
		p_alphabmp=MTOP( MMget( m, 2 ) );

		//$LB (13/06/2002)
		bmp = (PtrObjBitmap) GET_PTR_OBJ_BITMAP (GET_BMP (p_alphabmp));
		abmp = (PtrObjBitmap) GET_PTR_OBJ_BITMAP (GET_ABMP (p_alphabmp));


		if ((graphicsTransparency = GET_TRANSP(p_alphabmp) ) == NIL) 
			graphicsTransparency = NO_TRANSPARENCY;
		graphics = new Layer(	bmp, 
								abmp, 
								graphicsTransparency 
							);
		
		layer->fillAlphaLayer( text_transp );

		elementSize = h / itemsvisible;

		p_font = MTOP( MMget( m, 0 ) );

		flags_creation |= TRE_LEFT|TRE_V_CENTER;
		// creation du nouvel objet
		new_object=new CObjectTree(co,layer,graphics, x,y,w,h,flags_creation,flags_handler,transparency,text_transp,
								   textcolor, shadowcolor, shadowflag,
								   select_color, coeff_transp,
								   itemsvisible, elementSize, bitmaps_reserved_space, space_per_level_shift,
								   GET_PTR_OBJ_FONT( p_font ) );
	
		obj_root=co->Root();
	
		// pile: Chn ObjContainer ObjNode infos_slidebar_v infos_slidebar_h Chn AlphaBitmap NIL ObjFont // 9 elts
		CHECK( MMpush( m, NIL ) ); // pour le first_item, premier element visible de l'arbre
		INVERT( m, 0, 1 );
		CHECK( MMpush( m, NIL ) ); // pour le clicked_item, premier element visible de l'arbre
		INVERT( m, 0, 1 );
		CHECK( MMpush( m, NIL ) ); // pour le slidebar vertical
		CHECK( MMpush( m, NIL ) ); // pour le slidebar horizontal
		// pile: Chn ObjContainer ObjNode infos_slidebar_v infos_slidebar_h Chn AlphaBitmap NIL NIL NIL ObjFont NIL NIL
		

		// ajout de l'objet et creation du nouvel OBJNODE
		CHECK( AddNode(m,obj_root,obj_father,new_object,7) ); // p_font non utilisé mais tjs stocké pour ne pas le perdre

		// pile: Chn ObjContainer ObjNode infos_slidebar_1 infos_slidebar_2 new_object
		INVERT( m, 0, 3 );
		INVERT( m, 1, 2 );
		MMpull(m);

		// pile: Chn ObjContainer new_object infos_slidebar_h infos_slidebar_v
		// Construction du slide_bar vertical
		if ( MTOP( MMget( m, 0 ) ) != NIL ) 
		{
			CHECK(CreateLinkedScrollBar(m,4,flags_handler,SLB_VERTICAL,1));
			int p_tab=GetTab(m,MTOP(MMget(m,3)));
			MMstore(m,p_tab,SLIDE_V,MMpull(m));
		}		
		// pile : Chn ObjContainer new_object infos_slidebarh infos_slidebarv
		MMpull(m);
		// pile: Chn ObjContainer new_object infos_slidebarh 
		INVERT(m,1,3);
		m->pp += 3;
	}

	return 0;
}

int _DScompTree( mmachine m )
{
	int p;
	if ((p=MTOP(MMpull(m)))!=NIL)
	{
		DsNode(m,p);
		return MMpush(m,0);
	}
	else
		return MMpush(m,NIL);
}

int _CONVERTcompTreeToObjNode( mmachine m )
{
	return 0;
}

// un noeud de l'arbre de description du contenu est composé de: [[S AlphaBitmap] I p_brother p_son p_previous_brother p_father], le I indique l'état (TRE_SHRINK ou TRE_EXPAND)
int _ADDcompTree( mmachine m )
{
	CObjectBase *obj;
	int new_item, p_position;
	int p_obj, p_tab_alloc, p_tablist_alloc, p_tab;
	int tmp_res;

	new_item = MTOP( MMget( m, 0 ) );
	p_position = MTOP( MMget( m, 1 ) );
	p_obj = MTOP( MMget( m, 2 ) );

	if ( p_obj == NIL )
	{
		MMechostr(MSKTRACE,"_ADDcompTree: Object is nil.\n");
		m->pp += 2;
		return 0;
	}
	if ( new_item == NIL )
	{
		MMechostr(MSKTRACE,"_ADDcompTree: element is nil.\n");
		m->pp += 2;
		MMset( m, 0, NIL );
		return 0;
	}

	// pile: CompTree [I r1] [S AlphaBitmap]

	if ( new_item != NIL )
	{
		// on recupere l'objet
		if ((obj=GetObjectBase(m,p_obj))==NULL)
		{
			MMechostr(MSKTRACE,"_ADDcompTree: Object already destroyed.\n");
			m->pp += 2;
			MMset( m, 0, NIL );
			return 0;
		}
		else
		{
			if ((p_tab=GetTab(m,p_obj))!=NIL)
			{
				int p_content = MTOP( MMfetch( m, p_tab, CONTENT ) );

				// on commence par tester si c'est la racine qu'on positionne (on devra modifier le contenu de p_tab)
				if ( p_position == NIL )
				{
					// si il existe deja une racine, l'ajout echoue
					if ( p_content != NIL )
					{
						MMechostr(MSKTRACE,"_ADDcompTree: Root already exists.\n");
						m->pp += 2;
						MMset( m, 0, NIL );
					}
					else
					{
						CHECK( MMpush( m, PTOM( p_tab ) ) );
						// pile: CompTree [I r1] new_item p_tab
						INVERT( m, 0, 1 );

						if (( p_tab_alloc = MMmalloc( m, 2, TYPETAB ) ) < 0 ) return p_tab_alloc;

						new_item = MTOP( MMpull( m ) );

						MMstore( m, p_tab_alloc, 0, MMfetch( m, new_item, 0 ) );
						MMstore( m, p_tab_alloc, 1, MMfetch( m, new_item, 1 ) );

						CHECK( MMpush( m, PTOM( p_tab_alloc ) ) );

						// pile: CompTree [I r1] p_tab p_tab_alloc
						if (( p_tablist_alloc = MMmalloc( m, 6, TYPETAB ) ) < 0 ) return p_tablist_alloc;

						p_tab_alloc = MTOP( MMpull( m ) );
						p_tab = MTOP( MMpull( m ) );
						MMstore( m, p_tablist_alloc, ELEMENT, PTOM( p_tab_alloc ) );
						if ( (static_cast <CObjectTree *> (obj))->fixedRoot() || (static_cast <CObjectTree *> (obj))->hiddenRoot() )
							MMstore( m, p_tablist_alloc, STATE, ITOM( TRE_EXPAND ) );
						else
							MMstore( m, p_tablist_alloc, STATE, ITOM( TRE_SHRINK ) );
						MMstore( m, p_tablist_alloc, BROTHER, NIL );
						MMstore( m, p_tablist_alloc, SON, NIL );
						MMstore( m, p_tablist_alloc, PREVIOUS_BROTHER, NIL );
						MMstore( m, p_tablist_alloc, FATHER, NIL );

						MMstore( m, p_tab, CONTENT, PTOM( p_tablist_alloc ) );
						if ( !(static_cast <CObjectTree *> (obj))->hiddenRoot() )
						{
							// la racine devient le premier element visible
							MMstore( m, p_tab, FIRST_ITEM, PTOM( p_tablist_alloc ) );
							(static_cast <CObjectTree *> (obj))->addItem( 1, 1, 0 );
						}

						// pile: CompTree [I r1]
						MMpull( m );

					}
				}
				else
				{
					if ( p_content == NIL ) // on donne une position autre que la racine (nil) alors que l'arbre est vide
					{
						MMechostr(MSKTRACE,"_ADDcompTree: position cannot be reached.\n");
						m->pp += 2;
						MMset( m, 0, NIL );
					}
					else // cas generique: la position est autre que la racine, laquelle a au moins un fils
					{
						// l'ajout se differencie suivant 2 cas : en tant que 1er fils / en tant que frère
						// p_position et p_content sont initialises
						int add_as_brother = compTreeFindPreviousNode( m, &p_content, p_position, 0 );

						if ( p_content == NIL )
						{
							MMechostr(MSKTRACE,"_ADDcompTree: position cannot be reached.\n");
							m->pp += 2;
							MMset( m, 0, NIL );
						}
						else
						{
							CHECK( MMpush( m, PTOM( p_content ) ) );
							// pile: CompTree [I r1] new_item p_content
							INVERT( m, 0, 1 );

							if (( p_tab_alloc = MMmalloc( m, 2, TYPETAB ) ) < 0 ) return p_tab_alloc;

							new_item = MTOP( MMpull( m ) );

							MMstore( m, p_tab_alloc, 0, MMfetch( m, new_item, 0 ) );
							MMstore( m, p_tab_alloc, 1, MMfetch( m, new_item, 1 ) );

							CHECK( MMpush( m, PTOM( p_tab_alloc ) ) );

							// pile: CompTree [I r1] p_content p_tab_alloc
							if (( p_tablist_alloc = MMmalloc( m, 6, TYPETAB ) ) < 0 ) return p_tablist_alloc;

							p_tab_alloc = MTOP( MMpull( m ) );
							p_content = MTOP( MMpull( m ) );

							MMstore( m, p_tablist_alloc, ELEMENT, PTOM( p_tab_alloc ) );
							MMstore( m, p_tablist_alloc, STATE, ITOM( TRE_SHRINK ) );
							MMstore( m, p_tablist_alloc, SON, NIL ); // pas de fils a l'insertion

//							DEBUGnode( m, p_tab_alloc );

							if ( add_as_brother )
							{
								if ( MMfetch( m, p_content, BROTHER ) != NIL )
									MMstore( m, MTOP( MMfetch( m, p_content, BROTHER ) ), PREVIOUS_BROTHER, PTOM( p_tablist_alloc ) );
								MMstore( m, p_tablist_alloc, BROTHER, MMfetch( m, p_content, BROTHER ) );
								MMstore( m, p_content, BROTHER, PTOM( p_tablist_alloc ) );
								MMstore( m, p_tablist_alloc, FATHER, MMfetch( m, p_content, FATHER ) );
								MMstore( m, p_tablist_alloc, PREVIOUS_BROTHER, PTOM( p_content ) );
//								MMechostr(MSKTRACE,"_ADDcompTree: added as brother to %s\n", MMstartstr( m, MTOP( MMfetch( m, MTOP( MMfetch( m, p_content, 0 ) ), 0 ) ) ));
							}
							else
							{
								MMstore( m, p_tablist_alloc, BROTHER, MMfetch( m, p_content, SON ) );
								if ( MMfetch( m, p_content, SON ) != NIL )
//								{
//									MMechostr(MSKTRACE,"_ADDcompTree added as first son while one already exists.\n");
									MMstore( m, MTOP( MMfetch( m, p_content, SON ) ), PREVIOUS_BROTHER, PTOM( p_tablist_alloc ) );
//								}
								MMstore( m, p_content, SON, PTOM( p_tablist_alloc ) );
								MMstore( m, p_tablist_alloc, FATHER, PTOM( p_content ) );
								MMstore( m, p_tablist_alloc, PREVIOUS_BROTHER, NIL );
//								MMechostr(MSKTRACE,"_ADDcompTree: added as son to %s\n", MMstartstr( m, MTOP( MMfetch( m, MTOP( MMfetch( m, p_content, 0 ) ), 0 ) ) ));
							}

/*							// on corrige le FIRST_ITEM
							p_content = FindNextElement( m, p_tablist_alloc );
							if ( p_content == MTOP( MMfetch( m, p_tab, FIRST_ITEM ) ) )
								MMstore( m, p_tab, FIRST_ITEM, MTOP( p_tablist_alloc ) );
*/
							if ( IsNodeVisible( m, p_tablist_alloc ) )
							{
								(static_cast <CObjectTree *> (obj))->addItem( 1, 1, 0 );

								if ( IsNodeSignificant( m, p_tab, p_tablist_alloc ) )
/*								{
									MMechostr(MSKTRACE,"_ADDcompTree repositionnement fait !\n");
*/									(static_cast <CObjectTree *> (obj))->setFirstItem( 1, 1, 0 );
/*
								}
								else
								{
									MMechostr(MSKTRACE,"_ADDcompTree repositionnement non fait !\n");
//									(static_cast <CObjectTree *> (obj))->setFirstItem( 1, 1, 0 );
								}
*/							}

							if ( (static_cast <CObjectTree *> (obj))->hiddenRoot() )
							{
								if ( MMfetch( m, p_tab, FIRST_ITEM ) == NIL )
									MMstore( m, p_tab, FIRST_ITEM, PTOM( p_tablist_alloc ) );
								if ( (static_cast <CObjectTree *> (obj))->fixedRoot() && MMfetch( m, MTOP( MMfetch( m, p_content, FATHER ) ), FATHER ) == NIL )
									MMstore( m, p_content, STATE, ITOM( TRE_EXPAND ) );
							}

							// pile: CompTree [I r1]
							MMpull( m );
						}
					}
				}
			}
		}
	}

	return 0;
}

int _DELcompTree( mmachine m )
{
	CObjectBase *obj;
	int p_position;
	int p_obj, p_tab;

	p_position = MTOP( MMget( m, 0 ) );
	p_obj = MTOP( MMget( m, 1 ) );

	if ( p_obj == NIL )
	{
		MMechostr(MSKTRACE,"_DELcompTree: Object is nil.\n");
		m->pp += 1;
		return 0;
	}

	// pile: CompTree [I r1]
	// on recupere l'objet
	if ((obj=GetObjectBase(m,p_obj))==NULL)
	{
		MMechostr(MSKTRACE,"_DELcompTree: Object already destroyed.\n");
		m->pp += 1;
		MMset( m, 0, NIL );
		return 0;
	}
	else
	{
		if ((p_tab=GetTab(m,p_obj))!=NIL)
		{
			// on commence par tester si c'est la racine qu'on supprime (on devra supprimer le contenu de p_tab)
			if ( p_position == NIL )
			{
				int move_value = 0;
				int root = MTOP( MMfetch( m, p_tab, CONTENT ) );

				if ( root != NIL )
				{
					if ( !(static_cast <CObjectTree *> (obj))->hiddenRoot() )
						move_value = 1;
					if ( MTOI( MMfetch( m, root, STATE ) ) == TRE_EXPAND )
						move_value += VisibleNodesUnder( m, root );
					(static_cast <CObjectTree *> (obj))->delItem( move_value, 1, 0 );
				}

				MMstore( m, p_tab, FIRST_ITEM, NIL );
				MMstore( m, p_tab, CLICKED_ITEM, NIL );
				MMstore( m, p_tab, CONTENT, NIL );
			}
			else if ( MMfetch( m, p_tab, CONTENT ) == NIL )
				MMechostr(MSKTRACE,"_DELcompTree: tree is empty.\n");
			else
			{
				int p_content = MTOP( MMfetch( m, p_tab, CONTENT ) );
				int rem_as_brother = compTreeFindPreviousNode( m, &p_content, p_position, 1 );

				if ( p_content == NIL )
					MMechostr(MSKTRACE,"_DELcompTree: position cannot be reached.\n");
				else
				{
					int removed_node, check_first;
					int deleted_nodes_count;

					if ( rem_as_brother )
					{
						if ( MMfetch( m, p_content, BROTHER ) == NIL )
						{
							MMechostr(MSKTRACE,"_DELcompTree: position cannot be reached.\n");
							MMpull( m );
							return 0;
						}
						else
						{
							removed_node = MTOP( MMfetch( m, p_content, BROTHER ) );
							if ( IsNodeVisible( m, removed_node ) )
							{
								deleted_nodes_count = 1;
								if ( MTOI( MMfetch( m, removed_node, STATE ) ) == TRE_EXPAND )
									deleted_nodes_count += VisibleNodesUnder( m, removed_node );
							}
							else
								deleted_nodes_count = 0;
							MMstore( m, p_content, BROTHER, MMfetch( m, removed_node, BROTHER ) );

							if ( MMfetch( m, removed_node, BROTHER ) != NIL )
								MMstore( m, MTOP( MMfetch( m, removed_node, BROTHER ) ), PREVIOUS_BROTHER, PTOM( p_content ) );
						}
					}
					else
					{
						if ( MMfetch( m, p_content, SON ) == NIL )
						{
							MMechostr(MSKTRACE,"_DELcompTree: position cannot be reached.\n");
							MMpull( m );
							return 0;
						}
						else
						{
							removed_node = MTOP( MMfetch( m, p_content, SON ) );
							if ( IsNodeVisible( m, removed_node ) )
							{
								deleted_nodes_count = 1;
								if ( MTOI( MMfetch( m, removed_node, STATE ) ) == TRE_EXPAND )
									deleted_nodes_count += VisibleNodesUnder( m, removed_node );
							}
							else
								deleted_nodes_count = 0;
							MMstore( m, p_content, SON, MMfetch( m, removed_node, BROTHER ) );

							if ( MMfetch( m, removed_node, BROTHER ) != NIL )
								MMstore( m, MTOP( MMfetch( m, removed_node, BROTHER ) ), PREVIOUS_BROTHER, NIL );
						}
					}

					if ( deleted_nodes_count != 0 )
						(static_cast <CObjectTree *> (obj))->delItem( deleted_nodes_count, 1, 0 );

					// on corrige FIRST_ITEM du p_tab
					// MAT ICI CURRENT : pb suppression d'un elt au dessus de FIRST_ITEM avec un fils (noeud ouvert)
					if ( IsDescendantNode( m, removed_node, MTOP( MMfetch( m, p_tab, FIRST_ITEM ) ) ) )
					{
						int move_value;
						int next_content = MTOP( MMfetch( m, removed_node, BROTHER ) );

						if ( next_content == NIL )
							next_content = FindFirstUncle( m, removed_node );

						if ( next_content != NIL )
						{
							move_value = VisibleNodesBetween( m, MTOP( MMfetch( m, p_tab, FIRST_ITEM ) ), removed_node );
							MMstore( m, p_tab, FIRST_ITEM, PTOM( next_content ) );
						}
						else
						{
							int previous = FindPreviousElement( m, removed_node );
							move_value = VisibleNodesBetween( m, previous, removed_node );
							MMstore( m, p_tab, FIRST_ITEM, PTOM( previous ) );
						}

						(static_cast <CObjectTree *> (obj))->setFirstItem( -move_value, 1, 0 );
					}

					if ( IsNodeSignificant( m, p_tab, removed_node ) )
						(static_cast <CObjectTree *> (obj))->setFirstItem( -deleted_nodes_count, 1, 0 );

					check_first = CheckFirstNode( m, MTOP( MMfetch( m, p_tab, FIRST_ITEM ) ), (static_cast <CObjectTree *> (obj))->getVisibleItemsCount(), (static_cast <CObjectTree *> (obj))->hiddenRoot() );
					MMstore( m, p_tab, FIRST_ITEM, PTOM( check_first ) );

					// on corrige CLICKED_ITEM du p_tab
					if ( IsDescendantNode( m, removed_node, MTOP( MMfetch( m, p_tab, CLICKED_ITEM ) ) ) )
						MMstore( m, p_tab, CLICKED_ITEM, NIL );
				}
			}
		}
	}

	MMpull( m );
	return 0;
}

// LINK_MOVE: idem _DELcompTree
int _SDELcompTree( mmachine m )
{
	CObjectBase *obj;
	int p_obj, p_tab, p_content, p_text;
	int order;
	char *text;

	order = MTOI( MMpull( m ) );
	p_obj = MTOP( MMget( m, 1 ) );

	if ( p_obj == NIL )
	{
		MMechostr(MSKTRACE,"_SDELcompTree: Object is nil.\n");
		m->pp += 1;
		return 0;
	}

	// pile: CompTree S
	// on recupere l'objet
	if ((obj=GetObjectBase(m,p_obj))==NULL)
	{
		MMechostr(MSKTRACE,"_SDELcompTree: Object already destroyed.\n");
		m->pp += 1;
		MMset( m, 0, NIL );
		return 0;
	}
	else
	{
		if ((p_tab=GetTab(m,p_obj))!=NIL)
		{
			// on recupere l'objet
			p_text = MTOP( MMget( m, 0 ) );
			if ( p_text != NIL )
				text = MMstartstr( m, p_text );
			else
				text = NULL;

			p_content = MTOP( MMfetch( m, p_tab, CONTENT ) );
			if ( MMfetch( m, p_tab, CONTENT ) == NIL )
				MMechostr(MSKTRACE,"_SDELcompTree: tree is empty.\n");
			else
			if ( p_content != NIL )
			{
				// on commence par tester si c'est la racine qu'on positionne (on devra supprimer le contenu de p_tab)
				char *tgt_txt = NULL;
				if ( MMfetch( m, MTOP( MMfetch( m, p_content, ELEMENT ) ), 0 ) != NIL )
					tgt_txt = MMstartstr( m, MTOP( MMfetch( m, MTOP( MMfetch( m, p_content, ELEMENT ) ), 0 ) ) );

				if ( !MYstrcmp( text, tgt_txt ) )
				{
					int move_value = 1;

					if ( MTOI( MMfetch( m, MTOP( MMfetch( m, p_tab, CONTENT ) ), STATE ) ) == TRE_EXPAND )
						 move_value += VisibleNodesUnder( m, MTOP( MMfetch( m, p_tab, CONTENT ) ) );
					(static_cast <CObjectTree *> (obj))->delItem( move_value, 1, 0 );

					MMstore( m, p_tab, CONTENT, NIL );
					MMstore( m, p_tab, FIRST_ITEM, NIL );
					MMstore( m, p_tab, CLICKED_ITEM, NIL );
				}
				else
				{
					int rem_as_brother = compTreeSeekPreviousNode( m, &p_content, text, order );
					if ( p_content == NIL )
						MMechostr(MSKTRACE,"_SDELcompTree: element cannot be found.\n");
					else
					{
						int removed_node, check_first;
						int deleted_nodes_count;
						if ( rem_as_brother )
						{
							removed_node = MTOP( MMfetch( m, p_content, BROTHER ) );
							if ( IsNodeVisible( m, removed_node ) )
							{
								deleted_nodes_count = 1;
								if ( MTOI( MMfetch( m, removed_node, STATE ) ) == TRE_EXPAND )
									deleted_nodes_count += VisibleNodesUnder( m, removed_node );
							}
							else
								deleted_nodes_count = 0;
							MMstore( m, p_content, BROTHER, MMfetch( m, removed_node, BROTHER ) );

							if ( MMfetch( m, removed_node, BROTHER ) != NIL )
								MMstore( m, MTOP( MMfetch( m, removed_node, BROTHER ) ), PREVIOUS_BROTHER, PTOM( p_content ) );
						}
						else
						{
							removed_node = MTOP( MMfetch( m, p_content, SON ) );
							if ( IsNodeVisible( m, removed_node ) )
							{
								deleted_nodes_count = 1;
								if ( MTOI( MMfetch( m, removed_node, STATE ) ) == TRE_EXPAND )
									deleted_nodes_count += VisibleNodesUnder( m, removed_node );
							}
							else
								deleted_nodes_count = 0;
							MMstore( m, p_content, SON, MMfetch( m, removed_node, BROTHER ) );

							if ( MMfetch( m, removed_node, BROTHER ) != NIL )
								MMstore( m, MTOP( MMfetch( m, removed_node, BROTHER ) ), PREVIOUS_BROTHER, NIL );
						}

						if ( deleted_nodes_count != 0 )
							(static_cast <CObjectTree *> (obj))->delItem( deleted_nodes_count, 1, 0 );

						// on corrige FIRST_ITEM du p_tab
						if ( IsDescendantNode( m, removed_node, MTOP( MMfetch( m, p_tab, FIRST_ITEM ) ) ) )
						{
							int move_value;
							int next_content = MTOP( MMfetch( m, removed_node, BROTHER ) );

							if ( next_content == NIL )
								next_content = FindFirstUncle( m, removed_node );

							if ( next_content != NIL )
							{
								move_value = VisibleNodesBetween( m, MTOP( MMfetch( m, p_tab, FIRST_ITEM ) ), removed_node );
								MMstore( m, p_tab, FIRST_ITEM, PTOM( next_content ) );
							}
							else
							{
								int previous = FindPreviousElement( m, removed_node );
								move_value = VisibleNodesBetween( m, previous, removed_node );
								MMstore( m, p_tab, FIRST_ITEM, PTOM( previous ) );
							}

							(static_cast <CObjectTree *> (obj))->setFirstItem( -move_value, 1, 0 );
						}

						check_first = CheckFirstNode( m, MTOP( MMfetch( m, p_tab, FIRST_ITEM ) ), (static_cast <CObjectTree *> (obj))->getVisibleItemsCount(), (static_cast <CObjectTree *> (obj))->hiddenRoot() );
						MMstore( m, p_tab, FIRST_ITEM, PTOM( check_first ) );

						// on corrige CLICKED_ITEM du p_tab
						if ( IsDescendantNode( m, removed_node, MTOP( MMfetch( m, p_tab, CLICKED_ITEM ) ) ) )
							MMstore( m, p_tab, CLICKED_ITEM, NIL );
					}

				}
			}
		}
	}

	MMpull( m );
	return 0;
}

int _RSTcompTree( mmachine m )
{
	int tmp_res;

	CHECK( MMpush( m, NIL ) );
	return _DELcompTree( m );
}

int CompTreeCount( mmachine m, int p_content )
{
	if ( p_content == NIL )
		return 0;
	else
		return 1 + CompTreeCount( m, MTOP( MMfetch( m, p_content, BROTHER ) ) );
}

int _GETcompTreeCount( mmachine m )
{
	CObjectBase *obj;
	int p_position;
	int p_obj, p_tab;

	p_position = MTOP( MMget( m, 0 ) );
	p_obj = MTOP( MMget( m, 1 ) );

	if ( p_obj == NIL )
	{
		MMechostr(MSKTRACE,"_GETcompTreeCount: Object is nil.\n");
		m->pp += 1;
		return 0;
	}

	// pile: CompTree [I r1]
	// on recupere l'objet
	if ((obj=GetObjectBase(m,p_obj))==NULL)
	{
		MMechostr(MSKTRACE,"_GETcompTreeCount: Object already destroyed.\n");
		m->pp += 1;
		MMset( m, 0, NIL );
		return 0;
	}
	else
	{
		if ((p_tab=GetTab(m,p_obj))!=NIL)
		{
			int p_content = MTOP( MMfetch( m, p_tab, CONTENT ) );

			if ( p_position != NIL )
				p_content = compTreeFindNode( m, p_content, p_position );

			MMpull( m );

			if ( p_content == NIL )
			{
				MMechostr(MSKTRACE,"_GETcompTreeCount: position cannot be reached.\n");
				MMset( m, 0, NIL );
			}
			else
				MMset( m, 0, ITOM( CompTreeCount( m, MTOP( MMfetch( m, p_content, SON ) ) ) ) );
		}
	}

	return 0;
}

int _GETcompTreeValue( mmachine m )
{
	CObjectBase *obj;
	int p_position;
	int p_obj, p_tab;

	p_position = MTOP( MMget( m, 0 ) );
	p_obj = MTOP( MMget( m, 1 ) );

	if ( p_obj == NIL )
	{
		MMechostr(MSKTRACE,"_GETcompTreeValue: Object is nil.\n");
		m->pp += 1;
		return 0;
	}

	// pile: CompTree [I r1]
	// on recupere l'objet
	if ((obj=GetObjectBase(m,p_obj))==NULL)
	{
		MMechostr(MSKTRACE,"_GETcompTreeValue: Object already destroyed.\n");
		m->pp += 1;
		MMset( m, 0, NIL );
		return 0;
	}
	else
	{
		if ((p_tab=GetTab(m,p_obj))!=NIL)
		{
			int p_content = MTOP( MMfetch( m, p_tab, CONTENT ) );

			if ( p_position != NIL )
				p_content = compTreeFindNode( m, p_content, p_position );

			MMpull( m );

			if ( p_content == NIL )
			{
				MMechostr(MSKTRACE,"_GETcompTreeValue: position cannot be reached.\n");
				MMset( m, 0, NIL );
			}
			else
				MMset( m, 0, MMfetch( m, p_content, ELEMENT ) );
		}
	}

	return 0;
}

int _SETcompTreeValue( mmachine m )
{
	CObjectBase *obj;
	int p_position, new_item;
	int p_obj, p_tab;
	int tmp_res;

	INVERT( m, 0, 1 );
	p_position = MTOP( MMget( m, 0 ) );
	new_item = MTOP( MMget( m, 1 ) );
	p_obj = MTOP( MMget( m, 2 ) );

	if ( p_obj == NIL )
	{
		MMechostr(MSKTRACE,"_SETcompTreeValue: Object is nil.\n");
		m->pp += 2;
		return 0;
	}
	if ( new_item == NIL )
	{
		m->pp += 2;
		MMset( m, 0, NIL );
		return 0;
	}

	// pile: CompTree [S AlphaBitmap] [I r1]
	// on recupere l'objet
	if ((obj=GetObjectBase(m,p_obj))==NULL)
	{
		MMechostr(MSKTRACE,"_SETcompTreeValue: Object already destroyed.\n");
		m->pp += 2;
		MMset( m, 0, NIL );
		return 0;
	}
	else
	{
		if ((p_tab=GetTab(m,p_obj))!=NIL)
		{
			int p_content = MTOP( MMfetch( m, p_tab, CONTENT ) );

			if ( p_position != NIL )
				p_content = compTreeFindNode( m, p_content, p_position );

			MMpull( m );
			MMpull( m );

			if ( p_content == NIL )
				MMechostr(MSKTRACE,"_SETcompTreeValue: position cannot be reached.\n");
			else
			{
				MMstore( m, MTOP( MMfetch( m, p_content, ELEMENT ) ), 0, MMfetch( m, new_item, 0 ) );
				MMstore( m, MTOP( MMfetch( m, p_content, ELEMENT ) ), 1, MMfetch( m, new_item, 1 ) );
			}
		}
	}

	return 0;
}

int _SSETcompTreeValue( mmachine m )
{
	CObjectBase *obj;
	int new_item;
	int p_obj, p_tab, p_content, p_text;
	int tmp_res;
	int order;
	char *text;

	// pile: CompTree S I new_item
	INVERT( m, 0, 1 );
	order = MTOI( MMpull( m ) );
	// pile: CompTree S new_item
	INVERT( m, 0, 1 );
	new_item = MTOP( MMget( m, 1 ) );
	p_obj = MTOP( MMget( m, 2 ) );

	if ( p_obj == NIL )
	{
		MMechostr(MSKTRACE,"_SSETcompTreeValue: Object is nil.\n");
		m->pp += 2;
		return 0;
	}
	if ( new_item == NIL )
	{
		m->pp += 2;
		MMset( m, 0, NIL );
		return 0;
	}

	// pile: CompTree [S AlphaBitmap] S
	// on recupere l'objet
	if ((obj=GetObjectBase(m,p_obj))==NULL)
	{
		MMechostr(MSKTRACE,"_SSETcompTreeValue: Object already destroyed.\n");
		m->pp += 2;
		MMset( m, 0, NIL );
		return 0;
	}
	else
	{
		if ((p_tab=GetTab(m,p_obj))!=NIL)
		{
			// on recupere l'objet
			p_text = MTOP( MMget( m, 0 ) );
			if ( p_text != NIL )
				text = MMstartstr( m, p_text );
			else
				text = NULL;

			p_content = MTOP( MMfetch( m, p_tab, CONTENT ) );
			p_content = compTreeSeekNode( m, p_content, text, order );

			if ( p_content == NIL )
				MMechostr(MSKTRACE,"_SSETcompTreeValue: element cannot be found.\n");
			else
			{
				MMstore( m, MTOP( MMfetch( m, p_content, ELEMENT ) ), 0, MMfetch( m, new_item, 0 ) );
				MMstore( m, MTOP( MMfetch( m, p_content, ELEMENT ) ), 1, MMfetch( m, new_item, 1 ) );
			}
		}
	}

	MMpull( m );
	MMpull( m );
	return 0;
}

int _SETcompTreeFirst( mmachine m )
{
	CObjectBase *obj;
	int p_position;
	int p_obj, p_tab;

	p_position = MTOP( MMget( m, 0 ) );
	p_obj = MTOP( MMget( m, 1 ) );

	if ( p_obj == NIL )
	{
		MMechostr(MSKTRACE,"_SETcompTreeFirst: Object is nil.\n");
		m->pp += 1;
		return 0;
	}

	// pile: CompTree [I r1]
	// on recupere l'objet
	if ((obj=GetObjectBase(m,p_obj))==NULL)
	{
		MMechostr(MSKTRACE,"_SETcompTreeFirst: Object already destroyed.\n");
		m->pp += 1;
		MMset( m, 0, NIL);
	}
	else
	{
		if ((p_tab=GetTab(m,p_obj))!=NIL)
		{
			int p_content;
			
			p_content = MTOP( MMfetch( m, p_tab, CONTENT ) );

			if ( p_position != NIL )
				p_content = compTreeFindNode( m, p_content, p_position );

			MMpull( m );

			if ( (p_content == NIL) && (p_position != NIL) )
				MMechostr(MSKTRACE,"_SETcompTreeFirst: position cannot be reached.\n");
			else
			{
				if ( p_content != NIL )
				{
					int value;
					int old_p_content = MTOP( MMfetch( m, p_tab, FIRST_ITEM ) );

					p_content = FindFirstVisibleAscendantNode( m, p_content, p_content );

					value = VisibleNodesBetween( m, old_p_content, p_content );
					if ( value > 0 )
					{
						value = SetFirstItemForwards( m, p_tab, (static_cast <CObjectTree *> (obj))->getVisibleItemsCount(), (static_cast <CObjectTree *> (obj))->hiddenRoot(), value );
						(static_cast <CObjectTree *> (obj))->setFirstItem( value, 1, 0 );
					}
					else
					{
						value = VisibleNodesBetween( m, p_content, old_p_content );
						if ( value > 0 )
						{
							value = SetFirstItemBackwards( m, p_tab, (static_cast <CObjectTree *> (obj))->hiddenRoot(), value );
							(static_cast <CObjectTree *> (obj))->setFirstItem( -value, 1, 0 );
						}
//						else
//							MMechostr(MSKTRACE,"_SETcompTreeFirst: fatal error.\n");
					}

				}
			}
		}
	}

	return 0;
}

int _SSETcompTreeFirst( mmachine m )
{
	CObjectBase *obj;
	int p_obj, p_tab, p_content, p_text, p_content_seek;
	int order;
	char *text;

	order = MTOI( MMpull( m ) );
	p_obj = MTOP( MMget( m, 1 ) );

	if ( p_obj == NIL )
	{
		MMechostr(MSKTRACE,"_SSETcompTreeFirst: Object is nil.\n");
		m->pp += 1;
		return 0;
	}

	// pile: CompTree S
	// on recupere l'objet
	if ((obj=GetObjectBase(m,p_obj))==NULL)
	{
		MMechostr(MSKTRACE,"_SSETcompTreeFirst: Object already destroyed.\n");
		m->pp += 1;
		MMset( m, 0, NIL );
		return 0;
	}
	else
	{
		if ((p_tab=GetTab(m,p_obj))!=NIL)
		{
			p_text = MTOP( MMget( m, 0 ) );
			if ( p_text != NIL )
				text = MMstartstr( m, p_text );
			else
				text = NULL;

			p_content = MTOP( MMfetch( m, p_tab, CONTENT ) );

			if ( p_content != NIL )
			{

				// on commence par tester si c'est la racine qu'on positionne
				char *tgt_txt = NULL;
				if ( MMfetch( m, MTOP( MMfetch( m, p_content, ELEMENT ) ), 0 ) != NIL )
					tgt_txt = MMstartstr( m, MTOP( MMfetch( m, MTOP( MMfetch( m, p_content, ELEMENT ) ), 0 ) ) );

				if ( !MYstrcmp( text, tgt_txt ) )
					MMstore( m, p_tab, FIRST_ITEM, PTOM( p_content ) );
				else
				{
					p_content_seek = compTreeSeekNode( m, p_content, text, order );

					if ( p_content_seek == NIL )
						MMechostr(MSKTRACE,"_SSETcompTreeFirst: element cannot be found.\n");
					else
						MMstore( m, p_tab, FIRST_ITEM, PTOM( p_content_seek ) );
				}
			}
		}
	}

	MMpull( m );
	return 0;
}


int _GETcompTreeState( mmachine m )
{
	CObjectBase *obj;
	int p_position;
	int p_obj, p_tab;

	p_position = MTOP( MMget( m, 0 ) );
	p_obj = MTOP( MMget( m, 1 ) );

	if ( p_obj == NIL )
	{
		MMechostr(MSKTRACE,"_GETcompTreeState: Object is nil.\n");
		m->pp += 1;
		return 0;
	}

	// pile: CompTree [I r1]
	// on recupere l'objet
	if ((obj=GetObjectBase(m,p_obj))==NULL)
	{
		MMechostr(MSKTRACE,"_GETcompTreeState: Object already destroyed.\n");
		m->pp += 1;
		MMset( m, 0, NIL );
		return 0;
	}
	else
	{
		if ((p_tab=GetTab(m,p_obj))!=NIL)
		{
			int p_content = MTOP( MMfetch( m, p_tab, CONTENT ) );

			if ( p_position != NIL )
				p_content = compTreeFindNode( m, p_content, p_position );

			MMpull( m );

			if ( p_content == NIL )
				MMechostr(MSKTRACE,"_GETcompTreeState: position cannot be reached.\n");
			else
				MMset( m, 0, ITOM( MMfetch( m, p_content, STATE ) ) );
		}
	}

	return 0;
}

int _SETcompTreeState( mmachine m )
{
	CObjectBase *obj;
	int p_position, new_state;
	int p_obj, p_tab;

	new_state = MTOI( MMpull( m ) );
	p_position = MTOP( MMget( m, 0 ) );
	p_obj = MTOP( MMget( m, 1 ) );

	if ( p_obj == NIL )
	{
		MMechostr(MSKTRACE,"_SETcompTreeState: Object is nil.\n");
		m->pp += 1;
		return 0;
	}
	if ( new_state == NIL )
		new_state = TRE_SHRINK;

	// pile: CompTree [S AlphaBitmap] [I r1]
	// on recupere l'objet
	if ((obj=GetObjectBase(m,p_obj))==NULL)
	{
		MMechostr(MSKTRACE,"_SETcompTreeState: Object already destroyed.\n");
		m->pp += 1;
		MMset( m, 0, NIL );
		return 0;
	}
	else
	{
		if ((p_tab=GetTab(m,p_obj))!=NIL)
		{
			int p_content = MTOP( MMfetch( m, p_tab, CONTENT ) );

			if ( p_position != NIL )
				p_content = compTreeFindNode( m, p_content, p_position );

			MMpull( m );

			if ( p_content == NIL )
				MMechostr(MSKTRACE,"_SETcompTreeState: position cannot be reached.\n");
			// cas TRE_HIDDEN_ROOT ou TRE_FIXED_ROOT et racine concernee
			// ou cas TRE_HIDDEN_ROOT et TRE_FIXED_ROOT et racine ou fils 1ere generation
			else if ( ( MMfetch( m, p_content, FATHER ) == NIL 
						&& ( (static_cast <CObjectTree *> (obj))->hiddenRoot() || (static_cast <CObjectTree *> (obj))->fixedRoot()) )
				   || ( MMfetch( m, MTOP( MMfetch( m, p_content, FATHER ) ), FATHER ) == NIL
						&& (static_cast <CObjectTree *> (obj))->hiddenRoot() && (static_cast <CObjectTree *> (obj))->fixedRoot()) )
				return 0;
			else if ( new_state != MTOI( MMfetch( m, p_content, STATE ) ) )
			{
				// si le premier noeud courant (FIRST_ITEM) est descendant du noeud ferme, on place ce dernier comme FIRST_ITEM
				if ( new_state == TRE_SHRINK && IsDescendantNode( m, p_content, MTOP( MMfetch( m, p_tab, FIRST_ITEM ) ) ) )
				{
					int move_value = - VisibleNodesBetween( m, p_content, MTOP( MMfetch( m, p_tab, FIRST_ITEM ) ) );
					(static_cast <CObjectTree *> (obj))->setFirstItem( move_value, 1, 0 );

					MMstore( m, p_tab, FIRST_ITEM, PTOM( p_content ) );
				}

				if ( IsNodeVisible( m, p_content ) )
				{
//					MMechostr(MSKTRACE,"_SETcompTreeState: noeud visible !!\n");
					if ( new_state == TRE_EXPAND )
						(static_cast <CObjectTree *> (obj))->addItem( VisibleNodesUnder( m, p_content ), 1, 0 );
					else
						(static_cast <CObjectTree *> (obj))->delItem( VisibleNodesUnder( m, p_content ), 1, 0 );
				}
//				else
//					MMechostr(MSKTRACE,"_SETcompTreeState: noeud invisible...\n");
				MMstore( m, p_content, STATE, ITOM( new_state ) );

				if ( new_state == TRE_SHRINK )
				{
					int check_first = CheckFirstNode( m, MTOP( MMfetch( m, p_tab, FIRST_ITEM ) ), (static_cast <CObjectTree *> (obj))->getVisibleItemsCount(), (static_cast <CObjectTree *> (obj))->hiddenRoot() );
					MMstore( m, p_tab, FIRST_ITEM, PTOM( check_first ) );
				}
			}
		}
	}

	return 0;
}

int _GETcompTreeClicked( mmachine m )
{
	CObjectBase *obj;
	int p_obj, p_tab;
	int tmp_res;

	p_obj = MTOP( MMget( m, 0 ) );

	if ( p_obj == NIL )
	{
		MMechostr(MSKTRACE,"_GETcompTreeClicked: Object is nil.\n");
		return 0;
	}

	// pile: CompTree
	// on recupere l'objet
	if ((obj=GetObjectBase(m,p_obj))==NULL)
	{
		MMechostr(MSKTRACE,"_GETcompTreeClicked: Object already destroyed.\n");
		MMset( m, 0, NIL );
		return 0;
	}
	else
	{
		if ((p_tab=GetTab(m,p_obj))!=NIL)
		{
			if ( MMfetch( mm, p_tab, CLICKED_ITEM ) == NIL )
				MMset( m, 0, NIL );
			else
			{
				CHECK( MMpush( m, PTOM( p_tab ) ) );

				// on empile CLICKED_ITEM
				p_tab = MTOP( MMget( m, 0 ) );
				CHECK( MMpush( mm, MMfetch( mm, p_tab, CLICKED_ITEM ) ) );
				CHECK( MMpush( mm, NIL ) );
				CHECK( BuildPositionFromNode( mm ) );
				// on retire CLICKED_ITEM
				INVERT( mm, 0, 1 );
				MMpull( mm );

				// pile: CompTree p_tab [I r1]
				p_tab = MTOP( MMget( m, 1 ) );
				CHECK( MMpush( m, MMfetch( m, MTOP( MMfetch( mm, p_tab, CLICKED_ITEM ) ), ELEMENT ) ) );
				CHECK( MMpush( m, ITOM( 2 ) ) );
				CHECK( MBdeftab( m ) );

				// pile : CompTree p_tab [[I r1] [S AlphaBitmap]]
				INVERT( m, 0, 2 );
				MMpull( m );
				MMpull( m );
			}
		}
	}

	return 0;
}

int _SETcompTreeClicked( mmachine m )
{
	CObjectBase *obj;
	int p_position;
	int p_obj, p_tab;

	p_position = MTOP( MMget( m, 0 ) );
	p_obj = MTOP( MMget( m, 1 ) );

	if ( p_obj == NIL )
	{
		MMechostr(MSKTRACE,"_SETcompTreeClicked: Object is nil.\n");
		m->pp += 1;
		return 0;
	}

	// pile: CompTree [I r1]
	// on recupere l'objet
	if ((obj=GetObjectBase(m,p_obj))==NULL)
	{
		MMechostr(MSKTRACE,"_SETcompTreeClicked: Object already destroyed.\n");
		m->pp += 1;
		MMset( m, 0, NIL );
		return 0;
	}
	else
	{
		if ((p_tab=GetTab(m,p_obj))!=NIL)
		{
			int p_content = MTOP( MMfetch( m, p_tab, CONTENT ) );

			if ( p_position != NIL )
				p_content = compTreeFindNode( m, p_content, p_position );

			MMpull( m );

			if ( p_content == NIL )
				MMstore( m, p_tab, CLICKED_ITEM, NIL ); // si le chemin est non valide on déselectionne
			else
				MMstore( m, p_tab, CLICKED_ITEM, PTOM( p_content ) );
		}
	}

	return 0;
}

int _SSETcompTreeClicked( mmachine m )
{
	CObjectBase *obj;
	int order;
	char *text;
	int p_obj, p_tab, p_text, p_content, p_content_seek;

	order = MTOI( MMpull( m ) );
	p_obj = MTOP( MMget( m, 1 ) );

	if ( p_obj == NIL )
	{
		MMechostr(MSKTRACE,"_SSETcompTreeClicked: Object is nil.\n");
		m->pp += 1;
		return 0;
	}

	// pile: CompTree S I
	// on recupere l'objet
	if ((obj=GetObjectBase(m,p_obj))==NULL)
	{
		MMechostr(MSKTRACE,"_SSETcompTreeClicked: Object already destroyed.\n");
		m->pp += 1;
		MMset( m, 0, NIL );
		return 0;
	}
	else
	{
		if ((p_tab=GetTab(m,p_obj))!=NIL)
		{
			p_text = MTOP( MMget( m, 0 ) );
			if ( p_text != NIL )
				text = MMstartstr( m, p_text );
			else
				text = NULL;

			p_content = MTOP( MMfetch( m, p_tab, CONTENT ) );

			if ( p_content != NIL )
			{
				// on commence par tester si c'est la racine qu'on positionne
				char *tgt_txt = NULL;
				if ( MMfetch( m, MTOP( MMfetch( m, p_content, ELEMENT ) ), 0 ) != NIL )
					tgt_txt = MMstartstr( m, MTOP( MMfetch( m, MTOP( MMfetch( m, p_content, ELEMENT ) ), 0 ) ) );

				if ( !MYstrcmp( text, tgt_txt ) )
					MMstore( m, p_tab, CLICKED_ITEM, PTOM( p_content ) );
				else
				{
					p_content_seek = compTreeSeekNode( m, p_content, text, order );

					if ( p_content_seek == NIL )
						MMechostr(MSKTRACE,"_SSETcompTreeClicked: element cannot be found.\n");
					else
						MMstore( m, p_tab, CLICKED_ITEM, PTOM( p_content_seek ) );
				}
			}
			else
				MMechostr(MSKTRACE,"_SSETcompTreeClicked: tree is empty.\n");

			MMpull( m );
		}
	}

	return 0;
}

int MakeErrorPosition( mmachine m )
{
	int tmp_res;

	CHECK( MMpush( m, ITOM( -1 ) ) );
	CHECK( MMpush( m, NIL ) );
	CHECK( MMpush( m, ITOM( 2 ) ) );
	CHECK( MBdeftab( m ) );

	return 0;
}

int _POScompTree(mmachine m)
{
	CObjectBase *obj;
	int order;
	char *text;
	int p_obj, p_tab, p_text, p_content, p_content_seek, p_position;
	int tmp_res;

	order = MTOI( MMpull( m ) );
	p_obj = MTOP( MMget( m, 2 ) );

	if ( p_obj == NIL )
	{
		MMechostr(MSKTRACE,"_POScompTree: Object is nil.\n");
		m->pp += 2;
		return 0;
	}

	// pile: CompTree [I r1] S
	// on recupere l'objet
	if ((obj=GetObjectBase(m,p_obj))==NULL)
	{
		MMechostr(MSKTRACE,"_POScompTree: Object already destroyed.\n");
		m->pp += 2;
		MMset( m, 0, NIL );
		return 0;
	}
	else
	{
		if ((p_tab=GetTab(m,p_obj))!=NIL)
		{
			p_text = MTOP( MMpull( m ) );
			p_position = MTOP( MMpull( m ) );
			// pile: CompTree

			if ( p_text != NIL )
				text = MMstartstr( m, p_text );
			else
				text = NULL;

			if ( p_position == NIL )
				p_content = MTOP( MMfetch( m, p_tab, CONTENT ) );
			else
				p_content = compTreeFindNode( m, MTOP( MMfetch( m, p_tab, CONTENT ) ), p_position );

			if ( p_content != NIL )
			{
				p_content_seek = compTreeSeekNode( m, p_content, text, order );

				if ( p_content_seek == NIL )
				{
					MMechostr(MSKTRACE,"_POScompTree: element cannot be found.\n");
					MMpull( m );
					MakeErrorPosition( m );
//					MMset( m, 0, NIL );
					return 0;
				}
				else
				{
					CHECK( MMpush( m, PTOM( p_content_seek ) ) );
					CHECK( MMpush( m, NIL ) );
					CHECK( BuildPositionFromNode( m ) );
					INVERT( m, 0, 2 );
					MMpull( m );
					MMpull( m );
				}
			}
			else
			{
				MMechostr(MSKTRACE,"_POScompTree: start node cannot be found.\n");
				MMpull( m );
				MakeErrorPosition( m );
				return 0;
			}
		}
	}

	return 0;
}


int _CBcompTreeClick(mmachine m)
{
	return OBJaddreflex( m, OBJNODE, RFLOBJNODE_CLICK );
}

int _CBcompTreeDblClick(mmachine m)
{
	return OBJaddreflex( m, OBJNODE, RFLOBJNODE_DBLCLICK );
}

int _CBcompTreeStateChanged(mmachine m)
{
	return OBJaddreflex( m, OBJNODE, RFLOBJNODE_CHANGE );
}

int _CBcompTreeResizeResource(mmachine m)
{
	int p_obj,tmp_res;
	if ((p_obj=MTOP(MMget(m,4)))==NIL)
	{
		MMechostr(MSKTRACE,"_CBcompTreeResizeResource: CompTree is nil\n");
		m->pp += 4;
		MMset(m,0,NIL);
	}
	else if (GetObjectBase(m,p_obj)==NULL)
	{
		MMechostr(MSKTRACE,"_CBcompTreeResizeResource: CompTree already destroyed\n");
		m->pp += 4;
		MMset(m,0,NIL);
	}
	else
	{
		// pile  CompTree fun1 p1 fun2 p2
		int p_tab=GetTab(m,p_obj);
		CHECK(MMpush(m,MMfetch(m,p_tab,SLIDE_H)));
		INVERT(m,0,1);
		INVERT(m,1,2);
		// pile CompTree fun1 p1 slb2 fun2 p2
		CHECK(_CBcompSlideBarResizeResource(m));
		MMpull(m);

		//pile CompTree fun1 p1
		p_tab=GetTab(m,MTOP(MMget(m,2)));
		CHECK(MMpush(m,MMfetch(m,p_tab,SLIDE_V)));
		INVERT(m,0,1);
		INVERT(m,1,2);
		CHECK(_CBcompSlideBarResizeResource(m));
		MMpull(m);
		
		//pile CompTree
	}
	return 0;
}

int _CBcompTreeResize(mmachine m)
{
	int p_obj,tmp_res;
	if ((p_obj=MTOP(MMget(m,6)))==NIL)
	{
		MMechostr(MSKTRACE,"_CBcompTreeResize: CompTree is nil\n");
		m->pp += 6;
		MMset(m,0,NIL);
	}
	else if (GetObjectBase(m,p_obj)==NULL)
	{
		MMechostr(MSKTRACE,"_CBcompTreeResize: CompTree already destroyed\n");
		m->pp += 6;
		MMset(m,0,NIL);
	}
	else
	{
		// pile  CompTree fun1 p1 fun2 p2 fun3 u3
		int p_tab=GetTab(m,p_obj);
		CHECK(MMpush(m,MMfetch(m,p_tab,SLIDE_H)));
		INVERT(m,0,1);
		INVERT(m,1,2);
		
		// pile CompTree fun1 p1 slb fun2 p2
		CHECK(_CBcompSlideBarResize(m));
		MMpull(m);
		
		//pile CompTree fun1 p1 fun2 p2
		p_tab=GetTab(m,MTOP(MMget(m,4)));
		CHECK(MMpush(m,MMfetch(m,p_tab,SLIDE_V)));
		INVERT(m,0,1);
		INVERT(m,1,2);
		
		// pile CompTree fun1 p1 slb fun2 p2
		CHECK(_CBcompSlideBarResize(m));
		MMpull(m);
		
		return OBJaddreflex(m,OBJNODE,RFLOBJNODE_RESIZE);
	}
	return 0;
}




