View Issue Details

IDProjectCategoryView StatusLast Update
0005305JEDI VCL00 JVCL Componentspublic2011-06-07 17:28
ReporterralfiiiAssigned Toobones 
PrioritynormalSeverityfeatureReproducibilityalways
Status resolvedResolutionwon't fix 
Product Version3.39 
Target VersionFixed in Version 
Summary0005305: Allow use of TJvFormPlacement without AppStorage
DescriptionI'm just porting an old app that used the RxLibrary to Jedi.

Currently I have to replace all TFormStorages with TJvFormStorages which is OK.
BUT: I also have to add a TJvAppRegistryStorage (or whatever I need as storage) on every single form. Stupid work.

I suppose most times all forms in an application will use the same AppStorage, so I added (very simple) support for that.

I also added code to allow defining a default AppStorage-type, so that for your apps you only need to drop a TJvFormStorages, the defined AppStorage is created automatically on demand (if no other is defined already).
TagsNo tags attached.

Activities

2010-08-19 17:02

 

RxPatch.patch (1,518 bytes)
Index: JvAppStorage.pas
===================================================================
--- JvAppStorage.pas	(revision 14367)
+++ JvAppStorage.pas	(working copy)
@@ -959,6 +959,8 @@
     );
 {$ENDIF UNITVERSIONING}
 
+var DefaultAppStorage : TJvCustomAppStorage;
+
 implementation
 
 uses
@@ -1296,6 +1298,7 @@
   FReadOnly := False;
   FInternalTranslateStringEngine := TJvTranslateString.Create(Self);
   FSynchronizeFlushReload := False;
+  DefaultAppStorage:=self;
 end;
 
 destructor TJvCustomAppStorage.Destroy;
Index: JvFormPlacement.pas
===================================================================
--- JvFormPlacement.pas	(revision 14367)
+++ JvFormPlacement.pas	(working copy)
@@ -291,6 +291,10 @@
 uses
   Consts,
   JclStrings,
+{$DEFINE CreateRegAppStorageOnDemand}  // If not AppStorage is defined create a TJvAppRegistryStorage as default storage
+{$IFDEF CreateRegAppStorageOnDemand}
+  JvAppRegistryStorage,
+{$ENDIF}
   JvJCLUtils, JvPropertyStorage;
 
 const
@@ -345,6 +349,16 @@
   inherited Loaded;
   if not (csDesigning in ComponentState) then
   begin
+    if not assigned(FAppStorage) then
+    begin
+{$IFDEF CreateRegAppStorageOnDemand}
+         if not assigned(DefaultAppStorage) then
+            AppStorage:=TJvAppRegistryStorage.Create(Owner);
+{$ENDIF}
+         if assigned(DefaultAppStorage) then
+            AppStorage:=DefaultAppStorage;
+    end;
+
     ResolveAppStoragePath;
     if Loading then
       SetEvents;
RxPatch.patch (1,518 bytes)

obones

2010-08-20 11:39

administrator   ~0017588

You don't need that. Put a single AppStorage in a datamodule (or create it in a unit) and link it to all the form storages.
Even better, create an ancestor form that has the form storage on it and make all forms inherit from it. This way you only have to define the link once.

The way it has been separated is for flexibility, we won't be adding back the rigidity from the original design.

ralfiii

2010-08-20 12:00

reporter   ~0017609

I am not suggesting to reduce the flexibilty, I am only suggesting a way to reduce the work for the developer.
With the patch you can use the FormStorage in a simpler way than currently required but you can still do anything as currently. So I don't see any downsides of this modifications.

2010-08-20 13:01

 

RxPatch2.patch (3,131 bytes)
Index: JvAppStorage.pas
===================================================================
--- JvAppStorage.pas	(revision 14367)
+++ JvAppStorage.pas	(working copy)
@@ -959,6 +959,8 @@
     );
 {$ENDIF UNITVERSIONING}
 
+var DefaultAppStorage : TJvCustomAppStorage;  //!
+
 implementation
 
 uses
@@ -1296,10 +1298,13 @@
   FReadOnly := False;
   FInternalTranslateStringEngine := TJvTranslateString.Create(Self);
   FSynchronizeFlushReload := False;
+  DefaultAppStorage:=self;  //!
 end;
 
 destructor TJvCustomAppStorage.Destroy;
 begin
