Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Code Quality: Remove ini-parser-netstandard dependency #15654

Open
wants to merge 18 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions src/Files.App/Data/Contracts/IWindowsIniService.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// Copyright (c) 2024 Files Community
// Licensed under the MIT License. See the LICENSE.

namespace Files.App.Data.Contracts
{
/// <summary>
/// Provides service to retrieve value from an INI file.
/// </summary>
public interface IWindowsIniService
{
/// <summary>
/// Gets parsed data of INI file of a folder with each section.
/// </summary>
/// <remarks>
/// While there's Win32API GetPrivateProfileString, this API can only used for single key.
/// <br/>
/// For more information about INI format, visit <a href="https://en.wikipedia.org/wiki/INI_file"/>.
/// </remarks>
/// <param name="filePath">The INI file to look up.</param>
/// <param name="dataItem">The data class to hold the INI data.</param>
/// <returns>Returns true if succeeded; otherwise, false.</returns>
List<IniSectionDataItem> GetData(string filePath);
}
}
17 changes: 17 additions & 0 deletions src/Files.App/Data/Items/IniSectionDataItem.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// Copyright (c) 2024 Files Community
// Licensed under the MIT License. See the LICENSE.

using System.Collections.Immutable;

namespace Files.App.Data.Items
{
/// <summary>
/// Represents item of ini file's section.
/// </summary>
public class IniSectionDataItem
{
public string SectionName { get; set; } = "";

public IDictionary<string, string> Parameters { get; set; } = ImmutableDictionary<string, string>.Empty;
}
}
1 change: 0 additions & 1 deletion src/Files.App/Files.App.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,6 @@
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.2.2" />
<PackageReference Include="DiscUtils.Udf" Version="0.16.13" />
<PackageReference Include="FluentFTP" Version="43.0.1" />
<PackageReference Include="ini-parser-netstandard" Version="2.5.2" />
<PackageReference Include="LibGit2Sharp" Version="0.30.0" />
<PackageReference Include="MessageFormat" Version="7.1.0" />
<PackageReference Include="Microsoft.Data.Sqlite.Core" Version="8.0.6" />
Expand Down
1 change: 1 addition & 0 deletions src/Files.App/Helpers/Application/AppLifecycleHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,7 @@ public static IHost ConfigureHost()
.AddSingleton<ITagsContext, TagsContext>()
.AddSingleton<ISidebarContext, SidebarContext>()
// Services
.AddSingleton<IWindowsIniService, WindowsIniService>()
.AddSingleton<IAppThemeModeService, AppThemeModeService>()
.AddSingleton<IDialogService, DialogService>()
.AddSingleton<ICommonDialogService, CommonDialogService>()
Expand Down
46 changes: 12 additions & 34 deletions src/Files.App/Helpers/Layout/AdaptiveLayoutHelpers.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,28 +3,24 @@

using Files.App.ViewModels.Previews;
using Files.Shared.Helpers;
using IniParser.Model;
using Windows.Storage;
using static Files.App.Constants.AdaptiveLayout;
using IO = System.IO;

