Add GUI console.

This commit is contained in:
2018-08-27 18:25:11 +01:00
parent 5a2de92355
commit f2757dc24c
10 changed files with 316 additions and 12 deletions

View File

@@ -1,12 +1,12 @@
[*]
charset=utf-8
end_of_line=lf
trim_trailing_whitespace=false
trim_trailing_whitespace=true
insert_final_newline=false
indent_style=space
indent_size=4
[{.babelrc,.stylelintrc,.eslintrc,jest.config,*.bowerrc,*.jsb3,*.jsb2,*.json}]
[{.babelrc,.stylelintrc,.eslintrc,jest.config,*.uplugin,*.bowerrc,*.jsb3,*.jsb2,*.json}]
indent_style=space
indent_size=2

View File

@@ -56,6 +56,7 @@
</e>
</e>
</e>
<e p="test.log" t="Include" />
</e>
<e p="DiscImageChef.Checksums" t="IncludeRecursive">
<e p="Adler32Context.cs" t="Include" />
@@ -1247,8 +1248,11 @@
</e>
</e>
<e p="DiscImageChef.Gui" t="IncludeRecursive">
<e p="ConsoleHandler.cs" t="Include" />
<e p="DiscImageChef.Gui.csproj" t="IncludeRecursive" />
<e p="bin" t="ExcludeRecursive" />
<e p="frmConsole.xeto" t="Include" />
<e p="frmConsole.xeto.cs" t="Include" />
<e p="frmMain.xeto" t="Include" />
<e p="frmMain.xeto.cs" t="Include" />
<e p="obj" t="ExcludeRecursive">

View File

