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