34 Commits

Author SHA1 Message Date
mbabienco 03d2cf30d0 Bump to 1.4.3 2026-04-13 19:19:22 +09:00
mbabienco 0d07bc468c Add OS/comp arch to debug log 2026-04-13 19:19:03 +09:00
mbabienco 80bc33b85e Simplify expression 2026-04-13 19:13:04 +09:00
mbabienco 3482ed7336 Improve watermark for date display 2026-04-13 19:10:33 +09:00
mbabienco 5b0508721f Fix year-month-day ordering 2026-04-13 19:05:54 +09:00
mbabienco 69292b6410 Fix comment version number 2026-04-09 22:37:34 +09:00
mbabienco 4d722c0220 Downgrade Magick.NET for x64 computers 2026-04-09 22:18:01 +09:00
mbabienco 6fa58d3a62 On macOS x64, use x64 Magick.NET package 2026-04-09 16:12:39 +09:00
mbabienco 15038f5feb Bump avalonia version to 11.3.13 2026-04-09 15:45:15 +09:00
mbabienco 3b776e2534 Show all inner exceptions 2026-04-09 15:24:06 +09:00
mbabienco ec99595303 Use report date setting and tweak default 2026-04-08 22:35:07 +09:00
mbabienco 5fd61f1f84 Misc UI tweaks/fixes 2026-04-08 22:29:05 +09:00
mbabienco 23e8b2533a Update DateFormatConverter 2026-04-08 22:18:12 +09:00
mbabienco 4d89e49c96 Use multibinding for receipt date format 2026-04-08 22:16:56 +09:00
mbabienco 58c59dfc0a Fix bad namespace 2026-04-08 22:16:38 +09:00
mbabienco 867f57dcff Bind parent to UserControl, not grid 2026-04-08 21:51:29 +09:00
mbabienco 72abb6cbb3 Use custom date format for CalendarDatePicker 2026-04-08 21:48:36 +09:00
mbabienco 62307e09a1 Add binding member for data grid date format 2026-04-08 21:48:10 +09:00
mbabienco f39b643b00 Add date format pickers (settings not used yet) 2026-04-08 16:09:20 +09:00
mbabienco d90cd1354f Bump version 2026-03-13 08:18:32 +09:00
mbabienco f4dd498d22 Shortcut to view settings dir 2026-03-13 08:14:50 +09:00
mbabienco cd71df8a8e Add yyyy.MM.dd to acceptable formats 2026-03-13 08:10:11 +09:00
mbabienco de621fe9dc Add another file name date parsing format
Also redid code so more formats would be easy
2026-03-13 08:05:11 +09:00
mbabienco a1858443e8 Bump version number 2026-03-12 21:17:27 +09:00
mbabienco f34a7092e4 Resize PDF images too (Docnet rendering) 2026-03-12 21:09:34 +09:00
mbabienco aed85c7555 Use page size and pg items to calc max image size 2026-03-12 20:58:20 +09:00
mbabienco 25f739667d Allow resolving Noto Sans font 2026-03-12 20:56:38 +09:00
mbabienco 1fae3f5341 Redo image resizing
Hack fix guesswork sizing
2026-03-12 20:21:41 +09:00
mbabienco 5dda88521d Add black border around images/pdfs
Makes viewing things with no border/white border much easier.
2026-03-12 19:34:56 +09:00
mbabienco 742ecb148a Simply if statement 2026-03-12 19:07:25 +09:00
mbabienco 4ad4a0852f Bump Magick.NET for vulnerability 2026-03-12 19:02:17 +09:00
mbabienco c44093c5ee Fix wrong About menu title 2026-03-04 21:10:21 +09:00
mbabienco 71f4fda2d4 Remove unneeded TODO comment 2026-03-03 12:54:53 +09:00
mbabienco 478f67aa82 Convert remaining files to file-scoped namespace 2026-03-03 12:54:27 +09:00
26 changed files with 630 additions and 247 deletions
+1 -1
View File
@@ -3,7 +3,7 @@
; Non-commercial use only ; Non-commercial use only
#define MyAppName "MayShow" #define MyAppName "MayShow"
#define MyAppVersion "1.4.0" #define MyAppVersion "1.4.3"
#define MyAppPublisher "Quickity Quack Productions" #define MyAppPublisher "Quickity Quack Productions"
#define MyAppExeName "MayShow.exe" #define MyAppExeName "MayShow.exe"
+1 -1
View File
@@ -1,6 +1,6 @@
#!/bin/bash #!/bin/bash
VERSION="1.4.0" VERSION="1.4.3"
SRC_DIR="src" # user ran script from main folder SRC_DIR="src" # user ran script from main folder
if [ ! -d "$SRC_DIR" ]; then if [ ! -d "$SRC_DIR" ]; then
SRC_DIR= "../src" # try SRC_DIR= "../src" # try
+1 -1
View File
@@ -131,7 +131,7 @@
</Application.Resources> </Application.Resources>
<NativeMenu.Menu> <NativeMenu.Menu>
<NativeMenu> <NativeMenu>
<NativeMenuItem Header="About ReceiptBuilder" Click="AboutOnClick" /> <NativeMenuItem Header="About MayShow" Click="AboutOnClick" />
</NativeMenu> </NativeMenu>
</NativeMenu.Menu> </NativeMenu.Menu>
</Application> </Application>
+1 -1
View File
@@ -1,5 +1,5 @@
<Project> <Project>
<PropertyGroup> <PropertyGroup>
<AvaloniaVersion>11.3.12</AvaloniaVersion> <AvaloniaVersion>11.3.13</AvaloniaVersion>
</PropertyGroup> </PropertyGroup>
</Project> </Project>
+4 -6
View File
@@ -4,16 +4,14 @@ using System.ComponentModel;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Text; using System.Text;
namespace MayShow.Helpers namespace MayShow.Helpers;
{
// https://docs.microsoft.com/en-us/dotnet/api/system.componentmodel.inotifypropertychanged?view=netframework-4.7.2 // https://docs.microsoft.com/en-us/dotnet/api/system.componentmodel.inotifypropertychanged?view=netframework-4.7.2
class ChangeNotifier : INotifyPropertyChanged class ChangeNotifier : INotifyPropertyChanged
{ {
public event PropertyChangedEventHandler? PropertyChanged; public event PropertyChangedEventHandler? PropertyChanged;
protected void NotifyPropertyChanged([CallerMemberName] string propertyName = "") protected void NotifyPropertyChanged([CallerMemberName] string propertyName = "")
{ {
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
} }
}
} }
+20 -1
View File
@@ -1,15 +1,34 @@
using System.Collections.Generic;
using MayShow.Models;
namespace MayShow.Helpers; namespace MayShow.Helpers;
class Constants class Constants
{ {
public static string AppVersion = "1.4.0"; public static string AppVersion = "1.4.3";
public static string[] AllowedFileExtensionPatterns = [ "*.png", "*.jpg", "*.jpeg", "*.gif", "*.bmp", "*.webp", "*.pdf", "*.heic", ]; public static string[] AllowedFileExtensionPatterns = [ "*.png", "*.jpg", "*.jpeg", "*.gif", "*.bmp", "*.webp", "*.pdf", "*.heic", ];
public static string[] AllowedFileExtensionsNoStar = [ "png", "jpg", "jpeg", "gif", "bmp", "webp", "pdf", "heic", ]; public static string[] AllowedFileExtensionsNoStar = [ "png", "jpg", "jpeg", "gif", "bmp", "webp", "pdf", "heic", ];
public static string ReportSavedDataFileName = "report_data.json"; public static string ReportSavedDataFileName = "report_data.json";
public static List<DateDisplayFormat> GetDateDisplayFormats()
{
return [
new DateDisplayFormat("Month/Day/Year", "4/5/2026", "M/d/yyyy"),
new DateDisplayFormat("Year-Month-Day", "2026-04-05", "yyyy-MM-dd"),
new DateDisplayFormat("Month Day, Year", "April 5, 2026", "MMMM d, yyyy"),
new DateDisplayFormat("DOW, Month Day, Year", "Sunday, April 5, 2026", "dddd, MMMM d, yyyy"),
new DateDisplayFormat("Abbreviated-Month Day, Year", "Apr 5, 2026", "MMM d, yyyy"),
new DateDisplayFormat("DOW, Abbreviated-Month Day, Year", "Sunday, Apr 5, 2026", "dddd, MMM d, yyyy"),
new DateDisplayFormat("Day Month, Year", "5 April 2026", "d MMMM yyyy"),
new DateDisplayFormat("Day Abbreviated-Month, Year", "5 Apr 2026", "d MMM yyyy"),
new DateDisplayFormat("Day Month, Year", "05 April 2026", "dd MMMM yyyy"),
new DateDisplayFormat("Day Abbreviated-Month, Year", "05 Apr 2026", "dd MMM yyyy"),
];
}
public static string[] GetQuotes() public static string[] GetQuotes()
{ {
// sources: // sources:
+35
View File
@@ -0,0 +1,35 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using Avalonia.Data.Converters;
namespace MayShow.Helpers;
public class DateFormatConverter : IMultiValueConverter
{
public object? Convert(
IList<object?> values,
Type targetType,
object? parameter,
CultureInfo culture)
{
if (values.Count >= 2 && values[0] is DateOnly date && values[1] is string format)
{
return date.ToString(format);
}
if (values.Count >= 2 && values[0] is string dateFormat && values[1] is DateOnly dateOnly)
{
return dateOnly.ToString(dateFormat);
}
if (values.Count >= 2 && values[0] is DateTime dateTime && values[1] is string format3)
{
return dateTime.ToString(format3);
}
if (values.Count >= 2 && values[0] is string format4 && values[1] is DateTime dateTime2)
{
return dateTime2.ToString(format4);
}
return "";
}
}
+1 -1
View File
@@ -2,7 +2,7 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Threading; using System.Threading;
namespace MayShows.Helpers; namespace MayShow.Helpers;
public static class ThreadSafeRandom public static class ThreadSafeRandom
{ {
+20 -7
View File
@@ -1,12 +1,14 @@
using System; using System;
using System.Collections.Generic;
using System.Globalization; using System.Globalization;
using System.IO; using System.IO;
using System.Text.Json; using System.Text.Json;
using System.Text.Json.Serialization; using System.Text.Json.Serialization;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using Tmds.DBus.Protocol;
namespace MayShows.Helpers; namespace MayShow.Helpers;
class Utilities class Utilities
{ {
@@ -23,16 +25,27 @@ class Utilities
public static DateOnly? CheckValidDateInString(string str) public static DateOnly? CheckValidDateInString(string str)
{ {
// https://stackoverflow.com/a/14918404/3938401 // https://stackoverflow.com/a/14918404/3938401
var rgx = new Regex(@"\d{4}-\d{2}-\d{2}"); // formats = regex format -> DateTime parsing format
var formats = new Dictionary<string, string>
{
{@"\d{4}-\d{2}-\d{2}", "yyyy-MM-dd"},
{@"\d{4}.d{2}.d{2}", "yyyy.MM.dd"},
{@"\d{8}", "yyyyMMdd"}
};
foreach (var data in formats)
{
var rgx = new Regex(data.Key);
var mat = rgx.Match(str); var mat = rgx.Match(str);
if (mat.Success) if (mat.Success)
{ {
var dtStr = mat.ToString(); var dtStr = mat.ToString();
string[] formats = ["yyyy-MM-dd"]; var didWork = DateTime.TryParseExact(dtStr, [data.Value], CultureInfo.InvariantCulture,
DateTime parsedDateTime; DateTimeStyles.None, out var parsedDateTime);
var didWork = DateTime.TryParseExact(dtStr, formats, CultureInfo.InvariantCulture, if (didWork)
DateTimeStyles.None, out parsedDateTime); {
return didWork ? DateOnly.FromDateTime(parsedDateTime) : null; return DateOnly.FromDateTime(parsedDateTime);
}
}
} }
return null; return null;
} }
+4 -2
View File
@@ -12,7 +12,7 @@
<PublishTrimmed>true</PublishTrimmed> <PublishTrimmed>true</PublishTrimmed>
<PublishAot>true</PublishAot> <PublishAot>true</PublishAot>
<AssemblyName>MayShow</AssemblyName> <AssemblyName>MayShow</AssemblyName>
<AssemblyVersion>1.4.0</AssemblyVersion> <!-- Also update Constants version --> <AssemblyVersion>1.4.3</AssemblyVersion> <!-- Also update Constants version -->
<ApplicationIcon>MayShow-icon.ico</ApplicationIcon> <ApplicationIcon>MayShow-icon.ico</ApplicationIcon>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
@@ -58,7 +58,9 @@
<PrivateAssets Condition="'$(Configuration)' != 'Debug'">All</PrivateAssets> <PrivateAssets Condition="'$(Configuration)' != 'Debug'">All</PrivateAssets>
</PackageReference> </PackageReference>
<PackageReference Include="PDFsharp-MigraDoc" Version="6.2.4" /> <PackageReference Include="PDFsharp-MigraDoc" Version="6.2.4" />
<PackageReference Include="Magick.NET-Q8-AnyCPU" Version="14.10.3" /> <PackageReference Include="Magick.NET-Q8-AnyCPU" Version="14.11.1" Condition="'$(RuntimeIdentifier)' != 'osx-x64'" />
<!-- DO NOT UPDATE BEYOND 14.9.1 OR YOU WILL BREAK macOS MONTEREY USERS -->
<PackageReference Include="Magick.NET-Q8-x64" Version="14.9.1" Condition="'$(RuntimeIdentifier)' == 'osx-x64'" />
<PackageReference Include="Deadpikle.AvaloniaProgressRing" Version="0.10.11-preview20251127001" /> <PackageReference Include="Deadpikle.AvaloniaProgressRing" Version="0.10.11-preview20251127001" />
<PackageReference Include="DialogHost.Avalonia" Version="0.10.4" /> <PackageReference Include="DialogHost.Avalonia" Version="0.10.4" />
<PackageReference Include="Xaml.Behaviors.Interactions.DragAndDrop.DataGrid" Version="11.3.9.5" /> <PackageReference Include="Xaml.Behaviors.Interactions.DragAndDrop.DataGrid" Version="11.3.9.5" />
+35
View File
@@ -0,0 +1,35 @@
using MayShow.Helpers;
namespace MayShow.Models;
class DateDisplayFormat : ChangeNotifier
{
private string _title;
private string _example;
private string _value;
public DateDisplayFormat(string title, string example, string value)
{
_title = title;
_example = example;
_value = value;
}
public string Title
{
get => _title;
set { _title = value; NotifyPropertyChanged(); }
}
public string Example
{
get => _example;
set { _example = value; NotifyPropertyChanged(); }
}
public string Value
{
get => _value;
set { _value = value; NotifyPropertyChanged(); }
}
}
+3 -3
View File
@@ -23,7 +23,7 @@ class ReportFile : ChangeNotifier
public ReportFile(ReportFile other) public ReportFile(ReportFile other)
{ {
Title = _title = other.Title; Title = _title = other.Title;
ReceiptDateTime = _receiptDateTime = other.ReceiptDateTime; ReceiptDateTime = _receiptDateTime = other.ReceiptDateTime ?? DateTime.Now;
Notes = _notes = other.Notes; Notes = _notes = other.Notes;
FilePath = _filePath = other.FilePath; FilePath = _filePath = other.FilePath;
} }
@@ -34,12 +34,12 @@ class ReportFile : ChangeNotifier
set { _title = value; NotifyPropertyChanged(); } set { _title = value; NotifyPropertyChanged(); }
} }
public DateTime ReceiptDateTime public DateTime? ReceiptDateTime
{ {
get => _receiptDateTime; get => _receiptDateTime;
set set
{ {
_receiptDateTime = value; _receiptDateTime = value ?? DateTime.Now;
NotifyPropertyChanged(); NotifyPropertyChanged();
NotifyPropertyChanged(nameof(ReceiptDate)); NotifyPropertyChanged(nameof(ReceiptDate));
} }
+20 -1
View File
@@ -6,7 +6,6 @@ using System.Text.Json;
using System.Text.Json.Serialization; using System.Text.Json.Serialization;
using System.Threading.Tasks; using System.Threading.Tasks;
using MayShow.Helpers; using MayShow.Helpers;
using MayShows.Helpers;
namespace MayShow.Models; namespace MayShow.Models;
@@ -19,6 +18,8 @@ class Settings : ChangeNotifier
private decimal _imageResizeThreshold; private decimal _imageResizeThreshold;
private bool _saveReportJsonDataInInternalDir; private bool _saveReportJsonDataInInternalDir;
private Dictionary<string, string> _workingFolderToInternalFolderName; private Dictionary<string, string> _workingFolderToInternalFolderName;
public string _dataGridDateFormat;
public string _reportDateFormat;
public int _settingsVersion; public int _settingsVersion;
public Settings() public Settings()
@@ -31,6 +32,8 @@ class Settings : ChangeNotifier
_saveReportJsonDataInInternalDir = false; _saveReportJsonDataInInternalDir = false;
_workingFolderToInternalFolderName = []; _workingFolderToInternalFolderName = [];
_settingsVersion = 1; _settingsVersion = 1;
_dataGridDateFormat = "dd/MM/yyyy";
_reportDateFormat = "yyyy-MM-dd";
} }
public Settings(Settings other) public Settings(Settings other)
@@ -43,6 +46,8 @@ class Settings : ChangeNotifier
_saveReportJsonDataInInternalDir = other.SaveReportJsonDataInInternalDir; _saveReportJsonDataInInternalDir = other.SaveReportJsonDataInInternalDir;
_workingFolderToInternalFolderName = other.WorkingFolderToInternalFolderName; _workingFolderToInternalFolderName = other.WorkingFolderToInternalFolderName;
_settingsVersion = other.SettingsVersion; _settingsVersion = other.SettingsVersion;
_dataGridDateFormat = "yyyy-MM-dd";
_reportDateFormat = "yyyy-MM-dd";
} }
[JsonInclude] [JsonInclude]
@@ -102,6 +107,20 @@ class Settings : ChangeNotifier
set { _settingsVersion = value; NotifyPropertyChanged(); } set { _settingsVersion = value; NotifyPropertyChanged(); }
} }
[JsonInclude]
public string DataGridDateFormat
{
get => _dataGridDateFormat;
set { _dataGridDateFormat = value; NotifyPropertyChanged(); }
}
[JsonInclude]
public string ReportDateFormat
{
get => _reportDateFormat;
set { _reportDateFormat = value; NotifyPropertyChanged(); }
}
public static string SettingsFileName = "settings.json"; public static string SettingsFileName = "settings.json";
public static string GetSettingsPath() public static string GetSettingsPath()
+3 -4
View File
@@ -5,10 +5,10 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Text; using System.Text;
namespace MayShow.ViewModels namespace MayShow.ViewModels;
class BaseViewModel : ChangeNotifier
{ {
class BaseViewModel : ChangeNotifier
{
IChangeViewModel _viewModelChanger; IChangeViewModel _viewModelChanger;
ITopLevelGrabber? _topLevelGrabber; ITopLevelGrabber? _topLevelGrabber;
@@ -43,5 +43,4 @@ namespace MayShow.ViewModels
} }
#endregion #endregion
}
} }
+214 -46
View File
@@ -19,7 +19,6 @@ using PdfSharp.Snippets.Font;
using MayShow.Helpers; using MayShow.Helpers;
using MayShow.Interfaces; using MayShow.Interfaces;
using MayShow.Models; using MayShow.Models;
using MayShows.Helpers;
using Docnet.Core.Models; using Docnet.Core.Models;
using Docnet.Core; using Docnet.Core;
@@ -28,6 +27,9 @@ using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing; using SixLabors.ImageSharp.Processing;
using System.Reflection.Metadata.Ecma335; using System.Reflection.Metadata.Ecma335;
using Docnet.Core.Readers; using Docnet.Core.Readers;
using MigraDoc.DocumentObjectModel.Visitors;
using System.Collections.Generic;
using System.Runtime.InteropServices;
namespace MayShow.ViewModels; namespace MayShow.ViewModels;
@@ -44,6 +46,7 @@ class MainViewModel : BaseViewModel, IFontResolver, ICanCheckShutdown
private DateTime? _lastGeneratedTime; private DateTime? _lastGeneratedTime;
private Settings _settings; private Settings _settings;
private List<DateDisplayFormat> _dateDisplayFormats;
private bool _hasUnsavedWork; private bool _hasUnsavedWork;
@@ -56,7 +59,9 @@ class MainViewModel : BaseViewModel, IFontResolver, ICanCheckShutdown
var quotes = Constants.GetQuotes(); var quotes = Constants.GetQuotes();
Random random = new Random(); Random random = new Random();
var quoteIndex = random.Next(0, quotes.Length); var quoteIndex = random.Next(0, quotes.Length);
_programLog = "----- MayShow v" + Constants.AppVersion + " ------" + Environment.NewLine; var compDetails = RuntimeInformation.OSDescription + " | " +
RuntimeInformation.OSArchitecture.ToString();
_programLog = "----- MayShow v" + Constants.AppVersion + " | " + compDetails + " ------" + Environment.NewLine;
_programLog += quotes[quoteIndex] + Environment.NewLine; _programLog += quotes[quoteIndex] + Environment.NewLine;
_programLog += "---------------------------------------" + Environment.NewLine; _programLog += "---------------------------------------" + Environment.NewLine;
_programLog += "Loaded and ready to create report!" + Environment.NewLine; _programLog += "Loaded and ready to create report!" + Environment.NewLine;
@@ -66,6 +71,9 @@ class MainViewModel : BaseViewModel, IFontResolver, ICanCheckShutdown
_reportTitle = ""; _reportTitle = "";
_lastGeneratedTime = null; _lastGeneratedTime = null;
_settings = Settings.LoadSettings(); _settings = Settings.LoadSettings();
_dateDisplayFormats = Constants.GetDateDisplayFormats();
NotifyPropertyChanged(nameof(DataGridDateFormat));
NotifyPropertyChanged(nameof(DataGridDateFormatWatermark));
if (!string.IsNullOrWhiteSpace(_settings.LastUsedPath)) if (!string.IsNullOrWhiteSpace(_settings.LastUsedPath))
{ {
LogInfo("Loading data at last used path of {0}", _settings.LastUsedPath); LogInfo("Loading data at last used path of {0}", _settings.LastUsedPath);
@@ -172,6 +180,16 @@ class MainViewModel : BaseViewModel, IFontResolver, ICanCheckShutdown
} }
} }
public string DataGridDateFormat
{
get => _settings.DataGridDateFormat;
}
public string DataGridDateFormatWatermark
{
get => _dateDisplayFormats.FirstOrDefault(x => x.Value == _settings.DataGridDateFormat)?.Example ?? "2025-12-04";
}
private void LogInfo(string message, params object[]? arguments) private void LogInfo(string message, params object[]? arguments)
{ {
var timestamp = string.Format("[{0:s}]", DateTime.Now); var timestamp = string.Format("[{0:s}]", DateTime.Now);
@@ -298,6 +316,8 @@ class MainViewModel : BaseViewModel, IFontResolver, ICanCheckShutdown
_settings = (Settings)updatedSettings; _settings = (Settings)updatedSettings;
await _settings.SaveSettingsAsync(); await _settings.SaveSettingsAsync();
LogInfo("Saved updated settings!"); LogInfo("Saved updated settings!");
NotifyPropertyChanged(nameof(DataGridDateFormat));
NotifyPropertyChanged(nameof(DataGridDateFormatWatermark));
} }
} }
@@ -508,13 +528,15 @@ class MainViewModel : BaseViewModel, IFontResolver, ICanCheckShutdown
{ {
LogInfo(e.StackTrace); LogInfo(e.StackTrace);
} }
if (e.InnerException != null) var otherException = e.InnerException;
while (otherException != null)
{ {
LogInfo("Inner exception: " + e.InnerException.Message); LogInfo(">> Inner exception: " + otherException.Message);
if (e.InnerException.StackTrace != null) if (otherException.StackTrace != null)
{ {
LogInfo(e.InnerException.StackTrace); LogInfo(otherException.StackTrace);
} }
otherException = otherException.InnerException;
} }
LogInfo("Please report this error to a programmer or fix the issue listed above."); LogInfo("Please report this error to a programmer or fix the issue listed above.");
IsCreatingPDF = false; IsCreatingPDF = false;
@@ -576,6 +598,24 @@ class MainViewModel : BaseViewModel, IFontResolver, ICanCheckShutdown
public byte[]? GetFont(string faceName) public byte[]? GetFont(string faceName)
{ {
LogInfo(string.Format("Loading font {0}", faceName)); LogInfo(string.Format("Loading font {0}", faceName));
if (faceName == "Noto Sans")
{
var path = Path.Combine(_processDir, "Assets/Fonts/Noto_Sans/static/NotoSans-Regular.ttf");
if (!File.Exists(path))
{
path = Path.Combine(_processDir, "../Resources/Assets/Fonts/Noto_Sans/static/NotoSans-Regular.ttf");
}
return File.ReadAllBytes(path);
}
if (faceName == "Noto Sans Bold")
{
var path = Path.Combine(_processDir, "Assets/Fonts/Noto_Sans/static/NotoSans-Bold.ttf");
if (!File.Exists(path))
{
path = Path.Combine(_processDir, "../Resources/Assets/Fonts/Noto_Sans/static/NotoSans-Bold.ttf");
}
return File.ReadAllBytes(path);
}
if (faceName == "Noto Sans JP") if (faceName == "Noto Sans JP")
{ {
var path = Path.Combine(_processDir, "Assets/Fonts/Noto_Sans_JP/static/NotoSansJP-Regular.ttf"); var path = Path.Combine(_processDir, "Assets/Fonts/Noto_Sans_JP/static/NotoSansJP-Regular.ttf");
@@ -600,6 +640,14 @@ class MainViewModel : BaseViewModel, IFontResolver, ICanCheckShutdown
public FontResolverInfo? ResolveTypeface(string familyName, bool bold, bool italic) public FontResolverInfo? ResolveTypeface(string familyName, bool bold, bool italic)
{ {
// LogInfo(string.Format("Resolving font name {0}", familyName)); // LogInfo(string.Format("Resolving font name {0}", familyName));
if (familyName == "Noto Sans")
{
if (bold)
{
return new FontResolverInfo(familyName + " Bold");
}
return new FontResolverInfo(familyName);
}
if (familyName == "Noto Sans JP") if (familyName == "Noto Sans JP")
{ {
if (bold) if (bold)
@@ -611,11 +659,47 @@ class MainViewModel : BaseViewModel, IFontResolver, ICanCheckShutdown
return null; return null;
} }
private Paragraph GetFooterParagraph()
{
var footerPar = new Paragraph();
footerPar.Format.Alignment = ParagraphAlignment.Center;
footerPar.Format.Font.Size = 10;
footerPar.AddText("--Page ");
footerPar.AddPageField();
footerPar.AddText(" of ");
footerPar.AddNumPagesField();
footerPar.AddText("--");
footerPar.AddLineBreak();
footerPar.AddText("Report generated on " + DateTime.Now.ToString("f"));
footerPar.Tag = "FooterPar";
footerPar.Format.Font.Name = "Noto Sans";
return footerPar;
}
private decimal GetExistingPageItemHeight(PdfDocumentRenderer pdfRenderer, decimal footerParagraphHeight)
{
pdfRenderer.DocumentRenderer.PrepareDocument();
var currPageCount = pdfRenderer.DocumentRenderer.FormattedDocument?.PageCount;
var heightForExistingItemsOnPage = footerParagraphHeight;
if (currPageCount.HasValue)
{
var renderInfo = pdfRenderer.DocumentRenderer.GetRenderInfoFromPage(currPageCount.Value);
if (renderInfo != null)
{
// Console.WriteLine("Got render info for page: {0}", currPageCount);
foreach (var item in renderInfo)
{
heightForExistingItemsOnPage += (decimal)item.LayoutInfo.ContentArea.Height.Inch;
}
}
}
return heightForExistingItemsOnPage;
}
// https://forum.pdfsharp.net/viewtopic.php?f=2&t=1025 // https://forum.pdfsharp.net/viewtopic.php?f=2&t=1025
private async Task CreatePDF(string folderPath) private async Task CreatePDF(string folderPath)
{ {
// TODO: calculate needed width for images based on page width and margins and all that? // TODO: calculate needed width for images based on page width and margins and all that?
// TODO: resize (non-HEIC) images for smaller size...?
// safety checks // safety checks
var outputDir = _settings.SaveOutputPdfInWorkingDir ? folderPath : _settings.OutputPdfDir; var outputDir = _settings.SaveOutputPdfInWorkingDir ? folderPath : _settings.OutputPdfDir;
if (!Directory.Exists(outputDir)) if (!Directory.Exists(outputDir))
@@ -623,12 +707,26 @@ class MainViewModel : BaseViewModel, IFontResolver, ICanCheckShutdown
await DialogHost.Show(new WarningViewModel("Output directory not found! Please adjust your application Settings before continuing. Output directory: " + outputDir)); await DialogHost.Show(new WarningViewModel("Output directory not found! Please adjust your application Settings before continuing. Output directory: " + outputDir));
return; return;
} }
// setup globals and consts...
GlobalFontSettings.FontResolver = this;
GlobalFontSettings.FallbackFontResolver = new FailsafeFontResolver();
const decimal pageWidth = 8.5m;
const decimal pageHeight = 11.0m;
const decimal margin = 0.5m;
const int imageResolution = 72;
const int imageInsertMarginPixels = 30; // we calculate max available; use max - this # for max image size
var maxItemPxWidth = ((pageWidth - (2 * margin)) * imageResolution) - imageInsertMarginPixels;
// start making PDF! // start making PDF!
IsCreatingPDF = true; IsCreatingPDF = true;
var pdfDoc = new Document(); var pdfDoc = new Document();
var outputFileName = ReportTitle + ".pdf"; var outputFileName = ReportTitle + ".pdf";
var folderName = new DirectoryInfo(folderPath).Name; var folderName = new DirectoryInfo(folderPath).Name;
const int imageWidth = 425; const int maxImageWidth = 425;
var imageLineFormat = new MigraDoc.DocumentObjectModel.Shapes.LineFormat()
{
Color = Colors.Black,
Width = Unit.FromPoint(2),
};;
if (folderName.Contains('-')) if (folderName.Contains('-'))
{ {
// see if year/month format // see if year/month format
@@ -643,33 +741,51 @@ class MainViewModel : BaseViewModel, IFontResolver, ICanCheckShutdown
LogInfo("Auto-changed output file name to " + outputFileName); LogInfo("Auto-changed output file name to " + outputFileName);
} }
} }
// setup initial section (for page characteristics)
var section = pdfDoc.AddSection(); var section = pdfDoc.AddSection();
section.PageSetup.PageFormat = PageFormat.Letter; section.PageSetup.PageFormat = PageFormat.Letter;
section.PageSetup.PageWidth = "8.5in"; section.PageSetup.PageWidth = pageWidth + "in";
section.PageSetup.PageHeight = "11in"; section.PageSetup.PageHeight = pageHeight + "in";
section.PageSetup.TopMargin = "0.5in"; section.PageSetup.TopMargin = margin + "in";
section.PageSetup.RightMargin = "0.5in"; section.PageSetup.RightMargin = margin + "in";
section.PageSetup.BottomMargin = "0.5in"; section.PageSetup.BottomMargin = margin + "in";
section.PageSetup.LeftMargin = "0.5in"; section.PageSetup.LeftMargin = margin + "in";
// setup footer for page number // setup footer for page number
var footerPar = new Paragraph(); var footerPar = GetFooterParagraph();
footerPar.Format.Alignment = ParagraphAlignment.Center;
footerPar.Format.Font.Size = 10;
footerPar.AddText("--Page ");
footerPar.AddPageField();
footerPar.AddText(" of ");
footerPar.AddNumPagesField();
footerPar.AddText("--");
footerPar.AddLineBreak();
footerPar.AddText("Report generated on " + DateTime.Now.ToString("f"));
section.Footers.Primary.Add(footerPar); section.Footers.Primary.Add(footerPar);
// add report title // create a quick PDF doc renderer to measure footer paragraph height
var footerParagraphHeight = 0.4m; // estimate
var footerOnlyPdfDoc = new Document();
var sectionClone = section.Clone();
footerOnlyPdfDoc.Add(sectionClone);
sectionClone.Add(GetFooterParagraph());
var footerPdfRenderer = new PdfDocumentRenderer
{
Document = footerOnlyPdfDoc
};
footerPdfRenderer.DocumentRenderer.PrepareDocument();
var footerRenderInfo = footerPdfRenderer.DocumentRenderer.GetRenderInfoFromPage(1);
if (footerRenderInfo != null)
{
foreach (var item in footerRenderInfo)
{
if (item.DocumentObject.Tag?.ToString() == "FooterPar")
{
Console.WriteLine("Got footer paragraph height!");
footerParagraphHeight = (decimal)item.LayoutInfo.ContentArea.Height.Inch;
break;
}
}
}
// continue setting up document
// First page only: add report title
var reportTitlePar = section.AddParagraph(); var reportTitlePar = section.AddParagraph();
reportTitlePar.Format.Alignment = ParagraphAlignment.Center; reportTitlePar.Format.Alignment = ParagraphAlignment.Center;
reportTitlePar.Format.Font.Size = 16; reportTitlePar.Format.Font.Size = 16;
reportTitlePar.Format.Font.Bold = true; reportTitlePar.Format.Font.Bold = true;
reportTitlePar.Format.Font.Name = "Noto Sans JP"; // has english letters in it, too reportTitlePar.Format.Font.Name = "Noto Sans JP"; // has english letters in it, too
reportTitlePar.AddText(ReportTitle); reportTitlePar.AddText(ReportTitle);
reportTitlePar.Tag = "TitlePar";
// get converted files directory path and create it if necessary // get converted files directory path and create it if necessary
var convertedDir = Path.Combine(Utilities.GetInternalDataPath(), "converted"); var convertedDir = Path.Combine(Utilities.GetInternalDataPath(), "converted");
if (!Directory.Exists(convertedDir)) if (!Directory.Exists(convertedDir))
@@ -677,8 +793,11 @@ class MainViewModel : BaseViewModel, IFontResolver, ICanCheckShutdown
Directory.CreateDirectory(convertedDir); Directory.CreateDirectory(convertedDir);
} }
// //
GlobalFontSettings.FontResolver = this; var pdfRenderer = new PdfDocumentRenderer
GlobalFontSettings.FallbackFontResolver = new FailsafeFontResolver(); {
Document = pdfDoc,
WorkingDirectory = folderPath
};
var hasAddedData = false; var hasAddedData = false;
for (var i = 0; i < ReportFiles.Count; i++) for (var i = 0; i < ReportFiles.Count; i++)
{ {
@@ -705,12 +824,14 @@ class MainViewModel : BaseViewModel, IFontResolver, ICanCheckShutdown
imageTitlePar.Format.Font.Bold = true; imageTitlePar.Format.Font.Bold = true;
imageTitlePar.Format.Font.Name = "Noto Sans JP"; // has english letters in it, too imageTitlePar.Format.Font.Name = "Noto Sans JP"; // has english letters in it, too
imageTitlePar.AddText(string.IsNullOrWhiteSpace(file.Title) ? file.FileName : file.Title); imageTitlePar.AddText(string.IsNullOrWhiteSpace(file.Title) ? file.FileName : file.Title);
imageTitlePar.Tag = "ReceiptTitlePar";
var receiptDatePar = section.AddParagraph(); var receiptDatePar = section.AddParagraph();
receiptDatePar.Format.Alignment = ParagraphAlignment.Center; receiptDatePar.Format.Alignment = ParagraphAlignment.Center;
receiptDatePar.Format.Font.Size = 12; receiptDatePar.Format.Font.Size = 12;
receiptDatePar.Format.Font.Bold = true; receiptDatePar.Format.Font.Bold = true;
receiptDatePar.Format.Font.Name = "Noto Sans JP"; // has english letters in it, too receiptDatePar.Format.Font.Name = "Noto Sans JP"; // has english letters in it, too
receiptDatePar.AddText(file.ReceiptDate.ToString("yyyy-MM-dd")); receiptDatePar.AddText(file.ReceiptDate.ToString(_settings.ReportDateFormat));
receiptDatePar.Tag = "ReceiptDatePar";
if (!string.IsNullOrWhiteSpace(file.Notes)) if (!string.IsNullOrWhiteSpace(file.Notes))
{ {
var imageNotesPar = section.AddParagraph(); var imageNotesPar = section.AddParagraph();
@@ -719,8 +840,10 @@ class MainViewModel : BaseViewModel, IFontResolver, ICanCheckShutdown
imageNotesPar.Format.Font.Bold = false; imageNotesPar.Format.Font.Bold = false;
imageNotesPar.Format.Font.Name = "Noto Sans JP"; imageNotesPar.Format.Font.Name = "Noto Sans JP";
imageNotesPar.AddText(file.Notes); imageNotesPar.AddText(file.Notes);
imageNotesPar.Tag = "ReceiptNotesPar";
} }
section.AddParagraph(); // add empty line for spacing var emptyPar = section.AddParagraph(); // add empty line for spacing
emptyPar.Tag = "EmptyParagraph";
// now add the image // now add the image
var lowerName = fileName.ToLower(); var lowerName = fileName.ToLower();
var isPDF = lowerName.EndsWith(".pdf"); var isPDF = lowerName.EndsWith(".pdf");
@@ -732,6 +855,14 @@ class MainViewModel : BaseViewModel, IFontResolver, ICanCheckShutdown
var info = new FileInfo(file.FilePath); var info = new FileInfo(file.FilePath);
uint loadedImageWidth = 0; uint loadedImageWidth = 0;
uint loadedImageHeight = 0; uint loadedImageHeight = 0;
// get max pixel height remaining for items on this page
// (For multi-page PDFs, showing page 2 and on will have more height since they have no title,
// but to keep things consistent we will use the same height for all PDF pages.)
// render up to now on this page and get height remaining in inches
var currPageCount = pdfRenderer.DocumentRenderer.FormattedDocument?.PageCount;
var heightForExistingItemsOnPage = GetExistingPageItemHeight(pdfRenderer, footerParagraphHeight);
var remainingHeightInches = pageHeight - (2 * margin) - heightForExistingItemsOnPage;
var remainingHeightPixels = (remainingHeightInches * imageResolution) - imageInsertMarginPixels;
if (!isPDF) if (!isPDF)
{ {
using var mImage = new MagickImage(info.FullName); using var mImage = new MagickImage(info.FullName);
@@ -777,17 +908,23 @@ class MainViewModel : BaseViewModel, IFontResolver, ICanCheckShutdown
var paragraph = section.AddParagraph(); var paragraph = section.AddParagraph();
paragraph.Format.Alignment = ParagraphAlignment.Center; paragraph.Format.Alignment = ParagraphAlignment.Center;
var image = paragraph.AddImage(filePath); var image = paragraph.AddImage(filePath);
image.LockAspectRatio = true; image.Resolution = imageResolution; // dots per inch
if (!isPDF && loadedImageHeight > 600) image.Tag = "ReceiptImageTag";
paragraph.Tag = "ReceiptImageParagraphTag";
image.LineFormat = imageLineFormat.Clone();
// resize down until it will fit on the page
while (loadedImageHeight > remainingHeightPixels || loadedImageWidth > maxItemPxWidth)
{ {
image.Height = 550; // make sure it will fit on one page // Console.WriteLine("Image height = {0}, width = {1}; decreasing size by 5% to h={2}, w={3}", loadedImageHeight, loadedImageWidth, (uint)Math.Floor(loadedImageHeight * 0.95), (uint)Math.Floor(loadedImageWidth * 0.95));
// keep reducing size by 5% (little by little) until it fits on the page
// ...might skew ever so slightly but should not be noticable...
loadedImageHeight = (uint)Math.Floor(loadedImageHeight * 0.95);
loadedImageWidth = (uint)Math.Floor(loadedImageWidth * 0.95);
} }
else image.Height = loadedImageHeight;
{ image.Width = loadedImageWidth;
image.Width = imageWidth; // can't be too wide now...not sure why...maybe due to margins...
} }
} else // isPDF
else
{ {
// need to render PDF to images // need to render PDF to images
if (_settings.UseDocnetPDFImageRendering) if (_settings.UseDocnetPDFImageRendering)
@@ -826,9 +963,23 @@ class MainViewModel : BaseViewModel, IFontResolver, ICanCheckShutdown
pgCount == 1 ? "" : "s")); pgCount == 1 ? "" : "s"));
var paragraph = section.AddParagraph(); var paragraph = section.AddParagraph();
paragraph.Format.Alignment = ParagraphAlignment.Center; paragraph.Format.Alignment = ParagraphAlignment.Center;
// get image height/width off of disk so we can resize down if needed
var image = paragraph.AddImage(convertedPdfImagePath); var image = paragraph.AddImage(convertedPdfImagePath);
image.Width = imageWidth;
image.LockAspectRatio = true; image.LockAspectRatio = true;
image.LineFormat = imageLineFormat.Clone();
using (var firstPdfPageImage = new MagickImage(convertedPdfImagePath))
{
var pdfPageImageWidth = firstPdfPageImage.Width;
var pdfPageImageHeight = firstPdfPageImage.Height;
// resize down until it will fit on the page
while (pdfPageImageHeight > remainingHeightPixels || pdfPageImageWidth > maxItemPxWidth)
{
pdfPageImageHeight = (uint)Math.Floor(pdfPageImageHeight * 0.95);
pdfPageImageWidth = (uint)Math.Floor(pdfPageImageWidth * 0.95);
}
image.Height = pdfPageImageHeight;
image.Width = pdfPageImageWidth;
}
for (var j = 1; j < pgCount; j++) for (var j = 1; j < pgCount; j++)
{ {
section.AddPageBreak(); section.AddPageBreak();
@@ -837,18 +988,37 @@ class MainViewModel : BaseViewModel, IFontResolver, ICanCheckShutdown
convertedPdfImagePath = RenderPdfPageToImage(docReader, j); convertedPdfImagePath = RenderPdfPageToImage(docReader, j);
image = paragraph.AddImage(convertedPdfImagePath); image = paragraph.AddImage(convertedPdfImagePath);
image.LockAspectRatio = true; image.LockAspectRatio = true;
image.Width = imageWidth; image.Width = maxImageWidth;
image.LineFormat = imageLineFormat.Clone();
using (var otherPdfPageImage = new MagickImage(convertedPdfImagePath))
{
var pdfPageImageWidth = otherPdfPageImage.Width;
var pdfPageImageHeight = otherPdfPageImage.Height;
// resize down until it will fit on the page
while (pdfPageImageHeight > remainingHeightPixels || pdfPageImageWidth > maxItemPxWidth)
{
pdfPageImageHeight = (uint)Math.Floor(pdfPageImageHeight * 0.95);
pdfPageImageWidth = (uint)Math.Floor(pdfPageImageWidth * 0.95);
}
image.Height = pdfPageImageHeight;
image.Width = pdfPageImageWidth;
}
} }
} }
} }
else else
{ {
// use older, not-docnet rendering method.
// uses MigraDoc rendering. Does not work with annotations, and since Migradoc
// doesn't let us know how big the image is, we can't do the image resizing, so
// we just do our best.
// render first page (eventually need to improve code to just do everything in a loop) // render first page (eventually need to improve code to just do everything in a loop)
var paragraph = section.AddParagraph(); var paragraph = section.AddParagraph();
paragraph.Format.Alignment = ParagraphAlignment.Center; paragraph.Format.Alignment = ParagraphAlignment.Center;
var image = paragraph.AddImage(filePath); var image = paragraph.AddImage(filePath);
image.LockAspectRatio = true; image.LockAspectRatio = true;
image.Width = imageWidth; // can't be too wide now...not sure why...maybe due to margins... image.Width = maxImageWidth; // can't be too wide now...not sure why...maybe due to margins...
image.LineFormat = imageLineFormat.Clone();
// render other PDF pages, if any // render other PDF pages, if any
// see: https://stackoverflow.com/a/65091204/3938401 // see: https://stackoverflow.com/a/65091204/3938401
var pdfFileToAdd = PdfReader.Open(filePath, PdfDocumentOpenMode.Import); var pdfFileToAdd = PdfReader.Open(filePath, PdfDocumentOpenMode.Import);
@@ -863,20 +1033,18 @@ class MainViewModel : BaseViewModel, IFontResolver, ICanCheckShutdown
paragraph.Format.Alignment = ParagraphAlignment.Center; paragraph.Format.Alignment = ParagraphAlignment.Center;
image = paragraph.AddImage(filePath + "#" + j); image = paragraph.AddImage(filePath + "#" + j);
image.LockAspectRatio = true; image.LockAspectRatio = true;
image.Width = imageWidth; image.Width = maxImageWidth;
image.LineFormat = imageLineFormat.Clone();
} }
} }
} }
LogInfo(string.Format("Added image: {0} ({1})", file.Title, filePath)); LogInfo(string.Format("Added image: {0} ({1})", file.Title, filePath));
hasAddedData = true; hasAddedData = true;
} }
var pdfRenderer = new PdfDocumentRenderer
{
Document = pdfDoc,
WorkingDirectory = folderPath
};
LogInfo("Rendering document to PDF file..."); LogInfo("Rendering document to PDF file...");
pdfRenderer.DocumentRenderer.PrepareDocument(); // needed if you make edits after first PrepareDocument() is called
pdfRenderer.RenderDocument(); pdfRenderer.RenderDocument();
// actually save to disk now
string outputPDFFilePath = Path.Join(outputDir, outputFileName); string outputPDFFilePath = Path.Join(outputDir, outputFileName);
LogInfo("Saving PDF document to disk..."); LogInfo("Saving PDF document to disk...");
pdfRenderer.PdfDocument.Save(outputPDFFilePath); pdfRenderer.PdfDocument.Save(outputPDFFilePath);
+3 -4
View File
@@ -4,10 +4,10 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Text; using System.Text;
namespace MayShow.ViewModels namespace MayShow.ViewModels;
class MainWindowViewModel : ChangeNotifier, IChangeViewModel
{ {
class MainWindowViewModel : ChangeNotifier, IChangeViewModel
{
BaseViewModel _currentViewModel; BaseViewModel _currentViewModel;
Stack<BaseViewModel> _viewModels; Stack<BaseViewModel> _viewModels;
@@ -46,5 +46,4 @@ namespace MayShow.ViewModels
} }
#endregion #endregion
}
} }
+54
View File
@@ -18,6 +18,7 @@ using PdfSharp.Snippets.Font;
using MayShow.Interfaces; using MayShow.Interfaces;
using MayShow.Models; using MayShow.Models;
using MayShow.Helpers; using MayShow.Helpers;
using System.Collections.Generic;
namespace MayShow.ViewModels; namespace MayShow.ViewModels;
@@ -27,6 +28,9 @@ class SettingsViewModel: ChangeNotifier
private Settings _settings; private Settings _settings;
private string _errorMessage; private string _errorMessage;
private ITopLevelGrabber? _topLevelGrabber; private ITopLevelGrabber? _topLevelGrabber;
private List<DateDisplayFormat> _dateFormats;
private int _gridDisplayDateFormatSelectedIndex;
private int _reportDisplayDateFormatSelectedIndex;
public SettingsViewModel(Settings settingsToEdit, ITopLevelGrabber? topLevelGrabber) public SettingsViewModel(Settings settingsToEdit, ITopLevelGrabber? topLevelGrabber)
{ {
@@ -34,6 +38,17 @@ class SettingsViewModel: ChangeNotifier
_settings = new Settings(settingsToEdit); // clone it _settings = new Settings(settingsToEdit); // clone it
_errorMessage = ""; _errorMessage = "";
_topLevelGrabber = topLevelGrabber; _topLevelGrabber = topLevelGrabber;
_dateFormats = Constants.GetDateDisplayFormats();
_gridDisplayDateFormatSelectedIndex = _dateFormats.FindIndex(x => x.Value == _previousSettings.DataGridDateFormat);
if (_gridDisplayDateFormatSelectedIndex == -1)
{
_gridDisplayDateFormatSelectedIndex = 0;
}
_reportDisplayDateFormatSelectedIndex = _dateFormats.FindIndex(x => x.Value == _previousSettings.ReportDateFormat);
if (_reportDisplayDateFormatSelectedIndex == -1)
{
_reportDisplayDateFormatSelectedIndex = 0;
}
} }
public bool UseDocnetPDFImageRendering public bool UseDocnetPDFImageRendering
@@ -108,6 +123,33 @@ class SettingsViewModel: ChangeNotifier
} }
} }
public List<DateDisplayFormat> DateFormats
{
get => _dateFormats;
}
public int DataGridDisplayDateFormatSelectedIndex
{
get => _gridDisplayDateFormatSelectedIndex;
set
{
_gridDisplayDateFormatSelectedIndex = value;
_settings.DataGridDateFormat = _dateFormats[value].Value;
NotifyPropertyChanged();
}
}
public int ReportDisplayDateFormatSelectedIndex
{
get => _reportDisplayDateFormatSelectedIndex;
set
{
_reportDisplayDateFormatSelectedIndex = value;
_settings.ReportDateFormat = _dateFormats[value].Value;
NotifyPropertyChanged();
}
}
public async void ChooseOutputFolder() public async void ChooseOutputFolder()
{ {
var topLevel = _topLevelGrabber?.GetTopLevel(); var topLevel = _topLevelGrabber?.GetTopLevel();
@@ -126,6 +168,18 @@ class SettingsViewModel: ChangeNotifier
} }
} }
public void OpenSettingsDir()
{
var topLevel = _topLevelGrabber?.GetTopLevel();
Console.WriteLine(Utilities.GetInternalDataPath());
var dirName = Utilities.GetInternalDataPath();
if (topLevel is not null && dirName != null)
{
var launcher = topLevel.Launcher;
launcher.LaunchUriAsync(new Uri(dirName));
}
}
public void Cancel() public void Cancel()
{ {
DialogHost.Close("DialogHost", null); DialogHost.Close("DialogHost", null);
+3 -4
View File
@@ -2,10 +2,10 @@
using MayShow.Helpers; using MayShow.Helpers;
using MayShow.Models; using MayShow.Models;
namespace MayShow.ViewModels namespace MayShow.ViewModels;
class WarningDeleteItemViewModel : ChangeNotifier
{ {
class WarningDeleteItemViewModel : ChangeNotifier
{
ReportFile _file; ReportFile _file;
public WarningDeleteItemViewModel(ReportFile file) public WarningDeleteItemViewModel(ReportFile file)
@@ -27,5 +27,4 @@ namespace MayShow.ViewModels
{ {
DialogHost.Close("DialogHost", true); DialogHost.Close("DialogHost", true);
} }
}
} }
+1 -1
View File
@@ -11,7 +11,7 @@
MaxWidth="450"> MaxWidth="450">
<StackPanel Orientation="Vertical" <StackPanel Orientation="Vertical"
Spacing="4"> Spacing="4">
<TextBlock Text="MayShow 1.4.0" <TextBlock Text="MayShow 1.4.3"
HorizontalAlignment="Center" HorizontalAlignment="Center"
TextWrapping="Wrap" TextWrapping="Wrap"
FontSize="18" FontSize="18"
+3 -4
View File
@@ -5,10 +5,10 @@ using Avalonia;
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.Markup.Xaml; using Avalonia.Markup.Xaml;
namespace MayShow.Views namespace MayShow.Views;
public partial class AboutView : UserControl
{ {
public partial class AboutView : UserControl
{
public AboutView() public AboutView()
{ {
this.InitializeComponent(); this.InitializeComponent();
@@ -35,5 +35,4 @@ namespace MayShow.Views
} }
LicenseTextBlock.Text = licenseText.Trim(); LicenseTextBlock.Text = licenseText.Trim();
} }
}
} }
+3 -4
View File
@@ -3,13 +3,12 @@ using Avalonia;
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.Markup.Xaml; using Avalonia.Markup.Xaml;
namespace MayShow.Views namespace MayShow.Views;
public partial class EditFile : UserControl
{ {
public partial class EditFile : UserControl
{
public EditFile() public EditFile()
{ {
this.InitializeComponent(); this.InitializeComponent();
} }
}
} }
+30 -14
View File
@@ -10,6 +10,9 @@
xmlns:vm="clr-namespace:MayShow.ViewModels" xmlns:vm="clr-namespace:MayShow.ViewModels"
xmlns:progRing="clr-namespace:AvaloniaProgressRing;assembly=AvaloniaProgressRing" xmlns:progRing="clr-namespace:AvaloniaProgressRing;assembly=AvaloniaProgressRing"
x:DataType="vm:MainViewModel"> x:DataType="vm:MainViewModel">
<UserControl.Resources>
<helpers:DateFormatConverter x:Key="DateFormatter" />
</UserControl.Resources>
<Grid ColumnDefinitions="*" <Grid ColumnDefinitions="*"
RowDefinitions="Auto, 2*, Auto, Auto, *"> RowDefinitions="Auto, 2*, Auto, Auto, *">
<Button Command="{Binding ShowSettings}" <Button Command="{Binding ShowSettings}"
@@ -97,7 +100,7 @@
<DataGridTemplateColumn.CellTemplate> <DataGridTemplateColumn.CellTemplate>
<DataTemplate> <DataTemplate>
<Grid ColumnDefinitions="Auto, *"> <Grid ColumnDefinitions="Auto, *">
<Button Command="{Binding $parent[DataGrid].((vm:MainViewModel)DataContext).LocateFile}" <Button Command="{Binding $parent[UserControl].((vm:MainViewModel)DataContext).LocateFile}"
CommandParameter="{Binding}" CommandParameter="{Binding}"
IsVisible="{Binding !IsFileFoundOnDisk}" IsVisible="{Binding !IsFileFoundOnDisk}"
Margin="2" Margin="2"
@@ -107,7 +110,7 @@
Grid.Column="0" Grid.Column="0"
FontFamily="{StaticResource FontAwesomeSolid}" FontFamily="{StaticResource FontAwesomeSolid}"
ToolTip.Tip="File not found; click to locate..." ToolTip.Tip="File not found; click to locate..."
IsEnabled="{Binding !$parent[DataGrid].((vm:MainViewModel)DataContext).IsCreatingPDF}"/> IsEnabled="{Binding !$parent[UserControl].((vm:MainViewModel)DataContext).IsCreatingPDF}"/>
<TextBlock Text="{Binding Title}" <TextBlock Text="{Binding Title}"
TextTrimming="CharacterEllipsis" TextTrimming="CharacterEllipsis"
TextWrapping="NoWrap" TextWrapping="NoWrap"
@@ -133,19 +136,29 @@
</DataGridTemplateColumn> </DataGridTemplateColumn>
<DataGridTemplateColumn Header="Receipt Date" <DataGridTemplateColumn Header="Receipt Date"
IsReadOnly="False" IsReadOnly="False"
Width="125"> Width="150">
<DataGridTemplateColumn.CellTemplate> <DataGridTemplateColumn.CellTemplate>
<DataTemplate> <DataTemplate>
<Label Content="{Binding ReceiptDate}" <Label VerticalAlignment="Center"
VerticalAlignment="Center"
Margin="8,0,8,0" Margin="8,0,8,0"
HorizontalAlignment="Left"/> HorizontalAlignment="Left">
<Label.Content>
<MultiBinding Converter="{StaticResource DateFormatter}">
<Binding Path="ReceiptDate" />
<Binding Path="$parent[UserControl].((vm:MainViewModel)DataContext).DataGridDateFormat" />
</MultiBinding>
</Label.Content>
</Label>
</DataTemplate> </DataTemplate>
</DataGridTemplateColumn.CellTemplate> </DataGridTemplateColumn.CellTemplate>
<DataGridTemplateColumn.CellEditingTemplate> <DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate DataType="models:ReportFile"> <DataTemplate DataType="models:ReportFile">
<CalendarDatePicker SelectedDate="{Binding ReceiptDateTime}" <CalendarDatePicker SelectedDate="{Binding ReceiptDateTime}"
DisplayDate="{Binding ReceiptDateTime}"/> DisplayDate="{Binding ReceiptDateTime}"
SelectedDateFormat="Custom"
HorizontalAlignment="Stretch"
Watermark="{Binding $parent[UserControl].((vm:MainViewModel)DataContext).DataGridDateFormatWatermark}"
CustomDateFormatString="{Binding $parent[UserControl].((vm:MainViewModel)DataContext).DataGridDateFormat}"/>
</DataTemplate> </DataTemplate>
</DataGridTemplateColumn.CellEditingTemplate> </DataGridTemplateColumn.CellEditingTemplate>
</DataGridTemplateColumn> </DataGridTemplateColumn>
@@ -156,6 +169,7 @@
<DataTemplate> <DataTemplate>
<TextBlock Text="{Binding FileName}" <TextBlock Text="{Binding FileName}"
VerticalAlignment="Center" VerticalAlignment="Center"
TextTrimming="PrefixCharacterEllipsis"
ToolTip.Tip="{Binding FileName}" ToolTip.Tip="{Binding FileName}"
Margin="8,0,8,0" Margin="8,0,8,0"
HorizontalAlignment="Left"/> HorizontalAlignment="Left"/>
@@ -164,25 +178,27 @@
</DataGridTemplateColumn> </DataGridTemplateColumn>
<DataGridTemplateColumn Header="" <DataGridTemplateColumn Header=""
IsReadOnly="True" IsReadOnly="True"
Width="*"> Width="200">
<DataGridTemplateColumn.CellTemplate> <DataGridTemplateColumn.CellTemplate>
<DataTemplate> <DataTemplate>
<StackPanel Orientation="Horizontal" <StackPanel Orientation="Horizontal"
Spacing="4"> Spacing="4">
<Button Command="{Binding $parent[DataGrid].((vm:MainViewModel)DataContext).EditFileProperties}" <Button Command="{Binding $parent[UserControl].((vm:MainViewModel)DataContext).EditFileProperties}"
CommandParameter="{Binding}" CommandParameter="{Binding}"
Classes="accent" Classes="accent"
Margin="2" Margin="2"
IsEnabled="{Binding !$parent[DataGrid].((vm:MainViewModel)DataContext).IsCreatingPDF}"> FontSize="12"
IsEnabled="{Binding !$parent[UserControl].((vm:MainViewModel)DataContext).IsCreatingPDF}">
<Button.Content> <Button.Content>
<TextBlock><Run Text="&#xf044;" FontFamily="{StaticResource FontAwesomeSolid}"/> Edit</TextBlock> <TextBlock><Run Text="&#xf044;" FontFamily="{StaticResource FontAwesomeSolid}"/> Edit</TextBlock>
</Button.Content> </Button.Content>
</Button> </Button>
<Button Command="{Binding $parent[DataGrid].((vm:MainViewModel)DataContext).RemoveFile}" <Button Command="{Binding $parent[UserControl].((vm:MainViewModel)DataContext).RemoveFile}"
CommandParameter="{Binding}" CommandParameter="{Binding}"
Classes="Danger" Classes="Danger"
Margin="2" Margin="2"
IsEnabled="{Binding !$parent[DataGrid].((vm:MainViewModel)DataContext).IsCreatingPDF}"> FontSize="12"
IsEnabled="{Binding !$parent[UserControl].((vm:MainViewModel)DataContext).IsCreatingPDF}">
<Button.Content> <Button.Content>
<TextBlock><Run Text="&#xf1f8;" FontFamily="{StaticResource FontAwesomeSolid}"/> Remove</TextBlock> <TextBlock><Run Text="&#xf1f8;" FontFamily="{StaticResource FontAwesomeSolid}"/> Remove</TextBlock>
</Button.Content> </Button.Content>
@@ -206,13 +222,13 @@
Spacing="8" Spacing="8"
Margin="4" Margin="4"
Grid.Row="2"> Grid.Row="2">
<Button Command="{Binding $parent[DataGrid].((vm:MainViewModel)DataContext).OpenFileLocation}" <Button Command="{Binding $parent[UserControl].((vm:MainViewModel)DataContext).OpenFileLocation}"
CommandParameter="{Binding}"> CommandParameter="{Binding}">
<Button.Content> <Button.Content>
<TextBlock FontSize="12"><Run Text="&#xf07c;" FontFamily="{StaticResource FontAwesomeSolid}"/> Open File Location</TextBlock> <TextBlock FontSize="12"><Run Text="&#xf07c;" FontFamily="{StaticResource FontAwesomeSolid}"/> Open File Location</TextBlock>
</Button.Content> </Button.Content>
</Button> </Button>
<Button Command="{Binding $parent[DataGrid].((vm:MainViewModel)DataContext).OpenFile}" <Button Command="{Binding $parent[UserControl].((vm:MainViewModel)DataContext).OpenFile}"
CommandParameter="{Binding}"> CommandParameter="{Binding}">
<Button.Content> <Button.Content>
<TextBlock FontSize="12"><Run Text="&#xf07c;" FontFamily="{StaticResource FontAwesomeSolid}"/> Open File</TextBlock> <TextBlock FontSize="12"><Run Text="&#xf07c;" FontFamily="{StaticResource FontAwesomeSolid}"/> Open File</TextBlock>
+3 -4
View File
@@ -5,10 +5,10 @@ using Avalonia.Input;
using Avalonia.Markup.Xaml; using Avalonia.Markup.Xaml;
using MayShow.ViewModels; using MayShow.ViewModels;
namespace MayShow.Views namespace MayShow.Views;
public partial class MainView : UserControl
{ {
public partial class MainView : UserControl
{
public MainView() public MainView()
{ {
this.InitializeComponent(); this.InitializeComponent();
@@ -41,5 +41,4 @@ namespace MayShow.Views
mvm?.HasUnsavedWork = true; mvm?.HasUnsavedWork = true;
} }
} }
}
} }
+31
View File
@@ -46,7 +46,38 @@
HorizontalAlignment="Left" HorizontalAlignment="Left"
VerticalAlignment="Top"/> VerticalAlignment="Top"/>
</Grid> </Grid>
<Label Content="File List Date Format"
HorizontalAlignment="Left"
FontSize="14" />
<ComboBox SelectedIndex="{Binding DataGridDisplayDateFormatSelectedIndex}"
ItemsSource="{Binding DateFormats}"
MaxDropDownHeight="300">
<ComboBox.ItemTemplate>
<DataTemplate x:DataType="models:DateDisplayFormat">
<TextBlock Text="{Binding Example}"
TextWrapping="Wrap"/>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
<Label Content="PDF Report Date Format"
HorizontalAlignment="Left"
FontSize="14" />
<ComboBox SelectedIndex="{Binding ReportDisplayDateFormatSelectedIndex}"
ItemsSource="{Binding DateFormats}"
MaxDropDownHeight="300">
<ComboBox.ItemTemplate>
<DataTemplate x:DataType="models:DateDisplayFormat">
<TextBlock Text="{Binding Example}"
TextWrapping="Wrap"/>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
<CheckBox IsChecked="{Binding SaveReportJsonDataInInternalDir}">Save report data (names, notes, etc.) in MayShow settings directory (saves in working directory by default)</CheckBox> <CheckBox IsChecked="{Binding SaveReportJsonDataInInternalDir}">Save report data (names, notes, etc.) in MayShow settings directory (saves in working directory by default)</CheckBox>
<Button Command="{Binding OpenSettingsDir}">
<TextBlock>
<Run Text="&#xf07c;"
FontFamily="{StaticResource FontAwesomeSolid}" /> Open MayShow Settings Directory</TextBlock>
</Button>
<TextBlock TextWrapping="Wrap" <TextBlock TextWrapping="Wrap"
Foreground="Red" Foreground="Red"
Text="{Binding ErrorMessage}" Text="{Binding ErrorMessage}"
+3 -4
View File
@@ -3,13 +3,12 @@ using Avalonia;
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.Markup.Xaml; using Avalonia.Markup.Xaml;
namespace MayShow.Views namespace MayShow.Views;
public partial class WarningDeleteItem : UserControl
{ {
public partial class WarningDeleteItem : UserControl
{
public WarningDeleteItem() public WarningDeleteItem()
{ {
this.InitializeComponent(); this.InitializeComponent();
} }
}
} }
+1 -1
View File
@@ -3,7 +3,7 @@
<!-- This manifest is used on Windows only. <!-- This manifest is used on Windows only.
Don't remove it as it might cause problems with window transparency and embedded controls. Don't remove it as it might cause problems with window transparency and embedded controls.
For more details visit https://learn.microsoft.com/en-us/windows/win32/sbscs/application-manifests --> For more details visit https://learn.microsoft.com/en-us/windows/win32/sbscs/application-manifests -->
<assemblyIdentity version="1.4.0.0" name="MayShow.Desktop"/> <assemblyIdentity version="1.4.3.0" name="MayShow.Desktop"/>
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1"> <compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
<application> <application>