-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathfsmng.cpp
2381 lines (1999 loc) · 58.7 KB
/
fsmng.cpp
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
//
#include <stdp>
#include <ui64a>
#include <math>
#include <fnmanip.h>
//#define HEAP_MULTI_THREAD_MODE
// TODO GROUP FOLDERS TOGETHER
// TODO WHEN CTRL+SHIFT -> PROGLEM! CRTL+SHIFT CAN CHANGE LANGUAGE, CHECK IF IT DOES THEN MB ONLY CTRL WILL ENABLE THE MODE OR SHIFT HOLD ENTER DRAWING MODE DISABLE DRAG AND DROP! JUST DRAGGIN MOUSE WITH LMB PRESS SELECTS FILES LIKE A BRUSH! RMB TO DESELECT ERASE SELECTION
// TODO ADVANCED RENAMING OF MULTIPLE SELECTED FILES OR ALL FILES IN FOLDER/FOLDERS
// TODO SORTING FILEZ SMART NUMERIC\LEXICONUMERIC MB TEST ON WIN SHELL
// TODO SORTING BY EXTENTION (FILE TYPE), BUT WITHOUT SEPARATE TABLE COLUMN!
// TODO TRUNCATED FILE NAME DISPLAY ON LONG HOVER OR OTHER TRIGGER
// TODO FILE PROPERTIES INFO
// TODO https://stackoverflow.com/questions/61314541/how-do-i-list-all-file-names-of-a-zip-archive-in-c-from-scratch
// TODO IS IT WORTH TO DO DIRECTORY ENUMERATION ON OPENINGN FOLDER IN ANOTHER THREAD OR NOT? I THINK NOT!!! FOR SEARCH YES, BUT UI MUST NOT RESPOND DURING LOAD! https://devblogs.microsoft.com/oldnewthing/20131024-00/?p=2843
// TODO USE THIS FOR SUB CONTEXT MENU DISPLAY SystemParametersInfo and use SPI_GETMOUSEHOVERTIME to retrieve the default hover time-out.
// TODO PROGRESS BARS FOR LONG OPERATIONS
// TODO DRAG N DROP FROM SEPARATE PROCESSESS
// TODO FILES IN FOLDER COUNTER DISPLAY + SELECTION COUNT
// TODO INTEGRATE FSS INTO THE FSMNG TO SCAN SYSTEM/DISPLAY % OF SPACE USED, MENUBOX ENTRY 'CALC SIZE' FOR FOLDERS -> N/A SIZE CHANGE TO ACTUAL DIR SIZE
// TODO TABS
// TODO FILE RENAMING
// TODO UNDO ACTIONS
// TODO MOUNTED DRIVES POLLER THREAD IN DRIVES DISPLAY PANE MODE MAKE IT INTO SEPARATE WATCHER THREAD, ONLY ACTIVATE IT WHEN DRIVES DISPLAY ACTIVE!
// TODO MOUNTED DRIVES SHOW PERCENTAGE BAR OF FREE SPACE
// TODO WHEN WATCHED FOLDER CONTENT CHANGED SCROLL AUTOMATICALLY AND SELECT NEW CONTENT
// TODO C:\Users\ScienceDiscoverer\My Documents HIDDEN SYSTEM SYMLINK FILE CRUSH ON OPEN DISPLAY ERROR INSTEAD NO ACEESS
/* LIKELY REASON:
if(ffind == INVALID_HANDLE_VALUE)
{
p|PE|" --> "|fnd_all|N|N;
txtclr(fnd_all); <<<<<<<<<<<<<<<<<<<< !!!!!!!!!
return;
}
IF ACCES WAS DENIED, [fnd_all] IS NOT CLEARED AND ALL THE NEXT SCANNING WILL ALWAYS FAIL, AS IT WILL ALSWAYS TRIGGER "FILE NOT EXISTS" ERROR WHICH WILL AGAIN NOT CLEAR [fnd_all] TEXT
AND THE RESULT OF THIS HUGE BUG IS A GIANT STRING OF ENDLESSLY CONCATENATED FILEPATHS!
*/
// TODO WHEN CURRENTLY OPENED DIRECTORY IS DELETED BY EXTERNAL PROGRAM, EVERYTHING BREAKS, NO FILE DISPLAY, ONLY CAN GO BACK TO DRIVES [RELATED TO HIDDEN+SYSTEM+SYMLINK FILES ACCESS BUG] SAME RESULT, PROBABLY RELATED
// TODO !!!!!!!!IMPORTANT!!!!!!!! DISABLE DRIVES DRAG N' DROP AND DELETE/COPY/MOVE !!!
// TODO [PARTIALLY DONE? WITHOUT UpdateWindow() THERE IS NO WHITE FLASHING ON UPDATE EVEN WITH 20 MS INTERVALS] PUT SOME KIND OF LIMIT ON THE FOLDER UPDATE FREQENCY EXAMPLE: LOGGER WRITES TO FILE IN A WATCHED FOLDER 1000 TIMES PER SECOND
// TODO DEVISE A WAY TO TRUNCATE BASE DIRECTORY TEXT FOR VERY LONG PATHS
// TODO MAKE DND CURSOR WITH HIGH DPI SUPPORT 32 48 64 BITS FROM SCRATCH WITHOUT USING STUPID REALWORLD CURSOR EDITOR
// TODO HARDLINK SPECIFIC COMMAND TO FIND AND SHOW LIST OF ALL OTHER LINKS POINTING TO THE SAME FILE GetFileInformationByHandle(f, &bhfi) ---> CHECK ENTIRE DRIVE FOR FILES WITH > 1 bhfi.nNumberOfLinks ---> compare (ui64)bhfi.nFileIndexHigh << 32 | (ui64)bhfi.nFileIndexLow) IF MATCHES, THIS IS LINK TO THE SAME FILE!
// TODO NATIVE ZIP SUPPORT THAT WILL FULLY SUPPORT HARDLINKS PACKING/UNPACKING! According to pkware.cachefly.net/webdocs/casestudies/APPNOTE.TXT, in the UNIX extra field, the variable length data field can contain information about "symbolic or hard links". This suggests that some implementations could support that.
// TODO IMPLEMENT .LNK FILES CREATION WITHOUT USING ANY SHELL INTERFACES! GENERATE .LNK FILE BYTE BY BYTE IF NECESSARY!
// TODO NATIVE MESSAGEBOXES FOR DELETE OPERATION AND ERROR MESSAGES!
// TODO SEARCH IMPLEMENT
// TODO FILTER FOLDER FILE NAMES (LOCAL SEARCH) IMPLEMENT
// TODO IF FOLDER WATCHER DETECT CHANGE AND RELOAD PANE, SAVE CURRENT SELECTION AND SCROLL POSITION, RESTORE!
// TODO EMPTY SPACE + BASE DIR RMB MENU BOX COMMAND OPEN CMD HERE
// TODO FILE UNLOCKER IF FILE IS LOCKED BY SOME UNKNOWN PROCESS FIND PROCESS WITH ABILITY TO KILL
// TODO FILE DUPLICATE FINDER USING QUICK HASHING + HARDLINK DETECTION
// TODO OPTIONAL ICON/IMGTHUMBS DISPLAY NOTE: IMGTHUMBS CAN BE POTENTIALLY SAVED INSIDE THE IMAGE ITSELF, IN EXIF OR OTHER METADATA TAGS!
// TODO ADD MENU BOX ENTRY IF ANY SELECTED FILE OR FOLDER CONTAINS HARDLINK/SYMLINKS OR WHEN SHIFT IS PRESSED + RMB [COPY PRESERVING LINKS] CopyFile() -> If the source file is a symbolic link, the actual file copied is the target of the symbolic link. If the destination file already exists and is a symbolic link, the target of the symbolic link is overwritten by the source file. ALSO DETECT IF SYMLINC IS RELATIVE OR ABSOLUTE. IF ABSOLUTE, REPLACE THE BASE PATH WITH COPY/MOVE TARGET, CREATING NEW ABSOLUTE LINK. IF LINK IS RELATIVE, JUST CREATE THE SAME LINK! Symbolic links can either be absolute or relative links. Absolute links are links that specify each portion of the path name; relative links are determined relative to where relative–link specifiers are in a specified path. Relative links are specified using the following conventions: Dot (. and ..) conventions—for example, "..\" resolves the path relative to the parent directory. Names with no slashes (\\)—for example, "tmp" resolves the path relative to the current directory. Root relative—for example, "\Windows\System32" resolves to "current drive:\Windows\System32" PROBLEM I CANNOT RESTORE ABSOLUTE LINK IF IT PONITS TO ANOTHER DRIVE. WHAT TO DO? COPY TARGET FILE INSTEAD?
// TODO WHEN RETURNING BACK DIR RESTORE PREVIOUS SELECTION AND SCROLL POSITION
// TODO MAKE POSSIBILITY TO CREATE NEW FOLDER BY COPYING NAME OF EXISTING FOLDER + _0 _1 _2 ETC.
// TODO AUTOMATICALLY CANCEL COPY/MOVE OPERATION IF TARGET IS SAME FOLDER
// TODO [OPTIONAL: DO I REALLY NEED THE OVERHEAD?] ADD MOUSE TRACKING TO UNHOVER WHEN MOUSE LEAVES WINDOW
// TODO SOMETIMES BASE DIR NAME IS PAINTED COLORED IN THE HARDLINK COLOR IF FOLDER CONTAINS HARDLINK
// TODO SQUISH VERY STELTHY BUG -> SOME FILES HAVE DIFFERENT BACKGROUND THAN THE DEFAULT 0C0C0C INSTEAD IT HAS 0D0D0D BUT IS NOT SELECTED AND/OR HOVERED MIGHT BE PROBLEM WITH UNICODE EMOTICON IN THE FILE NAME THE SIZE OF FILE ENTRY IS ALSO INCORRECT, SMALLER THEN REST. BUG SCREENSHOT bug\fsmng_0.png
// TODO IMPLEMENT SORTING ORDER LIST EXAMPLI GRATA FIRST SORT BY NAME -> SIZE -> DATE -> FILE TYPE ETC
// TODO COPYFILEEX -> LPPROGRESS_ROUTINE -> RETURN VALUE PROGRESS_STOP Stop the copy operation. It can be restarted at a later time -> CALL COPYFILEEX WITH SAME FNAMES TO RESUME
#pragma comment( lib, "user32" )
#pragma comment( lib, "gdi32" )
#define HW(x) (((unsigned int)x) >> 16)
#define LW(x) (((unsigned int)x) & 0xFFFF)
#define HWSIGN(x) ((int)(short)HW(x))
#define LWSIGN(x) ((int)(short)LW(x))
#define DPIS(x) ((ui64)((double)x * dpi_scale)) // Scale value for DPI
#define HOVERED 0x1
#define SELECTED 0x2
#define DRAGGED_OVER 0x4
#define IS_HARDLINK 0x8
#define FILE_HOVERED (bools & HOVERED)
#define FILE_SELECTED (bools & SELECTED)
#define FILE_DRAGGED_OVER (bools & DRAGGED_OVER)
#define FILE_IS_HARDLINK (bools & IS_HARDLINK)
#define FSMM_LFOLDER_CHANGED (WM_USER + 42)
#define FSMM_RFOLDER_CHANGED (WM_USER + 43)
struct Pane;
class File
{
public:
File() = delete;
File(const wtxt &fn, DWORD atrib, const FILETIME &mt, ui64 sz, ui64 i, Pane *pane);
File(const wchar_t *fpath, ui64 i, Pane &pane);
File(const File &o) = delete;
File(File &&o) = delete;
~File() = default;
File & operator=(const File &o) = default;
File & operator=(File &&o) = delete;
File & operator=(const wchar_t *fpath);
const wtxt & Name() const;
const bool64 IsSelected() const;
wtxt & FullPathIntoTxt(wtxt &t) const;
void Hover();
void UnHover();
void Select(bool64 ctrl);
void UnSelect();
void ExecuteCommand(ui64 cmd, wtxt *targ);
void Paint(HDC dc);
void ComputeFullInfo(ui64 max_ns);
void SetFullInfo(const wtxt &inf);
void SetIndex(ui64 i);
void SetHardlinkState();
void ReleaseMemory();
private:
void ConstructFromFpath(const wchar_t *fpath);
void SetState(ui64 state);
void ResetState(ui64 state);
private:
ui32 attr;
ui32 bools;
ui64 mdate;
ui64 size;
wtxt name;
wtxt full_info;
i32 x;
i32 y;
ui64 idx;
Pane *pn;
};
class Files : public darr
{
public:
Files()
{
ts = 128;
s = 0;
d = (File *)Alloc(ts * sizeof(File));
memset(d, 0, ts * sizeof(File)); // All texts invalidated
}
Files(const Files &o) = delete;
~Files()
{
for(ui64 i = 0; i < s; ++i)
{
d[i].ReleaseMemory();
}
DARR_FREE(d);
}
File & operator[](ui64 i) { DARR_ASSERT_RANGE(i) return d[i]; }
const File & operator[](ui64 i) const { DARR_ASSERT_RANGE(i) return d[i]; }
Files & operator<<(const File &c)
{
ui64 ns = s + 1;
if(ts < ns)
{
d = (File *)ReAlloc(d, ns, sizeof(File));
memset(&d[s], 0, (ts - s) * sizeof(File)); // All new texts invalidated
}
d[s] = c;
s = ns;
return *this;
}
inline Files & Clear()
{
for(ui64 i = 0; i < s; ++i)
{
d[i].ReleaseMemory(); // Texts invalidated in ReleaseMemory()
}
s = 0;
return *this;
}
private:
File *d;
};
struct Pane
{
ui64 ch_msg; // Change message ID for the folder pane
wtxt bdir; // Base directory path for the pane
ui64a sel; // Indices of all selected files in the folder pane
ui64 phov_idx; // Index of the last hovered file
Files files; // Files in the folder pane
ui64 xl, xr, yt, yb; // Coordinates of the folder pane
ui64 top_idx; // Top visible index of a pane's file
ui64 max_files; // Maximum amount of files that can fit into pane's height
ui64 max_txt; // Maximum text that will fit in current pane's width
ui64 txtw; // Width of the file text's line in pixels
bool64 dnd; // Draggin' n' Droppin' over the folder pane
HANDLE watchtower; // FindFirstChangeNotification handle
HANDLE watcher; // Handle to thread that is currently observing the folder pane
};
struct ExtentionExecutor
{
wtxt ext;
cwstr executor;
};
ExtentionExecutor execs[] = {
{ WL(".txt.cpp.h.log.css.js"), WL("C:\\Program Files\\Notepad++\\notepad++.exe") },
{ WL(".png.jpg.jpeg.gif.bmp.ico"), WL("C:\\Program Files\\IrfanView\\i_view64.exe") },
{ WL(".mp4.mkv.avi.mov"), WL("C:\\Program Files\\MPC-HC\\mpc-hc64.exe") },
{ WL(".zip.rar.7z"), WL("C:\\Program Files\\7-Zip\\7zFM.exe") },
{ WL(".html.htm"), WL("C:\\Program Files\\Mozilla Firefox\\firefox.exe") },
{ WL(""), WL("") }
};
cwstr no_ext_executor = WL("C:\\Program Files\\Notepad++\\notepad++.exe");
ExtentionExecutor editors[] = {
{ WL(".txt.cpp.h.log.css.js.html.htm"), WL("C:\\Program Files\\Notepad++\\notepad++.exe") },
{ WL(".png.jpg.jpeg.gif.bmp.ico"), WL("C:\\Program Files\\GIMP 2\\bin\\gimp-2.10.exe") },
{ WL(".mp4.mkv.avi.mov"), WL("C:\\Program Files\\Adobe\\Adobe Premiere Pro 2020\\Adobe Premiere Pro.exe") },
{ WL(""), WL("") }
};
cwstr default_editor = WL("C:\\Program Files\\Notepad++\\notepad++.exe");
#define CMD_OPEN 0
#define CMD_EDIT 1
#define CMD_SYMLINK 2
#define CMD_HARDLINK 3
#define CMD_RENAME 4
#define CMD_DELETE 5
cwstr commands[] = {
WL("Open"),
WL("Edit"),
WL("Symlink"),
WL("Hardlink"),
WL("Rename"),
WL("Delete")
};
// TODO ADD THIS COMMANDS:
// COPY
// COPY NAME
// COPY PATH
#define NO_HOVER UI64_MAX
class MenuBox
{
public:
MenuBox();
MenuBox(const MenuBox &o) = delete;
MenuBox(MenuBox &&o) = delete;
~MenuBox() = default;
MenuBox & operator=(const MenuBox &o) = default;
MenuBox & operator=(MenuBox &&o) = delete;
operator bool64();
void Spawn(i64 x, i64 y, const ui64a &cmd_list, Pane *pane);
void Hover(i64 y);
void UnHover();
void Press(i64 y);
ui64 GetCommand();
void Exterminate();
Pane * GetPane() const;
bool64 MouseInside(i64 x, i64 y);
void Paint(HDC dc);
private:
void Annihilate();
private:
static const ui64 xpad;
private:
ui64a cmds; // Commands
ui64 hi; // Hover index
bool64 prs; // Pressed state
i64 xl; // X left
i64 xr; // X right
i64 yt; // Y top
i64 yb; // Y bottom
Pane *pn; // Pane where box was spawned
};
const ui64 MenuBox::xpad = 2;
HINSTANCE exec_adress;
ATOM main_class;
HWND main_wnd;
double dpi_scale;
HCURSOR dnd_cur; // Drag n' Drop cursor
HCURSOR def_cur;
HCURSOR cur_cur; // Current cursor
HFONT def_font;
HPEN def_pen;
HPEN dnd_pen; // Drag n' Drop pen
HBRUSH def_back_brush;
HBRUSH sel_back_brush;
HBRUSH scr_back_brush; // Scroll bar's background brush
HDC main_dc;
RECT work_pix;
UINT mwheel_scroll_ln, mwheel_scroll_ch; // System values for mouse wheel scrolling
ui64 cellw = 8;
ui64 cellh = 16;
ui64 clw, clh; // Client area width/height
i64 dnd_ignore_pix; // Mouse must move at least this much pixels to initiate Drag n' Drop
i64 dnd_lcx, dnd_lcy; // Position of last left mouse button click
Pane *dnd_orig_pane; // Folder pane where Drag n' Drop was initiated (NULL -> no DnD)
Pane *dnd_targ_pane; // Folder pane where the file/files where dropped
wtxt dnd_target; // Target where file/files where dropped
File *dnd_targ_file; // Pointer to file where DnD dropped something
const COLORREF def_back = 0x0C0C0C; // 0x00BBGGRR
const COLORREF hov_back = 0x1C1C1C;
const COLORREF sel_back = 0x4C4C4C;
const COLORREF def_front = 0xCCCCCC;
ui64 fold_pad_x = 4, fold_pad_y = 3; // Folder padding for the frame mesured in cells
Pane lp = { FSMM_LFOLDER_CHANGED, MAX_PATH, ui64a(1024), NFND };
Pane rp = { FSMM_RFOLDER_CHANGED, MAX_PATH, ui64a(1024), NFND };
MenuBox mbox;
// Left/Right panes specific variables ------------------------------------------------------------------------------
//Files lfiles, rfiles; // Files in a left/right folder pane
//
//ui64 lfold_xl, lfold_xr, lfold_yt, lfold_yb; // Coordinates of left folder pane
//ui64 rfold_xl, rfold_xr, rfold_yt, rfold_yb; // Coordinates of right folder pane
//
//ui64 lf_top_idx, rf_top_idx; // Left/Right folder top visible index
//
//ui64 fmax_files; // Maximum amount of files in current folder pane height
//ui64 lfmax_str, rfmax_str; // Maximum string that will fit in current left/right pane width
//ui64 lfinf_pw, rfinf_pw; // Width of right/left files' info entries in pixels
//
//ui64 lpast_hov_idx, rpast_hov_idx; // Index of last hovered file
//
//ui64a lsel = 1024, rsel = 1024; // Indices of all selected files in left/right folder panes
//
//bool64 dnd_lfold, dnd_rfold; // Draggin' n' Droppin' over the left/right folder pane
//
//wtxt lb_dir = MAX_PATH, rb_dir = MAX_PATH; // Base directory path for left/right panes
//
//
//HANDLE left_watchtower, right_watchtower;
//HANDLE left_watcher, right_watcher;
// Left/Right panes specific variables -------------------------------------------------------------------------------
inline bool64 pcoll(POINT pnt, const RECT &rc) // Check if POINT collides with RECT
{
return pnt.x >= rc.left && pnt.y >= rc.top && pnt.x <= rc.right && pnt.y <= rc.bottom;
}
inline bool64 pcoll(i64 x, i64 y, const RECT &rc) // Check if POINT collides with RECT
{
return x >= rc.left && y >= rc.top && x <= rc.right && y <= rc.bottom;
}
inline bool64 pcoll(POINT pnt, const Pane &pn)
{
return pnt.x >= (i64)pn.xl && pnt.y >= (i64)pn.yt && pnt.x <= (i64)pn.xr && pnt.y <= (i64)pn.yb;
}
inline bool64 pcoll(i64 x, i64 y, const Pane &pn)
{
return x >= (i64)pn.xl && y >= (i64)pn.yt && x <= (i64)pn.xr && y <= (i64)pn.yb;
}
inline bool64 pcoll(i64 x, i64 y, i64 xl, i64 xr, i64 yt, i64 yb)
{
return x >= xl && y >= yt && x <= xr && y <= yb;
}
inline bool64 pcoll(i64 x, i64 y, ui64 xl, ui64 xr, ui64 yt, ui64 yb)
{
return x >= (i64)xl && y >= (i64)yt && x <= (i64)xr && y <= (i64)yb;
}
inline void unSelectAll(Pane *pn)
{
if(~pn->sel == 0)
{
return;
}
for(ui64 i = 0; i < ~pn->sel; ++i)
{
pn->files[pn->sel[i]].UnSelect();
}
pn->sel.Clear();
}
inline wtxt errToWtxt(DWORD err)
{
wchar_t buff[300];
FormatMessage(
FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS,
NULL,
err,
0,
buff,
300,
NULL);
wtxt out = cwstr({ buff, strl(buff) });
txtd(out, ~out-2, 2); // Remove pesky newline
return out;
}
wtxt exec_ext = MAX_PATH;
void executeFile(wtxt &fp, const wtxt &bdir, bool64 edit_mode)
{
STARTUPINFO si;
PROCESS_INFORMATION pi;
memset(&si, 0, sizeof(si));
si.cb = sizeof(si);
memset(&pi, 0, sizeof(pi));
cwstr exec = { NULL, 0 };
fishOutFileExt(fp, exec_ext);
if(edit_mode)
{
t2low(exec_ext);
ui64 i = 0;
while(editors[i].ext != empty)
{
if(txtf(editors[i].ext, 0, exec_ext) != NFND)
{
exec = editors[i].executor;
break;
}
++i;
}
if(exec.t == NULL)
{
exec = default_editor;
}
}
else
{
if(exec_ext == empty)
{
exec = no_ext_executor;
}
else
{
t2low(exec_ext);
if(exec_ext == WL(".exe"))
{
goto skip_exec_search;
}
ui64 i = 0;
while(execs[i].ext != empty)
{
if(txtf(execs[i].ext, 0, exec_ext) != NFND)
{
exec = execs[i].executor;
break;
}
++i;
}
}
}
if(exec.t != NULL)
{
txti(fp, 0, WL(" \""));
fp += '"';
txti(fp, 0, exec);
txti(fp, exec.s, '"');
txti(fp, 0, '"');
}
skip_exec_search:
// Create Process ============================================================================
BOOL res = CreateProcess(
exec.t, // [I|O] Name of the module to be executed, that's it
fp, // [IO|O] Command line to be exectued, searches PATH, adds extention
NULL, // [I|O] Sec. Attrib. for inhereting new process by child processes
NULL, // [I|O] Sec. Attrib. for inhereting new thread by child processes
FALSE, // [I] New proc. inherits each inheritable handle
0, // [I] Process creation flags
NULL, // [I|O] Ptr to environment block of new process (inherit if NULL)
bdir, // [I|O] Full path to current directory for the process
&si, // [I] Ptr to STARTUPINFO struct, if dwFlags = 0, def. values used
&pi); // [O] Ptr to PROCESS_INFORMATION struct with new proc identification info
// ===========================================================================================
if(res)
{
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
}
else
{
DWORD e = GetLastError();
MessageBoxW(main_wnd, fp + '\n' + 'E' + i2wt(e) + ' ' + errToWtxt(e), L"ERROR EXECUTING FILE", MB_OK | MB_ICONSTOP);
}
}
//cwstr file_time_base = WL("00.00.00 00:00");
wtxt file_time_text = WL("00.00.00 00:00");
inline const wtxt & fileTimeToWtxt(ui64 time)
{
SYSTEMTIME fst;
FILETIME tloc;
FileTimeToLocalFileTime((FILETIME *)&time, &tloc);
FileTimeToSystemTime(&tloc, &fst);
// Unroll dat loop!
file_time_text[1] = (wchar_t)(fst.wYear % 10 | 0x30);
fst.wYear /= 10;
file_time_text[0] = (wchar_t)(fst.wYear % 10 | 0x30);
file_time_text[4] = (wchar_t)(fst.wMonth % 10 | 0x30);
fst.wMonth /= 10;
file_time_text[3] = (wchar_t)(fst.wMonth % 10 | 0x30);
file_time_text[7] = (wchar_t)(fst.wDay % 10 | 0x30);
fst.wDay /= 10;
file_time_text[6] = (wchar_t)(fst.wDay % 10 | 0x30);
file_time_text[10] = (wchar_t)(fst.wHour % 10 | 0x30);
fst.wHour /= 10;
file_time_text[9] = (wchar_t)(fst.wHour % 10 | 0x30);
file_time_text[13] = (wchar_t)(fst.wMinute % 10 | 0x30);
fst.wMinute /= 10;
file_time_text[12] = (wchar_t)(fst.wMinute % 10 | 0x30);
return file_time_text;
}
wtxt file_size_text = 15;
wtxt size_text = 15;
inline const wtxt & fileSizeToWtxt(ui64 sz)
{
wchar_t suffix;
file_size_text = WL(" ");
if(sz < 1'024) // Bytes
{
suffix = ' ';
}
else if(sz < 1'048'576) // Kilobytes
{
suffix = 'K';
sz /= 1'024;
}
else if(sz < 1'073'741'824) // Megabytes
{
suffix = 'M';
sz /= 1'048'576;
}
else if(sz < 1'099'511'627'776) // Gigabytes
{
suffix = 'G';
sz /= 1'073'741'824;
}
else if(sz < 1'125'899'906'842'624) // Terabytes
{
suffix = 'T';
sz /= 1'099'511'627'776;
}
else
{
file_size_text = WL("2BIG!");
return file_size_text;
}
i2t(sz, size_text);
size_text += suffix;
txto(file_size_text, 0, size_text);
return file_size_text;
}
wtxt trunced_fname = MAX_PATH;
wtxt trunced_fext = MAX_PATH;
const wtxt & truncFileName(const wtxt &fn, ui64 fn_max)
{
if(~fn < fn_max) // Add padding
{
trunced_fname = fn;
for(ui64 i = ~fn; i < fn_max; ++i)
{
trunced_fname[i] = ' ';
}
trunced_fname[fn_max] = 0;
txtssz(trunced_fname, fn_max);
return trunced_fname;
}
if(~fn == fn_max) // Do nothing
{
return fn;
}
// Star symbol will mean that file name is truncated, example:
// baka_sete_no_kara.txt
// baka_set*.txt
fishOutFileExt(fn, trunced_fext);
if(~trunced_fext >= fn_max) // Extention is too big, trunc it
{
trunced_fname = fn;
txtd(trunced_fname, fn_max-1, TEND);
trunced_fname += '*';
return trunced_fname;
}
ui64 fn_inc = fn_max - ~trunced_fext - 1; // Number of characters still included in the file name
txts(trunced_fname, fn, 0, fn_inc);
trunced_fname += '*', trunced_fname += trunced_fext;
return trunced_fname;
}
wtxt sl_dots = MAX_PATH;
inline void absToRelPath(const wtxt &link, wtxt &targ)
{
ui64 msz = MIN(~link, ~targ);
ui64 i = 0;
// link D:\T\foo\bar\baz\baka\sete\text.txt
// targ D:\T\foo\bar\boom\bg\text.txt
// ^
while(i < msz)
{
if(link[i] != targ[i])
{
break;
}
++i;
}
// link D:\T\foo\bar\baz\baka\sete\text.txt
// targ D:\T\foo\bar\boom\bg\text.txt
// ^
ui64 base_ed = txtfe(link, i, '\\');
txtclr(sl_dots);
for(ui64 j = i; j < ~link; ++j)
{
if(link[j] == '\\')
{
sl_dots += WL("..\\");
}
}
// link D:\T\foo\bar\baz\baka\sete\text.txt
// targ ..\..\..\boom\bg\text.txt
txtrp(targ, 0, base_ed, sl_dots);
}
namespace scan_FUNC { // Global Local Variables TM
wtxt star = WL("\\*");
wtxt fnd_all = MAX_PATH;
wtxt full_path = MAX_PATH;
wtxt fn; }
wtxt ffull_inf = 1023;
void scan(Pane *pn, const wtxt &dir)
{
using namespace scan_FUNC;
HANDLE ffind;
WIN32_FIND_DATAW fdata;
BY_HANDLE_FILE_INFORMATION bhfi;
ui64 idx = 0;
fnd_all += dir, fnd_all += star;
// Start files enumeration =================================================================
ffind = FindFirstFileExW(
fnd_all, // [I] LPCSTR Full dir + path
FindExInfoBasic, // [I] FINDEX_INFO_LEVELS Search accuracy
&fdata, // [O] LPVOID Ouput file data
FindExSearchNameMatch, // [I] FINDEX_SEARCH_OPS Non wildcard filters
NULL, // [I] LPVOID Must be NULL (no support)
FIND_FIRST_EX_LARGE_FETCH); // [I] DWORD Additional search flags
// =========================================================================================
if(ffind == INVALID_HANDLE_VALUE)
{
p|PE|" In FindFirstFileExA!"|N;
return;
}
do
{
txtsdt(fn, MAX_PATH, 0, fdata.cFileName); // Always set direct manipulated text back to 0!
txtszu(fn);
if((~fn == 1 && fn[0] == '.') || (~fn == 2 && fn[0] == '.' && fn[1] == '.'))
{
continue;
}
ui64 fsz = (ui64)fdata.nFileSizeHigh << 32 | (ui64)fdata.nFileSizeLow;
File f = { fn, fdata.dwFileAttributes, fdata.ftLastWriteTime, fsz, idx++, pn };
// Check if file is a HARDLINK
full_path = dir;
full_path += '\\', full_path += fn;
// Create or open File or Device =================================================================
HANDLE fh = CreateFile(
full_path, // [I] Name of the file or device to create/open
FILE_READ_ATTRIBUTES, // [I] Requested access GENERIC_READ|GENERIC_WRITE|0
0, // [I] Sharing mode FILE_SHARE_READ|WRITE|DELETE|0
NULL, // [I|O] SECURITY_ATTRIBUTES for file, handle inheritability
OPEN_EXISTING, // [I] Action to take if file/device exist or not
FILE_FLAG_OPEN_REPARSE_POINT, // [I] Attributes and flags for file/device
NULL); // [I|O] Handle to template file to copy attributes from
// ===============================================================================================
GetFileInformationByHandle(fh, &bhfi);
CloseHandle(fh);
if(!(fdata.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) && bhfi.nNumberOfLinks > 1)
{
f.SetHardlinkState();
}
pn->files << f;
}
while(FindNextFileW(ffind, &fdata));
FindClose(ffind);
ui64 max_fn = pn->max_txt - 23; // __XX.XX.XX XX:XX__XXXXY --> 23 CHARS
if(~pn->files > pn->max_files) // Account for scrollbar
{
--max_fn;
}
ui64 max_fold_fn = 0;
for(ui64 i = 0; i < ~pn->files; ++i)
{
if(~pn->files[i].Name() > max_fold_fn)
{
max_fold_fn = ~pn->files[i].Name();
}
}
if(max_fold_fn < max_fn)
{
max_fn = max_fold_fn;
}
pn->txtw = (max_fn + 23) * cellw;
for(ui64 i = 0; i < ~pn->files; ++i)
{
pn->files[i].ComputeFullInfo(max_fn);
}
txtclr(fnd_all);
txtsdt(fn, 0, 0, NULL); // Directly manipulated text set back to 0 before it goes out of scope
}
inline void invalidatePane(Pane *pn)
{
RECT rc = { (LONG)pn->xl, (LONG)pn->yt, (LONG)pn->xr, (LONG)pn->yb };
InvalidateRect(main_wnd, &rc, TRUE);
}
inline void invalidateWindowSide(Pane *pn)
{
RECT rc = { 0, 0, 0, (LONG)clh };
if(pn->ch_msg == FSMM_LFOLDER_CHANGED)
{
rc.right = (LONG)clw/2;
}
else
{
rc.left = (LONG)clw/2;
rc.right = (LONG)clw;
}
InvalidateRect(main_wnd, &rc, TRUE);
}
inline void invalidateBaseDir(Pane *pn)
{
RECT rc = { (LONG)(pn->xl + cellw), (LONG)(pn->yt - cellh), (LONG)pn->xr, (LONG)pn->yt };
InvalidateRect(main_wnd, &rc, TRUE);
}
void reInitPane(Pane *pn, const wtxt &new_bdir)
{
pn->top_idx = 0;
pn->phov_idx = NFND;
pn->sel.Clear();
pn->bdir = new_bdir;
pn->files.Clear();
scan(pn, new_bdir);
RECT rc = { (LONG)pn->xl, (LONG)pn->yt, (LONG)pn->xr, (LONG)pn->yb };
InvalidateRect(main_wnd, &rc, TRUE);
invalidateBaseDir(pn);
//UpdateWindow(main_wnd);
}
FILETIME null_time;
void listDrives(Pane *pn)
{
pn->top_idx = 0;
pn->phov_idx = 0;
pn->sel.Clear();
pn->files.Clear();
DWORD drives = GetLogicalDrives();
wchar_t disk_name[3] = { 'A', ':', 0 };
ui64 idx = 0;
for(ui64 i = 1; i <= 0x2000000; i <<= 1) // 26 letters, 26 bits
{
if(drives & i)
{
ULARGE_INTEGER tot, free;
GetDiskFreeSpaceEx(disk_name, NULL, &tot, &free);
File f = { cwstr({ disk_name, 2 }), FILE_ATTRIBUTE_DIRECTORY, null_time, tot.QuadPart, idx++, pn };
ffull_inf = WL("DISK ");
ffull_inf += disk_name[0], ffull_inf += WL(" ");
ffull_inf += fileSizeToWtxt(tot.QuadPart), ffull_inf += WL(" ");
ffull_inf += fileSizeToWtxt(free.QuadPart);
f.SetFullInfo(ffull_inf);
pn->files << f;
}
++disk_name[0];
}
pn->txtw = 20 * cellw; // DISK X__XXXXY__XXXXY
RECT rc = { (LONG)pn->xl, (LONG)pn->yt, (LONG)pn->xr, (LONG)pn->yb };
InvalidateRect(main_wnd, &rc, TRUE);
invalidateBaseDir(pn);
//UpdateWindow(main_wnd);
}
THREAD directoryWatcher(void *param)
{
while(1)
{
WaitForSingleObject(((Pane *)param)->watchtower, INFINITE);
PostMessage(main_wnd, (UINT)((Pane *)param)->ch_msg, 0, 0);
FindNextChangeNotification(((Pane *)param)->watchtower);
}
return 0;
}
wtxt to_observe_txt = MAX_PATH;
void observeDirectory(Pane *pn)
{
if(pn->watcher != NULL)
{
FindCloseChangeNotification(pn->watchtower);
TerminateThread(pn->watcher, 0);
}
const wchar_t *to_observe;
if(~pn->bdir == 2) // Handle root drives directories i.e. D:
{
to_observe_txt = pn->bdir;
to_observe_txt += '\\';
to_observe = to_observe_txt;
}
else
{
to_observe = pn->bdir;
}
pn->watchtower = FindFirstChangeNotification(
to_observe,
FALSE,
FILE_NOTIFY_CHANGE_FILE_NAME |
FILE_NOTIFY_CHANGE_DIR_NAME |
FILE_NOTIFY_CHANGE_ATTRIBUTES |
FILE_NOTIFY_CHANGE_SIZE |
FILE_NOTIFY_CHANGE_LAST_WRITE);
pn->watcher = CreateThread(NULL, 0, directoryWatcher, pn, 0, NULL);
}
void butcherWatcher(Pane *pn)
{
if(pn->watcher != NULL)
{
FindCloseChangeNotification(pn->watchtower);
TerminateThread(pn->watcher, 0);
pn->watchtower = NULL;
pn->watcher = NULL;
}
}
inline File::File(const wtxt &fn, DWORD atrib, const FILETIME &mt, ui64 sz, ui64 i, Pane *pane)
{
name = fn;
attr = atrib;
mdate = *((ui64 *)&mt);
size = sz;
bools = 0;
idx = i;
pn = pane;
}
inline File::File(const wchar_t *fpath, ui64 i, Pane &pane)
{
ConstructFromFpath(fpath);
bools = 0;
idx = i;
pn = &pane;
}
inline File & File::operator=(const wchar_t *fpath)
{
ConstructFromFpath(fpath);
}
inline const wtxt & File::Name() const
{
return name;
}
inline const bool64 File::IsSelected() const
{
return FILE_SELECTED;
}
inline wtxt & File::FullPathIntoTxt(wtxt &t) const
{
t = pn->bdir;
t += '\\', t += name;
return t;
}
inline void File::Hover() // TODO *FIXED* POSSIBLY? !!!NO!!! DIDNT FIXED! STILL HAPPENS ON HOVER SOMETIMES NOT ON SCROL ELLIMINATING TOO MUCH UPDATEWINDOW SOLVED IT? TO FIX BLACK CHAOS CORRUPTION LIMIT AMOUNT OF RECT INVALIDATION ON FAST MOUSE MOVEMENT! NOTE: CORRUPTION CAN BEGIN AT RANDOM MOMENT EVEN WITHOUT RAPID HOVERING!
{
if(pn->phov_idx != NFND)
{
pn->files[pn->phov_idx].UnHover();
}
//if(rp.phov_idx != NFND)
//{
// rp.files[rp.phov_idx].UnHover();
//}