1// ProgressDialog2.cpp 2 3#include "StdAfx.h" 4 5#ifdef Z7_OLD_WIN_SDK 6#include <ShlGuid.h> 7#endif 8 9#include "../../../Common/IntToString.h" 10#include "../../../Common/StringConvert.h" 11 12#include "../../../Windows/Clipboard.h" 13#include "../../../Windows/ErrorMsg.h" 14 15#include "../GUI/ExtractRes.h" 16 17#include "LangUtils.h" 18 19#include "DialogSize.h" 20#include "ProgressDialog2.h" 21#include "ProgressDialog2Res.h" 22 23using namespace NWindows; 24 25extern HINSTANCE g_hInstance; 26 27static const UINT_PTR kTimerID = 3; 28 29static const UINT kCloseMessage = WM_APP + 1; 30// we can't use WM_USER, since WM_USER can be used by standard Windows procedure for Dialog 31 32static const UINT kTimerElapse = 33 #ifdef UNDER_CE 34 500 35 #else 36 200 37 #endif 38 ; 39 40static const UINT kCreateDelay = 41 #ifdef UNDER_CE 42 2500 43 #else 44 500 45 #endif 46 ; 47 48static const DWORD kPauseSleepTime = 100; 49 50#ifdef Z7_LANG 51 52static const UInt32 kLangIDs[] = 53{ 54 IDT_PROGRESS_ELAPSED, 55 IDT_PROGRESS_REMAINING, 56 IDT_PROGRESS_TOTAL, 57 IDT_PROGRESS_SPEED, 58 IDT_PROGRESS_PROCESSED, 59 IDT_PROGRESS_RATIO, 60 IDT_PROGRESS_ERRORS, 61 IDB_PROGRESS_BACKGROUND, 62 IDB_PAUSE 63}; 64 65static const UInt32 kLangIDs_Colon[] = 66{ 67 IDT_PROGRESS_PACKED, 68 IDT_PROGRESS_FILES 69}; 70 71#endif 72 73 74#define UNDEFINED_VAL ((UInt64)(Int64)-1) 75#define INIT_AS_UNDEFINED(v) v = UNDEFINED_VAL; 76#define IS_UNDEFINED_VAL(v) ((v) == UNDEFINED_VAL) 77#define IS_DEFINED_VAL(v) ((v) != UNDEFINED_VAL) 78 79CProgressSync::CProgressSync(): 80 _stopped(false), _paused(false), 81 _bytesProgressMode(true), 82 _isDir(false), 83 _totalBytes(UNDEFINED_VAL), _completedBytes(0), 84 _totalFiles(UNDEFINED_VAL), _curFiles(0), 85 _inSize(UNDEFINED_VAL), 86 _outSize(UNDEFINED_VAL) 87 {} 88 89#define CHECK_STOP if (_stopped) return E_ABORT; if (!_paused) return S_OK; 90#define CRITICAL_LOCK NSynchronization::CCriticalSectionLock lock(_cs); 91 92bool CProgressSync::Get_Paused() 93{ 94 CRITICAL_LOCK 95 return _paused; 96} 97 98HRESULT CProgressSync::CheckStop() 99{ 100 for (;;) 101 { 102 { 103 CRITICAL_LOCK 104 CHECK_STOP 105 } 106 ::Sleep(kPauseSleepTime); 107 } 108} 109 110HRESULT CProgressSync::ScanProgress(UInt64 numFiles, UInt64 totalSize, const FString &fileName, bool isDir) 111{ 112 { 113 CRITICAL_LOCK 114 _totalFiles = numFiles; 115 _totalBytes = totalSize; 116 _filePath = fs2us(fileName); 117 _isDir = isDir; 118 // _completedBytes = 0; 119 CHECK_STOP 120 } 121 return CheckStop(); 122} 123 124HRESULT CProgressSync::Set_NumFilesTotal(UInt64 val) 125{ 126 { 127 CRITICAL_LOCK 128 _totalFiles = val; 129 CHECK_STOP 130 } 131 return CheckStop(); 132} 133 134void CProgressSync::Set_NumBytesTotal(UInt64 val) 135{ 136 CRITICAL_LOCK 137 _totalBytes = val; 138} 139 140void CProgressSync::Set_NumFilesCur(UInt64 val) 141{ 142 CRITICAL_LOCK 143 _curFiles = val; 144} 145 146HRESULT CProgressSync::Set_NumBytesCur(const UInt64 *val) 147{ 148 { 149 CRITICAL_LOCK 150 if (val) 151 _completedBytes = *val; 152 CHECK_STOP 153 } 154 return CheckStop(); 155} 156 157HRESULT CProgressSync::Set_NumBytesCur(UInt64 val) 158{ 159 { 160 CRITICAL_LOCK 161 _completedBytes = val; 162 CHECK_STOP 163 } 164 return CheckStop(); 165} 166 167void CProgressSync::Set_Ratio(const UInt64 *inSize, const UInt64 *outSize) 168{ 169 CRITICAL_LOCK 170 if (inSize) 171 _inSize = *inSize; 172 if (outSize) 173 _outSize = *outSize; 174} 175 176void CProgressSync::Set_TitleFileName(const UString &fileName) 177{ 178 CRITICAL_LOCK 179 _titleFileName = fileName; 180} 181 182void CProgressSync::Set_Status(const UString &s) 183{ 184 CRITICAL_LOCK 185 _status = s; 186} 187 188HRESULT CProgressSync::Set_Status2(const UString &s, const wchar_t *path, bool isDir) 189{ 190 { 191 CRITICAL_LOCK 192 _status = s; 193 if (path) 194 _filePath = path; 195 else 196 _filePath.Empty(); 197 _isDir = isDir; 198 } 199 return CheckStop(); 200} 201 202void CProgressSync::Set_FilePath(const wchar_t *path, bool isDir) 203{ 204 CRITICAL_LOCK 205 if (path) 206 _filePath = path; 207 else 208 _filePath.Empty(); 209 _isDir = isDir; 210} 211 212 213void CProgressSync::AddError_Message(const wchar_t *message) 214{ 215 CRITICAL_LOCK 216 Messages.Add(message); 217} 218 219void CProgressSync::AddError_Message_Name(const wchar_t *message, const wchar_t *name) 220{ 221 UString s; 222 if (name && *name != 0) 223 s += name; 224 if (message && *message != 0) 225 { 226 if (!s.IsEmpty()) 227 s.Add_LF(); 228 s += message; 229 if (!s.IsEmpty() && s.Back() == L'\n') 230 s.DeleteBack(); 231 } 232 AddError_Message(s); 233} 234 235void CProgressSync::AddError_Code_Name(HRESULT systemError, const wchar_t *name) 236{ 237 UString s = NError::MyFormatMessage(systemError); 238 if (systemError == 0) 239 s = "Error"; 240 AddError_Message_Name(s, name); 241} 242 243CProgressDialog::CProgressDialog(): 244 _timer(0), 245 CompressingMode(true), 246 MainWindow(NULL) 247{ 248 _isDir = false; 249 250 _numMessages = 0; 251 IconID = -1; 252 MessagesDisplayed = false; 253 _wasCreated = false; 254 _needClose = false; 255 _inCancelMessageBox = false; 256 _externalCloseMessageWasReceived = false; 257 258 _numPostedMessages = 0; 259 _numAutoSizeMessages = 0; 260 _errorsWereDisplayed = false; 261 _waitCloseByCancelButton = false; 262 _cancelWasPressed = false; 263 ShowCompressionInfo = true; 264 WaitMode = false; 265 if (_dialogCreatedEvent.Create() != S_OK) 266 throw 1334987; 267 if (_createDialogEvent.Create() != S_OK) 268 throw 1334987; 269 // #ifdef __ITaskbarList3_INTERFACE_DEFINED__ 270 CoCreateInstance(CLSID_TaskbarList, NULL, CLSCTX_INPROC_SERVER, IID_ITaskbarList3, (void**)&_taskbarList); 271 if (_taskbarList) 272 _taskbarList->HrInit(); 273 // #endif 274} 275 276#ifndef Z7_SFX 277 278CProgressDialog::~CProgressDialog() 279{ 280 // #ifdef __ITaskbarList3_INTERFACE_DEFINED__ 281 SetTaskbarProgressState(TBPF_NOPROGRESS); 282 // #endif 283 AddToTitle(L""); 284} 285void CProgressDialog::AddToTitle(LPCWSTR s) 286{ 287 if (MainWindow) 288 { 289 CWindow window(MainWindow); 290 window.SetText((UString)s + MainTitle); 291 } 292} 293 294#endif 295 296 297void CProgressDialog::SetTaskbarProgressState() 298{ 299 // #ifdef __ITaskbarList3_INTERFACE_DEFINED__ 300 if (_taskbarList && _hwndForTaskbar) 301 { 302 TBPFLAG tbpFlags; 303 if (Sync.Get_Paused()) 304 tbpFlags = TBPF_PAUSED; 305 else 306 tbpFlags = _errorsWereDisplayed ? TBPF_ERROR: TBPF_NORMAL; 307 SetTaskbarProgressState(tbpFlags); 308 } 309 // #endif 310} 311 312static const unsigned kTitleFileNameSizeLimit = 36; 313static const unsigned kCurrentFileNameSizeLimit = 82; 314 315static void ReduceString(UString &s, unsigned size) 316{ 317 if (s.Len() <= size) 318 return; 319 s.Delete(size / 2, s.Len() - size); 320 s.Insert(size / 2, L" ... "); 321} 322 323void CProgressDialog::EnableErrorsControls(bool enable) 324{ 325 ShowItem_Bool(IDT_PROGRESS_ERRORS, enable); 326 ShowItem_Bool(IDT_PROGRESS_ERRORS_VAL, enable); 327 ShowItem_Bool(IDL_PROGRESS_MESSAGES, enable); 328} 329 330bool CProgressDialog::OnInit() 331{ 332 _hwndForTaskbar = MainWindow; 333 if (!_hwndForTaskbar) 334 _hwndForTaskbar = GetParent(); 335 if (!_hwndForTaskbar) 336 _hwndForTaskbar = *this; 337 338 INIT_AS_UNDEFINED(_progressBar_Range) 339 INIT_AS_UNDEFINED(_progressBar_Pos) 340 341 INIT_AS_UNDEFINED(_prevPercentValue) 342 INIT_AS_UNDEFINED(_prevElapsedSec) 343 INIT_AS_UNDEFINED(_prevRemainingSec) 344 345 INIT_AS_UNDEFINED(_prevSpeed) 346 _prevSpeed_MoveBits = 0; 347 348 _prevTime = ::GetTickCount(); 349 _elapsedTime = 0; 350 351 INIT_AS_UNDEFINED(_totalBytes_Prev) 352 INIT_AS_UNDEFINED(_processed_Prev) 353 INIT_AS_UNDEFINED(_packed_Prev) 354 INIT_AS_UNDEFINED(_ratio_Prev) 355 356 _filesStr_Prev.Empty(); 357 _filesTotStr_Prev.Empty(); 358 359 _foreground = true; 360 361 m_ProgressBar.Attach(GetItem(IDC_PROGRESS1)); 362 _messageList.Attach(GetItem(IDL_PROGRESS_MESSAGES)); 363 _messageList.SetUnicodeFormat(); 364 _messageList.SetExtendedListViewStyle(LVS_EX_FULLROWSELECT); 365 366 _wasCreated = true; 367 _dialogCreatedEvent.Set(); 368 369 #ifdef Z7_LANG 370 LangSetDlgItems(*this, kLangIDs, Z7_ARRAY_SIZE(kLangIDs)); 371 LangSetDlgItems_Colon(*this, kLangIDs_Colon, Z7_ARRAY_SIZE(kLangIDs_Colon)); 372 #endif 373 374 CWindow window(GetItem(IDB_PROGRESS_BACKGROUND)); 375 window.GetText(_background_String); 376 _backgrounded_String = _background_String; 377 _backgrounded_String.RemoveChar(L'&'); 378 379 window = GetItem(IDB_PAUSE); 380 window.GetText(_pause_String); 381 382 LangString(IDS_PROGRESS_FOREGROUND, _foreground_String); 383 LangString(IDS_CONTINUE, _continue_String); 384 LangString(IDS_PROGRESS_PAUSED, _paused_String); 385 386 SetText(_title); 387 SetPauseText(); 388 SetPriorityText(); 389 390 _messageList.InsertColumn(0, L"", 30); 391 _messageList.InsertColumn(1, L"", 600); 392 393 _messageList.SetColumnWidthAuto(0); 394 _messageList.SetColumnWidthAuto(1); 395 396 EnableErrorsControls(false); 397 398 GetItemSizes(IDCANCEL, _buttonSizeX, _buttonSizeY); 399 _numReduceSymbols = kCurrentFileNameSizeLimit; 400 NormalizeSize(true); 401 402 if (!ShowCompressionInfo) 403 { 404 HideItem(IDT_PROGRESS_PACKED); 405 HideItem(IDT_PROGRESS_PACKED_VAL); 406 HideItem(IDT_PROGRESS_RATIO); 407 HideItem(IDT_PROGRESS_RATIO_VAL); 408 } 409 410 if (IconID >= 0) 411 { 412 HICON icon = LoadIcon(g_hInstance, MAKEINTRESOURCE(IconID)); 413 // SetIcon(ICON_SMALL, icon); 414 SetIcon(ICON_BIG, icon); 415 } 416 _timer = SetTimer(kTimerID, kTimerElapse); 417 #ifdef UNDER_CE 418 Foreground(); 419 #endif 420 421 CheckNeedClose(); 422 423 SetTaskbarProgressState(); 424 425 return CModalDialog::OnInit(); 426} 427 428static const UINT kIDs[] = 429{ 430 IDT_PROGRESS_ELAPSED, IDT_PROGRESS_ELAPSED_VAL, 431 IDT_PROGRESS_REMAINING, IDT_PROGRESS_REMAINING_VAL, 432 IDT_PROGRESS_FILES, IDT_PROGRESS_FILES_VAL, 433 0, IDT_PROGRESS_FILES_TOTAL, 434 IDT_PROGRESS_ERRORS, IDT_PROGRESS_ERRORS_VAL, 435 436 IDT_PROGRESS_TOTAL, IDT_PROGRESS_TOTAL_VAL, 437 IDT_PROGRESS_SPEED, IDT_PROGRESS_SPEED_VAL, 438 IDT_PROGRESS_PROCESSED, IDT_PROGRESS_PROCESSED_VAL, 439 IDT_PROGRESS_PACKED, IDT_PROGRESS_PACKED_VAL, 440 IDT_PROGRESS_RATIO, IDT_PROGRESS_RATIO_VAL 441}; 442 443bool CProgressDialog::OnSize(WPARAM /* wParam */, int xSize, int ySize) 444{ 445 int sY; 446 int sStep; 447 int mx, my; 448 { 449 RECT r; 450 GetClientRectOfItem(IDT_PROGRESS_ELAPSED, r); 451 mx = r.left; 452 my = r.top; 453 sY = RECT_SIZE_Y(r); 454 GetClientRectOfItem(IDT_PROGRESS_REMAINING, r); 455 sStep = r.top - my; 456 } 457 458 InvalidateRect(NULL); 459 460 const int xSizeClient = xSize - mx * 2; 461 462 { 463 unsigned i; 464 for (i = 800; i > 40; i = i * 9 / 10) 465 if (Units_To_Pixels_X((int)i) <= xSizeClient) 466 break; 467 _numReduceSymbols = i / 4; 468 } 469 470 int yPos = ySize - my - _buttonSizeY; 471 472 ChangeSubWindowSizeX(GetItem(IDT_PROGRESS_STATUS), xSize - mx * 2); 473 ChangeSubWindowSizeX(GetItem(IDT_PROGRESS_FILE_NAME), xSize - mx * 2); 474 ChangeSubWindowSizeX(GetItem(IDC_PROGRESS1), xSize - mx * 2); 475 476 int bSizeX = _buttonSizeX; 477 int mx2 = mx; 478 for (;; mx2--) 479 { 480 const int bSize2 = bSizeX * 3 + mx2 * 2; 481 if (bSize2 <= xSizeClient) 482 break; 483 if (mx2 < 5) 484 { 485 bSizeX = (xSizeClient - mx2 * 2) / 3; 486 break; 487 } 488 } 489 if (bSizeX < 2) 490 bSizeX = 2; 491 492 { 493 RECT r; 494 GetClientRectOfItem(IDL_PROGRESS_MESSAGES, r); 495 const int y = r.top; 496 int ySize2 = yPos - my - y; 497 const int kMinYSize = _buttonSizeY + _buttonSizeY * 3 / 4; 498 int xx = xSize - mx * 2; 499 if (ySize2 < kMinYSize) 500 { 501 ySize2 = kMinYSize; 502 if (xx > bSizeX * 2) 503 xx -= bSizeX; 504 } 505 506 _messageList.Move(mx, y, xx, ySize2); 507 } 508 509 { 510 int xPos = xSize - mx; 511 xPos -= bSizeX; 512 MoveItem(IDCANCEL, xPos, yPos, bSizeX, _buttonSizeY); 513 xPos -= (mx2 + bSizeX); 514 MoveItem(IDB_PAUSE, xPos, yPos, bSizeX, _buttonSizeY); 515 xPos -= (mx2 + bSizeX); 516 MoveItem(IDB_PROGRESS_BACKGROUND, xPos, yPos, bSizeX, _buttonSizeY); 517 } 518 519 int valueSize; 520 int labelSize; 521 int padSize; 522 523 labelSize = Units_To_Pixels_X(MY_PROGRESS_LABEL_UNITS_MIN); 524 valueSize = Units_To_Pixels_X(MY_PROGRESS_VAL_UNITS); 525 padSize = Units_To_Pixels_X(MY_PROGRESS_PAD_UNITS); 526 const int requiredSize = (labelSize + valueSize) * 2 + padSize; 527 528 int gSize; 529 { 530 if (requiredSize < xSizeClient) 531 { 532 const int incr = (xSizeClient - requiredSize) / 3; 533 labelSize += incr; 534 } 535 else 536 labelSize = (xSizeClient - valueSize * 2 - padSize) / 2; 537 if (labelSize < 0) 538 labelSize = 0; 539 540 gSize = labelSize + valueSize; 541 padSize = xSizeClient - gSize * 2; 542 } 543 544 labelSize = gSize - valueSize; 545 546 yPos = my; 547 for (unsigned i = 0; i < Z7_ARRAY_SIZE(kIDs); i += 2) 548 { 549 int x = mx; 550 const unsigned kNumColumn1Items = 5 * 2; 551 if (i >= kNumColumn1Items) 552 { 553 if (i == kNumColumn1Items) 554 yPos = my; 555 x = mx + gSize + padSize; 556 } 557 if (kIDs[i] != 0) 558 MoveItem(kIDs[i], x, yPos, labelSize, sY); 559 MoveItem(kIDs[i + 1], x + labelSize, yPos, valueSize, sY); 560 yPos += sStep; 561 } 562 return false; 563} 564 565void CProgressDialog::OnCancel() { Sync.Set_Stopped(true); } 566void CProgressDialog::OnOK() { } 567 568void CProgressDialog::SetProgressRange(UInt64 range) 569{ 570 if (range == _progressBar_Range) 571 return; 572 _progressBar_Range = range; 573 INIT_AS_UNDEFINED(_progressBar_Pos) 574 _progressConv.Init(range); 575 m_ProgressBar.SetRange32(0, _progressConv.Count(range)); 576} 577 578void CProgressDialog::SetProgressPos(UInt64 pos) 579{ 580 if (pos >= _progressBar_Range || 581 pos <= _progressBar_Pos || 582 pos - _progressBar_Pos >= (_progressBar_Range >> 10)) 583 { 584 m_ProgressBar.SetPos(_progressConv.Count(pos)); 585 // #ifdef __ITaskbarList3_INTERFACE_DEFINED__ 586 if (_taskbarList && _hwndForTaskbar) 587 _taskbarList->SetProgressValue(_hwndForTaskbar, pos, _progressBar_Range); 588 // #endif 589 _progressBar_Pos = pos; 590 } 591} 592 593#define UINT_TO_STR_2(val) { s[0] = (wchar_t)('0' + (val) / 10); s[1] = (wchar_t)('0' + (val) % 10); s += 2; } 594 595void GetTimeString(UInt64 timeValue, wchar_t *s); 596void GetTimeString(UInt64 timeValue, wchar_t *s) 597{ 598 UInt64 hours = timeValue / 3600; 599 UInt32 seconds = (UInt32)(timeValue - hours * 3600); 600 UInt32 minutes = seconds / 60; 601 seconds %= 60; 602 if (hours > 99) 603 { 604 ConvertUInt64ToString(hours, s); 605 for (; *s != 0; s++); 606 } 607 else 608 { 609 UInt32 hours32 = (UInt32)hours; 610 UINT_TO_STR_2(hours32) 611 } 612 *s++ = ':'; UINT_TO_STR_2(minutes) 613 *s++ = ':'; UINT_TO_STR_2(seconds) 614 *s = 0; 615} 616 617static void ConvertSizeToString(UInt64 v, wchar_t *s) 618{ 619 Byte c = 0; 620 if (v >= ((UInt64)100000 << 20)) { v >>= 30; c = 'G'; } 621 else if (v >= ((UInt64)100000 << 10)) { v >>= 20; c = 'M'; } 622 else if (v >= ((UInt64)100000 << 0)) { v >>= 10; c = 'K'; } 623 ConvertUInt64ToString(v, s); 624 if (c != 0) 625 { 626 s += MyStringLen(s); 627 *s++ = ' '; 628 *s++ = c; 629 *s++ = 'B'; 630 *s++ = 0; 631 } 632} 633 634void CProgressDialog::ShowSize(unsigned id, UInt64 val, UInt64 &prev) 635{ 636 if (val == prev) 637 return; 638 prev = val; 639 wchar_t s[40]; 640 s[0] = 0; 641 if (IS_DEFINED_VAL(val)) 642 ConvertSizeToString(val, s); 643 SetItemText(id, s); 644} 645 646static void GetChangedString(const UString &newStr, UString &prevStr, bool &hasChanged) 647{ 648 hasChanged = !(prevStr == newStr); 649 if (hasChanged) 650 prevStr = newStr; 651} 652 653static unsigned GetPower32(UInt32 val) 654{ 655 const unsigned kStart = 32; 656 UInt32 mask = ((UInt32)1 << (kStart - 1)); 657 for (unsigned i = kStart;; i--) 658 { 659 if (i == 0 || (val & mask) != 0) 660 return i; 661 mask >>= 1; 662 } 663} 664 665static unsigned GetPower64(UInt64 val) 666{ 667 UInt32 high = (UInt32)(val >> 32); 668 if (high == 0) 669 return GetPower32((UInt32)val); 670 return GetPower32(high) + 32; 671} 672 673static UInt64 MyMultAndDiv(UInt64 mult1, UInt64 mult2, UInt64 divider) 674{ 675 unsigned pow1 = GetPower64(mult1); 676 unsigned pow2 = GetPower64(mult2); 677 while (pow1 + pow2 > 64) 678 { 679 if (pow1 > pow2) { pow1--; mult1 >>= 1; } 680 else { pow2--; mult2 >>= 1; } 681 divider >>= 1; 682 } 683 UInt64 res = mult1 * mult2; 684 if (divider != 0) 685 res /= divider; 686 return res; 687} 688 689void CProgressDialog::UpdateStatInfo(bool showAll) 690{ 691 UInt64 total, completed, totalFiles, completedFiles, inSize, outSize; 692 bool bytesProgressMode; 693 694 bool titleFileName_Changed; 695 bool curFilePath_Changed; 696 bool status_Changed; 697 unsigned numErrors; 698 { 699 NSynchronization::CCriticalSectionLock lock(Sync._cs); 700 total = Sync._totalBytes; 701 completed = Sync._completedBytes; 702 totalFiles = Sync._totalFiles; 703 completedFiles = Sync._curFiles; 704 inSize = Sync._inSize; 705 outSize = Sync._outSize; 706 bytesProgressMode = Sync._bytesProgressMode; 707 708 GetChangedString(Sync._titleFileName, _titleFileName, titleFileName_Changed); 709 GetChangedString(Sync._filePath, _filePath, curFilePath_Changed); 710 GetChangedString(Sync._status, _status, status_Changed); 711 if (_isDir != Sync._isDir) 712 { 713 curFilePath_Changed = true; 714 _isDir = Sync._isDir; 715 } 716 numErrors = Sync.Messages.Size(); 717 } 718 719 UInt32 curTime = ::GetTickCount(); 720 721 const UInt64 progressTotal = bytesProgressMode ? total : totalFiles; 722 const UInt64 progressCompleted = bytesProgressMode ? completed : completedFiles; 723 { 724 if (IS_UNDEFINED_VAL(progressTotal)) 725 { 726 // SetPos(0); 727 // SetRange(progressCompleted); 728 } 729 else 730 { 731 if (_progressBar_Pos != 0 || progressCompleted != 0 || 732 (_progressBar_Range == 0 && progressTotal != 0)) 733 { 734 SetProgressRange(progressTotal); 735 SetProgressPos(progressCompleted); 736 } 737 } 738 } 739 740 ShowSize(IDT_PROGRESS_TOTAL_VAL, total, _totalBytes_Prev); 741 742 _elapsedTime += (curTime - _prevTime); 743 _prevTime = curTime; 744 UInt64 elapsedSec = _elapsedTime / 1000; 745 bool elapsedChanged = false; 746 if (elapsedSec != _prevElapsedSec) 747 { 748 _prevElapsedSec = elapsedSec; 749 elapsedChanged = true; 750 wchar_t s[40]; 751 GetTimeString(elapsedSec, s); 752 SetItemText(IDT_PROGRESS_ELAPSED_VAL, s); 753 } 754 755 bool needSetTitle = false; 756 if (elapsedChanged || showAll) 757 { 758 if (numErrors > _numPostedMessages) 759 { 760 UpdateMessagesDialog(); 761 wchar_t s[32]; 762 ConvertUInt64ToString(numErrors, s); 763 SetItemText(IDT_PROGRESS_ERRORS_VAL, s); 764 if (!_errorsWereDisplayed) 765 { 766 _errorsWereDisplayed = true; 767 EnableErrorsControls(true); 768 SetTaskbarProgressState(); 769 } 770 } 771 772 if (progressCompleted != 0) 773 { 774 if (IS_UNDEFINED_VAL(progressTotal)) 775 { 776 if (IS_DEFINED_VAL(_prevRemainingSec)) 777 { 778 INIT_AS_UNDEFINED(_prevRemainingSec) 779 SetItemText(IDT_PROGRESS_REMAINING_VAL, L""); 780 } 781 } 782 else 783 { 784 UInt64 remainingTime = 0; 785 if (progressCompleted < progressTotal) 786 remainingTime = MyMultAndDiv(_elapsedTime, progressTotal - progressCompleted, progressCompleted); 787 UInt64 remainingSec = remainingTime / 1000; 788 if (remainingSec != _prevRemainingSec) 789 { 790 _prevRemainingSec = remainingSec; 791 wchar_t s[40]; 792 GetTimeString(remainingSec, s); 793 SetItemText(IDT_PROGRESS_REMAINING_VAL, s); 794 } 795 } 796 { 797 const UInt64 elapsedTime = (_elapsedTime == 0) ? 1 : _elapsedTime; 798 // 22.02: progressCompleted can be for number of files 799 UInt64 v = (completed * 1000) / elapsedTime; 800 Byte c = 0; 801 unsigned moveBits = 0; 802 if (v >= ((UInt64)10000 << 10)) { moveBits = 20; c = 'M'; } 803 else if (v >= ((UInt64)10000 << 0)) { moveBits = 10; c = 'K'; } 804 v >>= moveBits; 805 if (moveBits != _prevSpeed_MoveBits || v != _prevSpeed) 806 { 807 _prevSpeed_MoveBits = moveBits; 808 _prevSpeed = v; 809 wchar_t s[40]; 810 ConvertUInt64ToString(v, s); 811 unsigned pos = MyStringLen(s); 812 s[pos++] = ' '; 813 if (moveBits != 0) 814 s[pos++] = c; 815 s[pos++] = 'B'; 816 s[pos++] = '/'; 817 s[pos++] = 's'; 818 s[pos++] = 0; 819 SetItemText(IDT_PROGRESS_SPEED_VAL, s); 820 } 821 } 822 } 823 824 { 825 UInt64 percent = 0; 826 { 827 if (IS_DEFINED_VAL(progressTotal)) 828 { 829 percent = progressCompleted * 100; 830 if (progressTotal != 0) 831 percent /= progressTotal; 832 } 833 } 834 if (percent != _prevPercentValue) 835 { 836 _prevPercentValue = percent; 837 needSetTitle = true; 838 } 839 } 840 841 { 842 wchar_t s[64]; 843 844 ConvertUInt64ToString(completedFiles, s); 845 if (_filesStr_Prev != s) 846 { 847 _filesStr_Prev = s; 848 SetItemText(IDT_PROGRESS_FILES_VAL, s); 849 } 850 851 s[0] = 0; 852 if (IS_DEFINED_VAL(totalFiles)) 853 { 854 MyStringCopy(s, L" / "); 855 ConvertUInt64ToString(totalFiles, s + MyStringLen(s)); 856 } 857 if (_filesTotStr_Prev != s) 858 { 859 _filesTotStr_Prev = s; 860 SetItemText(IDT_PROGRESS_FILES_TOTAL, s); 861 } 862 } 863 864 const UInt64 packSize = CompressingMode ? outSize : inSize; 865 const UInt64 unpackSize = CompressingMode ? inSize : outSize; 866 867 if (IS_UNDEFINED_VAL(unpackSize) && 868 IS_UNDEFINED_VAL(packSize)) 869 { 870 ShowSize(IDT_PROGRESS_PROCESSED_VAL, completed, _processed_Prev); 871 ShowSize(IDT_PROGRESS_PACKED_VAL, UNDEFINED_VAL, _packed_Prev); 872 } 873 else 874 { 875 ShowSize(IDT_PROGRESS_PROCESSED_VAL, unpackSize, _processed_Prev); 876 ShowSize(IDT_PROGRESS_PACKED_VAL, packSize, _packed_Prev); 877 878 if (IS_DEFINED_VAL(packSize) && 879 IS_DEFINED_VAL(unpackSize) && 880 unpackSize != 0) 881 { 882 wchar_t s[32]; 883 UInt64 ratio = packSize * 100 / unpackSize; 884 if (_ratio_Prev != ratio) 885 { 886 _ratio_Prev = ratio; 887 ConvertUInt64ToString(ratio, s); 888 MyStringCat(s, L"%"); 889 SetItemText(IDT_PROGRESS_RATIO_VAL, s); 890 } 891 } 892 } 893 } 894 895 if (needSetTitle || titleFileName_Changed) 896 SetTitleText(); 897 898 if (status_Changed) 899 { 900 UString s = _status; 901 ReduceString(s, _numReduceSymbols); 902 SetItemText(IDT_PROGRESS_STATUS, _status); 903 } 904 905 if (curFilePath_Changed) 906 { 907 UString s1, s2; 908 if (_isDir) 909 s1 = _filePath; 910 else 911 { 912 int slashPos = _filePath.ReverseFind_PathSepar(); 913 if (slashPos >= 0) 914 { 915 s1.SetFrom(_filePath, (unsigned)(slashPos + 1)); 916 s2 = _filePath.Ptr((unsigned)(slashPos + 1)); 917 } 918 else 919 s2 = _filePath; 920 } 921 ReduceString(s1, _numReduceSymbols); 922 ReduceString(s2, _numReduceSymbols); 923 s1.Add_LF(); 924 s1 += s2; 925 SetItemText(IDT_PROGRESS_FILE_NAME, s1); 926 } 927} 928 929bool CProgressDialog::OnTimer(WPARAM /* timerID */, LPARAM /* callback */) 930{ 931 if (Sync.Get_Paused()) 932 return true; 933 CheckNeedClose(); 934 UpdateStatInfo(false); 935 return true; 936} 937 938struct CWaitCursor 939{ 940 HCURSOR _waitCursor; 941 HCURSOR _oldCursor; 942 CWaitCursor() 943 { 944 _waitCursor = LoadCursor(NULL, IDC_WAIT); 945 if (_waitCursor != NULL) 946 _oldCursor = SetCursor(_waitCursor); 947 } 948 ~CWaitCursor() 949 { 950 if (_waitCursor != NULL) 951 SetCursor(_oldCursor); 952 } 953}; 954 955INT_PTR CProgressDialog::Create(const UString &title, NWindows::CThread &thread, HWND wndParent) 956{ 957 INT_PTR res = 0; 958 try 959 { 960 if (WaitMode) 961 { 962 CWaitCursor waitCursor; 963 HANDLE h[] = { thread, _createDialogEvent }; 964 965 const DWORD res2 = WaitForMultipleObjects(Z7_ARRAY_SIZE(h), h, FALSE, kCreateDelay); 966 if (res2 == WAIT_OBJECT_0 && !Sync.ThereIsMessage()) 967 return 0; 968 } 969 _title = title; 970 BIG_DIALOG_SIZE(360, 192); 971 res = CModalDialog::Create(SIZED_DIALOG(IDD_PROGRESS), wndParent); 972 } 973 catch(...) 974 { 975 _wasCreated = true; 976 _dialogCreatedEvent.Set(); 977 } 978 thread.Wait_Close(); 979 if (!MessagesDisplayed) 980 MessageBoxW(wndParent, L"Progress Error", L"7-Zip", MB_ICONERROR); 981 return res; 982} 983 984bool CProgressDialog::OnExternalCloseMessage() 985{ 986 // it doesn't work if there is MessageBox. 987 // #ifdef __ITaskbarList3_INTERFACE_DEFINED__ 988 SetTaskbarProgressState(TBPF_NOPROGRESS); 989 // #endif 990 // AddToTitle(L"Finished "); 991 // SetText(L"Finished2 "); 992 993 UpdateStatInfo(true); 994 995 SetItemText(IDCANCEL, LangString(IDS_CLOSE)); 996 ::SendMessage(GetItem(IDCANCEL), BM_SETSTYLE, BS_DEFPUSHBUTTON, MAKELPARAM(TRUE, 0)); 997 HideItem(IDB_PROGRESS_BACKGROUND); 998 HideItem(IDB_PAUSE); 999 1000 ProcessWasFinished_GuiVirt(); 1001 1002 bool thereAreMessages; 1003 CProgressFinalMessage fm; 1004 { 1005 NSynchronization::CCriticalSectionLock lock(Sync._cs); 1006 thereAreMessages = !Sync.Messages.IsEmpty(); 1007 fm = Sync.FinalMessage; 1008 } 1009 1010 if (!fm.ErrorMessage.Message.IsEmpty()) 1011 { 1012 MessagesDisplayed = true; 1013 if (fm.ErrorMessage.Title.IsEmpty()) 1014 fm.ErrorMessage.Title = "7-Zip"; 1015 MessageBoxW(*this, fm.ErrorMessage.Message, fm.ErrorMessage.Title, MB_ICONERROR); 1016 } 1017 else if (!thereAreMessages) 1018 { 1019 MessagesDisplayed = true; 1020 1021 if (!fm.OkMessage.Message.IsEmpty()) 1022 { 1023 if (fm.OkMessage.Title.IsEmpty()) 1024 fm.OkMessage.Title = "7-Zip"; 1025 MessageBoxW(*this, fm.OkMessage.Message, fm.OkMessage.Title, MB_OK); 1026 } 1027 } 1028 1029 if (thereAreMessages && !_cancelWasPressed) 1030 { 1031 _waitCloseByCancelButton = true; 1032 UpdateMessagesDialog(); 1033 return true; 1034 } 1035 1036 End(0); 1037 return true; 1038} 1039 1040bool CProgressDialog::OnMessage(UINT message, WPARAM wParam, LPARAM lParam) 1041{ 1042 switch (message) 1043 { 1044 case kCloseMessage: 1045 { 1046 if (_timer) 1047 { 1048 /* 21.03 : KillTimer(kTimerID) instead of KillTimer(_timer). 1049 But (_timer == kTimerID) in Win10. So it worked too */ 1050 KillTimer(kTimerID); 1051 _timer = 0; 1052 } 1053 if (_inCancelMessageBox) 1054 { 1055 /* if user is in MessageBox(), we will call OnExternalCloseMessage() 1056 later, when MessageBox() will be closed */ 1057 _externalCloseMessageWasReceived = true; 1058 break; 1059 } 1060 return OnExternalCloseMessage(); 1061 } 1062 /* 1063 case WM_SETTEXT: 1064 { 1065 if (_timer == 0) 1066 return true; 1067 break; 1068 } 1069 */ 1070 } 1071 return CModalDialog::OnMessage(message, wParam, lParam); 1072} 1073 1074void CProgressDialog::SetTitleText() 1075{ 1076 UString s; 1077 if (Sync.Get_Paused()) 1078 { 1079 s += _paused_String; 1080 s.Add_Space(); 1081 } 1082 if (IS_DEFINED_VAL(_prevPercentValue)) 1083 { 1084 char temp[32]; 1085 ConvertUInt64ToString(_prevPercentValue, temp); 1086 s += temp; 1087 s += '%'; 1088 } 1089 if (!_foreground) 1090 { 1091 s.Add_Space(); 1092 s += _backgrounded_String; 1093 } 1094 1095 s.Add_Space(); 1096 #ifndef Z7_SFX 1097 { 1098 unsigned len = s.Len(); 1099 s += MainAddTitle; 1100 AddToTitle(s); 1101 s.DeleteFrom(len); 1102 } 1103 #endif 1104 1105 s += _title; 1106 if (!_titleFileName.IsEmpty()) 1107 { 1108 UString fileName = _titleFileName; 1109 ReduceString(fileName, kTitleFileNameSizeLimit); 1110 s.Add_Space(); 1111 s += fileName; 1112 } 1113 SetText(s); 1114} 1115 1116void CProgressDialog::SetPauseText() 1117{ 1118 SetItemText(IDB_PAUSE, Sync.Get_Paused() ? _continue_String : _pause_String); 1119 SetTitleText(); 1120} 1121 1122void CProgressDialog::OnPauseButton() 1123{ 1124 bool paused = !Sync.Get_Paused(); 1125 Sync.Set_Paused(paused); 1126 UInt32 curTime = ::GetTickCount(); 1127 if (paused) 1128 _elapsedTime += (curTime - _prevTime); 1129 SetTaskbarProgressState(); 1130 _prevTime = curTime; 1131 SetPauseText(); 1132} 1133 1134void CProgressDialog::SetPriorityText() 1135{ 1136 SetItemText(IDB_PROGRESS_BACKGROUND, _foreground ? 1137 _background_String : 1138 _foreground_String); 1139 SetTitleText(); 1140} 1141 1142void CProgressDialog::OnPriorityButton() 1143{ 1144 _foreground = !_foreground; 1145 #ifndef UNDER_CE 1146 SetPriorityClass(GetCurrentProcess(), _foreground ? NORMAL_PRIORITY_CLASS: IDLE_PRIORITY_CLASS); 1147 #endif 1148 SetPriorityText(); 1149} 1150 1151void CProgressDialog::AddMessageDirect(LPCWSTR message, bool needNumber) 1152{ 1153 wchar_t sz[16]; 1154 sz[0] = 0; 1155 if (needNumber) 1156 ConvertUInt32ToString(_numMessages + 1, sz); 1157 const unsigned itemIndex = _messageStrings.Size(); // _messageList.GetItemCount(); 1158 if (_messageList.InsertItem(itemIndex, sz) == (int)itemIndex) 1159 { 1160 _messageList.SetSubItem(itemIndex, 1, message); 1161 _messageStrings.Add(message); 1162 } 1163} 1164 1165void CProgressDialog::AddMessage(LPCWSTR message) 1166{ 1167 UString s = message; 1168 bool needNumber = true; 1169 while (!s.IsEmpty()) 1170 { 1171 const int pos = s.Find(L'\n'); 1172 if (pos < 0) 1173 break; 1174 AddMessageDirect(s.Left((unsigned)pos), needNumber); 1175 needNumber = false; 1176 s.DeleteFrontal((unsigned)pos + 1); 1177 } 1178 AddMessageDirect(s, needNumber); 1179 _numMessages++; 1180} 1181 1182static unsigned GetNumDigits(UInt32 val) 1183{ 1184 unsigned i; 1185 for (i = 0; val >= 10; i++) 1186 val /= 10; 1187 return i; 1188} 1189 1190void CProgressDialog::UpdateMessagesDialog() 1191{ 1192 UStringVector messages; 1193 { 1194 NSynchronization::CCriticalSectionLock lock(Sync._cs); 1195 unsigned num = Sync.Messages.Size(); 1196 if (num > _numPostedMessages) 1197 { 1198 messages.ClearAndReserve(num - _numPostedMessages); 1199 for (unsigned i = _numPostedMessages; i < num; i++) 1200 messages.AddInReserved(Sync.Messages[i]); 1201 _numPostedMessages = num; 1202 } 1203 } 1204 if (!messages.IsEmpty()) 1205 { 1206 FOR_VECTOR (i, messages) 1207 AddMessage(messages[i]); 1208 if (_numAutoSizeMessages < 256 || GetNumDigits(_numPostedMessages) > GetNumDigits(_numAutoSizeMessages)) 1209 { 1210 _messageList.SetColumnWidthAuto(0); 1211 _messageList.SetColumnWidthAuto(1); 1212 _numAutoSizeMessages = _numPostedMessages; 1213 } 1214 } 1215} 1216 1217 1218bool CProgressDialog::OnButtonClicked(unsigned buttonID, HWND buttonHWND) 1219{ 1220 switch (buttonID) 1221 { 1222 // case IDOK: // if IDCANCEL is not DEFPUSHBUTTON 1223 case IDCANCEL: 1224 { 1225 if (_waitCloseByCancelButton) 1226 { 1227 MessagesDisplayed = true; 1228 End(IDCLOSE); 1229 break; 1230 } 1231 1232 if (_cancelWasPressed) 1233 return true; 1234 1235 const bool paused = Sync.Get_Paused(); 1236 1237 if (!paused) 1238 { 1239 OnPauseButton(); 1240 } 1241 1242 _inCancelMessageBox = true; 1243 const int res = ::MessageBoxW(*this, LangString(IDS_PROGRESS_ASK_CANCEL), _title, MB_YESNOCANCEL); 1244 _inCancelMessageBox = false; 1245 if (res == IDYES) 1246 _cancelWasPressed = true; 1247 1248 if (!paused) 1249 { 1250 OnPauseButton(); 1251 } 1252 1253 if (_externalCloseMessageWasReceived) 1254 { 1255 /* we have received kCloseMessage while we were in MessageBoxW(). 1256 so we call OnExternalCloseMessage() here. 1257 it can show MessageBox and it can close dialog */ 1258 OnExternalCloseMessage(); 1259 return true; 1260 } 1261 1262 if (!_cancelWasPressed) 1263 return true; 1264 1265 MessagesDisplayed = true; 1266 // we will call Sync.Set_Stopped(true) in OnButtonClicked() : OnCancel() 1267 break; 1268 } 1269 1270 case IDB_PAUSE: 1271 OnPauseButton(); 1272 return true; 1273 case IDB_PROGRESS_BACKGROUND: 1274 OnPriorityButton(); 1275 return true; 1276 } 1277 return CModalDialog::OnButtonClicked(buttonID, buttonHWND); 1278} 1279 1280void CProgressDialog::CheckNeedClose() 1281{ 1282 if (_needClose) 1283 { 1284 PostMsg(kCloseMessage); 1285 _needClose = false; 1286 } 1287} 1288 1289void CProgressDialog::ProcessWasFinished() 1290{ 1291 // Set Window title here. 1292 if (!WaitMode) 1293 WaitCreating(); 1294 1295 if (_wasCreated) 1296 PostMsg(kCloseMessage); 1297 else 1298 _needClose = true; 1299} 1300 1301 1302bool CProgressDialog::OnNotify(UINT /* controlID */, LPNMHDR header) 1303{ 1304 if (header->hwndFrom != _messageList) 1305 return false; 1306 switch (header->code) 1307 { 1308 case LVN_KEYDOWN: 1309 { 1310 LPNMLVKEYDOWN keyDownInfo = LPNMLVKEYDOWN(header); 1311 switch (keyDownInfo->wVKey) 1312 { 1313 case 'A': 1314 { 1315 if (IsKeyDown(VK_CONTROL)) 1316 { 1317 _messageList.SelectAll(); 1318 return true; 1319 } 1320 break; 1321 } 1322 case VK_INSERT: 1323 case 'C': 1324 { 1325 if (IsKeyDown(VK_CONTROL)) 1326 { 1327 CopyToClipboard(); 1328 return true; 1329 } 1330 break; 1331 } 1332 } 1333 } 1334 } 1335 return false; 1336} 1337 1338 1339static void ListView_GetSelected(NControl::CListView &listView, CUIntVector &vector) 1340{ 1341 vector.Clear(); 1342 int index = -1; 1343 for (;;) 1344 { 1345 index = listView.GetNextSelectedItem(index); 1346 if (index < 0) 1347 break; 1348 vector.Add((unsigned)index); 1349 } 1350} 1351 1352 1353void CProgressDialog::CopyToClipboard() 1354{ 1355 CUIntVector indexes; 1356 ListView_GetSelected(_messageList, indexes); 1357 UString s; 1358 unsigned numIndexes = indexes.Size(); 1359 if (numIndexes == 0) 1360 numIndexes = (unsigned)_messageList.GetItemCount(); 1361 1362 for (unsigned i = 0; i < numIndexes; i++) 1363 { 1364 const unsigned index = (i < indexes.Size() ? indexes[i] : i); 1365 // s.Add_UInt32(index); 1366 // s += ": "; 1367 s += _messageStrings[index]; 1368 { 1369 s += 1370 #ifdef _WIN32 1371 "\r\n" 1372 #else 1373 "\n" 1374 #endif 1375 ; 1376 } 1377 } 1378 1379 ClipboardSetText(*this, s); 1380} 1381 1382 1383static THREAD_FUNC_DECL MyThreadFunction(void *param) 1384{ 1385 CProgressThreadVirt *p = (CProgressThreadVirt *)param; 1386 try 1387 { 1388 p->Process(); 1389 p->ThreadFinishedOK = true; 1390 } 1391 catch (...) { p->Result = E_FAIL; } 1392 return 0; 1393} 1394 1395 1396HRESULT CProgressThreadVirt::Create(const UString &title, HWND parentWindow) 1397{ 1398 NWindows::CThread thread; 1399 const WRes wres = thread.Create(MyThreadFunction, this); 1400 if (wres != 0) 1401 return HRESULT_FROM_WIN32(wres); 1402 CProgressDialog::Create(title, thread, parentWindow); 1403 return S_OK; 1404} 1405 1406static void AddMessageToString(UString &dest, const UString &src) 1407{ 1408 if (!src.IsEmpty()) 1409 { 1410 if (!dest.IsEmpty()) 1411 dest.Add_LF(); 1412 dest += src; 1413 } 1414} 1415 1416void CProgressThreadVirt::Process() 1417{ 1418 CProgressCloser closer(*this); 1419 UString m; 1420 try { Result = ProcessVirt(); } 1421 catch(const wchar_t *s) { m = s; } 1422 catch(const UString &s) { m = s; } 1423 catch(const char *s) { m = GetUnicodeString(s); } 1424 catch(int v) 1425 { 1426 m = "Error #"; 1427 m.Add_UInt32((unsigned)v); 1428 } 1429 catch(...) { m = "Error"; } 1430 if (Result != E_ABORT) 1431 { 1432 if (m.IsEmpty() && Result != S_OK) 1433 m = HResultToMessage(Result); 1434 } 1435 AddMessageToString(m, FinalMessage.ErrorMessage.Message); 1436 1437 { 1438 FOR_VECTOR(i, ErrorPaths) 1439 { 1440 if (i >= 32) 1441 break; 1442 AddMessageToString(m, fs2us(ErrorPaths[i])); 1443 } 1444 } 1445 1446 CProgressSync &sync = Sync; 1447 NSynchronization::CCriticalSectionLock lock(sync._cs); 1448 if (m.IsEmpty()) 1449 { 1450 if (!FinalMessage.OkMessage.Message.IsEmpty()) 1451 sync.FinalMessage.OkMessage = FinalMessage.OkMessage; 1452 } 1453 else 1454 { 1455 sync.FinalMessage.ErrorMessage.Message = m; 1456 if (Result == S_OK) 1457 Result = E_FAIL; 1458 } 1459} 1460 1461UString HResultToMessage(HRESULT errorCode) 1462{ 1463 if (errorCode == E_OUTOFMEMORY) 1464 return LangString(IDS_MEM_ERROR); 1465 else 1466 return NError::MyFormatMessage(errorCode); 1467} 1468