// 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);
}