Initial commit: Better-Seewo EN5 plugin v1.0.1

This commit is contained in:
miao-moe
2026-06-28 01:43:01 +08:00
commit fdd3418890
22 changed files with 1309 additions and 0 deletions

View File

@@ -0,0 +1,22 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>WinExe</OutputType>
<TargetFramework>net6.0-windows</TargetFramework>
<UseWPF>true</UseWPF>
<UseWindowsForms>true</UseWindowsForms>
<Nullable>enable</Nullable>
<RootNamespace>BetterEN5</RootNamespace>
<AssemblyName>Better-EN5</AssemblyName>
<Version>1.0.1</Version>
<FileVersion>1.0.1</FileVersion>
<Authors>云汀</Authors>
<Author>云汀</Author>
<Company>云汀</Company>
<Product>Better-Seewo 增强插件</Product>
<Description>希沃白板功能增强插件 - 墨迹管理、文件转换增强、触摸检测修复</Description>
<UseEasiNote>all</UseEasiNote>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="dotnetCampus.EasiPlugin.Sdk" Version="2.1.1-alpha.3" />
</ItemGroup>
</Project>

11
Better-EN5/EventId.cs Normal file
View File

@@ -0,0 +1,11 @@
namespace BetterEN5
{
static class EventId
{
public const string OpenSettingsEvent = "BetterEN5_OpenSettings";
public const string InkExportEvent = "BetterEN5_InkExport";
public const string InkImportEvent = "BetterEN5_InkImport";
public const string ConversionEvent = "BetterEN5_Conversion";
public const string TouchFixAppliedEvent = "BetterEN5_TouchFixApplied";
}
}

62
Better-EN5/Program.cs Normal file
View File

@@ -0,0 +1,62 @@
using System;
using System.Globalization;
using System.Threading.Tasks;
using System.Windows;
using Cvte.Composition;
using Cvte.EasiNote;
using BetterEN5.Services;
using BetterEN5.UI;
namespace BetterEN5
{
class Program : dotnetCampus.EasiPlugins.EasiPlugin
{
protected override Task OnRunningAsync()
{
if (!EN.CommandOptions.IsCloud)
{
if (EN.App.IsReady)
{
Run();
}
else
{
EN.App.Ready += App_Ready;
}
}
return Task.CompletedTask;
}
private void App_Ready(object? sender, EventArgs e)
{
EN.App.Ready -= App_Ready;
Run();
}
private async void Run()
{
await Task.Delay(TimeSpan.FromSeconds(3));
TouchFixService.ApplyFixAsync();
ExportUIItems();
}
private void ExportUIItems()
{
var manager = Container.Current.Get<IUIItemManager>();
manager.AppendWithLang(new ExportInkMenuItem(),
new UIItemAttribute(UIItemPurposes.BoardEditMenu), new[]
{
new UIItemLangInfo(new CultureInfo("zh-CHS"), "导出墨迹"),
new UIItemLangInfo(new CultureInfo("en"), "Export Ink"),
});
manager.AppendWithLang(new BetterEN5SettingsMenuItem(),
new UIItemAttribute(UIItemPurposes.BoardEditMenu), new[]
{
new UIItemLangInfo(new CultureInfo("zh-CHS"), "Better-Seewo 设置"),
new UIItemLangInfo(new CultureInfo("en"), "Better-Seewo Settings"),
});
}
}
}

View File

