diff --git a/src/Covid19Radar.LogViewer.Globalization.English/English.cs b/src/Covid19Radar.LogViewer.Globalization.English/English.cs index 5f8ea0d..c063ce4 100644 --- a/src/Covid19Radar.LogViewer.Globalization.English/English.cs +++ b/src/Covid19Radar.LogViewer.Globalization.English/English.cs @@ -43,6 +43,7 @@ public class English : LanguageData public override string ControllerView_Copy => "Copy selected logs"; public override string ControllerView_CopyAsMarkdown => "Copy as Markdown"; public override string ControllerView_Copy_MessageBox => "Copied details of selected logs into the clipboard."; + public override string ControllerView_Save => "Save to other location"; public override string LogFileView_MessageBox_Title => "Open a log file"; public override string LogFileView_MessageBox_Failed => "An unexpected error occurred while opening a log file."; public override string LogFileModel_InvalidLog_Short => "INVALID LOG"; diff --git a/src/Covid19Radar.LogViewer.Launcher/Covid19Radar.LogViewer.Launcher.csproj b/src/Covid19Radar.LogViewer.Launcher/Covid19Radar.LogViewer.Launcher.csproj index b72a2fe..40d3131 100644 --- a/src/Covid19Radar.LogViewer.Launcher/Covid19Radar.LogViewer.Launcher.csproj +++ b/src/Covid19Radar.LogViewer.Launcher/Covid19Radar.LogViewer.Launcher.csproj @@ -9,6 +9,12 @@ WinExe + + Covid19Radar.LogViewer.Launcher.Program.DebugEnvironment + + + Covid19Radar.LogViewer.Launcher.Program + diff --git a/src/Covid19Radar.LogViewer.Launcher/Extensibility/PluginMenuItem.cs b/src/Covid19Radar.LogViewer.Launcher/Extensibility/PluginMenuItem.cs index 7a0b19f..a9940dd 100644 --- a/src/Covid19Radar.LogViewer.Launcher/Extensibility/PluginMenuItem.cs +++ b/src/Covid19Radar.LogViewer.Launcher/Extensibility/PluginMenuItem.cs @@ -28,7 +28,9 @@ internal PluginMenuItem(FormMain mwnd, IPlugin plugin) : base( if (plugin.GetChildPlugins() is not null and var plugins) { foreach (var childPlugin in plugins) { - this.DropDownItems.Add(new PluginMenuItem(mwnd, childPlugin)); + if (childPlugin.Visible) { + this.DropDownItems.Add(new PluginMenuItem(mwnd, childPlugin)); + } } } diff --git a/src/Covid19Radar.LogViewer.Launcher/FormReceiver.cs b/src/Covid19Radar.LogViewer.Launcher/FormReceiver.cs index 6b94678..1dfe29f 100644 --- a/src/Covid19Radar.LogViewer.Launcher/FormReceiver.cs +++ b/src/Covid19Radar.LogViewer.Launcher/FormReceiver.cs @@ -52,11 +52,15 @@ private async void FormReceiver_Load(object sender, EventArgs e) bool allowEscape; while (!_cts.IsCancellationRequested) { - string temp = Path.GetTempFileName(); - byte[] buf = new byte[1024]; + string temp = Path.GetTempFileName(); + IPAddress? ipa = null; + byte[] buf = new byte[1024]; using (var client = await Task.Run(listener.AcceptTcpClientAsync, _cts.Token)) { var ns = client.GetStream(); + if (client.Client.RemoteEndPoint is IPEndPoint ep) { + ipa = ep.Address; + } await using (ns.ConfigureAwait(false)) { using (var br = new BinaryReader(ns)) { while (!ns.DataAvailable) { @@ -80,6 +84,7 @@ private async void FormReceiver_Load(object sender, EventArgs e) } } + await SaveZoneIdentifier(temp, ipa); await _owner.OpenFileAsync(temp, allowEscape); } } finally { @@ -97,5 +102,21 @@ private static async ValueTask GetLocalIPAddress() var addrs = await Dns.GetHostAddressesAsync(Dns.GetHostName()); return addrs.Length > 0 ? addrs[0] : IPAddress.Loopback; } + + private static async ValueTask SaveZoneIdentifier(string filename, IPAddress? ipa) + { + var fs = new FileStream(filename + ":Zone.Identifier", FileMode.Create, FileAccess.Write, FileShare.None); + await using (fs.ConfigureAwait(false)) { + var sw = new StreamWriter(fs); + await using (sw.ConfigureAwait(false)) { + await sw.WriteLineAsync("[ZoneTransfer]"); + await sw.WriteLineAsync("ZoneId=3"); + if (ipa is not null) { + await sw.WriteLineAsync($"HostIpAddress={ipa}"); + } + await sw.WriteLineAsync("CocoaLogViewer=FormReceiver"); + } + } + } } } diff --git a/src/Covid19Radar.LogViewer.Launcher/Program.cs b/src/Covid19Radar.LogViewer.Launcher/Program.cs index 718d86b..ae18881 100644 --- a/src/Covid19Radar.LogViewer.Launcher/Program.cs +++ b/src/Covid19Radar.LogViewer.Launcher/Program.cs @@ -10,6 +10,7 @@ using System.Collections.Generic; using System.Diagnostics; using System.IO; +using System.Runtime.CompilerServices; using System.Text; using System.Threading; using System.Windows.Forms; @@ -25,9 +26,10 @@ internal static class Program private static int Main(string[] args) { try { - var context = new ModuleInitializationContextInternal(args); - var modules = ModuleLoader.LoadModules(context); - ShowWindow(modules, context); + AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException; + Application.ThreadException += Application_ThreadException; + + Run(args); return 0; } catch (Exception e) { HandleException(e); @@ -35,15 +37,31 @@ private static int Main(string[] args) } } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static void Run(string[] args) + { + var context = new ModuleInitializationContextInternal(args); + var modules = ModuleLoader.LoadModules(context); + ShowWindow(modules, context); + } + private static void ShowWindow(IEnumerable modules, ModuleInitializationContext context) { - Application.ThreadException += Application_ThreadException; Application.SetHighDpiMode(HighDpiMode.SystemAware); Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault(false); Application.Run(new FormMain(modules, context)); } + private static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e) + { + if (e.ExceptionObject is Exception exception) { + HandleException(exception); + } else { + HandleException(new Exception((sender, e).ToString())); + } + } + private static void Application_ThreadException(object sender, ThreadExceptionEventArgs e) { HandleException(e.Exception); @@ -70,5 +88,17 @@ private static void HandleException(Exception exception) sw.WriteLine(); } } + +#if DEBUG + private static class DebugEnvironment + { + [STAThread()] + [Conditional("DEBUG")] + private static void Main(string[] args) + { + Run(args); + } + } +#endif } } diff --git a/src/Covid19Radar.LogViewer.Transformers.Configuration/TransformerSwitcher.cs b/src/Covid19Radar.LogViewer.Transformers.Configuration/TransformerSwitcher.cs index 3b11dd2..451174b 100644 --- a/src/Covid19Radar.LogViewer.Transformers.Configuration/TransformerSwitcher.cs +++ b/src/Covid19Radar.LogViewer.Transformers.Configuration/TransformerSwitcher.cs @@ -19,6 +19,7 @@ public sealed class TransformerSwitcher : ILauncherFeature private readonly ModuleInitializationContext _context; private TransformerPipeline? _default; public string? DisplayName => Resources.TransformerSwitcher_DisplayName; + public bool Visible => true; public bool IsChecked => _context.TransformerPipeline == EmptyTransformerPipeline.Instance; public TransformerSwitcher(ModuleInitializationContext moduleInitializationContext) diff --git a/src/Covid19Radar.LogViewer/Extensibility/CocoaLogViewerModule.cs b/src/Covid19Radar.LogViewer/Extensibility/CocoaLogViewerModule.cs index fb53781..2b85510 100644 --- a/src/Covid19Radar.LogViewer/Extensibility/CocoaLogViewerModule.cs +++ b/src/Covid19Radar.LogViewer/Extensibility/CocoaLogViewerModule.cs @@ -17,6 +17,9 @@ public abstract class CocoaLogViewerModule : IPlugin public virtual string? DisplayName => this.GetType().Assembly.FullName; public virtual Image? Logo => null; + // 必ずメニューに表示する。(モジュールの Visible は無視される) + bool IPlugin.Visible => true; + public void Initialize(ModuleInitializationContext context) { if (context is null) { diff --git a/src/Covid19Radar.LogViewer/Extensibility/IPlugin.cs b/src/Covid19Radar.LogViewer/Extensibility/IPlugin.cs index 3447ad4..963639a 100644 --- a/src/Covid19Radar.LogViewer/Extensibility/IPlugin.cs +++ b/src/Covid19Radar.LogViewer/Extensibility/IPlugin.cs @@ -13,6 +13,7 @@ namespace Covid19Radar.LogViewer.Extensibility public interface IPlugin { public string? DisplayName { get; } + public bool Visible { get; } public IEnumerable? GetChildPlugins() { diff --git a/src/Covid19Radar.LogViewer/Globalization/Japanese.cs b/src/Covid19Radar.LogViewer/Globalization/Japanese.cs index 55e86ac..335ba94 100644 --- a/src/Covid19Radar.LogViewer/Globalization/Japanese.cs +++ b/src/Covid19Radar.LogViewer/Globalization/Japanese.cs @@ -43,6 +43,7 @@ public class Japanese : LanguageData public override string ControllerView_Copy => "選択範囲を一括コピー"; public override string ControllerView_CopyAsMarkdown => "Markdownとしてコピー"; public override string ControllerView_Copy_MessageBox => "クリップボードに選択されたログの詳細情報をコピーしました。"; + public override string ControllerView_Save => "別の場所に保存"; public override string LogFileView_MessageBox_Title => "動作情報ファイルを開く"; public override string LogFileView_MessageBox_Failed => "動作情報ファイルの読み込み中に予期せぬエラーが発生しました。"; public override string LogFileModel_InvalidLog_Short => "無効なログ"; diff --git a/src/Covid19Radar.LogViewer/Globalization/LanguageData.cs b/src/Covid19Radar.LogViewer/Globalization/LanguageData.cs index 086b658..db0da5f 100644 --- a/src/Covid19Radar.LogViewer/Globalization/LanguageData.cs +++ b/src/Covid19Radar.LogViewer/Globalization/LanguageData.cs @@ -109,6 +109,7 @@ public virtual string MainWindow_OFD_Filter() public abstract string ControllerView_Copy { get; } public abstract string ControllerView_CopyAsMarkdown { get; } public abstract string ControllerView_Copy_MessageBox { get; } + public abstract string ControllerView_Save { get; } public abstract string ControllerView_Refresh_Failed(MainWindow? mwnd); diff --git a/src/Covid19Radar.LogViewer/ViewModels/ControllerViewModel.cs b/src/Covid19Radar.LogViewer/ViewModels/ControllerViewModel.cs index 687df63..04d9185 100644 --- a/src/Covid19Radar.LogViewer/ViewModels/ControllerViewModel.cs +++ b/src/Covid19Radar.LogViewer/ViewModels/ControllerViewModel.cs @@ -7,20 +7,30 @@ ****/ using System; +using System.IO; using System.Text; using System.Threading.Tasks; using System.Windows; -using System.Windows.Controls; +using System.Windows.Forms; using Covid19Radar.LogViewer.Globalization; using Covid19Radar.LogViewer.Models; using Covid19Radar.LogViewer.Views; +using Clipboard = System.Windows.Clipboard; +using ListView = System.Windows.Controls.ListView; namespace Covid19Radar.LogViewer.ViewModels { public class ControllerViewModel : ViewModelBase { + private MainWindow? _mwnd; private LogFileView? _log_file_view; + public MainWindow? MainWindow + { + get => _mwnd; + set => this.RaisePropertyChanged(ref _mwnd, value, nameof(this.MainWindow)); + } + public LogFileView? LogFileView { get => _log_file_view; @@ -32,12 +42,14 @@ public LogFileView? LogFileView public DelegateCommand Refresh { get; } public DelegateCommand ClickCopy { get; } public DelegateCommand ClickCopyAsMarkdown { get; } + public DelegateCommand ClickSave { get; } public ControllerViewModel() { this.Refresh = new(this.RefreshCore); this.ClickCopy = new(this.ClickCopyCore); this.ClickCopyAsMarkdown = new(this.ClickCopyAsMarkdownCore); + this.ClickSave = new(this.ClickSaveCore); } private async ValueTask RefreshCore(object? ignored) @@ -74,6 +86,34 @@ private ValueTask ClickCopyAsMarkdownCore(object? ignored) return default; } + private ValueTask ClickSaveCore(object? ignored) + { + if (_mwnd is not null && _mwnd.FilePath is not null and var path) { + using (var sfd = new SaveFileDialog() { + Title = LanguageData.Current.ControllerView_Save, + Filter = LanguageData.Current.MainWindow_OFD_Filter(), + RestoreDirectory = true, + DereferenceLinks = true, + AddExtension = false, + SupportMultiDottedExtensions = true, + CheckPathExists = false, + CheckFileExists = false, + ValidateNames = true, + AutoUpgradeEnabled = true, + OverwritePrompt = true + }) { + if (sfd.ShowDialog() == DialogResult.OK) { + string? dir = Path.GetDirectoryName(path); + if (dir is not null && !Directory.Exists(dir)) { + Directory.CreateDirectory(dir); + } + File.Copy(path, sfd.FileName, true); + } + } + } + return default; + } + private static void ForAllLogData(StringBuilder sb, ListView listView, Action action) { var items = listView.SelectedItems; diff --git a/src/Covid19Radar.LogViewer/Views/ControllerView.xaml b/src/Covid19Radar.LogViewer/Views/ControllerView.xaml index ffb0377..fcf9b2a 100644 --- a/src/Covid19Radar.LogViewer/Views/ControllerView.xaml +++ b/src/Covid19Radar.LogViewer/Views/ControllerView.xaml @@ -16,6 +16,7 @@ + @@ -47,5 +48,12 @@ d:Content="copyAsMd" Margin="4,4,4,4" Command="{Binding ClickCopyAsMarkdown}" /> + +