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}" />
+
+
diff --git a/src/Covid19Radar.LogViewer/Views/ControllerView.xaml.cs b/src/Covid19Radar.LogViewer/Views/ControllerView.xaml.cs
index ef13291..e51ed09 100644
--- a/src/Covid19Radar.LogViewer/Views/ControllerView.xaml.cs
+++ b/src/Covid19Radar.LogViewer/Views/ControllerView.xaml.cs
@@ -16,6 +16,12 @@ public partial class ControllerView : UserControl
{
private readonly ControllerViewModel _view_model;
+ public MainWindow? MainWindow
+ {
+ get => _view_model.MainWindow;
+ set => _view_model.MainWindow = value;
+ }
+
public LogFileView? LogFileView
{
get => _view_model.LogFileView;
@@ -29,6 +35,7 @@ public ControllerView()
refresh .Content = LanguageData.Current.ControllerView_Refresh;
copy .Content = LanguageData.Current.ControllerView_Copy;
copyAsMd.Content = LanguageData.Current.ControllerView_CopyAsMarkdown;
+ save .Content = LanguageData.Current.ControllerView_Save;
}
}
}
diff --git a/src/Covid19Radar.LogViewer/Views/MainWindow.xaml.cs b/src/Covid19Radar.LogViewer/Views/MainWindow.xaml.cs
index 2befc34..be540e4 100644
--- a/src/Covid19Radar.LogViewer/Views/MainWindow.xaml.cs
+++ b/src/Covid19Radar.LogViewer/Views/MainWindow.xaml.cs
@@ -25,6 +25,9 @@ public partial class MainWindow : Window
private static readonly TransformerPipeline _default = new TransformerPipeline().ConfigureDefaults();
private readonly Func _transformer;
private bool _file_loaded;
+ private string? _file_path;
+
+ public string? FilePath => _file_path;
public MainWindow() : this(_default) { }
@@ -35,6 +38,7 @@ public MainWindow(TransformerPipeline transformerPipeline)
this.Title = LanguageData.Current.MainWindow_Title;
btnOpen .Content = LanguageData.Current.MainWindow_ButtonOpen;
lblVersion.Content = $"{VersionInfo.GetCaption()}\t{VersionInfo.GetCopyright()}";
+ controller.MainWindow = this;
controller.LogFileView = lfv;
}
@@ -77,7 +81,10 @@ private async ValueTask OpenFile(Func<(string path, Func open)?> f
try {
var f = file();
if (f.HasValue) {
- await this.Dispatcher.InvokeAsync(() => this.Title = Path.GetFileName(f.Value.path));
+ await this.Dispatcher.InvokeAsync(() => {
+ _file_path = f.Value.path;
+ this.Title = Path.GetFileName(_file_path);
+ });
lfv.ViewModel.LogFile = await Task.Run(() => new LogFileModel(f.Value.open(), _transformer, allowEscape)).ConfigureAwait(false);
await this.Dispatcher.InvokeAsync(() => {
btnOpen.Visibility = Visibility.Collapsed;