MainForm.cs 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576
  1. //Target:Winform
  2. //RefNuGet:jacobslusser.ScintillaNET,3.5.6
  3. using System;
  4. using System.Collections.Generic;
  5. using System.Diagnostics;
  6. using System.Drawing;
  7. using System.IO;
  8. using System.Linq;
  9. using System.Reflection;
  10. using System.Runtime.InteropServices;
  11. using System.Text;
  12. using System.Windows.Forms;
  13. using ScintillaNET;
  14. [assembly: AssemblyProduct("Filename Vertical Editor")]
  15. [assembly: AssemblyTitle("Edit multi file name at one time")]
  16. [assembly: AssemblyCopyright("Copyright © V 2016")]
  17. [assembly: AssemblyVersion("1.0.0.0")]
  18. [assembly: AssemblyFileVersion("1.0.0.0")]
  19. [assembly: Guid("C7102B4E-858D-470B-A3FB-630FC730EC7E")] //for ILRepack of ScintillaNET
  20. namespace FilenameVerticalEditor
  21. {
  22. internal class FilesNameComparer : IComparer<string>
  23. {
  24. //from http://www.cnblogs.com/linyechengwei/p/3224200.html with modify
  25. public int Compare(string x, string y)
  26. {
  27. var arr1 = x.Normalize(NormalizationForm.FormKC).ToCharArray();
  28. var arr2 = y.Normalize(NormalizationForm.FormKC).ToCharArray();
  29. int i = 0, j = 0;
  30. while (i < arr1.Length && j < arr2.Length)
  31. {
  32. if (char.IsDigit(arr1[i]) && char.IsDigit(arr2[j]))
  33. {
  34. string s1 = "", s2 = "";
  35. while (i < arr1.Length && char.IsDigit(arr1[i]))
  36. {
  37. s1 += arr1[i];
  38. i++;
  39. }
  40. while (j < arr2.Length && char.IsDigit(arr2[j]))
  41. {
  42. s2 += arr2[j];
  43. j++;
  44. }
  45. int a, b;
  46. // ReSharper disable RedundantAssignment
  47. var ca = int.TryParse(s1, out a);
  48. Debug.Assert(ca);
  49. var cb = int.TryParse(s2, out b);
  50. Debug.Assert(cb);
  51. // ReSharper restore RedundantAssignment
  52. if (a > b)
  53. {
  54. return 1;
  55. }
  56. if (a < b)
  57. {
  58. return -1;
  59. }
  60. }
  61. else
  62. {
  63. if (arr1[i] > arr2[j])
  64. {
  65. return 1;
  66. }
  67. if (arr1[i] < arr2[j])
  68. {
  69. return -1;
  70. }
  71. i++;
  72. j++;
  73. }
  74. }
  75. if (arr1.Length == arr2.Length)
  76. {
  77. return 0;
  78. }
  79. return arr1.Length > arr2.Length ? 1 : -1;
  80. }
  81. }
  82. internal static class UtilityKeyCode
  83. {
  84. //from http://stackoverflow.com/questions/5825820/how-to-capture-the-character-on-different-locale-keyboards-in-wpf-c
  85. private enum MapType : uint
  86. {
  87. MapvkVkToVsc = 0x0,
  88. //MapvkVScToVk = 0x1,
  89. //MapvkVkToChar = 0x2,
  90. //MapvkVScToVkex = 0x3,
  91. }
  92. [DllImport("user32.dll")]
  93. private static extern int ToUnicode(
  94. uint wVirtKey,
  95. uint wScanCode,
  96. byte[] lpKeyState,
  97. [Out, MarshalAs(UnmanagedType.LPWStr, SizeParamIndex = 4)]
  98. StringBuilder pwszBuff,
  99. int cchBuff,
  100. uint wFlags);
  101. [DllImport("user32.dll")]
  102. private static extern bool GetKeyboardState(byte[] lpKeyState);
  103. [DllImport("user32.dll")]
  104. private static extern uint MapVirtualKey(uint uCode, MapType uMapType);
  105. public static char ToChar(this Keys key)
  106. {
  107. var ch = '\0';
  108. var virtualKey = (int)key;
  109. var keyboardState = new byte[256];
  110. GetKeyboardState(keyboardState);
  111. var scanCode = MapVirtualKey((uint)virtualKey, MapType.MapvkVkToVsc);
  112. var stringBuilder = new StringBuilder(2);
  113. var result = ToUnicode((uint)virtualKey, scanCode, keyboardState, stringBuilder, stringBuilder.Capacity, 0);
  114. switch (result)
  115. {
  116. case -1:
  117. break;
  118. case 0:
  119. break;
  120. case 1:
  121. ch = stringBuilder[0];
  122. break;
  123. default:
  124. ch = stringBuilder[0];
  125. break;
  126. }
  127. return ch;
  128. }
  129. }
  130. internal static class UtilityString
  131. {
  132. public static int GetByteLength(this string me)
  133. {
  134. return Encoding.Default.GetByteCount(me);
  135. }
  136. public static string PadRightByByteCount(this string me, int totalWidth, char paddingChar = ' ')
  137. {
  138. var a = totalWidth - me.GetByteLength();
  139. return a > 0
  140. ? me + "".PadRight(a)
  141. : me;
  142. }
  143. }
  144. internal class ExScintilla : Scintilla
  145. {
  146. private class SelWrap
  147. {
  148. public int Begin { get; set; }
  149. public int Length { get; set; }
  150. }
  151. private enum MappedAction
  152. {
  153. Delete,
  154. Left,
  155. Right,
  156. MoveLeft,
  157. MoveRight,
  158. IncSelLen,
  159. DecSelLen,
  160. }
  161. public ExScintilla()
  162. {
  163. VirtualSpaceOptions = VirtualSpace.RectangularSelection;
  164. }
  165. protected override void OnKeyDown(KeyEventArgs e)
  166. {
  167. // ReSharper disable once AssignmentInConditionalExpression
  168. if (e.Alt || Selections.Count < 2)
  169. {
  170. base.OnKeyDown(e);
  171. return;
  172. }
  173. e.SuppressKeyPress = true;
  174. //LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL
  175. // >> EDITOR :: COLUMN MODE IMPLEMENT <<
  176. var tb = this;
  177. if (tb.Selections.Count < 2) return; // no in column mode
  178. e.SuppressKeyPress = true; //block input, handle by below code
  179. MappedAction? action = null;
  180. if (e.KeyCode == Keys.Delete && e.KeyData == Keys.Delete && e.KeyValue == 46)
  181. {
  182. action = MappedAction.Delete;
  183. }
  184. if (e.KeyCode == Keys.Left && e.KeyData == Keys.Left && e.KeyValue == 37)
  185. {
  186. action = MappedAction.Left;
  187. }
  188. if (e.KeyCode == Keys.Right && e.KeyData == Keys.Right && e.KeyValue == 39)
  189. {
  190. action = MappedAction.Right;
  191. }
  192. if (e.Shift && e.KeyData == (Keys.Shift | Keys.Left) && e.KeyCode == Keys.Left && e.KeyValue == 37)
  193. {
  194. action = MappedAction.MoveLeft;
  195. }
  196. if (e.Shift && e.KeyData == (Keys.Right | Keys.Shift) && e.KeyCode == Keys.Right && e.KeyValue == 39)
  197. {
  198. action = MappedAction.MoveRight;
  199. }
  200. if (e.Control && e.KeyData == (Keys.Control | Keys.Left) && e.KeyCode == Keys.Left && e.KeyValue == 37)
  201. {
  202. action = MappedAction.DecSelLen;
  203. }
  204. if (e.Control && e.KeyData == (Keys.Right | Keys.Control) && e.KeyCode == Keys.Right && e.KeyValue == 39)
  205. {
  206. action = MappedAction.IncSelLen;
  207. }
  208. string input;
  209. if (action.HasValue)
  210. {
  211. input = "\0";
  212. }
  213. else
  214. {
  215. input = e.KeyCode.ToChar().ToString();
  216. if (input == "\0")
  217. {
  218. //SystemSounds.Beep.Play();
  219. return;
  220. }
  221. }
  222. var selections = tb.Selections
  223. .OrderBy(p => p.Start)
  224. .Select(p => new SelWrap { Begin = p.Start, Length = p.End - p.Start })
  225. .ToArray();
  226. var contents = new string[selections.Length];
  227. for (var i = 0; i < contents.Length; i++)
  228. {
  229. contents[i] = input;
  230. }
  231. if (input == "\u0018" || input == "\u0003") //^X or ^C
  232. {
  233. for (var i = 0; i < selections.Length; i++)
  234. {
  235. contents[i] = tb.GetTextRange(selections[i].Begin, selections[i].Length);
  236. }
  237. Clipboard.SetText(string.Join(Environment.NewLine, contents));
  238. if (input == "\u0018") // ^X
  239. {
  240. for (var i = 0; i < selections.Length; i++)
  241. {
  242. contents[i] = "";
  243. }
  244. }
  245. else
  246. {
  247. return;
  248. }
  249. }
  250. if (input == "\u0016") //^V
  251. {
  252. for (var i = 0; i < selections.Length; i++)
  253. {
  254. contents[i] = "";
  255. }
  256. var clipLines = Clipboard.GetText().Split(new[] { Environment.NewLine }, StringSplitOptions.None);
  257. for (var i = 0; i < contents.Length && i < contents.Length; i++)
  258. {
  259. contents[i] = clipLines[i];
  260. }
  261. }
  262. //do process every caret(or selection)
  263. for (var i = 0; i < selections.Length; i++)
  264. {
  265. var item = selections[i];
  266. if (MappedAction.IncSelLen == action)
  267. {
  268. item.Length++;
  269. }
  270. else if (MappedAction.DecSelLen == action)
  271. {
  272. item.Length--;
  273. }
  274. else
  275. {
  276. if (action.HasValue) // before process, copy
  277. {
  278. for (var c = 0; c < selections.Length; c++)
  279. {
  280. contents[c] = tb.GetTextRange(selections[c].Begin, selections[c].Length);
  281. }
  282. }
  283. var removeSelected = item.Length > 0;
  284. if (removeSelected) //if has selected text, just clean
  285. {
  286. tb.DeleteRange(item.Begin, item.Length);
  287. for (var j = i + 1; j < selections.Length; j++)
  288. {
  289. selections[j].Begin -= item.Length;
  290. }
  291. }
  292. if (input == "\b") //backspace
  293. {
  294. if (item.Length != 0) continue;
  295. //delete a char before caret
  296. tb.DeleteRange(item.Begin - 1, 1);
  297. for (var j = i; j < selections.Length; j++)
  298. {
  299. selections[j].Begin--;
  300. }
  301. }
  302. else if (MappedAction.Delete == action)
  303. {
  304. selections[i].Length = 0;
  305. if (removeSelected) continue;
  306. //delete a char after caret
  307. tb.DeleteRange(item.Begin, 1);
  308. for (var j = i + 1; j < selections.Length; j++)
  309. {
  310. selections[j].Begin--;
  311. }
  312. }
  313. else if (MappedAction.Left == action || MappedAction.MoveLeft == action)
  314. {
  315. selections[i].Begin--;
  316. if (MappedAction.MoveLeft == action)
  317. {
  318. selections[i].Length = contents[i].Length;
  319. tb.InsertText(item.Begin, contents[i]);
  320. }
  321. else
  322. {
  323. tb.InsertText(item.Begin + 1, contents[i]);
  324. }
  325. for (var j = i + 1; j < selections.Length; j++) selections[j].Begin += contents[i].Length;
  326. }
  327. else if (MappedAction.Right == action || MappedAction.MoveRight == action)
  328. {
  329. selections[i].Begin++;
  330. if (MappedAction.MoveRight == action)
  331. {
  332. selections[i].Length = contents[i].Length;
  333. tb.InsertText(item.Begin, contents[i]);
  334. }
  335. else
  336. {
  337. tb.InsertText(item.Begin - 1, contents[i]);
  338. }
  339. for (var j = i + 1; j < selections.Length; j++) selections[j].Begin += contents[i].Length;
  340. }
  341. else
  342. {
  343. //just insert that
  344. tb.InsertText(item.Begin, contents[i]);
  345. item.Begin++;
  346. for (var j = i + 1; j < selections.Length; j++) selections[j].Begin += contents[i].Length;
  347. }
  348. }
  349. }
  350. //restore select status
  351. tb.ClearSelections();
  352. tb.SetSelection(selections[0].Begin, selections[0].Begin + selections[0].Length);
  353. for (var i = 1; i < selections.Length; i++)
  354. {
  355. var item = selections[i];
  356. tb.AddSelection(item.Begin, item.Begin + item.Length);
  357. }
  358. }
  359. private int _maxLineNumberCharLength1;
  360. protected override void OnTextChanged(EventArgs e)
  361. {
  362. var maxLineNumberCharLength = Lines.Count.ToString().Length;
  363. if (maxLineNumberCharLength == _maxLineNumberCharLength1)
  364. return;
  365. const int padding = 2;
  366. Margins[0].Width = TextWidth(Style.LineNumber, new string('9', maxLineNumberCharLength + 1)) + padding;
  367. _maxLineNumberCharLength1 = maxLineNumberCharLength;
  368. base.OnTextChanged(e);
  369. }
  370. }
  371. internal class MainForm : Form
  372. {
  373. [STAThread]
  374. private static void Main()
  375. {
  376. Application.EnableVisualStyles();
  377. Application.SetCompatibleTextRenderingDefault(false);
  378. Application.Run(new MainForm());
  379. }
  380. private MainForm()
  381. {
  382. InitUi();
  383. }
  384. private void InitUi()
  385. {
  386. //LLLLLLLLLLLLLL
  387. // >> FORMCNF <<
  388. ClientSize = new Size(640, 480);
  389. Text = Application.ProductName;
  390. AllowDrop = true;
  391. //LLLLLLLLLLLLLL
  392. // >> EDITOR <<
  393. var editor = new ExScintilla
  394. {
  395. Dock = DockStyle.Fill,
  396. Text = "<<Drag a *dir* in to here>>",
  397. };
  398. Controls.Add(editor);
  399. editor.Margins[0].Width = 16;
  400. editor.Margins[0].Type = MarginType.Number;
  401. editor.ReadOnly = true;
  402. editor.Styles[Style.Cpp.Default].Font = "fixedsys";
  403. editor.Styles[Style.Cpp.Default].Size = 12;
  404. //LLLLLLLLLLLLLL
  405. // >> TOOLBAR <<
  406. var toolbar = new ToolStrip { Dock = DockStyle.Top, GripStyle = ToolStripGripStyle.Hidden, RenderMode = ToolStripRenderMode.System };
  407. Controls.Add(toolbar);
  408. toolbar.Items.Add(new ToolStripLabel("Usage: Drop >> Edit >> Click ") { Font = new Font(Font, FontStyle.Italic | FontStyle.Bold) });
  409. toolbar.Items.Add(new ToolStripLabel("Go!") { Font = new Font(Font, FontStyle.Italic | FontStyle.Bold | FontStyle.Underline) });
  410. toolbar.Items.Add(new ToolStripLabel(" >> Click ") { Font = new Font(Font, FontStyle.Italic | FontStyle.Bold) });
  411. toolbar.Items.Add(new ToolStripLabel("Reset") { Font = new Font(Font, FontStyle.Italic | FontStyle.Bold | FontStyle.Underline) });
  412. toolbar.Items.Add(new ToolStripLabel(" for again") { Font = new Font(Font, FontStyle.Italic | FontStyle.Bold) });
  413. var mnuReset = new ToolStripButton("Reset");
  414. toolbar.Items.Add(mnuReset);
  415. mnuReset.Alignment = ToolStripItemAlignment.Right;
  416. mnuReset.Font = new Font(mnuReset.Font, FontStyle.Underline);
  417. var mnuGo = new ToolStripButton("Go!");
  418. toolbar.Items.Add(mnuGo);
  419. mnuGo.Alignment = ToolStripItemAlignment.Right;
  420. mnuGo.Font = new Font(mnuGo.Font, FontStyle.Underline);
  421. mnuReset.Click += delegate
  422. {
  423. editor.ReadOnly = false;
  424. editor.Text = "<<Drag a *dir* in to here>>";
  425. editor.ClearSelections();
  426. editor.ScrollRange(0, 0);
  427. editor.ReadOnly = true;
  428. };
  429. mnuGo.Click += delegate
  430. {
  431. if (editor.ReadOnly)
  432. {
  433. MessageBox.Show("What?");
  434. return;
  435. }
  436. var outputFilename = Path.GetTempFileName();
  437. var batFilename = outputFilename + ".bat";
  438. File.WriteAllText(batFilename, editor.Text, new UTF8Encoding(false));
  439. var p = new Process
  440. {
  441. StartInfo = new ProcessStartInfo()
  442. {
  443. FileName = "cmd",
  444. Arguments = $"/c chcp {Encoding.UTF8.CodePage} && prompt $G && {batFilename} > {outputFilename} 2>&1",
  445. CreateNoWindow = true,
  446. UseShellExecute = false,
  447. WindowStyle = ProcessWindowStyle.Hidden,
  448. }
  449. };
  450. p.Start();
  451. p.WaitForExit();
  452. File.Delete(batFilename);
  453. editor.Text = File.ReadAllText(outputFilename, Encoding.UTF8);
  454. File.Delete(outputFilename);
  455. editor.ReadOnly = true;
  456. };
  457. //LLLLLLLLLLLLLL
  458. // >> DRAGEVT <<
  459. DragEnter += delegate (object s, DragEventArgs e)
  460. {
  461. string[] items;
  462. e.Effect = e.Data.GetDataPresent(DataFormats.FileDrop)
  463. && (items = (string[])e.Data.GetData(DataFormats.FileDrop)).Length == 1
  464. && Directory.Exists(items[0])
  465. ? DragDropEffects.Link
  466. : DragDropEffects.None;
  467. };
  468. DragDrop += delegate (object s, DragEventArgs e)
  469. {
  470. var dir = ((string[])e.Data.GetData(DataFormats.FileDrop))[0];
  471. var files = Directory.GetFiles(dir, "*", SearchOption.AllDirectories)
  472. .Select(p => p.Substring(dir.Length + 1))
  473. .ToArray();
  474. Array.Sort(files, new FilesNameComparer());
  475. var lines = new List<string>(files.Length + 5)
  476. {
  477. $"@cd /d \"{dir}\"",
  478. ""
  479. };
  480. var width = files.Max(p => p.GetByteLength());
  481. lines.AddRange(files.Select(item => $"move \"{("" + item + "\"").PadRightByByteCount(width + 1)} \"{item}\""));
  482. editor.ReadOnly = false;
  483. editor.Text = string.Join(Environment.NewLine, lines);
  484. };
  485. }
  486. }
  487. }