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