#include "FileZilla.h"
#include "RemoteListView.h"
#include "commandqueue.h"
#include "queue.h"
#include "filezillaapp.h"
#include "inputdialog.h"
#include "chmoddialog.h"
#include "filter.h"
#include <algorithm>
#include <wx/dnd.h>
#include "dndobjects.h"
#include "Options.h"
#include "recursive_operation.h"
#ifdef __WXMSW__
#include "shellapi.h"
#include "commctrl.h"
#endif
#ifdef _DEBUG
#define new DEBUG_NEW
#endif
class CRemoteListViewDropTarget : public wxDropTarget
{
public:
CRemoteListViewDropTarget(CRemoteListView* pRemoteListView)
: m_pRemoteListView(pRemoteListView),
m_pFileDataObject(new wxFileDataObject()),
m_pRemoteDataObject(new CRemoteDataObject()),
m_pDataObject(new wxDataObjectComposite())
{
m_pDataObject->Add(m_pRemoteDataObject, true);
m_pDataObject->Add(m_pFileDataObject);
SetDataObject(m_pDataObject);
}
void ClearDropHighlight()
{
const int dropTarget = m_pRemoteListView->m_dropTarget;
if (dropTarget != -1)
{
m_pRemoteListView->SetItemState(dropTarget, 0, wxLIST_STATE_DROPHILITED);
m_pRemoteListView->m_dropTarget = -1;
}
}
virtual wxDragResult OnData(wxCoord x, wxCoord y, wxDragResult def)
{
if (def == wxDragError ||
def == wxDragNone ||
def == wxDragCancel)
return def;
if (!m_pRemoteListView->m_pDirectoryListing)
return wxDragError;
if (!GetData())
return wxDragError;
if (m_pDataObject->GetReceivedFormat() == m_pFileDataObject->GetFormat())
{
wxString subdir;
int flags = 0;
int hit = m_pRemoteListView->HitTest(wxPoint(x, y), flags, 0);
if (hit != -1 && (flags & wxLIST_HITTEST_ONITEM))
{
int index = m_pRemoteListView->GetItemIndex(hit);
if (index != -1)
{
if (index == (int)m_pRemoteListView->m_pDirectoryListing->GetCount())
subdir = _T("..");
else if ((*m_pRemoteListView->m_pDirectoryListing)[index].dir)
subdir = (*m_pRemoteListView->m_pDirectoryListing)[index].name;
}
}
m_pRemoteListView->m_pState->UploadDroppedFiles(m_pFileDataObject, subdir, false);
return wxDragCopy;
}
// At this point it's the remote data object.
if (m_pRemoteDataObject->GetProcessId() != (int)wxGetProcessId())
{
wxMessageBox(_("Drag&drop between different instances of FileZilla has not been implemented yet."));
return wxDragNone;
}
if (m_pRemoteDataObject->GetServer() != *m_pRemoteListView->m_pState->GetServer())
{
wxMessageBox(_("Drag&drop between different servers has not been implemented yet."));
return wxDragNone;
}
// Find drop directory (if it exists)
wxString subdir;
int flags = 0;
int hit = m_pRemoteListView->HitTest(wxPoint(x, y), flags, 0);
if (hit != -1 && (flags & wxLIST_HITTEST_ONITEM))
{
int index = m_pRemoteListView->GetItemIndex(hit);
if (index != -1)
{
if (index == (int)m_pRemoteListView->m_pDirectoryListing->GetCount())
subdir = _T("..");
else if ((*m_pRemoteListView->m_pDirectoryListing)[index].dir)
subdir = (*m_pRemoteListView->m_pDirectoryListing)[index].name;
}
}
// Get target path
CServerPath target = m_pRemoteListView->m_pDirectoryListing->path;
if (subdir == _T(".."))
{
if (target.HasParent())
target = target.GetParent();
}
else if (subdir != _T(""))
target.AddSegment(subdir);
// Make sure target path is valid
if (target == m_pRemoteDataObject->GetServerPath())
{
wxMessageBox(_("Source and target of the drop operation are identical"));
return wxDragNone;
}
const std::list<CRemoteDataObject::t_fileInfo>& files = m_pRemoteDataObject->GetFiles();
for (std::list<CRemoteDataObject::t_fileInfo>::const_iterator iter = files.begin(); iter != files.end(); iter++)
{
const CRemoteDataObject::t_fileInfo& info = *iter;
if (info.dir)
{
CServerPath dir = m_pRemoteDataObject->GetServerPath();
dir.AddSegment(info.name);
if (dir == target || dir.IsParentOf(target, false))
{
wxMessageBox(_("A directory cannot be dragged into one if its subdirectories."));
return wxDragNone;
}
}
}
for (std::list<CRemoteDataObject::t_fileInfo>::const_iterator iter = files.begin(); iter != files.end(); iter++)
{
const CRemoteDataObject::t_fileInfo& info = *iter;
m_pRemoteListView->m_pState->m_pCommandQueue->ProcessCommand(
new CRenameCommand(m_pRemoteDataObject->GetServerPath(), info.name, target, info.name)
);
}
m_pRemoteListView->m_pState->m_pCommandQueue->ProcessCommand(
new CListCommand()
);
return wxDragNone;
}
virtual bool OnDrop(wxCoord x, wxCoord y)
{
ClearDropHighlight();
if (!m_pRemoteListView->m_pDirectoryListing)
return false;
return true;
}
void DisplayDropHighlight(wxPoint point)
{
int flags;
int hit = m_pRemoteListView->HitTest(point, flags, 0);
if (!(flags & wxLIST_HITTEST_ONITEM))
hit = -1;
if (hit != -1)
{
int index = m_pRemoteListView->GetItemIndex(hit);
if (index == -1)
hit = -1;
else if (index != (int)m_pRemoteListView->m_pDirectoryListing->GetCount())
{
if (!(*m_pRemoteListView->m_pDirectoryListing)[index].dir)
hit = -1;
}
}
if (hit != m_pRemoteListView->m_dropTarget)
{
ClearDropHighlight();
if (hit != -1)
{
m_pRemoteListView->SetItemState(hit, wxLIST_STATE_DROPHILITED, wxLIST_STATE_DROPHILITED);
m_pRemoteListView->m_dropTarget = hit;
}
}
}
virtual wxDragResult OnDragOver(wxCoord x, wxCoord y, wxDragResult def)
{
if (def == wxDragError ||
def == wxDragNone ||
def == wxDragCancel)
{
ClearDropHighlight();
return def;
}
if (!m_pRemoteListView->m_pDirectoryListing)
{
ClearDropHighlight();
return wxDragNone;
}
DisplayDropHighlight(wxPoint(x, y));
return wxDragCopy;
}
virtual void OnLeave()
{
ClearDropHighlight();
}
virtual wxDragResult OnEnter(wxCoord x, wxCoord y, wxDragResult def)
{
return OnDragOver(x, y, def);
}
protected:
CRemoteListView *m_pRemoteListView;
wxFileDataObject* m_pFileDataObject;
CRemoteDataObject* m_pRemoteDataObject;
wxDataObjectComposite* m_pDataObject;
};
class CInfoText : public wxWindow
{
public:
CInfoText(wxWindow* parent, const wxString& text)
: wxWindow(parent, wxID_ANY, wxPoint(0, 60), wxDefaultSize),
m_text(_T("<") + text + _T(">"))
{
SetBackgroundColour(parent->GetBackgroundColour());
}
wxString m_text;
protected:
void OnPaint(wxPaintEvent& event)
{
wxPaintDC paintDc(this);
wxRect rect = GetClientRect();
paintDc.SetFont(GetFont());
int width, height;
paintDc.GetTextExtent(m_text, &width, &height);
paintDc.DrawText(m_text, rect.x + rect.GetWidth() / 2 - width / 2, rect.GetTop());
};
DECLARE_EVENT_TABLE();
};
BEGIN_EVENT_TABLE(CInfoText, wxWindow)
EVT_PAINT(CInfoText::OnPaint)
END_EVENT_TABLE()
BEGIN_EVENT_TABLE(CRemoteListView, wxListCtrl)
EVT_LIST_ITEM_ACTIVATED(wxID_ANY, CRemoteListView::OnItemActivated)
EVT_LIST_COL_CLICK(wxID_ANY, CRemoteListView::OnColumnClicked)
EVT_CONTEXT_MENU(CRemoteListView::OnContextMenu)
// Map both ID_DOWNLOAD and ID_ADDTOQUEUE to OnMenuDownload, code is identical
EVT_MENU(XRCID("ID_DOWNLOAD"), CRemoteListView::OnMenuDownload)
EVT_MENU(XRCID("ID_ADDTOQUEUE"), CRemoteListView::OnMenuDownload)
EVT_MENU(XRCID("ID_MKDIR"), CRemoteListView::OnMenuMkdir)
EVT_MENU(XRCID("ID_DELETE"), CRemoteListView::OnMenuDelete)
EVT_MENU(XRCID("ID_RENAME"), CRemoteListView::OnMenuRename)
EVT_MENU(XRCID("ID_CHMOD"), CRemoteListView::OnMenuChmod)
EVT_CHAR(CRemoteListView::OnChar)
EVT_KEY_DOWN(CRemoteListView::OnKeyDown)
EVT_LIST_BEGIN_LABEL_EDIT(wxID_ANY, CRemoteListView::OnBeginLabelEdit)
EVT_LIST_END_LABEL_EDIT(wxID_ANY, CRemoteListView::OnEndLabelEdit)
EVT_SIZE(CRemoteListView::OnSize)
EVT_LIST_BEGIN_DRAG(wxID_ANY, CRemoteListView::OnBeginDrag)
END_EVENT_TABLE()
CRemoteListView::CRemoteListView(wxWindow* parent, wxWindowID id, CState *pState, CQueueView* pQueue)
: wxListCtrl(parent, id, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL | wxLC_VIRTUAL | wxLC_REPORT | wxNO_BORDER | wxLC_EDIT_LABELS),
CSystemImageList(16),
CStateEventHandler(pState, STATECHANGE_REMOTE_DIR | STATECHANGE_REMOTE_DIR_MODIFIED | STATECHANGE_APPLYFILTER)
{
m_dropTarget = -1;
m_pInfoText = 0;
m_pDirectoryListing = 0;
m_pQueue = pQueue;
unsigned long widths[6] = { 80, 75, 80, 100, 80, 80 };
if (!wxGetKeyState(WXK_SHIFT) || !wxGetKeyState(WXK_ALT) || !wxGetKeyState(WXK_CONTROL))
COptions::Get()->ReadColumnWidths(OPTION_REMOTEFILELIST_COLUMN_WIDTHS, 6, widths);
InsertColumn(0, _("Filename"), wxLIST_FORMAT_LEFT, widths[0]);
InsertColumn(1, _("Filesize"), wxLIST_FORMAT_RIGHT, widths[1]);
InsertColumn(2, _("Filetype"), wxLIST_FORMAT_LEFT, widths[2]);
InsertColumn(3, _("Last modified"), wxLIST_FORMAT_LEFT, widths[3]);
InsertColumn(4, _("Permissions"), wxLIST_FORMAT_LEFT, widths[4]);
InsertColumn(5, _("Owner / Group"), wxLIST_FORMAT_LEFT, widths[5]);
wxString sortInfo = COptions::Get()->GetOption(OPTION_REMOTEFILELIST_SORTORDER);
m_sortDirection = sortInfo[0] - '0';
if (m_sortDirection < 0 || m_sortDirection > 1)
m_sortDirection = 0;
if (sortInfo.Len() == 3)
{
m_sortColumn = sortInfo[2] - '0';
if (m_sortColumn < 0 || m_sortColumn > 5)
m_sortColumn = 0;
}
else
m_sortColumn = 0;
m_dirIcon = GetIconIndex(dir);
SetImageList(GetSystemImageList(), wxIMAGE_LIST_SMALL);
#ifdef __WXMSW__
// Initialize imagelist for list header
m_pHeaderImageList = new wxImageListEx(8, 8, true, 3);
wxBitmap bmp;
bmp.LoadFile(wxGetApp().GetResourceDir() + _T("empty.png"), wxBITMAP_TYPE_PNG);
m_pHeaderImageList->Add(bmp);
bmp.LoadFile(wxGetApp().GetResourceDir() + _T("up.png"), wxBITMAP_TYPE_PNG);
m_pHeaderImageList->Add(bmp);
bmp.LoadFile(wxGetApp().GetResourceDir() + _T("down.png"), wxBITMAP_TYPE_PNG);
m_pHeaderImageList->Add(bmp);
HWND hWnd = (HWND)GetHandle();
if (!hWnd)
{
delete m_pHeaderImageList;
m_pHeaderImageList = 0;
return;
}
HWND header = (HWND)SendMessage(hWnd, LVM_GETHEADER, 0, 0);
if (!header)
{
delete m_pHeaderImageList;
m_pHeaderImageList = 0;
return;
}
wxChar buffer[1000] = {0};
HDITEM item;
item.mask = HDI_TEXT;
item.pszText = buffer;
item.cchTextMax = 999;
SendMessage(header, HDM_GETITEM, 0, (LPARAM)&item);
SendMessage(header, HDM_SETIMAGELIST, 0, (LPARAM)m_pHeaderImageList->GetHandle());
#endif
SetDirectoryListing(0);
SetDropTarget(new CRemoteListViewDropTarget(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_KEY_DOWN, wxKeyEventHandler(CRemoteListView::OnKeyDown), 0, this);
#endif
InitDateFormat();
}
CRemoteListView::~CRemoteListView()
{
wxString str = wxString::Format(_T("%d %d"), m_sortDirection, m_sortColumn);
COptions::Get()->SetOption(OPTION_REMOTEFILELIST_SORTORDER, str);
#ifdef __WXMSW__
delete m_pHeaderImageList;
#endif
}
// Defined in LocalListView.cpp
extern wxString FormatSize(const wxLongLong& size);
wxString CRemoteListView::OnGetItemText(long item, long column) const
{
CRemoteListView *pThis = const_cast<CRemoteListView *>(this);
int index = pThis->GetItemIndex(item);
if (index == -1)
return _T("");
if (!column)
{
if ((unsigned int)index == m_pDirectoryListing->GetCount())
return _T("..");
else
return (*m_pDirectoryListing)[index].name;
}
if (!item)
return _T(""); //.. has no attributes
if (column == 1)
{
const CDirentry& entry = (*m_pDirectoryListing)[index];
if (entry.dir || entry.size < 0)
return _T("");
else
return FormatSize(entry.size);
}
else if (column == 2)
{
t_fileData& data = pThis->m_fileData[index];
if (data.fileType == _T(""))
{
const CDirentry& entry = (*m_pDirectoryListing)[index];
data.fileType = pThis->GetType(entry.name, entry.dir);
}
return data.fileType;
}
else if (column == 3)
{
const CDirentry& entry = (*m_pDirectoryListing)[index];
if (!entry.hasDate)
return _T("");
if (entry.hasTime)
return entry.time.Format(m_timeFormat);
else
return entry.time.Format(m_dateFormat);
}
else if (column == 4)
return (*m_pDirectoryListing)[index].permissions;
else if (column == 5)
return (*m_pDirectoryListing)[index].ownerGroup;
return _T("");
}
#ifndef __WXMSW__
// This function converts to the right size with the given background colour
// Defined in LocalListView.cpp
wxBitmap PrepareIcon(wxIcon icon, wxColour colour);
#endif
// See comment to OnGetItemText
int CRemoteListView::OnGetItemImage(long item) const
{
CRemoteListView *pThis = const_cast<CRemoteListView *>(this);
int index = GetItemIndex(item);
if (index == -1)
return -1;
int &icon = pThis->m_fileData[index].icon;
if (icon != -2)
return icon;
icon = pThis->GetIconIndex(file, (*m_pDirectoryListing)[index].name, false);
return icon;
}
int CRemoteListView::GetItemIndex(unsigned int item) const
{
if (item >= m_indexMapping.size())
return -1;
unsigned int index = m_indexMapping[item];
if (index >= m_fileData.size())
return -1;
return index;
}
bool CRemoteListView::IsItemValid(unsigned int item) const
{
if (item >= m_indexMapping.size())
return false;
unsigned int index = m_indexMapping[item];
if (index >= m_fileData.size())
return false;
return true;
}
void CRemoteListView::UpdateDirectoryListing_Removed(const CDirectoryListing *pDirectoryListing)
{
std::list<unsigned int> removedItems;
unsigned int j = 0;
for (unsigned int i = 0; i < pDirectoryListing->GetCount(); i++, j++)
{
if ((*pDirectoryListing)[i].name == (*m_pDirectoryListing)[j].name)
continue;
removedItems.push_back(j++);
}
for (; j < m_pDirectoryListing->GetCount(); j++)
removedItems.push_back(j);
for (std::list<unsigned int>::reverse_iterator iter = removedItems.rbegin(); iter != removedItems.rend(); iter++)
{
m_fileData.erase(m_fileData.begin() + *iter);
}
std::list<int> selectedItems;
// Number of items left to remove
unsigned int toRemove = m_pDirectoryListing->GetCount() - pDirectoryListing->GetCount();
wxASSERT(toRemove == removedItems.size());
std::list<int> removedIndexes;
const int size = m_indexMapping.size();
for (int i = size - 1; i >= 0; i--)
{
bool removed = false;
// j is the offset to index has to be adjusted
int j = 0;
for (std::list<unsigned int>::const_iterator iter = removedItems.begin(); iter != removedItems.end(); iter++, j++)
{
if (*iter > m_indexMapping[i])
break;
if (*iter == m_indexMapping[i])
{
removedIndexes.push_back(i);
removed = true;
toRemove--;
break;
}
}
m_indexMapping[i] -= j;
// Update selection
bool isSelected = GetItemState(i, wxLIST_STATE_SELECTED) != 0;
bool needSelection;
if (selectedItems.empty())
needSelection = false;
else if (selectedItems.front() == i)
{
needSelection = true;
selectedItems.pop_front();
}
else
needSelection = false;
if (isSelected)
{
if (!needSelection && toRemove)
SetItemState(i, 0, wxLIST_STATE_SELECTED);
if (!removed)
selectedItems.push_back(i - toRemove);
}
else if (needSelection)
SetItemState(i, wxLIST_STATE_SELECTED, wxLIST_STATE_SELECTED);
}
wxASSERT(removedIndexes.size() == removedItems.size());
for (std::list<int>::reverse_iterator iter = removedIndexes.rbegin(); iter != removedIndexes.rend(); iter++)
{
m_indexMapping.erase(m_indexMapping.begin() + *iter);
}
m_pDirectoryListing = pDirectoryListing;
SetItemCount(m_indexMapping.size());
}
bool CRemoteListView::UpdateDirectoryListing(const CDirectoryListing *pDirectoryListing)
{
if ((pDirectoryListing->m_hasUnsureEntries & UNSURE_CHANGE) == UNSURE_CHANGE)
{
if (m_sortColumn && m_sortColumn != 2)
{
// If now sorted by file or type, changing file attributes can influence
// sort order.
return false;
}
}
if ((pDirectoryListing->m_hasUnsureEntries & UNSURE_CONFUSED) == UNSURE_CONFUSED)
return false;
int addremove = UNSURE_ADD | UNSURE_REMOVE;
if ((pDirectoryListing->m_hasUnsureEntries & addremove) == addremove)
return false;
if ((pDirectoryListing->m_hasUnsureEntries & UNSURE_REMOVE) == UNSURE_REMOVE)
{
wxASSERT(pDirectoryListing->GetCount() < m_pDirectoryListing->GetCount());
UpdateDirectoryListing_Removed(pDirectoryListing);
return true;
}
return false;
}
void CRemoteListView::SetDirectoryListing(const CDirectoryListing *pDirectoryListing, bool modified /*=false*/)
{
bool reset = false;
if (!pDirectoryListing || !m_pDirectoryListing)
reset = true;
else if (m_pDirectoryListing->path != pDirectoryListing->path)
reset = true;
else if (m_pDirectoryListing->GetCount() > 200 && m_pDirectoryListing->m_firstListTime == pDirectoryListing->m_firstListTime)
{
// Updated directory listing. Check if we can use process it in a different,
// more efficient way.
// Makes only sense for big listings though.
if (UpdateDirectoryListing(pDirectoryListing))
{
wxASSERT(GetItemCount() == (int)m_indexMapping.size());
wxASSERT(GetItemCount() == (int)m_fileData.size());
wxASSERT(m_pDirectoryListing->GetCount() + 1 >= (unsigned int)GetItemCount());
wxASSERT(m_indexMapping[0] == m_pDirectoryListing->GetCount());
Refresh();
return;
}
}
wxString prevFocused;
std::list<wxString> selectedNames;
if (reset)
{
// Clear selection
int item = -1;
while (true)
{
item = GetNextItem(item, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
if (item == -1)
break;
SetItemState(item, 0, wxLIST_STATE_SELECTED);
}
item = GetNextItem(-1, wxLIST_NEXT_ALL, wxLIST_STATE_FOCUSED);
if (item != -1)
{
SetItemState(item, 0, wxLIST_STATE_FOCUSED);
wxASSERT(GetNextItem(item, wxLIST_NEXT_ALL, wxLIST_STATE_FOCUSED) == -1);
}
}
else
{
// Remember which items were selected
selectedNames = RememberSelectedItems(prevFocused);
}
m_pDirectoryListing = pDirectoryListing;
m_fileData.clear();
m_indexMapping.clear();
bool eraseBackground = false;
if (m_pDirectoryListing)
{
if (m_pDirectoryListing->m_failed)
SetInfoText(_("Directory listing failed"));
else if (!m_pDirectoryListing->GetCount())
SetInfoText(_("Empty directory listing"));
else
SetInfoText(_T(""));
m_indexMapping.push_back(m_pDirectoryListing->GetCount());
CFilterDialog filter;
for (unsigned int i = 0; i < m_pDirectoryListing->GetCount(); i++)
{
const CDirentry& entry = (*m_pDirectoryListing)[i];
t_fileData data;
data.icon = entry.dir ? m_dirIcon : -2;
m_fileData.push_back(data);
if (!filter.FilenameFiltered(entry.name, entry.dir, entry.size, false))
m_indexMapping.push_back(i);
}
t_fileData data;
data.icon = m_dirIcon;
m_fileData.push_back(data);
}
else
{
eraseBackground = true;
SetInfoText(_("Not connected to any server"));
}
if (m_dropTarget != -1)
{
bool resetDropTarget = false;
int index = GetItemIndex(m_dropTarget);
if (index == -1)
resetDropTarget = true;
else if (index != (int)m_pDirectoryListing->GetCount())
if (!(*m_pDirectoryListing)[index].dir)
resetDropTarget = true;
if (resetDropTarget)
{
SetItemState(m_dropTarget, 0, wxLIST_STATE_DROPHILITED);
m_dropTarget = -1;
}
}
if ((unsigned int)GetItemCount() > m_indexMapping.size())
eraseBackground = true;
if ((unsigned int)GetItemCount() != m_indexMapping.size())
SetItemCount(m_indexMapping.size());
if (GetItemCount() && reset)
{
EnsureVisible(0);
SetItemState(0, wxLIST_STATE_FOCUSED, wxLIST_STATE_FOCUSED);
}
SortList();
ReselectItems(selectedNames, prevFocused);
Refresh(eraseBackground);
}
// Helper classes for fast sorting using std::sort
// -----------------------------------------------
class CRemoteListViewSort : public std::binary_function<int,int,bool>
{
public:
enum DirSortMode
{
dirsort_ontop,
dirsort_onbottom,
dirsort_inline
};
CRemoteListViewSort(const CDirectoryListing* const pDirectoryListing, enum DirSortMode dirSortMode)
: m_pDirectoryListing(pDirectoryListing), m_dirSortMode(dirSortMode)
{
}
#define CMP(f, data1, data2) \
{\
int res = f(data1, data2);\
if (res == -1)\
return true;\
else if (res == 1)\
return false;\
}
#define CMP_LESS(f, data1, data2) \
{\
int res = f(data1, data2);\
if (res == -1)\
return true;\
else\
return false;\
}
inline int CmpDir(const CDirentry &data1, const CDirentry &data2) const
{
switch (m_dirSortMode)
{
default:
case dirsort_ontop:
if (data1.dir)
{
if (!data2.dir)
return -1;
else
return 0;
}
else
{
if (data2.dir)
return 1;
else
return 0;
}
case dirsort_onbottom:
if (data1.dir)
{
if (!data2.dir)
return 1;
else
return 0;
}
else
{
if (data2.dir)
return -1;
else
return 0;
}
case dirsort_inline:
return 0;
}
}
inline int CmpName(const CDirentry &data1, const CDirentry &data2) const
{
#ifdef __WXMSW__
return data1.name.CmpNoCase(data2.name);
#else
return data1.name.Cmp(data2.name);
#endif
}
inline int CmpSize(const CDirentry &data1, const CDirentry &data2) const
{
const wxLongLong diff = data1.size - data2.size;
if (diff < 0)
return -1;
else if (diff > 0)
return 1;
else
return 0;
}
inline int CmpStringNoCase(const wxString &data1, const wxString &data2) const
{
return data1.CmpNoCase(data2);
}
inline int CmpTime(const CDirentry &data1, const CDirentry &data2) const
{
if (!data1.hasDate)
{
if (data2.hasDate)
return -1;
else
return 0;
}
else
{
if (!data2.hasDate)
return 1;
if (data1.time < data2.time)
return -1;
else if (data1.time > data2.time)
return 1;
else
return 0;
}
}
protected:
const CDirectoryListing* const m_pDirectoryListing;
const enum DirSortMode m_dirSortMode;
};
class CRemoteListViewSortName : public CRemoteListViewSort
{
public:
CRemoteListViewSortName(const CDirectoryListing* const pDirectoryListing, enum DirSortMode dirSortMode)
: CRemoteListViewSort(pDirectoryListing, dirSortMode)
{
}
bool operator()(int a, int b) const
{
const CDirentry& data1 = (*m_pDirectoryListing)[a];
const CDirentry& data2 = (*m_pDirectoryListing)[b];
CMP(CmpDir, data1, data2);
CMP_LESS(CmpName, data1, data2);
}
};
class CRemoteListViewSortSize : public CRemoteListViewSort
{
public:
CRemoteListViewSortSize(const CDirectoryListing* const pDirectoryListing, enum DirSortMode dirSortMode)
: CRemoteListViewSort(pDirectoryListing, dirSortMode)
{
}
bool operator()(int a, int b) const
{
const CDirentry& data1 = (*m_pDirectoryListing)[a];
const CDirentry& data2 = (*m_pDirectoryListing)[b];
CMP(CmpDir, data1, data2);
CMP(CmpSize, data1, data2);
CMP_LESS(CmpName, data1, data2);
}
};
class CRemoteListViewSortType : public CRemoteListViewSort
{
public:
CRemoteListViewSortType(CRemoteListView* const pRemoteListView, enum DirSortMode dirSortMode, const CDirectoryListing* const pDirectoryListing, std::vector<CRemoteListView::t_fileData>& fileData)
: CRemoteListViewSort(pDirectoryListing, dirSortMode), m_pRemoteListView(pRemoteListView), m_fileData(fileData)
{
}
bool operator()(int a, int b) const
{
const CDirentry& data1 = (*m_pDirectoryListing)[a];
const CDirentry& data2 = (*m_pDirectoryListing)[b];
CMP(CmpDir, data1, data2);
CRemoteListView::t_fileData &type1 = m_fileData[a];
CRemoteListView::t_fileData &type2 = m_fileData[b];
if (type1.fileType == _T(""))
type1.fileType = m_pRemoteListView->GetType(data1.name, data1.dir);
if (type2.fileType == _T(""))
type2.fileType = m_pRemoteListView->GetType(data2.name, data2.dir);
CMP(CmpStringNoCase, type1.fileType, type2.fileType);
CMP_LESS(CmpName, data1, data2);
}
protected:
CRemoteListView* const m_pRemoteListView;
std::vector<CRemoteListView::t_fileData>& m_fileData;
};
class CRemoteListViewSortTime : public CRemoteListViewSort
{
public:
CRemoteListViewSortTime(const CDirectoryListing* const pDirectoryListing, enum DirSortMode dirSortMode)
: CRemoteListViewSort(pDirectoryListing, dirSortMode)
{
}
bool operator()(int a, int b) const
{
const CDirentry& data1 = (*m_pDirectoryListing)[a];
const CDirentry& data2 = (*m_pDirectoryListing)[b];
CMP(CmpDir, data1, data2);
CMP(CmpTime, data1, data2);
CMP_LESS(CmpName, data1, data2);
}
};
class CRemoteListViewSortPermissions : public CRemoteListViewSort
{
public:
CRemoteListViewSortPermissions(const CDirectoryListing* const pDirectoryListing, enum DirSortMode dirSortMode)
: CRemoteListViewSort(pDirectoryListing, dirSortMode)
{
}
bool operator()(int a, int b) const
{
const CDirentry& data1 = (*m_pDirectoryListing)[a];
const CDirentry& data2 = (*m_pDirectoryListing)[b];
CMP(CmpDir, data1, data2);
CMP(CmpStringNoCase, data1.permissions, data2.permissions);
CMP_LESS(CmpName, data1, data2);
}
};
void CRemoteListView::SortList(int column /*=-1*/, int direction /*=-1*/)
{
if (column != -1)
{
#ifdef __WXMSW__
if (column != m_sortColumn && m_pHeaderImageList)
{
HWND hWnd = (HWND)GetHandle();
HWND header = (HWND)SendMessage(hWnd, LVM_GETHEADER, 0, 0);
wxChar buffer[100];
HDITEM item;
item.mask = HDI_TEXT | HDI_FORMAT;
item.pszText = buffer;
item.cchTextMax = 99;
SendMessage(header, HDM_GETITEM, m_sortColumn, (LPARAM)&item);
item.mask |= HDI_IMAGE;
item.fmt |= HDF_IMAGE | HDF_BITMAP_ON_RIGHT;
item.iImage = 0;
SendMessage(header, HDM_SETITEM, m_sortColumn, (LPARAM)&item);
}
#endif
}
else
column = m_sortColumn;
if (direction == -1)
direction = m_sortDirection;
#ifdef __WXMSW__
if (m_pHeaderImageList)
{
HWND hWnd = (HWND)GetHandle();
HWND header = (HWND)SendMessage(hWnd, LVM_GETHEADER, 0, 0);
wxChar buffer[100];
HDITEM item;
item.mask = HDI_TEXT | HDI_FORMAT;
item.pszText = buffer;
item.cchTextMax = 99;
SendMessage(header, HDM_GETITEM, column, (LPARAM)&item);
item.mask |= HDI_IMAGE;
item.fmt |= HDF_IMAGE | HDF_BITMAP_ON_RIGHT;
item.iImage = direction ? 2 : 1;
SendMessage(header, HDM_SETITEM, column, (LPARAM)&item);
}
#endif
// Remember which files are selected
int count = 1 + (m_pDirectoryListing ? m_pDirectoryListing->GetCount() : 0);
bool *selected = new bool[count];
memset(selected, 0, sizeof(bool) * count);
int item = -1;
while ((item = GetNextItem(item, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED)) != -1)
selected[m_indexMapping[item]] = 1;
int focused = GetNextItem(-1, wxLIST_NEXT_ALL, wxLIST_STATE_FOCUSED);
if (focused != -1)
focused = m_indexMapping[focused];
const int dirSortOption = COptions::Get()->GetOptionVal(OPTION_FILELIST_DIRSORT);
if (column == m_sortColumn && direction != m_sortDirection && !m_indexMapping.empty() &&
dirSortOption != 1)
{
// Simply reverse everything
m_sortDirection = direction;
m_sortColumn = column;
std::vector<unsigned int>::iterator iter = m_indexMapping.begin();
std::reverse(++iter, m_indexMapping.end());
SortList_UpdateSelections(selected, focused);
delete [] selected;
Refresh(false);
return;
}
m_sortDirection = direction;
m_sortColumn = column;
if (GetItemCount() < 3)
{
delete [] selected;
return;
}
CRemoteListViewSort::DirSortMode dirSortMode;
switch (dirSortOption)
{
case 0:
default:
dirSortMode = CRemoteListViewSort::dirsort_ontop;
break;
case 1:
if (m_sortDirection)
dirSortMode = CRemoteListViewSort::dirsort_onbottom;
else
dirSortMode = CRemoteListViewSort::dirsort_ontop;
break;
case 2:
dirSortMode = CRemoteListViewSort::dirsort_inline;
break;
}
std::vector<unsigned int>::iterator iter = m_indexMapping.begin();
if (!m_sortColumn)
std::sort(++iter, m_indexMapping.end(), CRemoteListViewSortName(m_pDirectoryListing, dirSortMode));
else if (m_sortColumn == 1)
std::sort(++iter, m_indexMapping.end(), CRemoteListViewSortSize(m_pDirectoryListing, dirSortMode));
else if (m_sortColumn == 2)
std::sort(++iter, m_indexMapping.end(), CRemoteListViewSortType(this, dirSortMode, m_pDirectoryListing, m_fileData));
else if (m_sortColumn == 3)
std::sort(++iter, m_indexMapping.end(), CRemoteListViewSortTime(m_pDirectoryListing, dirSortMode));
else if (m_sortColumn == 4)
std::sort(++iter, m_indexMapping.end(), CRemoteListViewSortPermissions(m_pDirectoryListing, dirSortMode));
if (m_sortDirection)
{
std::vector<unsigned int>::iterator iter = m_indexMapping.begin();
std::reverse(++iter, m_indexMapping.end());
}
SortList_UpdateSelections(selected, focused);
delete [] selected;
Refresh(false);
}
void CRemoteListView::SortList_UpdateSelections(bool* selections, int focus)
{
for (int i = 1; i < GetItemCount(); i++)
{
const int state = GetItemState(i, wxLIST_STATE_SELECTED | wxLIST_STATE_FOCUSED);
const bool selected = (state & wxLIST_STATE_SELECTED) != 0;
const bool focused = (state & wxLIST_STATE_FOCUSED) != 0;
int item = m_indexMapping[i];
if (selections[item] != selected)
SetItemState(i, selections[item] ? wxLIST_STATE_SELECTED : 0, wxLIST_STATE_SELECTED);
if (focused)
{
if (item != focus)
SetItemState(i, 0, wxLIST_STATE_FOCUSED);
}
else
{
if (item == focus)
SetItemState(i, wxLIST_STATE_FOCUSED, wxLIST_STATE_FOCUSED);
}
}
}
void CRemoteListView::OnColumnClicked(wxListEvent &event)
{
int col = event.GetColumn();
if (col == -1)
return;
int dir;
if (col == m_sortColumn)
dir = m_sortDirection ? 0 : 1;
else
dir = m_sortDirection;
SortList(col, dir);
}
wxString CRemoteListView::GetType(wxString name, bool dir)
{
#ifdef __WXMSW__
wxString type;
wxString ext = wxFileName(name).GetExt();
ext.MakeLower();
std::map<wxString, wxString>::iterator typeIter = m_fileTypeMap.find(ext);
if (typeIter != m_fileTypeMap.end())
type = typeIter->second;
else
{
SHFILEINFO shFinfo;
memset(&shFinfo,0,sizeof(SHFILEINFO));
if (SHGetFileInfo(name,
dir ? FILE_ATTRIBUTE_DIRECTORY : FILE_ATTRIBUTE_NORMAL,
&shFinfo,
sizeof(shFinfo),
SHGFI_TYPENAME | SHGFI_USEFILEATTRIBUTES))
{
type = shFinfo.szTypeName;
if (type == _T(""))
{
type = ext;
type.MakeUpper();
if (!type.IsEmpty())
{
type += _T("-");
type += _("file");
}
else
type = _("File");
}
else
{
if (!dir && ext != _T(""))
m_fileTypeMap[ext] = type;
}
}
else
{
type = ext;
type.MakeUpper();
if (!type.IsEmpty())
{
type += _T("-");
type += _("file");
}
else
type = _("File");
}
}
return type;
#else
if (dir)
return _("Folder");
wxFileName fn(name);
wxString ext = fn.GetExt();
if (ext == _T(""))
return _("File");
std::map<wxString, wxString>::iterator typeIter = m_fileTypeMap.find(ext);
if (typeIter != m_fileTypeMap.end())
return typeIter->second;
wxString desc;
wxFileType *pType = wxTheMimeTypesManager->GetFileTypeFromExtension(ext);
if (!pType)
{
m_fileTypeMap[ext] = desc;
return desc;
}
if (pType->GetDescription(&desc) && desc != _T(""))
{
delete pType;
m_fileTypeMap[ext] = desc;
return desc;
}
delete pType;
desc = _("File");
m_fileTypeMap[ext.MakeLower()] = desc;
return desc;
#endif
}
void CRemoteListView::OnItemActivated(wxListEvent &event)
{
if (!m_pState->IsRemoteIdle())
{
wxBell();
return;
}
int count = 0;
bool back = false;
int item = -1;
while (true)
{
item = GetNextItem(item, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
if (item == -1)
break;
count++;
if (!item)
back = true;
}
if (count > 1)
{
if (back)
{
wxBell();
return;
}
wxCommandEvent cmdEvent;
OnMenuDownload(cmdEvent);
return;
}
item = event.GetIndex();
if (item)
{
int index = GetItemIndex(item);
if (index == -1)
return;
const CDirentry& entry = (*m_pDirectoryListing)[index];
const wxString& name = entry.name;
if (entry.dir)
m_pState->m_pCommandQueue->ProcessCommand(new CListCommand(m_pDirectoryListing->path, name));
else
{
const CServer* pServer = m_pState->GetServer();
if (!pServer)
{
wxBell();
return;
}
wxFileName fn = wxFileName(m_pState->GetLocalDir(), name);
m_pQueue->QueueFile(false, true, fn.GetFullPath(), name, m_pDirectoryListing->path, *pServer, entry.size);
}
}
else
m_pState->m_pCommandQueue->ProcessCommand(new CListCommand(m_pDirectoryListing->path, _T("..")));
}
void CRemoteListView::OnContextMenu(wxContextMenuEvent& event)
{
wxMenu* pMenu = wxXmlResource::Get()->LoadMenu(_T("ID_MENU_REMOTEFILELIST"));
if (!pMenu)
return;
if (!m_pState->IsRemoteIdle())
{
pMenu->Enable(XRCID("ID_DOWNLOAD"), false);
pMenu->Enable(XRCID("ID_ADDTOQUEUE"), false);
pMenu->Enable(XRCID("ID_MKDIR"), false);
pMenu->Enable(XRCID("ID_DELETE"), false);
pMenu->Enable(XRCID("ID_RENAME"), false);
pMenu->Enable(XRCID("ID_CHMOD"), false);
}
else if ((GetItemCount() && GetItemState(0, wxLIST_STATE_SELECTED)) ||
GetNextItem(-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED) == -1)
{
pMenu->Enable(XRCID("ID_DOWNLOAD"), false);
pMenu->Enable(XRCID("ID_ADDTOQUEUE"), false);
pMenu->Enable(XRCID("ID_DELETE"), false);
pMenu->Enable(XRCID("ID_RENAME"), false);
pMenu->Enable(XRCID("ID_CHMOD"), false);
}
if (!m_pDirectoryListing)
pMenu->Enable(XRCID("ID_MKDIR"), false);
PopupMenu(pMenu);
delete pMenu;
}
void CRemoteListView::OnMenuDownload(wxCommandEvent& event)
{
// Make sure selection is valid
bool idle = m_pState->IsRemoteIdle();
long item = -1;
while (true)
{
item = GetNextItem(item, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
if (item == -1)
break;
if (!item)
{
wxBell();
return;
}
int index = GetItemIndex(item);
if (index == -1)
continue;
if ((*m_pDirectoryListing)[index].dir && !idle)
{
wxBell();
return;
}
}
TransferSelectedFiles(m_pState->GetLocalDir(), event.GetId() == XRCID("ID_ADDTOQUEUE"));
}
void CRemoteListView::TransferSelectedFiles(const wxString& localDir, bool queueOnly)
{
bool idle = m_pState->IsRemoteIdle();
CRecursiveOperation* pRecursiveOperation = m_pState->GetRecursiveOperationHandler();
wxASSERT(pRecursiveOperation);
long item = -1;
while (true)
{
item = GetNextItem(item, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
if (item == -1)
break;
int index = GetItemIndex(item);
if (index == -1)
continue;
const CDirentry& entry = (*m_pDirectoryListing)[index];
const wxString& name = entry.name;
const CServer* pServer = m_pState->GetServer();
if (!pServer)
{
wxBell();
return;
}
if (entry.dir)
{
if (!idle)
continue;
wxFileName fn = wxFileName(localDir, _T(""));
fn.AppendDir(name);
CServerPath remotePath = m_pDirectoryListing->path;
if (remotePath.AddSegment(name))
{
//m_pQueue->QueueFolder(event.GetId() == XRCID("ID_ADDTOQUEUE"), true, fn.GetFullPath(), remotePath, *pServer);
pRecursiveOperation->AddDirectoryToVisit(m_pDirectoryListing->path, name, fn.GetFullPath());
}
}
else
{
wxFileName fn = wxFileName(localDir, name);
m_pQueue->QueueFile(queueOnly, true, fn.GetFullPath(), name, m_pDirectoryListing->path, *pServer, entry.size);
}
}
pRecursiveOperation->StartRecursiveOperation(queueOnly ? CRecursiveOperation::recursive_addtoqueue : CRecursiveOperation::recursive_download, m_pDirectoryListing->path);
}
void CRemoteListView::OnMenuMkdir(wxCommandEvent& event)
{
if (!m_pDirectoryListing || !m_pState->IsRemoteIdle())
{
wxBell();
return;
}
CInputDialog dlg;
if (!dlg.Create(this, _("Create directory"), _("Please enter the name of the directory which should be created:")))
return;
CServerPath path = m_pDirectoryListing->path;
// Append a long segment which does (most likely) not exist in the path and
// replace it with "New folder" later. This way we get the exact position of
// "New folder" and can preselect it in the dialog.
wxString tmpName = _T("25CF809E56B343b5A12D1F0466E3B37A49A9087FDCF8412AA9AF8D1E849D01CF");
if (path.AddSegment(tmpName))
{
wxString pathName = path.GetPath();
int pos = pathName.Find(tmpName);
wxASSERT(pos != -1);
wxString newName = _("New folder");
pathName.Replace(tmpName, newName);
dlg.SetValue(pathName);
dlg.SelectText(pos, pos + newName.Length());
}
if (dlg.ShowModal() != wxID_OK)
return;
path = m_pDirectoryListing->path;
if (!path.ChangePath(dlg.GetValue()))
{
wxBell();
return;
}
m_pState->m_pCommandQueue->ProcessCommand(new CMkdirCommand(path));
}
void CRemoteListView::OnMenuDelete(wxCommandEvent& event)
{
if (!m_pState->IsRemoteIdle())
{
wxBell();
return;
}
long item = -1;
while (true)
{
item = GetNextItem(item, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
if (item == -1)
break;
if (!item || !IsItemValid(item))
{
wxBell();
return;
}
}
if (wxMessageBox(_("Really delete all selected files and/or directories?"), _("Confirmation needed"), wxICON_QUESTION | wxYES_NO, this) != wxYES)
return;
CRecursiveOperation* pRecursiveOperation = m_pState->GetRecursiveOperationHandler();
wxASSERT(pRecursiveOperation);
item = -1;
while (true)
{
item = GetNextItem(item, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
if (item == -1)
break;
int index = GetItemIndex(item);
if (index == -1)
continue;
const CDirentry& entry = (*m_pDirectoryListing)[index];
const wxString& name = entry.name;
if (entry.dir)
{
CServerPath remotePath = m_pDirectoryListing->path;
if (remotePath.AddSegment(name))
pRecursiveOperation->AddDirectoryToVisit(m_pDirectoryListing->path, name);
}
else
m_pState->m_pCommandQueue->ProcessCommand(new CDeleteCommand(m_pDirectoryListing->path, name));
}
pRecursiveOperation->StartRecursiveOperation(CRecursiveOperation::recursive_delete, m_pDirectoryListing->path);
}
void CRemoteListView::OnMenuRename(wxCommandEvent& event)
{
if (!m_pState->IsRemoteIdle())
{
wxBell();
return;
}
int item = GetNextItem(-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
if (item <= 0)
{
wxBell();
return;
}
if (GetNextItem(item, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED) != -1)
{
wxBell();
return;
}
EditLabel(item);
}
void CRemoteListView::OnChar(wxKeyEvent& event)
{
int code = event.GetKeyCode();
if (code == WXK_DELETE)
{
if (GetNextItem(-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED) == -1)
{
wxBell();
return;
}
wxCommandEvent tmp;
OnMenuDelete(tmp);
return;
}
else if (code == WXK_F2)
{
wxCommandEvent tmp;
OnMenuRename(tmp);
}
else if (code == WXK_BACK)
{
if (!m_pState->IsRemoteIdle())
{
wxBell();
return;
}
if (!m_pDirectoryListing)
{
wxBell();
return;
}
m_pState->m_pCommandQueue->ProcessCommand(new CListCommand(m_pDirectoryListing->path, _T("..")));
}
else if (code > 32 && code < 300 && !event.HasModifiers())
{
// Keyboard navigation within items
wxDateTime now = wxDateTime::UNow();
if (m_lastKeyPress.IsValid())
{
wxTimeSpan span = now - m_lastKeyPress;
if (span.GetSeconds() >= 1)
m_prefix = _T("");
}
m_lastKeyPress = now;
wxChar tmp[2];
#if wxUSE_UNICODE
tmp[0] = event.GetUnicodeKey();
#else
tmp[0] = code;
#endif
tmp[1] = 0;
wxString newPrefix = m_prefix + tmp;
bool beep = false;
int item = GetNextItem(-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
if (item != -1)
{
wxString text;
if (!item)
text = _T("..");
else
{
int index = GetItemIndex(item);
if (index != -1)
text = (*m_pDirectoryListing)[index].name;
}
if (text.Length() >= m_prefix.Length() && !m_prefix.CmpNoCase(text.Left(m_prefix.Length())))
beep = true;
}
else if (m_prefix == _T(""))
beep = true;
int start = item;
if (start < 0)
start = 0;
int newPos = FindItemWithPrefix(newPrefix, (item >= 0) ? item : 0);
if (newPos == -1 && m_prefix == tmp && item != -1 && beep)
{
// Search the next item that starts with the same letter
newPrefix = m_prefix;
newPos = FindItemWithPrefix(newPrefix, item + 1);
}
m_prefix = newPrefix;
if (newPos == -1)
{
if (beep)
wxBell();
return;
}
item = GetNextItem(-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
while (item != -1)
{
SetItemState(item, 0, wxLIST_STATE_SELECTED | wxLIST_STATE_FOCUSED);
item = GetNextItem(-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
}
SetItemState(newPos, wxLIST_STATE_SELECTED | wxLIST_STATE_FOCUSED, wxLIST_STATE_SELECTED | wxLIST_STATE_FOCUSED);
EnsureVisible(newPos);
}
else
event.Skip();
}
void CRemoteListView::OnKeyDown(wxKeyEvent& event)
{
const int code = event.GetKeyCode();
if (code == 'A' && event.GetModifiers() == wxMOD_CMD)
{
for (int i = 1; i < GetItemCount(); i++)
{
SetItemState(i, wxLIST_STATE_SELECTED, wxLIST_STATE_SELECTED);
}
}
else
OnChar(event);
//event.Skip();
}
int CRemoteListView::FindItemWithPrefix(const wxString& prefix, int start)
{
for (int i = start; i < (GetItemCount() + start); i++)
{
int item = i % GetItemCount();
wxString fn;
if (!item)
{
fn = _T("..");
fn = fn.Left(prefix.Length());
}
else
{
int index = GetItemIndex(item);
if (index == -1)
continue;
fn = (*m_pDirectoryListing)[index].name.Left(prefix.Length());
}
if (!fn.CmpNoCase(prefix))
return i % GetItemCount();
}
return -1;
}
void CRemoteListView::OnBeginLabelEdit(wxListEvent& event)
{
if (!m_pState->IsRemoteIdle())
{
event.Veto();
wxBell();
return;
}
if (!m_pDirectoryListing)
{
event.Veto();
wxBell();
return;
}
int item = event.GetIndex();
if (!item)
{
event.Veto();
return;
}
if (!IsItemValid(item))
{
event.Veto();
return;
}
}
void CRemoteListView::OnEndLabelEdit(wxListEvent& event)
{
if (event.IsEditCancelled() || event.GetLabel() == _T(""))
{
event.Veto();
return;
}
if (!m_pState->IsRemoteIdle())
{
event.Veto();
wxBell();
return;
}
if (!m_pDirectoryListing)
{
event.Veto();
wxBell();
return;
}
int item = event.GetIndex();
if (!item)
{
event.Veto();
return;
}
int index = GetItemIndex(item);
if (index == -1)
{
event.Veto();
return;
}
const CDirentry& entry = (*m_pDirectoryListing)[index];
wxString newFile = event.GetLabel();
CServerPath newPath = m_pDirectoryListing->path;
if (!newPath.ChangePath(newFile, true))
{
wxMessageBox(_("Filename invalid"), _("Cannot rename file"), wxICON_EXCLAMATION);
event.Veto();
return;
}
if (newPath == m_pDirectoryListing->path)
{
if (entry.name == newFile)
return;
// Check if target file already exists
for (unsigned int i = 0; i < m_pDirectoryListing->GetCount(); i++)
{
if (newFile == (*m_pDirectoryListing)[i].name)
{
if (wxMessageBox(_("Target filename already exists, really continue?"), _("File exists"), wxICON_QUESTION | wxYES_NO) != wxYES)
{
event.Veto();
return;
}
break;
}
}
}
m_pState->m_pCommandQueue->ProcessCommand(new CRenameCommand(m_pDirectoryListing->path, entry.name, newPath, newFile));
}
void CRemoteListView::OnMenuChmod(wxCommandEvent& event)
{
if (!m_pState->IsRemoteConnected() || !m_pState->IsRemoteIdle())
{
wxBell();
return;
}
int fileCount = 0;
int dirCount = 0;
wxString name;
char permissions[9] = {0};
const unsigned char permchars[3] = {'r', 'w', 'x'};
long item = -1;
while (true)
{
item = GetNextItem(item, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
if (item == -1)
break;
if (!item)
return;
int index = GetItemIndex(item);
if (index == -1)
return;
const CDirentry& entry = (*m_pDirectoryListing)[index];
if (entry.dir)
dirCount++;
else
fileCount++;
name = entry.name;
if (entry.permissions.Length() == 10)
{
for (int i = 0; i < 9; i++)
{
bool set = entry.permissions[i + 1] == permchars[i % 3];
if (!permissions[i] || permissions[i] == (set ? 2 : 1))
permissions[i] = set ? 2 : 1;
else
permissions[i] = -1;
}
}
}
for (int i = 0; i < 9; i++)
if (permissions[i] == -1)
permissions[i] = 0;
CChmodDialog* pChmodDlg = new CChmodDialog;
if (!pChmodDlg->Create(this, fileCount, dirCount, name, permissions))
{
pChmodDlg->Destroy();
pChmodDlg = 0;
return;
}
if (pChmodDlg->ShowModal() != wxID_OK)
{
pChmodDlg->Destroy();
pChmodDlg = 0;
return;
}
// State may have changed while chmod dialog was shown
if (!m_pState->IsRemoteConnected() || !m_pState->IsRemoteIdle())
{
pChmodDlg->Destroy();
pChmodDlg = 0;
wxBell();
return;
}
const int applyType = pChmodDlg->GetApplyType();
CRecursiveOperation* pRecursiveOperation = m_pState->GetRecursiveOperationHandler();
wxASSERT(pRecursiveOperation);
item = -1;
while (true)
{
item = GetNextItem(item, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
if (item == -1)
break;
if (!item)
{
pChmodDlg->Destroy();
pChmodDlg = 0;
return;
}
int index = GetItemIndex(item);
if (index == -1)
{
pChmodDlg->Destroy();
pChmodDlg = 0;
return;
}
const CDirentry& entry = (*m_pDirectoryListing)[index];
if (!applyType ||
(!entry.dir && applyType == 1) ||
(entry.dir && applyType == 2))
{
char permissions[9];
bool res = pChmodDlg->ConvertPermissions(entry.permissions, permissions);
wxString newPerms = pChmodDlg->GetPermissions(res ? permissions : 0);
m_pState->m_pCommandQueue->ProcessCommand(new CChmodCommand(m_pDirectoryListing->path, entry.name, newPerms));
}
if (pChmodDlg->Recursive() && entry.dir)
pRecursiveOperation->AddDirectoryToVisit(m_pDirectoryListing->path, entry.name);
}
if (pChmodDlg->Recursive())
{
pRecursiveOperation->SetChmodDialog(pChmodDlg);
pRecursiveOperation->StartRecursiveOperation(CRecursiveOperation::recursive_chmod, m_pDirectoryListing->path);
// Refresh listing. This gets done implicitely by the recursive operation, so
// only it if not recursing.
if (pRecursiveOperation->GetOperationMode() != CRecursiveOperation::recursive_chmod)
m_pState->m_pCommandQueue->ProcessCommand(new CListCommand(m_pDirectoryListing->path));
}
else
pChmodDlg->Destroy();
}
void CRemoteListView::ApplyCurrentFilter()
{
if (m_fileData.size() <= 1)
return;
wxString focused;
std::list<wxString> selectedNames = RememberSelectedItems(focused);
CFilterDialog filter;
m_indexMapping.clear();
const unsigned int count = m_pDirectoryListing->GetCount();
m_indexMapping.push_back(count);
for (unsigned int i = 0; i < count; i++)
{
const CDirentry& entry = (*m_pDirectoryListing)[i];
if (!filter.FilenameFiltered(entry.name, entry.dir, entry.size, false))
m_indexMapping.push_back(i);
}
SetItemCount(m_indexMapping.size());
SortList();
ReselectItems(selectedNames, focused);
}
std::list<wxString> CRemoteListView::RememberSelectedItems(wxString& focused)
{
std::list<wxString> selectedNames;
// Remember which items were selected
int item = -1;
while (true)
{
item = GetNextItem(item, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
if (item == -1)
break;
if (!item)
{
selectedNames.push_back(_T(".."));
continue;
}
const CDirentry& entry = (*m_pDirectoryListing)[m_indexMapping[item]];
if (entry.dir)
selectedNames.push_back(_T("d") + entry.name);
else
selectedNames.push_back(_T("-") + entry.name);
SetItemState(item, 0, wxLIST_STATE_SELECTED);
}
item = GetNextItem(-1, wxLIST_NEXT_ALL, wxLIST_STATE_FOCUSED);
if (item != -1)
{
if (!item)
focused = _T("..");
else
focused = (*m_pDirectoryListing)[m_indexMapping[item]].name;
SetItemState(item, 0, wxLIST_STATE_FOCUSED);
}
return selectedNames;
}
void CRemoteListView::ReselectItems(std::list<wxString>& selectedNames, wxString focused)
{
if (!GetItemCount())
return;
if (focused == _T(".."))
{
focused = _T("");
SetItemState(0, wxLIST_STATE_FOCUSED, wxLIST_STATE_FOCUSED);
return;
}
if (selectedNames.empty())
{
if (focused == _T(""))
return;
for (unsigned int i = 1; i < m_indexMapping.size(); i++)
if ((*m_pDirectoryListing)[m_indexMapping[i]].name == focused)
{
SetItemState(i, wxLIST_STATE_FOCUSED, wxLIST_STATE_FOCUSED);
return;
}
return;
}
if (selectedNames.front() == _T(".."))
{
selectedNames.pop_front();
SetItemState(0, wxLIST_STATE_SELECTED, wxLIST_STATE_SELECTED);
}
int firstSelected = -1;
// Reselect previous items if neccessary.
// Sorting direction did not change. We just have to scan through items once
unsigned i = 1;
for (std::list<wxString>::const_iterator iter = selectedNames.begin(); iter != selectedNames.end(); iter++)
{
while (i < m_indexMapping.size())
{
const CDirentry& entry = (*m_pDirectoryListing)[m_indexMapping[i]];
if (entry.name == focused)
{
SetItemState(i, wxLIST_STATE_FOCUSED, wxLIST_STATE_FOCUSED);
focused = _T("");
}
if (entry.dir && *iter == (_T("d") + entry.name))
{
if (firstSelected == -1)
firstSelected = i;
SetItemState(i, wxLIST_STATE_SELECTED, wxLIST_STATE_SELECTED);
i++;
break;
}
else if (*iter == (_T("-") + entry.name))
{
if (firstSelected == -1)
firstSelected = i;
SetItemState(i, wxLIST_STATE_SELECTED, wxLIST_STATE_SELECTED);
i++;
break;
}
else
i++;
}
if (i == m_indexMapping.size())
break;
}
if (focused != _T(""))
{
if (firstSelected != -1)
SetItemState(firstSelected, wxLIST_STATE_FOCUSED, wxLIST_STATE_FOCUSED);
else
SetItemState(0, wxLIST_STATE_FOCUSED, wxLIST_STATE_FOCUSED);
}
}
void CRemoteListView::OnSize(wxSizeEvent& event)
{
event.Skip();
RepositionInfoText();
}
void CRemoteListView::RepositionInfoText()
{
if (!m_pInfoText)
return;
wxRect rect = GetClientRect();
if (!GetItemCount())
rect.y = 60;
else
{
wxRect itemRect;
GetItemRect(0, itemRect);
rect.y = wxMax(60, itemRect.GetBottom() + 1);
}
m_pInfoText->SetSize(rect);
}
void CRemoteListView::OnStateChange(unsigned int event, const wxString& data)
{
wxASSERT(m_pState);
if (event == STATECHANGE_REMOTE_DIR)
SetDirectoryListing(m_pState->GetRemoteDir(), false);
else if (event == STATECHANGE_REMOTE_DIR_MODIFIED)
SetDirectoryListing(m_pState->GetRemoteDir(), true);
else if (event == STATECHANGE_APPLYFILTER)
ApplyCurrentFilter();
}
void CRemoteListView::SetInfoText(const wxString& text)
{
if (text == _T(""))
{
delete m_pInfoText;
m_pInfoText = 0;
return;
}
if (!m_pInfoText)
{
m_pInfoText = new CInfoText(this, text);
RepositionInfoText();
return;
}
if (m_pInfoText->m_text == _T("<") + text + _T(">"))
return;
m_pInfoText->m_text = text;
m_pInfoText->Refresh();
}
void CRemoteListView::OnBeginDrag(wxListEvent& event)
{
if (!m_pState->IsRemoteIdle())
{
wxBell();
return;
}
if (GetNextItem(-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED) == -1)
{
// Nothing selected
return;
}
bool idle = m_pState->m_pCommandQueue->Idle();
long item = -1;
while (true)
{
item = GetNextItem(item, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
if (item == -1)
break;
if (!item)
{
// Can't drag ".."
wxBell();
return;
}
int index = GetItemIndex(item);
if (index == -1)
continue;
if ((*m_pDirectoryListing)[index].dir && !idle)
{
// Drag could result in recursive operation, don't allow at this point
wxBell();
return;
}
}
wxDataObjectComposite object;
const CServer* const pServer = m_pState->GetServer();
if (!pServer)
return;
const CServer server = *pServer;
const CServerPath path = m_pDirectoryListing->path;
CRemoteDataObject *pRemoteDataObject = new CRemoteDataObject(*pServer, m_pDirectoryListing->path);
// Add files to remote data object
item = -1;
while (true)
{
item = GetNextItem(item, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
if (item == -1)
break;
int index = GetItemIndex(item);
const CDirentry& entry = (*m_pDirectoryListing)[index];
pRemoteDataObject->AddFile(entry.name, entry.dir, entry.size);
}
pRemoteDataObject->Finalize();
object.Add(pRemoteDataObject, true);
#if FZ3_USESHELLEXT
CShellExtensionInterface* ext = CShellExtensionInterface::CreateInitialized();
if (ext)
{
const wxString& file = ext->GetDragDirectory();
wxASSERT(file != _T(""));
wxFileDataObject *pFileDataObject = new wxFileDataObject;
pFileDataObject->AddFile(file);
object.Add(pFileDataObject);
}
#endif
wxDropSource source(this);
source.SetData(object);
if (source.DoDragDrop() != wxDragCopy)
{
#if FZ3_USESHELLEXT
delete ext;
ext = 0;
#endif
return;
}
const wxDataFormat fmt = object.GetReceivedFormat();
#if FZ3_USESHELLEXT
if (ext)
{
if (!pRemoteDataObject->DidSendData())
{
const CServer* pServer = m_pState->GetServer();
if (!m_pState->IsRemoteIdle() ||
!pServer || *pServer != server ||
!m_pDirectoryListing || m_pDirectoryListing->path != path)
{
// Remote listing has changed since drag started
wxBell();
delete ext;
ext = 0;
return;
}
// Same checks as before
bool idle = m_pState->m_pCommandQueue->Idle();
long item = -1;
while (true)
{
item = GetNextItem(item, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
if (item == -1)
break;
if (!item)
{
// Can't drag ".."
wxBell();
delete ext;
ext = 0;
return;
}
int index = GetItemIndex(item);
if (index == -1)
continue;
if ((*m_pDirectoryListing)[index].dir && !idle)
{
// Drag could result in recursive operation, don't allow at this point
wxBell();
delete ext;
ext = 0;
return;
}
}
wxString target = ext->GetTarget();
if (target == _T(""))
{
delete ext;
ext = 0;
wxMessageBox(_("Could not determine the target of the Drag&Drop operation.\nEither the shell extension is not installed properly or you didn't drop the files into an Explorer window."));
return;
}
TransferSelectedFiles(target, false);
delete ext;
ext = 0;
return;
}
delete ext;
ext = 0;
}
#endif
}
bool CRemoteListView::DownloadDroppedFiles(const CRemoteDataObject* pRemoteDataObject, wxString path, bool queueOnly)
{
if (!m_pState->IsRemoteIdle())
return false;
CRecursiveOperation* pRecursiveOperation = m_pState->GetRecursiveOperationHandler();
wxASSERT(pRecursiveOperation);
const std::list<CRemoteDataObject::t_fileInfo>& files = pRemoteDataObject->GetFiles();
for (std::list<CRemoteDataObject::t_fileInfo>::const_iterator iter = files.begin(); iter != files.end(); iter++)
{
if (!iter->dir)
continue;
pRecursiveOperation->AddDirectoryToVisit(pRemoteDataObject->GetServerPath(), iter->name, path + iter->name);
}
pRecursiveOperation->StartRecursiveOperation(queueOnly ? CRecursiveOperation::recursive_addtoqueue : CRecursiveOperation::recursive_download, pRemoteDataObject->GetServerPath());
return true;
}
void CRemoteListView::InitDateFormat()
{
const wxString& dateFormat = COptions::Get()->GetOption(OPTION_DATE_FORMAT);
const wxString& timeFormat = COptions::Get()->GetOption(OPTION_TIME_FORMAT);
if (dateFormat == _T("1"))
m_dateFormat = _T("%Y-%m-%d");
else if (dateFormat[0] == '2')
m_dateFormat = dateFormat.Mid(1);
else
m_dateFormat = _T("%x");
if (timeFormat == _T("1"))
m_timeFormat = m_dateFormat + _T(" %H:%M");
else if (timeFormat[0] == '2')
m_timeFormat = m_dateFormat + _T(" ") + timeFormat.Mid(1);
else
m_timeFormat = m_dateFormat + _T(" %X");
}
syntax highlighted by Code2HTML, v. 0.9.1