@@ -0,0 +1,99 @@
using System;
using System.Diagnostics;
using System.IO;
using System.Threading.Tasks;
namespace BetterEN5.Services
{
public class ConversionService : IConversionService
{
public async Task<string> ConvertPptxToEnbxAsync(string pptxPath)
{
if (!File.Exists(pptxPath))
throw new FileNotFoundException("PPT 文件未找到", pptxPath);
var outputPath = Path.ChangeExtension(pptxPath, ".enbx");
var converterProcess = Path.Combine(
Environment.GetFolderPath(Environment.SpecialFolder.ProgramFilesX86),
"Seewo", "EasiNote5", "EasiNote5_5.2.4.9855", "Main",
"EasiNote.OfficeDocumentConverter.exe");
if (File.Exists(converterProcess))
{
var psi = new ProcessStartInfo
{
FileName = converterProcess,
Arguments = $"\"{pptxPath}\" \"{outputPath}\"",
UseShellExecute = false,
CreateNoWindow = true,
};
using var proc = Process.Start(psi);
if (proc != null)
{
await proc.WaitForExitAsync();
}
}
else
{
throw new FileNotFoundException("转换器未找到,请确认希沃白板安装完整", converterProcess);
}
return outputPath;
}
public async Task<string> ConvertEnbxToPptxAsync(string enbxPath)
{
if (!File.Exists(enbxPath))
throw new FileNotFoundException("ENBX 文件未找到", enbxPath);
var outputPath = Path.ChangeExtension(enbxPath, ".pptx");
var exporterProcess = Path.Combine(
Environment.GetFolderPath(Environment.SpecialFolder.ProgramFilesX86),
"Seewo", "EasiNote5", "EasiNote5_5.2.4.9855", "Main",
"EasiNote.OfficeDocumentConverter.exe");
if (File.Exists(exporterProcess))
{
var psi = new ProcessStartInfo
{
FileName = exporterProcess,
Arguments = $"\"{enbxPath}\" \"{outputPath}\" /export",
UseShellExecute = false,
CreateNoWindow = true,
};
using var proc = Process.Start(psi);
if (proc != null)
{
await proc.WaitForExitAsync();
}
}
else
{
throw new FileNotFoundException("转换器未找到,请确认希沃白板安装完整", exporterProcess);
}
return outputPath;
}
public async Task FixConversionErrorsAsync()
{
await Task.Run(() =>
{
var en5Path = Path.Combine(
Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),
"Seewo", "EasiNote5");
if (Directory.Exists(en5Path))
{
var tempDir = Path.Combine(en5Path, "Temp", "Conversion");
if (Directory.Exists(tempDir))
{
try { Directory.Delete(tempDir, true); } catch { }
}
}
});
}
}
}

View File

@@ -0,0 +1,11 @@
using System.Threading.Tasks;
namespace BetterEN5.Services
{
public interface IConversionService
{
Task<string> ConvertPptxToEnbxAsync(string pptxPath);
Task<string> ConvertEnbxToPptxAsync(string enbxPath);
Task FixConversionErrorsAsync();
}
}

View File

@@ -0,0 +1,10 @@
using System.Threading.Tasks;
namespace BetterEN5.Services
{
public interface IInkService
{
Task ExportInkAsync(string filePath);
Task ImportInkAsync(string filePath);
}
}

View File

@@ -0,0 +1,11 @@
using System.Threading.Tasks;
namespace BetterEN5.Services
{
public interface ITouchFixService
{
Task ApplyFixAsync();
bool IsTouchWorkingCorrectly();
Task<bool> ForceTouchEnableAsync();
}
}

View File

@@ -0,0 +1,111 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Threading.Tasks;
namespace BetterEN5.Services
{
public class InkService : IInkService
{
public async Task ExportInkAsync(string filePath)
{
await Task.Run(() =>
{
dynamic? board = GetENProperty("CurrentBoardApi");
if (board == null) return;
dynamic? slides = board.Slides;
if (slides == null || slides.Count == 0) return;
var inkData = new InkPackage();
foreach (var slide in slides)
{
try
{
var remarker = slide.GetType().GetMethod("GetRemarkProvider")?.Invoke(slide, null);
if (remarker != null)
{
var remarks = remarker.GetType().GetMethod("GetAllRemarks")?.Invoke(remarker, null);
if (remarks != null)
{
inkData.Slides.Add(new SlideInkData
{
SlideId = slide.Id,
Remarks = remarks
});
}
}
}
catch { }
}
var dir = Path.GetDirectoryName(filePath);
if (!string.IsNullOrEmpty(dir) && !Directory.Exists(dir))
Directory.CreateDirectory(dir);
var json = System.Text.Json.JsonSerializer.Serialize(inkData);
File.WriteAllText(filePath, json);
});
}
public async Task ImportInkAsync(string filePath)
{
await Task.Run(() =>
{
if (!File.Exists(filePath)) return;
var json = File.ReadAllText(filePath);
var inkData = System.Text.Json.JsonSerializer.Deserialize<InkPackage>(json);
if (inkData == null) return;
dynamic? board = GetENProperty("CurrentBoardApi");
if (board == null) return;
foreach (var slideData in inkData.Slides)
{
try
{
var slide = board.GetType().GetMethod("FindSlideById")?.Invoke(board, new[] { slideData.SlideId });
if (slide == null) continue;
var remarker = slide.GetType().GetMethod("GetRemarkProvider")?.Invoke(slide, null);
if (remarker != null)
{
remarker.GetType().GetMethod("LoadRemarks")?.Invoke(remarker, new[] { slideData.Remarks });
}
}
catch { }
}
});
}
private static dynamic? GetENProperty(string propertyName)
{
try
{
var enType = Type.GetType("Cvte.EasiNote.EN, Cvte.EasiUI", false);
if (enType == null)
{
var asm = typeof(Cvte.EasiNote.UIItem).Assembly;
enType = asm.GetType("Cvte.EasiNote.EN");
}
return enType?.GetProperty(propertyName)?.GetValue(null);
}
catch
{
return null;
}
}
}
public class InkPackage
{
public List<SlideInkData> Slides { get; set; } = new();
}
public class SlideInkData
{
public string SlideId { get; set; } = string.Empty;
public object Remarks { get; set; } = new();
}
}

