1
0
mirror of https://github.com/chylex/Brotli-Builder.git synced 2025-04-27 14:15:42 +02:00

Refactor bit stream & output panel into a separate Control

This commit is contained in:
chylex 2019-02-07 03:11:29 +01:00
parent 66b8795135
commit 648d9b7393
8 changed files with 558 additions and 344 deletions

View File

@ -92,6 +92,15 @@
<DependentUpon>BuildWindowSize.cs</DependentUpon>
</Compile>
<Compile Include="Blocks\IBuildingBlockContext.cs" />
<Compile Include="Components\BrotliFilePanel.cs">
<SubType>UserControl</SubType>
</Compile>
<Compile Include="Components\BrotliFilePanel.Designer.cs">
<DependentUpon>BrotliFilePanel.cs</DependentUpon>
</Compile>
<Compile Include="Components\RicherTextBox.cs">
<SubType>Component</SubType>
</Compile>
<Compile Include="Dialogs\FormStaticDictionary.cs">
<SubType>Form</SubType>
</Compile>
@ -148,6 +157,9 @@
<EmbeddedResource Include="Blocks\Structure\BuildWindowSize.resx">
<DependentUpon>BuildWindowSize.cs</DependentUpon>
</EmbeddedResource>
<EmbeddedResource Include="Components\BrotliFilePanel.resx">
<DependentUpon>BrotliFilePanel.cs</DependentUpon>
</EmbeddedResource>
<EmbeddedResource Include="Dialogs\FormStaticDictionary.resx">
<DependentUpon>FormStaticDictionary.cs</DependentUpon>
</EmbeddedResource>

View File

@ -0,0 +1,134 @@
namespace BrotliBuilder.Components {
partial class BrotliFilePanel {
/// <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.splitContainer = new System.Windows.Forms.SplitContainer();
this.labelBitStream = new System.Windows.Forms.Label();
this.textBoxBitStream = new RicherTextBox();
this.textBoxOutput = new RicherTextBox();
this.labelOutput = new System.Windows.Forms.Label();
((System.ComponentModel.ISupportInitialize)(this.splitContainer)).BeginInit();
this.splitContainer.Panel1.SuspendLayout();
this.splitContainer.Panel2.SuspendLayout();
this.splitContainer.SuspendLayout();
this.SuspendLayout();
//
// splitContainer
//
this.splitContainer.Dock = System.Windows.Forms.DockStyle.Fill;
this.splitContainer.IsSplitterFixed = true;
this.splitContainer.Location = new System.Drawing.Point(0, 0);
this.splitContainer.Margin = new System.Windows.Forms.Padding(0);
this.splitContainer.Name = "splitContainer";
this.splitContainer.Orientation = System.Windows.Forms.Orientation.Horizontal;
//
// splitContainer.Panel1
//
this.splitContainer.Panel1.Controls.Add(this.labelBitStream);
this.splitContainer.Panel1.Controls.Add(this.textBoxBitStream);
//
// splitContainer.Panel2
//
this.splitContainer.Panel2.Controls.Add(this.textBoxOutput);
this.splitContainer.Panel2.Controls.Add(this.labelOutput);
this.splitContainer.Size = new System.Drawing.Size(957, 422);
this.splitContainer.SplitterDistance = 206;
this.splitContainer.TabIndex = 1;
//
// labelBitStream
//
this.labelBitStream.AutoSize = true;
this.labelBitStream.Location = new System.Drawing.Point(9, 2);
this.labelBitStream.Margin = new System.Windows.Forms.Padding(3, 2, 3, 0);
this.labelBitStream.Name = "labelBitStream";
this.labelBitStream.Size = new System.Drawing.Size(55, 13);
this.labelBitStream.TabIndex = 0;
this.labelBitStream.Text = "Bit Stream";
//
// textBoxBitStream
//
this.textBoxBitStream.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.textBoxBitStream.DetectUrls = false;
this.textBoxBitStream.Font = new System.Drawing.Font("Consolas", 9.75F);
this.textBoxBitStream.Location = new System.Drawing.Point(12, 20);
this.textBoxBitStream.Name = "textBoxBitStream";
this.textBoxBitStream.ReadOnly = true;
this.textBoxBitStream.ScrollBars = System.Windows.Forms.RichTextBoxScrollBars.Vertical;
this.textBoxBitStream.Size = new System.Drawing.Size(933, 183);
this.textBoxBitStream.TabIndex = 1;
this.textBoxBitStream.Text = "";
//
// textBoxOutput
//
this.textBoxOutput.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.textBoxOutput.DetectUrls = false;
this.textBoxOutput.Font = new System.Drawing.Font("Consolas", 9.75F);
this.textBoxOutput.Location = new System.Drawing.Point(12, 20);
this.textBoxOutput.Name = "textBoxOutput";
this.textBoxOutput.ReadOnly = true;
this.textBoxOutput.Size = new System.Drawing.Size(933, 189);
this.textBoxOutput.TabIndex = 1;
this.textBoxOutput.Text = "";
this.textBoxOutput.WordWrap = false;
//
// labelOutput
//
this.labelOutput.AutoSize = true;
this.labelOutput.Location = new System.Drawing.Point(9, 2);
this.labelOutput.Margin = new System.Windows.Forms.Padding(3, 2, 3, 0);
this.labelOutput.Name = "labelOutput";
this.labelOutput.Size = new System.Drawing.Size(39, 13);
this.labelOutput.TabIndex = 0;
this.labelOutput.Text = "Output";
//
// BrotliFilePanel
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.Controls.Add(this.splitContainer);
this.Name = "BrotliFilePanel";
this.Size = new System.Drawing.Size(957, 422);
this.splitContainer.Panel1.ResumeLayout(false);
this.splitContainer.Panel1.PerformLayout();
this.splitContainer.Panel2.ResumeLayout(false);
this.splitContainer.Panel2.PerformLayout();
((System.ComponentModel.ISupportInitialize)(this.splitContainer)).EndInit();
this.splitContainer.ResumeLayout(false);
this.ResumeLayout(false);
}
#endregion
private System.Windows.Forms.SplitContainer splitContainer;
private System.Windows.Forms.Label labelBitStream;
private RicherTextBox textBoxBitStream;
private RicherTextBox textBoxOutput;
private System.Windows.Forms.Label labelOutput;
}
}

