Initial check-in

This commit is contained in:
Robert McRackan 2019-10-04 16:14:04 -04:00
parent c080c9e51d
commit 2cc93078d2
282 changed files with 24387 additions and 0 deletions

View file

@ -0,0 +1,32 @@
<Project Sdk="Microsoft.NET.Sdk.WindowsDesktop">
<PropertyGroup>
<OutputType>WinExe</OutputType>
<TargetFramework>netcoreapp3.0</TargetFramework>
<UseWindowsForms>true</UseWindowsForms>
<ApplicationIcon>libation.ico</ApplicationIcon>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\audible api\AudibleApi\AudibleApi\AudibleApi.csproj" />
<ProjectReference Include="..\..\Dinah.Core\Dinah.Core.Drawing\Dinah.Core.Drawing.csproj" />
<ProjectReference Include="..\..\Dinah.Core\Dinah.Core.Windows.Forms\Dinah.Core.Windows.Forms.csproj" />
<ProjectReference Include="..\DomainServices\DomainServices.csproj" />
</ItemGroup>
<ItemGroup>
<Compile Update="Properties\Resources.Designer.cs">
<DesignTime>True</DesignTime>
<AutoGen>True</AutoGen>
<DependentUpon>Resources.resx</DependentUpon>
</Compile>
</ItemGroup>
<ItemGroup>
<EmbeddedResource Update="Properties\Resources.resx">
<Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
</EmbeddedResource>
</ItemGroup>
</Project>

View file

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
This file is automatically generated by Visual Studio .Net. It is
used to store generic object data source configuration information.
Renaming the file extension or editing the content of this file may
cause the file to be unrecognizable by the program.
-->
<GenericObjectDataSource DisplayName="GridEntry" Version="1.0" xmlns="urn:schemas-microsoft-com:xml-msdatasource">
<TypeInfo>LibationWinForm.ProductGrids.GridEntry, LibationWinForm, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null</TypeInfo>
</GenericObjectDataSource>

View file

@ -0,0 +1,83 @@
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
// Runtime Version:4.0.30319.42000
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
namespace LibationWinForm.Properties {
using System;
/// <summary>
/// A strongly-typed resource class, for looking up localized strings, etc.
/// </summary>
// This class was auto-generated by the StronglyTypedResourceBuilder
// class via a tool like ResGen or Visual Studio.
// To add or remove a member, edit your .ResX file then rerun ResGen
// with the /str option, or rebuild your VS project.
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
internal class Resources {
private static global::System.Resources.ResourceManager resourceMan;
private static global::System.Globalization.CultureInfo resourceCulture;
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
internal Resources() {
}
/// <summary>
/// Returns the cached ResourceManager instance used by this class.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Resources.ResourceManager ResourceManager {
get {
if (object.ReferenceEquals(resourceMan, null)) {
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("LibationWinForm.Properties.Resources", typeof(Resources).Assembly);
resourceMan = temp;
}
return resourceMan;
}
}
/// <summary>
/// Overrides the current thread's CurrentUICulture property for all
/// resource lookups using this strongly typed resource class.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Globalization.CultureInfo Culture {
get {
return resourceCulture;
}
set {
resourceCulture = value;
}
}
/// <summary>
/// Looks up a localized resource of type System.Drawing.Bitmap.
/// </summary>
internal static System.Drawing.Bitmap edit_tags_25x25 {
get {
object obj = ResourceManager.GetObject("edit_tags_25x25", resourceCulture);
return ((System.Drawing.Bitmap)(obj));
}
}
/// <summary>
/// Looks up a localized resource of type System.Drawing.Bitmap.
/// </summary>
internal static System.Drawing.Bitmap edit_tags_50x50 {
get {
object obj = ResourceManager.GetObject("edit_tags_50x50", resourceCulture);
return ((System.Drawing.Bitmap)(obj));
}
}
}
}

View file

@ -0,0 +1,127 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<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>
<assembly alias="System.Windows.Forms" name="System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
<data name="edit_tags_25x25" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\edit-tags-25x25.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
<data name="edit_tags_50x50" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\edit-tags-50x50.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
</root>

View file

@ -0,0 +1,12 @@
how to create the app's icon
============================
https://www.flaticon.com/free-icon/glass-with-wine_33529#term=wine&page=1&position=59
get this image, color=black, at each of these sizes: 16,32,64,128,256
from this answer: https://stackoverflow.com/a/16922387
install image magick. in the install process, check legacy support (necessary for "convert")
go to the install dir: C:\Program Files\ImageMagick...
run the convert method from the stackoverflow example
run cmd
drag/drop convert.exe into cmd
...

Binary file not shown.

After

Width:  |  Height:  |  Size: 314 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 573 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 92 KiB

View file

@ -0,0 +1,93 @@
namespace LibationWinForm.BookLiberation
{
partial class AutomatedBackupsForm
{
/// <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.keepGoingCb = new System.Windows.Forms.CheckBox();
this.logTb = new System.Windows.Forms.TextBox();
this.label1 = new System.Windows.Forms.Label();
this.SuspendLayout();
//
// keepGoingCb
//
this.keepGoingCb.AutoSize = true;
this.keepGoingCb.Checked = true;
this.keepGoingCb.CheckState = System.Windows.Forms.CheckState.Checked;
this.keepGoingCb.Location = new System.Drawing.Point(12, 12);
this.keepGoingCb.Name = "keepGoingCb";
this.keepGoingCb.Size = new System.Drawing.Size(325, 17);
this.keepGoingCb.TabIndex = 0;
this.keepGoingCb.Text = "Keep going. Uncheck to stop when current backup is complete";
this.keepGoingCb.UseVisualStyleBackColor = true;
//
// logTb
//
this.logTb.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.logTb.Location = new System.Drawing.Point(12, 48);
this.logTb.Multiline = true;
this.logTb.Name = "logTb";
this.logTb.ReadOnly = true;
this.logTb.ScrollBars = System.Windows.Forms.ScrollBars.Both;
this.logTb.Size = new System.Drawing.Size(960, 202);
this.logTb.TabIndex = 1;
//
// label1
//
this.label1.AutoSize = true;
this.label1.Location = new System.Drawing.Point(9, 32);
this.label1.Name = "label1";
this.label1.Size = new System.Drawing.Size(501, 13);
this.label1.TabIndex = 2;
this.label1.Text = "NOTE: if the working directories are inside of Dropbox, some book liberation acti" +
"ons may hang indefinitely";
//
// AutomatedBackupsForm
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(984, 262);
this.Controls.Add(this.label1);
this.Controls.Add(this.logTb);
this.Controls.Add(this.keepGoingCb);
this.Name = "AutomatedBackupsForm";
this.Text = "Automated Backups";
this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.AutomatedBackupsForm_FormClosing);
this.ResumeLayout(false);
this.PerformLayout();
}
#endregion
private System.Windows.Forms.CheckBox keepGoingCb;
private System.Windows.Forms.TextBox logTb;
private System.Windows.Forms.Label label1;
}
}

View file

@ -0,0 +1,28 @@
using System;
using System.Windows.Forms;
using Dinah.Core.Windows.Forms;
namespace LibationWinForm.BookLiberation
{
public partial class AutomatedBackupsForm : Form
{
public bool KeepGoingIsChecked => keepGoingCb.Checked;
public AutomatedBackupsForm()
{
InitializeComponent();
}
public void AppendError(Exception ex) => AppendText("ERROR: " + ex.Message);
public void AppendText(string text) => logTb.UIThread(() => logTb.AppendText($"{DateTime.Now} {text}{Environment.NewLine}"));
public void FinalizeUI()
{
keepGoingCb.Enabled = false;
logTb.AppendText("");
AppendText("DONE");
}
private void AutomatedBackupsForm_FormClosing(object sender, FormClosingEventArgs e) => keepGoingCb.Checked = false;
}
}

View file

@ -0,0 +1,120 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<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>
</root>

View file

@ -0,0 +1,103 @@
namespace LibationWinForm.BookLiberation
{
partial class DecryptForm
{
/// <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.pictureBox1 = new System.Windows.Forms.PictureBox();
this.bookInfoLbl = new System.Windows.Forms.Label();
this.progressBar1 = new System.Windows.Forms.ProgressBar();
this.rtbLog = new System.Windows.Forms.RichTextBox();
((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).BeginInit();
this.SuspendLayout();
//
// pictureBox1
//
this.pictureBox1.Location = new System.Drawing.Point(12, 12);
this.pictureBox1.Name = "pictureBox1";
this.pictureBox1.Size = new System.Drawing.Size(100, 100);
this.pictureBox1.SizeMode = System.Windows.Forms.PictureBoxSizeMode.StretchImage;
this.pictureBox1.TabIndex = 0;
this.pictureBox1.TabStop = false;
//
// bookInfoLbl
//
this.bookInfoLbl.AutoSize = true;
this.bookInfoLbl.Location = new System.Drawing.Point(118, 12);
this.bookInfoLbl.Name = "bookInfoLbl";
this.bookInfoLbl.Size = new System.Drawing.Size(100, 13);
this.bookInfoLbl.TabIndex = 0;
this.bookInfoLbl.Text = "[multi-line book info]";
//
// progressBar1
//
this.progressBar1.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.progressBar1.Location = new System.Drawing.Point(12, 526);
this.progressBar1.Name = "progressBar1";
this.progressBar1.Size = new System.Drawing.Size(582, 23);
this.progressBar1.TabIndex = 2;
//
// rtbLog
//
this.rtbLog.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.rtbLog.Location = new System.Drawing.Point(12, 118);
this.rtbLog.Name = "rtbLog";
this.rtbLog.Size = new System.Drawing.Size(582, 402);
this.rtbLog.TabIndex = 1;
this.rtbLog.Text = "";
//
// DecryptForm
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(606, 561);
this.Controls.Add(this.rtbLog);
this.Controls.Add(this.progressBar1);
this.Controls.Add(this.bookInfoLbl);
this.Controls.Add(this.pictureBox1);
this.Name = "DecryptForm";
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
this.Text = "DecryptForm";
this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.DecryptForm_FormClosing);
this.Load += new System.EventHandler(this.DecryptForm_Load);
((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).EndInit();
this.ResumeLayout(false);
this.PerformLayout();
}
#endregion
private System.Windows.Forms.PictureBox pictureBox1;
private System.Windows.Forms.Label bookInfoLbl;
private System.Windows.Forms.ProgressBar progressBar1;
private System.Windows.Forms.RichTextBox rtbLog;
}
}

View file

@ -0,0 +1,70 @@
using System;
using System.Windows.Forms;
using Dinah.Core.Drawing;
using Dinah.Core.IO;
using Dinah.Core.Windows.Forms;
namespace LibationWinForm.BookLiberation
{
public partial class DecryptForm : Form
{
public DecryptForm()
{
InitializeComponent();
}
System.IO.TextWriter origOut = Console.Out;
private void DecryptForm_Load(object sender, EventArgs e)
{
// redirect Console.WriteLine to console, textbox
System.IO.TextWriter origOut = Console.Out;
var controlWriter = new RichTextBoxTextWriter(this.rtbLog);
var multiLogger = new MultiTextWriter(origOut, controlWriter);
Console.SetOut(multiLogger);
}
private void DecryptForm_FormClosing(object sender, FormClosingEventArgs e)
{
// restore original
Console.SetOut(origOut);
}
// book info
string title;
string authorNames;
string narratorNames;
public void SetTitle(string title)
{
this.UIThread(() => this.Text = " Decrypting " + title);
this.title = title;
updateBookInfo();
}
public void SetAuthorNames(string authorNames)
{
this.authorNames = authorNames;
updateBookInfo();
}
public void SetNarratorNames(string narratorNames)
{
this.narratorNames = narratorNames;
updateBookInfo();
}
// thread-safe UI updates
private void updateBookInfo()
=> bookInfoLbl.UIThread(() => bookInfoLbl.Text = $"{title}\r\nBy {authorNames}\r\nNarrated by {narratorNames}");
public void SetCoverImage(byte[] coverBytes)
=> pictureBox1.UIThread(() => pictureBox1.Image = ImageConverter.GetPictureFromBytes(coverBytes));
public void AppendError(Exception ex) => AppendText("ERROR: " + ex.Message);
public void AppendText(string text) =>
// redirected to log textbox
Console.WriteLine($"{DateTime.Now} {text}")
//logTb.UIThread(() => logTb.AppendText($"{DateTime.Now} {text}{Environment.NewLine}"))
;
public void UpdateProgress(int percentage) => progressBar1.UIThread(() => progressBar1.Value = percentage);
}
}

View file

@ -0,0 +1,120 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<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>
</root>

View file

@ -0,0 +1,105 @@
namespace LibationWinForm.BookLiberation
{
partial class DownloadForm
{
/// <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.filenameLbl = new System.Windows.Forms.Label();
this.progressBar1 = new System.Windows.Forms.ProgressBar();
this.progressLbl = new System.Windows.Forms.Label();
this.lastUpdateLbl = new System.Windows.Forms.Label();
this.SuspendLayout();
//
// filenameLbl
//
this.filenameLbl.AutoSize = true;
this.filenameLbl.Location = new System.Drawing.Point(12, 9);
this.filenameLbl.Name = "filenameLbl";
this.filenameLbl.Size = new System.Drawing.Size(52, 13);
this.filenameLbl.TabIndex = 0;
this.filenameLbl.Text = "[filename]";
//
// progressBar1
//
this.progressBar1.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.progressBar1.Location = new System.Drawing.Point(15, 67);
this.progressBar1.Name = "progressBar1";
this.progressBar1.Size = new System.Drawing.Size(877, 23);
this.progressBar1.TabIndex = 4;
//
// progressLbl
//
this.progressLbl.Location = new System.Drawing.Point(12, 36);
this.progressLbl.Name = "progressLbl";
this.progressLbl.Size = new System.Drawing.Size(173, 13);
this.progressLbl.TabIndex = 5;
this.progressLbl.Text = "[2,999,999,999] of [2,999,999,999]";
this.progressLbl.TextAlign = System.Drawing.ContentAlignment.TopRight;
//
// lastUpdateLbl
//
this.lastUpdateLbl.AutoSize = true;
this.lastUpdateLbl.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
this.lastUpdateLbl.ForeColor = System.Drawing.Color.DarkRed;
this.lastUpdateLbl.Location = new System.Drawing.Point(361, 36);
this.lastUpdateLbl.Name = "lastUpdateLbl";
this.lastUpdateLbl.Size = new System.Drawing.Size(81, 13);
this.lastUpdateLbl.TabIndex = 6;
this.lastUpdateLbl.Text = "Last updated";
this.lastUpdateLbl.Visible = false;
//
// DownloadForm
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(904, 102);
this.Controls.Add(this.lastUpdateLbl);
this.Controls.Add(this.progressLbl);
this.Controls.Add(this.progressBar1);
this.Controls.Add(this.filenameLbl);
this.MaximizeBox = false;
this.MinimizeBox = false;
this.Name = "DownloadForm";
this.ShowIcon = false;
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
this.Text = "Downloading";
this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.DownloadForm_FormClosing);
this.Load += new System.EventHandler(this.DownloadForm_Load);
this.ResumeLayout(false);
this.PerformLayout();
}
#endregion
private System.Windows.Forms.Label filenameLbl;
private System.Windows.Forms.ProgressBar progressBar1;
private System.Windows.Forms.Label progressLbl;
private System.Windows.Forms.Label lastUpdateLbl;
}
}

View file

@ -0,0 +1,59 @@
using System;
using System.Windows.Forms;
using Dinah.Core.Windows.Forms;
namespace LibationWinForm.BookLiberation
{
public partial class DownloadForm : Form
{
public DownloadForm()
{
InitializeComponent();
progressLbl.Text = "";
filenameLbl.Text = "";
}
// thread-safe UI updates
public void UpdateFilename(string title) => filenameLbl.UIThread(() => filenameLbl.Text = title);
public void DownloadProgressChanged(long BytesReceived, long TotalBytesToReceive)
{
// this won't happen with download file. it will happen with download string
if (TotalBytesToReceive < 0)
return;
progressLbl.UIThread(() => progressLbl.Text = $"{BytesReceived:#,##0} of {TotalBytesToReceive:#,##0}");
var d = double.Parse(BytesReceived.ToString()) / double.Parse(TotalBytesToReceive.ToString()) * 100.0;
var i = int.Parse(Math.Truncate(d).ToString());
progressBar1.UIThread(() => progressBar1.Value = i);
lastDownloadProgress = DateTime.Now;
}
#region timer
Timer timer = new Timer { Interval = 1000 };
private void DownloadForm_Load(object sender, EventArgs e)
{
timer.Tick += new EventHandler(timer_Tick);
timer.Start();
}
DateTime lastDownloadProgress = DateTime.Now;
private void timer_Tick(object sender, EventArgs e)
{
// if no update in the last 30 seconds, display frozen label
lastUpdateLbl.Visible = lastDownloadProgress.AddSeconds(30) < DateTime.Now;
if (lastUpdateLbl.Visible)
{
var diff = lastDownloadProgress - DateTime.Now;
var min = (int)diff.TotalMinutes;
var minText = min > 0 ? $"{min}min " : "";
lastUpdateLbl.Text = $"Frozen? Last download activity: {minText}{diff.Seconds}sec ago";
}
}
private void DownloadForm_FormClosing(object sender, FormClosingEventArgs e) => timer.Stop();
#endregion
}
}

View file

@ -0,0 +1,120 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<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>
</root>

View file