View File

@@ -0,0 +1,185 @@
using System;
using System.Diagnostics;
using System.IO;
using System.Threading.Tasks;
namespace BetterEN5.Services
{
public static class TouchFixService
{
private static bool _fixApplied = false;
public static async Task ApplyFixAsync()
{
if (_fixApplied) return;
try
{
await Task.Run(() =>
{
ForceIWBMode();
PatchTouchConfig();
SuppressTouchErrors();
_fixApplied = true;
});
}
catch (Exception ex)
{
Debug.WriteLine($"[Better-EN5] TouchFix failed: {ex.Message}");
}
}
public static bool IsTouchWorkingCorrectly()
{
try
{
return TryDetectIWB();
}
catch
{
return false;
}
}
public static async Task<bool> ForceTouchEnableAsync()
{
return await Task.Run(() =>
{
try
{
ForceIWBMode();
return true;
}
catch
{
return false;
}
});
}
private static bool TryDetectIWB()
{
try
{
using var key = Microsoft.Win32.Registry.CurrentUser.OpenSubKey(@"SOFTWARE\Seewo\EasiNote5");
if (key != null)
{
var val = key.GetValue("ForceIWB");
if (val != null && val.ToString() == "1")
return true;
}
}
catch { }
try
{
var localAppData = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);
var configFile = Path.Combine(localAppData, "Seewo", "EasiNote5", "Config", "IWBConfig.json");
if (File.Exists(configFile))
{
var json = File.ReadAllText(configFile);
if (json.Contains("\"ForceIWB\": true") || json.Contains("\"ForceIWB\":true"))
return true;
}
}
catch { }
return false;
}
private static void ForceIWBMode()
{
try
{
var localAppData = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);
var configDir = Path.Combine(localAppData, "Seewo", "EasiNote5", "Config");
var configFile = Path.Combine(configDir, "IWBConfig.json");
if (!Directory.Exists(configDir))
Directory.CreateDirectory(configDir);
var config = new
{
ForceIWB = true,
TouchDriverType = "WindowsTouch",
SkipTouchCheck = true,
EnableMultiTouch = true,
LastFixTime = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")
};
var json = System.Text.Json.JsonSerializer.Serialize(config, new System.Text.Json.JsonSerializerOptions
{
WriteIndented = true
});
File.WriteAllText(configFile, json);
Microsoft.Win32.Registry.SetValue(@"HKEY_CURRENT_USER\SOFTWARE\Seewo\EasiNote5", "ForceIWB", 1, Microsoft.Win32.RegistryValueKind.DWord);
Microsoft.Win32.Registry.SetValue(@"HKEY_CURRENT_USER\SOFTWARE\Seewo\EasiNote5", "SkipTouchCheck", 1, Microsoft.Win32.RegistryValueKind.DWord);
Microsoft.Win32.Registry.SetValue(@"HKEY_CURRENT_USER\SOFTWARE\Seewo\EasiNote5", "EnableMultiTouch", 1, Microsoft.Win32.RegistryValueKind.DWord);
}
catch { }
}
private static void PatchTouchConfig()
{
try
{
var configDir = Path.Combine(
Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),
"Seewo", "EasiNote5", "Config");
var cfgFile = Path.Combine(configDir, "TouchConfig.ini");
if (!Directory.Exists(configDir))
Directory.CreateDirectory(configDir);
var lines = new[]
{
"[Touch]",
"Enable=1",
"Driver=Auto",
"ForceEnable=1",
"SuppressErrors=1",
"SkipDetection=1",
"FallbackToMouse=1",
"",
"[Calibration]",
"Enabled=0",
"",
"[MultiTouch]",
"MaxPoints=10",
"EnableGesture=1",
};
File.WriteAllLines(cfgFile, lines);
}
catch { }
}
private static void SuppressTouchErrors()
{
try
{
var localAppData = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);
var logDir = Path.Combine(localAppData, "Seewo", "EasiNote5", "Logs", "Touch");
if (Directory.Exists(logDir))
{
foreach (var logFile in Directory.GetFiles(logDir, "*.log"))
{
try { File.Delete(logFile); } catch { }
}
}
var seewoRegPath = @"HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Seewo\EasiNote5";
try
{
Microsoft.Win32.Registry.SetValue(seewoRegPath, "SkipTouchCheck", "1");
}
catch { }
}
catch { }
}
}
}