View File

@ -0,0 +1,150 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using System.Linq;
using System.Text.RegularExpressions;
using System.Windows.Forms;
using BrotliBuilder.Utils;
using BrotliLib.Brotli;
using BrotliLib.IO;
namespace BrotliBuilder.Components{
public partial class BrotliFilePanel : UserControl{
public string LabelPrefix{
get{
return labelPrefix;
}
set{
labelPrefix = value;
labelBitStream.Text = $"{value} Bit Stream";
labelOutput.Text = $"{value} Output";
loadWorker.Name = $"BrotliFilePanel ({value})";
}
}
public bool WordWrapOutput{
set => textBoxOutput.WordWrap = value;
}
public int MaxLength{
set{
textBoxBitStream.MaxDisplayedLength = value;
textBoxOutput.MaxDisplayedLength = value;
}
}
private readonly AsyncWorker loadWorker = new AsyncWorker();
private string labelPrefix = null;
public BrotliFilePanel(){
InitializeComponent();
textBoxOutput.SetPlainTextMode();
}
public void LoadBrotliFile(byte[] bytes, Action<BrotliFileStructure> callback){
InvalidatePanel();
loadWorker.Start(sync => {
BitStream bits = new BitStream(bytes);
string bitsStr = bits.ToString();
sync(() => UpdateTextBox(textBoxBitStream, bitsStr));
BrotliFileStructure file = BrotliFileStructure.FromBytes(bytes);
BrotliGlobalState state;
try{
state = file.GetDecompressionState(bits);
}catch(Exception ex){
sync(() => UpdateTextBox(textBoxOutput, ex));
return;
}
string outputStr = state.OutputAsUTF8;
sync(() => {
UpdateTextBox(textBoxOutput, outputStr);
callback(file);
});
});
}
public void LoadBrotliFile(BrotliFileStructure file, Action<Stopwatch> onSerialized, Action<Stopwatch> onDecompressed){
InvalidatePanel();
loadWorker.Start(sync => {
BitStream bits;
BrotliGlobalState state;
Stopwatch stopwatchSerialization = Stopwatch.StartNew();
try{
bits = file.Serialize();
}catch(Exception ex){
sync(() => {
UpdateTextBox(textBoxBitStream, ex);
onSerialized(null);
onDecompressed(null);
});
return;
}finally{
stopwatchSerialization.Stop();
}
string bitsStr = bits.ToString();
sync(() => {
UpdateTextBox(textBoxBitStream, bitsStr);
onSerialized(stopwatchSerialization);
});
Stopwatch stopwatchDecompression = Stopwatch.StartNew();
try{
state = file.GetDecompressionState(bits);
}catch(Exception ex){
sync(() => {
UpdateTextBox(textBoxOutput, ex);
onDecompressed(null);
});
return;
}finally{
stopwatchDecompression.Stop();
}
string outputStr = state.OutputAsUTF8;
sync(() => {
UpdateTextBox(textBoxOutput, outputStr);
onDecompressed(stopwatchDecompression);
});
});
}
public void InvalidatePanel(){
loadWorker.Abort();
textBoxBitStream.ForeColor = SystemColors.ControlDark;
textBoxOutput.ForeColor = SystemColors.ControlDark;
}
private void UpdateTextBox(RicherTextBox tb, string text, Color color){
tb.ForeColor = color;
tb.Text = text;
tb.Select(0, 0);
}
private void UpdateTextBox(RicherTextBox tb, string text){
UpdateTextBox(tb, text, SystemColors.WindowText);
}
private void UpdateTextBox(RicherTextBox tb, Exception ex){
UpdateTextBox(tb, Regex.Replace(ex.ToString(), " in (.*):", " : "), Color.Red);
}
}
}

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,41 @@
using System.Windows.Forms;
namespace BrotliBuilder.Components{
public sealed class RicherTextBox : RichTextBox{
public int MaxDisplayedLength{
get{
return maxDisplayedLength;
}
set{
if (maxDisplayedLength != value){
maxDisplayedLength = value;
UpdateDisplayedText();
}
}
}
public new string Text{
get{
return fullText;
}
set{
fullText = value;
UpdateDisplayedText();
}
}
private int maxDisplayedLength = -1;
private string fullText = string.Empty;
private void UpdateDisplayedText(){
if (maxDisplayedLength == -1 || fullText.Length <= maxDisplayedLength){
base.Text = fullText;
}
else{
base.Text = fullText.Substring(0, maxDisplayedLength) + "(...)";
}
}
}
}

