Repository navigation
Expand file tree
/
Copy pathfrmMain.vb
More file actions
1194 lines (936 loc) · 45.1 KB
/
Copy pathfrmMain.vb
File metadata and controls
1194 lines (936 loc) · 45.1 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
Imports System.IO
Imports System.Runtime.InteropServices
Imports System.Threading
Imports IniParser
Module GlobalVariables
Public _iniDict As Model.IniData
Public _iniParserObj As New FileIniDataParser
Public _ini As iniWrapper
Public _MainFormObj As frmMain
Public _initialized As Boolean = False
Public _pathList As ListViewHelper
Public _favoritesList As List(Of String)
Public _parentPathCharCount As Integer = 0
Public _fileNameCharCount As Integer = 0
Public _fullPathCharCount As Integer = 0
Public _MaxPathLength As Integer
Public _OriginalDPI As Integer
Public _DPI As Integer
Public _FontName As String
Public _FontSize As Single
Public _lastAction As UndoData
Public Const _MinX As Single = 11.5
Public Const _MinY As Single = 6.5
Public Const _sLwidth As Single = 1.25
Public _FolderInfo As FolderSelect
<STAThread>
Public Sub Main() 'Required to enable High DPI mode for winforms
Application.EnableVisualStyles()
Application.SetCompatibleTextRenderingDefault(False)
Application.Run(New frmMain())
End Sub
'___________________________________________Used for vertical centering RTB text boxes
Public Const EM_SETRECT As Integer = &HB3
<StructLayout(LayoutKind.Sequential)>
Public Structure RECT
Public Left As Integer
Public Top As Integer
Public Right As Integer
Public Bottom As Integer
End Structure
<DllImport("user32.dll")>
Public Function SendMessage(hWnd As IntPtr, msg As Integer, wParam As IntPtr, ByRef lParam As RECT) As IntPtr
End Function
'_______________________________________
<DllImport("user32.dll")>
Public Function SendMessage(hWnd As IntPtr, Msg As UInteger, wParam As IntPtr, lParam As IntPtr) As IntPtr
End Function
Public Sub CenterRTBvertically(rtb As RichTextBox)
Dim fontHeight As Integer = TextRenderer.MeasureText("A", rtb.Font).Height
Dim topPadding As Integer = (rtb.Height - fontHeight) \ 2
Dim rect As New RECT() With {
.Left = 3,
.Top = If(topPadding > 0, topPadding, 0),
.Right = rtb.Width - 1,
.Bottom = rtb.Height
}
SendMessage(rtb.Handle, EM_SETRECT, IntPtr.Zero, rect)
End Sub
Public Sub AlignControlToRight(margin As Integer, ctrl As Control, frm As Form, Optional WidenTo As Boolean = False)
If WidenTo = False Then
ctrl.Left = frm.ClientSize.Width - ctrl.Width - margin
Else
ctrl.Width = frm.ClientSize.Width - margin - ctrl.Left
End If
End Sub
Public Sub AlignControlToRight(margin As Integer, ctrl As Control(), frm As Form, Optional WidenTo As Boolean = False)
If WidenTo = False Then
Dim l1 As Integer = ctrl(0).Left
ctrl(0).Left = frm.ClientSize.Width - ctrl(0).Width - margin
Dim offset As Integer = ctrl(0).Left - l1
For i As Integer = 1 To ctrl.Length - 1
ctrl(i).Left = ctrl(i).Left + offset
Next
Else
ctrl(0).Width = frm.ClientSize.Width - margin - ctrl(0).Left
End If
End Sub
Public Class ToolTipCustom
Dim TIP As New ToolTip
Public Property DisplayLength_ms As Integer
Get
Return TIP.AutoPopDelay
End Get
Set(value As Integer)
TIP.AutoPopDelay = DisplayLength_ms
End Set
End Property
Sub New()
TIP.AutoPopDelay = 10000
TIP.InitialDelay = 150
TIP.ReshowDelay = 100
End Sub
Sub Add(ctrl As Control, captionText As String)
TIP.SetToolTip(ctrl, captionText)
End Sub
Sub AddWorkAround(ctrl As Control, captionText As String)
AddHandler ctrl.MouseHover, Sub() TIP.SetToolTip(ctrl, captionText)
End Sub
End Class
End Module
Public Class frmMain
Private _CurrentLVitem As ListViewItem
Private _StatusLabel_Main As New ToolStripStatusLabel
Private _StatusLabel_FileCount As New ToolStripStatusLabel
Private _rtbHelpBox As New RichTextBox
Private _ToolTips As New ToolTipCustom
Private _ConfirmChangeOnEnter As Boolean
Private cts As CancellationTokenSource
Private _hook As ListViewHook
'________________________________________________Allows auto expanding the windows when double clicking the right edge
Private Const WM_NCLBUTTONDBLCLK As Integer = &HA3
Private Const HTRIGHT As Integer = 11 ' Right edge
Private Const HTSIZEBOX As Integer = 4 ' Bottom-right corner (if needed)
Protected Overrides Sub WndProc(ByRef m As Message)
If m.Msg = WM_NCLBUTTONDBLCLK Then
Dim hitTest As Integer = m.WParam.ToInt32()
If hitTest = HTRIGHT Then
'
ExtendWindow()
Return ' Optionally suppress default behavior (e.g., resizing)
End If
End If
MyBase.WndProc(m)
End Sub
'___________________________________________________
<Runtime.InteropServices.DllImport("user32.dll")>
Private Shared Function GetDpiForWindow(hwnd As IntPtr) As UInteger
End Function
Protected Overrides Sub OnDpiChanged(e As DpiChangedEventArgs)
MyBase.OnDpiChanged(e)
DPIrescale()
End Sub
Private Sub DPIrescale() 'Certain dimensional parameters require manual correction upon DPI change
_DPI = Me.DeviceDpi
Me.MinimumSize = New Size(_MinX * _DPI, _MinY * _DPI)
Me.StatusStripMain.Items(2).Size = New Size(_sLwidth * _DPI, 26)
_pathList.UpdateColumnsFromDPIchange(_DPI)
rtbOriginalName.Font = Nothing 'workaround for winforms rendering bug
Dim correctedFontSize As Single = (_DPI / _OriginalDPI) * _FontSize 'Rich text box scaling is stupid and this is a workaround
rtbOriginalName.Font = New Font(_FontName, correctedFontSize, FontStyle.Regular)
CenterRTBvertically(rtbOriginalName)
End Sub
Public Sub FixControlScale(ctrl As Control, sX As Single, Optional fontSpec As Font = Nothing, Optional changeSize As Boolean = True)
ctrl.Top = ctrl.Top * sX
ctrl.Left = ctrl.Left * sX
Dim typeStr As String = ctrl.GetType.ToString.Replace("System.Windows.Forms.", "")
If changeSize = True Then
Select Case typeStr
Case "Button", "RichTextBox"
ctrl.Size = New Size(ctrl.Width * sX, ctrl.Height * sX)
Case "TextBox", "ComboBox"
ctrl.Width = ctrl.Width * sX
Case "Label", "CheckBox", "LinkLabel"
Case Else
Stop
End Select
End If
If fontSpec IsNot Nothing Then
ctrl.Font = fontSpec
End If
End Sub
Private Class ListViewHook
Inherits NativeWindow
Private Const WM_NOTIFY As Integer = &H4E
Private Const HDN_FIRST As Integer = -300
Private Const HDN_DIVIDERDBLCLICKA As Integer = HDN_FIRST - 5
Private Const HDN_DIVIDERDBLCLICKW As Integer = HDN_FIRST - 25
<StructLayout(LayoutKind.Sequential)>
Private Structure NMHDR
Public hwndFrom As IntPtr
Public idFrom As IntPtr
Public code As Integer
End Structure
<StructLayout(LayoutKind.Sequential)>
Private Structure NMHEADER
Public hdr As NMHDR
Public iItem As Integer ' Zero-based column index that was double clicked
Public iButton As Integer
Public pItem As IntPtr
End Structure
Public Sub New(lv As ListView)
AssignHandle(lv.Handle)
End Sub
Protected Overrides Sub WndProc(ByRef m As Message)
If m.Msg = WM_NOTIFY Then
Dim nmhdr As NMHDR = DirectCast(Marshal.PtrToStructure(m.LParam, GetType(NMHDR)), NMHDR)
If nmhdr.code = HDN_DIVIDERDBLCLICKA OrElse nmhdr.code = HDN_DIVIDERDBLCLICKW Then
Dim nmheader As NMHEADER = DirectCast(Marshal.PtrToStructure(m.LParam, GetType(NMHEADER)), NMHEADER)
Dim columnIndex As Integer = nmheader.iItem
_pathList.ExpandColumnToFitContents(columnIndex)
Return
End If
End If
MyBase.WndProc(m)
End Sub
End Class
Private Sub ExtendWindow()
Dim sX As Single = _DPI / _OriginalDPI 'Apparently TextRenderer is not affected by DPI change events and so it has to be corrected for.
Dim curFont As New Font(_FontName, _FontSize)
Dim p1 As Integer = rtbOriginalName.Left + TextRenderer.MeasureText(rtbOriginalName.Text, curFont).Width
Dim columnStart As Integer = (lvMain.Left + lvMain.Columns(0).Width + lvMain.Columns(1).Width) / sX
For i As Integer = 0 To lvMain.Items.Count - 1
Dim p As Integer = TextRenderer.MeasureText(_pathList.GetCellEntry(i, "Path"), curFont).Width + columnStart '
If p > p1 Then
p1 = p
End If
Next
Me.Width = p1 * sX + 36
End Sub
Public Sub New()
AutoScaleMode = AutoScaleMode.None 'Extremely important to have it set to none before InitializeComponent is run
InitializeComponent()
Dim primScreen As Screen = Screen.PrimaryScreen
Me.StartPosition = FormStartPosition.Manual
Me.Location = New Point(
primScreen.WorkingArea.Left + (primScreen.WorkingArea.Width - Me.Width) \ 2,
primScreen.WorkingArea.Top + (primScreen.WorkingArea.Height - Me.Height) \ 2
)
_MainFormObj = Me
Try
_ini = New iniWrapper(iniPath:="\resources\User Options.ini", AutoGenerateIfMissing:=True)
Catch ex As Exception
MsgBox($"Settings file data is missing from \resources folder.{vbCrLf}Re-extract utility if necessary.", MsgBoxStyle.Critical, "ERROR")
End
End Try
_hook = New ListViewHook(lvMain)
'Me.AutoScaleMode = AutoScaleMode.Dpi
_DPI = Me.DeviceDpi
_OriginalDPI = _DPI
End Sub
Private Async Sub frmMain_Load(sender As Object, e As EventArgs) Handles Me.Load
Me.Icon = My.Resources.icon
Dim sX As Single = _DPI / 96 'scaling Factor
Dim defaultFont As New Font("Microsoft Sans Seriff", 8.25)
FixControlScale(tbNewName, sX,, False)
FixControlScale(lab_SearchFolder, sX)
FixControlScale(lab_Name, sX, defaultFont) 'default fonts don't get autoscaled apparently
FixControlScale(lab_Total, sX, defaultFont)
FixControlScale(lab_SelectFolder, sX)
FixControlScale(lblMaxPathLength, sX)
FixControlScale(lblCharCountNewName, sX)
FixControlScale(lblCharCountNewName2, sX)
FixControlScale(lblCharCountOriginalName, sX)
FixControlScale(lblCharCountOriginalName2, sX)
FixControlScale(btnCancelScan, sX, defaultFont)
FixControlScale(btnChangeFileName, sX, defaultFont)
FixControlScale(btnChangeFolderName, sX, defaultFont)
FixControlScale(btnUndo, sX, defaultFont)
FixControlScale(btnOptions, sX, defaultFont)
FixControlScale(btnSelectFolder, sX, defaultFont)
FixControlScale(btnFavoriteFolder, sX, defaultFont)
FixControlScale(btnUnfavoriteFolder, sX, defaultFont)
FixControlScale(btnScanFolder, sX, defaultFont)
FixControlScale(btnLeftFolder, sX)
FixControlScale(btnRightFolder, sX)
FixControlScale(cbShowHelp, sX) : cbShowHelp.Width = cbShowHelp.Width * sX : cbShowHelp.Height = cbShowHelp.Height * sX
FixControlScale(cbSearchSubfolders, sX, defaultFont)
FixControlScale(tbSearchFolder, sX)
FixControlScale(tbMaxPathLength, sX, defaultFont)
FixControlScale(rtbOriginalName, sX)
FixControlScale(cmbBoxFavorites, sX)
With Me
Dim winSize As New Size(_MinX * _DPI, _MinY * _DPI)
.Size = winSize
.MinimumSize = winSize
End With
Dim LeftMargin As Integer = 8 * sX
Dim RightMargin As Integer = 8 * sX
AlignControlToRight(RightMargin, {tbMaxPathLength, lblMaxPathLength}, Me)
AlignControlToRight(RightMargin, tbNewName, Me, True)
AlignControlToRight(RightMargin, rtbOriginalName, Me, True)
AlignControlToRight(RightMargin, {cbShowHelp, btnOptions}, Me)
btnScanFolder.Left = LeftMargin
tbSearchFolder.Left = LeftMargin
lab_SearchFolder.Left = LeftMargin
btnCancelScan.Hide()
btnCancelScan.Location = btnScanFolder.Location
btnUnfavoriteFolder.Hide()
btnUnfavoriteFolder.Location = btnFavoriteFolder.Location
btnChangeFolderName.Hide()
btnChangeFolderName.Location = btnChangeFileName.Location
Me.Controls.Add(_rtbHelpBox)
With _rtbHelpBox
.ReadOnly = True
.BackColor = SystemColors.ControlLightLight
.Margin = New Padding(16)
.Dock = DockStyle.None
.Left = LeftMargin - 1
.Width = Me.ClientSize.Width - LeftMargin - RightMargin + 4 * sX
.Top = cbShowHelp.Bottom + 4 * sX
.Height = Me.ClientSize.Height - .Top - StatusStripMain.Height - 2 * sX
.Anchor = AnchorStyles.Left Or AnchorStyles.Right Or AnchorStyles.Bottom Or AnchorStyles.Top
.BringToFront()
.Hide()
End With
Dim statusBarHeight As Single = 24
With StatusStripMain
_StatusLabel_Main.Name = "statusLabel_Main"
_StatusLabel_Main.AutoSize = False
_StatusLabel_Main.Size = New Size(400 * sX, statusBarHeight * sX)
_StatusLabel_Main.Text = "Ready"
_StatusLabel_Main.TextAlign = ContentAlignment.MiddleLeft
_StatusLabel_Main.Alignment = ToolStripItemAlignment.Right
_StatusLabel_Main.Margin = New Padding(6 * sX, 0, 0, 0)
_StatusLabel_FileCount.Name = "statusLabel_FileCount"
_StatusLabel_FileCount.Size = New Size(_sLwidth * _DPI, statusBarHeight * sX)
_StatusLabel_FileCount.AutoSize = False
_StatusLabel_FileCount.Text = "Files Found: "
_StatusLabel_FileCount.TextAlign = ContentAlignment.MiddleLeft
_StatusLabel_FileCount.Alignment = ToolStripItemAlignment.Right
_StatusLabel_FileCount.Margin = New Padding(0, 0, 6 * sX, 0)
Dim emptyStatusLabel As New ToolStripStatusLabel()
emptyStatusLabel.Text = String.Empty
emptyStatusLabel.Spring = True
.Items.Add(_StatusLabel_Main)
.Items.Add(emptyStatusLabel)
.Items.Add(_StatusLabel_FileCount)
.Height = statusBarHeight * sX
'.AutoSize = False
End With
With lvMain
.CheckBoxes = False
.FullRowSelect = True
.AllowColumnReorder = False
.MultiSelect = False
.View = View.Details
.Top = .Top * sX
.Left = LeftMargin
.Width = Me.ClientSize.Width - LeftMargin - RightMargin
.Height = Me.ClientSize.Height - .Top - StatusStripMain.Height - 6 * sX
.Anchor = AnchorStyles.Left Or AnchorStyles.Right Or AnchorStyles.Bottom Or AnchorStyles.Top
.Columns.Add("Path Length", 65, HorizontalAlignment.Left)
.Columns.Add("Name Length", 65, HorizontalAlignment.Left)
.Columns.Add("Path", 1500, HorizontalAlignment.Left)
End With
With cmbBoxFavorites
Dim favArr() As String = _ini.GetIterativeEntryArray("FavFolder", "FavoriteFolders")
.Items.Add("Favorite Folders")
_favoritesList = New List(Of String)(favArr)
If favArr.Length <> 0 Then
For i As Integer = 0 To favArr.Length - 1
.Items.Add(favArr(i))
Next
End If
.SelectedIndex = 0
.DropDownStyle = ComboBoxStyle.DropDownList
.DrawMode = DrawMode.OwnerDrawFixed
End With
_pathList = New ListViewHelper(lvMain, _ini.Entry("DefaultTableConfig"), True)
_pathList.ApplyConfig(_ini.Entry("TableConfig"))
_pathList.AddContextMenuFunction("Show in file explorer", AddressOf ShowInExplorer, "Path")
_pathList.AddContextMenuFunction("Save view", AddressOf SaveTableView)
_ConfirmChangeOnEnter = _ini.Entry("ConfirmChangeOnEnter")
cbSearchSubfolders.Checked = _ini.Entry("IncludeSubFolders")
tbNewName.Anchor = AnchorStyles.Left Or AnchorStyles.Top Or AnchorStyles.Right
With rtbOriginalName
.ReadOnly = True
.BackColor = Color.FromArgb(248, 248, 248)
.Cursor = Cursors.Arrow
.Anchor = AnchorStyles.Left Or AnchorStyles.Top Or AnchorStyles.Right
.BorderStyle = BorderStyle.None
.Multiline = False
'AlignRightEdgeOfControl(rtbOriginalName, lvMain)
End With
tbSearchFolder.Text = _ini.Entry("LastSelectedFolder")
tbMaxPathLength.Text = _ini.Entry("MaxPathLength")
tbMaxPathLength.TextAlign = HorizontalAlignment.Right
_ToolTips.Add(tbMaxPathLength, "The max file path length in Windows is effectively 259 characters. Though technically it's 260 when including the null terminator of a string.")
_ToolTips.Add(lblMaxPathLength, "The max file path length in Windows is effectively 259 characters. Though technically it's 260 when including the null terminator of a string.")
_ToolTips.Add(lblCharCountOriginalName, "Left number is the total path length. Right number is the current name length.")
_ToolTips.Add(lblCharCountNewName, "Left number is the total path length. Right number is the current name length.")
_ToolTips.Add(lblCharCountOriginalName2, "Left number is the total path length. Right number is the current name length.")
_ToolTips.Add(lblCharCountNewName2, "Left number is the total path length. Right number is the current name length.")
_ToolTips.Add(btnUndo, "You can only undo one change.")
Dim adjustedFont As Font
Try
_FontSize = _ini.Entry("FontSize")
_FontName = _ini.Entry("FontName")
Dim installedFont As FontFamily = FontFamily.Families.FirstOrDefault(Function(f) f.Name.Equals(_FontName, StringComparison.OrdinalIgnoreCase))
If installedFont Is Nothing Then
adjustedFont = New Font("Microsoft Sans Serif", _FontSize, FontStyle.Regular)
End If
adjustedFont = New Font(_FontName, _FontSize, FontStyle.Regular)
cmbBoxFavorites.Font = adjustedFont
lvMain.Font = adjustedFont
tbSearchFolder.Font = adjustedFont
tbMaxPathLength.Font = adjustedFont
rtbOriginalName.Font = adjustedFont
tbNewName.Font = adjustedFont
lblCharCountNewName.Font = adjustedFont
lblCharCountNewName2.Font = adjustedFont
lblCharCountOriginalName.Font = adjustedFont
lblCharCountOriginalName2.Font = adjustedFont
Catch ex As Exception
SetStatus("Font could not be changed.")
End Try
CenterRTBvertically(rtbOriginalName)
btnScanFolder.Focus()
tbMaxPathLength.TabIndex = 39
btnSelectFolder.TabIndex = 40
btnScanFolder.TabIndex = 41
tbNewName.TabIndex = 50
btnChangeFileName.TabIndex = 51
btnChangeFolderName.TabIndex = 51
Me.Text = "LongPathFixer v1.0 - Long file path search and rename"
If _ini.Entry("Start11TaskBarFix") = True Then
Me.WindowState = FormWindowState.Minimized
Me.WindowState = FormWindowState.Normal
End If
_initialized = True
End Sub
Public Sub SaveTableView()
Try
_ini.Entry("TableConfig") = _pathList.GetConfigStr()
SetStatus("Table view saved")
Catch ex As Exception
SetStatus("Error saving table view")
End Try
End Sub
Public Sub SetStatus(statusStr As String, Optional mode As String = "[main,count]", Optional flashStatus As Boolean = False)
Dim StatusLabel As ToolStripStatusLabel
Select Case mode
Case "[main,count]", "main"
StatusLabel = _StatusLabel_Main
Case "count"
statusStr = "Files Found: " & statusStr
StatusLabel = _StatusLabel_FileCount
Case Else
Stop
End Select
If _StatusStripMain.InvokeRequired = True Then
_StatusStripMain.Invoke(Sub() StatusLabel.Text = statusStr)
Else
StatusLabel.Text = statusStr
End If
Application.DoEvents() 'UI doesn't always refresh without this command
If flashStatus = True Then
Dim black As Color = Color.Black
Dim red As Color = Color.Red
Dim act As Action = Sub()
For i = 0 To 5
_StatusStripMain.Invoke(Sub() StatusLabel.ForeColor = red)
Task.Delay(100).Wait()
_StatusStripMain.Invoke(Sub() StatusLabel.ForeColor = black)
Task.Delay(100).Wait()
Next
End Sub
Task.Run(act)
End If
End Sub
Private Sub btnSelectFolder_Click(sender As Object, e As EventArgs) Handles btnSelectFolder.Click
Using dialog As New WindowsAPICodePack.Dialogs.CommonOpenFileDialog()
dialog.IsFolderPicker = True
dialog.Title = "Select a Folder"
If dialog.ShowDialog() = WindowsAPICodePack.Dialogs.CommonFileDialogResult.Ok Then
tbSearchFolder.Text = dialog.FileName
End If
End Using
_ini.Entry("LastSelectedFolder") = tbSearchFolder.Text
End Sub
Private Sub btn_Options_Click(sender As Object, e As EventArgs) Handles btnOptions.Click
'_ini.OpenINI()
Dim optMenu As New frmOptions(_DPI)
optMenu.ShowDialog(Me)
End Sub
Private Sub btnScanFolder_Click(sender As Object, e As EventArgs) Handles btnScanFolder.Click
_pathList.ResetColumnSort()
btnScanFolder.Hide()
btnCancelScan.Show()
cts = New CancellationTokenSource()
ScanFolder(cts)
_lastAction.newList = True
_ini.Entry("TableConfig") = _pathList.GetConfigStr
_ini.Entry("MaxPathLength") = Me.tbMaxPathLength.Text
_FolderInfo = Nothing
End Sub
Private Sub btnCancelScan_Click(sender As Object, e As EventArgs) Handles btnCancelScan.Click
btnScanFolder.Show()
btnCancelScan.Hide()
cts.Cancel()
End Sub
Private Async Sub ScanFolder(cts As CancellationTokenSource)
Dim folderPath As String = tbSearchFolder.Text
Dim result As Boolean, maxPathLength As Integer
result = Integer.TryParse(tbMaxPathLength.Text, maxPathLength)
If result = False Or maxPathLength < 0 Or maxPathLength > 32767 Then
MessageBoxCustom.Show(Me, "Max path length value Is invalid.", "Error", MessageBoxButtons.OK, MessageBoxIcon.Warning, _DPI, "beep")
GoTo EndScan
End If
If IO.Directory.Exists(folderPath) = False Then
MessageBoxCustom.Show(Me, "Given folder does Not exist.", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error, _DPI, "beep")
GoTo EndScan
End If
_ini.Entry("LastSelectedFolder") = folderPath
SetStatus("Scan Started")
_MaxPathLength = maxPathLength
rtbOriginalName.Clear()
tbNewName.Clear()
lblCharCountNewName.Text = "0000"
lblCharCountNewName2.Text = "0000"
lblCharCountOriginalName.Text = "0000"
lblCharCountOriginalName2.Text = "0000"
Dim searchSubFolders As Boolean = cbSearchSubfolders.Checked
Dim completePathArr() As String, fileList() As String, searchExceptionList As New List(Of Exception)
Dim FilesScannedCount As Integer = 0
Try
Task.Run(Sub() ShowFileScanProgress(FilesScannedCount, cts.Token), cts.Token)
fileList = Await Task.Run(Function()
Return GetLongFilePaths3(folderPath, searchSubFolders, maxPathLength, completePathArr, FilesScannedCount, cts.Token, searchExceptionList)
End Function, cts.Token)
cts.Cancel()
Catch ex As Exception
MessageBoxCustom.Show(Me, $"UNKNOWN ERROR:{vbCrLf}{ex.Message}", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error, _DPI, "error")
End Try
'MessageBoxCustom.Show(Me, $"File scan error occurred:{vbCrLf}{ex.InnerException.Message}", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error, _DPI, "error")
ListFiles(fileList)
If searchExceptionList.Count = 0 Then
SetStatus($"Searched Files: {FilesScannedCount}")
Else
Dim exStr As String = ""
For Each ex As Exception In searchExceptionList
exStr &= ex.Message & vbCrLf
Next
WriteToTextFile("ScanErrorLog.txt", exStr)
SetStatus($"Searched Files: {FilesScannedCount} See ScanErrorLog for blocked folders.")
End If
EndScan:
btnScanFolder.Show()
btnCancelScan.Hide()
End Sub
Sub ShowFileScanProgress(ByRef FilesScannedCount As Integer, token As CancellationToken)
Try
Do While True
Thread.Sleep(500)
token.ThrowIfCancellationRequested()
SetStatus("Searched Files: " & FilesScannedCount)
Loop
Catch ex As Exception
If TypeOf ex IsNot OperationCanceledException Then
Stop
End If
End Try
End Sub
Private Sub ListFiles(pathArr() As String)
If pathArr.Length = 0 Then
MessageBoxCustom.Show(Me, "No file paths greater than " & _MaxPathLength & " characters found.", "Search Result", MessageBoxButtons.OK, MessageBoxIcon.Asterisk, _DPI, "notifyAlt")
SetStatus("0", "count")
Exit Sub
End If
SetStatus(pathArr.Length + 1, "count")
lvMain.BeginUpdate()
_pathList.ClearEntries()
With _pathList
For Each str As String In pathArr
.AddRow()
.SetCellVal(str, "Path")
.SetCellVal(str.Length, "Path Length")
.SetCellVal(Path.GetFileName(str).Length, "Name Length")
Next
End With
lvMain.EndUpdate()
End Sub
Private Sub cbSearchSubfolders_CheckedChanged(sender As Object, e As EventArgs) Handles cbSearchSubfolders.CheckedChanged
If _initialized = True Then
_ini.Entry("IncludeSubFolders") = cbSearchSubfolders.Checked
End If
End Sub
Private Sub btnUndo_Click(sender As Object, e As EventArgs) Handles btnUndo.Click
If _lastAction.newName <> "" Then
If _lastAction.mode = "file" Then
If _lastAction.newList = False Then
Dim fileFound As Boolean = False
For Each lvItem As ListViewItem In lvMain.Items
If lvItem.SubItems(2).Text = _lastAction.newPath Then
lvItem.Selected = True
tbNewName.Text = _lastAction.OriginalName
fileFound = True
Exit For
End If
Next
If fileFound = True Then
Dim result As String = ChangeFileOrFolderName("file")
If result.StartsWith("success") = False Then
SetStatus("UNDO FAILED")
Else
SetStatus("Undo Name Change Successful",, True)
_lastAction = Nothing
End If
Else
AskUndo("file")
End If
Else
AskUndo("file")
End If
Else 'if mode = "folder" then
Dim aFullPath As String = ""
If _lastAction.newList = False Then
For Each lvItem As ListViewItem In lvMain.Items
If lvItem.SubItems(2).Text.StartsWith(_lastAction.newPath) = True Then
aFullPath = lvItem.SubItems(2).Text
lvItem.Selected = True
Exit For
End If
Next
_FolderInfo = Nothing
_FolderInfo = New FolderSelect(aFullPath)
For i As Integer = 0 To _FolderInfo.FolderArr.Length - 1
If _FolderInfo.CurrentFolder("fullPath") = _lastAction.newPath Then
Exit For
End If
FolderSelectionMode("left")
Next
If _FolderInfo.CurrentFolder("fullPath") = _lastAction.newPath Then
tbNewName.Text = _lastAction.OriginalName
Dim result As String = ChangeFileOrFolderName("folder")
If result.StartsWith("success") = False Then
SetStatus("UNDO FAILED")
Else
SetStatus("Undo Name Change Successful",, True)
_lastAction = Nothing
End If
Else
AskUndo("folder")
End If
Else
AskUndo("folder")
End If
End If
Else
MessageBoxCustom.Show(Me, "Nothing to undo", "Undo", MessageBoxButtons.OK, MessageBoxIcon.Exclamation, _DPI, "beep")
End If
End Sub
Private Sub AskUndo(mode As String)
If vbYes = MessageBoxCustom.Show(Me, $"Change the following?{vbCrLf}{_lastAction.newPath}{vbCrLf}{vbCrLf}to{vbCrLf}{vbCrLf}{_lastAction.OriginalPath}", "Undo Confirmation", MessageBoxButtons.YesNo, MessageBoxIcon.Question, _DPI, "notifyAlt",, 800) Then
Dim folderMode As Boolean = If(mode = "file", False, True)
Dim result As String = ChangeFileName(_lastAction.newPath, _lastAction.OriginalName, folderMode)
If result.StartsWith("success") = True Then
SetStatus("Undo Name Change Successful",, True)
_lastAction = Nothing
Else
MessageBoxCustom.Show(Me, $"Failed to undo name change. Could not change{vbCrLf}{_lastAction.newPath}{vbCrLf}{vbCrLf}to{vbCrLf}{vbCrLf}{_lastAction.OriginalPath}", "Undo Error", MessageBoxButtons.OK, MessageBoxIcon.Error, _DPI, "criticalStop",, 800)
End If
End If
End Sub
Private Sub btnChangeFileName_Click(sender As Object, e As EventArgs) Handles btnChangeFileName.Click
ChangeFileOrFolderName("file")
End Sub
Private Sub btnChangeFolderName_Click(sender As Object, e As EventArgs) Handles btnChangeFolderName.Click
ChangeFileOrFolderName("folder")
End Sub
Private Function ChangeFileOrFolderName(Optional mode As String = "[file,folder]") As String
Dim fullPath As String, result As String, folderMode As Boolean
Try
If mode = "file" Then
fullPath = _pathList.GetCellEntry(_CurrentLVitem, "Path")
folderMode = False
ElseIf mode = "folder" Then
fullPath = _FolderInfo.CurrentFolder("fullPath")
folderMode = True
Else
Stop
Return ""
End If
Catch ex As Exception
Return ""
End Try
result = ChangeFileName(fullPath, tbNewName.Text, folderMode)
If result.StartsWith("success") = False Then '
MessageBoxCustom.Show(Me, $"Error changing {mode} name." & vbCrLf & result, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error, _DPI, "beep")
SetStatus($"ERROR: {mode} name not changed")
Else 'success
_lastAction.newName = tbNewName.Text
_lastAction.OriginalPath = fullPath
_lastAction.mode = mode
_lastAction.newList = False
If mode = "file" Then
Dim newPath As String = Path.GetDirectoryName(fullPath) & "\" & tbNewName.Text
Dim NameLength As Integer = tbNewName.Text.Length
Dim PathLength As Integer = newPath.Length
_pathList.SetCellVal2(NameLength, "Name Length", _CurrentLVitem)
_pathList.SetCellVal2(PathLength, "Path Length", _CurrentLVitem)
_pathList.SetCellVal2(newPath, "Path", _CurrentLVitem)
If PathLength < _MaxPathLength Then
_pathList.SetBackColor(Color.FromArgb(255, 255, 190), "AllColumns", _CurrentLVitem)
End If
Else 'if mode = "folder"
Dim currentPath As String = _FolderInfo.CurrentFolder("fullPath") ' current path of the folder to be renamed
Dim newPath As String = _FolderInfo.CurrentFolder("parentFolder") & "\" & tbNewName.Text 'new path name for folder being renamed
For i As Integer = 0 To _pathList.RowCount - 1
Dim pathStr As String = _pathList.GetCellEntry(i, "Path") 'filename for row
If pathStr.StartsWith(currentPath) = True Then
fullPath = _pathList.GetCellEntry(i, "Path")
Dim newFullPath As String = $"{newPath}{fullPath.Substring(currentPath.Length)}" 'new file path for the row
_pathList.SetCellVal(newFullPath.Length, "Path Length", i)
_pathList.SetCellVal(newFullPath, "Path", i)
If newFullPath.Length <= _MaxPathLength Then
_pathList.SetBackColor(Color.FromArgb(255, 255, 190), "AllColumns", i)
End If
End If
Next
End If
If result.Contains("cmdMove") Then
SetStatus($"F{mode.Substring(1)} name changed successfully. cmd.exe MOVE was used")
Else
SetStatus($"F{mode.Substring(1)} name changed successfully")
End If
_FolderInfo = Nothing
End If
Return result
End Function
Private Sub tbNewName_KeyDown(sender As Object, e As KeyEventArgs) Handles tbNewName.KeyDown
If e.KeyCode = Keys.Enter Then
e.SuppressKeyPress = True ' Prevent ding sound
Dim question As String, resp As Integer, subR As Action(Of Object, EventArgs)
If _FolderInfo.initialized = False Then 'if False, then folder select mode should be
question = "Are you sure you want to change this file name?"
subR = AddressOf btnChangeFileName_Click
Else
question = "Are you sure you want to change this folder name?"
subR = AddressOf btnChangeFolderName_Click
End If
If rtbOriginalName.Text = "" Then
'do nothing
ElseIf _ConfirmChangeOnEnter = True Then
resp = MessageBoxCustom.Show(Me, question, "Change Confirmation", MessageBoxButtons.YesNo, MessageBoxIcon.Question, _DPI)
Else
resp = vbYes
End If
If resp = vbYes Then
subR(New Object, New EventArgs)
End If
End If
End Sub
Private Sub btnRightLeftFolder_Click(sender As Windows.Forms.Button, e As EventArgs) Handles btnRightFolder.Click, btnLeftFolder.Click
If sender.Name = "btnLeftFolder" Then
FolderSelectionMode("left")
Else
FolderSelectionMode("right")
End If
End Sub
Private Sub FolderSelectionMode(Optional mode As String = "[left,right]")
Dim folderName As String
If _FolderInfo.initialized = False Then
Try
Dim path As String = _pathList.GetCellEntry(_CurrentLVitem, "Path")
_FolderInfo = New FolderSelect(path)
Catch ex As Exception
Exit Sub
End Try
folderName = _FolderInfo.CurrentFolder
Dim arr() As String = _FolderInfo.FolderArr
WriteToOriginalNameBox(rtbOriginalName, arr)
btnChangeFileName.Hide()
btnChangeFolderName.Show()
Else
Select Case mode
Case "left"
folderName = _FolderInfo.SwitchFolder("up")
Case "right"
folderName = _FolderInfo.SwitchFolder("down")
Case Else
Stop
Return
End Select
WriteToOriginalNameBox(rtbOriginalName, _FolderInfo.FolderArr)
End If
_parentPathCharCount = _fullPathCharCount - folderName.Length
tbNewName.Text = folderName
lblCharCountOriginalName2.Text = CStr(folderName.Length).PadLeft(4, " ")
End Sub
Private Sub WriteToOriginalNameBox(rtb As RichTextBox, arr() As String)
rtbOriginalName.Clear()
For i As Integer = 0 To arr.Length - 1
If i <> _FolderInfo.CurrentIndex Then
rtbOriginalName.AppendText(arr(i) & "\")
Else
Dim c As Integer = rtbOriginalName.TextLength