1 using System;
2 using System.Collections.Generic;
3 using System.Linq;
4 using System.Text;
5 using System.Threading.Tasks;
6 using System.Xml.Serialization;
7 using System.Windows.Forms;
8 using Mesen.GUI.Forms;
9 using static Mesen.GUI.Forms.BaseForm;
10 
11 namespace Mesen.GUI.Config
12 {
13 	public class DebuggerShortcutsConfig
14 	{
15 		//Shared
16 		[ShortcutName("Increase Font Size")]
17 		public XmlKeys IncreaseFontSize = Keys.Control | Keys.Oemplus;
18 		[ShortcutName("Decrease Font Size")]
19 		public XmlKeys DecreaseFontSize = Keys.Control | Keys.OemMinus;
20 		[ShortcutName("Reset Font Size")]
21 		public XmlKeys ResetFontSize = Keys.Control | Keys.D0;
22 
23 		[ShortcutName("Go To...")]
24 		public XmlKeys GoTo = Keys.Control | Keys.G;
25 
26 		[ShortcutName("Find")]
27 		public XmlKeys Find = Keys.Control | Keys.F;
28 		[ShortcutName("Find Next")]
29 		public XmlKeys FindNext = Keys.F3;
30 		[ShortcutName("Find Previous")]
31 		public XmlKeys FindPrev = Keys.Shift | Keys.F3;
32 
33 		[ShortcutName("Undo")]
34 		public XmlKeys Undo = Keys.Control | Keys.Z;
35 		[ShortcutName("Copy")]
36 		public XmlKeys Copy = Keys.Control | Keys.C;
37 		[ShortcutName("Cut")]
38 		public XmlKeys Cut = Keys.Control | Keys.X;
39 		[ShortcutName("Paste")]
40 		public XmlKeys Paste = Keys.Control | Keys.V;
41 		[ShortcutName("Select All")]
42 		public XmlKeys SelectAll = Keys.Control | Keys.A;
43 
44 		[ShortcutName("Refresh")]
45 		public XmlKeys Refresh = Keys.F5;
46 
47 		[ShortcutName("Mark Selection as Code")]
48 		public XmlKeys MarkAsCode = Keys.Control | Keys.D1;
49 		[ShortcutName("Mark Selection as Data")]
50 		public XmlKeys MarkAsData = Keys.Control | Keys.D2;
51 		[ShortcutName("Mark Selection as Unidentified Code/Data")]
52 		public XmlKeys MarkAsUnidentified = Keys.Control | Keys.D3;
53 
54 		[ShortcutName("Go to All")]
55 		public XmlKeys GoToAll = Keys.Control | Keys.Oemcomma;
56 
57 		[ShortcutName("PPU Viewer: Toggle View")]
58 		public XmlKeys PpuViewer_ToggleView = Keys.Control | Keys.Q;
59 		[ShortcutName("PPU Viewer: Toggle Zoom")]
60 		public XmlKeys PpuViewer_ToggleZoom = Keys.Control | Keys.W;
61 
62 		[ShortcutName("Nametable Viewer: Add breakpoint (Tile)")]
63 		public XmlKeys PpuViewer_AddBreakpointTile = Keys.F9;
64 		[ShortcutName("Nametable Viewer: Add breakpoint (Attribute)")]
65 		public XmlKeys PpuViewer_AddBreakpointAttribute = Keys.F10;
66 
67 		[ShortcutName("Edit in Memory Viewer")]
68 		public XmlKeys CodeWindow_EditInMemoryViewer = Keys.F1;
69 		[ShortcutName("View in disassembly")]
70 		public XmlKeys MemoryViewer_ViewInDisassembly = Keys.None;
71 
72 		[ShortcutName("Open APU Viewer")]
73 		public XmlKeys OpenApuViewer = Keys.Control | Keys.U;
74 		[ShortcutName("Open Assembler")]
75 		public XmlKeys OpenAssembler = Keys.Control | Keys.K;
76 		[ShortcutName("Open Debugger")]
77 		public XmlKeys OpenDebugger = Keys.Control | Keys.D;
78 		[ShortcutName("Open Event Viewer")]
79 		public XmlKeys OpenEventViewer = Keys.Control | Keys.E;
80 		[ShortcutName("Open Memory Tools")]
81 		public XmlKeys OpenMemoryTools = Keys.Control | Keys.M;
82 		[ShortcutName("Open PPU Viewer")]
83 		public XmlKeys OpenPpuViewer = Keys.Control | Keys.P;
84 		[ShortcutName("Open Performance Profiler")]
85 		public XmlKeys OpenProfiler = Keys.Control | Keys.Y;
86 		[ShortcutName("Open Script Window")]
87 		public XmlKeys OpenScriptWindow = Keys.Control | Keys.N;
88 		[ShortcutName("Open Trace Logger")]
89 		public XmlKeys OpenTraceLogger = Keys.Control | Keys.J;
90 		[ShortcutName("Open Text Hooker")]
91 		public XmlKeys OpenTextHooker = Keys.Control | Keys.H;
92 		[ShortcutName("Open Watch Window")]
93 		public XmlKeys OpenWatchWindow = Keys.Control | Keys.W;
94 
95 		[ShortcutName("Open Nametable Viewer (Compact)")]
96 		public XmlKeys OpenNametableViewer = Keys.Control | Keys.D1;
97 		[ShortcutName("Open CHR Viewer (Compact)")]
98 		public XmlKeys OpenChrViewer = Keys.Control | Keys.D2;
99 		[ShortcutName("Open Sprite Viewer (Compact)")]
100 		public XmlKeys OpenSpriteViewer = Keys.Control | Keys.D3;
101 		[ShortcutName("Open Palette Viewer (Compact)")]
102 		public XmlKeys OpenPaletteViewer = Keys.Control | Keys.D4;
103 
104 		//Debugger window
105 		[ShortcutName("Reset")]
106 		public XmlKeys Reset = Keys.Control | Keys.R;
107 		[ShortcutName("Power Cycle")]
108 		public XmlKeys PowerCycle = Keys.Control | Keys.T;
109 
110 		[ShortcutName("Continue")]
111 		public XmlKeys Continue = Keys.F5;
112 		[ShortcutName("Break")]
113 		public XmlKeys Break = Keys.Control | Keys.Alt | Keys.Cancel;
114 		[ShortcutName("Toggle Break/Continue")]
115 		public XmlKeys ToggleBreakContinue = Keys.Escape;
116 		[ShortcutName("Step Into")]
117 		public XmlKeys StepInto = Keys.F11;
118 		[ShortcutName("Step Over")]
119 		public XmlKeys StepOver = Keys.F10;
120 		[ShortcutName("Step Out")]
121 		public XmlKeys StepOut = Keys.Shift | Keys.F11;
122 		[ShortcutName("Step Back")]
123 		public XmlKeys StepBack = Keys.Shift | Keys.F10;
124 
125 		[ShortcutName("Run one CPU Cycle")]
126 		public XmlKeys RunCpuCycle = Keys.None;
127 		[ShortcutName("Run one PPU Cycle")]
128 		public XmlKeys RunPpuCycle = Keys.F6;
129 		[ShortcutName("Run one scanline")]
130 		public XmlKeys RunPpuScanline = Keys.F7;
131 		[ShortcutName("Run one frame")]
132 		public XmlKeys RunPpuFrame = Keys.F8;
133 
134 		[ShortcutName("Break In...")]
135 		public XmlKeys BreakIn = Keys.Control | Keys.B;
136 		[ShortcutName("Break On...")]
137 		public XmlKeys BreakOn = Keys.Alt | Keys.B;
138 
139 		[ShortcutName("Find Occurrences")]
140 		public XmlKeys FindOccurrences = Keys.Control | Keys.Shift | Keys.F;
141 		[ShortcutName("Go To Program Counter")]
142 		public XmlKeys GoToProgramCounter = Keys.Alt | Keys.Multiply;
143 
144 		[ShortcutName("Toggle Verified Data Display")]
145 		public XmlKeys ToggleVerifiedData = Keys.Alt | Keys.D1;
146 		[ShortcutName("Toggle Unidentified Code/Data Display")]
147 		public XmlKeys ToggleUnidentifiedCodeData = Keys.Alt | Keys.D2;
148 
149 		[ShortcutName("Code Window: Set Next Statement")]
150 		public XmlKeys CodeWindow_SetNextStatement = Keys.Control | Keys.Shift | Keys.F10;
151 		[ShortcutName("Code Window: Edit Subroutine")]
152 		public XmlKeys CodeWindow_EditSubroutine = Keys.F4;
153 		[ShortcutName("Code Window: Edit Selected Code")]
154 		public XmlKeys CodeWindow_EditSelectedCode = Keys.None;
155 		[ShortcutName("Code Window: Edit Source File (Source View)")]
156 		public XmlKeys CodeWindow_EditSourceFile = Keys.F4;
157 		[ShortcutName("Code Window: Edit Label")]
158 		public XmlKeys CodeWindow_EditLabel = Keys.F2;
159 		[ShortcutName("Code Window: Navigate Back")]
160 		public XmlKeys CodeWindow_NavigateBack = Keys.Alt | Keys.Left;
161 		[ShortcutName("Code Window: Navigate Forward")]
162 		public XmlKeys CodeWindow_NavigateForward = Keys.Alt | Keys.Right;
163 		[ShortcutName("Code Window: Toggle Breakpoint")]
164 		public XmlKeys CodeWindow_ToggleBreakpoint = Keys.F9;
165 		[ShortcutName("Code Window: Disable/Enable Breakpoint")]
166 		public XmlKeys CodeWindow_DisableEnableBreakpoint = Keys.Control | Keys.F9;
167 		[ShortcutName("Code Window: Switch View (Disassembly / Source View)")]
168 		public XmlKeys CodeWindow_SwitchView = Keys.Control | Keys.Q;
169 
170 		[ShortcutName("Function List: Edit Label")]
171 		public XmlKeys FunctionList_EditLabel = Keys.F2;
172 		[ShortcutName("Function List: Add Breakpoint")]
173 		public XmlKeys FunctionList_AddBreakpoint = Keys.None;
174 		[ShortcutName("Function List: Find Occurrences")]
175 		public XmlKeys FunctionList_FindOccurrences = Keys.None;
176 
177 		[ShortcutName("Label List: Add Label")]
178 		public XmlKeys LabelList_Add = Keys.Insert;
179 		[ShortcutName("Label List: Edit Label")]
180 		public XmlKeys LabelList_Edit = Keys.F2;
181 		[ShortcutName("Label List: Delete Label")]
182 		public XmlKeys LabelList_Delete = Keys.Delete;
183 		[ShortcutName("Label List: Add Breakpoint")]
184 		public XmlKeys LabelList_AddBreakpoint = Keys.None;
185 		[ShortcutName("Label List: Add to Watch")]
186 		public XmlKeys LabelList_AddToWatch = Keys.None;
187 		[ShortcutName("Label List: Find Occurrences")]
188 		public XmlKeys LabelList_FindOccurrences = Keys.None;
189 		[ShortcutName("Label List: View in CPU Memory")]
190 		public XmlKeys LabelList_ViewInCpuMemory = Keys.None;
191 		[ShortcutName("Label List: View in [memory type]")]
192 		public XmlKeys LabelList_ViewInMemoryType = Keys.None;
193 
194 		[ShortcutName("Breakpoint List: Add Breakpoint")]
195 		public XmlKeys BreakpointList_Add = Keys.Insert;
196 		[ShortcutName("Breakpoint List: Edit Breakpoint")]
197 		public XmlKeys BreakpointList_Edit = Keys.F2;
198 		[ShortcutName("Breakpoint List: Go To Location")]
199 		public XmlKeys BreakpointList_GoToLocation = Keys.None;
200 		[ShortcutName("Breakpoint List: Delete Breakpoint")]
201 		public XmlKeys BreakpointList_Delete = Keys.Delete;
202 
203 		[ShortcutName("Watch List: Delete")]
204 		public XmlKeys WatchList_Delete = Keys.Delete;
205 		[ShortcutName("Watch List: Move Up")]
206 		public XmlKeys WatchList_MoveUp = Keys.Control | Keys.Up;
207 		[ShortcutName("Watch List: Move Down")]
208 		public XmlKeys WatchList_MoveDown = Keys.Control | Keys.Down;
209 
210 		[ShortcutName("Save Rom")]
211 		public XmlKeys SaveRom = Keys.Control | Keys.S;
212 		[ShortcutName("Save Rom As...")]
213 		public XmlKeys SaveRomAs = Keys.None;
214 		[ShortcutName("Save edits as IPS patch...")]
215 		public XmlKeys SaveEditAsIps = Keys.None;
216 		[ShortcutName("Revert PRG/CHR changes")]
217 		public XmlKeys RevertPrgChrChanges = Keys.None;
218 
219 		//Memory Tools
220 		[ShortcutName("Freeze")]
221 		public XmlKeys MemoryViewer_Freeze = Keys.Control | Keys.Q;
222 		[ShortcutName("Unfreeze")]
223 		public XmlKeys MemoryViewer_Unfreeze = Keys.Control | Keys.W;
224 		[ShortcutName("Add to Watch")]
225 		public XmlKeys MemoryViewer_AddToWatch = Keys.None;
226 		[ShortcutName("Edit Breakpoint")]
227 		public XmlKeys MemoryViewer_EditBreakpoint = Keys.None;
228 		[ShortcutName("Edit Label")]
229 		public XmlKeys MemoryViewer_EditLabel = Keys.None;
230 		[ShortcutName("Import")]
231 		public XmlKeys MemoryViewer_Import = Keys.Control | Keys.O;
232 		[ShortcutName("Export")]
233 		public XmlKeys MemoryViewer_Export = Keys.Control | Keys.S;
234 		[ShortcutName("View in CPU/PPU Memory")]
235 		public XmlKeys MemoryViewer_ViewInCpuMemory = Keys.None;
236 		[ShortcutName("View in [memory type]")]
237 		public XmlKeys MemoryViewer_ViewInMemoryType = Keys.None;
238 
239 		//Script Window
240 		[ShortcutName("Open Script")]
241 		public XmlKeys ScriptWindow_OpenScript = Keys.Control | Keys.N;
242 		[ShortcutName("Save Script")]
243 		public XmlKeys ScriptWindow_SaveScript = Keys.Control | Keys.S;
244 		[ShortcutName("Run Script")]
245 		public XmlKeys ScriptWindow_RunScript = Keys.F5;
246 		[ShortcutName("Stop Script")]
247 		public XmlKeys ScriptWindow_StopScript = Keys.Escape;
248 
GetShortcutDisplay(Keys keys)249 		public static string GetShortcutDisplay(Keys keys)
250 		{
251 			if(keys == Keys.None) {
252 				return "";
253 			} else {
254 				string keyString = new KeysConverter().ConvertToString(keys);
255 				return keyString.Replace("+None", "").Replace("Oemcomma", ",").Replace("Oemplus", "+").Replace("Oemtilde", "Tilde").Replace("OemMinus", "-").Replace("Cancel", "Break").Replace("Escape", "Esc");
256 			}
257 		}
258 
259 		private static Dictionary<WeakReference<ToolStripMenuItem>, string> _bindings = new Dictionary<WeakReference<ToolStripMenuItem>, string>();
260 		private static Dictionary<WeakReference<ToolStripMenuItem>, WeakReference<Control>> _parents = new Dictionary<WeakReference<ToolStripMenuItem>, WeakReference<Control>>();
RegisterMenuItem(ToolStripMenuItem item, Control parent, string fieldName)261 		public static void RegisterMenuItem(ToolStripMenuItem item, Control parent, string fieldName)
262 		{
263 			var weakRef = new WeakReference<ToolStripMenuItem>(item);
264 			_bindings[weakRef] = fieldName;
265 			_parents[weakRef] = new WeakReference<Control>(parent);
266 
267 			//Remove old references
268 			var dictCopy = new Dictionary<WeakReference<ToolStripMenuItem>, string>(_bindings);
269 
270 			//Iterate on a copy to avoid "collection was modified" error
271 			foreach(var kvp in dictCopy) {
272 				ToolStripMenuItem menuItem;
273 				if(!kvp.Key.TryGetTarget(out menuItem)) {
274 					_bindings.Remove(kvp.Key);
275 					_parents.Remove(kvp.Key);
276 				}
277 			}
278 		}
279 
UpdateMenus()280 		public static void UpdateMenus()
281 		{
282 			foreach(WeakReference<ToolStripMenuItem> itemRef in _bindings.Keys) {
283 				ToolStripMenuItem item;
284 				if(itemRef.TryGetTarget(out item)) {
285 					string fieldName = _bindings[itemRef];
286 					Control parent;
287 					_parents[itemRef].TryGetTarget(out parent);
288 					if(parent != null) {
289 						UpdateShortcutItem(item, parent, fieldName);
290 					}
291 				}
292 			}
293 		}
294 
ClearProcessCmdKeyHandler(ToolStripMenuItem item, Control parent)295 		public static void ClearProcessCmdKeyHandler(ToolStripMenuItem item, Control parent)
296 		{
297 			Form parentForm = parent.FindForm();
298 			if(parentForm is BaseForm) {
299 				(parentForm as BaseForm).OnProcessCmdKey -= ((ShortcutInfo)item.Tag).KeyHandler;
300 			}
301 			((ShortcutInfo)item.Tag).KeyHandler = null;
302 		}
303 
UpdateShortcutItem(ToolStripMenuItem item, Control parent, string fieldName)304 		public static void UpdateShortcutItem(ToolStripMenuItem item, Control parent, string fieldName)
305 		{
306 			if(item.Tag == null) {
307 				item.Tag = new ShortcutInfo() { KeyHandler = null, ShortcutKey = fieldName };
308 			} else if(((ShortcutInfo)item.Tag).KeyHandler != null) {
309 				ClearProcessCmdKeyHandler(item, parent);
310 			}
311 
312 			Keys keys = (XmlKeys)typeof(DebuggerShortcutsConfig).GetField(fieldName).GetValue(ConfigManager.Config.DebugInfo.Shortcuts);
313 			if((keys != Keys.None && !ToolStripManager.IsValidShortcut(keys)) || Program.IsMono) {
314 				//Support normally invalid shortcut keys as a shortcut
315 				item.ShortcutKeys = Keys.None;
316 				item.ShortcutKeyDisplayString = GetShortcutDisplay(keys);
317 
318 				Form parentForm = parent.FindForm();
319 				if(parentForm is BaseForm) {
320 					ProcessCmdKeyHandler onProcessCmdKeyHandler = (Keys keyData, ref bool processed) => {
321 						if(!processed && item.Enabled && parent.ContainsFocus && keyData == keys) {
322 							item.PerformClick();
323 							processed = true;
324 						}
325 					};
326 
327 					((ShortcutInfo)item.Tag).KeyHandler = onProcessCmdKeyHandler;
328 					(parentForm as BaseForm).OnProcessCmdKey += onProcessCmdKeyHandler;
329 				}
330 			} else {
331 				item.ShortcutKeys = keys;
332 				item.ShortcutKeyDisplayString = GetShortcutDisplay(keys);
333 			}
334 		}
335 	}
336 
337 	public static class ToolStripMenuItemExtensions
338 	{
InitShortcut(this ToolStripMenuItem item, Control parent, string fieldName)339 		public static void InitShortcut(this ToolStripMenuItem item, Control parent, string fieldName)
340 		{
341 			DebuggerShortcutsConfig.UpdateShortcutItem(item, parent, fieldName);
342 			DebuggerShortcutsConfig.RegisterMenuItem(item, parent, fieldName);
343 		}
344 	}
345 
346 	public class ShortcutInfo
347 	{
348 		public string ShortcutKey;
349 		public ProcessCmdKeyHandler KeyHandler;
350 	}
351 
352 	public class XmlKeys
353 	{
354 		private Keys _keys = Keys.None;
355 
XmlKeys()356 		public XmlKeys() { }
XmlKeys(Keys k)357 		public XmlKeys(Keys k) { _keys = k; }
358 
operator Keys(XmlKeys k)359 		public static implicit operator Keys(XmlKeys k)
360 		{
361 			return k._keys;
362 		}
363 
operator XmlKeys(Keys k)364 		public static implicit operator XmlKeys(Keys k)
365 		{
366 			return new XmlKeys(k);
367 		}
368 
369 		[XmlAttribute]
370 		public string Value
371 		{
372 			get { return _keys.ToString(); }
373 			set
374 			{
375 				try {
376 					Enum.TryParse<Keys>(value, out _keys);
377 				} catch(Exception) {
378 					_keys = Keys.None;
379 				}
380 			}
381 		}
382 	}
383 
384 	public class ShortcutNameAttribute : Attribute
385 	{
386 		public string Name { get; private set; }
387 
ShortcutNameAttribute(string name)388 		public ShortcutNameAttribute(string name)
389 		{
390 			this.Name = name;
391 		}
392 	}
393 }
394