@ -0,0 +1,129 @@
namespace LibationWinForm.BookLiberation
{
partial class NoLongerAvailableForm
{
/// <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.label1 = new System.Windows.Forms.Label();
this.textBox1 = new System.Windows.Forms.TextBox();
this.missingBtn = new System.Windows.Forms.Button();
this.abortBtn = new System.Windows.Forms.Button();
this.label2 = new System.Windows.Forms.Label();
this.label3 = new System.Windows.Forms.Label();
this.SuspendLayout();
//
// label1
//
this.label1.AutoSize = true;
this.label1.Location = new System.Drawing.Point(12, 9);
this.label1.Name = "label1";
this.label1.Size = new System.Drawing.Size(174, 39);
this.label1.TabIndex = 0;
this.label1.Text = "Book details download failed.\r\n{0} may be no longer available.\r\nVerify the book i" +
"s still available here";
//
// textBox1
//
this.textBox1.Location = new System.Drawing.Point(15, 51);
this.textBox1.Name = "textBox1";
this.textBox1.ReadOnly = true;
this.textBox1.Size = new System.Drawing.Size(384, 20);
this.textBox1.TabIndex = 1;
//
// missingBtn
//
this.missingBtn.Location = new System.Drawing.Point(324, 77);
this.missingBtn.Name = "missingBtn";
this.missingBtn.Size = new System.Drawing.Size(75, 23);
this.missingBtn.TabIndex = 3;
this.missingBtn.Text = "Missing";
this.missingBtn.UseVisualStyleBackColor = true;
this.missingBtn.Click += new System.EventHandler(this.missingBtn_Click);
//
// abortBtn
//
this.abortBtn.Location = new System.Drawing.Point(324, 126);
this.abortBtn.Name = "abortBtn";
this.abortBtn.Size = new System.Drawing.Size(75, 23);
this.abortBtn.TabIndex = 5;
this.abortBtn.Text = "Abort";
this.abortBtn.UseVisualStyleBackColor = true;
this.abortBtn.Click += new System.EventHandler(this.abortBtn_Click);
//
// label2
//
this.label2.AutoSize = true;
this.label2.Location = new System.Drawing.Point(12, 74);
this.label2.Name = "label2";
this.label2.Size = new System.Drawing.Size(306, 26);
this.label2.TabIndex = 2;
this.label2.Text = "If the book is not available, click here to mark it as missing\r\nNo further book d" +
"etails download will be attempted for this book";
//
// label3
//
this.label3.AutoSize = true;
this.label3.Location = new System.Drawing.Point(12, 123);
this.label3.Name = "label3";
this.label3.Size = new System.Drawing.Size(204, 26);
this.label3.TabIndex = 4;
this.label3.Text = "If the book is actually available, click here\r\nto abort and try again later";
//
// NoLongerAvailableForm
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(411, 161);
this.Controls.Add(this.label3);
this.Controls.Add(this.label2);
this.Controls.Add(this.abortBtn);
this.Controls.Add(this.missingBtn);
this.Controls.Add(this.textBox1);
this.Controls.Add(this.label1);
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog;
this.MaximizeBox = false;
this.MinimizeBox = false;
this.Name = "NoLongerAvailableForm";
this.ShowIcon = false;
this.ShowInTaskbar = false;
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
this.Text = "No Longer Available";
this.ResumeLayout(false);
this.PerformLayout();
}
#endregion
private System.Windows.Forms.Label label1;
private System.Windows.Forms.TextBox textBox1;
private System.Windows.Forms.Button missingBtn;
private System.Windows.Forms.Button abortBtn;
private System.Windows.Forms.Label label2;
private System.Windows.Forms.Label label3;
}
}

View file

@ -0,0 +1,28 @@
using System;
using System.Windows.Forms;
using DomainServices;
namespace LibationWinForm.BookLiberation
{
public partial class NoLongerAvailableForm : Form
{
public ScrapeBookDetails.NoLongerAvailableEnum EnumResult { get; private set; }
public NoLongerAvailableForm(string title, string url) : this()
{
this.Text += ": " + title;
this.label1.Text = string.Format(this.label1.Text, title);
this.textBox1.Text = url;
}
public NoLongerAvailableForm() => InitializeComponent();
private void missingBtn_Click(object sender, EventArgs e) => complete(ScrapeBookDetails.NoLongerAvailableEnum.MarkAsMissing);
private void abortBtn_Click(object sender, EventArgs e) => complete(ScrapeBookDetails.NoLongerAvailableEnum.Abort);
private void complete(ScrapeBookDetails.NoLongerAvailableEnum nlaEnum)
{
EnumResult = nlaEnum;
Close();
}
}
}

View file

@ -0,0 +1,120 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<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>
</root>

View file

@ -0,0 +1,57 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using DataLayer;
using DomainServices;
namespace LibationWinForm.BookLiberation
{
public class BookLiberatorControllerExamples
{
async Task BackupBookAsync(string productId)
{
LibraryBook libraryBook;
using (var context = LibationContext.Create())
libraryBook = context
.Library
.GetLibrary()
.SingleOrDefault(lb => lb.Book.AudibleProductId == productId);
if (libraryBook == null)
return;
var backupBook = new BackupBook();
backupBook.Download.Completed += SetBackupCountsAsync;
backupBook.Decrypt.Completed += SetBackupCountsAsync;
await backupBook.ProcessValidateLibraryBookAsync(libraryBook);
}
// Download First Book (Download encrypted/DRM file)
async Task DownloadFirstBookAsync()
{
var downloadBook = ProcessorAutomationController.GetWiredUpDownloadBook();
downloadBook.Completed += SetBackupCountsAsync;
await downloadBook.ProcessFirstValidAsync();
}
// Decrypt First Book (Remove DRM from downloaded file)
async Task DecryptFirstBookAsync()
{
var decryptBook = ProcessorAutomationController.GetWiredUpDecryptBook();
decryptBook.Completed += SetBackupCountsAsync;
await decryptBook.ProcessFirstValidAsync();
}
// Backup First Book (Decrypt a non-liberated book. Download if needed)
async Task BackupFirstBookAsync()
{
var backupBook = ProcessorAutomationController.GetWiredUpBackupBook();
backupBook.Download.Completed += SetBackupCountsAsync;
backupBook.Decrypt.Completed += SetBackupCountsAsync;
await backupBook.ProcessFirstValidAsync();
}
async void SetBackupCountsAsync(object obj, string str) => throw new NotImplementedException();
}
}

View file

@ -0,0 +1,284 @@
using System;
using System.Threading.Tasks;
using DomainServices;
namespace LibationWinForm.BookLiberation
{
// matches a file processor with a form
public static class ProcessorAutomationController
{
//
// these utility methods ensure proper wiring
// 1) we can't forget to do it
// 2) we can't accidentally do it mult times becaues we lost track of complexity
//
public static BackupBook GetWiredUpBackupBook()
{
var backupBook = new BackupBook();
backupBook.Download.Begin += (_, __) => wireUpDownloadable(backupBook.Download);
backupBook.Decrypt.Begin += (_, __) => wireUpDecryptable(backupBook.Decrypt);
return backupBook;
}
public static DecryptBook GetWiredUpDecryptBook()
{
var decryptBook = new DecryptBook();
decryptBook.Begin += (_, __) => wireUpDecryptable(decryptBook);
return decryptBook;
}
public static DownloadBook GetWiredUpDownloadBook()
{
var downloadBook = new DownloadBook();
downloadBook.Begin += (_, __) => wireUpDownloadable(downloadBook);
return downloadBook;
}
public static DownloadPdf GetWiredUpDownloadPdf()
{
var downloadPdf = new DownloadPdf();
downloadPdf.Begin += (_, __) => wireUpDownloadable(downloadPdf);
return downloadPdf;
}
public static ScrapeBookDetails GetWiredUpScrapeBookDetails()
{
var scrapeBookDetails = new ScrapeBookDetails();
scrapeBookDetails.Begin += (_, __) => wireUpDownloadable(scrapeBookDetails);
scrapeBookDetails.NoLongerAvailableAction = noLongerAvailableUI;
return scrapeBookDetails;
}
static ScrapeBookDetails.NoLongerAvailableEnum noLongerAvailableUI(string title, string url)
{
var nla = new NoLongerAvailableForm(title, url);
nla.ShowDialog();
return nla.EnumResult;
}
// subscribed to Begin event because a new form should be created+processed+closed on each iteration
private static void wireUpDownloadable(IDownloadable downloadable)
{
#region create form
var downloadDialog = new DownloadForm();
#endregion
// extra complexity for wiring up download form:
// case 1: download is needed
// dialog created. subscribe to events
// downloadable.DownloadBegin fires. shows dialog
// downloadable.DownloadCompleted fires. closes dialog. which fires FormClosing, FormClosed, Disposed
// Disposed unsubscribe from events
// case 2: download is not needed
// dialog created. subscribe to events
// dialog is never shown nor closed
// downloadable.Completed fires. disposes dialog and unsubscribes from events
#region define how model actions will affect form behavior
void downloadBegin(object _, string str)
{
downloadDialog.UpdateFilename(str);
downloadDialog.Show();
}
// close form on DOWNLOAD completed, not final Completed. Else for BackupBook this form won't close until DECRYPT is also complete
void fileDownloadCompleted(object _, string __) => downloadDialog.Close();
void downloadProgressChanged(object _, System.Net.DownloadProgressChangedEventArgs arg)
=> downloadDialog.DownloadProgressChanged(arg.BytesReceived, arg.TotalBytesToReceive);
void unsubscribe(object _ = null, EventArgs __ = null)
{
downloadable.DownloadBegin -= downloadBegin;
downloadable.DownloadCompleted -= fileDownloadCompleted;
downloadable.DownloadProgressChanged -= downloadProgressChanged;
downloadable.Completed -= dialogDispose;
}
// unless we dispose, if the form is created but un-used/never-shown then weird UI stuff can happen
// also, since event unsubscribe occurs on FormClosing and an unused form is never closed, then the events will never be unsubscribed
void dialogDispose(object _, string __)
{
if (!downloadDialog.IsDisposed)
downloadDialog.Dispose();
}
#endregion
#region subscribe new form to model's events
downloadable.DownloadBegin += downloadBegin;
downloadable.DownloadCompleted += fileDownloadCompleted;
downloadable.DownloadProgressChanged += downloadProgressChanged;
downloadable.Completed += dialogDispose;
#endregion
#region when form closes, unsubscribe from model's events
// unsubscribe so disposed forms aren't still trying to receive notifications
// FormClosing is more UI safe but won't fire unless the form is shown and closed
// if form was shown, Disposed will fire for FormClosing, FormClosed, and Disposed
// if not shown, it will still fire for Disposed
downloadDialog.Disposed += unsubscribe;
#endregion
}
// subscribed to Begin event because a new form should be created+processed+closed on each iteration
private static void wireUpDecryptable(IDecryptable decryptBook)
{
#region create form
var decryptDialog = new DecryptForm();
#endregion
#region define how model actions will affect form behavior
void decryptBegin(object _, string __) => decryptDialog.Show();
void titleDiscovered(object _, string title) => decryptDialog.SetTitle(title);
void authorsDiscovered(object _, string authors) => decryptDialog.SetAuthorNames(authors);
void narratorsDiscovered(object _, string narrators) => decryptDialog.SetNarratorNames(narrators);
void coverImageFilepathDiscovered(object _, byte[] coverBytes) => decryptDialog.SetCoverImage(coverBytes);
void updateProgress(object _, int percentage) => decryptDialog.UpdateProgress(percentage);
void decryptCompleted(object _, string __) => decryptDialog.Close();
#endregion
#region subscribe new form to model's events
decryptBook.DecryptBegin += decryptBegin;
decryptBook.TitleDiscovered += titleDiscovered;
decryptBook.AuthorsDiscovered += authorsDiscovered;
decryptBook.NarratorsDiscovered += narratorsDiscovered;
decryptBook.CoverImageFilepathDiscovered += coverImageFilepathDiscovered;
decryptBook.UpdateProgress += updateProgress;
decryptBook.DecryptCompleted += decryptCompleted;
#endregion
#region when form closes, unsubscribe from model's events
// unsubscribe so disposed forms aren't still trying to receive notifications
decryptDialog.FormClosing += (_, __) =>
{
decryptBook.DecryptBegin -= decryptBegin;
decryptBook.TitleDiscovered -= titleDiscovered;
decryptBook.AuthorsDiscovered -= authorsDiscovered;
decryptBook.NarratorsDiscovered -= narratorsDiscovered;
decryptBook.CoverImageFilepathDiscovered -= coverImageFilepathDiscovered;
decryptBook.UpdateProgress -= updateProgress;
decryptBook.DecryptCompleted -= decryptCompleted;
};
#endregion
}
public static async Task RunAutomaticDownload(IDownloadable downloadable)
{
#region create form
var automatedBackupsForm = new AutomatedBackupsForm();
#endregion
#region define how model actions will affect form behavior
void begin(object _, string str) => automatedBackupsForm.AppendText("Begin: " + str);
void statusUpdate(object _, string str) => automatedBackupsForm.AppendText("- " + str);
// extra line after book is completely finished
void completed(object _, string str) => automatedBackupsForm.AppendText("Completed: " + str + Environment.NewLine);
#endregion
#region subscribe new form to model's events
downloadable.Begin += begin;
downloadable.StatusUpdate += statusUpdate;
downloadable.Completed += completed;
#endregion
#region when form closes, unsubscribe from model's events
// unsubscribe so disposed forms aren't still trying to receive notifications
automatedBackupsForm.FormClosing += (_, __) =>
{
downloadable.Begin -= begin;
downloadable.StatusUpdate -= statusUpdate;
downloadable.Completed -= completed;
};
#endregion
await runBackupLoop(downloadable, automatedBackupsForm);
}
public static async Task RunAutomaticBackup(BackupBook backupBook)
{
#region create form
var automatedBackupsForm = new AutomatedBackupsForm();
#endregion
#region define how model actions will affect form behavior
void downloadBegin(object _, string str) => automatedBackupsForm.AppendText("DownloadStep_Begin: " + str);
void statusUpdate(object _, string str) => automatedBackupsForm.AppendText("- " + str);
void downloadCompleted(object _, string str) => automatedBackupsForm.AppendText("DownloadStep_Completed: " + str);
void decryptBegin(object _, string str) => automatedBackupsForm.AppendText("DecryptStep_Begin: " + str);
// extra line after book is completely finished
void decryptCompleted(object _, string str) => automatedBackupsForm.AppendText("DecryptStep_Completed: " + str + Environment.NewLine);
#endregion
#region subscribe new form to model's events
backupBook.Download.Begin += downloadBegin;
backupBook.Download.StatusUpdate += statusUpdate;
backupBook.Download.Completed += downloadCompleted;
backupBook.Decrypt.Begin += decryptBegin;
backupBook.Decrypt.StatusUpdate += statusUpdate;
backupBook.Decrypt.Completed += decryptCompleted;
#endregion
#region when form closes, unsubscribe from model's events
// unsubscribe so disposed forms aren't still trying to receive notifications
automatedBackupsForm.FormClosing += (_, __) =>
{
backupBook.Download.Begin -= downloadBegin;
backupBook.Download.StatusUpdate -= statusUpdate;
backupBook.Download.Completed -= downloadCompleted;
backupBook.Decrypt.Begin -= decryptBegin;
backupBook.Decrypt.StatusUpdate -= statusUpdate;
backupBook.Decrypt.Completed -= decryptCompleted;
};
#endregion
await runBackupLoop(backupBook, automatedBackupsForm);
}
// automated backups looper feels like a composible IProcessable: logic, UI, begin + process child + end
// however the process step doesn't follow the pattern: Validate(product) + Process(product)
private static async Task runBackupLoop(IProcessable processable, AutomatedBackupsForm automatedBackupsForm)
{
automatedBackupsForm.Show();
try
{
do
{
var statusHandler = await processable.ProcessFirstValidAsync();
if (statusHandler == null)
{
automatedBackupsForm.AppendText("Done. All books have been processed");
break;
}
if (statusHandler.HasErrors)
{
automatedBackupsForm.AppendText("ERROR. All books have not been processed. Most recent valid book: processing failed");
foreach (var errorMessage in statusHandler.Errors)
automatedBackupsForm.AppendText(errorMessage);
break;
}
if (!automatedBackupsForm.KeepGoingIsChecked)
{
automatedBackupsForm.AppendText("'Keep going' is unchecked");
break;
}
}
while (automatedBackupsForm.KeepGoingIsChecked);
}
catch (Exception ex)
{
automatedBackupsForm.AppendError(ex);
}
automatedBackupsForm.FinalizeUI();
}
}
}

View file

