Skip to content

Commit a4e77c1

Browse files
committed
Add ability to load settings to the GUI fro the commandline, fixes #84. Also added UI to deal with loading channels data.
1 parent 13797f0 commit a4e77c1

File tree

3 files changed

+212
-9
lines changed

3 files changed

+212
-9
lines changed

LibSidWiz/LibSidWiz.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,10 @@
1515
</ItemGroup>
1616
<ItemGroup>
1717
<PackageReference Include="Cyotek.Windows.Forms.ColorPicker" Version="1.7.2" />
18+
<PackageReference Include="Microsoft-WindowsAPICodePack-Shell" Version="1.1.5" />
1819
<PackageReference Include="NAudio" Version="2.2.1" />
1920
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
2021
<PackageReference Include="SkiaSharp" Version="2.88.8" />
2122
<PackageReference Include="SkiaSharp.Views.WindowsForms" Version="2.88.8" />
22-
<PackageReference Include="WindowsAPICodePack-Shell" Version="1.1.1" />
2323
</ItemGroup>
2424
</Project>

SidWiz/HighDpiHelper.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,21 +26,21 @@ private static void AdjustControlImagesDpiScale(IEnumerable controls, float dpiS
2626
{
2727
switch (control)
2828
{
29-
case ButtonBase button when button.Image != null:
29+
case ButtonBase { Image: not null } button:
3030
button.Image = ScaleImage(button.Image, dpiScale);
3131
break;
3232
case SplitContainer splitContainer:
3333
splitContainer.SplitterDistance = (int)(splitContainer.SplitterDistance * dpiScale);
3434
break;
35-
case TabControl tabControl when tabControl.ImageList != null:
35+
case TabControl { ImageList: not null } tabControl:
3636
{
3737
var imageList = new ImageList
3838
{
3939
ImageSize = ScaleSize(tabControl.ImageList.ImageSize, dpiScale),
4040
ColorDepth = ColorDepth.Depth32Bit
4141
};
4242

43-
for (int i = 0 ; i < tabControl.ImageList.Images.Count; ++i)
43+
for (var i = 0 ; i < tabControl.ImageList.Images.Count; ++i)
4444
{
4545
imageList.Images.Add(
4646
tabControl.ImageList.Images.Keys[i],
@@ -98,7 +98,7 @@ private static Image ScaleImage(Image image, float dpiScale)
9898
using (var g = Graphics.FromImage(newBitmap))
9999
{
100100
g.PixelOffsetMode = PixelOffsetMode.HighQuality;
101-
g.InterpolationMode = InterpolationMode.HighQualityBicubic;
101+
g.InterpolationMode = InterpolationMode.NearestNeighbor;
102102
g.DrawImage(image, new Rectangle(new Point(), newSize));
103103
}
104104

SidWiz/SidWizPlusGUI.cs

Lines changed: 207 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
using LibSidWiz;
1717
using LibSidWiz.Outputs;
1818
using LibSidWiz.Triggers;
19+
using Microsoft.WindowsAPICodePack.Dialogs;
1920
using Newtonsoft.Json;
2021
using SkiaSharp;
2122

@@ -642,7 +643,7 @@ private Channel GetClickedChannel(int clickX, int clickY)
642643
var previewAspectRatio = (double) Preview.Width / Preview.Height;
643644
if (previewAspectRatio > imageAspectRatio)
644645
{
645-
// Preview is wider, we have pillarboxing
646+
// Preview is wider, we have pillar-boxing
646647
// So the y one is correct, x needs to be modified:
647648
// +--+-----+--+
648649
// | | | |
@@ -1033,6 +1034,14 @@ private void Initialize(object sender, EventArgs e)
10331034
toolStrip1.Items.Insert(indexToInsertAt, newItem);
10341035
}
10351036
}
1037+
1038+
// Load settings if passed on the commandline
1039+
foreach (var arg in Environment.GetCommandLineArgs()
1040+
.Skip(1)
1041+
.Where(x => x.ToLowerInvariant().EndsWith(".sidwizplus.json")))
1042+
{
1043+
LoadSettingsFromFile(arg);
1044+
}
10361045
}
10371046

10381047
private void RemoveSilentChannels(object sender, EventArgs e)
@@ -1097,11 +1106,162 @@ private void LoadProject(object sender, EventArgs e)
10971106
return;
10981107
}
10991108

1109+
LoadSettingsFromFile(ofd.FileName);
1110+
}
1111+
1112+
private void LoadSettingsFromFile(string path)
1113+
{
11001114
lock (_settings)
11011115
{
1102-
JsonConvert.PopulateObject(File.ReadAllText(ofd.FileName), _settings);
1116+
var newSettings = JsonConvert.DeserializeObject<Settings>(File.ReadAllText(path));
11031117

1104-
foreach (var channel in _settings.Channels)
1118+
// We inspect to decide what to do...
1119+
var clearExistingChannels = false;
1120+
var discardNewChannels = false;
1121+
var removeMissingFiles = false;
1122+
var removeDuplicates = false;
1123+
if (newSettings.Channels.Any())
1124+
{
1125+
// No new channels means nothing to check...
1126+
1127+
// If we already have channels, ask if the user wants to add or replace.
1128+
if (_settings.Channels.Any())
1129+
{
1130+
using var dialog = new TaskDialog();
1131+
dialog.InstructionText = "Loading new channels";
1132+
dialog.Text =
1133+
$"The settings being loaded contain {newSettings.Channels.Count} channels. What do you want to do?";
1134+
dialog.Cancelable = true;
1135+
dialog.Icon = TaskDialogStandardIcon.Warning;
1136+
dialog.StandardButtons = TaskDialogStandardButtons.Cancel;
1137+
dialog.DetailsExpandedText = $"""
1138+
Existing channels:
1139+
{string.Join("\n", PrintFilenames(_settings.Channels, null))}
1140+
New channels:
1141+
{string.Join("\n", PrintFilenames(newSettings.Channels, _settings.Channels))}
1142+
""";
1143+
AddDialogButton(dialog, "Add", "Add to the existing channels", () => {});
1144+
AddDialogButton(dialog, "Replace", "Replace the existing channels",
1145+
() => { clearExistingChannels = true; });
1146+
AddDialogButton(dialog, "Discard", "Discard the new channels",
1147+
() => { discardNewChannels = true; });
1148+
1149+
if (dialog.Show() == TaskDialogResult.Cancel)
1150+
{
1151+
return;
1152+
}
1153+
}
1154+
}
1155+
1156+
if (!discardNewChannels && !clearExistingChannels)
1157+
{
1158+
// If the new channels refer to files that don't exist, the user may not want them.
1159+
var missingFiles = newSettings.Channels
1160+
.Select(x => x.Filename).Where(x => !File.Exists(x))
1161+
.Distinct()
1162+
.ToList();
1163+
1164+
if (missingFiles.Any())
1165+
{
1166+
using var dialog = new TaskDialog();
1167+
dialog.InstructionText = "Missing audio files";
1168+
dialog.Text =
1169+
$"The settings being loaded contain {newSettings.Channels.Count} channels, but {missingFiles.Count} refer to files that don't exist. Do you want to add them anyway?";
1170+
dialog.Cancelable = true;
1171+
dialog.Icon = TaskDialogStandardIcon.Warning;
1172+
dialog.StandardButtons = TaskDialogStandardButtons.Yes | TaskDialogStandardButtons.No |
1173+
TaskDialogStandardButtons.Cancel;
1174+
dialog.DetailsExpandedText = string.Join("\n", PrintFilenames(newSettings.Channels, null));
1175+
1176+
switch (dialog.Show())
1177+
{
1178+
case TaskDialogResult.Cancel:
1179+
return;
1180+
case TaskDialogResult.Yes:
1181+
removeMissingFiles = true;
1182+
// Also clear the channels from the settings for the next check
1183+
newSettings.Channels.RemoveAll(x => missingFiles.Contains(x.Filename));
1184+
break;
1185+
}
1186+
}
1187+
1188+
// Now also check for duplicates
1189+
if (_settings.Channels.Any())
1190+
{
1191+
var duplicates = newSettings.Channels
1192+
.Where(x => _settings.Channels.Any(y =>
1193+
Path.GetFullPath(y.Filename) == Path.GetFullPath(x.Filename)))
1194+
.ToList();
1195+
if (duplicates.Any())
1196+
{
1197+
using var dialog = new TaskDialog();
1198+
dialog.InstructionText = "Duplicate audio files";
1199+
dialog.Text =
1200+
$"The settings being loaded contain {duplicates.Count} channels where the file is already loaded. Do you want to add them anyway?";
1201+
dialog.Cancelable = true;
1202+
dialog.Icon = TaskDialogStandardIcon.Warning;
1203+
dialog.StandardButtons = TaskDialogStandardButtons.Yes | TaskDialogStandardButtons.No |
1204+
TaskDialogStandardButtons.Cancel;
1205+
dialog.DetailsExpandedText = "New channels:\n" + string.Join("\n",
1206+
PrintFilenames(newSettings.Channels, _settings.Channels));
1207+
1208+
switch (dialog.Show())
1209+
{
1210+
case TaskDialogResult.Cancel:
1211+
return;
1212+
case TaskDialogResult.No:
1213+
removeDuplicates = true;
1214+
break;
1215+
}
1216+
}
1217+
}
1218+
}
1219+
1220+
// Deserialize again on top, so partial JSON loads will work
1221+
if (clearExistingChannels)
1222+
{
1223+
_settings.Channels.Clear();
1224+
}
1225+
var countOfOldChannels = _settings.Channels.Count;
1226+
JsonConvert.PopulateObject(File.ReadAllText(path), _settings);
1227+
if (discardNewChannels)
1228+
{
1229+
var oldChannels = _settings.Channels.Take(countOfOldChannels).ToList();
1230+
_settings.Channels.Clear();
1231+
_settings.Channels.AddRange(oldChannels);
1232+
}
1233+
1234+
// Then remove channels as specified
1235+
if (removeMissingFiles)
1236+
{
1237+
var channelsToRemove = _settings.Channels
1238+
.Skip(countOfOldChannels)
1239+
.Where(x => !File.Exists(x.Filename))
1240+
.ToList();
1241+
foreach (var channel in channelsToRemove)
1242+
{
1243+
_settings.Channels.Remove(channel);
1244+
}
1245+
}
1246+
1247+
if (removeDuplicates)
1248+
{
1249+
var oldChannels = _settings.Channels
1250+
.Take(countOfOldChannels)
1251+
.Select(x => Path.GetFullPath(x.Filename))
1252+
.ToList();
1253+
var channelsToRemove = _settings.Channels
1254+
.Skip(countOfOldChannels)
1255+
.Where(x => oldChannels.Contains(Path.GetFullPath(x.Filename)))
1256+
.ToList();
1257+
foreach (var channel in channelsToRemove)
1258+
{
1259+
_settings.Channels.Remove(channel);
1260+
}
1261+
}
1262+
1263+
// Finally, trigger a load on the new channels (and attach our event handler)
1264+
foreach (var channel in _settings.Channels.Skip(countOfOldChannels))
11051265
{
11061266
channel.Changed += ChannelOnChanged;
11071267
channel.LoadDataAsync();
@@ -1111,6 +1271,49 @@ private void LoadProject(object sender, EventArgs e)
11111271
}
11121272
}
11131273

1274+
private static IEnumerable<string> PrintFilenames(List<Channel> channels, List<Channel> existingChannels)
1275+
{
1276+
foreach (var channel in channels)
1277+
{
1278+
string icon;
1279+
if (!File.Exists(channel.Filename))
1280+
{
1281+
icon = "❌";
1282+
}
1283+
else if (
1284+
existingChannels != null &&
1285+
!existingChannels.Contains(channel) &&
1286+
existingChannels.Any(x => x.Filename == channel.Filename))
1287+
{
1288+
icon = "💥";
1289+
}
1290+
else
1291+
{
1292+
icon = "🔉";
1293+
}
1294+
1295+
yield return $@"{icon} {channel.Filename}";
1296+
}
1297+
yield return "❌ = file not found";
1298+
yield return "💥 = file is already present";
1299+
}
1300+
1301+
private static void AddDialogButton(TaskDialog dialog, string text, string instruction, Action action)
1302+
{
1303+
var button = new TaskDialogCommandLink
1304+
{
1305+
Text = text,
1306+
Instruction = instruction,
1307+
};
1308+
button.Click += (_, _) =>
1309+
{
1310+
action();
1311+
// ReSharper disable once AccessToDisposedClosure
1312+
dialog.Close(TaskDialogResult.Ok);
1313+
};
1314+
dialog.Controls.Add(button);
1315+
}
1316+
11141317
private void CopyChannelSettings(object sender, EventArgs e)
11151318
{
11161319
if (PropertyGrid.SelectedObject is not Channel source)
@@ -1240,7 +1443,7 @@ private void VideoCodec_DropDown(object sender, EventArgs e)
12401443
try
12411444
{
12421445
// We run FFMPEG to find what codecs it supports
1243-
using var process = new ProcessWrapper(_programSettings.FfmpegPath, "-encoders", false, true);
1446+
using var process = new ProcessWrapper(_programSettings.FfmpegPath, "-encoders");
12441447
process.WaitForExit();
12451448
foreach (var grouping in process
12461449
.Lines()

0 commit comments

Comments
 (0)