├── .gitignore
├── ExplorerCommand.dpr
├── ExplorerCommand.dproj
├── ExplorerCommand.res
├── ExplorerCommandProject.groupproj
├── ExplorerCommand_Icon.ico
├── HotkeyHook.dpr
├── HotkeyHook.dproj
├── HotkeyHook.res
├── LICENSE
├── README.md
├── main.dfm
└── main.pas
/.gitignore:
--------------------------------------------------------------------------------
1 | # Uncomment these types if you want even more clean repository. But be careful.
2 | # It can make harm to an existing project source. Read explanations below.
3 | #
4 | # Resource files are binaries containing manifest, project icon and version info.
5 | # They can not be viewed as text or compared by diff-tools. Consider replacing them with .rc files.
6 | #*.res
7 | #
8 | # Type library file (binary). In old Delphi versions it should be stored.
9 | # Since Delphi 2009 it is produced from .ridl file and can safely be ignored.
10 | #*.tlb
11 | #
12 | # Diagram Portfolio file. Used by the diagram editor up to Delphi 7.
13 | # Uncomment this if you are not using diagrams or use newer Delphi version.
14 | #*.ddp
15 | #
16 | # Visual LiveBindings file. Added in Delphi XE2.
17 | # Uncomment this if you are not using LiveBindings Designer.
18 | #*.vlb
19 | #
20 | # Deployment Manager configuration file for your project. Added in Delphi XE2.
21 | # Uncomment this if it is not mobile development and you do not use remote debug feature.
22 | #*.deployproj
23 | #
24 | # C++ object files produced when C/C++ Output file generation is configured.
25 | # Uncomment this if you are not using external objects (zlib library for example).
26 | #*.obj
27 | #
28 |
29 | # Delphi compiler-generated binaries (safe to delete)
30 | *.exe
31 | *.dll
32 | *.bpl
33 | *.bpi
34 | *.dcp
35 | *.so
36 | *.apk
37 | *.drc
38 | *.map
39 | *.dres
40 | *.rsm
41 | *.tds
42 | *.dcu
43 | *.lib
44 | *.a
45 | *.o
46 | *.ocx
47 |
48 | # Delphi autogenerated files (duplicated info)
49 | *.cfg
50 | *.hpp
51 | *Resource.rc
52 |
53 | # Delphi local files (user-specific info)
54 | *.local
55 | *.identcache
56 | *.projdata
57 | *.tvsconfig
58 | *.dsk
59 |
60 | # Delphi history and backups
61 | __history/
62 | __recovery/
63 | *.~*
64 |
65 | # Castalia statistics file (since XE7 Castalia is distributed with Delphi)
66 | *.stat
67 |
68 | Win32
69 | *.bak
70 |
--------------------------------------------------------------------------------
/ExplorerCommand.dpr:
--------------------------------------------------------------------------------
1 | program ExplorerCommand;
2 |
3 | uses
4 | Vcl.Forms,
5 | Windows,
6 | SysUtils,
7 | main in 'main.pas' {Form1},
8 | Vcl.Themes,
9 | Vcl.Styles;
10 |
11 | {$R *.res}
12 |
13 | begin
14 | if CreateMutex(nil, True, '{C97C27E2-C5FC-41BE-AF34-6C9E250FC303}') = 0 then
15 | RaiseLastOSError;
16 | if GetLastError = ERROR_ALREADY_EXISTS then
17 | Exit;
18 |
19 | Application.Initialize;
20 | Application.MainFormOnTaskbar := False;
21 | Application.ShowMainForm := False;
22 | Application.CreateForm(TForm1, Form1);
23 | Application.Run;
24 | end.
25 |
--------------------------------------------------------------------------------
/ExplorerCommand.dproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | {D22C21FB-5BAD-48E2-A372-BA38F79F80B9}
4 | 18.8
5 | VCL
6 | ExplorerCommand.dpr
7 | True
8 | Debug
9 | Win32
10 | 1
11 | Application
12 |
13 |
14 | true
15 |
16 |
17 | true
18 | Base
19 | true
20 |
21 |
22 | true
23 | Base
24 | true
25 |
26 |
27 | true
28 | Base
29 | true
30 |
31 |
32 | true
33 | Cfg_1
34 | true
35 | true
36 |
37 |
38 | true
39 | Base
40 | true
41 |
42 |
43 | true
44 | Cfg_2
45 | true
46 | true
47 |
48 |
49 | .\$(Platform)\$(Config)
50 | .\$(Platform)\$(Config)
51 | false
52 | false
53 | false
54 | false
55 | false
56 | System;Xml;Data;Datasnap;Web;Soap;Vcl;Vcl.Imaging;Vcl.Touch;Vcl.Samples;Vcl.Shell;$(DCC_Namespace)
57 | $(BDS)\bin\delphi_PROJECTICON.ico
58 | $(BDS)\bin\Artwork\Windows\UWP\delphi_UwpDefault_44.png
59 | $(BDS)\bin\Artwork\Windows\UWP\delphi_UwpDefault_150.png
60 | ExplorerCommand
61 |
62 |
63 | DBXSqliteDriver;bindcompdbx;IndyIPCommon;RESTComponents;DBXInterBaseDriver;vcl;IndyIPServer;vclactnband;OmniThreadLibraryRuntime;vclFireDAC;IndySystem;tethering;svnui;dsnapcon;FireDACADSDriver;madExcept_;VirtualTreesR;FireDACMSAccDriver;fmxFireDAC;METROVCL;vclimg;madBasic_;FireDAC;vcltouch;DragDropDR103R;vcldb;bindcompfmx;svn;FireDACSqliteDriver;FireDACPgDriver;inetdb;pkCindyPackD10;soaprtl;DbxCommonDriver;FireDACIBDriver;fmx;fmxdae;xmlrtl;soapmidas;fmxobj;vclwinx;rtl;madDisAsm_;DbxClientDriver;CustomIPTransport;vcldsnap;dbexpress;IndyCore;EmbeddedWebBrowser_XE8;DOSCommandDR;vclx;bindcomp;appanalytics;dsnap;EsVclCore;FireDACCommon;IndyIPClient;DelphiUCLPackage;bindcompvcl;SynEdit_R;RESTBackendComponents;VCLRESTComponents;BCEditor.Package;soapserver;dbxcds;VclSmp;adortl;DropShadow;vclie;bindengine;DBXMySQLDriver;CloudService;dsnapxml;FireDACMySQLDriver;dbrtl;inetdbxpress;IndyProtocols;EsVclComponents;FireDACCommonODBC;FireDACCommonDriver;RKAIOXE4U1;inet;fmxase;$(DCC_UsePackage)
64 | Winapi;System.Win;Data.Win;Datasnap.Win;Web.Win;Soap.Win;Xml.Win;Bde;$(DCC_Namespace)
65 | Debug
66 | true
67 | CompanyName=;FileDescription=$(MSBuildProjectName);FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProgramID=com.embarcadero.$(MSBuildProjectName);ProductName=$(MSBuildProjectName);ProductVersion=1.0.0.0;Comments=
68 | 1033
69 | $(BDS)\bin\default_app.manifest
70 | ExplorerCommand_Icon.ico
71 |
72 |
73 | DBXSqliteDriver;bindcompdbx;IndyIPCommon;RESTComponents;DBXInterBaseDriver;vcl;IndyIPServer;vclactnband;OmniThreadLibraryRuntime;vclFireDAC;IndySystem;tethering;dsnapcon;FireDACADSDriver;VirtualTreesR;FireDACMSAccDriver;fmxFireDAC;vclimg;FireDAC;vcltouch;DragDropDR103R;vcldb;bindcompfmx;FireDACSqliteDriver;FireDACPgDriver;inetdb;soaprtl;DbxCommonDriver;FireDACIBDriver;fmx;fmxdae;xmlrtl;soapmidas;fmxobj;vclwinx;rtl;DbxClientDriver;CustomIPTransport;vcldsnap;dbexpress;IndyCore;DOSCommandDR;vclx;bindcomp;appanalytics;dsnap;EsVclCore;FireDACCommon;IndyIPClient;bindcompvcl;SynEdit_R;RESTBackendComponents;VCLRESTComponents;BCEditor.Package;soapserver;dbxcds;VclSmp;adortl;vclie;bindengine;DBXMySQLDriver;CloudService;dsnapxml;FireDACMySQLDriver;dbrtl;inetdbxpress;IndyProtocols;EsVclComponents;FireDACCommonODBC;FireDACCommonDriver;RKAIOXE4U1;inet;fmxase;$(DCC_UsePackage)
74 |
75 |
76 | DEBUG;$(DCC_Define)
77 | true
78 | false
79 | true
80 | true
81 | true
82 |
83 |
84 | false
85 | true
86 | PerMonitorV2
87 | true
88 | 1033
89 |
90 |
91 | false
92 | RELEASE;$(DCC_Define)
93 | 0
94 | 0
95 |
96 |
97 | true
98 | PerMonitorV2
99 |
100 |
101 |
102 | MainSource
103 |
104 |
105 |
106 |
107 |
108 | Cfg_2
109 | Base
110 |
111 |
112 | Base
113 |
114 |
115 | Cfg_1
116 | Base
117 |
118 |
119 |
120 | Delphi.Personality.12
121 | Application
122 |
123 |
124 |
125 | ExplorerCommand.dpr
126 |
127 |
128 | Microsoft Office 2000 Sample Automation Server Wrapper Components
129 | Microsoft Office XP Sample Automation Server Wrapper Components
130 |
131 |
132 |
133 |
134 |
135 | ExplorerCommand.exe
136 | true
137 |
138 |
139 |
140 |
141 | 1
142 |
143 |
144 | Contents\MacOS
145 | 1
146 |
147 |
148 | 0
149 |
150 |
151 |
152 |
153 | classes
154 | 1
155 |
156 |
157 | classes
158 | 1
159 |
160 |
161 |
162 |
163 | res\xml
164 | 1
165 |
166 |
167 | res\xml
168 | 1
169 |
170 |
171 |
172 |
173 | library\lib\armeabi-v7a
174 | 1
175 |
176 |
177 |
178 |
179 | library\lib\armeabi
180 | 1
181 |
182 |
183 | library\lib\armeabi
184 | 1
185 |
186 |
187 |
188 |
189 | library\lib\armeabi-v7a
190 | 1
191 |
192 |
193 |
194 |
195 | library\lib\mips
196 | 1
197 |
198 |
199 | library\lib\mips
200 | 1
201 |
202 |
203 |
204 |
205 | library\lib\armeabi-v7a
206 | 1
207 |
208 |
209 | library\lib\arm64-v8a
210 | 1
211 |
212 |
213 |
214 |
215 | library\lib\armeabi-v7a
216 | 1
217 |
218 |
219 |
220 |
221 | res\drawable
222 | 1
223 |
224 |
225 | res\drawable
226 | 1
227 |
228 |
229 |
230 |
231 | res\values
232 | 1
233 |
234 |
235 | res\values
236 | 1
237 |
238 |
239 |
240 |
241 | res\values-v21
242 | 1
243 |
244 |
245 | res\values-v21
246 | 1
247 |
248 |
249 |
250 |
251 | res\values
252 | 1
253 |
254 |
255 | res\values
256 | 1
257 |
258 |
259 |
260 |
261 | res\drawable
262 | 1
263 |
264 |
265 | res\drawable
266 | 1
267 |
268 |
269 |
270 |
271 | res\drawable-xxhdpi
272 | 1
273 |
274 |
275 | res\drawable-xxhdpi
276 | 1
277 |
278 |
279 |
280 |
281 | res\drawable-ldpi
282 | 1
283 |
284 |
285 | res\drawable-ldpi
286 | 1
287 |
288 |
289 |
290 |
291 | res\drawable-mdpi
292 | 1
293 |
294 |
295 | res\drawable-mdpi
296 | 1
297 |
298 |
299 |
300 |
301 | res\drawable-hdpi
302 | 1
303 |
304 |
305 | res\drawable-hdpi
306 | 1
307 |
308 |
309 |
310 |
311 | res\drawable-xhdpi
312 | 1
313 |
314 |
315 | res\drawable-xhdpi
316 | 1
317 |
318 |
319 |
320 |
321 | res\drawable-mdpi
322 | 1
323 |
324 |
325 | res\drawable-mdpi
326 | 1
327 |
328 |
329 |
330 |
331 | res\drawable-hdpi
332 | 1
333 |
334 |
335 | res\drawable-hdpi
336 | 1
337 |
338 |
339 |
340 |
341 | res\drawable-xhdpi
342 | 1
343 |
344 |
345 | res\drawable-xhdpi
346 | 1
347 |
348 |
349 |
350 |
351 | res\drawable-xxhdpi
352 | 1
353 |
354 |
355 | res\drawable-xxhdpi
356 | 1
357 |
358 |
359 |
360 |
361 | res\drawable-xxxhdpi
362 | 1
363 |
364 |
365 | res\drawable-xxxhdpi
366 | 1
367 |
368 |
369 |
370 |
371 | res\drawable-small
372 | 1
373 |
374 |
375 | res\drawable-small
376 | 1
377 |
378 |
379 |
380 |
381 | res\drawable-normal
382 | 1
383 |
384 |
385 | res\drawable-normal
386 | 1
387 |
388 |
389 |
390 |
391 | res\drawable-large
392 | 1
393 |
394 |
395 | res\drawable-large
396 | 1
397 |
398 |
399 |
400 |
401 | res\drawable-xlarge
402 | 1
403 |
404 |
405 | res\drawable-xlarge
406 | 1
407 |
408 |
409 |
410 |
411 | res\values
412 | 1
413 |
414 |
415 | res\values
416 | 1
417 |
418 |
419 |
420 |
421 | 1
422 |
423 |
424 | Contents\MacOS
425 | 1
426 |
427 |
428 | 0
429 |
430 |
431 |
432 |
433 | Contents\MacOS
434 | 1
435 | .framework
436 |
437 |
438 | Contents\MacOS
439 | 1
440 | .framework
441 |
442 |
443 | 0
444 |
445 |
446 |
447 |
448 | 1
449 | .dylib
450 |
451 |
452 | 1
453 | .dylib
454 |
455 |
456 | 1
457 | .dylib
458 |
459 |
460 | Contents\MacOS
461 | 1
462 | .dylib
463 |
464 |
465 | Contents\MacOS
466 | 1
467 | .dylib
468 |
469 |
470 | 0
471 | .dll;.bpl
472 |
473 |
474 |
475 |
476 | 1
477 | .dylib
478 |
479 |
480 | 1
481 | .dylib
482 |
483 |
484 | 1
485 | .dylib
486 |
487 |
488 | Contents\MacOS
489 | 1
490 | .dylib
491 |
492 |
493 | Contents\MacOS
494 | 1
495 | .dylib
496 |
497 |
498 | 0
499 | .bpl
500 |
501 |
502 |
503 |
504 | 0
505 |
506 |
507 | 0
508 |
509 |
510 | 0
511 |
512 |
513 | 0
514 |
515 |
516 | 0
517 |
518 |
519 | Contents\Resources\StartUp\
520 | 0
521 |
522 |
523 | Contents\Resources\StartUp\
524 | 0
525 |
526 |
527 | 0
528 |
529 |
530 |
531 |
532 | 1
533 |
534 |
535 | 1
536 |
537 |
538 | 1
539 |
540 |
541 |
542 |
543 | 1
544 |
545 |
546 | 1
547 |
548 |
549 | 1
550 |
551 |
552 |
553 |
554 | 1
555 |
556 |
557 | 1
558 |
559 |
560 | 1
561 |
562 |
563 |
564 |
565 | 1
566 |
567 |
568 | 1
569 |
570 |
571 | 1
572 |
573 |
574 |
575 |
576 | 1
577 |
578 |
579 | 1
580 |
581 |
582 | 1
583 |
584 |
585 |
586 |
587 | 1
588 |
589 |
590 | 1
591 |
592 |
593 | 1
594 |
595 |
596 |
597 |
598 | 1
599 |
600 |
601 | 1
602 |
603 |
604 | 1
605 |
606 |
607 |
608 |
609 | 1
610 |
611 |
612 | 1
613 |
614 |
615 | 1
616 |
617 |
618 |
619 |
620 | 1
621 |
622 |
623 | 1
624 |
625 |
626 | 1
627 |
628 |
629 |
630 |
631 | 1
632 |
633 |
634 | 1
635 |
636 |
637 | 1
638 |
639 |
640 |
641 |
642 | 1
643 |
644 |
645 | 1
646 |
647 |
648 | 1
649 |
650 |
651 |
652 |
653 | 1
654 |
655 |
656 | 1
657 |
658 |
659 | 1
660 |
661 |
662 |
663 |
664 | 1
665 |
666 |
667 | 1
668 |
669 |
670 | 1
671 |
672 |
673 |
674 |
675 | 1
676 |
677 |
678 | 1
679 |
680 |
681 | 1
682 |
683 |
684 |
685 |
686 | 1
687 |
688 |
689 | 1
690 |
691 |
692 | 1
693 |
694 |
695 |
696 |
697 | 1
698 |
699 |
700 | 1
701 |
702 |
703 | 1
704 |
705 |
706 |
707 |
708 | 1
709 |
710 |
711 | 1
712 |
713 |
714 | 1
715 |
716 |
717 |
718 |
719 | 1
720 |
721 |
722 | 1
723 |
724 |
725 | 1
726 |
727 |
728 |
729 |
730 | 1
731 |
732 |
733 | 1
734 |
735 |
736 | 1
737 |
738 |
739 |
740 |
741 | 1
742 |
743 |
744 | 1
745 |
746 |
747 | 1
748 |
749 |
750 |
751 |
752 | 1
753 |
754 |
755 | 1
756 |
757 |
758 | 1
759 |
760 |
761 |
762 |
763 | 1
764 |
765 |
766 | 1
767 |
768 |
769 | 1
770 |
771 |
772 |
773 |
774 | 1
775 |
776 |
777 | 1
778 |
779 |
780 | 1
781 |
782 |
783 |
784 |
785 | 1
786 |
787 |
788 | 1
789 |
790 |
791 | 1
792 |
793 |
794 |
795 |
796 | 1
797 |
798 |
799 | 1
800 |
801 |
802 |
803 |
804 | ..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF
805 | 1
806 |
807 |
808 | ..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF
809 | 1
810 |
811 |
812 |
813 |
814 | 1
815 |
816 |
817 | 1
818 |
819 |
820 |
821 |
822 | ..\
823 | 1
824 |
825 |
826 | ..\
827 | 1
828 |
829 |
830 |
831 |
832 | 1
833 |
834 |
835 | 1
836 |
837 |
838 | 1
839 |
840 |
841 |
842 |
843 | 1
844 |
845 |
846 | 1
847 |
848 |
849 | 1
850 |
851 |
852 |
853 |
854 | ..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF
855 | 1
856 |
857 |
858 |
859 |
860 | ..\
861 | 1
862 |
863 |
864 | ..\
865 | 1
866 |
867 |
868 |
869 |
870 | Contents
871 | 1
872 |
873 |
874 | Contents
875 | 1
876 |
877 |
878 |
879 |
880 | Contents\Resources
881 | 1
882 |
883 |
884 | Contents\Resources
885 | 1
886 |
887 |
888 |
889 |
890 | library\lib\armeabi-v7a
891 | 1
892 |
893 |
894 | library\lib\arm64-v8a
895 | 1
896 |
897 |
898 | 1
899 |
900 |
901 | 1
902 |
903 |
904 | 1
905 |
906 |
907 | 1
908 |
909 |
910 | Contents\MacOS
911 | 1
912 |
913 |
914 | Contents\MacOS
915 | 1
916 |
917 |
918 | 0
919 |
920 |
921 |
922 |
923 | library\lib\armeabi-v7a
924 | 1
925 |
926 |
927 |
928 |
929 | 1
930 |
931 |
932 | 1
933 |
934 |
935 |
936 |
937 | Assets
938 | 1
939 |
940 |
941 | Assets
942 | 1
943 |
944 |
945 |
946 |
947 | Assets
948 | 1
949 |
950 |
951 | Assets
952 | 1
953 |
954 |
955 |
956 |
957 |
958 |
959 |
960 |
961 |
962 |
963 |
964 |
965 |
966 |
967 | True
968 | False
969 |
970 |
971 | 12
972 |
973 |
974 |
975 |
976 |
977 |
--------------------------------------------------------------------------------
/ExplorerCommand.res:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vhanla/ExplorerCommand/a01852f4e8d8bb61d7add91bdb0480da99b82f5e/ExplorerCommand.res
--------------------------------------------------------------------------------
/ExplorerCommandProject.groupproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | {310C8440-FB7D-4877-94D7-3051EF6DF673}
4 |
5 |
6 |
7 | HotkeyHook.dproj
8 |
9 |
10 |
11 |
12 |
13 |
14 | Default.Personality.12
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 |
--------------------------------------------------------------------------------
/ExplorerCommand_Icon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vhanla/ExplorerCommand/a01852f4e8d8bb61d7add91bdb0480da99b82f5e/ExplorerCommand_Icon.ico
--------------------------------------------------------------------------------
/HotkeyHook.dpr:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vhanla/ExplorerCommand/a01852f4e8d8bb61d7add91bdb0480da99b82f5e/HotkeyHook.dpr
--------------------------------------------------------------------------------
/HotkeyHook.dproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | {0752C59F-2066-40B4-8064-724C020132A3}
4 | 18.8
5 | None
6 | HotkeyHook.dpr
7 | True
8 | Debug
9 | Win32
10 | 1
11 | Library
12 |
13 |
14 | true
15 |
16 |
17 | true
18 | Base
19 | true
20 |
21 |
22 | true
23 | Base
24 | true
25 |
26 |
27 | true
28 | Base
29 | true
30 |
31 |
32 | true
33 | Cfg_1
34 | true
35 | true
36 |
37 |
38 | true
39 | Base
40 | true
41 |
42 |
43 | .\$(Platform)\$(Config)
44 | .\$(Platform)\$(Config)
45 | false
46 | false
47 | false
48 | false
49 | false
50 | true
51 | System;Xml;Data;Datasnap;Web;Soap;$(DCC_Namespace)
52 | HotkeyHook
53 |
54 |
55 | DBXSqliteDriver;bindcompdbx;IndyIPCommon;RESTComponents;DBXInterBaseDriver;vcl;IndyIPServer;vclactnband;OmniThreadLibraryRuntime;vclFireDAC;IndySystem;tethering;svnui;dsnapcon;FireDACADSDriver;madExcept_;VirtualTreesR;FireDACMSAccDriver;fmxFireDAC;METROVCL;vclimg;madBasic_;FireDAC;vcltouch;DragDropDR103R;vcldb;bindcompfmx;svn;FireDACSqliteDriver;FireDACPgDriver;inetdb;pkCindyPackD10;soaprtl;DbxCommonDriver;FireDACIBDriver;fmx;fmxdae;xmlrtl;soapmidas;fmxobj;vclwinx;rtl;madDisAsm_;DbxClientDriver;CustomIPTransport;vcldsnap;dbexpress;IndyCore;EmbeddedWebBrowser_XE8;DOSCommandDR;vclx;bindcomp;appanalytics;dsnap;EsVclCore;FireDACCommon;IndyIPClient;DelphiUCLPackage;bindcompvcl;SynEdit_R;RESTBackendComponents;VCLRESTComponents;BCEditor.Package;soapserver;dbxcds;VclSmp;adortl;DropShadow;vclie;bindengine;DBXMySQLDriver;CloudService;dsnapxml;FireDACMySQLDriver;dbrtl;inetdbxpress;IndyProtocols;EsVclComponents;FireDACCommonODBC;FireDACCommonDriver;RKAIOXE4U1;inet;fmxase;$(DCC_UsePackage)
56 | Winapi;System.Win;Data.Win;Datasnap.Win;Web.Win;Soap.Win;Xml.Win;Bde;$(DCC_Namespace)
57 | Debug
58 | true
59 | CompanyName=;FileDescription=$(MSBuildProjectName);FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProgramID=com.embarcadero.$(MSBuildProjectName);ProductName=$(MSBuildProjectName);ProductVersion=1.0.0.0;Comments=
60 | 1033
61 |
62 |
63 | DBXSqliteDriver;bindcompdbx;IndyIPCommon;RESTComponents;DBXInterBaseDriver;vcl;IndyIPServer;vclactnband;OmniThreadLibraryRuntime;vclFireDAC;IndySystem;tethering;dsnapcon;FireDACADSDriver;VirtualTreesR;FireDACMSAccDriver;fmxFireDAC;vclimg;FireDAC;vcltouch;DragDropDR103R;vcldb;bindcompfmx;FireDACSqliteDriver;FireDACPgDriver;inetdb;soaprtl;DbxCommonDriver;FireDACIBDriver;fmx;fmxdae;xmlrtl;soapmidas;fmxobj;vclwinx;rtl;DbxClientDriver;CustomIPTransport;vcldsnap;dbexpress;IndyCore;DOSCommandDR;vclx;bindcomp;appanalytics;dsnap;EsVclCore;FireDACCommon;IndyIPClient;bindcompvcl;SynEdit_R;RESTBackendComponents;VCLRESTComponents;BCEditor.Package;soapserver;dbxcds;VclSmp;adortl;vclie;bindengine;DBXMySQLDriver;CloudService;dsnapxml;FireDACMySQLDriver;dbrtl;inetdbxpress;IndyProtocols;EsVclComponents;FireDACCommonODBC;FireDACCommonDriver;RKAIOXE4U1;inet;fmxase;$(DCC_UsePackage)
64 |
65 |
66 | DEBUG;$(DCC_Define)
67 | true
68 | false
69 | true
70 | true
71 | true
72 |
73 |
74 | false
75 | L:\Proyectos\ExplorerCommand\Win32\Debug\ExplorerCommand.exe
76 |
77 |
78 | false
79 | RELEASE;$(DCC_Define)
80 | 0
81 | 0
82 |
83 |
84 |
85 | MainSource
86 |
87 |
88 | Cfg_2
89 | Base
90 |
91 |
92 | Base
93 |
94 |
95 | Cfg_1
96 | Base
97 |
98 |
99 |
100 | Delphi.Personality.12
101 | Application
102 |
103 |
104 |
105 | HotkeyHook.dpr
106 |
107 |
108 |
109 |
110 |
111 | true
112 |
113 |
114 |
115 |
116 | true
117 |
118 |
119 |
120 |
121 | true
122 |
123 |
124 |
125 |
126 | HotkeyHook.dll
127 | true
128 |
129 |
130 |
131 |
132 | 1
133 |
134 |
135 | Contents\MacOS
136 | 1
137 |
138 |
139 | 0
140 |
141 |
142 |
143 |
144 | classes
145 | 1
146 |
147 |
148 | classes
149 | 1
150 |
151 |
152 |
153 |
154 | res\xml
155 | 1
156 |
157 |
158 | res\xml
159 | 1
160 |
161 |
162 |
163 |
164 | library\lib\armeabi-v7a
165 | 1
166 |
167 |
168 |
169 |
170 | library\lib\armeabi
171 | 1
172 |
173 |
174 | library\lib\armeabi
175 | 1
176 |
177 |
178 |
179 |
180 | library\lib\armeabi-v7a
181 | 1
182 |
183 |
184 |
185 |
186 | library\lib\mips
187 | 1
188 |
189 |
190 | library\lib\mips
191 | 1
192 |
193 |
194 |
195 |
196 | library\lib\armeabi-v7a
197 | 1
198 |
199 |
200 | library\lib\arm64-v8a
201 | 1
202 |
203 |
204 |
205 |
206 | library\lib\armeabi-v7a
207 | 1
208 |
209 |
210 |
211 |
212 | res\drawable
213 | 1
214 |
215 |
216 | res\drawable
217 | 1
218 |
219 |
220 |
221 |
222 | res\values
223 | 1
224 |
225 |
226 | res\values
227 | 1
228 |
229 |
230 |
231 |
232 | res\values-v21
233 | 1
234 |
235 |
236 | res\values-v21
237 | 1
238 |
239 |
240 |
241 |
242 | res\values
243 | 1
244 |
245 |
246 | res\values
247 | 1
248 |
249 |
250 |
251 |
252 | res\drawable
253 | 1
254 |
255 |
256 | res\drawable
257 | 1
258 |
259 |
260 |
261 |
262 | res\drawable-xxhdpi
263 | 1
264 |
265 |
266 | res\drawable-xxhdpi
267 | 1
268 |
269 |
270 |
271 |
272 | res\drawable-ldpi
273 | 1
274 |
275 |
276 | res\drawable-ldpi
277 | 1
278 |
279 |
280 |
281 |
282 | res\drawable-mdpi
283 | 1
284 |
285 |
286 | res\drawable-mdpi
287 | 1
288 |
289 |
290 |
291 |
292 | res\drawable-hdpi
293 | 1
294 |
295 |
296 | res\drawable-hdpi
297 | 1
298 |
299 |
300 |
301 |
302 | res\drawable-xhdpi
303 | 1
304 |
305 |
306 | res\drawable-xhdpi
307 | 1
308 |
309 |
310 |
311 |
312 | res\drawable-mdpi
313 | 1
314 |
315 |
316 | res\drawable-mdpi
317 | 1
318 |
319 |
320 |
321 |
322 | res\drawable-hdpi
323 | 1
324 |
325 |
326 | res\drawable-hdpi
327 | 1
328 |
329 |
330 |
331 |
332 | res\drawable-xhdpi
333 | 1
334 |
335 |
336 | res\drawable-xhdpi
337 | 1
338 |
339 |
340 |
341 |
342 | res\drawable-xxhdpi
343 | 1
344 |
345 |
346 | res\drawable-xxhdpi
347 | 1
348 |
349 |
350 |
351 |
352 | res\drawable-xxxhdpi
353 | 1
354 |
355 |
356 | res\drawable-xxxhdpi
357 | 1
358 |
359 |
360 |
361 |
362 | res\drawable-small
363 | 1
364 |
365 |
366 | res\drawable-small
367 | 1
368 |
369 |
370 |
371 |
372 | res\drawable-normal
373 | 1
374 |
375 |
376 | res\drawable-normal
377 | 1
378 |
379 |
380 |
381 |
382 | res\drawable-large
383 | 1
384 |
385 |
386 | res\drawable-large
387 | 1
388 |
389 |
390 |
391 |
392 | res\drawable-xlarge
393 | 1
394 |
395 |
396 | res\drawable-xlarge
397 | 1
398 |
399 |
400 |
401 |
402 | res\values
403 | 1
404 |
405 |
406 | res\values
407 | 1
408 |
409 |
410 |
411 |
412 | 1
413 |
414 |
415 | Contents\MacOS
416 | 1
417 |
418 |
419 | 0
420 |
421 |
422 |
423 |
424 | Contents\MacOS
425 | 1
426 | .framework
427 |
428 |
429 | Contents\MacOS
430 | 1
431 | .framework
432 |
433 |
434 | 0
435 |
436 |
437 |
438 |
439 | 1
440 | .dylib
441 |
442 |
443 | 1
444 | .dylib
445 |
446 |
447 | 1
448 | .dylib
449 |
450 |
451 | Contents\MacOS
452 | 1
453 | .dylib
454 |
455 |
456 | Contents\MacOS
457 | 1
458 | .dylib
459 |
460 |
461 | 0
462 | .dll;.bpl
463 |
464 |
465 |
466 |
467 | 1
468 | .dylib
469 |
470 |
471 | 1
472 | .dylib
473 |
474 |
475 | 1
476 | .dylib
477 |
478 |
479 | Contents\MacOS
480 | 1
481 | .dylib
482 |
483 |
484 | Contents\MacOS
485 | 1
486 | .dylib
487 |
488 |
489 | 0
490 | .bpl
491 |
492 |
493 |
494 |
495 | 0
496 |
497 |
498 | 0
499 |
500 |
501 | 0
502 |
503 |
504 | 0
505 |
506 |
507 | 0
508 |
509 |
510 | Contents\Resources\StartUp\
511 | 0
512 |
513 |
514 | Contents\Resources\StartUp\
515 | 0
516 |
517 |
518 | 0
519 |
520 |
521 |
522 |
523 | 1
524 |
525 |
526 | 1
527 |
528 |
529 | 1
530 |
531 |
532 |
533 |
534 | 1
535 |
536 |
537 | 1
538 |
539 |
540 | 1
541 |
542 |
543 |
544 |
545 | 1
546 |
547 |
548 | 1
549 |
550 |
551 | 1
552 |
553 |
554 |
555 |
556 | 1
557 |
558 |
559 | 1
560 |
561 |
562 | 1
563 |
564 |
565 |
566 |
567 | 1
568 |
569 |
570 | 1
571 |
572 |
573 | 1
574 |
575 |
576 |
577 |
578 | 1
579 |
580 |
581 | 1
582 |
583 |
584 | 1
585 |
586 |
587 |
588 |
589 | 1
590 |
591 |
592 | 1
593 |
594 |
595 | 1
596 |
597 |
598 |
599 |
600 | 1
601 |
602 |
603 | 1
604 |
605 |
606 | 1
607 |
608 |
609 |
610 |
611 | 1
612 |
613 |
614 | 1
615 |
616 |
617 | 1
618 |
619 |
620 |
621 |
622 | 1
623 |
624 |
625 | 1
626 |
627 |
628 | 1
629 |
630 |
631 |
632 |
633 | 1
634 |
635 |
636 | 1
637 |
638 |
639 | 1
640 |
641 |
642 |
643 |
644 | 1
645 |
646 |
647 | 1
648 |
649 |
650 | 1
651 |
652 |
653 |
654 |
655 | 1
656 |
657 |
658 | 1
659 |
660 |
661 | 1
662 |
663 |
664 |
665 |
666 | 1
667 |
668 |
669 | 1
670 |
671 |
672 | 1
673 |
674 |
675 |
676 |
677 | 1
678 |
679 |
680 | 1
681 |
682 |
683 | 1
684 |
685 |
686 |
687 |
688 | 1
689 |
690 |
691 | 1
692 |
693 |
694 | 1
695 |
696 |
697 |
698 |
699 | 1
700 |
701 |
702 | 1
703 |
704 |
705 | 1
706 |
707 |
708 |
709 |
710 | 1
711 |
712 |
713 | 1
714 |
715 |
716 | 1
717 |
718 |
719 |
720 |
721 | 1
722 |
723 |
724 | 1
725 |
726 |
727 | 1
728 |
729 |
730 |
731 |
732 | 1
733 |
734 |
735 | 1
736 |
737 |
738 | 1
739 |
740 |
741 |
742 |
743 | 1
744 |
745 |
746 | 1
747 |
748 |
749 | 1
750 |
751 |
752 |
753 |
754 | 1
755 |
756 |
757 | 1
758 |
759 |
760 | 1
761 |
762 |
763 |
764 |
765 | 1
766 |
767 |
768 | 1
769 |
770 |
771 | 1
772 |
773 |
774 |
775 |
776 | 1
777 |
778 |
779 | 1
780 |
781 |
782 | 1
783 |
784 |
785 |
786 |
787 | 1
788 |
789 |
790 | 1
791 |
792 |
793 |
794 |
795 | ..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF
796 | 1
797 |
798 |
799 | ..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF
800 | 1
801 |
802 |
803 |
804 |
805 | 1
806 |
807 |
808 | 1
809 |
810 |
811 |
812 |
813 | ..\
814 | 1
815 |
816 |
817 | ..\
818 | 1
819 |
820 |
821 |
822 |
823 | 1
824 |
825 |
826 | 1
827 |
828 |
829 | 1
830 |
831 |
832 |
833 |
834 | 1
835 |
836 |
837 | 1
838 |
839 |
840 | 1
841 |
842 |
843 |
844 |
845 | ..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF
846 | 1
847 |
848 |
849 |
850 |
851 | ..\
852 | 1
853 |
854 |
855 | ..\
856 | 1
857 |
858 |
859 |
860 |
861 | Contents
862 | 1
863 |
864 |
865 | Contents
866 | 1
867 |
868 |
869 |
870 |
871 | Contents\Resources
872 | 1
873 |
874 |
875 | Contents\Resources
876 | 1
877 |
878 |
879 |
880 |
881 | library\lib\armeabi-v7a
882 | 1
883 |
884 |
885 | library\lib\arm64-v8a
886 | 1
887 |
888 |
889 | 1
890 |
891 |
892 | 1
893 |
894 |
895 | 1
896 |
897 |
898 | 1
899 |
900 |
901 | Contents\MacOS
902 | 1
903 |
904 |
905 | Contents\MacOS
906 | 1
907 |
908 |
909 | 0
910 |
911 |
912 |
913 |
914 | library\lib\armeabi-v7a
915 | 1
916 |
917 |
918 |
919 |
920 | 1
921 |
922 |
923 | 1
924 |
925 |
926 |
927 |
928 | Assets
929 | 1
930 |
931 |
932 | Assets
933 | 1
934 |
935 |
936 |
937 |
938 | Assets
939 | 1
940 |
941 |
942 | Assets
943 | 1
944 |
945 |
946 |
947 |
948 |
949 |
950 |
951 |
952 |
953 |
954 |
955 |
956 |
957 |
958 | True
959 | False
960 |
961 |
962 | 12
963 |
964 |
965 |
966 |
967 |
968 |
--------------------------------------------------------------------------------
/HotkeyHook.res:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vhanla/ExplorerCommand/a01852f4e8d8bb61d7add91bdb0480da99b82f5e/HotkeyHook.res
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2020 Victor Alberto Gil
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | Explorer Command
2 | ----------------
3 |
4 | Explorer Command is a third party tool that will add command line to interact with current Directory and Selected File/Files.
5 |
6 | The purpose is to add capabilities like:
7 |
8 | - [x] Run DOS commands in current directory
9 | - [ ] Run PowerShell commands in current directory
10 | - [ ] Open With via command line
11 | - [ ] Show Git status, as well common commands
12 | - [ ] Preview files
13 |
14 | Default Hotkey [ctrl-shift-p]
15 |
16 | ### Future Plans:
17 | - [ ] Extensible via Python plugins
18 |
19 | ### Disclaimer
20 |
21 | This is a proof of concept project, it might change without advertisement.
22 | Keep in mind that it is a W.I.P. and ideas are more than welcome.
23 |
--------------------------------------------------------------------------------
/main.pas:
--------------------------------------------------------------------------------
1 | unit main;
2 |
3 | interface
4 |
5 | uses
6 | Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
7 | Vcl.Controls, Vcl.Forms, Vcl.Dialogs, SynEdit, DosCommand, rkShellPath, rkEdit,
8 | Vcl.StdCtrls, Vcl.ExtCtrls, System.ImageList, Vcl.ImgList,
9 | Vcl.ComCtrls, Vcl.WinXCtrls, TlHelp32, ShellApi, ShDocVw, ActiveX, ShlObj, IniFiles, ComObj,
10 | Vcl.Menus, DzDirSeek, rkSmartPath, rkVistaProBar, Vcl.VirtualImage,
11 | uHostPreview, Winapi.Wincodec, StrUtils, ES.BaseControls, ES.Images, rkView,
12 | JPEG, Math, CommCtrl {HIMAGELIST}, rkIntegerList, SynEditHighlighter,
13 | SynHighlighterUNIXShellScript, CB.Form, madExceptVcl, scStyledForm, libgit2,
14 | rkPathViewer, IconFontsImageListBase, IconFontsImageList, Clipbrd,
15 | SynHighlighterMulti, SynEditCodeFolding, SynHighlighterPas, Vcl.Buttons,
16 | System.Actions, Vcl.ActnList, Vcl.ToolWin, MPCommonObjects,
17 | EasyListview, VirtualExplorerEasyListview,
18 | Process, CB.Autorun, System.SyncObjs, ACL.UI.Controls.Base,
19 | ACL.UI.Controls.Labels, ACL.UI.Controls.ActivityIndicator,
20 | Vcl.VirtualImageList, Vcl.BaseImageCollection, Vcl.ImageCollection,
21 | ACL.UI.Controls.CompoundControl, ACL.UI.Controls.HexView, ACL.Classes,
22 | ACL.UI.Application, ACL.UI.Controls.MagnifierGlass,
23 | ACL.UI.Controls.ColorPicker, ACL.UI.Controls.Buttons,
24 | ACL.UI.Dialogs.ColorPicker, ACL.UI.Controls.TreeList,
25 | ACL.UI.Controls.ShellTreeView;
26 |
27 | const
28 | KeyEvent = WM_USER + 11;
29 | KeyEventAll = WM_USER + 12;
30 | KeyEventUpdatePath = WM_USER + 13;
31 | KeyEventPickPaths = WM_USER + 14;
32 | CM_UpdateView = WM_USER + 2;
33 | CM_Progress = WM_USER + 3;
34 | IID_IImageList: TGUID = '{46EB5926-582E-4017-9FDF-E8998DAA0950}';
35 |
36 | type
37 | EInvalidImageFormat = class(Exception);
38 |
39 | type
40 |
41 | PItemData = ^TItemData;
42 | TItemData = record
43 | Name: string;
44 | ThumbWidth: Word;
45 | ThumbHeight: Word;
46 | Size: Integer;
47 | Modified: TDateTime;
48 | Dir: Boolean;
49 | GotThumb: Boolean;
50 | IWidth, IHeight: Word;
51 | ImgIdx: Integer;
52 | IsIcon: Boolean;
53 | ImgState: Byte;
54 | Image: TObject;
55 | end;
56 |
57 | ThumbThread = class(TThread)
58 | private
59 | { Private Declarations }
60 | ViewLink: TrkView;
61 | ItemsLink: TList;
62 | protected
63 | procedure Execute; override;
64 | public
65 | constructor Create(View: TrkView; Items: TList);
66 | end;
67 |
68 | TFuzzyStringMatcher = class
69 | private
70 | FThreshold: Integer;
71 | function DamerauLevenshteinDistance(const S1, S2: string): Integer;
72 | public
73 | constructor Create(Threshold: Integer);
74 | function IsMatch(const Str, SubStr: string): Boolean;
75 | end;
76 |
77 | // Autocomplete https://stackoverflow.com/a/5465826
78 | TEnumString = class(TInterfacedObject, IEnumString)
79 | private
80 | type
81 | TPointerList = array[0..0] of Pointer;
82 | var
83 | FStrings: TStringList;
84 | FCurrIndex: Integer;
85 | public
86 | // IEnumString
87 | function Next(celt: Longint; out elt;
88 | pceltFetched: PLongint): HResult; stdcall;
89 | function Skip(celt: Longint): HResult; stdcall;
90 | function Reset: HResult; stdcall;
91 | function Clone(out enm: IEnumString): HResult; stdcall;
92 | // VCL
93 | constructor Create;
94 | destructor Destroy; override;
95 | end;
96 |
97 | { ACO_NONE = 0;
98 | ACO_AUTOSUGGEST = $1;
99 | ACO_AUTOAPPEND = $2;
100 | ACO_SEARCH = $4;
101 | ACO_FILTERPREFIXES = $8;
102 | ACO_USETAB = $10;
103 | ACO_UPDOWNKEYDROPSLIST = $20;
104 | ACO_RTLREADING = $40;
105 | ACO_WORD_FILTER = $80;
106 | ACO_NOPREFIXFILTERING = $100;
107 | }
108 | TACOption = (acNone, acAutoSuggest, acAutoAppend, acSearch, acFilterPrefixes,
109 | acUseTab, acUpDownKeyDropsList, acRTLReading, acWordFilter, acNoPrefixFiltering);
110 | TACOptions = set of TACOption;
111 | TACSource = (acsList, acsHistory, acsMRU, acsShell);
112 | TButtonedEdit = class(Vcl.ExtCtrls.TButtonedEdit)
113 | private
114 | FACList: TEnumString;
115 | FEnumString: IEnumString;
116 | FAutoComplete: IAutoComplete;
117 | FACEnabled: Boolean;
118 | FACOptions: TACOptions;
119 | FACSource: TACSource;
120 | function GetACStrings : TStringList;
121 | procedure SetACEnabled(const Value: Boolean);
122 | procedure SetACOptions(const Value: TACOptions);
123 | procedure SetACSource(const Value: TACSource);
124 | procedure SetACStrings(const Value: TStringList);
125 | class constructor Create;
126 | protected
127 | procedure CreateWnd; override;
128 | procedure DestroyWnd; override;
129 | public
130 | constructor Create(AOwner: TComponent); override;
131 | destructor Destroy; override;
132 | published
133 | property ACEnabled: Boolean read FACEnabled write SetACEnabled;
134 | property ACOptions: TACOptions read FACOptions write SetACOptions;
135 | property ACSource: TACSource read FACSource write SetACSource;
136 | property ACStrings: TStringList read GetACStrings write SetACStrings;
137 | end;
138 |
139 | TCommandType = (ctNormal, ctEnvironment, ctOther);
140 |
141 | TForm1 = class(TForm)
142 | DosCommand1: TDosCommand;
143 | ButtonedEdit1: TButtonedEdit;
144 | ImageList1: TImageList;
145 | BCEditor1: TSynEdit;
146 | StatusBar1: TStatusBar;
147 | SearchBox1: TSearchBox;
148 | TrayIcon1: TTrayIcon;
149 | PopupMenu1: TPopupMenu;
150 | Exit1: TMenuItem;
151 | Show1: TMenuItem;
152 | N1: TMenuItem;
153 | DzDirSeek1: TDzDirSeek;
154 | pnlPreview: TPanel;
155 | Splitter1: TSplitter;
156 | EsImage1: TEsImage;
157 | Image1: TImage;
158 | SynUNIXShellScriptSyn1: TSynUNIXShellScriptSyn;
159 | ListBox1: TListBox;
160 | ComboBox1: TComboBox;
161 | pnlTop: TPanel;
162 | IconFontsImageList1: TIconFontsImageList;
163 | rkSmartPath1: TrkSmartPath;
164 | PopupMenu2: TPopupMenu;
165 | OpenURL1: TMenuItem;
166 | CopyPathtoClipboard1: TMenuItem;
167 | SynPasSyn1: TSynPasSyn;
168 | SynMultiSyn1: TSynMultiSyn;
169 | SpeedButton1: TSpeedButton;
170 | IconFontsImageList2: TIconFontsImageList;
171 | ActionList1: TActionList;
172 | actPreview: TAction;
173 | actHide: TAction;
174 | ToolBar1: TToolBar;
175 | ToolButton1: TToolButton;
176 | Panel1: TPanel;
177 | actUnPin: TAction;
178 | actSigInt: TAction;
179 | VirtualMultiPathExplorerEasyListview1: TVirtualMultiPathExplorerEasyListview;
180 | actPath2Clip: TAction;
181 | tmrToast: TTimer;
182 | AppAutoStart1: TCBAutoStart;
183 | mnuAutoStart: TMenuItem;
184 | pnlTitle: TPanel;
185 | LinkLabel1: TLinkLabel;
186 | tmrOutput: TTimer;
187 | ActivityIndicator1: TActivityIndicator;
188 | ImageCollection1: TImageCollection;
189 | VirtualImageList1: TVirtualImageList;
190 | ACLHexView1: TACLHexView;
191 | ACLApplicationController1: TACLApplicationController;
192 | btnFileHandler: TSpeedButton;
193 | ACLShellTreeView1: TACLShellTreeView;
194 | procedure ButtonedEdit1Enter(Sender: TObject);
195 | procedure ButtonedEdit1KeyUp(Sender: TObject; var Key: Word;
196 | Shift: TShiftState);
197 | procedure DosCommand1ExecuteError(ASender: TObject; AE: Exception;
198 | var AHandled: Boolean);
199 | procedure DosCommand1NewLine(ASender: TObject; const ANewLine: string;
200 | AOutputType: TOutputType);
201 | procedure FormCreate(Sender: TObject);
202 | procedure FormDestroy(Sender: TObject);
203 | procedure FormShow(Sender: TObject);
204 | procedure FormKeyUp(Sender: TObject; var Key: Word; Shift: TShiftState);
205 | procedure Show1Click(Sender: TObject);
206 | procedure Exit1Click(Sender: TObject);
207 | procedure TrayIcon1DblClick(Sender: TObject);
208 | procedure DosCommand1Terminated(Sender: TObject);
209 | procedure DosCommand1TerminateProcess(ASender: TObject;
210 | var ACanTerminate: Boolean);
211 | procedure ListBox1DblClick(Sender: TObject);
212 | procedure ListBox1KeyUp(Sender: TObject; var Key: Word; Shift: TShiftState);
213 | procedure OpenURL1Click(Sender: TObject);
214 | procedure CopyPathtoClipboard1Click(Sender: TObject);
215 | procedure ButtonedEdit1KeyPress(Sender: TObject; var Key: Char);
216 | procedure BCEditor1DblClick(Sender: TObject);
217 | procedure SpeedButton1Click(Sender: TObject);
218 | procedure actPreviewExecute(Sender: TObject);
219 | procedure actUnPinExecute(Sender: TObject);
220 | procedure actSigIntExecute(Sender: TObject);
221 | procedure actPath2ClipExecute(Sender: TObject);
222 | procedure tmrToastTimer(Sender: TObject);
223 | procedure mnuAutoStartClick(Sender: TObject);
224 | procedure LinkLabel1LinkClick(Sender: TObject; const Link: string;
225 | LinkType: TSysLinkType);
226 | procedure tmrOutputTimer(Sender: TObject);
227 | procedure btnFileHandlerClick(Sender: TObject);
228 | procedure ACLShellTreeView1DblClick(Sender: TObject);
229 | procedure ACLShellTreeView1KeyPress(Sender: TObject; var Key: Char);
230 | private
231 | { Private declarations }
232 | FOutputBuffer: TStringList;
233 | FSyncLock: TCriticalSection;
234 |
235 | FPinned: Boolean;
236 | Items: TList;
237 | ThumbSizeW, ThumbSizeH: Integer;
238 | FhImageList48: Cardinal;
239 | FIconSize: Integer;
240 |
241 | FCommandOutput: TStringList;
242 |
243 | lastExplorerHandle: HWND;
244 | lastExplorerPath: String;
245 | lstExplorerPath: TStringList;
246 | lstExplorerWnd: TStringList;
247 | lstExplorerItem: TStringList;
248 |
249 | fPreview: THostPreviewHandler;
250 | fHexBuffer: TFileStream;
251 |
252 | function ListExplorerInstances:Integer;
253 | procedure KeyEventHandler(var Msg: TMessage); message KeyEvent;
254 | procedure KeyEventHandlerAll(var Msg: TMessage); message KeyEventAll;
255 | procedure KeyEventUpdatePath(var Msg: TMessage); message KeyEventUpdatePath;
256 | procedure KeyEventPickPaths(var Msg: TMessage); message KeyEventPickPaths;
257 | procedure OnFocusLost(Sender: TObject);
258 |
259 | function GetExplorerAddressBarRect(AHandle: HWND): TRect;
260 | function ShowPreview(const FileName: string): Boolean;
261 | procedure SwitchToWindow(AWnd: HWND);
262 |
263 | procedure ProcessDosCommand(Sender: TObject; ACommand: string; terminateCurrent: Boolean = False);
264 |
265 | procedure CMFocusChanged(var Msg: TCMFocusChanged); message CM_FOCUSCHANGED;
266 |
267 | procedure UpdateMainMenu(const ForeGroundWindow: HWND);
268 |
269 | procedure FlushIcons;
270 |
271 | procedure NoBorder(var Msg: TWMNCActivate); message WM_NCACTIVATE;
272 | protected
273 | procedure CreateParams(var Params: TCreateParams); override;
274 | procedure WndProc(var Message: TMessage); override;
275 | private
276 | FCommandType: TCommandType;
277 | FEnvExecutables: TStringList;
278 | FEnvStrings: TStringList;
279 | procedure UpdateStyle;
280 |
281 | procedure RefreshEnvironmentVariables;
282 | procedure WMSettingChange(var Msg: TMessage); message WM_SETTINGCHANGE;
283 |
284 | function ConvertImageToJpeg(const InputFileName, OutputFileName: string): Boolean;
285 | public
286 | { Public declarations }
287 | Directory: string;
288 | CurrentDir: string;
289 | CurrentFile: string;
290 | GitUrl: string;
291 |
292 | procedure Toast(aText, aTitle: string; sType: string = 'S,I,E'; ParentBase: TWinControl = nil);
293 |
294 | procedure populateCommands;
295 | procedure populateEnvironmentStrings;
296 | procedure populateMyFolders;
297 | procedure populateEnvExecutables;
298 |
299 | procedure UpdateTheme;
300 | end;
301 |
302 | var
303 | Form1: TForm1;
304 | args: TStringList;
305 |
306 | function StartHook:BOOL; stdcall; external 'HotkeyHook.dll' name 'STARTHOOK';
307 | procedure StopHook; stdcall; external 'HotkeyHook.dll' name 'STOPHOOK';
308 | procedure SwitchToThisWindow(h1: hWnd; x: bool); stdcall;
309 | external user32 Name 'SwitchToThisWindow';
310 |
311 | implementation
312 |
313 | {$R *.dfm}
314 |
315 | uses
316 | frmHover, UIAutomationClient, DarkModeApi.Vcl, Vcl.Themes,
317 | DarkModeApi, Winapi.UxTheme, CB.DarkMode, Ntapi.UserEnv, Ntapi.WinNt, Ntapi.ntrtl,
318 | pngimage, GIFImg, Cod.Imaging.Heif, Cod.Imaging.WebP, Vcl.SysStyles, ACL.Utils.Common;
319 |
320 | type
321 | THostPreviewHandlerClass = class(THostPreviewHandler);
322 |
323 | { Global Functions}
324 | function RtlGetVersion(var RTL_OSVERSIONINFOEXW): LONGINT; stdcall;
325 | external 'ntdll.dll' Name 'RtlGetVersion';
326 | function isWindows11:Boolean;
327 | var
328 | winver: RTL_OSVERSIONINFOEXW;
329 | begin
330 | Result := False;
331 | if ((RtlGetVersion(winver) = 0) and (winver.dwMajorVersion>=10) and (winver.dwBuildNumber > 22000)) then
332 | Result := True;
333 | end;
334 |
335 | procedure EnableNCShadow(Wnd: HWND);
336 | const
337 | DWMWCP_DEFAULT = 0; // Let the system decide whether or not to round window corners
338 | DWMWCP_DONOTROUND = 1; // Never round window corners
339 | DWMWCP_ROUND = 2; // Round the corners if appropriate
340 | DWMWCP_ROUNDSMALL = 3; // Round the corners if appropriate, with a small radius
341 | DWMWA_WINDOW_CORNER_PREFERENCE = 33; // [set] WINDOW_CORNER_PREFERENCE, Controls the policy that rounds top-level window corners
342 | var
343 | DWM_WINDOW_CORNER_PREFERENCE: Cardinal;
344 | begin
345 |
346 | if isWindows11 then
347 | begin
348 |
349 | DWM_WINDOW_CORNER_PREFERENCE := DWMWCP_ROUNDSMALL;
350 | DwmSetWindowAttribute(Wnd, DWMWA_WINDOW_CORNER_PREFERENCE, @DWM_WINDOW_CORNER_PREFERENCE, sizeof(DWM_WINDOW_CORNER_PREFERENCE));
351 | end;
352 | end;
353 |
354 |
355 | procedure UseImmersiveDarkMode(Handle: HWND; Enable: Boolean);
356 | const
357 | DWMWA_USE_IMMERSIVE_DARK_MODE_BEFORE_20H1 = 19;
358 | DWMWA_USE_IMMERSIVE_DARK_MODE = 20;
359 | var
360 | DarkMode: DWORD;
361 | Attribute: DWORD;
362 | begin
363 | //https://stackoverflow.com/a/62811758
364 | DarkMode := DWORD(Enable);
365 |
366 | if Win32MajorVersion = 10 then
367 | begin
368 | if Win32BuildNumber >= 17763 then
369 | begin
370 | Attribute := DWMWA_USE_IMMERSIVE_DARK_MODE_BEFORE_20H1;
371 | if Win32BuildNumber >= 18985 then
372 | Attribute := DWMWA_USE_IMMERSIVE_DARK_MODE;
373 | DwmSetWindowAttribute(Handle, Attribute, @DarkMode, SizeOf(DWord));
374 | SetWindowPos(Handle, 0, 0, 0, 0, 0, SWP_DRAWFRAME or SWP_NOACTIVATE or SWP_NOMOVE or SWP_NOSIZE or SWP_NOZORDER);
375 | end;
376 | end;
377 | end;
378 |
379 | function RunProcess(const Binary: string; const DirPath: string; args: TStrings): Boolean;
380 | const
381 | BufSize = 4096; //1024
382 | var
383 | Process: TProcess;
384 | Buf: AnsiString;
385 | Count: Integer;
386 | i: Integer;
387 | LineStart: Integer;
388 | OutputLine: AnsiString;
389 | begin
390 | Process := TProcess.Create(nil);
391 | try
392 | Process.Executable := Binary;
393 |
394 | Process.Options := [poUsePipes, poStderrToOutPut];
395 | Process.ShowWindow := swoHIDE;
396 |
397 | Process.Parameters.Assign(args);
398 | Process.CurrentDirectory := DirPath;
399 | Process.Execute;
400 |
401 | OutputLine := '';
402 | SetLength(Buf, BufSize);
403 | repeat
404 | if (Process.Output <> nil) then
405 | begin
406 | Count := Process.Output.Read(PChar(Buf)^, BufSize);
407 | end
408 | else
409 | Count := 0;
410 |
411 | LineStart := 1;
412 | i := 1;
413 | while i <= Count do
414 | begin
415 | if CharInSet(Buf[i], [#10, #13]) then
416 | begin
417 | OutputLine := OutputLine + Copy(Buf, LineStart, i - LineStart);
418 | Form1.BCEditor1.Lines.Add(OutputLine);
419 | OutputLine := '';
420 | if (i < Count) and (CharInSet(Buf[i], [#10, #13])) and (Buf[i] <> Buf[i + 1]) then
421 | Inc(i);
422 | LineStart := i + 1;
423 | end;
424 | Inc(i);
425 | end;
426 | OutputLine := Copy(Buf, LineStart, Count - LineStart + 1);
427 | until Count = 0;
428 |
429 | if OutputLine <> '' then
430 | Form1.BCEditor1.Lines.Add(OutputLine);
431 |
432 | Process.WaitOnExit;
433 | Result := Process.ExitStatus = 0;
434 | if not Result then
435 | Form1.BCEditor1.Lines.Add('Command ' + Process.Executable + ' failed with exit code: ' + IntToStr(Process.ExitStatus));
436 |
437 | finally
438 | FreeAndNil(Process);
439 | end;
440 | end;
441 |
442 | function IsGitRepository(const Dir: string): Boolean;
443 | var
444 | repo: Pgit_repository;
445 | dirPath: PAnsiChar;
446 | error: Integer;
447 | begin
448 | dirPath := PAnsiChar(AnsiString(Dir));
449 | error := git_repository_open(@repo, dirPath);
450 |
451 | if error = 0 then
452 | begin
453 | git_repository_free(repo);
454 | Result := True;
455 | end
456 | else
457 | Result := False;
458 | end;
459 |
460 | function IsGit(const RepoDir): boolean;
461 | var
462 | repo: Pgit_repository;
463 | remote: Pgit_remote;
464 | dirPath, remoteNamePAnsi: PAnsiChar;
465 | remoteURL: PAnsiChar;
466 | error: Integer;
467 | begin
468 | Result := False;
469 |
470 | dirPath := PAnsiChar(AnsiString(RepoDir));
471 |
472 | // Open the repository
473 | error := git_repository_open(@repo, dirPath);
474 | if error <> 0 then
475 | Exit;
476 | Result := True;
477 | // Free the repository resource
478 | git_repository_free(repo);
479 | end;
480 |
481 |
482 | function GetRemoteURL(const RepoDir, RemoteName: string): string;
483 | var
484 | repo: Pgit_repository;
485 | remote: Pgit_remote;
486 | dirPath, remoteNamePAnsi: PAnsiChar;
487 | remoteURL: PAnsiChar;
488 | error: Integer;
489 | begin
490 | Result := '';
491 |
492 | dirPath := PAnsiChar(AnsiString(RepoDir));
493 | remoteNamePAnsi := PAnsiChar(AnsiString(RemoteName));
494 |
495 | // Open the repository
496 | error := git_repository_open(@repo, dirPath);
497 | if error <> 0 then
498 | Exit;
499 |
500 | // Look up the remote by its name
501 | error := git_remote_lookup(@remote, repo, remoteNamePAnsi);
502 |
503 | if error = 0 then
504 | begin
505 | // Get the remote URL
506 | remoteURL := git_remote_url(remote);
507 | Result := string(remoteURL);
508 |
509 | // Free the remote resource
510 | git_remote_free(remote);
511 | end;
512 |
513 | // Free the repository resource
514 | git_repository_free(repo);
515 | end;
516 |
517 | function ExtractThumbnail(Path: string; SizeX, SizeY: Integer; InitOle: Boolean = False): HBitmap;
518 | var
519 | ShellFolder, DesktopShellFolder: IShellFolder;
520 | XtractImage: IExtractImage;
521 | Eaten: DWord;
522 | PIDL: PItemIDList;
523 | RunnableTask: IRunnableTask;
524 | Flags: DWord;
525 | Buf: array [0 .. MAX_PATH] of Char;
526 | BmpHandle: HBITMAP;
527 | Atribute, Priority: DWord;
528 | GetLocationRes: HResult;
529 | ASize: TSize;
530 | begin
531 | Result := 0;
532 | try
533 | if InitOle then
534 | CoInitializeEx(nil, COINIT_APARTMENTTHREADED or COINIT_DISABLE_OLE1DDE);
535 | try
536 | OleCheck(SHGetDesktopFolder(DesktopShellFolder));
537 | OleCheck(DesktopShellFolder.ParseDisplayName(0, nil, StringToOleStr(ExtractFilePath(Path)),
538 | Eaten, PIDL, Atribute));
539 | OleCheck(DesktopShellFolder.BindToObject(PIDL, nil, IID_IShellFolder, Pointer(ShellFolder)));
540 | CoTaskMemFree(PIDL);
541 |
542 | OleCheck(ShellFolder.ParseDisplayName(0, nil, StringToOleStr(ExtractFileName(Path)), Eaten, PIDL, Atribute));
543 | ShellFolder.GetUIObjectOf(0, 1, PIDL, IExtractImage, nil, XtractImage);
544 | CoTaskMemFree(PIDL);
545 |
546 | if Assigned(XtractImage) then // Try getting a thumbnail..
547 | begin
548 | RunnableTask := nil;
549 | ASize.cx := SizeX;
550 | ASize.cy := SizeY;
551 | Priority := 0;
552 | Flags:= IEIFLAG_ASPECT or IEIFLAG_OFFLINE or IEIFLAG_CACHE or IEIFLAG_QUALITY;
553 | GetLocationRes := XtractImage.GetLocation(Buf, SizeOf(Buf), Priority, ASize, 32, Flags);
554 | if (GetLocationRes = NOERROR) or (GetLocationRes = E_PENDING) then
555 | begin
556 | if GetLocationRes = E_PENDING then
557 | if XtractImage.QueryInterface(IRunnableTask, RunnableTask) <> S_OK then
558 | RunnableTask := nil;
559 | try
560 | //do not call OleCheck for debug
561 | XtractImage.Extract(BmpHandle);
562 | // This could consume a long time.
563 | Result := BmpHandle;
564 | except
565 | on E: EOleSysError do
566 | OutputDebugString(PChar(string(E.ClassName) + ': ' + E.message))
567 | end; // try/except
568 | end;
569 | end;
570 |
571 | finally
572 | if InitOle then
573 | CoUninitialize;
574 | end;
575 | except
576 | Result := 0;
577 | end;
578 | end;
579 |
580 | procedure HackAlpha(ABitmap: TBitmap; Color: TColor);
581 | type
582 | PRGB32 = ^TRGB32;
583 | TRGB32 = record
584 | B, G, R, A: Byte;
585 | end;
586 | PPixel32 = ^TPixel32;
587 | TPixel32 = array[0..0] of TRGB32;
588 | var
589 | Row: PPixel32;
590 | X, Y, slMain, slSize: Integer;
591 | R, G, B: Byte;
592 | c: Integer;
593 | begin
594 | ABitmap.PixelFormat := pf32bit;
595 | c := ColorToRGB(Color);
596 | R := Byte(c);
597 | G := Byte(c shr 8);
598 | B := Byte(c shr 16);
599 | slMain := Integer(ABitmap.ScanLine[0]);
600 | slSize := Integer(ABitmap.ScanLine[1]) - slMain;
601 | for Y := 0 to ABitmap.Height - 1 do
602 | begin
603 | Row := PPixel32(slMain);
604 | for X := 0 to ABitmap.Width - 1 do
605 | begin
606 | Row[X].R := Row[X].A * (Row[X].R - R) shr 8 + R;
607 | Row[X].G := Row[X].A * (Row[X].G - G) shr 8 + G;
608 | Row[X].B := Row[X].A * (Row[X].B - B) shr 8 + B;
609 | end;
610 | slMain := slMain + slSize;
611 | end;
612 | end;
613 |
614 | function HackIconSize(ABitmap: TBitmap): TPoint;
615 | type
616 | PPixel32 = ^TPixel32;
617 | TPixel32 = array [0..0] of Cardinal;
618 | var
619 | Row: PPixel32;
620 | X, Y, i, j, slMain, slSize: Integer;
621 | begin
622 | ABitmap.PixelFormat := pf32bit;
623 | Result.X := ABitmap.Width;
624 | Result.Y := ABitmap.Height;
625 | if (Result.X < 1) or (Result.Y < 1) then
626 | Exit;
627 | slMain := Integer(ABitmap.ScanLine[0]);
628 | slSize := Integer(ABitmap.ScanLine[1]) - slMain;
629 | Result.X := 0;
630 | Result.Y := 0;
631 | for Y := 0 to ABitmap.Height - 1 do
632 | begin
633 | Row := PPixel32(slMain);
634 | for X := 0 to ABitmap.Width - 1 do
635 | begin
636 | if (Row[X] and $FF000000) <> 0 then
637 | begin
638 | if X > Result.X then
639 | Result.X := X;
640 | if Y > Result.Y then
641 | Result.Y := Y;
642 | end;
643 | end;
644 | slMain := slMain + slSize;
645 | end;
646 | I := Math.Max(Result.X, Result.Y);
647 | j := 0;
648 | while I > j do
649 | j := j + 8;
650 | if j > 256 then
651 | j := 256;
652 | Result.X := j;
653 | Result.Y := Result.X;
654 | end;
655 |
656 | procedure GetIconFromFile(AFile: string; var AIcon: TIcon; SHIL_FLAG: Cardinal);
657 | var
658 | LImgList: HIMAGELIST;
659 | SFI: TSHFileInfo;
660 | LIndex: Integer;
661 | begin
662 | // Get the index of the imagelist
663 | SHGetFileInfo(PChar(AFile), FILE_ATTRIBUTE_NORMAL, SFI, SizeOf(TSHFileInfo),
664 | SHGFI_ICON {or SHGFI_LARGEICON} or SHGFI_SHELLICONSIZE or
665 | SHGFI_SYSICONINDEX or SHGFI_TYPENAME or SHGFI_DISPLAYNAME);
666 | if not Assigned(AIcon) then
667 | AIcon := TIcon.Create;
668 | // get image list
669 | SHGetImageList(SHIL_FLAG, IID_IImageList, Pointer(LImgList));
670 | // its index
671 | LIndex := SFI.iIcon;
672 | // seems that ILD_NORMAL returns bad result in Windows 7, so opt for ILD_IMAGE
673 | AIcon.Handle := ImageList_GetIcon(LImgList, LIndex, ILD_IMAGE);
674 | end;
675 |
676 | procedure Graphic2Bitmap(const ASrc: TGraphic; const ADest: TBitmap;
677 | const ATransparentColor: TColor);
678 | var
679 | LCrop: TPoint;
680 | begin
681 | if not Assigned(ASrc) or not Assigned(ADest) then
682 | Exit;
683 | if (ASrc.Width = 0) or (ASrc.Height = 0) then
684 | Exit;
685 |
686 | ADest.Width := ASrc.Width;
687 | ADest.Height := ASrc.Height;
688 | if ASrc.Transparent then
689 | begin
690 | ADest.Transparent := True;
691 | if (ATransparentColor <> clNone) then
692 | begin
693 | ADest.TransparentColor := ATransparentColor;
694 | ADest.TransparentMode := tmFixed;
695 | ADest.Canvas.Brush.Color := ATransparentColor;
696 | end
697 | else
698 | ADest.TransparentMode := tmAuto;
699 | end;
700 |
701 | ADest.Canvas.FillRect(Rect(0, 0, ADest.Width, ADest.Height));
702 | ADest.Canvas.Draw(0, 0, ASrc);
703 | LCrop := HackIconSize(ADest);
704 | ADest.Width := LCrop.X;
705 | ADest.Height := LCrop.Y;
706 | end;
707 |
708 | function Byte2Str(const i64Size: Int64): string;
709 | const
710 | i64GB = 1024 * 1024 * 1024;
711 | i64MB = 1024 * 1024;
712 | i64KB = 1024;
713 | begin
714 | if i64Size div i64GB > 0 then
715 | Result := Format('%.1f GB', [i64Size / i64GB])
716 | else if i64Size div i64MB > 0 then
717 | Result := Format('%.2f MB', [i64Size / i64MB])
718 | else if i64Size div i64KB > 0 then
719 | Result := Format('%.0f KB', [i64Size / i64KB])
720 | else
721 | Result := IntToStr(i64Size) + ' bytes';
722 | end;
723 |
724 | function CalcTHumbSize(Width, Height, ThumbWidth, ThumbHeight: Cardinal): Cardinal;
725 | begin
726 | Result := 0;
727 | if (Width = 0) or (Height = 0) then
728 | Exit;
729 | if (Width < ThumbWidth) and (Height < ThumbHeight) then
730 | Result := (Width shl 16) + Height
731 | else
732 | begin
733 | if Width > Height then
734 | begin
735 | if Width < ThumbWidth then
736 | ThumbWidth := Width;
737 | Result := (ThumbWidth shl 16) + Trunc(ThumbWidth * Height / Width);
738 | if (Result and $FFFF) >ThumbHeight then
739 | Result := (Trunc(ThumbHeight * Width / Height) shl 16) + ThumbHeight;
740 | end
741 | else
742 | begin
743 | if Height < ThumbHeight then
744 | ThumbHeight := Height;
745 | Result := (Trunc(ThumbHeight * Width / Height) shl 16) + ThumbHeight;
746 | if ((Result shr 16) and $FFFF) > ThumbWidth then
747 | Result := (ThumbWidth shl 16) + Trunc(ThumbWidth * Height / Width);
748 | end;
749 | end;
750 | end;
751 |
752 | function Blend(Color1, Color2: TColor; A: Byte): TColor;
753 | var
754 | C1, C2: LongInt;
755 | R, G, B, v1, v2: Byte;
756 | begin
757 | A := Round(2.55 * A);
758 | C1 := ColorToRGB(Color1);
759 | C2 := COlorToRGB(COlor2);
760 | v1 := Byte(C1);
761 | v2 := Byte(C2);
762 | R := A * (v1 - v2) shr 8 + v2;
763 | v1 := Byte(C1 shr 8);
764 | v2 := Byte(C2 shr 8);
765 | G := A * (v1 - v2) shr 8 + v2;
766 | v1 := Byte(C1 shr 16);
767 | v2 := Byte(C2 shr 16);
768 | B := A * (v1 - v2) shr 8 + v2;
769 | Result := (B shl 16) + (G shl 8) + R;
770 | end;
771 |
772 | procedure WinGradient(DC: HDC; ARect: TRect; AColor1, AColor2: TColor);
773 | var
774 | Vertexs: array[0..1] of TTriVertex;
775 | GRect: TGradientRect;
776 | begin
777 | Vertexs[0].x := ARect.Left;
778 | Vertexs[0].y := ARect.Top;
779 | Vertexs[0].Red := (AColor1 and $000000FF) shl 8;
780 | Vertexs[0].Green := (AColor1 and $0000FF00);
781 | Vertexs[0].Blue := (AColor1 and $00FF0000) shr 8;
782 | Vertexs[0].Alpha := 0;
783 | Vertexs[1].x := ARect.Right;
784 | Vertexs[1].y := ARect.Bottom;
785 | Vertexs[1].Red := (AColor2 and $000000FF) shl 8;
786 | Vertexs[1].Green := (AColor2 and $0000FF00);
787 | Vertexs[1].Blue := (AColor2 and $00FF0000) shr 8;
788 | Vertexs[1].Alpha := 0;
789 | GRect.UpperLeft := 0;
790 | GRect.LowerRight := 1;
791 | GradientFill(DC, @Vertexs, 2, @GRect, 1, GRADIENT_FILL_RECT_V);
792 | end;
793 |
794 | function CompareNatural(s1, s2: string): Integer;
795 | function ExtractNr(n: Integer; var Txt: string): Int64;
796 | begin
797 | while (n <= Length(Txt)) and (Txt[n] >= '0') and (Txt[n] <= '9') do
798 | n := n + 1;
799 | Result := StrToInt64Def(Copy(Txt, 1, n - 1), 0);
800 | Delete(Txt, 1, (n - 1));
801 | end;
802 | var
803 | B: Boolean;
804 | begin
805 | Result := 0;
806 | s1 := LowerCase(s1);
807 | s2 := LowerCase(s2);
808 | if (s1 <> s2) and (s1 <> '') and (s2 <> '') then
809 | begin
810 | B := False;
811 | while (not B) do
812 | begin
813 | if ((s1[1] >= '0') and (s1[1] <= '9'))
814 | and ((s2[1] >= '0') and (s2[1] <= '9'))
815 | then
816 | Result := Sign(ExtractNr(1, s1) - ExtractNr(1, s2))
817 | else
818 | Result := Sign(Integer(s1[1]) - Integer(s2[1]));
819 | B := (Result <> 0) or (Min(Length(s1), Length(s2)) < 2);
820 | if not B then
821 | begin
822 | Delete(s1, 1, 1);
823 | Delete(s2, 1, 1);
824 | end;
825 | end;
826 | end;
827 | if Result = 0 then
828 | begin
829 | if (Length(s1) = 1) and (Length(s2) = 1) then
830 | Result := Sign(Integer(s1[1]) - Integer(s2[1]))
831 | else
832 | Result := Sign(Length(s1) - Length(s2));
833 | end;
834 | end;
835 |
836 | // a custom sort
837 | function SortItem(List: rkIntegerList.TIntList; Index1, Index2: Integer): Integer;
838 | var
839 | Item1, Item2: PItemData;
840 | begin
841 | Item1 := Form1.Items[List[Index1]];
842 | Item2 := Form1.Items[List[Index2]];
843 | if Item1.Dir and Item2.Dir then
844 | Result := CompareNatural(Item1.Name, Item2.Name)
845 | else if Item1.Dir then
846 | Result := -1
847 | else if Item2.Dir then
848 | Result := 1
849 | else
850 | Result := CompareNatural(Item1.Name, Item2.Name);
851 | end;
852 |
853 | { Form1 }
854 |
855 | procedure TForm1.ACLShellTreeView1DblClick(Sender: TObject);
856 | begin
857 | ShellExecute(0, 'OPEN', PChar(ACLShellTreeView1.GetFullPath(ACLShellTreeView1.FocusedNode)), nil, nil, SW_SHOWNORMAL);
858 | end;
859 |
860 | procedure TForm1.ACLShellTreeView1KeyPress(Sender: TObject; var Key: Char);
861 | begin
862 | if Key = #13 then
863 | ShellExecute(0, 'OPEN', PChar(ACLShellTreeView1.GetFullPath(ACLShellTreeView1.FocusedNode)), nil, nil, SW_SHOWNORMAL);
864 | end;
865 |
866 | procedure TForm1.actPath2ClipExecute(Sender: TObject);
867 | begin
868 | // Copy current path to clipboard
869 | if not CurrentDir.IsEmpty and DirectoryExists(CurrentDir) then
870 | begin
871 | Clipboard.AsText := CurrentDir;
872 | Toast('Path copied to clipboard!', 'Current Path', 'S');
873 | end;
874 | end;
875 |
876 | procedure TForm1.actPreviewExecute(Sender: TObject);
877 | begin
878 | pnlPreview.Visible := not pnlPreview.Visible;
879 | end;
880 |
881 | procedure TForm1.actSigIntExecute(Sender: TObject);
882 | begin
883 | if DosCommand1.IsRunning then
884 | DosCommand1.SigInt;
885 | end;
886 |
887 | procedure TForm1.actUnPinExecute(Sender: TObject);
888 | begin
889 | SpeedButton1Click(Sender);
890 | end;
891 |
892 | procedure TForm1.BCEditor1DblClick(Sender: TObject);
893 | begin
894 | UpdateMainMenu(lastExplorerHandle);
895 | end;
896 |
897 | procedure TForm1.btnFileHandlerClick(Sender: TObject);
898 | begin
899 | btnFileHandler.Visible := False;
900 | Panel1.Caption := '';
901 | if Assigned(fHexBuffer) then
902 | fHexBuffer.Free;
903 | ACLHexView1.Visible := False;
904 | try
905 | ACLHexView1.Data := nil;
906 | ACLHexView1.FullRefresh;
907 | except
908 | end;
909 | end;
910 |
911 | procedure TForm1.ButtonedEdit1Enter(Sender: TObject);
912 | begin
913 | // ButtonedEdit1.RightButton.Visible := True;
914 | end;
915 |
916 | procedure TForm1.ButtonedEdit1KeyPress(Sender: TObject; var Key: Char);
917 | begin
918 | // avoid ding sound
919 | if (Key = #13) or (Key = #27) then
920 | Key := #0;
921 | end;
922 |
923 | procedure TForm1.ButtonedEdit1KeyUp(Sender: TObject; var Key: Word;
924 | Shift: TShiftState);
925 | var
926 | I: Integer;
927 | CLI: string;
928 | begin
929 | CLI := ButtonedEdit1.Text;
930 | if key = 13 then
931 | begin
932 | // populateCommands;
933 | if CLI = 'list' then
934 | begin
935 | ListExplorerInstances;
936 | BCEditor1.Text := 'Current HWND: ' + IntToStr(lastExplorerHandle) + '';
937 | for I := 0 to lstExplorerPath.Count - 1 do
938 | begin
939 | if lstExplorerWnd[I] = IntToStr(lastExplorerHandle) then
940 |
941 | BCEditor1.Text := BCEditor1.Text + #13#10 + lstExplorerPath[I] + ' ' + lstExplorerWnd[i];
942 | end;
943 | end
944 | else if CLI = 'items' then
945 | begin
946 | ListExplorerInstances;
947 | BCEditor1.Text := 'Current HWND: ' + IntToStr(lastExplorerHandle) + '';
948 | for I := 0 to lstExplorerItem.Count - 1 do
949 | begin
950 | BCEditor1.Text := BCEditor1.Text + #13#10 + lstExplorerItem[I];
951 | end;
952 |
953 | end
954 | else if CLI = '%' then
955 | begin
956 | populateEnvironmentStrings;
957 | end
958 | else if CLI = 'hexview' then
959 | begin
960 | var curFile := StatusBar1.Panels[0].Text;
961 | if FileExists(curFile) then
962 | begin
963 | if Assigned(fHexBuffer) then
964 | fHexBuffer.Free;
965 | fHexBuffer := TFileStream.Create(curFile, fmOpenRead);
966 | try
967 | Panel1.Caption := 'Hex: ' + curFile;
968 | btnFileHandler.Visible := True;
969 | ACLHexView1.Visible := True;
970 | ACLHexView1.StyleScrollBox.Reset;
971 | ACLHexView1.SetSelection(0, 0);
972 | ACLHexView1.Data := nil;
973 | ACLHexView1.FullRefresh;
974 | ACLHexView1.Data := fHexBuffer;
975 | finally
976 | //fHexBuffer.Free; //we should keep this open so the hex viewer will read on demand
977 | end;
978 | end;
979 | end
980 | else if CLI = 'preview' then
981 | begin
982 | var curFile := StatusBar1.Panels[0].Text;
983 | if FileExists(curFile) then
984 | BCEditor1.Lines.LoadFromFile(curFile);
985 | end
986 | else if CLI = 'tojpg' then
987 | begin
988 | var curFile := StatusBar1.Panels[0].Text;
989 | if FileExists(curFile) then
990 | begin
991 | if ConvertImageToJpeg(curFile, curFile +'.jpg') then
992 | begin
993 | BCEditor1.Clear;
994 | BCEditor1.Lines.Add('Image converted to JPG %90');
995 | BCEditor1.Lines.Add(curFile + '.jpg');
996 | end;
997 | end;
998 | end
999 | else if CLI = 'center' then
1000 | begin
1001 | if IsZoomed(lastExplorerHandle) then Exit;
1002 |
1003 | var _R: TRect;
1004 | var _M: TMonitor;
1005 | GetWindowRect(lastExplorerHandle, _R);
1006 | _M := Screen.MonitorFromRect(_R);
1007 | if (_R.Width > 0) and (_R.Height > 0) then
1008 | begin
1009 | var NewPos: TPoint;
1010 | NewPos.X := _M.Left + (_M.Width - _R.Width) div 2;
1011 | NewPos.Y := _M.Top + (_M.Height - _R.Height) div 2;
1012 | MoveWindow(lastExplorerHandle, NewPos.X, NewPos.Y, _R.Width, _R.Height, True);
1013 | end;
1014 | end
1015 | else if CLI = 'cmd' then
1016 | begin
1017 | if DirectoryExists(lastExplorerPath) then
1018 | // ShellExecute(0, PChar('OPEN'), PChar('cmd.exe'), PChar('/k refreshenv && cd /d ' + lastExplorerPath), PChar(lastExplorerPath), SW_SHOWNORMAL);
1019 | ShellExecute(0, PChar('OPEN'), PChar('cmd.exe'), PChar('/k cd /d ' + lastExplorerPath), PChar(lastExplorerPath), SW_SHOWNORMAL)
1020 | else
1021 | ShellExecute(0, PChar('OPEN'), PChar('cmd.exe'), PChar('/k cd %USERPROFILE%'), nil, SW_SHOWNORMAL)
1022 | end
1023 | else if CLI = 'env' then
1024 | begin
1025 | BCEditor1.Lines.Clear;
1026 | BCEditor1.Lines.Add('[Environment PATH]');
1027 | for var _env in FEnvStrings do
1028 | BCEditor1.Lines.Add(PChar(_env));
1029 | end
1030 | else if CLI = 'flushicons' then
1031 | begin
1032 | FlushIcons;
1033 | end
1034 | // show file explorer quick access directories
1035 | else if CLI = ':' then
1036 | begin
1037 | populateMyFolders;
1038 | end
1039 | else if CLI = '>' then
1040 | begin
1041 | populateEnvExecutables;
1042 | end
1043 | else if Pos('>', CLI) = 1 then
1044 | begin
1045 | if Cli.Length > 1 then
1046 | begin
1047 | var command := Copy(CLI,2, Length(CLI) - 1);
1048 |
1049 | ShellExecute(0, PChar('OPEN'), PChar(command), nil, PChar(lastExplorerPath), SW_SHOWNORMAL);
1050 | end
1051 | end
1052 | else if Pos('find ', CLI) = 1 then
1053 | begin
1054 | if DirectoryExists(lastExplorerPath) then
1055 | begin
1056 | DzDirSeek1.Dir := lastExplorerPath;
1057 | DzDirSeek1.MaskKind := TDSMaskKind.mkInclusions;
1058 | DzDirSeek1.Masks.Clear;
1059 | DzDirSeek1.Masks.Add(Copy(CLI,6));
1060 | DzDirSeek1.ResultKind := TDSResultKind.rkRelative;
1061 | DzDirSeek1.Seek;
1062 | BCEditor1.Lines.Clear;
1063 | BCEditor1.Text := DzDirSeek1.List.GetText;
1064 | end;
1065 | end
1066 |
1067 | else if CLI = 'listexplorers' then
1068 | begin
1069 | ListBox1.Items := lstExplorerPath;
1070 | ListBox1.Show;
1071 | if ListBox1.Visible then
1072 | ListBox1.SetFocus;
1073 | end
1074 |
1075 | else if CLI = 'exit' then
1076 | close
1077 | else
1078 | begin
1079 |
1080 | try
1081 | begin
1082 | BCEditor1.Lines.Clear;
1083 | if CLI.Contains('=') then
1084 | begin
1085 | var ls := TStringList.Create;
1086 | try
1087 | ls.Delimiter := '=';
1088 | ls.DelimitedText := CLI;
1089 | if ls.Count > 1 then
1090 | begin
1091 | if DirectoryExists(ls[1]) then
1092 | begin
1093 | ShellExecute(0, PChar('OPEN'), PChar(ls[1]), nil, nil, SW_SHOWNORMAL);
1094 | end;
1095 | end;
1096 |
1097 | finally
1098 | ls.Free;
1099 | end;
1100 | end
1101 | else
1102 | if DirectoryExists(lastExplorerPath) then
1103 | begin
1104 | var basePath := lastExplorerPath;
1105 | if DirectoryExists(CurrentFile) then
1106 | basePath := CurrentFile;
1107 |
1108 | // Temporary disabled to try DOSCommand Instead
1109 | // args := TStringList.Create;
1110 | // args.Add('/c');
1111 | //// args.Add('chcp');
1112 | //// args.Add('65001');
1113 | //// args.Add('&');
1114 |
1115 | if OpenURL1.Enabled then //git folder detected
1116 | begin
1117 | if (CLI = 'gp') or CLI.Contains('-pull') then
1118 | begin
1119 | ButtonedEdit1.Text := 'git -c fetch.parallel=0 -c submodule.fetchjobs=0 pull --progress "origin"';
1120 | end
1121 | else if (CLI = 'gu') or CLI.Contains('-url') then
1122 | begin
1123 | ButtonedEdit1.Text := 'giturl';
1124 | end
1125 | else if (CLI = 'gr') or Cli.Contains('-readme') then
1126 | begin
1127 | var readmePath := basePath + '\README.md';
1128 | if FileExists(readmePath) then
1129 | ButtonedEdit1.Text := ('start ' + readmePath)
1130 | else
1131 | ButtonedEdit1.Text := ('echo NO README FOUND!');
1132 | end;
1133 | CLI := ButtonedEdit1.Text;
1134 | end;
1135 | // args.Add(CLI);
1136 | // RunProcess('cmd.exe', PChar(basePath), args);
1137 | // Toast('Command finished!', '','S');
1138 | // BCEditor1.GotoLineAndCenter(BCEditor1.Lines.Count);
1139 | // args.Free;
1140 | // args := nil;
1141 |
1142 | DosCommand1.CurrentDir := basePath;
1143 | // DosCommand1.CommandLine := 'cmd.exe /c ' + ButtonedEdit1.Text;
1144 | // DosCommand1.Execute;
1145 | ProcessDosCommand(Self, PChar('cmd.exe /c ' + CLI));
1146 | end;
1147 | end
1148 | except
1149 | //on E:Exception do
1150 |
1151 | end;
1152 |
1153 | end;
1154 | ButtonedEdit1.Text := '';
1155 | end;
1156 | end;
1157 |
1158 | procedure TForm1.CMFocusChanged(var Msg: TCMFocusChanged);
1159 | begin
1160 | ListBox1.Visible := ListBox1.Focused;
1161 |
1162 |
1163 | inherited;
1164 | end;
1165 |
1166 | function TForm1.ConvertImageToJpeg(const InputFileName,
1167 | OutputFileName: string): Boolean;
1168 | const
1169 | // Hex headers for different formats
1170 | BMPHeader: array[0..1] of Byte = ($42, $4D); // BM
1171 | PNGHeader: array[0..7] of Byte = ($89, $50, $4E, $47, $0D, $0A, $1A, $0A); // PNG signature
1172 | GIFHeader: array[0..2] of Byte = ($47, $49, $46); // GIF
1173 | JPEGHeader: array[0..1] of Byte = ($FF, $D8); // JPEG SOI marker
1174 | WebPHeader: array[0..3] of Byte = ($52, $49, $46, $46); // RIFF for WebP
1175 | HEIFHeader: array[0..3] of Byte = ($66, $74, $79, $70); // ftyp for HEIF
1176 | var
1177 | FileStream: TFileStream;
1178 | Header: TBytes;
1179 | InputImage: TGraphic;
1180 | JPEGImage: TJPEGImage;
1181 | Bitmap: TBitmap;
1182 | FormatValid: Boolean;
1183 |
1184 | function CompareHeader(const FileHeader, ValidHeader: array of Byte): Boolean;
1185 | var
1186 | I: Integer;
1187 | begin
1188 | Result := Length(FileHeader) >= Length(ValidHeader);
1189 | if Result then
1190 | for I := 0 to High(ValidHeader) do
1191 | if FileHeader[I] <> ValidHeader[I] then
1192 | Exit(False);
1193 | end;
1194 |
1195 | function ConfirmOverwrite(const FileName: string): Boolean;
1196 | begin
1197 | Result := not FileExists(FileName) or
1198 | (MessageDlg(Format('File "%s" already exists. Do you want to overwrite it?',
1199 | [FileName]), mtConfirmation, [mbYes, mbNo], 0) = mrYes);
1200 | end;
1201 |
1202 | begin
1203 | Result := False;
1204 | FormatValid := False;
1205 | Header := nil;
1206 | InputImage := nil;
1207 | JPEGImage := nil;
1208 | Bitmap := nil;
1209 |
1210 | // Check if output file exists and confirm overwrite
1211 | if not ConfirmOverwrite(OutputFileName) then
1212 | Exit;
1213 |
1214 | try
1215 | // Open file to read header
1216 | FileStream := TFileStream.Create(InputFileName, fmOpenRead or fmShareDenyWrite);
1217 | try
1218 | SetLength(Header, 8); // Longest header is 8 bytes (PNG)
1219 | FileStream.ReadBuffer(Header[0], Length(Header));
1220 | finally
1221 | FileStream.Free;
1222 | end;
1223 |
1224 | // Validate format based on header
1225 | if CompareHeader(Header, BMPHeader) then
1226 | InputImage := TBitmap.Create
1227 | else if CompareHeader(Header, PNGHeader) then
1228 | InputImage := TPngImage.Create
1229 | else if CompareHeader(Header, GIFHeader) then
1230 | InputImage := TGIFImage.Create
1231 | else if CompareHeader(Header, JPEGHeader) then
1232 | InputImage := TJPEGImage.Create
1233 | else if CompareHeader(Header, WebPHeader) then
1234 | InputImage := TWebPImage.Create
1235 | else if CompareHeader(Header, HEIFHeader) then
1236 | InputImage := THEIFImage.Create
1237 | else
1238 | raise EInvalidImageFormat.Create('Unsupported image format.');
1239 |
1240 | FormatValid := True;
1241 |
1242 | // Load image into InputImage
1243 | InputImage.LoadFromFile(InputFileName);
1244 |
1245 | // Create intermediate bitmap for PNG and HEIF
1246 | if (InputImage is TPngImage) or (InputImage is THEIFImage) then
1247 | begin
1248 | Bitmap := TBitmap.Create;
1249 | try
1250 | Bitmap.Width := InputImage.Width;
1251 | Bitmap.Height := InputImage.Height;
1252 | Bitmap.Canvas.Draw(0, 0, InputImage);
1253 |
1254 | // Convert to JPEG
1255 | JPEGImage := TJPEGImage.Create;
1256 | try
1257 | JPEGImage.Assign(Bitmap); // Assign from bitmap instead of direct conversion
1258 | JPEGImage.CompressionQuality := 90;
1259 | JPEGImage.SaveToFile(OutputFileName);
1260 | Result := True;
1261 | finally
1262 | JPEGImage.Free;
1263 | end;
1264 | finally
1265 | Bitmap.Free;
1266 | end;
1267 | end
1268 | else
1269 | begin
1270 | // Direct conversion for other formats
1271 | JPEGImage := TJPEGImage.Create;
1272 | try
1273 | JPEGImage.Assign(InputImage);
1274 | JPEGImage.CompressionQuality := 90;
1275 | JPEGImage.SaveToFile(OutputFileName);
1276 | Result := True;
1277 | finally
1278 | JPEGImage.Free;
1279 | end;
1280 | end;
1281 | except
1282 | on E: Exception do
1283 | raise Exception.CreateFmt('Error converting image: %s', [E.Message]);
1284 | end;
1285 |
1286 | // Clean up
1287 | if not FormatValid then
1288 | raise EInvalidImageFormat.Create('Image format validation failed.');
1289 | if Assigned(InputImage) then
1290 | InputImage.Free;
1291 | end;
1292 |
1293 | procedure TForm1.CopyPathtoClipboard1Click(Sender: TObject);
1294 | begin
1295 | Clipboard.SetTextBuf(PChar(CurrentDir));
1296 | end;
1297 |
1298 | procedure TForm1.CreateParams(var Params: TCreateParams);
1299 | begin
1300 | inherited;
1301 |
1302 | Params.WinClassName := 'ExplorerCommandWnd';
1303 | end;
1304 |
1305 | procedure TForm1.DosCommand1ExecuteError(ASender: TObject; AE: Exception;
1306 | var AHandled: Boolean);
1307 | begin
1308 | if AHandled then
1309 | BCEditor1.Lines.Text := AE.ToString;
1310 | end;
1311 |
1312 | //procedure TForm1.DosCommand1NewChar(ASender: TObject; ANewChar: Char);
1313 | //begin
1314 | // BCEditor1.BeginUpdate;
1315 | //
1316 | // if ANewChar <> #13 then
1317 | // BCEditor1.Text := BCEditor1.Text + ANewChar;
1318 | //
1319 | // BCEditor1.GotoLineAndCenter(BCEditor1.Lines.Count);
1320 | //
1321 | // BCEditor1.EndUpdate;
1322 | // KHexEditor1.ExecuteCommand(ecInsertString, PChar(ANewChar));
1323 | //end;
1324 |
1325 | procedure TForm1.DosCommand1NewLine(ASender: TObject; const ANewLine: string;
1326 | AOutputType: TOutputType);
1327 | begin
1328 | //// AOutputType := otEntireLine;
1329 | //// BCEditor1.Lines.Add(ANewLine);
1330 | //// BCEditor1.Text := BCEditor1.Text +#13#10+ ANewLine;
1331 | // FCommandOutput.Add(ANewLine);
1332 | // BCEditor1.BeginUpdate;
1333 | // BCEditor1.Lines := FCommandOutput;
1334 | //// KHexEditor1.ExecuteCommand(ecInsertString, PChar(ANewLine));
1335 | //// BCEditor1.Perform(EM_SCROLL, SB_LINEDOWN, 0);
1336 | // BCEditor1.GotoLineAndCenter(BCEditor1.Lines.Count);
1337 | // BCEditor1.EndUpdate;
1338 | // Application.ProcessMessages;
1339 |
1340 | FSyncLock.Enter;
1341 | try
1342 | FOutputBuffer.Add(ANewLine);
1343 | finally
1344 | FSyncLock.Leave;
1345 | end;
1346 | end;
1347 |
1348 | procedure TForm1.DosCommand1Terminated(Sender: TObject);
1349 | begin
1350 | BCEditor1.Lines.Add('¡Completed process!');
1351 | ActivityIndicator1.Animate := False;
1352 | ActivityIndicator1.Visible := False;
1353 | // BCEditor1.Lines := FCommandOutput;
1354 | BCEditor1.GotoLineAndCenter(BCEditor1.Lines.Count);
1355 | FCommandOutput.Clear;
1356 | end;
1357 |
1358 | procedure TForm1.DosCommand1TerminateProcess(ASender: TObject;
1359 | var ACanTerminate: Boolean);
1360 | begin
1361 | ACanTerminate := True;
1362 | end;
1363 |
1364 | procedure TForm1.Exit1Click(Sender: TObject);
1365 | begin
1366 | Close;
1367 | end;
1368 |
1369 | procedure TForm1.FlushIcons;
1370 | var
1371 | DesktopFolder: IShellFolder;
1372 | Pidl: PItemIDList;
1373 | begin
1374 | // Get the desktop folder
1375 | SHGetDesktopFolder(DesktopFolder);
1376 |
1377 | // Get the PIDL for the desktop folder
1378 | SHGetSpecialFolderLocation(0, CSIDL_DESKTOP, Pidl);
1379 |
1380 | try
1381 | // Notify the system of the association change
1382 | SHChangeNotify(SHCNE_ASSOCCHANGED, SHCNF_IDLIST, Pidl, nil);
1383 | finally
1384 | // Free the PIDL
1385 | CoTaskMemFree(Pidl);
1386 | end;
1387 | end;
1388 |
1389 | procedure TForm1.FormCreate(Sender: TObject);
1390 | begin
1391 |
1392 | AllowSetForegroundWindow(GetCurrentProcessId);
1393 |
1394 | if not StartHook then
1395 | begin
1396 | MessageDlg('Couldn''t set global hotkey.',mtError, [mbOK], 0);
1397 | Application.Terminate;
1398 | end;
1399 |
1400 | KeyPreview := True;
1401 |
1402 | ActivityIndicator1.Visible := False;
1403 |
1404 | lstExplorerPath := TStringList.Create;
1405 | lstExplorerWnd := TStringList.Create;
1406 | lstExplorerItem := TStringList.Create;
1407 |
1408 | Application.OnDeactivate := OnFocusLost;
1409 |
1410 | // IShellPreview
1411 | fPreview := nil;
1412 |
1413 | // BCEditor1.Font.Name := 'Consolas';
1414 | // BCEditor1.Font.Size := 9;
1415 |
1416 | FEnvExecutables := TStringList.Create;
1417 | FEnvStrings := TStringList.Create;
1418 |
1419 | RefreshEnvironmentVariables;
1420 |
1421 | // IAutoComplete
1422 | ButtonedEdit1.ACEnabled := True;
1423 | ButtonedEdit1.ACOptions := [acAutoAppend, acAutoSuggest, acUpDownKeyDropsList];
1424 | ButtonedEdit1.ACSource := acsList;
1425 | populateCommands;
1426 |
1427 | FCommandOutput := TStringList.Create;
1428 |
1429 | // LibGit2 initialization
1430 | // git_libgit2_init;
1431 | InitLibgit2;
1432 |
1433 | mnuAutoStart.Checked := AppAutoStart1.IsStartupEnabled;
1434 |
1435 | // Speeding up the DOSCommand output
1436 | FOutputBuffer := TStringList.Create;
1437 | FSyncLock := TCriticalSection.Create;
1438 |
1439 | // SetWindowColorModeAsSystem;
1440 | UpdateTheme;
1441 | end;
1442 |
1443 | procedure TForm1.FormDestroy(Sender: TObject);
1444 | begin
1445 | FSyncLock.Free;
1446 | FOutputBuffer.Free;
1447 |
1448 | ShutdownLibgit2;
1449 | // git_libgit2_shutdown;
1450 |
1451 | FEnvStrings.Free;
1452 | FEnvExecutables.Free;
1453 |
1454 |
1455 | DosCommand1.Stop;
1456 | FCommandOutput.Free;
1457 |
1458 | if fPreview <> nil then
1459 | fPreview.Free;
1460 |
1461 | lstExplorerWnd.Free;
1462 | lstExplorerPath.Free;
1463 | lstExplorerItem.Free;
1464 |
1465 | StopHook;
1466 | end;
1467 |
1468 | procedure TForm1.FormKeyUp(Sender: TObject; var Key: Word; Shift: TShiftState);
1469 | begin
1470 | if (key = VK_ESCAPE) and not FPinned then
1471 | Hide;
1472 | end;
1473 |
1474 | procedure TForm1.FormShow(Sender: TObject);
1475 | begin
1476 | ShowWindow(Application.Handle, SW_HIDE);
1477 |
1478 | // if GetForegroundWindow <> Handle then
1479 | // begin
1480 | // SwitchToThisWindow(Handle, );
1481 | // SetForegroundWindow(Handle);
1482 | // end;
1483 |
1484 | end;
1485 |
1486 | // since Windows 11 22h2 build 22621.2506 new file explorer address bar is available
1487 | // so it should choose the new address bar
1488 | function TForm1.GetExplorerAddressBarRect(AHandle: HWND): TRect;
1489 | var
1490 | ExplorerRect: TRect;
1491 | LWND: HWND;
1492 | begin
1493 | // we assume it is a valid explorer instance before calling this function
1494 | Winapi.Windows.GetWindowRect(AHandle, ExplorerRect);
1495 |
1496 | LWND := FindWindowEx(AHandle, 0, 'WorkerW', nil);
1497 | if LWND > 0 then
1498 | LWND := FindWindowEx(LWND, 0, 'ReBarWindow32', nil);
1499 | if LWND > 0 then
1500 | LWND := FindWindowEx(LWND, 0, 'Address Band Root', nil);
1501 | if LWND > 0 then
1502 | LWND := FindWindowEx(LWND, 0, 'msctls_progress32', nil);
1503 | if LWND > 0 then
1504 | LWND := FindWindowEx(LWND, 0, 'Breadcrumb Parent', nil);
1505 | if LWND > 0 then
1506 | begin
1507 | Winapi.Windows.GetWindowRect(LWND, Result);
1508 | // Result.Width := Width;
1509 | if Result.Width < 600 then
1510 | Result.Width := 600;
1511 | Result.Height := Height;
1512 | end
1513 | else
1514 | begin
1515 | // on newer File Explorer on Windows 11 let's pick the empty area of the
1516 | // Child Class: Microsoft.UI.Content.DesktopChildSiteBridge (top area)
1517 | LWND := FindWindowEx(AHandle, 0, 'Microsoft.UI.Content.DesktopChildSiteBridge', nil);
1518 | if LWND > 0 then
1519 | begin
1520 | var nRect: TRect;
1521 | Winapi.Windows.GetWindowRect(LWND, nRect);
1522 | Result.Width := Width;
1523 | Result.Height := Height;
1524 | Result.Left := ExplorerRect.Left + (ExplorerRect.Width - Result.Width) div 2;
1525 | Result.Top := ExplorerRect.Top + nRect.Height;
1526 | end
1527 | else
1528 | begin
1529 | // it might be a different explorer version, maybe the newer on Windows 11 Insider which changed its address bar position
1530 | Result.Width := Width;
1531 | Result.Height := Height;
1532 | Result.Left := ExplorerRect.Left + (ExplorerRect.Width - Result.Width) div 2;
1533 | Result.Top := ExplorerRect.Top + (ExplorerRect.Height - Height) div 2;
1534 | end;
1535 | end;
1536 | end;
1537 |
1538 | procedure TForm1.KeyEventHandler(var Msg: TMessage);
1539 | var
1540 | I: Integer;
1541 | command: String;
1542 | Ret: Integer;
1543 |
1544 | HActiveWindow: HWND;
1545 | HForegroundThread, HAppThread: DWORD;
1546 | FClientId: DWORD;
1547 | Win11TabContainer: HWND; //TITLE_BAR_SCAFFOLDING_WINDOW_CLASS
1548 | begin
1549 | populateCommands;
1550 | // OutputDebugString(PChar('heehhehe'));
1551 | command := PChar(Msg.LParam);
1552 | lastExplorerHandle := StrToInt(command);
1553 | lastExplorerPath := '';
1554 |
1555 | if not Visible then
1556 | begin
1557 | ACLShellTreeView1.Visible := False;
1558 | var rct: TRect;
1559 | rct := GetExplorerAddressBarRect(lastExplorerHandle);
1560 | Left := rct.Left;
1561 | Width := rct.Width;
1562 | if Width < 800 then
1563 | Width := 800;
1564 | Top := rct.Top;
1565 |
1566 | // SwitchToThisWindow(GetDesktopWindow, True);
1567 |
1568 | // BorderStyle := bsNone;
1569 | // AnimateWindow(Handle, 128, AW_SLIDE or AW_VER_POSITIVE );
1570 | // BorderStyle := bsSizeable;
1571 | Show;
1572 |
1573 | HActiveWindow := GetForegroundWindow();
1574 | HForegroundThread := GetWindowThreadProcessId(HActiveWindow, @FClientId);
1575 | AllowSetForegroundWindow(FClientId);
1576 |
1577 | HAppThread := GetCurrentThreadId;
1578 |
1579 | if not SetForegroundWindow(Handle) then
1580 | SwitchToThisWindow(GetDesktopWindow, True);
1581 |
1582 | // magic part to switch correctly to our window
1583 | if HForegroundThread <> HAppThread then
1584 | begin
1585 | AttachThreadInput(HForegroundThread, HAppThread, True);
1586 | BringWindowToTop(Handle);
1587 | Winapi.Windows.SetFocus(Handle);
1588 | AttachThreadInput(HForegroundThread, HAppThread, False);
1589 | end;
1590 |
1591 | // Winapi.Windows.GetWindowRect(HActiveWindow, rct);
1592 | SetWindowPos(Handle, HWND_TOP, 0, 0, 0, 0, {SWP_ASYNCWINDOWPOS or }SWP_NOMOVE or SWP_NOSIZE or SWP_SHOWWINDOW);
1593 |
1594 | // let's put out menu in the Explorer window
1595 | // Win11TabContainer := FindWindowEx(HActiveWindow, 0, 'TITLE_BAR_SCAFFOLDING_WINDOW_CLASS', nil);
1596 | // if Win11TabContainer > 0 then
1597 | // begin
1598 | // formHover.Show;
1599 | // var mr: TRect;
1600 | // Winapi.Windows.GetWindowRect(Win11TabContainer, mr);
1601 | // formHover.Left := 0;
1602 | // formHover.Top := 0;
1603 | // formHover.Width := mr.Width;
1604 | // formHover.Height := mr.Height;
1605 | // formHover.BoundsRect := mr;
1606 | // Winapi.Windows.SetParent(formHover.Handle, Win11TabContainer);
1607 | // end;
1608 |
1609 |
1610 | // ButtonedEdit1.SetFocus;
1611 |
1612 | // before listing explorer instances let's see if we are in a open save dialog
1613 | // WorkerW->ReBarWindow32->Address Band Root->msctls_progress32->ComboBoxEx32->ComboBox->Edit
1614 |
1615 | Ret := ListExplorerInstances;
1616 |
1617 | { var FirstPath := IntToStr(Ret);
1618 | StatusBar1.Panels[0].Text := FirstPath;
1619 | }
1620 |
1621 | for I := 0 to lstExplorerWnd.Count - 1 do
1622 | begin
1623 | if lstExplorerWnd[i] = IntToStr(lastExplorerHandle) then
1624 | begin
1625 | lastExplorerPath := lstExplorerPath[I];
1626 | //rkSmartPath1.Path := lstExplorerPath[I];
1627 | StatusBar1.Panels[0].Text := lstExplorerItem[i];
1628 | //WIC
1629 | if FileExists(lstExplorerItem[i]) then
1630 | ShowPreview(lstExplorerItem[i]);
1631 | CurrentFile := lstExplorerItem[i];
1632 | end;
1633 | end;
1634 |
1635 | if DirectoryExists(StatusBar1.Panels[0].Text) then
1636 | rkSmartPath1.Path := StatusBar1.Panels[0].Text
1637 | else if DirectoryExists(ExtractFilePath(StatusBar1.Panels[0].Text)) then
1638 | begin
1639 | rkSmartPath1.Path := ExtractFilePath(StatusBar1.Panels[0].Text);
1640 | end;
1641 |
1642 | if IsGitRepository(rkSmartPath1.Path) then
1643 | begin
1644 | ButtonedEdit1.LeftButton.ImageIndex := 3
1645 | end
1646 | else
1647 | ButtonedEdit1.LeftButton.ImageIndex := 0;
1648 |
1649 | CurrentDir := rkSmartPath1.Path;
1650 | GitUrl := GetRemoteURL(rkSmartPath1.Path, 'origin');
1651 | if Pos('http', LowerCase(GitUrl)) = 1 then
1652 | begin
1653 | OpenURL1.Enabled := True;
1654 | pnlTitle.Visible := True;
1655 | LinkLabel1.Caption := 'Repository: ' + GitUrl + '';
1656 | LinkLabel1.Left := (pnlTitle.Width - LinkLabel1.Width) div 2;
1657 | end
1658 | else
1659 | begin
1660 | OpenURL1.Enabled := False;
1661 | pnlTitle.Visible := False;
1662 | end;
1663 |
1664 | // BCEditor1.Lines.Add(gurl);
1665 | end
1666 | else
1667 | begin
1668 | // SwitchToThisWindow(Handle, True);
1669 | Hide;
1670 | end;
1671 | end;
1672 |
1673 | procedure TForm1.KeyEventHandlerAll(var Msg: TMessage);
1674 | var
1675 | I: Integer;
1676 | command: String;
1677 | Ret: Integer;
1678 |
1679 | HActiveWindow: HWND;
1680 | HForegroundThread, HAppThread: DWORD;
1681 | FClientId: DWORD;
1682 |
1683 | begin
1684 | // OutputDebugString(PChar('heehhehe'));
1685 | command := PChar(Msg.LParam);
1686 | lastExplorerHandle := StrToInt(command);
1687 | lastExplorerPath := '';
1688 |
1689 | if not Visible then
1690 | begin
1691 | ACLShellTreeView1.Visible := False;
1692 | // SwitchToThisWindow(GetDesktopWindow, True);
1693 | Show;
1694 | HActiveWindow := GetForegroundWindow();
1695 | // UpdateMainMenu(lastExplorerHandle);
1696 | HForegroundThread := GetWindowThreadProcessId(HActiveWindow, @FClientId);
1697 | AllowSetForegroundWindow(FClientId);
1698 |
1699 | HAppThread := GetCurrentThreadId;
1700 |
1701 | if not SetForegroundWindow(Handle) then
1702 | SwitchToThisWindow(GetDesktopWindow, True);
1703 |
1704 |
1705 |
1706 | // magic part to switch correctly to our window
1707 | if HForegroundThread <> HAppThread then
1708 | begin
1709 | AttachThreadInput(HForegroundThread, HAppThread, True);
1710 | BringWindowToTop(Handle);
1711 | Winapi.Windows.SetFocus(Handle);
1712 | AttachThreadInput(HForegroundThread, HAppThread, False);
1713 | end;
1714 |
1715 | var rct: TRect;
1716 | Winapi.Windows.GetWindowRect(HActiveWindow, rct);
1717 | SetWindowPos(Handle, HWND_TOP, 0, 0, 0, 0, {SWP_ASYNCWINDOWPOS or }SWP_NOMOVE or SWP_NOSIZE or SWP_SHOWWINDOW);
1718 | // center to current window, otherwise to monitors
1719 | var nLeft := rct.Left + (rct.Width - Width) div 2;
1720 | var nTop := rct.Top + (rct.Height - Height) div 2;
1721 | if nLeft < 0 then
1722 | Left := (Screen.Width - Width) div 2
1723 | else
1724 | Left := nLeft;
1725 | if nTop < 0 then
1726 | Top := (Screen.Height - Height) div 2
1727 | else
1728 | Top := nTop;
1729 |
1730 | // ButtonedEdit1.SetFocus;
1731 |
1732 | // before listing explorer instances let's see if we are in a open save dialog
1733 | // WorkerW->ReBarWindow32->Address Band Root->msctls_progress32->ComboBoxEx32->ComboBox->Edit
1734 |
1735 | Ret := ListExplorerInstances;
1736 |
1737 | { var FirstPath := IntToStr(Ret);
1738 | StatusBar1.Panels[0].Text := FirstPath;
1739 | }
1740 |
1741 | for I := 0 to lstExplorerWnd.Count - 1 do
1742 | begin
1743 | if lstExplorerWnd[i] = IntToStr(lastExplorerHandle) then
1744 | begin
1745 | lastExplorerPath := lstExplorerPath[I];
1746 | //rkSmartPath1.Path := lstExplorerPath[I];
1747 | StatusBar1.Panels[0].Text := lstExplorerItem[i];
1748 | //WIC
1749 | if FileExists(lstExplorerItem[i]) then
1750 | ShowPreview(lstExplorerItem[i]);
1751 | CurrentFile := lstExplorerItem[i];
1752 | end;
1753 | end;
1754 |
1755 | if DirectoryExists(StatusBar1.Panels[0].Text) then
1756 | rkSmartPath1.Path := StatusBar1.Panels[0].Text
1757 | else if DirectoryExists(ExtractFilePath(StatusBar1.Panels[0].Text)) then
1758 | begin
1759 | rkSmartPath1.Path := ExtractFilePath(StatusBar1.Panels[0].Text);
1760 | end;
1761 |
1762 | if IsGitRepository(rkSmartPath1.Path) then
1763 | begin
1764 | ButtonedEdit1.LeftButton.ImageIndex := 3
1765 | end
1766 | else
1767 | ButtonedEdit1.LeftButton.ImageIndex := 0;
1768 |
1769 | CurrentDir := rkSmartPath1.Path;
1770 | GitUrl := GetRemoteURL(rkSmartPath1.Path, 'origin');
1771 | if Pos('http', LowerCase(GitUrl)) = 1 then
1772 | OpenURL1.Enabled := True
1773 | else
1774 | OpenURL1.Enabled := False;
1775 |
1776 | // BCEditor1.Lines.Add(gurl);
1777 | end
1778 | else
1779 | begin
1780 | // SwitchToThisWindow(Handle, True);
1781 | Hide;
1782 | end;
1783 | end;
1784 |
1785 | procedure TForm1.KeyEventPickPaths(var Msg: TMessage);
1786 | var
1787 | I: Integer;
1788 | command: String;
1789 | Ret: Integer;
1790 |
1791 | HActiveWindow: HWND;
1792 | HForegroundThread, HAppThread: DWORD;
1793 | FClientId: DWORD;
1794 |
1795 | begin
1796 | command := PChar(Msg.LParam);
1797 | lastExplorerHandle := StrToInt(command);
1798 | lastExplorerPath := '';
1799 |
1800 | if not Visible then
1801 | begin
1802 | Show;
1803 | HActiveWindow := GetForegroundWindow();
1804 | HForegroundThread := GetWindowThreadProcessId(HActiveWindow, @FClientId);
1805 | AllowSetForegroundWindow(FClientId);
1806 |
1807 | HAppThread := GetCurrentThreadId;
1808 |
1809 | if not SetForegroundWindow(Handle) then
1810 | SwitchToThisWindow(GetDesktopWindow, True);
1811 |
1812 | // magic part to switch correctly to our window
1813 | if HForegroundThread <> HAppThread then
1814 | begin
1815 | AttachThreadInput(HForegroundThread, HAppThread, True);
1816 | BringWindowToTop(Handle);
1817 | Winapi.Windows.SetFocus(Handle);
1818 | AttachThreadInput(HForegroundThread, HAppThread, False);
1819 | end;
1820 |
1821 | var rct: TRect;
1822 | Winapi.Windows.GetWindowRect(HActiveWindow, rct);
1823 | SetWindowPos(Handle, HWND_TOP, 0, 0, 0, 0, {SWP_ASYNCWINDOWPOS or }SWP_NOMOVE or SWP_NOSIZE or SWP_SHOWWINDOW);
1824 | // center to current window, otherwise to monitors
1825 | var nLeft := rct.Left + (rct.Width - Width) div 2;
1826 | var nTop := rct.Top + (rct.Height - Height) div 2;
1827 | if nLeft < 0 then
1828 | Left := (Screen.Width - Width) div 2
1829 | else
1830 | Left := nLeft;
1831 | if nTop < 0 then
1832 | Top := (Screen.Height - Height) div 2
1833 | else
1834 | Top := nTop;
1835 |
1836 | Ret := ListExplorerInstances;
1837 |
1838 | for I := 0 to lstExplorerWnd.Count - 1 do
1839 | begin
1840 | if lstExplorerWnd[i] = IntToStr(lastExplorerHandle) then
1841 | begin
1842 | lastExplorerPath := lstExplorerPath[I];
1843 | StatusBar1.Panels[0].Text := lstExplorerItem[i];
1844 | if FileExists(lstExplorerItem[i]) then
1845 | ShowPreview(lstExplorerItem[i]);
1846 | CurrentFile := lstExplorerItem[i];
1847 | end;
1848 | end;
1849 |
1850 | if DirectoryExists(StatusBar1.Panels[0].Text) then
1851 | rkSmartPath1.Path := StatusBar1.Panels[0].Text
1852 | else if DirectoryExists(ExtractFilePath(StatusBar1.Panels[0].Text)) then
1853 | begin
1854 | rkSmartPath1.Path := ExtractFilePath(StatusBar1.Panels[0].Text);
1855 | end;
1856 |
1857 | if IsGitRepository(rkSmartPath1.Path) then
1858 | begin
1859 | ButtonedEdit1.LeftButton.ImageIndex := 3
1860 | end
1861 | else
1862 | ButtonedEdit1.LeftButton.ImageIndex := 0;
1863 |
1864 | CurrentDir := rkSmartPath1.Path;
1865 | GitUrl := GetRemoteURL(rkSmartPath1.Path, 'origin');
1866 | if Pos('http', LowerCase(GitUrl)) = 1 then
1867 | OpenURL1.Enabled := True
1868 | else
1869 | OpenURL1.Enabled := False;
1870 |
1871 | // Show file/folder picker
1872 | ACLShellTreeView1.Visible := True;
1873 | ACLShellTreeView1.SetFocus;
1874 |
1875 | end
1876 | else
1877 | begin
1878 | Hide;
1879 | end;
1880 | end;
1881 |
1882 |
1883 | var
1884 | ChildEditHwnd: HWND = 0;
1885 | function EnumChildEditProc(ChildWnd: HWND; lParam: LPARAM): BOOL; stdcall;
1886 | var
1887 | ClassName: PChar;
1888 | Buffer: array[0..255] of Char;
1889 | LWnd: HWND;
1890 | begin
1891 | GetClassName(ChildWnd, Buffer, SizeOf(Buffer) div SizeOf(Char));
1892 | ClassName := PChar(lParam);
1893 |
1894 | // If the class name matches, return the window handle
1895 | if CompareText(Buffer, ClassName) = 0 then
1896 | begin
1897 | // We now need to find if a 'ComboBox' classname child exists with a child with classname 'Edit', which is the Filename Edit box
1898 | LWnd := FindWindowEx(ChildWnd, 0, 'ComboBox', nil);
1899 | if LWnd <> 0 then
1900 | begin
1901 | ChildEditHwnd := LWnd;
1902 | LWnd := FindWindowEx(LWnd, 0, 'Edit', nil);
1903 | if LWnd <> 0 then
1904 | begin
1905 | Result := FALSE; // Found a matching control
1906 | Exit;
1907 | end
1908 | else
1909 | ChildEditHwnd := 0;
1910 | end;
1911 | end;
1912 | Result := TRUE; // Continue enumeration
1913 | end;
1914 |
1915 | procedure TForm1.KeyEventUpdatePath(var Msg: TMessage);
1916 | var
1917 | I: Integer;
1918 | command: String;
1919 | Ret: Integer;
1920 |
1921 | HActiveWindow: HWND;
1922 | HForegroundThread, HAppThread: DWORD;
1923 | FClientId: DWORD;
1924 | AppClassName: array[0..255] of char;
1925 | CtrlPressed, AltPressed, UpArrowPressed: Boolean;
1926 | begin
1927 | if not DirectoryExists(CurrentDir) then Exit;
1928 |
1929 | ACLShellTreeView1.Visible := False;
1930 |
1931 | command := PChar(Msg.LParam);
1932 | lastExplorerHandle := StrToInt(command);
1933 | lastExplorerPath := '';
1934 |
1935 | HActiveWindow := GetForegroundWindow();
1936 | GetClassName(HActiveWindow, AppClassName, 255);
1937 | // ShowMessage(AppClassName);
1938 | if AppClassName = '#32770' then
1939 | begin
1940 | //Winapi.Windows.Beep(400,1000); // annoying sound while the user release the hotkey to proceed
1941 | repeat
1942 | CtrlPressed := GetAsyncKeyState(VK_CONTROL) < 0;
1943 | AltPressed := GetAsyncKeyState(VK_MENU) < 0;
1944 |
1945 | // Sleep to prevent high CPU usage while waiting
1946 | Sleep(10);
1947 | until not CtrlPressed and not AltPressed; // Wait until both Ctrl and Alt are released
1948 | // Step 1: Press down the Ctrl key (KEYEVENTF_KEYDOWN)
1949 | keybd_event(VK_CONTROL, 0, 0, 0);
1950 |
1951 | // Step 2: Press down the L key (KEYEVENTF_KEYDOWN)
1952 | keybd_event(Ord('L'), 0, 0, 0);
1953 |
1954 | // Step 3: Release the L key (KEYEVENTF_KEYUP)
1955 | keybd_event(Ord('L'), 0, KEYEVENTF_KEYUP, 0);
1956 |
1957 | // Step 4: Release the Ctrl key (KEYEVENTF_KEYUP)
1958 | keybd_event(VK_CONTROL, 0, KEYEVENTF_KEYUP, 0);
1959 |
1960 | // Find the Directory Path Edit Box
1961 | var edFolder := FindWindowEx(HActiveWindow, 0, 'WorkerW', nil);
1962 | if edFolder <> 0 then
1963 | edFolder := FindWindowEx(edFolder, 0, 'ReBarWindow32', nil);
1964 | if edFolder <> 0 then
1965 | edFolder := FindWindowEx(edFolder, 0, 'Address Band Root', nil);
1966 | if edFolder <> 0 then
1967 | edFolder := FindWindowEx(edFolder, 0, 'msctls_progress32', nil);
1968 | if edFolder <> 0 then
1969 | edFolder := FindWindowEx(edFolder, 0, 'ComboBoxEx32', nil);
1970 | if edFolder <> 0 then
1971 | edFolder := FindWindowEx(edFolder, 0, 'ComboBox', nil);
1972 | if edFolder <> 0 then
1973 | edFolder := FindWindowEx(edFolder, 0, 'Edit', nil);
1974 | if edFolder <> 0 then
1975 | begin
1976 | SendMessage(edFolder, WM_SETTEXT, 0, LPARAM(PChar(CurrentDir)));
1977 | //Winapi.Windows.SetFocus(edFolder);
1978 | PostMessage(edFolder, WM_SETFOCUS, 0, 0);
1979 | keybd_event(VK_RETURN, 0, 0, 0);
1980 | // Step 4: Release the Ctrl key (KEYEVENTF_KEYUP)
1981 | keybd_event(VK_RETURN, 0, KEYEVENTF_KEYUP, 0);
1982 | end;
1983 |
1984 | Sleep(100); // it seems to be required as sometimes changing directory fails
1985 | // find the Filename Edit Box in Open Dialog
1986 | var edFilename := FindWindowEx(HActiveWindow, 0, 'ComboBoxEx32', nil);
1987 | if edFilename <> 0 then
1988 | begin
1989 | PostMessage(edFilename, WM_SETFOCUS, 0, 0);
1990 | edFilename := FindWindowEx(edFilename, 0, 'ComboBox', nil);
1991 | if edFilename <> 0 then
1992 | edFilename := FindWindowEx(edFilename, 0, 'Edit', nil);
1993 | if edFilename <> 0 then
1994 | begin
1995 | //SendMessage(edFilename, WM_SETTEXT, 0, LPARAM(PChar(CurrentDir)));
1996 | // these won't work directly, its parent ComboBoxEx32 is enough
1997 | //Winapi.Windows.SetFocus(edFilename); // SetFocus seems not to work
1998 | //PostMessage(edFilename, WM_SETFOCUS, 0, 0); // This might work, but as said above, its parent is the key to focus
1999 | end;
2000 |
2001 | end
2002 | // find the Filename Edit Box in Save Dialog
2003 | else
2004 | begin
2005 | edFilename := FindWindowEx(HActiveWindow, 0, 'DUIViewWndClassName', nil);
2006 | if edFilename <> 0 then
2007 | edFilename := FindWindowEx(edFilename, 0, 'DirectUIHWND', nil);
2008 | if edFilename <> 0 then
2009 | // there are other FloatNotifySink, we need to use EnumChildWindows
2010 | EnumChildWindows(edFilename, @EnumChildEditProc, LPARAM(PChar('FloatNotifySink'))); // updates global ChildEditHwnd variable if ComboBox is found inside
2011 | //edFilename := FindWindowEx(edFilename, 0, 'FloatNotifySink', nil); // this way only finds the first one that holds the Save button, which we won't want
2012 | if ChildEditHwnd <> 0 then
2013 | //edFilename := FindWindowEx(edFilename, 0, 'ComboBox', nil);
2014 | edFilename := ChildEditHwnd;
2015 | SendMessage(edFilename, WM_SETFOCUS, 0, 0);
2016 | // if edFilename <> 0 then
2017 | // edFilename := FindWindowEx(edFilename, 0, 'Edit', nil);
2018 | // if edFilename <> 0 then
2019 | // begin
2020 | // //SendMessage(edFilename, WM_SETTEXT, 0, LPARAM(PChar(CurrentDir)));
2021 | // end;
2022 | end;
2023 | end;
2024 | exit;
2025 |
2026 | HForegroundThread := GetWindowThreadProcessId(HActiveWindow, @FClientId);
2027 | AllowSetForegroundWindow(FClientId);
2028 |
2029 | HAppThread := GetCurrentThreadId;
2030 |
2031 | Ret := ListExplorerInstances;
2032 |
2033 |
2034 | for I := 0 to lstExplorerWnd.Count - 1 do
2035 | begin
2036 | if lstExplorerWnd[i] = IntToStr(lastExplorerHandle) then
2037 | begin
2038 | lastExplorerPath := lstExplorerPath[I];
2039 | StatusBar1.Panels[0].Text := lstExplorerItem[i];
2040 |
2041 | if FileExists(lstExplorerItem[i]) then
2042 | ShowPreview(lstExplorerItem[i]);
2043 | CurrentFile := lstExplorerItem[i];
2044 | end;
2045 | end;
2046 |
2047 | if DirectoryExists(StatusBar1.Panels[0].Text) then
2048 | rkSmartPath1.Path := StatusBar1.Panels[0].Text
2049 | else if DirectoryExists(ExtractFilePath(StatusBar1.Panels[0].Text)) then
2050 | begin
2051 | rkSmartPath1.Path := ExtractFilePath(StatusBar1.Panels[0].Text);
2052 | end;
2053 |
2054 | CurrentDir := rkSmartPath1.Path;
2055 | end;
2056 |
2057 | // Lists explorer instances which has items visible, ignores special directories
2058 | procedure TForm1.LinkLabel1LinkClick(Sender: TObject; const Link: string;
2059 | LinkType: TSysLinkType);
2060 | begin
2061 | if LinkType = sltURL then
2062 | begin
2063 | ShellExecute(0, 'OPEN', PChar(Link), nil, nil, SW_NORMAL);
2064 | end;
2065 | end;
2066 |
2067 | procedure TForm1.ListBox1DblClick(Sender: TObject);
2068 | begin
2069 | Hide;
2070 | SwitchToThisWindow(StrToInt(lstExplorerWnd[ListBox1.ItemIndex]), True);
2071 | end;
2072 |
2073 | procedure TForm1.ListBox1KeyUp(Sender: TObject; var Key: Word;
2074 | Shift: TShiftState);
2075 | begin
2076 | if Key = 13 then
2077 | begin
2078 | Hide;
2079 | // SwitchToThisWindow(StrToInt(lstExplorerWnd[ListBox1.ItemIndex]), True);
2080 | SwitchToWindow(StrToInt(lstExplorerWnd[ListBox1.ItemIndex]));
2081 | ListBox1.Visible := False;
2082 | end;
2083 | end;
2084 |
2085 | function GetSpecialFolderPath(const FolderID: Integer): string;
2086 | var
2087 | ShellFolder: IShellFolder;
2088 | IDList: PItemIDList;
2089 | StrRet: TStrRet;
2090 | FolderPath: array [0..MAX_PATH] of Char;
2091 | begin
2092 | Result := '';
2093 |
2094 | if Succeeded(SHGetSpecialFolderLocation(0, FolderID, IDList)) then
2095 | begin
2096 | if Succeeded(SHGetDesktopFolder(ShellFolder)) then
2097 | begin
2098 | if Succeeded(ShellFolder.GetDisplayNameOf(IDList, SHGDN_FORPARSING, StrRet)) then
2099 | begin
2100 | if StrRet.uType = STRRET_WSTR then
2101 | begin
2102 | OleStrToStrVar(StrRet.pOleStr, Result);
2103 | CoTaskMemFree(StrRet.pOleStr);
2104 | end
2105 | else
2106 | begin
2107 | // FIXLATER
2108 | // StrRetToStr(StrRet, IDList, FolderPath, SizeOf(FolderPath));
2109 | Result := FolderPath;
2110 | end;
2111 | end;
2112 | end;
2113 |
2114 | CoTaskMemFree(IDList);
2115 | end;
2116 | end;
2117 |
2118 | function TForm1.ListExplorerInstances: Integer;
2119 | const
2120 | IID_IServiceProvider: TGUID = '{6D5140C1-7436-11CE-8034-00AA006009FA}';
2121 | SID_STopLevelBrowser: TGUID = '{4C96BE40-915C-11CF-99D3-00AA004AE837}';
2122 | LOOPTIME = 500; //ms i.e. half a second
2123 | var
2124 | ShellWindows: IShellWindows;
2125 | I: Integer;
2126 | ShellBrowser: IShellBrowser;
2127 | WndIface: IDispatch;
2128 | WebBrowserApp: IWebBrowserApp;
2129 | ServiceProvider: IServiceProvider;
2130 | ItemIDList, ItemIDList2: PItemIDList;
2131 | bar: HWND;
2132 | ShellView: IShellView;
2133 | FolderView: IFolderView;
2134 | PersistFolder2: IPersistFolder2;
2135 | ShellFolder: IShellFolder;
2136 | focus: Integer;
2137 | ret: _STRRET;
2138 | folderPath: PChar;
2139 | AMalloc: IMalloc;
2140 | hr: HRESULT;
2141 | CurTime: Int64;
2142 | // Thumbnail
2143 | // ItemIDList3: PItemIDList;
2144 | // Thumbnail: IExtractImage;
2145 | // ThumbBuf: array[0..MAX_PATH] of Char;
2146 | // Runnable: IRunnableTask;
2147 | // Flags, Priority: DWORD;
2148 | // BmpHandle: HBITMAP;
2149 | // ASize: TSize;
2150 | // GetLocationRes: HRESULT;
2151 | begin
2152 | Result := 0;
2153 | lstExplorerPath.BeginUpdate;
2154 | lstExplorerPath.Clear;
2155 | lstExplorerWnd.BeginUpdate;
2156 | lstExplorerWnd.Clear;
2157 | lstExplorerItem.BeginUpdate;
2158 | lstExplorerItem.Clear;
2159 |
2160 | hr := CoInitializeEx(nil, COINIT_APARTMENTTHREADED); // <-- manually call CoInitialize()
2161 | if Succeeded(hr) then
2162 | begin
2163 |
2164 | // this might fail on first try, so let's insist for LOOPTIME ms
2165 | hr := CoCreateInstance(CLASS_ShellWindows, nil, CLSCTX_ALL,
2166 | IID_IShellWindows, ShellWindows);
2167 | CurTime := GetTickCount64;
2168 | while not Succeeded(hr) do
2169 | begin
2170 | if ((GetTickCount64-CurTime)>LOOPTIME) then break;
2171 | hr := CoCreateInstance(CLASS_ShellWindows, nil, CLSCTX_ALL,
2172 | IID_IShellWindows, ShellWindows);
2173 | end;
2174 |
2175 | if Succeeded(hr) then
2176 | begin
2177 | Result := 1;
2178 | for I := 0 to ShellWindows.Count - 1 do
2179 | begin
2180 | if VarType(ShellWindows.Item(I)) = varDispatch then
2181 | begin
2182 | WndIface := ShellWindows.Item(VarAsType(I, VT_I4));
2183 | if WndIface <> nil then
2184 | try
2185 | if Succeeded(WndIface.QueryInterface(IID_IWebBrowserApp, WebBrowserApp)) then
2186 | begin
2187 | lstExplorerWnd.Add(inttostr(WebBrowserApp.HWND));
2188 |
2189 | begin
2190 | if Succeeded(WebBrowserApp.QueryInterface(IID_IServiceProvider,
2191 | ServiceProvider)) then
2192 | begin
2193 | if Succeeded(ServiceProvider.QueryService(SID_STopLevelBrowser,
2194 | IID_IShellBrowser, ShellBrowser)) then
2195 | begin
2196 | if Succeeded(ShellBrowser.QueryActiveShellView(ShellView)) then
2197 | begin
2198 | if Succeeded(ShellView.QueryInterface(IID_IFolderView, FolderView)) then
2199 | begin
2200 | FolderView.GetFocusedItem(focus);
2201 | FolderView.Item(focus,ItemIDList);
2202 | if Succeeded(FolderView.GetFolder(IID_IPersistFolder2, PersistFolder2)) then
2203 | begin
2204 | if succeeded(PersistFolder2.GetCurFolder(ItemIDList2)) then
2205 | begin
2206 | // mmmm
2207 | { if (ItemIDList <> nil)
2208 | and Succeeded(ShellFolder.GetDisplayNameOf(ItemIDList, SHGDN_FORPARSING, Ret))
2209 | then
2210 | begin
2211 | case Ret.uType of
2212 | STRRET_WSTR:
2213 | begin
2214 | // FolderPath := StrPas(Ret.pOleStr);
2215 | CoTaskMemFree(Ret.pOleStr);
2216 | end;
2217 | STRRET_CSTR:
2218 | begin
2219 | // FolderPath := Ret.cStr;
2220 | end;
2221 | STRRET_OFFSET:
2222 | begin
2223 | FolderPath := PChar(Integer(ItemIDList) + Ret.uOffset);
2224 | end
2225 | else
2226 | FolderPath := ' ';
2227 | end;
2228 | end;}
2229 |
2230 | folderPath := StrAlloc(MAX_PATH);
2231 | if SHGetPathFromIDList(ItemIDList2, folderPath) then
2232 | lstExplorerPath.Add(folderPath);
2233 | SHGetMalloc(AMalloc);
2234 | AMalloc.Free(ItemIDList2);
2235 | StrDispose(folderPath);
2236 | end;
2237 |
2238 | if Succeeded(PersistFolder2.QueryInterface(IID_IShellFolder, ShellFolder)) then
2239 | begin
2240 | if (ItemIDList <> nil) and Succeeded(ShellFolder.GetDisplayNameOf(ItemIDList, SHGDN_FORPARSING, ret)) then
2241 | lstExplorerItem.Add(ret.pOleStr)
2242 | else
2243 | lstExplorerItem.Add('no name');
2244 | end;
2245 |
2246 | // //extract thumbnail
2247 | // if Succeeded(ShellFolder.GetUIObjectOf(0, 1, ItemIDList3, IExtractImage, nil, Thumbnail)) then
2248 | // begin
2249 | // CoTaskMemFree(ItemIDList3);
2250 | //
2251 | // if Assigned(Thumbnail) then
2252 | // begin
2253 | // Runnable := nil;
2254 | // ASize.cx := 256;
2255 | // ASize.cy := 256;
2256 | // Priority := 0;
2257 | // Flags := IEIFLAG_ASPECT or IEIFLAG_OFFLINE or IEIFLAG_CACHE or IEIFLAG_QUALITY;
2258 | // GetLocationRes := Thumbnail.GetLocation(ThumbBuf, SizeOf(ThumbBuf), Priority, ASize, 32, Flags);
2259 | // if (GetLocationRes = NOERROR) or (GetLocationRes = E_PENDING) then
2260 | // begin
2261 | // if GetLocationRes = E_PENDING then
2262 | // if Thumbnail.QueryInterface(IRunnableTask, Runnable) <> S_OK then
2263 | // Runnable := nil;
2264 | // try
2265 | // Thumbnail.Extract(BmpHandle);
2266 | // Image1.Picture.Bitmap.Handle := BmpHandle;
2267 | // except
2268 | // on E: EOleSysError do
2269 | // OutputDebugString(PChar(string(E.ClassName) + ': ' + E.Message));
2270 | // end;
2271 | // end;
2272 | // end;
2273 | // end;
2274 | end;
2275 | end;
2276 | end;
2277 |
2278 | end;
2279 |
2280 | end;
2281 | end;
2282 | // make sure the other lists are even to found explorer
2283 | if lstExplorerWnd.Count > lstExplorerPath.Count then
2284 | lstExplorerPath.Add('');
2285 | if lstExplorerWnd.Count > lstExplorerItem.Count then
2286 | lstExplorerPath.Add('');
2287 | end;
2288 | except
2289 | end;
2290 | end;
2291 |
2292 | end;
2293 |
2294 | end;
2295 | end;
2296 | CoUninitialize; // <-- free memory
2297 | lstExplorerItem.EndUpdate;
2298 | lstExplorerWnd.EndUpdate;
2299 | lstExplorerPath.EndUpdate;
2300 | end;
2301 |
2302 | procedure TForm1.mnuAutoStartClick(Sender: TObject);
2303 | begin
2304 | mnuAutoStart.Checked := not mnuAutoStart.Checked;
2305 | AppAutoStart1.Enabled := mnuAutoStart.Checked;
2306 | end;
2307 |
2308 | procedure TForm1.NoBorder(var Msg: TWMNCActivate);
2309 | begin
2310 | Msg.Active := False;
2311 | inherited;
2312 | end;
2313 |
2314 | procedure TForm1.OnFocusLost(Sender: TObject);
2315 | begin
2316 | EsImage1.Picture.Assign(nil);
2317 |
2318 | StatusBar1.Panels[0].Text := '';
2319 | if not FPinned then
2320 | Hide;
2321 | end;
2322 |
2323 | procedure TForm1.OpenURL1Click(Sender: TObject);
2324 | begin
2325 | ShellExecute(0, 'OPEN', PChar(GitUrl), nil, nil, SW_SHOWNORMAL);
2326 | end;
2327 |
2328 | procedure TForm1.populateCommands;
2329 | begin
2330 | FCommandType := ctNormal;
2331 |
2332 | with ButtonedEdit1.ACStrings do
2333 | begin
2334 | BeginUpdate;
2335 | Clear;
2336 | Add('>');
2337 | Add(':');
2338 | Add('%');
2339 | Add('help');
2340 | Add('exit');
2341 | Add('find');
2342 | Add('open');
2343 | Add('cmd');
2344 | Add('env');
2345 | Add('dir');
2346 | if OpenURL1.Enabled then
2347 | begin
2348 | Add('git');
2349 | Add('git-pull'); // git pull
2350 | Add('gp');
2351 | Add('git-readme'); // git readme
2352 | Add('gr');
2353 | Add('git-url'); // git url
2354 | Add('gu');
2355 | end;
2356 | Add('cls');
2357 | Add('listexplorers');
2358 | Add('list');
2359 | Add('center');
2360 | Add('cmd');
2361 | Add('hexview');
2362 | Add('preview');
2363 | Add('tojpg');
2364 | EndUpdate;
2365 | end;
2366 | end;
2367 |
2368 | procedure TForm1.populateEnvExecutables;
2369 | var
2370 | Envs, Env : PChar;
2371 | PathList: TStringList;
2372 | Path: string;
2373 | SR: TSearchRec;
2374 | FilePath: string;
2375 | I: Integer;
2376 | begin
2377 | ButtonedEdit1.ACStrings.BeginUpdate;
2378 | ButtonedEdit1.ACStrings.Clear;
2379 |
2380 | if FEnvExecutables.Count < 1 then
2381 | begin
2382 | Envs := GetEnvironmentStrings;
2383 | PathList := TStringList.Create;
2384 | try
2385 | Env := Envs;
2386 | while Env^ <> #0 do
2387 | begin
2388 | if Pos('PATH=', WideCharToString(Env)) = 1 then
2389 | PathList.DelimitedText := StringReplace(WideCharToString(Env), 'PATH=', '', []);
2390 | Env := Env + StrLen(Env) + 1;
2391 | end;
2392 |
2393 | for Path in PathList do
2394 | begin
2395 | FilePath := IncludeTrailingPathDelimiter(Path);
2396 |
2397 | if not DirectoryExists(FilePath) then Continue;
2398 |
2399 | if FindFirst(FilePath + '*.cmd', faAnyFile, SR) = 0 then
2400 | begin
2401 | repeat
2402 | FEnvExecutables.Add(FilePath + SR.Name);
2403 | ButtonedEdit1.ACStrings.Add(FilePath + SR.Name);
2404 | until FindNext(SR) <> 0;
2405 | FindClose(SR);
2406 | end;
2407 | if FindFirst(FilePath + '*.bat', faAnyFile, SR) = 0 then
2408 | begin
2409 | repeat
2410 | FEnvExecutables.Add(FilePath + SR.Name);
2411 | ButtonedEdit1.ACStrings.Add(FilePath + SR.Name);
2412 | until FindNext(SR) <> 0;
2413 | FindClose(SR);
2414 | end;
2415 | if FindFirst(FilePath + '*.exe', faAnyFile, SR) = 0 then
2416 | begin
2417 | repeat
2418 | FEnvExecutables.Add(FilePath + SR.Name);
2419 | ButtonedEdit1.ACStrings.Add(FilePath + SR.Name);
2420 | until FindNext(SR) <> 0;
2421 | FindClose(SR);
2422 | end;
2423 |
2424 | end;
2425 |
2426 | finally
2427 | FreeEnvironmentStringsW(Envs);
2428 | PathList.Free;
2429 | end;
2430 | end
2431 | else
2432 | begin
2433 | for I := 0 to FEnvExecutables.Count - 1 do
2434 | begin
2435 | ButtonedEdit1.ACStrings.Add(FEnvExecutables[I]);
2436 | end;
2437 | end;
2438 |
2439 | ButtonedEdit1.ACStrings.EndUpdate;
2440 |
2441 | end;
2442 |
2443 | procedure TForm1.populateEnvironmentStrings;
2444 | var
2445 | Envs, Env : PChar;
2446 | begin
2447 | FCommandType := ctEnvironment;
2448 |
2449 | ButtonedEdit1.ACStrings.BeginUpdate;
2450 | ButtonedEdit1.ACStrings.Clear;
2451 |
2452 | Envs := GetEnvironmentStrings;
2453 | try
2454 | Env := Envs;
2455 | while Env^ <> #0 do
2456 | begin
2457 | ButtonedEdit1.ACStrings.Add(Env);
2458 | Env := Env + StrLen(Env) + 1;
2459 | end;
2460 | finally
2461 | FreeEnvironmentStrings(Envs);
2462 | end;
2463 |
2464 | ButtonedEdit1.ACStrings.EndUpdate;
2465 | end;
2466 |
2467 | procedure TForm1.populateMyFolders;
2468 | begin
2469 | with ButtonedEdit1.ACStrings do
2470 | begin
2471 | BeginUpdate;
2472 | Clear;
2473 | Add('Dir=L:\Proyectos');
2474 | Add('Dir=F:\Components');
2475 | Add('Dir=L:\FreepascalProjects');
2476 | Add('Dir=F:\Projects');
2477 | Add('Dir=O:\Projects');
2478 | EndUpdate;
2479 | end;
2480 | end;
2481 |
2482 | procedure TForm1.ProcessDosCommand(Sender: TObject; ACommand: string; terminateCurrent: Boolean = False);
2483 | begin
2484 | if DosCommand1.IsRunning and terminateCurrent then
2485 | begin
2486 | DosCommand1.Stop;
2487 | end
2488 | else if DosCommand1.IsRunning and not terminateCurrent then
2489 | begin
2490 | if MessageDlg('A previous command is processing!'#13#10'Shoul I kill it?', TMsgDlgType.mtWarning, mbYesNo, 0) = mrYes then
2491 | begin
2492 | DosCommand1.Stop;
2493 | end;
2494 | end;
2495 |
2496 | if not DosCommand1.IsRunning then
2497 | begin
2498 | try
2499 | DosCommand1.InputToOutput := False;
2500 |
2501 | DosCommand1.CommandLine := ACommand;
2502 | DosCommand1.Execute;
2503 | ActivityIndicator1.Visible := True;
2504 | ActivityIndicator1.Animate := DosCommand1.IsRunning;
2505 | except
2506 | on e:ECreateProcessError do
2507 | begin
2508 |
2509 | end;
2510 | end;
2511 | end;
2512 | end;
2513 |
2514 | procedure TForm1.Show1Click(Sender: TObject);
2515 | begin
2516 | Show;
2517 | end;
2518 |
2519 | function TForm1.ShowPreview(const FileName: string): Boolean;
2520 | var
2521 | wicImg: TWICImage;
2522 | wicList: IWICImagingFactory;
2523 | hr: HRESULT;
2524 | list: IEnumUnknown;
2525 | vInt: IUnknown;
2526 | decoder: IWICBitmapDecoderInfo;
2527 | vBuf: array[0..255] of Char;
2528 | vLen: UINT;
2529 | friendlyName: string;
2530 | fileext: string;
2531 | begin
2532 |
2533 | {CoInitialize(nil);
2534 |
2535 | hr := CoCreateInstance(CLSID_WICImagingFactory, nil, CLSCTX_INPROC_SERVER,
2536 | IID_IWICImagingFactory, wicList);
2537 | //OleCheck(hr);
2538 | if Succeeded(hr) then
2539 | begin
2540 | hr := wicList.CreateComponentEnumerator(WICDecoder, WICComponentEnumerateDefault, list);
2541 | if Succeeded(hr) then
2542 | begin
2543 | while list.Next(1, vInt, nil) = S_OK do
2544 | begin
2545 | if Succeeded(vInt.QueryInterface(IID_IWICBitmapDecoderInfo, decoder)) then
2546 | begin
2547 | if (decoder.GetFriendlyName(High(vBuf), vBuf, vLen) = S_OK) and (vLen > 1) then
2548 | begin
2549 | SetString(friendlyName, PChar(@vBuf), vLen - 1);
2550 | BCEditor1.Lines.Add('WIC : ' + friendlyName);
2551 | end;
2552 | if (decoder.GetFileExtensions(0, nil, vLen) = S_OK) and (vLen > 1) then
2553 | begin
2554 | SetLength(fileext, vLen - 1);
2555 | decoder.GetFileExtensions(vLen, PChar(fileExt), vLen);
2556 | BCEditor1.Lines.Add('WIC extensions: ' + fileext);
2557 | end;
2558 |
2559 | end;
2560 | vInt := nil;
2561 | end;
2562 |
2563 | end;
2564 | end;
2565 |
2566 |
2567 | CoUninitialize;}
2568 |
2569 | Image1.Picture.Bitmap.Handle := ExtractThumbnail(FileName, 256, 256 );
2570 |
2571 | Result := False;
2572 |
2573 | if fPreview <> nil then
2574 | fPreview.Free;
2575 | { DISABLE FOR NOW
2576 | fPreview := THostPreviewHandler.Create(Self);
2577 | fPreview.Top := 0;
2578 | fPreview.Left := 0;
2579 | fPreview.Width := pnlPreview.ClientWidth;
2580 | fPreview.Height := pnlPreview.ClientHeight;
2581 | fPreview.Parent := pnlPreview;
2582 | fPreview.Align := alClient;
2583 | fPreview.FileName := FileName;
2584 |
2585 | if fPreview.Previewable then
2586 | begin
2587 | fPreview.Visible := True;
2588 | THostPreviewHandlerClass(fPreview).Paint
2589 | end
2590 | else
2591 | begin
2592 | // handle by ourselves the preview
2593 | fPreview.Visible := False;
2594 | case IndexStr(ExtractFileExt(FileName).ToLower,[
2595 | // WIC supported by default are the following according to
2596 | // https://docs.microsoft.com/es-mx/windows/win32/wic/-wic-about-windows-imaging-codec?redirectedfrom=MSDN
2597 | '.bmp', '.gif', '.ico', '.jpeg', '.jpg',
2598 | '.jfif', '.png', '.tiff', '.wdp', '.dds',
2599 | //https://docs.microsoft.com/en-us/windows/win32/wic/native-wic-codecs
2600 | '.dng', '.jxr', '.tif', '.jpe', '.dib',
2601 | // unsupported (unless you installed a wic-enabled codec)
2602 | '.webp', '.avif', '.heif', '.flif'
2603 | ]) of
2604 | 0..14:
2605 | begin
2606 | wicImg := TWICImage.Create;
2607 | try
2608 |
2609 | wicImg.LoadFromFile(FileName);
2610 | EsImage1.Picture.Assign(wicImg);
2611 | EsImage1.Repaint;
2612 | finally
2613 | wicImg.Free;
2614 | end;
2615 | end
2616 | else
2617 | begin
2618 |
2619 | end;
2620 |
2621 | end;
2622 |
2623 | end;}
2624 | end;
2625 |
2626 | procedure TForm1.SpeedButton1Click(Sender: TObject);
2627 | begin
2628 | with SpeedButton1 do
2629 | begin
2630 | if FPinned then // if not pinned
2631 | begin
2632 | FPinned := False;
2633 | Caption := '';
2634 | end
2635 | else
2636 | begin
2637 | FPinned := True;
2638 | Caption := '' // pin
2639 | end;
2640 | end;
2641 |
2642 | end;
2643 |
2644 | procedure TForm1.SwitchToWindow(AWnd: HWND);
2645 | var
2646 | HActiveWindow: HWND;
2647 | HForegroundThread, HAppThread: DWORD;
2648 | FClientId: DWORD;
2649 | begin
2650 | HActiveWindow := AWnd;
2651 |
2652 | HForegroundThread := GetWindowThreadProcessId(HActiveWindow, @FClientId);
2653 | AllowSetForegroundWindow(FClientId);
2654 | // SwitchToThisWindow(AWnd, True);
2655 | HAppThread := GetCurrentThreadId;
2656 |
2657 | AttachThreadInput(HForegroundThread, HAppThread, True);
2658 | BringWindowToTop(AWnd);
2659 | Winapi.Windows.SetFocus(AWnd);
2660 | AttachThreadInput(HForegroundThread, HAppThread, False);
2661 | SetForegroundWindow(AWnd);
2662 | end;
2663 |
2664 | procedure TForm1.tmrOutputTimer(Sender: TObject);
2665 | var
2666 | tmpbuf: TStringList;
2667 | begin
2668 | if not Assigned(FOutputBuffer) then Exit;
2669 |
2670 | if FOutputBuffer.Count = 0 then
2671 | Exit;
2672 |
2673 | tmpbuf := TStringList.Create;
2674 | try
2675 | FSyncLock.Enter;
2676 | try
2677 | tmpbuf.Assign(FOutputBuffer);
2678 | FOutputBuffer.Clear;
2679 | finally
2680 | FSyncLock.Leave;
2681 | end;
2682 |
2683 | BCEditor1.Lines.BeginUpdate;
2684 | try
2685 | BCEditor1.Lines.AddStrings(tmpbuf);
2686 | BCEditor1.GotoLineAndCenter(BCEditor1.Lines.Count - 1);
2687 | BCEditor1.Refresh;
2688 | finally
2689 | BCEditor1.Lines.EndUpdate;
2690 | end;
2691 | finally
2692 | tmpbuf.Free;
2693 | end;
2694 | end;
2695 |
2696 | procedure TForm1.tmrToastTimer(Sender: TObject);
2697 | begin
2698 | StatusBar1.Panels[0].Text := CurrentFile;
2699 | tmrToast.Enabled := False;
2700 | end;
2701 |
2702 | procedure TForm1.Toast(aText, aTitle, sType: string; ParentBase: TWinControl);
2703 | begin
2704 | StatusBar1.Panels[0].Text := aText;
2705 | tmrToast.Enabled := True;
2706 |
2707 | end;
2708 |
2709 | procedure TForm1.TrayIcon1DblClick(Sender: TObject);
2710 | begin
2711 | Visible := not Visible;
2712 | end;
2713 |
2714 | procedure TForm1.UpdateMainMenu(const ForeGroundWindow: HWND);
2715 | var
2716 | lMenu: HMENU;
2717 | MenuItemCount, MenuItemID: Integer;
2718 | MenuItemText: array[0..255] of Char;
2719 | i: Integer;
2720 | NewMenuItem: TMenuItem;
2721 |
2722 | AU: TCUIAutomation;
2723 | WindowElement, MenuElement, MenuItemElement: IUIAutomationElement;
2724 | Collection, MenuItems: IUIAutomationElementArray;
2725 | Condition: IUIAutomationCondition;
2726 | MenuItemName: WideString;
2727 | Len: Integer;
2728 | retVal: Integer;
2729 | ExpandCollapsePattern: IUIAutomationExpandCollapsePattern;
2730 | //const
2731 | // ControlType_Menu: TGUID = '{d9077285-5a2e-4fb1-991c-ac0f69a4d9b3}'; // Menu control type GUID
2732 |
2733 | begin
2734 | // MainMenu1.Items.Clear;
2735 |
2736 | if ForeGroundWindow = Handle then
2737 | Exit;
2738 |
2739 | AU := TCUIAutomation.Create(nil);
2740 |
2741 | AU.ElementFromHandle(Pointer(ForeGroundWindow), WindowElement);
2742 |
2743 | // AU.CreatePropertyCondition(UIA_ControlTypePropertyId, ControlType_Menu, Condition);
2744 | AU.CreateTrueCondition(Condition);
2745 |
2746 | // WindowElement.FindFirst(TreeScope_Descendants, Condition, MenuElement);
2747 | WindowElement.FindAll(TreeScope_Descendants, Condition, Collection);
2748 |
2749 | Collection.Get_Length(Len);
2750 |
2751 | for I := 0 to Len - 1 do
2752 | begin
2753 | Collection.GetElement(I, MenuItemElement);
2754 | MenuItemElement.Get_CurrentControlType(retVal);
2755 |
2756 | if (retVal = UIA_MenuItemControlTypeId) then
2757 | begin
2758 | MenuItemElement.Get_CurrentName(MenuItemName);
2759 |
2760 | // NewMenuItem := TMenuItem.Create(MainMenu1);
2761 | // NewMenuItem.Caption := MenuItemName;
2762 | // NewMenuItem.Tag := I;
2763 | BCEditor1.Lines.Add(MenuItemName);
2764 | // MainMenu1.Items.Add(NewMenuItem);
2765 |
2766 | // MenuItemElement.GetCurrentPattern(UIA_ExpandCollapsePatternId, IInterface(ExpandCollapsePattern));
2767 | // if Assigned(ExpandCollapsePattern) then
2768 | // begin
2769 | // ExpandCollapsePattern.Expand;
2770 | // if Recurse = True then
2771 | //
2772 | //
2773 | // end;
2774 |
2775 | end;
2776 |
2777 | end;
2778 |
2779 | // if Assigned(MenuElement) then
2780 | // begin
2781 | // MenuElement.FindAll(TreeScope_Children, Condition, MenuItems);
2782 | // if Assigned(MenuItems) then
2783 | // begin
2784 | // MenuItems.Get_Length(Len);
2785 | // for I := 0 to Len - 1 do
2786 | // begin
2787 | // MenuItems.GetElement(I, MenuItemElement);
2788 | // MenuItemElement.GetCurrentPropertyValue(UIA_NamePropertyId, MenuItemName);
2789 | //
2790 | // NewMenuItem := TMenuItem.Create(MainMenu1);
2791 | // NewMenuItem.Caption := MenuItemName;
2792 | // NewMenuItem.Tag := I;
2793 | // BCEditor1.Lines.Add(MenuItemName);
2794 | //
2795 | // MainMenu1.Items.Add(NewMenuItem);
2796 | // end;
2797 | //
2798 | // end;
2799 | // end;
2800 |
2801 | AU.Free;
2802 |
2803 | // lMenu := GetMenu(FindWindow('TAppBuilder', 'ExplorerCommand - RAD Studio 11 - main [Built]'));
2804 | // if lMenu <> 0 then
2805 | // begin
2806 | // MenuItemCount := GetMenuItemCount(lMenu);
2807 | // for I := 0 to MenuItemCount - 1 do
2808 | // begin
2809 | // MenuItemID := GetMenuItemID(lMenu, I);
2810 | // if MenuItemID <> -1 then
2811 | // begin
2812 | // GetMenuString(lMenu, MenuItemID, MenuItemText, SizeOf(MenuItemText), MF_BYCOMMAND);
2813 | // NewMenuItem := TMenuItem.Create(MainMenu1);
2814 | // NewMenuItem.Caption := MenuItemText;
2815 | // NewMenuItem.Tag := MenuItemID;
2816 | // MainMenu1.Items.Add(NewMenuItem);
2817 | // end;
2818 | // end;
2819 | // end;
2820 | end;
2821 |
2822 | procedure TForm1.UpdateStyle;
2823 | const
2824 | BGCOLOR = $00191919;//$00362A28;
2825 | begin
2826 | //on light
2827 | if IsWindowsDarkMode then
2828 | begin
2829 | AllowDarkModeForApp(True);
2830 | Form1.Color := RGB(38, 40, 4);
2831 | Form1.AlphaBlend := True;
2832 | Form1.AlphaBlendValue := 253;
2833 | with SynPasSyn1 do
2834 | begin
2835 | CommentAttri.Foreground := $00A47262;
2836 | CommentAttri.Background := BGCOLOR;
2837 |
2838 | // EventAttri.Foreground := $00FDE98B;
2839 | // EventAttri.Background := $00362A28;
2840 | // EventAttri.Style := [fsBold];
2841 |
2842 | IdentifierAttri.Foreground := $00F2F8F8;
2843 | IdentifierAttri.Background := BGCOLOR;
2844 |
2845 | KeyAttri.Foreground := $0054B91D;//FDE98B;
2846 | KeyAttri.Background := BGCOLOR;
2847 | KeyAttri.Style := [fsBold];
2848 |
2849 | // NonReservedKeyAttri.Foreground := $0054B91D;//$00FDE98B;
2850 | // NonReservedKeyAttri.Background := $00362A28;
2851 | // NonReservedKeyAttri.Style := [fsBold];
2852 |
2853 | NumberAttri.Foreground := $00F993BD;
2854 | NumberAttri.Background := BGCOLOR;
2855 |
2856 | SpaceAttri.Foreground := clWindowText;
2857 | SpaceAttri.Background := BGCOLOR;//MOST PART
2858 |
2859 | // SpecVarAttri.Foreground := $00C679FF;
2860 | // SpecVarAttri.Background := $00362A28;
2861 | // SpecVarAttri.Style := [fsBold];
2862 |
2863 | StringAttri.Foreground := $008BE9FC;
2864 | StringAttri.Background := clNone;
2865 |
2866 | SymbolAttri.Foreground := $00C679FF;
2867 | SymbolAttri.Background := BGCOLOR;
2868 |
2869 | // TemplateAttri.Foreground := $008BE9FC;
2870 | // TemplateAttri.Background := clNone;
2871 | end;
2872 |
2873 | rkSmartPath1.Font.Color := clWhite;
2874 | TStyleManager.TrySetStyle('Windows11 Modern Dark');
2875 | end
2876 | else
2877 | begin
2878 | Form1.Color := RGB(248, 249, 253); //dark: 38, 40 44
2879 | Form1.AlphaBlend := True;
2880 | Form1.AlphaBlendValue := 250; // 253
2881 | TStyleManager.TrySetStyle('Windows');
2882 | end;
2883 |
2884 | end;
2885 |
2886 | procedure TForm1.UpdateTheme;
2887 | begin
2888 | UpdateStyle;
2889 |
2890 | // EnableImmersiveDarkMode(True);
2891 | // UseImmersiveDarkMode(Handle, True); //my function to dark mode titlebar win11+
2892 | // EnableNCShadow(Handle);
2893 |
2894 | if IsWindowsDarkMode then
2895 | begin
2896 | ACLApplicationController1.DarkMode := TACLBoolean.True;
2897 | SetDarkMode(Handle, True);
2898 | end
2899 | else
2900 | begin
2901 | ACLApplicationController1.DarkMode := TACLBoolean.False;
2902 | SetDarkMode(Handle, False);
2903 | end;
2904 | end;
2905 |
2906 | procedure TForm1.RefreshEnvironmentVariables;
2907 | var
2908 | TokenHandle: THandle;
2909 | EnvironmentStrings: PEnvironment; // LPTSTR;
2910 | Current: PChar;
2911 | begin
2912 | TokenHandle := 0;
2913 | try
2914 | if not OpenProcessToken(GetCurrentProcess, TOKEN_QUERY or TOKEN_DUPLICATE, TokenHandle) then
2915 | RaiseLastOSError;
2916 |
2917 | // Get the environment strings block
2918 | //EnvironmentStrings := GetEnvironmentStrings;
2919 | if CreateEnvironmentBlock(EnvironmentStrings, TokenHandle, False) then
2920 | try
2921 | if EnvironmentStrings = nil then
2922 | Exit;
2923 |
2924 | FEnvStrings.Clear;
2925 | FEnvStrings.Delimiter := ';';
2926 | FEnvStrings.StrictDelimiter := True;
2927 | // Loop through the environment strings and reload them
2928 | Current := PChar(EnvironmentStrings);
2929 | while Current^ <> #0 do
2930 | begin
2931 | var EnvEntry := String(Current);
2932 | var Pos := EnvEntry.IndexOf('=');
2933 | if Pos > 0 then
2934 | begin
2935 | var Name := Copy(EnvEntry, 1, Pos);
2936 | var Value := Copy(EnvEntry, Pos + 2, Length(EnvEntry) - Pos - 1);
2937 | Winapi.Windows.SetEnvironmentVariable(PChar(Name), PChar(Value));
2938 | if LowerCase(Name) = 'path' then
2939 | FEnvStrings.DelimitedText := PChar(Value);
2940 | end;
2941 |
2942 | // Move to the next environment string
2943 | Inc(Current, StrLen(Current) + 1);
2944 | end;
2945 | finally
2946 | //FreeEnvironmentStrings(EnvironmentStrings);
2947 | RtlDestroyEnvironment(EnvironmentStrings);
2948 | end
2949 | else
2950 | RaiseLastOSError;
2951 | finally
2952 | if TokenHandle <> 0 then
2953 | CloseHandle(TokenHandle);
2954 | end;
2955 | end;
2956 |
2957 | procedure TForm1.WMSettingChange(var Msg: TMessage);
2958 | begin
2959 | if PChar(Msg.LParam) = 'Environment' then
2960 | begin
2961 | RefreshEnvironmentVariables;
2962 | // ShowMessage('Environment refreshed!');
2963 | end;
2964 | inherited;
2965 | end;
2966 |
2967 | procedure TForm1.WndProc(var Message: TMessage);
2968 | begin
2969 | inherited;
2970 |
2971 | if Message.Msg = WM_SETTINGCHANGE then
2972 | begin
2973 | UpdateTheme;
2974 | end;
2975 | end;
2976 |
2977 | { ThumbThread }
2978 |
2979 | constructor ThumbThread.Create(View: TrkView; Items: TList);
2980 | begin
2981 | ViewLink := View;
2982 | ItemsLink := Items;
2983 | FreeOnTerminate := False;
2984 | inherited Create(False);
2985 | Priority := tpLower;
2986 | end;
2987 |
2988 | procedure ThumbThread.Execute;
2989 | var
2990 | Cnt, I: Integer;
2991 | PThumb: PItemData;
2992 | Old: Integer;
2993 | InView: Integer;
2994 | ShellFolder, DesktopShellFolder: IShellFolder;
2995 | XtractImage: IExtractImage;
2996 | XtractImage2: IExtractImage2;
2997 | XtractIcon: IExtractIcon;
2998 | fileShellItemImage: IShellItemImageFactory;
2999 | ImageFactory: IShellItemImageFactory;
3000 | Bmp: TBitmap;
3001 | Path: string;
3002 | Eaten: DWORD;
3003 | PIDL: PItemIDList;
3004 | RunnableTask: IRunnableTask;
3005 | Flags: DWORD;
3006 | Buf: array[0..MAX_PATH * 4] of WideChar;
3007 | BmpHandle: HBITMAP;
3008 | Attribute, Priority: DWORD;
3009 | GetLocationRes: HRESULT;
3010 | ThumbJPEG: TJPEGImage;
3011 | MS: TMemoryStream;
3012 | ASize: TSize;
3013 | FName: string;
3014 | p, pro: Integer;
3015 | PV: Single;
3016 | IIdx: Integer;
3017 | IFlags: Cardinal;
3018 | SIcon, LIcon: HICON;
3019 | IconS, IconL: TIcon;
3020 | Done: Boolean;
3021 | Res: HRESULT;
3022 | ColorDepth: Cardinal;
3023 | IsVistaOrLater: Boolean;
3024 | begin
3025 | inherited;
3026 | if (ViewLink.Items.Count = 0) then
3027 | Exit;
3028 |
3029 | IsVistaOrLater := CheckWin32Version(6);
3030 |
3031 | CoInitializeEx(nil, COINIT_APARTMENTTHREADED or COINIT_DISABLE_OLE1DDE);
3032 | try
3033 | ThumbJPEG := TJPEGImage.Create;
3034 | ThumbJPEG.CompressionQuality := 80;
3035 | ThumbJPEG.Performance := jpBestSpeed;
3036 | Path := form1.Directory;
3037 |
3038 | OleCheck(SHGetDesktopFolder(DesktopShellFolder));
3039 | OleCheck(DesktopShellFolder.ParseDisplayName(0, nil, StringToOleStr(Path),
3040 | Eaten, PIDL, Attribute));
3041 | OleCheck(DesktopShellFolder.BindToObject(PIDL, nil, IID_IShellFolder,
3042 | Pointer(ShellFolder)));
3043 | CoTaskMemFree(PIDL);
3044 |
3045 | Cnt := 0;
3046 | Old := ViewLink.ViewIdx;
3047 | pro := 0;
3048 | PV := 100 / ViewLink.Items.Count;
3049 | repeat
3050 | while (not Terminated) and (Cnt < ViewLink.Items.Count) do
3051 | begin
3052 | if Old <> ViewLink.ViewIdx then
3053 | begin
3054 | Cnt := ViewLink.ViewIdx - 1;
3055 | if Cnt = -1 then
3056 | Cnt := 0;
3057 | Old := ViewLink.ViewIdx;
3058 | end;
3059 |
3060 | PThumb := PItemData(ItemsLink.Items[ViewLink.Items[Cnt]]);
3061 | Done := PThumb.GotThumb;
3062 | PThumb.ImgState := 0;
3063 |
3064 | if IsVistaOrLater then
3065 | begin
3066 | if not Done then
3067 | begin
3068 | Bmp := TBitmap.Create;
3069 | Bmp.Canvas.Lock;
3070 | FName := Path + PThumb.Name;
3071 | Res := SHCreateItemFromParsingName(PChar(FName), nil,
3072 | IShellItemImageFactory, fileShellItemImage);
3073 | if Succeeded(Res) then
3074 | begin
3075 | ASize.cx := 256;
3076 | ASize.cy := 256;
3077 | Res := fileShellItemImage.GetImage(ASize, SIIGBF_THUMBNAILONLY or SIIGBF_BIGGERSIZEOK,
3078 | BmpHandle);
3079 | if Succeeded(Res) then
3080 | begin
3081 | Bmp.Canvas.Unlock;
3082 | Bmp.Handle := BmpHandle;
3083 | Bmp.Canvas.Lock;
3084 | HackAlpha(Bmp, clWhite);
3085 | PThumb.IsIcon := False;
3086 | Done := True;
3087 | end;
3088 | end;
3089 | end;
3090 | end
3091 | else
3092 | begin
3093 | if not Done then
3094 | begin
3095 | Bmp := TBitmap.Create;
3096 | Bmp.Canvas.Lock;
3097 | OleCheck(ShellFolder.ParseDisplayName(0, nil,
3098 | StringToOleStr(PThumb.Name), Eaten, PIDL, Attribute));
3099 | ShellFolder.GetUIObjectOf(0, 1, PIDL, IExtractImage, nil,
3100 | XtractImage);
3101 | CoTaskMemFree(PIDL);
3102 | if Assigned(XtractImage) then
3103 | begin
3104 | if XtractImage.QueryInterface(IID_IExtractImage2,
3105 | Pointer(XtractImage2)) <> E_NOINTERFACE then
3106 | else
3107 | XtractImage2 := nil;
3108 | RunnableTask := nil;
3109 | ASize.cx := 256;
3110 | ASize.cy := 256;
3111 | Priority := 0;
3112 | Flags :=
3113 | IEIFLAG_SCREEN or IEIFLAG_OFFLINE or IEIFLAG_ORIGSIZE
3114 | or IEIFLAG_QUALITY;
3115 | ColorDepth := 32;
3116 | GetLocationRes := XtractImage.GetLocation(Buf, MAX_PATH,
3117 | Priority, ASize, ColorDepth, Flags);
3118 | if (GetLocationRes = NOERROR) or (GetLocationRes = E_PENDING) then
3119 | begin
3120 | if GetLocationRes = E_PENDING then
3121 | if XtractImage.QueryInterface(IRunnableTask, RunnableTask)
3122 | <> S_OK then
3123 | RunnableTask := nil;
3124 | try
3125 | if Succeeded(XtractImage.Extract(BmpHandle)) then
3126 | begin
3127 | Bmp.Canvas.Unlock;
3128 | Bmp.Handle := BmpHandle;
3129 | Bmp.Canvas.Lock;
3130 | HackAlpha(Bmp, clWhite);
3131 | PThumb.IsIcon := False;
3132 | Done := True;
3133 | end;
3134 | except
3135 | on E: EOleSysError do
3136 | OutputDebugString(
3137 | PChar(string(E.ClassName) + ': ' + E.Message)
3138 | )
3139 | else
3140 | raise;
3141 | end;
3142 | end;
3143 | end;
3144 | end;
3145 | end;
3146 |
3147 | end;
3148 | until (Cnt = 0) or (Terminated);
3149 |
3150 | if not Terminated then
3151 | PostMessage(Form1.Handle, CM_UpdateView, 0, 0);
3152 | PostMessage(Form1.Handle, CM_Progress, 0, 100);
3153 | ThumbJPEG.Free;
3154 | finally
3155 | CoUninitialize;
3156 | end;
3157 |
3158 | end;
3159 |
3160 | { TEnumString }
3161 |
3162 | function TEnumString.Clone(out enm: IEnumString): HResult;
3163 | begin
3164 | Result := E_NOTIMPL;
3165 | Pointer(enm) := nil;
3166 | end;
3167 |
3168 | constructor TEnumString.Create;
3169 | begin
3170 | inherited Create;
3171 | FStrings := TStringList.Create;
3172 | FCurrIndex := 0;
3173 | end;
3174 |
3175 | destructor TEnumString.Destroy;
3176 | begin
3177 | FStrings.Free;
3178 | inherited;
3179 | end;
3180 |
3181 | function TEnumString.Next(celt: Longint; out elt;
3182 | pceltFetched: PLongint): HResult;
3183 | var
3184 | I: Integer;
3185 | wStr: WideString;
3186 | begin
3187 | I := 0;
3188 | while (I < celt) and (FCurrIndex < FStrings.Count) do
3189 | begin
3190 | wStr := FStrings[FCurrIndex];
3191 | TPointerList(elt)[I] := CoTaskMemAlloc(2 * (Length(wStr) + 1));
3192 | StringToWideChar(wStr, TPointerList(elt)[I], 2 * (Length(wStr) + 1));
3193 | Inc(I);
3194 | Inc(FCurrIndex);
3195 | end;
3196 | if pceltFetched <> nil then
3197 | pceltFetched^ := I;
3198 | if I = celt then
3199 | Result := S_OK
3200 | else
3201 | Result := S_FALSE;
3202 | end;
3203 |
3204 | function TEnumString.Reset: HResult;
3205 | begin
3206 | FCurrIndex := 0;
3207 | Result := S_OK;
3208 | end;
3209 |
3210 | function TEnumString.Skip(celt: Longint): HResult;
3211 | begin
3212 | if (FCurrIndex + celt) <= FStrings.Count then
3213 | begin
3214 | Inc(FCurrIndex, celt);
3215 | Result := S_OK;
3216 | end
3217 | else
3218 | begin
3219 | FCurrIndex := FStrings.Count;
3220 | Result := S_FALSE;
3221 | end;
3222 | end;
3223 |
3224 | { TButtonedEdit }
3225 |
3226 | constructor TButtonedEdit.Create(AOwner: TComponent);
3227 | begin
3228 | inherited;
3229 | FACList := TEnumString.Create;
3230 | FEnumString := FACList;
3231 | FACEnabled := True;
3232 | FACOptions := [acAutoSuggest, acUpDownKeyDropsList];
3233 | end;
3234 |
3235 | class constructor TButtonedEdit.Create;
3236 | begin
3237 | if not TStyleManager.IsCustomStyleActive then
3238 | begin
3239 | Winapi.Windows.Beep(400, 1000);
3240 | TCustomStyleEngine.UnRegisterSysStyleHook('SysListView32', TSysListViewStyleHook);
3241 | TCustomStyleEngine.RegisterSysStyleHook('SysListView32', TSysListViewStyleHook);
3242 | end;
3243 | end;
3244 |
3245 | procedure TButtonedEdit.CreateWnd;
3246 | var
3247 | Dummy: IUnknown;
3248 | Strings: IEnumString;
3249 | FuzzyMatchList: TStringList;
3250 | FuzzyMatcher: TFuzzyStringMatcher;
3251 | AutocompleteEx: IAutoComplete2;
3252 | begin
3253 | inherited;
3254 | // SetWindowTheme(Handle, PChar('DarkMode_Explorer'), nil);
3255 | if HandleAllocated then
3256 | begin
3257 | try
3258 | Dummy := CreateComObject(CLSID_AutoComplete);
3259 | if (Dummy <> nil) and
3260 | (Dummy.QueryInterface(IID_IAutoComplete, FAutoComplete) = S_OK) then
3261 | begin
3262 | //https://learn.microsoft.com/en-us/windows/win32/api/shldisp/ne-shldisp-autocompleteoptions
3263 | // set auto completion options
3264 | if Dummy.QueryInterface(IID_IAutoComplete2, AutoCompleteEx) = S_OK then
3265 | AutoCompleteEx.SetOptions(ACO_AUTOSUGGEST or ACO_AUTOAPPEND or ACO_UPDOWNKEYDROPSLIST);
3266 |
3267 | case FACSource of
3268 | // acsList: ;
3269 | //It is used to manage the history of autocomplete entries.
3270 | acsHistory: Strings := CreateComObject(CLSID_ACLHistory) as IEnumString;
3271 | //It is used to manage the MRU autocomplete entries.
3272 | acsMRU: Strings := CreateComObject(CLSID_ACLMRU) as IEnumString;
3273 | //It is used to manage autocomplete entries specific to shell folders.
3274 | acsShell:
3275 | begin
3276 | Strings := CreateComObject(CLSID_ACListISF) as IEnumString;
3277 | end
3278 | else
3279 | begin
3280 | // Use FuzzyStringMatch to perform fuzzy string matching
3281 | FuzzyMatchList := TStringList.Create;
3282 | try
3283 | FuzzyMatcher := TFuzzyStringMatcher.Create(8);
3284 | finally
3285 |
3286 | end;
3287 | Strings := FACList as IEnumString; // original
3288 | end;
3289 | end;
3290 | if S_OK = FAutoComplete.Init(Handle, Strings, nil, nil) then
3291 | begin
3292 | SetACEnabled(FACEnabled);
3293 | SetACOptions(FACOptions);
3294 | // TCustomStyleEngine.RegisterSysStyleHook('SysListView32', TSysListViewStyleHook);
3295 | // TCustomStyleEngine.RegisterSysStyleHook('SysListView32', TSysListViewStyleHook);
3296 | end;
3297 | end;
3298 | except
3299 | // CLSID_IAutoComplete is not available
3300 | end;
3301 | end;
3302 |
3303 | end;
3304 |
3305 | destructor TButtonedEdit.Destroy;
3306 | begin
3307 | FACList := nil;
3308 | inherited;
3309 | end;
3310 |
3311 | procedure TButtonedEdit.DestroyWnd;
3312 | begin
3313 | if (FAutoComplete <> nil) then
3314 | begin
3315 | FAutoComplete.Enable(False);
3316 | FAutoComplete := nil;
3317 | end;
3318 |
3319 | inherited;
3320 |
3321 | end;
3322 |
3323 | function TButtonedEdit.GetACStrings: TStringList;
3324 | begin
3325 | Result := FACList.FStrings;
3326 | end;
3327 |
3328 | procedure TButtonedEdit.SetACEnabled(const Value: Boolean);
3329 | begin
3330 | if (FAutoComplete <> nil) then
3331 | begin
3332 | FAutoComplete.Enable(FACEnabled);
3333 | end;
3334 | FACEnabled := Value;
3335 | end;
3336 |
3337 | procedure TButtonedEdit.SetACOptions(const Value: TACOptions);
3338 | const
3339 | Options : array[TACOption]
3340 | of Integer = (
3341 | ACO_NONE,
3342 | ACO_AUTOSUGGEST,
3343 | ACO_AUTOAPPEND,
3344 | ACO_SEARCH,
3345 | ACO_FILTERPREFIXES,
3346 | ACO_USETAB,
3347 | ACO_UPDOWNKEYDROPSLIST,
3348 | ACO_RTLREADING,
3349 | ACO_WORD_FILTER,
3350 | ACO_NOPREFIXFILTERING
3351 | );
3352 | var
3353 | Option: TACOption;
3354 | Opt: DWORD;
3355 | AC2: IAutoComplete2;
3356 | begin
3357 | if (FAutoComplete <> nil) then
3358 | begin
3359 | if S_OK = FAutoComplete.QueryInterface(IID_IAutoComplete2, AC2) then
3360 | begin
3361 | Opt := ACO_NONE;
3362 | for Option := Low(Options) to High(Options) do
3363 | begin
3364 | if (Option in FACOptions) then
3365 | Opt := Opt or DWORD(Options[Option]);
3366 | end;
3367 | AC2.SetOptions(Opt);
3368 | end;
3369 | end;
3370 | FACOptions := Value;
3371 | end;
3372 |
3373 | procedure TButtonedEdit.SetACSource(const Value: TACSource);
3374 | begin
3375 | if FACSource <> Value then
3376 | begin
3377 | FACSource := Value;
3378 | RecreateWnd;
3379 | end;
3380 | end;
3381 |
3382 | procedure TButtonedEdit.SetACStrings(const Value: TStringList);
3383 | begin
3384 | if Value <> FACList.FStrings then
3385 | FACList.FStrings.Assign(Value);
3386 | end;
3387 |
3388 | { TFuzzyStringMatcher }
3389 |
3390 | constructor TFuzzyStringMatcher.Create(Threshold: Integer);
3391 | begin
3392 | FThreshold := Threshold;
3393 | end;
3394 |
3395 | function TFuzzyStringMatcher.DamerauLevenshteinDistance(const S1,
3396 | S2: string): Integer;
3397 | var
3398 | Len1, Len2, I, J, Cost, PrevCost: Integer;
3399 | D: array of array of Integer;
3400 | begin
3401 | Len1 := Length(S1);
3402 | Len2 := Length(S2);
3403 | SetLength(D, Len1 + 1, Len2 + 1);;
3404 |
3405 | for I := 0 to Len1 do
3406 | D[I, 0] := I;
3407 |
3408 | for J := 0 to Len2 do
3409 | D[0, J] := J;
3410 |
3411 | for I := 1 to Len1 do
3412 | begin
3413 | for J := 1 to Len2 do
3414 | begin
3415 | if S1[I] = S2[J] then
3416 | Cost := 0
3417 | else
3418 | Cost := 1;
3419 |
3420 | PrevCost := D[I - 1, J - 1];
3421 |
3422 | if (I > 1) and (J > 1) and (S1[I - 1] = S2[J]) and (S1[I] = S2[J - 1]) then
3423 | PrevCost := Min(PrevCost, D[I - 2, J - 2]);
3424 |
3425 | D[I, J] := Min(Min(D[I - 1, J] + 1, D[I, J - 1] + 1), PrevCost + Cost);
3426 | end;
3427 | end;
3428 |
3429 | Result := D[Len1, Len2];
3430 | end;
3431 |
3432 | function TFuzzyStringMatcher.IsMatch(const Str, SubStr: string): Boolean;
3433 | begin
3434 | Result := DamerauLevenshteinDistance(Str, SubStr) <= FThreshold;
3435 | end;
3436 |
3437 | end.
3438 |
--------------------------------------------------------------------------------