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