565 lines
20 KiB
C#
565 lines
20 KiB
C#
using AIProofread.Controls;
|
||
using Microsoft.Office.Interop.Word;
|
||
using Microsoft.Office.Tools.Word;
|
||
using Microsoft.Web.WebView2.Core;
|
||
using Microsoft.Web.WebView2.WinForms;
|
||
using Newtonsoft.Json;
|
||
using System;
|
||
using System.Collections.Generic;
|
||
using System.Diagnostics;
|
||
using System.Drawing;
|
||
using System.Linq;
|
||
using System.Reflection;
|
||
using System.Runtime.InteropServices;
|
||
using System.Runtime.InteropServices.ComTypes;
|
||
using System.Runtime.Remoting.Contexts;
|
||
using System.Text;
|
||
using System.Text.RegularExpressions;
|
||
using System.Windows.Forms;
|
||
using System.Xml.Linq;
|
||
using UtilLib;
|
||
using static System.Windows.Forms.VisualStyles.VisualStyleElement.Header;
|
||
using static System.Windows.Forms.VisualStyles.VisualStyleElement.TaskbarClock;
|
||
using Document = Microsoft.Office.Interop.Word.Document;
|
||
|
||
namespace AIProofread
|
||
{
|
||
public enum BridgeEvent
|
||
{
|
||
LoginSuccess,
|
||
WindowClose
|
||
}
|
||
public delegate void BridgeEventHandler(object sender, EventArgs e);
|
||
|
||
|
||
[ClassInterface(ClassInterfaceType.AutoDual)]
|
||
[ComVisible(true)]
|
||
public class Bridge
|
||
{
|
||
public static Bridge bridge = new Bridge();
|
||
|
||
private static readonly Dictionary<string, WebView2> webViewDict = new Dictionary<string, WebView2>();
|
||
|
||
private static readonly Dictionary<BridgeEvent, List<BridgeEventHandler>> eventHandlers = new Dictionary<BridgeEvent, List<BridgeEventHandler>>();
|
||
|
||
public static Dictionary<int, ProofreadItem> marks;
|
||
|
||
private static int selectProofreadId = -1;
|
||
|
||
private static object missing = System.Reflection.Missing.Value;
|
||
|
||
public string GetAppVersion()
|
||
{
|
||
return Config.APP_VERSION;
|
||
}
|
||
|
||
public void showDialog(string message)
|
||
{
|
||
System.Windows.Forms.MessageBox.Show(message);
|
||
}
|
||
|
||
public int getMinWIdth()
|
||
{
|
||
return Globals.ThisAddIn.GetMinWidth();
|
||
}
|
||
|
||
/// <summary>
|
||
/// 添加事件
|
||
/// </summary>
|
||
/// <param name="eventName"></param>
|
||
/// <param name="handler"></param>
|
||
public static void AddEventHandler(BridgeEvent eventName, BridgeEventHandler handler)
|
||
{
|
||
if (!eventHandlers.ContainsKey(eventName))
|
||
{
|
||
eventHandlers.Add(eventName, new List<BridgeEventHandler>());
|
||
}
|
||
eventHandlers[eventName].Add(handler);
|
||
}
|
||
public static void RemoveEventHandler(BridgeEvent eventName, BridgeEventHandler handler)
|
||
{
|
||
if (eventHandlers.ContainsKey(eventName))
|
||
{
|
||
eventHandlers[eventName].Remove(handler);
|
||
}
|
||
}
|
||
|
||
// 打开网页
|
||
public void OpenUrlWithOsBrowser(string url)
|
||
{
|
||
try
|
||
{
|
||
Process.Start(url);
|
||
}
|
||
catch
|
||
{
|
||
showDialog("打开网页失败");
|
||
}
|
||
}
|
||
|
||
public void StartProofread()
|
||
{
|
||
Globals.ThisAddIn.SendMessageToWeb("start", "login");
|
||
}
|
||
|
||
public void ShowCurrentPane()
|
||
{
|
||
Globals.ThisAddIn.currentDocumentTaskPane.Visible = true;
|
||
}
|
||
public void HideCurrentPane()
|
||
{
|
||
Globals.ThisAddIn.currentDocumentTaskPane.Visible = false;
|
||
}
|
||
|
||
public void Logout(string action)
|
||
{
|
||
if (action == "async-info")
|
||
{
|
||
// web同步注销到ribbon
|
||
Globals.ThisAddIn.SyncLogout();
|
||
}
|
||
else
|
||
{
|
||
// ribbon 发送注销动作到 web
|
||
Globals.ThisAddIn.SendMessageToWeb("logout", null);
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 登录成功后通知后端
|
||
/// </summary>
|
||
public void loginSuccess(string userinfo)
|
||
{
|
||
if (userinfo == null || userinfo.Length == 0) return;
|
||
//
|
||
Userinfo info = JsonConvert.DeserializeObject<Userinfo>(userinfo);
|
||
// 登录成功 展示
|
||
Globals.ThisAddIn.ribbon.ProcessLoginInfo(info);
|
||
//Globals.ThisAddIn.SendMessageToWeb("async-login-success", null);
|
||
}
|
||
|
||
// 获取文档所有文本数据
|
||
public Dictionary<string, object> getAllText()
|
||
{
|
||
return Tools.GetAllText();
|
||
}
|
||
public string getDocumentData()
|
||
{
|
||
Dictionary<string, object> data = new Dictionary<string, object>();
|
||
var name = Globals.ThisAddIn.Application.ActiveDocument.Name;
|
||
data.Add("name", name);
|
||
data.Add("fullName", Globals.ThisAddIn.Application.ActiveDocument.FullName);
|
||
data.Add("content", Tools.GetAllText());
|
||
return Tools.GetJSONString(data);
|
||
}
|
||
|
||
public void ShowUpgrade(string data)
|
||
{
|
||
var upgradeData = JsonConvert.DeserializeObject<UpgradeData>(data);
|
||
var ret = System.Windows.Forms.MessageBox.Show(upgradeData.Message, "更新提示", System.Windows.Forms.MessageBoxButtons.YesNo, System.Windows.Forms.MessageBoxIcon.Question);
|
||
if (ret == System.Windows.Forms.DialogResult.Yes)
|
||
{
|
||
OpenUrlWithOsBrowser(upgradeData.DownloadUrl);
|
||
}
|
||
}
|
||
|
||
public void noticeOtherWeb(string json, string targetWebName)
|
||
{
|
||
if (targetWebName != null)
|
||
{
|
||
if (webViewDict.ContainsKey(targetWebName))
|
||
{
|
||
SendMessageToWeb(webViewDict[targetWebName], json);
|
||
}
|
||
}
|
||
else
|
||
{
|
||
// 如果没有指定 则向所有 webview 发送消息
|
||
foreach (var item in webViewDict)
|
||
{
|
||
SendMessageToWeb(item.Value, json);
|
||
}
|
||
}
|
||
}
|
||
|
||
private void SendMessageToWeb(WebView2 webView, string json)
|
||
{
|
||
webView.CoreWebView2.PostWebMessageAsJson(json);
|
||
}
|
||
|
||
public async static void InitWebEnvAsync(string name, WebView2 webView)
|
||
{
|
||
webView.Name = name;
|
||
if (webViewDict.ContainsKey(webView.Name))
|
||
{
|
||
webViewDict[name] = webView;
|
||
}
|
||
else
|
||
{
|
||
webViewDict.Add(name, webView);
|
||
}
|
||
|
||
// 禁用web安全,允许跨域 否则需要web编译为umd加载模式
|
||
var ops = new CoreWebView2EnvironmentOptions("--disable-web-security");
|
||
var env = await CoreWebView2Environment.CreateAsync(null, Config.WEB_DATA_PATH, ops);
|
||
await webView.EnsureCoreWebView2Async(env);
|
||
//webView.CoreWebView2.Settings.AreDevToolsEnabled = false;
|
||
//webView.CoreWebView2.Settings.AreDefaultScriptDialogsEnabled = false;
|
||
//webView.CoreWebView2.Settings.AreHostObjectsAllowed = true;
|
||
// 添加 js与客户端代理
|
||
|
||
webView.CoreWebView2.AddHostObjectToScript("bridge", bridge);
|
||
}
|
||
|
||
|
||
// 清除所有标记
|
||
public void clearAllProofreadMark()
|
||
{
|
||
// 设置当前文档数据
|
||
Globals.ThisAddIn.ActiveCurrentDocumentMarks();
|
||
try
|
||
{
|
||
selectProofreadId = -1;
|
||
foreach (var item in marks.Values)
|
||
{
|
||
if (item.mark != null)
|
||
{
|
||
if(item.content.tag == "i" && item.content.isAccept == AcceptStatus.Default) {
|
||
item.mark.Text = "";
|
||
}
|
||
item.ResetMarkStyle();
|
||
}
|
||
}
|
||
marks.Clear();
|
||
DocumentUtil.ClearProofreadMarks();
|
||
}catch (Exception ex)
|
||
{
|
||
Logger.Log("ClearAllProofreadMark",ex);
|
||
}
|
||
}
|
||
|
||
public void removeBookmark(string markId)
|
||
{
|
||
DocumentUtil.RemoveBookmark(markId);
|
||
}
|
||
public void addBookmark(string data)
|
||
{
|
||
var item = JsonConvert.DeserializeObject<CorrectedContent>(data);
|
||
//var mark = DocumentUtil.AddBookmark(item.color, item.start, item.end);
|
||
// 初始化校对对象
|
||
marks.Add(item.id, new ProofreadItem(item));
|
||
}
|
||
|
||
public string getAllBookmark()
|
||
{
|
||
return ToJSON(DocumentUtil.GetAllBookmark());
|
||
}
|
||
public string getSectionText()
|
||
{
|
||
return ToJSON(DocumentUtil.GetSectionText());
|
||
}
|
||
|
||
private static string ToJSON(object data)
|
||
{
|
||
return JsonConvert.SerializeObject(data);
|
||
}
|
||
|
||
public string GetCheckText()
|
||
{
|
||
var document = Globals.ThisAddIn.Application.ActiveDocument;
|
||
int count = Globals.ThisAddIn.Application.ActiveDocument.Paragraphs.Count;
|
||
if (count > 10000)
|
||
{
|
||
return document.Content.Text;
|
||
}
|
||
string text = "";
|
||
for (int i = 0; i < count; i++)
|
||
{
|
||
string input = document.Paragraphs[i + 1].Range.Text.Replace("\f\r", "");
|
||
string pattern = "[\\r\\f\\n]";
|
||
string replacement = "\t";
|
||
string text2 = Regex.Replace(input, pattern, replacement);
|
||
if (text2.EndsWith("\t"))
|
||
{
|
||
text2 = text2.Substring(0, text2.Length - 1);
|
||
}
|
||
text2 += "\r";
|
||
text += text2;
|
||
}
|
||
return text;
|
||
}
|
||
|
||
public string GetSelectedText()
|
||
{
|
||
// 文档对象
|
||
var document = Globals.ThisAddIn.Application.ActiveDocument;
|
||
var vstoDocument = Globals.Factory.GetVstoObject(document);
|
||
// 获取选区
|
||
var selection = Globals.ThisAddIn.Application.Selection;
|
||
// 段落数
|
||
int count = selection.Paragraphs.Count;
|
||
if (count > 10000)
|
||
{
|
||
return selection.Text;
|
||
}
|
||
string text = "";
|
||
if (count == 1)
|
||
{
|
||
string input = selection.Text.Replace("\f\r", "");
|
||
string pattern = "[\\r\\f\\n]";
|
||
string replacement = "\t";
|
||
string text2 = Regex.Replace(input, pattern, replacement);
|
||
return text + text2;
|
||
}
|
||
for (int i = 0; i < count; i++)
|
||
{
|
||
string input;
|
||
if (i == 0)
|
||
{
|
||
int start = selection.Range.Start;
|
||
int end = selection.Paragraphs[i + 1].Range.End;
|
||
Document obj = document;
|
||
object Start = start;
|
||
object End = end;
|
||
input = obj.Range(ref Start, ref End).Text;
|
||
}
|
||
else if (i == count - 1)
|
||
{
|
||
int start2 = selection.Paragraphs[i + 1].Range.Start;
|
||
int end2 = selection.Range.End;
|
||
Document obj2 = document;
|
||
object End = start2;
|
||
object Start = end2;
|
||
input = obj2.Range(ref End, ref Start).Text;
|
||
}
|
||
else
|
||
{
|
||
input = selection.Paragraphs[i + 1].Range.Text;
|
||
}
|
||
input = input.Replace("\f\r", "");
|
||
string pattern2 = "[\\r\\f\\n]";
|
||
string replacement2 = "\t";
|
||
string text3 = Regex.Replace(input, pattern2, replacement2);
|
||
if (text3.EndsWith("\t"))
|
||
{
|
||
text3 = text3.Substring(0, text3.Length - 1);
|
||
}
|
||
text3 += "\r";
|
||
text += text3;
|
||
}
|
||
return text;
|
||
}
|
||
|
||
public void ShowLoginForm(string action)
|
||
{
|
||
Globals.ThisAddIn.ShowLoginForm(action);
|
||
}
|
||
public void ShowSettingForm()
|
||
{
|
||
FormSetting frm = new FormSetting();
|
||
frm.Show();
|
||
}
|
||
|
||
public void MoveCursor(int pos)
|
||
{
|
||
var rng = Globals.ThisAddIn.Application.ActiveDocument.Range(pos, pos);
|
||
rng.Select();
|
||
}
|
||
|
||
public void SelectMarkById(int proofreadId)
|
||
{
|
||
// 设置当前文档数据
|
||
Globals.ThisAddIn.ActiveCurrentDocumentMarks();
|
||
|
||
if (proofreadId == selectProofreadId) return;
|
||
var doc = Globals.ThisAddIn.Application.ActiveDocument;
|
||
// 取消上一个标签移除
|
||
if (selectProofreadId > 0 && marks.ContainsKey(selectProofreadId))
|
||
{
|
||
var m = marks[selectProofreadId];
|
||
if (doc.Bookmarks.Exists(m.Name))
|
||
{
|
||
marks[selectProofreadId].UnSelect();
|
||
}
|
||
else
|
||
{
|
||
marks.Remove(selectProofreadId);
|
||
}
|
||
}
|
||
selectProofreadId = proofreadId;
|
||
|
||
if (proofreadId > 0 && marks.ContainsKey(proofreadId))
|
||
{
|
||
var mark = marks[proofreadId].mark;
|
||
// 已经不存在该标签了
|
||
if (!doc.Bookmarks.Exists(mark.Name))
|
||
{
|
||
marks.Remove(selectProofreadId);
|
||
return;
|
||
}
|
||
//object lineNum = (int)mark.Range.Information[WdInformation.wdFirstCharacterLineNumber] - 1;
|
||
//object goToLine = WdGoToItem.wdGoToLine;
|
||
//object goNext = WdGoToDirection.wdGoToNext;
|
||
//Globals.ThisAddIn.Application.ActiveWindow.Selection.GoTo(ref goToLine, ref goNext, ref lineNum);
|
||
//
|
||
object bookmark = WdGoToItem.wdGoToBookmark;
|
||
object bookmarkName = mark.Name;
|
||
//doc.GoTo(mark);
|
||
//mark.Range.GoTo();
|
||
Globals.ThisAddIn.Application.ActiveWindow.Selection.GoTo(ref bookmark, ref missing, ref missing, ref bookmarkName);
|
||
//
|
||
//mark.DisableCharacterSpaceGrid = false;
|
||
// 先滚动到可视区域
|
||
//doc.ActiveWindow.ScrollIntoView(mark.Range);
|
||
marks[proofreadId].Select();
|
||
Globals.ThisAddIn.SendMessageToWeb("select", proofreadId);
|
||
}
|
||
Globals.ThisAddIn.SendMessageToWeb("select_proofread", proofreadId);
|
||
}
|
||
|
||
public void processMark(int proofreadId, int status)
|
||
{
|
||
if (proofreadId > 0 && marks.ContainsKey(proofreadId))
|
||
{
|
||
marks[proofreadId].Process(status);
|
||
}
|
||
}
|
||
|
||
public void InitContent(string content)
|
||
{
|
||
List<DocumentCorrectItem> list = JsonConvert.DeserializeObject<List<DocumentCorrectItem>>(content);
|
||
// 先清除所有数据
|
||
clearAllProofreadMark();
|
||
|
||
//var app = Globals.ThisAddIn.Application;
|
||
//var cur = app.Selection;
|
||
//
|
||
foreach (var correct in list)
|
||
{
|
||
//cur.TypeText(correct.Insert);
|
||
//var rng = cur.Range;
|
||
if (correct.Diffs != null && correct.Diffs.Count > 0)
|
||
{
|
||
//var diffs = correct.Diffs.OrderBy(it =>
|
||
//{
|
||
// if (it.tag != "i") return it.start;
|
||
// return it.start + correct.Insert_len;
|
||
//}).ToList();
|
||
int index = 0;
|
||
foreach (var item in correct.Diffs)
|
||
{
|
||
var mark = AddBookmark(item, index, correct.Sentence_offset, correct.Insert_len, correct.Paragraph_offset);
|
||
if (item.tag != "i") index++;
|
||
if (mark != null) {
|
||
marks.Add(item.id, new ProofreadItem(item, mark));
|
||
}
|
||
}
|
||
}
|
||
}
|
||
foreach (var item in marks)
|
||
{
|
||
if(item.Value.mark != null)
|
||
{
|
||
if (item.Value.content.tag == "i")
|
||
{
|
||
item.Value.mark.Text = ToolUtil.GetBlankText(item.Value.content.text.Length);
|
||
}
|
||
if (item.Value.content.color != null)
|
||
{
|
||
// 给选区添加背景颜色
|
||
item.Value.mark.Shading.BackgroundPatternColor = (WdColor)ColorTranslator.ToOle(Colors.FromHex(item.Value.content.color));
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
public string GetParagraph(int index)
|
||
{
|
||
|
||
var document = Globals.ThisAddIn.Application.ActiveDocument;
|
||
try
|
||
{
|
||
var paragraphs = document.Paragraphs;
|
||
var total = paragraphs.Count;
|
||
// 判断索引是否超出范围
|
||
if (index > total)
|
||
{
|
||
return null;
|
||
}
|
||
var paragraph = document.Paragraphs[index];
|
||
return paragraph.Range.Text;
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
return "error:" + ex.Message;
|
||
}
|
||
}
|
||
|
||
public Microsoft.Office.Tools.Word.Bookmark AddBookmark(CorrectedContent item, int findIndex, int offset, int length,int paragraphIndex)
|
||
{
|
||
Microsoft.Office.Tools.Word.Bookmark bookmark = null;
|
||
try
|
||
{
|
||
var document = Globals.ThisAddIn.Application.ActiveDocument;
|
||
ControlCollection controls = Globals.Factory.GetVstoObject(document).Controls;
|
||
// 判断并获取当前段落
|
||
if (paragraphIndex > document.Paragraphs.Count) return null;
|
||
var paragraph = document.Paragraphs[paragraphIndex];
|
||
var paragraphStart = paragraph.Range.Start;
|
||
var Start = paragraphStart + offset;
|
||
var End = Start + length - 1;
|
||
if(End > paragraph.Range.End)
|
||
{
|
||
End = paragraph.Range.End;
|
||
}
|
||
|
||
// 当前句子的选区
|
||
var fullRange = document.Range(Start, End);
|
||
string fullText = fullRange.Text;
|
||
Start = paragraphStart + offset + item.start;
|
||
End = paragraphStart + offset + item.end;
|
||
|
||
// 定位要操作的文字
|
||
var r = document.Range(Start, End);
|
||
var markName = Config.BuildBookmarkName(item.id);
|
||
|
||
// 判断是否已经存在
|
||
if (controls.Contains(markName))
|
||
{
|
||
controls.Remove(markName);
|
||
}
|
||
// 判断选区是否正确
|
||
if (item.tag == "i" || r.Text == item.origin)
|
||
{
|
||
bookmark = controls.AddBookmark(r, markName);
|
||
bookmark.Tag = "ai_proofread";
|
||
}
|
||
else
|
||
{
|
||
int startPos = 0, index, findCount = 0;
|
||
while ((index = fullText.IndexOf(item.origin, startPos)) != -1)
|
||
{
|
||
if (findCount == findIndex)
|
||
{
|
||
r = document.Range(offset + index, offset + index + item.origin.Length);
|
||
bookmark = controls.AddBookmark(r, markName);
|
||
bookmark.Tag = "ai_proofread";
|
||
break;
|
||
}
|
||
startPos = index;
|
||
findCount++;
|
||
}
|
||
}
|
||
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Logger.Log("create mark error:" + ex.Message + "\n" + ex.StackTrace + "\n\n");
|
||
}
|
||
return bookmark;
|
||
}
|
||
|
||
}
|
||
}
|