/***********************************************************/
	/*					genliste.cpp						   */
	/* Module de generation de listing de composants, labels.. */
	/***********************************************************/

#include "fctsys.h"
#include "gr_basic.h"

#include "common.h"
#include "program.h"
#include "libcmp.h"
#include "general.h"
#include "netlist.h"

#include "protos.h"

/* Structures pour memo et liste des elements */
typedef struct ListLabel
{
	int Type();
	void * m_Label;
	int m_SheetNumber;
} ListLabel;


/* fonctions locales */
static void GenereListeOfItems(wxWindow * frame, const wxString & FullFileName);
static int GenListeGLabels( ListLabel * List );
static int ListTriComposantByRef(EDA_SchComponentStruct **Objet1,
									EDA_SchComponentStruct **Objet2);
static int ListTriComposantByVal(EDA_SchComponentStruct **Objet1,
									EDA_SchComponentStruct **Objet2);
static int ListTriGLabelBySheet(ListLabel *Objet1, ListLabel *Objet2);
static int ListTriGLabelByVal(ListLabel *Objet1, ListLabel *Objet2);
static void DeleteSubCmp( EDA_BaseStruct ** List , int NbItems);

static int PrintListeCmpByRef( FILE *f, EDA_BaseStruct **List, int NbItems);
static int PrintListeCmpByVal( FILE *f, EDA_BaseStruct **List, int NbItems);

static int PrintListeGLabel( FILE *f, ListLabel *List, int NbItems);

// Constantes:
#define LISTCMP_BY_REF 1
#define LISTCMP_BY_VAL 2
#define LIST_SUBCMP 4
#define LIST_HPINS_BY_NAME 8
#define LIST_HPINS_BY_SHEET 0x10

/* Variable locales */
int ItemsToList = LISTCMP_BY_REF | LISTCMP_BY_VAL;

enum id_tools {
	ID_TOOLS_GEN_LIST = 1500,
	ID_EXIT_TOOLS,
	ID_SET_FILENAME_LIST
};

/* Classe de la frame de gestion de l'annotation */
class WinEDA_GenCmpListFrame: public wxDialog
{
public:
	WinEDA_DrawFrame * m_Parent;

	wxCheckBox * m_ListCmpbyRefItems;
	wxCheckBox * m_ListCmpbyValItems;
	wxCheckBox * m_ListSubCmpItems;
	wxCheckBox * m_GenListLabelsbyVal;
	wxCheckBox * m_GenListLabelsbySheet;
	wxString m_LibArchiveFileName;
	wxString m_ListFileName;

	// Constructor and destructor
	WinEDA_GenCmpListFrame(WinEDA_DrawFrame *parent, wxPoint& pos);
	~WinEDA_GenCmpListFrame() {};

	void GenList(wxCommandEvent& event);
	void GenListUpdateOpt();
	void ToolsExit(wxCommandEvent& event);

	DECLARE_EVENT_TABLE()
};

BEGIN_EVENT_TABLE(WinEDA_GenCmpListFrame, wxDialog)
	EVT_BUTTON(ID_EXIT_TOOLS, WinEDA_GenCmpListFrame::ToolsExit)
	EVT_BUTTON(ID_TOOLS_GEN_LIST, WinEDA_GenCmpListFrame::GenList)
END_EVENT_TABLE()


/***************************************************************/
void InstallToolsFrame(WinEDA_DrawFrame *parent, wxPoint & pos)
/***************************************************************/
{
	WinEDA_GenCmpListFrame * frame = new WinEDA_GenCmpListFrame(parent, pos);
	frame->ShowModal(); frame->Destroy();
}


/*****************************************************************************/
WinEDA_GenCmpListFrame::WinEDA_GenCmpListFrame(WinEDA_DrawFrame *parent,
		wxPoint& framepos):
		wxDialog(parent, -1, _("List of Material"), framepos, wxSize(360, 200),
			 DIALOG_STYLE)
