Forráskód Böngészése

Refactor: added UMemory unit and misc
- Removed UAutoScope unit
- Added UMemory unit with TDisposables and TAutoPtr
- Added delelect modes to TVisualGrid

Herman Schoenfeld 7 éve
szülő
commit
30cb896a8f

+ 24 - 12
src/core/UDataSources.pas

@@ -27,6 +27,7 @@ type
       property Overview : TOverview read FLastOverview;
       property LastKnownUserAccounts : TArray<TAccount> read FLastKnownUserAccounts;
       function GetSearchCapabilities: TSearchCapabilities; override;
+      function GetEntityKey(constref AItem: TAccount) : Variant; override;
       procedure FetchAll(const AContainer : TList<TAccount>); override;
       function GetItemField(constref AItem: TAccount; const AColumnName : utf8string) : Variant; override;
       procedure DehydrateItem(constref AItem: TAccount; var ATableRow: Variant); override;
@@ -48,6 +49,7 @@ type
       property StartBlock : Cardinal read FStart write FStart;
       property EndBlock : Cardinal read FEnd write FEnd;
       function GetSearchCapabilities: TSearchCapabilities; override;
+      function GetEntityKey(constref AItem: TOperationResume) : Variant; override;
       function GetItemField(constref AItem: TOperationResume; const AColumnName : utf8string) : Variant; override;
       procedure DehydrateItem(constref AItem: TOperationResume; var ATableRow: Variant); override;
   end;
@@ -90,7 +92,7 @@ type
 
 implementation
 
-uses UWallet, UUserInterface, UAutoScope, UCommon.Collections, math, UTime;
+uses UWallet, UUserInterface, UMemory, UCommon.Collections, math, UTime;
 
 { TUserAccountsDataSource }
 
@@ -113,13 +115,18 @@ begin
   );
 end;
 
+function TUserAccountsDataSource.GetEntityKey(constref AItem: TAccount) : Variant;
+begin
+  Result := AItem.account;
+end;
+
 procedure TUserAccountsDataSource.FetchAll(const AContainer : TList<TAccount>);
 var
   i : integer;
   acc : TAccount;
   safeBox : TPCSafeBox;
   keys : TOrderedAccountKeysList;
-  GC : TScoped;
+  GC : TDisposables;
 begin
   FLastOverview.TotalPASC := 0;
   FLastOverview.TotalPASA := 0;
@@ -172,19 +179,19 @@ begin
 end;
 
 procedure TUserAccountsDataSource.DehydrateItem(constref AItem: TAccount; var ATableRow: Variant);
-var
-  index : Integer;
+//var
+//  index : Integer;
 begin
   // 'Account', 'Name', 'Balance', 'Key', 'AccType', 'State', 'Price', 'LockedUntil'
   ATableRow.Account := TAccountComp.AccountNumberToAccountTxtNumber(AItem.account);
   ATableRow.Name := Variant(AItem.name);
   ATableRow.Balance := TAccountComp.FormatMoney(AItem.balance);
-  index := TWallet.Keys.AccountsKeyList.IndexOfAccountKey(AItem.accountInfo.accountKey);
-  if index>=0 then begin
-    ATableRow.Key := TDataSourceTool.AccountKeyShortText(TWallet.Keys[index].Name)
-  end else begin
-    ATableRow.Key := TDataSourceTool.AccountKeyShortText(TAccountComp.AccountPublicKeyExport(AItem.accountInfo.accountKey));
-  end;
+ // index := TWallet.Keys.AccountsKeyList.IndexOfAccountKey(AItem.accountInfo.accountKey);
+ // if index>=0 then begin
+ //   ATableRow.Key := TDataSourceTool.AccountKeyShortText(TWallet.Keys[index].Name)
+ // end else begin
+ //   ATableRow.Key := TDataSourceTool.AccountKeyShortText(TAccountComp.AccountPublicKeyExport(AItem.accountInfo.accountKey));
+ // end;
   ATableRow.AccType := Word(AItem.account_type);
   ATableRow.State := Cardinal(AItem.accountInfo.state);
   ATableRow.Price := TAccountComp.FormatMoney(Aitem.accountInfo.price);
@@ -251,6 +258,11 @@ begin
   );
 end;
 
+function TOperationsDataSourceBase.GetEntityKey(constref AItem: TOperationResume) : Variant;
+begin
+  Result := TPCOperation.FinalOperationHashAsHexa(AItem.OperationHash);
+end;
+
 function TOperationsDataSourceBase.GetItemField(constref AItem: TOperationResume; const AColumnName : utf8string) : Variant;
 var
   index : Integer;
@@ -380,7 +392,7 @@ var
   list : Classes.TList;
   Op : TPCOperation;
   acc : Cardinal;
-  GC : TScoped;
+  GC : TDisposables;
 begin
   if FAccounts.Count = 0
     then exit;
@@ -444,7 +456,7 @@ var
   OPR : TOperationResume;
   blockOps : TPCOperationsComp;
   node : TNode;
-  GC : TScoped;
+  GC : TDisposables;
 
 begin
   node := TNode.Node;

+ 1 - 0
src/gui/UCTRLWallet.lfm

@@ -10,6 +10,7 @@ object CTRLWallet: TCTRLWallet
   ClientWidth = 1151
   OnCreate = FormCreate
   OnResize = FormResize
+  LCLVersion = '1.8.2.0'
   Visible = False
   object PairSplitter1: TPairSplitter
     Left = 0

+ 8 - 5
src/gui/UCTRLWallet.pas

@@ -84,7 +84,7 @@ implementation
 
 uses
   UUserInterface, UBlockChain, UWallet,
-  UCommon, UAutoScope, Generics.Collections, UCommon.Collections;
+  UCommon, UMemory, Generics.Collections, UCommon.Collections;
 
 {$R *.lfm}
 
@@ -108,7 +108,8 @@ begin
   FAccountsGrid.FetchDataInThread:= true;
   FAccountsGrid.AutoPageSize:= true;
   FAccountsGrid.SelectionType:= stMultiRow;
-  FAccountsGrid.Options := [vgoColAutoFill, vgoColSizing, vgoAllowDeselect, vgoSortDirectionAllowNone];
+  FAccountsGrid.DeselectionType:= dtDefault;
+  FAccountsGrid.Options := [vgoColAutoFill, vgoColSizing, vgoSortDirectionAllowNone];
   FAccountsGrid.DefaultColumnWidths := TArray<Integer>.Create(
     100,                   // Account
     CT_VISUALGRID_STRETCH, // Name
@@ -124,7 +125,8 @@ begin
   FOperationsGrid.FetchDataInThread:= true;
   FOperationsGrid.AutoPageSize:= true;
   FOperationsGrid.SelectionType:= stRow;
-  FOperationsGrid.Options := [vgoColAutoFill, vgoAllowDeselect, vgoColSizing, vgoSortDirectionAllowNone];
+  FOperationsGrid.DeselectionType:= dtDefault;
+  FOperationsGrid.Options := [vgoColAutoFill, vgoColSizing, vgoSortDirectionAllowNone];
   FOperationsGrid.DefaultColumnWidths := TArray<Integer>.Create(
     130,                   // Time
     CT_VISUALGRID_DEFAULT, // Block
@@ -268,7 +270,7 @@ var
   row : longint;
   selectedAccounts : Generics.Collections.TList<Cardinal>;
   acc : Cardinal;
-  GC : TScoped;
+  GC : TDisposables;
 begin
   selectedAccounts := GC.AddObject( TList<Cardinal>.Create ) as TList<Cardinal>;
 
@@ -348,7 +350,8 @@ end;
 
 procedure TCTRLWallet.miOperationInfoClick(Sender: TObject);
 begin
-  raise ENotImplemented.Create('Not Implemented');
+  if FOperationsGrid.Selection.RowCount = 0 then exit;
+  TUserInterface.ShowOperationInfoDialog(Self, FOperationsGrid.SelectedRows[0].EntityKey);
 end;
 
 end.

+ 2 - 1
src/gui/UFRMWalletKeys.lfm

@@ -9,7 +9,8 @@ object FRMWalletKeys: TFRMWalletKeys
   OnCreate = FormCreate
   OnDestroy = FormDestroy
   Position = poOwnerFormCenter
-  LCLVersion = '1.8.0.6'
+  LCLVersion = '1.8.2.0'
+  Visible = False
   object lbWalletKeys: TListBox
     Left = 24
     Height = 210

+ 2 - 2
src/gui/UFRMWalletKeys.pas

@@ -90,7 +90,7 @@ implementation
 
 {$R *.lfm}
 
-uses  LCLIntf, Clipbrd, UUserInterface, USettings, UCommon, UAccounts, UWIZAddKey, UAutoScope;
+uses  LCLIntf, Clipbrd, UUserInterface, USettings, UCommon, UAccounts, UWIZAddKey, UMemory;
 
 {%region Form life-cycle}
 
@@ -121,7 +121,7 @@ end;
 
 procedure TFRMWalletKeys.AddNewKey;
 var
-  Scoped : TScoped;
+  Scoped : TDisposables;
   wiz : TWIZAddKeyWizard;
   model : TWizAddKeyModel;
 begin

+ 1 - 0
src/gui/wizards/UWIZAddKey_SelectEncryption.lfm

@@ -7,6 +7,7 @@ object WIZAddKey_SelectEncryption: TWIZAddKey_SelectEncryption
   ClientHeight = 240
   ClientWidth = 320
   OnCreate = FormCreate
+  LCLVersion = '1.8.2.0'
   Visible = False
   object rgKeyType: TRadioGroup
     Left = 24

+ 2 - 2
src/gui/wizards/UWIZAddKey_SelectEncryption.pas

@@ -24,14 +24,14 @@ implementation
 
 {$R *.lfm}
 
-uses UAccounts, UConst, UAutoScope;
+uses UAccounts, UConst, UMemory;
 
 { TWIZAddKey_SelectEncryption }
 
 procedure TWIZAddKey_SelectEncryption.FormCreate(Sender: TObject);
 var
   i : Integer;
-  GC : TScoped;
+  GC : TDisposables;
   availableEncryptionTypes : TList;
   name, desc : UTF8String;
 begin

+ 0 - 438
src/libraries/sphere10/UAutoScope.pas

@@ -1,438 +0,0 @@
-{**********************************************************************
-    ● Copyright(c) 2017 Dmitriy Pomerantsev <[email protected]>
-    See the file COPYING.FPC, included in this distribution,
-    for details about the copyright.
-    This program is distributed in the hope that it will be useful,
-    but WITHOUT ANY WARRANTY; without even the implied warranty of
-    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
-    https://github.com/pda0/AutoScope
-    Ver 1.0.2
-    + Added some new tests.
-    + Added `paranoia' mode (disabled by default). Define WITH_PARANOIA if you
-      want. TScoped will place a reference to itself in an external variable to
-      prevent too smart compiler from removing the record prematurely.
-    Ver 1.0.1
-    * Now cleanup process is protected from destructor's exceptions.
-      It may not work temporarily in llvm-based compiler because of the bug
-      https://quality.embarcadero.com/browse/RSP-18031
-    Ver 1.0.0
-    * Initial release.
- **********************************************************************}
-unit UAutoScope;
-{$IFDEF FPC}
-  {$CODEPAGE UTF8}
-  {$MODE DELPHI}{$H+}
-  {$MODESWITCH ADVANCEDRECORDS}
-{$ENDIF}
-
-interface
-
-{$IFDEF FPC}
-  { #%^$! delphi compiller!!! }
-  {$IFDEF VER1}{$ERROR Too old compiller.}{$ENDIF}
-  {$IFDEF VER2}
-    {$IFDEF VER2_0}{$ERROR Too old compiller.}{$ENDIF}
-    {$IFDEF VER2_2}{$ERROR Too old compiller.}{$ENDIF}
-    {$IFDEF VER2_4}{$ERROR Too old compiller.}{$ENDIF}
-  {$ENDIF}
-  {$DEFINE USE_INTERFACE}
-  //{$IFNDEF VER_3_0}
-  //  {$IFDEF USE_INTERFACE}{$UNDEF USE_INTERFACE}{$ENDIF}
-  //  {$DEFINE USE_OPERATORS}
-  //{$ENDIF}
-{$ELSE}
-  {$DEFINE USE_INTERFACE}
-{$ENDIF}
-
-type
-  TScopedPtr = record
-    Ptr: Pointer;
-    {$IFNDEF NEXTGEN}
-    IsObject: Boolean;
-    {$ENDIF}
-  end;
-  {$IFDEF USE_INTERFACE}
-    {$DEFINE NEED_SCOPED_PTR}
-  {$ENDIF}
-  {$IFDEF WITH_PARANOIA}
-    {$IFNDEF NEED_SCOPED_PTR}{$DEFINE NEED_SCOPED_PTR}{$ENDIF}
-  {$ENDIF}
-
-  {$IFDEF NEED_SCOPED_PTR}
-  PScoped = ^TScoped;
-  {$ENDIF}
-
-  /// <summary>The automatic memory deallocation object.</summary>
-  /// <remarks>
-  ///   TScoped automatically free memory when it's instance runs out of scope.
-  ///   Do not declare it as const or threadvar variable.
-  /// </remarks>
-  /// <threadsafety static="false" instance="false" />
-  TScoped = record
-  {$IFDEF USE_INTERFACE}
-  private type
-    TScopedGuardian = class(TInterfacedObject)
-    private
-      FScopedRec: PScoped;
-    public
-      constructor Create(ScopedRec: PScoped; ACapacity:Integer);
-      destructor Destroy; override;
-    end;
-  {$ENDIF}
-  private
-    {$IFDEF USE_INTERFACE}
-    FGuardian: IInterface;
-    {$ENDIF}
-    FPointers: array of TScopedPtr;
-    FLastIndex: {$IFDEF FPC}TDynArrayIndex{$ELSE}Integer{$ENDIF};
-    {$IFDEF USE_OPERATORS}
-    class operator Initialize(var AScope: TScoped);
-    class operator Finalize(var AScope: TScoped; ACapacity:Integer);
-    {$ENDIF}
-    {$IFDEF USE_INTERFACE}
-    class procedure Initialize(var AScope: TScoped; ACapacity:Integer); static;
-    class procedure Finalize(var AScope: TScoped); static;
-    {$ENDIF}
-    procedure RegisterPointer(Ptr: Pointer; IsObject: Boolean);
-    procedure UnregisterPointer(Ptr: Pointer);
-  public
-    /// <summary>Adds the object to the automatic deletion list.</summary>
-    /// <param name="AnObject">A class instance for automatic deletion.</param>
-    /// <returns><c>AnObject</c> value as is.</returns>
-    /// <remarks>
-    ///   <para>
-    ///     When an instance of <c>TScoped</c> runs out of a scope, all objects
-    ///     will be deleted in the reverse order to the addition.
-    ///   </para>
-    ///   <para>
-    ///     Does nothing in NextGen mode due of ARC.
-    ///   </para>
-    /// </remarks>
-    function AddObject(const AnObject: TObject): TObject;
-    /// <summary>Removes the object from the automatic deletion list.</summary>
-    /// <param name="AnObject">A class instance for removal from list.</param>
-    /// <remarks>
-    ///   <para>
-    ///     After calling this method, you have to remove the class instance
-    ///     by yourself.
-    ///   </para>
-    ///   <para>
-    ///     This method have O(n) complexity because it's not a primary case
-    ///     scenario of <c>TScoped</c> usage. If you want to create and destroy
-    ///     some class often, please use normal methods like
-    ///     <c>Create</c>/<c>Free</c>.
-    ///   </para>
-    ///   <para>
-    ///     Does nothing in NextGen mode due of ARC.
-    ///   </para>
-    /// </remarks>
-    procedure RemoveObject(const AnObject: TObject);
-
-    // HS: added
-    procedure InitCapacity(ACapacity : Integer);
-    function Count : Integer;
-    function ItemAt(AIndex : Integer) : TObject;
-    function ScopedPtrAt(AIndex : Integer) : TScopedPtr;
-
-    /// <summary>Allocates an automatically releasing memory block.</summary>
-    /// <param name="P">Returns a pointer to allocated memory block.</param>
-    /// <param name="Size">Is a size in bytes of required memory.</param>
-    /// <remarks>
-    ///   When an instance of <c>TScoped</c> runs out of a scope, all memory
-    ///   block will be released in the reverse order to the allocation.
-    /// </remarks>
-    procedure GetMem(out P: Pointer; Size: {$IFDEF FPC}PtrUInt{$ELSE}Integer{$ENDIF});
-    /// <summary>Releases previously allocated memory block.</summary>
-    /// <param name="P">
-    ///   Is a pointer to memory block, allocated by
-    ///   <see cref="TScoped.GetMem"/> or added by <see cref="TScoped.AddMem"/>.
-    /// </param>
-    /// <remarks>
-    ///   This method have O(n) complexity because it's not a primary case
-    ///   scenario of <c>TScoped</c> usage. If you want to allocate and release
-    ///   memory often, please use normal functions like
-    ///   <c>System.GetMem</c>/<c>System.FreeMem</c>.
-    /// </remarks>
-    procedure FreeMem(var P: Pointer);
-    /// <summary>Adds a memory block to the automatically releasing list.</summary>
-    /// <param name="P">
-    ///   Is a pointer to memory block, allocated by somewhere else.
-    /// </param>
-    /// <remarks>
-    ///   <para>
-    ///     When an instance of <c>TScoped</c> runs out of a scope, all memory
-    ///     block will be released in the reverse order to the addition.
-    ///   </para>
-    ///   <para>
-    ///     Do not try to add pointer to memory block, allocated by
-    ///     <see cref="TScoped.GetMem"/>, do not add some pointer more than one
-    ///     time.
-    ///   </para>
-    ///   <para>
-    ///     Use only pointer which have to be releasev via
-    ///     <see cref="System.FreeMem"/>. Do not use typed pointers, allocated
-    ///     by <see cref="New"/>. <c>TScoped</c> is incompatible with typed
-    ///     pointers.
-    ///   </para>
-    /// </remarks>
-    procedure AddMem(const P: Pointer);
-    /// <summary>Reallocates a memory block.</summary>
-    /// <param name="P">
-    ///   Is a pointer to memory block, allocated by
-    ///   <see cref="TScoped.GetMem"/> or added by <see cref="TScoped.AddMem"/>.
-    /// </param>
-    /// <remarks>
-    ///   This method have O(n) complexity because it's not a primary case
-    ///   scenario of <c>TScoped</c> usage. If you want to allocate and release
-    ///   memory often, please use normal functions like
-    ///   <c>System.GetMem</c>/<c>System.FreeMem</c>.
-    /// </remarks>
-    procedure ReallocMem(var P: Pointer; Size: {$IFDEF FPC}PtrUInt{$ELSE}Integer{$ENDIF});
-    /// <summary>Removes a memory block from the automatic deletion list.</summary>
-    /// <param name="P">
-    ///   Is a pointer to memory block, allocated by
-    ///   <see cref="TScoped.GetMem"/> or added by <see cref="TScoped.AddMem"/>.
-    /// </param>
-    /// <remarks>
-    ///   <para>
-    ///     After calling this method, you have to release the memory block
-    ///     by yourself.
-    ///   </para>
-    ///   <para>
-    ///     This method have O(n) complexity because it's not a primary case
-    ///     scenario of <c>TScoped</c> usage. If you want to allocate and
-    ///     release memory often, please use normal functions like
-    ///     <c>System.GetMem</c>/<c>System.FreeMem</c>.
-    ///   </para>
-    /// </remarks>
-    procedure RemoveMem(const P: Pointer);
-    /// <summary>A syntax sugar for the AddObject method.</summary>
-    property Objects[const AnObject: TObject]: TObject read AddObject; default;
-  end;
-
-{$IFDEF WITH_PARANOIA}
-var
-  __no_use_ptr: PScoped;
-{$ENDIF}
-
-implementation
-
-uses sysutils;
-
-{ TScoped }
-
-{$IFDEF USE_INTERFACE}
-constructor TScoped.TScopedGuardian.Create(ScopedRec: PScoped; ACapacity:Integer);
-begin
-  FScopedRec := ScopedRec;
-  TScoped.Initialize(FScopedRec^, ACapacity);
-end;
-
-destructor TScoped.TScopedGuardian.Destroy;
-begin
-  inherited;
-  try
-    TScoped.Finalize(FScopedRec^);
-  except
-    FreeInstance;
-    raise;
-  end;
-end;
-{$ENDIF}
-
-{$IFDEF USE_OPERATORS}
-class operator TScoped.Initialize(var AScope: TScoped);
-{$ENDIF}
-{$IFDEF USE_INTERFACE}
-class procedure TScoped.Initialize(var AScope: TScoped; ACapacity:Integer);
-{$ENDIF}
-begin
-  {$IFDEF WITH_PARANOIA}
-   __no_use_ptr := @AScope;
-  {$ENDIF}
-  AScope.FLastIndex := -1;
-  SetLength(AScope.FPointers, ACapacity);
-end;
-
-{$IFDEF USE_OPERATORS}
-class operator TScoped.Finalize(var AScope: TScoped);
-{$ENDIF}
-{$IFDEF USE_INTERFACE}
-class procedure TScoped.Finalize(var AScope: TScoped);
-{$ENDIF}
-var
-  {$IFNDEF NEXTGEN}
-  FirstException: Pointer;
-  {$ENDIF}
-  i: {$IFDEF FPC}TDynArrayIndex{$ELSE}Integer{$ENDIF};
-begin
-  FirstException := nil;
-
-  for i := AScope.FLastIndex downto 0 do
-  {$IFNDEF NEXTGEN}
-  try
-    if AScope.FPointers[i].IsObject then
-      TObject(AScope.FPointers[i].Ptr).Free
-    else begin
-  {$ENDIF}
-      if Assigned(AScope.FPointers[i].Ptr) then
-        System.FreeMem(AScope.FPointers[i].Ptr);
-  {$IFNDEF NEXTGEN}
-    end;
-  except
-    if not Assigned(FirstException) then
-      FirstException := AcquireExceptionObject;
-  end;
-
-  if Assigned(FirstException) then
-  begin
-    SetLength(AScope.FPointers, 0);
-    raise TObject(FirstException);
-  end;
-  {$ENDIF}
-end;
-
-{ TScoped is for small amount of local objects or memory blocks, which will be
-  created at start of a routine, deleted at the end, and very rarely at the
-  middle of the execution. Therefore there is no need for complex methods of
-  low `big O' complexity. The simplicity and fast of primary case scenario speed
-  is preferred. }
-
-procedure TScoped.RegisterPointer(Ptr: Pointer; IsObject: Boolean);
-begin
-  if FLastIndex > High(FPointers) then
-    SetLength(FPointers, Length(FPointers) * 2);
-
-  Inc(FLastIndex);
-  FPointers[FLastIndex].Ptr := Ptr;
-  {$IFNDEF NEXTGEN}
-  FPointers[FLastIndex].IsObject := IsObject;
-  {$ENDIF}
-end;
-
-procedure TScoped.UnregisterPointer(Ptr: Pointer);
-var
-  i: {$IFDEF FPC}TDynArrayIndex{$ELSE}Integer{$ENDIF};
-begin
-  for i := 0 to FLastIndex do
-    if FPointers[i].Ptr = Ptr then
-    begin
-      FPointers[i].Ptr := nil;
-      Break;
-    end;
-end;
-
-function TScoped.AddObject(const AnObject: TObject): TObject;
-begin
-  {$IFNDEF NEXTGEN}
-    {$IFDEF USE_INTERFACE}
-    if not Assigned(FGuardian) then
-      FGuardian := TScopedGuardian.Create(@Self, 16);
-    {$ENDIF}
-
-  RegisterPointer(Pointer(AnObject), True);
-  {$ENDIF}
-  Result := AnObject;
-end;
-
-procedure TScoped.RemoveObject(const AnObject: TObject);
-begin
-  {$IFNDEF NEXTGEN}
-    {$IFDEF USE_INTERFACE}
-    if not Assigned(FGuardian) then
-      FGuardian := TScopedGuardian.Create(@Self, 16);
-    {$ENDIF}
-
-  UnregisterPointer(Pointer(AnObject));
-  {$ENDIF}
-end;
-
-procedure TScoped.InitCapacity(ACapacity : Integer);
-begin
-  {$IFNDEF NEXTGEN}
-    {$IFDEF USE_INTERFACE}
-    if Assigned(FGuardian) then
-      raise Exception.Create('Already initialized');
-    FGuardian := TScopedGuardian.Create(@Self, ACapacity);
-    {$ENDIF}
-  {$ENDIF}
-end;
-
-function TScoped.Count : Integer;
-begin
-  if not Assigned(FGuardian) then
-    Exit(0);
-  Result := FLastIndex + 1;
-end;
-
-function TScoped.ItemAt(AIndex : Integer) : TObject;
-var sp : TScopedPtr;
-begin
-  sp := FPointers[AIndex];
-  Result := TObject(sp.Ptr);
-end;
-
-function TScoped.ScopedPtrAt(AIndex : Integer) : TScopedPtr;
-var sp : TScopedPtr;
-begin
-  Result := FPointers[AIndex];
-end;
-
-procedure TScoped.GetMem(out P: Pointer; Size:
-  {$IFDEF FPC}PtrUInt{$ELSE}Integer{$ENDIF});
-begin
-  System.GetMem(P, Size);
-  AddMem(P);
-end;
-
-procedure TScoped.FreeMem(var P: Pointer);
-begin
-  {$IFDEF USE_INTERFACE}
-  if not Assigned(FGuardian) then
-    FGuardian := TScopedGuardian.Create(@Self, 16);
-  {$ENDIF}
-
-  UnregisterPointer(P);
-  System.FreeMem(P);
-end;
-
-procedure TScoped.AddMem(const P: Pointer);
-begin
-  {$IFDEF USE_INTERFACE}
-  if not Assigned(FGuardian) then
-    FGuardian := TScopedGuardian.Create(@Self, 16);
-  {$ENDIF}
-
-  RegisterPointer(P, False);
-end;
-
-procedure TScoped.ReallocMem(var P: Pointer; Size:
-  {$IFDEF FPC}PtrUInt{$ELSE}Integer{$ENDIF});
-var
-  i: {$IFDEF FPC}TDynArrayIndex{$ELSE}Integer{$ENDIF};
-begin
-  {$IFDEF USE_INTERFACE}
-  if not Assigned(FGuardian) then
-    FGuardian := TScopedGuardian.Create(@Self, 16);
-  {$ENDIF}
-
-  for i := FLastIndex downto 0 do
-    if not FPointers[i].IsObject and (FPointers[i].Ptr = P) then
-    begin
-      System.ReallocMem(FPointers[i].Ptr, Size);
-      P := FPointers[i].Ptr;
-      Break;
-    end;
-end;
-
-procedure TScoped.RemoveMem(const P: Pointer);
-begin
-  {$IFDEF USE_INTERFACE}
-  if not Assigned(FGuardian) then
-    FGuardian := TScopedGuardian.Create(@Self, 16);
-  {$ENDIF}
-
-  UnregisterPointer(P);
-end;
-
-end.

+ 6 - 8
src/libraries/sphere10/UCommon.Data.pas

@@ -65,7 +65,6 @@ type
 
   TTableRow = class(TInvokeableVariantType)
   private
-    FEntityKey : Variant;
     class constructor Create;
     class destructor Destroy;
   protected type
@@ -76,7 +75,6 @@ type
   protected
     class function MapColumns(AColumns: PTableColumns): TColumnMapToIndex;
   public
-    property EntityKey : Variant read FEntityKey write FEntityKey;
     function GetProperty(var Dest: TVarData; const V: TVarData; const Name: string): Boolean; override;
     function SetProperty(var V: TVarData; const Name: string; const Value: TVarData): Boolean; override;
     procedure Clear(var V: TVarData); override;
@@ -274,7 +272,7 @@ resourcestring
 
 implementation
 
-uses dateutils, UAutoScope;
+uses dateutils, UMemory;
 
 { VARIABLES }
 
@@ -503,7 +501,7 @@ function TCustomDataSource<T>.FetchPage(constref AParams: TPageFetchParams; var
 var
   i, j : SizeInt;
   data : TList<T>;
-  GC : TScoped;
+  GC : TDisposables;
   pageStart, pageEnd : SizeInt;
   entity : T;
   comparer : IComparer<T>;
@@ -609,7 +607,7 @@ var
   i : integer;
   comparers : TList<__IComparer_T>;
   filter : TColumnFilter;
-  GC : TScoped;
+  GC : TDisposables;
 begin
   comparers := GC.AddObject(  TList<__IComparer_T>.Create ) as TList<__IComparer_T>;
   for i := Low(AFilters) to High(AFilters) do begin
@@ -632,7 +630,7 @@ type
 var
   i : integer;
   filters : __TList_IPredicate_T;
-  GC : TScoped;
+  GC : TDisposables;
 begin
   filters := GC.AddObject( __TList_IPredicate_T.Create ) as __TList_IPredicate_T;
   for i := Low(AFilters) to High(AFilters) do begin
@@ -664,7 +662,7 @@ end;
 function TPageFetchParams.GetSortFilters : TArray<TColumnFilter>;
 var
   sortFilters : TList<TColumnFilter>;
-  GC : TScoped;
+  GC : TDisposables;
 
   function IsSortFilter(constref AColFilter : TColumnFilter) : boolean;
   begin
@@ -683,7 +681,7 @@ end;
 function TPageFetchParams.GetSearchFilters : TArray<TColumnFilter>;
 var
   searchFilters : TList<TColumnFilter>;
-  GC : TScoped;
+  GC : TDisposables;
 
   function IsSearchFilter(constref AColFilter : TColumnFilter) : boolean;
   begin

+ 5 - 48
src/libraries/sphere10/UCommon.pas

@@ -20,7 +20,7 @@ interface
 
 uses
   Classes, SysUtils, Generics.Collections, Generics.Defaults,
-  Variants, LazUTF8, math, typinfo, UAutoScope;
+  Variants, LazUTF8, math, typinfo, UMemory;
 
 { CONSTANTS }
 
@@ -67,6 +67,7 @@ function UtcTimeStamp : AnsiString;
 type
 
   {$IFDEF FPC}
+
   { TTimeSpan }
 
   TTimeSpan = record
@@ -129,19 +130,8 @@ type
       property TotalSeconds: Double read GetTotalSeconds;
       property TotalMilliseconds: Double read GetTotalMilliseconds;
     end;
-  {$ENDIF}
-
-  { TAuto }
 
-  TAuto<T> = record
-    private
-      FGC : TScoped;
-      function GetItem : T;
-      procedure SetItem(const AItem: T);
-    public
-      constructor Create(const AItem: T);
-      property Item : T read GetItem write SetItem;
-  end;
+  {$ENDIF}
 
   { TDateTimeHelper }
 
@@ -232,7 +222,6 @@ type
      function WithinMilliseconds(const aDateTime: TDateTime; const AMilliseconds: Int64): Boolean; inline;
    end;
 
-
   { TItemDisposePolicy }
 
   TItemDisposePolicy = (idpNone, idpNil, idpFreeAndNil);
@@ -340,6 +329,7 @@ const
 { VARIABLES }
 
 var
+  {DynamicType: TDynamic = nil;}
   MinTimeStampDateTime : TDateTime = 0;
   VarTrue : Variant;
   VarFalse : Variant;
@@ -604,40 +594,8 @@ end;
 
 {$ENDIF}
 
-{%region TAuto }
-
-constructor TAuto<T>.Create(const AItem: T);
-begin
-  FGC.InitCapacity(1);
-  FGC.AddObject(AItem);
-end;
-
-function TAuto<T>.GetItem : T;
-begin
-  if FGC.Count = 1 then
-    Result := T(FGC.ItemAt(0))
-  else
-    Result := Default(T)
-end;
-
-procedure TAuto<T>.SetItem(const AItem: T);
-var
-  oldsp : TScopedPtr;
-  old : TObject;
-begin
-  while FGC.Count > 0 do begin
-    oldsp := FGC.ScopedPtrAt(0);
-    old := FGC.ItemAt(0);
-    FGC.RemoveObject(old);
-    if (oldsp.IsObject) then
-      FreeAndNil(Pointer(old));
-  end;
-  FGC.AddObject(AItem);
-end;
-
-{%endregion}
-
 {%region Language-level tools }
+
 function IIF(const ACondition: Boolean; const ATrueResult, AFalseResult: Cardinal): Cardinal;
 begin
   if ACondition then
@@ -1541,4 +1499,3 @@ initialization
 finalization
 
 end.
-

+ 311 - 0
src/libraries/sphere10/UMemory.pas

@@ -0,0 +1,311 @@
+{
+  Copyright (c) 2017 - 2018 Sphere 10 Software
+
+  Memory management routines.
+
+  Distributed under the MIT software license, see the accompanying file LICENSE
+  or visit http://www.opensource.org/licenses/mit-license.php.
+
+  Acknowledgements:
+    Herman Schoenfeld
+    Dmitriy Pomerantsev <[email protected]>: scope management inspired by https://github.com/pda0/AutoScope/blob/master/AutoScope.pas
+}
+
+unit UMemory;
+
+{$IFDEF FPC}
+  {$MODE DELPHI}{$H+}
+  {$MODESWITCH ADVANCEDRECORDS}
+{$ENDIF}
+
+interface
+
+type
+
+  { TDisposables }
+
+  TDisposables = record
+    private type
+       PDisposables = ^TDisposables;
+
+       TDisposablePointer = record
+         Ptr: Pointer;
+         IsObject: Boolean;
+       end;
+
+      TGuard = class(TInterfacedObject)
+        private
+          FDispoablesRec: PDisposables;
+        public
+          constructor Create(ADisposablesRec: PDisposables);
+          destructor Destroy; override;
+      end;
+    private
+      FGuardian: IInterface;
+      FPointers: array of TDisposablePointer;
+      FLastIndex: Integer;
+      class procedure Initialize(var ADisposables: TDisposables); static;
+      class procedure Finalize(var ADisposables: TDisposables); static;
+      procedure RegisterPointer(Ptr: Pointer; IsObject: Boolean);
+      procedure UnregisterPointer(Ptr: Pointer);
+    public
+      function AddObject(const AnObject: TObject): TObject;
+      procedure RemoveObject(const AnObject: TObject);
+      procedure GetMem(out P: Pointer; Size: Integer);
+      procedure FreeMem(var P: Pointer);
+      procedure AddMem(const P: Pointer);
+      procedure ReallocMem(var P: Pointer; Size: Integer);
+      procedure RemoveMem(const P: Pointer);
+      /// <summary>A syntax sugar for the AddObject method.</summary>
+      property Objects[const AnObject: TObject]: TObject read AddObject; default;
+  end;
+
+  { TAutoPtr - for disposing single instances }
+
+  TAutoPtr<T> = record
+    private type
+      __TAutoPtr_T = TAutoPtr<T>;
+      __PAutoPtr_T = ^__TAutoPtr_T;
+      TGuard = class(TInterfacedObject)
+        private
+          FOwner: __PAutoPtr_T;
+        public
+          constructor Create(AAutoPtrRec: __PAutoPtr_T);
+          destructor Destroy; override;
+      end;
+    private
+      FGuardian: IInterface;
+      FPointer:  Pointer;
+      class procedure Initialize(var AAutoPtr: __TAutoPtr_T); static;
+      class procedure Finalize(var AAutoPtr: __TAutoPtr_T); static;
+      function GetPointer : Pointer; inline;
+      procedure SetPointer(Ptr: Pointer); inline;
+      function GetObject : TObject; inline;
+      procedure SetObject(const AnObject: TObject); inline;
+      function GetValue : T; inline;
+      procedure SetValue(const AValue : T); inline;
+      procedure CheckGuard; inline;
+    public
+      property Pointer_ : Pointer read GetPointer write SetPointer;
+      property Object_ : TObject read GetObject write SetObject;
+      property Value : T read GetValue write SetValue;
+      procedure Clear;
+  end;
+
+implementation
+
+uses sysutils;
+
+{ TDisposables }
+
+constructor TDisposables.TGuard.Create(ADisposablesRec: PDisposables);
+begin
+  FDispoablesRec := ADisposablesRec;
+  TDisposables.Initialize(FDispoablesRec^);
+end;
+
+destructor TDisposables.TGuard.Destroy;
+begin
+  inherited;
+  try
+    TDisposables.Finalize(FDispoablesRec^);
+  except
+    FreeInstance;
+    raise;
+  end;
+end;
+
+class procedure TDisposables.Initialize(var ADisposables: TDisposables);
+begin
+  ADisposables.FLastIndex := -1;
+  SetLength(ADisposables.FPointers, 16);
+end;
+
+class procedure TDisposables.Finalize(var ADisposables: TDisposables);
+var
+  FirstException: Pointer;
+  i: Integer;
+begin
+  FirstException := nil;
+
+  for i := ADisposables.FLastIndex downto 0 do
+  try
+    if ADisposables.FPointers[i].IsObject then
+      TObject(ADisposables.FPointers[i].Ptr).Free
+    else begin
+      if Assigned(ADisposables.FPointers[i].Ptr) then
+        System.FreeMem(ADisposables.FPointers[i].Ptr);
+    end;
+  except
+    if not Assigned(FirstException) then
+      FirstException := AcquireExceptionObject;
+  end;
+
+  if Assigned(FirstException) then
+  begin
+    SetLength(ADisposables.FPointers, 0);
+    raise TObject(FirstException);
+  end;
+end;
+
+procedure TDisposables.RegisterPointer(Ptr: Pointer; IsObject: Boolean);
+begin
+  if FLastIndex > High(FPointers) then
+    SetLength(FPointers, Length(FPointers) * 2);
+
+  Inc(FLastIndex);
+  FPointers[FLastIndex].Ptr := Ptr;
+  FPointers[FLastIndex].IsObject := IsObject;
+end;
+
+procedure TDisposables.UnregisterPointer(Ptr: Pointer);
+var i: Integer;
+begin
+  for i := 0 to FLastIndex do
+    if FPointers[i].Ptr = Ptr then
+    begin
+      FPointers[i].Ptr := nil;
+      Break;
+    end;
+end;
+
+function TDisposables.AddObject(const AnObject: TObject): TObject;
+begin
+    if not Assigned(FGuardian) then
+      FGuardian := TGuard.Create(@Self);
+  RegisterPointer(Pointer(AnObject), True);
+  Result := AnObject;
+end;
+
+procedure TDisposables.RemoveObject(const AnObject: TObject);
+begin
+    if not Assigned(FGuardian) then
+      FGuardian := TGuard.Create(@Self);
+  UnregisterPointer(Pointer(AnObject));
+end;
+
+procedure TDisposables.GetMem(out P: Pointer; Size: Integer);
+begin
+  System.GetMem(P, Size);
+  AddMem(P);
+end;
+
+procedure TDisposables.FreeMem(var P: Pointer);
+begin
+  if not Assigned(FGuardian) then
+    FGuardian := TGuard.Create(@Self);
+  UnregisterPointer(P);
+  System.FreeMem(P);
+end;
+
+procedure TDisposables.AddMem(const P: Pointer);
+begin
+  if not Assigned(FGuardian) then
+    FGuardian := TGuard.Create(@Self);
+  RegisterPointer(P, False);
+end;
+
+procedure TDisposables.ReallocMem(var P: Pointer; Size:Integer);
+var
+  i: Integer;
+begin
+  if not Assigned(FGuardian) then
+    FGuardian := TGuard.Create(@Self);
+
+  for i := FLastIndex downto 0 do
+    if not FPointers[i].IsObject and (FPointers[i].Ptr = P) then
+    begin
+      System.ReallocMem(FPointers[i].Ptr, Size);
+      P := FPointers[i].Ptr;
+      Break;
+    end;
+end;
+
+procedure TDisposables.RemoveMem(const P: Pointer);
+begin
+  if not Assigned(FGuardian) then
+    FGuardian := TGuard.Create(@Self);
+  UnregisterPointer(P);
+end;
+
+{ TAutoPtr }
+
+constructor TAutoPtr<T>.TGuard.Create(AAutoPtrRec: __PAutoPtr_T);
+begin
+  FOwner := AAutoPtrRec;
+  TAutoPtr<T>.Initialize(FOwner^);
+end;
+
+destructor TAutoPtr<T>.TGuard.Destroy;
+begin
+  inherited;
+  try
+    TAutoPtr<T>.Finalize(FOwner^);
+  except
+    FreeInstance;
+    raise;
+  end;
+end;
+
+class procedure TAutoPtr<T>.Initialize(var AAutoPtr: __TAutoPtr_T);
+begin
+  AAutoPtr.FGuardian := nil;
+  AAutoPtr.FPointer := nil;
+end;
+
+class procedure TAutoPtr<T>.Finalize(var AAutoPtr: __TAutoPtr_T);
+begin
+  AAutoPtr.Clear;
+end;
+
+procedure TAutoPtr<T>.Clear;
+begin
+  CheckGuard;
+  if FPointer <> nil then begin
+    TObject(FPointer).Free;
+    FPointer := nil;
+    // avoid FGuard nullifcation due to recursive calls
+  end;
+end;
+
+function TAutoPtr<T>.GetPointer : Pointer;
+begin
+  CheckGuard;
+  Result := FPointer;
+end;
+
+procedure TAutoPtr<T>.SetPointer(Ptr: Pointer);
+begin
+  CheckGuard;
+  if FPointer <> nil then
+    Clear;
+  FPointer := Ptr;
+end;
+
+function TAutoPtr<T>.GetObject : TObject;
+begin
+  Result := TObject(GetPointer);
+end;
+
+procedure TAutoPtr<T>.SetObject(const AnObject: TObject);
+begin
+  SetPointer( Pointer(AnObject));
+end;
+
+function TAutoPtr<T>.GetValue : TObject;
+begin
+  Result := T(GetPointer);
+end;
+
+procedure TAutoPtr<T>.SetValue(const AValue: T);
+begin
+  SetObject(TObject(AValue));
+end;
+
+procedure TAutoPtr<T>.CheckGuard;
+begin
+  if not Assigned(FGuardian) then
+    FGuardian := TGuard.Create(@Self);
+end;
+
+end.

+ 53 - 12
src/libraries/sphere10/UVisualGrid.pas

@@ -36,6 +36,14 @@ type
 
   TSelectionType = (stNone, stCell, stRow, stMultiRow);
 
+  { TDeselectionType }
+
+  TDeselectionType = (
+    dtNone,    { deselection is disallowed }
+    dtDefault, { click on selection means deselect (except multirow which is special case), click outside selection means new selection }
+    dtClick    { each click anywhere if anything is selected means deselection }
+  );
+
   { TVisualGridSelection }
 
   TVisualGridSelection = record
@@ -89,7 +97,7 @@ type
 
   { TVisualGridOptions }
 
-  TVisualGridOptions = set of (vgoColAutoFill, vgoColSizing, vgoAllowDeselect,
+  TVisualGridOptions = set of (vgoColAutoFill, vgoColSizing,
     vgoMultiSearchCheckComboBox, vgoSortDirectionAllowNone);
 
   { TSortMode }
@@ -210,6 +218,8 @@ type
   protected { events for UI }
     procedure StandardDrawCell(Sender: TObject; ACol, ARow: Longint;
       Rect: TRect; State: TGridDrawState);
+    procedure GridMouseDown(Sender: TObject; Button: TMouseButton;
+      Shift: TShiftState; X, Y: Integer);
     procedure GridMouseUp(Sender: TObject; Button: TMouseButton;
       Shift: TShiftState; X, Y: Integer);
     procedure SearchKindPopupMenuClick(Sender: TObject);
@@ -237,6 +247,7 @@ type
     FCanPage: boolean;
     FCanSearch: boolean;
     FSelectionType: TSelectionType;
+    FDeselectionType: TDeselectionType;
     FCurrentSelectionType: TSelectionType;
     FLastSelection: TVisualGridSelection;
     FIgnoreSelectionEvent: boolean;
@@ -271,6 +282,7 @@ type
     procedure SetPageIndex(Value: Integer);
     procedure SetPageSize(Value: Integer);
     procedure SetSelectionType(AValue: TSelectionType);
+    procedure SetDeselectionType(AValue: TDeselectionType);
     procedure SetWidgetControl(AValue: TControl);
     function CalculateCellContentRect(const ARect : TRect) : TRect;
   protected { TComponent }
@@ -336,6 +348,7 @@ type
     property Options: TVisualGridOptions read FOptions write SetOptions;
     property Canvas: TCanvas read GetCanvas;
     property SelectionType: TSelectionType read FSelectionType write SetSelectionType;
+    property DeselectionType: TDeselectionType read FDeselectionType write SetDeselectionType;
     property Selection: TVisualGridSelection read GetSelection;
     property SelectedRows : TArray<Variant> read GetSelectedRows;
     property SortMode: TSortMode read FSortMode write SetSortMode;
@@ -374,6 +387,7 @@ type
     property CanSearch;
     property Options;
     property SelectionType;
+    property DeselectionType;
     property SortMode;
     property SearchMode;
     property FetchDataInThread;
@@ -393,7 +407,7 @@ procedure Register;
 
 implementation
 
-uses Variants, UAutoScope, Dialogs;
+uses Variants, UMemory, Dialogs;
 
 resourcestring
   sTotal = 'Total: %d';
@@ -991,6 +1005,7 @@ begin
       Align := alClient;
       BorderStyle := bsNone;
       OnDrawCell := StandardDrawCell;
+      OnMouseDown := GridMouseDown;
       OnMouseUp := GridMouseUp;
       OnSelection := GridSelection;
       OnHeaderClick := GridHeaderClick;
@@ -1481,7 +1496,7 @@ function TCustomVisualGrid.GetSelectedRows : TArray<Variant>;
 var
   sel : TVisualGridSelection;
   selectedRows : TList<Variant>;
-  GC: TScoped;
+  GC: TDisposables;
   row : Integer;
 begin
   sel := GetSelection;
@@ -1628,11 +1643,9 @@ end;
 procedure TCustomVisualGrid.SetOptions(AValue: TVisualGridOptions);
 var
   LSortDirectionAllowNone: boolean;
-  LRefreshSelection: boolean;
 begin
   if FOptions=AValue then Exit;
   LSortDirectionAllowNone := vgoSortDirectionAllowNone in FOptions;
-  LRefreshSelection := vgoAllowDeselect in FOptions;
 
   FOptions:=AValue;
   if vgoColSizing in FOptions then
@@ -1646,12 +1659,6 @@ begin
   // refresh for sort direction graphic
   if LSortDirectionAllowNone <> (vgoSortDirectionAllowNone in AValue) then
     SortDirectionGlyphRefresh;
-  // refresh selection
-  if LRefreshSelection <> (vgoAllowDeselect in AValue) then
-  begin
-    ResetLastSelection;
-    UpdateSelection(SelectionType);
-  end;
 end;
 
 procedure TCustomVisualGrid.SetRows(ARow: Integer; AValue: Variant);
@@ -2126,6 +2133,17 @@ begin
   GridSelection(Self, 0, 0);
 end;
 
+procedure TCustomVisualGrid.SetDeselectionType(AValue: TDeselectionType);
+begin
+  if FDeselectionType=AValue then
+    Exit;
+
+  FDeselectionType:=AValue;
+
+  ResetLastSelection;
+  UpdateSelection(SelectionType);
+end;
+
 procedure TCustomVisualGrid.SetWidgetControl(AValue: TControl);
 begin
   if FWidgetControl=AValue then Exit;
@@ -2178,6 +2196,26 @@ begin
     DoDrawCell(Self, ACol, ARow, Rect, State, LCellData);
 end;
 
+procedure TCustomVisualGrid.GridMouseDown(Sender: TObject; Button: TMouseButton;
+  Shift: TShiftState; X, Y: Integer);
+begin
+  if ColCount = 0 then
+    Exit;
+
+  if FDeselectionType = dtClick then
+    case Button of
+      mbLeft:
+        if (SelectionType <> stNone) and (FDrawGrid.MouseToGridZone(X, Y) = gzNormal) then
+          if Assigned(FLastSelection.Selections) then
+          begin
+            ResetLastSelection;
+            UpdateSelection(stNone);
+            FIgnoreSelectionEvent := true;
+          end else
+            UpdateSelection(FSelectionType);
+    end;
+end;
+
 procedure TCustomVisualGrid.GridMouseUp(Sender: TObject; Button: TMouseButton;
   Shift: TShiftState; X, Y: Integer);
 var
@@ -2214,10 +2252,13 @@ begin
             LPopup.PopUp(X, Y);
       end;
     mbLeft:
-      if (SelectionType <> stNone) and (vgoAllowDeselect in FOptions) and
+      if (SelectionType <> stNone) and (FDeselectionType <> dtNone) and
        (FDrawGrid.MouseToGridZone(X, Y) = gzNormal) then
       begin
         LSelection := GetSelection;
+        if FIgnoreSelectionEvent then
+          FIgnoreSelectionEvent:=false
+        else
         if (FCurrentSelectionType <> stNone) and SelectionsEquals(LSelection, FLastSelection) then
         begin
           ResetLastSelection;