xref: /aosp_15_r20/external/lzma/CPP/7zip/UI/FileManager/App.cpp (revision f6dc9357d832569d4d1f5d24eacdb3935a1ae8e6)
1 // App.cpp
2 
3 #include "StdAfx.h"
4 
5 #include "resource.h"
6 #include "OverwriteDialogRes.h"
7 
8 #include "../../../Windows/FileName.h"
9 #include "../../../Windows/PropVariantConv.h"
10 
11 /*
12 #include "Windows/COM.h"
13 #include "Windows/Error.h"
14 #include "Windows/FileDir.h"
15 
16 #include "Windows/PropVariant.h"
17 #include "Windows/Thread.h"
18 */
19 
20 #include "App.h"
21 #include "CopyDialog.h"
22 #include "ExtractCallback.h"
23 #include "FormatUtils.h"
24 #include "IFolder.h"
25 #include "LangUtils.h"
26 #include "MyLoadMenu.h"
27 #include "RegistryUtils.h"
28 #include "ViewSettings.h"
29 
30 #include "PropertyNameRes.h"
31 
32 using namespace NWindows;
33 using namespace NFile;
34 using namespace NDir;
35 using namespace NFind;
36 using namespace NName;
37 
38 extern HINSTANCE g_hInstance;
39 
40 #define kTempDirPrefix FTEXT("7zE")
41 
OnTab()42 void CPanelCallbackImp::OnTab()
43 {
44   if (g_App.NumPanels != 1)
45     _app->Panels[1 - _index].SetFocusToList();
46   _app->RefreshTitle();
47 }
48 
SetFocusToPath(unsigned index)49 void CPanelCallbackImp::SetFocusToPath(unsigned index)
50 {
51   unsigned newPanelIndex = index;
52   if (g_App.NumPanels == 1)
53     newPanelIndex = g_App.LastFocusedPanel;
54   _app->RefreshTitle();
55   _app->Panels[newPanelIndex]._headerComboBox.SetFocus();
56   _app->Panels[newPanelIndex]._headerComboBox.ShowDropDown();
57 }
58 
59 
OnCopy(bool move,bool copyToSame)60 void CPanelCallbackImp::OnCopy(bool move, bool copyToSame) { _app->OnCopy(move, copyToSame, _index); }
OnSetSameFolder()61 void CPanelCallbackImp::OnSetSameFolder() { _app->OnSetSameFolder(_index); }
OnSetSubFolder()62 void CPanelCallbackImp::OnSetSubFolder()  { _app->OnSetSubFolder(_index); }
PanelWasFocused()63 void CPanelCallbackImp::PanelWasFocused() { _app->SetFocusedPanel(_index); _app->RefreshTitlePanel(_index); }
DragBegin()64 void CPanelCallbackImp::DragBegin() { _app->DragBegin(_index); }
DragEnd()65 void CPanelCallbackImp::DragEnd() { _app->DragEnd(); }
RefreshTitle(bool always)66 void CPanelCallbackImp::RefreshTitle(bool always) { _app->RefreshTitlePanel(_index, always); }
67 
ReloadLangItems()68 void CApp::ReloadLangItems()
69 {
70   LangString(IDS_N_SELECTED_ITEMS, LangString_N_SELECTED_ITEMS);
71 }
72 
SetListSettings()73 void CApp::SetListSettings()
74 {
75   CFmSettings st;
76   st.Load();
77 
78   ShowSystemMenu = st.ShowSystemMenu;
79 
80   DWORD extendedStyle = LVS_EX_HEADERDRAGDROP;
81   if (st.FullRow)
82     extendedStyle |= LVS_EX_FULLROWSELECT;
83   if (st.ShowGrid)
84     extendedStyle |= LVS_EX_GRIDLINES;
85 
86   if (st.SingleClick)
87   {
88     extendedStyle |= LVS_EX_ONECLICKACTIVATE | LVS_EX_TRACKSELECT;
89     /*
90     if (ReadUnderline())
91       extendedStyle |= LVS_EX_UNDERLINEHOT;
92     */
93   }
94 
95   for (unsigned i = 0; i < kNumPanelsMax; i++)
96   {
97     CPanel &panel = Panels[i];
98     panel._mySelectMode = st.AlternativeSelection;
99     panel._showDots = st.ShowDots;
100     panel._showRealFileIcons = st.ShowRealFileIcons;
101     panel._exStyle = extendedStyle;
102 
103     LONG_PTR style = panel._listView.GetStyle();
104     if (st.AlternativeSelection)
105       style |= LVS_SINGLESEL;
106     else
107       style &= ~(LONG_PTR)(DWORD)LVS_SINGLESEL;
108     panel._listView.SetStyle(style);
109     panel.SetExtendedStyle();
110   }
111 }
112 
113 #ifndef ILC_COLOR32
114 #define ILC_COLOR32 0x0020
115 #endif
116 
CreateOnePanel(unsigned panelIndex,const UString & mainPath,const UString & arcFormat,bool needOpenArc,COpenResult & openRes)117 HRESULT CApp::CreateOnePanel(unsigned panelIndex, const UString &mainPath, const UString &arcFormat,
118     bool needOpenArc,
119     COpenResult &openRes)
120 {
121   if (Panels[panelIndex].PanelCreated)
122     return S_OK;
123 
124   m_PanelCallbackImp[panelIndex].Init(this, panelIndex);
125 
126   UString path;
127   if (mainPath.IsEmpty())
128   {
129     if (!::ReadPanelPath(panelIndex, path))
130       path.Empty();
131   }
132   else
133     path = mainPath;
134 
135   const unsigned id = 1000 + 100 * panelIndex; // check it
136 
137   return Panels[panelIndex].Create(_window, _window,
138       id, path, arcFormat, &m_PanelCallbackImp[panelIndex], &AppState,
139       needOpenArc,
140       openRes);
141 }
142 
143 
CreateToolbar(HWND parent,NControl::CImageList & imageList,NControl::CToolBar & toolBar,bool largeButtons)144 static void CreateToolbar(HWND parent,
145     NControl::CImageList &imageList,
146     NControl::CToolBar &toolBar,
147     bool largeButtons)
148 {
149   toolBar.Attach(::CreateWindowEx(0, TOOLBARCLASSNAME, NULL, 0
150       | WS_CHILD
151       | WS_VISIBLE
152       | TBSTYLE_FLAT
153       | TBSTYLE_TOOLTIPS
154       | TBSTYLE_WRAPABLE
155       // | TBSTYLE_AUTOSIZE
156       // | CCS_NORESIZE
157       #ifdef UNDER_CE
158       | CCS_NODIVIDER
159       | CCS_NOPARENTALIGN
160       #endif
161       ,0,0,0,0, parent, NULL, g_hInstance, NULL));
162 
163   // TB_BUTTONSTRUCTSIZE message, which is required for
164   // backward compatibility.
165   toolBar.ButtonStructSize();
166 
167   imageList.Create(
168       largeButtons ? 48: 24,
169       largeButtons ? 36: 24,
170       ILC_MASK | ILC_COLOR32, 0, 0);
171   toolBar.SetImageList(0, imageList);
172 }
173 
174 
175 struct CButtonInfo
176 {
177   int CommandID;
178   UINT BitmapResID;
179   UINT Bitmap2ResID;
180   UINT StringResID;
181 
GetTextCButtonInfo182   UString GetText() const { return LangString(StringResID); }
183 };
184 
185 static const CButtonInfo g_StandardButtons[] =
186 {
187   { IDM_COPY_TO,    IDB_COPY,   IDB_COPY2,   IDS_BUTTON_COPY },
188   { IDM_MOVE_TO,    IDB_MOVE,   IDB_MOVE2,   IDS_BUTTON_MOVE },
189   { IDM_DELETE,     IDB_DELETE, IDB_DELETE2, IDS_BUTTON_DELETE } ,
190   { IDM_PROPERTIES, IDB_INFO,   IDB_INFO2,   IDS_BUTTON_INFO }
191 };
192 
193 static const CButtonInfo g_ArchiveButtons[] =
194 {
195   { kMenuCmdID_Toolbar_Add,     IDB_ADD,     IDB_ADD2,     IDS_ADD },
196   { kMenuCmdID_Toolbar_Extract, IDB_EXTRACT, IDB_EXTRACT2, IDS_EXTRACT },
197   { kMenuCmdID_Toolbar_Test,    IDB_TEST,    IDB_TEST2,    IDS_TEST }
198 };
199 
SetButtonText(int commandID,const CButtonInfo * buttons,unsigned numButtons,UString & s)200 static bool SetButtonText(int commandID, const CButtonInfo *buttons, unsigned numButtons, UString &s)
201 {
202   for (unsigned i = 0; i < numButtons; i++)
203   {
204     const CButtonInfo &b = buttons[i];
205     if (b.CommandID == commandID)
206     {
207       s = b.GetText();
208       return true;
209     }
210   }
211   return false;
212 }
213 
SetButtonText(int commandID,UString & s)214 static void SetButtonText(int commandID, UString &s)
215 {
216   if (SetButtonText(commandID, g_StandardButtons, Z7_ARRAY_SIZE(g_StandardButtons), s))
217     return;
218   SetButtonText(commandID, g_ArchiveButtons, Z7_ARRAY_SIZE(g_ArchiveButtons), s);
219 }
220 
AddButton(NControl::CImageList & imageList,NControl::CToolBar & toolBar,const CButtonInfo & butInfo,bool showText,bool large)221 static void AddButton(
222     NControl::CImageList &imageList,
223     NControl::CToolBar &toolBar,
224     const CButtonInfo &butInfo, bool showText, bool large)
225 {
226   TBBUTTON but;
227   but.iBitmap = 0;
228   but.idCommand = butInfo.CommandID;
229   but.fsState = TBSTATE_ENABLED;
230   but.fsStyle = TBSTYLE_BUTTON;
231   but.dwData = 0;
232 
233   UString s = butInfo.GetText();
234   but.iString = 0;
235   if (showText)
236     but.iString = (INT_PTR)(LPCWSTR)s;
237 
238   but.iBitmap = imageList.GetImageCount();
239   HBITMAP b = ::LoadBitmap(g_hInstance,
240       large ?
241       MAKEINTRESOURCE(butInfo.BitmapResID):
242       MAKEINTRESOURCE(butInfo.Bitmap2ResID));
243   if (b)
244   {
245     imageList.AddMasked(b, RGB(255, 0, 255));
246     ::DeleteObject(b);
247   }
248   #ifdef _UNICODE
249   toolBar.AddButton(1, &but);
250   #else
251   toolBar.AddButtonW(1, &but);
252   #endif
253 }
254 
ReloadToolbars()255 void CApp::ReloadToolbars()
256 {
257   _buttonsImageList.Destroy();
258   _toolBar.Destroy();
259 
260 
261   if (ShowArchiveToolbar || ShowStandardToolbar)
262   {
263     CreateToolbar(_window, _buttonsImageList, _toolBar, LargeButtons);
264     unsigned i;
265     if (ShowArchiveToolbar)
266       for (i = 0; i < Z7_ARRAY_SIZE(g_ArchiveButtons); i++)
267         AddButton(_buttonsImageList, _toolBar, g_ArchiveButtons[i], ShowButtonsLables, LargeButtons);
268     if (ShowStandardToolbar)
269       for (i = 0; i < Z7_ARRAY_SIZE(g_StandardButtons); i++)
270         AddButton(_buttonsImageList, _toolBar, g_StandardButtons[i], ShowButtonsLables, LargeButtons);
271 
272     _toolBar.AutoSize();
273   }
274 }
275 
SaveToolbarChanges()276 void CApp::SaveToolbarChanges()
277 {
278   SaveToolbar();
279   ReloadToolbars();
280   MoveSubWindows();
281 }
282 
283 
Create(HWND hwnd,const UString & mainPath,const UString & arcFormat,int xSizes[2],bool needOpenArc,COpenResult & openRes)284 HRESULT CApp::Create(HWND hwnd, const UString &mainPath, const UString &arcFormat, int xSizes[2], bool needOpenArc, COpenResult &openRes)
285 {
286   _window.Attach(hwnd);
287 
288   #ifdef UNDER_CE
289   _commandBar.Create(g_hInstance, hwnd, 1);
290   #endif
291 
292   MyLoadMenu(false);  // needResetMenu
293 
294   #ifdef UNDER_CE
295   _commandBar.AutoSize();
296   #endif
297 
298   ReadToolbar();
299   ReloadToolbars();
300 
301   unsigned i;
302   for (i = 0; i < kNumPanelsMax; i++)
303     Panels[i].PanelCreated = false;
304 
305   AppState.Read();
306 
307   SetListSettings();
308 
309   if (LastFocusedPanel >= kNumPanelsMax)
310     LastFocusedPanel = 0;
311   // ShowDeletedFiles = Read_ShowDeleted();
312 
313   CListMode listMode;
314   listMode.Read();
315 
316   for (i = 0; i < kNumPanelsMax; i++)
317   {
318     CPanel &panel = Panels[i];
319     panel._listViewMode = listMode.Panels[i];
320     panel._xSize = xSizes[i];
321     panel._flatModeForArc = ReadFlatView(i);
322   }
323 
324   for (i = 0; i < kNumPanelsMax; i++)
325   {
326     unsigned panelIndex = i;
327     if (needOpenArc && LastFocusedPanel == 1)
328       panelIndex = 1 - i;
329 
330     bool isMainPanel = (panelIndex == LastFocusedPanel);
331 
332     if (NumPanels > 1 || isMainPanel)
333     {
334       if (NumPanels == 1)
335         Panels[panelIndex]._xSize = xSizes[0] + xSizes[1];
336 
337       COpenResult openRes2;
338       UString path;
339       if (isMainPanel)
340         path = mainPath;
341 
342       RINOK(CreateOnePanel(panelIndex, path, arcFormat,
343           isMainPanel && needOpenArc,
344           *(isMainPanel ? &openRes : &openRes2)))
345 
346       if (isMainPanel)
347       {
348         if (needOpenArc && !openRes.ArchiveIsOpened)
349           return S_OK;
350       }
351     }
352   }
353 
354   SetFocusedPanel(LastFocusedPanel);
355   Panels[LastFocusedPanel].SetFocusToList();
356   return S_OK;
357 }
358 
359 
SwitchOnOffOnePanel()360 HRESULT CApp::SwitchOnOffOnePanel()
361 {
362   if (NumPanels == 1)
363   {
364     NumPanels++;
365     COpenResult openRes;
366     RINOK(CreateOnePanel(1 - LastFocusedPanel, UString(), UString(),
367         false, // needOpenArc
368         openRes))
369     Panels[1 - LastFocusedPanel].Enable(true);
370     Panels[1 - LastFocusedPanel].Show(SW_SHOWNORMAL);
371   }
372   else
373   {
374     NumPanels--;
375     Panels[1 - LastFocusedPanel].Enable(false);
376     Panels[1 - LastFocusedPanel].Show(SW_HIDE);
377   }
378   MoveSubWindows();
379   return S_OK;
380 }
381 
Save()382 void CApp::Save()
383 {
384   AppState.Save();
385   CListMode listMode;
386 
387   for (unsigned i = 0; i < kNumPanelsMax; i++)
388   {
389     const CPanel &panel = Panels[i];
390     UString path;
391     if (panel._parentFolders.IsEmpty())
392       path = panel._currentFolderPrefix;
393     else
394       path = panel._parentFolders[0].ParentFolderPath;
395       // GetFolderPath(panel._parentFolders[0].ParentFolder);
396     SavePanelPath(i, path);
397     listMode.Panels[i] = panel.GetListViewMode();
398     SaveFlatView(i, panel._flatModeForArc);
399   }
400 
401   listMode.Save();
402   // Save_ShowDeleted(ShowDeletedFiles);
403 }
404 
ReleaseApp()405 void CApp::ReleaseApp()
406 {
407   // 24.09: ReleasePanel() will stop panel timer processing.
408   // but we want to stop timer processing for all panels
409   // before ReleasePanel() calling.
410   unsigned i;
411   for (i = 0; i < kNumPanelsMax; i++)
412     Panels[i].Disable_Processing_Timer_Notify_StatusBar();
413   // It's for unloading COM dll's: don't change it.
414   for (i = 0; i < kNumPanelsMax; i++)
415     Panels[i].ReleasePanel();
416 }
417 
418 // reduces path to part that exists on disk (or root prefix of path)
419 // output path is normalized (with WCHAR_PATH_SEPARATOR)
Reduce_Path_To_RealFileSystemPath(UString & path)420 static void Reduce_Path_To_RealFileSystemPath(UString &path)
421 {
422   unsigned prefixSize = GetRootPrefixSize(path);
423 
424   while (!path.IsEmpty())
425   {
426     if (NFind::DoesDirExist_FollowLink(us2fs(path)))
427     {
428       NName::NormalizeDirPathPrefix(path);
429       break;
430     }
431     int pos = path.ReverseFind_PathSepar();
432     if (pos < 0)
433     {
434       path.Empty();
435       break;
436     }
437     path.DeleteFrom((unsigned)(pos + 1));
438     if ((unsigned)pos + 1 == prefixSize)
439       break;
440     path.DeleteFrom((unsigned)pos);
441   }
442 }
443 
444 // returns: true, if such dir exists or is root
445 /*
446 static bool CheckFolderPath(const UString &path)
447 {
448   UString pathReduced = path;
449   Reduce_Path_To_RealFileSystemPath(pathReduced);
450   return (pathReduced == path);
451 }
452 */
453 
454 extern UString ConvertSizeToString(UInt64 value);
455 
AddSizeValue(UString & s,UInt64 size)456 static void AddSizeValue(UString &s, UInt64 size)
457 {
458   s += MyFormatNew(IDS_FILE_SIZE, ConvertSizeToString(size));
459 }
460 
AddValuePair1(UString & s,UINT resourceID,UInt64 size)461 static void AddValuePair1(UString &s, UINT resourceID, UInt64 size)
462 {
463   AddLangString(s, resourceID);
464   s += ": ";
465   AddSizeValue(s, size);
466   s.Add_LF();
467 }
468 
469 void AddValuePair2(UString &s, UINT resourceID, UInt64 num, UInt64 size);
AddValuePair2(UString & s,UINT resourceID,UInt64 num,UInt64 size)470 void AddValuePair2(UString &s, UINT resourceID, UInt64 num, UInt64 size)
471 {
472   if (num == 0)
473     return;
474   AddLangString(s, resourceID);
475   s += ": ";
476   s += ConvertSizeToString(num);
477 
478   if (size != (UInt64)(Int64)-1)
479   {
480     s += "    ( ";
481     AddSizeValue(s, size);
482     s += " )";
483   }
484   s.Add_LF();
485 }
486 
AddPropValueToSum(IFolderFolder * folder,UInt32 index,PROPID propID,UInt64 & sum)487 static void AddPropValueToSum(IFolderFolder *folder, UInt32 index, PROPID propID, UInt64 &sum)
488 {
489   if (sum == (UInt64)(Int64)-1)
490     return;
491   NCOM::CPropVariant prop;
492   folder->GetProperty(index, propID, &prop);
493   UInt64 val = 0;
494   if (ConvertPropVariantToUInt64(prop, val))
495     sum += val;
496   else
497     sum = (UInt64)(Int64)-1;
498 }
499 
GetItemsInfoString(const CRecordVector<UInt32> & indices)500 UString CPanel::GetItemsInfoString(const CRecordVector<UInt32> &indices)
501 {
502   UString info;
503   UInt64 numDirs, numFiles, filesSize, foldersSize;
504   numDirs = numFiles = filesSize = foldersSize = 0;
505 
506   unsigned i;
507   for (i = 0; i < indices.Size(); i++)
508   {
509     const UInt32 index = indices[i];
510     if (IsItem_Folder(index))
511     {
512       AddPropValueToSum(_folder, index, kpidSize, foldersSize);
513       numDirs++;
514     }
515     else
516     {
517       AddPropValueToSum(_folder, index, kpidSize, filesSize);
518       numFiles++;
519     }
520   }
521 
522   AddValuePair2(info, IDS_PROP_FOLDERS, numDirs, foldersSize);
523   AddValuePair2(info, IDS_PROP_FILES, numFiles, filesSize);
524   int numDefined = ((foldersSize != (UInt64)(Int64)-1) && foldersSize != 0) ? 1: 0;
525   numDefined += ((filesSize != (UInt64)(Int64)-1) && filesSize != 0) ? 1: 0;
526   if (numDefined == 2)
527     AddValuePair1(info, IDS_PROP_SIZE, filesSize + foldersSize);
528 
529   info.Add_LF();
530   info += _currentFolderPrefix;
531 
532   for (i = 0; i < indices.Size() && (int)i < (int)kCopyDialog_NumInfoLines - 6; i++)
533   {
534     info.Add_LF();
535     info += "  ";
536     const UInt32 index = indices[i];
537     info += GetItemRelPath(index);
538     if (IsItem_Folder(index))
539       info.Add_PathSepar();
540   }
541   if (i != indices.Size())
542   {
543     info.Add_LF();
544     info += "  ...";
545   }
546   return info;
547 }
548 
549 bool IsCorrectFsName(const UString &name);
550 
551 
552 
553 /* Returns true, if path is path that can be used as path for File System functions
554 */
555 
556 /*
557 static bool IsFsPath(const FString &path)
558 {
559   if (!IsAbsolutePath(path))
560     return false;
561   unsigned prefixSize = GetRootPrefixSize(path);
562 }
563 */
564 
OnCopy(bool move,bool copyToSame,unsigned srcPanelIndex)565 void CApp::OnCopy(bool move, bool copyToSame, unsigned srcPanelIndex)
566 {
567   const unsigned destPanelIndex = (NumPanels <= 1) ? srcPanelIndex : (1 - srcPanelIndex);
568   CPanel &srcPanel = Panels[srcPanelIndex];
569   CPanel &destPanel = Panels[destPanelIndex];
570 
571   CPanel::CDisableTimerProcessing disableTimerProcessing1(destPanel);
572   CPanel::CDisableTimerProcessing disableTimerProcessing2(srcPanel);
573 
574   if (move)
575   {
576     if (!srcPanel.CheckBeforeUpdate(IDS_MOVE))
577       return;
578   }
579   else if (!srcPanel.DoesItSupportOperations())
580   {
581     srcPanel.MessageBox_Error_UnsupportOperation();
582     return;
583   }
584 
585   CRecordVector<UInt32> indices;
586   UString destPath;
587   bool useDestPanel = false;
588 
589   {
590     if (copyToSame)
591     {
592       const int focusedItem = srcPanel._listView.GetFocusedItem();
593       if (focusedItem < 0)
594         return;
595       const unsigned realIndex = srcPanel.GetRealItemIndex(focusedItem);
596       if (realIndex == kParentIndex)
597         return;
598       indices.Add(realIndex);
599       destPath = srcPanel.GetItemName(realIndex);
600     }
601     else
602     {
603       srcPanel.Get_ItemIndices_OperSmart(indices);
604       if (indices.Size() == 0)
605         return;
606       destPath = destPanel.GetFsPath();
607       if (NumPanels == 1)
608         Reduce_Path_To_RealFileSystemPath(destPath);
609     }
610   }
611 
612   UStringVector copyFolders;
613   ReadCopyHistory(copyFolders);
614 
615   const bool useFullItemPaths = srcPanel.Is_IO_FS_Folder(); // maybe we need flat also here ??
616 
617   {
618     CCopyDialog copyDialog;
619 
620     copyDialog.Strings = copyFolders;
621     copyDialog.Value = destPath;
622     LangString(move ? IDS_MOVE : IDS_COPY, copyDialog.Title);
623     LangString(move ? IDS_MOVE_TO : IDS_COPY_TO, copyDialog.Static);
624     copyDialog.Info = srcPanel.GetItemsInfoString(indices);
625 
626     if (copyDialog.Create(srcPanel.GetParent()) != IDOK)
627       return;
628 
629     destPath = copyDialog.Value;
630   }
631 
632   {
633     if (destPath.IsEmpty())
634     {
635       srcPanel.MessageBox_Error_UnsupportOperation();
636       return;
637     }
638 
639     UString correctName;
640     if (!srcPanel.CorrectFsPath(destPath, correctName))
641     {
642       srcPanel.MessageBox_Error_HRESULT(E_INVALIDARG);
643       return;
644     }
645 
646     if (IsAbsolutePath(destPath))
647       destPath.Empty();
648     else
649       destPath = srcPanel.GetFsPath();
650     destPath += correctName;
651 
652     #if defined(_WIN32) && !defined(UNDER_CE)
653     if (destPath.Len() != 0 && destPath[0] == '\\')
654       if (destPath.Len() == 1 || destPath[1] != '\\')
655       {
656         srcPanel.MessageBox_Error_UnsupportOperation();
657         return;
658       }
659     #endif
660 
661     bool possibleToUseDestPanel = false;
662 
663     if (CompareFileNames(destPath, destPanel.GetFsPath()) == 0)
664     {
665       if (NumPanels == 1 || CompareFileNames(destPath, srcPanel.GetFsPath()) == 0)
666       {
667         srcPanel.MessageBox_Error(L"Cannot copy files onto itself");
668         return;
669       }
670 
671       if (destPanel.DoesItSupportOperations())
672         possibleToUseDestPanel = true;
673     }
674 
675     bool destIsFsPath = false;
676 
677     if (possibleToUseDestPanel)
678     {
679       if (destPanel.IsFSFolder() || destPanel.IsAltStreamsFolder())
680         destIsFsPath = true;
681       else if (destPanel.IsFSDrivesFolder() || destPanel.IsRootFolder())
682       {
683         srcPanel.MessageBox_Error_UnsupportOperation();
684         return;
685       }
686     }
687     else
688     {
689       if (IsAltPathPrefix(us2fs(destPath)))
690       {
691         // we allow alt streams dest only to alt stream folder in second panel
692         srcPanel.MessageBox_Error_UnsupportOperation();
693         return;
694         /*
695         FString basePath = us2fs(destPath);
696         basePath.DeleteBack();
697         if (!DoesFileOrDirExist(basePath))
698         {
699           srcPanel.MessageBoxError2Lines(basePath, ERROR_FILE_NOT_FOUND); // GetLastError()
700           return;
701         }
702         destIsFsPath = true;
703         */
704       }
705       else
706       {
707         if (indices.Size() == 1 &&
708           !destPath.IsEmpty() && !IS_PATH_SEPAR(destPath.Back()))
709         {
710           int pos = destPath.ReverseFind_PathSepar();
711           if (pos < 0)
712           {
713             srcPanel.MessageBox_Error_UnsupportOperation();
714             return;
715           }
716           {
717             /*
718             #ifdef _WIN32
719             UString name = destPath.Ptr(pos + 1);
720             if (name.Find(L':') >= 0)
721             {
722               srcPanel.MessageBox_Error_UnsupportOperation();
723               return;
724             }
725             #endif
726             */
727             UString prefix = destPath.Left(pos + 1);
728             if (!CreateComplexDir(us2fs(prefix)))
729             {
730               const HRESULT lastError = GetLastError_noZero_HRESULT();
731               srcPanel.MessageBox_Error_2Lines_Message_HRESULT(prefix, lastError);
732               return;
733             }
734           }
735           // bool isFolder = srcPanael.IsItem_Folder(indices[0]);
736         }
737         else
738         {
739           NName::NormalizeDirPathPrefix(destPath);
740           if (!CreateComplexDir(us2fs(destPath)))
741           {
742             const HRESULT lastError = GetLastError_noZero_HRESULT();
743             srcPanel.MessageBox_Error_2Lines_Message_HRESULT(destPath, lastError);
744             return;
745           }
746         }
747         destIsFsPath = true;
748       }
749     }
750 
751     if (!destIsFsPath)
752       useDestPanel = true;
753 
754     AddUniqueStringToHeadOfList(copyFolders, destPath);
755     while (copyFolders.Size() > 20)
756       copyFolders.DeleteBack();
757     SaveCopyHistory(copyFolders);
758   }
759 
760   bool useSrcPanel = !useDestPanel || !srcPanel.Is_IO_FS_Folder();
761 
762   bool useTemp = useSrcPanel && useDestPanel;
763   if (useTemp && NumPanels == 1)
764   {
765     srcPanel.MessageBox_Error_UnsupportOperation();
766     return;
767   }
768 
769   CTempDir tempDirectory;
770   FString tempDirPrefix;
771   if (useTemp)
772   {
773     tempDirectory.Create(kTempDirPrefix);
774     tempDirPrefix = tempDirectory.GetPath();
775     NFile::NName::NormalizeDirPathPrefix(tempDirPrefix);
776   }
777 
778   CSelectedState srcSelState;
779   CSelectedState destSelState;
780   srcPanel.SaveSelectedState(srcSelState);
781   destPanel.SaveSelectedState(destSelState);
782 
783   CPanel::CDisableNotify disableNotify1(destPanel);
784   CPanel::CDisableNotify disableNotify2(srcPanel);
785 
786   HRESULT result = S_OK;
787 
788   if (useSrcPanel)
789   {
790     CCopyToOptions options;
791     // options.src_Is_IO_FS_Folder = useFullItemPaths;
792     options.folder = useTemp ? fs2us(tempDirPrefix) : destPath;
793     options.moveMode = move;
794     options.includeAltStreams = true;
795     options.replaceAltStreamChars = false;
796     options.showErrorMessages = true;
797 
798     result = srcPanel.CopyTo(options, indices, NULL);
799   }
800 
801   if (result == S_OK && useDestPanel)
802   {
803     UStringVector filePaths;
804     UString folderPrefix;
805 
806     if (useTemp)
807       folderPrefix = fs2us(tempDirPrefix);
808     else
809       folderPrefix = srcPanel.GetFsPath();
810 
811     filePaths.ClearAndReserve(indices.Size());
812 
813     FOR_VECTOR (i, indices)
814     {
815       UInt32 index = indices[i];
816       UString s;
817       if (useFullItemPaths)
818         s = srcPanel.GetItemRelPath2(index);
819       else
820         s = srcPanel.GetItemName_for_Copy(index);
821       filePaths.AddInReserved(s);
822     }
823 
824     result = destPanel.CopyFrom(move, folderPrefix, filePaths, true, NULL);
825   }
826 
827   if (result != S_OK)
828   {
829     // disableNotify1.Restore();
830     // disableNotify2.Restore();
831     // For Password:
832     // srcPanel.SetFocusToList();
833     // srcPanel.InvalidateList(NULL, true);
834 
835     if (result != E_ABORT)
836       srcPanel.MessageBox_Error_HRESULT(result);
837     // return;
838   }
839 
840   RefreshTitleAlways();
841 
842   if (copyToSame || move)
843   {
844     srcPanel.RefreshListCtrl(srcSelState);
845   }
846 
847   if (!copyToSame)
848   {
849     destPanel.RefreshListCtrl(destSelState);
850     srcPanel.KillSelection();
851   }
852 
853   disableNotify1.Restore();
854   disableNotify2.Restore();
855   srcPanel.SetFocusToList();
856 }
857 
OnSetSameFolder(unsigned srcPanelIndex)858 void CApp::OnSetSameFolder(unsigned srcPanelIndex)
859 {
860   if (NumPanels <= 1)
861     return;
862   const CPanel &srcPanel = Panels[srcPanelIndex];
863   CPanel &destPanel = Panels[1 - srcPanelIndex];
864   destPanel.BindToPathAndRefresh(srcPanel._currentFolderPrefix);
865 }
866 
OnSetSubFolder(unsigned srcPanelIndex)867 void CApp::OnSetSubFolder(unsigned srcPanelIndex)
868 {
869   if (NumPanels <= 1)
870     return;
871   const CPanel &srcPanel = Panels[srcPanelIndex];
872   CPanel &destPanel = Panels[1 - srcPanelIndex];
873 
874   const int focusedItem = srcPanel._listView.GetFocusedItem();
875   if (focusedItem < 0)
876     return;
877   const unsigned realIndex = srcPanel.GetRealItemIndex(focusedItem);
878   if (!srcPanel.IsItem_Folder(realIndex))
879     return;
880 
881   // destPanel.BindToFolder(srcPanel._currentFolderPrefix + srcPanel.GetItemName(realIndex) + WCHAR_PATH_SEPARATOR);
882 
883   CMyComPtr<IFolderFolder> newFolder;
884   if (realIndex == kParentIndex)
885   {
886     if (srcPanel._folder->BindToParentFolder(&newFolder) != S_OK)
887       return;
888     if (!newFolder)
889     {
890       {
891         const UString parentPrefix = srcPanel.GetParentDirPrefix();
892         COpenResult openRes;
893         destPanel.BindToPath(parentPrefix, UString(), openRes);
894       }
895       destPanel.RefreshListCtrl();
896       return;
897     }
898   }
899   else
900   {
901     if (srcPanel._folder->BindToFolder(realIndex, &newFolder) != S_OK)
902       return;
903   }
904 
905   if (!newFolder)
906     return;
907 
908   destPanel.CloseOpenFolders();
909   destPanel.SetNewFolder(newFolder);
910   destPanel.RefreshListCtrl();
911 }
912 
913 /*
914 int CApp::GetFocusedPanelIndex() const
915 {
916   return LastFocusedPanel;
917   HWND hwnd = ::GetFocus();
918   for (;;)
919   {
920     if (hwnd == 0)
921       return 0;
922     for (unsigned i = 0; i < kNumPanelsMax; i++)
923     {
924       if (PanelsCreated[i] &&
925           ((HWND)Panels[i] == hwnd || Panels[i]._listView == hwnd))
926         return i;
927     }
928     hwnd = GetParent(hwnd);
929   }
930 }
931 */
932 
933 static UString g_ToolTipBuffer;
934 static CSysString g_ToolTipBufferSys;
935 
OnNotify(int,LPNMHDR pnmh)936 void CApp::OnNotify(int /* ctrlID */, LPNMHDR pnmh)
937 {
938   {
939     if (pnmh->code == TTN_GETDISPINFO)
940     {
941       LPNMTTDISPINFO info = (LPNMTTDISPINFO)pnmh;
942       info->hinst = NULL;
943       g_ToolTipBuffer.Empty();
944       SetButtonText((int)info->hdr.idFrom, g_ToolTipBuffer);
945       g_ToolTipBufferSys = GetSystemString(g_ToolTipBuffer);
946       info->lpszText = g_ToolTipBufferSys.Ptr_non_const();
947       return;
948     }
949     #ifndef _UNICODE
950     if (pnmh->code == TTN_GETDISPINFOW)
951     {
952       LPNMTTDISPINFOW info = (LPNMTTDISPINFOW)pnmh;
953       info->hinst = NULL;
954       g_ToolTipBuffer.Empty();
955       SetButtonText((int)info->hdr.idFrom, g_ToolTipBuffer);
956       info->lpszText = g_ToolTipBuffer.Ptr_non_const();
957       return;
958     }
959     #endif
960   }
961 }
962 
RefreshTitle(bool always)963 void CApp::RefreshTitle(bool always)
964 {
965   UString path = GetFocusedPanel()._currentFolderPrefix;
966   if (path.IsEmpty())
967     path = "7-Zip"; // LangString(IDS_APP_TITLE);
968   if (!always && path == PrevTitle)
969     return;
970   PrevTitle = path;
971   NWindows::MySetWindowText(_window, path);
972 }
973 
RefreshTitlePanel(unsigned panelIndex,bool always)974 void CApp::RefreshTitlePanel(unsigned panelIndex, bool always)
975 {
976   if (panelIndex != GetFocusedPanelIndex())
977     return;
978   RefreshTitle(always);
979 }
980 
AddUniqueStringToHead(UStringVector & list,const UString & s)981 static void AddUniqueStringToHead(UStringVector &list, const UString &s)
982 {
983   for (unsigned i = 0; i < list.Size();)
984     if (s.IsEqualTo_NoCase(list[i]))
985       list.Delete(i);
986     else
987       i++;
988   list.Insert(0, s);
989 }
990 
991 
Normalize()992 void CFolderHistory::Normalize()
993 {
994   const unsigned kMaxSize = 100;
995   if (Strings.Size() > kMaxSize)
996     Strings.DeleteFrom(kMaxSize);
997 }
998 
AddString(const UString & s)999 void CFolderHistory::AddString(const UString &s)
1000 {
1001   NSynchronization::CCriticalSectionLock lock(_criticalSection);
1002   AddUniqueStringToHead(Strings, s);
1003   Normalize();
1004 }
1005