@ -0,0 +1,152 @@
namespace LibationWinForm.Dialogs
{
partial class EditQuickFilters
{
/// <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.cancelBtn = new System.Windows.Forms.Button();
this.saveBtn = new System.Windows.Forms.Button();
this.dataGridView1 = new System.Windows.Forms.DataGridView();
this.Original = new System.Windows.Forms.DataGridViewTextBoxColumn();
this.Delete = new System.Windows.Forms.DataGridViewButtonColumn();
this.Filter = new System.Windows.Forms.DataGridViewTextBoxColumn();
this.MoveUp = new System.Windows.Forms.DataGridViewButtonColumn();
this.MoveDown = new System.Windows.Forms.DataGridViewButtonColumn();
((System.ComponentModel.ISupportInitialize)(this.dataGridView1)).BeginInit();
this.SuspendLayout();
//
// cancelBtn
//
this.cancelBtn.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
this.cancelBtn.DialogResult = System.Windows.Forms.DialogResult.Cancel;
this.cancelBtn.Location = new System.Drawing.Point(713, 415);
this.cancelBtn.Name = "cancelBtn";
this.cancelBtn.Size = new System.Drawing.Size(75, 23);
this.cancelBtn.TabIndex = 2;
this.cancelBtn.Text = "Cancel";
this.cancelBtn.UseVisualStyleBackColor = true;
this.cancelBtn.Click += new System.EventHandler(this.CancelBtn_Click);
//
// saveBtn
//
this.saveBtn.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
this.saveBtn.Location = new System.Drawing.Point(612, 415);
this.saveBtn.Name = "saveBtn";
this.saveBtn.Size = new System.Drawing.Size(75, 23);
this.saveBtn.TabIndex = 1;
this.saveBtn.Text = "Save";
this.saveBtn.UseVisualStyleBackColor = true;
this.saveBtn.Click += new System.EventHandler(this.SaveBtn_Click);
//
// dataGridView1
//
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.AutoSizeColumnsMode = System.Windows.Forms.DataGridViewAutoSizeColumnsMode.AllCells;
this.dataGridView1.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize;
this.dataGridView1.Columns.AddRange(new System.Windows.Forms.DataGridViewColumn[] {
this.Original,
this.Delete,
this.Filter,
this.MoveUp,
this.MoveDown});
this.dataGridView1.Location = new System.Drawing.Point(12, 12);
this.dataGridView1.MultiSelect = false;
this.dataGridView1.Name = "dataGridView1";
this.dataGridView1.Size = new System.Drawing.Size(776, 397);
this.dataGridView1.TabIndex = 0;
this.dataGridView1.CellContentClick += new System.Windows.Forms.DataGridViewCellEventHandler(this.DataGridView1_CellContentClick);
//
// Original
//
this.Original.HeaderText = "Original";
this.Original.Name = "Original";
this.Original.ReadOnly = true;
this.Original.SortMode = System.Windows.Forms.DataGridViewColumnSortMode.NotSortable;
this.Original.Visible = false;
this.Original.Width = 48;
//
// Delete
//
this.Delete.HeaderText = "Delete";
this.Delete.Name = "Delete";
this.Delete.ReadOnly = true;
this.Delete.Text = "x";
this.Delete.Width = 44;
//
// Filter
//
this.Filter.HeaderText = "Filter";
this.Filter.Name = "Filter";
this.Filter.SortMode = System.Windows.Forms.DataGridViewColumnSortMode.NotSortable;
this.Filter.Width = 35;
//
// MoveUp
//
this.MoveUp.HeaderText = "Move Up";
this.MoveUp.Name = "MoveUp";
this.MoveUp.ReadOnly = true;
this.MoveUp.Text = "^";
this.MoveUp.Width = 57;
//
// MoveDown
//
this.MoveDown.HeaderText = "Move Down";
this.MoveDown.Name = "MoveDown";
this.MoveDown.ReadOnly = true;
this.MoveDown.Text = "v";
this.MoveDown.Width = 71;
//
// EditQuickFilters
//
this.AcceptButton = this.saveBtn;
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.CancelButton = this.cancelBtn;
this.ClientSize = new System.Drawing.Size(800, 450);
this.Controls.Add(this.dataGridView1);
this.Controls.Add(this.cancelBtn);
this.Controls.Add(this.saveBtn);
this.Name = "EditQuickFilters";
this.Text = "Edit Quick Filters";
((System.ComponentModel.ISupportInitialize)(this.dataGridView1)).EndInit();
this.ResumeLayout(false);
}
#endregion
private System.Windows.Forms.Button cancelBtn;
private System.Windows.Forms.Button saveBtn;
private System.Windows.Forms.DataGridView dataGridView1;
private System.Windows.Forms.DataGridViewTextBoxColumn Original;
private System.Windows.Forms.DataGridViewButtonColumn Delete;
private System.Windows.Forms.DataGridViewTextBoxColumn Filter;
private System.Windows.Forms.DataGridViewButtonColumn MoveUp;
private System.Windows.Forms.DataGridViewButtonColumn MoveDown;
}
}

View file

@ -0,0 +1,92 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using FileManager;
namespace LibationWinForm.Dialogs
{
public partial class EditQuickFilters : Form
{
const string COL_Original = "Original";
const string COL_Delete = "Delete";
const string COL_Filter = "Filter";
const string COL_MoveUp = "MoveUp";
const string COL_MoveDown = "MoveDown";
Form1 _parent { get; }
public EditQuickFilters(Form1 parent)
{
_parent = parent;
InitializeComponent();
dataGridView1.Columns[COL_Filter].AutoSizeMode = DataGridViewAutoSizeColumnMode.Fill;
populateFilters();
}
private void populateFilters()
{
var filters = QuickFilters.Filters;
if (!filters.Any())
return;
foreach (var filter in filters)
dataGridView1.Rows.Add(filter, "X", filter, "\u25B2", "\u25BC");
}
private void SaveBtn_Click(object sender, EventArgs e)
{
var list = dataGridView1.Rows
.OfType<DataGridViewRow>()
.Select(r => r.Cells[COL_Filter].Value?.ToString())
.ToList();
QuickFilters.ReplaceAll(list);
_parent.UpdateFilterDropDown();
this.Close();
}
private void CancelBtn_Click(object sender, EventArgs e) => this.Close();
private void DataGridView1_CellContentClick(object sender, DataGridViewCellEventArgs e)
{
var dgv = (DataGridView)sender;
var col = dgv.Columns[e.ColumnIndex];
if (col is DataGridViewButtonColumn && e.RowIndex >= 0)
{
var row = dgv.Rows[e.RowIndex];
switch (col.Name)
{
case COL_Delete:
// if final/edit row: do nothing
if (e.RowIndex < dgv.RowCount - 1)
dgv.Rows.Remove(row);
break;
case COL_MoveUp:
// if top: do nothing
if (e.RowIndex < 1)
break;
dgv.Rows.Remove(row);
dgv.Rows.Insert(e.RowIndex - 1, row);
break;
case COL_MoveDown:
// if final/edit row or bottom filter row: do nothing
if (e.RowIndex >= dgv.RowCount - 2)
break;
dgv.Rows.Remove(row);
dgv.Rows.Insert(e.RowIndex + 1, row);
break;
}
}
}
}
}

View file

@ -0,0 +1,135 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<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="Original.UserAddedColumn" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<value>True</value>
</metadata>
<metadata name="Delete.UserAddedColumn" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<value>True</value>
</metadata>
<metadata name="Filter.UserAddedColumn" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<value>True</value>
</metadata>
<metadata name="MoveUp.UserAddedColumn" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<value>True</value>
</metadata>
<metadata name="MoveDown.UserAddedColumn" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<value>True</value>
</metadata>
</root>

View file

@ -0,0 +1,91 @@
namespace LibationWinForm
{
partial class EditTagsDialog
{
/// <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.SaveBtn = new System.Windows.Forms.Button();
this.newTagsTb = new System.Windows.Forms.TextBox();
this.label1 = new System.Windows.Forms.Label();
this.SuspendLayout();
//
// SaveBtn
//
this.SaveBtn.Location = new System.Drawing.Point(396, 25);
this.SaveBtn.Name = "SaveBtn";
this.SaveBtn.Size = new System.Drawing.Size(75, 23);
this.SaveBtn.TabIndex = 1;
this.SaveBtn.Text = "Save";
this.SaveBtn.UseVisualStyleBackColor = true;
this.SaveBtn.Click += new System.EventHandler(this.SaveBtn_Click);
//
// newTagsTb
//
this.newTagsTb.Location = new System.Drawing.Point(12, 27);
this.newTagsTb.Name = "newTagsTb";
this.newTagsTb.ScrollBars = System.Windows.Forms.ScrollBars.Both;
this.newTagsTb.Size = new System.Drawing.Size(375, 20);
this.newTagsTb.TabIndex = 0;
//
// label1
//
this.label1.AutoSize = true;
this.label1.Location = new System.Drawing.Point(12, 9);
this.label1.Name = "label1";
this.label1.Size = new System.Drawing.Size(424, 13);
this.label1.TabIndex = 2;
this.label1.Text = "Tags are separated by a space. Each tag can contain letters, numbers, and undersc" +
"ores";
//
// EditTagsDialog
//
this.AcceptButton = this.SaveBtn;
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(483, 60);
this.Controls.Add(this.label1);
this.Controls.Add(this.newTagsTb);
this.Controls.Add(this.SaveBtn);
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog;
this.MaximizeBox = false;
this.MinimizeBox = false;
this.Name = "EditTagsDialog";
this.ShowIcon = false;
this.ShowInTaskbar = false;
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
this.Text = "Edit Tags";
this.ResumeLayout(false);
this.PerformLayout();
}
#endregion
private System.Windows.Forms.Button SaveBtn;
private System.Windows.Forms.TextBox newTagsTb;
private System.Windows.Forms.Label label1;
}
}

View file

@ -0,0 +1,27 @@
using System;
using System.Windows.Forms;
namespace LibationWinForm
{
public partial class EditTagsDialog : Form
{
public string NewTags { get; private set; }
public EditTagsDialog()
{
InitializeComponent();
}
public EditTagsDialog(string title, string rawTags) : this()
{
this.Text = $"Edit Tags - {title}";
this.newTagsTb.Text = rawTags;
}
private void SaveBtn_Click(object sender, EventArgs e)
{
NewTags = this.newTagsTb.Text;
DialogResult = DialogResult.OK;
}
}
}

View file

@ -0,0 +1,120 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<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>
</root>

View file

@ -0,0 +1,8 @@
namespace LibationWinForm
{
public interface IIndexLibraryDialog : IRunnableDialog
{
int TotalBooksProcessed { get; }
int NewBooksAdded { get; }
}
}

View file

@ -0,0 +1,20 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace LibationWinForm
{
public interface IRunnableDialog : IValidatable
{
IButtonControl AcceptButton { get; set; }
Control.ControlCollection Controls { get; }
Task DoMainWorkAsync();
string SuccessMessage { get; }
DialogResult ShowDialog();
DialogResult DialogResult { get; set; }
void Close();
}
}

View file

@ -0,0 +1,96 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.Windows.Forms;
using Dinah.Core.Windows.Forms;
namespace LibationWinForm
{
public static class IRunnableDialogExt
{
public static DialogResult RunDialog(this IRunnableDialog dialog)
{
// hook up runner before dialog.ShowDialog for all
var acceptButton = (ButtonBase)dialog.AcceptButton;
acceptButton.Click += acceptButton_Click;
return dialog.ShowDialog();
}
// running/workflow logic is in IndexDialogRunner.Run()
private static async void acceptButton_Click(object sender, EventArgs e)
{
var form = ((Control)sender).FindForm();
var iRunnableDialog = form as IRunnableDialog;
try
{
await iRunnableDialog.Run();
}
catch (Exception ex)
{
throw new Exception("Did the database get created correctly? Including seed data. Eg: Update-Database", ex);
}
}
public static async Task Run(this IRunnableDialog dialog)
{
// validate children
// OfType<T>() -- skips items which aren't of the required type
// Cast<T>() -- throws an exception
var errorStrings = dialog
// get children
.Controls
.GetControlListRecursive()
.OfType<IValidatable>()
// and self
.Append(dialog)
// validate. get errors
.Select(c => c.StringBasedValidate())
// ignore successes
.Where(e => e != null);
if (errorStrings.Any())
{
MessageBox.Show(errorStrings.Aggregate((a, b) => a + "\r\n" + b));
return;
}
// get top level controls only. If Enabled, disable and push on stack
var disabledStack = disable(dialog);
// lazy-man's async. also violates the intent of async/await.
// use here for now simply for UI responsiveness
await dialog.DoMainWorkAsync().ConfigureAwait(true);
// after running, unwind and re-enable
enable(disabledStack);
MessageBox.Show(dialog.SuccessMessage);
dialog.DialogResult = DialogResult.OK;
dialog.Close();
}
static Stack<Control> disable(IRunnableDialog dialog)
{
var disableStack = new Stack<Control>();
foreach (Control ctrl in dialog.Controls)
{
if (ctrl.Enabled)
{
disableStack.Push(ctrl);
ctrl.Enabled = false;
}
}
return disableStack;
}
static void enable(Stack<Control> disabledStack)
{
while (disabledStack.Count > 0)
{
var ctrl = disabledStack.Pop();
ctrl.Enabled = true;
}
}
}
}

View file

@ -0,0 +1,15 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace LibationWinForm
{
public interface IValidatable
{
// forms has a framework for ValidateChildren and ErrorProvider.s
// i don't feel like setting it up right now. doing this instead
string StringBasedValidate();
}
}

View file

@ -0,0 +1,78 @@
namespace LibationWinForm
{
partial class ScanLibraryDialog
{
/// <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.websiteProcessorControl1 = new LibationWinForm.WebsiteProcessorControl();
this.BeginScanBtn = new System.Windows.Forms.Button();
this.SuspendLayout();
//
// websiteProcessorControl1
//
this.websiteProcessorControl1.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.websiteProcessorControl1.Location = new System.Drawing.Point(12, 12);
this.websiteProcessorControl1.Name = "websiteProcessorControl1";
this.websiteProcessorControl1.Size = new System.Drawing.Size(324, 137);
this.websiteProcessorControl1.TabIndex = 0;
//
// BeginScanBtn
//
this.BeginScanBtn.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.BeginScanBtn.Location = new System.Drawing.Point(12, 155);
this.BeginScanBtn.Name = "BeginScanBtn";
this.BeginScanBtn.Size = new System.Drawing.Size(324, 23);
this.BeginScanBtn.TabIndex = 1;
this.BeginScanBtn.Text = "BEGIN SCAN";
this.BeginScanBtn.UseVisualStyleBackColor = true;
//
// ScanLibraryDialog
//
this.AcceptButton = this.BeginScanBtn;
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(348, 190);
this.Controls.Add(this.BeginScanBtn);
this.Controls.Add(this.websiteProcessorControl1);
this.Name = "ScanLibraryDialog";
this.ShowIcon = false;
this.ShowInTaskbar = false;
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
this.Text = "Scan Library";
this.ResumeLayout(false);
}
#endregion
private WebsiteProcessorControl websiteProcessorControl1;
private System.Windows.Forms.Button BeginScanBtn;
}
}

View file

@ -0,0 +1,42 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Threading.Tasks;
using System.Windows.Forms;
using DomainServices;
using Dinah.Core;
namespace LibationWinForm
{
public partial class ScanLibraryDialog : Form, IIndexLibraryDialog
{
public ScanLibraryDialog()
{
InitializeComponent();
}
public string StringBasedValidate() => null;
List<string> successMessages = new List<string>();
public string SuccessMessage => string.Join("\r\n", successMessages);
public int NewBooksAdded { get; private set; }
public int TotalBooksProcessed { get; private set; }
public async Task DoMainWorkAsync()
{
List<FileInfo> jsonFilepaths;
using (var pageRetriever = websiteProcessorControl1.GetPageRetriever())
jsonFilepaths = await DownloadLibrary.DownloadLibraryAsync(pageRetriever).ConfigureAwait(false);
successMessages.Add($"Downloaded {"library page".PluralizeWithCount(jsonFilepaths.Count)}");
(TotalBooksProcessed, NewBooksAdded) = await Indexer
.IndexLibraryAsync(jsonFilepaths)
.ConfigureAwait(false);
successMessages.Add($"Total processed: {TotalBooksProcessed}");
successMessages.Add($"New: {NewBooksAdded}");
}
}
}

View file

@ -0,0 +1,120 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<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>
</root>

View file

@ -0,0 +1,161 @@
namespace LibationWinForm
{
partial class WebsiteProcessorControl
{
/// <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 Component 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.AuthGb = new System.Windows.Forms.GroupBox();
this.AuthRb_Browserless = new System.Windows.Forms.RadioButton();
this.AuthRb_UseCanonicalChrome = new System.Windows.Forms.RadioButton();
this.label3 = new System.Windows.Forms.Label();
this.AuthRb_ManualLogin = new System.Windows.Forms.RadioButton();
this.label2 = new System.Windows.Forms.Label();
this.PasswordTb = new System.Windows.Forms.TextBox();
this.UsernameTb = new System.Windows.Forms.TextBox();
this.AuthGb.SuspendLayout();
this.SuspendLayout();
//
// AuthGb
//
this.AuthGb.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.AuthGb.Controls.Add(this.AuthRb_Browserless);
this.AuthGb.Controls.Add(this.AuthRb_UseCanonicalChrome);
this.AuthGb.Controls.Add(this.label3);
this.AuthGb.Controls.Add(this.AuthRb_ManualLogin);
this.AuthGb.Controls.Add(this.label2);
this.AuthGb.Controls.Add(this.PasswordTb);
this.AuthGb.Controls.Add(this.UsernameTb);
this.AuthGb.Location = new System.Drawing.Point(0, 0);
this.AuthGb.Name = "AuthGb";
this.AuthGb.Size = new System.Drawing.Size(324, 137);
this.AuthGb.TabIndex = 1;
this.AuthGb.TabStop = false;
this.AuthGb.Text = "Authentication";
//
// AuthRb_Browserless
//
this.AuthRb_Browserless.AutoSize = true;
this.AuthRb_Browserless.Checked = true;
this.AuthRb_Browserless.Location = new System.Drawing.Point(6, 19);
this.AuthRb_Browserless.Name = "AuthRb_Browserless";
this.AuthRb_Browserless.Size = new System.Drawing.Size(143, 17);
this.AuthRb_Browserless.TabIndex = 0;
this.AuthRb_Browserless.TabStop = true;
this.AuthRb_Browserless.Text = "Browserless with cookies";
this.AuthRb_Browserless.UseVisualStyleBackColor = true;
//
// AuthRb_UseCanonicalChrome
//
this.AuthRb_UseCanonicalChrome.AutoSize = true;
this.AuthRb_UseCanonicalChrome.Location = new System.Drawing.Point(6, 114);
this.AuthRb_UseCanonicalChrome.Name = "AuthRb_UseCanonicalChrome";
this.AuthRb_UseCanonicalChrome.Size = new System.Drawing.Size(216, 17);
this.AuthRb_UseCanonicalChrome.TabIndex = 6;
this.AuthRb_UseCanonicalChrome.Text = "Use Canonical Chrome. SEE WARNING";
this.AuthRb_UseCanonicalChrome.UseVisualStyleBackColor = true;
this.AuthRb_UseCanonicalChrome.CheckedChanged += new System.EventHandler(this.AuthRb_UseCanonicalChrome_CheckedChanged);
//
// label3
//
this.label3.AutoSize = true;
this.label3.Location = new System.Drawing.Point(27, 91);
this.label3.Name = "label3";
this.label3.Size = new System.Drawing.Size(53, 13);
this.label3.TabIndex = 4;
this.label3.Text = "Password";
//
// AuthRb_ManualLogin
//
this.AuthRb_ManualLogin.AutoSize = true;
this.AuthRb_ManualLogin.Location = new System.Drawing.Point(6, 42);
this.AuthRb_ManualLogin.Name = "AuthRb_ManualLogin";
this.AuthRb_ManualLogin.Size = new System.Drawing.Size(89, 17);
this.AuthRb_ManualLogin.TabIndex = 1;
this.AuthRb_ManualLogin.Text = "Manual Login";
this.AuthRb_ManualLogin.UseVisualStyleBackColor = true;
//
// label2
//
this.label2.AutoSize = true;
this.label2.Location = new System.Drawing.Point(27, 65);
this.label2.Name = "label2";
this.label2.Size = new System.Drawing.Size(85, 13);
this.label2.TabIndex = 2;
this.label2.Text = "Username/Email";
//
// PasswordTb
//
this.PasswordTb.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.PasswordTb.Location = new System.Drawing.Point(118, 88);
this.PasswordTb.Name = "PasswordTb";
this.PasswordTb.PasswordChar = '*';
this.PasswordTb.Size = new System.Drawing.Size(200, 20);
this.PasswordTb.TabIndex = 5;
this.PasswordTb.TextChanged += new System.EventHandler(this.UserIsEnteringLoginInfo);
this.PasswordTb.KeyPress += new System.Windows.Forms.KeyPressEventHandler(this.UsernamePasswordTb_KeyPress);
this.PasswordTb.MouseUp += new System.Windows.Forms.MouseEventHandler(this.UserIsEnteringLoginInfo);
//
// UsernameTb
//
this.UsernameTb.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.UsernameTb.Location = new System.Drawing.Point(118, 62);
this.UsernameTb.Name = "UsernameTb";
this.UsernameTb.Size = new System.Drawing.Size(200, 20);
this.UsernameTb.TabIndex = 3;
this.UsernameTb.TextChanged += new System.EventHandler(this.UserIsEnteringLoginInfo);
this.UsernameTb.KeyPress += new System.Windows.Forms.KeyPressEventHandler(this.UsernamePasswordTb_KeyPress);
this.UsernameTb.MouseUp += new System.Windows.Forms.MouseEventHandler(this.UserIsEnteringLoginInfo);
//
// WebsiteProcessorControl
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.Controls.Add(this.AuthGb);
this.Name = "WebsiteProcessorControl";
this.Size = new System.Drawing.Size(324, 137);
this.AuthGb.ResumeLayout(false);
this.AuthGb.PerformLayout();
this.ResumeLayout(false);
}
#endregion
private System.Windows.Forms.GroupBox AuthGb;
private System.Windows.Forms.RadioButton AuthRb_UseCanonicalChrome;
private System.Windows.Forms.Label label3;
private System.Windows.Forms.RadioButton AuthRb_ManualLogin;
private System.Windows.Forms.Label label2;
private System.Windows.Forms.TextBox PasswordTb;
private System.Windows.Forms.TextBox UsernameTb;
private System.Windows.Forms.RadioButton AuthRb_Browserless;
}
}

