// ProgressDialog2.cpp #include "StdAfx.h" #include "Common/IntToString.h" #include "ProgressDialog2.h" using namespace NWindows; static const UINT_PTR kTimerID = 3; static const UINT kTimerElapse = 100; #ifdef LANG #include "LangUtils.h" #endif #ifdef LANG static CIDLangPair kIDLangPairs[] = { { IDCANCEL, 0x02000C00 }, { IDC_PROGRESS_ELAPSED, 0x02000C01 }, { IDC_PROGRESS_REMAINING, 0x02000C02 }, { IDC_PROGRESS_TOTAL, 0x02000C03 }, { IDC_PROGRESS_SPEED, 0x02000C04 }, { IDC_PROGRESS_UNPACKED, 0x02000C05 }, { IDC_PROGRESS_PACKED, 0x02000323 }, { IDC_PROGRESS_RATIO, 0x02000C06 }, { IDC_PROGRESS_SPEED, 0x02000C04 }, { IDC_PROGRESS_FILES, 0x02000320 }, { IDC_BUTTON_PROGRESS_PRIORITY, 0x02000C10 }, { IDC_BUTTON_PAUSE, 0x02000C12 }, { IDCANCEL, 0x02000711 }, }; #endif HRESULT CProgressSynch::ProcessStopAndPause() { for (;;) { if (GetStopped()) return E_ABORT; if (!GetPaused()) break; ::Sleep(100); } return S_OK; } HRESULT CProgressSynch::SetPosAndCheckPaused(UInt64 completed) { RINOK(ProcessStopAndPause()); SetPos(completed); return S_OK; } #ifndef _SFX CProgressDialog::~CProgressDialog() { AddToTitle(L""); } void CProgressDialog::AddToTitle(LPCWSTR s) { if (MainWindow != 0) { CWindow window(MainWindow); window.SetText(s + UString(MainTitle)); } } static const int kTitleFileNameSizeLimit = 40; static const int kCurrentFileNameSizeLimit = 82; static void ReduceString(UString &s, int size) { if (s.Length() > size) s = s.Left(size / 2) + UString(L" ... ") + s.Right(size / 2); } #endif bool CProgressDialog::OnInit() { _range = (UInt64)(Int64)(-1); _prevPercentValue = UInt32(-1); _prevElapsedSec = UInt32(-1); _prevRemainingSec = UInt32(-1); _prevSpeed = UInt32(-1); _prevMode = kSpeedBytes; _prevTime = ::GetTickCount(); _elapsedTime = 0; _foreground = true; #ifdef LANG // LangSetWindowText(HWND(*this), 0x02000C00); LangSetDlgItemsText(HWND(*this), kIDLangPairs, sizeof(kIDLangPairs) / sizeof(kIDLangPairs[0])); #endif CWindow window(GetItem(IDC_BUTTON_PROGRESS_PRIORITY)); window.GetText(backgroundString); backgroundedString = backgroundString; backgroundedString.Replace(L"&", L""); window = GetItem(IDC_BUTTON_PAUSE); window.GetText(pauseString); foregroundString = LangString(IDS_PROGRESS_FOREGROUND, 0x02000C11); continueString = LangString(IDS_PROGRESS_CONTINUE, 0x02000C13); pausedString = LangString(IDS_PROGRESS_PAUSED, 0x02000C20); m_ProgressBar.Attach(GetItem(IDC_PROGRESS1)); _timer = SetTimer(kTimerID, kTimerElapse); _dialogCreatedEvent.Set(); SetText(_title); SetPauseText(); SetPriorityText(); return CModalDialog::OnInit(); } void CProgressDialog::OnCancel() { ProgressSynch.SetStopped(true); } static void ConvertSizeToString(UInt64 value, wchar_t *s) { const wchar_t *kModif = L" KM"; for (int i = 0; ; i++) if (i == 2 || value < (UInt64(10000) << (i * 10))) { ConvertUInt64ToString(value >> (i * 10), s); s += wcslen(s); *s++ = ' '; if (i != 0) *s++ = kModif[i]; *s++ = L'B'; *s++ = L'\0'; return; } } void CProgressDialog::SetRange(UInt64 range) { _range = range; _previousPos = (UInt64)(Int64)-1; _converter.Init(range); m_ProgressBar.SetRange32(0, _converter.Count(range)); // Test it for 100% } void CProgressDialog::SetPos(UInt64 pos) { bool redraw = true; if (pos < _range && pos > _previousPos) { if (pos - _previousPos < (_range >> 10)) redraw = false; } if(redraw) { m_ProgressBar.SetPos(_converter.Count(pos)); // Test it for 100% _previousPos = pos; } } static void GetTimeString(UInt64 timeValue, TCHAR *s) { wsprintf(s, TEXT("%02d:%02d:%02d"), UInt32(timeValue / 3600), UInt32((timeValue / 60) % 60), UInt32(timeValue % 60)); } void CProgressDialog::ShowSize(int id, UInt64 value) { wchar_t s[40]; s[0] = 0; if (value != (UInt64)(Int64)-1) ConvertSizeToString(value, s); SetItemText(id, s); } bool CProgressDialog::OnTimer(WPARAM /* timerID */, LPARAM /* callback */) { if (ProgressSynch.GetPaused()) return true; UInt64 total, completed, totalFiles, completedFiles, inSize, outSize; bool bytesProgressMode; ProgressSynch.GetProgress(total, completed, totalFiles, completedFiles, inSize, outSize, bytesProgressMode); UInt32 curTime = ::GetTickCount(); UInt64 progressTotal = bytesProgressMode ? total : totalFiles; UInt64 progressCompleted = bytesProgressMode ? completed : completedFiles; if (progressTotal != _range) SetRange(progressTotal); if (progressTotal == (UInt64)(Int64)-1) { SetPos(0); SetRange(progressCompleted); } else SetPos(progressCompleted); wchar_t s[32] = { 0 }; if (total != (UInt64)(Int64)-1) ConvertSizeToString(total, s); SetItemText(IDC_PROGRESS_TOTAL_VALUE, s); _elapsedTime += (curTime - _prevTime); _prevTime = curTime; UInt32 elapsedSec = _elapsedTime / 1000; bool elapsedChanged = false; if (elapsedSec != _prevElapsedSec) { TCHAR s[40]; GetTimeString(elapsedSec, s); SetItemText(IDC_PROGRESS_ELAPSED_VALUE, s); _prevElapsedSec = elapsedSec; elapsedChanged = true; } if (elapsedChanged) { if (completed != 0) { if (total == (UInt64)(Int64)-1) { SetItemText(IDC_PROGRESS_REMAINING_VALUE, L""); } else { UInt64 remainingTime = 0; if (completed < total) remainingTime = _elapsedTime * (total - completed) / completed; UInt64 remainingSec = remainingTime / 1000; if (remainingSec != _prevRemainingSec) { TCHAR s[40]; GetTimeString(remainingSec, s); SetItemText(IDC_PROGRESS_REMAINING_VALUE, s); _prevRemainingSec = remainingSec; } } // if (elapsedChanged) { UInt32 elapsedTime = (_elapsedTime == 0) ? 1 : _elapsedTime; UInt64 speedB = (completed * 1000) / elapsedTime; UInt64 speedKB = speedB / 1024; UInt64 speedMB = speedKB / 1024; const UInt32 kLimit1 = 10; TCHAR s[40]; bool needRedraw = false; if (speedMB >= kLimit1) { if (_prevMode != kSpeedMBytes || speedMB != _prevSpeed) { ConvertUInt64ToString(speedMB, s); lstrcat(s, TEXT(" MB/s")); _prevMode = kSpeedMBytes; _prevSpeed = speedMB; needRedraw = true; } } else if (speedKB >= kLimit1) { if (_prevMode != kSpeedKBytes || speedKB != _prevSpeed) { ConvertUInt64ToString(speedKB, s); lstrcat(s, TEXT(" KB/s")); _prevMode = kSpeedKBytes; _prevSpeed = speedKB; needRedraw = true; } } else { if (_prevMode != kSpeedBytes || speedB != _prevSpeed) { ConvertUInt64ToString(speedB, s); lstrcat(s, TEXT(" B/s")); _prevMode = kSpeedBytes; _prevSpeed = speedB; needRedraw = true; } } if (needRedraw) SetItemText(IDC_PROGRESS_SPEED_VALUE, s); } } if (total == 0) total = 1; UInt32 percentValue = (UInt32)(completed * 100 / total); UString titleName; ProgressSynch.GetTitleFileName(titleName); if (percentValue != _prevPercentValue || _prevTitleName != titleName) { _prevPercentValue = percentValue; SetTitleText(); _prevTitleName = titleName; } TCHAR s[64]; ConvertUInt64ToString(completedFiles, s); if (totalFiles != (UInt64)(Int64)-1) { lstrcat(s, TEXT(" / ")); ConvertUInt64ToString(totalFiles, s + lstrlen(s)); } SetItemText(IDC_PROGRESS_FILES_VALUE, s); const UInt64 packSize = CompressingMode ? outSize : inSize; const UInt64 unpackSize = CompressingMode ? inSize : outSize; if (unpackSize == (UInt64)(Int64)-1 && packSize == (UInt64)(Int64)-1) { ShowSize(IDC_PROGRESS_UNPACKED_VALUE, completed); SetItemText(IDC_PROGRESS_PACKED_VALUE, L""); } else { ShowSize(IDC_PROGRESS_UNPACKED_VALUE, unpackSize); ShowSize(IDC_PROGRESS_PACKED_VALUE, packSize); if (packSize != (UInt64)(Int64)-1 && unpackSize != (UInt64)(Int64)-1 && unpackSize != 0) { UInt64 ratio = packSize * 100 / unpackSize; ConvertUInt64ToString(ratio, s); lstrcat(s, TEXT("%")); SetItemText(IDC_PROGRESS_RATIO_VALUE, s); } } } UString fileName; ProgressSynch.GetCurrentFileName(fileName); if (_prevFileName != fileName) { int slashPos = fileName.ReverseFind(WCHAR_PATH_SEPARATOR); UString s1, s2; if (slashPos >= 0) { s1 = fileName.Left(slashPos + 1); s2 = fileName.Mid(slashPos + 1); } else s2 = fileName; ReduceString(s1, kCurrentFileNameSizeLimit); ReduceString(s2, kCurrentFileNameSizeLimit); UString s = s1 + L"\n" + s2; SetItemText(IDC_PROGRESS_FILE_NAME, s); _prevFileName == fileName; } return true; } //////////////////// // CU64ToI32Converter static const UInt64 kMaxIntValue = 0x7FFFFFFF; void CU64ToI32Converter::Init(UInt64 range) { _numShiftBits = 0; while(range > kMaxIntValue) { range >>= 1; _numShiftBits++; } } int CU64ToI32Converter::Count(UInt64 aValue) { return int(aValue >> _numShiftBits); } const UINT CProgressDialog::kCloseMessage = WM_USER + 1; bool CProgressDialog::OnMessage(UINT message, WPARAM wParam, LPARAM lParam) { switch(message) { case kCloseMessage: { KillTimer(_timer); _timer = 0; End(0); return true; } case WM_SETTEXT: { if (_timer == 0) return true; } } return CModalDialog::OnMessage(message, wParam, lParam); } void CProgressDialog::SetTitleText() { UString title; if (ProgressSynch.GetPaused()) { title = pausedString; title += L" "; } if (_prevPercentValue != UInt32(-1)) { wchar_t s[64]; ConvertUInt64ToString(_prevPercentValue, s); title += s; title += L"%"; } if (!_foreground) { title += L" "; title += backgroundedString; } title += L" "; UString totalTitle = title + _title; UString fileName; ProgressSynch.GetTitleFileName(fileName); if (!fileName.IsEmpty()) { ReduceString(fileName, kTitleFileNameSizeLimit); totalTitle += L" "; totalTitle += fileName; } SetText(totalTitle); #ifndef _SFX AddToTitle(title + MainAddTitle); #endif } void CProgressDialog::SetPauseText() { SetItemText(IDC_BUTTON_PAUSE, ProgressSynch.GetPaused() ? continueString : pauseString); SetTitleText(); } void CProgressDialog::OnPauseButton() { bool paused = !ProgressSynch.GetPaused(); ProgressSynch.SetPaused(paused); UInt32 curTime = ::GetTickCount(); if (paused) _elapsedTime += (curTime - _prevTime); _prevTime = curTime; SetPauseText(); } void CProgressDialog::SetPriorityText() { SetItemText(IDC_BUTTON_PROGRESS_PRIORITY, _foreground ? backgroundString : foregroundString); SetTitleText(); } void CProgressDialog::OnPriorityButton() { _foreground = !_foreground; SetPriorityClass(GetCurrentProcess(), _foreground ? NORMAL_PRIORITY_CLASS: IDLE_PRIORITY_CLASS); SetPriorityText(); } bool CProgressDialog::OnButtonClicked(int buttonID, HWND buttonHWND) { switch(buttonID) { case IDCANCEL: { bool paused = ProgressSynch.GetPaused();; // ProgressSynch.SetPaused(true); if (!paused) OnPauseButton(); int res = ::MessageBoxW(HWND(*this), LangString(IDS_PROGRESS_ASK_CANCEL, 0x02000C30), _title, MB_YESNOCANCEL); // ProgressSynch.SetPaused(paused); if (!paused) OnPauseButton(); if (res == IDCANCEL || res == IDNO) return true; break; } case IDC_BUTTON_PAUSE: OnPauseButton(); return true; case IDC_BUTTON_PROGRESS_PRIORITY: { OnPriorityButton(); return true; } } return CModalDialog::OnButtonClicked(buttonID, buttonHWND); }