From 3f3a9bb029e348e025056cb288a803c36d9242ea Mon Sep 17 00:00:00 2001 From: Peter Johnson <5164283+delphidabbler@users.noreply.github.com> Date: Wed, 9 Nov 2022 23:04:19 +0000 Subject: [PATCH 01/34] Update License.html * Change image attribution for CodeSnip images. * Strike out a broken link. * Change links that support it to use https instead of http. --- Docs/License.html | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/Docs/License.html b/Docs/License.html index 9964fa5df..d4b0f895c 100644 --- a/Docs/License.html +++ b/Docs/License.html @@ -340,12 +340,12 @@

License.
- The license requires that the images should be attributed. To do this + This license requires that the images should be attributed. To do this simply note in your documentation, about box, web page or similar that the icons form part of the image set for DelphiDabbler CodeSnip and provide a link to https://github.com/delphidabbler/codesnip. + href="https://delphidabbler.com/software/codesnip" + >https://delphidabbler.com/software/codesnip.
These images include modifications and remixes of icons supplied under @@ -1614,7 +1614,7 @@

Toolbar Icons is made available under the terms of the MIT License. See http://toolbaricons.sourceforge.net/ for more information.

Copyright © 2010 Florian Haag

@@ -1685,9 +1685,7 @@

Led Icon Set v1.0: http://led24.de/iconset/ [link broken].
  • - 16x16-free-application-icons by Aha-Soft: https://www.aha-soft.com. + 16x16-free-application-icons by Aha-Soft: https://www.aha-soft.com [link broken].
  • @@ -1715,7 +1713,7 @@

  • Some program icons are based on Florian Haag's Toolbar Icons set at http://toolbaricons.sourceforge.net/.
  • From 2bed7c731cb5f28ef029541a69285e54a8972a19 Mon Sep 17 00:00:00 2001 From: Peter Johnson <5164283+delphidabbler@users.noreply.github.com> Date: Wed, 9 Nov 2022 23:31:09 +0000 Subject: [PATCH 02/34] Update License.rtf Update copyright date to 2022. Fix typo. --- Src/Install/Assets/License.rtf | Bin 1189 -> 1192 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/Src/Install/Assets/License.rtf b/Src/Install/Assets/License.rtf index 998dd2d2fa0ba0479cd2a88f475f28678eab7bbd..3795e032266c876568ad121ba7a2cd003548536c 100644 GIT binary patch delta 27 jcmZ3=xq@>88zZC9W_HGtOiY=1lP@!0WPQJXFE1AWcee>@ delta 24 gcmZ3%xs-DQ8zZCPW_HGtOw5USm6I Date: Sat, 3 Dec 2022 10:40:21 +0000 Subject: [PATCH 03/34] Update README.md Change Source Code section re addition of caboli branch and abandonment of belvedere. Some other corrections & clarifications --- README.md | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index 5bfc29a5d..61d3680c7 100644 --- a/README.md +++ b/README.md @@ -22,13 +22,13 @@ CodeSnip requires Windows 2000 or later and Internet Explorer 6 or later, althou ## Installation -The standard edition of CodeSnip is installed and removed using a standard Windows installer. Administrator privileges are required for installation. +The standard edition of CodeSnip is installed and removed using a Windows installer. Administrator privileges are required for installation. The portable edition has no installer. Simply follow the instructions in the [read me file](https://raw.githubusercontent.com/delphidabbler/codesnip/master/Docs/ReadMe.txt) that is included in the download zip file. ## Support -The following support is available CodeSnip users: +The following support is available to CodeSnip users: * A comprehensive help file. * A [read-me file](https://raw.githubusercontent.com/delphidabbler/codesnip/master/Docs/ReadMe.txt) * that discusses installation, configuration, updating and known issues. @@ -43,19 +43,17 @@ There's also plenty of info available on how to compile CodeSnip from source - s CodeSnip's source code is maintained in the [`delphidabbler/codesnip`](https://github.com/delphidabbler/codesnip) Git repository on GitHub†. -The [Git Flow](https://nvie.com/posts/a-successful-git-branching-model/) methodology has been adopted, with the exception of some branches that have been used in various attempts to start work on CodeSnip 5. +The [Git Flow](https://nvie.com/posts/a-successful-git-branching-model/) methodology has been adopted, with the exception of some experimental branches. -The following branches existed as of 2022/07/01: +The following branches existed as of 2022/12/03: -* `master`: Always reflects the state of the source code as of the latest release.‡ -* `develop`: Main development branch. The head of this branch contains the latest v4 development code. -* `belvedere`: The latest attempt to develop CodeSnip 5. See the [Belvedere Readme file](https://github.com/delphidabbler/codesnip/blob/belvedere/README.md) for a full explanation. -* `pagoda`: An abortive attempt at developing CodeSnip 5. Work on this branch has halted. It does not follow GitFlow methodology. ***Do not use this branch: it may be pruned.*** -* `pavilion`: Another attempt at working on CodeSnip 5. It branched off `pagoda` and work on it has halted. Again it does not follow GitFlow methodology. ***Do not use this branch: it may be pruned.*** - -New features and most bug fixes are worked on in `feature/xxxx` branches that are branched off `develop` locally. They are merged into `develop` as they are completed and the branches are deleted. - -Note that the default branch on GitHub is `master`, which contains the state of the project as of the latest release. If you want to see the current state of play with new developments switch to `develop`. +* [`master`](https://github.com/delphidabbler/codesnip/tree/master): Always reflects the state of the source code as of the latest release.‡ +* [`develop`](https://github.com/delphidabbler/codesnip/tree/develop): Main development branch. The head of this branch contains the latest v4 development code. Normal development of CodeSnip 4 takes place in `feature/xxx` branches off `develop`. +* [`caboli`](https://github.com/delphidabbler/codesnip/tree/caboli): Experimental branch where an attempt is being made to (a) modernise the UI and (b) get the code to work properly when compiled with Delphi 11. +* Abandoned branches: + * [`pagoda`](https://github.com/delphidabbler/codesnip/tree/pagoda): An abortive attempt at developing CodeSnip 5. + * [`pavilion`](https://github.com/delphidabbler/codesnip/tree/pavilion): Another attempt at working on CodeSnip 5 that branched off `pagoda`. + * [`belvedere`](https://github.com/delphidabbler/codesnip/tree/belvedere): A thiird, failed attempt to develop CodeSnip 5 as a ground up rewrite. Not related to `pagoda` & `pavilion`. > † Up to and including v4.13.1 the source code was kept in a Subversion repository on SourceForge. It was converted to Git in October 2015 and imported into GitHub. All releases from v3.0.0 are marked by tags in the form `version-x.x.x` where `x.x.x` is the version number. None of the Subversion branches made it through the conversion to Git, so to see a full history look at the old [SourceForge repository](https://sourceforge.net/p/codesnip/code/). @@ -65,7 +63,9 @@ Note that the default branch on GitHub is `master`, which contains the state of To contribute to CodeSnip 4 development please fork the repository on GitHub. Create a feature branch off the `develop` branch. Make your changes to your feature branch then submit a pull request via GitHub. -> **Do not create branches off `master`, always branch from `develop`.** +:warning: **Do not create branches off `master`, always branch from `develop`.** + +:no_entry: Contributions to experimental branches are not being excepted just now. #### Licensing of contributions From 3b99e86907a052e1032047e5406aa1d292889d37 Mon Sep 17 00:00:00 2001 From: Peter Johnson <5164283+delphidabbler@users.noreply.github.com> Date: Mon, 12 Dec 2022 04:30:45 +0000 Subject: [PATCH 04/34] Update copyright date range in help license --- Src/Help/HTML/license.htm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Src/Help/HTML/license.htm b/Src/Help/HTML/license.htm index 66c40cee7..50c1357c2 100644 --- a/Src/Help/HTML/license.htm +++ b/Src/Help/HTML/license.htm @@ -27,7 +27,7 @@

    Summary of End User License Agreement

    - DelphiDabbler CodeSnip is copyright © 2005-2020 by Peter D + DelphiDabbler CodeSnip is copyright © 2005-2022 by Peter D Johnson, Date: Sun, 11 Dec 2022 21:01:40 +0000 Subject: [PATCH 05/34] Add new REML character entities in UREMLDataIO unit Remove REML docs from comments: they are in a separate unit and fix other header comments. --- Src/UREMLDataIO.pas | 96 +++++++++++++++++---------------------------- 1 file changed, 37 insertions(+), 59 deletions(-) diff --git a/Src/UREMLDataIO.pas b/Src/UREMLDataIO.pas index 5f809c127..5381b6565 100644 --- a/Src/UREMLDataIO.pas +++ b/Src/UREMLDataIO.pas @@ -7,7 +7,7 @@ * * Implements classes that render and parse Routine Extra Markup Language (REML) * code. This markup is used to read and store active text objects as used by - * the Extra property of a TSnippet object. Also includes helper classes. + * some properties of a TSnippet object. Also includes helper classes. } @@ -139,59 +139,8 @@ implementation It comprises plain text with limited inline and block level formatting and hyperlink specified by HTML like tags. - Supported tags are as follows. Unless otherwise specified, no tags may have - any attributes: - - Inline: - xxxx - Hyperlink: must have an href attribute that - specifies the link destination as a valid URL. - URLs must not be URL encoded. No other attributes - may be specified. - .. - Renders enclosed text with strong emphasis. - .. - Renders enclosed text emphasised. - .. - Renders enclosed text as a programming variable. - .. - Renders enclosed text as a warning. - .. - Renders enclosed text as mono spaced. - - Block: -

    ..

    - Enclosed text is formatted as a paragraph. - .. - Enclosed text is formatted as a heading. - - Certain characters in plain text or in attribute values must be encoded as - HTML-like character entities. Attribute names must not contain any of these - characters. The characters that must be encoded are: - - Character Entity - > > - < < - " " - & & - � © - - No other entities are supported. Any other character can be encoded using its - unicode or ascii value. For example, the @ symbol (ascii 64) is encoded as - @ - - Example: - Hello -

    "Hello" to - you

    - - This example specifes a heading "Hello" followed by a single paragraph. In the - paragraph, "Hello" will be bold, "to" should be plain text and "you" should - hyperlink to "example.com". - - There are two versions of REML as follows: - v1 - supported tags: and . - - supported entities: >, <, ", &. - v2 - added tags: , , , , Returns a string containing Count copies of character Ch. +/// +/// If Count is zero then the empty string is returned. +function StrOfChar(const Ch: Char; const Count: Word): string; + +/// Returns a string of a given number of spaces. +/// Word [in] Required number of spaces. +/// string. Required number of spaces. +/// If Count is zero then an empty string is returned. +function StrOfSpaces(const Count: Word): string; implementation @@ -769,7 +779,6 @@ function StrWrap(const Str: UnicodeString; const MaxLen, Margin: Integer): Word: UnicodeString; // next word in input Str Line: UnicodeString; // current output line Words: TStringList; // list of words in input Str - I: Integer; // loops thru all words in input Str // ------------------------------------------------------------------------- /// Adds a line of text to output, offseting line by Margin spaces @@ -777,7 +786,7 @@ function StrWrap(const Str: UnicodeString; const MaxLen, Margin: Integer): begin if Result <> '' then // not first line: insert new line Result := Result + EOL; - Result := Result + StringOfChar(' ', Margin) + Line; + Result := Result + StrOfSpaces(Margin) + Line; end; // ------------------------------------------------------------------------- @@ -789,9 +798,8 @@ function StrWrap(const Str: UnicodeString; const MaxLen, Margin: Integer): Result := ''; Line := ''; // Loop for each word in Str - for I := 0 to Pred(Words.Count) do + for Word in Words do begin - Word := Words[I]; if Length(Line) + Length(Word) + 1 <= MaxLen then begin // Word fits on current line: add it @@ -904,5 +912,17 @@ function StrIsEmpty(const S: string; const IgnoreWhiteSpace: Boolean = False): Result := S = ''; end; +function StrOfChar(const Ch: Char; const Count: Word): string; +begin + if Count = 0 then + Exit(''); + Result := System.StringOfChar(Ch, Count); +end; + +function StrOfSpaces(const Count: Word): string; +begin + Result := StrOfChar(' ', Count); +end; + end. From c4ef947cfee1cfc42eabc8c7132a59b59b670430 Mon Sep 17 00:00:00 2001 From: delphidabbler Date: Sun, 19 Jul 2020 05:18:48 +0100 Subject: [PATCH 07/34] ActiveText.UMain.pas: Rename parameters --- Src/ActiveText.UMain.pas | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/Src/ActiveText.UMain.pas b/Src/ActiveText.UMain.pas index bb5e534a5..5a5ce1fcc 100644 --- a/Src/ActiveText.UMain.pas +++ b/Src/ActiveText.UMain.pas @@ -383,13 +383,14 @@ TActiveTextActionElem = class(TInterfacedObject, fAttrs: IActiveTextAttrs; public /// Object constructor. Creates an action element. - /// TActiveTextElemKind [in] Required kind of element. + /// TActiveTextElemKind [in] Required kind of element. /// - /// IActiveTextAttrs [in] Element's attributes. - /// TActiveTextElemState [in] State of element: opening - /// or closing. - constructor Create(const Kind: TActiveTextActionElemKind; - Attrs: IActiveTextAttrs; const State: TActiveTextElemState); + /// IActiveTextAttrs [in] Element's attributes. + /// + /// TActiveTextElemState [in] State of element: + /// opening or closing. + constructor Create(const AKind: TActiveTextActionElemKind; + AAttrs: IActiveTextAttrs; const AState: TActiveTextElemState); /// Assigns properties of another object to this object. /// IInterface [in] Object whose properties are to be /// assigned. Src must support IActiveTextActionElem. @@ -674,13 +675,13 @@ function TActiveTextActionElem.Clone: IInterface; Result := TActiveTextActionElem.Create(GetKind, Attrs, GetState); end; -constructor TActiveTextActionElem.Create(const Kind: TActiveTextActionElemKind; - Attrs: IActiveTextAttrs; const State: TActiveTextElemState); +constructor TActiveTextActionElem.Create(const AKind: TActiveTextActionElemKind; + AAttrs: IActiveTextAttrs; const AState: TActiveTextElemState); begin inherited Create; - fAttrs := Attrs; - fState := State; - fKind := Kind; + fAttrs := AAttrs; + fState := AState; + fKind := AKind; end; function TActiveTextActionElem.GetAttrs: IActiveTextAttrs; From 5ec55ba6ee5cc6624a483c0b52bb2d92f9abb460 Mon Sep 17 00:00:00 2001 From: delphidabbler Date: Fri, 24 Jul 2020 05:26:06 +0100 Subject: [PATCH 08/34] Add new TCSSBuilder.EnsureSelector method This added because attempting to add an existing selector raises an exception and reading a non-existant selector returns a null pointer. This makes it hard to write bug-free code without checking for the existence of a selector. It's been annoying me for years! --- Src/UCSSBuilder.pas | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/Src/UCSSBuilder.pas b/Src/UCSSBuilder.pas index fbc1d307f..662e44dd6 100644 --- a/Src/UCSSBuilder.pas +++ b/Src/UCSSBuilder.pas @@ -83,6 +83,12 @@ TCSSBuilder = class(TObject) @param Selector [in] Name of new selector. @return New empty selector object. } + function EnsureSelector(const Selector: string): TCSSSelector; + {Returns selector object with given name or adds a new selector with the + given name if no such selector exists. + @parm Selector [in] Name of selector. + @return Reference to new or pre-existing selector. + } procedure Clear; {Clears all selectors from style sheet and frees selector objects. } @@ -204,6 +210,18 @@ destructor TCSSBuilder.Destroy; inherited; end; +function TCSSBuilder.EnsureSelector(const Selector: string): TCSSSelector; + {Returns selector object with given name or adds a new selector with the given + name if no such selector exists. + @parm Selector [in] Name of selector. + @return Reference to new or pre-existing selector. + } +begin + Result := GetSelector(Selector); + if not Assigned(Result) then + Result := AddSelector(Selector); +end; + function TCSSBuilder.GetSelector(const Selector: string): TCSSSelector; {Read access method for Selectors property. Returns selector object with given name. From 473739d1f29d5955a910ad2ac8492e1284353646 Mon Sep 17 00:00:00 2001 From: delphidabbler Date: Sun, 19 Jul 2020 04:53:44 +0100 Subject: [PATCH 09/34] Add list support to REML New tags supported:
      ,

    and . - - added entity: ©. - - The implementation of active text's link element changed over time. At first - it supported only the http:// protocol for URLs. This limited REML v1 tags - to using just that protocol. CodeSnip v3.0.1 added support to active text for - the file:// protocol. From CodeSnip v4.0 active text was extended to support - the https:// protocol. + Valid REML tags and character entities are documented in the file reml.html in + the Docs/Design directory. } @@ -841,13 +790,42 @@ class function TREMLEntities.CharToMnemonicEntity(const Ch: Char): string; {Class constructor. Creates map of mnemonic entities to equivalent characters. } begin - SetLength(fEntityMap, 5); - // Record all supported character entities - fEntityMap[0] := TREMLEntity.Create('amp', '&'); + SetLength(fEntityMap, 34); + // Supported character entities. All are optional unless otherwise stated + fEntityMap[0] := TREMLEntity.Create('amp', '&'); // required in REML fEntityMap[1] := TREMLEntity.Create('quot', DOUBLEQUOTE); - fEntityMap[2] := TREMLEntity.Create('gt', '>'); - fEntityMap[3] := TREMLEntity.Create('lt', '<'); + fEntityMap[2] := TREMLEntity.Create('gt', '>'); + fEntityMap[3] := TREMLEntity.Create('lt', '<'); // required in REML fEntityMap[4] := TREMLEntity.Create('copy', '©'); + fEntityMap[5] := TREMLEntity.Create('times', '×'); + fEntityMap[6] := TREMLEntity.Create('divide', '÷'); + fEntityMap[7] := TREMLEntity.Create('div', '÷'); + fEntityMap[8] := TREMLEntity.Create('plusmn', '±'); + fEntityMap[9] := TREMLEntity.Create('ne', '≠'); + fEntityMap[10] := TREMLEntity.Create('neq', '≠'); + fEntityMap[11] := TREMLEntity.Create('sum', '∑'); + fEntityMap[12] := TREMLEntity.Create('infin', '∞'); + fEntityMap[13] := TREMLEntity.Create('pound', '£'); + fEntityMap[14] := TREMLEntity.Create('curren', '¤'); + fEntityMap[15] := TREMLEntity.Create('yen', 'Â¥'); + fEntityMap[16] := TREMLEntity.Create('euro', '€'); + fEntityMap[17] := TREMLEntity.Create('dagger', '†'); + fEntityMap[18] := TREMLEntity.Create('ddagger', '‡'); + fEntityMap[19] := TREMLEntity.Create('Dagger', '‡'); + fEntityMap[20] := TREMLEntity.Create('hellip', '…'); + fEntityMap[21] := TREMLEntity.Create('para', '¶'); + fEntityMap[22] := TREMLEntity.Create('sect', '§'); + fEntityMap[23] := TREMLEntity.Create('reg', '®'); + fEntityMap[24] := TREMLEntity.Create('frac14', '¼'); + fEntityMap[25] := TREMLEntity.Create('frac12', '½'); + fEntityMap[26] := TREMLEntity.Create('half', '½'); + fEntityMap[27] := TREMLEntity.Create('frac34', '¾'); + fEntityMap[28] := TREMLEntity.Create('micro', 'µ'); + fEntityMap[29] := TREMLEntity.Create('deg', '°'); + fEntityMap[30] := TREMLEntity.Create('cent', '¢'); + fEntityMap[31] := TREMLEntity.Create('laquo', '«'); + fEntityMap[32] := TREMLEntity.Create('raquo', '»'); + fEntityMap[33] := TREMLEntity.Create('iquest', '¿'); end; class destructor TREMLEntities.Destroy; From 0ff201464e15100a7b253d1b5ef75d0433dbcf1f Mon Sep 17 00:00:00 2001 From: delphidabbler Date: Thu, 23 Jul 2020 21:01:56 +0100 Subject: [PATCH 06/34] Update UStrUtils unit & update other unit re changes * Add new StrOfChar and StrOfSpaces functions * Refactor to use StrOfSpaces instead of StringOfChar routine. * Update indexed for loop to for .. in loop FmSWAGImportDlg: Replace StringOfChar routine call with StrOfChar --- Src/FmSWAGImportDlg.pas | 2 +- Src/UStrUtils.pas | 28 ++++++++++++++++++++++++---- 2 files changed, 25 insertions(+), 5 deletions(-) diff --git a/Src/FmSWAGImportDlg.pas b/Src/FmSWAGImportDlg.pas index efeb0e8a9..421840eb8 100644 --- a/Src/FmSWAGImportDlg.pas +++ b/Src/FmSWAGImportDlg.pas @@ -812,7 +812,7 @@ procedure TSWAGImportDlg.PreviewSelectedPacket; FullPacket.FileName, FullPacket.Title, FullPacket.Author, - StringOfChar('-', 80), + StrOfChar('-', 80), StrWindowsLineBreaks(FullPacket.SourceCode) ] ); diff --git a/Src/UStrUtils.pas b/Src/UStrUtils.pas index 972302609..0d4eb057d 100644 --- a/Src/UStrUtils.pas +++ b/Src/UStrUtils.pas @@ -269,6 +269,16 @@ procedure StrArrayToStrList(const SA: array of string; const SL: TStrings); function StrIsEmpty(const S: string; const IgnoreWhiteSpace: Boolean = False): Boolean; +///

  • +

    + Readers of v2 and later files may parse REML from any file version as if it were REML v5, since all versions of REML up to v5 are compatible. +

    +

    Handling Text File Encodings

    diff --git a/Docs/Design/reml.html b/Docs/Design/reml.html index bbc138a8b..3fce8fae1 100644 --- a/Docs/Design/reml.html +++ b/Docs/Design/reml.html @@ -44,7 +44,6 @@ text-align: center; margin: 0.5em; } - header nav { display: block; margin: 1em; @@ -150,7 +149,7 @@ dt { margin: 0; padding: 0; - font-weight: inherited; + font-weight: bold; } dd { margin: 0 0 0 2em; @@ -172,6 +171,36 @@ margin-top: 0; margin-bottom: 0.5em; } +table { + border-collapse: collapse; +} +tr { + border-bottom: 1px silver solid; +} +th { + text-align: center; + font-weight: bold; + margin: 0; + padding: 0; + border-bottom: 2px silver solid; +} +td { + border-bottom: 1px silver solid; + margin: 0; + padding: 0; +} +td, th { + padding: 0.5em; +} +td:first-child, th:first-child { + border-right: 1px silver solid; +} +td:last-child { + text-align: center; +} +tr:nth-child(even), th { + background-color: #eee; +} @@ -222,7 +251,7 @@ <h1> The REML language is a SGML language similar to a greatly simplified XHTML. The are a small number of tags and character entities that can be used. </p> <aside> - <strong>Note:</strong> The language described here is REML v4. Earlier versions are obsolete. + <strong>Note:</strong> The language described here is REML v5. v4 is still in regular use in CodeSnip up to v4.20.x. Earlier versions are obsolete. </aside> </section> @@ -255,19 +284,34 @@ <h2> <li> <code class="value"><heading>...</heading></code> – Renders the enclosed markup as a heading. </li> + <li> + <code class="value"><ol>...</ol></code> – Renders the enclosed HTML as an ordered list. <span class="very-strong">Must</span> contain <code class="value"><li>...</li></code> blocks and nothing else. + </li> + <li> + <code class="value"><ul>...</ul></code> – Renders the enclosed HTML as an unordered list. <span class="very-strong">Must</span> contain <code class="value"><li>...</li></code> blocks and nothing else. + </li> + <li> + <code class="value"><li>...</li></code> – Renders the enclosed HTML as a list item. <span class="very-strong">Must</span> only be used within <code class="value"><ol>...</ol></code> and <code class="value"><ul>...</ul></code> blocks. + </li> </ul> <p> The following rules apply to the use of block level tags: </p> <ul class="unspaced"> <li> - The tags <span class="very-strong">must not</span> be nested. + <span class="very-strong">Must</span> be matched, e.g. <code class="value"><p></code> <span class="very-strong">must</span> have a matching <code class="value"></p></code>. + </li> + <li> + <code class="value"><p>...</p></code> and <code class="value"><heading>...</heading></code> blocks <span class="very-strong">must not</span> contain other block level tags. + </li> + <li> + <code class="value"><ol>...</ol></code> and <code class="value"><ul>...</ul></code> blocks <span class="very-strong">must only</span> contain one or more <code class="value"><li>...</li></code> blocks. </li> <li> - The tags <span class="very-strong">must</span> be matched, e.g. <code class="value"><p></code> must have a matching <code class="value"></p></code>. + <code class="value"><li>...</li></code> blocks <span class="very-strong">must not</span> contain <code class="value"><p>...</p></code>, <code class="value"><heading>...</heading></code> or other <code class="value"><li>...</li></code> blocks directly, but <em>may</em> contain <code class="value"><ol>...</ol></code> and <code class="value"><ul>...</ul></code> blocks. </li> <li> - All text <em>should</em> be embedded within block level tags, e.g. <code class="value"><heading>heading</heading><p>text</p></code> or simply <code class="value"><p>text</p></code>. + All text <em>should</em> be embedded within <code class="value"><p>...</p></code>, <code class="value"><heading>...</heading></code> or <code class="value"><li>...</li></code> block level tags, e.g. <code class="value"><heading>heading</heading><p>text</p></code> or simply <code class="value"><p>text</p></code>. </li> <li> White space between blocks <span class="very-strong">must</span> be ignored. @@ -278,7 +322,12 @@ <h2> </p> <pre class="sample"><p>Hello World</p> <heading>Hello</heading> -<p>Hello World</p></pre> +<p>Hello World</p> +<ol> + <li>one</li> + <li>two</li> + <li>three</li> +</ol></pre> <p> Strictly speaking, the following example is invalid code – all occurrences of <code class="value">wrong</code> are in error because they are not contained within block tags. </p> @@ -353,32 +402,146 @@ <h1> </h1> <p> - A few symbolic character entities are supported in REML. Here is the complete list: + Some symbolic character entities are supported in REML. Many symbols, but not all, have analogues in the list of supported character entities in XHTML or HTML 5. Some entities have alternate symbols. Here is the complete list. </p> - <ul class="half-spaced"> - <li> - <code class="value">&lt;</code> for <code class="value"><</code> - </li> - <li> - <code class="value">&gt;</code> for <code class="value">></code> - </li> - <li> - <code class="value">&quot;</code> for <code class="value">"</code> - </li> - <li> - <code class="value">&amp;</code> for <code class="value">&</code> - </li> - <li> - <code class="value">&copy;</code> for <code class="value">©</code> - </li> - </ul> + + <table> + <thead> + <tr> + <th>Character Entity</th> + <th>Actual Character</th> + </tr> + </thead> + <tbody> + <tr> + <td><code>&amp;</code></td> + <td>&</td> + </tr> + <tr> + <td><code>&quot;</code></td> + <td>"</td> + </tr> + <tr> + <td><code>&gt;</code></td> + <td>></td> + </tr> + <tr> + <td><code>&lt;</code></td> + <td><</td> + </tr> + <tr> + <td><code>&copy;</code></td> + <td>©</td> + </tr> + <tr> + <td><code>&times;</code></td> + <td>×</td> + </tr> + <tr> + <td><code>&divide;</code> or <code>&div;</code></td> + <td>÷</td> + </tr> + <tr> + <td><code>&plusmn;</code></td> + <td>±</td> + </tr> + <tr> + <td><code>&ne;</code> or <code>&neq;</code></td> + <td>≠</td> + </tr> + <tr> + <td><code>&sum;</code></td> + <td>∑</td> + </tr> + <tr> + <td><code>&infin;</code></td> + <td>∞</td> + </tr> + <tr> + <td><code>&pound;</code></td> + <td>£</td> + </tr> + <tr> + <td><code>&curren;</code></td> + <td>¤</td> + </tr> + <tr> + <td><code>&yen;</code></td> + <td>Â¥</td> + </tr> + <tr> + <td><code>&euro;</code></td> + <td>€</td> + </tr> + <tr> + <td><code>&cent;</code></td> + <td>¢</td> + </tr> + <tr> + <td><code>&dagger;</code></td> + <td>†</td> + </tr> + <tr> + <td><code>&ddagger;</code> or <code>&Dagger;</code></td> + <td>‡</td> + </tr> + <tr> + <td><code>&hellip;</code></td> + <td>…</td> + </tr> + <tr> + <td><code>&para;</code></td> + <td>¶</td> + </tr> + <tr> + <td><code>&sect;</code></td> + <td>§</td> + </tr> + <tr> + <td><code>&reg;</code></td> + <td>®</td> + </tr> + <tr> + <td><code>&frac14;</code></td> + <td>¼</td> + </tr> + <tr> + <td><code>&frac12;</code> or <code>&half;</code></td> + <td>½</td> + </tr> + <tr> + <td><code>&frac34;</code></td> + <td>¾</td> + </tr> + <tr> + <td><code>&micro;</code></td> + <td>µ</td> + </tr> + <tr> + <td><code>&deg;</code></td> + <td>°</td> + </tr> + <tr> + <td><code>&laquo;</code></td> + <td>«</td> + </tr> + <tr> + <td><code>&raquo;</code></td> + <td>»</td> + </tr> + <tr> + <td><code>&iquest;</code></td> + <td>¿</td> + </tr> + </tbody> + </table> <aside> <strong>Note:</strong> the '<' and '&' characters are special within the markup and cannot be used literally, even when you are just entering plain text. You <span class="very-strong">must</span> use the <code class="value">&lt;</code> character entity in place of <code class="value"><</code> and <code class="value">&amp;</code> instead of <code class="value">&</code>. For example to write <code class="value">x<y</code> in REML use <code class="value">x&lt;y</code> and to write <code class="value">you & me</code> use <code class="value">you &amp; me</code>. </aside> <p> - To express other special symbols for which there is no symbolic character entity, numeric character entities can be used. For example to display the '¶' character (Unicode <em>pilcrow sign</em>) use <code class="value">&#182;</code>. + To express other special symbols for which there is no symbolic character entity, numeric character entities can be used. For example to display the 'Ω' character (Unicode <em>Greek capital letter Omega</em>) use <code class="value">&#937;</code>. </p> <aside> @@ -387,6 +550,7 @@ <h1> </section> + <section id="changes"> <h1>Change Log</h1> @@ -396,7 +560,7 @@ <h1>Change Log</h1> </p> <p> - <strong>v1 of 2008/12/31</strong> + <strong>v1 of 2008-12-31</strong> </p> <p> @@ -416,7 +580,7 @@ <h1>Change Log</h1> </ul> <p> - <strong>v2 of 2009/06/29</strong> + <strong>v2 of 2009-06-29</strong> </p> <p> @@ -433,7 +597,7 @@ <h1>Change Log</h1> </ul> <p> - <strong>v3 of 2009/07/06</strong> + <strong>v3 of 2009-07-06</strong> </p> <p> @@ -447,7 +611,7 @@ <h1>Change Log</h1> </ul> <p> - <strong>v4 of 2011/12/31</strong> + <strong>v4 of 2011-12-31</strong> </p> <p> @@ -460,7 +624,26 @@ <h1>Change Log</h1> </li> </ul> + <p> + <strong>v5 of ««TODO»»</strong> + </p> + + <p> + Introduced in CodeSnip ««TODO»» + </p> + + <ul> + <li> + Added support for lists with the <code class="value"><ol></code>, <code class="value"><ul></code> & <code class="value"><li></code> block tags. + </li> + <li> + Added entities: <code class="value">&times;</code>, <code class="value">&divide;</code>, <code class="value">&div;</code> <code class="value">&plusmn;</code>, <code class="value">&ne;</code>, <code class="value">&neq;</code>, <code class="value">&sum;</code>, <code class="value">&infin;</code>, <code class="value">&pound;</code>, <code class="value">&curren;</code>, <code class="value">&yen;</code>, <code class="value">&euro;</code>, <code class="value">&cent;</code>, <code class="value">&dagger;</code>, <code class="value">&ddagger;</code>, <code class="value">&Dagger;</code>, <code class="value">&hellip;</code>, <code class="value">&para;</code>, <code class="value">&sect;</code>, <code class="value">&reg;</code>, <code class="value">&frac14;</code>, <code class="value">frac12</code>, <code class="value">&half;</code>, <code class="value">&frac34;</code>, <code class="value">&micro;</code>, <code class="value">&deg;</code>, <code class="value">&laquo;</code>, <code class="value">&raquo;</code> & <code class="value">&iquest;</code>. + </li> + </ul> + </section> + + </body> </html> diff --git a/Src/Help/CSS/codesnip.css b/Src/Help/CSS/codesnip.css index 3460d1fd8..333f831cf 100644 --- a/Src/Help/CSS/codesnip.css +++ b/Src/Help/CSS/codesnip.css @@ -112,7 +112,7 @@ tr { margin: 0; } -td { +td, th { padding: 4px; margin: 1px; } @@ -122,6 +122,18 @@ table.menu { margin-left: 1em; } +table.bordered { + padding: 0; +} + +table.bordered { + background-color: #ccc; +} + +table.bordered th { + background-color: white; +} + table.menu td.img { width: 24px; height: 24px; @@ -141,10 +153,15 @@ table.menu td.mainitem { background-color: #eee; } -table.menu td.desc { +table.menu td.desc, +table.bordered td { background-color: white; } +table.bordered td.centre { + text-align: center; +} + pre.reml, code.reml { color: purple; diff --git a/Src/Help/HTML/reml.htm b/Src/Help/HTML/reml.htm index cac5e92a4..a3dc8260e 100644 --- a/Src/Help/HTML/reml.htm +++ b/Src/Help/HTML/reml.htm @@ -43,7 +43,8 @@ <h1> <p> <em>REML</em> is <em>CodeSnip</em>'s own little markup language that can be used to style the text of a snippet's description and / or extra - information. + information. The latest version is v5, which is backwards compatible with + all other versions. </p> <h2> Language Details @@ -66,43 +67,80 @@ <h3> <dd> Renders the enclosed markup as a heading. </dd> + <dt><code class="reml"><ol>...</ol></code></dt> + <dd> + Renders the enclosed HTML as an ordered list. Must contain + <code class="value"><li>...</li></code> blocks and nothing + else. + </dd> + <dt><code class="reml"><ul>...</ul></code></dt> + <dd> + Renders the enclosed HTML as an unordered list. Must contain + <code class="value"><li>...</li></code> blocks and nothing + else. + </dd> + <dt><code class="reml"><l1>...</li></code></dt> + <dd> + Renders the enclosed HTML as a list item. May only be used within + <code class="value"><ol>...</ol></code> and + <code class="value"><ul>...</ul></code> blocks. + </dd> </dl> <p> - The following rules apply to the use of - <code class="reml"><p></code> and - <code class="reml"><heading></code> + The following rules apply to the use of block level tags: </p> <ol> <li> - The tags must not be nested. + Must be matched, e.g. + <code class="value"><p></code> must have a matching + <code class="value"></p></code>. </li> <li> - The tags must be matched, e.g. - <code class="reml"><p></code> must have a matching - <code class="reml"></p></code>. + <code class="value"><p>...</p></code> and + <code class="value"><heading>...</heading></code> blocks + must not contain other block level tags. </li> <li> - All text should be embedded within block level tags, e.g. <span - class="code" - ><heading>heading</heading> <p>text</p></span> - or simply <span - class="code" - ><p>text</p></span>. + <code class="value"><ol>...</ol></code> and + <code class="value"><ul>...</ul></code> blocks must only + contain one or more + <code class="value"><li>...</li></code> blocks. </li> - </ol> - <p> - Here are some valid examples: - </p> - <ol> - <li class="extra-spacing"> - <code class="reml"><p>Hello World</p></code> + <li> + <code class="value"><li>...</li></code> blocks must not + contain + <code class="value"><p>...</p></code>, + <code class="value"><heading>...</heading></code> or other + <code class="value"><li>...</li></code> blocks directly, + but may contain + <code class="value"><ol>...</ol></code> and + <code class="value"><ul>...</ul></code> blocks. + </li> + <li> + All text should be embedded within + <code class="value"><p>...</p></code>, + <code class="value"><heading>...</heading></code> or + <code class="value"><li>...</li></code> block level tags, + e.g. <code class="value" + ><heading>heading</heading><p>text</p></code> + or simply <code class="value"><p>text</p></code>. </li> <li> - <code class="reml"><heading>Hello</heading><br> - <p>Hello World</p></code> + White space between blocks must be ignored. </li> </ol> <p> + Here is a valid example: + </p> + <pre class="sample"><p>Hello World</p> +<heading>Hello</heading> +<p>Hello World</p> +<ol> + <li>one</li> + <li>two</li> + <li>three</li> +</ol></pre> + <p> Srictly speaking, the following example is invalid code – the highlighted sections are in error, because they are not contained within block tags. @@ -187,30 +225,141 @@ <h3 id="entities"> A few other character entities are supported for convenience. Here is the complete list: </p> - <ul class="unspaced"> - <li> - <code class="reml">&lt;</code> for <code class="reml"><</code> - </li> - <li> - <code class="reml">&gt;</code> for <code class="reml">></code> - </li> - <li> - <code class="reml">&quot;</code> for - <code class="reml">"</code> - </li> - <li> - <code class="reml">&amp;</code> for <code class="reml">&</code> - </li> - <li> - <code class="reml">&copy;</code> for - <code class="reml">©</code> - </li> - </ul> + <table class="bordered" cellspacing="1" cellpadding="0"> + <thead> + <tr> + <th>Character Entity</th> + <th>Actual Character</th> + </tr> + </thead> + <tbody> + <tr> + <td><code>&amp;</code></td> + <td class="centre">&</td> + </tr> + <tr> + <td><code>&quot;</code></td> + <td class="centre">"</td> + </tr> + <tr> + <td><code>&gt;</code></td> + <td class="centre">></td> + </tr> + <tr> + <td><code>&lt;</code></td> + <td class="centre"><</td> + </tr> + <tr> + <td><code>&copy;</code></td> + <td class="centre">©</td> + </tr> + <tr> + <td><code>&times;</code></td> + <td class="centre">×</td> + </tr> + <tr> + <td><code>&divide;</code> or <code>&div;</code></td> + <td class="centre">÷</td> + </tr> + <tr> + <td><code>&plusmn;</code></td> + <td class="centre">±</td> + </tr> + <tr> + <td><code>&ne;</code> or <code>&neq;</code></td> + <td class="centre">≠</td> + </tr> + <tr> + <td><code>&sum;</code></td> + <td class="centre">∑</td> + </tr> + <tr> + <td><code>&infin;</code></td> + <td class="centre">∞</td> + </tr> + <tr> + <td><code>&pound;</code></td> + <td class="centre">£</td> + </tr> + <tr> + <td><code>&curren;</code></td> + <td class="centre">¤</td> + </tr> + <tr> + <td><code>&yen;</code></td> + <td class="centre">¥</td> + </tr> + <tr> + <td><code>&euro;</code></td> + <td class="centre">€</td> + </tr> + <tr> + <td><code>&cent;</code></td> + <td class="centre">¢</td> + </tr> + <tr> + <td><code>&dagger;</code></td> + <td class="centre">†</td> + </tr> + <tr> + <td><code>&ddagger;</code> or <code>&Dagger;</code></td> + <td class="centre">‡</td> + </tr> + <tr> + <td><code>&hellip;</code></td> + <td class="centre">…</td> + </tr> + <tr> + <td><code>&para;</code></td> + <td class="centre">¶</td> + </tr> + <tr> + <td><code>&sect;</code></td> + <td class="centre">§</td> + </tr> + <tr> + <td><code>&reg;</code></td> + <td class="centre">®</td> + </tr> + <tr> + <td><code>&frac14;</code></td> + <td class="centre">¼</td> + </tr> + <tr> + <td><code>&frac12;</code> or <code>&half;</code></td> + <td class="centre">½</td> + </tr> + <tr> + <td><code>&frac34;</code></td> + <td class="centre">¾</td> + </tr> + <tr> + <td><code>&micro;</code></td> + <td class="centre">µ</td> + </tr> + <tr> + <td><code>&deg;</code></td> + <td class="centre">°</td> + </tr> + <tr> + <td><code>&laquo;</code></td> + <td class="centre">«</td> + </tr> + <tr> + <td><code>&raquo;</code></td> + <td class="centre">»</td> + </tr> + <tr> + <td><code>&iquest;</code></td> + <td class="centre">¿</td> + </tr> + </tbody> + </table> <p> - By way of an example, if you want to display <code>x < y</code>, use: + By way of an example, if you want to display <code>x ≠ y</code>, use: </p> <p class="indent"> - <code class="reml">x &lt; y</code> + <code class="reml">x &ne; y</code> </p> <p> No other symbolic character entities are supported. @@ -226,4 +375,3 @@ <h3 id="entities"> </p> </body> </html> - From 1907d1b3dca6c4cdb5032d8b200b2e4ffa310c74 Mon Sep 17 00:00:00 2001 From: delphidabbler <delphidabbler@gmail.com> Date: Sun, 19 Jul 2020 03:21:41 +0100 Subject: [PATCH 11/34] Fix typos in comments In FmSnippetsEditorDlg.FrActiveTextEditor.pas --- Src/FmSnippetsEditorDlg.FrActiveTextEditor.pas | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Src/FmSnippetsEditorDlg.FrActiveTextEditor.pas b/Src/FmSnippetsEditorDlg.FrActiveTextEditor.pas index dacef9202..541f19416 100644 --- a/Src/FmSnippetsEditorDlg.FrActiveTextEditor.pas +++ b/Src/FmSnippetsEditorDlg.FrActiveTextEditor.pas @@ -129,7 +129,7 @@ function TSnippetsActiveTextEdFrame.ActiveTextToPlainText( begin // NOTE: we use IActiveText.ToString here, because there may be text not in // blocks and we want to see that: usual renderer will ignore that text. - // However all lines are trimmed and empty blanks are ingored. + // However all lines are trimmed and empty blanks are ignored. Lines := TIStringList.Create(ActiveText.ToString, EOL, False, True); Result := Lines.GetText(EOL2, False); // insert blank line between paras end; @@ -215,7 +215,7 @@ function TSnippetsActiveTextEdFrame.PlainTextToActiveText(Text: string): Paragraph: string; // each paragraph in paragraphs begin // NOTE: TSnippetExtraHelper.PlainTextToActiveText is not sufficient for use - // here since it ignores newlines and we want double newlines to separated + // here since it ignores newlines and we want double newlines to separate // paragraphs. Result := TActiveTextFactory.CreateActiveText; Text := StrTrim(Text); From 241d61c918d755042873fe5ec3de2895f8c158d1 Mon Sep 17 00:00:00 2001 From: delphidabbler <5164283+delphidabbler@users.noreply.github.com> Date: Thu, 15 Dec 2022 12:49:26 +0000 Subject: [PATCH 12/34] Update TCSSSelector for fluid chaining of methods Modify TCSSSelector.AddProperty to reference to current TCSSSelector instance to permit chaining the method to others that return aborts TCSSSelector reference. Add new TCSSSelector.AddPropertyIf fluid method to permit a choice of two different properties to be added depending on the value of a Boolean variable. This was created prevent chaining from being broken when a conditional statement was encountered. Closes #73 --- Src/UCSSBuilder.pas | 41 +++++++++++++++++++++++++++++++---------- 1 file changed, 31 insertions(+), 10 deletions(-) diff --git a/Src/UCSSBuilder.pas b/Src/UCSSBuilder.pas index 662e44dd6..172f8b07d 100644 --- a/Src/UCSSBuilder.pas +++ b/Src/UCSSBuilder.pas @@ -3,7 +3,7 @@ * v. 2.0. If a copy of the MPL was not distributed with this file, You can * obtain one at https://mozilla.org/MPL/2.0/ * - * Copyright (C) 2005-2021, Peter Johnson (gravatar.com/delphidabbler). + * Copyright (C) 2005-2022, Peter Johnson (gravatar.com/delphidabbler). * * Classes that help create and manage cascading style sheet code. } @@ -46,12 +46,25 @@ TCSSSelector = class(TObject) {Checks whether the selector is empty, i.e. contains no code. @return True if selector is empty and false if not. } - procedure AddProperty(const CSSProp: string); - {Adds a new CSS property to the selector. - @param CSSProp [in] CSS property to be added. - } + /// <summary>Adds a new CSS property to the selector.</summary> + /// <param name="CSSProp">string [in] Property definition.</param> + /// <returns>TCSSSelector. Class instance returned to enable the method to + /// be chained.</returns> + function AddProperty(const CSSProp: string): TCSSSelector; + /// <summary>Adds a new CSS property to the selector depending on a given + /// condition.</summary> + /// <param name="Condition">Boolean [in] Condition that determines which + /// CSS property is added.</param> + /// <param name="CSSPropTrue">string [in] CSS property that is added when + /// Condition is True.</param> + /// <param name="CSSPropFalse">string [in] CSS property that is added when + /// Condition is False.</param> + /// <returns>TCSSSelector. Class instance returned to enable the method to + /// be chained.</returns> + function AddPropertyIf(const Condition: Boolean; + const CSSPropTrue: string; const CSSPropFalse: string = ''): TCSSSelector; + /// <summary>Name of selector.</summary> property Selector: string read fSelector; - {Name of selector} end; { @@ -112,12 +125,20 @@ implementation { TCSSSelector } -procedure TCSSSelector.AddProperty(const CSSProp: string); - {Adds a new CSS property to the selector. - @param CSSProp [in] CSS property to be added. - } +function TCSSSelector.AddProperty(const CSSProp: string): TCSSSelector; begin fProperties.Add(CSSProp); + Result := Self; +end; + +function TCSSSelector.AddPropertyIf(const Condition: Boolean; + const CSSPropTrue: string; const CSSPropFalse: string): TCSSSelector; +begin + if Condition then + AddProperty(CSSPropTrue) + else if CSSPropFalse <> '' then + AddProperty(CSSPropFalse); + Result := Self; end; function TCSSSelector.AsString: string; From 4dca160f800709365780aae12d3077b43d99eb3d Mon Sep 17 00:00:00 2001 From: delphidabbler <5164283+delphidabbler@users.noreply.github.com> Date: Thu, 15 Dec 2022 12:58:23 +0000 Subject: [PATCH 13/34] Refactor units to use fluid TCSSSelector methods Various units that were using a "with..do" statement to add properties to a CSS selector were refactored to get rid of the "with" statement and to use chains of TCSSSelector AddProperty & AddPropertyIf method calls instead. --- Src/FirstRun.FmWhatsNew.FrHTML.pas | 46 +++--- Src/FmAboutDlg.pas | 48 +++--- Src/FmActiveTextPreviewDlg.pas | 113 ++++++------- Src/FmDBUpdateDlg.pas | 10 +- Src/FmSWAGImportDlg.pas | 16 +- Src/FrBrowserBase.pas | 94 +++++------ Src/FrDetailView.pas | 245 ++++++++++++++--------------- Src/FrEasterEgg.pas | 6 +- Src/FrHTMLDlg.pas | 84 ++++++---- Src/FrHTMLPreview.pas | 6 +- Src/Hiliter.UCSS.pas | 25 ++- 11 files changed, 325 insertions(+), 368 deletions(-) diff --git a/Src/FirstRun.FmWhatsNew.FrHTML.pas b/Src/FirstRun.FmWhatsNew.FrHTML.pas index 69110c448..749ea3122 100644 --- a/Src/FirstRun.FmWhatsNew.FrHTML.pas +++ b/Src/FirstRun.FmWhatsNew.FrHTML.pas @@ -3,7 +3,7 @@ * v. 2.0. If a copy of the MPL was not distributed with this file, You can * obtain one at https://mozilla.org/MPL/2.0/ * - * Copyright (C) 2020-2021, Peter Johnson (gravatar.com/delphidabbler). + * Copyright (C) 2020-2022, Peter Johnson (gravatar.com/delphidabbler). * * Frame that displays HTML of "what's new" message in a TWebBrowser control. } @@ -67,34 +67,24 @@ procedure TWhatsNewHTMLFrame.BuildCSS(const CSSBuilder: TCSSBuilder); try TFontHelper.SetContentFont(CSSFont); CSSFont.Size := CSSFont.Size + 2; - with CSSBuilder.AddSelector('body') do - begin - AddProperty(TCSS.FontProps(CSSFont)); - AddProperty(TCSS.MarginProp(0, 8, 0, 8)); - end; - with CSSBuilder.AddSelector('.lead') do - begin - AddProperty(TCSS.FontSizeProp(CSSFont.Size + 2)); - AddProperty(TCSS.FontWeightProp(cfwBold)); - AddProperty(TCSS.ColorProp($233bc2)); - end; + CSSBuilder.AddSelector('body') + .AddProperty(TCSS.FontProps(CSSFont)) + .AddProperty(TCSS.MarginProp(0, 8, 0, 8)); + CSSBuilder.AddSelector('.lead') + .AddProperty(TCSS.FontSizeProp(CSSFont.Size + 2)) + .AddProperty(TCSS.FontWeightProp(cfwBold)) + .AddProperty(TCSS.ColorProp($233bc2)); // Sets paragraph margins and padding - with CSSBuilder.AddSelector('p') do - begin - AddProperty(TCSS.MarginProp(cssTop, 6)); - AddProperty(TCSS.MarginProp(cssBottom, 0)); - AddProperty(TCSS.PaddingProp(0)); - end; - with CSSBuilder.AddSelector('ul') do - begin - AddProperty(TCSS.MarginProp(cssTop, 6)); - AddProperty(TCSS.MarginProp(cssBottom, 0)); - AddProperty(TCSS.PaddingProp(0)); - end; - with CSSBuilder.AddSelector('li') do - begin - AddProperty(TCSS.MarginProp(cssTop, 6)); - end; + CSSBuilder.AddSelector('p') + .AddProperty(TCSS.MarginProp(cssTop, 6)) + .AddProperty(TCSS.MarginProp(cssBottom, 0)) + .AddProperty(TCSS.PaddingProp(0)); + CSSBuilder.AddSelector('ul') + .AddProperty(TCSS.MarginProp(cssTop, 6)) + .AddProperty(TCSS.MarginProp(cssBottom, 0)) + .AddProperty(TCSS.PaddingProp(0)); + CSSBuilder.AddSelector('li') + .AddProperty(TCSS.MarginProp(cssTop, 6)); finally CSSFont.Free; end; diff --git a/Src/FmAboutDlg.pas b/Src/FmAboutDlg.pas index d278b6810..421495c56 100644 --- a/Src/FmAboutDlg.pas +++ b/Src/FmAboutDlg.pas @@ -3,7 +3,7 @@ * v. 2.0. If a copy of the MPL was not distributed with this file, You can * obtain one at https://mozilla.org/MPL/2.0/ * - * Copyright (C) 2005-2021, Peter Johnson (gravatar.com/delphidabbler). + * Copyright (C) 2005-2022, Peter Johnson (gravatar.com/delphidabbler). * * Implements the program's About dialogue box. } @@ -524,40 +524,36 @@ procedure TAboutDlg.UpdateDetailCSS(Sender: TObject; ContentFont: TFont; // font used for content begin // Modify body's margin and, for themed windows, background colour - with CSSBuilder.Selectors['body'] do - begin - ContentFont := TFont.Create; - try - TFontHelper.SetContentFont(ContentFont); - AddProperty(TCSS.FontProps(ContentFont)); - if ThemeServicesEx.ThemesEnabled then - AddProperty(TCSS.BackgroundColorProp(ThemeServicesEx.GetTabBodyColour)); - AddProperty(UCSSUtils.TCSS.MarginProp(0, 2, 6, 2)); - finally - FreeAndNil(ContentFont); - end; + ContentFont := TFont.Create; + try + TFontHelper.SetContentFont(ContentFont); + CSSBuilder.Selectors['body'] + .AddProperty(TCSS.FontProps(ContentFont)) + .AddProperty(UCSSUtils.TCSS.MarginProp(0, 2, 6, 2)) + .AddPropertyIf( + ThemeServicesEx.ThemesEnabled, + TCSS.BackgroundColorProp(ThemeServicesEx.GetTabBodyColour) + ); + finally + FreeAndNil(ContentFont); end; // Put border round scroll box - with CSSBuilder.AddSelector('.scrollbox') do - AddProperty(UCSSUtils.TCSS.BorderProp(cssAll, 1, cbsSolid, clBorder)); + CSSBuilder.AddSelector('.scrollbox') + .AddProperty(UCSSUtils.TCSS.BorderProp(cssAll, 1, cbsSolid, clBorder)); // Set colours and font style of contributors and testers headings - with CSSBuilder.AddSelector('.contrib-head, .tester-head') do - begin - AddProperty(TCSS.BackgroundColorProp(clBtnFace)); - AddProperty(TCSS.ColorProp(clBtnText)); - AddProperty(TCSS.FontWeightProp(cfwBold)); - end; + CSSBuilder.AddSelector('.contrib-head, .tester-head') + .AddProperty(TCSS.BackgroundColorProp(clBtnFace)) + .AddProperty(TCSS.ColorProp(clBtnText)) + .AddProperty(TCSS.FontWeightProp(cfwBold)); end; procedure TAboutDlg.UpdateTitleCSS(Sender: TObject; const CSSBuilder: TCSSBuilder); begin // Set body colour, and put border round it - with CSSBuilder.Selectors['body'] do - begin - AddProperty(TCSS.BackgroundColorProp(clWindow)); - AddProperty(TCSS.PaddingProp(4)); - end; + CSSBuilder.Selectors['body'] + .AddProperty(TCSS.BackgroundColorProp(clWindow)) + .AddProperty(TCSS.PaddingProp(4)); end; procedure TAboutDlg.ViewConfigFile(const FileName, DlgTitle: string); diff --git a/Src/FmActiveTextPreviewDlg.pas b/Src/FmActiveTextPreviewDlg.pas index e9bd1071f..d35db88b1 100644 --- a/Src/FmActiveTextPreviewDlg.pas +++ b/Src/FmActiveTextPreviewDlg.pas @@ -3,7 +3,7 @@ * v. 2.0. If a copy of the MPL was not distributed with this file, You can * obtain one at https://mozilla.org/MPL/2.0/ * - * Copyright (C) 2009-2021, Peter Johnson (gravatar.com/delphidabbler). + * Copyright (C) 2009-2022, Peter Johnson (gravatar.com/delphidabbler). * * Implements a dialogue box that displays active text rendered from REML markup * or plain text. @@ -214,75 +214,62 @@ procedure TActiveTextPreviewDlg.UpdateCSS(Sender: TObject; const MaxHTMLHeight = 240; // max height of rendered HTML var - ContentFont: TFont; // font used for #content tab + ContentFont: TFont; // font used for #content tab begin ContentFont := TFont.Create; try TFontHelper.SetContentFont(ContentFont); // Set rendered REML container - with CSSBuilder.EnsureSelector('#content') do - begin - AddProperty(TCSS.FontProps(ContentFont)); - AddProperty(TCSS.BackgroundColorProp(clWindow)); - AddProperty(TCSS.PaddingProp(0, 6, 6, 6)); - AddProperty(TCSS.MarginProp(cssTop, 6)); - AddProperty(TCSS.BorderProp(cssAll, 1, cbsSolid, clBorder)); - AddProperty(TCSS.OverflowProp(covAuto)); - AddProperty(TCSS.WidthProp(cluAuto, 0)); - // Use height instead of maxheight if IE 6 or lower - if TIEInfo.SupportsCSSMaxHeight then - AddProperty(TCSS.MaxHeightProp(MaxHTMLHeight)) - else - AddProperty(TCSS.HeightProp(MaxHTMLHeight)); - end; - with CSSBuilder.EnsureSelector('.active-text h2') do - begin - AddProperty(TCSS.MarginProp(4, 0, 0, 0)); - AddProperty(TCSS.FontWeightProp(cfwBold)); - AddProperty(TCSS.FontSizeProp(ContentFont.Size + 1)); - end; - with CSSBuilder.EnsureSelector('.active-text p') do - AddProperty(TCSS.MarginProp(4, 0, 0, 0)); + CSSBuilder.EnsureSelector('#content') + .AddProperty(TCSS.FontProps(ContentFont)) + .AddProperty(TCSS.BackgroundColorProp(clWindow)) + .AddProperty(TCSS.PaddingProp(0, 6, 6, 6)) + .AddProperty(TCSS.MarginProp(cssTop, 6)) + .AddProperty(TCSS.BorderProp(cssAll, 1, cbsSolid, clBorder)) + .AddProperty(TCSS.OverflowProp(covAuto)) + .AddProperty(TCSS.WidthProp(cluAuto, 0)) + .AddPropertyIf( + TIEInfo.SupportsCSSMaxHeight, + TCSS.MaxHeightProp(MaxHTMLHeight), + TCSS.HeightProp(MaxHTMLHeight) + ); + CSSBuilder.EnsureSelector('.active-text h2') + .AddProperty(TCSS.MarginProp(4, 0, 0, 0)) + .AddProperty(TCSS.FontWeightProp(cfwBold)) + .AddProperty(TCSS.FontSizeProp(ContentFont.Size + 1)); + CSSBuilder.EnsureSelector('.active-text p') + .AddProperty(TCSS.MarginProp(4, 0, 0, 0)); // Show or hide text about links depending on if links are present - with CSSBuilder.EnsureSelector('#linktext') do - begin - if ContainsLinks then - AddProperty(TCSS.DisplayProp(cdsInline)) - else - AddProperty(TCSS.DisplayProp(cdsNone)); - end; + CSSBuilder.EnsureSelector('#linktext') + .AddPropertyIf( + ContainsLinks, TCSS.DisplayProp(cdsInline), TCSS.DisplayProp(cdsNone) + ); // Set up lists - with CSSBuilder.EnsureSelector('.active-text ul') do - begin - AddProperty(TCSS.MarginProp(cssAll, 0)); - AddProperty(TCSS.MarginProp(cssTop, 4)); - AddProperty(TCSS.PaddingProp(cssAll, 0)); - AddProperty(TCSS.PaddingProp(cssLeft, 24)); - AddProperty(TCSS.ListStylePositionProp(clspOutside)); - AddProperty(TCSS.ListStyleTypeProp(clstDisc)); - end; - with CSSBuilder.EnsureSelector('.active-text ol') do - begin - AddProperty(TCSS.MarginProp(cssAll, 0)); - AddProperty(TCSS.MarginProp(cssTop, 4)); - AddProperty(TCSS.PaddingProp(cssAll, 0)); - AddProperty(TCSS.PaddingProp(cssLeft, 32)); - AddProperty(TCSS.ListStylePositionProp(clspOutside)); - AddProperty(TCSS.ListStyleTypeProp(clstDecimal)); - end; - with CSSBuilder.EnsureSelector('.active-text li') do - begin - AddProperty(TCSS.PaddingProp(cssAll, 0)); - AddProperty(TCSS.MarginProp(cssAll, 0)); - end; - with CSSBuilder.EnsureSelector('.active-text li ol') do - AddProperty(TCSS.MarginProp(cssTop, 0)); - with CSSBuilder.EnsureSelector('.active-text li ul') do - AddProperty(TCSS.MarginProp(cssTop, 0)); - with CSSBuilder.EnsureSelector('.active-text ul li') do - AddProperty(TCSS.PaddingProp(cssLeft, 8)); - with CSSBuilder.EnsureSelector('.active-text ul li ol li') do - AddProperty(TCSS.PaddingProp(cssLeft, 0)); + CSSBuilder.EnsureSelector('.active-text ul') + .AddProperty(TCSS.MarginProp(cssAll, 0)) + .AddProperty(TCSS.MarginProp(cssTop, 4)) + .AddProperty(TCSS.PaddingProp(cssAll, 0)) + .AddProperty(TCSS.PaddingProp(cssLeft, 24)) + .AddProperty(TCSS.ListStylePositionProp(clspOutside)) + .AddProperty(TCSS.ListStyleTypeProp(clstDisc)); + CSSBuilder.EnsureSelector('.active-text ol') + .AddProperty(TCSS.MarginProp(cssAll, 0)) + .AddProperty(TCSS.MarginProp(cssTop, 4)) + .AddProperty(TCSS.PaddingProp(cssAll, 0)) + .AddProperty(TCSS.PaddingProp(cssLeft, 32)) + .AddProperty(TCSS.ListStylePositionProp(clspOutside)) + .AddProperty(TCSS.ListStyleTypeProp(clstDecimal)); + CSSBuilder.EnsureSelector('.active-text li') + .AddProperty(TCSS.PaddingProp(cssAll, 0)) + .AddProperty(TCSS.MarginProp(cssAll, 0)); + CSSBuilder.EnsureSelector('.active-text li ol') + .AddProperty(TCSS.MarginProp(cssTop, 0)); + CSSBuilder.EnsureSelector('.active-text li ul') + .AddProperty(TCSS.MarginProp(cssTop, 0)); + CSSBuilder.EnsureSelector('.active-text ul li') + .AddProperty(TCSS.PaddingProp(cssLeft, 8)); + CSSBuilder.EnsureSelector('.active-text ul li ol li') + .AddProperty(TCSS.PaddingProp(cssLeft, 0)); finally ContentFont.Free; end; diff --git a/Src/FmDBUpdateDlg.pas b/Src/FmDBUpdateDlg.pas index 14e7dddec..80cd905b2 100644 --- a/Src/FmDBUpdateDlg.pas +++ b/Src/FmDBUpdateDlg.pas @@ -3,7 +3,7 @@ * v. 2.0. If a copy of the MPL was not distributed with this file, You can * obtain one at https://mozilla.org/MPL/2.0/ * - * Copyright (C) 2005-2021, Peter Johnson (gravatar.com/delphidabbler). + * Copyright (C) 2005-2022, Peter Johnson (gravatar.com/delphidabbler). * * Implements a wizard dialogue box that handles the updating of the main * DelphiDabbler Code Snippets database. @@ -254,11 +254,9 @@ procedure TDBUpdateDlg.BuildCSS(Sender: TObject; begin inherited; // Create .framed border style - with CSSBuilder.AddSelector('.framed') do - begin - AddProperty(TCSS.BorderProp(cssAll, 1, cbsSolid, clBorder)); - AddProperty(TCSS.PaddingProp(4)); - end; + CSSBuilder.AddSelector('.framed') + .AddProperty(TCSS.BorderProp(cssAll, 1, cbsSolid, clBorder)) + .AddProperty(TCSS.PaddingProp(4)); end; procedure TDBUpdateDlg.ConfigForm; diff --git a/Src/FmSWAGImportDlg.pas b/Src/FmSWAGImportDlg.pas index 421840eb8..ceb931628 100644 --- a/Src/FmSWAGImportDlg.pas +++ b/Src/FmSWAGImportDlg.pas @@ -3,7 +3,7 @@ * v. 2.0. If a copy of the MPL was not distributed with this file, You can * obtain one at https://mozilla.org/MPL/2.0/ * - * Copyright (C) 2013-2021, Peter Johnson (gravatar.com/delphidabbler). + * Copyright (C) 2013-2022, Peter Johnson (gravatar.com/delphidabbler). * * Implements a wizard dialogue box that lets the user select and import * packets from the DelphiDabbler implementation of the SWAG Pascal archive as @@ -422,15 +422,13 @@ procedure TSWAGImportDlg.BuildCSS(Sender: TObject; begin inherited; // Set body text spacing - with CSSBuilder.Selectors['body'] do - AddProperty(TCSS.LineHeightProp(120)); + CSSBuilder.Selectors['body'] + .AddProperty(TCSS.LineHeightProp(120)); // Create .framed border style - with CSSBuilder.AddSelector('.framed') do - begin - AddProperty(TCSS.BorderProp(cssAll, 1, cbsSolid, clBorder)); - AddProperty(TCSS.PaddingProp(0, 4, 4, 4)); - AddProperty(TCSS.MarginProp(cssTop, 4)); - end; + CSSBuilder.AddSelector('.framed') + .AddProperty(TCSS.BorderProp(cssAll, 1, cbsSolid, clBorder)) + .AddProperty(TCSS.PaddingProp(0, 4, 4, 4)) + .AddProperty(TCSS.MarginProp(cssTop, 4)); end; procedure TSWAGImportDlg.clbSelectPacketsClickCheck(Sender: TObject); diff --git a/Src/FrBrowserBase.pas b/Src/FrBrowserBase.pas index 5f07709ff..479b1c828 100644 --- a/Src/FrBrowserBase.pas +++ b/Src/FrBrowserBase.pas @@ -3,7 +3,7 @@ * v. 2.0. If a copy of the MPL was not distributed with this file, You can * obtain one at https://mozilla.org/MPL/2.0/ * - * Copyright (C) 2005-2021, Peter Johnson (gravatar.com/delphidabbler). + * Copyright (C) 2005-2022, Peter Johnson (gravatar.com/delphidabbler). * * Implements a base class for all frames that contain a web browser control. } @@ -193,76 +193,56 @@ procedure TBrowserBaseFrame.BuildCSS(const CSSBuilder: TCSSBuilder); CSSFont: TFont; begin // <img> tag style: no borders - with CSSBuilder.AddSelector('img') do - AddProperty(TCSS.HideBorderProp(cssAll)); + CSSBuilder.AddSelector('img') + .AddProperty(TCSS.HideBorderProp(cssAll)); // Default <a> tag style: fall back links for unknown link classes. // Each link type is expected to define own colour as a minimum. - with CSSBuilder.AddSelector('a') do - begin - AddProperty(TCSS.ColorProp(clDefaultLink)); - AddProperty(TCSS.TextDecorationProp([ctdUnderline])); - end; + CSSBuilder.AddSelector('a') + .AddProperty(TCSS.ColorProp(clDefaultLink)) + .AddProperty(TCSS.TextDecorationProp([ctdUnderline])); // <a class="help-link"> override - with CSSBuilder.AddSelector('a.help-link') do - begin - AddProperty(TCSS.ColorProp(clHelpLink)); - end; + CSSBuilder.AddSelector('a.help-link') + .AddProperty(TCSS.ColorProp(clHelpLink)); // <a class="snippet-link"> and <a class="category-link"> overrides - with CSSBuilder.AddSelector('a.snippet-link, a.category-link') do - begin - AddProperty(TCSS.ColorProp(clDBLink)); - AddProperty(TCSS.FontStyleProp(cfsItalic)); - AddProperty(TCSS.TextDecorationProp([ctdNone])); - end; - with CSSBuilder.AddSelector('a:hover.snippet-link, a:hover.category-link') do - begin - AddProperty(TCSS.BorderProp(cssBottom, 1, cbsDotted, clDBLink)); - end; + CSSBuilder.AddSelector('a.snippet-link, a.category-link') + .AddProperty(TCSS.ColorProp(clDBLink)) + .AddProperty(TCSS.FontStyleProp(cfsItalic)) + .AddProperty(TCSS.TextDecorationProp([ctdNone])); + CSSBuilder.AddSelector('a:hover.snippet-link, a:hover.category-link') + .AddProperty(TCSS.BorderProp(cssBottom, 1, cbsDotted, clDBLink)); // <a class="command-link"> override - with CSSBuilder.AddSelector('a.command-link') do - begin - AddProperty(TCSS.ColorProp(clCommandLink)); - AddProperty(TCSS.FontStyleProp(cfsItalic)); - AddProperty(TCSS.TextDecorationProp([ctdNone])); - end; - with CSSBuilder.AddSelector('a:hover.command-link') do - begin - AddProperty(TCSS.BorderProp(cssBottom, 1, cbsDotted, clCommandLink)); - end; - with CSSBuilder.AddSelector('.no-link-decoration a:hover') do - AddProperty(TCSS.HideBorderProp(cssBottom)); + CSSBuilder.AddSelector('a.command-link') + .AddProperty(TCSS.ColorProp(clCommandLink)) + .AddProperty(TCSS.FontStyleProp(cfsItalic)) + .AddProperty(TCSS.TextDecorationProp([ctdNone])); + CSSBuilder.AddSelector('a:hover.command-link') + .AddProperty(TCSS.BorderProp(cssBottom, 1, cbsDotted, clCommandLink)); + CSSBuilder.AddSelector('.no-link-decoration a:hover') + .AddProperty(TCSS.HideBorderProp(cssBottom)); // <a class="external-link"> override - with CSSBuilder.AddSelector('a.external-link') do - begin - AddProperty(TCSS.ColorProp(clExternalLink)); - end; + CSSBuilder.AddSelector('a.external-link') + .AddProperty(TCSS.ColorProp(clExternalLink)); // <var> tag style - with CSSBuilder.AddSelector('var') do - begin - AddProperty(TCSS.ColorProp(clVarText)); - AddProperty(TCSS.FontStyleProp(cfsItalic)); - end; + CSSBuilder.AddSelector('var') + .AddProperty(TCSS.ColorProp(clVarText)) + .AddProperty(TCSS.FontStyleProp(cfsItalic)); // <code> tag style - with CSSBuilder.AddSelector('code') do - begin - CSSFont := TFont.Create; - try - TFontHelper.SetDefaultMonoFont(CSSFont); - AddProperty(TCSS.FontProps(CSSFont)); - finally - CSSFont.Free; - end; + CSSFont := TFont.Create; + try + TFontHelper.SetDefaultMonoFont(CSSFont); + CSSBuilder.AddSelector('code') + .AddProperty(TCSS.FontProps(CSSFont)); + finally + CSSFont.Free; end; // .warning class style: mainly for use inline - with CSSBuilder.AddSelector('.warning') do - begin - AddProperty(TCSS.ColorProp(clWarningText)); - AddProperty(TCSS.FontWeightProp(cfwBold)); - end; + CSSBuilder.AddSelector('.warning') + .AddProperty(TCSS.ColorProp(clWarningText)) + .AddProperty(TCSS.FontWeightProp(cfwBold)); end; function TBrowserBaseFrame.CanCopy: Boolean; diff --git a/Src/FrDetailView.pas b/Src/FrDetailView.pas index 7c21d29ba..bb68e32d5 100644 --- a/Src/FrDetailView.pas +++ b/Src/FrDetailView.pas @@ -143,6 +143,7 @@ procedure TDetailViewFrame.BuildCSS(const CSSBuilder: TCSSBuilder); ContentFont := TFont.Create; TFontHelper.SetDefaultMonoFont(MonoFont); TFontHelper.SetContentFont(ContentFont); + // Must do next two lines before changing content & mono font sizes DefContentFontSize := ContentFont.Size; DefMonoFontSize := MonoFont.Size; @@ -150,96 +151,92 @@ procedure TDetailViewFrame.BuildCSS(const CSSBuilder: TCSSBuilder); MonoToContentFontRatio := DefMonoFontSize / DefContentFontSize; ContentFont.Size := Preferences.DetailFontSize; MonoFont.Size := Round(ContentFont.Size * MonoToContentFontRatio); + // Set body style to use program's font and window colour - with CSSBuilder.AddSelector('body') do - begin - CSSFont.Assign(ContentFont); - AddProperty(TCSS.FontProps(CSSFont)); - AddProperty(TCSS.BackgroundColorProp(clWindow)); - end; - with CSSBuilder.Selectors['code'] do - begin - CSSFont.Assign(MonoFont); - AddProperty(TCSS.FontProps(CSSFont)); - end; + CSSFont.Assign(ContentFont); + CSSBuilder.AddSelector('body') + .AddProperty(TCSS.FontProps(CSSFont)) + .AddProperty(TCSS.BackgroundColorProp(clWindow)); + + // Set mono spaced font for <code> tags + CSSFont.Assign(MonoFont); + CSSBuilder.Selectors['code'] + .AddProperty(TCSS.FontProps(CSSFont)); + // Set table to use required font - with CSSBuilder.AddSelector('table') do - begin - CSSFont.Assign(ContentFont); - AddProperty(TCSS.FontProps(CSSFont)); - AddProperty(TCSS.BackgroundColorProp(clBorder)); - end; + CSSFont.Assign(ContentFont); + CSSBuilder.AddSelector('table') + .AddProperty(TCSS.FontProps(CSSFont)) + .AddProperty(TCSS.BackgroundColorProp(clBorder)); + // Set default table cell colour (must be different to table to get border) - with CSSBuilder.AddSelector('td') do - AddProperty(TCSS.BackgroundColorProp(clWindow)); + CSSBuilder.AddSelector('td') + .AddProperty(TCSS.BackgroundColorProp(clWindow)); + // Sets H1 heading font size and border - with CSSBuilder.AddSelector('h1') do - begin - CSSFont.Assign(ContentFont); - CSSFont.Size := CSSFont.Size + Max( - Round(2 * ContentFontScaleFactor * CSSFont.Size), 2 - ); - CSSFont.Style := [fsBold]; - AddProperty(TCSS.FontProps(CSSFont)); - AddProperty(TCSS.BorderProp(cssBottom, 1, cbsSolid, clBorder)); - end; + CSSFont.Assign(ContentFont); + CSSFont.Size := CSSFont.Size + Max( + Round(2 * ContentFontScaleFactor * CSSFont.Size), 2 + ); + CSSFont.Style := [fsBold]; + CSSBuilder.AddSelector('h1') + .AddProperty(TCSS.FontProps(CSSFont)) + .AddProperty(TCSS.BorderProp(cssBottom, 1, cbsSolid, clBorder)); + // Sets H2 heading font size and border - with CSSBuilder.AddSelector('h2') do - begin - CSSFont.Assign(ContentFont); - CSSFont.Style := [fsBold]; - AddProperty(TCSS.FontProps(CSSFont)); - end; + CSSFont.Assign(ContentFont); + CSSFont.Style := [fsBold]; + CSSBuilder.AddSelector('h2') + .AddProperty(TCSS.FontProps(CSSFont)); + // Set H2 heading font for use in rendered active text - with CSSBuilder.AddSelector('.active-text h2') do - begin - CSSFont.Assign(ContentFont); - CSSFont.Style := [fsBold]; - CSSFont.Size := CSSFont.Size + Max( - Round(ContentFontScaleFactor * CSSFont.Size), 1 - ); - AddProperty(TCSS.FontProps(CSSFont)); - end; + CSSFont.Assign(ContentFont); + CSSFont.Style := [fsBold]; + CSSFont.Size := CSSFont.Size + Max( + Round(ContentFontScaleFactor * CSSFont.Size), 1 + ); + CSSBuilder.AddSelector('.active-text h2') + .AddProperty(TCSS.FontProps(CSSFont)); + // Set CODE tag within H2 heading for use in rendered active text - with CSSBuilder.AddSelector('.active-text h2 code') do - begin - CSSFont.Assign(MonoFont); - CSSFont.Style := [fsBold]; - CSSFont.Size := CSSFont.Size + Max( - Round(ContentFontScaleFactor * CSSFont.Size), 1 - ); - AddProperty(TCSS.FontProps(CSSFont)); - end; + CSSFont.Assign(MonoFont); + CSSFont.Style := [fsBold]; + CSSFont.Size := CSSFont.Size + Max( + Round(ContentFontScaleFactor * CSSFont.Size), 1 + ); + CSSBuilder.AddSelector('.active-text h2 code') + .AddProperty(TCSS.FontProps(CSSFont)); + // Set H2 heading font for use in rendered active text in snippet list table - with CSSBuilder.AddSelector('.snippet-list .active-text h2') do - begin - CSSFont.Assign(ContentFont); - CSSFont.Style := [fsBold]; - AddProperty(TCSS.FontProps(CSSFont)); - end; - // Set CODE within H2 heading font for use in rendered active text in + CSSFont.Assign(ContentFont); + CSSFont.Style := [fsBold]; + CSSBuilder.AddSelector('.snippet-list .active-text h2') + .AddProperty(TCSS.FontProps(CSSFont)); + + // Set <code> within H2 heading font for use in rendered active text in // snippet list table - with CSSBuilder.AddSelector('.snippet-list .active-text h2 code') do - begin - CSSFont.Assign(MonoFont); - CSSFont.Style := [fsBold]; - AddProperty(TCSS.FontProps(CSSFont)); - end; + CSSFont.Assign(MonoFont); + CSSFont.Style := [fsBold]; + CSSBuilder.AddSelector('.snippet-list .active-text h2 code') + .AddProperty(TCSS.FontProps(CSSFont)); + // Style of box that appears around clickable options (or actions) - with CSSBuilder.AddSelector('.optionbox') do - AddProperty(TCSS.BorderProp(cssAll, 1, cbsSolid, clBorder)); - with CSSBuilder.AddSelector('.userdb') do - AddProperty(TCSS.ColorProp(Preferences.DBHeadingColours[True])); - with CSSBuilder.AddSelector('.maindb') do - AddProperty(TCSS.ColorProp(Preferences.DBHeadingColours[False])); + CSSBuilder.AddSelector('.optionbox') + .AddProperty(TCSS.BorderProp(cssAll, 1, cbsSolid, clBorder)); + + // Heading colours for user & main databases + CSSBuilder.AddSelector('.userdb') + .AddProperty(TCSS.ColorProp(Preferences.DBHeadingColours[True])); + CSSBuilder.AddSelector('.maindb') + .AddProperty(TCSS.ColorProp(Preferences.DBHeadingColours[False])); + // Sets CSS for style of New Tab text - with CSSBuilder.AddSelector('#newtab') do - begin - CSSFont.Assign(ContentFont); - CSSFont.Size := 36 + Round(36 * ContentFontScaleFactor); - CSSFont.Color := clNewTabText; - AddProperty(TCSS.FontProps(CSSFont)); - end; + CSSFont.Assign(ContentFont); + CSSFont.Size := 36 + Round(36 * ContentFontScaleFactor); + CSSFont.Color := clNewTabText; + CSSBuilder.AddSelector('#newtab') + .AddProperty(TCSS.FontProps(CSSFont)); + // Sets text styles and colours used by syntax highlighter HiliteAttrs := THiliteAttrsFactory.CreateUserAttrs; with THiliterCSS.Create(HiliteAttrs) do @@ -248,55 +245,53 @@ procedure TDetailViewFrame.BuildCSS(const CSSBuilder: TCSSBuilder); finally Free; end; + // Adjust .pas-source class to use required background colour - with CSSBuilder.Selectors['.' + THiliterCSS.GetMainCSSClassName] do - begin - AddProperty(TCSS.BackgroundColorProp(Preferences.SourceCodeBGcolour)); - if TIEInfo.SupportsCSSOverflowX then - AddProperty(TCSS.OverflowProp(covAuto, codX)); - end; + CSSBuilder.Selectors['.' + THiliterCSS.GetMainCSSClassName] + .AddProperty(TCSS.BackgroundColorProp(Preferences.SourceCodeBGcolour)) + .AddPropertyIf( + TIEInfo.SupportsCSSOverflowX, TCSS.OverflowProp(covAuto, codX) + ); + + // Address IE peculiarities if TIEInfo.SupportsCSSOverflowX then - begin - with CSSBuilder.AddSelector('#compile-results') do - AddProperty(TCSS.OverflowProp(covAuto, codX)); - end; - with CSSBuilder.AddSelector('.comptable th') do - begin - AddProperty(TCSS.BackgroundColorProp(clCompTblHeadBg)); - AddProperty(TCSS.FontWeightProp(cfwNormal)); - end; + CSSBuilder.AddSelector('#compile-results') + .AddProperty(TCSS.OverflowProp(covAuto, codX)); + + // Compiler table heading + CSSBuilder.AddSelector('.comptable th') + .AddProperty(TCSS.BackgroundColorProp(clCompTblHeadBg)) + .AddProperty(TCSS.FontWeightProp(cfwNormal)); + // Set active text list classes - with CSSBuilder.EnsureSelector('.active-text ul') do - begin - AddProperty(TCSS.MarginProp(cssAll, 0)); - AddProperty(TCSS.MarginProp(cssTop, 4)); - AddProperty(TCSS.PaddingProp(cssAll, 0)); - AddProperty(TCSS.PaddingProp(cssLeft, 24)); - AddProperty(TCSS.ListStylePositionProp(clspOutside)); - AddProperty(TCSS.ListStyleTypeProp(clstDisc)); - end; - with CSSBuilder.EnsureSelector('.active-text ol') do - begin - AddProperty(TCSS.MarginProp(cssAll, 0)); - AddProperty(TCSS.MarginProp(cssTop, 4)); - AddProperty(TCSS.PaddingProp(cssAll, 0)); - AddProperty(TCSS.PaddingProp(cssLeft, 32)); - AddProperty(TCSS.ListStylePositionProp(clspOutside)); - AddProperty(TCSS.ListStyleTypeProp(clstDecimal)); - end; - with CSSBuilder.EnsureSelector('.active-text li') do - begin - AddProperty(TCSS.PaddingProp(cssAll, 0)); - AddProperty(TCSS.MarginProp(cssAll, 0)); - end; - with CSSBuilder.EnsureSelector('.active-text li ol') do - AddProperty(TCSS.MarginProp(cssTop, 0)); - with CSSBuilder.EnsureSelector('.active-text li ul') do - AddProperty(TCSS.MarginProp(cssTop, 0)); - with CSSBuilder.EnsureSelector('.active-text ul li') do - AddProperty(TCSS.PaddingProp(cssLeft, 8)); - with CSSBuilder.EnsureSelector('.active-text ul li ol li') do - AddProperty(TCSS.PaddingProp(cssLeft, 0)); + CSSBuilder.EnsureSelector('.active-text ul') + .AddProperty(TCSS.MarginProp(cssAll, 0)) + .AddProperty(TCSS.MarginProp(cssTop, 4)) + .AddProperty(TCSS.PaddingProp(cssAll, 0)) + .AddProperty(TCSS.PaddingProp(cssLeft, 24)) + .AddProperty(TCSS.ListStylePositionProp(clspOutside)) + .AddProperty(TCSS.ListStyleTypeProp(clstDisc)); + + // Active list styling + CSSBuilder.EnsureSelector('.active-text ol') + .AddProperty(TCSS.MarginProp(cssAll, 0)) + .AddProperty(TCSS.MarginProp(cssTop, 4)) + .AddProperty(TCSS.PaddingProp(cssAll, 0)) + .AddProperty(TCSS.PaddingProp(cssLeft, 32)) + .AddProperty(TCSS.ListStylePositionProp(clspOutside)) + .AddProperty(TCSS.ListStyleTypeProp(clstDecimal)); + CSSBuilder.EnsureSelector('.active-text li') + .AddProperty(TCSS.PaddingProp(cssAll, 0)) + .AddProperty(TCSS.MarginProp(cssAll, 0)); + CSSBuilder.EnsureSelector('.active-text li ol') + .AddProperty(TCSS.MarginProp(cssTop, 0)); + CSSBuilder.EnsureSelector('.active-text li ul') + .AddProperty(TCSS.MarginProp(cssTop, 0)); + CSSBuilder.EnsureSelector('.active-text ul li') + .AddProperty(TCSS.PaddingProp(cssLeft, 8)); + CSSBuilder.EnsureSelector('.active-text ul li ol li') + .AddProperty(TCSS.PaddingProp(cssLeft, 0)); + finally ContentFont.Free; MonoFont.Free; diff --git a/Src/FrEasterEgg.pas b/Src/FrEasterEgg.pas index f815303e3..f993747b7 100644 --- a/Src/FrEasterEgg.pas +++ b/Src/FrEasterEgg.pas @@ -3,7 +3,7 @@ * v. 2.0. If a copy of the MPL was not distributed with this file, You can * obtain one at https://mozilla.org/MPL/2.0/ * - * Copyright (C) 2009-2021, Peter Johnson (gravatar.com/delphidabbler). + * Copyright (C) 2009-2022, Peter Johnson (gravatar.com/delphidabbler). * * Implements a frame that hosts the HTML, CSS and JavaScript used to display * the program's animated easter egg. @@ -74,8 +74,8 @@ procedure TEasterEggFrame.BuildCSS(const CSSBuilder: TCSSBuilder); CSSFont := TFont.Create; try TFontHelper.SetContentFont(CSSFont); - with CSSBuilder.AddSelector('body') do - AddProperty(TCSS.FontProps(CSSFont)); + CSSBuilder.AddSelector('body') + .AddProperty(TCSS.FontProps(CSSFont)); finally FreeAndNil(CSSFont); end; diff --git a/Src/FrHTMLDlg.pas b/Src/FrHTMLDlg.pas index f2e87cde4..b9901068a 100644 --- a/Src/FrHTMLDlg.pas +++ b/Src/FrHTMLDlg.pas @@ -3,7 +3,7 @@ * v. 2.0. If a copy of the MPL was not distributed with this file, You can * obtain one at https://mozilla.org/MPL/2.0/ * - * Copyright (C) 2005-2021, Peter Johnson (gravatar.com/delphidabbler). + * Copyright (C) 2005-2022, Peter Johnson (gravatar.com/delphidabbler). * * Implements a frame containing a web browser control that displays HTML * content that takes on the appearance of a dialogue box. @@ -18,9 +18,16 @@ interface uses // Delphi - OleCtrls, SHDocVw, Classes, Controls, ExtCtrls, Forms, + OleCtrls, + SHDocVw, + Classes, + Controls, + ExtCtrls, + Forms, // Project - FrBrowserBase, UCSSBuilder; + Browser.UUIMgr, + FrBrowserBase, + UCSSBuilder; type @@ -35,8 +42,11 @@ THTMLDlgFrame = class(TBrowserBaseFrame) fOwner: TForm; {Form that owns the frame. Used to set font and colours to display in frame's HTML } + function GetScrollStyle: TWBScrollbarStyle; + procedure SetScrollStyle(const AValue: TWBScrollbarStyle); strict protected procedure BuildCSS(const CSSBuilder: TCSSBuilder); override; + published {Generates CSS classes specific to HTML dialog boxes. This CSS is added to that provided by parent class. @param CSSBuilder [in] Object used to build the CSS code. @@ -50,6 +60,7 @@ THTMLDlgFrame = class(TBrowserBaseFrame) {Calculates height of document displayed in browser. @return Document height in pixels. } + property ScrollStyle: TWBScrollbarStyle read GetScrollStyle write SetScrollStyle default sbsHide; end; @@ -58,9 +69,11 @@ implementation uses // Delphi - SysUtils, Graphics, + SysUtils, + Graphics, // Project - Browser.UUIMgr, UCSSUtils, UNulDropTarget; + UCSSUtils, + UNulDropTarget; {$R *.dfm} @@ -80,38 +93,30 @@ procedure THTMLDlgFrame.BuildCSS(const CSSBuilder: TCSSBuilder); // Set body style to use dialog box colour and font with no margin CSSFont := TFont.Create; try - with CSSBuilder.AddSelector('body') do - begin - AddProperty(TCSS.BackgroundColorProp(fOwner.Color)); - AddProperty(TCSS.FontProps(fOwner.Font)); - AddProperty(TCSS.MarginProp(0)); - end; + CSSBuilder.AddSelector('body') + .AddProperty(TCSS.BackgroundColorProp(fOwner.Color)) + .AddProperty(TCSS.FontProps(fOwner.Font)) + .AddProperty(TCSS.MarginProp(0)); // Sets heading margins, padding and font size - with CSSBuilder.AddSelector('h1') do - begin - CSSFont.Assign(Self.Font); - CSSFont.Size := CSSFont.Size + 2; - CSSFont.Style := [fsBold]; - AddProperty(TCSS.FontProps(CSSFont)); - AddProperty(TCSS.MarginProp(0)); - AddProperty(TCSS.PaddingProp(0)); - end; + CSSFont.Assign(Self.Font); + CSSFont.Size := CSSFont.Size + 2; + CSSFont.Style := [fsBold]; + CSSBuilder.AddSelector('h1') + .AddProperty(TCSS.FontProps(CSSFont)) + .AddProperty(TCSS.MarginProp(0)) + .AddProperty(TCSS.PaddingProp(0)); // Sets paragraph margins and padding - with CSSBuilder.AddSelector('p') do - begin - AddProperty(TCSS.MarginProp(cssTop, 6)); - AddProperty(TCSS.MarginProp(cssBottom, 0)); - AddProperty(TCSS.PaddingProp(0)); - end; - with CSSBuilder.AddSelector('ul') do - begin - AddProperty(TCSS.MarginProp(cssTop, 6)); - AddProperty(TCSS.MarginProp(cssBottom, 0)); - AddProperty(TCSS.PaddingProp(0)); - end; + CSSBuilder.AddSelector('p') + .AddProperty(TCSS.MarginProp(cssTop, 6)) + .AddProperty(TCSS.MarginProp(cssBottom, 0)) + .AddProperty(TCSS.PaddingProp(0)); + CSSBuilder.AddSelector('ul') + .AddProperty(TCSS.MarginProp(cssTop, 6)) + .AddProperty(TCSS.MarginProp(cssBottom, 0)) + .AddProperty(TCSS.PaddingProp(0)); // Sets table font info - with CSSBuilder.AddSelector('table') do - AddProperty(TCSS.FontProps(fOwner.Font)); + CSSBuilder.AddSelector('table') + .AddProperty(TCSS.FontProps(fOwner.Font)); finally FreeAndNil(CSSFont); end; @@ -140,5 +145,16 @@ function THTMLDlgFrame.DocHeight: Integer; Result := WBController.UIMgr.DocHeight; end; +function THTMLDlgFrame.GetScrollStyle: TWBScrollbarStyle; +begin + Result := WBController.UIMgr.ScrollbarStyle; +end; + +procedure THTMLDlgFrame.SetScrollStyle(const AValue: TWBScrollbarStyle); +begin + if AValue <> GetScrollStyle then + WBController.UIMgr.ScrollbarStyle := AValue; +end; + end. diff --git a/Src/FrHTMLPreview.pas b/Src/FrHTMLPreview.pas index 5e895a9f9..e45cc0641 100644 --- a/Src/FrHTMLPreview.pas +++ b/Src/FrHTMLPreview.pas @@ -3,7 +3,7 @@ * v. 2.0. If a copy of the MPL was not distributed with this file, You can * obtain one at https://mozilla.org/MPL/2.0/ * - * Copyright (C) 2005-2021, Peter Johnson (gravatar.com/delphidabbler). + * Copyright (C) 2005-2022, Peter Johnson (gravatar.com/delphidabbler). * * Implements a frame used to display previews of HTML documents. } @@ -70,8 +70,8 @@ procedure THTMLPreviewFrame.BuildCSS(const CSSBuilder: TCSSBuilder); } begin inherited; - with CSSBuilder.AddSelector('body') do - AddProperty(TCSS.MarginProp(cPreviewMargin)); + CSSBuilder.AddSelector('body') + .AddProperty(TCSS.MarginProp(cPreviewMargin)); end; procedure THTMLPreviewFrame.Display(const DocContent: TEncodedData); diff --git a/Src/Hiliter.UCSS.pas b/Src/Hiliter.UCSS.pas index 785e56802..2a605527e 100644 --- a/Src/Hiliter.UCSS.pas +++ b/Src/Hiliter.UCSS.pas @@ -3,7 +3,7 @@ * v. 2.0. If a copy of the MPL was not distributed with this file, You can * obtain one at https://mozilla.org/MPL/2.0/ * - * Copyright (C) 2006-2021, Peter Johnson (gravatar.com/delphidabbler). + * Copyright (C) 2006-2022, Peter Johnson (gravatar.com/delphidabbler). * * Defines a class that generates CSS code to enable syntax highlighted source * to be displayed in HTML. CSS code uses a highlighter's attributes. Access to @@ -82,11 +82,9 @@ procedure THiliterCSS.BuildCSS(const CSSBuilder: TCSSBuilder); Elem: THiliteElement; // loops thru highlighter elements begin // Add font definition in main class - with CSSBuilder.AddSelector('.' + GetMainCSSClassName) do - begin - AddProperty(TCSS.FontFamilyProp(fHiliteAttrs.FontName, cfgMonoSpace)); - AddProperty(TCSS.FontSizeProp(fHiliteAttrs.FontSize)); - end; + CSSBuilder.AddSelector('.' + GetMainCSSClassName) + .AddProperty(TCSS.FontFamilyProp(fHiliteAttrs.FontName, cfgMonoSpace)) + .AddProperty(TCSS.FontSizeProp(fHiliteAttrs.FontSize)); // Add font style and colour definitions for each element for Elem := Low(THiliteElement) to High(THiliteElement) do BuildElemCSS(Elem, CSSBuilder); @@ -105,14 +103,13 @@ procedure THiliterCSS.BuildElemCSS(const Elem: THiliteElement; // We only create CSS class if element attributes are non-nul if not ElemAttr.IsNul then begin - with CSSBuilder.AddSelector('.' + GetElemCSSClassName(Elem)) do - begin - if ElemAttr.ForeColor <> clNone then - AddProperty(TCSS.ColorProp(ElemAttr.ForeColor)); - AddProperty(TCSS.FontWeightProp(ElemAttr.FontStyle)); - AddProperty(TCSS.FontStyleProp(ElemAttr.FontStyle)); - AddProperty(TCSS.TextDecorationProp(ElemAttr.FontStyle)); - end; + CSSBuilder.AddSelector('.' + GetElemCSSClassName(Elem)) + .AddProperty(TCSS.FontWeightProp(ElemAttr.FontStyle)) + .AddProperty(TCSS.FontStyleProp(ElemAttr.FontStyle)) + .AddProperty(TCSS.TextDecorationProp(ElemAttr.FontStyle)) + .AddPropertyIf( + ElemAttr.ForeColor <> clNone, TCSS.ColorProp(ElemAttr.ForeColor) + ); end; end; From 97411af8941507e2ca5c69714487361e08d69b7c Mon Sep 17 00:00:00 2001 From: delphidabbler <5164283+delphidabbler@users.noreply.github.com> Date: Tue, 13 Dec 2022 11:11:59 +0000 Subject: [PATCH 14/34] Refactor TBorlandCompiler.DetectExeFile method Passed off installed versions of Delphi to new private GetExePathIfRegistered method. --- Src/Compilers.UBorland.pas | 39 ++++++++++++++++++++++++++++---------- 1 file changed, 29 insertions(+), 10 deletions(-) diff --git a/Src/Compilers.UBorland.pas b/Src/Compilers.UBorland.pas index d25b99538..1786c4ad2 100644 --- a/Src/Compilers.UBorland.pas +++ b/Src/Compilers.UBorland.pas @@ -40,6 +40,15 @@ TBorlandCompiler = class(TCompilerBase) @param RootKey [in] Given registry root key. @return Required root path or '' if not compiler not installed. } + /// <summary>Gets the path to the compiler exe file if the compiler is + /// registered as installed on the user's computer.</summary> + /// <param name="ExePath">string [out] Set to path to compiler executable + /// file. Empty string if compiler not installed.</param> + /// <returns>Boolean. True if compiler is registered as installed or False + /// otherwise.</returns> + /// <remarks>Does not check if compiler exe is actually present, just if + /// it is registered.</remarks> + function GetExePathIfInstalled(out ExePath: string): Boolean; strict protected function SearchDirParams: string; override; {One of more parameters that define any search directories to be passed @@ -128,17 +137,11 @@ function TBorlandCompiler.DetectExeFile: Boolean; @return True if compiler path found, false otherwise. } var - InstDir: string; // installation root directory + ExePath: string; begin - // try HKLM - InstDir := InstallPathFromReg(HKEY_LOCAL_MACHINE); - if InstDir = '' then - // in case install was for user only, try HKCU - InstDir := InstallPathFromReg(HKEY_CURRENT_USER); - if InstDir = '' then - Exit(False); - SetExecFile(IncludeTrailingPathDelimiter(InstDir) + 'Bin\DCC32.exe'); - Result := True; + Result := GetExePathIfInstalled(ExePath); + if Result then + SetExecFile(ExePath); end; function TBorlandCompiler.GetDefaultSwitches: string; @@ -161,6 +164,22 @@ function TBorlandCompiler.GetDefaultSwitches: string; + '-$P+'; // Open string params ON end; +function TBorlandCompiler.GetExePathIfInstalled(out ExePath: string): Boolean; +var + InstDir: string; +begin + ExePath := ''; + // try HKLM + InstDir := InstallPathFromReg(HKEY_LOCAL_MACHINE); + if InstDir = '' then + // in case install was for user only, try HKCU + InstDir := InstallPathFromReg(HKEY_CURRENT_USER); + if InstDir = '' then + Exit(False); + ExePath := TPath.Combine(InstDir, 'Bin\DCC32.exe'); + Result := True; +end; + function TBorlandCompiler.GetID: TCompilerID; {Provides the unique id of the compiler. @return Compiler id. From 86d1e8aa18de8ced9e699def48086c954892675f Mon Sep 17 00:00:00 2001 From: delphidabbler <5164283+delphidabbler@users.noreply.github.com> Date: Tue, 13 Dec 2022 11:34:49 +0000 Subject: [PATCH 15/34] Add support for new ICompilerAutoDetect methods Add methods to ICompilerAutoDetect in Compilers.UGlobals. Implement methods in Compilers.UBorland Update TPersistCompilers in Compilers.UCompilers to persist CanAutoInstall "property" in config file. --- Src/Compilers.UBorland.pas | 55 +++++++++++++++++++++++++++++++++--- Src/Compilers.UCompilers.pas | 10 +++++++ Src/Compilers.UGlobals.pas | 11 +++++++- 3 files changed, 71 insertions(+), 5 deletions(-) diff --git a/Src/Compilers.UBorland.pas b/Src/Compilers.UBorland.pas index 1786c4ad2..79d680290 100644 --- a/Src/Compilers.UBorland.pas +++ b/Src/Compilers.UBorland.pas @@ -35,6 +35,8 @@ TBorlandCompiler = class(TCompilerBase) var fId: TCompilerID; {Identifies compiler} + // Flags whether user permits compiler to be auto installed. + fCanAutoInstall: Boolean; function InstallPathFromReg(const RootKey: HKEY): string; {Gets compiler install root path from given registry root key, if present. @param RootKey [in] Given registry root key. @@ -70,10 +72,35 @@ TBorlandCompiler = class(TCompilerBase) @param Obj Compiler object to copy. } { ICompilerAutoDetect } + /// <summary>Detects and records path to command line compiler exe file, + /// if compiler is registered as installed.</summary> + /// <returns>Boolean. True if compiler is registered as installed, False + /// otherwise.</returns> + /// <remarks> + /// <para>Does not check if the compiler exe file actually exists.</para> + /// <para>Does not set compiler exe file if compiler is not installed. + /// </para> + /// <para>Method of ICompilerAutoDetect.</para> + /// </remarks> function DetectExeFile: Boolean; - {Detects and records path to command line compiler if present. - @return True if compiler path found, false otherwise. - } + /// <summary>Checks if the compiler is installed on the user's system. + /// </summary> + /// <returns>Boolean. True if compiler is physically installed, False + /// otherwise.</returns> + /// <remarks> + /// <para>Checks if compiler exe is actually present.</para> + /// <para>Method of ICompilerAutoDetect.</para> + /// </remarks> + function IsInstalled: Boolean; + /// <summary>Checks if the compiler is permitted to be automatically + /// installed.</summary> + /// <remarks>Method of ICompilerAutoDetect.</remarks> + function GetCanAutoInstall: Boolean; + /// <summary>Determines whether the compiler can be automatically + /// installed.</summary> + /// <remarks>Method of ICompilerAutoDetect.</remarks> + procedure SetCanAutoInstall(const Value: Boolean); + { ICompiler } function GetDefaultSwitches: string; override; {Returns default command line switches for compiler. @@ -97,7 +124,7 @@ implementation uses // Delphi - SysUtils, Registry, + SysUtils, Registry, IOUtils, // Project UIStringList, UStrUtils, USystemInfo; @@ -118,6 +145,7 @@ constructor TBorlandCompiler.CreateCopy(const Obj: TBorlandCompiler); begin inherited CreateCopy(Obj); fId := Obj.GetID; + fCanAutoInstall := Obj.GetCanAutoInstall; end; procedure TBorlandCompiler.DeleteObjFiles(const Path, Project: string); @@ -144,6 +172,11 @@ function TBorlandCompiler.DetectExeFile: Boolean; SetExecFile(ExePath); end; +function TBorlandCompiler.GetCanAutoInstall: Boolean; +begin + Result := fCanAutoInstall; +end; + function TBorlandCompiler.GetDefaultSwitches: string; {Returns default command line switches for Borland compilers. @return Switches separated by commas. @@ -211,6 +244,15 @@ function TBorlandCompiler.InstallPathFromReg(const RootKey: HKEY): string; end; end; +function TBorlandCompiler.IsInstalled: Boolean; +var + ExePath: string; +begin + if not GetExePathIfInstalled(ExePath) then + Exit(False); + Result := TFile.Exists(ExePath, False); +end; + function TBorlandCompiler.SearchDirParams: string; {One of more parameters that define any search directories to be passed to compiler on command line. @@ -228,5 +270,10 @@ function TBorlandCompiler.SearchDirParams: string; + ' ' + StrQuoteSpaced('-R' + Dirs.GetText(';', False)); end; +procedure TBorlandCompiler.SetCanAutoInstall(const Value: Boolean); +begin + fCanAutoInstall := Value; +end; + end. diff --git a/Src/Compilers.UCompilers.pas b/Src/Compilers.UCompilers.pas index a0d840690..16c484e30 100644 --- a/Src/Compilers.UCompilers.pas +++ b/Src/Compilers.UCompilers.pas @@ -328,6 +328,12 @@ procedure TPersistCompilers.Load(const Compilers: ICompilers); // Load search directories SearchDirNames := Storage.GetStrings('SearchDirCount', 'SearchDir%d'); Compiler.SetSearchDirs(TSearchDirs.Create(SearchDirNames.ToArray)); + + // Check if compiler can be auto-detected + if Supports(Compiler, ICompilerAutoDetect) then + (Compiler as ICompilerAutoDetect).SetCanAutoInstall( + Storage.GetBoolean('CanAutoInstall', True) + ); end; end; @@ -363,6 +369,10 @@ procedure TPersistCompilers.Save(const Compilers: ICompilers); Storage.SetString('Namespaces', Compiler.GetRTLNamespaces); SearchDirNames := TIStringList.Create(Compiler.GetSearchDirs.ToStrings); Storage.SetStrings('SearchDirCount', 'SearchDir%d', SearchDirNames); + if Supports(Compiler, ICompilerAutoDetect) then + Storage.SetBoolean( + 'CanAutoInstall', (Compiler as ICompilerAutoDetect).GetCanAutoInstall + ); // save the data Storage.Save; end; diff --git a/Src/Compilers.UGlobals.pas b/Src/Compilers.UGlobals.pas index 5653202f9..3600176bf 100644 --- a/Src/Compilers.UGlobals.pas +++ b/Src/Compilers.UGlobals.pas @@ -299,7 +299,7 @@ interface property Count: Integer read GetCount; /// <summary>Number of compilers installed on this computer and made - /// available CodeSnip.</summary> + /// available to CodeSnip.</summary> property AvailableCount: Integer read GetAvailableCount; /// <summary>Checks if any compilers in the list are displayable.</summary> @@ -327,6 +327,15 @@ interface /// <summary>Detects and records the full path of the compiler's /// executable.</summary> function DetectExeFile: Boolean; + /// <summary>Checks if the compiler is installed on the user's system. + /// </summary> + function IsInstalled: Boolean; + /// <summary>Checks if the compiler is permitted to be automatically + /// installed.</summary> + function GetCanAutoInstall: Boolean; + /// <summary>Determines whether the compiler can be automatically + /// installed.</summary> + procedure SetCanAutoInstall(const Value: Boolean); end; From cb68028d471e50387202fd9b772a5210489bbd67 Mon Sep 17 00:00:00 2001 From: delphidabbler <5164283+delphidabbler@users.noreply.github.com> Date: Thu, 15 Dec 2022 03:36:50 +0000 Subject: [PATCH 16/34] New TCompilerList class Added to Compilers.UCompilers unit --- Src/Compilers.UCompilers.pas | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/Src/Compilers.UCompilers.pas b/Src/Compilers.UCompilers.pas index 16c484e30..92b6e7881 100644 --- a/Src/Compilers.UCompilers.pas +++ b/Src/Compilers.UCompilers.pas @@ -20,6 +20,8 @@ interface uses + // Delphi + Generics.Collections, // Project Compilers.UGlobals, UBaseObjects; @@ -64,13 +66,18 @@ TCompilersFactory = class(TNoConstructObject) } end; + TCompilerList = class(TList<ICompiler>) + public + constructor Create; + end; + implementation uses // Delphi - Generics.Collections, SysUtils, + Generics.Defaults, SysUtils, // Project Compilers.UBDS, Compilers.UDelphi, Compilers.UFreePascal, Compilers.USearchDirs, IntfCommon, UConsts, UExceptions, UIStringList, @@ -378,5 +385,19 @@ procedure TPersistCompilers.Save(const Compilers: ICompilers); end; end; +{ TCompilerList } + +constructor TCompilerList.Create; +begin + inherited Create( + TDelegatedComparer<ICompiler>.Create( + function (const Left, Right: ICompiler): Integer + begin + Result := Ord(Left.GetID) - Ord(Right.GetID); + end + ) + ) +end; + end. From 9209e64f106aff94410bf6c4d4b5a8e29faceba0 Mon Sep 17 00:00:00 2001 From: delphidabbler <5164283+delphidabbler@users.noreply.github.com> Date: Tue, 13 Dec 2022 16:02:44 +0000 Subject: [PATCH 17/34] Add option to switch off pre-compiler auto-detect Update FmCompilersDlg.FrCompiler frame to display a check box for Delphi compilers to permit auto-detection of each compiler to be disabled. --- Src/FmCompilersDlg.FrCompiler.dfm | 13 ++++++++++++- Src/FmCompilersDlg.FrCompiler.pas | 14 +++++++++++++- 2 files changed, 25 insertions(+), 2 deletions(-) diff --git a/Src/FmCompilersDlg.FrCompiler.dfm b/Src/FmCompilersDlg.FrCompiler.dfm index 1d844035a..91c15db22 100644 --- a/Src/FmCompilersDlg.FrCompiler.dfm +++ b/Src/FmCompilersDlg.FrCompiler.dfm @@ -1,4 +1,7 @@ inherited CompilersDlgCompilerFrame: TCompilersDlgCompilerFrame + DesignSize = ( + 362 + 235) object lblCompilerPath: TLabel Left = 4 Top = 4 @@ -46,9 +49,17 @@ inherited CompilersDlgCompilerFrame: TCompilersDlgCompilerFrame object chkShowInMain: TCheckBox Left = 4 Top = 88 - Width = 273 + Width = 320 Height = 17 Caption = 'Display &results for this compiler in details pane' TabOrder = 3 end + object chkPermitAutoDetect: TCheckBox + Left = 4 + Top = 111 + Width = 320 + Height = 17 + Caption = 'Permit &auto-detection && registration of this compiler' + TabOrder = 4 + end end diff --git a/Src/FmCompilersDlg.FrCompiler.pas b/Src/FmCompilersDlg.FrCompiler.pas index 8b9c2b2db..b4657ab02 100644 --- a/Src/FmCompilersDlg.FrCompiler.pas +++ b/Src/FmCompilersDlg.FrCompiler.pas @@ -3,7 +3,7 @@ * v. 2.0. If a copy of the MPL was not distributed with this file, You can * obtain one at https://mozilla.org/MPL/2.0/ * - * Copyright (C) 2011-2021, Peter Johnson (gravatar.com/delphidabbler). + * Copyright (C) 2011-2022, Peter Johnson (gravatar.com/delphidabbler). * * Implements a frame used to edit executable file name of compiler being edited * in TCompilersDlg. @@ -34,6 +34,7 @@ TCompilersDlgCompilerFrame = class(TCompilersDlgBaseFrame) btnBrowse: TButton; btnClear: TButton; chkShowInMain: TCheckBox; + chkPermitAutoDetect: TCheckBox; /// <summary>Displays file open dialog box and places entered file name in /// compiler file name edit control.</summary> procedure btnBrowseClick(Sender: TObject); @@ -98,6 +99,7 @@ procedure TCompilersDlgCompilerFrame.ArrangeControls; ); btnClear.Top := TCtrlArranger.BottomOf([edCompilerPath, btnBrowse], 8); chkShowInMain.Top := TCtrlArranger.BottomOf(btnClear, 24); + chkPermitAutoDetect.Top := TCtrlArranger.BottomOf(chkShowInMain, 8); end; procedure TCompilersDlgCompilerFrame.btnBrowseClick(Sender: TObject); @@ -164,12 +166,22 @@ procedure TCompilersDlgCompilerFrame.Initialise; begin edCompilerPath.Text := Compiler.GetExecFile; chkShowInMain.Checked := Compiler.GetDisplayable; + chkPermitAutoDetect.Visible := Supports(Compiler, ICompilerAutoDetect); + if chkPermitAutoDetect.Visible then + chkPermitAutoDetect.Checked := + (Compiler as ICompilerAutoDetect).GetCanAutoInstall + else + chkPermitAutoDetect.Checked := False; end; procedure TCompilersDlgCompilerFrame.UpdateCompiler; begin Compiler.SetExecFile(GetCompilerPath); Compiler.SetDisplayable(chkShowInMain.Checked); + if Supports(Compiler, ICompilerAutoDetect) then + (Compiler as ICompilerAutoDetect).SetCanAutoInstall( + chkPermitAutoDetect.Checked + ); end; function TCompilersDlgCompilerFrame.ValidateFileName(const FileName: string; From c4d6ca767f859d37b14f6481f5d3dacf0af20dd8 Mon Sep 17 00:00:00 2001 From: delphidabbler <5164283+delphidabbler@users.noreply.github.com> Date: Thu, 15 Dec 2022 17:15:34 +0000 Subject: [PATCH 18/34] Add new Compilers.UAutoDetect unit to project Implements a static class that can detect and register Delphi compilers present on the user's system that are not yet registered with CodeSnip. --- Src/CodeSnip.dpr | 3 +- Src/CodeSnip.dproj | 1 + Src/Compilers.UAutoDetect.pas | 115 ++++++++++++++++++++++++++++++++++ 3 files changed, 118 insertions(+), 1 deletion(-) create mode 100644 Src/Compilers.UAutoDetect.pas diff --git a/Src/CodeSnip.dpr b/Src/CodeSnip.dpr index d608babf8..a3b16a3cc 100644 --- a/Src/CodeSnip.dpr +++ b/Src/CodeSnip.dpr @@ -370,7 +370,8 @@ uses UXMLDocConsts in 'UXMLDocConsts.pas', UXMLDocHelper in 'UXMLDocHelper.pas', UXMLDocumentEx in 'UXMLDocumentEx.pas', - FmDeleteUserDBDlg in 'FmDeleteUserDBDlg.pas' {DeleteUserDBDlg}; + FmDeleteUserDBDlg in 'FmDeleteUserDBDlg.pas' {DeleteUserDBDlg}, + Compilers.UAutoDetect in 'Compilers.UAutoDetect.pas'; // Include resources {$Resource ExternalObj.tlb} // Type library file diff --git a/Src/CodeSnip.dproj b/Src/CodeSnip.dproj index e1a91d26d..096228d32 100644 --- a/Src/CodeSnip.dproj +++ b/Src/CodeSnip.dproj @@ -575,6 +575,7 @@ <DCCReference Include="FmDeleteUserDBDlg.pas"> <Form>DeleteUserDBDlg</Form> </DCCReference> + <DCCReference Include="Compilers.UAutoDetect.pas"/> <None Include="CodeSnip.todo"/> <BuildConfiguration Include="Base"> <Key>Base</Key> diff --git a/Src/Compilers.UAutoDetect.pas b/Src/Compilers.UAutoDetect.pas new file mode 100644 index 000000000..40d1bebea --- /dev/null +++ b/Src/Compilers.UAutoDetect.pas @@ -0,0 +1,115 @@ +{ + * This Source Code Form is subject to the terms of the Mozilla Public License, + * v. 2.0. If a copy of the MPL was not distributed with this file, You can + * obtain one at https://mozilla.org/MPL/2.0/ + * + * Copyright (C) 2022, Peter Johnson (gravatar.com/delphidabbler). + * + * Implements a static class that can detect and register Delphi compilers + * present on the user's system that are not yet registered with CodeSnip. +} + + +unit Compilers.UAutoDetect; + +interface + +uses + Compilers.UGlobals, + Compilers.UCompilers, + UBaseObjects; + +type + TCompilerAutoDetect = class(TNoConstructObject) + public + type + TCallback = reference to procedure (Compiler: ICompiler); + strict private + class procedure DoCallback(const Callback: TCallback; + Compiler: ICompiler); + public + class procedure RegisterCompilers(Compilers: ICompilers; + const Callback: TCallback = nil); overload; + class procedure RegisterSpecificCompilers(AllCompilers: ICompilers; + const RegList: TCompilerList; const Callback: TCallback = nil); + class procedure ListRegisterableCompilers(Compilers: ICompilers; + const Registerable: TCompilerList); + end; + +implementation + +uses + SysUtils; + +{ TCompilerAutoDetect } + +class procedure TCompilerAutoDetect.DoCallback( + const Callback: TCallback; Compiler: ICompiler); +begin + if Assigned(Callback) then + Callback(Compiler); +end; + +class procedure TCompilerAutoDetect.ListRegisterableCompilers( + Compilers: ICompilers; const Registerable: TCompilerList); +var + Compiler: ICompiler; +begin + Registerable.Clear; + for Compiler in Compilers do + begin + if not Supports(Compiler, ICompilerAutoDetect) then + Continue; // compiler can't be auto-detected/registered + if not (Compiler as ICompilerAutoDetect).IsInstalled then + Continue; // compiler is not installed on the user's system + if Compiler.IsAvailable then + Continue; // compiler installed & already registered for use by CodeSnip + if not (Compiler as ICompilerAutoDetect).GetCanAutoInstall then + Continue; // user has excluded this compiler from being auto-registered + // We get here then we have an installed, un-registered, auto-detectable + // compiler with permission to register + Registerable.Add(Compiler); + end; +end; + +class procedure TCompilerAutoDetect.RegisterSpecificCompilers( + AllCompilers: ICompilers; const RegList: TCompilerList; + const Callback: TCallback); +var + Compiler: ICompiler; +begin + for Compiler in AllCompilers do + begin + if RegList.IndexOf(Compiler) >= 0 then + begin + Assert(Supports(Compiler, ICompilerAutoDetect), ClassName + + '.RegisterCompilers: Compiler does not support ICompilerAutoDetect'); + if (Compiler as ICompilerAutoDetect).DetectExeFile then + DoCallback(Callback, Compiler); + end; + end; +end; + +class procedure TCompilerAutoDetect.RegisterCompilers(Compilers: ICompilers; + const Callback: TCallback); +var + Registerable: TCompilerList; + Compiler: ICompiler; +begin + Registerable := TCompilerList.Create; + try + ListRegisterableCompilers(Compilers, Registerable); + for Compiler in Registerable do + begin + Assert(Supports(Compiler, ICompilerAutoDetect), ClassName + + '.RegisterCompilers: Compiler does not support ICompilerAutoDetect'); + if (Compiler as ICompilerAutoDetect).DetectExeFile then + DoCallback(Callback, Compiler); + end; + finally + Registerable.Free; + end; +end; + +end. + From d156dcadd324f089efaa8a99db8decd62dae4883 Mon Sep 17 00:00:00 2001 From: delphidabbler <5164283+delphidabbler@users.noreply.github.com> Date: Thu, 15 Dec 2022 17:26:57 +0000 Subject: [PATCH 19/34] Refactor compiler registration in FmCompilersDlg Change to use Compilers.UAutoDetect unit to perform compiler registration. Also refactored out a with..do statement. --- Src/FmCompilersDlg.pas | 55 +++++++++++++++++++++--------------------- 1 file changed, 27 insertions(+), 28 deletions(-) diff --git a/Src/FmCompilersDlg.pas b/Src/FmCompilersDlg.pas index 135895a33..4dc64a6e8 100644 --- a/Src/FmCompilersDlg.pas +++ b/Src/FmCompilersDlg.pas @@ -3,7 +3,7 @@ * v. 2.0. If a copy of the MPL was not distributed with this file, You can * obtain one at https://mozilla.org/MPL/2.0/ * - * Copyright (C) 2005-2021, Peter Johnson (gravatar.com/delphidabbler). + * Copyright (C) 2005-2022, Peter Johnson (gravatar.com/delphidabbler). * * Implements a dialogue box where the user can configure the Pascal compilers * that are to be used by CodeSnip. @@ -122,7 +122,12 @@ implementation uses // Project - Compilers.UCompilers, IntfCommon, UCtrlArranger, UExeFileType, UFontHelper, + Compilers.UAutoDetect, + Compilers.UCompilers, + IntfCommon, + UCtrlArranger, + UExeFileType, + UFontHelper, UMessageBox; @@ -148,8 +153,6 @@ procedure TCompilersDlg.ArrangeForm; end; procedure TCompilersDlg.btnDetectClick(Sender: TObject); -var - Compiler: ICompiler; // refers to each compiler resourcestring // Text displayed in confirmation box sOKToDetect = 'Detected compiler file names will overwrite any existing ' @@ -159,19 +162,14 @@ procedure TCompilersDlg.btnDetectClick(Sender: TObject); Exit; // Record any changes to current compiler UpdateCurrentCompiler; - // Loop thru all compilers attempting to detect exe files - for Compiler in fLocalCompilers do - begin - if Supports(Compiler, ICompilerAutoDetect) then + // Register any available compilers + TCompilerAutoDetect.RegisterCompilers(fLocalCompilers, + procedure (RegisteredCompiler: ICompiler) begin - if (Compiler as ICompilerAutoDetect).DetectExeFile then - begin - // Update currently displayed compiler details - if Compiler.GetID = fCurCompiler.GetID then - UpdateEditFrames; - end; - end; - end; + if RegisteredCompiler.GetID = fCurCompiler.GetID then + UpdateEditFrames; + end + ); // Redisplay compiler list and current compiler title to reflect any changes fCompListMgr.Refresh; fBannerMgr.Refresh; @@ -234,20 +232,21 @@ class function TCompilersDlg.Execute(AOwner: TComponent; const ACompilers: ICompilers): Boolean; var Persister: IPersistCompilers; // object used to save object to storage + Dlg: TCompilersDlg; // dialogue box instance begin - with InternalCreate(AOwner) do - try - (fLocalCompilers as IAssignable).Assign(ACompilers); - Result := ShowModal = mrOK; - if Result then - begin - (ACompilers as IAssignable).Assign(fLocalCompilers); - Persister := TPersistCompilers.Create; - Persister.Save(ACompilers); - end; - finally - Free; + Dlg := InternalCreate(AOwner); + try + (Dlg.fLocalCompilers as IAssignable).Assign(ACompilers); + Result := Dlg.ShowModal = mrOK; + if Result then + begin + (ACompilers as IAssignable).Assign(Dlg.fLocalCompilers); + Persister := TPersistCompilers.Create; + Persister.Save(ACompilers); end; + finally + Dlg.Free; + end; end; procedure TCompilersDlg.FormCreate(Sender: TObject); From 4ec90aeafcd65ee08356e093c7b94b0c7485bbd1 Mon Sep 17 00:00:00 2001 From: delphidabbler <5164283+delphidabbler@users.noreply.github.com> Date: Thu, 15 Dec 2022 17:32:08 +0000 Subject: [PATCH 20/34] Add new all-compilers settings unit to project New static TCompilerSettings class in new Compiler.USettings unit provided to read/write settings to config file that apply to all compilers. Added only a single PermitStartupDetection class property that specifies whether CodeSnip should detect un-registered Delphi compiler installations on start-up. Added a new ssCompilers ("Compilers") section to USettings unit for use by the new TCompilerSettings class. --- Src/CodeSnip.dpr | 3 +- Src/CodeSnip.dproj | 1 + Src/Compilers.USettings.pas | 61 +++++++++++++++++++++++++++++++++++++ Src/USettings.pas | 8 +++-- 4 files changed, 69 insertions(+), 4 deletions(-) create mode 100644 Src/Compilers.USettings.pas diff --git a/Src/CodeSnip.dpr b/Src/CodeSnip.dpr index a3b16a3cc..def4bca01 100644 --- a/Src/CodeSnip.dpr +++ b/Src/CodeSnip.dpr @@ -371,7 +371,8 @@ uses UXMLDocHelper in 'UXMLDocHelper.pas', UXMLDocumentEx in 'UXMLDocumentEx.pas', FmDeleteUserDBDlg in 'FmDeleteUserDBDlg.pas' {DeleteUserDBDlg}, - Compilers.UAutoDetect in 'Compilers.UAutoDetect.pas'; + Compilers.UAutoDetect in 'Compilers.UAutoDetect.pas', + Compilers.USettings in 'Compilers.USettings.pas'; // Include resources {$Resource ExternalObj.tlb} // Type library file diff --git a/Src/CodeSnip.dproj b/Src/CodeSnip.dproj index 096228d32..6370f7d80 100644 --- a/Src/CodeSnip.dproj +++ b/Src/CodeSnip.dproj @@ -576,6 +576,7 @@ <Form>DeleteUserDBDlg</Form> </DCCReference> <DCCReference Include="Compilers.UAutoDetect.pas"/> + <DCCReference Include="Compilers.USettings.pas"/> <None Include="CodeSnip.todo"/> <BuildConfiguration Include="Base"> <Key>Base</Key> diff --git a/Src/Compilers.USettings.pas b/Src/Compilers.USettings.pas new file mode 100644 index 000000000..a4baee07f --- /dev/null +++ b/Src/Compilers.USettings.pas @@ -0,0 +1,61 @@ +{ + * This Source Code Form is subject to the terms of the Mozilla Public License, + * v. 2.0. If a copy of the MPL was not distributed with this file, You can + * obtain one at https://mozilla.org/MPL/2.0/ + * + * Copyright (C) 2022, Peter Johnson (gravatar.com/delphidabbler). + * + * Class that reads and writes settings that apply to all compilers. +} + + +unit Compilers.USettings; + +interface + +uses + UBaseObjects, + USettings; + +type + /// <summary>Manages settings that apply to all compilers.</summary> + TCompilerSettings = class(TNoConstructObject) + strict private + const + AllCompilersConfigSection = ssCompilers; + PermitStartupDetectionKey = 'PermitStartupDetection'; + class function ReadStorage: ISettingsSection; + class function GetPermitStartupDetection: Boolean; static; + class procedure SetPermitStartupDetection(const Value: Boolean); static; + public + class property PermitStartupDetection: Boolean + read GetPermitStartupDetection write SetPermitStartupDetection + default True; + end; + +implementation + +{ TCompilerSettings } + +class function TCompilerSettings.GetPermitStartupDetection: Boolean; +begin + Result := ReadStorage.GetBoolean(PermitStartupDetectionKey, True); +end; + +class function TCompilerSettings.ReadStorage: ISettingsSection; +begin + Result := Settings.ReadSection(AllCompilersConfigSection); +end; + +class procedure TCompilerSettings.SetPermitStartupDetection( + const Value: Boolean); +var + Stg: ISettingsSection; +begin + Stg := ReadStorage; + Stg.SetBoolean(PermitStartupDetectionKey, Value); + Stg.Save; +end; + +end. + diff --git a/Src/USettings.pas b/Src/USettings.pas index 527364fbf..7de87b900 100644 --- a/Src/USettings.pas +++ b/Src/USettings.pas @@ -164,12 +164,12 @@ interface /// <para>-ssWindowState - info about the size and state of various /// windows</para> /// <para>-ssDatabase - database customisation info</para> - /// <para>-ssUpdateChecks - info about update checks</para> + /// <para>-ssCompilers - info about all compilers</para> /// </summary> TSettingsSectionId = ( ssFindText, ssFindCompiler, ssFindXRefs, ssCompilerInfo, ssPreferences, ssUnits, ssDuplicateSnippet, - ssFavourites, ssWindowState, ssDatabase + ssFavourites, ssWindowState, ssDatabase, ssCompilers ); type @@ -540,7 +540,8 @@ function TIniSettings.SectionName(const Id: TSettingsSectionId; 'DuplicateSnippet', // ssDuplicateSnippet 'Favourites', // ssFavourites 'WindowState', // ssWindowState - 'Database' // ssDatabase + 'Database', // ssDatabase + 'Compilers' // ssCompilers ); begin Result := cSectionNames[Id]; @@ -751,3 +752,4 @@ procedure TIniSettingsSection.SetStrings(const CountName, ItemFmt: string; end. + From 7172b540b763eeccf06323416e3873ad1fb21dae Mon Sep 17 00:00:00 2001 From: delphidabbler <5164283+delphidabbler@users.noreply.github.com> Date: Thu, 15 Dec 2022 17:38:34 +0000 Subject: [PATCH 21/34] Update config compilers dlg with auto-detect option A check box was added to FmCompilersDlg that enables/disables the option to detect and register compilers at start up. Value of the option is stored in the config file via the TCompilerSettings class from Compilers.USettings unit. --- Src/FmCompilersDlg.dfm | 18 ++++++++++++++---- Src/FmCompilersDlg.pas | 18 +++++++++++++++--- 2 files changed, 29 insertions(+), 7 deletions(-) diff --git a/Src/FmCompilersDlg.dfm b/Src/FmCompilersDlg.dfm index 12ae8c2a1..f9155a014 100644 --- a/Src/FmCompilersDlg.dfm +++ b/Src/FmCompilersDlg.dfm @@ -138,13 +138,13 @@ inherited CompilersDlg: TCompilersDlg end end inherited btnHelp: TButton - TabOrder = 4 + TabOrder = 5 end inherited btnCancel: TButton - TabOrder = 3 + TabOrder = 4 end inherited btnOK: TButton - TabOrder = 2 + TabOrder = 3 OnClick = btnOKClick end object btnDetect: TButton @@ -153,7 +153,17 @@ inherited CompilersDlg: TCompilersDlg Width = 153 Height = 25 Caption = '&Detect Delphi Compilers' - TabOrder = 1 + TabOrder = 2 OnClick = btnDetectClick end + object chkStartupDetection: TCheckBox + Left = 8 + Top = 336 + Width = 417 + Height = 17 + Caption = + 'Automatically register newly installed Delphi compilers at progr' + + 'am startup' + TabOrder = 1 + end end diff --git a/Src/FmCompilersDlg.pas b/Src/FmCompilersDlg.pas index 4dc64a6e8..e78f4fd14 100644 --- a/Src/FmCompilersDlg.pas +++ b/Src/FmCompilersDlg.pas @@ -45,6 +45,7 @@ TCompilersDlg = class(TGenericOKDlg, INoPublicConstruct) frmSearchDirs: TCompilersDlgSearchDirsFrame; tsNamespaces: TTabSheet; frmNamespaces: TCompilersDlgNamespacesFrame; + chkStartupDetection: TCheckBox; /// <summary>When Auto Detect Compilers button is clicked, sets executable /// program path for each installed compiler that can detect its own path. /// </summary> @@ -124,6 +125,7 @@ implementation // Project Compilers.UAutoDetect, Compilers.UCompilers, + Compilers.USettings, IntfCommon, UCtrlArranger, UExeFileType, @@ -147,9 +149,16 @@ procedure TCompilersDlg.ArrangeForm; ); // size dialogue and arrange inherited controls inherited; - // arrange extra button in bottom button line - btnDetect.Left := pnlBody.Left; - btnDetect.Top := btnHelp.Top; + TCtrlArranger.AlignLefts([btnDetect, chkStartupDetection], pnlBody.Left); + // place chkStartupDetection below bevel under body panel + TCtrlArranger.MoveBelow(bvlBottom, chkStartupDetection, 8); + // push buttons below chkStartupDetection + TCtrlArranger.AlignTops( + [btnDetect, btnOK, btnCancel, btnHelp], + TCtrlArranger.BottomOf(chkStartupDetection, 8) + ); + // stretch form height to accomodate insertion of chkStartupDetection + ClientHeight := TCtrlArranger.BottomOf(btnHelp, 6); end; procedure TCompilersDlg.btnDetectClick(Sender: TObject); @@ -237,12 +246,15 @@ class function TCompilersDlg.Execute(AOwner: TComponent; Dlg := InternalCreate(AOwner); try (Dlg.fLocalCompilers as IAssignable).Assign(ACompilers); + Dlg.chkStartupDetection.Checked := TCompilerSettings.PermitStartupDetection; Result := Dlg.ShowModal = mrOK; if Result then begin (ACompilers as IAssignable).Assign(Dlg.fLocalCompilers); Persister := TPersistCompilers.Create; Persister.Save(ACompilers); + TCompilerSettings.PermitStartupDetection := + Dlg.chkStartupDetection.Checked; end; finally Dlg.Free; From 561501e64422aac3326de595e7646a82b68a2b1c Mon Sep 17 00:00:00 2001 From: delphidabbler <5164283+delphidabbler@users.noreply.github.com> Date: Thu, 15 Dec 2022 17:45:58 +0000 Subject: [PATCH 22/34] Add FmRegisterCompilersDlg dialogue box to project. This dialogue box is designed to present a list of Delphi compiler(s) to the user to enabled them to select which, if any, compiler(s) are to be registered. Designed for display at startup when unregistered versions of Delphi are detected. The dialogue includes an HTML resource that is used to display help information on the face of the dialogue box. --- Src/CodeSnip.dpr | 3 +- Src/CodeSnip.dproj | 3 + Src/FmRegisterCompilersDlg.dfm | 91 +++++++++ Src/FmRegisterCompilersDlg.pas | 250 +++++++++++++++++++++++ Src/HTML.hrc | 3 + Src/Res/HTML/dlg-register-compilers.html | 59 ++++++ 6 files changed, 408 insertions(+), 1 deletion(-) create mode 100644 Src/FmRegisterCompilersDlg.dfm create mode 100644 Src/FmRegisterCompilersDlg.pas create mode 100644 Src/Res/HTML/dlg-register-compilers.html diff --git a/Src/CodeSnip.dpr b/Src/CodeSnip.dpr index def4bca01..e12778a39 100644 --- a/Src/CodeSnip.dpr +++ b/Src/CodeSnip.dpr @@ -372,7 +372,8 @@ uses UXMLDocumentEx in 'UXMLDocumentEx.pas', FmDeleteUserDBDlg in 'FmDeleteUserDBDlg.pas' {DeleteUserDBDlg}, Compilers.UAutoDetect in 'Compilers.UAutoDetect.pas', - Compilers.USettings in 'Compilers.USettings.pas'; + Compilers.USettings in 'Compilers.USettings.pas', + FmRegisterCompilersDlg in 'FmRegisterCompilersDlg.pas' {RegisterCompilersDlg}; // Include resources {$Resource ExternalObj.tlb} // Type library file diff --git a/Src/CodeSnip.dproj b/Src/CodeSnip.dproj index 6370f7d80..628ac2cce 100644 --- a/Src/CodeSnip.dproj +++ b/Src/CodeSnip.dproj @@ -577,6 +577,9 @@ </DCCReference> <DCCReference Include="Compilers.UAutoDetect.pas"/> <DCCReference Include="Compilers.USettings.pas"/> + <DCCReference Include="FmRegisterCompilersDlg.pas"> + <Form>RegisterCompilersDlg</Form> + </DCCReference> <None Include="CodeSnip.todo"/> <BuildConfiguration Include="Base"> <Key>Base</Key> diff --git a/Src/FmRegisterCompilersDlg.dfm b/Src/FmRegisterCompilersDlg.dfm new file mode 100644 index 000000000..c9db2d7e4 --- /dev/null +++ b/Src/FmRegisterCompilersDlg.dfm @@ -0,0 +1,91 @@ +inherited RegisterCompilersDlg: TRegisterCompilersDlg + Caption = 'RegisterCompilersDlg' + ClientHeight = 453 + ExplicitWidth = 474 + ExplicitHeight = 482 + PixelsPerInch = 96 + TextHeight = 13 + inherited bvlBottom: TBevel + Top = 403 + ExplicitTop = 403 + end + inherited pnlBody: TPanel + Width = 433 + Height = 389 + ExplicitWidth = 433 + ExplicitHeight = 389 + object lblDesc: TLabel + Left = 0 + Top = 0 + Width = 33 + Height = 13 + Caption = 'lblDesc' + FocusControl = clbCompilers + end + object clbCompilers: TCheckListBox + Left = 0 + Top = 32 + Width = 361 + Height = 56 + IntegralHeight = True + ItemHeight = 13 + TabOrder = 0 + end + inline frmNotes: TFixedHTMLDlgFrame + Left = 0 + Top = 159 + Width = 361 + Height = 199 + TabOrder = 1 + TabStop = True + ExplicitTop = 159 + ExplicitWidth = 361 + ExplicitHeight = 199 + inherited pnlBrowser: TPanel + Width = 361 + Height = 199 + ExplicitWidth = 361 + ExplicitHeight = 199 + inherited wbBrowser: TWebBrowser + Width = 361 + Height = 199 + ExplicitWidth = 353 + ExplicitHeight = 199 + ControlData = { + 4C0000004F250000911400000000000000000000000000000000000000000000 + 000000004C000000000000000000000001000000E0D057007335CF11AE690800 + 2B2E126208000000000000004C0000000114020000000000C000000000000046 + 8000000000000000000000000000000000000000000000000000000000000000 + 00000000000000000100000000000000000000000000000000000000} + end + end + end + object chkDontShowAgain: TCheckBox + Left = 0 + Top = 372 + Width = 145 + Height = 17 + Caption = '&Don'#39't show this again' + TabOrder = 2 + end + end + inherited btnHelp: TButton + Left = 362 + Top = 420 + ExplicitLeft = 362 + ExplicitTop = 420 + end + inherited btnCancel: TButton + Left = 282 + Top = 420 + ExplicitLeft = 282 + ExplicitTop = 420 + end + inherited btnOK: TButton + Left = 201 + Top = 420 + Default = False + ExplicitLeft = 201 + ExplicitTop = 420 + end +end diff --git a/Src/FmRegisterCompilersDlg.pas b/Src/FmRegisterCompilersDlg.pas new file mode 100644 index 000000000..05c897c49 --- /dev/null +++ b/Src/FmRegisterCompilersDlg.pas @@ -0,0 +1,250 @@ +{ + * This Source Code Form is subject to the terms of the Mozilla Public License, + * v. 2.0. If a copy of the MPL was not distributed with this file, You can + * obtain one at https://mozilla.org/MPL/2.0/ + * + * Copyright (C) 2022, Peter Johnson (gravatar.com/delphidabbler). + * + * Implements a dialogue box that is optionally displayed at start-up when + * Delphi compiler(s) are installed but not registered with CodeSnip. Allows the + * user to specify which, if any, compiler(s) are to be registered. +} + + +unit FmRegisterCompilersDlg; + +interface + +uses + // Delphi + Forms, + Classes, + StdCtrls, + Controls, + CheckLst, + ExtCtrls, + SysUtils, + // Project + FmGenericOKDlg, + FrBrowserBase, + FrFixedHTMLDlg, + FrHTMLDlg, + Compilers.UCompilers, + Compilers.UGlobals, + UBaseObjects, + UCSSBuilder; + +type + TRegisterCompilersDlg = class(TGenericOKDlg, INoPublicConstruct) + lblDesc: TLabel; + clbCompilers: TCheckListBox; + frmNotes: TFixedHTMLDlgFrame; + chkDontShowAgain: TCheckBox; + procedure FormDestroy(Sender: TObject); + procedure alMainUpdate(Action: TBasicAction; var Handled: Boolean); + strict private + var + fCandidateCompilers: TCompilerList; + fSelectedCompilers: TCompilerList; + function CountTickedCompilers: Integer; + procedure StoreSelectedCompilers; + procedure SetCaptions; + procedure PopulateList; + strict protected + procedure ConfigForm; override; + procedure InitForm; override; + procedure ArrangeForm; override; + /// <summary>Handles HTML frame's OnBuildCSS event. Adds additional CSS + /// required by HTML in this form.</summary> + /// <param name="Sender">TObject [in] Reference to object triggering event. + /// </param> + /// <param name="CSSBuilder">TCSSBuilder [in] Object used to construct the + /// CSS.</param> + procedure BuildCSS(Sender: TObject; const CSSBuilder: TCSSBuilder); + public + class function Execute(const AOwner: TComponent; + const CandidateCompilers: TCompilerList; + const SelectedCompilers: TCompilerList): Boolean; + end; + +implementation + +uses + Compilers.USettings, + UBox, + UCSSUtils, + UCtrlArranger; + +{$R *.dfm} + +{ TRegisterCompilersDlg } + +procedure TRegisterCompilersDlg.alMainUpdate(Action: TBasicAction; + var Handled: Boolean); +begin + inherited; + btnOK.Enabled := chkDontShowAgain.Checked or (CountTickedCompilers > 0); +end; + +procedure TRegisterCompilersDlg.ArrangeForm; +begin + TCtrlArranger.SetLabelHeight(lblDesc); + clbCompilers.Top := TCtrlArranger.BottomOf(lblDesc, 8); + frmNotes.Top := TCtrlArranger.BottomOf(clbCompilers, 0); + frmNotes.Height := frmNotes.DocHeight; + chkDontShowAgain.Top := TCtrlArranger.BottomOf(frmNotes, 8); + TCtrlArranger.AlignLefts( + [lblDesc, clbCompilers, frmNotes, chkDontShowAgain], 0 + ); + pnlBody.ClientHeight := TCtrlArranger.TotalControlHeight(pnlBody) + 8; + pnlBody.ClientWidth := TCtrlArranger.TotalControlWidth(pnlBody); + inherited; +end; + +procedure TRegisterCompilersDlg.BuildCSS(Sender: TObject; + const CSSBuilder: TCSSBuilder); +var + Selector: TCSSSelector; +begin + Selector := CSSBuilder.AddSelector('ol'); + Selector.AddProperty(TCSS.MarginProp(cssLeft, 24)); + Selector.AddProperty(TCSS.MarginProp(cssTop, 6)); + Selector.AddProperty(TCSS.MarginProp(cssBottom, 0)); + Selector.AddProperty(TCSS.PaddingProp(0)); + + Selector := CSSBuilder.AddSelector('ol li'); + Selector.AddProperty(TCSS.MarginProp(cssTop, 6)); + + Selector := CSSBuilder.AddSelector('ol li ul'); + Selector.AddProperty(TCSS.MarginProp(cssLeft, 16)); + Selector.AddProperty(TCSS.MarginProp(cssTop, 6)); + Selector.AddProperty(TCSS.MarginProp(cssBottom, 0)); + Selector.AddProperty(TCSS.PaddingProp(0)); + + Selector := CSSBuilder.AddSelector('ol li ul li'); + Selector.AddProperty(TCSS.MarginProp(cssTop, 0)); +end; + +procedure TRegisterCompilersDlg.ConfigForm; +begin + inherited; + frmNotes.OnBuildCSS := BuildCSS; + frmNotes.Initialise('dlg-register-compilers.html'); +end; + +function TRegisterCompilersDlg.CountTickedCompilers: Integer; +var + I: Integer; +begin + Result := 0; + for I := 0 to Pred(clbCompilers.Count) do + if clbCompilers.Checked[I] then + Inc(Result); +end; + +class function TRegisterCompilersDlg.Execute(const AOwner: TComponent; + const CandidateCompilers: TCompilerList; + const SelectedCompilers: TCompilerList): Boolean; +var + Dlg: TRegisterCompilersDlg; +begin + Assert(Assigned(CandidateCompilers), + ClassName + '.Execute: AvailCompilers list is nil'); + Assert(Assigned(SelectedCompilers), + ClassName + '.Execute: CompilersToReg list is nil'); + Assert(CandidateCompilers.Count > 0, + ClassName + '.Execute: AvailCompilers list must not be empty'); + + Dlg := InternalCreate(AOwner); + try + Dlg.fCandidateCompilers := CandidateCompilers; // reference to list + Dlg.fSelectedCompilers := SelectedCompilers; // reference to list + Result := Dlg.ShowModal = mrOK; + if Result then + begin + Dlg.StoreSelectedCompilers; + TCompilerSettings.PermitStartupDetection := + not Dlg.chkDontShowAgain.Checked; + end; + finally + Dlg.Free; + end; +end; + +procedure TRegisterCompilersDlg.FormDestroy(Sender: TObject); +var + Idx: Integer; +begin + // Free TBox<> objects associated with compiler list items + for Idx := Pred(clbCompilers.Count) downto 0 do + clbCompilers.Items.Objects[Idx].Free; + inherited; +end; + +procedure TRegisterCompilersDlg.InitForm; +begin + inherited; + SetCaptions; + PopulateList; +end; + +procedure TRegisterCompilersDlg.PopulateList; +var + Compiler: ICompiler; +begin + clbCompilers.Items.BeginUpdate; + try + clbCompilers.Clear; + for Compiler in fCandidateCompilers do + begin + clbCompilers.Items.AddObject( + Compiler.GetName, + TBox<ICompiler>.Create(Compiler) + ); + end; + finally + clbCompilers.Items.EndUpdate; + end; + Assert(clbCompilers.Count > 0, + ClassName + '.PopulateList: compiler list empty'); + clbCompilers.ItemIndex := 0; +end; + +procedure TRegisterCompilersDlg.SetCaptions; +resourcestring + sFormCaptionS = 'Unregistered Delphi Installations Detected'; + sFormCaptionP = 'Unregistered Delphi Installation Detected'; + sDescS = '&Unregistered compiler. Tick to register:'; + sDescP = '&Unregistered compilers. Tick the ones to be registered:'; +begin + // Set form and description captions + if fCandidateCompilers.Count = 1 then + begin + Caption := sFormCaptionS; + lblDesc.Caption := sDescS; + end + else + begin + // fCandidateCompilers.Count > 1 because fCandidateCompilers.Count > 0 assured by + // assertions in Execute method + Caption := sFormCaptionP; + lblDesc.Caption := sDescP; + end; +end; + +procedure TRegisterCompilersDlg.StoreSelectedCompilers; +var + I: Integer; +begin + fSelectedCompilers.Clear; + for I := 0 to Pred(clbCompilers.Count) do + if clbCompilers.Checked[I] then + begin + fSelectedCompilers.Add( + TBox<ICompiler>(clbCompilers.Items.Objects[I]).Value + ); + end; +end; + +end. + diff --git a/Src/HTML.hrc b/Src/HTML.hrc index a731c26f2..600d279b8 100644 --- a/Src/HTML.hrc +++ b/Src/HTML.hrc @@ -43,6 +43,9 @@ Res\HTML\dlg-whatsnew.html # delete database dialogue Res\HTML\dlg-dbdelete.html +# auto-register new compilers dialogue +Res\HTML\dlg-register-compilers.html + # Detail pane pages, scripts and CSS # ================================== diff --git a/Src/Res/HTML/dlg-register-compilers.html b/Src/Res/HTML/dlg-register-compilers.html new file mode 100644 index 000000000..773bf8734 --- /dev/null +++ b/Src/Res/HTML/dlg-register-compilers.html @@ -0,0 +1,59 @@ +<?xml version="1.0"?> + +<!DOCTYPE html + PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" + "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> + +<!-- + * This Source Code Form is subject to the terms of the Mozilla Public License, + * v. 2.0. If a copy of the MPL was not distributed with this file, You can + * obtain one at http://mozilla.org/MPL/2.0/ + * + * Copyright (C) 2022, Peter Johnson (gravatar.com/delphidabbler). + * + * Instructions diplayed on Register Compilers dialogue box. +--> + +<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> + + <head> + <title>Register Compilers Configration + + + +

    + If you don't wish to receive notifications like this again you have two + options: +

    +
      +
    1. +

      + Turn off all checks for new compiler installations. + Do this by ticking the Don't show this again check box below. +

      +

      + If you change your mind later you can re-enable the checks from the + Configure Compilers dialogue box. +

      +
    2. +
    3. +

      + Turn notifications on or off on a per-compiler basis. + You need to do this after you have closed this dialogue box, again by + using the Configure Compilers dialogue box. +

      +
    4. +
    +

    + For information about how to configure automatic detection of Delphi + compilers see the Register Compilers with CodeSnip help topic. +

    +

    + Note: only Delphi compilers can be automatically + registered with CodeSnip: this doesn't work with Free Pascal. +

    + + From a7286b4a2928d4cc5160bee667fc84f712b1e3fa Mon Sep 17 00:00:00 2001 From: delphidabbler <5164283+delphidabbler@users.noreply.github.com> Date: Thu, 15 Dec 2022 17:50:05 +0000 Subject: [PATCH 23/34] Add support for registering new compiler at startup Add TMainCompileMgr.CheckForNewCompilerInstalls method to UCompileMgr that checks for registerable compilers, gets user choices using FmRegisterCompilers dialogue box thwn registers any selected compiler. The main form, FmMain, simply calls CheckForNewCompilerInstalls just after the main windows displays, in TMainForm.AfterShow. --- Src/FmMain.pas | 2 + Src/UCompileMgr.pas | 118 ++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 117 insertions(+), 3 deletions(-) diff --git a/Src/FmMain.pas b/Src/FmMain.pas index 828ef997f..f2f5d1e91 100644 --- a/Src/FmMain.pas +++ b/Src/FmMain.pas @@ -1218,6 +1218,8 @@ procedure TMainForm.AfterShowForm; // initialise display fMainDisplayMgr.Initialise(fWindowSettings.OverviewTab); fMainDisplayMgr.ShowWelcomePage; + // check for registerable Delphi compiler installations + fCompileMgr.CheckForNewCompilerInstalls; end; function TMainForm.appEventsHelp(Command: Word; Data: Integer; diff --git a/Src/UCompileMgr.pas b/Src/UCompileMgr.pas index 22a5271ad..8a7cea3b5 100644 --- a/Src/UCompileMgr.pas +++ b/Src/UCompileMgr.pas @@ -3,7 +3,7 @@ * v. 2.0. If a copy of the MPL was not distributed with this file, You can * obtain one at https://mozilla.org/MPL/2.0/ * - * Copyright (C) 2009-2021, Peter Johnson (gravatar.com/delphidabbler). + * Copyright (C) 2009-2022, Peter Johnson (gravatar.com/delphidabbler). * * Provides objects that manage test compilation and assoicated UI, display of * compilation results via a callback and and compiler configuration. @@ -91,7 +91,8 @@ TCompileMgr = class(TComponent) { TMainCompileMgr: Extended compilation manager for use with main form. Checks if a view item - can be compiled and also permits user to configure compilers. + can be compiled, permits user to configure compilers and checks for newly + installed compilers. } TMainCompileMgr = class(TCompileMgr) public @@ -112,6 +113,15 @@ TMainCompileMgr = class(TCompileMgr) properties. @return True if user accepts changes, False if not. } + /// Check for new compiler installations, get user permission to + /// install any that are found and register any compilers that user + /// selects. + /// + /// Does nothing if compiler detection is disabled or if there are + /// no installed but unregistered compilers. + /// Should be called at program startup. + /// + procedure CheckForNewCompilerInstalls; end; @@ -121,8 +131,18 @@ implementation uses // Delphi SysUtils, + Generics.Collections, // Project - Compilers.UCompilers, DB.UMain, FmCompErrorDlg, FmCompilersDlg, + Compilers.UAutoDetect, + Compilers.UCompilers, + Compilers.USettings, + DB.UMain, + FmCompErrorDlg, + FmCompilersDlg, + FmRegisterCompilersDlg, + UConsts, + UMessageBox, + UStrUtils, UTestCompileUI; @@ -244,6 +264,98 @@ function TMainCompileMgr.CanCompile(View: IView): Boolean; and SnippetView.Snippet.CanCompile; end; +procedure TMainCompileMgr.CheckForNewCompilerInstalls; +var + CandidateCompilers: TCompilerList; // compilers available for registration + SelectedCompilers: TCompilerList; // compilers chosen for registration + Persister: IPersistCompilers; // object to store compiler data in config + + // Display message box informing user of which compilers were registered + // MUST be called with non zero number of registered compilers + procedure NotifyResults; + var + CompList: string; // string containing list of compilers registered + Compiler: ICompiler; // each compiler + RegCount: Integer; // count of compilers registered + resourcestring + sPrefixS = 'The following compiler was registered:'; + sPrefixP = 'The following compilers were registered:'; + sNoRegistrations = 'Unexpected error. None of the requested compilers were ' + + 'registered.'; + begin + CompList := ''; + RegCount := 0; + for Compiler in Compilers do + begin + if (SelectedCompilers.IndexOf(Compiler) >= 0) + and Compiler.IsAvailable then + begin + CompList := CompList + '• ' + Compiler.GetName + EOL; + Inc(RegCount); + end; + end; + if RegCount > 0 then + begin + CompList := StrIf(RegCount = 1, sPrefixS, sPrefixP) + EOL2 + CompList; + TMessageBox.Information(Owner, CompList); + end + else + TMessageBox.Error(Owner, sNoRegistrations); + end; + + // Display message to user informing that no compilers were registred + // MUST be called only with a zero number of registered compilers + procedure NotifyNoRegistrations; + resourcestring + sNothingRegistered = 'No compilers were selected for registration'; + begin + TMessageBox.Information(Owner, sNothingRegistered); + end; + +begin + if not TCompilerSettings.PermitStartupDetection then + Exit; + SelectedCompilers := nil; + CandidateCompilers := TCompilerList.Create; + try + SelectedCompilers := TCompilerList.Create; + // Build list of compilers that are installed but not registered + TCompilerAutoDetect.ListRegisterableCompilers( + Self.Compilers, CandidateCompilers + ); + if CandidateCompilers.Count = 0 then + Exit; // no compilers to register + + // We have candidate compilers: get user to select + if TRegisterCompilersDlg.Execute( + Owner, + CandidateCompilers, + SelectedCompilers + ) then + begin + if SelectedCompilers.Count > 0 then + begin + // User selected one or more compilers to register + // register compiler(s) + TCompilerAutoDetect.RegisterSpecificCompilers( + Compilers, SelectedCompilers + ); + // update config file with changes + Persister := TPersistCompilers.Create; + Persister.Save(Compilers); + // tell user what got registered + NotifyResults; + end + else + // User didn't select a file: tell them + NotifyNoRegistrations; + end; + finally + SelectedCompilers.Free; + CandidateCompilers.Free; + end; +end; + function TMainCompileMgr.ConfigCompilers: Boolean; {Displays Configure Compilers dialog to permit user to update compiler properties. From 4e6924ffd68741a77530f6344f4a24089caea1a2 Mon Sep 17 00:00:00 2001 From: delphidabbler <5164283+delphidabbler@users.noreply.github.com> Date: Tue, 13 Dec 2022 16:05:22 +0000 Subject: [PATCH 24/34] Update help files for compiler registration changes Add & update help re auto-detect compiler feature Add new dlg_registercompilers.htm and task_registercompilers.htm help topics. Noted new option to switch off compiler auto-detection. Update other topics re the new topics and auto-detection feature. Update help project, index and TOC files re new topics. --- Src/Help/CodeSnip.hhp | 2 + Src/Help/HTML/about_compiler_checks.htm | 24 ++++- Src/Help/HTML/dlg_configcompilers.htm | 57 +++++++++-- Src/Help/HTML/dlg_registercompilers.htm | 66 +++++++++++++ Src/Help/HTML/task_registercompilers.htm | 120 +++++++++++++++++++++++ Src/Help/HTML/tasks.htm | 5 + Src/Help/Index.hhk | 8 ++ Src/Help/TOC.hhc | 4 + 8 files changed, 276 insertions(+), 10 deletions(-) create mode 100644 Src/Help/HTML/dlg_registercompilers.htm create mode 100644 Src/Help/HTML/task_registercompilers.htm diff --git a/Src/Help/CodeSnip.hhp b/Src/Help/CodeSnip.hhp index 271557747..c9d4acebd 100644 --- a/Src/Help/CodeSnip.hhp +++ b/Src/Help/CodeSnip.hhp @@ -53,6 +53,7 @@ HTML\dlg_prefs_printing.htm HTML\dlg_prefs_snippetlayout.htm HTML\dlg_prefs_sourcecode.htm HTML\dlg_print.htm +HTML\dlg_registercompilers.htm HTML\dlg_renamecategory.htm HTML\dlg_restore.htm HTML\dlg_savehiliter.htm @@ -101,6 +102,7 @@ HTML\task_deleteuserdb.htm HTML\task_export.htm HTML\task_generateunit.htm HTML\task_printroutine.htm +HTML\task_registercompilers.htm HTML\task_savesnippet.htm HTML\task_search.htm HTML\task_testcompile.htm diff --git a/Src/Help/HTML/about_compiler_checks.htm b/Src/Help/HTML/about_compiler_checks.htm index 0aa09cb0e..54ab897b0 100644 --- a/Src/Help/HTML/about_compiler_checks.htm +++ b/Src/Help/HTML/about_compiler_checks.htm @@ -41,7 +41,8 @@

    Before CodeSnip can use the compilers it needs to be told about - them. This is done using the CodeSnip This is done + using the Configure Compilers dialogue box that is accessed from the Tools menu.

    @@ -60,7 +61,26 @@

    different compiler switches and search paths.

    - The portable version of CodeSnip only maintains one set of compiler configuration information per installation. + The portable version of CodeSnip only maintains one set of compiler + configuration information per installation. +

    +

    + Automatic detection of Delphi compilers +

    +

    + By default, when CodeSnip starts up, it will check if there are + any versions of Delphi installed on the user's system that + CodeSnip could use, but that are not registered for use. Any such + compilers are displayed in the Unregistered Delphi Installation(s) Detected dialogue box where you + can choose whether or not to register them. +

    +

    + It is possible to switch off automatic detection on either a global or a + per compiler basis. For more information see the Register Compilers with CodeSnip topic.

    Running the tests diff --git a/Src/Help/HTML/dlg_configcompilers.htm b/Src/Help/HTML/dlg_configcompilers.htm index 9055f3c9f..73e6ad0be 100644 --- a/Src/Help/HTML/dlg_configcompilers.htm +++ b/Src/Help/HTML/dlg_configcompilers.htm @@ -37,12 +37,12 @@

    Compilers that are installed on the system. It is displayed using the Tools | Configure Compilers menu option. CodeSnip uses these compilers when test - compiling routines. + compiling snippets.

    When using the standard version of CodeSnip each logged on user has their - their own compiler configuration, so any changes you make will not affect - other users. The portable edition of CodeSnip only supports one set of + own compiler configuration, so any changes you make will not affect other + users. The portable edition of CodeSnip only supports one set of configuration data per installation.

    @@ -54,11 +54,14 @@

    Selecting a compiler on the left displays information about it on the right hand side of the dialogue. Here you enter or edit the required - information on three tabs, as follows. + information on four or five tabs, as follows.

    Compiler Tab

    +

    + Compiler file name +

    Enter the the full path to the compiler's executable file in the Enter compiler executable file name edit box. This must be file name of @@ -75,8 +78,11 @@

    path to its executable file. The Enter compiler executable file name edit box is cleared.

    +

    + Display compiler results +

    - Finally, you can choose whether the compiler is included in the table of + You can choose whether the compiler is included in the table of compile results displayed in the Details Pane. This is done using the Display results for this compiler in details pane check box. Tick the box to display the compiler's @@ -97,6 +103,31 @@

    remove the Compiler Results Table component from the different kinds of snippet pages.

    +

    + Automatic compiler detection & registration +

    +

    + By default CodeSnip will automatically detect and register + installed Delphi compilers for use with the program. This can happen in + two ways: +

    +
      +
    1. + When the Detect Delphi Compilers button is clicked (see below). +
    2. +
    3. + Optionally, when the program starts up. +
    4. +
    +

    + You can prevent a specific Delphi compiler from being automatically + registered by clearing the Permit auto-detection & registration of + this compiler check box. +

    +

    + Note: This check box does not appear when the Free Pascal + compiler is selected. +

    Switches Tab

    @@ -283,13 +314,23 @@

    CodeSnip can automatically detect the presence of Win 32 Delphi compilers from Delphi 2 to Delphi 11.x Alexandria. Click the Detect Delphi Compilers button to do this. Any supported installed version - of Delphi will be recorded. This can save considerable time and avoid - errors. + of Delphi will be recorded. This can save considerable + time and avoid errors.

    Free Pascal cannot be detected automatically and must be configured manually.

    +

    + If the Automatically register newly installed Delphi compilers at + program startup check box is ticked then, when it starts up, + CodeSnip will attempt to register any known Delphi compiler that + is not yet registered with Delphi. +

    +

    + † Any version of Delphi for which the Permit auto-detection + & registration of this compiler check box is cleared will not be + recorded. +

    - diff --git a/Src/Help/HTML/dlg_registercompilers.htm b/Src/Help/HTML/dlg_registercompilers.htm new file mode 100644 index 000000000..3dc52b335 --- /dev/null +++ b/Src/Help/HTML/dlg_registercompilers.htm @@ -0,0 +1,66 @@ + + + + + + + Unregistered Delphi Installation(s) Detected Dialogue Box + + + + + + + +

    + Unregistered Delphi Installation(s) + Detected Dialogue Box +

    +

    + This dialogue box is displayed at program start-up if CodeSnip + detects that Delphi compilers are installed on the user's system that have + not yet been registered for use by CodeSnip. +

    +

    + The available compilers are listed at the top of the dialogue box. Each + compiler has a check box to its left. You should tick this check box to + permit CodeSnip to register the compiler or leave it clear if you + don't want to register the compiler with CodeSnip. +

    +

    + Underneath the list of compilers are some instructions about how to + disable auto-registration of some or all compilers. +

    +

    + Once you have made your selection click the OK button to register + the selected compilers with CodeSnip. A message box will pop up + to confirm that the compilers have been registered. +

    +

    + Preventing automatic detection +

    +

    + You can prevent CodeSnip from checking for any + new compilers on startup in future by ticking the Don't show this + again check box before clicking OK. +

    +

    + If you change your mind startup checks can be re-enabled. Alternatively + you can exclude individual Delphi compilers from automatic detection. + For information about how to do this see Register Compilers with CodeSnip. +

    + + diff --git a/Src/Help/HTML/task_registercompilers.htm b/Src/Help/HTML/task_registercompilers.htm new file mode 100644 index 000000000..21d556623 --- /dev/null +++ b/Src/Help/HTML/task_registercompilers.htm @@ -0,0 +1,120 @@ + + + + + + + Register Compilers + + + + + + + +

    + Register Compilers with + CodeSnip +

    +

    + Before you can test compile a snippet you must first register one or more installed + compilers with CodeSnip. +

    +

    + To do this you must tell CodeSnip how to find the compiler and + configure the required options for it. There are three ways to do this: +

    +
      +
    1. +

      + Manually configure the compiler using the Configure Compilers dialogue box, accessed from the Tools | + Configure Compilers dialogue box. +

      +

      + Note: This is the only option available for Free + Pascal. +

      +
    2. +
    3. +

      + Use the Detect Delphi Compilers button in the Configure Compilers dialogue box. This searches for any installed + Delphi compilers that are not registered and registers them. Any + compilers for which automatic detection has been disabled is ignored + (see below). +

      +
    4. +
    5. +

      + By default CodeSnip checks for any un-registered installed + version of Delphi at start-up, and offers to register them by popping + up the Unregistered Delphi Installation(s) Detected Dialogue Box. +

      +
    6. +
    +

    + Inhibiting Auto-Detection +

    +

    + If you don't want CodeSnip to detect any new, unregistered, + Delphi compilers at startup you can switch the feature off entirely from + the Unregistered Delphi Installation(s) Detected Dialogue Box, by ticking + the Don't show this again check box. You can do the same thing + from the Configure Compilers dialogue box by clearing the Automatically + register newly installed Delphi compilers at startup check box. +

    +

    + Alternatively, if you don't want CodeSnip to automatically detect + one or more specific Delphi compilers you can do that in the Configure Compilers dialogue box. You just need to clear the + Permit auto-detection & registration of this compiler check + box on a selected compiler's Compiler tab. If you do this then + CodeSnip will not suggest registering that compiler at startup. + The Configure Compilers dialogue box's Detect Delphi Compilers + feature will ignore them too. +

    +

    + (Re-)enabling Auto-Detection +

    +

    + Global automatic detection can be enabled from the Configure Compilers dialogue box by ticking the Automatically + register newly installed Delphi compilers at startup check box. +

    +

    + Individually excluded compilers can be included in automatic detection + again by ticking the Permit auto-detection & registration of this + compiler check box on a selected compiler's Compiler tab in + the Configure Compilers dialogue box. +

    + + + diff --git a/Src/Help/HTML/tasks.htm b/Src/Help/HTML/tasks.htm index 2c11e282a..196b56c9c 100644 --- a/Src/Help/HTML/tasks.htm +++ b/Src/Help/HTML/tasks.htm @@ -42,6 +42,7 @@

  • Print Information about a Snippet +
  • Generate a Pascal Unit
  • @@ -51,6 +52,10 @@

  • Test Compile a Snippet
  • +
  • + Register a Compiler for Test + Compilation +
  • Install or Update the DelphiDabbler Code Snippets Database diff --git a/Src/Help/Index.hhk b/Src/Help/Index.hhk index f5550c5d1..0b17203b6 100644 --- a/Src/Help/Index.hhk +++ b/Src/Help/Index.hhk @@ -99,6 +99,10 @@ +
  • + + +
  • @@ -146,6 +150,10 @@ +
  • + + +
  • diff --git a/Src/Help/TOC.hhc b/Src/Help/TOC.hhc index cd91c1f05..ac2c63139 100644 --- a/Src/Help/TOC.hhc +++ b/Src/Help/TOC.hhc @@ -132,6 +132,10 @@ +
  • + + +
  • From d8b61dda10ba74b53ee85186a78aae661b93500f Mon Sep 17 00:00:00 2001 From: delphidabbler <5164283+delphidabbler@users.noreply.github.com> Date: Fri, 16 Dec 2022 00:14:41 +0000 Subject: [PATCH 25/34] Documents new values & section in config file Update config file documentation with details of changes resulting from implementing issue 19. Add TODO placeholders that need to be resolved once next release version and date are known. Closes #74 --- Docs/Design/FileFormats/config.html | 43 ++++++++++++++++++++++++++++- 1 file changed, 42 insertions(+), 1 deletion(-) diff --git a/Docs/Design/FileFormats/config.html b/Docs/Design/FileFormats/config.html index c81851d94..d925f86c5 100644 --- a/Docs/Design/FileFormats/config.html +++ b/Docs/Design/FileFormats/config.html @@ -167,7 +167,7 @@

    - There have been several versions of this file. The current one is version 18. The change to version 18 came with CodeSnip v4.20.0 and the addition of the [Prefs] section. + There have been several versions of this file. The current one is version 18 ««TODO»». The change to version 18 came with CodeSnip v4.20.0««TODO»» and the addition of the [Prefs] section.

    @@ -333,8 +333,49 @@

    Each entry contains a fully specified directory path. +
    + CanAutoInstall (Boolean) +
    +
    +
    + Determines whether the compiler can be automatically detected and registered by CodeSnip. +
    +
    + Applies to Delphi compilers only, not to Free Pascal. +
    +
    + + +

    + [Compilers] section +

    + +

    + This section records configuration information that applies to all, or + multiple, compilers. +

    + +

    + Name / Value pairs: +

    + +
    +
    + PermitStartupDetection (Boolean) +
    +
    +
    + Determines whether CodeSnip should detect, and potentially register, any + Delphi compilers that are installed on the user's system but not + registered with the program. +
    +
    + Does not apply to the Free Pascal compiler. +
    +
    +

    [Database] section

    From 4ee4a9c2888b551710e260abb4badbe734cdcd88 Mon Sep 17 00:00:00 2001 From: delphidabbler <5164283+delphidabbler@users.noreply.github.com> Date: Fri, 16 Dec 2022 01:17:02 +0000 Subject: [PATCH 26/34] Extract common version information into include file Version information from VCodeSnip.vi and VCodeSnipPortable.vi that is common to both files was extracted into the VersionInfo.vi-inc file as macro definitions. The macros were then imported into to .vi files. Changed file description. Renamed program from historic "Viewer" (that dates back to v1!) to "Repository". Also added "Standard Version" for standard version to match "Portable Version" that was already appended to file description in portable version. As a side effect, this commit fixes problem of rendering copyright symbol, because VIEd handles UTF-8 correctly in macro import files but not in .vi files! Closes #75 --- Src/VCodeSnip.vi | 19 ++++++++++++------- Src/VCodeSnipPortable.vi | 19 ++++++++++++------- Src/VersionInfo.vi-inc | 12 ++++++++++++ 3 files changed, 36 insertions(+), 14 deletions(-) create mode 100644 Src/VersionInfo.vi-inc diff --git a/Src/VCodeSnip.vi b/Src/VCodeSnip.vi index 3ed5f7959..8dc6fb9de 100644 --- a/Src/VCodeSnip.vi +++ b/Src/VCodeSnip.vi @@ -7,9 +7,12 @@ ; Version information description file for CodeSnip. +[Macros] +Import:ver=.\VersionInfo.vi-inc + [Fixed File Info] -File Version #=4, 20, 2, 266 -Product Version #=4, 20, 2, 0 +File Version #=<%ver.version>.<%ver.build> +Product Version #=<%ver.version> File OS=4 File Type=1 File Sub-Type=0 @@ -21,19 +24,21 @@ Language=2057 Character Set=1252 [String File Info] -Comments=Released under the terms of the Mozilla Public License v2.0 (https://www.mozilla.org/MPL/2.0/) -Company Name=DelphiDabbler -File Description=CodeSnip Database Viewer +Comments=<%var.license> +Company Name=<%ver.company> +File Description=<%ver.description> (Standard Edition) File Version=<#F1>.<#F2>.<#F3> build <#F4> Internal Name= -Legal Copyright=Copyright © P.D.Johnson, 2005-. +Legal Copyright=<%ver.copyright> Legal Trademark= Original File Name=CodeSnip.exe Private Build= -Product Name=DelphiDabbler CodeSnip +Product Name=<%ver.company> <%ver.name> Product Version=Release <#P1>.<#P2>.<#P3> Special Build= [Configuration Details] Identifier= NumRCComments=0 +ResOutputDir= +FileVersion=1 diff --git a/Src/VCodeSnipPortable.vi b/Src/VCodeSnipPortable.vi index ff5d8d1b6..90646ad38 100644 --- a/Src/VCodeSnipPortable.vi +++ b/Src/VCodeSnipPortable.vi @@ -7,9 +7,12 @@ ; Version information description file for the portable edition of CodeSnip +[Macros] +Import:ver=.\VersionInfo.vi-inc + [Fixed File Info] -File Version #=4, 20, 2, 266 -Product Version #=4, 20, 2, 0 +File Version #=<%ver.version>.<%ver.build> +Product Version #=<%ver.version> File OS=4 File Type=1 File Sub-Type=0 @@ -21,19 +24,21 @@ Language=2057 Character Set=1252 [String File Info] -Comments=Released under the terms of the Mozilla Public License v2.0 (https://www.mozilla.org/MPL/2.0/) -Company Name=DelphiDabbler -File Description=CodeSnip Database Viewer (Portable Edition) +Comments=<%var.license> +Company Name=<%ver.company> +File Description=<%ver.description> (Portable Edition) File Version=<#F1>.<#F2>.<#F3> build <#F4> Internal Name= -Legal Copyright=Copyright © P.D.Johnson, 2005-. +Legal Copyright=<%ver.copyright> Legal Trademark= Original File Name=CodeSnip-p.exe Private Build= -Product Name=DelphiDabbler CodeSnip +Product Name=<%ver.company> <%ver.name> Product Version=Release <#P1>.<#P2>.<#P3> Special Build=Portable [Configuration Details] Identifier= NumRCComments=0 +ResOutputDir= +FileVersion=1 diff --git a/Src/VersionInfo.vi-inc b/Src/VersionInfo.vi-inc new file mode 100644 index 000000000..b75936ffb --- /dev/null +++ b/Src/VersionInfo.vi-inc @@ -0,0 +1,12 @@ +# CodeSnip Version Information Macros for Including in .vi files + +# Version & build numbers +version=4.22.2 +build=266 + +# String file information +copyright=Copyright © P.D.Johnson, 2005-. +description=Code Snippets Repository +company=DelphiDabbler +name=CodeSnip +license=Released under the terms of the Mozilla Public License v2.0 (https://www.mozilla.org/MPL/2.0/) From be944290f0710d797b1d629ae9f61b54fdae6ef8 Mon Sep 17 00:00:00 2001 From: delphidabbler <5164283+delphidabbler@users.noreply.github.com> Date: Fri, 16 Dec 2022 01:28:03 +0000 Subject: [PATCH 27/34] Update Build.html re change in required VIEd version --- Build.html | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Build.html b/Build.html index a6a432a39..679825b07 100644 --- a/Build.html +++ b/Build.html @@ -6,7 +6,7 @@ * v. 2.0. If a copy of the MPL was not distributed with this file, You can * obtain one at https://mozilla.org/MPL/2.0/ * - * Copyright (C) 2009-2021, Peter Johnson (gravatar.com/delphidabbler). + * Copyright (C) 2009-2022, Peter Johnson (gravatar.com/delphidabbler). * * Instructions for building CodeSnip from source. --> @@ -191,10 +191,10 @@

    - This tool is used to compile version information (.vi) files - into intermediate resource source (.rc) files. Version 2.11.2 - or later is required. - Version Information Editor can be obtained from + This tool is used to compile version information (.vi) files and + any associated macro file(s) into intermediate resource source + (.rc) files. Version 2.14.0 or later is required. Version + Information Editor can be obtained from https://github.com/delphidabbler/vied/releases. From c29bd68727653e19f3ce2eb7cb7b6b8ad044b252 Mon Sep 17 00:00:00 2001 From: delphidabbler <5164283+delphidabbler@users.noreply.github.com> Date: Fri, 16 Dec 2022 16:24:53 +0000 Subject: [PATCH 28/34] Fix format error in REML code generator Code in UREMLDataIO unit that pretty-printed REML from active text was malforming some lines where (a) text followed a closing block tag and (b) an inline tag immediately followed an opening block tag. --- Src/UREMLDataIO.pas | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/Src/UREMLDataIO.pas b/Src/UREMLDataIO.pas index acd5bc135..c6ae83bbc 100644 --- a/Src/UREMLDataIO.pas +++ b/Src/UREMLDataIO.pas @@ -598,6 +598,7 @@ function TREMLWriter.RenderTag( begin Dec(fLevel); Result := EOL + StrOfSpaces(IndentMult * fLevel) + Result + EOL; + fIsStartOfTextLine := True; end; end; fsOpen: @@ -620,6 +621,14 @@ function TREMLWriter.RenderTag( Result := EOL + StrOfSpaces(IndentMult * fLevel) + Result + EOL; Inc(fLevel); fIsStartOfTextLine := True; + end + else if TActiveTextElemCaps.DisplayStyleOf(TagElem.Kind) = dsInline then + begin + if fIsStartOfTextLine then + begin + Result := StrOfSpaces(IndentMult * fLevel) + Result; + fIsStartOfTextLine := False; + end; end; end; end; From e073b9c443abb2e721eb71cca41ce100d2822a96 Mon Sep 17 00:00:00 2001 From: delphidabbler <5164283+delphidabbler@users.noreply.github.com> Date: Fri, 16 Dec 2022 16:30:27 +0000 Subject: [PATCH 29/34] Fix welcome page redraw after compilers registered Main form was not redrawing the welcome page to reflect the addition of compilers after user accepted new compilers automatically detected at startup. Changed UCompileMgr.TMainCompileMgr.CheckForNewCompilerInstalls to return True if user had permitted compilers to be registered. Main form's AfterShowForm event handler wa changed to refresh the display when the method returns True. --- Src/FmMain.pas | 3 ++- Src/UCompileMgr.pas | 9 ++++++--- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/Src/FmMain.pas b/Src/FmMain.pas index f2f5d1e91..54ee4a6bf 100644 --- a/Src/FmMain.pas +++ b/Src/FmMain.pas @@ -1219,7 +1219,8 @@ procedure TMainForm.AfterShowForm; fMainDisplayMgr.Initialise(fWindowSettings.OverviewTab); fMainDisplayMgr.ShowWelcomePage; // check for registerable Delphi compiler installations - fCompileMgr.CheckForNewCompilerInstalls; + if fCompileMgr.CheckForNewCompilerInstalls then + fMainDisplayMgr.Refresh; end; function TMainForm.appEventsHelp(Command: Word; Data: Integer; diff --git a/Src/UCompileMgr.pas b/Src/UCompileMgr.pas index 8a7cea3b5..47dc26f18 100644 --- a/Src/UCompileMgr.pas +++ b/Src/UCompileMgr.pas @@ -116,12 +116,14 @@ TMainCompileMgr = class(TCompileMgr) ///

    Check for new compiler installations, get user permission to /// install any that are found and register any compilers that user /// selects. + /// Boolean. True if any compilers were registered, False if not. + /// /// /// Does nothing if compiler detection is disabled or if there are /// no installed but unregistered compilers. /// Should be called at program startup. /// - procedure CheckForNewCompilerInstalls; + function CheckForNewCompilerInstalls: Boolean; end; @@ -264,7 +266,7 @@ function TMainCompileMgr.CanCompile(View: IView): Boolean; and SnippetView.Snippet.CanCompile; end; -procedure TMainCompileMgr.CheckForNewCompilerInstalls; +function TMainCompileMgr.CheckForNewCompilerInstalls: Boolean; var CandidateCompilers: TCompilerList; // compilers available for registration SelectedCompilers: TCompilerList; // compilers chosen for registration @@ -313,6 +315,7 @@ procedure TMainCompileMgr.CheckForNewCompilerInstalls; end; begin + Result := False; if not TCompilerSettings.PermitStartupDetection then Exit; SelectedCompilers := nil; @@ -345,6 +348,7 @@ procedure TMainCompileMgr.CheckForNewCompilerInstalls; Persister.Save(Compilers); // tell user what got registered NotifyResults; + Result := True; end else // User didn't select a file: tell them @@ -381,4 +385,3 @@ function TMainCompileMgr.IsLastCompiledView(View: IView): Boolean; end; end. - From e45c1490e6d360a930bab3f32e6be7efa30bb1f3 Mon Sep 17 00:00:00 2001 From: delphidabbler <5164283+delphidabbler@users.noreply.github.com> Date: Fri, 16 Dec 2022 16:35:27 +0000 Subject: [PATCH 30/34] Fix corrupted bullet character in message box. Modified UCompileMgr.TMainCompileMgr.CheckForNewCompilerInstalls to specify Unicode hex character representation of bullet character instead of literal character when displaying message box that confirms new compilers have been registered. --- Src/UCompileMgr.pas | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Src/UCompileMgr.pas b/Src/UCompileMgr.pas index 47dc26f18..ded36eca3 100644 --- a/Src/UCompileMgr.pas +++ b/Src/UCompileMgr.pas @@ -292,7 +292,7 @@ function TMainCompileMgr.CheckForNewCompilerInstalls: Boolean; if (SelectedCompilers.IndexOf(Compiler) >= 0) and Compiler.IsAvailable then begin - CompList := CompList + '• ' + Compiler.GetName + EOL; + CompList := CompList + #$2022' ' + Compiler.GetName + EOL; Inc(RegCount); end; end; From 845c392cb0640218706bd860b96bc66a7c9c54a5 Mon Sep 17 00:00:00 2001 From: delphidabbler <5164283+delphidabbler@users.noreply.github.com> Date: Fri, 16 Dec 2022 16:38:08 +0000 Subject: [PATCH 31/34] Replace TODO placeholders with CodeSnip ver & date Update design documents with CodeSnip release version and release date relating to changes in config, export, user database and REML file formats. Some other minor tweak too. --- Docs/Design/FileFormats/config.html | 4 ++-- Docs/Design/FileFormats/export.html | 6 +++--- Docs/Design/FileFormats/user-db.html | 6 +++--- Docs/Design/reml.html | 4 ++-- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/Docs/Design/FileFormats/config.html b/Docs/Design/FileFormats/config.html index d925f86c5..8c0061093 100644 --- a/Docs/Design/FileFormats/config.html +++ b/Docs/Design/FileFormats/config.html @@ -167,7 +167,7 @@

    - There have been several versions of this file. The current one is version 18 ««TODO»». The change to version 18 came with CodeSnip v4.20.0««TODO»» and the addition of the [Prefs] section. + There have been several versions of this file. The current one is version 19. The change to version 19 came with CodeSnip v4.21.0 and the addition of the [Compilers] section and the CanAutoInstall key in the [Cmp:XXX] sections.

    @@ -762,7 +762,7 @@

    The version number of the config file. Incremented whenever the file format changes. If this section or this value is missing then the default value is 1.
    - The current value is 16. + The current value is 19.
    diff --git a/Docs/Design/FileFormats/export.html b/Docs/Design/FileFormats/export.html index 3a383591d..5f57b1c60 100644 --- a/Docs/Design/FileFormats/export.html +++ b/Docs/Design/FileFormats/export.html @@ -175,7 +175,7 @@

    version

    - Identifies version of file. Determines which tags are valid and + Identifies major version of file. Determines which tags are valid and establishes rules concerning content. Valid versions are 1 to 7.
    @@ -979,10 +979,10 @@

    Updated with CodeSnip v4.18.0 to add support for Delphi 11.x Alexandria.
    - Version 7.3 - ««TODO»» + Version 7.3 - 16 December 2022
    - Updated with CodeSnip v««TODO»» to add support for REML v5, which is backward compatible with REML v4. + Updated with CodeSnip v4.21.0 to add support for REML v5, which is backward compatible with REML v4.
    diff --git a/Docs/Design/FileFormats/user-db.html b/Docs/Design/FileFormats/user-db.html index db90b23df..a5e03cfca 100644 --- a/Docs/Design/FileFormats/user-db.html +++ b/Docs/Design/FileFormats/user-db.html @@ -199,7 +199,7 @@

    version
    - Identifies version of file. Determines which tags are valid and rules + Identifies major version of file. Determines which tags are valid and rules concerning content. Valid versions are 1..6.
    @@ -1015,10 +1015,10 @@

    Updated with CodeSnip v4.18.0 to add support for Delphi 11.x Alexandria.
    - Version 6.11 - ««TODO»» + Version 6.11 - 16 December 2022
    - Updated with CodeSnip v««TODO»» to add support for REML v5, which is backwards compatible with REML v4. + Updated with CodeSnip v4.21.0 to add support for REML v5, which is backwards compatible with REML v4.
    diff --git a/Docs/Design/reml.html b/Docs/Design/reml.html index 3fce8fae1..9762decaf 100644 --- a/Docs/Design/reml.html +++ b/Docs/Design/reml.html @@ -625,11 +625,11 @@

    Change Log

    - v5 of ««TODO»» + v5 of 2022-12-16

    - Introduced in CodeSnip ««TODO»» + Introduced in CodeSnip v4.21.0

      From aa102f9c31e2f27312e1c9d6aa89bac0e630fc5f Mon Sep 17 00:00:00 2001 From: delphidabbler <5164283+delphidabbler@users.noreply.github.com> Date: Fri, 16 Dec 2022 16:39:53 +0000 Subject: [PATCH 32/34] Bump version # of user config file from 18 to 19 v19 is now stamped into config files when CodeSnip v4.21.0 starts for the first time. --- Src/FirstRun.UConfigFile.pas | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Src/FirstRun.UConfigFile.pas b/Src/FirstRun.UConfigFile.pas index 2645b6acc..50bba121b 100644 --- a/Src/FirstRun.UConfigFile.pas +++ b/Src/FirstRun.UConfigFile.pas @@ -82,7 +82,7 @@ TUserConfigFileUpdater = class(TConfigFileUpdater) strict private const /// Current user config file version. - FileVersion = 18; + FileVersion = 19; strict protected /// Returns current user config file version. class function GetFileVersion: Integer; override; From ef878a6611397cd3caadfff1bf2654d973eb737d Mon Sep 17 00:00:00 2001 From: delphidabbler <5164283+delphidabbler@users.noreply.github.com> Date: Fri, 16 Dec 2022 16:41:25 +0000 Subject: [PATCH 33/34] Update program version to 4.21.0 build 267 !!!NOTE The version number was actually reduced from v4.22.2 because that number was entered in error when VersionInfo.vi-inc was first created: the version should have been v4.20.2 !!! --- Src/VersionInfo.vi-inc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Src/VersionInfo.vi-inc b/Src/VersionInfo.vi-inc index b75936ffb..f255c1655 100644 --- a/Src/VersionInfo.vi-inc +++ b/Src/VersionInfo.vi-inc @@ -1,8 +1,8 @@ # CodeSnip Version Information Macros for Including in .vi files # Version & build numbers -version=4.22.2 -build=266 +version=4.21.0 +build=267 # String file information copyright=Copyright © P.D.Johnson, 2005-. From 97030525bcb2ab2812af20057ece7f46284f9bd2 Mon Sep 17 00:00:00 2001 From: delphidabbler <5164283+delphidabbler@users.noreply.github.com> Date: Fri, 16 Dec 2022 16:43:23 +0000 Subject: [PATCH 34/34] Update change log with details of release v4.21.0 --- CHANGELOG.md | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2c58946c1..36e25aaf7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,26 @@ This change log begins with the first ever pre-release version of _CodeSnip_. Re From v4.1.0 the version numbering has attempted to adhere to the principles of [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## Release v4.21.0 of 16 December 2022 + +* Updated to support [REML version 5](https://htmlpreview.github.io/?https://github.com/delphidabbler/codesnip/blob/version-4.21.0`/Docs/Design/reml.html) in snippet description & extra information [issue #71]: + * Numerous new character entities supported. + * New list tags: `
        `, `
          ` & `
        • `. +* Program now automatically detects new (supported) Delphi installations at startup and offers to register the compiler(s) to be used for test compiling snippets. This feature is on by default but can be turned off completely or for specifically excluded compilers [issue #19]. +* Modified Configure Compilers dialogue box: + * Added facility to customise automatic compiler detection on per-compiler or global basis. + * Changed manually triggered compiler detection to ignore excluded compilers. +* Some refactoring [including issues #73 and #75]. +* Minor changes to program license + * Changed required image attribution in `Docs/License.html` [issue #63] + * Corrected copyright date & fix typo in licenses displayed by installer and help file [issue #65 & PR #72]. +* Bump per-user config file to version 19. +* Documentation updates: + * Updated `README.md` re abandoned and new Git repo branches. + * Updated config file, database, export file & REML documentation re changes in this release [including issue #74]. + * Help file updated with details of changes in this release. + * Updated development tool chain requirements in `Build.html`. + ## Release v4.20.2 of 04 November 2022 * Fixes bug where an exception was raised when selecting a main menu item with the cursor keys then pressing F1. [issue 54]