View file

@ -0,0 +1,47 @@
using System;
using System.Windows.Forms;
using AudibleDotComAutomation;
namespace LibationWinForm
{
public partial class WebsiteProcessorControl : UserControl, IValidatable
{
public event EventHandler<KeyPressEventArgs> KeyPressSubmit;
public WebsiteProcessorControl()
{
InitializeComponent();
}
public IPageRetriever GetPageRetriever()
=> AuthRb_UseCanonicalChrome.Checked ? new UserDataSeleniumRetriever()
: AuthRb_Browserless.Checked ? (IPageRetriever)new BrowserlessRetriever()
: new ManualLoginSeleniumRetriever(UsernameTb.Text, PasswordTb.Text);
public string StringBasedValidate()
{
if (AuthRb_ManualLogin.Checked && (string.IsNullOrWhiteSpace(UsernameTb.Text) || string.IsNullOrWhiteSpace(PasswordTb.Text)))
return "must fill in username and password";
return null;
}
private void UsernamePasswordTb_KeyPress(object sender, KeyPressEventArgs e)
{
if (e.KeyChar == (char)Keys.Return)
{
KeyPressSubmit?.Invoke(sender, e);
// call your method for action on enter
e.Handled = true; // suppress default handling
}
}
private void UserIsEnteringLoginInfo(object sender, EventArgs e) => AuthRb_ManualLogin.Checked = true;
private void AuthRb_UseCanonicalChrome_CheckedChanged(object sender, EventArgs e)
{
if (AuthRb_UseCanonicalChrome.Checked)
MessageBox.Show(@"A canonical version of Chrome will be used including User Data, cookies. etc. Selenium chromedriver won't launch URL if another Chrome instance is open");
}
}
}

View file

@ -0,0 +1,120 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<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>
</root>

View file

@ -0,0 +1,133 @@
namespace LibationWinForm.Dialogs
{
partial class SearchSyntaxDialog
{
/// <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.label1 = new System.Windows.Forms.Label();
this.label2 = new System.Windows.Forms.Label();
this.label3 = new System.Windows.Forms.Label();
this.label4 = new System.Windows.Forms.Label();
this.label5 = new System.Windows.Forms.Label();
this.closeBtn = new System.Windows.Forms.Button();
this.SuspendLayout();
//
// label1
//
this.label1.AutoSize = true;
this.label1.Location = new System.Drawing.Point(12, 9);
this.label1.Name = "label1";
this.label1.Size = new System.Drawing.Size(358, 52);
this.label1.TabIndex = 0;
this.label1.Text = "Full Lucene query syntax is supported\r\nFields with similar names are synomyns (eg" +
": Author, Authors, AuthorNames)\r\n\r\nTAG FORMAT: [tagName]";
//
// label2
//
this.label2.AutoSize = true;
this.label2.Location = new System.Drawing.Point(12, 71);
this.label2.Name = "label2";
this.label2.Size = new System.Drawing.Size(118, 65);
this.label2.TabIndex = 1;
this.label2.Text = "STRING FIELDS\r\n\r\nSearch for wizard of oz:\r\n title:oz\r\n title:\"wizard of o" +
"z\"";
//
// label3
//
this.label3.AutoSize = true;
this.label3.Location = new System.Drawing.Point(233, 71);
this.label3.Name = "label3";
this.label3.Size = new System.Drawing.Size(195, 78);
this.label3.TabIndex = 2;
this.label3.Text = "NUMBER FIELDS\r\n\r\nFind books between 1-100 minutes long\r\n length:[1 TO 100]\r\nF" +
"ind books exactly 1 hr long\r\n length:60";
//
// label4
//
this.label4.AutoSize = true;
this.label4.Location = new System.Drawing.Point(454, 71);
this.label4.Name = "label4";
this.label4.Size = new System.Drawing.Size(168, 52);
this.label4.TabIndex = 3;
this.label4.Text = "BOOL FIELDS\r\n\r\nFind books that you haven\'t rated:\r\n -IsRated";
//
// label5
//
this.label5.AutoSize = true;
this.label5.Location = new System.Drawing.Point(673, 71);
this.label5.Name = "label5";
this.label5.Size = new System.Drawing.Size(257, 78);
this.label5.TabIndex = 4;
this.label5.Text = "ID FIELDS\r\n\r\nAlice\'s Adventures in Wonderland (ID: B015D78L0U)\r\n id:B015D78L0" +
"U\r\n\r\nAll of these are synonyms for the ID field";
//
// closeBtn
//
this.closeBtn.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
this.closeBtn.DialogResult = System.Windows.Forms.DialogResult.Cancel;
this.closeBtn.Location = new System.Drawing.Point(890, 415);
this.closeBtn.Name = "closeBtn";
this.closeBtn.Size = new System.Drawing.Size(75, 23);
this.closeBtn.TabIndex = 5;
this.closeBtn.Text = "Close";
this.closeBtn.UseVisualStyleBackColor = true;
this.closeBtn.Click += new System.EventHandler(this.CloseBtn_Click);
//
// SearchSyntaxDialog
//
this.AcceptButton = this.closeBtn;
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.CancelButton = this.closeBtn;
this.ClientSize = new System.Drawing.Size(977, 450);
this.Controls.Add(this.closeBtn);
this.Controls.Add(this.label5);
this.Controls.Add(this.label4);
this.Controls.Add(this.label3);
this.Controls.Add(this.label2);
this.Controls.Add(this.label1);
this.MaximizeBox = false;
this.MinimizeBox = false;
this.Name = "SearchSyntaxDialog";
this.ShowIcon = false;
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
this.Text = "Filter options";
this.ResumeLayout(false);
this.PerformLayout();
}
#endregion
private System.Windows.Forms.Label label1;
private System.Windows.Forms.Label label2;
private System.Windows.Forms.Label label3;
private System.Windows.Forms.Label label4;
private System.Windows.Forms.Label label5;
private System.Windows.Forms.Button closeBtn;
}
}

View file

@ -0,0 +1,20 @@
using System;
using System.Windows.Forms;
namespace LibationWinForm.Dialogs
{
public partial class SearchSyntaxDialog : Form
{
public SearchSyntaxDialog()
{
InitializeComponent();
label2.Text += "\r\n\r\n" + string.Join("\r\n", LibationSearchEngine.SearchEngine.GetSearchStringFields());
label3.Text += "\r\n\r\n" + string.Join("\r\n", LibationSearchEngine.SearchEngine.GetSearchNumberFields());
label4.Text += "\r\n\r\n" + string.Join("\r\n", LibationSearchEngine.SearchEngine.GetSearchBoolFields());
label5.Text += "\r\n\r\n" + string.Join("\r\n", LibationSearchEngine.SearchEngine.GetSearchIdFields());
}
private void CloseBtn_Click(object sender, EventArgs e) => this.Close();
}
}

View file

@ -0,0 +1,120 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<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>
</root>

View file

