Add book records dialog

This commit is contained in:
Michael Bucari-Tovo 2023-01-05 17:02:39 -07:00
parent ddd6a3b279
commit 6417aee780
5 changed files with 696 additions and 2 deletions

View 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>());
}
}
}

View 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;
}
}

View 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
}
}

View 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>

View File

@ -8,6 +8,7 @@ using ApplicationServices;
using DataLayer;
using Dinah.Core.WindowsDesktop.Forms;
using LibationFileManager;
using LibationWinForms.Dialogs;
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(setNotDownloadMenuItem);
stopLightContextMenu.Items.Add(removeMenuItem);
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);