#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 #include "dndobjects.h" #include "loginmanager.h" #include "aui_notebook_ex.h" #include "queueview_failed.h" #include "queueview_successful.h" #include "commandqueue.h" #include #include #include #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 &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 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& files = dataObject.GetFiles(); for (std::list::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::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(pNotification)); delete pNotification; break; case nId_operation: ProcessReply(*pEngineData, reinterpret_cast(pNotification)); delete pNotification; break; case nId_asyncrequest: if (!pEngineData->pItem) { delete pNotification; break; } switch (reinterpret_cast(pNotification)->GetRequestID()) { case reqId_fileexists: { // Set default file exists action CFileExistsNotification *pFileExistsNotification = reinterpret_cast(pNotification); pFileExistsNotification->overwriteAction = (enum CFileExistsNotification::OverwriteAction)pEngineData->pItem->m_defaultFileExistsAction; } break; default: break; } m_pAsyncRequestQueue->AddRequest(pEngineData->pEngine, reinterpret_cast(pNotification)); break; case nId_active: { CActiveNotification *pActiveNotification = reinterpret_cast(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(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::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::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(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::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::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::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::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 &entryList, bool queueOnly, bool download, CServerItem* pServerItem, const int defaultFileExistsAction) { wxASSERT(pServerItem); for (std::list::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::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 newServerList; m_itemCount = 0; for (std::vector::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 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 items; for (unsigned int i = 0; i < pServerItem->GetChildrenCount(false); i++) items.push_back(pServerItem->GetChild(i, false)); for (std::list::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 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::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 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::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 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::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__