1 // ProgressDialog2.cpp
2 
3 #include "StdAfx.h"
4 
5 #include "../../../Common/IntToString.h"
6 #include "../../../Common/StringConvert.h"
7 
8 #include "../../../Windows/Control/Static.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(0)
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 != 0)
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   _filesStr_Prev.Empty();
352 
353   _foreground = true;
354 
355   m_ProgressBar.Attach(GetItem(IDC_PROGRESS1));
356   _messageList.Attach(GetItem(IDL_PROGRESS_MESSAGES));
357   _messageList.SetUnicodeFormat();
358 
359   _wasCreated = true;
360   _dialogCreatedEvent.Set();
361 
362   #ifdef LANG
363   LangSetDlgItems(*this, kLangIDs, ARRAY_SIZE(kLangIDs));
364   LangSetDlgItems_Colon(*this, kLangIDs_Colon, ARRAY_SIZE(kLangIDs_Colon));
365   #endif
366 
367   CWindow window(GetItem(IDB_PROGRESS_BACKGROUND));
368   window.GetText(_background_String);
369   _backgrounded_String = _background_String;
370   _backgrounded_String.RemoveChar(L'&');
371 
372   window = GetItem(IDB_PAUSE);
373   window.GetText(_pause_String);
374 
375   LangString(IDS_PROGRESS_FOREGROUND, _foreground_String);
376   LangString(IDS_CONTINUE, _continue_String);
377   LangString(IDS_PROGRESS_PAUSED, _paused_String);
378 
379   SetText(_title);
380   SetPauseText();
381   SetPriorityText();
382 
383   _messageList.InsertColumn(0, L"", 30);
384   _messageList.InsertColumn(1, L"", 600);
385 
386   _messageList.SetColumnWidthAuto(0);
387   _messageList.SetColumnWidthAuto(1);
388 
389   EnableErrorsControls(false);
390 
391   GetItemSizes(IDCANCEL, _buttonSizeX, _buttonSizeY);
392   _numReduceSymbols = kCurrentFileNameSizeLimit;
393   NormalizeSize(true);
394 
395   if (!ShowCompressionInfo)
396   {
397     HideItem(IDT_PROGRESS_PACKED);
398     HideItem(IDT_PROGRESS_PACKED_VAL);
399     HideItem(IDT_PROGRESS_RATIO);
400     HideItem(IDT_PROGRESS_RATIO_VAL);
401   }
402 
403   if (IconID >= 0)
404   {
405     HICON icon = LoadIcon(g_hInstance, MAKEINTRESOURCE(IconID));
406     // SetIcon(ICON_SMALL, icon);
407     SetIcon(ICON_BIG, icon);
408   }
409   _timer = SetTimer(kTimerID, kTimerElapse);
410   #ifdef UNDER_CE
411   Foreground();
412   #endif
413 
414   CheckNeedClose();
415 
416   SetTaskbarProgressState();
417 
418   return CModalDialog::OnInit();
419 }
420 
421 static const UINT kIDs[] =
422 {
423   IDT_PROGRESS_ELAPSED,   IDT_PROGRESS_ELAPSED_VAL,
424   IDT_PROGRESS_REMAINING, IDT_PROGRESS_REMAINING_VAL,
425   IDT_PROGRESS_FILES,     IDT_PROGRESS_FILES_VAL,
426   IDT_PROGRESS_RATIO,     IDT_PROGRESS_RATIO_VAL,
427   IDT_PROGRESS_ERRORS,    IDT_PROGRESS_ERRORS_VAL,
428 
429   IDT_PROGRESS_TOTAL,     IDT_PROGRESS_TOTAL_VAL,
430   IDT_PROGRESS_SPEED,     IDT_PROGRESS_SPEED_VAL,
431   IDT_PROGRESS_PROCESSED, IDT_PROGRESS_PROCESSED_VAL,
432   IDT_PROGRESS_PACKED,    IDT_PROGRESS_PACKED_VAL
433 };
434 
OnSize(WPARAM,int xSize,int ySize)435 bool CProgressDialog::OnSize(WPARAM /* wParam */, int xSize, int ySize)
436 {
437   int sY;
438   int sStep;
439   int mx, my;
440   {
441     RECT r;
442     GetClientRectOfItem(IDT_PROGRESS_ELAPSED, r);
443     mx = r.left;
444     my = r.top;
445     sY = RECT_SIZE_Y(r);
446     GetClientRectOfItem(IDT_PROGRESS_REMAINING, r);
447     sStep = r.top - my;
448   }
449 
450   InvalidateRect(NULL);
451 
452   int xSizeClient = xSize - mx * 2;
453 
454   {
455     int i;
456     for (i = 800; i > 40; i = i * 9 / 10)
457       if (Units_To_Pixels_X(i) <= xSizeClient)
458         break;
459     _numReduceSymbols = i / 4;
460   }
461 
462   int yPos = ySize - my - _buttonSizeY;
463 
464   ChangeSubWindowSizeX(GetItem(IDT_PROGRESS_STATUS), xSize - mx * 2);
465   ChangeSubWindowSizeX(GetItem(IDT_PROGRESS_FILE_NAME), xSize - mx * 2);
466   ChangeSubWindowSizeX(GetItem(IDC_PROGRESS1), xSize - mx * 2);
467 
468   int bSizeX = _buttonSizeX;
469   int mx2 = mx;
470   for (;; mx2--)
471   {
472     int bSize2 = bSizeX * 3 + mx2 * 2;
473     if (bSize2 <= xSizeClient)
474       break;
475     if (mx2 < 5)
476     {
477       bSizeX = (xSizeClient - mx2 * 2) / 3;
478       break;
479     }
480   }
481   if (bSizeX < 2)
482     bSizeX = 2;
483 
484   {
485     RECT r;
486     GetClientRectOfItem(IDL_PROGRESS_MESSAGES, r);
487     int y = r.top;
488     int ySize2 = yPos - my - y;
489     const int kMinYSize = _buttonSizeY + _buttonSizeY * 3 / 4;
490     int xx = xSize - mx * 2;
491     if (ySize2 < kMinYSize)
492     {
493       ySize2 = kMinYSize;
494       if (xx > bSizeX * 2)
495         xx -= bSizeX;
496     }
497 
498     _messageList.Move(mx, y, xx, ySize2);
499   }
500 
501   {
502     int xPos = xSize - mx;
503     xPos -= bSizeX;
504     MoveItem(IDCANCEL, xPos, yPos, bSizeX, _buttonSizeY);
505     xPos -= (mx2 + bSizeX);
506     MoveItem(IDB_PAUSE, xPos, yPos, bSizeX, _buttonSizeY);
507     xPos -= (mx2 + bSizeX);
508     MoveItem(IDB_PROGRESS_BACKGROUND, xPos, yPos, bSizeX, _buttonSizeY);
509   }
510 
511   int valueSize;
512   int labelSize;
513   int padSize;
514 
515   labelSize = Units_To_Pixels_X(MY_PROGRESS_LABEL_UNITS_MIN);
516   valueSize = Units_To_Pixels_X(MY_PROGRESS_VAL_UNITS);
517   padSize = Units_To_Pixels_X(MY_PROGRESS_PAD_UNITS);
518   int requiredSize = (labelSize + valueSize) * 2 + padSize;
519 
520   int gSize;
521   {
522     if (requiredSize < xSizeClient)
523     {
524       int incr = (xSizeClient - requiredSize) / 3;
525       labelSize += incr;
526     }
527     else
528       labelSize = (xSizeClient - valueSize * 2 - padSize) / 2;
529     if (labelSize < 0)
530       labelSize = 0;
531 
532     gSize = labelSize + valueSize;
533     padSize = xSizeClient - gSize * 2;
534   }
535 
536   labelSize = gSize - valueSize;
537 
538   yPos = my;
539   for (int i = 0; i < ARRAY_SIZE(kIDs); i += 2)
540   {
541     int x = mx;
542     const int kNumColumn1Items = 5 * 2;
543     if (i >= kNumColumn1Items)
544     {
545       if (i == kNumColumn1Items)
546         yPos = my;
547       x = mx + gSize + padSize;
548     }
549     MoveItem(kIDs[i], x, yPos, labelSize, sY);
550     MoveItem(kIDs[i + 1], x + labelSize, yPos, valueSize, sY);
551     yPos += sStep;
552   }
553   return false;
554 }
555 
OnCancel()556 void CProgressDialog::OnCancel() { Sync.Set_Stopped(true); }
OnOK()557 void CProgressDialog::OnOK() { }
558 
SetProgressRange(UInt64 range)559 void CProgressDialog::SetProgressRange(UInt64 range)
560 {
561   if (range == _progressBar_Range)
562     return;
563   _progressBar_Range = range;
564   INIT_AS_UNDEFINED(_progressBar_Pos);
565   _progressConv.Init(range);
566   m_ProgressBar.SetRange32(0, _progressConv.Count(range));
567 }
568 
SetProgressPos(UInt64 pos)569 void CProgressDialog::SetProgressPos(UInt64 pos)
570 {
571   if (pos >= _progressBar_Range ||
572       pos <= _progressBar_Pos ||
573       pos - _progressBar_Pos >= (_progressBar_Range >> 10))
574   {
575     m_ProgressBar.SetPos(_progressConv.Count(pos));
576     #ifdef __ITaskbarList3_INTERFACE_DEFINED__
577     if (_taskbarList && _hwndForTaskbar)
578       _taskbarList->SetProgressValue(_hwndForTaskbar, pos, _progressBar_Range);
579     #endif
580     _progressBar_Pos = pos;
581   }
582 }
583 
584 #define UINT_TO_STR_2(val) { s[0] = (wchar_t)('0' + (val) / 10); s[1] = (wchar_t)('0' + (val) % 10); s += 2; }
585 
GetTimeString(UInt64 timeValue,wchar_t * s)586 void GetTimeString(UInt64 timeValue, wchar_t *s)
587 {
588   UInt64 hours = timeValue / 3600;
589   UInt32 seconds = (UInt32)(timeValue - hours * 3600);
590   UInt32 minutes = seconds / 60;
591   seconds %= 60;
592   if (hours > 99)
593   {
594     ConvertUInt64ToString(hours, s);
595     for (; *s != 0; s++);
596   }
597   else
598   {
599     UInt32 hours32 = (UInt32)hours;
600     UINT_TO_STR_2(hours32);
601   }
602   *s++ = ':'; UINT_TO_STR_2(minutes);
603   *s++ = ':'; UINT_TO_STR_2(seconds);
604   *s = 0;
605 }
606 
ConvertSizeToString(UInt64 v,wchar_t * s)607 static void ConvertSizeToString(UInt64 v, wchar_t *s)
608 {
609   Byte c = 0;
610        if (v >= ((UInt64)100000 << 20)) { v >>= 30; c = 'G'; }
611   else if (v >= ((UInt64)100000 << 10)) { v >>= 20; c = 'M'; }
612   else if (v >= ((UInt64)100000 <<  0)) { v >>= 10; c = 'K'; }
613   ConvertUInt64ToString(v, s);
614   if (c != 0)
615   {
616     s += MyStringLen(s);
617     *s++ = ' ';
618     *s++ = c;
619     *s++ = 0;
620   }
621 }
622 
ShowSize(int id,UInt64 val,UInt64 & prev)623 void CProgressDialog::ShowSize(int id, UInt64 val, UInt64 &prev)
624 {
625   if (val == prev)
626     return;
627   prev = val;
628   wchar_t s[40];
629   s[0] = 0;
630   if (IS_DEFINED_VAL(val))
631     ConvertSizeToString(val, s);
632   SetItemText(id, s);
633 }
634 
GetChangedString(const UString & newStr,UString & prevStr,bool & hasChanged)635 static void GetChangedString(const UString &newStr, UString &prevStr, bool &hasChanged)
636 {
637   hasChanged = !(prevStr == newStr);
638   if (hasChanged)
639     prevStr = newStr;
640 }
641 
GetPower32(UInt32 val)642 static unsigned GetPower32(UInt32 val)
643 {
644   const unsigned kStart = 32;
645   UInt32 mask = ((UInt32)1 << (kStart - 1));
646   for (unsigned i = kStart;; i--)
647   {
648     if (i == 0 || (val & mask) != 0)
649       return i;
650     mask >>= 1;
651   }
652 }
653 
GetPower64(UInt64 val)654 static unsigned GetPower64(UInt64 val)
655 {
656   UInt32 high = (UInt32)(val >> 32);
657   if (high == 0)
658     return GetPower32((UInt32)val);
659   return GetPower32(high) + 32;
660 }
661 
MyMultAndDiv(UInt64 mult1,UInt64 mult2,UInt64 divider)662 static UInt64 MyMultAndDiv(UInt64 mult1, UInt64 mult2, UInt64 divider)
663 {
664   unsigned pow1 = GetPower64(mult1);
665   unsigned pow2 = GetPower64(mult2);
666   while (pow1 + pow2 > 64)
667   {
668     if (pow1 > pow2) { pow1--; mult1 >>= 1; }
669     else             { pow2--; mult2 >>= 1; }
670     divider >>= 1;
671   }
672   UInt64 res = mult1 * mult2;
673   if (divider != 0)
674     res /= divider;
675   return res;
676 }
677 
UpdateStatInfo(bool showAll)678 void CProgressDialog::UpdateStatInfo(bool showAll)
679 {
680   UInt64 total, completed, totalFiles, completedFiles, inSize, outSize;
681   bool bytesProgressMode;
682 
683   bool titleFileName_Changed;
684   bool curFilePath_Changed;
685   bool status_Changed;
686   unsigned numErrors;
687   {
688     NSynchronization::CCriticalSectionLock lock(Sync._cs);
689     total = Sync._totalBytes;
690     completed = Sync._completedBytes;
691     totalFiles = Sync._totalFiles;
692     completedFiles = Sync._curFiles;
693     inSize = Sync._inSize;
694     outSize = Sync._outSize;
695     bytesProgressMode = Sync._bytesProgressMode;
696 
697     GetChangedString(Sync._titleFileName, _titleFileName, titleFileName_Changed);
698     GetChangedString(Sync._filePath, _filePath, curFilePath_Changed);
699     GetChangedString(Sync._status, _status, status_Changed);
700     if (_isDir != Sync._isDir)
701     {
702       curFilePath_Changed = true;
703       _isDir = Sync._isDir;
704     }
705     numErrors = Sync.Messages.Size();
706   }
707 
708   UInt32 curTime = ::GetTickCount();
709 
710   const UInt64 progressTotal = bytesProgressMode ? total : totalFiles;
711   const UInt64 progressCompleted = bytesProgressMode ? completed : completedFiles;
712   {
713     if (IS_UNDEFINED_VAL(progressTotal))
714     {
715       // SetPos(0);
716       // SetRange(progressCompleted);
717     }
718     else
719     {
720       if (_progressBar_Pos != 0 || progressCompleted != 0 ||
721           (_progressBar_Range == 0 && progressTotal != 0))
722       {
723         SetProgressRange(progressTotal);
724         SetProgressPos(progressCompleted);
725       }
726     }
727   }
728 
729   ShowSize(IDT_PROGRESS_TOTAL_VAL, total, _totalBytes_Prev);
730 
731   _elapsedTime += (curTime - _prevTime);
732   _prevTime = curTime;
733   UInt64 elapsedSec = _elapsedTime / 1000;
734   bool elapsedChanged = false;
735   if (elapsedSec != _prevElapsedSec)
736   {
737     _prevElapsedSec = elapsedSec;
738     elapsedChanged = true;
739     wchar_t s[40];
740     GetTimeString(elapsedSec, s);
741     SetItemText(IDT_PROGRESS_ELAPSED_VAL, s);
742   }
743 
744   bool needSetTitle = false;
745   if (elapsedChanged || showAll)
746   {
747     if (numErrors > _numPostedMessages)
748     {
749       UpdateMessagesDialog();
750       wchar_t s[32];
751       ConvertUInt64ToString(numErrors, s);
752       SetItemText(IDT_PROGRESS_ERRORS_VAL, s);
753       if (!_errorsWereDisplayed)
754       {
755         _errorsWereDisplayed = true;
756         EnableErrorsControls(true);
757         SetTaskbarProgressState();
758       }
759     }
760 
761     if (progressCompleted != 0)
762     {
763       if (IS_UNDEFINED_VAL(progressTotal))
764       {
765         if (IS_DEFINED_VAL(_prevRemainingSec))
766         {
767           INIT_AS_UNDEFINED(_prevRemainingSec);
768           SetItemText(IDT_PROGRESS_REMAINING_VAL, L"");
769         }
770       }
771       else
772       {
773         UInt64 remainingTime = 0;
774         if (progressCompleted < progressTotal)
775           remainingTime = MyMultAndDiv(_elapsedTime, progressTotal - progressCompleted, progressCompleted);
776         UInt64 remainingSec = remainingTime / 1000;
777         if (remainingSec != _prevRemainingSec)
778         {
779           _prevRemainingSec = remainingSec;
780           wchar_t s[40];
781           GetTimeString(remainingSec, s);
782           SetItemText(IDT_PROGRESS_REMAINING_VAL, s);
783         }
784       }
785       {
786         UInt64 elapsedTime = (_elapsedTime == 0) ? 1 : _elapsedTime;
787         UInt64 v = (progressCompleted * 1000) / elapsedTime;
788         Byte c = 0;
789         unsigned moveBits = 0;
790              if (v >= ((UInt64)10000 << 10)) { moveBits = 20; c = 'M'; }
791         else if (v >= ((UInt64)10000 <<  0)) { moveBits = 10; c = 'K'; }
792         v >>= moveBits;
793         if (moveBits != _prevSpeed_MoveBits || v != _prevSpeed)
794         {
795           _prevSpeed_MoveBits = moveBits;
796           _prevSpeed = v;
797           wchar_t s[40];
798           ConvertUInt64ToString(v, s);
799           unsigned pos = MyStringLen(s);
800           s[pos++] = ' ';
801           if (moveBits != 0)
802             s[pos++] = c;
803           s[pos++] = 'B';
804           s[pos++] = '/';
805           s[pos++] = 's';
806           s[pos++] = 0;
807           SetItemText(IDT_PROGRESS_SPEED_VAL, s);
808         }
809       }
810     }
811 
812     {
813       UInt64 percent = 0;
814       {
815         if (IS_DEFINED_VAL(progressTotal))
816         {
817           percent = progressCompleted * 100;
818           if (progressTotal != 0)
819             percent /= progressTotal;
820         }
821       }
822       if (percent != _prevPercentValue)
823       {
824         _prevPercentValue = percent;
825         needSetTitle = true;
826       }
827     }
828 
829     {
830       wchar_t s[64];
831       ConvertUInt64ToString(completedFiles, s);
832       if (IS_DEFINED_VAL(totalFiles))
833       {
834         MyStringCat(s, L" / ");
835         ConvertUInt64ToString(totalFiles, s + MyStringLen(s));
836       }
837       if (_filesStr_Prev != s)
838       {
839         _filesStr_Prev = s;
840         SetItemText(IDT_PROGRESS_FILES_VAL, s);
841       }
842     }
843 
844     const UInt64 packSize   = CompressingMode ? outSize : inSize;
845     const UInt64 unpackSize = CompressingMode ? inSize : outSize;
846 
847     if (IS_UNDEFINED_VAL(unpackSize) &&
848         IS_UNDEFINED_VAL(packSize))
849     {
850       ShowSize(IDT_PROGRESS_PROCESSED_VAL, completed, _processed_Prev);
851       ShowSize(IDT_PROGRESS_PACKED_VAL, UNDEFINED_VAL, _packed_Prev);
852     }
853     else
854     {
855       ShowSize(IDT_PROGRESS_PROCESSED_VAL, unpackSize, _processed_Prev);
856       ShowSize(IDT_PROGRESS_PACKED_VAL, packSize, _packed_Prev);
857 
858       if (IS_DEFINED_VAL(packSize) &&
859           IS_DEFINED_VAL(unpackSize) &&
860           unpackSize != 0)
861       {
862         wchar_t s[32];
863         UInt64 ratio = packSize * 100 / unpackSize;
864         if (_ratio_Prev != ratio)
865         {
866           _ratio_Prev = ratio;
867           ConvertUInt64ToString(ratio, s);
868           MyStringCat(s, L"%");
869           SetItemText(IDT_PROGRESS_RATIO_VAL, s);
870         }
871       }
872     }
873   }
874 
875   if (needSetTitle || titleFileName_Changed)
876     SetTitleText();
877 
878   if (status_Changed)
879   {
880     UString s = _status;
881     ReduceString(s, _numReduceSymbols);
882     SetItemText(IDT_PROGRESS_STATUS, _status);
883   }
884 
885   if (curFilePath_Changed)
886   {
887     UString s1, s2;
888     if (_isDir)
889       s1 = _filePath;
890     else
891     {
892       int slashPos = _filePath.ReverseFind_PathSepar();
893       if (slashPos >= 0)
894       {
895         s1.SetFrom(_filePath, slashPos + 1);
896         s2 = _filePath.Ptr(slashPos + 1);
897       }
898       else
899         s2 = _filePath;
900     }
901     ReduceString(s1, _numReduceSymbols);
902     ReduceString(s2, _numReduceSymbols);
903     s1.Add_LF();
904     s1 += s2;
905     SetItemText(IDT_PROGRESS_FILE_NAME, s1);
906   }
907 }
908 
OnTimer(WPARAM,LPARAM)909 bool CProgressDialog::OnTimer(WPARAM /* timerID */, LPARAM /* callback */)
910 {
911   if (Sync.Get_Paused())
912     return true;
913   CheckNeedClose();
914   UpdateStatInfo(false);
915   return true;
916 }
917 
918 struct CWaitCursor
919 {
920   HCURSOR _waitCursor;
921   HCURSOR _oldCursor;
CWaitCursorCWaitCursor922   CWaitCursor()
923   {
924     _waitCursor = LoadCursor(NULL, IDC_WAIT);
925     if (_waitCursor != NULL)
926       _oldCursor = SetCursor(_waitCursor);
927   }
~CWaitCursorCWaitCursor928   ~CWaitCursor()
929   {
930     if (_waitCursor != NULL)
931       SetCursor(_oldCursor);
932   }
933 };
934 
Create(const UString & title,NWindows::CThread & thread,HWND wndParent)935 INT_PTR CProgressDialog::Create(const UString &title, NWindows::CThread &thread, HWND wndParent)
936 {
937   INT_PTR res = 0;
938   try
939   {
940     if (WaitMode)
941     {
942       CWaitCursor waitCursor;
943       HANDLE h[] = { thread, _createDialogEvent };
944 
945       WRes res2 = WaitForMultipleObjects(ARRAY_SIZE(h), h, FALSE, kCreateDelay);
946       if (res2 == WAIT_OBJECT_0 && !Sync.ThereIsMessage())
947         return 0;
948     }
949     _title = title;
950     BIG_DIALOG_SIZE(360, 192);
951     res = CModalDialog::Create(SIZED_DIALOG(IDD_PROGRESS), wndParent);
952   }
953   catch(...)
954   {
955     _wasCreated = true;
956     _dialogCreatedEvent.Set();
957     res = res;
958   }
959   thread.Wait();
960   if (!MessagesDisplayed)
961     MessageBoxW(wndParent, L"Progress Error", L"7-Zip", MB_ICONERROR);
962   return res;
963 }
964 
OnExternalCloseMessage()965 bool CProgressDialog::OnExternalCloseMessage()
966 {
967   // it doesn't work if there is MessageBox.
968   #ifdef __ITaskbarList3_INTERFACE_DEFINED__
969   SetTaskbarProgressState(TBPF_NOPROGRESS);
970   #endif
971   // AddToTitle(L"Finished ");
972   // SetText(L"Finished2 ");
973 
974   UpdateStatInfo(true);
975 
976   SetItemText(IDCANCEL, LangString(IDS_CLOSE));
977   ::SendMessage(GetItem(IDCANCEL), BM_SETSTYLE, BS_DEFPUSHBUTTON, MAKELPARAM(TRUE, 0));
978   HideItem(IDB_PROGRESS_BACKGROUND);
979   HideItem(IDB_PAUSE);
980 
981   ProcessWasFinished_GuiVirt();
982 
983   bool thereAreMessages;
984   CProgressFinalMessage fm;
985   {
986     NSynchronization::CCriticalSectionLock lock(Sync._cs);
987     thereAreMessages = !Sync.Messages.IsEmpty();
988     fm = Sync.FinalMessage;
989   }
990 
991   if (!fm.ErrorMessage.Message.IsEmpty())
992   {
993     MessagesDisplayed = true;
994     if (fm.ErrorMessage.Title.IsEmpty())
995       fm.ErrorMessage.Title = "7-Zip";
996     MessageBoxW(*this, fm.ErrorMessage.Message, fm.ErrorMessage.Title, MB_ICONERROR);
997   }
998   else if (!thereAreMessages)
999   {
1000     MessagesDisplayed = true;
1001 
1002     if (!fm.OkMessage.Message.IsEmpty())
1003     {
1004       if (fm.OkMessage.Title.IsEmpty())
1005         fm.OkMessage.Title = "7-Zip";
1006       MessageBoxW(*this, fm.OkMessage.Message, fm.OkMessage.Title, MB_OK);
1007     }
1008   }
1009 
1010   if (thereAreMessages && !_cancelWasPressed)
1011   {
1012     _waitCloseByCancelButton = true;
1013     UpdateMessagesDialog();
1014     return true;
1015   }
1016 
1017   End(0);
1018   return true;
1019 }
1020 
OnMessage(UINT message,WPARAM wParam,LPARAM lParam)1021 bool CProgressDialog::OnMessage(UINT message, WPARAM wParam, LPARAM lParam)
1022 {
1023   switch (message)
1024   {
1025     case kCloseMessage:
1026     {
1027       KillTimer(_timer);
1028       _timer = 0;
1029       if (_inCancelMessageBox)
1030       {
1031         _externalCloseMessageWasReceived = true;
1032         break;
1033       }
1034       return OnExternalCloseMessage();
1035     }
1036     /*
1037     case WM_SETTEXT:
1038     {
1039       if (_timer == 0)
1040         return true;
1041       break;
1042     }
1043     */
1044   }
1045   return CModalDialog::OnMessage(message, wParam, lParam);
1046 }
1047 
SetTitleText()1048 void CProgressDialog::SetTitleText()
1049 {
1050   UString s;
1051   if (Sync.Get_Paused())
1052   {
1053     s += _paused_String;
1054     s.Add_Space();
1055   }
1056   if (IS_DEFINED_VAL(_prevPercentValue))
1057   {
1058     char temp[32];
1059     ConvertUInt64ToString(_prevPercentValue, temp);
1060     s += temp;
1061     s += '%';
1062   }
1063   if (!_foreground)
1064   {
1065     s.Add_Space();
1066     s += _backgrounded_String;
1067   }
1068 
1069   s.Add_Space();
1070   #ifndef _SFX
1071   {
1072     unsigned len = s.Len();
1073     s += MainAddTitle;
1074     AddToTitle(s);
1075     s.DeleteFrom(len);
1076   }
1077   #endif
1078 
1079   s += _title;
1080   if (!_titleFileName.IsEmpty())
1081   {
1082     UString fileName = _titleFileName;
1083     ReduceString(fileName, kTitleFileNameSizeLimit);
1084     s.Add_Space();
1085     s += fileName;
1086   }
1087   SetText(s);
1088 }
1089 
SetPauseText()1090 void CProgressDialog::SetPauseText()
1091 {
1092   SetItemText(IDB_PAUSE, Sync.Get_Paused() ? _continue_String : _pause_String);
1093   SetTitleText();
1094 }
1095 
OnPauseButton()1096 void CProgressDialog::OnPauseButton()
1097 {
1098   bool paused = !Sync.Get_Paused();
1099   Sync.Set_Paused(paused);
1100   UInt32 curTime = ::GetTickCount();
1101   if (paused)
1102     _elapsedTime += (curTime - _prevTime);
1103   SetTaskbarProgressState();
1104   _prevTime = curTime;
1105   SetPauseText();
1106 }
1107 
SetPriorityText()1108 void CProgressDialog::SetPriorityText()
1109 {
1110   SetItemText(IDB_PROGRESS_BACKGROUND, _foreground ?
1111       _background_String :
1112       _foreground_String);
1113   SetTitleText();
1114 }
1115 
OnPriorityButton()1116 void CProgressDialog::OnPriorityButton()
1117 {
1118   _foreground = !_foreground;
1119   #ifndef UNDER_CE
1120   SetPriorityClass(GetCurrentProcess(), _foreground ? NORMAL_PRIORITY_CLASS: IDLE_PRIORITY_CLASS);
1121   #endif
1122   SetPriorityText();
1123 }
1124 
AddMessageDirect(LPCWSTR message,bool needNumber)1125 void CProgressDialog::AddMessageDirect(LPCWSTR message, bool needNumber)
1126 {
1127   int itemIndex = _messageList.GetItemCount();
1128   wchar_t sz[16];
1129   sz[0] = 0;
1130   if (needNumber)
1131     ConvertUInt32ToString(_numMessages + 1, sz);
1132   _messageList.InsertItem(itemIndex, sz);
1133   _messageList.SetSubItem(itemIndex, 1, message);
1134 }
1135 
AddMessage(LPCWSTR message)1136 void CProgressDialog::AddMessage(LPCWSTR message)
1137 {
1138   UString s = message;
1139   bool needNumber = true;
1140   while (!s.IsEmpty())
1141   {
1142     int pos = s.Find(L'\n');
1143     if (pos < 0)
1144       break;
1145     AddMessageDirect(s.Left(pos), needNumber);
1146     needNumber = false;
1147     s.DeleteFrontal(pos + 1);
1148   }
1149   AddMessageDirect(s, needNumber);
1150   _numMessages++;
1151 }
1152 
GetNumDigits(UInt32 val)1153 static unsigned GetNumDigits(UInt32 val)
1154 {
1155   unsigned i;
1156   for (i = 0; val >= 10; i++)
1157     val /= 10;
1158   return i;
1159 }
1160 
UpdateMessagesDialog()1161 void CProgressDialog::UpdateMessagesDialog()
1162 {
1163   UStringVector messages;
1164   {
1165     NSynchronization::CCriticalSectionLock lock(Sync._cs);
1166     unsigned num = Sync.Messages.Size();
1167     if (num > _numPostedMessages)
1168     {
1169       messages.ClearAndReserve(num - _numPostedMessages);
1170       for (unsigned i = _numPostedMessages; i < num; i++)
1171         messages.AddInReserved(Sync.Messages[i]);
1172       _numPostedMessages = num;
1173     }
1174   }
1175   if (!messages.IsEmpty())
1176   {
1177     FOR_VECTOR (i, messages)
1178       AddMessage(messages[i]);
1179     if (_numAutoSizeMessages < 256 || GetNumDigits(_numPostedMessages) > GetNumDigits(_numAutoSizeMessages))
1180     {
1181       _messageList.SetColumnWidthAuto(0);
1182       _messageList.SetColumnWidthAuto(1);
1183       _numAutoSizeMessages = _numPostedMessages;
1184     }
1185   }
1186 }
1187 
1188 
OnButtonClicked(int buttonID,HWND buttonHWND)1189 bool CProgressDialog::OnButtonClicked(int buttonID, HWND buttonHWND)
1190 {
1191   switch (buttonID)
1192   {
1193     // case IDOK: // if IDCANCEL is not DEFPUSHBUTTON
1194     case IDCANCEL:
1195     {
1196       if (_waitCloseByCancelButton)
1197       {
1198         MessagesDisplayed = true;
1199         End(IDCLOSE);
1200         break;
1201       }
1202 
1203       bool paused = Sync.Get_Paused();
1204       if (!paused)
1205         OnPauseButton();
1206       _inCancelMessageBox = true;
1207       int res = ::MessageBoxW(*this, LangString(IDS_PROGRESS_ASK_CANCEL), _title, MB_YESNOCANCEL);
1208       _inCancelMessageBox = false;
1209       if (!paused)
1210         OnPauseButton();
1211       if (res == IDCANCEL || res == IDNO)
1212       {
1213         if (_externalCloseMessageWasReceived)
1214           OnExternalCloseMessage();
1215         return true;
1216       }
1217 
1218       _cancelWasPressed = true;
1219       MessagesDisplayed = true;
1220       break;
1221     }
1222 
1223     case IDB_PAUSE:
1224       OnPauseButton();
1225       return true;
1226     case IDB_PROGRESS_BACKGROUND:
1227       OnPriorityButton();
1228       return true;
1229   }
1230   return CModalDialog::OnButtonClicked(buttonID, buttonHWND);
1231 }
1232 
CheckNeedClose()1233 void CProgressDialog::CheckNeedClose()
1234 {
1235   if (_needClose)
1236   {
1237     PostMsg(kCloseMessage);
1238     _needClose = false;
1239   }
1240 }
1241 
ProcessWasFinished()1242 void CProgressDialog::ProcessWasFinished()
1243 {
1244   // Set Window title here.
1245   if (!WaitMode)
1246     WaitCreating();
1247 
1248   if (_wasCreated)
1249     PostMsg(kCloseMessage);
1250   else
1251     _needClose = true;
1252 }
1253 
1254 
MyThreadFunction(void * param)1255 static THREAD_FUNC_DECL MyThreadFunction(void *param)
1256 {
1257   CProgressThreadVirt *p = (CProgressThreadVirt *)param;
1258   try
1259   {
1260     p->Process();
1261     p->ThreadFinishedOK = true;
1262   }
1263   catch (...) { p->Result = E_FAIL; }
1264   return 0;
1265 }
1266 
1267 
Create(const UString & title,HWND parentWindow)1268 HRESULT CProgressThreadVirt::Create(const UString &title, HWND parentWindow)
1269 {
1270   NWindows::CThread thread;
1271   RINOK(thread.Create(MyThreadFunction, this));
1272   CProgressDialog::Create(title, thread, parentWindow);
1273   return S_OK;
1274 }
1275 
AddMessageToString(UString & dest,const UString & src)1276 static void AddMessageToString(UString &dest, const UString &src)
1277 {
1278   if (!src.IsEmpty())
1279   {
1280     if (!dest.IsEmpty())
1281       dest.Add_LF();
1282     dest += src;
1283   }
1284 }
1285 
Process()1286 void CProgressThreadVirt::Process()
1287 {
1288   CProgressCloser closer(*this);
1289   UString m;
1290   try { Result = ProcessVirt(); }
1291   catch(const wchar_t *s) { m = s; }
1292   catch(const UString &s) { m = s; }
1293   catch(const char *s) { m = GetUnicodeString(s); }
1294   catch(int v)
1295   {
1296     m = "Error #";
1297     m.Add_UInt32(v);
1298   }
1299   catch(...) { m = "Error"; }
1300   if (Result != E_ABORT)
1301   {
1302     if (m.IsEmpty() && Result != S_OK)
1303       m = HResultToMessage(Result);
1304   }
1305   AddMessageToString(m, FinalMessage.ErrorMessage.Message);
1306 
1307   {
1308     FOR_VECTOR(i, ErrorPaths)
1309     {
1310       if (i >= 32)
1311         break;
1312       AddMessageToString(m, fs2us(ErrorPaths[i]));
1313     }
1314   }
1315 
1316   CProgressSync &sync = Sync;
1317   NSynchronization::CCriticalSectionLock lock(sync._cs);
1318   if (m.IsEmpty())
1319   {
1320     if (!FinalMessage.OkMessage.Message.IsEmpty())
1321       sync.FinalMessage.OkMessage = FinalMessage.OkMessage;
1322   }
1323   else
1324   {
1325     sync.FinalMessage.ErrorMessage.Message = m;
1326     if (Result == S_OK)
1327       Result = E_FAIL;
1328   }
1329 }
1330 
HResultToMessage(HRESULT errorCode)1331 UString HResultToMessage(HRESULT errorCode)
1332 {
1333   if (errorCode == E_OUTOFMEMORY)
1334     return LangString(IDS_MEM_ERROR);
1335   else
1336     return NError::MyFormatMessage(errorCode);
1337 }
1338