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

Numbers v2 #33

Open
wants to merge 4 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
3 changes: 0 additions & 3 deletions NativeInterop.Win32.Xaml/DesktopWindow.cs
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using Windows.UI;
using Windows.Win32;
using Microsoft.UI;
using Microsoft.UI.Xaml;
using WinRT;

Expand Down
2 changes: 0 additions & 2 deletions NativeInterop.Win32/ParentProcessUtilities.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@
using System.Diagnostics;
using System.Runtime.InteropServices;
using System;
using System.Linq;
using System.Management;

namespace NativeInterop.Win32;

Expand Down
2 changes: 0 additions & 2 deletions NativeInterop.Win32/Pinvoke.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
using System;
using System.Drawing;
using System.Runtime.InteropServices;
using Windows.Win32.Foundation;
using Windows.Win32.Graphics.Gdi;
using Windows.Win32.UI.WindowsAndMessaging;

// ReSharper disable once CheckNamespace
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
using System.Collections;
using CsvHelper;
using CsvHelper.Configuration;
using System.Globalization;
using System.Reflection;
using CsvHelper;
using CsvHelper.Configuration;
using CsvHelper.TypeConversion;

namespace Numbers.Core.Services;
namespace Numbers.Core.Services.Implementations;

public class Csv : ICsv
{
Expand Down Expand Up @@ -49,6 +49,7 @@ public async Task SaveAsAsync(ICsvTable data, FileEncoding encoding, string file
Quote = encoding.QuoteCharacter,
BadDataFound = null,
TrimOptions = TrimOptions.Trim,
ShouldQuote = _ => encoding.QuoteCharacter != '\0'
};

using var writer = new StreamWriter(
Expand All @@ -67,23 +68,22 @@ await csv.WriteRecordsAsync(data.Rows.Select((r) => r.Cells) as IEnumerable)

public Task SaveAsAsync(IGlobalContext context)
{
if (context.FilePath is null)
{
throw new ArgumentException("file path in context must not be null", nameof(context));
}

var encoding = context.FileEncoding;
if (encoding is null)
{
throw new ArgumentException("encoding in context must not be null", nameof(context));
}

if (context.Table is null)
{
throw new ArgumentException("csv data in context must not be null", nameof(context));
}

return SaveAsAsync(context.Table, encoding.Value, context.FilePath);
var filePath =
context.FilePath
?? throw new ArgumentException(
"file path in context must not be null",
nameof(context)
);

var encoding =
context.FileEncoding
?? throw new ArgumentException("encoding in context must not be null", nameof(context));

var table =
context.Table
?? throw new ArgumentException("csv data in context must not be null", nameof(context));

return SaveAsAsync(table, encoding, filePath);
}

private async Task<(List<ICsvRow>, FileEncoding)> ReadRowsAsync(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
namespace Numbers.Core.Services;
namespace Numbers.Core.Services.Implementations;

public class CsvRow : ICsvRow
{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
using System.ComponentModel;
using System.Runtime.CompilerServices;

namespace Numbers.Core.Services;
namespace Numbers.Core.Services.Implementations;

class CsvRow100 : ICsvRowCells
{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
namespace Numbers.Core.Services;
namespace Numbers.Core.Services.Implementations;

class CsvTable : ICsvTable
{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
namespace Numbers.Core.Services;
namespace Numbers.Core.Services.Implementations;

public class DefaultTypeGenerator : ITypeGenerator
{
Expand Down
120 changes: 120 additions & 0 deletions Numbers.Core.Services.Implementations/ExcelImpl.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
using System.Globalization;
using Microsoft.Win32;
using Shared.Services;

namespace Numbers.Core.Services.Implementations;

public class ExcelImpl : IExcel
{
private readonly ILauncher _launcher;

public ExcelImpl(ILauncher launcher)
{
_launcher = launcher;
}

#pragma warning disable MA0051
private string? FindExcelPath()
#pragma warning restore MA0051
{
RegistryKey? excelKey = null;

try
{
// First, try to find the Excel path for Office 2019 and later versions.
excelKey = Registry.ClassesRoot.OpenSubKey("Excel.Application\\CurVer", false);
if (excelKey != null)
{
var version = excelKey
.GetValue("")
?.ToString()
?.Replace(".", "_", StringComparison.OrdinalIgnoreCase);
excelKey = Registry.ClassesRoot.OpenSubKey(
$"Excel.Application\\{version}\\InstallRoot",
false
);

if (TryValidateExcelPath(excelKey?.GetValue("Path"), out var path))
{
return path;
}
}

// Next, try for office 365
excelKey = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Microsoft\Office");
if (excelKey != null)
{
var officeVersionKeys = excelKey.GetSubKeyNames().Where((key) => float.TryParse(key, NumberStyles.Float, CultureInfo.InvariantCulture, out _)).ToArray();

foreach (string officeSubKey in officeVersionKeys)
{
var officeKey = excelKey.OpenSubKey(officeSubKey, false)!;

foreach (string subKeyName in officeKey.GetSubKeyNames())
{
if (subKeyName.StartsWith("Excel", StringComparison.OrdinalIgnoreCase))
{
var subKey = officeKey.OpenSubKey($"{subKeyName}\\InstallRoot", false);

if (TryValidateExcelPath(subKey?.GetValue("Path"), out var path))
{
return path;
}
}
}
}
}

// Next, try to find the Excel path for Office 2016 and earlier versions.
excelKey = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Microsoft\Office");
if (excelKey != null)
{
foreach (string subKeyName in excelKey.GetSubKeyNames())
{
if (subKeyName.StartsWith("Excel"))
{
var subKey = excelKey.OpenSubKey($"{subKeyName}\\InstallRoot", false);

if (TryValidateExcelPath(subKey?.GetValue("Path"), out var path))
{
return path;
}
}
}
}
}
catch (Exception)
{
return null;
}
finally
{
excelKey?.Close();
}

return null;

bool TryValidateExcelPath(object? officePath, out string fullExcelPath)
{
fullExcelPath = String.Empty;

if (officePath is not string strOfficePath || string.IsNullOrWhiteSpace(strOfficePath))
{
return false;
}

fullExcelPath = Path.Combine(strOfficePath, "Excel.exe");

return File.Exists(fullExcelPath);
}
}

public async Task OpenAsync(string filePath)
{
var excelPath = FindExcelPath();
if (excelPath != null)
{
await _launcher.LaunchWithAssociatedAppAsync(filePath, excelPath).ConfigureAwait(false);
}
}
}
45 changes: 45 additions & 0 deletions Numbers.Core.Services.Implementations/ExplorerShellImpl.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
using ShellLink;

namespace Numbers.Core.Services.Implementations;

public class ExplorerShellImpl : IExplorerShell
{
public IEnumerable<RecentlyUsedFile> GetRecentlyUsedFiles(params string[] extensionFilter)
{
var rufs = Directory.EnumerateFiles(
Environment.GetFolderPath(Environment.SpecialFolder.Recent)
);

return rufs.Where(
(f) =>
{
const string LINK_FILE_EXTENSION = ".lnk";
var cleanFileExtension = Path.GetExtension(
f.Substring(0, f.Length - LINK_FILE_EXTENSION.Length)
);

return extensionFilter.Length == 0
|| extensionFilter.Contains(
cleanFileExtension,
StringComparer.OrdinalIgnoreCase
);
}
)
.Select(GetLinkTarget)
.Where(File.Exists)
.Select(
(f) =>
new RecentlyUsedFile()
{
FileName = Path.GetFileName(f),
FullPath = f,
AccessTime = File.GetLastAccessTime(f),
}
);
}

public string GetLinkTarget(string pathToLnkFile)
{
return Shortcut.ReadFromFile(pathToLnkFile).LinkTargetIDList.Path;
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
namespace Numbers.Core.Services;
namespace Numbers.Core.Services.Implementations;

public class GlobalContext : IGlobalContext
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@
<RuntimeIdentifiers>win-x86;win-x64;win-arm64</RuntimeIdentifiers>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="CsvHelper" Version="28.0.1" />
<PackageReference Include="CsvHelper" Version="30.0.1" />
<PackageReference Include="securifybv.ShellLink" Version="0.1.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Numbers.Core\Numbers.Core.csproj" />
Expand Down
66 changes: 66 additions & 0 deletions Numbers.Core/ModelViews/RecentlyUsedFileViewModel.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
using System.Reactive;
using Numbers.Core.Services;
using ReactiveUI;
using Sextant;
using Shared.Misc;

namespace Numbers.Core.ModelViews;

public class RecentlyUsedFileViewModel : ReactiveObject, IViewModel
{
private readonly IParameterViewStackService _viewStackService;

public string Id { get; } = nameof(RecentlyUsedFileViewModel);

public RecentlyUsedFileViewModel(IParameterViewStackService viewStackService)
{
_viewStackService = viewStackService;

OpenDocument = ReactiveCommand.Create(Open);
_fileName = _path = _formatedAccessDate = String.Empty;
}

private void Open()
{
var navParams = new PreparationViewModel.NavigationParameter()
{
Arguments = new[] { Path },
};

_viewStackService
.PushPage<PreparationViewModel>(navParams.ToNavigationParameter())
.Subscribe();
}

public RecentlyUsedFileViewModel Set(RecentlyUsedFile file)
{
FileName = file.FileName;
Path = file.FullPath;
FormatedAccessDate = file.AccessTime.ToShortDateString();

return this;
}

string _fileName;
public string FileName
{
get { return _fileName; }
private set { this.RaiseAndSetIfChanged(ref _fileName, value); }
}

string _formatedAccessDate;
public string FormatedAccessDate
{
get { return _formatedAccessDate; }
private set { this.RaiseAndSetIfChanged(ref _formatedAccessDate, value); }
}

string _path;
public string Path
{
get { return _path; }
private set { this.RaiseAndSetIfChanged(ref _path, value); }
}

public ReactiveCommand<Unit, Unit> OpenDocument { get; }
}
21 changes: 8 additions & 13 deletions Numbers.Core/ModelViews/SaveViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -137,21 +137,16 @@ public IObservable<Unit> WhenNavigatedFrom(INavigationParameter parameter)

public IObservable<Unit> WhenNavigatingTo(INavigationParameter parameter)
{
var contextFileEncoding = _context.FileEncoding;
if (contextFileEncoding == null)
{
throw new Exception("Expected global context to have file encoding");
}
var contextFileEncoding =
_context.FileEncoding
?? throw new Exception("Expected global context to have file encoding");

var filePath = _context.FilePath;
if (filePath == null)
{
throw new Exception("Expected global context to have file path");
}
var filePath =
_context.FilePath ?? throw new Exception("Expected global context to have file path");

Delimiter = contextFileEncoding.Value.Delimiter;
QuoteCharacter = contextFileEncoding.Value.QuoteCharacter;
Encoding = contextFileEncoding.Value.Encoding;
Delimiter = contextFileEncoding.Delimiter;
QuoteCharacter = contextFileEncoding.QuoteCharacter;
Encoding = contextFileEncoding.Encoding;

FileName = Path.GetFileName(filePath);
FilePath = Path.GetDirectoryName(filePath) ?? "";
Expand Down
Loading