1 // ProgressDialog2.cpp
2 
3 #include "StdAfx.h"
4 
5 #include "Common/IntToString.h"
6 
7 #include "ProgressDialog2.h"
8 
9 using namespace NWindows;
10 
11 static const UINT_PTR kTimerID = 3;
12 static const UINT kTimerElapse = 100;
13 
14 #ifdef LANG
15 #include "LangUtils.h"
16 #endif
17 
18 #ifdef LANG
19 static CIDLangPair kIDLangPairs[] =
20 {
21   { IDCANCEL, 0x02000C00 },
22   { IDC_PROGRESS_ELAPSED, 0x02000C01 },
23   { IDC_PROGRESS_REMAINING, 0x02000C02 },
24   { IDC_PROGRESS_TOTAL, 0x02000C03 },
25   { IDC_PROGRESS_SPEED, 0x02000C04 },
26   { IDC_PROGRESS_UNPACKED, 0x02000C05 },
27   { IDC_PROGRESS_PACKED, 0x02000323 },
28   { IDC_PROGRESS_RATIO, 0x02000C06 },
29   { IDC_PROGRESS_SPEED, 0x02000C04 },
30   { IDC_PROGRESS_FILES, 0x02000320 },
31   { IDC_BUTTON_PROGRESS_PRIORITY, 0x02000C10 },
32   { IDC_BUTTON_PAUSE, 0x02000C12 },
33   { IDCANCEL, 0x02000711 },
34 };
35 #endif
36 
ProcessStopAndPause()37 HRESULT CProgressSynch::ProcessStopAndPause()
38 {
39   for (;;)
40   {
41     if (GetStopped())
42       return E_ABORT;
43     if (!GetPaused())
44       break;
45     ::Sleep(100);
46   }
47   return S_OK;
48 }
49 
SetPosAndCheckPaused(UInt64 completed)50 HRESULT CProgressSynch::SetPosAndCheckPaused(UInt64 completed)
51 {
52   RINOK(ProcessStopAndPause());
53   SetPos(completed);
54   return S_OK;
55 }
56 
57 #ifndef _SFX
~CProgressDialog()58 CProgressDialog::~CProgressDialog()
59 {
60   AddToTitle(L"");
61 }
AddToTitle(LPCWSTR s)62 void CProgressDialog::AddToTitle(LPCWSTR s)
63 {
64   if (MainWindow != 0)
65   {
66     CWindow window(MainWindow);
67     window.SetText(s + UString(MainTitle));
68   }
69 }
70 
71 static const int kTitleFileNameSizeLimit = 40;
72 static const int kCurrentFileNameSizeLimit = 82;
73 
ReduceString(UString & s,int size)74 static void ReduceString(UString &s, int size)
75 {
76   if (s.Length() > size)
77     s = s.Left(size / 2) + UString(L" ... ") + s.Right(size / 2);
78 }
79 #endif
80 
OnInit()81 bool CProgressDialog::OnInit()
82 {
83   _range = (UInt64)(Int64)(-1);
84   _prevPercentValue = UInt32(-1);
85   _prevElapsedSec = UInt32(-1);
86   _prevRemainingSec = UInt32(-1);
87   _prevSpeed = UInt32(-1);
88   _prevMode = kSpeedBytes;
89   _prevTime = ::GetTickCount();
90   _elapsedTime = 0;
91   _foreground = true;
92 
93   #ifdef LANG
94   // LangSetWindowText(HWND(*this), 0x02000C00);
95   LangSetDlgItemsText(HWND(*this), kIDLangPairs, sizeof(kIDLangPairs) / sizeof(kIDLangPairs[0]));
96   #endif
97 
98 
99   CWindow window(GetItem(IDC_BUTTON_PROGRESS_PRIORITY));
100   window.GetText(backgroundString);
101   backgroundedString = backgroundString;
102   backgroundedString.Replace(L"&", L"");
103 
104   window = GetItem(IDC_BUTTON_PAUSE);
105   window.GetText(pauseString);
106 
107   foregroundString = LangString(IDS_PROGRESS_FOREGROUND, 0x02000C11);
108   continueString = LangString(IDS_PROGRESS_CONTINUE, 0x02000C13);
109   pausedString = LangString(IDS_PROGRESS_PAUSED, 0x02000C20);
110 
111   m_ProgressBar.Attach(GetItem(IDC_PROGRESS1));
112   _timer = SetTimer(kTimerID, kTimerElapse);
113   _dialogCreatedEvent.Set();
114   SetText(_title);
115   SetPauseText();
116   SetPriorityText();
117   return CModalDialog::OnInit();
118 }
119 
OnCancel()120 void CProgressDialog::OnCancel()
121 {
122   ProgressSynch.SetStopped(true);
123 }
124 
ConvertSizeToString(UInt64 value,wchar_t * s)125 static void ConvertSizeToString(UInt64 value, wchar_t *s)
126 {
127   const wchar_t *kModif = L" KM";
128   for (int i = 0; ; i++)
129     if (i == 2 || value < (UInt64(10000) << (i * 10)))
130     {
131       ConvertUInt64ToString(value >> (i * 10), s);
132       s += wcslen(s);
133       *s++ = ' ';
134       if (i != 0)
135         *s++ = kModif[i];
136       *s++ = L'B';
137       *s++ = L'\0';
138       return;
139     }
140 }
141 
SetRange(UInt64 range)142 void CProgressDialog::SetRange(UInt64 range)
143 {
144   _range = range;
145   _previousPos = (UInt64)(Int64)-1;
146   _converter.Init(range);
147   m_ProgressBar.SetRange32(0, _converter.Count(range)); // Test it for 100%
148 }
149 
SetPos(UInt64 pos)150 void CProgressDialog::SetPos(UInt64 pos)
151 {
152   bool redraw = true;
153   if (pos < _range && pos > _previousPos)
154   {
155     if (pos - _previousPos < (_range >> 10))
156       redraw = false;
157   }
158   if(redraw)
159   {
160     m_ProgressBar.SetPos(_converter.Count(pos));  // Test it for 100%
161     _previousPos = pos;
162   }
163 }
164 
GetTimeString(UInt64 timeValue,TCHAR * s)165 static void GetTimeString(UInt64 timeValue, TCHAR *s)
166 {
167   wsprintf(s, TEXT("%02d:%02d:%02d"),
168       UInt32(timeValue / 3600),
169       UInt32((timeValue / 60) % 60),
170       UInt32(timeValue % 60));
171 }
172 
ShowSize(int id,UInt64 value)173 void CProgressDialog::ShowSize(int id, UInt64 value)
174 {
175   wchar_t s[40];
176   s[0] = 0;
177   if (value != (UInt64)(Int64)-1)
178     ConvertSizeToString(value, s);
179   SetItemText(id, s);
180 }
181 
OnTimer(WPARAM,LPARAM)182 bool CProgressDialog::OnTimer(WPARAM /* timerID */, LPARAM /* callback */)
183 {
184   if (ProgressSynch.GetPaused())
185     return true;
186   UInt64 total, completed, totalFiles, completedFiles, inSize, outSize;
187   bool bytesProgressMode;
188   ProgressSynch.GetProgress(total, completed, totalFiles, completedFiles, inSize, outSize, bytesProgressMode);
189 
190   UInt32 curTime = ::GetTickCount();
191 
192   UInt64 progressTotal = bytesProgressMode ? total : totalFiles;
193   UInt64 progressCompleted = bytesProgressMode ? completed : completedFiles;
194 
195   if (progressTotal != _range)
196     SetRange(progressTotal);
197   if (progressTotal == (UInt64)(Int64)-1)
198   {
199     SetPos(0);
200     SetRange(progressCompleted);
201   }
202   else
203     SetPos(progressCompleted);
204 
205   wchar_t s[32] = { 0 };
206   if (total != (UInt64)(Int64)-1)
207     ConvertSizeToString(total, s);
208   SetItemText(IDC_PROGRESS_TOTAL_VALUE, s);
209 
210   _elapsedTime += (curTime - _prevTime);
211   _prevTime = curTime;
212 
213   UInt32 elapsedSec = _elapsedTime / 1000;
214 
215   bool elapsedChanged = false;
216   if (elapsedSec != _prevElapsedSec)
217   {
218     TCHAR s[40];
219     GetTimeString(elapsedSec, s);
220     SetItemText(IDC_PROGRESS_ELAPSED_VALUE, s);
221     _prevElapsedSec = elapsedSec;
222     elapsedChanged = true;
223   }
224 
225   if (elapsedChanged)
226   {
227     if (completed != 0)
228     {
229 
230     if (total == (UInt64)(Int64)-1)
231     {
232       SetItemText(IDC_PROGRESS_REMAINING_VALUE, L"");
233     }
234     else
235     {
236       UInt64 remainingTime = 0;
237       if (completed < total)
238         remainingTime = _elapsedTime * (total - completed)  / completed;
239       UInt64 remainingSec = remainingTime / 1000;
240       if (remainingSec != _prevRemainingSec)
241       {
242         TCHAR s[40];
243         GetTimeString(remainingSec, s);
244         SetItemText(IDC_PROGRESS_REMAINING_VALUE, s);
245         _prevRemainingSec = remainingSec;
246       }
247     }
248     // if (elapsedChanged)
249     {
250       UInt32 elapsedTime = (_elapsedTime == 0) ? 1 : _elapsedTime;
251       UInt64 speedB = (completed * 1000) / elapsedTime;
252       UInt64 speedKB = speedB / 1024;
253       UInt64 speedMB = speedKB / 1024;
254       const UInt32 kLimit1 = 10;
255       TCHAR s[40];
256       bool needRedraw = false;
257       if (speedMB >= kLimit1)
258       {
259         if (_prevMode != kSpeedMBytes || speedMB != _prevSpeed)
260         {
261           ConvertUInt64ToString(speedMB, s);
262           lstrcat(s, TEXT(" MB/s"));
263           _prevMode = kSpeedMBytes;
264           _prevSpeed = speedMB;
265           needRedraw = true;
266         }
267       }
268       else if (speedKB >= kLimit1)
269       {
270         if (_prevMode != kSpeedKBytes || speedKB != _prevSpeed)
271         {
272           ConvertUInt64ToString(speedKB, s);
273           lstrcat(s, TEXT(" KB/s"));
274           _prevMode = kSpeedKBytes;
275           _prevSpeed = speedKB;
276           needRedraw = true;
277         }
278       }
279       else
280       {
281         if (_prevMode != kSpeedBytes || speedB != _prevSpeed)
282         {
283           ConvertUInt64ToString(speedB, s);
284           lstrcat(s, TEXT(" B/s"));
285           _prevMode = kSpeedBytes;
286           _prevSpeed = speedB;
287           needRedraw = true;
288         }
289       }
290       if (needRedraw)
291         SetItemText(IDC_PROGRESS_SPEED_VALUE, s);
292     }
293     }
294 
295     if (total == 0)
296       total = 1;
297     UInt32 percentValue = (UInt32)(completed * 100 / total);
298     UString titleName;
299     ProgressSynch.GetTitleFileName(titleName);
300     if (percentValue != _prevPercentValue || _prevTitleName != titleName)
301     {
302       _prevPercentValue = percentValue;
303       SetTitleText();
304       _prevTitleName = titleName;
305     }
306 
307     TCHAR s[64];
308     ConvertUInt64ToString(completedFiles, s);
309     if (totalFiles != (UInt64)(Int64)-1)
310     {
311       lstrcat(s, TEXT(" / "));
312       ConvertUInt64ToString(totalFiles, s + lstrlen(s));
313     }
314 
315     SetItemText(IDC_PROGRESS_FILES_VALUE, s);
316 
317     const UInt64 packSize   = CompressingMode ? outSize : inSize;
318     const UInt64 unpackSize = CompressingMode ? inSize : outSize;
319 
320     if (unpackSize == (UInt64)(Int64)-1 && packSize == (UInt64)(Int64)-1)
321     {
322       ShowSize(IDC_PROGRESS_UNPACKED_VALUE, completed);
323       SetItemText(IDC_PROGRESS_PACKED_VALUE, L"");
324     }
325     else
326     {
327       ShowSize(IDC_PROGRESS_UNPACKED_VALUE, unpackSize);
328       ShowSize(IDC_PROGRESS_PACKED_VALUE, packSize);
329 
330       if (packSize != (UInt64)(Int64)-1 && unpackSize != (UInt64)(Int64)-1 && unpackSize != 0)
331       {
332         UInt64 ratio = packSize * 100 / unpackSize;
333         ConvertUInt64ToString(ratio, s);
334         lstrcat(s, TEXT("%"));
335         SetItemText(IDC_PROGRESS_RATIO_VALUE, s);
336       }
337     }
338   }
339 
340 
341   UString fileName;
342   ProgressSynch.GetCurrentFileName(fileName);
343   if (_prevFileName != fileName)
344   {
345     int slashPos = fileName.ReverseFind(WCHAR_PATH_SEPARATOR);
346     UString s1, s2;
347     if (slashPos >= 0)
348     {
349       s1 = fileName.Left(slashPos + 1);
350       s2 = fileName.Mid(slashPos + 1);
351     }
352     else
353       s2 = fileName;
354     ReduceString(s1, kCurrentFileNameSizeLimit);
355     ReduceString(s2, kCurrentFileNameSizeLimit);
356     UString s = s1 + L"\n" + s2;
357     SetItemText(IDC_PROGRESS_FILE_NAME, s);
358     _prevFileName == fileName;
359   }
360 
361   return true;
362 }
363 
364 
365 ////////////////////
366 // CU64ToI32Converter
367 
368 static const UInt64 kMaxIntValue = 0x7FFFFFFF;
369 
Init(UInt64 range)370 void CU64ToI32Converter::Init(UInt64 range)
371 {
372   _numShiftBits = 0;
373   while(range > kMaxIntValue)
374   {
375     range >>= 1;
376     _numShiftBits++;
377   }
378 }
379 
Count(UInt64 aValue)380 int CU64ToI32Converter::Count(UInt64 aValue)
381 {
382   return int(aValue >> _numShiftBits);
383 }
384 
385 const UINT CProgressDialog::kCloseMessage = WM_USER + 1;
386 
OnMessage(UINT message,WPARAM wParam,LPARAM lParam)387 bool CProgressDialog::OnMessage(UINT message, WPARAM wParam, LPARAM lParam)
388 {
389   switch(message)
390   {
391     case kCloseMessage:
392     {
393       KillTimer(_timer);
394       _timer = 0;
395       End(0);
396       return true;
397     }
398     case WM_SETTEXT:
399     {
400       if (_timer == 0)
401         return true;
402     }
403   }
404   return CModalDialog::OnMessage(message, wParam, lParam);
405 }
406 
SetTitleText()407 void CProgressDialog::SetTitleText()
408 {
409   UString title;
410   if (ProgressSynch.GetPaused())
411   {
412     title = pausedString;
413     title += L" ";
414   }
415   if (_prevPercentValue != UInt32(-1))
416   {
417     wchar_t s[64];
418     ConvertUInt64ToString(_prevPercentValue, s);
419     title += s;
420     title += L"%";
421   }
422   if (!_foreground)
423   {
424     title += L" ";
425     title += backgroundedString;
426   }
427   title += L" ";
428   UString totalTitle = title + _title;
429   UString fileName;
430   ProgressSynch.GetTitleFileName(fileName);
431   if (!fileName.IsEmpty())
432   {
433     ReduceString(fileName, kTitleFileNameSizeLimit);
434     totalTitle += L" ";
435     totalTitle += fileName;
436   }
437   SetText(totalTitle);
438   #ifndef _SFX
439   AddToTitle(title + MainAddTitle);
440   #endif
441 }
442 
SetPauseText()443 void CProgressDialog::SetPauseText()
444 {
445   SetItemText(IDC_BUTTON_PAUSE, ProgressSynch.GetPaused() ?
446     continueString : pauseString);
447   SetTitleText();
448 }
449 
OnPauseButton()450 void CProgressDialog::OnPauseButton()
451 {
452   bool paused = !ProgressSynch.GetPaused();
453   ProgressSynch.SetPaused(paused);
454   UInt32 curTime = ::GetTickCount();
455   if (paused)
456     _elapsedTime += (curTime - _prevTime);
457   _prevTime = curTime;
458   SetPauseText();
459 }
460 
SetPriorityText()461 void CProgressDialog::SetPriorityText()
462 {
463   SetItemText(IDC_BUTTON_PROGRESS_PRIORITY, _foreground ?
464       backgroundString :
465       foregroundString);
466   SetTitleText();
467 }
468 
OnPriorityButton()469 void CProgressDialog::OnPriorityButton()
470 {
471   _foreground = !_foreground;
472   SetPriorityClass(GetCurrentProcess(), _foreground ?
473     NORMAL_PRIORITY_CLASS: IDLE_PRIORITY_CLASS);
474   SetPriorityText();
475 }
476 
OnButtonClicked(int buttonID,HWND buttonHWND)477 bool CProgressDialog::OnButtonClicked(int buttonID, HWND buttonHWND)
478 {
479   switch(buttonID)
480   {
481     case IDCANCEL:
482     {
483       bool paused = ProgressSynch.GetPaused();;
484       // ProgressSynch.SetPaused(true);
485       if (!paused)
486         OnPauseButton();
487       int res = ::MessageBoxW(HWND(*this),
488           LangString(IDS_PROGRESS_ASK_CANCEL, 0x02000C30),
489           _title, MB_YESNOCANCEL);
490       // ProgressSynch.SetPaused(paused);
491       if (!paused)
492         OnPauseButton();
493       if (res == IDCANCEL || res == IDNO)
494         return true;
495       break;
496     }
497     case IDC_BUTTON_PAUSE:
498       OnPauseButton();
499       return true;
500     case IDC_BUTTON_PROGRESS_PRIORITY:
501     {
502       OnPriorityButton();
503       return true;
504     }
505   }
506   return CModalDialog::OnButtonClicked(buttonID, buttonHWND);
507 }
508