Recent

Author Topic: [SOLVED] Add Help to an Application, SQLite database  (Read 915 times)

1HuntnMan

  • Sr. Member
  • ****
  • Posts: 445
  • From Delphi 7 to Lazarus
    • NewFound Photo Art
[SOLVED] Add Help to an Application, SQLite database
« on: March 04, 2026, 11:35:17 pm »
Anyone experienced with designing help for your app.? I'm new to this and have been researching, anyone will to make a recommendation, that would be great.  I downloaded a document called ADD HELP TO YOUR APPLICATION and reading thru it, it discussed using CHM and HTML.  From reading I thought I'd attempt to use CHM.  I first attempted to load the lhelp CHM viewer, lazarus/components/chmhelp/democontrol/. Next I loaded the Chmmaker tool to write my CHM files. It's located at lazarus/tools/chmmaker.  Problem immediately is that it won't compile. The chmmaker.lpr requres a unit called unit1 and another, CHMsiteMapEditor.

Is there a better help design system since this doesn't appear to work?
« Last Edit: March 06, 2026, 08:44:49 pm by 1HuntnMan »

1HuntnMan

  • Sr. Member
  • ****
  • Posts: 445
  • From Delphi 7 to Lazarus
    • NewFound Photo Art
Re: Add Help to an Application, SQLite database
« Reply #1 on: March 04, 2026, 11:38:21 pm »
The document I downloaded, most of it addresses HTML help for you application.

cdbc

  • Hero Member
  • *****
  • Posts: 2716
    • http://www.cdbc.dk