+  if DefaultAppStorage=self then //!
+     DefaultAppStorage:=nil;     //!
   if FlushOnDestroy then
     Flush;
   FreeAndNil(FInternalTranslateStringEngine);
Index: JvFormPlacement.pas
===================================================================
--- JvFormPlacement.pas	(revision 14367)
+++ JvFormPlacement.pas	(working copy)
@@ -111,6 +111,7 @@
     procedure FormDestroy(Sender: TObject);
     function GetForm: TForm;
     procedure SetAppStorage(const Value: TJvCustomAppStorage);
+    function Store_AppStorePath : boolean; //!
   protected
     procedure ResolveAppStoragePath;
     procedure Loaded; override;
@@ -141,7 +142,7 @@
   published
     property Active: Boolean read FActive write FActive default True;
     property AppStorage: TJvCustomAppStorage read FAppStorage write SetAppStorage;
-    property AppStoragePath: string read FAppStoragePath write SetAppStoragePath;
+    property AppStoragePath: string read FAppStoragePath write SetAppStoragePath stored Store_AppStorePath;    //!
     property MinMaxInfo: TJvWinMinMaxInfo read FWinMinMaxInfo write SetWinMinMaxInfo;
     property Options: TPlacementOptions read FOptions write FOptions default [fpState, fpSize, fpLocation];
     property PreventResize: Boolean read FPreventResize write SetPreventResize default False;
@@ -291,6 +292,10 @@
 uses
   Consts,
   JclStrings,
+{$DEFINE CreateRegAppStorageOnDemand}  //! If not AppStorage is defined create a TJvAppRegistryStorage as default storage
+{$IFDEF CreateRegAppStorageOnDemand}
+  JvAppRegistryStorage,
+{$ENDIF}
   JvJCLUtils, JvPropertyStorage;
 
 const
@@ -345,6 +350,17 @@
   inherited Loaded;
   if not (csDesigning in ComponentState) then
   begin
+    //! Make use of FormStorage simpler
+    if not assigned(FAppStorage) then
+    begin
+{$IFDEF CreateRegAppStorageOnDemand}
+         if not assigned(DefaultAppStorage) then
+            AppStorage:=TJvAppRegistryStorage.Create(Owner);
+{$ENDIF}
+         if assigned(DefaultAppStorage) then
+            AppStorage:=DefaultAppStorage;
+    end;
+
     ResolveAppStoragePath;
     if Loading then
       SetEvents;
@@ -1357,6 +1373,12 @@
   ReplaceComponentReference(Self, Value, TComponent(FAppStorage));
 end;
 
+//! reduce footprint in dfm: Don't store path if it was not modified
+function TJvFormPlacement.Store_AppStorePath : boolean;
+begin
+  result:=FAppStoragePath<>cFormNameMask+'\';
+end;
+
 { TJvFormStorageStringList }
 
 procedure TJvFormStorageStringList.Assign(Source: TPersistent);
RxPatch2.patch (3,131 bytes)

ralfiii

2010-08-20 13:08

reporter   ~0017611

...and, just for the sake of completeness: The 2nd patch (includes the 1st one) does a bit cleanup when the default AppStorage is destroyed.

It also reduces the footprint of the dfm file by not storing the AppPath if it still holds the default value.

ralfiii

2010-08-20 16:20

reporter   ~0017613

And, one more: In TJvAppRegistryStorage the default value for UseOldDefaultRoot should be set to True.
Otherwise for lazy developers that forgot to set the Root-value the storage puts values directly under deposits by default under HKCU
HKCU/Software/%CompanyName%/%ApplicationName% is clearly a better default location

ralfiii

2010-08-23 13:41

reporter   ~0017618

And: The Form storage utilty functions like "InternalSaveFormPlacement" are only used by TJvFormPlacement.
So these functions could/should be moved out of unit JvJVCLUtils.pas (which btw. holds a lot of very clever and handy functions) to JvFormPlacement.pas

ralfiii

2010-08-23 14:07

reporter   ~0017619

...sorry, even more.

CrtResString in JvJVCLUtils does not work properly on multi monitor systems.
Use
  Result := Format( '(%d/%d)',
                    [Screen.DesktopWidth, Screen.DesktopHeight]);
instead.

