• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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