Skip to content

Commit

Permalink
Update
Browse files Browse the repository at this point in the history
  • Loading branch information
0x5bfa committed Sep 16, 2024
1 parent 77afe0b commit 0d0a836
Show file tree
Hide file tree
Showing 5 changed files with 75 additions and 56 deletions.
2 changes: 2 additions & 0 deletions src/Files.App.CsWin32/NativeMethods.txt
Original file line number Diff line number Diff line change
Expand Up @@ -138,3 +138,5 @@ SHARD
BHID_EnumItems
FOLDERID_RecycleBinFolder
CoTaskMemFree
SHGetIDListFromObject
SHCreateItemFromIDList
5 changes: 0 additions & 5 deletions src/Files.App/Data/Contracts/IWindowsRecentItemsService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -54,10 +54,5 @@ public interface IWindowsRecentItemsService
/// Clears recent files and folders of File Explorer.
/// </summary>
bool Clear();

/// <summary>
/// Checks multiple visibility settings of recent items that can be configured in Windows Settings or Group Policy, in Windows Registry.
/// </summary>
bool CheckIsRecentItemsEnabled();
}
}
27 changes: 21 additions & 6 deletions src/Files.App/Data/Items/WidgetRecentItem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,21 @@
// Licensed under the MIT License. See the LICENSE.

using Microsoft.UI.Xaml.Media.Imaging;
using Windows.Win32;
using Windows.Win32.UI.Shell.Common;

namespace Files.App.Data.Items
{
/// <summary>
/// Represents an item for recent item of File Explorer on Windows.
/// </summary>
public sealed class RecentItem : WidgetCardItem, IEquatable<RecentItem>
public sealed class RecentItem : WidgetCardItem, IEquatable<RecentItem>, IDisposable
{
private BitmapImage _Icon;
private BitmapImage? _Icon;
/// <summary>
/// Gets or sets thumbnail icon of the recent item.
/// </summary>
public BitmapImage Icon
public BitmapImage? Icon
{
get => _Icon;
set => SetProperty(ref _Icon, value);
Expand All @@ -23,12 +25,20 @@ public BitmapImage Icon
/// <summary>
/// Gets or sets name of the recent item.
/// </summary>
public string Name { get; set; }
public required string Name { get; set; }

/// <summary>
/// Gets or sets target path of the recent item.
/// </summary>
public override string Path { get; set; }
public override required string Path { get; set; }

/// <summary>
/// Gets or initializes PIDL of the recent item.
/// </summary>
/// <remarks>
/// This has to be removed in the future.
/// </remarks>
public unsafe required ITEMIDLIST* PIDL { get; init; }

/// <summary>
/// Loads thumbnail icon of the recent item.
Expand All @@ -45,6 +55,11 @@ public async Task LoadRecentItemIconAsync()

public override int GetHashCode() => (Path, Name).GetHashCode();
public override bool Equals(object? other) => other is RecentItem item && Equals(item);
public bool Equals(RecentItem other) => other.Name == Name && other.Path == Path;
public bool Equals(RecentItem? other) => other is not null && other.Name == Name && other.Path == Path;

public unsafe void Dispose()
{
PInvoke.CoTaskMemFree(PIDL);
}
}
}
77 changes: 33 additions & 44 deletions src/Files.App/Services/Windows/WindowsRecentItemsService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@
// Licensed under the MIT License. See the LICENSE.

using Microsoft.Extensions.Logging;
using Microsoft.Win32;
using System.Collections.Specialized;
using Windows.Win32;
using Windows.Win32.Foundation;
using Windows.Win32.System.Com;
using Windows.Win32.UI.Shell;

namespace Files.App.Services
Expand Down Expand Up @@ -108,60 +108,48 @@ public unsafe bool Add(string path)

/// <inheritdoc/>
public unsafe bool Remove(RecentItem item)
{
return false; // TODO: Use ContextMenu class to invoke "delete" verb
}

/// <inheritdoc/>
public unsafe bool Clear()
{
try
{
PInvoke.SHAddToRecentDocs((uint)SHARD.SHARD_PIDL, null);
// Initialize IFileOperation instance
using ComPtr<IFileOperation> pFileOperation = default;
var fileOperationIid = typeof(IFileOperation).GUID;
var fileOperationInstanceIid = typeof(FileOperation).GUID;
HRESULT hr = PInvoke.CoCreateInstance(&fileOperationInstanceIid, null, CLSCTX.CLSCTX_LOCAL_SERVER, &fileOperationIid, (void**)pFileOperation.GetAddressOf());
hr = pFileOperation.Get()->SetOperationFlags(FILEOPERATION_FLAGS.FOF_NO_UI);
hr = pFileOperation.Get()->SetOwnerWindow(new(MainWindow.Instance.WindowHandle));

// Get IShellItem from PIDL
var shellItemIid = typeof(IShellItem).GUID;
ComPtr<IShellItem> pShellItem = default;
PInvoke.SHCreateItemFromIDList(item.PIDL, &shellItemIid, (void**)pShellItem.GetAddressOf());

// Perform deletion
hr = pFileOperation.Get()->DeleteItem(pShellItem.Get(), null);
hr = pFileOperation.Get()->PerformOperations();

return true;
}
catch (Exception ex)
catch
{
App.Logger.LogWarning(ex, ex.Message);
return false;
}
}

/// <inheritdoc/>
public bool CheckIsRecentItemsEnabled()
public unsafe bool Clear()
{
using var explorerSubKey = Registry.CurrentUser.OpenSubKey(@"SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer");
using var advSubkey = Registry.CurrentUser.OpenSubKey(@"SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Advanced");
using var userPolicySubkey = Registry.CurrentUser.OpenSubKey(@"SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\Explorer");
using var sysPolicySubkey = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\Explorer");

if (explorerSubKey is not null)
try
{
// File Explorer settings (default: 1)
bool showRecentValue = Convert.ToBoolean(explorerSubKey.GetValue("ShowRecent", true));
if (!showRecentValue)
return false;
}
PInvoke.SHAddToRecentDocs((uint)SHARD.SHARD_PIDL, null);

if (advSubkey is not null)
{
// Windows Settings (default: 1)
bool startTrackDocsValue = Convert.ToBoolean(advSubkey.GetValue("Start_TrackDocs", true));
if (!startTrackDocsValue)
return false;
return true;
}

// Group Policy settings (default: 0)
var policySubkey = userPolicySubkey ?? sysPolicySubkey;
if (policySubkey is not null)
catch (Exception ex)
{
bool noRecentDocsHistoryValue = Convert.ToBoolean(policySubkey.GetValue("NoRecentDocsHistory", false));
if (noRecentDocsHistoryValue)
return false;
App.Logger.LogWarning(ex, ex.Message);
return false;
}

return true;
}

private unsafe bool UpdateRecentItems(bool isFolder)
Expand All @@ -172,8 +160,8 @@ private unsafe bool UpdateRecentItems(bool isFolder)

string szFolderShellPath =
isFolder
? "Shell:::{22877a6d-37a1-461a-91b0-dbda5aaebc99}" // Recent Places folder
: "Shell:::{679f85cb-0220-4080-b29b-5540cc05aab6}"; // Quick Access folder
? "Shell:::{22877A6D-37A1-461A-91B0-DBDA5AAEBC99}" // Recent Places folder (recent folders)
: "Shell:::{679F85CB-0220-4080-B29B-5540CC05AAB6}"; // Quick Access folder (recent files)

// Get IShellItem of the shell folder
var shellItemIid = typeof(IShellItem).GUID;
Expand All @@ -200,12 +188,12 @@ private unsafe bool UpdateRecentItems(bool isFolder)
// Get the target path
pShellItem.Get()->GetDisplayName(SIGDN.SIGDN_DESKTOPABSOLUTEEDITING, out var szDisplayName);
var targetPath = szDisplayName.ToString();
PInvoke.CoTaskMemFree((void*)szDisplayName.Value);
PInvoke.CoTaskMemFree(szDisplayName.Value);

// Get the display name
pShellItem.Get()->GetDisplayName(SIGDN.SIGDN_NORMALDISPLAY, out szDisplayName);
var fileName = szDisplayName.ToString();
PInvoke.CoTaskMemFree((void*)szDisplayName.Value);
PInvoke.CoTaskMemFree(szDisplayName.Value);

// Strip the file extension except when the file name only contains extension (e.g. ".gitignore")
if (!FoldersSettingsService.ShowFileExtensions)
Expand All @@ -214,12 +202,13 @@ private unsafe bool UpdateRecentItems(bool isFolder)
fileName = string.IsNullOrEmpty(strippedExtension) ? SystemIO.Path.GetFileName(fileName) : strippedExtension;
}

