source: filezilla/trunk/fuentes/src/interface/RemoteTreeView.cpp @ 130

Last change on this file since 130 was 130, checked in by jrpelegrina, 3 years ago

First release to xenial

File size: 35.4 KB
Line 
1#include <filezilla.h>
2#include "RemoteTreeView.h"
3#include "commandqueue.h"
4#include <wx/dnd.h>
5#include "dndobjects.h"
6#include "chmoddialog.h"
7#include "recursive_operation.h"
8#include "inputdialog.h"
9#include "dragdropmanager.h"
10#include "drop_target_ex.h"
11#include <wx/clipbrd.h>
12#include "queue.h"
13#include "QueueView.h"
14#include "themeprovider.h"
15#include "Options.h"
16
17#include <algorithm>
18
19class CItemData : public wxTreeItemData
20{
21public:
22        CItemData(CServerPath path) : m_path(path) {}
23        CServerPath m_path;
24};
25
26class CRemoteTreeViewDropTarget : public CScrollableDropTarget<wxTreeCtrlEx>
27{
28public:
29        CRemoteTreeViewDropTarget(CRemoteTreeView* pRemoteTreeView)
30                : CScrollableDropTarget<wxTreeCtrlEx>(pRemoteTreeView)
31                , m_pRemoteTreeView(pRemoteTreeView)
32                , m_pFileDataObject(new wxFileDataObject())
33                , m_pRemoteDataObject(new CRemoteDataObject())
34        {
35                m_pDataObject = new wxDataObjectComposite;
36                m_pDataObject->Add(m_pRemoteDataObject, true);
37                m_pDataObject->Add(m_pFileDataObject, false);
38                SetDataObject(m_pDataObject);
39        }
40
41        void ClearDropHighlight()
42        {
43                const wxTreeItemId dropHighlight = m_pRemoteTreeView->m_dropHighlight;
44                if (dropHighlight != wxTreeItemId()) {
45                        m_pRemoteTreeView->SetItemDropHighlight(dropHighlight, false);
46                        m_pRemoteTreeView->m_dropHighlight = wxTreeItemId();
47                }
48        }
49
50        wxTreeItemId GetHit(const wxPoint& point)
51        {
52                int flags = 0;
53                wxTreeItemId hit = m_pRemoteTreeView->HitTest(point, flags);
54
55                if (flags & (wxTREE_HITTEST_ABOVE | wxTREE_HITTEST_BELOW | wxTREE_HITTEST_NOWHERE | wxTREE_HITTEST_TOLEFT | wxTREE_HITTEST_TORIGHT))
56                        return wxTreeItemId();
57
58                return hit;
59        }
60
61        virtual wxDragResult OnData(wxCoord x, wxCoord y, wxDragResult def)
62        {
63                if (def == wxDragError ||
64                        def == wxDragNone ||
65                        def == wxDragCancel)
66                        return def;
67
68                wxTreeItemId hit = GetHit(wxPoint(x, y));
69                if (!hit)
70                        return wxDragNone;
71
72                CServerPath path = m_pRemoteTreeView->GetPathFromItem(hit);
73                if (path.empty())
74                        return wxDragNone;
75
76                if (!GetData())
77                        return wxDragError;
78
79                CDragDropManager* pDragDropManager = CDragDropManager::Get();
80                if (pDragDropManager)
81                        pDragDropManager->pDropTarget = m_pRemoteTreeView;
82
83                if (m_pDataObject->GetReceivedFormat() == m_pFileDataObject->GetFormat())
84                        m_pRemoteTreeView->m_pState->UploadDroppedFiles(m_pFileDataObject, path, false);
85                else
86                {
87                        if (m_pRemoteDataObject->GetProcessId() != (int)wxGetProcessId())
88                        {
89                                wxMessageBoxEx(_("Drag&drop between different instances of FileZilla has not been implemented yet."));
90                                return wxDragNone;
91                        }
92
93                        if (!m_pRemoteTreeView->m_pState->GetServer() || !m_pRemoteDataObject->GetServer().EqualsNoPass(*m_pRemoteTreeView->m_pState->GetServer()))
94                        {
95                                wxMessageBoxEx(_("Drag&drop between different servers has not been implemented yet."));
96                                return wxDragNone;
97                        }
98
99                        // Make sure path path is valid
100                        if (path == m_pRemoteDataObject->GetServerPath())
101                        {
102                                wxMessageBoxEx(_("Source and path of the drop operation are identical"));
103                                return wxDragNone;
104                        }
105
106                        const std::list<CRemoteDataObject::t_fileInfo>& files = m_pRemoteDataObject->GetFiles();
107                        for (std::list<CRemoteDataObject::t_fileInfo>::const_iterator iter = files.begin(); iter != files.end(); ++iter)
108                        {
109                                const CRemoteDataObject::t_fileInfo& info = *iter;
110                                if (info.dir)
111                                {
112                                        CServerPath dir = m_pRemoteDataObject->GetServerPath();
113                                        dir.AddSegment(info.name);
114                                        if (dir == path)
115                                                return wxDragNone;
116                                        else if (dir.IsParentOf(path, false))
117                                        {
118                                                wxMessageBoxEx(_("A directory cannot be dragged into one of its subdirectories."));
119                                                return wxDragNone;
120                                        }
121                                }
122                        }
123
124                        for (std::list<CRemoteDataObject::t_fileInfo>::const_iterator iter = files.begin(); iter != files.end(); ++iter)
125                        {
126                                const CRemoteDataObject::t_fileInfo& info = *iter;
127                                m_pRemoteTreeView->m_pState->m_pCommandQueue->ProcessCommand(
128                                        new CRenameCommand(m_pRemoteDataObject->GetServerPath(), info.name, path, info.name)
129                                        );
130                        }
131
132                        return wxDragNone;
133                }
134
135                return def;
136        }
137
138        virtual bool OnDrop(wxCoord x, wxCoord y)
139        {
140                if (!CScrollableDropTarget<wxTreeCtrlEx>::OnDrop(x, y)) {
141                        return false;
142                }
143                ClearDropHighlight();
144
145                wxTreeItemId hit = GetHit(wxPoint(x, y));
146                if (!hit)
147                        return false;
148
149                const CServerPath& path = m_pRemoteTreeView->GetPathFromItem(hit);
150                if (path.empty())
151                        return false;
152
153                return true;
154        }
155
156        wxTreeItemId DisplayDropHighlight(wxPoint point)
157        {
158                wxTreeItemId hit = GetHit(point);
159                if (!hit) {
160                        ClearDropHighlight();
161                        return wxTreeItemId();
162                }
163
164                const CServerPath& path = m_pRemoteTreeView->GetPathFromItem(hit);
165
166                if (path.empty()) {
167                        ClearDropHighlight();
168                        return wxTreeItemId();
169                }
170
171                const wxTreeItemId dropHighlight = m_pRemoteTreeView->m_dropHighlight;
172                if (dropHighlight != wxTreeItemId())
173                        m_pRemoteTreeView->SetItemDropHighlight(dropHighlight, false);
174
175                m_pRemoteTreeView->SetItemDropHighlight(hit, true);
176                m_pRemoteTreeView->m_dropHighlight = hit;
177
178                return hit;
179        }
180
181        virtual wxDragResult OnDragOver(wxCoord x, wxCoord y, wxDragResult def)
182        {
183                def = CScrollableDropTarget<wxTreeCtrlEx>::OnDragOver(x, y, def);
184
185                if (def == wxDragError ||
186                        def == wxDragNone ||
187                        def == wxDragCancel)
188                {
189                        ClearDropHighlight();
190                        return def;
191                }
192
193                wxTreeItemId hit = DisplayDropHighlight(wxPoint(x, y));
194                if (!hit.IsOk())
195                        return wxDragNone;
196
197                if (def == wxDragLink)
198                        def = wxDragCopy;
199
200                return def;
201        }
202
203        virtual void OnLeave()
204        {
205                CScrollableDropTarget<wxTreeCtrlEx>::OnLeave();
206                ClearDropHighlight();
207        }
208
209        virtual wxDragResult OnEnter(wxCoord x, wxCoord y, wxDragResult def)
210        {
211                def = CScrollableDropTarget<wxTreeCtrlEx>::OnEnter(x, y, def);
212                return OnDragOver(x, y, def);
213        }
214
215protected:
216        CRemoteTreeView *m_pRemoteTreeView;
217        wxFileDataObject* m_pFileDataObject;
218        CRemoteDataObject* m_pRemoteDataObject;
219        wxDataObjectComposite* m_pDataObject;
220};
221
222IMPLEMENT_CLASS(CRemoteTreeView, wxTreeCtrlEx)
223
224BEGIN_EVENT_TABLE(CRemoteTreeView, wxTreeCtrlEx)
225EVT_TREE_ITEM_EXPANDING(wxID_ANY, CRemoteTreeView::OnItemExpanding)
226EVT_TREE_SEL_CHANGED(wxID_ANY, CRemoteTreeView::OnSelectionChanged)
227EVT_TREE_ITEM_ACTIVATED(wxID_ANY, CRemoteTreeView::OnItemActivated)
228EVT_TREE_BEGIN_DRAG(wxID_ANY, CRemoteTreeView::OnBeginDrag)
229EVT_TREE_ITEM_MENU(wxID_ANY, CRemoteTreeView::OnContextMenu)
230EVT_MENU(XRCID("ID_CHMOD"), CRemoteTreeView::OnMenuChmod)
231EVT_MENU(XRCID("ID_DOWNLOAD"), CRemoteTreeView::OnMenuDownload)
232EVT_MENU(XRCID("ID_ADDTOQUEUE"), CRemoteTreeView::OnMenuDownload)
233EVT_MENU(XRCID("ID_DELETE"), CRemoteTreeView::OnMenuDelete)
234EVT_MENU(XRCID("ID_RENAME"), CRemoteTreeView::OnMenuRename)
235EVT_TREE_BEGIN_LABEL_EDIT(wxID_ANY, CRemoteTreeView::OnBeginLabelEdit)
236EVT_TREE_END_LABEL_EDIT(wxID_ANY, CRemoteTreeView::OnEndLabelEdit)
237EVT_MENU(XRCID("ID_MKDIR"), CRemoteTreeView::OnMkdir)
238EVT_MENU(XRCID("ID_MKDIR_CHGDIR"), CRemoteTreeView::OnMenuMkdirChgDir)
239EVT_CHAR(CRemoteTreeView::OnChar)
240EVT_MENU(XRCID("ID_GETURL"), CRemoteTreeView::OnMenuGeturl)
241END_EVENT_TABLE()
242
243CRemoteTreeView::CRemoteTreeView(wxWindow* parent, wxWindowID id, CState* pState, CQueueView* pQueue)
244        : wxTreeCtrlEx(parent, id, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL | wxTR_EDIT_LABELS | wxTR_LINES_AT_ROOT | wxTR_HAS_BUTTONS | wxNO_BORDER | wxTR_HIDE_ROOT),
245        CSystemImageList(16),
246        CStateEventHandler(pState)
247{
248#ifdef __WXMAC__
249        SetFont(wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT));
250#endif
251
252        pState->RegisterHandler(this, STATECHANGE_REMOTE_DIR);
253        pState->RegisterHandler(this, STATECHANGE_APPLYFILTER);
254
255        CreateImageList();
256
257        UpdateSortMode();
258        RegisterOption(OPTION_FILELIST_NAMESORT);
259
260        m_busy = false;
261        m_pQueue = pQueue;
262        AddRoot(_T(""));
263        m_ExpandAfterList = wxTreeItemId();
264
265        SetDropTarget(new CRemoteTreeViewDropTarget(this));
266
267        Enable(false);
268}
269
270CRemoteTreeView::~CRemoteTreeView()
271{
272        SetImageList(0);
273        delete m_pImageList;
274}
275
276void CRemoteTreeView::OnStateChange(CState* pState, enum t_statechange_notifications notification, const wxString&, const void* v)
277{
278        if (notification == STATECHANGE_REMOTE_DIR)
279                SetDirectoryListing(pState->GetRemoteDir(), v ? *reinterpret_cast<bool const*>(v) : false);
280        else if (notification == STATECHANGE_APPLYFILTER)
281                ApplyFilters(false);
282}
283
284void CRemoteTreeView::SetDirectoryListing(std::shared_ptr<CDirectoryListing> const& pListing, bool modified)
285{
286        m_busy = true;
287
288        if (!pListing) {
289                m_ExpandAfterList = wxTreeItemId();
290                DeleteAllItems();
291                AddRoot(_T(""));
292                m_busy = false;
293                if (FindFocus() == this) {
294                        wxNavigationKeyEvent *evt = new wxNavigationKeyEvent();
295                        evt->SetFromTab(true);
296                        evt->SetEventObject(this);
297                        evt->SetDirection(true);
298                        QueueEvent(evt);
299                }
300                Enable(false);
301                m_contextMenuItem = wxTreeItemId();
302                return;
303        }
304        Enable(true);
305#ifdef __WXGTK__
306        GetParent()->m_dirtyTabOrder = true;
307#endif
308
309        if (pListing->get_unsure_flags() && !(pListing->get_unsure_flags() & ~(CDirectoryListing::unsure_unknown | CDirectoryListing::unsure_file_mask))) {
310                // Just files changed, does not affect directory tree
311                m_busy = false;
312                return;
313        }
314
315#ifndef __WXMSW__
316        Freeze();
317#endif
318        wxTreeItemId parent = MakeParent(pListing->path, !modified);
319        if (!parent)
320        {
321                m_busy = false;
322#ifndef __WXMSW__
323                Thaw();
324#endif
325                return;
326        }
327
328        if (!IsExpanded(parent) && parent != m_ExpandAfterList)
329        {
330                DeleteChildren(parent);
331                CFilterManager filter;
332                if (HasSubdirs(*pListing, filter))
333                        AppendItem(parent, _T(""), -1, -1);
334        }
335        else
336        {
337                RefreshItem(parent, *pListing, !modified);
338
339                if (m_ExpandAfterList == parent)
340                {
341#ifndef __WXMSW__
342                        // Prevent CalculatePositions from being called
343                        wxGenericTreeItem *anchor = m_anchor;
344                        m_anchor = 0;
345#endif
346                        Expand(parent);
347#ifndef __WXMSW__
348                        m_anchor = anchor;
349#endif
350                }
351        }
352        m_ExpandAfterList = wxTreeItemId();
353
354        SetItemImages(parent, false);
355
356#ifndef __WXMSW__
357        Thaw();
358#endif
359        if (!modified)
360                SafeSelectItem(parent);
361#ifndef __WXMSW__
362        else
363                Refresh();
364#endif
365
366        m_busy = false;
367}
368
369wxTreeItemId CRemoteTreeView::MakeParent(CServerPath path, bool select)
370{
371        std::vector<wxString> pieces;
372        pieces.reserve(path.SegmentCount() + 1);
373        while (path.HasParent()) {
374                pieces.push_back(path.GetLastSegment());
375                path = path.GetParent();
376        }
377        wxASSERT(!path.GetPath().empty());
378        pieces.push_back(path.GetPath());
379
380        const wxTreeItemId root = GetRootItem();
381        wxTreeItemId parent = root;
382
383        for (std::vector<wxString>::const_reverse_iterator iter = pieces.rbegin(); iter != pieces.rend(); ++iter) {
384                if (iter != pieces.rbegin())
385                        path.AddSegment(*iter);
386
387                wxTreeItemIdValue cookie;
388                wxTreeItemId child = GetFirstChild(parent, cookie);
389                if (child && GetItemText(child).empty()) {
390                        Delete(child);
391                        child = wxTreeItemId();
392                        if (parent != root)
393                                ListExpand(parent);
394                }
395                for (child = GetFirstChild(parent, cookie); child; child = GetNextSibling(child)) {
396                        const wxString& text = GetItemText(child);
397                        if (text == *iter)
398                                break;
399                }
400                if (!child) {
401                        CDirectoryListing listing;
402
403                        if (m_pState->m_pEngine->CacheLookup(path, listing) == FZ_REPLY_OK) {
404                                child = AppendItem(parent, *iter, 0, 2, path.HasParent() ? 0 : new CItemData(path));
405                                SetItemImages(child, false);
406                        }
407                        else {
408                                child = AppendItem(parent, *iter, 1, 3, path.HasParent() ? 0 : new CItemData(path));
409                                SetItemImages(child, true);
410                        }
411                        SortChildren(parent);
412
413                        auto nextIter = iter;
414                        ++nextIter;
415                        if (nextIter != pieces.rend())
416                                DisplayItem(child, listing);
417                }
418                if (select && iter != pieces.rbegin()) {
419#ifndef __WXMSW__
420                        // Prevent CalculatePositions from being called
421                        wxGenericTreeItem *anchor = m_anchor;
422                        m_anchor = 0;
423#endif
424                        Expand(parent);
425#ifndef __WXMSW__
426                        m_anchor = anchor;
427#endif
428                }
429
430                parent = child;
431        }
432
433        return parent;
434}
435
436wxBitmap CRemoteTreeView::CreateIcon(int index, const wxString& overlay /*=_T("")*/)
437{
438        // Create memory DC
439        wxSize s = CThemeProvider::GetIconSize(iconSizeSmall);
440#ifdef __WXMSW__
441        wxBitmap bmp(s.x, s.y, 32);
442#else
443        wxBitmap bmp(s.x, s.y, 24);
444#endif
445        wxMemoryDC dc;
446        dc.SelectObject(bmp);
447
448        // Make sure the background is set correctly
449        dc.SetBrush(wxBrush(GetBackgroundColour()));
450        dc.SetPen(wxPen(GetBackgroundColour()));
451        dc.DrawRectangle(0, 0, s.x, s.y);
452
453        // Draw item from system image list
454        GetSystemImageList()->Draw(index, dc, 0, 0, wxIMAGELIST_DRAW_TRANSPARENT);
455
456        // Load overlay
457        if (!overlay.empty()) {
458                wxImage unknownIcon = wxArtProvider::GetBitmap(overlay, wxART_OTHER, CThemeProvider::GetIconSize(iconSizeSmall)).ConvertToImage();
459
460                // Convert mask into alpha channel
461                if (unknownIcon.IsOk() && !unknownIcon.HasAlpha()) {
462                        wxASSERT(unknownIcon.HasMask());
463                        unknownIcon.InitAlpha();
464                }
465
466                // Draw overlay
467                dc.DrawBitmap(unknownIcon, 0, 0, true);
468        }
469
470        dc.SelectObject(wxNullBitmap);
471        return bmp;
472}
473
474void CRemoteTreeView::CreateImageList()
475{
476        wxSize s = CThemeProvider::GetIconSize(iconSizeSmall);
477        m_pImageList = new wxImageList(s.x, s.y, true, 4);
478
479        // Normal directory
480        int index = GetIconIndex(iconType::dir, _T("{78013B9C-3532-4fe1-A418-5CD1955127CC}"), false);
481        m_pImageList->Add(CreateIcon(index));
482        m_pImageList->Add(CreateIcon(index, _T("ART_UNKNOWN")));
483
484        // Opened directory
485        index = GetIconIndex(iconType::opened_dir, _T("{78013B9C-3532-4fe1-A418-5CD1955127CC}"), false);
486        m_pImageList->Add(CreateIcon(index));
487        m_pImageList->Add(CreateIcon(index, _T("ART_UNKNOWN")));
488
489        SetImageList(m_pImageList);
490}
491
492bool CRemoteTreeView::HasSubdirs(const CDirectoryListing& listing, const CFilterManager& filter)
493{
494        if (!listing.has_dirs())
495                return false;
496
497        if (!filter.HasActiveFilters())
498                return true;
499
500        const wxString path = listing.path.GetPath();
501        for (unsigned int i = 0; i < listing.GetCount(); i++)
502        {
503                if (!listing[i].is_dir())
504                        continue;
505
506                if (filter.FilenameFiltered(listing[i].name, path, true, -1, false, 0, listing[i].time))
507                        continue;
508
509                return true;
510        }
511
512        return false;
513}
514
515void CRemoteTreeView::DisplayItem(wxTreeItemId parent, const CDirectoryListing& listing)
516{
517        DeleteChildren(parent);
518
519        const wxString path = listing.path.GetPath();
520
521        CFilterDialog filter;
522        for (unsigned int i = 0; i < listing.GetCount(); ++i) {
523                if (!listing[i].is_dir())
524                        continue;
525
526                if (filter.FilenameFiltered(listing[i].name, path, true, -1, false, 0, listing[i].time))
527                        continue;
528
529                const wxString& name = listing[i].name;
530                CServerPath subdir = listing.path;
531                subdir.AddSegment(name);
532
533                CDirectoryListing subListing;
534
535                if (m_pState->m_pEngine->CacheLookup(subdir, subListing) == FZ_REPLY_OK) {
536                        wxTreeItemId child = AppendItem(parent, name, 0, 2, 0);
537                        SetItemImages(child, false);
538
539                        if (HasSubdirs(subListing, filter))
540                                AppendItem(child, _T(""), -1, -1);
541                }
542                else {
543                        wxTreeItemId child = AppendItem(parent, name, 1, 3, 0);
544                        SetItemImages(child, true);
545                }
546        }
547        SortChildren(parent);
548}
549
550void CRemoteTreeView::RefreshItem(wxTreeItemId parent, const CDirectoryListing& listing, bool will_select_parent)
551{
552        SetItemImages(parent, false);
553
554        wxTreeItemIdValue cookie;
555        wxTreeItemId child = GetFirstChild(parent, cookie);
556        if (!child || GetItemText(child).empty()) {
557                DisplayItem(parent, listing);
558                return;
559        }
560
561        CFilterManager filter;
562
563        wxString const path = listing.path.GetPath();
564
565        std::vector<wxString> dirs;
566        for (unsigned int i = 0; i < listing.GetCount(); ++i) {
567                if (!listing[i].is_dir())
568                        continue;
569
570                if (!filter.FilenameFiltered(listing[i].name, path, true, -1, false, 0, listing[i].time))
571                        dirs.push_back(listing[i].name);
572        }
573
574        auto const& sortFunc = CFileListCtrlSortBase::GetCmpFunction(m_nameSortMode);
575        std::sort(dirs.begin(), dirs.end(), [&](auto const& lhs, auto const& rhs) { return sortFunc(lhs, rhs) < 0; });
576
577        bool inserted = false;
578        child = GetLastChild(parent);
579        auto iter = dirs.rbegin();
580        while (child && iter != dirs.rend()) {
581                int cmp = sortFunc(GetItemText(child), *iter);
582
583                if (!cmp) {
584                        CServerPath childPath = listing.path;
585                        childPath.AddSegment(*iter);
586
587                        CDirectoryListing subListing;
588                        if (m_pState->m_pEngine->CacheLookup(childPath, subListing) == FZ_REPLY_OK) {
589                                if (!GetLastChild(child) && HasSubdirs(subListing, filter))
590                                        AppendItem(child, _T(""), -1, -1);
591                                SetItemImages(child, false);
592                        }
593                        else
594                                SetItemImages(child, true);
595
596                        child = GetPrevSibling(child);
597                        ++iter;
598                }
599                else if (cmp > 0) {
600                        // Child no longer exists
601                        wxTreeItemId sel = GetSelection();
602                        while (sel && sel != child)
603                                sel = GetItemParent(sel);
604                        wxTreeItemId prev = GetPrevSibling(child);
605                        if (!sel || will_select_parent)
606                                Delete(child);
607                        child = prev;
608                }
609                else if (cmp < 0) {
610                        // New directory
611                        CServerPath childPath = listing.path;
612                        childPath.AddSegment(*iter);
613
614                        CDirectoryListing subListing;
615                        if (m_pState->m_pEngine->CacheLookup(childPath, subListing) == FZ_REPLY_OK) {
616                                wxTreeItemId childItem = AppendItem(parent, *iter, 0, 2, 0);
617                                if (childItem) {
618                                        SetItemImages(childItem, false);
619
620                                        if (HasSubdirs(subListing, filter)) {
621                                                AppendItem(childItem, _T(""), -1, -1);
622                                        }
623                                }
624                        }
625                        else {
626                                wxTreeItemId childItem = AppendItem(parent, *iter, 1, 3, 0);
627                                if (childItem) {
628                                        SetItemImages(childItem, true);
629                                }
630                        }
631
632                        ++iter;
633                        inserted = true;
634                }
635        }
636        while (child) {
637                // Child no longer exists
638                wxTreeItemId sel = GetSelection();
639                while (sel && sel != child)
640                        sel = GetItemParent(sel);
641                wxTreeItemId prev = GetPrevSibling(child);
642                if (!sel || will_select_parent)
643                        Delete(child);
644                child = prev;
645        }
646        while (iter != dirs.rend()) {
647                CServerPath childPath = listing.path;
648                childPath.AddSegment(*iter);
649
650                CDirectoryListing subListing;
651                if (m_pState->m_pEngine->CacheLookup(childPath, subListing) == FZ_REPLY_OK) {
652                        wxTreeItemId childItem = AppendItem(parent, *iter, 0, 2, 0);
653                        if (childItem) {
654                                SetItemImages(childItem, false);
655
656                                if (HasSubdirs(subListing, filter)) {
657                                        AppendItem(childItem, _T(""), -1, -1);
658                                }
659                        }
660                }
661                else {
662                        wxTreeItemId childItem = AppendItem(parent, *iter, 1, 3, 0);
663                        if (childItem) {
664                                SetItemImages(childItem, true);
665                        }
666                }
667
668                ++iter;
669                inserted = true;
670        }
671
672        if (inserted)
673                SortChildren(parent);
674}
675
676void CRemoteTreeView::OnItemExpanding(wxTreeEvent& event)
677{
678        if (m_busy)
679                return;
680
681        wxTreeItemId item = event.GetItem();
682        if (!item)
683                return;
684
685        if (!ListExpand(item))
686        {
687                event.Veto();
688                return;
689        }
690
691        Refresh(false);
692}
693
694void CRemoteTreeView::SetItemImages(wxTreeItemId item, bool unknown)
695{
696        const int old_image = GetItemImage(item, wxTreeItemIcon_Normal);
697        if (!unknown)
698        {
699                if (old_image == 0)
700                        return;
701                SetItemImage(item, 0, wxTreeItemIcon_Normal);
702                SetItemImage(item, 2, wxTreeItemIcon_Selected);
703                SetItemImage(item, 0, wxTreeItemIcon_Expanded);
704                SetItemImage(item, 2, wxTreeItemIcon_SelectedExpanded);
705        }
706        else
707        {
708                if (old_image == 1)
709                        return;
710                SetItemImage(item, 1, wxTreeItemIcon_Normal);
711                SetItemImage(item, 3, wxTreeItemIcon_Selected);
712                SetItemImage(item, 1, wxTreeItemIcon_Expanded);
713                SetItemImage(item, 3, wxTreeItemIcon_SelectedExpanded);
714        }
715}
716
717void CRemoteTreeView::OnSelectionChanged(wxTreeEvent& event)
718{
719        if (event.GetItem() != m_ExpandAfterList)
720                m_ExpandAfterList = wxTreeItemId();
721        if (m_busy)
722                return;
723
724        if (!m_pState->IsRemoteIdle(true)) {
725                wxBell();
726                return;
727        }
728
729        wxTreeItemId item = event.GetItem();
730        if (!item)
731                return;
732
733        const CServerPath path = GetPathFromItem(item);
734        wxASSERT(!path.empty());
735        if (path.empty())
736                return;
737
738        m_pState->ChangeRemoteDir(path);
739}
740
741void CRemoteTreeView::OnItemActivated(wxTreeEvent& event)
742{
743        m_ExpandAfterList = GetSelection();
744        event.Skip();
745}
746
747CServerPath CRemoteTreeView::GetPathFromItem(const wxTreeItemId& item) const
748{
749        std::list<wxString> segments;
750
751        wxTreeItemId i = item;
752        while (i != GetRootItem())
753        {
754                const CItemData* const pData = (const CItemData*)GetItemData(i);
755                if (pData)
756                {
757                        CServerPath path = pData->m_path;
758                        for (std::list<wxString>::const_iterator iter = segments.begin(); iter != segments.end(); ++iter)
759                        {
760                                if (!path.AddSegment(*iter))
761                                        return CServerPath();
762                        }
763                        return path;
764                }
765
766                segments.push_front(GetItemText(i));
767                i = GetItemParent(i);
768        }
769
770        return CServerPath();
771}
772
773void CRemoteTreeView::OnBeginDrag(wxTreeEvent& event)
774{
775        // Drag could result in recursive operation, don't allow at this point
776        if (!m_pState->IsRemoteIdle()) {
777                wxBell();
778                return;
779        }
780
781        const wxTreeItemId& item = event.GetItem();
782        if (!item)
783                return;
784
785        CServerPath path = GetPathFromItem(item);
786        if (path.empty() || !path.HasParent())
787                return;
788
789        const CServerPath& parent = path.GetParent();
790        const wxString& lastSegment = path.GetLastSegment();
791        if (lastSegment.empty())
792                return;
793
794        wxDataObjectComposite object;
795
796        CServer const* pServer = m_pState->GetServer();
797        if (!pServer)
798                return;
799        CServer const server = *pServer;
800
801
802        CRemoteDataObject *pRemoteDataObject = new CRemoteDataObject(*pServer, parent);
803        pRemoteDataObject->AddFile(lastSegment, true, -1, false);
804
805        pRemoteDataObject->Finalize();
806
807        object.Add(pRemoteDataObject, true);
808
809#if FZ3_USESHELLEXT
810        std::unique_ptr<CShellExtensionInterface> ext = CShellExtensionInterface::CreateInitialized();
811        if (ext) {
812                const wxString& file = ext->GetDragDirectory();
813
814                wxASSERT(!file.empty());
815
816                wxFileDataObject *pFileDataObject = new wxFileDataObject;
817                pFileDataObject->AddFile(file);
818
819                object.Add(pFileDataObject);
820        }
821#endif
822
823        CDragDropManager* pDragDropManager = CDragDropManager::Init();
824        pDragDropManager->pDragSource = this;
825        pDragDropManager->server = *pServer;
826        pDragDropManager->remoteParent = parent;
827
828        wxDropSource source(this);
829        source.SetData(object);
830
831        int res = source.DoDragDrop();
832
833        pDragDropManager->Release();
834
835        if (res != wxDragCopy) {
836                return;
837        }
838
839#if FZ3_USESHELLEXT
840        if (ext) {
841                if (!pRemoteDataObject->DidSendData()) {
842                        pServer = m_pState->GetServer();
843                        if (!pServer || !m_pState->IsRemoteIdle() || *pServer != server) {
844                                wxBell();
845                                return;
846                        }
847
848                        CLocalPath target(ext->GetTarget());
849                        if (target.empty()) {
850                                ext.reset(); // Release extension before the modal message box
851                                wxMessageBoxEx(_("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."));
852                                return;
853                        }
854
855                        m_pState->DownloadDroppedFiles(pRemoteDataObject, target);
856                }
857        }
858#endif
859}
860
861void CRemoteTreeView::OnContextMenu(wxTreeEvent& event)
862{
863        m_contextMenuItem = event.GetItem();
864        wxMenu* pMenu = wxXmlResource::Get()->LoadMenu(_T("ID_MENU_REMOTETREE"));
865        if (!pMenu)
866                return;
867
868        const CServerPath& path = m_contextMenuItem ? GetPathFromItem(m_contextMenuItem) : CServerPath();
869        if (!m_pState->IsRemoteIdle() || path.empty()) {
870                pMenu->Enable(XRCID("ID_DOWNLOAD"), false);
871                pMenu->Enable(XRCID("ID_ADDTOQUEUE"), false);
872                pMenu->Enable(XRCID("ID_MKDIR"), false);
873                pMenu->Enable(XRCID("ID_MKDIR_CHGDIR"), false);
874                pMenu->Enable(XRCID("ID_DELETE"), false);
875                pMenu->Enable(XRCID("ID_CHMOD"), false);
876                pMenu->Enable(XRCID("ID_RENAME"), false);
877                pMenu->Enable(XRCID("ID_GETURL"), false);
878        }
879        else if (!path.HasParent())
880                pMenu->Enable(XRCID("ID_RENAME"), false);
881
882        if (!m_pState->GetLocalDir().IsWriteable()) {
883                pMenu->Enable(XRCID("ID_DOWNLOAD"), false);
884                pMenu->Enable(XRCID("ID_ADDTOQUEUE"), false);
885        }
886
887        PopupMenu(pMenu);
888        delete pMenu;
889}
890
891void CRemoteTreeView::OnMenuChmod(wxCommandEvent&)
892{
893        if (!m_pState->IsRemoteIdle())
894                return;
895
896        if (!m_contextMenuItem)
897                return;
898
899        const CServerPath& path = GetPathFromItem(m_contextMenuItem);
900        if (path.empty())
901                return;
902
903        const bool hasParent = path.HasParent();
904
905        CChmodDialog* pChmodDlg = new CChmodDialog;
906
907        // Get current permissions of directory
908        const wxString& name = GetItemText(m_contextMenuItem);
909        char permissions[9] = {0};
910        bool cached = false;
911
912        // Obviously item needs to have a parent directory...
913        if (hasParent) {
914                const CServerPath& parentPath = path.GetParent();
915                CDirectoryListing listing;
916
917                // ... and it needs to be cached
918                cached = m_pState->m_pEngine->CacheLookup(parentPath, listing) == FZ_REPLY_OK;
919                if (cached) {
920                        for (unsigned int i = 0; i < listing.GetCount(); ++i) {
921                                if (listing[i].name != name)
922                                        continue;
923
924                                pChmodDlg->ConvertPermissions(*listing[i].permissions, permissions);
925                        }
926                }
927        }
928
929        if (!pChmodDlg->Create(this, 0, 1, name, permissions)) {
930                pChmodDlg->Destroy();
931                pChmodDlg = 0;
932                return;
933        }
934
935        if (pChmodDlg->ShowModal() != wxID_OK) {
936                pChmodDlg->Destroy();
937                pChmodDlg = 0;
938                return;
939        }
940
941        // State may have changed while chmod dialog was shown
942        if (!m_contextMenuItem || !m_pState->IsRemoteConnected() || !m_pState->IsRemoteIdle()) {
943                pChmodDlg->Destroy();
944                pChmodDlg = 0;
945                return;
946        }
947
948        const int applyType = pChmodDlg->GetApplyType();
949
950        CRecursiveOperation* pRecursiveOperation = m_pState->GetRecursiveOperationHandler();
951
952        if (cached) { // Implies hasParent
953                // Change directory permissions
954                if (!applyType || applyType == 2) {
955                        wxString newPerms = pChmodDlg->GetPermissions(permissions, true);
956
957                        m_pState->m_pCommandQueue->ProcessCommand(new CChmodCommand(path.GetParent(), name, newPerms));
958                }
959
960                if (pChmodDlg->Recursive())
961                        // Start recursion
962                        pRecursiveOperation->AddDirectoryToVisit(path, _T(""), CLocalPath());
963        }
964        else {
965                if (hasParent)
966                        pRecursiveOperation->AddDirectoryToVisitRestricted(path.GetParent(), name, pChmodDlg->Recursive());
967                else
968                        pRecursiveOperation->AddDirectoryToVisitRestricted(path, _T(""), pChmodDlg->Recursive());
969        }
970
971        if (!cached || pChmodDlg->Recursive()) {
972                pRecursiveOperation->SetChmodDialog(pChmodDlg);
973
974                CServerPath currentPath;
975                const wxTreeItemId selected = GetSelection();
976                if (selected)
977                        currentPath = GetPathFromItem(selected);
978                CFilterManager filter;
979                pRecursiveOperation->StartRecursiveOperation(CRecursiveOperation::recursive_chmod, hasParent ? path.GetParent() : path, filter.GetActiveFilters(false), !cached, currentPath);
980        }
981        else {
982                pChmodDlg->Destroy();
983                const wxTreeItemId selected = GetSelection();
984                if (selected) {
985                        CServerPath currentPath = GetPathFromItem(selected);
986                        m_pState->ChangeRemoteDir(currentPath);
987                }
988        }
989}
990
991void CRemoteTreeView::OnMenuDownload(wxCommandEvent& event)
992{
993        CLocalPath localDir = m_pState->GetLocalDir();
994        if (!localDir.IsWriteable()) {
995                wxBell();
996                return;
997        }
998
999        if (!m_pState->IsRemoteIdle())
1000                return;
1001
1002        if (!m_contextMenuItem)
1003                return;
1004
1005        const CServerPath& path = GetPathFromItem(m_contextMenuItem);
1006        if (path.empty())
1007                return;
1008
1009        const wxString& name = GetItemText(m_contextMenuItem);
1010
1011        localDir.AddSegment(CQueueView::ReplaceInvalidCharacters(name));
1012
1013        CRecursiveOperation* pRecursiveOperation = m_pState->GetRecursiveOperationHandler();
1014        pRecursiveOperation->AddDirectoryToVisit(path, _T(""), localDir);
1015
1016        CServerPath currentPath;
1017        const wxTreeItemId selected = GetSelection();
1018        if (selected)
1019                currentPath = GetPathFromItem(selected);
1020
1021        const bool addOnly = event.GetId() == XRCID("ID_ADDTOQUEUE");
1022        CFilterManager filter;
1023        pRecursiveOperation->StartRecursiveOperation(addOnly ? CRecursiveOperation::recursive_addtoqueue : CRecursiveOperation::recursive_download, path, filter.GetActiveFilters(false), true, currentPath);
1024}
1025
1026void CRemoteTreeView::OnMenuDelete(wxCommandEvent&)
1027{
1028        if (!m_pState->IsRemoteIdle())
1029                return;
1030
1031        if (!m_contextMenuItem)
1032                return;
1033
1034        CServerPath const& pathToDelete = GetPathFromItem(m_contextMenuItem);
1035        if (pathToDelete.empty())
1036                return;
1037
1038        if (wxMessageBoxEx(_("Really delete all selected files and/or directories from the server?"), _("Confirmation needed"), wxICON_QUESTION | wxYES_NO, this) != wxYES)
1039                return;
1040
1041        const bool hasParent = pathToDelete.HasParent();
1042
1043        CRecursiveOperation* pRecursiveOperation = m_pState->GetRecursiveOperationHandler();
1044
1045        CServerPath startDir;
1046        if (hasParent) {
1047                const wxString& name = GetItemText(m_contextMenuItem);
1048                startDir = pathToDelete.GetParent();
1049                pRecursiveOperation->AddDirectoryToVisit(startDir, name);
1050        }
1051        else {
1052                startDir = pathToDelete;
1053                pRecursiveOperation->AddDirectoryToVisit(startDir, _T(""));
1054        }
1055
1056        CServerPath currentPath;
1057        const wxTreeItemId selected = GetSelection();
1058        if (selected)
1059                currentPath = GetPathFromItem(selected);
1060        if (!currentPath.empty() && (pathToDelete == currentPath || pathToDelete.IsParentOf(currentPath, false))) {
1061                currentPath = startDir;
1062                m_pState->ChangeRemoteDir(startDir);
1063        }
1064
1065        CFilterManager filter;
1066        pRecursiveOperation->StartRecursiveOperation(CRecursiveOperation::recursive_delete, startDir, filter.GetActiveFilters(false), !hasParent, currentPath);
1067}
1068
1069void CRemoteTreeView::OnMenuRename(wxCommandEvent&)
1070{
1071        if (!m_pState->IsRemoteIdle())
1072                return;
1073
1074        if (!m_contextMenuItem)
1075                return;
1076
1077        const CServerPath& path = GetPathFromItem(m_contextMenuItem);
1078        if (path.empty())
1079                return;
1080
1081        if (!path.HasParent())
1082                return;
1083
1084        EditLabel(m_contextMenuItem);
1085}
1086
1087void CRemoteTreeView::OnBeginLabelEdit(wxTreeEvent& event)
1088{
1089        if (!m_pState->IsRemoteIdle()) {
1090                event.Veto();
1091                return;
1092        }
1093
1094        const CServerPath& path = GetPathFromItem(event.GetItem());
1095        if (path.empty()) {
1096                event.Veto();
1097                return;
1098        }
1099
1100        if (!path.HasParent()) {
1101                event.Veto();
1102                return;
1103        }
1104}
1105
1106void CRemoteTreeView::OnEndLabelEdit(wxTreeEvent& event)
1107{
1108        if (event.IsEditCancelled()) {
1109                event.Veto();
1110                return;
1111        }
1112
1113        if (!m_pState->IsRemoteIdle()) {
1114                event.Veto();
1115                return;
1116        }
1117
1118        CItemData* const pData = (CItemData*)GetItemData(event.GetItem());
1119        if (pData) {
1120                event.Veto();
1121                return;
1122        }
1123
1124        CServerPath old_path = GetPathFromItem(event.GetItem());
1125        CServerPath parent = old_path.GetParent();
1126
1127        const wxString& oldName = GetItemText(event.GetItem());
1128        const wxString& newName = event.GetLabel();
1129        if (oldName == newName) {
1130                event.Veto();
1131                return;
1132        }
1133
1134        m_pState->m_pCommandQueue->ProcessCommand(new CRenameCommand(parent, oldName, parent, newName));
1135        m_pState->ChangeRemoteDir(parent);
1136
1137        CServerPath currentPath;
1138        const wxTreeItemId selected = GetSelection();
1139        if (selected)
1140                currentPath = GetPathFromItem(selected);
1141        if (currentPath.empty())
1142                return;
1143
1144        if (currentPath == old_path || currentPath.IsSubdirOf(old_path, false)) {
1145                // Previously selected path was below renamed one, list the new one
1146
1147                std::list<wxString> subdirs;
1148                while (currentPath != old_path) {
1149                        if (!currentPath.HasParent()) {
1150                                // Abort just in case
1151                                return;
1152                        }
1153                        subdirs.push_front(currentPath.GetLastSegment());
1154                        currentPath = currentPath.GetParent();
1155                }
1156                currentPath = parent;
1157                currentPath.AddSegment(newName);
1158                for (std::list<wxString>::const_iterator iter = subdirs.begin(); iter != subdirs.end(); ++iter)
1159                        currentPath.AddSegment(*iter);
1160                m_pState->ChangeRemoteDir(currentPath);
1161        }
1162        else if (currentPath != parent)
1163                m_pState->ChangeRemoteDir(currentPath);
1164}
1165
1166
1167// Create a new Directory
1168void CRemoteTreeView::OnMkdir(wxCommandEvent&)
1169{
1170        CServerPath newpath = MenuMkdir();
1171
1172        CServerPath listed;
1173        if (newpath.HasParent())
1174        {
1175                listed = newpath.GetParent();
1176                m_pState->ChangeRemoteDir(listed);
1177        }
1178
1179        CServerPath currentPath;
1180        const wxTreeItemId selected = GetSelection();
1181        if (selected)
1182                currentPath = GetPathFromItem(selected);
1183        if (!currentPath.empty() && currentPath != listed)
1184                m_pState->ChangeRemoteDir(currentPath);
1185
1186}
1187
1188// Create a new Directory and enter the new Directory
1189void CRemoteTreeView::OnMenuMkdirChgDir(wxCommandEvent&)
1190{
1191        CServerPath newpath = MenuMkdir();
1192        if (!newpath.empty()) {
1193                m_pState->ChangeRemoteDir(newpath);
1194        }
1195}
1196
1197// Help-Function to create a new Directory
1198// Returns the name of the new directory
1199CServerPath CRemoteTreeView::MenuMkdir()
1200{
1201        if (!m_pState->IsRemoteIdle())
1202                return CServerPath();
1203
1204        if (!m_contextMenuItem)
1205                return CServerPath();
1206
1207        const CServerPath& path = GetPathFromItem(m_contextMenuItem);
1208        if (path.empty())
1209                return CServerPath();
1210
1211        CInputDialog dlg;
1212        if (!dlg.Create(this, _("Create directory"), _("Please enter the name of the directory which should be created:")))
1213                return CServerPath();
1214
1215        CServerPath newPath = path;
1216
1217        // Append a long segment which does (most likely) not exist in the path and
1218        // replace it with "New directory" later. This way we get the exact position of
1219        // "New directory" and can preselect it in the dialog.
1220        wxString tmpName = _T("25CF809E56B343b5A12D1F0466E3B37A49A9087FDCF8412AA9AF8D1E849D01CF");
1221        if (newPath.AddSegment(tmpName)) {
1222                wxString pathName = newPath.GetPath();
1223                int pos = pathName.Find(tmpName);
1224                wxASSERT(pos != -1);
1225                wxString newName = _("New directory");
1226                pathName.Replace(tmpName, newName);
1227                dlg.SetValue(pathName);
1228                dlg.SelectText(pos, pos + newName.Length());
1229        }
1230
1231        if (dlg.ShowModal() != wxID_OK)
1232                return CServerPath();
1233
1234        newPath = path;
1235        if (!newPath.ChangePath(dlg.GetValue())) {
1236                wxBell();
1237                return CServerPath();
1238        }
1239
1240        m_pState->m_pCommandQueue->ProcessCommand(new CMkdirCommand(newPath));
1241
1242        return newPath;
1243}
1244
1245bool CRemoteTreeView::ListExpand(wxTreeItemId item)
1246{
1247        const CServerPath path = GetPathFromItem(item);
1248        wxASSERT(!path.empty());
1249        if (path.empty())
1250                return false;
1251
1252        CDirectoryListing listing;
1253        if (m_pState->m_pEngine->CacheLookup(path, listing) == FZ_REPLY_OK)
1254                RefreshItem(item, listing, false);
1255        else
1256        {
1257                SetItemImages(item, true);
1258
1259                wxTreeItemId child = GetLastChild(item);
1260                if (!child || GetItemText(child).empty())
1261                        return false;
1262        }
1263
1264        return true;
1265}
1266
1267void CRemoteTreeView::OnChar(wxKeyEvent& event)
1268{
1269        m_contextMenuItem = GetSelection();
1270
1271        wxCommandEvent cmdEvt;
1272        if (event.GetKeyCode() == WXK_F2)
1273                OnMenuRename(cmdEvt);
1274        else if (event.GetKeyCode() == WXK_DELETE || event.GetKeyCode() == WXK_NUMPAD_DELETE)
1275                OnMenuDelete(cmdEvt);
1276        else
1277                event.Skip();
1278}
1279
1280struct _parents
1281{
1282        wxTreeItemId item;
1283        CServerPath path;
1284};
1285
1286void CRemoteTreeView::ApplyFilters(bool resort)
1287{
1288        std::list<struct _parents> parents;
1289
1290        const wxTreeItemId root = GetRootItem();
1291        wxTreeItemIdValue cookie;
1292        for (wxTreeItemId child = GetFirstChild(root, cookie); child; child = GetNextSibling(child)) {
1293                CServerPath path = GetPathFromItem(child);
1294                if (path.empty())
1295                        continue;
1296
1297                struct _parents dir;
1298                dir.item = child;
1299                dir.path = path;
1300                parents.push_back(dir);
1301        }
1302
1303        CFilterManager filter;
1304        while (!parents.empty()) {
1305                struct _parents parent = parents.back();
1306                parents.pop_back();
1307
1308                if (resort) {
1309                        SortChildren(parent.item);
1310                }
1311
1312                CDirectoryListing listing;
1313                if (m_pState->m_pEngine->CacheLookup(parent.path, listing) == FZ_REPLY_OK)
1314                        RefreshItem(parent.item, listing, false);
1315                else if (filter.HasActiveFilters()) {
1316                        for (wxTreeItemId child = GetFirstChild(parent.item, cookie); child; child = GetNextSibling(child)) {
1317                                CServerPath path = GetPathFromItem(child);
1318                                if (path.empty())
1319                                        continue;
1320
1321                                if (filter.FilenameFiltered(GetItemText(child), path.GetPath(), true, -1, false, 0, CDateTime())) {
1322                                        wxTreeItemId sel = GetSelection();
1323                                        while (sel && sel != child)
1324                                                sel = GetItemParent(sel);
1325                                        if (!sel) {
1326                                                Delete(child);
1327                                                continue;
1328                                        }
1329                                }
1330
1331                                struct _parents dir;
1332                                dir.item = child;
1333                                dir.path = path;
1334                                parents.push_back(dir);
1335                        }
1336
1337                        // The stuff below has already been done above in this one case
1338                        continue;
1339                }
1340                for (wxTreeItemId child = GetFirstChild(parent.item, cookie); child; child = GetNextSibling(child)) {
1341                        CServerPath path = GetPathFromItem(child);
1342                        if (path.empty())
1343                                continue;
1344
1345                        struct _parents dir;
1346                        dir.item = child;
1347                        dir.path = path;
1348                        parents.push_back(dir);
1349                }
1350        }
1351}
1352
1353void CRemoteTreeView::OnMenuGeturl(wxCommandEvent&)
1354{
1355        if (!m_contextMenuItem)
1356                return;
1357
1358        const CServerPath& path = GetPathFromItem(m_contextMenuItem);
1359        if (path.empty())
1360        {
1361                wxBell();
1362                return;
1363        }
1364
1365        const CServer *pServer = m_pState->GetServer();
1366        if (!pServer)
1367        {
1368                wxBell();
1369                return;
1370        }
1371
1372        if (!wxTheClipboard->Open())
1373        {
1374                wxMessageBoxEx(_("Could not open clipboard"), _("Could not copy URLs"), wxICON_EXCLAMATION);
1375                return;
1376        }
1377
1378        wxString url = pServer->FormatServer(true);
1379        url += path.GetPath();
1380
1381        // Poor mans URLencode
1382        url.Replace(_T(" "), _T("%20"));
1383
1384        wxTheClipboard->SetData(new wxURLDataObject(url));
1385
1386        wxTheClipboard->Flush();
1387        wxTheClipboard->Close();
1388}
1389
1390void CRemoteTreeView::UpdateSortMode()
1391{
1392        switch (COptions::Get()->GetOptionVal(OPTION_FILELIST_NAMESORT))
1393        {
1394        case 0:
1395        default:
1396                m_nameSortMode = CFileListCtrlSortBase::namesort_caseinsensitive;
1397                break;
1398        case 1:
1399                m_nameSortMode = CFileListCtrlSortBase::namesort_casesensitive;
1400                break;
1401        case 2:
1402                m_nameSortMode = CFileListCtrlSortBase::namesort_natural;
1403                break;
1404        }
1405}
1406
1407void CRemoteTreeView::OnOptionsChanged(changed_options_t const& options)
1408{
1409        if (options.test(OPTION_FILELIST_NAMESORT)) {
1410                UpdateSortMode();
1411                ApplyFilters(true);
1412        }
1413}
Note: See TracBrowser for help on using the repository browser.