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 = L"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 wcscat(s, L" / ");
832 ConvertUInt64ToString(totalFiles, s + wcslen(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 wcscat(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 = L"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 = L"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.AddAscii(temp);
1054 s += L'%';
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 wchar_t s[16];
1277 ConvertUInt32ToString(v, s);
1278 m = L"Error #";
1279 m += s;
1280 }
1281 catch(...) { m = L"Error"; }
1282 if (Result != E_ABORT)
1283 {
1284 if (m.IsEmpty() && Result != S_OK)
1285 m = HResultToMessage(Result);
1286 }
1287 AddMessageToString(m, FinalMessage.ErrorMessage.Message);
1288
1289 {
1290 FOR_VECTOR(i, ErrorPaths)
1291 {
1292 if (i >= 32)
1293 break;
1294 AddMessageToString(m, fs2us(ErrorPaths[i]));
1295 }
1296 }
1297
1298 CProgressSync &sync = ProgressDialog.Sync;
1299 NSynchronization::CCriticalSectionLock lock(sync._cs);
1300 if (m.IsEmpty())
1301 {
1302 if (!FinalMessage.OkMessage.Message.IsEmpty())
1303 sync.FinalMessage.OkMessage = FinalMessage.OkMessage;
1304 }
1305 else
1306 {
1307 sync.FinalMessage.ErrorMessage.Message = m;
1308 if (Result == S_OK)
1309 Result = E_FAIL;
1310 }
1311 }
1312
HResultToMessage(HRESULT errorCode)1313 UString HResultToMessage(HRESULT errorCode)
1314 {
1315 if (errorCode == E_OUTOFMEMORY)
1316 return LangString(IDS_MEM_ERROR);
1317 else
1318 return NError::MyFormatMessage(errorCode);
1319 }
1320