lazarus/lcl/lazhelpintf.pas

2695 lines
77 KiB
ObjectPascal

{ $Id$ }
{
*****************************************************************************
This file is part of the Lazarus Component Library (LCL)
See the file COPYING.modifiedLGPL.txt, included in this distribution,
for details about the license.
*****************************************************************************
Author: Mattias Gaertner
Abstract:
This unit defines various base classes for the LCL Help System.
ToDo:
- fix TCHMHelpViewer
- Make THelpDatabase and THelpViewer components usable in the designer.
- localization support.
- Add Help Editing functions
}
unit LazHelpIntf;
{$mode objfpc}{$H+}
interface
uses
Classes, SysUtils,
// LazUtils
FileUtil, LazFileUtils, LazUtilities, LazLoggerBase, LazConfigStorage, Masks,
// LCL
LCLStrConsts, Dialogs, HelpIntfs;
type
{ THelpQueryItem }
THelpQueryItem = class
public
function AsString: string; virtual; abstract;
function IsEqual(QueryItem: THelpQueryItem): boolean; virtual;
end;
{ TPascalHelpContextList }
TPascalHelpContextType = (
pihcFilename,
pihcSourceName, // unit name, library name, ..
pihcProperty,
pihcProcedure,
pihcParameterList,
pihcVariable,
pihcType,
pihcConst
);
TPascalHelpContext = record
Descriptor: TPascalHelpContextType;
Context: string;
end;
TPascalHelpContextPtr = ^TPascalHelpContext;
TPascalHelpContextList = class(THelpQueryItem)
private
FCount: integer;
fItems: TPascalHelpContextPtr;
function GetItems(Index: integer): TPascalHelpContext;
public
procedure Add(const Context: TPascalHelpContext);
procedure Add(Descriptor: TPascalHelpContextType; const Context: string);
procedure Insert(Index: integer; const Context: TPascalHelpContext);
procedure Clear;
destructor Destroy; override;
function IsEqual(QueryItem: THelpQueryItem): boolean; override;
function CompareList(AList: TPascalHelpContextList): integer;
function AsString: string; override;
public
property Count: integer read FCount;
property Items[Index: integer]: TPascalHelpContext read GetItems;
property List: TPascalHelpContextPtr read fItems;
end;
THelpDatabase = class;
{ THelpNode
A help node is a position/place in a help database.
For example it points to a Help file or to a Link on a HTML file. }
THelpNodeType = (
hntURLIDContext, // URL, ID and Context valid
hntURL, // URL valid, ignore ID and Context
hntURLID, // URL and ID valid, ignore Context
hntID, // ID valid, ignore URL and Context
hntContext, // Context valid, ignore URL and ID
hntURLContext // URL and Context valid, ignore ID
);
THelpNode = class(TPersistent)
private
FContext: THelpContext;
FURL: string;
FHelpType: THelpNodeType;
fID: string;
FOwner: THelpDatabase;
FTitle: string;
public
constructor Create(TheOwner: THelpDatabase; Node: THelpNode);
constructor Create(TheOwner: THelpDatabase;
const TheTitle, TheURL, TheID: string;
TheContext: THelpContext);
constructor CreateURL(TheOwner: THelpDatabase;
const TheTitle, TheURL: string);
constructor CreateID(TheOwner: THelpDatabase; const TheTitle, TheID: string);
constructor CreateURLID(TheOwner: THelpDatabase; const TheTitle,
TheURL, TheID: string);
constructor CreateContext(TheOwner: THelpDatabase; const TheTitle: string;
TheContext: THelpContext);
constructor CreateURLContext(TheOwner: THelpDatabase;
const TheTitle, TheURL: string;
TheContext: THelpContext);
public
property Owner: THelpDatabase read FOwner write FOwner;
function URLValid: boolean;
function IDValid: boolean;
function ContextValid: boolean;
function AsString: string;
procedure Assign(Source: TPersistent); override;
published
property Title: string read FTitle write FTitle;
property HelpType: THelpNodeType read FHelpType write FHelpType;
property URL: string read FURL write FURL;
property ID: string read fID write fID;
property Context: THelpContext read FContext write FContext;
end;
{ THelpNodeQuery }
THelpNodeQuery = class
private
FNode: THelpNode;
FQueryItem: THelpQueryItem;
public
constructor Create;
constructor Create(TheNode: THelpNode; TheQueryItem: THelpQueryItem);
function IsEqual(TheNode: THelpNode; TheQueryItem: THelpQueryItem): boolean;
function IsEqual(NodeQuery: THelpNodeQuery): boolean;
function AsString: string;
property Node: THelpNode read FNode write FNode;
property QueryItem: THelpQueryItem read FQueryItem write FQueryItem;
end;
{ THelpNodeQueryList }
THelpNodeQueryList = class
private
fItems: TFPList;
function GetItems(Index: integer): THelpNodeQuery;
procedure SetItems(Index: integer; const AValue: THelpNodeQuery);
public
constructor Create;
destructor Destroy; override;
function Count: integer;
function Add(NodeQuery: THelpNodeQuery): integer;
function Add(Node: THelpNode; QueryItem: THelpQueryItem): integer;
procedure Delete(Index: integer);
function IndexOf(NodeQuery: THelpNodeQuery): integer;
function IndexOf(Node: THelpNode; QueryItem: THelpQueryItem): integer;
procedure Clear;
property Items[Index: integer]: THelpNodeQuery read GetItems write SetItems; default;
end;
{ THelpDBItem
Base class for registration items associated with a THelpDatabase.
See THelpDBSISourceDirectory for an example.
Node is optional, pointing to a help page about the help item. }
THelpDBItem = class(TPersistent)
private
FNode: THelpNode;
public
constructor Create(TheNode: THelpNode);
destructor Destroy; override;
published
property Node: THelpNode read FNode write FNode;
end;
{ THelpDBSISourceFile
Help registration item for a single source file.
If Filename is relative, the BasePathObject is used to get a base directory.
For example: If BasePathObject is a TLazPackage the Filename is relative to
the directory of the .lpk file }
THelpDBISourceFile = class(THelpDBItem)
private
FBasePathObject: TObject;
FFilename: string;
procedure SetFilename(const AValue: string);
public
constructor Create(TheNode: THelpNode; const TheFilename: string);
function FileMatches(const AFilename: string): boolean; virtual;
function GetFullFilename: string; virtual;
function GetBasePath: string; virtual;
published
property BasePathObject: TObject read FBasePathObject write FBasePathObject;
property Filename: string read FFilename write SetFilename;
end;
{ THelpDBISourceDirectory
Help registration item for source directory.
As THelpDBISourceFile, except that Filename is a directory and
the item is valid for all source files fitting the FileMask.
FileMask can be for example '*.pp;*.pas;*.inc'
For example: A package providing help for all its source files registers
a THelpDBISourceDirectory. Node points to the fpdoc main page.
}
THelpDBISourceDirectory = class(THelpDBISourceFile)
private
FFileMask: string;
FWithSubDirectories: boolean;
public
constructor Create(TheNode: THelpNode; const Directory,
TheFileMask: string; Recursive: boolean);
function FileMatches(const AFilename: string): boolean; override;
published
property FileMask: string read FFileMask write FFileMask;
property WithSubDirectories: boolean read FWithSubDirectories
write FWithSubDirectories;
end;
{ THelpDBISourceDirectories
Help registration item for source directories.
As THelpDBISourceDirectory, except that Filename are directories separated
by semicolon and the item is valid for all source files fitting the FileMask.
FileMask can be for example '*.pp;*.pas;*.inc'
For example: A package providing help for all its source files registers
a THelpDBISourceDirectory. Node points to the fpdoc main page.
}
THelpDBISourceDirectories = class(THelpDBISourceDirectory)
private
FBaseDirectory: string;
public
constructor Create(TheNode: THelpNode; const BaseDir, Directories,
TheFileMask: string; Recursive: boolean);
function FileMatches(const AFilename: string): boolean; override;
function GetFullFilename: string; override;
function GetBasePath: string; override;
published
property BaseDirectory: string read FBaseDirectory write FBaseDirectory;
end;
{ THelpDBIClass
Help registration item for a class.
Used by the IDE to search for help for a class without source.
For example for a registered component class in the component palette, that
comes without source. If the component comes with source use the
THelpDBISourceDirectory or THelpDBISourceFile instead. }
THelpDBIClass = class(THelpDBItem)
private
FTheClass: TClass;
public
property TheClass: TClass read FTheClass write FTheClass;
end;
{ THelpDBIMessage
Help registration item for a message (e.g. an FPC warning).
Used by the IDE to search for help for one message (typically a line).
For example a line like
"/usr/share/lazarus/components/synedit/syneditkeycmds.pp(532,10) Warning: Function result does not seem to be set"
}
THelpDBIMessage = class(THelpDBItem)
public
function MessageMatches(const TheMessage: string; MessageParts: TStrings
): boolean; virtual; abstract;
end;
{ THelpQueryNode }
THelpQueryNode = class(THelpQuery)
private
FNode: THelpNode;
public
constructor Create(const TheHelpDatabaseID: THelpDatabaseID;
const TheNode: THelpNode);
property Node: THelpNode read FNode write FNode;
end;
{ THelpDatabase
Base class for a collection of help files or entries.
BasePathObject: THelpDatabase can be created by packages.
The IDE will set BasePathObject accordingly. }
THelpDatabases = class;
THelpViewer = class;
TOnHelpDBFindViewer =
function(HelpDB: THelpDatabase; const MimeType: string;
var ErrMsg: string; out Viewer: THelpViewer): TShowHelpResult of object;
THelpDatabase = class(TComponent)
private
FAutoRegister: boolean;
FBasePathObject: TObject;
FID: THelpDatabaseID;
FDatabases: THelpDatabases;
FOnFindViewer: TOnHelpDBFindViewer;
FRefCount: integer;
FSearchItems: TFPList;
FSupportedMimeTypes: TStrings;
FTOCNode: THelpNode;
procedure SetAutoRegister(const AValue: boolean);
procedure SetID(const AValue: THelpDatabaseID);
procedure SetDatabases(const AValue: THelpDatabases);
protected
procedure SetSupportedMimeTypes(List: TStrings); virtual;
procedure AddSupportedMimeType(const AMimeType: string); virtual;
public
constructor Create(TheOwner: TComponent); override;
destructor Destroy; override;
procedure Reference;
procedure RegisterSelf;
procedure Release;
procedure UnregisterSelf;
function Registered: boolean;
function CanShowTableOfContents: boolean; virtual;
procedure ShowTableOfContents; virtual;
procedure ShowError(ShowResult: TShowHelpResult; const ErrMsg: string); virtual;
function ShowHelp(Query: THelpQuery; BaseNode, NewNode: THelpNode;
QueryItem: THelpQueryItem;
var ErrMsg: string): TShowHelpResult; virtual;
function ShowHelpFile(Query: THelpQuery; BaseNode: THelpNode;
const Title, Filename: string;
var ErrMsg: string): TShowHelpResult; virtual;
function SupportsMimeType(const AMimeType: string): boolean; virtual;
function GetNodesForKeyword(const HelpKeyword: string;
var ListOfNodes: THelpNodeQueryList;
var ErrMsg: string): TShowHelpResult; virtual;
function GetNodesForDirective(const HelpDirective: string;
var ListOfNodes: THelpNodeQueryList;
var ErrMsg: string): TShowHelpResult; virtual;
function GetNodesForContext(HelpContext: THelpContext;
var ListOfNodes: THelpNodeQueryList;
var ErrMsg: string): TShowHelpResult; virtual;
function GetNodesForPascalContexts(ListOfPascalHelpContextList: TList;
var ListOfNodes: THelpNodeQueryList;
var ErrMsg: string): TShowHelpResult; virtual;
function GetNodesForClass(AClass: TClass;
var ListOfNodes: THelpNodeQueryList;
var ErrMsg: string): TShowHelpResult; virtual;
function GetNodesForMessage(const AMessage: string; MessageParts: TStrings;
var ListOfNodes: THelpNodeQueryList;
var ErrMsg: string): TShowHelpResult; virtual;
function FindViewer(const MimeType: string; var ErrMsg: string;
out Viewer: THelpViewer): TShowHelpResult; virtual;
public
// registration
procedure RegisterItem(NewItem: THelpDBItem);
procedure RegisterItemWithNode(Node: THelpNode);
procedure RegisterFileItemWithNode(const Filename: string; Node: THelpNode);
procedure UnregisterItem(AnItem: THelpDBItem);
procedure UnregisterAllItems;
function RegisteredItemCount: integer;
function GetRegisteredItem(Index: integer): THelpDBItem;
procedure Load(Storage: TConfigStorage); virtual;
procedure Save(Storage: TConfigStorage); virtual;
function GetLocalizedName: string; virtual;
public
property Databases: THelpDatabases read FDatabases write SetDatabases;
property ID: THelpDatabaseID read FID write SetID;
property SupportedMimeTypes: TStrings read FSupportedMimeTypes;
property BasePathObject: TObject read FBasePathObject write FBasePathObject;
property TOCNode: THelpNode read FTOCNode write FTOCNode;
property AutoRegister: boolean read FAutoRegister write SetAutoRegister;
property OnFindViewer: TOnHelpDBFindViewer read FOnFindViewer write FOnFindViewer;
end;
THelpDatabaseClass = class of THelpDatabase;
{ THelpDatabases
Class for storing all registered THelpDatabase(s) }
THelpDatabases = class(THelpManager)
private
FItems: TFPList;
FHelpDBClasses: TFPList;
function GetItems(Index: integer): THelpDatabase;
procedure DoRegisterDatabase(ADatabase: THelpDatabase);
procedure DoUnregisterDatabase(ADatabase: THelpDatabase);
public
constructor Create;
destructor Destroy; override;
function Count: integer;
property Items[Index: integer]: THelpDatabase read GetItems; default;
public
function FindDatabase(ID: THelpDatabaseID): THelpDatabase;
function GetDatabase(ID: THelpDatabaseID; var HelpDB: THelpDatabase;
var HelpResult: TShowHelpResult; var ErrMsg: string): boolean;
function IndexOf(ID: THelpDatabaseID): integer;
function CreateUniqueDatabaseID(const WishID: string): THelpDatabaseID;
function CreateHelpDatabase(const WishID: string;
HelpDataBaseClass: THelpDatabaseClass;
AutoRegister: boolean): THelpDatabase;
function ShowTableOfContents(var ErrMsg: string): TShowHelpResult; override;
procedure ShowError(ShowResult: TShowHelpResult; const ErrMsg: string); override;
function GetBaseURLForBasePathObject(BasePathObject: TObject): string; virtual;
function GetBaseDirectoryForBasePathObject(BasePathObject: TObject): string; virtual;
function FindViewer(const MimeType: string; var ErrMsg: string;
var Viewer: THelpViewer): TShowHelpResult; virtual;
function SubstituteMacros(var s: string): boolean; virtual;
public
// show help for ...
function ShowHelpForNodes(Query: THelpQuery; Nodes: THelpNodeQueryList;
var ErrMsg: string): TShowHelpResult; virtual;
function ShowHelpForQuery(Query: THelpQuery; AutoFreeQuery: boolean;
var ErrMsg: string): TShowHelpResult; override;
function ShowHelpForContext(Query: THelpQueryContext;
var ErrMsg: string): TShowHelpResult; override;
function ShowHelpForKeyword(Query: THelpQueryKeyword;
var ErrMsg: string): TShowHelpResult; override;
function ShowHelpForDirective(Query: THelpQueryDirective;
var ErrMsg: string): TShowHelpResult; override;
function ShowHelpForPascalContexts(Query: THelpQueryPascalContexts;
var ErrMsg: string): TShowHelpResult; override;
function ShowHelpForSourcePosition(Query: THelpQuerySourcePosition;
var ErrMsg: string): TShowHelpResult; override;
function ShowHelpForMessageLine(Query: THelpQueryMessage;
var ErrMsg: string): TShowHelpResult; override;
function ShowHelpForClass(Query: THelpQueryClass;
var ErrMsg: string): TShowHelpResult; override;
function ShowHelpFile(const Filename, Title, MimeType: string;
var ErrMsg: string): TShowHelpResult; override;
function ShowHelp(const URL, Title, MimeType: string;
var ErrMsg: string): TShowHelpResult; override;
// search registered items in all databases
function GetNodesForKeyword(const HelpKeyword: string;
var ListOfNodes: THelpNodeQueryList;
var ErrMsg: string): TShowHelpResult; virtual;
function GetNodesForDirective(const HelpDirective: string;
var ListOfNodes: THelpNodeQueryList;
var ErrMsg: string): TShowHelpResult; virtual;
function GetNodesForContext(HelpContext: THelpContext;
var ListOfNodes: THelpNodeQueryList;
var ErrMsg: string): TShowHelpResult; virtual;
function GetNodesForPascalContexts(ListOfPascalHelpContextList: TList;
var ListOfNodes: THelpNodeQueryList;
var ErrMsg: string): TShowHelpResult; virtual;
function GetNodesForClass(AClass: TClass;
var ListOfNodes: THelpNodeQueryList;
var ErrMsg: string): TShowHelpResult; virtual;
function GetNodesForMessage(const AMessage: string; MessageParts: TStrings;
var ListOfNodes: THelpNodeQueryList;
var ErrMsg: string): TShowHelpResult; virtual;
// Show the help selector
function ShowHelpSelector(Query: THelpQuery; Nodes: THelpNodeQueryList;
var ErrMsg: string;
var Selection: THelpNodeQuery
): TShowHelpResult; virtual;
public
// registration of THelpDatabaseClass
procedure RegisterHelpDatabaseClass(NewHelpDB: THelpDatabaseClass);
procedure UnregisterHelpDatabaseClass(AHelpDB: THelpDatabaseClass);
function HelpDatabaseClassCount: integer;
function GetHelpDatabaseClass(Index: integer): THelpDatabaseClass;
procedure Load(Storage: TConfigStorage); virtual;
procedure Save(Storage: TConfigStorage); virtual;
end;
{ THelpViewer
base class for all Help viewers }
THelpViewer = class(TComponent)
private
FAutoRegister: boolean;
FParameterHelp: string;
FStorageName: string;
FSupportedMimeTypes: TStrings;
procedure SetAutoRegister(const AValue: boolean);
protected
procedure SetSupportedMimeTypes(List: TStrings); virtual;
procedure AddSupportedMimeType(const AMimeType: string); virtual;
public
constructor Create(TheOwner: TComponent); override;
destructor Destroy; override;
function SupportsTableOfContents: boolean; virtual;
procedure ShowTableOfContents(Node: THelpNode); virtual;
function SupportsMimeType(const AMimeType: string): boolean; virtual;
function ShowNode(Node: THelpNode; var ErrMsg: string): TShowHelpResult; virtual;
procedure Hide; virtual;
procedure Assign(Source: TPersistent); override;
procedure Load(Storage: TConfigStorage); virtual;
procedure Save(Storage: TConfigStorage); virtual;
function GetLocalizedName: string; virtual;
procedure RegisterSelf; virtual;
procedure UnregisterSelf; virtual;
public
property SupportedMimeTypes: TStrings read FSupportedMimeTypes;
property ParameterHelp: string read FParameterHelp write FParameterHelp;
property StorageName: string read FStorageName write FStorageName;
property AutoRegister: boolean read FAutoRegister write SetAutoRegister;
end;
THelpViewerClass = class of THelpViewer;
{ THelpViewers }
THelpViewers = class
private
FItems: TFPList;
FDestroying: boolean;
function GetItems(Index: integer): THelpViewer;
public
constructor Create;
destructor Destroy; override;
procedure Clear;
function Count: integer;
function GetViewersSupportingMimeType(const MimeType: string): TList;
procedure RegisterViewer(AHelpViewer: THelpViewer);
procedure UnregisterViewer(AHelpViewer: THelpViewer);
procedure Load(Storage: TConfigStorage); virtual;
procedure Save(Storage: TConfigStorage); virtual;
function IndexOf(AHelpViewer: THelpViewer): integer;
public
property Items[Index: integer]: THelpViewer read GetItems; default;
end;
{ THelpBasePathObject
Simple class to store a base file path for help databases. }
THelpBasePathObject = class(TPersistent)
private
FBasePath: string;
protected
procedure SetBasePath(const AValue: string); virtual;
public
constructor Create;
constructor Create(const TheBasePath: string);
property BasePath: string read FBasePath write SetBasePath;
end;
{ THelpBaseURLObject
Simple class to store a base URL path for help databases. }
THelpBaseURLObject = class(TPersistent)
private
FBaseURL: string;
protected
procedure SetBaseURL(const AValue: string);
public
constructor Create;
constructor Create(const TheBaseURL: string);
property BaseURL: string read FBaseURL write SetBaseURL;
end;
var
HelpDatabases: THelpDatabases = nil; // initialized by the IDE
HelpViewers: THelpViewers = nil; // initialized by the IDE
procedure CreateLCLHelpSystem;
procedure FreeLCLHelpSystem;
procedure FreeUnusedLCLHelpSystem;
// URL functions
// used names:
// URL: Scheme + Path + Params e.g. http://www.freepascal.org?param
// URLScheme: e.g. file or http
// URLPath: URL without type and without parameters (always / as path delimiter)
// URLParams: parameters appended by ? or #
function FilenameToURL(const Filename: string): string;
function FilenameToURLPath(const Filename: string): string;
function URLPathToFilename(const URLPath: string): string;
procedure SplitURL(const URL: string; out URLScheme, URLPath, URLParams: string);
function CombineURL(const URLScheme, URLPath, URLParams: string): string;
function URLFilenameIsAbsolute(const URLPath: string): boolean;
function FindURLPathStart(const URL: string): integer;
function FindURLPathEnd(const URL: string): integer;
function ChompURLParams(const URL: string): string;
function ExtractURLPath(const URL: string): string;
function ExtractURLDirectory(const URL: string): string;
function TrimUrl(const URL: string): string;
function TrimURLPath(const URLPath: string): string;
function IsFileURL(const URL: string): boolean;
function AppendURLPathDelim(const URLPath: string): string;
procedure CreateListAndAdd(const AnObject: TObject; var List: TList;
OnlyIfNotExists: boolean);
procedure CreateNodeQueryListAndAdd(const ANode: THelpNode;
const QueryItem: THelpQueryItem;
var List: THelpNodeQueryList; OnlyIfNotExists: boolean);
implementation
procedure CreateLCLHelpSystem;
begin
if (HelpDatabases<>nil) or (HelpManager<>nil) then exit;
HelpDatabases:=THelpDatabases.Create;
HelpManager:=HelpDatabases;
HelpViewers:=THelpViewers.Create;
end;
procedure FreeLCLHelpSystem;
begin
FreeThenNil(HelpDatabases);
FreeThenNil(HelpViewers);
HelpManager:=nil;
end;
procedure FreeUnusedLCLHelpSystem;
begin
if (HelpViewers<>nil) and (HelpViewers.Count>0) then exit;
if (HelpDatabases<>nil) and (HelpDatabases.Count>0) then exit;
FreeLCLHelpSystem;
end;
function FilenameToURL(const Filename: string): string;
begin
Result:=FilenameToURLPath(Filename);
if Result<>'' then
Result:='file://'+Result;
end;
function FilenameToURLPath(const Filename: string): string;
var
i: Integer;
begin
Result:=Filename;
{$push}
{$warnings off}
if PathDelim<>'/' then
for i:=1 to length(Result) do
if Result[i]=PathDelim then
Result[i]:='/';
{$pop}
end;
function URLPathToFilename(const URLPath: string): string;
var
i: Integer;
begin
Result:=URLPath;
{$push}
{$warnings off}
if PathDelim<>'/' then
for i:=1 to length(Result) do
if Result[i]='/' then
Result[i]:=PathDelim;
{$pop}
end;
procedure SplitURL(const URL: string; out URLScheme, URLPath, URLParams: string);
var
Len: Integer;
ColonPos: Integer;
ParamStartPos: integer;
URLStartPos: Integer;
begin
URLScheme:='';
URLPath:='';
URLParams:='';
Len:=length(URL);
// search colon
ColonPos:=1;
while (ColonPos<=len) and (URL[ColonPos]<>':') do
inc(ColonPos);
if ColonPos>len then exit;
// get URLScheme
URLScheme:=copy(URL,1,ColonPos-1);
URLStartPos:=ColonPos+1;
// skip the '//' after the colon
if (URLStartPos<=len) and (URL[URLStartPos]='/') then inc(URLStartPos);
if (URLStartPos<=len) and (URL[URLStartPos]='/') then inc(URLStartPos);
// search for param delimiter (?) or anchor delimiter (#)
ParamStartPos:=ColonPos+1;
while (ParamStartPos<=len) and not (URL[ParamStartPos]in ['?', '#']) do
inc(ParamStartPos);
// get URLPath and URLParams
URLPath:=copy(URL,URLStartPos,ParamStartPos-URLStartPos);
URLParams:=copy(URL,ParamStartPos,len-ParamStartPos+1);
end;
function CombineURL(const URLScheme, URLPath, URLParams: string): string;
begin
Result:=URLScheme+'://'+URLPath;
if URLParams<>'' then
Result:=Result+URLParams;
end;
function URLFilenameIsAbsolute(const URLPath: string): boolean;
begin
Result:=FilenameIsAbsolute(URLPathToFilename(URLPath));
end;
function FindURLPathStart(const URL: string): integer;
var
Len: Integer;
ColonPos: Integer;
URLStartPos: Integer;
begin
Result:=-1;
Len:=length(URL);
// search colon
ColonPos:=1;
while (ColonPos<=len) and (URL[ColonPos]<>':') do
inc(ColonPos);
if ColonPos=Len then exit;
URLStartPos:=ColonPos+1;
// skip the '//' after the colon
if (URLStartPos<=Len) and (URL[URLStartPos]='/') then inc(URLStartPos);
if (URLStartPos<=Len) and (URL[URLStartPos]='/') then inc(URLStartPos);
Result:=URLStartPos;
end;
function FindURLPathEnd(const URL: string): integer;
var
Len: Integer;
begin
Result:=1;
Len:=length(URL);
while (Result<=Len) and not (URL[Result] in ['?','#']) do inc(Result);
end;
function ChompURLParams(const URL: string): string;
begin
Result:=copy(URL,1,FindURLPathEnd(URL)-1);
end;
function ExtractURLDirectory(const URL: string): string;
var
p: Integer;
PathStart: LongInt;
begin
Result:='';
PathStart:=FindURLPathStart(URL);
if PathStart<1 then exit;
p:=FindURLPathEnd(URL);
repeat
dec(p);
until (p<=0) or (URL[p]='/');
if p<=PathStart then exit;
Result:=copy(URL,1,p);
end;
function TrimUrl(const URL: string): string;
var
URLType, URLPath, URLParams: string;
begin
SplitURL(URL,URLType,URLPath,URLParams);
Result:=CombineURL(URLType,TrimURLPath(URLPath),URLParams);
end;
function TrimURLPath(const URLPath: string): string;
begin
Result:=FilenameToURLPath(TrimFilename(URLPathToFilename(URLPath)));
end;
function IsFileURL(const URL: string): boolean;
begin
Result:=(length(URL)>=7)
and (CompareByte(URL[1],'file://',7)=0);
end;
function AppendURLPathDelim(const URLPath: string): string;
begin
if (URLPath<>'') and (URLPath[length(URLPath)]<>'/') then
Result:=URLPath+'/'
else
Result:=URLPath;
end;
function ExtractURLPath(const URL: string): string;
var
URLType, URLPath, URLParams: string;
begin
SplitURL(URL,URLType,URLPath,URLParams);
Result:=URLPath;
end;
procedure CreateListAndAdd(const AnObject: TObject; var List: TList;
OnlyIfNotExists: boolean);
begin
if List=nil then
List:=TList.Create
else if OnlyIfNotExists and (List.IndexOf(AnObject)>=0) then
exit;
List.Add(AnObject);
end;
procedure CreateNodeQueryListAndAdd(const ANode: THelpNode;
const QueryItem: THelpQueryItem;
var List: THelpNodeQueryList; OnlyIfNotExists: boolean);
begin
if List=nil then
List:=THelpNodeQueryList.Create
else if OnlyIfNotExists and (List.IndexOf(ANode,QueryItem)>=0) then
exit;
List.Add(ANode,QueryItem);
end;
{ THelpDBISourceDirectories }
constructor THelpDBISourceDirectories.Create(TheNode: THelpNode; const BaseDir,
Directories, TheFileMask: string; Recursive: boolean);
begin
inherited Create(TheNode,Directories,TheFileMask,Recursive);
FBaseDirectory:=BaseDir;
end;
function THelpDBISourceDirectories.FileMatches(const AFilename: string
): boolean;
var
SearchPath: String;
EndPos: Integer;
StartPos: Integer;
Dir: String;
begin
Result:=false;
//debugln('THelpDBISourceDirectories.FileMatches AFilename="',AFilename,'" FFilename="',FFilename,'"');
if (FFilename='') or (AFilename='') then exit;
SearchPath:=GetFullFilename;
if SearchPath='' then begin
{$IFNDEF DisableChecks}
DebugLn(['WARNING: THelpDBISourceDirectory.FileMatches ',DbgSName(Self),' Filename="',Filename,'" -> ""']);
{$ENDIF}
exit;
end;
EndPos:=1;
while EndPos<=length(SearchPath) do begin
StartPos:=EndPos;
while (EndPos<=length(SearchPath)) and (SearchPath[EndPos]<>';') do
inc(EndPos);
Dir:=copy(SearchPath,StartPos,EndPos-StartPos);
inc(EndPos);
//debugln(['THelpDBISourceDirectories.FileMatches TheDirectory="',Dir,'" WithSubDirectories=',WithSubDirectories]);
if WithSubDirectories then begin
if not FileIsInPath(AFilename,Dir) then continue;
end else begin
if not FileIsInDirectory(AFilename,Dir) then continue;
end;
//debugln('THelpDBISourceDirectories.FileMatches FileMask="',FileMask,'"');
if (FileMask='')
or MatchesMaskList(ExtractFilename(AFilename),FileMask) then
exit(true);
end;
end;
function THelpDBISourceDirectories.GetFullFilename: string;
var
ExpFilename: String;
EndPos: Integer;
StartPos: Integer;
Dir: String;
BaseDir: String;
begin
ExpFilename:=FFilename;
//DebugLn(['THelpDBISourceDirectories.GetFullFilename ExpFilename="',ExpFilename,'" HelpDatabases=',DbgSName(HelpDatabases)]);
if (HelpDatabases<>nil) then
HelpDatabases.SubstituteMacros(ExpFilename);
//DebugLn(['THelpDBISourceFile.GetFullFilename substituted ',ExpFilename]);
EndPos:=1;
Result:='';
BaseDir:='';
while EndPos<=length(ExpFilename) do begin
StartPos:=EndPos;
while (EndPos<=length(ExpFilename)) and (ExpFilename[EndPos]<>';') do
inc(EndPos);
Dir:=TrimFilename(GetForcedPathDelims(copy(ExpFilename,StartPos,EndPos-StartPos)));
if Dir<>'' then begin
if not FilenameIsAbsolute(Dir) then begin
if BaseDir='' then
BaseDir:=AppendPathDelim(GetBasePath);
Dir:=BaseDir+Dir;
end;
if Result<>'' then Result:=Result+';';
Result:=Result+Dir;
end;
inc(EndPos);
end;
end;
function THelpDBISourceDirectories.GetBasePath: string;
begin
if BaseDirectory='' then
Result:=inherited GetBasePath
else begin
Result:=BaseDirectory;
if (HelpDatabases<>nil) then
HelpDatabases.SubstituteMacros(Result);
end;
Result:=TrimFilename(GetForcedPathDelims(Result));
end;
{ THelpDatabase }
procedure THelpDatabase.SetID(const AValue: THelpDatabaseID);
var
OldRegistered: Boolean;
begin
if FID=AValue then exit;
OldRegistered:=Registered;
if OldRegistered then UnregisterSelf;
FID:=AValue;
if OldRegistered then RegisterSelf;
end;
procedure THelpDatabase.SetAutoRegister(const AValue: boolean);
begin
if FAutoRegister=AValue then exit;
FAutoRegister:=AValue;
if not (csDesigning in ComponentState) then begin
if FAutoRegister then begin
if FID='' then
FID:=Name;
if Databases=nil then RegisterSelf;
end else begin
if Databases<>nil then UnregisterSelf;
end;
end;
end;
procedure THelpDatabase.SetDatabases(const AValue: THelpDatabases);
begin
if AValue=Databases then exit;
Reference;
if FDatabases<>nil then FDatabases.DoUnregisterDatabase(Self);
FDatabases:=AValue;
if FDatabases<>nil then FDatabases.DoRegisterDatabase(Self);
Release;
end;
procedure THelpDatabase.SetSupportedMimeTypes(List: TStrings);
begin
FSupportedMimeTypes.Free;
FSupportedMimeTypes:=List;
end;
procedure THelpDatabase.AddSupportedMimeType(const AMimeType: string);
begin
if FSupportedMimeTypes=nil then SetSupportedMimeTypes(TStringList.Create);
FSupportedMimeTypes.Add(AMimeType);
end;
constructor THelpDatabase.Create(TheOwner: TComponent);
begin
inherited Create(TheOwner);
end;
destructor THelpDatabase.Destroy;
var
i: Integer;
begin
Reference; // reference to not call Free again
if Databases<>nil then UnregisterSelf;
FSupportedMimeTypes.Free;
if FSearchItems<>nil then begin
for i:=FSearchItems.Count-1 downto 0 do
THelpNode(FSearchItems[i]).Free;
FSearchItems.Free;
end;
FTOCNode.Free;
inherited Destroy;
end;
procedure THelpDatabase.RegisterSelf;
begin
if Databases<>nil then
raise EHelpSystemException.Create(Format(rsHelpAlreadyRegistered, [ID]));
if HelpDatabases=nil then CreateLCLHelpSystem;
Databases:=HelpDatabases;
end;
procedure THelpDatabase.UnregisterSelf;
begin
if Databases=nil then
raise EHelpSystemException.Create(Format(rsHelpNotRegistered, [ID]));
Databases:=nil;
FreeUnusedLCLHelpSystem;
end;
function THelpDatabase.Registered: boolean;
begin
Result:=Databases<>nil;
end;
function THelpDatabase.CanShowTableOfContents: boolean;
begin
Result:=TOCNode<>nil;
end;
procedure THelpDatabase.ShowTableOfContents;
var
ErrMsg: string;
ShowResult: TShowHelpResult;
Query: THelpQueryTOC;
begin
if TOCNode=nil then exit;
ErrMsg:='';
Query:=THelpQueryTOC.Create(ID);
try
ShowResult:=ShowHelp(Query,nil,TOCNode,nil,ErrMsg);
finally
Query.Free;
end;
ShowError(ShowResult,ErrMsg);
end;
procedure THelpDatabase.ShowError(ShowResult: TShowHelpResult;
const ErrMsg: string);
begin
if ShowResult=shrSuccess then exit;
if Databases<>nil then
Databases.ShowError(ShowResult,ErrMsg)
else
raise EHelpSystemException.Create(ErrMsg);
end;
function THelpDatabase.ShowHelp(Query: THelpQuery; BaseNode, NewNode: THelpNode;
QueryItem: THelpQueryItem; var ErrMsg: string): TShowHelpResult;
begin
ErrMsg:='';
Result:=shrContextNotFound;
end;
function THelpDatabase.ShowHelpFile(Query: THelpQuery; BaseNode: THelpNode;
const Title, Filename: string; var ErrMsg: string): TShowHelpResult;
var
FileNode: THelpNode;
begin
FileNode:=THelpNode.CreateURL(Self,Title,FilenameToURL(Filename));
try
Result:=ShowHelp(Query,BaseNode,FileNode,nil,ErrMsg);
finally
FileNode.Free;
end;
end;
function THelpDatabase.SupportsMimeType(const AMimeType: string): boolean;
begin
Result:=false;
if FSupportedMimeTypes<>nil then
Result:=(FSupportedMimeTypes.IndexOf(AMimeType)>=0);
end;
function THelpDatabase.GetNodesForKeyword(const HelpKeyword: string;
var ListOfNodes: THelpNodeQueryList; var ErrMsg: string): TShowHelpResult;
// if ListOfNodes<>nil new nodes will be appended
// if ListOfNodes=nil and nodes exists a new list will be created
var
i: Integer;
Node: THelpNode;
begin
Result:=shrSuccess;
ErrMsg:='';
if csDesigning in ComponentState then exit;
// add the registered nodes
if FSearchItems<>nil then begin
for i:=0 to FSearchItems.Count-1 do begin
Node:=THelpDBItem(FSearchItems[i]).Node;
if (Node=nil) or (not Node.IDValid) then continue;
if AnsiCompareText(Node.ID,HelpKeyword)<>0 then continue;
CreateNodeQueryListAndAdd(Node,nil,ListOfNodes,true);
end;
end;
end;
function THelpDatabase.GetNodesForDirective(const HelpDirective: string;
var ListOfNodes: THelpNodeQueryList; var ErrMsg: string): TShowHelpResult;
// if ListOfNodes<>nil new nodes will be appended
// if ListOfNodes=nil and nodes exists a new list will be created
var
i: Integer;
Node: THelpNode;
begin
Result:=shrSuccess;
ErrMsg:='';
if csDesigning in ComponentState then exit;
// add the registered nodes
if FSearchItems<>nil then begin
for i:=0 to FSearchItems.Count-1 do begin
Node:=THelpDBItem(FSearchItems[i]).Node;
if (Node=nil) or (not Node.IDValid) then continue;
if AnsiCompareText(Node.ID,HelpDirective)<>0 then continue;
CreateNodeQueryListAndAdd(Node,nil,ListOfNodes,true);
end;
end;
end;
function THelpDatabase.GetNodesForContext(HelpContext: THelpContext;
var ListOfNodes: THelpNodeQueryList; var ErrMsg: string): TShowHelpResult;
// if ListOfNodes<>nil new nodes will be appended
// if ListOfNodes=nil and nodes exists a new list will be created
var
i: Integer;
Node: THelpNode;
begin
Result:=shrSuccess;
ErrMsg:='';
if csDesigning in ComponentState then exit;
// add the registered nodes
if FSearchItems<>nil then begin
for i:=0 to FSearchItems.Count-1 do begin
Node:=THelpDBItem(FSearchItems[i]).Node;
if (Node=nil) or (not Node.ContextValid) then continue;
if Node.Context<>HelpContext then continue;
CreateNodeQueryListAndAdd(Node,nil,ListOfNodes,true);
end;
end;
end;
function THelpDatabase.GetNodesForPascalContexts(
ListOfPascalHelpContextList: TList; var ListOfNodes: THelpNodeQueryList;
var ErrMsg: string): TShowHelpResult;
// if ListOfNodes<>nil new nodes will be appended
// if ListOfNodes=nil and nodes exists a new list will be created
var
i: Integer;
j: Integer;
SearchItem: THelpDBItem;
PascalContext: TPascalHelpContextList;
FileItem: THelpDBISourceFile;
Filename: String;
begin
Result:=shrSuccess;
ErrMsg:='';
if csDesigning in ComponentState then exit;
if (ListOfPascalHelpContextList=nil) or
(ListOfPascalHelpContextList.Count=0) then exit;
// add the registered nodes
//debugln('THelpDatabase.GetNodesForPascalContexts A ID="',ID,'" ListOfPascalHelpContextList.Count=',dbgs(ListOfPascalHelpContextList.Count));
if FSearchItems<>nil then begin
// check every Pascal context
for j:=0 to ListOfPascalHelpContextList.Count-1 do begin
PascalContext:=TPascalHelpContextList(ListOfPascalHelpContextList[j]);
//debugln('THelpDatabase.GetNodesForPascalContexts A ID="',ID,'" PascalContext.Count=',dbgs(PascalContext.Count));
if (PascalContext.Count>0) and
(PascalContext.List[0].Descriptor=pihcFilename) then begin
Filename:=PascalContext.List[0].Context;
// search file item
for i:=0 to FSearchItems.Count-1 do begin
SearchItem:=THelpDBItem(FSearchItems[i]);
if not (SearchItem is THelpDBISourceFile) then continue;
FileItem:=THelpDBISourceFile(SearchItem);
//debugln('THelpDatabase.GetNodesForPascalContexts B FileItem.ClassName=',FileItem.ClassName,' Filename=',Filename,' FileItem.GetFullFilename="',FileItem.GetFullFilename,'"');
if (FileItem.FileMatches(Filename)) then begin
CreateNodeQueryListAndAdd(FileItem.Node,PascalContext,ListOfNodes,true);
{$IFNDEF DisableChecks}
debugln(['THelpDatabase.GetNodesForPascalContexts C ID="',ID,'" ',i+1,'/',ListOfPascalHelpContextList.Count,' FileItem.ClassName=',FileItem.ClassName,' Filename=',Filename,' ',ListOfNodes.Count]);
{$ENDIF}
end;
end;
end;
end;
end;
end;
function THelpDatabase.GetNodesForClass(AClass: TClass;
var ListOfNodes: THelpNodeQueryList; var ErrMsg: string): TShowHelpResult;
// if ListOfNodes<>nil new nodes will be appended
// if ListOfNodes=nil and nodes exists a new list will be created
var
i: Integer;
SearchItem: THelpDBItem;
begin
Result:=shrSuccess;
ErrMsg:='';
if csDesigning in ComponentState then exit;
// add the registered nodes
if FSearchItems<>nil then begin
for i:=0 to FSearchItems.Count-1 do begin
SearchItem:=THelpDBItem(FSearchItems[i]);
if not (SearchItem is THelpDBIClass) then continue;
if THelpDBIClass(SearchItem).TheClass<>AClass then continue;
CreateNodeQueryListAndAdd(SearchItem.Node,nil,ListOfNodes,true);
end;
end;
end;
function THelpDatabase.GetNodesForMessage(const AMessage: string;
MessageParts: TStrings; var ListOfNodes: THelpNodeQueryList;
var ErrMsg: string): TShowHelpResult;
// if ListOfNodes<>nil new nodes will be appended
// if ListOfNodes=nil and nodes exists a new list will be created
var
i: Integer;
SearchItem: THelpDBItem;
begin
Result:=shrSuccess;
ErrMsg:='';
if csDesigning in ComponentState then exit;
// add the registered nodes
if FSearchItems<>nil then begin
for i:=0 to FSearchItems.Count-1 do begin
SearchItem:=THelpDBItem(FSearchItems[i]);
if not (SearchItem is THelpDBIMessage) then continue;
if not THelpDBIMessage(SearchItem).MessageMatches(AMessage,MessageParts)
then continue;
CreateNodeQueryListAndAdd(SearchItem.Node,nil,ListOfNodes,true);
end;
end;
end;
function THelpDatabase.FindViewer(const MimeType: string; var ErrMsg: string;
out Viewer: THelpViewer): TShowHelpResult;
var
Viewers: TList;
begin
Viewer:=nil;
if Assigned(OnFindViewer) then begin
Result:=OnFindViewer(Self,MimeType,ErrMsg,Viewer);
exit;
end;
Viewers:=HelpViewers.GetViewersSupportingMimeType(MimeType);
try
if (Viewers=nil) or (Viewers.Count=0) then begin
ErrMsg:=Format(rsHelpHelpDatabaseDidNotFoundAViewerForAHelpPageOfType,
[ID, MimeType]);
Result:=shrViewerNotFound;
end else begin
Viewer:=THelpViewer(Viewers[0]);
Result:=shrSuccess;
end;
finally
Viewers.Free;
end;
end;
procedure THelpDatabase.RegisterItem(NewItem: THelpDBItem);
begin
if NewItem=nil then
raise EHelpSystemException.Create('THelpDatabase.RegisterItem NewItem=nil');
if FSearchItems=nil then FSearchItems:=TFPList.Create;
if FSearchItems.IndexOf(NewItem)<0 then
FSearchItems.Add(NewItem)
else
NewItem.Free;
end;
procedure THelpDatabase.RegisterItemWithNode(Node: THelpNode);
begin
if Node=nil then
raise EHelpSystemException.Create('THelpDatabase.RegisterItemWithNode Node=nil');
RegisterItem(THelpDBItem.Create(Node));
end;
procedure THelpDatabase.RegisterFileItemWithNode(const Filename: string;
Node: THelpNode);
begin
RegisterItem(THelpDBISourceFile.Create(Node,Filename));
end;
procedure THelpDatabase.UnregisterItem(AnItem: THelpDBItem);
begin
if FSearchItems=nil then exit;
FSearchItems.Remove(AnItem);
end;
procedure THelpDatabase.UnregisterAllItems;
var
i: Integer;
begin
if FSearchItems=nil then exit;
for i:=0 to FSearchItems.Count-1 do
TObject(FSearchItems[i]).Free;
FSearchItems.Clear;
end;
function THelpDatabase.RegisteredItemCount: integer;
begin
if FSearchItems=nil then
Result:=0
else
Result:=FSearchItems.Count;
end;
function THelpDatabase.GetRegisteredItem(Index: integer): THelpDBItem;
begin
Result:=THelpDBItem(FSearchItems[Index]);
end;
procedure THelpDatabase.Load(Storage: TConfigStorage);
begin
end;
procedure THelpDatabase.Save(Storage: TConfigStorage);
begin
end;
function THelpDatabase.GetLocalizedName: string;
begin
Result:=ID;
end;
procedure THelpDatabase.Reference;
begin
inc(FRefCount);
end;
procedure THelpDatabase.Release;
begin
if FRefCount=0 then
raise EHelpSystemException.Create('THelpDatabase.Release');
dec(FRefCount);
if FRefCount=0 then Free;
end;
{ THelpDatabases }
function THelpDatabases.GetItems(Index: integer): THelpDatabase;
begin
Result:=THelpDatabase(FItems[Index]);
end;
procedure THelpDatabases.DoRegisterDatabase(ADatabase: THelpDatabase);
begin
ADatabase.Reference;
if FItems=nil then FItems:=TFPList.Create;
FItems.Add(ADatabase);
end;
procedure THelpDatabases.DoUnregisterDatabase(ADatabase: THelpDatabase);
begin
if FItems<>nil then
FItems.Remove(ADatabase);
ADatabase.Release;
end;
constructor THelpDatabases.Create;
begin
end;
destructor THelpDatabases.Destroy;
begin
while (Count>0) do Items[Count-1].UnregisterSelf;
FItems.Free;
FHelpDBClasses.Free;
inherited Destroy;
end;
function THelpDatabases.Count: integer;
begin
if FItems=nil then
Result:=0
else
Result:=FItems.Count;
end;
function THelpDatabases.FindDatabase(ID: THelpDatabaseID): THelpDatabase;
var
Index: LongInt;
begin
Index:=IndexOf(ID);
if Index>=0 then
Result:=Items[Index]
else
Result:=nil;
end;
function THelpDatabases.GetDatabase(ID: THelpDatabaseID; var HelpDB: THelpDatabase;
var HelpResult: TShowHelpResult; var ErrMsg: string): boolean;
begin
HelpDB:=FindDatabase(ID);
if HelpDB=nil then begin
Result:=false;
HelpResult:=shrDatabaseNotFound;
ErrMsg:=Format(rsHelpHelpDatabaseNotFound, [ID]);
end else begin
HelpResult:=shrSuccess;
Result:=true;
ErrMsg:='';
end;
end;
function THelpDatabases.IndexOf(ID: THelpDatabaseID): integer;
begin
Result:=Count-1;
while (Result>=0) and (AnsiCompareText(ID,Items[Result].ID)<>0) do
dec(Result);
end;
function THelpDatabases.CreateUniqueDatabaseID(
const WishID: string): THelpDatabaseID;
var
i: Integer;
begin
if (WishID<>'') and (FindDatabase(WishID)=nil) then begin
Result:=WishID;
end else begin
i:=1;
repeat
Result:=WishID+IntToStr(i);
if FindDatabase(Result)=nil then exit;
inc(i);
until false;
end;
end;
function THelpDatabases.CreateHelpDatabase(const WishID: string;
HelpDataBaseClass: THelpDatabaseClass; AutoRegister: boolean): THelpDatabase;
begin
Result:=HelpDataBaseClass.Create(nil);
Result.FID:=CreateUniqueDatabaseID(WishID);
if AutoRegister then Result.RegisterSelf;
end;
function THelpDatabases.ShowTableOfContents(var ErrMsg: string
): TShowHelpResult;
begin
Result:=shrHelpNotFound;
ErrMsg:='THelpDatabases.ShowTableOfContents not implemented';
// ToDo
end;
procedure THelpDatabases.ShowError(ShowResult: TShowHelpResult;
const ErrMsg: string);
var
ErrorCaption: String;
begin
case ShowResult of
shrNone: ErrorCaption:=rsHelpError;
shrSuccess: exit;
shrCancel: exit;
shrDatabaseNotFound: ErrorCaption:=rsHelpDatabaseNotFound;
shrContextNotFound: ErrorCaption:=rsHelpContextNotFound;
shrViewerNotFound: ErrorCaption:=rsHelpViewerNotFound;
shrHelpNotFound: ErrorCaption:=rsHelpNotFound;
shrViewerError: ErrorCaption:=rsHelpViewerError;
shrSelectorError: ErrorCaption:=rsHelpSelectorError;
else ErrorCaption:=rsUnknownErrorPleaseReportThisBug;
end;
MessageDlg(ErrorCaption,ErrMsg,mtError,[mbCancel],0);
end;
function THelpDatabases.GetBaseURLForBasePathObject(BasePathObject: TObject
): string;
begin
// this method will be overriden by the IDE
// provide some useful defaults:
if (BasePathObject is THelpBaseURLObject) then begin
Result:=THelpBaseURLObject(BasePathObject).BaseURL;
end else begin
// otherwise fetch a filename
Result:=GetBaseDirectoryForBasePathObject(BasePathObject);
if Result='' then exit;
Result:=FilenameToURL(Result);
end;
Result:=AppendURLPathDelim(Result);
end;
function THelpDatabases.GetBaseDirectoryForBasePathObject(
BasePathObject: TObject): string;
// returns the base file directory of the BasePathObject
begin
if (BasePathObject is THelpBaseURLObject) then begin
Result:=THelpBaseURLObject(BasePathObject).BaseURL;
if Result='' then exit;
if not IsFileURL(Result) then begin
Result:='';
exit;
end;
Result:=ExtractURLPath(Result);
end else if (BasePathObject is THelpBasePathObject) then
Result:=THelpBasePathObject(BasePathObject).BasePath
else
Result:='';
Result:=AppendURLPathDelim(Result);
end;
function THelpDatabases.FindViewer(const MimeType: string; var ErrMsg: string;
var Viewer: THelpViewer): TShowHelpResult;
var
Viewers: TList;
begin
Viewer:=nil;
Viewers:=HelpViewers.GetViewersSupportingMimeType(MimeType);
try
if (Viewers=nil) or (Viewers.Count=0) then begin
ErrMsg:=Format(rsHelpThereIsNoViewerForHelpType, [MimeType]);
Result:=shrViewerNotFound;
end else begin
Viewer:=THelpViewer(Viewers[0]);
Result:=shrSuccess;
end;
finally
Viewers.Free;
end;
end;
function THelpDatabases.SubstituteMacros(var s: string): boolean;
begin
Result:=true;
end;
function THelpDatabases.ShowHelpForNodes(Query: THelpQuery;
Nodes: THelpNodeQueryList; var ErrMsg: string): TShowHelpResult;
var
NodeQuery: THelpNodeQuery;
Node: THelpNode;
begin
// check if several nodes found
//debugln('THelpDatabases.ShowHelpForNodes A Nodes.Count=',dbgs(Nodes.Count));
NodeQuery:=nil;
if (Nodes.Count>1) then begin
Result:=ShowHelpSelector(Query,Nodes,ErrMsg,NodeQuery);
if Result<>shrSuccess then exit;
if NodeQuery=nil then exit;
end else begin
NodeQuery:=Nodes[0];
end;
// show node
Node:=NodeQuery.Node;
if Node.Owner=nil then begin
Result:=shrDatabaseNotFound;
ErrMsg:=Format(rsHelpHelpNodeHasNoHelpDatabase, [Node.Title]);
exit;
end;
{$IFDEF VerboseLCLHelp}
debugln(['THelpDatabases.ShowHelpForNodes Node.Owner=',DbgSName(Node.Owner),' UnitName=',Node.Owner.UnitName]);
{$ENDIF}
Result:=Node.Owner.ShowHelp(Query,nil,Node,NodeQuery.QueryItem,ErrMsg);
end;
function THelpDatabases.ShowHelpForQuery(Query: THelpQuery;
AutoFreeQuery: boolean; var ErrMsg: string): TShowHelpResult;
begin
try
// descendants first
if Query is THelpQueryPascalContexts then
Result:=ShowHelpForPascalContexts(THelpQueryPascalContexts(Query),ErrMsg)
else if Query is THelpQueryTOC then
Result:=ShowTableOfContents(ErrMsg)
else if Query is THelpQueryContext then
Result:=ShowHelpForContext(THelpQueryContext(Query),ErrMsg)
else if Query is THelpQueryKeyword then
Result:=ShowHelpForKeyword(THelpQueryKeyword(Query),ErrMsg)
else if Query is THelpQueryDirective then
Result:=ShowHelpForDirective(THelpQueryDirective(Query),ErrMsg)
else if Query is THelpQuerySourcePosition then
Result:=ShowHelpForSourcePosition(THelpQuerySourcePosition(Query),ErrMsg)
else if Query is THelpQueryMessage then
Result:=ShowHelpForMessageLine(THelpQueryMessage(Query),ErrMsg)
else if Query is THelpQueryClass then
Result:=ShowHelpForClass(THelpQueryClass(Query),ErrMsg)
else
Result:=shrContextNotFound;
finally
if AutoFreeQuery then Query.Free;
end;
end;
function THelpDatabases.ShowHelpForContext(Query: THelpQueryContext;
var ErrMsg: string): TShowHelpResult;
var
Nodes: THelpNodeQueryList;
HelpDB: THelpDatabase;
begin
ErrMsg:='';
Result:=shrHelpNotFound;
// search node
Nodes:=nil;
try
if Query.HelpDatabaseID<>'' then begin
HelpDB:=nil;
if not GetDatabase(Query.HelpDatabaseID,HelpDB,Result,ErrMsg) then exit;
Result:=HelpDB.GetNodesForContext(Query.Context,Nodes,ErrMsg);
if Result<>shrSuccess then exit;
end else begin
Result:=GetNodesForContext(Query.Context,Nodes,ErrMsg);
if Result<>shrSuccess then exit;
end;
// check if at least one node found
if (Nodes=nil) or (Nodes.Count=0) then begin
Result:=shrContextNotFound;
if Query.HelpDatabaseID<>'' then
ErrMsg:=Format(rsHelpHelpContextNotFoundInDatabase,
[IntToStr(Query.Context), Query.HelpDatabaseID])
else
ErrMsg:=Format(rsHelpHelpContextNotFound, [IntToStr(Query.Context)]);
exit;
end;
Result:=ShowHelpForNodes(Query,Nodes,ErrMsg);
finally
Nodes.Free;
end;
end;
function THelpDatabases.ShowHelpForKeyword(Query: THelpQueryKeyword;
var ErrMsg: string): TShowHelpResult;
var
Nodes: THelpNodeQueryList;
HelpDB: THelpDatabase;
begin
ErrMsg:='';
Result:=shrHelpNotFound;
// search node
Nodes:=nil;
try
if Query.HelpDatabaseID<>'' then begin
HelpDB:=nil;
if not GetDatabase(Query.HelpDatabaseID,HelpDB,Result,ErrMsg) then exit;
Result:=HelpDB.GetNodesForKeyword(Query.Keyword,Nodes,ErrMsg);
if Result<>shrSuccess then exit;
end else begin
Result:=GetNodesForKeyword(Query.Keyword,Nodes,ErrMsg);
if Result<>shrSuccess then exit;
end;
// check if at least one node found
if (Nodes=nil) or (Nodes.Count=0) then begin
Result:=shrContextNotFound;
if Query.HelpDatabaseID<>'' then
ErrMsg:=Format(rsHelpHelpKeywordNotFoundInDatabase, [Query.Keyword, Query.HelpDatabaseID])
else
ErrMsg:=Format(rsHelpHelpKeywordNotFound, [Query.Keyword]);
exit;
end;
Result:=ShowHelpForNodes(Query,Nodes,ErrMsg);
finally
Nodes.Free;
end;
end;
function THelpDatabases.ShowHelpForDirective(Query: THelpQueryDirective;
var ErrMsg: string): TShowHelpResult;
var
Nodes: THelpNodeQueryList;
HelpDB: THelpDatabase;
begin
ErrMsg:='';
Result:=shrHelpNotFound;
// search node
Nodes:=nil;
try
if Query.HelpDatabaseID<>'' then begin
HelpDB:=nil;
if not GetDatabase(Query.HelpDatabaseID,HelpDB,Result,ErrMsg) then exit;
Result:=HelpDB.GetNodesForKeyword(Query.Directive,Nodes,ErrMsg);
if Result<>shrSuccess then exit;
end else begin
Result:=GetNodesForDirective(Query.Directive,Nodes,ErrMsg);
if Result<>shrSuccess then exit;
end;
// check if at least one node found
if (Nodes=nil) or (Nodes.Count=0) then begin
Result:=shrContextNotFound;
if Query.HelpDatabaseID<>'' then
ErrMsg:=Format(rsHelpHelpForDirectiveNotFoundInDatabase, [Query.Directive, Query.HelpDatabaseID])
else
ErrMsg:=Format(rsHelpHelpForDirectiveNotFound, [Query.Directive]);
exit;
end;
Result:=ShowHelpForNodes(Query,Nodes,ErrMsg);
finally
Nodes.Free;
end;
end;
function THelpDatabases.ShowHelpForPascalContexts(
Query: THelpQueryPascalContexts; var ErrMsg: string): TShowHelpResult;
var
Nodes: THelpNodeQueryList;
begin
ErrMsg:='';
Result:=shrSuccess;
{$IFDEF VerboseLCLHelp}
debugln('THelpDatabases.ShowHelpForPascalContexts A Count=',dbgs(Query.ListOfPascalHelpContextList.Count));
{$ENDIF}
// search node
Nodes:=nil;
try
Result:=GetNodesForPascalContexts(Query.ListOfPascalHelpContextList,Nodes,
ErrMsg);
if Result<>shrSuccess then exit;
// check if at least one node found
if (Nodes=nil) or (Nodes.Count=0) then begin
Result:=shrHelpNotFound;
ErrMsg:=format(rsHelpNoHelpFoundForSource,
[Query.SourcePosition.y, Query.SourcePosition.x, Query.Filename]);
exit;
end;
{$IFDEF VerboseLCLHelp}
debugln('THelpDatabases.ShowHelpForPascalContexts B Nodes.Count=',dbgs(Nodes.Count));
{$ENDIF}
Result:=ShowHelpForNodes(Query,Nodes,ErrMsg);
finally
Nodes.Free;
end;
end;
function THelpDatabases.ShowHelpForSourcePosition(
Query: THelpQuerySourcePosition; var ErrMsg: string): TShowHelpResult;
begin
Result:=shrHelpNotFound;
ErrMsg:='THelpDatabases.ShowHelpForPascalSource not implemented';
end;
function THelpDatabases.ShowHelpForMessageLine(Query: THelpQueryMessage;
var ErrMsg: string): TShowHelpResult;
var
Nodes: THelpNodeQueryList;
begin
ErrMsg:='';
Result:=shrSuccess;
{$IFDEF VerboseLCLHelp}
debugln('THelpDatabases.ShowHelpForMessageLine A Msg="',Query.WholeMessage,'"');
{$ENDIF}
// search node
Nodes:=nil;
try
Result:=GetNodesForMessage(Query.WholeMessage,Query.MessageParts,Nodes,
ErrMsg);
if Result<>shrSuccess then exit;
// check if at least one node found
if (Nodes=nil) or (Nodes.Count=0) then begin
Result:=shrHelpNotFound;
ErrMsg:='No help found for "'+Query.WholeMessage+'"';
exit;
end;
Result:=ShowHelpForNodes(Query,Nodes,ErrMsg);
finally
Nodes.Free;
end;
end;
function THelpDatabases.ShowHelpForClass(Query: THelpQueryClass;
var ErrMsg: string): TShowHelpResult;
var
Nodes: THelpNodeQueryList;
begin
ErrMsg:='';
Result:=shrSuccess;
{$IFDEF VerboseLCLHelp}
debugln('THelpDatabases.ShowHelpForClass A ',Query.TheClass.ClassName);
{$ENDIF}
// search node
Nodes:=nil;
try
Result:=GetNodesForClass(Query.TheClass,Nodes,ErrMsg);
if Result<>shrSuccess then exit;
// check if at least one node found
if (Nodes=nil) or (Nodes.Count=0) then begin
// no node found for the class is not a bug
Result:=shrSuccess;
ErrMsg:='';
exit;
end;
Result:=ShowHelpForNodes(Query,Nodes,ErrMsg);
finally
Nodes.Free;
end;
end;
function THelpDatabases.ShowHelpFile(const Filename, Title, MimeType: string;
var ErrMsg: string): TShowHelpResult;
begin
Result:=ShowHelp(FilenameToURL(Filename),Title,MimeType,ErrMsg);
end;
function THelpDatabases.ShowHelp(const URL, Title, MimeType: string;
var ErrMsg: string): TShowHelpResult;
var
Viewer: THelpViewer;
Node: THelpNode;
begin
ErrMsg:='';
// get a viewer for this file
Result:=FindViewer(MimeType,ErrMsg,Viewer);
if Result<>shrSuccess then exit;
// call viewer
Node:=nil;
try
Node:=THelpNode.CreateURL(nil,Title,URL);
Result:=Viewer.ShowNode(Node,ErrMsg);
finally
Node.Free;
end;
end;
function THelpDatabases.GetNodesForKeyword(const HelpKeyword: string;
var ListOfNodes: THelpNodeQueryList; var ErrMsg: string): TShowHelpResult;
// if ListOfNodes<>nil then new nodes will be appended
// if ListOfNodes=nil and nodes exists a new list will be created
var
i: Integer;
begin
ErrMsg:='';
for i:=Count-1 downto 0 do begin
Result:=Items[i].GetNodesForKeyword(HelpKeyword,ListOfNodes,ErrMsg);
if Result=shrCancel then exit;
end;
Result:=shrSuccess;
end;
function THelpDatabases.GetNodesForDirective(const HelpDirective: string;
var ListOfNodes: THelpNodeQueryList; var ErrMsg: string): TShowHelpResult;
// if ListOfNodes<>nil then new nodes will be appended
// if ListOfNodes=nil and nodes exists a new list will be created
var
i: Integer;
begin
ErrMsg:='';
for i:=Count-1 downto 0 do begin
Result:=Items[i].GetNodesForDirective(HelpDirective,ListOfNodes,ErrMsg);
if Result=shrCancel then exit;
end;
Result:=shrSuccess;
end;
function THelpDatabases.GetNodesForContext(HelpContext: THelpContext;
var ListOfNodes: THelpNodeQueryList; var ErrMsg: string): TShowHelpResult;
// if ListOfNodes<>nil then new nodes will be appended
// if ListOfNodes=nil and nodes exists a new list will be created
var
i: Integer;
begin
ErrMsg:='';
for i:=Count-1 downto 0 do begin
Result:=Items[i].GetNodesForContext(HelpContext,ListOfNodes,ErrMsg);
if Result=shrCancel then exit;
end;
Result:=shrSuccess;
end;
function THelpDatabases.GetNodesForPascalContexts(
ListOfPascalHelpContextList: TList; var ListOfNodes: THelpNodeQueryList;
var ErrMsg: string): TShowHelpResult;
// if ListOfNodes<>nil then new nodes will be appended
// if ListOfNodes=nil and nodes exists a new list will be created
var
i: Integer;
begin
ErrMsg:='';
for i:=Count-1 downto 0 do begin
Result:=Items[i].GetNodesForPascalContexts(ListOfPascalHelpContextList,
ListOfNodes,ErrMsg);
if Result=shrCancel then exit;
end;
Result:=shrSuccess;
end;
function THelpDatabases.GetNodesForClass(AClass: TClass;
var ListOfNodes: THelpNodeQueryList; var ErrMsg: string): TShowHelpResult;
// if ListOfNodes<>nil then new nodes will be appended
// if ListOfNodes=nil and nodes exists a new list will be created
var
i: Integer;
begin
ErrMsg:='';
for i:=Count-1 downto 0 do begin
Result:=Items[i].GetNodesForClass(AClass,ListOfNodes,ErrMsg);
if Result=shrCancel then exit;
end;
Result:=shrSuccess;
end;
function THelpDatabases.GetNodesForMessage(const AMessage: string;
MessageParts: TStrings; var ListOfNodes: THelpNodeQueryList;
var ErrMsg: string): TShowHelpResult;
// if ListOfNodes<>nil then new nodes will be appended
// if ListOfNodes=nil and nodes exists a new list will be created
var
i: Integer;
begin
ErrMsg:='';
for i:=Count-1 downto 0 do begin
Result:=Items[i].GetNodesForMessage(AMessage,MessageParts,ListOfNodes,
ErrMsg);
if Result=shrCancel then exit;
end;
Result:=shrSuccess;
end;
function THelpDatabases.ShowHelpSelector(Query: THelpQuery;
Nodes: THelpNodeQueryList; var ErrMsg: string;
var Selection: THelpNodeQuery): TShowHelpResult;
// to override
// default is to always take the first node
begin
if (Nodes=nil) or (Nodes.Count=0) then begin
Result:=shrSelectorError;
Selection:=nil;
ErrMsg:=rsHelpNoHelpNodesAvailable;
end else begin
Selection:=THelpNodeQuery(Nodes[0]);
Result:=shrSuccess;
ErrMsg:='';
end;
end;
procedure THelpDatabases.RegisterHelpDatabaseClass(NewHelpDB: THelpDatabaseClass);
begin
if FHelpDBClasses=nil then FHelpDBClasses:=TFPList.Create;
if FHelpDBClasses.IndexOf(NewHelpDB)<0 then
FHelpDBClasses.Add(NewHelpDB);
end;
procedure THelpDatabases.UnregisterHelpDatabaseClass(
AHelpDB: THelpDatabaseClass);
begin
if FHelpDBClasses=nil then exit;
FHelpDBClasses.Remove(AHelpDB);
end;
function THelpDatabases.HelpDatabaseClassCount: integer;
begin
if FHelpDBClasses=nil then
Result:=0
else
Result:=FHelpDBClasses.Count;
end;
function THelpDatabases.GetHelpDatabaseClass(Index: integer
): THelpDatabaseClass;
begin
Result:=THelpDatabaseClass(FHelpDBClasses[Index]);
end;
procedure THelpDatabases.Load(Storage: TConfigStorage);
var
i: Integer;
HelpDB: THelpDatabase;
Path: String;
begin
for i:=0 to Count-1 do begin
HelpDB:=Items[i];
Path:=HelpDB.ID;
if not IsValidIdent(Path) then continue;
Storage.AppendBasePath(Path);
try
HelpDB.Load(Storage);
finally
Storage.UndoAppendBasePath;
end;
end;
end;
procedure THelpDatabases.Save(Storage: TConfigStorage);
var
i: Integer;
HelpDB: THelpDatabase;
Path: String;
begin
for i:=0 to Count-1 do begin
HelpDB:=Items[i];
Path:=HelpDB.ID;
if not IsValidIdent(Path) then continue;
Storage.AppendBasePath(Path);
try
HelpDB.Save(Storage);
finally
Storage.UndoAppendBasePath;
end;
end;
end;
{ THelpViewers }
function THelpViewers.GetItems(Index: integer): THelpViewer;
begin
Result:=THelpViewer(FItems[Index]);
end;
constructor THelpViewers.Create;
begin
FItems:=TFPList.Create;
end;
destructor THelpViewers.Destroy;
begin
FDestroying:=true;
Clear;
FreeAndNil(fItems);
inherited Destroy;
end;
procedure THelpViewers.Clear;
var
i: Integer;
begin
i:=Count-1;
while (i>=0) do begin
if i<Count then begin
if Items[i].Owner=nil then begin
Items[i].Free;
if fItems=nil then exit;
end;
if i<Count then
FItems[i]:=nil;
end;
dec(i);
end;
FItems.Clear;
end;
function THelpViewers.Count: integer;
begin
if fItems<>nil then
Result:=FItems.Count
else
Result:=0;
end;
function THelpViewers.GetViewersSupportingMimeType(
const MimeType: string): TList;
var
i: Integer;
begin
Result:=nil;
// LIFO: last registered, first shown
for i:=Count-1 downto 0 do
if Items[i].SupportsMimeType(MimeType) then begin
if Result=nil then Result:=TList.Create;
Result.Add(Items[i]);
end;
end;
procedure THelpViewers.RegisterViewer(AHelpViewer: THelpViewer);
begin
FItems.Add(AHelpViewer);
end;
procedure THelpViewers.UnregisterViewer(AHelpViewer: THelpViewer);
begin
if FDestroying then exit;
FItems.Remove(AHelpViewer);
end;
procedure THelpViewers.Load(Storage: TConfigStorage);
var
i: Integer;
Viewer: THelpViewer;
Path: String;
begin
for i:=0 to Count-1 do begin
Viewer:=Items[i];
Path:=Viewer.StorageName;
if not IsValidIdent(Path) then continue;
Storage.AppendBasePath(Path);
try
Viewer.Load(Storage);
finally
Storage.UndoAppendBasePath;
end;
end;
end;
procedure THelpViewers.Save(Storage: TConfigStorage);
var
i: Integer;
Viewer: THelpViewer;
Path: String;
begin
for i:=0 to Count-1 do begin
Viewer:=Items[i];
Path:=Viewer.StorageName;
if not IsValidIdent(Path) then continue;
Storage.AppendBasePath(Path);
try
Viewer.Save(Storage);
finally
Storage.UndoAppendBasePath;
end;
end;
end;
function THelpViewers.IndexOf(AHelpViewer: THelpViewer): integer;
begin
Result:=FItems.IndexOf(AHelpViewer);
end;
{ THelpViewer }
procedure THelpViewer.SetAutoRegister(const AValue: boolean);
begin
if FAutoRegister=AValue then exit;
FAutoRegister:=AValue;
if not (csDesigning in ComponentState) then begin
if FAutoRegister then begin
RegisterSelf;
end else begin
UnregisterSelf;
end;
end;
end;
procedure THelpViewer.SetSupportedMimeTypes(List: TStrings);
begin
if FSupportedMimeTypes<>nil then FSupportedMimeTypes.Free;
FSupportedMimeTypes:=nil;
end;
procedure THelpViewer.AddSupportedMimeType(const AMimeType: string);
begin
if FSupportedMimeTypes=nil then FSupportedMimeTypes:=TStringList.Create;
FSupportedMimeTypes.Add(AMimeType);
end;
constructor THelpViewer.Create(TheOwner: TComponent);
begin
inherited Create(TheOwner);
FStorageName:=ClassName;
end;
destructor THelpViewer.Destroy;
begin
UnregisterSelf;
FreeAndNil(FSupportedMimeTypes);
inherited Destroy;
end;
function THelpViewer.SupportsTableOfContents: boolean;
begin
Result:=false;
end;
procedure THelpViewer.ShowTableOfContents(Node: THelpNode);
begin
raise EHelpSystemException.Create('THelpViewer.ShowTableOfContents not implemented');
end;
function THelpViewer.SupportsMimeType(const AMimeType: string): boolean;
begin
Result:=false;
if FSupportedMimeTypes<>nil then
Result:=(FSupportedMimeTypes.IndexOf(AMimeType)>=0);
end;
function THelpViewer.ShowNode(Node: THelpNode; var ErrMsg: string
): TShowHelpResult;
begin
// for descendents to override
Result:=shrViewerError;
ErrMsg:='THelpViewer.ShowNode not implemented for this help type';
end;
procedure THelpViewer.Hide;
begin
// override this
end;
procedure THelpViewer.Assign(Source: TPersistent);
begin
if Source is THelpViewer then begin
end;
inherited Assign(Source);
end;
procedure THelpViewer.Load(Storage: TConfigStorage);
begin
end;
procedure THelpViewer.Save(Storage: TConfigStorage);
begin
end;
function THelpViewer.GetLocalizedName: string;
begin
Result:=StorageName;
end;
procedure THelpViewer.RegisterSelf;
begin
if (HelpViewers<>nil) and (HelpViewers.IndexOf(Self)>=0) then
raise EHelpSystemException.Create('help viewer is already registered');
CreateLCLHelpSystem;
HelpViewers.RegisterViewer(Self);
end;
procedure THelpViewer.UnregisterSelf;
begin
if (HelpViewers=nil) or (HelpViewers.IndexOf(Self)<0) then exit;
HelpViewers.UnregisterViewer(Self);
FreeUnusedLCLHelpSystem;
end;
{ THelpNode }
constructor THelpNode.Create(TheOwner: THelpDatabase; Node: THelpNode);
begin
FOwner:=TheOwner;
Assign(Node);
end;
constructor THelpNode.Create(TheOwner: THelpDatabase; const TheTitle,
TheURL, TheID: string; TheContext: THelpContext);
begin
FOwner:=TheOwner;
FHelpType:=hntURLIDContext;
FTitle:=TheTitle;
FURL:=TheURL;
FID:=TheID;
FContext:=TheContext;
end;
constructor THelpNode.CreateURL(TheOwner: THelpDatabase; const TheTitle,
TheURL: string);
begin
FOwner:=TheOwner;
FHelpType:=hntURL;
FTitle:=TheTitle;
FURL:=TheURL;
end;
constructor THelpNode.CreateID(TheOwner: THelpDatabase;
const TheTitle, TheID: string);
begin
FOwner:=TheOwner;
FHelpType:=hntID;
FTitle:=TheTitle;
FID:=TheID;
end;
constructor THelpNode.CreateURLID(TheOwner: THelpDatabase;
const TheTitle, TheURL, TheID: string);
begin
FOwner:=TheOwner;
FHelpType:=hntURLID;
FTitle:=TheTitle;
FURL:=TheURL;
FID:=TheID;
end;
constructor THelpNode.CreateContext(TheOwner: THelpDatabase;
const TheTitle: string; TheContext: THelpContext);
begin
FOwner:=TheOwner;
FHelpType:=hntContext;
FTitle:=TheTitle;
FContext:=TheContext;
end;
constructor THelpNode.CreateURLContext(TheOwner: THelpDatabase; const TheTitle,
TheURL: string; TheContext: THelpContext);
begin
FOwner:=TheOwner;
FHelpType:=hntURLContext;
FTitle:=TheTitle;
FURL:=TheURL;
FContext:=TheContext;
end;
function THelpNode.URLValid: boolean;
begin
Result:=FHelpType in [hntURL,hntURLIDContext,hntURLID,hntURLContext];
end;
function THelpNode.IDValid: boolean;
begin
Result:=FHelpType in [hntURLIDContext,hntURLID,hntID];
end;
function THelpNode.ContextValid: boolean;
begin
Result:=FHelpType in [hntURLIDContext,hntURLContext,hntContext];
end;
function THelpNode.AsString: string;
begin
Result:=Title;
end;
procedure THelpNode.Assign(Source: TPersistent);
var
Node: THelpNode;
begin
if Source is THelpNode then begin
Node:=THelpNode(Source);
FHelpType:=Node.HelpType;
FTitle:=Node.Title;
FURL:=Node.URL;
FID:=Node.ID;
FContext:=Node.Context;
end else
inherited Assign(Source);
end;
{ THelpDBItem }
constructor THelpDBItem.Create(TheNode: THelpNode);
begin
Node:=TheNode
end;
destructor THelpDBItem.Destroy;
begin
Node.Free;
inherited Destroy;
end;
{ TPascalHelpContextList }
function TPascalHelpContextList.GetItems(Index: integer): TPascalHelpContext;
begin
Result:=fItems[Index];
end;
procedure TPascalHelpContextList.Add(const Context: TPascalHelpContext);
begin
inc(FCount);
ReAllocMem(fItems,SizeOf(TPascalHelpContext)*FCount);
// to prevent freeing uninitialized strings, initialize the new strings to nil
FillChar(fItems[FCount-1], SizeOf(TPascalHelpContext), 0);
fItems[FCount-1]:=Context;
end;
procedure TPascalHelpContextList.Add(Descriptor: TPascalHelpContextType;
const Context: string);
var
CurContext: TPascalHelpContext;
begin
CurContext.Descriptor:=Descriptor;
CurContext.Context:=Context;
Add(CurContext);
end;
procedure TPascalHelpContextList.Insert(Index: integer;
const Context: TPascalHelpContext);
begin
inc(FCount);
ReAllocMem(fItems,SizeOf(TPascalHelpContext)*FCount);
if Index<FCount-1 then
System.Move(fItems[Index],fItems[Index+1],
SizeOf(TPascalHelpContext)*(FCount-Index-1));
// to prevent freeing uninitialized strings, initialize the new strings to nil
FillChar(fItems[Index], SizeOf(TPascalHelpContext), 0);
fItems[Index]:=Context;
end;
procedure TPascalHelpContextList.Clear;
var
Index: Integer;
begin
// Set all item strings to '', so fpc will finalize them.
for Index := 0 to FCount-1 do
fItems[Index].Context := '';
ReAllocMem(fItems,0);
end;
destructor TPascalHelpContextList.Destroy;
begin
Clear;
inherited Destroy;
end;
function TPascalHelpContextList.IsEqual(QueryItem: THelpQueryItem): boolean;
begin
Result:=(QueryItem is TPascalHelpContextList)
and (CompareList(TPascalHelpContextList(QueryItem))=0);
end;
function TPascalHelpContextList.CompareList(AList: TPascalHelpContextList
): integer;
var
i: Integer;
begin
i:=0;
while (i<Count) and (i<AList.Count) do begin
if fItems[i].Descriptor<AList.fItems[i].Descriptor then begin
Result:=1;
exit;
end else if fItems[i].Descriptor>AList.fItems[i].Descriptor then begin
Result:=-1;
exit;
end else begin
Result:=CompareText(fItems[i].Context,AList.fItems[i].Context);
if Result<>0 then exit;
end;
inc(i);
end;
if Count>i then
Result:=-1
else
Result:=1;
end;
function TPascalHelpContextList.AsString: string;
var
i: Integer;
Item: TPascalHelpContext;
Filename: String;
begin
Result:='';
i:=0;
while (i<Count) and (Items[i].Descriptor=pihcFilename) do begin
Filename:=Items[i].Context;
inc(i);
end;
while i<Count do begin
Item:=Items[i];
case Item.Descriptor of
pihcFilename: Result:=Result+Item.Context;
pihcSourceName: ;
pihcProperty: Result:=Result+' property '+Item.Context;
pihcProcedure: Result:=Result+' procedure/function '+Item.Context;
pihcParameterList: Result:=Result+Item.Context;
pihcVariable: Result:=Result+' var '+Item.Context;
pihcType: Result:=Result+' type '+Item.Context;
pihcConst: Result:=Result+' const '+Item.Context;
end;
//DebugLn(['TPascalHelpContextList.AsString ',i,' ',Item.Descriptor,' ',Result]);
inc(i);
end;
if Filename<>'' then
Result:=Result+' in '+Filename;
end;
{ THelpDBISourceFile }
procedure THelpDBISourceFile.SetFilename(const AValue: string);
begin
FFilename:=AValue;
end;
constructor THelpDBISourceFile.Create(TheNode: THelpNode;
const TheFilename: string);
begin
inherited Create(TheNode);
FFilename:=TrimFilename(GetForcedPathDelims(TheFilename));
end;
function THelpDBISourceFile.FileMatches(const AFilename: string): boolean;
begin
if (FFilename<>'') and (AFilename<>'') then
Result:=CompareFilenames(GetFullFilename,AFilename)=0
else
Result:=false;
end;
function THelpDBISourceFile.GetFullFilename: string;
var
BaseDir: String;
ExpFilename: String;
begin
ExpFilename:=FFilename;
//DebugLn(['THelpDBISourceFile.GetFullFilename ExpFilename="',ExpFilename,'" HelpDatabases=',DbgSName(HelpDatabases)]);
if (HelpDatabases<>nil) then
HelpDatabases.SubstituteMacros(ExpFilename);
//DebugLn(['THelpDBISourceFile.GetFullFilename substituted ',ExpFilename]);
ExpFilename:=TrimFilename(GetForcedPathDelims(ExpFilename));
if FilenameIsAbsolute(ExpFilename) then
Result:=ExpFilename
else begin
BaseDir:=GetBasePath;
Result:=AppendPathDelim(BaseDir)+ExpFilename;
end;
end;
function THelpDBISourceFile.GetBasePath: string;
begin
if BasePathObject=nil then
Result:=''
else
Result:=AppendPathDelim(TrimFilename(GetForcedPathDelims(
HelpDatabases.GetBaseDirectoryForBasePathObject(BasePathObject))));
end;
{ THelpDBISourceDirectory }
constructor THelpDBISourceDirectory.Create(TheNode: THelpNode;
const Directory, TheFileMask: string; Recursive: boolean);
begin
inherited Create(TheNode,Directory);
FFileMask:=GetForcedPathDelims(TheFileMask);
WithSubDirectories:=Recursive;
end;
function THelpDBISourceDirectory.FileMatches(const AFilename: string
): boolean;
var
TheDirectory: String;
begin
Result:=false;
//debugln('THelpDBISourceDirectory.FileMatches AFilename="',AFilename,'" FFilename="',FFilename,'"');
if (FFilename='') or (AFilename='') then exit;
TheDirectory:=GetFullFilename;
if TheDirectory='' then begin
{$IFNDEF DisableChecks}
DebugLn(['WARNING: THelpDBISourceDirectory.FileMatches ',DbgSName(Self),' Filename="',Filename,'" -> ""']);
{$ENDIF}
exit;
end;
//debugln('THelpDBISourceDirectory.FileMatches TheDirectory="',TheDirectory,'" WithSubDirectories=',dbgs(WithSubDirectories));
if WithSubDirectories then begin
if not FileIsInPath(AFilename,TheDirectory) then exit;
end else begin
if not FileIsInDirectory(AFilename,TheDirectory) then exit;
end;
//debugln('THelpDBISourceDirectory.FileMatches FileMask="',FileMask,'"');
if (FileMask<>'')
and (not MatchesMaskList(ExtractFilename(AFilename),FileMask)) then exit;
//debugln('THelpDBISourceDirectory.FileMatches Success');
Result:=true;
end;
{ THelpQueryNode }
constructor THelpQueryNode.Create(const TheHelpDatabaseID: THelpDatabaseID;
const TheNode: THelpNode);
begin
inherited Create(TheHelpDatabaseID);
FNode:=TheNode;
end;
{ THelpBasePathObject }
procedure THelpBasePathObject.SetBasePath(const AValue: string);
begin
if FBasePath=AValue then exit;
FBasePath:=AValue;
end;
constructor THelpBasePathObject.Create;
begin
end;
constructor THelpBasePathObject.Create(const TheBasePath: string);
begin
BasePath:=TheBasePath;
end;
{ THelpNodeQuery }
constructor THelpNodeQuery.Create;
begin
end;
constructor THelpNodeQuery.Create(TheNode: THelpNode;
TheQueryItem: THelpQueryItem);
begin
Create;
FNode:=TheNode;
FQueryItem:=TheQueryItem;
end;
function THelpNodeQuery.IsEqual(TheNode: THelpNode; TheQueryItem: THelpQueryItem
): boolean;
begin
Result:=(Node=TheNode) and (QueryItem.IsEqual(TheQueryItem))
end;
function THelpNodeQuery.IsEqual(NodeQuery: THelpNodeQuery): boolean;
begin
Result:=IsEqual(NodeQuery.Node,NodeQuery.QueryItem)
end;
function THelpNodeQuery.AsString: string;
begin
Result:=Node.AsString;
if QueryItem<>nil then
Result:=Result+' ('+QueryItem.AsString+')';
end;
{ THelpNodeQueryList }
function THelpNodeQueryList.GetItems(Index: integer): THelpNodeQuery;
begin
Result:=THelpNodeQuery(fItems[Index]);
end;
procedure THelpNodeQueryList.SetItems(Index: integer;
const AValue: THelpNodeQuery);
begin
fItems[Index]:=AValue;
end;
constructor THelpNodeQueryList.Create;
begin
fItems:=TFPList.Create;
end;
destructor THelpNodeQueryList.Destroy;
begin
Clear;
fItems.Free;
inherited Destroy;
end;
function THelpNodeQueryList.Count: integer;
begin
Result:=fItems.Count;
end;
function THelpNodeQueryList.Add(NodeQuery: THelpNodeQuery): integer;
begin
Result:=fItems.Add(NodeQuery);
end;
function THelpNodeQueryList.Add(Node: THelpNode; QueryItem: THelpQueryItem
): integer;
begin
Result:=Add(THelpNodeQuery.Create(Node,QueryItem));
end;
procedure THelpNodeQueryList.Delete(Index: integer);
begin
TObject(fItems[Index]).Free;
fItems.Delete(Index);
end;
function THelpNodeQueryList.IndexOf(NodeQuery: THelpNodeQuery): integer;
begin
Result:=Count;
while (Result>=0) and (not Items[Result].IsEqual(NodeQuery)) do
dec(Result);
end;
function THelpNodeQueryList.IndexOf(Node: THelpNode; QueryItem: THelpQueryItem
): integer;
begin
Result:=Count-1;
while (Result>=0) and (not Items[Result].IsEqual(Node,QueryItem)) do
dec(Result);
end;
procedure THelpNodeQueryList.Clear;
var
i: Integer;
begin
for i:=0 to Count-1 do TObject(fItems[i]).Free;
fItems.Clear;
end;
{ THelpQueryItem }
function THelpQueryItem.IsEqual(QueryItem: THelpQueryItem): boolean;
begin
Result:=AsString=QueryItem.AsString;
end;
{ THelpBaseURLObject }
procedure THelpBaseURLObject.SetBaseURL(const AValue: string);
begin
if FBaseURL=AValue then exit;
FBaseURL:=AValue;
end;
constructor THelpBaseURLObject.Create;
begin
end;
constructor THelpBaseURLObject.Create(const TheBaseURL: string);
begin
BaseURL:=TheBaseURL;
end;
end.