Add config import and export
This commit is contained in:
@@ -19,15 +19,17 @@
|
|||||||
|
|
||||||
<Border DockPanel.Dock="Bottom" BorderThickness="1,0,0,0" Padding="0,12,0,0"
|
<Border DockPanel.Dock="Bottom" BorderThickness="1,0,0,0" Padding="0,12,0,0"
|
||||||
BorderBrush="{DynamicResource AppBorderBrush}">
|
BorderBrush="{DynamicResource AppBorderBrush}">
|
||||||
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right">
|
<WrapPanel HorizontalAlignment="Right">
|
||||||
<Button Content="Save" Click="Save_Click" Margin="0,0,8,0"/>
|
<Button Content="Save" Click="Save_Click" Margin="0,0,8,0"/>
|
||||||
<Button Content="Backup Config" Click="BackupConfig_Click" Margin="0,0,8,0"/>
|
<Button Content="Backup Config" Click="BackupConfig_Click" Margin="0,0,8,0"/>
|
||||||
<Button Content="Restore Config" Click="RestoreConfig_Click" Margin="0,0,8,0"/>
|
<Button Content="Restore Config" Click="RestoreConfig_Click" Margin="0,0,8,0"/>
|
||||||
|
<Button Content="Export Config" Click="ExportConfig_Click" Margin="0,0,8,0"/>
|
||||||
|
<Button Content="Import Config" Click="ImportConfig_Click" Margin="0,0,8,0"/>
|
||||||
<Button Content="Reload Config" Click="ReloadConfig_Click" Margin="0,0,8,0"/>
|
<Button Content="Reload Config" Click="ReloadConfig_Click" Margin="0,0,8,0"/>
|
||||||
<Button Content="Open Config Folder" Click="OpenConfigFolder_Click" Margin="0,0,8,0"/>
|
<Button Content="Open Config Folder" Click="OpenConfigFolder_Click" Margin="0,0,8,0"/>
|
||||||
<Button Content="About" Click="About_Click" Margin="0,0,8,0"/>
|
<Button Content="About" Click="About_Click" Margin="0,0,8,0"/>
|
||||||
<Button Content="Close" Click="Close_Click"/>
|
<Button Content="Close" Click="Close_Click"/>
|
||||||
</StackPanel>
|
</WrapPanel>
|
||||||
</Border>
|
</Border>
|
||||||
|
|
||||||
<Grid>
|
<Grid>
|
||||||
|
|||||||
@@ -636,6 +636,77 @@ public partial class MainWindow : Window
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void ExportConfig_Click(object sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
if (controller is null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ApplyConfigFields();
|
||||||
|
using SaveFileDialog dialog = new()
|
||||||
|
{
|
||||||
|
Title = "Export launcher config",
|
||||||
|
Filter = "JSON config files|*.json|All files|*.*",
|
||||||
|
FileName = $"TaskbarLauncher-config-{DateTime.Now:yyyyMMdd-HHmmss}.json",
|
||||||
|
InitialDirectory = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments)
|
||||||
|
};
|
||||||
|
|
||||||
|
if (dialog.ShowDialog() != System.Windows.Forms.DialogResult.OK)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
string exportPath = ConfigService.ExportTo(dialog.FileName, controller.Config);
|
||||||
|
StatusText.Text = $"Exported config to {exportPath}";
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
MessageBox.Show($"Could not export config.\n\n{ex.Message}", "Taskbar Launcher");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ImportConfig_Click(object sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
using OpenFileDialog dialog = new()
|
||||||
|
{
|
||||||
|
Title = "Import launcher config",
|
||||||
|
Filter = "JSON config files|*.json|All files|*.*",
|
||||||
|
InitialDirectory = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments)
|
||||||
|
};
|
||||||
|
|
||||||
|
if (dialog.ShowDialog() != System.Windows.Forms.DialogResult.OK)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
MessageBoxResult result = MessageBox.Show(
|
||||||
|
"Import this config file and replace the current launcher config?\n\nA local backup will be created first.",
|
||||||
|
"Taskbar Launcher",
|
||||||
|
MessageBoxButton.YesNo,
|
||||||
|
MessageBoxImage.Question);
|
||||||
|
|
||||||
|
if (result != MessageBoxResult.Yes)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
string backupPath = ConfigService.Backup();
|
||||||
|
ConfigService.ImportFrom(dialog.FileName);
|
||||||
|
controller?.ReloadConfig();
|
||||||
|
RefreshView();
|
||||||
|
StatusText.Text = $"Imported config from {dialog.FileName}. Previous config backed up to {backupPath}";
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
MessageBox.Show($"Could not import config.\n\n{ex.Message}", "Taskbar Launcher");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private List<int>? FindItemPath(LauncherItem item)
|
private List<int>? FindItemPath(LauncherItem item)
|
||||||
{
|
{
|
||||||
if (controller is null)
|
if (controller is null)
|
||||||
|
|||||||
@@ -61,18 +61,35 @@ public static class ConfigService
|
|||||||
return backupPath;
|
return backupPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static LauncherConfig Restore(string sourcePath)
|
public static string ExportTo(string destinationPath, LauncherConfig config)
|
||||||
{
|
{
|
||||||
if (!File.Exists(sourcePath))
|
if (string.IsNullOrWhiteSpace(destinationPath))
|
||||||
{
|
{
|
||||||
throw new FileNotFoundException("The selected config file was not found.", sourcePath);
|
throw new ArgumentException("Choose a destination path for the exported config.", nameof(destinationPath));
|
||||||
}
|
}
|
||||||
|
|
||||||
string json = File.ReadAllText(sourcePath);
|
string? destinationFolder = Path.GetDirectoryName(destinationPath);
|
||||||
LauncherConfig config = JsonSerializer.Deserialize<LauncherConfig>(json, JsonOptions)
|
if (!string.IsNullOrWhiteSpace(destinationFolder))
|
||||||
?? throw new InvalidDataException("The selected config file could not be read.");
|
{
|
||||||
|
Directory.CreateDirectory(destinationFolder);
|
||||||
|
}
|
||||||
|
|
||||||
NormalizeDisplayModes(config);
|
NormalizeDisplayModes(config);
|
||||||
|
string json = JsonSerializer.Serialize(config, JsonOptions);
|
||||||
|
File.WriteAllText(destinationPath, json);
|
||||||
|
return destinationPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static LauncherConfig ImportFrom(string sourcePath)
|
||||||
|
{
|
||||||
|
LauncherConfig config = ReadConfigFile(sourcePath);
|
||||||
|
Save(config);
|
||||||
|
return config;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static LauncherConfig Restore(string sourcePath)
|
||||||
|
{
|
||||||
|
LauncherConfig config = ReadConfigFile(sourcePath);
|
||||||
Save(config);
|
Save(config);
|
||||||
return config;
|
return config;
|
||||||
}
|
}
|
||||||
@@ -135,6 +152,61 @@ public static class ConfigService
|
|||||||
return config;
|
return config;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static LauncherConfig ReadConfigFile(string sourcePath)
|
||||||
|
{
|
||||||
|
if (!File.Exists(sourcePath))
|
||||||
|
{
|
||||||
|
throw new FileNotFoundException("The selected config file was not found.", sourcePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
string json = File.ReadAllText(sourcePath);
|
||||||
|
LauncherConfig config;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
config = JsonSerializer.Deserialize<LauncherConfig>(json, JsonOptions)
|
||||||
|
?? throw new InvalidDataException("The selected config file could not be read.");
|
||||||
|
}
|
||||||
|
catch (JsonException ex)
|
||||||
|
{
|
||||||
|
throw new InvalidDataException("The selected file is not a valid Taskbar Launcher config.", ex);
|
||||||
|
}
|
||||||
|
|
||||||
|
ValidateConfig(config);
|
||||||
|
NormalizeDisplayModes(config);
|
||||||
|
return config;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void ValidateConfig(LauncherConfig config)
|
||||||
|
{
|
||||||
|
if (config.Items is null)
|
||||||
|
{
|
||||||
|
throw new InvalidDataException("The selected config is missing launcher items.");
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (LauncherItem item in config.Items)
|
||||||
|
{
|
||||||
|
ValidateItem(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void ValidateItem(LauncherItem item)
|
||||||
|
{
|
||||||
|
if (item.Children is null)
|
||||||
|
{
|
||||||
|
throw new InvalidDataException($"The item '{item.Title}' has an invalid children list.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!Enum.IsDefined(item.Type))
|
||||||
|
{
|
||||||
|
throw new InvalidDataException($"The item '{item.Title}' has an invalid type.");
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (LauncherItem child in item.Children)
|
||||||
|
{
|
||||||
|
ValidateItem(child);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static void NormalizeDisplayModes(LauncherConfig config)
|
private static void NormalizeDisplayModes(LauncherConfig config)
|
||||||
{
|
{
|
||||||
foreach (LauncherItem item in config.Items)
|
foreach (LauncherItem item in config.Items)
|
||||||
|
|||||||
Reference in New Issue
Block a user