/*****************************************************************************/
{
wxPoint pos;

	m_Parent = parent;
	SetFont(*g_DialogFont);

	if ( (framepos.x == -1) && (framepos.x == -1) ) Centre();

	/* Calcul du nom du fichier d'archivage */
	m_LibArchiveFileName = MakeFileName(wxEmptyString,ScreenSch->m_FileName,wxEmptyString);
	/* mise a jour extension  */
	ChangeFileNameExt(m_LibArchiveFileName, g_LibExtBuffer);

	/* Calcul du nom du fichier de listage */
	m_ListFileName = MakeFileName(wxEmptyString,ScreenSch->m_FileName,wxEmptyString);
	/* mise a jour extension  */
	ChangeFileNameExt(m_ListFileName, wxT(".lst"));

	pos.x = 5; pos.y = 15;
	new wxStaticBox(this, -1,_(" List items: "), pos, wxSize(230, 120));

	pos.x = 10; pos.y += 22;
	m_ListCmpbyRefItems = new wxCheckBox(this,-1, _("Components by Reference"), pos);
	if ( ItemsToList & LISTCMP_BY_REF ) m_ListCmpbyRefItems->SetValue(TRUE);
	pos.y += 17;
	m_ListCmpbyValItems = new wxCheckBox(this,-1, _("Components by Value"), pos);
	if ( ItemsToList & LISTCMP_BY_VAL ) m_ListCmpbyValItems->SetValue(TRUE);
	pos.y += 17;
	m_ListSubCmpItems = new  wxCheckBox(this,-1, _("Sub Components (i.e U2A, U2B..)"), pos);
	if ( ItemsToList & LIST_SUBCMP ) m_ListSubCmpItems->SetValue(TRUE);
	pos.y += 17;
	m_GenListLabelsbyVal = new wxCheckBox(this,-1, _("Hierachy Pins by name"), pos);
	if ( ItemsToList & LIST_HPINS_BY_NAME ) m_GenListLabelsbyVal->SetValue(TRUE);
	pos.y += 17;
	m_GenListLabelsbySheet = new wxCheckBox(this,-1, _("Hierachy Pins by Sheets"), pos);
	if ( ItemsToList & LIST_HPINS_BY_SHEET ) m_GenListLabelsbySheet->SetValue(TRUE);

	/* Creation des boutons de commande */
	pos.x = 260; pos.y = 40;
	wxButton * Button = new wxButton(this, ID_TOOLS_GEN_LIST,
						_("&Create List"), pos);
	Button->SetForegroundColour(*wxRED);

	pos.y += Button->GetSize().y + 5;
	Button = new wxButton(this,	ID_EXIT_TOOLS,
						_("&Exit"), pos);
	Button->SetForegroundColour(*wxBLUE);
}

// Fonctions de positionnement des variables d'option
void WinEDA_GenCmpListFrame::GenListUpdateOpt()
{
	ItemsToList = 0;
	if ( m_ListCmpbyRefItems->GetValue() ) ItemsToList |= LISTCMP_BY_REF;
	if ( m_ListCmpbyValItems->GetValue() ) ItemsToList |= LISTCMP_BY_VAL;
	if ( m_ListSubCmpItems->GetValue() ) ItemsToList |= LIST_SUBCMP;
	if ( m_GenListLabelsbyVal->GetValue() ) ItemsToList |= LIST_HPINS_BY_NAME;
	if ( m_GenListLabelsbySheet->GetValue() ) ItemsToList |= LIST_HPINS_BY_SHEET;
}

void WinEDA_GenCmpListFrame::GenList(wxCommandEvent& event)
{
#define EXT_LIST wxT(".lst")
wxString mask, filename;

	GenListUpdateOpt();

	mask = wxT("*"); mask += EXT_LIST;
	filename = EDA_FileSelector(_("List of material:"),
					wxEmptyString,				/* Chemin par defaut (ici dir courante) */
					m_ListFileName,	/* nom fichier par defaut, et resultat */
					EXT_LIST,		/* extension par defaut */
					mask,			/* Masque d'affichage */
					this,
					wxSAVE,
					TRUE
					);
	if ( filename.IsEmpty() ) return;
	else m_ListFileName = filename;

	GenereListeOfItems(this, m_ListFileName);
	Close();
}

void WinEDA_GenCmpListFrame::ToolsExit(wxCommandEvent& event)
{
	GenListUpdateOpt();
	Close();
}



