1 // PanelOperations.cpp
2
3 #include "StdAfx.h"
4
5 #include "../../../Common/DynamicBuffer.h"
6 #include "../../../Common/StringConvert.h"
7 #include "../../../Common/Wildcard.h"
8
9 #include "../../../Windows/COM.h"
10 #include "../../../Windows/FileName.h"
11 #include "../../../Windows/PropVariant.h"
12
13 #include "ComboDialog.h"
14
15 #include "FSFolder.h"
16 #include "FormatUtils.h"
17 #include "LangUtils.h"
18 #include "Panel.h"
19 #include "UpdateCallback100.h"
20
21 #include "resource.h"
22
23 using namespace NWindows;
24 using namespace NFile;
25 using namespace NName;
26
27 #ifndef _UNICODE
28 extern bool g_IsNT;
29 #endif
30
31 enum EFolderOpType
32 {
33 FOLDER_TYPE_CREATE_FOLDER = 0,
34 FOLDER_TYPE_DELETE = 1,
35 FOLDER_TYPE_RENAME = 2
36 };
37
38 class CThreadFolderOperations: public CProgressThreadVirt
39 {
40 HRESULT ProcessVirt() Z7_override;
41 public:
42 EFolderOpType OpType;
43 UString Name;
44 UInt32 Index;
45 CRecordVector<UInt32> Indices;
46
47 CMyComPtr<IFolderOperations> FolderOperations;
48 CMyComPtr<IProgress> UpdateCallback;
49 CUpdateCallback100Imp *UpdateCallbackSpec;
50
CThreadFolderOperations(EFolderOpType opType)51 CThreadFolderOperations(EFolderOpType opType): OpType(opType) {}
52 HRESULT DoOperation(CPanel &panel, const UString &progressTitle, const UString &titleError);
53 };
54
ProcessVirt()55 HRESULT CThreadFolderOperations::ProcessVirt()
56 {
57 NCOM::CComInitializer comInitializer;
58 switch ((int)OpType)
59 {
60 case FOLDER_TYPE_CREATE_FOLDER:
61 return FolderOperations->CreateFolder(Name, UpdateCallback);
62 case FOLDER_TYPE_DELETE:
63 return FolderOperations->Delete(Indices.ConstData(), Indices.Size(), UpdateCallback);
64 case FOLDER_TYPE_RENAME:
65 return FolderOperations->Rename(Index, Name, UpdateCallback);
66 default:
67 return E_FAIL;
68 }
69 }
70
71
DoOperation(CPanel & panel,const UString & progressTitle,const UString & titleError)72 HRESULT CThreadFolderOperations::DoOperation(CPanel &panel, const UString &progressTitle, const UString &titleError)
73 {
74 UpdateCallbackSpec = new CUpdateCallback100Imp;
75 UpdateCallback = UpdateCallbackSpec;
76 UpdateCallbackSpec->ProgressDialog = this;
77
78 WaitMode = true;
79 Sync.FinalMessage.ErrorMessage.Title = titleError;
80
81 UpdateCallbackSpec->Init();
82
83 if (panel._parentFolders.Size() > 0)
84 {
85 const CFolderLink &fl = panel._parentFolders.Back();
86 UpdateCallbackSpec->PasswordIsDefined = fl.UsePassword;
87 UpdateCallbackSpec->Password = fl.Password;
88 }
89
90 MainWindow = panel._mainWindow; // panel.GetParent()
91 MainTitle = "7-Zip"; // LangString(IDS_APP_TITLE);
92 MainAddTitle = progressTitle + L' ';
93
94 RINOK(Create(progressTitle, MainWindow))
95 return Result;
96 }
97
98 #ifndef _UNICODE
99 typedef int (WINAPI * Func_SHFileOperationW)(LPSHFILEOPSTRUCTW lpFileOp);
100 #endif
101
102 /*
103 void CPanel::MessageBoxErrorForUpdate(HRESULT errorCode, UINT resourceID)
104 {
105 if (errorCode == E_NOINTERFACE)
106 MessageBox_Error_UnsupportOperation();
107 else
108 MessageBox_Error_HRESULT_Caption(errorCode, LangString(resourceID));
109 }
110 */
111
DeleteItems(bool NON_CE_VAR (toRecycleBin))112 void CPanel::DeleteItems(bool NON_CE_VAR(toRecycleBin))
113 {
114 CDisableTimerProcessing disableTimerProcessing(*this);
115 CRecordVector<UInt32> indices;
116 Get_ItemIndices_Operated(indices);
117 if (indices.IsEmpty())
118 return;
119 CSelectedState state;
120 SaveSelectedState(state);
121
122 #ifndef UNDER_CE
123 // WM6 / SHFileOperationW doesn't ask user! So we use internal delete
124 if (IsFSFolder() && toRecycleBin)
125 {
126 bool useInternalDelete = false;
127 #ifndef _UNICODE
128 if (!g_IsNT)
129 {
130 CDynamicBuffer<CHAR> buffer;
131 FOR_VECTOR (i, indices)
132 {
133 const AString path (GetSystemString(GetItemFullPath(indices[i])));
134 buffer.AddData(path, path.Len() + 1);
135 }
136 *buffer.GetCurPtrAndGrow(1) = 0;
137 SHFILEOPSTRUCTA fo;
138 fo.hwnd = GetParent();
139 fo.wFunc = FO_DELETE;
140 fo.pFrom = (const CHAR *)buffer;
141 fo.pTo = NULL;
142 fo.fFlags = 0;
143 if (toRecycleBin)
144 fo.fFlags |= FOF_ALLOWUNDO;
145 // fo.fFlags |= FOF_NOCONFIRMATION;
146 // fo.fFlags |= FOF_NOERRORUI;
147 // fo.fFlags |= FOF_SILENT;
148 // fo.fFlags |= FOF_WANTNUKEWARNING;
149 fo.fAnyOperationsAborted = FALSE;
150 fo.hNameMappings = NULL;
151 fo.lpszProgressTitle = NULL;
152 /* int res = */ ::SHFileOperationA(&fo);
153 }
154 else
155 #endif
156 {
157 CDynamicBuffer<WCHAR> buffer;
158 unsigned maxLen = 0;
159 const UString prefix = GetFsPath();
160 FOR_VECTOR (i, indices)
161 {
162 // L"\\\\?\\") doesn't work here.
163 const UString path = prefix + GetItemRelPath2(indices[i]);
164 if (path.Len() > maxLen)
165 maxLen = path.Len();
166 buffer.AddData(path, path.Len() + 1);
167 }
168 *buffer.GetCurPtrAndGrow(1) = 0;
169 if (maxLen >= MAX_PATH)
170 {
171 if (toRecycleBin)
172 {
173 MessageBox_Error_LangID(IDS_ERROR_LONG_PATH_TO_RECYCLE);
174 return;
175 }
176 useInternalDelete = true;
177 }
178 else
179 {
180 SHFILEOPSTRUCTW fo;
181 fo.hwnd = GetParent();
182 fo.wFunc = FO_DELETE;
183 fo.pFrom = (const WCHAR *)buffer;
184 fo.pTo = NULL;
185 fo.fFlags = 0;
186 if (toRecycleBin)
187 fo.fFlags |= FOF_ALLOWUNDO;
188 fo.fAnyOperationsAborted = FALSE;
189 fo.hNameMappings = NULL;
190 fo.lpszProgressTitle = NULL;
191 // int res;
192 #ifdef _UNICODE
193 /* res = */ ::SHFileOperationW(&fo);
194 #else
195 Z7_DIAGNOSTIC_IGNORE_CAST_FUNCTION
196 const
197 Func_SHFileOperationW
198 f_SHFileOperationW = Z7_GET_PROC_ADDRESS(
199 Func_SHFileOperationW, ::GetModuleHandleW(L"shell32.dll"),
200 "SHFileOperationW");
201 if (!f_SHFileOperationW)
202 return;
203 /* res = */ f_SHFileOperationW(&fo);
204 #endif
205 }
206 }
207 /*
208 if (fo.fAnyOperationsAborted)
209 MessageBox_Error_HRESULT_Caption(result, LangString(IDS_ERROR_DELETING));
210 */
211 if (!useInternalDelete)
212 {
213 RefreshListCtrl(state);
214 return;
215 }
216 }
217 #endif
218
219 // DeleteItemsInternal
220
221 if (!CheckBeforeUpdate(IDS_ERROR_DELETING))
222 return;
223
224 UInt32 titleID, messageID;
225 UString messageParam;
226 if (indices.Size() == 1)
227 {
228 const unsigned index = indices[0];
229 messageParam = GetItemRelPath2(index);
230 if (IsItem_Folder(index))
231 {
232 titleID = IDS_CONFIRM_FOLDER_DELETE;
233 messageID = IDS_WANT_TO_DELETE_FOLDER;
234 }
235 else
236 {
237 titleID = IDS_CONFIRM_FILE_DELETE;
238 messageID = IDS_WANT_TO_DELETE_FILE;
239 }
240 }
241 else
242 {
243 titleID = IDS_CONFIRM_ITEMS_DELETE;
244 messageID = IDS_WANT_TO_DELETE_ITEMS;
245 messageParam = NumberToString(indices.Size());
246 }
247 if (::MessageBoxW(GetParent(), MyFormatNew(messageID, messageParam), LangString(titleID), MB_OKCANCEL | MB_ICONQUESTION) != IDOK)
248 return;
249
250 CDisableNotify disableNotify(*this);
251 {
252 CThreadFolderOperations op(FOLDER_TYPE_DELETE);
253 op.FolderOperations = _folderOperations;
254 op.Indices = indices;
255 op.DoOperation(*this,
256 LangString(IDS_DELETING),
257 LangString(IDS_ERROR_DELETING));
258 }
259 RefreshTitleAlways();
260 RefreshListCtrl(state);
261 }
262
OnBeginLabelEdit(LV_DISPINFOW * lpnmh)263 BOOL CPanel::OnBeginLabelEdit(LV_DISPINFOW * lpnmh)
264 {
265 const unsigned realIndex = GetRealIndex(lpnmh->item);
266 if (realIndex == kParentIndex)
267 return TRUE;
268 if (IsThereReadOnlyFolder())
269 return TRUE;
270 return FALSE;
271 }
272
IsCorrectFsName(const UString & name)273 static bool IsCorrectFsName(const UString &name)
274 {
275 const UString lastPart = name.Ptr((unsigned)(name.ReverseFind_PathSepar() + 1));
276 return
277 lastPart != L"." &&
278 lastPart != L"..";
279 }
280
281 bool CorrectFsPath(const UString &relBase, const UString &path, UString &result);
282
CorrectFsPath(const UString & path2,UString & result)283 bool CPanel::CorrectFsPath(const UString &path2, UString &result)
284 {
285 return ::CorrectFsPath(GetFsPath(), path2, result);
286 }
287
OnEndLabelEdit(LV_DISPINFOW * lpnmh)288 BOOL CPanel::OnEndLabelEdit(LV_DISPINFOW * lpnmh)
289 {
290 if (lpnmh->item.pszText == NULL)
291 return FALSE;
292 CDisableTimerProcessing disableTimerProcessing2(*this);
293
294 if (!CheckBeforeUpdate(IDS_ERROR_RENAMING))
295 return FALSE;
296
297 UString newName = lpnmh->item.pszText;
298 if (!IsCorrectFsName(newName))
299 {
300 MessageBox_Error_HRESULT(E_INVALIDARG);
301 return FALSE;
302 }
303
304 if (IsFSFolder())
305 {
306 UString correctName;
307 if (!CorrectFsPath(newName, correctName))
308 {
309 MessageBox_Error_HRESULT(E_INVALIDARG);
310 return FALSE;
311 }
312 newName = correctName;
313 }
314
315 SaveSelectedState(_selectedState);
316
317 const unsigned realIndex = GetRealIndex(lpnmh->item);
318 if (realIndex == kParentIndex)
319 return FALSE;
320 const UString prefix = GetItemPrefix(realIndex);
321 const UString oldName = GetItemName(realIndex);
322
323 CDisableNotify disableNotify(*this);
324 {
325 CThreadFolderOperations op(FOLDER_TYPE_RENAME);
326 op.FolderOperations = _folderOperations;
327 op.Index = realIndex;
328 op.Name = newName;
329 const HRESULT res = op.DoOperation(*this,
330 LangString(IDS_RENAMING),
331 LangString(IDS_ERROR_RENAMING));
332 // fixed in 9.26: we refresh list even after errors
333 // (it's more safe, since error can be at different stages, so list can be incorrect).
334 if (res == S_OK)
335 _selectedState.FocusedName = prefix + newName;
336 else
337 {
338 _selectedState.FocusedName = prefix + oldName;
339 // return FALSE;
340 }
341 }
342
343 // Can't use RefreshListCtrl here.
344 // RefreshListCtrlSaveFocused();
345 _selectedState.FocusedName_Defined = true;
346 _selectedState.SelectFocused = true;
347
348 // We need clear all items to disable GetText before Reload:
349 // number of items can change.
350 // DeleteListItems();
351 // But seems it can still call GetText (maybe for current item)
352 // so we can't delete items.
353
354 _dontShowMode = true;
355
356 PostMsg(kReLoadMessage);
357 return TRUE;
358 }
359
360 bool Dlg_CreateFolder(HWND wnd, UString &destName);
361
CreateFolder()362 void CPanel::CreateFolder()
363 {
364 if (IsHashFolder())
365 return;
366
367 if (!CheckBeforeUpdate(IDS_CREATE_FOLDER_ERROR))
368 return;
369
370 CDisableTimerProcessing disableTimerProcessing2(*this);
371 CSelectedState state;
372 SaveSelectedState(state);
373
374 UString newName;
375 if (!Dlg_CreateFolder(GetParent(), newName))
376 return;
377
378 if (!IsCorrectFsName(newName))
379 {
380 MessageBox_Error_HRESULT(E_INVALIDARG);
381 return;
382 }
383
384 if (IsFSFolder())
385 {
386 UString correctName;
387 if (!CorrectFsPath(newName, correctName))
388 {
389 MessageBox_Error_HRESULT(E_INVALIDARG);
390 return;
391 }
392 newName = correctName;
393 }
394
395 HRESULT res;
396 CDisableNotify disableNotify(*this);
397 {
398 CThreadFolderOperations op(FOLDER_TYPE_CREATE_FOLDER);
399 op.FolderOperations = _folderOperations;
400 op.Name = newName;
401 res = op.DoOperation(*this,
402 LangString(IDS_CREATE_FOLDER),
403 LangString(IDS_CREATE_FOLDER_ERROR));
404 /*
405 // fixed for 9.26: we must refresh always
406 if (res != S_OK)
407 return;
408 */
409 }
410 if (res == S_OK)
411 {
412 int pos = newName.Find(WCHAR_PATH_SEPARATOR);
413 if (pos >= 0)
414 newName.DeleteFrom((unsigned)(pos));
415 if (!_mySelectMode)
416 state.SelectedNames.Clear();
417 state.FocusedName = newName;
418 state.FocusedName_Defined = true;
419 state.SelectFocused = true;
420 }
421 RefreshTitleAlways();
422 RefreshListCtrl(state);
423 }
424
CreateFile()425 void CPanel::CreateFile()
426 {
427 if (IsHashFolder())
428 return;
429
430 if (!CheckBeforeUpdate(IDS_CREATE_FILE_ERROR))
431 return;
432
433 CDisableTimerProcessing disableTimerProcessing2(*this);
434 CSelectedState state;
435 SaveSelectedState(state);
436 CComboDialog dlg;
437 LangString(IDS_CREATE_FILE, dlg.Title);
438 LangString(IDS_CREATE_FILE_NAME, dlg.Static);
439 LangString(IDS_CREATE_FILE_DEFAULT_NAME, dlg.Value);
440
441 if (dlg.Create(GetParent()) != IDOK)
442 return;
443
444 CDisableNotify disableNotify(*this);
445
446 UString newName = dlg.Value;
447
448 if (IsFSFolder())
449 {
450 UString correctName;
451 if (!CorrectFsPath(newName, correctName))
452 {
453 MessageBox_Error_HRESULT(E_INVALIDARG);
454 return;
455 }
456 newName = correctName;
457 }
458
459 const HRESULT result = _folderOperations->CreateFile(newName, NULL);
460 if (result != S_OK)
461 {
462 MessageBox_Error_HRESULT_Caption(result, LangString(IDS_CREATE_FILE_ERROR));
463 // MessageBoxErrorForUpdate(result, IDS_CREATE_FILE_ERROR);
464 return;
465 }
466 const int pos = newName.Find(WCHAR_PATH_SEPARATOR);
467 if (pos >= 0)
468 newName.DeleteFrom((unsigned)pos);
469 if (!_mySelectMode)
470 state.SelectedNames.Clear();
471 state.FocusedName = newName;
472 state.FocusedName_Defined = true;
473 state.SelectFocused = true;
474 RefreshListCtrl(state);
475 }
476
RenameFile()477 void CPanel::RenameFile()
478 {
479 if (!CheckBeforeUpdate(IDS_ERROR_RENAMING))
480 return;
481 int index = _listView.GetFocusedItem();
482 if (index >= 0)
483 _listView.EditLabel(index);
484 }
485
ChangeComment()486 void CPanel::ChangeComment()
487 {
488 if (IsHashFolder())
489 return;
490 if (!CheckBeforeUpdate(IDS_COMMENT))
491 return;
492 CDisableTimerProcessing disableTimerProcessing2(*this);
493 const int index = _listView.GetFocusedItem();
494 if (index < 0)
495 return;
496 const unsigned realIndex = GetRealItemIndex(index);
497 if (realIndex == kParentIndex)
498 return;
499 CSelectedState state;
500 SaveSelectedState(state);
501 UString comment;
502 {
503 NCOM::CPropVariant propVariant;
504 if (_folder->GetProperty(realIndex, kpidComment, &propVariant) != S_OK)
505 return;
506 if (propVariant.vt == VT_BSTR)
507 comment = propVariant.bstrVal;
508 else if (propVariant.vt != VT_EMPTY)
509 return;
510 }
511 const UString name = GetItemRelPath2(realIndex);
512 CComboDialog dlg;
513 dlg.Title = name;
514 dlg.Title += " : ";
515 AddLangString(dlg.Title, IDS_COMMENT);
516 dlg.Value = comment;
517 LangString(IDS_COMMENT2, dlg.Static);
518 if (dlg.Create(GetParent()) != IDOK)
519 return;
520 NCOM::CPropVariant propVariant (dlg.Value);
521
522 CDisableNotify disableNotify(*this);
523 const HRESULT result = _folderOperations->SetProperty(realIndex, kpidComment, &propVariant, NULL);
524 if (result != S_OK)
525 {
526 if (result == E_NOINTERFACE)
527 MessageBox_Error_UnsupportOperation();
528 else
529 MessageBox_Error_HRESULT_Caption(result, L"Set Comment Error");
530 }
531 RefreshListCtrl(state);
532 }
533