-
Notifications
You must be signed in to change notification settings - Fork 5
/
frmChooseExt_U.pas
285 lines (257 loc) · 8.57 KB
/
frmChooseExt_U.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
unit frmChooseExt_U;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, Vcl.ExtCtrls,
System.ImageList, Vcl.ImgList, System.Generics.Collections;
type
TfrmChooseExt = class(TForm)
ImageList: TImageList;
gbExtensions: TGroupBox;
edtExt: TLabeledEdit;
lbExtensions: TListBox;
lblExtensions: TLabel;
gbButtons: TGroupBox;
btnOK: TButton;
btnCancel: TButton;
procedure FormShow(Sender: TObject);
procedure FormClose(Sender: TObject; var Action: TCloseAction);
procedure lbExtensionsClick(Sender: TObject);
procedure lbExtensionsDrawItem(Control: TWinControl; Index: Integer;
Rect: TRect; State: TOwnerDrawState);
procedure edtExtChange(Sender: TObject);
procedure btnOKClick(Sender: TObject);
procedure FormDestroy(Sender: TObject);
private
{ Private declarations }
FlbChanging: Boolean;
function FindExtInList(const Ext: string): integer; inline;
public
{ Public declarations }
Extension: string;
StartWithExtensions: TList<string>;
constructor Create(AOwner: TComponent); override;
end;
var
frmChooseExt: TfrmChooseExt;
implementation
uses System.Win.Registry, WinAPI.CommCtrl, WinAPI.ShellAPI, System.Generics.Defaults;
{$R *.dfm}
const clbPairDelimiter: char = '/';
function MyCompareStr(const Left, Right: string): Integer;
begin
var vLeft := Left.Split(clbPairDelimiter)[0];
var vRight := Right.Split(clbPairDelimiter)[0];
Result := vLeft.Length - vRight.Length;
if Result = 0 then
Result := AnsiCompareStr(vLeft, vRight);
end;
function MyStringListSortCompare(List: TStringList; LeftIndex, RightIndex: Integer): Integer;
begin
Result := MyCompareStr(List[LeftIndex], List[RightIndex]);
end;
procedure TfrmChooseExt.btnOKClick(Sender: TObject);
begin
Extension := edtExt.Text;
end;
constructor TfrmChooseExt.Create(AOwner: TComponent);
begin
inherited;
Extension := '';
StartWithExtensions := TList<string>.Create;
lbExtensions.Items.NameValueSeparator := clbPairDelimiter;
end;
procedure TfrmChooseExt.edtExtChange(Sender: TObject);
begin
if FlbChanging or (Length(edtExt.Text) <= 0) then
Exit;
var vNewIndex := FindExtInList(edtExt.Text);
//SendMessageW(lbExtensions.Handle, LB_FINDSTRING, -1, NativeInt(PChar(edtExt.Text)));
if lbExtensions.ItemIndex <> vNewIndex then
begin
lbExtensions.ItemIndex := vNewIndex;
lbExtensions.Repaint;
end;
end;
function TfrmChooseExt.FindExtInList(const Ext: string): integer;
begin
Result := SendMessageW(lbExtensions.Handle, LB_FINDSTRING, -1, NativeInt(PChar(Ext)));
end;
procedure TfrmChooseExt.FormClose(Sender: TObject; var Action: TCloseAction);
begin
Extension := edtExt.Text;
StartWithExtensions.Clear;
end;
procedure TfrmChooseExt.FormDestroy(Sender: TObject);
begin
FreeAndNil(StartWithExtensions);
end;
procedure TfrmChooseExt.FormShow(Sender: TObject);
// following in the footsteps of the algorithm: http://www.mlsite.net/blog/?p=2250 with some improvements
procedure SyncToRight(const ALeft: TArray<string>; const ARight: TStringList);
begin
var vLeft: Integer := 0; var vRight: Integer := 0;
var vLeftCount := Length(ALeft); var vRightCount := ARight.Count;
while (vLeft < vLeftCount) or (vRight < vRightCount) do
begin
if vRight >= vRightCount then
begin
// If the target list is exhausted,
// delete the current element from the subject list
ARight.Add(ALeft[vLeft]);
Inc(vLeft);
end
else if vLeft >= vLeftCount then
begin
// O/w, if the subject list is exhausted,
// insert the current element from the target list
For var i := vRight to vRightCount - 1 do
ARight.Delete(vRight);
break;
end
else
begin
var vRes := MyCompareStr(ALeft[vLeft], ARight[vRight]);//AnsiCompareStr(ALeft[vLeft], ARight[vRight]);
if vRes > 0 then // Left > Right
begin
// O/w, if the current subject element precedes the current target element,
// delete the current subject element.
ARight.Add(ALeft[vLeft]); //We can't use ARight.Insert() because later we'll go throught this (we still need to compare upper value)
Inc(vLeft);
end
else if vRes < 0 then
begin
// O/w, if the current subject element follows the current target element,
// insert the current target element.
ARight.Delete(vRight);
Dec(vRightCount);
end
else
begin
// O/w the current elements match; consider the next pair
Inc(vLeft);
Inc(vRight);
end;
end;
end;
ARight.CustomSort(MyStringListSortCompare);
end;
begin
FlbChanging := True;
edtExt.Text := Extension;
lbExtensions.Items.BeginUpdate;
var reg: TRegistry := TRegistry.Create;
try
reg.rootkey := HKEY_CLASSES_ROOT;
if reg.OpenKey('', False) then
begin
try
var vReg: TArray<string>;
var vRegCount := 0;
var vRegInfo: TRegKeyInfo;
if reg.GetKeyInfo(vRegInfo) then
begin
SetLength(vReg, vRegInfo.NumSubKeys);
var vExtMaxLen: DWORD := vRegInfo.MaxSubKeyLen + 1;
var vExt: string;
SetString(vExt, nil, vExtMaxLen);
for var I := 0 to vRegInfo.NumSubKeys - 1 do
begin
var Len := vExtMaxLen;
RegEnumKeyEx(reg.CurrentKey, I, PChar(vExt), Len, nil, nil, nil, nil);
if(vExt[1] = '.') then
begin
vReg[vRegCount] := PChar(vExt.Substring(1).ToLower);
Inc(vRegCount);
end;
end;
SetLength(vReg, vRegCount);
TArray.Sort<string>(vReg, TComparer<string>.Construct(
function(const Left, Right: string): Integer
begin
Result := MyCompareStr(Left, Right);
end
)
);
end;
reg.CloseKey;
if lbExtensions.Items.Count = 0 then
begin
for var i := 0 to vRegCount - 1 do
begin
lbExtensions.Items.Add(vReg[i]);
end;
end
else // lbExtensions.Items.Count > 0
begin
var vLBStringList := TStringList.Create;
try
vLBStringList.Assign(lbExtensions.Items);
SyncToRight(vReg, vLBStringList);
lbExtensions.Items.Assign(vLBStringList);
finally
FreeAndNil(vLBStringList);
end;
end;
finally
FlbChanging := False;
end;
try
// if list is not empty Extension is not Empty and vise versa
for var s: string in StartWithExtensions do
if FindExtInList(s) > -1 then
begin
Extension := s;
break;
end;
edtExt.Text := Extension;
edtExtChange(edtExt);
finally
lbExtensions.Items.EndUpdate;
end;
end;
finally
reg.Free;
end;
edtExt.SetFocus;
end;
procedure TfrmChooseExt.lbExtensionsClick(Sender: TObject);
begin
if lbExtensions.ItemIndex >= 0 then
edtExt.Text := lbExtensions.Items[lbExtensions.ItemIndex].Split(lbExtensions.Items.NameValueSeparator)[0]; //lbExtensions.Items[lbExtensions.ItemIndex];
end;
procedure TfrmChooseExt.lbExtensionsDrawItem(Control: TWinControl;
Index: Integer; Rect: TRect; State: TOwnerDrawState);
begin
lbExtensions.Canvas.FillRect(Rect);
var vArText: TArray<string> := lbExtensions.Items[Index].Split(clbPairDelimiter);
var vText: string := vArText[0];
if Length(vArText) = 2 then
begin
var vIcon := TIcon.Create;
ImageList.GetIcon(vArText[1].ToInteger, vIcon);
DrawIconEx(lbExtensions.Canvas.Handle, Rect.Left + 1, Rect.Top + 1,
vIcon.Handle, 16, 16, 0, 0, DI_NORMAL);
vIcon.Free;
end
else
begin
var vInfo: TSHFileInfo;
if SHGetFileInfo(PChar('.' + vText), FILE_ATTRIBUTE_NORMAL, vInfo,
SizeOf(TSHFileInfo), SHGFI_ICON or SHGFI_SMALLICON or SHGFI_USEFILEATTRIBUTES) <> 0 then
begin
DrawIconEx(lbExtensions.Canvas.Handle, Rect.Left + 1, Rect.Top + 1,
vInfo.hIcon, 16, 16, 0, 0, DI_NORMAL);
var vImageListIndexNew := ImageList_ReplaceIcon(ImageList.Handle, -1, vInfo.hIcon);
DestroyIcon(vInfo.hIcon);
lbExtensions.Items[Index] := String.Join(lbExtensions.Items.NameValueSeparator,
[vText, vImageListIndexNew.ToString]);
end;
end;
var vTextRect := TRect.Create(Rect.Left + 18, Rect.Top, Rect.Right, Rect.Bottom);
DrawTextEx(lbExtensions.Canvas.Handle, PChar(vText), Length(vText), vTextRect,
DT_SINGLELINE or DT_VCENTER, nil);
if odFocused in State then // also check for styles if there's a possibility of using ..
lbExtensions.Canvas.DrawFocusRect(Rect);
end;
end.