mirror of
https://gitlab.com/freepascal.org/lazarus/lazarus.git
synced 2025-04-05 03:48:07 +02:00
2695 lines
77 KiB
ObjectPascal
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.
|
|
|