Files correlati : ba0 Ricompilazione Demo : [ ] Commento : Aggiunto supporto per disabilitazione nodi git-svn-id: svn://10.65.10.50/trunk@19843 c028cbd2-c16b-5b4b-a496-9718f37d4682
		
			
				
	
	
		
			520 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			C++
		
	
	
		
			Executable File
		
	
	
	
	
			
		
		
	
	
			520 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			C++
		
	
	
		
			Executable File
		
	
	
	
	
#include "wxinc.h"
 | 
						||
#include "incstr.h"
 | 
						||
 | 
						||
#include "agasys.h"
 | 
						||
#include "xvt.h"
 | 
						||
 | 
						||
///////////////////////////////////////////////////////////
 | 
						||
// Unzip support
 | 
						||
///////////////////////////////////////////////////////////
 | 
						||
 | 
						||
#include <wx/dir.h>
 | 
						||
#include <wx/progdlg.h>
 | 
						||
#include <wx/wfstream.h>
 | 
						||
#include <wx/zipstrm.h>
 | 
						||
 | 
						||
static unsigned int aga_getziplist(const char* zipfile, wxArrayString& aFiles)
 | 
						||
{
 | 
						||
	wxFFileInputStream fin(zipfile);
 | 
						||
  wxZipInputStream zip(fin);
 | 
						||
  for (wxZipEntry* z = zip.GetNextEntry(); z; z = zip.GetNextEntry())
 | 
						||
  {
 | 
						||
    const wxString str = z->GetInternalName();
 | 
						||
    aFiles.Add(str);
 | 
						||
  }
 | 
						||
 | 
						||
	return aFiles.GetCount();
 | 
						||
}
 | 
						||
 | 
						||
static int aga_find_slash(const wxString& path, int from)
 | 
						||
{
 | 
						||
  for (int i = from; path[i]; i++)
 | 
						||
    if (wxIsPathSeparator(path[i]))
 | 
						||
      return i;
 | 
						||
 | 
						||
    return -1;
 | 
						||
}
 | 
						||
 | 
						||
static void aga_create_dir(wxString strPath)
 | 
						||
{
 | 
						||
  if (!wxEndsWithPathSeparator(strPath))
 | 
						||
		strPath += wxFILE_SEP_PATH;
 | 
						||
 | 
						||
  for (int i = aga_find_slash(strPath, 0); i > 0; i = aga_find_slash(strPath, i+1))
 | 
						||
  {
 | 
						||
    const wxString strDir = strPath.Mid(0,i);
 | 
						||
	  if (!::wxDirExists(strDir))
 | 
						||
    {
 | 
						||
      if (!::wxMkdir(strDir))
 | 
						||
      {
 | 
						||
        wxString strMsg = "Impossibile creare la cartella ";
 | 
						||
        strMsg << strDir << " !!";
 | 
						||
        wxMessageBox(strMsg, "Attenzione!", wxICON_ERROR);
 | 
						||
      }
 | 
						||
    }			
 | 
						||
  }
 | 
						||
}
 | 
						||
 | 
						||
bool aga_unzip(const char* zipfile, const char* destdir)
 | 
						||
{
 | 
						||
	wxArrayString aFiles;
 | 
						||
	const unsigned int files = aga_getziplist(zipfile, aFiles);
 | 
						||
 | 
						||
  wxProgressDialog pi("Unzip", "", files, NULL, wxPD_AUTO_HIDE | wxPD_APP_MODAL | wxPD_CAN_ABORT);
 | 
						||
  pi.SetSize(480, 120);
 | 
						||
  pi.Center();
 | 
						||
 | 
						||
	for (unsigned int f = 0; f < files; f++)
 | 
						||
	{
 | 
						||
    const wxString& strFileName = aFiles[f];
 | 
						||
    if (!pi.Update(f, strFileName))
 | 
						||
      break;
 | 
						||
 | 
						||
    if (wxEndsWithPathSeparator(strFileName) || strFileName.Find('.') < 0)  // Is dir name
 | 
						||
    {
 | 
						||
		  wxString strOutDir = destdir;
 | 
						||
      if (!wxEndsWithPathSeparator(strOutDir))
 | 
						||
		    strOutDir += wxFILE_SEP_PATH;
 | 
						||
		  strOutDir += strFileName;
 | 
						||
      aga_create_dir(strOutDir);
 | 
						||
    }
 | 
						||
    else
 | 
						||
    {
 | 
						||
		  wxZipInputStream fin(zipfile, strFileName);
 | 
						||
 | 
						||
		  wxString strOutFile = destdir;
 | 
						||
      if (!wxEndsWithPathSeparator(strOutFile) && !wxIsPathSeparator(strFileName[0]))
 | 
						||
		    strOutFile += wxFILE_SEP_PATH;
 | 
						||
		  strOutFile += strFileName;
 | 
						||
 | 
						||
		  wxString strPath;
 | 
						||
		  ::wxSplitPath(strOutFile, &strPath, NULL, NULL);
 | 
						||
      aga_create_dir(strPath);
 | 
						||
	
 | 
						||
      wxFileOutputStream fout(strOutFile);
 | 
						||
		  fout.Write(fin);
 | 
						||
    }
 | 
						||
	}
 | 
						||
	return files > 0;
 | 
						||
}
 | 
						||
 | 
						||
