xref: /aosp_15_r20/external/lzma/CPP/7zip/UI/FileManager/ProgressDialog2.cpp (revision f6dc9357d832569d4d1f5d24eacdb3935a1ae8e6)
1 // ProgressDialog2.cpp
2 
3 #include "StdAfx.h"
4 
5 #ifdef Z7_OLD_WIN_SDK
6 #include <ShlGuid.h>
7 #endif
8 
9 #include "../../../Common/IntToString.h"
10 #include "../../../Common/StringConvert.h"
11 
12 #include "../../../Windows/Clipboard.h"
13 #include "../../../Windows/ErrorMsg.h"
14 
15 #include "../GUI/ExtractRes.h"
16 
17 #include "LangUtils.h"
18 
19 #include "DialogSize.h"
20 #include "ProgressDialog2.h"
21 #include "ProgressDialog2Res.h"
22 
23 using namespace NWindows;
24 
25 extern HINSTANCE g_hInstance;
26 extern bool g_DisableUserQuestions;
27 
28 static const UINT_PTR kTimerID = 3;
29 
30 static const UINT kCloseMessage = WM_APP + 1;
31 // we can't use WM_USER, since WM_USER can be used by standard Windows procedure for Dialog
32 
33 static const UINT kTimerElapse =
34   #ifdef UNDER_CE
35   500
36   #else
37   200
38   #endif
39   ;
40 
41 static const UINT kCreateDelay =
42   #ifdef UNDER_CE
43   2500
44   #else
45   500
46   #endif
47   ;
48 
49 static const DWORD kPauseSleepTime = 100;
50 
51 #ifdef Z7_LANG
52 
53 static const UInt32 kLangIDs[] =
54 {
55   IDT_PROGRESS_ELAPSED,
56   IDT_PROGRESS_REMAINING,
57   IDT_PROGRESS_TOTAL,
58   IDT_PROGRESS_SPEED,
59   IDT_PROGRESS_PROCESSED,
60   IDT_PROGRESS_RATIO,
61   IDT_PROGRESS_ERRORS,
62   IDB_PROGRESS_BACKGROUND,
63   IDB_PAUSE
64 };
65 
66 static const UInt32 kLangIDs_Colon[] =
67 {
68   IDT_PROGRESS_PACKED,
69   IDT_PROGRESS_FILES
70 };
71 
72 #endif
73 
74 
75 #define UNDEFINED_VAL         ((UInt64)(Int64)-1)
76 #define INIT_AS_UNDEFINED(v)  v = UNDEFINED_VAL;
77 #define IS_UNDEFINED_VAL(v)   ((v) == UNDEFINED_VAL)
78 #define IS_DEFINED_VAL(v)     ((v) != UNDEFINED_VAL)
79 
CProgressSync()80 CProgressSync::CProgressSync():
81     _stopped(false),
82     _paused(false),
83     _filesProgressMode(false),
84     _isDir(false),
85     _totalBytes(UNDEFINED_VAL), _completedBytes(0),
86     _totalFiles(UNDEFINED_VAL), _curFiles(0),
87     _inSize(UNDEFINED_VAL),
88     _outSize(UNDEFINED_VAL)
89     {}
90 
91 #define CHECK_STOP  if (_stopped) return E_ABORT; if (!_paused) return S_OK;
92 #define CRITICAL_LOCK NSynchronization::CCriticalSectionLock lock(_cs);
93 
Get_Paused()94 bool CProgressSync::Get_Paused()
95 {
96   CRITICAL_LOCK
97   return _paused;
98 }
99 
CheckStop()100 HRESULT CProgressSync::CheckStop()
101 {
102   for (;;)
103   {
104     {
105       CRITICAL_LOCK
106       CHECK_STOP
107     }
108     ::Sleep(kPauseSleepTime);
109   }
110 }
111 
Clear_Stop_Status()112 void CProgressSync::Clear_Stop_Status()
113 {
114   CRITICAL_LOCK
115   if (_stopped)
116     _stopped = false;
117 }
118 
ScanProgress(UInt64 numFiles,UInt64 totalSize,const FString & fileName,bool isDir)119 HRESULT CProgressSync::ScanProgress(UInt64 numFiles, UInt64 totalSize, const FString &fileName, bool isDir)
120 {
121   {
122     CRITICAL_LOCK
123     _totalFiles = numFiles;
124     _totalBytes = totalSize;
125     _filePath = fs2us(fileName);
126     _isDir = isDir;
127     // _completedBytes = 0;
128     CHECK_STOP
129   }
130   return CheckStop();
131 }
132 
Set_NumFilesTotal(UInt64 val)133 HRESULT CProgressSync::Set_NumFilesTotal(UInt64 val)
134 {
135   {
136     CRITICAL_LOCK
137     _totalFiles = val;
138     CHECK_STOP
139   }
140   return CheckStop();
141 }
142 
Set_NumBytesTotal(UInt64 val)143 void CProgressSync::Set_NumBytesTotal(UInt64 val)
144 {
145   CRITICAL_LOCK
146   _totalBytes = val;
147 }
148 
Set_NumFilesCur(UInt64 val)149 void CProgressSync::Set_NumFilesCur(UInt64 val)
150 {
151   CRITICAL_LOCK
152   _curFiles = val;
153 }
154 
Set_NumBytesCur(const UInt64 * val)155 HRESULT CProgressSync::Set_NumBytesCur(const UInt64 *val)
156 {
157   {
158     CRITICAL_LOCK
159     if (val)
160       _completedBytes = *val;
161     CHECK_STOP
162   }
163   return CheckStop();
164 }
165 
Set_NumBytesCur(UInt64 val)166 HRESULT CProgressSync::Set_NumBytesCur(UInt64 val)
167 {
168   {
169     CRITICAL_LOCK
170     _completedBytes = val;
171     CHECK_STOP
172   }
173   return CheckStop();
174 }
175 
Set_Ratio(const UInt64 * inSize,const UInt64 * outSize)176 void CProgressSync::Set_Ratio(const UInt64 *inSize, const UInt64 *outSize)
177 {
178   CRITICAL_LOCK
179   if (inSize)
180     _inSize = *inSize;
181   if (outSize)
182     _outSize = *outSize;
183 }
184 
Set_TitleFileName(const UString & fileName)185 void CProgressSync::Set_TitleFileName(const UString &fileName)
186 {
187   CRITICAL_LOCK
188   _titleFileName = fileName;
189 }
190 
Set_Status(const UString & s)191 void CProgressSync::Set_Status(const UString &s)
192 {
193   CRITICAL_LOCK
194   _status = s;
195 }
196 
Set_Status2(const UString & s,const wchar_t * path,bool isDir)197 HRESULT CProgressSync::Set_Status2(const UString &s, const wchar_t *path, bool isDir)
198 {
199   {
200     CRITICAL_LOCK
201     _status = s;
202     if (path)
203       _filePath = path;
204     else
205       _filePath.Empty();
206     _isDir = isDir;
207   }
208   return CheckStop();
209 }
210 
Set_FilePath(const wchar_t * path,bool isDir)211 void CProgressSync::Set_FilePath(const wchar_t *path, bool isDir)
212 {
213   CRITICAL_LOCK
214   if (path)
215     _filePath = path;
216   else
217     _filePath.Empty();
218   _isDir = isDir;
219 }
220 
221 
AddError_Message(const wchar_t * message)222 void CProgressSync::AddError_Message(const wchar_t *message)
223 {
224   CRITICAL_LOCK
225   Messages.Add(message);
226 }
227 
AddError_Message_Name(const wchar_t * message,const wchar_t * name)228 void CProgressSync::AddError_Message_Name(const wchar_t *message, const wchar_t *name)
229 {
230   UString s;
231   if (name && *name != 0)
232     s += name;
233   if (message && *message != 0)
234   {
235     if (!s.IsEmpty())
236       s.Add_LF();
237     s += message;
238     if (!s.IsEmpty() && s.Back() == L'\n')
239       s.DeleteBack();
240   }
241   AddError_Message(s);
242 }
243 
AddError_Code_Name(HRESULT systemError,const wchar_t * name)244 void CProgressSync::AddError_Code_Name(HRESULT systemError, const wchar_t *name)
245 {
246   UString s = NError::MyFormatMessage(systemError);
247   if (systemError == 0)
248     s = "Error";
249   AddError_Message_Name(s, name);
250 }
251 
CProgressDialog()252 CProgressDialog::CProgressDialog():
253     _isDir(false),
254     _wasCreated(false),
255     _needClose(false),
256     _errorsWereDisplayed(false),
257     _waitCloseByCancelButton(false),
258     _cancelWasPressed(false),
259     _inCancelMessageBox(false),
260     _externalCloseMessageWasReceived(false),
261     _background(false),
262     WaitMode(false),
263     MessagesDisplayed(false),
264     CompressingMode(true),
265     ShowCompressionInfo(true),
266     _numPostedMessages(0),
267     _numAutoSizeMessages(0),
268     _numMessages(0),
269     _timer(0),
270     IconID(-1),
271     MainWindow(NULL)
272 {
273 
274   if (_dialogCreatedEvent.Create() != S_OK)
275     throw 1334987;
276   if (_createDialogEvent.Create() != S_OK)
277     throw 1334987;
278   // #ifdef __ITaskbarList3_INTERFACE_DEFINED__
279   CoCreateInstance(CLSID_TaskbarList, NULL, CLSCTX_INPROC_SERVER, IID_ITaskbarList3, (void**)&_taskbarList);
280   if (_taskbarList)
281     _taskbarList->HrInit();
282   // #endif
283 }
284 
285 #ifndef Z7_SFX
286 
~CProgressDialog()287 CProgressDialog::~CProgressDialog()
288 {
289   // #ifdef __ITaskbarList3_INTERFACE_DEFINED__
290   SetTaskbarProgressState(TBPF_NOPROGRESS);
291   // #endif
292   AddToTitle(L"");
293 }
AddToTitle(LPCWSTR s)294 void CProgressDialog::AddToTitle(LPCWSTR s)
295 {
296   if (MainWindow)
297   {
298     CWindow window(MainWindow);
299     window.SetText((UString)s + MainTitle);
300   }
301 }
302 
303 #endif
304 
305 
SetTaskbarProgressState()306 void CProgressDialog::SetTaskbarProgressState()
307 {
308   // #ifdef __ITaskbarList3_INTERFACE_DEFINED__
309   if (_taskbarList && _hwndForTaskbar)
310   {
311     TBPFLAG tbpFlags;
312     if (Sync.Get_Paused())
313       tbpFlags = TBPF_PAUSED;
314     else
315       tbpFlags = _errorsWereDisplayed ? TBPF_ERROR: TBPF_NORMAL;
316     SetTaskbarProgressState(tbpFlags);
317   }
318   // #endif
319 }
320 
321 static const unsigned kTitleFileNameSizeLimit = 36;
322 static const unsigned kCurrentFileNameSizeLimit = 82;
323 
ReduceString(UString & s,unsigned size)324 static void ReduceString(UString &s, unsigned size)
325 {
326   if (s.Len() <= size)
327     return;
328   s.Delete(size / 2, s.Len() - size);
329   s.Insert(size / 2, L" ... ");
330 }
331 
EnableErrorsControls(bool enable)332 void CProgressDialog::EnableErrorsControls(bool enable)
333 {
334   ShowItem_Bool(IDT_PROGRESS_ERRORS, enable);
335   ShowItem_Bool(IDT_PROGRESS_ERRORS_VAL, enable);
336   ShowItem_Bool(IDL_PROGRESS_MESSAGES, enable);
337 }
338 
OnInit()339 bool CProgressDialog::OnInit()
340 {
341   _hwndForTaskbar = MainWindow;
342   if (!_hwndForTaskbar)
343     _hwndForTaskbar = GetParent();
344   if (!_hwndForTaskbar)
345     _hwndForTaskbar = *this;
346 
347   INIT_AS_UNDEFINED(_progressBar_Range)
348   INIT_AS_UNDEFINED(_progressBar_Pos)
349 
350   INIT_AS_UNDEFINED(_prevPercentValue)
351   INIT_AS_UNDEFINED(_prevElapsedSec)
352   INIT_AS_UNDEFINED(_prevRemainingSec)
353 
354   INIT_AS_UNDEFINED(_prevSpeed)
355   _prevSpeed_MoveBits = 0;
356 
357   _prevTime = ::GetTickCount();
358   _elapsedTime = 0;
359 
360   INIT_AS_UNDEFINED(_totalBytes_Prev)
361   INIT_AS_UNDEFINED(_processed_Prev)
362   INIT_AS_UNDEFINED(_packed_Prev)
363   INIT_AS_UNDEFINED(_ratio_Prev)
364 
365   _filesStr_Prev.Empty();
366   _filesTotStr_Prev.Empty();
367 
368   m_ProgressBar.Attach(GetItem(IDC_PROGRESS1));
369   _messageList.Attach(GetItem(IDL_PROGRESS_MESSAGES));
370   _messageList.SetUnicodeFormat();
371   _messageList.SetExtendedListViewStyle(LVS_EX_FULLROWSELECT);
372 
373   _wasCreated = true;
374   _dialogCreatedEvent.Set();
375 
376   #ifdef Z7_LANG
377   LangSetDlgItems(*this, kLangIDs, Z7_ARRAY_SIZE(kLangIDs));
378   LangSetDlgItems_Colon(*this, kLangIDs_Colon, Z7_ARRAY_SIZE(kLangIDs_Colon));
379   #endif
380 
381   CWindow window(GetItem(IDB_PROGRESS_BACKGROUND));
382   window.GetText(_background_String);
383   _backgrounded_String = _background_String;
384   _backgrounded_String.RemoveChar(L'&');
385 
386   window = GetItem(IDB_PAUSE);
387   window.GetText(_pause_String);
388 
389   LangString(IDS_PROGRESS_FOREGROUND, _foreground_String);
390   LangString(IDS_CONTINUE, _continue_String);
391   LangString(IDS_PROGRESS_PAUSED, _paused_String);
392 
393   SetText(_title);
394   SetPauseText();
395   SetPriorityText();
396 
397   _messageList.InsertColumn(0, L"", 40);
398   _messageList.InsertColumn(1, L"", 460);
399   _messageList.SetColumnWidthAuto(0);
400   _messageList.SetColumnWidthAuto(1);
401 
402   EnableErrorsControls(false);
403 
404   GetItemSizes(IDCANCEL, _buttonSizeX, _buttonSizeY);
405   _numReduceSymbols = kCurrentFileNameSizeLimit;
406   NormalizeSize(true);
407 
408   if (!ShowCompressionInfo)
409   {
410     HideItem(IDT_PROGRESS_PACKED);
411     HideItem(IDT_PROGRESS_PACKED_VAL);
412     HideItem(IDT_PROGRESS_RATIO);
413     HideItem(IDT_PROGRESS_RATIO_VAL);
414   }
415 
416   if (IconID >= 0)
417   {
418     HICON icon = LoadIcon(g_hInstance, MAKEINTRESOURCE(IconID));
419     // SetIcon(ICON_SMALL, icon);
420     SetIcon(ICON_BIG, icon);
421   }
422   _timer = SetTimer(kTimerID, kTimerElapse);
423   #ifdef UNDER_CE
424   Foreground();
425   #endif
426 
427   CheckNeedClose();
428 
429   SetTaskbarProgressState();
430 
431   return CModalDialog::OnInit();
432 }
433 
434 static const UINT kIDs[] =
435 {
436   IDT_PROGRESS_ELAPSED,   IDT_PROGRESS_ELAPSED_VAL,
437   IDT_PROGRESS_REMAINING, IDT_PROGRESS_REMAINING_VAL,
438   IDT_PROGRESS_FILES,     IDT_PROGRESS_FILES_VAL,
439   0,                      IDT_PROGRESS_FILES_TOTAL,
440   IDT_PROGRESS_ERRORS,    IDT_PROGRESS_ERRORS_VAL,
441 
442   IDT_PROGRESS_TOTAL,     IDT_PROGRESS_TOTAL_VAL,
443   IDT_PROGRESS_SPEED,     IDT_PROGRESS_SPEED_VAL,
444   IDT_PROGRESS_PROCESSED, IDT_PROGRESS_PROCESSED_VAL,
445   IDT_PROGRESS_PACKED,    IDT_PROGRESS_PACKED_VAL,
446   IDT_PROGRESS_RATIO,     IDT_PROGRESS_RATIO_VAL
447 };
448 
OnSize(WPARAM,int xSize,int ySize)449 bool CProgressDialog::OnSize(WPARAM /* wParam */, int xSize, int ySize)
450 {
451   int sY;
452   int sStep;
453   int mx, my;
454   {
455     RECT r;
456     GetClientRectOfItem(IDT_PROGRESS_ELAPSED, r);
457     mx = r.left;
458     my = r.top;
459     sY = RECT_SIZE_Y(r);
460     GetClientRectOfItem(IDT_PROGRESS_REMAINING, r);
461     sStep = r.top - my;
462   }
463 
464   InvalidateRect(NULL);
465 
466   const int xSizeClient = xSize - mx * 2;
467 
468   {
469     unsigned i;
470     for (i = 800; i > 40; i = i * 9 / 10)
471       if (Units_To_Pixels_X((int)i) <= xSizeClient)
472         break;
473     _numReduceSymbols = i / 4;
474   }
475 
476   int yPos = ySize - my - _buttonSizeY;
477 
478   ChangeSubWindowSizeX(GetItem(IDT_PROGRESS_STATUS), xSize - mx * 2);
479   ChangeSubWindowSizeX(GetItem(IDT_PROGRESS_FILE_NAME), xSize - mx * 2);
480   ChangeSubWindowSizeX(GetItem(IDC_PROGRESS1), xSize - mx * 2);
481 
482   int bSizeX = _buttonSizeX;
483   int mx2 = mx;
484   for (;; mx2--)
485   {
486     const int bSize2 = bSizeX * 3 + mx2 * 2;
487     if (bSize2 <= xSizeClient)
488       break;
489     if (mx2 < 5)
490     {
491       bSizeX = (xSizeClient - mx2 * 2) / 3;
492       break;
493     }
494   }
495   if (bSizeX < 2)
496     bSizeX = 2;
497 
498   {
499     RECT r;
500     GetClientRectOfItem(IDL_PROGRESS_MESSAGES, r);
501     const int y = r.top;
502     int ySize2 = yPos - my - y;
503     const int kMinYSize = _buttonSizeY + _buttonSizeY * 3 / 4;
504     int xx = xSize - mx * 2;
505     if (ySize2 < kMinYSize)
506     {
507       ySize2 = kMinYSize;
508       if (xx > bSizeX * 2)
509         xx -= bSizeX;
510     }
511 
512     _messageList.Move(mx, y, xx, ySize2);
513   }
514 
515   {
516     int xPos = xSize - mx;
517     xPos -= bSizeX;
518     MoveItem(IDCANCEL, xPos, yPos, bSizeX, _buttonSizeY);
519     xPos -= (mx2 + bSizeX);
520     MoveItem(IDB_PAUSE, xPos, yPos, bSizeX, _buttonSizeY);
521     xPos -= (mx2 + bSizeX);
522     MoveItem(IDB_PROGRESS_BACKGROUND, xPos, yPos, bSizeX, _buttonSizeY);
523   }
524 
525   int valueSize;
526   int labelSize;
527   int padSize;
528 
529   labelSize = Units_To_Pixels_X(MY_PROGRESS_LABEL_UNITS_MIN);
530   valueSize = Units_To_Pixels_X(MY_PROGRESS_VAL_UNITS);
531   padSize = Units_To_Pixels_X(MY_PROGRESS_PAD_UNITS);
532   const int requiredSize = (labelSize + valueSize) * 2 + padSize;
533 
534   int gSize;
535   {
536     if (requiredSize < xSizeClient)
537     {
538       const int incr = (xSizeClient - requiredSize) / 3;
539       labelSize += incr;
540     }
541     else
542       labelSize = (xSizeClient - valueSize * 2 - padSize) / 2;
543     if (labelSize < 0)
544       labelSize = 0;
545 
546     gSize = labelSize + valueSize;
547     padSize = xSizeClient - gSize * 2;
548   }
549 
550   labelSize = gSize - valueSize;
551 
552   yPos = my;
553   for (unsigned i = 0; i < Z7_ARRAY_SIZE(kIDs); i += 2)
554   {
555     int x = mx;
556     const unsigned kNumColumn1Items = 5 * 2;
557     if (i >= kNumColumn1Items)
558     {
559       if (i == kNumColumn1Items)
560         yPos = my;
561       x = mx + gSize + padSize;
562     }
563     if (kIDs[i] != 0)
564     MoveItem(kIDs[i], x, yPos, labelSize, sY);
565     MoveItem(kIDs[i + 1], x + labelSize, yPos, valueSize, sY);
566     yPos += sStep;
567   }
568   return false;
569 }
570 
OnCancel()571 void CProgressDialog::OnCancel() { Sync.Set_Stopped(true); }
OnOK()572 void CProgressDialog::OnOK() { }
573 
SetProgressRange(UInt64 range)574 void CProgressDialog::SetProgressRange(UInt64 range)
575 {
576   if (range == _progressBar_Range)
577     return;
578   _progressBar_Range = range;
579   INIT_AS_UNDEFINED(_progressBar_Pos)
580   _progressConv.Init(range);
581   m_ProgressBar.SetRange32(0, _progressConv.Count(range));
582 }
583 
SetProgressPos(UInt64 pos)584 void CProgressDialog::SetProgressPos(UInt64 pos)
585 {
586   if (pos >= _progressBar_Range ||
587       pos <= _progressBar_Pos ||
588       pos - _progressBar_Pos >= (_progressBar_Range >> 10))
589   {
590     m_ProgressBar.SetPos(_progressConv.Count(pos));
591     // #ifdef __ITaskbarList3_INTERFACE_DEFINED__
592     if (_taskbarList && _hwndForTaskbar)
593       _taskbarList->SetProgressValue(_hwndForTaskbar, pos, _progressBar_Range);
594     // #endif
595     _progressBar_Pos = pos;
596   }
597 }
598 
599 #define UINT_TO_STR_2(val) { s[0] = (wchar_t)('0' + (val) / 10); s[1] = (wchar_t)('0' + (val) % 10); s += 2; }
600 
601 void GetTimeString(UInt64 timeValue, wchar_t *s);
GetTimeString(UInt64 timeValue,wchar_t * s)602 void GetTimeString(UInt64 timeValue, wchar_t *s)
603 {
604   UInt64 hours = timeValue / 3600;
605   UInt32 seconds = (UInt32)(timeValue - hours * 3600);
606   UInt32 minutes = seconds / 60;
607   seconds %= 60;
608   if (hours > 99)
609   {
610     ConvertUInt64ToString(hours, s);
611     for (; *s != 0; s++);
612   }
613   else
614   {
615     UInt32 hours32 = (UInt32)hours;
616     UINT_TO_STR_2(hours32)
617   }
618   *s++ = ':'; UINT_TO_STR_2(minutes)
619   *s++ = ':'; UINT_TO_STR_2(seconds)
620   *s = 0;
621 }
622 
ConvertSizeToString(UInt64 v,wchar_t * s)623 static void ConvertSizeToString(UInt64 v, wchar_t *s)
624 {
625   Byte c = 0;
626        if (v >= ((UInt64)100000 << 20)) { v >>= 30; c = 'G'; }
627   else if (v >= ((UInt64)100000 << 10)) { v >>= 20; c = 'M'; }
628   else if (v >= ((UInt64)100000 <<  0)) { v >>= 10; c = 'K'; }
629   ConvertUInt64ToString(v, s);
630   if (c != 0)
631   {
632     s += MyStringLen(s);
633     *s++ = ' ';
634     *s++ = c;
635     *s++ = 'B';
636     *s++ = 0;
637   }
638 }
639 
ShowSize(unsigned id,UInt64 val,UInt64 & prev)640 void CProgressDialog::ShowSize(unsigned id, UInt64 val, UInt64 &prev)
641 {
642   if (val == prev)
643     return;
644   prev = val;
645   wchar_t s[40];
646   s[0] = 0;
647   if (IS_DEFINED_VAL(val))
648     ConvertSizeToString(val, s);
649   SetItemText(id, s);
650 }
651 
GetChangedString(const UString & newStr,UString & prevStr,bool & hasChanged)652 static void GetChangedString(const UString &newStr, UString &prevStr, bool &hasChanged)
653 {
654   hasChanged = !(prevStr == newStr);
655   if (hasChanged)
656     prevStr = newStr;
657 }
658 
GetPower32(UInt32 val)659 static unsigned GetPower32(UInt32 val)
660 {
661   const unsigned kStart = 32;
662   UInt32 mask = ((UInt32)1 << (kStart - 1));
663   for (unsigned i = kStart;; i--)
664   {
665     if (i == 0 || (val & mask) != 0)
666       return i;
667     mask >>= 1;
668   }
669 }
670 
GetPower64(UInt64 val)671 static unsigned GetPower64(UInt64 val)
672 {
673   UInt32 high = (UInt32)(val >> 32);
674   if (high == 0)
675     return GetPower32((UInt32)val);
676   return GetPower32(high) + 32;
677 }
678 
MyMultAndDiv(UInt64 mult1,UInt64 mult2,UInt64 divider)679 static UInt64 MyMultAndDiv(UInt64 mult1, UInt64 mult2, UInt64 divider)
680 {
681   unsigned pow1 = GetPower64(mult1);
682   unsigned pow2 = GetPower64(mult2);
683   while (pow1 + pow2 > 64)
684   {
685     if (pow1 > pow2) { pow1--; mult1 >>= 1; }
686     else             { pow2--; mult2 >>= 1; }
687     divider >>= 1;
688   }
689   UInt64 res = mult1 * mult2;
690   if (divider != 0)
691     res /= divider;
692   return res;
693 }
694 
UpdateStatInfo(bool showAll)695 void CProgressDialog::UpdateStatInfo(bool showAll)
696 {
697   UInt64 total, completed, totalFiles, completedFiles, inSize, outSize;
698   bool filesProgressMode;
699 
700   bool titleFileName_Changed;
701   bool curFilePath_Changed;
702   bool status_Changed;
703   unsigned numErrors;
704   {
705     NSynchronization::CCriticalSectionLock lock(Sync._cs);
706     total = Sync._totalBytes;
707     completed = Sync._completedBytes;
708     totalFiles = Sync._totalFiles;
709     completedFiles = Sync._curFiles;
710     inSize = Sync._inSize;
711     outSize = Sync._outSize;
712     filesProgressMode = Sync._filesProgressMode;
713 
714     GetChangedString(Sync._titleFileName, _titleFileName, titleFileName_Changed);
715     GetChangedString(Sync._filePath, _filePath, curFilePath_Changed);
716     GetChangedString(Sync._status, _status, status_Changed);
717     if (_isDir != Sync._isDir)
718     {
719       curFilePath_Changed = true;
720       _isDir = Sync._isDir;
721     }
722     numErrors = Sync.Messages.Size();
723   }
724 
725   UInt32 curTime = ::GetTickCount();
726 
727   const UInt64 progressTotal = filesProgressMode ? totalFiles : total;
728   const UInt64 progressCompleted = filesProgressMode ? completedFiles : completed;
729   {
730     if (IS_UNDEFINED_VAL(progressTotal))
731     {
732       // SetPos(0);
733       // SetRange(progressCompleted);
734     }
735     else
736     {
737       if (_progressBar_Pos != 0 || progressCompleted != 0 ||
738           (_progressBar_Range == 0 && progressTotal != 0))
739       {
740         SetProgressRange(progressTotal);
741         SetProgressPos(progressCompleted);
742       }
743     }
744   }
745 
746   ShowSize(IDT_PROGRESS_TOTAL_VAL, total, _totalBytes_Prev);
747 
748   _elapsedTime += (curTime - _prevTime);
749   _prevTime = curTime;
750   UInt64 elapsedSec = _elapsedTime / 1000;
751   bool elapsedChanged = false;
752   if (elapsedSec != _prevElapsedSec)
753   {
754     _prevElapsedSec = elapsedSec;
755     elapsedChanged = true;
756     wchar_t s[40];
757     GetTimeString(elapsedSec, s);
758     SetItemText(IDT_PROGRESS_ELAPSED_VAL, s);
759   }
760 
761   bool needSetTitle = false;
762   if (elapsedChanged || showAll)
763   {
764     if (numErrors > _numPostedMessages)
765     {
766       UpdateMessagesDialog();
767       wchar_t s[32];
768       ConvertUInt64ToString(numErrors, s);
769       SetItemText(IDT_PROGRESS_ERRORS_VAL, s);
770       if (!_errorsWereDisplayed)
771       {
772         _errorsWereDisplayed = true;
773         EnableErrorsControls(true);
774         SetTaskbarProgressState();
775       }
776     }
777 
778     if (progressCompleted != 0)
779     {
780       if (IS_UNDEFINED_VAL(progressTotal))
781       {
782         if (IS_DEFINED_VAL(_prevRemainingSec))
783         {
784           INIT_AS_UNDEFINED(_prevRemainingSec)
785           SetItemText(IDT_PROGRESS_REMAINING_VAL, L"");
786         }
787       }
788       else
789       {
790         UInt64 remainingTime = 0;
791         if (progressCompleted < progressTotal)
792           remainingTime = MyMultAndDiv(_elapsedTime, progressTotal - progressCompleted, progressCompleted);
793         UInt64 remainingSec = remainingTime / 1000;
794         if (remainingSec != _prevRemainingSec)
795         {
796           _prevRemainingSec = remainingSec;
797           wchar_t s[40];
798           GetTimeString(remainingSec, s);
799           SetItemText(IDT_PROGRESS_REMAINING_VAL, s);
800         }
801       }
802       {
803         const UInt64 elapsedTime = (_elapsedTime == 0) ? 1 : _elapsedTime;
804         // 22.02: progressCompleted can be for number of files
805         UInt64 v = (completed * 1000) / elapsedTime;
806         Byte c = 0;
807         unsigned moveBits = 0;
808              if (v >= ((UInt64)10000 << 10)) { moveBits = 20; c = 'M'; }
809         else if (v >= ((UInt64)10000 <<  0)) { moveBits = 10; c = 'K'; }
810         v >>= moveBits;
811         if (moveBits != _prevSpeed_MoveBits || v != _prevSpeed)
812         {
813           _prevSpeed_MoveBits = moveBits;
814           _prevSpeed = v;
815           wchar_t s[40];
816           ConvertUInt64ToString(v, s);
817           unsigned pos = MyStringLen(s);
818           s[pos++] = ' ';
819           if (moveBits != 0)
820             s[pos++] = c;
821           s[pos++] = 'B';
822           s[pos++] = '/';
823           s[pos++] = 's';
824           s[pos++] = 0;
825           SetItemText(IDT_PROGRESS_SPEED_VAL, s);
826         }
827       }
828     }
829 
830     {
831       UInt64 percent = 0;
832       {
833         if (IS_DEFINED_VAL(progressTotal))
834         {
835           percent = progressCompleted * 100;
836           if (progressTotal != 0)
837             percent /= progressTotal;
838         }
839       }
840       if (percent != _prevPercentValue)
841       {
842         _prevPercentValue = percent;
843         needSetTitle = true;
844       }
845     }
846 
847     {
848       wchar_t s[64];
849 
850       ConvertUInt64ToString(completedFiles, s);
851       if (_filesStr_Prev != s)
852       {
853         _filesStr_Prev = s;
854         SetItemText(IDT_PROGRESS_FILES_VAL, s);
855       }
856 
857       s[0] = 0;
858       if (IS_DEFINED_VAL(totalFiles))
859       {
860         MyStringCopy(s, L" / ");
861         ConvertUInt64ToString(totalFiles, s + MyStringLen(s));
862       }
863       if (_filesTotStr_Prev != s)
864       {
865         _filesTotStr_Prev = s;
866         SetItemText(IDT_PROGRESS_FILES_TOTAL, s);
867       }
868     }
869 
870     const UInt64 packSize   = CompressingMode ? outSize : inSize;
871     const UInt64 unpackSize = CompressingMode ? inSize : outSize;
872 
873     if (IS_UNDEFINED_VAL(unpackSize) &&
874         IS_UNDEFINED_VAL(packSize))
875     {
876       ShowSize(IDT_PROGRESS_PROCESSED_VAL, completed, _processed_Prev);
877       ShowSize(IDT_PROGRESS_PACKED_VAL, UNDEFINED_VAL, _packed_Prev);
878     }
879     else
880     {
881       ShowSize(IDT_PROGRESS_PROCESSED_VAL, unpackSize, _processed_Prev);
882       ShowSize(IDT_PROGRESS_PACKED_VAL, packSize, _packed_Prev);
883 
884       if (IS_DEFINED_VAL(packSize) &&
885           IS_DEFINED_VAL(unpackSize) &&
886           unpackSize != 0)
887       {
888         wchar_t s[32];
889         UInt64 ratio = packSize * 100 / unpackSize;
890         if (_ratio_Prev != ratio)
891         {
892           _ratio_Prev = ratio;
893           ConvertUInt64ToString(ratio, s);
894           MyStringCat(s, L"%");
895           SetItemText(IDT_PROGRESS_RATIO_VAL, s);
896         }
897       }
898     }
899   }
900 
901   if (needSetTitle || titleFileName_Changed)
902     SetTitleText();
903 
904   if (status_Changed)
905   {
906     UString s = _status;
907     ReduceString(s, _numReduceSymbols);
908     SetItemText(IDT_PROGRESS_STATUS, s);
909   }
910 
911   if (curFilePath_Changed)
912   {
913     UString s1, s2;
914     if (_isDir)
915       s1 = _filePath;
916     else
917     {
918       int slashPos = _filePath.ReverseFind_PathSepar();
919       if (slashPos >= 0)
920       {
921         s1.SetFrom(_filePath, (unsigned)(slashPos + 1));
922         s2 = _filePath.Ptr((unsigned)(slashPos + 1));
923       }
924       else
925         s2 = _filePath;
926     }
927     ReduceString(s1, _numReduceSymbols);
928     ReduceString(s2, _numReduceSymbols);
929     s1.Add_LF();
930     s1 += s2;
931     SetItemText(IDT_PROGRESS_FILE_NAME, s1);
932   }
933 }
934 
OnTimer(WPARAM,LPARAM)935 bool CProgressDialog::OnTimer(WPARAM /* timerID */, LPARAM /* callback */)
936 {
937   if (Sync.Get_Paused())
938     return true;
939   CheckNeedClose();
940   UpdateStatInfo(false);
941   return true;
942 }
943 
944 struct CWaitCursor
945 {
946   HCURSOR _waitCursor;
947   HCURSOR _oldCursor;
CWaitCursorCWaitCursor948   CWaitCursor()
949   {
950     _waitCursor = LoadCursor(NULL, IDC_WAIT);
951     if (_waitCursor != NULL)
952       _oldCursor = SetCursor(_waitCursor);
953   }
~CWaitCursorCWaitCursor954   ~CWaitCursor()
955   {
956     if (_waitCursor != NULL)
957       SetCursor(_oldCursor);
958   }
959 };
960 
Create(const UString & title,NWindows::CThread & thread,HWND wndParent)961 INT_PTR CProgressDialog::Create(const UString &title, NWindows::CThread &thread, HWND wndParent)
962 {
963   INT_PTR res = 0;
964   try
965   {
966     if (WaitMode)
967     {
968       CWaitCursor waitCursor;
969       HANDLE h[] = { thread, _createDialogEvent };
970 
971       const DWORD res2 = WaitForMultipleObjects(Z7_ARRAY_SIZE(h), h, FALSE, kCreateDelay);
972       if (res2 == WAIT_OBJECT_0 && !Sync.ThereIsMessage())
973         return 0;
974     }
975     _title = title;
976     BIG_DIALOG_SIZE(360, 192);
977     res = CModalDialog::Create(SIZED_DIALOG(IDD_PROGRESS), wndParent);
978   }
979   catch(...)
980   {
981     _wasCreated = true;
982     _dialogCreatedEvent.Set();
983   }
984   thread.Wait_Close();
985   if (!MessagesDisplayed)
986   if (!g_DisableUserQuestions)
987     MessageBoxW(wndParent, L"Progress Error", L"7-Zip", MB_ICONERROR);
988   return res;
989 }
990 
OnExternalCloseMessage()991 bool CProgressDialog::OnExternalCloseMessage()
992 {
993   // it doesn't work if there is MessageBox.
994   // #ifdef __ITaskbarList3_INTERFACE_DEFINED__
995   SetTaskbarProgressState(TBPF_NOPROGRESS);
996   // #endif
997   // AddToTitle(L"Finished ");
998   // SetText(L"Finished2 ");
999 
1000   UpdateStatInfo(true);
1001 
1002   SetItemText(IDCANCEL, LangString(IDS_CLOSE));
1003   ::SendMessage(GetItem(IDCANCEL), BM_SETSTYLE, BS_DEFPUSHBUTTON, MAKELPARAM(TRUE, 0));
1004   HideItem(IDB_PROGRESS_BACKGROUND);
1005   HideItem(IDB_PAUSE);
1006 
1007   ProcessWasFinished_GuiVirt();
1008 
1009   bool thereAreMessages;
1010   CProgressFinalMessage fm;
1011   {
1012     NSynchronization::CCriticalSectionLock lock(Sync._cs);
1013     thereAreMessages = !Sync.Messages.IsEmpty();
1014     fm = Sync.FinalMessage;
1015   }
1016 
1017   if (!fm.ErrorMessage.Message.IsEmpty())
1018   {
1019     MessagesDisplayed = true;
1020     if (fm.ErrorMessage.Title.IsEmpty())
1021       fm.ErrorMessage.Title = "7-Zip";
1022     if (!g_DisableUserQuestions)
1023       MessageBoxW(*this, fm.ErrorMessage.Message, fm.ErrorMessage.Title, MB_ICONERROR);
1024   }
1025   else if (!thereAreMessages)
1026   {
1027     MessagesDisplayed = true;
1028 
1029     if (!fm.OkMessage.Message.IsEmpty())
1030     {
1031       if (fm.OkMessage.Title.IsEmpty())
1032         fm.OkMessage.Title = "7-Zip";
1033       if (!g_DisableUserQuestions)
1034         MessageBoxW(*this, fm.OkMessage.Message, fm.OkMessage.Title, MB_OK);
1035     }
1036   }
1037 
1038   if (!g_DisableUserQuestions)
1039   if (thereAreMessages && !_cancelWasPressed)
1040   {
1041     _waitCloseByCancelButton = true;
1042     UpdateMessagesDialog();
1043     return true;
1044   }
1045 
1046   End(0);
1047   return true;
1048 }
1049 
OnMessage(UINT message,WPARAM wParam,LPARAM lParam)1050 bool CProgressDialog::OnMessage(UINT message, WPARAM wParam, LPARAM lParam)
1051 {
1052   switch (message)
1053   {
1054     case kCloseMessage:
1055     {
1056       if (_timer)
1057       {
1058         /* 21.03 : KillTimer(kTimerID) instead of KillTimer(_timer).
1059            But (_timer == kTimerID) in Win10. So it worked too */
1060         KillTimer(kTimerID);
1061         _timer = 0;
1062       }
1063       if (_inCancelMessageBox)
1064       {
1065         /* if user is in MessageBox(), we will call OnExternalCloseMessage()
1066            later, when MessageBox() will be closed */
1067         _externalCloseMessageWasReceived = true;
1068         break;
1069       }
1070       return OnExternalCloseMessage();
1071     }
1072     /*
1073     case WM_SETTEXT:
1074     {
1075       if (_timer == 0)
1076         return true;
1077       break;
1078     }
1079     */
1080   }
1081   return CModalDialog::OnMessage(message, wParam, lParam);
1082 }
1083 
SetTitleText()1084 void CProgressDialog::SetTitleText()
1085 {
1086   UString s;
1087   if (Sync.Get_Paused())
1088   {
1089     s += _paused_String;
1090     s.Add_Space();
1091   }
1092   if (IS_DEFINED_VAL(_prevPercentValue))
1093   {
1094     s.Add_UInt64(_prevPercentValue);
1095     s.Add_Char('%');
1096   }
1097   if (_background)
1098   {
1099     s.Add_Space();
1100     s += _backgrounded_String;
1101   }
1102 
1103   s.Add_Space();
1104   #ifndef Z7_SFX
1105   {
1106     unsigned len = s.Len();
1107     s += MainAddTitle;
1108     AddToTitle(s);
1109     s.DeleteFrom(len);
1110   }
1111   #endif
1112 
1113   s += _title;
1114   if (!_titleFileName.IsEmpty())
1115   {
1116     UString fileName = _titleFileName;
1117     ReduceString(fileName, kTitleFileNameSizeLimit);
1118     s.Add_Space();
1119     s += fileName;
1120   }
1121   SetText(s);
1122 }
1123 
SetPauseText()1124 void CProgressDialog::SetPauseText()
1125 {
1126   SetItemText(IDB_PAUSE, Sync.Get_Paused() ? _continue_String : _pause_String);
1127   SetTitleText();
1128 }
1129 
OnPauseButton()1130 void CProgressDialog::OnPauseButton()
1131 {
1132   bool paused = !Sync.Get_Paused();
1133   Sync.Set_Paused(paused);
1134   UInt32 curTime = ::GetTickCount();
1135   if (paused)
1136     _elapsedTime += (curTime - _prevTime);
1137   SetTaskbarProgressState();
1138   _prevTime = curTime;
1139   SetPauseText();
1140 }
1141 
SetPriorityText()1142 void CProgressDialog::SetPriorityText()
1143 {
1144   SetItemText(IDB_PROGRESS_BACKGROUND, _background ?
1145       _foreground_String :
1146       _background_String);
1147   SetTitleText();
1148 }
1149 
OnPriorityButton()1150 void CProgressDialog::OnPriorityButton()
1151 {
1152   _background = !_background;
1153   #ifndef UNDER_CE
1154   SetPriorityClass(GetCurrentProcess(), _background ? IDLE_PRIORITY_CLASS : NORMAL_PRIORITY_CLASS);
1155   #endif
1156   SetPriorityText();
1157 }
1158 
AddMessageDirect(LPCWSTR message,bool needNumber)1159 void CProgressDialog::AddMessageDirect(LPCWSTR message, bool needNumber)
1160 {
1161   wchar_t sz[16];
1162   sz[0] = 0;
1163   if (needNumber)
1164     ConvertUInt32ToString(_numMessages + 1, sz);
1165   const unsigned itemIndex = _messageStrings.Size(); // _messageList.GetItemCount();
1166   if (_messageList.InsertItem(itemIndex, sz) == (int)itemIndex)
1167   {
1168     _messageList.SetSubItem(itemIndex, 1, message);
1169     _messageStrings.Add(message);
1170   }
1171 }
1172 
AddMessage(LPCWSTR message)1173 void CProgressDialog::AddMessage(LPCWSTR message)
1174 {
1175   UString s = message;
1176   bool needNumber = true;
1177   while (!s.IsEmpty())
1178   {
1179     const int pos = s.Find(L'\n');
1180     if (pos < 0)
1181       break;
1182     AddMessageDirect(s.Left((unsigned)pos), needNumber);
1183     needNumber = false;
1184     s.DeleteFrontal((unsigned)pos + 1);
1185   }
1186   AddMessageDirect(s, needNumber);
1187   _numMessages++;
1188 }
1189 
GetNumDigits(unsigned val)1190 static unsigned GetNumDigits(unsigned val)
1191 {
1192   unsigned i = 0;
1193   for (;;)
1194   {
1195     i++;
1196     val /= 10;
1197     if (val == 0)
1198       return i;
1199   }
1200 }
1201 
UpdateMessagesDialog()1202 void CProgressDialog::UpdateMessagesDialog()
1203 {
1204   UStringVector messages;
1205   {
1206     NSynchronization::CCriticalSectionLock lock(Sync._cs);
1207     const unsigned num = Sync.Messages.Size();
1208     if (num > _numPostedMessages)
1209     {
1210       messages.ClearAndReserve(num - _numPostedMessages);
1211       for (unsigned i = _numPostedMessages; i < num; i++)
1212         messages.AddInReserved(Sync.Messages[i]);
1213       _numPostedMessages = num;
1214     }
1215   }
1216   if (!messages.IsEmpty())
1217   {
1218     FOR_VECTOR (i, messages)
1219       AddMessage(messages[i]);
1220     // SetColumnWidthAuto() can be slow for big number of files.
1221     if (_numPostedMessages < 1000000 || _numAutoSizeMessages < 100)
1222     if (_numAutoSizeMessages < 100 ||
1223         GetNumDigits(_numPostedMessages) >
1224         GetNumDigits(_numAutoSizeMessages))
1225     {
1226       _messageList.SetColumnWidthAuto(0);
1227       _messageList.SetColumnWidthAuto(1);
1228       _numAutoSizeMessages = _numPostedMessages;
1229     }
1230   }
1231 }
1232 
1233 
OnButtonClicked(unsigned buttonID,HWND buttonHWND)1234 bool CProgressDialog::OnButtonClicked(unsigned buttonID, HWND buttonHWND)
1235 {
1236   switch (buttonID)
1237   {
1238     // case IDOK: // if IDCANCEL is not DEFPUSHBUTTON
1239     case IDCANCEL:
1240     {
1241       if (_waitCloseByCancelButton)
1242       {
1243         MessagesDisplayed = true;
1244         End(IDCLOSE);
1245         break;
1246       }
1247 
1248       if (_cancelWasPressed)
1249         return true;
1250 
1251       const bool paused = Sync.Get_Paused();
1252 
1253       if (!paused)
1254       {
1255         OnPauseButton();
1256       }
1257 
1258       _inCancelMessageBox = true;
1259       const int res = ::MessageBoxW(*this, LangString(IDS_PROGRESS_ASK_CANCEL), _title, MB_YESNOCANCEL);
1260       _inCancelMessageBox = false;
1261       if (res == IDYES)
1262         _cancelWasPressed = true;
1263 
1264       if (!paused)
1265       {
1266         OnPauseButton();
1267       }
1268 
1269       if (_externalCloseMessageWasReceived)
1270       {
1271         /* we have received kCloseMessage while we were in MessageBoxW().
1272            so we call OnExternalCloseMessage() here.
1273            it can show MessageBox and it can close dialog */
1274         OnExternalCloseMessage();
1275         return true;
1276       }
1277 
1278       if (!_cancelWasPressed)
1279         return true;
1280 
1281       MessagesDisplayed = true;
1282       // we will call Sync.Set_Stopped(true) in OnButtonClicked() : OnCancel()
1283       break;
1284     }
1285 
1286     case IDB_PAUSE:
1287       OnPauseButton();
1288       return true;
1289     case IDB_PROGRESS_BACKGROUND:
1290       OnPriorityButton();
1291       return true;
1292   }
1293   return CModalDialog::OnButtonClicked(buttonID, buttonHWND);
1294 }
1295 
CheckNeedClose()1296 void CProgressDialog::CheckNeedClose()
1297 {
1298   if (_needClose)
1299   {
1300     PostMsg(kCloseMessage);
1301     _needClose = false;
1302   }
1303 }
1304 
ProcessWasFinished()1305 void CProgressDialog::ProcessWasFinished()
1306 {
1307   // Set Window title here.
1308   if (!WaitMode)
1309     WaitCreating();
1310 
1311   if (_wasCreated)
1312     PostMsg(kCloseMessage);
1313   else
1314     _needClose = true;
1315 }
1316 
1317 
OnNotify(UINT,LPNMHDR header)1318 bool CProgressDialog::OnNotify(UINT /* controlID */, LPNMHDR header)
1319 {
1320   if (header->hwndFrom != _messageList)
1321     return false;
1322   switch (header->code)
1323   {
1324     case LVN_KEYDOWN:
1325     {
1326       LPNMLVKEYDOWN keyDownInfo = LPNMLVKEYDOWN(header);
1327       switch (keyDownInfo->wVKey)
1328       {
1329         case 'A':
1330         {
1331           if (IsKeyDown(VK_CONTROL))
1332           {
1333             _messageList.SelectAll();
1334             return true;
1335           }
1336           break;
1337         }
1338         case VK_INSERT:
1339         case 'C':
1340         {
1341           if (IsKeyDown(VK_CONTROL))
1342           {
1343             CopyToClipboard();
1344             return true;
1345           }
1346           break;
1347         }
1348       }
1349     }
1350   }
1351   return false;
1352 }
1353 
1354 
ListView_GetSelected(NControl::CListView & listView,CUIntVector & vector)1355 static void ListView_GetSelected(NControl::CListView &listView, CUIntVector &vector)
1356 {
1357   vector.Clear();
1358   int index = -1;
1359   for (;;)
1360   {
1361     index = listView.GetNextSelectedItem(index);
1362     if (index < 0)
1363       break;
1364     vector.Add((unsigned)index);
1365   }
1366 }
1367 
1368 
CopyToClipboard()1369 void CProgressDialog::CopyToClipboard()
1370 {
1371   CUIntVector indexes;
1372   ListView_GetSelected(_messageList, indexes);
1373   UString s;
1374   unsigned numIndexes = indexes.Size();
1375   if (numIndexes == 0)
1376     numIndexes = (unsigned)_messageList.GetItemCount();
1377 
1378   for (unsigned i = 0; i < numIndexes; i++)
1379   {
1380     const unsigned index = (i < indexes.Size() ? indexes[i] : i);
1381     // s.Add_UInt32(index);
1382     // s += ": ";
1383     s += _messageStrings[index];
1384     {
1385       s +=
1386         #ifdef _WIN32
1387           "\r\n"
1388         #else
1389           "\n"
1390         #endif
1391         ;
1392     }
1393   }
1394 
1395   ClipboardSetText(*this, s);
1396 }
1397 
1398 
MyThreadFunction(void * param)1399 static THREAD_FUNC_DECL MyThreadFunction(void *param)
1400 {
1401   CProgressThreadVirt *p = (CProgressThreadVirt *)param;
1402   try
1403   {
1404     p->Process();
1405     p->ThreadFinishedOK = true;
1406   }
1407   catch (...) { p->Result = E_FAIL; }
1408   return 0;
1409 }
1410 
1411 
Create(const UString & title,HWND parentWindow)1412 HRESULT CProgressThreadVirt::Create(const UString &title, HWND parentWindow)
1413 {
1414   NWindows::CThread thread;
1415   const WRes wres = thread.Create(MyThreadFunction, this);
1416   if (wres != 0)
1417     return HRESULT_FROM_WIN32(wres);
1418   CProgressDialog::Create(title, thread, parentWindow);
1419   return S_OK;
1420 }
1421 
AddMessageToString(UString & dest,const UString & src)1422 static void AddMessageToString(UString &dest, const UString &src)
1423 {
1424   if (!src.IsEmpty())
1425   {
1426     if (!dest.IsEmpty())
1427       dest.Add_LF();
1428     dest += src;
1429   }
1430 }
1431 
Process()1432 void CProgressThreadVirt::Process()
1433 {
1434   CProgressCloser closer(*this);
1435   UString m;
1436   try { Result = ProcessVirt(); }
1437   catch(const wchar_t *s) { m = s; }
1438   catch(const UString &s) { m = s; }
1439   catch(const char *s) { m = GetUnicodeString(s); }
1440   catch(int v)
1441   {
1442     m = "Error #";
1443     m.Add_UInt32((unsigned)v);
1444   }
1445   catch(...) { m = "Error"; }
1446   if (Result != E_ABORT)
1447   {
1448     if (m.IsEmpty() && Result != S_OK)
1449       m = HResultToMessage(Result);
1450   }
1451   AddMessageToString(m, FinalMessage.ErrorMessage.Message);
1452 
1453   {
1454     FOR_VECTOR(i, ErrorPaths)
1455     {
1456       if (i >= 32)
1457         break;
1458       AddMessageToString(m, fs2us(ErrorPaths[i]));
1459     }
1460   }
1461 
1462   CProgressSync &sync = Sync;
1463   NSynchronization::CCriticalSectionLock lock(sync._cs);
1464   if (m.IsEmpty())
1465   {
1466     if (!FinalMessage.OkMessage.Message.IsEmpty())
1467       sync.FinalMessage.OkMessage = FinalMessage.OkMessage;
1468   }
1469   else
1470   {
1471     sync.FinalMessage.ErrorMessage.Message = m;
1472     if (Result == S_OK)
1473       Result = E_FAIL;
1474   }
1475 }
1476 
HResultToMessage(HRESULT errorCode)1477 UString HResultToMessage(HRESULT errorCode)
1478 {
1479   if (errorCode == E_OUTOFMEMORY)
1480     return LangString(IDS_MEM_ERROR);
1481   else
1482     return NError::MyFormatMessage(errorCode);
1483 }
1484