From ca42523866c90e8bbeff5f5ce4490a311b076cc0 Mon Sep 17 00:00:00 2001
From: delphidabbler <5164283+delphidabbler@users.noreply.github.com>
Date: Mon, 14 Apr 2025 14:13:33 +0100
Subject: [PATCH 01/77] Revise hibernate bug fix (issue #70)
Following a discussion in the comments the DelphiDabbler Blog post at
https://tinyurl.com/mrp76mdy it seems that was not a good idea to rely
upon handling WM_POWERBROADCAST's PBT_APMPOWERSTATUSCHANGE event to
restore the overview pane's tree view nodes to the expected state after
Windows has recreated the tree view in an invalid state.
So I've modified the code to only rely on the PBT_APMSUSPEND event of
WM_POWERBROADCAST and not PBT_APMPOWERSTATUSCHANGE. PBT_APMSUSPEND is
handled to prepare for hibernation by not only saving the tree view's
state (as per the previous fix) but also setting an event handler that
gets called only when the tree view's window gets recreated by Windows
AND the treeview contains nodes with nil IView pointers. When called,
the event handler rebuilds the tree view with nodes containing valid
IView references.
The problem is that the event needs to be triggered from the
TTreeView.CreateWnd method that gets called when Windows recreates the
tree view. Since TTreeView exposes no suitable events, the only way is
to inject a suitable event using a nasty hack. Not good practise.
Note that all the methods that depend on the hack have been given names
beginning with "_HACK_" to make it obvious where the naughtiness lies.
---
Src/FmMain.pas | 10 ++++----
Src/FrOverview.pas | 52 +++++++++++++++++++++++++++++++++++++++++
Src/IntfFrameMgrs.pas | 6 ++++-
Src/UMainDisplayMgr.pas | 52 ++++++++++++++++++++---------------------
4 files changed, 87 insertions(+), 33 deletions(-)
diff --git a/Src/FmMain.pas b/Src/FmMain.pas
index adfd77419..8bffbe2b8 100644
--- a/Src/FmMain.pas
+++ b/Src/FmMain.pas
@@ -524,9 +524,10 @@ TMainForm = class(THelpAwareForm)
/// Handles the WM_POWERBROADCAST messages to detect and
/// respond to hibernation messages.
- /// This is necessary as part of the fix for an obscure bug. See
+ /// !! HACK necessary as part of the fix for an obscure bug. See
/// https://github.com/delphidabbler/codesnip/issues/70
procedure WMPowerBroadcast(var Msg: TMessage); message WM_POWERBROADCAST;
+
/// Displays view item given by TViewItemAction instance
/// referenced by Sender and adds to history list.
procedure ActViewItemExecute(Sender: TObject);
@@ -1587,6 +1588,7 @@ procedure TMainForm.splitVertCanResize(Sender: TObject;
procedure TMainForm.WMPowerBroadcast(var Msg: TMessage);
begin
+ // !! HACK
// Sometimes when the computer is resumed from hibernation the tree view in
// the overview frame is destroyed and recreated by Windows. Unfortunately the
// IView instances associated with the recreated tree nodes are lost.
@@ -1594,11 +1596,7 @@ procedure TMainForm.WMPowerBroadcast(var Msg: TMessage);
// access violation.
case Msg.WParam of
PBT_APMSUSPEND:
- // Get ready for isolation
- fMainDisplayMgr.PrepareForHibernate;
- PBT_APMPOWERSTATUSCHANGE:
- // Restore from hibernation: ensure the IView instances are recreeated
- fMainDisplayMgr.RestoreFromHibernation;
+ fMainDisplayMgr._HACK_PrepareForHibernate;
end;
end;
diff --git a/Src/FrOverview.pas b/Src/FrOverview.pas
index e608e6d4a..9cf76a2a8 100644
--- a/Src/FrOverview.pas
+++ b/Src/FrOverview.pas
@@ -26,6 +26,29 @@ interface
type
+ // !! HACK
+ // Horrible hack to expose CreateWnd for overiding TTreeView.CreateWnd for the
+ // existing TTreeView component of TOverviewFrame. The hack avoids having to
+ // remove the component and replacing it with a descendant class that is
+ // manually constructed at run time.
+ // This is here to enable the tree view to be recreated with correctly
+ // instantiated TViewItemTreeNode nodes after Windows recreates the tree
+ // behind the scenes after resuming from hibernation.
+ // I am deeply ashamed of this hack.
+ TTreeView = class(ComCtrls.TTreeView)
+ strict private
+ var
+ _HACK_fOnAfterCreateNilViews: TNotifyEvent;
+ protected
+ procedure CreateWnd; override;
+ public
+ /// !! HACK. Event triggered after the inherited CreateWnd is
+ /// called. Only called if the tree view has nil references to IView
+ /// objects.
+ property _HACK_OnAfterCreateNilViews: TNotifyEvent
+ read _HACK_fOnAfterCreateNilViews write _HACK_fOnAfterCreateNilViews;
+ end;
+
{
TOverviewFrame:
Titled frame that displays lists of snippets grouped in various ways and
@@ -214,6 +237,10 @@ TTVDraw = class(TSnippetsTVDraw)
procedure RestoreTreeState;
{Restores last saved treeview expansion state from memory.
}
+ /// !! HACK: Sets an event handler on the tree view to work
+ /// around a bug that can occur after resuming from hibernation.
+ /// Method of IOverviewDisplayMgr.
+ procedure _HACK_SetHibernateHandler(const AHandler: TNotifyEvent);
{ IPaneInfo }
function IsInteractive: Boolean;
{Checks if the pane is currently interactive with user.
@@ -955,6 +982,12 @@ procedure TOverviewFrame.UpdateTreeState(const State: TTreeNodeAction);
end;
end;
+procedure TOverviewFrame._HACK_SetHibernateHandler(
+ const AHandler: TNotifyEvent);
+begin
+ tvSnippets._HACK_OnAfterCreateNilViews := AHandler;
+end;
+
{ TOverviewFrame.TTVDraw }
function TOverviewFrame.TTVDraw.IsSectionHeadNode(
@@ -993,5 +1026,24 @@ function TOverviewFrame.TTVDraw.IsUserDefinedNode(
Result := False;
end;
+{ TTreeView }
+
+procedure TTreeView.CreateWnd;
+var
+ HasNilViews: Boolean;
+ Node: TTreeNode;
+begin
+ inherited;
+ HasNilViews := False;
+ for Node in Items do
+ begin
+ HasNilViews := not Assigned((Node as TViewItemTreeNode).ViewItem);
+ if HasNilViews then
+ Break;
+ end;
+ if HasNilViews and Assigned(_HACK_fOnAfterCreateNilViews) then
+ _HACK_fOnAfterCreateNilViews(Self);
+end;
+
end.
diff --git a/Src/IntfFrameMgrs.pas b/Src/IntfFrameMgrs.pas
index 0f409800c..b3cb76101 100644
--- a/Src/IntfFrameMgrs.pas
+++ b/Src/IntfFrameMgrs.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-2025, Peter Johnson (gravatar.com/delphidabbler).
*
* Declares interfaces, constants and enumerations required to manage various
* parts of CodeSnip's UI.
@@ -19,6 +19,7 @@ interface
uses
// Delphi
SHDocVw, ActiveX,
+ Classes, // !! For HACK
// Project
Browser.IntfDocHostUI, DB.USnippet, Compilers.UGlobals, UCommandBars, UView;
@@ -145,6 +146,9 @@ interface
/// Restore expand / collapse state of treeview to last save
/// state.
procedure RestoreTreeState;
+ /// !! HACK: Sets an event handler on the tree view to work
+ /// around a bug that can occur after resuming from hibernation.
+ procedure _HACK_SetHibernateHandler(const AHandler: TNotifyEvent);
end;
type
diff --git a/Src/UMainDisplayMgr.pas b/Src/UMainDisplayMgr.pas
index 65e98e2fb..0c64a17d5 100644
--- a/Src/UMainDisplayMgr.pas
+++ b/Src/UMainDisplayMgr.pas
@@ -165,6 +165,11 @@ TMainDisplayMgr = class(TObject)
procedure DisplayViewItem(ViewItem: IView; Mode: TDetailPageDisplayMode);
overload;
+ /// !! HACK event handle to redisplay the overview pane treeview.
+ /// Called only if Windows has mysteriously recreated the treeview and lost
+ /// necessary object references.
+ procedure _HACK_HibernateHandler(Sender: TObject);
+
public
/// Object contructor. Sets up object to work with given frame
/// manager objects.
@@ -292,21 +297,12 @@ TMainDisplayMgr = class(TObject)
/// Prepares display ready for database to be reloaded.
procedure PrepareForDBReload;
- /// Gets the overview frame prepared for program hibernation.
- ///
+ /// !!HACK: gets the overview frame prepared for program
+ /// hibernation.
/// Saves the overview tree view state ready for restoring after
- /// hibernation.
- procedure PrepareForHibernate;
-
- /// Restores the overview's tree view to have the correct IView
- /// instances after hibernation restores the previously saved state.
- ///
- /// Sometimes, Windows quietly recreates the node of the tree view
- /// after resuming from hibernation, without restoring the associated IView
- /// instances, leading to access violations. This method should be called
- /// after resuming from hibernation to recreate the tree view with the
- /// correct IView instances.
- procedure RestoreFromHibernation;
+ /// hibernation if Windows has recreated the overview pane's treeview,
+ /// losing necessary IView object references..
+ procedure _HACK_PrepareForHibernate;
end;
@@ -583,12 +579,6 @@ procedure TMainDisplayMgr.PrepareForDBViewChange(View: IView);
fPendingViewChange := True;
end;
-procedure TMainDisplayMgr.PrepareForHibernate;
-begin
- // simply save the state of the overview tree view ready for later restoration
- (fOverviewMgr as IOverviewDisplayMgr).SaveTreeState;
-end;
-
procedure TMainDisplayMgr.RedisplayOverview;
begin
(fOverviewMgr as IOverviewDisplayMgr).Display(Query.Selection, True);
@@ -616,12 +606,6 @@ procedure TMainDisplayMgr.ReStart;
(fOverviewMgr as IOverviewDisplayMgr).Display(Query.Selection, True);
end;
-procedure TMainDisplayMgr.RestoreFromHibernation;
-begin
- (fOverviewMgr as IOverviewDisplayMgr).Display(Query.Selection, True);
- (fOverviewMgr as IOverviewDisplayMgr).RestoreTreeState;
-end;
-
procedure TMainDisplayMgr.SelectAll;
begin
// Only details pane supports text selection
@@ -720,5 +704,21 @@ procedure TMainDisplayMgr.UpdateOverviewTreeState(const State: TTreeNodeAction);
(fOverviewMgr as IOverviewDisplayMgr).UpdateTreeState(State);
end;
+procedure TMainDisplayMgr._HACK_HibernateHandler(Sender: TObject);
+begin
+ (fOverviewMgr as IOverviewDisplayMgr).Display(Query.Selection, True);
+ (fOverviewMgr as IOverviewDisplayMgr).RestoreTreeState;
+ // disable this handler until next resume from hibernation
+ (fOverviewMgr as IOverviewDisplayMgr)._HACK_SetHibernateHandler(nil);
+end;
+
+procedure TMainDisplayMgr._HACK_PrepareForHibernate;
+begin
+ (fOverviewMgr as IOverviewDisplayMgr).SaveTreeState;
+ (fOverviewMgr as IOverviewDisplayMgr)._HACK_SetHibernateHandler(
+ _HACK_HibernateHandler
+ );
+end;
+
end.
From ef80e27bbdbb30e895f2b73fbac5519ab2512119 Mon Sep 17 00:00:00 2001
From: delphidabbler <5164283+delphidabbler@users.noreply.github.com>
Date: Mon, 14 Apr 2025 19:19:29 +0100
Subject: [PATCH 02/77] Bump version number to v4.24.2 build 274
---
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 513eae126..fbd558db1 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.24.1
-build=273
+version=4.24.2
+build=274
# String file information
copyright=Copyright © P.D.Johnson, 2005-.
From 0ad4fbe75b4cb4bbdb6b27844057107c39bcaedb Mon Sep 17 00:00:00 2001
From: delphidabbler <5164283+delphidabbler@users.noreply.github.com>
Date: Mon, 14 Apr 2025 19:37:10 +0100
Subject: [PATCH 03/77] Update change log with details of release v4.24.2
---
CHANGELOG.md | 9 ++++++++-
1 file changed, 8 insertions(+), 1 deletion(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index bafd5addf..16846323a 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -6,7 +6,14 @@ Releases are listed in reverse version number order.
> Note that _CodeSnip_ v4 was developed in parallel with v3 for a while. As a consequence some v3 releases have later release dates than early v4 releases.
-## Release v4.24.1 of 13 April 2005
+## Release v4.24.2 of 14 April 2025
+
+Hotfix release.
+
+* Updated bug fix implemented in v4.24.1 to avoid relying on a potentially problematic windows event [issue #70 (2nd attempt)].
+* Corrected release date error for v4.24.1 in `CHANGELOG.md`.
+
+## Release v4.24.1 of 13 April 2025
* Fixed bug where CodeSnip occasionally crashes after a computer resumes from hibernation [issue #70].
* Bumped some copyright dates for 2025.
From efcef0f2453ff43d525bddabd78d88f2dbb780d6 Mon Sep 17 00:00:00 2001
From: delphidabbler <5164283+delphidabbler@users.noreply.github.com>
Date: Tue, 15 Apr 2025 08:22:24 +0100
Subject: [PATCH 04/77] Fix malformed bullet char in snippet import wizard
The last page of the Import Wizard dialogue box lists imported snippets
as bullet points. The bullet point string literal had become corrupted.
Replaced the string literal with a constant containing the Unicode
character code point.
Fixes #147
---
Src/FmCodeImportDlg.pas | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/Src/FmCodeImportDlg.pas b/Src/FmCodeImportDlg.pas
index 7315b29e8..86f0fbef2 100644
--- a/Src/FmCodeImportDlg.pas
+++ b/Src/FmCodeImportDlg.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-2023, Peter Johnson (gravatar.com/delphidabbler).
+ * Copyright (C) 2011-2025, Peter Johnson (gravatar.com/delphidabbler).
*
* Implements a wizard dialogue box that handles the import of user defined
* snippets into the database. Permits snippets from the import file to be
@@ -419,6 +419,8 @@ procedure TCodeImportDlg.PresentResults;
/// Creates a label containing name of an imported snippet and adds it to
/// scroll box with top at given position.
procedure AddLabel(var Top: Integer; const SnippetName: string);
+ const
+ Bullet = #$2022;
var
Lbl: TLabel;
begin
@@ -426,7 +428,7 @@ procedure TCodeImportDlg.PresentResults;
Lbl.Parent := sbFinish;
Lbl.Left := 0;
Lbl.Top := Top;
- Lbl.Caption := '� ' + SnippetName;
+ Lbl.Caption := Bullet + ' ' + SnippetName;
Top := TCtrlArranger.BottomOf(Lbl, 2);
end;
// ---------------------------------------------------------------------------
From 0a0681841be323fdf1001794777fae820b8a790e Mon Sep 17 00:00:00 2001
From: delphidabbler <5164283+delphidabbler@users.noreply.github.com>
Date: Tue, 15 Apr 2025 08:29:22 +0100
Subject: [PATCH 05/77] Correct program copyright date in about box
Updated copyright date to 2025.
Fixes #149
---
Src/Res/HTML/dlg-about-program-tplt.html | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/Src/Res/HTML/dlg-about-program-tplt.html b/Src/Res/HTML/dlg-about-program-tplt.html
index be93a30c3..a337e8b80 100644
--- a/Src/Res/HTML/dlg-about-program-tplt.html
+++ b/Src/Res/HTML/dlg-about-program-tplt.html
@@ -9,7 +9,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-2024, Peter Johnson (gravatar.com/delphidabbler).
+ * Copyright (C) 2005-2025, Peter Johnson (gravatar.com/delphidabbler).
*
* Template for content displayed in program tab of about dialog box.
-->
@@ -47,7 +47,7 @@
- DelphiDabbler CodeSnip is copyright © 2005-2024 by CodeSnip is copyright © 2005-2025 by Peter D Johnson.
From 46ae69be45e9904cf04f31f5f43fa01464bccdeb Mon Sep 17 00:00:00 2001
From: delphidabbler <5164283+delphidabbler@users.noreply.github.com>
Date: Tue, 15 Apr 2025 09:42:42 +0100
Subject: [PATCH 06/77] Fix error in export file format documentation
Documentation of XML tags relating to storing snippet xrefs in export
files was removed as erroneous. The error was noted and the file format
bumped to v8 following this change.
Updated the current file version number in the UCodeImportExport unit to
v8.
Fixes #151
Updated the export help topic to note that snippet categories and xrefs
are not exported.
---
Docs/Design/FileFormats/export.html | 75 ++++++++++++++++-------------
Src/Help/HTML/dlg_export.htm | 6 ++-
Src/UCodeImportExport.pas | 4 +-
3 files changed, 48 insertions(+), 37 deletions(-)
diff --git a/Docs/Design/FileFormats/export.html b/Docs/Design/FileFormats/export.html
index 29ca8a849..7f6e80653 100644
--- a/Docs/Design/FileFormats/export.html
+++ b/Docs/Design/FileFormats/export.html
@@ -5,7 +5,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) 2012-2024, Peter Johnson (gravatar.com/delphidabbler).
+ * Copyright (C) 2012-2025, Peter Johnson (gravatar.com/delphidabbler).
*
* CodeSnip File Format Documentation: Export
-->
@@ -57,6 +57,9 @@
File Format
+
+ Erratum
+
Change Log
@@ -114,8 +117,8 @@
- There have been seven different versions of the XML export file format – v1 to
- v7. Tags used by all versions are explained below, with notes describing
+ There have been eight different versions of the XML export file format – v1 to
+ v8. Tags used by all versions are explained below, with notes describing
which versions a tag applies to. Where there is no note the tag is valid in
all versions.
@@ -176,7 +179,7 @@
Identifies major version of file. Determines which tags are valid and
- establishes rules concerning content. Valid versions are 1 to 7.
+ establishes rules concerning content. Valid versions are 1 to 8.
@@ -198,7 +201,7 @@
versions 1 to 6: Contains information about user who created the file used for submissions to the online database, omitted for other exports.
- version 7: Not supported. Ignored if present.
+ version 7 and later: Not supported. Ignored if present.
@@ -212,7 +215,7 @@
versions 1 to 6: User's name or nickname.
- version 7: Not supported. Ignored if present.
+ version 7 and later: Not supported. Ignored if present.
@@ -226,7 +229,7 @@
versions 1 to 6: User's email address.
- version 7: Not supported. Ignored if present.
+ version 7 and later: Not supported. Ignored if present.
@@ -240,7 +243,7 @@
versions 1 to 6: Any comments provided by user.
- version 7: Not supported. Ignored if present.
+ version 7 and later: Not supported. Ignored if present.
@@ -687,33 +690,21 @@
-
- codesnip-export/routines/routine/xref
-
-
- List of cross-referenced snippets.
-
+
-
- codesnip-export/routines/routine/xref/pascal-name
-
-
-
- Name of a snippet within cross-reference list.
-
-
- -
- versions 1 to 4: Name must begin with an
- English language letter or the underscore.
-
- -
- version 5 and later: Name can begin with
- any character that is valid as the first character of a Unicode Pascal
- identifier.
-
-
-
-
+
+
+
+ Erratum
+
+
+
+ The codesnip-export/routines/routine/xref and codesnip-export/routines/routine/xref/pascal-name tags were included in versions 1 to 7 of this specification in error. XRefs were never intended to be written to export files by any version of CodeSnip, as source code comments make clear.
+
+
+
+ These tags have been removed from this document entirely of specification version 8.
+
@@ -1008,6 +999,18 @@
+
+
+ Version 8 - 15 April 2025
+
+
+
+ Introduced with CodeSnip v4.24.3.
+
+
+ The codesnip-export/routines/routine/xref and codesnip-export/routines/routine/xref/pascal-name tags were removed from the specification. See Erratum above for details.
+
+
@@ -1058,6 +1061,10 @@
Readers of v2 files and later can parse REML as v6, since all versions of REML up to v6 are backwards compatible.
+
+ Readers of v1 to v7 files must ignore any codesnip-export/routines/routine/xref tags and sub tags in the unlikely event that they are found. For an explanation see Erratum above.
+
+
diff --git a/Src/Help/HTML/dlg_export.htm b/Src/Help/HTML/dlg_export.htm
index a17b626a4..b8e1db3ff 100644
--- a/Src/Help/HTML/dlg_export.htm
+++ b/Src/Help/HTML/dlg_export.htm
@@ -4,7 +4,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) 2008-2021, Peter Johnson (gravatar.com/delphidabbler).
+ * Copyright (C) 2008-2025, Peter Johnson (gravatar.com/delphidabbler).
*
* Help topic for Export Snippets dialogue box.
-->
@@ -57,6 +57,10 @@
and the dialogue box remains open. The export can be aborted by clicking
the Cancel button.
+
+ Note: Snippet categories and cross references are not
+ included in the export file.
+