View File

@ -30,45 +30,30 @@
this.menuItemSave = new System.Windows.Forms.MenuItem();
this.menuItemExit = new System.Windows.Forms.MenuItem();
this.menuItemView = new System.Windows.Forms.MenuItem();
this.menuItemWrapOutput = new System.Windows.Forms.MenuItem();
this.menuItemLimitOutput = new System.Windows.Forms.MenuItem();
this.menuItemTools = new System.Windows.Forms.MenuItem();
this.menuItemStaticDictionary = new System.Windows.Forms.MenuItem();
this.menuItemEncodeFile = new System.Windows.Forms.MenuItem();
this.menuItemEncodeUncompressedMBs = new System.Windows.Forms.MenuItem();
this.flowPanelBlocks = new System.Windows.Forms.FlowLayoutPanel();
this.textBoxGenBitStream = new System.Windows.Forms.RichTextBox();
this.labelGenBitStream = new System.Windows.Forms.Label();
this.labelGenOutput = new System.Windows.Forms.Label();
this.textBoxGenOutput = new System.Windows.Forms.RichTextBox();
this.statusBar = new System.Windows.Forms.StatusBar();
this.statusBarPanelPadding1 = new System.Windows.Forms.StatusBarPanel();
this.statusBarPanelTimeBits = new System.Windows.Forms.StatusBarPanel();
this.statusBarPanelPadding2 = new System.Windows.Forms.StatusBarPanel();
this.statusBarPanelTimeOutput = new System.Windows.Forms.StatusBarPanel();
this.timerRegenerationDelay = new System.Windows.Forms.Timer(this.components);
this.splitContainerLeft = new System.Windows.Forms.SplitContainer();
this.splitContainerOuter = new System.Windows.Forms.SplitContainer();
this.splitContainerRight = new System.Windows.Forms.SplitContainer();
this.labelOrigBitStream = new System.Windows.Forms.Label();
this.textBoxOrigBitStream = new System.Windows.Forms.RichTextBox();
this.textBoxOrigOutput = new System.Windows.Forms.RichTextBox();
this.labelOrigOutput = new System.Windows.Forms.Label();
this.brotliFilePanelGenerated = new BrotliBuilder.Components.BrotliFilePanel();
this.brotliFilePanelOriginal = new BrotliBuilder.Components.BrotliFilePanel();
((System.ComponentModel.ISupportInitialize)(this.statusBarPanelPadding1)).BeginInit();
((System.ComponentModel.ISupportInitialize)(this.statusBarPanelTimeBits)).BeginInit();
((System.ComponentModel.ISupportInitialize)(this.statusBarPanelPadding2)).BeginInit();
((System.ComponentModel.ISupportInitialize)(this.statusBarPanelTimeOutput)).BeginInit();
((System.ComponentModel.ISupportInitialize)(this.splitContainerLeft)).BeginInit();
this.splitContainerLeft.Panel1.SuspendLayout();
this.splitContainerLeft.Panel2.SuspendLayout();
this.splitContainerLeft.SuspendLayout();
((System.ComponentModel.ISupportInitialize)(this.splitContainerOuter)).BeginInit();
this.splitContainerOuter.Panel1.SuspendLayout();
this.splitContainerOuter.Panel2.SuspendLayout();
this.splitContainerOuter.SuspendLayout();
((System.ComponentModel.ISupportInitialize)(this.splitContainerRight)).BeginInit();
this.splitContainerRight.Panel1.SuspendLayout();
this.splitContainerRight.Panel2.SuspendLayout();
this.splitContainerRight.SuspendLayout();
this.SuspendLayout();
//
// mainMenu
@ -111,13 +96,19 @@
//
this.menuItemView.Index = 1;
this.menuItemView.MenuItems.AddRange(new System.Windows.Forms.MenuItem[] {
this.menuItemWrapOutput,
this.menuItemLimitOutput});
this.menuItemView.Text = "&View";
//
// menuItemWrapOutput
//
this.menuItemWrapOutput.Index = 0;
this.menuItemWrapOutput.Text = "Wrap Output";
this.menuItemWrapOutput.Click += new System.EventHandler(this.menuItemWrapOutput_Click);
//
// menuItemLimitOutput
//
this.menuItemLimitOutput.Checked = true;
this.menuItemLimitOutput.Index = 0;
this.menuItemLimitOutput.Index = 1;
this.menuItemLimitOutput.Text = "Limit Output Length";
this.menuItemLimitOutput.Click += new System.EventHandler(this.menuItemLimitOutput_Click);
//
@ -163,56 +154,6 @@
this.flowPanelBlocks.TabIndex = 0;
this.flowPanelBlocks.WrapContents = false;
//
// textBoxGenBitStream
//
this.textBoxGenBitStream.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.textBoxGenBitStream.DetectUrls = false;
this.textBoxGenBitStream.Font = new System.Drawing.Font("Consolas", 9.75F);
this.textBoxGenBitStream.Location = new System.Drawing.Point(12, 20);
this.textBoxGenBitStream.Name = "textBoxGenBitStream";
this.textBoxGenBitStream.ReadOnly = true;
this.textBoxGenBitStream.ScrollBars = System.Windows.Forms.RichTextBoxScrollBars.Vertical;
this.textBoxGenBitStream.Size = new System.Drawing.Size(478, 101);
this.textBoxGenBitStream.TabIndex = 1;
this.textBoxGenBitStream.Text = "";
//
// labelGenBitStream
//
this.labelGenBitStream.AutoSize = true;
this.labelGenBitStream.Location = new System.Drawing.Point(9, 2);
this.labelGenBitStream.Margin = new System.Windows.Forms.Padding(3, 2, 3, 0);
this.labelGenBitStream.Name = "labelGenBitStream";
this.labelGenBitStream.Size = new System.Drawing.Size(118, 15);
this.labelGenBitStream.TabIndex = 0;
this.labelGenBitStream.Text = "Generated Bit Stream";
//
// labelGenOutput
//
this.labelGenOutput.AutoSize = true;
this.labelGenOutput.Location = new System.Drawing.Point(9, 2);
this.labelGenOutput.Margin = new System.Windows.Forms.Padding(3, 2, 3, 0);
this.labelGenOutput.Name = "labelGenOutput";
this.labelGenOutput.Size = new System.Drawing.Size(102, 15);
this.labelGenOutput.TabIndex = 0;
this.labelGenOutput.Text = "Generated Output";
//
// textBoxGenOutput
//
this.textBoxGenOutput.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.textBoxGenOutput.DetectUrls = false;
this.textBoxGenOutput.Font = new System.Drawing.Font("Consolas", 9.75F);
this.textBoxGenOutput.Location = new System.Drawing.Point(12, 20);
this.textBoxGenOutput.Name = "textBoxGenOutput";
this.textBoxGenOutput.ReadOnly = true;
this.textBoxGenOutput.ScrollBars = System.Windows.Forms.RichTextBoxScrollBars.Vertical;
this.textBoxGenOutput.Size = new System.Drawing.Size(478, 101);
this.textBoxGenOutput.TabIndex = 1;
this.textBoxGenOutput.Text = "";
//
// statusBar
//
this.statusBar.Location = new System.Drawing.Point(0, 687);
@ -256,28 +197,6 @@
this.timerRegenerationDelay.Interval = 300;
this.timerRegenerationDelay.Tick += new System.EventHandler(this.timerRegenerationDelay_Tick);
//
// splitContainerLeft
//
this.splitContainerLeft.Dock = System.Windows.Forms.DockStyle.Fill;
this.splitContainerLeft.IsSplitterFixed = true;
this.splitContainerLeft.Location = new System.Drawing.Point(0, 0);
this.splitContainerLeft.Margin = new System.Windows.Forms.Padding(0);
this.splitContainerLeft.Name = "splitContainerLeft";
this.splitContainerLeft.Orientation = System.Windows.Forms.Orientation.Horizontal;
//
// splitContainerLeft.Panel1
//
this.splitContainerLeft.Panel1.Controls.Add(this.labelGenBitStream);
this.splitContainerLeft.Panel1.Controls.Add(this.textBoxGenBitStream);
//
// splitContainerLeft.Panel2
//
this.splitContainerLeft.Panel2.Controls.Add(this.textBoxGenOutput);
this.splitContainerLeft.Panel2.Controls.Add(this.labelGenOutput);
this.splitContainerLeft.Size = new System.Drawing.Size(502, 252);
this.splitContainerLeft.SplitterDistance = 124;
this.splitContainerLeft.TabIndex = 0;
//
// splitContainerOuter
//
this.splitContainerOuter.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
@ -288,86 +207,32 @@
//
// splitContainerOuter.Panel1
//
this.splitContainerOuter.Panel1.Controls.Add(this.splitContainerLeft);
this.splitContainerOuter.Panel1.Controls.Add(this.brotliFilePanelGenerated);
//
// splitContainerOuter.Panel2
//
this.splitContainerOuter.Panel2.Controls.Add(this.splitContainerRight);
this.splitContainerOuter.Panel2.Controls.Add(this.brotliFilePanelOriginal);
this.splitContainerOuter.Size = new System.Drawing.Size(1008, 252);
this.splitContainerOuter.SplitterDistance = 502;
this.splitContainerOuter.TabIndex = 1;
//
// splitContainerRight
// brotliFilePanelGenerated
//
this.splitContainerRight.Dock = System.Windows.Forms.DockStyle.Fill;
this.splitContainerRight.IsSplitterFixed = true;
this.splitContainerRight.Location = new System.Drawing.Point(0, 0);
this.splitContainerRight.Margin = new System.Windows.Forms.Padding(0);
this.splitContainerRight.Name = "splitContainerRight";
this.splitContainerRight.Orientation = System.Windows.Forms.Orientation.Horizontal;
this.brotliFilePanelGenerated.Dock = System.Windows.Forms.DockStyle.Fill;
this.brotliFilePanelGenerated.LabelPrefix = "Generated";
this.brotliFilePanelGenerated.Location = new System.Drawing.Point(0, 0);
this.brotliFilePanelGenerated.Name = "brotliFilePanelGenerated";
this.brotliFilePanelGenerated.Size = new System.Drawing.Size(502, 252);
this.brotliFilePanelGenerated.TabIndex = 0;
//
// splitContainerRight.Panel1
// brotliFilePanelOriginal
//
this.splitContainerRight.Panel1.Controls.Add(this.labelOrigBitStream);
this.splitContainerRight.Panel1.Controls.Add(this.textBoxOrigBitStream);
//
// splitContainerRight.Panel2
//
this.splitContainerRight.Panel2.Controls.Add(this.textBoxOrigOutput);
this.splitContainerRight.Panel2.Controls.Add(this.labelOrigOutput);
this.splitContainerRight.Size = new System.Drawing.Size(502, 252);
this.splitContainerRight.SplitterDistance = 124;
this.splitContainerRight.TabIndex = 0;
//
// labelOrigBitStream
//
this.labelOrigBitStream.AutoSize = true;
this.labelOrigBitStream.Location = new System.Drawing.Point(9, 2);
this.labelOrigBitStream.Margin = new System.Windows.Forms.Padding(3, 2, 3, 0);
this.labelOrigBitStream.Name = "labelOrigBitStream";
this.labelOrigBitStream.Size = new System.Drawing.Size(106, 15);
this.labelOrigBitStream.TabIndex = 0;
this.labelOrigBitStream.Text = "Original Bit Stream";
//
// textBoxOrigBitStream
//
this.textBoxOrigBitStream.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.textBoxOrigBitStream.DetectUrls = false;
this.textBoxOrigBitStream.Font = new System.Drawing.Font("Consolas", 9.75F);
this.textBoxOrigBitStream.Location = new System.Drawing.Point(12, 20);
this.textBoxOrigBitStream.Name = "textBoxOrigBitStream";
this.textBoxOrigBitStream.ReadOnly = true;
this.textBoxOrigBitStream.ScrollBars = System.Windows.Forms.RichTextBoxScrollBars.Vertical;
this.textBoxOrigBitStream.Size = new System.Drawing.Size(478, 101);
this.textBoxOrigBitStream.TabIndex = 1;
this.textBoxOrigBitStream.Text = "";
//
// textBoxOrigOutput
//
this.textBoxOrigOutput.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.textBoxOrigOutput.DetectUrls = false;
this.textBoxOrigOutput.Font = new System.Drawing.Font("Consolas", 9.75F);
this.textBoxOrigOutput.Location = new System.Drawing.Point(12, 20);
this.textBoxOrigOutput.Name = "textBoxOrigOutput";
this.textBoxOrigOutput.ReadOnly = true;
this.textBoxOrigOutput.ScrollBars = System.Windows.Forms.RichTextBoxScrollBars.Vertical;
this.textBoxOrigOutput.Size = new System.Drawing.Size(478, 101);
this.textBoxOrigOutput.TabIndex = 1;
this.textBoxOrigOutput.Text = "";
//
// labelOrigOutput
//
this.labelOrigOutput.AutoSize = true;
this.labelOrigOutput.Location = new System.Drawing.Point(9, 2);
this.labelOrigOutput.Margin = new System.Windows.Forms.Padding(3, 2, 3, 0);
this.labelOrigOutput.Name = "labelOrigOutput";
this.labelOrigOutput.Size = new System.Drawing.Size(90, 15);
this.labelOrigOutput.TabIndex = 0;
this.labelOrigOutput.Text = "Original Output";
this.brotliFilePanelOriginal.Dock = System.Windows.Forms.DockStyle.Fill;
this.brotliFilePanelOriginal.LabelPrefix = "Original";
this.brotliFilePanelOriginal.Location = new System.Drawing.Point(0, 0);
this.brotliFilePanelOriginal.Name = "brotliFilePanelOriginal";
this.brotliFilePanelOriginal.Size = new System.Drawing.Size(502, 252);
this.brotliFilePanelOriginal.TabIndex = 0;
//
// FormMain
//
@ -389,22 +254,10 @@
((System.ComponentModel.ISupportInitialize)(this.statusBarPanelTimeBits)).EndInit();
((System.ComponentModel.ISupportInitialize)(this.statusBarPanelPadding2)).EndInit();
((System.ComponentModel.ISupportInitialize)(this.statusBarPanelTimeOutput)).EndInit();
this.splitContainerLeft.Panel1.ResumeLayout(false);
this.splitContainerLeft.Panel1.PerformLayout();
this.splitContainerLeft.Panel2.ResumeLayout(false);
this.splitContainerLeft.Panel2.PerformLayout();
((System.ComponentModel.ISupportInitialize)(this.splitContainerLeft)).EndInit();
this.splitContainerLeft.ResumeLayout(false);
this.splitContainerOuter.Panel1.ResumeLayout(false);
this.splitContainerOuter.Panel2.ResumeLayout(false);
((System.ComponentModel.ISupportInitialize)(this.splitContainerOuter)).EndInit();
this.splitContainerOuter.ResumeLayout(false);
this.splitContainerRight.Panel1.ResumeLayout(false);
this.splitContainerRight.Panel1.PerformLayout();
this.splitContainerRight.Panel2.ResumeLayout(false);
this.splitContainerRight.Panel2.PerformLayout();
((System.ComponentModel.ISupportInitialize)(this.splitContainerRight)).EndInit();
this.splitContainerRight.ResumeLayout(false);
this.ResumeLayout(false);
}
@ -415,12 +268,8 @@
private System.Windows.Forms.MenuItem menuItemFile;
private System.Windows.Forms.MenuItem menuItemExit;
private System.Windows.Forms.FlowLayoutPanel flowPanelBlocks;
private System.Windows.Forms.RichTextBox textBoxGenBitStream;
private System.Windows.Forms.MenuItem menuItemSave;
private System.Windows.Forms.Label labelGenBitStream;
private System.Windows.Forms.MenuItem menuItemOpen;
private System.Windows.Forms.Label labelGenOutput;
private System.Windows.Forms.RichTextBox textBoxGenOutput;
private System.Windows.Forms.StatusBar statusBar;
private System.Windows.Forms.StatusBarPanel statusBarPanelTimeBits;
private System.Windows.Forms.StatusBarPanel statusBarPanelTimeOutput;
@ -433,12 +282,9 @@
private System.Windows.Forms.MenuItem menuItemView;
private System.Windows.Forms.MenuItem menuItemLimitOutput;
private System.Windows.Forms.MenuItem menuItemStaticDictionary;
private System.Windows.Forms.SplitContainer splitContainerLeft;
private System.Windows.Forms.SplitContainer splitContainerOuter;
private System.Windows.Forms.SplitContainer splitContainerRight;
private System.Windows.Forms.Label labelOrigBitStream;
private System.Windows.Forms.RichTextBox textBoxOrigBitStream;
private System.Windows.Forms.RichTextBox textBoxOrigOutput;
private System.Windows.Forms.Label labelOrigOutput;
private System.Windows.Forms.MenuItem menuItemWrapOutput;
private Components.BrotliFilePanel brotliFilePanelGenerated;
private Components.BrotliFilePanel brotliFilePanelOriginal;
}
}

