#include "FileZilla.h"
#include "queue.h"
#include "Mainfrm.h"
#include "Options.h"
#include "StatusView.h"
#include "statuslinectrl.h"
#include "../tinyxml/tinyxml.h"
#include "xmlfunctions.h"
#include "filezillaapp.h"
#include "ipcmutex.h"
#include "state.h"
#include "asyncrequestqueue.h"
#include "defaultfileexistsdlg.h"
#include "filter.h"
#include <wx/dnd.h>
#include "dndobjects.h"
#include "loginmanager.h"
#include "aui_notebook_ex.h"
#include "queueview_failed.h"
#include "queueview_successful.h"
#include "commandqueue.h"
#include <wx/utils.h>
#include <wx/progdlg.h>
#include <wx/sound.h>
#ifdef _DEBUG
#define new DEBUG_NEW
#endif
class CQueueViewDropTarget : public wxDropTarget
{
public:
CQueueViewDropTarget(CQueueView* pQueueView)
: m_pQueueView(pQueueView), m_pFileDataObject(new wxFileDataObject()),
m_pRemoteDataObject(new CRemoteDataObject())
{
m_pDataObject = new wxDataObjectComposite;
m_pDataObject->Add(m_pRemoteDataObject, true);
m_pDataObject->Add(m_pFileDataObject, false);
SetDataObject(m_pDataObject);
}
virtual wxDragResult OnData(wxCoord x, wxCoord y, wxDragResult def)
{
if (def == wxDragError ||
def == wxDragNone ||
def == wxDragCancel)
return def;
if (!GetData())
return wxDragError;
if (m_pDataObject->GetReceivedFormat() == m_pFileDataObject->GetFormat())
{
CState* const pState = m_pQueueView->m_pMainFrame->GetState();
const CServer* const pServer = pState->GetServer();
if (!pServer)
return wxDragNone;
const CServerPath& path = pState->GetRemotePath();
if (path.IsEmpty())
return wxDragNone;
pState->UploadDroppedFiles(m_pFileDataObject, path, true);
}
else
{
if (m_pRemoteDataObject->GetProcessId() != (int)wxGetProcessId())
{
wxMessageBox(_("Drag&drop between different instances of FileZilla has not been implemented yet."));
return wxDragNone;
}
CState* const pState = m_pQueueView->m_pMainFrame->GetState();
const CServer* const pServer = pState->GetServer();
if (!pServer)
return wxDragNone;
if (*pServer != m_pRemoteDataObject->GetServer())
{
wxMessageBox(_("Drag&drop between different servers has not been implemented yet."));
return wxDragNone;
}
const wxString& target = pState->GetLocalDir();
#ifdef __WXMSW__
if (target == _T("\\"))
{
wxBell();
return wxDragNone;
}
#endif
if (!pState->DownloadDroppedFiles(m_pRemoteDataObject, target, true))
return wxDragNone;
}
return def;
}
virtual bool OnDrop(wxCoord x, wxCoord y)
{
return true;
}
virtual wxDragResult OnDragOver(wxCoord x, wxCoord y, wxDragResult def)
{
if (def == wxDragError ||
def == wxDragNone ||
def == wxDragCancel)
{
return def;
}
def = wxDragCopy;
return def;
}
virtual void OnLeave()
{
}
virtual wxDragResult OnEnter(wxCoord x, wxCoord y, wxDragResult def)
{
return OnDragOver(x, y, def);
}
protected:
CQueueView *m_pQueueView;
wxFileDataObject* m_pFileDataObject;
CRemoteDataObject* m_pRemoteDataObject;
wxDataObjectComposite* m_pDataObject;
};
DECLARE_EVENT_TYPE(fzEVT_FOLDERTHREAD_COMPLETE, -1)
DEFINE_EVENT_TYPE(fzEVT_FOLDERTHREAD_COMPLETE)
DECLARE_EVENT_TYPE(fzEVT_FOLDERTHREAD_FILES, -1)
DEFINE_EVENT_TYPE(fzEVT_FOLDERTHREAD_FILES)
DECLARE_EVENT_TYPE(fzEVT_UPDATE_STATUSLINES, -1)
DEFINE_EVENT_TYPE(fzEVT_UPDATE_STATUSLINES)
DECLARE_EVENT_TYPE(fzEVT_ASKFORPASSWORD, -1)
DEFINE_EVENT_TYPE(fzEVT_ASKFORPASSWORD)
BEGIN_EVENT_TABLE(CQueueView, CQueueViewBase)
EVT_FZ_NOTIFICATION(wxID_ANY, CQueueView::OnEngineEvent)
EVT_COMMAND(wxID_ANY, fzEVT_FOLDERTHREAD_COMPLETE, CQueueView::OnFolderThreadComplete)
EVT_COMMAND(wxID_ANY, fzEVT_FOLDERTHREAD_FILES, CQueueView::OnFolderThreadFiles)
EVT_SCROLLWIN(CQueueView::OnScrollEvent)
EVT_COMMAND(wxID_ANY, fzEVT_UPDATE_STATUSLINES, CQueueView::OnUpdateStatusLines)
EVT_MOUSEWHEEL(CQueueView::OnMouseWheel)
EVT_CONTEXT_MENU(CQueueView::OnContextMenu)
EVT_MENU(XRCID("ID_PROCESSQUEUE"), CQueueView::OnProcessQueue)
EVT_MENU(XRCID("ID_REMOVEALL"), CQueueView::OnStopAndClear)
EVT_MENU(XRCID("ID_REMOVE"), CQueueView::OnRemoveSelected)
EVT_MENU(XRCID("ID_DEFAULT_FILEEXISTSACTION"), CQueueView::OnSetDefaultFileExistsAction)
EVT_MENU(XRCID("ID_ACTIONAFTER_DISABLE"), CQueueView::OnActionAfter)
EVT_MENU(XRCID("ID_ACTIONAFTER_CLOSE"), CQueueView::OnActionAfter)
EVT_MENU(XRCID("ID_ACTIONAFTER_DISCONNECT"), CQueueView::OnActionAfter)
EVT_MENU(XRCID("ID_ACTIONAFTER_RUNCOMMAND"), CQueueView::OnActionAfter)
EVT_MENU(XRCID("ID_ACTIONAFTER_SHOWMESSAGE"), CQueueView::OnActionAfter)
EVT_MENU(XRCID("ID_ACTIONAFTER_PLAYSOUND"), CQueueView::OnActionAfter)
EVT_MENU(XRCID("ID_ACTIONAFTER_REBOOT"), CQueueView::OnActionAfter)
EVT_MENU(XRCID("ID_ACTIONAFTER_SHUTDOWN"), CQueueView::OnActionAfter)
EVT_COMMAND(wxID_ANY, fzEVT_ASKFORPASSWORD, CQueueView::OnAskPassword)
EVT_LIST_ITEM_FOCUSED(wxID_ANY, CQueueView::OnFocusItemChanged)
EVT_TIMER(wxID_ANY, CQueueView::OnTimer)
EVT_MENU(XRCID("ID_PRIORITY_HIGHEST"), CQueueView::OnSetPriority)
EVT_MENU(XRCID("ID_PRIORITY_HIGH"), CQueueView::OnSetPriority)
EVT_MENU(XRCID("ID_PRIORITY_NORMAL"), CQueueView::OnSetPriority)
EVT_MENU(XRCID("ID_PRIORITY_LOW"), CQueueView::OnSetPriority)
EVT_MENU(XRCID("ID_PRIORITY_LOWEST"), CQueueView::OnSetPriority)
EVT_COMMAND(wxID_ANY, fzEVT_GRANTEXCLUSIVEENGINEACCESS, CQueueView::OnExclusiveEngineRequestGranted)
END_EVENT_TABLE()
class CFolderProcessingThread : public wxThread
{
public:
CFolderProcessingThread(CQueueView* pOwner, CFolderScanItem* pFolderItem)
: wxThread(wxTHREAD_JOINABLE), m_condition(m_sync) {
m_pOwner = pOwner;
m_pFolderItem = pFolderItem;
m_didSendEvent = false;
m_threadWaiting = false;
}
~CFolderProcessingThread() { }
void GetFiles(std::list<t_newEntry> &entryList)
{
wxASSERT(entryList.empty());
wxMutexLocker locker(m_sync);
entryList.swap(m_entryList);
m_didSendEvent = false;
if (m_threadWaiting)
{
m_threadWaiting = false;
m_condition.Signal();
}
}
protected:
void AddEntry(const t_newEntry& entry)
{
m_sync.Lock();
m_entryList.push_back(entry);
// Wait if there are more than 100 items to queue,
// don't send notification if there are less than 10.
// This reduces overhead
bool send;
if (m_didSendEvent)
{
send = false;
if (m_entryList.size() >= 100)
{
m_threadWaiting = true;
m_condition.Wait();
}
}
else if (m_entryList.size() < 10)
send = false;
else
send = true;
m_sync.Unlock();
if (send)
{
// We send the notification after leaving the critical section, else we
// could get into a deadlock. wxWidgets event system does internal
// locking.
wxCommandEvent evt(fzEVT_FOLDERTHREAD_FILES, wxID_ANY);
wxPostEvent(m_pOwner, evt);
}
}
ExitCode Entry()
{
wxMutexGuiEnter();
wxASSERT(m_pFolderItem->GetTopLevelItem() && m_pFolderItem->GetTopLevelItem()->GetType() == QueueItemType_Server);
wxMutexGuiLeave();
wxASSERT(!m_pFolderItem->Download());
wxString currentLocalPath;
CServerPath currentRemotePath;
wxDir* pDir = 0;
while (!TestDestroy())
{
if (m_pFolderItem->m_remove)
{
wxCommandEvent evt(fzEVT_FOLDERTHREAD_COMPLETE, wxID_ANY);
wxPostEvent(m_pOwner, evt);
delete pDir;
return 0;
}
bool found;
wxString file;
if (!pDir)
{
if (!m_pFolderItem->m_dirsToCheck.empty())
{
const CFolderScanItem::t_dirPair& pair = m_pFolderItem->m_dirsToCheck.front();
wxLogNull nullLog;
pDir = new wxDir(pair.localPath);
if (pDir->IsOpened())
{
currentLocalPath = pair.localPath;
currentRemotePath = pair.remotePath;
found = pDir->GetFirst(&file);
if (!found)
{
// Empty directory
t_newEntry entry;
entry.remotePath.SetSafePath(pair.remotePath.GetSafePath().c_str());
AddEntry(entry);
}
}
else
found = false;
m_pFolderItem->m_dirsToCheck.pop_front();
}
else
{
wxCommandEvent evt(fzEVT_FOLDERTHREAD_COMPLETE, wxID_ANY);
wxPostEvent(m_pOwner, evt);
if (pDir)
delete pDir;
return 0;
}
}
else
found = pDir->GetNext(&file);
while (found && !TestDestroy() && !m_pFolderItem->m_remove)
{
const wxString& fullName = currentLocalPath + wxFileName::GetPathSeparator() + file;
if (wxDir::Exists(fullName))
{
#ifdef S_ISLNK
wxStructStat buf;
const int result = wxLstat(fullName, &buf);
if (!result && S_ISLNK(buf.st_mode))
{
found = pDir->GetNext(&file);
continue;
}
#endif
if (!m_filters.FilenameFiltered(file, true, -1, true))
{
CFolderScanItem::t_dirPair pair;
pair.localPath = fullName;
pair.remotePath = currentRemotePath;
pair.remotePath.AddSegment(file);
m_pFolderItem->m_dirsToCheck.push_back(pair);
}
}
else
{
wxStructStat buf;
int result;
result = wxStat(fullName, &buf);
if (!m_filters.FilenameFiltered(file, false, result ? -1 : buf.st_size, true))
{
t_newEntry entry;
entry.localFile = fullName.c_str();
entry.remoteFile = file.c_str();
entry.remotePath.SetSafePath(currentRemotePath.GetSafePath().c_str());
entry.size = result ? -1 : buf.st_size;
AddEntry(entry);
}
}
found = pDir->GetNext(&file);
}
bool send;
m_sync.Lock();
if (!m_didSendEvent && m_entryList.size())
send = true;
else
send = false;
m_sync.Unlock();
if (send)
{
// We send the notification after leaving the critical section, else we
// could get into a deadlock. wxWidgets event system does internal
// locking.
wxCommandEvent evt(fzEVT_FOLDERTHREAD_FILES, wxID_ANY);
wxPostEvent(m_pOwner, evt);
}
delete pDir;
pDir = 0;
}
return 0;
}
// Access has to be guarded by m_sync
std::list<t_newEntry> m_entryList;
CQueueView* m_pOwner;
CFolderScanItem* m_pFolderItem;
CFilterDialog m_filters;
wxMutex m_sync;
wxCondition m_condition;
bool m_threadWaiting;
bool m_didSendEvent;
};
CQueueView::CQueueView(CQueue* parent, int index, CMainFrame* pMainFrame, CAsyncRequestQueue *pAsyncRequestQueue)
: CQueueViewBase(parent, index, _("Queued files")),
m_pMainFrame(pMainFrame),
m_pAsyncRequestQueue(pAsyncRequestQueue)
{
if (m_pAsyncRequestQueue)
m_pAsyncRequestQueue->SetQueue(this);
m_activeCount = 0;
m_activeCountDown = 0;
m_activeCountUp = 0;
m_activeMode = 0;
m_quit = false;
m_waitStatusLineUpdate = false;
m_lastTopItem = -1;
m_pFolderProcessingThread = 0;
m_totalQueueSize = 0;
m_filesWithUnknownSize = 0;
m_actionAfterState = ActionAfterState_Disabled;
#ifdef __WXMSW__
m_actionAfterWarnDialog = 0;
m_actionAfterTimer = 0;
m_actionAfterTimerId = -1;
#endif
CreateColumns(_("Status"));
//Initialize the engine data
t_EngineData *pData = new t_EngineData;
pData->active = false;
pData->pItem = 0;
pData->pStatusLineCtrl = 0;
pData->state = t_EngineData::none;
pData->pEngine = 0; // TODO: Primary transfer engine data
pData->m_idleDisconnectTimer = 0;
m_engineData.push_back(pData);
int engineCount = COptions::Get()->GetOptionVal(OPTION_NUMTRANSFERS);
for (int i = 0; i < engineCount; i++)
{
t_EngineData *pData = new t_EngineData;
pData->active = false;
pData->pItem = 0;
pData->pStatusLineCtrl = 0;
pData->state = t_EngineData::none;
pData->m_idleDisconnectTimer = 0;
pData->pEngine = new CFileZillaEngine();
pData->pEngine->Init(this, COptions::Get());
m_engineData.push_back(pData);
}
SettingsChanged();
SetDropTarget(new CQueueViewDropTarget(this));
#if (!defined(__WIN32__) && !defined(__WXMAC__)) || defined(__WXUNIVERSAL__)
// The generic list control a scrolled child window. In order to receive
// scroll events, we have to connect the event handler to it.
((wxWindow*)m_mainWin)->Connect(-1, wxEVT_SCROLLWIN_TOP, wxScrollWinEventHandler(CQueueView::OnScrollEvent), 0, this);
((wxWindow*)m_mainWin)->Connect(-1, wxEVT_SCROLLWIN_BOTTOM, wxScrollWinEventHandler(CQueueView::OnScrollEvent), 0, this);
((wxWindow*)m_mainWin)->Connect(-1, wxEVT_SCROLLWIN_LINEUP, wxScrollWinEventHandler(CQueueView::OnScrollEvent), 0, this);
((wxWindow*)m_mainWin)->Connect(-1, wxEVT_SCROLLWIN_LINEDOWN, wxScrollWinEventHandler(CQueueView::OnScrollEvent), 0, this);
((wxWindow*)m_mainWin)->Connect(-1, wxEVT_SCROLLWIN_PAGEUP, wxScrollWinEventHandler(CQueueView::OnScrollEvent), 0, this);
((wxWindow*)m_mainWin)->Connect(-1, wxEVT_SCROLLWIN_PAGEDOWN, wxScrollWinEventHandler(CQueueView::OnScrollEvent), 0, this);
#endif
}
CQueueView::~CQueueView()
{
if (m_pFolderProcessingThread)
{
m_pFolderProcessingThread->Delete();
m_pFolderProcessingThread->Wait();
delete m_pFolderProcessingThread;
}
DeleteEngines();
}
bool CQueueView::QueueFile(const bool queueOnly, const bool download, const wxString& localFile,
const wxString& remoteFile, const CServerPath& remotePath,
const CServer& server, const wxLongLong size)
{
CServerItem* pServerItem = CreateServerItem(server);
CFileItem* fileItem;
if (localFile == _T("") || remotePath.IsEmpty())
{
if (download)
fileItem = new CFolderItem(pServerItem, queueOnly, localFile);
else
fileItem = new CFolderItem(pServerItem, queueOnly, remotePath, remoteFile);
}
else
{
fileItem = new CFileItem(pServerItem, queueOnly, download, localFile, remoteFile, remotePath, size);
fileItem->m_transferSettings.binary = ShouldUseBinaryMode(download ? remoteFile : wxFileName(localFile).GetFullName());
}
InsertItem(pServerItem, fileItem);
CommitChanges();
if (!m_activeMode && !queueOnly)
m_activeMode = 1;
m_waitStatusLineUpdate = true;
AdvanceQueue();
m_waitStatusLineUpdate = false;
UpdateStatusLinePositions();
Refresh(false);
return true;
}
bool CQueueView::QueueFiles(const bool queueOnly, const wxString& localPath, const CRemoteDataObject& dataObject)
{
CServerItem* pServerItem = CreateServerItem(dataObject.GetServer());
const std::list<CRemoteDataObject::t_fileInfo>& files = dataObject.GetFiles();
for (std::list<CRemoteDataObject::t_fileInfo>::const_iterator iter = files.begin(); iter != files.end(); iter++)
{
if (iter->dir)
continue;
CFileItem* fileItem;
fileItem = new CFileItem(pServerItem, queueOnly, true, localPath + iter->name, iter->name, dataObject.GetServerPath(), iter->size);
fileItem->m_transferSettings.binary = ShouldUseBinaryMode(iter->name);
InsertItem(pServerItem, fileItem);
}
CommitChanges();
if (!m_activeMode && !queueOnly)
m_activeMode = 1;
m_waitStatusLineUpdate = true;
AdvanceQueue();
m_waitStatusLineUpdate = false;
UpdateStatusLinePositions();
Refresh(false);
return true;
}
void CQueueView::OnEngineEvent(wxEvent &event)
{
std::vector<t_EngineData*>::iterator iter;
for (iter = m_engineData.begin(); iter != m_engineData.end(); iter++)
{
if ((wxObject *)(*iter)->pEngine == event.GetEventObject())
break;
}
if (iter == m_engineData.end())
return;
t_EngineData* const pEngineData = *iter;
CNotification *pNotification = pEngineData->pEngine->GetNextNotification();
while (pNotification)
{
ProcessNotification(pEngineData, pNotification);
if (m_engineData.empty() || !pEngineData->pEngine)
break;
pNotification = pEngineData->pEngine->GetNextNotification();
}
}
void CQueueView::ProcessNotification(CNotification* pNotification)
{
if (m_engineData.empty())
{
delete pNotification;
return;
}
t_EngineData* pEngineData = m_engineData[0];
if (!pEngineData->active)
{
delete pNotification;
return;
}
ProcessNotification(pEngineData, pNotification);
}
void CQueueView::ProcessNotification(t_EngineData* pEngineData, CNotification* pNotification)
{
switch (pNotification->GetID())
{
case nId_logmsg:
m_pMainFrame->GetStatusView()->AddToLog(reinterpret_cast<CLogmsgNotification *>(pNotification));
delete pNotification;
break;
case nId_operation:
ProcessReply(*pEngineData, reinterpret_cast<COperationNotification*>(pNotification));
delete pNotification;
break;
case nId_asyncrequest:
if (!pEngineData->pItem)
{
delete pNotification;
break;
}
switch (reinterpret_cast<CAsyncRequestNotification *>(pNotification)->GetRequestID())
{
case reqId_fileexists:
{
// Set default file exists action
CFileExistsNotification *pFileExistsNotification = reinterpret_cast<CFileExistsNotification *>(pNotification);
pFileExistsNotification->overwriteAction = (enum CFileExistsNotification::OverwriteAction)pEngineData->pItem->m_defaultFileExistsAction;
}
break;
default:
break;
}
m_pAsyncRequestQueue->AddRequest(pEngineData->pEngine, reinterpret_cast<CAsyncRequestNotification *>(pNotification));
break;
case nId_active:
{
CActiveNotification *pActiveNotification = reinterpret_cast<CActiveNotification *>(pNotification);
if (pActiveNotification->IsRecv())
m_pMainFrame->UpdateRecvLed();
else
m_pMainFrame->UpdateSendLed();
delete pNotification;
}
break;
case nId_transferstatus:
if (pEngineData->pItem && pEngineData->pStatusLineCtrl)
{
CTransferStatusNotification *pTransferStatusNotification = reinterpret_cast<CTransferStatusNotification *>(pNotification);
const CTransferStatus *pStatus = pTransferStatusNotification->GetStatus();
if (pEngineData->active)
pEngineData->pStatusLineCtrl->SetTransferStatus(pStatus);
}
delete pNotification;
break;
default:
delete pNotification;
break;
}
}
bool CQueueView::TryStartNextTransfer()
{
if (m_quit || !m_activeMode)
return false;
// Check transfer limit
if (m_activeCount >= COptions::Get()->GetOptionVal(OPTION_NUMTRANSFERS))
return false;
// Check limits for concurrent up/downloads
const int maxDownloads = COptions::Get()->GetOptionVal(OPTION_CONCURRENTDOWNLOADLIMIT);
const int maxUploads = COptions::Get()->GetOptionVal(OPTION_CONCURRENTUPLOADLIMIT);
enum TransferDirection wantedDirection;
if (maxDownloads && m_activeCountDown >= maxDownloads)
{
if (maxUploads && m_activeCountUp >= maxUploads)
return false;
else
wantedDirection = upload;
}
else if (maxUploads && m_activeCountUp >= maxUploads)
wantedDirection = download;
else
wantedDirection = both;
struct t_bestMatch
{
CFileItem* fileItem;
CServerItem* serverItem;
t_EngineData* pEngineData;
bool browsingConnection;
} bestMatch = {0};
// Find inactive file. Check all servers for
// the file with the highest priority
for (std::vector<CServerItem*>::iterator iter = m_serverList.begin(); iter != m_serverList.end(); iter++)
{
t_EngineData* pEngineData = 0;
CServerItem* currentServerItem = *iter;
int maxCount = currentServerItem->GetServer().MaximumMultipleConnections();
int activeCount = currentServerItem->m_activeCount;
const CServer* pBrowsingServer = m_pMainFrame->GetState()->GetServer();
bool browsingOnSame = false;
if (pBrowsingServer && currentServerItem->GetServer() == *pBrowsingServer)
{
browsingOnSame = true;
activeCount++;
}
if (maxCount && activeCount >= maxCount)
{
// If we got an idle engine connected to this very server, start the
// transfer anyhow. Let's not get this connection go to waste.
pEngineData = GetIdleEngine(&(*iter)->GetServer());
if (pEngineData)
{
if (!pEngineData->pEngine->IsConnected() || pEngineData->lastServer != (*iter)->GetServer())
{
// If the browsing connection is connected to this server and idle, use it
if (browsingOnSame && activeCount == 1)
{
pEngineData = m_engineData[0];
if (pEngineData->active)
continue;
}
else
continue;
}
}
else
{
// If the browsing connection is connected to this server and idle, use it
if (browsingOnSame && activeCount == 1)
{
pEngineData = m_engineData[0];
if (pEngineData->active)
continue;
}
else
continue;
}
}
CFileItem* newFileItem = currentServerItem->GetIdleChild(m_activeMode == 1, wantedDirection);
while (newFileItem && newFileItem->Download() && newFileItem->GetType() == QueueItemType_Folder)
{
wxFileName fn(newFileItem->GetLocalFile(), _T(""));
wxFileName::Mkdir(fn.GetPath(), 0777, wxPATH_MKDIR_FULL);
CState* const pState = m_pMainFrame->GetState();
pState->RefreshLocalFile(fn.GetFullPath());
if (RemoveItem(newFileItem, true))
{
// Server got deleted. Unfortunately we have to start over now
if (m_serverList.empty())
return false;
return true;
}
newFileItem = currentServerItem->GetIdleChild(m_activeMode == 1, wantedDirection);
}
if (!newFileItem)
continue;
if (!bestMatch.fileItem || newFileItem->GetPriority() > bestMatch.fileItem->GetPriority())
{
bestMatch.serverItem = currentServerItem;
bestMatch.fileItem = newFileItem;
bestMatch.pEngineData = pEngineData;
if (newFileItem->GetPriority() == priority_highest)
break;
}
}
if (!bestMatch.fileItem)
return false;
// Find idle engine
t_EngineData* pEngineData;
if (bestMatch.pEngineData)
pEngineData = bestMatch.pEngineData;
else
{
pEngineData = GetIdleEngine(&bestMatch.serverItem->GetServer());
if (!pEngineData)
return false;
}
// Now we have both inactive engine and file.
// Assign the file to the engine.
bestMatch.fileItem->SetActive(true);
pEngineData->pItem = bestMatch.fileItem;
bestMatch.fileItem->m_pEngineData = pEngineData;
pEngineData->active = true;
delete pEngineData->m_idleDisconnectTimer;
pEngineData->m_idleDisconnectTimer = 0;
bestMatch.serverItem->m_activeCount++;
m_activeCount++;
if (bestMatch.fileItem->Download())
m_activeCountDown++;
else
m_activeCountUp++;
const CServer oldServer = pEngineData->lastServer;
pEngineData->lastServer = bestMatch.serverItem->GetServer();
if (!pEngineData->pEngine)
{
pEngineData->state = t_EngineData::waitprimary;
}
else if (!pEngineData->pEngine->IsConnected())
{
if (pEngineData->lastServer.GetLogonType() == ASK)
{
if (CLoginManager::Get().GetPassword(pEngineData->lastServer, true))
pEngineData->state = t_EngineData::connect;
else
pEngineData->state = t_EngineData::askpassword;
}
else
pEngineData->state = t_EngineData::connect;
}
else if (oldServer != bestMatch.serverItem->GetServer())
pEngineData->state = t_EngineData::disconnect;
else if (pEngineData->pItem->GetType() == QueueItemType_File)
pEngineData->state = t_EngineData::transfer;
else
pEngineData->state = t_EngineData::mkdir;
if (bestMatch.fileItem->GetType() == QueueItemType_File)
{
// Create status line
m_itemCount++;
SetItemCount(m_itemCount);
int lineIndex = GetItemIndex(bestMatch.fileItem);
UpdateSelections_ItemAdded(lineIndex + 1);
wxRect rect;
GetItemRect(lineIndex + 1, rect);
m_allowBackgroundErase = false;
if (!pEngineData->pStatusLineCtrl)
pEngineData->pStatusLineCtrl = new CStatusLineCtrl(this, pEngineData, rect);
else
{
pEngineData->pStatusLineCtrl->SetTransferStatus(0);
pEngineData->pStatusLineCtrl->SetSize(rect);
pEngineData->pStatusLineCtrl->Show();
}
m_allowBackgroundErase = true;
m_statusLineList.push_back(pEngineData->pStatusLineCtrl);
}
SendNextCommand(*pEngineData);
return true;
}
void CQueueView::ProcessReply(t_EngineData& engineData, COperationNotification* pNotification)
{
if (pNotification->nReplyCode & FZ_REPLY_DISCONNECTED &&
pNotification->commandId == cmd_none)
{
// Queue is not interested in disconnect notifications
return;
}
wxASSERT(pNotification->commandId != cmd_none);
// Cancel pending requests
m_pAsyncRequestQueue->ClearPending(engineData.pEngine);
// Process reply from the engine
int replyCode = pNotification->nReplyCode;
if ((replyCode & FZ_REPLY_CANCELED) == FZ_REPLY_CANCELED)
{
enum ResetReason reason;
if (engineData.pItem && engineData.pItem->m_remove)
reason = remove;
else
reason = reset;
engineData.pItem->m_statusMessage = _("Interrupted by user");
ResetEngine(engineData, reason);
return;
}
// Cycle through queue states
switch (engineData.state)
{
case t_EngineData::disconnect:
if (engineData.active)
{
engineData.state = t_EngineData::connect;
engineData.pStatusLineCtrl->SetTransferStatus(0);
}
else
engineData.state = t_EngineData::none;
break;
case t_EngineData::connect:
if (replyCode == FZ_REPLY_OK)
{
if (engineData.pItem->GetType() == QueueItemType_File)
engineData.state = t_EngineData::transfer;
else
engineData.state = t_EngineData::mkdir;
if (engineData.active && engineData.pStatusLineCtrl)
engineData.pStatusLineCtrl->SetTransferStatus(0);
}
else
{
if (replyCode & FZ_REPLY_PASSWORDFAILED)
CLoginManager::Get().CachedPasswordFailed(engineData.lastServer);
if ((replyCode & FZ_REPLY_CANCELED) == FZ_REPLY_CANCELED)
engineData.pItem->m_statusMessage = _T("");
else if (replyCode & FZ_REPLY_PASSWORDFAILED)
engineData.pItem->m_statusMessage = _("Incorrect password");
else if ((replyCode & FZ_REPLY_TIMEOUT) == FZ_REPLY_TIMEOUT)
engineData.pItem->m_statusMessage = _("Timeout");
else if (replyCode & FZ_REPLY_DISCONNECTED)
engineData.pItem->m_statusMessage = _("Disconnected from server");
else
engineData.pItem->m_statusMessage = _("Connection attempt failed");
if (!IncreaseErrorCount(engineData))
return;
}
break;
case t_EngineData::transfer:
if (replyCode == FZ_REPLY_OK)
{
ResetEngine(engineData, success);
return;
}
// Increase error count only if item didn't make any progress. This keeps
// user interaction at a minimum if connection is unstable.
// FIXME: Disabled since detection isn't reliable (yet)
//else if (!engineData.pStatusLineCtrl || !engineData.pStatusLineCtrl->MadeProgress())
{
if ((replyCode & FZ_REPLY_CANCELED) == FZ_REPLY_CANCELED)
engineData.pItem->m_statusMessage = _T("");
else if ((replyCode & FZ_REPLY_TIMEOUT) == FZ_REPLY_TIMEOUT)
engineData.pItem->m_statusMessage = _("Timeout");
else if (replyCode & FZ_REPLY_DISCONNECTED)
engineData.pItem->m_statusMessage = _("Disconnected from server");
else if ((replyCode & FZ_REPLY_CRITICALERROR) == FZ_REPLY_CRITICALERROR)
{
engineData.pItem->m_statusMessage = _("Could not start transfer");
ResetEngine(engineData, failure);
return;
}
else
engineData.pItem->m_statusMessage = _("Could not start transfer");
if (!IncreaseErrorCount(engineData))
return;
if (replyCode & FZ_REPLY_DISCONNECTED && &engineData == m_engineData[0])
{
ResetEngine(engineData, retry);
return;
}
}
if (replyCode & FZ_REPLY_DISCONNECTED)
engineData.state = t_EngineData::connect;
break;
case t_EngineData::mkdir:
if (replyCode == FZ_REPLY_OK)
{
ResetEngine(engineData, success);
return;
}
if (replyCode & FZ_REPLY_DISCONNECTED)
{
if (!IncreaseErrorCount(engineData))
return;
if (&engineData == m_engineData[0])
{
ResetEngine(engineData, retry);
return;
}
engineData.state = t_EngineData::connect;
}
else
{
// Cannot retry
ResetEngine(engineData, failure);
return;
}
break;
case t_EngineData::list:
ResetEngine(engineData, remove);
return;
default:
return;
}
if (engineData.state == t_EngineData::connect && engineData.lastServer.GetLogonType() == ASK)
{
if (!CLoginManager::Get().GetPassword(engineData.lastServer, true))
engineData.state = t_EngineData::askpassword;
}
if (!m_activeMode)
{
enum ResetReason reason;
if (engineData.pItem && engineData.pItem->m_remove)
reason = remove;
else
reason = reset;
ResetEngine(engineData, reason);
return;
}
SendNextCommand(engineData);
}
void CQueueView::ResetEngine(t_EngineData& data, const enum ResetReason reason)
{
if (!data.active)
return;
m_waitStatusLineUpdate = true;
if (data.pItem)
{
if (data.pItem->GetType() == QueueItemType_File)
{
wxASSERT(data.pStatusLineCtrl);
for (std::list<CStatusLineCtrl*>::iterator iter = m_statusLineList.begin(); iter != m_statusLineList.end(); iter++)
{
if (*iter == data.pStatusLineCtrl)
{
m_statusLineList.erase(iter);
break;
}
}
m_allowBackgroundErase = false;
data.pStatusLineCtrl->Hide();
m_allowBackgroundErase = true;
UpdateSelections_ItemRemoved(GetItemIndex(data.pItem) + 1);
m_itemCount--;
SetItemCount(m_itemCount);
const CFileItem* const pFileItem = (CFileItem*)data.pItem;
if (pFileItem->Download())
m_pMainFrame->GetState()->RefreshLocalFile(pFileItem->GetLocalFile());
}
wxASSERT(data.pItem->IsActive());
wxASSERT(data.pItem->m_pEngineData == &data);
if (data.pItem->IsActive())
data.pItem->SetActive(false);
if (data.pItem->Download())
{
wxASSERT(m_activeCountDown > 0);
if (m_activeCountDown > 0)
m_activeCountDown--;
}
else
{
wxASSERT(m_activeCountUp > 0);
if (m_activeCountUp > 0)
m_activeCountUp--;
}
if (reason == reset)
{
if (!data.pItem->Queued())
reinterpret_cast<CServerItem*>(data.pItem->GetTopLevelItem())->QueueImmediateFile(data.pItem);
}
else if (reason == failure)
{
if (data.pItem->GetType() == QueueItemType_File || data.pItem->GetType() == QueueItemType_Folder)
{
const CServer server = ((CServerItem*)data.pItem->GetTopLevelItem())->GetServer();
RemoveItem(data.pItem, false);
CQueueViewFailed* pQueueViewFailed = m_pQueue->GetQueueView_Failed();
CServerItem* pServerItem = pQueueViewFailed->CreateServerItem(server);
data.pItem->SetParent(pServerItem);
pQueueViewFailed->InsertItem(pServerItem, data.pItem);
pQueueViewFailed->CommitChanges();
}
}
else if (reason == success)
{
if (data.pItem->GetType() == QueueItemType_File || data.pItem->GetType() == QueueItemType_Folder)
{
CQueueViewSuccessful* pQueueViewSuccessful = m_pQueue->GetQueueView_Successful();
if (pQueueViewSuccessful->AutoClear())
RemoveItem(data.pItem, true);
else
{
const CServer server = ((CServerItem*)data.pItem->GetTopLevelItem())->GetServer();
RemoveItem(data.pItem, false);
CServerItem* pServerItem = pQueueViewSuccessful->CreateServerItem(server);
data.pItem->SetParent(pServerItem);
pQueueViewSuccessful->InsertItem(pServerItem, data.pItem);
pQueueViewSuccessful->CommitChanges();
}
}
else
RemoveItem(data.pItem, true);
}
else if (reason == retry)
{
}
else
RemoveItem(data.pItem, true);
data.pItem = 0;
wxASSERT(m_activeCount > 0);
if (m_activeCount > 0)
m_activeCount--;
}
data.active = false;
if (data.state == t_EngineData::waitprimary)
{
CCommandQueue* pCommandQueue = m_pMainFrame->GetState()->m_pCommandQueue;
if (pCommandQueue)
pCommandQueue->RequestExclusiveEngine(false);
}
data.state = t_EngineData::none;
CServerItem* item = GetServerItem(data.lastServer);
if (item)
{
wxASSERT(item->m_activeCount > 0);
if (item->m_activeCount > 0)
item->m_activeCount--;
}
AdvanceQueue();
m_waitStatusLineUpdate = false;
UpdateStatusLinePositions();
}
bool CQueueView::RemoveItem(CQueueItem* item, bool destroy, bool updateItemCount /*=true*/, bool updateSelections /*=true*/)
{
// RemoveItem assumes that the item has already been removed from all engines
if (item->GetType() == QueueItemType_File)
{
// Update size information
const CFileItem* const pFileItem = (const CFileItem* const)item;
const wxLongLong& size = pFileItem->GetSize();
if (size < 0)
{
m_filesWithUnknownSize--;
wxASSERT(m_filesWithUnknownSize >= 0);
if (!m_filesWithUnknownSize && updateItemCount)
DisplayQueueSize();
}
else if (size > 0)
{
m_totalQueueSize -= size;
if (updateItemCount)
DisplayQueueSize();
wxASSERT(m_totalQueueSize >= 0);
}
}
bool didRemoveParent = CQueueViewBase::RemoveItem(item, destroy, updateItemCount, updateSelections);
UpdateStatusLinePositions();
return didRemoveParent;
}
void CQueueView::SendNextCommand(t_EngineData& engineData)
{
while (true)
{
if (engineData.state == t_EngineData::waitprimary)
{
engineData.pItem->m_statusMessage = _("Waiting for browsing connection");
CState* const pState = m_pMainFrame->GetState();
CCommandQueue* pCommandQueue = pState->m_pCommandQueue;
pCommandQueue->RequestExclusiveEngine(true);
return;
}
if (engineData.state == t_EngineData::disconnect)
{
engineData.pItem->m_statusMessage = _("Disconnecting from previous server");
RefreshItem(engineData.pItem);
if (engineData.pEngine->Command(CDisconnectCommand()) == FZ_REPLY_WOULDBLOCK)
return;
engineData.state = t_EngineData::connect;
if (engineData.active && engineData.pStatusLineCtrl)
engineData.pStatusLineCtrl->SetTransferStatus(0);
}
if (engineData.state == t_EngineData::askpassword)
{
engineData.pItem->m_statusMessage = _("Waiting for password");
RefreshItem(engineData.pItem);
if (m_waitingForPassword.empty())
{
wxCommandEvent evt(fzEVT_ASKFORPASSWORD);
wxPostEvent(this, evt);
}
m_waitingForPassword.push_back(engineData.pEngine);
return;
}
if (engineData.state == t_EngineData::connect)
{
engineData.pItem->m_statusMessage = _("Connecting");
RefreshItem(engineData.pItem);
int res = engineData.pEngine->Command(CConnectCommand(engineData.lastServer));
wxASSERT((res & FZ_REPLY_BUSY) != FZ_REPLY_BUSY);
if (res == FZ_REPLY_WOULDBLOCK)
return;
if (res == FZ_REPLY_ALREADYCONNECTED)
{
engineData.state = t_EngineData::disconnect;
continue;
}
if (res == FZ_REPLY_OK)
{
if (engineData.pItem->GetType() == QueueItemType_File)
{
engineData.state = t_EngineData::transfer;
if (engineData.active)
engineData.pStatusLineCtrl->SetTransferStatus(0);
}
else
engineData.state = t_EngineData::mkdir;
break;
}
if (!IncreaseErrorCount(engineData))
return;
continue;
}
if (engineData.state == t_EngineData::transfer)
{
CFileItem* fileItem = engineData.pItem;
fileItem->m_statusMessage = _("Transferring");
RefreshItem(engineData.pItem);
int res = engineData.pEngine->Command(CFileTransferCommand(fileItem->GetLocalFile(), fileItem->GetRemotePath(),
fileItem->GetRemoteFile(), fileItem->Download(), fileItem->m_transferSettings));
wxASSERT((res & FZ_REPLY_BUSY) != FZ_REPLY_BUSY);
if (res == FZ_REPLY_WOULDBLOCK)
return;
if (res == FZ_REPLY_NOTCONNECTED)
{
if (&engineData == m_engineData[0])
{
ResetEngine(engineData, retry);
return;
}
engineData.state = t_EngineData::connect;
continue;
}
if (res == FZ_REPLY_OK)
{
ResetEngine(engineData, success);
return;
}
if (!IncreaseErrorCount(engineData))
return;
continue;
}
if (engineData.state == t_EngineData::mkdir)
{
CFileItem* fileItem = engineData.pItem;
fileItem->m_statusMessage = _("Creating directory");
RefreshItem(engineData.pItem);
int res = engineData.pEngine->Command(CMkdirCommand(fileItem->GetRemotePath()));
wxASSERT((res & FZ_REPLY_BUSY) != FZ_REPLY_BUSY);
if (res == FZ_REPLY_WOULDBLOCK)
return;
if (res == FZ_REPLY_NOTCONNECTED)
{
if (&engineData == m_engineData[0])
{
ResetEngine(engineData, retry);
return;
}
engineData.state = t_EngineData::connect;
continue;
}
if (res == FZ_REPLY_OK)
{
ResetEngine(engineData, success);
return;
}
// Pointless to retry
ResetEngine(engineData, failure);
return;
}
}
}
bool CQueueView::SetActive(bool active /*=true*/)
{
if (!active)
{
m_activeMode = 0;
for (std::vector<CServerItem*>::iterator iter = m_serverList.begin(); iter != m_serverList.end(); iter++)
(*iter)->QueueImmediateFiles();
UpdateStatusLinePositions();
// Send active engines the cancel command
unsigned int engineIndex;
for (engineIndex = 0; engineIndex < m_engineData.size(); engineIndex++)
{
t_EngineData* const pEngineData = m_engineData[engineIndex];
if (!pEngineData->active)
continue;
if (pEngineData->state == t_EngineData::waitprimary)
{
if (pEngineData->pItem)
pEngineData->pItem->m_statusMessage = _("Interrupted by user");
ResetEngine(*pEngineData, reset);
}
else
{
wxASSERT(pEngineData->pEngine);
if (!pEngineData->pEngine)
continue;
pEngineData->pEngine->Command(CCancelCommand());
}
}
return m_activeCount == 0;
}
else
{
m_activeMode = 2;
m_waitStatusLineUpdate = true;
AdvanceQueue();
m_waitStatusLineUpdate = false;
UpdateStatusLinePositions();
}
return true;
}
bool CQueueView::Quit()
{
m_quit = true;
#ifdef __WXMSW__
if (m_actionAfterWarnDialog)
{
m_actionAfterWarnDialog->Destroy();
m_actionAfterWarnDialog = 0;
}
delete m_actionAfterTimer;
m_actionAfterTimer = 0;
#endif
bool canQuit = true;
if (!SetActive(false))
canQuit = false;
for (unsigned int i = 0; i < 2; i++)
{
if (!m_queuedFolders[i].empty())
{
canQuit = false;
for (std::list<CFolderScanItem*>::iterator iter = m_queuedFolders[i].begin(); iter != m_queuedFolders[i].end(); iter++)
(*iter)->m_remove = true;
}
}
if (m_pFolderProcessingThread)
canQuit = false;
if (!canQuit)
return false;
DeleteEngines();
SaveQueue();
COptions::Get()->SaveColumnWidths(this, OPTION_QUEUE_COLUMN_WIDTHS);
return true;
}
void CQueueView::CheckQueueState()
{
if (!m_engineData.empty() && !m_engineData[0]->active && m_engineData[0]->pEngine)
{
CCommandQueue* pCommandQueue = m_pMainFrame->GetState()->m_pCommandQueue;
if (pCommandQueue)
pCommandQueue->ReleaseEngine();
m_engineData[0]->pEngine = 0;
}
if (m_activeCount)
return;
if (m_activeMode)
{
m_activeMode = 0;
/* Users don't seem to like this, so comment it out for now.
* maybe make it configureable in future?
if (!m_pQueue->GetSelection())
{
CQueueViewBase* pFailed = m_pQueue->GetQueueView_Failed();
CQueueViewBase* pSuccessful = m_pQueue->GetQueueView_Successful();
if (pFailed->GetItemCount())
m_pQueue->SetSelection(1);
else if (pSuccessful->GetItemCount())
m_pQueue->SetSelection(2);
}
*/
if (!m_quit)
ActionAfter();
}
if (m_quit)
m_pMainFrame->Close();
}
bool CQueueView::IncreaseErrorCount(t_EngineData& engineData)
{
engineData.pItem->m_errorCount++;
if (engineData.pItem->m_errorCount <= COptions::Get()->GetOptionVal(OPTION_RECONNECTCOUNT))
return true;
ResetEngine(engineData, failure);
return false;
}
void CQueueView::UpdateStatusLinePositions()
{
if (m_waitStatusLineUpdate)
return;
m_lastTopItem = GetTopItem();
int bottomItem = m_lastTopItem + GetCountPerPage();
for (std::list<CStatusLineCtrl*>::iterator iter = m_statusLineList.begin(); iter != m_statusLineList.end(); iter++)
{
CStatusLineCtrl *pCtrl = *iter;
int index = GetItemIndex(pCtrl->GetItem()) + 1;
if (index < m_lastTopItem || index > bottomItem)
{
pCtrl->Show(false);
continue;
}
wxRect rect;
if (!GetItemRect(index, rect))
{
pCtrl->Show(false);
continue;
}
m_allowBackgroundErase = bottomItem + 1 >= m_itemCount;
pCtrl->SetSize(rect);
m_allowBackgroundErase = false;
pCtrl->Show();
m_allowBackgroundErase = true;
}
}
void CQueueView::CalculateQueueSize()
{
// Collect total queue size
m_totalQueueSize = 0;
m_fileCount = 0;
m_folderScanCount = 0;
m_filesWithUnknownSize = 0;
for (std::vector<CServerItem*>::const_iterator iter = m_serverList.begin(); iter != m_serverList.end(); iter++)
m_totalQueueSize += (*iter)->GetTotalSize(m_filesWithUnknownSize, m_fileCount, m_folderScanCount);
DisplayQueueSize();
DisplayNumberQueuedFiles();
}
void CQueueView::DisplayQueueSize()
{
wxStatusBar *pStatusBar = m_pMainFrame->GetStatusBar();
if (!pStatusBar)
return;
wxString queueSize;
wxLongLong totalSize = m_totalQueueSize;
if (totalSize == 0 && m_filesWithUnknownSize == 0)
queueSize = _("Queue: empty");
if (totalSize > (1000 * 1000))
{
totalSize /= 1000 * 1000;
queueSize.Printf(_("Queue: %s%d MB"), (m_filesWithUnknownSize > 0) ? _T(">") : _T(""), totalSize.GetLo());
}
else if (totalSize > 1000)
{
totalSize /= 1000;
queueSize.Printf(_("Queue: %s%d KB"), (m_filesWithUnknownSize > 0) ? _T(">") : _T(""), totalSize.GetLo());
}
else
queueSize.Printf(_("Queue: %s%d bytes"), (m_filesWithUnknownSize > 0) ? _T(">") : _T(""), totalSize.GetLo());
pStatusBar->SetStatusText(queueSize, 4);
}
bool CQueueView::QueueFolder(bool queueOnly, bool download, const wxString& localPath, const CServerPath& remotePath, const CServer& server)
{
CServerItem* pServerItem = CreateServerItem(server);
CFolderScanItem* folderItem = new CFolderScanItem(pServerItem, queueOnly, download, localPath, remotePath);
InsertItem(pServerItem, folderItem);
folderItem->m_statusMessage = _("Waiting");
CommitChanges();
m_queuedFolders[download ? 0 : 1].push_back(folderItem);
ProcessFolderItems();
Refresh(false);
return true;
}
bool CQueueView::ProcessFolderItems(int type /*=-1*/)
{
if (type == -1)
{
while (ProcessFolderItems(0));
ProcessUploadFolderItems();
return true;
}
return false;
}
void CQueueView::ProcessUploadFolderItems()
{
if (m_queuedFolders[1].empty())
{
if (m_quit)
m_pMainFrame->Close();
return;
}
if (m_pFolderProcessingThread)
return;
CFolderScanItem* pItem = m_queuedFolders[1].front();
if (pItem->Queued())
pItem->m_statusMessage = _("Scanning for files to add to queue");
else
pItem->m_statusMessage = _("Scanning for files to upload");
RefreshItem(pItem);
pItem->m_active = true;
m_pFolderProcessingThread = new CFolderProcessingThread(this, pItem);
m_pFolderProcessingThread->Create();
m_pFolderProcessingThread->Run();
Refresh(false);
}
void CQueueView::OnFolderThreadComplete(wxCommandEvent& event)
{
if (!m_pFolderProcessingThread)
return;
wxASSERT(!m_queuedFolders[1].empty());
CFolderScanItem* pItem = m_queuedFolders[1].front();
m_queuedFolders[1].pop_front();
RemoveItem(pItem, true);
m_pFolderProcessingThread->Wait();
delete m_pFolderProcessingThread;
m_pFolderProcessingThread = 0;
ProcessUploadFolderItems();
}
bool CQueueView::QueueFiles(const std::list<t_newEntry> &entryList, bool queueOnly, bool download, CServerItem* pServerItem, const int defaultFileExistsAction)
{
wxASSERT(pServerItem);
for (std::list<t_newEntry>::const_iterator iter = entryList.begin(); iter != entryList.end(); iter++)
{
const t_newEntry& entry = *iter;
CFileItem* fileItem;
if (entry.localFile != _T(""))
{
fileItem = new CFileItem(pServerItem, queueOnly, download, entry.localFile, entry.remoteFile, entry.remotePath, entry.size);
fileItem->m_transferSettings.binary = ShouldUseBinaryMode(download ? entry.remoteFile : wxFileName(entry.localFile).GetFullName());
fileItem->m_defaultFileExistsAction = defaultFileExistsAction;
}
else
fileItem = new CFolderItem(pServerItem, queueOnly, entry.remotePath, _T(""));
InsertItem(pServerItem, fileItem);
}
CommitChanges();
if (!m_activeMode && !queueOnly)
m_activeMode = 1;
m_waitStatusLineUpdate = true;
AdvanceQueue();
m_waitStatusLineUpdate = false;
UpdateStatusLinePositions();
Refresh(false);
return true;
}
void CQueueView::SaveQueue()
{
// We have to synchronize access to queue.xml so that multiple processed don't write
// to the same file or one is reading while the other one writes.
CInterProcessMutex mutex(MUTEX_QUEUE);
wxFileName file(wxGetApp().GetSettingsDir(), _T("queue.xml"));
TiXmlElement* pDocument = GetXmlFile(file);
if (!pDocument)
{
wxString msg = wxString::Format(_("Could not load \"%s\", please make sure the file is valid and can be accessed.\nThe queue will not be saved."), file.GetFullPath().c_str());
wxMessageBox(msg, _("Error loading xml file"), wxICON_ERROR);
return;
}
WriteToFile(pDocument);
if (!pDocument->GetDocument()->SaveFile(file.GetFullPath().mb_str()))
{
wxString msg = wxString::Format(_("Could not write \"%s\", the queue could not be saved."), file.GetFullPath().c_str());
wxMessageBox(msg, _("Error writing xml file"), wxICON_ERROR);
}
delete pDocument->GetDocument();
}
void CQueueView::LoadQueue()
{
// We have to synchronize access to queue.xml so that multiple processed don't write
// to the same file or one is reading while the other one writes.
CInterProcessMutex mutex(MUTEX_QUEUE);
wxFileName file(wxGetApp().GetSettingsDir(), _T("queue.xml"));
TiXmlElement* pDocument = GetXmlFile(file);
if (!pDocument)
{
wxString msg = wxString::Format(_("Could not load \"%s\", please make sure the file is valid and can be accessed.\nThe queue will not be saved."), file.GetFullPath().c_str());
wxMessageBox(msg, _("Error loading xml file"), wxICON_ERROR);
return;
}
TiXmlElement* pQueue = pDocument->FirstChildElement("Queue");
if (!pQueue)
{
delete pDocument->GetDocument();
return;
}
ImportQueue(pQueue, false);
pDocument->RemoveChild(pQueue);
if (!pDocument->GetDocument()->SaveFile(file.GetFullPath().mb_str()))
{
wxString msg = wxString::Format(_("Could not write \"%s\", the queue could not be saved."), file.GetFullPath().c_str());
wxMessageBox(msg, _("Error writing xml file"), wxICON_ERROR);
}
delete pDocument->GetDocument();
}
void CQueueView::ImportQueue(TiXmlElement* pElement, bool updateSelections)
{
TiXmlElement* pServer = pElement->FirstChildElement("Server");
while (pServer)
{
CServer server;
if (GetServer(pServer, server))
{
m_insertionStart = -1;
m_insertionCount = 0;
CServerItem *pServerItem = CreateServerItem(server);
for (TiXmlElement* pFile = pServer->FirstChildElement("File"); pFile; pFile = pFile->NextSiblingElement("File"))
{
wxString localFile = GetTextElement(pFile, "LocalFile");
wxString remoteFile = GetTextElement(pFile, "RemoteFile");
wxString safeRemotePath = GetTextElement(pFile, "RemotePath");
bool download = GetTextElementInt(pFile, "Download") != 0;
wxLongLong size = GetTextElementLongLong(pFile, "Size", -1);
unsigned int errorCount = GetTextElementInt(pFile, "ErrorCount");
unsigned int priority = GetTextElementInt(pFile, "Priority", priority_normal);
unsigned int itemState = GetTextElementInt(pFile, "ItemState", ItemState_Wait);
bool binary = GetTextElementInt(pFile, "TransferMode", 1) != 0;
CServerPath remotePath;
if (localFile != _T("") && remoteFile != _T("") && remotePath.SetSafePath(safeRemotePath) &&
size >= -1 && priority < PRIORITY_COUNT &&
(itemState == ItemState_Wait || itemState == ItemState_Error))
{
CFileItem* fileItem = new CFileItem(pServerItem, true, download, localFile, remoteFile, remotePath, size);
fileItem->m_transferSettings.binary = binary;
fileItem->SetPriorityRaw((enum QueuePriority)priority);
fileItem->SetItemState((enum ItemState)itemState);
fileItem->m_errorCount = errorCount;
InsertItem(pServerItem, fileItem);
}
}
for (TiXmlElement* pFolder = pServer->FirstChildElement("Folder"); pFolder; pFolder = pFolder->NextSiblingElement("Folder"))
{
CFolderItem* folderItem;
bool download = GetTextElementInt(pFolder, "Download") != 0;
if (download)
{
wxString localFile = GetTextElement(pFolder, "LocalFile");
if (localFile == _T(""))
continue;
folderItem = new CFolderItem(pServerItem, true, localFile);
}
else
{
wxString remoteFile = GetTextElement(pFolder, "RemoteFile");
wxString safeRemotePath = GetTextElement(pFolder, "RemotePath");
if (safeRemotePath == _T(""))
continue;
CServerPath remotePath;
if (!remotePath.SetSafePath(safeRemotePath))
continue;
folderItem = new CFolderItem(pServerItem, true, remotePath, remoteFile);
}
unsigned int priority = GetTextElementInt(pFolder, "Priority", priority_normal);
if (priority >= PRIORITY_COUNT)
{
delete folderItem;
continue;
}
folderItem->SetPriority((enum QueuePriority)priority);
InsertItem(pServerItem, folderItem);
}
if (!pServerItem->GetChild(0))
{
m_itemCount--;
m_serverList.pop_back();
delete pServerItem;
}
else if (updateSelections)
CommitChanges();
}
pServer = pServer->NextSiblingElement("Server");
}
if (!updateSelections)
{
m_insertionStart = -1;
m_insertionCount = 0;
CommitChanges();
}
else
Refresh();
}
void CQueueView::SettingsChanged()
{
m_asciiFiles.clear();
wxString extensions = COptions::Get()->GetOption(OPTION_ASCIIFILES);
wxString ext;
int pos = extensions.Find(_T("|"));
while (pos != -1)
{
if (!pos)
{
if (ext != _T(""))
{
ext.Replace(_T("\\\\"), _T("\\"));
m_asciiFiles.push_back(ext);
ext = _T("");
}
}
else if (extensions.c_str()[pos - 1] != '\\')
{
ext += extensions.Left(pos);
ext.Replace(_T("\\\\"), _T("\\"));
m_asciiFiles.push_back(ext);
ext = _T("");
}
else
{
ext += extensions.Left(pos - 1) + _T("|");
}
extensions = extensions.Mid(pos + 1);
pos = extensions.Find(_T("|"));
}
ext += extensions;
ext.Replace(_T("\\\\"), _T("\\"));
m_asciiFiles.push_back(ext);
}
bool CQueueView::ShouldUseBinaryMode(wxString filename)
{
int mode = COptions::Get()->GetOptionVal(OPTION_ASCIIBINARY);
if (mode == 1)
return false;
else if (mode == 2)
return true;
int pos = filename.Find('.');
if (pos == -1)
return COptions::Get()->GetOptionVal(OPTION_ASCIINOEXT) != 0;
else if (!pos)
return COptions::Get()->GetOptionVal(OPTION_ASCIIDOTFILE) != 0;
wxString ext = filename;
do
{
ext = ext.Mid(pos + 1);
}
while ((pos = ext.Find('.')) != -1);
if (ext == _T(""))
return true;
for (std::list<wxString>::const_iterator iter = m_asciiFiles.begin(); iter != m_asciiFiles.end(); iter++)
if (!ext.CmpNoCase(*iter))
return false;
return true;
}
void CQueueView::OnScrollEvent(wxScrollWinEvent& event)
{
event.Skip();
wxCommandEvent evt(fzEVT_UPDATE_STATUSLINES, wxID_ANY);
AddPendingEvent(evt);
}
void CQueueView::OnUpdateStatusLines(wxCommandEvent& event)
{
if (GetTopItem() != m_lastTopItem)
UpdateStatusLinePositions();
}
void CQueueView::OnMouseWheel(wxMouseEvent& event)
{
event.Skip();
wxCommandEvent evt(fzEVT_UPDATE_STATUSLINES, wxID_ANY);
AddPendingEvent(evt);
}
void CQueueView::OnContextMenu(wxContextMenuEvent& event)
{
wxMenu* pMenu = wxXmlResource::Get()->LoadMenu(_T("ID_MENU_QUEUE"));
if (!pMenu)
return;
pMenu->Check(XRCID("ID_PROCESSQUEUE"), IsActive() ? true : false);
pMenu->Check(XRCID("ID_ACTIONAFTER_DISABLE"), IsActionAfter(ActionAfterState_Disabled));
pMenu->Check(XRCID("ID_ACTIONAFTER_CLOSE"), IsActionAfter(ActionAfterState_Close));
pMenu->Check(XRCID("ID_ACTIONAFTER_DISCONNECT"), IsActionAfter(ActionAfterState_Disconnect));
pMenu->Check(XRCID("ID_ACTIONAFTER_RUNCOMMAND"), IsActionAfter(ActionAfterState_RunCommand));
pMenu->Check(XRCID("ID_ACTIONAFTER_SHOWMESSAGE"), IsActionAfter(ActionAfterState_ShowMessage));
pMenu->Check(XRCID("ID_ACTIONAFTER_PLAYSOUND"), IsActionAfter(ActionAfterState_PlaySound));
#ifdef __WXMSW__
pMenu->Check(XRCID("ID_ACTIONAFTER_REBOOT"), IsActionAfter(ActionAfterState_Reboot));
pMenu->Check(XRCID("ID_ACTIONAFTER_SHUTDOWN"), IsActionAfter(ActionAfterState_Shutdown));
#endif
pMenu->Enable(XRCID("ID_REMOVE"), GetNextItem(-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED) != -1);
const bool hasSelection = GetNextItem(-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED) != -1;
pMenu->Enable(XRCID("ID_PRIORITY"), hasSelection);
pMenu->Enable(XRCID("ID_DEFAULT_FILEEXISTSACTION"), hasSelection);
#ifdef __WXMSW__
pMenu->Enable(XRCID("ID_ACTIONAFTER"), m_actionAfterWarnDialog == NULL);
#endif
PopupMenu(pMenu);
delete pMenu;
}
void CQueueView::OnProcessQueue(wxCommandEvent& event)
{
SetActive(event.IsChecked());
}
void CQueueView::OnStopAndClear(wxCommandEvent& event)
{
SetActive(false);
RemoveAll();
}
void CQueueView::OnActionAfter(wxCommandEvent& event)
{
if (!event.IsChecked() || event.GetId() == XRCID("ID_ACTIONAFTER_DISABLE"))
{ // Goes from checked to non-checked or disable is pressed
m_actionAfterState = ActionAfterState_Disabled;
m_actionAfterRunCommand = _T("");
#ifdef __WXMSW__
if (m_actionAfterWarnDialog)
{
m_actionAfterWarnDialog->Destroy();
m_actionAfterWarnDialog = 0;
}
delete m_actionAfterTimer;
m_actionAfterTimer = 0;
#endif
}
else if (event.GetId() == XRCID("ID_ACTIONAFTER_DISABLE"))
m_actionAfterState = ActionAfterState_Disabled;
else if (event.GetId() == XRCID("ID_ACTIONAFTER_CLOSE"))
m_actionAfterState = ActionAfterState_Close;
else if (event.GetId() == XRCID("ID_ACTIONAFTER_DISCONNECT"))
m_actionAfterState = ActionAfterState_Disconnect;
else if (event.GetId() == XRCID("ID_ACTIONAFTER_SHOWMESSAGE"))
m_actionAfterState = ActionAfterState_ShowMessage;
else if (event.GetId() == XRCID("ID_ACTIONAFTER_PLAYSOUND"))
m_actionAfterState = ActionAfterState_PlaySound;
else if (event.GetId() == XRCID("ID_ACTIONAFTER_RUNCOMMAND"))
{
m_actionAfterState = ActionAfterState_RunCommand;
wxTextEntryDialog dlg(m_pMainFrame, _("Please enter a path and executable to run.\nE.g. c:\\somePath\\file.exe under MS Windows or /somePath/file under Unix.\nYou can also optionally specify program arguments."), _("Enter command"));
if (dlg.ShowModal() != wxID_OK)
{
m_actionAfterState = ActionAfterState_Disabled;
return;
}
const wxString &command = dlg.GetValue();
if (command == _T(""))
{
wxMessageBox(_("No command given, aborting."), _("Empty command"), wxICON_ERROR, m_pMainFrame);
m_actionAfterState = ActionAfterState_Disabled;
return;
}
m_actionAfterRunCommand = command;
}
#ifdef __WXMSW__
else if (event.GetId() == XRCID("ID_ACTIONAFTER_REBOOT"))
m_actionAfterState = ActionAfterState_Reboot;
else if (event.GetId() == XRCID("ID_ACTIONAFTER_SHUTDOWN"))
m_actionAfterState = ActionAfterState_Shutdown;
#endif
}
void CQueueView::RemoveAll()
{
// This function removes all inactive items and queues active items
// for removal
// First, clear all selections
int item;
while ((item = GetNextItem(-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED)) != -1)
SetItemState(item, 0, wxLIST_STATE_SELECTED);
std::vector<CServerItem*> newServerList;
m_itemCount = 0;
for (std::vector<CServerItem*>::iterator iter = m_serverList.begin(); iter != m_serverList.end(); iter++)
{
if ((*iter)->TryRemoveAll())
delete *iter;
else
{
newServerList.push_back(*iter);
m_itemCount += 1 + (*iter)->GetChildrenCount(true);
}
}
SetItemCount(m_itemCount);
m_actionAfterState = ActionAfterState_Disabled;
m_serverList = newServerList;
UpdateStatusLinePositions();
CalculateQueueSize();
CheckQueueState();
Refresh();
}
void CQueueView::OnRemoveSelected(wxCommandEvent& event)
{
std::list<CQueueItem*> selectedItems;
long item = -1;
while (true)
{
item = GetNextItem(item, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
if (item == -1)
break;
selectedItems.push_front(GetQueueItem(item));
SetItemState(item, 0, wxLIST_STATE_SELECTED);
}
m_waitStatusLineUpdate = true;
while (!selectedItems.empty())
{
CQueueItem* pItem = selectedItems.front();
selectedItems.pop_front();
if (pItem->GetType() == QueueItemType_Status)
continue;
else if (pItem->GetType() == QueueItemType_FolderScan)
{
CFolderScanItem* pFolder = (CFolderScanItem*)pItem;
if (pFolder->m_active)
{
pFolder->m_remove = true;
continue;
}
}
else if (pItem->GetType() == QueueItemType_Server)
{
CServerItem* pServer = (CServerItem*)pItem;
StopItem(pServer);
// Server items get deleted automatically if all children are gone
continue;
}
else if (pItem->GetType() == QueueItemType_File ||
pItem->GetType() == QueueItemType_Folder)
{
CFileItem* pFile = (CFileItem*)pItem;
if (pFile->IsActive())
{
pFile->m_remove = true;
StopItem(pFile);
continue;
}
}
CQueueItem* pTopLevelItem = pItem->GetTopLevelItem();
if (!pTopLevelItem->GetChild(1))
{
// Parent will get deleted
// If next selected item is parent, remove it from list
if (!selectedItems.empty() && selectedItems.front() == pTopLevelItem)
selectedItems.pop_front();
}
RemoveItem(pItem, true, false, false);
}
DisplayNumberQueuedFiles();
DisplayQueueSize();
SetItemCount(m_itemCount);
m_waitStatusLineUpdate = false;
UpdateStatusLinePositions();
Refresh();
}
bool CQueueView::StopItem(CFileItem* item)
{
if (!item->IsActive())
return true;
((CServerItem*)item->GetTopLevelItem())->QueueImmediateFile(item);
if (item->m_pEngineData->state == t_EngineData::waitprimary)
{
enum ResetReason reason;
if (item->m_pEngineData->pItem && item->m_pEngineData->pItem->m_remove)
reason = remove;
else
reason = reset;
if (item->m_pEngineData->pItem)
item->m_pEngineData->pItem->m_statusMessage = _T("");
ResetEngine(*item->m_pEngineData, reason);
return true;
}
else
{
item->m_pEngineData->pEngine->Command(CCancelCommand());
return false;
}
}
bool CQueueView::StopItem(CServerItem* pServerItem)
{
std::list<CQueueItem*> items;
for (unsigned int i = 0; i < pServerItem->GetChildrenCount(false); i++)
items.push_back(pServerItem->GetChild(i, false));
for (std::list<CQueueItem*>::reverse_iterator iter = items.rbegin(); iter != items.rend(); iter++)
{
CQueueItem* pItem = *iter;
if (pItem->GetType() == QueueItemType_FolderScan)
{
CFolderScanItem* pFolder = (CFolderScanItem*)pItem;
if (pFolder->m_active)
{
pFolder->m_remove = true;
continue;
}
}
else if (pItem->GetType() == QueueItemType_File ||
pItem->GetType() == QueueItemType_Folder)
{
CFileItem* pFile = (CFileItem*)pItem;
if (pFile->IsActive())
{
pFile->m_remove = true;
StopItem(pFile);
continue;
}
}
else
// Unknown type, shouldn't be here.
wxASSERT(false);
if (RemoveItem(pItem, true, false))
{
DisplayNumberQueuedFiles();
SetItemCount(m_itemCount);
return true;
}
}
DisplayNumberQueuedFiles();
SetItemCount(m_itemCount);
return false;
}
void CQueueView::OnFolderThreadFiles(wxCommandEvent& event)
{
if (!m_pFolderProcessingThread)
return;
wxASSERT(!m_queuedFolders[1].empty());
CFolderScanItem* pItem = m_queuedFolders[1].front();
std::list<t_newEntry> entryList;
m_pFolderProcessingThread->GetFiles(entryList);
QueueFiles(entryList, pItem->Queued(), false, (CServerItem*)pItem->GetTopLevelItem(), pItem->m_defaultFileExistsAction);
pItem->m_count += entryList.size();
pItem->m_statusMessage = wxString::Format(_("%d files added to queue"), pItem->GetCount());
RefreshItem(pItem);
}
void CQueueView::SetDefaultFileExistsAction(int action, const enum TransferDirection direction)
{
for (std::vector<CServerItem*>::iterator iter = m_serverList.begin(); iter != m_serverList.end(); iter++)
(*iter)->SetDefaultFileExistsAction(action, direction);
}
void CQueueView::OnSetDefaultFileExistsAction(wxCommandEvent &event)
{
if (GetNextItem(-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED) == -1)
return;
CDefaultFileExistsDlg dlg;
if (!dlg.Load(this, true))
return;
int downloadAction, uploadAction;
if (!dlg.Run(&downloadAction, &uploadAction))
return;
std::list<long> selectedItems;
long item = -1;
while (true)
{
item = GetNextItem(item, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
if (item == -1)
break;
CQueueItem* pItem = GetQueueItem(item);
if (!pItem)
continue;
switch (pItem->GetType())
{
case QueueItemType_FolderScan:
((CFolderScanItem*)pItem)->m_defaultFileExistsAction = uploadAction;
break;
case QueueItemType_File:
{
CFileItem *pFileItem = (CFileItem*)pItem;
if (pFileItem->Download())
pFileItem->m_defaultFileExistsAction = downloadAction;
else
pFileItem->m_defaultFileExistsAction = uploadAction;
}
break;
case QueueItemType_Server:
{
CServerItem *pServerItem = (CServerItem*)pItem;
pServerItem->SetDefaultFileExistsAction(downloadAction, download);
pServerItem->SetDefaultFileExistsAction(uploadAction, upload);
}
break;
default:
break;
}
}
}
t_EngineData* CQueueView::GetIdleEngine(const CServer* pServer /*=0*/)
{
t_EngineData* pFirstIdle = 0;
// We start with 1, since 0 is the primary connection
for (unsigned int i = 1; i < m_engineData.size(); i++)
{
if (m_engineData[i]->active)
continue;
if (!pServer)
return m_engineData[i];
if (m_engineData[i]->pEngine->IsConnected() && m_engineData[i]->lastServer == *pServer)
return m_engineData[i];
if (!pFirstIdle)
pFirstIdle = m_engineData[i];
}
return pFirstIdle;
}
void CQueueView::TryRefreshListings()
{
// TODO: This function is currently unused
if (m_quit)
return;
const CState* const pState = m_pMainFrame->GetState();
const CServer* const pServer = pState->GetServer();
if (!pServer)
return;
const CDirectoryListing* const pListing = pState->GetRemoteDir();
if (!pListing || !pListing->m_hasUnsureEntries)
return;
// See if there's an engine with is already listing
for (unsigned int i = 1; i < m_engineData.size(); i++)
{
if (!m_engineData[i]->active)
continue;
if (m_engineData[i]->pItem)
continue;
if (m_engineData[i]->pEngine->IsConnected() && m_engineData[i]->lastServer == *pServer)
{
// This engine is already listing a directory on the current server
return;
}
}
t_EngineData* pEngineData = GetIdleEngine(pServer);
if (!pEngineData)
return;
if (!pEngineData->pEngine->IsConnected() || pEngineData->lastServer != *pServer)
return;
CListCommand command(pListing->path);
int res = pEngineData->pEngine->Command(command);
if (res != FZ_REPLY_WOULDBLOCK)
return;
pEngineData->active = true;
pEngineData->state = t_EngineData::list;
}
void CQueueView::OnAskPassword(wxCommandEvent& event)
{
while (!m_waitingForPassword.empty())
{
const CFileZillaEngine* const pEngine = m_waitingForPassword.front();
std::vector<t_EngineData*>::iterator iter;
for (iter = m_engineData.begin(); iter != m_engineData.end(); iter++)
{
if ((*iter)->pEngine == pEngine)
break;
}
if (iter == m_engineData.end())
{
m_waitingForPassword.pop_front();
continue;
}
t_EngineData* pEngineData = *iter;
if (pEngineData->state != t_EngineData::askpassword)
{
m_waitingForPassword.pop_front();
continue;
}
static std::list<void*> AskPasswordBusyList;
if (CLoginManager::Get().GetPassword(pEngineData->lastServer, false))
{
pEngineData->state = t_EngineData::connect;
SendNextCommand(*pEngineData);
}
else
ResetEngine(*pEngineData, remove);
m_waitingForPassword.pop_front();
}
}
void CQueueView::OnFocusItemChanged(wxListEvent& event)
{
event.Skip();
wxCommandEvent evt(fzEVT_UPDATE_STATUSLINES, wxID_ANY);
AddPendingEvent(evt);
}
void CQueueView::UpdateItemSize(CFileItem* pItem, wxLongLong size)
{
wxASSERT(pItem);
const wxLongLong oldSize = pItem->GetSize();
if (size == oldSize)
return;
if (oldSize == -1)
{
wxASSERT(m_filesWithUnknownSize);
if (m_filesWithUnknownSize)
m_filesWithUnknownSize--;
}
else
{
wxASSERT(m_totalQueueSize > oldSize);
if (m_totalQueueSize > oldSize)
m_totalQueueSize -= oldSize;
else
m_totalQueueSize = 0;
}
if (size == -1)
m_filesWithUnknownSize++;
else
m_totalQueueSize += size;
pItem->SetSize(size);
DisplayQueueSize();
}
void CQueueView::AdvanceQueue()
{
static bool insideAdvanceQueue = false;
if (insideAdvanceQueue)
return;
insideAdvanceQueue = true;
while (TryStartNextTransfer())
{
}
// Set timer for connected, idle engines
for (unsigned int i = 1; i < m_engineData.size(); i++)
{
if (m_engineData[i]->active)
continue;
if (m_engineData[i]->m_idleDisconnectTimer)
{
if (m_engineData[i]->pEngine->IsConnected())
continue;
delete m_engineData[i]->m_idleDisconnectTimer;
m_engineData[i]->m_idleDisconnectTimer = 0;
}
else
{
if (!m_engineData[i]->pEngine->IsConnected())
continue;
m_engineData[i]->m_idleDisconnectTimer = new wxTimer(this);
m_engineData[i]->m_idleDisconnectTimer->Start(30000, true);
}
}
insideAdvanceQueue = false;
CheckQueueState();
}
void CQueueView::InsertItem(CServerItem* pServerItem, CQueueItem* pItem)
{
CQueueViewBase::InsertItem(pServerItem, pItem);
if (pItem->GetType() == QueueItemType_File)
{
CFileItem* pFileItem = (CFileItem*)pItem;
const wxLongLong& size = pFileItem->GetSize();
if (size < 0)
m_filesWithUnknownSize++;
else if (size > 0)
m_totalQueueSize += size;
}
}
void CQueueView::CommitChanges()
{
CQueueViewBase::CommitChanges();
DisplayQueueSize();
}
void CQueueView::OnTimer(wxTimerEvent& event)
{
const int id = event.GetId();
if (id == -1)
return;
#ifdef __WXMSW__
if (id == m_actionAfterTimerId)
{
OnActionAfterTimerTick();
return;
}
#endif
for (unsigned int i = 1; i < m_engineData.size(); i++)
{
t_EngineData* pData = m_engineData[i];
if (pData->m_idleDisconnectTimer && !pData->m_idleDisconnectTimer->IsRunning())
{
delete pData->m_idleDisconnectTimer;
pData->m_idleDisconnectTimer = 0;
if (pData->pEngine->IsConnected())
pData->pEngine->Command(CDisconnectCommand());
}
}
}
void CQueueView::DeleteEngines()
{
for (unsigned int engineIndex = 1; engineIndex < m_engineData.size(); engineIndex++)
{
t_EngineData* const data = m_engineData[engineIndex];
delete data->pEngine;
data->pEngine = 0;
delete data->m_idleDisconnectTimer;
data->m_idleDisconnectTimer = 0;
}
for (unsigned int engineIndex = 0; engineIndex < m_engineData.size(); engineIndex++)
delete m_engineData[engineIndex];
m_engineData.clear();
}
void CQueueView::WriteToFile(TiXmlElement* pElement) const
{
TiXmlElement* pQueue = pElement->FirstChildElement("Queue");
if (!pQueue)
{
pQueue = pElement->InsertEndChild(TiXmlElement("Queue"))->ToElement();
}
wxASSERT(pQueue);
for (std::vector<CServerItem*>::const_iterator iter = m_serverList.begin(); iter != m_serverList.end(); iter++)
(*iter)->SaveItem(pQueue);
}
void CQueueView::OnSetPriority(wxCommandEvent& event)
{
enum QueuePriority priority;
const int id = event.GetId();
if (id == XRCID("ID_PRIORITY_LOWEST"))
priority = priority_lowest;
else if (id == XRCID("ID_PRIORITY_LOW"))
priority = priority_low;
else if (id == XRCID("ID_PRIORITY_HIGH"))
priority = priority_high;
else if (id == XRCID("ID_PRIORITY_HIGHEST"))
priority = priority_highest;
else
priority = priority_normal;
CQueueItem* pSkip = 0;
long item = -1;
while (-1 != (item = GetNextItem(item, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED)))
{
CQueueItem* pItem = GetQueueItem(item);
if (!pItem)
continue;
if (pItem->GetType() == QueueItemType_Server)
pSkip = pItem;
else if (pItem->GetTopLevelItem() == pSkip)
continue;
else
pSkip = 0;
pItem->SetPriority(priority);
}
Refresh();
}
void CQueueView::OnExclusiveEngineRequestGranted(wxCommandEvent& event)
{
CCommandQueue* pCommandQueue = m_pMainFrame->GetState()->m_pCommandQueue;
if (!pCommandQueue)
return;
CFileZillaEngine* pEngine = pCommandQueue->GetEngineExclusive(event.GetId());
t_EngineData* pEngineData = m_engineData[0];
if (!pEngineData->active)
{
wxASSERT(!pEngineData->pEngine);
if (pEngine)
pCommandQueue->ReleaseEngine();
return;
}
if (!pEngine)
return;
wxASSERT(!pEngineData->pEngine);
wxASSERT(pEngineData->state == t_EngineData::waitprimary);
if (pEngineData->state != t_EngineData::waitprimary)
return;
const CServer* pCurrentServer = m_pMainFrame->GetState()->GetServer();
CServerItem* pServerItem = (CServerItem*)pEngineData->pItem->GetParent();
wxASSERT(pServerItem);
if (!pCurrentServer || *pCurrentServer != pServerItem->GetServer())
{
pCommandQueue->ReleaseEngine();
ResetEngine(*pEngineData, retry);
return;
}
if (pEngineData->pItem->GetType() == QueueItemType_File)
pEngineData->state = t_EngineData::transfer;
else
pEngineData->state = t_EngineData::mkdir;
pEngineData->pEngine = pEngine;
SendNextCommand(*pEngineData);
}
enum ActionAfterState CQueueView::GetActionAfterState() const
{
return m_actionAfterState;
}
bool CQueueView::IsActionAfter(enum ActionAfterState state)
{
return m_actionAfterState == state;
}
void CQueueView::ActionAfter(bool warned /*=false*/)
{
switch (m_actionAfterState)
{
case ActionAfterState_Close:
{
m_pMainFrame->Close();
break;
}
case ActionAfterState_Disconnect:
{
if (m_pMainFrame->GetState()->IsRemoteConnected() && m_pMainFrame->GetState()->IsRemoteIdle())
m_pMainFrame->GetState()->m_pCommandQueue->ProcessCommand(new CDisconnectCommand());
break;
}
case ActionAfterState_RunCommand:
{
wxExecute(m_actionAfterRunCommand);
break;
}
case ActionAfterState_ShowMessage:
{
wxMessageDialog* dialog = new wxMessageDialog(m_pMainFrame, _T("No more files in the queue!"), _T("Queue completion"), wxOK | wxICON_INFORMATION);
dialog->ShowModal();
m_pMainFrame->RequestUserAttention(wxUSER_ATTENTION_ERROR);
break;
}
case ActionAfterState_PlaySound:
{
wxSound(wxGetApp().GetResourceDir() + _T("finished.wav")).Play(wxSOUND_ASYNC);
break;
}
#ifdef __WXMSW__
case ActionAfterState_Reboot:
{
if (!warned)
ActionAfterWarnUser(_T("The system will soon reboot unless you press cancel."));
else
wxShutdown(wxSHUTDOWN_REBOOT);
break;
}
case ActionAfterState_Shutdown:
{
if (!warned)
ActionAfterWarnUser(_T("The system will soon shutdown unless you press cancel."));
else
wxShutdown(wxSHUTDOWN_POWEROFF);
break;
}
#endif
}
m_actionAfterState = ActionAfterState_Disabled; // Resetting the state.
}
#ifdef __WXMSW__
void CQueueView::ActionAfterWarnUser(wxString message)
{
if (m_actionAfterWarnDialog != NULL)
return;
m_actionAfterWarnDialog = new wxProgressDialog(_T("Queue completion dialog"), message, 150, m_pMainFrame, wxPD_CAN_ABORT | wxPD_AUTO_HIDE | wxPD_CAN_SKIP | wxPD_APP_MODAL);
wxSize dialogSize = m_actionAfterWarnDialog->GetSize();
m_actionAfterWarnDialog->SetSize(dialogSize.GetWidth() / 2, dialogSize.GetHeight());
m_actionAfterWarnDialog->CentreOnParent();
m_actionAfterWarnDialog->SetFocus();
m_pMainFrame->RequestUserAttention(wxUSER_ATTENTION_ERROR);
wxASSERT(!m_actionAfterTimer);
m_actionAfterTimer = new wxTimer(this, m_actionAfterTimerId);
m_actionAfterTimerId = m_actionAfterTimer->GetId();
m_actionAfterTimerCount = 0;
m_actionAfterTimer->Start(100, wxTIMER_CONTINUOUS);
}
void CQueueView::OnActionAfterTimerTick()
{
if (!m_actionAfterWarnDialog)
{
delete m_actionAfterTimer;
m_actionAfterTimer = 0;
return;
}
bool skipped = false;
if (m_actionAfterTimerCount > 150)
{
m_actionAfterWarnDialog->Destroy();
m_actionAfterWarnDialog = 0;
delete m_actionAfterTimer;
m_actionAfterTimer = 0;
ActionAfter(true);
}
else if (!m_actionAfterWarnDialog->Update(m_actionAfterTimerCount++, _T(""), &skipped))
{
// User has pressed cancel!
m_actionAfterState = ActionAfterState_Disabled; // resetting to disabled
m_actionAfterWarnDialog->Destroy();
m_actionAfterWarnDialog = 0;
delete m_actionAfterTimer;
m_actionAfterTimer = 0;
}
else if (skipped)
{
m_actionAfterWarnDialog->Destroy();
m_actionAfterWarnDialog = 0;
delete m_actionAfterTimer;
m_actionAfterTimer = 0;
ActionAfter(true);
}
}
#endif //__WXMSW__
syntax highlighted by Code2HTML, v. 0.9.1