@ -0,0 +1,411 @@
namespace LibationWinForm
{
partial class SettingsDialog
{
/// <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.settingsFileLbl = new System.Windows.Forms.Label();
this.settingsFileTb = new System.Windows.Forms.TextBox();
this.decryptKeyLbl = new System.Windows.Forms.Label();
this.decryptKeyTb = new System.Windows.Forms.TextBox();
this.booksLocationLbl = new System.Windows.Forms.Label();
this.booksLocationTb = new System.Windows.Forms.TextBox();
this.booksLocationSearchBtn = new System.Windows.Forms.Button();
this.settingsFileDescLbl = new System.Windows.Forms.Label();
this.decryptKeyDescLbl = new System.Windows.Forms.Label();
this.booksLocationDescLbl = new System.Windows.Forms.Label();
this.libationFilesGb = new System.Windows.Forms.GroupBox();
this.libationFilesDescLbl = new System.Windows.Forms.Label();
this.libationFilesCustomBtn = new System.Windows.Forms.Button();
this.libationFilesCustomTb = new System.Windows.Forms.TextBox();
this.libationFilesCustomRb = new System.Windows.Forms.RadioButton();
this.libationFilesMyDocsRb = new System.Windows.Forms.RadioButton();
this.libationFilesRootRb = new System.Windows.Forms.RadioButton();
this.downloadsInProgressGb = new System.Windows.Forms.GroupBox();
this.downloadsInProgressLibationFilesRb = new System.Windows.Forms.RadioButton();
this.downloadsInProgressWinTempRb = new System.Windows.Forms.RadioButton();
this.downloadsInProgressDescLbl = new System.Windows.Forms.Label();
this.decryptInProgressGb = new System.Windows.Forms.GroupBox();
this.decryptInProgressLibationFilesRb = new System.Windows.Forms.RadioButton();
this.decryptInProgressWinTempRb = new System.Windows.Forms.RadioButton();
this.decryptInProgressDescLbl = new System.Windows.Forms.Label();
this.saveBtn = new System.Windows.Forms.Button();
this.cancelBtn = new System.Windows.Forms.Button();
this.libationFilesGb.SuspendLayout();
this.downloadsInProgressGb.SuspendLayout();
this.decryptInProgressGb.SuspendLayout();
this.SuspendLayout();
//
// settingsFileLbl
//
this.settingsFileLbl.AutoSize = true;
this.settingsFileLbl.Location = new System.Drawing.Point(7, 15);
this.settingsFileLbl.Name = "settingsFileLbl";
this.settingsFileLbl.Size = new System.Drawing.Size(61, 13);
this.settingsFileLbl.TabIndex = 0;
this.settingsFileLbl.Text = "Settings file";
//
// settingsFileTb
//
this.settingsFileTb.Location = new System.Drawing.Point(90, 12);
this.settingsFileTb.Name = "settingsFileTb";
this.settingsFileTb.ReadOnly = true;
this.settingsFileTb.Size = new System.Drawing.Size(698, 20);
this.settingsFileTb.TabIndex = 1;
//
// decryptKeyLbl
//
this.decryptKeyLbl.AutoSize = true;
this.decryptKeyLbl.Location = new System.Drawing.Point(7, 59);
this.decryptKeyLbl.Name = "decryptKeyLbl";
this.decryptKeyLbl.Size = new System.Drawing.Size(64, 13);
this.decryptKeyLbl.TabIndex = 3;
this.decryptKeyLbl.Text = "Decrypt key";
//
// decryptKeyTb
//
this.decryptKeyTb.Location = new System.Drawing.Point(90, 56);
this.decryptKeyTb.Name = "decryptKeyTb";
this.decryptKeyTb.Size = new System.Drawing.Size(100, 20);
this.decryptKeyTb.TabIndex = 4;
//
// booksLocationLbl
//
this.booksLocationLbl.AutoSize = true;
this.booksLocationLbl.Location = new System.Drawing.Point(7, 103);
this.booksLocationLbl.Name = "booksLocationLbl";
this.booksLocationLbl.Size = new System.Drawing.Size(77, 13);
this.booksLocationLbl.TabIndex = 6;
this.booksLocationLbl.Text = "Books location";
//
// booksLocationTb
//
this.booksLocationTb.Location = new System.Drawing.Point(90, 100);
this.booksLocationTb.Name = "booksLocationTb";
this.booksLocationTb.Size = new System.Drawing.Size(657, 20);
this.booksLocationTb.TabIndex = 7;
//
// booksLocationSearchBtn
//
this.booksLocationSearchBtn.Location = new System.Drawing.Point(753, 98);
this.booksLocationSearchBtn.Name = "booksLocationSearchBtn";
this.booksLocationSearchBtn.Size = new System.Drawing.Size(35, 23);
this.booksLocationSearchBtn.TabIndex = 8;
this.booksLocationSearchBtn.Text = "...";
this.booksLocationSearchBtn.UseVisualStyleBackColor = true;
this.booksLocationSearchBtn.Click += new System.EventHandler(this.booksLocationSearchBtn_Click);
//
// settingsFileDescLbl
//
this.settingsFileDescLbl.AutoSize = true;
this.settingsFileDescLbl.Location = new System.Drawing.Point(87, 35);
this.settingsFileDescLbl.Name = "settingsFileDescLbl";
this.settingsFileDescLbl.Size = new System.Drawing.Size(36, 13);
this.settingsFileDescLbl.TabIndex = 2;
this.settingsFileDescLbl.Text = "[desc]";
//
// decryptKeyDescLbl
//
this.decryptKeyDescLbl.AutoSize = true;
this.decryptKeyDescLbl.Location = new System.Drawing.Point(87, 79);
this.decryptKeyDescLbl.Name = "decryptKeyDescLbl";
this.decryptKeyDescLbl.Size = new System.Drawing.Size(36, 13);
this.decryptKeyDescLbl.TabIndex = 5;
this.decryptKeyDescLbl.Text = "[desc]";
//
// booksLocationDescLbl
//
this.booksLocationDescLbl.AutoSize = true;
this.booksLocationDescLbl.Location = new System.Drawing.Point(87, 123);
this.booksLocationDescLbl.Name = "booksLocationDescLbl";
this.booksLocationDescLbl.Size = new System.Drawing.Size(36, 13);
this.booksLocationDescLbl.TabIndex = 9;
this.booksLocationDescLbl.Text = "[desc]";
//
// libationFilesGb
//
this.libationFilesGb.Controls.Add(this.libationFilesDescLbl);
this.libationFilesGb.Controls.Add(this.libationFilesCustomBtn);
this.libationFilesGb.Controls.Add(this.libationFilesCustomTb);
this.libationFilesGb.Controls.Add(this.libationFilesCustomRb);
this.libationFilesGb.Controls.Add(this.libationFilesMyDocsRb);
this.libationFilesGb.Controls.Add(this.libationFilesRootRb);
this.libationFilesGb.Location = new System.Drawing.Point(12, 139);
this.libationFilesGb.Name = "libationFilesGb";
this.libationFilesGb.Size = new System.Drawing.Size(776, 131);
this.libationFilesGb.TabIndex = 10;
this.libationFilesGb.TabStop = false;
this.libationFilesGb.Text = "Libation files";
//
// libationFilesDescLbl
//
this.libationFilesDescLbl.AutoSize = true;
this.libationFilesDescLbl.Location = new System.Drawing.Point(6, 16);
this.libationFilesDescLbl.Name = "libationFilesDescLbl";
this.libationFilesDescLbl.Size = new System.Drawing.Size(36, 13);
this.libationFilesDescLbl.TabIndex = 0;
this.libationFilesDescLbl.Text = "[desc]";
//
// libationFilesCustomBtn
//
this.libationFilesCustomBtn.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
this.libationFilesCustomBtn.Location = new System.Drawing.Point(741, 102);
this.libationFilesCustomBtn.Name = "libationFilesCustomBtn";
this.libationFilesCustomBtn.Size = new System.Drawing.Size(35, 23);
this.libationFilesCustomBtn.TabIndex = 5;
this.libationFilesCustomBtn.Text = "...";
this.libationFilesCustomBtn.UseVisualStyleBackColor = true;
this.libationFilesCustomBtn.Click += new System.EventHandler(this.libationFilesCustomBtn_Click);
//
// libationFilesCustomTb
//
this.libationFilesCustomTb.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.libationFilesCustomTb.Location = new System.Drawing.Point(29, 104);
this.libationFilesCustomTb.Name = "libationFilesCustomTb";
this.libationFilesCustomTb.Size = new System.Drawing.Size(706, 20);
this.libationFilesCustomTb.TabIndex = 4;
this.libationFilesCustomTb.TextChanged += new System.EventHandler(this.libationFiles_Changed);
//
// libationFilesCustomRb
//
this.libationFilesCustomRb.AutoSize = true;
this.libationFilesCustomRb.Location = new System.Drawing.Point(9, 107);
this.libationFilesCustomRb.Name = "libationFilesCustomRb";
this.libationFilesCustomRb.Size = new System.Drawing.Size(14, 13);
this.libationFilesCustomRb.TabIndex = 3;
this.libationFilesCustomRb.TabStop = true;
this.libationFilesCustomRb.UseVisualStyleBackColor = true;
//
// libationFilesMyDocsRb
//
this.libationFilesMyDocsRb.AutoSize = true;
this.libationFilesMyDocsRb.CheckAlign = System.Drawing.ContentAlignment.TopLeft;
this.libationFilesMyDocsRb.Location = new System.Drawing.Point(9, 68);
this.libationFilesMyDocsRb.Name = "libationFilesMyDocsRb";
this.libationFilesMyDocsRb.Size = new System.Drawing.Size(111, 30);
this.libationFilesMyDocsRb.TabIndex = 2;
this.libationFilesMyDocsRb.TabStop = true;
this.libationFilesMyDocsRb.Text = "[desc]\r\n[myDocs\\Libation]";
this.libationFilesMyDocsRb.UseVisualStyleBackColor = true;
this.libationFilesMyDocsRb.CheckedChanged += new System.EventHandler(this.libationFiles_Changed);
//
// libationFilesRootRb
//
this.libationFilesRootRb.AutoSize = true;
this.libationFilesRootRb.CheckAlign = System.Drawing.ContentAlignment.TopLeft;
this.libationFilesRootRb.Location = new System.Drawing.Point(9, 32);
this.libationFilesRootRb.Name = "libationFilesRootRb";
this.libationFilesRootRb.Size = new System.Drawing.Size(113, 30);
this.libationFilesRootRb.TabIndex = 1;
this.libationFilesRootRb.TabStop = true;
this.libationFilesRootRb.Text = "[desc]\r\n[exeRoot\\Libation]";
this.libationFilesRootRb.UseVisualStyleBackColor = true;
this.libationFilesRootRb.CheckedChanged += new System.EventHandler(this.libationFiles_Changed);
//
// downloadsInProgressGb
//
this.downloadsInProgressGb.Controls.Add(this.downloadsInProgressLibationFilesRb);
this.downloadsInProgressGb.Controls.Add(this.downloadsInProgressWinTempRb);
this.downloadsInProgressGb.Controls.Add(this.downloadsInProgressDescLbl);
this.downloadsInProgressGb.Location = new System.Drawing.Point(12, 276);
this.downloadsInProgressGb.Name = "downloadsInProgressGb";
this.downloadsInProgressGb.Size = new System.Drawing.Size(776, 117);
this.downloadsInProgressGb.TabIndex = 11;
this.downloadsInProgressGb.TabStop = false;
this.downloadsInProgressGb.Text = "Downloads in progress";
//
// downloadsInProgressLibationFilesRb
//
this.downloadsInProgressLibationFilesRb.AutoSize = true;
this.downloadsInProgressLibationFilesRb.CheckAlign = System.Drawing.ContentAlignment.TopLeft;
this.downloadsInProgressLibationFilesRb.Location = new System.Drawing.Point(9, 81);
this.downloadsInProgressLibationFilesRb.Name = "downloadsInProgressLibationFilesRb";
this.downloadsInProgressLibationFilesRb.Size = new System.Drawing.Size(193, 30);
this.downloadsInProgressLibationFilesRb.TabIndex = 2;
this.downloadsInProgressLibationFilesRb.TabStop = true;
this.downloadsInProgressLibationFilesRb.Text = "[desc]\r\n[libationFiles\\DownloadsInProgress]";
this.downloadsInProgressLibationFilesRb.UseVisualStyleBackColor = true;
//
// downloadsInProgressWinTempRb
//
this.downloadsInProgressWinTempRb.AutoSize = true;
this.downloadsInProgressWinTempRb.CheckAlign = System.Drawing.ContentAlignment.TopLeft;
this.downloadsInProgressWinTempRb.Location = new System.Drawing.Point(9, 45);
this.downloadsInProgressWinTempRb.Name = "downloadsInProgressWinTempRb";
this.downloadsInProgressWinTempRb.Size = new System.Drawing.Size(182, 30);
this.downloadsInProgressWinTempRb.TabIndex = 1;
this.downloadsInProgressWinTempRb.TabStop = true;
this.downloadsInProgressWinTempRb.Text = "[desc]\r\n[winTemp\\DownloadsInProgress]";
this.downloadsInProgressWinTempRb.UseVisualStyleBackColor = true;
//
// downloadsInProgressDescLbl
//
this.downloadsInProgressDescLbl.AutoSize = true;
this.downloadsInProgressDescLbl.Location = new System.Drawing.Point(6, 16);
this.downloadsInProgressDescLbl.Name = "downloadsInProgressDescLbl";
this.downloadsInProgressDescLbl.Size = new System.Drawing.Size(38, 26);
this.downloadsInProgressDescLbl.TabIndex = 0;
this.downloadsInProgressDescLbl.Text = "[desc]\r\n[line 2]";
//
// decryptInProgressGb
//
this.decryptInProgressGb.Controls.Add(this.decryptInProgressLibationFilesRb);
this.decryptInProgressGb.Controls.Add(this.decryptInProgressWinTempRb);
this.decryptInProgressGb.Controls.Add(this.decryptInProgressDescLbl);
this.decryptInProgressGb.Location = new System.Drawing.Point(12, 399);
this.decryptInProgressGb.Name = "decryptInProgressGb";
this.decryptInProgressGb.Size = new System.Drawing.Size(776, 117);
this.decryptInProgressGb.TabIndex = 12;
this.decryptInProgressGb.TabStop = false;
this.decryptInProgressGb.Text = "Decrypt in progress";
//
// decryptInProgressLibationFilesRb
//
this.decryptInProgressLibationFilesRb.AutoSize = true;
this.decryptInProgressLibationFilesRb.CheckAlign = System.Drawing.ContentAlignment.TopLeft;
this.decryptInProgressLibationFilesRb.Location = new System.Drawing.Point(6, 81);
this.decryptInProgressLibationFilesRb.Name = "decryptInProgressLibationFilesRb";
this.decryptInProgressLibationFilesRb.Size = new System.Drawing.Size(177, 30);
this.decryptInProgressLibationFilesRb.TabIndex = 3;
this.decryptInProgressLibationFilesRb.TabStop = true;
this.decryptInProgressLibationFilesRb.Text = "[desc]\r\n[libationFiles\\DecryptInProgress]";
this.decryptInProgressLibationFilesRb.UseVisualStyleBackColor = true;
//
// decryptInProgressWinTempRb
//
this.decryptInProgressWinTempRb.AutoSize = true;
this.decryptInProgressWinTempRb.CheckAlign = System.Drawing.ContentAlignment.TopLeft;
this.decryptInProgressWinTempRb.Location = new System.Drawing.Point(6, 45);
this.decryptInProgressWinTempRb.Name = "decryptInProgressWinTempRb";
this.decryptInProgressWinTempRb.Size = new System.Drawing.Size(166, 30);
this.decryptInProgressWinTempRb.TabIndex = 2;
this.decryptInProgressWinTempRb.TabStop = true;
this.decryptInProgressWinTempRb.Text = "[desc]\r\n[winTemp\\DecryptInProgress]";
this.decryptInProgressWinTempRb.UseVisualStyleBackColor = true;
//
// decryptInProgressDescLbl
//
this.decryptInProgressDescLbl.AutoSize = true;
this.decryptInProgressDescLbl.Location = new System.Drawing.Point(6, 16);
this.decryptInProgressDescLbl.Name = "decryptInProgressDescLbl";
this.decryptInProgressDescLbl.Size = new System.Drawing.Size(38, 26);
this.decryptInProgressDescLbl.TabIndex = 1;
this.decryptInProgressDescLbl.Text = "[desc]\r\n[line 2]";
//
// saveBtn
//
this.saveBtn.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
this.saveBtn.Location = new System.Drawing.Point(612, 522);
this.saveBtn.Name = "saveBtn";
this.saveBtn.Size = new System.Drawing.Size(75, 23);
this.saveBtn.TabIndex = 13;
this.saveBtn.Text = "Save";
this.saveBtn.UseVisualStyleBackColor = true;
this.saveBtn.Click += new System.EventHandler(this.saveBtn_Click);
//
// cancelBtn
//
this.cancelBtn.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
this.cancelBtn.DialogResult = System.Windows.Forms.DialogResult.Cancel;
this.cancelBtn.Location = new System.Drawing.Point(713, 522);
this.cancelBtn.Name = "cancelBtn";
this.cancelBtn.Size = new System.Drawing.Size(75, 23);
this.cancelBtn.TabIndex = 14;
this.cancelBtn.Text = "Cancel";
this.cancelBtn.UseVisualStyleBackColor = true;
this.cancelBtn.Click += new System.EventHandler(this.cancelBtn_Click);
//
// SettingsDialog
//
this.AcceptButton = this.saveBtn;
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.CancelButton = this.cancelBtn;
this.ClientSize = new System.Drawing.Size(800, 557);
this.Controls.Add(this.cancelBtn);
this.Controls.Add(this.saveBtn);
this.Controls.Add(this.decryptInProgressGb);
this.Controls.Add(this.downloadsInProgressGb);
this.Controls.Add(this.libationFilesGb);
this.Controls.Add(this.booksLocationDescLbl);
this.Controls.Add(this.decryptKeyDescLbl);
this.Controls.Add(this.settingsFileDescLbl);
this.Controls.Add(this.booksLocationSearchBtn);
this.Controls.Add(this.booksLocationTb);
this.Controls.Add(this.booksLocationLbl);
this.Controls.Add(this.decryptKeyTb);
this.Controls.Add(this.decryptKeyLbl);
this.Controls.Add(this.settingsFileTb);
this.Controls.Add(this.settingsFileLbl);
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedToolWindow;
this.Name = "SettingsDialog";
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
this.Text = "Edit Settings";
this.Load += new System.EventHandler(this.SettingsDialog_Load);
this.libationFilesGb.ResumeLayout(false);
this.libationFilesGb.PerformLayout();
this.downloadsInProgressGb.ResumeLayout(false);
this.downloadsInProgressGb.PerformLayout();
this.decryptInProgressGb.ResumeLayout(false);
this.decryptInProgressGb.PerformLayout();
this.ResumeLayout(false);
this.PerformLayout();
}
#endregion
private System.Windows.Forms.Label settingsFileLbl;
private System.Windows.Forms.TextBox settingsFileTb;
private System.Windows.Forms.Label decryptKeyLbl;
private System.Windows.Forms.TextBox decryptKeyTb;
private System.Windows.Forms.Label booksLocationLbl;
private System.Windows.Forms.TextBox booksLocationTb;
private System.Windows.Forms.Button booksLocationSearchBtn;
private System.Windows.Forms.Label settingsFileDescLbl;
private System.Windows.Forms.Label decryptKeyDescLbl;
private System.Windows.Forms.Label booksLocationDescLbl;
private System.Windows.Forms.GroupBox libationFilesGb;
private System.Windows.Forms.Button libationFilesCustomBtn;
private System.Windows.Forms.TextBox libationFilesCustomTb;
private System.Windows.Forms.RadioButton libationFilesCustomRb;
private System.Windows.Forms.RadioButton libationFilesMyDocsRb;
private System.Windows.Forms.RadioButton libationFilesRootRb;
private System.Windows.Forms.Label libationFilesDescLbl;
private System.Windows.Forms.GroupBox downloadsInProgressGb;
private System.Windows.Forms.Label downloadsInProgressDescLbl;
private System.Windows.Forms.RadioButton downloadsInProgressWinTempRb;
private System.Windows.Forms.RadioButton downloadsInProgressLibationFilesRb;
private System.Windows.Forms.GroupBox decryptInProgressGb;
private System.Windows.Forms.Label decryptInProgressDescLbl;
private System.Windows.Forms.RadioButton decryptInProgressLibationFilesRb;
private System.Windows.Forms.RadioButton decryptInProgressWinTempRb;
private System.Windows.Forms.Button saveBtn;
private System.Windows.Forms.Button cancelBtn;
}
}

View file

@ -0,0 +1,163 @@
using FileManager;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using Dinah.Core;
namespace LibationWinForm
{
public partial class SettingsDialog : Form
{
Configuration config { get; } = Configuration.Instance;
Func<string, string> desc { get; } = Configuration.GetDescription;
string exeRoot { get; }
string myDocs { get; }
public SettingsDialog()
{
InitializeComponent();
exeRoot = Path.GetFullPath(Path.Combine(Path.GetDirectoryName(Exe.FileLocationOnDisk), "Libation"));
myDocs = Path.GetFullPath(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments), "Libation"));
}
private void SettingsDialog_Load(object sender, EventArgs e)
{
this.settingsFileTb.Text = config.Filepath;
this.settingsFileDescLbl.Text = desc(nameof(config.Filepath));
this.decryptKeyTb.Text = config.DecryptKey;
this.decryptKeyDescLbl.Text = desc(nameof(config.DecryptKey));
this.booksLocationTb.Text = config.Books;
this.booksLocationDescLbl.Text = desc(nameof(config.Books));
libationFilesDescLbl.Text = desc(nameof(config.LibationFiles));
this.libationFilesRootRb.Text = "In the same folder that Libation is running from\r\n" + exeRoot;
this.libationFilesMyDocsRb.Text = "In My Documents\r\n" + myDocs;
if (config.LibationFiles == exeRoot)
libationFilesRootRb.Checked = true;
else if (config.LibationFiles == myDocs)
libationFilesMyDocsRb.Checked = true;
else
{
libationFilesCustomRb.Checked = true;
libationFilesCustomTb.Text = config.LibationFiles;
}
this.downloadsInProgressDescLbl.Text = desc(nameof(config.DownloadsInProgressEnum));
var winTempDownloadsInProgress = Path.Combine(config.WinTemp, "DownloadsInProgress");
this.downloadsInProgressWinTempRb.Text = "In your Windows temporary folder\r\n" + winTempDownloadsInProgress;
switch (config.DownloadsInProgressEnum)
{
case "LibationFiles":
downloadsInProgressLibationFilesRb.Checked = true;
break;
case "WinTemp":
default:
downloadsInProgressWinTempRb.Checked = true;
break;
}
this.decryptInProgressDescLbl.Text = desc(nameof(config.DecryptInProgressEnum));
var winTempDecryptInProgress = Path.Combine(config.WinTemp, "DecryptInProgress");
this.decryptInProgressWinTempRb.Text = "In your Windows temporary folder\r\n" + winTempDecryptInProgress;
switch (config.DecryptInProgressEnum)
{
case "LibationFiles":
decryptInProgressLibationFilesRb.Checked = true;
break;
case "WinTemp":
default:
decryptInProgressWinTempRb.Checked = true;
break;
}
libationFiles_Changed(this, null);
}
private void libationFiles_Changed(object sender, EventArgs e)
{
var libationFilesDir
= libationFilesRootRb.Checked ? exeRoot
: libationFilesMyDocsRb.Checked ? myDocs
: libationFilesCustomTb.Text;
var downloadsInProgress = Path.Combine(libationFilesDir, "DownloadsInProgress");
this.downloadsInProgressLibationFilesRb.Text = $"In your Libation Files (ie: program-created files)\r\n{downloadsInProgress}";
var decryptInProgress = Path.Combine(libationFilesDir, "DecryptInProgress");
this.decryptInProgressLibationFilesRb.Text = $"In your Libation Files (ie: program-created files)\r\n{decryptInProgress}";
}
private void booksLocationSearchBtn_Click(object sender, EventArgs e) => selectFolder("Search for books location", this.booksLocationTb);
private void libationFilesCustomBtn_Click(object sender, EventArgs e) => selectFolder("Search for Libation Files location", this.libationFilesCustomTb);
private static void selectFolder(string desc, TextBox textbox)
{
var dialog = new FolderBrowserDialog { Description = desc, SelectedPath = "" };
dialog.ShowDialog();
if (!string.IsNullOrWhiteSpace(dialog.SelectedPath))
textbox.Text = dialog.SelectedPath;
}
private void saveBtn_Click(object sender, EventArgs e)
{
config.DecryptKey = this.decryptKeyTb.Text;
var pathsChanged = false;
if (!Directory.Exists(this.booksLocationTb.Text))
MessageBox.Show("Not saving change to Books location. This folder does not exist:\r\n" + this.booksLocationTb.Text);
else if (config.Books != this.booksLocationTb.Text)
{
pathsChanged = true;
config.Books = this.booksLocationTb.Text;
}
var libationDir
= libationFilesRootRb.Checked ? exeRoot
: libationFilesMyDocsRb.Checked ? myDocs
: libationFilesCustomTb.Text;
if (!Directory.Exists(libationDir))
MessageBox.Show("Not saving change to Libation Files location. This folder does not exist:\r\n" + libationDir);
else if (config.LibationFiles != libationDir)
{
pathsChanged = true;
config.LibationFiles = libationDir;
}
config.DownloadsInProgressEnum = downloadsInProgressLibationFilesRb.Checked ? "LibationFiles" : "WinTemp";
config.DecryptInProgressEnum = decryptInProgressLibationFilesRb.Checked ? "LibationFiles" : "WinTemp";
if (pathsChanged)
{
var shutdownResult = MessageBox.Show(
"You have changed a file path important for this program. All files will remain in their original location; nothing will be moved. It is highly recommended that you restart this program so these changes are handled correctly."
+ "\r\n"
+ "\r\nClose program?",
"Restart program",
MessageBoxButtons.YesNo,
MessageBoxIcon.Exclamation,
MessageBoxDefaultButton.Button1);
if (shutdownResult == DialogResult.Yes)
{
Application.Exit();
}
}
this.DialogResult = DialogResult.OK;
this.Close();
}
private void cancelBtn_Click(object sender, EventArgs e) => this.Close();
}
}

View file

@ -0,0 +1,120 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<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>
</root>

303
LibationWinForm/UNTESTED/Form1.Designer.cs generated Normal file
View file

