#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<CQueueItem*>::iterator iter;
for (iter = m_children.begin(); iter != m_children.end(); iter++)
delete *iter;
}
void CQueueItem::SetPriority(enum QueuePriority priority)
{
std::vector<CQueueItem*>::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<CQueueItem*>::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<CQueueItem*>::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<CQueueItem*>::iterator iter;
std::vector<CQueueItem*> 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<CQueueItem*>::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<CFileItem*>& fileList = m_fileList[pItem->m_queued ? 0 : 1][pItem->GetPriority()];
for (std::list<CFileItem*>::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<CQueueItem*>::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<CFileItem*>& fileList = m_fileList[1][i];
for (std::list<CFileItem*>::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<CFileItem*>& fileList = m_fileList[0][i];
for (std::list<CFileItem*>::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<CFileItem*>(pItem);
RemoveFileItemFromList(pFileItem);
}
return CQueueItem::RemoveChild(pItem, destroy);
}
void CServerItem::QueueImmediateFiles()
{
for (int i = 0; i < PRIORITY_COUNT; i++)
{
std::list<CFileItem*> activeList;
std::list<CFileItem*>& fileList = m_fileList[1][i];
for (std::list<CFileItem*>::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<CFileItem*>& fileList = m_fileList[1][pItem->GetPriority()];
for (std::list<CFileItem*>::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<CQueueItem*>::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<CFileItem*>& fileList = m_fileList[j][i];
for (std::list<CFileItem*>::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<CQueueItem*>::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<CQueueItem*>::iterator iter;
std::vector<CQueueItem*> 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<CFileItem*>(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<CQueueItem*>::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<CFileItem*>::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<CServerItem*>::iterator iter = m_serverList.begin(); iter != m_serverList.end(); iter++)
delete *iter;
}
CQueueItem* CQueueViewBase::GetQueueItem(unsigned int item)
{
std::vector<CServerItem*>::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<CServerItem*>::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<CQueueViewBase*>(this);
CQueueItem* pItem = pThis->GetQueueItem(item);
if (!pItem)
return _T("");
switch (pItem->GetType())
{
case QueueItemType_Server:
{
CServerItem* pServerItem = reinterpret_cast<CServerItem*>(pItem);
if (!column)
return pServerItem->GetName();
}
break;
case QueueItemType_File:
{
CFileItem* pFileItem = reinterpret_cast<CFileItem*>(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<CFolderScanItem*>(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<CFolderItem*>(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<CQueueViewBase*>(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<int> 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<int>::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<int> 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<int>::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<CServerItem*>::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<CServerItem*>::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();
}
syntax highlighted by Code2HTML, v. 0.9.1