/** * Grasscutter Tools * Copyright (C) 2022 jie65535 * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published * by the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . * **/ using System; using System.Collections.Generic; using System.Collections.Specialized; using System.Drawing; using System.Linq; using System.Threading.Tasks; using System.Windows.Forms; using GrasscutterTools.Game; using GrasscutterTools.Pages; using GrasscutterTools.Properties; using GrasscutterTools.Utils; namespace GrasscutterTools.Forms { internal partial class FormMain : Form { private const string TAG = nameof(FormMain); public static FormMain Instance { get; private set; } #region - 初始化 Init - public FormMain() { Logger.I(TAG, "FormMain ctor enter"); Instance = this; InitializeComponent(); Icon = Resources.IconGrasscutter; if (DesignMode) return; Common.KeyGo = new KeyGo(Handle); Common.KeyGo.HotKeyTriggerEvent += OnHotKeyTrigger; try { if (!Settings.Default.IsUpgraded) { Settings.Default.Upgrade(); Settings.Default.IsUpgraded = true; } } catch (Exception ex) { Logger.W(TAG, "Upgrade Settings failed.", ex); } try { var location = Settings.Default.MainFormLocation; // 还原窗体位置 if (location.X > 0 && location.Y > 0) { StartPosition = FormStartPosition.Manual; Location = location; Logger.I(TAG, "Restore window location: " + Location); } // 还原窗体大小 if (Settings.Default.MainFormSize != default) { Size = Settings.Default.MainFormSize; Logger.I(TAG, "Restore window size: " + Size); } // 还原导航容器间隔位置 if (Settings.Default.NavContainerSplitterDistance >= NavContainer.Panel1MinSize) { NavContainer.SplitterDistance = Settings.Default.NavContainerSplitterDistance; Logger.I(TAG, "Restore NavContainer SplitterDistance: " + NavContainer.SplitterDistance); } // 还原窗口的不透明度 if (Settings.Default.WindowOpacity < 100) { Opacity = Settings.Default.WindowOpacity / 100.0; Logger.I(TAG, "Restore window opacity: " + Opacity); } // 恢复自动复制选项状态 ChkAutoCopy.Checked = Settings.Default.AutoCopy; // 初始化页面 InitPages(); } catch (Exception ex) { Logger.E(TAG, "Loading settings error", ex); MessageBox.Show(Resources.SettingLoadError + ex.Message, Resources.Error, MessageBoxButtons.OK, MessageBoxIcon.Error); } Logger.I(TAG, "FormMain ctor completed"); } /// /// 重载界面 /// public void Reload() { FormMain_Load(this, null); } /// /// 窗体载入时触发(切换语言时会重新载入) /// private void FormMain_Load(object sender, EventArgs e) { Logger.I(TAG, "FormMain_Load enter"); Text += " - by jie65535 - v" + Common.AppVersion.ToString(3); #if DEBUG Text += "-debug"; #endif if (DesignMode) return; // 加载页面导航 UpdatePagesNav(); // 加载游戏ID资源 GameData.LoadResources(); // 遍历每一个页面重新加载 foreach (var page in Pages.Values) { Logger.I(TAG, $"{page.Name} OnLoad enter"); page.OnLoad(); Logger.I(TAG, $"{page.Name} OnLoad completed"); } Logger.I(TAG, "FormMain_Load completed"); } /// /// 窗口关闭后触发 /// private void FormMain_FormClosed(object sender, FormClosedEventArgs e) { Logger.I(TAG, "FormMain FormClosed enter"); // 遍历每一个页面,通知关闭 foreach (var page in Pages.Values) { page.OnClosed(); } // 保存当前设置 SaveSettings(); Logger.I(TAG, "FormMain FormClosed completed"); } /// /// 保存设置 /// private void SaveSettings() { try { // 记录界面状态 Settings.Default.AutoCopy = ChkAutoCopy.Checked; // 记录窗口位置 if (WindowState == FormWindowState.Normal) Settings.Default.MainFormLocation = Location; // 如果命令窗口已经弹出了,则不要保存多余的高度 Settings.Default.MainFormSize = TxtCommandRunLog != null ? new Size(Width, Height - TxtCommandRunLogMinHeight) : Size; // 记录导航容器分隔位置 Settings.Default.NavContainerSplitterDistance = NavContainer.SplitterDistance; // 保存设置 Settings.Default.Save(); } catch (Exception ex) { Logger.E(TAG, "Save settings failed.", ex); MessageBox.Show(Resources.SettingSaveError + ex.Message, Resources.Error, MessageBoxButtons.OK, MessageBoxIcon.Error); } } #endregion - 初始化 Init - #region - 页面导航 Nav - public Dictionary Pages { get; private set; } /// /// 初始化并创建所有页面 /// private void InitPages() { Logger.I(TAG, "InitPages enter"); Pages = new Dictionary(32); TCMain.SuspendLayout(); CreatePage(); var poc = CreatePage(); poc.ShowTipInRunButton = msg => ShowTip(msg, BtnInvokeOpenCommand); CreatePage(); CreatePage(); CreatePage(); CreatePage(); CreatePage(); CreatePage(); CreatePage(); CreatePage(); CreatePage(); CreatePage(); CreatePage(); CreatePage(); CreatePage(); CreatePage(); CreatePage(); CreatePage(); CreatePage(); CreatePage(); CreatePage(); #if DEBUG CreatePage(); #endif TCMain.ResumeLayout(); Logger.I(TAG, "InitPages completed"); } /// /// 当前的页面选项卡顺序 /// string Item1 = Page Name(Key) /// bool Item2 = IsVisible /// public List> PageTabOrders { get; set; } /// /// 加载页面选项卡顺序 /// private List> LoadPageTabOrders() { if (PageTabOrders != null) return PageTabOrders; List> tabOrders; if (!(Settings.Default.PageOrders?.Count > 0)) { tabOrders = new List>(Pages.Count); // 默认状态 foreach (var tab in Pages) tabOrders.Add(new Tuple(tab.Key, true)); } else { tabOrders = new List>(Settings.Default.PageOrders.Count); // 从设置中读取 foreach (var item in Settings.Default.PageOrders) { // 冒号分隔的项 "PageHome:1" 0=隐藏 1=显示 var sp = item.IndexOf(':'); if (sp == -1 || !int.TryParse(item.Substring(sp + 1), out var isVisible)) continue; tabOrders.Add(new Tuple(item.Substring(0, sp), isVisible != 0)); } } return tabOrders; } /// /// 重置页面选项卡顺序 /// public void ResetPageTabOrders() { PageTabOrders = new List>(Pages.Count); // 默认状态 foreach (var tab in Pages) PageTabOrders.Add(new Tuple(tab.Key, true)); } /// /// 保存页面选项卡顺序 /// public void SavePageTabOrders() { if (PageTabOrders == null || PageTabOrders.Count == 0) { Settings.Default.PageOrders = null; return; } var setting = new StringCollection(); // 冒号分隔的项 "PageHome:1" 0=隐藏 1=显示 foreach (var pageOrder in PageTabOrders) setting.Add($"{pageOrder.Item1}:{(pageOrder.Item2?'1':'0')}"); Settings.Default.PageOrders = setting; } /// /// 初始化页面导航 /// public void UpdatePagesNav() { ListPages.BeginUpdate(); ListPages.Items.Clear(); // 以下代码主要是为了加载用户自定义顺序的选项卡 var tabOrders = LoadPageTabOrders(); // 程序更新后增加或减少了界面的情况 if (tabOrders.Count != Pages.Count) { PageTabOrders = new List>(Pages.Count); var i = 0; var pageKeys = Pages.Keys.ToList(); foreach (var pageOrder in tabOrders) { // 新增页面优先显示 if (tabOrders.All(it => it.Item1 != pageKeys[i])) { PageTabOrders.Add(new Tuple(pageKeys[i], true)); ListPages.Items.Add(Pages[pageKeys[i]].Text); } // 尝试获取页面标题 if (Pages.TryGetValue(pageOrder.Item1, out var page)) { // 仅设置为可见时添加 if (pageOrder.Item2) ListPages.Items.Add(page.Text); PageTabOrders.Add(new Tuple(pageOrder.Item1, pageOrder.Item2)); } // 如果获取不到页面标题,说明在本次更新中这个页面被删掉了,因此设置项也随之更新 i++; } // 加上新增在最后的页面 if (ListPages.Items.Count == i) { while (i < Pages.Count) { PageTabOrders.Add(new Tuple(pageKeys[i], true)); ListPages.Items.Add(Pages[pageKeys[i]].Text); i++; } } // 保存页面顺序 SavePageTabOrders(); } else { // 按照设定顺序显示 foreach (var pageOrder in tabOrders) { if (pageOrder.Item2) ListPages.Items.Add(Pages[pageOrder.Item1].Text); } PageTabOrders = tabOrders; } ListPages.EndUpdate(); } /// /// 导航列表选中项改变时触发 /// private void ListPages_SelectedIndexChanged(object sender, EventArgs e) { if (ListPages.SelectedIndex == -1) return; // 根据选中索引反查选中页面Key var key = PageTabOrders.Where(it => it.Item2) .ElementAt(ListPages.SelectedIndex) .Item1; // 通过Key找到页面的父节点也就是TabPage,设置为选中项 TCMain.SelectedTab = Pages[key].Parent as TabPage; } /// /// 导航列表项居中绘制 /// /// /// private void ListPages_DrawItem(object sender, DrawItemEventArgs e) { e.DrawBackground(); e.DrawFocusRectangle(); var strFmt = new StringFormat { Alignment = StringAlignment.Center, //文本垂直居中 LineAlignment = StringAlignment.Center //文本水平居中 }; e.Graphics.DrawString(ListPages.Items[e.Index].ToString(), e.Font, new SolidBrush(e.ForeColor), e.Bounds, strFmt); } /// /// 导航列表大小改变时触发 /// private void ListPages_SizeChanged(object sender, EventArgs e) { // 立刻重绘列表项 ListPages.Refresh(); } /// /// 创建指定类型页面 /// /// 页面类型,必须继承BasePage /// 页面实例 private T CreatePage() where T : BasePage, new() { var page = new T { SetCommand = SetCommand, RunCommands = RunCommands, GetCommand = () => CmbCommand.Text, Dock = DockStyle.Fill, Name = typeof(T).Name, }; Pages.Add(page.Name, page); var tp = new TabPage(); TCMain.TabPages.Add(tp); tp.Controls.Add(page); return page; } #endregion #region - 快捷键执行 HotKey - /// /// 快捷键触发时执行 /// private void OnHotKeyTrigger(object sender, HotKeyTriggerEventArgs e) { BeginInvoke(new Func(() => RunRawCommands(e.HotKeyItem.Commands))); e.Handle = true; } private const int WM_HOTKEY = 0x312; protected override void WndProc(ref Message m) { base.WndProc(ref m); switch (m.Msg) { case WM_HOTKEY: Common.KeyGo.ProcessHotKey(m.WParam.ToInt32()); break; } } #endregion - 快捷键执行 HotKey - #region - 命令 Command - /// /// 设置命令 /// /// 命令 private void SetCommand(string command) { Logger.I(TAG, $"SetCommand(\"{command}\")"); var oldCommand = CmbCommand.Text; if (ModifierKeys == Keys.Shift && !string.IsNullOrEmpty(oldCommand)) command = $"{oldCommand} | {command}"; AddCommandToList(command); if (ChkAutoCopy.Checked) CopyCommand(); if (ModifierKeys == Keys.Control) { OnOpenCommandInvoke(); } else if (ModifierKeys == Keys.Alt) { OnOpenCommandInvoke(); CmbCommand.Text = oldCommand; } } /// /// 添加命令到执行记录 /// private void AddCommandToList(string command = "") { if (string.IsNullOrEmpty(command)) command = CmbCommand.Text; if (string.IsNullOrEmpty(command)) return; if (CmbCommand.Items.Count > 19) CmbCommand.Items.RemoveAt(0); CmbCommand.Items.Add(command); CmbCommand.SelectedIndex = CmbCommand.Items.Count - 1; } /// /// 设置带参数的命令 /// /// 命令 /// 参数 private void SetCommand(string command, string args) { if (!string.IsNullOrEmpty(args)) command = command + ' ' + args; SetCommand(Settings.Default.IsIncludeUID ? $"{command} @{Settings.Default.Uid}" : command); } /// /// 点击复制按钮时触发 /// private async void BtnCopy_Click(object sender, EventArgs e) { CopyCommand(); await UIUtil.ButtonComplete(BtnCopy); } /// /// 复制命令 /// private void CopyCommand() { if (!string.IsNullOrEmpty(CmbCommand.Text)) Clipboard.SetText(CmbCommand.Text); } /// /// 在命令行内按下回车时直接执行 /// private void TxtCommand_KeyDown(object sender, KeyEventArgs e) { if (e.KeyCode == Keys.Enter) OnOpenCommandInvoke(); } /// /// 开放命令执行时触发 /// private void OnOpenCommandInvoke() { BtnInvokeOpenCommand_Click(BtnInvokeOpenCommand, EventArgs.Empty); } /// /// 点击执行开放命令按钮时触发 /// private async void BtnInvokeOpenCommand_Click(object sender, EventArgs e) { if (!BtnInvokeOpenCommand.Enabled) return; var cmd = CmbCommand.Text; if (cmd.Length < 2) { ShowTip(Resources.CommandContentCannotBeEmpty, CmbCommand); return; } await RunRawCommands(cmd); } /// /// 运行原始命令 /// /// 命令字符串 /// 是否执行成功 private async Task RunRawCommands(string commands) { if (commands.IndexOf('|') == -1 || Common.OC?.CanInvokeMultipleCmd == true) return await RunCommands(FormatCommand(commands)); return await RunCommands(commands.Split('|').Select(FormatCommand).ToArray()); } /// /// 格式化命令 /// (去除收尾空白,替换换行) /// /// 原始输入 /// 格式化后可执行命令 private string FormatCommand(string raw) { return raw.Trim().Replace("\\r", "\r").Replace("\\n", "\n"); } /// /// 运行命令 /// /// 命令列表 /// 是否执行成功 private async Task RunCommands(params string[] commands) { if (Common.OC == null || !Common.OC.CanInvoke) { ShowTip(Resources.RequireOpenCommandTip, BtnInvokeOpenCommand); NavigateTo(); return false; } ExpandCommandRunLog(); try { BtnInvokeOpenCommand.Enabled = false; BtnInvokeOpenCommand.Cursor = Cursors.WaitCursor; int i = 0; foreach (var command in commands) { TxtCommandRunLog.AppendText(">"); TxtCommandRunLog.AppendText(command); if (commands.Length > 1) TxtCommandRunLog.AppendText($" ({++i}/{commands.Length})"); TxtCommandRunLog.AppendText(Environment.NewLine); var cmd = command.TrimStart('/'); try { Logger.I(TAG, "RunCommand:" + cmd); var msg = await Common.OC.Invoke(cmd); TxtCommandRunLog.AppendText(string.IsNullOrEmpty(msg) ? "OK" : msg.Replace("\n", "\r\n")); TxtCommandRunLog.AppendText(Environment.NewLine); } catch (Exception ex) { Logger.W(TAG, "RunCommand Error:", ex); TxtCommandRunLog.AppendText("Error: "); TxtCommandRunLog.AppendText(ex.Message); TxtCommandRunLog.AppendText(Environment.NewLine); MessageBox.Show(ex.Message, Resources.Error, MessageBoxButtons.OK, MessageBoxIcon.Error); return false; } TxtCommandRunLog.ScrollToCaret(); } } finally { BtnInvokeOpenCommand.Cursor = Cursors.Default; BtnInvokeOpenCommand.Enabled = true; } return true; } /// /// 命令日志最小高度 /// private const int TxtCommandRunLogMinHeight = 150; /// /// 命令日志文本框 /// private TextBox TxtCommandRunLog; /// /// 展开命令记录(可重入) /// private void ExpandCommandRunLog() { if (GrpCommand.Height < TxtCommandRunLogMinHeight) { if (WindowState == FormWindowState.Maximized) WindowState = FormWindowState.Normal; NavContainer.Anchor &= ~AnchorStyles.Bottom; GrpCommand.Anchor |= AnchorStyles.Top; Size = new Size(Width, Height + TxtCommandRunLogMinHeight); MinimumSize = new Size(MinimumSize.Width, MinimumSize.Height + TxtCommandRunLogMinHeight); NavContainer.Anchor |= AnchorStyles.Bottom; GrpCommand.Anchor &= ~AnchorStyles.Top; } if (TxtCommandRunLog == null) { TxtCommandRunLog = new TextBox { Anchor = AnchorStyles.Left | AnchorStyles.Top | AnchorStyles.Right | AnchorStyles.Bottom, Multiline = true, Font = new Font("Consolas", 9F), Location = new Point(ChkAutoCopy.Left, ChkAutoCopy.Bottom + 6), Size = new Size(GrpCommand.Width - ChkAutoCopy.Left * 2, TxtCommandRunLogMinHeight), ReadOnly = true, BackColor = Color.White, ScrollBars = ScrollBars.Vertical, }; GrpCommand.Controls.Add(TxtCommandRunLog); } } #endregion - 命令 Command - #region - 通用 General - /// /// 窗口按键按下时触发 /// private void FormMain_KeyDown(object sender, KeyEventArgs e) { if (e.KeyCode == Keys.F5) { // F5 为执行命令 OnOpenCommandInvoke(); } else if (e.Alt && e.KeyCode >= Keys.D0 && e.KeyCode <= Keys.D9) { // Alt+数字键 = 跳转到对应页面 var i = e.KeyCode == Keys.D0 ? 9 : e.KeyCode - Keys.D1; if (i < ListPages.Items.Count) ListPages.SelectedIndex = i; } else if (Common.KeyGo.IsEnabled == false) { foreach (var hotkeyItem in Common.KeyGo.Items) { if (!hotkeyItem.IsEnabled) continue; var t = hotkeyItem.HotKey.LastIndexOf('+'); var key = (t >= 0) ? hotkeyItem.HotKey.Substring(t+1) : hotkeyItem.HotKey; if (e.KeyCode != (Keys)Enum.Parse(typeof(Keys), key.Trim())) continue; if (t >= 0) { if (hotkeyItem.HotKey.Contains("Ctrl") && !e.Control) continue; if (hotkeyItem.HotKey.Contains("Shift") && !e.Shift) continue; if (hotkeyItem.HotKey.Contains("Alt") && !e.Alt) continue; } BeginInvoke(new Func(() => RunRawCommands(hotkeyItem.Commands))); break; } } } /// /// 提示气泡对象 /// private readonly ToolTip TTip = new ToolTip(); /// /// 在指定控件上显示提示气泡 /// /// 消息 /// 控件 private void ShowTip(string message, Control control) { if (InvokeRequired) { BeginInvoke(new Action(() => ShowTip(message, control))); return; } TTip.Show(message, control, 0, control.Size.Height, 3000); } /// /// 导航到目标页面并返回该页面实例 /// /// 页面类型 /// 如果导航到了则返回页面实例,否则返回空 public TPage NavigateTo() where TPage : BasePage { ListPages.SelectedIndex = -1; var page = Pages[typeof(TPage).Name]; TCMain.SelectedTab = page.Parent as TabPage; return page as TPage; } #endregion - 通用 General - /// /// 命令栏文本改变时触发 /// private void CmbCommand_TextChanged(object sender, EventArgs e) { LblClearFilter.Visible = CmbCommand.Text.Length > 0; } /// /// 点击清空命令栏标签时触发 /// private void LblClearFilter_Click(object sender, EventArgs e) { CmbCommand.Text = ""; } } }