I guess there are a few more inherited flaws in the FormStorage-component. Should I post further notes here or does nobody look into posts with "feedback" status anyway?!

ralfiii

2010-08-25 14:20

reporter   ~0017622

...and the form state (e.g. Maximized) is not restored with Delphi Version < 2009...

2010-08-25 16:52

 

Placement.patch (6,435 bytes)
Index: JvAppRegistryStorage.pas
===================================================================
--- JvAppRegistryStorage.pas	(revision 14328)
+++ JvAppRegistryStorage.pas	(working copy)
@@ -1,4 +1,4 @@
-{-----------------------------------------------------------------------------
+{-----------------------------------------------------------------------------
 The contents of this file are subject to the Mozilla Public License
 Version 1.1 (the "License"); you may not use this file except in compliance
 with the License. You may obtain a copy of the License at
@@ -85,6 +85,7 @@
     FCompanyNameErrorHandled: Boolean;
     function GetStorageOptions: TJvAppRegistryStorageOptions;
     procedure SetStorageOptions(const Value: TJvAppRegistryStorageOptions);
+    function Store_Root : boolean;  //!
   protected
     procedure Loaded; override;
 
@@ -125,7 +126,7 @@
     constructor Create(AOwner: TComponent); override;
   published
     property RegRoot: TJvRegKey read GetRegRoot write SetRegRoot default hkCurrentUser;
-    property Root read GetRoot write SetRoot;
+    property Root read GetRoot write SetRoot stored Store_Root;  //!
     property SubStorages;
     property FlushOnDestroy;
     property UseOldDefaultRoot: Boolean read FUseOldDefaultRoot write SetUseOldDefaultRoot stored True default False ;
@@ -182,7 +183,7 @@
 begin
   inherited Create(AOwner);
   FRegHKEY := HKEY_CURRENT_USER;
-  FUseOldDefaultRoot := False;
+  UseOldDefaultRoot := True; //! False;
   FAppNameErrorHandled := False;
   FCompanyNameErrorHandled := False;
 end;
@@ -573,6 +574,12 @@
   (Inherited StorageOptions).Assign(Value);
 end;
 
+//! Don't store root if default path should be used anyway
+function TJvAppRegistryStorage.Store_Root : boolean;
+begin
+     result:=not FUseOldDefaultRoot;
+end;
+
 {$IFDEF UNITVERSIONING}
 initialization
   RegisterUnitVersion(HInstance, UnitVersioning);
Index: JvAppStorage.pas
===================================================================
--- JvAppStorage.pas	(revision 14328)
+++ JvAppStorage.pas	(working copy)
@@ -959,6 +959,8 @@
     );
 {$ENDIF UNITVERSIONING}
 
+var DefaultAppStorage : TJvCustomAppStorage;  //!
+
 implementation
 
 uses
@@ -1296,10 +1298,13 @@
   FReadOnly := False;
   FInternalTranslateStringEngine := TJvTranslateString.Create(Self);
   FSynchronizeFlushReload := False;
+  DefaultAppStorage:=self;  //!
 end;
 
 destructor TJvCustomAppStorage.Destroy;
 begin
+  if DefaultAppStorage=self then //!
+     DefaultAppStorage:=nil;     //!
   if FlushOnDestroy then
     Flush;
   FreeAndNil(FInternalTranslateStringEngine);
Index: JvFormPlacement.pas
===================================================================
--- JvFormPlacement.pas	(revision 14328)
+++ JvFormPlacement.pas	(working copy)
@@ -111,6 +111,7 @@
     procedure FormDestroy(Sender: TObject);
     function GetForm: TForm;
     procedure SetAppStorage(const Value: TJvCustomAppStorage);
+    function Store_AppStorePath : boolean; //!
   protected
     procedure ResolveAppStoragePath;
     procedure Loaded; override;
@@ -141,7 +142,7 @@
   published
     property Active: Boolean read FActive write FActive default True;
     property AppStorage: TJvCustomAppStorage read FAppStorage write SetAppStorage;
-    property AppStoragePath: string read FAppStoragePath write SetAppStoragePath;
+    property AppStoragePath: string read FAppStoragePath write SetAppStoragePath stored Store_AppStorePath;    //!
     property MinMaxInfo: TJvWinMinMaxInfo read FWinMinMaxInfo write SetWinMinMaxInfo;
     property Options: TPlacementOptions read FOptions write FOptions default [fpState, fpSize, fpLocation];
     property PreventResize: Boolean read FPreventResize write SetPreventResize default False;
@@ -291,6 +292,10 @@
 uses
   Consts,
   JclStrings,
+{$DEFINE CreateRegAppStorageOnDemand}  //! If not AppStorage is defined create a TJvAppRegistryStorage as default storage
+{$IFDEF CreateRegAppStorageOnDemand}
+  JvAppRegistryStorage,
+{$ENDIF}
   JvJCLUtils, JvPropertyStorage;
 
 const
@@ -345,6 +350,17 @@
   inherited Loaded;
   if not (csDesigning in ComponentState) then
   begin
+    //! Make use of FormStorage simpler
+    if not assigned(FAppStorage) then
+    begin
+{$IFDEF CreateRegAppStorageOnDemand}
+         if not assigned(DefaultAppStorage) then
+            AppStorage:=TJvAppRegistryStorage.Create(Owner);
+{$ENDIF}
+         if assigned(DefaultAppStorage) then
+            AppStorage:=DefaultAppStorage;
+    end;
+
     ResolveAppStoragePath;
     if Loading then
       SetEvents;
@@ -1357,6 +1373,12 @@
   ReplaceComponentReference(Self, Value, TComponent(FAppStorage));
 end;
 
+//! reduce footprint in dfm: Don't store path if it was not modified
+function TJvFormPlacement.Store_AppStorePath : boolean;
+begin
+  result:=(FAppStoragePath<>cFormNameMask+'\') and (FAppStoragePath<>cFormNameMask);
+end;
+
 { TJvFormStorageStringList }
 
 procedure TJvFormStorageStringList.Assign(Source: TPersistent);
Index: JvJVCLUtils.pas
===================================================================
--- JvJVCLUtils.pas	(revision 14328)
+++ JvJVCLUtils.pas	(working copy)
@@ -3908,8 +3908,13 @@
 
 function CrtResString: string;
 begin
-  Result := Format('(%dx%d)', [GetSystemMetrics(SM_CXSCREEN),
-    GetSystemMetrics(SM_CYSCREEN)]);
+//!  Result := Format('(%dx%d)', [GetSystemMetrics(SM_CXSCREEN),
+//!    GetSystemMetrics(SM_CYSCREEN)]);
+
+//! New Resolution Identifier, because old method did not work
+//    properly for Multi-screen systems (returned only width/height of current screen)
+  Result := Format( '(%dx%d)',
+                    [Screen.DesktopWidth, Screen.DesktopHeight]);
 end;
 
 function ReadPosStr(AppStorage: TJvCustomAppStorage; const Path: string): string;
@@ -4146,13 +4151,12 @@
       PostMessage(Application.Handle, WM_SYSCOMMAND, SC_MINIMIZE, 0);
       Exit;
     end;
-    {$IFNDEF DELPHI2010_UP} // Using this Hack didn't work with D2010
+    {$IFNDEF DELPHI2010_UP} //! Using this Hack didn't work with D2010
     if Form.FormStyle in [fsMDIChild, fsMDIForm] then
       TWindowState(Pointer(@Form.WindowState)^) := WinState
     else
-    {$ELSE}
+    {$ENDIF}
       Form.WindowState := WinState;
-    {$ENDIF}
   end;
   Form.Update;
 end;
Placement.patch (6,435 bytes)

ralfiii

2010-08-25 16:59

reporter   ~0017626

Attached is a patch that fixes all mentioned problems.
*) feature: JvFormStorage uses application wide default AppStorage if one is defined
*) feature: dfm footprint reduced for TJvFormPlacement (AppStoragePath)
*) feature: dfm foorprint recuded for TJvAppRegistryStorage (Root)
*) fix: JvJVCL.CrtResString returned wrong value for multi monitor systems (used by JFormPlacement)
*) fix: Form state (e.g. Maximized) was not returned for Delphi Versions < 2009

