Add book records dialog
This commit is contained in:
parent
ddd6a3b279
commit
6417aee780
143
Source/ApplicationServices/RecordExporter.cs
Normal file
143
Source/ApplicationServices/RecordExporter.cs
Normal file
@ -0,0 +1,143 @@
|
|||||||
|
using AudibleApi.Common;
|
||||||
|
using CsvHelper;
|
||||||
|
using DataLayer;
|
||||||
|
using Newtonsoft.Json.Linq;
|
||||||
|
using NPOI.XSSF.UserModel;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
|
namespace ApplicationServices
|
||||||
|
{
|
||||||
|
public static class RecordExporter
|
||||||
|
{
|
||||||
|
public static void ToXlsx(string saveFilePath, IEnumerable<IRecord> records)
|
||||||
|
{
|
||||||
|
if (!records.Any())
|
||||||
|
return;
|
||||||
|
|
||||||
|
using var workbook = new XSSFWorkbook();
|
||||||
|
var sheet = workbook.CreateSheet("Records");
|
||||||
|
|
||||||
|
var detailSubtotalFont = workbook.CreateFont();
|
||||||
|
detailSubtotalFont.IsBold = true;
|
||||||
|
|
||||||
|
var detailSubtotalCellStyle = workbook.CreateCellStyle();
|
||||||
|
detailSubtotalCellStyle.SetFont(detailSubtotalFont);
|
||||||
|
|
||||||
|
// headers
|
||||||
|
var rowIndex = 0;
|
||||||
|
var row = sheet.CreateRow(rowIndex);
|
||||||
|
|
||||||
|
var columns = new List<string>
|
||||||
|
{
|
||||||
|
nameof(IRecord.RecordType),
|
||||||
|
nameof(IRecord.Created),
|
||||||
|
nameof(IRecord.Start) + "_ms",
|
||||||
|
};
|
||||||
|
|
||||||
|
if (records.OfType<IAnnotation>().Any())
|
||||||
|
{
|
||||||
|
columns.Add(nameof(IAnnotation.AnnotationId));
|
||||||
|
columns.Add(nameof(IAnnotation.LastModified));
|
||||||
|
}
|
||||||
|
if (records.OfType<IRangeAnnotation>().Any())
|
||||||
|
{
|
||||||
|
columns.Add(nameof(IRangeAnnotation.End) + "_ms");
|
||||||
|
columns.Add(nameof(IRangeAnnotation.Text));
|
||||||
|
}
|
||||||
|
if (records.OfType<Clip>().Any())
|
||||||
|
columns.Add(nameof(Clip.Title));
|
||||||
|
|
||||||
|
var col = 0;
|
||||||
|
foreach (var c in columns)
|
||||||
|
{
|
||||||
|
var cell = row.CreateCell(col++);
|
||||||
|
cell.SetCellValue(c);
|
||||||
|
cell.CellStyle = detailSubtotalCellStyle;
|
||||||
|
}
|
||||||
|
|
||||||
|
var dateFormat = workbook.CreateDataFormat();
|
||||||
|
var dateStyle = workbook.CreateCellStyle();
|
||||||
|
dateStyle.DataFormat = dateFormat.GetFormat("MM/dd/yyyy HH:mm:ss");
|
||||||
|
|
||||||
|
// Add data rows
|
||||||
|
foreach (var record in records)
|
||||||
|
{
|
||||||
|
col = 0;
|
||||||
|
|
||||||
|
row = sheet.CreateRow(++rowIndex);
|
||||||
|
|
||||||
|
row.CreateCell(col++).SetCellValue(record.RecordType);
|
||||||
|
|
||||||
|
var dateCreatedCell = row.CreateCell(col++);
|
||||||
|
dateCreatedCell.CellStyle = dateStyle;
|
||||||
|
dateCreatedCell.SetCellValue(record.Created.DateTime);
|
||||||
|
|
||||||
|
row.CreateCell(col++).SetCellValue(record.Start.TotalMilliseconds);
|
||||||
|
|
||||||
|
if (record is IAnnotation annotation)
|
||||||
|
{
|
||||||
|
row.CreateCell(col++).SetCellValue(annotation.AnnotationId);
|
||||||
|
|
||||||
|
var lastModifiedCell = row.CreateCell(col++);
|
||||||
|
lastModifiedCell.CellStyle = dateStyle;
|
||||||
|
lastModifiedCell.SetCellValue(annotation.LastModified.DateTime);
|
||||||
|
|
||||||
|
if (annotation is IRangeAnnotation rangeAnnotation)
|
||||||
|
{
|
||||||
|
row.CreateCell(col++).SetCellValue(rangeAnnotation.End.TotalMilliseconds);
|
||||||
|
row.CreateCell(col++).SetCellValue(rangeAnnotation.Text);
|
||||||
|
|
||||||
|
if (rangeAnnotation is Clip clip)
|
||||||
|
row.CreateCell(col++).SetCellValue(clip.Title);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
using var fileData = new System.IO.FileStream(saveFilePath, System.IO.FileMode.Create);
|
||||||
|
workbook.Write(fileData);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void ToJson(string saveFilePath, LibraryBook libraryBook, IEnumerable<IRecord> records)
|
||||||
|
{
|
||||||
|
if (!records.Any())
|
||||||
|
return;
|
||||||
|
|
||||||
|
var recordsObj = new JObject
|
||||||
|
{
|
||||||
|
{ "title", libraryBook.Book.Title},
|
||||||
|
{ "asin", libraryBook.Book.AudibleProductId},
|
||||||
|
{ "exportTime", DateTime.Now},
|
||||||
|
{ "records", JArray.FromObject(records) }
|
||||||
|
};
|
||||||
|
|
||||||
|
System.IO.File.WriteAllText(saveFilePath, recordsObj.ToString(Newtonsoft.Json.Formatting.Indented));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void ToCsv(string saveFilePath, IEnumerable<IRecord> records)
|
||||||
|
{
|
||||||
|
if (!records.Any())
|
||||||
|
return;
|
||||||
|
|
||||||
|
using var writer = new System.IO.StreamWriter(saveFilePath);
|
||||||
|
using var csv = new CsvWriter(writer, System.Globalization.CultureInfo.CurrentCulture);
|
||||||
|
|
||||||
|
//Write headers for the present type that has the most properties
|
||||||
|
if (records.OfType<Clip>().Any())
|
||||||
|
csv.WriteHeader(typeof(Clip));
|
||||||
|
else if (records.OfType<IRangeAnnotation>().Any())
|
||||||
|
csv.WriteHeader(typeof(IRangeAnnotation));
|
||||||
|
else if (records.OfType<IAnnotation>().Any())
|
||||||
|
csv.WriteHeader(typeof(IAnnotation));
|
||||||
|
else
|
||||||
|
csv.WriteHeader(typeof(IRecord));
|
||||||
|
|
||||||
|
csv.NextRecord();
|
||||||
|
csv.WriteRecords(records.OfType<Clip>());
|
||||||
|
csv.WriteRecords(records.OfType<Note>());
|
||||||
|
csv.WriteRecords(records.OfType<Bookmark>());
|
||||||
|
csv.WriteRecords(records.OfType<LastHeard>());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
231
Source/LibationWinForms/Dialogs/BookRecordsDialog.Designer.cs
generated
Normal file
231
Source/LibationWinForms/Dialogs/BookRecordsDialog.Designer.cs
generated
Normal file
@ -0,0 +1,231 @@
|
|||||||
|
namespace LibationWinForms.Dialogs
|
||||||
|
{
|
||||||
|
partial class BookRecordsDialog
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Required designer variable.
|
||||||
|
/// </summary>
|
||||||
|
private System.ComponentModel.IContainer components = null;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Clean up any resources being used.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
|
||||||
|
protected override void Dispose(bool disposing)
|
||||||
|
{
|
||||||
|
if (disposing && (components != null))
|
||||||
|
{
|
||||||
|
components.Dispose();
|
||||||
|
}
|
||||||
|
base.Dispose(disposing);
|
||||||
|
}
|
||||||
|
|
||||||
|
#region Windows Form Designer generated code
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Required method for Designer support - do not modify
|
||||||
|
/// the contents of this method with the code editor.
|
||||||
|
/// </summary>
|
||||||
|
private void InitializeComponent()
|
||||||
|
{
|
||||||
|
this.components = new System.ComponentModel.Container();
|
||||||
|
this.syncBindingSource = new LibationWinForms.GridView.SyncBindingSource(this.components);
|
||||||
|
this.dataGridView1 = new System.Windows.Forms.DataGridView();
|
||||||
|
this.checkboxColumn = new System.Windows.Forms.DataGridViewCheckBoxColumn();
|
||||||
|
this.typeColumn = new System.Windows.Forms.DataGridViewTextBoxColumn();
|
||||||
|
this.createdColumn = new System.Windows.Forms.DataGridViewTextBoxColumn();
|
||||||
|
this.startTimeColumn = new System.Windows.Forms.DataGridViewTextBoxColumn();
|
||||||
|
this.modifiedColumn = new System.Windows.Forms.DataGridViewTextBoxColumn();
|
||||||
|
this.endTimeColumn = new System.Windows.Forms.DataGridViewTextBoxColumn();
|
||||||
|
this.noteColumn = new System.Windows.Forms.DataGridViewTextBoxColumn();
|
||||||
|
this.titleColumn = new System.Windows.Forms.DataGridViewTextBoxColumn();
|
||||||
|
this.checkAllBtn = new System.Windows.Forms.Button();
|
||||||
|
this.uncheckAllBtn = new System.Windows.Forms.Button();
|
||||||
|
this.deleteCheckedBtn = new System.Windows.Forms.Button();
|
||||||
|
this.exportAllBtn = new System.Windows.Forms.Button();
|
||||||
|
this.exportCheckedBtn = new System.Windows.Forms.Button();
|
||||||
|
((System.ComponentModel.ISupportInitialize)(this.syncBindingSource)).BeginInit();
|
||||||
|
((System.ComponentModel.ISupportInitialize)(this.dataGridView1)).BeginInit();
|
||||||
|
this.SuspendLayout();
|
||||||
|
//
|
||||||
|
// dataGridView1
|
||||||
|
//
|
||||||
|
this.dataGridView1.AllowUserToAddRows = false;
|
||||||
|
this.dataGridView1.AllowUserToDeleteRows = false;
|
||||||
|
this.dataGridView1.AllowUserToResizeRows = false;
|
||||||
|
this.dataGridView1.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
|
||||||
|
| System.Windows.Forms.AnchorStyles.Left)
|
||||||
|
| System.Windows.Forms.AnchorStyles.Right)));
|
||||||
|
this.dataGridView1.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize;
|
||||||
|
this.dataGridView1.Columns.AddRange(new System.Windows.Forms.DataGridViewColumn[] {
|
||||||
|
this.checkboxColumn,
|
||||||
|
this.typeColumn,
|
||||||
|
this.createdColumn,
|
||||||
|
this.startTimeColumn,
|
||||||
|
this.modifiedColumn,
|
||||||
|
this.endTimeColumn,
|
||||||
|
this.noteColumn,
|
||||||
|
this.titleColumn});
|
||||||
|
this.dataGridView1.Location = new System.Drawing.Point(0, 0);
|
||||||
|
this.dataGridView1.Name = "dataGridView1";
|
||||||
|
this.dataGridView1.RowHeadersVisible = false;
|
||||||
|
this.dataGridView1.RowTemplate.Height = 25;
|
||||||
|
this.dataGridView1.Size = new System.Drawing.Size(334, 291);
|
||||||
|
this.dataGridView1.TabIndex = 0;
|
||||||
|
//
|
||||||
|
// checkboxColumn
|
||||||
|
//
|
||||||
|
this.checkboxColumn.DataPropertyName = "IsChecked";
|
||||||
|
this.checkboxColumn.HeaderText = "Checked";
|
||||||
|
this.checkboxColumn.Name = "checkboxColumn";
|
||||||
|
this.checkboxColumn.Width = 60;
|
||||||
|
//
|
||||||
|
// typeColumn
|
||||||
|
//
|
||||||
|
this.typeColumn.DataPropertyName = "Type";
|
||||||
|
this.typeColumn.HeaderText = "Type";
|
||||||
|
this.typeColumn.Name = "typeColumn";
|
||||||
|
this.typeColumn.ReadOnly = true;
|
||||||
|
this.typeColumn.Width = 80;
|
||||||
|
//
|
||||||
|
// createdColumn
|
||||||
|
//
|
||||||
|
this.createdColumn.DataPropertyName = "Created";
|
||||||
|
this.createdColumn.HeaderText = "Created";
|
||||||
|
this.createdColumn.Name = "createdColumn";
|
||||||
|
this.createdColumn.ReadOnly = true;
|
||||||
|
//
|
||||||
|
// startTimeColumn
|
||||||
|
//
|
||||||
|
this.startTimeColumn.DataPropertyName = "Start";
|
||||||
|
this.startTimeColumn.HeaderText = "Start";
|
||||||
|
this.startTimeColumn.Name = "startTimeColumn";
|
||||||
|
this.startTimeColumn.ReadOnly = true;
|
||||||
|
//
|
||||||
|
// modifiedColumn
|
||||||
|
//
|
||||||
|
this.modifiedColumn.DataPropertyName = "Modified";
|
||||||
|
this.modifiedColumn.HeaderText = "Modified";
|
||||||
|
this.modifiedColumn.Name = "modifiedColumn";
|
||||||
|
this.modifiedColumn.ReadOnly = true;
|
||||||
|
//
|
||||||
|
// endTimeColumn
|
||||||
|
//
|
||||||
|
this.endTimeColumn.DataPropertyName = "End";
|
||||||
|
this.endTimeColumn.HeaderText = "End";
|
||||||
|
this.endTimeColumn.Name = "endTimeColumn";
|
||||||
|
this.endTimeColumn.ReadOnly = true;
|
||||||
|
//
|
||||||
|
// noteColumn
|
||||||
|
//
|
||||||
|
this.noteColumn.DataPropertyName = "Note";
|
||||||
|
this.noteColumn.HeaderText = "Note";
|
||||||
|
this.noteColumn.Name = "noteColumn";
|
||||||
|
this.noteColumn.ReadOnly = true;
|
||||||
|
//
|
||||||
|
// titleColumn
|
||||||
|
//
|
||||||
|
this.titleColumn.DataPropertyName = "Title";
|
||||||
|
this.titleColumn.HeaderText = "Title";
|
||||||
|
this.titleColumn.Name = "titleColumn";
|
||||||
|
this.titleColumn.ReadOnly = true;
|
||||||
|
//
|
||||||
|
// checkAllBtn
|
||||||
|
//
|
||||||
|
this.checkAllBtn.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
|
||||||
|
this.checkAllBtn.Location = new System.Drawing.Point(12, 297);
|
||||||
|
this.checkAllBtn.Name = "checkAllBtn";
|
||||||
|
this.checkAllBtn.Size = new System.Drawing.Size(80, 23);
|
||||||
|
this.checkAllBtn.TabIndex = 1;
|
||||||
|
this.checkAllBtn.Text = "Check All";
|
||||||
|
this.checkAllBtn.UseVisualStyleBackColor = true;
|
||||||
|
this.checkAllBtn.Click += new System.EventHandler(this.checkAllBtn_Click);
|
||||||
|
//
|
||||||
|
// uncheckAllBtn
|
||||||
|
//
|
||||||
|
this.uncheckAllBtn.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
|
||||||
|
this.uncheckAllBtn.Location = new System.Drawing.Point(12, 326);
|
||||||
|
this.uncheckAllBtn.Name = "uncheckAllBtn";
|
||||||
|
this.uncheckAllBtn.Size = new System.Drawing.Size(80, 23);
|
||||||
|
this.uncheckAllBtn.TabIndex = 2;
|
||||||
|
this.uncheckAllBtn.Text = "Uncheck All";
|
||||||
|
this.uncheckAllBtn.UseVisualStyleBackColor = true;
|
||||||
|
this.uncheckAllBtn.Click += new System.EventHandler(this.uncheckAllBtn_Click);
|
||||||
|
//
|
||||||
|
// deleteCheckedBtn
|
||||||
|
//
|
||||||
|
this.deleteCheckedBtn.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
|
||||||
|
this.deleteCheckedBtn.Location = new System.Drawing.Point(115, 297);
|
||||||
|
this.deleteCheckedBtn.Margin = new System.Windows.Forms.Padding(20, 3, 3, 3);
|
||||||
|
this.deleteCheckedBtn.Name = "deleteCheckedBtn";
|
||||||
|
this.deleteCheckedBtn.Size = new System.Drawing.Size(61, 52);
|
||||||
|
this.deleteCheckedBtn.TabIndex = 3;
|
||||||
|
this.deleteCheckedBtn.Text = "Delete Checked";
|
||||||
|
this.deleteCheckedBtn.UseVisualStyleBackColor = true;
|
||||||
|
this.deleteCheckedBtn.Click += new System.EventHandler(this.deleteCheckedBtn_Click);
|
||||||
|
//
|
||||||
|
// exportAllBtn
|
||||||
|
//
|
||||||
|
this.exportAllBtn.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
|
||||||
|
this.exportAllBtn.Location = new System.Drawing.Point(221, 326);
|
||||||
|
this.exportAllBtn.Name = "exportAllBtn";
|
||||||
|
this.exportAllBtn.Size = new System.Drawing.Size(101, 23);
|
||||||
|
this.exportAllBtn.TabIndex = 4;
|
||||||
|
this.exportAllBtn.Text = "Export All";
|
||||||
|
this.exportAllBtn.UseVisualStyleBackColor = true;
|
||||||
|
this.exportAllBtn.Click += new System.EventHandler(this.exportAllBtn_Click);
|
||||||
|
//
|
||||||
|
// exportCheckedBtn
|
||||||
|
//
|
||||||
|
this.exportCheckedBtn.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
|
||||||
|
this.exportCheckedBtn.Location = new System.Drawing.Point(221, 297);
|
||||||
|
this.exportCheckedBtn.Name = "exportCheckedBtn";
|
||||||
|
this.exportCheckedBtn.Size = new System.Drawing.Size(101, 23);
|
||||||
|
this.exportCheckedBtn.TabIndex = 5;
|
||||||
|
this.exportCheckedBtn.Text = "Export Checked";
|
||||||
|
this.exportCheckedBtn.UseVisualStyleBackColor = true;
|
||||||
|
this.exportCheckedBtn.Click += new System.EventHandler(this.exportCheckedBtn_Click);
|
||||||
|
//
|
||||||
|
// BookRecordsDialog
|
||||||
|
//
|
||||||
|
this.AutoScaleDimensions = new System.Drawing.SizeF(7F, 15F);
|
||||||
|
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
||||||
|
this.ClientSize = new System.Drawing.Size(334, 361);
|
||||||
|
this.Controls.Add(this.exportCheckedBtn);
|
||||||
|
this.Controls.Add(this.exportAllBtn);
|
||||||
|
this.Controls.Add(this.deleteCheckedBtn);
|
||||||
|
this.Controls.Add(this.uncheckAllBtn);
|
||||||
|
this.Controls.Add(this.checkAllBtn);
|
||||||
|
this.Controls.Add(this.dataGridView1);
|
||||||
|
this.KeyPreview = true;
|
||||||
|
this.MaximizeBox = false;
|
||||||
|
this.MinimizeBox = false;
|
||||||
|
this.MinimumSize = new System.Drawing.Size(350, 400);
|
||||||
|
this.Name = "BookRecordsDialog";
|
||||||
|
this.Text = "Book Dialog";
|
||||||
|
this.Shown += new System.EventHandler(this.BookRecordsDialog_Shown);
|
||||||
|
((System.ComponentModel.ISupportInitialize)(this.syncBindingSource)).EndInit();
|
||||||
|
((System.ComponentModel.ISupportInitialize)(this.dataGridView1)).EndInit();
|
||||||
|
this.ResumeLayout(false);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
private System.Windows.Forms.DataGridView dataGridView1;
|
||||||
|
private LibationWinForms.GridView.SyncBindingSource syncBindingSource;
|
||||||
|
private System.Windows.Forms.Button checkAllBtn;
|
||||||
|
private System.Windows.Forms.Button uncheckAllBtn;
|
||||||
|
private System.Windows.Forms.Button deleteCheckedBtn;
|
||||||
|
private System.Windows.Forms.Button exportAllBtn;
|
||||||
|
private System.Windows.Forms.Button exportCheckedBtn;
|
||||||
|
private System.Windows.Forms.DataGridViewCheckBoxColumn checkboxColumn;
|
||||||
|
private System.Windows.Forms.DataGridViewTextBoxColumn typeColumn;
|
||||||
|
private System.Windows.Forms.DataGridViewTextBoxColumn createdColumn;
|
||||||
|
private System.Windows.Forms.DataGridViewTextBoxColumn startTimeColumn;
|
||||||
|
private System.Windows.Forms.DataGridViewTextBoxColumn modifiedColumn;
|
||||||
|
private System.Windows.Forms.DataGridViewTextBoxColumn endTimeColumn;
|
||||||
|
private System.Windows.Forms.DataGridViewTextBoxColumn noteColumn;
|
||||||
|
private System.Windows.Forms.DataGridViewTextBoxColumn titleColumn;
|
||||||
|
}
|
||||||
|
}
|
||||||
227
Source/LibationWinForms/Dialogs/BookRecordsDialog.cs
Normal file
227
Source/LibationWinForms/Dialogs/BookRecordsDialog.cs
Normal file
@ -0,0 +1,227 @@
|
|||||||
|
using ApplicationServices;
|
||||||
|
using AudibleApi.Common;
|
||||||
|
using DataLayer;
|
||||||
|
using FileLiberator;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.ComponentModel;
|
||||||
|
using System.Data;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Windows.Forms;
|
||||||
|
|
||||||
|
namespace LibationWinForms.Dialogs
|
||||||
|
{
|
||||||
|
public partial class BookRecordsDialog : Form
|
||||||
|
{
|
||||||
|
private readonly Func<ScrollBar> VScrollBar;
|
||||||
|
private readonly LibraryBook libraryBook;
|
||||||
|
private BookRecordBindingList bookRecordEntries;
|
||||||
|
|
||||||
|
public BookRecordsDialog()
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
|
||||||
|
if (!DesignMode)
|
||||||
|
{
|
||||||
|
//Prevent the designer from auto-generating columns
|
||||||
|
dataGridView1.AutoGenerateColumns = false;
|
||||||
|
dataGridView1.DataSource = syncBindingSource;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.SetLibationIcon();
|
||||||
|
|
||||||
|
VScrollBar =
|
||||||
|
typeof(DataGridView)
|
||||||
|
.GetProperty("VerticalScrollBar", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance)
|
||||||
|
.GetMethod
|
||||||
|
.CreateDelegate<Func<ScrollBar>>(dataGridView1);
|
||||||
|
|
||||||
|
this.RestoreSizeAndLocation(LibationFileManager.Configuration.Instance);
|
||||||
|
FormClosing += (_, _) => this.SaveSizeAndLocation(LibationFileManager.Configuration.Instance);
|
||||||
|
}
|
||||||
|
|
||||||
|
public BookRecordsDialog(LibraryBook libraryBook) : this()
|
||||||
|
{
|
||||||
|
this.libraryBook = libraryBook;
|
||||||
|
|
||||||
|
Text = $"{libraryBook.Book.Title} - Clips and Bookmarks";
|
||||||
|
}
|
||||||
|
|
||||||
|
private async void BookRecordsDialog_Shown(object sender, EventArgs e)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var api = await libraryBook.GetApiAsync();
|
||||||
|
var records = await api.GetRecordsAsync(libraryBook.Book.AudibleProductId);
|
||||||
|
|
||||||
|
bookRecordEntries = new BookRecordBindingList(records.Select(r => new BookRecordEntry(r)));
|
||||||
|
}
|
||||||
|
catch(Exception ex)
|
||||||
|
{
|
||||||
|
Serilog.Log.Error(ex, "Failed to retrieve records for {libraryBook}", libraryBook);
|
||||||
|
bookRecordEntries = new();
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
syncBindingSource.DataSource = bookRecordEntries;
|
||||||
|
|
||||||
|
//Autosize columns and resize form to column width so no horizontal scroll bar is necessary.
|
||||||
|
dataGridView1.AutoResizeColumns(DataGridViewAutoSizeColumnsMode.AllCells);
|
||||||
|
var columnWidth = dataGridView1.Columns.OfType<DataGridViewColumn>().Sum(c => c.Width);
|
||||||
|
Width = Width - dataGridView1.Width + columnWidth + dataGridView1.Margin.Right + (VScrollBar().Visible? VScrollBar().ClientSize.Width : 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#region Buttons
|
||||||
|
|
||||||
|
private void exportCheckedBtn_Click(object sender, EventArgs e)
|
||||||
|
=> saveRecords(bookRecordEntries.Where(r => r.IsChecked).Select(r => r.Record));
|
||||||
|
|
||||||
|
private void exportAllBtn_Click(object sender, EventArgs e)
|
||||||
|
=> saveRecords(bookRecordEntries.Select(r => r.Record));
|
||||||
|
|
||||||
|
private void uncheckAllBtn_Click(object sender, EventArgs e)
|
||||||
|
{
|
||||||
|
foreach (var record in bookRecordEntries)
|
||||||
|
record.IsChecked = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void checkAllBtn_Click(object sender, EventArgs e)
|
||||||
|
{
|
||||||
|
foreach (var record in bookRecordEntries)
|
||||||
|
record.IsChecked = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async void deleteCheckedBtn_Click(object sender, EventArgs e)
|
||||||
|
{
|
||||||
|
var records = bookRecordEntries.Where(r => r.IsChecked).Select(r => r.Record).ToList();
|
||||||
|
|
||||||
|
if (!records.Any()) return;
|
||||||
|
|
||||||
|
bool success = false;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var api = await libraryBook.GetApiAsync();
|
||||||
|
success = await api.DeleteRecordsAsync(libraryBook.Book.AudibleProductId, records);
|
||||||
|
records = await api.GetRecordsAsync(libraryBook.Book.AudibleProductId);
|
||||||
|
|
||||||
|
var removed = bookRecordEntries.ExceptBy(records, r => r.Record).ToList();
|
||||||
|
|
||||||
|
foreach (var r in removed)
|
||||||
|
bookRecordEntries.Remove(r);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Serilog.Log.Error(ex, ex.Message);
|
||||||
|
}
|
||||||
|
if (!success)
|
||||||
|
MessageBox.Show(this, $"Libation was unable to delete the {records.Count} selected records", "Deletion Failed", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
private void saveRecords(IEnumerable<IRecord> records)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var saveFileDialog = new SaveFileDialog
|
||||||
|
{
|
||||||
|
Title = "Where to export records",
|
||||||
|
AddExtension = true,
|
||||||
|
FileName = $"{libraryBook.Book.Title} - Records",
|
||||||
|
DefaultExt = "xlsx",
|
||||||
|
Filter = "Excel Workbook (*.xlsx)|*.xlsx|CSV files (*.csv)|*.csv|JSON files (*.json)|*.json" // + "|All files (*.*)|*.*"
|
||||||
|
};
|
||||||
|
|
||||||
|
if (saveFileDialog.ShowDialog() != DialogResult.OK)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// FilterIndex is 1-based, NOT 0-based
|
||||||
|
switch (saveFileDialog.FilterIndex)
|
||||||
|
{
|
||||||
|
case 1: // xlsx
|
||||||
|
default:
|
||||||
|
RecordExporter.ToXlsx(saveFileDialog.FileName, records);
|
||||||
|
break;
|
||||||
|
case 2: // csv
|
||||||
|
RecordExporter.ToCsv(saveFileDialog.FileName, records);
|
||||||
|
break;
|
||||||
|
case 3: // json
|
||||||
|
RecordExporter.ToJson(saveFileDialog.FileName, libraryBook, records);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
MessageBoxLib.ShowAdminAlert(this, "Error attempting to export your library.", "Error exporting", ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnKeyDown(KeyEventArgs e)
|
||||||
|
{
|
||||||
|
if (e.KeyCode == Keys.Escape) Close();
|
||||||
|
base.OnKeyDown(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
#region dataGridView Bindings
|
||||||
|
|
||||||
|
private class BookRecordBindingList : BindingList<BookRecordEntry>
|
||||||
|
{
|
||||||
|
private PropertyDescriptor _propertyDescriptor;
|
||||||
|
private ListSortDirection _listSortDirection;
|
||||||
|
private bool _isSortedCore;
|
||||||
|
|
||||||
|
protected override PropertyDescriptor SortPropertyCore => _propertyDescriptor;
|
||||||
|
protected override ListSortDirection SortDirectionCore => _listSortDirection;
|
||||||
|
protected override bool IsSortedCore => _isSortedCore;
|
||||||
|
protected override bool SupportsSortingCore => true;
|
||||||
|
public BookRecordBindingList() : base(new List<BookRecordEntry>()) { }
|
||||||
|
public BookRecordBindingList(IEnumerable<BookRecordEntry> records) : base(records.ToList()) { }
|
||||||
|
protected override void ApplySortCore(PropertyDescriptor prop, ListSortDirection direction)
|
||||||
|
{
|
||||||
|
var itemsList = (List<BookRecordEntry>)Items;
|
||||||
|
|
||||||
|
var sorted =
|
||||||
|
direction is ListSortDirection.Ascending ? itemsList.OrderBy(prop.GetValue).ToList()
|
||||||
|
: itemsList.OrderByDescending(prop.GetValue).ToList();
|
||||||
|
|
||||||
|
itemsList.Clear();
|
||||||
|
itemsList.AddRange(sorted);
|
||||||
|
|
||||||
|
_propertyDescriptor = prop;
|
||||||
|
_listSortDirection = direction;
|
||||||
|
_isSortedCore = true;
|
||||||
|
|
||||||
|
OnListChanged(new ListChangedEventArgs(ListChangedType.Reset, -1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class BookRecordEntry : GridView.AsyncNotifyPropertyChanged
|
||||||
|
{
|
||||||
|
private const string DateFormat = "yyyy-MM-dd HH\\:mm";
|
||||||
|
private bool _ischecked;
|
||||||
|
public IRecord Record { get; }
|
||||||
|
public bool IsChecked { get => _ischecked; set { _ischecked = value; NotifyPropertyChanged(); } }
|
||||||
|
public string Type => Record.GetType().Name;
|
||||||
|
public string Start => formatTimeSpan(Record.Start);
|
||||||
|
public string Created => Record.Created.ToString(DateFormat);
|
||||||
|
public string Modified => Record is IAnnotation annotation ? annotation.Created.ToString(DateFormat) : string.Empty;
|
||||||
|
public string End => Record is IRangeAnnotation range ? formatTimeSpan(range.End) : string.Empty;
|
||||||
|
public string Note => Record is IRangeAnnotation range ? range.Text : string.Empty;
|
||||||
|
public string Title => Record is Clip range ? range.Title : string.Empty;
|
||||||
|
public BookRecordEntry(IRecord record) => Record = record;
|
||||||
|
|
||||||
|
private static string formatTimeSpan(TimeSpan timeSpan)
|
||||||
|
{
|
||||||
|
int h = (int)timeSpan.TotalHours;
|
||||||
|
int m = timeSpan.Minutes;
|
||||||
|
int s = timeSpan.Seconds;
|
||||||
|
int ms = timeSpan.Milliseconds;
|
||||||
|
|
||||||
|
return ms == 0 ? $"{h:d2}:{m:d2}:{s:d2}" : $"{h:d2}:{m:d2}:{s:d2}.{ms:d3}";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
}
|
||||||
87
Source/LibationWinForms/Dialogs/BookRecordsDialog.resx
Normal file
87
Source/LibationWinForms/Dialogs/BookRecordsDialog.resx
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
<root>
|
||||||
|
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||||
|
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||||
|
<xsd:element name="root" msdata:IsDataSet="true">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:choice maxOccurs="unbounded">
|
||||||
|
<xsd:element name="metadata">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||||
|
</xsd:sequence>
|
||||||
|
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||||
|
<xsd:attribute name="type" type="xsd:string" />
|
||||||
|
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||||
|
<xsd:attribute ref="xml:space" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
<xsd:element name="assembly">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:attribute name="alias" type="xsd:string" />
|
||||||
|
<xsd:attribute name="name" type="xsd:string" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
<xsd:element name="data">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||||
|
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||||
|
</xsd:sequence>
|
||||||
|
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
|
||||||
|
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||||
|
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||||
|
<xsd:attribute ref="xml:space" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
<xsd:element name="resheader">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||||
|
</xsd:sequence>
|
||||||
|
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
</xsd:choice>
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
</xsd:schema>
|
||||||
|
<resheader name="resmimetype">
|
||||||
|
<value>text/microsoft-resx</value>
|
||||||
|
</resheader>
|
||||||
|
<resheader name="version">
|
||||||
|
<value>2.0</value>
|
||||||
|
</resheader>
|
||||||
|
<resheader name="reader">
|
||||||
|
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||||
|
</resheader>
|
||||||
|
<resheader name="writer">
|
||||||
|
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||||
|
</resheader>
|
||||||
|
<metadata name="syncBindingSource.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
|
||||||
|
<value>17, 17</value>
|
||||||
|
</metadata>
|
||||||
|
<metadata name="checkboxColumn.UserAddedColumn" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
|
||||||
|
<value>True</value>
|
||||||
|
</metadata>
|
||||||
|
<metadata name="typeColumn.UserAddedColumn" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
|
||||||
|
<value>True</value>
|
||||||
|
</metadata>
|
||||||
|
<metadata name="createdColumn.UserAddedColumn" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
|
||||||
|
<value>True</value>
|
||||||
|
</metadata>
|
||||||
|
<metadata name="startTimeColumn.UserAddedColumn" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
|
||||||
|
<value>True</value>
|
||||||
|
</metadata>
|
||||||
|
<metadata name="modifiedColumn.UserAddedColumn" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
|
||||||
|
<value>True</value>
|
||||||
|
</metadata>
|
||||||
|
<metadata name="endTimeColumn.UserAddedColumn" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
|
||||||
|
<value>True</value>
|
||||||
|
</metadata>
|
||||||
|
<metadata name="noteColumn.UserAddedColumn" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
|
||||||
|
<value>True</value>
|
||||||
|
</metadata>
|
||||||
|
<metadata name="titleColumn.UserAddedColumn" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
|
||||||
|
<value>True</value>
|
||||||
|
</metadata>
|
||||||
|
</root>
|
||||||
@ -8,6 +8,7 @@ using ApplicationServices;
|
|||||||
using DataLayer;
|
using DataLayer;
|
||||||
using Dinah.Core.WindowsDesktop.Forms;
|
using Dinah.Core.WindowsDesktop.Forms;
|
||||||
using LibationFileManager;
|
using LibationFileManager;
|
||||||
|
using LibationWinForms.Dialogs;
|
||||||
|
|
||||||
namespace LibationWinForms.GridView
|
namespace LibationWinForms.GridView
|
||||||
{
|
{
|
||||||
@ -174,13 +175,18 @@ namespace LibationWinForms.GridView
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
var stopLightContextMenu = new ContextMenuStrip();
|
var bookRecordMenuItem = new ToolStripMenuItem { Text = "View Bookmarks/Clips" };
|
||||||
|
bookRecordMenuItem.Click += (_, _) => new BookRecordsDialog(entry.LibraryBook).ShowDialog(this);
|
||||||
|
|
||||||
|
var stopLightContextMenu = new ContextMenuStrip();
|
||||||
stopLightContextMenu.Items.Add(setDownloadMenuItem);
|
stopLightContextMenu.Items.Add(setDownloadMenuItem);
|
||||||
stopLightContextMenu.Items.Add(setNotDownloadMenuItem);
|
stopLightContextMenu.Items.Add(setNotDownloadMenuItem);
|
||||||
stopLightContextMenu.Items.Add(removeMenuItem);
|
stopLightContextMenu.Items.Add(removeMenuItem);
|
||||||
stopLightContextMenu.Items.Add(locateFileMenuItem);
|
stopLightContextMenu.Items.Add(locateFileMenuItem);
|
||||||
|
stopLightContextMenu.Items.Add(new ToolStripSeparator());
|
||||||
|
stopLightContextMenu.Items.Add(bookRecordMenuItem);
|
||||||
|
|
||||||
e.ContextMenuStrip = stopLightContextMenu;
|
e.ContextMenuStrip = stopLightContextMenu;
|
||||||
}
|
}
|
||||||
|
|
||||||
private GridEntry getGridEntry(int rowIndex) => gridEntryDataGridView.GetBoundItem<GridEntry>(rowIndex);
|
private GridEntry getGridEntry(int rowIndex) => gridEntryDataGridView.GetBoundItem<GridEntry>(rowIndex);
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user