///////////////////////////////////////////////////////////
 | 
						||
// Zip support
 | 
						||
///////////////////////////////////////////////////////////
 | 
						||
 | 
						||
static void AddFileToZip(const wxString& strPrefix, const wxString& strFile,
 | 
						||
												 wxZipOutputStream& zip)
 | 
						||
{
 | 
						||
	if (!wxFileExists(strFile))
 | 
						||
		return;
 | 
						||
 | 
						||
	wxString strPath, strName, strExt;
 | 
						||
	wxSplitPath(strFile, &strPath, &strName, &strExt);	
 | 
						||
 | 
						||
	wxString strRelName;
 | 
						||
	strRelName = strPath.Mid(strPrefix.Length());
 | 
						||
	if (!strRelName.IsEmpty())
 | 
						||
	  strRelName += '/';
 | 
						||
	strRelName += strName;
 | 
						||
	strRelName += '.';
 | 
						||
	strRelName += strExt;
 | 
						||
 | 
						||
  zip.PutNextEntry(strRelName);
 | 
						||
 	wxFileInputStream fin(strFile);
 | 
						||
	zip.Write(fin);                                  // Scrivo file compresso
 | 
						||
}
 | 
						||
 | 
						||
static bool AddFilesToZip(const wxString& strBase, wxArrayString& aFiles, const char* zipfile)
 | 
						||
{
 | 
						||
	wxFFileOutputStream out(zipfile);
 | 
						||
	wxZipOutputStream zip(out);
 | 
						||
 | 
						||
  const size_t nFiles = aFiles.GetCount();
 | 
						||
  wxProgressDialog pi("Zip", "", nFiles, NULL, wxPD_AUTO_HIDE | wxPD_APP_MODAL | wxPD_CAN_ABORT);
 | 
						||
  pi.SetSize(480, 120);
 | 
						||
  pi.Center();
 | 
						||
 | 
						||
  for (size_t i = 0; i < nFiles; i++) 
 | 
						||
  {
 | 
						||
    const wxString& str = aFiles[i];
 | 
						||
    if (!pi.Update(i, str))
 | 
						||
      break;
 | 
						||
		AddFileToZip(strBase, str, zip);
 | 
						||
  }
 | 
						||
	return true;
 | 
						||
}
 | 
						||
 | 
						||
bool aga_zip(const char* srcfiles, const char* zipfile)
 | 
						||
{
 | 
						||
	wxString strBase, strMask, strExt;
 | 
						||
	wxSplitPath(srcfiles, &strBase, &strMask, &strExt);
 | 
						||
	strMask += '.';
 | 
						||
	strMask += strExt;
 | 
						||
 | 
						||
	wxArrayString aFiles;
 | 
						||
  wxDir::GetAllFiles(strBase, &aFiles, strMask);
 | 
						||
 | 
						||
	return AddFilesToZip(strBase, aFiles, zipfile);
 | 
						||
}
 | 
						||
 | 
						||
bool aga_zip_filelist(const char* filelist, const char* zipfile)
 | 
						||
{
 | 
						||
  wxArrayString aFiles;
 | 
						||
	ifstream fin(filelist);
 | 
						||
	while (!fin.eof())
 | 
						||
	{
 | 
						||
		char name[_MAX_PATH];
 | 
						||
		fin.getline(name, sizeof(name));
 | 
						||
		if (*name)
 | 
						||
		  aFiles.Add(name);
 | 
						||
		else
 | 
						||
			break;
 | 
						||
	}
 | 
						||
	return AddFilesToZip("", aFiles, zipfile);
 | 
						||
}
 | 
						||
 | 
						||
