mirror of
https://github.com/rmcrackan/Libation.git
synced 2026-01-02 10:58:43 -05:00
Compare commits
30 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d857882220 | ||
|
|
d731db4036 | ||
|
|
ca5b40b176 | ||
|
|
b29ec26f63 | ||
|
|
7569b01bd0 | ||
|
|
6465b0a885 | ||
|
|
5e99cb6f02 | ||
|
|
d737cd2199 | ||
|
|
2d2907e076 | ||
|
|
05c454dce4 | ||
|
|
e64a9d2adf | ||
|
|
6252f015b3 | ||
|
|
7ada0082a9 | ||
|
|
826e53c9cb | ||
|
|
2248d7b24e | ||
|
|
69918c2587 | ||
|
|
1991bf5b4d | ||
|
|
756d387238 | ||
|
|
8d73f5cc7e | ||
|
|
4a65d6bbd3 | ||
|
|
10a1b56b3c | ||
|
|
66fb392b7f | ||
|
|
49ef96055c | ||
|
|
cb4a209f69 | ||
|
|
255e18eb5e | ||
|
|
7e1ec47b46 | ||
|
|
40c725b8c2 | ||
|
|
5d0937dc48 | ||
|
|
bff81bfc4b | ||
|
|
aa7c159985 |
@@ -1,6 +1,6 @@
|
||||
## [Download Libation](https://github.com/rmcrackan/Libation/releases/latest)
|
||||
|
||||
### If you found this useful, tell a friend. If you found this REALLY useful, you can click here to [PayPal.me](https://paypal.me/mcrackan?locale.x=en_us)
|
||||
### If you found this useful, tell a friend. If you found this REALLY useful, you can click here to [PayPal.me](https://paypal.me/MBucari?locale.x=en_us)
|
||||
...or just tell more friends. As long as I'm maintaining this software, it will remain **free** and **open source**.
|
||||
|
||||
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
## [Download Libation](https://github.com/rmcrackan/Libation/releases/latest)
|
||||
|
||||
### If you found this useful, tell a friend. If you found this REALLY useful, you can click here to [PayPal.me](https://paypal.me/mcrackan?locale.x=en_us)
|
||||
### If you found this useful, tell a friend. If you found this REALLY useful, you can click here to [PayPal.me](https://paypal.me/MBucari?locale.x=en_us)
|
||||
...or just tell more friends. As long as I'm maintaining this software, it will remain **free** and **open source**.
|
||||
|
||||
|
||||
### Disclaimer
|
||||
The docker image is provided as-is. We hope it can be useful to you but it is not officially supported.
|
||||
|
||||
### Setup
|
||||
In order to use the docker image, you'll need to provide it with a copy of the `AccountsSettings.json`, `Settings.json`, and `LibationContext.db` files. These files can usually be found in the Libation folder in your user's home directory. If you haven't run Libation yet, you'll need to launch it to generate these files and setup your accounts. Once you have them, copy these files to a new location, such as `/opt/libation/config`. Before using them we'll need to make a couple edits so that the filepaths referenced are correct when running from the docker image.
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
## [Download Libation](https://github.com/rmcrackan/Libation/releases/latest)
|
||||
|
||||
### If you found this useful, tell a friend. If you found this REALLY useful, you can click here to [PayPal.me](https://paypal.me/mcrackan?locale.x=en_us)
|
||||
### If you found this useful, tell a friend. If you found this REALLY useful, you can click here to [PayPal.me](https://paypal.me/MBucari?locale.x=en_us)
|
||||
...or just tell more friends. As long as I'm maintaining this software, it will remain **free** and **open source**.
|
||||
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
## [Download Libation](https://github.com/rmcrackan/Libation/releases/latest)
|
||||
|
||||
### If you found this useful, tell a friend. If you found this REALLY useful, you can click here to [PayPal.me](https://paypal.me/mcrackan?locale.x=en_us)
|
||||
### If you found this useful, tell a friend. If you found this REALLY useful, you can click here to [PayPal.me](https://paypal.me/MBucari?locale.x=en_us)
|
||||
...or just tell more friends. As long as I'm maintaining this software, it will remain **free** and **open source**.
|
||||
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
## [Download Libation](https://github.com/rmcrackan/Libation/releases/latest)
|
||||
|
||||
### If you found this useful, tell a friend. If you found this REALLY useful, you can click here to [PayPal.me](https://paypal.me/mcrackan?locale.x=en_us)
|
||||
### If you found this useful, tell a friend. If you found this REALLY useful, you can click here to [PayPal.me](https://paypal.me/MBucari?locale.x=en_us)
|
||||
...or just tell more friends. As long as I'm maintaining this software, it will remain **free** and **open source**.
|
||||
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
## [Download Libation](https://github.com/rmcrackan/Libation/releases/latest)
|
||||
|
||||
### If you found this useful, tell a friend. If you found this REALLY useful, you can click here to [PayPal.me](https://paypal.me/mcrackan?locale.x=en_us)
|
||||
### If you found this useful, tell a friend. If you found this REALLY useful, you can click here to [PayPal.me](https://paypal.me/MBucari?locale.x=en_us)
|
||||
...or just tell more friends. As long as I'm maintaining this software, it will remain **free** and **open source**.
|
||||
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
## [Download Libation](https://github.com/rmcrackan/Libation/releases/latest)
|
||||
|
||||
### If you found this useful, tell a friend. If you found this REALLY useful, you can click here to [PayPal.me](https://paypal.me/mcrackan?locale.x=en_us)
|
||||
### If you found this useful, tell a friend. If you found this REALLY useful, you can click here to [PayPal.me](https://paypal.me/MBucari?locale.x=en_us)
|
||||
...or just tell more friends. As long as I'm maintaining this software, it will remain **free** and **open source**.
|
||||
|
||||
# Table of Contents
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="AAXClean.Codecs" Version="1.0.1" />
|
||||
<PackageReference Include="AAXClean.Codecs" Version="1.0.2" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<Version>10.0.2.1</Version>
|
||||
<Version>10.0.4.1</Version>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Octokit" Version="5.0.2" />
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
<Application xmlns="https://github.com/avaloniaui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:local="using:LibationAvalonia"
|
||||
x:Class="LibationAvalonia.App">
|
||||
xmlns:controls="using:LibationAvalonia.Controls"
|
||||
x:Class="LibationAvalonia.App"
|
||||
Name="Libation">
|
||||
|
||||
<Application.DataTemplates>
|
||||
<local:ViewLocator/>
|
||||
@@ -68,5 +70,19 @@
|
||||
<Setter Property="Background" Value="{DynamicResource SystemControlBackgroundBaseLowBrush}" />
|
||||
</Style>
|
||||
</Style>
|
||||
<Style Selector="controls|LinkLabel">
|
||||
<Setter Property="Foreground" Value="{DynamicResource HyperlinkNew}"/>
|
||||
<Setter Property="ForegroundVisited" Value="{DynamicResource HyperlinkVisited}"/>
|
||||
</Style>
|
||||
<Style Selector="Button">
|
||||
<Setter Property="VerticalContentAlignment" Value="Center"/>
|
||||
</Style>
|
||||
</Application.Styles>
|
||||
|
||||
<NativeMenu.Menu>
|
||||
<NativeMenu>
|
||||
<NativeMenuItem Header="About Libation" />
|
||||
</NativeMenu>
|
||||
</NativeMenu.Menu>
|
||||
|
||||
</Application>
|
||||
@@ -2,6 +2,7 @@
|
||||
using Avalonia;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Controls.ApplicationLifetimes;
|
||||
using Avalonia.Input;
|
||||
using Avalonia.Markup.Xaml;
|
||||
using Avalonia.Media;
|
||||
using Avalonia.Platform;
|
||||
@@ -13,6 +14,8 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
using ReactiveUI;
|
||||
using DataLayer;
|
||||
|
||||
namespace LibationAvalonia
|
||||
{
|
||||
@@ -24,7 +27,6 @@ namespace LibationAvalonia
|
||||
public static IBrush ProcessQueueBookCancelledBrush { get; private set; }
|
||||
public static IBrush ProcessQueueBookDefaultBrush { get; private set; }
|
||||
public static IBrush SeriesEntryGridBackgroundBrush { get; private set; }
|
||||
public static IBrush HyperlinkVisited { get; private set; }
|
||||
|
||||
public static IAssetLoader AssetLoader { get; private set; }
|
||||
|
||||
@@ -215,9 +217,8 @@ namespace LibationAvalonia
|
||||
LoadStyles();
|
||||
var mainWindow = new MainWindow();
|
||||
desktop.MainWindow = MainWindow = mainWindow;
|
||||
mainWindow.RestoreSizeAndLocation(Configuration.Instance);
|
||||
mainWindow.OnLoad();
|
||||
mainWindow.OnLibraryLoaded(LibraryTask.GetAwaiter().GetResult());
|
||||
mainWindow.RestoreSizeAndLocation(Configuration.Instance);
|
||||
mainWindow.Show();
|
||||
}
|
||||
|
||||
@@ -228,7 +229,6 @@ namespace LibationAvalonia
|
||||
ProcessQueueBookCancelledBrush = AvaloniaUtils.GetBrushFromResources(nameof(ProcessQueueBookCancelledBrush));
|
||||
SeriesEntryGridBackgroundBrush = AvaloniaUtils.GetBrushFromResources(nameof(SeriesEntryGridBackgroundBrush));
|
||||
ProcessQueueBookDefaultBrush = AvaloniaUtils.GetBrushFromResources(nameof(ProcessQueueBookDefaultBrush));
|
||||
HyperlinkVisited = AvaloniaUtils.GetBrushFromResources(nameof(HyperlinkVisited));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -64,6 +64,33 @@
|
||||
M7.2,0.8 a 0.8,0.8 0 0 1 1.6,0 v8 l0.9929,-0.9929 a 0.8,0.8 0 0 1 1.1314,1.1314 l-2.3586,2.3586
|
||||
a 0.8,0.8 0 0 1 -1.1314,0 l-2.3586,-2.3586 a 0.8,0.8 0 0 1 1.1314,-1.1314 l0.9929,0.9929 v8
|
||||
</StreamGeometry>
|
||||
|
||||
<StreamGeometry x:Key="LibationCheersIcon">
|
||||
M139,2
|
||||
A 192,200 0 0 0 103,84
|
||||
A 222,334 41 0 0 241,320
|
||||
V478
|
||||
H160
|
||||
A 16,16 0 0 0 160,510
|
||||
H352
|
||||
A16 16 0 0 0 352,478
|
||||
H271
|
||||
V320
|
||||
A 222,334 -41 0 0 409,84
|
||||
A 192,200 0 0 0 373,2
|
||||
M355,32
|
||||
A 192,200 0 0 1 381,127
|
||||
A 187.5,334 -35 0 1 256,286
|
||||
A 187.5,334 35 0 1 131,127
|
||||
A 192,200 0 0 1 157,32
|
||||
H355
|
||||
M146,147
|
||||
A 168,300 35 0 0 256,270
|
||||
A 168,300 -35 0 0 366,128
|
||||
S 360,50 280,110
|
||||
S 192,128 147,147
|
||||
</StreamGeometry>
|
||||
|
||||
</ResourceDictionary>
|
||||
</Styles.Resources>
|
||||
</Styles>
|
||||
|
||||
@@ -5,29 +5,30 @@
|
||||
xmlns:controls="clr-namespace:LibationAvalonia.Controls"
|
||||
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
||||
x:Class="LibationAvalonia.Controls.DirectoryOrCustomSelectControl">
|
||||
<Grid ColumnDefinitions="Auto,*" RowDefinitions="Auto,Auto">
|
||||
<controls:DirectorySelectControl
|
||||
Grid.Column="1"
|
||||
Grid.Row="0"
|
||||
Name="directorySelectControl"
|
||||
SubDirectory="{Binding $parent.SubDirectory}"
|
||||
KnownDirectories="{Binding $parent.KnownDirectories}" />
|
||||
|
||||
<Grid ColumnDefinitions="Auto,*" RowDefinitions="Auto,Auto" Name="grid">
|
||||
<controls:DirectorySelectControl
|
||||
Grid.Column="1"
|
||||
Grid.Row="0"
|
||||
IsEnabled="{Binding KnownChecked}"
|
||||
SelectedDirectory="{Binding SelectedDirectory, Mode=TwoWay}"
|
||||
SubDirectory="{Binding $parent[1].SubDirectory}"
|
||||
KnownDirectories="{Binding $parent[1].KnownDirectories}" />
|
||||
|
||||
<RadioButton
|
||||
Grid.Column="0"
|
||||
Grid.Row="0"
|
||||
Name="knownDirRadio"
|
||||
IsChecked="{Binding KnownChecked, Mode=TwoWay}" />
|
||||
IsChecked="{Binding KnownChecked, Mode=TwoWay}"/>
|
||||
|
||||
<RadioButton
|
||||
Grid.Column="0"
|
||||
Grid.Row="1"
|
||||
Name="customDirRadio"
|
||||
IsChecked="{Binding CustomChecked, Mode=TwoWay}" />
|
||||
IsChecked="{Binding CustomChecked, Mode=TwoWay}"/>
|
||||
|
||||
<Grid Grid.Column="1" Grid.Row="1" ColumnDefinitions="*,Auto">
|
||||
<TextBox IsEnabled="{Binding CustomChecked}" Name="customDirTbox" Grid.Column="0" IsReadOnly="True" Text="{Binding CustomDir, Mode=TwoWay}" />
|
||||
<Button Name="customDirBrowseBtn" Grid.Column="1" Content="..." Margin="5,0,0,0" Padding="10,0,10,0" VerticalAlignment="Stretch" />
|
||||
<Grid Grid.Column="1" Grid.Row="1" ColumnDefinitions="*,Auto"
|
||||
IsEnabled="{Binding CustomChecked}">
|
||||
<TextBox Grid.Column="0" IsReadOnly="True" Text="{Binding CustomDir, Mode=TwoWay}" />
|
||||
<Button Grid.Column="1" Content="..." Margin="5,0,0,0" Padding="10,0,10,0" Click="CustomDirBrowseBtn_Click" VerticalAlignment="Stretch" />
|
||||
</Grid>
|
||||
</Grid>
|
||||
</UserControl>
|
||||
|
||||
@@ -36,55 +36,49 @@ namespace LibationAvalonia.Controls
|
||||
get => GetValue(SubDirectoryProperty);
|
||||
set => SetValue(SubDirectoryProperty, value);
|
||||
}
|
||||
CustomState customStates = new();
|
||||
|
||||
private readonly DirectoryState directoryState = new();
|
||||
|
||||
public DirectoryOrCustomSelectControl()
|
||||
{
|
||||
InitializeComponent();
|
||||
|
||||
customDirBrowseBtn = this.Find<Button>(nameof(customDirBrowseBtn));
|
||||
directorySelectControl = this.Find<DirectorySelectControl>(nameof(directorySelectControl));
|
||||
grid.DataContext = directoryState;
|
||||
|
||||
this.Find<TextBox>(nameof(customDirTbox)).DataContext = customStates;
|
||||
this.Find<RadioButton>(nameof(knownDirRadio)).DataContext = customStates;
|
||||
this.Find<RadioButton>(nameof(customDirRadio)).DataContext = customStates;
|
||||
|
||||
customStates.PropertyChanged += CheckStates_PropertyChanged;
|
||||
customDirBrowseBtn.Click += CustomDirBrowseBtn_Click;
|
||||
directoryState.PropertyChanged += DirectoryState_PropertyChanged;
|
||||
PropertyChanged += DirectoryOrCustomSelectControl_PropertyChanged;
|
||||
directorySelectControl.PropertyChanged += DirectorySelectControl_PropertyChanged;
|
||||
}
|
||||
|
||||
private class CustomState : ViewModels.ViewModelBase
|
||||
private void DirectoryState_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
|
||||
{
|
||||
if (e.PropertyName is nameof(DirectoryState.SelectedDirectory) or nameof(DirectoryState.KnownChecked) &&
|
||||
directoryState.KnownChecked &&
|
||||
directoryState.SelectedDirectory is Configuration.KnownDirectories kdir &&
|
||||
kdir is not Configuration.KnownDirectories.None)
|
||||
{
|
||||
Directory = kdir is Configuration.KnownDirectories.AppDir ? Configuration.AppDir_Absolute : Configuration.GetKnownDirectoryPath(kdir);
|
||||
}
|
||||
else if (e.PropertyName is nameof(DirectoryState.CustomDir) or nameof(DirectoryState.CustomChecked) &&
|
||||
directoryState.CustomChecked &&
|
||||
directoryState.CustomDir is not null)
|
||||
{
|
||||
Directory = directoryState.CustomDir;
|
||||
}
|
||||
}
|
||||
|
||||
private class DirectoryState : ViewModels.ViewModelBase
|
||||
{
|
||||
private string _customDir;
|
||||
private string _subDirectory;
|
||||
private bool _knownChecked;
|
||||
private bool _customChecked;
|
||||
private Configuration.KnownDirectories? _selectedDirectory;
|
||||
public string CustomDir { get => _customDir; set => this.RaiseAndSetIfChanged(ref _customDir, value); }
|
||||
public bool KnownChecked
|
||||
{
|
||||
get => _knownChecked;
|
||||
set
|
||||
{
|
||||
this.RaiseAndSetIfChanged(ref _knownChecked, value);
|
||||
if (value)
|
||||
CustomChecked = false;
|
||||
else if (!CustomChecked)
|
||||
CustomChecked = true;
|
||||
}
|
||||
}
|
||||
public bool CustomChecked
|
||||
{
|
||||
get => _customChecked;
|
||||
set
|
||||
{
|
||||
this.RaiseAndSetIfChanged(ref _customChecked, value);
|
||||
if (value)
|
||||
KnownChecked = false;
|
||||
else if (!KnownChecked)
|
||||
KnownChecked = true;
|
||||
}
|
||||
}
|
||||
public string SubDirectory { get => _subDirectory; set => this.RaiseAndSetIfChanged(ref _subDirectory, value); }
|
||||
public bool KnownChecked { get => _knownChecked; set => this.RaiseAndSetIfChanged(ref _knownChecked, value); }
|
||||
public bool CustomChecked { get => _customChecked; set => this.RaiseAndSetIfChanged(ref _customChecked, value); }
|
||||
|
||||
public Configuration.KnownDirectories? SelectedDirectory { get => _selectedDirectory; set => this.RaiseAndSetIfChanged(ref _selectedDirectory, value); }
|
||||
}
|
||||
|
||||
private async void CustomDirBrowseBtn_Click(object sender, Avalonia.Interactivity.RoutedEventArgs e)
|
||||
@@ -96,43 +90,12 @@ namespace LibationAvalonia.Controls
|
||||
|
||||
var selectedFolders = await (VisualRoot as Window).StorageProvider.OpenFolderPickerAsync(options);
|
||||
|
||||
customStates.CustomDir = selectedFolders.SingleOrDefault()?.Path?.LocalPath ?? customStates.CustomDir;
|
||||
}
|
||||
|
||||
private void CheckStates_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
|
||||
{
|
||||
if (e.PropertyName != nameof(CustomState.CustomDir))
|
||||
{
|
||||
directorySelectControl.IsEnabled = !customStates.CustomChecked;
|
||||
customDirBrowseBtn.IsEnabled = customStates.CustomChecked;
|
||||
}
|
||||
|
||||
setDirectory();
|
||||
}
|
||||
|
||||
|
||||
private void DirectorySelectControl_PropertyChanged(object sender, AvaloniaPropertyChangedEventArgs e)
|
||||
{
|
||||
if (e.Property.Name == nameof(DirectorySelectControl.SelectedDirectory))
|
||||
{
|
||||
setDirectory();
|
||||
}
|
||||
}
|
||||
|
||||
private void setDirectory()
|
||||
{
|
||||
var selectedDir
|
||||
= customStates.CustomChecked ? customStates.CustomDir
|
||||
: directorySelectControl.SelectedDirectory is Configuration.KnownDirectories.AppDir ? Configuration.AppDir_Absolute
|
||||
: Configuration.GetKnownDirectoryPath(directorySelectControl.SelectedDirectory);
|
||||
selectedDir ??= string.Empty;
|
||||
|
||||
Directory = customStates.CustomChecked ? selectedDir : System.IO.Path.Combine(selectedDir, SubDirectory ?? "");
|
||||
directoryState.CustomDir = selectedFolders.SingleOrDefault()?.Path?.LocalPath ?? directoryState.CustomDir;
|
||||
}
|
||||
|
||||
private void DirectoryOrCustomSelectControl_PropertyChanged(object sender, AvaloniaPropertyChangedEventArgs e)
|
||||
{
|
||||
if (e.Property.Name == nameof(Directory) && e.OldValue is null)
|
||||
if (e.Property == DirectoryProperty)
|
||||
{
|
||||
var directory = Directory?.Trim() ?? "";
|
||||
|
||||
@@ -144,19 +107,19 @@ namespace LibationAvalonia.Controls
|
||||
|
||||
if (known is Configuration.KnownDirectories.None)
|
||||
{
|
||||
customStates.CustomChecked = true;
|
||||
customStates.CustomDir = directory;
|
||||
directoryState.CustomDir = noSubDir;
|
||||
directoryState.CustomChecked = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
customStates.KnownChecked = true;
|
||||
directorySelectControl.SelectedDirectory = known;
|
||||
directoryState.SelectedDirectory = known;
|
||||
directoryState.KnownChecked = true;
|
||||
}
|
||||
}
|
||||
else if (e.Property.Name == nameof(KnownDirectories))
|
||||
directorySelectControl.KnownDirectories = KnownDirectories;
|
||||
else if (e.Property.Name == nameof(SubDirectory))
|
||||
directorySelectControl.SubDirectory = SubDirectory;
|
||||
else if (e.Property == KnownDirectoriesProperty &&
|
||||
KnownDirectories.Count > 0 &&
|
||||
directoryState.SelectedDirectory is null or Configuration.KnownDirectories.None)
|
||||
directoryState.SelectedDirectory = KnownDirectories[0];
|
||||
}
|
||||
|
||||
private string RemoveSubDirectoryFromPath(string path)
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
|
||||
<UserControl.Resources>
|
||||
<controls:KnownDirectoryConverter x:Key="KnownDirectoryConverter" />
|
||||
<controls:KnownDirectoryPath x:Key="KnownDirectoryPath" />
|
||||
</UserControl.Resources>
|
||||
|
||||
|
||||
@@ -20,19 +21,26 @@
|
||||
<controls:WheelComboBox
|
||||
HorizontalContentAlignment = "Stretch"
|
||||
HorizontalAlignment = "Stretch"
|
||||
Name="combo"
|
||||
MinHeight="{Binding #displayPathTbox.MinHeight}"
|
||||
SelectedItem="{Binding $parent[1].SelectedDirectory, Mode=TwoWay}"
|
||||
Items="{Binding $parent[1].KnownDirectories}">
|
||||
ItemsSource="{Binding $parent[1].KnownDirectories}">
|
||||
<ComboBox.ItemTemplate>
|
||||
<DataTemplate>
|
||||
|
||||
<TextBlock
|
||||
Text="{Binding, Converter={StaticResource KnownDirectoryConverter}}" />
|
||||
|
||||
<TextBlock Text="{Binding Converter={StaticResource KnownDirectoryConverter}}" />
|
||||
</DataTemplate>
|
||||
</ComboBox.ItemTemplate>
|
||||
</controls:WheelComboBox>
|
||||
<TextBox Margin="0,10,0,10" IsReadOnly="True" Name="displayPathTbox" />
|
||||
<TextBox Margin="0,10,0,10" IsReadOnly="True">
|
||||
<TextBox.Text>
|
||||
<MultiBinding Converter="{StaticResource KnownDirectoryPath}">
|
||||
<MultiBinding.Bindings>
|
||||
<Binding Path="#combo.SelectedItem"/>
|
||||
<Binding Path="$parent[1].SubDirectory"/>
|
||||
</MultiBinding.Bindings>
|
||||
</MultiBinding>
|
||||
</TextBox.Text>
|
||||
</TextBox>
|
||||
</StackPanel>
|
||||
|
||||
</UserControl>
|
||||
|
||||
@@ -8,7 +8,6 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Reactive.Subjects;
|
||||
|
||||
namespace LibationAvalonia.Controls
|
||||
{
|
||||
@@ -26,6 +25,24 @@ namespace LibationAvalonia.Controls
|
||||
return new BindingNotification(new InvalidCastException(), BindingErrorType.Error);
|
||||
}
|
||||
}
|
||||
public class KnownDirectoryPath : IMultiValueConverter
|
||||
{
|
||||
public object Convert(IList<object> values, Type targetType, object parameter, CultureInfo culture)
|
||||
{
|
||||
if (values?.Count == 2 && values[0] is Configuration.KnownDirectories kdir && kdir is not Configuration.KnownDirectories.None)
|
||||
{
|
||||
var subdir = values[1] as string ?? "";
|
||||
var path = kdir is Configuration.KnownDirectories.AppDir ? Configuration.AppDir_Absolute : Configuration.GetKnownDirectoryPath(kdir);
|
||||
return Path.Combine(path, subdir);
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
|
||||
{
|
||||
return new BindingNotification(new InvalidCastException(), BindingErrorType.Error);
|
||||
}
|
||||
}
|
||||
|
||||
public partial class DirectorySelectControl : UserControl
|
||||
{
|
||||
@@ -39,8 +56,8 @@ namespace LibationAvalonia.Controls
|
||||
Configuration.KnownDirectories.LibationFiles
|
||||
};
|
||||
|
||||
public static readonly StyledProperty<Configuration.KnownDirectories> SelectedDirectoryProperty =
|
||||
AvaloniaProperty.Register<DirectorySelectControl, Configuration.KnownDirectories>(nameof(SelectedDirectory));
|
||||
public static readonly StyledProperty<Configuration.KnownDirectories?> SelectedDirectoryProperty =
|
||||
AvaloniaProperty.Register<DirectorySelectControl, Configuration.KnownDirectories?>(nameof(SelectedDirectory));
|
||||
|
||||
public static readonly StyledProperty<List<Configuration.KnownDirectories>> KnownDirectoriesProperty =
|
||||
AvaloniaProperty.Register<DirectorySelectControl, List<Configuration.KnownDirectories>>(nameof(KnownDirectories), DefaultKnownDirectories);
|
||||
@@ -51,25 +68,6 @@ namespace LibationAvalonia.Controls
|
||||
public DirectorySelectControl()
|
||||
{
|
||||
InitializeComponent();
|
||||
|
||||
displayPathTbox = this.Get<TextBox>(nameof(displayPathTbox));
|
||||
displayPathTbox.Bind(TextBox.TextProperty, TextboxPath);
|
||||
PropertyChanged += DirectorySelectControl_PropertyChanged;
|
||||
}
|
||||
|
||||
private Subject<string> TextboxPath = new Subject<string>();
|
||||
|
||||
private void DirectorySelectControl_PropertyChanged(object sender, AvaloniaPropertyChangedEventArgs e)
|
||||
{
|
||||
if (e.Property.Name == nameof(SelectedDirectory))
|
||||
{
|
||||
TextboxPath.OnNext(
|
||||
Path.Combine(
|
||||
SelectedDirectory is Configuration.KnownDirectories.None ? string.Empty
|
||||
: SelectedDirectory is Configuration.KnownDirectories.AppDir ? Configuration.AppDir_Absolute
|
||||
: Configuration.GetKnownDirectoryPath(SelectedDirectory)
|
||||
, SubDirectory ?? string.Empty));
|
||||
}
|
||||
}
|
||||
|
||||
public List<Configuration.KnownDirectories> KnownDirectories
|
||||
@@ -78,7 +76,7 @@ namespace LibationAvalonia.Controls
|
||||
set => SetValue(KnownDirectoriesProperty, value);
|
||||
}
|
||||
|
||||
public Configuration.KnownDirectories SelectedDirectory
|
||||
public Configuration.KnownDirectories? SelectedDirectory
|
||||
{
|
||||
get => GetValue(SelectedDirectoryProperty);
|
||||
set => SetValue(SelectedDirectoryProperty, value);
|
||||
|
||||
@@ -3,49 +3,53 @@
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:controls="clr-namespace:LibationAvalonia.Controls"
|
||||
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
||||
mc:Ignorable="d" d:DesignWidth="200" d:DesignHeight="200"
|
||||
x:Class="LibationAvalonia.Controls.GroupBox">
|
||||
|
||||
<Design.DataContext>
|
||||
</Design.DataContext>
|
||||
|
||||
<ContentControl.Styles>
|
||||
<Style Selector="controls|GroupBox Border">
|
||||
<Setter Property="BorderBrush" Value="DarkGray" />
|
||||
</Style>
|
||||
<Style Selector="controls|GroupBox">
|
||||
<Setter Property="BorderBrush" Value="{DynamicResource SystemBaseMediumLowColor}" />
|
||||
<Setter Property="BorderThickness" Value="1" />
|
||||
<Setter Property="CornerRadius" Value="3" />
|
||||
<Setter Property="Template">
|
||||
<ControlTemplate>
|
||||
<Grid ColumnDefinitions="Auto,*,Auto" RowDefinitions="7,10,*,Auto">
|
||||
|
||||
<Grid ColumnDefinitions="Auto,Auto,*,Auto" RowDefinitions="Auto,*,Auto">
|
||||
<Panel
|
||||
Name="PART_LabelOffsetter"
|
||||
Grid.Column="1"
|
||||
Margin="8,9,0,0" />
|
||||
<Grid
|
||||
ZIndex="1"
|
||||
Grid.Row="0"
|
||||
Grid.RowSpan="2"
|
||||
Grid.Column="1" Margin="8,0,0,0"
|
||||
Grid.Column="2"
|
||||
ColumnDefinitions="Auto,*"
|
||||
VerticalAlignment="Top">
|
||||
<TextBlock
|
||||
Padding="4,0,4,0"
|
||||
Name="PART_Label"
|
||||
Padding="4,0"
|
||||
Background="{DynamicResource SystemAltHighColor}"
|
||||
Text="{TemplateBinding Label}"
|
||||
/>
|
||||
</Grid>
|
||||
|
||||
<ContentPresenter
|
||||
Margin="8,0,8,5"
|
||||
Grid.Row="2"
|
||||
Name="PART_ContentPresenter"
|
||||
Margin="8,10,8,5"
|
||||
Grid.Row="1"
|
||||
Grid.Column="1"
|
||||
Grid.ColumnSpan="2"
|
||||
Content="{TemplateBinding Content}"/>
|
||||
|
||||
<Border
|
||||
BorderBrush="DarkGray"
|
||||
BorderThickness="{TemplateBinding BorderWidth}"
|
||||
CornerRadius="3"
|
||||
Name="PART_Border"
|
||||
BorderBrush="{TemplateBinding BorderBrush}"
|
||||
BorderThickness="{TemplateBinding BorderThickness}"
|
||||
CornerRadius="{TemplateBinding CornerRadius}"
|
||||
Grid.Column="0"
|
||||
Grid.ColumnSpan="3"
|
||||
Grid.ColumnSpan="4"
|
||||
Grid.Row="1"
|
||||
Grid.RowSpan="3"/>
|
||||
Grid.RowSpan="2"/>
|
||||
|
||||
</Grid>
|
||||
</ControlTemplate>
|
||||
|
||||
@@ -1,27 +1,19 @@
|
||||
using Avalonia;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Styling;
|
||||
|
||||
namespace LibationAvalonia.Controls
|
||||
{
|
||||
public partial class GroupBox : ContentControl
|
||||
{
|
||||
|
||||
public static readonly StyledProperty<Thickness> BorderWidthProperty =
|
||||
AvaloniaProperty.Register<GroupBox, Thickness>(nameof(BorderWidth));
|
||||
|
||||
public static readonly StyledProperty<string> LabelProperty =
|
||||
AvaloniaProperty.Register<GroupBox, string>(nameof(Label));
|
||||
public GroupBox()
|
||||
{
|
||||
InitializeComponent();
|
||||
BorderWidth = new Thickness(3);
|
||||
Label = "This is a groupbox label";
|
||||
}
|
||||
public Thickness BorderWidth
|
||||
{
|
||||
get { return GetValue(BorderWidthProperty); }
|
||||
set { SetValue(BorderWidthProperty, value); }
|
||||
}
|
||||
|
||||
public string Label
|
||||
{
|
||||
|
||||
@@ -3,10 +3,10 @@
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
||||
xmlns:controls="using:LibationAvalonia.Controls"
|
||||
x:Class="LibationAvalonia.Controls.LinkLabel">
|
||||
<TextBlock.Styles>
|
||||
<Style Selector="TextBlock">
|
||||
<Setter Property="Foreground" Value="{DynamicResource HyperlinkNew}"/>
|
||||
<Style Selector="controls|LinkLabel">
|
||||
<Setter Property="TextDecorations" Value="Underline"/>
|
||||
</Style>
|
||||
</TextBlock.Styles>
|
||||
|
||||
@@ -1,14 +1,57 @@
|
||||
using Avalonia;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Data;
|
||||
using Avalonia.Input;
|
||||
using Avalonia.Interactivity;
|
||||
using Avalonia.Media;
|
||||
using Avalonia.Styling;
|
||||
using System;
|
||||
using System.Windows.Input;
|
||||
|
||||
namespace LibationAvalonia.Controls
|
||||
{
|
||||
public partial class LinkLabel : TextBlock, IStyleable
|
||||
public partial class LinkLabel : TextBlock, IStyleable, ICommandSource
|
||||
{
|
||||
Type IStyleable.StyleKey => typeof(TextBlock);
|
||||
Type IStyleable.StyleKey => typeof(LinkLabel);
|
||||
|
||||
public static readonly StyledProperty<ICommand> CommandProperty =
|
||||
AvaloniaProperty.Register<LinkLabel, ICommand>(nameof(Command), enableDataValidation: true);
|
||||
|
||||
public static readonly StyledProperty<object> CommandParameterProperty =
|
||||
AvaloniaProperty.Register<LinkLabel, object>(nameof(CommandParameter));
|
||||
|
||||
public static readonly StyledProperty<IBrush> ForegroundVisitedProperty =
|
||||
AvaloniaProperty.Register<LinkLabel, IBrush>(nameof(ForegroundVisited));
|
||||
|
||||
public static readonly RoutedEvent<RoutedEventArgs> ClickEvent =
|
||||
RoutedEvent.Register<Button, RoutedEventArgs>(nameof(Click), RoutingStrategies.Bubble);
|
||||
|
||||
public ICommand Command
|
||||
{
|
||||
get => GetValue(CommandProperty);
|
||||
set => SetValue(CommandProperty, value);
|
||||
}
|
||||
|
||||
public object CommandParameter
|
||||
{
|
||||
get => GetValue(CommandParameterProperty);
|
||||
set => SetValue(CommandParameterProperty, value);
|
||||
}
|
||||
|
||||
public IBrush ForegroundVisited
|
||||
{
|
||||
get => GetValue(ForegroundVisitedProperty);
|
||||
set => SetValue(ForegroundVisitedProperty, value);
|
||||
}
|
||||
|
||||
public event EventHandler<RoutedEventArgs> Click
|
||||
{
|
||||
add => AddHandler(ClickEvent, value);
|
||||
remove => RemoveHandler(ClickEvent, value);
|
||||
}
|
||||
|
||||
private static readonly Cursor HandCursor = new Cursor(StandardCursorType.Hand);
|
||||
private bool _commandCanExecute = true;
|
||||
public LinkLabel()
|
||||
{
|
||||
InitializeComponent();
|
||||
@@ -17,7 +60,19 @@ namespace LibationAvalonia.Controls
|
||||
|
||||
private void LinkLabel_Tapped(object sender, TappedEventArgs e)
|
||||
{
|
||||
Foreground = App.HyperlinkVisited;
|
||||
Foreground = ForegroundVisited;
|
||||
if (IsEffectivelyEnabled)
|
||||
{
|
||||
|
||||
var args = new RoutedEventArgs(ClickEvent);
|
||||
RaiseEvent(args);
|
||||
|
||||
if (!args.Handled && Command?.CanExecute(CommandParameter) == true)
|
||||
{
|
||||
Command.Execute(CommandParameter);
|
||||
args.Handled = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnPointerEntered(PointerEventArgs e)
|
||||
@@ -30,5 +85,33 @@ namespace LibationAvalonia.Controls
|
||||
this.Cursor = Cursor.Default;
|
||||
base.OnPointerExited(e);
|
||||
}
|
||||
protected override bool IsEnabledCore => base.IsEnabledCore && _commandCanExecute;
|
||||
|
||||
protected override void UpdateDataValidation(AvaloniaProperty property, BindingValueType state, Exception error)
|
||||
{
|
||||
base.UpdateDataValidation(property, state, error);
|
||||
if (property == CommandProperty)
|
||||
{
|
||||
if (state == BindingValueType.BindingError)
|
||||
{
|
||||
if (_commandCanExecute)
|
||||
{
|
||||
_commandCanExecute = false;
|
||||
UpdateIsEffectivelyEnabled();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void CanExecuteChanged(object sender, EventArgs e)
|
||||
{
|
||||
var canExecute = Command == null || Command.CanExecute(CommandParameter);
|
||||
|
||||
if (canExecute != _commandCanExecute)
|
||||
{
|
||||
_commandCanExecute = canExecute;
|
||||
UpdateIsEffectivelyEnabled();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
329
Source/LibationAvalonia/Controls/Settings/Audio.axaml
Normal file
329
Source/LibationAvalonia/Controls/Settings/Audio.axaml
Normal file
@@ -0,0 +1,329 @@
|
||||
<UserControl xmlns="https://github.com/avaloniaui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
mc:Ignorable="d" d:DesignWidth="750" d:DesignHeight="600"
|
||||
xmlns:controls="clr-namespace:LibationAvalonia.Controls"
|
||||
xmlns:vm="clr-namespace:LibationAvalonia.ViewModels.Settings"
|
||||
x:DataType="vm:AudioSettingsVM"
|
||||
x:Class="LibationAvalonia.Controls.Settings.Audio">
|
||||
|
||||
<Grid
|
||||
Margin="5"
|
||||
RowDefinitions="Auto,*,Auto"
|
||||
ColumnDefinitions="*,*">
|
||||
|
||||
<Grid.Styles>
|
||||
<Style Selector="CheckBox">
|
||||
<Setter Property="Margin" Value="0,0,0,5" />
|
||||
<Style Selector="^ > TextBlock">
|
||||
<Setter Property="TextWrapping" Value="Wrap" />
|
||||
</Style>
|
||||
</Style>
|
||||
<Style Selector="RadioButton">
|
||||
<Setter Property="Margin" Value="0,0,0,5" />
|
||||
<Style Selector="^ TextBlock">
|
||||
<Setter Property="TextWrapping" Value="Wrap" />
|
||||
</Style>
|
||||
</Style>
|
||||
</Grid.Styles>
|
||||
|
||||
<StackPanel
|
||||
Grid.Row="0"
|
||||
Grid.Column="0">
|
||||
|
||||
<CheckBox IsChecked="{CompiledBinding CreateCueSheet, Mode=TwoWay}">
|
||||
<TextBlock Text="{CompiledBinding CreateCueSheetText}" />
|
||||
</CheckBox>
|
||||
|
||||
<CheckBox IsChecked="{CompiledBinding DownloadCoverArt, Mode=TwoWay}">
|
||||
<TextBlock Text="{CompiledBinding DownloadCoverArtText}" />
|
||||
</CheckBox>
|
||||
|
||||
<Grid ColumnDefinitions="*,Auto">
|
||||
<CheckBox IsChecked="{CompiledBinding DownloadClipsBookmarks, Mode=TwoWay}">
|
||||
<TextBlock Text="Download Clips, Notes and Bookmarks as" />
|
||||
</CheckBox>
|
||||
|
||||
<controls:WheelComboBox
|
||||
Margin="5,0,0,0"
|
||||
Grid.Column="1"
|
||||
IsEnabled="{CompiledBinding DownloadClipsBookmarks}"
|
||||
ItemsSource="{CompiledBinding ClipBookmarkFormats}"
|
||||
SelectedItem="{CompiledBinding ClipBookmarkFormat}"/>
|
||||
</Grid>
|
||||
|
||||
<CheckBox IsChecked="{CompiledBinding RetainAaxFile, Mode=TwoWay}">
|
||||
<TextBlock Text="{CompiledBinding RetainAaxFileText}" />
|
||||
</CheckBox>
|
||||
|
||||
<CheckBox IsChecked="{CompiledBinding MergeOpeningAndEndCredits, Mode=TwoWay}">
|
||||
<TextBlock Text="{CompiledBinding MergeOpeningEndCreditsText}" />
|
||||
</CheckBox>
|
||||
|
||||
<CheckBox IsChecked="{CompiledBinding AllowLibationFixup, Mode=TwoWay}">
|
||||
<TextBlock Text="{CompiledBinding AllowLibationFixupText}" />
|
||||
</CheckBox>
|
||||
</StackPanel>
|
||||
|
||||
<controls:GroupBox
|
||||
Grid.Row="1"
|
||||
Label="Audiobook Fix-ups"
|
||||
IsEnabled="{CompiledBinding AllowLibationFixup}">
|
||||
|
||||
<StackPanel Orientation="Vertical">
|
||||
|
||||
<CheckBox IsChecked="{CompiledBinding SplitFilesByChapter, Mode=TwoWay}">
|
||||
<TextBlock Text="{CompiledBinding SplitFilesByChapterText}" />
|
||||
</CheckBox>
|
||||
|
||||
<CheckBox IsChecked="{CompiledBinding StripAudibleBrandAudio, Mode=TwoWay}">
|
||||
<TextBlock Text="{CompiledBinding StripAudibleBrandingText}" />
|
||||
</CheckBox>
|
||||
|
||||
<CheckBox IsChecked="{CompiledBinding StripUnabridged, Mode=TwoWay}">
|
||||
<TextBlock Text="{CompiledBinding StripUnabridgedText}" />
|
||||
</CheckBox>
|
||||
|
||||
<RadioButton IsChecked="{CompiledBinding !DecryptToLossy, Mode=TwoWay}">
|
||||
|
||||
<StackPanel VerticalAlignment="Center">
|
||||
|
||||
<TextBlock
|
||||
Text="Download my books in the original audio format (Lossless)" />
|
||||
<CheckBox
|
||||
IsEnabled="{CompiledBinding !DecryptToLossy}"
|
||||
IsChecked="{CompiledBinding MoveMoovToBeginning, Mode=TwoWay}">
|
||||
|
||||
<TextBlock Text="{CompiledBinding MoveMoovToBeginningText}" />
|
||||
|
||||
</CheckBox>
|
||||
|
||||
</StackPanel>
|
||||
</RadioButton>
|
||||
|
||||
<RadioButton IsChecked="{CompiledBinding DecryptToLossy, Mode=TwoWay}">
|
||||
|
||||
<TextBlock
|
||||
TextWrapping="Wrap"
|
||||
Text="Download my books as .MP3 files (transcode if necessary)" />
|
||||
|
||||
</RadioButton>
|
||||
</StackPanel>
|
||||
</controls:GroupBox>
|
||||
|
||||
<controls:GroupBox
|
||||
Grid.Column="1"
|
||||
Grid.RowSpan="2"
|
||||
Margin="10,0,0,0"
|
||||
Label="Mp3 Encoding Options">
|
||||
|
||||
<Grid RowDefinitions="Auto,Auto,Auto,Auto,*">
|
||||
|
||||
<Grid
|
||||
Margin="0,5"
|
||||
ColumnDefinitions="Auto,*">
|
||||
|
||||
<controls:GroupBox
|
||||
Grid.Column="0"
|
||||
Label="Target">
|
||||
|
||||
<Grid ColumnDefinitions="Auto,Auto">
|
||||
<RadioButton
|
||||
Margin="5"
|
||||
Content="Bitrate"
|
||||
IsChecked="{CompiledBinding LameTargetBitrate, Mode=TwoWay}"/>
|
||||
|
||||
<RadioButton
|
||||
Grid.Column="1"
|
||||
Margin="5"
|
||||
Content="Quality"
|
||||
IsChecked="{CompiledBinding !LameTargetBitrate, Mode=TwoWay}"/>
|
||||
</Grid>
|
||||
</controls:GroupBox>
|
||||
|
||||
<CheckBox
|
||||
HorizontalAlignment="Right"
|
||||
Grid.Column="1"
|
||||
IsChecked="{CompiledBinding LameDownsampleMono, Mode=TwoWay}">
|
||||
|
||||
<TextBlock
|
||||
TextWrapping="Wrap"
|
||||
Text="Downsample to mono? (Recommended)" />
|
||||
|
||||
</CheckBox>
|
||||
</Grid>
|
||||
|
||||
<Grid Grid.Row="1" Margin="0,5" RowDefinitions="Auto,Auto" ColumnDefinitions="Auto,*,Auto">
|
||||
|
||||
<TextBlock Margin="0,0,0,5" Text="Max audio sample rate:" />
|
||||
<controls:WheelComboBox
|
||||
Grid.Row="1"
|
||||
HorizontalAlignment="Stretch"
|
||||
ItemsSource="{CompiledBinding SampleRates}"
|
||||
SelectedItem="{CompiledBinding SelectedSampleRate, Mode=TwoWay}"/>
|
||||
|
||||
<TextBlock Margin="0,0,0,5" Grid.Column="2" Text="Encoder Quality:" />
|
||||
|
||||
<controls:WheelComboBox
|
||||
Grid.Column="2"
|
||||
Grid.Row="1"
|
||||
HorizontalAlignment="Stretch"
|
||||
ItemsSource="{CompiledBinding EncoderQualities}"
|
||||
SelectedItem="{CompiledBinding SelectedEncoderQuality, Mode=TwoWay}"/>
|
||||
</Grid>
|
||||
|
||||
<controls:GroupBox
|
||||
Grid.Row="2"
|
||||
Margin="0,5"
|
||||
Label="Bitrate"
|
||||
IsEnabled="{CompiledBinding LameTargetBitrate}" >
|
||||
|
||||
<StackPanel>
|
||||
<Grid ColumnDefinitions="*,25,Auto">
|
||||
|
||||
<Slider
|
||||
Grid.Column="0"
|
||||
IsEnabled="{CompiledBinding !LameMatchSource}"
|
||||
Value="{CompiledBinding LameBitrate, Mode=TwoWay}"
|
||||
Minimum="16"
|
||||
Maximum="320"
|
||||
IsSnapToTickEnabled="True" TickFrequency="16"
|
||||
Ticks="16,32,48,64,80,96,112,128,144,160,176,192,208,224,240,256,272,288,304,320"
|
||||
TickPlacement="Outside">
|
||||
|
||||
<Slider.Styles>
|
||||
<Style Selector="Slider /template/ Thumb">
|
||||
<Setter Property="ToolTip.Tip" Value="{CompiledBinding $parent[Slider].Value, Mode=OneWay, StringFormat='\{0:f0\} Kbps'}" />
|
||||
<Setter Property="ToolTip.Placement" Value="Top" />
|
||||
<Setter Property="ToolTip.VerticalOffset" Value="-10" />
|
||||
<Setter Property="ToolTip.HorizontalOffset" Value="-30" />
|
||||
</Style>
|
||||
</Slider.Styles>
|
||||
</Slider>
|
||||
|
||||
<TextBlock
|
||||
Grid.Column="1"
|
||||
HorizontalAlignment="Right"
|
||||
Text="{CompiledBinding LameBitrate}" />
|
||||
|
||||
<TextBlock
|
||||
Grid.Column="2"
|
||||
Text=" Kbps" />
|
||||
|
||||
</Grid>
|
||||
|
||||
<Grid ColumnDefinitions="*,*">
|
||||
|
||||
<CheckBox
|
||||
Grid.Column="0"
|
||||
IsChecked="{CompiledBinding LameConstantBitrate, Mode=TwoWay}">
|
||||
|
||||
<TextBlock
|
||||
TextWrapping="Wrap"
|
||||
Text="Restrict Encoder to Constant Bitrate?" />
|
||||
|
||||
</CheckBox>
|
||||
|
||||
<CheckBox
|
||||
Grid.Column="1"
|
||||
HorizontalAlignment="Right"
|
||||
IsChecked="{CompiledBinding LameMatchSource, Mode=TwoWay}">
|
||||
|
||||
<TextBlock
|
||||
TextWrapping="Wrap"
|
||||
Text="Match Source Bitrate?" />
|
||||
|
||||
</CheckBox>
|
||||
</Grid>
|
||||
</StackPanel>
|
||||
</controls:GroupBox>
|
||||
|
||||
<controls:GroupBox
|
||||
Grid.Row="3"
|
||||
Margin="0,5"
|
||||
Label="Quality"
|
||||
IsEnabled="{CompiledBinding !LameTargetBitrate}">
|
||||
|
||||
<Grid
|
||||
ColumnDefinitions="*,Auto,25"
|
||||
RowDefinitions="*,Auto">
|
||||
|
||||
<Slider
|
||||
Grid.Column="0"
|
||||
Grid.ColumnSpan="2"
|
||||
Value="{CompiledBinding LameVBRQuality, Mode=TwoWay}"
|
||||
Minimum="0"
|
||||
Maximum="9"
|
||||
IsSnapToTickEnabled="True" TickFrequency="1"
|
||||
Ticks="0,1,2,3,4,5,6,7,8,9"
|
||||
TickPlacement="Outside">
|
||||
<Slider.Styles>
|
||||
<Style Selector="Slider /template/ Thumb">
|
||||
<Setter Property="ToolTip.Tip" Value="{CompiledBinding $parent[Slider].Value, Mode=OneWay, StringFormat='V\{0:f0\}'}" />
|
||||
<Setter Property="ToolTip.Placement" Value="Top" />
|
||||
<Setter Property="ToolTip.VerticalOffset" Value="-10" />
|
||||
<Setter Property="ToolTip.HorizontalOffset" Value="-30" />
|
||||
</Style>
|
||||
</Slider.Styles>
|
||||
</Slider>
|
||||
|
||||
<StackPanel
|
||||
Grid.Column="2"
|
||||
HorizontalAlignment="Right"
|
||||
Orientation="Horizontal">
|
||||
|
||||
<TextBlock Text="V" />
|
||||
<TextBlock Text="{CompiledBinding LameVBRQuality}" />
|
||||
|
||||
</StackPanel>
|
||||
|
||||
<TextBlock
|
||||
Grid.Column="0"
|
||||
Grid.Row="1"
|
||||
Text="Higher" />
|
||||
|
||||
<TextBlock
|
||||
Grid.Column="1"
|
||||
Grid.Row="1"
|
||||
HorizontalAlignment="Right"
|
||||
Text="Lower" />
|
||||
|
||||
</Grid>
|
||||
</controls:GroupBox>
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="4"
|
||||
Margin="0,5"
|
||||
VerticalAlignment="Bottom"
|
||||
Text="Using L.A.M.E encoding engine"
|
||||
FontStyle="Oblique" />
|
||||
</Grid>
|
||||
</controls:GroupBox>
|
||||
|
||||
<controls:GroupBox
|
||||
Grid.Row="2"
|
||||
Grid.ColumnSpan="2"
|
||||
Margin="0,10,0,0"
|
||||
IsEnabled="{CompiledBinding SplitFilesByChapter}"
|
||||
Label="{CompiledBinding ChapterTitleTemplateText}">
|
||||
|
||||
<Grid ColumnDefinitions="*,Auto" Margin="0,8" >
|
||||
|
||||
<TextBox
|
||||
Grid.Column="0"
|
||||
FontSize="14"
|
||||
IsReadOnly="True"
|
||||
Text="{CompiledBinding ChapterTitleTemplate}" />
|
||||
|
||||
<Button
|
||||
Grid.Column="1"
|
||||
Content="Edit"
|
||||
Padding="30,0"
|
||||
Margin="10,0,0,0"
|
||||
VerticalAlignment="Stretch"
|
||||
Click="EditChapterTitleTemplateButton_Click" />
|
||||
</Grid>
|
||||
</controls:GroupBox>
|
||||
</Grid>
|
||||
</UserControl>
|
||||
38
Source/LibationAvalonia/Controls/Settings/Audio.axaml.cs
Normal file
38
Source/LibationAvalonia/Controls/Settings/Audio.axaml.cs
Normal file
@@ -0,0 +1,38 @@
|
||||
using Avalonia.Controls;
|
||||
using LibationAvalonia.Dialogs;
|
||||
using LibationAvalonia.ViewModels.Settings;
|
||||
using LibationFileManager;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace LibationAvalonia.Controls.Settings
|
||||
{
|
||||
public partial class Audio : UserControl
|
||||
{
|
||||
private AudioSettingsVM _viewModel => DataContext as AudioSettingsVM;
|
||||
public Audio()
|
||||
{
|
||||
InitializeComponent();
|
||||
if (Design.IsDesignMode)
|
||||
{
|
||||
_ = Configuration.Instance.LibationFiles;
|
||||
DataContext = new AudioSettingsVM(Configuration.Instance);
|
||||
}
|
||||
}
|
||||
|
||||
public async void EditChapterTitleTemplateButton_Click(object sender, Avalonia.Interactivity.RoutedEventArgs e)
|
||||
{
|
||||
if (_viewModel is null) return;
|
||||
var newTemplate = await editTemplate(TemplateEditor<Templates.ChapterTitleTemplate>.CreateNameEditor(_viewModel.ChapterTitleTemplate));
|
||||
if (newTemplate is not null)
|
||||
_viewModel.ChapterTitleTemplate = newTemplate;
|
||||
}
|
||||
|
||||
private async Task<string> editTemplate(ITemplateEditor template)
|
||||
{
|
||||
var form = new EditTemplateDialog(template);
|
||||
if (await form.ShowDialog<DialogResult>(this.GetParentWindow()) == DialogResult.OK)
|
||||
return template.EditingTemplate.TemplateText;
|
||||
else return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
182
Source/LibationAvalonia/Controls/Settings/DownloadDecrypt.axaml
Normal file
182
Source/LibationAvalonia/Controls/Settings/DownloadDecrypt.axaml
Normal file
@@ -0,0 +1,182 @@
|
||||
<UserControl xmlns="https://github.com/avaloniaui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
mc:Ignorable="d" d:DesignWidth="600" d:DesignHeight="700"
|
||||
xmlns:controls="clr-namespace:LibationAvalonia.Controls"
|
||||
xmlns:vm="clr-namespace:LibationAvalonia.ViewModels.Settings"
|
||||
x:DataType="vm:DownloadDecryptSettingsVM"
|
||||
x:Class="LibationAvalonia.Controls.Settings.DownloadDecrypt">
|
||||
|
||||
<Grid RowDefinitions="Auto,Auto,Auto,*">
|
||||
<controls:GroupBox
|
||||
Grid.Row="0"
|
||||
Margin="5"
|
||||
Label="{CompiledBinding BadBookGroupboxText}">
|
||||
|
||||
<Grid
|
||||
ColumnDefinitions="*,*"
|
||||
RowDefinitions="Auto,Auto">
|
||||
<Grid.Styles>
|
||||
<Style Selector="RadioButton">
|
||||
<Setter Property="Margin" Value="0,5,0,5" />
|
||||
<Style Selector="^ > TextBlock">
|
||||
<Setter Property="TextWrapping" Value="Wrap" />
|
||||
</Style>
|
||||
</Style>
|
||||
</Grid.Styles>
|
||||
|
||||
<RadioButton
|
||||
Grid.Column="0"
|
||||
Grid.Row="0"
|
||||
IsChecked="{CompiledBinding BadBookAsk, Mode=TwoWay}">
|
||||
|
||||
<TextBlock Text="{CompiledBinding BadBookAskText}" />
|
||||
|
||||
</RadioButton>
|
||||
|
||||
<RadioButton
|
||||
Grid.Column="1"
|
||||
Grid.Row="0"
|
||||
IsChecked="{CompiledBinding BadBookAbort, Mode=TwoWay}">
|
||||
|
||||
<TextBlock Text="{CompiledBinding BadBookAbortText}" />
|
||||
|
||||
</RadioButton>
|
||||
|
||||
<RadioButton
|
||||
Grid.Column="0"
|
||||
Grid.Row="1"
|
||||
IsChecked="{CompiledBinding BadBookRetry, Mode=TwoWay}">
|
||||
|
||||
<TextBlock Text="{CompiledBinding BadBookRetryText}" />
|
||||
|
||||
</RadioButton>
|
||||
|
||||
<RadioButton
|
||||
Grid.Column="1"
|
||||
Grid.Row="1"
|
||||
IsChecked="{CompiledBinding BadBookIgnore, Mode=TwoWay}">
|
||||
|
||||
<TextBlock Text="{CompiledBinding BadBookIgnoreText}" />
|
||||
|
||||
</RadioButton>
|
||||
</Grid>
|
||||
</controls:GroupBox>
|
||||
|
||||
<controls:GroupBox
|
||||
Margin="5"
|
||||
Grid.Row="1"
|
||||
Label="Custom File Naming">
|
||||
|
||||
<Grid
|
||||
RowDefinitions="Auto,Auto,Auto,Auto,Auto,Auto,Auto"
|
||||
ColumnDefinitions="*,Auto">
|
||||
|
||||
<Grid.Styles>
|
||||
<Style Selector="TextBox">
|
||||
<Setter Property="Margin" Value="0,5,10,10" />
|
||||
<Setter Property="FontSize" Value="14" />
|
||||
<Setter Property="IsReadOnly" Value="True" />
|
||||
</Style>
|
||||
<Style Selector="Button">
|
||||
<Setter Property="VerticalAlignment" Value="Stretch" />
|
||||
<Setter Property="Margin" Value="0,5,0,10" />
|
||||
<Setter Property="Padding" Value="30,0" />
|
||||
</Style>
|
||||
</Grid.Styles>
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="0"
|
||||
Grid.Column="0"
|
||||
Margin="0,5,0,0"
|
||||
Text="{CompiledBinding FolderTemplateText}" />
|
||||
|
||||
<TextBox
|
||||
Grid.Row="1"
|
||||
Grid.Column="0"
|
||||
Text="{CompiledBinding FolderTemplate}" />
|
||||
|
||||
<Button
|
||||
Grid.Row="1"
|
||||
Grid.Column="1"
|
||||
Content="Edit"
|
||||
Click="EditFolderTemplateButton_Click" />
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="2"
|
||||
Grid.Column="0"
|
||||
Text="{CompiledBinding FileTemplateText}" />
|
||||
|
||||
<TextBox
|
||||
Grid.Row="3"
|
||||
Grid.Column="0"
|
||||
Text="{CompiledBinding FileTemplate}" />
|
||||
|
||||
<Button
|
||||
Grid.Row="3"
|
||||
Grid.Column="1"
|
||||
Content="Edit"
|
||||
Click="EditFileTemplateButton_Click" />
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="4"
|
||||
Grid.Column="0"
|
||||
Text="{CompiledBinding ChapterFileTemplateText}" />
|
||||
|
||||
<TextBox
|
||||
Grid.Row="5"
|
||||
Grid.Column="0"
|
||||
Text="{CompiledBinding ChapterFileTemplate}" />
|
||||
|
||||
<Button
|
||||
Grid.Row="5"
|
||||
Grid.Column="1"
|
||||
Content="Edit"
|
||||
Click="EditChapterFileTemplateButton_Click" />
|
||||
|
||||
<Button
|
||||
Grid.Row="6"
|
||||
Grid.Column="0"
|
||||
Height="30"
|
||||
Margin="0"
|
||||
Content="{CompiledBinding EditCharReplacementText}"
|
||||
Click="EditCharReplacementButton_Click" />
|
||||
|
||||
</Grid>
|
||||
</controls:GroupBox>
|
||||
<controls:GroupBox
|
||||
Grid.Row="2"
|
||||
Margin="5"
|
||||
Label="Temporary Files Location">
|
||||
|
||||
<StackPanel
|
||||
Margin="0,5" >
|
||||
|
||||
<TextBlock
|
||||
Margin="0,0,0,10"
|
||||
TextWrapping="Wrap"
|
||||
Text="{CompiledBinding InProgressDescriptionText}" />
|
||||
|
||||
<controls:DirectoryOrCustomSelectControl
|
||||
Directory="{CompiledBinding InProgressDirectory, Mode=TwoWay}"
|
||||
KnownDirectories="{CompiledBinding KnownDirectories}" />
|
||||
|
||||
</StackPanel>
|
||||
</controls:GroupBox>
|
||||
|
||||
<CheckBox
|
||||
Grid.Row="3"
|
||||
Margin="5"
|
||||
VerticalAlignment="Top"
|
||||
IsVisible="{CompiledBinding !Config.IsLinux}"
|
||||
IsChecked="{CompiledBinding UseCoverAsFolderIcon, Mode=TwoWay}">
|
||||
|
||||
<TextBlock
|
||||
TextWrapping="Wrap"
|
||||
Text="{CompiledBinding UseCoverAsFolderIconText}" />
|
||||
|
||||
</CheckBox>
|
||||
|
||||
</Grid>
|
||||
</UserControl>
|
||||
@@ -0,0 +1,62 @@
|
||||
using Avalonia.Controls;
|
||||
using LibationAvalonia.Dialogs;
|
||||
using LibationAvalonia.ViewModels.Settings;
|
||||
using LibationFileManager;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace LibationAvalonia.Controls.Settings
|
||||
{
|
||||
public partial class DownloadDecrypt : UserControl
|
||||
{
|
||||
private DownloadDecryptSettingsVM _viewModel => DataContext as DownloadDecryptSettingsVM;
|
||||
public DownloadDecrypt()
|
||||
{
|
||||
InitializeComponent();
|
||||
if (Design.IsDesignMode)
|
||||
{
|
||||
_ = Configuration.Instance.LibationFiles;
|
||||
DataContext = new DownloadDecryptSettingsVM(Configuration.Instance);
|
||||
}
|
||||
}
|
||||
|
||||
public async void EditFolderTemplateButton_Click(object sender, Avalonia.Interactivity.RoutedEventArgs e)
|
||||
{
|
||||
if (_viewModel is null) return;
|
||||
var newTemplate = await editTemplate(TemplateEditor<Templates.FolderTemplate>.CreateFilenameEditor(_viewModel.Config.Books, _viewModel.FolderTemplate));
|
||||
if (newTemplate is not null)
|
||||
_viewModel.FolderTemplate = newTemplate;
|
||||
}
|
||||
|
||||
public async void EditFileTemplateButton_Click(object sender, Avalonia.Interactivity.RoutedEventArgs e)
|
||||
{
|
||||
if (_viewModel is null) return;
|
||||
var newTemplate = await editTemplate(TemplateEditor<Templates.FileTemplate>.CreateFilenameEditor(_viewModel.Config.Books, _viewModel.FileTemplate));
|
||||
if (newTemplate is not null)
|
||||
_viewModel.FileTemplate = newTemplate;
|
||||
}
|
||||
|
||||
public async void EditChapterFileTemplateButton_Click(object sender, Avalonia.Interactivity.RoutedEventArgs e)
|
||||
{
|
||||
if (_viewModel is null) return;
|
||||
var newTemplate = await editTemplate(TemplateEditor<Templates.ChapterFileTemplate>.CreateFilenameEditor(_viewModel.Config.Books, _viewModel.ChapterFileTemplate));
|
||||
if (newTemplate is not null)
|
||||
_viewModel.ChapterFileTemplate = newTemplate;
|
||||
}
|
||||
|
||||
public async void EditCharReplacementButton_Click(object sender, Avalonia.Interactivity.RoutedEventArgs e)
|
||||
{
|
||||
if (_viewModel is null) return;
|
||||
var form = new EditReplacementChars(_viewModel.Config);
|
||||
await form.ShowDialog<DialogResult>(this.GetParentWindow());
|
||||
}
|
||||
|
||||
|
||||
private async Task<string> editTemplate(ITemplateEditor template)
|
||||
{
|
||||
var form = new EditTemplateDialog(template);
|
||||
if (await form.ShowDialog<DialogResult>(this.GetParentWindow()) == DialogResult.OK)
|
||||
return template.EditingTemplate.TemplateText;
|
||||
else return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
41
Source/LibationAvalonia/Controls/Settings/Import.axaml
Normal file
41
Source/LibationAvalonia/Controls/Settings/Import.axaml
Normal file
@@ -0,0 +1,41 @@
|
||||
<UserControl xmlns="https://github.com/avaloniaui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
mc:Ignorable="d" d:DesignWidth="600" d:DesignHeight="450"
|
||||
xmlns:controls="clr-namespace:LibationAvalonia.Controls"
|
||||
xmlns:vm="clr-namespace:LibationAvalonia.ViewModels.Settings"
|
||||
x:DataType="vm:ImportSettingsVM"
|
||||
x:Class="LibationAvalonia.Controls.Settings.Import">
|
||||
|
||||
<StackPanel Margin="5">
|
||||
<StackPanel.Styles>
|
||||
<Style Selector="CheckBox">
|
||||
<Setter Property="Margin" Value="0,0,0,10" />
|
||||
<Style Selector="^ > TextBlock">
|
||||
<Setter Property="TextWrapping" Value="Wrap" />
|
||||
</Style>
|
||||
</Style>
|
||||
</StackPanel.Styles>
|
||||
|
||||
<CheckBox IsChecked="{CompiledBinding AutoScan, Mode=TwoWay}">
|
||||
<TextBlock Text="{CompiledBinding AutoScanText}" />
|
||||
</CheckBox>
|
||||
|
||||
<CheckBox IsChecked="{CompiledBinding ShowImportedStats, Mode=TwoWay}">
|
||||
<TextBlock Text="{CompiledBinding ShowImportedStatsText}" />
|
||||
</CheckBox>
|
||||
|
||||
<CheckBox IsChecked="{CompiledBinding ImportEpisodes, Mode=TwoWay}">
|
||||
<TextBlock Text="{CompiledBinding ImportEpisodesText}" />
|
||||
</CheckBox>
|
||||
|
||||
<CheckBox IsChecked="{CompiledBinding DownloadEpisodes, Mode=TwoWay}">
|
||||
<TextBlock Text="{CompiledBinding DownloadEpisodesText}" />
|
||||
</CheckBox>
|
||||
|
||||
<CheckBox IsChecked="{CompiledBinding AutoDownloadEpisodes, Mode=TwoWay}">
|
||||
<TextBlock Text="{CompiledBinding AutoDownloadEpisodesText}" />
|
||||
</CheckBox>
|
||||
</StackPanel>
|
||||
</UserControl>
|
||||
20
Source/LibationAvalonia/Controls/Settings/Import.axaml.cs
Normal file
20
Source/LibationAvalonia/Controls/Settings/Import.axaml.cs
Normal file
@@ -0,0 +1,20 @@
|
||||
using Avalonia.Controls;
|
||||
using LibationAvalonia.ViewModels.Settings;
|
||||
using LibationFileManager;
|
||||
|
||||
namespace LibationAvalonia.Controls.Settings
|
||||
{
|
||||
public partial class Import : UserControl
|
||||
{
|
||||
public Import()
|
||||
{
|
||||
InitializeComponent();
|
||||
|
||||
if (Design.IsDesignMode)
|
||||
{
|
||||
_ = Configuration.Instance.LibationFiles;
|
||||
DataContext = new ImportSettingsVM(Configuration.Instance);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
88
Source/LibationAvalonia/Controls/Settings/Important.axaml
Normal file
88
Source/LibationAvalonia/Controls/Settings/Important.axaml
Normal file
@@ -0,0 +1,88 @@
|
||||
<UserControl xmlns="https://github.com/avaloniaui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
mc:Ignorable="d" d:DesignWidth="600" d:DesignHeight="450"
|
||||
xmlns:controls="clr-namespace:LibationAvalonia.Controls"
|
||||
xmlns:vm="clr-namespace:LibationAvalonia.ViewModels.Settings"
|
||||
x:DataType="vm:ImportantSettingsVM"
|
||||
x:Class="LibationAvalonia.Controls.Settings.Important">
|
||||
|
||||
<Grid RowDefinitions="Auto,Auto,*">
|
||||
<controls:GroupBox
|
||||
Grid.Row="0"
|
||||
Margin="5"
|
||||
Label="Books Location">
|
||||
|
||||
<StackPanel>
|
||||
<TextBlock
|
||||
Margin="5"
|
||||
Text="{CompiledBinding BooksText}" />
|
||||
|
||||
<controls:DirectoryOrCustomSelectControl Margin="0,10,0,10"
|
||||
SubDirectory="Books"
|
||||
Directory="{CompiledBinding BooksDirectory, Mode=TwoWay}"
|
||||
KnownDirectories="{CompiledBinding KnownDirectories}" />
|
||||
|
||||
<CheckBox IsChecked="{CompiledBinding SavePodcastsToParentFolder, Mode=TwoWay}">
|
||||
<TextBlock Text="{CompiledBinding SavePodcastsToParentFolderText}" />
|
||||
</CheckBox>
|
||||
|
||||
</StackPanel>
|
||||
|
||||
</controls:GroupBox>
|
||||
|
||||
<StackPanel
|
||||
Grid.Row="1" Margin="5"
|
||||
Orientation="Horizontal">
|
||||
|
||||
<TextBlock
|
||||
Margin="0,0,10,0"
|
||||
VerticalAlignment="Center"
|
||||
Text="Logging level" />
|
||||
|
||||
<controls:WheelComboBox
|
||||
Width="120"
|
||||
Height="25"
|
||||
HorizontalContentAlignment="Stretch"
|
||||
SelectedItem="{CompiledBinding LoggingLevel, Mode=TwoWay}"
|
||||
ItemsSource="{CompiledBinding LoggingLevels}" />
|
||||
|
||||
<Button
|
||||
Margin="50,0,0,0"
|
||||
Padding="20,0"
|
||||
VerticalAlignment="Stretch"
|
||||
Content="Open Log Folder"
|
||||
Click="OpenLogFolderButton_Click" />
|
||||
|
||||
</StackPanel>
|
||||
|
||||
<Grid
|
||||
Grid.Row="2"
|
||||
ColumnDefinitions="Auto,Auto,*"
|
||||
Margin="10"
|
||||
VerticalAlignment="Bottom">
|
||||
<TextBlock
|
||||
Grid.Column="0"
|
||||
FontSize="16"
|
||||
VerticalAlignment="Center"
|
||||
Text="Theme: "/>
|
||||
|
||||
<controls:WheelComboBox
|
||||
Grid.Column="1"
|
||||
MinWidth="80"
|
||||
SelectedItem="{CompiledBinding ThemeVariant, Mode=TwoWay}"
|
||||
ItemsSource="{CompiledBinding Themes}"/>
|
||||
|
||||
<TextBlock
|
||||
Grid.Column="2"
|
||||
FontSize="16"
|
||||
FontWeight="Bold"
|
||||
Margin="10,0"
|
||||
VerticalAlignment="Center"
|
||||
IsVisible="{CompiledBinding SelectionChanged}"
|
||||
Text="Theme change takes effect on restart"/>
|
||||
|
||||
</Grid>
|
||||
</Grid>
|
||||
</UserControl>
|
||||
26
Source/LibationAvalonia/Controls/Settings/Important.axaml.cs
Normal file
26
Source/LibationAvalonia/Controls/Settings/Important.axaml.cs
Normal file
@@ -0,0 +1,26 @@
|
||||
using Avalonia.Controls;
|
||||
using Dinah.Core;
|
||||
using FileManager;
|
||||
using LibationAvalonia.ViewModels.Settings;
|
||||
using LibationFileManager;
|
||||
|
||||
namespace LibationAvalonia.Controls.Settings
|
||||
{
|
||||
public partial class Important : UserControl
|
||||
{
|
||||
public Important()
|
||||
{
|
||||
InitializeComponent();
|
||||
if (Design.IsDesignMode)
|
||||
{
|
||||
_ = Configuration.Instance.LibationFiles;
|
||||
DataContext = new ImportantSettingsVM(Configuration.Instance);
|
||||
}
|
||||
}
|
||||
|
||||
public void OpenLogFolderButton_Click(object sender, Avalonia.Interactivity.RoutedEventArgs e)
|
||||
{
|
||||
Go.To.Folder(((LongPath)Configuration.Instance.LibationFiles).ShortPathName);
|
||||
}
|
||||
}
|
||||
}
|
||||
73
Source/LibationAvalonia/Dialogs/AboutDialog.axaml
Normal file
73
Source/LibationAvalonia/Dialogs/AboutDialog.axaml
Normal file
@@ -0,0 +1,73 @@
|
||||
<Window xmlns="https://github.com/avaloniaui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
mc:Ignorable="d" d:DesignWidth="400" d:DesignHeight="520"
|
||||
MinWidth="400" MinHeight="520"
|
||||
Width="400" Height="520"
|
||||
x:Class="LibationAvalonia.Dialogs.AboutDialog"
|
||||
xmlns:controls="clr-namespace:LibationAvalonia.Controls"
|
||||
Title="About Libation"
|
||||
Icon="/Assets/libation.ico">
|
||||
|
||||
<Grid Margin="10" ColumnDefinitions="Auto,*" RowDefinitions="Auto,Auto,Auto,*">
|
||||
|
||||
<controls:LinkLabel Grid.ColumnSpan="2" FontSize="16" FontWeight="Bold" Text="{Binding Version}" ToolTip.Tip="View Release Notes" Tapped="ViewReleaseNotes_Tapped" />
|
||||
|
||||
<controls:LinkLabel Grid.Column="1" FontSize="14" VerticalAlignment="Center" HorizontalAlignment="Right" Text="https://getlibation.com" Tapped="Link_getlibation"/>
|
||||
|
||||
<Button Grid.Row="1" Grid.ColumnSpan="2" HorizontalAlignment="Stretch" HorizontalContentAlignment="Center" Margin="0,20,0,0" IsEnabled="{Binding CanCheckForUpgrade}" Content="{Binding UpgradeButtonText}" Click="CheckForUpgrade_Click" />
|
||||
|
||||
<Canvas Grid.Row="2" Grid.ColumnSpan="2" Margin="0,30,0,20" Width="280" Height="220">
|
||||
<Path Stretch="None" Fill="{DynamicResource IconFill}" Data="{DynamicResource LibationCheersIcon}">
|
||||
<Path.RenderTransform>
|
||||
<TransformGroup>
|
||||
<RotateTransform Angle="12" />
|
||||
<ScaleTransform ScaleX="0.4" ScaleY="0.4" />
|
||||
<TranslateTransform X="-160" Y="-150" />
|
||||
</TransformGroup>
|
||||
</Path.RenderTransform>
|
||||
</Path>
|
||||
<Path Stretch="None" Fill="{DynamicResource IconFill}" Data="{DynamicResource LibationCheersIcon}">
|
||||
<Path.RenderTransform>
|
||||
<TransformGroup>
|
||||
<ScaleTransform ScaleX="-1" ScaleY="1" />
|
||||
<RotateTransform Angle="-12" />
|
||||
<ScaleTransform ScaleX="0.4" ScaleY="0.4" />
|
||||
<TranslateTransform X="23" Y="-150" />
|
||||
</TransformGroup>
|
||||
</Path.RenderTransform>
|
||||
</Path>
|
||||
</Canvas>
|
||||
|
||||
<controls:GroupBox Grid.Row="3" Label="Acknowledgements" Grid.ColumnSpan="2">
|
||||
<StackPanel>
|
||||
|
||||
<Grid ColumnDefinitions="Auto,*" RowDefinitions="Auto,Auto">
|
||||
<controls:LinkLabel FontWeight="Bold" Text="rmcrackan" Tapped="Link_GithubUser" />
|
||||
<TextBlock Grid.Column="1" Margin="10,0" Text="Creator" />
|
||||
<controls:LinkLabel Grid.Row="1" FontWeight="Bold" Text="Mbucari" Tapped="Link_GithubUser" />
|
||||
<TextBlock Grid.Row="1" Grid.Column="1" Margin="10,0" Text="Developer" />
|
||||
</Grid>
|
||||
|
||||
<TextBlock Margin="0,10" FontSize="12" Text="Additional Contributions by:" TextDecorations="Underline"/>
|
||||
|
||||
<WrapPanel>
|
||||
<WrapPanel.Styles>
|
||||
<Style Selector="controls|LinkLabel">
|
||||
<Setter Property="Margin" Value="5,0" />
|
||||
<Setter Property="FontSize" Value="13" />
|
||||
</Style>
|
||||
</WrapPanel.Styles>
|
||||
<controls:LinkLabel Text="pixil98" Tapped="Link_GithubUser" />
|
||||
<controls:LinkLabel Text="hutattedonmyarm" Tapped="Link_GithubUser" />
|
||||
<controls:LinkLabel Text="seanke" Tapped="Link_GithubUser" />
|
||||
<controls:LinkLabel Text="wtanksleyjr" Tapped="Link_GithubUser" />
|
||||
<controls:LinkLabel Text="Dr.Blank" Tapped="Link_GithubUser" />
|
||||
<controls:LinkLabel Text="CharlieRussel" Tapped="Link_GithubUser" />
|
||||
</WrapPanel>
|
||||
</StackPanel>
|
||||
</controls:GroupBox>
|
||||
|
||||
</Grid>
|
||||
</Window>
|
||||
80
Source/LibationAvalonia/Dialogs/AboutDialog.axaml.cs
Normal file
80
Source/LibationAvalonia/Dialogs/AboutDialog.axaml.cs
Normal file
@@ -0,0 +1,80 @@
|
||||
using Avalonia.Controls;
|
||||
using LibationAvalonia.Controls;
|
||||
using LibationAvalonia.ViewModels;
|
||||
using LibationFileManager;
|
||||
using LibationUiBase;
|
||||
using ReactiveUI;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace LibationAvalonia.Dialogs
|
||||
{
|
||||
public partial class AboutDialog : DialogWindow
|
||||
{
|
||||
private readonly AboutVM _viewModel;
|
||||
public AboutDialog() : base(saveAndRestorePosition:false)
|
||||
{
|
||||
if (Design.IsDesignMode)
|
||||
_ = Configuration.Instance.LibationFiles;
|
||||
|
||||
InitializeComponent();
|
||||
|
||||
DataContext = _viewModel = new AboutVM();
|
||||
}
|
||||
|
||||
private async void CheckForUpgrade_Click(object sender, Avalonia.Interactivity.RoutedEventArgs e)
|
||||
{
|
||||
var mainWindow = Owner as Views.MainWindow;
|
||||
|
||||
var upgrader = new Upgrader();
|
||||
upgrader.DownloadProgress += async (_, e) => await Avalonia.Threading.Dispatcher.UIThread.InvokeAsync(() => mainWindow.ViewModel.DownloadProgress = e.ProgressPercentage);
|
||||
upgrader.DownloadCompleted += async (_, _) => await Avalonia.Threading.Dispatcher.UIThread.InvokeAsync(() => mainWindow.ViewModel.DownloadProgress = null);
|
||||
|
||||
_viewModel.CanCheckForUpgrade = false;
|
||||
Version latestVersion = null;
|
||||
await upgrader.CheckForUpgradeAsync(OnUpgradeAvailable);
|
||||
|
||||
_viewModel.CanCheckForUpgrade = latestVersion is null;
|
||||
|
||||
_viewModel.UpgradeButtonText = latestVersion is null ? "Libation is up to date. Check Again." : $"Version {latestVersion:3} is available";
|
||||
|
||||
async Task OnUpgradeAvailable(UpgradeEventArgs e)
|
||||
{
|
||||
var notificationResult = await new UpgradeNotificationDialog(e.UpgradeProperties, e.CapUpgrade).ShowDialogAsync(this);
|
||||
|
||||
e.Ignore = notificationResult == DialogResult.Ignore;
|
||||
e.InstallUpgrade = notificationResult == DialogResult.OK;
|
||||
latestVersion = e.UpgradeProperties.LatestRelease;
|
||||
}
|
||||
}
|
||||
|
||||
private void Link_GithubUser(object sender, Avalonia.Input.TappedEventArgs e)
|
||||
{
|
||||
if (sender is LinkLabel lbl)
|
||||
{
|
||||
Dinah.Core.Go.To.Url($"ht" + $"tps://github.com/{lbl.Text.Replace('.','-')}");
|
||||
}
|
||||
}
|
||||
|
||||
private void Link_getlibation(object sender, Avalonia.Input.TappedEventArgs e) => Dinah.Core.Go.To.Url(AppScaffolding.LibationScaffolding.WebsiteUrl);
|
||||
|
||||
private void ViewReleaseNotes_Tapped(object sender, Avalonia.Input.TappedEventArgs e)
|
||||
=> Dinah.Core.Go.To.Url($"{AppScaffolding.LibationScaffolding.RepositoryUrl}/releases/tag/v{AppScaffolding.LibationScaffolding.BuildVersion.ToString(3)}");
|
||||
}
|
||||
|
||||
public class AboutVM : ViewModelBase
|
||||
{
|
||||
public string Version { get; }
|
||||
public bool CanCheckForUpgrade { get => canCheckForUpgrade; set => this.RaiseAndSetIfChanged(ref canCheckForUpgrade, value); }
|
||||
public string UpgradeButtonText { get => upgradeButtonText; set => this.RaiseAndSetIfChanged(ref upgradeButtonText, value); }
|
||||
|
||||
|
||||
private bool canCheckForUpgrade = true;
|
||||
private string upgradeButtonText = "Check for Upgrade";
|
||||
|
||||
public AboutVM()
|
||||
{
|
||||
Version = $"Libation {AppScaffolding.LibationScaffolding.Variety} v{AppScaffolding.LibationScaffolding.BuildVersion}";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -81,7 +81,7 @@
|
||||
HorizontalContentAlignment = "Stretch"
|
||||
HorizontalAlignment = "Stretch"
|
||||
SelectedItem="{Binding SelectedLocale, Mode=TwoWay}"
|
||||
Items="{Binding Locales}">
|
||||
ItemsSource="{Binding Locales}">
|
||||
|
||||
<ComboBox.ItemTemplate>
|
||||
<DataTemplate>
|
||||
@@ -113,14 +113,13 @@
|
||||
|
||||
<Button
|
||||
Grid.Column="0"
|
||||
Height="30"
|
||||
Padding="5,5"
|
||||
Content="Import from audible-cli"
|
||||
Click="ImportButton_Clicked" />
|
||||
|
||||
<Button
|
||||
Grid.Column="1"
|
||||
Height="30"
|
||||
Padding="30,3,30,3"
|
||||
Padding="30,5"
|
||||
Content="Save"
|
||||
Click="SaveButton_Clicked" />
|
||||
</Grid>
|
||||
|
||||
@@ -45,7 +45,6 @@
|
||||
<controls:GroupBox
|
||||
Label="Edit Tags"
|
||||
Grid.Row="1"
|
||||
BorderWidth="1"
|
||||
Margin="10,0,10,0">
|
||||
|
||||
<StackPanel Orientation="Vertical">
|
||||
@@ -63,7 +62,6 @@
|
||||
<controls:GroupBox
|
||||
Label="Liberated status: Whether the book/pdf has been downloaded"
|
||||
Grid.Row="2"
|
||||
BorderWidth="1"
|
||||
Margin="10,10,10,10">
|
||||
|
||||
<StackPanel Orientation="Vertical">
|
||||
@@ -95,7 +93,7 @@
|
||||
Height="25"
|
||||
VerticalAlignment="Center"
|
||||
SelectedItem="{Binding BookLiberatedSelectedItem, Mode=TwoWay}"
|
||||
Items="{Binding BookLiberatedItems}">
|
||||
ItemsSource="{Binding BookLiberatedItems}">
|
||||
|
||||
<ComboBox.ItemTemplate>
|
||||
<DataTemplate>
|
||||
@@ -116,8 +114,8 @@
|
||||
Height="25"
|
||||
Width="150"
|
||||
VerticalAlignment="Center"
|
||||
SelectedItem="{Binding PdfLiberatedSelectedItem, Mode=TwoWay}"
|
||||
Items="{Binding PdfLiberatedItems}">
|
||||
SelectedItem="{Binding PdfLiberatedSelectedItem, Mode=TwoWay}"
|
||||
ItemsSource="{Binding PdfLiberatedItems}">
|
||||
|
||||
<ComboBox.ItemTemplate>
|
||||
<DataTemplate>
|
||||
|
||||
@@ -101,13 +101,13 @@
|
||||
Grid.Column="0"
|
||||
Grid.Row="0"
|
||||
Content="Check All"
|
||||
Click="CheckAll_Click"/>
|
||||
Command="{Binding CheckAll}"/>
|
||||
|
||||
<Button
|
||||
Grid.Column="0"
|
||||
Grid.Row="1"
|
||||
Content="Uncheck All"
|
||||
Click="UncheckAll_Click"/>
|
||||
Command="{Binding UncheckAll}"/>
|
||||
|
||||
<Button
|
||||
Grid.Column="1"
|
||||
|
||||
@@ -77,12 +77,12 @@ namespace LibationAvalonia.Dialogs
|
||||
await setControlEnabled(sender, true);
|
||||
}
|
||||
|
||||
public void CheckAll_Click(object sender, Avalonia.Interactivity.RoutedEventArgs e)
|
||||
public void CheckAll()
|
||||
{
|
||||
foreach (var record in bookRecordEntries)
|
||||
record.IsChecked = true;
|
||||
}
|
||||
public void UncheckAll_Click(object sender, Avalonia.Interactivity.RoutedEventArgs e)
|
||||
public void UncheckAll()
|
||||
{
|
||||
foreach (var record in bookRecordEntries)
|
||||
record.IsChecked = false;
|
||||
|
||||
@@ -99,10 +99,10 @@
|
||||
|
||||
<Button
|
||||
Grid.Column="1"
|
||||
Height="30"
|
||||
Padding="30,3,30,3"
|
||||
Padding="30,5"
|
||||
Name="saveBtn"
|
||||
Content="Save"
|
||||
Click="SaveButton_Clicked" />
|
||||
Command="{Binding SaveAndClose}" />
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Window>
|
||||
|
||||
@@ -38,7 +38,7 @@ namespace LibationAvalonia.Dialogs
|
||||
if (!accounts.Any())
|
||||
return;
|
||||
|
||||
ControlToFocusOnShow = this.FindControl<Button>(nameof(SaveButton_Clicked));
|
||||
ControlToFocusOnShow = this.FindControl<Button>(nameof(saveBtn));
|
||||
|
||||
var allFilters = QuickFilters.Filters.Select(f => new Filter { FilterString = f }).ToList();
|
||||
allFilters.Add(new Filter());
|
||||
@@ -100,10 +100,5 @@ namespace LibationAvalonia.Dialogs
|
||||
Filters.Insert(index + 1, filter);
|
||||
}
|
||||
}
|
||||
|
||||
public void SaveButton_Clicked(object sender, Avalonia.Interactivity.RoutedEventArgs e)
|
||||
{
|
||||
SaveAndClose();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
IsReadOnly="False"
|
||||
BeginningEdit="ReplacementGrid_BeginningEdit"
|
||||
CellEditEnding="ReplacementGrid_CellEditEnding"
|
||||
KeyDown="ReplacementGrid_KeyDown"
|
||||
KeyDown="ReplacementGrid_KeyDown"
|
||||
Items="{Binding replacements}">
|
||||
|
||||
<DataGrid.Columns>
|
||||
@@ -62,9 +62,9 @@
|
||||
Margin="5"
|
||||
Orientation="Horizontal">
|
||||
|
||||
<Button Margin="0,0,10,0" Click="Defaults_Click" Content="Defaults" />
|
||||
<Button Margin="0,0,10,0" Click="LoFiDefaults_Click" Content="LoFi Defaults" />
|
||||
<Button Click="Barebones_Click" Content="Barebones" />
|
||||
<Button Margin="0,0,10,0" Command="{Binding Defaults}" Content="Defaults" />
|
||||
<Button Margin="0,0,10,0" Command="{Binding LoFiDefaults}" Content="LoFi Defaults" />
|
||||
<Button Command="{Binding Barebones}" Content="Barebones" />
|
||||
</StackPanel>
|
||||
|
||||
<StackPanel
|
||||
@@ -73,8 +73,8 @@
|
||||
Margin="5"
|
||||
Orientation="Horizontal">
|
||||
|
||||
<Button Margin="0,0,10,0" Click="Cancel_Click" Content="Cancel" />
|
||||
<Button Padding="20,5,20,6" Click="Save_Click" Content="Save" />
|
||||
<Button Margin="0,0,10,0" Command="{Binding Close}" Content="Cancel" />
|
||||
<Button Padding="20,5,20,6" Command="{Binding SaveAndClose}" Content="Save" />
|
||||
</StackPanel>
|
||||
|
||||
</Grid>
|
||||
|
||||
@@ -35,16 +35,12 @@ namespace LibationAvalonia.Dialogs
|
||||
LoadTable(config.ReplacementCharacters.Replacements);
|
||||
}
|
||||
|
||||
public void Defaults_Click(object sender, Avalonia.Interactivity.RoutedEventArgs e)
|
||||
public void Defaults()
|
||||
=> LoadTable(ReplacementCharacters.Default.Replacements);
|
||||
public void LoFiDefaults_Click(object sender, Avalonia.Interactivity.RoutedEventArgs e)
|
||||
public void LoFiDefaults()
|
||||
=> LoadTable(ReplacementCharacters.LoFiDefault.Replacements);
|
||||
public void Barebones_Click(object sender, Avalonia.Interactivity.RoutedEventArgs e)
|
||||
public void Barebones()
|
||||
=> LoadTable(ReplacementCharacters.Barebones.Replacements);
|
||||
public void Save_Click(object sender, Avalonia.Interactivity.RoutedEventArgs e)
|
||||
=> SaveAndClose();
|
||||
public void Cancel_Click(object sender, Avalonia.Interactivity.RoutedEventArgs e)
|
||||
=> Close();
|
||||
|
||||
protected override void SaveAndClose()
|
||||
{
|
||||
|
||||
@@ -33,7 +33,7 @@
|
||||
VerticalAlignment="Stretch"
|
||||
VerticalContentAlignment="Center"
|
||||
Content="Reset to Default"
|
||||
Click="ResetButton_Click" />
|
||||
Command="{Binding ResetToDefault}"/>
|
||||
</Grid>
|
||||
<Grid Grid.Row="1" ColumnDefinitions="Auto,*">
|
||||
|
||||
@@ -69,8 +69,7 @@
|
||||
|
||||
</DataGrid.Columns>
|
||||
</DataGrid>
|
||||
|
||||
|
||||
|
||||
<Grid
|
||||
Grid.Column="1"
|
||||
Margin="5,0,5,0"
|
||||
|
||||
@@ -19,14 +19,14 @@ namespace LibationAvalonia.Dialogs
|
||||
|
||||
public EditTemplateDialog()
|
||||
{
|
||||
AvaloniaXamlLoader.Load(this);
|
||||
userEditTbox = this.FindControl<TextBox>(nameof(userEditTbox));
|
||||
InitializeComponent();
|
||||
|
||||
if (Design.IsDesignMode)
|
||||
{
|
||||
_ = Configuration.Instance.LibationFiles;
|
||||
var editor = TemplateEditor<Templates.FileTemplate>.CreateFilenameEditor(Configuration.Instance.Books, Configuration.Instance.FileTemplate);
|
||||
_viewModel = new(Configuration.Instance, editor);
|
||||
_viewModel.resetTextBox(editor.EditingTemplate.TemplateText);
|
||||
_viewModel.ResetTextBox(editor.EditingTemplate.TemplateText);
|
||||
Title = $"Edit {editor.TemplateName}";
|
||||
DataContext = _viewModel;
|
||||
}
|
||||
@@ -37,7 +37,7 @@ namespace LibationAvalonia.Dialogs
|
||||
ArgumentValidator.EnsureNotNull(templateEditor, nameof(templateEditor));
|
||||
|
||||
_viewModel = new EditTemplateViewModel(Configuration.Instance, templateEditor);
|
||||
_viewModel.resetTextBox(templateEditor.EditingTemplate.TemplateText);
|
||||
_viewModel.ResetTextBox(templateEditor.EditingTemplate.TemplateText);
|
||||
Title = $"Edit {templateEditor.TemplateName}";
|
||||
DataContext = _viewModel;
|
||||
}
|
||||
@@ -67,9 +67,6 @@ namespace LibationAvalonia.Dialogs
|
||||
public async void SaveButton_Click(object sender, Avalonia.Interactivity.RoutedEventArgs e)
|
||||
=> await SaveAndCloseAsync();
|
||||
|
||||
public void ResetButton_Click(object sender, Avalonia.Interactivity.RoutedEventArgs e)
|
||||
=> _viewModel.resetTextBox(_viewModel.TemplateEditor.DefaultTemplate);
|
||||
|
||||
private class EditTemplateViewModel : ViewModels.ViewModelBase
|
||||
{
|
||||
private readonly Configuration config;
|
||||
@@ -115,7 +112,8 @@ namespace LibationAvalonia.Dialogs
|
||||
|
||||
public AvaloniaList<Tuple<string, string, string>> ListItems { get; set; }
|
||||
|
||||
public void resetTextBox(string value) => UserTemplateText = value;
|
||||
public void ResetTextBox(string value) => UserTemplateText = value;
|
||||
public void ResetToDefault() => ResetTextBox(TemplateEditor.DefaultTemplate);
|
||||
|
||||
public async Task<bool> Validate()
|
||||
{
|
||||
|
||||
@@ -27,6 +27,6 @@
|
||||
Margin="5"
|
||||
Padding="30,3,30,3"
|
||||
Content="Save"
|
||||
Click="SaveButton_Click" />
|
||||
Command="{Binding SaveButtonAsync}" />
|
||||
</Grid>
|
||||
</Window>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using Avalonia.Controls;
|
||||
using LibationFileManager;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace LibationAvalonia.Dialogs
|
||||
{
|
||||
@@ -27,9 +28,8 @@ namespace LibationAvalonia.Dialogs
|
||||
DataContext = dirSelectOptions = new();
|
||||
}
|
||||
|
||||
public async void SaveButton_Click(object sender, Avalonia.Interactivity.RoutedEventArgs e)
|
||||
public async Task SaveButtonAsync()
|
||||
{
|
||||
|
||||
var libationDir = dirSelectOptions.Directory;
|
||||
|
||||
if (!System.IO.Directory.Exists(libationDir))
|
||||
|
||||
@@ -2,21 +2,21 @@
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
mc:Ignorable="d" d:DesignWidth="400" d:DesignHeight="120"
|
||||
mc:Ignorable="d" d:DesignWidth="550" d:DesignHeight="135"
|
||||
xmlns:controls="clr-namespace:LibationAvalonia.Controls"
|
||||
x:Class="LibationAvalonia.Dialogs.LiberatedStatusBatchAutoDialog"
|
||||
Title="Liberated status: Whether the book has been downloaded"
|
||||
MinHeight="100" MaxHeight="165"
|
||||
MinWidth="600" MaxWidth="800"
|
||||
Width="600"
|
||||
MinHeight="135" MaxHeight="135"
|
||||
MinWidth="550" MaxWidth="550"
|
||||
Width="550" Height="135"
|
||||
WindowStartupLocation="CenterOwner"
|
||||
Icon="/Assets/libation.ico">
|
||||
|
||||
<Grid RowDefinitions="Auto,Auto,Auto">
|
||||
|
||||
<Grid Margin="10" RowDefinitions="Auto,Auto">
|
||||
|
||||
<StackPanel
|
||||
Grid.Row="0"
|
||||
Orientation="Horizontal">
|
||||
Orientation="Vertical">
|
||||
|
||||
<CheckBox
|
||||
Margin="0,0,0,10"
|
||||
@@ -26,12 +26,6 @@
|
||||
TextWrapping="Wrap"
|
||||
Text="If the audio file can be found, set download status to 'Downloaded'" />
|
||||
</CheckBox>
|
||||
</StackPanel>
|
||||
|
||||
<StackPanel
|
||||
Grid.Row="1"
|
||||
Orientation="Horizontal">
|
||||
|
||||
<CheckBox
|
||||
Margin="0,0,0,10"
|
||||
IsChecked="{Binding SetNotDownloaded, Mode=TwoWay}">
|
||||
@@ -43,12 +37,10 @@
|
||||
</StackPanel>
|
||||
|
||||
<Button
|
||||
Grid.Row="2"
|
||||
Padding="30,0,30,0"
|
||||
Margin="10,0,10,10"
|
||||
Grid.Row="1"
|
||||
Padding="30,5"
|
||||
HorizontalAlignment="Right"
|
||||
Height="25"
|
||||
Content="Save"
|
||||
Click="SaveButton_Clicked"/>
|
||||
Command="{Binding SaveAndClose}"/>
|
||||
</Grid>
|
||||
</Window>
|
||||
|
||||
@@ -10,8 +10,5 @@ namespace LibationAvalonia.Dialogs
|
||||
InitializeComponent();
|
||||
DataContext = this;
|
||||
}
|
||||
|
||||
public void SaveButton_Clicked(object sender, Avalonia.Interactivity.RoutedEventArgs e)
|
||||
=> SaveAndClose();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,25 +2,25 @@
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
mc:Ignorable="d" d:DesignWidth="400" d:DesignHeight="120"
|
||||
mc:Ignorable="d" d:DesignWidth="400" d:DesignHeight="100"
|
||||
xmlns:controls="clr-namespace:LibationAvalonia.Controls"
|
||||
x:Class="LibationAvalonia.Dialogs.LiberatedStatusBatchManualDialog"
|
||||
Title="Liberated status: Whether the book has been downloaded"
|
||||
MinWidth="400" MinHeight="120"
|
||||
MaxWidth="400" MaxHeight="120"
|
||||
Width="400" Height="120"
|
||||
MinWidth="400" MinHeight="100"
|
||||
MaxWidth="400" MaxHeight="100"
|
||||
Width="400" Height="100"
|
||||
WindowStartupLocation="CenterOwner"
|
||||
Icon="/Assets/libation.ico">
|
||||
|
||||
<Grid RowDefinitions="Auto,Auto,Auto">
|
||||
<Grid RowDefinitions="Auto,Auto" ColumnDefinitions="*,Auto">
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="0"
|
||||
Margin="10,10,10,0"
|
||||
Grid.ColumnSpan="2"
|
||||
Margin="10"
|
||||
Text="To download again next time: change to Not Downloaded
To not download: change to Downloaded"/>
|
||||
|
||||
<StackPanel
|
||||
Margin="10"
|
||||
Margin="10,0"
|
||||
Grid.Row="1"
|
||||
Orientation="Horizontal">
|
||||
|
||||
@@ -36,7 +36,7 @@
|
||||
Height="25"
|
||||
VerticalAlignment="Center"
|
||||
SelectedItem="{Binding SelectedItem, Mode=TwoWay}"
|
||||
Items="{Binding BookStatuses}">
|
||||
ItemsSource="{Binding BookStatuses}">
|
||||
|
||||
<ComboBox.ItemTemplate>
|
||||
<DataTemplate>
|
||||
@@ -49,14 +49,15 @@
|
||||
</ComboBox.ItemTemplate>
|
||||
</controls:WheelComboBox>
|
||||
</StackPanel>
|
||||
|
||||
|
||||
<Button
|
||||
Grid.Row="2"
|
||||
Padding="30,0,30,0"
|
||||
Margin="10,0,10,10"
|
||||
Grid.Row="1"
|
||||
Grid.Column="1"
|
||||
Margin="10,0"
|
||||
Padding="30,5"
|
||||
VerticalAlignment="Stretch"
|
||||
HorizontalAlignment="Right"
|
||||
Height="25"
|
||||
Content="Save"
|
||||
Click="SaveButton_Clicked"/>
|
||||
Click="SaveButton_Clicked" />
|
||||
</Grid>
|
||||
</Window>
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
<TextBlock Text="IDs Found: " />
|
||||
<TextBlock Text="{Binding FoundAsins}" />
|
||||
</StackPanel>
|
||||
<ListBox Margin="0,5,0,0" Grid.Row="1" Grid.ColumnSpan="2" Name="foundAudiobooksLB" Items="{Binding FoundFiles}" AutoScrollToSelectedItem="true">
|
||||
<ListBox Margin="0,5,0,0" Grid.Row="1" Grid.ColumnSpan="2" Name="foundAudiobooksLB" ItemsSource="{Binding FoundFiles}" AutoScrollToSelectedItem="true">
|
||||
<ListBox.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<Grid ColumnDefinitions="Auto,*">
|
||||
|
||||
@@ -89,7 +89,7 @@ namespace LibationAvalonia.Dialogs
|
||||
FilePathCache.Insert(book);
|
||||
|
||||
var lb = context.GetLibraryBook_Flat_NoTracking(book.Id);
|
||||
if (lb.Book.UserDefinedItem.BookStatus is not LiberatedStatus.Liberated)
|
||||
if (lb?.Book?.UserDefinedItem.BookStatus is not LiberatedStatus.Liberated)
|
||||
await Task.Run(() => lb.UpdateBookStatus(LiberatedStatus.Liberated));
|
||||
|
||||
FileFound?.Invoke(this, book);
|
||||
|
||||
@@ -2,9 +2,10 @@
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
mc:Ignorable="d" d:DesignWidth="240" d:DesignHeight="120"
|
||||
MinWidth="240" MinHeight="120"
|
||||
MaxWidth="240" MaxHeight="120"
|
||||
mc:Ignorable="d" d:DesignWidth="240" d:DesignHeight="140"
|
||||
MinWidth="240" MinHeight="140"
|
||||
MaxWidth="240" MaxHeight="140"
|
||||
Width="240" Height="140"
|
||||
x:Class="LibationAvalonia.Dialogs.Login.ApprovalNeededDialog"
|
||||
Title="Approval Alert Detected"
|
||||
Icon="/Assets/libation.ico">
|
||||
|
||||
@@ -25,7 +25,8 @@ namespace LibationAvalonia.Dialogs.Login
|
||||
{
|
||||
var dialog = new LoginChoiceEagerDialog(_account);
|
||||
|
||||
if (await dialog.ShowDialogAsync() is not DialogResult.OK || string.IsNullOrWhiteSpace(dialog.Password))
|
||||
if (await dialog.ShowDialogAsync() is not DialogResult.OK ||
|
||||
(dialog.LoginMethod is LoginMethod.Api && string.IsNullOrWhiteSpace(dialog.Password)))
|
||||
return null;
|
||||
|
||||
switch (dialog.LoginMethod)
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
mc:Ignorable="d" d:DesignWidth="220" d:DesignHeight="250"
|
||||
MinWidth="220" MinHeight="250"
|
||||
MaxWidth="220" MaxHeight="250"
|
||||
Width="220" Height="250"
|
||||
x:Class="LibationAvalonia.Dialogs.Login.CaptchaDialog"
|
||||
Title="CAPTCHA"
|
||||
Icon="/Assets/libation.ico">
|
||||
|
||||
@@ -4,7 +4,8 @@
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
mc:Ignorable="d" d:DesignWidth="400" d:DesignHeight="200"
|
||||
MinWidth="400" MinHeight="200"
|
||||
MaxWidth="400" MaxHeight="200"
|
||||
MaxWidth="400" MaxHeight="400"
|
||||
Width="400" Height="200"
|
||||
x:Class="LibationAvalonia.Dialogs.Login.MfaDialog"
|
||||
Title="Two-Step Verification"
|
||||
Icon="/Assets/libation.ico">
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
mc:Ignorable="d" d:DesignWidth="200" d:DesignHeight="200"
|
||||
MinWidth="200" MinHeight="200"
|
||||
MaxWidth="200" MaxHeight="200"
|
||||
Width="200" Height="200"
|
||||
x:Class="LibationAvalonia.Dialogs.Login._2faCodeDialog"
|
||||
Title="2FA Code"
|
||||
Icon="/Assets/libation.ico">
|
||||
|
||||
@@ -40,7 +40,7 @@
|
||||
<Button Grid.Column="1" IsVisible="{Binding HasButton2}" MinWidth="75" MinHeight="28" Name="Button2" Click="Button2_Click" Margin="5">
|
||||
<TextBlock VerticalAlignment="Center" HorizontalAlignment="Center" Text="{Binding Button2Text}"/>
|
||||
</Button>
|
||||
<Button Grid.Column="2" IsVisible="{Binding HasButton3}" MinWidth="75" MinHeight="28" Name="Button3" Click="Button3_Click" Content="Cancel" Margin="5">
|
||||
<Button Grid.Column="2" IsVisible="{Binding HasButton3}" MinWidth="75" MinHeight="28" Name="Button3" Click="Button3_Click" Margin="5">
|
||||
<TextBlock VerticalAlignment="Center" HorizontalAlignment="Center" Text="{Binding Button3Text}"/>
|
||||
</Button>
|
||||
</StackPanel>
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
mc:Ignorable="d" d:DesignWidth="500" d:DesignHeight="185"
|
||||
mc:Ignorable="d" d:DesignWidth="500" d:DesignHeight="200"
|
||||
x:Class="LibationAvalonia.Dialogs.ScanAccountsDialog"
|
||||
MinWidth="500" MinHeight="160"
|
||||
Width="500" Height="200"
|
||||
@@ -10,7 +10,7 @@
|
||||
WindowStartupLocation="CenterOwner"
|
||||
Icon="/Assets/libation.ico">
|
||||
|
||||
<Grid ColumnDefinitions="*,Auto" RowDefinitions="Auto,Auto,Auto">
|
||||
<Grid ColumnDefinitions="*,Auto" RowDefinitions="Auto,*,Auto">
|
||||
|
||||
<Grid.Styles>
|
||||
<Style Selector="Button:focus">
|
||||
@@ -23,20 +23,19 @@
|
||||
Grid.Row="0"
|
||||
Grid.Column="0"
|
||||
Grid.ColumnSpan="2"
|
||||
Margin="10,10,10,0"
|
||||
Margin="10"
|
||||
Text="Check the accounts to scan and import.
To change default selections, go to: Settings > Accounts"/>
|
||||
|
||||
<ScrollViewer
|
||||
Grid.Row="1"
|
||||
Grid.Column="0"
|
||||
Grid.ColumnSpan="2"
|
||||
Margin="10,0"
|
||||
VerticalAlignment="Stretch"
|
||||
HorizontalScrollBarVisibility="Disabled"
|
||||
VerticalScrollBarVisibility="Auto"
|
||||
Margin="10"
|
||||
MinHeight="90"
|
||||
MaxHeight="90">
|
||||
VerticalScrollBarVisibility="Auto">
|
||||
|
||||
<ListBox Items="{Binding Accounts}">
|
||||
<ListBox ItemsSource="{Binding Accounts}">
|
||||
<ListBox.ItemTemplate>
|
||||
<DataTemplate>
|
||||
|
||||
@@ -62,21 +61,19 @@
|
||||
<Button
|
||||
Grid.Row="2"
|
||||
Grid.Column="0"
|
||||
Padding="20,0,20,0"
|
||||
Margin="10,0,10,10"
|
||||
Height="25"
|
||||
Padding="20,5"
|
||||
Margin="10"
|
||||
Content="Edit Accounts"
|
||||
Click="EditAccountsButton_Clicked"/>
|
||||
Command="{Binding EditAccountsAsync}"/>
|
||||
|
||||
<Button
|
||||
Grid.Row="2"
|
||||
Grid.Column="1"
|
||||
Padding="30,0,30,0"
|
||||
Margin="10,0,10,10"
|
||||
Padding="30,5"
|
||||
Margin="10"
|
||||
HorizontalAlignment="Right"
|
||||
Height="25"
|
||||
Content="Import"
|
||||
Name="ImportButton"
|
||||
Click="ImportButton_Clicked"/>
|
||||
Command="{Binding SaveAndClose}"/>
|
||||
</Grid>
|
||||
</Window>
|
||||
|
||||
@@ -4,6 +4,7 @@ using Avalonia.Interactivity;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace LibationAvalonia.Dialogs
|
||||
{
|
||||
@@ -25,7 +26,7 @@ namespace LibationAvalonia.Dialogs
|
||||
InitializeComponent();
|
||||
|
||||
this.HideMinMaxBtns();
|
||||
this.Opened += ScanAccountsDialog_Opened;
|
||||
ControlToFocusOnShow = this.FindControl<Button>(nameof(ImportButton));
|
||||
|
||||
LoadAccounts();
|
||||
}
|
||||
@@ -46,12 +47,7 @@ namespace LibationAvalonia.Dialogs
|
||||
DataContext = this;
|
||||
}
|
||||
|
||||
private void ScanAccountsDialog_Opened(object sender, System.EventArgs e)
|
||||
{
|
||||
this.FindControl<Button>(nameof(ImportButton)).Focus();
|
||||
}
|
||||
|
||||
public async void EditAccountsButton_Clicked(object sender, RoutedEventArgs e)
|
||||
public async Task EditAccountsAsync()
|
||||
{
|
||||
if (await new AccountsDialog().ShowDialog<DialogResult>(this) == DialogResult.OK)
|
||||
{
|
||||
@@ -67,7 +63,5 @@ namespace LibationAvalonia.Dialogs
|
||||
|
||||
base.SaveAndClose();
|
||||
}
|
||||
|
||||
public void ImportButton_Clicked(object sender, RoutedEventArgs e) => SaveAndClose();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,10 +3,13 @@
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
mc:Ignorable="d" d:DesignWidth="900" d:DesignHeight="750"
|
||||
MinWidth="900" MinHeight="700"
|
||||
Width="900" Height="700"
|
||||
MinWidth="900" MinHeight="750"
|
||||
Width="900" Height="750"
|
||||
x:Class="LibationAvalonia.Dialogs.SettingsDialog"
|
||||
xmlns:controls="clr-namespace:LibationAvalonia.Controls"
|
||||
xmlns:settings="clr-namespace:LibationAvalonia.Controls.Settings"
|
||||
xmlns:vm="clr-namespace:LibationAvalonia.ViewModels.Settings"
|
||||
x:DataType="vm:SettingsVM"
|
||||
Title="Edit Settings"
|
||||
Icon="/Assets/libation.ico">
|
||||
|
||||
@@ -24,789 +27,58 @@
|
||||
|
||||
<TabControl Name="tabControl" Grid.Column="0">
|
||||
<TabControl.Styles>
|
||||
<Style Selector="ItemsPresenter#PART_ItemsPresenter">
|
||||
<Setter Property="Height" Value="28"/>
|
||||
<Style Selector="TabControl /template/ ItemsPresenter#PART_ItemsPresenter">
|
||||
<Setter Property="Height" Value="30"/>
|
||||
</Style>
|
||||
<Style Selector="TabControl /template/ ContentPresenter#PART_SelectedContentHost">
|
||||
<Setter Property="BorderBrush" Value="{DynamicResource SystemBaseLowColor}" />
|
||||
<Setter Property="BorderThickness" Value="1" />
|
||||
</Style>
|
||||
<Style Selector="TabItem">
|
||||
<Setter Property="MinHeight" Value="40"/>
|
||||
<Setter Property="Height" Value="40"/>
|
||||
<Setter Property="MinHeight" Value="45"/>
|
||||
<Setter Property="Height" Value="45"/>
|
||||
<Setter Property="Padding" Value="8,2,8,10"/>
|
||||
</Style>
|
||||
<Style Selector="TabItem#Header TextBlock">
|
||||
<Setter Property="MinHeight" Value="5"/>
|
||||
<Style Selector="^ > TextBlock" >
|
||||
<Setter Property="FontSize" Value="14" />
|
||||
<Setter Property="VerticalAlignment" Value="Center" />
|
||||
</Style>
|
||||
</Style>
|
||||
</TabControl.Styles>
|
||||
|
||||
|
||||
<TabItem>
|
||||
|
||||
<TabItem.Header>
|
||||
|
||||
<TextBlock
|
||||
FontSize="14"
|
||||
VerticalAlignment="Center"
|
||||
Text="Important Settings"/>
|
||||
|
||||
<TextBlock Text="Important Settings"/>
|
||||
</TabItem.Header>
|
||||
|
||||
<Border
|
||||
Grid.Column="0"
|
||||
Grid.Row="0"
|
||||
BorderThickness="2"
|
||||
BorderBrush="{DynamicResource DataGridGridLinesBrush}">
|
||||
|
||||
<Grid RowDefinitions="Auto,Auto,*">
|
||||
|
||||
<controls:GroupBox
|
||||
Grid.Row="0"
|
||||
Margin="5"
|
||||
BorderWidth="1"
|
||||
Label="Books Location">
|
||||
|
||||
<StackPanel>
|
||||
<TextBlock
|
||||
Margin="5"
|
||||
Text="{Binding ImportantSettings.BooksText}" />
|
||||
|
||||
<controls:DirectoryOrCustomSelectControl Margin="0,10,0,10"
|
||||
SubDirectory="Books"
|
||||
Directory="{Binding ImportantSettings.BooksDirectory, Mode=TwoWay}"
|
||||
KnownDirectories="{Binding ImportantSettings.KnownDirectories}" />
|
||||
|
||||
<CheckBox IsChecked="{Binding ImportantSettings.SavePodcastsToParentFolder, Mode=TwoWay}">
|
||||
<TextBlock Text="{Binding ImportantSettings.SavePodcastsToParentFolderText}" />
|
||||
</CheckBox>
|
||||
|
||||
</StackPanel>
|
||||
|
||||
</controls:GroupBox>
|
||||
|
||||
<StackPanel
|
||||
Grid.Row="1" Margin="5"
|
||||
Orientation="Horizontal">
|
||||
|
||||
<TextBlock
|
||||
Margin="0,0,10,0"
|
||||
VerticalAlignment="Center"
|
||||
Text="Logging level" />
|
||||
|
||||
<controls:WheelComboBox
|
||||
Width="150"
|
||||
Height="25"
|
||||
HorizontalContentAlignment="Stretch"
|
||||
SelectedItem="{Binding ImportantSettings.LoggingLevel, Mode=TwoWay}"
|
||||
Items="{Binding ImportantSettings.LoggingLevels}" />
|
||||
|
||||
<Button
|
||||
Margin="50,0,0,0"
|
||||
Padding="20,3,20,3"
|
||||
Content="Open Log Folder"
|
||||
Click="OpenLogFolderButton_Click" />
|
||||
|
||||
</StackPanel>
|
||||
<!--
|
||||
<CheckBox
|
||||
Grid.Row="2"
|
||||
Margin="5"
|
||||
VerticalAlignment="Bottom"
|
||||
IsChecked="{Binding ImportantSettings.BetaOptIn, Mode=TwoWay}">
|
||||
|
||||
<TextBlock Text="{Binding ImportantSettings.BetaOptInText}" />
|
||||
|
||||
</CheckBox>
|
||||
-->
|
||||
<Grid
|
||||
Grid.Row="2"
|
||||
ColumnDefinitions="Auto,Auto,*"
|
||||
Margin="10"
|
||||
VerticalAlignment="Bottom">
|
||||
<TextBlock
|
||||
Grid.Column="0"
|
||||
FontSize="16"
|
||||
VerticalAlignment="Center"
|
||||
Text="Theme: "/>
|
||||
|
||||
<controls:WheelComboBox
|
||||
Grid.Column="1"
|
||||
SelectedItem="{Binding ImportantSettings.ThemeVariant, Mode=TwoWay}"
|
||||
Items="{Binding ImportantSettings.Themes}" />
|
||||
<TextBlock
|
||||
Grid.Column="2"
|
||||
FontSize="16"
|
||||
FontWeight="Bold"
|
||||
Margin="10,0,0,0"
|
||||
VerticalAlignment="Center"
|
||||
IsVisible="{Binding ImportantSettings.SelectionChanged}"
|
||||
Text="Theme change takes effect on restart"/>
|
||||
|
||||
</Grid>
|
||||
|
||||
</Grid>
|
||||
</Border>
|
||||
</TabItem>
|
||||
|
||||
|
||||
<TabItem>
|
||||
|
||||
<TabItem.Header>
|
||||
|
||||
<TextBlock
|
||||
FontSize="14"
|
||||
VerticalAlignment="Center"
|
||||
Text="Import Library"/>
|
||||
|
||||
</TabItem.Header>
|
||||
|
||||
<Border
|
||||
Grid.Column="0"
|
||||
Grid.Row="0"
|
||||
BorderThickness="2"
|
||||
BorderBrush="{DynamicResource DataGridGridLinesBrush}">
|
||||
|
||||
<StackPanel Margin="5">
|
||||
|
||||
<CheckBox
|
||||
Margin="0,0,0,10"
|
||||
IsChecked="{Binding ImportSettings.AutoScan, Mode=TwoWay}">
|
||||
|
||||
<TextBlock
|
||||
TextWrapping="Wrap"
|
||||
Text="{Binding ImportSettings.AutoScanText}" />
|
||||
</CheckBox>
|
||||
|
||||
<CheckBox
|
||||
Margin="0,0,0,10"
|
||||
IsChecked="{Binding ImportSettings.ShowImportedStats, Mode=TwoWay}">
|
||||
|
||||
<TextBlock
|
||||
TextWrapping="Wrap"
|
||||
Text="{Binding ImportSettings.ShowImportedStatsText}" />
|
||||
|
||||
</CheckBox>
|
||||
|
||||
<CheckBox
|
||||
Margin="0,0,0,10"
|
||||
IsChecked="{Binding ImportSettings.ImportEpisodes, Mode=TwoWay}">
|
||||
|
||||
<TextBlock
|
||||
TextWrapping="Wrap"
|
||||
Text="{Binding ImportSettings.ImportEpisodesText}" />
|
||||
|
||||
</CheckBox>
|
||||
|
||||
<CheckBox
|
||||
Margin="0,0,0,10"
|
||||
IsChecked="{Binding ImportSettings.DownloadEpisodes, Mode=TwoWay}">
|
||||
|
||||
<TextBlock
|
||||
TextWrapping="Wrap"
|
||||
Text="{Binding ImportSettings.DownloadEpisodesText}" />
|
||||
|
||||
</CheckBox>
|
||||
|
||||
<CheckBox
|
||||
Margin="0,0,0,10"
|
||||
IsChecked="{Binding ImportSettings.AutoDownloadEpisodes, Mode=TwoWay}">
|
||||
|
||||
<TextBlock
|
||||
TextWrapping="Wrap"
|
||||
Text="{Binding ImportSettings.AutoDownloadEpisodesText}" />
|
||||
|
||||
</CheckBox>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
<settings:Important DataContext="{CompiledBinding ImportantSettings}" />
|
||||
</TabItem>
|
||||
|
||||
<TabItem>
|
||||
|
||||
<TabItem.Header>
|
||||
|
||||
<TextBlock
|
||||
FontSize="14"
|
||||
VerticalAlignment="Center"
|
||||
Text="Download/Decrypt"/>
|
||||
|
||||
<TextBlock Text="Import Library"/>
|
||||
</TabItem.Header>
|
||||
|
||||
<Border
|
||||
Grid.Column="0"
|
||||
Grid.Row="0"
|
||||
BorderThickness="2"
|
||||
BorderBrush="{DynamicResource DataGridGridLinesBrush}">
|
||||
|
||||
<Grid RowDefinitions="Auto,Auto,Auto,*">
|
||||
<controls:GroupBox
|
||||
Grid.Row="0"
|
||||
Margin="5"
|
||||
BorderWidth="1"
|
||||
Label="{Binding DownloadDecryptSettings.BadBookGroupboxText}">
|
||||
|
||||
<Grid
|
||||
ColumnDefinitions="*,*"
|
||||
RowDefinitions="Auto,Auto">
|
||||
|
||||
<RadioButton
|
||||
Grid.Column="0"
|
||||
Grid.Row="0"
|
||||
Margin="0,5,0,5"
|
||||
IsChecked="{Binding DownloadDecryptSettings.BadBookAsk, Mode=TwoWay}">
|
||||
|
||||
<TextBlock
|
||||
TextWrapping="Wrap"
|
||||
Text="{Binding DownloadDecryptSettings.BadBookAskText}" />
|
||||
|
||||
</RadioButton>
|
||||
|
||||
<RadioButton
|
||||
Grid.Column="1"
|
||||
Grid.Row="0"
|
||||
Margin="0,5,0,5"
|
||||
IsChecked="{Binding DownloadDecryptSettings.BadBookAbort, Mode=TwoWay}">
|
||||
|
||||
<TextBlock
|
||||
TextWrapping="Wrap"
|
||||
Text="{Binding DownloadDecryptSettings.BadBookAbortText}" />
|
||||
|
||||
</RadioButton>
|
||||
|
||||
<RadioButton
|
||||
Grid.Column="0"
|
||||
Grid.Row="1"
|
||||
Margin="0,5,0,5"
|
||||
IsChecked="{Binding DownloadDecryptSettings.BadBookRetry, Mode=TwoWay}">
|
||||
|
||||
<TextBlock
|
||||
TextWrapping="Wrap"
|
||||
Text="{Binding DownloadDecryptSettings.BadBookRetryText}" />
|
||||
|
||||
</RadioButton>
|
||||
|
||||
<RadioButton
|
||||
Grid.Column="1"
|
||||
Grid.Row="1"
|
||||
Margin="0,5,0,5"
|
||||
IsChecked="{Binding DownloadDecryptSettings.BadBookIgnore, Mode=TwoWay}">
|
||||
|
||||
<TextBlock
|
||||
TextWrapping="Wrap"
|
||||
Text="{Binding DownloadDecryptSettings.BadBookIgnoreText}" />
|
||||
|
||||
</RadioButton>
|
||||
</Grid>
|
||||
</controls:GroupBox>
|
||||
|
||||
<controls:GroupBox
|
||||
Margin="5"
|
||||
Grid.Row="1"
|
||||
BorderWidth="1"
|
||||
Label="Custom File Naming">
|
||||
|
||||
<Grid
|
||||
RowDefinitions="Auto,Auto,Auto,Auto,Auto,Auto,Auto"
|
||||
ColumnDefinitions="*,Auto">
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="0"
|
||||
Grid.Column="0"
|
||||
Margin="0,5,0,0"
|
||||
Text="{Binding DownloadDecryptSettings.FolderTemplateText}" />
|
||||
|
||||
<TextBox
|
||||
Grid.Row="1"
|
||||
Grid.Column="0"
|
||||
Margin="0,5,10,10"
|
||||
FontSize="14"
|
||||
IsReadOnly="True"
|
||||
Text="{Binding DownloadDecryptSettings.FolderTemplate}" />
|
||||
|
||||
<Button
|
||||
Grid.Row="1"
|
||||
Grid.Column="1"
|
||||
Content="Edit"
|
||||
Height="30"
|
||||
Padding="30,3,30,3"
|
||||
Click="EditFolderTemplateButton_Click" />
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="2"
|
||||
Grid.Column="0"
|
||||
Text="{Binding DownloadDecryptSettings.FileTemplateText}" />
|
||||
|
||||
<TextBox
|
||||
Grid.Row="3"
|
||||
Grid.Column="0"
|
||||
Margin="0,5,10,10"
|
||||
FontSize="14"
|
||||
IsReadOnly="True"
|
||||
Text="{Binding DownloadDecryptSettings.FileTemplate}" />
|
||||
|
||||
<Button
|
||||
Grid.Row="3"
|
||||
Grid.Column="1"
|
||||
Content="Edit"
|
||||
Height="30"
|
||||
Padding="30,3,30,3"
|
||||
Click="EditFileTemplateButton_Click" />
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="4"
|
||||
Grid.Column="0"
|
||||
Text="{Binding DownloadDecryptSettings.ChapterFileTemplateText}" />
|
||||
|
||||
<TextBox
|
||||
Grid.Row="5"
|
||||
Grid.Column="0"
|
||||
Margin="0,5,10,10"
|
||||
FontSize="14"
|
||||
IsReadOnly="True"
|
||||
Text="{Binding DownloadDecryptSettings.ChapterFileTemplate}" />
|
||||
|
||||
<Button
|
||||
Grid.Row="5"
|
||||
Grid.Column="1"
|
||||
Content="Edit"
|
||||
Height="30"
|
||||
Padding="30,3,30,3"
|
||||
Click="EditChapterFileTemplateButton_Click" />
|
||||
|
||||
<Button
|
||||
Grid.Row="6"
|
||||
Grid.Column="0"
|
||||
Content="{Binding DownloadDecryptSettings.EditCharReplacementText}"
|
||||
Height="30"
|
||||
Padding="30,3,30,3"
|
||||
Click="EditCharReplacementButton_Click" />
|
||||
|
||||
</Grid>
|
||||
</controls:GroupBox>
|
||||
<controls:GroupBox
|
||||
Grid.Row="2"
|
||||
Margin="5"
|
||||
BorderWidth="1"
|
||||
Label="Temporary Files Location">
|
||||
|
||||
<StackPanel
|
||||
Margin="5" >
|
||||
|
||||
<TextBlock
|
||||
Margin="0,0,0,10"
|
||||
Text="{Binding DownloadDecryptSettings.InProgressDescriptionText}" />
|
||||
|
||||
<controls:DirectoryOrCustomSelectControl
|
||||
Directory="{Binding DownloadDecryptSettings.InProgressDirectory, Mode=TwoWay}"
|
||||
KnownDirectories="{Binding DownloadDecryptSettings.KnownDirectories}" />
|
||||
|
||||
|
||||
</StackPanel>
|
||||
</controls:GroupBox>
|
||||
|
||||
<CheckBox
|
||||
Grid.Row="3"
|
||||
Margin="5"
|
||||
VerticalAlignment="Top"
|
||||
IsVisible="{Binding !IsLinux}"
|
||||
IsChecked="{Binding DownloadDecryptSettings.UseCoverAsFolderIcon, Mode=TwoWay}">
|
||||
|
||||
<TextBlock
|
||||
TextWrapping="Wrap"
|
||||
Text="{Binding DownloadDecryptSettings.UseCoverAsFolderIconText}" />
|
||||
|
||||
</CheckBox>
|
||||
|
||||
</Grid>
|
||||
</Border>
|
||||
<settings:Import DataContext="{CompiledBinding ImportSettings}" />
|
||||
</TabItem>
|
||||
|
||||
<TabItem>
|
||||
|
||||
<TabItem.Header>
|
||||
|
||||
<TextBlock
|
||||
FontSize="14"
|
||||
VerticalAlignment="Center"
|
||||
Text="Audio File Settings"/>
|
||||
|
||||
<TextBlock Text="Download/Decrypt"/>
|
||||
</TabItem.Header>
|
||||
|
||||
<Border
|
||||
Grid.Column="0"
|
||||
Grid.Row="0"
|
||||
BorderThickness="2"
|
||||
BorderBrush="{DynamicResource DataGridGridLinesBrush}">
|
||||
<settings:DownloadDecrypt DataContext="{CompiledBinding DownloadDecryptSettings}" />
|
||||
</TabItem>
|
||||
|
||||
<Grid
|
||||
RowDefinitions="*,Auto"
|
||||
ColumnDefinitions="*,*">
|
||||
<TabItem>
|
||||
|
||||
<StackPanel
|
||||
Margin="5"
|
||||
Grid.Row="0"
|
||||
Grid.Column="0">
|
||||
<TabItem.Header>
|
||||
<TextBlock Text="Audio File Settings"/>
|
||||
</TabItem.Header>
|
||||
|
||||
<CheckBox
|
||||
Margin="0,0,0,5"
|
||||
IsChecked="{Binding AudioSettings.CreateCueSheet, Mode=TwoWay}">
|
||||
|
||||
<TextBlock
|
||||
TextWrapping="Wrap"
|
||||
Text="{Binding AudioSettings.CreateCueSheetText}" />
|
||||
|
||||
</CheckBox>
|
||||
|
||||
<CheckBox
|
||||
Margin="0,0,0,5"
|
||||
IsChecked="{Binding AudioSettings.DownloadCoverArt, Mode=TwoWay}">
|
||||
|
||||
<TextBlock
|
||||
TextWrapping="Wrap"
|
||||
Text="{Binding AudioSettings.DownloadCoverArtText}" />
|
||||
|
||||
</CheckBox>
|
||||
|
||||
<StackPanel Orientation="Horizontal">
|
||||
|
||||
<CheckBox
|
||||
Margin="0,0,0,5"
|
||||
IsChecked="{Binding AudioSettings.DownloadClipsBookmarks, Mode=TwoWay}">
|
||||
|
||||
<TextBlock
|
||||
TextWrapping="Wrap"
|
||||
Text="Download Clips, Notes and Bookmarks as" />
|
||||
|
||||
</CheckBox>
|
||||
|
||||
<controls:WheelComboBox
|
||||
Margin="5,0,0,0"
|
||||
IsEnabled="{Binding AudioSettings.DownloadClipsBookmarks}"
|
||||
Items="{Binding AudioSettings.ClipBookmarkFormats}"
|
||||
SelectedItem="{Binding AudioSettings.ClipBookmarkFormat}"/>
|
||||
|
||||
</StackPanel>
|
||||
|
||||
<CheckBox
|
||||
Margin="0,0,0,5"
|
||||
IsChecked="{Binding AudioSettings.RetainAaxFile, Mode=TwoWay}">
|
||||
|
||||
<TextBlock
|
||||
TextWrapping="Wrap"
|
||||
Text="{Binding AudioSettings.RetainAaxFileText}" />
|
||||
|
||||
</CheckBox>
|
||||
|
||||
<CheckBox
|
||||
Margin="0,0,0,5"
|
||||
IsChecked="{Binding AudioSettings.MergeOpeningAndEndCredits, Mode=TwoWay}">
|
||||
|
||||
<TextBlock
|
||||
TextWrapping="Wrap"
|
||||
Text="{Binding AudioSettings.MergeOpeningEndCreditsText}" />
|
||||
|
||||
</CheckBox>
|
||||
|
||||
<CheckBox
|
||||
Margin="0,0,0,5"
|
||||
IsChecked="{Binding AudioSettings.AllowLibationFixup, Mode=TwoWay}">
|
||||
|
||||
<TextBlock
|
||||
TextWrapping="Wrap"
|
||||
Text="{Binding AudioSettings.AllowLibationFixupText}" />
|
||||
</CheckBox>
|
||||
|
||||
<controls:GroupBox
|
||||
BorderWidth="1"
|
||||
Label="Audiobook Fix-ups"
|
||||
IsEnabled="{Binding AudioSettings.AllowLibationFixup}">
|
||||
|
||||
<StackPanel Orientation="Vertical">
|
||||
|
||||
<CheckBox
|
||||
Margin="0,0,0,5"
|
||||
IsChecked="{Binding AudioSettings.SplitFilesByChapter, Mode=TwoWay}">
|
||||
|
||||
<TextBlock
|
||||
TextWrapping="Wrap"
|
||||
Text="{Binding AudioSettings.SplitFilesByChapterText}" />
|
||||
|
||||
</CheckBox>
|
||||
|
||||
<CheckBox
|
||||
Margin="0,0,0,5"
|
||||
IsChecked="{Binding AudioSettings.StripAudibleBrandAudio, Mode=TwoWay}">
|
||||
|
||||
<TextBlock
|
||||
TextWrapping="Wrap"
|
||||
Text="{Binding AudioSettings.StripAudibleBrandingText}" />
|
||||
|
||||
</CheckBox>
|
||||
|
||||
<CheckBox
|
||||
Margin="0,0,0,5"
|
||||
IsChecked="{Binding AudioSettings.StripUnabridged, Mode=TwoWay}">
|
||||
|
||||
<TextBlock
|
||||
TextWrapping="Wrap"
|
||||
Text="{Binding AudioSettings.StripUnabridgedText}" />
|
||||
|
||||
</CheckBox>
|
||||
|
||||
<RadioButton
|
||||
Margin="0,5,0,5"
|
||||
IsChecked="{Binding !AudioSettings.DecryptToLossy, Mode=TwoWay}">
|
||||
|
||||
<StackPanel >
|
||||
|
||||
<TextBlock
|
||||
TextWrapping="Wrap"
|
||||
Text="Download my books in the original audio format (Lossless)" />
|
||||
<CheckBox
|
||||
Margin="0,0,0,5"
|
||||
IsEnabled="{Binding !AudioSettings.DecryptToLossy}"
|
||||
IsChecked="{Binding AudioSettings.MoveMoovToBeginning, Mode=TwoWay}">
|
||||
|
||||
<TextBlock
|
||||
TextWrapping="Wrap"
|
||||
Text="{Binding AudioSettings.MoveMoovToBeginningText}" />
|
||||
|
||||
</CheckBox>
|
||||
|
||||
</StackPanel>
|
||||
</RadioButton>
|
||||
|
||||
<RadioButton
|
||||
Margin="0,5,0,5"
|
||||
IsChecked="{Binding AudioSettings.DecryptToLossy, Mode=TwoWay}">
|
||||
|
||||
<TextBlock
|
||||
TextWrapping="Wrap"
|
||||
Text="Download my books as .MP3 files (transcode if necessary)" />
|
||||
|
||||
</RadioButton>
|
||||
</StackPanel>
|
||||
</controls:GroupBox>
|
||||
</StackPanel>
|
||||
|
||||
<StackPanel
|
||||
Grid.Row="0"
|
||||
Grid.Column="1">
|
||||
|
||||
<controls:GroupBox
|
||||
BorderWidth="1"
|
||||
Label="Mp3 Encoding Options">
|
||||
|
||||
<StackPanel Orientation="Vertical">
|
||||
|
||||
<Grid
|
||||
Margin="5,5,5,0"
|
||||
ColumnDefinitions="Auto,*">
|
||||
|
||||
<controls:GroupBox
|
||||
BorderWidth="1"
|
||||
Grid.Column="0"
|
||||
Label="Target">
|
||||
|
||||
<StackPanel Orientation="Horizontal">
|
||||
|
||||
<RadioButton
|
||||
Margin="10"
|
||||
Content="Bitrate"
|
||||
IsChecked="{Binding AudioSettings.LameTargetBitrate, Mode=TwoWay}"/>
|
||||
|
||||
<RadioButton
|
||||
Margin="10"
|
||||
Content="Quality"
|
||||
IsChecked="{Binding !AudioSettings.LameTargetBitrate, Mode=TwoWay}"/>
|
||||
|
||||
</StackPanel>
|
||||
</controls:GroupBox>
|
||||
|
||||
<CheckBox
|
||||
HorizontalAlignment="Right"
|
||||
Grid.Column="1"
|
||||
IsChecked="{Binding AudioSettings.LameDownsampleMono, Mode=TwoWay}">
|
||||
|
||||
<TextBlock
|
||||
TextWrapping="Wrap"
|
||||
Text="Downsample to mono? (Recommended)" />
|
||||
|
||||
</CheckBox>
|
||||
</Grid>
|
||||
|
||||
<Grid Margin="5,5,5,0" RowDefinitions="Auto,Auto" ColumnDefinitions="Auto,*,Auto">
|
||||
|
||||
<TextBlock Margin="0,0,0,5" Text="Max audio sample rate:" />
|
||||
<controls:WheelComboBox
|
||||
Grid.Row="1"
|
||||
HorizontalAlignment="Stretch"
|
||||
Items="{Binding AudioSettings.SampleRates}"
|
||||
SelectedItem="{Binding AudioSettings.SelectedSampleRate, Mode=TwoWay}"/>
|
||||
|
||||
<TextBlock Margin="0,0,0,5" Grid.Column="2" Text="Encoder Quality:" />
|
||||
|
||||
<controls:WheelComboBox
|
||||
Grid.Column="2"
|
||||
Grid.Row="1"
|
||||
HorizontalAlignment="Stretch"
|
||||
Items="{Binding AudioSettings.EncoderQualities}"
|
||||
SelectedItem="{Binding AudioSettings.SelectedEncoderQuality, Mode=TwoWay}"/>
|
||||
</Grid>
|
||||
|
||||
<controls:GroupBox
|
||||
Margin="5,5,5,0"
|
||||
BorderWidth="1"
|
||||
Label="Bitrate"
|
||||
IsEnabled="{Binding AudioSettings.LameTargetBitrate}" >
|
||||
|
||||
<StackPanel>
|
||||
|
||||
<Grid ColumnDefinitions="*,25,Auto">
|
||||
|
||||
<Slider
|
||||
Grid.Column="0"
|
||||
IsEnabled="{Binding !AudioSettings.LameMatchSource}"
|
||||
Value="{Binding AudioSettings.LameBitrate, Mode=TwoWay}"
|
||||
Minimum="16"
|
||||
Maximum="320"
|
||||
IsSnapToTickEnabled="True" TickFrequency="16"
|
||||
Ticks="16,32,48,64,80,96,112,128,144,160,176,192,208,224,240,256,272,288,304,320"
|
||||
TickPlacement="Outside">
|
||||
|
||||
<Slider.Styles>
|
||||
<Style Selector="Slider /template/ Thumb">
|
||||
<Setter Property="ToolTip.Tip" Value="{Binding $parent[Slider].Value, Mode=OneWay, StringFormat='\{0:f0\} Kbps'}" />
|
||||
<Setter Property="ToolTip.Placement" Value="Top" />
|
||||
<Setter Property="ToolTip.VerticalOffset" Value="-10" />
|
||||
<Setter Property="ToolTip.HorizontalOffset" Value="-30" />
|
||||
</Style>
|
||||
</Slider.Styles>
|
||||
</Slider>
|
||||
|
||||
<TextBlock
|
||||
Grid.Column="1"
|
||||
HorizontalAlignment="Right"
|
||||
Text="{Binding AudioSettings.LameBitrate}" />
|
||||
|
||||
<TextBlock
|
||||
Grid.Column="2"
|
||||
Text=" Kbps" />
|
||||
|
||||
</Grid>
|
||||
|
||||
<Grid ColumnDefinitions="*,*">
|
||||
|
||||
<CheckBox
|
||||
Grid.Column="0"
|
||||
IsChecked="{Binding AudioSettings.LameConstantBitrate, Mode=TwoWay}">
|
||||
|
||||
<TextBlock
|
||||
TextWrapping="Wrap"
|
||||
Text="Restrict Encoder to Constant Bitrate?" />
|
||||
|
||||
</CheckBox>
|
||||
|
||||
<CheckBox
|
||||
Grid.Column="1"
|
||||
HorizontalAlignment="Right"
|
||||
IsChecked="{Binding AudioSettings.LameMatchSource, Mode=TwoWay}">
|
||||
|
||||
<TextBlock
|
||||
TextWrapping="Wrap"
|
||||
Text="Match Source Bitrate?" />
|
||||
|
||||
</CheckBox>
|
||||
</Grid>
|
||||
</StackPanel>
|
||||
</controls:GroupBox>
|
||||
|
||||
<controls:GroupBox
|
||||
Margin="5,5,5,0"
|
||||
BorderWidth="1"
|
||||
Label="Quality"
|
||||
IsEnabled="{Binding !AudioSettings.LameTargetBitrate}">
|
||||
|
||||
<Grid
|
||||
ColumnDefinitions="*,*,25"
|
||||
RowDefinitions="*,Auto">
|
||||
|
||||
<Slider
|
||||
Grid.Column="0"
|
||||
Grid.ColumnSpan="2"
|
||||
Value="{Binding AudioSettings.LameVBRQuality, Mode=TwoWay}"
|
||||
Minimum="0"
|
||||
Maximum="9"
|
||||
IsSnapToTickEnabled="True" TickFrequency="1"
|
||||
Ticks="0,1,2,3,4,5,6,7,8,9"
|
||||
TickPlacement="Outside">
|
||||
<Slider.Styles>
|
||||
<Style Selector="Slider /template/ Thumb">
|
||||
<Setter Property="ToolTip.Tip" Value="{Binding $parent[Slider].Value, Mode=OneWay, StringFormat='V\{0:f0\}'}" />
|
||||
<Setter Property="ToolTip.Placement" Value="Top" />
|
||||
<Setter Property="ToolTip.VerticalOffset" Value="-10" />
|
||||
<Setter Property="ToolTip.HorizontalOffset" Value="-30" />
|
||||
</Style>
|
||||
</Slider.Styles>
|
||||
</Slider>
|
||||
|
||||
<StackPanel
|
||||
Grid.Column="2"
|
||||
HorizontalAlignment="Right"
|
||||
Orientation="Horizontal">
|
||||
|
||||
<TextBlock Text="V" />
|
||||
<TextBlock Text="{Binding AudioSettings.LameVBRQuality}" />
|
||||
|
||||
</StackPanel>
|
||||
|
||||
<TextBlock
|
||||
Grid.Column="0"
|
||||
Grid.Row="1"
|
||||
Margin="10,0,0,0"
|
||||
Text="Higher" />
|
||||
|
||||
<TextBlock
|
||||
Grid.Column="1"
|
||||
Grid.Row="1"
|
||||
Margin="0,0,10,0"
|
||||
HorizontalAlignment="Right"
|
||||
Text="Lower" />
|
||||
|
||||
</Grid>
|
||||
</controls:GroupBox>
|
||||
|
||||
<TextBlock
|
||||
Margin="5,5,5,5"
|
||||
Text="Using L.A.M.E encoding engine"
|
||||
FontStyle="Italic" />
|
||||
|
||||
</StackPanel>
|
||||
</controls:GroupBox>
|
||||
</StackPanel>
|
||||
|
||||
<controls:GroupBox
|
||||
Grid.Row="1"
|
||||
Grid.ColumnSpan="2"
|
||||
Margin="5"
|
||||
BorderWidth="1" IsEnabled="{Binding AudioSettings.SplitFilesByChapter}"
|
||||
Label="{Binding AudioSettings.ChapterTitleTemplateText}">
|
||||
|
||||
<Grid ColumnDefinitions="*,Auto">
|
||||
|
||||
<TextBox
|
||||
Grid.Column="0"
|
||||
Margin="0,10,10,10"
|
||||
FontSize="14"
|
||||
IsReadOnly="True"
|
||||
Text="{Binding AudioSettings.ChapterTitleTemplate}" />
|
||||
|
||||
<Button
|
||||
Grid.Column="1"
|
||||
Content="Edit"
|
||||
Height="30"
|
||||
Padding="30,3,30,3"
|
||||
Click="EditChapterTitleTemplateButton_Click" />
|
||||
</Grid>
|
||||
</controls:GroupBox>
|
||||
</Grid>
|
||||
</Border>
|
||||
<settings:Audio DataContext="{CompiledBinding AudioSettings}" />
|
||||
</TabItem>
|
||||
</TabControl>
|
||||
</Grid>
|
||||
|
||||
@@ -2,6 +2,7 @@ using Avalonia.Collections;
|
||||
using Avalonia.Controls;
|
||||
using Dinah.Core;
|
||||
using FileManager;
|
||||
using LibationAvalonia.ViewModels.Settings;
|
||||
using LibationFileManager;
|
||||
using LibationUiBase;
|
||||
using ReactiveUI;
|
||||
@@ -14,7 +15,7 @@ namespace LibationAvalonia.Dialogs
|
||||
{
|
||||
public partial class SettingsDialog : DialogWindow
|
||||
{
|
||||
private SettingsPages settingsDisp;
|
||||
private SettingsVM settingsDisp;
|
||||
|
||||
private readonly Configuration config = Configuration.Instance;
|
||||
public SettingsDialog()
|
||||
@@ -28,8 +29,17 @@ namespace LibationAvalonia.Dialogs
|
||||
|
||||
protected override async Task SaveAndCloseAsync()
|
||||
{
|
||||
if (!await settingsDisp.SaveSettingsAsync(config))
|
||||
#region validation
|
||||
|
||||
if (string.IsNullOrWhiteSpace(settingsDisp.ImportantSettings.BooksDirectory))
|
||||
{
|
||||
await MessageBox.Show(this.GetParentWindow(), "Cannot set Books Location to blank", "Location is blank", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||||
return;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
settingsDisp.SaveSettings(config);
|
||||
|
||||
await MessageBox.VerboseLoggingWarning_ShowIfTrue();
|
||||
await base.SaveAndCloseAsync();
|
||||
@@ -42,489 +52,5 @@ namespace LibationAvalonia.Dialogs
|
||||
{
|
||||
Go.To.Folder(((LongPath)Configuration.Instance.LibationFiles).ShortPathName);
|
||||
}
|
||||
|
||||
public async void EditFolderTemplateButton_Click(object sender, Avalonia.Interactivity.RoutedEventArgs e)
|
||||
{
|
||||
var newTemplate = await editTemplate(TemplateEditor<Templates.FolderTemplate>.CreateFilenameEditor(config.Books, settingsDisp.DownloadDecryptSettings.FolderTemplate));
|
||||
if (newTemplate is not null)
|
||||
settingsDisp.DownloadDecryptSettings.FolderTemplate = newTemplate;
|
||||
}
|
||||
|
||||
public async void EditFileTemplateButton_Click(object sender, Avalonia.Interactivity.RoutedEventArgs e)
|
||||
{
|
||||
var newTemplate = await editTemplate(TemplateEditor<Templates.FileTemplate>.CreateFilenameEditor(config.Books, settingsDisp.DownloadDecryptSettings.FileTemplate));
|
||||
if (newTemplate is not null)
|
||||
settingsDisp.DownloadDecryptSettings.FileTemplate = newTemplate;
|
||||
}
|
||||
|
||||
public async void EditChapterFileTemplateButton_Click(object sender, Avalonia.Interactivity.RoutedEventArgs e)
|
||||
{
|
||||
|
||||
var newTemplate = await editTemplate(TemplateEditor<Templates.ChapterFileTemplate>.CreateFilenameEditor(config.Books, settingsDisp.DownloadDecryptSettings.ChapterFileTemplate));
|
||||
if (newTemplate is not null)
|
||||
settingsDisp.DownloadDecryptSettings.ChapterFileTemplate = newTemplate;
|
||||
}
|
||||
|
||||
public async void EditCharReplacementButton_Click(object sender, Avalonia.Interactivity.RoutedEventArgs e)
|
||||
{
|
||||
var form = new EditReplacementChars(config);
|
||||
await form.ShowDialog<DialogResult>(this);
|
||||
}
|
||||
|
||||
public async void EditChapterTitleTemplateButton_Click(object sender, Avalonia.Interactivity.RoutedEventArgs e)
|
||||
{
|
||||
var newTemplate = await editTemplate(TemplateEditor<Templates.ChapterTitleTemplate>.CreateNameEditor(settingsDisp.AudioSettings.ChapterTitleTemplate));
|
||||
if (newTemplate is not null)
|
||||
settingsDisp.AudioSettings.ChapterTitleTemplate = newTemplate;
|
||||
}
|
||||
|
||||
private async Task<string> editTemplate(ITemplateEditor template)
|
||||
{
|
||||
var form = new EditTemplateDialog(template);
|
||||
if (await form.ShowDialog<DialogResult>(this) == DialogResult.OK)
|
||||
return template.EditingTemplate.TemplateText;
|
||||
else return null;
|
||||
}
|
||||
}
|
||||
|
||||
internal interface ISettingsDisplay
|
||||
{
|
||||
void LoadSettings(Configuration config);
|
||||
Task<bool> SaveSettingsAsync(Configuration config);
|
||||
}
|
||||
|
||||
public class SettingsPages : ISettingsDisplay
|
||||
{
|
||||
public SettingsPages(Configuration config)
|
||||
{
|
||||
LoadSettings(config);
|
||||
}
|
||||
|
||||
public bool IsLinux => Configuration.IsLinux;
|
||||
public bool IsWindows => Configuration.IsWindows;
|
||||
public ImportantSettings ImportantSettings { get; private set; }
|
||||
public ImportSettings ImportSettings { get; private set; }
|
||||
public DownloadDecryptSettings DownloadDecryptSettings { get; private set; }
|
||||
public AudioSettings AudioSettings { get; private set; }
|
||||
|
||||
public void LoadSettings(Configuration config)
|
||||
{
|
||||
ImportantSettings = new(config);
|
||||
ImportSettings = new(config);
|
||||
DownloadDecryptSettings = new(config);
|
||||
AudioSettings = new(config);
|
||||
}
|
||||
|
||||
public async Task<bool> SaveSettingsAsync(Configuration config)
|
||||
{
|
||||
var result = await ImportantSettings.SaveSettingsAsync(config);
|
||||
result &= await ImportSettings.SaveSettingsAsync(config);
|
||||
result &= await DownloadDecryptSettings.SaveSettingsAsync(config);
|
||||
result &= await AudioSettings.SaveSettingsAsync(config);
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
public class ImportantSettings : ViewModels.ViewModelBase, ISettingsDisplay
|
||||
{
|
||||
public ImportantSettings(Configuration config)
|
||||
{
|
||||
LoadSettings(config);
|
||||
}
|
||||
|
||||
public void LoadSettings(Configuration config)
|
||||
{
|
||||
BooksDirectory = config.Books.PathWithoutPrefix;
|
||||
SavePodcastsToParentFolder = config.SavePodcastsToParentFolder;
|
||||
LoggingLevel = config.LogLevel;
|
||||
BetaOptIn = config.BetaOptIn;
|
||||
ThemeVariant = InitialThemeVariant
|
||||
= Configuration.Instance.GetString(propertyName: nameof(ThemeVariant)) is nameof(Avalonia.Styling.ThemeVariant.Dark)
|
||||
? nameof(Avalonia.Styling.ThemeVariant.Dark)
|
||||
: nameof(Avalonia.Styling.ThemeVariant.Light);
|
||||
}
|
||||
|
||||
public async Task<bool> SaveSettingsAsync(Configuration config)
|
||||
{
|
||||
#region validation
|
||||
|
||||
if (string.IsNullOrWhiteSpace(BooksDirectory))
|
||||
{
|
||||
await MessageBox.Show("Cannot set Books Location to blank", "Location is blank", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||||
return false;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
LongPath lonNewBooks = BooksDirectory;
|
||||
if (!System.IO.Directory.Exists(lonNewBooks))
|
||||
System.IO.Directory.CreateDirectory(lonNewBooks);
|
||||
config.Books = BooksDirectory;
|
||||
config.SavePodcastsToParentFolder = SavePodcastsToParentFolder;
|
||||
config.LogLevel = LoggingLevel;
|
||||
config.BetaOptIn = BetaOptIn;
|
||||
Configuration.Instance.SetString(ThemeVariant, nameof(ThemeVariant));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
public List<Configuration.KnownDirectories> KnownDirectories { get; } = new()
|
||||
{
|
||||
Configuration.KnownDirectories.UserProfile,
|
||||
Configuration.KnownDirectories.AppDir,
|
||||
Configuration.KnownDirectories.MyDocs
|
||||
};
|
||||
|
||||
public string BooksText { get; } = Configuration.GetDescription(nameof(Configuration.Books));
|
||||
public string SavePodcastsToParentFolderText { get; } = Configuration.GetDescription(nameof(Configuration.SavePodcastsToParentFolder));
|
||||
public Serilog.Events.LogEventLevel[] LoggingLevels { get; } = Enum.GetValues<Serilog.Events.LogEventLevel>();
|
||||
public string BetaOptInText { get; } = Configuration.GetDescription(nameof(Configuration.BetaOptIn));
|
||||
public string[] Themes { get; } = { nameof(Avalonia.Styling.ThemeVariant.Light), nameof(Avalonia.Styling.ThemeVariant.Dark) };
|
||||
|
||||
|
||||
public string BooksDirectory { get; set; }
|
||||
public bool SavePodcastsToParentFolder { get; set; }
|
||||
public Serilog.Events.LogEventLevel LoggingLevel { get; set; }
|
||||
public bool BetaOptIn { get; set; }
|
||||
private string themeVariant;
|
||||
public string ThemeVariant
|
||||
{
|
||||
get => themeVariant;
|
||||
set
|
||||
{
|
||||
this.RaiseAndSetIfChanged(ref themeVariant, value);
|
||||
|
||||
SelectionChanged = ThemeVariant != InitialThemeVariant;
|
||||
this.RaisePropertyChanged(nameof(SelectionChanged));
|
||||
}
|
||||
}
|
||||
public string InitialThemeVariant { get; private set; }
|
||||
public bool SelectionChanged { get; private set; }
|
||||
}
|
||||
|
||||
public class ImportSettings : ISettingsDisplay
|
||||
{
|
||||
public ImportSettings(Configuration config)
|
||||
{
|
||||
LoadSettings(config);
|
||||
}
|
||||
|
||||
public void LoadSettings(Configuration config)
|
||||
{
|
||||
AutoScan = config.AutoScan;
|
||||
ShowImportedStats = config.ShowImportedStats;
|
||||
ImportEpisodes = config.ImportEpisodes;
|
||||
DownloadEpisodes = config.DownloadEpisodes;
|
||||
AutoDownloadEpisodes = config.AutoDownloadEpisodes;
|
||||
}
|
||||
|
||||
public Task<bool> SaveSettingsAsync(Configuration config)
|
||||
{
|
||||
config.AutoScan = AutoScan;
|
||||
config.ShowImportedStats = ShowImportedStats;
|
||||
config.ImportEpisodes = ImportEpisodes;
|
||||
config.DownloadEpisodes = DownloadEpisodes;
|
||||
config.AutoDownloadEpisodes = AutoDownloadEpisodes;
|
||||
return Task.FromResult(true);
|
||||
}
|
||||
|
||||
public string AutoScanText { get; } = Configuration.GetDescription(nameof(Configuration.AutoScan));
|
||||
public string ShowImportedStatsText { get; } = Configuration.GetDescription(nameof(Configuration.ShowImportedStats));
|
||||
public string ImportEpisodesText { get; } = Configuration.GetDescription(nameof(Configuration.ImportEpisodes));
|
||||
public string DownloadEpisodesText { get; } = Configuration.GetDescription(nameof(Configuration.DownloadEpisodes));
|
||||
public string AutoDownloadEpisodesText { get; } = Configuration.GetDescription(nameof(Configuration.AutoDownloadEpisodes));
|
||||
|
||||
public bool AutoScan { get; set; }
|
||||
public bool ShowImportedStats { get; set; }
|
||||
public bool ImportEpisodes { get; set; }
|
||||
public bool DownloadEpisodes { get; set; }
|
||||
public bool AutoDownloadEpisodes { get; set; }
|
||||
}
|
||||
|
||||
public class DownloadDecryptSettings : ViewModels.ViewModelBase, ISettingsDisplay
|
||||
{
|
||||
private bool _badBookAsk;
|
||||
private bool _badBookAbort;
|
||||
private bool _badBookRetry;
|
||||
private bool _badBookIgnore;
|
||||
|
||||
private string _folderTemplate;
|
||||
private string _fileTemplate;
|
||||
private string _chapterFileTemplate;
|
||||
|
||||
public DownloadDecryptSettings(Configuration config)
|
||||
{
|
||||
LoadSettings(config);
|
||||
}
|
||||
|
||||
public List<Configuration.KnownDirectories> KnownDirectories { get; } = new()
|
||||
{
|
||||
Configuration.KnownDirectories.WinTemp,
|
||||
Configuration.KnownDirectories.UserProfile,
|
||||
Configuration.KnownDirectories.AppDir,
|
||||
Configuration.KnownDirectories.MyDocs,
|
||||
Configuration.KnownDirectories.LibationFiles
|
||||
};
|
||||
|
||||
public string InProgressDirectory { get; set; }
|
||||
public void LoadSettings(Configuration config)
|
||||
{
|
||||
BadBookAsk = config.BadBook is Configuration.BadBookAction.Ask;
|
||||
BadBookAbort = config.BadBook is Configuration.BadBookAction.Abort;
|
||||
BadBookRetry = config.BadBook is Configuration.BadBookAction.Retry;
|
||||
BadBookIgnore = config.BadBook is Configuration.BadBookAction.Ignore;
|
||||
FolderTemplate = config.FolderTemplate;
|
||||
FileTemplate = config.FileTemplate;
|
||||
ChapterFileTemplate = config.ChapterFileTemplate;
|
||||
InProgressDirectory = config.InProgress;
|
||||
UseCoverAsFolderIcon = config.UseCoverAsFolderIcon;
|
||||
}
|
||||
|
||||
public Task<bool> SaveSettingsAsync(Configuration config)
|
||||
{
|
||||
config.BadBook
|
||||
= BadBookAbort ? Configuration.BadBookAction.Abort
|
||||
: BadBookRetry ? Configuration.BadBookAction.Retry
|
||||
: BadBookIgnore ? Configuration.BadBookAction.Ignore
|
||||
: Configuration.BadBookAction.Ask;
|
||||
|
||||
config.FolderTemplate = FolderTemplate;
|
||||
config.FileTemplate = FileTemplate;
|
||||
config.ChapterFileTemplate = ChapterFileTemplate;
|
||||
config.InProgress = InProgressDirectory;
|
||||
|
||||
config.UseCoverAsFolderIcon = UseCoverAsFolderIcon;
|
||||
|
||||
return Task.FromResult(true);
|
||||
}
|
||||
|
||||
public string UseCoverAsFolderIconText { get; } = Configuration.GetDescription(nameof(Configuration.UseCoverAsFolderIcon));
|
||||
public string BadBookGroupboxText { get; } = Configuration.GetDescription(nameof(Configuration.BadBook));
|
||||
public string BadBookAskText { get; } = Configuration.BadBookAction.Ask.GetDescription();
|
||||
public string BadBookAbortText { get; } = Configuration.BadBookAction.Abort.GetDescription();
|
||||
public string BadBookRetryText { get; } = Configuration.BadBookAction.Retry.GetDescription();
|
||||
public string BadBookIgnoreText { get; } = Configuration.BadBookAction.Ignore.GetDescription();
|
||||
public string FolderTemplateText { get; } = Configuration.GetDescription(nameof(Configuration.FolderTemplate));
|
||||
public string FileTemplateText { get; } = Configuration.GetDescription(nameof(Configuration.FileTemplate));
|
||||
public string ChapterFileTemplateText { get; } = Configuration.GetDescription(nameof(Configuration.ChapterFileTemplate));
|
||||
public string EditCharReplacementText { get; } = Configuration.GetDescription(nameof(Configuration.ReplacementCharacters));
|
||||
public string InProgressDescriptionText { get; } = Configuration.GetDescription(nameof(Configuration.InProgress));
|
||||
|
||||
public string FolderTemplate { get => _folderTemplate; set { this.RaiseAndSetIfChanged(ref _folderTemplate, value); } }
|
||||
public string FileTemplate { get => _fileTemplate; set { this.RaiseAndSetIfChanged(ref _fileTemplate, value); } }
|
||||
public string ChapterFileTemplate { get => _chapterFileTemplate; set { this.RaiseAndSetIfChanged(ref _chapterFileTemplate, value); } }
|
||||
public bool UseCoverAsFolderIcon { get; set; }
|
||||
|
||||
public bool BadBookAsk
|
||||
{
|
||||
get => _badBookAsk;
|
||||
set
|
||||
{
|
||||
this.RaiseAndSetIfChanged(ref _badBookAsk, value);
|
||||
if (value)
|
||||
{
|
||||
BadBookAbort = false;
|
||||
BadBookRetry = false;
|
||||
BadBookIgnore = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
public bool BadBookAbort
|
||||
{
|
||||
get => _badBookAbort;
|
||||
set
|
||||
{
|
||||
this.RaiseAndSetIfChanged(ref _badBookAbort, value);
|
||||
if (value)
|
||||
{
|
||||
BadBookAsk = false;
|
||||
BadBookRetry = false;
|
||||
BadBookIgnore = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
public bool BadBookRetry
|
||||
{
|
||||
get => _badBookRetry;
|
||||
set
|
||||
{
|
||||
this.RaiseAndSetIfChanged(ref _badBookRetry, value);
|
||||
if (value)
|
||||
{
|
||||
BadBookAsk = false;
|
||||
BadBookAbort = false;
|
||||
BadBookIgnore = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
public bool BadBookIgnore
|
||||
{
|
||||
get => _badBookIgnore;
|
||||
set
|
||||
{
|
||||
this.RaiseAndSetIfChanged(ref _badBookIgnore, value);
|
||||
if (value)
|
||||
{
|
||||
BadBookAsk = false;
|
||||
BadBookAbort = false;
|
||||
BadBookRetry = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class AudioSettings : ViewModels.ViewModelBase, ISettingsDisplay
|
||||
{
|
||||
|
||||
private bool _downloadClipsBookmarks;
|
||||
private bool _decryptToLossy;
|
||||
private bool _splitFilesByChapter;
|
||||
private bool _allowLibationFixup;
|
||||
private bool _lameTargetBitrate;
|
||||
private bool _lameMatchSource;
|
||||
private int _lameBitrate;
|
||||
private int _lameVBRQuality;
|
||||
private string _chapterTitleTemplate;
|
||||
public SampleRateSelection SelectedSampleRate { get; set; }
|
||||
public NAudio.Lame.EncoderQuality SelectedEncoderQuality { get; set; }
|
||||
|
||||
public AvaloniaList<SampleRateSelection> SampleRates { get; }
|
||||
= new(
|
||||
new[]
|
||||
{
|
||||
AAXClean.SampleRate.Hz_44100,
|
||||
AAXClean.SampleRate.Hz_32000,
|
||||
AAXClean.SampleRate.Hz_24000,
|
||||
AAXClean.SampleRate.Hz_22050,
|
||||
AAXClean.SampleRate.Hz_16000,
|
||||
AAXClean.SampleRate.Hz_12000,
|
||||
}
|
||||
.Select(s => new SampleRateSelection(s)));
|
||||
|
||||
public AvaloniaList<NAudio.Lame.EncoderQuality> EncoderQualities { get; }
|
||||
= new(
|
||||
new[]
|
||||
{
|
||||
NAudio.Lame.EncoderQuality.High,
|
||||
NAudio.Lame.EncoderQuality.Standard,
|
||||
NAudio.Lame.EncoderQuality.Fast,
|
||||
});
|
||||
|
||||
|
||||
public AudioSettings(Configuration config)
|
||||
{
|
||||
LoadSettings(config);
|
||||
}
|
||||
public void LoadSettings(Configuration config)
|
||||
{
|
||||
CreateCueSheet = config.CreateCueSheet;
|
||||
AllowLibationFixup = config.AllowLibationFixup;
|
||||
DownloadCoverArt = config.DownloadCoverArt;
|
||||
RetainAaxFile = config.RetainAaxFile;
|
||||
DownloadClipsBookmarks = config.DownloadClipsBookmarks;
|
||||
ClipBookmarkFormat = config.ClipsBookmarksFileFormat;
|
||||
SplitFilesByChapter = config.SplitFilesByChapter;
|
||||
MergeOpeningAndEndCredits = config.MergeOpeningAndEndCredits;
|
||||
StripAudibleBrandAudio = config.StripAudibleBrandAudio;
|
||||
StripUnabridged = config.StripUnabridged;
|
||||
ChapterTitleTemplate = config.ChapterTitleTemplate;
|
||||
DecryptToLossy = config.DecryptToLossy;
|
||||
MoveMoovToBeginning = config.MoveMoovToBeginning;
|
||||
LameTargetBitrate = config.LameTargetBitrate;
|
||||
LameDownsampleMono = config.LameDownsampleMono;
|
||||
LameConstantBitrate = config.LameConstantBitrate;
|
||||
LameMatchSource = config.LameMatchSourceBR;
|
||||
LameBitrate = config.LameBitrate;
|
||||
LameVBRQuality = config.LameVBRQuality;
|
||||
|
||||
SelectedSampleRate = SampleRates.FirstOrDefault(s => s.SampleRate == config.MaxSampleRate);
|
||||
SelectedEncoderQuality = config.LameEncoderQuality;
|
||||
}
|
||||
|
||||
public Task<bool> SaveSettingsAsync(Configuration config)
|
||||
{
|
||||
config.CreateCueSheet = CreateCueSheet;
|
||||
config.AllowLibationFixup = AllowLibationFixup;
|
||||
config.DownloadCoverArt = DownloadCoverArt;
|
||||
config.RetainAaxFile = RetainAaxFile;
|
||||
config.DownloadClipsBookmarks = DownloadClipsBookmarks;
|
||||
config.ClipsBookmarksFileFormat = ClipBookmarkFormat;
|
||||
config.SplitFilesByChapter = SplitFilesByChapter;
|
||||
config.MergeOpeningAndEndCredits = MergeOpeningAndEndCredits;
|
||||
config.StripAudibleBrandAudio = StripAudibleBrandAudio;
|
||||
config.StripUnabridged = StripUnabridged;
|
||||
config.ChapterTitleTemplate = ChapterTitleTemplate;
|
||||
config.DecryptToLossy = DecryptToLossy;
|
||||
config.MoveMoovToBeginning = MoveMoovToBeginning;
|
||||
config.LameTargetBitrate = LameTargetBitrate;
|
||||
config.LameDownsampleMono = LameDownsampleMono;
|
||||
config.LameConstantBitrate = LameConstantBitrate;
|
||||
config.LameMatchSourceBR = LameMatchSource;
|
||||
config.LameBitrate = LameBitrate;
|
||||
config.LameVBRQuality = LameVBRQuality;
|
||||
|
||||
config.LameEncoderQuality = SelectedEncoderQuality;
|
||||
config.MaxSampleRate = SelectedSampleRate?.SampleRate ?? config.MaxSampleRate;
|
||||
|
||||
return Task.FromResult(true);
|
||||
}
|
||||
|
||||
public AvaloniaList<Configuration.ClipBookmarkFormat> ClipBookmarkFormats { get; } = new(Enum<Configuration.ClipBookmarkFormat>.GetValues());
|
||||
public string CreateCueSheetText { get; } = Configuration.GetDescription(nameof(Configuration.CreateCueSheet));
|
||||
public string AllowLibationFixupText { get; } = Configuration.GetDescription(nameof(Configuration.AllowLibationFixup));
|
||||
public string DownloadCoverArtText { get; } = Configuration.GetDescription(nameof(Configuration.DownloadCoverArt));
|
||||
public string RetainAaxFileText { get; } = Configuration.GetDescription(nameof(Configuration.RetainAaxFile));
|
||||
public string SplitFilesByChapterText { get; } = Configuration.GetDescription(nameof(Configuration.SplitFilesByChapter));
|
||||
public string MergeOpeningEndCreditsText { get; } = Configuration.GetDescription(nameof(Configuration.MergeOpeningAndEndCredits));
|
||||
public string StripAudibleBrandingText { get; } = Configuration.GetDescription(nameof(Configuration.StripAudibleBrandAudio));
|
||||
public string StripUnabridgedText { get; } = Configuration.GetDescription(nameof(Configuration.StripUnabridged));
|
||||
public string ChapterTitleTemplateText { get; } = Configuration.GetDescription(nameof(Configuration.ChapterTitleTemplate));
|
||||
public string MoveMoovToBeginningText { get; } = Configuration.GetDescription(nameof(Configuration.MoveMoovToBeginning));
|
||||
|
||||
public bool CreateCueSheet { get; set; }
|
||||
public bool DownloadCoverArt { get; set; }
|
||||
public bool RetainAaxFile { get; set; }
|
||||
public bool DownloadClipsBookmarks { get => _downloadClipsBookmarks; set => this.RaiseAndSetIfChanged(ref _downloadClipsBookmarks, value); }
|
||||
public Configuration.ClipBookmarkFormat ClipBookmarkFormat { get; set; }
|
||||
public bool MergeOpeningAndEndCredits { get; set; }
|
||||
public bool StripAudibleBrandAudio { get; set; }
|
||||
public bool StripUnabridged { get; set; }
|
||||
public bool DecryptToLossy { get => _decryptToLossy; set => this.RaiseAndSetIfChanged(ref _decryptToLossy, value); }
|
||||
public bool MoveMoovToBeginning { get; set; }
|
||||
|
||||
public bool LameDownsampleMono { get; set; } = Design.IsDesignMode;
|
||||
public bool LameConstantBitrate { get; set; } = Design.IsDesignMode;
|
||||
|
||||
public bool SplitFilesByChapter { get => _splitFilesByChapter; set { this.RaiseAndSetIfChanged(ref _splitFilesByChapter, value); } }
|
||||
public bool LameTargetBitrate { get => _lameTargetBitrate; set { this.RaiseAndSetIfChanged(ref _lameTargetBitrate, value); } }
|
||||
public bool LameMatchSource { get => _lameMatchSource; set { this.RaiseAndSetIfChanged(ref _lameMatchSource, value); } }
|
||||
public int LameBitrate { get => _lameBitrate; set { this.RaiseAndSetIfChanged(ref _lameBitrate, value); } }
|
||||
public int LameVBRQuality { get => _lameVBRQuality; set { this.RaiseAndSetIfChanged(ref _lameVBRQuality, value); } }
|
||||
|
||||
|
||||
public string ChapterTitleTemplate { get => _chapterTitleTemplate; set { this.RaiseAndSetIfChanged(ref _chapterTitleTemplate, value); } }
|
||||
|
||||
|
||||
public bool AllowLibationFixup
|
||||
{
|
||||
get => _allowLibationFixup;
|
||||
set
|
||||
{
|
||||
this.RaiseAndSetIfChanged(ref _allowLibationFixup, value);
|
||||
if (!_allowLibationFixup)
|
||||
{
|
||||
SplitFilesByChapter = false;
|
||||
StripAudibleBrandAudio = false;
|
||||
StripUnabridged = false;
|
||||
DecryptToLossy = false;
|
||||
this.RaisePropertyChanged(nameof(SplitFilesByChapter));
|
||||
this.RaisePropertyChanged(nameof(StripAudibleBrandAudio));
|
||||
this.RaisePropertyChanged(nameof(StripUnabridged));
|
||||
this.RaisePropertyChanged(nameof(DecryptToLossy));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,33 +2,32 @@
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
mc:Ignorable="d" d:DesignWidth="630" d:DesignHeight="110"
|
||||
mc:Ignorable="d" d:DesignWidth="630" d:DesignHeight="90"
|
||||
x:Class="LibationAvalonia.Dialogs.TagsBatchDialog"
|
||||
MinWidth="630" MinHeight="110"
|
||||
MaxWidth="630" MaxHeight="110"
|
||||
MinWidth="630" MinHeight="90"
|
||||
MaxWidth="630" MaxHeight="90"
|
||||
Width="630" Height="110"
|
||||
Title="Replace Tags"
|
||||
WindowStartupLocation="CenterOwner"
|
||||
Icon="/Assets/libation.ico">
|
||||
|
||||
<Grid RowDefinitions="Auto,Auto,Auto">
|
||||
<Grid RowDefinitions="Auto,Auto" ColumnDefinitions="*,Auto" Margin="10">
|
||||
<TextBlock
|
||||
Grid.Row="0"
|
||||
Margin="10,10,10,0"
|
||||
Grid.ColumnSpan="2"
|
||||
Margin="0,0,0,10"
|
||||
Text="Tags are separated by a space. Each tag can contain letters, numbers, and underscores"/>
|
||||
<TextBox
|
||||
Grid.Row="1"
|
||||
Margin="10"
|
||||
MinHeight="25"
|
||||
Name="EditTagsTb"
|
||||
Text="{Binding NewTags, Mode=TwoWay}" />
|
||||
<Button
|
||||
Grid.Row="2"
|
||||
Padding="30,0,30,0"
|
||||
Margin="10,0,10,10"
|
||||
HorizontalAlignment="Right"
|
||||
Height="25"
|
||||
Grid.Row="1"
|
||||
Grid.Column="1"
|
||||
Margin="10,0,0,0"
|
||||
Padding="20,3"
|
||||
VerticalAlignment="Stretch"
|
||||
Content="Save"
|
||||
Click="SaveButton_Clicked"/>
|
||||
Command="{Binding SaveAndClose}"/>
|
||||
</Grid>
|
||||
</Window>
|
||||
|
||||
@@ -12,8 +12,5 @@ namespace LibationAvalonia.Dialogs
|
||||
|
||||
DataContext = this;
|
||||
}
|
||||
|
||||
public void SaveButton_Clicked(object sender, Avalonia.Interactivity.RoutedEventArgs e)
|
||||
=> SaveAndClose();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -52,12 +52,12 @@
|
||||
VerticalAlignment="Stretch"
|
||||
VerticalContentAlignment="Center"
|
||||
Content="Restore"
|
||||
Click="Restore_Click" />
|
||||
Command="{Binding RestoreCheckedAsync}"/>
|
||||
|
||||
<Button
|
||||
IsEnabled="{Binding ControlsEnabled}"
|
||||
Grid.Column="3"
|
||||
Click="EmptyTrash_Click" >
|
||||
Command="{Binding PermanentlyDeleteCheckedAsync}" >
|
||||
<TextBlock
|
||||
TextAlignment="Center"
|
||||
Text="Permanently Delete
from Libation" />
|
||||
|
||||
@@ -16,21 +16,20 @@ namespace LibationAvalonia.Dialogs
|
||||
{
|
||||
public partial class TrashBinDialog : Window
|
||||
{
|
||||
TrashBinViewModel _viewModel;
|
||||
|
||||
public TrashBinDialog()
|
||||
{
|
||||
InitializeComponent();
|
||||
this.RestoreSizeAndLocation(Configuration.Instance);
|
||||
DataContext = _viewModel = new();
|
||||
DataContext = new TrashBinViewModel();
|
||||
|
||||
this.Closing += (_, _) => this.SaveSizeAndLocation(Configuration.Instance);
|
||||
}
|
||||
|
||||
public async void EmptyTrash_Click(object sender, Avalonia.Interactivity.RoutedEventArgs e)
|
||||
=> await _viewModel.PermanentlyDeleteCheckedAsync();
|
||||
public async void Restore_Click(object sender, Avalonia.Interactivity.RoutedEventArgs e)
|
||||
=> await _viewModel.RestoreCheckedAsync();
|
||||
KeyBindings.Add(new Avalonia.Input.KeyBinding
|
||||
{
|
||||
Gesture = new Avalonia.Input.KeyGesture(Avalonia.Input.Key.Escape),
|
||||
Command = ReactiveCommand.Create(Close)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public class TrashBinViewModel : ViewModelBase, IDisposable
|
||||
|
||||
@@ -21,7 +21,6 @@
|
||||
|
||||
<controls:GroupBox
|
||||
Grid.Row="1"
|
||||
BorderWidth="2"
|
||||
Label="Release Information"
|
||||
Margin="0,10,0,10">
|
||||
|
||||
|
||||
@@ -51,8 +51,8 @@
|
||||
<Compile Update="Views\LiberateStatusButton.axaml.cs">
|
||||
<DependentUpon>LiberateStatusButton.axaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Update="Views\MainWindow.*.cs">
|
||||
<DependentUpon>MainWindow.axaml</DependentUpon>
|
||||
<Compile Update="ViewModels\MainVM.*.cs">
|
||||
<DependentUpon>MainVM.cs</DependentUpon>
|
||||
</Compile>
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
44
Source/LibationAvalonia/MacAccessKeyHandler.cs
Normal file
44
Source/LibationAvalonia/MacAccessKeyHandler.cs
Normal file
@@ -0,0 +1,44 @@
|
||||
using Avalonia;
|
||||
using Avalonia.Input;
|
||||
|
||||
namespace LibationAvalonia
|
||||
{
|
||||
internal class MacAccessKeyHandler : AccessKeyHandler
|
||||
{
|
||||
protected override void OnPreviewKeyDown(object sender, KeyEventArgs e)
|
||||
{
|
||||
if (e.Key is Key.LWin or Key.RWin)
|
||||
{
|
||||
var newArgs = new KeyEventArgs { Key = Key.LeftAlt, Handled = e.Handled };
|
||||
base.OnPreviewKeyDown(sender, newArgs);
|
||||
e.Handled = newArgs.Handled;
|
||||
}
|
||||
else if (e.Key is not Key.LeftAlt and not Key.RightAlt)
|
||||
base.OnPreviewKeyDown(sender, e);
|
||||
}
|
||||
|
||||
protected override void OnPreviewKeyUp(object sender, KeyEventArgs e)
|
||||
{
|
||||
if (e.Key is Key.LWin or Key.RWin)
|
||||
{
|
||||
var newArgs = new KeyEventArgs { Key = Key.LeftAlt, Handled = e.Handled };
|
||||
base.OnPreviewKeyUp(sender, newArgs);
|
||||
e.Handled = newArgs.Handled;
|
||||
}
|
||||
else if (e.Key is not Key.LeftAlt and not Key.RightAlt)
|
||||
base.OnPreviewKeyDown(sender, e);
|
||||
}
|
||||
|
||||
protected override void OnKeyDown(object sender, KeyEventArgs e)
|
||||
{
|
||||
if (e.KeyModifiers.HasAllFlags(KeyModifiers.Meta))
|
||||
{
|
||||
var newArgs = new KeyEventArgs { Key = e.Key, Handled = e.Handled, KeyModifiers = KeyModifiers.Alt };
|
||||
base.OnKeyDown(sender, newArgs);
|
||||
e.Handled = newArgs.Handled;
|
||||
}
|
||||
else if (!e.KeyModifiers.HasFlag(KeyModifiers.Alt))
|
||||
base.OnPreviewKeyDown(sender, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
58
Source/LibationAvalonia/ViewModels/MainVM.BackupCounts.cs
Normal file
58
Source/LibationAvalonia/ViewModels/MainVM.BackupCounts.cs
Normal file
@@ -0,0 +1,58 @@
|
||||
using ApplicationServices;
|
||||
using Avalonia.Threading;
|
||||
using ReactiveUI;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace LibationAvalonia.ViewModels
|
||||
{
|
||||
partial class MainVM
|
||||
{
|
||||
private Task<LibraryCommands.LibraryStats> updateCountsTask;
|
||||
private LibraryCommands.LibraryStats _libraryStats;
|
||||
|
||||
/// <summary> The "Begin Book and PDF Backup" menu item header text </summary>
|
||||
public string BookBackupsToolStripText { get; private set; } = "Begin Book and PDF Backups: 0";
|
||||
/// <summary> The "Begin PDF Only Backup" menu item header text </summary>
|
||||
public string PdfBackupsToolStripText { get; private set; } = "Begin PDF Only Backups: 0";
|
||||
|
||||
/// <summary> The user's library statistics </summary>
|
||||
public LibraryCommands.LibraryStats LibraryStats
|
||||
{
|
||||
get => _libraryStats;
|
||||
set
|
||||
{
|
||||
this.RaiseAndSetIfChanged(ref _libraryStats, value);
|
||||
|
||||
BookBackupsToolStripText
|
||||
= LibraryStats.HasPendingBooks
|
||||
? "Begin " + menufyText($"Book and PDF Backups: {LibraryStats.PendingBooks} remaining")
|
||||
: "All books have been liberated";
|
||||
|
||||
PdfBackupsToolStripText
|
||||
= LibraryStats.pdfsNotDownloaded > 0
|
||||
? "Begin " + menufyText($"PDF Only Backups: {LibraryStats.pdfsNotDownloaded} remaining")
|
||||
: "All PDFs have been downloaded";
|
||||
|
||||
this.RaisePropertyChanged(nameof(BookBackupsToolStripText));
|
||||
this.RaisePropertyChanged(nameof(PdfBackupsToolStripText));
|
||||
}
|
||||
}
|
||||
|
||||
private void Configure_BackupCounts()
|
||||
{
|
||||
MainWindow.Loaded += setBackupCounts;
|
||||
LibraryCommands.LibrarySizeChanged += setBackupCounts;
|
||||
LibraryCommands.BookUserDefinedItemCommitted += setBackupCounts;
|
||||
}
|
||||
|
||||
private async void setBackupCounts(object _, object __)
|
||||
{
|
||||
if (updateCountsTask?.IsCompleted ?? true)
|
||||
{
|
||||
updateCountsTask = Task.Run(() => LibraryCommands.GetCounts());
|
||||
var stats = await updateCountsTask;
|
||||
await Dispatcher.UIThread.InvokeAsync(() => LibraryStats = stats);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,21 +3,22 @@ using Avalonia.Platform.Storage;
|
||||
using FileManager;
|
||||
using LibationFileManager;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace LibationAvalonia.Views
|
||||
namespace LibationAvalonia.ViewModels
|
||||
{
|
||||
public partial class MainWindow
|
||||
partial class MainVM
|
||||
{
|
||||
private void Configure_Export() { }
|
||||
|
||||
public async void exportLibraryToolStripMenuItem_Click(object sender, Avalonia.Interactivity.RoutedEventArgs e)
|
||||
public async Task ExportLibraryAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
var options = new FilePickerSaveOptions
|
||||
{
|
||||
Title = "Where to export Library",
|
||||
SuggestedStartLocation = await StorageProvider.TryGetFolderFromPathAsync(Configuration.Instance.Books.PathWithoutPrefix),
|
||||
SuggestedStartLocation = await MainWindow.StorageProvider.TryGetFolderFromPathAsync(Configuration.Instance.Books.PathWithoutPrefix),
|
||||
SuggestedFileName = $"Libation Library Export {DateTime.Now:yyyy-MM-dd}",
|
||||
DefaultExtension = "xlsx",
|
||||
ShowOverwritePrompt = true,
|
||||
@@ -43,7 +44,7 @@ namespace LibationAvalonia.Views
|
||||
}
|
||||
};
|
||||
|
||||
var selectedFile = (await StorageProvider.SaveFilePickerAsync(options))?.TryGetLocalPath();
|
||||
var selectedFile = (await MainWindow.StorageProvider.SaveFilePickerAsync(options))?.TryGetLocalPath();
|
||||
|
||||
if (selectedFile is null) return;
|
||||
|
||||
@@ -66,7 +67,7 @@ namespace LibationAvalonia.Views
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
await MessageBox.ShowAdminAlert(this, "Error attempting to export your library.", "Error exporting", ex);
|
||||
await MessageBox.ShowAdminAlert(MainWindow, "Error attempting to export your library.", "Error exporting", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
123
Source/LibationAvalonia/ViewModels/MainVM.Filters.cs
Normal file
123
Source/LibationAvalonia/ViewModels/MainVM.Filters.cs
Normal file
@@ -0,0 +1,123 @@
|
||||
using Avalonia;
|
||||
using Avalonia.Collections;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Data;
|
||||
using Avalonia.Input;
|
||||
using LibationFileManager;
|
||||
using ReactiveUI;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace LibationAvalonia.ViewModels
|
||||
{
|
||||
partial class MainVM
|
||||
{
|
||||
private string lastGoodFilter = "";
|
||||
private string _filterString;
|
||||
private bool _firstFilterIsDefault = true;
|
||||
|
||||
/// <summary> Library filterting query </summary>
|
||||
public string FilterString { get => _filterString; set => this.RaiseAndSetIfChanged(ref _filterString, value); }
|
||||
public AvaloniaList<Control> QuickFilterMenuItems { get; } = new();
|
||||
/// <summary> Indicates if the first quick filter is the default filter </summary>
|
||||
public bool FirstFilterIsDefault { get => _firstFilterIsDefault; set => QuickFilters.UseDefault = this.RaiseAndSetIfChanged(ref _firstFilterIsDefault, value); }
|
||||
|
||||
|
||||
private void Configure_Filters()
|
||||
{
|
||||
FirstFilterIsDefault = QuickFilters.UseDefault;
|
||||
MainWindow.Initialized += updateFiltersMenu;
|
||||
QuickFilters.Updated += updateFiltersMenu;
|
||||
|
||||
//We need to be able to dynamically add and remove menu items from the Quick Filters menu.
|
||||
//To do that, we need quick filter's menu items source to be writable, which we can only
|
||||
//achieve by creating the list ourselves (instead of allowing Avalonia to create it from the xaml)
|
||||
|
||||
QuickFilterMenuItems.Add(new MenuItem
|
||||
{
|
||||
|
||||
Header = "Start Libation with 1st filter _Default",
|
||||
Command = ReactiveCommand.Create(ToggleFirstFilterIsDefault),
|
||||
Icon = new CheckBox
|
||||
{
|
||||
BorderThickness = new Thickness(0),
|
||||
IsHitTestVisible = false,
|
||||
[!CheckBox.IsCheckedProperty] = new Binding(nameof(FirstFilterIsDefault))
|
||||
}
|
||||
});
|
||||
QuickFilterMenuItems.Add(new MenuItem { Header = "_Edit quick filters...", Command = ReactiveCommand.Create(EditQuickFiltersAsync) });
|
||||
QuickFilterMenuItems.Add(new Separator());
|
||||
}
|
||||
|
||||
public void AddQuickFilterBtn() => QuickFilters.Add(FilterString);
|
||||
public async Task FilterBtn() => await PerformFilter(FilterString);
|
||||
public async Task FilterHelpBtn() => await new LibationAvalonia.Dialogs.SearchSyntaxDialog().ShowDialog(MainWindow);
|
||||
public void ToggleFirstFilterIsDefault() => FirstFilterIsDefault = !FirstFilterIsDefault;
|
||||
public async Task EditQuickFiltersAsync() => await new LibationAvalonia.Dialogs.EditQuickFilters().ShowDialog(MainWindow);
|
||||
public async Task PerformFilter(string filterString)
|
||||
{
|
||||
FilterString = filterString;
|
||||
|
||||
try
|
||||
{
|
||||
await ProductsDisplay.Filter(filterString);
|
||||
lastGoodFilter = filterString;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
await MessageBox.Show($"Bad filter string:\r\n\r\n{ex.Message}", "Bad filter string", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||||
|
||||
// re-apply last good filter
|
||||
await PerformFilter(lastGoodFilter);
|
||||
}
|
||||
}
|
||||
|
||||
private void updateFiltersMenu(object _ = null, object __ = null)
|
||||
{
|
||||
//Clear all filters
|
||||
var quickFilterNativeMenu = (NativeMenuItem)NativeMenu.GetMenu(MainWindow).Items[3];
|
||||
for (int i = quickFilterNativeMenu.Menu.Items.Count - 1; i >= 3; i--)
|
||||
{
|
||||
var command = ((NativeMenuItem)quickFilterNativeMenu.Menu.Items[i]).Command as IDisposable;
|
||||
if (command != null)
|
||||
{
|
||||
var existingBinding = MainWindow.KeyBindings.FirstOrDefault(kb => kb.Command == command);
|
||||
if (existingBinding != null)
|
||||
MainWindow.KeyBindings.Remove(existingBinding);
|
||||
|
||||
command.Dispose();
|
||||
}
|
||||
|
||||
quickFilterNativeMenu.Menu.Items.RemoveAt(i);
|
||||
QuickFilterMenuItems.RemoveAt(i);
|
||||
}
|
||||
|
||||
// re-populate
|
||||
var index = 0;
|
||||
foreach (var filter in QuickFilters.Filters)
|
||||
{
|
||||
var command = ReactiveCommand.Create(async () => await PerformFilter(filter));
|
||||
|
||||
var menuItem = new MenuItem { Header = $"{++index}: {filter}", Command = command };
|
||||
var nativeMenuItem = new NativeMenuItem { Header = $"{index}: {filter}", Command = command };
|
||||
|
||||
if (Configuration.IsMacOs && index <= 10)
|
||||
{
|
||||
//Register hotkeys Command + 1 - 0 for quick filters
|
||||
var key = index == 10 ? Key.D0 : Key.D0 + index;
|
||||
nativeMenuItem.Gesture = new KeyGesture(key, KeyModifiers.Meta);
|
||||
}
|
||||
else if (!Configuration.IsMacOs && index <= 12)
|
||||
{
|
||||
//Register hotkeys F1 - F12 for quick filters
|
||||
menuItem.InputGesture = new KeyGesture(Key.F1 + index - 1);
|
||||
MainWindow.KeyBindings.Add(new KeyBinding { Command = command, Gesture = menuItem.InputGesture });
|
||||
}
|
||||
|
||||
QuickFilterMenuItems.Add(menuItem);
|
||||
quickFilterNativeMenu.Menu.Items.Add(nativeMenuItem);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
254
Source/LibationAvalonia/ViewModels/MainVM.Import.cs
Normal file
254
Source/LibationAvalonia/ViewModels/MainVM.Import.cs
Normal file
@@ -0,0 +1,254 @@
|
||||
using ApplicationServices;
|
||||
using AudibleUtilities;
|
||||
using Avalonia.Controls;
|
||||
using LibationFileManager;
|
||||
using ReactiveUI;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Avalonia.Input;
|
||||
|
||||
namespace LibationAvalonia.ViewModels
|
||||
{
|
||||
public partial class MainVM
|
||||
{
|
||||
private bool _autoScanChecked = Configuration.Instance.AutoScan;
|
||||
private string _removeBooksButtonText = "Remove # Books from Libation";
|
||||
private bool _removeBooksButtonEnabled = Design.IsDesignMode;
|
||||
private bool _removeButtonsVisible = Design.IsDesignMode;
|
||||
private int _numAccountsScanning = 2;
|
||||
private int _accountsCount = 0;
|
||||
|
||||
/// <summary> Auto scanning accounts is enables </summary>
|
||||
public bool AutoScanChecked { get => _autoScanChecked; set => Configuration.Instance.AutoScan = this.RaiseAndSetIfChanged(ref _autoScanChecked, value); }
|
||||
/// <summary> Display text for the "Remove # Books from Libation" button </summary>
|
||||
public string RemoveBooksButtonText { get => _removeBooksButtonText; set => this.RaiseAndSetIfChanged(ref _removeBooksButtonText, value); }
|
||||
/// <summary> Indicates if the "Remove # Books from Libation" button is enabled </summary>
|
||||
public bool RemoveBooksButtonEnabled { get => _removeBooksButtonEnabled; set { this.RaiseAndSetIfChanged(ref _removeBooksButtonEnabled, value); } }
|
||||
/// <summary> Indicates if the "Remove # Books from Libation" and "Done Removing" buttons should be visible </summary>
|
||||
public bool RemoveButtonsVisible
|
||||
{
|
||||
get => _removeButtonsVisible;
|
||||
set
|
||||
{
|
||||
this.RaiseAndSetIfChanged(ref _removeButtonsVisible, value);
|
||||
this.RaisePropertyChanged(nameof(RemoveMenuItemsEnabled));
|
||||
}
|
||||
}
|
||||
/// <summary> Indicates if Libation is currently scanning account(s) </summary>
|
||||
public bool ActivelyScanning => _numAccountsScanning > 0;
|
||||
/// <summary> Indicates if the "Remove Books" menu items are enabled</summary>
|
||||
public bool RemoveMenuItemsEnabled => !RemoveButtonsVisible && !ActivelyScanning;
|
||||
/// <summary> The library scanning status text </summary>
|
||||
public string ScanningText => _numAccountsScanning == 1 ? "Scanning..." : $"Scanning {_numAccountsScanning} accounts...";
|
||||
/// <summary> There is at least one Audible account </summary>
|
||||
public bool AnyAccounts => AccountsCount > 0;
|
||||
/// <summary> There is exactly one Audible account </summary>
|
||||
public bool OneAccount => AccountsCount == 1;
|
||||
/// <summary> There are more than 1 Audible accounts </summary>
|
||||
public bool MultipleAccounts => AccountsCount > 1;
|
||||
/// <summary> The number of accounts added to Libation </summary>
|
||||
public int AccountsCount
|
||||
{
|
||||
get => _accountsCount;
|
||||
set
|
||||
{
|
||||
this.RaiseAndSetIfChanged(ref _accountsCount, value);
|
||||
this.RaisePropertyChanged(nameof(AnyAccounts));
|
||||
this.RaisePropertyChanged(nameof(OneAccount));
|
||||
this.RaisePropertyChanged(nameof(MultipleAccounts));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void Configure_Import()
|
||||
{
|
||||
MainWindow.Loaded += (_, _) =>
|
||||
{
|
||||
refreshImportMenu();
|
||||
AccountsSettingsPersister.Saved += refreshImportMenu;
|
||||
};
|
||||
|
||||
AutoScanChecked = Configuration.Instance.AutoScan;
|
||||
|
||||
setyNumScanningAccounts(0);
|
||||
LibraryCommands.ScanBegin += (_, accountsLength) => setyNumScanningAccounts(accountsLength);
|
||||
LibraryCommands.ScanEnd += (_, newCount) => setyNumScanningAccounts(0);
|
||||
|
||||
if (!Design.IsDesignMode)
|
||||
RemoveButtonsVisible = false;
|
||||
}
|
||||
|
||||
public void ToggleAutoScan() => AutoScanChecked = !AutoScanChecked;
|
||||
|
||||
public async Task AddAccountsAsync()
|
||||
{
|
||||
await MessageBox.Show("To load your Audible library, come back here to the Import menu after adding your account");
|
||||
await new LibationAvalonia.Dialogs.AccountsDialog().ShowDialog(MainWindow);
|
||||
}
|
||||
|
||||
public async Task ScanAccountAsync()
|
||||
{
|
||||
using var persister = AudibleApiStorage.GetAccountsSettingsPersister();
|
||||
await scanLibrariesAsync(persister.AccountsSettings.GetAll().FirstOrDefault());
|
||||
}
|
||||
|
||||
public async Task ScanAllAccountsAsync()
|
||||
{
|
||||
using var persister = AudibleApiStorage.GetAccountsSettingsPersister();
|
||||
await scanLibrariesAsync(persister.AccountsSettings.GetAll().ToArray());
|
||||
}
|
||||
|
||||
public async Task ScanSomeAccountsAsync()
|
||||
{
|
||||
var scanAccountsDialog = new LibationAvalonia.Dialogs.ScanAccountsDialog();
|
||||
|
||||
if (await scanAccountsDialog.ShowDialog<DialogResult>(MainWindow) != DialogResult.OK)
|
||||
return;
|
||||
|
||||
if (!scanAccountsDialog.CheckedAccounts.Any())
|
||||
return;
|
||||
|
||||
await scanLibrariesAsync(scanAccountsDialog.CheckedAccounts.ToArray());
|
||||
}
|
||||
|
||||
public async Task RemoveBooksAsync()
|
||||
{
|
||||
// if 0 accounts, this will not be visible
|
||||
// if 1 account, run scanLibrariesRemovedBooks() on this account
|
||||
// if multiple accounts, another menu set will open. do not run scanLibrariesRemovedBooks()
|
||||
using var persister = AudibleApiStorage.GetAccountsSettingsPersister();
|
||||
var accounts = persister.AccountsSettings.GetAll();
|
||||
|
||||
if (accounts.Count != 1)
|
||||
return;
|
||||
|
||||
var firstAccount = accounts.Single();
|
||||
await scanLibrariesRemovedBooks(firstAccount);
|
||||
}
|
||||
|
||||
// selectively remove books from all accounts
|
||||
public async Task RemoveBooksAllAsync()
|
||||
{
|
||||
using var persister = AudibleApiStorage.GetAccountsSettingsPersister();
|
||||
var allAccounts = persister.AccountsSettings.GetAll();
|
||||
await scanLibrariesRemovedBooks(allAccounts.ToArray());
|
||||
}
|
||||
|
||||
public async Task RemoveBooksBtn()
|
||||
{
|
||||
RemoveBooksButtonEnabled = false;
|
||||
await ProductsDisplay.RemoveCheckedBooksAsync();
|
||||
RemoveBooksButtonEnabled = true;
|
||||
}
|
||||
|
||||
public async Task DoneRemovingBtn()
|
||||
{
|
||||
RemoveButtonsVisible = false;
|
||||
|
||||
ProductsDisplay.DoneRemovingBooks();
|
||||
|
||||
//Restore the filter
|
||||
await PerformFilter(lastGoodFilter);
|
||||
}
|
||||
|
||||
// selectively remove books from some accounts
|
||||
public async Task RemoveBooksSomeAsync()
|
||||
{
|
||||
var scanAccountsDialog = new LibationAvalonia.Dialogs.ScanAccountsDialog();
|
||||
|
||||
if (await scanAccountsDialog.ShowDialog<DialogResult>(MainWindow) != DialogResult.OK)
|
||||
return;
|
||||
|
||||
if (!scanAccountsDialog.CheckedAccounts.Any())
|
||||
return;
|
||||
|
||||
await scanLibrariesRemovedBooks(scanAccountsDialog.CheckedAccounts.ToArray());
|
||||
}
|
||||
|
||||
public async Task LocateAudiobooksAsync()
|
||||
{
|
||||
var locateDialog = new LibationAvalonia.Dialogs.LocateAudiobooksDialog();
|
||||
await locateDialog.ShowDialog(MainWindow);
|
||||
}
|
||||
|
||||
private void setyNumScanningAccounts(int numScanning)
|
||||
{
|
||||
_numAccountsScanning = numScanning;
|
||||
this.RaisePropertyChanged(nameof(ActivelyScanning));
|
||||
this.RaisePropertyChanged(nameof(RemoveMenuItemsEnabled));
|
||||
this.RaisePropertyChanged(nameof(ScanningText));
|
||||
}
|
||||
|
||||
private async Task scanLibrariesRemovedBooks(params Account[] accounts)
|
||||
{
|
||||
//This action is meant to operate on the entire library.
|
||||
//For removing books within a filter set, use
|
||||
//Visible Books > Remove from library
|
||||
|
||||
await ProductsDisplay.Filter(null);
|
||||
|
||||
RemoveBooksButtonEnabled = true;
|
||||
RemoveButtonsVisible = true;
|
||||
|
||||
await ProductsDisplay.ScanAndRemoveBooksAsync(accounts);
|
||||
}
|
||||
|
||||
private async Task scanLibrariesAsync(params Account[] accounts)
|
||||
{
|
||||
try
|
||||
{
|
||||
var (totalProcessed, newAdded) = await LibraryCommands.ImportAccountAsync(LibationAvalonia.Dialogs.Login.AvaloniaLoginChoiceEager.ApiExtendedFunc, accounts);
|
||||
|
||||
// this is here instead of ScanEnd so that the following is only possible when it's user-initiated, not automatic loop
|
||||
if (Configuration.Instance.ShowImportedStats && newAdded > 0)
|
||||
await MessageBox.Show($"Total processed: {totalProcessed}\r\nNew: {newAdded}");
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
Serilog.Log.Information("Audible login attempt cancelled by user");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
await MessageBox.ShowAdminAlert(
|
||||
MainWindow,
|
||||
"Error importing library. Please try again. If this still happens after 2 or 3 tries, stop and contact administrator",
|
||||
"Error importing library",
|
||||
ex);
|
||||
}
|
||||
}
|
||||
|
||||
private void refreshImportMenu(object _ = null, EventArgs __ = null)
|
||||
{
|
||||
using var persister = AudibleApiStorage.GetAccountsSettingsPersister();
|
||||
AccountsCount = persister.AccountsSettings.Accounts.Count;
|
||||
|
||||
var importMenuItem = (NativeMenuItem)NativeMenu.GetMenu(MainWindow).Items[0];
|
||||
|
||||
for (int i = importMenuItem.Menu.Items.Count - 1; i >= 2; i--)
|
||||
importMenuItem.Menu.Items.RemoveAt(i);
|
||||
|
||||
if (AccountsCount < 1)
|
||||
{
|
||||
importMenuItem.Menu.Items.Add(new NativeMenuItem { Header = "No accounts yet. Add Account...", Command = ReactiveCommand.Create(AddAccountsAsync) });
|
||||
}
|
||||
else if (AccountsCount == 1)
|
||||
{
|
||||
importMenuItem.Menu.Items.Add(new NativeMenuItem { Header = "Scan Library", Command = ReactiveCommand.Create(ScanAccountAsync), Gesture = new KeyGesture(Key.S, KeyModifiers.Alt | KeyModifiers.Meta)});
|
||||
importMenuItem.Menu.Items.Add(new NativeMenuItemSeparator());
|
||||
importMenuItem.Menu.Items.Add(new NativeMenuItem { Header = "Remove Library Books", Command = ReactiveCommand.Create(RemoveBooksAsync), Gesture = new KeyGesture(Key.R, KeyModifiers.Alt | KeyModifiers.Meta)});
|
||||
}
|
||||
else
|
||||
{
|
||||
importMenuItem.Menu.Items.Add(new NativeMenuItem { Header = "Scan Library of All Accounts", Command = ReactiveCommand.Create(ScanAllAccountsAsync), Gesture = new KeyGesture(Key.S, KeyModifiers.Alt | KeyModifiers.Meta)});
|
||||
importMenuItem.Menu.Items.Add(new NativeMenuItem { Header = "Scan Library of Some Accounts", Command = ReactiveCommand.Create(ScanSomeAccountsAsync), Gesture = new KeyGesture(Key.S, KeyModifiers.Alt | KeyModifiers.Meta | KeyModifiers.Shift) });
|
||||
importMenuItem.Menu.Items.Add(new NativeMenuItemSeparator());
|
||||
importMenuItem.Menu.Items.Add(new NativeMenuItem { Header = "Remove Books from All Accounts", Command = ReactiveCommand.Create(RemoveBooksAllAsync), Gesture = new KeyGesture(Key.R, KeyModifiers.Alt | KeyModifiers.Meta)});
|
||||
importMenuItem.Menu.Items.Add(new NativeMenuItem { Header = "Remove Books from Some Accounts", Command = ReactiveCommand.Create(RemoveBooksSomeAsync), Gesture = new KeyGesture(Key.R, KeyModifiers.Alt | KeyModifiers.Meta | KeyModifiers.Shift) });
|
||||
}
|
||||
|
||||
importMenuItem.Menu.Items.Add(new NativeMenuItemSeparator());
|
||||
importMenuItem.Menu.Items.Add(new NativeMenuItem { Header = "Locate Audiobooks...", Command = ReactiveCommand.Create(LocateAudiobooksAsync) });
|
||||
}
|
||||
}
|
||||
}
|
||||
64
Source/LibationAvalonia/ViewModels/MainVM.Liberate.cs
Normal file
64
Source/LibationAvalonia/ViewModels/MainVM.Liberate.cs
Normal file
@@ -0,0 +1,64 @@
|
||||
using ApplicationServices;
|
||||
using LibationFileManager;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using DataLayer;
|
||||
|
||||
namespace LibationAvalonia.ViewModels
|
||||
{
|
||||
partial class MainVM
|
||||
{
|
||||
public void Configure_Liberate() { }
|
||||
|
||||
public void BackupAllBooks()
|
||||
{
|
||||
try
|
||||
{
|
||||
setQueueCollapseState(false);
|
||||
|
||||
Serilog.Log.Logger.Information("Begin backing up all library books");
|
||||
|
||||
ProcessQueue.AddDownloadDecrypt(
|
||||
DbContexts
|
||||
.GetLibrary_Flat_NoTracking()
|
||||
.UnLiberated()
|
||||
);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Serilog.Log.Logger.Error(ex, "An error occurred while backing up all library books");
|
||||
}
|
||||
}
|
||||
|
||||
public void BackupAllPdfs()
|
||||
{
|
||||
setQueueCollapseState(false);
|
||||
ProcessQueue.AddDownloadPdf(DbContexts.GetLibrary_Flat_NoTracking().Where(lb => lb.Book.UserDefinedItem.PdfStatus is LiberatedStatus.NotLiberated));
|
||||
}
|
||||
|
||||
public async Task ConvertAllToMp3Async()
|
||||
{
|
||||
var result = await MessageBox.Show(MainWindow,
|
||||
"This converts all m4b titles in your library to mp3 files. Original files are not deleted."
|
||||
+ "\r\nFor large libraries this will take a long time and will take up more disk space."
|
||||
+ "\r\n\r\nContinue?"
|
||||
+ "\r\n\r\n(To always download titles as mp3 instead of m4b, go to Settings: Download my books as .MP3 files)",
|
||||
"Convert all M4b => Mp3?",
|
||||
MessageBoxButtons.YesNo,
|
||||
MessageBoxIcon.Warning);
|
||||
if (result == DialogResult.Yes)
|
||||
{
|
||||
setQueueCollapseState(false);
|
||||
ProcessQueue.AddConvertMp3(DbContexts.GetLibrary_Flat_NoTracking().Where(lb => lb.Book.UserDefinedItem.BookStatus is LiberatedStatus.Liberated && lb.Book.ContentType is ContentType.Product));
|
||||
}
|
||||
//Only Queue Liberated books for conversion. This isn't a perfect filter, but it's better than nothing.
|
||||
}
|
||||
|
||||
private void setQueueCollapseState(bool collapsed)
|
||||
{
|
||||
QueueOpen = !collapsed;
|
||||
Configuration.Instance.SetNonString(!collapsed, nameof(QueueOpen));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,35 +1,52 @@
|
||||
using DataLayer;
|
||||
using Dinah.Core;
|
||||
using LibationFileManager;
|
||||
using LibationUiBase.GridView;
|
||||
using LibationFileManager;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using DataLayer;
|
||||
using Dinah.Core;
|
||||
using LibationUiBase.GridView;
|
||||
using ReactiveUI;
|
||||
|
||||
namespace LibationAvalonia.Views
|
||||
namespace LibationAvalonia.ViewModels
|
||||
{
|
||||
public partial class MainWindow
|
||||
partial class MainVM
|
||||
{
|
||||
private void Configure_ProcessQueue()
|
||||
private bool _queueOpen = false;
|
||||
|
||||
/// <summary> The Process Queue panel is open </summary>
|
||||
public bool QueueOpen
|
||||
{
|
||||
var collapseState = !Configuration.Instance.GetNonString(defaultValue: true, nameof(_viewModel.QueueOpen));
|
||||
SetQueueCollapseState(collapseState);
|
||||
get => _queueOpen;
|
||||
set
|
||||
{
|
||||
this.RaiseAndSetIfChanged(ref _queueOpen, value);
|
||||
QueueButtonAngle = value ? 180 : 0;
|
||||
this.RaisePropertyChanged(nameof(QueueButtonAngle));
|
||||
}
|
||||
}
|
||||
|
||||
public async void ProductsDisplay_LiberateClicked(object sender, LibraryBook libraryBook)
|
||||
public double QueueButtonAngle { get; private set; }
|
||||
|
||||
private void Configure_ProcessQueue()
|
||||
{
|
||||
var collapseState = !Configuration.Instance.GetNonString(defaultValue: true, nameof(QueueOpen));
|
||||
setQueueCollapseState(collapseState);
|
||||
}
|
||||
|
||||
public async void LiberateClicked(LibraryBook libraryBook)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (libraryBook.Book.UserDefinedItem.BookStatus is LiberatedStatus.NotLiberated or LiberatedStatus.PartialDownload)
|
||||
{
|
||||
Serilog.Log.Logger.Information("Begin single book backup of {libraryBook}", libraryBook);
|
||||
SetQueueCollapseState(false);
|
||||
_viewModel.ProcessQueue.AddDownloadDecrypt(libraryBook);
|
||||
setQueueCollapseState(false);
|
||||
ProcessQueue.AddDownloadDecrypt(libraryBook);
|
||||
}
|
||||
else if (libraryBook.Book.UserDefinedItem.PdfStatus is LiberatedStatus.NotLiberated)
|
||||
{
|
||||
Serilog.Log.Logger.Information("Begin single pdf backup of {libraryBook}", libraryBook);
|
||||
SetQueueCollapseState(false);
|
||||
_viewModel.ProcessQueue.AddDownloadPdf(libraryBook);
|
||||
setQueueCollapseState(false);
|
||||
ProcessQueue.AddDownloadPdf(libraryBook);
|
||||
}
|
||||
else if (libraryBook.Book.Audio_Exists())
|
||||
{
|
||||
@@ -49,15 +66,15 @@ namespace LibationAvalonia.Views
|
||||
}
|
||||
}
|
||||
|
||||
public void ProductsDisplay_LiberateSeriesClicked(object sender, ISeriesEntry series)
|
||||
public void LiberateSeriesClicked(ISeriesEntry series)
|
||||
{
|
||||
try
|
||||
{
|
||||
SetQueueCollapseState(false);
|
||||
setQueueCollapseState(false);
|
||||
|
||||
Serilog.Log.Logger.Information("Begin backing up all {series} episodes", series.LibraryBook);
|
||||
|
||||
_viewModel.ProcessQueue.AddDownloadDecrypt(series.Children.Select(c => c.LibraryBook).UnLiberated());
|
||||
ProcessQueue.AddDownloadDecrypt(series.Children.Select(c => c.LibraryBook).UnLiberated());
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -65,15 +82,15 @@ namespace LibationAvalonia.Views
|
||||
}
|
||||
}
|
||||
|
||||
public void ProductsDisplay_ConvertToMp3Clicked(object sender, LibraryBook libraryBook)
|
||||
public void ConvertToMp3Clicked(LibraryBook libraryBook)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (libraryBook.Book.UserDefinedItem.BookStatus is LiberatedStatus.Liberated)
|
||||
{
|
||||
Serilog.Log.Logger.Information("Begin single pdf backup of {libraryBook}", libraryBook);
|
||||
SetQueueCollapseState(false);
|
||||
_viewModel.ProcessQueue.AddConvertMp3(libraryBook);
|
||||
Serilog.Log.Logger.Information("Begin convert to mp3 {libraryBook}", libraryBook);
|
||||
setQueueCollapseState(false);
|
||||
ProcessQueue.AddConvertMp3(libraryBook);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
@@ -81,15 +98,7 @@ namespace LibationAvalonia.Views
|
||||
Serilog.Log.Logger.Error(ex, "An error occurred while handling the stop light button click for {libraryBook}", libraryBook);
|
||||
}
|
||||
}
|
||||
private void SetQueueCollapseState(bool collapsed)
|
||||
{
|
||||
_viewModel.QueueOpen = !collapsed;
|
||||
Configuration.Instance.SetNonString(!collapsed, nameof(_viewModel.QueueOpen));
|
||||
}
|
||||
|
||||
public void ToggleQueueHideBtn_Click(object sender, Avalonia.Interactivity.RoutedEventArgs e)
|
||||
{
|
||||
SetQueueCollapseState(_viewModel.QueueOpen);
|
||||
}
|
||||
public void ToggleQueueHideBtn() => setQueueCollapseState(QueueOpen);
|
||||
}
|
||||
}
|
||||
@@ -1,23 +1,19 @@
|
||||
using ApplicationServices;
|
||||
using AudibleUtilities;
|
||||
using Avalonia.Controls;
|
||||
using Dinah.Core;
|
||||
using LibationFileManager;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace LibationAvalonia.Views
|
||||
namespace LibationAvalonia.ViewModels
|
||||
{
|
||||
public partial class MainWindow
|
||||
partial class MainVM
|
||||
{
|
||||
private InterruptableTimer autoScanTimer;
|
||||
private readonly InterruptableTimer autoScanTimer = new InterruptableTimer(TimeSpan.FromMinutes(5));
|
||||
|
||||
private void Configure_ScanAuto()
|
||||
{
|
||||
// creating InterruptableTimer inside 'Configure_' is a break from the pattern. As long as no one else needs to access or subscribe to it, this is ok
|
||||
autoScanTimer = new InterruptableTimer(TimeSpan.FromMinutes(5));
|
||||
|
||||
// subscribe as async/non-blocking. I'd actually rather prefer blocking but real-world testing found that caused a deadlock in the AudibleAPI
|
||||
autoScanTimer.Elapsed += async (_, __) =>
|
||||
{
|
||||
@@ -30,7 +26,7 @@ namespace LibationAvalonia.Views
|
||||
// in autoScan, new books SHALL NOT show dialog
|
||||
try
|
||||
{
|
||||
await LibraryCommands.ImportAccountAsync(Dialogs.Login.AvaloniaLoginChoiceEager.ApiExtendedFunc, accounts);
|
||||
await LibraryCommands.ImportAccountAsync(LibationAvalonia.Dialogs.Login.AvaloniaLoginChoiceEager.ApiExtendedFunc, accounts);
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
@@ -42,10 +38,8 @@ namespace LibationAvalonia.Views
|
||||
}
|
||||
};
|
||||
|
||||
_viewModel.AutoScanChecked = Configuration.Instance.AutoScan;
|
||||
|
||||
// if enabled: begin on load
|
||||
Opened += startAutoScan;
|
||||
MainWindow.Loaded += startAutoScan;
|
||||
|
||||
// if new 'default' account is added, run autoscan
|
||||
AccountsSettingsPersister.Saving += accountsPreSave;
|
||||
@@ -55,6 +49,7 @@ namespace LibationAvalonia.Views
|
||||
Configuration.Instance.PropertyChanged += startAutoScan;
|
||||
}
|
||||
|
||||
|
||||
private List<(string AccountId, string LocaleName)> preSaveDefaultAccounts;
|
||||
private List<(string AccountId, string LocaleName)> getDefaultAccounts()
|
||||
{
|
||||
@@ -65,32 +60,24 @@ namespace LibationAvalonia.Views
|
||||
.Select(a => (a.AccountId, a.Locale.Name))
|
||||
.ToList();
|
||||
}
|
||||
|
||||
private void accountsPreSave(object sender = null, EventArgs e = null)
|
||||
=> preSaveDefaultAccounts = getDefaultAccounts();
|
||||
|
||||
private void accountsPostSave(object sender = null, EventArgs e = null)
|
||||
{
|
||||
var postSaveDefaultAccounts = getDefaultAccounts();
|
||||
var newDefaultAccounts = postSaveDefaultAccounts.Except(preSaveDefaultAccounts).ToList();
|
||||
|
||||
if (newDefaultAccounts.Any())
|
||||
if (getDefaultAccounts().Except(preSaveDefaultAccounts).Any())
|
||||
startAutoScan();
|
||||
}
|
||||
|
||||
[PropertyChangeFilter(nameof(Configuration.AutoScan))]
|
||||
private void startAutoScan(object sender = null, EventArgs e = null)
|
||||
{
|
||||
_viewModel.AutoScanChecked = Configuration.Instance.AutoScan;
|
||||
if (_viewModel.AutoScanChecked)
|
||||
AutoScanChecked = Configuration.Instance.AutoScan;
|
||||
if (AutoScanChecked)
|
||||
autoScanTimer.PerformNow();
|
||||
else
|
||||
autoScanTimer.Stop();
|
||||
}
|
||||
public void autoScanLibraryToolStripMenuItem_Click(object sender, Avalonia.Interactivity.RoutedEventArgs e)
|
||||
{
|
||||
if (sender is MenuItem mi && mi.Icon is CheckBox checkBox)
|
||||
{
|
||||
checkBox.IsChecked = !(checkBox.IsChecked ?? false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
42
Source/LibationAvalonia/ViewModels/MainVM.Settings.cs
Normal file
42
Source/LibationAvalonia/ViewModels/MainVM.Settings.cs
Normal file
@@ -0,0 +1,42 @@
|
||||
using Avalonia.Controls;
|
||||
using LibationFileManager;
|
||||
using ReactiveUI;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace LibationAvalonia.ViewModels
|
||||
{
|
||||
partial class MainVM
|
||||
{
|
||||
private bool _menuBarVisible = !Configuration.IsMacOs;
|
||||
public bool MenuBarVisible { get => _menuBarVisible; set => this.RaiseAndSetIfChanged(ref _menuBarVisible, value); }
|
||||
private void Configure_Settings()
|
||||
{
|
||||
((NativeMenuItem)NativeMenu.GetMenu(App.Current).Items[0]).Command = ReactiveCommand.Create(ShowAboutAsync);
|
||||
}
|
||||
|
||||
public Task ShowAboutAsync() => new LibationAvalonia.Dialogs.AboutDialog().ShowDialog(MainWindow);
|
||||
public Task ShowAccountsAsync() => new LibationAvalonia.Dialogs.AccountsDialog().ShowDialog(MainWindow);
|
||||
public Task ShowSettingsAsync() => new LibationAvalonia.Dialogs.SettingsDialog().ShowDialog(MainWindow);
|
||||
public Task ShowTrashBinAsync() => new LibationAvalonia.Dialogs.TrashBinDialog().ShowDialog(MainWindow);
|
||||
|
||||
public void LaunchHangover()
|
||||
{
|
||||
try
|
||||
{
|
||||
System.Diagnostics.Process.Start("Hangover" + (Configuration.IsWindows ? ".exe" : ""));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Serilog.Log.Logger.Error(ex, "Failed to launch Hangover");
|
||||
}
|
||||
}
|
||||
|
||||
public async Task StartWalkthroughAsync()
|
||||
{
|
||||
MenuBarVisible = true;
|
||||
await new Walkthrough(MainWindow).RunAsync();
|
||||
MenuBarVisible = !Configuration.IsMacOs;
|
||||
}
|
||||
}
|
||||
}
|
||||
203
Source/LibationAvalonia/ViewModels/MainVM.VisibleBooks.cs
Normal file
203
Source/LibationAvalonia/ViewModels/MainVM.VisibleBooks.cs
Normal file
@@ -0,0 +1,203 @@
|
||||
using ApplicationServices;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using DataLayer;
|
||||
using Avalonia.Threading;
|
||||
using LibationAvalonia.Dialogs;
|
||||
using ReactiveUI;
|
||||
|
||||
namespace LibationAvalonia.ViewModels
|
||||
{
|
||||
partial class MainVM
|
||||
{
|
||||
private int _visibleNotLiberated = 1;
|
||||
private int _visibleCount = 1;
|
||||
|
||||
/// <summary> The Bottom-right visible book count status text </summary>
|
||||
public string VisibleCountText => $"Visible: {_visibleCount}";
|
||||
/// <summary> The Visible Books menu item header text </summary>
|
||||
public string VisibleCountMenuItemText => menufyText($"Visible Books {_visibleCount}");
|
||||
/// <summary> Indicates if any of the books visible in the Products Display haven't been liberated </summary>
|
||||
public bool AnyVisibleNotLiberated => _visibleNotLiberated > 0;
|
||||
/// <summary> The "Liberate Visible Books" menu item header text (submenu item of the "Liberate Menu" menu item) </summary>
|
||||
public string LiberateVisibleToolStripText { get; private set; } = "Liberate _Visible Books: 0";
|
||||
/// <summary> The "Liberate" menu item header text (submenu item of the "Visible Books" menu item) </summary>
|
||||
public string LiberateVisibleToolStripText_2 { get; private set; } = menufyText("Liberate: 0");
|
||||
|
||||
private void Configure_VisibleBooks()
|
||||
{
|
||||
LibraryCommands.BookUserDefinedItemCommitted += setLiberatedVisibleMenuItemAsync;
|
||||
ProductsDisplay.VisibleCountChanged += ProductsDisplay_VisibleCountChanged;
|
||||
}
|
||||
|
||||
private void setVisibleCount(int visibleCount)
|
||||
{
|
||||
_visibleCount = visibleCount;
|
||||
this.RaisePropertyChanged(nameof(VisibleCountText));
|
||||
this.RaisePropertyChanged(nameof(VisibleCountMenuItemText));
|
||||
}
|
||||
|
||||
private void setVisibleNotLiberatedCount(int visibleNotLiberated)
|
||||
{
|
||||
_visibleNotLiberated = visibleNotLiberated;
|
||||
|
||||
LiberateVisibleToolStripText
|
||||
= AnyVisibleNotLiberated
|
||||
? "Liberate " + menufyText($"Visible Books: {visibleNotLiberated}")
|
||||
: "All visible books are liberated";
|
||||
|
||||
LiberateVisibleToolStripText_2
|
||||
= AnyVisibleNotLiberated
|
||||
? menufyText($"Liberate: {visibleNotLiberated}")
|
||||
: "All visible books are liberated";
|
||||
|
||||
this.RaisePropertyChanged(nameof(AnyVisibleNotLiberated));
|
||||
this.RaisePropertyChanged(nameof(LiberateVisibleToolStripText));
|
||||
this.RaisePropertyChanged(nameof(LiberateVisibleToolStripText_2));
|
||||
}
|
||||
|
||||
public async void ProductsDisplay_VisibleCountChanged(object sender, int qty)
|
||||
{
|
||||
setVisibleCount(qty);
|
||||
await Dispatcher.UIThread.InvokeAsync(setLiberatedVisibleMenuItem);
|
||||
}
|
||||
|
||||
private async void setLiberatedVisibleMenuItemAsync(object _, object __)
|
||||
=> await Dispatcher.UIThread.InvokeAsync(setLiberatedVisibleMenuItem);
|
||||
|
||||
|
||||
public void LiberateVisible()
|
||||
{
|
||||
try
|
||||
{
|
||||
setQueueCollapseState(false);
|
||||
|
||||
Serilog.Log.Logger.Information("Begin backing up visible library books");
|
||||
|
||||
ProcessQueue.AddDownloadDecrypt(
|
||||
ProductsDisplay
|
||||
.GetVisibleBookEntries()
|
||||
.UnLiberated()
|
||||
);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Serilog.Log.Logger.Error(ex, "An error occurred while backing up visible library books");
|
||||
}
|
||||
}
|
||||
|
||||
public async Task ReplaceTagsAsync()
|
||||
{
|
||||
var dialog = new TagsBatchDialog();
|
||||
var result = await dialog.ShowDialog<DialogResult>(MainWindow);
|
||||
if (result != DialogResult.OK)
|
||||
return;
|
||||
|
||||
var visibleLibraryBooks = ProductsDisplay.GetVisibleBookEntries();
|
||||
|
||||
var confirmationResult = await MessageBox.ShowConfirmationDialog(
|
||||
MainWindow,
|
||||
visibleLibraryBooks,
|
||||
// do not use `$` string interpolation. See impl.
|
||||
"Are you sure you want to replace tags in {0}?",
|
||||
"Replace tags?");
|
||||
|
||||
if (confirmationResult != DialogResult.Yes)
|
||||
return;
|
||||
|
||||
visibleLibraryBooks.UpdateTags(dialog.NewTags);
|
||||
}
|
||||
|
||||
public async Task SetBookDownloadedAsync()
|
||||
{
|
||||
var dialog = new LiberatedStatusBatchManualDialog();
|
||||
var result = await dialog.ShowDialog<DialogResult>(MainWindow);
|
||||
if (result != DialogResult.OK)
|
||||
return;
|
||||
|
||||
var visibleLibraryBooks = ProductsDisplay.GetVisibleBookEntries();
|
||||
|
||||
var confirmationResult = await MessageBox.ShowConfirmationDialog(
|
||||
MainWindow,
|
||||
visibleLibraryBooks,
|
||||
// do not use `$` string interpolation. See impl.
|
||||
"Are you sure you want to replace book downloaded status in {0}?",
|
||||
"Replace downloaded status?");
|
||||
|
||||
if (confirmationResult != DialogResult.Yes)
|
||||
return;
|
||||
|
||||
visibleLibraryBooks.UpdateBookStatus(dialog.BookLiberatedStatus);
|
||||
}
|
||||
|
||||
public async Task SetPdfDownloadedAsync()
|
||||
{
|
||||
var dialog = new LiberatedStatusBatchManualDialog(isPdf: true);
|
||||
var result = await dialog.ShowDialog<DialogResult>(MainWindow);
|
||||
if (result != DialogResult.OK)
|
||||
return;
|
||||
|
||||
var visibleLibraryBooks = ProductsDisplay.GetVisibleBookEntries();
|
||||
|
||||
var confirmationResult = await MessageBox.ShowConfirmationDialog(
|
||||
MainWindow,
|
||||
visibleLibraryBooks,
|
||||
// do not use `$` string interpolation. See impl.
|
||||
"Are you sure you want to replace PDF downloaded status in {0}?",
|
||||
"Replace downloaded status?");
|
||||
|
||||
if (confirmationResult != DialogResult.Yes)
|
||||
return;
|
||||
|
||||
visibleLibraryBooks.UpdatePdfStatus(dialog.BookLiberatedStatus);
|
||||
}
|
||||
|
||||
public async Task SetDownloadedAutoAsync()
|
||||
{
|
||||
var dialog = new LiberatedStatusBatchAutoDialog();
|
||||
var result = await dialog.ShowDialog<DialogResult>(MainWindow);
|
||||
if (result != DialogResult.OK)
|
||||
return;
|
||||
|
||||
var bulkSetStatus = new BulkSetDownloadStatus(ProductsDisplay.GetVisibleBookEntries(), dialog.SetDownloaded, dialog.SetNotDownloaded);
|
||||
var count = await Task.Run(bulkSetStatus.Discover);
|
||||
|
||||
if (count == 0)
|
||||
return;
|
||||
|
||||
var confirmationResult = await MessageBox.Show(
|
||||
bulkSetStatus.AggregateMessage,
|
||||
"Replace downloaded status?",
|
||||
MessageBoxButtons.YesNo,
|
||||
MessageBoxIcon.Question,
|
||||
MessageBoxDefaultButton.Button1);
|
||||
|
||||
if (confirmationResult != DialogResult.Yes)
|
||||
return;
|
||||
|
||||
bulkSetStatus.Execute();
|
||||
}
|
||||
|
||||
public async Task RemoveVisibleAsync()
|
||||
{
|
||||
var visibleLibraryBooks = ProductsDisplay.GetVisibleBookEntries();
|
||||
|
||||
var confirmationResult = await MessageBox.ShowConfirmationDialog(
|
||||
MainWindow,
|
||||
visibleLibraryBooks,
|
||||
// do not use `$` string interpolation. See impl.
|
||||
"Are you sure you want to remove {0} from Libation's library?",
|
||||
"Remove books from Libation?",
|
||||
MessageBoxDefaultButton.Button2);
|
||||
|
||||
if (confirmationResult is DialogResult.Yes)
|
||||
await visibleLibraryBooks.RemoveBooksAsync();
|
||||
}
|
||||
|
||||
private void setLiberatedVisibleMenuItem()
|
||||
{
|
||||
var libraryStats = LibraryCommands.GetCounts(ProductsDisplay.GetVisibleBookEntries());
|
||||
setVisibleNotLiberatedCount(libraryStats.PendingBooks);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,9 +2,9 @@
|
||||
using LibationUiBase;
|
||||
using System.IO;
|
||||
|
||||
namespace LibationAvalonia.Views
|
||||
namespace LibationAvalonia.ViewModels
|
||||
{
|
||||
public partial class MainWindow
|
||||
partial class MainVM
|
||||
{
|
||||
private void Configure_NonUI()
|
||||
{
|
||||
39
Source/LibationAvalonia/ViewModels/MainVM.cs
Normal file
39
Source/LibationAvalonia/ViewModels/MainVM.cs
Normal file
@@ -0,0 +1,39 @@
|
||||
using ApplicationServices;
|
||||
using LibationAvalonia.Views;
|
||||
using LibationFileManager;
|
||||
using ReactiveUI;
|
||||
|
||||
namespace LibationAvalonia.ViewModels
|
||||
{
|
||||
public partial class MainVM : ViewModelBase
|
||||
{
|
||||
public ProcessQueueViewModel ProcessQueue { get; } = new ProcessQueueViewModel();
|
||||
public ProductsDisplayViewModel ProductsDisplay { get; } = new ProductsDisplayViewModel();
|
||||
|
||||
private double? _downloadProgress = null;
|
||||
public double? DownloadProgress { get => _downloadProgress; set => this.RaiseAndSetIfChanged(ref _downloadProgress, value); }
|
||||
|
||||
|
||||
private readonly MainWindow MainWindow;
|
||||
public MainVM(MainWindow mainWindow)
|
||||
{
|
||||
MainWindow = mainWindow;
|
||||
|
||||
ProductsDisplay.RemovableCountChanged += (_, removeCount) => RemoveBooksButtonText = removeCount == 1 ? "Remove 1 Book from Libation" : $"Remove {removeCount} Books from Libation";
|
||||
LibraryCommands.LibrarySizeChanged += async (_, _) => await ProductsDisplay.UpdateGridAsync(DbContexts.GetLibrary_Flat_NoTracking(includeParents: true));
|
||||
|
||||
Configure_NonUI();
|
||||
Configure_BackupCounts();
|
||||
Configure_Export();
|
||||
Configure_Filters();
|
||||
Configure_Import();
|
||||
Configure_Liberate();
|
||||
Configure_ProcessQueue();
|
||||
Configure_ScanAuto();
|
||||
Configure_Settings();
|
||||
Configure_VisibleBooks();
|
||||
}
|
||||
|
||||
private static string menufyText(string header) => Configuration.IsMacOs ? header : $"_{header}";
|
||||
}
|
||||
}
|
||||
@@ -1,226 +0,0 @@
|
||||
using ApplicationServices;
|
||||
using Avalonia.Collections;
|
||||
using Avalonia.Controls;
|
||||
using LibationFileManager;
|
||||
using ReactiveUI;
|
||||
|
||||
namespace LibationAvalonia.ViewModels
|
||||
{
|
||||
public class MainWindowViewModel : ViewModelBase
|
||||
{
|
||||
private string _filterString;
|
||||
private string _removeBooksButtonText = "Remove # Books from Libation";
|
||||
private bool _removeBooksButtonEnabled = true;
|
||||
private bool _autoScanChecked = true;
|
||||
private bool _firstFilterIsDefault = true;
|
||||
private bool _removeButtonsVisible = true;
|
||||
private int _numAccountsScanning = 2;
|
||||
private int _accountsCount = 0;
|
||||
private bool _queueOpen = true;
|
||||
private int _visibleCount = 1;
|
||||
private LibraryCommands.LibraryStats _libraryStats;
|
||||
private int _visibleNotLiberated = 1;
|
||||
public bool IsMp3Supported => Configuration.IsLinux || Configuration.IsWindows;
|
||||
|
||||
/// <summary> The Process Queue's viewmodel </summary>
|
||||
public ProcessQueueViewModel ProcessQueue { get; } = new ProcessQueueViewModel();
|
||||
public ProductsDisplayViewModel ProductsDisplay { get; } = new ProductsDisplayViewModel();
|
||||
|
||||
private double? _downloadProgress = null;
|
||||
public double? DownloadProgress { get => _downloadProgress; set => this.RaiseAndSetIfChanged(ref _downloadProgress, value); }
|
||||
|
||||
|
||||
/// <summary> Library filterting query </summary>
|
||||
public string FilterString { get => _filterString; set => this.RaiseAndSetIfChanged(ref _filterString, value); }
|
||||
|
||||
|
||||
/// <summary> Display text for the "Remove # Books from Libation" button </summary>
|
||||
public string RemoveBooksButtonText { get => _removeBooksButtonText; set => this.RaiseAndSetIfChanged(ref _removeBooksButtonText, value); }
|
||||
|
||||
|
||||
/// <summary> Indicates if the "Remove # Books from Libation" button is enabled </summary>
|
||||
public bool RemoveBooksButtonEnabled { get => _removeBooksButtonEnabled; set { this.RaiseAndSetIfChanged(ref _removeBooksButtonEnabled, value); } }
|
||||
|
||||
|
||||
/// <summary> Auto scanning accounts is enables </summary>
|
||||
public bool AutoScanChecked
|
||||
{
|
||||
get => _autoScanChecked;
|
||||
set
|
||||
{
|
||||
if (value != _autoScanChecked)
|
||||
Configuration.Instance.AutoScan = value;
|
||||
this.RaiseAndSetIfChanged(ref _autoScanChecked, value);
|
||||
}
|
||||
}
|
||||
|
||||
public AvaloniaList<Control> QuickFilterMenuItems { get; } = new();
|
||||
|
||||
/// <summary> Indicates if the first quick filter is the default filter </summary>
|
||||
public bool FirstFilterIsDefault
|
||||
{
|
||||
get => _firstFilterIsDefault;
|
||||
set
|
||||
{
|
||||
if (value != _firstFilterIsDefault)
|
||||
QuickFilters.UseDefault = value;
|
||||
this.RaiseAndSetIfChanged(ref _firstFilterIsDefault, value);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary> Indicates if the "Remove # Books from Libation" and "Done Removing" buttons should be visible </summary>
|
||||
public bool RemoveButtonsVisible
|
||||
{
|
||||
get => _removeButtonsVisible;
|
||||
set
|
||||
{
|
||||
this.RaiseAndSetIfChanged(ref _removeButtonsVisible, value);
|
||||
this.RaisePropertyChanged(nameof(RemoveMenuItemsEnabled));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/// <summary> The number of accounts currently being scanned </summary>
|
||||
public int NumAccountsScanning
|
||||
{
|
||||
get => _numAccountsScanning;
|
||||
set
|
||||
{
|
||||
this.RaiseAndSetIfChanged(ref _numAccountsScanning, value);
|
||||
this.RaisePropertyChanged(nameof(ActivelyScanning));
|
||||
this.RaisePropertyChanged(nameof(RemoveMenuItemsEnabled));
|
||||
this.RaisePropertyChanged(nameof(ScanningText));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary> Indicates if Libation is currently scanning account(s) </summary>
|
||||
public bool ActivelyScanning => _numAccountsScanning > 0;
|
||||
/// <summary> Indicates if the "Remove Books" menu items are enabled</summary>
|
||||
public bool RemoveMenuItemsEnabled => !RemoveButtonsVisible && !ActivelyScanning;
|
||||
/// <summary> The library scanning status text </summary>
|
||||
public string ScanningText => _numAccountsScanning == 1 ? "Scanning..." : $"Scanning {_numAccountsScanning} accounts...";
|
||||
|
||||
|
||||
|
||||
/// <summary> The number of accounts added to Libation </summary>
|
||||
public int AccountsCount
|
||||
{
|
||||
get => _accountsCount;
|
||||
set
|
||||
{
|
||||
this.RaiseAndSetIfChanged(ref _accountsCount, value);
|
||||
this.RaisePropertyChanged(nameof(ZeroAccounts));
|
||||
this.RaisePropertyChanged(nameof(AnyAccounts));
|
||||
this.RaisePropertyChanged(nameof(OneAccount));
|
||||
this.RaisePropertyChanged(nameof(MultipleAccounts));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary> There are no Audible accounts </summary>
|
||||
public bool ZeroAccounts => _accountsCount == 0;
|
||||
/// <summary> There is at least one Audible account </summary>
|
||||
public bool AnyAccounts => _accountsCount > 0;
|
||||
/// <summary> There is exactly one Audible account </summary>
|
||||
public bool OneAccount => _accountsCount == 1;
|
||||
/// <summary> There are more than 1 Audible accounts </summary>
|
||||
public bool MultipleAccounts => _accountsCount > 1;
|
||||
|
||||
|
||||
|
||||
/// <summary> The Process Queue panel is open </summary>
|
||||
public bool QueueOpen
|
||||
{
|
||||
get => _queueOpen;
|
||||
set
|
||||
{
|
||||
this.RaiseAndSetIfChanged(ref _queueOpen, value);
|
||||
QueueButtonAngle = value ? 180 : 0;
|
||||
this.RaisePropertyChanged(nameof(QueueButtonAngle));
|
||||
}
|
||||
}
|
||||
public double QueueButtonAngle { get; private set; }
|
||||
|
||||
|
||||
/// <summary> The number of books visible in the Product Display </summary>
|
||||
public int VisibleCount
|
||||
{
|
||||
get => _visibleCount;
|
||||
set
|
||||
{
|
||||
this.RaiseAndSetIfChanged(ref _visibleCount, value);
|
||||
this.RaisePropertyChanged(nameof(VisibleCountText));
|
||||
this.RaisePropertyChanged(nameof(VisibleCountMenuItemText));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary> The Bottom-right visible book count status text </summary>
|
||||
public string VisibleCountText => $"Visible: {VisibleCount}";
|
||||
/// <summary> The Visible Books menu item header text </summary>
|
||||
public string VisibleCountMenuItemText => $"_Visible Books {VisibleCount}";
|
||||
|
||||
|
||||
|
||||
/// <summary> The user's library statistics </summary>
|
||||
public LibraryCommands.LibraryStats LibraryStats
|
||||
{
|
||||
get => _libraryStats;
|
||||
set
|
||||
{
|
||||
this.RaiseAndSetIfChanged(ref _libraryStats, value);
|
||||
|
||||
BookBackupsToolStripText
|
||||
= LibraryStats.HasPendingBooks
|
||||
? $"Begin _Book and PDF Backups: {LibraryStats.PendingBooks} remaining"
|
||||
: "All books have been liberated";
|
||||
|
||||
PdfBackupsToolStripText
|
||||
= LibraryStats.pdfsNotDownloaded > 0
|
||||
? $"Begin _PDF Only Backups: {LibraryStats.pdfsNotDownloaded} remaining"
|
||||
: "All PDFs have been downloaded";
|
||||
|
||||
this.RaisePropertyChanged(nameof(BookBackupsToolStripText));
|
||||
this.RaisePropertyChanged(nameof(PdfBackupsToolStripText));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary> The "Begin Book and PDF Backup" menu item header text </summary>
|
||||
public string BookBackupsToolStripText { get; private set; } = "Begin _Book and PDF Backups: 0";
|
||||
/// <summary> The "Begin PDF Only Backup" menu item header text </summary>
|
||||
public string PdfBackupsToolStripText { get; private set; } = "Begin _PDF Only Backups: 0";
|
||||
|
||||
|
||||
/// <summary> The number of books visible in the Products Display that have not yet been liberated </summary>
|
||||
public int VisibleNotLiberated
|
||||
{
|
||||
get => _visibleNotLiberated;
|
||||
set
|
||||
{
|
||||
this.RaiseAndSetIfChanged(ref _visibleNotLiberated, value);
|
||||
|
||||
LiberateVisibleToolStripText
|
||||
= AnyVisibleNotLiberated
|
||||
? $"Liberate _Visible Books: {VisibleNotLiberated}"
|
||||
: "All visible books are liberated";
|
||||
|
||||
LiberateVisibleToolStripText_2
|
||||
= AnyVisibleNotLiberated
|
||||
? $"_Liberate: {VisibleNotLiberated}"
|
||||
: "All visible books are liberated";
|
||||
|
||||
this.RaisePropertyChanged(nameof(AnyVisibleNotLiberated));
|
||||
this.RaisePropertyChanged(nameof(LiberateVisibleToolStripText));
|
||||
this.RaisePropertyChanged(nameof(LiberateVisibleToolStripText_2));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary> Indicates if any of the books visible in the Products Display haven't been liberated </summary>
|
||||
public bool AnyVisibleNotLiberated => VisibleNotLiberated > 0;
|
||||
/// <summary> The "Liberate Visible Books" menu item header text (submenu item of the "Liberate Menu" menu item) </summary>
|
||||
public string LiberateVisibleToolStripText { get; private set; } = "Liberate _Visible Books: 0";
|
||||
/// <summary> The "Liberate" menu item header text (submenu item of the "Visible Books" menu item) </summary>
|
||||
public string LiberateVisibleToolStripText_2 { get; private set; } = "_Liberate: 0";
|
||||
}
|
||||
}
|
||||
@@ -117,7 +117,7 @@ namespace LibationAvalonia.ViewModels
|
||||
}
|
||||
|
||||
//Create the filtered-in list before adding entries to avoid a refresh
|
||||
FilteredInGridEntries = QueryResults(geList, FilterString);
|
||||
FilteredInGridEntries = QueryResults(geList.Union(geList.OfType<ISeriesEntry>().SelectMany(s => s.Children)), FilterString);
|
||||
SOURCE.AddRange(geList.OrderByDescending(e => e.DateAdded));
|
||||
|
||||
//Add all children beneath their parent
|
||||
|
||||
@@ -70,9 +70,9 @@ namespace LibationAvalonia.ViewModels
|
||||
return InternalCompare(parentA, geB);
|
||||
}
|
||||
|
||||
//both are children of the same series, always present in order of series index, ascending
|
||||
//both are children of the same series
|
||||
if (parentA == parentB)
|
||||
return geA.SeriesIndex.CompareTo(geB.SeriesIndex) * (sortDirection is ListSortDirection.Ascending ? 1 : -1);
|
||||
return InternalCompare(geA, geB);
|
||||
|
||||
//a and b are children of different series.
|
||||
return InternalCompare(parentA, parentB);
|
||||
|
||||
157
Source/LibationAvalonia/ViewModels/Settings/AudioSettingsVM.cs
Normal file
157
Source/LibationAvalonia/ViewModels/Settings/AudioSettingsVM.cs
Normal file
@@ -0,0 +1,157 @@
|
||||
using Avalonia.Collections;
|
||||
using Avalonia.Controls;
|
||||
using Dinah.Core;
|
||||
using LibationFileManager;
|
||||
using LibationUiBase;
|
||||
using ReactiveUI;
|
||||
using System.Linq;
|
||||
|
||||
namespace LibationAvalonia.ViewModels.Settings
|
||||
{
|
||||
public class AudioSettingsVM : ViewModelBase
|
||||
{
|
||||
private bool _downloadClipsBookmarks;
|
||||
private bool _decryptToLossy;
|
||||
private bool _splitFilesByChapter;
|
||||
private bool _allowLibationFixup;
|
||||
private bool _lameTargetBitrate;
|
||||
private bool _lameMatchSource;
|
||||
private int _lameBitrate;
|
||||
private int _lameVBRQuality;
|
||||
private string _chapterTitleTemplate;
|
||||
public SampleRateSelection SelectedSampleRate { get; set; }
|
||||
public NAudio.Lame.EncoderQuality SelectedEncoderQuality { get; set; }
|
||||
|
||||
public AvaloniaList<SampleRateSelection> SampleRates { get; }
|
||||
= new(
|
||||
new[]
|
||||
{
|
||||
AAXClean.SampleRate.Hz_44100,
|
||||
AAXClean.SampleRate.Hz_32000,
|
||||
AAXClean.SampleRate.Hz_24000,
|
||||
AAXClean.SampleRate.Hz_22050,
|
||||
AAXClean.SampleRate.Hz_16000,
|
||||
AAXClean.SampleRate.Hz_12000,
|
||||
}
|
||||
.Select(s => new SampleRateSelection(s)));
|
||||
|
||||
public AvaloniaList<NAudio.Lame.EncoderQuality> EncoderQualities { get; }
|
||||
= new(
|
||||
new[]
|
||||
{
|
||||
NAudio.Lame.EncoderQuality.High,
|
||||
NAudio.Lame.EncoderQuality.Standard,
|
||||
NAudio.Lame.EncoderQuality.Fast,
|
||||
});
|
||||
|
||||
|
||||
public AudioSettingsVM(Configuration config)
|
||||
{
|
||||
LoadSettings(config);
|
||||
}
|
||||
public void LoadSettings(Configuration config)
|
||||
{
|
||||
CreateCueSheet = config.CreateCueSheet;
|
||||
AllowLibationFixup = config.AllowLibationFixup;
|
||||
DownloadCoverArt = config.DownloadCoverArt;
|
||||
RetainAaxFile = config.RetainAaxFile;
|
||||
DownloadClipsBookmarks = config.DownloadClipsBookmarks;
|
||||
ClipBookmarkFormat = config.ClipsBookmarksFileFormat;
|
||||
SplitFilesByChapter = config.SplitFilesByChapter;
|
||||
MergeOpeningAndEndCredits = config.MergeOpeningAndEndCredits;
|
||||
StripAudibleBrandAudio = config.StripAudibleBrandAudio;
|
||||
StripUnabridged = config.StripUnabridged;
|
||||
ChapterTitleTemplate = config.ChapterTitleTemplate;
|
||||
DecryptToLossy = config.DecryptToLossy;
|
||||
MoveMoovToBeginning = config.MoveMoovToBeginning;
|
||||
LameTargetBitrate = config.LameTargetBitrate;
|
||||
LameDownsampleMono = config.LameDownsampleMono;
|
||||
LameConstantBitrate = config.LameConstantBitrate;
|
||||
LameMatchSource = config.LameMatchSourceBR;
|
||||
LameBitrate = config.LameBitrate;
|
||||
LameVBRQuality = config.LameVBRQuality;
|
||||
|
||||
SelectedSampleRate = SampleRates.FirstOrDefault(s => s.SampleRate == config.MaxSampleRate);
|
||||
SelectedEncoderQuality = config.LameEncoderQuality;
|
||||
}
|
||||
|
||||
public void SaveSettings(Configuration config)
|
||||
{
|
||||
config.CreateCueSheet = CreateCueSheet;
|
||||
config.AllowLibationFixup = AllowLibationFixup;
|
||||
config.DownloadCoverArt = DownloadCoverArt;
|
||||
config.RetainAaxFile = RetainAaxFile;
|
||||
config.DownloadClipsBookmarks = DownloadClipsBookmarks;
|
||||
config.ClipsBookmarksFileFormat = ClipBookmarkFormat;
|
||||
config.SplitFilesByChapter = SplitFilesByChapter;
|
||||
config.MergeOpeningAndEndCredits = MergeOpeningAndEndCredits;
|
||||
config.StripAudibleBrandAudio = StripAudibleBrandAudio;
|
||||
config.StripUnabridged = StripUnabridged;
|
||||
config.ChapterTitleTemplate = ChapterTitleTemplate;
|
||||
config.DecryptToLossy = DecryptToLossy;
|
||||
config.MoveMoovToBeginning = MoveMoovToBeginning;
|
||||
config.LameTargetBitrate = LameTargetBitrate;
|
||||
config.LameDownsampleMono = LameDownsampleMono;
|
||||
config.LameConstantBitrate = LameConstantBitrate;
|
||||
config.LameMatchSourceBR = LameMatchSource;
|
||||
config.LameBitrate = LameBitrate;
|
||||
config.LameVBRQuality = LameVBRQuality;
|
||||
|
||||
config.LameEncoderQuality = SelectedEncoderQuality;
|
||||
config.MaxSampleRate = SelectedSampleRate?.SampleRate ?? config.MaxSampleRate;
|
||||
}
|
||||
|
||||
public AvaloniaList<Configuration.ClipBookmarkFormat> ClipBookmarkFormats { get; } = new(Enum<Configuration.ClipBookmarkFormat>.GetValues());
|
||||
public string CreateCueSheetText { get; } = Configuration.GetDescription(nameof(Configuration.CreateCueSheet));
|
||||
public string AllowLibationFixupText { get; } = Configuration.GetDescription(nameof(Configuration.AllowLibationFixup));
|
||||
public string DownloadCoverArtText { get; } = Configuration.GetDescription(nameof(Configuration.DownloadCoverArt));
|
||||
public string RetainAaxFileText { get; } = Configuration.GetDescription(nameof(Configuration.RetainAaxFile));
|
||||
public string SplitFilesByChapterText { get; } = Configuration.GetDescription(nameof(Configuration.SplitFilesByChapter));
|
||||
public string MergeOpeningEndCreditsText { get; } = Configuration.GetDescription(nameof(Configuration.MergeOpeningAndEndCredits));
|
||||
public string StripAudibleBrandingText { get; } = Configuration.GetDescription(nameof(Configuration.StripAudibleBrandAudio));
|
||||
public string StripUnabridgedText { get; } = Configuration.GetDescription(nameof(Configuration.StripUnabridged));
|
||||
public string ChapterTitleTemplateText { get; } = Configuration.GetDescription(nameof(Configuration.ChapterTitleTemplate));
|
||||
public string MoveMoovToBeginningText { get; } = Configuration.GetDescription(nameof(Configuration.MoveMoovToBeginning));
|
||||
|
||||
public bool CreateCueSheet { get; set; }
|
||||
public bool DownloadCoverArt { get; set; }
|
||||
public bool RetainAaxFile { get; set; }
|
||||
public bool DownloadClipsBookmarks { get => _downloadClipsBookmarks; set => this.RaiseAndSetIfChanged(ref _downloadClipsBookmarks, value); }
|
||||
public Configuration.ClipBookmarkFormat ClipBookmarkFormat { get; set; }
|
||||
public bool MergeOpeningAndEndCredits { get; set; }
|
||||
public bool StripAudibleBrandAudio { get; set; }
|
||||
public bool StripUnabridged { get; set; }
|
||||
public bool DecryptToLossy { get => _decryptToLossy; set => this.RaiseAndSetIfChanged(ref _decryptToLossy, value); }
|
||||
public bool MoveMoovToBeginning { get; set; }
|
||||
|
||||
public bool LameDownsampleMono { get; set; } = Design.IsDesignMode;
|
||||
public bool LameConstantBitrate { get; set; } = Design.IsDesignMode;
|
||||
|
||||
public bool SplitFilesByChapter { get => _splitFilesByChapter; set { this.RaiseAndSetIfChanged(ref _splitFilesByChapter, value); } }
|
||||
public bool LameTargetBitrate { get => _lameTargetBitrate; set { this.RaiseAndSetIfChanged(ref _lameTargetBitrate, value); } }
|
||||
public bool LameMatchSource { get => _lameMatchSource; set { this.RaiseAndSetIfChanged(ref _lameMatchSource, value); } }
|
||||
public int LameBitrate { get => _lameBitrate; set { this.RaiseAndSetIfChanged(ref _lameBitrate, value); } }
|
||||
public int LameVBRQuality { get => _lameVBRQuality; set { this.RaiseAndSetIfChanged(ref _lameVBRQuality, value); } }
|
||||
|
||||
public string ChapterTitleTemplate { get => _chapterTitleTemplate; set { this.RaiseAndSetIfChanged(ref _chapterTitleTemplate, value); } }
|
||||
|
||||
public bool AllowLibationFixup
|
||||
{
|
||||
get => _allowLibationFixup;
|
||||
set
|
||||
{
|
||||
if (!this.RaiseAndSetIfChanged(ref _allowLibationFixup, value))
|
||||
{
|
||||
SplitFilesByChapter = false;
|
||||
StripAudibleBrandAudio = false;
|
||||
StripUnabridged = false;
|
||||
DecryptToLossy = false;
|
||||
this.RaisePropertyChanged(nameof(SplitFilesByChapter));
|
||||
this.RaisePropertyChanged(nameof(StripAudibleBrandAudio));
|
||||
this.RaisePropertyChanged(nameof(StripUnabridged));
|
||||
this.RaisePropertyChanged(nameof(DecryptToLossy));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
using Dinah.Core;
|
||||
using LibationFileManager;
|
||||
using ReactiveUI;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace LibationAvalonia.ViewModels.Settings
|
||||
{
|
||||
public class DownloadDecryptSettingsVM : ViewModelBase
|
||||
{
|
||||
private string _folderTemplate;
|
||||
private string _fileTemplate;
|
||||
private string _chapterFileTemplate;
|
||||
|
||||
public Configuration Config { get; }
|
||||
public DownloadDecryptSettingsVM(Configuration config)
|
||||
{
|
||||
Config = config;
|
||||
LoadSettings(config);
|
||||
}
|
||||
|
||||
public List<Configuration.KnownDirectories> KnownDirectories { get; } = new()
|
||||
{
|
||||
Configuration.KnownDirectories.WinTemp,
|
||||
Configuration.KnownDirectories.UserProfile,
|
||||
Configuration.KnownDirectories.AppDir,
|
||||
Configuration.KnownDirectories.MyDocs,
|
||||
Configuration.KnownDirectories.LibationFiles
|
||||
};
|
||||
|
||||
public void LoadSettings(Configuration config)
|
||||
{
|
||||
BadBookAsk = config.BadBook is Configuration.BadBookAction.Ask;
|
||||
BadBookAbort = config.BadBook is Configuration.BadBookAction.Abort;
|
||||
BadBookRetry = config.BadBook is Configuration.BadBookAction.Retry;
|
||||
BadBookIgnore = config.BadBook is Configuration.BadBookAction.Ignore;
|
||||
FolderTemplate = config.FolderTemplate;
|
||||
FileTemplate = config.FileTemplate;
|
||||
ChapterFileTemplate = config.ChapterFileTemplate;
|
||||
InProgressDirectory = config.InProgress;
|
||||
UseCoverAsFolderIcon = config.UseCoverAsFolderIcon;
|
||||
}
|
||||
|
||||
public void SaveSettings(Configuration config)
|
||||
{
|
||||
config.BadBook
|
||||
= BadBookAbort ? Configuration.BadBookAction.Abort
|
||||
: BadBookRetry ? Configuration.BadBookAction.Retry
|
||||
: BadBookIgnore ? Configuration.BadBookAction.Ignore
|
||||
: Configuration.BadBookAction.Ask;
|
||||
|
||||
config.FolderTemplate = FolderTemplate;
|
||||
config.FileTemplate = FileTemplate;
|
||||
config.ChapterFileTemplate = ChapterFileTemplate;
|
||||
config.InProgress = InProgressDirectory;
|
||||
|
||||
config.UseCoverAsFolderIcon = UseCoverAsFolderIcon;
|
||||
}
|
||||
|
||||
public string UseCoverAsFolderIconText { get; } = Configuration.GetDescription(nameof(Configuration.UseCoverAsFolderIcon));
|
||||
public string BadBookGroupboxText { get; } = Configuration.GetDescription(nameof(Configuration.BadBook));
|
||||
public string BadBookAskText { get; } = Configuration.BadBookAction.Ask.GetDescription();
|
||||
public string BadBookAbortText { get; } = Configuration.BadBookAction.Abort.GetDescription();
|
||||
public string BadBookRetryText { get; } = Configuration.BadBookAction.Retry.GetDescription();
|
||||
public string BadBookIgnoreText { get; } = Configuration.BadBookAction.Ignore.GetDescription();
|
||||
public string FolderTemplateText { get; } = Configuration.GetDescription(nameof(Configuration.FolderTemplate));
|
||||
public string FileTemplateText { get; } = Configuration.GetDescription(nameof(Configuration.FileTemplate));
|
||||
public string ChapterFileTemplateText { get; } = Configuration.GetDescription(nameof(Configuration.ChapterFileTemplate));
|
||||
public string EditCharReplacementText { get; } = Configuration.GetDescription(nameof(Configuration.ReplacementCharacters));
|
||||
public string InProgressDescriptionText { get; } = Configuration.GetDescription(nameof(Configuration.InProgress));
|
||||
|
||||
public string FolderTemplate { get => _folderTemplate; set { this.RaiseAndSetIfChanged(ref _folderTemplate, value); } }
|
||||
public string FileTemplate { get => _fileTemplate; set { this.RaiseAndSetIfChanged(ref _fileTemplate, value); } }
|
||||
public string ChapterFileTemplate { get => _chapterFileTemplate; set { this.RaiseAndSetIfChanged(ref _chapterFileTemplate, value); } }
|
||||
public bool UseCoverAsFolderIcon { get; set; }
|
||||
|
||||
public bool BadBookAsk { get; set; }
|
||||
public bool BadBookAbort { get; set; }
|
||||
public bool BadBookRetry { get; set; }
|
||||
public bool BadBookIgnore { get; set; }
|
||||
|
||||
public string InProgressDirectory { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
using LibationFileManager;
|
||||
|
||||
namespace LibationAvalonia.ViewModels.Settings
|
||||
{
|
||||
public class ImportSettingsVM
|
||||
{
|
||||
public ImportSettingsVM(Configuration config)
|
||||
{
|
||||
LoadSettings(config);
|
||||
}
|
||||
|
||||
public void LoadSettings(Configuration config)
|
||||
{
|
||||
AutoScan = config.AutoScan;
|
||||
ShowImportedStats = config.ShowImportedStats;
|
||||
ImportEpisodes = config.ImportEpisodes;
|
||||
DownloadEpisodes = config.DownloadEpisodes;
|
||||
AutoDownloadEpisodes = config.AutoDownloadEpisodes;
|
||||
}
|
||||
|
||||
public void SaveSettings(Configuration config)
|
||||
{
|
||||
config.AutoScan = AutoScan;
|
||||
config.ShowImportedStats = ShowImportedStats;
|
||||
config.ImportEpisodes = ImportEpisodes;
|
||||
config.DownloadEpisodes = DownloadEpisodes;
|
||||
config.AutoDownloadEpisodes = AutoDownloadEpisodes;
|
||||
}
|
||||
|
||||
public string AutoScanText { get; } = Configuration.GetDescription(nameof(Configuration.AutoScan));
|
||||
public string ShowImportedStatsText { get; } = Configuration.GetDescription(nameof(Configuration.ShowImportedStats));
|
||||
public string ImportEpisodesText { get; } = Configuration.GetDescription(nameof(Configuration.ImportEpisodes));
|
||||
public string DownloadEpisodesText { get; } = Configuration.GetDescription(nameof(Configuration.DownloadEpisodes));
|
||||
public string AutoDownloadEpisodesText { get; } = Configuration.GetDescription(nameof(Configuration.AutoDownloadEpisodes));
|
||||
|
||||
public bool AutoScan { get; set; }
|
||||
public bool ShowImportedStats { get; set; }
|
||||
public bool ImportEpisodes { get; set; }
|
||||
public bool DownloadEpisodes { get; set; }
|
||||
public bool AutoDownloadEpisodes { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
using FileManager;
|
||||
using LibationFileManager;
|
||||
using ReactiveUI;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace LibationAvalonia.ViewModels.Settings
|
||||
{
|
||||
public class ImportantSettingsVM : ViewModelBase
|
||||
{
|
||||
private string themeVariant;
|
||||
private string initialThemeVariant;
|
||||
|
||||
public ImportantSettingsVM(Configuration config)
|
||||
{
|
||||
LoadSettings(config);
|
||||
}
|
||||
|
||||
public void LoadSettings(Configuration config)
|
||||
{
|
||||
BooksDirectory = config.Books.PathWithoutPrefix;
|
||||
SavePodcastsToParentFolder = config.SavePodcastsToParentFolder;
|
||||
LoggingLevel = config.LogLevel;
|
||||
ThemeVariant = initialThemeVariant
|
||||
= Configuration.Instance.GetString(propertyName: nameof(ThemeVariant)) is nameof(Avalonia.Styling.ThemeVariant.Dark)
|
||||
? nameof(Avalonia.Styling.ThemeVariant.Dark)
|
||||
: nameof(Avalonia.Styling.ThemeVariant.Light);
|
||||
}
|
||||
|
||||
public void SaveSettings(Configuration config)
|
||||
{
|
||||
LongPath lonNewBooks = Configuration.GetKnownDirectory(BooksDirectory) is Configuration.KnownDirectories.None ? BooksDirectory : System.IO.Path.Combine(BooksDirectory, "Books");
|
||||
if (!System.IO.Directory.Exists(lonNewBooks))
|
||||
System.IO.Directory.CreateDirectory(lonNewBooks);
|
||||
config.Books = lonNewBooks;
|
||||
config.SavePodcastsToParentFolder = SavePodcastsToParentFolder;
|
||||
config.LogLevel = LoggingLevel;
|
||||
Configuration.Instance.SetString(ThemeVariant, nameof(ThemeVariant));
|
||||
}
|
||||
|
||||
public List<Configuration.KnownDirectories> KnownDirectories { get; } = new()
|
||||
{
|
||||
Configuration.KnownDirectories.UserProfile,
|
||||
Configuration.KnownDirectories.AppDir,
|
||||
Configuration.KnownDirectories.MyDocs
|
||||
};
|
||||
|
||||
public string BooksText { get; } = Configuration.GetDescription(nameof(Configuration.Books));
|
||||
public string SavePodcastsToParentFolderText { get; } = Configuration.GetDescription(nameof(Configuration.SavePodcastsToParentFolder));
|
||||
public Serilog.Events.LogEventLevel[] LoggingLevels { get; } = Enum.GetValues<Serilog.Events.LogEventLevel>();
|
||||
public string BetaOptInText { get; } = Configuration.GetDescription(nameof(Configuration.BetaOptIn));
|
||||
public string[] Themes { get; } = { nameof(Avalonia.Styling.ThemeVariant.Light), nameof(Avalonia.Styling.ThemeVariant.Dark) };
|
||||
|
||||
public string BooksDirectory { get; set; }
|
||||
public bool SavePodcastsToParentFolder { get; set; }
|
||||
public Serilog.Events.LogEventLevel LoggingLevel { get; set; }
|
||||
|
||||
public string ThemeVariant
|
||||
{
|
||||
get => themeVariant;
|
||||
set
|
||||
{
|
||||
this.RaiseAndSetIfChanged(ref themeVariant, value);
|
||||
|
||||
SelectionChanged = ThemeVariant != initialThemeVariant;
|
||||
this.RaisePropertyChanged(nameof(SelectionChanged));
|
||||
}
|
||||
}
|
||||
public bool SelectionChanged { get; private set; }
|
||||
}
|
||||
}
|
||||
33
Source/LibationAvalonia/ViewModels/Settings/SettingsVM.cs
Normal file
33
Source/LibationAvalonia/ViewModels/Settings/SettingsVM.cs
Normal file
@@ -0,0 +1,33 @@
|
||||
using LibationFileManager;
|
||||
|
||||
namespace LibationAvalonia.ViewModels.Settings
|
||||
{
|
||||
public class SettingsVM
|
||||
{
|
||||
public SettingsVM(Configuration config)
|
||||
{
|
||||
LoadSettings(config);
|
||||
}
|
||||
|
||||
public ImportantSettingsVM ImportantSettings { get; private set; }
|
||||
public ImportSettingsVM ImportSettings { get; private set; }
|
||||
public DownloadDecryptSettingsVM DownloadDecryptSettings { get; private set; }
|
||||
public AudioSettingsVM AudioSettings { get; private set; }
|
||||
|
||||
public void LoadSettings(Configuration config)
|
||||
{
|
||||
ImportantSettings = new ImportantSettingsVM(config);
|
||||
ImportSettings = new ImportSettingsVM(config);
|
||||
DownloadDecryptSettings = new DownloadDecryptSettingsVM(config);
|
||||
AudioSettings = new AudioSettingsVM(config);
|
||||
}
|
||||
|
||||
public void SaveSettings(Configuration config)
|
||||
{
|
||||
ImportantSettings.SaveSettings(config);
|
||||
ImportSettings.SaveSettings(config);
|
||||
DownloadDecryptSettings.SaveSettings(config);
|
||||
AudioSettings.SaveSettings(config);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
using ApplicationServices;
|
||||
using Avalonia.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace LibationAvalonia.Views
|
||||
{
|
||||
public partial class MainWindow
|
||||
{
|
||||
private Task updateCountsTask;
|
||||
private void Configure_BackupCounts()
|
||||
{
|
||||
Load += setBackupCounts;
|
||||
LibraryCommands.LibrarySizeChanged += setBackupCounts;
|
||||
LibraryCommands.BookUserDefinedItemCommitted += setBackupCounts;
|
||||
}
|
||||
|
||||
private void setBackupCounts(object _, object __)
|
||||
{
|
||||
if (updateCountsTask?.IsCompleted is not false)
|
||||
updateCountsTask = Dispatcher.UIThread.InvokeAsync(() => _viewModel.LibraryStats = LibraryCommands.GetCounts());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,47 +0,0 @@
|
||||
using Avalonia.Input;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace LibationAvalonia.Views
|
||||
{
|
||||
public partial class MainWindow
|
||||
{
|
||||
protected void Configure_Filter() { }
|
||||
|
||||
public async void filterHelpBtn_Click(object sender, Avalonia.Interactivity.RoutedEventArgs e)
|
||||
=> await (new LibationAvalonia.Dialogs.SearchSyntaxDialog()).ShowDialog(this);
|
||||
|
||||
public async void filterSearchTb_KeyPress(object sender, KeyEventArgs e)
|
||||
{
|
||||
if (e.Key == Key.Return)
|
||||
{
|
||||
await performFilter(_viewModel.FilterString);
|
||||
|
||||
// silence the 'ding'
|
||||
e.Handled = true;
|
||||
}
|
||||
}
|
||||
|
||||
public async void filterBtn_Click(object sender, Avalonia.Interactivity.RoutedEventArgs e)
|
||||
=> await performFilter(_viewModel.FilterString);
|
||||
|
||||
private string lastGoodFilter = "";
|
||||
private async Task performFilter(string filterString)
|
||||
{
|
||||
_viewModel.FilterString = filterString;
|
||||
|
||||
try
|
||||
{
|
||||
await _viewModel.ProductsDisplay.Filter(filterString);
|
||||
lastGoodFilter = filterString;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
await MessageBox.Show($"Bad filter string:\r\n\r\n{ex.Message}", "Bad filter string", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||||
|
||||
// re-apply last good filter
|
||||
await performFilter(lastGoodFilter);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,59 +0,0 @@
|
||||
using DataLayer;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace LibationAvalonia.Views
|
||||
{
|
||||
public partial class MainWindow
|
||||
{
|
||||
private void Configure_Liberate() { }
|
||||
|
||||
//GetLibrary_Flat_NoTracking() may take a long time on a hugh library. so run in new thread
|
||||
public void beginBookBackupsToolStripMenuItem_Click(object _ = null, Avalonia.Interactivity.RoutedEventArgs __ = null)
|
||||
{
|
||||
try
|
||||
{
|
||||
SetQueueCollapseState(false);
|
||||
|
||||
Serilog.Log.Logger.Information("Begin backing up all library books");
|
||||
|
||||
_viewModel.ProcessQueue.AddDownloadDecrypt(
|
||||
ApplicationServices.DbContexts
|
||||
.GetLibrary_Flat_NoTracking()
|
||||
.UnLiberated()
|
||||
);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Serilog.Log.Logger.Error(ex, "An error occurred while backing up all library books");
|
||||
}
|
||||
}
|
||||
|
||||
public async void beginPdfBackupsToolStripMenuItem_Click(object sender, Avalonia.Interactivity.RoutedEventArgs args)
|
||||
{
|
||||
SetQueueCollapseState(false);
|
||||
await Task.Run(() => _viewModel.ProcessQueue.AddDownloadPdf(ApplicationServices.DbContexts.GetLibrary_Flat_NoTracking()
|
||||
.Where(lb => lb.Book.UserDefinedItem.PdfStatus is DataLayer.LiberatedStatus.NotLiberated)));
|
||||
}
|
||||
|
||||
public async void convertAllM4bToMp3ToolStripMenuItem_Click(object sender, Avalonia.Interactivity.RoutedEventArgs args)
|
||||
{
|
||||
var result = await MessageBox.Show(
|
||||
"This converts all m4b titles in your library to mp3 files. Original files are not deleted."
|
||||
+ "\r\nFor large libraries this will take a long time and will take up more disk space."
|
||||
+ "\r\n\r\nContinue?"
|
||||
+ "\r\n\r\n(To always download titles as mp3 instead of m4b, go to Settings: Download my books as .MP3 files)",
|
||||
"Convert all M4b => Mp3?",
|
||||
MessageBoxButtons.YesNo,
|
||||
MessageBoxIcon.Warning);
|
||||
if (result == DialogResult.Yes)
|
||||
{
|
||||
SetQueueCollapseState(false);
|
||||
await Task.Run(() => _viewModel.ProcessQueue.AddConvertMp3(ApplicationServices.DbContexts.GetLibrary_Flat_NoTracking()
|
||||
.Where(lb => lb.Book.UserDefinedItem.BookStatus is DataLayer.LiberatedStatus.Liberated && lb.Book.ContentType is DataLayer.ContentType.Product)));
|
||||
}
|
||||
//Only Queue Liberated books for conversion. This isn't a perfect filter, but it's better than nothing.
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,90 +0,0 @@
|
||||
using Avalonia;
|
||||
using Avalonia.Controls;
|
||||
using LibationFileManager;
|
||||
using System.Linq;
|
||||
using Avalonia.Data;
|
||||
|
||||
namespace LibationAvalonia.Views
|
||||
{
|
||||
public partial class MainWindow
|
||||
{
|
||||
private void Configure_QuickFilters()
|
||||
{
|
||||
_viewModel.FirstFilterIsDefault = QuickFilters.UseDefault;
|
||||
Load += updateFiltersMenu;
|
||||
QuickFilters.Updated += updateFiltersMenu;
|
||||
|
||||
//We need to be able to dynamically add and remove menu items from the Quick Filters menu.
|
||||
//To do that, we need quick filter's menu items source to be writable, which we can only
|
||||
//achieve by creating the list ourselves (instead of allowing Avalonia to create it from the xaml)
|
||||
|
||||
var startWithFilterMenuItem = new MenuItem
|
||||
{
|
||||
Header = "Start Libation with 1st filter _Default",
|
||||
Icon = new CheckBox
|
||||
{
|
||||
BorderThickness = new Thickness(0),
|
||||
IsHitTestVisible = false,
|
||||
[!CheckBox.IsCheckedProperty] = new Binding(nameof(_viewModel.FirstFilterIsDefault))
|
||||
}
|
||||
};
|
||||
|
||||
var editFiltersMenuItem = new MenuItem { Header = "_Edit quick filters..." };
|
||||
|
||||
startWithFilterMenuItem.Click += firstFilterIsDefaultToolStripMenuItem_Click;
|
||||
editFiltersMenuItem.Click += editQuickFiltersToolStripMenuItem_Click;
|
||||
|
||||
_viewModel.QuickFilterMenuItems.Add(startWithFilterMenuItem);
|
||||
_viewModel.QuickFilterMenuItems.Add(editFiltersMenuItem);
|
||||
_viewModel.QuickFilterMenuItems.Add(new Separator());
|
||||
}
|
||||
|
||||
private async void QuickFiltersMenuItem_KeyDown(object sender, Avalonia.Input.KeyEventArgs e)
|
||||
{
|
||||
int keyNum = (int)e.Key - 34;
|
||||
|
||||
if (keyNum <=9 && keyNum >= 1)
|
||||
{
|
||||
var menuItem = _viewModel.QuickFilterMenuItems
|
||||
.OfType<MenuItem>()
|
||||
.FirstOrDefault(i => i.Header is string h && h.StartsWith($"_{keyNum}"));
|
||||
|
||||
if (menuItem is not null)
|
||||
{
|
||||
await performFilter(menuItem.Tag as string);
|
||||
e.Handled = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void updateFiltersMenu(object _ = null, object __ = null)
|
||||
{
|
||||
//Clear all filters
|
||||
_viewModel.QuickFilterMenuItems.RemoveAll(_viewModel.QuickFilterMenuItems.Where(i => i.Tag is string).ToList());
|
||||
|
||||
// re-populate
|
||||
var index = 0;
|
||||
foreach (var filter in QuickFilters.Filters)
|
||||
{
|
||||
var quickFilterMenuItem = new MenuItem
|
||||
{
|
||||
Tag = filter,
|
||||
Header = $"_{++index}: {filter}"
|
||||
};
|
||||
quickFilterMenuItem.Click += async (_, __) => await performFilter(filter);
|
||||
_viewModel.QuickFilterMenuItems.Add(quickFilterMenuItem);
|
||||
}
|
||||
}
|
||||
|
||||
private void firstFilterIsDefaultToolStripMenuItem_Click(object sender, Avalonia.Interactivity.RoutedEventArgs e)
|
||||
{
|
||||
_viewModel.FirstFilterIsDefault = !_viewModel.FirstFilterIsDefault;
|
||||
}
|
||||
|
||||
private void addQuickFilterBtn_Click(object sender, Avalonia.Interactivity.RoutedEventArgs e)
|
||||
=> QuickFilters.Add(_viewModel.FilterString);
|
||||
|
||||
private async void editQuickFiltersToolStripMenuItem_Click(object sender, Avalonia.Interactivity.RoutedEventArgs e)
|
||||
=> await new Dialogs.EditQuickFilters().ShowDialog(this);
|
||||
}
|
||||
}
|
||||
@@ -1,100 +0,0 @@
|
||||
using AudibleUtilities;
|
||||
using LibationAvalonia.Dialogs;
|
||||
using System.Linq;
|
||||
|
||||
namespace LibationAvalonia.Views
|
||||
{
|
||||
public partial class MainWindow
|
||||
{
|
||||
private void Configure_RemoveBooks()
|
||||
{
|
||||
if (Avalonia.Controls.Design.IsDesignMode)
|
||||
return;
|
||||
|
||||
_viewModel.RemoveButtonsVisible = false;
|
||||
}
|
||||
|
||||
public async void openTrashBinToolStripMenuItem_Click(object sender, Avalonia.Interactivity.RoutedEventArgs e)
|
||||
{
|
||||
var trash = new TrashBinDialog();
|
||||
await trash.ShowDialog(this);
|
||||
}
|
||||
|
||||
public void removeLibraryBooksToolStripMenuItem_Click(object sender, Avalonia.Interactivity.RoutedEventArgs e)
|
||||
{
|
||||
// if 0 accounts, this will not be visible
|
||||
// if 1 account, run scanLibrariesRemovedBooks() on this account
|
||||
// if multiple accounts, another menu set will open. do not run scanLibrariesRemovedBooks()
|
||||
using var persister = AudibleApiStorage.GetAccountsSettingsPersister();
|
||||
var accounts = persister.AccountsSettings.GetAll();
|
||||
|
||||
if (accounts.Count != 1)
|
||||
return;
|
||||
|
||||
var firstAccount = accounts.Single();
|
||||
scanLibrariesRemovedBooks(firstAccount);
|
||||
}
|
||||
|
||||
// selectively remove books from all accounts
|
||||
public void removeAllAccountsToolStripMenuItem_Click(object sender, Avalonia.Interactivity.RoutedEventArgs e)
|
||||
{
|
||||
using var persister = AudibleApiStorage.GetAccountsSettingsPersister();
|
||||
var allAccounts = persister.AccountsSettings.GetAll();
|
||||
scanLibrariesRemovedBooks(allAccounts.ToArray());
|
||||
}
|
||||
|
||||
// selectively remove books from some accounts
|
||||
public async void removeSomeAccountsToolStripMenuItem_Click(object sender, Avalonia.Interactivity.RoutedEventArgs e)
|
||||
{
|
||||
var scanAccountsDialog = new Dialogs.ScanAccountsDialog();
|
||||
|
||||
if (await scanAccountsDialog.ShowDialog<DialogResult>(this) != DialogResult.OK)
|
||||
return;
|
||||
|
||||
if (!scanAccountsDialog.CheckedAccounts.Any())
|
||||
return;
|
||||
|
||||
scanLibrariesRemovedBooks(scanAccountsDialog.CheckedAccounts.ToArray());
|
||||
}
|
||||
|
||||
private async void scanLibrariesRemovedBooks(params Account[] accounts)
|
||||
{
|
||||
//This action is meant to operate on the entire library.
|
||||
//For removing books within a filter set, use
|
||||
//Visible Books > Remove from library
|
||||
|
||||
await _viewModel.ProductsDisplay.Filter(null);
|
||||
|
||||
_viewModel.RemoveBooksButtonEnabled = true;
|
||||
_viewModel.RemoveButtonsVisible = true;
|
||||
|
||||
await _viewModel.ProductsDisplay.ScanAndRemoveBooksAsync(accounts);
|
||||
}
|
||||
|
||||
public async void removeBooksBtn_Click(object sender, Avalonia.Interactivity.RoutedEventArgs e)
|
||||
{
|
||||
_viewModel.RemoveBooksButtonEnabled = false;
|
||||
await _viewModel.ProductsDisplay.RemoveCheckedBooksAsync();
|
||||
_viewModel.RemoveBooksButtonEnabled = true;
|
||||
}
|
||||
|
||||
public async void doneRemovingBtn_Click(object sender, Avalonia.Interactivity.RoutedEventArgs e)
|
||||
{
|
||||
_viewModel.RemoveButtonsVisible = false;
|
||||
|
||||
_viewModel.ProductsDisplay.DoneRemovingBooks();
|
||||
|
||||
//Restore the filter
|
||||
await performFilter(lastGoodFilter);
|
||||
}
|
||||
|
||||
public void ProductsDisplay_RemovableCountChanged(object sender, int removeCount)
|
||||
{
|
||||
_viewModel.RemoveBooksButtonText = removeCount switch
|
||||
{
|
||||
1 => "Remove 1 Book from Libation",
|
||||
_ => $"Remove {removeCount} Books from Libation"
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,90 +0,0 @@
|
||||
using ApplicationServices;
|
||||
using AudibleUtilities;
|
||||
using LibationAvalonia.Dialogs;
|
||||
using LibationFileManager;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace LibationAvalonia.Views
|
||||
{
|
||||
public partial class MainWindow
|
||||
{
|
||||
private void Configure_ScanManual()
|
||||
{
|
||||
Load += refreshImportMenu;
|
||||
AccountsSettingsPersister.Saved += refreshImportMenu;
|
||||
}
|
||||
|
||||
private void refreshImportMenu(object _, EventArgs __)
|
||||
{
|
||||
using var persister = AudibleApiStorage.GetAccountsSettingsPersister();
|
||||
_viewModel.AccountsCount = persister.AccountsSettings.Accounts.Count;
|
||||
}
|
||||
|
||||
public async void noAccountsYetAddAccountToolStripMenuItem_Click(object sender, Avalonia.Interactivity.RoutedEventArgs e)
|
||||
{
|
||||
await MessageBox.Show("To load your Audible library, come back here to the Import menu after adding your account");
|
||||
await new Dialogs.AccountsDialog().ShowDialog(this);
|
||||
}
|
||||
|
||||
public async void scanLibraryToolStripMenuItem_Click(object sender, Avalonia.Interactivity.RoutedEventArgs e)
|
||||
{
|
||||
using var persister = AudibleApiStorage.GetAccountsSettingsPersister();
|
||||
var firstAccount = persister.AccountsSettings.GetAll().FirstOrDefault();
|
||||
await scanLibrariesAsync(firstAccount);
|
||||
}
|
||||
|
||||
public async void scanLibraryOfAllAccountsToolStripMenuItem_Click(object sender, Avalonia.Interactivity.RoutedEventArgs e)
|
||||
{
|
||||
using var persister = AudibleApiStorage.GetAccountsSettingsPersister();
|
||||
var allAccounts = persister.AccountsSettings.GetAll();
|
||||
await scanLibrariesAsync(allAccounts);
|
||||
}
|
||||
|
||||
public async void scanLibraryOfSomeAccountsToolStripMenuItem_Click(object sender, Avalonia.Interactivity.RoutedEventArgs e)
|
||||
{
|
||||
var scanAccountsDialog = new Dialogs.ScanAccountsDialog();
|
||||
|
||||
if (await scanAccountsDialog.ShowDialog<DialogResult>(this) != DialogResult.OK)
|
||||
return;
|
||||
|
||||
if (!scanAccountsDialog.CheckedAccounts.Any())
|
||||
return;
|
||||
|
||||
await scanLibrariesAsync(scanAccountsDialog.CheckedAccounts);
|
||||
}
|
||||
|
||||
private async Task scanLibrariesAsync(IEnumerable<Account> accounts) => await scanLibrariesAsync(accounts.ToArray());
|
||||
private async Task scanLibrariesAsync(params Account[] accounts)
|
||||
{
|
||||
try
|
||||
{
|
||||
var (totalProcessed, newAdded) = await LibraryCommands.ImportAccountAsync(Dialogs.Login.AvaloniaLoginChoiceEager.ApiExtendedFunc, accounts);
|
||||
|
||||
// this is here instead of ScanEnd so that the following is only possible when it's user-initiated, not automatic loop
|
||||
if (Configuration.Instance.ShowImportedStats && newAdded > 0)
|
||||
await MessageBox.Show($"Total processed: {totalProcessed}\r\nNew: {newAdded}");
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
Serilog.Log.Information("Audible login attempt cancelled by user");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
await MessageBox.ShowAdminAlert(
|
||||
this,
|
||||
"Error importing library. Please try again. If this still happens after 2 or 3 tries, stop and contact administrator",
|
||||
"Error importing library",
|
||||
ex);
|
||||
}
|
||||
}
|
||||
|
||||
private async void locateAudiobooksToolStripMenuItem_Click(object sender, Avalonia.Interactivity.RoutedEventArgs e)
|
||||
{
|
||||
var locateDialog = new LocateAudiobooksDialog();
|
||||
await locateDialog.ShowDialog(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
using ApplicationServices;
|
||||
using System;
|
||||
|
||||
namespace LibationAvalonia.Views
|
||||
{
|
||||
public partial class MainWindow
|
||||
{
|
||||
private void Configure_ScanNotification()
|
||||
{
|
||||
_viewModel.NumAccountsScanning = 0;
|
||||
LibraryCommands.ScanBegin += LibraryCommands_ScanBegin;
|
||||
LibraryCommands.ScanEnd += LibraryCommands_ScanEnd;
|
||||
}
|
||||
private void LibraryCommands_ScanBegin(object sender, int accountsLength)
|
||||
{
|
||||
_viewModel.NumAccountsScanning = accountsLength;
|
||||
}
|
||||
|
||||
private void LibraryCommands_ScanEnd(object sender, int newCount)
|
||||
{
|
||||
_viewModel.NumAccountsScanning = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,34 +0,0 @@
|
||||
using LibationFileManager;
|
||||
using System;
|
||||
|
||||
namespace LibationAvalonia.Views
|
||||
{
|
||||
public partial class MainWindow
|
||||
{
|
||||
private void Configure_Settings() { }
|
||||
|
||||
public async void accountsToolStripMenuItem_Click(object sender, Avalonia.Interactivity.RoutedEventArgs e)
|
||||
=> await new Dialogs.AccountsDialog().ShowDialog(this);
|
||||
|
||||
public async void basicSettingsToolStripMenuItem_Click(object sender, Avalonia.Interactivity.RoutedEventArgs e)
|
||||
=> await new Dialogs.SettingsDialog().ShowDialog(this);
|
||||
|
||||
public async void aboutToolStripMenuItem_Click(object sender, Avalonia.Interactivity.RoutedEventArgs e)
|
||||
=> await MessageBox.Show($"Libation {AppScaffolding.LibationScaffolding.Variety}{Environment.NewLine}Version {AppScaffolding.LibationScaffolding.BuildVersion}", $"Libation v{AppScaffolding.LibationScaffolding.BuildVersion}");
|
||||
|
||||
public async void tourToolStripMenuItem_Click(object sender, Avalonia.Interactivity.RoutedEventArgs e)
|
||||
=> await new Walkthrough(this).RunAsync();
|
||||
|
||||
public void launchHangoverToolStripMenuItem_Click(object sender, Avalonia.Interactivity.RoutedEventArgs e)
|
||||
{
|
||||
try
|
||||
{
|
||||
System.Diagnostics.Process.Start("Hangover" + (Configuration.IsWindows ? ".exe" : ""));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Serilog.Log.Logger.Error(ex, "Failed to launch Hangover");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,34 +0,0 @@
|
||||
using Avalonia.Threading;
|
||||
using LibationAvalonia.Dialogs;
|
||||
using LibationUiBase;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace LibationAvalonia.Views
|
||||
{
|
||||
public partial class MainWindow
|
||||
{
|
||||
private void Configure_Upgrade()
|
||||
{
|
||||
setProgressVisible(false);
|
||||
#if !DEBUG
|
||||
async Task upgradeAvailable(UpgradeEventArgs e)
|
||||
{
|
||||
var notificationResult = await new UpgradeNotificationDialog(e.UpgradeProperties, e.CapUpgrade).ShowDialogAsync(this);
|
||||
|
||||
e.Ignore = notificationResult == DialogResult.Ignore;
|
||||
e.InstallUpgrade = notificationResult == DialogResult.OK;
|
||||
}
|
||||
|
||||
var upgrader = new Upgrader();
|
||||
upgrader.DownloadProgress += async (_, e) => await Dispatcher.UIThread.InvokeAsync(() => _viewModel.DownloadProgress = e.ProgressPercentage);
|
||||
upgrader.DownloadBegin += async (_, _) => await Dispatcher.UIThread.InvokeAsync(() => setProgressVisible(true));
|
||||
upgrader.DownloadCompleted += async (_, _) => await Dispatcher.UIThread.InvokeAsync(() => setProgressVisible(false));
|
||||
|
||||
Opened += async (_, _) => await upgrader.CheckForUpgradeAsync(upgradeAvailable);
|
||||
#endif
|
||||
}
|
||||
|
||||
private void setProgressVisible(bool visible) => _viewModel.DownloadProgress = visible ? 0 : null;
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,158 +0,0 @@
|
||||
using ApplicationServices;
|
||||
using Avalonia.Threading;
|
||||
using DataLayer;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace LibationAvalonia.Views
|
||||
{
|
||||
public partial class MainWindow
|
||||
{
|
||||
private void Configure_VisibleBooks()
|
||||
{
|
||||
LibraryCommands.BookUserDefinedItemCommitted += setLiberatedVisibleMenuItemAsync;
|
||||
}
|
||||
|
||||
private async void setLiberatedVisibleMenuItemAsync(object _, object __)
|
||||
=> await Dispatcher.UIThread.InvokeAsync(setLiberatedVisibleMenuItem);
|
||||
|
||||
public void liberateVisible(object sender, Avalonia.Interactivity.RoutedEventArgs args)
|
||||
{
|
||||
try
|
||||
{
|
||||
SetQueueCollapseState(false);
|
||||
|
||||
Serilog.Log.Logger.Information("Begin backing up visible library books");
|
||||
|
||||
_viewModel.ProcessQueue.AddDownloadDecrypt(
|
||||
_viewModel
|
||||
.ProductsDisplay
|
||||
.GetVisibleBookEntries()
|
||||
.UnLiberated()
|
||||
);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Serilog.Log.Logger.Error(ex, "An error occurred while backing up visible library books");
|
||||
}
|
||||
}
|
||||
public async void replaceTagsToolStripMenuItem_Click(object sender, Avalonia.Interactivity.RoutedEventArgs args)
|
||||
{
|
||||
var dialog = new Dialogs.TagsBatchDialog();
|
||||
var result = await dialog.ShowDialog<DialogResult>(this);
|
||||
if (result != DialogResult.OK)
|
||||
return;
|
||||
|
||||
var visibleLibraryBooks = _viewModel.ProductsDisplay.GetVisibleBookEntries();
|
||||
|
||||
var confirmationResult = await MessageBox.ShowConfirmationDialog(
|
||||
this,
|
||||
visibleLibraryBooks,
|
||||
// do not use `$` string interpolation. See impl.
|
||||
"Are you sure you want to replace tags in {0}?",
|
||||
"Replace tags?");
|
||||
|
||||
if (confirmationResult != DialogResult.Yes)
|
||||
return;
|
||||
|
||||
visibleLibraryBooks.UpdateTags(dialog.NewTags);
|
||||
}
|
||||
|
||||
public async void setBookDownloadedManualToolStripMenuItem_Click(object sender, Avalonia.Interactivity.RoutedEventArgs args)
|
||||
{
|
||||
var dialog = new Dialogs.LiberatedStatusBatchManualDialog();
|
||||
var result = await dialog.ShowDialog<DialogResult>(this);
|
||||
if (result != DialogResult.OK)
|
||||
return;
|
||||
|
||||
var visibleLibraryBooks = _viewModel.ProductsDisplay.GetVisibleBookEntries();
|
||||
|
||||
var confirmationResult = await MessageBox.ShowConfirmationDialog(
|
||||
this,
|
||||
visibleLibraryBooks,
|
||||
// do not use `$` string interpolation. See impl.
|
||||
"Are you sure you want to replace book downloaded status in {0}?",
|
||||
"Replace downloaded status?");
|
||||
|
||||
if (confirmationResult != DialogResult.Yes)
|
||||
return;
|
||||
|
||||
visibleLibraryBooks.UpdateBookStatus(dialog.BookLiberatedStatus);
|
||||
}
|
||||
|
||||
public async void setPdfDownloadedManualToolStripMenuItem_Click(object sender, Avalonia.Interactivity.RoutedEventArgs args)
|
||||
{
|
||||
var dialog = new Dialogs.LiberatedStatusBatchManualDialog(isPdf: true);
|
||||
var result = await dialog.ShowDialog<DialogResult>(this);
|
||||
if (result != DialogResult.OK)
|
||||
return;
|
||||
|
||||
var visibleLibraryBooks = _viewModel.ProductsDisplay.GetVisibleBookEntries();
|
||||
|
||||
var confirmationResult = await MessageBox.ShowConfirmationDialog(
|
||||
this,
|
||||
visibleLibraryBooks,
|
||||
// do not use `$` string interpolation. See impl.
|
||||
"Are you sure you want to replace PDF downloaded status in {0}?",
|
||||
"Replace downloaded status?");
|
||||
|
||||
if (confirmationResult != DialogResult.Yes)
|
||||
return;
|
||||
|
||||
visibleLibraryBooks.UpdatePdfStatus(dialog.BookLiberatedStatus);
|
||||
}
|
||||
|
||||
public async void setDownloadedAutoToolStripMenuItem_Click(object sender, Avalonia.Interactivity.RoutedEventArgs args)
|
||||
{
|
||||
var dialog = new Dialogs.LiberatedStatusBatchAutoDialog();
|
||||
var result = await dialog.ShowDialog<DialogResult>(this);
|
||||
if (result != DialogResult.OK)
|
||||
return;
|
||||
|
||||
var bulkSetStatus = new BulkSetDownloadStatus(_viewModel.ProductsDisplay.GetVisibleBookEntries(), dialog.SetDownloaded, dialog.SetNotDownloaded);
|
||||
var count = await Task.Run(bulkSetStatus.Discover);
|
||||
|
||||
if (count == 0)
|
||||
return;
|
||||
|
||||
var confirmationResult = await MessageBox.Show(
|
||||
bulkSetStatus.AggregateMessage,
|
||||
"Replace downloaded status?",
|
||||
MessageBoxButtons.YesNo,
|
||||
MessageBoxIcon.Question,
|
||||
MessageBoxDefaultButton.Button1);
|
||||
|
||||
if (confirmationResult != DialogResult.Yes)
|
||||
return;
|
||||
|
||||
bulkSetStatus.Execute();
|
||||
}
|
||||
|
||||
public async void removeToolStripMenuItem_Click(object sender, Avalonia.Interactivity.RoutedEventArgs args)
|
||||
{
|
||||
var visibleLibraryBooks = _viewModel.ProductsDisplay.GetVisibleBookEntries();
|
||||
|
||||
var confirmationResult = await MessageBox.ShowConfirmationDialog(
|
||||
this,
|
||||
visibleLibraryBooks,
|
||||
// do not use `$` string interpolation. See impl.
|
||||
"Are you sure you want to remove {0} from Libation's library?",
|
||||
"Remove books from Libation?",
|
||||
MessageBoxDefaultButton.Button2);
|
||||
|
||||
if (confirmationResult is DialogResult.Yes)
|
||||
await visibleLibraryBooks.RemoveBooksAsync();
|
||||
}
|
||||
public async void ProductsDisplay_VisibleCountChanged(object sender, int qty)
|
||||
{
|
||||
_viewModel.VisibleCount = qty;
|
||||
|
||||
await Dispatcher.UIThread.InvokeAsync(setLiberatedVisibleMenuItem);
|
||||
}
|
||||
void setLiberatedVisibleMenuItem()
|
||||
{
|
||||
var libraryStats = LibraryCommands.GetCounts(_viewModel.ProductsDisplay.GetVisibleBookEntries());
|
||||
_viewModel.VisibleNotLiberated = libraryStats.PendingBooks;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -9,15 +9,77 @@
|
||||
mc:Ignorable="d" d:DesignWidth="1850" d:DesignHeight="700"
|
||||
x:Class="LibationAvalonia.Views.MainWindow"
|
||||
Title="Libation: Liberate your Library"
|
||||
x:DataType="vm:MainVM"
|
||||
Name="Form1"
|
||||
Icon="/Assets/libation.ico">
|
||||
|
||||
<NativeMenu.Menu>
|
||||
<NativeMenu>
|
||||
<NativeMenuItem Header="Import">
|
||||
<NativeMenu>
|
||||
<NativeMenuItem
|
||||
Header="Auto Scan Library"
|
||||
Command="{CompiledBinding ToggleAutoScan}"
|
||||
IsChecked="{CompiledBinding AutoScanChecked}"
|
||||
ToggleType="CheckBox" />
|
||||
<NativeMenuItemSeparator />
|
||||
</NativeMenu>
|
||||
</NativeMenuItem>
|
||||
<NativeMenuItem Header="Liberate">
|
||||
<NativeMenu>
|
||||
<NativeMenuItem Command="{CompiledBinding BackupAllBooks}" Header="{CompiledBinding BookBackupsToolStripText}" Gesture="{OnPlatform macOS='alt+⌘+B'}" />
|
||||
<NativeMenuItem Command="{CompiledBinding BackupAllPdfs}" Header="{CompiledBinding PdfBackupsToolStripText}" Gesture="{OnPlatform macOS='alt+⌘+P'}"/>
|
||||
<NativeMenuItem Command="{CompiledBinding ConvertAllToMp3Async}" Header="Convert all M4b to Mp3 [Long-running]..." />
|
||||
<NativeMenuItem Command="{CompiledBinding LiberateVisible}" Header="{CompiledBinding LiberateVisibleToolStripText}" IsEnabled="{CompiledBinding AnyVisibleNotLiberated}" />
|
||||
</NativeMenu>
|
||||
</NativeMenuItem>
|
||||
<NativeMenuItem Header="Export">
|
||||
<NativeMenu>
|
||||
<NativeMenuItem Command="{CompiledBinding ExportLibraryAsync}" Header="Export Library" Gesture="{OnPlatform macOS='alt+⌘+X'}"/>
|
||||
</NativeMenu>
|
||||
</NativeMenuItem>
|
||||
<NativeMenuItem Header="Quick Filters">
|
||||
<NativeMenu>
|
||||
<NativeMenuItem
|
||||
Header="Start Libation with 1st filter Default"
|
||||
Command="{CompiledBinding ToggleFirstFilterIsDefault}"
|
||||
IsChecked="{CompiledBinding FirstFilterIsDefault}"
|
||||
ToggleType="CheckBox" />
|
||||
<NativeMenuItem Command="{CompiledBinding EditQuickFiltersAsync}" Header="Edit quick filters..." Gesture="{OnPlatform macOS='alt+⌘+Q'}" />
|
||||
<NativeMenuItemSeparator />
|
||||
</NativeMenu>
|
||||
</NativeMenuItem>
|
||||
<NativeMenuItem Header="Visible Books">
|
||||
<NativeMenu>
|
||||
<NativeMenuItem Command="{CompiledBinding LiberateVisible}" Header="{CompiledBinding LiberateVisibleToolStripText_2}" IsEnabled="{CompiledBinding AnyVisibleNotLiberated}" Gesture="{OnPlatform macOS='alt+⌘+V'}" />
|
||||
<NativeMenuItem Command="{CompiledBinding ReplaceTagsAsync}" Header="Replace Tags..." />
|
||||
<NativeMenuItem Command="{CompiledBinding SetBookDownloadedAsync}" Header="Set book 'Downloaded' status manually..." />
|
||||
<NativeMenuItem Command="{CompiledBinding SetPdfDownloadedAsync}" Header="Set PDF 'Downloaded' status manually..." />
|
||||
<NativeMenuItem Command="{CompiledBinding SetDownloadedAutoAsync}" Header="Set 'Downloaded' status automatically..." />
|
||||
<NativeMenuItem Command="{CompiledBinding RemoveVisibleAsync}" Header="Remove from library..." />
|
||||
</NativeMenu>
|
||||
</NativeMenuItem>
|
||||
<NativeMenuItem Header="Settings">
|
||||
<NativeMenu>
|
||||
<NativeMenuItem Command="{CompiledBinding ShowAccountsAsync}" Header="Accounts..." Gesture="{OnPlatform macOS='⌘+.'}" />
|
||||
<NativeMenuItem Command="{CompiledBinding ShowSettingsAsync}" Header="Settings..." Gesture="{OnPlatform macOS='⌘+,'}" />
|
||||
<NativeMenuItemSeparator />
|
||||
<NativeMenuItem Command="{CompiledBinding ShowTrashBinAsync}" Header="Trash Bin" Gesture="{OnPlatform macOS='alt+⌘+T'}" />
|
||||
<NativeMenuItem Command="{CompiledBinding LaunchHangover}" Header="Launch Hangover" />
|
||||
<NativeMenuItemSeparator />
|
||||
<NativeMenuItem Command="{CompiledBinding StartWalkthroughAsync}" Header="Take a Guided Tour of Libation" />
|
||||
<NativeMenuItem Command="{CompiledBinding ShowAboutAsync}" Header="About..." />
|
||||
</NativeMenu>
|
||||
</NativeMenuItem>
|
||||
</NativeMenu>
|
||||
</NativeMenu.Menu>
|
||||
|
||||
<Border BorderBrush="{DynamicResource DataGridGridLinesBrush}" BorderThickness="2" Padding="10,0,10,10">
|
||||
<Grid RowDefinitions="Auto,Auto,*,Auto">
|
||||
<Grid Grid.Row="0" ColumnDefinitions="1*,Auto">
|
||||
|
||||
<!-- Menu Strip -->
|
||||
<Menu Grid.Column="0" VerticalAlignment="Top">
|
||||
<Menu Grid.Column="0" VerticalAlignment="Top" IsVisible="{CompiledBinding MenuBarVisible}">
|
||||
<!-- Decrease height of menu strop -->
|
||||
|
||||
<Menu.Styles>
|
||||
@@ -35,26 +97,27 @@
|
||||
<Setter Property="Height" Value="NaN"/>
|
||||
</Style>
|
||||
</MenuItem.Styles>
|
||||
<MenuItem IsVisible="{Binding AnyAccounts}" Click="autoScanLibraryToolStripMenuItem_Click" Header="A_uto Scan Library">
|
||||
<MenuItem IsVisible="{CompiledBinding AnyAccounts}" Command="{CompiledBinding ToggleAutoScan}" Header="A_uto Scan Library">
|
||||
<MenuItem.Icon>
|
||||
<CheckBox BorderThickness="0" IsChecked="{Binding AutoScanChecked, Mode=TwoWay}" IsHitTestVisible="False" />
|
||||
<CheckBox BorderThickness="0" IsChecked="{CompiledBinding AutoScanChecked, Mode=TwoWay}" IsHitTestVisible="False" />
|
||||
</MenuItem.Icon>
|
||||
</MenuItem>
|
||||
<MenuItem IsVisible="{Binding !AnyAccounts}" Click="noAccountsYetAddAccountToolStripMenuItem_Click" Header="No accounts yet. A_dd Account..." />
|
||||
<MenuItem IsVisible="{CompiledBinding !AnyAccounts}" Command="{CompiledBinding AddAccountsAsync}" Header="No accounts yet. A_dd Account..." />
|
||||
|
||||
<!-- Scan Library -->
|
||||
<MenuItem IsVisible="{Binding OneAccount}" IsEnabled="{Binding !ActivelyScanning}" Name="scanLibraryToolStripMenuItem" Click="scanLibraryToolStripMenuItem_Click" Header="Scan _Library" />
|
||||
<MenuItem IsVisible="{Binding MultipleAccounts}" IsEnabled="{Binding !ActivelyScanning}" Name="scanLibraryOfAllAccountsToolStripMenuItem" Click="scanLibraryOfAllAccountsToolStripMenuItem_Click" Header="Scan Library of _All Accounts" />
|
||||
<MenuItem IsVisible="{Binding MultipleAccounts}" IsEnabled="{Binding !ActivelyScanning}" Click="scanLibraryOfSomeAccountsToolStripMenuItem_Click" Header="Scan Library of _Some Accounts" />
|
||||
<Separator />
|
||||
<MenuItem IsVisible="{CompiledBinding OneAccount}" IsEnabled="{CompiledBinding !ActivelyScanning}" Name="scanLibraryToolStripMenuItem" Command="{CompiledBinding ScanAccountAsync}" Header="Scan _Library" />
|
||||
<MenuItem IsVisible="{CompiledBinding MultipleAccounts}" IsEnabled="{CompiledBinding !ActivelyScanning}" Name="scanLibraryOfAllAccountsToolStripMenuItem" Command="{CompiledBinding ScanAllAccountsAsync}" Header="Scan Library of _All Accounts" />
|
||||
<MenuItem IsVisible="{CompiledBinding MultipleAccounts}" IsEnabled="{CompiledBinding !ActivelyScanning}" Command="{CompiledBinding ScanSomeAccountsAsync}" Header="Scan Library of _Some Accounts" />
|
||||
|
||||
<Separator IsVisible="{CompiledBinding AnyAccounts}" />
|
||||
|
||||
<!-- Remove Books -->
|
||||
<MenuItem IsVisible="{Binding OneAccount}" IsEnabled="{Binding RemoveMenuItemsEnabled}" Click="removeLibraryBooksToolStripMenuItem_Click" Header="_Remove Library Books" />
|
||||
<MenuItem IsVisible="{Binding MultipleAccounts}" IsEnabled="{Binding RemoveMenuItemsEnabled}" Click="removeAllAccountsToolStripMenuItem_Click" Header="_Remove Books from All Accounts" />
|
||||
<MenuItem IsVisible="{Binding MultipleAccounts}" IsEnabled="{Binding RemoveMenuItemsEnabled}" Click="removeSomeAccountsToolStripMenuItem_Click" Header="_Remove Books from Some Accounts" />
|
||||
<MenuItem IsVisible="{CompiledBinding OneAccount}" IsEnabled="{CompiledBinding RemoveMenuItemsEnabled}" Command="{CompiledBinding RemoveBooksAsync}" Header="_Remove Library Books" />
|
||||
<MenuItem IsVisible="{CompiledBinding MultipleAccounts}" IsEnabled="{CompiledBinding RemoveMenuItemsEnabled}" Command="{CompiledBinding RemoveBooksAllAsync}" Header="_Remove Books from All Accounts" />
|
||||
<MenuItem IsVisible="{CompiledBinding MultipleAccounts}" IsEnabled="{CompiledBinding RemoveMenuItemsEnabled}" Command="{CompiledBinding RemoveBooksSomeAsync}" Header="_Remove Books from Some Accounts" />
|
||||
|
||||
<Separator />
|
||||
<MenuItem Click="locateAudiobooksToolStripMenuItem_Click" Header="L_ocate Audiobooks" />
|
||||
<MenuItem Command="{CompiledBinding LocateAudiobooksAsync}" Header="L_ocate Audiobooks..." />
|
||||
|
||||
</MenuItem>
|
||||
|
||||
@@ -67,10 +130,10 @@
|
||||
<Setter Property="Height" Value="NaN"/>
|
||||
</Style>
|
||||
</MenuItem.Styles>
|
||||
<MenuItem Click="beginBookBackupsToolStripMenuItem_Click" Header="{Binding BookBackupsToolStripText}" />
|
||||
<MenuItem Click="beginPdfBackupsToolStripMenuItem_Click" Header="{Binding PdfBackupsToolStripText}" />
|
||||
<MenuItem Click="convertAllM4bToMp3ToolStripMenuItem_Click" Header="Convert all _M4b to Mp3 [Long-running]..." IsVisible="{Binding IsMp3Supported}" />
|
||||
<MenuItem Click="liberateVisible" Header="{Binding LiberateVisibleToolStripText}" IsEnabled="{Binding AnyVisibleNotLiberated}" />
|
||||
<MenuItem Command="{CompiledBinding BackupAllBooks}" Header="{CompiledBinding BookBackupsToolStripText}" />
|
||||
<MenuItem Command="{CompiledBinding BackupAllPdfs}" Header="{CompiledBinding PdfBackupsToolStripText}" />
|
||||
<MenuItem Command="{CompiledBinding ConvertAllToMp3Async}" Header="Convert all _M4b to Mp3 [Long-running]..." />
|
||||
<MenuItem Command="{CompiledBinding LiberateVisible}" Header="{CompiledBinding LiberateVisibleToolStripText}" IsEnabled="{CompiledBinding AnyVisibleNotLiberated}" />
|
||||
</MenuItem>
|
||||
|
||||
<!-- Export Menu -->
|
||||
@@ -82,12 +145,12 @@
|
||||
<Setter Property="Height" Value="NaN"/>
|
||||
</Style>
|
||||
</MenuItem.Styles>
|
||||
<MenuItem IsEnabled="{Binding LibraryStats.HasBookResults}" Click="exportLibraryToolStripMenuItem_Click" Header="E_xport Library" />
|
||||
<MenuItem IsEnabled="{CompiledBinding LibraryStats.HasBookResults}" Command="{CompiledBinding ExportLibraryAsync}" Header="E_xport Library" InputGesture="ctrl+S" />
|
||||
</MenuItem>
|
||||
|
||||
<!-- Quick Filters Menu -->
|
||||
|
||||
<MenuItem Name="quickFiltersToolStripMenuItem" Header="Quick _Filters" ItemsSource="{Binding QuickFilterMenuItems}" KeyDown="QuickFiltersMenuItem_KeyDown">
|
||||
<MenuItem Name="quickFiltersToolStripMenuItem" Header="Quick _Filters" ItemsSource="{CompiledBinding QuickFilterMenuItems}">
|
||||
<!-- Remove height style property for menu item -->
|
||||
<MenuItem.Styles>
|
||||
<Style Selector="ItemsPresenter#PART_ItemsPresenter">
|
||||
@@ -98,19 +161,19 @@
|
||||
|
||||
<!-- Visible Books Menu -->
|
||||
|
||||
<MenuItem Header="{Binding VisibleCountMenuItemText}" >
|
||||
<MenuItem Header="{CompiledBinding VisibleCountMenuItemText}" >
|
||||
<!-- Remove height style property for menu item -->
|
||||
<MenuItem.Styles>
|
||||
<Style Selector="ItemsPresenter#PART_ItemsPresenter">
|
||||
<Setter Property="Height" Value="NaN"/>
|
||||
</Style>
|
||||
</MenuItem.Styles>
|
||||
<MenuItem Click="liberateVisible" Header="{Binding LiberateVisibleToolStripText_2}" IsEnabled="{Binding AnyVisibleNotLiberated}" />
|
||||
<MenuItem Click="replaceTagsToolStripMenuItem_Click" Header="Replace _Tags..." />
|
||||
<MenuItem Click="setBookDownloadedManualToolStripMenuItem_Click" Header="Set book '_Downloaded' status manually..." />
|
||||
<MenuItem Click="setPdfDownloadedManualToolStripMenuItem_Click" Header="Set _PDF 'Downloaded' status manually..." />
|
||||
<MenuItem Click="setDownloadedAutoToolStripMenuItem_Click" Header="Set '_Downloaded' status automatically..." />
|
||||
<MenuItem Click="removeToolStripMenuItem_Click" Header="_Remove from library..." />
|
||||
<MenuItem Command="{CompiledBinding LiberateVisible}" Header="{CompiledBinding LiberateVisibleToolStripText_2}" IsEnabled="{CompiledBinding AnyVisibleNotLiberated}" />
|
||||
<MenuItem Command="{CompiledBinding ReplaceTagsAsync}" Header="Replace _Tags..." />
|
||||
<MenuItem Command="{CompiledBinding SetBookDownloadedAsync}" Header="Set book '_Downloaded' status manually..." />
|
||||
<MenuItem Command="{CompiledBinding SetPdfDownloadedAsync}" Header="Set _PDF 'Downloaded' status manually..." />
|
||||
<MenuItem Command="{CompiledBinding SetDownloadedAutoAsync}" Header="Set '_Downloaded' status automatically..." />
|
||||
<MenuItem Command="{CompiledBinding RemoveVisibleAsync}" Header="_Remove from library..." />
|
||||
</MenuItem>
|
||||
|
||||
<!-- Settings Menu -->
|
||||
@@ -122,19 +185,19 @@
|
||||
<Setter Property="Height" Value="NaN"/>
|
||||
</Style>
|
||||
</MenuItem.Styles>
|
||||
<MenuItem Name="accountsToolStripMenuItem" Click="accountsToolStripMenuItem_Click" Header="_Accounts..." />
|
||||
<MenuItem Name="basicSettingsToolStripMenuItem" Click="basicSettingsToolStripMenuItem_Click" Header="_Settings..." />
|
||||
<MenuItem Name="accountsToolStripMenuItem" Command="{CompiledBinding ShowAccountsAsync}" Header="_Accounts..." InputGesture="ctrl+shift+A"/>
|
||||
<MenuItem Name="basicSettingsToolStripMenuItem" Command="{CompiledBinding ShowSettingsAsync}" Header="_Settings..." InputGesture="ctrl+P" />
|
||||
<Separator />
|
||||
<MenuItem Click="openTrashBinToolStripMenuItem_Click" Header="Trash Bin" />
|
||||
<MenuItem Click="launchHangoverToolStripMenuItem_Click" Header="Launch _Hangover" />
|
||||
<MenuItem Command="{CompiledBinding ShowTrashBinAsync}" Header="Trash Bin" />
|
||||
<MenuItem Command="{CompiledBinding LaunchHangover}" Header="Launch _Hangover" />
|
||||
<Separator />
|
||||
<MenuItem Click="tourToolStripMenuItem_Click" Header="Take a Guided _Tour of Libation" />
|
||||
<MenuItem Click="aboutToolStripMenuItem_Click" Header="A_bout..." />
|
||||
<MenuItem Command="{CompiledBinding StartWalkthroughAsync}" Header="Take a Guided _Tour of Libation" />
|
||||
<MenuItem Command="{CompiledBinding ShowAboutAsync}" Header="A_bout..." />
|
||||
</MenuItem>
|
||||
</Menu>
|
||||
<StackPanel IsVisible="{Binding ActivelyScanning}" Grid.Column="1" Orientation="Horizontal" HorizontalAlignment="Right">
|
||||
<StackPanel IsVisible="{CompiledBinding ActivelyScanning}" Grid.Column="1" Orientation="Horizontal" HorizontalAlignment="Right">
|
||||
<Path VerticalAlignment="Center" Fill="{StaticResource IconFill}" Data="{StaticResource ImportIcon}" />
|
||||
<TextBlock Margin="5,0,5,0" VerticalAlignment="Center" Text="{Binding ScanningText}"/>
|
||||
<TextBlock Margin="5,0,5,0" VerticalAlignment="Center" Text="{CompiledBinding ScanningText}"/>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
|
||||
@@ -155,23 +218,23 @@
|
||||
</Grid.Styles>
|
||||
|
||||
<StackPanel Grid.Column="0" Orientation="Horizontal">
|
||||
<Button Name="filterHelpBtn" Margin="0" Click="filterHelpBtn_Click" Content="?"/>
|
||||
<Button Name="addQuickFilterBtn" Click="addQuickFilterBtn_Click" Content="Add To Quick Filters"/>
|
||||
<Button Name="filterHelpBtn" Margin="0" Command="{CompiledBinding FilterHelpBtn}" Content="?"/>
|
||||
<Button Name="addQuickFilterBtn" Command="{CompiledBinding AddQuickFilterBtn}" Content="Add To Quick Filters"/>
|
||||
</StackPanel>
|
||||
|
||||
<StackPanel Grid.Column="1" Orientation="Horizontal">
|
||||
<Button IsVisible="{Binding RemoveButtonsVisible}" IsEnabled="{Binding RemoveBooksButtonEnabled}" Click="removeBooksBtn_Click" Content="{Binding RemoveBooksButtonText}"/>
|
||||
<Button IsVisible="{Binding RemoveButtonsVisible}" Click="doneRemovingBtn_Click" Content="Done Removing Books"/>
|
||||
<Button IsVisible="{CompiledBinding RemoveButtonsVisible}" IsEnabled="{CompiledBinding RemoveBooksButtonEnabled}" Command="{CompiledBinding RemoveBooksBtn}" Content="{CompiledBinding RemoveBooksButtonText}"/>
|
||||
<Button IsVisible="{CompiledBinding RemoveButtonsVisible}" Command="{CompiledBinding DoneRemovingBtn}" Content="Done Removing Books"/>
|
||||
</StackPanel>
|
||||
|
||||
<TextBox Grid.Column="1" Margin="10,0,0,0" Name="filterSearchTb" IsVisible="{Binding !RemoveButtonsVisible}" Text="{Binding FilterString, Mode=TwoWay}" KeyDown="filterSearchTb_KeyPress" />
|
||||
<TextBox Grid.Column="1" Margin="10,0,0,0" Name="filterSearchTb" IsVisible="{CompiledBinding !RemoveButtonsVisible}" Text="{CompiledBinding FilterString, Mode=TwoWay}" KeyDown="filterSearchTb_KeyPress" />
|
||||
|
||||
<StackPanel Grid.Column="2" Height="30" Orientation="Horizontal">
|
||||
<Button Name="filterBtn" Click="filterBtn_Click" VerticalAlignment="Stretch" HorizontalContentAlignment="Center" VerticalContentAlignment="Center" Content="Filter"/>
|
||||
<Button Padding="2,6,2,6" VerticalAlignment="Stretch" Click="ToggleQueueHideBtn_Click">
|
||||
<Button Name="filterBtn" Command="{CompiledBinding FilterBtn}" VerticalAlignment="Stretch" HorizontalContentAlignment="Center" VerticalContentAlignment="Center" Content="Filter"/>
|
||||
<Button Padding="2,6,2,6" VerticalAlignment="Stretch" Command="{CompiledBinding ToggleQueueHideBtn}">
|
||||
<Path Stretch="Uniform" Fill="{DynamicResource IconFill}" Data="{StaticResource LeftArrows}">
|
||||
<Path.RenderTransform>
|
||||
<RotateTransform Angle="{Binding QueueButtonAngle}"/>
|
||||
<RotateTransform Angle="{CompiledBinding QueueButtonAngle}"/>
|
||||
</Path.RenderTransform>
|
||||
</Path>
|
||||
</Button>
|
||||
@@ -179,17 +242,17 @@
|
||||
|
||||
</Grid>
|
||||
<Border Grid.Row="2" BorderThickness="1" BorderBrush="{DynamicResource DataGridGridLinesBrush}">
|
||||
<SplitView IsPaneOpen="{Binding QueueOpen}" DisplayMode="Inline" OpenPaneLength="400" MinWidth="400" PanePlacement="Right">
|
||||
<SplitView IsPaneOpen="{CompiledBinding QueueOpen}" DisplayMode="Inline" OpenPaneLength="400" MinWidth="400" PanePlacement="Right">
|
||||
|
||||
<!-- Process Queue -->
|
||||
<SplitView.Pane>
|
||||
<views:ProcessQueueControl DataContext="{Binding ProcessQueue}"/>
|
||||
<views:ProcessQueueControl DataContext="{CompiledBinding ProcessQueue}"/>
|
||||
</SplitView.Pane>
|
||||
|
||||
<!-- Product Display Grid -->
|
||||
<views:ProductsDisplay
|
||||
Name="productsDisplay"
|
||||
DataContext="{Binding ProductsDisplay}"
|
||||
DataContext="{CompiledBinding ProductsDisplay}"
|
||||
LiberateClicked="ProductsDisplay_LiberateClicked"
|
||||
LiberateSeriesClicked="ProductsDisplay_LiberateSeriesClicked"
|
||||
ConvertToMp3Clicked="ProductsDisplay_ConvertToMp3Clicked" />
|
||||
@@ -203,10 +266,10 @@
|
||||
<Setter Property="MinWidth" Value="100" />
|
||||
</Style>
|
||||
</Grid.Styles>
|
||||
<TextBlock FontSize="14" Grid.Column="0" Text="Upgrading:" VerticalAlignment="Center" IsVisible="{Binding DownloadProgress, Converter={x:Static ObjectConverters.IsNotNull}}" />
|
||||
<ProgressBar Grid.Column="1" Margin="5,0,10,0" VerticalAlignment="Stretch" Width="100" Value="{Binding DownloadProgress}" IsVisible="{Binding DownloadProgress, Converter={x:Static ObjectConverters.IsNotNull}}"/>
|
||||
<TextBlock FontSize="14" Grid.Column="2" Text="{Binding VisibleCountText}" VerticalAlignment="Center" />
|
||||
<TextBlock FontSize="14" Grid.Column="3" Text="{Binding LibraryStats.StatusString}" VerticalAlignment="Center" />
|
||||
<TextBlock FontSize="14" Grid.Column="0" Text="Upgrading:" VerticalAlignment="Center" IsVisible="{CompiledBinding DownloadProgress, Converter={x:Static ObjectConverters.IsNotNull}}" />
|
||||
<ProgressBar Grid.Column="1" Margin="5,0,10,0" VerticalAlignment="Stretch" Width="100" Value="{CompiledBinding DownloadProgress}" IsVisible="{CompiledBinding DownloadProgress, Converter={x:Static ObjectConverters.IsNotNull}}"/>
|
||||
<TextBlock FontSize="14" Grid.Column="2" Text="{CompiledBinding VisibleCountText}" VerticalAlignment="Center" />
|
||||
<TextBlock FontSize="14" Grid.Column="3" Text="{CompiledBinding LibraryStats.StatusString}" VerticalAlignment="Center" />
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Border>
|
||||
|
||||
@@ -1,58 +1,41 @@
|
||||
using ApplicationServices;
|
||||
using Avalonia.Input;
|
||||
using Avalonia.ReactiveUI;
|
||||
using DataLayer;
|
||||
using LibationAvalonia.ViewModels;
|
||||
using LibationFileManager;
|
||||
using LibationUiBase.GridView;
|
||||
using ReactiveUI;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace LibationAvalonia.Views
|
||||
{
|
||||
public partial class MainWindow : ReactiveWindow<MainWindowViewModel>
|
||||
public partial class MainWindow : ReactiveWindow<MainVM>
|
||||
{
|
||||
public event EventHandler Load;
|
||||
public event EventHandler<List<LibraryBook>> LibraryLoaded;
|
||||
private readonly MainWindowViewModel _viewModel;
|
||||
|
||||
public MainWindow()
|
||||
{
|
||||
this.DataContext = _viewModel = new MainWindowViewModel();
|
||||
DataContext = new MainVM(this);
|
||||
|
||||
InitializeComponent();
|
||||
|
||||
// eg: if one of these init'd productsGrid, then another can't reliably subscribe to it
|
||||
Configure_BackupCounts();
|
||||
Configure_ScanAuto();
|
||||
Configure_ScanNotification();
|
||||
Configure_VisibleBooks();
|
||||
Configure_QuickFilters();
|
||||
Configure_ScanManual();
|
||||
Configure_RemoveBooks();
|
||||
Configure_Liberate();
|
||||
Configure_Export();
|
||||
Configure_Settings();
|
||||
Configure_ProcessQueue();
|
||||
Configure_Upgrade();
|
||||
Configure_Filter();
|
||||
// misc which belongs in winforms app but doesn't have a UI element
|
||||
Configure_NonUI();
|
||||
|
||||
_viewModel.ProductsDisplay.RemovableCountChanged += ProductsDisplay_RemovableCountChanged;
|
||||
_viewModel.ProductsDisplay.VisibleCountChanged += ProductsDisplay_VisibleCountChanged;
|
||||
|
||||
{
|
||||
this.LibraryLoaded += MainWindow_LibraryLoaded;
|
||||
|
||||
LibraryCommands.LibrarySizeChanged += async (_, _) => await _viewModel.ProductsDisplay.UpdateGridAsync(DbContexts.GetLibrary_Flat_NoTracking(includeParents: true));
|
||||
Closing += (_, _) => this.SaveSizeAndLocation(Configuration.Instance);
|
||||
}
|
||||
Loaded += MainWindow_Loaded;
|
||||
Closing += MainWindow_Closing;
|
||||
LibraryLoaded += MainWindow_LibraryLoaded;
|
||||
|
||||
Opened += MainWindow_Opened;
|
||||
KeyBindings.Add(new KeyBinding { Command = ReactiveCommand.Create(selectAndFocusSearchBox), Gesture = new KeyGesture(Key.F, Configuration.IsMacOs ? KeyModifiers.Meta : KeyModifiers.Control) });
|
||||
|
||||
if (!Configuration.IsMacOs)
|
||||
{
|
||||
KeyBindings.Add(new KeyBinding { Command = ReactiveCommand.Create(ViewModel.ShowSettingsAsync), Gesture = new KeyGesture(Key.P, KeyModifiers.Control) });
|
||||
KeyBindings.Add(new KeyBinding { Command = ReactiveCommand.Create(ViewModel.ShowAccountsAsync), Gesture = new KeyGesture(Key.A, KeyModifiers.Control | KeyModifiers.Shift) });
|
||||
KeyBindings.Add(new KeyBinding { Command = ReactiveCommand.Create(ViewModel.ExportLibraryAsync), Gesture = new KeyGesture(Key.S, KeyModifiers.Control) });
|
||||
}
|
||||
}
|
||||
|
||||
private async void MainWindow_Opened(object sender, EventArgs e)
|
||||
private async void MainWindow_Loaded(object sender, EventArgs e)
|
||||
{
|
||||
if (Configuration.Instance.FirstLaunch)
|
||||
{
|
||||
@@ -66,21 +49,64 @@ namespace LibationAvalonia.Views
|
||||
Configuration.Instance.FirstLaunch = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void MainWindow_Closing(object sender, System.ComponentModel.CancelEventArgs e)
|
||||
{
|
||||
productsDisplay?.CloseImageDisplay();
|
||||
this.SaveSizeAndLocation(Configuration.Instance);
|
||||
}
|
||||
|
||||
private async void MainWindow_LibraryLoaded(object sender, List<LibraryBook> dbBooks)
|
||||
{
|
||||
if (QuickFilters.UseDefault)
|
||||
await performFilter(QuickFilters.Filters.FirstOrDefault());
|
||||
await ViewModel.PerformFilter(QuickFilters.Filters.FirstOrDefault());
|
||||
|
||||
_viewModel.ProductsDisplay.BindToGrid(dbBooks);
|
||||
ViewModel.ProductsDisplay.BindToGrid(dbBooks);
|
||||
}
|
||||
|
||||
private void selectAndFocusSearchBox()
|
||||
{
|
||||
filterSearchTb.SelectAll();
|
||||
filterSearchTb.Focus();
|
||||
}
|
||||
|
||||
public void OnLoad() => Load?.Invoke(this, EventArgs.Empty);
|
||||
public void OnLibraryLoaded(List<LibraryBook> initialLibrary) => LibraryLoaded?.Invoke(this, initialLibrary);
|
||||
public void ProductsDisplay_LiberateClicked(object _, LibraryBook libraryBook) => ViewModel.LiberateClicked(libraryBook);
|
||||
public void ProductsDisplay_LiberateSeriesClicked(object _, ISeriesEntry series) => ViewModel.LiberateSeriesClicked(series);
|
||||
public void ProductsDisplay_ConvertToMp3Clicked(object _, LibraryBook libraryBook) => ViewModel.ConvertToMp3Clicked(libraryBook);
|
||||
|
||||
public async void filterSearchTb_KeyPress(object _, KeyEventArgs e)
|
||||
{
|
||||
if (e.Key == Key.Return)
|
||||
{
|
||||
await ViewModel.PerformFilter(ViewModel.FilterString);
|
||||
|
||||
// silence the 'ding'
|
||||
e.Handled = true;
|
||||
}
|
||||
}
|
||||
|
||||
private void Configure_Upgrade()
|
||||
{
|
||||
setProgressVisible(false);
|
||||
#if !DEBUG
|
||||
async System.Threading.Tasks.Task upgradeAvailable(LibationUiBase.UpgradeEventArgs e)
|
||||
{
|
||||
var notificationResult = await new Dialogs.UpgradeNotificationDialog(e.UpgradeProperties, e.CapUpgrade).ShowDialogAsync(this);
|
||||
|
||||
e.Ignore = notificationResult == DialogResult.Ignore;
|
||||
e.InstallUpgrade = notificationResult == DialogResult.OK;
|
||||
}
|
||||
|
||||
var upgrader = new LibationUiBase.Upgrader();
|
||||
upgrader.DownloadProgress += async (_, e) => await Avalonia.Threading.Dispatcher.UIThread.InvokeAsync(() => ViewModel.DownloadProgress = e.ProgressPercentage);
|
||||
upgrader.DownloadBegin += async (_, _) => await Avalonia.Threading.Dispatcher.UIThread.InvokeAsync(() => setProgressVisible(true));
|
||||
upgrader.DownloadCompleted += async (_, _) => await Avalonia.Threading.Dispatcher.UIThread.InvokeAsync(() => setProgressVisible(false));
|
||||
|
||||
Opened += async (_, _) => await upgrader.CheckForUpgradeAsync(upgradeAvailable);
|
||||
#endif
|
||||
}
|
||||
|
||||
private void setProgressVisible(bool visible) => ViewModel.DownloadProgress = visible ? 0 : null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,9 +11,6 @@
|
||||
|
||||
<UserControl.Resources>
|
||||
<views:DecimalConverter x:Key="myConverter" />
|
||||
</UserControl.Resources>
|
||||
|
||||
<UserControl.Resources>
|
||||
<RecyclePool x:Key="RecyclePool" />
|
||||
<DataTemplate x:Key="queuedBook">
|
||||
<views:ProcessBookControl />
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
Title="View All Items in Series">
|
||||
|
||||
<TabControl
|
||||
Items="{Binding TabItems}"
|
||||
ItemsSource="{Binding TabItems}"
|
||||
VerticalAlignment="Stretch"
|
||||
HorizontalAlignment="Stretch">
|
||||
<TabControl.Styles>
|
||||
|
||||
@@ -149,7 +149,7 @@ namespace LibationAvalonia
|
||||
await displayControlAsync(MainForm.importToolStripMenuItem);
|
||||
await displayControlAsync(scanItem);
|
||||
|
||||
scanItem.RaiseEvent(new RoutedEventArgs(MenuItem.ClickEvent));
|
||||
scanItem.Command.Execute(null);
|
||||
MainForm.importToolStripMenuItem.Close();
|
||||
|
||||
var tcs = new TaskCompletionSource();
|
||||
@@ -193,7 +193,7 @@ namespace LibationAvalonia
|
||||
|
||||
await displayControlAsync(MainForm.filterBtn);
|
||||
|
||||
MainForm.filterBtn.RaiseEvent(new RoutedEventArgs(Button.ClickEvent));
|
||||
MainForm.filterBtn.Command.Execute(null);
|
||||
|
||||
await Task.Delay(1000);
|
||||
|
||||
@@ -222,7 +222,7 @@ namespace LibationAvalonia
|
||||
|
||||
await Task.Delay(750);
|
||||
await displayControlAsync(MainForm.addQuickFilterBtn);
|
||||
MainForm.addQuickFilterBtn.RaiseEvent(new RoutedEventArgs(Button.ClickEvent));
|
||||
MainForm.addQuickFilterBtn.Command.Execute(null);
|
||||
await displayControlAsync(MainForm.quickFiltersToolStripMenuItem);
|
||||
await displayControlAsync(editQuickFiltersToolStripMenuItem);
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="SixLabors.ImageSharp" Version="3.0.0" />
|
||||
<PackageReference Include="SixLabors.ImageSharp" Version="3.0.1" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
331
Source/LibationWinForms/Dialogs/AboutDialog.Designer.cs
generated
Normal file
331
Source/LibationWinForms/Dialogs/AboutDialog.Designer.cs
generated
Normal file
@@ -0,0 +1,331 @@
|
||||
namespace LibationWinForms.Dialogs
|
||||
{
|
||||
partial class AboutDialog
|
||||
{
|
||||
/// <summary>
|
||||
/// Required designer variable.
|
||||
/// </summary>
|
||||
private System.ComponentModel.IContainer components = null;
|
||||
|
||||
/// <summary>
|
||||
/// Clean up any resources being used.
|
||||
/// </summary>
|
||||
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing && (components != null))
|
||||
{
|
||||
components.Dispose();
|
||||
}
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
|
||||
#region Windows Form Designer generated code
|
||||
|
||||
/// <summary>
|
||||
/// Required method for Designer support - do not modify
|
||||
/// the contents of this method with the code editor.
|
||||
/// </summary>
|
||||
private void InitializeComponent()
|
||||
{
|
||||
pictureBox1 = new System.Windows.Forms.PictureBox();
|
||||
releaseNotesLbl = new System.Windows.Forms.LinkLabel();
|
||||
checkForUpgradeBtn = new System.Windows.Forms.Button();
|
||||
getLibationLbl = new System.Windows.Forms.LinkLabel();
|
||||
rmcrackanLbl = new System.Windows.Forms.LinkLabel();
|
||||
MBucariLbl = new System.Windows.Forms.LinkLabel();
|
||||
groupBox1 = new System.Windows.Forms.GroupBox();
|
||||
label3 = new System.Windows.Forms.Label();
|
||||
label4 = new System.Windows.Forms.Label();
|
||||
label2 = new System.Windows.Forms.Label();
|
||||
label1 = new System.Windows.Forms.Label();
|
||||
flowLayoutPanel1 = new System.Windows.Forms.FlowLayoutPanel();
|
||||
linkLabel4 = new System.Windows.Forms.LinkLabel();
|
||||
linkLabel2 = new System.Windows.Forms.LinkLabel();
|
||||
linkLabel3 = new System.Windows.Forms.LinkLabel();
|
||||
linkLabel1 = new System.Windows.Forms.LinkLabel();
|
||||
linkLabel5 = new System.Windows.Forms.LinkLabel();
|
||||
linkLabel6 = new System.Windows.Forms.LinkLabel();
|
||||
((System.ComponentModel.ISupportInitialize)pictureBox1).BeginInit();
|
||||
groupBox1.SuspendLayout();
|
||||
flowLayoutPanel1.SuspendLayout();
|
||||
SuspendLayout();
|
||||
//
|
||||
// pictureBox1
|
||||
//
|
||||
pictureBox1.Anchor = System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right;
|
||||
pictureBox1.Image = Properties.Resources.cheers;
|
||||
pictureBox1.Location = new System.Drawing.Point(12, 91);
|
||||
pictureBox1.Name = "pictureBox1";
|
||||
pictureBox1.Size = new System.Drawing.Size(410, 210);
|
||||
pictureBox1.SizeMode = System.Windows.Forms.PictureBoxSizeMode.Zoom;
|
||||
pictureBox1.TabIndex = 0;
|
||||
pictureBox1.TabStop = false;
|
||||
//
|
||||
// releaseNotesLbl
|
||||
//
|
||||
releaseNotesLbl.AutoSize = true;
|
||||
releaseNotesLbl.Font = new System.Drawing.Font("Segoe UI", 11F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point);
|
||||
releaseNotesLbl.Location = new System.Drawing.Point(12, 12);
|
||||
releaseNotesLbl.Name = "releaseNotesLbl";
|
||||
releaseNotesLbl.Size = new System.Drawing.Size(171, 20);
|
||||
releaseNotesLbl.TabIndex = 2;
|
||||
releaseNotesLbl.TabStop = true;
|
||||
releaseNotesLbl.Text = "Libation Classic v11.0.0.0";
|
||||
releaseNotesLbl.LinkClicked += releaseNotesLbl_LinkClicked;
|
||||
//
|
||||
// checkForUpgradeBtn
|
||||
//
|
||||
checkForUpgradeBtn.Anchor = System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right;
|
||||
checkForUpgradeBtn.Font = new System.Drawing.Font("Segoe UI", 10F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point);
|
||||
checkForUpgradeBtn.Location = new System.Drawing.Point(12, 54);
|
||||
checkForUpgradeBtn.Name = "checkForUpgradeBtn";
|
||||
checkForUpgradeBtn.Size = new System.Drawing.Size(410, 31);
|
||||
checkForUpgradeBtn.TabIndex = 3;
|
||||
checkForUpgradeBtn.Text = "Check for Upgrade";
|
||||
checkForUpgradeBtn.UseVisualStyleBackColor = true;
|
||||
checkForUpgradeBtn.Click += checkForUpgradeBtn_Click;
|
||||
//
|
||||
// getLibationLbl
|
||||
//
|
||||
getLibationLbl.Anchor = System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right;
|
||||
getLibationLbl.AutoSize = true;
|
||||
getLibationLbl.Font = new System.Drawing.Font("Segoe UI", 11F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point);
|
||||
getLibationLbl.Location = new System.Drawing.Point(245, 12);
|
||||
getLibationLbl.Name = "getLibationLbl";
|
||||
getLibationLbl.Size = new System.Drawing.Size(162, 20);
|
||||
getLibationLbl.TabIndex = 7;
|
||||
getLibationLbl.TabStop = true;
|
||||
getLibationLbl.Text = "https://getlibation.com";
|
||||
getLibationLbl.LinkClicked += getLibationLbl_LinkClicked;
|
||||
//
|
||||
// rmcrackanLbl
|
||||
//
|
||||
rmcrackanLbl.Anchor = System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right;
|
||||
rmcrackanLbl.AutoSize = true;
|
||||
rmcrackanLbl.Font = new System.Drawing.Font("Segoe UI", 10F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point);
|
||||
rmcrackanLbl.Location = new System.Drawing.Point(6, 19);
|
||||
rmcrackanLbl.Name = "rmcrackanLbl";
|
||||
rmcrackanLbl.Padding = new System.Windows.Forms.Padding(0, 3, 0, 3);
|
||||
rmcrackanLbl.Size = new System.Drawing.Size(80, 25);
|
||||
rmcrackanLbl.TabIndex = 8;
|
||||
rmcrackanLbl.TabStop = true;
|
||||
rmcrackanLbl.Text = "rmcrackan";
|
||||
rmcrackanLbl.LinkClicked += Link_GithubUser;
|
||||
//
|
||||
// MBucariLbl
|
||||
//
|
||||
MBucariLbl.Anchor = System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right;
|
||||
MBucariLbl.AutoSize = true;
|
||||
MBucariLbl.Font = new System.Drawing.Font("Segoe UI", 10F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point);
|
||||
MBucariLbl.Location = new System.Drawing.Point(6, 40);
|
||||
MBucariLbl.Name = "MBucariLbl";
|
||||
MBucariLbl.Padding = new System.Windows.Forms.Padding(0, 3, 0, 3);
|
||||
MBucariLbl.Size = new System.Drawing.Size(64, 25);
|
||||
MBucariLbl.TabIndex = 9;
|
||||
MBucariLbl.TabStop = true;
|
||||
MBucariLbl.Text = "Mbucari";
|
||||
MBucariLbl.LinkClicked += Link_GithubUser;
|
||||
//
|
||||
// groupBox1
|
||||
//
|
||||
groupBox1.Anchor = System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right;
|
||||
groupBox1.Controls.Add(label3);
|
||||
groupBox1.Controls.Add(label4);
|
||||
groupBox1.Controls.Add(label2);
|
||||
groupBox1.Controls.Add(label1);
|
||||
groupBox1.Controls.Add(flowLayoutPanel1);
|
||||
groupBox1.Controls.Add(MBucariLbl);
|
||||
groupBox1.Controls.Add(rmcrackanLbl);
|
||||
groupBox1.Location = new System.Drawing.Point(12, 307);
|
||||
groupBox1.Name = "groupBox1";
|
||||
groupBox1.Size = new System.Drawing.Size(410, 172);
|
||||
groupBox1.TabIndex = 10;
|
||||
groupBox1.TabStop = false;
|
||||
groupBox1.Text = "Acknowledgements";
|
||||
//
|
||||
// label3
|
||||
//
|
||||
label3.AutoSize = true;
|
||||
label3.Font = new System.Drawing.Font("Segoe UI", 10F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point);
|
||||
label3.Location = new System.Drawing.Point(92, 43);
|
||||
label3.Name = "label3";
|
||||
label3.Padding = new System.Windows.Forms.Padding(0, 0, 0, 3);
|
||||
label3.Size = new System.Drawing.Size(71, 22);
|
||||
label3.TabIndex = 12;
|
||||
label3.Text = "Developer";
|
||||
//
|
||||
// label4
|
||||
//
|
||||
label4.AutoSize = true;
|
||||
label4.Font = new System.Drawing.Font("Segoe UI", 10F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point);
|
||||
label4.Location = new System.Drawing.Point(92, 22);
|
||||
label4.Name = "label4";
|
||||
label4.Padding = new System.Windows.Forms.Padding(0, 0, 0, 3);
|
||||
label4.Size = new System.Drawing.Size(55, 22);
|
||||
label4.TabIndex = 12;
|
||||
label4.Text = "Creator";
|
||||
//
|
||||
// label2
|
||||
//
|
||||
label2.AutoSize = true;
|
||||
label2.Font = new System.Drawing.Font("Segoe UI", 10F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point);
|
||||
label2.Location = new System.Drawing.Point(92, 22);
|
||||
label2.Name = "label2";
|
||||
label2.Padding = new System.Windows.Forms.Padding(0, 0, 0, 3);
|
||||
label2.Size = new System.Drawing.Size(45, 22);
|
||||
label2.TabIndex = 12;
|
||||
label2.Text = "label2";
|
||||
//
|
||||
// label1
|
||||
//
|
||||
label1.AutoSize = true;
|
||||
label1.Location = new System.Drawing.Point(6, 82);
|
||||
label1.Name = "label1";
|
||||
label1.Size = new System.Drawing.Size(157, 15);
|
||||
label1.TabIndex = 11;
|
||||
label1.Text = "Additional Contributions by:";
|
||||
//
|
||||
// flowLayoutPanel1
|
||||
//
|
||||
flowLayoutPanel1.Anchor = System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right;
|
||||
flowLayoutPanel1.Controls.Add(linkLabel4);
|
||||
flowLayoutPanel1.Controls.Add(linkLabel2);
|
||||
flowLayoutPanel1.Controls.Add(linkLabel3);
|
||||
flowLayoutPanel1.Controls.Add(linkLabel1);
|
||||
flowLayoutPanel1.Controls.Add(linkLabel5);
|
||||
flowLayoutPanel1.Controls.Add(linkLabel6);
|
||||
flowLayoutPanel1.Location = new System.Drawing.Point(6, 100);
|
||||
flowLayoutPanel1.Name = "flowLayoutPanel1";
|
||||
flowLayoutPanel1.Size = new System.Drawing.Size(398, 66);
|
||||
flowLayoutPanel1.TabIndex = 10;
|
||||
//
|
||||
// linkLabel4
|
||||
//
|
||||
linkLabel4.Anchor = System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right;
|
||||
linkLabel4.AutoSize = true;
|
||||
linkLabel4.Font = new System.Drawing.Font("Segoe UI", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point);
|
||||
linkLabel4.Location = new System.Drawing.Point(3, 0);
|
||||
linkLabel4.Name = "linkLabel4";
|
||||
linkLabel4.Padding = new System.Windows.Forms.Padding(0, 3, 0, 3);
|
||||
linkLabel4.Size = new System.Drawing.Size(41, 21);
|
||||
linkLabel4.TabIndex = 9;
|
||||
linkLabel4.TabStop = true;
|
||||
linkLabel4.Text = "pixil98";
|
||||
linkLabel4.LinkClicked += Link_GithubUser;
|
||||
//
|
||||
// linkLabel2
|
||||
//
|
||||
linkLabel2.Anchor = System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right;
|
||||
linkLabel2.AutoSize = true;
|
||||
linkLabel2.Font = new System.Drawing.Font("Segoe UI", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point);
|
||||
linkLabel2.Location = new System.Drawing.Point(50, 0);
|
||||
linkLabel2.Name = "linkLabel2";
|
||||
linkLabel2.Padding = new System.Windows.Forms.Padding(0, 3, 0, 3);
|
||||
linkLabel2.Size = new System.Drawing.Size(104, 21);
|
||||
linkLabel2.TabIndex = 9;
|
||||
linkLabel2.TabStop = true;
|
||||
linkLabel2.Text = "hutattedonmyarm";
|
||||
linkLabel2.LinkClicked += Link_GithubUser;
|
||||
//
|
||||
// linkLabel3
|
||||
//
|
||||
linkLabel3.Anchor = System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right;
|
||||
linkLabel3.AutoSize = true;
|
||||
linkLabel3.Font = new System.Drawing.Font("Segoe UI", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point);
|
||||
linkLabel3.Location = new System.Drawing.Point(160, 0);
|
||||
linkLabel3.Name = "linkLabel3";
|
||||
linkLabel3.Padding = new System.Windows.Forms.Padding(0, 3, 0, 3);
|
||||
linkLabel3.Size = new System.Drawing.Size(43, 21);
|
||||
linkLabel3.TabIndex = 9;
|
||||
linkLabel3.TabStop = true;
|
||||
linkLabel3.Text = "seanke";
|
||||
linkLabel3.LinkClicked += Link_GithubUser;
|
||||
//
|
||||
// linkLabel1
|
||||
//
|
||||
linkLabel1.Anchor = System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right;
|
||||
linkLabel1.AutoSize = true;
|
||||
linkLabel1.Font = new System.Drawing.Font("Segoe UI", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point);
|
||||
linkLabel1.Location = new System.Drawing.Point(209, 0);
|
||||
linkLabel1.Name = "linkLabel1";
|
||||
linkLabel1.Padding = new System.Windows.Forms.Padding(0, 3, 0, 3);
|
||||
linkLabel1.Size = new System.Drawing.Size(66, 21);
|
||||
linkLabel1.TabIndex = 9;
|
||||
linkLabel1.TabStop = true;
|
||||
linkLabel1.Text = "wtanksleyjr";
|
||||
linkLabel1.LinkClicked += Link_GithubUser;
|
||||
//
|
||||
// linkLabel5
|
||||
//
|
||||
linkLabel5.Anchor = System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right;
|
||||
linkLabel5.AutoSize = true;
|
||||
linkLabel5.Font = new System.Drawing.Font("Segoe UI", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point);
|
||||
linkLabel5.Location = new System.Drawing.Point(281, 0);
|
||||
linkLabel5.Name = "linkLabel5";
|
||||
linkLabel5.Padding = new System.Windows.Forms.Padding(0, 3, 0, 3);
|
||||
linkLabel5.Size = new System.Drawing.Size(51, 21);
|
||||
linkLabel5.TabIndex = 9;
|
||||
linkLabel5.TabStop = true;
|
||||
linkLabel5.Text = "Dr.Blank";
|
||||
linkLabel5.LinkClicked += Link_GithubUser;
|
||||
//
|
||||
// linkLabel6
|
||||
//
|
||||
linkLabel6.Anchor = System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right;
|
||||
linkLabel6.AutoSize = true;
|
||||
linkLabel6.Font = new System.Drawing.Font("Segoe UI", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point);
|
||||
linkLabel6.Location = new System.Drawing.Point(3, 21);
|
||||
linkLabel6.Name = "linkLabel6";
|
||||
linkLabel6.Padding = new System.Windows.Forms.Padding(0, 3, 0, 3);
|
||||
linkLabel6.Size = new System.Drawing.Size(77, 21);
|
||||
linkLabel6.TabIndex = 9;
|
||||
linkLabel6.TabStop = true;
|
||||
linkLabel6.Text = "CharlieRussel";
|
||||
linkLabel6.LinkClicked += Link_GithubUser;
|
||||
//
|
||||
// AboutDialog
|
||||
//
|
||||
AutoScaleDimensions = new System.Drawing.SizeF(7F, 15F);
|
||||
AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
||||
ClientSize = new System.Drawing.Size(434, 491);
|
||||
Controls.Add(groupBox1);
|
||||
Controls.Add(getLibationLbl);
|
||||
Controls.Add(checkForUpgradeBtn);
|
||||
Controls.Add(releaseNotesLbl);
|
||||
Controls.Add(pictureBox1);
|
||||
MinimumSize = new System.Drawing.Size(445, 530);
|
||||
Name = "AboutDialog";
|
||||
StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
|
||||
Text = "About Libation";
|
||||
((System.ComponentModel.ISupportInitialize)pictureBox1).EndInit();
|
||||
groupBox1.ResumeLayout(false);
|
||||
groupBox1.PerformLayout();
|
||||
flowLayoutPanel1.ResumeLayout(false);
|
||||
flowLayoutPanel1.PerformLayout();
|
||||
ResumeLayout(false);
|
||||
PerformLayout();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private System.Windows.Forms.PictureBox pictureBox1;
|
||||
private System.Windows.Forms.LinkLabel releaseNotesLbl;
|
||||
private System.Windows.Forms.Button checkForUpgradeBtn;
|
||||
private System.Windows.Forms.LinkLabel getLibationLbl;
|
||||
private System.Windows.Forms.LinkLabel rmcrackanLbl;
|
||||
private System.Windows.Forms.LinkLabel MBucariLbl;
|
||||
private System.Windows.Forms.GroupBox groupBox1;
|
||||
private System.Windows.Forms.Label label1;
|
||||
private System.Windows.Forms.FlowLayoutPanel flowLayoutPanel1;
|
||||
private System.Windows.Forms.LinkLabel linkLabel1;
|
||||
private System.Windows.Forms.LinkLabel linkLabel4;
|
||||
private System.Windows.Forms.LinkLabel linkLabel2;
|
||||
private System.Windows.Forms.LinkLabel linkLabel3;
|
||||
private System.Windows.Forms.LinkLabel linkLabel5;
|
||||
private System.Windows.Forms.LinkLabel linkLabel6;
|
||||
private System.Windows.Forms.Label label3;
|
||||
private System.Windows.Forms.Label label4;
|
||||
private System.Windows.Forms.Label label2;
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user