-
Notifications
You must be signed in to change notification settings - Fork 5
/
Copy pathValueNamesProvider.pas
434 lines (364 loc) · 14.1 KB
/
ValueNamesProvider.pas
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
{-----------------------------------------------------------------------------
Unit Name: ValueNamesProvider
This software and source code are distributed on an as is basis, without
warranty of any kind, either express or implied.
This file can be redistributed, modified if you keep this header.
Copyright © Erwien Saputra 2005 All Rights Reserved.
Author: Erwien Saputra
Purpose:
IValueNamesProvider is an observer of the IDelphiSettingRegistry. Whenever
it reads new names from the registry keys, it triggers OnValueNamesChanged and
OnStringChanged.
This class is intended to be used with a TListBox, the form is responsible to
arrange the names. If the user select an item on the listbox, the TListBox
should set the SelectedIndex. Whenever the SelectedIndex is changed, it will
trigger OnStringChanged.
The main purpose for this class is as provider for the names under a key and
to manage registry value names manipulation.
The form that displays the ListBox which uses IValueNameProvider as data source
displays two Listboxes and synchronizes the value names. The form may inserts
blank lines between the ListBox items. For that reason ListBox.ItemIndex cannot
be used directly with the IValueNamesProvider.SelectedItemIndex.
Currently, this interface does not support multi-select items.
The ListBox.ItemIndex
may not be used directly to assign
History:
02/01/05 - Initial creation.
02/17/05 - Implemented ClearSelectedValue. Updated with a lot of comments.
02/19/05 - Update CopySelectedValue, it calls UpdateValue if the Name is
already exist.
-----------------------------------------------------------------------------}
unit ValueNamesProvider;
interface
uses
DelphiSettingRegistry, Classes;
type
TIntfNotifyEvent = procedure (const Intf : IInterface) of object;
IValueNamesProvider = interface
['{098F1A23-0169-4812-B00E-BD6FB2F77306}']
function GetRelativePath : string;
function GetSelectedIndex : integer;
function GetValueNames : TStrings;
function GetAsString : string;
function GetOnValueNamesChanged : TIntfNotifyEvent;
function GetOnAsStringChanged : TIntfNotifyEvent;
procedure SetSelectedIndex (const Idx : integer);
procedure SetOnValueNamesChanged (const Value : TIntfNotifyEvent);
procedure SetOnAsStringChanged (const Value : TIntfNotifyEvent);
function GetNameIndex (const AName : string): integer;
function GetSettingPath : string;
procedure SetSettingPath (const ASettingPath : string);
procedure CopySelectedValue (const Source : IValueNamesProvider);
procedure DeleteSelectedValue;
procedure ClearSelectedValue;
property RelativePath : string read GetRelativePath;
property ValueNames : TStrings read GetValueNames;
property AsString : string read GetAsString;
property SelectedIndex : integer read GetSelectedIndex write SetSelectedIndex;
property OnValueNamesChanged : TIntfNotifyEvent read GetOnValueNamesChanged
write SetOnValueNamesChanged;
property OnAsStringChanged : TIntfNotifyEvent read GetOnAsStringChanged
write SetOnAsStringChanged;
end;
function GetValueNamesProvider (const Intf : IDelphiSettingRegistry) : IValueNamesProvider;
implementation
uses
SysUtils, Registry, IntfObserver;
type
TValueNamesProvider = class (TInterfacedObject, IValueNamesProvider,
IObserver)
private
FOnValueNamesChanged : TIntfNotifyEvent;
FOnAsStringChanged : TIntfNotifyEvent;
FSelectedIndex : integer;
FValueNames : TStringList;
FValue : string;
FReg : TRegistry;
FSettingPath : string;
procedure LoadValueNames;
procedure ClearValueNames;
procedure SetSelectedIndex (const Idx : integer);
procedure UpdateValue;
protected
function GetRelativePath : string;
function GetSelectedIndex : integer;
function GetValueNames : TStrings;
function GetAsString : string;
function GetOnValueNamesChanged : TIntfNotifyEvent;
function GetOnAsStringChanged : TIntfNotifyEvent;
procedure SetOnValueNamesChanged (const Value : TIntfNotifyEvent);
procedure SetOnAsStringChanged (const Value : TIntfNotifyEvent);
function GetNameIndex (const AName : string): integer;
function GetSettingPath : string;
procedure SetSettingPath (const ASettingPath : string);
procedure CopySelectedValue (const Source : IValueNamesProvider);
procedure DeleteSelectedValue;
procedure ClearSelectedValue;
procedure Update (const Subject : ISubject; const AIntf : IInterface = nil);
public
constructor Create;
destructor Destroy; override;
end;
//Factory function.
function GetValueNamesProvider (const Intf : IDelphiSettingRegistry): IValueNamesProvider;
begin
Result := TValueNamesProvider.Create;
Result.SetSettingPath (Intf.SettingPath);
(Intf as ISubject).AttachObserver (Result as IObserver);
end;
{ TValueNamesProvider }
//Returns the index of the selected value name.
function TValueNamesProvider.GetSelectedIndex: integer;
begin
Result := self.FSelectedIndex;
end;
constructor TValueNamesProvider.Create;
begin
inherited Create;
FSelectedIndex := -1;
FValueNames := TStringList.Create;
FValueNames.Sorted := true;
FReg := TRegistry.Create;
end;
//Event handler setter.
procedure TValueNamesProvider.SetOnAsStringChanged(
const Value: TIntfNotifyEvent);
begin
FOnAsStringChanged := Value;
end;
//Retrieve all value names.
function TValueNamesProvider.GetValueNames: TStrings;
begin
Result := FValueNames;
end;
//Event handler getter.
function TValueNamesProvider.GetOnValueNamesChanged: TIntfNotifyEvent;
begin
Result := FOnValueNamesChanged;
end;
//Event handler setter.
procedure TValueNamesProvider.SetOnValueNamesChanged(
const Value: TIntfNotifyEvent);
begin
FOnValueNamesChanged := Value;
end;
//Returns the value of the selected registry value name.
function TValueNamesProvider.GetAsString: string;
begin
Result := FValue;
end;
//Event handler getter.
function TValueNamesProvider.GetOnAsStringChanged: TIntfNotifyEvent;
begin
Result := FOnAsStringChanged;
end;
//Returns the relative path of the current Delph Registry Setting. This function
//should not be here, it is more appopriate to have it at the
//IDelphiSettingRegistry. Unfortunately it has to be here for now, as this class
//needs this information and this class does not have reference to the
//IDelphiSettingRegistry.
function TValueNamesProvider.GetRelativePath: string;
begin
//Returns the path after the setting path. Setting Path is string from
//'\Software' to the current setting name.
Result := Copy (FReg.CurrentPath, Length (self.FSettingPath) + 1,
Length (FReg.CurrentPath));
end;
destructor TValueNamesProvider.Destroy;
begin
FValueNames.Free;
inherited;
end;
//Returns the internal list index for AName.
function TValueNamesProvider.GetNameIndex(const AName: string): integer;
begin
Result := FValueNames.IndexOf (AName);
end;
//Set the currently selected index.
procedure TValueNamesProvider.SetSelectedIndex(const Idx: integer);
begin
if (FSelectedIndex = Idx) then
Exit;
FSelectedIndex := Idx;
UpdateValue;
end;
//Reads the string representatino of the selected value name.
procedure TValueNamesProvider.UpdateValue;
var
DataType : TRegDataType;
ValueName : string;
ValueExist : boolean;
begin
ValueExist := (FSelectedIndex <> -1) and
FReg.ValueExists (FValueNames [FSelectedIndex]);
if ValueExist then begin
ValueName := FValueNames [FSelectedIndex];
DataType := FReg.GetDataType (ValueName);
//Read the value and stored it in FValue. If the key is a binary key, set
//FValue as {Binary}.
case DataType of
rdString,
rdExpandString : FValue := FReg.ReadString (ValueName);
rdInteger : FValue := IntToStr (FReg.ReadInteger (ValueName));
rdBinary : FValue := '(Binary)';
else
FValue := '(Unknown)';
end;
end
else
FValue := '';
if Assigned (FOnAsStringChanged) = true then
FOnAsStringChanged (self);
end;
//This method loads the FValueNames with the value names of the selected
//selected registry key. FReg is alredy opened the 'current key'.
procedure TValueNamesProvider.LoadValueNames;
begin
//Reset the selected index and load the value names for the current key.
FSelectedIndex := -1;
FReg.GetValueNames (FValueNames);
if Assigned (FOnValueNamesChanged) then
FOnValueNamesChanged (self);
//Update the FValue. UpdateValue will trigger FOnAsStringChanged.
UpdateValue;
end;
//Clear the value names.
procedure TValueNamesProvider.ClearValueNames;
begin
//Clear the value names and reset the selected index.
FValueNames.Clear;
FSelectedIndex := -1;
if Assigned (FOnValueNamesChanged) then
FOnValueNamesChanged (self);
//UpdateValue will clear the FValue by the virtue that FSelectedIndex is -1,
//and trigger the FOnAsStringChanged.
UpdateValue;
end;
//This method is called by the Subject, IDelphiSettingRegistry.
//This method reads the current registry key from IDelphiSettingRegistry, and
//then opens the same key with the internal FReg.
procedure TValueNamesProvider.Update(const Subject: ISubject;
const AIntf: IInterface);
var
Intf : IDelphiSettingRegistry;
begin
Intf := Subject as IDelphiSettingRegistry;
if (FReg.CurrentPath = Intf.CurrentPath) then
Exit;
//FReg should be able to open the current path, as both points to the same
//registry key. If for some reason this key cannot be opened, reset the FReg
//to open the root key and clear the value names.
if (FReg.OpenKey (Intf.CurrentPath, false)) = false then begin
FReg.OpenKey('\', false);
ClearValueNames;
end
else
//The key is found and was opened. Load the value names and notify the form.
LoadValueNames;
end;
//This method sets the setting path. Setting path is the path from '\Software'
//to the delphi setting registry name. This information is required to get the
//relative path.
procedure TValueNamesProvider.SetSettingPath(const ASettingPath: string);
begin
FSettingPath := ASettingPath;
end;
//Using other IValueNamesProvider as source, copy the selected value. This is
//where the GetRelativePath is needed. If the key from the source exist in this
//class, this method will copy the value.
procedure TValueNamesProvider.CopySelectedValue(
const Source: IValueNamesProvider);
var
ValueName, Path : string;
Reg : TRegistry;
Buffer : pointer;
BytesRead,
DataSize : integer;
ValueExist : boolean;
begin
if Source = nil then
Exit;
//The relative path of the source is different than the path of this object,
//that means the source path does not exist. Create it first.
if Source.RelativePath <> self.GetRelativePath then
raise Exception.Create ('Relative paths must be identical.');
//Get the name of the selected key from the source provider and checks if the
//same value name exist in this IValueNamesProvider. ValueExist determines
//whether this class neeed to reload the value names or not.
ValueName := Source.ValueNames [Source.SelectedIndex];
ValueExist := FValueNames.IndexOf (ValueName) > -1;
//Get the full path of the source key.
Path := IncludeTrailingBackslash (Source.GetSettingPath) +
Source.RelativePath;
//Use Reg to read the source and FReg will write the value.
Reg := TRegistry.Create;
try
Reg.OpenKeyReadOnly (Path);
case Reg.GetDataType (ValueName) of
rdString : FReg.WriteString (ValueName, Reg.ReadString (ValueName));
rdExpandString : FReg.WriteExpandString (ValueName,
Reg.ReadString (ValueName));
rdInteger : FReg.WriteInteger (ValueName,
Reg.ReadInteger (ValueName));
rdBinary : begin
dataSize := Reg.GetDataSize (ValueName);
GetMem (Buffer, DataSize);
try
BytesRead := Reg.ReadBinaryData (ValueName,
Buffer^, dataSize);
FReg.WriteBinaryData (ValueName, Buffer^, BytesRead);
finally
FreeMem (Buffer, dataSize);
end;
end;
end;
finally
Reg.Free;
end;
if ValueExist = false then begin
//Load the value names, as the value did not exist before.
LoadValueNames;
//The SelectedIndex was -1, set it to points the newly created value.
SetSelectedIndex (FValueNames.IndexOf (ValueName));
end
else
//The value name, it exist but the value has been modified. The form must
//update itself.
UpdateValue;
end;
//Getter for the setting path.
function TValueNamesProvider.GetSettingPath: string;
begin
Result := FSettingPath;
end;
//This method delete the selected value.
procedure TValueNamesProvider.DeleteSelectedValue;
begin
if FSelectedIndex = -1 then
Exit;
if FReg.ValueExists (FValueNames [FSelectedIndex]) then
FReg.DeleteValue (FValueNames [FSelectedIndex]);
//Reload the value names and notify the view.
LoadValueNames;
end;
//This method clears the value of a value name. The value name itself will not
//bd deleted.
procedure TValueNamesProvider.ClearSelectedValue;
var
ValueName: string;
begin
if FSelectedIndex = -1 then
Exit;
if FReg.ValueExists (FValueNames [FSelectedIndex]) then begin
ValueName := FValueNames [FSelectedIndex];
case FReg.GetDataType (ValueName) of
rdString,
rdExpandString : FReg.WriteExpandString (ValueName, EmptyStr);
rdInteger : FReg.WriteInteger (ValueName, 0);
rdBinary : FReg.WriteBinaryData (ValueName, Ptr (0)^, 0);
end;
//The value for the selected valuename has been changed, notify the view.
UpdateValue;
end;
end;
end.