///////////////////////////////////////////////////////////
 | 
						||
// DDE
 | 
						||
///////////////////////////////////////////////////////////
 | 
						||
 | 
						||
#ifdef __WXMSW__
 | 
						||
  #include <wx/dde.h>
 | 
						||
  #define wxAgaClient wxDDEClient
 | 
						||
  #define wxAgaConnection wxDDEConnection
 | 
						||
#else
 | 
						||
  // Forse inutile: forse gia' fatto da wxWidgets
 | 
						||
  #include <wx/sckipc.h>
 | 
						||
  #define wxAgaClient wxTCPClient
 | 
						||
  #define wxAgaConnection wxTCPConnection
 | 
						||
#endif
 | 
						||
 | 
						||
static wxAgaClient* _net_client = NULL;
 | 
						||
static unsigned long _net_conns = 0;
 | 
						||
 | 
						||
unsigned long aga_dde_connect(const char* host, const char* service, const char* topic)
 | 
						||
{
 | 
						||
  if (_net_client == NULL)
 | 
						||
  {
 | 
						||
    _net_client = new wxAgaClient;
 | 
						||
    _net_conns = 0;
 | 
						||
  }
 | 
						||
 | 
						||
  wxConnectionBase* conn = _net_client->MakeConnection(host, service, topic);
 | 
						||
  if (conn != NULL)
 | 
						||
    _net_conns++;
 | 
						||
 | 
						||
  return (unsigned long)conn;
 | 
						||
}
 | 
						||
 | 
						||
bool aga_dde_execute(unsigned long connection, const char* msg)
 | 
						||
{
 | 
						||
  bool ok = false;
 | 
						||
  if (connection != 0)
 | 
						||
  {
 | 
						||
    wxAgaConnection* conn = (wxAgaConnection*)connection;
 | 
						||
    ok = conn->Execute((char*)msg, -1);
 | 
						||
  }
 | 
						||
  return ok;
 | 
						||
}
 | 
						||
 | 
						||
bool aga_dde_terminate(unsigned long connection)
 | 
						||
{
 | 
						||
  bool ok = false;
 | 
						||
  if (connection != 0 && _net_client != NULL)
 | 
						||
  {
 | 
						||
    wxAgaConnection* conn = (wxAgaConnection*)connection;
 | 
						||
    ok = conn->Disconnect();
 | 
						||
    if (ok && _net_conns > 0)
 | 
						||
    {
 | 
						||
      _net_conns--;
 | 
						||
      if (_net_conns == 0)
 | 
						||
      {
 | 
						||
        delete _net_client;
 | 
						||
        _net_client = NULL;
 | 
						||
      }
 | 
						||
    }
 | 
						||
  }
 | 
						||
  return ok;
 | 
						||
}
 | 
						||
 | 
						||
///////////////////////////////////////////////////////////
 | 
						||
// TProgressIndicator
 | 
						||
///////////////////////////////////////////////////////////
 | 
						||
 | 
						||
class TProgressIndicator : public wxDialog
 | 
						||
{
 | 
						||
  enum { MAX_GAUGES = 16 };
 | 
						||
  wxGauge* m_pGauge[MAX_GAUGES];
 | 
						||
  wxStaticText* m_pPassed;
 | 
						||
  wxStaticText* m_pResidual;
 | 
						||
  wxStaticText* m_pEstimated;
 | 
						||
  wxStopWatch m_chrono;
 | 
						||
  long m_nNextUpdate;
 | 
						||
 | 
						||
protected:
 | 
						||
  DECLARE_EVENT_TABLE();
 | 
						||
 | 
						||
  void Init(wxString msg, int nGauges, bool bCancellable);
 | 
						||
  wxString msec2str(long ms) const;
 | 
						||
  long Elapsed() const { return m_chrono.Time(); }
 | 
						||
 | 
						||
public:
 | 
						||
  void SetRange(int nRange, int nGauge = 0);
 | 
						||
  bool SetProgress(int nPos, int nGauge = 0);
 | 
						||
  void SetMessage(wxString msg);
 | 
						||
 | 
						||
  TProgressIndicator(wxString msg, bool bCancellable, int nGauges);
 | 
						||
  TProgressIndicator(size_t nRange, wxString msg, bool bCancellable);
 | 
						||
  virtual ~TProgressIndicator();
 | 
						||
};
 | 
						||
 | 
						||
BEGIN_EVENT_TABLE(TProgressIndicator, wxDialog)
 | 
						||
