{ $Id$ This file is part of the Free Component Library (FCL) Copyright (c) 1999-2000 by Michael Van Canneyt and Florian Klaempfl See the file COPYING.FPC, included in this distribution, for details about the copyright. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. **********************************************************************} {********************************************************************** * Class implementations are in separate files. * **********************************************************************} var ClassList : TThreadlist; ClassAliasList : TStringList; { Include all message strings Add a language with IFDEF LANG_NAME just befor the final ELSE. This way English will always be the default. } {$IFDEF LANG_GERMAN} {$i constsg.inc} {$ELSE} {$IFDEF LANG_SPANISH} {$i constss.inc} {$ELSE} {$i constse.inc} {$ENDIF} {$ENDIF} { Utility routines } {$i util.inc} { TBits implementation } {$i bits.inc} { All streams implementations: } { Tstreams THandleStream TFileStream TResourcseStreams TStringStream } { TCustomMemoryStream TMemoryStream } {$i streams.inc} { TParser implementation} {$i parser.inc} { TCollection and TCollectionItem implementations } {$i collect.inc} { TList and TThreadList implementations } {$i lists.inc} { TStrings and TStringList implementations } {$i stringl.inc} { TPersistent implementation } {$i persist.inc } { TComponent implementation } {$i compon.inc} { TBasicAction implementation } {$i action.inc} { TDataModule implementation } {$i dm.inc} { Class and component registration routines } {$I cregist.inc} { Interface related stuff } {$ifdef HASINTF} {$I intf.inc} {$endif HASINTF} {********************************************************************** * Miscellaneous procedures and functions * **********************************************************************} { Point and rectangle constructors } function Point(AX, AY: Integer): TPoint; begin with Result do begin X := AX; Y := AY; end; end; function SmallPoint(AX, AY: SmallInt): TSmallPoint; begin with Result do begin X := AX; Y := AY; end; end; function Rect(ALeft, ATop, ARight, ABottom: Integer): TRect; begin with Result do begin Left := ALeft; Top := ATop; Right := ARight; Bottom := ABottom; end; end; function Bounds(ALeft, ATop, AWidth, AHeight: Integer): TRect; begin with Result do begin Left := ALeft; Top := ATop; Right := ALeft + AWidth; Bottom := ATop + AHeight; end; end; { Object filing routines } var IntConstList: TThreadList; type TIntConst = class IntegerType: PTypeInfo; // The integer type RTTI pointer IdentToIntFn: TIdentToInt; // Identifier to Integer conversion IntToIdentFn: TIntToIdent; // Integer to Identifier conversion constructor Create(AIntegerType: PTypeInfo; AIdentToInt: TIdentToInt; AIntToIdent: TIntToIdent); end; constructor TIntConst.Create(AIntegerType: PTypeInfo; AIdentToInt: TIdentToInt; AIntToIdent: TIntToIdent); begin IntegerType := AIntegerType; IdentToIntFn := AIdentToInt; IntToIdentFn := AIntToIdent; end; procedure RegisterIntegerConsts(IntegerType: Pointer; IdentToIntFn: TIdentToInt; IntToIdentFn: TIntToIdent); begin IntConstList.Add(TIntConst.Create(IntegerType, IdentToIntFn, IntToIdentFn)); end; function FindIntToIdent(AIntegerType: Pointer): TIntToIdent; var i: Integer; begin with IntConstList.LockList do try for i := 0 to Count - 1 do if TIntConst(Items[i]).IntegerType = AIntegerType then exit(TIntConst(Items[i]).IntToIdentFn); Result := nil; finally IntConstList.UnlockList; end; end; function FindIdentToInt(AIntegerType: Pointer): TIdentToInt; var i: Integer; begin with IntConstList.LockList do try for i := 0 to Count - 1 do with TIntConst(Items[I]) do if TIntConst(Items[I]).IntegerType = AIntegerType then exit(IdentToIntFn); Result := nil; finally IntConstList.UnlockList; end; end; function IdentToInt(const Ident: String; var Int: LongInt; const Map: array of TIdentMapEntry): Boolean; var i: Integer; begin for i := Low(Map) to High(Map) do if CompareText(Map[i].Name, Ident) = 0 then begin Int := Map[i].Value; exit(True); end; Result := False; end; function IntToIdent(Int: LongInt; var Ident: String; const Map: array of TIdentMapEntry): Boolean; var i: Integer; begin for i := Low(Map) to High(Map) do if Map[i].Value = Int then begin Ident := Map[i].Name; exit(True); end; Result := False; end; { TPropFixup } type TPropFixup = class FInstance: TPersistent; FInstanceRoot: TComponent; FPropInfo: PPropInfo; FRootName: string; FName: string; constructor Create(AInstance: TPersistent; AInstanceRoot: TComponent; APropInfo: PPropInfo; const ARootName, AName: String); function MakeGlobalReference: Boolean; end; var GlobalFixupList: TThreadList; constructor TPropFixup.Create(AInstance: TPersistent; AInstanceRoot: TComponent; APropInfo: PPropInfo; const ARootName, AName: String); begin FInstance := AInstance; FInstanceRoot := AInstanceRoot; FPropInfo := APropInfo; FRootName := ARootName; FName := AName; end; function TPropFixup.MakeGlobalReference: Boolean; var i: Integer; s, p: PChar; begin i := Pos('.', FName); if i = 0 then exit(False); FRootName := Copy(FName, 1, i - 1); FName := Copy(FName, i + 1, Length(FName)); Result := True; end; Type TInitHandler = Class(TObject) AHandler : TInitComponentHandler; AClass : TComponentClass; end; Var InitHandlerList : TList; procedure RegisterInitComponentHandler(ComponentClass: TComponentClass; Handler: TInitComponentHandler); Var I : Integer; H: TInitHandler; begin If (InitHandlerList=Nil) then InitHandlerList:=TList.Create; H:=TInitHandler.Create; H.Aclass:=ComponentClass; H.AHandler:=Handler; With InitHandlerList do begin I:=0; While (I 0 then begin ToDoList := nil; DoneList := TList.Create; ToDoList := TList.Create; try i := 0; while i < GlobalList.Count do with TPropFixup(GlobalList[i]) do begin Root := FindGlobalComponent(FRootName); if Assigned(Root) or (GetOrdProp(FInstance, FPropInfo) <> 0) then begin if Assigned(Root) then begin Reference := FindNestedComponent(Root, FName); SetOrdProp(FInstance, FPropInfo, Longint(Reference)); end; // Move component to list of done components, if necessary if (DoneList.IndexOf(FInstance) < 0) and (ToDoList.IndexOf(FInstance) >= 0) then DoneList.Add(FInstance); GlobalList.Delete(i); Free; // ...the fixup end else begin // Move component to list of components to process, if necessary Index := DoneList.IndexOf(FInstance); if Index <> -1 then DoneList.Delete(Index); if ToDoList.IndexOf(FInstance) < 0 then ToDoList.Add(FInstance); Inc(i); end; end; for i := 0 to DoneList.Count - 1 do begin Instance := TPersistent(DoneList[I]); if Instance.InheritsFrom(TComponent) then Exclude(TComponent(Instance).FComponentState, csFixups); end; finally ToDoList.Free; DoneList.Free; end; end; finally GlobalFixupList.UnlockList; end; {finally GlobalNameSpace.EndWrite; end;} end; function IsStringInList(const AString: String; AList: TStrings): Boolean; var i: Integer; begin for i := 0 to AList.Count - 1 do if CompareText(AList[i], AString) = 0 then exit(True); Result := False; end; procedure GetFixupReferenceNames(Root: TComponent; Names: TStrings); var i: Integer; CurFixup: TPropFixup; begin with GlobalFixupList.LockList do try for i := 0 to Count - 1 do begin CurFixup := TPropFixup(Items[i]); if ((not Assigned(Root)) or (CurFixup.FInstanceRoot = Root)) and not IsStringInList(CurFixup.FRootName, Names) then Names.Add(CurFixup.FRootName); end; finally GlobalFixupList.UnlockList; end; end; procedure GetFixupInstanceNames(Root: TComponent; const ReferenceRootName: string; Names: TStrings); var i: Integer; CurFixup: TPropFixup; begin with GlobalFixupList.LockList do try for i := 0 to Count - 1 do begin CurFixup := TPropFixup(Items[i]); if (CurFixup.FInstanceRoot = Root) and (UpperCase(ReferenceRootName) = UpperCase(CurFixup.FRootName)) and not IsStringInList(CurFixup.FName, Names) then Names.Add(CurFixup.FName); end; finally GlobalFixupList.UnlockList; end; end; procedure RedirectFixupReferences(Root: TComponent; const OldRootName, NewRootName: string); var i: Integer; CurFixup: TPropFixup; begin with GlobalFixupList.LockList do try for i := 0 to Count - 1 do begin CurFixup := TPropFixup(Items[i]); if ((not Assigned(Root)) or (CurFixup.FInstanceRoot = Root)) and (UpperCase(OldRootName) = UpperCase(CurFixup.FRootName)) then CurFixup.FRootName := NewRootName; end; GlobalFixupReferences; finally GlobalFixupList.Unlocklist; end; end; procedure RemoveFixupReferences(Root: TComponent; const RootName: string); var i: Integer; CurFixup: TPropFixup; begin if not Assigned(GlobalFixupList) then exit; with GlobalFixupList.LockList do try for i := Count - 1 downto 0 do begin CurFixup := TPropFixup(Items[i]); if ((not Assigned(Root)) or (CurFixup.FInstanceRoot = Root)) and ((Length(RootName) = 0) or (UpperCase(RootName) = UpperCase(CurFixup.FRootName))) then begin Delete(i); CurFixup.Free; end; end; finally GlobalFixupList.UnlockList; end; end; procedure RemoveFixups(Instance: TPersistent); var i: Integer; CurFixup: TPropFixup; begin if not Assigned(GlobalFixupList) then exit; with GlobalFixupList.LockList do try for i := Count - 1 downto 0 do begin CurFixup := TPropFixup(Items[i]); if (CurFixup.FInstance = Instance) then begin Delete(i); CurFixup.Free; end; end; finally GlobalFixupList.UnlockList; end; end; function FindNestedComponent(Root: TComponent; const NamePath: string): TComponent; var Current, Found: TComponent; s, p: PChar; Name: String; begin Result := nil; if Length(NamePath) > 0 then begin Current := Root; p := PChar(NamePath); while p[0] <> #0 do begin s := p; while not (p^ in ['.', '-', #0]) do Inc(p); SetString(Name, s, p - s); Found := Current.FindComponent(Name); if (not Assigned(Found)) and (UpperCase(Name) = 'OWNER') then Found := Current; if not Assigned(Found) then exit; // Remove the dereference operator from the name if p[0] = '.' then Inc(P); if p[0] = '-' then Inc(P); if p[0] = '>' then Inc(P); Current := Found; end; end; Result := Current; end; {!!!: Should be threadvar - doesn't work for all platforms yet!} var GlobalLoaded, GlobalLists: TList; procedure BeginGlobalLoading; begin if not Assigned(GlobalLists) then GlobalLists := TList.Create; GlobalLists.Add(GlobalLoaded); GlobalLoaded := TList.Create; end; { Notify all global components that they have been loaded completely } procedure NotifyGlobalLoading; var i: Integer; begin for i := 0 to GlobalLoaded.Count - 1 do TComponent(GlobalLoaded[i]).Loaded; end; procedure EndGlobalLoading; begin { Free the memory occupied by BeginGlobalLoading } GlobalLoaded.Free; GlobalLoaded := TList(GlobalLists.Last); GlobalLists.Delete(GlobalLists.Count - 1); if GlobalLists.Count = 0 then begin GlobalLists.Free; GlobalLists := nil; end; end; function CollectionsEqual(C1, C2: TCollection): Boolean; begin // !!!: Implement this CollectionsEqual:=false; end; { Object conversion routines } procedure ObjectBinaryToText(Input, Output: TStream); procedure OutStr(s: String); begin if Length(s) > 0 then Output.Write(s[1], Length(s)); end; procedure OutLn(s: String); begin OutStr(s + #10); end; procedure OutString(s: String); var res, NewStr: String; i: Integer; InString, NewInString: Boolean; begin res := ''; InString := False; for i := 1 to Length(s) do begin NewInString := InString; case s[i] of #0..#31: begin if InString then NewInString := False; NewStr := '#' + IntToStr(Ord(s[i])); end; '''': if InString then NewStr := '''''' else NewStr := ''''''''; else begin if not InString then NewInString := True; NewStr := s[i]; end; end; if NewInString <> InString then begin NewStr := '''' + NewStr; InString := NewInString; end; res := res + NewStr; end; if InString then res := res + ''''; OutStr(res); end; function ReadInt(ValueType: TValueType): LongInt; begin case ValueType of vaInt8: Result := ShortInt(Input.ReadByte); vaInt16: Result := SmallInt(Input.ReadWord); vaInt32: Result := LongInt(Input.ReadDWord); end; end; function ReadInt: LongInt; begin Result := ReadInt(TValueType(Input.ReadByte)); end; function ReadSStr: String; var len: Byte; begin len := Input.ReadByte; SetLength(Result, len); Input.Read(Result[1], len); end; procedure ReadPropList(indent: String); procedure ProcessValue(ValueType: TValueType; Indent: String); procedure Stop(s: String); begin WriteLn(s); Halt; end; procedure ProcessBinary; var ToDo, DoNow, i: LongInt; lbuf: array[0..31] of Byte; s: String; begin ToDo := Input.ReadDWord; OutLn('{'); while ToDo > 0 do begin DoNow := ToDo; if DoNow > 32 then DoNow := 32; Dec(ToDo, DoNow); s := Indent + ' '; Input.Read(lbuf, DoNow); for i := 0 to DoNow - 1 do s := s + IntToHex(lbuf[i], 2); OutLn(s); end; OutLn(indent + '}'); end; var s: String; len: LongInt; IsFirst: Boolean; ext: Extended; begin case ValueType of vaList: begin OutStr('('); IsFirst := True; while True do begin ValueType := TValueType(Input.ReadByte); if ValueType = vaNull then break; if IsFirst then begin OutLn(''); IsFirst := False; end; OutStr(Indent + ' '); ProcessValue(ValueType, Indent + ' '); end; OutLn(Indent + ')'); end; vaInt8: OutLn(IntToStr(ShortInt(Input.ReadByte))); vaInt16: OutLn( IntToStr(SmallInt(Input.ReadWord))); vaInt32: OutLn(IntToStr(LongInt(Input.ReadDWord))); vaExtended: begin Input.Read(ext, SizeOf(ext)); OutLn(FloatToStr(ext)); end; vaString: begin OutString(ReadSStr); OutLn(''); end; vaIdent: OutLn(ReadSStr); vaFalse: OutLn('False'); vaTrue: OutLn('True'); vaBinary: ProcessBinary; vaSet: begin OutStr('['); IsFirst := True; while True do begin s := ReadSStr; if Length(s) = 0 then break; if not IsFirst then OutStr(', '); IsFirst := False; OutStr(s); end; OutLn(']'); end; vaLString: Stop('!!LString!!'); vaNil: OutLn('nil'); vaCollection: begin OutStr('<'); while Input.ReadByte <> 0 do begin OutLn(Indent); Input.Seek(-1, soFromCurrent); OutStr(indent + ' item'); ValueType := TValueType(Input.ReadByte); if ValueType <> vaList then OutStr('[' + IntToStr(ReadInt(ValueType)) + ']'); OutLn(''); ReadPropList(indent + ' '); OutStr(indent + ' end'); end; OutLn('>'); end; {vaSingle: begin OutLn('!!Single!!'); exit end; vaCurrency: begin OutLn('!!Currency!!'); exit end; vaDate: begin OutLn('!!Date!!'); exit end; vaWString: begin OutLn('!!WString!!'); exit end;} else Stop(IntToStr(Ord(ValueType))); end; end; begin while Input.ReadByte <> 0 do begin Input.Seek(-1, soFromCurrent); OutStr(indent + ReadSStr + ' = '); ProcessValue(TValueType(Input.ReadByte), Indent); end; end; procedure ReadObject(indent: String); var b: Byte; ObjClassName, ObjName: String; ChildPos: LongInt; begin // Check for FilerFlags b := Input.ReadByte; if (b and $f0) = $f0 then begin if (b and 2) <> 0 then ChildPos := ReadInt; end else begin b := 0; Input.Seek(-1, soFromCurrent); end; ObjClassName := ReadSStr; ObjName := ReadSStr; OutStr(Indent); if (b and 1) <> 0 then OutStr('inherited') else OutStr('object'); OutStr(' '); if ObjName <> '' then OutStr(ObjName + ': '); OutStr(ObjClassName); if (b and 2) <> 0 then OutStr('[' + IntToStr(ChildPos) + ']'); OutLn(''); ReadPropList(indent + ' '); while Input.ReadByte <> 0 do begin Input.Seek(-1, soFromCurrent); ReadObject(indent + ' '); end; OutLn(indent + 'end'); end; type PLongWord = ^LongWord; const signature: PChar = 'TPF0'; begin if Input.ReadDWord <> PLongWord(Pointer(signature))^ then raise EReadError.Create('Illegal stream image' {###SInvalidImage}); ReadObject(''); end; procedure ObjectTextToBinary(Input, Output: TStream); var parser: TParser; procedure WriteString(s: String); begin Output.WriteByte(Length(s)); if Length(s) > 0 then Output.Write(s[1], Length(s)); end; procedure WriteInteger(value: LongInt); begin if (value >= -128) and (value <= 127) then begin Output.WriteByte(Ord(vaInt8)); Output.WriteByte(Byte(value)); end else if (value >= -32768) and (value <= 32767) then begin Output.WriteByte(Ord(vaInt16)); Output.WriteWord(Word(value)); end else begin Output.WriteByte(ord(vaInt32)); Output.WriteDWord(LongWord(value)); end; end; procedure ProcessProperty; forward; procedure ProcessValue; var flt: Extended; s: String; stream: TMemoryStream; begin case parser.Token of toInteger: begin WriteInteger(parser.TokenInt); parser.NextToken; end; toFloat: begin Output.WriteByte(Ord(vaExtended)); flt := Parser.TokenFloat; Output.Write(flt, SizeOf(flt)); parser.NextToken; end; toString: begin s := parser.TokenString; while parser.NextToken = '+' do begin parser.NextToken; // Get next string fragment parser.CheckToken(toString); s := s + parser.TokenString; end; Output.WriteByte(Ord(vaString)); WriteString(s); end; toSymbol: begin if CompareText(parser.TokenString, 'True') = 0 then Output.WriteByte(Ord(vaTrue)) else if CompareText(parser.TokenString, 'False') = 0 then Output.WriteByte(Ord(vaFalse)) else if CompareText(parser.TokenString, 'nil') = 0 then Output.WriteByte(Ord(vaNil)) else begin Output.WriteByte(Ord(vaIdent)); WriteString(parser.TokenComponentIdent); end; Parser.NextToken; end; // Set '[': begin parser.NextToken; Output.WriteByte(Ord(vaSet)); if parser.Token <> ']' then while True do begin parser.CheckToken(toSymbol); WriteString(parser.TokenString); parser.NextToken; if parser.Token = ']' then break; parser.CheckToken(','); parser.NextToken; end; Output.WriteByte(0); parser.NextToken; end; // List '(': begin parser.NextToken; Output.WriteByte(Ord(vaList)); while parser.Token <> ')' do ProcessValue; Output.WriteByte(0); parser.NextToken; end; // Collection '<': begin parser.NextToken; Output.WriteByte(Ord(vaCollection)); while parser.Token <> '>' do begin parser.CheckTokenSymbol('item'); parser.NextToken; // ConvertOrder Output.WriteByte(Ord(vaList)); while not parser.TokenSymbolIs('end') do ProcessProperty; parser.NextToken; // Skip 'end' Output.WriteByte(0); end; Output.WriteByte(0); parser.NextToken; end; // Binary data '{': begin Output.WriteByte(Ord(vaBinary)); stream := TMemoryStream.Create; try parser.HexToBinary(stream); Output.WriteDWord(stream.Size); Output.Write(Stream.Memory^, stream.Size); finally stream.Free; end; parser.NextToken; end; else parser.Error(SInvalidProperty); end; end; procedure ProcessProperty; var name: String; begin // Get name of property parser.CheckToken(toSymbol); name := parser.TokenString; while True do begin parser.NextToken; if parser.Token <> '.' then break; parser.NextToken; parser.CheckToken(toSymbol); name := name + '.' + parser.TokenString; end; WriteString(name); parser.CheckToken('='); parser.NextToken; ProcessValue; end; procedure ProcessObject; var IsInherited: Boolean; ObjectName, ObjectType: String; begin if parser.TokenSymbolIs('OBJECT') then IsInherited := False else begin parser.CheckTokenSymbol('INHERITED'); IsInherited := True; end; parser.NextToken; parser.CheckToken(toSymbol); ObjectName := ''; ObjectType := parser.TokenString; parser.NextToken; if parser.Token = ':' then begin parser.NextToken; parser.CheckToken(toSymbol); ObjectName := ObjectType; ObjectType := parser.TokenString; parser.NextToken; end; WriteString(ObjectType); WriteString(ObjectName); // Convert property list while not (parser.TokenSymbolIs('END') or parser.TokenSymbolIs('OBJECT') or parser.TokenSymbolIs('INHERITED')) do ProcessProperty; Output.WriteByte(0); // Terminate property list // Convert child objects while not parser.TokenSymbolIs('END') do ProcessObject; parser.NextToken; // Skip end token Output.WriteByte(0); // Terminate property list end; const signature: PChar = 'TPF0'; begin parser := TParser.Create(Input); try Output.Write(signature[0], 4); ProcessObject; finally parser.Free; end; end; procedure ObjectResourceToText(Input, Output: TStream); begin Input.ReadResHeader; ObjectBinaryToText(Input, Output); end; procedure ObjectTextToResource(Input, Output: TStream); var StartPos, SizeStartPos, BinSize: LongInt; parser: TParser; name: String; begin // Get form type name StartPos := Input.Position; parser := TParser.Create(Input); try if not parser.TokenSymbolIs('OBJECT') then parser.CheckTokenSymbol('INHERITED'); parser.NextToken; parser.CheckToken(toSymbol); parser.NextToken; parser.CheckToken(':'); parser.NextToken; parser.CheckToken(toSymbol); name := parser.TokenString; finally parser.Free; Input.Position := StartPos; end; // Write resource header name := UpperCase(name); Output.WriteByte($ff); Output.WriteByte(10); Output.WriteByte(0); Output.Write(name[1], Length(name) + 1); // Write null-terminated form type name Output.WriteWord($1030); SizeStartPos := Output.Position; Output.WriteDWord(0); // Placeholder for data size ObjectTextToBinary(Input, Output); // Convert the stuff! BinSize := Output.Position - SizeStartPos - 4; Output.Position := SizeStartPos; Output.WriteDWord(BinSize); // Insert real resource data size end; { Utility routines } function LineStart(Buffer, BufPos: PChar): PChar; begin Result := BufPos; while Result > Buffer do begin Dec(Result); if Result[0] = #10 then break; end; end; procedure CommonInit; begin InitHandlerList:=Nil; IntConstList := TThreadList.Create; GlobalFixupList := TThreadList.Create; ClassList := TThreadList.Create; ClassAliasList := TStringList.Create; end; procedure CommonCleanup; var i: Integer; begin // !!!: GlobalNameSpace.BeginWrite; with IntConstList.LockList do try for i := 0 to Count - 1 do TIntConst(Items[I]).Free; finally IntConstList.UnlockList; end; IntConstList.Free; ClassList.Free; ClassAliasList.Free; RemoveFixupReferences(nil, ''); GlobalFixupList.Free; GlobalFixupList := nil; GlobalLists.Free; ComponentPages.Free; {!!!: GlobalNameSpace.Free; GlobalNameSpace := nil;} InitHandlerList.Free; InitHandlerList:=Nil; end; { TFiler implementation } {$i filer.inc} { TReader implementation } {$i reader.inc} { TWriter implementations } {$i writer.inc} {$i twriter.inc} { $Log$ Revision 1.1 2003-10-06 20:33:58 peter * classes moved to rtl for 1.1 * classes .inc and classes.pp files moved to fcl/classes for backwards 1.0.x compatiblity to have it in the fcl Revision 1.14 2003/06/04 17:40:44 michael + Minor fix by Mattias Gaertner Revision 1.13 2003/06/04 15:27:24 michael + TDataModule en InitInheritedComponent erbij voor Delphi 6 compatibility Revision 1.12 2003/04/19 14:29:25 michael + Fix from Mattias Gaertner, closes memory leak Revision 1.11 2002/12/02 12:04:07 sg * Fixed handling of zero-length strings (classes.inc: When converting empty strings from text forms to binary forms; reader.inc: When reading an empty string from a binary serialization) Revision 1.10 2002/09/07 15:15:24 peter * old logs removed and tabs fixed Revision 1.9 2002/07/16 13:32:51 florian + skeleton for TInterfaceList added Revision 1.8 2002/01/06 21:54:49 peter * action classes added }