#include "FileZilla.h" #include "queue.h" #include "queueview_failed.h" #include "queueview_successful.h" #include "Options.h" CQueueItem::CQueueItem() { m_visibleOffspring = 0; m_parent = 0; m_maxCachedIndex = -1; } CQueueItem::~CQueueItem() { std::vector::iterator iter; for (iter = m_children.begin(); iter != m_children.end(); iter++) delete *iter; } void CQueueItem::SetPriority(enum QueuePriority priority) { std::vector::iterator iter; for (iter = m_children.begin(); iter != m_children.end(); iter++) (*iter)->SetPriority(priority); } void CQueueItem::AddChild(CQueueItem* item) { item->m_indent = m_indent + _T(" "); m_children.push_back(item); m_visibleOffspring += 1 + item->m_visibleOffspring; m_maxCachedIndex = -1; CQueueItem* parent = GetParent(); while (parent) { parent->m_visibleOffspring += 1 + item->m_visibleOffspring; parent->m_maxCachedIndex = -1; parent = parent->GetParent(); } } CQueueItem* CQueueItem::GetChild(unsigned int item, bool recursive /*=true*/) { std::vector::iterator iter; if (!recursive) { if (item >= m_children.size()) return 0; iter = m_children.begin(); iter += item; return *iter; } if ((int)item <= m_maxCachedIndex) { // Index is cached iter = m_children.begin(); iter += m_lookupCache[item].child; item -= m_lookupCache[item].index; if (!item) return *iter; else return (*iter)->GetChild(item - 1); } int index; int child; iter = m_children.begin(); if (m_maxCachedIndex == -1) { child = 0; index = 0; } else { // Start with loop with the last cached item index iter += m_lookupCache[m_maxCachedIndex].child + 1; item -= m_maxCachedIndex + 1; index = m_maxCachedIndex + 1; child = m_lookupCache[m_maxCachedIndex].child + 1; } for (; iter != m_children.end(); iter++, child++) { if (!item) return *iter; unsigned int count = (*iter)->GetChildrenCount(true); if (item > count) { if (m_maxCachedIndex == -1 && m_lookupCache.size() < (unsigned int)m_visibleOffspring) m_lookupCache.resize(m_visibleOffspring); for (unsigned int k = index; k <= index + count; k++) { m_lookupCache[k].child = child; m_lookupCache[k].index = index; } m_maxCachedIndex = index + count; item -= count + 1; index += count + 1; continue; } return (*iter)->GetChild(item - 1); } return 0; } unsigned int CQueueItem::GetChildrenCount(bool recursive) { if (!recursive) return m_children.size(); return m_visibleOffspring; } bool CQueueItem::RemoveChild(CQueueItem* pItem, bool destroy /*=true*/) { int oldVisibleOffspring = m_visibleOffspring; std::vector::iterator iter; bool deleted = false; for (iter = m_children.begin(); iter != m_children.end(); iter++) { if (*iter == pItem) { m_visibleOffspring -= 1; m_visibleOffspring -= pItem->m_visibleOffspring; if (destroy) delete pItem; m_children.erase(iter); deleted = true; break; } int visibleOffspring = (*iter)->m_visibleOffspring; if ((*iter)->RemoveChild(pItem, destroy)) { m_visibleOffspring -= visibleOffspring - (*iter)->m_visibleOffspring; if (!(*iter)->m_children.size()) { m_visibleOffspring -= 1; delete *iter; m_children.erase(iter); } deleted = true; break; } } if (!deleted) return false; m_maxCachedIndex = -1; // Propagate new children count to parent CQueueItem* parent = GetParent(); while (parent) { parent->m_maxCachedIndex = -1; parent->m_visibleOffspring -= oldVisibleOffspring - m_visibleOffspring; parent = parent->GetParent(); } return true; } bool CQueueItem::TryRemoveAll() { const int oldVisibleOffspring = m_visibleOffspring; std::vector::iterator iter; std::vector keepChildren; m_visibleOffspring = 0; for (iter = m_children.begin(); iter != m_children.end(); iter++) { CQueueItem* pItem = *iter; if (pItem->TryRemoveAll()) delete pItem; else { keepChildren.push_back(pItem); m_visibleOffspring++; m_visibleOffspring += pItem->GetChildrenCount(true); } } m_children = keepChildren; m_maxCachedIndex = -1; CQueueItem* parent = GetParent(); while (parent) { parent->m_maxCachedIndex = -1; parent->m_visibleOffspring -= oldVisibleOffspring - m_visibleOffspring; parent = parent->GetParent(); } return m_children.empty(); } CQueueItem* CQueueItem::GetTopLevelItem() { if (!m_parent) return this; CQueueItem* newParent = m_parent; CQueueItem* parent = 0; while (newParent) { parent = newParent; newParent = newParent->GetParent(); } return parent; } const CQueueItem* CQueueItem::GetTopLevelItem() const { if (!m_parent) return this; const CQueueItem* newParent = m_parent; const CQueueItem* parent = 0; while (newParent) { parent = newParent; newParent = newParent->GetParent(); } return parent; } int CQueueItem::GetItemIndex() const { const CQueueItem* pParent = GetParent(); if (!pParent) return 0; int index = 1; for (std::vector::const_iterator iter = pParent->m_children.begin(); iter != pParent->m_children.end(); iter++) { if (*iter == this) break; index += (*iter)->GetChildrenCount(true) + 1; } return index + pParent->GetItemIndex(); } CFileItem::CFileItem(CServerItem* parent, bool queued, bool download, const wxString& localFile, const wxString& remoteFile, const CServerPath& remotePath, wxLongLong size) { m_parent = parent; m_priority = priority_normal; m_download = download; m_localFile = localFile; m_remoteFile = remoteFile; m_remotePath = remotePath; m_size = size; m_itemState = ItemState_Wait; m_queued = queued; m_active = false; m_errorCount = 0; m_remove = false; m_pEngineData = 0; m_defaultFileExistsAction = -1; } CFileItem::~CFileItem() { } void CFileItem::SetPriority(enum QueuePriority priority) { if (priority == m_priority) return; CServerItem* parent = (CServerItem*)m_parent; parent->SetChildPriority(this, m_priority, priority); m_priority = priority; } void CFileItem::SetPriorityRaw(enum QueuePriority priority) { m_priority = priority; } enum ItemState CFileItem::GetItemState() const { return m_itemState; } void CFileItem::SetItemState(const enum ItemState itemState) { m_itemState = itemState; } enum QueuePriority CFileItem::GetPriority() const { return m_priority; } void CFileItem::SetActive(const bool active) { if (active && !m_active) { AddChild(new CStatusItem); } else if (!active && m_active) { CQueueItem* pItem = m_children.front(); RemoveChild(pItem); } m_active = active; } void CFileItem::SaveItem(TiXmlElement* pElement) const { if (GetItemState() == ItemState_Complete) return; //TODO: Save error items? TiXmlElement file("File"); AddTextElement(&file, "LocalFile", m_localFile); AddTextElement(&file, "RemoteFile", m_remoteFile); AddTextElement(&file, "RemotePath", m_remotePath.GetSafePath()); AddTextElement(&file, "Download", m_download ? _T("1") : _T("0")); if (m_size != -1) AddTextElement(&file, "Size", m_size.ToString()); if (m_errorCount) AddTextElement(&file, "ErrorCount", m_errorCount); AddTextElement(&file, "Priority", m_priority); if (m_itemState) AddTextElement(&file, "ItemState", m_itemState); AddTextElement(&file, "TransferMode", m_transferSettings.binary ? _T("1") : _T("0")); pElement->InsertEndChild(file); } bool CFileItem::TryRemoveAll() { if (!IsActive()) return true; m_remove = true; return false; } CFolderItem::CFolderItem(CServerItem* parent, bool queued, const wxString& localFile) : CFileItem(parent, queued, true, localFile, _T(""), CServerPath(), -1) { } CFolderItem::CFolderItem(CServerItem* parent, bool queued, const CServerPath& remotePath, const wxString& remoteFile) : CFileItem(parent, queued, false, _T(""), remoteFile, remotePath, -1) { } void CFolderItem::SaveItem(TiXmlElement* pElement) const { if (GetItemState() == ItemState_Complete || GetItemState() == ItemState_Error) return; TiXmlElement file("Folder"); if (m_download) AddTextElement(&file, "LocalFile", m_localFile); else { AddTextElement(&file, "RemoteFile", m_remoteFile); AddTextElement(&file, "RemotePath", m_remotePath.GetSafePath()); } AddTextElement(&file, "Download", m_download ? _T("1") : _T("0")); pElement->InsertEndChild(file); } void CFolderItem::SetActive(const bool active) { m_active = active; } CServerItem::CServerItem(const CServer& server) { m_server = server; m_activeCount = 0; } CServerItem::~CServerItem() { } const CServer& CServerItem::GetServer() const { return m_server; } wxString CServerItem::GetName() const { return m_server.FormatServer(); } void CServerItem::AddChild(CQueueItem* pItem) { CQueueItem::AddChild(pItem); if (pItem->GetType() == QueueItemType_File || pItem->GetType() == QueueItemType_Folder) AddFileItemToList((CFileItem*)pItem); } void CServerItem::AddFileItemToList(CFileItem* pItem) { if (!pItem) return; m_fileList[pItem->m_queued ? 0 : 1][pItem->GetPriority()].push_back(pItem); } void CServerItem::RemoveFileItemFromList(CFileItem* pItem) { std::list& fileList = m_fileList[pItem->m_queued ? 0 : 1][pItem->GetPriority()]; for (std::list::iterator iter = fileList.begin(); iter != fileList.end(); iter++) { if (*iter == pItem) { fileList.erase(iter); return; } } wxFAIL_MSG(_T("File item not deleted from m_fileList")); } void CServerItem::SetDefaultFileExistsAction(int action, const enum TransferDirection direction) { for (std::vector::iterator iter = m_children.begin(); iter != m_children.end(); iter++) { CQueueItem *pItem = *iter; if (pItem->GetType() == QueueItemType_File) { CFileItem* pFileItem = ((CFileItem *)pItem); if (direction == upload && pFileItem->Download()) continue; else if (direction == download && !pFileItem->Download()) continue; pFileItem->m_defaultFileExistsAction = action; } else if (pItem->GetType() == QueueItemType_FolderScan) { if (direction == download) continue; ((CFolderScanItem *)pItem)->m_defaultFileExistsAction = action; } } } CFileItem* CServerItem::GetIdleChild(bool immediateOnly, enum TransferDirection direction) { int i = 0; for (i = (PRIORITY_COUNT - 1); i >= 0; i--) { std::list& fileList = m_fileList[1][i]; for (std::list::iterator iter = fileList.begin(); iter != fileList.end(); iter++) { CFileItem* item = *iter; if (item->IsActive()) continue; if (direction == both) return item; if (direction == download) { if (item->Download()) return item; } else if (!item->Download()) return item; } } if (immediateOnly) return 0; for (i = (PRIORITY_COUNT - 1); i >= 0; i--) { std::list& fileList = m_fileList[0][i]; for (std::list::iterator iter = fileList.begin(); iter != fileList.end(); iter++) { CFileItem* item = *iter; if (item->IsActive()) continue; if (direction == both) return item; if (direction == download) { if (item->Download()) return item; } else if (!item->Download()) return item; } } return 0; } bool CServerItem::RemoveChild(CQueueItem* pItem, bool destroy /*=true*/) { if (!pItem) return false; if (pItem->GetType() == QueueItemType_File || pItem->GetType() == QueueItemType_Folder) { CFileItem* pFileItem = reinterpret_cast(pItem); RemoveFileItemFromList(pFileItem); } return CQueueItem::RemoveChild(pItem, destroy); } void CServerItem::QueueImmediateFiles() { for (int i = 0; i < PRIORITY_COUNT; i++) { std::list activeList; std::list& fileList = m_fileList[1][i]; for (std::list::reverse_iterator iter = fileList.rbegin(); iter != fileList.rend(); iter++) { CFileItem* item = *iter; wxASSERT(!item->m_queued); if (item->IsActive()) activeList.push_front(item); else { item->m_queued = true; m_fileList[0][i].push_front(item); } } fileList = activeList; } } void CServerItem::QueueImmediateFile(CFileItem* pItem) { if (pItem->m_queued) return; std::list& fileList = m_fileList[1][pItem->GetPriority()]; for (std::list::iterator iter = fileList.begin(); iter != fileList.end(); iter++) { if (*iter != pItem) continue; pItem->m_queued = true; fileList.erase(iter); m_fileList[0][pItem->GetPriority()].push_front(pItem); return; } wxASSERT(false); } void CServerItem::SaveItem(TiXmlElement* pElement) const { TiXmlElement server("Server"); SetServer(&server, m_server); for (std::vector::const_iterator iter = m_children.begin(); iter != m_children.end(); iter++) (*iter)->SaveItem(&server); pElement->InsertEndChild(server); } wxLongLong CServerItem::GetTotalSize(int& filesWithUnknownSize, int& queuedFiles, int& folderScanCount) const { wxLongLong totalSize = 0; for (int i = 0; i < PRIORITY_COUNT; i++) { for (int j = 0; j < 2; j++) { const std::list& fileList = m_fileList[j][i]; for (std::list::const_iterator iter = fileList.begin(); iter != fileList.end(); iter++) { const CFileItem* item = *iter; if (item->GetItemState() != ItemState_Complete) { wxLongLong size = item->GetSize(); if (size >= 0) totalSize += size; else filesWithUnknownSize++; } } } } for (std::vector::const_iterator iter = m_children.begin(); iter != m_children.end(); iter++) { if ((*iter)->GetType() == QueueItemType_File || (*iter)->GetType() == QueueItemType_Folder) queuedFiles++; else if ((*iter)->GetType() == QueueItemType_FolderScan) folderScanCount++; } return totalSize; } bool CServerItem::TryRemoveAll() { const int oldVisibleOffspring = m_visibleOffspring; std::vector::iterator iter; std::vector keepChildren; m_visibleOffspring = 0; for (iter = m_children.begin(); iter != m_children.end(); iter++) { CQueueItem* pItem = *iter; if (pItem->TryRemoveAll()) { if (pItem->GetType() == QueueItemType_File || pItem->GetType() == QueueItemType_Folder) { CFileItem* pFileItem = reinterpret_cast(pItem); RemoveFileItemFromList(pFileItem); } delete pItem; } else { keepChildren.push_back(pItem); m_visibleOffspring++; m_visibleOffspring += pItem->GetChildrenCount(true); } } m_children = keepChildren; CQueueItem* parent = GetParent(); while (parent) { parent->m_visibleOffspring -= oldVisibleOffspring - m_visibleOffspring; parent = parent->GetParent(); } return m_children.empty(); } void CServerItem::DetachChildren() { wxASSERT(!m_activeCount); m_children.clear(); m_visibleOffspring = 0; m_maxCachedIndex = -1; for (int i = 0; i < 2; i++) for (int j = 0; j < PRIORITY_COUNT; j++) m_fileList[i][j].clear(); } void CServerItem::SetPriority(enum QueuePriority priority) { std::vector::iterator iter; for (iter = m_children.begin(); iter != m_children.end(); iter++) { if ((*iter)->GetType() == QueueItemType_File) ((CFileItem*)(*iter))->SetPriorityRaw(priority); else (*iter)->SetPriority(priority); } for (int i = 0; i < 2; i++) for (int j = 0; j < PRIORITY_COUNT; j++) if (j != priority) m_fileList[i][priority].splice(m_fileList[i][priority].end(), m_fileList[i][j]); } void CServerItem::SetChildPriority(CFileItem* pItem, enum QueuePriority oldPriority, enum QueuePriority newPriority) { int i = pItem->Queued() ? 0 : 1; for (std::list::iterator iter = m_fileList[i][oldPriority].begin(); iter != m_fileList[i][oldPriority].end(); iter++) { if (*iter != pItem) continue; m_fileList[i][oldPriority].erase(iter); m_fileList[i][newPriority].push_back(pItem); return; } wxFAIL; } CFolderScanItem::CFolderScanItem(CServerItem* parent, bool queued, bool download, const wxString& localPath, const CServerPath& remotePath) { m_parent = parent; m_download = download; m_localPath = localPath; m_remotePath = remotePath; m_queued = queued; m_remove = false; m_active = false; m_count = 0; t_dirPair pair; pair.localPath = localPath.c_str(); pair.remotePath.SetSafePath(remotePath.GetSafePath().c_str()); m_dirsToCheck.push_back(pair); m_defaultFileExistsAction = -1; } bool CFolderScanItem::TryRemoveAll() { if (!m_active) return true; m_remove = true; return false; } // -------------- // CQueueViewBase // -------------- BEGIN_EVENT_TABLE(CQueueViewBase, wxListCtrl) EVT_ERASE_BACKGROUND(CQueueViewBase::OnEraseBackground) EVT_NAVIGATION_KEY(CQueueViewBase::OnNavigationKey) EVT_CHAR(CQueueViewBase::OnChar) EVT_LIST_COL_END_DRAG(wxID_ANY, CQueueViewBase::OnEndColumnDrag) END_EVENT_TABLE() CQueueViewBase::CQueueViewBase(CQueue* parent, int index, const wxString& title) : wxListCtrl(parent, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxCLIP_CHILDREN | wxLC_REPORT | wxLC_VIRTUAL | wxSUNKEN_BORDER | wxTAB_TRAVERSAL), m_pageIndex(index), m_title(title) { m_pQueue = parent; m_insertionStart = -1; m_insertionCount = 0; m_itemCount = 0; m_allowBackgroundErase = true; m_fileCount = 0; m_folderScanCount = 0; m_fileCountChanged = false; m_folderScanCountChanged = false; // Create and assign the image list for the queue wxImageList* pImageList = new wxImageList(16, 16); pImageList->Add(wxArtProvider::GetBitmap(_T("ART_SERVER"), wxART_OTHER, wxSize(16, 16))); pImageList->Add(wxArtProvider::GetBitmap(_T("ART_FILE"), wxART_OTHER, wxSize(16, 16))); pImageList->Add(wxArtProvider::GetBitmap(_T("ART_FOLDERCLOSED"), wxART_OTHER, wxSize(16, 16))); pImageList->Add(wxArtProvider::GetBitmap(_T("ART_FOLDER"), wxART_OTHER, wxSize(16, 16))); AssignImageList(pImageList, wxIMAGE_LIST_SMALL); } CQueueViewBase::~CQueueViewBase() { for (std::vector::iterator iter = m_serverList.begin(); iter != m_serverList.end(); iter++) delete *iter; } CQueueItem* CQueueViewBase::GetQueueItem(unsigned int item) { std::vector::iterator iter; for (iter = m_serverList.begin(); iter != m_serverList.end(); iter++) { if (!item) return *iter; unsigned int count = (*iter)->GetChildrenCount(true); if (item > count) { item -= count + 1; continue; } return (*iter)->GetChild(item - 1); } return 0; } int CQueueViewBase::GetItemIndex(const CQueueItem* item) { const CQueueItem* pTopLevelItem = item->GetTopLevelItem(); int index = 0; for (std::vector::const_iterator iter = m_serverList.begin(); iter != m_serverList.end(); iter++) { if (pTopLevelItem == *iter) break; index += (*iter)->GetChildrenCount(true) + 1; } return index + item->GetItemIndex(); } void CQueueViewBase::OnEraseBackground(wxEraseEvent& event) { if (m_allowBackgroundErase) event.Skip(); } // Defined in LocalListView.cpp extern wxString FormatSize(const wxLongLong& size); wxString CQueueViewBase::OnGetItemText(long item, long column) const { CQueueViewBase* pThis = const_cast(this); CQueueItem* pItem = pThis->GetQueueItem(item); if (!pItem) return _T(""); switch (pItem->GetType()) { case QueueItemType_Server: { CServerItem* pServerItem = reinterpret_cast(pItem); if (!column) return pServerItem->GetName(); } break; case QueueItemType_File: { CFileItem* pFileItem = reinterpret_cast(pItem); switch (column) { case 0: return pFileItem->GetIndent() + pFileItem->GetLocalFile(); case 1: if (pFileItem->Download()) if (pFileItem->Queued()) return _T("<--"); else return _T("<<--"); else if (pFileItem->Queued()) return _T("-->"); else return _T("-->>"); break; case 2: return pFileItem->GetRemotePath().FormatFilename(pFileItem->GetRemoteFile()); case 3: { const wxLongLong& size = pFileItem->GetSize(); if (size >= 0) return FormatSize(size); else return _T("?"); } case 4: switch (pFileItem->GetPriority()) { case 0: return _("Lowest"); case 1: return _("Low"); default: case 2: return _("Normal"); case 3: return _("High"); case 4: return _("Highest"); } break; case 5: return pFileItem->m_statusMessage; default: break; } } break; case QueueItemType_FolderScan: { CFolderScanItem* pFolderItem = reinterpret_cast(pItem); switch (column) { case 0: return _T(" ") + pFolderItem->GetLocalPath(); case 1: if (pFolderItem->Download()) if (pFolderItem->Queued()) return _T("<--"); else return _T("<<--"); else if (pFolderItem->Queued()) return _T("-->"); else return _T("-->>"); break; case 2: return pFolderItem->GetRemotePath().GetPath(); case 5: return pFolderItem->m_statusMessage; default: break; } } break; case QueueItemType_Folder: { CFileItem* pFolderItem = reinterpret_cast(pItem); switch (column) { case 0: if (pFolderItem->Download()) return pFolderItem->GetIndent() + pFolderItem->GetLocalFile(); break; case 1: if (pFolderItem->Download()) if (pFolderItem->Queued()) return _T("<--"); else return _T("<<--"); else if (pFolderItem->Queued()) return _T("-->"); else return _T("-->>"); break; case 2: if (!pFolderItem->Download()) { if (pFolderItem->GetRemoteFile() == _T("")) return pFolderItem->GetRemotePath().GetPath(); else return pFolderItem->GetRemotePath().FormatFilename(pFolderItem->GetRemoteFile()); } break; case 4: switch (pFolderItem->GetPriority()) { case 0: return _("Lowest"); case 1: return _("Low"); default: case 2: return _("Normal"); case 3: return _("High"); case 4: return _("Highest"); } break; case 5: return pFolderItem->m_statusMessage; default: break; } } break; default: break; } return _T(""); } int CQueueViewBase::OnGetItemImage(long item) const { CQueueViewBase* pThis = const_cast(this); CQueueItem* pItem = pThis->GetQueueItem(item); if (!pItem) return -1; switch (pItem->GetType()) { case QueueItemType_Server: return 0; case QueueItemType_File: return 1; case QueueItemType_FolderScan: case QueueItemType_Folder: return 3; default: return -1; } return -1; } void CQueueViewBase::UpdateSelections_ItemAdded(int added) { // This is the fastest algorithm I can think of to move all // selections. Though worst case is still O(n), as with every algorithm to // move selections. // Go through all items, keep record of the previous selected item int item = GetNextItem(-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED); while (item != -1 && item < added) item = GetNextItem(item, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED); int prevItem = -1; while (item != -1) { if (prevItem != -1) { if (prevItem + 1 != item) { // Previous selected item was not the direct predecessor // That means we have to select the successor of prevItem // and unselect current item SetItemState(prevItem + 1, wxLIST_STATE_SELECTED, wxLIST_STATE_SELECTED); SetItemState(item, 0, wxLIST_STATE_SELECTED); } } else { // First selected item, no predecessor yet. We have to unselect SetItemState(item, 0, wxLIST_STATE_SELECTED); } prevItem = item; item = GetNextItem(item, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED); } if (prevItem != -1 && prevItem < m_itemCount - 1) { // Move the very last selected item SetItemState(prevItem + 1, wxLIST_STATE_SELECTED, wxLIST_STATE_SELECTED); } SetItemState(added, 0, wxLIST_STATE_SELECTED); } void CQueueViewBase::UpdateSelections_ItemRangeAdded(int added, int count) { std::list itemsToSelect; // Go through all selected items int item = GetNextItem(-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED); while (item != -1 && item < added) item = GetNextItem(item, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED); while (item != -1) { // Select new items preceeding to current one while (!itemsToSelect.empty() && itemsToSelect.front() < item) { SetItemState(itemsToSelect.front(), wxLIST_STATE_SELECTED, wxLIST_STATE_SELECTED); itemsToSelect.pop_front(); } if (itemsToSelect.empty()) SetItemState(item, 0, wxLIST_STATE_SELECTED); else if (itemsToSelect.front() == item) itemsToSelect.pop_front(); itemsToSelect.push_back(item + count); item = GetNextItem(item, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED); } for (std::list::const_iterator iter = itemsToSelect.begin(); iter != itemsToSelect.end(); iter++) SetItemState(*iter, wxLIST_STATE_SELECTED, wxLIST_STATE_SELECTED); } void CQueueViewBase::UpdateSelections_ItemRemoved(int removed) { SetItemState(removed, 0, wxLIST_STATE_SELECTED); int prevItem = -1; int item = GetNextItem(-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED); while (item != -1 && item < removed) item = GetNextItem(item, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED); while (item != -1) { if (prevItem != -1) { if (prevItem + 1 != item) { // Previous selected item was not the direct predecessor // That means we have to select our predecessor and unselect // prevItem SetItemState(prevItem, 0, wxLIST_STATE_SELECTED); SetItemState(item - 1, wxLIST_STATE_SELECTED, wxLIST_STATE_SELECTED); } } else { // First selected item, no predecessor yet. We have to unselect SetItemState(item - 1, wxLIST_STATE_SELECTED, wxLIST_STATE_SELECTED); } prevItem = item; item = GetNextItem(item, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED); } if (prevItem != -1) { SetItemState(prevItem, 0, wxLIST_STATE_SELECTED); } } void CQueueViewBase::UpdateSelections_ItemRangeRemoved(int removed, int count) { SetItemState(removed, 0, wxLIST_STATE_SELECTED); std::list itemsToUnselect; int item = GetNextItem(-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED); while (item != -1 && item < removed) item = GetNextItem(item, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED); while (item != -1) { // Unselect new items preceeding to current one while (!itemsToUnselect.empty() && itemsToUnselect.front() < item - count) { SetItemState(itemsToUnselect.front(), 0, wxLIST_STATE_SELECTED); itemsToUnselect.pop_front(); } if (itemsToUnselect.empty()) SetItemState(item - count, wxLIST_STATE_SELECTED, wxLIST_STATE_SELECTED); else if (itemsToUnselect.front() == item - count) itemsToUnselect.pop_front(); else SetItemState(item - count, wxLIST_STATE_SELECTED, wxLIST_STATE_SELECTED); itemsToUnselect.push_back(item); item = GetNextItem(item, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED); } for (std::list::const_iterator iter = itemsToUnselect.begin(); iter != itemsToUnselect.end(); iter++) SetItemState(*iter, 0, wxLIST_STATE_SELECTED); } void CQueueViewBase::CreateColumns(const wxString& lastColumnName) { static unsigned long widths[6] = { 180, 60, 180, 80, 60, 150 }; static bool widths_loaded = false; if (!widths_loaded) { widths_loaded = true; if (!wxGetKeyState(WXK_SHIFT) || !wxGetKeyState(WXK_ALT) || !wxGetKeyState(WXK_CONTROL)) COptions::Get()->ReadColumnWidths(OPTION_QUEUE_COLUMN_WIDTHS, 6, widths); } InsertColumn(0, _("Server / Local file"), wxLIST_FORMAT_LEFT, widths[0]); InsertColumn(1, _("Direction"), wxLIST_FORMAT_CENTER, widths[1]); InsertColumn(2, _("Remote file"), wxLIST_FORMAT_LEFT, widths[2]); InsertColumn(3, _("Size"), wxLIST_FORMAT_RIGHT, widths[3]); InsertColumn(4, _("Priority"), wxLIST_FORMAT_LEFT, widths[4]); if (lastColumnName != _T("")) InsertColumn(5, lastColumnName, wxLIST_FORMAT_LEFT, widths[5]); } CServerItem* CQueueViewBase::GetServerItem(const CServer& server) { for (std::vector::iterator iter = m_serverList.begin(); iter != m_serverList.end(); iter++) { if ((*iter)->GetServer() == server) return *iter; } return NULL; } CServerItem* CQueueViewBase::CreateServerItem(const CServer& server) { CServerItem* pItem = GetServerItem(server); if (!pItem) { pItem = new CServerItem(server); m_serverList.push_back(pItem); m_itemCount++; wxASSERT(m_insertionStart == -1); wxASSERT(m_insertionCount == 0); m_insertionStart = GetItemIndex(pItem); m_insertionCount = 1; } return pItem; } void CQueueViewBase::CommitChanges() { SetItemCount(m_itemCount); if (m_insertionStart != -1) { wxASSERT(m_insertionCount != 0); if (m_insertionCount == 1) UpdateSelections_ItemAdded(m_insertionStart); else UpdateSelections_ItemRangeAdded(m_insertionStart, m_insertionCount); m_insertionStart = -1; m_insertionCount = 0; } if (m_fileCountChanged || m_folderScanCountChanged) DisplayNumberQueuedFiles(); } void CQueueViewBase::DisplayNumberQueuedFiles() { wxString str; if (m_fileCount > 0) { if (!m_folderScanCount) str.Printf(m_title + _T(" (%d)"), m_fileCount); else str.Printf(m_title + _T(" (%d+)"), m_fileCount); } else { if (m_folderScanCount) str.Printf(m_title + _T(" (0+)"), m_fileCount); else str = m_title; } m_pQueue->SetPageText(m_pageIndex, str); m_fileCountChanged = false; m_folderScanCountChanged = false; } void CQueueViewBase::InsertItem(CServerItem* pServerItem, CQueueItem* pItem) { pServerItem->AddChild(pItem); m_itemCount++; if (m_insertionStart == -1) m_insertionStart = GetItemIndex(pItem); m_insertionCount++; if (pItem->GetType() == QueueItemType_File || pItem->GetType() == QueueItemType_Folder) { m_fileCount++; m_fileCountChanged = true; } else if (pItem->GetType() == QueueItemType_FolderScan) { m_folderScanCount++; m_folderScanCountChanged = true; } } bool CQueueViewBase::RemoveItem(CQueueItem* pItem, bool destroy, bool updateItemCount /*=true*/, bool updateSelections /*=true*/) { if (pItem->GetType() == QueueItemType_File || pItem->GetType() == QueueItemType_Folder) { wxASSERT(m_fileCount > 0); m_fileCount--; m_fileCountChanged = true; } else if (pItem->GetType() == QueueItemType_FolderScan) { wxASSERT(m_folderScanCount > 0); m_folderScanCount--; m_folderScanCountChanged = true; } int index = 0; if (updateSelections) index = GetItemIndex(pItem); CQueueItem* topLevelItem = pItem->GetTopLevelItem(); int count = topLevelItem->GetChildrenCount(true); topLevelItem->RemoveChild(pItem, destroy); bool didRemoveParent; int oldCount = m_itemCount; if (!topLevelItem->GetChild(0)) { std::vector::iterator iter; for (iter = m_serverList.begin(); iter != m_serverList.end(); iter++) { if (*iter == topLevelItem) break; } if (iter != m_serverList.end()) m_serverList.erase(iter); UpdateSelections_ItemRangeRemoved(GetItemIndex(topLevelItem), count + 1); delete topLevelItem; m_itemCount -= count + 1; if (updateItemCount) SetItemCount(m_itemCount); didRemoveParent = true; } else { count -= topLevelItem->GetChildrenCount(true); if (updateSelections) UpdateSelections_ItemRangeRemoved(index, count); m_itemCount -= count; if (updateItemCount) SetItemCount(m_itemCount); didRemoveParent = false; } if (updateItemCount) { if (m_fileCountChanged || m_folderScanCountChanged) DisplayNumberQueuedFiles(); if (oldCount > m_itemCount) { bool eraseBackground = GetTopItem() + GetCountPerPage() + 1 >= m_itemCount; Refresh(eraseBackground); if (eraseBackground) Update(); } } return didRemoveParent; } void CQueueViewBase::RefreshItem(const CQueueItem* pItem) { wxASSERT(pItem); int index = GetItemIndex(pItem); wxListCtrl::RefreshItem(index); } void CQueueViewBase::OnNavigationKey(wxNavigationKeyEvent& event) { event.SetEventObject(m_pQueue); m_pQueue->ProcessEvent(event); } void CQueueViewBase::OnChar(wxKeyEvent& event) { const int code = event.GetKeyCode(); if (code != WXK_LEFT && code != WXK_RIGHT) { event.Skip(); return; } int selection = m_pQueue->GetSelection(); if (selection > 0 && code == WXK_LEFT) selection--; else if (selection < (int)m_pQueue->GetPageCount() - 1 && code == WXK_RIGHT) selection++; else return; m_pQueue->SetSelection(selection); } void CQueueViewBase::OnEndColumnDrag(wxListEvent& event) { for (unsigned int i = 0; i < m_pQueue->GetPageCount(); i++) { CQueueViewBase* page = (CQueueViewBase*)m_pQueue->GetPage(i); if (!page || page == this) continue; for (int col = 0; col < wxMin(GetColumnCount(), page->GetColumnCount()); col++) page->SetColumnWidth(col, GetColumnWidth(col)); } } // ------ // CQueue // ------ CQueue::CQueue(wxWindow* parent, CMainFrame *pMainFrame, CAsyncRequestQueue *pAsyncRequestQueue) { Create(parent, -1, wxDefaultPosition, wxDefaultSize, wxNO_BORDER | wxAUI_NB_BOTTOM); SetExArtProvider(); m_pQueueView = new CQueueView(this, 0, pMainFrame, pAsyncRequestQueue); AddPage(m_pQueueView, m_pQueueView->GetTitle()); m_pQueueView_Failed = new CQueueViewFailed(this, 1); AddPage(m_pQueueView_Failed, m_pQueueView_Failed->GetTitle()); m_pQueueView_Successful = new CQueueViewSuccessful(this, 2); AddPage(m_pQueueView_Successful, m_pQueueView_Successful->GetTitle()); RemoveExtraBorders(); m_pQueueView->LoadQueue(); } void CQueue::SetFocus() { GetPage(GetSelection())->SetFocus(); }