{******************************************************************
 ***                      Addresses                             ***
 ******************************************************************}
{As different countries have different address formats, and to work on
automatic letters/communications to various targets (eg email, fax,
post), this module provides a set of different kinds of addresses.  They
can be inserted into other objects (eg kperson) by setting aside an area
then calling "InputAddressBox" with the co-ordinates of the area and
the location of an "address" button that allows changes of format, etc.

As the address is descended from Tjimmy, it can be hooked as a form of
alternative addresses}
{$I compflgs}

unit address;

INTERFACE

uses	tuiedit,{for TinputGroup}
			dattime,
			views,
			scodes,
			files,
			forms, devices,
			global,
			lstrings,
			objects, {pstring}
			drivers, {Tevent}
			jimmys, tuijimmy;


{address formats:
	UK: 3 lines			PO Box:	1 line 		USA:	1 line      	Europe:	1 line
			Town                PO Box #        Postcode/Town					Postcode/Town
			County              Town						State									Country
			Postcode						Country					Auto-country
			Country

	email: 	ID
					@Address

	fax: 1 line (different fax nos...?)
{}


const
	{-- address purpouses - could be one or all ---}
	apAny = $FF;
	apCorrespondence = $01;
	apHome = $02;
	apWork = $04;
	apInvoice = $08;
	apDelivery = $10;

	apText : array[0..4] of string[15] = ('~L~etters','~H~ome','~W~ork','~I~nvoice','~D~elivery');

	{-- address type ---}
	adUK = $01; {or delivery - street name, etc}
	adPOBox = $02;
	adUSA = $03;
	adEurope = $04;
	ademail = $05;
	adfax = $06;

	adText : array[1..6] of string[15] = ('UK/Delivery','PO Box','USA','Europe','email','Fax');

	NumAddressLines = 9;
	NumTelLines = 3;

type
	{--- Root address type ----}
	PAddress = ^TAddress;
	TAddress = object(TJimmy)

		ForWho : longint;
		ForDates : TDateRange; {valid for date range - eg alternative addresses}
		adType : byte;  {type - ie email, usa, etc}

		Line : array[1..NumAddressLines] of PString;
		Country : TSCode;

		Tel : array[1..NumTelLines] of PString;
		Fax : PString;

		apType : word;  {purpose - needs to be a word for the check boxes}

		constructor Init(Param : PJimmyInitParam);
		procedure CommonInit; virtual;
		destructor Done; virtual;

		{address routines}
		procedure SetLine(const L : byte; const S : string); virtual;
		function GetLine(const L : byte) : string; virtual;
		function NumLines : byte; {number of non-blank lines}
		procedure CopyFrom(const CopyAddress : PAddress);
		function Search(const S : string) : byte;

		function GetAddressLine(const L : byte) : string;
		function GetFormattedLine (const L : byte) : string;
		function Town : string;
		function Postcode : string;

		function Blank : boolean; virtual;

		{telephone/fax routines}
		function GetTelNum(const L : byte) : string;
		function GetFaxNum : string; virtual;

		{display routines}
		function DisplayLine(ListForWho : longint; lstype : byte; Maxlen : integer; View : word) : string; virtual;
		procedure MakeEditBox(var EditBox : PEditBox; Caller : PView); virtual;
		procedure AddEditFields(const X,Y : byte; const Group : PGroup);
		function GetName(naType : byte; Maxlen : integer) : string; virtual; {used for various displays/prints -

		{DataBase}
		constructor Load(var S : TDataStream);
		procedure   StoreFields(var S : TDataSTream); virtual;

		function RecSize : word; virtual;
		function srType : word; virtual;

		{for fixit, etc}
		function NumIDs : byte; virtual; {the number of jimmy ID ptrs in the data}
		function GetJimmyID(const jiType : byte) : PLongint; virtual; {each jimmy ID}

		{-- Hooking to others -----}
		function NumHookTo : byte; virtual;
		procedure GetHookTo(const htType : byte; var HookToID,SubHookToID : PLongint;
												var hkType : byte; var Key : longint; var InsertBias : boolean); virtual;

		{printing/output}
{		function DefPrefix(const hkType : word) : string; virtual;{}
		procedure SetFormCodes(const FormCodes: PFormCodeCollection); virtual;

		procedure PrintFull(const Device : PDevicestream; const PrintAs : word); virtual;
		procedure PrintSummary(const Device : PDevicestream; const PrintAs : word); virtual;
{		procedure PrintLine(const Device : PDevicestream); virtual;{}
		procedure PrintLabel(const Device : PDeviceStream; LabelAs : word); virtual;


{		procedure DoLabel(const Device : PDeviceStream);
		procedure DoLabelNow(Const NumLabels : byte);{}
		procedure DeferLabel(const NumLabels : byte);

	end;

type
	PInputAddress = ^TInputAddress;
	TInputAddress = object(TJimmyInputGroup)
		AddressOWner : PJimmy; {probably a directoryitem}
		AltAddressOwner : PJimmy; {can be set for alternative address list - eg
																	for listing company addresses of a person}

		constructor Init(X,Y : byte; NAddressOwner : PJimmy; const Indirect : boolean);
		function datasize : word; virtual;{}
		procedure SetData(var Rec); virtual;
		procedure GetData(var Rec); virtual;{}
		procedure HandleEvent(var Event : TEvent); virtual;
		procedure ExecuteList(const ListAddOwner : PJimmy);
		procedure InsertEditFields; virtual;

		procedure MakeInitParam(var InitParam : TJimmyInitParam); virtual;
		procedure SetForWho; virtual; {*MUST* be overriden}
		function JimmyValid(TestJimmy : PJimmy) : boolean; virtual;
	end;


{function CreateUKAddress(P : pointer) : pointer;
function CreatePOBoxAddress(P : pointer) : pointer;
function CreateemailAddress(P : pointer) : pointer;{}


const
	{--- Required for Stream ----}
	RAddress : TStreamRec = (
		ObjType : srAddress;
		VmtLink : Ofs(TypeOf(TAddress)^);
		Load : @TAddress.Load;
		Store : @TAddress.Store
	);


IMPLEMENTATION

uses
			kamsetup,
			country, {country scodes}
			dialogs,
			inpdnt,
			menus,
			labels,
			jimhooks, {for listing addresses}
			kdirctry, kdirsetu, {that's who it hooks to}
			app,
			tuimsgs,
			tui,
			tuilist,
			help,
			tasks,
			inpjimmy,
			inptel,
			minilib;


{****************************************************
 ***                                              ***
 ***               ADDRESS OBJECT DEF             ***
 ***                                              ***
 ****************************************************}

constructor TAddress.Init;
var I : byte;
begin
	inherited Init;
	if Param<>nil then ForWho := Param^.ForWho else ForWho := -1;
	ForDates.Start.Clear;
	ForDates.Finish.Clear;
	ForDates.Start.SetToToday;

	adType := DirectorySetup.DefadType+1;
	apType := $FF; {all}

	case adType of
		adUSA : Country := 'USA';
{		adUK 	: Country := 'UK'; might be a delivery}
	else
		Country := ProgramSetup.Get(siCountry,'UK');
	end;
end;

procedure TAddress.CommonInit;
var I : byte;
begin
	inherited CommonInit;
	SCodeCollection[scCountries]^.LogOn;
	for I := 1 to NumAddressLines do Line[I] := nil;
	for I := 1 to NumTelLines do Tel[I] := nil;
	Fax := nil;
end;

destructor TAddress.Done;
var I :byte;
begin
	SCodeCollection[scCountries]^.LogOff;
	for I := 1 to NumAddressLines do if Line[I]<>nil then disposeStr(Line[I]);
	for I := 1 to NumTelLines do if Tel[I]<>nil then disposeStr(Tel[I]);
	if Fax<>nil then DisposeStr(Fax);
	inherited Done;
end;

{****************************************************
 ***               TEL/FAX    ROUTINES            ***
 ****************************************************}
function TAddress.GetTelNum;
begin
	GetTelNum := '';
	if (L>=1) and (L<=NumTelLines) and (Tel[L]<>nil) then	GetTelNum := Tel[L]^;
end;

function TAddress.GetFaxNum;
begin
	if Fax<>nil then GetFaxNum := Fax^ else GetFaxNUm := '';
end;



{****************************************************
 ***               ADDRESSING ROUTINES            ***
 ****************************************************}
procedure TAddress.SetLine;
begin
	if L<=NumAddressLines then begin
		if Line[L]<>nil then DisposeStr(Line[L]);
		Line[L] := NewStr(S);
	end;{}
end;

function TAddress.GetLine(const L : byte) : string;
begin
	if (L<=NumAddressLines) and (Line[L]<>nil) then GetLine := Line[L]^ else GetLine := '';{}
end;

function TAddress.NumLines : byte;
var I,L : byte;
begin
	I := 1; L := 0;
	while (I<=NumAddressLines) do begin
		if (Line[I]<>nil) then inc(L);
		inc(I);
	end;
	if (L>0) and (delspace(Country)<>'') and (delspace(Country)<>delspace(PRogramSetup.Get(siCountry,'UK'))) then inc(L);
	NumLines := L;{}
end;

procedure TAddress.CopyFrom;
var I : byte;
begin
	ForWho := CopyAddress^.ForWho;
	apType := CopyAddress^.apType;
	adType := CopyAddress^.adType;
	ForDates.Start.SetToDate(CopyAddress^.ForDates.Start);
	ForDates.Finish.SetToDate(CopyAddress^.ForDates.Finish);
	for I := 1 to NumAddressLines do SetLine(I, CopyAddress^.GetLine(I));
end;

{returns line number that search string exists in, or 0 if it doesn't}
{adds ' '+Line+' ' so we can do a whole word search by passing ' '+Word+' '
in S}
function TAddress.Search;
var I : byte;
begin
	Search := 0;
	for I :=1 to NumAddressLines do
		if pos(ucase(S), ' '+ucase(GetLine(i))+' ')>0 then Search := I;
end;


{====== DETAIL EXTRACTION ==============}

{gets lines without gaps & formatted - ie suitable for labels, letters, etc}
function TAddress.GetAddressLine(const L : byte) : string;
var B,LineCount : byte;
		S : string;
begin
	B := 1; LineCount := 0;
	repeat
		S := GetFOrmattedLine(B);
		if S<>'' then inc(LineCOunt);
		inc(B);
	until (B>NumAddressLines+1) or (LineCount=L);

	if (LineCount=L)  then
		GetAddressLine := S
	else
		GetAddressLine := '';
end;



{formats line - ie adds POBox, etc}
function TAddress.GetFormattedLine(const L : byte) : string;
begin
	if L>NumAddressLines then
		if delspaceR(Country)<>delspaceR(ProgramSetup.Get(siCountry,'UK')) then
			GetFormattedLine := ExpandSCode(scCountries, Country)
		else
			GetFormattedLine := ''
	else begin
		GetFormattedLine := GetLine(L); {for now}
		case adType of
			adPOBOx : begin
				if L = 2 then GetFormattedLine := 'PO Box '+GetLine(L);
{				if L > 3 then GetFormattedLine := ''; {for some reason the town is sometimes getting stored twice}
			end;
			adEurope : begin
				if L = 2 then GetFormattedLine := GetLine(2)+' '+GetLine(3);
				if L > 2 then GetFormattedLine := GetLine(L+1); {make up for above}
			end;
			adUSA : begin
			end;
			ademail : begin
				if L = 1 then
					GetFormattedLine := GetLine(1)+'@'+GetLine(2)
				else
					GetFormattedLine := '';
			end;
		end;
	end;
end;



function TAddress.Town;
begin
	case adType of
		adUK 			: Town := GetLine(4);
		adPOBox 	: Town := GetLine(3);
		adEurope	: Town := GetLine(3);
		adUSA 		: Town := GetLine(3);
	else
		Town := '';
	end;
end;

function TAddress.Postcode;
begin
	case adType of
		adUK		 : PostCode := GetLine(6);
		adEurope : PostCode := GetLine(2);
		adUSA		 : PostCode := GetLine(2);
	else
		PostCode := '';
	end;
end;

function TAddress.Blank : boolean;
var B : byte;
begin
	Blank := True;
	for B := 1 to NumAddressLines do
		if (Line[B]<>nil) and (delspaceR(Line[B]^)<>'') then Blank := False;

	for B := 1 to NumTelLines do
		if (Tel[B]<>nil) and (delspaceR(tel[B]^)<>'') then Blank := False;

	if (Fax<>nil) and (delspaceR(fax^)<>'') then Blank := False;
end;




{****************************************************
 ***               DISPLAY    ROUTINES            ***
 ****************************************************}
function TAddress.GetName; {for use by inputlines}
begin
	if delspaceR(Town)<>'' then GetName := Town+', '+GetLine(1) else GetName := GetLine(1);
end;


function TAddress.DisplayLine;
var S : string;
		B : byte;
begin
	S := '';
	if (apType and $1F)<>$1F then {not a general purpose, all-marked address}
		for B := 0 to 4 do if (apType and Exp2(B))<>0 then S := S + ucase(deltildes(apText[B]))+' ';

	{Display whether current, old, future}
	TOday.SetToToday;
	if (Today.Days >ForDates.Finish.Days) and (not ForDates.Finish.Blank) then S := 'OLD '+S
		else if (Today.Days <ForDates.Start.Days) and (not ForDates.Start.Blank) then S := 'FUTURE '+S;

	case adType of
		ademail : S := S + 'EMAIL: '+GetLine(1)+'@'+GetLine(2);
	else
		S := S + GetLine(1);
		if (S<>'') and (GetLine(2)<>'') then S := S+', ';
		if (adType = adPOBox) and (GetLine(2)<>'') then S := S + 'PO BOX ';
		S :=S +GetLine(2);
		if Town<>'' then S := S +' ('+Town+')';
	end;

	if S = '' then begin
		{nothing entered in address, so use telephone}
		S := S + 'Tel: '+GetTelNum(1);
		if GetFaxNum<>'' then S := S +',  Fax: '+GetFaxNum;
	end;

	DisplayLine := S;
end;

{use TGroup so that it can be used for TInputAddress as well as
 the makeeditbox below}
procedure TADdress.AddEditFields(const X,Y : byte; const Group : PGroup);
var R : TRect;
		B : byte;
begin
	with Group^ do
	case adType of
		adUK : begin{}
			R.XYLD(X+ 9,Y+0,22,1);Insert(New(PInputPStr, init(R,35)));	AddLabel('~A~ddress', Current);{}

			R.Move(0,1); 					Insert(New(PInputPStr, init(R,35)));{}
			R.Move(0,1);					Insert(New(PInputPStr, init(R,35)));{}

			{town}
			R.Move(0,1); 					Insert(New(PInputPStr, init(R,30)));	AddLabel('To~w~n', Current);
			PInputPStr(Current)^.UpperCase := True;

			{county - should be coded?}
			R.XYLD(X+ 9,Y+4,22,1);Insert(New(PInputPStr, init(R, 30)));	AddLabel('County', Current);

			{postcode}
			R.XYLD(X+ 9,Y+5,10,1);Insert(New(PInputPStr, init(R, 10)));	AddLabel('~P~ostcode', Current);
			PInputPStr(Current)^.UpperCase := True;

			for B := 7 to NumAddressLines do Insert(New(PSkipBytes, init(sizeof(PString)))); {skip rest of lines}

			{Country}
			R.XYLD(X+26,Y+5, 5,1);Insert(New(PInputSCode, init(R, scCountries))); AddLabel('Cntry', Current);
		end;
		adPOBox : begin
			R.XYLD(X+ 9,Y+0,22,1);Insert(New(PInputPStr, init(R, 35)));	AddLabel('~A~ddress', Current);

			{PO Box}
			R.Assign(X+ 9,Y+1,X+21,Y+2); Insert(New(PInputPStr, init(R, 10)));	AddLabel('~P~O Box', Current);

			{town}
			R.Assign(X+ 9,Y+2,X+31,Y+3); Insert(New(PInputPStr, init(R, 30)));	AddLabel('To~w~n', Current);
			PInputPStr(Current)^.UpperCase := True;

			for B := 4 to NumAddressLines do Insert(New(PSkipBytes, init(sizeof(PString)))); {skip rest of lines}

			{Country}
			R.Assign(X+ 9,Y+3,X+31,Y+4); Insert(New(PInputSCode, init(R, scCountries))); AddLabel('Country', Current);
		end;
		adEurope : begin
			R.Assign(X+ 9,Y+0,X+31,Y+1); Insert(New(PInputPStr, init(R, 35)));{}
			R.Assign(X+ 1,Y+0,X+ 8,Y+1); Insert(New(PLabel, init(R, '~A~ddress', Current)));

			{Postcode}
			R.Assign(X+ 9,Y+1,X+21,Y+2); Insert(New(PInputPStr, init(R, 10)));{}
			R.Assign(X+ 2,Y+1,X+ 8,Y+2); Insert(New(PLabel, init(R, '~P~ostcode', Current)));

			{town}
			R.Assign(X+ 9,Y+2,X+31,Y+3); Insert(New(PInputPStr, init(R, 30)));{}
			PInputPStr(Current)^.UpperCase := True;
			R.Assign(X+ 4,Y+2,X+ 8,Y+3); Insert(New(PLabel, init(R, 'To~w~n', Current)));

			for B := 4 to NumAddressLines do Insert(New(PSkipBytes, init(sizeof(PString)))); {skip rest of lines}

			{Country}
			R.Assign(X+ 9,Y+3,X+31,Y+4); Insert(New(PInputSCode, init(R, scCountries)));{}
			R.Assign(X+ 1,Y+3,X+ 8,Y+4); Insert(New(PLabel, init(R, 'Country', Current)));
		end;
		adUSA : begin
			R.Assign(X+ 9,Y+0,X+31,Y+1); Insert(New(PInputPStr, init(R, 35)));{}
			R.Assign(X+ 1,Y+0,X+ 8,Y+1); Insert(New(PLabel, init(R, '~A~ddress', Current)));

			{Zip code}
			R.Assign(X+ 9,Y+1,X+21,Y+2); Insert(New(PInputPStr, init(R, 10)));{}
			R.Assign(X+ 5,Y+1,X+ 8,Y+2); Insert(New(PLabel, init(R, '~Z~ip', Current)));

			{town}
			R.Assign(X+ 9,Y+2,X+31,Y+3); Insert(New(PInputPStr, init(R, 30)));{}
			PInputPStr(Current)^.UpperCase := True;
			R.Assign(X+ 4,Y+2,X+ 8,Y+3); Insert(New(PLabel, init(R, 'To~w~n', Current)));

			{state - should be coded?}
			R.Assign(X+ 9,Y+3,X+31,Y+4); Insert(New(PInputPStr, init(R, 20)));{}
			R.Assign(X+ 3,Y+3,X+ 8,Y+4); Insert(New(PLabel, init(R, '~S~tate', Current)));

			for B := 5 to NumAddressLines do Insert(New(PSkipBytes, init(sizeof(PString)))); {skip rest of lines}

			{Country}
			R.Assign(X+ 9,Y+4,X+31,Y+5); Insert(New(PInputSCode, init(R, scCountries)));{}
			R.Assign(X+ 1,Y+4,X+ 8,Y+5); Insert(New(PLabel, init(R, 'Country', Current)));
		end;
		ademail : begin
			R.Assign(X+ 9,Y+0,X+31,Y+1); Insert(New(PInputPStr, init(R, 20)));{}
			R.Assign(X+ 0,Y+0,X+ 8,Y+1); Insert(New(PLabel, init(R, '~e~mail ID', Current)));

			R.Assign(X+ 9,Y+1,X+31,Y+2); Insert(New(PInputPStr, init(R, 30)));{}
			R.Assign(X+ 7,Y+1,X+ 8,Y+2); Insert(New(PLabel, init(R, '@', Current)));

			for B := 3 to NumAddressLines do Insert(New(PSkipBytes, init(sizeof(PString)))); {skip rest of lines}

			Insert(New(PSkipBytes, init(sizeof(TSCode)))); {skip country}
		end;
	end;{}

	if adType<>ademail then with Group^ do begin
		R.XYLD(X+39,Y+0,22,1); Insert(New(PInputTelNum, init(R, 25))); AddLabel('Te~l~', Current);{}
		R.XYLD(X+39,Y+1,22,1); Insert(New(PInputTelNum, init(R, 25)));
		R.XYLD(X+39,Y+2,22,1); Insert(New(PInputTelNum, init(R, 25)));

		R.XYLD(X+39,Y+3,22,1); Insert(New(PInputTelNum, init(R, 25))); AddLabel('Fa~x~', Current);{}
	end else
		Group^.Insert(New(PSkipBytes, init(sizeof(PString)*4)));

	Group^.SetDataOrder;

end;

procedure TAddress.MakeEditBox;
var
	R: TRect;
	I : integer;
	View : PView;

begin
	R.Assign(0, 0, 59, 15);
	CentreOnView(R, Caller);
	EditBox := New(PJimmyEditBox, init(R, 'Address',Caller, @Self));

	inherited MakeEditBox(EditBox, caller);

	with EditBox^ do begin
		HelpCtx := hcAddressEditBox;

		InsTitledField(10, 1, 20, 1, 'For', New(PInputJImmy, init(R, 20, lsDirectory)));
		Current^.SetState(sfDisabled, True); {not editable, but for display and to store the forwho of the address}

		Insert(New(PSkipBytes, init(2))); {skip tdaterange vmt}

		InsTitledField(10, 3,10, 1, 'From', New(PInputDate, Init(R)));
		InsTitledField(10, 4,10, 1, 'to',   New(PInputDate, Init(R)));

		View := Current;

		Insert(New(PSkipBytes, init(1))); {skip type - set in creators below}

		AddEditFields(1, 6, EditBox);

		InsTitledField(26,  1,24, 3, '',
						New(PCheckBoxes, init(R, 	NewSItem('~C~/dence',
																			NewSITem('~H~ome',
																			NewSItem('~W~ork',
																			NewSITem('~I~nvoice',
																			NewSItem('~D~elivery',
						nil))))))));

		{-- Buttons --}
		Insert(New(PJimmyOKButton, Init(24,12, @Self)));
		Insert(New(PJimmyCancelButton, init(35,12, @Self)));

		EndInit;
		View^.Focus; SelectNext(False); {start on first line of address}

		SetDAtaOrder; {as addeditfields will do it}
	end;
end;




{*****************************************
 ***     STREAMING DEFINITIONS         ***
 *****************************************}

function TAddress.RecSize : word;
begin
	case adType of
		adPOBox : RecSize := 200;
		adEurope: RecSize := 250;
		adUSA   : REcSize := 250;
		ademail : REcSize := 100;
	else
		RecSize := 300;
	end;
end;

function TAddress.srType : word;
begin srType := srAddress; end;

constructor TAddress.Load(var S : TDataStream);
var I : integer;
		Ver : byte;

begin
	S.Read(Ver, 1);

	case Ver of
		2 : begin
			inherited Load(S);

			S.Read(ForWho, 4);
			ForDates.Start.Load(S);
			ForDates.Finish.Load(S);

			S.Read(apType, 2);
			S.Read(adType, 1);

			for I := 1 to NumAddressLines do Line[I] := NewStr(S.ReadStr);
			S.Read(Country, sizeof(Country));

			for I := 1 to NumTelLines 		do  Tel[I] := NewStr(S.ReadStr);
			Fax := NewStr(S.ReadStr);

			{bit of a botch job as po box addresses seem to have two towns, and as
			soon as stuff is written past the end of where the input lines usually
			are, they don't get deleted}
			case adType of
				adPOBox 	: for I := 4 to NumAddressLines do SetLine(I,'');
				adUK 			: for I := 7 to NumAddressLines do SetLine(I,'');
				adEurope 	: for I := 4 to NumAddressLines do SetLine(I,'');
				adUSA 		: for I := 5 to NumAddressLines do SetLine(I,'');
				ademail 	: for I := 3 to NumAddressLines do SetLine(I,'');
			end;

		end;
		1 : begin
			{pre v4.3}
			CommonInit;
			S.Read(LockTerminal, 1);
			Deleted := False;

			S.Read(ForWho, 4);
			ForDates.Start.Load(S);
			ForDates.Finish.Load(S);

			S.Read(apType, 2);
			S.Read(adType, 1);

			for I := 1 to NumAddressLines do Line[I] := NewStr(S.ReadStr);
			S.Read(Country, sizeof(Country));

			for I := 1 to NumtelLines do Tel[I] := nil;
			Fax := nil;
		end;
	else
		DBaseMessage(@S, 'Version '+N2Str(Ver)+' not understood'#13#10'TAddress.Load',mfError,hcInternalErrorMsg);
	end;
end;

procedure TAddress.StoreFields(var S : TDataStream);
var I : integer;
		Ver : byte;
begin
	Ver := 2; S.Write(ver ,1);

	inherited StoreFields(S);

	S.Write(ForWho, 4);

	ForDates.Start.Store(S);
	ForDates.Finish.Store(S);

	S.Write(apType, 2);
	S.Write(adType, 1);

	for I := 1 to NumAddressLines do S.WriteStr(Line[I]);
	S.Write(Country, sizeof(Country));

	for I := 1 to NumTelLines 		do S.WriteStr(Tel[I]);
	S.WriteStr(Fax);
end;


{============== POINTERS TO OTHER JIMMYS===================}
function TAddress.NumIDs;
begin NumIDs := 1; end;

function TAddress.GetJImmyID;
begin
	case jiType of
		1 : GetJimmyID := @ForWho;
	else
		GetJimmyID := nil;
	end;
end;



function TAddress.NumHookTo : byte;
begin NumHookTo := 1; end;

procedure TAddress.GetHookTo;
begin
	inherited GetHookTo(htType, HookToID,SubHookToID, hkType, Key, InsertBias);
	case htType of
		1 : begin
			HookToID := @ForWho;
			hkType := hkAddress;
			if not ForDates.InRange(Today) then
				Key := 0 {move to end of list}
			else
				Key := -ForDates.Start.Days;
		end;
	end;
end;

{****************************************************
 ***                PRINTING/CODES                ***
 ****************************************************}
{function TAddress.DefPrefix(const hkType : word) : string;
begin DefPrefix := 'ADD'; end;{}

procedure TAddress.SetFormCodes;
var S : string;
		L : byte;
begin
	inherited SetFormCodes(FormCodes);

	{address}
	with FormCodes^ do begin
		{formatted}
		S := ''; for  L := 1 to NumLines do S := S + GetAddressLine(L) + CRLF;
		SetStr('ADD',TrimCRLF(S));

		{straight}
		S := ''; for  L := 1 to NumAddressLines do S := S + GetLine(L) + CRLF;
		SetStr('ADDLINE',TrimCRLF(S));

		{british types}
		SetStr('TOWN',Town);

		SetStr('POSTCODE',PostCode);
		SetStr('ZIP',PostCode); {for those american types}

		SetStr('COUNTY', '');
		SetStr('POBOX','');
		SetStr('DOMAIN','');

		SetStr('STATE', '<COUNTY>'); {americans again}

		case adType of
			adUK 		: SetStr('COUNTY',GetLine(5));
			adUSA 	: SetStr('COUNTY', GetLine(4));
			adPOBox : SetStr('POBOX', GetLine(2)); {returns just po box number (w/o "PO Box")}
			ademail	: SetStr('DOMAIN', GetLine(2));
		end;

		{telephone numbers}
		S := ''; for  L := 1 to NumTelLines do if Tel[L]<>nil then S := S + Tel[L]^ + CRLF;
		SetStr('TEL',TrimCRLF(S));

		{fax}
		if Fax=nil then SetStr('FAX','') else SetStr('FAX', Fax^);

		SetStr('CNTRY', Country); {nb, faxto.cntry uses this}

	end;
end;


{printing/output}
procedure TAddress.PrintFull;
begin
	PrintLabel(Device,0); {for now}
end;

procedure TAddress.PrintSummary;
begin
	PrintLabel(Device,0); {for now}
end;

{procedure TAddress.PrintLine;
begin
	Device^.writeln(DisplayLine(-1,0,0,0));
end;{}

{============== DO LABEL ===================}
procedure TAddress.PrintLabel;
var I : byte;
begin
	for I := 1 to NUmLines do
		Device^.writeln(GetAddressLine(I));

{	Device^.writeln(#12); {end of label marker}
end;

procedure TAddress.DeferLabel;
var Device : PDeviceStream;
		I : byte;
begin
	ThinkingOn('Deferring Label(s)');
	OpenDeferredLabels(Device);
	for I := 1 to NumLabels do PrintLabel(Device,0);
	dispose(Device, done);
	ThinkingOff;
end;

{****************************************************
 ***                                              ***
 ***               INPUT ADDRESS  DEF             ***
 ***                                              ***
 ****************************************************}

constructor TInputAddress.Init;
var R : TRect;
		adType, sr : word;
begin
	R.XYLD(X,Y,61,8);
	adType := DirectorySetup.DefadType+1;
	case adType of
		adPOBox 	: sr := cmNewPOBoxAddress;
		adUSA 		: sr := cmNewUSAAddress;
		adEurope 	: sr := cmNewEuropeanAddress;
		ademail 	: sr := cmNewEmailAddress;
	else
		sr := cmNewUKAddress; {adUK}
	end;

	inherited Init(R,sr);

{	PAddress(Jimmy)^.ForWho := NAddressOwner^.RecNo;{now dealt with in setforwho in the case of new ones}

	AddressOwner := NAddressOwner;
	AltAddressOwner := nil;
{	IndirectData := Indirect;

	InsertEditFields;{}
end;

function TInputAddress.DataSize;
begin
	DataSize := sizeof(pointer);
end;{}

procedure TInputAddress.GetData;
begin
	inherited GetData(PLongint(Rec)^); {indirect pointer to ID}
end;

procedure TInputAddress.SetData;
begin
	inherited SetData(Plongint(Rec)^); {indirect pointer to ID}
end;

function TInputAddress.JimmyValid(TestJimmy : PJimmy) : boolean;
begin
	if TestJimmy^.srType<>srAddress then begin
		ProgramWarning('Address Jimmy not srtype 1306, its '+N2Str(TestJimmy^.srType), hcInternalErrorMsg);
		JimmyValid := False;
	end else
		JimmyValid := True;
end;

procedure TInputAddress.SetForWho;
begin
	if PAddress(Jimmy)^.ForWho = -1 then PAddress(Jimmy)^.ForWho := AddressOwner^.RecNo;
end;

procedure TInputAddress.MakeInitParam;
begin
	inherited MakeInitParam(Initparam); {clear}
	if AddressOwner<>nil then InitParam.ForWho := AddressOwner^.RecNo;
end;

procedure TInputAddress.HandleEvent;
{var	A : PAddress;{}
begin
{	if (Event.What = evBroadcast) and (Event.Command=cmStoreJimmy) and
		(PADdress(Jimmy)^.ForWho = -1) then
			{address owner just been stored by cmstorejimmy, so set recno}
{			PAddress(Jimmy)^.ForWho := AddressOwner^.RecNo;{}
	{Check for dial *before* the telephone line catches it}
{	if (Owner^.Phase = phFocused) then begin

		if (srType<>cmNewemailAddress) and (Event.What = evKeydown) and (Event.KeyCode = kbDial) then begin
			ProgramPause('TInputAddress not handling this yet - should give menu of all options');
			{But not if on fax number....}
			{ClearEvent(Event)}
{		end;
	end; too complicated, anyway, leave for directory item dial to handle
	multiple lines - if you dial from an address teleophone line, that is
	the telephone you want...}

	inherited HandleEvent(Event);

	if (Owner^.Phase = phFocused) then begin

		if (Event.What = evKeydown) and (Event.KeyCode = kbList) then begin
			ClearEvent(Event);
			ExecuteList(AddressOwner);
		end;

		if (Event.What = evKeydown) and (Event.KeyCode = kbSuperList) and
				(AltAddressOwner<>nil) then begin

			ClearEvent(Event);
			ExecuteList(AltAddressOwner);
		end;

		if (Event.COmmand=cmAcceptJimmy) and GetState(sfSelected) then begin
			{$IFDEF fixit}
				Owner^.SetData(AddressOwner^); {update any changes - ie ptr2addresses}
				Owner^.ReDraw;
			{$ENDIF}

			{accept address from list}
			Owner^.Lock;
			SetChanged;
			SetData(Event.InfoPtr);
			ReDraw;
			Owner^.Unlock;

			ClearEvent(Event);
		end;
	end;

end;

procedure TInputAddress.ExecuteList(const ListAddOwner : PJimmy);
var Bounds : TRect;
		ChainWindow : PListWindow;

begin
	{Need to make sure address Owner is stored before accessing list, so that
	list has something to tie to}
	{check for changed as well in case address has changed & should be stored}
	if (AddressOwner^.RecNo =-1)
{		or (Message(Owner, evBroadCast, cmGetChangedIndicator, nil)<>nil){} then begin
		Owner^.GetData(AddressOwner^); {update any changes}
		AddressOwner^.StoreSelf;
	end;

	Owner^.SetState(sfActive, False); {switch off frame}

	{produce alternative address list}
	{Set bounds for window}
	Bounds.Assign(0,0,50,10);
	CentreOnView(Bounds, @Self);

	{Execute list}
	ChainWindow := NewJimmyHookWindow(Bounds,
													lsAddresses,
													0, {restrictions - none}
													hkAddress,
													ListAddOwner,
													nil);
	ChainWindow^.List^.AcceptorLink := @Self;
	Desktop^.ExecView(ChainWindow); {modal}
	dispose(ChainWindow, done);

	Owner^.SetState(sfActive, True); {switch frame back on}
end;


procedure TInputAddress.InsertEditFields;
begin
	HelpCtx := hcAddresses;

	inherited InsertEditFields;

	{Skip admin stuff}
	Insert(New(PSkipBytes, init(sizeof(TJimmy))));
	Insert(New(PSkipBytes, init(4))); {skip "for"}
	Insert(New(PSkipBytes, init(sizeof(TDateRange)))); {skip daterange}
	Insert(New(PSkipBytes, init(1))); {skip adtypr}

	{now we insert the fields...}
	PAddress(Jimmy)^.AddEditFields(0,0, @Self);

	SelectNext(False); {move to first one}
end;




{**************************************
 ***   CREATORS, ETC                ***
 **************************************}
function CreateAddress(P : pointer; const adType : byte) : pointer;
var Address : PAddress;
begin
	Address := New(PAddress, init(PJImmyInitParam(P)));

	Address^.adType := adType;
	case adType of
		adUSA : Address^.Country := 'USA';
{		adUK : Address^.Country := 'UK'; might be a delivery address}
	else
		Address^.Country := ProgramSetup.Get(siCountry,'UK');
	end;

	CreateAddress := Address;
end;

function CreateUKAddress(P : pointer) : pointer; far;
begin	CreateUKAddress := CreateAddress(P, adUK);end;

function CreatePOBoxAddress(P : pointer) : pointer; far;
begin	CreatePOBoxAddress := CreateAddress(P, adPOBox);end;

function CreateUSAAddress(P : pointer) : pointer; far;
begin	CreateUSAAddress := CreateAddress(P, adUSA);end;

function CreateEuropeanAddress(P : pointer) : pointer; far;
begin	CreateEuropeanAddress := CreateAddress(P, adEurope);end;

function CreateEmailAddress(P : pointer) : pointer; far;
begin	CreateEmailAddress := CreateAddress(P, ademail);end;


begin
{$IFDEF fixit} writeln('Initialising Address unit....'); {$ENDIF}

	RegisterType(RAddress); {Register for streams}

	RegisterWithList(lsAddresses, '~N~ew', NewItem('~U~K Address','',kbNone, cmNewUKAddress, hcNoContext, nil),nil);
	RegisterCreator(cmNewUKAddress, CreateUKAddress);

	RegisterWithList(lsAddresses, '~N~ew', NewItem('~P~O Box Address','',kbNone, cmNewPOBoxAddress, hcNoContext, nil),nil);
	RegisterCreator(cmNewPOBoxAddress, CreatePOBoxAddress);

	RegisterWithList(lsAddresses, '~N~ew', NewItem('US~A~ Address','',kbNone, cmNewUSAAddress, hcNoContext, nil),nil);
	RegisterCreator(cmNewUSAAddress, CreateUSAAddress);

	RegisterWithList(lsAddresses, '~N~ew', NewItem('~E~uropean Address','',kbNone, cmNewEuropeanAddress, hcNoContext, nil),nil);
	RegisterCreator(cmNewEuropeanAddress, CreateEuropeanAddress);

	RegisterWithList(lsAddresses, '~N~ew', NewItem('~e~mail Address','',kbNone, cmNewEmailAddress, hcNoContext, nil),nil);
	RegisterCreator(cmNewEmailAddress, CreateEmailAddress);
end.