/***************************************************************************/
static void GenereListeOfItems(wxWindow * frame, const wxString & FullFileName)
/***************************************************************************/
/*
	Routine principale pour la creation des listings ( composants et/ou labels
	globaux et "sheet labels" )
*/
{
FILE *f;
EDA_BaseStruct ** List;
ListLabel * ListOfLabels;
int NbItems;
char Line[1024];
wxString msg;
	
	/* Creation de la liste des elements */
	if ((f = wxFopen(FullFileName, wxT("wt"))) == NULL)
	{
		msg = _("Failed to open file "); msg << FullFileName;
		DisplayError(frame, msg);
		return;
	}

	NbItems = GenListeCmp(NULL );
	if ( NbItems )
	{
		List = (EDA_BaseStruct **)
				MyZMalloc( NbItems * sizeof(EDA_BaseStruct **) );
		if (List == NULL )
		{
			fclose(f);  return;
		}

		GenListeCmp(List);

		/* generation du fichier listing */
		DateAndTime(Line);
		fprintf( f, "%s  >> Creation date: %s\n", CONV_TO_UTF8(Main_Title), Line );

		/* Tri et impression de la liste des composants */

		qsort( List, NbItems, sizeof( EDA_BaseStruct * ),
				(int(*)(const void*, const void*))ListTriComposantByRef);

		if( (ItemsToList & LIST_SUBCMP) == 0 ) DeleteSubCmp(List, NbItems);

		if( (ItemsToList & LISTCMP_BY_REF) )
		{
			PrintListeCmpByRef(f, List, NbItems);
		}

		if( (ItemsToList & LISTCMP_BY_VAL) )
		{
			qsort( List, NbItems, sizeof( EDA_BaseStruct * ),
					(int(*)(const void*, const void*))ListTriComposantByVal);
			PrintListeCmpByVal(f, List, NbItems);
		}
		MyFree( List );
	}

	/***************************************/
	/* Generation liste des Labels globaux */
	/***************************************/

	NbItems = GenListeGLabels( NULL );
	if ( NbItems )
	{
		ListOfLabels = (ListLabel *) MyZMalloc( NbItems * sizeof(ListLabel) );
		if (ListOfLabels == NULL )
		{
			  fclose(f); return;
		}

		GenListeGLabels(ListOfLabels);

		/* Tri de la liste */
		if( (ItemsToList & LIST_HPINS_BY_SHEET) )
		{
			qsort( ListOfLabels, NbItems, sizeof( ListLabel ),
				(int(*)(const void*, const void*))ListTriGLabelBySheet);

			msg = _("\n#Glob labels ( order = Sheet Number )\n");
			fprintf( f, "%s", CONV_TO_UTF8(msg));
			PrintListeGLabel(f, ListOfLabels, NbItems);
		}

		if( (ItemsToList & LIST_HPINS_BY_NAME) )
		{
			qsort( ListOfLabels, NbItems, sizeof( ListLabel ),
				(int(*)(const void*, const void*))ListTriGLabelByVal);

			msg = _("\n#Glob labels ( order = Alphab. )\n");
			fprintf( f, "%s", CONV_TO_UTF8(msg));
			PrintListeGLabel(f, ListOfLabels, NbItems);
		}
		MyFree( ListOfLabels );
	}

	msg = _("\n#End List\n");
	fprintf( f, "%s", CONV_TO_UTF8(msg));
	fclose(f);
}



/****************************************/
int GenListeCmp( EDA_BaseStruct ** List )
/****************************************/
/* Routine de generation de la liste des elements utiles du dessin
	Si List == NULL: comptage des elements
	Sinon remplissage de la liste
	Initialise "FlagControlMulti" a SheetNumber pour la sortie des listes
	et m_Father comme pointeur sur la sheet d'appartenance
*/
{
int ii = 0;
EDA_BaseStruct *DrawList;
EDA_SchComponentStruct *DrawLibItem;
BASE_SCREEN * screen = ScreenSch;

	for( ; screen != NULL ; screen = (BASE_SCREEN*)screen->Pnext )
		{
		DrawList = screen->EEDrawList;
		while ( DrawList )
			{
			switch( DrawList->Type() )
				{

				case DRAW_LIB_ITEM_STRUCT_TYPE :
					ii++;
					DrawLibItem = (EDA_SchComponentStruct *) DrawList;
					DrawLibItem->m_FlagControlMulti = screen->m_SheetNumber;
					DrawLibItem->m_Parent = screen;
					if( List )
						{
						*List = DrawList; List++;
						}
					break;


				default: break;
				}
			DrawList = DrawList->Pnext;
			}
		}
	return ( ii );
}

