mirror of
https://github.com/rmcrackan/Libation.git
synced 2026-05-24 16:37:19 -04:00
@@ -14,7 +14,7 @@ namespace FileManager
|
||||
/// </summary>
|
||||
public class BackgroundFileSystem : IDisposable
|
||||
{
|
||||
public LongPath RootDirectory { get; private set; }
|
||||
public LongPath? RootDirectory { get; private set; }
|
||||
public string SearchPattern { get; private set; }
|
||||
public SearchOption SearchOption { get; private set; }
|
||||
|
||||
@@ -51,7 +51,8 @@ namespace FileManager
|
||||
lock (fsCacheLocker)
|
||||
{
|
||||
fsCache.Clear();
|
||||
fsCache.AddRange(SafestEnumerateFiles(RootDirectory));
|
||||
if (Directory.Exists(RootDirectory))
|
||||
fsCache.AddRange(SafestEnumerateFiles(RootDirectory));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -60,7 +61,14 @@ namespace FileManager
|
||||
Stop();
|
||||
|
||||
lock (fsCacheLocker)
|
||||
fsCache.AddRange(SafestEnumerateFiles(RootDirectory));
|
||||
{
|
||||
if (!Directory.Exists(RootDirectory))
|
||||
{
|
||||
RootDirectory = null;
|
||||
return;
|
||||
}
|
||||
fsCache.AddRange(SafestEnumerateFiles(RootDirectory));
|
||||
}
|
||||
|
||||
directoryChangesEvents = new BlockingCollection<FileSystemEventArgs>();
|
||||
fileSystemWatcher = new FileSystemWatcher(RootDirectory)
|
||||
|
||||
25
Source/LibationAvalonia/Controls/DataGridTextColumnExt.cs
Normal file
25
Source/LibationAvalonia/Controls/DataGridTextColumnExt.cs
Normal file
@@ -0,0 +1,25 @@
|
||||
using Avalonia;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Interactivity;
|
||||
|
||||
namespace LibationAvalonia.Controls;
|
||||
internal class DataGridTextColumnExt : DataGridTextColumn
|
||||
{
|
||||
public static readonly StyledProperty<int> MaxLengthProperty =
|
||||
AvaloniaProperty.Register<DataGridTextColumnExt, int>(nameof(MaxLength));
|
||||
|
||||
public int MaxLength
|
||||
{
|
||||
get => GetValue(MaxLengthProperty);
|
||||
set => SetValue(MaxLengthProperty, value);
|
||||
}
|
||||
|
||||
protected override object PrepareCellForEdit(Control editingElement, RoutedEventArgs editingEventArgs)
|
||||
{
|
||||
if (editingElement is TextBox textBox)
|
||||
{
|
||||
textBox.MaxLength = MaxLength;
|
||||
}
|
||||
return base.PrepareCellForEdit(editingElement, editingEventArgs);
|
||||
}
|
||||
}
|
||||
@@ -6,29 +6,63 @@
|
||||
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
||||
x:Class="LibationAvalonia.Controls.DirectoryOrCustomSelectControl">
|
||||
|
||||
<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}" />
|
||||
<Grid
|
||||
RowDefinitions="Auto,Auto,Auto"
|
||||
ColumnDefinitions="Auto,*,Auto">
|
||||
|
||||
<RadioButton
|
||||
Grid.Column="0"
|
||||
Grid.Row="0"
|
||||
IsChecked="{Binding KnownChecked, Mode=TwoWay}"/>
|
||||
<RadioButton
|
||||
Grid.RowSpan="2"
|
||||
Name="rbKnown" />
|
||||
|
||||
<RadioButton
|
||||
Grid.Column="0"
|
||||
Grid.Row="1"
|
||||
IsChecked="{Binding CustomChecked, Mode=TwoWay}"/>
|
||||
<TextBlock
|
||||
Grid.Column="1"
|
||||
Grid.ColumnSpan="2"
|
||||
VerticalAlignment="Center"
|
||||
Margin="10,0"
|
||||
IsEnabled="False"
|
||||
IsVisible="{Binding #cmbKnownDirs.SelectedItem, Converter={x:Static ObjectConverters.IsNull}}"
|
||||
Text="Select Known Directory:" />
|
||||
|
||||
<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>
|
||||
<controls:WheelComboBox
|
||||
Grid.Column="1"
|
||||
Grid.ColumnSpan="2"
|
||||
HorizontalAlignment="Stretch"
|
||||
Margin="0,0,0,3"
|
||||
IsEnabled="{Binding #rbKnown.IsChecked}"
|
||||
Name="cmbKnownDirs" />
|
||||
|
||||
<TextBox
|
||||
Grid.Row="1"
|
||||
Grid.Column="1"
|
||||
Grid.ColumnSpan="2"
|
||||
IsReadOnly="True"
|
||||
Margin="0,0,0,8"
|
||||
Name="tboxKnownDirPath"
|
||||
IsEnabled="{Binding #rbKnown.IsChecked}"
|
||||
Text="{Binding #cmbKnownDirs.SelectedItem.Directory}" />
|
||||
|
||||
|
||||
<RadioButton
|
||||
Grid.Row="2"
|
||||
Name="rbCustom" />
|
||||
|
||||
<TextBox
|
||||
Grid.Row="2"
|
||||
Grid.Column="1"
|
||||
HorizontalAlignment="Stretch"
|
||||
Name="tboxCustomDirPath"
|
||||
Margin="0,0,10,0"
|
||||
VerticalAlignment="Center"
|
||||
Text="{Binding $parent[1].Directory, Mode=OneWayToSource}"
|
||||
IsEnabled="{Binding #rbCustom.IsChecked}"/>
|
||||
|
||||
<Button
|
||||
Grid.Row="2"
|
||||
Grid.Column="2"
|
||||
Name="btnBrowse"
|
||||
IsEnabled="{Binding #rbCustom.IsChecked}">
|
||||
<TextBlock Text="..." />
|
||||
</Button>
|
||||
|
||||
</Grid>
|
||||
</UserControl>
|
||||
|
||||
@@ -1,142 +1,184 @@
|
||||
using Avalonia;
|
||||
using Avalonia.Collections;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Platform.Storage;
|
||||
using Dinah.Core;
|
||||
using LibationFileManager;
|
||||
using ReactiveUI;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
|
||||
#nullable enable
|
||||
namespace LibationAvalonia.Controls
|
||||
{
|
||||
public partial class DirectoryOrCustomSelectControl : UserControl
|
||||
{
|
||||
public static readonly StyledProperty<List<Configuration.KnownDirectories>> KnownDirectoriesProperty =
|
||||
AvaloniaProperty.Register<DirectorySelectControl, List<Configuration.KnownDirectories>>(nameof(KnownDirectories), DirectorySelectControl.DefaultKnownDirectories);
|
||||
public static readonly StyledProperty<IList<Configuration.KnownDirectories>?> KnownDirectoriesProperty =
|
||||
AvaloniaProperty.Register<DirectoryOrCustomSelectControl, IList<Configuration.KnownDirectories>?>(nameof(KnownDirectories), DefaultKnownDirectories);
|
||||
|
||||
public static readonly StyledProperty<string> SubDirectoryProperty =
|
||||
AvaloniaProperty.Register<DirectorySelectControl, string>(nameof(SubDirectory));
|
||||
public static readonly StyledProperty<string?> SubDirectoryProperty =
|
||||
AvaloniaProperty.Register<DirectoryOrCustomSelectControl, string?>(nameof(SubDirectory));
|
||||
|
||||
public static readonly StyledProperty<string> DirectoryProperty =
|
||||
AvaloniaProperty.Register<DirectorySelectControl, string>(nameof(Directory));
|
||||
public static readonly StyledProperty<string?> DirectoryProperty =
|
||||
AvaloniaProperty.Register<DirectoryOrCustomSelectControl, string?>(nameof(Directory));
|
||||
|
||||
public List<Configuration.KnownDirectories> KnownDirectories
|
||||
public IList<Configuration.KnownDirectories>? KnownDirectories
|
||||
{
|
||||
get => GetValue(KnownDirectoriesProperty);
|
||||
set => SetValue(KnownDirectoriesProperty, value);
|
||||
}
|
||||
|
||||
public string Directory
|
||||
public string? Directory
|
||||
{
|
||||
get => GetValue(DirectoryProperty);
|
||||
set => SetValue(DirectoryProperty, value);
|
||||
}
|
||||
|
||||
public string SubDirectory
|
||||
public string? SubDirectory
|
||||
{
|
||||
get => GetValue(SubDirectoryProperty);
|
||||
set => SetValue(SubDirectoryProperty, value);
|
||||
}
|
||||
|
||||
private readonly DirectoryState directoryState = new();
|
||||
public static IList<Configuration.KnownDirectories> DefaultKnownDirectories => [
|
||||
Configuration.KnownDirectories.WinTemp,
|
||||
Configuration.KnownDirectories.UserProfile,
|
||||
Configuration.KnownDirectories.ApplicationData,
|
||||
Configuration.KnownDirectories.AppDir,
|
||||
Configuration.KnownDirectories.MyMusic,
|
||||
Configuration.KnownDirectories.MyDocs,
|
||||
Configuration.KnownDirectories.LibationFiles];
|
||||
|
||||
private readonly AvaloniaList<KnownDirectoryItem> _knownDirNames;
|
||||
public DirectoryOrCustomSelectControl()
|
||||
{
|
||||
InitializeComponent();
|
||||
|
||||
grid.DataContext = directoryState;
|
||||
|
||||
directoryState.PropertyChanged += DirectoryState_PropertyChanged;
|
||||
PropertyChanged += DirectoryOrCustomSelectControl_PropertyChanged;
|
||||
_knownDirNames = new(GetKnownDirectories(DefaultKnownDirectories));
|
||||
cmbKnownDirs.ItemsSource = _knownDirNames;
|
||||
cmbKnownDirs.SelectionChanged += CmbKnownDirs_SelectionChanged;
|
||||
btnBrowse.Click += Browse_Click;
|
||||
}
|
||||
|
||||
private void DirectoryState_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
|
||||
private void CmbKnownDirs_SelectionChanged(object? sender, SelectionChangedEventArgs 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)
|
||||
if (cmbKnownDirs.SelectedItem is KnownDirectoryItem item && item.Directory is not null)
|
||||
{
|
||||
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;
|
||||
Directory = item.Directory;
|
||||
}
|
||||
}
|
||||
|
||||
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 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); }
|
||||
private IEnumerable<KnownDirectoryItem> GetKnownDirectories(IEnumerable<Configuration.KnownDirectories> knownDirs)
|
||||
=> knownDirs.Select(k => new KnownDirectoryItem(k, SubDirectory)).Where(k => k.Directory is not null);
|
||||
|
||||
public Configuration.KnownDirectories? SelectedDirectory { get => _selectedDirectory; set => this.RaiseAndSetIfChanged(ref _selectedDirectory, value); }
|
||||
protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
|
||||
{
|
||||
if (change.Property == SubDirectoryProperty)
|
||||
{
|
||||
foreach (var item in _knownDirNames)
|
||||
{
|
||||
item.SubDirectory = SubDirectory;
|
||||
}
|
||||
VerifyAndApplyDirectory(Directory);
|
||||
}
|
||||
else if (change.Property == KnownDirectoriesProperty)
|
||||
{
|
||||
var knownDirs = KnownDirectories?.Count > 0 ? KnownDirectories : DefaultKnownDirectories;
|
||||
if (!_knownDirNames.Select(k => k.KnownDirectory).SequenceEqual(knownDirs))
|
||||
{
|
||||
_knownDirNames.Clear();
|
||||
_knownDirNames.AddRange(GetKnownDirectories(knownDirs));
|
||||
}
|
||||
VerifyAndApplyDirectory(Directory);
|
||||
}
|
||||
else if (change.Property == DirectoryProperty)
|
||||
{
|
||||
VerifyAndApplyDirectory(Directory);
|
||||
}
|
||||
|
||||
base.OnPropertyChanged(change);
|
||||
}
|
||||
|
||||
private async void CustomDirBrowseBtn_Click(object sender, Avalonia.Interactivity.RoutedEventArgs e)
|
||||
private void VerifyAndApplyDirectory(string? directory)
|
||||
{
|
||||
var options = new Avalonia.Platform.Storage.FolderPickerOpenOptions
|
||||
if (string.IsNullOrWhiteSpace(Directory))
|
||||
return;
|
||||
|
||||
bool dirIsKnown = false;
|
||||
foreach (var item in _knownDirNames)
|
||||
{
|
||||
if (item.IsSamePathAs(directory))
|
||||
{
|
||||
rbKnown.IsChecked = true;
|
||||
Directory = item.Directory;
|
||||
cmbKnownDirs.SelectedItem = item;
|
||||
dirIsKnown = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!dirIsKnown)
|
||||
{
|
||||
tboxCustomDirPath.Text = directory;
|
||||
rbCustom.IsChecked = true;
|
||||
}
|
||||
}
|
||||
|
||||
public async void Browse_Click(object? sender, Avalonia.Interactivity.RoutedEventArgs e)
|
||||
{
|
||||
if (VisualRoot is not Window window)
|
||||
return;
|
||||
|
||||
var options = new FolderPickerOpenOptions
|
||||
{
|
||||
AllowMultiple = false
|
||||
};
|
||||
|
||||
var selectedFolders = await (VisualRoot as Window).StorageProvider.OpenFolderPickerAsync(options);
|
||||
|
||||
directoryState.CustomDir = selectedFolders.SingleOrDefault()?.TryGetLocalPath() ?? directoryState.CustomDir;
|
||||
var selectedFolders = await window.StorageProvider.OpenFolderPickerAsync(options);
|
||||
Directory = selectedFolders.SingleOrDefault()?.TryGetLocalPath() ?? Directory;
|
||||
}
|
||||
|
||||
private void DirectoryOrCustomSelectControl_PropertyChanged(object sender, AvaloniaPropertyChangedEventArgs e)
|
||||
private class KnownDirectoryItem : ReactiveObject
|
||||
{
|
||||
if (e.Property == DirectoryProperty)
|
||||
public Configuration.KnownDirectories KnownDirectory { get; set; }
|
||||
private string? _directory;
|
||||
public string? Directory { get => _directory; private set => this.RaiseAndSetIfChanged(ref _directory, value); }
|
||||
public string? Name { get; }
|
||||
private string? _subDir;
|
||||
public string? SubDirectory
|
||||
{
|
||||
var directory = Directory?.Trim() ?? "";
|
||||
|
||||
var noSubDir = RemoveSubDirectoryFromPath(directory);
|
||||
var known = Configuration.GetKnownDirectory(noSubDir);
|
||||
|
||||
if (known == Configuration.KnownDirectories.None && noSubDir == Configuration.AppDir_Absolute)
|
||||
known = Configuration.KnownDirectories.AppDir;
|
||||
|
||||
if (known is Configuration.KnownDirectories.None)
|
||||
get => _subDir;
|
||||
set
|
||||
{
|
||||
directoryState.CustomDir = directory;
|
||||
directoryState.CustomChecked = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
directoryState.SelectedDirectory = known;
|
||||
directoryState.KnownChecked = true;
|
||||
_subDir = value;
|
||||
if (Configuration.GetKnownDirectoryPath(KnownDirectory) is string dir)
|
||||
{
|
||||
Directory = Path.Combine(dir, _subDir ?? "");
|
||||
}
|
||||
}
|
||||
}
|
||||
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)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(SubDirectory))
|
||||
return path;
|
||||
public KnownDirectoryItem(Configuration.KnownDirectories known, string? subDir)
|
||||
{
|
||||
Name = known.GetDescription();
|
||||
KnownDirectory = known;
|
||||
SubDirectory = subDir;
|
||||
}
|
||||
|
||||
path = path?.Trim() ?? "";
|
||||
if (string.IsNullOrWhiteSpace(path))
|
||||
return path;
|
||||
public bool IsSamePathAs(string? otherPath)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(otherPath) || string.IsNullOrWhiteSpace(Directory))
|
||||
return false;
|
||||
|
||||
var bottomDir = System.IO.Path.GetFileName(path);
|
||||
if (SubDirectory.EqualsInsensitive(bottomDir))
|
||||
return System.IO.Path.GetDirectoryName(path);
|
||||
try
|
||||
{
|
||||
var p1 = Path.GetFullPath(Directory);
|
||||
var p2 = Path.GetFullPath(otherPath);
|
||||
return p1.Equals(p2, System.StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
catch { return false; }
|
||||
}
|
||||
|
||||
return path;
|
||||
public override string? ToString() => Name?.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,15 +9,15 @@
|
||||
xmlns:controls="clr-namespace:LibationAvalonia.Controls"
|
||||
Title="About Libation">
|
||||
|
||||
<Grid Margin="10" ColumnDefinitions="Auto,*" RowDefinitions="Auto,Auto,Auto,*">
|
||||
<Grid Margin="10" RowDefinitions="Auto,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 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"/>
|
||||
<controls:LinkLabel Grid.Row="1" FontSize="14" VerticalAlignment="Center" 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" />
|
||||
<Button Grid.Row="2" HorizontalAlignment="Stretch" HorizontalContentAlignment="Center" Margin="0,10,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">
|
||||
<Canvas Grid.Row="3" Margin="0,30,0,20" Width="280" Height="220">
|
||||
<Path Stretch="None" Fill="{DynamicResource IconFill}" Data="{DynamicResource LibationCheersIcon}">
|
||||
<Path.RenderTransform>
|
||||
<TransformGroup>
|
||||
@@ -39,7 +39,7 @@
|
||||
</Path>
|
||||
</Canvas>
|
||||
|
||||
<controls:GroupBox Grid.Row="3" Label="Acknowledgements" Grid.ColumnSpan="2">
|
||||
<controls:GroupBox Grid.Row="4" Label="Acknowledgements">
|
||||
<StackPanel>
|
||||
<StackPanel.Styles>
|
||||
<Style Selector="controls|LinkLabel">
|
||||
|
||||
@@ -2,92 +2,80 @@
|
||||
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="450"
|
||||
MinWidth="500" MinHeight="450"
|
||||
Width="500" Height="450"
|
||||
mc:Ignorable="d" d:DesignWidth="450" d:DesignHeight="450"
|
||||
MinWidth="450" MinHeight="450"
|
||||
Width="450" Height="450"
|
||||
x:Class="LibationAvalonia.Dialogs.EditReplacementChars"
|
||||
xmlns:dialogs="clr-namespace:LibationAvalonia.Dialogs"
|
||||
xmlns:controls="clr-namespace:LibationAvalonia.Controls"
|
||||
x:DataType="dialogs:EditReplacementChars"
|
||||
Title="Illegal Character Replacement">
|
||||
|
||||
<Grid
|
||||
RowDefinitions="*,Auto"
|
||||
ColumnDefinitions="*,Auto">
|
||||
x:CompileBindings="True"
|
||||
Title="File Path Character Replacement">
|
||||
|
||||
<Grid RowDefinitions="*,Auto">
|
||||
<DataGrid
|
||||
Grid.Row="0"
|
||||
Grid.ColumnSpan="2"
|
||||
GridLinesVisibility="All"
|
||||
Margin="5"
|
||||
Name="replacementGrid"
|
||||
AutoGenerateColumns="False"
|
||||
IsReadOnly="False"
|
||||
BeginningEdit="ReplacementGrid_BeginningEdit"
|
||||
CellEditEnding="ReplacementGrid_CellEditEnding"
|
||||
KeyDown="ReplacementGrid_KeyDown"
|
||||
ItemsSource="{CompiledBinding replacements}">
|
||||
|
||||
GridLinesVisibility="All"
|
||||
CanUserSortColumns="False"
|
||||
AutoGenerateColumns="False"
|
||||
ItemsSource="{Binding Replacements}"
|
||||
KeyDown="replacementGrid_KeyDown"
|
||||
BeginningEdit="replacementGrid_BeginningEdit"
|
||||
CellEditEnded="replacementGrid_CellEditEnded"
|
||||
CellEditEnding="replacementGrid_CellEditEnding">
|
||||
<DataGrid.Columns>
|
||||
|
||||
<DataGridTemplateColumn Header="Char to
Replace">
|
||||
<DataGridTemplateColumn.CellTemplate>
|
||||
<DataTemplate x:DataType="dialogs:EditReplacementChars+ReplacementsExt">
|
||||
<TextBox IsReadOnly="{CompiledBinding Mandatory}" Text="{CompiledBinding CharacterToReplace, Mode=TwoWay}" />
|
||||
</DataTemplate>
|
||||
</DataGridTemplateColumn.CellTemplate>
|
||||
</DataGridTemplateColumn>
|
||||
|
||||
<DataGridTemplateColumn Header="Replacement
Text">
|
||||
<DataGridTemplateColumn.CellTemplate>
|
||||
<DataTemplate x:DataType="dialogs:EditReplacementChars+ReplacementsExt">
|
||||
<TextBox Text="{CompiledBinding ReplacementText, Mode=TwoWay}" />
|
||||
</DataTemplate>
|
||||
</DataGridTemplateColumn.CellTemplate>
|
||||
</DataGridTemplateColumn>
|
||||
<controls:DataGridTextColumnExt
|
||||
x:DataType="dialogs:EditReplacementChars+ReplacementsExt"
|
||||
MaxLength="1"
|
||||
Header="Char to
Replace"
|
||||
Binding="{Binding CharacterToReplace, Mode=TwoWay}"/>
|
||||
|
||||
<DataGridTemplateColumn Width="*" Header="Description">
|
||||
<DataGridTemplateColumn.CellTemplate>
|
||||
<DataTemplate x:DataType="dialogs:EditReplacementChars+ReplacementsExt">
|
||||
<TextBox IsReadOnly="{CompiledBinding Mandatory}" Text="{CompiledBinding Description, Mode=TwoWay}" />
|
||||
</DataTemplate>
|
||||
</DataGridTemplateColumn.CellTemplate>
|
||||
</DataGridTemplateColumn>
|
||||
<DataGridTextColumn
|
||||
x:DataType="dialogs:EditReplacementChars+ReplacementsExt"
|
||||
Header="Replacement
Text"
|
||||
Binding="{Binding ReplacementText, Mode=TwoWay}"/>
|
||||
|
||||
<DataGridTextColumn
|
||||
x:DataType="dialogs:EditReplacementChars+ReplacementsExt"
|
||||
Header="Description"
|
||||
Binding="{Binding Description, Mode=TwoWay}"/>
|
||||
|
||||
</DataGrid.Columns>
|
||||
</DataGrid>
|
||||
|
||||
<Grid
|
||||
Grid.Row="1"
|
||||
Grid.Column="0"
|
||||
RowDefinitions="Auto,Auto"
|
||||
Margin="5"
|
||||
ColumnDefinitions="Auto,Auto,Auto,Auto">
|
||||
ColumnDefinitions="Auto,Auto,Auto,*,Auto,Auto"
|
||||
Margin="5">
|
||||
<Grid.Styles>
|
||||
<Style Selector="Button">
|
||||
<Setter Property="Margin" Value="2"/>
|
||||
<Setter Property="Padding" Value="6"/>
|
||||
<Setter Property="VerticalAlignment" Value="Bottom"/>
|
||||
</Style>
|
||||
<Style Selector="TextBlock">
|
||||
<Setter Property="VerticalAlignment" Value="Center"/>
|
||||
</Style>
|
||||
</Grid.Styles>
|
||||
|
||||
<TextBlock IsVisible="{CompiledBinding !EnvironmentIsWindows}" Text="This System:" Margin="0,0,10,0" VerticalAlignment="Center" />
|
||||
<TextBlock IsVisible="{CompiledBinding !EnvironmentIsWindows}" Grid.Row="1" Text="NTFS:" Margin="0,0,10,0" VerticalAlignment="Center" />
|
||||
<TextBlock Grid.Row="0" Text="This
System:" IsVisible="{Binding !EnvironmentIsWindows}" />
|
||||
<TextBlock Grid.Row="1" Text="NTFS:" IsVisible="{Binding !EnvironmentIsWindows}" />
|
||||
|
||||
<Button Grid.Column="1" Margin="0,0,10,0" Command="{CompiledBinding Defaults}" CommandParameter="{CompiledBinding EnvironmentIsWindows}" Content="Defaults" />
|
||||
<Button Grid.Column="2" Margin="0,0,10,0" Command="{CompiledBinding LoFiDefaults}" CommandParameter="{CompiledBinding EnvironmentIsWindows}" Content="LoFi Defaults" />
|
||||
<Button Grid.Column="3" Command="{CompiledBinding Barebones}" CommandParameter="{CompiledBinding EnvironmentIsWindows}" Content="Barebones" />
|
||||
<Button Grid.Column="1" Command="{Binding Defaults}" CommandParameter="{Binding EnvironmentIsWindows}" Content="Defaults" />
|
||||
<Button Grid.Column="2" Command="{Binding LoFiDefaults}" CommandParameter="{Binding EnvironmentIsWindows}" Content="LoFi Defaults" />
|
||||
<Button Grid.Column="3" Command="{Binding Barebones}" CommandParameter="{Binding EnvironmentIsWindows}" Content="Barebones" />
|
||||
|
||||
<Button IsVisible="{CompiledBinding !EnvironmentIsWindows}" Grid.Row="1" Grid.Column="1" Margin="0,10,10,0" Command="{CompiledBinding Defaults}" CommandParameter="True" Content="Defaults" />
|
||||
<Button IsVisible="{CompiledBinding !EnvironmentIsWindows}" Grid.Row="1" Grid.Column="2" Margin="0,10,10,0" Command="{CompiledBinding LoFiDefaults}" CommandParameter="True" Content="LoFi Defaults" />
|
||||
<Button IsVisible="{CompiledBinding !EnvironmentIsWindows}" Grid.Row="1" Grid.Column="3" Margin="0,10,0,0" Command="{CompiledBinding Barebones}" CommandParameter="True" Content="Barebones" />
|
||||
|
||||
</Grid>
|
||||
|
||||
<StackPanel
|
||||
Grid.Row="1"
|
||||
Grid.Column="1"
|
||||
Margin="5"
|
||||
VerticalAlignment="Bottom"
|
||||
Orientation="Horizontal">
|
||||
|
||||
<Button Margin="0,0,10,0" Command="{Binding Close}" Content="Cancel" />
|
||||
<Button Padding="20,5,20,6" Command="{Binding SaveAndClose}" Content="Save" />
|
||||
</StackPanel>
|
||||
|
||||
</Grid>
|
||||
|
||||
|
||||
<Button Grid.Row="1" Grid.Column="1" IsVisible="{Binding !EnvironmentIsWindows}" Command="{Binding Defaults}" CommandParameter="True" Content="Defaults" />
|
||||
<Button Grid.Row="1" Grid.Column="2" IsVisible="{Binding !EnvironmentIsWindows}" Command="{Binding LoFiDefaults}" CommandParameter="True" Content="LoFi Defaults" />
|
||||
<Button Grid.Row="1" Grid.Column="3" IsVisible="{Binding !EnvironmentIsWindows}" Command="{Binding Barebones}" CommandParameter="True" Content="Barebones" />
|
||||
|
||||
<Button Grid.RowSpan="2" Grid.Column="4" Command="{Binding Close}" Content="Cancel" />
|
||||
<Button Grid.RowSpan="2" Grid.Column="5" Padding="20,6" Command="{Binding SaveAndClose}" Content="Save" />
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Window>
|
||||
|
||||
@@ -1,27 +1,27 @@
|
||||
using Avalonia.Collections;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Data;
|
||||
using FileManager;
|
||||
using LibationFileManager;
|
||||
using ReactiveUI;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
#nullable enable
|
||||
namespace LibationAvalonia.Dialogs
|
||||
{
|
||||
public partial class EditReplacementChars : DialogWindow
|
||||
{
|
||||
Configuration config;
|
||||
private Configuration? Config { get; }
|
||||
|
||||
public bool EnvironmentIsWindows => Configuration.IsWindows;
|
||||
|
||||
private readonly List<ReplacementsExt> SOURCE = new();
|
||||
public DataGridCollectionView replacements { get; }
|
||||
private readonly AvaloniaList<ReplacementsExt> SOURCE = new();
|
||||
public DataGridCollectionView Replacements { get; }
|
||||
public EditReplacementChars()
|
||||
{
|
||||
InitializeComponent();
|
||||
|
||||
replacements = new(SOURCE);
|
||||
Replacements = new(SOURCE);
|
||||
|
||||
if (Design.IsDesignMode)
|
||||
{
|
||||
@@ -33,7 +33,7 @@ namespace LibationAvalonia.Dialogs
|
||||
|
||||
public EditReplacementChars(Configuration config) : this()
|
||||
{
|
||||
this.config = config;
|
||||
Config = config;
|
||||
LoadTable(config.ReplacementCharacters.Replacements);
|
||||
}
|
||||
|
||||
@@ -44,15 +44,14 @@ namespace LibationAvalonia.Dialogs
|
||||
public void Barebones(bool isNtfs)
|
||||
=> LoadTable(ReplacementCharacters.Barebones(isNtfs).Replacements);
|
||||
|
||||
protected override void SaveAndClose()
|
||||
public new void Close() => base.Close();
|
||||
public new void SaveAndClose()
|
||||
{
|
||||
var replacements = SOURCE
|
||||
.Where(r => !r.IsDefault)
|
||||
.Select(r => new Replacement(r.Character, r.ReplacementText, r.Description) { Mandatory = r.Mandatory })
|
||||
.ToList();
|
||||
|
||||
if (config is not null)
|
||||
config.ReplacementCharacters = new ReplacementCharacters { Replacements = replacements };
|
||||
if (Config is not null)
|
||||
{
|
||||
var replacements = SOURCE.Where(r => !r.IsDefault).Select(r => r.ToReplacement()).ToArray();
|
||||
Config.ReplacementCharacters = new ReplacementCharacters { Replacements = replacements };
|
||||
}
|
||||
base.SaveAndClose();
|
||||
}
|
||||
|
||||
@@ -61,59 +60,64 @@ namespace LibationAvalonia.Dialogs
|
||||
SOURCE.Clear();
|
||||
SOURCE.AddRange(replacements.Select(r => new ReplacementsExt(r)));
|
||||
SOURCE.Add(new ReplacementsExt());
|
||||
this.replacements.Refresh();
|
||||
}
|
||||
|
||||
public void ReplacementGrid_KeyDown(object sender, Avalonia.Input.KeyEventArgs e)
|
||||
private bool ColumnIsCharacter(DataGridColumn column)
|
||||
=> column.DisplayIndex is 0;
|
||||
|
||||
private bool ColumnIsReplacement(DataGridColumn column)
|
||||
=> column.DisplayIndex is 1;
|
||||
|
||||
private bool RowIsReadOnly(DataGridRow row)
|
||||
=> row.DataContext is ReplacementsExt rep && rep.Mandatory;
|
||||
|
||||
private bool CanDeleteSelectedItem(ReplacementsExt selectedItem)
|
||||
=> !selectedItem.Mandatory && (!selectedItem.IsDefault || SOURCE[^1] != selectedItem);
|
||||
|
||||
private void replacementGrid_BeginningEdit(object sender, DataGridBeginningEditEventArgs e)
|
||||
{
|
||||
if (e.Key == Avalonia.Input.Key.Delete
|
||||
&& ((DataGrid)sender).SelectedItem is ReplacementsExt repl
|
||||
&& !repl.Mandatory
|
||||
&& !repl.IsDefault)
|
||||
{
|
||||
replacements.Remove(repl);
|
||||
}
|
||||
e.Cancel = RowIsReadOnly(e.Row) && !ColumnIsReplacement(e.Column);
|
||||
}
|
||||
|
||||
public void ReplacementGrid_CellEditEnding(object sender, DataGridCellEditEndingEventArgs e)
|
||||
private void replacementGrid_CellEditEnding(object? sender, DataGridCellEditEndingEventArgs e)
|
||||
{
|
||||
var replacement = e.Row.DataContext as ReplacementsExt;
|
||||
var colBinding = columnBindingPath(e.Column);
|
||||
|
||||
//Prevent duplicate CharacterToReplace
|
||||
if (e.EditingElement is TextBox tbox
|
||||
&& colBinding == nameof(replacement.CharacterToReplace)
|
||||
&& SOURCE.Any(r => r != replacement && r.CharacterToReplace == tbox.Text))
|
||||
//Disallow duplicates of CharacterToReplace
|
||||
if (ColumnIsCharacter(e.Column) && e.Row.DataContext is ReplacementsExt r && r.CharacterToReplace.Length > 0 && SOURCE.Count(rep => rep.CharacterToReplace == r.CharacterToReplace) > 1)
|
||||
{
|
||||
tbox.Text = replacement.CharacterToReplace;
|
||||
}
|
||||
|
||||
//Add new blank row
|
||||
void Replacement_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
|
||||
{
|
||||
if (!SOURCE.Any(r => r.IsDefault))
|
||||
{
|
||||
var rewRepl = new ReplacementsExt();
|
||||
SOURCE.Add(rewRepl);
|
||||
}
|
||||
replacement.PropertyChanged -= Replacement_PropertyChanged;
|
||||
}
|
||||
|
||||
replacement.PropertyChanged += Replacement_PropertyChanged;
|
||||
}
|
||||
|
||||
public void ReplacementGrid_BeginningEdit(object sender, DataGridBeginningEditEventArgs e)
|
||||
{
|
||||
var replacement = e.Row.DataContext as ReplacementsExt;
|
||||
|
||||
//Disallow editing of Mandatory CharacterToReplace and Descriptions
|
||||
if (replacement.Mandatory
|
||||
&& columnBindingPath(e.Column) != nameof(replacement.ReplacementText))
|
||||
r.CharacterToReplace = "";
|
||||
e.Cancel = true;
|
||||
}
|
||||
}
|
||||
|
||||
private static string columnBindingPath(DataGridColumn column)
|
||||
=> ((Binding)((DataGridBoundColumn)column).Binding).Path;
|
||||
private void replacementGrid_CellEditEnded(object? sender, DataGridCellEditEndedEventArgs e)
|
||||
{
|
||||
if (ColumnIsCharacter(e.Column) && e.Row.DataContext is ReplacementsExt r && r.CharacterToReplace.Length > 0 && !SOURCE[^1].IsDefault)
|
||||
{
|
||||
Replacements.AddNew();
|
||||
}
|
||||
}
|
||||
|
||||
private void replacementGrid_KeyDown(object? sender, Avalonia.Input.KeyEventArgs e)
|
||||
{
|
||||
if (e.Key == Avalonia.Input.Key.Delete && (sender as DataGrid)?.SelectedItem is ReplacementsExt r && CanDeleteSelectedItem(r))
|
||||
{
|
||||
if (Replacements.IsEditingItem)
|
||||
{
|
||||
if (Replacements.CanCancelEdit)
|
||||
Replacements.CancelEdit();
|
||||
else
|
||||
Replacements.CommitEdit();
|
||||
}
|
||||
if (Replacements.IsAddingNew)
|
||||
{
|
||||
Replacements.CancelNew();
|
||||
}
|
||||
if (Replacements.CanRemove)
|
||||
{
|
||||
Replacements.Remove(r);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class ReplacementsExt : ViewModels.ViewModelBase
|
||||
{
|
||||
@@ -122,7 +126,6 @@ namespace LibationAvalonia.Dialogs
|
||||
_replacementText = string.Empty;
|
||||
_description = string.Empty;
|
||||
_characterToReplace = string.Empty;
|
||||
IsDefault = true;
|
||||
}
|
||||
public ReplacementsExt(Replacement replacement)
|
||||
{
|
||||
@@ -131,41 +134,19 @@ namespace LibationAvalonia.Dialogs
|
||||
_description = replacement.Description;
|
||||
Mandatory = replacement.Mandatory;
|
||||
}
|
||||
|
||||
private string _replacementText;
|
||||
private string _description;
|
||||
private string _characterToReplace;
|
||||
public bool Mandatory { get; }
|
||||
public string ReplacementText
|
||||
{
|
||||
get => _replacementText;
|
||||
set
|
||||
{
|
||||
if (ReplacementCharacters.ContainsInvalidFilenameChar(value))
|
||||
this.RaisePropertyChanged(nameof(ReplacementText));
|
||||
else
|
||||
this.RaiseAndSetIfChanged(ref _replacementText, value);
|
||||
}
|
||||
}
|
||||
|
||||
public string ReplacementText { get => _replacementText; set => this.RaiseAndSetIfChanged(ref _replacementText, value); }
|
||||
public string Description { get => _description; set => this.RaiseAndSetIfChanged(ref _description, value); }
|
||||
|
||||
public string CharacterToReplace
|
||||
{
|
||||
get => _characterToReplace;
|
||||
|
||||
set
|
||||
{
|
||||
if (value?.Length != 1)
|
||||
this.RaisePropertyChanged(nameof(CharacterToReplace));
|
||||
else
|
||||
{
|
||||
IsDefault = false;
|
||||
this.RaiseAndSetIfChanged(ref _characterToReplace, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
public string CharacterToReplace { get => _characterToReplace; set => this.RaiseAndSetIfChanged(ref _characterToReplace, value); }
|
||||
public char Character => string.IsNullOrEmpty(_characterToReplace) ? default : _characterToReplace[0];
|
||||
public bool IsDefault { get; private set; }
|
||||
public bool IsDefault => !Mandatory && string.IsNullOrEmpty(CharacterToReplace);
|
||||
public bool Mandatory { get; }
|
||||
|
||||
public Replacement ToReplacement()
|
||||
=> new(Character, ReplacementText, Description) { Mandatory = Mandatory };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,15 +1,23 @@
|
||||
<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="500" d:DesignHeight="200"
|
||||
x:Class="LibationAvalonia.Dialogs.ScanAccountsDialog"
|
||||
MinWidth="500" MinHeight="160"
|
||||
Width="500" Height="200"
|
||||
Title="Which Accounts?"
|
||||
WindowStartupLocation="CenterOwner">
|
||||
<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="500" d:DesignHeight="340"
|
||||
MinWidth="200" MinHeight="210"
|
||||
Width="500" Height="500"
|
||||
x:Class="LibationAvalonia.Dialogs.ScanAccountsDialog"
|
||||
xmlns:dialogs="clr-namespace:LibationAvalonia.Dialogs"
|
||||
x:DataType="dialogs:ScanAccountsDialog"
|
||||
x:CompileBindings="True"
|
||||
Title="Which Accounts?"
|
||||
WindowStartupLocation="CenterOwner">
|
||||
|
||||
<Grid ColumnDefinitions="*,Auto" RowDefinitions="Auto,*,Auto">
|
||||
<Grid
|
||||
ColumnDefinitions="*,Auto"
|
||||
RowDefinitions="Auto,*,Auto"
|
||||
Margin="10">
|
||||
|
||||
<Grid.Styles>
|
||||
<Style Selector="Button:focus">
|
||||
@@ -22,54 +30,38 @@
|
||||
Grid.Row="0"
|
||||
Grid.Column="0"
|
||||
Grid.ColumnSpan="2"
|
||||
Margin="10"
|
||||
Text="Check the accounts to scan and import.
To change default selections, go to: Settings > Accounts"/>
|
||||
|
||||
<ScrollViewer
|
||||
<DockPanel
|
||||
Grid.Row="1"
|
||||
Grid.Column="0"
|
||||
Grid.ColumnSpan="2"
|
||||
Margin="10,0"
|
||||
VerticalAlignment="Stretch"
|
||||
HorizontalScrollBarVisibility="Disabled"
|
||||
VerticalScrollBarVisibility="Auto">
|
||||
Margin="0,10"
|
||||
VerticalAlignment="Stretch">
|
||||
|
||||
<ListBox ItemsSource="{Binding Accounts}">
|
||||
<ListBox Name="lbAccounts" ItemsSource="{Binding Accounts}">
|
||||
<ListBox.ItemTemplate>
|
||||
<DataTemplate>
|
||||
|
||||
<StackPanel Height="20" Orientation="Horizontal">
|
||||
|
||||
<CheckBox
|
||||
Margin="0,0,10,0"
|
||||
IsChecked="{Binding IsChecked, Mode=TwoWay}" />
|
||||
<TextBlock
|
||||
FontSize="12"
|
||||
VerticalAlignment="Center"
|
||||
Text="{Binding Text}" />
|
||||
|
||||
</StackPanel>
|
||||
|
||||
<CheckBox
|
||||
IsChecked="{Binding IsChecked, Mode=TwoWay}">
|
||||
<TextBlock Text="{Binding Text}" />
|
||||
</CheckBox>
|
||||
</DataTemplate>
|
||||
</ListBox.ItemTemplate>
|
||||
|
||||
</ListBox>
|
||||
|
||||
</ScrollViewer>
|
||||
</ListBox.ItemTemplate>
|
||||
</ListBox>
|
||||
</DockPanel>
|
||||
|
||||
<Button
|
||||
Grid.Row="2"
|
||||
Grid.Column="0"
|
||||
Padding="20,5"
|
||||
Margin="10"
|
||||
Padding="20,6"
|
||||
Content="Edit Accounts"
|
||||
Command="{Binding EditAccountsAsync}"/>
|
||||
|
||||
<Button
|
||||
Grid.Row="2"
|
||||
Grid.Column="1"
|
||||
Padding="30,5"
|
||||
Margin="10"
|
||||
Padding="30,6"
|
||||
HorizontalAlignment="Right"
|
||||
Content="Import"
|
||||
Name="ImportButton"
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
using AudibleUtilities;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Collections;
|
||||
using LibationUiBase.Forms;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
@@ -10,41 +9,36 @@ namespace LibationAvalonia.Dialogs
|
||||
{
|
||||
public partial class ScanAccountsDialog : DialogWindow
|
||||
{
|
||||
public List<Account> CheckedAccounts { get; } = new();
|
||||
private List<listItem> _accounts { get; } = new();
|
||||
public IList Accounts => _accounts;
|
||||
private class listItem
|
||||
public IEnumerable<Account> CheckedAccounts => Accounts.Where(a => a.IsChecked).Select(a => a.Account);
|
||||
public AvaloniaList<ListItem> Accounts { get; } = new();
|
||||
public class ListItem
|
||||
{
|
||||
public Account Account { get; set; }
|
||||
public string Text { get; set; }
|
||||
public bool IsChecked { get; set; } = true;
|
||||
public ListItem(Account account)
|
||||
{
|
||||
Account = account;
|
||||
IsChecked = account.LibraryScan;
|
||||
Text = $"{account.AccountName} ({account.AccountId} - {account.Locale.Name})";
|
||||
}
|
||||
public Account Account { get; }
|
||||
public string Text { get; }
|
||||
public bool IsChecked { get; set; }
|
||||
public override string ToString() => Text;
|
||||
}
|
||||
|
||||
public ScanAccountsDialog()
|
||||
{
|
||||
InitializeComponent();
|
||||
|
||||
ControlToFocusOnShow = this.FindControl<Button>(nameof(ImportButton));
|
||||
|
||||
ControlToFocusOnShow = ImportButton;
|
||||
DataContext = this;
|
||||
LoadAccounts();
|
||||
}
|
||||
|
||||
private void LoadAccounts()
|
||||
{
|
||||
_accounts.Clear();
|
||||
Accounts.Clear();
|
||||
using var persister = AudibleApiStorage.GetAccountsSettingsPersister();
|
||||
var accounts = persister.AccountsSettings.Accounts;
|
||||
|
||||
foreach (var account in accounts)
|
||||
_accounts.Add(new listItem
|
||||
{
|
||||
Account = account,
|
||||
IsChecked = account.LibraryScan,
|
||||
Text = $"{account.AccountName} ({account.AccountId} - {account.Locale.Name})"
|
||||
});
|
||||
|
||||
DataContext = this;
|
||||
Accounts.AddRange(accounts.Select(account => new ListItem(account)));
|
||||
}
|
||||
|
||||
public async Task EditAccountsAsync()
|
||||
@@ -56,12 +50,7 @@ namespace LibationAvalonia.Dialogs
|
||||
}
|
||||
}
|
||||
|
||||
protected override void SaveAndClose()
|
||||
{
|
||||
foreach (listItem item in _accounts.Where(a => a.IsChecked))
|
||||
CheckedAccounts.Add(item.Account);
|
||||
|
||||
base.SaveAndClose();
|
||||
}
|
||||
public new void SaveAndClose()
|
||||
=> base.SaveAndClose();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -50,25 +50,25 @@
|
||||
<Grid Grid.Row="1" RowDefinitions="Auto,Auto,*">
|
||||
<TextBlock Text="STRING FIELDS" />
|
||||
<TextBlock Grid.Row="1" Text="{CompiledBinding StringUsage}" />
|
||||
<ListBox Grid.Row="2" ItemsSource="{CompiledBinding StringFields}"/>
|
||||
<ListBox Grid.Row="2" DoubleTapped="ListBox_DoubleTapped" ItemsSource="{CompiledBinding StringFields}"/>
|
||||
</Grid>
|
||||
|
||||
<Grid Grid.Row="1" Grid.Column="1" RowDefinitions="Auto,Auto,*">
|
||||
<TextBlock Text="NUMBER FIELDS" />
|
||||
<TextBlock Grid.Row="1" Text="{CompiledBinding NumberUsage}" />
|
||||
<ListBox Grid.Row="2" ItemsSource="{CompiledBinding NumberFields}"/>
|
||||
<ListBox Grid.Row="2" DoubleTapped="ListBox_DoubleTapped" ItemsSource="{CompiledBinding NumberFields}"/>
|
||||
</Grid>
|
||||
|
||||
<Grid Grid.Row="1" Grid.Column="2" RowDefinitions="Auto,Auto,*">
|
||||
<TextBlock Text="BOOLEAN (TRUE/FALSE) FIELDS" />
|
||||
<TextBlock Grid.Row="1" Text="{CompiledBinding BoolUsage}" />
|
||||
<ListBox Grid.Row="2" ItemsSource="{CompiledBinding BoolFields}"/>
|
||||
<ListBox Grid.Row="2" DoubleTapped="ListBox_DoubleTapped" ItemsSource="{CompiledBinding BoolFields}"/>
|
||||
</Grid>
|
||||
|
||||
<Grid Grid.Row="1" Grid.Column="3" RowDefinitions="Auto,Auto,*">
|
||||
<TextBlock Text="ID FIELDS" />
|
||||
<TextBlock Grid.Row="1" Text="{CompiledBinding IdUsage}" />
|
||||
<ListBox Grid.Row="2" ItemsSource="{CompiledBinding IdFields}"/>
|
||||
<ListBox Grid.Row="2" DoubleTapped="ListBox_DoubleTapped" ItemsSource="{CompiledBinding IdFields}"/>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Window>
|
||||
|
||||
@@ -1,10 +1,14 @@
|
||||
using Avalonia;
|
||||
using LibationSearchEngine;
|
||||
using System;
|
||||
using System.Linq;
|
||||
|
||||
#nullable enable
|
||||
namespace LibationAvalonia.Dialogs
|
||||
{
|
||||
public partial class SearchSyntaxDialog : DialogWindow
|
||||
{
|
||||
public event EventHandler<string>? TagDoubleClicked;
|
||||
public string StringUsage { get; }
|
||||
public string NumberUsage { get; }
|
||||
public string BoolUsage { get; }
|
||||
@@ -51,5 +55,13 @@ namespace LibationAvalonia.Dialogs
|
||||
|
||||
DataContext = this;
|
||||
}
|
||||
|
||||
private void ListBox_DoubleTapped(object? sender, Avalonia.Input.TappedEventArgs e)
|
||||
{
|
||||
if (e.Source is StyledElement { DataContext: string tag })
|
||||
{
|
||||
TagDoubleClicked?.Invoke(this, tag);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@ namespace LibationAvalonia.Dialogs
|
||||
{
|
||||
TopMessage = UpdateMessage;
|
||||
Title = "Libation version 8.7.0 is now available.";
|
||||
DownloadLinkText = "Libation.8.7.0-macos-chardonnay.tar.gz";
|
||||
DownloadLinkText = "\r\nLibation.12.7.0-macOS-chardonnay-arm64.tgz ";
|
||||
ReleaseNotes = "New features:\r\n\r\n* 'Remove' now removes forever. Removed books won't be re-added on next scan\r\n* #406 : Right Click Menu for Stop-Light Icon\r\n* #398 : Grid, right-click, copy\r\n* Add option for user to choose custom temp folder\r\n* Build Docker image\r\n\r\nEnhancements\r\n\r\n* Illegal Char Replace dialog in Chardonnay\r\n* Filename character replacement allows replacing any char, not just illegal\r\n* #352 : Better error messages for license denial\r\n* Improve 'cancel download'\r\n\r\nThanks to @Mbucari (u/MSWMan), @pixil98 (u/pixil)\r\n\r\nLibation is a free, open source audible library manager for Windows. Decrypt, backup, organize, and search your audible library\r\n\r\nI intend to keep Libation free and open source, but if you want to leave a tip, who am I to argue?";
|
||||
OkText = "Yes";
|
||||
DataContext = this;
|
||||
|
||||
@@ -55,7 +55,7 @@ namespace LibationAvalonia.ViewModels
|
||||
|
||||
public void AddQuickFilterBtn() { if (SelectedNamedFilter != null) QuickFilters.Add(SelectedNamedFilter); }
|
||||
public async Task FilterBtn(string filterString) => await PerformFilter(new(filterString, null));
|
||||
public async Task FilterHelpBtn() => await new LibationAvalonia.Dialogs.SearchSyntaxDialog().ShowDialog(MainWindow);
|
||||
public void FilterHelpBtn() => MainWindow.ShowSearchSyntaxDialog();
|
||||
public void ToggleFirstFilterIsDefault() => FirstFilterIsDefault = !FirstFilterIsDefault;
|
||||
public async Task EditQuickFiltersAsync() => await new LibationAvalonia.Dialogs.EditQuickFilters().ShowDialog(MainWindow);
|
||||
public async Task PerformFilter(QuickFilters.NamedFilter? namedFilter)
|
||||
|
||||
@@ -67,10 +67,11 @@ namespace LibationAvalonia.ViewModels.Settings
|
||||
|
||||
public List<Configuration.KnownDirectories> KnownDirectories { get; } = new()
|
||||
{
|
||||
Configuration.KnownDirectories.UserProfile,
|
||||
Configuration.KnownDirectories.AppDir,
|
||||
Configuration.KnownDirectories.MyDocs,
|
||||
Configuration.KnownDirectories.LibationFiles,
|
||||
Configuration.KnownDirectories.MyMusic,
|
||||
Configuration.KnownDirectories.MyDocs,
|
||||
Configuration.KnownDirectories.AppDir,
|
||||
Configuration.KnownDirectories.UserProfile
|
||||
};
|
||||
|
||||
public string BooksText { get; } = Configuration.GetDescription(nameof(Configuration.Books));
|
||||
|
||||
@@ -223,5 +223,28 @@ namespace LibationAvalonia.Views
|
||||
}
|
||||
|
||||
private void setProgressVisible(bool visible) => ViewModel.DownloadProgress = visible ? 0 : null;
|
||||
|
||||
public SearchSyntaxDialog ShowSearchSyntaxDialog()
|
||||
{
|
||||
var dialog = new SearchSyntaxDialog();
|
||||
dialog.TagDoubleClicked += Dialog_TagDoubleClicked;
|
||||
dialog.Closed += Dialog_Closed;
|
||||
filterHelpBtn.IsEnabled = false;
|
||||
dialog.Show(this);
|
||||
return dialog;
|
||||
|
||||
void Dialog_Closed(object sender, EventArgs e)
|
||||
{
|
||||
dialog.TagDoubleClicked -= Dialog_TagDoubleClicked;
|
||||
filterHelpBtn.IsEnabled = true;
|
||||
}
|
||||
void Dialog_TagDoubleClicked(object sender, string tag)
|
||||
{
|
||||
var text = filterSearchTb.Text;
|
||||
filterSearchTb.Text = text.Insert(Math.Min(Math.Max(0, filterSearchTb.CaretIndex), text.Length), tag);
|
||||
filterSearchTb.CaretIndex += tag.Length;
|
||||
filterSearchTb.Focus();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -203,9 +203,10 @@ namespace LibationAvalonia
|
||||
|
||||
await displayControlAsync(MainForm.filterHelpBtn);
|
||||
|
||||
var filterHelp = new SearchSyntaxDialog();
|
||||
await filterHelp.ShowDialog(MainForm);
|
||||
|
||||
var searchDialog = MainForm.ShowSearchSyntaxDialog();
|
||||
var tcs = new TaskCompletionSource();
|
||||
searchDialog.Closed += (_, _) => tcs.SetResult();
|
||||
await tcs.Task;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -146,16 +146,7 @@ namespace LibationFileManager
|
||||
|
||||
protected override List<LongPath> GetFilePathsCustom(string productId)
|
||||
{
|
||||
// If user changed the BooksDirectory: reinitialize
|
||||
lock (bookDirectoryFilesLocker)
|
||||
{
|
||||
if (BooksDirectory != BookDirectoryFiles?.RootDirectory)
|
||||
{
|
||||
BookDirectoryFiles?.Dispose();
|
||||
BookDirectoryFiles = newBookDirectoryFiles();
|
||||
}
|
||||
}
|
||||
|
||||
ValidateBookDirectoryFiles();
|
||||
var regex = GetBookSearchRegex(productId);
|
||||
var diskFiles = BookDirectoryFiles?.FindFiles(regex) ?? [];
|
||||
|
||||
@@ -172,14 +163,25 @@ namespace LibationFileManager
|
||||
|
||||
public void Refresh()
|
||||
{
|
||||
lock (bookDirectoryFilesLocker)
|
||||
{
|
||||
BookDirectoryFiles ??= newBookDirectoryFiles();
|
||||
}
|
||||
|
||||
ValidateBookDirectoryFiles();
|
||||
BookDirectoryFiles?.RefreshFiles();
|
||||
}
|
||||
|
||||
private void ValidateBookDirectoryFiles()
|
||||
{
|
||||
lock (bookDirectoryFilesLocker)
|
||||
{
|
||||
if (BooksDirectory != BookDirectoryFiles?.RootDirectory)
|
||||
{
|
||||
//Will happen if the user changed the Books directory
|
||||
//or if BackgroundFileSystem errored out.
|
||||
BookDirectoryFiles?.Dispose();
|
||||
BookDirectoryFiles = newBookDirectoryFiles();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public LongPath? GetPath(string productId) => GetFilePath(productId);
|
||||
|
||||
public static async IAsyncEnumerable<FilePathCache.CacheEntry> FindAudiobooksAsync(LongPath searchDirectory, [EnumeratorCancellation] CancellationToken cancellationToken)
|
||||
|
||||
@@ -10,8 +10,8 @@ namespace LibationFileManager
|
||||
{
|
||||
public partial class Configuration
|
||||
{
|
||||
public static string ProcessDirectory { get; } = Path.GetDirectoryName(Exe.FileLocationOnDisk)!;
|
||||
public static string AppDir_Relative => $@".{Path.PathSeparator}{LIBATION_FILES_KEY}";
|
||||
public static string ProcessDirectory { get; } = Path.GetDirectoryName(Environment.ProcessPath)!;
|
||||
public static string AppDir_Relative => $@".{Path.DirectorySeparatorChar}{LIBATION_FILES_KEY}";
|
||||
public static string AppDir_Absolute => Path.GetFullPath(Path.Combine(ProcessDirectory, LIBATION_FILES_KEY));
|
||||
public static string MyDocs => Path.GetFullPath(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments), "Libation"));
|
||||
public static string MyMusic => Path.GetFullPath(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyMusic), "Libation"));
|
||||
|
||||
@@ -43,6 +43,8 @@ public class LibationContributor
|
||||
GitHubUser("patienttruth"),
|
||||
GitHubUser("stickystyle"),
|
||||
GitHubUser("cherez"),
|
||||
GitHubUser("delebash"),
|
||||
GitHubUser("twsouthwick"),
|
||||
]);
|
||||
|
||||
private LibationContributor(string name, LibationContributorType type,Uri link)
|
||||
|
||||
Reference in New Issue
Block a user