tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

ProgressDialog2.cpp (31886B)


      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 }