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