diff --git a/packages/chm/src/chmfilewriter.pas b/packages/chm/src/chmfilewriter.pas index c884ae7931..27e4a951ab 100644 --- a/packages/chm/src/chmfilewriter.pas +++ b/packages/chm/src/chmfilewriter.pas @@ -25,7 +25,7 @@ unit chmfilewriter; interface uses - Classes, SysUtils, chmwriter; + Classes, SysUtils, chmwriter, inifiles, contnrs; type TChmProject = class; @@ -49,13 +49,18 @@ type FOutputFileName: String; FTableOfContentsFileName: String; FTitle: String; + FWindows : TObjectList; + FMergeFiles : TStringlist; + fDefaultWindow : string; protected function GetData(const DataName: String; out PathInChm: String; out FileName: String; var Stream: TStream): Boolean; procedure LastFileAdded(Sender: TObject); + procedure readIniOptions(keyvaluepairs:tstringlist); public constructor Create; virtual; destructor Destroy; override; procedure LoadFromFile(AFileName: String); virtual; + procedure LoadFromhhp (AFileName:String;LeaveInclude:Boolean); virtual; procedure SaveToFile(AFileName: String); virtual; procedure WriteChm(AOutStream: TStream); virtual; function ProjectDir: String; @@ -74,8 +79,10 @@ type property MakeSearchable: Boolean read FMakeSearchable write FMakeSearchable; property DefaultPage: String read FDefaultPage write FDefaultPage; property DefaultFont: String read FDefaultFont write FDefaultFont; - + property Windows :TObjectList read FWindows write FWindows; + property MergeFiles :TStringlist read FMergeFiles write FMergefiles; property OnProgress: TChmProgressCB read FOnProgress write FOnProgress; + property DefaultWindow : String read FDefaultWindow write FDefaultWindow; end; TChmContextNode = Class @@ -86,7 +93,7 @@ type implementation -uses XmlCfg, chmsitemap; +uses XmlCfg, chmsitemap, CHMTypes; { TChmProject } @@ -153,6 +160,8 @@ end; constructor TChmProject.Create; begin FFiles := TStringList.Create; + FWindows:=TObjectList.Create(True); + FMergeFiles:=TStringlist.Create; end; destructor TChmProject.Destroy; @@ -160,16 +169,111 @@ var i : integer; begin for i:=0 to ffiles.count -1 do ffiles.objects[i].free; + FMergeFiles.Free; FFiles.Free; + FWindows.Free; inherited Destroy; end; + +Type + TSectionEnum = (secOptions,secWindows,secFiles,secMergeFiles,secAlias,secMap,secInfoTypes,secTextPopups,secUnknown); + TOptionEnum = (OPTAUTO_INDEX,OPTAUTO_TOC,OPTBINARY_INDEX,OPTBINARY_TOC,OPTCITATION, + OPTCOMPRESS,OPTCOPYRIGHT,OPTCOMPATIBILITY,OPTCOMPILED_FILE,OPTCONTENTS_FILE, + OPTCREATE_CHI_FILE,OPTDBCS,OPTDEFAULT_FONT,OPTDEFAULT_WINDOW,OPTDEFAULT_TOPIC, + OPTDISPLAY_COMPILE_NOTES,OPTDISPLAY_COMPILE_PROGRESS,OPTENHANCED_DECOMPILATION,OPTERROR_LOG_FILE,OPTFLAT, + OPTFULL_TEXT_SEARCH_STOP_LIST,OPTFULL_TEXT_SEARCH,OPTIGNORE,OPTINDEX_FILE,OPTLANGUAGE,OPTPREFIX, + OPTSAMPLE_STAGING_PATH,OPTSAMPLE_LIST_FILE,OPTTMPDIR,OPTTITLE,OPTCUSTOM_TAB,OPTUNKNOWN); + +Const + SectionNames : Array[TSectionEnum] of String = + ('OPTIONS','WINDOWS','FILES','MERGE FILES','ALIAS','MAP','INFOTYPES','TEXT POPUPS','UNKNOWN'); + + OptionKeys : array [TOptionEnum] of String = + ('AUTO INDEX','AUTO TOC','BINARY INDEX','BINARY TOC','CITATION', + 'COMPRESS','COPYRIGHT','COMPATIBILITY','COMPILED FILE','CONTENTS FILE', + 'CREATE CHI FILE','DBCS','DEFAULT FONT','DEFAULT WINDOW','DEFAULT TOPIC', + 'DISPLAY COMPILE NOTES','DISPLAY COMPILE PROGRESS','ENHANCED DECOMPILATION','ERROR LOG FILE','FLAT', + 'FULL-TEXT SEARCH STOP LIST','FULL TEXT SEARCH','IGNORE','INDEX FILE','LANGUAGE','PREFIX', + 'SAMPLE STAGING PATH','SAMPLE LIST FILE','TMPDIR','TITLE','CUSTOM TAB','UNKNOWN'); + + + +function FindSectionName (const name:string):TSectionEnum; + +begin + result:=low(TSectionEnum); + while (resultSectionNames[Result]) do + inc(result); +end; + +function FindOptionName(Const name:string):TOptionEnum; + +begin + result:=low(TOptionEnum); + while (resultOptionKeys[Result]) do + inc(result); +end; + +procedure TChmProject.readIniOptions(keyvaluepairs:tstringlist); +var i : integer; + Opt : TOptionEnum; + OptVal, + OptValUpper : string; +begin + for i:=0 to keyvaluepairs.count-1 do + begin + Opt:=findoptionname(uppercase(keyvaluepairs.names[i])); + optval :=keyvaluepairs.valuefromindex[i]; + optvalupper:=uppercase(OptVal); + case Opt Of + OPTAUTO_INDEX : ; + OPTAUTO_TOC : ; + OPTBINARY_INDEX : MakeBinaryIndex:=optvalupper='YES'; + OPTBINARY_TOC : MakeBinaryToc :=optvalupper='YES'; + OPTCITATION : ; + OPTCOMPRESS : ; // Doesn't seem to have effect in workshop + OPTCOPYRIGHT : ; + OPTCOMPATIBILITY : ; + OPTCOMPILED_FILE : OutputFilename:=optval; + OPTCONTENTS_FILE : TableOfContentsFileName:=optval; + OPTCREATE_CHI_FILE : ; + OPTDBCS : ; // What this field makes unicode is not known? + OPTDEFAULT_FONT : defaultfont:=optval; + OPTDEFAULT_WINDOW : defaultwindow:=optval; + OPTDEFAULT_TOPIC : defaultpage:=optval; + OPTDISPLAY_COMPILE_NOTES : ; + OPTDISPLAY_COMPILE_PROGRESS : ; + OPTENHANCED_DECOMPILATION : ; + OPTERROR_LOG_FILE : ; + OPTFLAT : ; + OPTFULL_TEXT_SEARCH_STOP_LIST: ; + OPTIGNORE : ; + OPTINDEX_FILE : Indexfilename:=optval; + OPTLANGUAGE : ; + OPTPREFIX : ; // doesn't seem to have effect + OPTSAMPLE_STAGING_PATH : ; + OPTSAMPLE_LIST_FILE : ; + OPTTMPDIR : ; + OPTTITLE : Title:=optval; + OPTCUSTOM_TAB : ; + OPTUNKNOWN : ; // can be used for errors on unknown keys + end; + end; +end; + + procedure TChmProject.LoadFromFile(AFileName: String); var Cfg: TXMLConfig; + MergeFileCount, + WinCount, FileCount: Integer; I : Integer; nd : TChmContextNode; + win: TCHMWindow; + s : String; + begin Cfg := TXMLConfig.Create(nil); Cfg.Filename := AFileName; @@ -185,20 +289,257 @@ begin nd.contextname:=Cfg.GetValue('Files/FileName'+IntToStr(I)+'/ContextName',''); Files.AddObject(nd.urlname,nd); end; + + WinCount:= Cfg.GetValue('Windows/Count/Value', 0); + for i:=0 To WinCount-1 do + begin + win:=TCHMWindow.Create; + win.loadfromxml(cfg,'Windows/item'+inttostr(i)+'/'); + fwindows.add(win); + end; + + Mergefilecount:=Cfg.getValue('MergeFiles/Count/Value', 0); + for i:=0 To MergeFileCount-1 do + Mergefiles.add(Cfg.getValue('MergeFiles/FileName'+IntToStr(I)+'/value','')); + + // load some values that changed key backwards compatible. + IndexFileName := Cfg.GetValue('Files/IndexFile/Value',''); + if IndexFileName='' Then + IndexFileName := Cfg.GetValue('Settings/IndexFile/Value',''); + TableOfContentsFileName := Cfg.GetValue('Files/TOCFile/Value',''); + If TableOfContentsFileName='' then + TableOfContentsFileName := Cfg.GetValue('Settings/TOCFile/Value',''); + // For chm file merging, bintoc must be false and binindex true. Change defaults in time? - MakeBinaryTOC := Cfg.GetValue('Files/MakeBinaryTOC/Value', True); - MakeBinaryIndex:= Cfg.GetValue('Files/MakeBinaryIndex/Value', False); + // OTOH, merging will be mostly done for fpdoc files, and that doesn't care about defaults. + + S:=Cfg.GetValue('Files/MakeBinaryTOC/Value', ''); + if s='' Then + MakeBinaryTOC := Cfg.GetValue('Settings/MakeBinaryTOC/Value', True) + else + MakeBinaryTOC := Cfg.GetValue('Files/MakeBinaryTOC/Value', True); + + S:=Cfg.GetValue('Files/MakeBinaryIndex/Value', ''); + if s='' Then + MakeBinaryIndex := Cfg.GetValue('Settings/MakeBinaryIndex/Value', False) + else + MakeBinaryIndex := Cfg.GetValue('Files/MakeBinaryIndex/Value', False); + AutoFollowLinks := Cfg.GetValue('Settings/AutoFollowLinks/Value', False); MakeSearchable := Cfg.GetValue('Settings/MakeSearchable/Value', False); DefaultPage := Cfg.GetValue('Settings/DefaultPage/Value', ''); Title := Cfg.GetValue('Settings/Title/Value', ''); OutputFileName := Cfg.GetValue('Settings/OutputFileName/Value', ''); - DefaultFont := Cfg.GetValue('Settings/DefaultFont/Value', ''); + DefaultFont := Cfg.GetValue('Settings/DefaultFont/Value', ''); + DefaultWindow:= Cfg.GetValue('Settings/DefaultWindow/Value', ''); Cfg.Free; end; +function cleanupstring(const s:string):string; +var + i:integer; +begin + i:=pos(';',s); + if i>0 then + result:=trim(copy(s,1,i-1)) + else + result:=trim(s); +end; + +procedure TChmProject.LoadFromhhp (AFileName:String;LeaveInclude:Boolean); +// leaveinclude=true leaves includefiles includefiles. + +procedure addalias(const key,value :string); + +var i,j : integer; + node: TCHMContextNode; + keyupper : string; +begin + {$ifdef hhp_debug} + writeln('alias entry:',key,'=',value); + {$endif} + keyupper:=uppercase(value); + i:=0; j:=files.count; + while (ikeyupper) do + inc(i); + if i=j then + begin + {$ifdef hhp_debug} + writeln('alias new node:',key); + {$endif} + node:=TCHMContextNode.create; + node.URLName:=value; + node.contextname:=key; + end + else + begin + node:=TCHMContextNode(Files.objects[i]); + node.ContextName:=key; + end; +end; + +procedure processalias(strs:TStringlist); +var i,j : integer; + s : string; + strls2:tstringlist; + +begin + for i:=0 to strs.count-1 do + begin + s:=cleanupstring(strs[i]); + if uppercase(copy(s,1,8))='#INCLUDE' then + begin + delete(s,1,8); + s:=trim(s); + if fileexists(s) then + begin + strls2:=TstringList.create; + strls2.loadfromfile(s); + processalias(strls2); + strls2.free; + end; + + end + else + begin + s:=cleanupstring(s); + j:=pos('=',s); + if j>0 then + addalias(trim(copy(s,1,j-1)),copy(s,j+1,length(s)-j)); + end; + end; +end; + +procedure addmap(const key,value :string); + +var i,j : integer; + node: TCHMContextNode; + keyupper : string; +begin + {$ifdef hhp_debug} + writeln('map entry:',key,'=',value); + {$endif} + keyupper:=uppercase(key); + i:=0; j:=files.count; + while (ikeyupper) do + inc(i); + if i=j then + raise Exception.create('context "'+key+'" not found!') + else + begin + node:=TCHMContextNode(Files.objects[i]); + node.Contextnumber:=strtointdef(value,0); + end; +end; + +procedure processmap(strs:TStringlist); +var i,j : integer; + s : string; + strls2:tstringlist; + +begin + for i:=0 to strs.count-1 do + begin + s:=cleanupstring(strs[i]); + {$ifdef hhp_debug} + writeln('map item:',s); + {$endif} + if uppercase(copy(s,1,8))='#INCLUDE' then + begin + delete(s,1,8); + s:=trim(s); + if fileexists(s) then + begin + strls2:=TstringList.create; + strls2.loadfromfile(s); + processmap(strls2); + strls2.free; + end; + end + else + begin + s:=cleanupstring(s); + if uppercase(copy(s,1,7))='#DEFINE' Then + begin + delete(s,1,7); + s:=trim(s); + j:=pos(' ',s); + if j>0 then + addmap(trim(copy(s,1,j-1)),copy(s,j+1,length(s)-j)); + end + else + begin + {$ifdef hhp_debug} + writeln('map leftover:',s); + {$endif} + end; + end; + end; +end; + +var + Fini : TMemIniFile; // TMemInifile is more compatible with Delphi. Delphi's API based TIniFile fails on .hhp files. + secs,strs : TStringList; + i,j : Integer; + section : TSectionEnum; + nd : TChmContextNode; + +begin + Fini:=TMeminiFile.Create(AFileName); + secs := TStringList.create; + strs := TStringList.create; + fini.readsections(secs); + + // Do the files section first so that we can emit errors if + // other sections reference unknown files. + + fini.readsectionvalues(SectionNames[secFiles] ,strs); + if strs.count>0 then + for j:=0 to strs.count-1 do + begin + nd:=TChmContextNode.Create; + nd.urlname:=strs[j]; + nd.contextnumber:=0; + nd.contextname:=''; + Files.AddObject(nd.urlname,nd); + end; + + // aliases also add file nodes. + + fini.readsectionvalues(SectionNames[secAlias] ,strs); // resolve all aliases. + if strs.count>0 then + processalias(strs); + + // map files only add to existing file nodes. + fini.readsectionvalues(SectionNames[secmap] ,strs); + if strs.count>0 then + processmap(strs); + + + for i:=0 to secs.count-1 do + begin + section:=FindSectionName(Uppercase(Secs[i])); + if section<>secunknown then + fini.readsectionvalues(secs[i] ,strs); + case section of + secOptions : readinioptions(strs); + secWindows : for j:=0 to strs.count-1 do + FWindows.add(TCHMWindow.Create(strs[j])); + secFiles : ; // already done + secMergeFiles: FMergeFiles.Assign(Strs); // just a filelist + secAlias : ; // already done + secMap : ; // already done + secInfoTypes : ; // unused for now. + secTextPopups: ; // rarely used. + end; + end; + secs.free; + strs.free; + fini.free; +end; + procedure TChmProject.AddFileWithContext(contextid:integer;filename:ansistring;contextname:ansistring=''); var x : integer; nd : TChmContextNode; @@ -247,16 +588,35 @@ begin Cfg.SetValue('Files/FileName'+IntToStr(I)+'/ContextName', nd.contextname); end; end; - Cfg.SetValue('Files/IndexFile/Value', IndexFileName); - Cfg.SetValue('Files/TOCFile/Value', TableOfContentsFileName); - Cfg.SetValue('Files/MakeBinaryTOC/Value',MakeBinaryTOC); - Cfg.SetValue('Files/MakeBinaryIndex/Value',MakeBinaryIndex); + + Cfg.SetValue('Windows/Count/Value', FWindows.count); + for i:=0 To FWindows.Count-1 do + TCHMWindow(FWindows[i]).savetoxml(cfg,'Windows/item'+inttostr(i)+'/'); + + Cfg.SetValue('MergeFiles/Count/Value', FMergeFiles.count); + for i:=0 To FMergeFiles.Count-1 do + Cfg.SetValue('MergeFiles/FileName'+IntToStr(I)+'/value',FMergeFiles[i]); + + // delete legacy keys. + Cfg.SetValue('Files/IndexFile/Value',''); + Cfg.SetValue('Files/TOCFile/Value', ''); + Cfg.SetValue('Files/MakeBinaryTOC/Value',''); + Cfg.SetValue('Files/MakeBinaryIndex/Value',''); + Cfg.SetValue('Settings/IndexFile/Value', IndexFileName); + Cfg.SetValue('Settings/TOCFile/Value', TableOfContentsFileName); + Cfg.SetValue('Settings/MakeBinaryTOC/Value',MakeBinaryTOC); + Cfg.SetValue('Settings/MakeBinaryIndex/Value',MakeBinaryIndex); + Cfg.SetValue('Settings/AutoFollowLinks/Value', AutoFollowLinks); Cfg.SetValue('Settings/MakeSearchable/Value', MakeSearchable); Cfg.SetValue('Settings/DefaultPage/Value', DefaultPage); Cfg.SetValue('Settings/Title/Value', Title); Cfg.SetValue('Settings/OutputFileName/Value', OutputFileName); Cfg.SetValue('Settings/DefaultFont/Value', DefaultFont); + + Cfg.SetValue('Settings/DefaultWindow/Value', DefaultWindow); + + Cfg.Flush; Cfg.Free; end; diff --git a/packages/chm/src/chmtypes.pas b/packages/chm/src/chmtypes.pas index 05a70ca60b..30f2eec4c7 100644 --- a/packages/chm/src/chmtypes.pas +++ b/packages/chm/src/chmtypes.pas @@ -25,7 +25,7 @@ unit chmtypes; interface uses - Classes, SysUtils; + Classes, SysUtils,xmlcfg; type TSectionName = (snMSCompressed, snUnCompressed); @@ -91,6 +91,38 @@ type end; + TCHMWindow = Class + window_type, + Title_bar_text, + Toc_file, + index_file, + Default_File, + Home_button_file, + Jumpbutton_1_File, + Jumpbutton_1_Text, + Jumpbutton_2_File, + Jumpbutton_2_Text : string; + nav_style : integer; // overlay with bitfields (next 2 also) + navpanewidth : integer; + buttons : integer; + left, + top, + right, + bottom : integer; + styleflags , + xtdstyleflags, + window_show_state, + navpane_initially_closed, + navpane_default, + navpane_location, + wm_notify_id : integer; + Constructor create(s:string=''); + procedure load_from_ini(txt:string); + procedure savetoxml(cfg:TXMLConfig;key:string); + procedure loadfromxml(cfg:TXMLConfig;key:string); + end; + + TTOCIdxHeader = record BlockSize: DWord; // 4096 EntriesOffset: DWord; @@ -392,5 +424,144 @@ begin //WriteLn(ChunkLevelCount); end; -end. +function getnext(const s:string;var i: integer;len:integer):string; +var + ind : integer; +begin + if i>len then exit(''); + ind:=i; + if s[ind]='"' then + begin + inc(ind); + while (ind<=len) and (s[ind]<>'"') do inc(ind); + result:=copy(s,i+1,ind-i-1); + inc(ind); // skip " + end + else + begin + while (ind<=len) and (s[ind]<>',') do inc(ind); + result:=copy(s,i,ind-i); + end; + i:=ind+1; // skip , +end; + +function getnextint(const txt:string;var ind: integer;len:integer):integer; + +var s : string; +begin + s:=getnext(txt,ind,len); + result:=strtointdef(s,0); // I think this does C style hex, if not fixup here. +end; + +procedure TCHMWindow.load_from_ini(txt:string); +var ind,len, + j,k : integer; + arr : array[0..3] of integer; + s2 : string; +begin + j:=pos('=',txt); + if j>0 then + txt[j]:=','; + ind:=1; len:=length(txt); + window_type :=getnext(txt,ind,len); + Title_bar_text :=getnext(txt,ind,len); + index_file :=getnext(txt,ind,len); + Toc_file :=getnext(txt,ind,len); + Default_File :=getnext(txt,ind,len); + Home_button_file :=getnext(txt,ind,len); + Jumpbutton_1_File :=getnext(txt,ind,len); + Jumpbutton_1_Text :=getnext(txt,ind,len); + Jumpbutton_2_File :=getnext(txt,ind,len); + Jumpbutton_2_Text :=getnext(txt,ind,len); + + nav_style :=getnextint(txt,ind,len); + navpanewidth :=getnextint(txt,ind,len); + buttons :=getnextint(txt,ind,len); + k:=0; + repeat + s2:=getnext(txt,ind,len); + if (length(s2)>0) and (s2[1]='[') then delete(s2,1,1); + j:=pos(']',s2); + if j>0 then delete(s2,j,1); + arr[k]:=strtointdef(s2,0); + inc(k); + until (j<>0) or (ind>len); + left :=arr[0]; + top :=arr[1]; + right :=arr[2]; + bottom:=arr[3]; + styleflags :=getnextint(txt,ind,len); + xtdstyleflags :=getnextint(txt,ind,len); + window_show_state :=getnextint(txt,ind,len); + navpane_initially_closed :=getnextint(txt,ind,len); + navpane_default :=getnextint(txt,ind,len); + navpane_location :=getnextint(txt,ind,len); + wm_notify_id :=getnextint(txt,ind,len); +end; + +procedure TCHMWindow.savetoxml(cfg:TXMLConfig;key:string); +begin + cfg.setvalue(key+'window_type',window_type); + cfg.setvalue(key+'title_bar_text',title_bar_text); + cfg.setvalue(key+'toc_file', Toc_file ); + cfg.setvalue(key+'index_file', index_file ); + cfg.setvalue(key+'default_file', Default_File ); + cfg.setvalue(key+'home_button_file', Home_button_file); + cfg.setvalue(key+'jumpbutton_1_file', Jumpbutton_1_File ); + cfg.setvalue(key+'jumpbutton_1_text', Jumpbutton_1_Text ); + cfg.setvalue(key+'jumpbutton_2_file', Jumpbutton_2_File ); + cfg.setvalue(key+'jumpbutton_2_text', Jumpbutton_2_Text ); + cfg.setvalue(key+'nav_style', nav_style ); + cfg.setvalue(key+'navpanewidth', navpanewidth ); + cfg.setvalue(key+'buttons', buttons ); + cfg.setvalue(key+'left', left); + cfg.setvalue(key+'top', top ); + cfg.setvalue(key+'right', right ); + cfg.setvalue(key+'bottom', bottom ); + cfg.setvalue(key+'styleflags', styleflags); + cfg.setvalue(key+'xtdstyleflags', xtdstyleflags ); + cfg.setvalue(key+'window_show_state', window_show_state ); + cfg.setvalue(key+'navpane_initially_closed',navpane_initially_closed ); + cfg.setvalue(key+'navpane_default',navpane_default); + cfg.setvalue(key+'navpane_location',navpane_location ); + cfg.setvalue(key+'wm_notify_id',wm_notify_id ); +end; + +procedure TCHMWindow.loadfromxml(cfg:TXMLConfig;key:string); + +begin + window_type :=cfg.getvalue(key+'window_type',''); + Title_bar_text :=cfg.getvalue(key+'title_bar_text',''); + Toc_file :=cfg.getvalue(key+'toc_file',''); + Index_file :=cfg.getvalue(key+'index_file',''); + Default_File :=cfg.getvalue(key+'default_file',''); + Home_button_file :=cfg.getvalue(key+'home_button_file',''); + Jumpbutton_1_File :=cfg.getvalue(key+'jumpbutton_1_file',''); + Jumpbutton_1_Text :=cfg.getvalue(key+'jumpbutton_1_text',''); + Jumpbutton_2_File :=cfg.getvalue(key+'jumpbutton_2_file',''); + Jumpbutton_2_Text :=cfg.getvalue(key+'jumpbutton_2_text',''); + nav_style :=cfg.getvalue(key+'nav_style',0); + navpanewidth :=cfg.getvalue(key+'navpanewidth',0); + buttons :=cfg.getvalue(key+'buttons',0); + left :=cfg.getvalue(key+'left',0); + top :=cfg.getvalue(key+'top',0); + right :=cfg.getvalue(key+'right',0); + bottom :=cfg.getvalue(key+'bottom',0); + styleflags :=cfg.getvalue(key+'styleflags',0); + xtdstyleflags :=cfg.getvalue(key+'xtdstyleflags',0); + window_show_state :=cfg.getvalue(key+'window_show_state',0); + navpane_initially_closed :=cfg.getvalue(key+'navpane_initially_closed',0); + navpane_default :=cfg.getvalue(key+'navpane_default',0); + navpane_location :=cfg.getvalue(key+'navpane_location',0); + wm_notify_id :=cfg.getvalue(key+'wm_notify_id',0); +end; + +Constructor TCHMWindow.create(s:string=''); + +begin + if s<>'' then + load_from_ini(s); +end; + +end.