//  EVT_MENU(wxID_CANCEL, TProgressIndicator::OnCancel)
 | 
						||
END_EVENT_TABLE()
 | 
						||
 | 
						||
wxString TProgressIndicator::msec2str(long ms) const
 | 
						||
{
 | 
						||
  int s = ms/1000;
 | 
						||
  const int h = s / 3600;
 | 
						||
  s -= h*3600;
 | 
						||
  const int m = s / 60;
 | 
						||
  s -= m*60;
 | 
						||
  return wxString::Format(wxT("%02d:%02d:%02d"), h, m, s);
 | 
						||
}
 | 
						||
 | 
						||
void TProgressIndicator::SetRange(int nRange, int nGauge)
 | 
						||
{
 | 
						||
  wxASSERT(nRange >= 0 && nGauge >= 0 && nGauge < MAX_GAUGES);
 | 
						||
  m_pGauge[nGauge]->SetRange(nRange);
 | 
						||
}
 | 
						||
 | 
						||
bool TProgressIndicator::SetProgress(int nPos, int nGauge)
 | 
						||
{
 | 
						||
  if (!IsShown())
 | 
						||
    return false;
 | 
						||
 | 
						||
  wxASSERT(nPos >= 0 && nGauge >= 0 && nGauge < MAX_GAUGES);
 | 
						||
  m_pGauge[nGauge]->SetValue(nPos);
 | 
						||
 | 
						||
  const long elap = Elapsed();
 | 
						||
  if (elap > m_nNextUpdate)
 | 
						||
  {
 | 
						||
    m_nNextUpdate = elap+CLOCKS_PER_SEC;
 | 
						||
    m_pPassed->SetLabel(msec2str(elap));
 | 
						||
 | 
						||
    wxLongLong_t nTotValue = 0, nTotRange = 0;
 | 
						||
    for (int i = 0; i < MAX_GAUGES && m_pGauge[i]; i++)
 | 
						||
    {
 | 
						||
      const int nValue = m_pGauge[i]->GetValue();
 | 
						||
      const int nRange = m_pGauge[i]->GetRange();
 | 
						||
      if (nValue < nRange) // Escludi dal calcolo i processi gi<67> terminati
 | 
						||
      {
 | 
						||
        nTotValue += nValue;
 | 
						||
        nTotRange += nRange;
 | 
						||
      }
 | 
						||
    }
 | 
						||
    if (nTotValue > 0)
 | 
						||
    {
 | 
						||
      const long est = long(elap * nTotRange / nTotValue);
 | 
						||
      m_pEstimated->SetLabel(msec2str(est));
 | 
						||
      m_pResidual->SetLabel(msec2str(est-elap));
 | 
						||
    }
 | 
						||
 | 
						||
    wxYield();
 | 
						||
  }
 | 
						||
 | 
						||
  return true;
 | 
						||
}
 | 
						||
 | 
						||
void TProgressIndicator::SetMessage(wxString msg)
 | 
						||
{ SetLabel(msg); }
 | 
						||
 | 
						||
