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

Last change on this file since 3185 was 3185, checked in by jrpelegrina, 2 years ago

Update new version: 3.15.02

File size: 35.6 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        recursion_root root(hasParent ? path.GetParent() : path, !cached);
951        if (cached) { // Implies hasParent
952                // Change directory permissions
953                if (!applyType || applyType == 2) {
954                        wxString newPerms = pChmodDlg->GetPermissions(permissions, true);
955
956                        m_pState->m_pCommandQueue->ProcessCommand(new CChmodCommand(path.GetParent(), name, newPerms));
957                }
958
959                if (pChmodDlg->Recursive())
960                        // Start recursion
961                        root.add_dir_to_visit(path, _T(""), CLocalPath());
962        }
963        else {
964                if (hasParent)
965                        root.add_dir_to_visit_restricted(path.GetParent(), name, pChmodDlg->Recursive());
966                else
967                        root.add_dir_to_visit_restricted(path, _T(""), pChmodDlg->Recursive());
968        }
969
970        if (!cached || pChmodDlg->Recursive()) {
971                CRecursiveOperation* pRecursiveOperation = m_pState->GetRecursiveOperationHandler();
972                pRecursiveOperation->AddRecursionRoot(std::move(root));
973                pRecursiveOperation->SetChmodDialog(pChmodDlg);
974
975                CServerPath currentPath;
976                const wxTreeItemId selected = GetSelection();
977                if (selected)
978                        currentPath = GetPathFromItem(selected);
979                CFilterManager filter;
980                pRecursiveOperation->StartRecursiveOperation(CRecursiveOperation::recursive_chmod, filter.GetActiveFilters(false), currentPath);
981        }
982        else {
983                pChmodDlg->Destroy();
984                const wxTreeItemId selected = GetSelection();
985                if (selected) {
986                        CServerPath currentPath = GetPathFromItem(selected);
987                        m_pState->ChangeRemoteDir(currentPath);
988                }
989        }
990}
991
992void CRemoteTreeView::OnMenuDownload(wxCommandEvent& event)
993{
994        CLocalPath localDir = m_pState->GetLocalDir();
995        if (!localDir.IsWriteable()) {
996                wxBell();
997                return;
998        }
999
1000        if (!m_pState->IsRemoteIdle())
1001                return;
1002
1003        if (!m_contextMenuItem)
1004                return;
1005
1006        const CServerPath& path = GetPathFromItem(m_contextMenuItem);
1007        if (path.empty())
1008                return;
1009
1010        const wxString& name = GetItemText(m_contextMenuItem);
1011
1012        localDir.AddSegment(CQueueView::ReplaceInvalidCharacters(name));
1013
1014        recursion_root root(path, true);
1015        root.add_dir_to_visit(path, _T(""), localDir);
1016        CRecursiveOperation* pRecursiveOperation = m_pState->GetRecursiveOperationHandler();
1017        pRecursiveOperation->AddRecursionRoot(std::move(root));
1018
1019        CServerPath currentPath;
1020        const wxTreeItemId selected = GetSelection();
1021        if (selected)
1022                currentPath = GetPathFromItem(selected);
1023
1024        const bool addOnly = event.GetId() == XRCID("ID_ADDTOQUEUE");
1025        CFilterManager filter;
1026        pRecursiveOperation->StartRecursiveOperation(addOnly ? CRecursiveOperation::recursive_addtoqueue : CRecursiveOperation::recursive_download, filter.GetActiveFilters(false), currentPath);
1027}
1028
1029void CRemoteTreeView::OnMenuDelete(wxCommandEvent&)
1030{
1031        if (!m_pState->IsRemoteIdle())
1032                return;
1033
1034        if (!m_contextMenuItem)
1035                return;
1036
1037        CServerPath const& pathToDelete = GetPathFromItem(m_contextMenuItem);
1038        if (pathToDelete.empty())
1039                return;
1040
1041        if (wxMessageBoxEx(_("Really delete all selected files and/or directories from the server?"), _("Confirmation needed"), wxICON_QUESTION | wxYES_NO, this) != wxYES)
1042                return;
1043
1044        const bool hasParent = pathToDelete.HasParent();
1045
1046        CRecursiveOperation* pRecursiveOperation = m_pState->GetRecursiveOperationHandler();
1047
1048        recursion_root root;
1049        CServerPath startDir;
1050        if (hasParent) {
1051                const wxString& name = GetItemText(m_contextMenuItem);
1052                startDir = pathToDelete.GetParent();
1053                root = recursion_root(startDir, !hasParent);
1054                root.add_dir_to_visit(startDir, name);
1055        }
1056        else {
1057                startDir = pathToDelete;
1058                root = recursion_root(startDir, !hasParent);
1059                root.add_dir_to_visit(startDir, _T(""));
1060        }
1061        pRecursiveOperation->AddRecursionRoot(std::move(root));
1062
1063        CServerPath currentPath;
1064        const wxTreeItemId selected = GetSelection();
1065        if (selected)
1066                currentPath = GetPathFromItem(selected);
1067        if (!currentPath.empty() && (pathToDelete == currentPath || pathToDelete.IsParentOf(currentPath, false))) {
1068                currentPath = startDir;
1069                m_pState->ChangeRemoteDir(startDir);
1070        }
1071
1072        CFilterManager filter;
1073        pRecursiveOperation->StartRecursiveOperation(CRecursiveOperation::recursive_delete, filter.GetActiveFilters(false), currentPath);
1074}
1075
1076void CRemoteTreeView::OnMenuRename(wxCommandEvent&)
1077{
1078        if (!m_pState->IsRemoteIdle())
1079                return;
1080
1081        if (!m_contextMenuItem)
1082                return;
1083
1084        const CServerPath& path = GetPathFromItem(m_contextMenuItem);
1085        if (path.empty())
1086                return;
1087
1088        if (!path.HasParent())
1089                return;
1090
1091        EditLabel(m_contextMenuItem);
1092}
1093
1094void CRemoteTreeView::OnBeginLabelEdit(wxTreeEvent& event)
1095{
1096        if (!m_pState->IsRemoteIdle()) {
1097                event.Veto();
1098                return;
1099        }
1100
1101        const CServerPath& path = GetPathFromItem(event.GetItem());
1102        if (path.empty()) {
1103                event.Veto();
1104                return;
1105        }
1106
1107        if (!path.HasParent()) {
1108                event.Veto();
1109                return;
1110        }
1111}
1112
1113void CRemoteTreeView::OnEndLabelEdit(wxTreeEvent& event)
1114{
1115        if (event.IsEditCancelled()) {
1116                event.Veto();
1117                return;
1118        }
1119
1120        if (!m_pState->IsRemoteIdle()) {
1121                event.Veto();
1122                return;
1123        }
1124
1125        CItemData* const pData = (CItemData*)GetItemData(event.GetItem());
1126        if (pData) {
1127                event.Veto();
1128                return;
1129        }
1130
1131        CServerPath old_path = GetPathFromItem(event.GetItem());
1132        CServerPath parent = old_path.GetParent();
1133
1134        const wxString& oldName = GetItemText(event.GetItem());
1135        const wxString& newName = event.GetLabel();
1136        if (oldName == newName) {
1137                event.Veto();
1138                return;
1139        }
1140
1141        m_pState->m_pCommandQueue->ProcessCommand(new CRenameCommand(parent, oldName, parent, newName));
1142        m_pState->ChangeRemoteDir(parent);
1143
1144        CServerPath currentPath;
1145        const wxTreeItemId selected = GetSelection();
1146        if (selected)
1147                currentPath = GetPathFromItem(selected);
1148        if (currentPath.empty())
1149                return;
1150
1151        if (currentPath == old_path || currentPath.IsSubdirOf(old_path, false)) {
1152                // Previously selected path was below renamed one, list the new one
1153
1154                std::list<wxString> subdirs;
1155                while (currentPath != old_path) {
1156                        if (!currentPath.HasParent()) {
1157                                // Abort just in case
1158                                return;
1159                        }
1160                        subdirs.push_front(currentPath.GetLastSegment());
1161                        currentPath = currentPath.GetParent();
1162                }
1163                currentPath = parent;
1164                currentPath.AddSegment(newName);
1165                for (std::list<wxString>::const_iterator iter = subdirs.begin(); iter != subdirs.end(); ++iter)
1166                        currentPath.AddSegment(*iter);
1167                m_pState->ChangeRemoteDir(currentPath);
1168        }
1169        else if (currentPath != parent)
1170                m_pState->ChangeRemoteDir(currentPath);
1171}
1172
1173
1174// Create a new Directory
1175void CRemoteTreeView::OnMkdir(wxCommandEvent&)
1176{
1177        CServerPath newpath = MenuMkdir();
1178
1179        CServerPath listed;
1180        if (newpath.HasParent())
1181        {
1182                listed = newpath.GetParent();
1183                m_pState->ChangeRemoteDir(listed);
1184        }
1185
1186        CServerPath currentPath;
1187        const wxTreeItemId selected = GetSelection();
1188        if (selected)
1189                currentPath = GetPathFromItem(selected);
1190        if (!currentPath.empty() && currentPath != listed)
1191                m_pState->ChangeRemoteDir(currentPath);
1192
1193}
1194
1195// Create a new Directory and enter the new Directory
1196void CRemoteTreeView::OnMenuMkdirChgDir(wxCommandEvent&)
1197{
1198        CServerPath newpath = MenuMkdir();
1199        if (!newpath.empty()) {
1200                m_pState->ChangeRemoteDir(newpath);
1201        }
1202}
1203
1204// Help-Function to create a new Directory
1205// Returns the name of the new directory
1206CServerPath CRemoteTreeView::MenuMkdir()
1207{
1208        if (!m_pState->IsRemoteIdle())
1209                return CServerPath();
1210
1211        if (!m_contextMenuItem)
1212                return CServerPath();
1213
1214        const CServerPath& path = GetPathFromItem(m_contextMenuItem);
1215        if (path.empty())
1216                return CServerPath();
1217
1218        CInputDialog dlg;
1219        if (!dlg.Create(this, _("Create directory"), _("Please enter the name of the directory which should be created:")))
1220                return CServerPath();
1221
1222        CServerPath newPath = path;
1223
1224        // Append a long segment which does (most likely) not exist in the path and
1225        // replace it with "New directory" later. This way we get the exact position of
1226        // "New directory" and can preselect it in the dialog.
1227        wxString tmpName = _T("25CF809E56B343b5A12D1F0466E3B37A49A9087FDCF8412AA9AF8D1E849D01CF");
1228        if (newPath.AddSegment(tmpName)) {
1229                wxString pathName = newPath.GetPath();
1230                int pos = pathName.Find(tmpName);
1231                wxASSERT(pos != -1);
1232                wxString newName = _("New directory");
1233                pathName.Replace(tmpName, newName);
1234                dlg.SetValue(pathName);
1235                dlg.SelectText(pos, pos + newName.Length());
1236        }
1237
1238        if (dlg.ShowModal() != wxID_OK)
1239                return CServerPath();
1240
1241        newPath = path;
1242        if (!newPath.ChangePath(dlg.GetValue())) {
1243                wxBell();
1244                return CServerPath();
1245        }
1246
1247        m_pState->m_pCommandQueue->ProcessCommand(new CMkdirCommand(newPath));
1248
1249        return newPath;
1250}
1251
1252bool CRemoteTreeView::ListExpand(wxTreeItemId item)
1253{
1254        const CServerPath path = GetPathFromItem(item);
1255        wxASSERT(!path.empty());
1256        if (path.empty())
1257                return false;
1258
1259        CDirectoryListing listing;
1260        if (m_pState->m_pEngine->CacheLookup(path, listing) == FZ_REPLY_OK)
1261                RefreshItem(item, listing, false);
1262        else
1263        {
1264                SetItemImages(item, true);
1265
1266                wxTreeItemId child = GetLastChild(item);
1267                if (!child || GetItemText(child).empty())
1268                        return false;
1269        }
1270
1271        return true;
1272}
1273
1274void CRemoteTreeView::OnChar(wxKeyEvent& event)
1275{
1276        m_contextMenuItem = GetSelection();
1277
1278        wxCommandEvent cmdEvt;
1279        if (event.GetKeyCode() == WXK_F2)
1280                OnMenuRename(cmdEvt);
1281        else if (event.GetKeyCode() == WXK_DELETE || event.GetKeyCode() == WXK_NUMPAD_DELETE)
1282                OnMenuDelete(cmdEvt);
1283        else
1284                event.Skip();
1285}
1286
1287struct _parents
1288{
1289        wxTreeItemId item;
1290        CServerPath path;
1291};
1292
1293void CRemoteTreeView::ApplyFilters(bool resort)
1294{
1295        std::list<struct _parents> parents;
1296
1297        const wxTreeItemId root = GetRootItem();
1298        wxTreeItemIdValue cookie;
1299        for (wxTreeItemId child = GetFirstChild(root, cookie); child; child = GetNextSibling(child)) {
1300                CServerPath path = GetPathFromItem(child);
1301                if (path.empty())
1302                        continue;
1303
1304                struct _parents dir;
1305                dir.item = child;
1306                dir.path = path;
1307                parents.push_back(dir);
1308        }
1309
1310        CFilterManager filter;
1311        while (!parents.empty()) {
1312                struct _parents parent = parents.back();
1313                parents.pop_back();
1314
1315                if (resort) {
1316                        SortChildren(parent.item);
1317                }
1318
1319                CDirectoryListing listing;
1320                if (m_pState->m_pEngine->CacheLookup(parent.path, listing) == FZ_REPLY_OK)
1321                        RefreshItem(parent.item, listing, false);
1322                else if (filter.HasActiveFilters()) {
1323                        for (wxTreeItemId child = GetFirstChild(parent.item, cookie); child; child = GetNextSibling(child)) {
1324                                CServerPath path = GetPathFromItem(child);
1325                                if (path.empty())
1326                                        continue;
1327
1328                                if (filter.FilenameFiltered(GetItemText(child), path.GetPath(), true, -1, false, 0, fz::datetime())) {
1329                                        wxTreeItemId sel = GetSelection();
1330                                        while (sel && sel != child)
1331                                                sel = GetItemParent(sel);
1332                                        if (!sel) {
1333                                                Delete(child);
1334                                                continue;
1335                                        }
1336                                }
1337
1338                                struct _parents dir;
1339                                dir.item = child;
1340                                dir.path = path;
1341                                parents.push_back(dir);
1342                        }
1343
1344                        // The stuff below has already been done above in this one case
1345                        continue;
1346                }
1347                for (wxTreeItemId child = GetFirstChild(parent.item, cookie); child; child = GetNextSibling(child)) {
1348                        CServerPath path = GetPathFromItem(child);
1349                        if (path.empty())
1350                                continue;
1351
1352                        struct _parents dir;
1353                        dir.item = child;
1354                        dir.path = path;
1355                        parents.push_back(dir);
1356                }
1357        }
1358}
1359
1360void CRemoteTreeView::OnMenuGeturl(wxCommandEvent&)
1361{
1362        if (!m_contextMenuItem)
1363                return;
1364
1365        const CServerPath& path = GetPathFromItem(m_contextMenuItem);
1366        if (path.empty())
1367        {
1368                wxBell();
1369                return;
1370        }
1371
1372        const CServer *pServer = m_pState->GetServer();
1373        if (!pServer)
1374        {
1375                wxBell();
1376                return;
1377        }
1378
1379        if (!wxTheClipboard->Open())
1380        {
1381                wxMessageBoxEx(_("Could not open clipboard"), _("Could not copy URLs"), wxICON_EXCLAMATION);
1382                return;
1383        }
1384
1385        wxString url = pServer->FormatServer(true);
1386        url += path.GetPath();
1387
1388        // Poor mans URLencode
1389        url.Replace(_T(" "), _T("%20"));
1390
1391        wxTheClipboard->SetData(new wxURLDataObject(url));
1392
1393        wxTheClipboard->Flush();
1394        wxTheClipboard->Close();
1395}
1396
1397void CRemoteTreeView::UpdateSortMode()
1398{
1399        switch (COptions::Get()->GetOptionVal(OPTION_FILELIST_NAMESORT))
1400        {
1401        case 0:
1402        default:
1403                m_nameSortMode = CFileListCtrlSortBase::namesort_caseinsensitive;
1404                break;
1405        case 1:
1406                m_nameSortMode = CFileListCtrlSortBase::namesort_casesensitive;
1407                break;
1408        case 2:
1409                m_nameSortMode = CFileListCtrlSortBase::namesort_natural;
1410                break;
1411        }
1412}
1413
1414void CRemoteTreeView::OnOptionsChanged(changed_options_t const& options)
1415{
1416        if (options.test(OPTION_FILELIST_NAMESORT)) {
1417                UpdateSortMode();
1418                ApplyFilters(true);
1419        }
1420}
Note: See TracBrowser for help on using the repository browser.