/*********************************************/
static int GenListeGLabels( ListLabel * List )
/*********************************************/
/* Count the Glabels, or fill the list Listwith Glabel pointers 
	If List == NULL: Item count only
	Else fill list of Glabels
*/
{
int ii = 0;
EDA_BaseStruct *DrawList;
DrawSheetLabelStruct *SheetLabel;
BASE_SCREEN * screen = ScreenSch;

	for( ; screen != NULL ; screen = (BASE_SCREEN*)screen->Pnext )
	{
		DrawList = screen->EEDrawList;
		while ( DrawList )
		{
			switch( DrawList->Type() )
			{
				case DRAW_GLOBAL_LABEL_STRUCT_TYPE :
					if( List )
					{
						List->Type() = DRAW_TEXT_STRUCT_TYPE;
						List->m_SheetNumber = screen->m_SheetNumber;
						List->m_Label = DrawList; List++;
					}
					ii++;
					break;
				
				case DRAW_SHEET_STRUCT_TYPE :
				{
					#define Sheet ((DrawSheetStruct * ) DrawList)
					SheetLabel= Sheet->m_Label;
					while( SheetLabel != NULL )
					{
						if ( List )
						{
							List->Type() = DRAW_SHEETLABEL_STRUCT_TYPE;
							List->m_SheetNumber = screen->m_SheetNumber;
							List->m_Label = SheetLabel;
							List++;
						}
						ii++;
						SheetLabel = (DrawSheetLabelStruct*)(SheetLabel->Pnext);
					}
					break;
				}

				default: break;
			}
			DrawList = DrawList->Pnext;
		}
	}
	return ( ii );
}

/**********************************************************/
static int ListTriComposantByVal(EDA_SchComponentStruct **Objet1,
							EDA_SchComponentStruct **Objet2)
/**********************************************************/
 /* Routine de comparaison pour le tri du Tableau par qsort()
	Les composants sont tries
		par valeur
		si meme valeur: par reference
			si meme valeur: par numero d'unite

*/
{
int ii;
const wxString * Text1, *Text2;

	if( (*Objet1 == NULL) && (*Objet2 == NULL ) ) return(0);
	if( *Objet1 == NULL) return(-1);
	if( *Objet2 == NULL) return(1);

	Text1 = &(*Objet1)->m_Field[VALUE].m_Text;
	Text2 = &(*Objet2)->m_Field[VALUE].m_Text;
	ii = Text1->CmpNoCase(*Text2);

	if( ii == 0 )
	{
		Text1 = &(*Objet1)->m_Field[REFERENCE].m_Text;
		Text2 = &(*Objet2)->m_Field[REFERENCE].m_Text;
		ii = Text1->CmpNoCase(*Text2);
	}

	if ( ii == 0 )
	{
		ii = (*Objet1)->m_Multi - (*Objet2)->m_Multi;
	}

	return(ii);
}

/**********************************************************/
static int ListTriComposantByRef(EDA_SchComponentStruct **Objet1,
							EDA_SchComponentStruct **Objet2)
/**********************************************************/
 /* Routine de comparaison pour le tri du Tableau par qsort()
	Les composants sont tries
		par reference
		si meme referenece: par valeur
			si meme valeur: par numero d'unite

*/
{
int ii;
const wxString * Text1, *Text2;

	if( (*Objet1 == NULL) && (*Objet2 == NULL ) ) return(0);
	if( *Objet1 == NULL) return(-1);
	if( *Objet2 == NULL) return(1);

	Text1 = &(*Objet1)->m_Field[REFERENCE].m_Text;
	Text2 = &(*Objet2)->m_Field[REFERENCE].m_Text;
	ii = Text1->CmpNoCase(*Text2);

	if( ii == 0 )
	{
		Text1 = &(*Objet1)->m_Field[VALUE].m_Text;
		Text2 = &(*Objet2)->m_Field[VALUE].m_Text;
		ii = Text1->CmpNoCase(*Text2);
	}

	if ( ii == 0 )
	{
		ii = (*Objet1)->m_Multi - (*Objet2)->m_Multi;
	}

	return(ii);
}

/******************************************************************/
static int ListTriGLabelByVal(ListLabel *Objet1, ListLabel *Objet2)
/*******************************************************************/
/* Routine de comparaison pour le tri du Tableau par qsort()
	Les labels sont tries
		par comparaison ascii
		si meme valeur: par numero de sheet

*/
{
int ii;
const wxString * Text1, *Text2;

	if( Objet1->Type() == DRAW_SHEETLABEL_STRUCT_TYPE )
		Text1 = &((DrawSheetLabelStruct *)Objet1->m_Label)->m_Text;
	else
		Text1 = &((DrawTextStruct *)Objet1->m_Label)->m_Text;

	if( Objet2->Type() == DRAW_SHEETLABEL_STRUCT_TYPE )
		Text2 = &((DrawSheetLabelStruct *)Objet2->m_Label)->m_Text;
	else
		Text2 = &((DrawTextStruct *)Objet2->m_Label)->m_Text;
	ii = Text1->CmpNoCase(*Text2);

	if ( ii == 0 )
		{
		ii = Objet1->m_SheetNumber - Objet2->m_SheetNumber;
		}

	return(ii);
}

