// ProgressDialog2.cpp | |
#include "StdAfx.h" | |
#include "../../../Common/IntToString.h" | |
#include "../../../Common/StringConvert.h" | |
#include "../../../Windows/Control/Static.h" | |
#include "../../../Windows/ErrorMsg.h" | |
#include "ProgressDialog2.h" | |
#include "DialogSize.h" | |
#include "ProgressDialog2Res.h" | |
#include "../GUI/ExtractRes.h" | |
using namespace NWindows; | |
extern HINSTANCE g_hInstance; | |
static const UINT_PTR kTimerID = 3; | |
static const UINT kCloseMessage = WM_APP + 1; | |
// we can't use WM_USER, since WM_USER can be used by standard Windows procedure for Dialog | |
static const UINT kTimerElapse = | |
#ifdef UNDER_CE | |
500 | |
#else | |
100 | |
#endif | |
; | |
static const UINT kCreateDelay = | |
#ifdef UNDER_CE | |
2500 | |
#else | |
500 | |
#endif | |
; | |
static const DWORD kPauseSleepTime = 100; | |
#include "LangUtils.h" | |
#ifdef LANG | |
static const UInt32 kLangIDs[] = | |
{ | |
IDT_PROGRESS_ELAPSED, | |
IDT_PROGRESS_REMAINING, | |
IDT_PROGRESS_TOTAL, | |
IDT_PROGRESS_SPEED, | |
IDT_PROGRESS_PROCESSED, | |
IDT_PROGRESS_RATIO, | |
IDT_PROGRESS_ERRORS, | |
IDB_PROGRESS_BACKGROUND, | |
IDB_PAUSE | |
}; | |
static const UInt32 kLangIDs_Colon[] = | |
{ | |
IDT_PROGRESS_PACKED, | |
IDT_PROGRESS_FILES | |
}; | |
#endif | |
#define UNDEFINED_VAL ((UInt64)(Int64)-1) | |
#define INIT_AS_UNDEFINED(v) v = UNDEFINED_VAL; | |
#define IS_UNDEFINED_VAL(v) ((v) == UNDEFINED_VAL) | |
#define IS_DEFINED_VAL(v) ((v) != UNDEFINED_VAL) | |
CProgressSync::CProgressSync(): | |
_stopped(false), _paused(false), | |
_bytesProgressMode(true), | |
_totalBytes(UNDEFINED_VAL), _completedBytes(0), | |
_totalFiles(UNDEFINED_VAL), _curFiles(0), | |
_inSize(UNDEFINED_VAL), | |
_outSize(UNDEFINED_VAL), | |
_isDir(false) | |
{} | |
#define CHECK_STOP if (_stopped) return E_ABORT; if (!_paused) return S_OK; | |
#define CRITICAL_LOCK NSynchronization::CCriticalSectionLock lock(_cs); | |
bool CProgressSync::Get_Paused() | |
{ | |
CRITICAL_LOCK | |
return _paused; | |
} | |
HRESULT CProgressSync::CheckStop() | |
{ | |
for (;;) | |
{ | |
{ | |
CRITICAL_LOCK | |
CHECK_STOP | |
} | |
::Sleep(kPauseSleepTime); | |
} | |
} | |
HRESULT CProgressSync::ScanProgress(UInt64 numFiles, UInt64 totalSize, const UString &fileName, bool isDir) | |
{ | |
{ | |
CRITICAL_LOCK | |
_totalFiles = numFiles; | |
_totalBytes = totalSize; | |
_filePath = fileName; | |
_isDir = isDir; | |
// _completedBytes = 0; | |
CHECK_STOP | |
} | |
return CheckStop(); | |
} | |
void CProgressSync::Set_NumFilesTotal(UInt64 val) | |
{ | |
CRITICAL_LOCK | |
_totalFiles = val; | |
} | |
void CProgressSync::Set_NumBytesTotal(UInt64 val) | |
{ | |
CRITICAL_LOCK | |
_totalBytes = val; | |
} | |
void CProgressSync::Set_NumFilesCur(UInt64 val) | |
{ | |
CRITICAL_LOCK | |
_curFiles = val; | |
} | |
HRESULT CProgressSync::Set_NumBytesCur(const UInt64 *val) | |
{ | |
{ | |
CRITICAL_LOCK | |
if (val) | |
_completedBytes = *val; | |
CHECK_STOP | |
} | |
return CheckStop(); | |
} | |
HRESULT CProgressSync::Set_NumBytesCur(UInt64 val) | |
{ | |
{ | |
CRITICAL_LOCK | |
_completedBytes = val; | |
CHECK_STOP | |
} | |
return CheckStop(); | |
} | |
void CProgressSync::Set_Ratio(const UInt64 *inSize, const UInt64 *outSize) | |
{ | |
CRITICAL_LOCK | |
if (inSize) | |
_inSize = *inSize; | |
if (outSize) | |
_outSize = *outSize; | |
} | |
void CProgressSync::Set_TitleFileName(const UString &fileName) | |
{ | |
CRITICAL_LOCK | |
_titleFileName = fileName; | |
} | |
void CProgressSync::Set_Status(const UString &s) | |
{ | |
CRITICAL_LOCK | |
_status = s; | |
} | |
void CProgressSync::Set_FilePath(const UString &path, bool isDir) | |
{ | |
CRITICAL_LOCK | |
_filePath = path; | |
_isDir = isDir; | |
} | |
void CProgressSync::AddError_Message(const wchar_t *message) | |
{ | |
CRITICAL_LOCK | |
Messages.Add(message); | |
} | |
void CProgressSync::AddError_Message_Name(const wchar_t *message, const wchar_t *name) | |
{ | |
UString s; | |
if (name && *name != 0) | |
s += name; | |
if (message && *message != 0 ) | |
{ | |
if (!s.IsEmpty()) | |
s += L'\n'; | |
s += message; | |
if (!s.IsEmpty() && s.Back() == L'\n') | |
s.DeleteBack(); | |
} | |
AddError_Message(s); | |
} | |
void CProgressSync::AddError_Code_Name(DWORD systemError, const wchar_t *name) | |
{ | |
UString s = NError::MyFormatMessage(systemError); | |
if (systemError == 0) | |
s = L"Error"; | |
AddError_Message_Name(s, name); | |
} | |
CProgressDialog::CProgressDialog(): _timer(0), CompressingMode(true), MainWindow(0) | |
{ | |
_isDir = false; | |
_numMessages = 0; | |
IconID = -1; | |
MessagesDisplayed = false; | |
_wasCreated = false; | |
_needClose = false; | |
_inCancelMessageBox = false; | |
_externalCloseMessageWasReceived = false; | |
_numPostedMessages = 0; | |
_numAutoSizeMessages = 0; | |
_errorsWereDisplayed = false; | |
_waitCloseByCancelButton = false; | |
_cancelWasPressed = false; | |
ShowCompressionInfo = true; | |
WaitMode = false; | |
if (_dialogCreatedEvent.Create() != S_OK) | |
throw 1334987; | |
if (_createDialogEvent.Create() != S_OK) | |
throw 1334987; | |
#ifdef __ITaskbarList3_INTERFACE_DEFINED__ | |
CoCreateInstance(CLSID_TaskbarList, NULL, CLSCTX_INPROC_SERVER, IID_ITaskbarList3, (void**)&_taskbarList); | |
if (_taskbarList) | |
_taskbarList->HrInit(); | |
#endif | |
} | |
#ifndef _SFX | |
CProgressDialog::~CProgressDialog() | |
{ | |
#ifdef __ITaskbarList3_INTERFACE_DEFINED__ | |
SetTaskbarProgressState(TBPF_NOPROGRESS); | |
#endif | |
AddToTitle(L""); | |
} | |
void CProgressDialog::AddToTitle(LPCWSTR s) | |
{ | |
if (MainWindow != 0) | |
{ | |
CWindow window(MainWindow); | |
window.SetText((UString)s + MainTitle); | |
} | |
} | |
#endif | |
void CProgressDialog::SetTaskbarProgressState() | |
{ | |
#ifdef __ITaskbarList3_INTERFACE_DEFINED__ | |
if (_taskbarList && _hwndForTaskbar) | |
{ | |
TBPFLAG tbpFlags; | |
if (Sync.Get_Paused()) | |
tbpFlags = TBPF_PAUSED; | |
else | |
tbpFlags = _errorsWereDisplayed ? TBPF_ERROR: TBPF_NORMAL; | |
SetTaskbarProgressState(tbpFlags); | |
} | |
#endif | |
} | |
static const unsigned kTitleFileNameSizeLimit = 36; | |
static const unsigned kCurrentFileNameSizeLimit = 82; | |
static void ReduceString(UString &s, unsigned size) | |
{ | |
if (s.Len() <= size) | |
return; | |
s.Delete(size / 2, s.Len() - size); | |
s.Insert(size / 2, L" ... "); | |
} | |
void CProgressDialog::EnableErrorsControls(bool enable) | |
{ | |
ShowItem_Bool(IDT_PROGRESS_ERRORS, enable); | |
ShowItem_Bool(IDT_PROGRESS_ERRORS_VAL, enable); | |
ShowItem_Bool(IDL_PROGRESS_MESSAGES, enable); | |
} | |
bool CProgressDialog::OnInit() | |
{ | |
_hwndForTaskbar = MainWindow; | |
if (!_hwndForTaskbar) | |
_hwndForTaskbar = GetParent(); | |
if (!_hwndForTaskbar) | |
_hwndForTaskbar = *this; | |
INIT_AS_UNDEFINED(_progressBar_Range); | |
INIT_AS_UNDEFINED(_progressBar_Pos); | |
INIT_AS_UNDEFINED(_prevPercentValue); | |
INIT_AS_UNDEFINED(_prevElapsedSec); | |
INIT_AS_UNDEFINED(_prevRemainingSec); | |
INIT_AS_UNDEFINED(_prevSpeed); | |
_prevSpeed_MoveBits = 0; | |
_prevTime = ::GetTickCount(); | |
_elapsedTime = 0; | |
INIT_AS_UNDEFINED(_totalBytes_Prev); | |
INIT_AS_UNDEFINED(_processed_Prev); | |
INIT_AS_UNDEFINED(_packed_Prev); | |
INIT_AS_UNDEFINED(_ratio_Prev); | |
_filesStr_Prev.Empty(); | |
_foreground = true; | |
m_ProgressBar.Attach(GetItem(IDC_PROGRESS1)); | |
_messageList.Attach(GetItem(IDL_PROGRESS_MESSAGES)); | |
_messageList.SetUnicodeFormat(); | |
_wasCreated = true; | |
_dialogCreatedEvent.Set(); | |
#ifdef LANG | |
LangSetDlgItems(*this, kLangIDs, ARRAY_SIZE(kLangIDs)); | |
LangSetDlgItems_Colon(*this, kLangIDs_Colon, ARRAY_SIZE(kLangIDs_Colon)); | |
#endif | |
CWindow window(GetItem(IDB_PROGRESS_BACKGROUND)); | |
window.GetText(_background_String); | |
_backgrounded_String = _background_String; | |
_backgrounded_String.RemoveChar(L'&'); | |
window = GetItem(IDB_PAUSE); | |
window.GetText(_pause_String); | |
LangString(IDS_PROGRESS_FOREGROUND, _foreground_String); | |
LangString(IDS_CONTINUE, _continue_String); | |
LangString(IDS_PROGRESS_PAUSED, _paused_String); | |
SetText(_title); | |
SetPauseText(); | |
SetPriorityText(); | |
_messageList.InsertColumn(0, L"", 30); | |
_messageList.InsertColumn(1, L"", 600); | |
_messageList.SetColumnWidthAuto(0); | |
_messageList.SetColumnWidthAuto(1); | |
EnableErrorsControls(false); | |
GetItemSizes(IDCANCEL, _buttonSizeX, _buttonSizeY); | |
_numReduceSymbols = kCurrentFileNameSizeLimit; | |
NormalizeSize(true); | |
if (!ShowCompressionInfo) | |
{ | |
HideItem(IDT_PROGRESS_PACKED); | |
HideItem(IDT_PROGRESS_PACKED_VAL); | |
HideItem(IDT_PROGRESS_RATIO); | |
HideItem(IDT_PROGRESS_RATIO_VAL); | |
} | |
if (IconID >= 0) | |
{ | |
HICON icon = LoadIcon(g_hInstance, MAKEINTRESOURCE(IconID)); | |
// SetIcon(ICON_SMALL, icon); | |
SetIcon(ICON_BIG, icon); | |
} | |
_timer = SetTimer(kTimerID, kTimerElapse); | |
#ifdef UNDER_CE | |
Foreground(); | |
#endif | |
CheckNeedClose(); | |
SetTaskbarProgressState(); | |
return CModalDialog::OnInit(); | |
} | |
static const UINT kIDs[] = | |
{ | |
IDT_PROGRESS_ELAPSED, IDT_PROGRESS_ELAPSED_VAL, | |
IDT_PROGRESS_REMAINING, IDT_PROGRESS_REMAINING_VAL, | |
IDT_PROGRESS_FILES, IDT_PROGRESS_FILES_VAL, | |
IDT_PROGRESS_RATIO, IDT_PROGRESS_RATIO_VAL, | |
IDT_PROGRESS_ERRORS, IDT_PROGRESS_ERRORS_VAL, | |
IDT_PROGRESS_TOTAL, IDT_PROGRESS_TOTAL_VAL, | |
IDT_PROGRESS_SPEED, IDT_PROGRESS_SPEED_VAL, | |
IDT_PROGRESS_PROCESSED, IDT_PROGRESS_PROCESSED_VAL, | |
IDT_PROGRESS_PACKED, IDT_PROGRESS_PACKED_VAL | |
}; | |
bool CProgressDialog::OnSize(WPARAM /* wParam */, int xSize, int ySize) | |
{ | |
int sY; | |
int sStep; | |
int mx, my; | |
{ | |
RECT r; | |
GetClientRectOfItem(IDT_PROGRESS_ELAPSED, r); | |
mx = r.left; | |
my = r.top; | |
sY = RECT_SIZE_Y(r); | |
GetClientRectOfItem(IDT_PROGRESS_REMAINING, r); | |
sStep = r.top - my; | |
} | |
InvalidateRect(NULL); | |
int xSizeClient = xSize - mx * 2; | |
{ | |
int i; | |
for (i = 800; i > 40; i = i * 9 / 10) | |
if (Units_To_Pixels_X(i) <= xSizeClient) | |
break; | |
_numReduceSymbols = i / 4; | |
} | |
int yPos = ySize - my - _buttonSizeY; | |
ChangeSubWindowSizeX(GetItem(IDT_PROGRESS_STATUS), xSize - mx * 2); | |
ChangeSubWindowSizeX(GetItem(IDT_PROGRESS_FILE_NAME), xSize - mx * 2); | |
ChangeSubWindowSizeX(GetItem(IDC_PROGRESS1), xSize - mx * 2); | |
int bSizeX = _buttonSizeX; | |
int mx2 = mx; | |
for (;; mx2--) | |
{ | |
int bSize2 = bSizeX * 3 + mx2 * 2; | |
if (bSize2 <= xSizeClient) | |
break; | |
if (mx2 < 5) | |
{ | |
bSizeX = (xSizeClient - mx2 * 2) / 3; | |
break; | |
} | |
} | |
if (bSizeX < 2) | |
bSizeX = 2; | |
{ | |
RECT r; | |
GetClientRectOfItem(IDL_PROGRESS_MESSAGES, r); | |
int y = r.top; | |
int ySize2 = yPos - my - y; | |
const int kMinYSize = _buttonSizeY + _buttonSizeY * 3 / 4; | |
int xx = xSize - mx * 2; | |
if (ySize2 < kMinYSize) | |
{ | |
ySize2 = kMinYSize; | |
if (xx > bSizeX * 2) | |
xx -= bSizeX; | |
} | |
_messageList.Move(mx, y, xx, ySize2); | |
} | |
{ | |
int xPos = xSize - mx; | |
xPos -= bSizeX; | |
MoveItem(IDCANCEL, xPos, yPos, bSizeX, _buttonSizeY); | |
xPos -= (mx2 + bSizeX); | |
MoveItem(IDB_PAUSE, xPos, yPos, bSizeX, _buttonSizeY); | |
xPos -= (mx2 + bSizeX); | |
MoveItem(IDB_PROGRESS_BACKGROUND, xPos, yPos, bSizeX, _buttonSizeY); | |
} | |
int valueSize; | |
int labelSize; | |
int padSize; | |
labelSize = Units_To_Pixels_X(MY_PROGRESS_LABEL_UNITS_MIN); | |
valueSize = Units_To_Pixels_X(MY_PROGRESS_VAL_UNITS); | |
padSize = Units_To_Pixels_X(MY_PROGRESS_PAD_UNITS); | |
int requiredSize = (labelSize + valueSize) * 2 + padSize; | |
int gSize; | |
{ | |
if (requiredSize < xSizeClient) | |
{ | |
int incr = (xSizeClient - requiredSize) / 3; | |
labelSize += incr; | |
} | |
else | |
labelSize = (xSizeClient - valueSize * 2 - padSize) / 2; | |
if (labelSize < 0) | |
labelSize = 0; | |
gSize = labelSize + valueSize; | |
padSize = xSizeClient - gSize * 2; | |
} | |
labelSize = gSize - valueSize; | |
yPos = my; | |
for (int i = 0; i < ARRAY_SIZE(kIDs); i += 2) | |
{ | |
int x = mx; | |
const int kNumColumn1Items = 5 * 2; | |
if (i >= kNumColumn1Items) | |
{ | |
if (i == kNumColumn1Items) | |
yPos = my; | |
x = mx + gSize + padSize; | |
} | |
MoveItem(kIDs[i], x, yPos, labelSize, sY); | |
MoveItem(kIDs[i + 1], x + labelSize, yPos, valueSize, sY); | |
yPos += sStep; | |
} | |
return false; | |
} | |
void CProgressDialog::OnCancel() { Sync.Set_Stopped(true); } | |
void CProgressDialog::OnOK() { } | |
void CProgressDialog::SetProgressRange(UInt64 range) | |
{ | |
if (range == _progressBar_Range) | |
return; | |
_progressBar_Range = range; | |
INIT_AS_UNDEFINED(_progressBar_Pos); | |
_progressConv.Init(range); | |
m_ProgressBar.SetRange32(0, _progressConv.Count(range)); | |
} | |
void CProgressDialog::SetProgressPos(UInt64 pos) | |
{ | |
if (pos >= _progressBar_Range || | |
pos <= _progressBar_Pos || | |
pos - _progressBar_Pos >= (_progressBar_Range >> 10)) | |
{ | |
m_ProgressBar.SetPos(_progressConv.Count(pos)); | |
#ifdef __ITaskbarList3_INTERFACE_DEFINED__ | |
if (_taskbarList && _hwndForTaskbar) | |
_taskbarList->SetProgressValue(_hwndForTaskbar, pos, _progressBar_Range); | |
#endif | |
_progressBar_Pos = pos; | |
} | |
} | |
#define UINT_TO_STR_2(val) { s[0] = (wchar_t)('0' + (val) / 10); s[1] = (wchar_t)('0' + (val) % 10); s += 2; } | |
void GetTimeString(UInt64 timeValue, wchar_t *s) | |
{ | |
UInt64 hours = timeValue / 3600; | |
UInt32 seconds = (UInt32)(timeValue - hours * 3600); | |
UInt32 minutes = seconds / 60; | |
seconds %= 60; | |
if (hours > 99) | |
{ | |
ConvertUInt64ToString(hours, s); | |
for (; *s != 0; s++); | |
} | |
else | |
{ | |
UInt32 hours32 = (UInt32)hours; | |
UINT_TO_STR_2(hours32); | |
} | |
*s++ = ':'; UINT_TO_STR_2(minutes); | |
*s++ = ':'; UINT_TO_STR_2(seconds); | |
*s = 0; | |
} | |
static void ConvertSizeToString(UInt64 v, wchar_t *s) | |
{ | |
Byte c = 0; | |
if (v >= ((UInt64)100000 << 20)) { v >>= 30; c = 'G'; } | |
else if (v >= ((UInt64)100000 << 10)) { v >>= 20; c = 'M'; } | |
else if (v >= ((UInt64)100000 << 0)) { v >>= 10; c = 'K'; } | |
ConvertUInt64ToString(v, s); | |
if (c != 0) | |
{ | |
s += MyStringLen(s); | |
*s++ = ' '; | |
*s++ = c; | |
*s++ = 0; | |
} | |
} | |
void CProgressDialog::ShowSize(int id, UInt64 val, UInt64 &prev) | |
{ | |
if (val == prev) | |
return; | |
prev = val; | |
wchar_t s[40]; | |
s[0] = 0; | |
if (IS_DEFINED_VAL(val)) | |
ConvertSizeToString(val, s); | |
SetItemText(id, s); | |
} | |
static void GetChangedString(const UString &newStr, UString &prevStr, bool &hasChanged) | |
{ | |
hasChanged = !(prevStr == newStr); | |
if (hasChanged) | |
prevStr = newStr; | |
} | |
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; | |
} | |
void CProgressDialog::UpdateStatInfo(bool showAll) | |
{ | |
UInt64 total, completed, totalFiles, completedFiles, inSize, outSize; | |
bool bytesProgressMode; | |
bool titleFileName_Changed; | |
bool curFilePath_Changed; | |
bool status_Changed; | |
unsigned numErrors; | |
{ | |
NSynchronization::CCriticalSectionLock lock(Sync._cs); | |
total = Sync._totalBytes; | |
completed = Sync._completedBytes; | |
totalFiles = Sync._totalFiles; | |
completedFiles = Sync._curFiles; | |
inSize = Sync._inSize; | |
outSize = Sync._outSize; | |
bytesProgressMode = Sync._bytesProgressMode; | |
GetChangedString(Sync._titleFileName, _titleFileName, titleFileName_Changed); | |
GetChangedString(Sync._filePath, _filePath, curFilePath_Changed); | |
GetChangedString(Sync._status, _status, status_Changed); | |
if (_isDir != Sync._isDir) | |
{ | |
curFilePath_Changed = true; | |
_isDir = Sync._isDir; | |
} | |
numErrors = Sync.Messages.Size(); | |
} | |
UInt32 curTime = ::GetTickCount(); | |
{ | |
UInt64 progressTotal = bytesProgressMode ? total : totalFiles; | |
UInt64 progressCompleted = bytesProgressMode ? completed : completedFiles; | |
if (IS_UNDEFINED_VAL(progressTotal)) | |
{ | |
// SetPos(0); | |
// SetRange(progressCompleted); | |
} | |
else | |
{ | |
if (_progressBar_Pos != 0 || progressCompleted != 0 || | |
(_progressBar_Range == 0 && progressTotal != 0)) | |
{ | |
SetProgressRange(progressTotal); | |
SetProgressPos(progressCompleted); | |
} | |
} | |
} | |
ShowSize(IDT_PROGRESS_TOTAL_VAL, total, _totalBytes_Prev); | |
_elapsedTime += (curTime - _prevTime); | |
_prevTime = curTime; | |
UInt64 elapsedSec = _elapsedTime / 1000; | |
bool elapsedChanged = false; | |
if (elapsedSec != _prevElapsedSec) | |
{ | |
_prevElapsedSec = elapsedSec; | |
elapsedChanged = true; | |
wchar_t s[40]; | |
GetTimeString(elapsedSec, s); | |
SetItemText(IDT_PROGRESS_ELAPSED_VAL, s); | |
} | |
bool needSetTitle = false; | |
if (elapsedChanged || showAll) | |
{ | |
if (numErrors > _numPostedMessages) | |
{ | |
UpdateMessagesDialog(); | |
wchar_t s[32]; | |
ConvertUInt64ToString(numErrors, s); | |
SetItemText(IDT_PROGRESS_ERRORS_VAL, s); | |
if (!_errorsWereDisplayed) | |
{ | |
_errorsWereDisplayed = true; | |
EnableErrorsControls(true); | |
SetTaskbarProgressState(); | |
} | |
} | |
if (completed != 0) | |
{ | |
if (IS_UNDEFINED_VAL(total)) | |
{ | |
if (IS_DEFINED_VAL(_prevRemainingSec)) | |
{ | |
INIT_AS_UNDEFINED(_prevRemainingSec); | |
SetItemText(IDT_PROGRESS_REMAINING_VAL, L""); | |
} | |
} | |
else | |
{ | |
UInt64 remainingTime = 0; | |
if (completed < total) | |
remainingTime = MyMultAndDiv(_elapsedTime, total - completed, completed); | |
UInt64 remainingSec = remainingTime / 1000; | |
if (remainingSec != _prevRemainingSec) | |
{ | |
_prevRemainingSec = remainingSec; | |
wchar_t s[40]; | |
GetTimeString(remainingSec, s); | |
SetItemText(IDT_PROGRESS_REMAINING_VAL, s); | |
} | |
} | |
{ | |
UInt64 elapsedTime = (_elapsedTime == 0) ? 1 : _elapsedTime; | |
UInt64 v = (completed * 1000) / elapsedTime; | |
Byte c = 0; | |
unsigned moveBits = 0; | |
if (v >= ((UInt64)10000 << 10)) { moveBits = 20; c = 'M'; } | |
else if (v >= ((UInt64)10000 << 0)) { moveBits = 10; c = 'K'; } | |
v >>= moveBits; | |
if (moveBits != _prevSpeed_MoveBits || v != _prevSpeed) | |
{ | |
_prevSpeed_MoveBits = moveBits; | |
_prevSpeed = v; | |
wchar_t s[40]; | |
ConvertUInt64ToString(v, s); | |
unsigned pos = MyStringLen(s); | |
s[pos++] = ' '; | |
if (moveBits != 0) | |
s[pos++] = c; | |
s[pos++] = 'B'; | |
s[pos++] = '/'; | |
s[pos++] = 's'; | |
s[pos++] = 0; | |
SetItemText(IDT_PROGRESS_SPEED_VAL, s); | |
} | |
} | |
} | |
{ | |
UInt64 percent = 0; | |
{ | |
if (IS_DEFINED_VAL(total)) | |
{ | |
percent = completed * 100; | |
if (total != 0) | |
percent /= total; | |
} | |
} | |
if (percent != _prevPercentValue) | |
{ | |
_prevPercentValue = percent; | |
needSetTitle = true; | |
} | |
} | |
{ | |
wchar_t s[64]; | |
ConvertUInt64ToString(completedFiles, s); | |
if (IS_DEFINED_VAL(totalFiles)) | |
{ | |
wcscat(s, L" / "); | |
ConvertUInt64ToString(totalFiles, s + wcslen(s)); | |
} | |
if (_filesStr_Prev != s) | |
{ | |
_filesStr_Prev = s; | |
SetItemText(IDT_PROGRESS_FILES_VAL, s); | |
} | |
} | |
const UInt64 packSize = CompressingMode ? outSize : inSize; | |
const UInt64 unpackSize = CompressingMode ? inSize : outSize; | |
if (IS_UNDEFINED_VAL(unpackSize) && | |
IS_UNDEFINED_VAL(packSize)) | |
{ | |
ShowSize(IDT_PROGRESS_PROCESSED_VAL, completed, _processed_Prev); | |
ShowSize(IDT_PROGRESS_PACKED_VAL, UNDEFINED_VAL, _packed_Prev); | |
} | |
else | |
{ | |
ShowSize(IDT_PROGRESS_PROCESSED_VAL, unpackSize, _processed_Prev); | |
ShowSize(IDT_PROGRESS_PACKED_VAL, packSize, _packed_Prev); | |
if (IS_DEFINED_VAL(packSize) && | |
IS_DEFINED_VAL(unpackSize) && | |
unpackSize != 0) | |
{ | |
wchar_t s[32]; | |
UInt64 ratio = packSize * 100 / unpackSize; | |
if (_ratio_Prev != ratio) | |
{ | |
_ratio_Prev = ratio; | |
ConvertUInt64ToString(ratio, s); | |
wcscat(s, L"%"); | |
SetItemText(IDT_PROGRESS_RATIO_VAL, s); | |
} | |
} | |
} | |
} | |
if (needSetTitle || titleFileName_Changed) | |
SetTitleText(); | |
if (status_Changed) | |
{ | |
UString s = _status; | |
ReduceString(s, _numReduceSymbols); | |
SetItemText(IDT_PROGRESS_STATUS, _status); | |
} | |
if (curFilePath_Changed) | |
{ | |
UString s1, s2; | |
if (_isDir) | |
s1 = _filePath; | |
else | |
{ | |
int slashPos = _filePath.ReverseFind(WCHAR_PATH_SEPARATOR); | |
if (slashPos >= 0) | |
{ | |
s1.SetFrom(_filePath, slashPos + 1); | |
s2 = _filePath.Ptr(slashPos + 1); | |
} | |
else | |
s2 = _filePath; | |
} | |
ReduceString(s1, _numReduceSymbols); | |
ReduceString(s2, _numReduceSymbols); | |
s1 += L'\n'; | |
s1 += s2; | |
SetItemText(IDT_PROGRESS_FILE_NAME, s1); | |
} | |
} | |
bool CProgressDialog::OnTimer(WPARAM /* timerID */, LPARAM /* callback */) | |
{ | |
if (Sync.Get_Paused()) | |
return true; | |
CheckNeedClose(); | |
UpdateStatInfo(false); | |
return true; | |
} | |
struct CWaitCursor | |
{ | |
HCURSOR _waitCursor; | |
HCURSOR _oldCursor; | |
CWaitCursor() | |
{ | |
_waitCursor = LoadCursor(NULL, IDC_WAIT); | |
if (_waitCursor != NULL) | |
_oldCursor = SetCursor(_waitCursor); | |
} | |
~CWaitCursor() | |
{ | |
if (_waitCursor != NULL) | |
SetCursor(_oldCursor); | |
} | |
}; | |
INT_PTR CProgressDialog::Create(const UString &title, NWindows::CThread &thread, HWND wndParent) | |
{ | |
INT_PTR res = 0; | |
try | |
{ | |
if (WaitMode) | |
{ | |
CWaitCursor waitCursor; | |
HANDLE h[] = { thread, _createDialogEvent }; | |
WRes res = WaitForMultipleObjects(ARRAY_SIZE(h), h, FALSE, kCreateDelay); | |
if (res == WAIT_OBJECT_0 && !Sync.ThereIsMessage()) | |
return 0; | |
} | |
_title = title; | |
BIG_DIALOG_SIZE(360, 192); | |
res = CModalDialog::Create(SIZED_DIALOG(IDD_PROGRESS), wndParent); | |
} | |
catch(...) | |
{ | |
_wasCreated = true; | |
_dialogCreatedEvent.Set(); | |
res = res; | |
} | |
thread.Wait(); | |
if (!MessagesDisplayed) | |
MessageBoxW(wndParent, L"Progress Error", L"7-Zip", MB_ICONERROR); | |
return res; | |
} | |
bool CProgressDialog::OnExternalCloseMessage() | |
{ | |
// it doesn't work if there is MessageBox. | |
#ifdef __ITaskbarList3_INTERFACE_DEFINED__ | |
SetTaskbarProgressState(TBPF_NOPROGRESS); | |
#endif | |
// AddToTitle(L"Finished "); | |
// SetText(L"Finished2 "); | |
UpdateStatInfo(true); | |
SetItemText(IDCANCEL, LangString(IDS_CLOSE)); | |
::SendMessage(GetItem(IDCANCEL), BM_SETSTYLE, BS_DEFPUSHBUTTON, MAKELPARAM(TRUE, 0)); | |
HideItem(IDB_PROGRESS_BACKGROUND); | |
HideItem(IDB_PAUSE); | |
bool thereAreMessages; | |
CProgressFinalMessage fm; | |
{ | |
NSynchronization::CCriticalSectionLock lock(Sync._cs); | |
thereAreMessages = !Sync.Messages.IsEmpty(); | |
fm = Sync.FinalMessage; | |
} | |
if (!fm.ErrorMessage.Message.IsEmpty()) | |
{ | |
MessagesDisplayed = true; | |
if (fm.ErrorMessage.Title.IsEmpty()) | |
fm.ErrorMessage.Title = L"7-Zip"; | |
MessageBoxW(*this, fm.ErrorMessage.Message, fm.ErrorMessage.Title, MB_ICONERROR); | |
} | |
else if (!thereAreMessages) | |
{ | |
MessagesDisplayed = true; | |
if (!fm.OkMessage.Message.IsEmpty()) | |
{ | |
if (fm.OkMessage.Title.IsEmpty()) | |
fm.OkMessage.Title = L"7-Zip"; | |
MessageBoxW(*this, fm.OkMessage.Message, fm.OkMessage.Title, MB_OK); | |
} | |
} | |
if (thereAreMessages && !_cancelWasPressed) | |
{ | |
_waitCloseByCancelButton = true; | |
UpdateMessagesDialog(); | |
return true; | |
} | |
End(0); | |
return true; | |
} | |
bool CProgressDialog::OnMessage(UINT message, WPARAM wParam, LPARAM lParam) | |
{ | |
switch (message) | |
{ | |
case kCloseMessage: | |
{ | |
KillTimer(_timer); | |
_timer = 0; | |
if (_inCancelMessageBox) | |
{ | |
_externalCloseMessageWasReceived = true; | |
break; | |
} | |
return OnExternalCloseMessage(); | |
} | |
/* | |
case WM_SETTEXT: | |
{ | |
if (_timer == 0) | |
return true; | |
break; | |
} | |
*/ | |
} | |
return CModalDialog::OnMessage(message, wParam, lParam); | |
} | |
void CProgressDialog::SetTitleText() | |
{ | |
UString s; | |
if (Sync.Get_Paused()) | |
{ | |
s += _paused_String; | |
s += L' '; | |
} | |
if (IS_DEFINED_VAL(_prevPercentValue)) | |
{ | |
wchar_t temp[32]; | |
ConvertUInt64ToString(_prevPercentValue, temp); | |
s += temp; | |
s += L'%'; | |
} | |
if (!_foreground) | |
{ | |
s += L' '; | |
s += _backgrounded_String; | |
} | |
s += L' '; | |
#ifndef _SFX | |
{ | |
unsigned len = s.Len(); | |
s += MainAddTitle; | |
AddToTitle(s); | |
s.DeleteFrom(len); | |
} | |
#endif | |
s += _title; | |
if (!_titleFileName.IsEmpty()) | |
{ | |
UString fileName = _titleFileName; | |
ReduceString(fileName, kTitleFileNameSizeLimit); | |
s += L' '; | |
s += fileName; | |
} | |
SetText(s); | |
} | |
void CProgressDialog::SetPauseText() | |
{ | |
SetItemText(IDB_PAUSE, Sync.Get_Paused() ? _continue_String : _pause_String); | |
SetTitleText(); | |
} | |
void CProgressDialog::OnPauseButton() | |
{ | |
bool paused = !Sync.Get_Paused(); | |
Sync.Set_Paused(paused); | |
UInt32 curTime = ::GetTickCount(); | |
if (paused) | |
_elapsedTime += (curTime - _prevTime); | |
SetTaskbarProgressState(); | |
_prevTime = curTime; | |
SetPauseText(); | |
} | |
void CProgressDialog::SetPriorityText() | |
{ | |
SetItemText(IDB_PROGRESS_BACKGROUND, _foreground ? | |
_background_String : | |
_foreground_String); | |
SetTitleText(); | |
} | |
void CProgressDialog::OnPriorityButton() | |
{ | |
_foreground = !_foreground; | |
#ifndef UNDER_CE | |
SetPriorityClass(GetCurrentProcess(), _foreground ? NORMAL_PRIORITY_CLASS: IDLE_PRIORITY_CLASS); | |
#endif | |
SetPriorityText(); | |
} | |
void CProgressDialog::AddMessageDirect(LPCWSTR message, bool needNumber) | |
{ | |
int itemIndex = _messageList.GetItemCount(); | |
wchar_t sz[16]; | |
sz[0] = 0; | |
if (needNumber) | |
ConvertUInt32ToString(_numMessages + 1, sz); | |
_messageList.InsertItem(itemIndex, sz); | |
_messageList.SetSubItem(itemIndex, 1, message); | |
} | |
void CProgressDialog::AddMessage(LPCWSTR message) | |
{ | |
UString s = message; | |
bool needNumber = true; | |
while (!s.IsEmpty()) | |
{ | |
int pos = s.Find(L'\n'); | |
if (pos < 0) | |
break; | |
AddMessageDirect(s.Left(pos), needNumber); | |
needNumber = false; | |
s.DeleteFrontal(pos + 1); | |
} | |
AddMessageDirect(s, needNumber); | |
_numMessages++; | |
} | |
static unsigned GetNumDigits(UInt32 val) | |
{ | |
unsigned i; | |
for (i = 0; val >= 10; i++) | |
val /= 10; | |
return i; | |
} | |
void CProgressDialog::UpdateMessagesDialog() | |
{ | |
UStringVector messages; | |
{ | |
NSynchronization::CCriticalSectionLock lock(Sync._cs); | |
unsigned num = Sync.Messages.Size(); | |
if (num > _numPostedMessages) | |
{ | |
messages.ClearAndReserve(num - _numPostedMessages); | |
for (unsigned i = _numPostedMessages; i < num; i++) | |
messages.AddInReserved(Sync.Messages[i]); | |
_numPostedMessages = num; | |
} | |
} | |
if (!messages.IsEmpty()) | |
{ | |
FOR_VECTOR (i, messages) | |
AddMessage(messages[i]); | |
if (_numAutoSizeMessages < 256 || GetNumDigits(_numPostedMessages) > GetNumDigits(_numAutoSizeMessages)) | |
{ | |
_messageList.SetColumnWidthAuto(0); | |
_messageList.SetColumnWidthAuto(1); | |
_numAutoSizeMessages = _numPostedMessages; | |
} | |
} | |
} | |
bool CProgressDialog::OnButtonClicked(int buttonID, HWND buttonHWND) | |
{ | |
switch (buttonID) | |
{ | |
// case IDOK: // if IDCANCEL is not DEFPUSHBUTTON | |
case IDCANCEL: | |
{ | |
if (_waitCloseByCancelButton) | |
{ | |
MessagesDisplayed = true; | |
End(IDCLOSE); | |
break; | |
} | |
bool paused = Sync.Get_Paused(); | |
if (!paused) | |
OnPauseButton(); | |
_inCancelMessageBox = true; | |
int res = ::MessageBoxW(*this, LangString(IDS_PROGRESS_ASK_CANCEL), _title, MB_YESNOCANCEL); | |
_inCancelMessageBox = false; | |
if (!paused) | |
OnPauseButton(); | |
if (res == IDCANCEL || res == IDNO) | |
{ | |
if (_externalCloseMessageWasReceived) | |
OnExternalCloseMessage(); | |
return true; | |
} | |
_cancelWasPressed = true; | |
MessagesDisplayed = true; | |
break; | |
} | |
case IDB_PAUSE: | |
OnPauseButton(); | |
return true; | |
case IDB_PROGRESS_BACKGROUND: | |
OnPriorityButton(); | |
return true; | |
} | |
return CModalDialog::OnButtonClicked(buttonID, buttonHWND); | |
} | |
void CProgressDialog::CheckNeedClose() | |
{ | |
if (_needClose) | |
{ | |
PostMessage(kCloseMessage); | |
_needClose = false; | |
} | |
} | |
void CProgressDialog::ProcessWasFinished() | |
{ | |
// Set Window title here. | |
if (!WaitMode) | |
WaitCreating(); | |
if (_wasCreated) | |
PostMessage(kCloseMessage); | |
else | |
_needClose = true; | |
} | |
HRESULT CProgressThreadVirt::Create(const UString &title, HWND parentWindow) | |
{ | |
NWindows::CThread thread; | |
RINOK(thread.Create(MyThreadFunction, this)); | |
ProgressDialog.Create(title, thread, parentWindow); | |
return S_OK; | |
} | |
static void AddMessageToString(UString &dest, const UString &src) | |
{ | |
if (!src.IsEmpty()) | |
{ | |
if (!dest.IsEmpty()) | |
dest += L'\n'; | |
dest += src; | |
} | |
} | |
void CProgressThreadVirt::Process() | |
{ | |
CProgressCloser closer(ProgressDialog); | |
UString m; | |
try { Result = ProcessVirt(); } | |
catch(const wchar_t *s) { m = s; } | |
catch(const UString &s) { m = s; } | |
catch(const char *s) { m = GetUnicodeString(s); } | |
catch(int v) | |
{ | |
wchar_t s[16]; | |
ConvertUInt32ToString(v, s); | |
m = L"Error #"; | |
m += s; | |
} | |
catch(...) { m = L"Error"; } | |
if (Result != E_ABORT) | |
{ | |
if (m.IsEmpty() && Result != S_OK) | |
m = HResultToMessage(Result); | |
} | |
AddMessageToString(m, FinalMessage.ErrorMessage.Message); | |
AddMessageToString(m, fs2us(ErrorPath1)); | |
AddMessageToString(m, fs2us(ErrorPath2)); | |
CProgressSync &sync = ProgressDialog.Sync; | |
NSynchronization::CCriticalSectionLock lock(sync._cs); | |
if (m.IsEmpty()) | |
{ | |
if (!FinalMessage.OkMessage.Message.IsEmpty()) | |
sync.FinalMessage.OkMessage = FinalMessage.OkMessage; | |
} | |
else | |
{ | |
sync.FinalMessage.ErrorMessage.Message = m; | |
if (Result == S_OK) | |
Result = E_FAIL; | |
} | |
} | |
UString HResultToMessage(HRESULT errorCode) | |
{ | |
if (errorCode == E_OUTOFMEMORY) | |
return LangString(IDS_MEM_ERROR); | |
else | |
return NError::MyFormatMessage(errorCode); | |
} |