namespace Files.App.Helpers
{
public static class AdaptiveLayoutHelpers
{
private static IFoldersSettingsService FoldersSettingsService { get; } = Ioc.Default.GetRequiredService<IFoldersSettingsService>();
private static ILayoutSettingsService LayoutSettingsService { get; } = Ioc.Default.GetRequiredService<ILayoutSettingsService>();
private static IContentPageContext ContentPageContext { get; } = Ioc.Default.GetRequiredService<IContentPageContext>();

public static void ApplyAdaptativeLayout(LayoutPreferencesManager folderSettings, string path, IList<ListedItem> filesAndFolders)
public static void ApplyAdaptativeLayout(LayoutPreferencesManager folderSettings, IList<ListedItem> filesAndFolders)
{
if (LayoutSettingsService.SyncFolderPreferencesAcrossDirectories)
return;
if (string.IsNullOrWhiteSpace(path))
return;
if (folderSettings.IsLayoutModeFixed || !folderSettings.IsAdaptiveLayoutEnabled)
return;

var layout = GetAdaptiveLayout(path, filesAndFolders);
var layout = GetAdaptiveLayout(filesAndFolders);
switch (layout)
{
case Layouts.Detail:
Expand All @@ -36,49 +32,31 @@ public static void ApplyAdaptativeLayout(LayoutPreferencesManager folderSettings
}
}

private static Layouts GetAdaptiveLayout(string path, IList<ListedItem> filesAndFolders)
private static Layouts GetAdaptiveLayout(IList<ListedItem> filesAndFolders)
{
var pathLayout = GetPathLayout(path);
var pathLayout = GetPathLayout();
if (pathLayout is not Layouts.None)
return pathLayout;

return GetContentLayout(filesAndFolders);
}

private static Layouts GetPathLayout(string path)
private static Layouts GetPathLayout()
{
var iniPath = IO.Path.Combine(path, "desktop.ini");

var iniContents = Win32Helper.ReadStringFromFile(iniPath)?.Trim();
if (string.IsNullOrEmpty(iniContents))
return Layouts.None;

var parser = new IniParser.Parser.IniDataParser();
parser.Configuration.ThrowExceptionsOnError = false;
var data = parser.Parse(iniContents);
if (data is null)
return Layouts.None;
var desktopIni = ContentPageContext.ShellPage!.ShellViewModel.DesktopIni;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this change is appropriate, because this method is to get information for a given path, not for the current context.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually this string path is always WorkindDirectory.
I removed this argument from all callers.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I still think we should use the path parameter here, because I don't think static helper methods should be context dependent.
Even if we make this context dependent, ShellPage should be null-safe.


var viewModeSection = data.Sections.FirstOrDefault(IsViewState);
if (viewModeSection is null)
return Layouts.None;
var viewStateSection = desktopIni.FirstOrDefault(x => x.SectionName == "ViewState");
if (viewStateSection is null)
return Layouts.Detail;

var folderTypeKey = viewModeSection.Keys.FirstOrDefault(IsFolderType);
if (folderTypeKey is null)
return Layouts.None;
var viewMode = viewStateSection.Parameters.FirstOrDefault(x => x.Key == "Mode").Value;

return folderTypeKey.Value switch
return viewMode switch
{
"Pictures" => Layouts.Grid,
"Videos" => Layouts.Grid,
_ => Layouts.Detail,
};

static bool IsViewState(SectionData data)
=> "ViewState".Equals(data.SectionName, StringComparison.OrdinalIgnoreCase);

static bool IsFolderType(KeyData data)
=> "FolderType".Equals(data.KeyName, StringComparison.OrdinalIgnoreCase);
}

private static Layouts GetContentLayout(IList<ListedItem> filesAndFolders)
Expand Down
75 changes: 75 additions & 0 deletions src/Files.App/Services/Windows/WindowsIniService.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
// Copyright (c) 2024 Files Community
// Licensed under the MIT License. See the LICENSE.

namespace Files.App.Services
{
/// <inheritdoc cref="IWindowsIniService"/>
public sealed class WindowsIniService : IWindowsIniService
{
/// <inheritdoc/>
public List<IniSectionDataItem> GetData(string filePath)
{
var iniPath = SystemIO.Path.Combine(filePath);
if (!SystemIO.File.Exists(iniPath))
return [];

var lines = Enumerable.Empty<string>().ToList();

try
{
lines = SystemIO.File.ReadLines(iniPath)
.Where(line => !line.StartsWith(';') && !string.IsNullOrEmpty(line))
.ToList();
}
catch (UnauthorizedAccessException)
{
return [];
}

// Get sections
var sections = lines
.Where(line => line.StartsWith('[') && line.EndsWith(']'));

// Get section line indexes
List<int> sectionLineIndexes = [];
foreach (var section in sections)
sectionLineIndexes.Add(lines.IndexOf(section));

List<IniSectionDataItem> dataItems = [];

for (int index = 0; index < sectionLineIndexes.Count; index++)
{
var sectionIndex = sectionLineIndexes[index];

var count =
index + 1 == sectionLineIndexes.Count
? (lines.Count - 1) - sectionIndex
: sectionLineIndexes[index + 1] - sectionIndex;

if (count == 0)
continue;

var range = lines.GetRange(sectionIndex + 1, count);

var sectionName = lines[sectionLineIndexes[index]].TrimStart('[').TrimEnd(']');

// Read data
var parameters = range
// Split the lines into key and value
.Select(line => line.Split('='))
// Validate
.Where(parts => parts.Length == 2)
// Gather as dictionary
.ToDictionary(parts => parts[0].Trim(), parts => parts[1].Trim());

dataItems.Add(new()
{
SectionName = sectionName,
Parameters = parameters,
});
}

return dataItems;
}
}
}
54 changes: 34 additions & 20 deletions src/Files.App/ViewModels/ShellViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ public sealed class ShellViewModel : ObservableObject, IDisposable

// Files and folders list for manipulating
private ConcurrentCollection<ListedItem> filesAndFolders;
private readonly IWindowsIniService WindowsIniService = Ioc.Default.GetRequiredService<IWindowsIniService>();
private readonly IWindowsJumpListService jumpListService = Ioc.Default.GetRequiredService<IWindowsJumpListService>();
private readonly IDialogService dialogService = Ioc.Default.GetRequiredService<IDialogService>();
private IUserSettingsService UserSettingsService { get; } = Ioc.Default.GetRequiredService<IUserSettingsService>();
Expand Down Expand Up @@ -154,6 +155,8 @@ public GitProperties EnabledGitProperties

public bool IsValidGitDirectory { get; private set; }

public List<IniSectionDataItem> DesktopIni { get; private set; }

private StorageFolderWithPath? currentStorageFolder;
private StorageFolderWithPath workingRoot;

Expand Down Expand Up @@ -1443,7 +1446,7 @@ private async Task RapidAddItemsToCollectionAsync(string path, string? previousD
ItemLoadStatusChanged?.Invoke(this, new ItemLoadStatusChangedEventArgs() { Status = ItemLoadStatusChangedEventArgs.ItemLoadStatus.Complete, PreviousDirectory = previousDir, Path = path });
IsLoadingItems = false;

AdaptiveLayoutHelpers.ApplyAdaptativeLayout(folderSettings, WorkingDirectory, filesAndFolders.ToList());
AdaptiveLayoutHelpers.ApplyAdaptativeLayout(folderSettings, filesAndFolders.ToList());
}
finally
{
Expand Down Expand Up @@ -1736,6 +1739,7 @@ private async Task<int> EnumerateItemsFromStandardFolderAsync(string path, Cance
await OrderFilesAndFoldersAsync();
await ApplyFilesAndFoldersChangesAsync();
await dispatcherQueue.EnqueueOrInvokeAsync(CheckForSolutionFile, Microsoft.UI.Dispatching.DispatcherQueuePriority.Low);
await dispatcherQueue.EnqueueOrInvokeAsync(GetDesktopIniFileData, Microsoft.UI.Dispatching.DispatcherQueuePriority.Low);
await dispatcherQueue.EnqueueOrInvokeAsync(CheckForBackgroundImage, Microsoft.UI.Dispatching.DispatcherQueuePriority.Low);
});

Expand Down Expand Up @@ -1797,51 +1801,61 @@ private void CheckForSolutionFile()
.FirstOrDefault()?.ItemPath;
}

private void GetDesktopIniFileData()
{
var path = Path.Combine(WorkingDirectory, "desktop.ini");
DesktopIni = WindowsIniService.GetData(path);
}

public void CheckForBackgroundImage()
{
var iniPath = Path.Combine(WorkingDirectory, "desktop.ini");
if (!File.Exists(iniPath))
var filesAppSection = DesktopIni.FirstOrDefault(x => x.SectionName == "FilesApp");
yaira2 marked this conversation as resolved.
Show resolved Hide resolved
if (filesAppSection is null || folderSettings.LayoutMode is FolderLayoutModes.ColumnView)
{
FolderBackgroundImageSource = null;
return;
yaira2 marked this conversation as resolved.
Show resolved Hide resolved

// Read data
var lines = File.ReadLines(iniPath);
var keys = lines.Select(line => line.Split('='))
.Where(parts => parts.Length == 2)
.ToDictionary(parts => parts[0].Trim(), parts => parts[1].Trim());
}

// Image source
if (folderSettings.LayoutMode is not FolderLayoutModes.ColumnView && keys.TryGetValue("Files_BackgroundImage", out var backgroundImage))
var backgroundImage = filesAppSection.Parameters.FirstOrDefault(x => x.Key == "Files_BackgroundImage").Value;
if (string.IsNullOrEmpty(backgroundImage))
{
FolderBackgroundImageSource = null;
return;
}
else
{
FolderBackgroundImageSource = new BitmapImage
{
UriSource = new Uri(backgroundImage, UriKind.RelativeOrAbsolute),
CreateOptions = BitmapCreateOptions.IgnoreImageCache
};
else
{
FolderBackgroundImageSource = null;
return;
}

// Opacity
if (keys.TryGetValue("Files_BackgroundOpacity", out var backgroundOpacity) && float.TryParse(backgroundOpacity, out var opacity))
var backgroundOpacity = filesAppSection.Parameters.FirstOrDefault(x => x.Key == "Files_BackgroundOpacity").Value;
if (float.TryParse(backgroundOpacity, out var opacity))
FolderBackgroundImageOpacity = opacity;
else
FolderBackgroundImageOpacity = 1f;

// Stretch
if (keys.TryGetValue("Files_BackgroundFit", out var backgroundFit) && Enum.TryParse<Stretch>(backgroundFit, out var fit))
var backgroundFit = filesAppSection.Parameters.FirstOrDefault(x => x.Key == "Files_BackgroundFit").Value;
if (Enum.TryParse<Stretch>(backgroundFit, out var fit))
FolderBackgroundImageFit = fit;
else
FolderBackgroundImageFit = Stretch.UniformToFill;

// VerticalAlignment
if (keys.TryGetValue("Files_BackgroundVerticalAlignment", out var verticalAlignment) && Enum.TryParse<VerticalAlignment>(verticalAlignment, out var vAlignment))
// Vertical alignment
var verticalAlignment = filesAppSection.Parameters.FirstOrDefault(x => x.Key == "Files_BackgroundVerticalAlignment").Value;
if (Enum.TryParse<VerticalAlignment>(verticalAlignment, out var vAlignment))
FolderBackgroundImageVerticalAlignment = vAlignment;
else
FolderBackgroundImageVerticalAlignment = VerticalAlignment.Center;

// HorizontalAlignment
if (keys.TryGetValue("Files_BackgroundHorizontalAlignment", out var horizontalAlignment) && Enum.TryParse<HorizontalAlignment>(horizontalAlignment, out var hAlignment))
// Horizontal alignment
var horizontalAlignment = filesAppSection.Parameters.FirstOrDefault(x => x.Key == "Files_BackgroundHorizontalAlignment").Value;
if (Enum.TryParse<HorizontalAlignment>(horizontalAlignment, out var hAlignment))
FolderBackgroundImageHorizontalAlignment = hAlignment;
else
FolderBackgroundImageHorizontalAlignment = HorizontalAlignment.Center;
Expand Down
2 changes: 1 addition & 1 deletion src/Files.App/Views/Shells/ModernShellPage.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ protected override void FolderSettings_LayoutPreferencesUpdateRequired(object se

LayoutPreferencesManager.SetLayoutPreferencesForPath(ShellViewModel.WorkingDirectory, e.LayoutPreference);
if (e.IsAdaptiveLayoutUpdateRequired)
AdaptiveLayoutHelpers.ApplyAdaptativeLayout(InstanceViewModel.FolderSettings, ShellViewModel.WorkingDirectory, ShellViewModel.FilesAndFolders.ToList());
AdaptiveLayoutHelpers.ApplyAdaptativeLayout(InstanceViewModel.FolderSettings, ShellViewModel.FilesAndFolders.ToList());
}

protected override void OnNavigatedTo(NavigationEventArgs eventArgs)
Expand Down
Loading