diff --git a/amp.Database/ExtensionClasses/DbContextExtensions.cs b/amp.Database/ExtensionClasses/DbContextExtensions.cs
index 3efa3c52..bb953b1c 100644
--- a/amp.Database/ExtensionClasses/DbContextExtensions.cs
+++ b/amp.Database/ExtensionClasses/DbContextExtensions.cs
@@ -24,8 +24,9 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
*/
#endregion
+using System.Reflection;
+using amp.Shared.Interfaces;
using Microsoft.EntityFrameworkCore;
-using Microsoft.EntityFrameworkCore.ChangeTracking;
namespace amp.Database.ExtensionClasses;
@@ -35,36 +36,47 @@ namespace amp.Database.ExtensionClasses;
public static class DbContextExtensions
{
///
- /// Updates the specified range of entities, saves the possible changes and stops tracking the if the is set to true.
+ /// Upserts the specified entity range into the database and saves the changes.
///
- /// The type of entity being operated on by this set.
- /// The database context the entities belong to.
- /// The entities to update.
- /// if set to true the is called.
- /// A task that represents the asynchronous save operation. The task result contains the number of state entries written to the database.
- public static async Task UpdateRangeAndSave(this DbContext context, IEnumerable entities, bool clearTracking = true) where TEntity : class
+ /// The type of the entity.
+ /// The database context.
+ /// The entities to upsert.
+ public static async Task UpsertRange(this DbContext context, params TEntity[] entities)
+ where TEntity : class, IEntity
{
- return await context.UpdateRangeAndSave(entities.ToArray(), clearTracking);
+ var toInsert = entities.Where(f => f.Id == 0).ToList();
+ var toUpdate = entities.Where(f => f.Id != 0).ToList();
+
+ foreach (var entity in toUpdate)
+ {
+ var update = await context.Set().FirstOrDefaultAsync(f => f.Id == entity.Id);
+ if (update != null)
+ {
+ UpdateEntity(update, entity);
+ await context.SaveChangesAsync();
+ }
+ }
+
+ if (toInsert.Count > 0)
+ {
+ context.Set().AddRange(toInsert);
+ await context.SaveChangesAsync();
+ }
}
- ///
- /// Updates the specified range of entities, saves the possible changes and stops tracking the if the is set to true.
- ///
- /// The type of entity being operated on by this set.
- /// The database context the entities belong to.
- /// The entities to update.
- /// if set to true the is called.
- /// A task that represents the asynchronous save operation. The task result contains the number of state entries written to the database.
- public static async Task UpdateRangeAndSave(this DbContext context, TEntity[] entities, bool clearTracking = true) where TEntity : class
+ private static void UpdateEntity(IEntity destination, IEntity source)
{
- context.Set().UpdateRange(entities);
- var result = await context.SaveChangesAsync();
+ var propertyInfos = destination.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public);
- if (clearTracking)
+ foreach (var propertyInfo in propertyInfos)
{
- context.ChangeTracker.Clear();
- }
+ if (destination.Id != 0 && propertyInfo.Name == nameof(IEntity.Id) || propertyInfo.Name == nameof(IRowVersionEntity.RowVersion))
+ {
+ continue;
+ }
- return result;
+ var sourceValue = propertyInfo.GetValue(source);
+ propertyInfo.SetValue(destination, sourceValue);
+ }
}
}
\ No newline at end of file
diff --git a/amp.EtoForms/Classes/AlbumTrackMethods.cs b/amp.EtoForms/Classes/AlbumTrackMethods.cs
index 8a0e46f7..fe69c1c6 100644
--- a/amp.EtoForms/Classes/AlbumTrackMethods.cs
+++ b/amp.EtoForms/Classes/AlbumTrackMethods.cs
@@ -26,7 +26,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
using System.Collections.ObjectModel;
using amp.Database;
-using amp.EtoForms.Models;
+using amp.EtoForms.DtoClasses;
using Eto.Forms;
using EtoForms.Controls.Custom.Utilities;
using Microsoft.EntityFrameworkCore;
diff --git a/amp.EtoForms/Dialogs/DialogModifySavedQueue.cs b/amp.EtoForms/Dialogs/DialogModifySavedQueue.cs
index 90739b17..6aa80c22 100644
--- a/amp.EtoForms/Dialogs/DialogModifySavedQueue.cs
+++ b/amp.EtoForms/Dialogs/DialogModifySavedQueue.cs
@@ -26,8 +26,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
using System.Collections.ObjectModel;
using amp.Database;
-using amp.Database.DataModel;
-using amp.Database.ExtensionClasses;
+using amp.EtoForms.DtoClasses;
using amp.EtoForms.Utilities;
using amp.Shared.Localization;
using Eto.Drawing;
@@ -103,14 +102,14 @@ public DialogModifySavedQueue(AmpContext context, long queueId)
new GridColumn
{
DataCell = new TextBoxCell(nameof(QueueTrack.QueueIndex)),
- HeaderText = UI.QueueCreated,
+ HeaderText = UI.QueueIndex,
},
new GridColumn
{
DataCell = new TextBoxCell
{
Binding = Binding
- .Property((QueueTrack qs) => TrackDisplayNameGenerate.GetAudioTrackName(qs.AudioTrack!))
+ .Property((QueueTrack qs) => TrackDisplayNameGenerate.GetAudioTrackName(qs.AudioTrack))
.Convert(s => s)
.Cast(),
}, Expand = true,
@@ -211,7 +210,7 @@ private async Task ListQueue()
{
queueTracks.Clear();
foreach (var queueTrack in await context.QueueTracks.Include(f => f.AudioTrack).Where(f => f.QueueSnapshotId == queueId)
- .OrderBy(f => f.QueueIndex).AsNoTracking().ToListAsync())
+ .OrderBy(f => f.QueueIndex).Select(f => Globals.AutoMapper.Map(f)).ToListAsync())
{
queueTracks.Add(queueTrack);
}
@@ -225,6 +224,7 @@ private async void DialogModifySavedQueue_Shown(object? sender, EventArgs e)
private async void BtnSaveAndClose_Click(object? sender, EventArgs e)
{
+ context.ChangeTracker.Clear();
await using var transaction = await context.Database.BeginTransactionAsync();
await Globals.LoggerSafeInvokeAsync(async () =>
@@ -233,12 +233,30 @@ await Globals.LoggerSafeInvokeAsync(async () =>
context.QueueTracks.RemoveRange(itemsToDelete);
await context.SaveChangesAsync();
- await context.UpdateRangeAndSave(queueTracks);
+ foreach (var queueTrack in queueTracks)
+ {
+ await UpdateTrack(context, queueTrack);
+ }
+
+ await context.SaveChangesAsync();
+ await transaction.CommitAsync();
}, async (_) => await transaction.RollbackAsync());
Close(true);
}
+ private static async Task UpdateTrack(AmpContext context, QueueTrack queueTrack)
+ {
+ var track = await context.QueueTracks.FirstAsync(f => f.Id == queueTrack.Id);
+ track.AudioTrackId = queueTrack.AudioTrackId;
+ track.CreatedAtUtc = queueTrack.CreatedAtUtc;
+ track.ModifiedAtUtc = queueTrack.ModifiedAtUtc;
+ track.QueueSnapshotId = queueTrack.QueueSnapshotId;
+ track.QueueIndex = queueTrack.QueueIndex;
+
+ await context.SaveChangesAsync();
+ }
+
private void BtnCancel_Click(object? sender, EventArgs e)
{
Close(false);
diff --git a/amp.EtoForms/Dialogs/DialogUpdateTagData.cs b/amp.EtoForms/Dialogs/DialogUpdateTagData.cs
index 688fb97a..85be7488 100644
--- a/amp.EtoForms/Dialogs/DialogUpdateTagData.cs
+++ b/amp.EtoForms/Dialogs/DialogUpdateTagData.cs
@@ -25,7 +25,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
#endregion
using amp.Database;
-using amp.EtoForms.Models;
+using amp.EtoForms.DtoClasses;
using amp.Shared.Classes;
using amp.Shared.Localization;
using Eto.Drawing;
diff --git a/amp.EtoForms/Models/Album.cs b/amp.EtoForms/DtoClasses/Album.cs
similarity index 91%
rename from amp.EtoForms/Models/Album.cs
rename to amp.EtoForms/DtoClasses/Album.cs
index ff03f738..2351fea9 100644
--- a/amp.EtoForms/Models/Album.cs
+++ b/amp.EtoForms/DtoClasses/Album.cs
@@ -28,7 +28,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
using System.Runtime.CompilerServices;
using amp.Shared.Interfaces;
-namespace amp.EtoForms.Models;
+namespace amp.EtoForms.DtoClasses;
///
/// A class for album.
@@ -56,7 +56,7 @@ public DateTime? ModifiedAtUtc
if (modifiedAtUtc != value)
{
modifiedAtUtc = value;
- OnPropertyChanged(nameof(ModifiedAtUtc));
+ OnPropertyChanged();
}
}
}
@@ -71,7 +71,7 @@ public DateTime CreatedAtUtc
if (createdAtUtc != value)
{
createdAtUtc = value;
- OnPropertyChanged(nameof(CreatedAtUtc));
+ OnPropertyChanged();
}
}
}
@@ -86,7 +86,7 @@ public string AlbumName
if (albumName != value)
{
albumName = value;
- OnPropertyChanged(nameof(AlbumName));
+ OnPropertyChanged();
}
}
}
diff --git a/amp.EtoForms/Models/AlbumTrack.cs b/amp.EtoForms/DtoClasses/AlbumTrack.cs
similarity index 88%
rename from amp.EtoForms/Models/AlbumTrack.cs
rename to amp.EtoForms/DtoClasses/AlbumTrack.cs
index a7827d96..3277b9f6 100644
--- a/amp.EtoForms/Models/AlbumTrack.cs
+++ b/amp.EtoForms/DtoClasses/AlbumTrack.cs
@@ -29,7 +29,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
using amp.EtoForms.Utilities;
using amp.Shared.Interfaces;
-namespace amp.EtoForms.Models;
+namespace amp.EtoForms.DtoClasses;
///
/// A class for album tracks.
@@ -78,7 +78,7 @@ public DateTime? ModifiedAtUtc
if (modifiedAtUtc != value)
{
modifiedAtUtc = value;
- OnPropertyChanged(nameof(ModifiedAtUtc));
+ OnPropertyChanged();
}
}
}
@@ -93,7 +93,7 @@ public DateTime CreatedAtUtc
if (createdAtUtc != value)
{
createdAtUtc = value;
- OnPropertyChanged(nameof(CreatedAtUtc));
+ OnPropertyChanged();
}
}
}
@@ -108,7 +108,7 @@ public long AlbumId
if (albumId != value)
{
albumId = value;
- OnPropertyChanged(nameof(AlbumId));
+ OnPropertyChanged();
}
}
@@ -124,7 +124,7 @@ public long AudioTrackId
if (trackId != value)
{
trackId = value;
- OnPropertyChanged(nameof(AudioTrackId));
+ OnPropertyChanged();
}
}
}
@@ -139,7 +139,7 @@ public int QueueIndex
if (queueIndex != value)
{
queueIndex = value;
- OnPropertyChanged(nameof(QueueIndex));
+ OnPropertyChanged();
}
}
@@ -155,7 +155,7 @@ public int QueueIndexAlternate
if (queueIndexAlternate != value)
{
queueIndexAlternate = value;
- OnPropertyChanged(nameof(QueueIndexAlternate));
+ OnPropertyChanged();
}
}
@@ -171,7 +171,7 @@ public AudioTrack? AudioTrack
if (track != value)
{
track = value;
- OnPropertyChanged(nameof(AudioTrack));
+ OnPropertyChanged();
}
}
}
@@ -186,7 +186,7 @@ public Album? Album
if (album != value)
{
album = value;
- OnPropertyChanged(nameof(Album));
+ OnPropertyChanged();
}
}
}
diff --git a/amp.EtoForms/Models/AudioTrack.cs b/amp.EtoForms/DtoClasses/AudioTrack.cs
similarity index 85%
rename from amp.EtoForms/Models/AudioTrack.cs
rename to amp.EtoForms/DtoClasses/AudioTrack.cs
index dcd99254..6db41b9c 100644
--- a/amp.EtoForms/Models/AudioTrack.cs
+++ b/amp.EtoForms/DtoClasses/AudioTrack.cs
@@ -30,7 +30,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
using amp.Shared.Enumerations;
using amp.Shared.Interfaces;
-namespace amp.EtoForms.Models;
+namespace amp.EtoForms.DtoClasses;
///
/// The track entity-independent representation.
@@ -110,7 +110,7 @@ public DateTime? ModifiedAtUtc
if (modifiedAtUtc != value)
{
modifiedAtUtc = value;
- OnPropertyChanged(nameof(ModifiedAtUtc));
+ OnPropertyChanged();
}
}
}
@@ -125,7 +125,7 @@ public DateTime CreatedAtUtc
if (createdAtUtc != value)
{
createdAtUtc = value;
- OnPropertyChanged(nameof(CreatedAtUtc));
+ OnPropertyChanged();
}
}
}
@@ -140,7 +140,7 @@ public int? PlayedByRandomize
if (playedByRandomize != value)
{
playedByRandomize = value;
- OnPropertyChanged(nameof(PlayedByRandomize));
+ OnPropertyChanged();
}
}
}
@@ -155,7 +155,7 @@ public int? PlayedByUser
if (playedByUser != value)
{
playedByUser = value;
- OnPropertyChanged(nameof(PlayedByUser));
+ OnPropertyChanged();
}
}
}
@@ -170,7 +170,7 @@ public int? SkippedEarlyCount
if (skippedEarlyCount != value)
{
skippedEarlyCount = value;
- OnPropertyChanged(nameof(SkippedEarlyCount));
+ OnPropertyChanged();
}
}
}
@@ -185,7 +185,7 @@ public string FileName
if (fileName != value)
{
fileName = value;
- OnPropertyChanged(nameof(FileName));
+ OnPropertyChanged();
}
}
}
@@ -200,7 +200,7 @@ public string? Artist
if (artist != value)
{
artist = value;
- OnPropertyChanged(nameof(Artist));
+ OnPropertyChanged();
}
}
}
@@ -215,7 +215,7 @@ public string? Album
if (album != value)
{
album = value;
- OnPropertyChanged(nameof(Album));
+ OnPropertyChanged();
}
}
}
@@ -230,7 +230,7 @@ public string? Track
if (track != value)
{
track = value;
- OnPropertyChanged(nameof(Track));
+ OnPropertyChanged();
}
}
}
@@ -245,7 +245,7 @@ public string? Year
if (year != value)
{
year = value;
- OnPropertyChanged(nameof(Year));
+ OnPropertyChanged();
}
}
}
@@ -260,7 +260,7 @@ public string? Lyrics
if (lyrics != value)
{
lyrics = value;
- OnPropertyChanged(nameof(Lyrics));
+ OnPropertyChanged();
}
}
}
@@ -275,7 +275,7 @@ public int? Rating
if (rating != value)
{
rating = value;
- OnPropertyChanged(nameof(Rating));
+ OnPropertyChanged();
}
}
}
@@ -290,7 +290,7 @@ public long? FileSizeBytes
if (fileSizeBytes != value)
{
fileSizeBytes = value;
- OnPropertyChanged(nameof(FileSizeBytes));
+ OnPropertyChanged();
}
}
}
@@ -305,7 +305,7 @@ public double PlaybackVolume
if (Math.Abs(playbackVolume - value) > Globals.FloatingPointTolerance)
{
playbackVolume = value;
- OnPropertyChanged(nameof(PlaybackVolume));
+ OnPropertyChanged();
}
}
}
@@ -320,7 +320,7 @@ public string? OverrideName
if (overrideName != value)
{
overrideName = value;
- OnPropertyChanged(nameof(OverrideName));
+ OnPropertyChanged();
}
}
}
@@ -335,7 +335,7 @@ public string? TagFindString
if (tagFindString != value)
{
tagFindString = value;
- OnPropertyChanged(nameof(TagFindString));
+ OnPropertyChanged();
}
}
}
@@ -350,7 +350,7 @@ public bool? TagRead
if (tagRead != value)
{
tagRead = value;
- OnPropertyChanged(nameof(TagRead));
+ OnPropertyChanged();
}
}
}
@@ -365,7 +365,7 @@ public string? FileNameNoPath
if (fileNameNoPath != value)
{
fileNameNoPath = value;
- OnPropertyChanged(nameof(FileNameNoPath));
+ OnPropertyChanged();
}
}
}
@@ -380,7 +380,7 @@ public string? Title
if (title != value)
{
title = value;
- OnPropertyChanged(nameof(Title));
+ OnPropertyChanged();
}
}
}
@@ -395,7 +395,7 @@ public byte[]? TrackImageData
if (trackImageData != value)
{
trackImageData = value;
- OnPropertyChanged(nameof(TrackImageData));
+ OnPropertyChanged();
}
}
}
@@ -410,7 +410,7 @@ public MusicFileType MusicFileType
if (musicFileType != value)
{
musicFileType = value;
- OnPropertyChanged(nameof(MusicFileType));
+ OnPropertyChanged();
}
}
}
diff --git a/amp.EtoForms/DtoClasses/QueueSnapshot.cs b/amp.EtoForms/DtoClasses/QueueSnapshot.cs
new file mode 100644
index 00000000..7c97fdbc
--- /dev/null
+++ b/amp.EtoForms/DtoClasses/QueueSnapshot.cs
@@ -0,0 +1,144 @@
+#region License
+/*
+MIT License
+
+Copyright(c) 2022 Petteri Kautonen
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+*/
+#endregion
+
+using System.ComponentModel;
+using System.Runtime.CompilerServices;
+using amp.Shared.Interfaces;
+
+namespace amp.EtoForms.DtoClasses;
+
+///
+/// An entity to save queues into the database.
+/// Implements the
+///
+///
+public class QueueSnapshot : IQueueSnapshot, INotifyPropertyChanged
+{
+ private long id;
+ private long albumId;
+ private string snapshotName = string.Empty;
+ private DateTime snapshotDate;
+ private DateTime? modifiedAtUtc;
+ private DateTime createdAtUtc;
+ private IList? queueTracks;
+ private Album? album;
+
+ ///
+ public long Id
+ {
+ get => id;
+ set => SetField(ref id, value);
+ }
+
+ ///
+ public long AlbumId
+ {
+ get => albumId;
+ set => SetField(ref albumId, value);
+ }
+
+ ///
+ public string SnapshotName
+ {
+ get => snapshotName;
+ set => SetField(ref snapshotName, value);
+ }
+
+ ///
+ public DateTime SnapshotDate
+ {
+ get => snapshotDate;
+ set => SetField(ref snapshotDate, value);
+ }
+
+ ///
+ public DateTime? ModifiedAtUtc
+ {
+ get => modifiedAtUtc;
+ set => SetField(ref modifiedAtUtc, value);
+ }
+
+ ///
+ public DateTime CreatedAtUtc
+ {
+ get => createdAtUtc;
+ set => SetField(ref createdAtUtc, value);
+ }
+
+ ///
+ /// Gets or sets the queue tracks belonging to this queue snapshot.
+ ///
+ /// The queued tracks.
+ public IList? QueueTracks
+ {
+ get => queueTracks;
+ set => SetField(ref queueTracks, value);
+ }
+
+ ///
+ /// Gets or sets the album of the queue snapshot.
+ ///
+ /// The album of the queue snapshot.
+ public Album? Album
+ {
+ get => album;
+ set => SetField(ref album, value);
+ }
+
+ ///
+ /// Occurs when a property value changes.
+ ///
+ public event PropertyChangedEventHandler? PropertyChanged;
+
+ ///
+ /// Called when property value changes.
+ ///
+ /// Name of the property.
+ protected virtual void OnPropertyChanged([CallerMemberName] string? propertyName = null)
+ {
+ PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
+ }
+
+ ///
+ /// Sets the property backing field value and calls the for the field property.
+ ///
+ /// The type of the field.
+ /// The field which value to set.
+ /// The value set for the field.
+ /// The name of the property.
+ /// true if the field value was changed and set, false otherwise.
+ protected bool SetField(ref T field, T value, [CallerMemberName] string? propertyName = null)
+ {
+ if (EqualityComparer.Default.Equals(field, value))
+ {
+ return false;
+ }
+
+ field = value;
+ OnPropertyChanged(propertyName);
+ return true;
+ }
+}
\ No newline at end of file
diff --git a/amp.EtoForms/DtoClasses/QueueTrack.cs b/amp.EtoForms/DtoClasses/QueueTrack.cs
new file mode 100644
index 00000000..f2e2cb0c
--- /dev/null
+++ b/amp.EtoForms/DtoClasses/QueueTrack.cs
@@ -0,0 +1,133 @@
+#region License
+/*
+MIT License
+
+Copyright(c) 2022 Petteri Kautonen
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+*/
+#endregion
+
+using amp.Shared.Interfaces;
+using System.ComponentModel;
+using System.Runtime.CompilerServices;
+
+namespace amp.EtoForms.DtoClasses;
+
+///
+/// A DTO class for queue track data.
+/// Implements the
+///
+///
+public class QueueTrack : IQueueTrack, INotifyPropertyChanged
+{
+ private long id;
+ private long audioTrackId;
+ private long queueSnapshotId;
+ private int queueIndex;
+ private DateTime? modifiedAtUtc;
+ private DateTime createdAtUtc;
+ private AudioTrack audioTrack = new();
+
+ ///
+ public long Id
+ {
+ get => id;
+ set => SetField(ref id, value);
+ }
+
+ ///
+ public long AudioTrackId
+ {
+ get => audioTrackId;
+ set => SetField(ref audioTrackId, value);
+ }
+
+ ///
+ public long QueueSnapshotId
+ {
+ get => queueSnapshotId;
+ set => SetField(ref queueSnapshotId, value);
+ }
+
+ ///
+ public int QueueIndex
+ {
+ get => queueIndex;
+ set => SetField(ref queueIndex, value);
+ }
+
+ ///
+ public DateTime? ModifiedAtUtc
+ {
+ get => modifiedAtUtc;
+ set => SetField(ref modifiedAtUtc, value);
+ }
+
+ ///
+ public DateTime CreatedAtUtc
+ {
+ get => createdAtUtc;
+ set => SetField(ref createdAtUtc, value);
+ }
+
+ ///
+ /// Gets or sets the audio track of this queue track.
+ ///
+ /// The audio track of this queue track.
+ public AudioTrack AudioTrack
+ {
+ get => audioTrack;
+ set => SetField(ref audioTrack, value);
+ }
+
+ ///
+ /// Occurs when a property value changes.
+ ///
+ public event PropertyChangedEventHandler? PropertyChanged;
+
+ ///
+ /// Called when [property changed].
+ ///
+ /// Name of the property.
+ protected virtual void OnPropertyChanged([CallerMemberName] string? propertyName = null)
+ {
+ PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
+ }
+
+ ///
+ /// Sets the property backing field value and calls the for the field property.
+ ///
+ /// The type of the field.
+ /// The field which value to set.
+ /// The value set for the field.
+ /// The name of the property.
+ /// true if the field value was changed and set, false otherwise.
+ protected bool SetField(ref T field, T value, [CallerMemberName] string? propertyName = null)
+ {
+ if (EqualityComparer.Default.Equals(field, value))
+ {
+ return false;
+ }
+
+ field = value;
+ OnPropertyChanged(propertyName);
+ return true;
+ }
+}
\ No newline at end of file
diff --git a/amp.EtoForms/FormMain.Events.cs b/amp.EtoForms/FormMain.Events.cs
index bcf6ab83..528db878 100644
--- a/amp.EtoForms/FormMain.Events.cs
+++ b/amp.EtoForms/FormMain.Events.cs
@@ -46,7 +46,8 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
using EtoForms.Controls.Custom.UserIdle;
using EtoForms.Controls.Custom.Utilities;
using Microsoft.EntityFrameworkCore;
-using AudioTrack = amp.EtoForms.Models.AudioTrack;
+using AlbumTrack = amp.EtoForms.DtoClasses.AlbumTrack;
+using AudioTrack = amp.EtoForms.DtoClasses.AudioTrack;
namespace amp.EtoForms;
@@ -104,7 +105,7 @@ await playbackOrder.MoveToQueueTopOrBottom(tracks, shift, e.Key == Keys.PageUp,
{
if (gvAudioTracks.SelectedItem != null)
{
- var albumTrack = (Models.AlbumTrack)gvAudioTracks.SelectedItem;
+ var albumTrack = (AlbumTrack)gvAudioTracks.SelectedItem;
await playbackManager.PlayAudioTrack(albumTrack, true);
e.Handled = true;
return;
@@ -192,9 +193,9 @@ private void FormMain_Closing(object? sender, CancelEventArgs e)
idleChecker.Dispose();
}
- private async Task GetNextAudioTrackFunc()
+ private async Task GetNextAudioTrackFunc()
{
- Models.AlbumTrack? result = null;
+ AlbumTrack? result = null;
await Application.Instance.InvokeAsync(async () =>
{
var nextTrackData = await playbackOrder.NextTrack(tracks);
@@ -224,6 +225,7 @@ await Application.Instance.InvokeAsync(() =>
{
var track = tracks.FirstOrDefault(f => f.AudioTrackId == e.AudioTrackId);
lbTracksTitle.Text = track?.GetAudioTrackName() ?? string.Empty;
+ currentTrackId = track != null ? e.AudioTrackId : 0;
btnPlayPause.CheckedChange -= PlayPauseToggle;
btnPlayPause.Checked = e.PlaybackState == PlaybackState.Playing;
btnPlayPause.CheckedChange += PlayPauseToggle;
@@ -231,6 +233,30 @@ await Application.Instance.InvokeAsync(() =>
});
}
+ private void LbTracksTitle_MouseDown(object? sender, MouseEventArgs e)
+ {
+ if (e.Buttons == MouseButtons.Primary && currentTrackId != 0)
+ {
+ var index = tracks.FindIndex(f => f.AudioTrackId == currentTrackId);
+ if (index != -1)
+ {
+ var indexFiltered = filteredTracks.FindIndex(f => f.AudioTrackId == currentTrackId);
+
+ if (indexFiltered != -1)
+ {
+ var dataSource = gvAudioTracks.DataStore.Cast().ToList();
+ var displayTrack = dataSource.FindIndex(f => f.AudioTrackId == currentTrackId);
+ if (displayTrack != -1)
+ {
+ gvAudioTracks.SelectedRow = displayTrack;
+ gvAudioTracks.ScrollToRow(displayTrack);
+ gvAudioTracks.Focus();
+ }
+ }
+ }
+ }
+ }
+
private void PlaybackManagerTrackChanged(object? sender, TrackChangedArgs e)
{
Application.Instance.Invoke(() =>
@@ -243,8 +269,9 @@ private void PlaybackManagerTrackChanged(object? sender, TrackChangedArgs e)
trackVolumeSlider.SuspendEventInvocation = false;
trackRatingSlider.SuspendEventInvocation = false;
lbTracksTitle.Text = track?.GetAudioTrackName() ?? string.Empty;
+ currentTrackId = track != null ? e.AudioTrackId : 0;
- var dataSource = gvAudioTracks.DataStore.Cast().ToList();
+ var dataSource = gvAudioTracks.DataStore.Cast().ToList();
var displayTrack = dataSource.FindIndex(f => f.AudioTrackId == e.AudioTrackId);
if (displayTrack != -1)
{
@@ -315,9 +342,9 @@ private async void PlayNextAudioTrackClick(object? sender, EventArgs e)
await playbackManager.PlayNextTrack(true);
}
- private async Task GetTrackById(long trackId)
+ private async Task GetTrackById(long trackId)
{
- return await Application.Instance.InvokeAsync(Models.AlbumTrack? () =>
+ return await Application.Instance.InvokeAsync(AlbumTrack? () =>
{
return tracks.FirstOrDefault(f => f.AudioTrackId == trackId);
});
@@ -428,7 +455,8 @@ private async void PlayPreviousClick(object? sender, EventArgs e)
private void ManageSavedQueues_Executed(object? sender, EventArgs e)
{
- new FormSavedQueues(context, LoadOrAppendQueue).ShowModal(this);
+ using var form = new FormSavedQueues(context, LoadOrAppendQueue);
+ form.ShowModal(this);
}
private async void SaveQueueCommand_Executed(object? sender, EventArgs e)
@@ -571,7 +599,7 @@ private void BtnStackQueueToggle_CheckedChange(object? sender, CheckedChangeEven
private void TrackInfoCommand_Executed(object? sender, EventArgs e)
{
- var track = (Models.AlbumTrack?)gvAudioTracks.SelectedItem;
+ var track = (AlbumTrack?)gvAudioTracks.SelectedItem;
if (track != null)
{
using var dialog = new FormDialogTrackInfo(track.AudioTrack!, AudioTrackChanged);
@@ -598,11 +626,11 @@ private async void AudioTrackChanged(object? sender, AudioTrackChangedEventArgs
}
}
- private async void QueryDivider_QueryCompleted(object? sender, QueryCompletedEventArgs e)
+ private async void QueryDivider_QueryCompleted(object? sender, QueryCompletedEventArgs e)
{
- tracks = new ObservableCollection(e.ResultList);
+ tracks = new ObservableCollection(e.ResultList);
- tracks = new ObservableCollection(tracks.OrderBy(f => f.DisplayName));
+ tracks = new ObservableCollection(tracks.OrderBy(f => f.DisplayName));
await Application.Instance.InvokeAsync(() =>
{
@@ -611,7 +639,7 @@ await Application.Instance.InvokeAsync(() =>
if (!string.IsNullOrWhiteSpace(tbSearch.Text))
{
filteredTracks =
- new ObservableCollection(tracks
+ new ObservableCollection(tracks
.Where(f => f.AudioTrack!.Match(tbSearch.Text))
.ToList());
}
diff --git a/amp.EtoForms/FormMain.Fields.cs b/amp.EtoForms/FormMain.Fields.cs
index 03a85e88..982afbe7 100644
--- a/amp.EtoForms/FormMain.Fields.cs
+++ b/amp.EtoForms/FormMain.Fields.cs
@@ -25,8 +25,8 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
#endregion
using amp.Database.QueryHelpers;
+using amp.EtoForms.DtoClasses;
using amp.EtoForms.Forms;
-using amp.EtoForms.Models;
using amp.Shared.Localization;
using Eto.Drawing;
using Eto.Forms;
@@ -109,6 +109,7 @@ partial class FormMain
private WindowState previousWindowState;
private readonly FormAlbumImage formAlbumImage = new();
private QueryDivider? queryDivider;
+ private long currentTrackId;
// About
private readonly AboutDialog aboutDialog = new();
diff --git a/amp.EtoForms/FormMain.Layout.cs b/amp.EtoForms/FormMain.Layout.cs
index 16f7e858..bb83218e 100644
--- a/amp.EtoForms/FormMain.Layout.cs
+++ b/amp.EtoForms/FormMain.Layout.cs
@@ -26,7 +26,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
-using amp.EtoForms.Models;
+using amp.EtoForms.DtoClasses;
using amp.EtoForms.Properties;
using amp.Shared.Localization;
using Eto.Drawing;
@@ -245,6 +245,9 @@ private StackLayout CreateMainContent()
btnClearSearch = new ImageOnlyButton(ClearSearchClick, Size20.ic_fluent_eraser_20_filled) { ImageColor = Color.Parse(Globals.ColorConfiguration.ClearSearchButtonColor), Size = Globals.SmallImageButtonDefaultSize, ToolTip = UI.ClearSearch, };
+ lbTracksTitle.Cursor = Cursors.Pointer;
+ lbTracksTitle.MouseDown += LbTracksTitle_MouseDown;
+
var result = new StackLayout
{
Items =
diff --git a/amp.EtoForms/FormMain.Methods.cs b/amp.EtoForms/FormMain.Methods.cs
index bdd6ce09..30fe5d24 100644
--- a/amp.EtoForms/FormMain.Methods.cs
+++ b/amp.EtoForms/FormMain.Methods.cs
@@ -29,9 +29,9 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
using System.Reflection;
using amp.Database.QueryHelpers;
using amp.EtoForms.Dialogs;
+using amp.EtoForms.DtoClasses;
using amp.EtoForms.ExtensionClasses;
using amp.EtoForms.Forms.Enumerations;
-using amp.EtoForms.Models;
using amp.EtoForms.Properties;
using amp.EtoForms.Utilities;
using amp.Playback.Classes;
diff --git a/amp.EtoForms/FormMain.Properties.cs b/amp.EtoForms/FormMain.Properties.cs
index 550cfbc9..a62d5af4 100644
--- a/amp.EtoForms/FormMain.Properties.cs
+++ b/amp.EtoForms/FormMain.Properties.cs
@@ -24,6 +24,8 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
*/
#endregion
+using amp.EtoForms.DtoClasses;
+
namespace amp.EtoForms;
public partial class FormMain
@@ -63,7 +65,7 @@ private long SelectedAlbumTrackId
{
if (gvAudioTracks.SelectedItem != null)
{
- var albumTrackId = ((Models.AlbumTrack)gvAudioTracks.SelectedItem).Id;
+ var albumTrackId = ((AlbumTrack)gvAudioTracks.SelectedItem).Id;
return albumTrackId;
}
@@ -77,7 +79,7 @@ private IEnumerable SelectedAlbumTrackIds
{
foreach (var selectedItem in gvAudioTracks.SelectedItems)
{
- var trackId = ((Models.AlbumTrack)selectedItem).Id;
+ var trackId = ((AlbumTrack)selectedItem).Id;
yield return trackId;
}
}
@@ -89,7 +91,7 @@ private bool QueuedItemsInSelection
{
foreach (var selectedItem in gvAudioTracks.SelectedItems)
{
- return ((Models.AlbumTrack)selectedItem).QueueIndex > 0;
+ return ((AlbumTrack)selectedItem).QueueIndex > 0;
}
return false;
@@ -102,7 +104,7 @@ private bool AlternateQueuedItemsInSelection
{
foreach (var selectedItem in gvAudioTracks.SelectedItems)
{
- return ((Models.AlbumTrack)selectedItem).QueueIndexAlternate > 0;
+ return ((AlbumTrack)selectedItem).QueueIndexAlternate > 0;
}
return false;
diff --git a/amp.EtoForms/FormMain.cs b/amp.EtoForms/FormMain.cs
index 761e8d67..79233fc8 100644
--- a/amp.EtoForms/FormMain.cs
+++ b/amp.EtoForms/FormMain.cs
@@ -34,8 +34,8 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
using Eto.Drawing;
using Eto.Forms;
using EtoForms.Controls.Custom.UserIdle;
-using AlbumTrack = amp.EtoForms.Models.AlbumTrack;
-using AudioTrack = amp.EtoForms.Models.AudioTrack;
+using AlbumTrack = amp.EtoForms.DtoClasses.AlbumTrack;
+using AudioTrack = amp.EtoForms.DtoClasses.AudioTrack;
namespace amp.EtoForms;
@@ -62,7 +62,7 @@ public FormMain()
positionSaveLoad = new FormSaveLoadPosition(this);
- playbackOrder = new PlaybackOrder(Globals.Settings,
+ playbackOrder = new PlaybackOrder(Globals.Settings,
Globals.Settings.StackQueueRandomPercentage, UpdateQueueFunc);
// ReSharper disable once StringLiteralTypo
@@ -76,7 +76,7 @@ public FormMain()
Database.Globals.ConnectionString = $"Data Source={databaseFile}";
- playbackManager = new PlaybackManager(Globals.Logger, GetNextAudioTrackFunc, GetTrackById,
+ playbackManager = new PlaybackManager(Globals.Logger, GetNextAudioTrackFunc, GetTrackById,
() => Application.Instance.RunIteration(), Globals.Settings.PlaybackRetryCount);
context = new AmpContext();
@@ -112,9 +112,9 @@ private void TestStuff_Executed(object? sender, EventArgs e)
private ObservableCollection tracks = new();
private ObservableCollection filteredTracks = new();
- private readonly PlaybackManager playbackManager;
- private QuietHourHandler quietHourHandler;
- private readonly PlaybackOrder playbackOrder;
+ private readonly PlaybackManager playbackManager;
+ private QuietHourHandler quietHourHandler;
+ private readonly PlaybackOrder playbackOrder;
private readonly AmpContext context;
private readonly UserIdleChecker idleChecker;
private readonly System.Timers.Timer tmMessageQueueTimer = new(1000);
diff --git a/amp.EtoForms/Forms/EventArguments/AudioTrackChangedEventArgs.cs b/amp.EtoForms/Forms/EventArguments/AudioTrackChangedEventArgs.cs
index 71d2cf8d..2a4a7aad 100644
--- a/amp.EtoForms/Forms/EventArguments/AudioTrackChangedEventArgs.cs
+++ b/amp.EtoForms/Forms/EventArguments/AudioTrackChangedEventArgs.cs
@@ -24,7 +24,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
*/
#endregion
-using amp.EtoForms.Models;
+using amp.EtoForms.DtoClasses;
namespace amp.EtoForms.Forms.EventArguments;
diff --git a/amp.EtoForms/Forms/FormAlbums.cs b/amp.EtoForms/Forms/FormAlbums.cs
index 2288991e..2b775ad7 100644
--- a/amp.EtoForms/Forms/FormAlbums.cs
+++ b/amp.EtoForms/Forms/FormAlbums.cs
@@ -151,7 +151,9 @@ await Globals.LoggerSafeInvokeAsync(async () =>
var toUpdate = dataSource!.Where(f => existingIds.Contains(f.Id)).ToList();
- context.Albums.UpdateRange(toUpdate);
+
+
+ await context.UpsertRange(toUpdate.Cast().ToArray());
await context.SaveChangesAsync();
await transaction.CommitAsync();
context.ChangeTracker.Clear();
diff --git a/amp.EtoForms/Forms/FormDialogTrackInfo.cs b/amp.EtoForms/Forms/FormDialogTrackInfo.cs
index 84c1c523..75e22094 100644
--- a/amp.EtoForms/Forms/FormDialogTrackInfo.cs
+++ b/amp.EtoForms/Forms/FormDialogTrackInfo.cs
@@ -24,8 +24,8 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
*/
#endregion
+using amp.EtoForms.DtoClasses;
using amp.EtoForms.Forms.EventArguments;
-using amp.EtoForms.Models;
using amp.Shared.Classes;
using amp.Shared.Localization;
using ATL;
diff --git a/amp.EtoForms/Forms/FormSavedQueues.cs b/amp.EtoForms/Forms/FormSavedQueues.cs
index 23875d0b..08d40800 100644
--- a/amp.EtoForms/Forms/FormSavedQueues.cs
+++ b/amp.EtoForms/Forms/FormSavedQueues.cs
@@ -26,8 +26,9 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
using System.Collections.ObjectModel;
using amp.Database;
-using amp.Database.DataModel;
+using amp.Database.ExtensionClasses;
using amp.EtoForms.Dialogs;
+using amp.EtoForms.DtoClasses;
using amp.EtoForms.Forms.Enumerations;
using amp.EtoForms.Layout;
using amp.EtoForms.Utilities;
@@ -38,6 +39,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
using EtoForms.Controls.Custom.Utilities;
using FluentIcons.Resources.Filled;
using Microsoft.EntityFrameworkCore;
+using Album = amp.EtoForms.DtoClasses.Album;
namespace amp.EtoForms.Forms;
@@ -133,7 +135,7 @@ public FormSavedQueues(AmpContext context, Func, long, Que
DataCell = new TextBoxCell
{
Binding = Binding
- .Property((QueueTrack qs) => TrackDisplayNameGenerate.GetAudioTrackName(qs.AudioTrack!))
+ .Property((QueueTrack qs) => TrackDisplayNameGenerate.GetAudioTrackName(qs.AudioTrack))
.Convert(s => s)
.Cast(),
}, Expand = true,
@@ -144,9 +146,9 @@ public FormSavedQueues(AmpContext context, Func, long, Que
DataStore = queueTracks,
};
- var panel1 = new Panel {Content = gvAlbumQueues, Size = new Size(700, 250),};
- var panel2 = new Panel {Content = gvAlbumQueueTracks, Size = new Size(700, 250),};
-
+ var panel1 = new Panel { Content = gvAlbumQueues, Size = new Size(700, 250), };
+ var panel2 = new Panel { Content = gvAlbumQueueTracks, Size = new Size(700, 250), };
+
Content = new TableLayout
{
Rows =
@@ -182,7 +184,7 @@ public FormSavedQueues(AmpContext context, Func, long, Que
private async void BtnLoadQueueClick(object? sender, EventArgs e)
{
- var albumId = ((Models.Album?)(cmbAlbumSelect.SelectedValue))?.Id;
+ var albumId = ((Album?)(cmbAlbumSelect.SelectedValue))?.Id;
if (SelectedQueueId != 0 && albumId != null)
{
var queueData = new Dictionary(context.QueueTracks
@@ -199,7 +201,7 @@ private async void BtnLoadQueueClick(object? sender, EventArgs e)
private void CopyToFolderClick(object? sender, EventArgs e)
{
- var fileNames = queueTracks.Select(f => f.AudioTrack!.FileName).ToList();
+ var fileNames = queueTracks.Select(f => f.AudioTrack.FileName).ToList();
if (selectFolderDialog.ShowDialog(this) == DialogResult.Ok)
{
Globals.LoggerSafeInvoke(() =>
@@ -232,6 +234,7 @@ private async Task Save()
await using var transaction = await context.Database.BeginTransactionAsync();
await Globals.LoggerSafeInvokeAsync(async () =>
{
+ context.ChangeTracker.Clear();
var removeTracks = await context.QueueTracks.Where(f => queuesToDelete.Contains(f.QueueSnapshotId))
.ToListAsync();
context.QueueTracks.RemoveRange(removeTracks);
@@ -242,7 +245,9 @@ await Globals.LoggerSafeInvokeAsync(async () =>
context.QueueSnapshots.RemoveRange(removeQueues);
await context.SaveChangesAsync();
- context.QueueSnapshots.UpdateRange(queueSnapshots);
+ var updateData = queueSnapshots.Select(f => Globals.AutoMapper.Map(f)).ToArray();
+
+ await context.UpsertRange(updateData);
await context.SaveChangesAsync();
await transaction.CommitAsync();
@@ -265,19 +270,19 @@ private void GvAlbumQueues_SelectionChanged(object? sender, EventArgs e)
foreach (var queueSnapshot in context.QueueTracks.Include(f => f.AudioTrack).Where(f => f.QueueSnapshotId == queueId)
.OrderBy(f => f.QueueIndex).AsNoTracking())
{
- queueTracks.Add(queueSnapshot);
+ queueTracks.Add(Globals.AutoMapper.Map(queueSnapshot));
}
}
private void RefreshQueueSnapshots(long? arg)
{
- arg ??= ((Models.Album?)(cmbAlbumSelect.SelectedValue))?.Id;
+ arg ??= ((Album?)(cmbAlbumSelect.SelectedValue))?.Id;
queueSnapshots.Clear();
foreach (var queueSnapshot in context.QueueSnapshots.Where(f => !queuesToDelete.Contains(f.Id) && f.AlbumId == arg).OrderBy(f => f.SnapshotName).AsNoTracking())
{
- queueSnapshots.Add(queueSnapshot);
+ queueSnapshots.Add(Globals.AutoMapper.Map(queueSnapshot));
}
}
@@ -289,7 +294,8 @@ private Task SelectedValueChanged(long? arg)
private void EditClick(object? sender, EventArgs e)
{
- new DialogModifySavedQueue(context, SelectedQueueId).ShowModal(this);
+ using var dialog = new DialogModifySavedQueue(context, SelectedQueueId);
+ dialog.ShowModal(this);
}
private long SelectedQueueId => ((QueueSnapshot?)gvAlbumQueues.SelectedItem)?.Id ?? 0;
diff --git a/amp.EtoForms/Globals.cs b/amp.EtoForms/Globals.cs
index 411ef44c..303070a8 100644
--- a/amp.EtoForms/Globals.cs
+++ b/amp.EtoForms/Globals.cs
@@ -328,12 +328,17 @@ internal static IMapper AutoMapper
{
mapperConfiguration ??= new MapperConfiguration(cfg =>
{
- cfg.CreateMap();
- cfg.CreateMap();
- cfg.CreateMap();
- cfg.CreateMap();
- cfg.CreateMap();
- cfg.CreateMap();
+ cfg.CreateMap();
+ cfg.CreateMap();
+ cfg.CreateMap();
+ cfg.CreateMap();
+ cfg.CreateMap();
+
+ cfg.CreateMap();
+ cfg.CreateMap();
+ cfg.CreateMap();
+ cfg.CreateMap();
+ cfg.CreateMap();
});
mapper ??= mapperConfiguration.CreateMapper();
diff --git a/amp.EtoForms/Layout/ReusableControls.cs b/amp.EtoForms/Layout/ReusableControls.cs
index 7654626a..30135b34 100644
--- a/amp.EtoForms/Layout/ReusableControls.cs
+++ b/amp.EtoForms/Layout/ReusableControls.cs
@@ -26,6 +26,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
using amp.Database;
using amp.Database.ExtensionClasses;
+using amp.EtoForms.DtoClasses;
using Eto.Forms;
namespace amp.EtoForms.Layout;
@@ -38,7 +39,7 @@ public static ComboBox CreateAlbumSelectCombo(Func? selectedValueCh
cmbAlbumSelect.SelectedValueChanged += async (_, _) =>
{
- var id = ((Models.Album?)cmbAlbumSelect.SelectedValue)?.Id;
+ var id = ((Album?)cmbAlbumSelect.SelectedValue)?.Id;
if (selectedValueChanged != null)
{
@@ -55,7 +56,7 @@ private static async Task UpdateAlbumDataSource(ComboBox cmbAlbumSelect, AmpCont
{
var albumsEntity = await context.Albums.GetUnTrackedList(f => f.AlbumName, new long[] { 1, });
- var albums = albumsEntity.Select(f => Globals.AutoMapper.Map(f)).ToList();
+ var albums = albumsEntity.Select(f => Globals.AutoMapper.Map(f)).ToList();
cmbAlbumSelect.DataStore = albums;
if (albums.Any(f => f.Id == currentAlbumId))
diff --git a/amp.EtoForms/Utilities/FileUtils.cs b/amp.EtoForms/Utilities/FileUtils.cs
new file mode 100644
index 00000000..1078e1b4
--- /dev/null
+++ b/amp.EtoForms/Utilities/FileUtils.cs
@@ -0,0 +1,51 @@
+#region License
+/*
+MIT License
+
+Copyright(c) 2022 Petteri Kautonen
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+*/
+#endregion
+
+namespace amp.EtoForms.Utilities;
+
+///
+/// Some file and folder utilities.
+///
+public static class FileUtils
+{
+ ///
+ /// Gets the first existing file from the specified list of files.
+ ///
+ /// The files.
+ /// A file name.
+ public static string FirstExistingFile(params string[] files)
+ {
+ foreach (var file in files)
+ {
+ if (File.Exists(file))
+ {
+ return file;
+ }
+ }
+
+ return files.LastOrDefault() ?? string.Empty;
+ }
+}
\ No newline at end of file
diff --git a/amp.EtoForms/Utilities/Help.cs b/amp.EtoForms/Utilities/Help.cs
index 21c824bd..4d53fa8b 100644
--- a/amp.EtoForms/Utilities/Help.cs
+++ b/amp.EtoForms/Utilities/Help.cs
@@ -25,6 +25,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
#endregion
using System.Reflection;
+using amp.Shared.Classes;
using Eto.Forms;
namespace amp.EtoForms.Utilities;
@@ -86,18 +87,22 @@ internal static void LaunchHelp()
/// Launches the help from the folder specified in the settings.
///
/// The parent for a dialog in case the help file is not found.
- /// true if the web browser for the help file was successfully launched, false otherwise.
- internal static bool LaunchHelpFromSettings(Control parent)
+ internal static void LaunchHelpFromSettings(Control parent)
{
- var helpFile = Path.Combine(Globals.Settings.HelpFolder, "index.html");
+
+ var helpFileFallback = Path.Combine(Globals.Settings.HelpFolder, $"amp-en-{UtilityOS.OsNameLowerCase}", "index.html");
+ var helpFileCurrentLocale = Path.Combine(Globals.Settings.HelpFolder, $"amp-{Globals.Settings.Locale}-{UtilityOS.OsNameLowerCase}", "index.html");
+ var indexHtml = Path.Combine(Globals.Settings.HelpFolder, "index.html");
+
+ var helpFile = FileUtils.FirstExistingFile(helpFileCurrentLocale, helpFileFallback, indexHtml);
+
if (File.Exists(helpFile))
{
var uri = new Uri(helpFile).AbsoluteUri;
Application.Instance.Open(uri);
- return true;
+ return;
}
MessageBox.Show(parent, Shared.Localization.Messages.PleaseSetTheHelpPathFromTheSettings, Shared.Localization.Messages.Information);
- return false;
}
}
\ No newline at end of file
diff --git a/amp.EtoForms/amp.EtoForms.csproj b/amp.EtoForms/amp.EtoForms.csproj
index db115a86..a6452788 100644
--- a/amp.EtoForms/amp.EtoForms.csproj
+++ b/amp.EtoForms/amp.EtoForms.csproj
@@ -22,6 +22,12 @@
OSX
+
+
+ $(ProjectDir)\FormMain.cs
+
+
+
@@ -64,11 +70,6 @@
-
-
-
-
-
True
diff --git a/amp.Shared/Classes/UtilityOS.cs b/amp.Shared/Classes/UtilityOS.cs
index 5a259b76..204399fc 100644
--- a/amp.Shared/Classes/UtilityOS.cs
+++ b/amp.Shared/Classes/UtilityOS.cs
@@ -99,4 +99,42 @@ public static T GetValueForOSNotNull(T windowsValue, T linuxValue, T macValue
/// true if the current operating system is Linux; otherwise, false.
// ReSharper disable once InconsistentNaming, OS is upper case
public static bool IsLinuxOS => RuntimeInformation.IsOSPlatform(OSPlatform.Linux);
+
+ ///
+ /// The macOS operating system name in lower case ("macos").
+ ///
+ public const string MacOSNameLowerCase = "macos";
+
+ ///
+ /// The Windows operating system name in lower case ("windows").
+ ///
+ public const string WindowsNameLowerCase = "windows";
+
+ ///
+ /// The Linux operating system name in lower case ("linux").
+ ///
+ public const string LinuxNameLowerCase = "linux";
+
+
+ ///
+ /// Gets the operating system name in lower case. E.g. windows, linux, macos.
+ ///
+ /// The operating system name in lower case.
+ public static string OsNameLowerCase
+ {
+ get
+ {
+ if (IsMacOS)
+ {
+ return MacOSNameLowerCase;
+ }
+
+ if (IsLinuxOS)
+ {
+ return LinuxNameLowerCase;
+ }
+
+ return WindowsNameLowerCase;
+ }
+ }
}
\ No newline at end of file
diff --git a/amp.Shared/Localization/UI.Designer.cs b/amp.Shared/Localization/UI.Designer.cs
index fabbd590..39fbdee3 100644
--- a/amp.Shared/Localization/UI.Designer.cs
+++ b/amp.Shared/Localization/UI.Designer.cs
@@ -907,6 +907,15 @@ public static string QueueEntries {
}
}
+ ///
+ /// Looks up a localized string similar to Queue index.
+ ///
+ public static string QueueIndex {
+ get {
+ return ResourceManager.GetString("QueueIndex", resourceCulture);
+ }
+ }
+
///
/// Looks up a localized string similar to Queue name.
///
diff --git a/amp.Shared/Localization/UI.fi.resx b/amp.Shared/Localization/UI.fi.resx
index 96cbb763..596f37a5 100644
--- a/amp.Shared/Localization/UI.fi.resx
+++ b/amp.Shared/Localization/UI.fi.resx
@@ -533,4 +533,7 @@ lopputulokseen, jos muuttujalla on arvo esim. Artisti. ({@Ar /}).
Aputiedostokansio
+
+ Jonotusnumero
+
\ No newline at end of file
diff --git a/amp.Shared/Localization/UI.resx b/amp.Shared/Localization/UI.resx
index 0f17b6c4..215bf6a9 100644
--- a/amp.Shared/Localization/UI.resx
+++ b/amp.Shared/Localization/UI.resx
@@ -533,4 +533,7 @@ result if the actual variable e.g. Artist exists. ({@Ar /}).
Help folder
+
+ Queue index
+
\ No newline at end of file