diff --git a/.editorconfig b/.editorconfig index 0a0e808bb..585e7fdd4 100644 --- a/.editorconfig +++ b/.editorconfig @@ -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 diff --git a/.idea/.idea.DiscImageChef/.idea/contentModel.xml b/.idea/.idea.DiscImageChef/.idea/contentModel.xml index 24c28fc19..d52253f6b 100644 --- a/.idea/.idea.DiscImageChef/.idea/contentModel.xml +++ b/.idea/.idea.DiscImageChef/.idea/contentModel.xml @@ -56,6 +56,7 @@ + @@ -1247,8 +1248,11 @@ + + + diff --git a/DiscImageChef.Console/DicConsole.cs b/DiscImageChef.Console/DicConsole.cs index 3c0c82be8..5fdabb613 100644 --- a/DiscImageChef.Console/DicConsole.cs +++ b/DiscImageChef.Console/DicConsole.cs @@ -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); + /// /// Implements a console abstraction that defines four level of messages that can be routed to different consoles: /// standard, error, verbose and debug. /// 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() diff --git a/DiscImageChef.Gui/ConsoleHandler.cs b/DiscImageChef.Gui/ConsoleHandler.cs new file mode 100644 index 000000000..27262fdd5 --- /dev/null +++ b/DiscImageChef.Gui/ConsoleHandler.cs @@ -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 Entries { get; } = new ObservableCollection(); + + 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; } + } +} \ No newline at end of file diff --git a/DiscImageChef.Gui/DiscImageChef.Gui.csproj b/DiscImageChef.Gui/DiscImageChef.Gui.csproj index d00e57abe..85e732a52 100644 --- a/DiscImageChef.Gui/DiscImageChef.Gui.csproj +++ b/DiscImageChef.Gui/DiscImageChef.Gui.csproj @@ -6,6 +6,7 @@ DiscImageChef.Gui Copyright © 2018 Description of DiscImageChef.Gui + 7.2 diff --git a/DiscImageChef.Gui/frmConsole.xeto b/DiscImageChef.Gui/frmConsole.xeto new file mode 100644 index 000000000..b3715b17d --- /dev/null +++ b/DiscImageChef.Gui/frmConsole.xeto @@ -0,0 +1,22 @@ + +
+ + + + + + Enable debug console + + + + + + + + + + + + +
\ No newline at end of file diff --git a/DiscImageChef.Gui/frmConsole.xeto.cs b/DiscImageChef.Gui/frmConsole.xeto.cs new file mode 100644 index 000000000..d897d6d97 --- /dev/null +++ b/DiscImageChef.Gui/frmConsole.xeto.cs @@ -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(r => $"{r.Timestamp}")}, + HeaderText = "Time", + Sortable = true + }); + grdMessages.Columns.Add(new GridColumn + { + DataCell = new TextBoxCell {Binding = Binding.Property(r => r.Type)}, + HeaderText = "Type", + Sortable = true + }); + grdMessages.Columns.Add(new GridColumn + { + DataCell = new TextBoxCell {Binding = Binding.Property(r => r.Module)}, + HeaderText = "Module", + Sortable = true + }); + grdMessages.Columns.Add(new GridColumn + { + DataCell = new TextBoxCell {Binding = Binding.Property(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 + } +} \ No newline at end of file diff --git a/DiscImageChef.Gui/frmMain.xeto b/DiscImageChef.Gui/frmMain.xeto index 8fb6dcfd4..1103534ea 100644 --- a/DiscImageChef.Gui/frmMain.xeto +++ b/DiscImageChef.Gui/frmMain.xeto @@ -12,11 +12,14 @@ - + + + + diff --git a/DiscImageChef.Gui/frmMain.xeto.cs b/DiscImageChef.Gui/frmMain.xeto.cs index d39575ba6..66cf17637 100644 --- a/DiscImageChef.Gui/frmMain.xeto.cs +++ b/DiscImageChef.Gui/frmMain.xeto.cs @@ -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; diff --git a/DiscImageChef/Main.cs b/DiscImageChef/Main.cs index 80d2c3a4a..5f523b0e3 100644 --- a/DiscImageChef/Main.cs +++ b/DiscImageChef/Main.cs @@ -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(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();