// TODO: Get PIDL to prepare for removal of the item via "delete" verb for now
PInvoke.SHGetIDListFromObject((IUnknown*)pShellItem.Get(), out var pidl);

recentItems.Add(new()
{
Path = targetPath,
Name = fileName,
PIDL = pidl,
});

index++;
Expand All @@ -238,15 +227,15 @@ private unsafe bool UpdateRecentItems(bool isFolder)
{
_RecentFolders.Clear();
_RecentFolders.AddRange(recentItems);
}
}
}
else
{
lock (_RecentFiles)
{
_RecentFiles.Clear();
_RecentFiles.AddRange(recentItems);
}
}
}

var eventArgs = GetChangedActionEventArgs(snapshot, recentItems);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

using Microsoft.Extensions.Logging;
using Microsoft.UI.Xaml.Controls;
using Microsoft.Win32;
using System.Collections.Specialized;
using System.IO;
using Windows.Foundation.Metadata;
Expand Down Expand Up @@ -66,10 +67,11 @@ public RecentFilesWidgetViewModel()

public async Task RefreshWidgetAsync()
{
IsRecentFilesDisabledInWindows = !WindowsRecentItemsService.CheckIsRecentItemsEnabled();
IsRecentFilesDisabledInWindows = !CheckIsRecentItemsEnabled();
await WindowsRecentItemsService.UpdateRecentFilesAsync();
}


public override List<ContextMenuFlyoutItemViewModel> GetItemMenuItems(WidgetCardItem item, bool isPinned, bool isFolder = false)
{
return new List<ContextMenuFlyoutItemViewModel>()
Expand Down Expand Up @@ -232,6 +234,22 @@ public void NavigateToPath(string path)
catch (Exception) { }
}

public bool CheckIsRecentItemsEnabled()
{
using var explorerSubKey = Registry.CurrentUser.OpenSubKey(@"SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer");
using var advSubkey = Registry.CurrentUser.OpenSubKey(@"SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Advanced");
using var userPolicySubkey = Registry.CurrentUser.OpenSubKey(@"SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\Explorer");
using var sysPolicySubkey = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\Explorer");
var policySubkey = userPolicySubkey ?? sysPolicySubkey;

if (Convert.ToBoolean(explorerSubKey?.GetValue("ShowRecent", true)) &&
Convert.ToBoolean(advSubkey?.GetValue("Start_TrackDocs", true)) &&
!Convert.ToBoolean(policySubkey?.GetValue("NoRecentDocsHistory", false)))
return true;

return false;
}

// Event methods

private async void Manager_RecentFilesChanged(object? sender, NotifyCollectionChangedEventArgs e)
Expand Down

0 comments on commit 0d0a836

Please sign in to comment.