jfudickar

2010-09-06 00:32

developer   ~0017649

Hi,

I've added your patch for the CrtResString problem, but the problem of the storing the Form state was already fixed.

I'm not sure that we should your DefaultStorage solution. I think we need a little bit more discussion about it.

Kind regards
Jens

ralfiii

2010-09-07 17:01

reporter   ~0017659

Fine.

What's with the reduced footprint-stuff?

Regarding DefaultStorage: I'd just like to remind that the adoptions we made have no effects whatsoever on an existing application, I hope you understand that.
The whole thing only has an effect if you "forgot" to wire a Formstorage (you have to admit, this makes the use of the formstorage a lot easier)
So I don't see any disadvantages. If you feel different please let me know.

obones

2010-10-11 10:27

administrator   ~0017865

I don't like that DefaultStorage idea at all.
The point is to have them split, and independent.
Furthermore, there are issues when many components all write to the same file at once. Most obvious one is having parameters overwritten with wrong values.
The way to overcome this is to centralize the storage component in a datamodule (for instance) and have all the others use it.
So to me, sorry, allowing people not to think for a split second means that we have more and more requests about something they should have thought of when creating their program

jfudickar

2010-10-11 23:36

developer   ~0017875

I agree with you.

obones

2010-10-12 09:29

administrator   ~0017876