@ -0,0 +1,303 @@
namespace LibationWinForm
{
partial class Form1
{
/// <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()
{
System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(Form1));
this.gridPanel = new System.Windows.Forms.Panel();
this.filterHelpBtn = new System.Windows.Forms.Button();
this.filterBtn = new System.Windows.Forms.Button();
this.filterSearchTb = new System.Windows.Forms.TextBox();
this.menuStrip1 = new System.Windows.Forms.MenuStrip();
this.indexToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.scanLibraryToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.reimportMostRecentLibraryScanToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.beginImportingBookDetailsToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.liberateToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.beginBookBackupsToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.beginPdfBackupsToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.quickFiltersToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.firstFilterIsDefaultToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.editQuickFiltersToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.toolStripSeparator1 = new System.Windows.Forms.ToolStripSeparator();
this.settingsToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.statusStrip1 = new System.Windows.Forms.StatusStrip();
this.visibleCountLbl = new System.Windows.Forms.ToolStripStatusLabel();
this.springLbl = new System.Windows.Forms.ToolStripStatusLabel();
this.backupsCountsLbl = new System.Windows.Forms.ToolStripStatusLabel();
this.pdfsCountsLbl = new System.Windows.Forms.ToolStripStatusLabel();
this.addFilterBtn = new System.Windows.Forms.Button();
this.menuStrip1.SuspendLayout();
this.statusStrip1.SuspendLayout();
this.SuspendLayout();
//
// gridPanel
//
this.gridPanel.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.gridPanel.Location = new System.Drawing.Point(12, 56);
this.gridPanel.Name = "gridPanel";
this.gridPanel.Size = new System.Drawing.Size(839, 386);
this.gridPanel.TabIndex = 5;
//
// filterHelpBtn
//
this.filterHelpBtn.Location = new System.Drawing.Point(12, 27);
this.filterHelpBtn.Name = "filterHelpBtn";
this.filterHelpBtn.Size = new System.Drawing.Size(22, 23);
this.filterHelpBtn.TabIndex = 3;
this.filterHelpBtn.Text = "?";
this.filterHelpBtn.UseVisualStyleBackColor = true;
this.filterHelpBtn.Click += new System.EventHandler(this.filterHelpBtn_Click);
//
// filterBtn
//
this.filterBtn.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
this.filterBtn.Location = new System.Drawing.Point(776, 27);
this.filterBtn.Name = "filterBtn";
this.filterBtn.Size = new System.Drawing.Size(75, 23);
this.filterBtn.TabIndex = 2;
this.filterBtn.Text = "Filter";
this.filterBtn.UseVisualStyleBackColor = true;
this.filterBtn.Click += new System.EventHandler(this.filterBtn_Click);
//
// filterSearchTb
//
this.filterSearchTb.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.filterSearchTb.Location = new System.Drawing.Point(186, 29);
this.filterSearchTb.Name = "filterSearchTb";
this.filterSearchTb.Size = new System.Drawing.Size(584, 20);
this.filterSearchTb.TabIndex = 1;
this.filterSearchTb.KeyPress += new System.Windows.Forms.KeyPressEventHandler(this.filterSearchTb_KeyPress);
//
// menuStrip1
//
this.menuStrip1.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
this.indexToolStripMenuItem,
this.liberateToolStripMenuItem,
this.quickFiltersToolStripMenuItem,
this.settingsToolStripMenuItem});
this.menuStrip1.Location = new System.Drawing.Point(0, 0);
this.menuStrip1.Name = "menuStrip1";
this.menuStrip1.Size = new System.Drawing.Size(863, 24);
this.menuStrip1.TabIndex = 0;
this.menuStrip1.Text = "menuStrip1";
//
// indexToolStripMenuItem
//
this.indexToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
this.scanLibraryToolStripMenuItem,
this.reimportMostRecentLibraryScanToolStripMenuItem,
this.beginImportingBookDetailsToolStripMenuItem});
this.indexToolStripMenuItem.Name = "indexToolStripMenuItem";
this.indexToolStripMenuItem.Size = new System.Drawing.Size(47, 20);
this.indexToolStripMenuItem.Text = "&Index";
this.indexToolStripMenuItem.DropDownOpening += new System.EventHandler(this.indexToolStripMenuItem_DropDownOpening);
//
// scanLibraryToolStripMenuItem
//
this.scanLibraryToolStripMenuItem.Name = "scanLibraryToolStripMenuItem";
this.scanLibraryToolStripMenuItem.Size = new System.Drawing.Size(277, 22);
this.scanLibraryToolStripMenuItem.Text = "Scan &Library...";
this.scanLibraryToolStripMenuItem.Click += new System.EventHandler(this.scanLibraryToolStripMenuItem_Click);
//
// reimportMostRecentLibraryScanToolStripMenuItem
//
this.reimportMostRecentLibraryScanToolStripMenuItem.Name = "reimportMostRecentLibraryScanToolStripMenuItem";
this.reimportMostRecentLibraryScanToolStripMenuItem.Size = new System.Drawing.Size(277, 22);
this.reimportMostRecentLibraryScanToolStripMenuItem.Text = "Re-&import most recent library scan: {0}";
this.reimportMostRecentLibraryScanToolStripMenuItem.Click += new System.EventHandler(this.reimportMostRecentLibraryScanToolStripMenuItem_Click);
//
// beginImportingBookDetailsToolStripMenuItem
//
this.beginImportingBookDetailsToolStripMenuItem.Name = "beginImportingBookDetailsToolStripMenuItem";
this.beginImportingBookDetailsToolStripMenuItem.Size = new System.Drawing.Size(277, 22);
this.beginImportingBookDetailsToolStripMenuItem.Text = "Begin importing book details: {0}";
this.beginImportingBookDetailsToolStripMenuItem.Click += new System.EventHandler(this.beginImportingBookDetailsToolStripMenuItem_Click);
//
// liberateToolStripMenuItem
//
this.liberateToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
this.beginBookBackupsToolStripMenuItem,
this.beginPdfBackupsToolStripMenuItem});
this.liberateToolStripMenuItem.Name = "liberateToolStripMenuItem";
this.liberateToolStripMenuItem.Size = new System.Drawing.Size(61, 20);
this.liberateToolStripMenuItem.Text = "&Liberate";
//
// beginBookBackupsToolStripMenuItem
//
this.beginBookBackupsToolStripMenuItem.Name = "beginBookBackupsToolStripMenuItem";
this.beginBookBackupsToolStripMenuItem.Size = new System.Drawing.Size(201, 22);
this.beginBookBackupsToolStripMenuItem.Text = "Begin &Book Backups: {0}";
this.beginBookBackupsToolStripMenuItem.Click += new System.EventHandler(this.beginBookBackupsToolStripMenuItem_Click);
//
// beginPdfBackupsToolStripMenuItem
//
this.beginPdfBackupsToolStripMenuItem.Name = "beginPdfBackupsToolStripMenuItem";
this.beginPdfBackupsToolStripMenuItem.Size = new System.Drawing.Size(201, 22);
this.beginPdfBackupsToolStripMenuItem.Text = "Begin &PDF Backups: {0}";
this.beginPdfBackupsToolStripMenuItem.Click += new System.EventHandler(this.beginPdfBackupsToolStripMenuItem_Click);
//
// quickFiltersToolStripMenuItem
//
this.quickFiltersToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
this.firstFilterIsDefaultToolStripMenuItem,
this.editQuickFiltersToolStripMenuItem,
this.toolStripSeparator1});
this.quickFiltersToolStripMenuItem.Name = "quickFiltersToolStripMenuItem";
this.quickFiltersToolStripMenuItem.Size = new System.Drawing.Size(84, 20);
this.quickFiltersToolStripMenuItem.Text = "Quick &Filters";
//
// firstFilterIsDefaultToolStripMenuItem
//
this.firstFilterIsDefaultToolStripMenuItem.Name = "firstFilterIsDefaultToolStripMenuItem";
this.firstFilterIsDefaultToolStripMenuItem.Size = new System.Drawing.Size(256, 22);
this.firstFilterIsDefaultToolStripMenuItem.Text = "Start Libation with 1st filter &Default";
this.firstFilterIsDefaultToolStripMenuItem.Click += new System.EventHandler(this.FirstFilterIsDefaultToolStripMenuItem_Click);
//
// editQuickFiltersToolStripMenuItem
//
this.editQuickFiltersToolStripMenuItem.Name = "editQuickFiltersToolStripMenuItem";
this.editQuickFiltersToolStripMenuItem.Size = new System.Drawing.Size(256, 22);
this.editQuickFiltersToolStripMenuItem.Text = "&Edit quick filters";
this.editQuickFiltersToolStripMenuItem.Click += new System.EventHandler(this.EditQuickFiltersToolStripMenuItem_Click);
//
// toolStripSeparator1
//
this.toolStripSeparator1.Name = "toolStripSeparator1";
this.toolStripSeparator1.Size = new System.Drawing.Size(253, 6);
//
// settingsToolStripMenuItem
//
this.settingsToolStripMenuItem.Name = "settingsToolStripMenuItem";
this.settingsToolStripMenuItem.Size = new System.Drawing.Size(61, 20);
this.settingsToolStripMenuItem.Text = "&Settings";
this.settingsToolStripMenuItem.Click += new System.EventHandler(this.settingsToolStripMenuItem_Click);
//
// statusStrip1
//
this.statusStrip1.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
this.visibleCountLbl,
this.springLbl,
this.backupsCountsLbl,
this.pdfsCountsLbl});
this.statusStrip1.Location = new System.Drawing.Point(0, 445);
this.statusStrip1.Name = "statusStrip1";
this.statusStrip1.Size = new System.Drawing.Size(863, 22);
this.statusStrip1.TabIndex = 6;
this.statusStrip1.Text = "statusStrip1";
//
// visibleCountLbl
//
this.visibleCountLbl.Name = "visibleCountLbl";
this.visibleCountLbl.Size = new System.Drawing.Size(61, 17);
this.visibleCountLbl.Text = "Visible: {0}";
//
// springLbl
//
this.springLbl.Name = "springLbl";
this.springLbl.Size = new System.Drawing.Size(232, 17);
this.springLbl.Spring = true;
//
// backupsCountsLbl
//
this.backupsCountsLbl.Name = "backupsCountsLbl";
this.backupsCountsLbl.Size = new System.Drawing.Size(336, 17);
this.backupsCountsLbl.Text = "BACKUPS: No progress: {0} Encrypted: {1} Fully backed up: {2}";
//
// pdfsCountsLbl
//
this.pdfsCountsLbl.Name = "pdfsCountsLbl";
this.pdfsCountsLbl.Size = new System.Drawing.Size(219, 17);
this.pdfsCountsLbl.Text = "| PDFs: NOT d/l\'ed: {0} Downloaded: {1}";
//
// addFilterBtn
//
this.addFilterBtn.Location = new System.Drawing.Point(40, 27);
this.addFilterBtn.Name = "addFilterBtn";
this.addFilterBtn.Size = new System.Drawing.Size(140, 23);
this.addFilterBtn.TabIndex = 4;
this.addFilterBtn.Text = "Add To Quick Filters";
this.addFilterBtn.UseVisualStyleBackColor = true;
this.addFilterBtn.Click += new System.EventHandler(this.AddFilterBtn_Click);
//
// Form1
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(863, 467);
this.Controls.Add(this.filterBtn);
this.Controls.Add(this.addFilterBtn);
this.Controls.Add(this.filterSearchTb);
this.Controls.Add(this.filterHelpBtn);
this.Controls.Add(this.statusStrip1);
this.Controls.Add(this.gridPanel);
this.Controls.Add(this.menuStrip1);
this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon")));
this.MainMenuStrip = this.menuStrip1;
this.Name = "Form1";
this.Text = "Libation: Liberate your Library";
this.Load += new System.EventHandler(this.Form1_Load);
this.menuStrip1.ResumeLayout(false);
this.menuStrip1.PerformLayout();
this.statusStrip1.ResumeLayout(false);
this.statusStrip1.PerformLayout();
this.ResumeLayout(false);
this.PerformLayout();
}
#endregion
private System.Windows.Forms.Panel gridPanel;
private System.Windows.Forms.MenuStrip menuStrip1;
private System.Windows.Forms.ToolStripMenuItem indexToolStripMenuItem;
private System.Windows.Forms.StatusStrip statusStrip1;
private System.Windows.Forms.ToolStripStatusLabel springLbl;
private System.Windows.Forms.ToolStripStatusLabel visibleCountLbl;
private System.Windows.Forms.ToolStripMenuItem liberateToolStripMenuItem;
private System.Windows.Forms.ToolStripStatusLabel backupsCountsLbl;
private System.Windows.Forms.ToolStripMenuItem beginBookBackupsToolStripMenuItem;
private System.Windows.Forms.ToolStripStatusLabel pdfsCountsLbl;
private System.Windows.Forms.ToolStripMenuItem beginPdfBackupsToolStripMenuItem;
private System.Windows.Forms.TextBox filterSearchTb;
private System.Windows.Forms.Button filterBtn;
private System.Windows.Forms.Button filterHelpBtn;
private System.Windows.Forms.ToolStripMenuItem settingsToolStripMenuItem;
private System.Windows.Forms.ToolStripMenuItem scanLibraryToolStripMenuItem;
private System.Windows.Forms.ToolStripMenuItem reimportMostRecentLibraryScanToolStripMenuItem;
private System.Windows.Forms.ToolStripMenuItem beginImportingBookDetailsToolStripMenuItem;
private System.Windows.Forms.ToolStripMenuItem quickFiltersToolStripMenuItem;
private System.Windows.Forms.ToolStripMenuItem firstFilterIsDefaultToolStripMenuItem;
private System.Windows.Forms.Button addFilterBtn;
private System.Windows.Forms.ToolStripMenuItem editQuickFiltersToolStripMenuItem;
private System.Windows.Forms.ToolStripSeparator toolStripSeparator1;
}
}

View file

