-
Notifications
You must be signed in to change notification settings - Fork 6
/
mmpFileUtils.pas
310 lines (258 loc) · 13.5 KB
/
mmpFileUtils.pas
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
{ MMP: Minimalist Media Player
Copyright (C) 2021-2024 Baz Cuda
https://github.com/BazzaCuda/MinimalistMediaPlayerX
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA
}
unit mmpFileUtils;
interface
uses
system.classes, system.sysUtils,
mmpNotify.notices, mmpNotify.notifier, mmpNotify.subscriber;
function mmpCanDeleteThis(const aFilePath: string; const aShiftState: TShiftState): boolean;
function mmpConfigFilePath: string;
function mmpCopyFile(const aFilePath: string; const aDstFolder: string; const bDeleteIt: boolean = FALSE; const bRecordUndo: boolean = TRUE): boolean;
function mmpDeleteThisFile(const aFilePath: string; const aShiftState: TShiftState; const bSilentDelete: boolean = FALSE; const bRunTasks: boolean = TRUE): boolean;
function mmpExePath: string;
function mmpFileNameWithoutExtension(const aFilePath: string): string;
function mmpFileSize(const aFilePath: string): int64;
function mmpFileVersionFmt(const aFilePath: string = ''; const fmt: string = 'v%d.%d.%d.%d'): string;
function mmpIsEditFriendly(const aFilePath: string): boolean;
function mmpIsFileInUse(const aFilePath: string; out aSysErrorMessage: string): boolean;
function mmpKeepDelete(const aFolderPath: string): boolean;
function mmpRenameFile(const aFilePath: string; const aNewFileNamePart: string = ''): string;
function mmpRunTasks: boolean;
implementation
uses
winApi.windows,
system.ioUtils,
vcl.dialogs,
mmpConsts, mmpDialogs, mmpFolderUtils, mmpFormInputBox, mmpShellUtils, mmpShredUtils, mmpUtils,
model.mmpConfigFile, model.mmpMediaTypes, model.mmpUndoMove;
function mmpCanDeleteThis(const aFilePath: string; const aShiftState: TShiftState): boolean;
begin
result := FALSE;
var vMT := MT.mediaType(aFilePath);
case ssCtrl in aShiftState of TRUE: case CF.asBoolean[CONF_FOLDER_DELETE] of FALSE: EXIT; end;end;
case ssCtrl in aShiftState of FALSE: case vMT of
mtAudio: case CF.asBoolean[CONF_AUDIO_DELETE] of FALSE: EXIT; end;
mtImage: case CF.asBoolean[CONF_IMAGE_DELETE] of FALSE: EXIT; end;
mtVideo: case CF.asBoolean[CONF_VIDEO_DELETE] of FALSE: EXIT; end;end;end;
result := TRUE;
end;
function mmpConfigFilePath: string;
begin
result := mmpExePath + 'MinimalistMediaPlayer.conf';
end;
function mmpConfirmDelete(const aFilePath: string; const aShiftState: TShiftState): boolean;
begin
result := FALSE;
var vMsg := 'DELETE '#13#10#13#10'Folder: ' + extractFilePath(aFilePath);
case ssCtrl in aShiftState of TRUE: vMsg := 'DELETE folder contents '#13#10#13#10'Folder: ' + extractFilePath(aFilePath) + '*.*';
FALSE: vMsg := vMsg + #13#10#13#10'File: ' + extractFileName(aFilePath); end;
case CF.asDeleteMethod[CONF_DELETE_METHOD] in [dmStandard, dmShred] of TRUE: vMsg := vMsg + #13#10#13#10'Only click OK if you are ABSOLUTELY SURE'; end;
case CF.asDeleteMethod[CONF_DELETE_METHOD] of dmShred: vMsg := vMsg + #13#10'Shred: Once they''re gone, they are GONE!'; end;
case ssCtrl in aShiftState of TRUE: vMsg := vMsg + #13#10#13#10'(doesn''t affect the contents of subfolders)'; end;
result := mmpShowOkCancelMsgDlg(vMsg) = IDOK;
end;
function mmpCopyFile(const aFilePath: string; const aDstFolder: string; const bDeleteIt: boolean = FALSE; const bRecordUndo: boolean = TRUE): boolean;
var
vDestFile: string;
vDestFolder: string;
vCancel: PBOOL;
i: integer;
vExt: string;
begin
result := FALSE;
try
vDestFolder := aDstFolder;
vDestFile := vDestFolder + ExtractFileName(aFilePath);
try
forceDirectories(vDestFolder);
except end; // this will get picked up by the failed directoryExists below
vCancel := PBOOL(FALSE);
i := 0;
case directoryExists(vDestFolder) of TRUE: begin
vExt := extractFileExt(aFilePath);
while fileExists(vDestFile) do begin
inc(i);
vDestFile := vDestFolder + TPath.getFileNameWithoutExtension(aFilePath) + ' ' + intToStr(i) + vExt;
end;
result := copyFileEx(PChar(aFilePath), PChar(vDestFile), NIL, NIL, vCancel, 0);
case result and bDeleteIt of TRUE: begin
mmpDeleteThisFile(aFilePath, [], bDeleteIt);
case bRecordUndo of TRUE: UM.recordUndo(aFilePath, vDestFile); end;
end;end;
end;end;
finally
end;
end;
function mmpExePath: string;
begin
result := mmpITBS(extractFilePath(paramStr(0)));
end;
function mmpFileNameWithoutExtension(const aFilePath: string): string;
begin
result := TPath.getFileNameWithoutExtension(aFilePath);
end;
function mmpDeleteThisFile(const aFilePath: string; const aShiftState: TShiftState; const bSilentDelete: boolean = FALSE; const bRunTasks: boolean = TRUE): boolean;
var vSysMessage: string;
begin
result := FALSE;
case bSilentDelete of FALSE: begin
case mmpCanDeleteThis(aFilePath, aShiftState) of FALSE: begin
mmpShowOKCancelMsgDlg('MinimalistMediaPlayer.conf settings prevented this deletion operation', mtInformation, [mbOK]);
EXIT; end;end;
case mmpConfirmDelete(aFilePath, aShiftState) of FALSE: EXIT; end;
end;end;
case mmpIsFileInUse(aFilePath, vSysMessage) of TRUE: begin
mmpShowOkCancelMsgDlg(aFilePath + #13#10#13#10 +
'This file won''t be deleted or moved'#13#10#13#10 +
vSysMessage, TMsgDlgType.mtWarning, [mbOK]);
EXIT; end;end;
case ssCtrl in aShiftState of TRUE: mmpShredThis(extractFilePath(aFilePath), CF.asDeleteMethod[CONF_DELETE_METHOD]); // folder contents but not subfolders
FALSE: mmpShredThis(aFilePath, CF.asDeleteMethod[CONF_DELETE_METHOD]); end; // one individual file
case bRunTasks of TRUE: result := mmpRunTasks;
FALSE: result := TRUE; end;
end;
function mmpFileSize(const aFilePath: string): int64;
var
vHandle: THandle;
vRec: TWin32FindData;
begin
result := -1;
vHandle := findFirstFile(PChar(aFilePath), vRec);
case vHandle <> INVALID_HANDLE_VALUE of TRUE: begin
winAPI.windows.findClose(vHandle);
case (vRec.dwFileAttributes and FILE_ATTRIBUTE_DIRECTORY) = 0 of TRUE:
result := (int64(vRec.nFileSizeHigh) shl 32) + vRec.nFileSizeLow; end;end;end;
end;
function mmpFileVersionFmt(const aFilePath: string = ''; const fmt: string = 'v%d.%d.%d.%d'): string;
var
vFilePath: string;
iBufferSize: DWORD;
iDummy: DWORD;
pBuffer: pointer;
pFileInfo: pointer;
iVer: array[1..4] of WORD;
begin
// set default value
result := '';
// get filename of exe/dll if no filename is specified
vFilePath := aFilePath;
case vFilePath = '' of TRUE: begin
// prepare buffer for path and terminating #0
setLength(vFilePath, MAX_PATH + 1);
setLength(vFilePath, getModuleFileName(hInstance, PChar(vFilePath), MAX_PATH + 1));
end;end;
// get size of version info (0 if no version info exists)
iBufferSize := getFileVersionInfoSize(PChar(vFilePath), iDummy);
case iBufferSize > 0 of TRUE: begin
getMem(pBuffer, iBufferSize);
try
// get fixed file info (language independent)
getFileVersionInfo(PChar(vFilePath), 0, iBufferSize, pBuffer);
verQueryValue(pBuffer, '\', pFileInfo, iDummy);
// read version blocks
iVer[1] := hiWord(PVSFixedFileInfo(pFileInfo)^.dwFileVersionMS);
iVer[2] := loWord(PVSFixedFileInfo(pFileInfo)^.dwFileVersionMS);
iVer[3] := hiWord(PVSFixedFileInfo(pFileInfo)^.dwFileVersionLS);
iVer[4] := loWord(PVSFixedFileInfo(pFileInfo)^.dwFileVersionLS);
finally
freeMem(pBuffer);
end;
// format result string
result := format(fmt, [iVer[1], iVer[2], iVer[3], iVer[4]]);
end;end;
end;
function mmpIsEditFriendly(const aFilePath: string): boolean;
begin
result := NOT aFilePath.contains('''') and NOT aFilePath.contains('&');
end;
function mmpIsFileInUse(const aFilePath: string; out aSysErrorMessage: string): boolean;
begin
result := FALSE;
aSysErrorMessage := '';
setLastError(ERROR_SUCCESS);
var hFile := createFile(PWideChar(aFilePath), GENERIC_WRITE, FILE_SHARE_READ OR FILE_SHARE_WRITE, NIL, OPEN_EXISTING, FILE_FLAG_WRITE_THROUGH, 0);
result := hFile = INVALID_HANDLE_VALUE;
case result of TRUE: aSysErrorMessage := sysErrorMessage(getLastError);
FALSE: closeHandle(hFile); end;
end;
function mmpKeepDelete(const aFolderPath: string): boolean;
const
faFilesOnly = faAnyFile AND NOT faDirectory AND NOT faHidden AND NOT faSysFile;
var
vSR: TSearchRec;
vExt: string;
function fileOK: boolean;
begin
result := vSR.name[1] <> '!';
end;
begin
result := FALSE;
case directoryExists(aFolderPath) of FALSE: EXIT; end;
case CF.asBoolean[CONF_KEEP_DELETE] of FALSE: begin
var vMsg := 'keepDelete=no'#13#10#13#10;
vMsg := vMsg + 'To use this functionality, you must explicitly enable it'#13#10;
vMsg := vMsg + 'in MinimalistMediaPlayer.conf with keepDelete=yes';
mmpShowOKCancelMsgDlg(vMsg, TMsgDlgType.mtInformation, [mbOK]);
EXIT; end;end;
var vMsg := 'KEEP/DELETE '#13#10#13#10'Folder: ' + aFolderPath + '*.*';
vMsg := vMsg + #13#10#13#10'WARNING: This will delete every file in the folder'#13#10;
vMsg := vMsg + 'that doesn''t start with an exclamation point !';
case CF.asDeleteMethod[CONF_DELETE_METHOD] in [dmStandard, dmShred] of TRUE: vMsg := vMsg + #13#10#13#10'Only click OK if you are ABSOLUTELY SURE'; end;
case CF.asDeleteMethod[CONF_DELETE_METHOD] of dmShred: vMsg := vMsg + #13#10'Shred: Once they''re gone, they are GONE!'; end;
case mmpShowOkCancelMsgDlg(vMsg) = IDOK of TRUE:;
FALSE: EXIT; end;
case findFirst(aFolderPath + '*.*', faFilesOnly, vSR) = 0 of TRUE:
repeat
case fileOK of TRUE: mmpShredThis(aFolderPath + vSR.Name, CF.asDeleteMethod[CONF_DELETE_METHOD]); end;
until findNext(vSR) <> 0;
end;
system.sysUtils.findClose(vSR);
result := TRUE;
end;
function mmpRenameFile(const aFilePath: string; const aNewFileNamePart: string = ''): string;
// the user gets to edit the filename part without the path and the extension
var
vOldFileNamePart: string;
vExt: string;
s: string;
vNewFilePath: string;
begin
result := aFilePath; // indicates failure
try
vOldFileNamePart := extractFileName(aFilePath);
vExt := extractFileExt(vOldFileNamePart);
vOldFileNamePart := mmpFileNameWithoutExtension(vOldFileNamePart);
case aNewFileNamePart <> '' of TRUE: s := aNewFileNamePart;
FALSE: begin
try
s := mmpInputBoxForm(vOldFileNamePart); // the form returns the edited filename or the original if the user pressed cancel
finally
end;end;end;
except
s := ''; // any funny business, force the rename to be abandoned
end;
case (s = '') OR (s = vOldFileNamePart) of TRUE: EXIT; end; // nothing to do
vNewFilePath := extractFilePath(aFilePath) + s + vExt; // construct the full path and new filename with the original extension
case system.sysUtils.renameFile(aFilePath, vNewFilePath) of TRUE: result := vNewFilePath;
FALSE: mmpShowOKCancelMsgDlg('Rename failed:' + #13#10 + sysErrorMessage(getlasterror), mtError, [mbOK]); end;
end;
function mmpRunTasks: boolean;
begin
result := FALSE;
mmpStartTasks;
result := TRUE;
end;
end.