/*******************************************************************/
static int ListTriGLabelBySheet(ListLabel *Objet1, ListLabel *Objet2)
/*******************************************************************/
/* Routine de comparaison pour le tri du Tableau par qsort()
	Les labels sont tries
		par sheet number
		si meme valeur, par ordre alphabetique

*/
{
int ii;
const wxString * Text1, *Text2;

	ii = Objet1->m_SheetNumber - Objet2->m_SheetNumber;

	if ( ii == 0 )
	{
		if( Objet1->Type() == DRAW_SHEETLABEL_STRUCT_TYPE )
			Text1 = &((DrawSheetLabelStruct *)Objet1->m_Label)->m_Text;
		else
			Text1 = &((DrawTextStruct *)Objet1->m_Label)->m_Text;

		if( Objet2->Type() == DRAW_SHEETLABEL_STRUCT_TYPE )
			Text2 = &((DrawSheetLabelStruct *)Objet2->m_Label)->m_Text;
		else
			Text2 = &((DrawTextStruct *)Objet2->m_Label)->m_Text;
		ii = Text1->CmpNoCase(*Text2);
	}

	return(ii);
}



/**************************************************************/
static void DeleteSubCmp( EDA_BaseStruct ** List, int NbItems )
/**************************************************************/
/* Supprime les sous-composants, c'est a dire les descriptions redonnantes des
boitiers multiples
	La liste des composant doit etre triee par reference et par num d'unite
*/
{
int ii;
EDA_SchComponentStruct * LibItem;
const wxString * OldName = NULL;

	for( ii = 0; ii < NbItems ; ii++ )
	{
		LibItem = (EDA_SchComponentStruct *) List[ii];
		if ( LibItem == NULL ) continue;
		if( OldName )
		{
			if ( OldName->CmpNoCase( LibItem->m_Field[REFERENCE].m_Text ) == 0 )
			{
				List[ii] = NULL;
			}
		}
		OldName = &LibItem->m_Field[REFERENCE].m_Text;
	}
}


/**********************************************************************/
int PrintListeCmpByRef( FILE * f, EDA_BaseStruct ** List, int NbItems )
/**********************************************************************/
/* Impression de la liste des composants tries par reference
*/
{
int ii, Multi, Unit;
EDA_BaseStruct *DrawList;
EDA_SchComponentStruct *DrawLibItem;
EDA_LibComponentStruct *Entry;
char NameCmp[80];
wxString msg;
	
	msg = _("\n#Cmp ( order = Reference )");
	if ( (ItemsToList & LIST_SUBCMP) ) msg << _(" (with SubCmp)");
	fprintf( f, "%s\n", CONV_TO_UTF8(msg));

	for ( ii = 0; ii < NbItems; ii++ )
		{
		DrawList = List[ii];

		if( DrawList == NULL ) continue;
		if( DrawList->Type() != DRAW_LIB_ITEM_STRUCT_TYPE ) continue;

		DrawLibItem = (EDA_SchComponentStruct *) DrawList;
		if( DrawLibItem->m_Field[REFERENCE].m_Text[0] == '#' ) continue;

		Multi = 0; Unit = ' ';
		Entry = FindLibPart(DrawLibItem->m_ChipName.GetData(), wxEmptyString, FIND_ROOT);
		if( Entry ) Multi = Entry->m_UnitCount;
		if( (Multi > 1 ) && ( ItemsToList & LIST_SUBCMP ) )
			 Unit = DrawLibItem->m_Multi + 'A' - 1;

		sprintf( NameCmp,"%s%c", CONV_TO_UTF8(DrawLibItem->m_Field[REFERENCE].m_Text),
			Unit);
		fprintf(f, "| %-10.10s %-12.12s",
					NameCmp,
					CONV_TO_UTF8(DrawLibItem->m_Field[VALUE].m_Text));

		if ( (ItemsToList & LIST_SUBCMP) )
			{
			BASE_SCREEN * screen = (BASE_SCREEN *)(DrawLibItem->m_Parent);
			wxString sheetname;
			if( screen->m_Parent )
				sheetname = ((DrawSheetStruct*)screen->m_Parent)->m_Field[VALUE].m_Text.GetData();
			else sheetname = _("Root");
			fprintf(f, "   (Sheet %.2d: \"%s\")", DrawLibItem->m_FlagControlMulti,
					CONV_TO_UTF8(sheetname));
			}

		fprintf(f,"\n");
		}
	msg = _("#End Cmp\n");
	fprintf(f, CONV_TO_UTF8(msg));
	return(0);
}