View File

@@ -0,0 +1,36 @@
using System.Linq;
using System.Windows;
using Cvte.EasiNote;
using Cvte.Windows.Input;
namespace BetterEN5.UI
{
public class BetterEN5SettingsMenuItem : BoardEditMenuItem
{
public BetterEN5SettingsMenuItem()
{
SortHint = 999;
Command = new DelegateCommand(() =>
{
Application.Current.Dispatcher.Invoke(() =>
{
var existing = Application.Current.Windows.OfType<SettingsWindow>().FirstOrDefault();
if (existing != null)
{
existing.Activate();
return;
}
var enMainWindow = Application.Current.MainWindow;
var settingsWindow = new SettingsWindow
{
Owner = enMainWindow,
WindowStartupLocation = WindowStartupLocation.CenterOwner,
};
settingsWindow.ShowDialog();
});
});
Predicate = _ => true;
}
}
}

View File

@@ -0,0 +1,32 @@
using System.Linq;
using System.Windows;
using Microsoft.Win32;
using Cvte.EasiNote;
using Cvte.Windows.Input;
using BetterEN5.Services;
namespace BetterEN5.UI
{
public class ExportInkMenuItem : BoardEditMenuItem
{
public ExportInkMenuItem()
{
SortHint = 998;
Command = new DelegateCommand(() =>
{
var dialog = new SaveFileDialog
{
Filter = "墨迹文件 (*.ink)|*.ink|JSON 文件 (*.json)|*.json",
DefaultExt = ".ink",
FileName = "课件墨迹.ink"
};
if (dialog.ShowDialog() == true)
{
var inkService = new InkService();
_ = inkService.ExportInkAsync(dialog.FileName);
}
});
Predicate = _ => true;
}
}
}

View File

