GrasscutterCommandGenerator/Source/GrasscutterTools/Forms/FormMain.cs
2023-10-07 22:22:35 +08:00

642 lines
22 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* 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 <https://www.gnu.org/licenses/>.
*
**/
using System;
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
{
public partial class FormMain : Form
{
#region - Init -
private const string TAG = "FormMain";
public FormMain()
{
Logger.I(TAG, "FormMain ctor enter");
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);
}
// 恢复自动复制选项状态
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");
}
/// <summary>
/// 初始化并创建所有页面
/// </summary>
private void InitPages()
{
Logger.I(TAG, "InitPages enter");
TCMain.SuspendLayout();
var ph = CreatePage<PageHome>();
ph.OnLanguageChanged = () => FormMain_Load(this, EventArgs.Empty);
var poc = CreatePage<PageOpenCommand>();
poc.ShowTipInRunButton = msg => ShowTip(msg, BtnInvokeOpenCommand);
var pcc = CreatePage<PageCustomCommands>();
var phk = CreatePage<PageHotKey>();
pcc.OnAddHotKey = tag =>
{
phk.AddNewHotKey(tag);
// 跳转到快捷键界面
for (var i = 0; i < TCMain.Controls.Count; i++)
{
if (TCMain.Controls[i].Controls[0] == phk)
{
ListPages.SelectedIndex = i;
break;
}
}
};
CreatePage<PageGiveArtifact>();
CreatePage<PageSetProp>();
CreatePage<PageSpawn>();
CreatePage<PageGiveItem>();
CreatePage<PageAvatar>();
CreatePage<PageGiveWeapon>();
CreatePage<PageScene>();
CreatePage<PageSceneTag>();
CreatePage<PageTasks>();
CreatePage<PageManagement>();
CreatePage<PageMail>();
CreatePage<PageQuest>();
CreatePage<PageAchievement>();
CreatePage<PageAbout>();
#if DEBUG
CreatePage<PageTools>();
#endif
TCMain.ResumeLayout();
Logger.I(TAG, "InitPages completed");
}
/// <summary>
/// 初始化页面导航
/// </summary>
private void InitPagesNav()
{
ListPages.Items.Clear();
ListPages.Items.AddRange(new object[]
{
Resources.PageHomeTitle,
Resources.PageOpenCommandTitle,
Resources.PageCustomCommandsTitle,
Resources.PageHotKey,
Resources.PageGetArtifactTitle,
Resources.PageSetPropTitle,
Resources.PageSpawnTitle,
Resources.PageGiveItemTitle,
Resources.PageAvatarTitle,
Resources.PageGiveWeaponTitle,
Resources.PageSceneTitle,
Resources.PageSceneTagTitle,
Resources.PageTasksTitle,
Resources.PageManagementTitle,
Resources.PageMailTitle,
Resources.PageQuestTitle,
Resources.PageAchievementTitle,
Resources.PageAboutTitle,
#if DEBUG
"Tools",
#endif
});
}
/// <summary>
/// 导航列表项居中绘制
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
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);
}
/// <summary>
/// 导航列表大小改变时触发
/// </summary>
private void ListPages_SizeChanged(object sender, EventArgs e)
{
// 立刻重绘列表项
ListPages.Refresh();
}
/// <summary>
/// 创建指定类型页面
/// </summary>
/// <typeparam name="T">页面类型必须继承BasePage</typeparam>
/// <returns>页面实例</returns>
private T CreatePage<T>() where T : BasePage, new()
{
var page = new T
{
SetCommand = SetCommand,
RunCommands = RunCommands,
GetCommand = () => CmbCommand.Text,
Dock = DockStyle.Fill,
Name = typeof(T).Name,
};
var tp = new TabPage();
TCMain.TabPages.Add(tp);
tp.Controls.Add(page);
return page;
}
private void ListPages_SelectedIndexChanged(object sender, EventArgs e)
{
TCMain.SelectedIndex = ListPages.SelectedIndex;
}
/// <summary>
/// 窗体载入时触发(切换语言时会重新载入)
/// </summary>
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;
// 加载页面导航
InitPagesNav();
// 加载游戏ID资源
GameData.LoadResources();
// 遍历每一个页面重新加载
foreach (TabPage tp in TCMain.Controls)
{
if (tp.Controls.Count > 0 && tp.Controls[0] is BasePage page)
{
Logger.I(TAG, $"{page.Name} OnLoad enter");
page.OnLoad();
Logger.I(TAG, $"{page.Name} OnLoad completed");
}
}
Logger.I(TAG, "FormMain_Load completed");
}
/// <summary>
/// 窗口关闭后触发
/// </summary>
private void FormMain_FormClosed(object sender, FormClosedEventArgs e)
{
Logger.I(TAG, "FormMain FormClosed enter");
// 遍历每一个页面,通知关闭
foreach (TabPage tp in TCMain.Controls)
{
if (tp.Controls.Count > 0 && tp.Controls[0] is BasePage page)
page.OnClosed();
}
// 保存当前设置
SaveSettings();
Logger.I(TAG, "FormMain FormClosed completed");
}
/// <summary>
/// 保存设置
/// </summary>
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 - HotKey -
/// <summary>
/// 快捷键触发时执行
/// </summary>
private void OnHotKeyTrigger(object sender, HotKeyTriggerEventArgs e)
{
BeginInvoke(new Func<Task>(() => 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 -
/// <summary>
/// 设置命令
/// </summary>
/// <param name="command">命令</param>
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;
}
}
/// <summary>
/// 添加命令到执行记录
/// </summary>
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;
}
/// <summary>
/// 设置带参数的命令
/// </summary>
/// <param name="command">命令</param>
/// <param name="args">参数</param>
private void SetCommand(string command, string args)
{
if (!string.IsNullOrEmpty(args))
command = command + ' ' + args;
SetCommand(Settings.Default.IsIncludeUID
? $"{command} @{Settings.Default.Uid}"
: command);
}
/// <summary>
/// 点击复制按钮时触发
/// </summary>
private async void BtnCopy_Click(object sender, EventArgs e)
{
CopyCommand();
await UIUtil.ButtonComplete(BtnCopy);
}
/// <summary>
/// 复制命令
/// </summary>
private void CopyCommand()
{
if (!string.IsNullOrEmpty(CmbCommand.Text))
Clipboard.SetText(CmbCommand.Text);
}
/// <summary>
/// 在命令行内按下回车时直接执行
/// </summary>
private void TxtCommand_KeyDown(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.Enter) OnOpenCommandInvoke();
}
/// <summary>
/// 开放命令执行时触发
/// </summary>
private void OnOpenCommandInvoke()
{
BtnInvokeOpenCommand_Click(BtnInvokeOpenCommand, EventArgs.Empty);
}
/// <summary>
/// 点击执行开放命令按钮时触发
/// </summary>
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);
}
/// <summary>
/// 运行原始命令
/// </summary>
/// <param name="commands">命令字符串</param>
/// <returns>是否执行成功</returns>
private async Task<bool> RunRawCommands(string commands)
{
if (commands.IndexOf('|') == -1 || Common.OC?.CanInvokeMultipleCmd == true)
return await RunCommands(FormatCommand(commands));
return await RunCommands(commands.Split('|').Select(FormatCommand).ToArray());
}
/// <summary>
/// 格式化命令
/// (去除收尾空白,替换换行)
/// </summary>
/// <param name="raw">原始输入</param>
/// <returns>格式化后可执行命令</returns>
private string FormatCommand(string raw)
{
return raw.Trim().Replace("\\r", "\r").Replace("\\n", "\n");
}
/// <summary>
/// 运行命令
/// </summary>
/// <param name="commands">命令列表</param>
/// <returns>是否执行成功</returns>
private async Task<bool> RunCommands(params string[] commands)
{
if (Common.OC == null || !Common.OC.CanInvoke)
{
ShowTip(Resources.RequireOpenCommandTip, BtnInvokeOpenCommand);
//TCMain.SelectedTab = TPRemoteCall;
for (var i = 0; i < TCMain.Controls.Count; i++)
{
if (TCMain.Controls[i].Controls[0] is PageOpenCommand)
{
ListPages.SelectedIndex = i;
break;
}
}
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);
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;
}
/// <summary>
/// 命令日志最小高度
/// </summary>
private const int TxtCommandRunLogMinHeight = 150;
/// <summary>
/// 命令日志文本框
/// </summary>
private TextBox TxtCommandRunLog;
/// <summary>
/// 展开命令记录(可重入)
/// </summary>
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 -
/// <summary>
/// 窗口按键按下时触发
/// </summary>
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<Task>(() => RunRawCommands(hotkeyItem.Commands)));
break;
}
}
}
/// <summary>
/// 提示气泡对象
/// </summary>
private readonly ToolTip TTip = new ToolTip();
/// <summary>
/// 在指定控件上显示提示气泡
/// </summary>
/// <param name="message">消息</param>
/// <param name="control">控件</param>
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);
}
#endregion - General -
}
}