Skip to content

New fix for hibernation bug w/out hacks #160

New issue

Have a question about this project? Sign up for a free account to open an issue and contact its maintainers and the community.

By clicking “Sign up for ”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on ? Sign in to your account

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 0 additions & 20 deletions Src/FmMain.pas
Original file line numberDiff line numberDiff line change
Expand Up@@ -522,12 +522,6 @@ TMainForm = class(THelpAwareForm)
/// <summary>Object that manages favourites.</summary>
fFavouritesMgr: TFavouritesManager;

/// <summary>Handles the <c>WM_POWERBROADCAST</c> messages to detect and
/// respond to hibernation messages.</summary>
/// <remarks>!! HACK necessary as part of the fix for an obscure bug. See
/// https://.com/delphidabbler/codesnip/issues/70</remarks>
procedure WMPowerBroadcast(var Msg: TMessage); message WM_POWERBROADCAST;

/// <summary>Displays view item given by TViewItemAction instance
/// referenced by Sender and adds to history list.</summary>
procedure ActViewItemExecute(Sender: TObject);
Expand DownExpand Up@@ -1586,19 +1580,5 @@ procedure TMainForm.splitVertCanResize(Sender: TObject;
Accept := False;
end;

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.
// Attempting to read those (now nil) IView instances was resulting in an
// access violation.
case Msg.WParam of
PBT_APMSUSPEND:
fMainDisplayMgr._HACK_PrepareForHibernate;
end;
end;

end.

4 changes: 2 additions & 2 deletions Src/FrOverview.dfm
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
inherited OverviewFrame: TOverviewFrame
inherited pnlTitle: TPanel
inherited lblTitle: TLabel
Width = 54
Width = 53
Caption = 'Overview'
ExplicitWidth = 54
ExplicitWidth = 53
end
object tbarOverview: TToolBar
Left = 224
Expand Down
58 changes: 5 additions & 53 deletions Src/FrOverview.pas
Original file line numberDiff line numberDiff line change
Expand Up@@ -18,6 +18,7 @@ interface

uses
// Delphi
Generics.Collections,
ComCtrls, Controls, Classes, Windows, ExtCtrls, StdCtrls, ToolWin, Menus,
// Project
DB.USnippet, FrTitled, IntfFrameMgrs, IntfNotifier, UCommandBars,
Expand All@@ -26,29 +27,6 @@ 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
/// <summary>!! HACK. Event triggered after the inherited CreateWnd is
/// called. Only called if the tree view has nil references to IView
/// objects.</summary>
property _HACK_OnAfterCreateNilViews: TNotifyEvent
read _HACK_fOnAfterCreateNilViews write _HACK_fOnAfterCreateNilViews;
end;

{
TOverviewFrame:
Titled frame that displays lists of snippets grouped in various ways and
Expand DownExpand Up@@ -111,6 +89,7 @@ TTVDraw = class(TSnippetsTVDraw)
end;

var
fViewStore : TList<IView>; // Stores the references of the Views
fTVDraw: TTVDraw; // Object that renders tree view nodes
fNotifier: INotifier; // Notifies app of user initiated events
fCanChange: Boolean; // Whether selected node allowed to change
Expand DownExpand Up@@ -237,10 +216,6 @@ TTVDraw = class(TSnippetsTVDraw)
procedure RestoreTreeState;
{Restores last saved treeview expansion state from memory.
}
/// <summary>!! HACK: Sets an event handler on the tree view to work
/// around a bug that can occur after resuming from hibernation.</summary>
/// <remarks>Method of IOverviewDisplayMgr.</remarks>
procedure _HACK_SetHibernateHandler(const AHandler: TNotifyEvent);
{ IPaneInfo }
function IsInteractive: Boolean;
{Checks if the pane is currently interactive with user.
Expand DownExpand Up@@ -311,6 +286,7 @@ constructor TOverviewFrame.Create(AOwner: TComponent);
TabIdx: Integer; // loops through tabs
begin
inherited;
fViewStore := TList<IView>.Create;
// Create delegated (contained) command bar manager for toolbar and popup menu
fCommandBars := TCommandBarMgr.Create(Self);
fCommandBars.AddCommandBar(
Expand DownExpand Up@@ -346,6 +322,7 @@ destructor TOverviewFrame.Destroy;
fSelectedItem := nil;
fSnippetList.Free; // does not free referenced snippets
fCommandBars.Free;
fViewStore.Free;
inherited;
end;

Expand DownExpand Up@@ -547,7 +524,7 @@ procedure TOverviewFrame.Redisplay;
Exit;
// Build new treeview using grouping determined by selected tab
Builder := BuilderClasses[tcDisplayStyle.TabIndex].Create(
tvSnippets, fSnippetList
tvSnippets, fSnippetList, fViewStore
);
Builder.Build;
// Restore state of treeview based on last time it was displayed
Expand DownExpand Up@@ -982,12 +959,6 @@ 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(
Expand DownExpand Up@@ -1026,24 +997,5 @@ 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.

4 changes: 0 additions & 4 deletions Src/IntfFrameMgrs.pas
Original file line numberDiff line numberDiff line change
Expand Up@@ -19,7 +19,6 @@ interface
uses
// Delphi
SHDocVw, ActiveX,
Classes, // !! For HACK
// Project
Browser.IntfDocHostUI, DB.USnippet, Compilers.UGlobals, UCommandBars, UView;

Expand DownExpand Up@@ -146,9 +145,6 @@ interface
/// <summary>Restore expand / collapse state of treeview to last save
/// state.</summary>
procedure RestoreTreeState;
/// <summary>!! HACK: Sets an event handler on the tree view to work
/// around a bug that can occur after resuming from hibernation.</summary>
procedure _HACK_SetHibernateHandler(const AHandler: TNotifyEvent);
end;

type
Expand Down
28 changes: 0 additions & 28 deletions Src/UMainDisplayMgr.pas
Original file line numberDiff line numberDiff line change
Expand Up@@ -165,11 +165,6 @@ TMainDisplayMgr = class(TObject)
procedure DisplayViewItem(ViewItem: IView; Mode: TDetailPageDisplayMode);
overload;

/// <summary>!! HACK event handle to redisplay the overview pane treeview.
/// Called only if Windows has mysteriously recreated the treeview and lost
/// necessary object references.</summary>
procedure _HACK_HibernateHandler(Sender: TObject);

public
/// <summary>Object contructor. Sets up object to work with given frame
/// manager objects.</summary>
Expand DownExpand Up@@ -297,13 +292,6 @@ TMainDisplayMgr = class(TObject)
/// <summary>Prepares display ready for database to be reloaded.</summary>
procedure PrepareForDBReload;

/// <summary>!!HACK: gets the overview frame prepared for program
/// hibernation.</summary>
/// <remarks>Saves the overview tree view state ready for restoring after
/// hibernation if Windows has recreated the overview pane's treeview,
/// losing necessary IView object references..</remarks>
procedure _HACK_PrepareForHibernate;

end;


Expand DownExpand Up@@ -704,21 +692,5 @@ 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.

21 changes: 17 additions & 4 deletions Src/UOverviewTreeBuilder.pas
Original file line numberDiff line numberDiff line change
Expand Up@@ -18,6 +18,7 @@ interface

uses
// Delphu
Generics.Collections,
ComCtrls,
// Project
DB.USnippet, UGroups, UView, UViewItemTreeNode;
Expand All@@ -34,11 +35,14 @@ TOverviewTreeBuilder = class abstract(TObject)
var
fTreeView: TTreeView; // Value of TreeView property
fSnippetList: TSnippetList; // Value of SnippetList property
fViewStore : TList<IView>;
strict protected
property TreeView: TTreeView read fTreeView;
{Reference to treeview populated by class}
property SnippetList: TSnippetList read fSnippetList;
{List of snippets to be displayed in treeview}
property ViewStore : TList<IView> read fViewStore;
{List of views attached to treeview nodes}
function AddViewItemNode(const ParentNode: TViewItemTreeNode;
ViewItem: IView): TViewItemTreeNode;
{Adds a new node to the tree view that represents a view item.
Expand All@@ -57,7 +61,7 @@ TOverviewTreeBuilder = class abstract(TObject)
@return Required view item object.
}
public
constructor Create(const TV: TTreeView; const SnippetList: TSnippetList);
constructor Create(const TV: TTreeView; const SnippetList: TSnippetList; const ViewStore : TList<IView>);
{Class constructor. Sets up object to populate a treeview with a list of
snippets.
@param TV [in] Treeview control to be populated.
Expand DownExpand Up@@ -177,7 +181,9 @@ procedure TOverviewTreeBuilder.Build;
ParentNode: TViewItemTreeNode; // each section node in tree
Grouping: TGrouping; // groups snippets
Group: TGroupItem; // each group of snippets
View: IView;
begin
ViewStore.Clear;
// Create required grouping of snippets
Grouping := CreateGrouping;
try
Expand All@@ -186,11 +192,17 @@ procedure TOverviewTreeBuilder.Build;
begin
if not Group.IsEmpty or Preferences.ShowEmptySections then
begin
ParentNode := AddViewItemNode(nil, CreateViewItemForGroup(Group));
View := CreateViewItemForGroup(Group);
ParentNode := AddViewItemNode(nil, View);
ViewStore.Add(View);
for Snippet in Group.SnippetList do
begin
View := TViewFactory.CreateSnippetView(Snippet);
AddViewItemNode(
ParentNode, TViewFactory.CreateSnippetView(Snippet)
ParentNode, View
);
ViewStore.Add(View);
end;
end;
end;
finally
Expand All@@ -199,7 +211,7 @@ procedure TOverviewTreeBuilder.Build;
end;

constructor TOverviewTreeBuilder.Create(const TV: TTreeView;
const SnippetList: TSnippetList);
const SnippetList: TSnippetList; const ViewStore : TList<IView>);
{Class constructor. Sets up object to populate a treeview with a list of
snippets.
@param TV [in] Treeview control to be populated.
Expand All@@ -209,6 +221,7 @@ constructor TOverviewTreeBuilder.Create(const TV: TTreeView;
inherited Create;
fTreeView := TV;
fSnippetList := SnippetList;
fViewStore := ViewStore;
end;

{ TOverviewCategorisedTreeBuilder }
Expand Down
18 changes: 16 additions & 2 deletions Src/UViewItemTreeNode.pas
Original file line numberDiff line numberDiff line change
Expand Up@@ -31,14 +31,28 @@ interface
}
TViewItemTreeNode = class(TTreeNode)
strict private
var fViewItem: IView; // Value of ViewItem property
function GetViewItem: IView;
procedure SetViewItem(const Value: IView);
published // Value of ViewItem property
public
property ViewItem: IView read fViewItem write fViewItem;
property ViewItem: IView read GetViewItem write SetViewItem;
{View item associated with tree node}
end;


implementation

{ TViewItemTreeNode }

function TViewItemTreeNode.GetViewItem: IView;
begin
Result := IView(Data);
end;

procedure TViewItemTreeNode.SetViewItem(const Value: IView);
begin
Data := Pointer(Value);
end;

end.