📄 shellctrls.pas
字号:
ParentFolder: IShellFolder;
begin
if Parent <> nil then
ParentFolder := ParentShellFolder
else
ParentFolder := DesktopShellFolder;
Result := GetDisplayName(ParentFolder, FPIDL, SHGDN_INFOLDER)
end;
function TShellFolder.Rename(const NewName: Widestring): boolean;
var
NewPIDL: PItemIDList;
begin
Result := False;
if not (fcCanRename in Capabilities) then Exit;
Result := ParentShellFolder.SetNameOf(
0,
FPIDL,
PWideChar(NewName),
SHGDN_NORMAL,
NewPIDL) = S_OK;
if Result then
begin
DisposePIDL(FPIDL);
DisposePIDL(FFullPIDL);
FPIDL := NewPIDL;
if (FParent <> nil) then
FFullPIDL := ConcatPIDLs(FParent.FPIDL, NewPIDL)
else
FFullPIDL := CopyPIDL(NewPIDL);
end
else
Raise Exception.Create(Format(SRenamedFailedError,[NewName]));
end;
function TShellFolder.ImageIndex(LargeIcon: Boolean): Integer;
begin
Result := GetShellImage(AbsoluteID, LargeIcon, False);
end;
function TShellFolder.ExecuteDefault: Integer;
var
SEI: TShellExecuteInfo;
begin
FillChar(SEI, SizeOf(SEI), 0);
with SEI do
begin
cbSize := SizeOf(SEI);
wnd := Application.Handle;
fMask := SEE_MASK_INVOKEIDLIST;
lpIDList := AbsoluteID;
nShow := SW_SHOW;
end;
Result := Integer(ShellExecuteEx(@SEI));
end;
{ TCustomShellChangeNotifier }
procedure TCustomShellChangeNotifier.Change;
function NotifyOptionFlags: DWORD;
begin
Result := 0;
if nfFileNameChange in FFilters then
Result := Result or FILE_NOTIFY_CHANGE_FILE_NAME;
if nfDirNameChange in FFilters then
Result := Result or FILE_NOTIFY_CHANGE_DIR_NAME;
if nfSizeChange in FFilters then
Result := Result or FILE_NOTIFY_CHANGE_SIZE;
if nfAttributeChange in FFilters then
Result := Result or FILE_NOTIFY_CHANGE_ATTRIBUTES;
if nfWriteChange in FFilters then
Result := Result or FILE_NOTIFY_CHANGE_LAST_WRITE;
if nfSecurityChange in FFilters then
Result := Result or FILE_NOTIFY_CHANGE_SECURITY;
end;
begin
if Assigned(FThread) then
begin
FThread.SetDirectoryOptions(Root, LongBool(FWatchSubTree),
NotifyOptionFlags);
end;
end;
constructor TCustomShellChangeNotifier.Create(AOwner : TComponent);
begin
inherited;
FRoot := 'C:\'; { Do not localize }
FWatchSubTree := True;
FFilters := [nfFilenameChange, nfDirNameChange];
Start;
end;
destructor TCustomShellChangeNotifier.Destroy;
var
Temp : TShellChangeThread;
begin
if Assigned(FThread) then
begin
Temp := FThread;
FThread := nil;
Temp.Terminate;
ReleaseMutex(Temp.FMutex);
end;
inherited;
end;
procedure TCustomShellChangeNotifier.SetRoot(const Value: TRoot);
begin
if not SameText(FRoot, Value) then
begin
FRoot := Value;
Change;
end;
end;
procedure TCustomShellChangeNotifier.SetFilters(const Value: TNotifyFilters);
begin
FFilters := Value;
Change;
end;
procedure TCustomShellChangeNotifier.SetOnChange(const Value: TThreadMethod);
begin
FOnChange := Value;
if Assigned(FThread) then
FThread.ChangeEvent := FOnChange
else
Start;
end;
procedure TCustomShellChangeNotifier.SetWatchSubTree(const Value: Boolean);
begin
FWatchSubTree := Value;
Change;
end;
procedure TCustomShellChangeNotifier.Start;
function NotifyOptionFlags: DWORD;
begin
Result := 0;
if nfFileNameChange in FFilters then
Result := Result or FILE_NOTIFY_CHANGE_FILE_NAME;
if nfDirNameChange in FFilters then
Result := Result or FILE_NOTIFY_CHANGE_DIR_NAME;
if nfSizeChange in FFilters then
Result := Result or FILE_NOTIFY_CHANGE_SIZE;
if nfAttributeChange in FFilters then
Result := Result or FILE_NOTIFY_CHANGE_ATTRIBUTES;
if nfWriteChange in FFilters then
Result := Result or FILE_NOTIFY_CHANGE_LAST_WRITE;
if nfSecurityChange in FFilters then
Result := Result or FILE_NOTIFY_CHANGE_SECURITY;
end;
begin
if Assigned(FOnChange) then
begin
FThread := TShellChangeThread.Create(FOnChange);
FThread.SetDirectoryOptions(FRoot,
LongBool(FWatchSubTree), NotifyOptionFlags);
FThread.Resume;
end;
end;
{ TShellChangeThread }
constructor TShellChangeThread.Create(ChangeEvent: TThreadMethod);
begin
FreeOnTerminate := True;
FChangeEvent := ChangeEvent;
FMutex := CreateMutex(nil, True, nil);
//Mutex is used to wake up the thread as it waits for any change notifications.
WaitForSingleObject(FMutex, INFINITE); //Grab the mutex.
FWaitChanged := false;
inherited Create(True);
end;
destructor TShellChangeThread.Destroy;
begin
if FWaitHandle <> ERROR_INVALID_HANDLE then
FindCloseChangeNotification(FWaitHandle);
CloseHandle(FMutex);
inherited Destroy;
end;
procedure TShellChangeThread.Execute;
var
Obj: DWORD;
Handles: array[0..1] of DWORD;
begin
EnterCriticalSection(CS);
FWaitHandle := FindFirstChangeNotification(PChar(FDirectory),
LongBool(FWatchSubTree), FNotifyOptionFlags);
LeaveCriticalSection(CS);
if FWaitHandle = ERROR_INVALID_HANDLE then Exit;
while not Terminated do
begin
Handles[0] := FWaitHandle;
Handles[1] := FMutex;
Obj := WaitForMultipleObjects(2, @Handles, False, INFINITE);
case Obj of
WAIT_OBJECT_0:
begin
Synchronize(FChangeEvent);
FindNextChangeNotification(FWaitHandle);
end;
WAIT_OBJECT_0 + 1:
ReleaseMutex(FMutex);
WAIT_FAILED:
Exit;
end;
EnterCriticalSection(CS);
if FWaitChanged then
begin
FWaitHandle := FindFirstChangeNotification(PChar(FDirectory),
LongBool(FWatchSubTree), FNotifyOptionFlags);
FWaitChanged := false;
end;
LeaveCriticalSection(CS);
end;
end;
procedure TShellChangeThread.SetDirectoryOptions(Directory: String;
WatchSubTree: Boolean; NotifyOptionFlags: DWORD);
begin
EnterCriticalSection(CS);
FDirectory := Directory;
FWatchSubTree := WatchSubTree;
FNotifyOptionFlags := NotifyOptionFlags;
// Release the current notification handle
FindCloseChangeNotification(FWaitHandle);
FWaitChanged := true;
LeaveCriticalSection(CS);
end;
{ TCustomShellTreeView }
constructor TCustomShellTreeView.Create(AOwner: TComponent);
var
FileInfo: TSHFileInfo;
begin
inherited Create(AOwner);
FRootFolder := nil;
ShowRoot := False;
FObjectTypes := [otFolders];
RightClickSelect := True;
FAutoContext := True;
//! OnDeletion := NodeDeleted;
FUpdating := False;
FComboBox := nil;
FListView := nil;
FImageListChanging := False;
FUseShellImages := True;
FImages := SHGetFileInfo('C:\', { Do not localize }
0, FileInfo, SizeOf(FileInfo), SHGFI_SYSICONINDEX or SHGFI_SMALLICON);
FNotifier := TShellChangeNotifier.Create(Self);
FNotifier.FComponentStyle := FNotifier.FComponentStyle + [ csSubComponent ];
FRoot := SRFDesktop;
FLoadingRoot := False;
end;
procedure TCustomShellTreeView.ClearItems;
var
I: Integer;
begin
Items.BeginUpdate;
try
for I := 0 to Items.Count-1 do
begin
if Assigned(Folders[i]) then
Folders[I].Free;
Items[I].Data := nil;
end;
Items.Clear;
finally
Items.EndUpdate;
end;
end;
procedure TCustomShellTreeView.CreateWnd;
begin
inherited CreateWnd;
if (Items.Count > 0) then
ClearItems;
if not Assigned(Images) then SetUseShellImages(FUseShellImages);
{ TODO : What is the Items.Count test for here? }
if (not FLoadingRoot) {and (Items.Count = 0)} then
CreateRoot;
end;
procedure TCustomShellTreeView.DestroyWnd;
begin
ClearItems;
inherited DestroyWnd;
end;
procedure TCustomShellTreeView.CommandCompleted(Verb: String;
Succeeded: Boolean);
var
Fldr : TShellFolder;
begin
if not Succeeded then Exit;
if Assigned(Selected) then
begin
if SameText(Verb, SCmdVerbDelete) then
begin
Fldr := TShellFolder(Selected.Data);
if not FileExists(Fldr.PathName) then
begin
Selected.Data := nil;
Selected.Delete;
FreeAndNil(Fldr);
end;
end
else if SameText(Verb, SCmdVerbPaste) then
Refresh(Selected)
else if SameText(Verb, SCmdVerbOpen) then
SetCurrentDirectory(PChar(FSavePath));
end;
end;
procedure TCustomShellTreeView.ExecuteCommand(Verb: String;
var Handled: Boolean);
var
szPath: array[0..MAX_PATH] of char;
begin
if SameText(Verb, SCmdVerbRename) and Assigned(Selected) then
begin
Selected.EditText;
Handled := True;
end
else if SameText(Verb, SCmdVerbOpen) then
begin
GetCurrentDirectory(MAX_PATH, szPath);
FSavePath := StrPas(szPath);
StrPCopy(szPath, ExtractFilePath(TShellFolder(Selected.Data).PathName));
SetCurrentDirectory(szPath);
end;
end;
function TreeSortFunc(Node1, Node2: TTreeNode; lParam: Integer): Integer; stdcall;
begin
Result := SmallInt(TShellFolder(Node1.Data).ParentShellFolder.CompareIDs(
0, TShellFolder(Node1.Data).RelativeID, TShellFolder(Node2.Data).RelativeID));
end;
procedure TCustomShellTreeView.InitNode(NewNode: TTreeNode; ID: PItemIDList; ParentNode: TTreeNode);
var
CanAdd: Boolean;
NewFolder: IShellFolder;
AFolder: TShellFolder;
begin
AFolder := TShellFolder(ParentNode.Data);
NewFolder := GetIShellFolder(AFolder.ShellFolder, ID);
NewNode.Data := TShellFolder.Create(AFolder, ID, NewFolder);
with TShellFolder(NewNode.Data) do
begin
NewNode.Text := DisplayName;
if FUseShellImages and not Assigned(Images) then
begin
NewNode.ImageIndex := GetShellImage(AbsoluteID, False, False);
NewNode.SelectedIndex := GetShellImage(AbsoluteID, False, True);
end;
if NewNode.SelectedIndex = 0 then NewNode.SelectedIndex := NewNode.ImageIndex;
NewNode.HasChildren := SubFolders;
if fpShared in Properties then NewNode.OverlayIndex := 0;
if (otNonFolders in ObjectTypes) and (ShellFolder <> nil) then
NewNode.HasChildren := GetHasSubItems(ShellFolder, ObjectFlags(FObjectTypes));
end;
CanAdd := True;
if Assigned(FOnAddFolder) then FOnAddFolder(Self, TShellFolder(NewNode.Data), CanAdd);
if not CanAdd then
NewNode.Delete;
end;
procedure TCustomShellTreeView.PopulateNode(Node: TTreeNode);
var
ID: PItemIDList;
EnumList: IEnumIDList;
NewNode: TTreeNode;
NumIDs: LongWord;
SaveCursor: TCursor;
HR: HResult;
begin
SaveCursor := Screen.Cursor;
Screen.Cursor := crHourglass;
Items.BeginUpdate;
try
try
HR := TShellFolder(Node.Data).ShellFolder.EnumObjects(Application.Handle,
ObjectFlags(FObjectTypes),
EnumList);
if HR <> 0 then Exit;
except on E:Exception do end;
while EnumList.Next(1, ID, NumIDs) = S_OK do
begin
NewNode := Items.AddChild(Node, '');
InitNode(NewNode, ID, Node);
end;
Node.CustomSort(@TreeSortFunc, 0);
finally
Items.EndUpdate;
Screen.Cursor := SaveCursor;
end;
end;
procedure TCustomShellTreeView.SetObjectTypes(Value: TShellObjectTypes);
begin
FObjectTypes := Value;
RootChanged;
end;
procedure TCustomShellTreeView.CreateRoot;
var
RootNode: TTreeNode;
ErrorMsg: string;
begin
if (csLoading in ComponentState) then Exit;
try
FRootFolder := CreateRootFolder(FRootFolder, FOldRoot, FRoot);
ErrorMsg := '';
except
on E : Exception do ErrorMsg := E.Message;
end;
if Assigned(FRootFolder) then
begin
FLoadingRoot := true;
try
if Items.Count > 0 then
ClearItems;
RootNode := Items.Add(nil, '');
with RootNode do
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -