// ProgressBox.cpp #include "StdAfx.h" #include "../../../Common/IntToString.h" #include "../../../Common/StringConvert.h" #include "FarUtils.h" #include "ProgressBox.h" void CPercentPrinterState::ClearCurState() { Completed = 0; Total = ((UInt64)(Int64)-1); Files = 0; FilesTotal = 0; Command.Empty(); FileName.Empty(); } void CProgressBox::Init(const char *title) { _title = title; _wasPrinted = false; StartTick = GetTickCount(); _prevTick = StartTick; _prevElapsedSec = 0; } static unsigned GetPower32(UInt32 val) { const unsigned kStart = 32; UInt32 mask = ((UInt32)1 << (kStart - 1)); for (unsigned i = kStart;; i--) { if (i == 0 || (val & mask) != 0) return i; mask >>= 1; } } static unsigned GetPower64(UInt64 val) { UInt32 high = (UInt32)(val >> 32); if (high == 0) return GetPower32((UInt32)val); return GetPower32(high) + 32; } static UInt64 MyMultAndDiv(UInt64 mult1, UInt64 mult2, UInt64 divider) { unsigned pow1 = GetPower64(mult1); unsigned pow2 = GetPower64(mult2); while (pow1 + pow2 > 64) { if (pow1 > pow2) { pow1--; mult1 >>= 1; } else { pow2--; mult2 >>= 1; } divider >>= 1; } UInt64 res = mult1 * mult2; if (divider != 0) res /= divider; return res; } #define UINT_TO_STR_2(val) { s[0] = (char)('0' + (val) / 10); s[1] = (char)('0' + (val) % 10); s += 2; } static void GetTimeString(UInt64 timeValue, char *s) { const UInt64 hours = timeValue / 3600; UInt32 seconds = (UInt32)(timeValue - hours * 3600); const UInt32 minutes = seconds / 60; seconds %= 60; if (hours > 99) { ConvertUInt64ToString(hours, s); for (; *s != 0; s++); } else { const UInt32 hours32 = (UInt32)hours; UINT_TO_STR_2(hours32) } *s++ = ':'; UINT_TO_STR_2(minutes) *s++ = ':'; UINT_TO_STR_2(seconds) *s = 0; } void CProgressBox::ReduceString(const UString &src, AString &dest) { UnicodeStringToMultiByte2(dest, src, CP_OEMCP); if (dest.Len() <= MaxLen) return; unsigned len = FileName.Len(); for (; len != 0;) { unsigned delta = len / 8; if (delta == 0) delta = 1; len -= delta; _tempU = FileName; _tempU.Delete(len / 2, FileName.Len() - len); _tempU.Insert(len / 2, L" . "); UnicodeStringToMultiByte2(dest, _tempU, CP_OEMCP); if (dest.Len() <= MaxLen) return; } dest.Empty(); } static void Print_UInt64_and_String(AString &s, UInt64 val, const char *name) { char temp[32]; ConvertUInt64ToString(val, temp); s += temp; s.Add_Space(); s += name; } static void PrintSize_bytes_Smart(AString &s, UInt64 val) { // Print_UInt64_and_String(s, val, "bytes"); { char temp[32]; ConvertUInt64ToString(val, temp); s += temp; } if (val == 0) return; unsigned numBits = 10; char c = 'K'; char temp[4] = { 'K', 'i', 'B', 0 }; if (val >= ((UInt64)10 << 30)) { numBits = 30; c = 'G'; } else if (val >= ((UInt64)10 << 20)) { numBits = 20; c = 'M'; } temp[0] = c; s += " ("; Print_UInt64_and_String(s, ((val + ((UInt64)1 << numBits) - 1) >> numBits), temp); s += ')'; } static const unsigned kPercentsSize = 4; void CProgressBox::Print() { DWORD tick = GetTickCount(); DWORD elapsedTicks = tick - StartTick; DWORD elapsedSec = elapsedTicks / 1000; if (_wasPrinted) { if (elapsedSec == _prevElapsedSec) { if ((UInt32)(tick - _prevTick) < _tickStep) return; if (_printedState.IsEqualTo((const CPercentPrinterState &)*this)) return; } } UInt64 cur = Completed; UInt64 total = Total; if (!UseBytesForPercents) { cur = Files; total = FilesTotal; } { _timeStr.Empty(); _timeStr = "Elapsed time: "; char s[40]; GetTimeString(elapsedSec, s); _timeStr += s; if (cur != 0) { UInt64 remainingTime = 0; if (cur < total) remainingTime = MyMultAndDiv(elapsedTicks, total - cur, cur); UInt64 remainingSec = remainingTime / 1000; _timeStr += " Remaining time: "; GetTimeString(remainingSec, s); _timeStr += s; } } { _perc.Empty(); char s[32]; unsigned size; { UInt64 val = 0; if (total != (UInt64)(Int64)-1 && total != 0) val = cur * 100 / Total; ConvertUInt64ToString(val, s); size = (unsigned)strlen(s); s[size++] = '%'; s[size] = 0; } unsigned len = size; while (len < kPercentsSize) len = kPercentsSize; len++; if (len < MaxLen) { unsigned numChars = MaxLen - len; unsigned filled = 0; if (total != (UInt64)(Int64)-1 && total != 0) filled = (unsigned)(cur * numChars / total); if (filled > numChars) filled = numChars; unsigned i = 0; for (i = 0; i < filled; i++) _perc += (char)(Byte)0xDB; // '='; for (; i < numChars; i++) _perc += (char)(Byte)0xB0; // '.'; } _perc.Add_Space(); while (size < kPercentsSize) { _perc.Add_Space(); size++; } _perc += s; } _files.Empty(); if (Files != 0 || FilesTotal != 0) { _files += "Files: "; char s[32]; // if (Files != 0) { ConvertUInt64ToString(Files, s); _files += s; } if (FilesTotal != 0) { _files += " / "; ConvertUInt64ToString(FilesTotal, s); _files += s; } } _sizesStr.Empty(); if (Total != 0) { _sizesStr += "Size: "; PrintSize_bytes_Smart(_sizesStr, Completed); if (Total != 0 && Total != (UInt64)(Int64)-1) { _sizesStr += " / "; PrintSize_bytes_Smart(_sizesStr, Total); } } _name1.Empty(); _name2.Empty(); if (!FileName.IsEmpty()) { _name1U.Empty(); _name2U.Empty(); /* if (_isDir) s1 = _filePath; else */ { const int slashPos = FileName.ReverseFind_PathSepar(); if (slashPos >= 0) { _name1U.SetFrom(FileName, (unsigned)(slashPos + 1)); _name2U = FileName.Ptr(slashPos + 1); } else _name2U = FileName; } ReduceString(_name1U, _name1); ReduceString(_name2U, _name2); } { const char *strings[] = { _title, _timeStr, _files, _sizesStr, Command, _name1, _name2, _perc }; NFar::g_StartupInfo.ShowMessage(FMSG_LEFTALIGN, NULL, strings, Z7_ARRAY_SIZE(strings), 0); } _wasPrinted = true; _printedState = *this; _prevTick = tick; _prevElapsedSec = elapsedSec; }