@ -0,0 +1,473 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.Windows.Forms;
using DomainServices;
using DataLayer;
using Dinah.Core;
using Dinah.Core.Collections.Generic;
using Dinah.Core.Windows.Forms;
using FileManager;
namespace LibationWinForm
{
public partial class Form1 : Form
{
// initial call here will initiate config loading
private Configuration config = Configuration.Instance;
private string backupsCountsLbl_Format;
private string pdfsCountsLbl_Format;
private string visibleCountLbl_Format;
private string reimportMostRecentLibraryScanToolStripMenuItem_format;
private string beginImportingBookDetailsToolStripMenuItem_format;
private string beginBookBackupsToolStripMenuItem_format;
private string beginPdfBackupsToolStripMenuItem_format;
public Form1()
{
InitializeComponent();
// back up string formats
backupsCountsLbl_Format = backupsCountsLbl.Text;
pdfsCountsLbl_Format = pdfsCountsLbl.Text;
visibleCountLbl_Format = visibleCountLbl.Text;
reimportMostRecentLibraryScanToolStripMenuItem_format = reimportMostRecentLibraryScanToolStripMenuItem.Text;
beginImportingBookDetailsToolStripMenuItem_format = beginImportingBookDetailsToolStripMenuItem.Text;
beginBookBackupsToolStripMenuItem_format = beginBookBackupsToolStripMenuItem.Text;
beginPdfBackupsToolStripMenuItem_format = beginPdfBackupsToolStripMenuItem.Text;
}
private async void Form1_Load(object sender, EventArgs e)
{
// call static ctor. There are bad race conditions if static ctor is first executed when we're running in parallel in setBackupCountsAsync()
var foo = FilePathCache.JsonFile;
reloadGrid();
// also applies filter. ONLY call AFTER loading grid
loadInitialQuickFilterState();
{ // init bottom counts
backupsCountsLbl.Text = "[Calculating backed up book quantities]";
pdfsCountsLbl.Text = "[Calculating backed up PDFs]";
await setBackupCountsAsync();
}
}
#region bottom: qty books visible
public void SetVisibleCount(int qty, string str = null)
{
visibleCountLbl.Text = string.Format(visibleCountLbl_Format, qty);
if (!string.IsNullOrWhiteSpace(str))
visibleCountLbl.Text += " | " + str;
}
#endregion
#region bottom: backup counts
private async Task setBackupCountsAsync()
{
var books = LibraryQueries.GetLibrary_Flat_NoTracking()
.Select(sp => sp.Book)
.ToList();
await setBookBackupCountsAsync(books).ConfigureAwait(false);
await setPdfBackupCountsAsync(books).ConfigureAwait(false);
}
enum AudioFileState { full, aax, none }
private async Task setBookBackupCountsAsync(IEnumerable<Book> books)
{
var libraryProductIds = books
.Select(b => b.AudibleProductId)
.ToList();
var noProgress = 0;
var downloadedOnly = 0;
var fullyBackedUp = 0;
//// serial
//foreach (var productId in libraryProductIds)
//{
// if (await AudibleFileStorage.Audio.ExistsAsync(productId))
// fullyBackedUp++;
// else if (await AudibleFileStorage.AAX.ExistsAsync(productId))
// downloadedOnly++;
// else
// noProgress++;
//}
// parallel
async Task<AudioFileState> getAudioFileStateAsync(string productId)
{
if (await AudibleFileStorage.Audio.ExistsAsync(productId))
return AudioFileState.full;
if (await AudibleFileStorage.AAX.ExistsAsync(productId))
return AudioFileState.aax;
return AudioFileState.none;
}
var tasks = libraryProductIds.Select(productId => getAudioFileStateAsync(productId));
var results = await Task.WhenAll(tasks).ConfigureAwait(false);
fullyBackedUp = results.Count(r => r == AudioFileState.full);
downloadedOnly = results.Count(r => r == AudioFileState.aax);
noProgress = results.Count(r => r == AudioFileState.none);
// update bottom numbers
var pending = noProgress + downloadedOnly;
var text
= !results.Any() ? "No books. Begin by indexing your library"
: pending > 0 ? string.Format(backupsCountsLbl_Format, noProgress, downloadedOnly, fullyBackedUp)
: $"All {"book".PluralizeWithCount(fullyBackedUp)} backed up";
statusStrip1.UIThread(() => backupsCountsLbl.Text = text);
// update menu item
var menuItemText
= pending > 0
? $"{pending} remaining"
: "All books have been liberated";
menuStrip1.UIThread(() => beginBookBackupsToolStripMenuItem.Enabled = pending > 0);
menuStrip1.UIThread(() => beginBookBackupsToolStripMenuItem.Text = string.Format(beginBookBackupsToolStripMenuItem_format, menuItemText));
}
private async Task setPdfBackupCountsAsync(IEnumerable<Book> books)
{
var libraryProductIds = books
.Where(b => b.Supplements.Any())
.Select(b => b.AudibleProductId)
.ToList();
int notDownloaded;
int downloaded;
//// serial
//notDownloaded = 0;
//downloaded = 0;
//foreach (var productId in libraryProductIds)
//{
// if (await AudibleFileStorage.PDF.ExistsAsync(productId))
// downloaded++;
// else
// notDownloaded++;
//}
// parallel
var tasks = libraryProductIds.Select(productId => AudibleFileStorage.PDF.ExistsAsync(productId));
var boolResults = await Task.WhenAll(tasks).ConfigureAwait(false);
downloaded = boolResults.Count(r => r);
notDownloaded = boolResults.Count(r => !r);
// update bottom numbers
var text
= !boolResults.Any() ? ""
: notDownloaded > 0 ? string.Format(pdfsCountsLbl_Format, notDownloaded, downloaded)
: $"| All {downloaded} PDFs downloaded";
statusStrip1.UIThread(() => pdfsCountsLbl.Text = text);
// update menu item
var menuItemText
= notDownloaded > 0
? $"{notDownloaded} remaining"
: "All PDFs have been downloaded";
menuStrip1.UIThread(() => beginPdfBackupsToolStripMenuItem.Enabled = notDownloaded > 0);
menuStrip1.UIThread(() => beginPdfBackupsToolStripMenuItem.Text = string.Format(beginPdfBackupsToolStripMenuItem_format, menuItemText));
}
#endregion
#region grid select
bool isProcessingGridSelect = false;
private void reloadGrid()
{
// suppressed filter while init'ing UI
var prev_isProcessingGridSelect = isProcessingGridSelect;
isProcessingGridSelect = true;
setGrid();
isProcessingGridSelect = prev_isProcessingGridSelect;
// UI init complete. now we can apply filter
doFilter(lastGoodFilter);
}
ProductsGrid currProductsGrid;
private void setGrid()
{
SuspendLayout();
{
if (currProductsGrid != null)
{
gridPanel.Controls.Remove(currProductsGrid);
currProductsGrid.Dispose();
}
currProductsGrid = new ProductsGrid(this) { Dock = DockStyle.Fill };
gridPanel.Controls.Add(currProductsGrid);
currProductsGrid.Display();
}
ResumeLayout();
}
#endregion
#region filter
private void filterHelpBtn_Click(object sender, EventArgs e) => new Dialogs.SearchSyntaxDialog().ShowDialog();
private void AddFilterBtn_Click(object sender, EventArgs e)
{
QuickFilters.Add(this.filterSearchTb.Text);
UpdateFilterDropDown();
}
private void filterSearchTb_KeyPress(object sender, KeyPressEventArgs e)
{
if (e.KeyChar == (char)Keys.Return)
{
doFilter();
// silence the 'ding'
e.Handled = true;
}
}
private void filterBtn_Click(object sender, EventArgs e) => doFilter();
string lastGoodFilter = "";
private void doFilter(string filterString)
{
this.filterSearchTb.Text = filterString;
doFilter();
}
private void doFilter()
{
if (isProcessingGridSelect || currProductsGrid == null)
return;
try
{
currProductsGrid.Filter(filterSearchTb.Text);
lastGoodFilter = filterSearchTb.Text;
}
catch (Exception ex)
{
MessageBox.Show($"Bad filter string:\r\n\r\n{ex.Message}", "Bad filter string", MessageBoxButtons.OK, MessageBoxIcon.Error);
// re-apply last good filter
filterSearchTb.Text = lastGoodFilter;
doFilter();
}
}
#endregion
#region index menu
//
// IMPORTANT
//
// IRunnableDialog.Run() extension method contains work flow
//
#region // example code: chaining multiple dialogs
public class MyDialog1 : IRunnableDialog
{
public IEnumerable<string> Files;
public IButtonControl AcceptButton { get => throw new NotImplementedException(); set => throw new NotImplementedException(); }
public Control.ControlCollection Controls => throw new NotImplementedException();
public string SuccessMessage => throw new NotImplementedException();
public DialogResult DialogResult { get => throw new NotImplementedException(); set => throw new NotImplementedException(); }
public void Close() => throw new NotImplementedException();
public Task DoMainWorkAsync() => throw new NotImplementedException();
public DialogResult ShowDialog() => throw new NotImplementedException();
public string StringBasedValidate() => throw new NotImplementedException();
}
public class MyDialog2 : Form, IIndexLibraryDialog
{
public MyDialog2(IEnumerable<string> files) { }
Button BeginFileImportBtn = new Button();
public void Begin() => BeginFileImportBtn.PerformClick();
public int TotalBooksProcessed => throw new NotImplementedException();
public int NewBooksAdded => throw new NotImplementedException();
public string SuccessMessage => throw new NotImplementedException();
public Task DoMainWorkAsync() => throw new NotImplementedException();
public string StringBasedValidate() => throw new NotImplementedException();
}
private async void downloadPagesToFile(object sender, EventArgs e)
{
var dialog1 = new MyDialog1();
if (dialog1.RunDialog() != DialogResult.OK || !dialog1.Files.Any())
return;
if (MessageBox.Show("Index from these files?", "Index?", MessageBoxButtons.YesNo) == DialogResult.Yes)
{
var dialog2 = new MyDialog2(dialog1.Files);
dialog2.Shown += (_, __) => dialog2.Begin();
await indexDialog(dialog2);
}
}
#endregion
private void indexToolStripMenuItem_DropDownOpening(object sender, EventArgs e)
{
#region label: Re-import most recent library scan
{
var libDir = WebpageStorage.GetMostRecentLibraryDir();
if (libDir == null)
{
reimportMostRecentLibraryScanToolStripMenuItem.Enabled = false;
reimportMostRecentLibraryScanToolStripMenuItem.Text = string.Format(reimportMostRecentLibraryScanToolStripMenuItem_format, "No previous scans");
}
else
{
reimportMostRecentLibraryScanToolStripMenuItem.Enabled = true;
var now = DateTime.Now;
var span = now - libDir.CreationTime;
var ago
// less than 1 min
= (int)span.TotalSeconds < 60 ? $"{(int)span.TotalSeconds} sec ago"
// less than 1 hr
: (int)span.TotalMinutes < 60 ? $"{(int)span.TotalMinutes} min ago"
// today. eg: 4:25 PM
: now.Date == libDir.CreationTime.Date ? libDir.CreationTime.ToString("h:mm tt")
// else date and time
: libDir.CreationTime.ToString("MM/dd/yyyy h:mm tt");
reimportMostRecentLibraryScanToolStripMenuItem.Text = string.Format(reimportMostRecentLibraryScanToolStripMenuItem_format, ago);
}
}
#endregion
#region label: Begin importing book details
{
var noDetails = BookQueries.BooksWithoutDetailsCount();
if (noDetails == 0)
{
beginImportingBookDetailsToolStripMenuItem.Enabled = false;
beginImportingBookDetailsToolStripMenuItem.Text = string.Format(beginImportingBookDetailsToolStripMenuItem_format, "No books without details");
}
else
{
beginImportingBookDetailsToolStripMenuItem.Enabled = true;
beginImportingBookDetailsToolStripMenuItem.Text = string.Format(beginImportingBookDetailsToolStripMenuItem_format, $"{noDetails} remaining");
}
}
#endregion
}
private async void scanLibraryToolStripMenuItem_Click(object sender, EventArgs e) => await indexDialog(new ScanLibraryDialog());
private async void reimportMostRecentLibraryScanToolStripMenuItem_Click(object sender, EventArgs e)
{
// DO NOT ConfigureAwait(false)
// this would result in index() => reloadGrid() => setGrid() => "gridPanel.Controls.Remove(currProductsGrid);"
// throwing 'Cross-thread operation not valid: Control 'ProductsGrid' accessed from a thread other than the thread it was created on.'
var (TotalBooksProcessed, NewBooksAdded) = await Indexer.IndexLibraryAsync(WebpageStorage.GetMostRecentLibraryDir());
MessageBox.Show($"Total processed: {TotalBooksProcessed}\r\nNew: {NewBooksAdded}");
await index(NewBooksAdded, TotalBooksProcessed);
}
private async Task indexDialog(IIndexLibraryDialog dialog)
{
if (!dialog.RunDialog().In(DialogResult.Abort, DialogResult.Cancel, DialogResult.None))
await index(dialog.NewBooksAdded, dialog.TotalBooksProcessed);
}
private async Task index(int newBooksAdded, int totalBooksProcessed)
{
// update backup counts if we have new library items
if (newBooksAdded > 0)
await setBackupCountsAsync();
// skip reload if:
// - no grid is loaded
// - none indexed
if (currProductsGrid == null || totalBooksProcessed == 0)
return;
reloadGrid();
}
private void updateGridRow(object _, string productId) => currProductsGrid?.UpdateRow(productId);
private async void beginImportingBookDetailsToolStripMenuItem_Click(object sender, EventArgs e)
{
var scrapeBookDetails = BookLiberation.ProcessorAutomationController.GetWiredUpScrapeBookDetails();
scrapeBookDetails.BookSuccessfullyImported += updateGridRow;
await BookLiberation.ProcessorAutomationController.RunAutomaticDownload(scrapeBookDetails);
}
#endregion
#region liberate menu
private async void setBackupCountsAsync(object _, string __) => await setBackupCountsAsync();
private async void beginBookBackupsToolStripMenuItem_Click(object sender, EventArgs e)
{
var backupBook = BookLiberation.ProcessorAutomationController.GetWiredUpBackupBook();
backupBook.Download.Completed += setBackupCountsAsync;
backupBook.Decrypt.Completed += setBackupCountsAsync;
await BookLiberation.ProcessorAutomationController.RunAutomaticBackup(backupBook);
}
private async void beginPdfBackupsToolStripMenuItem_Click(object sender, EventArgs e)
{
var downloadPdf = BookLiberation.ProcessorAutomationController.GetWiredUpDownloadPdf();
downloadPdf.Completed += setBackupCountsAsync;
await BookLiberation.ProcessorAutomationController.RunAutomaticDownload(downloadPdf);
}
#endregion
#region quick filters menu
private void loadInitialQuickFilterState()
{
// set inital state. do once only
firstFilterIsDefaultToolStripMenuItem.Checked = QuickFilters.UseDefault;
// load default filter. do once only
if (QuickFilters.UseDefault)
doFilter(QuickFilters.Filters.FirstOrDefault());
// do after every save
UpdateFilterDropDown();
}
private void FirstFilterIsDefaultToolStripMenuItem_Click(object sender, EventArgs e)
{
firstFilterIsDefaultToolStripMenuItem.Checked = !firstFilterIsDefaultToolStripMenuItem.Checked;
QuickFilters.UseDefault = firstFilterIsDefaultToolStripMenuItem.Checked;
}
object quickFilterTag { get; } = new object();
public void UpdateFilterDropDown()
{
// remove old
for (var i = quickFiltersToolStripMenuItem.DropDownItems.Count - 1; i >= 0; i--)
{
var menuItem = quickFiltersToolStripMenuItem.DropDownItems[i];
if (menuItem.Tag == quickFilterTag)
quickFiltersToolStripMenuItem.DropDownItems.Remove(menuItem);
}
// re-populate
var index = 0;
foreach (var filter in QuickFilters.Filters)
{
var menuItem = new ToolStripMenuItem
{
Tag = quickFilterTag,
Text = $"&{++index}: {filter}"
};
menuItem.Click += (_, __) => doFilter(filter);
quickFiltersToolStripMenuItem.DropDownItems.Add(menuItem);
}
}
private void EditQuickFiltersToolStripMenuItem_Click(object sender, EventArgs e) => new Dialogs.EditQuickFilters(this).ShowDialog();
#endregion
#region settings menu item
private void settingsToolStripMenuItem_Click(object sender, EventArgs e) => new SettingsDialog().ShowDialog();
#endregion
}
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,177 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Linq;
using DataLayer;
namespace LibationWinForm
{
internal class GridEntry
{
private LibraryBook libraryBook;
private Book book => libraryBook.Book;
public Book GetBook() => book;
// this special case is obvious and ugly
public void REPLACE_Library_Book(LibraryBook libraryBook) => this.libraryBook = libraryBook;
public GridEntry(LibraryBook libraryBook) => this.libraryBook = libraryBook;
// hide from public fields from Data Source GUI with [Browsable(false)]
[Browsable(false)]
public string Tags => book.UserDefinedItem.Tags;
[Browsable(false)]
public IEnumerable<string> TagsEnumerated => book.UserDefinedItem.TagsEnumerated;
private Dictionary<string, string> formatReplacements { get; } = new Dictionary<string, string>();
public bool TryGetFormatted(string key, out string value) => formatReplacements.TryGetValue(key, out value);
public Image Cover =>
Dinah.Core.Drawing.ImageConverter.GetPictureFromBytes(
FileManager.PictureStorage.GetImage(book.PictureId, FileManager.PictureStorage.PictureSize._80x80)
);
public string Title
{
get
{
formatReplacements[nameof(Title)] = book.Title;
var sortName = book.Title
.Replace("|", "")
.Replace(":", "")
.ToLowerInvariant();
if (sortName.StartsWith("the ") || sortName.StartsWith("a ") || sortName.StartsWith("an "))
sortName = sortName.Substring(sortName.IndexOf(" ") + 1);
return sortName;
}
}
public string Authors => book.AuthorNames;
public string Narrators => book.NarratorNames;
public int Length
{
get
{
formatReplacements[nameof(Length)]
= book.LengthInMinutes == 0
? "[pre-release]"
: $"{book.LengthInMinutes / 60} hr {book.LengthInMinutes % 60} min";
return book.LengthInMinutes;
}
}
public string Series => book.SeriesNames;
public string Description
=> book.Description == null ? ""
: book.Description.Length < 63 ? book.Description
: book.Description.Substring(0, 60) + "...";
public string Category => string.Join(" > ", book.CategoriesNames);
// star ratings retain numeric value but display star text. this is needed because just using star text doesn't sort correctly:
// - star
// - star star
// - star 1/2
public string Product_Rating
{
get
{
Rating rating = book.Rating;
formatReplacements[nameof(Product_Rating)] = starString(rating);
return firstScore(rating);
}
}
public DateTime? Purchase_Date => libraryBook.DateAdded;
public string My_Rating
{
get
{
Rating rating = book.UserDefinedItem.Rating;
formatReplacements[nameof(My_Rating)] = starString(rating);
return firstScore(rating);
}
}
private string starString(Rating rating)
=> (rating?.FirstScore != null && rating?.FirstScore > 0f)
? rating?.ToStarString()
: "";
private string firstScore(Rating rating) => rating?.FirstScore.ToString("0.0");
// max 5 text rows
public string Misc
{
get
{
var details = new List<string>();
if (book.HasPdfs)
details.Add("Has PDFs");
if (book.IsAbridged)
details.Add("Abridged");
if (book.DatePublished.HasValue)
details.Add($"Date pub'd: {book.DatePublished.Value.ToString("MM/dd/yyyy")}");
// this goes last since it's most likely to have a line-break
if (!string.IsNullOrWhiteSpace(book.Publisher))
details.Add($"Pub: {book.Publisher}");
if (!details.Any())
return "[details not imported]";
return string.Join("\r\n", details);
}
}
public string Download_Status
{
get
{
var print
= FileManager.AudibleFileStorage.Audio.ExistsAsync(book.AudibleProductId).GetAwaiter().GetResult() ? "Liberated"
: FileManager.AudibleFileStorage.AAX.ExistsAsync(book.AudibleProductId).GetAwaiter().GetResult() ? "DRM"
: "NOT d/l'ed";
if (!book.Supplements.Any())
return print;
print += "\r\n";
var downloadStatuses = book.Supplements
.Select(d => FileManager.AudibleFileStorage.PDF.ExistsAsync(book.AudibleProductId).GetAwaiter().GetResult())
// break delayed execution right now!
.ToList();
var count = downloadStatuses.Count;
if (count == 1)
{
print += downloadStatuses[0]
? "PDF d/l'ed"
: "PDF NOT d/l'ed";
}
else
{
var downloadedCount = downloadStatuses.Count(s => s);
print
+= downloadedCount == count ? $"{count} PDFs d/l'ed"
: downloadedCount == 0 ? $"{count} PDFs NOT d/l'ed"
: $"{downloadedCount} of {count} PDFs d/l'ed";
}
return print;
}
}
}
}

View file

@ -0,0 +1,201 @@
namespace LibationWinForm
{
partial class ProductsGrid
{
/// <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 Component 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.gridEntryBindingSource = new System.Windows.Forms.BindingSource(this.components);
this.gridEntryDataGridView = new System.Windows.Forms.DataGridView();
this.dataGridViewImageColumn1 = new System.Windows.Forms.DataGridViewImageColumn();
this.dataGridViewTextBoxColumn1 = new System.Windows.Forms.DataGridViewTextBoxColumn();
this.dataGridViewTextBoxColumn2 = new System.Windows.Forms.DataGridViewTextBoxColumn();
this.dataGridViewTextBoxColumn3 = new System.Windows.Forms.DataGridViewTextBoxColumn();
this.dataGridViewTextBoxColumn4 = new System.Windows.Forms.DataGridViewTextBoxColumn();
this.dataGridViewTextBoxColumn5 = new System.Windows.Forms.DataGridViewTextBoxColumn();
this.dataGridViewTextBoxColumn6 = new System.Windows.Forms.DataGridViewTextBoxColumn();
this.dataGridViewTextBoxColumn7 = new System.Windows.Forms.DataGridViewTextBoxColumn();
this.dataGridViewTextBoxColumn8 = new System.Windows.Forms.DataGridViewTextBoxColumn();
this.dataGridViewTextBoxColumn9 = new System.Windows.Forms.DataGridViewTextBoxColumn();
this.dataGridViewTextBoxColumn10 = new System.Windows.Forms.DataGridViewTextBoxColumn();
this.dataGridViewTextBoxColumn11 = new System.Windows.Forms.DataGridViewTextBoxColumn();
this.dataGridViewTextBoxColumn12 = new System.Windows.Forms.DataGridViewTextBoxColumn();
((System.ComponentModel.ISupportInitialize)(this.gridEntryBindingSource)).BeginInit();
((System.ComponentModel.ISupportInitialize)(this.gridEntryDataGridView)).BeginInit();
this.SuspendLayout();
//
// gridEntryBindingSource
//
this.gridEntryBindingSource.DataSource = typeof(LibationWinForm.GridEntry);
//
// gridEntryDataGridView
//
this.gridEntryDataGridView.AutoGenerateColumns = false;
this.gridEntryDataGridView.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize;
this.gridEntryDataGridView.Columns.AddRange(new System.Windows.Forms.DataGridViewColumn[] {
this.dataGridViewImageColumn1,
this.dataGridViewTextBoxColumn1,
this.dataGridViewTextBoxColumn2,
this.dataGridViewTextBoxColumn3,
this.dataGridViewTextBoxColumn4,
this.dataGridViewTextBoxColumn5,
this.dataGridViewTextBoxColumn6,
this.dataGridViewTextBoxColumn7,
this.dataGridViewTextBoxColumn8,
this.dataGridViewTextBoxColumn9,
this.dataGridViewTextBoxColumn10,
this.dataGridViewTextBoxColumn11,
this.dataGridViewTextBoxColumn12});
this.gridEntryDataGridView.DataSource = this.gridEntryBindingSource;
this.gridEntryDataGridView.Location = new System.Drawing.Point(54, 58);
this.gridEntryDataGridView.Name = "gridEntryDataGridView";
this.gridEntryDataGridView.Size = new System.Drawing.Size(300, 220);
this.gridEntryDataGridView.TabIndex = 0;
//
// dataGridViewImageColumn1
//
this.dataGridViewImageColumn1.DataPropertyName = "Cover";
this.dataGridViewImageColumn1.HeaderText = "Cover";
this.dataGridViewImageColumn1.Name = "dataGridViewImageColumn1";
this.dataGridViewImageColumn1.ReadOnly = true;
//
// dataGridViewTextBoxColumn1
//
this.dataGridViewTextBoxColumn1.DataPropertyName = "Title";
this.dataGridViewTextBoxColumn1.HeaderText = "Title";
this.dataGridViewTextBoxColumn1.Name = "dataGridViewTextBoxColumn1";
this.dataGridViewTextBoxColumn1.ReadOnly = true;
//
// dataGridViewTextBoxColumn2
//
this.dataGridViewTextBoxColumn2.DataPropertyName = "Authors";
this.dataGridViewTextBoxColumn2.HeaderText = "Authors";
this.dataGridViewTextBoxColumn2.Name = "dataGridViewTextBoxColumn2";
this.dataGridViewTextBoxColumn2.ReadOnly = true;
//
// dataGridViewTextBoxColumn3
//
this.dataGridViewTextBoxColumn3.DataPropertyName = "Narrators";
this.dataGridViewTextBoxColumn3.HeaderText = "Narrators";
this.dataGridViewTextBoxColumn3.Name = "dataGridViewTextBoxColumn3";
this.dataGridViewTextBoxColumn3.ReadOnly = true;
//
// dataGridViewTextBoxColumn4
//
this.dataGridViewTextBoxColumn4.DataPropertyName = "Length";
this.dataGridViewTextBoxColumn4.HeaderText = "Length";
this.dataGridViewTextBoxColumn4.Name = "dataGridViewTextBoxColumn4";
this.dataGridViewTextBoxColumn4.ReadOnly = true;
//
// dataGridViewTextBoxColumn5
//
this.dataGridViewTextBoxColumn5.DataPropertyName = "Series";
this.dataGridViewTextBoxColumn5.HeaderText = "Series";
this.dataGridViewTextBoxColumn5.Name = "dataGridViewTextBoxColumn5";
this.dataGridViewTextBoxColumn5.ReadOnly = true;
//
// dataGridViewTextBoxColumn6
//
this.dataGridViewTextBoxColumn6.DataPropertyName = "Description";
this.dataGridViewTextBoxColumn6.HeaderText = "Description";
this.dataGridViewTextBoxColumn6.Name = "dataGridViewTextBoxColumn6";
this.dataGridViewTextBoxColumn6.ReadOnly = true;
//
// dataGridViewTextBoxColumn7
//
this.dataGridViewTextBoxColumn7.DataPropertyName = "Category";
this.dataGridViewTextBoxColumn7.HeaderText = "Category";
this.dataGridViewTextBoxColumn7.Name = "dataGridViewTextBoxColumn7";
this.dataGridViewTextBoxColumn7.ReadOnly = true;
//
// dataGridViewTextBoxColumn8
//
this.dataGridViewTextBoxColumn8.DataPropertyName = "Product_Rating";
this.dataGridViewTextBoxColumn8.HeaderText = "Product_Rating";
this.dataGridViewTextBoxColumn8.Name = "dataGridViewTextBoxColumn8";
this.dataGridViewTextBoxColumn8.ReadOnly = true;
//
// dataGridViewTextBoxColumn9
//
this.dataGridViewTextBoxColumn9.DataPropertyName = "Purchase_Date";
this.dataGridViewTextBoxColumn9.HeaderText = "Purchase_Date";
this.dataGridViewTextBoxColumn9.Name = "dataGridViewTextBoxColumn9";
this.dataGridViewTextBoxColumn9.ReadOnly = true;
//
// dataGridViewTextBoxColumn10
//
this.dataGridViewTextBoxColumn10.DataPropertyName = "My_Rating";
this.dataGridViewTextBoxColumn10.HeaderText = "My_Rating";
this.dataGridViewTextBoxColumn10.Name = "dataGridViewTextBoxColumn10";
this.dataGridViewTextBoxColumn10.ReadOnly = true;
//
// dataGridViewTextBoxColumn11
//
this.dataGridViewTextBoxColumn11.DataPropertyName = "Misc";
this.dataGridViewTextBoxColumn11.HeaderText = "Misc";
this.dataGridViewTextBoxColumn11.Name = "dataGridViewTextBoxColumn11";
this.dataGridViewTextBoxColumn11.ReadOnly = true;
//
// dataGridViewTextBoxColumn12
//
this.dataGridViewTextBoxColumn12.DataPropertyName = "Download_Status";
this.dataGridViewTextBoxColumn12.HeaderText = "Download_Status";
this.dataGridViewTextBoxColumn12.Name = "dataGridViewTextBoxColumn12";
this.dataGridViewTextBoxColumn12.ReadOnly = true;
//
// ProductsGrid
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.Controls.Add(this.gridEntryDataGridView);
this.Name = "ProductsGrid";
this.Size = new System.Drawing.Size(434, 329);
((System.ComponentModel.ISupportInitialize)(this.gridEntryBindingSource)).EndInit();
((System.ComponentModel.ISupportInitialize)(this.gridEntryDataGridView)).EndInit();
this.ResumeLayout(false);
}
#endregion
private System.Windows.Forms.BindingSource gridEntryBindingSource;
private System.Windows.Forms.DataGridView gridEntryDataGridView;
private System.Windows.Forms.DataGridViewImageColumn dataGridViewImageColumn1;
private System.Windows.Forms.DataGridViewTextBoxColumn dataGridViewTextBoxColumn1;
private System.Windows.Forms.DataGridViewTextBoxColumn dataGridViewTextBoxColumn2;
private System.Windows.Forms.DataGridViewTextBoxColumn dataGridViewTextBoxColumn3;
private System.Windows.Forms.DataGridViewTextBoxColumn dataGridViewTextBoxColumn4;
private System.Windows.Forms.DataGridViewTextBoxColumn dataGridViewTextBoxColumn5;
private System.Windows.Forms.DataGridViewTextBoxColumn dataGridViewTextBoxColumn6;
private System.Windows.Forms.DataGridViewTextBoxColumn dataGridViewTextBoxColumn7;
private System.Windows.Forms.DataGridViewTextBoxColumn dataGridViewTextBoxColumn8;
private System.Windows.Forms.DataGridViewTextBoxColumn dataGridViewTextBoxColumn9;
private System.Windows.Forms.DataGridViewTextBoxColumn dataGridViewTextBoxColumn10;
private System.Windows.Forms.DataGridViewTextBoxColumn dataGridViewTextBoxColumn11;
private System.Windows.Forms.DataGridViewTextBoxColumn dataGridViewTextBoxColumn12;
}
}