What we could add, though, is the raise of an exception if AppStorage is not set and the SavePlacement/LoadPlacement ReadXXX/WriteXXX methods are called. That exception would mention that one needs to set AppStorage to a global instance of a TJvAppStorage derived class
And we could also add a "IgnoreNilAppStorage" boolean property that defaults to False but can be set to True to avoid the exception if a developer needs that.

ralfiii

2010-10-13 15:46

reporter   ~0017881

As I said in my mail: You don't like the idea of a default classtype.
I understand that and I agree.

What do think about the idea of the default instance?

In other words: Instead of raising an exception when no AppStorage is defined simply use the same instance of a TJvAppStorage-descendant that was used the last time. (so not create a new object, but re-use the one that is already in use).

This basically results in what you recommended - just instead of a central datamodule the component grabs the storage-object it needs itself.

ralfiii

2010-10-13 15:51

reporter   ~0017882

To complete that:

Using that approach you as a developer can throw a formstorage and an appstorage on the main form of a project, configure the appstorage and wire them together.
On all other forms dropping the formstorage would be enough.

So there's no need for a central datamodule and there's no danger that you forget to wire a formstorage.

obones

2010-11-09 14:46

administrator   ~0018054

I've seen this implementation in FastReport, and, to be "nice", I'll just say that I did not like it at all. It was buggy, error prone, and sometimes prevented anyone from using multiple instances of that "common" component.

In the end, I'm not at all in favor of somehow magically wiring things together when it takes so little time to do it manually.

jfudickar

2010-11-09 16:14

developer   ~0018064

Agreed!

obones

2011-06-07 17:28

administrator   ~0018573

Then we won't change this, sorry.

Issue History

Date Modified Username Field Change
2010-08-19 17:02 ralfiii New Issue
2010-08-19 17:02 ralfiii File Added: RxPatch.patch
2010-08-20 11:39 obones Note Added: 0017588
2010-08-20 11:39 obones Status new => feedback
2010-08-20 12:00 ralfiii Note Added: 0017609
2010-08-20 13:01 ralfiii File Added: RxPatch2.patch
2010-08-20 13:08 ralfiii Note Added: 0017611
2010-08-20 16:20 ralfiii Note Added: 0017613
2010-08-23 13:41 ralfiii Note Added: 0017618
2010-08-23 14:07 ralfiii Note Added: 0017619
2010-08-25 14:20 ralfiii Note Added: 0017622
2010-08-25 16:52 ralfiii File Added: Placement.patch
2010-08-25 16:59 ralfiii Note Added: 0017626
2010-09-06 00:32 jfudickar Note Added: 0017649
2010-09-07 17:01 ralfiii Note Added: 0017659
2010-10-11 10:27 obones Note Added: 0017865
2010-10-11 23:36 jfudickar Note Added: 0017875
2010-10-12 09:29 obones Note Added: 0017876
2010-10-13 15:46 ralfiii Note Added: 0017881
2010-10-13 15:51 ralfiii Note Added: 0017882
2010-11-09 14:46 obones Note Added: 0018054
2010-11-09 16:14 jfudickar Note Added: 0018064
2011-06-07 17:28 obones Note Added: 0018573
2011-06-07 17:28 obones Status feedback => resolved
2011-06-07 17:28 obones Resolution open => won't fix
2011-06-07 17:28 obones Assigned To => obones