The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
// ProgressDialog.h

#ifndef __PROGRESS_DIALOG_H
#define __PROGRESS_DIALOG_H

#include "Windows/Synchronization.h"
#include "Windows/Thread.h"

#include "Windows/Control/Dialog.h"
#include "Windows/Control/ProgressBar.h"

#include "ProgressDialogRes.h"

class CProgressSync
{
  NWindows::NSynchronization::CCriticalSection _cs;
  bool _stopped;
  bool _paused;
  UInt64 _total;
  UInt64 _completed;
public:
  CProgressSync(): _stopped(false), _paused(false), _total(1), _completed(0) {}

  HRESULT ProcessStopAndPause();
  bool GetStopped()
  {
    NWindows::NSynchronization::CCriticalSectionLock lock(_cs);
    return _stopped;
  }
  void SetStopped(bool value)
  {
    NWindows::NSynchronization::CCriticalSectionLock lock(_cs);
    _stopped = value;
  }
  bool GetPaused()
  {
    NWindows::NSynchronization::CCriticalSectionLock lock(_cs);
    return _paused;
  }
  void SetPaused(bool value)
  {
    NWindows::NSynchronization::CCriticalSectionLock lock(_cs);
    _paused = value;
  }
  void SetProgress(UInt64 total, UInt64 completed)
  {
    NWindows::NSynchronization::CCriticalSectionLock lock(_cs);
    _total = total;
    _completed = completed;
  }
  void SetPos(UInt64 completed)
  {
    NWindows::NSynchronization::CCriticalSectionLock lock(_cs);
    _completed = completed;
  }
  void GetProgress(UInt64 &total, UInt64 &completed)
  {
    NWindows::NSynchronization::CCriticalSectionLock lock(_cs);
    total = _total;
    completed = _completed;
  }
};

class CU64ToI32Converter
{
  UInt64 _numShiftBits;
public:
  void Init(UInt64 range)
  {
    // Windows CE doesn't like big number here.
    for (_numShiftBits = 0; range > (1 << 15); _numShiftBits++)
      range >>= 1;
  }
  int Count(UInt64 value) { return int(value >> _numShiftBits); }
};

class CProgressDialog: public NWindows::NControl::CModalDialog
{
private:
  UINT_PTR _timer;

  UString _title;
  CU64ToI32Converter _converter;
  UInt64 _peviousPos;
  UInt64 _range;
  NWindows::NControl::CProgressBar m_ProgressBar;

  int _prevPercentValue;

  bool _wasCreated;
  bool _needClose;
  bool _inCancelMessageBox;
  bool _externalCloseMessageWasReceived;

  bool OnTimer(WPARAM timerID, LPARAM callback);
  void SetRange(UInt64 range);
  void SetPos(UInt64 pos);
  virtual bool OnInit();
  virtual void OnCancel();
  virtual void OnOK();
  NWindows::NSynchronization::CManualResetEvent _dialogCreatedEvent;
  #ifndef _SFX
  void AddToTitle(LPCWSTR string);
  #endif
  bool OnButtonClicked(int buttonID, HWND buttonHWND);

  void WaitCreating() { _dialogCreatedEvent.Lock(); }
  void CheckNeedClose();
  bool OnExternalCloseMessage();
public:
  CProgressSync Sync;
  int IconID;

  #ifndef _SFX
  HWND MainWindow;
  UString MainTitle;
  UString MainAddTitle;
  ~CProgressDialog();
  #endif

  CProgressDialog(): _timer(0)
    #ifndef _SFX
    ,MainWindow(0)
    #endif
  {
    IconID = -1;
    _wasCreated = false;
    _needClose = false;
    _inCancelMessageBox = false;
    _externalCloseMessageWasReceived = false;

    if (_dialogCreatedEvent.Create() != S_OK)
      throw 1334987;
  }

  INT_PTR Create(const UString &title, NWindows::CThread &thread, HWND wndParent = 0)
  {
    _title = title;
    INT_PTR res = CModalDialog::Create(IDD_DIALOG_PROGRESS, wndParent);
    thread.Wait();
    return res;
  }

  enum
  {
    kCloseMessage = WM_USER + 1
  };

  virtual bool OnMessage(UINT message, WPARAM wParam, LPARAM lParam);

  void ProcessWasFinished()
  {
    WaitCreating();
    if (_wasCreated)
      PostMessage(kCloseMessage);
    else
      _needClose = true;
  };
};


class CProgressCloser
{
  CProgressDialog *_p;
public:
  CProgressCloser(CProgressDialog &p) : _p(&p) {}
  ~CProgressCloser() { _p->ProcessWasFinished(); }
};

#endif