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