{----------------------------------------------------------------------------- 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 http://www.mozilla.org/MPL/MPL-1.1.html Software distributed under the License is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either expressed or implied. See the License for the specific language governing rights and limitations under the License. The Original Code is: JvPlacemnt.PAS, released on 2002-07-04. The Initial Developers of the Original Code are: Fedor Koshevnikov, Igor Pavluk and Serge Korolev Copyright (c) 1997, 1998 Fedor Koshevnikov, Igor Pavluk and Serge Korolev Copyright (c) 2001,2002 SGB Software All Rights Reserved. Last Modified: 2002-07-04 You may retrieve the latest version of this file at the Project JEDI's JVCL home page, located at http://jvcl.sourceforge.net Known Issues: -----------------------------------------------------------------------------} {$I JVCL.INC} unit JvPlacemnt; interface uses {$IFDEF COMPILER6_UP} RTLConsts, Variants, {$ENDIF} Windows, Registry, Controls, Messages, Classes, Forms, IniFiles, JvWndProcHook {, JvComponent}; type TPlacementOption = (fpState, fpPosition, fpActiveControl); TPlacementOptions = set of TPlacementOption; TPlacementOperation = (poSave, poRestore); {$IFDEF WIN32} TPlacementRegRoot = (prCurrentUser, prLocalMachine, prCurrentConfig, prClassesRoot, prUsers, prDynData); {$ENDIF} TJvIniLink = class; TJvFormPlacement = class; TJvWinMinMaxInfo = class(TPersistent) private FOwner: TJvFormPlacement; FMinMaxInfo: TMinMaxInfo; function GetMinMaxInfo(Index: Integer): Integer; procedure SetMinMaxInfo(Index: Integer; Value: Integer); public function DefaultMinMaxInfo: Boolean; procedure Assign(Source: TPersistent); override; published property MaxPosLeft: Integer index 0 read GetMinMaxInfo write SetMinMaxInfo default 0; property MaxPosTop: Integer index 1 read GetMinMaxInfo write SetMinMaxInfo default 0; property MaxSizeHeight: Integer index 2 read GetMinMaxInfo write SetMinMaxInfo default 0; property MaxSizeWidth: Integer index 3 read GetMinMaxInfo write SetMinMaxInfo default 0; property MaxTrackHeight: Integer index 4 read GetMinMaxInfo write SetMinMaxInfo default 0; property MaxTrackWidth: Integer index 5 read GetMinMaxInfo write SetMinMaxInfo default 0; property MinTrackHeight: Integer index 6 read GetMinMaxInfo write SetMinMaxInfo default 0; property MinTrackWidth: Integer index 7 read GetMinMaxInfo write SetMinMaxInfo default 0; end; TJvFormPlacement = class(TComponent) private FActive: Boolean; FIniFileName: string; FIniSection: string; FIniFile: TIniFile; FUseRegistry: Boolean; {$IFDEF WIN32} FRegIniFile: TRegIniFile; FRegistryRoot: TPlacementRegRoot; {$ENDIF WIN32} FLinks: TList; FOptions: TPlacementOptions; FVersion: Integer; FSaved: Boolean; FRestored: Boolean; FDestroying: Boolean; FPreventResize: Boolean; FWinMinMaxInfo: TJvWinMinMaxInfo; FDefMaximize: Boolean; FWinHook: TJvWindowHook; FSaveFormShow: TNotifyEvent; FSaveFormDestroy: TNotifyEvent; FSaveFormCloseQuery: TCloseQueryEvent; FOnSavePlacement: TNotifyEvent; FOnRestorePlacement: TNotifyEvent; procedure SetEvents; procedure RestoreEvents; procedure SetHook; procedure ReleaseHook; procedure CheckToggleHook; function CheckMinMaxInfo: Boolean; procedure MinMaxInfoModified; procedure SetWinMinMaxInfo(Value: TJvWinMinMaxInfo); function GetIniSection: string; procedure SetIniSection(const Value: string); function GetIniFileName: string; procedure SetIniFileName(const Value: string); function GetIniFile: TObject; procedure SetPreventResize(Value: Boolean); procedure UpdatePreventResize; procedure UpdatePlacement; procedure IniNeeded(ReadOnly: Boolean); procedure IniFree; procedure AddLink(ALink: TJvIniLink); procedure NotifyLinks(Operation: TPlacementOperation); procedure RemoveLink(ALink: TJvIniLink); procedure WndMessage(Sender: TObject; var Msg: TMessage; var Handled: Boolean); procedure FormShow(Sender: TObject); procedure FormCloseQuery(Sender: TObject; var CanClose: Boolean); procedure FormDestroy(Sender: TObject); function GetForm: TForm; protected procedure Loaded; override; procedure Save; dynamic; procedure Restore; dynamic; procedure SavePlacement; virtual; procedure RestorePlacement; virtual; function DoReadString(const Section, Ident, Default: string): string; virtual; procedure DoWriteString(const Section, Ident, Value: string); virtual; property Form: TForm read GetForm; public constructor Create(AOwner: TComponent); override; destructor Destroy; override; procedure SaveFormPlacement; procedure RestoreFormPlacement; function ReadString(const Ident, Default: string): string; procedure WriteString(const Ident, Value: string); function ReadInteger(const Ident: string; Default: Longint): Longint; procedure WriteInteger(const Ident: string; Value: Longint); procedure EraseSections; property IniFileObject: TObject read GetIniFile; property IniFile: TIniFile read FIniFile; {$IFDEF WIN32} property RegIniFile: TRegIniFile read FRegIniFile; {$ENDIF WIN32} published property Active: Boolean read FActive write FActive default True; property IniFileName: string read GetIniFileName write SetIniFileName; property IniSection: string read GetIniSection write SetIniSection; property MinMaxInfo: TJvWinMinMaxInfo read FWinMinMaxInfo write SetWinMinMaxInfo; property Options: TPlacementOptions read FOptions write FOptions default [fpState, fpPosition]; property PreventResize: Boolean read FPreventResize write SetPreventResize default False; {$IFDEF WIN32} property RegistryRoot: TPlacementRegRoot read FRegistryRoot write FRegistryRoot default prCurrentUser; {$ENDIF WIN32} property UseRegistry: Boolean read FUseRegistry write FUseRegistry default False; property Version: Integer read FVersion write FVersion default 0; property OnSavePlacement: TNotifyEvent read FOnSavePlacement write FOnSavePlacement; property OnRestorePlacement: TNotifyEvent read FOnRestorePlacement write FOnRestorePlacement; end; {$IFDEF COMPILER3_UP} TJvStoredValues = class; TJvStoredValue = class; {$ENDIF COMPILER3_UP} TJvFormStorage = class(TJvFormPlacement) private FStoredProps: TStrings; {$IFDEF COMPILER3_UP} FStoredValues: TJvStoredValues; {$ENDIF COMPILER3_UP} procedure SetStoredProps(Value: TStrings); {$IFDEF COMPILER3_UP} procedure SeTJvStoredValues(Value: TJvStoredValues); function GeTJvStoredValue(const Name: string): Variant; procedure SeTJvStoredValue(const Name: string; Value: Variant); {$ENDIF COMPILER3_UP} protected procedure Loaded; override; procedure Notification(AComponent: TComponent; Operation: TOperation); override; procedure SavePlacement; override; procedure RestorePlacement; override; procedure SaveProperties; virtual; procedure RestoreProperties; virtual; procedure WriteState(Writer: TWriter); override; public constructor Create(AOwner: TComponent); override; destructor Destroy; override; {$IFDEF WIN32} procedure SetNotification; {$ENDIF WIN32} {$IFDEF COMPILER3_UP} property StoredValue[const Name: string]: Variant read GeTJvStoredValue write SeTJvStoredValue; {$ENDIF COMPILER3_UP} published property StoredProps: TStrings read FStoredProps write SetStoredProps; {$IFDEF COMPILER3_UP} property StoredValues: TJvStoredValues read FStoredValues write SeTJvStoredValues; {$ENDIF COMPILER3_UP} end; TJvIniLink = class(TPersistent) private FStorage: TJvFormPlacement; FOnSave: TNotifyEvent; FOnLoad: TNotifyEvent; function GetIniObject: TObject; function GetRootSection: string; procedure SetStorage(Value: TJvFormPlacement); protected procedure SaveToIni; virtual; procedure LoadFromIni; virtual; public destructor Destroy; override; property IniObject: TObject read GetIniObject; property Storage: TJvFormPlacement read FStorage write SetStorage; property RootSection: string read GetRootSection; property OnSave: TNotifyEvent read FOnSave write FOnSave; property OnLoad: TNotifyEvent read FOnLoad write FOnLoad; end; {$IFDEF COMPILER3_UP} TJvStoredValueEvent = procedure(Sender: TJvStoredValue; var Value: Variant) of object; TJvStoredValue = class(TCollectionItem) private FName: string; FValue: Variant; FKeyString: string; FOnSave: TJvStoredValueEvent; FOnRestore: TJvStoredValueEvent; function IsValueStored: Boolean; function GeTJvStoredValues: TJvStoredValues; protected function GetDisplayName: string; override; procedure SetDisplayName(const Value: string); override; public constructor Create(Collection: TCollection); override; procedure Assign(Source: TPersistent); override; procedure Clear; procedure Save; virtual; procedure Restore; virtual; property StoredValues: TJvStoredValues read GeTJvStoredValues; published property Name: string read FName write SetDisplayName; property Value: Variant read FValue write FValue stored IsValueStored; property KeyString: string read FKeyString write FKeyString; property OnSave: TJvStoredValueEvent read FOnSave write FOnSave; property OnRestore: TJvStoredValueEvent read FOnRestore write FOnRestore; end; {$IFDEF COMPILER4_UP} TJvStoredValues = class(TOwnedCollection) {$ELSE} TJvStoredValues = class(TCollection) {$ENDIF} private FStorage: TJvFormPlacement; function GetValue(const Name: string): TJvStoredValue; procedure SetValue(const Name: string; StoredValue: TJvStoredValue); function GeTJvStoredValue(const Name: string): Variant; procedure SeTJvStoredValue(const Name: string; Value: Variant); function GetItem(Index: Integer): TJvStoredValue; procedure SetItem(Index: Integer; StoredValue: TJvStoredValue); public {$IFDEF COMPILER4_UP} constructor Create(AOwner: TPersistent); {$ELSE} constructor Create; {$ENDIF} function IndexOf(const Name: string): Integer; procedure SaveValues; virtual; procedure RestoreValues; virtual; property Storage: TJvFormPlacement read FStorage write FStorage; property Items[Index: Integer]: TJvStoredValue read GetItem write SetItem; default; property Values[const Name: string]: TJvStoredValue read GetValue write SetValue; property StoredValue[const Name: string]: Variant read GeTJvStoredValue write SeTJvStoredValue; end; {$ENDIF COMPILER3_UP} implementation uses SysUtils, {$IFDEF COMPILER3_UP} Consts, {$ENDIF COMPILER3_UP} JvAppUtils, JvStrUtils, JvProps, JvTypes; const { The following string should not be localized } siActiveCtrl = 'ActiveControl'; siVisible = 'Visible'; siVersion = 'FormVersion'; //=== TJvFormPlacement ======================================================= constructor TJvFormPlacement.Create(AOwner: TComponent); begin inherited Create(AOwner); FIniFileName := EmptyStr; FIniSection := EmptyStr; FActive := True; if AOwner is TForm then FOptions := [fpState, fpPosition] else FOptions := []; FWinHook := TJvWindowHook.Create(Self); FWinHook.AfterMessage := WndMessage; FWinMinMaxInfo := TJvWinMinMaxInfo.Create; FWinMinMaxInfo.FOwner := Self; FLinks := TList.Create; end; destructor TJvFormPlacement.Destroy; begin IniFree; while FLinks.Count > 0 do RemoveLink(FLinks.Last); FLinks.Free; if not (csDesigning in ComponentState) then begin ReleaseHook; RestoreEvents; end; //DisposeStr(FIniFileName); //DisposeStr(FIniSection); FWinMinMaxInfo.Free; inherited Destroy; end; procedure TJvFormPlacement.Loaded; var Loading: Boolean; begin Loading := csLoading in ComponentState; inherited Loaded; if not (csDesigning in ComponentState) then begin if Loading then SetEvents; CheckToggleHook; end; end; procedure TJvFormPlacement.AddLink(ALink: TJvIniLink); begin FLinks.Add(ALink); ALink.FStorage := Self; end; procedure TJvFormPlacement.NotifyLinks(Operation: TPlacementOperation); var I: Integer; begin for I := 0 to FLinks.Count - 1 do with TJvIniLink(FLinks[I]) do case Operation of poSave: SaveToIni; poRestore: LoadFromIni; end; end; procedure TJvFormPlacement.RemoveLink(ALink: TJvIniLink); begin ALink.FStorage := nil; FLinks.Remove(ALink); end; function TJvFormPlacement.GetForm: TForm; begin if Owner is TCustomForm then Result := TForm(Owner as TCustomForm) else Result := nil; end; procedure TJvFormPlacement.SetEvents; begin if Owner is TCustomForm then begin with TForm(Form) do begin FSaveFormShow := OnShow; OnShow := FormShow; FSaveFormCloseQuery := OnCloseQuery; OnCloseQuery := FormCloseQuery; FSaveFormDestroy := OnDestroy; OnDestroy := FormDestroy; FDefMaximize := (biMaximize in BorderIcons); end; if FPreventResize then UpdatePreventResize; end; end; procedure TJvFormPlacement.RestoreEvents; begin if (Owner <> nil) and (Owner is TCustomForm) then with TForm(Form) do begin OnShow := FSaveFormShow; OnCloseQuery := FSaveFormCloseQuery; OnDestroy := FSaveFormDestroy; end; end; procedure TJvFormPlacement.SetHook; begin if not (csDesigning in ComponentState) and (Owner <> nil) and (Owner is TCustomForm) then FWinHook.Control := Form; end; procedure TJvFormPlacement.ReleaseHook; begin FWinHook.Control := nil; end; procedure TJvFormPlacement.CheckToggleHook; begin if CheckMinMaxInfo or PreventResize then SetHook else ReleaseHook; end; function TJvFormPlacement.CheckMinMaxInfo: Boolean; begin Result := not FWinMinMaxInfo.DefaultMinMaxInfo; end; procedure TJvFormPlacement.MinMaxInfoModified; begin UpdatePlacement; if not (csLoading in ComponentState) then CheckToggleHook; end; procedure TJvFormPlacement.SetWinMinMaxInfo(Value: TJvWinMinMaxInfo); begin FWinMinMaxInfo.Assign(Value); end; procedure TJvFormPlacement.WndMessage(Sender: TObject; var Msg: TMessage; var Handled: Boolean); begin if FPreventResize and (Owner is TCustomForm) then begin case Msg.Msg of WM_GETMINMAXINFO: if Form.HandleAllocated and IsWindowVisible(Form.Handle) then begin with TWMGetMinMaxInfo(Msg).MinMaxInfo^ do begin ptMinTrackSize := Point(Form.Width, Form.Height); ptMaxTrackSize := Point(Form.Width, Form.Height); end; Msg.Result := 1; end; WM_INITMENUPOPUP: if TWMInitMenuPopup(Msg).SystemMenu then begin if Form.Menu <> nil then Form.Menu.DispatchPopup(TWMInitMenuPopup(Msg).MenuPopup); EnableMenuItem(TWMInitMenuPopup(Msg).MenuPopup, SC_SIZE, MF_BYCOMMAND or MF_GRAYED); EnableMenuItem(TWMInitMenuPopup(Msg).MenuPopup, SC_MAXIMIZE, MF_BYCOMMAND or MF_GRAYED); Msg.Result := 1; end; WM_NCHITTEST: begin if Msg.Result in [HTLEFT, HTRIGHT, HTBOTTOM, HTBOTTOMRIGHT, HTBOTTOMLEFT, HTTOP, HTTOPRIGHT, HTTOPLEFT] then Msg.Result := HTNOWHERE; end; end; end else if Msg.Msg = WM_GETMINMAXINFO then begin if CheckMinMaxInfo then begin with TWMGetMinMaxInfo(Msg).MinMaxInfo^ do begin if FWinMinMaxInfo.MinTrackWidth <> 0 then ptMinTrackSize.X := FWinMinMaxInfo.MinTrackWidth; if FWinMinMaxInfo.MinTrackHeight <> 0 then ptMinTrackSize.Y := FWinMinMaxInfo.MinTrackHeight; if FWinMinMaxInfo.MaxTrackWidth <> 0 then ptMaxTrackSize.X := FWinMinMaxInfo.MaxTrackWidth; if FWinMinMaxInfo.MaxTrackHeight <> 0 then ptMaxTrackSize.Y := FWinMinMaxInfo.MaxTrackHeight; if FWinMinMaxInfo.MaxSizeWidth <> 0 then ptMaxSize.X := FWinMinMaxInfo.MaxSizeWidth; if FWinMinMaxInfo.MaxSizeHeight <> 0 then ptMaxSize.Y := FWinMinMaxInfo.MaxSizeHeight; if FWinMinMaxInfo.MaxPosLeft <> 0 then ptMaxPosition.X := FWinMinMaxInfo.MaxPosLeft; if FWinMinMaxInfo.MaxPosTop <> 0 then ptMaxPosition.Y := FWinMinMaxInfo.MaxPosTop; end; end else begin TWMGetMinMaxInfo(Msg).MinMaxInfo^.ptMaxPosition.X := 0; TWMGetMinMaxInfo(Msg).MinMaxInfo^.ptMaxPosition.Y := 0; end; Msg.Result := 1; end; end; procedure TJvFormPlacement.FormShow(Sender: TObject); begin if Active then try RestoreFormPlacement; except Application.HandleException(Self); end; if Assigned(FSaveFormShow) then FSaveFormShow(Sender); end; procedure TJvFormPlacement.FormCloseQuery(Sender: TObject; var CanClose: Boolean); begin if Assigned(FSaveFormCloseQuery) then FSaveFormCloseQuery(Sender, CanClose); if CanClose and Active and (Owner is TCustomForm) and (Form.Handle <> 0) then try SaveFormPlacement; except Application.HandleException(Self); end; end; procedure TJvFormPlacement.FormDestroy(Sender: TObject); begin if Active and not FSaved then begin FDestroying := True; try SaveFormPlacement; except Application.HandleException(Self); end; FDestroying := False; end; if Assigned(FSaveFormDestroy) then FSaveFormDestroy(Sender); end; procedure TJvFormPlacement.UpdatePlacement; const {$IFDEF WIN32} Metrics: array [bsSingle..bsSizeToolWin] of Word = (SM_CXBORDER, SM_CXFRAME, SM_CXDLGFRAME, SM_CXBORDER, SM_CXFRAME); {$ELSE} Metrics: array [bsSingle..bsDialog] of Word = (SM_CXBORDER, SM_CXFRAME, SM_CXDLGFRAME); {$ENDIF} var Placement: TWindowPlacement; begin if (Owner <> nil) and (Owner is TCustomForm) and Form.HandleAllocated and not (csLoading in ComponentState) then if not (FPreventResize or CheckMinMaxInfo) then begin Placement.Length := SizeOf(TWindowPlacement); GetWindowPlacement(Form.Handle, @Placement); if not IsWindowVisible(Form.Handle) then Placement.ShowCmd := SW_HIDE; if Form.BorderStyle <> bsNone then begin Placement.ptMaxPosition.X := -GetSystemMetrics(Metrics[Form.BorderStyle]); Placement.ptMaxPosition.Y := -GetSystemMetrics(Metrics[Form.BorderStyle] + 1); end else Placement.ptMaxPosition := Point(0, 0); SetWindowPlacement(Form.Handle, @Placement); end; end; procedure TJvFormPlacement.UpdatePreventResize; var IsActive: Boolean; begin if not (csDesigning in ComponentState) and (Owner is TCustomForm) then begin if FPreventResize then FDefMaximize := (biMaximize in Form.BorderIcons); IsActive := Active; Active := False; try if (not FPreventResize) and FDefMaximize and (Form.BorderStyle <> bsDialog) then Form.BorderIcons := Form.BorderIcons + [biMaximize] else Form.BorderIcons := Form.BorderIcons - [biMaximize]; finally Active := IsActive; end; if not (csLoading in ComponentState) then CheckToggleHook; end; end; procedure TJvFormPlacement.SetPreventResize(Value: Boolean); begin if (Form <> nil) and (FPreventResize <> Value) then begin FPreventResize := Value; UpdatePlacement; UpdatePreventResize; end; end; function TJvFormPlacement.GetIniFile: TObject; begin {$IFDEF WIN32} if UseRegistry then Result := FRegIniFile else Result := FIniFile; {$ELSE} Result := FIniFile; {$ENDIF WIN32} end; function TJvFormPlacement.GetIniFileName: string; begin Result := FIniFileName; if (Result = '') and not (csDesigning in ComponentState) then begin {$IFDEF WIN32} if UseRegistry then Result := GetDefaultIniRegKey else Result := GetDefaultIniName; {$ELSE} Result := GetDefaultIniName; {$ENDIF} end; end; procedure TJvFormPlacement.SetIniFileName(const Value: string); begin FIniFileName := Value; end; function TJvFormPlacement.GetIniSection: string; begin Result := FIniSection; if (Result = '') and not (csDesigning in ComponentState) then Result := GetDefaultSection(Owner); end; procedure TJvFormPlacement.SetIniSection(const Value: string); begin FIniSection := Value; end; procedure TJvFormPlacement.Save; begin if Assigned(FOnSavePlacement) then FOnSavePlacement(Self); end; procedure TJvFormPlacement.Restore; begin if Assigned(FOnRestorePlacement) then FOnRestorePlacement(Self); end; procedure TJvFormPlacement.SavePlacement; begin if Owner is TCustomForm then begin {$IFDEF WIN32} if UseRegistry then begin if Options * [fpState, fpPosition] <> [] then begin WriteFormPlacementReg(Form, FRegIniFile, IniSection); FRegIniFile.WriteBool(IniSection, siVisible, FDestroying); end; if (fpActiveControl in Options) and (Form.ActiveControl <> nil) then FRegIniFile.WriteString(IniSection, siActiveCtrl, Form.ActiveControl.Name); end else begin if Options * [fpState, fpPosition] <> [] then begin WriteFormPlacement(Form, FIniFile, IniSection); FIniFile.WriteBool(IniSection, siVisible, FDestroying); end; if (fpActiveControl in Options) and (Form.ActiveControl <> nil) then FIniFile.WriteString(IniSection, siActiveCtrl, Form.ActiveControl.Name); end; {$ELSE} if Options * [fpState, fpPosition] <> [] then begin WriteFormPlacement(Form, FIniFile, IniSection); FIniFile.WriteBool(IniSection, siVisible, FDestroying); end; if (fpActiveControl in Options) and (Form.ActiveControl <> nil) then FIniFile.WriteString(IniSection, siActiveCtrl, Form.ActiveControl.Name); {$ENDIF} end; NotifyLinks(poSave); end; procedure TJvFormPlacement.RestorePlacement; begin if Owner is TCustomForm then begin {$IFDEF WIN32} if UseRegistry then ReadFormPlacementReg(Form, FRegIniFile, IniSection, fpState in Options, fpPosition in Options) else {$ENDIF} ReadFormPlacement(Form, FIniFile, IniSection, fpState in Options, fpPosition in Options); end; NotifyLinks(poRestore); end; procedure TJvFormPlacement.IniNeeded(ReadOnly: Boolean); begin if IniFileObject = nil then begin {$IFDEF WIN32} if UseRegistry then begin FRegIniFile := TRegIniFile.Create(IniFileName); {$IFDEF COMPILER5_UP} if ReadOnly then FRegIniFile.Access := KEY_READ; {$ENDIF} case FRegistryRoot of prLocalMachine: FRegIniFile.RootKey := HKEY_LOCAL_MACHINE; prClassesRoot: FRegIniFile.RootKey := HKEY_CLASSES_ROOT; prCurrentConfig: FRegIniFile.RootKey := HKEY_CURRENT_CONFIG; prUsers: FRegIniFile.RootKey := HKEY_USERS; prDynData: FRegIniFile.RootKey := HKEY_DYN_DATA; end; if FRegIniFile.RootKey <> HKEY_CURRENT_USER then FRegIniFile.OpenKey(FRegIniFile.FileName, not ReadOnly); end else {$ENDIF} FIniFile := TIniFile.Create(IniFileName); end; end; procedure TJvFormPlacement.IniFree; begin if IniFileObject <> nil then begin IniFileObject.Free; FIniFile := nil; {$IFDEF WIN32} FRegIniFile := nil; {$ENDIF} end; end; function TJvFormPlacement.DoReadString(const Section, Ident, Default: string): string; begin if IniFileObject <> nil then Result := IniReadString(IniFileObject, Section, Ident, Default) else begin IniNeeded(True); try Result := IniReadString(IniFileObject, Section, Ident, Default); finally IniFree; end; end; end; function TJvFormPlacement.ReadString(const Ident, Default: string): string; begin Result := DoReadString(IniSection, Ident, Default); end; procedure TJvFormPlacement.DoWriteString(const Section, Ident, Value: string); begin if IniFileObject <> nil then IniWriteString(IniFileObject, Section, Ident, Value) else begin IniNeeded(False); try IniWriteString(IniFileObject, Section, Ident, Value); finally IniFree; end; end; end; procedure TJvFormPlacement.WriteString(const Ident, Value: string); begin DoWriteString(IniSection, Ident, Value); end; function TJvFormPlacement.ReadInteger(const Ident: string; Default: Longint): Longint; begin if IniFileObject <> nil then Result := IniReadInteger(IniFileObject, IniSection, Ident, Default) else begin IniNeeded(True); try Result := IniReadInteger(IniFileObject, IniSection, Ident, Default); finally IniFree; end; end; end; procedure TJvFormPlacement.WriteInteger(const Ident: string; Value: Longint); begin if IniFileObject <> nil then IniWriteInteger(IniFileObject, IniSection, Ident, Value) else begin IniNeeded(False); try IniWriteInteger(IniFileObject, IniSection, Ident, Value); finally IniFree; end; end; end; procedure TJvFormPlacement.EraseSections; var Lines: TStrings; I: Integer; begin if IniFileObject = nil then begin IniNeeded(False); try Lines := TStringList.Create; try IniReadSections(IniFileObject, Lines); for I := 0 to Lines.Count - 1 do begin if (Lines[I] = IniSection) or (IsWild(Lines[I], IniSection + '.*', False) or IsWild(Lines[I], IniSection + '\*', False)) then IniEraseSection(IniFileObject, Lines[I]); end; finally Lines.Free; end; finally IniFree; end; end; end; procedure TJvFormPlacement.SaveFormPlacement; begin if FRestored or not Active then begin IniNeeded(False); try WriteInteger(siVersion, FVersion); SavePlacement; Save; FSaved := True; finally IniFree; end; end; end; procedure TJvFormPlacement.RestoreFormPlacement; var cActive: TComponent; begin FSaved := False; IniNeeded(True); try if ReadInteger(siVersion, 0) >= FVersion then begin RestorePlacement; FRestored := True; Restore; if (fpActiveControl in Options) and (Owner is TCustomForm) then begin cActive := Form.FindComponent(IniReadString(IniFileObject, IniSection, siActiveCtrl, '')); if (cActive <> nil) and (cActive is TWinControl) and TWinControl(cActive).CanFocus then Form.ActiveControl := TWinControl(cActive); end; end; FRestored := True; finally IniFree; end; UpdatePlacement; end; //=== TJvWinMinMaxInfo ======================================================= procedure TJvWinMinMaxInfo.Assign(Source: TPersistent); begin if Source is TJvWinMinMaxInfo then begin FMinMaxInfo := TJvWinMinMaxInfo(Source).FMinMaxInfo; if FOwner <> nil then FOwner.MinMaxInfoModified; end else inherited Assign(Source); end; function TJvWinMinMaxInfo.GetMinMaxInfo(Index: Integer): Integer; begin with FMinMaxInfo do begin case Index of 0: Result := ptMaxPosition.X; 1: Result := ptMaxPosition.Y; 2: Result := ptMaxSize.Y; 3: Result := ptMaxSize.X; 4: Result := ptMaxTrackSize.Y; 5: Result := ptMaxTrackSize.X; 6: Result := ptMinTrackSize.Y; 7: Result := ptMinTrackSize.X; else Result := 0; end; end; end; procedure TJvWinMinMaxInfo.SetMinMaxInfo(Index: Integer; Value: Integer); begin if GetMinMaxInfo(Index) <> Value then begin with FMinMaxInfo do begin case Index of 0: ptMaxPosition.X := Value; 1: ptMaxPosition.Y := Value; 2: ptMaxSize.Y := Value; 3: ptMaxSize.X := Value; 4: ptMaxTrackSize.Y := Value; 5: ptMaxTrackSize.X := Value; 6: ptMinTrackSize.Y := Value; 7: ptMinTrackSize.X := Value; end; end; if FOwner <> nil then FOwner.MinMaxInfoModified; end; end; function TJvWinMinMaxInfo.DefaultMinMaxInfo: Boolean; begin with FMinMaxInfo do begin Result := not ((ptMinTrackSize.X <> 0) or (ptMinTrackSize.Y <> 0) or (ptMaxTrackSize.X <> 0) or (ptMaxTrackSize.Y <> 0) or (ptMaxSize.X <> 0) or (ptMaxSize.Y <> 0) or (ptMaxPosition.X <> 0) or (ptMaxPosition.Y <> 0)); end; end; //=== TJvFormStorage ========================================================= constructor TJvFormStorage.Create(AOwner: TComponent); begin inherited Create(AOwner); FStoredProps := TStringList.Create; {$IFDEF COMPILER3_UP} FStoredValues := TJvStoredValues.Create{$IFDEF COMPILER4_UP}(Self){$ENDIF COMPILER4_UP}; FStoredValues.Storage := Self; {$ENDIF COMPILER3_UP} end; destructor TJvFormStorage.Destroy; begin FStoredProps.Free; FStoredProps := nil; {$IFDEF COMPILER3_UP} FStoredValues.Free; FStoredValues := nil; {$ENDIF COMPILER3_UP} inherited Destroy; end; {$IFDEF WIN32} procedure TJvFormStorage.SetNotification; var I: Integer; Component: TComponent; begin for I := FStoredProps.Count - 1 downto 0 do begin Component := TComponent(FStoredProps.Objects[I]); if Component <> nil then Component.FreeNotification(Self); end; end; {$ENDIF WIN32} procedure TJvFormStorage.SetStoredProps(Value: TStrings); begin FStoredProps.Assign(Value); {$IFDEF WIN32} SetNotification; {$ENDIF} end; {$IFDEF COMPILER3_UP} procedure TJvFormStorage.SeTJvStoredValues(Value: TJvStoredValues); begin FStoredValues.Assign(Value); end; function TJvFormStorage.GeTJvStoredValue(const Name: string): Variant; begin Result := StoredValues.StoredValue[Name]; end; procedure TJvFormStorage.SeTJvStoredValue(const Name: string; Value: Variant); begin StoredValues.StoredValue[Name] := Value; end; {$ENDIF COMPILER3_UP} procedure TJvFormStorage.Loaded; begin inherited Loaded; UpdateStoredList(Owner, FStoredProps, True); end; procedure TJvFormStorage.WriteState(Writer: TWriter); begin UpdateStoredList(Owner, FStoredProps, False); inherited WriteState(Writer); end; procedure TJvFormStorage.Notification(AComponent: TComponent; Operation: TOperation); var I: Integer; Component: TComponent; begin inherited Notification(AComponent, Operation); if not (csDestroying in ComponentState) and (Operation = opRemove) and (FStoredProps <> nil) then for I := FStoredProps.Count - 1 downto 0 do begin Component := TComponent(FStoredProps.Objects[I]); if Component = AComponent then FStoredProps.Delete(I); end; end; procedure TJvFormStorage.SaveProperties; begin with TJvPropsStorage.Create do try Section := IniSection; OnWriteString := DoWriteString; {$IFDEF WIN32} if UseRegistry then OnEraseSection := FRegIniFile.EraseSection else OnEraseSection := FIniFile.EraseSection; {$ELSE} OnEraseSection := FIniFile.EraseSection; {$ENDIF WIN32} StoreObjectsProps(Owner, FStoredProps); finally Free; end; end; procedure TJvFormStorage.RestoreProperties; begin with TJvPropsStorage.Create do try Section := IniSection; OnReadString := DoReadString; try LoadObjectsProps(Owner, FStoredProps); except { ignore any exceptions } end; finally Free; end; end; procedure TJvFormStorage.SavePlacement; begin inherited SavePlacement; SaveProperties; {$IFDEF COMPILER3_UP} StoredValues.SaveValues; {$ENDIF} end; procedure TJvFormStorage.RestorePlacement; begin inherited RestorePlacement; FRestored := True; RestoreProperties; {$IFDEF COMPILER3_UP} StoredValues.RestoreValues; {$ENDIF} end; //=== TJvIniLink ============================================================= destructor TJvIniLink.Destroy; begin FOnSave := nil; FOnLoad := nil; SetStorage(nil); inherited Destroy; end; function TJvIniLink.GetIniObject: TObject; begin if Assigned(FStorage) then Result := FStorage.IniFileObject else Result := nil; end; function TJvIniLink.GetRootSection: string; begin if Assigned(FStorage) then Result := FStorage.FIniSection else Result := ''; if Result <> '' then Result := Result + '\'; end; procedure TJvIniLink.SetStorage(Value: TJvFormPlacement); begin if FStorage <> Value then begin if FStorage <> nil then FStorage.RemoveLink(Self); if Value <> nil then Value.AddLink(Self); end; end; procedure TJvIniLink.SaveToIni; begin if Assigned(FOnSave) then FOnSave(Self); end; procedure TJvIniLink.LoadFromIni; begin if Assigned(FOnLoad) then FOnLoad(Self); end; //=== TJvStoredValue ========================================================= {$IFDEF COMPILER3_UP} constructor TJvStoredValue.Create(Collection: TCollection); begin inherited Create(Collection); FValue := Unassigned; end; procedure TJvStoredValue.Assign(Source: TPersistent); begin if (Source is TJvStoredValue) and (Source <> nil) then begin if VarIsEmpty(TJvStoredValue(Source).FValue) then Clear else Value := TJvStoredValue(Source).FValue; Name := TJvStoredValue(Source).Name; KeyString := TJvStoredValue(Source).KeyString; end; end; function TJvStoredValue.GetDisplayName: string; begin if FName = '' then Result := inherited GetDisplayName else Result := FName; end; procedure TJvStoredValue.SetDisplayName(const Value: string); begin if (Value <> '') and (AnsiCompareText(Value, FName) <> 0) and (Collection is TJvStoredValues) and (TJvStoredValues(Collection).IndexOf(Value) >= 0) then raise EJVCLException.Create(SDuplicateString); FName := Value; inherited; end; function TJvStoredValue.GeTJvStoredValues: TJvStoredValues; begin if Collection is TJvStoredValues then Result := TJvStoredValues(Collection) else Result := nil; end; procedure TJvStoredValue.Clear; begin FValue := Unassigned; end; function TJvStoredValue.IsValueStored: Boolean; begin Result := not VarIsEmpty(FValue); end; procedure TJvStoredValue.Save; var SaveValue: Variant; SaveStrValue: string; begin SaveValue := Value; if Assigned(FOnSave) then FOnSave(Self, SaveValue); SaveStrValue := VarToStr(SaveValue); if KeyString <> '' then SaveStrValue := XorEncode(KeyString, SaveStrValue); StoredValues.Storage.WriteString(Name, SaveStrValue); end; procedure TJvStoredValue.Restore; var RestoreValue: Variant; RestoreStrValue, DefaultStrValue: string; begin DefaultStrValue := VarToStr(Value); if KeyString <> '' then DefaultStrValue := XorEncode(KeyString, DefaultStrValue); RestoreStrValue := StoredValues.Storage.ReadString(Name, DefaultStrValue); if KeyString <> '' then RestoreStrValue := XorDecode(KeyString, RestoreStrValue); RestoreValue := RestoreStrValue; if Assigned(FOnRestore) then FOnRestore(Self, RestoreValue); Value := RestoreValue; end; //=== TJvStoredValues ======================================================== {$IFDEF COMPILER4_UP} constructor TJvStoredValues.Create(AOwner: TPersistent); begin inherited Create(AOwner, TJvStoredValue); end; {$ELSE} constructor TJvStoredValues.Create; begin inherited Create(TJvStoredValue); end; {$ENDIF} function TJvStoredValues.IndexOf(const Name: string): Integer; begin for Result := 0 to Count - 1 do if AnsiCompareText(Items[Result].Name, Name) = 0 then Exit; Result := -1; end; function TJvStoredValues.GetItem(Index: Integer): TJvStoredValue; begin Result := TJvStoredValue(inherited Items[Index]); end; procedure TJvStoredValues.SetItem(Index: Integer; StoredValue: TJvStoredValue); begin inherited SetItem(Index, TCollectionItem(StoredValue)); end; function TJvStoredValues.GeTJvStoredValue(const Name: string): Variant; var StoredValue: TJvStoredValue; begin StoredValue := GetValue(Name); if StoredValue = nil then Result := Null else Result := StoredValue.Value; end; procedure TJvStoredValues.SeTJvStoredValue(const Name: string; Value: Variant); var StoredValue: TJvStoredValue; begin StoredValue := GetValue(Name); if StoredValue = nil then begin StoredValue := TJvStoredValue(Add); StoredValue.Name := Name; StoredValue.Value := Value; end else StoredValue.Value := Value; end; function TJvStoredValues.GetValue(const Name: string): TJvStoredValue; var I: Integer; begin I := IndexOf(Name); if I < 0 then Result := nil else Result := Items[I]; end; procedure TJvStoredValues.SetValue(const Name: string; StoredValue: TJvStoredValue); var I: Integer; begin I := IndexOf(Name); if I >= 0 then Items[I].Assign(StoredValue); end; procedure TJvStoredValues.SaveValues; var I: Integer; begin for I := 0 to Count - 1 do Items[I].Save; end; procedure TJvStoredValues.RestoreValues; var I: Integer; begin for I := 0 to Count - 1 do Items[I].Restore; end; {$ENDIF COMPILER3_UP} end.