Re: Add Help to an Application, SQLite database
« Reply #2 on: March 04, 2026, 11:56:01 pm »
Hi
Hmmm -- Roll your own  ;D
Take a look at how 'StaticTexts' are handled here in this model-unit:
Code: Pascal  [Select][+][-]
  1. unit model.main;
  2. {$mode ObjFPC}{$H+}
  3. {$WARN 6058 off : Call to subroutine "$1" marked as inline is not inlined}
  4. {-$define dbg}
  5. interface                                             /// locked 211225 /bc
  6. uses classes, sysutils, bufdataset, istrlist, model.decl, model.intf, cdbc.litedb;
  7.  
  8. const MinIStreamsVersion = 0290126  ; { ~ 0.29.01.2026 }
  9.  
  10. type
  11. {$Region 'TModelMainH'}
  12.   { TModelMain }
  13.   TModelMain = class(TObject,IModelMain)
  14.   private { remember to use 'RegisterSection' in units, you want to add to 'Sects' :o) }
  15.   const Sects: TStringArray = ('[view.main]'); { very necessary, because we need it at idx 0 }
  16.   var                                              
  17.     fInSection: boolean; { used while searching static text sections }
  18.     fSecId: integer; { tmp id while searching }
  19.     fSectMaxIdx: integer; { used while searching static text sections, limits to 1 section }
  20.     { we need a filter to compare against; NO filter (='') means ALL FILES ARE ALLOWED!!! }
  21.     function IsInFilter(aNode: PFSNode; aFilter: string): boolean;
  22.     function Obj: TObject;
  23.   protected
  24.     fDb: TLiteDb;
  25.     fLookUp: TBufDataset;
  26.     fPresenter: IPresenterMain;
  27.     fRoot: shortstring;
  28.     fSection: string; // for use in getting static texts
  29.     fTarget: integer; // for targetting the right result in view
  30.     fTextCache: IStringList;
  31.     { used while searching static text sections, new implementation ;) }
  32.     procedure DoEach(const aValue: string; const {%H-}anIdx: ptrint; {%H-}anObj: TObject; aData: pointer);
  33.     procedure CheckAndInitDb; //=^
  34.     function FetchDirNames: boolean;
  35.     function LeftWord(const aStr: string): string; // used while searching static text sections    
  36.   public
  37.     constructor Create(aPresenter: IPresenterMain;const aRoot: shortstring = ''); overload;
  38.     destructor Destroy; override;
  39.     { returns the ID of the 'aDirName', existing or new }
  40.     function AppendDirectory(const aDirName, aRootDir: string): ptrint; { returns the ID of the 'aDirName' }
  41.     function AppendFile(const aPath,aFilename: string;anID: ptrint): boolean; { uses the ID of the 'aDirName' }
  42.     function GetBackendVersion: string;
  43.     function GetStaticTexts(const aSection: string; out aTarget: integer): IStringList;
  44.     procedure RefreshSections;
  45.     procedure ReloadTextCache;
  46.     function UploadBlobDir(aDir: INodeList;aFilter: string;out aLogRes: string): ptrint; { returns -1 on error }
  47.   end;
  48.  
  49. {$EndRegion 'TModelMainH'}
  50.  
  51. { datastore factory, its intended use is in scenarios, where the 'viewmodel' / 'presenter'
  52.   does NOT OWN the 'model' and thus doesn't free it on end of use...,
  53.   in other words:
  54.     "if you create your 'model' with this factory-function then DON'T FREE IT!" just := nil }
  55. function gModelMain(aPresenter: IPresenterMain;const aRoot: shortstring): IModelMain;
  56.  
  57.  
  58. implementation
  59. uses common.consts, obs_prosu, strutils, db, istreams, model.base;  /// , baseunix  , dateutils,
  60.  
  61. const {$i model.sql.inc}
  62.  
  63. var Singleton: TModelMain = nil;
  64.  
  65. { ModelMain factory }
  66. function gModelMain(aPresenter: IPresenterMain; const aRoot: shortstring): IModelMain;
  67. begin
  68.   if not Assigned(Singleton) then Singleton:= TModelMain.Create(aPresenter,aRoot);
  69.   Result:= Singleton;
  70. end;
  71.  
  72. {$Region 'TModelMain'}
  73. {$Region 'boilerplate' -fold}
  74. { TModelMain }
  75. function TModelMain.IsInFilter(aNode: PFSNode; aFilter: string): boolean;
  76. var lext: string = '';
  77. begin
  78.   Result:= false;
  79.   if aNode = nil then exit;
  80.   if aNode^.fnType <> 0 then exit(false); { NO DIRS!!! 0 = file, 1 = directory }
  81.   if not aNode^.fnInclude then exit(false); { excluded beforehand!!! }
  82.   { we need a filter to compare against; NO filter means ALL FILES ARE ALLOWED!!! }
  83.   if aFilter <> '' then aFilter:= LowerCase(aFilter) else exit(true);
  84.   with aNode^ do begin
  85.     lext:= LowerCase(ExtractFileExt(fnFile)); { only file-extensions, can be empty ==> returns false!!! }
  86.     Result:= (pos(lext,aFilter) > 0);  { works like a goddamn charm \o/\ö/\o/ }
  87.   end;
  88. end;
  89.  
  90. function TModelMain.Obj: TObject;
  91. begin
  92.   Result:= Self;
  93. end;
  94.  
  95. procedure TModelMain.DoEach(const aValue: string; const anIdx: ptrint;
  96.                                anObj: TObject; aData: pointer);  
  97. var ls: string; lid: integer;
  98. begin
  99.   if fSecId = -1 then exit;
  100.   ls:= LeftWord(aValue);
  101.   lid:= IndexText(ls,Sects);
  102.   if lid = fSecId then begin
  103.     IStringList(aData).Append(aValue); //<- new feature <aData> typecast :o)
  104.     fInSection:= true;
  105.   end else begin
  106.     if fInSection then begin
  107.       if ((lid >= 0) and (lid <= fSectMaxIdx)) then fInSection:= false; /// 250824 /bc
  108.       if fInSection then IStringList(aData).Append(aValue); //<- new feature <aData>
  109.     end;
  110.   end;
  111. end;
  112.  
  113. procedure TModelMain.CheckAndInitDb; //=^
  114. var lbd: boolean = false; lbdn: boolean = false;
  115. begin
  116.   with fDb.QuickConnect do try { no param = start transaction }
  117.     Query.SQL.Text:= checkBlobTable; { consults "sqlite_schema" table }
  118.     Query.Open;
  119.     while not Query.EOF do begin
  120.       if Query.FieldByName('name').AsString = 'blobdata' then lbd:= true;
  121.       if Query.FieldByName('name').AsString = 'blobdirnames' then lbdn:= true;
  122.       Query.Next;
  123.     end;        
  124.     if not lbdn then begin
  125.       Exec.SQL.Text:= CreBlobDirNames; { 1.st setup master }
  126.       Exec.ExecSQL;
  127.       lbdn:= true;
  128.     end;
  129.     if not lbd then begin
  130.       Exec.SQL.Text:= CreBlobData;     { 2.nd setup detail }
  131.       Exec.ExecSQL;
  132.       lbd:= true;
  133.     end;
  134.   finally QuickDisConnect; end; { no param = commit transaction }
  135.   if lbdn and lbd then
  136.     fPresenter.Provider.NotifySubscribers(prStatus,nil,Str2Pch('(i) Datastore initialized OK'));
  137. end;
  138.  
  139. function TModelMain.FetchDirNames: boolean;
  140. begin
  141.   Result:= fDb.QuerySQL(SelBlobDirNames,fLookUp)
  142. end;
  143.  
  144. function TModelMain.LeftWord(const aStr: string): string;
  145. var li: integer = 1;
  146. begin { pick the left-most word in a string }
  147.   Result:= aStr;
  148.   if Result = '' then exit;
  149.   while ((li <= Length(aStr)) and (not (Result[li] in [#9,#13,#10,' ']))) do inc(li);
  150.   SetLength(Result,li-1);
  151. end;
  152. {$EndRegion 'boilerplate'}
  153.  
  154. constructor TModelMain.Create(aPresenter: IPresenterMain; const aRoot: shortstring);
  155. var ltxt: IStringList; ldummy: integer = -1;
  156. begin
  157.   inherited Create;
  158.   if Vers2Int(istVersion) < MinIStreamsVersion then
  159.     aPresenter.Provider.NotifySubscribers(prErrorFile,nil,Str2Pch('(!) WARNING! "istreams.pas" is too old, must be >= 0.29.01.2026! Go to Gitlab'));
  160.   fPresenter:= aPresenter;
  161.   fRoot:= aRoot;                                        
  162.   UpdateSections(Sects); { <- fetch our registered sections, v- i18n }
  163.   fTextCache:= CreStrListFromFile(format(mvpTexts,[fRoot,Lang])); ///<- i18n
  164.   { we need the model, this is a minor flaw if it fails, because then the }
  165.   if fTextCache.Count = 0 then { user will see a view filled with 'dummy' ;) }
  166.     fPresenter.Provider.NotifySubscribers(prStatus,nil,Str2Pch('(!) ERROR: Could NOT retrieve static texts!'));
  167.   ltxt:= GetStaticTexts(ClassName,ldummy);
  168.   fDb:= LiteDb; { get a hold of the global singleton }
  169.   fDb.DbName:= ltxt.Values['DbName']; { fetch our db-name }
  170.   CheckAndInitDb; /// temporary ?!? why ///
  171.   fLookUp:= TBufDataset.Create(nil){%H-};
  172. end; /// the above text can't be translated in the i18n'ed mvptexts, the count is 0! ///
  173.  
  174. destructor TModelMain.Destroy;
  175. begin
  176.   fLookUp.Clear;
  177.   fLookUp.Free; { class }
  178.   fTextCache:= nil; // com-object
  179.   fPresenter:= nil;
  180.   fDb:= nil; { just unref it }
  181.   inherited Destroy;
  182. end;
  183.  
  184. function TModelMain.AppendDirectory(const aDirName, aRootDir: string): ptrint;
  185. begin { first see if it's already there? }
  186.   if FetchDirNames then begin
  187.     if fLookUp.Locate('directory_bdn',aDirName,[loCaseInsensitive{,loPartialKey}]) then
  188.       Result:= fLookUp.FieldByName('id_bdn').AsLargeInt
  189.     else Result:= -1;
  190.   end else Result:= -1;
  191.   if Result = -1 then begin { ok, it wasn't, so we'll append it to the table }
  192.     with fDb.QuickConnect do try
  193.       Exec.Close;
  194.       Exec.SQL.Text:= InsBlobDirNames;
  195.       Exec.ParamByName('pdate').AsLargeInt:= Dati2Int(NowUTC); { calls internally 'DateTimeToUnix();' }
  196.       Exec.ParamByName('pdirectory').AsString:= aDirName;
  197.       Exec.ParamByName('prootdir').AsString:= aRootDir;          
  198.       { Exec.ParamByName('preserved').AsString:= 'reserved'; }
  199.       Exec.ExecSQL;
  200.       Result:= LastInsertedId; { return the newly created ID }
  201.     finally QuickDisConnect; end;
  202.   end;
  203. end;
  204.  
  205. function TModelMain.AppendFile(const aPath,aFilename: string; anID: ptrint): boolean;
  206. var lst: IMemoryStream;
  207. begin
  208.   if not FileExists(aPath+aFilename) then exit(false);
  209.   lst:= CreMemStream;
  210.   lst.LoadFromFile(aPath+aFilename);
  211.   lst.Position:= 0;
  212.   with fDb.QuickConnect do try  
  213.     Exec.Close;
  214.     Exec.SQL.Text:= insBlobData;
  215.     Exec.ParamByName('pfk_idbdn').AsLargeInt:= anID;
  216.     Exec.ParamByName('pfilename').AsString:= aFilename;  
  217.     Exec.ParamByName('pfiletype').AsString:= ExtractFileExt(aFilename);
  218.     Exec.ParamByName('pdate').AsLargeInt:= Dati2Int(NowUTC+0.041666666667);
  219.     Exec.ParamByName('pblob').AsBytes:= lst.AsBytes; { new feature in IStreamFP }
  220.     Exec.ParamByName('pflag').AsInteger:= -1; { not used -- for now }
  221.     Exec.ParamByName('preserved').AsString:= 'reserved'; { here's room for a shortstring }
  222.     Exec.ExecSQL;
  223.     Result:= true;
  224.   finally QuickDisConnect; end;
  225. end;
  226.  
  227. function TModelMain.GetBackendVersion: string;
  228. begin
  229.   Result:= fDb.LibVersion;
  230. end;
  231.  
  232. function TModelMain.GetStaticTexts(const aSection: string; out aTarget: integer): IStringList;
  233. begin { we use the [] here, because it fits in with standard ini-file format, nifty huh?!? }
  234.   if aSection = '' then exit(nil);
  235.   fSection:= '['+aSection+']'; fSecId:= IndexText(fSection,sects); { iterator-search }
  236.   fSectMaxIdx:= high(Sects); { iterator-search, sets up the section 'break-off' maxidx }
  237.   if fTextCache.Count = 0 then begin
  238.     fPresenter.Provider.NotifySubscribers(prStatus,nil,Str2Pch('(!) ERROR: Could NOT retrieve static texts!'));
  239.     exit(nil); /// the above text can't be translated in the i18n'ed mvptexts, the count is 0! ///
  240.   end;
  241.   Result:= CreateStrList; { create our resulting stringlist }
  242.   fTextCache.ForEach(@DoEach,Result);{ iterate over the source-list items, sending 'Result' along }
  243.   aTarget:= fSecId; { for the presenter to differentiate between views }
  244. end;
  245.  
  246. procedure TModelMain.RefreshSections;
  247. begin
  248.   UpdateSections(Sects);
  249. end;
  250.  
  251. procedure TModelMain.ReloadTextCache;
  252. begin
  253.   fTextCache.Clear;
  254.   fTextCache.LoadFromFile(format(mvpTexts,[fRoot,Lang]));
  255. end;
  256.  
  257. function TModelMain.UploadBlobDir(aDir: INodeList; aFilter: string; out aLogRes: string): ptrint;
  258. const OldCount: ptrint = 0; { static var }
  259. var lf0,lfn: PFSNode; lcurr, lcurroo, ldir, lroot: string; ldbID, lidx: ptrint; lprg: TUploadProgress;
  260. begin
  261.   Result:= -1; aLogRes:= '';
  262.   if aDir = nil then begin aLogRes+= '(X) ERROR! IModelMain.UploadBlobDir: "aDir: INodeList = NIL"'; exit; end;
  263.   lf0:= aDir.Items[0]; { items[0] = clean 'root' and 'dir', no subdirs-chickey-mickey -- yet }
  264.   ldir:= PickLastDir(lf0^.fnDir); { in nodelist, directory ALWAYS comes first, before files!!! }
  265.   lroot:= BackUpOneDir(lf0^.fnDir); inc(Result); /// 0
  266.   aLogRes+= '(+) Uploading "' + ldir + '":'; lidx:= 0;
  267.   lprg.upDirectory:= ldir; lprg.upMin:= 0; lprg.upMax:= aDir.Count-1; lprg.upPos:= 0; lprg.upPct:= 0.0;
  268.   fPresenter.Provider.NotifySubscribers(prProgressInit,nil,@lprg);
  269.   for lfn in aDir do with lfn^ do begin         { we'll do this here, before the }
  270.     inc(lprg.upPos); lprg.upPct:= (lprg.upPos / lprg.upMax) * 100;    { possible }
  271.     fPresenter.Provider.NotifySubscribers(prProgressUpd,nil,@lprg); { 'continue' }
  272.     if not fnInclude then continue; { decided beforehand }
  273.     if fnType = 1 then begin { fnType = 1 => directory }
  274.       lcurr:= IPD(PickLastDir(fnDir)); { last dir + include-trailing-path-delimiter }
  275.       if fnLevel > 0 then lcurroo:= BackUpOneDir(IPD(AMinusB(fnDir,lroot)))
  276.       else lcurroo:= ''; /// BackupOneDir(fnDir); { new root for subdir }
  277.       ldbID:= AppendDirectory(lcurroo+lcurr,lroot); /// maybe aLogRes += ?!?;
  278.     end else begin { fnType = 0 => file }
  279.       if IsInFilter(lfn,aFilter) then begin
  280.         if AppendFile(lroot+lcurroo+lcurr,fnFile,ldbID) then begin
  281.           inc(Result); { counting items :o) }
  282.           aLogRes+= LE+'· Uploaded: "'+lcurr+fnFile+'" to db.';
  283.         end; { AppendFile }
  284.       end; { IsInFilter }
  285.     end; { fnType = 0 => file }
  286.   end; { lfn in aDir do }
  287.   fPresenter.Provider.NotifySubscribers(prProgressFini,nil,nil);
  288.   OldCount:= Result; { preserve the count for the next time round...S }
  289. end;
  290.  
  291. {$EndRegion 'TModelMain'}
  292.  
  293. initialization
  294.   RegisterSection(TModelMain.ClassName);
  295.  
  296. finalization
  297.   if Assigned(Singleton) then FreeAndNil(Singleton); { checks for nil explicitly, freeandnil doesn't! }
  298. end.
  299.  
If this wets your appetite, then you should take a look at MVP-Setup & 'IStringList' for full code...
Regards Benny
If it ain't broke, don't fix it ;)
PCLinuxOS(rolling release) 64bit -> KDE6/QT6 -> FPC Release -> Lazarus Release &  FPC Main -> Lazarus Main

dsiders

  • Hero Member
  • *****
  • Posts: 1592
Re: Add Help to an Application, SQLite database
« Reply #3 on: March 05, 2026, 12:27:29 am »
Anyone experienced with designing help for your app.? I'm new to this and have been researching, anyone will to make a recommendation, that would be great.  I downloaded a document called ADD HELP TO YOUR APPLICATION and reading thru it, it discussed using CHM and HTML.  From reading I thought I'd attempt to use CHM.  I first attempted to load the lhelp CHM viewer, lazarus/components/chmhelp/democontrol/. Next I loaded the Chmmaker tool to write my CHM files. It's located at lazarus/tools/chmmaker.  Problem immediately is that it won't compile. The chmmaker.lpr requres a unit called unit1 and another, CHMsiteMapEditor.

Is there a better help design system since this doesn't appear to work?

Authoring CHM help is not specific to Lazarus. Use any one of the thousand of offerings you can find on Google search. Plugging context help in on forms and controls works according to the documented steps.

I tried chmaker.lpi on my Linux system. It compiles just fine - with no mention of a unit1. But I'm using Lazarus trunk.

wp

  • Hero Member
  • *****
  • Posts: 13482
Re: Add Help to an Application, SQLite database
« Reply #4 on: March 05, 2026, 01:19:25 am »
Compiled chmmaker with Laz 4.6 (Windows 11) - no problem. Loading the example file and creating a chm from it worked fine. The same with Laz 4.0.

What is you Laz version/FPC version/operating system so that I can verify? Often when units are not found, it helps to do a clean recompilation of the project: "Run" > "Clean-up and Build"

1HuntnMan

  • Sr. Member
  • ****
  • Posts: 445
  • From Delphi 7 to Lazarus
    • NewFound Photo Art
Re: Add Help to an Application, SQLite database
« Reply #5 on: March 05, 2026, 02:41:40 pm »
Using this doc I downloaded, "ADD HELP TO YOUR APPLICATION" at the beginning of the doc it discusses both CHM and HTML.  The beginning discusses CHM, and mentions a demo program in the Lazarus/components/chmhelp/democontrol/ and you can write your own CHM files using the old Microsoft HTML Workshop or with the new Lazarus chmmaker tools in Lazarus/tools/chmmaker using the TCHMHelpDatabase control. I loaded that into Lazarus (Lazarus IDE v4.6) and attempted to compile it. It includes chmmaker.lpr, unit1 and CHMSiteMapEditor. dsiders and cdbc both loaded and compiled with no issues.  Mine errors: Cannont find LHelpControl used by unit1. In the Unit1.pas file it's erroring at line 113: uses CHMSiteMapEditor, LHelpControl, Process;
The error Cannot find LHelpControl used by unit1. Check if package lhelpcontrolpkg is in dependencies. But I'll start looking and learning since their are others out there. I did a Cleanup & Build and the same error...

valdir.marcos

  • Hero Member
  • *****
  • Posts: 1225
Re: [SOLVED] Add Help to an Application, SQLite database
« Reply #6 on: March 07, 2026, 07:18:00 pm »
Anyone experienced with designing help for your app.? I'm new to this and have been researching, anyone will to make a recommendation, that would be great.  I downloaded a document called ADD HELP TO YOUR APPLICATION and reading thru it, it discussed using CHM and HTML.  From reading I thought I'd attempt to use CHM.  I first attempted to load the lhelp CHM viewer, lazarus/components/chmhelp/democontrol/. Next I loaded the Chmmaker tool to write my CHM files. It's located at lazarus/tools/chmmaker.  Problem immediately is that it won't compile. The chmmaker.lpr requres a unit called unit1 and another, CHMsiteMapEditor.

Is there a better help design system since this doesn't appear to work?


The document I downloaded, most of it addresses HTML help for you application.


Using this doc I downloaded, "ADD HELP TO YOUR APPLICATION" at the beginning of the doc it discusses both CHM and HTML.  The beginning discusses CHM, and mentions a demo program in the Lazarus/components/chmhelp/democontrol/ and you can write your own CHM files using the old Microsoft HTML Workshop or with the new Lazarus chmmaker tools in Lazarus/tools/chmmaker using the TCHMHelpDatabase control. I loaded that into Lazarus (Lazarus IDE v4.6) and attempted to compile it. It includes chmmaker.lpr, unit1 and CHMSiteMapEditor. dsiders and cdbc both loaded and compiled with no issues.  Mine errors: Cannont find LHelpControl used by unit1. In the Unit1.pas file it's erroring at line 113: uses CHMSiteMapEditor, LHelpControl, Process;
The error Cannot find LHelpControl used by unit1. Check if package lhelpcontrolpkg is in dependencies. But I'll start looking and learning since their are others out there. I did a Cleanup & Build and the same error...


Add Help to Your Application
https://wiki.freepascal.org/Add_Help_to_Your_Application


I could use CHM with the Lazarus version, but in general I'm trying to get away from any MS dependency, so that eventually I can create a Linux version.
I did the same process some 10 years ago.

Quote
Are CHM files supported on Linux?
There are a couple of CHM viewers but the functionality varies wildly between them. And don't bother with the LHelp viewer included with Lazarus, it is the worst of the bunch.

Quote
Any other suggestions gratefully received.
During my research of help formats some 10 years ago, I eventually remembered OS/2's help system created by IBM. It was brilliant, extremely compact and fast. So I did some tests and eventually implemented across platform INF help viewer called DocView and used that ever since for all my personal and commercial cross-platform projects. The DocView executable is 1MB in size, so doesn't add much to the overall size of a installed project - and has no external dependencies.

As I mentioned, INF help files are viewable with DocView. fpdoc can generate IPF output, which you then compile to highly optimised INF help files using the WIPFC compiler - a stand alone console tool included in the fpGUI code repository (available for Linux and Windows).

DocView is better than any CHM viewer I have come across. It is much faster at starting up and loading INF files (near instant). You can load multiple INF files at the same time too (runtime concatenation of INF files), supports inline annotations (user comments embedded in the help content), advanced searching with a rating system, and runtime building of Indexes, font and colour customization etc. DocView is also fully cross-platform, tested under Windows (win2k-win10), Linux, FreeBSD, Solaris and OSX.


Authoring help for your own applications (not class documentation):
I use the text editor called EditPad Pro. It has brilliant code template support, live file navigation and customisable syntax highlighter. A IPF syntax highlighter and navigation scheme is freely available on the EditPad Pro website. But any text editor (even Lazarus IDE) can be used to author INF help. The IPC syntax is easy and the tags are mnemonic based, so very easy to learn and associated with their function - it is also much less verbose than HTML or XML.

INF was originally developed by IBM, and is still an excellent (and relevant) choice today. Especially with the much more feature complete DocView INF viewer (compared to IBM's original VIEW application).

Even though Docview was initially written for fpGUI based projects, it works equally well as an eBook type application or integration with LCL based applications. Here is a link to a forum attachment of a fully working LCL application with DocView+INF incorporated. For the full discussion, see the Lazarus Forum post.

 http://forum.lazarus.freepascal.org/index.php/topic,27864.msg173887.html#msg173887

Docview screenshots:
  http://fpgui.sourceforge.net/screenshots_apps.shtml

Docview pre-compiled binaries:
  http://sourceforge.net/projects/fpgui/files/fpGUI/1.4/

INF help files for RTL, FCL, LCL and fpGUI
Pre-built INF files are available for download from the above SourceForge link too. They are also a fraction of the size of HTML or even CHM - yet contain the exact same help content.

@1HuntnMan
Do yourself a favor and take a look at Graeme's DocView solution.

 

TinyPortal © 2005-2018