{**************************************************************
 ***                                                        ***
 ***                 RALLY VIEWS                            ***
 ***                                                        ***
 **************************************************************}
{$I compdirs}
{Split from krally module due to module too big for real mode
compiling}
unit KRLYView;

INTERFACE

uses
			drivers,
			jimindxs,
			tuijimmy,
			jimhooks,
			idindex,
			inpjimmy,
			tuilist,
			objects;

procedure NewGeogPosView(Bounds : TRect);
{procedure NewCompPosView(Bounds : TRect);{}
procedure NewRunningPosView(Bounds : TRect);

procedure NewControlList(Bounds : TRect);
procedure NewCarNumberList(Bounds : TRect);

type
	PControlListView   = ^TControlListView;          {Interior}
	TControlListView   = object(TIndexedJimmyListView)
		constructor Init(Bounds : TRect);
		procedure HandleEvent(var Event : TEvent); virtual;
		function GetSearch4Index : string; virtual;  {converts search typed to search to search on}
		function GetSearch4Display : string; virtual;  {converts search typed to search to search on}
		procedure SetTabs; virtual;
	end;

	PCarListView   = ^TCarListView;          {Interior}
	TCarListView   = object(TIndexedJimmyListView)
		procedure HandleEvent(var Event : TEvent); virtual;
		function GetSearch4Index : string; virtual;  {converts search typed to search to search on}
		procedure PrintList(const TaggedOnly : boolean; const UpToItemNo,NumPages : longint); virtual;
		procedure SetTabs; virtual;
	end;

	PStartListView = ^TStartListView;
	TStartListView = object(TRefNumList)
		procedure PrintList(const TaggedOnly : boolean; const UpToItemNo,NumPages : longint); virtual;
	end;

	{auto-cancels on update - for press view}
	PUpdaterRallyBox	= ^TUpdaterRallyBox;
	TUpdaterRallyBox	= object(TJimmyEditBox)
		procedure HandleEvent(var Event : TEvent); virtual;
	end;

	PTimingListView   = ^TTimingListView;          {Interior}
	TTimingListView   = object(TDlgHookView)
		procedure CutToClipBoard(ItemNo : longint); virtual;
	end;

	PInputCar = ^TInputCar;
	TInputCar = object(TinputIndexedJimmy)
		constructor Init(Bounds : TRect; NFieldLen : byte);
		function SearchOn : string; virtual; {converts data tzped to search string}
		function CreateList(Bounds : TRect) : PListWindow; virtual;
	end;

	PInputCP = ^TInputCP;
	TInputCP = object(TinputIndexedJimmy)
		CType : string;

		constructor Init(Bounds : TRect; NFieldLen : byte; NCType : string);
		function SearchOn : string; virtual; {converts data tzped to search string}
		function Valid(Command : word) : boolean; virtual;
		function CreateList(Bounds : TRect) : PListWindow; virtual;
	end;

	procedure StartStartList;


IMPLEMENTATION

uses
			tasks,
			menus,
			krally,
			krlysetu,
			global,
			devices, printers,
			indexes,
			minilib,
			jimmys,
			jimprint,
			views,
			messtext,
			dialogs,
			files,
			app,
			tuiedit,
			tui,
			tuimsgs,
			scodes,
			dattime;

procedure TUpdaterRallyBox.HandleEvent;
begin
	if (Event.What = evBroadCast) and (Event.Command = cmUpdateAll) then begin
		{if we are doing an update, edit dialog boxes should automatically cancel
		unless this is overridden, as the information won't be updated}
		if PJimmy(Jimmy)^.RecNo<>-1 then {if it's new, don't need to}
			Message(@Self, evCommand, cmCancel, nil);
	end;

	inherited HandleEvent(Event);
end;



{***********************************************************************
 ***                    INPUT OBJECTS                                ***
 ***********************************************************************}

{============ INPUT CAR =======================}
{Selects by number only}
constructor TInputCar.Init;
begin
	inherited Init(Bounds, NFieldLen, fiCarNumberIdx, lsCar, '');
end;

function TInputCar.SearchOn;
begin
	SearchOn := PadZero(N2Str(S2Num(FirstWord(Data^))),3);
end;

function TInputCar.CreateList;
begin
	CreateList := New(PListWindow, init(
		Bounds, GetMessage(msListTitles, lsType),
		New(PCarListView, init(
			Bounds, lsType, fiType, ''
		))
	));
end;

{=========== INPUT CONTROL POINT ===============}
{User can type in just number - search has to ignore letters anyway}
{pass subindexstring as restrictor if required}
constructor TInputCP.Init;
begin
	inherited Init(Bounds, NFieldLen,  fiControlIdx, lsRoute, '');
	CType := NCType;
end;

function TInputCP.SearchOn;
var S : string;
		B : byte;
begin
	S := FirstWord(Data^);
	{remove tc, etc}
	B := 1; while (B<length(S)) and ((S[B]<#48) or (S[B]>#57)) do inc(B);
	S := Copy(S,B,99);
	SearchOn := PadZero(N2Str(S2Num(S)),3);
end;

function TInputCP.Valid;
var V : boolean;
begin
	V := inherited Valid(Command);

	if V and DoValidFor(Command) and (CType<>'') and (GetJimmy<>nil) then begin
		if delspaceR(PControl(GetJimmy)^.CType)<>delspace(CType) then begin
			if Command<>cmAccept then Focus;
			InputWarning('Should be '+ExpandSCode(scCPType, CType),hcNoContext);
			V := False;
		end;
	end;

	Valid := V;
end;

function TInputCP.CreateList;
begin
	CreateList := New(PListWindow, init(
		Bounds, GetMessage(msListTitles, lsType),
		New(PControlListView, init(
			Bounds
		))
	));
end;

{**********************************************
 ***             CONTROL LIST VIEW          ***
 **********************************************}
{Allows entry of timing direct from control list, and does search
by comparing just CP number not complete type/number}

constructor TControlListView.Init;
begin
	inherited Init(Bounds, lsControl, fiControlIdx, '');
	SearchCol := 2;
	ColHeader := 'TC'+Tab+'Ps'+Tab+'Name'+Tab+'    km'+Tab+' ETT'+Tab+'ETA L'+Tab+'Lateness';
end;

procedure TControlListView.SetTabs;
begin
	if Size.X>57 then
		Tabs := #5#8#30#38#45#52
	else
		Tabs := #5#8#20#27#33#39;
end;

procedure TControlListView.HandleEvent;
var Event2 : TEvent;
begin
	if (Event.What = evCommand) and DrawnFocused then begin

		case Event.Command of
			{Attached information - edit & press button automatically}
			cmNewTiming: begin
				{Stack up which button will be pressed}
				Event2.What := evKeyDown;
				Event2.InfoPtr := nil;
				Event2.KeyCode := kbAltT;   {timing list}
				QueueEvent(Event2);

				{and ask to edit now}
				Event.What := evCommand;
				Event.Command := cmEdit;
			end;
		end;
	end;

	inherited HandleEvent(Event);
end;


function TControlListView.GetSearch4Index : string;
begin
	GetSearch4Index := PadZero(Search,3);
end;

function TControlListView.GetSearch4Display : string;
begin
	if Search='' then GetSearch4Display := '' else GetSearch4Display := PadZero(Search,2);
end;


{**********************************************
 ***             TIMINGS LIST VIEW          ***
 **********************************************}
{Deletion of timings has to be done carefully so as to remove entry from
timing grid, and remove timing completely, not leave half entry in control
list if deleted in car's list, and v.v.}

procedure TTimingListView.CUtToClipBoard;
var Hook : PHook;
		Jimmy : PJimmy;
begin
	Hook := PHook(Tree^.NodeAt(ItemNo));
	Jimmy := PJimmy(GetJimmy(Hook^.JimmyID));

	DeleteJimmy(Jimmy, True);

	dispose(Jimmy, done);

	LoadTree;
end;

{**********************************************
 ***             START LIST VIEW             ***
 **********************************************}
procedure LinkSLForm(const Linker : PInputLinker; const CallingView : PView); far;
var PrintAs : word;
		FormName : string;
begin
	if CallingView = Linker^.SourceView[1] then begin
		CallingView^.GetData(PrintAs);

		case PrintAs of
			0 : begin FormName := 'STARTLST'; end;
			1 : begin FOrmName := 'CETSLST'; end;
		end;

		Linker^.TargetView[1]^.SetData(FOrmName);
		Linker^.TargetView[1]^.DrawView;
	end;
end;

procedure TStartListView.PrintList(const TaggedOnly : boolean; const UpToItemNo,NumPages : longint);
var Item : longint;
		JimmyID : longint;
		Control : word;
		PrintType : TJimmyPrintType;
		Device : PDeviceStream;
		Jimmy : PJimmy;
		Pos : word;
		PrintAs : PSitem;
		PrintAsLink : pointer;

begin
	PrintType := BlankPrintType;
	PrintAs := 	NewSItem('~S~tart list',
							NewSItem('~C~ETS', nil));
	PrintAsLink := @LinkSLForm;
	Control := GetJimmyPrintType('Print Start List', PrintType, PrintAs, PrintAsLink);
	if Control=cmCancel then exit;

	Device := SetDeviceFrom(PrintType.Editor, PrintType.Target, PrintType.DeviceName);

	THinkingOn('Printing List');

	Device^.ClearCodes;
	Device^.FormCodes^.SetStr('RTITLE','Start List');

	Device^.StartPrint('STARTLST','REPORT');
	Item := FirstItem;
	Pos	:= 0;
	while (Item<=LastItem) and (Item>-1) do begin
		Jimmy := PJimmy(GetJimmy(IDIndexFile^.GetIDPtr(Item)));
		if Jimmy<>nil then begin
			inc(Pos);
			Device^.FormCodes^.SetStr('POS',N2Str(Pos));
			Jimmy^.PrintForm(Device, 'STARTLST.FRM');
			dispose(Jimmy, done);
		end;
		GetNextItemNo(Item);
	end;
	Device^.EndPrint;{}
	THinkingOff;
end;


{**********************************************
 ***             CAR  LIST VIEW             ***
 **********************************************}
{As above, allows entry of timing direct from list, and also helps
search by padding search string with zeros.  Summary list of drivers
also available}

procedure TCarListView.HandleEvent;
var Event2 : TEvent;
begin
	if (Event.What = evCommand) and DrawnFocused then begin

		case Event.Command of
			{Attached information - edit & press button automatically}
			cmNewTiming: begin
				{Stack up which button will be pressed}
				Event2.What := evKeyDown;
				Event2.InfoPtr := nil;
				Event2.KeyCode := kbAltT;   {timing list}
				QueueEvent(Event2);

				{and ask to edit now}
				Event.What := evCommand;
				Event.Command := cmEdit;
			end;
		end;
	end;

	if (Event.What = evCommand) and (Event.Command=cmPrintFirstTen) then begin
		PrintList(False, 10,0);
	end;

	inherited HandleEvent(Event);
end;


function TCarListView.GetSearch4Index : string;
begin
	GetSearch4Index := PadZero(Search,3); {database is 3 digits}
end;

procedure TCarListView.PrintList;
var Form : string[8];
		IdxRec : longint;
		Device : PDeviceStream;
		Jimmy : PJimmy;
		CarPos : word;

begin
	THinkingOn('Printing list');

	case fiType of
		fiCarRacePosIdx : Form := 'CARPOS';
	else
		Form := 'CARLIST';
	end;

	Device := Printer;

	Device^.StartPrint(form,'REPORT');

	CarPos := 0;
	IdxRec := 0;

	while (IdxREc<Stream(fiType)^.NoRecs) and ((UpToItemNo=0) or (CarPos<UpToItemNo)) do begin

		Jimmy := PIndexedJimmyStream(Stream(fiType))^.GetJimmyAtIdx(IdxRec);

		if Jimmy<>nil then begin
			Device^.FormCodes^.SetPrefix('');

			inc(CarPos); Device^.FormCodes^.SetStr('POS',N2Str(CarPos));

			Jimmy^.SetFormCodes(Device^.FOrmCodes);

			Device^.PrintForm(Form+'.FRM');

			dispose(Jimmy, done);
		end;

		inc(IdxRec);
	end;

	Device^.EndPrint;

	ThinkingOff;
end;

procedure TCarListView.SetTabs;
begin
	if lsType = lsRace then
		if Size.X>60 then
			Tabs := #3#6#50#65
		else if Size.X>40 then
				Tabs := #3#6#30#45
			else
				Tabs := #3#6#23#28
	else
		if Size.X>60 then
			Tabs := #3#55#60
		else if Size.X>40 then
				Tabs := #3#35#40
			else
				Tabs := #3#23#28;
end;


{**************************************************************
 ***                                                        ***
 ***                 PICTURE POSITION                       ***
 ***                                                        ***
 **************************************************************}
{a view with scroll bars for viewing either race position or geographical
position by section.  Consists of a top line bar of CP's, a collection for
the cars, scroll offsets, etc}
type
	PCarItem = ^TCarItem;
	TCarItem = object(TObject)
		Text	: Pstring;
		CPPos	: longint; {pointer to CP race position}
		Key 	: longint; {seconds of time taken/cum penalty/time departure depending on view}
		constructor Init(NText : string; NCPPos, NKey : longint);
		destructor Done; virtual;
		function GetKey : string; {returns combined cppos & key}
	end;

	constructor TCarItem.Init;
	begin
		inherited Init;
		Text := NewStr(NText);
		CPPos := NCPPos;
		Key := NKey;
	end;

	destructor TCarItem.Done;
	begin
		DisposeStr(Text);
		inherited Done;
	end;

	function TCarItem.GetKey;
	begin
		GetKey := RPakLint(CPPos)+RPakLint(Key)+Text^;
	end;

type
	PCPItem = ^TCPItem;
	TCPItem = object(TObject)
		Text : Pstring;
		constructor Init(NText : string);
		destructor Done; virtual;
	end;

	constructor TCPITem.Init;
	begin
		inherited Init;
		Text := NewStr(NText);
	end;

	destructor TCPItem.Done;
	begin
		disposeStr(Text);
		inherited Done;
	end;


type
	PCarCollection = ^TCarCollection;
	TCarCollection = object(TSortedCollection)
		function Compare(Key1, Key2: Pointer): Integer; virtual;
	end;

	function TCarCollection.Compare;
	begin
		if PCarItem(Key1)^.GetKey<PCarItem(Key2)^.GetKey then
			Compare := -1
		else
			if PCarItem(Key1)^.GetKey=PCarItem(Key2)^.GetKey then
				Compare := 0
			else
				Compare := 1;
	end;

type
	PPosPicture = ^TPosPicture;
	TPosPicture = object(TView)
		Cars : PCarCollection;
		CPs		: PCollection;
		MinCPPos, MaxCPPos : word; {limits to scrolling}
		MaxYPos : word; {same}
		VScrollBar, HScrollBar : PSCrollBar;

		CarOffset,CPOffset,CPGap : integer;

		DoneFirstLoad : boolean;
		Search : string;
		Focused : PCarItem;

		constructor Init(Bounds : TRect);
		destructor Done; virtual;
		procedure HandleEvent(var Event : TEvent); virtual;
		procedure LoadCars; virtual;
		procedure Draw; virtual;
		procedure LoadCPs; virtual;
		procedure FocusCar(const CarNo : word);
		procedure ChangeBounds(var Bounds: TRect); virtual;
		procedure SetScrollBars;
		procedure CalcRaceInfo;
		function GetPalette: PPalette; virtual;
	end;

constructor TPosPicture.Init;
var R : Trect;
begin
	inherited Init(Bounds);

	New(Cars, init(50,10));
	Cars^.Duplicates := True; {allow duplicates}
	New(CPs, init(50,10));

	EventMask := EventMask or evBroadCast;
	Options := (Options or ofSelectable or ofFirstClick);
	GrowMode := gfGrowHiX + gfGrowHiY;

	R.Copy(Bounds); R.A.X := R.B.X;	inc(R.B.X); New(VSCrollBar, init(R));
	R.Copy(Bounds); R.A.Y := R.B.Y;	inc(R.B.Y); New(HSCrollBar, init(R));

	{display parameters}
	CarOffset := 0;
	CPOffset  := 0;
	CPGap			:= 7;
	MaxYPOs		:= 0;

	FileAdmin(fiJimmys)^.LogOn;

	DoneFirstLoad := False;
	Search := '';
	Focused := nil;
end;

destructor TPosPicture.Done;
begin
	dispose(Cars, done);
	dispose(CPs, done);
	FileAdmin(fiJimmys)^.LogOff;
	inherited Done;
end;

procedure TPosPicture.HandleEvent;
begin
	inherited HandleEvent(Event);

	if not DoneFirstLoad then begin
		DoneFirstLoad := True;
		LoadCPs; {load control array}
		LoadCars;
		HScrollBar^.SetValue(HScrollBar^.Max);
		Draw;
	end;

	if (Event.What = evBroadCast) then
		case Event.Command of
			cmRedraw : begin
				LoadCars;
				if Search<>'' then
					FocusCar(S2Num(Search))
				else
					HScrollBar^.SetValue(HScrollBar^.Max);
				Draw;
			end;

			cmJimmyStored :
				case PJimmyStoredInfo(Event.InfoPtr)^.Jimmy^.srtype of
					srTiming,srCar 	: begin LoadCars; HSCrollBar^.SetValue(HScrollBar^.Max); Draw; end;
					srControl 			: begin LoadCPs; Draw; end;
				end;

			cmScrollBarChanged :
				if (Event.InfoPtr=VScrollBar) or (Event.InfoPtr=HScrollBar) then begin
					DrawView;
					ClearEvent(Event);
				end;
		end;

	if (Event.What = evKeyDown) then
		case Event.KeyCode of
			{let scroll bars validate}
			kbLeft 	: begin HSCrollBar^.SetValue(HSCrollBar^.Value-1); ClearEvent(Event); Search := ''; end;
			kbRight : begin HSCrollBar^.SetValue(HSCrollBar^.Value+1); ClearEvent(Event); Search := ''; end;
			kbUp		: begin VSCrollBar^.SetValue(VSCrollBar^.Value-1); ClearEvent(Event); Search := ''; end;
			kbDown 	: begin VSCrollBar^.SetValue(VSCrollBar^.Value+1); ClearEvent(Event); Search := ''; end;
			kbESC		: StartEvent(evCommand, cmClose);

			kbBack  : if Search<>'' then begin dec(Search[0]); FocusCar(S2Num(Search)); ClearEvent(Event); end;
		end;

	if (Event.What=evKeyDown) then
		if (Event.CharCode>=#32) and (Event.CharCode<=#127) then begin
			Search := Search + Event.CharCode;
			FocusCar(S2Num(Search));
			ClearEvent(Event);
		end;

end;

procedure TPosPicture.LoadCars;
begin
	{descendant will need to load collection with caritems appropriate text}
end; {descendant to do}

procedure TPosPicture.LoadCPs;
var	CPPos : longint;
		Control : PControl;
		Text : string;
begin
	ThinkingOn('Loading Controls');
	CPs^.FreeAll;

	MinCPPos := 0;

	FileAdmin(fiRouteIDIdx)^.LogOn;
	MaxCPPos := IDStream(fiRouteIDIdx)^.GetNewID-1;

	CPs^.Insert(New(PCPItem, init(' '))); {0 index is not a CP}

	for CPPos := 1 to MaxCPPos do begin
		Control := PControl(GetJImmy(IDStream(fiRouteIDIdx)^.GetIDPtr(CPPos)));

		Text := Control^.GetName(naREf, 0); {+#196+Control^.ETT.Digit5;{}
		CPs^.Insert(New(PCPItem, init(Text)));
		dispose(Control, done);
	end;

	FileAdmin(fiRouteIDIdx)^.LogOff;
	SetScrollBars;
	ThinkingOff;
end;

{==== DRAW ========}
procedure TPosPicture.Draw;
var	CarItemNum : integer;
		CarItem : PCarItem;
		X,Y,LastX : integer;
		CP, NumCP : integer;
		Attr : byte;

begin
	inherited Draw;

	{do CP bar}
	NumCP := Size.X div CPGap;
	WriteChar(0,0, #196, 2,Size.X);
	if NumCP+HSCrollBar^.Value>=CPs^.Count then NumCP := CPs^.Count - HSCrollBar^.Value-1;

	for CP := HScrollBar^.Value to HScrollBar^.Value + NumCP do
		writeStr((CP-HSCrollBar^.Value)*CPGap+CPOffset, 0, PCPItem(CPs^.At(CP))^.Text^, 3);

	{"more" arrows}
	if HScrollBar^.Value>HScrollBar^.Min then WriteChar(0,0, #17, 2,1);
	if HScrollBar^.Value<HscrollBar^.Max then WriteChar(Size.X-1,0, #16, 2,1);

	LastX := -1; MaxYPos := 0;
	for CarItemNum := 0 to Cars^.Count-1 do begin
		CarItem := PCarItem(Cars^.At(CarItemNum));
		if (CarItem^.CPPos>=HSCrollBar^.Value) and (CarItem^.CPPos<=HSCrollBar^.Value+NumCP) then begin
			X := CarOffset+(CarItem^.CPPos-HScrollBar^.Value)*CPGap;

			if LastX<>X then begin
				LastX := X;
				Y := 1;
			end;

			{draw item}
			if Y-VScrollBar^.Value>=0 then begin
				if pointer(CarItem) = pointer(Focused) then Attr := 6 else Attr := 4;
				WriteStr(X,Y-VSCrollBar^.Value+1, CarItem^.Text^,Attr);
			end;

			inc(Y);
			if Y>MaxYPos then MaxYPos := Y;
		end;
	end;

	SetScrollBars;
end;

procedure TPosPicture.FocusCar;
var CarItem : PCarItem;
		I : longint;
begin
	if CarNo=0 then
		Focused := nil
	else begin
		{search from end so that we get *latest* for section scores/etc}
{		I := Cars^.Count-1;
		while (I>=0) and (S2Num(copy(PCarItem(Cars^.At(I))^.Text^,1,3))<>CarNo) do dec(I);

		if I>=0 then Focused := PCarItem(Cars^.At(I)) else{} Focused := nil{}
	end;
	Draw;
end;

function TPosPicture.GetPalette;
{1 - background/text, 2 - cp bar, 3 - cp's, 4 - cars}
const
	P : string[4] = #6#6#6#6;  {6 static text & labels 7 - normal, 8 highlight}
begin
	GetPalette := @P;
end;


procedure TPosPicture.ChangeBounds;
begin
	inherited ChangeBounds(Bounds);
	SetScrollBars;
	Draw;
end;

procedure TPosPicture.SetScrollBars;
begin
	HScrollBar^.SetRange(MinCPPos, MaxCPPos - (Size.X div CPGap)+1);
	HSCrollBar^.DrawView;
	VScrollBar^.SetRange(1, MaxYPos - Size.Y +2);
	VSCrollBar^.DrawView;
end;


{Runs through and works out position number and time difference of each
car at each section}
procedure TPosPicture.CalcRaceInfo;
var CarItem : PCarItem;
		Text,DiffS : string;
		CarItemNum : longint;
		Diff : longint;
		LastKey,LastCP : longint;
		Pos : word;

begin
	{the first one is at position 1}
	LastCP := -1; LastKEy := 0;
	for CarItemNum := 0 to Cars^.Count -1 do begin

		CarItem := PCarItem(Cars^.At(CarItemNum));

		if LastCP<>CarItem^.CPPos then begin
			{first one in section, at position 1}
			LastKey := CarItem^.Key;
			LastCP := CarItem^.CPPos;
			Pos := 1;
			DiffS := '';
		end else begin
			Diff := CarItem^.Key - LastKey;
			if Diff<>0 then inc(Pos);
			if Diff>59 then
				DiffS := '+'+N2Str(Diff div 60)+#39+N2Str(Diff mod 60)+'"'
			else
				if Diff=0 then
					DiffS := '+0'
				else
					DiffS := '+'+N2Str(Diff)+'s';
		end;

		Text := PadSpaceL(N2Str(Pos),2)+' '+CarItem^.Text^+' '+DiffS;
		DisposeStr(CarItem^.Text);
		CarItem^.Text := NewStr(Text);
	end;
end;



type
	PPosWindow = ^TPosWindow;
	TPosWindow = object(TWindow)
		constructor Init(Bounds : TRect; NTitle : string; PosPicture : PPosPicture);
	end;

	constructor TPosWindow.Init;
	begin
		inherited Init(Bounds, NTitle,0);
		Options := Options or ofSelectable or ofFirstClick;
		EventMask := EventMask or evBroadCast;
		Insert(PosPicture);
		Insert(PosPicture^.VSCrollBar);
		Insert(PosPicture^.HScrollBar);
	end;



{**********************************************************
 ***                                                    ***
 ***          GEOGRAPHICAL PICTURE                      ***
 ***                                                    ***
 **********************************************************}
{Cars are listed by sorted control point and departure time, minimum
scroll point is first car and maximum is end of leg (allow one gap
past it so finishers can be viewed).  Display number running{}

type
	PGeogPosView = ^TGeogPosView;
	TGeogPosVIew = object(TPosPicture)

		procedure LoadCars; virtual;
		procedure Draw; virtual;
	end;


procedure TGeogPosView.LoadCars;
var Rec : longint;
		Car : Pcar;
		CarItem : PCarItem;
		Timing : PTiming;
		Control : PControl;
		Text : string;

begin
	ThinkingOn('Loading Positions');
	Cars^.FreeAll;

	{run through cars, picking up location}
	FileAdmin(fiCarRacePosIdx)^.LogOn;
	FileAdmin(fiJimmys)^.LogOn;
	FileAdmin(fiHooks)^.LogOn;

	MinCPPos := 999; MaxCPPos := 0;

	for Rec := 0 to Stream(fiCarRacePosIdx)^.NoRecs-1 do begin
		Car := PCar(PIndexedJimmyStream(Stream(fiCarRacePosIdx))^.GetJimmyAtIdx(Rec));

		if (Car<>nil) and ((Car^.Status and stRunning)>0) then begin
			Text := Car^.GetName(naRef,0);

			{most recent position}
			Control := Car^.GetLastControl;
			if Control<>nil then begin
				Timing := Car^.GetLastTiming;

				Text := Text + ' '+Timing^.TD.Digit5;
				{$IFNDEF RallyPress}
				if Car^.IsTimeBarred(TimeNow) then Text := Text + 'TB';
				{$ENDIF}

				New(CarItem, init(Text, Control^.RoutePos, Timing^.TD.Mins));

				if MinCPPos>Control^.RoutePos then MinCPPos := Control^.RoutePos;
				if MaxCPPos<Control^.RoutePos then MaxCPPos := Control^.RoutePos;

				dispose(Control, done);
				dispose(Timing, done);
			end else begin
				New(CarItem, init(Text, 0, Car^.StartPos));
				MinCPPos := 0;
			end;

			Cars^.Insert(CarItem);
		end;

		if Car<>nil then dispose(Car, done);

	end;

	FileAdmin(fiCarRacePosIdx)^.LogOff;
	FileAdmin(fiHooks)^.LogOff;
	FileAdmin(fiJimmys)^.LogOff;

	CarOffset := 4;
	CPGap := 14;

	SetScrollBars;

	DrawView;
	ThinkingOff;
end;

procedure TGeogPosView.Draw;
begin
	inherited Draw;

	WriteStr(0,Size.Y-1, N2Str(Cars^.Count)+' Running      ',1);
end;

{**********************************************************
 ***                                                    ***
 ***                 COMPETITVE PICTURE                 ***
 ***                                                    ***
 **********************************************************}
{Cars are listed by timings in each section - ie each
section has all cars listed in order of first/last *for that section*.}

{This one works on just Competitive sections, so on loading cp's/cars,
bear in mind that the cp number is not the routepos, but has 1 - first
comp section, 2 - second comp section, 3 - third, etc}

type
	PCompPosView = ^TCompPosView;
	TCompPosVIew = object(TPosPicture)
		Cum : boolean; {cumulative marker}
		constructor Init(Bounds : TRect; NCum : boolean);
		procedure LoadCPs; virtual;
		procedure LoadCars; virtual;
	end;

constructor TCompPosView.Init;
begin
	inherited init(Bounds);
	Cum := NCum;
end;

procedure TCompPosView.LoadCPs;
var	CPPos : longint;
		Control : PControl;
		CurrentLeg : PLeg;
		StartCP, FinishCP : PControl;

begin
	ThinkingOn('Loading Controls');
	CPs^.FreeAll;

	MinCPPos := 0;

	CurrentLeg := PLeg(GetJimmy(RallySetup.CurrentLegID));
	if CurrentLeg=nil then begin
		ProgramWarning('No current leg set',hcNoContext);
		exit;
	end;

	FileAdmin(fiRouteIDIdx)^.LogOn;
	MaxCPPos := IDStream(fiRouteIDIdx)^.GetNewID-1;

	CPs^.Insert(New(PCPItem, init(' '))); {0 index is not a CP}

	StartCP := PControl(GetJimmy(CurrentLeg^.CPStart));
	FinishCP := PControl(GetJimmy(CurrentLeg^.CPFinish));

	for CPPos := StartCP^.RoutePos to FinishCP^.RoutePos do begin
		Control := PControl(GetJImmy(IDStream(fiRouteIDIdx)^.GetIDPtr(CPPos)));

		if Control^.CType = ctComp then
			CPs^.Insert(New(PCPItem, init(Control^.GetName(naRef,0))));

		dispose(Control, done);
	end;

	FileAdmin(fiRouteIDIdx)^.LogOff;

	SetScrollBars;

	dispose(StartCP, done);
	dispose(FinishCP, done);
	dispose(CurrentLeg, done);

	CPGap := 38;
	CPOffset := 5;
	ThinkingOff;
end;

procedure TCompPosView.LoadCars;
var CP : longint;
		Car : Pcar;
		Timing : PTiming;
		Control : PControl;
		Text : string;
		MaxY : integer;
		ThisY : integer;
		CurrentLeg : PLeg;
		StartCP, FinishCP : PControl;
		NumCompCPs : word;
		Time : TTime;

begin
	ThinkingOn('Loading Positions');
	Cars^.FreeAll;

	CurrentLeg := PLeg(GetJimmy(RallySetup.CurrentLegID));
	if CurrentLeg=nil then begin
		ProgramWarning('No current leg set',hcNoContext);
		exit;
	end;

	{run through controls, picking up timings by section time}
	FileAdmin(fiRouteIDIdx)^.LogOn;
	FileAdmin(fiJimmys)^.LogOn;
	FileAdmin(fiHooks)^.LogOn;

	MinCPPos := 999; MaxY := 0; MaxCPPos := 0; NumCompCPs := 0;

	StartCP := PControl(GetJimmy(CurrentLeg^.CPStart));
	FinishCP := PControl(GetJimmy(CurrentLeg^.CPFinish));

	for CP := StartCP^.RoutePos to FinishCP^.RoutePos do begin
		Control := PControl(GetJimmy(IDStream(fiRouteIDIdx)^.GetIDPtr(CP)));
		ThisY := 0;

		if Control^.CType=ctComp then begin

			{read timings - hooked in order of time taken}
			Timing := PTIming(HookFile^.GetFirst(Control^.TimingsHook, srTiming));

			if Timing<>nil then begin
				inc(NumCompCPs);{}

				{set max/min only if at least one timing for this cp}
				if MinCPPos>NumCompCPs then MinCPPos := NumCompCPs;
				if MaxCPPos<NumCompCPs then MaxCPPos := NumCompCPs;
			end;

			while Timing<>nil do begin
				Car := PCar(GetJimmy(Timing^.CarID));

				if Cum then Time.SetTo(Timing^.CumTotal) else Time.SetTo(Timing^.Penalty);

				Text := Car^.GetName(naRef, 0)
								+' '+Car^.Class
								+setlength(WordNo(Car^.Driver,2),6)
								+' '+Time.Digit8;
				if not Cum then
					Text := Text +' '+padspaceL(N2Str(Timing^.Speed),3)+'k';

				Cars^.Insert(New(PCarItem, init(Text, NumCompCPs, Time.Secs)));

				dispose(Timing, done);
				dispose(Car, done);
				Timing := PTiming(HookFile^.GetNextJimmy);

				inc(ThisY);
			end;

			if MaxY<ThisY then MaxY := ThisY;

		end;
		dispose(Control, done);
	end;

	dispose(StartCP, done);
	dispose(FinishCP, done);
	dispose(CurrentLeg, done);

	FileAdmin(fiHooks)^.LogOff;
	FileAdmin(fiJimmys)^.LogOff;
	FileAdmin(fiRouteIDIdx)^.LogOn;

	CalcRaceInfo;

	SetScrollBars;

	DrawView;
	ThinkingOff;
end;



{**********************************************************
 ***                                                    ***
 ***          	   RUNNING PICTURE                      ***
 ***                                                    ***
 **********************************************************}
{Cars are listed by cumulative timings up to that section - ie
the position they were in the race on completing that section}

type
	PRunningPosView = ^TRunningPosView;
	TRunningPosVIew = object(TPosPicture) {derive from competitve raceposview to get calcraceinfo}
		procedure LoadCars; virtual;
	end;


procedure TRunningPosView.LoadCars;
var CP : longint;
		Car : Pcar;
		Timing : PTiming;
		Control : PControl;
		CurrentLeg : PLeg;
		StartCP, FinishCP : PControl;

begin
	ThinkingOn('Loading Positions');
	Cars^.FreeAll;

	CurrentLeg := PLeg(GetJimmy(RallySetup.CurrentLegID));
	if CurrentLeg=nil then begin
		ProgramWarning('No current leg set',hcNoContext);
		exit;
	end;

	{run through controls, picking up timings by section time}
	FileAdmin(fiRouteIDIdx)^.LogOn;
	FileAdmin(fiJimmys)^.LogOn;
	FileAdmin(fiHooks)^.LogOn;

	StartCP := PControl(GetJimmy(CurrentLeg^.CPStart));
	FinishCP := PControl(GetJimmy(CurrentLeg^.CPFinish));

	for CP := StartCP^.RoutePos to FinishCP^.RoutePos do begin
		Control := PControl(GetJimmy(IDStream(fiRouteIDIdx)^.GetIDPtr(CP)));

		{read timings - hooked in order of time taken}
		Timing := PTIming(HookFile^.GetFirst(Control^.TimingsHook, srTiming));
		while Timing<>nil do begin
			Car := PCar(GetJimmy(Timing^.CarID));

			{key is cumtime}
			Cars^.Insert(New(PCarItem, init(Car^.GetName(naRef, 0), CP, Timing^.CumTotal.Secs)));

			dispose(Timing, done);
			dispose(Car, done);
			Timing := PTiming(HookFile^.GetNextJimmy);
		end;
		dispose(Control, done);
	end;

	FileAdmin(fiHooks)^.LogOff;
	FileAdmin(fiJimmys)^.LogOff;
	FileAdmin(fiRouteIDIdx)^.LogOff;

	dispose(StartCP, done);
	dispose(FinishCP, done);
	dispose(CurrentLeg, done);

	CalcRaceInfo;

	MinCPPos := 1;

	CPGap := 11;
	CPOffset := 3;

	SetSCrollBars;

	DrawView;
	ThinkingOff;
end;



{********************************************************
 ***                 LISTS                            ***
 ********************************************************}

{==== PICTURES =========}
procedure NewGeogPosView(Bounds : TRect);
var	R : TRect;
begin
	R.XYLD(1,1,Bounds.B.X-Bounds.A.X-2, Bounds.B.Y-Bounds.A.Y-2);
	Desktop^.Insert(New(PPosWindow, init(Bounds,'GEOGRAPHICAL POSITION',New(PGeogPosView, init(R)))));
end;

procedure NewCumCompPosView(Bounds : TRect);
var	R : TRect;
begin
	R.XYLD(1,1,Bounds.B.X-Bounds.A.X-2, Bounds.B.Y-Bounds.A.Y-2);
	Desktop^.Insert(New(PPosWindow, init(Bounds,'OVERALL CS POSITION',New(PCompPosView, init(R,True)))));
end;

procedure NewSecCompPosView(Bounds : TRect);
var	R : TRect;
begin
	R.XYLD(1,1,Bounds.B.X-Bounds.A.X-2, Bounds.B.Y-Bounds.A.Y-2);
	Desktop^.Insert(New(PPosWindow, init(Bounds,'SECTION POSITION',New(PCompPosView, init(R,False)))));
end;

procedure NewRunningPosView(Bounds : TRect);
var	R : TRect;
begin
	R.XYLD(1,1,Bounds.B.X-Bounds.A.X-2, Bounds.B.Y-Bounds.A.Y-2);
	Desktop^.Insert(New(PPosWindow, init(Bounds,'RUNNING (CUMULATIVE) POSITION', New(PRunningPosView, init(R)))));
end;

{====== LISTS =========}
procedure NewLegList(Bounds : TRect);
begin
	Desktop^.Insert(New(PIndexedJimmyListWindow, init(Bounds, 'Legs',
		New(PIndexedJimmyListView, init(Bounds, lsLeg, fiLegIdx, '')))));
end;

procedure NewControlList(Bounds : TRect);
begin
	Desktop^.Insert(New(PIndexedJimmyListWindow, init(Bounds, 'Controls',
		New(PControlListView, init(Bounds)))));
end;

procedure NewCarNumberList(Bounds : TRect);
begin
	Desktop^.Insert(New(PIndexedJimmyListWindow, init(Bounds, 'Cars',
		New(PCarListView, init(Bounds, lsCar, fiCarNumberIdx, '')))));
end;

procedure NewRacePosList(Bounds : TRect);
begin
	Desktop^.Insert(New(PIndexedJimmyListWindow, init(Bounds, 'Rally Position',
		New(PCarListView, init(Bounds, lsRace, fiCarRacePosIdx, '')))));
	{$IFDEF RallyPress}
		Desktop^.DisableCommands([cmPrint, cmPRintBox, cmPrintAll, cmPRintTagged]);
	{$ENDIF}
end;

{procedure NewRaceClassPosList(Bounds : TRect);
begin
	Desktop^.Insert(New(PIndexedJimmyListWindow, init(Bounds, 'Rally Position by Class',
		New(PCarListView, init(Bounds, lsRace, fiCarRacePosClassIdx, '')))));
	{{$IFDEF RallyPress}
{		Desktop^.DisableCommands([cmPrint, cmPRintBox, cmPrintAll, cmPRintTagged]);
{	{$ENDIF}
{end;{}

procedure NewRouteList(Bounds : TREct);
var RefNumList : PRefNumList;
begin
	New(RefNumList, init(Bounds, srControl, 1, lsRoute));
	RefNumList^.Tabs := #5#8#30#38#45#52;
	Desktop^.Insert(New(PListWindow, init(Bounds, 'Route', RefNumList)));
end;

{******************************************
 ***          TASKS                     ***
 ******************************************}
procedure StartLegList; far;
var Bounds : TRect;
begin
	Bounds.Assign(0,0,30,10);	NewLegList(Bounds);
end;

procedure StartControlList;far;
var Bounds : TRect;
begin
	Desktop^.GetExtent(Bounds); NewControlList(Bounds);
end;

procedure StartCarNumberList; far;
var Bounds : TRect;
begin
	Desktop^.GetExtent(Bounds); NewCarNumberList(Bounds);
end;

procedure StartRacePosList; far;
var Bounds : TRect;
begin
	Desktop^.GetExtent(Bounds); NewRacePosList(Bounds);
end;

{procedure StartRaceClassPosList; far;
var Bounds : TRect;
begin
	Desktop^.GetExtent(Bounds); NewRaceClassPosList(Bounds);
end;{}

procedure StartRouteList; far;
var Bounds : TRect;
begin
	Desktop^.GetExtent(Bounds); NewRouteList(Bounds);
end;

procedure StartStartList; {defined in interface for rlysetu (make start list) too}
var Bounds : TRect;
		StartListView : PStartListView;
begin
	{$IFDEF rallypress}
	CloseAllViews;
	{$ENDIF}
	Desktop^.GetExtent(Bounds);
	New(StartListView, init(Bounds, srCar, 1, lsStartList));
	StartListView^.Tabs := #3#6#60#70;
	{$IFDEF RallyPress}
	StartListView^.ViewOnly := True;
	{$ENDIF}
	Desktop^.Insert(New(PListWindow, init(Bounds, 'Crews Eligable to Start', StartListView)));
end;

procedure StartGeogPosView; far;
var Bounds : TRect;
begin
	Bounds.Assign(0,0,80,14);	NewGeogPosView(Bounds);
end;

procedure StartCompPosView; far;
var Bounds : TRect;
begin
	Bounds.Assign(0,0,80,14);	NewCumCompPosView(Bounds);
end;


procedure SetupLists; far;
var Full, Bounds : TRect;
begin
	CloseAllViews;
	Desktop^.GetExtent(Full);
	Bounds.assign(0,0,60,Full.B.Y div 2-2);	NewCarNumberList(Bounds);	{Cars}
	Bounds.Assign(60,0,80,10);  					NewLegList(Bounds);				{legs}
	Bounds.Assign(60,10,80,Full.B.Y);     NewRouteList(Bounds);
	Bounds.Assign(0,Full.B.Y div 2-2,60,Full.B.Y); NewControlList(Bounds);
end;

procedure PressLists; far;
var Bounds : TRect;
		PicY : integer;
begin
	CloseAllViews;
	Desktop^.GetExtent(Bounds);
	PicY := Bounds.B.Y div 2;
	Bounds.B.Y := PicY;{}  			NewGeogPosView(Bounds);{}
	{cumulative times}
	Bounds.Move(0,PicY);
	Bounds.B.X := Bounds.B.X div 2;
	NewCumCompPosView(Bounds);
	{section times}
	Bounds.Move(Bounds.B.X,0);
	NewSecCompPosView(Bounds);
end;

procedure AuditLists; far;
var Bounds : TRect;
		F : PView;
		PicY : integer;
begin
	CloseAllViews;
	Desktop^.GetExtent(Bounds);
	PicY := Bounds.B.Y div 3;

	Bounds.B.Y := PicY;		NewGeogPosView(bounds);
	Bounds.Move(0,PicY);	NewCumCompPosView(Bounds);
	Bounds.Move(0,PicY); Bounds.B.X := Bounds.B.X div 2; NewControlList(Bounds);
	F := Desktop^.Current;

	Bounds.Move(Bounds.B.X,0);  NewCarNumberList(Bounds);
	F^.Focus;
end;

begin
	RegisterWithList(lsRace, '~P~rint',
						NewItem('T~o~p Ten', 'Shift-F9', kbShiftF9, cmPrintFirstTen, hcNoContext,
						NewLine(
							nil)), nil);

	RegisterWithList(lsRoute, '~P~rint',
						NewItem('~R~oute', 'F9', kbF9, cmPrintList, hcNoContext,
						NewLine(
							nil)), nil);

	RegisterWithList(lsStartList, '~P~rint',
						NewItem('~L~ist', 'F9', kbF9, cmPrintList, hcNoContext,
						NewLine(
							nil)), nil);

	{$IFNDEF RallyPress}
		RegisterNewWithList(lsCarTiming, '~T~iming', cmNewTiming);
		RegisterNewWithList(lsCarTiming, '~P~enalty', cmNewPenalty);
		RegisterNewWithList(lsControlTiming, '~T~iming', cmNewTiming);

		RegisterTask(DesktopTasks, cmNewLegList, 	@StartLegList);
		RegisterTask(DesktopTasks, cmNewControlList, @StartControlList);
		RegisterTask(DesktopTasks, cmNewCarNumberList, @StartCarNumberList);
		RegisterTask(DesktopTasks, cmNewCarRacePosList, @StartRacePosList);
{		RegisterTask(DesktopTasks, cmNewCarRacePosClassList, @StartRaceClassPosList);{}
		RegisterTask(DesktopTasks, cmGeogPos, @StartGeogPosView);
		RegisterTask(DesktopTasks, cmCompPos, @StartCompPosView);

		RegisterTask(DesktopTasks, cmNewRouteList, @StartRouteList);

		{{$IFNDEF MSDOS}
		RegisterTask(desktopTasks, cmRallySetupLists, @SetupLists);
		RegisterTask(desktopTasks, cmRallyAuditLists, @AuditLists);{}
		{{$ENDIF}
	{$ENDIF}

	RegisterTask(DesktopTasks, cmNewStartList, @StartStartList);{}
	RegisterTask(desktopTasks, cmRallyPressLists, @PressLists);{}
end.