View File

@ -1,17 +1,12 @@
using System;
using System.Diagnostics;
using System.Drawing;
using System.IO;
using System.Text.RegularExpressions;
using System.Windows.Forms;
using BrotliBuilder.Blocks;
using BrotliBuilder.Dialogs;
using BrotliBuilder.Utils;
using BrotliLib.Brotli;
using BrotliLib.Brotli.Components;
using BrotliLib.Brotli.Dictionary.Source;
using BrotliLib.Brotli.Encode;
using BrotliLib.IO;
namespace BrotliBuilder{
partial class FormMain : Form{
@ -64,35 +59,11 @@ namespace BrotliBuilder{
private BrotliFileStructure brotliFile = BrotliFileStructure.NewEmpty();
private string lastFileName = "compressed";
private bool isDirty = false;
private readonly AsyncWorker<string> workerStream;
private readonly AsyncWorker<string> workerOutput;
public FormMain(){
InitializeComponent();
textBoxGenBitStream.SetPlainTextMode();
textBoxGenOutput.SetPlainTextMode();
workerStream = new AsyncWorker<string>("GenBits");
workerOutput = new AsyncWorker<string>("GenOutput");
SetupWorker(
workerStream,
textBoxGenBitStream,
statusBarPanelTimeBits,
ms => "Generated bit stream in " + ms + " ms.",
() => "Error generating bit stream."
);
SetupWorker(
workerOutput,
textBoxGenOutput,
statusBarPanelTimeOutput,
ms => "Generated output in " + ms + " ms.",
() => "Error generating output."
);
menuItemLimitOutput_Click(this, EventArgs.Empty);
splitContainerOuter.Panel2Collapsed = true;
OnNewBrotliFile();
}
@ -100,30 +71,53 @@ namespace BrotliBuilder{
#region File state handling
private void LoadExistingBrotliFile(byte[] bytes){
brotliFile = BrotliFileStructure.FromBytes(bytes);
OnNewBrotliFile();
BitStream bits = new BitStream(bytes);
textBoxOrigBitStream.Text = bits.ToString();
try{
UpdateTextBox(textBoxOrigOutput, brotliFile.GetDecompressionState(bits).OutputAsUTF8);
}catch(Exception ex){
UpdateTextBox(textBoxOrigOutput, ex);
}
splitContainerOuter.Panel2Collapsed = false;
statusBarPanelTimeBits.Text = "Decompressing...";
statusBarPanelTimeOutput.Text = "Decompressing...";
flowPanelBlocks.Controls.Clear();
brotliFilePanelGenerated.InvalidatePanel();
brotliFilePanelOriginal.LoadBrotliFile(bytes, file => {
brotliFile = file;
OnNewBrotliFile();
});
}
private void OnNewBrotliFile(){
flowPanelBlocks.Controls.Clear();
flowPanelBlocks.Controls.Add(new BuildFileStructure(new BuildingBlockContext(this, flowPanelBlocks), brotliFile));
splitContainerOuter.Panel2Collapsed = true;
RegenerateBrotliStream(markAsDirty: false);
isDirty = false;
}
private void timerRegenerationDelay_Tick(object sender, EventArgs e){
timerRegenerationDelay.Stop();
statusBarPanelTimeBits.Text = "Generating...";
statusBarPanelTimeOutput.Text = "Generating...";
brotliFilePanelGenerated.LoadBrotliFile(
brotliFile,
onSerializedStopwatch =>
statusBarPanelTimeBits.Text = onSerializedStopwatch == null ?
"Error generating bit stream." :
"Generated bit stream in " + onSerializedStopwatch.ElapsedMilliseconds + " ms.",
onDecompressedStopwatch =>
statusBarPanelTimeOutput.Text = onDecompressedStopwatch == null ?
"Error generating output." :
"Generated output in " + onDecompressedStopwatch.ElapsedMilliseconds + " ms."
);
}
private void RegenerateBrotliStream(bool markAsDirty){
isDirty = isDirty || markAsDirty;
timerRegenerationDelay.Stop();
timerRegenerationDelay.Start();
}
private bool PromptUnsavedChanges(string message){
if (!isDirty){
@ -148,55 +142,6 @@ namespace BrotliBuilder{
#endregion
#region Output generation
private void UpdateTextBox(RichTextBox tb, string text){
if (menuItemLimitOutput.Checked && text.Length > LimitOutputLength){
text = text.Substring(0, LimitOutputLength) + "(...)";
}
tb.ForeColor = SystemColors.WindowText;
tb.Text = text;
}
private void UpdateTextBox(RichTextBox tb, Exception ex){
tb.ForeColor = Color.Red;
tb.Text = Regex.Replace(ex.ToString(), " in (.*):", " : ");
}
private void SetupWorker(AsyncWorker<string> worker, RichTextBox tb, StatusBarPanel status, Func<long, string> funcStatusTextSuccess, Func<string> funcStatusTextFailure){
worker.WorkFinished += (sender, args) => {
UpdateTextBox(tb, args.Result);
status.Text = funcStatusTextSuccess(args.Stopwatch.ElapsedMilliseconds);
};
worker.WorkCrashed += (sender, args) => {
UpdateTextBox(tb, args.Exception);
status.Text = funcStatusTextFailure();
};
}
private void timerRegenerationDelay_Tick(object sender, EventArgs e){
timerRegenerationDelay.Stop();
textBoxGenBitStream.ForeColor = SystemColors.GrayText;
textBoxGenOutput.ForeColor = SystemColors.GrayText;
statusBarPanelTimeBits.Text = "Generating...";
statusBarPanelTimeOutput.Text = "Generating...";
workerStream.Start(() => brotliFile.Serialize().ToString());
workerOutput.Start(() => brotliFile.GetDecompressionState(brotliFile.Serialize()).OutputAsUTF8);
}
private void RegenerateBrotliStream(bool markAsDirty){
isDirty = isDirty || markAsDirty;
timerRegenerationDelay.Stop();
timerRegenerationDelay.Start();
}
#endregion
#region Form events
private void FormMain_FormClosing(object sender, FormClosingEventArgs e){
@ -255,9 +200,18 @@ namespace BrotliBuilder{
#region Menu events (View)
private void menuItemWrapOutput_Click(object sender, EventArgs e){
bool enable = !menuItemWrapOutput.Checked;
menuItemWrapOutput.Checked = enable;
brotliFilePanelGenerated.WordWrapOutput = brotliFilePanelOriginal.WordWrapOutput = enable;
}
private void menuItemLimitOutput_Click(object sender, EventArgs e){
menuItemLimitOutput.Checked = !menuItemLimitOutput.Checked;
RegenerateBrotliStream(markAsDirty: false);
bool enable = !menuItemLimitOutput.Checked;
menuItemLimitOutput.Checked = enable;
brotliFilePanelGenerated.MaxLength = brotliFilePanelOriginal.MaxLength = enable ? LimitOutputLength : -1;
}
#endregion
@ -265,21 +219,13 @@ namespace BrotliBuilder{
#region Menu events (Tools)
private void menuItemStaticDictionary_Click(object sender, EventArgs e){
using(OpenFileDialog dialog = new OpenFileDialog{
Title = "Static Dictionary",
Filter = "All Files (*.*)|*.*"
}){
if (dialog.ShowDialog() == DialogResult.OK){
try{
using(BrotliDefaultDictionary dict = new BrotliDefaultDictionary(new MemorySource(dialog.FileName)))
using(FormStaticDictionary form = new FormStaticDictionary(dict)){
form.ShowDialog();
}
}catch(Exception ex){
Debug.WriteLine(ex.ToString());
MessageBox.Show(ex.Message, "Static Dictionary Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
try{
using(FormStaticDictionary form = new FormStaticDictionary(BrotliDefaultDictionary.Embedded)){
form.ShowDialog();
}
}catch(Exception ex){
Debug.WriteLine(ex.ToString());
MessageBox.Show(ex.Message, "Static Dictionary Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
@ -309,6 +255,8 @@ namespace BrotliBuilder{
MessageBox.Show(ex.Message, "File Open Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
return;
}
splitContainerOuter.Panel2Collapsed = true;
try{
brotliFile = BrotliFileStructure.FromEncoder(windowSize, encoder, bytes);
@ -322,5 +270,6 @@ namespace BrotliBuilder{
}
#endregion
}
}

View File

@ -1,51 +1,29 @@
using System;
using System.Diagnostics;
using System.Threading;
using System.Threading.Tasks;
namespace BrotliBuilder.Utils{
sealed class AsyncWorker<T>{
public class FinishedArgs : EventArgs{
public T Result { get; }
public Stopwatch Stopwatch { get; }
sealed class AsyncWorker{
public delegate void Work(Action<Action> sync);
public FinishedArgs(T result, Stopwatch stopwatch){
this.Result = result;
this.Stopwatch = stopwatch;
}
}
public class CrashedArgs : EventArgs{
public Exception Exception { get; }
public CrashedArgs(Exception exception){
this.Exception = exception;
}
}
public event EventHandler<FinishedArgs> WorkFinished;
public event EventHandler<CrashedArgs> WorkCrashed;
public event EventHandler<EventArgs> WorkAborted;
public bool IsBusy => currentThread?.IsAlive == true;
public string Name { get; set; }
private readonly TaskFactory taskFactory = new TaskFactory(TaskScheduler.FromCurrentSynchronizationContext());
private readonly string threadName;
private Thread currentThread;
public AsyncWorker(string threadName){
this.threadName = threadName;
}
public void Start(Func<T> action){
public void Start(Work action){
Abort();
currentThread = new Thread(Work){
Name = threadName,
void Worker(){
action(callback => taskFactory.StartNew(callback));
}
currentThread = new Thread(Worker){
Name = this.Name,
IsBackground = true
};
currentThread.Start(action);
currentThread.Start();
}
public void Abort(){
@ -56,21 +34,5 @@ namespace BrotliBuilder.Utils{
currentThread = null;
}
}
private void Work(object data){
Func<T> action = (Func<T>)data;
try{
Stopwatch stopwatch = Stopwatch.StartNew();
T result = action.Invoke();
stopwatch.Stop();
taskFactory.StartNew(() => WorkFinished?.Invoke(this, new FinishedArgs(result, stopwatch)));
}catch(ThreadAbortException){
taskFactory.StartNew(() => WorkAborted?.Invoke(this, EventArgs.Empty));
}catch(Exception e){
taskFactory.StartNew(() => WorkCrashed?.Invoke(this, new CrashedArgs(e)));
}
}
}
}