new keyboard navigation method now is default
[far2l.git] / far2l / src / setattr.cpp
blobb1620731eba4cda02f9534f5e5201c0b314063b3
1 /*
2 setattr.cpp
4 Установка атрибутов файлов
5 */
6 /*
7 Copyright (c) 1996 Eugene Roshal
8 Copyright (c) 2000 Far Group
9 All rights reserved.
11 Redistribution and use in source and binary forms, with or without
12 modification, are permitted provided that the following conditions
13 are met:
14 1. Redistributions of source code must retain the above copyright
15 notice, this list of conditions and the following disclaimer.
16 2. Redistributions in binary form must reproduce the above copyright
17 notice, this list of conditions and the following disclaimer in the
18 documentation and/or other materials provided with the distribution.
19 3. The name of the authors may not be used to endorse or promote products
20 derived from this software without specific prior written permission.
22 THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
23 IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
24 OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
25 IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
26 INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
27 NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
31 THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34 #include "headers.hpp"
36 #include <set> // for std::set
37 #include <pwd.h> // for getpwent()
38 #include <grp.h> // for getgrent()
40 #include "lang.hpp"
41 #include "dialog.hpp"
42 #include "chgprior.hpp"
43 #include "scantree.hpp"
44 #include "filepanels.hpp"
45 #include "panel.hpp"
46 #include "ctrlobj.hpp"
47 #include "constitle.hpp"
48 #include "TPreRedrawFunc.hpp"
49 #include "keyboard.hpp"
50 #include "message.hpp"
51 #include "config.hpp"
52 #include "datetime.hpp"
53 #include "fileattr.hpp"
54 #include "setattr.hpp"
55 #include "pathmix.hpp"
56 #include "strmix.hpp"
57 #include "fileowner.hpp"
58 #include "wakeful.hpp"
59 #include "DlgGuid.hpp"
60 #include "execute.hpp"
61 #include "FSFileFlags.h"
63 #include "interf.hpp" // for BoxSymbols
65 struct FSFileFlagsSafe : FSFileFlags
67 FSFileFlagsSafe(const FARString &path, DWORD attrs)
68 : FSFileFlags((attrs & FILE_ATTRIBUTE_DEVICE) ? DEVNULL : path.GetMB())
69 { }
72 enum SETATTRDLG
74 SA_DOUBLEBOX,
75 SA_TEXT_LABEL,
76 SA_TEXT_NAME,
77 SA_SEPARATOR1,
78 SA_TXTBTN_INFO,
79 SA_EDIT_INFO,
80 SA_SEPARATOR_OWNERSHIP,
81 SA_TEXT_OWNER,
82 SA_COMBO_OWNER,
83 SA_TEXT_GROUP,
84 SA_COMBO_GROUP,
86 SA_SEPARATOR_MODE,
87 SA_SEPARATOR_MODE_V1,
88 SA_SEPARATOR_MODE_V2,
89 SA_TEXT_MODE_USER,
90 SA_CHECKBOX_USER_READ,
91 SA_CHECKBOX_USER_WRITE,
92 SA_CHECKBOX_USER_EXECUTE,
93 SA_TEXT_MODE_GROUP,
94 SA_CHECKBOX_GROUP_READ,
95 SA_CHECKBOX_GROUP_WRITE,
96 SA_CHECKBOX_GROUP_EXECUTE,
97 SA_TEXT_MODE_OTHER,
98 SA_CHECKBOX_OTHER_READ,
99 SA_CHECKBOX_OTHER_WRITE,
100 SA_CHECKBOX_OTHER_EXECUTE,
101 SA_CHECKBOX_SUID,
102 SA_CHECKBOX_SGID,
103 SA_CHECKBOX_STICKY,
105 SA_TEXT_MODE_OCTAL,
106 SA_FIXEDIT_MODE_OCTAL,
107 SA_BUTTON_MODE_ORIGINAL,
109 SA_SEPARATOR_ATTRIBUTES,
110 SA_CHECKBOX_IMMUTABLE,
111 SA_CHECKBOX_APPEND,
112 #if defined(__APPLE__) || defined(__FreeBSD__) || defined(__DragonFly__)
113 SA_CHECKBOX_HIDDEN,
114 #endif
116 SA_SEPARATOR3,
117 SA_TEXT_TITLEDATE,
118 SA_TEXT_LAST_ACCESS,
119 SA_FIXEDIT_LAST_ACCESS_DATE,
120 SA_FIXEDIT_LAST_ACCESS_TIME,
121 SA_TEXT_LAST_MODIFICATION,
122 SA_FIXEDIT_LAST_MODIFICATION_DATE,
123 SA_FIXEDIT_LAST_MODIFICATION_TIME,
124 SA_TEXT_LAST_CHANGE,
125 SA_FIXEDIT_LAST_CHANGE_DATE,
126 SA_FIXEDIT_LAST_CHANGE_TIME,
127 SA_BUTTON_ORIGINAL,
128 SA_BUTTON_CURRENT,
129 SA_BUTTON_BLANK,
130 SA_SEPARATOR4,
131 SA_CHECKBOX_SUBFOLDERS,
132 SA_SEPARATOR5,
133 SA_BUTTON_SET,
134 SA_BUTTON_CANCEL
137 static const struct MODEPAIR
139 SETATTRDLG Item;
140 mode_t Mode;
141 } AP[] = {
142 {SA_CHECKBOX_USER_READ, S_IRUSR},
143 {SA_CHECKBOX_USER_WRITE, S_IWUSR},
144 {SA_CHECKBOX_USER_EXECUTE, S_IXUSR},
145 {SA_CHECKBOX_GROUP_READ, S_IRGRP},
146 {SA_CHECKBOX_GROUP_WRITE, S_IWGRP},
147 {SA_CHECKBOX_GROUP_EXECUTE, S_IXGRP},
148 {SA_CHECKBOX_OTHER_READ, S_IROTH},
149 {SA_CHECKBOX_OTHER_WRITE, S_IWOTH},
150 {SA_CHECKBOX_OTHER_EXECUTE, S_IXOTH},
151 {SA_CHECKBOX_SUID, S_ISUID},
152 {SA_CHECKBOX_SGID, S_ISGID},
153 {SA_CHECKBOX_STICKY, S_ISVTX}
156 #define EDITABLE_MODES \
157 (S_IXOTH | S_IWOTH | S_IROTH | S_IXGRP | S_IWGRP | S_IRGRP | S_IXUSR | S_IWUSR | S_IRUSR | S_ISUID \
158 | S_ISGID | S_ISVTX)
160 static const int PreserveOriginalIDs[] = {
161 SA_CHECKBOX_USER_READ, SA_CHECKBOX_USER_WRITE, SA_CHECKBOX_USER_EXECUTE,
162 SA_CHECKBOX_GROUP_READ, SA_CHECKBOX_GROUP_WRITE, SA_CHECKBOX_GROUP_EXECUTE,
163 SA_CHECKBOX_OTHER_READ, SA_CHECKBOX_OTHER_WRITE, SA_CHECKBOX_OTHER_EXECUTE,
164 SA_CHECKBOX_SUID, SA_CHECKBOX_SGID, SA_CHECKBOX_STICKY,
165 SA_CHECKBOX_IMMUTABLE, SA_CHECKBOX_APPEND,
166 #if defined(__APPLE__) || defined(__FreeBSD__) || defined(__DragonFly__)
167 SA_CHECKBOX_HIDDEN,
168 #endif
171 enum DIALOGMODE
173 MODE_FILE,
174 MODE_FOLDER,
175 MODE_MULTIPLE,
178 struct SetAttrDlgParam
180 bool Plugin = false;
181 DWORD FileSystemFlags = 0;
182 DIALOGMODE DialogMode;
183 FARString strSelName;
184 FARString strOwner;
185 FARString strGroup;
186 bool OwnerChanged = false, GroupChanged = false;
187 // значения CheckBox`ов на момент старта диалога
188 int OriginalCBAttr[ARRAYSIZE(PreserveOriginalIDs)]; // values at dialog start and any user change for restore by subfolders checkbox
189 int OriginalCBAttr0[ARRAYSIZE(PreserveOriginalIDs)]; // values at dialog start
190 int OriginalCBAttr2[ARRAYSIZE(PreserveOriginalIDs)]; // -1 = original, other = was change
191 DWORD OriginalCBFlag[ARRAYSIZE(PreserveOriginalIDs)]; // checkbox flags at dialog start
192 bool _b_mode_check_or_edit_process = false;
193 FARCHECKEDSTATE OSubfoldersState;
194 bool OAccessTime, OModifyTime, OStatusChangeTime;
195 unsigned char SymLinkInfoCycle = 0;
196 FARString SymLink;
197 FARString SymlinkButtonTitles[3];
200 #define DM_SETATTR (DM_USER + 1)
202 static int DialogID2PreservedOriginalIndex(int id)
204 for (size_t i = 0; i < ARRAYSIZE(PreserveOriginalIDs); ++i) {
205 if (PreserveOriginalIDs[i] == id)
206 return i;
208 return -1;
211 static void BlankEditIfChanged(HANDLE hDlg, int EditControl, FARString &Remembered, bool &Changed)
213 LPCWSTR Actual = reinterpret_cast<LPCWSTR>(SendDlgMessage(hDlg, DM_GETCONSTTEXTPTR, EditControl, 0));
214 if (!Changed)
215 Changed = StrCmp(Actual, Remembered) != 0;
217 Remembered = Actual;
219 if (!Changed)
220 SendDlgMessage(hDlg, DM_SETTEXTPTR, EditControl, reinterpret_cast<LONG_PTR>(L""));
223 static std::wstring BriefInfo(const FARString &strSelName)
225 std::vector<std::wstring> lines;
227 std::string cmd = "file -- \"";
228 cmd+= EscapeCmdStr(Wide2MB(strSelName.CPtr()));
229 cmd+= '\"';
231 std::wstring out;
233 if (POpen(lines, cmd.c_str())) {
234 for (const auto &line : lines) {
235 out = line;
236 size_t p = out.find(':');
237 if (p != std::string::npos) {
238 out.erase(0, p + 1);
240 StrTrim(out);
241 if (p != std::string::npos && !out.empty()) {
242 break;
246 return out;
249 static char SetAttrGetBitCharFromModeCheckBoxes(HANDLE hDlg, int _i1, int _i2, int _i3)
251 int i1, i2, i3;
253 i1 = (int)SendDlgMessage(hDlg, DM_GETCHECK, _i1, 0);
254 i2 = (int)SendDlgMessage(hDlg, DM_GETCHECK, _i2, 0);
255 i3 = (int)SendDlgMessage(hDlg, DM_GETCHECK, _i3, 0);
256 if (i1 == BSTATE_3STATE || i2 == BSTATE_3STATE || i3 == BSTATE_3STATE)
257 return '-';
258 else {
259 int i = (i1 == BSTATE_CHECKED ? 1 : 0) + (i2 == BSTATE_CHECKED ? 2 : 0) + (i3 == BSTATE_CHECKED ? 4 : 0);
260 char buffer[3] = {0};
261 snprintf(buffer, 2, "%o", i);
262 return buffer[0];
266 static void SetAttrCalcBitsCharFromModeCheckBoxes(HANDLE hDlg)
268 wchar_t str_octal[5] = {0};
269 str_octal[0] = SetAttrGetBitCharFromModeCheckBoxes(hDlg, SA_CHECKBOX_STICKY, SA_CHECKBOX_SGID, SA_CHECKBOX_SUID);
270 str_octal[1] = SetAttrGetBitCharFromModeCheckBoxes(hDlg, SA_CHECKBOX_USER_EXECUTE, SA_CHECKBOX_USER_WRITE, SA_CHECKBOX_USER_READ);
271 str_octal[2] = SetAttrGetBitCharFromModeCheckBoxes(hDlg, SA_CHECKBOX_GROUP_EXECUTE, SA_CHECKBOX_GROUP_WRITE, SA_CHECKBOX_GROUP_READ);
272 str_octal[3] = SetAttrGetBitCharFromModeCheckBoxes(hDlg, SA_CHECKBOX_OTHER_EXECUTE, SA_CHECKBOX_OTHER_WRITE, SA_CHECKBOX_OTHER_READ);
274 SendDlgMessage(hDlg, DM_SETTEXTPTR, SA_FIXEDIT_MODE_OCTAL, (LONG_PTR)str_octal);
277 void SetAttrGetModeCheckBoxesFromChar(HANDLE hDlg, wchar_t c, int _i1, int _i2, int _i3)
279 switch(c) {
280 case L'0':
281 SendDlgMessage(hDlg, DM_SETCHECK, _i1, BSTATE_UNCHECKED);
282 SendDlgMessage(hDlg, DM_SETCHECK, _i2, BSTATE_UNCHECKED);
283 SendDlgMessage(hDlg, DM_SETCHECK, _i3, BSTATE_UNCHECKED);
284 break;
285 case L'1':
286 SendDlgMessage(hDlg, DM_SETCHECK, _i1, BSTATE_CHECKED);
287 SendDlgMessage(hDlg, DM_SETCHECK, _i2, BSTATE_UNCHECKED);
288 SendDlgMessage(hDlg, DM_SETCHECK, _i3, BSTATE_UNCHECKED);
289 break;
290 case L'2':
291 SendDlgMessage(hDlg, DM_SETCHECK, _i1, BSTATE_UNCHECKED);
292 SendDlgMessage(hDlg, DM_SETCHECK, _i2, BSTATE_CHECKED);
293 SendDlgMessage(hDlg, DM_SETCHECK, _i3, BSTATE_UNCHECKED);
294 break;
295 case L'3':
296 SendDlgMessage(hDlg, DM_SETCHECK, _i1, BSTATE_CHECKED);
297 SendDlgMessage(hDlg, DM_SETCHECK, _i2, BSTATE_CHECKED);
298 SendDlgMessage(hDlg, DM_SETCHECK, _i3, BSTATE_UNCHECKED);
299 break;
300 case L'4':
301 SendDlgMessage(hDlg, DM_SETCHECK, _i1, BSTATE_UNCHECKED);
302 SendDlgMessage(hDlg, DM_SETCHECK, _i2, BSTATE_UNCHECKED);
303 SendDlgMessage(hDlg, DM_SETCHECK, _i3, BSTATE_CHECKED);
304 break;
305 case L'5':
306 SendDlgMessage(hDlg, DM_SETCHECK, _i1, BSTATE_CHECKED);
307 SendDlgMessage(hDlg, DM_SETCHECK, _i2, BSTATE_UNCHECKED);
308 SendDlgMessage(hDlg, DM_SETCHECK, _i3, BSTATE_CHECKED);
309 break;
310 case L'6':
311 SendDlgMessage(hDlg, DM_SETCHECK, _i1, BSTATE_UNCHECKED);
312 SendDlgMessage(hDlg, DM_SETCHECK, _i2, BSTATE_CHECKED);
313 SendDlgMessage(hDlg, DM_SETCHECK, _i3, BSTATE_CHECKED);
314 break;
315 case L'7':
316 SendDlgMessage(hDlg, DM_SETCHECK, _i1, BSTATE_CHECKED);
317 SendDlgMessage(hDlg, DM_SETCHECK, _i2, BSTATE_CHECKED);
318 SendDlgMessage(hDlg, DM_SETCHECK, _i3, BSTATE_CHECKED);
319 break;
320 default:
321 SendDlgMessage(hDlg, DM_SETCHECK, _i1, BSTATE_3STATE);
322 SendDlgMessage(hDlg, DM_SETCHECK, _i2, BSTATE_3STATE);
323 SendDlgMessage(hDlg, DM_SETCHECK, _i3, BSTATE_3STATE);
324 break;
328 LONG_PTR WINAPI SetAttrDlgProc(HANDLE hDlg, int Msg, int Param1, LONG_PTR Param2)
330 SetAttrDlgParam *DlgParam =
331 reinterpret_cast<SetAttrDlgParam *>(SendDlgMessage(hDlg, DM_GETDLGDATA, 0, 0));
332 int OrigIdx;
334 switch (Msg) {
335 case DN_CLOSE:
336 if (DlgParam->SymLinkInfoCycle == 0) {
337 DlgParam->SymLink = reinterpret_cast<LPCWSTR>
338 (SendDlgMessage(hDlg, DM_GETCONSTTEXTPTR, SA_EDIT_INFO, 0));
340 break;
342 case DN_BTNCLICK:
343 OrigIdx = DialogID2PreservedOriginalIndex(Param1);
344 if (OrigIdx != -1 || Param1 == SA_CHECKBOX_SUBFOLDERS) {
345 if (OrigIdx != -1) {
346 DlgParam->OriginalCBAttr[OrigIdx] = static_cast<int>(Param2);
347 DlgParam->OriginalCBAttr2[OrigIdx] = 0;
348 if( !DlgParam->_b_mode_check_or_edit_process) {
349 DlgParam->_b_mode_check_or_edit_process = true;
350 SetAttrCalcBitsCharFromModeCheckBoxes(hDlg);
351 DlgParam->_b_mode_check_or_edit_process = false;
354 int FocusPos = static_cast<int>(SendDlgMessage(hDlg, DM_GETFOCUS, 0, 0));
355 FARCHECKEDSTATE SubfoldersState = static_cast<FARCHECKEDSTATE>(
356 SendDlgMessage(hDlg, DM_GETCHECK, SA_CHECKBOX_SUBFOLDERS, 0));
359 DlgParam->_b_mode_check_or_edit_process = true;
360 // если снимаем атрибуты для SubFolders
361 // этот кусок всегда работает если есть хотя бы одна папка
362 // иначе SA_CHECKBOX_SUBFOLDERS недоступен и всегда снят.
363 if (FocusPos == SA_CHECKBOX_SUBFOLDERS) {
364 if (DlgParam->DialogMode == MODE_FOLDER) // каталог однозначно!
366 if (DlgParam->OSubfoldersState != SubfoldersState) // Состояние изменилось?
368 // установили?
369 if (SubfoldersState != BSTATE_UNCHECKED) {
370 for (size_t i = 0; i < ARRAYSIZE(PreserveOriginalIDs); ++i) {
371 SendDlgMessage(hDlg, DM_SET3STATE, PreserveOriginalIDs[i], TRUE);
372 if (DlgParam->OriginalCBAttr2[i] == -1) {
373 SendDlgMessage(hDlg, DM_SETCHECK, PreserveOriginalIDs[i],
374 BSTATE_3STATE);
378 BlankEditIfChanged(hDlg, SA_COMBO_OWNER, DlgParam->strOwner,
379 DlgParam->OwnerChanged);
380 BlankEditIfChanged(hDlg, SA_COMBO_GROUP, DlgParam->strGroup,
381 DlgParam->GroupChanged);
383 // сняли?
384 else {
385 for (size_t i = 0; i < ARRAYSIZE(PreserveOriginalIDs); ++i) {
386 SendDlgMessage(hDlg, DM_SET3STATE, PreserveOriginalIDs[i], FALSE);
387 SendDlgMessage(hDlg, DM_SETCHECK, PreserveOriginalIDs[i],
388 DlgParam->OriginalCBAttr[i]);
390 SendDlgMessage(hDlg, DM_SETTEXTPTR, SA_COMBO_OWNER,
391 reinterpret_cast<LONG_PTR>(DlgParam->strOwner.CPtr()));
392 SendDlgMessage(hDlg, DM_SETTEXTPTR, SA_COMBO_GROUP,
393 reinterpret_cast<LONG_PTR>(DlgParam->strGroup.CPtr()));
396 if (Opt.SetAttrFolderRules) {
397 FAR_FIND_DATA_EX FindData;
399 if (apiGetFindDataEx(DlgParam->strSelName, FindData)) {
400 const SETATTRDLG Items[] = {SA_TEXT_LAST_ACCESS,
401 SA_TEXT_LAST_MODIFICATION, SA_TEXT_LAST_CHANGE};
402 bool *ParamTimes[] = {&DlgParam->OAccessTime, &DlgParam->OModifyTime,
403 &DlgParam->OStatusChangeTime};
404 const PFILETIME FDTimes[] = {&FindData.ftUnixAccessTime,
405 &FindData.ftUnixModificationTime,
406 &FindData.ftUnixStatusChangeTime};
408 for (size_t i = 0; i < ARRAYSIZE(Items); i++) {
409 if (!*ParamTimes[i]) {
410 SendDlgMessage(hDlg, DM_SETATTR, Items[i],
411 (SubfoldersState != BSTATE_UNCHECKED)
413 : (LONG_PTR)FDTimes[i]);
414 *ParamTimes[i] = false;
421 // много объектов
422 else {
423 // Состояние изменилось?
424 if (DlgParam->OSubfoldersState != SubfoldersState) {
425 // установили?
426 if (SubfoldersState != BSTATE_UNCHECKED) {
427 for (size_t i = 0; i < ARRAYSIZE(PreserveOriginalIDs); ++i) {
428 if (DlgParam->OriginalCBAttr2[i] == -1) {
429 SendDlgMessage(hDlg, DM_SET3STATE, PreserveOriginalIDs[i], TRUE);
430 SendDlgMessage(hDlg, DM_SETCHECK, PreserveOriginalIDs[i],
431 BSTATE_3STATE);
434 SendDlgMessage(hDlg, DM_SETTEXTPTR, SA_COMBO_OWNER,
435 reinterpret_cast<LONG_PTR>(L""));
436 SendDlgMessage(hDlg, DM_SETTEXTPTR, SA_COMBO_GROUP,
437 reinterpret_cast<LONG_PTR>(L""));
439 // сняли?
440 else {
441 for (size_t i = 0; i < ARRAYSIZE(PreserveOriginalIDs); ++i) {
442 SendDlgMessage(hDlg, DM_SET3STATE, PreserveOriginalIDs[i],
443 ((DlgParam->OriginalCBFlag[i] & DIF_3STATE) ? TRUE : FALSE));
444 SendDlgMessage(hDlg, DM_SETCHECK, PreserveOriginalIDs[i],
445 DlgParam->OriginalCBAttr[i]);
447 SendDlgMessage(hDlg, DM_SETTEXTPTR, SA_COMBO_OWNER,
448 reinterpret_cast<LONG_PTR>(DlgParam->strOwner.CPtr()));
449 SendDlgMessage(hDlg, DM_SETTEXTPTR, SA_COMBO_GROUP,
450 reinterpret_cast<LONG_PTR>(DlgParam->strGroup.CPtr()));
455 DlgParam->OSubfoldersState = SubfoldersState;
457 SetAttrCalcBitsCharFromModeCheckBoxes(hDlg);
458 DlgParam->_b_mode_check_or_edit_process = false;
461 return TRUE;
463 else if (Param1 == SA_TXTBTN_INFO) {
464 FARString strText;
465 SendDlgMessage(hDlg, DM_SETTEXTPTR, SA_TXTBTN_INFO,
466 reinterpret_cast<LONG_PTR>(DlgParam->SymlinkButtonTitles[DlgParam->SymLinkInfoCycle].CPtr()));
468 switch (DlgParam->SymLinkInfoCycle++) {
469 case 0: {
470 DlgParam->SymLink = reinterpret_cast<LPCWSTR>
471 (SendDlgMessage(hDlg, DM_GETCONSTTEXTPTR, SA_EDIT_INFO, 0));
472 ConvertNameToReal(DlgParam->SymLink, strText);
473 SendDlgMessage(hDlg, DM_SETREADONLY, SA_EDIT_INFO, 1);
474 } break;
476 case 1:
477 ConvertNameToReal(DlgParam->SymLink, strText);
478 strText = BriefInfo(strText);
479 SendDlgMessage(hDlg, DM_SETREADONLY, SA_EDIT_INFO, 1);
480 break;
482 default:
483 strText = DlgParam->SymLink;
484 SendDlgMessage(hDlg, DM_SETREADONLY, SA_EDIT_INFO, 0);
485 DlgParam->SymLinkInfoCycle = 0;
487 SendDlgMessage(hDlg, DM_SETTEXTPTR, SA_EDIT_INFO, reinterpret_cast<LONG_PTR>(strText.CPtr()));
488 return TRUE;
490 // Set Original for Modes and Attributes
491 } else if (Param1 == SA_BUTTON_MODE_ORIGINAL) {
492 DlgParam->_b_mode_check_or_edit_process = true;
493 for (size_t i = 0; i < ARRAYSIZE(PreserveOriginalIDs); ++i) {
494 DlgParam->OriginalCBAttr2[i] = -1;
495 SendDlgMessage(hDlg, DM_SET3STATE, PreserveOriginalIDs[i],
496 ((DlgParam->OriginalCBFlag[i] & DIF_3STATE) ? TRUE : FALSE));
497 SendDlgMessage(hDlg, DM_SETCHECK, PreserveOriginalIDs[i],
498 DlgParam->OriginalCBAttr0[i]);
500 SetAttrCalcBitsCharFromModeCheckBoxes(hDlg);
501 DlgParam->_b_mode_check_or_edit_process = false;
502 SendDlgMessage(hDlg, DM_SETFOCUS, SA_FIXEDIT_MODE_OCTAL, 0);
504 // Set Original? / Set All? / Clear All?
505 } else if (Param1 == SA_BUTTON_ORIGINAL) {
506 FAR_FIND_DATA_EX FindData;
508 if (apiGetFindDataEx(DlgParam->strSelName, FindData)) {
509 SendDlgMessage(hDlg, DM_SETATTR, SA_TEXT_LAST_ACCESS,
510 (LONG_PTR)&FindData.ftUnixAccessTime);
511 SendDlgMessage(hDlg, DM_SETATTR, SA_TEXT_LAST_MODIFICATION,
512 (LONG_PTR)&FindData.ftUnixModificationTime);
513 SendDlgMessage(hDlg, DM_SETATTR, SA_TEXT_LAST_CHANGE,
514 (LONG_PTR)&FindData.ftUnixStatusChangeTime);
515 DlgParam->OAccessTime = DlgParam->OModifyTime = DlgParam->OStatusChangeTime = false;
518 SendDlgMessage(hDlg, DM_SETFOCUS, SA_FIXEDIT_LAST_ACCESS_DATE, 0);
519 return TRUE;
520 } else if (Param1 == SA_BUTTON_CURRENT || Param1 == SA_BUTTON_BLANK) {
521 LONG_PTR Value = 0;
522 FILETIME CurrentTime;
523 if (Param1 == SA_BUTTON_CURRENT) {
524 WINPORT(GetSystemTimeAsFileTime)(&CurrentTime);
525 Value = reinterpret_cast<LONG_PTR>(&CurrentTime);
527 SendDlgMessage(hDlg, DM_SETATTR, SA_TEXT_LAST_ACCESS, Value);
528 SendDlgMessage(hDlg, DM_SETATTR, SA_TEXT_LAST_MODIFICATION, Value);
529 // SendDlgMessage(hDlg, DM_SETATTR, SA_TEXT_LAST_CHANGE, Value);
530 DlgParam->OAccessTime = DlgParam->OModifyTime = DlgParam->OStatusChangeTime = true;
531 SendDlgMessage(hDlg, DM_SETFOCUS, SA_FIXEDIT_LAST_ACCESS_DATE, 0);
532 return TRUE;
535 break;
536 case DN_MOUSECLICK: {
537 //_SVS(SysLog(L"Msg=DN_MOUSECLICK Param1=%d Param2=%d",Param1,Param2));
538 if (Param1 >= SA_TEXT_LAST_ACCESS && Param1 <= SA_FIXEDIT_LAST_CHANGE_TIME) {
539 if (reinterpret_cast<MOUSE_EVENT_RECORD *>(Param2)->dwEventFlags == DOUBLE_CLICK) {
540 // Дадим Менеджеру диалогов "попотеть"
541 DefDlgProc(hDlg, Msg, Param1, Param2);
542 SendDlgMessage(hDlg, DM_SETATTR, Param1, -1);
545 if (Param1 == SA_TEXT_LAST_ACCESS || Param1 == SA_TEXT_LAST_MODIFICATION
546 || Param1 == SA_TEXT_LAST_CHANGE) {
547 Param1++;
550 SendDlgMessage(hDlg, DM_SETFOCUS, Param1, 0);
552 } break;
553 case DN_EDITCHANGE: {
554 switch (Param1) {
555 case SA_FIXEDIT_MODE_OCTAL:
556 if (!DlgParam->_b_mode_check_or_edit_process) {
557 DlgParam->_b_mode_check_or_edit_process = true;
558 std::wstring str_octal;
559 int length = (int)SendDlgMessage(hDlg, DM_GETTEXTLENGTH, SA_FIXEDIT_MODE_OCTAL, 0);
560 str_octal.resize(length+1);
561 LONG_PTR rv = SendDlgMessage(hDlg, DM_GETTEXTPTR, SA_FIXEDIT_MODE_OCTAL, (LONG_PTR)&str_octal[0]);
562 if (rv>0) {
563 length = str_octal.length();
564 SetAttrGetModeCheckBoxesFromChar(hDlg, length<1 ? ' ' : str_octal[0], SA_CHECKBOX_STICKY, SA_CHECKBOX_SGID, SA_CHECKBOX_SUID);
565 SetAttrGetModeCheckBoxesFromChar(hDlg, length<2 ? ' ' : str_octal[1], SA_CHECKBOX_USER_EXECUTE, SA_CHECKBOX_USER_WRITE, SA_CHECKBOX_USER_READ);
566 SetAttrGetModeCheckBoxesFromChar(hDlg, length<3 ? ' ' : str_octal[2], SA_CHECKBOX_GROUP_EXECUTE, SA_CHECKBOX_GROUP_WRITE, SA_CHECKBOX_GROUP_READ);
567 SetAttrGetModeCheckBoxesFromChar(hDlg, length<4 ? ' ' : str_octal[3], SA_CHECKBOX_OTHER_EXECUTE, SA_CHECKBOX_OTHER_WRITE, SA_CHECKBOX_OTHER_READ);
569 DlgParam->_b_mode_check_or_edit_process = false;
571 break;
572 case SA_FIXEDIT_LAST_ACCESS_DATE:
573 case SA_FIXEDIT_LAST_ACCESS_TIME:
574 DlgParam->OAccessTime = true;
575 break;
576 case SA_FIXEDIT_LAST_MODIFICATION_DATE:
577 case SA_FIXEDIT_LAST_MODIFICATION_TIME:
578 DlgParam->OModifyTime = true;
579 break;
580 case SA_FIXEDIT_LAST_CHANGE_DATE:
581 case SA_FIXEDIT_LAST_CHANGE_TIME:
582 DlgParam->OStatusChangeTime = true;
583 break;
586 break;
589 case DN_GOTFOCUS: {
590 if (Param1 == SA_FIXEDIT_LAST_ACCESS_DATE || Param1 == SA_FIXEDIT_LAST_MODIFICATION_DATE
591 || Param1 == SA_FIXEDIT_LAST_CHANGE_DATE) {
592 if (GetDateFormat() == 2) {
593 if (reinterpret_cast<LPCWSTR>(SendDlgMessage(hDlg, DM_GETCONSTTEXTPTR, Param1, 0))[0]
594 == L' ') {
595 COORD Pos;
596 SendDlgMessage(hDlg, DM_GETCURSORPOS, Param1, (LONG_PTR)&Pos);
597 if (Pos.X == 0) {
598 Pos.X = 1;
599 SendDlgMessage(hDlg, DM_SETCURSORPOS, Param1, (LONG_PTR)&Pos);
604 } break;
606 case DM_SETATTR: {
607 FARString strDate, strTime;
609 if (Param2) // Set?
611 FILETIME ft;
613 if (Param2 == -1) {
614 WINPORT(GetSystemTimeAsFileTime)(&ft);
615 } else {
616 ft = *reinterpret_cast<PFILETIME>(Param2);
619 ConvertDate(ft, strDate, strTime, 12, FALSE, FALSE, 2, TRUE);
622 // Глянем на место, где был клик
623 int Set1 = -1;
624 int Set2 = Param1;
626 switch (Param1) {
627 case SA_TEXT_LAST_ACCESS:
628 Set1 = SA_FIXEDIT_LAST_ACCESS_DATE;
629 Set2 = SA_FIXEDIT_LAST_ACCESS_TIME;
630 DlgParam->OAccessTime = true;
631 break;
632 case SA_TEXT_LAST_MODIFICATION:
633 Set1 = SA_FIXEDIT_LAST_MODIFICATION_DATE;
634 Set2 = SA_FIXEDIT_LAST_MODIFICATION_TIME;
635 DlgParam->OModifyTime = true;
636 break;
637 case SA_TEXT_LAST_CHANGE:
638 Set1 = SA_FIXEDIT_LAST_CHANGE_DATE;
639 Set2 = SA_FIXEDIT_LAST_CHANGE_TIME;
640 DlgParam->OStatusChangeTime = true;
641 break;
643 case SA_FIXEDIT_LAST_ACCESS_DATE:
644 case SA_FIXEDIT_LAST_MODIFICATION_DATE:
645 case SA_FIXEDIT_LAST_CHANGE_DATE:
646 Set1 = Param1;
647 Set2 = -1;
648 break;
651 if (Set1 != -1) {
652 SendDlgMessage(hDlg, DM_SETTEXTPTR, Set1, (LONG_PTR)strDate.CPtr());
655 if (Set2 != -1) {
656 SendDlgMessage(hDlg, DM_SETTEXTPTR, Set2, (LONG_PTR)strTime.CPtr());
659 return TRUE;
663 return DefDlgProc(hDlg, Msg, Param1, Param2);
666 void ShellSetFileAttributesMsg(const wchar_t *Name)
668 static int Width = 54;
669 int WidthTemp;
671 if (Name && *Name)
672 WidthTemp = Max(StrLength(Name), 54);
673 else
674 Width = WidthTemp = 54;
676 WidthTemp = Min(WidthTemp, WidthNameForMessage);
677 Width = Max(Width, WidthTemp);
678 FARString strOutFileName = Name;
679 TruncPathStr(strOutFileName, Width);
680 CenterStr(strOutFileName, strOutFileName, Width + 4);
681 Message(0, 0, Msg::SetAttrTitle, Msg::SetAttrSetting, strOutFileName);
682 PreRedrawItem preRedrawItem = PreRedraw.Peek();
683 preRedrawItem.Param.Param1 = Name;
684 PreRedraw.SetParam(preRedrawItem.Param);
687 bool ReadFileTime(int Type, const wchar_t *Name, FILETIME &FileTime, const wchar_t *OSrcDate,
688 const wchar_t *OSrcTime)
690 bool Result = false;
691 FAR_FIND_DATA_EX ffd = {};
693 if (apiGetFindDataEx(Name, ffd)) {
694 LPFILETIME Times[] = {&ffd.ftLastWriteTime, &ffd.ftCreationTime, &ffd.ftLastAccessTime,
695 &ffd.ftChangeTime};
696 LPFILETIME OriginalFileTime = Times[Type];
697 FILETIME oft = {};
698 if (WINPORT(FileTimeToLocalFileTime)(OriginalFileTime, &oft)) {
699 SYSTEMTIME ost = {};
700 if (WINPORT(FileTimeToSystemTime)(&oft, &ost)) {
701 WORD DateN[3] = {};
702 GetFileDateAndTime(OSrcDate, DateN, ARRAYSIZE(DateN), GetDateSeparator());
703 WORD TimeN[4] = {};
704 GetFileDateAndTime(OSrcTime, TimeN, ARRAYSIZE(TimeN), GetTimeSeparator());
705 SYSTEMTIME st = {};
707 switch (GetDateFormat()) {
708 case 0:
709 st.wMonth = DateN[0] != (WORD)-1 ? DateN[0] : ost.wMonth;
710 st.wDay = DateN[1] != (WORD)-1 ? DateN[1] : ost.wDay;
711 st.wYear = DateN[2] != (WORD)-1 ? DateN[2] : ost.wYear;
712 break;
713 case 1:
714 st.wDay = DateN[0] != (WORD)-1 ? DateN[0] : ost.wDay;
715 st.wMonth = DateN[1] != (WORD)-1 ? DateN[1] : ost.wMonth;
716 st.wYear = DateN[2] != (WORD)-1 ? DateN[2] : ost.wYear;
717 break;
718 default:
719 st.wYear = DateN[0] != (WORD)-1 ? DateN[0] : ost.wYear;
720 st.wMonth = DateN[1] != (WORD)-1 ? DateN[1] : ost.wMonth;
721 st.wDay = DateN[2] != (WORD)-1 ? DateN[2] : ost.wDay;
722 break;
725 st.wHour = TimeN[0] != (WORD)-1 ? (TimeN[0]) : ost.wHour;
726 st.wMinute = TimeN[1] != (WORD)-1 ? (TimeN[1]) : ost.wMinute;
727 st.wSecond = TimeN[2] != (WORD)-1 ? (TimeN[2]) : ost.wSecond;
728 st.wMilliseconds = TimeN[3] != (WORD)-1 ? (TimeN[3]) : ost.wMilliseconds;
730 if (st.wYear < 100) {
731 st.wYear = static_cast<WORD>(ConvertYearToFull(st.wYear));
734 FILETIME lft = {};
735 if (WINPORT(SystemTimeToFileTime)(&st, &lft)) {
736 if (WINPORT(LocalFileTimeToFileTime)(&lft, &FileTime)) {
737 Result = WINPORT(CompareFileTime)(&FileTime, OriginalFileTime) != 0;
743 return Result;
746 void PR_ShellSetFileAttributesMsg()
748 PreRedrawItem preRedrawItem = PreRedraw.Peek();
749 ShellSetFileAttributesMsg(reinterpret_cast<const wchar_t *>(preRedrawItem.Param.Param1));
752 static void CheckFileOwnerGroup(DialogItemEx &ComboItem,
753 bool(WINAPI *GetFN)(const wchar_t *, const wchar_t *, FARString &),
754 FARString strComputerName,
755 FARString strSelName)
757 FARString strCur;
758 GetFN(strComputerName, strSelName, strCur);
759 if (ComboItem.strData.IsEmpty()) {
760 ComboItem.strData = strCur;
762 else if (ComboItem.strData != strCur) {
763 ComboItem.strData = Msg::SetAttrOwnerMultiple;
767 static bool ApplyFileOwnerGroupIfChanged(DialogItemEx &ComboItem,
768 int (*ESetFN)(LPCWSTR Name, LPCWSTR Owner, int SkipMode),
769 int &SkipMode, const FARString &strSelName, const FARString &strInit, bool force = false)
771 if (!ComboItem.strData.IsEmpty() && (force || StrCmp(strInit, ComboItem.strData))) {
772 int Result = ESetFN(strSelName, ComboItem.strData, SkipMode);
773 if (Result == SETATTR_RET_SKIPALL) {
774 SkipMode = SETATTR_RET_SKIP;
776 else if (Result == SETATTR_RET_ERROR) {
777 return false;
780 return true;
783 static void ApplyFSFileFlags(DialogItemEx *AttrDlg, const FARString &strSelName, DWORD FileAttr)
785 FSFileFlagsSafe FFFlags(strSelName.GetMB(), FileAttr);
786 if (AttrDlg[SA_CHECKBOX_IMMUTABLE].Selected == BSTATE_CHECKED
787 || AttrDlg[SA_CHECKBOX_IMMUTABLE].Selected == BSTATE_UNCHECKED) {
788 FFFlags.SetImmutable(AttrDlg[SA_CHECKBOX_IMMUTABLE].Selected != BSTATE_UNCHECKED);
790 if (AttrDlg[SA_CHECKBOX_APPEND].Selected == BSTATE_CHECKED
791 || AttrDlg[SA_CHECKBOX_APPEND].Selected == BSTATE_UNCHECKED) {
792 FFFlags.SetAppend(AttrDlg[SA_CHECKBOX_APPEND].Selected != BSTATE_UNCHECKED);
794 #if defined(__APPLE__) || defined(__FreeBSD__) || defined(__DragonFly__)
795 if (AttrDlg[SA_CHECKBOX_HIDDEN].Selected == BSTATE_CHECKED
796 || AttrDlg[SA_CHECKBOX_HIDDEN].Selected == BSTATE_UNCHECKED) {
797 FFFlags.SetHidden(AttrDlg[SA_CHECKBOX_HIDDEN].Selected != BSTATE_UNCHECKED);
799 #endif
800 FFFlags.Apply(strSelName.GetMB());
803 class ListPwGrEnt {
804 std::set<FARString> Set; // sorts and prevents duplicates
805 std::vector<FarListItem> Items;
806 FarList List;
807 void Append(const wchar_t *s) { Set.emplace(s); }
808 void Append(const char *s) { Set.emplace(s); }
809 public:
810 ListPwGrEnt(bool bGroups, int SelCount);
811 FarList *GetFarList() {
812 return &List;
816 ListPwGrEnt::ListPwGrEnt(bool bGroups, int SelCount)
818 if (SelCount >= 2)
819 Append(Msg::SetAttrOwnerMultiple);
821 if (!bGroups) { // usernames
822 struct passwd *pw;
823 setpwent();
824 while ((pw = getpwent()) != NULL) {
825 Append(pw->pw_name);
827 endpwent();
829 else { // groups
830 struct group *gr;
831 setgrent();
832 while ((gr = getgrent()) != NULL) {
833 Append(gr->gr_name);
835 endgrent();
838 Items.reserve(Set.size());
839 for (const auto &Str: Set) {
840 Items.emplace_back();
841 Items.back().Flags = 0;
842 Items.back().Text = Str.CPtr();
845 List.ItemsNumber = Items.size();
846 List.Items = Items.data();
849 bool ShellSetFileAttributes(Panel *SrcPanel, LPCWSTR Object)
851 std::vector<FARString> SelectedNames;
853 SudoClientRegion scr;
855 ChangePriority ChPriority(ChangePriority::NORMAL);
856 short DlgX = 70, DlgY = 25;
858 int SelCount = SrcPanel ? SrcPanel->GetSelCount() : 1;
860 if (!SelCount) {
861 return false;
864 const short ColX1Of3 = 5;
865 const short ColX2Of3 = ColX1Of3 + (DlgX - 3) / 3;
866 #if defined(__APPLE__) || defined(__FreeBSD__) || defined(__DragonFly__)
867 const short ColX3Of3 = ColX2Of3 + (DlgX - 3) / 3;
868 #endif
869 static const wchar_t VerticalLine[] = {BoxSymbols[BS_V1], BoxSymbols[BS_V1], BoxSymbols[BS_V1], 0};
871 DialogDataEx AttrDlgData[] = {
872 {DI_DOUBLEBOX, 3, 1, short(DlgX - 4), short(DlgY - 2), {}, 0, Msg::SetAttrTitle},
873 {DI_TEXT, -1, 2, 0, 2, {}, 0, Msg::SetAttrFor},
874 {DI_TEXT, -1, 3, 0, 3, {}, DIF_SHOWAMPERSAND, L""},
875 {DI_TEXT, 3, 4, 0, 4, {}, DIF_SEPARATOR, L""},
876 {DI_TEXT, 5, 5, 17, 5, {}, DIF_FOCUS, Msg::SetAttrBriefInfo}, // if symlink in will Button & need first focus here
877 {DI_EDIT, 18, 5, short(DlgX - 6), 5, {}, DIF_SELECTONENTRY | DIF_FOCUS | DIF_READONLY, L""}, // not readonly only if symlink
878 {DI_TEXT, 3, 6, 0, 6, {}, DIF_SEPARATOR, Msg::SetAttrOwnerTitle},
879 {DI_TEXT, 5, 7, 17, 7, {}, 0, Msg::SetAttrOwner},
880 //{DI_EDIT, 18, 6, short(DlgX - 6), 6, {}, 0, L""},
881 {DI_COMBOBOX, 18, 7, short(DlgX-6), 7, {}, DIF_DROPDOWNLIST|DIF_LISTNOAMPERSAND|DIF_LISTWRAPMODE,L""},
882 {DI_TEXT, 5, 8, 17, 8, {}, 0, Msg::SetAttrGroup},
883 //{DI_EDIT, 18, 7, short(DlgX - 6), 7, {}, 0, L""},
884 {DI_COMBOBOX, 18, 8, short(DlgX-6), 8, {}, DIF_DROPDOWNLIST|DIF_LISTNOAMPERSAND|DIF_LISTWRAPMODE,L""},
886 {DI_TEXT, 3, 9, 0, 9, {}, DIF_SEPARATOR, Msg::SetAttrModeTitle},
887 {DI_VTEXT, 39, 10, 39, 12, {}, DIF_BOXCOLOR, VerticalLine},
888 {DI_VTEXT, 51, 10, 51, 12, {}, DIF_BOXCOLOR, VerticalLine},
890 {DI_TEXT, 5, 10, 18, 10, {}, 0, Msg::SetAttrAccessUser},
891 {DI_CHECKBOX, 19, 10, 23, 10, {}, DIF_3STATE, Msg::SetAttrAccessUserRead},
892 {DI_CHECKBOX, 26, 10, 30, 10, {}, DIF_3STATE, Msg::SetAttrAccessUserWrite},
893 {DI_CHECKBOX, 33, 10, 37, 10, {}, DIF_3STATE, Msg::SetAttrAccessUserExecute},
894 {DI_TEXT, 5, 11, 18, 11, {}, 0, Msg::SetAttrAccessGroup},
895 {DI_CHECKBOX, 19, 11, 23, 11, {}, DIF_3STATE, Msg::SetAttrAccessGroupRead},
896 {DI_CHECKBOX, 26, 11, 30, 11, {}, DIF_3STATE, Msg::SetAttrAccessGroupWrite},
897 {DI_CHECKBOX, 33, 11, 37, 11, {}, DIF_3STATE, Msg::SetAttrAccessGroupExecute},
898 {DI_TEXT, 5, 12, 18, 12, {}, 0, Msg::SetAttrAccessOther},
899 {DI_CHECKBOX, 19, 12, 23, 12, {}, DIF_3STATE, Msg::SetAttrAccessOtherRead},
900 {DI_CHECKBOX, 26, 12, 30, 12, {}, DIF_3STATE, Msg::SetAttrAccessOtherWrite},
901 {DI_CHECKBOX, 33, 12, 37, 12, {}, DIF_3STATE, Msg::SetAttrAccessOtherExecute},
903 {DI_CHECKBOX, 40, 10, 52, 10, {}, DIF_3STATE, Msg::SetAttrSUID},
904 {DI_CHECKBOX, 40, 11, 52, 11, {}, DIF_3STATE, Msg::SetAttrSGID},
905 {DI_CHECKBOX, 40, 12, 52, 12, {}, DIF_3STATE, Msg::SetAttrSticky},
907 {DI_TEXT, 52, 10, 62, 10, {}, 0, L"O&ctal: SUGO"},
908 {DI_FIXEDIT, 59, 11, 62, 11, {(DWORD_PTR)L"####"}, DIF_MASKEDIT, L""},
909 {DI_BUTTON, 52, 12, 62, 12, {}, DIF_BTNNOCLOSE, Msg::SetAttrModeOriginal},
911 {DI_TEXT, 3, 13, 0, 13, {}, DIF_SEPARATOR, Msg::SetAttrAttributesTitle},
912 {DI_CHECKBOX, ColX1Of3, 14, 0, 14, {}, DIF_FOCUS | DIF_3STATE, Msg::SetAttrImmutable},
913 {DI_CHECKBOX, ColX2Of3, 14, 0, 14, {}, DIF_3STATE, Msg::SetAttrAppend},
914 #if defined(__APPLE__) || defined(__FreeBSD__) || defined(__DragonFly__)
915 {DI_CHECKBOX, ColX3Of3, 14, 0, 14, {}, DIF_3STATE, Msg::SetAttrHidden},
916 #endif
918 {DI_TEXT, 3, 15, 0, 15, {}, DIF_SEPARATOR, L""},
919 {DI_TEXT, short(DlgX - 29), 16, 0, 16, {}, 0, L""},
920 {DI_TEXT, 5, 17, 0, 17, {}, 0, Msg::SetAttrAccessTime},
921 {DI_FIXEDIT, short(DlgX - 29), 17, short(DlgX - 19), 17, {}, DIF_MASKEDIT, L""},
922 {DI_FIXEDIT, short(DlgX - 17), 17, short(DlgX - 6), 17, {}, DIF_MASKEDIT, L""},
923 {DI_TEXT, 5, 18, 0, 18, {}, 0, Msg::SetAttrModificationTime},
924 {DI_FIXEDIT, short(DlgX - 29), 18, short(DlgX - 19), 18, {}, DIF_MASKEDIT, L""},
925 {DI_FIXEDIT, short(DlgX - 17), 18, short(DlgX - 6), 18, {}, DIF_MASKEDIT, L""},
926 {DI_TEXT, 5, 19, 0, 19, {}, 0, Msg::SetAttrStatusChangeTime},
927 {DI_FIXEDIT, short(DlgX - 29), 19, short(DlgX - 19), 19, {}, DIF_MASKEDIT | DIF_READONLY, L""},
928 {DI_FIXEDIT, short(DlgX - 17), 19, short(DlgX - 6), 19, {}, DIF_MASKEDIT | DIF_READONLY, L""},
929 {DI_BUTTON, 0, 20, 0, 20, {}, DIF_CENTERGROUP | DIF_BTNNOCLOSE, Msg::SetAttrOriginal},
930 {DI_BUTTON, 0, 20, 0, 20, {}, DIF_CENTERGROUP | DIF_BTNNOCLOSE, Msg::SetAttrCurrent},
931 {DI_BUTTON, 0, 20, 0, 20, {}, DIF_CENTERGROUP | DIF_BTNNOCLOSE, Msg::SetAttrBlank},
932 {DI_TEXT, 3, 21, 0, 21, {}, DIF_SEPARATOR | DIF_HIDDEN, L""},
933 {DI_CHECKBOX, 5, 22, 0, 22, {}, DIF_DISABLE | DIF_HIDDEN, Msg::SetAttrSubfolders},
934 {DI_TEXT, 3, short(DlgY - 4), 0, short(DlgY - 4), {}, DIF_SEPARATOR, L""},
935 {DI_BUTTON, 0, short(DlgY - 3), 0, short(DlgY - 3), {}, DIF_DEFAULT | DIF_CENTERGROUP, Msg::SetAttrSet},
936 {DI_BUTTON, 0, short(DlgY - 3), 0, short(DlgY - 3), {}, DIF_CENTERGROUP, Msg::Cancel}
938 MakeDialogItemsEx(AttrDlgData, AttrDlg);
939 SetAttrDlgParam DlgParam{};
941 ListPwGrEnt Owners(false, SelCount);
942 ListPwGrEnt Groups(true, SelCount);
943 AttrDlg[SA_COMBO_OWNER].ListItems = Owners.GetFarList();
944 AttrDlg[SA_COMBO_GROUP].ListItems = Groups.GetFarList();
946 if (SrcPanel && SrcPanel->GetMode() == PLUGIN_PANEL) {
947 OpenPluginInfo Info;
948 HANDLE hPlugin = SrcPanel->GetPluginHandle();
950 if (hPlugin == INVALID_HANDLE_VALUE) {
951 return false;
954 CtrlObject->Plugins.GetOpenPluginInfo(hPlugin, &Info);
956 if (!(Info.Flags & OPIF_REALNAMES)) {
957 AttrDlg[SA_BUTTON_SET].Flags|= DIF_DISABLE;
958 //AttrDlg[SA_BUTTON_BRIEFINFO].Flags|= DIF_DISABLE;
959 DlgParam.Plugin = true;
964 DWORD FileAttr = INVALID_FILE_ATTRIBUTES, FileMode = 0;
965 FARString strSelName;
966 FAR_FIND_DATA_EX FindData;
967 if (SrcPanel) {
968 SrcPanel->GetSelName(nullptr, FileAttr, FileMode);
969 SrcPanel->GetSelName(&strSelName, FileAttr, FileMode, &FindData);
970 /* if (!FileMode) {
971 FAR_FIND_DATA_EX FindData2;
972 if (apiGetFindDataEx(strSelName, FindData2) && FindData2.dwUnixMode) {
973 FindData = FindData2;
974 FileAttr = FindData.dwFileAttributes;
975 FileMode = FindData.dwUnixMode;
978 } else {
979 strSelName = Object;
980 apiGetFindDataEx(Object, FindData);
981 FileAttr = FindData.dwFileAttributes;
982 FileMode = FindData.dwUnixMode;
985 // fprintf(stderr, "FileMode=%u\n", FileMode);
987 if (SelCount == 1 && TestParentFolderName(strSelName))
988 return false;
990 wchar_t DateSeparator = GetDateSeparator();
991 wchar_t TimeSeparator = GetTimeSeparator();
992 wchar_t DecimalSeparator = GetDecimalSeparator();
993 LPCWSTR FmtMask1 = L"99%c99%c99%c99N", FmtMask2 = L"99%c99%c9999N", FmtMask3 = L"N9999%c99%c99";
994 FARString strDMask, strTMask;
995 strTMask.Format(FmtMask1, TimeSeparator, TimeSeparator, DecimalSeparator);
997 switch (GetDateFormat()) {
998 case 0:
999 AttrDlg[SA_TEXT_TITLEDATE].strData.Format(Msg::SetAttrTimeTitle1, DateSeparator,
1000 DateSeparator, TimeSeparator, TimeSeparator, DecimalSeparator);
1001 strDMask.Format(FmtMask2, DateSeparator, DateSeparator);
1002 break;
1003 case 1:
1004 AttrDlg[SA_TEXT_TITLEDATE].strData.Format(Msg::SetAttrTimeTitle2, DateSeparator,
1005 DateSeparator, TimeSeparator, TimeSeparator, DecimalSeparator);
1006 strDMask.Format(FmtMask2, DateSeparator, DateSeparator);
1007 break;
1008 default:
1009 AttrDlg[SA_TEXT_TITLEDATE].strData.Format(Msg::SetAttrTimeTitle3, DateSeparator,
1010 DateSeparator, TimeSeparator, TimeSeparator, DecimalSeparator);
1011 strDMask.Format(FmtMask3, DateSeparator, DateSeparator);
1012 break;
1015 AttrDlg[SA_FIXEDIT_LAST_ACCESS_DATE].strMask = AttrDlg[SA_FIXEDIT_LAST_MODIFICATION_DATE].strMask =
1016 AttrDlg[SA_FIXEDIT_LAST_CHANGE_DATE].strMask = strDMask;
1018 AttrDlg[SA_FIXEDIT_LAST_ACCESS_TIME].strMask = AttrDlg[SA_FIXEDIT_LAST_MODIFICATION_TIME].strMask =
1019 AttrDlg[SA_FIXEDIT_LAST_CHANGE_TIME].strMask = strTMask;
1020 bool FolderPresent = false;//, LinkPresent = false;
1021 int FolderCount = 0, Link2FileCount = 0, Link2DirCount = 0;
1022 FARString strLinkName;
1024 if (SelCount == 1) {
1025 FSFileFlagsSafe FFFlags(strSelName.GetMB(), FileAttr);
1026 if (FileAttr & FILE_ATTRIBUTE_REPARSE_POINT) {
1027 DlgParam.SymlinkButtonTitles[0] = Msg::SetAttrSymlinkObject;
1028 DlgParam.SymlinkButtonTitles[1] = Msg::SetAttrSymlinkObjectInfo;
1029 DlgParam.SymlinkButtonTitles[2] = Msg::SetAttrSymlinkContent;
1031 AttrDlg[SA_TXTBTN_INFO].Type = DI_BUTTON;
1032 AttrDlg[SA_TXTBTN_INFO].strData = DlgParam.SymlinkButtonTitles[2];
1033 ReadSymlink(strSelName, DlgParam.SymLink);
1034 AttrDlg[SA_EDIT_INFO].strData = DlgParam.SymLink;
1035 AttrDlg[SA_EDIT_INFO].Flags &= ~DIF_READONLY; // not readonly only if symlink
1038 else if (FileAttr & FILE_ATTRIBUTE_DEVICE_CHAR)
1039 AttrDlg[SA_EDIT_INFO].strData = Msg::FileFilterAttrDevChar;
1040 else if (FileAttr & FILE_ATTRIBUTE_DEVICE_BLOCK)
1041 AttrDlg[SA_EDIT_INFO].strData = Msg::FileFilterAttrDevBlock;
1042 else if (FileAttr & FILE_ATTRIBUTE_DEVICE_FIFO)
1043 AttrDlg[SA_EDIT_INFO].strData = Msg::FileFilterAttrDevFIFO;
1044 else if (FileAttr & FILE_ATTRIBUTE_DEVICE_SOCK)
1045 AttrDlg[SA_EDIT_INFO].strData = Msg::FileFilterAttrDevSock;
1046 else {
1047 AttrDlg[SA_EDIT_INFO].strData = BriefInfo(strSelName);
1051 if (FileAttr & FILE_ATTRIBUTE_DIRECTORY) {
1052 if (!DlgParam.Plugin) {
1053 FileAttr = apiGetFileAttributes(strSelName);
1056 //_SVS(SysLog(L"SelName=%ls FileAttr=0x%08X",SelName,FileAttr));
1057 AttrDlg[SA_SEPARATOR4].Flags&= ~DIF_HIDDEN;
1058 AttrDlg[SA_CHECKBOX_SUBFOLDERS].Flags&= ~(DIF_DISABLE | DIF_HIDDEN);
1059 AttrDlg[SA_CHECKBOX_SUBFOLDERS].Selected =
1060 Opt.SetAttrFolderRules ? BSTATE_UNCHECKED : BSTATE_CHECKED;
1061 AttrDlg[SA_DOUBLEBOX].Y2+= 2;
1062 for (int i = SA_SEPARATOR5; i <= SA_BUTTON_CANCEL; i++) {
1063 AttrDlg[i].Y1+= 2;
1064 AttrDlg[i].Y2+= 2;
1066 DlgY+= 2;
1068 if (Opt.SetAttrFolderRules) {
1069 if (DlgParam.Plugin || apiGetFindDataEx(strSelName, FindData)) {
1070 ConvertDate(FindData.ftUnixAccessTime, AttrDlg[SA_FIXEDIT_LAST_ACCESS_DATE].strData,
1071 AttrDlg[SA_FIXEDIT_LAST_ACCESS_TIME].strData, 12, FALSE, FALSE, 2, TRUE);
1072 ConvertDate(FindData.ftUnixModificationTime,
1073 AttrDlg[SA_FIXEDIT_LAST_MODIFICATION_DATE].strData,
1074 AttrDlg[SA_FIXEDIT_LAST_MODIFICATION_TIME].strData, 12, FALSE, FALSE, 2,
1075 TRUE);
1076 ConvertDate(FindData.ftUnixStatusChangeTime,
1077 AttrDlg[SA_FIXEDIT_LAST_CHANGE_DATE].strData,
1078 AttrDlg[SA_FIXEDIT_LAST_CHANGE_TIME].strData, 12, FALSE, FALSE, 2, TRUE);
1082 FolderPresent = TRUE;
1085 if ((FileAttr != INVALID_FILE_ATTRIBUTES)
1086 && ((FileAttr & FILE_ATTRIBUTE_DIRECTORY) == 0 || Opt.SetAttrFolderRules)) {
1087 for (size_t i = 0; i < ARRAYSIZE(AP); i++) {
1088 AttrDlg[AP[i].Item].Selected =
1089 (FileMode & AP[i].Mode) ? BSTATE_CHECKED : BSTATE_UNCHECKED;
1091 AttrDlg[SA_CHECKBOX_IMMUTABLE].Selected = FFFlags.Immutable() ? BSTATE_CHECKED : BSTATE_UNCHECKED;
1092 AttrDlg[SA_CHECKBOX_APPEND].Selected = FFFlags.Append() ? BSTATE_CHECKED : BSTATE_UNCHECKED;
1093 #if defined(__APPLE__) || defined(__FreeBSD__) || defined(__DragonFly__)
1094 AttrDlg[SA_CHECKBOX_HIDDEN].Selected = FFFlags.Hidden() ? BSTATE_CHECKED : BSTATE_UNCHECKED;
1095 #endif
1098 for (auto i : PreserveOriginalIDs) {
1099 AttrDlg[i].Flags&= ~DIF_3STATE;
1102 AttrDlg[SA_TEXT_NAME].strData = strSelName;
1103 TruncStr(AttrDlg[SA_TEXT_NAME].strData, DlgX - 10);
1105 const SETATTRDLG Dates[] = {SA_FIXEDIT_LAST_ACCESS_DATE, SA_FIXEDIT_LAST_MODIFICATION_DATE,
1106 SA_FIXEDIT_LAST_CHANGE_DATE};
1107 const SETATTRDLG Times[] = {SA_FIXEDIT_LAST_ACCESS_TIME, SA_FIXEDIT_LAST_MODIFICATION_TIME,
1108 SA_FIXEDIT_LAST_CHANGE_TIME};
1109 const PFILETIME TimeValues[] = {&FindData.ftUnixAccessTime, &FindData.ftUnixModificationTime,
1110 &FindData.ftUnixStatusChangeTime};
1112 if (DlgParam.Plugin || (!DlgParam.Plugin && apiGetFindDataEx(strSelName, FindData))) {
1113 for (size_t i = 0; i < ARRAYSIZE(Dates); i++) {
1114 ConvertDate(*TimeValues[i], AttrDlg[Dates[i]].strData, AttrDlg[Times[i]].strData, 12,
1115 FALSE, FALSE, 2, TRUE);
1119 FARString strComputerName;
1120 if (SrcPanel) {
1121 FARString strCurDir;
1122 SrcPanel->GetCurDir(strCurDir);
1125 GetFileOwner(strComputerName, strSelName, AttrDlg[SA_COMBO_OWNER].strData);
1126 GetFileGroup(strComputerName, strSelName, AttrDlg[SA_COMBO_GROUP].strData);
1127 } // end of if (SelCount==1)
1128 else {
1129 for (size_t i = 0; i < ARRAYSIZE(AP); i++) {
1130 AttrDlg[AP[i].Item].Selected = BSTATE_3STATE;
1133 AttrDlg[SA_FIXEDIT_LAST_ACCESS_DATE].strData.Clear();
1134 AttrDlg[SA_FIXEDIT_LAST_ACCESS_TIME].strData.Clear();
1135 AttrDlg[SA_FIXEDIT_LAST_MODIFICATION_DATE].strData.Clear();
1136 AttrDlg[SA_FIXEDIT_LAST_MODIFICATION_TIME].strData.Clear();
1137 AttrDlg[SA_FIXEDIT_LAST_CHANGE_DATE].strData.Clear();
1138 AttrDlg[SA_FIXEDIT_LAST_CHANGE_TIME].strData.Clear();
1139 AttrDlg[SA_BUTTON_ORIGINAL].Flags|= DIF_DISABLE;
1140 AttrDlg[SA_TEXT_NAME].strData = Msg::SetAttrSelectedObjects;
1142 for (auto i : PreserveOriginalIDs) {
1143 AttrDlg[i].Selected = BSTATE_UNCHECKED;
1146 // проверка - есть ли среди выделенных - каталоги?
1147 // так же проверка на атрибуты
1148 // так же подсчет числа каталогов и symlinks
1149 if (SrcPanel) {
1150 SrcPanel->GetSelName(nullptr, FileAttr, FileMode);
1152 FolderPresent = false;
1153 FolderCount = 0; Link2FileCount = 0; Link2DirCount = 0;
1155 if (SrcPanel) {
1156 FARString strComputerName;
1157 FARString strCurDir;
1158 SrcPanel->GetCurDir(strCurDir);
1160 while (SrcPanel->GetSelName(&strSelName, FileAttr, FileMode, &FindData)) {
1161 if (FileAttr & FILE_ATTRIBUTE_DIRECTORY) {
1162 if (FileAttr & FILE_ATTRIBUTE_REPARSE_POINT)
1163 Link2DirCount++;
1164 else
1165 FolderCount++;
1166 if (!FolderPresent) {
1167 FolderPresent = true;
1168 AttrDlg[SA_SEPARATOR4].Flags&= ~DIF_HIDDEN;
1169 AttrDlg[SA_CHECKBOX_SUBFOLDERS].Flags&= ~(DIF_DISABLE | DIF_HIDDEN);
1170 AttrDlg[SA_DOUBLEBOX].Y2+= 2;
1171 for (int i = SA_SEPARATOR5; i <= SA_BUTTON_CANCEL; i++) {
1172 AttrDlg[i].Y1+= 2;
1173 AttrDlg[i].Y2+= 2;
1175 DlgY+= 2;
1178 else if (FileAttr & FILE_ATTRIBUTE_REPARSE_POINT)
1179 Link2FileCount++;
1181 for (size_t i = 0; i < ARRAYSIZE(AP); i++) {
1182 if (FileMode & AP[i].Mode) {
1183 AttrDlg[AP[i].Item].Selected++;
1186 CheckFileOwnerGroup(AttrDlg[SA_COMBO_OWNER], GetFileOwner, strComputerName, strSelName);
1187 CheckFileOwnerGroup(AttrDlg[SA_COMBO_GROUP], GetFileGroup, strComputerName, strSelName);
1189 FSFileFlagsSafe FFFlags(strSelName.GetMB(), FileAttr);
1190 if (FFFlags.Immutable())
1191 AttrDlg[SA_CHECKBOX_IMMUTABLE].Selected++;
1192 if (FFFlags.Append())
1193 AttrDlg[SA_CHECKBOX_APPEND].Selected++;
1194 #if defined(__APPLE__) || defined(__FreeBSD__) || defined(__DragonFly__)
1195 if (FFFlags.Hidden())
1196 AttrDlg[SA_CHECKBOX_HIDDEN].Selected++;
1197 #endif
1199 } else {
1200 // BUGBUG, copy-paste
1201 if (!FolderPresent && (FindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) {
1202 FolderPresent = true;
1203 AttrDlg[SA_SEPARATOR4].Flags&= ~DIF_HIDDEN;
1204 AttrDlg[SA_CHECKBOX_SUBFOLDERS].Flags&= ~(DIF_DISABLE | DIF_HIDDEN);
1205 AttrDlg[SA_DOUBLEBOX].Y2+= 2;
1206 for (int i = SA_SEPARATOR5; i <= SA_BUTTON_CANCEL; i++) {
1207 AttrDlg[i].Y1+= 2;
1208 AttrDlg[i].Y2+= 2;
1210 DlgY+= 2;
1212 for (size_t i = 0; i < ARRAYSIZE(AP); i++) {
1213 if (FindData.dwUnixMode & AP[i].Mode) {
1214 AttrDlg[AP[i].Item].Selected++;
1218 if (SrcPanel) {
1219 SrcPanel->GetSelName(nullptr, FileAttr, FileMode);
1220 SrcPanel->GetSelName(&strSelName, FileAttr, FileMode, &FindData);
1223 // выставим "неопределенку" или то, что нужно
1224 for (auto i : PreserveOriginalIDs) {
1225 // снимаем 3-state, если "есть все или нет ничего"
1226 // за исключением случая, если есть Фолдер среди объектов
1227 if ((!AttrDlg[i].Selected || AttrDlg[i].Selected >= SelCount) && !FolderPresent) {
1228 AttrDlg[i].Flags&= ~DIF_3STATE;
1231 AttrDlg[i].Selected = (AttrDlg[i].Selected >= SelCount)
1232 ? BST_CHECKED
1233 : (!AttrDlg[i].Selected ? BSTATE_UNCHECKED : BSTATE_3STATE);
1237 FARString strTmp, strSep=L" (";
1238 int FilesCount = SelCount-FolderCount-Link2FileCount-Link2DirCount;
1239 strTmp.Format(Msg::SetAttrInfoSelAll, SelCount);
1240 if (FolderCount>0)
1241 { strTmp.AppendFormat(Msg::SetAttrInfoSelDirs, strSep.CPtr(), FolderCount); strSep=L", "; }
1242 if (FilesCount>0)
1243 { strTmp.AppendFormat(Msg::SetAttrInfoSelFiles, strSep.CPtr(), FilesCount); strSep=L", "; }
1244 if (Link2DirCount>0)
1245 { strTmp.AppendFormat(Msg::SetAttrInfoSelSymDirs, strSep.CPtr(), Link2DirCount); strSep=L", "; }
1246 if (Link2FileCount>0)
1247 strTmp.AppendFormat(Msg::SetAttrInfoSelSymFiles, strSep.CPtr(), Link2FileCount);
1248 strTmp.Append(L')');
1249 AttrDlg[SA_EDIT_INFO].strData = strTmp;
1254 // поведение для каталогов как у 1.65?
1255 if (FolderPresent && !Opt.SetAttrFolderRules) {
1256 AttrDlg[SA_CHECKBOX_SUBFOLDERS].Selected = BSTATE_CHECKED;
1257 AttrDlg[SA_FIXEDIT_LAST_ACCESS_DATE].strData.Clear();
1258 AttrDlg[SA_FIXEDIT_LAST_ACCESS_TIME].strData.Clear();
1259 AttrDlg[SA_FIXEDIT_LAST_MODIFICATION_DATE].strData.Clear();
1260 AttrDlg[SA_FIXEDIT_LAST_MODIFICATION_TIME].strData.Clear();
1261 AttrDlg[SA_FIXEDIT_LAST_CHANGE_DATE].strData.Clear();
1262 AttrDlg[SA_FIXEDIT_LAST_CHANGE_TIME].strData.Clear();
1264 for (auto i : PreserveOriginalIDs) {
1265 AttrDlg[i].Selected = BSTATE_3STATE;
1266 AttrDlg[i].Flags|= DIF_3STATE;
1270 // запомним состояние переключателей.
1271 for (size_t i = 0; i < ARRAYSIZE(PreserveOriginalIDs); ++i) {
1272 DlgParam.OriginalCBAttr[i] = DlgParam.OriginalCBAttr0[i] =AttrDlg[PreserveOriginalIDs[i]].Selected;
1273 DlgParam.OriginalCBAttr2[i] = -1;
1274 DlgParam.OriginalCBFlag[i] = AttrDlg[PreserveOriginalIDs[i]].Flags;
1277 DlgParam.strOwner = AttrDlg[SA_COMBO_OWNER].strData;
1278 DlgParam.strGroup = AttrDlg[SA_COMBO_GROUP].strData;
1279 FARString strInitOwner = DlgParam.strOwner, strInitGroup = DlgParam.strGroup;
1281 DlgParam.DialogMode = ((SelCount == 1 && !(FileAttr & FILE_ATTRIBUTE_DIRECTORY))
1282 ? MODE_FILE
1283 : (SelCount == 1 ? MODE_FOLDER : MODE_MULTIPLE));
1284 DlgParam.strSelName = strSelName;
1285 DlgParam.OSubfoldersState = static_cast<FARCHECKEDSTATE>(AttrDlg[SA_CHECKBOX_SUBFOLDERS].Selected);
1287 Dialog Dlg(AttrDlg, ARRAYSIZE(AttrDlgData), SetAttrDlgProc, (LONG_PTR)&DlgParam);
1288 Dlg.SetHelp(L"FileAttrDlg"); // ^ - это одиночный диалог!
1289 Dlg.SetId(FileAttrDlgId);
1291 /*if (LinkPresent) {
1292 DlgY++;
1295 Dlg.SetPosition(-1, -1, DlgX, DlgY);
1296 SetAttrCalcBitsCharFromModeCheckBoxes(&Dlg); // set octal
1297 Dlg.Process();
1299 switch (Dlg.GetExitCode()) {
1300 case SA_BUTTON_SET: {
1301 const size_t Times[] = {SA_FIXEDIT_LAST_ACCESS_TIME, SA_FIXEDIT_LAST_MODIFICATION_TIME,
1302 SA_FIXEDIT_LAST_CHANGE_TIME};
1304 for (size_t i = 0; i < ARRAYSIZE(Times); i++) {
1305 LPWSTR TimePtr = AttrDlg[Times[i]].strData.GetBuffer();
1306 TimePtr[8] = GetTimeSeparator();
1307 AttrDlg[Times[i]].strData.ReleaseBuffer(AttrDlg[Times[i]].strData.GetLength());
1310 TPreRedrawFuncGuard preRedrawFuncGuard(PR_ShellSetFileAttributesMsg);
1311 ShellSetFileAttributesMsg(SelCount == 1 ? strSelName.CPtr() : nullptr);
1312 int SkipMode = SETATTR_RET_UNKNOWN;
1314 if (SelCount == 1 && (FileAttr & FILE_ATTRIBUTE_REPARSE_POINT) != 0) {
1315 FARString OldSymLink;
1316 ReadSymlink(strSelName, OldSymLink);
1317 if (DlgParam.SymLink != OldSymLink) {
1318 int r = 1;
1319 if ( !apiPathExists(DlgParam.SymLink) ) {
1320 FARString strTmp1, strTmp2, strTmp3;
1321 strTmp1.Format(Msg::SetAttrSymlinkWarn1, strSelName.CPtr());
1322 strTmp2.Format(Msg::SetAttrSymlinkWarn2, DlgParam.SymLink.CPtr());
1323 strTmp3.Format(Msg::SetAttrSymlinkWarn3, OldSymLink.CPtr());
1324 r = Message(MSG_WARNING, 2,
1325 Msg::Error, strTmp1, strTmp2, strTmp3, L" ", Msg::SetAttrSymlinkWarn4,
1326 Msg::HSkip, Msg::HChange);
1328 if( r == 1 ) {
1329 fprintf(stderr, "Symlink change: '%ls' -> '%ls'\n",
1330 OldSymLink.CPtr(), DlgParam.SymLink.CPtr());
1331 sdc_unlink(strSelName.GetMB().c_str());
1332 r = sdc_symlink(DlgParam.SymLink.GetMB().c_str(), strSelName.GetMB().c_str());
1333 if (r != 0) {
1334 Message(MSG_WARNING | MSG_ERRORTYPE, 1,
1335 Msg::Error, Msg::SetAttrSymlinkFailed, strSelName, Msg::Ok);
1340 if (SelCount == 1 && !(FileAttr & FILE_ATTRIBUTE_DIRECTORY)) {
1341 DWORD NewMode = 0;
1343 for (size_t i = 0; i < ARRAYSIZE(AP); i++) {
1344 if (AttrDlg[AP[i].Item].Selected) {
1345 NewMode|= AP[i].Mode;
1349 if (!ApplyFileOwnerGroupIfChanged(AttrDlg[SA_COMBO_OWNER], ESetFileOwner, SkipMode,
1350 strSelName, strInitOwner))
1351 break;
1352 if (!ApplyFileOwnerGroupIfChanged(AttrDlg[SA_COMBO_GROUP], ESetFileGroup, SkipMode,
1353 strSelName, strInitGroup))
1354 break;
1356 FILETIME UnixAccessTime = {}, UnixModificationTime = {};
1357 int SetAccessTime = DlgParam.OAccessTime
1358 && ReadFileTime(0, strSelName, UnixAccessTime,
1359 AttrDlg[SA_FIXEDIT_LAST_ACCESS_DATE].strData,
1360 AttrDlg[SA_FIXEDIT_LAST_ACCESS_TIME].strData);
1361 int SetModifyTime = DlgParam.OModifyTime
1362 && ReadFileTime(1, strSelName, UnixModificationTime,
1363 AttrDlg[SA_FIXEDIT_LAST_MODIFICATION_DATE].strData,
1364 AttrDlg[SA_FIXEDIT_LAST_MODIFICATION_TIME].strData);
1366 //_SVS(SysLog(L"\n\tSetWriteTime=%d\n\tSetCreationTime=%d\n\tSetLastAccessTime=%d",SetWriteTime,SetCreationTime,SetLastAccessTime));
1368 if (SetAccessTime || SetModifyTime) {
1369 if (ESetFileTime(strSelName, SetAccessTime ? &UnixAccessTime : nullptr,
1370 SetModifyTime ? &UnixModificationTime : nullptr, FileAttr, SkipMode)
1371 == SETATTR_RET_SKIPALL) {
1372 SkipMode = SETATTR_RET_SKIP;
1376 if ((FileMode & EDITABLE_MODES) != (NewMode & EDITABLE_MODES)) {
1377 if (ESetFileMode(strSelName, NewMode, SkipMode) == SETATTR_RET_SKIPALL) {
1378 SkipMode = SETATTR_RET_SKIP;
1382 ApplyFSFileFlags(AttrDlg, strSelName, FileAttr);
1384 /* Multi *********************************************************** */
1385 else {
1386 int RetCode = 1;
1387 ConsoleTitle SetAttrTitle(Msg::SetAttrTitle);
1388 if (SrcPanel) {
1389 CtrlObject->Cp()->GetAnotherPanel(SrcPanel)->CloseFile();
1391 DWORD SetMode = 0, ClearMode = 0;
1393 for (size_t i = 0; i < ARRAYSIZE(AP); i++) {
1394 switch (AttrDlg[AP[i].Item].Selected) {
1395 case BSTATE_CHECKED:
1396 SetMode|= AP[i].Mode;
1397 break;
1398 case BSTATE_UNCHECKED:
1399 ClearMode|= AP[i].Mode;
1400 break;
1404 if (SrcPanel) {
1405 SrcPanel->GetSelName(nullptr, FileAttr, FileMode);
1407 wakeful W;
1408 bool Cancel = false;
1409 DWORD LastTime = 0;
1411 bool SingleFileDone = false;
1412 while ((SrcPanel ? SrcPanel->GetSelName(&strSelName, FileAttr, FileMode, &FindData)
1413 : !SingleFileDone)
1414 && !Cancel) {
1415 if (!SrcPanel) {
1416 SingleFileDone = true;
1418 //_SVS(SysLog(L"SelName='%ls'\n\tFileAttr =0x%08X\n\tSetAttr =0x%08X\n\tClearAttr=0x%08X\n\tResult =0x%08X",
1419 // SelName,FileAttr,SetAttr,ClearAttr,((FileAttr|SetAttr)&(~ClearAttr))));
1420 DWORD CurTime = WINPORT(GetTickCount)();
1422 if (CurTime - LastTime > RedrawTimeout) {
1423 LastTime = CurTime;
1424 ShellSetFileAttributesMsg(strSelName);
1426 if (CheckForEsc())
1427 break;
1430 if (!ApplyFileOwnerGroupIfChanged(AttrDlg[SA_COMBO_OWNER], ESetFileOwner, SkipMode,
1431 strSelName, strInitOwner))
1432 break;
1433 if (!ApplyFileOwnerGroupIfChanged(AttrDlg[SA_COMBO_GROUP], ESetFileGroup, SkipMode,
1434 strSelName, strInitGroup))
1435 break;
1437 FILETIME UnixAccessTime = {}, UnixModificationTime = {};
1438 int SetAccessTime = DlgParam.OAccessTime
1439 && ReadFileTime(0, strSelName, UnixAccessTime,
1440 AttrDlg[SA_FIXEDIT_LAST_ACCESS_DATE].strData,
1441 AttrDlg[SA_FIXEDIT_LAST_ACCESS_TIME].strData);
1442 int SetModifyTime = DlgParam.OModifyTime
1443 && ReadFileTime(1, strSelName, UnixModificationTime,
1444 AttrDlg[SA_FIXEDIT_LAST_MODIFICATION_DATE].strData,
1445 AttrDlg[SA_FIXEDIT_LAST_MODIFICATION_TIME].strData);
1447 RetCode = ESetFileTime(strSelName, SetAccessTime ? &UnixAccessTime : nullptr,
1448 SetModifyTime ? &UnixModificationTime : nullptr, FileAttr, SkipMode);
1450 if (RetCode == SETATTR_RET_ERROR)
1451 break;
1452 else if (RetCode == SETATTR_RET_SKIP)
1453 continue;
1454 else if (RetCode == SETATTR_RET_SKIPALL) {
1455 SkipMode = SETATTR_RET_SKIP;
1456 continue;
1459 if (FileAttr != INVALID_FILE_ATTRIBUTES) {
1460 if (((FileMode | SetMode) & ~ClearMode) != FileMode) {
1462 RetCode = ESetFileMode(strSelName, ((FileMode | SetMode) & (~ClearMode)),
1463 SkipMode);
1465 if (RetCode == SETATTR_RET_ERROR)
1466 break;
1467 else if (RetCode == SETATTR_RET_SKIP)
1468 continue;
1469 else if (RetCode == SETATTR_RET_SKIPALL) {
1470 SkipMode = SETATTR_RET_SKIP;
1471 continue;
1475 if ((FileAttr & FILE_ATTRIBUTE_DIRECTORY)
1476 && AttrDlg[SA_CHECKBOX_SUBFOLDERS].Selected) {
1477 ScanTree ScTree(FALSE);
1478 ScTree.SetFindPath(strSelName, L"*");
1479 DWORD LastTime = WINPORT(GetTickCount)();
1480 FARString strFullName;
1482 while (ScTree.GetNextName(&FindData, strFullName)) {
1483 DWORD CurTime = WINPORT(GetTickCount)();
1485 if (CurTime - LastTime > RedrawTimeout) {
1486 LastTime = CurTime;
1487 ShellSetFileAttributesMsg(strFullName);
1489 if (CheckForEsc()) {
1490 Cancel = true;
1491 break;
1495 if (!ApplyFileOwnerGroupIfChanged(AttrDlg[SA_COMBO_GROUP], ESetFileOwner,
1496 SkipMode, strFullName, strInitOwner,
1497 DlgParam.OSubfoldersState))
1498 break;
1499 if (!ApplyFileOwnerGroupIfChanged(AttrDlg[SA_COMBO_GROUP], ESetFileGroup,
1500 SkipMode, strFullName, strInitGroup,
1501 DlgParam.OSubfoldersState))
1502 break;
1504 SetAccessTime = DlgParam.OAccessTime
1505 && ReadFileTime(0, strFullName, UnixAccessTime,
1506 AttrDlg[SA_FIXEDIT_LAST_ACCESS_DATE].strData,
1507 AttrDlg[SA_FIXEDIT_LAST_ACCESS_TIME].strData);
1508 SetModifyTime = DlgParam.OModifyTime
1509 && ReadFileTime(1, strFullName, UnixModificationTime,
1510 AttrDlg[SA_FIXEDIT_LAST_MODIFICATION_DATE].strData,
1511 AttrDlg[SA_FIXEDIT_LAST_MODIFICATION_TIME].strData);
1513 if (SetAccessTime || SetModifyTime) {
1514 RetCode = ESetFileTime(strFullName,
1515 SetAccessTime ? &UnixAccessTime : nullptr,
1516 SetModifyTime ? &UnixModificationTime : nullptr,
1517 FindData.dwFileAttributes, SkipMode);
1519 if (RetCode == SETATTR_RET_ERROR) {
1520 Cancel = true;
1521 break;
1522 } else if (RetCode == SETATTR_RET_SKIP)
1523 continue;
1524 else if (RetCode == SETATTR_RET_SKIPALL) {
1525 SkipMode = SETATTR_RET_SKIP;
1526 continue;
1530 if (((FindData.dwUnixMode | SetMode) & (~ClearMode))
1531 != FindData.dwUnixMode) {
1532 RetCode = ESetFileMode(strFullName,
1533 (FindData.dwUnixMode | SetMode) & (~ClearMode), SkipMode);
1535 if (RetCode == SETATTR_RET_ERROR) {
1536 Cancel = true;
1537 break;
1538 } else if (RetCode == SETATTR_RET_SKIP)
1539 continue;
1540 else if (RetCode == SETATTR_RET_SKIPALL) {
1541 SkipMode = SETATTR_RET_SKIP;
1542 continue;
1544 ApplyFSFileFlags(AttrDlg, strFullName, FileAttr);
1549 ApplyFSFileFlags(AttrDlg, strSelName, FileAttr);
1551 } // END: while (SrcPanel->GetSelNameCompat(...))
1553 } break;
1554 default:
1555 return false;
1559 if (SrcPanel) {
1560 SrcPanel->SaveSelection();
1561 SrcPanel->Update(UPDATE_KEEP_SELECTION);
1562 SrcPanel->ClearSelection();
1563 CtrlObject->Cp()->GetAnotherPanel(SrcPanel)->Update(UPDATE_KEEP_SELECTION | UPDATE_SECONDARY);
1565 CtrlObject->Cp()->Redraw();
1566 return true;