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 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 89 bool CProgressSync::Get_Paused() 90 { 91 CRITICAL_LOCK 92 return _paused; 93 } 94 95 HRESULT CProgressSync::CheckStop() 96 { 97 for (;;) 98 { 99 { 100 CRITICAL_LOCK 101 CHECK_STOP 102 } 103 ::Sleep(kPauseSleepTime); 104 } 105 } 106 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 121 void CProgressSync::Set_NumFilesTotal(UInt64 val) 122 { 123 CRITICAL_LOCK 124 _totalFiles = val; 125 } 126 127 void CProgressSync::Set_NumBytesTotal(UInt64 val) 128 { 129 CRITICAL_LOCK 130 _totalBytes = val; 131 } 132 133 void CProgressSync::Set_NumFilesCur(UInt64 val) 134 { 135 CRITICAL_LOCK 136 _curFiles = val; 137 } 138 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 150 HRESULT CProgressSync::Set_NumBytesCur(UInt64 val) 151 { 152 { 153 CRITICAL_LOCK 154 _completedBytes = val; 155 CHECK_STOP 156 } 157 return CheckStop(); 158 } 159 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 169 void CProgressSync::Set_TitleFileName(const UString &fileName) 170 { 171 CRITICAL_LOCK 172 _titleFileName = fileName; 173 } 174 void CProgressSync::Set_Status(const UString &s) 175 { 176 CRITICAL_LOCK 177 _status = s; 178 } 179 180 void CProgressSync::Set_FilePath(const UString &path, bool isDir) 181 { 182 CRITICAL_LOCK 183 _filePath = path; 184 _isDir = isDir; 185 } 186 187 188 void CProgressSync::AddError_Message(const wchar_t *message) 189 { 190 CRITICAL_LOCK 191 Messages.Add(message); 192 } 193 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 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 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 250 CProgressDialog::~CProgressDialog() 251 { 252 #ifdef __ITaskbarList3_INTERFACE_DEFINED__ 253 SetTaskbarProgressState(TBPF_NOPROGRESS); 254 #endif 255 AddToTitle(L""); 256 } 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 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 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 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 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 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 532 void CProgressDialog::OnCancel() { Sync.Set_Stopped(true); } 533 void CProgressDialog::OnOK() { } 534 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 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 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 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 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 611 static void GetChangedString(const UString &newStr, UString &prevStr, bool &hasChanged) 612 { 613 hasChanged = !(prevStr == newStr); 614 if (hasChanged) 615 prevStr = newStr; 616 } 617 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 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 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 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 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; 900 CWaitCursor() 901 { 902 _waitCursor = LoadCursor(NULL, IDC_WAIT); 903 if (_waitCursor != NULL) 904 _oldCursor = SetCursor(_waitCursor); 905 } 906 ~CWaitCursor() 907 { 908 if (_waitCursor != NULL) 909 SetCursor(_oldCursor); 910 } 911 }; 912 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 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 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 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 1064 void CProgressDialog::SetPauseText() 1065 { 1066 SetItemText(IDB_PAUSE, Sync.Get_Paused() ? _continue_String : _pause_String); 1067 SetTitleText(); 1068 } 1069 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 1082 void CProgressDialog::SetPriorityText() 1083 { 1084 SetItemText(IDB_PROGRESS_BACKGROUND, _foreground ? 1085 _background_String : 1086 _foreground_String); 1087 SetTitleText(); 1088 } 1089 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 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 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 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 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 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 1207 void CProgressDialog::CheckNeedClose() 1208 { 1209 if (_needClose) 1210 { 1211 PostMessage(kCloseMessage); 1212 _needClose = false; 1213 } 1214 } 1215 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 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 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 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 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