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 19using namespace NWindows; 20 21extern HINSTANCE g_hInstance; 22 23static const UINT_PTR kTimerID = 3; 24 25static 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 28static const UINT kTimerElapse = 29 #ifdef UNDER_CE 30 500 31 #else 32 200 33 #endif 34 ; 35 36static const UINT kCreateDelay = 37 #ifdef UNDER_CE 38 2500 39 #else 40 500 41 #endif 42 ; 43 44static const DWORD kPauseSleepTime = 100; 45 46#ifdef LANG 47 48static 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 61static 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 75CProgressSync::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 88bool CProgressSync::Get_Paused() 89{ 90 CRITICAL_LOCK 91 return _paused; 92} 93 94HRESULT CProgressSync::CheckStop() 95{ 96 for (;;) 97 { 98 { 99 CRITICAL_LOCK 100 CHECK_STOP 101 } 102 ::Sleep(kPauseSleepTime); 103 } 104} 105 106HRESULT 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 120HRESULT CProgressSync::Set_NumFilesTotal(UInt64 val) 121{ 122 { 123 CRITICAL_LOCK 124 _totalFiles = val; 125 CHECK_STOP 126 } 127 return CheckStop(); 128} 129 130void CProgressSync::Set_NumBytesTotal(UInt64 val) 131{ 132 CRITICAL_LOCK 133 _totalBytes = val; 134} 135 136void CProgressSync::Set_NumFilesCur(UInt64 val) 137{ 138 CRITICAL_LOCK 139 _curFiles = val; 140} 141 142HRESULT 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 153HRESULT CProgressSync::Set_NumBytesCur(UInt64 val) 154{ 155 { 156 CRITICAL_LOCK 157 _completedBytes = val; 158 CHECK_STOP 159 } 160 return CheckStop(); 161} 162 163void 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 172void CProgressSync::Set_TitleFileName(const UString &fileName) 173{ 174 CRITICAL_LOCK 175 _titleFileName = fileName; 176} 177 178void CProgressSync::Set_Status(const UString &s) 179{ 180 CRITICAL_LOCK 181 _status = s; 182} 183 184HRESULT 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 198void 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 209void CProgressSync::AddError_Message(const wchar_t *message) 210{ 211 CRITICAL_LOCK 212 Messages.Add(message); 213} 214 215void 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 231void 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 239CProgressDialog::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 271CProgressDialog::~CProgressDialog() 272{ 273 #ifdef __ITaskbarList3_INTERFACE_DEFINED__ 274 SetTaskbarProgressState(TBPF_NOPROGRESS); 275 #endif 276 AddToTitle(L""); 277} 278void 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 290void 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 305static const unsigned kTitleFileNameSizeLimit = 36; 306static const unsigned kCurrentFileNameSizeLimit = 82; 307 308static 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 316void 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 323bool 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 418static 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 432bool 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 553void CProgressDialog::OnCancel() { Sync.Set_Stopped(true); } 554void CProgressDialog::OnOK() { } 555 556void 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 566void 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 583void 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 604static 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 620void 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 632static void GetChangedString(const UString &newStr, UString &prevStr, bool &hasChanged) 633{ 634 hasChanged = !(prevStr == newStr); 635 if (hasChanged) 636 prevStr = newStr; 637} 638 639static 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 651static 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 659static 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 675void 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 906bool 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 915struct CWaitCursor 916{ 917 HCURSOR _waitCursor; 918 HCURSOR _oldCursor; 919 CWaitCursor() 920 { 921 _waitCursor = LoadCursor(NULL, IDC_WAIT); 922 if (_waitCursor != NULL) 923 _oldCursor = SetCursor(_waitCursor); 924 } 925 ~CWaitCursor() 926 { 927 if (_waitCursor != NULL) 928 SetCursor(_oldCursor); 929 } 930}; 931 932INT_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 962bool 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 1014bool 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 1041void 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 1083void CProgressDialog::SetPauseText() 1084{ 1085 SetItemText(IDB_PAUSE, Sync.Get_Paused() ? _continue_String : _pause_String); 1086 SetTitleText(); 1087} 1088 1089void 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 1101void CProgressDialog::SetPriorityText() 1102{ 1103 SetItemText(IDB_PROGRESS_BACKGROUND, _foreground ? 1104 _background_String : 1105 _foreground_String); 1106 SetTitleText(); 1107} 1108 1109void 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 1118void 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 1129void 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 1146static unsigned GetNumDigits(UInt32 val) 1147{ 1148 unsigned i; 1149 for (i = 0; val >= 10; i++) 1150 val /= 10; 1151 return i; 1152} 1153 1154void 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 1182bool 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 1226void CProgressDialog::CheckNeedClose() 1227{ 1228 if (_needClose) 1229 { 1230 PostMsg(kCloseMessage); 1231 _needClose = false; 1232 } 1233} 1234 1235void 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 1248HRESULT 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 1256static 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 1266void 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 1313UString HResultToMessage(HRESULT errorCode) 1314{ 1315 if (errorCode == E_OUTOFMEMORY) 1316 return LangString(IDS_MEM_ERROR); 1317 else 1318 return NError::MyFormatMessage(errorCode); 1319} 1320