//Target:Winform //RefNuGet:jacobslusser.ScintillaNET,3.5.6 using System; using System.Collections.Generic; using System.Diagnostics; using System.Drawing; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.InteropServices; using System.Text; using System.Windows.Forms; using ScintillaNET; [assembly: AssemblyProduct("Filename Vertical Editor")] [assembly: AssemblyTitle("Edit multi file name at one time")] [assembly: AssemblyCopyright("Copyright © V 2016")] [assembly: AssemblyVersion("1.0.0.0")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: Guid("C7102B4E-858D-470B-A3FB-630FC730EC7E")] //for ILRepack of ScintillaNET namespace FilenameVerticalEditor { internal class FilesNameComparer : IComparer { //from http://www.cnblogs.com/linyechengwei/p/3224200.html with modify public int Compare(string x, string y) { var arr1 = x.Normalize(NormalizationForm.FormKC).ToCharArray(); var arr2 = y.Normalize(NormalizationForm.FormKC).ToCharArray(); int i = 0, j = 0; while (i < arr1.Length && j < arr2.Length) { if (char.IsDigit(arr1[i]) && char.IsDigit(arr2[j])) { string s1 = "", s2 = ""; while (i < arr1.Length && char.IsDigit(arr1[i])) { s1 += arr1[i]; i++; } while (j < arr2.Length && char.IsDigit(arr2[j])) { s2 += arr2[j]; j++; } int a, b; // ReSharper disable RedundantAssignment var ca = int.TryParse(s1, out a); Debug.Assert(ca); var cb = int.TryParse(s2, out b); Debug.Assert(cb); // ReSharper restore RedundantAssignment if (a > b) { return 1; } if (a < b) { return -1; } } else { if (arr1[i] > arr2[j]) { return 1; } if (arr1[i] < arr2[j]) { return -1; } i++; j++; } } if (arr1.Length == arr2.Length) { return 0; } return arr1.Length > arr2.Length ? 1 : -1; } } internal static class UtilityKeyCode { //from http://stackoverflow.com/questions/5825820/how-to-capture-the-character-on-different-locale-keyboards-in-wpf-c private enum MapType : uint { MapvkVkToVsc = 0x0, //MapvkVScToVk = 0x1, //MapvkVkToChar = 0x2, //MapvkVScToVkex = 0x3, } [DllImport("user32.dll")] private static extern int ToUnicode( uint wVirtKey, uint wScanCode, byte[] lpKeyState, [Out, MarshalAs(UnmanagedType.LPWStr, SizeParamIndex = 4)] StringBuilder pwszBuff, int cchBuff, uint wFlags); [DllImport("user32.dll")] private static extern bool GetKeyboardState(byte[] lpKeyState); [DllImport("user32.dll")] private static extern uint MapVirtualKey(uint uCode, MapType uMapType); public static char ToChar(this Keys key) { var ch = '\0'; var virtualKey = (int)key; var keyboardState = new byte[256]; GetKeyboardState(keyboardState); var scanCode = MapVirtualKey((uint)virtualKey, MapType.MapvkVkToVsc); var stringBuilder = new StringBuilder(2); var result = ToUnicode((uint)virtualKey, scanCode, keyboardState, stringBuilder, stringBuilder.Capacity, 0); switch (result) { case -1: break; case 0: break; case 1: ch = stringBuilder[0]; break; default: ch = stringBuilder[0]; break; } return ch; } } internal static class UtilityString { public static int GetByteLength(this string me) { return Encoding.Default.GetByteCount(me); } public static string PadRightByByteCount(this string me, int totalWidth, char paddingChar = ' ') { var a = totalWidth - me.GetByteLength(); return a > 0 ? me + "".PadRight(a) : me; } } internal class ExScintilla : Scintilla { private class SelWrap { public int Begin { get; set; } public int Length { get; set; } } private enum MappedAction { Delete, Left, Right, MoveLeft, MoveRight, IncSelLen, DecSelLen, } public ExScintilla() { VirtualSpaceOptions = VirtualSpace.RectangularSelection; } protected override void OnKeyDown(KeyEventArgs e) { // ReSharper disable once AssignmentInConditionalExpression if (e.Alt || Selections.Count < 2) { base.OnKeyDown(e); return; } e.SuppressKeyPress = true; //LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL // >> EDITOR :: COLUMN MODE IMPLEMENT << var tb = this; if (tb.Selections.Count < 2) return; // no in column mode e.SuppressKeyPress = true; //block input, handle by below code MappedAction? action = null; if (e.KeyCode == Keys.Delete && e.KeyData == Keys.Delete && e.KeyValue == 46) { action = MappedAction.Delete; } if (e.KeyCode == Keys.Left && e.KeyData == Keys.Left && e.KeyValue == 37) { action = MappedAction.Left; } if (e.KeyCode == Keys.Right && e.KeyData == Keys.Right && e.KeyValue == 39) { action = MappedAction.Right; } if (e.Shift && e.KeyData == (Keys.Shift | Keys.Left) && e.KeyCode == Keys.Left && e.KeyValue == 37) { action = MappedAction.MoveLeft; } if (e.Shift && e.KeyData == (Keys.Right | Keys.Shift) && e.KeyCode == Keys.Right && e.KeyValue == 39) { action = MappedAction.MoveRight; } if (e.Control && e.KeyData == (Keys.Control | Keys.Left) && e.KeyCode == Keys.Left && e.KeyValue == 37) { action = MappedAction.DecSelLen; } if (e.Control && e.KeyData == (Keys.Right | Keys.Control) && e.KeyCode == Keys.Right && e.KeyValue == 39) { action = MappedAction.IncSelLen; } string input; if (action.HasValue) { input = "\0"; } else { input = e.KeyCode.ToChar().ToString(); if (input == "\0") { //SystemSounds.Beep.Play(); return; } } var selections = tb.Selections .OrderBy(p => p.Start) .Select(p => new SelWrap { Begin = p.Start, Length = p.End - p.Start }) .ToArray(); var contents = new string[selections.Length]; for (var i = 0; i < contents.Length; i++) { contents[i] = input; } if (input == "\u0018" || input == "\u0003") //^X or ^C { for (var i = 0; i < selections.Length; i++) { contents[i] = tb.GetTextRange(selections[i].Begin, selections[i].Length); } Clipboard.SetText(string.Join(Environment.NewLine, contents)); if (input == "\u0018") // ^X { for (var i = 0; i < selections.Length; i++) { contents[i] = ""; } } else { return; } } if (input == "\u0016") //^V { for (var i = 0; i < selections.Length; i++) { contents[i] = ""; } var clipLines = Clipboard.GetText().Split(new[] { Environment.NewLine }, StringSplitOptions.None); for (var i = 0; i < contents.Length && i < contents.Length; i++) { contents[i] = clipLines[i]; } } //do process every caret(or selection) for (var i = 0; i < selections.Length; i++) { var item = selections[i]; if (MappedAction.IncSelLen == action) { item.Length++; } else if (MappedAction.DecSelLen == action) { item.Length--; } else { if (action.HasValue) // before process, copy { for (var c = 0; c < selections.Length; c++) { contents[c] = tb.GetTextRange(selections[c].Begin, selections[c].Length); } } var removeSelected = item.Length > 0; if (removeSelected) //if has selected text, just clean { tb.DeleteRange(item.Begin, item.Length); for (var j = i + 1; j < selections.Length; j++) { selections[j].Begin -= item.Length; } } if (input == "\b") //backspace { if (item.Length != 0) continue; //delete a char before caret tb.DeleteRange(item.Begin - 1, 1); for (var j = i; j < selections.Length; j++) { selections[j].Begin--; } } else if (MappedAction.Delete == action) { selections[i].Length = 0; if (removeSelected) continue; //delete a char after caret tb.DeleteRange(item.Begin, 1); for (var j = i + 1; j < selections.Length; j++) { selections[j].Begin--; } } else if (MappedAction.Left == action || MappedAction.MoveLeft == action) { selections[i].Begin--; if (MappedAction.MoveLeft == action) { selections[i].Length = contents[i].Length; tb.InsertText(item.Begin, contents[i]); } else { tb.InsertText(item.Begin + 1, contents[i]); } for (var j = i + 1; j < selections.Length; j++) selections[j].Begin += contents[i].Length; } else if (MappedAction.Right == action || MappedAction.MoveRight == action) { selections[i].Begin++; if (MappedAction.MoveRight == action) { selections[i].Length = contents[i].Length; tb.InsertText(item.Begin, contents[i]); } else { tb.InsertText(item.Begin - 1, contents[i]); } for (var j = i + 1; j < selections.Length; j++) selections[j].Begin += contents[i].Length; } else { //just insert that tb.InsertText(item.Begin, contents[i]); item.Begin++; for (var j = i + 1; j < selections.Length; j++) selections[j].Begin += contents[i].Length; } } } //restore select status tb.ClearSelections(); tb.SetSelection(selections[0].Begin, selections[0].Begin + selections[0].Length); for (var i = 1; i < selections.Length; i++) { var item = selections[i]; tb.AddSelection(item.Begin, item.Begin + item.Length); } } private int _maxLineNumberCharLength1; protected override void OnTextChanged(EventArgs e) { var maxLineNumberCharLength = Lines.Count.ToString().Length; if (maxLineNumberCharLength == _maxLineNumberCharLength1) return; const int padding = 2; Margins[0].Width = TextWidth(Style.LineNumber, new string('9', maxLineNumberCharLength + 1)) + padding; _maxLineNumberCharLength1 = maxLineNumberCharLength; base.OnTextChanged(e); } } internal class MainForm : Form { [STAThread] private static void Main() { Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault(false); Application.Run(new MainForm()); } private MainForm() { InitUi(); } private void InitUi() { //LLLLLLLLLLLLLL // >> FORMCNF << ClientSize = new Size(640, 480); Text = Application.ProductName; AllowDrop = true; //LLLLLLLLLLLLLL // >> EDITOR << var editor = new ExScintilla { Dock = DockStyle.Fill, Text = "<>", }; Controls.Add(editor); editor.Margins[0].Width = 16; editor.Margins[0].Type = MarginType.Number; editor.ReadOnly = true; editor.Styles[Style.Cpp.Default].Font = "fixedsys"; editor.Styles[Style.Cpp.Default].Size = 12; //LLLLLLLLLLLLLL // >> TOOLBAR << var toolbar = new ToolStrip { Dock = DockStyle.Top, GripStyle = ToolStripGripStyle.Hidden, RenderMode = ToolStripRenderMode.System }; Controls.Add(toolbar); toolbar.Items.Add(new ToolStripLabel("Usage: Drop >> Edit >> Click ") { Font = new Font(Font, FontStyle.Italic | FontStyle.Bold) }); toolbar.Items.Add(new ToolStripLabel("Go!") { Font = new Font(Font, FontStyle.Italic | FontStyle.Bold | FontStyle.Underline) }); toolbar.Items.Add(new ToolStripLabel(" >> Click ") { Font = new Font(Font, FontStyle.Italic | FontStyle.Bold) }); toolbar.Items.Add(new ToolStripLabel("Reset") { Font = new Font(Font, FontStyle.Italic | FontStyle.Bold | FontStyle.Underline) }); toolbar.Items.Add(new ToolStripLabel(" for again") { Font = new Font(Font, FontStyle.Italic | FontStyle.Bold) }); var mnuReset = new ToolStripButton("Reset"); toolbar.Items.Add(mnuReset); mnuReset.Alignment = ToolStripItemAlignment.Right; mnuReset.Font = new Font(mnuReset.Font, FontStyle.Underline); var mnuGo = new ToolStripButton("Go!"); toolbar.Items.Add(mnuGo); mnuGo.Alignment = ToolStripItemAlignment.Right; mnuGo.Font = new Font(mnuGo.Font, FontStyle.Underline); mnuReset.Click += delegate { editor.ReadOnly = false; editor.Text = "<>"; editor.ClearSelections(); editor.ScrollRange(0, 0); editor.ReadOnly = true; }; mnuGo.Click += delegate { if (editor.ReadOnly) { MessageBox.Show("What?"); return; } var outputFilename = Path.GetTempFileName(); var batFilename = outputFilename + ".bat"; File.WriteAllText(batFilename, editor.Text, new UTF8Encoding(false)); var p = new Process { StartInfo = new ProcessStartInfo() { FileName = "cmd", Arguments = $"/c chcp {Encoding.UTF8.CodePage} && prompt $G && {batFilename} > {outputFilename} 2>&1", CreateNoWindow = true, UseShellExecute = false, WindowStyle = ProcessWindowStyle.Hidden, } }; p.Start(); p.WaitForExit(); File.Delete(batFilename); editor.Text = File.ReadAllText(outputFilename, Encoding.UTF8); File.Delete(outputFilename); editor.ReadOnly = true; }; //LLLLLLLLLLLLLL // >> DRAGEVT << DragEnter += delegate (object s, DragEventArgs e) { string[] items; e.Effect = e.Data.GetDataPresent(DataFormats.FileDrop) && (items = (string[])e.Data.GetData(DataFormats.FileDrop)).Length == 1 && Directory.Exists(items[0]) ? DragDropEffects.Link : DragDropEffects.None; }; DragDrop += delegate (object s, DragEventArgs e) { var dir = ((string[])e.Data.GetData(DataFormats.FileDrop))[0]; var files = Directory.GetFiles(dir, "*", SearchOption.AllDirectories) .Select(p => p.Substring(dir.Length + 1)) .ToArray(); Array.Sort(files, new FilesNameComparer()); var lines = new List(files.Length + 5) { $"@cd /d \"{dir}\"", "" }; var width = files.Max(p => p.GetByteLength()); lines.AddRange(files.Select(item => $"move \"{("" + item + "\"").PadRightByByteCount(width + 1)} \"{item}\"")); editor.ReadOnly = false; editor.Text = string.Join(Environment.NewLine, lines); }; } } }