@@ -48,16 +48,19 @@ namespace DiscImageChef.Console
public delegate void DebugWriteHandler(string format, params object[] arg);
public delegate void DebugWithModuleWriteLineHandler(string module, string format, params object[] arg);
/// <summary>
/// Implements a console abstraction that defines four level of messages that can be routed to different consoles:
/// standard, error, verbose and debug.
/// </summary>
public static class DicConsole
{
public static event WriteLineHandler WriteLineEvent;
public static event ErrorWriteLineHandler ErrorWriteLineEvent;
public static event VerboseWriteLineHandler VerboseWriteLineEvent;
public static event DebugWriteLineHandler DebugWriteLineEvent;
public static event WriteLineHandler WriteLineEvent;
public static event ErrorWriteLineHandler ErrorWriteLineEvent;
public static event VerboseWriteLineHandler VerboseWriteLineEvent;
public static event DebugWriteLineHandler DebugWriteLineEvent;
public static event DebugWithModuleWriteLineHandler DebugWithModuleWriteLineEvent;
public static event WriteHandler WriteEvent;
public static event ErrorWriteHandler ErrorWriteEvent;
@@ -82,6 +85,7 @@ namespace DiscImageChef.Console
public static void DebugWriteLine(string module, string format, params object[] arg)
{
DebugWriteLineEvent?.Invoke("DEBUG (" + module + "): " + format, arg);
DebugWithModuleWriteLineEvent?.Invoke(module, format, arg);
}
public static void WriteLine()

View File

@@ -0,0 +1,99 @@
using System;
using System.Collections.ObjectModel;
using DiscImageChef.Console;
namespace DiscImageChef.Gui
{
static class ConsoleHandler
{
static bool _debug;
static bool _verbose;
public static bool Debug
{
get => _debug;
set
{
if(_debug == value) return;
_debug = value;
if(_debug) DicConsole.DebugWithModuleWriteLineEvent += OnDebugWriteHandler;
else DicConsole.DebugWithModuleWriteLineEvent -= OnDebugWriteHandler;
}
}
public static bool Verbose
{
get => _verbose;
set
{
if(_verbose == value) return;
_verbose = value;
if(_verbose) DicConsole.VerboseWriteLineEvent += OnVerboseWriteHandler;
else DicConsole.VerboseWriteLineEvent -= OnVerboseWriteHandler;
}
}
public static ObservableCollection<LogEntry> Entries { get; } = new ObservableCollection<LogEntry>();
internal static void Init()
{
DicConsole.WriteLineEvent += OnWriteHandler;
DicConsole.ErrorWriteLineEvent += OnErrorWriteHandler;
}
static void OnWriteHandler(string format, params object[] arg)
{
Entries.Add(new LogEntry
{
Message = string.Format(format, arg),
Module = null,
Timestamp = DateTime.Now,
Type = "Info"
});
}
static void OnErrorWriteHandler(string format, params object[] arg)
{
Entries.Add(new LogEntry
{
Message = string.Format(format, arg),
Module = null,
Timestamp = DateTime.Now,
Type = "Error"
});
}
static void OnVerboseWriteHandler(string format, params object[] arg)
{
Entries.Add(new LogEntry
{
Message = string.Format(format, arg),
Module = null,
Timestamp = DateTime.Now,
Type = "Verbose"
});
}
static void OnDebugWriteHandler(string module, string format, params object[] arg)
{
Entries.Add(new LogEntry
{
Message = string.Format(format, arg),
Module = module,
Timestamp = DateTime.Now,
Type = "Debug"
});
}
}
class LogEntry
{
public string Message { get; set; }
public string Module { get; set; }
public DateTime Timestamp { get; set; }
public string Type { get; set; }
}
}

View File

@@ -6,6 +6,7 @@
<Title>DiscImageChef.Gui</Title>
<Copyright>Copyright © 2018</Copyright>
<Description>Description of DiscImageChef.Gui</Description>
<LangVersion>7.2</LangVersion>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Eto.Forms" Version="2.4.1" />

View File

@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="UTF-8"?>
<Form xmlns="http://schema.picoe.ca/eto.forms" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="Console"
ClientSize="600, 450" Padding="10">
<StackLayout Orientation="Vertical">
<StackLayoutItem HorizontalAlignment="Stretch" Expand="True" VerticalAlignment="Stretch">
<GridView ID="grdMessages"/>
</StackLayoutItem>
<StackLayoutItem HorizontalAlignment="Left" VerticalAlignment="Bottom">
<CheckBox ID="chkDebug" CheckedChanged="OnChkDebugChecked">Enable debug console</CheckBox>
</StackLayoutItem>
<StackLayoutItem HorizontalAlignment="Right" VerticalAlignment="Bottom">
<StackLayout Orientation="Horizontal">
<StackLayoutItem HorizontalAlignment="Stretch" Expand="True">
<Button ID="btnClear" Click="OnBtnClearClicked">Clear</Button>
</StackLayoutItem>
<StackLayoutItem HorizontalAlignment="Stretch" Expand="True">
<Button ID="btnSave" Click="OnBtnSaveClicked">Save</Button>
</StackLayoutItem>
</StackLayout>
</StackLayoutItem>
</StackLayout>
</Form>

View File

@@ -0,0 +1,142 @@
using System;
using System.ComponentModel;
using System.IO;
using System.Reflection;
using DiscImageChef.CommonTypes.Interop;
using DiscImageChef.Console;
using Eto.Drawing;
using Eto.Forms;
using Eto.Serialization.Xaml;
using PlatformID = DiscImageChef.CommonTypes.Interop.PlatformID;
using Version = DiscImageChef.CommonTypes.Interop.Version;
namespace DiscImageChef.Gui
{
public class frmConsole : Form
{
public frmConsole()
{
XamlReader.Load(this);
grdMessages.DataStore = ConsoleHandler.Entries;
grdMessages.Columns.Add(new GridColumn
{
DataCell = new TextBoxCell {Binding = Binding.Property<LogEntry, string>(r => $"{r.Timestamp}")},
HeaderText = "Time",
Sortable = true
});
grdMessages.Columns.Add(new GridColumn
{
DataCell = new TextBoxCell {Binding = Binding.Property<LogEntry, string>(r => r.Type)},
HeaderText = "Type",
Sortable = true
});
grdMessages.Columns.Add(new GridColumn
{
DataCell = new TextBoxCell {Binding = Binding.Property<LogEntry, string>(r => r.Module)},
HeaderText = "Module",
Sortable = true
});
grdMessages.Columns.Add(new GridColumn
{
DataCell = new TextBoxCell {Binding = Binding.Property<LogEntry, string>(r => r.Message)},
HeaderText = "Message",
Sortable = true
});
grdMessages.AllowMultipleSelection = false;
grdMessages.CellFormatting += (sender, e) =>
{
if(((LogEntry)e.Item).Type.ToLower() != "error") return;
e.BackgroundColor = Colors.Red;
e.ForegroundColor = Colors.Black;
};
grdMessages.AllowColumnReordering = true;
chkDebug.Checked = ConsoleHandler.Debug;
Closing += OnClosing;
}
void OnClosing(object sender, CancelEventArgs e)
{
// Otherwise if this closes it does not stop hearing events from collection, preventing console to keep working.
grdMessages.DataStore = null;
}
protected void OnChkDebugChecked(object sender, EventArgs e)
{
ConsoleHandler.Debug = chkDebug.Checked.Value;
}
protected void OnBtnClearClicked(object sender, EventArgs e)
{
ConsoleHandler.Entries.Clear();
}
protected void OnBtnSaveClicked(object sender, EventArgs e)
{
SaveFileDialog dlgSave = new SaveFileDialog {CheckFileExists = true};
dlgSave.Filters.Add(new FileFilter {Extensions = new[] {"log"}, Name = "Log files"});
DialogResult result = dlgSave.ShowDialog(this);
if(result != DialogResult.Ok) return;
try
{
FileStream logFs = new FileStream(dlgSave.FileName, FileMode.Create, FileAccess.ReadWrite);
StreamWriter logSw = new StreamWriter(logFs);
logSw.WriteLine("Log saved at {0}", DateTime.Now);
PlatformID platId = DetectOS.GetRealPlatformID();
string platVer = DetectOS.GetVersion();
AssemblyInformationalVersionAttribute assemblyVersion =
Attribute.GetCustomAttribute(typeof(DicConsole).Assembly,
typeof(AssemblyInformationalVersionAttribute)) as
AssemblyInformationalVersionAttribute;
logSw.WriteLine("################# System information #################");
logSw.WriteLine("{0} {1} ({2}-bit)", DetectOS.GetPlatformName(platId, platVer), platVer,
Environment.Is64BitOperatingSystem ? 64 : 32);
if(DetectOS.IsMono) logSw.WriteLine("Mono {0}", Version.GetMonoVersion());
else logSw.WriteLine(".NET Framework {0}", Environment.Version);
logSw.WriteLine();
logSw.WriteLine("################# Program information ################");
logSw.WriteLine("DiscImageChef {0}", assemblyVersion?.InformationalVersion);
logSw.WriteLine("Running in {0}-bit", Environment.Is64BitProcess ? 64 : 32);
logSw.WriteLine("Running GUI mode using {0}", Application.Instance.Platform.ID);
#if DEBUG
logSw.WriteLine("DEBUG version");
#endif
logSw.WriteLine("Command line: {0}", Environment.CommandLine);
logSw.WriteLine();
logSw.WriteLine("################# Console ################");
foreach(LogEntry entry in ConsoleHandler.Entries)
if(entry.Type != "Info")
logSw.WriteLine("{0}: ({1}) {2}", entry.Timestamp, entry.Type.ToLower(), entry.Message);
else
logSw.WriteLine("{0}: {1}", entry.Timestamp, entry.Message);
logSw.Close();
logFs.Close();
}
catch(Exception exception)
{
MessageBox.Show("Exception {0} trying to save logfile, details has been sent to console.",
exception.Message);
DicConsole.ErrorWriteLine("Console", exception.Message);
DicConsole.ErrorWriteLine("Console", exception.StackTrace);
}
}
#region XAML controls
GridView grdMessages;
CheckBox chkDebug;
Button btnClear;
Button btnSave;
#endregion
}
}

View File

@@ -12,11 +12,14 @@
<Form.Menu>
<MenuBar>
<ButtonMenuItem Text="&amp;File">
<ButtonMenuItem Text="Open" Click="OnMenuOpen"/>
<ButtonMenuItem Text="&amp;Open" Click="OnMenuOpen"/>
</ButtonMenuItem>
<ButtonMenuItem Text="&amp;Devices">
<ButtonMenuItem Text="&amp;Refresh" Click="OnDeviceRefresh" Shortcut="F5"/>
</ButtonMenuItem>
<ButtonMenuItem Text="&amp;Window">
<ButtonMenuItem Text="&amp;Console" Click="OnMenuConsole"/>
</ButtonMenuItem>
<MenuBar.ApplicationItems>
<ButtonMenuItem Text="Preferences.." Shortcut="{On Control+O, Mac=Application+Comma}"/>
</MenuBar.ApplicationItems>

View File

@@ -1,5 +1,7 @@
using System;
using System.ComponentModel;
using System.Linq;
using DiscImageChef.Console;
using DiscImageChef.Devices;
using Eto.Forms;
using Eto.Serialization.Xaml;
@@ -8,14 +10,19 @@ namespace DiscImageChef.Gui
{
public class frmMain : Form
{
bool closing;
Splitter splMain;
TreeGridView treeImages;
TreeGridItemCollection treeImagesItems;
public frmMain()
public frmMain(bool debug, bool verbose)
{
XamlReader.Load(this);
ConsoleHandler.Init();
ConsoleHandler.Debug = debug;
ConsoleHandler.Verbose = verbose;
treeImagesItems = new TreeGridItemCollection();
treeImages.Columns.Add(new GridColumn {HeaderText = "Name", DataCell = new TextBoxCell(0)});
@@ -24,11 +31,22 @@ namespace DiscImageChef.Gui
treeImages.ShowHeader = false;
treeImages.DataStore = treeImagesItems;
imagesRoot = new TreeGridItem {Values = new object[] {"Images"}};
imagesRoot = new TreeGridItem {Values = new object[] {"Images"}};
devicesRoot = new TreeGridItem {Values = new object[] {"Devices"}};
treeImagesItems.Add(imagesRoot);
treeImagesItems.Add(devicesRoot);
Closing += OnClosing;
}
void OnClosing(object sender, CancelEventArgs e)
{
// This prevents an infinite loop of crashes :p
if(closing) return;
closing = true;
Application.Instance.Quit();
}
protected void OnMenuOpen(object sender, EventArgs e)
@@ -60,11 +78,15 @@ namespace DiscImageChef.Gui
void RefreshDevices()
{
DicConsole.WriteLine("Refreshing devices");
devicesRoot.Children.Clear();
foreach(DeviceInfo device in Device.ListDevices().Where(d => d.Supported).OrderBy(d => d.Vendor)
.ThenBy(d => d.Model))
{
DicConsole.DebugWriteLine("Main window",
"Found support device model {0} by manufacturer {1} on bus {2} and path {3}",
device.Model, device.Vendor, device.Bus, device.Path);
devicesRoot.Children.Add(new TreeGridItem
{
Values = new object[] {$"{device.Vendor} {device.Model} ({device.Bus})", device.Path}
@@ -74,6 +96,11 @@ namespace DiscImageChef.Gui
treeImages.ReloadData();
}
protected void OnMenuConsole(object sender, EventArgs e)
{
new frmConsole().Show();
}
#region XAML IDs
TreeGridItem devicesRoot;
GridView grdFiles;

View File

@@ -37,6 +37,7 @@ using DiscImageChef.Commands;
using DiscImageChef.Console;
using DiscImageChef.Gui;
using DiscImageChef.Settings;
using Eto;
using Eto.Forms;
using Statistics = DiscImageChef.Core.Statistics;
@@ -208,9 +209,10 @@ namespace DiscImageChef
Commands.Statistics.ShowStats();
}).WithParsed<GuiOptions>(opts =>
{
if (opts.Debug) DicConsole.DebugWriteLineEvent += System.Console.Error.WriteLine;
if (opts.Verbose) DicConsole.VerboseWriteLineEvent += System.Console.WriteLine;
new Application(Eto.Platform.Detect).Run(new frmMain());
if(opts.Debug) DicConsole.DebugWriteLineEvent += System.Console.Error.WriteLine;
if(opts.Verbose) DicConsole.VerboseWriteLineEvent += System.Console.WriteLine;
new Application(Platform.Detect).Run(new frmMain(opts.Debug, opts.Verbose));
}).WithNotParsed(errs => Environment.Exit(1));
Statistics.SaveStats();