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