/***********************************************************************/
int PrintListeCmpByVal( FILE * f, EDA_BaseStruct ** List, int NbItems )
/**********************************************************************/
{
int ii, Multi;
wxChar Unit;
EDA_BaseStruct *DrawList;
EDA_SchComponentStruct *DrawLibItem;
EDA_LibComponentStruct *Entry;
wxString msg;
	
	msg = _("\n#Cmp ( order = Value )");
	if ( (ItemsToList & LIST_SUBCMP) ) msg <<  _(" (with SubCmp)");
	msg << wxT("\n");
	fprintf(f, CONV_TO_UTF8(msg));

	for ( ii = 0; ii < NbItems; ii++ )
		{
		DrawList = List[ii];

		if( DrawList == NULL ) continue;
		if( DrawList->Type() != DRAW_LIB_ITEM_STRUCT_TYPE ) continue;

		DrawLibItem = (EDA_SchComponentStruct *) DrawList;
		if( DrawLibItem->m_Field[REFERENCE].m_Text[0] == '#' ) continue;

		Multi = 0; Unit = ' ';
		Entry = FindLibPart(DrawLibItem->m_ChipName.GetData(), wxEmptyString, FIND_ROOT);
		if( Entry ) Multi = Entry->m_UnitCount;
		if( (Multi > 1 ) && ( ItemsToList & LIST_SUBCMP ) )
			 Unit = DrawLibItem->m_Multi + 'A' - 1;
		msg = DrawLibItem->m_Field[REFERENCE].m_Text;
		msg.Append(Unit);

		fprintf(f, "| %-12.12s %-10.10s",
					CONV_TO_UTF8(DrawLibItem->m_Field[VALUE].m_Text),
					CONV_TO_UTF8(msg) );
		if ( (ItemsToList & LIST_SUBCMP) )
		{
			fprintf(f, "   (Sheet %.2d)", DrawLibItem->m_FlagControlMulti);
		}

		fprintf(f,"\n");
	}
	msg = _("#End Cmp\n");
	fprintf(f, CONV_TO_UTF8(msg));
	return(0);
}


/******************************************************************/
static int PrintListeGLabel( FILE *f, ListLabel *List, int NbItems)
/******************************************************************/
{
int ii, jj;
DrawGlobalLabelStruct *DrawTextItem;
DrawSheetLabelStruct * DrawSheetLabel;
ListLabel * LabelItem;
wxString msg;
	
	for ( ii = 0; ii < NbItems; ii++ )
	{
		LabelItem = & List[ii];

		switch( LabelItem->Type() )
		{
			case DRAW_GLOBAL_LABEL_STRUCT_TYPE :
				DrawTextItem = (DrawGlobalLabelStruct *)(LabelItem->m_Label);
				msg.Printf(
                        _("> %-28.28s Global        (Sheet %.2d) pos: %3.3f, %3.3f\n"),
							DrawTextItem->m_Text.GetData(),
							LabelItem->m_SheetNumber,
							(float)DrawTextItem->m_Pos.x / 1000,
							(float)DrawTextItem->m_Pos.y / 1000);
				
				fprintf(f, CONV_TO_UTF8(msg));
 				break;

			case DRAW_SHEETLABEL_STRUCT_TYPE :
			{
				DrawSheetLabel = (DrawSheetLabelStruct *) LabelItem->m_Label;
				jj = DrawSheetLabel->m_Shape;
				if ( jj < 0 ) jj = NET_TMAX; if ( jj > NET_TMAX ) jj = 4;
				wxString labtype = CONV_FROM_UTF8(SheetLabelType[jj]);
				msg.Printf(
                        _("> %-28.28s Sheet %-7.7s (Sheet %.2d) pos: %3.3f, %3.3f\n"),
							DrawSheetLabel->m_Text.GetData(),
							labtype.GetData(),
							LabelItem->m_SheetNumber,
							(float)DrawSheetLabel->m_Pos.x / 1000,
							(float)DrawSheetLabel->m_Pos.y / 1000);
				fprintf(f, CONV_TO_UTF8(msg));
			}
				break;

			default: break;
		}
	}
	msg = _("#End labels\n");
	fprintf(f, CONV_TO_UTF8(msg));
 	return(0);
}