Merge pull request #2265 from akruphi/case_sensitive_findfile
[far2l.git] / far2l / src / setattr.cpp
blob1d227877988174b25d9ebbb3f804aaa0108bf6b8
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_TEXT_INFO_CHMARK,
80 SA_EDIT_INFO,
81 SA_SEPARATOR_OWNERSHIP,
82 SA_TEXT_OWNER,
83 SA_TEXT_OWNER_CHMARK,
84 SA_COMBO_OWNER,
85 SA_TEXT_GROUP,
86 SA_TEXT_GROUP_CHMARK,
87 SA_COMBO_GROUP,
89 SA_SEPARATOR_MODE,
90 SA_SEPARATOR_MODE_V1,
91 SA_SEPARATOR_MODE_V2,
92 SA_TEXT_MODE_USER,
93 SA_TEXT_USER_READ_CHMARK,
94 SA_CHECKBOX_USER_READ,
95 SA_TEXT_USER_WRITE_CHMARK,
96 SA_CHECKBOX_USER_WRITE,
97 SA_TEXT_USER_EXECUTE_CHMARK,
98 SA_CHECKBOX_USER_EXECUTE,
99 SA_TEXT_MODE_GROUP,
100 SA_TEXT_GROUP_READ_CHMARK,
101 SA_CHECKBOX_GROUP_READ,
102 SA_TEXT_GROUP_WRITE_CHMARK,
103 SA_CHECKBOX_GROUP_WRITE,
104 SA_TEXT_GROUP_EXECUTE_CHMARK,
105 SA_CHECKBOX_GROUP_EXECUTE,
106 SA_TEXT_MODE_OTHER,
107 SA_TEXT_OTHER_READ_CHMARK,
108 SA_CHECKBOX_OTHER_READ,
109 SA_TEXT_OTHER_WRITE_CHMARK,
110 SA_CHECKBOX_OTHER_WRITE,
111 SA_TEXT_OTHER_EXECUTE_CHMARK,
112 SA_CHECKBOX_OTHER_EXECUTE,
113 SA_TEXT_SUID_CHMARK,
114 SA_CHECKBOX_SUID,
115 SA_TEXT_SGID_CHMARK,
116 SA_CHECKBOX_SGID,
117 SA_TEXT_STICKY_CHMARK,
118 SA_CHECKBOX_STICKY,
120 SA_TEXT_MODE_OCTAL,
121 SA_TEXT_MODE_OCTAL_ORIGINAL,
122 SA_TEXT_MODE_OCTAL_CHMARK,
123 SA_FIXEDIT_MODE_OCTAL,
124 SA_BUTTON_MODE_ORIGINAL,
126 SA_SEPARATOR_ATTRIBUTES,
127 SA_TEXT_IMMUTABLE_CHMARK,
128 SA_CHECKBOX_IMMUTABLE,
129 SA_TEXT_APPEND_CHMARK,
130 SA_CHECKBOX_APPEND,
131 #if defined(__APPLE__) || defined(__FreeBSD__) || defined(__DragonFly__)
132 SA_TEXT_HIDDEN_CHMARK,
133 SA_CHECKBOX_HIDDEN,
134 #endif
136 SA_SEPARATOR3,
137 SA_TEXT_TITLEDATE,
138 SA_TEXT_LAST_ACCESS,
139 SA_TEXT_LAST_ACCESS_DATE_CHMARK,
140 SA_FIXEDIT_LAST_ACCESS_DATE,
141 SA_TEXT_LAST_ACCESS_TIME_CHMARK,
142 SA_FIXEDIT_LAST_ACCESS_TIME,
143 SA_TEXT_LAST_MODIFICATION,
144 SA_TEXT_LAST_MODIFICATION_DATE_CHMARK,
145 SA_FIXEDIT_LAST_MODIFICATION_DATE,
146 SA_TEXT_LAST_MODIFICATION_TIME_CHMARK,
147 SA_FIXEDIT_LAST_MODIFICATION_TIME,
148 SA_TEXT_LAST_CHANGE,
149 SA_TEXT_LAST_CHANGE_DATE_CHMARK,
150 SA_FIXEDIT_LAST_CHANGE_DATE,
151 SA_TEXT_LAST_CHANGE_TIME_CHMARK,
152 SA_FIXEDIT_LAST_CHANGE_TIME,
153 SA_BUTTON_ORIGINAL,
154 SA_BUTTON_CURRENT,
155 SA_BUTTON_BLANK,
156 SA_SEPARATOR4,
157 SA_CHECKBOX_SUBFOLDERS,
158 SA_SEPARATOR5,
159 SC_SYMLINK_PROPERTIES_EXPLAIN_TEXT_1,
160 SC_SYMLINK_PROPERTIES_EXPLAIN_TEXT_2,
161 SA_BUTTON_SET,
162 SA_BUTTON_CANCEL
165 static const struct MODEPAIR
167 SETATTRDLG Item;
168 mode_t Mode;
169 } AP[] = {
170 {SA_CHECKBOX_USER_READ, S_IRUSR},
171 {SA_CHECKBOX_USER_WRITE, S_IWUSR},
172 {SA_CHECKBOX_USER_EXECUTE, S_IXUSR},
173 {SA_CHECKBOX_GROUP_READ, S_IRGRP},
174 {SA_CHECKBOX_GROUP_WRITE, S_IWGRP},
175 {SA_CHECKBOX_GROUP_EXECUTE, S_IXGRP},
176 {SA_CHECKBOX_OTHER_READ, S_IROTH},
177 {SA_CHECKBOX_OTHER_WRITE, S_IWOTH},
178 {SA_CHECKBOX_OTHER_EXECUTE, S_IXOTH},
179 {SA_CHECKBOX_SUID, S_ISUID},
180 {SA_CHECKBOX_SGID, S_ISGID},
181 {SA_CHECKBOX_STICKY, S_ISVTX}
184 #define EDITABLE_MODES \
185 (S_IXOTH | S_IWOTH | S_IROTH | S_IXGRP | S_IWGRP | S_IRGRP | S_IXUSR | S_IWUSR | S_IRUSR | S_ISUID \
186 | S_ISGID | S_ISVTX)
188 static const int PreserveOriginalIDs[] = {
189 SA_CHECKBOX_USER_READ, SA_CHECKBOX_USER_WRITE, SA_CHECKBOX_USER_EXECUTE,
190 SA_CHECKBOX_GROUP_READ, SA_CHECKBOX_GROUP_WRITE, SA_CHECKBOX_GROUP_EXECUTE,
191 SA_CHECKBOX_OTHER_READ, SA_CHECKBOX_OTHER_WRITE, SA_CHECKBOX_OTHER_EXECUTE,
192 SA_CHECKBOX_SUID, SA_CHECKBOX_SGID, SA_CHECKBOX_STICKY,
193 SA_CHECKBOX_IMMUTABLE, SA_CHECKBOX_APPEND,
194 #if defined(__APPLE__) || defined(__FreeBSD__) || defined(__DragonFly__)
195 SA_CHECKBOX_HIDDEN,
196 #endif
199 enum DIALOGMODE
201 MODE_FILE,
202 MODE_FOLDER,
203 MODE_MULTIPLE,
206 struct SetAttrDlgParam
208 bool Plugin = false;
209 DWORD FileSystemFlags = 0;
210 DIALOGMODE DialogMode;
211 FARString strSelName;
212 FARString strOwner;
213 FARString strGroup;
214 bool OwnerChanged = false, GroupChanged = false;
215 // значения CheckBox`ов на момент старта диалога
216 int OriginalCBAttr[ARRAYSIZE(PreserveOriginalIDs)]; // values at dialog start and any user change for restore by subfolders checkbox
217 int OriginalCBAttr0[ARRAYSIZE(PreserveOriginalIDs)]; // values at dialog start
218 int OriginalCBAttr2[ARRAYSIZE(PreserveOriginalIDs)]; // -1 = original, other = was change
219 DWORD OriginalCBFlag[ARRAYSIZE(PreserveOriginalIDs)]; // checkbox flags at dialog start
220 bool _b_mode_check_or_edit_process = false;
221 FARCHECKEDSTATE OSubfoldersState;
222 bool OAccessTime, OModifyTime, OStatusChangeTime;
223 unsigned char SymLinkInfoCycle = 0;
224 FARString SymLink;
225 FARString SymlinkButtonTitles[3];
226 // initial values at dialog start:
227 FARString strInitInfoSymLink,
228 strInitOwner, strInitGroup,
229 strInitOctal,
230 strInitAccessDate, strInitModifyDate, strInitStatusChangeDate,
231 strInitAccessTime, strInitModifyTime, strInitStatusChangeTime;
234 #define DM_SETATTR (DM_USER + 1)
236 static int DialogID2PreservedOriginalIndex(int id)
238 for (size_t i = 0; i < ARRAYSIZE(PreserveOriginalIDs); ++i) {
239 if (PreserveOriginalIDs[i] == id)
240 return i;
242 return -1;
245 inline static bool IsEditChanged(HANDLE hDlg, int EditControl, FARString &Original)
247 return 0 != StrCmp(
248 reinterpret_cast<LPCWSTR>(SendDlgMessage(hDlg, DM_GETCONSTTEXTPTR, EditControl, 0)),
249 Original);
252 static void BlankEditIfChanged(HANDLE hDlg, int EditControl, FARString &Remembered, bool &Changed)
254 LPCWSTR Actual = reinterpret_cast<LPCWSTR>(SendDlgMessage(hDlg, DM_GETCONSTTEXTPTR, EditControl, 0));
255 if (!Changed)
256 Changed = StrCmp(Actual, Remembered) != 0;
258 Remembered = Actual;
260 if (!Changed)
261 SendDlgMessage(hDlg, DM_SETTEXTPTR, EditControl, reinterpret_cast<LONG_PTR>(L""));
264 static std::wstring BriefInfo(const FARString &strSelName)
266 std::vector<std::wstring> lines;
268 std::string cmd = "file -- \"";
269 cmd+= EscapeCmdStr(Wide2MB(strSelName.CPtr()));
270 cmd+= '\"';
272 std::wstring out;
274 if (POpen(lines, cmd.c_str())) {
275 for (const auto &line : lines) {
276 out = line;
277 size_t p = out.find(':');
278 if (p != std::string::npos) {
279 out.erase(0, p + 1);
281 StrTrim(out);
282 if (p != std::string::npos && !out.empty()) {
283 break;
287 return out;
290 inline void SetAttrDefaultMark(HANDLE hDlg, int id, bool nodefault)
292 // id - id of DI_TEXT for non-default marker
293 SendDlgMessage(hDlg, DM_SETTEXTPTR, id, // mark (un)changed
294 nodefault ? reinterpret_cast<LONG_PTR>(L"*") : reinterpret_cast<LONG_PTR>(L"") );
297 static void SetAttrSetCheckAndDefaultMark(HANDLE hDlg, SetAttrDlgParam *DlgParam, int idx, int id, int bstate)
299 // idx - index in arrays PreserveOriginalIDs and OriginalCBAttr0, if < 0 will be detected here
300 // id - id of DI_CHECKBOX
301 // id-1 - must be DI_TEXT for non-default marker
302 SendDlgMessage(hDlg, DM_SETCHECK, id, bstate);
304 if( idx < 0 )
305 idx = DialogID2PreservedOriginalIndex(id);
306 if (idx != -1) {
307 if( DlgParam == nullptr )
308 DlgParam = reinterpret_cast<SetAttrDlgParam *>(SendDlgMessage(hDlg, DM_GETDLGDATA, 0, 0));
309 SetAttrDefaultMark(hDlg, id-1, bstate != DlgParam->OriginalCBAttr0[idx]);
313 static char SetAttrGetBitCharFromModeCheckBoxes(HANDLE hDlg, int _i1, int _i2, int _i3)
315 int i1, i2, i3;
317 i1 = (int)SendDlgMessage(hDlg, DM_GETCHECK, _i1, 0);
318 i2 = (int)SendDlgMessage(hDlg, DM_GETCHECK, _i2, 0);
319 i3 = (int)SendDlgMessage(hDlg, DM_GETCHECK, _i3, 0);
320 if (i1 == BSTATE_3STATE || i2 == BSTATE_3STATE || i3 == BSTATE_3STATE)
321 return '-';
322 else {
323 int i = (i1 == BSTATE_CHECKED ? 1 : 0) + (i2 == BSTATE_CHECKED ? 2 : 0) + (i3 == BSTATE_CHECKED ? 4 : 0);
324 return '0' + i;
328 static void SetAttrCalcBitsCharFromModeCheckBoxes(HANDLE hDlg)
330 wchar_t str_octal[5] = {0};
331 str_octal[0] = SetAttrGetBitCharFromModeCheckBoxes(hDlg, SA_CHECKBOX_STICKY, SA_CHECKBOX_SGID, SA_CHECKBOX_SUID);
332 str_octal[1] = SetAttrGetBitCharFromModeCheckBoxes(hDlg, SA_CHECKBOX_USER_EXECUTE, SA_CHECKBOX_USER_WRITE, SA_CHECKBOX_USER_READ);
333 str_octal[2] = SetAttrGetBitCharFromModeCheckBoxes(hDlg, SA_CHECKBOX_GROUP_EXECUTE, SA_CHECKBOX_GROUP_WRITE, SA_CHECKBOX_GROUP_READ);
334 str_octal[3] = SetAttrGetBitCharFromModeCheckBoxes(hDlg, SA_CHECKBOX_OTHER_EXECUTE, SA_CHECKBOX_OTHER_WRITE, SA_CHECKBOX_OTHER_READ);
336 SendDlgMessage(hDlg, DM_SETTEXTPTR, SA_FIXEDIT_MODE_OCTAL, reinterpret_cast<LONG_PTR>(str_octal));
339 static void SetAttrGetModeCheckBoxesFromChar(HANDLE hDlg, SetAttrDlgParam *DlgParam, wchar_t c, int _i1, int _i2, int _i3)
341 switch(c) {
342 case L'0':
343 SetAttrSetCheckAndDefaultMark(hDlg, DlgParam, -1, _i1, BSTATE_UNCHECKED);
344 SetAttrSetCheckAndDefaultMark(hDlg, DlgParam, -1, _i2, BSTATE_UNCHECKED);
345 SetAttrSetCheckAndDefaultMark(hDlg, DlgParam, -1, _i3, BSTATE_UNCHECKED);
346 break;
347 case L'1':
348 SetAttrSetCheckAndDefaultMark(hDlg, DlgParam, -1, _i1, BSTATE_CHECKED);
349 SetAttrSetCheckAndDefaultMark(hDlg, DlgParam, -1, _i2, BSTATE_UNCHECKED);
350 SetAttrSetCheckAndDefaultMark(hDlg, DlgParam, -1, _i3, BSTATE_UNCHECKED);
351 break;
352 case L'2':
353 SetAttrSetCheckAndDefaultMark(hDlg, DlgParam, -1, _i1, BSTATE_UNCHECKED);
354 SetAttrSetCheckAndDefaultMark(hDlg, DlgParam, -1, _i2, BSTATE_CHECKED);
355 SetAttrSetCheckAndDefaultMark(hDlg, DlgParam, -1, _i3, BSTATE_UNCHECKED);
356 break;
357 case L'3':
358 SetAttrSetCheckAndDefaultMark(hDlg, DlgParam, -1, _i1, BSTATE_CHECKED);
359 SetAttrSetCheckAndDefaultMark(hDlg, DlgParam, -1, _i2, BSTATE_CHECKED);
360 SetAttrSetCheckAndDefaultMark(hDlg, DlgParam, -1, _i3, BSTATE_UNCHECKED);
361 break;
362 case L'4':
363 SetAttrSetCheckAndDefaultMark(hDlg, DlgParam, -1, _i1, BSTATE_UNCHECKED);
364 SetAttrSetCheckAndDefaultMark(hDlg, DlgParam, -1, _i2, BSTATE_UNCHECKED);
365 SetAttrSetCheckAndDefaultMark(hDlg, DlgParam, -1, _i3, BSTATE_CHECKED);
366 break;
367 case L'5':
368 SetAttrSetCheckAndDefaultMark(hDlg, DlgParam, -1, _i1, BSTATE_CHECKED);
369 SetAttrSetCheckAndDefaultMark(hDlg, DlgParam, -1, _i2, BSTATE_UNCHECKED);
370 SetAttrSetCheckAndDefaultMark(hDlg, DlgParam, -1, _i3, BSTATE_CHECKED);
371 break;
372 case L'6':
373 SetAttrSetCheckAndDefaultMark(hDlg, DlgParam, -1, _i1, BSTATE_UNCHECKED);
374 SetAttrSetCheckAndDefaultMark(hDlg, DlgParam, -1, _i2, BSTATE_CHECKED);
375 SetAttrSetCheckAndDefaultMark(hDlg, DlgParam, -1, _i3, BSTATE_CHECKED);
376 break;
377 case L'7':
378 SetAttrSetCheckAndDefaultMark(hDlg, DlgParam, -1, _i1, BSTATE_CHECKED);
379 SetAttrSetCheckAndDefaultMark(hDlg, DlgParam, -1, _i2, BSTATE_CHECKED);
380 SetAttrSetCheckAndDefaultMark(hDlg, DlgParam, -1, _i3, BSTATE_CHECKED);
381 break;
382 default:
383 SetAttrSetCheckAndDefaultMark(hDlg, DlgParam, -1, _i1, BSTATE_3STATE);
384 SetAttrSetCheckAndDefaultMark(hDlg, DlgParam, -1, _i2, BSTATE_3STATE);
385 SetAttrSetCheckAndDefaultMark(hDlg, DlgParam, -1, _i3, BSTATE_3STATE);
386 break;
390 LONG_PTR WINAPI SetAttrDlgProc(HANDLE hDlg, int Msg, int Param1, LONG_PTR Param2)
392 SetAttrDlgParam *DlgParam =
393 reinterpret_cast<SetAttrDlgParam *>(SendDlgMessage(hDlg, DM_GETDLGDATA, 0, 0));
394 int OrigIdx;
396 switch (Msg) {
397 case DN_CLOSE:
398 if (DlgParam->SymLinkInfoCycle == 0) {
399 DlgParam->SymLink = reinterpret_cast<LPCWSTR>
400 (SendDlgMessage(hDlg, DM_GETCONSTTEXTPTR, SA_EDIT_INFO, 0));
402 break;
404 case DN_BTNCLICK:
405 OrigIdx = DialogID2PreservedOriginalIndex(Param1);
406 if (OrigIdx != -1 || Param1 == SA_CHECKBOX_SUBFOLDERS) {
407 if (OrigIdx != -1) {
408 DlgParam->OriginalCBAttr[OrigIdx] = static_cast<int>(Param2);
409 DlgParam->OriginalCBAttr2[OrigIdx] = 0;
410 if( !DlgParam->_b_mode_check_or_edit_process) {
411 DlgParam->_b_mode_check_or_edit_process = true;
412 SetAttrCalcBitsCharFromModeCheckBoxes(hDlg);
413 DlgParam->_b_mode_check_or_edit_process = false;
415 SetAttrDefaultMark(hDlg, Param1-1, Param2 != DlgParam->OriginalCBAttr0[OrigIdx]); // mark (un)changed
417 int FocusPos = static_cast<int>(SendDlgMessage(hDlg, DM_GETFOCUS, 0, 0));
418 FARCHECKEDSTATE SubfoldersState = static_cast<FARCHECKEDSTATE>(
419 SendDlgMessage(hDlg, DM_GETCHECK, SA_CHECKBOX_SUBFOLDERS, 0));
422 DlgParam->_b_mode_check_or_edit_process = true;
423 // если снимаем атрибуты для SubFolders
424 // этот кусок всегда работает если есть хотя бы одна папка
425 // иначе SA_CHECKBOX_SUBFOLDERS недоступен и всегда снят.
426 if (FocusPos == SA_CHECKBOX_SUBFOLDERS) {
427 if (DlgParam->DialogMode == MODE_FOLDER) // каталог однозначно!
429 if (DlgParam->OSubfoldersState != SubfoldersState) // Состояние изменилось?
431 // установили?
432 if (SubfoldersState != BSTATE_UNCHECKED) {
433 for (size_t i = 0; i < ARRAYSIZE(PreserveOriginalIDs); ++i) {
434 SendDlgMessage(hDlg, DM_SET3STATE, PreserveOriginalIDs[i], TRUE);
435 if (DlgParam->OriginalCBAttr2[i] == -1) {
436 SetAttrSetCheckAndDefaultMark(hDlg, DlgParam, i, PreserveOriginalIDs[i],
437 BSTATE_3STATE);
441 BlankEditIfChanged(hDlg, SA_COMBO_OWNER, DlgParam->strOwner,
442 DlgParam->OwnerChanged);
443 BlankEditIfChanged(hDlg, SA_COMBO_GROUP, DlgParam->strGroup,
444 DlgParam->GroupChanged);
446 // сняли?
447 else {
448 for (size_t i = 0; i < ARRAYSIZE(PreserveOriginalIDs); ++i) {
449 SendDlgMessage(hDlg, DM_SET3STATE, PreserveOriginalIDs[i], FALSE);
450 SetAttrSetCheckAndDefaultMark(hDlg, DlgParam, i, PreserveOriginalIDs[i],
451 DlgParam->OriginalCBAttr[i]);
453 SendDlgMessage(hDlg, DM_SETTEXTPTR, SA_COMBO_OWNER,
454 reinterpret_cast<LONG_PTR>(DlgParam->strOwner.CPtr()));
455 SendDlgMessage(hDlg, DM_SETTEXTPTR, SA_COMBO_GROUP,
456 reinterpret_cast<LONG_PTR>(DlgParam->strGroup.CPtr()));
459 if (Opt.SetAttrFolderRules) {
460 FAR_FIND_DATA_EX FindData;
462 if (apiGetFindDataEx(DlgParam->strSelName, FindData)) {
463 const SETATTRDLG Items[] = {SA_TEXT_LAST_ACCESS,
464 SA_TEXT_LAST_MODIFICATION, SA_TEXT_LAST_CHANGE};
465 bool *ParamTimes[] = {&DlgParam->OAccessTime, &DlgParam->OModifyTime,
466 &DlgParam->OStatusChangeTime};
467 const PFILETIME FDTimes[] = {&FindData.ftUnixAccessTime,
468 &FindData.ftUnixModificationTime,
469 &FindData.ftUnixStatusChangeTime};
471 for (size_t i = 0; i < ARRAYSIZE(Items); i++) {
472 if (!*ParamTimes[i]) {
473 SendDlgMessage(hDlg, DM_SETATTR, Items[i],
474 (SubfoldersState != BSTATE_UNCHECKED)
476 : (LONG_PTR)FDTimes[i]);
477 *ParamTimes[i] = false;
484 // много объектов
485 else {
486 // Состояние изменилось?
487 if (DlgParam->OSubfoldersState != SubfoldersState) {
488 // установили?
489 if (SubfoldersState != BSTATE_UNCHECKED) {
490 for (size_t i = 0; i < ARRAYSIZE(PreserveOriginalIDs); ++i) {
491 if (DlgParam->OriginalCBAttr2[i] == -1) {
492 SendDlgMessage(hDlg, DM_SET3STATE, PreserveOriginalIDs[i], TRUE);
493 SetAttrSetCheckAndDefaultMark(hDlg, DlgParam, i, PreserveOriginalIDs[i],
494 BSTATE_3STATE);
497 SendDlgMessage(hDlg, DM_SETTEXTPTR, SA_COMBO_OWNER,
498 reinterpret_cast<LONG_PTR>(L""));
499 SendDlgMessage(hDlg, DM_SETTEXTPTR, SA_COMBO_GROUP,
500 reinterpret_cast<LONG_PTR>(L""));
502 // сняли?
503 else {
504 for (size_t i = 0; i < ARRAYSIZE(PreserveOriginalIDs); ++i) {
505 SendDlgMessage(hDlg, DM_SET3STATE, PreserveOriginalIDs[i],
506 ((DlgParam->OriginalCBFlag[i] & DIF_3STATE) ? TRUE : FALSE));
507 SetAttrSetCheckAndDefaultMark(hDlg, DlgParam, i, PreserveOriginalIDs[i],
508 DlgParam->OriginalCBAttr[i]);
510 SendDlgMessage(hDlg, DM_SETTEXTPTR, SA_COMBO_OWNER,
511 reinterpret_cast<LONG_PTR>(DlgParam->strOwner.CPtr()));
512 SendDlgMessage(hDlg, DM_SETTEXTPTR, SA_COMBO_GROUP,
513 reinterpret_cast<LONG_PTR>(DlgParam->strGroup.CPtr()));
518 DlgParam->OSubfoldersState = SubfoldersState;
520 SetAttrCalcBitsCharFromModeCheckBoxes(hDlg);
521 DlgParam->_b_mode_check_or_edit_process = false;
524 return TRUE;
526 else if (Param1 == SA_TXTBTN_INFO) {
527 FARString strText;
528 SendDlgMessage(hDlg, DM_SETTEXTPTR, SA_TXTBTN_INFO,
529 reinterpret_cast<LONG_PTR>(DlgParam->SymlinkButtonTitles[DlgParam->SymLinkInfoCycle].CPtr()));
531 switch (DlgParam->SymLinkInfoCycle++) {
532 case 0: {
533 DlgParam->SymLink = reinterpret_cast<LPCWSTR>
534 (SendDlgMessage(hDlg, DM_GETCONSTTEXTPTR, SA_EDIT_INFO, 0));
535 ConvertNameToReal(DlgParam->SymLink, strText);
536 SendDlgMessage(hDlg, DM_SETREADONLY, SA_EDIT_INFO, 1);
537 } break;
539 case 1:
540 ConvertNameToReal(DlgParam->SymLink, strText);
541 strText = BriefInfo(strText);
542 SendDlgMessage(hDlg, DM_SETREADONLY, SA_EDIT_INFO, 1);
543 break;
545 default:
546 strText = DlgParam->SymLink;
547 SendDlgMessage(hDlg, DM_SETREADONLY, SA_EDIT_INFO, 0);
548 DlgParam->SymLinkInfoCycle = 0;
550 SendDlgMessage(hDlg, DM_SETTEXTPTR, SA_EDIT_INFO, reinterpret_cast<LONG_PTR>(strText.CPtr()));
551 return TRUE;
553 // Set Original for Modes and Attributes
554 } else if (Param1 == SA_BUTTON_MODE_ORIGINAL) {
555 DlgParam->_b_mode_check_or_edit_process = true;
556 for (size_t i = 0; i < ARRAYSIZE(PreserveOriginalIDs); ++i) {
557 DlgParam->OriginalCBAttr2[i] = -1;
558 SendDlgMessage(hDlg, DM_SET3STATE, PreserveOriginalIDs[i],
559 ((DlgParam->OriginalCBFlag[i] & DIF_3STATE) ? TRUE : FALSE));
560 SetAttrSetCheckAndDefaultMark(hDlg, DlgParam, i, PreserveOriginalIDs[i],
561 DlgParam->OriginalCBAttr0[i]);
563 SetAttrCalcBitsCharFromModeCheckBoxes(hDlg);
564 DlgParam->_b_mode_check_or_edit_process = false;
565 SendDlgMessage(hDlg, DM_SETFOCUS, SA_FIXEDIT_MODE_OCTAL, 0);
567 // Set Original? / Set All? / Clear All?
568 } else if (Param1 == SA_BUTTON_ORIGINAL) {
569 FAR_FIND_DATA_EX FindData;
571 if (apiGetFindDataEx(DlgParam->strSelName, FindData)) {
572 SendDlgMessage(hDlg, DM_SETATTR, SA_TEXT_LAST_ACCESS,
573 (LONG_PTR)&FindData.ftUnixAccessTime);
574 SendDlgMessage(hDlg, DM_SETATTR, SA_TEXT_LAST_MODIFICATION,
575 (LONG_PTR)&FindData.ftUnixModificationTime);
576 SendDlgMessage(hDlg, DM_SETATTR, SA_TEXT_LAST_CHANGE,
577 (LONG_PTR)&FindData.ftUnixStatusChangeTime);
578 DlgParam->OAccessTime = DlgParam->OModifyTime = DlgParam->OStatusChangeTime = false;
581 SendDlgMessage(hDlg, DM_SETFOCUS, SA_FIXEDIT_LAST_ACCESS_DATE, 0);
582 return TRUE;
583 } else if (Param1 == SA_BUTTON_CURRENT || Param1 == SA_BUTTON_BLANK) {
584 LONG_PTR Value = 0;
585 FILETIME CurrentTime;
586 if (Param1 == SA_BUTTON_CURRENT) {
587 WINPORT(GetSystemTimeAsFileTime)(&CurrentTime);
588 Value = reinterpret_cast<LONG_PTR>(&CurrentTime);
590 SendDlgMessage(hDlg, DM_SETATTR, SA_TEXT_LAST_ACCESS, Value);
591 SendDlgMessage(hDlg, DM_SETATTR, SA_TEXT_LAST_MODIFICATION, Value);
592 // SendDlgMessage(hDlg, DM_SETATTR, SA_TEXT_LAST_CHANGE, Value);
593 DlgParam->OAccessTime = DlgParam->OModifyTime = DlgParam->OStatusChangeTime = true;
594 SendDlgMessage(hDlg, DM_SETFOCUS, SA_FIXEDIT_LAST_ACCESS_DATE, 0);
595 return TRUE;
598 break;
600 case DN_MOUSECLICK: {
601 //_SVS(SysLog(L"Msg=DN_MOUSECLICK Param1=%d Param2=%d",Param1,Param2));
602 if (Param1 >= SA_TEXT_LAST_ACCESS && Param1 <= SA_FIXEDIT_LAST_MODIFICATION_TIME/*SA_FIXEDIT_LAST_CHANGE_TIME*/) {
603 if (reinterpret_cast<MOUSE_EVENT_RECORD *>(Param2)->dwEventFlags == DOUBLE_CLICK) {
604 // Дадим Менеджеру диалогов "попотеть"
605 DefDlgProc(hDlg, Msg, Param1, Param2);
606 SendDlgMessage(hDlg, DM_SETATTR, Param1, -1);
609 if (Param1 == SA_TEXT_LAST_ACCESS || Param1 == SA_TEXT_LAST_MODIFICATION
610 /*|| Param1 == SA_TEXT_LAST_CHANGE*/) {
611 Param1++;
614 SendDlgMessage(hDlg, DM_SETFOCUS, Param1, 0);
616 } break;
618 case DN_EDITCHANGE: {
619 switch (Param1) {
620 case SA_FIXEDIT_MODE_OCTAL:
621 if (!DlgParam->_b_mode_check_or_edit_process) {
622 DlgParam->_b_mode_check_or_edit_process = true;
623 int length = (int)SendDlgMessage(hDlg, DM_GETTEXTLENGTH, SA_FIXEDIT_MODE_OCTAL, 0);
624 LPCWSTR str_octal = reinterpret_cast<LPCWSTR>(SendDlgMessage(hDlg, DM_GETCONSTTEXTPTR, SA_FIXEDIT_MODE_OCTAL, 0));
625 if (length>0 && str_octal!=nullptr) {
626 SetAttrGetModeCheckBoxesFromChar(hDlg, DlgParam, length<1 ? ' ' : str_octal[0], SA_CHECKBOX_STICKY, SA_CHECKBOX_SGID, SA_CHECKBOX_SUID);
627 SetAttrGetModeCheckBoxesFromChar(hDlg, DlgParam, length<2 ? ' ' : str_octal[1], SA_CHECKBOX_USER_EXECUTE, SA_CHECKBOX_USER_WRITE, SA_CHECKBOX_USER_READ);
628 SetAttrGetModeCheckBoxesFromChar(hDlg, DlgParam, length<3 ? ' ' : str_octal[2], SA_CHECKBOX_GROUP_EXECUTE, SA_CHECKBOX_GROUP_WRITE, SA_CHECKBOX_GROUP_READ);
629 SetAttrGetModeCheckBoxesFromChar(hDlg, DlgParam, length<4 ? ' ' : str_octal[3], SA_CHECKBOX_OTHER_EXECUTE, SA_CHECKBOX_OTHER_WRITE, SA_CHECKBOX_OTHER_READ);
631 DlgParam->_b_mode_check_or_edit_process = false;
633 SetAttrDefaultMark(hDlg, SA_FIXEDIT_MODE_OCTAL-1, // mark (un)changed
634 IsEditChanged(hDlg, SA_FIXEDIT_MODE_OCTAL, DlgParam->strInitOctal) );
635 break;
636 case SA_EDIT_INFO:
637 SetAttrDefaultMark(hDlg, SA_EDIT_INFO-1, // mark (un)changed
638 DlgParam->SymLinkInfoCycle
639 ? StrCmp(DlgParam->SymLink, DlgParam->strInitInfoSymLink)
640 : IsEditChanged(hDlg, SA_EDIT_INFO, DlgParam->strInitInfoSymLink) );
641 case SA_COMBO_OWNER:
642 SetAttrDefaultMark(hDlg, SA_COMBO_OWNER-1, // mark (un)changed
643 IsEditChanged(hDlg, SA_COMBO_OWNER, DlgParam->strInitOwner) );
644 break;
645 case SA_COMBO_GROUP:
646 SetAttrDefaultMark(hDlg, SA_COMBO_GROUP-1, // mark (un)changed
647 IsEditChanged(hDlg, SA_COMBO_GROUP, DlgParam->strInitOwner) );
648 break;
649 case SA_FIXEDIT_LAST_ACCESS_DATE:
650 SetAttrDefaultMark(hDlg, SA_FIXEDIT_LAST_ACCESS_DATE-1, // mark (un)changed
651 IsEditChanged(hDlg, SA_FIXEDIT_LAST_ACCESS_DATE, DlgParam->strInitAccessDate) );
652 DlgParam->OAccessTime = true;
653 break;
654 case SA_FIXEDIT_LAST_ACCESS_TIME:
655 SetAttrDefaultMark(hDlg, SA_FIXEDIT_LAST_ACCESS_TIME-1, // mark (un)changed
656 IsEditChanged(hDlg, SA_FIXEDIT_LAST_ACCESS_TIME, DlgParam->strInitAccessTime) );
657 DlgParam->OAccessTime = true;
658 break;
659 case SA_FIXEDIT_LAST_MODIFICATION_DATE:
660 SetAttrDefaultMark(hDlg, SA_FIXEDIT_LAST_MODIFICATION_DATE-1, // mark (un)changed
661 IsEditChanged(hDlg, SA_FIXEDIT_LAST_MODIFICATION_DATE, DlgParam->strInitModifyDate) );
662 DlgParam->OModifyTime = true;
663 break;
664 case SA_FIXEDIT_LAST_MODIFICATION_TIME:
665 SetAttrDefaultMark(hDlg, SA_FIXEDIT_LAST_MODIFICATION_TIME-1, // mark (un)changed
666 IsEditChanged(hDlg, SA_FIXEDIT_LAST_MODIFICATION_TIME, DlgParam->strInitModifyTime) );
667 DlgParam->OModifyTime = true;
668 break;
669 case SA_FIXEDIT_LAST_CHANGE_DATE:
670 SetAttrDefaultMark(hDlg, SA_FIXEDIT_LAST_CHANGE_DATE-1, // mark (un)changed
671 IsEditChanged(hDlg, SA_FIXEDIT_LAST_CHANGE_DATE, DlgParam->strInitStatusChangeDate) );
672 DlgParam->OStatusChangeTime = true;
673 break;
674 case SA_FIXEDIT_LAST_CHANGE_TIME:
675 SetAttrDefaultMark(hDlg, SA_FIXEDIT_LAST_CHANGE_TIME-1, // mark (un)changed
676 IsEditChanged(hDlg, SA_FIXEDIT_LAST_CHANGE_TIME, DlgParam->strInitStatusChangeTime) );
677 DlgParam->OStatusChangeTime = true;
678 break;
681 break;
684 case DN_GOTFOCUS: {
685 if (Param1 == SA_FIXEDIT_LAST_ACCESS_DATE || Param1 == SA_FIXEDIT_LAST_MODIFICATION_DATE
686 || Param1 == SA_FIXEDIT_LAST_CHANGE_DATE) {
687 if (GetDateFormat() == 2) {
688 if (reinterpret_cast<LPCWSTR>(SendDlgMessage(hDlg, DM_GETCONSTTEXTPTR, Param1, 0))[0]
689 == L' ') {
690 COORD Pos;
691 SendDlgMessage(hDlg, DM_GETCURSORPOS, Param1, (LONG_PTR)&Pos);
692 if (Pos.X == 0) {
693 Pos.X = 1;
694 SendDlgMessage(hDlg, DM_SETCURSORPOS, Param1, (LONG_PTR)&Pos);
699 } break;
701 case DM_SETATTR: {
702 FARString strDate, strTime;
704 if (Param2) // Set?
706 FILETIME ft;
708 if (Param2 == -1) {
709 WINPORT(GetSystemTimeAsFileTime)(&ft);
710 } else {
711 ft = *reinterpret_cast<PFILETIME>(Param2);
714 ConvertDate(ft, strDate, strTime, 12, FALSE, FALSE, 2, TRUE);
717 // Глянем на место, где был клик
718 int Set1 = -1;
719 int Set2 = Param1;
721 switch (Param1) {
722 case SA_TEXT_LAST_ACCESS:
723 Set1 = SA_FIXEDIT_LAST_ACCESS_DATE;
724 Set2 = SA_FIXEDIT_LAST_ACCESS_TIME;
725 DlgParam->OAccessTime = true;
726 break;
727 case SA_TEXT_LAST_MODIFICATION:
728 Set1 = SA_FIXEDIT_LAST_MODIFICATION_DATE;
729 Set2 = SA_FIXEDIT_LAST_MODIFICATION_TIME;
730 DlgParam->OModifyTime = true;
731 break;
732 case SA_TEXT_LAST_CHANGE:
733 Set1 = SA_FIXEDIT_LAST_CHANGE_DATE;
734 Set2 = SA_FIXEDIT_LAST_CHANGE_TIME;
735 DlgParam->OStatusChangeTime = true;
736 break;
738 case SA_FIXEDIT_LAST_ACCESS_DATE:
739 case SA_FIXEDIT_LAST_MODIFICATION_DATE:
740 case SA_FIXEDIT_LAST_CHANGE_DATE:
741 Set1 = Param1;
742 Set2 = -1;
743 break;
746 if (Set1 != -1) {
747 SendDlgMessage(hDlg, DM_SETTEXTPTR, Set1, (LONG_PTR)strDate.CPtr());
750 if (Set2 != -1) {
751 SendDlgMessage(hDlg, DM_SETTEXTPTR, Set2, (LONG_PTR)strTime.CPtr());
754 return TRUE;
758 return DefDlgProc(hDlg, Msg, Param1, Param2);
761 void ShellSetFileAttributesMsg(const wchar_t *Name)
763 static int Width = 54;
764 int WidthTemp;
766 if (Name && *Name)
767 WidthTemp = Max(StrLength(Name), 54);
768 else
769 Width = WidthTemp = 54;
771 WidthTemp = Min(WidthTemp, WidthNameForMessage);
772 Width = Max(Width, WidthTemp);
773 FARString strOutFileName = Name;
774 TruncPathStr(strOutFileName, Width);
775 CenterStr(strOutFileName, strOutFileName, Width + 4);
776 Message(0, 0, Msg::SetAttrTitle, Msg::SetAttrSetting, strOutFileName);
777 PreRedrawItem preRedrawItem = PreRedraw.Peek();
778 preRedrawItem.Param.Param1 = Name;
779 PreRedraw.SetParam(preRedrawItem.Param);
782 bool ReadFileTime(int Type, const wchar_t *Name, FILETIME &FileTime, const wchar_t *OSrcDate,
783 const wchar_t *OSrcTime)
785 bool Result = false;
786 FAR_FIND_DATA_EX ffd = {};
788 if (apiGetFindDataEx(Name, ffd)) {
789 LPFILETIME Times[] = {&ffd.ftLastWriteTime, &ffd.ftCreationTime, &ffd.ftLastAccessTime,
790 &ffd.ftChangeTime};
791 LPFILETIME OriginalFileTime = Times[Type];
792 FILETIME oft = {};
793 if (WINPORT(FileTimeToLocalFileTime)(OriginalFileTime, &oft)) {
794 SYSTEMTIME ost = {};
795 if (WINPORT(FileTimeToSystemTime)(&oft, &ost)) {
796 WORD DateN[3] = {};
797 GetFileDateAndTime(OSrcDate, DateN, ARRAYSIZE(DateN), GetDateSeparator());
798 WORD TimeN[4] = {};
799 GetFileDateAndTime(OSrcTime, TimeN, ARRAYSIZE(TimeN), GetTimeSeparator());
800 SYSTEMTIME st = {};
802 switch (GetDateFormat()) {
803 case 0:
804 st.wMonth = DateN[0] != (WORD)-1 ? DateN[0] : ost.wMonth;
805 st.wDay = DateN[1] != (WORD)-1 ? DateN[1] : ost.wDay;
806 st.wYear = DateN[2] != (WORD)-1 ? DateN[2] : ost.wYear;
807 break;
808 case 1:
809 st.wDay = DateN[0] != (WORD)-1 ? DateN[0] : ost.wDay;
810 st.wMonth = DateN[1] != (WORD)-1 ? DateN[1] : ost.wMonth;
811 st.wYear = DateN[2] != (WORD)-1 ? DateN[2] : ost.wYear;
812 break;
813 default:
814 st.wYear = DateN[0] != (WORD)-1 ? DateN[0] : ost.wYear;
815 st.wMonth = DateN[1] != (WORD)-1 ? DateN[1] : ost.wMonth;
816 st.wDay = DateN[2] != (WORD)-1 ? DateN[2] : ost.wDay;
817 break;
820 st.wHour = TimeN[0] != (WORD)-1 ? (TimeN[0]) : ost.wHour;
821 st.wMinute = TimeN[1] != (WORD)-1 ? (TimeN[1]) : ost.wMinute;
822 st.wSecond = TimeN[2] != (WORD)-1 ? (TimeN[2]) : ost.wSecond;
823 st.wMilliseconds = TimeN[3] != (WORD)-1 ? (TimeN[3]) : ost.wMilliseconds;
825 if (st.wYear < 100) {
826 st.wYear = static_cast<WORD>(ConvertYearToFull(st.wYear));
829 FILETIME lft = {};
830 if (WINPORT(SystemTimeToFileTime)(&st, &lft)) {
831 if (WINPORT(LocalFileTimeToFileTime)(&lft, &FileTime)) {
832 Result = WINPORT(CompareFileTime)(&FileTime, OriginalFileTime) != 0;
838 return Result;
841 void PR_ShellSetFileAttributesMsg()
843 PreRedrawItem preRedrawItem = PreRedraw.Peek();
844 ShellSetFileAttributesMsg(reinterpret_cast<const wchar_t *>(preRedrawItem.Param.Param1));
847 static void CheckFileOwnerGroup(DialogItemEx &ComboItem,
848 bool(WINAPI *GetFN)(const wchar_t *, const wchar_t *, FARString &),
849 FARString strComputerName,
850 FARString strSelName)
852 FARString strCur;
853 GetFN(strComputerName, strSelName, strCur);
854 if (ComboItem.strData.IsEmpty()) {
855 ComboItem.strData = strCur;
857 else if (ComboItem.strData != strCur) {
858 ComboItem.strData = Msg::SetAttrOwnerMultiple;
862 static bool ApplyFileOwnerGroupIfChanged(DialogItemEx &ComboItem,
863 int (*ESetFN)(LPCWSTR Name, LPCWSTR Owner, int SkipMode),
864 int &SkipMode, const FARString &strSelName, const FARString &strInit, bool force = false)
866 if (!ComboItem.strData.IsEmpty() && (force || StrCmp(strInit, ComboItem.strData))) {
867 int Result = ESetFN(strSelName, ComboItem.strData, SkipMode);
868 if (Result == SETATTR_RET_SKIPALL) {
869 SkipMode = SETATTR_RET_SKIP;
871 else if (Result == SETATTR_RET_ERROR) {
872 return false;
875 return true;
878 static void ApplyFSFileFlags(DialogItemEx *AttrDlg, const FARString &strSelName, DWORD FileAttr)
880 FSFileFlagsSafe FFFlags(strSelName.GetMB(), FileAttr);
881 if (AttrDlg[SA_CHECKBOX_IMMUTABLE].Selected == BSTATE_CHECKED
882 || AttrDlg[SA_CHECKBOX_IMMUTABLE].Selected == BSTATE_UNCHECKED) {
883 FFFlags.SetImmutable(AttrDlg[SA_CHECKBOX_IMMUTABLE].Selected != BSTATE_UNCHECKED);
885 if (AttrDlg[SA_CHECKBOX_APPEND].Selected == BSTATE_CHECKED
886 || AttrDlg[SA_CHECKBOX_APPEND].Selected == BSTATE_UNCHECKED) {
887 FFFlags.SetAppend(AttrDlg[SA_CHECKBOX_APPEND].Selected != BSTATE_UNCHECKED);
889 #if defined(__APPLE__) || defined(__FreeBSD__) || defined(__DragonFly__)
890 if (AttrDlg[SA_CHECKBOX_HIDDEN].Selected == BSTATE_CHECKED
891 || AttrDlg[SA_CHECKBOX_HIDDEN].Selected == BSTATE_UNCHECKED) {
892 FFFlags.SetHidden(AttrDlg[SA_CHECKBOX_HIDDEN].Selected != BSTATE_UNCHECKED);
894 #endif
895 FFFlags.Apply(strSelName.GetMB());
898 class ListPwGrEnt {
899 std::set<FARString> Set; // sorts and prevents duplicates
900 std::vector<FarListItem> Items;
901 FarList List;
902 void Append(const wchar_t *s) { Set.emplace(s); }
903 void Append(const char *s) { Set.emplace(s); }
904 public:
905 ListPwGrEnt(bool bGroups, int SelCount);
906 FarList *GetFarList() {
907 return &List;
911 ListPwGrEnt::ListPwGrEnt(bool bGroups, int SelCount)
913 if (SelCount >= 2)
914 Append(Msg::SetAttrOwnerMultiple);
916 if (!bGroups) { // usernames
917 struct passwd *pw;
918 setpwent();
919 while ((pw = getpwent()) != NULL) {
920 Append(pw->pw_name);
922 endpwent();
924 else { // groups
925 struct group *gr;
926 setgrent();
927 while ((gr = getgrent()) != NULL) {
928 Append(gr->gr_name);
930 endgrent();
933 Items.reserve(Set.size());
934 for (const auto &Str: Set) {
935 Items.emplace_back();
936 Items.back().Flags = 0;
937 Items.back().Text = Str.CPtr();
940 List.ItemsNumber = Items.size();
941 List.Items = Items.data();
944 bool ShellSetFileAttributes(Panel *SrcPanel, LPCWSTR Object)
946 std::vector<FARString> SelectedNames;
948 SudoClientRegion scr;
950 ChangePriority ChPriority(ChangePriority::NORMAL);
951 short DlgX = 70, DlgY = 25;
953 int SelCount = SrcPanel ? SrcPanel->GetSelCount() : 1;
955 if (!SelCount) {
956 return false;
959 const short ColX1Of3 = 5;
960 const short ColX2Of3 = ColX1Of3 + (DlgX - 3) / 3;
961 #if defined(__APPLE__) || defined(__FreeBSD__) || defined(__DragonFly__)
962 const short ColX3Of3 = ColX2Of3 + (DlgX - 3) / 3;
963 #endif
964 static const wchar_t VerticalLine[] = {BoxSymbols[BS_V1], BoxSymbols[BS_V1], BoxSymbols[BS_V1], 0};
966 DialogDataEx AttrDlgData[] = {
967 {DI_DOUBLEBOX, 3, 1, short(DlgX - 4), short(DlgY - 2), {}, 0, Msg::SetAttrTitle},
968 {DI_TEXT, -1, 2, 0, 2, {}, 0, Msg::SetAttrFor},
969 {DI_TEXT, -1, 3, 0, 3, {}, DIF_SHOWAMPERSAND, L""},
970 {DI_TEXT, 3, 4, 0, 4, {}, DIF_SEPARATOR, L""},
971 {DI_TEXT, 5, 5, 17, 5, {}, DIF_FOCUS, Msg::SetAttrBriefInfo}, // if symlink in will Button & need first focus here
972 {DI_TEXT, 18, 5, 18, 5, {}, 0, L""},
973 {DI_EDIT, 19, 5, short(DlgX - 6), 5, {}, DIF_SELECTONENTRY | DIF_FOCUS | DIF_READONLY, L""}, // not readonly only if symlink
974 {DI_TEXT, 3, 6, 0, 6, {}, DIF_SEPARATOR, Msg::SetAttrOwnerTitle},
975 {DI_TEXT, 5, 7, 17, 7, {}, 0, Msg::SetAttrOwner},
976 {DI_TEXT, 18, 7, 18, 7, {}, 0, L""},
977 {DI_COMBOBOX, 19, 7, short(DlgX-6), 7, {}, DIF_DROPDOWNLIST|DIF_LISTNOAMPERSAND|DIF_LISTWRAPMODE,L""},
978 {DI_TEXT, 5, 8, 17, 8, {}, 0, Msg::SetAttrGroup},
979 {DI_TEXT, 18, 8, 18, 8, {}, 0, L""},
980 {DI_COMBOBOX, 19, 8, short(DlgX-6), 8, {}, DIF_DROPDOWNLIST|DIF_LISTNOAMPERSAND|DIF_LISTWRAPMODE,L""},
982 {DI_TEXT, 3, 9, 0, 9, {}, DIF_SEPARATOR, Msg::SetAttrModeTitle},
983 {DI_VTEXT, 39, 10, 39, 12, {}, DIF_BOXCOLOR, VerticalLine},
984 {DI_VTEXT, 52, 10, 52, 12, {}, DIF_BOXCOLOR, VerticalLine},
986 {DI_TEXT, 5, 10, 17, 10, {}, 0, Msg::SetAttrAccessUser},
987 {DI_TEXT, 18, 10, 18, 10, {}, 0, L""},
988 {DI_CHECKBOX, 19, 10, 23, 10, {}, DIF_3STATE, Msg::SetAttrAccessUserRead},
989 {DI_TEXT, 25, 10, 25, 10, {}, 0, L""},
990 {DI_CHECKBOX, 26, 10, 30, 10, {}, DIF_3STATE, Msg::SetAttrAccessUserWrite},
991 {DI_TEXT, 32, 10, 32, 10, {}, 0, L""},
992 {DI_CHECKBOX, 33, 10, 37, 10, {}, DIF_3STATE, Msg::SetAttrAccessUserExecute},
993 {DI_TEXT, 5, 11, 18, 11, {}, 0, Msg::SetAttrAccessGroup},
994 {DI_TEXT, 18, 11, 18, 11, {}, 0, L""},
995 {DI_CHECKBOX, 19, 11, 23, 11, {}, DIF_3STATE, Msg::SetAttrAccessGroupRead},
996 {DI_TEXT, 25, 11, 25, 11, {}, 0, L""},
997 {DI_CHECKBOX, 26, 11, 30, 11, {}, DIF_3STATE, Msg::SetAttrAccessGroupWrite},
998 {DI_TEXT, 32, 11, 32, 11, {}, 0, L""},
999 {DI_CHECKBOX, 33, 11, 37, 11, {}, DIF_3STATE, Msg::SetAttrAccessGroupExecute},
1000 {DI_TEXT, 5, 12, 18, 12, {}, 0, Msg::SetAttrAccessOther},
1001 {DI_TEXT, 18, 12, 18, 12, {}, 0, L""},
1002 {DI_CHECKBOX, 19, 12, 23, 12, {}, DIF_3STATE, Msg::SetAttrAccessOtherRead},
1003 {DI_TEXT, 25, 12, 25, 12, {}, 0, L""},
1004 {DI_CHECKBOX, 26, 12, 30, 12, {}, DIF_3STATE, Msg::SetAttrAccessOtherWrite},
1005 {DI_TEXT, 32, 12, 32, 12, {}, 0, L""},
1006 {DI_CHECKBOX, 33, 12, 37, 12, {}, DIF_3STATE, Msg::SetAttrAccessOtherExecute},
1008 {DI_TEXT, 40, 10, 40, 10, {}, 0, L""},
1009 {DI_CHECKBOX, 41, 10, 53, 10, {}, DIF_3STATE, Msg::SetAttrSUID},
1010 {DI_TEXT, 40, 11, 40, 11, {}, 0, L""},
1011 {DI_CHECKBOX, 41, 11, 53, 11, {}, DIF_3STATE, Msg::SetAttrSGID},
1012 {DI_TEXT, 40, 12, 40, 12, {}, 0, L""},
1013 {DI_CHECKBOX, 41, 12, 53, 12, {}, DIF_3STATE, Msg::SetAttrSticky},
1015 {DI_TEXT, 53, 10, 63, 10, {}, 0, L"O&ctal: SUGO"},
1016 {DI_TEXT, 54, 11, 57, 11, {}, 0, L""},
1017 {DI_TEXT, 59, 11, 59, 11, {}, 0, L""},
1018 {DI_FIXEDIT, 60, 11, 63, 11, {(DWORD_PTR)L"####"}, DIF_MASKEDIT, L""},
1019 {DI_BUTTON, 53, 12, 63, 12, {}, DIF_BTNNOCLOSE, Msg::SetAttrModeOriginal},
1021 {DI_TEXT, 3, 13, 0, 13, {}, DIF_SEPARATOR, Msg::SetAttrAttributesTitle},
1022 {DI_TEXT, ColX1Of3, 14, ColX1Of3, 14, {}, 0, L""},
1023 {DI_CHECKBOX, short(ColX1Of3+1), 14, 0, 14, {}, DIF_FOCUS | DIF_3STATE, Msg::SetAttrImmutable},
1024 {DI_TEXT, ColX2Of3, 14, ColX2Of3, 14, {}, 0, L""},
1025 {DI_CHECKBOX, short(ColX2Of3+1), 14, 0, 14, {}, DIF_3STATE, Msg::SetAttrAppend},
1026 #if defined(__APPLE__) || defined(__FreeBSD__) || defined(__DragonFly__)
1027 {DI_TEXT, ColX3Of3, 14, ColX3Of3, 14, {}, 0, L""},
1028 {DI_CHECKBOX, short(ColX3Of3+1), 14, 0, 14, {}, DIF_3STATE, Msg::SetAttrHidden},
1029 #endif
1031 {DI_TEXT, 3, 15, 0, 15, {}, DIF_SEPARATOR, L""},
1032 {DI_TEXT, short(DlgX - 29), 16, 0, 16, {}, 0, L""},
1033 {DI_TEXT, 5, 17, 0, 17, {}, 0, Msg::SetAttrAccessTime},
1034 {DI_TEXT, short(DlgX - 30), 17, short(DlgX - 30), 17, {}, 0, L""},
1035 {DI_FIXEDIT, short(DlgX - 29), 17, short(DlgX - 19), 17, {}, DIF_MASKEDIT, L""},
1036 {DI_TEXT, short(DlgX - 18), 17, short(DlgX - 18), 17, {}, 0, L""},
1037 {DI_FIXEDIT, short(DlgX - 17), 17, short(DlgX - 6), 17, {}, DIF_MASKEDIT, L""},
1038 {DI_TEXT, 5, 18, 0, 18, {}, 0, Msg::SetAttrModificationTime},
1039 {DI_TEXT, short(DlgX - 30), 18, short(DlgX - 30), 18, {}, 0, L""},
1040 {DI_FIXEDIT, short(DlgX - 29), 18, short(DlgX - 19), 18, {}, DIF_MASKEDIT, L""},
1041 {DI_TEXT, short(DlgX - 18), 18, short(DlgX - 18), 18, {}, 0, L""},
1042 {DI_FIXEDIT, short(DlgX - 17), 18, short(DlgX - 6), 18, {}, DIF_MASKEDIT, L""},
1043 {DI_TEXT, 5, 19, 0, 19, {}, 0, Msg::SetAttrStatusChangeTime},
1044 {DI_TEXT, short(DlgX - 30), 19, short(DlgX - 30), 19, {}, 0, L""},
1045 {DI_FIXEDIT, short(DlgX - 29), 19, short(DlgX - 19), 19, {}, DIF_MASKEDIT | DIF_READONLY, L""},
1046 {DI_TEXT, short(DlgX - 18), 19, short(DlgX - 18), 19, {}, 0, L""},
1047 {DI_FIXEDIT, short(DlgX - 17), 19, short(DlgX - 6), 19, {}, DIF_MASKEDIT | DIF_READONLY, L""},
1048 {DI_BUTTON, 0, 20, 0, 20, {}, DIF_CENTERGROUP | DIF_BTNNOCLOSE, Msg::SetAttrOriginal},
1049 {DI_BUTTON, 0, 20, 0, 20, {}, DIF_CENTERGROUP | DIF_BTNNOCLOSE, Msg::SetAttrCurrent},
1050 {DI_BUTTON, 0, 20, 0, 20, {}, DIF_CENTERGROUP | DIF_BTNNOCLOSE, Msg::SetAttrBlank},
1051 {DI_TEXT, 3, 21, 0, 21, {}, DIF_SEPARATOR | DIF_HIDDEN, L""},
1052 {DI_CHECKBOX, 5, 22, 0, 22, {}, DIF_DISABLE | DIF_HIDDEN, Msg::SetAttrSubfolders},
1053 {DI_TEXT, 3, short(DlgY - 4), 0, short(DlgY - 4), {}, DIF_SEPARATOR, L""},
1054 {DI_TEXT, 5, short(DlgY - 3), 0, short(DlgY - 3), {}, DIF_DISABLE | DIF_HIDDEN, Msg::SetAttrSymlinkExplain1},
1055 {DI_TEXT, 5, short(DlgY - 2), 0, short(DlgY - 2), {}, DIF_DISABLE | DIF_HIDDEN, Msg::SetAttrSymlinkExplain2},
1056 {DI_BUTTON, 0, short(DlgY - 3), 0, short(DlgY - 3), {}, DIF_DEFAULT | DIF_CENTERGROUP, Msg::SetAttrSet},
1057 {DI_BUTTON, 0, short(DlgY - 3), 0, short(DlgY - 3), {}, DIF_CENTERGROUP, Msg::Cancel}
1059 MakeDialogItemsEx(AttrDlgData, AttrDlg);
1060 SetAttrDlgParam DlgParam{};
1062 ListPwGrEnt Owners(false, SelCount);
1063 ListPwGrEnt Groups(true, SelCount);
1064 AttrDlg[SA_COMBO_OWNER].ListItems = Owners.GetFarList();
1065 AttrDlg[SA_COMBO_GROUP].ListItems = Groups.GetFarList();
1067 if (SrcPanel && SrcPanel->GetMode() == PLUGIN_PANEL) {
1068 OpenPluginInfo Info;
1069 HANDLE hPlugin = SrcPanel->GetPluginHandle();
1071 if (hPlugin == INVALID_HANDLE_VALUE) {
1072 return false;
1075 CtrlObject->Plugins.GetOpenPluginInfo(hPlugin, &Info);
1077 if (!(Info.Flags & OPIF_REALNAMES)) {
1078 AttrDlg[SA_BUTTON_SET].Flags|= DIF_DISABLE;
1079 //AttrDlg[SA_BUTTON_BRIEFINFO].Flags|= DIF_DISABLE;
1080 DlgParam.Plugin = true;
1085 DWORD FileAttr = INVALID_FILE_ATTRIBUTES, FileMode = 0;
1086 FARString strSelName;
1087 FAR_FIND_DATA_EX FindData;
1088 if (SrcPanel) {
1089 SrcPanel->GetSelName(nullptr, FileAttr, FileMode);
1090 SrcPanel->GetSelName(&strSelName, FileAttr, FileMode, &FindData);
1091 /* if (!FileMode) {
1092 FAR_FIND_DATA_EX FindData2;
1093 if (apiGetFindDataEx(strSelName, FindData2) && FindData2.dwUnixMode) {
1094 FindData = FindData2;
1095 FileAttr = FindData.dwFileAttributes;
1096 FileMode = FindData.dwUnixMode;
1099 } else {
1100 strSelName = Object;
1101 apiGetFindDataEx(Object, FindData);
1102 FileAttr = FindData.dwFileAttributes;
1103 FileMode = FindData.dwUnixMode;
1106 // fprintf(stderr, "FileMode=%u\n", FileMode);
1108 if (SelCount == 1 && TestParentFolderName(strSelName))
1109 return false;
1111 wchar_t DateSeparator = GetDateSeparator();
1112 wchar_t TimeSeparator = GetTimeSeparator();
1113 wchar_t DecimalSeparator = GetDecimalSeparator();
1114 LPCWSTR FmtMask1 = L"99%c99%c99%c99N", FmtMask2 = L"99%c99%c9999N", FmtMask3 = L"N9999%c99%c99";
1115 FARString strDMask, strTMask;
1116 strTMask.Format(FmtMask1, TimeSeparator, TimeSeparator, DecimalSeparator);
1118 switch (GetDateFormat()) {
1119 case 0:
1120 AttrDlg[SA_TEXT_TITLEDATE].strData.Format(Msg::SetAttrTimeTitle1, DateSeparator,
1121 DateSeparator, TimeSeparator, TimeSeparator, DecimalSeparator);
1122 strDMask.Format(FmtMask2, DateSeparator, DateSeparator);
1123 break;
1124 case 1:
1125 AttrDlg[SA_TEXT_TITLEDATE].strData.Format(Msg::SetAttrTimeTitle2, DateSeparator,
1126 DateSeparator, TimeSeparator, TimeSeparator, DecimalSeparator);
1127 strDMask.Format(FmtMask2, DateSeparator, DateSeparator);
1128 break;
1129 default:
1130 AttrDlg[SA_TEXT_TITLEDATE].strData.Format(Msg::SetAttrTimeTitle3, DateSeparator,
1131 DateSeparator, TimeSeparator, TimeSeparator, DecimalSeparator);
1132 strDMask.Format(FmtMask3, DateSeparator, DateSeparator);
1133 break;
1136 AttrDlg[SA_FIXEDIT_LAST_ACCESS_DATE].strMask = AttrDlg[SA_FIXEDIT_LAST_MODIFICATION_DATE].strMask =
1137 AttrDlg[SA_FIXEDIT_LAST_CHANGE_DATE].strMask = strDMask;
1139 AttrDlg[SA_FIXEDIT_LAST_ACCESS_TIME].strMask = AttrDlg[SA_FIXEDIT_LAST_MODIFICATION_TIME].strMask =
1140 AttrDlg[SA_FIXEDIT_LAST_CHANGE_TIME].strMask = strTMask;
1141 bool FolderPresent = false;//, LinkPresent = false;
1142 int FolderCount = 0, Link2FileCount = 0, Link2DirCount = 0;
1143 FARString strLinkName;
1145 if (SelCount == 1) {
1146 FSFileFlagsSafe FFFlags(strSelName.GetMB(), FileAttr);
1147 if (FileAttr & FILE_ATTRIBUTE_REPARSE_POINT) {
1148 DlgParam.SymlinkButtonTitles[0] = Msg::SetAttrSymlinkObject;
1149 DlgParam.SymlinkButtonTitles[1] = Msg::SetAttrSymlinkObjectInfo;
1150 DlgParam.SymlinkButtonTitles[2] = Msg::SetAttrSymlinkContent;
1152 AttrDlg[SA_TXTBTN_INFO].Type = DI_BUTTON;
1153 AttrDlg[SA_TXTBTN_INFO].strData = DlgParam.SymlinkButtonTitles[2];
1154 ReadSymlink(strSelName, DlgParam.SymLink);
1155 AttrDlg[SA_EDIT_INFO].strData = DlgParam.SymLink;
1156 AttrDlg[SA_EDIT_INFO].Flags &= ~DIF_READONLY; // not readonly only if symlink
1158 Link2FileCount=1; // only for show symlink explain text
1160 else if (FileAttr & FILE_ATTRIBUTE_DEVICE_CHAR)
1161 AttrDlg[SA_EDIT_INFO].strData = Msg::FileFilterAttrDevChar;
1162 else if (FileAttr & FILE_ATTRIBUTE_DEVICE_BLOCK)
1163 AttrDlg[SA_EDIT_INFO].strData = Msg::FileFilterAttrDevBlock;
1164 else if (FileAttr & FILE_ATTRIBUTE_DEVICE_FIFO)
1165 AttrDlg[SA_EDIT_INFO].strData = Msg::FileFilterAttrDevFIFO;
1166 else if (FileAttr & FILE_ATTRIBUTE_DEVICE_SOCK)
1167 AttrDlg[SA_EDIT_INFO].strData = Msg::FileFilterAttrDevSock;
1168 else {
1169 AttrDlg[SA_EDIT_INFO].strData = BriefInfo(strSelName);
1173 if (FileAttr & FILE_ATTRIBUTE_DIRECTORY) {
1174 if (!DlgParam.Plugin) {
1175 FileAttr = apiGetFileAttributes(strSelName);
1178 //_SVS(SysLog(L"SelName=%ls FileAttr=0x%08X",SelName,FileAttr));
1179 AttrDlg[SA_SEPARATOR4].Flags&= ~DIF_HIDDEN;
1180 AttrDlg[SA_CHECKBOX_SUBFOLDERS].Flags&= ~(DIF_DISABLE | DIF_HIDDEN);
1181 AttrDlg[SA_CHECKBOX_SUBFOLDERS].Selected =
1182 Opt.SetAttrFolderRules ? BSTATE_UNCHECKED : BSTATE_CHECKED;
1183 AttrDlg[SA_DOUBLEBOX].Y2+= 2;
1184 for (int i = SA_SEPARATOR5; i <= SA_BUTTON_CANCEL; i++) {
1185 AttrDlg[i].Y1+= 2;
1186 AttrDlg[i].Y2+= 2;
1188 DlgY+= 2;
1190 if (Opt.SetAttrFolderRules) {
1191 if (DlgParam.Plugin || apiGetFindDataEx(strSelName, FindData)) {
1192 ConvertDate(FindData.ftUnixAccessTime, AttrDlg[SA_FIXEDIT_LAST_ACCESS_DATE].strData,
1193 AttrDlg[SA_FIXEDIT_LAST_ACCESS_TIME].strData, 12, FALSE, FALSE, 2, TRUE);
1194 ConvertDate(FindData.ftUnixModificationTime,
1195 AttrDlg[SA_FIXEDIT_LAST_MODIFICATION_DATE].strData,
1196 AttrDlg[SA_FIXEDIT_LAST_MODIFICATION_TIME].strData, 12, FALSE, FALSE, 2,
1197 TRUE);
1198 ConvertDate(FindData.ftUnixStatusChangeTime,
1199 AttrDlg[SA_FIXEDIT_LAST_CHANGE_DATE].strData,
1200 AttrDlg[SA_FIXEDIT_LAST_CHANGE_TIME].strData, 12, FALSE, FALSE, 2, TRUE);
1204 FolderPresent = TRUE;
1207 if ((FileAttr != INVALID_FILE_ATTRIBUTES)
1208 && ((FileAttr & FILE_ATTRIBUTE_DIRECTORY) == 0 || Opt.SetAttrFolderRules)) {
1209 for (size_t i = 0; i < ARRAYSIZE(AP); i++) {
1210 AttrDlg[AP[i].Item].Selected =
1211 (FileMode & AP[i].Mode) ? BSTATE_CHECKED : BSTATE_UNCHECKED;
1213 AttrDlg[SA_CHECKBOX_IMMUTABLE].Selected = FFFlags.Immutable() ? BSTATE_CHECKED : BSTATE_UNCHECKED;
1214 AttrDlg[SA_CHECKBOX_APPEND].Selected = FFFlags.Append() ? BSTATE_CHECKED : BSTATE_UNCHECKED;
1215 #if defined(__APPLE__) || defined(__FreeBSD__) || defined(__DragonFly__)
1216 AttrDlg[SA_CHECKBOX_HIDDEN].Selected = FFFlags.Hidden() ? BSTATE_CHECKED : BSTATE_UNCHECKED;
1217 #endif
1220 for (auto i : PreserveOriginalIDs) {
1221 AttrDlg[i].Flags&= ~DIF_3STATE;
1224 AttrDlg[SA_TEXT_NAME].strData = strSelName;
1225 TruncStr(AttrDlg[SA_TEXT_NAME].strData, DlgX - 10);
1227 const SETATTRDLG Dates[] = {SA_FIXEDIT_LAST_ACCESS_DATE, SA_FIXEDIT_LAST_MODIFICATION_DATE,
1228 SA_FIXEDIT_LAST_CHANGE_DATE};
1229 const SETATTRDLG Times[] = {SA_FIXEDIT_LAST_ACCESS_TIME, SA_FIXEDIT_LAST_MODIFICATION_TIME,
1230 SA_FIXEDIT_LAST_CHANGE_TIME};
1231 const PFILETIME TimeValues[] = {&FindData.ftUnixAccessTime, &FindData.ftUnixModificationTime,
1232 &FindData.ftUnixStatusChangeTime};
1234 if (DlgParam.Plugin || (!DlgParam.Plugin && apiGetFindDataEx(strSelName, FindData))) {
1235 for (size_t i = 0; i < ARRAYSIZE(Dates); i++) {
1236 ConvertDate(*TimeValues[i], AttrDlg[Dates[i]].strData, AttrDlg[Times[i]].strData, 12,
1237 FALSE, FALSE, 2, TRUE);
1241 FARString strComputerName;
1242 if (SrcPanel) {
1243 FARString strCurDir;
1244 SrcPanel->GetCurDir(strCurDir);
1247 GetFileOwner(strComputerName, strSelName, AttrDlg[SA_COMBO_OWNER].strData);
1248 GetFileGroup(strComputerName, strSelName, AttrDlg[SA_COMBO_GROUP].strData);
1249 } // end of if (SelCount==1)
1250 else {
1251 for (size_t i = 0; i < ARRAYSIZE(AP); i++) {
1252 AttrDlg[AP[i].Item].Selected = BSTATE_3STATE;
1255 AttrDlg[SA_FIXEDIT_LAST_ACCESS_DATE].strData.Clear();
1256 AttrDlg[SA_FIXEDIT_LAST_ACCESS_TIME].strData.Clear();
1257 AttrDlg[SA_FIXEDIT_LAST_MODIFICATION_DATE].strData.Clear();
1258 AttrDlg[SA_FIXEDIT_LAST_MODIFICATION_TIME].strData.Clear();
1259 AttrDlg[SA_FIXEDIT_LAST_CHANGE_DATE].strData.Clear();
1260 AttrDlg[SA_FIXEDIT_LAST_CHANGE_TIME].strData.Clear();
1261 AttrDlg[SA_BUTTON_ORIGINAL].Flags|= DIF_DISABLE;
1262 AttrDlg[SA_TEXT_NAME].strData = Msg::SetAttrSelectedObjects;
1264 for (auto i : PreserveOriginalIDs) {
1265 AttrDlg[i].Selected = BSTATE_UNCHECKED;
1268 // проверка - есть ли среди выделенных - каталоги?
1269 // так же проверка на атрибуты
1270 // так же подсчет числа каталогов и symlinks
1271 if (SrcPanel) {
1272 SrcPanel->GetSelName(nullptr, FileAttr, FileMode);
1274 FolderPresent = false;
1275 FolderCount = 0; Link2FileCount = 0; Link2DirCount = 0;
1277 if (SrcPanel) {
1278 FARString strComputerName;
1279 FARString strCurDir;
1280 SrcPanel->GetCurDir(strCurDir);
1282 while (SrcPanel->GetSelName(&strSelName, FileAttr, FileMode, &FindData)) {
1283 if (FileAttr & FILE_ATTRIBUTE_DIRECTORY) {
1284 if (FileAttr & FILE_ATTRIBUTE_REPARSE_POINT)
1285 Link2DirCount++;
1286 else
1287 FolderCount++;
1288 if (!FolderPresent) {
1289 FolderPresent = true;
1290 AttrDlg[SA_SEPARATOR4].Flags&= ~DIF_HIDDEN;
1291 AttrDlg[SA_CHECKBOX_SUBFOLDERS].Flags&= ~(DIF_DISABLE | DIF_HIDDEN);
1292 AttrDlg[SA_DOUBLEBOX].Y2+= 2;
1293 for (int i = SA_SEPARATOR5; i <= SA_BUTTON_CANCEL; i++) {
1294 AttrDlg[i].Y1+= 2;
1295 AttrDlg[i].Y2+= 2;
1297 DlgY+= 2;
1300 else if (FileAttr & FILE_ATTRIBUTE_REPARSE_POINT)
1301 Link2FileCount++;
1303 for (size_t i = 0; i < ARRAYSIZE(AP); i++) {
1304 if (FileMode & AP[i].Mode) {
1305 AttrDlg[AP[i].Item].Selected++;
1308 CheckFileOwnerGroup(AttrDlg[SA_COMBO_OWNER], GetFileOwner, strComputerName, strSelName);
1309 CheckFileOwnerGroup(AttrDlg[SA_COMBO_GROUP], GetFileGroup, strComputerName, strSelName);
1311 FSFileFlagsSafe FFFlags(strSelName.GetMB(), FileAttr);
1312 if (FFFlags.Immutable())
1313 AttrDlg[SA_CHECKBOX_IMMUTABLE].Selected++;
1314 if (FFFlags.Append())
1315 AttrDlg[SA_CHECKBOX_APPEND].Selected++;
1316 #if defined(__APPLE__) || defined(__FreeBSD__) || defined(__DragonFly__)
1317 if (FFFlags.Hidden())
1318 AttrDlg[SA_CHECKBOX_HIDDEN].Selected++;
1319 #endif
1321 } else {
1322 // BUGBUG, copy-paste
1323 if (!FolderPresent && (FindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) {
1324 FolderPresent = true;
1325 AttrDlg[SA_SEPARATOR4].Flags&= ~DIF_HIDDEN;
1326 AttrDlg[SA_CHECKBOX_SUBFOLDERS].Flags&= ~(DIF_DISABLE | DIF_HIDDEN);
1327 AttrDlg[SA_DOUBLEBOX].Y2+= 2;
1328 for (int i = SA_SEPARATOR5; i <= SA_BUTTON_CANCEL; i++) {
1329 AttrDlg[i].Y1+= 2;
1330 AttrDlg[i].Y2+= 2;
1332 DlgY+= 2;
1334 for (size_t i = 0; i < ARRAYSIZE(AP); i++) {
1335 if (FindData.dwUnixMode & AP[i].Mode) {
1336 AttrDlg[AP[i].Item].Selected++;
1340 if (SrcPanel) {
1341 SrcPanel->GetSelName(nullptr, FileAttr, FileMode);
1342 SrcPanel->GetSelName(&strSelName, FileAttr, FileMode, &FindData);
1345 // выставим "неопределенку" или то, что нужно
1346 for (auto i : PreserveOriginalIDs) {
1347 // снимаем 3-state, если "есть все или нет ничего"
1348 // за исключением случая, если есть Фолдер среди объектов
1349 if ((!AttrDlg[i].Selected || AttrDlg[i].Selected >= SelCount) && !FolderPresent) {
1350 AttrDlg[i].Flags&= ~DIF_3STATE;
1353 AttrDlg[i].Selected = (AttrDlg[i].Selected >= SelCount)
1354 ? BST_CHECKED
1355 : (!AttrDlg[i].Selected ? BSTATE_UNCHECKED : BSTATE_3STATE);
1359 FARString strTmp, strSep=L" (";
1360 int FilesCount = SelCount-FolderCount-Link2FileCount-Link2DirCount;
1361 strTmp.Format(Msg::SetAttrInfoSelAll, SelCount);
1362 if (FolderCount>0)
1363 { strTmp.AppendFormat(Msg::SetAttrInfoSelDirs, strSep.CPtr(), FolderCount); strSep=L", "; }
1364 if (FilesCount>0)
1365 { strTmp.AppendFormat(Msg::SetAttrInfoSelFiles, strSep.CPtr(), FilesCount); strSep=L", "; }
1366 if (Link2DirCount>0)
1367 { strTmp.AppendFormat(Msg::SetAttrInfoSelSymDirs, strSep.CPtr(), Link2DirCount); strSep=L", "; }
1368 if (Link2FileCount>0)
1369 strTmp.AppendFormat(Msg::SetAttrInfoSelSymFiles, strSep.CPtr(), Link2FileCount);
1370 strTmp.Append(L')');
1371 AttrDlg[SA_EDIT_INFO].strData = strTmp;
1376 // поведение для каталогов как у 1.65?
1377 if (FolderPresent && !Opt.SetAttrFolderRules) {
1378 AttrDlg[SA_CHECKBOX_SUBFOLDERS].Selected = BSTATE_CHECKED;
1379 AttrDlg[SA_FIXEDIT_LAST_ACCESS_DATE].strData.Clear();
1380 AttrDlg[SA_FIXEDIT_LAST_ACCESS_TIME].strData.Clear();
1381 AttrDlg[SA_FIXEDIT_LAST_MODIFICATION_DATE].strData.Clear();
1382 AttrDlg[SA_FIXEDIT_LAST_MODIFICATION_TIME].strData.Clear();
1383 AttrDlg[SA_FIXEDIT_LAST_CHANGE_DATE].strData.Clear();
1384 AttrDlg[SA_FIXEDIT_LAST_CHANGE_TIME].strData.Clear();
1386 for (auto i : PreserveOriginalIDs) {
1387 AttrDlg[i].Selected = BSTATE_3STATE;
1388 AttrDlg[i].Flags|= DIF_3STATE;
1392 // запомним состояние переключателей.
1393 for (size_t i = 0; i < ARRAYSIZE(PreserveOriginalIDs); ++i) {
1394 DlgParam.OriginalCBAttr[i] = DlgParam.OriginalCBAttr0[i] =AttrDlg[PreserveOriginalIDs[i]].Selected;
1395 DlgParam.OriginalCBAttr2[i] = -1;
1396 DlgParam.OriginalCBFlag[i] = AttrDlg[PreserveOriginalIDs[i]].Flags;
1399 // explain text about symlinks properties
1400 if (FolderPresent || Link2FileCount>0 || Link2DirCount>0) {
1401 AttrDlg[SC_SYMLINK_PROPERTIES_EXPLAIN_TEXT_1].Flags&= ~DIF_HIDDEN;
1402 AttrDlg[SC_SYMLINK_PROPERTIES_EXPLAIN_TEXT_2].Flags&= ~DIF_HIDDEN;
1403 AttrDlg[SA_DOUBLEBOX].Y2+= 2;
1404 for (int i = SC_SYMLINK_PROPERTIES_EXPLAIN_TEXT_2+1; i <= SA_BUTTON_CANCEL; i++) {
1405 AttrDlg[i].Y1+= 2;
1406 AttrDlg[i].Y2+= 2;
1408 DlgY+= 2;
1411 DlgParam.strOwner = AttrDlg[SA_COMBO_OWNER].strData;
1412 DlgParam.strGroup = AttrDlg[SA_COMBO_GROUP].strData;
1413 DlgParam.strInitOwner = DlgParam.strOwner; DlgParam.strInitGroup = DlgParam.strGroup;
1415 DlgParam.DialogMode = ((SelCount == 1 && !(FileAttr & FILE_ATTRIBUTE_DIRECTORY))
1416 ? MODE_FILE
1417 : (SelCount == 1 ? MODE_FOLDER : MODE_MULTIPLE));
1418 DlgParam.strSelName = strSelName;
1419 DlgParam.OSubfoldersState = static_cast<FARCHECKEDSTATE>(AttrDlg[SA_CHECKBOX_SUBFOLDERS].Selected);
1421 Dialog Dlg(AttrDlg, ARRAYSIZE(AttrDlgData), SetAttrDlgProc, (LONG_PTR)&DlgParam);
1422 Dlg.SetHelp(L"FileAttrDlg"); // ^ - это одиночный диалог!
1423 Dlg.SetId(FileAttrDlgId);
1425 /*if (LinkPresent) {
1426 DlgY++;
1429 Dlg.SetPosition(-1, -1, DlgX, DlgY);
1430 SetAttrCalcBitsCharFromModeCheckBoxes(&Dlg); // set octal
1431 // initial values at dialog start for control change marker
1432 DlgParam.strInitInfoSymLink = reinterpret_cast<LPCWSTR>(SendDlgMessage(&Dlg, DM_GETCONSTTEXTPTR, SA_EDIT_INFO, 0));
1433 DlgParam.strInitOctal = reinterpret_cast<LPCWSTR>(SendDlgMessage(&Dlg, DM_GETCONSTTEXTPTR, SA_FIXEDIT_MODE_OCTAL, 0));
1434 SendDlgMessage(&Dlg, DM_SETTEXTPTR, SA_TEXT_MODE_OCTAL_ORIGINAL, reinterpret_cast<LONG_PTR>(DlgParam.strInitOctal.CPtr()));
1435 DlgParam.strInitAccessDate = AttrDlg[SA_FIXEDIT_LAST_ACCESS_DATE].strData;
1436 DlgParam.strInitAccessTime = AttrDlg[SA_FIXEDIT_LAST_ACCESS_TIME].strData;
1437 DlgParam.strInitModifyDate = AttrDlg[SA_FIXEDIT_LAST_MODIFICATION_DATE].strData;
1438 DlgParam.strInitModifyTime = AttrDlg[SA_FIXEDIT_LAST_MODIFICATION_TIME].strData;
1439 DlgParam.strInitStatusChangeDate = AttrDlg[SA_FIXEDIT_LAST_CHANGE_DATE].strData;
1440 DlgParam.strInitStatusChangeTime = AttrDlg[SA_FIXEDIT_LAST_CHANGE_TIME].strData;
1441 // workaround to compare current & initial date:
1442 // - in case of mask "9999N" each edit adds a space to the last position after 4-digit year
1443 switch (GetDateFormat()) {
1444 case 0:
1445 case 1:
1446 if (DlgParam.strInitAccessDate.GetLength() < 11)
1447 DlgParam.strInitAccessDate += L' ';
1448 if (DlgParam.strInitModifyDate.GetLength() < 11)
1449 DlgParam.strInitModifyDate += L' ';
1450 if (DlgParam.strInitStatusChangeDate.GetLength() < 11)
1451 DlgParam.strInitStatusChangeDate += L' ';
1452 default:
1453 break;
1456 Dlg.Process();
1458 switch (Dlg.GetExitCode()) {
1459 case SA_BUTTON_SET: {
1460 const size_t Times[] = {SA_FIXEDIT_LAST_ACCESS_TIME, SA_FIXEDIT_LAST_MODIFICATION_TIME,
1461 SA_FIXEDIT_LAST_CHANGE_TIME};
1463 for (size_t i = 0; i < ARRAYSIZE(Times); i++) {
1464 LPWSTR TimePtr = AttrDlg[Times[i]].strData.GetBuffer();
1465 TimePtr[8] = GetTimeSeparator();
1466 AttrDlg[Times[i]].strData.ReleaseBuffer(AttrDlg[Times[i]].strData.GetLength());
1469 TPreRedrawFuncGuard preRedrawFuncGuard(PR_ShellSetFileAttributesMsg);
1470 ShellSetFileAttributesMsg(SelCount == 1 ? strSelName.CPtr() : nullptr);
1471 int SkipMode = SETATTR_RET_UNKNOWN;
1473 if (SelCount == 1 && (FileAttr & FILE_ATTRIBUTE_REPARSE_POINT) != 0) {
1474 FARString OldSymLink;
1475 ReadSymlink(strSelName, OldSymLink);
1476 if (DlgParam.SymLink != OldSymLink) {
1477 int r = 1;
1478 if ( !apiPathExists(DlgParam.SymLink) ) {
1479 FARString strTmp1, strTmp2, strTmp3;
1480 strTmp1.Format(Msg::SetAttrSymlinkWarn1, strSelName.CPtr());
1481 strTmp2.Format(Msg::SetAttrSymlinkWarn2, DlgParam.SymLink.CPtr());
1482 strTmp3.Format(Msg::SetAttrSymlinkWarn3, OldSymLink.CPtr());
1483 r = Message(MSG_WARNING, 2,
1484 Msg::Error, strTmp1, strTmp2, strTmp3, L" ", Msg::SetAttrSymlinkWarn4,
1485 Msg::HSkip, Msg::HChange);
1487 if( r == 1 ) {
1488 fprintf(stderr, "Symlink change: '%ls' -> '%ls'\n",
1489 OldSymLink.CPtr(), DlgParam.SymLink.CPtr());
1490 sdc_unlink(strSelName.GetMB().c_str());
1491 r = sdc_symlink(DlgParam.SymLink.GetMB().c_str(), strSelName.GetMB().c_str());
1492 if (r != 0) {
1493 Message(MSG_WARNING | MSG_ERRORTYPE, 1,
1494 Msg::Error, Msg::SetAttrSymlinkFailed, strSelName, Msg::Ok);
1499 if (SelCount == 1 && !(FileAttr & FILE_ATTRIBUTE_DIRECTORY)) {
1500 DWORD NewMode = 0;
1502 for (size_t i = 0; i < ARRAYSIZE(AP); i++) {
1503 if (AttrDlg[AP[i].Item].Selected) {
1504 NewMode|= AP[i].Mode;
1508 if (!ApplyFileOwnerGroupIfChanged(AttrDlg[SA_COMBO_OWNER], ESetFileOwner, SkipMode,
1509 strSelName, DlgParam.strInitOwner))
1510 break;
1511 if (!ApplyFileOwnerGroupIfChanged(AttrDlg[SA_COMBO_GROUP], ESetFileGroup, SkipMode,
1512 strSelName, DlgParam.strInitGroup))
1513 break;
1515 FILETIME UnixAccessTime = {}, UnixModificationTime = {};
1516 int SetAccessTime = DlgParam.OAccessTime
1517 && ReadFileTime(0, strSelName, UnixAccessTime,
1518 AttrDlg[SA_FIXEDIT_LAST_ACCESS_DATE].strData,
1519 AttrDlg[SA_FIXEDIT_LAST_ACCESS_TIME].strData);
1520 int SetModifyTime = DlgParam.OModifyTime
1521 && ReadFileTime(1, strSelName, UnixModificationTime,
1522 AttrDlg[SA_FIXEDIT_LAST_MODIFICATION_DATE].strData,
1523 AttrDlg[SA_FIXEDIT_LAST_MODIFICATION_TIME].strData);
1525 //_SVS(SysLog(L"\n\tSetWriteTime=%d\n\tSetCreationTime=%d\n\tSetLastAccessTime=%d",SetWriteTime,SetCreationTime,SetLastAccessTime));
1527 if (SetAccessTime || SetModifyTime) {
1528 if (ESetFileTime(strSelName, SetAccessTime ? &UnixAccessTime : nullptr,
1529 SetModifyTime ? &UnixModificationTime : nullptr, FileAttr, SkipMode)
1530 == SETATTR_RET_SKIPALL) {
1531 SkipMode = SETATTR_RET_SKIP;
1535 if ((FileMode & EDITABLE_MODES) != (NewMode & EDITABLE_MODES)) {
1536 if (ESetFileMode(strSelName, NewMode, SkipMode) == SETATTR_RET_SKIPALL) {
1537 SkipMode = SETATTR_RET_SKIP;
1541 ApplyFSFileFlags(AttrDlg, strSelName, FileAttr);
1543 /* Multi *********************************************************** */
1544 else {
1545 int RetCode = 1;
1546 ConsoleTitle SetAttrTitle(Msg::SetAttrTitle);
1547 if (SrcPanel) {
1548 CtrlObject->Cp()->GetAnotherPanel(SrcPanel)->CloseFile();
1550 DWORD SetMode = 0, ClearMode = 0;
1552 for (size_t i = 0; i < ARRAYSIZE(AP); i++) {
1553 switch (AttrDlg[AP[i].Item].Selected) {
1554 case BSTATE_CHECKED:
1555 SetMode|= AP[i].Mode;
1556 break;
1557 case BSTATE_UNCHECKED:
1558 ClearMode|= AP[i].Mode;
1559 break;
1563 if (SrcPanel) {
1564 SrcPanel->GetSelName(nullptr, FileAttr, FileMode);
1566 wakeful W;
1567 bool Cancel = false;
1568 DWORD LastTime = 0;
1570 bool SingleFileDone = false;
1571 while ((SrcPanel ? SrcPanel->GetSelName(&strSelName, FileAttr, FileMode, &FindData)
1572 : !SingleFileDone)
1573 && !Cancel) {
1574 if (!SrcPanel) {
1575 SingleFileDone = true;
1577 //_SVS(SysLog(L"SelName='%ls'\n\tFileAttr =0x%08X\n\tSetAttr =0x%08X\n\tClearAttr=0x%08X\n\tResult =0x%08X",
1578 // SelName,FileAttr,SetAttr,ClearAttr,((FileAttr|SetAttr)&(~ClearAttr))));
1579 DWORD CurTime = WINPORT(GetTickCount)();
1581 if (CurTime - LastTime > RedrawTimeout) {
1582 LastTime = CurTime;
1583 ShellSetFileAttributesMsg(strSelName);
1585 if (CheckForEsc())
1586 break;
1589 if (!ApplyFileOwnerGroupIfChanged(AttrDlg[SA_COMBO_OWNER], ESetFileOwner, SkipMode,
1590 strSelName, DlgParam.strInitOwner))
1591 break;
1592 if (!ApplyFileOwnerGroupIfChanged(AttrDlg[SA_COMBO_GROUP], ESetFileGroup, SkipMode,
1593 strSelName, DlgParam.strInitGroup))
1594 break;
1596 FILETIME UnixAccessTime = {}, UnixModificationTime = {};
1597 int SetAccessTime = DlgParam.OAccessTime
1598 && ReadFileTime(0, strSelName, UnixAccessTime,
1599 AttrDlg[SA_FIXEDIT_LAST_ACCESS_DATE].strData,
1600 AttrDlg[SA_FIXEDIT_LAST_ACCESS_TIME].strData);
1601 int SetModifyTime = DlgParam.OModifyTime
1602 && ReadFileTime(1, strSelName, UnixModificationTime,
1603 AttrDlg[SA_FIXEDIT_LAST_MODIFICATION_DATE].strData,
1604 AttrDlg[SA_FIXEDIT_LAST_MODIFICATION_TIME].strData);
1606 RetCode = ESetFileTime(strSelName, SetAccessTime ? &UnixAccessTime : nullptr,
1607 SetModifyTime ? &UnixModificationTime : nullptr, FileAttr, SkipMode);
1609 if (RetCode == SETATTR_RET_ERROR)
1610 break;
1611 else if (RetCode == SETATTR_RET_SKIP)
1612 continue;
1613 else if (RetCode == SETATTR_RET_SKIPALL) {
1614 SkipMode = SETATTR_RET_SKIP;
1615 continue;
1618 if (FileAttr != INVALID_FILE_ATTRIBUTES) {
1619 if (((FileMode | SetMode) & ~ClearMode) != FileMode) {
1621 RetCode = ESetFileMode(strSelName, ((FileMode | SetMode) & (~ClearMode)),
1622 SkipMode);
1624 if (RetCode == SETATTR_RET_ERROR)
1625 break;
1626 else if (RetCode == SETATTR_RET_SKIP)
1627 continue;
1628 else if (RetCode == SETATTR_RET_SKIPALL) {
1629 SkipMode = SETATTR_RET_SKIP;
1630 continue;
1634 if ((FileAttr & FILE_ATTRIBUTE_DIRECTORY)
1635 && AttrDlg[SA_CHECKBOX_SUBFOLDERS].Selected) {
1636 ScanTree ScTree(FALSE);
1637 ScTree.SetFindPath(strSelName, L"*");
1638 DWORD LastTime = WINPORT(GetTickCount)();
1639 FARString strFullName;
1641 while (ScTree.GetNextName(&FindData, strFullName)) {
1642 DWORD CurTime = WINPORT(GetTickCount)();
1644 if (CurTime - LastTime > RedrawTimeout) {
1645 LastTime = CurTime;
1646 ShellSetFileAttributesMsg(strFullName);
1648 if (CheckForEsc()) {
1649 Cancel = true;
1650 break;
1654 if (!ApplyFileOwnerGroupIfChanged(AttrDlg[SA_COMBO_GROUP], ESetFileOwner,
1655 SkipMode, strFullName, DlgParam.strInitOwner,
1656 DlgParam.OSubfoldersState))
1657 break;
1658 if (!ApplyFileOwnerGroupIfChanged(AttrDlg[SA_COMBO_GROUP], ESetFileGroup,
1659 SkipMode, strFullName, DlgParam.strInitGroup,
1660 DlgParam.OSubfoldersState))
1661 break;
1663 SetAccessTime = DlgParam.OAccessTime
1664 && ReadFileTime(0, strFullName, UnixAccessTime,
1665 AttrDlg[SA_FIXEDIT_LAST_ACCESS_DATE].strData,
1666 AttrDlg[SA_FIXEDIT_LAST_ACCESS_TIME].strData);
1667 SetModifyTime = DlgParam.OModifyTime
1668 && ReadFileTime(1, strFullName, UnixModificationTime,
1669 AttrDlg[SA_FIXEDIT_LAST_MODIFICATION_DATE].strData,
1670 AttrDlg[SA_FIXEDIT_LAST_MODIFICATION_TIME].strData);
1672 if (SetAccessTime || SetModifyTime) {
1673 RetCode = ESetFileTime(strFullName,
1674 SetAccessTime ? &UnixAccessTime : nullptr,
1675 SetModifyTime ? &UnixModificationTime : nullptr,
1676 FindData.dwFileAttributes, SkipMode);
1678 if (RetCode == SETATTR_RET_ERROR) {
1679 Cancel = true;
1680 break;
1681 } else if (RetCode == SETATTR_RET_SKIP)
1682 continue;
1683 else if (RetCode == SETATTR_RET_SKIPALL) {
1684 SkipMode = SETATTR_RET_SKIP;
1685 continue;
1689 if (((FindData.dwUnixMode | SetMode) & (~ClearMode))
1690 != FindData.dwUnixMode) {
1691 RetCode = ESetFileMode(strFullName,
1692 (FindData.dwUnixMode | SetMode) & (~ClearMode), SkipMode);
1694 if (RetCode == SETATTR_RET_ERROR) {
1695 Cancel = true;
1696 break;
1697 } else if (RetCode == SETATTR_RET_SKIP)
1698 continue;
1699 else if (RetCode == SETATTR_RET_SKIPALL) {
1700 SkipMode = SETATTR_RET_SKIP;
1701 continue;
1703 ApplyFSFileFlags(AttrDlg, strFullName, FileAttr);
1708 ApplyFSFileFlags(AttrDlg, strSelName, FileAttr);
1710 } // END: while (SrcPanel->GetSelNameCompat(...))
1712 } break;
1713 default:
1714 return false;
1718 if (SrcPanel) {
1719 SrcPanel->SaveSelection();
1720 SrcPanel->Update(UPDATE_KEEP_SELECTION);
1721 SrcPanel->ClearSelection();
1722 CtrlObject->Cp()->GetAnotherPanel(SrcPanel)->Update(UPDATE_KEEP_SELECTION | UPDATE_SECONDARY);
1724 CtrlObject->Cp()->Redraw();
1725 return true;