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