void TProgressIndicator::Init(wxString msg, int nGauges, bool bCancellable)
 | 
						||
{
 | 
						||
  const int nGap = 4;
 | 
						||
  wxBoxSizer* pTopSizer = new wxBoxSizer(wxVERTICAL);
 | 
						||
  SetSizer(pTopSizer);
 | 
						||
 | 
						||
  memset(m_pGauge, 0, sizeof(m_pGauge));
 | 
						||
  for (int i = 0; i < nGauges; i++)
 | 
						||
  {
 | 
						||
    m_pGauge[i] = new wxGauge(this, 1001+i, 100, wxDefaultPosition, wxSize(400, -1));
 | 
						||
    pTopSizer->Add(m_pGauge[i], 0, wxALL, nGap);
 | 
						||
  }
 | 
						||
 | 
						||
  wxGridSizer* pTimersSizer = new wxGridSizer(1, 3, nGap, nGap);
 | 
						||
  pTopSizer->Add(pTimersSizer, 0, wxEXPAND);
 | 
						||
 | 
						||
  wxStaticBoxSizer* pPassed = new wxStaticBoxSizer(wxVERTICAL, this, _("Elapsed Time"));
 | 
						||
  pTimersSizer->Add(pPassed, 0, wxEXPAND);
 | 
						||
  m_pPassed = new wxStaticText(this, 1101, wxT("00:00:00"));
 | 
						||
  pPassed->Add(m_pPassed, 0, wxALL|wxALIGN_CENTER, 0);
 | 
						||
 | 
						||
  wxStaticBoxSizer* pResidual = new wxStaticBoxSizer(wxVERTICAL, this, _("Residual Time"));
 | 
						||
  pTimersSizer->Add(pResidual, 0, wxEXPAND);
 | 
						||
  m_pResidual = new wxStaticText(this, 1102, wxT("00:00:00"));
 | 
						||
  pResidual->Add(m_pResidual, 0, wxALL|wxALIGN_CENTER, 0);
 | 
						||
 | 
						||
  wxStaticBoxSizer* pEstimated = new wxStaticBoxSizer(wxVERTICAL, this, _("Estimated Time"));
 | 
						||
  pTimersSizer->Add(pEstimated, 0, wxEXPAND);
 | 
						||
  m_pEstimated = new wxStaticText(this, 1103, wxT("00:00:00"));
 | 
						||
  pEstimated->Add(m_pEstimated, 0, wxALL|wxALIGN_CENTER, 0);
 | 
						||
 | 
						||
  wxButton* pCancel = NULL;
 | 
						||
  if (bCancellable)
 | 
						||
  {
 | 
						||
    pCancel = new wxButton(this, wxID_CANCEL, _("Cancel"));
 | 
						||
    pTopSizer->Add(pCancel, 0, wxALL|wxALIGN_CENTER_HORIZONTAL, nGap);
 | 
						||
  }
 | 
						||
 | 
						||
  pTopSizer->SetSizeHints(this);
 | 
						||
 | 
						||
  SetMessage(msg);
 | 
						||
 | 
						||
  Show();
 | 
						||
  Enable();
 | 
						||
  Update();
 | 
						||
 | 
						||
  m_nNextUpdate = 0;
 | 
						||
  m_chrono.Start();
 | 
						||
}
 | 
						||
 | 
						||
TProgressIndicator::TProgressIndicator(size_t nRange, wxString msg, bool bCancellable)
 | 
						||
                   : wxDialog(NULL, wxID_ANY, msg)  
 | 
						||
{
 | 
						||
  Init(msg, 1, bCancellable);
 | 
						||
  SetRange(nRange, 0);
 | 
						||
}
 | 
						||
 | 
						||
TProgressIndicator::TProgressIndicator(wxString msg, bool bCancellable, int nGauges)
 | 
						||
                   : wxDialog(NULL, wxID_ANY, msg)
 | 
						||
{
 | 
						||
  Init(msg, nGauges, bCancellable);
 | 
						||
}
 | 
						||
 | 
						||
TProgressIndicator::~TProgressIndicator()
 | 
						||
{ }
 | 
						||
 | 
						||
///////////////////////////////////////////////////////////
 | 
						||
// Multiprocess
 | 
						||
///////////////////////////////////////////////////////////
 | 
						||
 | 
						||
class TWorker : public wxThread
 | 
						||
{
 | 
						||
  XVT_MULTITHREAD_CALLBACK m_pFunc;
 | 
						||
  void* m_pCaller;
 | 
						||
  void* m_pData;
 | 
						||
  int m_nFirst, m_nLast, m_nTotal;
 | 
						||
  TProgressIndicator* m_pi;
 | 
						||
  int m_nGauge;
 | 
						||
 | 
						||
protected:
 | 
						||
  virtual ExitCode Entry();
 | 
						||
 | 
						||
public:
 | 
						||
  TWorker(XVT_MULTITHREAD_CALLBACK func, void* pCaller, void* pData, 
 | 
						||
          int nFirst, int nLast, int nTotal, TProgressIndicator* pi, int nGauge);
 | 
						||
};
 | 
						||
 | 
						||
wxThread::ExitCode TWorker::Entry()
 | 
						||
{
 | 
						||
  ExitCode ec = 0;
 | 
						||
  if (m_pi != NULL)
 | 
						||
  {
 | 
						||
    m_pi->SetRange(m_nLast-m_nFirst, m_nGauge);
 | 
						||
    for (int i = m_nFirst; i < m_nLast && ec == 0; i++)
 | 
						||
    {
 | 
						||
      ec = (ExitCode)m_pFunc(m_pCaller, m_pData, i, m_nTotal);
 | 
						||
      if (!m_pi->SetProgress(i-m_nFirst+1, m_nGauge))
 | 
						||
        ec = ExitCode(-1);
 | 
						||
    }
 | 
						||
  }
 | 
						||
  else
 | 
						||
  {
 | 
						||
    for (int i = m_nFirst; i < m_nLast && ec == 0; i++)
 | 
						||
      ec = (ExitCode)m_pFunc(m_pCaller, m_pData, i, m_nTotal);
 | 
						||
  }
 | 
						||
  return ec;
 | 
						||
}
 | 
						||
 | 
						||
