asjgfsdkjsfld { Resource Unit Programmer: Brad Williams BitSoft Development, L.L.C. Copyright (c) 1996 Version 1.1 Revision History 1.1 (12/26/97) - updated to add cdResource directive so that can use standard TStringList resources created by TVRW and TVDT 1.0 - original implementation } unit Resource; interface { The Resource unit provides global variables which are used to build and access resource files. InitRez must always be called before accessing any variables in the Resource unit. The programmer should also always call Done to free all file handles allocated to the program. } {$i platform.inc} {$ifdef PPC_FPC} {$H-} {$else} {$F+,O+,E+,N+} {$endif} {$X+,R-,I-,Q-,V-} {$ifndef OS_UNIX} {$S-} {$endif} uses FVConsts, Objects, Dos; const RezExt: ExtStr = '.RES'; { The file extension used on all resource files. } RezBufferSize: Word = 4096; { RezBufferSize is the number of bytes to use for the resource file's stream's buffer. RezBufferSize is passed to TBufStream.Init. } { reXXXX constants are used with resource files to retrieve the standard Free Vision dialogs. The constant is followed by the Unit in which it is used and the resource which is stored separated by a period. } reChDirDialog = 'ChDirDialog'; { StdDlg.TChDirDialog } reEditChDirDialog = 'EditChDirDialog'; { StdDlg.TEditChDirDialog } reFindTextDlg = 'FindTextDlg'; { Editors.CreateFindDialog } reHints = 'Hints'; { Resource.Hints } reJumpLineDlg = 'JumpLineDlg'; { Editors.MakeJumpLineDlg } reLabels = 'Labels'; { Resource.Labels } reMenuBar = 'MenuBar'; { App.MenuBar } reOpenDlg = 'OpenDlg'; { StdDlg.TFileDialog - Open } reReformDocDlg = 'ReformDocDlg'; { Editors.MakeReformDocDlg } reReplaceDlg = 'ReplaceDlg'; { Editors.CreateReplaceDialog } reRightMarginDlg = 'RightMarginDlg'; { Editors.MakeRightMarginDlg } reStatusLine = 'StatusLine'; { App.StatusLine } reStrings = 'Strings'; { Resource.Strings } reSaveAsDlg = 'SaveAsDlg'; { StdDlg.TFileDialog - Save As } reTabStopDlg = 'TabStopDlg'; { Editors.MakeTabStopDlg } reWindowListDlg = 'WindowListDlg'; { Editors.MakeWindowListDlg } reAboutDlg = 'About'; { App unit about dialog } {$I str.inc} { STR.INC declares all the string list constants used in the standard Free Vision library units. They are placed in a separate file as a template for use by the resource file generator, MakeRez. Applications which use resource files and need to add strings of their own should use STR.INC as the start for the resource file. See MakeRez.PAS for more information about generating resource files.} type PConstant = ^TConstant; TConstant = object(TObject) Value: Word; { The value assigned to the constant. } constructor Init (AValue: Word; AText: string); { Init assigns AValue to Value to AText to Text. AText may be an empty string. If an error occurs Init fails. } destructor Done; virtual; { Done disposes of Text then calls the inherited destructor. } procedure SetText (AText: string); { SetText changes FText to the word equivalent of AText. } procedure SetValue (AValue: string); { SetValue changes Value to the word equivalent of AValue. } function Text: string; { Text returns a string equivalent to FText. If FText is nil, an empty string is returned. } function ValueAsString: string; { ValueAsString returns the string equivalent of Value. } private FText: PString; { The text to display for the constant. } end; { of TConstant } PMemStringList = ^TMemStringList; TMemStringList = object(TSortedCollection) { A TMemStringList combines the functions of a TStrListMaker and a TStringList into one object, allowing generation and use of string lists in the same application. TMemStringList is fully compatible with string lists created using TStrListMaker, so legacy applications will work without problems. When using a string list in the same program as it is created, a resource file is not required. This allows language independant coding of units without the need for conditional defines and recompiling. } constructor Init; { Creates an empty, in-memory string list that is not associated with a resource file. } constructor Load (var S: TStream); { Load creates a TStringList from which it gets its strings upon a call to Get. The strings on the resource file may be loaded into memory for editing by calling LoadList. If initialized with Load, the stream must remain valid for the life of this object. } destructor Done; virtual; { Done deallocates the memory allocated to the string list. } function Compare (Key1, Key2: Pointer): Sw_Integer; virtual; { Compare assumes Key1 and Key2 are Word values and returns: -1 if Key1 < Key2 0 if Key1 = Key2 1 if Key1 > Key2 } function Get (Key: Word): String; virtual; { GetKey searches for a string with a key matching Key and returns it. An empty string is returned if a string with a matching Key is not found. If Count > 0, the in memory collection is searched. If List^.Count is 0, the inherited Get method is called. } procedure Insert (Item: Pointer); virtual; { If Item is not nil, Insert attempts to insert the item into the collection. If a collection expansion error occurs Insert disposes of Item by calling FreeItem. Item must be a pointer to a TConstant or its descendant. } function KeyOf (Item: Pointer): Pointer; virtual; { KeyOf returns a pointer to TConstant.Value. } function LoadStrings: Sw_Integer; { LoadStrings reads all strings the associated resource file into memory, places them in the collection, and returns 0. If an error occurs LoadStrings returns the stream status error code or a DOS error code. Possible DOS error codes include: 2: no associated resource file 8: out of memory } function NewConstant (Value: Word; S: string): PConstant; virtual; { NewConstant is called by LoadStrings. } procedure Put (Key: Word; S: String); virtual; { Put creates a new PConstant containing Key and Word then calls Insert to place it in the collection. } procedure Store (var S: TStream); { Store creates a TStrListMaker, fills it with the items in List, writes the TStrListMaker to the stream by calling TStrListMaker.Store, then disposes of the TStrListMaker. } private StringList: PStringList; end; { of TMemStringList) } var {$ifdef cdResource} Hints: PStringList; {$else} Hints: PMemStringList; {$endif cdResource} { Hints is a string list for use within the application to provide context sensitive help on the command line. Hints is always used in the application. } {$ifdef cdResource} Strings: PStringList; {$else} Strings: PMemStringList; {$endif cdResource} { Strings holds messages such as errors and general information that are displayed at run-time, normally with MessageBox. Strings is always used in the application. } {$ifdef cdResource} Labels: PStringList; {$else} Labels: PMemStringList; {$endif cdResource} { Labels is a string list for use within the application when a resource file is not used, or when creating a resource file. Labels contains all text used in dialog titles, labels, buttons, menus, statuslines, etc., used in the application which can be burned into language specific resources. It does not contain any messages displayed at run-time using MessageBox or the status line hints. Using the Labels variable when creating views allows language independant coding of views such as the MessageBox, StdDlg and Editors units. } RezFile: PResourceFile; { RezFile is a global variable used when the Free Vision library is compiled using the cdResource conditional define, or when creating resource files. All standard Free Vision application resources are accessed from the resource file using the reXXXX constants. Modify the STR.INC under a new file name to create new language specific resource files. See the MakeRez program file for more information. } procedure DoneResource; { Done destructs all objects initialized in this unit and frees all allocated heap. } {$ifndef cdResource} function InitResource: Boolean; {$endif cdResource} { Init initializes the Hints and Strings for use with in memory strings lists. Init should be used in applications which do not use a resource file, or when creating resource files. } {$ifdef cdResource} function InitRezFile (AFile: FNameStr; Mode: Word; var AResFile: PResourceFile): Sw_Integer; {$endif cdResource} { InitRezFile initializes a new PResourceFile using the name passed in AFile and the stream mode passed in Mode and returns 0. If an error occurs InitRezFile returns the DOS error and AResFile is invalid. Possible DOS error values include: 2: file not found or other stream initialization error 11: invalid format - not a valid resource file } {$ifdef cdResource} function LoadResource (AFile: FNameStr): Boolean; {$endif cdResource} { Load is used to open a resource file for use in the application. For Load to return True, the resource file must be properly opened and assigned to RezFile and the Hints string list must be successfully loaded from the stream. If an error occurs, Load displays an English error message using PrintStr and returns False. } function MergeLists (Source, Dest: PMemStringList): Sw_Integer; { MergeLists moves all key/string pairs from Source to destination, deleting them from Source. Duplicate strings are ignored. } const RMemStringList: TStreamRec = ( ObjType: idMemStringList; VmtLink: Ofs(TypeOf(TMemStringList)^); Load: @TMemStringList.Load; Store: @TMemStringList.Store); implementation {****************************************************************************} { Private Declarations } {****************************************************************************} uses {Memory, }Drivers; {****************************************************************************} { TConstant object } {****************************************************************************} {****************************************************************************} { TConstant.Init } {****************************************************************************} constructor TConstant.Init (AValue: Word; AText: string); begin if not inherited Init then Fail; Value := AValue; FText := NewStr(AText); if (FText = nil) and (AText <> '') then begin inherited Done; Fail; end; end; {****************************************************************************} { TConstant.Done } {****************************************************************************} destructor TConstant.Done; begin DisposeStr(FText); inherited Done; end; {****************************************************************************} { TConstant.SetText } {****************************************************************************} procedure TConstant.SetText (AText: string); begin DisposeStr(FText); FText := NewStr(AText); end; {****************************************************************************} { TConstant.SetValue } {****************************************************************************} procedure TConstant.SetValue (AValue: string); var N: Word; ErrorCode: Integer; begin Val(AValue,N,ErrorCode); if ErrorCode = 0 then Value := N; end; {****************************************************************************} { TConstant.Text } {****************************************************************************} function TConstant.Text: string; begin if (FText = nil) then Text := '' else Text := FText^; end; {****************************************************************************} { TConstant.ValueAsString } {****************************************************************************} function TConstant.ValueAsString: string; var S: string[5]; begin Str(Value,S); ValueAsString := S; end; {****************************************************************************} { TMemStringList Object } {****************************************************************************} {****************************************************************************} { TMemStringList.Init } {****************************************************************************} constructor TMemStringList.Init; begin if not inherited Init(10,10) then Fail; StringList := nil; end; {****************************************************************************} { TMemStringList.Load } {****************************************************************************} constructor TMemStringList.Load (var S: TStream); begin if not inherited Init(10,10) then Fail; StringList := New(PStringList,Load(S)); end; {****************************************************************************} { TMemStringList.Done } {****************************************************************************} destructor TMemStringList.Done; begin if (StringList <> nil) then Dispose(StringList,Done); inherited Done; end; {****************************************************************************} { TMemStringList.Compare } {****************************************************************************} function TMemStringList.Compare (Key1, Key2: Pointer): Sw_Integer; begin if Word(Key1^) < Word(Key2^) then Compare := -1 else Compare := Byte(Word(Key1^) > Word(Key2^)); end; {****************************************************************************} { TMemStringList.Get } {****************************************************************************} function TMemStringList.Get (Key: Word): string; var i: Sw_Integer; S: string; begin if (StringList = nil) then begin { started with Init, use in memory string list } if Search(@Key,i) then Get := PConstant(At(i))^.Text else Get := ''; end else begin S := StringList^.Get(Key); Get := S; end; end; {****************************************************************************} { TMemStringList.Insert } {****************************************************************************} procedure TMemStringList.Insert (Item: Pointer); var i: Sw_Integer; begin if (Item <> nil) then begin i := Count; inherited Insert(Item); if (i = Count) then { collection expansion failed } Dispose(PConstant(Item),Done); end; end; {****************************************************************************} { TMemStringList.KeyOf } {****************************************************************************} function TMemStringList.KeyOf (Item: Pointer): Pointer; begin KeyOf := @(PConstant(Item)^.Value); end; {****************************************************************************} { TMemStringList.LoadStrings } {****************************************************************************} function TMemStringList.LoadStrings: Sw_Integer; procedure MakeEditableString (var Str: string); const SpecialChars: array[1..3] of Char = #3#10#13; var i, j: Byte; begin for i := 1 to 3 do while (Pos(SpecialChars[i],Str) <> 0) do begin j := Pos(SpecialChars[i],Str); System.Delete(Str,j,1); case i of 1: System.Insert('#3',Str,j); 2: System.Insert('#10',Str,j); 3: System.Insert('#13',Str,j); end; end; end; var Constant: PConstant; i: Word; S: string; begin LoadStrings := 0; if (StringList = nil) then begin LoadStrings := 2; Exit; end; for i := 0 to 65535 do begin S := StringList^.Get(i); if (S <> '') then begin MakeEditableString(S); Constant := NewConstant(i,S); (* if LowMemory then begin if (Constant <> nil) then Dispose(Constant,Done); LoadStrings := 8; { out of memory } Exit; end; *) Insert(Constant); end; end; end; {****************************************************************************} { TMemStringList.NewConstant } {****************************************************************************} function TMemStringList.NewConstant (Value: Word; S: string): PConstant; begin NewConstant := New(PConstant,Init(Value,S)); end; {****************************************************************************} { TMemStringList.Put } {****************************************************************************} procedure TMemStringList.Put (Key: Word; S: string); begin Insert(New(PConstant,Init(Key,S))); end; {****************************************************************************} { TMemStringList.Store } {****************************************************************************} procedure TMemStringList.Store (var S: TStream); var StrList: PStrListMaker; Size: Word; procedure Total (Constant: PConstant);{$ifndef FPC}far;{$endif} begin with Constant^ do Inc(Size,Succ(Length(Text))); end; procedure AddString (Constant: PConstant);{$ifndef FPC}far;{$endif} const Numbers = ['0'..'9']; var i, j: Byte; N: Byte; ErrorCode: Integer; S: string; begin with Constant^ do begin { convert formatting characters } S := Text; while (Pos('#',S) <> 0) do begin i := Succ(Pos('#',S)); j := i; if (Length(S) > j) then Inc(j,Byte(S[Succ(j)] in Numbers)); Val(Copy(S,i,j-i+1),N,ErrorCode); System.Delete(S,Pred(i),j-i+2); System.Insert(Char(N),S,Pred(i)); end; StrList^.Put(Value,Text) end; end; begin Size := 0; ForEach(@Total); StrList := New(PStrListMaker,Init(Size,Count * 6)); if (StrList = nil) then begin S.Status := 8; { DOS error not enough memory } Exit; end; ForEach(@AddString); StrList^.Store(S); Dispose(StrList,Done); end; {****************************************************************************} { Public Procedures and Functions } {****************************************************************************} {****************************************************************************} { Done } {****************************************************************************} procedure DoneResource; begin if (RezFile <> nil) then begin Dispose(RezFile,Done); RezFile:=nil; end; if (Strings <> nil) then begin Dispose(Strings,Done); Strings:=nil; end; if (Hints <> nil) then begin Dispose(Hints,Done); Hints:=nil; end; if (Labels <> nil) then begin Dispose(Labels,Done); Labels:=nil; end; end; {****************************************************************************} { Init } {****************************************************************************} {$ifndef cdResource} {$I strtxt.inc} { strtxt.inc contains the real strings and procedures InitRes... which is converted from str.inc } function InitResource: Boolean; begin InitResource := False; Hints := New(PMemStringList,Init); if (Hints = nil) then begin PrintStr('Fatal error. Could not create Hints list.'); Exit; end; Strings := New(PMemStringList,Init); if (Strings = nil) then begin DoneResource; Exit; end; Labels := New(PMemStringList,Init); if (Labels = nil) then begin DoneResource; Exit; end; { now load the defaults } InitResLabels; InitResStrings; InitResource := True; end; {$endif cdResource} {****************************************************************************} { InitRezFile } {****************************************************************************} {$ifdef cdResource} function InitRezFile (AFile: FNameStr; Mode: Word; var AResFile: PResourceFile): Sw_Integer; var Stream: PBufStream; Result: Sw_Integer; begin Stream := New(PBufStream,Init(AFile,Mode,RezBufferSize)); if (Stream = nil) then Result := 2 { file not found; could also be out of memory } else begin AResFile := New(PResourceFile,Init(Stream)); if (AResFile = nil) then begin Dispose(Stream,Done); Result := 11; end else Result := 0; end; InitRezFile := Result; end; {$endif cdResource} {****************************************************************************} { Load } {****************************************************************************} {$ifdef cdResource} function LoadResource (AFile: FNameStr): Boolean; var Stream: PBufStream; begin Load := False; Stream := New(PBufStream,Init(AFile,stOpenRead,RezBufferSize)); if (Stream = nil) or (Stream^.Status <> 0) then begin Done; PrintStr('Fatal error. Could not open resource file: ' + AFile); Exit; end; RezFile := New(PResourceFile,Init(Stream)); if (RezFile = nil) then begin Dispose(Stream,Done); Done; PrintStr('Fatal error. Could not initialize resource file.'); Exit; end; Hints := PStringList(RezFile^.Get(reHints)); if (Hints = nil) then begin Done; PrintStr('Fatal error. Could not load Hints string list.'); Exit; end; Strings := PStringList(RezFile^.Get(reStrings)); if (Strings = nil) then begin Done; PrintStr('Fatal error. Could not load Strings string list.'); Exit; end; Load := True; end; {$endif cdResource} {****************************************************************************} { MergeLists } {****************************************************************************} function MergeLists (Source, Dest: PMemStringList): Sw_Integer; var Result: Sw_Integer; procedure MoveItem (Constant: PConstant);{$ifndef FPC}far;{$endif} var j: Sw_Integer; begin if (Result = 0) and (not Dest^.Search(Dest^.KeyOf(Constant),j)) then begin j := Dest^.Count; Dest^.Insert(Constant); if (j = Dest^.Count) then Result := 8 else Source^.Delete(Constant); end; end; begin if (Source = nil) or (Dest = nil) then begin MergeLists := 6; Exit; end; Result := 0; Source^.ForEach(@MoveItem); MergeLists := Result; end; {****************************************************************************} { Unit Initialization } {****************************************************************************} begin RezFile := nil; Hints := nil; Strings := nil; Labels := nil; end.