source: filezilla/trunk/fuentes/src/interface/LocalTreeView.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.7 KB
Line 
1#include <filezilla.h>
2#include "LocalTreeView.h"
3#include "queue.h"
4#include "filezillaapp.h"
5#include "filter.h"
6#include "file_utils.h"
7#include <wx/dnd.h>
8#include "dndobjects.h"
9#include "inputdialog.h"
10#include "dragdropmanager.h"
11#include "drop_target_ex.h"
12#include "Options.h"
13
14#include <libfilezilla/local_filesys.hpp>
15
16#ifdef __WXMSW__
17#include <wx/msw/registry.h>
18#include <shlobj.h>
19#include <dbt.h>
20#include "volume_enumerator.h"
21#endif
22
23#include <algorithm>
24
25class CTreeItemData : public wxTreeItemData
26{
27public:
28        CTreeItemData(const wxString& known_subdir) : m_known_subdir(known_subdir) {}
29        wxString m_known_subdir;
30};
31
32class CLocalTreeViewDropTarget : public CScrollableDropTarget<wxTreeCtrlEx>
33{
34public:
35        CLocalTreeViewDropTarget(CLocalTreeView* pLocalTreeView)
36                : CScrollableDropTarget(pLocalTreeView)
37                , m_pLocalTreeView(pLocalTreeView), m_pFileDataObject(new wxFileDataObject()),
38                m_pRemoteDataObject(new CRemoteDataObject())
39        {
40                m_pDataObject = new wxDataObjectComposite;
41                m_pDataObject->Add(m_pRemoteDataObject, true);
42                m_pDataObject->Add(m_pFileDataObject, false);
43                SetDataObject(m_pDataObject);
44        }
45
46        void ClearDropHighlight()
47        {
48                const wxTreeItemId dropHighlight = m_pLocalTreeView->m_dropHighlight;
49                if (dropHighlight != wxTreeItemId())
50                {
51                        m_pLocalTreeView->SetItemDropHighlight(dropHighlight, false);
52                        m_pLocalTreeView->m_dropHighlight = wxTreeItemId();
53                }
54        }
55
56        wxString GetDirFromItem(const wxTreeItemId& item)
57        {
58                const wxString& dir = m_pLocalTreeView->GetDirFromItem(item);
59
60#ifdef __WXMSW__
61                if (dir == _T("/"))
62                        return wxString();
63#endif
64
65                return dir;
66        }
67
68        wxTreeItemId GetHit(const wxPoint& point)
69        {
70                int flags = 0;
71                wxTreeItemId hit = m_pLocalTreeView->HitTest(point, flags);
72
73                if (flags & (wxTREE_HITTEST_ABOVE | wxTREE_HITTEST_BELOW | wxTREE_HITTEST_NOWHERE | wxTREE_HITTEST_TOLEFT | wxTREE_HITTEST_TORIGHT))
74                        return wxTreeItemId();
75
76                return hit;
77        }
78
79        virtual wxDragResult OnData(wxCoord x, wxCoord y, wxDragResult def)
80        {
81                if (def == wxDragError ||
82                        def == wxDragNone ||
83                        def == wxDragCancel)
84                        return def;
85                if( def == wxDragLink ) {
86                        def = wxDragCopy;
87                }
88
89                wxTreeItemId hit = GetHit(wxPoint(x, y));
90                if (!hit)
91                        return wxDragNone;
92
93                const CLocalPath path(GetDirFromItem(hit));
94                if (path.empty() || !path.IsWriteable())
95                        return wxDragNone;
96
97                if (!GetData())
98                        return wxDragError;
99
100                CDragDropManager* pDragDropManager = CDragDropManager::Get();
101                if (pDragDropManager)
102                        pDragDropManager->pDropTarget = m_pLocalTreeView;
103
104                if (m_pDataObject->GetReceivedFormat() == m_pFileDataObject->GetFormat())
105                        m_pLocalTreeView->m_pState->HandleDroppedFiles(m_pFileDataObject, path, def == wxDragCopy);
106                else
107                {
108                        if (m_pRemoteDataObject->GetProcessId() != (int)wxGetProcessId())
109                        {
110                                wxMessageBoxEx(_("Drag&drop between different instances of FileZilla has not been implemented yet."));
111                                return wxDragNone;
112                        }
113
114                        if (!m_pLocalTreeView->m_pState->GetServer() || !m_pRemoteDataObject->GetServer().EqualsNoPass(*m_pLocalTreeView->m_pState->GetServer()))
115                        {
116                                wxMessageBoxEx(_("Drag&drop between different servers has not been implemented yet."));
117                                return wxDragNone;
118                        }
119
120                        if (!m_pLocalTreeView->m_pState->DownloadDroppedFiles(m_pRemoteDataObject, path))
121                                return wxDragNone;
122                }
123
124                return def;
125        }
126
127        virtual bool OnDrop(wxCoord x, wxCoord y)
128        {
129                if (!CScrollableDropTarget<wxTreeCtrlEx>::OnDrop(x, y)) {
130                        return false;
131                }
132
133                ClearDropHighlight();
134
135                wxTreeItemId hit = GetHit(wxPoint(x, y));
136                if (!hit)
137                        return false;
138
139                const wxString dir = GetDirFromItem(hit);
140                if (dir.empty() || !CLocalPath(dir).IsWriteable())
141                        return false;
142
143                return true;
144        }
145
146        wxTreeItemId DisplayDropHighlight(wxPoint point)
147        {
148                wxTreeItemId hit = GetHit(point);
149                if (!hit) {
150                        ClearDropHighlight();
151                        return hit;
152                }
153
154                wxString dir = GetDirFromItem(hit);
155
156                if (dir.empty()) {
157                        ClearDropHighlight();
158                        return wxTreeItemId();
159                }
160
161                const wxTreeItemId dropHighlight = m_pLocalTreeView->m_dropHighlight;
162                if (dropHighlight != wxTreeItemId())
163                        m_pLocalTreeView->SetItemDropHighlight(dropHighlight, false);
164
165                m_pLocalTreeView->SetItemDropHighlight(hit, true);
166                m_pLocalTreeView->m_dropHighlight = hit;
167
168
169                return hit;
170        }
171
172        virtual wxDragResult OnDragOver(wxCoord x, wxCoord y, wxDragResult def)
173        {
174                def = CScrollableDropTarget<wxTreeCtrlEx>::OnDragOver(x, y, def);
175
176                if (def == wxDragError ||
177                        def == wxDragNone ||
178                        def == wxDragCancel)
179                {
180                        ClearDropHighlight();
181                        return def;
182                }
183
184                wxTreeItemId hit = DisplayDropHighlight(wxPoint(x, y));
185                if (!hit.IsOk())
186                        return wxDragNone;
187
188                if (def == wxDragLink)
189                        def = wxDragCopy;
190
191                return def;
192        }
193
194        virtual void OnLeave()
195        {
196                CScrollableDropTarget<wxTreeCtrlEx>::OnLeave();
197                ClearDropHighlight();
198        }
199
200        virtual wxDragResult OnEnter(wxCoord x, wxCoord y, wxDragResult def)
201        {
202                def = CScrollableDropTarget<wxTreeCtrlEx>::OnEnter(x, y, def);
203                return OnDragOver(x, y, def);
204        }
205
206protected:
207        CLocalTreeView *m_pLocalTreeView;
208        wxFileDataObject* m_pFileDataObject;
209        CRemoteDataObject* m_pRemoteDataObject;
210        wxDataObjectComposite* m_pDataObject;
211};
212
213IMPLEMENT_CLASS(CLocalTreeView, wxTreeCtrlEx)
214
215BEGIN_EVENT_TABLE(CLocalTreeView, wxTreeCtrlEx)
216EVT_TREE_ITEM_EXPANDING(wxID_ANY, CLocalTreeView::OnItemExpanding)
217#ifdef __WXMSW__
218EVT_TREE_SEL_CHANGING(wxID_ANY, CLocalTreeView::OnSelectionChanging)
219#endif
220EVT_TREE_SEL_CHANGED(wxID_ANY, CLocalTreeView::OnSelectionChanged)
221EVT_TREE_BEGIN_DRAG(wxID_ANY, CLocalTreeView::OnBeginDrag)
222#ifdef __WXMSW__
223EVT_COMMAND(-1, fzEVT_VOLUMESENUMERATED, CLocalTreeView::OnVolumesEnumerated)
224EVT_COMMAND(-1, fzEVT_VOLUMEENUMERATED, CLocalTreeView::OnVolumesEnumerated)
225#endif //__WXMSW__
226EVT_TREE_ITEM_MENU(wxID_ANY, CLocalTreeView::OnContextMenu)
227EVT_MENU(XRCID("ID_UPLOAD"), CLocalTreeView::OnMenuUpload)
228EVT_MENU(XRCID("ID_ADDTOQUEUE"), CLocalTreeView::OnMenuUpload)
229EVT_MENU(XRCID("ID_DELETE"), CLocalTreeView::OnMenuDelete)
230EVT_MENU(XRCID("ID_RENAME"), CLocalTreeView::OnMenuRename)
231EVT_MENU(XRCID("ID_MKDIR"), CLocalTreeView::OnMenuMkdir)
232EVT_MENU(XRCID("ID_MKDIR_CHGDIR"), CLocalTreeView::OnMenuMkdirChgDir)
233EVT_TREE_BEGIN_LABEL_EDIT(wxID_ANY, CLocalTreeView::OnBeginLabelEdit)
234EVT_TREE_END_LABEL_EDIT(wxID_ANY, CLocalTreeView::OnEndLabelEdit)
235EVT_CHAR(CLocalTreeView::OnChar)
236EVT_MENU(XRCID("ID_OPEN"), CLocalTreeView::OnMenuOpen)
237END_EVENT_TABLE()
238
239CLocalTreeView::CLocalTreeView(wxWindow* parent, wxWindowID id, CState *pState, CQueueView *pQueueView)
240        : wxTreeCtrlEx(parent, id, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL | wxTR_EDIT_LABELS | wxTR_LINES_AT_ROOT | wxTR_HAS_BUTTONS | wxNO_BORDER),
241        CSystemImageList(16),
242        CStateEventHandler(pState),
243        m_pQueueView(pQueueView)
244{
245        wxGetApp().AddStartupProfileRecord(_T("CLocalTreeView::CLocalTreeView"));
246#ifdef __WXMAC__
247        SetFont(wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT));
248#endif
249
250        pState->RegisterHandler(this, STATECHANGE_LOCAL_DIR);
251        pState->RegisterHandler(this, STATECHANGE_APPLYFILTER);
252
253        SetImageList(GetSystemImageList());
254
255        UpdateSortMode();
256        RegisterOption(OPTION_FILELIST_NAMESORT);
257
258#ifdef __WXMSW__
259        m_pVolumeEnumeratorThread = 0;
260
261        CreateRoot();
262#else
263        wxTreeItemId root = AddRoot(_T("/"));
264        SetItemImage(root, GetIconIndex(iconType::dir), wxTreeItemIcon_Normal);
265        SetItemImage(root, GetIconIndex(iconType::opened_dir), wxTreeItemIcon_Selected);
266        SetItemImage(root, GetIconIndex(iconType::dir), wxTreeItemIcon_Expanded);
267        SetItemImage(root, GetIconIndex(iconType::opened_dir), wxTreeItemIcon_SelectedExpanded);
268
269        SetDir(_T("/"));
270#endif
271
272        SetDropTarget(new CLocalTreeViewDropTarget(this));
273}
274
275CLocalTreeView::~CLocalTreeView()
276{
277#ifdef __WXMSW__
278        delete m_pVolumeEnumeratorThread;
279#endif
280}
281
282void CLocalTreeView::SetDir(wxString localDir)
283{
284        if (m_currentDir == localDir)
285        {
286                RefreshListing();
287                return;
288        }
289
290        if (localDir.Left(2) == _T("\\\\"))
291        {
292                // TODO: UNC path, don't display it yet
293                m_currentDir = _T("");
294                SafeSelectItem(wxTreeItemId());
295                return;
296        }
297        m_currentDir = localDir;
298
299#ifdef __WXMSW__
300        if (localDir == _T("\\"))
301        {
302                SafeSelectItem(m_drives);
303                return;
304        }
305#endif
306
307        wxString subDirs = localDir;
308        wxTreeItemId parent = GetNearestParent(subDirs);
309        if (!parent)
310        {
311                SafeSelectItem(wxTreeItemId());
312                return;
313        }
314
315        if (subDirs.empty())
316        {
317                SafeSelectItem(parent);
318                return;
319        }
320        wxTreeItemId item = MakeSubdirs(parent, localDir.Left(localDir.Length() - subDirs.Length()), subDirs);
321        if (!item)
322                return;
323
324        SafeSelectItem(item);
325}
326
327wxTreeItemId CLocalTreeView::GetNearestParent(wxString& localDir)
328{
329        const wxString separator = wxFileName::GetPathSeparator();
330#ifdef __WXMSW__
331        int pos = localDir.Find(separator);
332        if (pos == -1)
333                return wxTreeItemId();
334
335        wxString drive = localDir.Left(pos);
336        localDir = localDir.Mid(pos + 1);
337
338        wxTreeItemIdValue value;
339        wxTreeItemId root = GetFirstChild(m_drives, value);
340        while (root) {
341                if (!GetItemText(root).Left(drive.Length()).CmpNoCase(drive))
342                        break;
343
344                root = GetNextSibling(root);
345        }
346        if (!root) {
347                if (drive[1] == ':')
348                        return AddDrive(drive[0]);
349                return wxTreeItemId();
350        }
351#else
352                if (localDir[0] == '/')
353                        localDir = localDir.Mid(1);
354                wxTreeItemId root = GetRootItem();
355#endif
356
357        while (!localDir.empty()) {
358                wxString subDir;
359                int pos2 = localDir.Find(separator);
360                if (pos2 == -1)
361                        subDir = localDir;
362                else
363                        subDir = localDir.Left(pos2);
364
365                wxTreeItemId child = GetSubdir(root, subDir);
366                if (!child)
367                        return root;
368
369                if (!pos2)
370                        return child;
371
372                root = child;
373                localDir = localDir.Mid(pos2 + 1);
374        }
375
376        return root;
377}
378
379wxTreeItemId CLocalTreeView::GetSubdir(wxTreeItemId parent, const wxString& subDir)
380{
381        wxTreeItemIdValue value;
382        wxTreeItemId child = GetFirstChild(parent, value);
383        while (child)
384        {
385#ifdef __WXMSW__
386                if (!GetItemText(child).CmpNoCase(subDir))
387#else
388                if (GetItemText(child) == subDir)
389#endif
390                        return child;
391
392                child = GetNextSibling(child);
393        }
394        return wxTreeItemId();
395}
396
397#ifdef __WXMSW__
398
399bool CLocalTreeView::DisplayDrives(wxTreeItemId parent)
400{
401        wxGetApp().AddStartupProfileRecord(_T("CLocalTreeView::DisplayDrives"));
402
403        std::list<wxString> drives = CVolumeDescriptionEnumeratorThread::GetDrives();
404
405        m_pVolumeEnumeratorThread = new CVolumeDescriptionEnumeratorThread(this);
406        if (m_pVolumeEnumeratorThread->Failed()) {
407                delete m_pVolumeEnumeratorThread;
408                m_pVolumeEnumeratorThread = 0;
409        }
410
411        for( std::list<wxString>::const_iterator it = drives.begin(); it != drives.end(); ++it ) {
412                wxString drive = *it;
413                if (drive.Right(1) == _T("\\"))
414                        drive = drive.RemoveLast();
415
416                wxGetApp().AddStartupProfileRecord(wxString::Format(_T("CLocalTreeView::DisplayDrives adding drive %s"), drive.c_str()));
417                wxTreeItemId item = AppendItem(parent, drive, GetIconIndex(iconType::dir, drive));
418                AppendItem(item, _T(""));
419                SortChildren(parent);
420        }
421
422        wxGetApp().AddStartupProfileRecord(_T("CLocalTreeView::DisplayDrives adding drives done"));
423
424        return true;
425}
426
427#endif
428
429void CLocalTreeView::DisplayDir(wxTreeItemId parent, const wxString& dirname, const wxString& knownSubdir /*=_T("")*/)
430{
431        fz::local_filesys local_filesys;
432
433        {
434                wxLogNull log;
435                if (!local_filesys.begin_find_files(fz::to_native(dirname), true)) {
436                        if (!knownSubdir.empty()) {
437                                wxTreeItemId item = GetSubdir(parent, knownSubdir);
438                                if (item != wxTreeItemId())
439                                        return;
440
441                                const wxString fullName = dirname + knownSubdir;
442                                item = AppendItem(parent, knownSubdir, GetIconIndex(iconType::dir, fullName),
443#ifdef __WXMSW__
444                                                -1
445#else
446                                                GetIconIndex(iconType::opened_dir, fullName)
447#endif
448                                        );
449                                CheckSubdirStatus(item, fullName);
450                        }
451                        else
452                        {
453                                m_setSelection = true;
454                                DeleteChildren(parent);
455                                m_setSelection = false;
456                        }
457                        return;
458                }
459        }
460
461        wxASSERT(parent);
462        m_setSelection = true;
463        DeleteChildren(parent);
464        m_setSelection = false;
465
466        CFilterManager filter;
467
468        bool matchedKnown = false;
469
470        fz::native_string file;
471        bool wasLink;
472        int attributes;
473        bool is_dir;
474        static int64_t const size(-1);
475        fz::datetime date;
476        while (local_filesys.get_next_file(file, wasLink, is_dir, 0, &date, &attributes)) {
477                wxASSERT(is_dir);
478                if (file.empty()) {
479                        wxGetApp().DisplayEncodingWarning();
480                        continue;
481                }
482                wxString file_wx = file;
483
484                wxString fullName = dirname + file_wx;
485#ifdef __WXMSW__
486                if (file_wx.CmpNoCase(knownSubdir))
487#else
488                if (file_wx != knownSubdir)
489#endif
490                {
491                        if (filter.FilenameFiltered(file_wx, dirname, true, size, true, attributes, date))
492                                continue;
493                }
494                else
495                        matchedKnown = true;
496
497                wxTreeItemId item = AppendItem(parent, file_wx, GetIconIndex(iconType::dir, fullName),
498#ifdef __WXMSW__
499                                -1
500#else
501                                GetIconIndex(iconType::opened_dir, fullName)
502#endif
503                        );
504
505                CheckSubdirStatus(item, fullName);
506        }
507
508        if (!matchedKnown && !knownSubdir.empty()) {
509                const wxString fullName = dirname + knownSubdir;
510                wxTreeItemId item = AppendItem(parent, knownSubdir, GetIconIndex(iconType::dir, fullName),
511#ifdef __WXMSW__
512                                -1
513#else
514                                GetIconIndex(iconType::opened_dir, fullName)
515#endif
516                        );
517
518                CheckSubdirStatus(item, fullName);
519        }
520
521        SortChildren(parent);
522}
523
524wxString CLocalTreeView::HasSubdir(const wxString& dirname)
525{
526        wxLogNull nullLog;
527
528        CFilterManager filter;
529
530        fz::local_filesys local_filesys;
531        if (!local_filesys.begin_find_files(fz::to_native(dirname), true))
532                return wxString();
533
534        fz::native_string file;
535        bool wasLink;
536        int attributes;
537        bool is_dir;
538        static int64_t const size(-1);
539        fz::datetime date;
540        while (local_filesys.get_next_file(file, wasLink, is_dir, 0, &date, &attributes)) {
541                wxASSERT(is_dir);
542                if (file.empty()) {
543                        wxGetApp().DisplayEncodingWarning();
544                        continue;
545                }
546
547                wxString file_wx = file;
548                if (filter.FilenameFiltered(file_wx, dirname, true, size, true, attributes, date))
549                        continue;
550
551                return file_wx;
552        }
553
554        return wxString();
555}
556
557wxTreeItemId CLocalTreeView::MakeSubdirs(wxTreeItemId parent, wxString dirname, wxString subDir)
558{
559        const wxString& separator = wxFileName::GetPathSeparator();
560
561        while (!subDir.empty()) {
562                int pos = subDir.Find(separator);
563                wxString segment;
564                if (pos == -1) {
565                        segment = subDir;
566                        subDir = _T("");
567                }
568                else {
569                        segment = subDir.Left(pos);
570                        subDir = subDir.Mid(pos + 1);
571                }
572
573                DisplayDir(parent, dirname, segment);
574
575                wxTreeItemId item = GetSubdir(parent, segment);
576                if (!item)
577                        return wxTreeItemId();
578
579                parent = item;
580                dirname += segment + separator;
581        }
582
583        // Not needed, stays unexpanded by default
584        // DisplayDir(parent, dirname);
585        return parent;
586}
587
588void CLocalTreeView::OnItemExpanding(wxTreeEvent& event)
589{
590        wxTreeItemId item = event.GetItem();
591
592        wxTreeItemIdValue value;
593        wxTreeItemId child = GetFirstChild(item, value);
594        if (child && GetItemText(child).empty())
595                DisplayDir(item, GetDirFromItem(item));
596}
597
598wxString CLocalTreeView::GetDirFromItem(wxTreeItemId item)
599{
600        const wxString separator = wxFileName::GetPathSeparator();
601        wxString dir;
602        while (item)
603        {
604#ifdef __WXMSW__
605                if (item == m_desktop) {
606                        wxChar path[MAX_PATH + 1];
607                        if (SHGetFolderPath(0, CSIDL_DESKTOPDIRECTORY, 0, SHGFP_TYPE_CURRENT, path) != S_OK) {
608                                if (SHGetFolderPath(0, CSIDL_DESKTOP, 0, SHGFP_TYPE_CURRENT, path) != S_OK) {
609                                        wxMessageBoxEx(_("Failed to get desktop path"));
610                                        return _T("/");
611                                }
612                        }
613                        dir = path;
614                        if (dir.empty() || dir.Last() != separator)
615                                dir += separator;
616                        return dir;
617                }
618                else if (item == m_documents) {
619                        wxChar path[MAX_PATH + 1];
620                        if (SHGetFolderPath(0, CSIDL_PERSONAL, 0, SHGFP_TYPE_CURRENT, path) != S_OK) {
621                                wxMessageBoxEx(_("Failed to get 'My Documents' path"));
622                                return _T("/");
623                        }
624                        dir = path;
625                        if (dir.empty() || dir.Last() != separator)
626                                dir += separator;
627                        return dir;
628                }
629                else if (item == m_drives)
630                        return _T("/");
631                else if (GetItemParent(item) == m_drives) {
632                        wxString text = GetItemText(item);
633                        int pos = text.Find(_T(" "));
634                        if (pos == -1)
635                                return text + separator + dir;
636                        else
637                                return text.Left(pos) + separator + dir;
638                }
639                else
640#endif
641                if (item == GetRootItem())
642                        return _T("/") + dir;
643
644                dir = GetItemText(item) + separator + dir;
645
646                item = GetItemParent(item);
647        }
648
649        return separator;
650}
651
652struct t_dir
653{
654        wxString dir;
655        wxTreeItemId item;
656};
657
658void CLocalTreeView::UpdateSortMode()
659{
660        switch (COptions::Get()->GetOptionVal(OPTION_FILELIST_NAMESORT))
661        {
662        case 0:
663        default:
664                m_nameSortMode = CFileListCtrlSortBase::namesort_caseinsensitive;
665                break;
666        case 1:
667                m_nameSortMode = CFileListCtrlSortBase::namesort_casesensitive;
668                break;
669        case 2:
670                m_nameSortMode = CFileListCtrlSortBase::namesort_natural;
671                break;
672        }
673}
674
675void CLocalTreeView::RefreshListing()
676{
677        wxLogNull nullLog;
678
679        const wxString separator = wxFileName::GetPathSeparator();
680
681        std::list<t_dir> dirsToCheck;
682
683#ifdef __WXMSW__
684        int prevErrorMode = SetErrorMode(SEM_FAILCRITICALERRORS);
685
686        wxTreeItemIdValue tmp;
687        for (auto child = GetFirstChild(m_drives, tmp); child; child = GetNextSibling(child)) {
688                if (IsExpanded(child)) {
689                        wxString drive = GetItemText(child);
690                        int pos = drive.Find(_T(" "));
691                        if (pos != -1)
692                                drive = drive.Left(pos);
693
694                        t_dir dir;
695                        dir.dir = drive + separator;
696                        dir.item = child;
697                        dirsToCheck.push_back(dir);
698                }
699        }
700#else
701        t_dir dir;
702        dir.dir = separator;
703        dir.item = GetRootItem();
704        dirsToCheck.push_back(dir);
705#endif
706
707        CFilterManager filter;
708
709        while (!dirsToCheck.empty()) {
710                t_dir dir = dirsToCheck.front();
711                dirsToCheck.pop_front();
712
713                // Step 1: Check if directory exists
714                fz::local_filesys local_filesys;
715                if (!local_filesys.begin_find_files(fz::to_native(dir.dir), true)) {
716                        // Dir does exist (listed in parent) but may not be accessible.
717                        // Recurse into children anyhow, they might be accessible again.
718                        wxTreeItemIdValue value;
719                        for (auto child = GetFirstChild(dir.item, value); child; child = GetNextSibling(child)) {
720                                t_dir subdir;
721                                subdir.dir = dir.dir + GetItemText(child) + separator;
722                                subdir.item = child;
723                                dirsToCheck.push_back(subdir);
724                        }
725                        continue;
726                }
727
728                // Step 2: Enumerate subdirectories on disk and sort them
729                std::vector<wxString> dirs;
730
731                fz::native_string file;
732                static int64_t const size(-1);
733                bool was_link;
734                bool is_dir;
735                int attributes;
736                fz::datetime date;
737                while (local_filesys.get_next_file(file, was_link, is_dir, 0, &date, &attributes)) {
738                        if (file.empty()) {
739                                wxGetApp().DisplayEncodingWarning();
740                                continue;
741                        }
742
743                        wxString file_wx = file;
744                        if (filter.FilenameFiltered(file_wx, dir.dir, true, size, true, attributes, date))
745                                continue;
746
747                        dirs.push_back(file_wx);
748                }
749                auto const& sortFunc = CFileListCtrlSortBase::GetCmpFunction(m_nameSortMode);
750                std::sort(dirs.begin(), dirs.end(), [&](auto const& lhs, auto const& rhs) { return sortFunc(lhs, rhs) < 0; });
751
752                bool inserted = false;
753
754                // Step 3: Merge list of subdirectories with subtree.
755                wxTreeItemId child = GetLastChild(dir.item);
756                auto iter = dirs.rbegin();
757                while (child || iter != dirs.rend()) {
758                        int cmp;
759                        if (child && iter != dirs.rend())
760#ifdef __WXMSW__
761                                cmp = GetItemText(child).CmpNoCase(*iter);
762#else
763                                cmp = GetItemText(child).Cmp(*iter);
764#endif
765                        else if (child)
766                                cmp = 1;
767                        else
768                                cmp = -1;
769
770                        if (!cmp) {
771                                // Found item with same name. Mark it for further processing
772                                if (!IsExpanded(child)) {
773                                        wxString path = dir.dir + *iter + separator;
774                                        if (!CheckSubdirStatus(child, path)) {
775                                                t_dir subdir;
776                                                subdir.dir = path;
777                                                subdir.item = child;
778                                                dirsToCheck.push_front(subdir);
779                                        }
780                                }
781                                else {
782                                        t_dir subdir;
783                                        subdir.dir = dir.dir + *iter + separator;
784                                        subdir.item = child;
785                                        dirsToCheck.push_front(subdir);
786                                }
787                                child = GetPrevSibling(child);
788                                ++iter;
789                        }
790                        else if (cmp > 0) {
791                                // Subdirectory currently in tree no longer exists.
792                                // Delete child from tree, unless current selection
793                                // is in the subtree.
794                                wxTreeItemId sel = GetSelection();
795                                while (sel && sel != child)
796                                        sel = GetItemParent(sel);
797                                wxTreeItemId prev = GetPrevSibling(child);
798                                if (!sel)
799                                        Delete(child);
800                                child = prev;
801                        }
802                        else if (cmp < 0) {
803                                // New subdirectory, add treeitem
804                                wxString fullname = dir.dir + *iter + separator;
805                                wxTreeItemId newItem = AppendItem(dir.item, *iter, GetIconIndex(iconType::dir, fullname),
806#ifdef __WXMSW__
807                                                -1
808#else
809                                                GetIconIndex(iconType::opened_dir, fullname)
810#endif
811                                        );
812
813                                CheckSubdirStatus(newItem, fullname);
814                                ++iter;
815                                inserted = true;
816                        }
817                }
818                if (inserted)
819                        SortChildren(dir.item);
820        }
821
822#ifdef __WXMSW__
823        SetErrorMode(prevErrorMode);
824#endif
825}
826
827void CLocalTreeView::OnSelectionChanged(wxTreeEvent& event)
828{
829        if (m_setSelection) {
830                event.Skip();
831                return;
832        }
833
834        wxTreeItemId item = event.GetItem();
835        if (!item)
836                return;
837
838        wxString dir = GetDirFromItem(item);
839
840        wxString error;
841        if (!m_pState->SetLocalDir(dir, &error)) {
842                if (!error.empty())
843                        wxMessageBoxEx(error, _("Failed to change directory"), wxICON_INFORMATION);
844                else
845                        wxBell();
846                m_setSelection = true;
847                SelectItem(event.GetOldItem());
848                m_setSelection = false;
849        }
850}
851
852void CLocalTreeView::OnStateChange(CState*, enum t_statechange_notifications notification, const wxString&, const void*)
853{
854        if (notification == STATECHANGE_LOCAL_DIR)
855                SetDir(m_pState->GetLocalDir().GetPath());
856        else {
857                wxASSERT(notification == STATECHANGE_APPLYFILTER);
858                RefreshListing();
859        }
860}
861
862void CLocalTreeView::OnBeginDrag(wxTreeEvent& event)
863{
864        wxTreeItemId item = event.GetItem();
865        if (!item)
866                return;
867
868#ifdef __WXMSW__
869        if (item == m_drives || item == m_desktop || item == m_documents)
870                return;
871#endif
872
873        wxString dir = GetDirFromItem(item);
874        if (dir == _T("/"))
875                return;
876
877#ifdef __WXMSW__
878        if (!dir.empty() && dir.Last() == '\\')
879                dir.RemoveLast();
880#endif
881        if (!dir.empty() && dir.Last() == '/')
882                dir.RemoveLast();
883
884#ifdef __WXMSW__
885        if (!dir.empty() && dir.Last() == ':')
886                return;
887#endif
888
889        CDragDropManager* pDragDropManager = CDragDropManager::Init();
890        pDragDropManager->pDragSource = this;
891
892        wxFileDataObject obj;
893
894        obj.AddFile(dir);
895
896        wxDropSource source(this);
897        source.SetData(obj);
898        int res = source.DoDragDrop(wxDrag_AllowMove);
899
900        bool handled_internally = pDragDropManager->pDropTarget != 0;
901
902        pDragDropManager->Release();
903
904        if (!handled_internally && (res == wxDragCopy || res == wxDragMove))
905        {
906                // We only need to refresh local side if the operation got handled
907                // externally, the internal handlers do this for us already
908                m_pState->RefreshLocal();
909        }
910}
911
912#ifdef __WXMSW__
913
914wxString CLocalTreeView::GetSpecialFolder(int folder, int &iconIndex, int &openIconIndex)
915{
916        LPITEMIDLIST list;
917        if (SHGetSpecialFolderLocation((HWND)GetHandle(), folder, &list) != S_OK)
918                return wxString();
919
920        SHFILEINFO shFinfo;
921        if (!SHGetFileInfo((LPCTSTR)list, 0, &shFinfo, sizeof(shFinfo), SHGFI_PIDL | SHGFI_ICON | SHGFI_SMALLICON))
922                return wxString();
923
924        DestroyIcon(shFinfo.hIcon);
925        iconIndex = shFinfo.iIcon;
926
927        if (!SHGetFileInfo((LPCTSTR)list, 0, &shFinfo, sizeof(shFinfo), SHGFI_PIDL | SHGFI_ICON | SHGFI_SMALLICON | SHGFI_OPENICON | SHGFI_DISPLAYNAME))
928                return wxString();
929
930        DestroyIcon(shFinfo.hIcon);
931        openIconIndex = shFinfo.iIcon;
932
933        wxString name = shFinfo.szDisplayName;
934
935        LPMALLOC pMalloc;
936        SHGetMalloc(&pMalloc);
937
938        if (pMalloc)
939        {
940                pMalloc->Free(list);
941                pMalloc->Release();
942        }
943        else
944                wxLogLastError(wxT("SHGetMalloc"));
945
946        return name;
947}
948
949bool CLocalTreeView::CreateRoot()
950{
951        int iconIndex, openIconIndex;
952        wxString name = GetSpecialFolder(CSIDL_DESKTOP, iconIndex, openIconIndex);
953        if (name.empty())
954        {
955                name = _("Desktop");
956                iconIndex = openIconIndex = -1;
957        }
958
959        m_desktop = AddRoot(name, iconIndex, openIconIndex);
960
961        name = GetSpecialFolder(CSIDL_PERSONAL, iconIndex, openIconIndex);
962        if (name.empty())
963        {
964                name = _("My Documents");
965                iconIndex = openIconIndex = -1;
966        }
967
968        m_documents = AppendItem(m_desktop, name, iconIndex, openIconIndex);
969
970
971        name = GetSpecialFolder(CSIDL_DRIVES, iconIndex, openIconIndex);
972        if (name.empty())
973        {
974                name = _("My Computer");
975                iconIndex = openIconIndex = -1;
976        }
977
978        m_drives = AppendItem(m_desktop, name, iconIndex, openIconIndex);
979
980        DisplayDrives(m_drives);
981        Expand(m_desktop);
982        Expand(m_drives);
983
984        return true;
985}
986
987void CLocalTreeView::OnVolumesEnumerated(wxCommandEvent& event)
988{
989        if (!m_pVolumeEnumeratorThread)
990                return;
991
992        std::list<CVolumeDescriptionEnumeratorThread::t_VolumeInfo> volumeInfo;
993        volumeInfo = m_pVolumeEnumeratorThread->GetVolumes();
994
995        if (event.GetEventType() == fzEVT_VOLUMESENUMERATED)
996        {
997                delete m_pVolumeEnumeratorThread;
998                m_pVolumeEnumeratorThread = 0;
999        }
1000
1001        for (std::list<CVolumeDescriptionEnumeratorThread::t_VolumeInfo>::const_iterator iter = volumeInfo.begin(); iter != volumeInfo.end(); ++iter)
1002        {
1003                wxString drive = iter->volume;
1004
1005                wxTreeItemIdValue tmp;
1006                wxTreeItemId item = GetFirstChild(m_drives, tmp);
1007                while (item)
1008                {
1009                        wxString name = GetItemText(item);
1010                        if (name == drive || name.Left(drive.Len() + 1) == drive + _T(" "))
1011                        {
1012                                SetItemText(item, drive + _T(" (") + iter->volumeName + _T(")"));
1013                                break;
1014                        }
1015                        item = GetNextChild(m_drives, tmp);
1016                }
1017        }
1018}
1019
1020#endif
1021
1022void CLocalTreeView::OnContextMenu(wxTreeEvent& event)
1023{
1024        m_contextMenuItem = event.GetItem();
1025        if (!m_contextMenuItem.IsOk())
1026                return;
1027
1028        wxMenu* pMenu = wxXmlResource::Get()->LoadMenu(_T("ID_MENU_LOCALTREE"));
1029        if (!pMenu)
1030                return;
1031
1032        const CLocalPath path(GetDirFromItem(m_contextMenuItem));
1033
1034        const bool hasParent = path.HasParent();
1035        const bool writeable = path.IsWriteable();
1036
1037        const bool remoteConnected = m_pState->IsRemoteConnected() && !m_pState->GetRemotePath().empty();
1038
1039        pMenu->Enable(XRCID("ID_UPLOAD"), hasParent && remoteConnected);
1040        pMenu->Enable(XRCID("ID_ADDTOQUEUE"), hasParent && remoteConnected);
1041        pMenu->Enable(XRCID("ID_MKDIR"), writeable);
1042        pMenu->Enable(XRCID("ID_DELETE"), writeable && hasParent);
1043        pMenu->Enable(XRCID("ID_RENAME"), writeable && hasParent);
1044
1045        PopupMenu(pMenu);
1046        delete pMenu;
1047}
1048
1049void CLocalTreeView::OnMenuUpload(wxCommandEvent& event)
1050{
1051        if (!m_contextMenuItem.IsOk())
1052                return;
1053
1054        CLocalPath path(GetDirFromItem(m_contextMenuItem));
1055
1056        if (!path.HasParent())
1057                return;
1058
1059        if (!m_pState->IsRemoteConnected())
1060                return;
1061
1062        const CServer server = *m_pState->GetServer();
1063        CServerPath remotePath = m_pState->GetRemotePath();
1064        if (remotePath.empty())
1065                return;
1066
1067        if (!remotePath.ChangePath(GetItemText(m_contextMenuItem)))
1068                return;
1069
1070        m_pQueueView->QueueFolder(event.GetId() == XRCID("ID_ADDTOQUEUE"), false, path, remotePath, server);
1071}
1072
1073// Create a new Directory
1074void CLocalTreeView::OnMenuMkdir(wxCommandEvent&)
1075{
1076        wxString newdir = MenuMkdir();
1077        if (!newdir.empty()) {
1078                RefreshListing();
1079                m_pState->RefreshLocal();
1080        }
1081}
1082
1083// Create a new Directory and enter the new Directory
1084void CLocalTreeView::OnMenuMkdirChgDir(wxCommandEvent&)
1085{
1086        wxString newdir = MenuMkdir();
1087        if (newdir.empty()) {
1088                return;
1089        }
1090
1091        // OnMenuEnter
1092        wxString error;
1093        if (!m_pState->SetLocalDir(newdir, &error))
1094        {
1095                if (!error.empty())
1096                        wxMessageBoxEx(error, _("Failed to change directory"), wxICON_INFORMATION);
1097                else
1098                        wxBell();
1099        }
1100}
1101
1102// Helper-Function to create a new Directory
1103// Returns the name of the new directory
1104wxString CLocalTreeView::MenuMkdir()
1105{
1106        if (!m_contextMenuItem.IsOk())
1107                return wxString();
1108
1109        wxString path = GetDirFromItem(m_contextMenuItem);
1110        if (path.Last() != wxFileName::GetPathSeparator())
1111                path += wxFileName::GetPathSeparator();
1112
1113        if (!CLocalPath(path).IsWriteable())
1114        {
1115                wxBell();
1116                return wxString();
1117        }
1118
1119        CInputDialog dlg;
1120        if (!dlg.Create(this, _("Create directory"), _("Please enter the name of the directory which should be created:")))
1121                return wxString();
1122
1123        wxString newName = _("New directory");
1124        dlg.SetValue(path + newName);
1125        dlg.SelectText(path.Len(), path.Len() + newName.Len());
1126
1127        if (dlg.ShowModal() != wxID_OK)
1128                return wxString();
1129
1130        wxFileName fn(dlg.GetValue(), _T(""));
1131        if (!fn.Normalize(wxPATH_NORM_ALL, path))
1132        {
1133                wxBell();
1134                return wxString();
1135        }
1136
1137        bool res;
1138        {
1139                wxLogNull log;
1140                res = fn.Mkdir(fn.GetPath(), 0777, wxPATH_MKDIR_FULL);
1141        }
1142
1143        if (!res) {
1144                wxBell();
1145                return wxString();
1146        }
1147
1148        return fn.GetPath();
1149}
1150
1151void CLocalTreeView::OnMenuRename(wxCommandEvent&)
1152{
1153        if (!m_contextMenuItem.IsOk())
1154                return;
1155
1156#ifdef __WXMSW__
1157        if (m_contextMenuItem == m_desktop || m_contextMenuItem == m_documents) {
1158                wxBell();
1159                return;
1160        }
1161#endif
1162
1163        CLocalPath path(GetDirFromItem(m_contextMenuItem));
1164        if (!path.HasParent() || !path.IsWriteable()) {
1165                wxBell();
1166                return;
1167        }
1168
1169        EditLabel(m_contextMenuItem);
1170}
1171
1172void CLocalTreeView::OnMenuDelete(wxCommandEvent&)
1173{
1174        if (!m_contextMenuItem.IsOk())
1175                return;
1176
1177        wxString path = GetDirFromItem(m_contextMenuItem);
1178
1179        CLocalPath local_path(path);
1180        if (!local_path.HasParent() || !local_path.IsWriteable())
1181                return;
1182
1183        gui_recursive_remove rmd(this);
1184        rmd.remove(fz::to_native(path));
1185
1186        wxTreeItemId item = GetSelection();
1187        while (item && item != m_contextMenuItem)
1188                item = GetItemParent(item);
1189
1190        if (!item) {
1191                if (GetItemParent(m_contextMenuItem) == GetSelection())
1192                        m_pState->RefreshLocal();
1193                else
1194                        RefreshListing();
1195                return;
1196        }
1197
1198        if (!path.empty() && path.Last() == wxFileName::GetPathSeparator())
1199                path.RemoveLast();
1200        int pos = path.Find(wxFileName::GetPathSeparator(), true);
1201        if (pos < 1)
1202                path = _T("/");
1203        else
1204                path = path.Left(pos);
1205
1206        m_pState->SetLocalDir(path);
1207        RefreshListing();
1208}
1209
1210void CLocalTreeView::OnBeginLabelEdit(wxTreeEvent& event)
1211{
1212        wxTreeItemId item = event.GetItem();
1213
1214#ifdef __WXMSW__
1215        if (item == m_desktop || item == m_documents)
1216        {
1217                wxBell();
1218                event.Veto();
1219                return;
1220        }
1221#endif
1222
1223        CLocalPath path(GetDirFromItem(item));
1224
1225        if (!path.HasParent() || !path.IsWriteable())
1226        {
1227                wxBell();
1228                event.Veto();
1229                return;
1230        }
1231}
1232
1233void CLocalTreeView::OnEndLabelEdit(wxTreeEvent& event)
1234{
1235        if (event.IsEditCancelled()) {
1236                event.Veto();
1237                return;
1238        }
1239
1240        wxTreeItemId item = event.GetItem();
1241
1242#ifdef __WXMSW__
1243        if (item == m_desktop || item == m_documents) {
1244                wxBell();
1245                event.Veto();
1246                return;
1247        }
1248#endif
1249
1250        wxString path = GetDirFromItem(item);
1251
1252        CLocalPath local_path(path);
1253        if (!local_path.HasParent() || !local_path.IsWriteable()) {
1254                wxBell();
1255                event.Veto();
1256                return;
1257        }
1258
1259        if (!path.empty() && path.Last() == wxFileName::GetPathSeparator())
1260                path.RemoveLast();
1261
1262        int pos = path.Find(wxFileName::GetPathSeparator(), true);
1263        wxASSERT(pos != -1);
1264
1265        wxString parent = path.Left(pos + 1);
1266
1267        const wxString& oldName = GetItemText(item);
1268        const wxString& newName = event.GetLabel();
1269        if (newName.empty()) {
1270                wxBell();
1271                event.Veto();
1272                return;
1273        }
1274
1275        wxASSERT(parent + oldName == path);
1276
1277        if (oldName == newName)
1278                return;
1279
1280        if (!RenameFile(this, parent, oldName, newName)) {
1281                event.Veto();
1282                return;
1283        }
1284
1285        // We may call SetLocalDir, item might be deleted by it, so
1286        // if we don't rename item now and veto the event, wxWidgets
1287        // might access deleted item.
1288        event.Veto();
1289        SetItemText(item, newName);
1290
1291        wxTreeItemId currentSel = GetSelection();
1292        if (currentSel == wxTreeItemId()) {
1293                RefreshListing();
1294                return;
1295        }
1296
1297        if (item == currentSel) {
1298                m_pState->SetLocalDir(parent + newName);
1299                return;
1300        }
1301
1302        wxString sub;
1303
1304        wxTreeItemId tmp = currentSel;
1305        while (tmp != GetRootItem() && tmp != item) {
1306                sub = wxFileName::GetPathSeparator() + GetItemText(tmp) + sub;
1307                tmp = GetItemParent(tmp);
1308        }
1309
1310        if (tmp == GetRootItem()) {
1311                // Rename unrelated to current selection
1312                return;
1313        }
1314
1315        // Current selection below renamed item
1316        m_pState->SetLocalDir(parent + newName + sub);
1317}
1318
1319void CLocalTreeView::OnChar(wxKeyEvent& event)
1320{
1321        m_contextMenuItem = GetSelection();
1322
1323        wxCommandEvent cmdEvt;
1324        if (event.GetKeyCode() == WXK_F2)
1325                OnMenuRename(cmdEvt);
1326        else if (event.GetKeyCode() == WXK_DELETE || event.GetKeyCode() == WXK_NUMPAD_DELETE)
1327                OnMenuDelete(cmdEvt);
1328        else
1329                event.Skip();
1330}
1331
1332bool CLocalTreeView::CheckSubdirStatus(wxTreeItemId& item, const wxString& path)
1333{
1334        wxTreeItemIdValue value;
1335        wxTreeItemId child = GetFirstChild(item, value);
1336
1337        static int64_t const size(-1);
1338
1339#ifdef __WXMAC__
1340        // By default, OS X has a list of servers mounted into /net,
1341        // listing that directory is slow.
1342        if (GetItemParent(item) == GetRootItem() && (path == _T("/net") || path == _T("/net/")))
1343        {
1344                CFilterManager filter;
1345
1346                const int attributes = S_IRUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH;
1347                if (!filter.FilenameFiltered(_T("localhost"), path, true, size, true, attributes, fz::datetime()))
1348                {
1349                        if (!child)
1350                                AppendItem(item, _T(""));
1351                        return true;
1352                }
1353        }
1354#endif
1355
1356        if (child) {
1357                if (!GetItemText(child).empty())
1358                        return false;
1359
1360                CTreeItemData* pData = (CTreeItemData*)GetItemData(child);
1361                if (pData) {
1362                        bool wasLink;
1363                        int attributes;
1364                        enum fz::local_filesys::type type;
1365                        fz::datetime date;
1366                        if (!path.empty() && path.Last() == fz::local_filesys::path_separator)
1367                                type = fz::local_filesys::get_file_info(fz::to_native(path + pData->m_known_subdir), wasLink, 0, &date, &attributes);
1368                        else
1369                                type = fz::local_filesys::get_file_info(fz::to_native(path + fz::local_filesys::path_separator + pData->m_known_subdir), wasLink, 0, &date, &attributes);
1370                        if (type == fz::local_filesys::dir) {
1371                                CFilterManager filter;
1372                                if (!filter.FilenameFiltered(pData->m_known_subdir, path, true, size, true, attributes, date))
1373                                        return true;
1374                        }
1375                }
1376        }
1377
1378        wxString sub = HasSubdir(path);
1379        if (!sub.empty()) {
1380                wxTreeItemId subItem = AppendItem(item, _T(""));
1381                SetItemData(subItem, new CTreeItemData(sub));
1382        }
1383        else if (child)
1384                Delete(child);
1385
1386        return true;
1387}
1388
1389#ifdef __WXMSW__
1390void CLocalTreeView::OnDevicechange(WPARAM wParam, LPARAM lParam)
1391{
1392        if (!m_drives)
1393                return;
1394
1395        if (wParam == DBT_DEVICEARRIVAL || wParam == DBT_DEVICEREMOVECOMPLETE) {
1396                DEV_BROADCAST_HDR* pHdr = (DEV_BROADCAST_HDR*)lParam;
1397                if (pHdr->dbch_devicetype != DBT_DEVTYP_VOLUME)
1398                        return;
1399
1400                // Added or removed volume
1401                DEV_BROADCAST_VOLUME* pVolume = (DEV_BROADCAST_VOLUME*)lParam;
1402
1403                wxChar drive = 'A';
1404                int mask = 1;
1405                while (drive <= 'Z') {
1406                        if (pVolume->dbcv_unitmask & mask) {
1407                                if (wParam == DBT_DEVICEARRIVAL)
1408                                        AddDrive(drive);
1409                                else {
1410                                        RemoveDrive(drive);
1411
1412                                        if (pVolume->dbcv_flags & DBTF_MEDIA) {
1413                                                // E.g. disk removed from CD-ROM drive, need to keep the drive letter
1414                                                AddDrive(drive);
1415                                        }
1416                                }
1417                        }
1418                        drive++;
1419                        mask <<= 1;
1420                }
1421
1422                if (GetSelection() == m_drives && m_pState)
1423                        m_pState->RefreshLocal();
1424        }
1425}
1426
1427wxTreeItemId CLocalTreeView::AddDrive(wxChar letter)
1428{
1429        wxString drive = letter;
1430        drive += _T(":");
1431
1432        long drivesToHide = CVolumeDescriptionEnumeratorThread::GetDrivesToHide();
1433        if( CVolumeDescriptionEnumeratorThread::IsHidden(drive.wc_str(), drivesToHide) ) {
1434                return wxTreeItemId();
1435        }
1436
1437        // Get the label of the drive
1438        wxChar volumeName[501];
1439        int oldErrorMode = SetErrorMode(SEM_FAILCRITICALERRORS);
1440        BOOL res = GetVolumeInformation((drive + _T("\\")).wc_str(), volumeName, 500, 0, 0, 0, 0, 0);
1441        SetErrorMode(oldErrorMode);
1442
1443        wxString itemLabel = drive;
1444        if (res && volumeName[0]) {
1445                itemLabel += _T(" (");
1446                itemLabel += volumeName;
1447                itemLabel += _T(")");
1448        }
1449
1450        wxTreeItemIdValue value;
1451        wxTreeItemId driveItem = GetFirstChild(m_drives, value);
1452        while (driveItem)
1453        {
1454                if (!GetItemText(driveItem).Left(2).CmpNoCase(drive))
1455                        break;
1456
1457                driveItem = GetNextSibling(driveItem);
1458        }
1459        if (driveItem)
1460        {
1461                SetItemText(driveItem, itemLabel);
1462                int icon = GetIconIndex(iconType::dir, drive + _T("\\"));
1463                SetItemImage(driveItem, icon, wxTreeItemIcon_Normal);
1464                SetItemImage(driveItem, icon, wxTreeItemIcon_Selected);
1465                SetItemImage(driveItem, icon, wxTreeItemIcon_Expanded);
1466                SetItemImage(driveItem, icon, wxTreeItemIcon_SelectedExpanded);
1467
1468                return driveItem;
1469        }
1470
1471        wxTreeItemId item = AppendItem(m_drives, itemLabel, GetIconIndex(iconType::dir, drive + _T("\\")));
1472        AppendItem(item, _T(""));
1473        SortChildren(m_drives);
1474
1475        return item;
1476}
1477
1478void CLocalTreeView::RemoveDrive(wxChar drive)
1479{
1480        wxString driveName = drive;
1481        driveName += _T(":");
1482        wxTreeItemIdValue value;
1483        wxTreeItemId driveItem = GetFirstChild(m_drives, value);
1484        while (driveItem)
1485        {
1486                if (!GetItemText(driveItem).Left(2).CmpNoCase(driveName))
1487                        break;
1488
1489                driveItem = GetNextSibling(driveItem);
1490        }
1491        if (!driveItem)
1492                return;
1493
1494        Delete(driveItem);
1495}
1496
1497void CLocalTreeView::OnSelectionChanging(wxTreeEvent& event)
1498{
1499        // On-demand open icon for selected items
1500        wxTreeItemId item = event.GetItem();
1501
1502        if (!item)
1503                return;
1504
1505        if (GetItemImage(item, wxTreeItemIcon_Selected) == -1)
1506        {
1507                int icon = GetIconIndex(iconType::opened_dir, GetDirFromItem(item));
1508                SetItemImage(item, icon, wxTreeItemIcon_Selected);
1509                SetItemImage(item, icon, wxTreeItemIcon_SelectedExpanded);
1510        }
1511}
1512
1513#endif
1514
1515void CLocalTreeView::OnMenuOpen(wxCommandEvent&)
1516{
1517        if (!m_contextMenuItem.IsOk())
1518                return;
1519
1520        wxString path = GetDirFromItem(m_contextMenuItem);
1521        if (path.empty())
1522                return;
1523
1524        OpenInFileManager(path);
1525}
1526
1527void CLocalTreeView::OnOptionsChanged(changed_options_t const& options)
1528{
1529        if (options.test(OPTION_FILELIST_NAMESORT)) {
1530                UpdateSortMode();
1531                RefreshListing();
1532        }
1533}
Note: See TracBrowser for help on using the repository browser.