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 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 88 bool CProgressSync::Get_Paused() 89 { 90 CRITICAL_LOCK 91 return _paused; 92 } 93 94 HRESULT CProgressSync::CheckStop() 95 { 96 for (;;) 97 { 98 { 99 CRITICAL_LOCK 100 CHECK_STOP 101 } 102 ::Sleep(kPauseSleepTime); 103 } 104 } 105 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 120 HRESULT CProgressSync::Set_NumFilesTotal(UInt64 val) 121 { 122 { 123 CRITICAL_LOCK 124 _totalFiles = val; 125 CHECK_STOP 126 } 127 return CheckStop(); 128 } 129 130 void CProgressSync::Set_NumBytesTotal(UInt64 val) 131 { 132 CRITICAL_LOCK 133 _totalBytes = val; 134 } 135 136 void CProgressSync::Set_NumFilesCur(UInt64 val) 137 { 138 CRITICAL_LOCK 139 _curFiles = val; 140 } 141 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 153 HRESULT CProgressSync::Set_NumBytesCur(UInt64 val) 154 { 155 { 156 CRITICAL_LOCK 157 _completedBytes = val; 158 CHECK_STOP 159 } 160 return CheckStop(); 161 } 162 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 172 void CProgressSync::Set_TitleFileName(const UString &fileName) 173 { 174 CRITICAL_LOCK 175 _titleFileName = fileName; 176 } 177 178 void CProgressSync::Set_Status(const UString &s) 179 { 180 CRITICAL_LOCK 181 _status = s; 182 } 183 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 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 209 void CProgressSync::AddError_Message(const wchar_t *message) 210 { 211 CRITICAL_LOCK 212 Messages.Add(message); 213 } 214 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 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 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 274 CProgressDialog::~CProgressDialog() 275 { 276 #ifdef __ITaskbarList3_INTERFACE_DEFINED__ 277 SetTaskbarProgressState(TBPF_NOPROGRESS); 278 #endif 279 AddToTitle(L""); 280 } 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 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 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 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 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 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 556 void CProgressDialog::OnCancel() { Sync.Set_Stopped(true); } 557 void CProgressDialog::OnOK() { } 558 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 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 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 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 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 635 static void GetChangedString(const UString &newStr, UString &prevStr, bool &hasChanged) 636 { 637 hasChanged = !(prevStr == newStr); 638 if (hasChanged) 639 prevStr = newStr; 640 } 641 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 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 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 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 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; 922 CWaitCursor() 923 { 924 _waitCursor = LoadCursor(NULL, IDC_WAIT); 925 if (_waitCursor != NULL) 926 _oldCursor = SetCursor(_waitCursor); 927 } 928 ~CWaitCursor() 929 { 930 if (_waitCursor != NULL) 931 SetCursor(_oldCursor); 932 } 933 }; 934 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 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 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 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 1090 void CProgressDialog::SetPauseText() 1091 { 1092 SetItemText(IDB_PAUSE, Sync.Get_Paused() ? _continue_String : _pause_String); 1093 SetTitleText(); 1094 } 1095 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 1108 void CProgressDialog::SetPriorityText() 1109 { 1110 SetItemText(IDB_PROGRESS_BACKGROUND, _foreground ? 1111 _background_String : 1112 _foreground_String); 1113 SetTitleText(); 1114 } 1115 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 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 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 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 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 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 1233 void CProgressDialog::CheckNeedClose() 1234 { 1235 if (_needClose) 1236 { 1237 PostMsg(kCloseMessage); 1238 _needClose = false; 1239 } 1240 } 1241 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 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 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 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 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 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