TWorker::TWorker(XVT_MULTITHREAD_CALLBACK func, void* pCaller, void* pData, 
 | 
						||
                   int nFirst, int nLast, int nTotal, TProgressIndicator* pi, int nGauge)
 | 
						||
        : wxThread(wxTHREAD_JOINABLE), m_pFunc(func), m_pCaller(pCaller), m_pData(pData), 
 | 
						||
          m_nFirst(nFirst), m_nLast(nLast), m_nTotal(nTotal), m_pi(pi), m_nGauge(nGauge)
 | 
						||
 | 
						||
{ 
 | 
						||
//  SetPriority(WXTHREAD_MIN_PRIORITY);
 | 
						||
  Create(); 
 | 
						||
}
 | 
						||
 | 
						||
BOOLEAN xvt_sys_multithread(XVT_MULTITHREAD_CALLBACK pFunc, void* pCaller, void* pData,
 | 
						||
                            int tot, const char* msg)
 | 
						||
{
 | 
						||
  const int MAX_WORKERS = 16;
 | 
						||
 | 
						||
  // Bilanciamento automatico
 | 
						||
  int nWorkers = wxThread::GetCPUCount();
 | 
						||
  if (nWorkers <= 0) 
 | 
						||
    nWorkers = 1;
 | 
						||
  if (nWorkers > tot)
 | 
						||
    nWorkers = tot;
 | 
						||
  if (nWorkers > MAX_WORKERS)
 | 
						||
    nWorkers = MAX_WORKERS;
 | 
						||
 | 
						||
  int ret = 0;
 | 
						||
  if (nWorkers > 1)
 | 
						||
  {
 | 
						||
    TProgressIndicator* pi = NULL;
 | 
						||
    if (msg && *msg)
 | 
						||
      pi = new TProgressIndicator(msg, tot > nWorkers, nWorkers);
 | 
						||
    
 | 
						||
    TWorker* worker[MAX_WORKERS]; memset(worker, 0, sizeof(worker));
 | 
						||
 | 
						||
    int nFirst, nLast = 0, w;
 | 
						||
    for (w = 0; w < nWorkers; w++)
 | 
						||
    {
 | 
						||
      nFirst = nLast;
 | 
						||
      nLast = tot*(w+1)/nWorkers;
 | 
						||
      worker[w] = new TWorker(pFunc, pCaller, pData, nFirst, nLast, tot, pi, w);
 | 
						||
    }
 | 
						||
  
 | 
						||
    for (w = 0; w < nWorkers; w++)
 | 
						||
      worker[w]->Run();
 | 
						||
 | 
						||
    if (pi != NULL)
 | 
						||
      pi->Refresh();
 | 
						||
    
 | 
						||
    for (w = 0; w < nWorkers; w++)
 | 
						||
    {
 | 
						||
      const wxThread::ExitCode r = worker[w]->Wait();
 | 
						||
      if (ret == 0 && r != 0) 
 | 
						||
        ret = int(r);
 | 
						||
      delete worker[w];
 | 
						||
      worker[w] = NULL;
 | 
						||
    }
 | 
						||
 | 
						||
    if (pi != NULL)
 | 
						||
      delete pi;
 | 
						||
  }
 | 
						||
  else
 | 
						||
  {
 | 
						||
    if (msg && *msg)
 | 
						||
    {
 | 
						||
      TProgressIndicator pi(size_t(tot), msg, tot > nWorkers);
 | 
						||
      for (int i = 0; i < tot && ret == 0; i++)
 | 
						||
      {
 | 
						||
        ret = pFunc(pCaller, pData, i, tot);
 | 
						||
        if (!pi.SetProgress(i+1))
 | 
						||
          ret = -1;
 | 
						||
      }
 | 
						||
    }
 | 
						||
    else
 | 
						||
    {
 | 
						||
      for (int i = 0; i < tot && ret == 0; i++)
 | 
						||
        ret = pFunc(pCaller, pData, i, tot);
 | 
						||
    }
 | 
						||
  }
 | 
						||
 | 
						||
  return ret;
 | 
						||
}
 |