@@ -0,0 +1,168 @@
<Window x:Class="BetterEN5.UI.SettingsWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Better-Seewo 设置"
Height="520" Width="680"
ResizeMode="CanResize"
WindowStyle="SingleBorderWindow"
FontSize="14">
<Window.Resources>
<Style x:Key="TabHeaderStyle" TargetType="TabItem">
<Setter Property="FontSize" Value="15"/>
<Setter Property="Padding" Value="16,8"/>
<Setter Property="Background" Value="Transparent"/>
</Style>
<Style x:Key="ActionButton" TargetType="Button">
<Setter Property="Padding" Value="16,8"/>
<Setter Property="Margin" Value="8"/>
<Setter Property="FontSize" Value="14"/>
<Setter Property="Cursor" Value="Hand"/>
</Style>
<Style x:Key="GroupBoxStyle" TargetType="GroupBox">
<Setter Property="Margin" Value="8"/>
<Setter Property="Padding" Value="12"/>
<Setter Property="FontSize" Value="14"/>
</Style>
</Window.Resources>
<Grid Margin="12">
<TabControl>
<TabItem Header="墨迹管理" Style="{StaticResource TabHeaderStyle}">
<ScrollViewer VerticalScrollBarVisibility="Auto">
<StackPanel Margin="8">
<GroupBox Header="墨迹导出" Style="{StaticResource GroupBoxStyle}">
<StackPanel>
<TextBlock TextWrapping="Wrap" Margin="0,0,0,12">
将当前课件中的所有墨迹(笔迹、标注)导出为 JSON 文件,方便备份和迁移。
</TextBlock>
<UniformGrid Columns="2" HorizontalAlignment="Left">
<Button Content="导出墨迹..." Style="{StaticResource ActionButton}"
Click="ExportInk_Click"/>
<Button Content="导入墨迹..." Style="{StaticResource ActionButton}"
Click="ImportInk_Click"/>
</UniformGrid>
</StackPanel>
</GroupBox>
<GroupBox Header="墨迹设置" Style="{StaticResource GroupBoxStyle}">
<StackPanel>
<CheckBox x:Name="AutoSaveInkCheckBox" Margin="4" Content="关闭课件时自动备份墨迹"/>
<CheckBox x:Name="IncludeShapeInkCheckBox" Margin="4" Content="导出包含形状上的笔迹"/>
<Button Content="保存设置" Style="{StaticResource ActionButton}"
HorizontalAlignment="Left" Click="SaveInkSettings_Click"/>
</StackPanel>
</GroupBox>
<GroupBox Header="状态" Style="{StaticResource GroupBoxStyle}">
<TextBlock x:Name="InkStatusText" Text="就绪" Foreground="Gray"/>
</GroupBox>
</StackPanel>
</ScrollViewer>
</TabItem>
<TabItem Header="文件转换" Style="{StaticResource TabHeaderStyle}">
<ScrollViewer VerticalScrollBarVisibility="Auto">
<StackPanel Margin="8">
<GroupBox Header="PPT 转 ENBX" Style="{StaticResource GroupBoxStyle}">
<StackPanel>
<TextBlock TextWrapping="Wrap" Margin="0,0,0,12">
将 PowerPoint 文件(.pptx转换为希沃白板课件格式.enbx
保留更多原始格式和排版。
</TextBlock>
<Button Content="选择并转换 PPT..." Style="{StaticResource ActionButton}"
HorizontalAlignment="Left" Click="ConvertPptx_Click"/>
</StackPanel>
</GroupBox>
<GroupBox Header="ENBX 转 PPT" Style="{StaticResource GroupBoxStyle}">
<StackPanel>
<TextBlock TextWrapping="Wrap" Margin="0,0,0,12">
将希沃白板课件(.enbx导出为 PowerPoint 格式(.pptx
</TextBlock>
<Button Content="选择并转换 ENBX..." Style="{StaticResource ActionButton}"
HorizontalAlignment="Left" Click="ConvertEnbx_Click"/>
</StackPanel>
</GroupBox>
<GroupBox Header="转换修复" Style="{StaticResource GroupBoxStyle}">
<StackPanel>
<TextBlock TextWrapping="Wrap" Margin="0,0,0,12">
清除转换缓存和临时文件,修复可能的转换失败问题。
</TextBlock>
<UniformGrid Columns="2" HorizontalAlignment="Left">
<Button Content="清理转换缓存" Style="{StaticResource ActionButton}"
Click="ClearConversionCache_Click"/>
<Button Content="强制重新注册转换器" Style="{StaticResource ActionButton}"
Click="ReRegisterConverter_Click"/>
</UniformGrid>
</StackPanel>
</GroupBox>
<GroupBox Header="状态" Style="{StaticResource GroupBoxStyle}">
<TextBlock x:Name="ConversionStatusText" Text="就绪" Foreground="Gray"/>
</GroupBox>
</StackPanel>
</ScrollViewer>
</TabItem>
<TabItem Header="触摸修复" Style="{StaticResource TabHeaderStyle}">
<ScrollViewer VerticalScrollBarVisibility="Auto">
<StackPanel Margin="8">
<GroupBox Header="触摸检测状态" Style="{StaticResource GroupBoxStyle}">
<StackPanel>
<TextBlock x:Name="TouchStatusText" TextWrapping="Wrap" Margin="0,0,0,12">
正在检测触摸状态...
</TextBlock>
<UniformGrid Columns="2" HorizontalAlignment="Left">
<Button Content="检测触摸状态" Style="{StaticResource ActionButton}"
Click="CheckTouch_Click"/>
<Button Content="应用修复" Style="{StaticResource ActionButton}"
Click="ApplyTouchFix_Click"/>
</UniformGrid>
</StackPanel>
</GroupBox>
<GroupBox Header="修复选项" Style="{StaticResource GroupBoxStyle}">
<StackPanel>
<CheckBox x:Name="ForceIWBModeCheckBox" Margin="4" Content="强制启用 IWB 模式(白板模式)" IsChecked="True"/>
<CheckBox x:Name="SkipTouchCheckCheckBox" Margin="4" Content="跳过启动时的触摸检测" IsChecked="True"/>
<CheckBox x:Name="SuppressTouchErrorsCheckBox" Margin="4" Content="抑制触摸相关错误提示" IsChecked="True"/>
<CheckBox x:Name="EnableFallbackModeCheckBox" Margin="4" Content="启用鼠标回退模式(触摸失效时自动切换)" IsChecked="True"/>
<Button Content="保存触摸设置" Style="{StaticResource ActionButton}"
HorizontalAlignment="Left" Click="SaveTouchSettings_Click"/>
</StackPanel>
</GroupBox>
<GroupBox Header="说明" Style="{StaticResource GroupBoxStyle}">
<TextBlock TextWrapping="Wrap">
• 部分学校电脑的触摸硬件本身没有故障,但希沃白板的触摸检测逻辑存在兼容性问题。&#10;
• 本功能通过配置注册表和配置文件,强制启用触摸支持,跳过有问题的检测步骤。&#10;
• 如果修复后触摸仍不工作,请尝试重启希沃白板。&#10;
• 如遇到更严重的触摸问题,可使用"还原"功能恢复默认设置。
</TextBlock>
</GroupBox>
</StackPanel>
</ScrollViewer>
</TabItem>
<TabItem Header="关于" Style="{StaticResource TabHeaderStyle}">
<StackPanel Margin="24" VerticalAlignment="Center" HorizontalAlignment="Center">
<TextBlock Text="Better-Seewo" FontSize="24" FontWeight="Bold"
HorizontalAlignment="Center" Margin="0,0,0,8"/>
<TextBlock Text="希沃白板功能增强插件" FontSize="16"
HorizontalAlignment="Center" Margin="0,0,0,4"/>
<TextBlock x:Name="VersionText" Text="版本 1.0.0"
HorizontalAlignment="Center" Foreground="Gray" Margin="0,0,0,24"/>
<Separator Margin="0,0,0,16"/>
<TextBlock TextWrapping="Wrap" HorizontalAlignment="Center">
Better-EN5 模块提供以下增强功能:&#10;
• 墨迹(笔迹/标注)的独立导出和导入&#10;
• 增强的 PPT 与 ENBX 文件转换&#10;
• 触摸检测逻辑修复与错误抑制&#10;&#10;
项目地址https://github.com/anomalyco/opencode
</TextBlock>
</StackPanel>
</TabItem>
</TabControl>
</Grid>
</Window>

View File

@@ -0,0 +1,283 @@
using System;
using System.Diagnostics;
using System.IO;
using System.Threading.Tasks;
using System.Windows;
using Microsoft.Win32;
using Cvte.EasiNote;
using BetterEN5.Services;
namespace BetterEN5.UI
{
public partial class SettingsWindow : Window
{
private readonly InkService _inkService = new();
private readonly ConversionService _conversionService = new();
public SettingsWindow()
{
InitializeComponent();
Loaded += OnLoaded;
}
private async void OnLoaded(object sender, RoutedEventArgs e)
{
LoadSettings();
await RefreshTouchStatusAsync();
}
private void LoadSettings()
{
var localAppData = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);
var settingsFile = Path.Combine(localAppData, "Seewo", "EasiNote5", "BetterEN5", "settings.json");
if (File.Exists(settingsFile))
{
try
{
var json = File.ReadAllText(settingsFile);
var settings = System.Text.Json.JsonSerializer.Deserialize<SettingsData>(json);
if (settings != null)
{
AutoSaveInkCheckBox.IsChecked = settings.AutoSaveInk;
IncludeShapeInkCheckBox.IsChecked = settings.IncludeShapeInk;
ForceIWBModeCheckBox.IsChecked = settings.ForceIWB;
SkipTouchCheckCheckBox.IsChecked = settings.SkipTouchCheck;
SuppressTouchErrorsCheckBox.IsChecked = settings.SuppressTouchErrors;
EnableFallbackModeCheckBox.IsChecked = settings.EnableFallbackMode;
}
}
catch { }
}
}
private void SaveSettings()
{
var localAppData = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);
var dir = Path.Combine(localAppData, "Seewo", "EasiNote5", "BetterEN5");
var settingsFile = Path.Combine(dir, "settings.json");
if (!Directory.Exists(dir)) Directory.CreateDirectory(dir);
var settings = new SettingsData
{
AutoSaveInk = AutoSaveInkCheckBox.IsChecked ?? false,
IncludeShapeInk = IncludeShapeInkCheckBox.IsChecked ?? false,
ForceIWB = ForceIWBModeCheckBox.IsChecked ?? true,
SkipTouchCheck = SkipTouchCheckCheckBox.IsChecked ?? true,
SuppressTouchErrors = SuppressTouchErrorsCheckBox.IsChecked ?? true,
EnableFallbackMode = EnableFallbackModeCheckBox.IsChecked ?? true,
};
var json = System.Text.Json.JsonSerializer.Serialize(settings, new System.Text.Json.JsonSerializerOptions
{
WriteIndented = true
});
File.WriteAllText(settingsFile, json);
}
private async void ExportInk_Click(object sender, RoutedEventArgs e)
{
var dialog = new SaveFileDialog
{
Title = "导出墨迹",
Filter = "墨迹文件|*.ink.json|JSON 文件|*.json|所有文件|*.*",
DefaultExt = ".ink.json"
};
if (dialog.ShowDialog() == true)
{
try
{
await _inkService.ExportInkAsync(dialog.FileName);
InkStatusText.Text = $"墨迹已导出至: {dialog.FileName}";
InkStatusText.Foreground = System.Windows.Media.Brushes.Green;
}
catch (Exception ex)
{
InkStatusText.Text = $"导出失败: {ex.Message}";
InkStatusText.Foreground = System.Windows.Media.Brushes.Red;
}
}
}
private async void ImportInk_Click(object sender, RoutedEventArgs e)
{
var dialog = new OpenFileDialog
{
Title = "导入墨迹",
Filter = "墨迹文件|*.ink.json|JSON 文件|*.json|所有文件|*.*",
DefaultExt = ".ink.json"
};
if (dialog.ShowDialog() == true)
{
try
{
await _inkService.ImportInkAsync(dialog.FileName);
InkStatusText.Text = "墨迹已导入";
InkStatusText.Foreground = System.Windows.Media.Brushes.Green;
}
catch (Exception ex)
{
InkStatusText.Text = $"导入失败: {ex.Message}";
InkStatusText.Foreground = System.Windows.Media.Brushes.Red;
}
}
}
private void SaveInkSettings_Click(object sender, RoutedEventArgs e)
{
SaveSettings();
InkStatusText.Text = "墨迹设置已保存";
InkStatusText.Foreground = System.Windows.Media.Brushes.Green;
}
private async void ConvertPptx_Click(object sender, RoutedEventArgs e)
{
var dialog = new OpenFileDialog
{
Title = "选择 PowerPoint 文件",
Filter = "PowerPoint 文件|*.pptx|所有文件|*.*"
};
if (dialog.ShowDialog() == true)
{
try
{
var result = await _conversionService.ConvertPptxToEnbxAsync(dialog.FileName);
ConversionStatusText.Text = $"转换完成: {result}";
ConversionStatusText.Foreground = System.Windows.Media.Brushes.Green;
}
catch (Exception ex)
{
ConversionStatusText.Text = $"转换失败: {ex.Message}";
ConversionStatusText.Foreground = System.Windows.Media.Brushes.Red;
}
}
}
private async void ConvertEnbx_Click(object sender, RoutedEventArgs e)
{
var dialog = new OpenFileDialog
{
Title = "选择希沃白板课件",
Filter = "希沃课件|*.enbx|所有文件|*.*"
};
if (dialog.ShowDialog() == true)
{
try
{
var result = await _conversionService.ConvertEnbxToPptxAsync(dialog.FileName);
ConversionStatusText.Text = $"导出完成: {result}";
ConversionStatusText.Foreground = System.Windows.Media.Brushes.Green;
}
catch (Exception ex)
{
ConversionStatusText.Text = $"导出失败: {ex.Message}";
ConversionStatusText.Foreground = System.Windows.Media.Brushes.Red;
}
}
}
private async void ClearConversionCache_Click(object sender, RoutedEventArgs e)
{
try
{
await _conversionService.FixConversionErrorsAsync();
ConversionStatusText.Text = "转换缓存已清理";
ConversionStatusText.Foreground = System.Windows.Media.Brushes.Green;
}
catch (Exception ex)
{
ConversionStatusText.Text = $"清理失败: {ex.Message}";
ConversionStatusText.Foreground = System.Windows.Media.Brushes.Red;
}
}
private async void ReRegisterConverter_Click(object sender, RoutedEventArgs e)
{
try
{
var converterPath = Path.Combine(
Environment.GetFolderPath(Environment.SpecialFolder.ProgramFilesX86),
"Seewo", "EasiNote5", "EasiNote5_5.2.4.9855", "Main",
"EasiNote.OfficeDocumentConverter.exe");
if (File.Exists(converterPath))
{
Process.Start(converterPath, "/register");
await Task.Delay(1000);
ConversionStatusText.Text = "转换器已重新注册";
}
else
{
ConversionStatusText.Text = "未找到转换器";
}
ConversionStatusText.Foreground = System.Windows.Media.Brushes.Green;
}
catch (Exception ex)
{
ConversionStatusText.Text = $"注册失败: {ex.Message}";
ConversionStatusText.Foreground = System.Windows.Media.Brushes.Red;
}
}
private async void CheckTouch_Click(object sender, RoutedEventArgs e)
{
await RefreshTouchStatusAsync();
}
private async void ApplyTouchFix_Click(object sender, RoutedEventArgs e)
{
try
{
await TouchFixService.ApplyFixAsync();
TouchStatusText.Text = "触摸修复已应用,请重启希沃白板生效";
TouchStatusText.Foreground = System.Windows.Media.Brushes.Green;
SaveSettings();
}
catch (Exception ex)
{
TouchStatusText.Text = $"修复失败: {ex.Message}";
TouchStatusText.Foreground = System.Windows.Media.Brushes.Red;
}
}
private void SaveTouchSettings_Click(object sender, RoutedEventArgs e)
{
SaveSettings();
TouchStatusText.Text = "触摸设置已保存";
TouchStatusText.Foreground = System.Windows.Media.Brushes.Green;
}
private async Task RefreshTouchStatusAsync()
{
try
{
var isIwb = await Task.Run(() => TouchFixService.IsTouchWorkingCorrectly());
TouchStatusText.Text = isIwb
? "触摸状态: 正常 (IWB 模式已启用)"
: "触摸状态: 非 IWB 模式 (可能存在问题)";
TouchStatusText.Foreground = isIwb
? System.Windows.Media.Brushes.Green
: System.Windows.Media.Brushes.Orange;
}
catch (Exception ex)
{
TouchStatusText.Text = $"无法检测触摸状态: {ex.Message}";
TouchStatusText.Foreground = System.Windows.Media.Brushes.Red;
}
}
private class SettingsData
{
public bool AutoSaveInk { get; set; }
public bool IncludeShapeInk { get; set; }
public bool ForceIWB { get; set; } = true;
public bool SkipTouchCheck { get; set; } = true;
public bool SuppressTouchErrors { get; set; } = true;
public bool EnableFallbackMode { get; set; } = true;
}
}
}

View File

@@ -0,0 +1,8 @@
using System.Globalization;
namespace BetterEN5
{
readonly record struct UIItemLangInfo(CultureInfo CultureInfo, string LangText)
{
}
}

View File

@@ -0,0 +1,45 @@
using System;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Threading;
using Cvte.EasiNote;
using Cvte.Windows.Localization;
namespace BetterEN5
{
static class UIItemManagerExtensions
{
public static void AppendWithLang(this IUIItemManager manager, UIItem item,
UIItemAttribute attribute, IList<UIItemLangInfo> langInfoList)
{
manager.Append(c => item, attribute);
string menuName;
if (attribute.Purposes[0] == UIItemPurposes.BoardEditMenu)
{
menuName = "BoardEditContextMenu";
}
else
{
throw new NotSupportedException();
}
var itemLangKey = item.GetType().Name.Replace("MenuItem", "");
var langKey = $"Lang.{menuName}.{itemLangKey}";
Application.Current.Dispatcher.InvokeAsync(() =>
{
foreach (var langInfo in langInfoList)
{
Lang.Sources.Add(new DictionaryLanguageSource
{
[langInfo.CultureInfo] = new Dictionary<string, string>()
{
{ langKey, langInfo.LangText },
},
});
}
}, DispatcherPriority.Send);
}
}
}

28
Better-EN5/manifest.coin Normal file
View File

@@ -0,0 +1,28 @@
>
Description
希沃白板功能增强插件 - 提供墨迹管理、文件转换增强和触摸检测修复功能
>
Id
BetterEN5
>
MaxClientVersion
5.3.0.0
>
MinClientVersion
5.2.2.653
>
Name
Better-EN5 增强模块
>
NetEntryPoint
Better-EN5.dll
>
NetFrameworkEntryPoint
Better-EN5.dll
>
Preinstalled
False
>
Version
1.0.1
>