View file

@ -0,0 +1,271 @@
using System;
using System.Drawing;
using System.Linq;
using System.Windows.Forms;
using Dinah.Core.DataBinding;
using DataLayer;
namespace LibationWinForm
{
// INSTRUCTIONS TO UPDATE DATA_GRID_VIEW
// - delete current DataGridView
// - view > other windows > data sources
// - refresh
// OR
// - Add New Data Source
// Object. Next
// LibationWinForm
// AudibleDTO
// GridEntry
// - go to Design view
// - click on Data Sources > ProductItem. drowdown: DataGridView
// - drag/drop ProductItem on design surface
public partial class ProductsGrid : UserControl
{
private DataGridView dataGridView;
private Form1 parent;
// this is a simple ctor for loading library and wish list. can expand later for other options. eg: overload ctor
public ProductsGrid(Form1 parent) : this() => this.parent = parent;
public ProductsGrid() => InitializeComponent();
private bool hasBeenDisplayed = false;
public void Display()
{
if (hasBeenDisplayed)
return;
hasBeenDisplayed = true;
dataGridView = gridEntryDataGridView;
dataGridView.Dock = DockStyle.Fill;
dataGridView.AllowUserToAddRows = false;
dataGridView.AllowUserToDeleteRows = false;
dataGridView.AutoGenerateColumns = false;
dataGridView.ColumnHeadersHeightSizeMode = DataGridViewColumnHeadersHeightSizeMode.AutoSize;
dataGridView.DefaultCellStyle.WrapMode = DataGridViewTriState.True;
dataGridView.ReadOnly = true;
dataGridView.RowHeadersVisible = false;
// adjust height for 80x80 pictures.
// this must be done before databinding. or can alter later by iterating through rows
dataGridView.RowTemplate.Height = 82;
dataGridView.CellFormatting += replaceFormatted;
dataGridView.CellFormatting += hiddenFormatting;
// sorting breaks filters. must reapply filters after sorting
dataGridView.Sorted += (_, __) => Filter();
{ // add tag buttons
var editUserTagsButton = new DataGridViewButtonColumn { HeaderText = "Edit Tags" };
dataGridView.Columns.Add(editUserTagsButton);
// add image and handle click
dataGridView.CellPainting += paintEditTag_TextAndImage;
dataGridView.CellContentClick += dataGridView_GridButtonClick;
}
for (var i = dataGridView.ColumnCount - 1; i >= 0; i--)
{
DataGridViewColumn col = dataGridView.Columns[i];
// initial HeaderText is the lookup name from GridEntry class. any formatting below won't change this
col.Name = col.HeaderText;
if (!(col is DataGridViewImageColumn || col is DataGridViewButtonColumn))
col.SortMode = DataGridViewColumnSortMode.Automatic;
col.HeaderText = col.HeaderText.Replace("_", " ");
if (col.Name == nameof(GridEntry.Title))
col.Width *= 2;
if (col.Name == nameof(GridEntry.Misc))
col.Width = (int)(col.Width * 1.35);
}
//
// transform into sorted GridEntry.s BEFORE binding
//
var lib = LibraryQueries.GetLibrary_Flat_NoTracking();
var orderedGridEntries = lib
.Select(lb => new GridEntry(lb)).ToList()
// default load order: sort by author, then series, then title
.OrderBy(ge => ge.Authors)
.ThenBy(ge => ge.Series)
.ThenBy(ge => ge.Title)
.ToList();
//
// BIND
//
gridEntryBindingSource.DataSource = orderedGridEntries.ToSortableBindingList();
//
// AFTER BINDING, BEFORE FILTERING
//
// now that we have data, remove/hide text columns with blank data. don't search image and button columns.
// simplifies the interface in general. also distinuishes library from wish list etc w/o explicit filters.
// must be AFTER BINDING, BEFORE FILTERING because we don't want to remove rows when valid data is simply not visible due to filtering.
for (var c = dataGridView.ColumnCount - 1; c >= 0; c--)
{
if (!(dataGridView.Columns[c] is DataGridViewTextBoxColumn textCol))
continue;
bool hasData = false;
for (var r = 0; r < dataGridView.RowCount; r++)
{
var value = dataGridView[c, r].Value;
if (value != null && value.ToString() != "")
{
hasData = true;
break;
}
}
if (!hasData)
dataGridView.Columns.Remove(textCol);
}
//
// FILTER
//
Filter();
}
private void paintEditTag_TextAndImage(object sender, DataGridViewCellPaintingEventArgs e)
{
// DataGridView Image for Button Column: https://stackoverflow.com/a/36253883
if (e.RowIndex < 0 || !(((DataGridView)sender).Columns[e.ColumnIndex] is DataGridViewButtonColumn))
return;
var gridEntry = getGridEntry(e.RowIndex);
var displayTags = gridEntry.TagsEnumerated.ToList();
if (displayTags.Any())
dataGridView.Rows[e.RowIndex].Cells[e.ColumnIndex].Value = string.Join("\r\n", displayTags);
else // no tags: use image
{
// clear tag text
dataGridView.Rows[e.RowIndex].Cells[e.ColumnIndex].Value = "";
// images from: icons8.com -- search: tags
var image = Properties.Resources.edit_tags_25x25;
e.Paint(e.CellBounds, DataGridViewPaintParts.All);
var w = image.Width;
var h = image.Height;
var x = e.CellBounds.Left + (e.CellBounds.Width - w) / 2;
var y = e.CellBounds.Top + (e.CellBounds.Height - h) / 2;
e.Graphics.DrawImage(image, new Rectangle(x, y, w, h));
e.Handled = true;
}
}
private void dataGridView_GridButtonClick(object sender, DataGridViewCellEventArgs e)
{
// handle grid button click: https://stackoverflow.com/a/13687844
if (e.RowIndex < 0)
return;
if (sender != dataGridView)
throw new Exception($"{nameof(dataGridView_GridButtonClick)} has incorrect sender ...somehow");
if (!(dataGridView.Columns[e.ColumnIndex] is DataGridViewButtonColumn))
return;
var liveGridEntry = getGridEntry(e.RowIndex);
// EditTagsDialog should display better-formatted title
liveGridEntry.TryGetFormatted(nameof(liveGridEntry.Title), out string value);
var editTagsForm = new EditTagsDialog(value, liveGridEntry.Tags);
if (editTagsForm.ShowDialog() != DialogResult.OK)
return;
var qtyChanges = saveChangedTags(liveGridEntry.GetBook(), editTagsForm.NewTags);
if (qtyChanges == 0)
return;
// force a re-draw, and re-apply filters
// needed to update text colors
dataGridView.InvalidateRow(e.RowIndex);
Filter();
}
private static int saveChangedTags(Book book, string newTags)
{
book.UserDefinedItem.Tags = newTags;
var qtyChanges = DomainServices.Indexer.IndexChangedTags(book);
return qtyChanges;
}
#region Cell Formatting
private void replaceFormatted(object sender, DataGridViewCellFormattingEventArgs e)
{
var col = ((DataGridView)sender).Columns[e.ColumnIndex];
if (col is DataGridViewTextBoxColumn textCol && getGridEntry(e.RowIndex).TryGetFormatted(textCol.Name, out string value))
e.Value = value;
}
private void hiddenFormatting(object sender, DataGridViewCellFormattingEventArgs e)
{
var isHidden = getGridEntry(e.RowIndex).TagsEnumerated.Contains("hidden");
dataGridView.Rows[e.RowIndex].Cells[e.ColumnIndex].Style
= isHidden
? new DataGridViewCellStyle { ForeColor = Color.LightGray }
: dataGridView.DefaultCellStyle;
}
#endregion
public void UpdateRow(string productId)
{
for (var r = dataGridView.RowCount - 1; r >= 0; r--)
{
var gridEntry = getGridEntry(r);
if (gridEntry.GetBook().AudibleProductId == productId)
{
var libBook = LibraryQueries.GetLibraryBook_Flat_NoTracking(productId);
gridEntry.REPLACE_Library_Book(libBook);
dataGridView.InvalidateRow(r);
return;
}
}
}
#region filter
string _filterSearchString;
public void Filter() => Filter(_filterSearchString);
public void Filter(string searchString)
{
_filterSearchString = searchString;
var searchResults = new LibationSearchEngine.SearchEngine().Search(searchString);
var productIds = searchResults.Docs.Select(d => d.ProductId).ToList();
// https://stackoverflow.com/a/18942430
var currencyManager = (CurrencyManager)BindingContext[dataGridView.DataSource];
currencyManager.SuspendBinding();
{
for (var r = dataGridView.RowCount - 1; r >= 0; r--)
dataGridView.Rows[r].Visible = productIds.Contains(getGridEntry(r).GetBook().AudibleProductId);
}
currencyManager.ResumeBinding();
// after applying filters, display new visible count
parent.SetVisibleCount(dataGridView.Rows.Cast<DataGridViewRow>().Count(r => r.Visible), searchResults.SearchString);
}
#endregion
private GridEntry getGridEntry(int rowIndex) => (GridEntry)dataGridView.Rows[rowIndex].DataBoundItem;
}
}

View file

@ -0,0 +1,123 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<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="gridEntryBindingSource.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<value>17, 17</value>
</metadata>
</root>

View file

@ -0,0 +1,22 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace LibationWinForm
{
static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Form1());
}
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 92 KiB