fpc/packages/ide/wchmhwrap.pas
marco 95217ad437 --- Merging r45406 into '.':
U    packages/ide/wchmhwrap.pas
--- Recording mergeinfo for merge of r45406 into '.':
 U   .

# revisions: 45406

git-svn-id: branches/fixes_3_2@45407 -
2020-05-17 18:29:57 +00:00

331 lines
9.7 KiB
ObjectPascal

{
This file is part of the Free Pascal Integrated Development Environment
Copyright (c) 2008 by Marco van de Voort
Wrapper for CHM reading to avoid having to import Delphi units into whtmlhlp,
which can cause all kinds of namespace conflicts.
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.
**********************************************************************}
unit wchmhwrap;
interface
{$Mode Delphi}
Uses wutils,whelp,whtml,SysUtils,ChmReader,ChmSiteMap,Classes;
Type
// TopicLinks: PTopicLinkCollection;IndexEntries : PUnsortedIndexEntryCollection;
TChmWrapper = Class
private
ffs : Classes.TFileStream;
fchmr : TChmReader;
findex : TChmSiteMap;
ftopic : TChmSiteMap;
floaded : boolean;
fileid : integer;
fshortname : string;
flongname : string;
fTopicLinks : PTopicLinkCollection;
public
constructor Create(name:String;aid:integer;TopicLinks:PTopicLinkCollection);
function LoadIndex(id:integer;TopicLinks: PTopicLinkCollection;IndexEntries : PUnsortedIndexEntryCollection;helpfacility:PHelpFacility):boolean;
function GetTopic(name:string):PMemoryTextFile;
destructor Destroy;override;
end;
function combinepaths(relpath,basepath:String):String;
function CHMResolve( href: ansistring; var AFileId,ALinkId : longint):boolean;
function stringreplace(const s:ansistring;const oldstr:ansistring; const newstr:ansistring):ansistring;
implementation
var CHMIndex : TStringList; // list to register open CHMs.
function combinepaths(relpath,basepath:String):String;
begin
{$ifdef combinedebug}
debugmessageS({$i %file%},'combine in "'+relpath+'" and "'+basepath+'"',{$i %line%},'1',0,0);
{$endif}
if relpath='' then exit;
if relpath[length(relpath)]<>'/' Then
basepath:=extractfiledir(basepath);
while (length(relpath)>0) and (copy(relpath,1,3)='../') do
begin
basepath:=extractfiledir(basepath);
delete(relpath,1,3);
end;
{$ifdef combinedebug}
debugmessageS({$i %file%},'combine out "'+relpath+'" and "'+basepath+'"',{$i %line%},'1',0,0);
{$endif}
if (length(basepath)>0) and (length(relpath)>0) then
begin
if (relpath[1]<>'/') and (basepath[length(basepath)]<>'/') then
basepath:=basepath+'/';
{$ifdef combinedebug}
debugmessageS({$i %file%},'combine out2 "'+relpath+'" and "'+basepath+'"',{$i %line%},'1',0,0);
{$endif}
end;
result:=basepath+relpath;
end;
Constructor TChmWrapper.Create(name:string;aid:integer;TopicLinks:PTopicLinkCollection);
begin
ffs:=Classes.TFileStream.create(name,fmOpenRead or fmsharedenynone);
fchmr:=TChmReader.Create(ffs,True); // owns ffs
findex:=nil;
FTopicLinks:=TopicLinks;
if not fchmr.isvalidfile then
begin
freeandnil(fchmr);
freeandnil(ffs);
exit;
end;
fileid:=aid;
flongname:=name;
fshortname:=lowercase(extractfilename(name)); // We assume ms-its: urls are case insensitive wrt filename.
chmindex.addobject(fshortname,self);
{$ifdef wdebug}
debugmessageS({$i %file%},'TCHMWrapper.Create: before sitemap creation '+fshortname+' id='+inttostr(aid),{$i %line%},'1',0,0);
{$endif}
findex:=TChmSiteMap.create(stindex);
ftopic:=TChmSiteMap.create(sttoc);
floaded:=false;
end;
function TChmWrapper.LoadIndex(id:integer;TopicLinks: PTopicLinkCollection;IndexEntries : PUnsortedIndexEntryCollection;helpfacility:PHelpFacility):boolean;
function FormatAlias(Alias: string): string;
begin
if Assigned(HelpFacility) then
if length(Alias)>HelpFacility^.IndexTabSize-4 then
Alias:=Trim(copy(Alias,1,HelpFacility^.IndexTabSize-4-2))+'..';
// if (length(alias)>0) and (alias[1]<>'/') then Alias:='/'+alias;
FormatAlias:=Alias;
end;
function searchlocal(item:TChmSiteMapItem):string;
var i:integer;
sitem : TChmSiteMapSubItem;
begin
result:='';
for i:=0 to item.SubItemcount-1 do
begin
sitem:=item.subitem[i];
if sitem.local<>'' then
exit(sitem.local);
end;
end;
var
m : Classes.TMemoryStream;
i,j : integer;
item : TChmSiteMapItem;
tli: integer;
s,s2 : String;
begin
result:=false;
if floaded then exit;
if not assigned (fchmr) then exit;
{$ifdef wdebug}
debugmessageS({$i %file%},'TCHMWrapper: indexfilename:'+fchmr.indexfile,{$i %line%},'1',0,0);
{$endif}
if assigned(findex) then
freeandnil(findex);
findex:=fchmr.GetIndexSitemap(false);
{$ifdef wdebug}
debugmessageS({$i %file%},'TCHMWrapper: loadindex after final ',{$i %line%},'1',0,0);
{$endif}
tli:=TopicLinks^.AddItem(fchmr.defaultpage);
TLI:=EncodeHTMLCtx(ID,TLI+1);
IndexEntries^.Insert(NewIndexEntry( FormatAlias('Table of contents'),ID,TLI));
if findex= Nil Then
begin
floaded:=true;
exit(true);
end;
if assigned(findex.items) and (findex.items.count>0) Then
for i:=0 to findex.items.count-1 do
begin
item:=findex.items.item[i];
s:=formatalias(item.text);
if s<>'' then
begin
s2:='';
if item.SubItemcount>1 then
s2:=item.SubItem[1].local;
if s2='' then
s2:=searchlocal(item);
if (length(s2)>0) and (s2[1]<>'/') then
tli:=TopicLinks^.AddItem('/'+s2)
else
tli:=TopicLinks^.AddItem(s2);
TLI:=EncodeHTMLCtx(ID,TLI+1);
IndexEntries^.Insert(NewIndexEntry( FormatAlias(item.text),ID,TLI));
end;
end;
{$ifdef wdebug}
debugmessageS({$i %file%},'TCHMWrapper: endloadindex ',{$i %line%},'1',0,0);
{$endif}
floaded:=true;
result:=true;
end;
procedure splitline(idestream:PMemoryTextFile;s:ansistring);
function scanvalue:integer; // searches for a possible breaking point left of char 255.
var n,i : integer;
lastpoint:integer;
inquote : boolean;
begin
lastpoint:=-1;
n:=length(s);
if n>250 then n:=250;
i:=1; inquote:=false;
while (i<=n) do
begin
while (s[i]<>' ') and (s[i]<>'"') and (i<=n) do inc(i);
if (s[i]=' ') and not inquote then lastpoint:=i;
if (s[i]='"') then inquote:=not inquote;
inc(i);
end;
scanvalue:=lastpoint;
end;
var position : longint;
begin
position:=0;
while (length(s)>250) and (position<>-1) do
begin
position:=scanvalue;
if position<>-1 then
begin
idestream.addline(copy(s,1,position-1));
delete(s,1,position);
end;
end;
if length(s)<>0 then
idestream.addline(s);
end;
function TChmWrapper.GetTopic(name:string):PMemoryTextFile;
var
m : Classes.TMemorystream;
linedata:Classes.TStringList;
i : integer;
begin
result:=nil;
if not assigned(fchmr) or (name='') then exit;
If (name[1]<>'/') and (copy(name,1,7)<>'ms-its:') Then
name:='/'+name;
linedata:=Classes.TStringList.create;
try
{$ifdef wdebug}
debugmessageS({$i %file%},'TCHMWrapper: Getting file '+name,{$i %line%},'1',0,0);
{$endif}
// if uppercase(name)='TABLE OF CONTENTS' Then
// m:=fchmr.getobject(fchmr.tocfile)
// else
m:=fchmr.getobject(name);
if not assigned(m) then exit;
linedata.loadfromstream(m);
result:=new(PMemoryTextFile,Init);
for i:=0 to linedata.count-1 do
begin
if length(linedata[i])>250 Then
splitline(result,linedata[i])
else
result.addline(linedata[i]);
end;
finally
m.free;
linedata.free;
end;
end;
destructor TChmWrapper.Destroy;
var i : integer;
begin
i:=chmindex.indexof(fshortname);
if i<>-1 then
begin
chmindex.delete(i);
{$ifdef wdebug}
debugmessageS({$i %file%},'TCHMWrapper: deregistering '+fshortname,{$i %line%},'1',0,0);
{$endif}
end;
freeandnil(ftopic);
freeandnil(findex);
freeandnil(fchmr);
{$ifdef wdebug}
debugmessageS({$i %file%},'TCHMWrapper: destroying ',{$i %line%},'1',0,0);
{$endif}
end;
function CHMResolve( href: ansistring; var AFileId,ALinkId : longint):boolean;
var filename, restlink : ansistring;
I :integer;
chmw: TCHMWrapper;
begin
result:=false;
if copy(href,1,7)='ms-its:' then
begin
{$ifdef wdebug}
debugmessageS({$i %file%},'TCHMWrapper: resolving '+href,{$i %line%},'1',0,0);
{$endif}
delete(href,1,7);
i:=pos('::',href);
if i<>0 then
begin
filename:=lowercase(copy(href,1,i-1));
restlink:=lowercase(copy(href,i+2,length(href)-(I+2)+1));
i:=chmindex.indexof(filename);
if i<>-1 then
begin
{$ifdef wdebug}
debugmessageS({$i %file%},'TCHMWrapper: resolving '+filename+' '+inttostr(i),{$i %line%},'1',0,0);
debugmessageS({$i %file%},'TCHMWrapper: resolving '+restlink+' ',{$i %line%},'1',0,0);
{$endif}
chmw:=TCHMWrapper(chmindex.objects[i]);
Afileid:=chmw.fileid;
alinkid:=chmw.fTopicLinks.additem(restlink);
result:=true;
end;
end;
end
end;
function stringreplace(const s:ansistring;const oldstr:ansistring; const newstr:ansistring):ansistring;
begin
result:=sysutils.stringreplace(s,oldstr,newstr,[rfreplaceall]);
end;
initialization
ChmIndex:=TStringlist.create;
ChmIndex.sorted:=true;
finalization
ChmIndex.Free;
end.