source: filezilla/trunk/fuentes/src/interface/LocalListView.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: 42.2 KB
Line 
1#include <filezilla.h>
2
3#define FILELISTCTRL_INCLUDE_TEMPLATE_DEFINITION
4
5#include "LocalListView.h"
6#include "queue.h"
7#include "filezillaapp.h"
8#include "filter.h"
9#include "file_utils.h"
10#include "inputdialog.h"
11#include <algorithm>
12#include "dndobjects.h"
13#include "Options.h"
14#ifdef __WXMSW__
15#include "lm.h"
16#include <wx/msw/registry.h>
17#include "volume_enumerator.h"
18#endif
19#include "edithandler.h"
20#include "dragdropmanager.h"
21#include "drop_target_ex.h"
22#include "filelist_statusbar.h"
23#include "sizeformatting.h"
24#include "timeformatting.h"
25
26#include <libfilezilla/local_filesys.hpp>
27#include <libfilezilla/recursive_remove.hpp>
28
29class CLocalListViewDropTarget : public CScrollableDropTarget<wxListCtrlEx>
30{
31public:
32        CLocalListViewDropTarget(CLocalListView* pLocalListView)
33                : CScrollableDropTarget<wxListCtrlEx>(pLocalListView)
34                , m_pLocalListView(pLocalListView), m_pFileDataObject(new wxFileDataObject()),
35                m_pRemoteDataObject(new CRemoteDataObject())
36        {
37                m_pDataObject = new wxDataObjectComposite;
38                m_pDataObject->Add(m_pRemoteDataObject, true);
39                m_pDataObject->Add(m_pFileDataObject, false);
40                SetDataObject(m_pDataObject);
41        }
42
43        void ClearDropHighlight()
44        {
45                const int dropTarget = m_pLocalListView->m_dropTarget;
46                if (dropTarget != -1)
47                {
48                        m_pLocalListView->m_dropTarget = -1;
49#ifdef __WXMSW__
50                        m_pLocalListView->SetItemState(dropTarget, 0, wxLIST_STATE_DROPHILITED);
51#else
52                        m_pLocalListView->RefreshItem(dropTarget);
53#endif
54                }
55        }
56
57        virtual wxDragResult OnData(wxCoord x, wxCoord y, wxDragResult def)
58        {
59                def = CScrollableDropTarget<wxListCtrlEx>::FixupDragResult(def);
60
61                if (def == wxDragError ||
62                        def == wxDragNone ||
63                        def == wxDragCancel)
64                        return def;
65
66                if (m_pLocalListView->m_fileData.empty())
67                        return wxDragError;
68
69                if (def != wxDragCopy && def != wxDragMove)
70                        return wxDragError;
71
72                CDragDropManager* pDragDropManager = CDragDropManager::Get();
73                if (pDragDropManager)
74                        pDragDropManager->pDropTarget = m_pLocalListView;
75
76                wxString subdir;
77                int flags;
78                int hit = m_pLocalListView->HitTest(wxPoint(x, y), flags, 0);
79                if (hit != -1 && (flags & wxLIST_HITTEST_ONITEM))
80                {
81                        const CLocalFileData* const data = m_pLocalListView->GetData(hit);
82                        if (data && data->dir)
83                                subdir = data->name;
84                }
85
86                CLocalPath dir = m_pLocalListView->m_pState->GetLocalDir();
87                if (!subdir.empty())
88                {
89                        if (!dir.ChangePath(subdir))
90                                return wxDragError;
91                }
92
93                if (!dir.IsWriteable())
94                        return wxDragError;
95
96                if (!GetData())
97                        return wxDragError;
98
99                if (m_pDataObject->GetReceivedFormat() == m_pFileDataObject->GetFormat())
100                        m_pLocalListView->m_pState->HandleDroppedFiles(m_pFileDataObject, dir, def == wxDragCopy);
101                else
102                {
103                        if (m_pRemoteDataObject->GetProcessId() != (int)wxGetProcessId())
104                        {
105                                wxMessageBoxEx(_("Drag&drop between different instances of FileZilla has not been implemented yet."));
106                                return wxDragNone;
107                        }
108
109                        if (!m_pLocalListView->m_pState->GetServer() || !m_pRemoteDataObject->GetServer().EqualsNoPass(*m_pLocalListView->m_pState->GetServer()))
110                        {
111                                wxMessageBoxEx(_("Drag&drop between different servers has not been implemented yet."));
112                                return wxDragNone;
113                        }
114
115                        if (!m_pLocalListView->m_pState->DownloadDroppedFiles(m_pRemoteDataObject, dir))
116                                return wxDragNone;
117                }
118
119                return def;
120        }
121
122        virtual bool OnDrop(wxCoord x, wxCoord y)
123        {
124                CScrollableDropTarget<wxListCtrlEx>::OnDrop(x, y);
125                ClearDropHighlight();
126
127                if (m_pLocalListView->m_fileData.empty())
128                        return false;
129
130                return true;
131        }
132
133        virtual int DisplayDropHighlight(wxPoint point)
134        {
135                DoDisplayDropHighlight(point);
136                return -1;
137        }
138
139        virtual wxString DoDisplayDropHighlight(wxPoint point)
140        {
141                wxString subDir;
142
143                int flags;
144                int hit = m_pLocalListView->HitTest(point, flags, 0);
145                if (!(flags & wxLIST_HITTEST_ONITEM))
146                        hit = -1;
147
148                if (hit != -1)
149                {
150                        const CLocalFileData* const data = m_pLocalListView->GetData(hit);
151                        if (!data || !data->dir)
152                                hit = -1;
153                        else
154                        {
155                                const CDragDropManager* pDragDropManager = CDragDropManager::Get();
156                                if (pDragDropManager && pDragDropManager->pDragSource == m_pLocalListView)
157                                {
158                                        if (m_pLocalListView->GetItemState(hit, wxLIST_STATE_SELECTED))
159                                                hit = -1;
160                                        else
161                                                subDir = data->name;
162                                }
163                                else
164                                        subDir = data->name;
165                        }
166                }
167                if (hit != m_pLocalListView->m_dropTarget)
168                {
169                        ClearDropHighlight();
170                        if (hit != -1)
171                        {
172                                m_pLocalListView->m_dropTarget = hit;
173#ifdef __WXMSW__
174                                m_pLocalListView->SetItemState(hit, wxLIST_STATE_DROPHILITED, wxLIST_STATE_DROPHILITED);
175#else
176                                m_pLocalListView->RefreshItem(hit);
177#endif
178                        }
179                }
180
181                return subDir;
182        }
183
184        virtual wxDragResult OnDragOver(wxCoord x, wxCoord y, wxDragResult def)
185        {
186                def = CScrollableDropTarget<wxListCtrlEx>::OnDragOver(x, y, def);
187
188                if (def == wxDragError ||
189                        def == wxDragNone ||
190                        def == wxDragCancel)
191                {
192                        ClearDropHighlight();
193                        return def;
194                }
195
196                if (m_pLocalListView->m_fileData.empty())
197                {
198                        ClearDropHighlight();
199                        return wxDragNone;
200                }
201
202                const wxString& subdir = DoDisplayDropHighlight(wxPoint(x, y));
203
204                CLocalPath dir = m_pLocalListView->m_pState->GetLocalDir();
205                if (subdir.empty()) {
206                        const CDragDropManager* pDragDropManager = CDragDropManager::Get();
207                        if (pDragDropManager && pDragDropManager->localParent == m_pLocalListView->m_dir)
208                                return wxDragNone;
209                }
210                else
211                {
212                        if (!dir.ChangePath(subdir))
213                                return wxDragNone;
214                }
215
216                if (!dir.IsWriteable())
217                        return wxDragNone;
218
219                return def;
220        }
221
222        virtual void OnLeave()
223        {
224                CScrollableDropTarget<wxListCtrlEx>::OnLeave();
225                ClearDropHighlight();
226        }
227
228        virtual wxDragResult OnEnter(wxCoord x, wxCoord y, wxDragResult def)
229        {
230                def = CScrollableDropTarget<wxListCtrlEx>::OnEnter(x, y, def);
231                return OnDragOver(x, y, def);
232        }
233
234protected:
235        CLocalListView *m_pLocalListView;
236        wxFileDataObject* m_pFileDataObject;
237        CRemoteDataObject* m_pRemoteDataObject;
238        wxDataObjectComposite* m_pDataObject;
239};
240
241BEGIN_EVENT_TABLE(CLocalListView, CFileListCtrl<CLocalFileData>)
242        EVT_LIST_ITEM_ACTIVATED(wxID_ANY, CLocalListView::OnItemActivated)
243        EVT_CONTEXT_MENU(CLocalListView::OnContextMenu)
244        // Map both ID_UPLOAD and ID_ADDTOQUEUE to OnMenuUpload, code is identical
245        EVT_MENU(XRCID("ID_UPLOAD"), CLocalListView::OnMenuUpload)
246        EVT_MENU(XRCID("ID_ADDTOQUEUE"), CLocalListView::OnMenuUpload)
247        EVT_MENU(XRCID("ID_MKDIR"), CLocalListView::OnMenuMkdir)
248        EVT_MENU(XRCID("ID_MKDIR_CHGDIR"), CLocalListView::OnMenuMkdirChgDir)
249        EVT_MENU(XRCID("ID_DELETE"), CLocalListView::OnMenuDelete)
250        EVT_MENU(XRCID("ID_RENAME"), CLocalListView::OnMenuRename)
251        EVT_KEY_DOWN(CLocalListView::OnKeyDown)
252        EVT_LIST_BEGIN_DRAG(wxID_ANY, CLocalListView::OnBeginDrag)
253        EVT_MENU(XRCID("ID_OPEN"), CLocalListView::OnMenuOpen)
254        EVT_MENU(XRCID("ID_EDIT"), CLocalListView::OnMenuEdit)
255        EVT_MENU(XRCID("ID_ENTER"), CLocalListView::OnMenuEnter)
256#ifdef __WXMSW__
257        EVT_COMMAND(-1, fzEVT_VOLUMESENUMERATED, CLocalListView::OnVolumesEnumerated)
258        EVT_COMMAND(-1, fzEVT_VOLUMEENUMERATED, CLocalListView::OnVolumesEnumerated)
259#endif
260        EVT_MENU(XRCID("ID_CONTEXT_REFRESH"), CLocalListView::OnMenuRefresh)
261END_EVENT_TABLE()
262
263CLocalListView::CLocalListView(wxWindow* pParent, CState *pState, CQueueView *pQueue)
264        : CFileListCtrl<CLocalFileData>(pParent, pState, pQueue),
265        CStateEventHandler(pState)
266{
267        wxGetApp().AddStartupProfileRecord(_T("CLocalListView::CLocalListView"));
268        m_pState->RegisterHandler(this, STATECHANGE_LOCAL_DIR);
269        m_pState->RegisterHandler(this, STATECHANGE_APPLYFILTER);
270        m_pState->RegisterHandler(this, STATECHANGE_LOCAL_REFRESH_FILE);
271
272        m_dropTarget = -1;
273
274        const unsigned long widths[4] = { 120, 80, 100, 120 };
275
276        AddColumn(_("Filename"), wxLIST_FORMAT_LEFT, widths[0], true);
277        AddColumn(_("Filesize"), wxLIST_FORMAT_RIGHT, widths[1]);
278        AddColumn(_("Filetype"), wxLIST_FORMAT_LEFT, widths[2]);
279        AddColumn(_("Last modified"), wxLIST_FORMAT_LEFT, widths[3]);
280        LoadColumnSettings(OPTION_LOCALFILELIST_COLUMN_WIDTHS, OPTION_LOCALFILELIST_COLUMN_SHOWN, OPTION_LOCALFILELIST_COLUMN_ORDER);
281
282        InitSort(OPTION_LOCALFILELIST_SORTORDER);
283
284        SetImageList(GetSystemImageList(), wxIMAGE_LIST_SMALL);
285
286#ifdef __WXMSW__
287        m_pVolumeEnumeratorThread = 0;
288#endif
289
290        InitHeaderSortImageList();
291
292        SetDropTarget(new CLocalListViewDropTarget(this));
293
294        EnablePrefixSearch(true);
295}
296
297CLocalListView::~CLocalListView()
298{
299        wxString str = wxString::Format(_T("%d %d"), m_sortDirection, m_sortColumn);
300        COptions::Get()->SetOption(OPTION_LOCALFILELIST_SORTORDER, str);
301
302#ifdef __WXMSW__
303        delete m_pVolumeEnumeratorThread;
304#endif
305}
306
307bool CLocalListView::DisplayDir(CLocalPath const& dirname)
308{
309       
310        CancelLabelEdit();
311
312        wxString focused;
313        std::list<wxString> selectedNames;
314        bool ensureVisible = false;
315        if (m_dir != dirname)
316        {
317                ResetSearchPrefix();
318
319                if (IsComparing())
320                        ExitComparisonMode();
321
322                ClearSelection();
323                focused = m_pState->GetPreviouslyVisitedLocalSubdir();
324                ensureVisible = !focused.empty();
325                if (focused.empty())
326                        focused = _T("..");
327
328                if (GetItemCount())
329                        EnsureVisible(0);
330                m_dir = dirname;
331        }
332        else
333        {
334                // Remember which items were selected
335                selectedNames = RememberSelectedItems(focused);
336        }
337
338        if (m_pFilelistStatusBar)
339                m_pFilelistStatusBar->UnselectAll();
340
341        const int oldItemCount = m_indexMapping.size();
342
343        m_fileData.clear();
344        m_indexMapping.clear();
345
346        m_hasParent = m_dir.HasLogicalParent();
347
348        if (m_hasParent) {
349                CLocalFileData data;
350                data.dir = true;
351                data.name = _T("..");
352                data.size = -1;
353                m_fileData.push_back(data);
354                m_indexMapping.push_back(0);
355        }
356
357#ifdef __WXMSW__
358        if (m_dir.GetPath() == _T("\\")) {
359                DisplayDrives();
360        }
361        else if (m_dir.GetPath().Left(2) == _T("\\\\"))
362        {
363                int pos = m_dir.GetPath().Mid(2).Find('\\');
364                if (pos != -1 && pos + 3 != (int)m_dir.GetPath().Len())
365                        goto regular_dir;
366
367                // UNC path without shares
368                DisplayShares(m_dir.GetPath());
369        }
370        else
371#endif
372        {
373#ifdef __WXMSW__
374regular_dir:
375#endif
376                CFilterManager filter;
377                fz::local_filesys local_filesys;
378
379                if (!local_filesys.begin_find_files(fz::to_native(m_dir.GetPath()), false)) {
380                        SetItemCount(1);
381                        return false;
382                }
383
384                int64_t totalSize{};
385                int unknown_sizes = 0;
386                int totalFileCount = 0;
387                int totalDirCount = 0;
388                int hidden = 0;
389
390                int num = m_fileData.size();
391                CLocalFileData data;
392                bool wasLink;
393                fz::native_string name;
394                while (local_filesys.get_next_file(name, wasLink, data.dir, &data.size, &data.time, &data.attributes)) {
395                        if (name.empty()) {
396                                wxGetApp().DisplayEncodingWarning();
397                                continue;
398                        }
399
400                        data.name = name;
401                        m_fileData.push_back(data);
402                        if (!filter.FilenameFiltered(data.name, m_dir.GetPath(), data.dir, data.size, true, data.attributes, data.time)) {
403                                if (data.dir)
404                                        totalDirCount++;
405                                else {
406                                        if (data.size != -1)
407                                                totalSize += data.size;
408                                        else
409                                                unknown_sizes++;
410                                        totalFileCount++;
411                                }
412                                m_indexMapping.push_back(num);
413                        }
414                        else
415                                hidden++;
416                        num++;
417                }
418
419                if (m_pFilelistStatusBar)
420                        m_pFilelistStatusBar->SetDirectoryContents(totalFileCount, totalDirCount, totalSize, unknown_sizes, hidden);
421        }
422
423        if (m_dropTarget != -1) {
424                CLocalFileData* data = GetData(m_dropTarget);
425                if (!data || !data->dir) {
426                        SetItemState(m_dropTarget, 0, wxLIST_STATE_DROPHILITED);
427                        m_dropTarget = -1;
428                }
429        }
430
431        const int count = m_indexMapping.size();
432        if (oldItemCount != count)
433                SetItemCount(count);
434
435        SortList(-1, -1, false);
436
437        if (IsComparing()) {
438                m_originalIndexMapping.clear();
439                RefreshComparison();
440        }
441
442        ReselectItems(selectedNames, focused, ensureVisible);
443
444        RefreshListOnly();
445
446        return true;
447}
448
449// See comment to OnGetItemText
450int CLocalListView::OnGetItemImage(long item) const
451{
452        CLocalListView *pThis = const_cast<CLocalListView *>(this);
453        CLocalFileData *data = pThis->GetData(item);
454        if (!data)
455                return -1;
456        int &icon = data->icon;
457
458        if (icon == -2)
459        {
460                wxString path = _T("");
461                if (data->name != _T(".."))
462                {
463#ifdef __WXMSW__
464                        if (m_dir.GetPath() == _T("\\"))
465                                path = data->name + _T("\\");
466                        else
467#endif
468                                path = m_dir.GetPath() + data->name;
469                }
470
471                icon = pThis->GetIconIndex(data->dir ? iconType::dir : iconType::file, path);
472        }
473        return icon;
474}
475
476void CLocalListView::OnItemActivated(wxListEvent &event)
477{
478        int count = 0;
479        bool back = false;
480
481        int item = -1;
482        for (;;)
483        {
484                item = GetNextItem(item, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
485                if (item == -1)
486                        break;
487
488                count++;
489
490                if (!item && m_hasParent)
491                        back = true;
492        }
493        if (count > 1)
494        {
495                if (back)
496                {
497                        wxBell();
498                        return;
499                }
500
501                wxCommandEvent cmdEvent;
502                OnMenuUpload(cmdEvent);
503                return;
504        }
505
506        item = event.GetIndex();
507
508        CLocalFileData *data = GetData(item);
509        if (!data)
510                return;
511
512        if (data->dir)
513        {
514                const int action = COptions::Get()->GetOptionVal(OPTION_DOUBLECLICK_ACTION_DIRECTORY);
515                if (action == 3)
516                {
517                        // No action
518                        wxBell();
519                        return;
520                }
521
522                if (!action || data->name == _T(".."))
523                {
524                        // Enter action
525
526                        wxString error;
527                        if (!m_pState->SetLocalDir(data->name, &error))
528                        {
529                                if (!error.empty())
530                                        wxMessageBoxEx(error, _("Failed to change directory"), wxICON_INFORMATION);
531                                else
532                                        wxBell();
533                        }
534                        return;
535                }
536
537                wxCommandEvent evt(0, action == 1 ? XRCID("ID_UPLOAD") : XRCID("ID_ADDTOQUEUE"));
538                OnMenuUpload(evt);
539                return;
540        }
541
542        if (data->comparison_flags == fill)
543        {
544                wxBell();
545                return;
546        }
547
548        const int action = COptions::Get()->GetOptionVal(OPTION_DOUBLECLICK_ACTION_FILE);
549        if (action == 3)
550        {
551                // No action
552                wxBell();
553                return;
554        }
555
556        if (action == 2)
557        {
558                // View / Edit action
559                wxCommandEvent evt;
560                OnMenuEdit(evt);
561                return;
562        }
563
564        const CServer* pServer = m_pState->GetServer();
565        if (!pServer)
566        {
567                wxBell();
568                return;
569        }
570
571        CServerPath path = m_pState->GetRemotePath();
572        if (path.empty())
573        {
574                wxBell();
575                return;
576        }
577
578        const bool queue_only = action == 1;
579
580        m_pQueue->QueueFile(queue_only, false, data->name, wxEmptyString, m_dir, path, *pServer, data->size);
581        m_pQueue->QueueFile_Finish(true);
582}
583
584void CLocalListView::OnMenuEnter(wxCommandEvent &)
585{
586        int item = GetNextItem(-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
587        if (item == -1) {
588                wxBell();
589                return;
590        }
591
592        if (GetNextItem(item, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED) != -1) {
593                wxBell();
594                return;
595        }
596
597        CLocalFileData *data = GetData(item);
598        if (!data || !data->dir) {
599                wxBell();
600                return;
601        }
602
603        wxString error;
604        if (!m_pState->SetLocalDir(data->name, &error)) {
605                if (!error.empty())
606                        wxMessageBoxEx(error, _("Failed to change directory"), wxICON_INFORMATION);
607                else
608                        wxBell();
609        }
610}
611
612#ifdef __WXMSW__
613void CLocalListView::DisplayDrives()
614{
615        int count = m_fileData.size();
616
617        std::list<wxString> drives = CVolumeDescriptionEnumeratorThread::GetDrives();
618        for( std::list<wxString>::const_iterator it = drives.begin(); it != drives.end(); ++it ) {
619                wxString drive = *it;
620                if (drive.Right(1) == _T("\\"))
621                        drive.RemoveLast();
622
623                CLocalFileData data;
624                data.name = drive;
625                data.label = CSparseOptional<wxString>(data.name);
626                data.dir = true;
627                data.size = -1;
628
629                m_fileData.push_back(data);
630                m_indexMapping.push_back(count);
631                count++;
632        }
633
634        if (m_pFilelistStatusBar)
635                m_pFilelistStatusBar->SetDirectoryContents(0, drives.size(), 0, false, 0);
636
637        if (!m_pVolumeEnumeratorThread) {
638                m_pVolumeEnumeratorThread = new CVolumeDescriptionEnumeratorThread(this);
639                if (m_pVolumeEnumeratorThread->Failed()) {
640                        delete m_pVolumeEnumeratorThread;
641                        m_pVolumeEnumeratorThread = 0;
642                }
643        }
644}
645
646void CLocalListView::DisplayShares(wxString computer)
647{
648        // Cast through a union to avoid warning about breaking strict aliasing rule
649        union
650        {
651                SHARE_INFO_1* pShareInfo;
652                LPBYTE pShareInfoBlob;
653        } si;
654
655        DWORD read, total;
656        DWORD resume_handle = 0;
657
658        if (!computer.empty() && computer.Last() == '\\')
659                computer.RemoveLast();
660
661        int j = m_fileData.size();
662        int share_count = 0;
663        int res = 0;
664        do
665        {
666                const wxWX2WCbuf buf = computer.wc_str(wxConvLocal);
667                res = NetShareEnum((wchar_t*)(const wchar_t*)buf, 1, &si.pShareInfoBlob, MAX_PREFERRED_LENGTH, &read, &total, &resume_handle);
668
669                if (res != ERROR_SUCCESS && res != ERROR_MORE_DATA)
670                        break;
671
672                SHARE_INFO_1* p = si.pShareInfo;
673                for (unsigned int i = 0; i < read; i++, p++)
674                {
675                        if (p->shi1_type != STYPE_DISKTREE)
676                                continue;
677
678                        CLocalFileData data;
679                        data.name = p->shi1_netname;
680#ifdef __WXMSW__
681                        data.label = CSparseOptional<wxString>(data.name);
682#endif
683                        data.dir = true;
684                        data.size = -1;
685
686                        m_fileData.push_back(data);
687                        m_indexMapping.push_back(j++);
688
689                        share_count++;
690                }
691
692                NetApiBufferFree(si.pShareInfo);
693        }
694        while (res == ERROR_MORE_DATA);
695
696        if (m_pFilelistStatusBar)
697                m_pFilelistStatusBar->SetDirectoryContents(0, share_count, 0, false, 0);
698}
699
700#endif //__WXMSW__
701
702CLocalFileData* CLocalListView::GetData(unsigned int item)
703{
704        if (!IsItemValid(item))
705                return 0;
706
707        return &m_fileData[m_indexMapping[item]];
708}
709
710bool CLocalListView::IsItemValid(unsigned int item) const
711{
712        if (item >= m_indexMapping.size())
713                return false;
714
715        unsigned int index = m_indexMapping[item];
716        if (index >= m_fileData.size())
717                return false;
718
719        return true;
720}
721
722CFileListCtrl<CLocalFileData>::CSortComparisonObject CLocalListView::GetSortComparisonObject()
723{
724        CFileListCtrlSortBase::DirSortMode dirSortMode = GetDirSortMode();
725        CFileListCtrlSortBase::NameSortMode nameSortMode = GetNameSortMode();
726
727        if (!m_sortDirection)
728        {
729                if (m_sortColumn == 1)
730                        return CFileListCtrl<CLocalFileData>::CSortComparisonObject(new CFileListCtrlSortSize<std::vector<CLocalFileData>, CLocalFileData>(m_fileData, m_fileData, dirSortMode, nameSortMode, this));
731                else if (m_sortColumn == 2)
732                        return CFileListCtrl<CLocalFileData>::CSortComparisonObject(new CFileListCtrlSortType<std::vector<CLocalFileData>, CLocalFileData>(m_fileData, m_fileData, dirSortMode, nameSortMode, this));
733                else if (m_sortColumn == 3)
734                        return CFileListCtrl<CLocalFileData>::CSortComparisonObject(new CFileListCtrlSortTime<std::vector<CLocalFileData>, CLocalFileData>(m_fileData, m_fileData, dirSortMode, nameSortMode, this));
735                else
736                        return CFileListCtrl<CLocalFileData>::CSortComparisonObject(new CFileListCtrlSortName<std::vector<CLocalFileData>, CLocalFileData>(m_fileData, m_fileData, dirSortMode, nameSortMode, this));
737        }
738        else
739        {
740                if (m_sortColumn == 1)
741                        return CFileListCtrl<CLocalFileData>::CSortComparisonObject(new CReverseSort<CFileListCtrlSortSize<std::vector<CLocalFileData>, CLocalFileData>, CLocalFileData>(m_fileData, m_fileData, dirSortMode, nameSortMode, this));
742                else if (m_sortColumn == 2)
743                        return CFileListCtrl<CLocalFileData>::CSortComparisonObject(new CReverseSort<CFileListCtrlSortType<std::vector<CLocalFileData>, CLocalFileData>, CLocalFileData>(m_fileData, m_fileData, dirSortMode, nameSortMode, this));
744                else if (m_sortColumn == 3)
745                        return CFileListCtrl<CLocalFileData>::CSortComparisonObject(new CReverseSort<CFileListCtrlSortTime<std::vector<CLocalFileData>, CLocalFileData>, CLocalFileData>(m_fileData, m_fileData, dirSortMode, nameSortMode, this));
746                else
747                        return CFileListCtrl<CLocalFileData>::CSortComparisonObject(new CReverseSort<CFileListCtrlSortName<std::vector<CLocalFileData>, CLocalFileData>, CLocalFileData>(m_fileData, m_fileData, dirSortMode, nameSortMode, this));
748        }
749}
750
751void CLocalListView::OnContextMenu(wxContextMenuEvent& event)
752{
753        if (GetEditControl())
754        {
755                event.Skip();
756                return;
757        }
758
759        wxMenu* pMenu = wxXmlResource::Get()->LoadMenu(_T("ID_MENU_LOCALFILELIST"));
760        if (!pMenu)
761                return;
762
763        const bool connected = m_pState->IsRemoteConnected();
764        if (!connected)
765        {
766                pMenu->Enable(XRCID("ID_EDIT"), COptions::Get()->GetOptionVal(OPTION_EDIT_TRACK_LOCAL) == 0);
767                pMenu->Enable(XRCID("ID_UPLOAD"), false);
768                pMenu->Enable(XRCID("ID_ADDTOQUEUE"), false);
769        }
770
771        int index = GetNextItem(-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
772        int count = 0;
773        int fillCount = 0;
774        bool selectedDir = false;
775        while (index != -1)
776        {
777                count++;
778                const CLocalFileData* const data = GetData(index);
779                if (!data || (!index && m_hasParent))
780                {
781                        pMenu->Enable(XRCID("ID_OPEN"), false);
782                        pMenu->Enable(XRCID("ID_RENAME"), false);
783                        pMenu->Enable(XRCID("ID_EDIT"), false);
784                }
785                if ((data && data->comparison_flags == fill) || (!index && m_hasParent))
786                        fillCount++;
787                if (data && data->dir)
788                        selectedDir = true;
789                index = GetNextItem(index, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
790        }
791        if (!count || fillCount == count)
792        {
793                pMenu->Delete(XRCID("ID_ENTER"));
794                pMenu->Enable(XRCID("ID_UPLOAD"), false);
795                pMenu->Enable(XRCID("ID_ADDTOQUEUE"), false);
796                pMenu->Enable(XRCID("ID_DELETE"), false);
797                pMenu->Enable(XRCID("ID_RENAME"), false);
798                pMenu->Enable(XRCID("ID_EDIT"), false);
799        }
800        else if (count > 1)
801        {
802                pMenu->Delete(XRCID("ID_ENTER"));
803                pMenu->Enable(XRCID("ID_RENAME"), false);
804        }
805        else
806        {
807                // Exactly one item selected
808                if (!selectedDir)
809                        pMenu->Delete(XRCID("ID_ENTER"));
810        }
811        if (selectedDir)
812                pMenu->Enable(XRCID("ID_EDIT"), false);
813
814        PopupMenu(pMenu);
815        delete pMenu;
816}
817
818void CLocalListView::OnMenuUpload(wxCommandEvent& event)
819{
820        const CServer* pServer = m_pState->GetServer();
821        if (!pServer) {
822                wxBell();
823                return;
824        }
825
826        bool added = false;
827
828        bool queue_only = event.GetId() == XRCID("ID_ADDTOQUEUE");
829
830        long item = -1;
831        for (;;) {
832                item = GetNextItem(item, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
833                if (!item && m_hasParent)
834                        continue;
835                if (item == -1)
836                        break;
837
838                const CLocalFileData *data = GetData(item);
839                if (!data)
840                        break;
841
842                if (data->comparison_flags == fill)
843                        continue;
844
845                CServerPath path = m_pState->GetRemotePath();
846                if (path.empty()) {
847                        wxBell();
848                        break;
849                }
850
851                if (data->dir) {
852                        if (!path.ChangePath(data->name)) {
853                                continue;
854                        }
855
856                        CLocalPath localPath(m_dir);
857                        localPath.AddSegment(data->name);
858                        m_pQueue->QueueFolder(event.GetId() == XRCID("ID_ADDTOQUEUE"), false, localPath, path, *pServer);
859                }
860                else {
861                        m_pQueue->QueueFile(queue_only, false, data->name, wxEmptyString, m_dir, path, *pServer, data->size);
862                        added = true;
863                }
864        }
865        if (added)
866                m_pQueue->QueueFile_Finish(!queue_only);
867}
868
869// Create a new Directory
870void CLocalListView::OnMenuMkdir(wxCommandEvent&)
871{
872        wxString newdir = MenuMkdir();
873        if (!newdir.empty()) {
874                m_pState->RefreshLocal();
875        }
876}
877
878// Create a new Directory and enter the new Directory
879void CLocalListView::OnMenuMkdirChgDir(wxCommandEvent&)
880{
881        wxString newdir = MenuMkdir();
882        if (newdir.empty()) {
883                return;
884        }
885
886        // OnMenuEnter
887        wxString error;
888        if (!m_pState->SetLocalDir(newdir, &error))
889        {
890                if (!error.empty())
891                        wxMessageBoxEx(error, _("Failed to change directory"), wxICON_INFORMATION);
892                else
893                        wxBell();
894        }
895}
896
897// Helper-Function to create a new Directory
898// Returns the name of the new directory
899wxString CLocalListView::MenuMkdir()
900{
901        CInputDialog dlg;
902        if (!dlg.Create(this, _("Create directory"), _("Please enter the name of the directory which should be created:")))
903                return wxString();
904
905        if (dlg.ShowModal() != wxID_OK)
906                return wxString();
907
908        if (dlg.GetValue().empty())
909        {
910                wxBell();
911                return wxString();
912        }
913
914        wxFileName fn(dlg.GetValue(), _T(""));
915        fn.Normalize(wxPATH_NORM_ALL, m_dir.GetPath());
916
917        bool res;
918
919        {
920                wxLogNull log;
921                res = fn.Mkdir(fn.GetPath(), 0777, wxPATH_MKDIR_FULL);
922        }
923
924        if (!res) {
925                wxBell();
926                return wxString();
927        }
928
929        // Return name of the New Directory
930        return fn.GetPath();
931}
932
933void CLocalListView::OnMenuDelete(wxCommandEvent&)
934{
935        std::list<fz::native_string> pathsToDelete;
936        long item = -1;
937        while ((item = GetNextItem(item, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED)) != -1) {
938                if (!item && m_hasParent)
939                        continue;
940
941                CLocalFileData *data = GetData(item);
942                if (!data)
943                        continue;
944
945                if (data->comparison_flags == fill)
946                        continue;
947
948                pathsToDelete.push_back(fz::to_native(m_dir.GetPath() + data->name));
949        }
950        gui_recursive_remove rmd(this);
951        rmd.remove(pathsToDelete);
952
953        m_pState->SetLocalDir(m_dir);
954}
955
956void CLocalListView::OnMenuRename(wxCommandEvent&)
957{
958        if (GetEditControl()) {
959                GetEditControl()->SetFocus();
960                return;
961        }
962
963        int item = GetNextItem(-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
964        if (item < 0 || (!item && m_hasParent)) {
965                wxBell();
966                return;
967        }
968
969        if (GetNextItem(item, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED) != -1) {
970                wxBell();
971                return;
972        }
973
974        CLocalFileData *data = GetData(item);
975        if (!data || data->comparison_flags == fill) {
976                wxBell();
977                return;
978        }
979
980        EditLabel(item);
981}
982
983void CLocalListView::OnKeyDown(wxKeyEvent& event)
984{
985#ifdef __WXMAC__
986#define CursorModifierKey wxMOD_CMD
987#else
988#define CursorModifierKey wxMOD_ALT
989#endif
990
991        const int code = event.GetKeyCode();
992        if (code == WXK_DELETE || code == WXK_NUMPAD_DELETE) {
993                if (GetNextItem(-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED) == -1) {
994                        wxBell();
995                        return;
996                }
997
998                wxCommandEvent tmp;
999                OnMenuDelete(tmp);
1000        }
1001        else if (code == WXK_F2) {
1002                wxCommandEvent tmp;
1003                OnMenuRename(tmp);
1004        }
1005        else if (code == WXK_RIGHT && event.GetModifiers() == CursorModifierKey) {
1006                wxListEvent evt;
1007                evt.m_itemIndex = GetNextItem(-1, wxLIST_NEXT_ALL, wxLIST_STATE_FOCUSED);
1008                OnItemActivated(evt);
1009        }
1010        else if (code == WXK_DOWN && event.GetModifiers() == CursorModifierKey) {
1011                wxCommandEvent cmdEvent;
1012                OnMenuUpload(cmdEvent);
1013        }
1014        else if (code == 'N' && event.GetModifiers() == (wxMOD_CONTROL | wxMOD_SHIFT)) {
1015                wxCommandEvent cmdEvent;
1016                OnMenuMkdir(cmdEvent);
1017        }
1018        else
1019                event.Skip();
1020}
1021
1022bool CLocalListView::OnBeginRename(const wxListEvent& event)
1023{
1024        if (!m_pState->GetLocalDir().IsWriteable())
1025                return false;
1026
1027        if (event.GetIndex() == 0 && m_hasParent)
1028                return false;
1029
1030        const CLocalFileData * const data = GetData(event.GetIndex());
1031        if (!data || data->comparison_flags == fill)
1032                return false;
1033
1034        return true;
1035}
1036
1037bool CLocalListView::OnAcceptRename(const wxListEvent& event)
1038{
1039        const int index = event.GetIndex();
1040        if (!index && m_hasParent)
1041                return false;
1042
1043        if (event.GetLabel().empty())
1044                return false;
1045
1046        if (!m_pState->GetLocalDir().IsWriteable())
1047                return false;
1048
1049        CLocalFileData *const data = GetData(event.GetIndex());
1050        if (!data || data->comparison_flags == fill)
1051                return false;
1052
1053        wxString newname = event.GetLabel();
1054#ifdef __WXMSW__
1055        newname = newname.Left(255);
1056#endif
1057
1058        if (newname == data->name)
1059                return false;
1060
1061        if (!RenameFile(this, m_dir.GetPath(), data->name, newname))
1062                return false;
1063
1064        data->name = newname;
1065#ifdef __WXMSW__
1066        data->label.clear();
1067#endif
1068        m_pState->RefreshLocal();
1069
1070        return true;
1071}
1072
1073void CLocalListView::ApplyCurrentFilter()
1074{
1075        CFilterManager filter;
1076
1077        if (!filter.HasSameLocalAndRemoteFilters() && IsComparing())
1078                ExitComparisonMode();
1079
1080        unsigned int min = m_hasParent ? 1 : 0;
1081        if (m_fileData.size() <= min)
1082                return;
1083
1084        wxString focused;
1085        const std::list<wxString>& selectedNames = RememberSelectedItems(focused);
1086
1087        if (m_pFilelistStatusBar)
1088                m_pFilelistStatusBar->UnselectAll();
1089
1090        int64_t totalSize{};
1091        int unknown_sizes = 0;
1092        int totalFileCount = 0;
1093        int totalDirCount = 0;
1094        int hidden = 0;
1095
1096        m_indexMapping.clear();
1097        if (m_hasParent)
1098                m_indexMapping.push_back(0);
1099        for (unsigned int i = min; i < m_fileData.size(); ++i) {
1100                const CLocalFileData& data = m_fileData[i];
1101                if (data.comparison_flags == fill)
1102                        continue;
1103                if (filter.FilenameFiltered(data.name, m_dir.GetPath(), data.dir, data.size, true, data.attributes, data.time)) {
1104                        ++hidden;
1105                        continue;
1106                }
1107
1108                if (data.dir)
1109                        ++totalDirCount;
1110                else {
1111                        if (data.size != -1)
1112                                totalSize += data.size;
1113                        else
1114                                ++unknown_sizes;
1115                        ++totalFileCount;
1116                }
1117
1118                m_indexMapping.push_back(i);
1119        }
1120        SetItemCount(m_indexMapping.size());
1121
1122        if (m_pFilelistStatusBar)
1123                m_pFilelistStatusBar->SetDirectoryContents(totalFileCount, totalDirCount, totalSize, unknown_sizes, hidden);
1124
1125        SortList(-1, -1, false);
1126
1127        if (IsComparing()) {
1128                m_originalIndexMapping.clear();
1129                RefreshComparison();
1130        }
1131
1132        ReselectItems(selectedNames, focused);
1133
1134        if (!IsComparing())
1135                RefreshListOnly();
1136}
1137
1138std::list<wxString> CLocalListView::RememberSelectedItems(wxString& focused)
1139{
1140        std::list<wxString> selectedNames;
1141        // Remember which items were selected
1142#ifndef __WXMSW__
1143        // GetNextItem is O(n) if nothing is selected, GetSelectedItemCount() is O(1)
1144        if (GetSelectedItemCount())
1145#endif
1146        {
1147                int item = -1;
1148                for (;;)
1149                {
1150                        item = GetNextItem(item, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
1151                        if (item == -1)
1152                                break;
1153                        const CLocalFileData &data = m_fileData[m_indexMapping[item]];
1154                        if (data.comparison_flags != fill)
1155                        {
1156                                if (data.dir)
1157                                        selectedNames.push_back(_T("d") + data.name);
1158                                else
1159                                        selectedNames.push_back(_T("-") + data.name);
1160                        }
1161                        SetSelection(item, false);
1162                }
1163        }
1164
1165        int item = GetNextItem(-1, wxLIST_NEXT_ALL, wxLIST_STATE_FOCUSED);
1166        if (item != -1)
1167        {
1168                const CLocalFileData &data = m_fileData[m_indexMapping[item]];
1169                if (data.comparison_flags != fill)
1170                        focused = data.name;
1171
1172                SetItemState(item, 0, wxLIST_STATE_FOCUSED);
1173        }
1174
1175        return selectedNames;
1176}
1177
1178void CLocalListView::ReselectItems(const std::list<wxString>& selectedNames, wxString focused, bool ensureVisible)
1179{
1180        // Reselect previous items if neccessary.
1181        // Sorting direction did not change. We just have to scan through items once
1182
1183        if (selectedNames.empty())
1184        {
1185                if (focused.empty())
1186                        return;
1187                for (unsigned int i = 0; i < m_indexMapping.size(); i++)
1188                {
1189                        const CLocalFileData &data = m_fileData[m_indexMapping[i]];
1190                        if (data.name == focused)
1191                        {
1192                                SetItemState(i, wxLIST_STATE_FOCUSED, wxLIST_STATE_FOCUSED);
1193                                if (ensureVisible)
1194                                        EnsureVisible(i);
1195                                return;
1196                        }
1197                }
1198                return;
1199        }
1200
1201        int firstSelected = -1;
1202
1203        int i = -1;
1204        for (std::list<wxString>::const_iterator iter = selectedNames.begin(); iter != selectedNames.end(); ++iter)
1205        {
1206                while (++i < (int)m_indexMapping.size())
1207                {
1208                        const CLocalFileData &data = m_fileData[m_indexMapping[i]];
1209                        if (data.name == focused)
1210                        {
1211                                SetItemState(i, wxLIST_STATE_FOCUSED, wxLIST_STATE_FOCUSED);
1212                                if (ensureVisible)
1213                                        EnsureVisible(i);
1214                                focused = _T("");
1215                        }
1216                        if (data.dir && *iter == (_T("d") + data.name))
1217                        {
1218                                if (firstSelected == -1)
1219                                        firstSelected = i;
1220                                if (m_pFilelistStatusBar)
1221                                        m_pFilelistStatusBar->SelectDirectory();
1222                                SetSelection(i, true);
1223                                break;
1224                        }
1225                        else if (*iter == (_T("-") + data.name))
1226                        {
1227                                if (firstSelected == -1)
1228                                        firstSelected = i;
1229                                if (m_pFilelistStatusBar)
1230                                        m_pFilelistStatusBar->SelectFile(data.size);
1231                                SetSelection(i, true);
1232                                break;
1233                        }
1234                }
1235                if (i == (int)m_indexMapping.size())
1236                        break;
1237        }
1238        if (!focused.empty())
1239        {
1240                if (firstSelected != -1)
1241                        SetItemState(firstSelected, wxLIST_STATE_FOCUSED, wxLIST_STATE_FOCUSED);
1242                else
1243                        SetItemState(0, wxLIST_STATE_FOCUSED, wxLIST_STATE_FOCUSED);
1244        }
1245}
1246
1247void CLocalListView::OnStateChange(CState*, enum t_statechange_notifications notification, const wxString& data, const void*)
1248{
1249        if (notification == STATECHANGE_LOCAL_DIR)
1250                DisplayDir(m_pState->GetLocalDir());
1251        else if (notification == STATECHANGE_APPLYFILTER)
1252                ApplyCurrentFilter();
1253        else
1254        {
1255                wxASSERT(notification == STATECHANGE_LOCAL_REFRESH_FILE);
1256                RefreshFile(data);
1257        }
1258}
1259
1260void CLocalListView::OnBeginDrag(wxListEvent&)
1261{
1262        long item = -1;
1263        for (;;) {
1264                item = GetNextItem(item, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
1265                if (item == -1)
1266                        break;
1267
1268                if (!item && m_hasParent)
1269                        return;
1270        }
1271
1272        wxFileDataObject obj;
1273
1274        CDragDropManager* pDragDropManager = CDragDropManager::Init();
1275        pDragDropManager->pDragSource = this;
1276        pDragDropManager->localParent = m_dir;
1277
1278        item = -1;
1279        for (;;) {
1280                item = GetNextItem(item, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
1281                if (item == -1)
1282                        break;
1283
1284                CLocalFileData *data = GetData(item);
1285                if (!data)
1286                        continue;
1287
1288                if (data->comparison_flags == fill)
1289                        continue;
1290
1291                wxString const name = m_dir.GetPath() + data->name;
1292                pDragDropManager->m_localFiles.push_back(name);
1293                obj.AddFile(name);
1294        }
1295
1296        if (obj.GetFilenames().empty()) {
1297                pDragDropManager->Release();
1298                return;
1299        }
1300
1301        CLabelEditBlocker b(*this);
1302
1303        wxDropSource source(this);
1304        source.SetData(obj);
1305        int res = source.DoDragDrop(wxDrag_AllowMove);
1306
1307        bool handled_internally = pDragDropManager->pDropTarget != 0;
1308
1309        pDragDropManager->Release();
1310
1311        if (!handled_internally && (res == wxDragCopy || res == wxDragMove)) {
1312                // We only need to refresh local side if the operation got handled
1313                // externally, the internal handlers do this for us already
1314                m_pState->RefreshLocal();
1315        }
1316}
1317
1318void CLocalListView::RefreshFile(const wxString& file)
1319{
1320        CLocalFileData data;
1321
1322        bool wasLink;
1323        enum fz::local_filesys::type type = fz::local_filesys::get_file_info(fz::to_native(m_dir.GetPath() + file), wasLink, &data.size, &data.time, &data.attributes);
1324        if (type == fz::local_filesys::unknown)
1325                return;
1326
1327        data.name = file;
1328        data.dir = type == fz::local_filesys::dir;
1329
1330        CFilterManager filter;
1331        if (filter.FilenameFiltered(data.name, m_dir.GetPath(), data.dir, data.size, true, data.attributes, data.time))
1332                return;
1333
1334        CancelLabelEdit();
1335
1336        // Look if file data already exists
1337        unsigned int i = 0;
1338        for (auto iter = m_fileData.begin(); iter != m_fileData.end(); ++iter, ++i) {
1339                const CLocalFileData& oldData = *iter;
1340                if (oldData.name != file)
1341                        continue;
1342
1343                // Update file list status bar
1344                if (m_pFilelistStatusBar) {
1345#ifndef __WXMSW__
1346                        // GetNextItem is O(n) if nothing is selected, GetSelectedItemCount() is O(1)
1347                        if (GetSelectedItemCount())
1348#endif
1349                        {
1350                                int item = -1;
1351                                for (;;) {
1352                                        item = GetNextItem(item, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
1353                                        if (item == -1)
1354                                                break;
1355                                        if (m_indexMapping[item] != i)
1356                                                continue;
1357
1358                                        if (oldData.dir)
1359                                                m_pFilelistStatusBar->UnselectDirectory();
1360                                        else
1361                                                m_pFilelistStatusBar->UnselectFile(oldData.size);
1362                                        if (data.dir)
1363                                                m_pFilelistStatusBar->SelectDirectory();
1364                                        else
1365                                                m_pFilelistStatusBar->SelectFile(data.size);
1366                                        break;
1367                                }
1368                        }
1369
1370                        if (oldData.dir)
1371                                m_pFilelistStatusBar->RemoveDirectory();
1372                        else
1373                                m_pFilelistStatusBar->RemoveFile(oldData.size);
1374                        if (data.dir)
1375                                m_pFilelistStatusBar->AddDirectory();
1376                        else
1377                                m_pFilelistStatusBar->AddFile(data.size);
1378                }
1379
1380                // Update the data
1381                data.fileType = oldData.fileType;
1382
1383                *iter = data;
1384                if (IsComparing()) {
1385                        // Sort order doesn't change
1386                        RefreshComparison();
1387                }
1388                else {
1389                        if (m_sortColumn)
1390                                SortList();
1391                        RefreshListOnly(false);
1392                }
1393                return;
1394        }
1395
1396        if (data.dir)
1397                m_pFilelistStatusBar->AddDirectory();
1398        else
1399                m_pFilelistStatusBar->AddFile(data.size);
1400
1401        wxString focused;
1402        std::list<wxString> selectedNames;
1403        if (IsComparing()) {
1404                selectedNames = RememberSelectedItems(focused);
1405                if (!m_originalIndexMapping.empty()) {
1406                        m_indexMapping.clear();
1407                        m_originalIndexMapping.swap(m_indexMapping);
1408                }
1409        }
1410
1411        // Insert new entry
1412        int index = m_fileData.size();
1413        m_fileData.push_back(data);
1414
1415        // Find correct position in index mapping
1416        std::vector<unsigned int>::iterator start = m_indexMapping.begin();
1417        if (m_hasParent)
1418                ++start;
1419        CFileListCtrl<CLocalFileData>::CSortComparisonObject compare = GetSortComparisonObject();
1420        std::vector<unsigned int>::iterator insertPos = std::lower_bound(start, m_indexMapping.end(), index, compare);
1421        compare.Destroy();
1422
1423        const int item = insertPos - m_indexMapping.begin();
1424        m_indexMapping.insert(insertPos, index);
1425
1426        if (!IsComparing()) {
1427                SetItemCount(m_indexMapping.size());
1428
1429                // Move selections
1430                int prevState = 0;
1431                for (unsigned int j = item; j < m_indexMapping.size(); ++j) {
1432                        int state = GetItemState(j, wxLIST_STATE_SELECTED | wxLIST_STATE_FOCUSED);
1433                        if (state != prevState) {
1434                                SetItemState(j, prevState, wxLIST_STATE_FOCUSED);
1435                                SetSelection(j, (prevState & wxLIST_STATE_SELECTED) != 0);
1436                                prevState = state;
1437                        }
1438                }
1439                RefreshListOnly();
1440        }
1441        else {
1442                RefreshComparison();
1443                if (m_pFilelistStatusBar)
1444                        m_pFilelistStatusBar->UnselectAll();
1445                ReselectItems(selectedNames, focused);
1446        }
1447}
1448
1449wxListItemAttr* CLocalListView::OnGetItemAttr(long item) const
1450{
1451        CLocalListView *pThis = const_cast<CLocalListView *>(this);
1452        const CLocalFileData* const data = pThis->GetData((unsigned int)item);
1453
1454        if (!data)
1455                return 0;
1456
1457#ifndef __WXMSW__
1458        if (item == m_dropTarget)
1459                return &pThis->m_dropHighlightAttribute;
1460#endif
1461
1462        switch (data->comparison_flags)
1463        {
1464        case different:
1465                return &pThis->m_comparisonBackgrounds[0];
1466        case lonely:
1467                return &pThis->m_comparisonBackgrounds[1];
1468        case newer:
1469                return &pThis->m_comparisonBackgrounds[2];
1470        default:
1471                return 0;
1472        }
1473}
1474
1475void CLocalListView::StartComparison()
1476{
1477        if (m_sortDirection || m_sortColumn)
1478        {
1479                wxASSERT(m_originalIndexMapping.empty());
1480                SortList(0, 0);
1481        }
1482
1483        ComparisonRememberSelections();
1484
1485        if (m_originalIndexMapping.empty())
1486                m_originalIndexMapping.swap(m_indexMapping);
1487        else
1488                m_indexMapping.clear();
1489
1490        m_comparisonIndex = -1;
1491
1492        const CLocalFileData& last = m_fileData[m_fileData.size() - 1];
1493        if (last.comparison_flags != fill)
1494        {
1495                CLocalFileData data;
1496                data.dir = false;
1497                data.icon = -1;
1498                data.size = -1;
1499                data.comparison_flags = fill;
1500                m_fileData.push_back(data);
1501        }
1502}
1503
1504bool CLocalListView::get_next_file(wxString& name, bool& dir, int64_t& size, fz::datetime& date)
1505{
1506        if (++m_comparisonIndex >= (int)m_originalIndexMapping.size())
1507                return false;
1508
1509        const unsigned int index = m_originalIndexMapping[m_comparisonIndex];
1510        if (index >= m_fileData.size())
1511                return false;
1512
1513        const CLocalFileData& data = m_fileData[index];
1514
1515        name = data.name;
1516        dir = data.dir;
1517        size = data.size;
1518        date = data.time;
1519
1520        return true;
1521}
1522
1523void CLocalListView::FinishComparison()
1524{
1525        SetItemCount(m_indexMapping.size());
1526
1527        ComparisonRestoreSelections();
1528
1529        RefreshListOnly();
1530
1531        CComparableListing* pOther = GetOther();
1532        if (!pOther)
1533                return;
1534
1535        pOther->ScrollTopItem(GetTopItem());
1536}
1537
1538bool CLocalListView::CanStartComparison()
1539{
1540        return true;
1541}
1542
1543wxString CLocalListView::GetItemText(int item, unsigned int column)
1544{
1545        CLocalFileData *data = GetData(item);
1546        if (!data)
1547                return wxString();
1548
1549        if (!column) {
1550#ifdef __WXMSW__
1551                return data->label ? *data->label : data->name;
1552#else
1553                return data->name;
1554#endif
1555        }
1556        else if (column == 1) {
1557                if (data->size < 0)
1558                        return wxString();
1559                else
1560                        return CSizeFormat::Format(data->size);
1561        }
1562        else if (column == 2) {
1563                if (!item && m_hasParent)
1564                        return wxString();
1565
1566                if (data->comparison_flags == fill)
1567                        return wxString();
1568
1569                if (data->fileType.empty())
1570                        data->fileType = GetType(data->name, data->dir, m_dir.GetPath());
1571
1572                return data->fileType;
1573        }
1574        else if (column == 3) {
1575                return CTimeFormat::Format(data->time);
1576        }
1577        return wxString();
1578}
1579
1580void CLocalListView::OnMenuEdit(wxCommandEvent&)
1581{
1582        CServer server;
1583        CServerPath path;
1584
1585        if (!m_pState->GetServer()) {
1586                if (COptions::Get()->GetOptionVal(OPTION_EDIT_TRACK_LOCAL)) {
1587                        wxMessageBoxEx(_("Cannot edit file, not connected to any server."), _("Editing failed"), wxICON_EXCLAMATION);
1588                        return;
1589                }
1590        }
1591        else {
1592                server = *m_pState->GetServer();
1593
1594                path = m_pState->GetRemotePath();
1595                if (path.empty()) {
1596                        wxMessageBoxEx(_("Cannot edit file, remote path unknown."), _("Editing failed"), wxICON_EXCLAMATION);
1597                        return;
1598                }
1599        }
1600
1601        std::vector<CEditHandler::FileData> selected_item;
1602
1603        long item = -1;
1604        while ((item = GetNextItem(item, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED)) != -1) {
1605                if (!item && m_hasParent) {
1606                        wxBell();
1607                        return;
1608                }
1609
1610                const CLocalFileData *data = GetData(item);
1611                if (!data)
1612                        continue;
1613
1614                if (data->dir) {
1615                        wxBell();
1616                        return;
1617                }
1618
1619                if (data->comparison_flags == fill)
1620                        continue;
1621
1622                selected_item.push_back({m_dir.GetPath() + data->name, data->size});
1623        }
1624
1625        CEditHandler* pEditHandler = CEditHandler::Get();
1626        pEditHandler->Edit(CEditHandler::local, selected_item, path, server, this);
1627}
1628
1629void CLocalListView::OnMenuOpen(wxCommandEvent&)
1630{
1631        long item = GetNextItem(-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
1632        if (item == -1) {
1633                OpenInFileManager(m_dir.GetPath());
1634                return;
1635        }
1636
1637        std::list<CLocalFileData> selected_item_list;
1638
1639        item = -1;
1640        while ((item = GetNextItem(item, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED)) != -1) {
1641                if (!item && m_hasParent) {
1642                        wxBell();
1643                        return;
1644                }
1645
1646                const CLocalFileData *data = GetData(item);
1647                if (!data)
1648                        continue;
1649
1650                if (data->comparison_flags == fill)
1651                        continue;
1652
1653                selected_item_list.push_back(*data);
1654        }
1655
1656        CEditHandler* pEditHandler = CEditHandler::Get();
1657        if (!pEditHandler) {
1658                wxBell();
1659                return;
1660        }
1661
1662        if (selected_item_list.empty()) {
1663                wxBell();
1664                return;
1665        }
1666
1667        if (selected_item_list.size() > 10) {
1668                CConditionalDialog dlg(this, CConditionalDialog::many_selected_for_edit, CConditionalDialog::yesno);
1669                dlg.SetTitle(_("Confirmation needed"));
1670                dlg.AddText(_("You have selected more than 10 files or directories to open, do you really want to continue?"));
1671
1672                if (!dlg.Run())
1673                        return;
1674        }
1675
1676        for (auto const& data : selected_item_list) {
1677                if (data.dir) {
1678                        CLocalPath path(m_dir);
1679                        if (!path.ChangePath(data.name)) {
1680                                wxBell();
1681                                continue;
1682                        }
1683
1684                        OpenInFileManager(path.GetPath());
1685                        continue;
1686                }
1687
1688                wxFileName fn(m_dir.GetPath(), data.name);
1689                if (wxLaunchDefaultApplication(fn.GetFullPath(), 0)) {
1690                        continue;
1691                }
1692                bool program_exists = false;
1693                wxString cmd = GetSystemOpenCommand(fn.GetFullPath(), program_exists);
1694                if (cmd.empty()) {
1695                        int pos = data.name.Find('.') == -1;
1696                        if (pos == -1 || (pos == 0 && data.name.Mid(1).Find('.') == -1))
1697                                cmd = pEditHandler->GetOpenCommand(fn.GetFullPath(), program_exists);
1698                }
1699                if (cmd.empty()) {
1700                        wxMessageBoxEx(wxString::Format(_("The file '%s' could not be opened:\nNo program has been associated on your system with this file type."), fn.GetFullPath()), _("Opening failed"), wxICON_EXCLAMATION);
1701                        continue;
1702                }
1703                if (!program_exists) {
1704                        wxString msg = wxString::Format(_("The file '%s' cannot be opened:\nThe associated program (%s) could not be found.\nPlease check your filetype associations."), fn.GetFullPath(), cmd);
1705                        wxMessageBoxEx(msg, _("Cannot edit file"), wxICON_EXCLAMATION);
1706                        continue;
1707                }
1708
1709                if (wxExecute(cmd))
1710                        continue;
1711
1712                wxMessageBoxEx(wxString::Format(_("The file '%s' could not be opened:\nThe associated command failed"), fn.GetFullPath()), _("Opening failed"), wxICON_EXCLAMATION);
1713        }
1714}
1715
1716bool CLocalListView::ItemIsDir(int index) const
1717{
1718        return m_fileData[index].dir;
1719}
1720
1721int64_t CLocalListView::ItemGetSize(int index) const
1722{
1723        return m_fileData[index].size;
1724}
1725
1726#ifdef __WXMSW__
1727
1728void CLocalListView::OnVolumesEnumerated(wxCommandEvent& event)
1729{
1730        if (!m_pVolumeEnumeratorThread)
1731                return;
1732
1733        std::list<CVolumeDescriptionEnumeratorThread::t_VolumeInfo> volumeInfo;
1734        volumeInfo = m_pVolumeEnumeratorThread->GetVolumes();
1735
1736        if (event.GetEventType() == fzEVT_VOLUMESENUMERATED) {
1737                delete m_pVolumeEnumeratorThread;
1738                m_pVolumeEnumeratorThread = 0;
1739        }
1740
1741        if (m_dir.GetPath() != _T("\\"))
1742                return;
1743
1744        for (std::list<CVolumeDescriptionEnumeratorThread::t_VolumeInfo>::const_iterator iter = volumeInfo.begin(); iter != volumeInfo.end(); ++iter) {
1745                wxString drive = iter->volume;
1746
1747                unsigned int item, index;
1748                for (item = 1; item < m_indexMapping.size(); ++item) {
1749                        index = m_indexMapping[item];
1750                        if (m_fileData[index].name == drive || m_fileData[index].name.Left(drive.Len() + 1) == drive + _T(" "))
1751                                break;
1752                }
1753                if (item >= m_indexMapping.size())
1754                        continue;
1755
1756                m_fileData[index].label = CSparseOptional<wxString>(drive + _T(" (") + iter->volumeName + _T(")"));
1757
1758                RefreshItem(item);
1759        }
1760}
1761
1762#endif
1763
1764void CLocalListView::OnMenuRefresh(wxCommandEvent&)
1765{
1766        m_pState->RefreshLocal();
1767}
1768
1769void CLocalListView::OnNavigationEvent(bool forward)
1770{
1771        if (!forward)
1772        {
1773                if (!m_hasParent)
1774                {
1775                        wxBell();
1776                        return;
1777                }
1778
1779                wxString error;
1780                if (!m_pState->SetLocalDir(_T(".."), &error))
1781                {
1782                        if (!error.empty())
1783                                wxMessageBoxEx(error, _("Failed to change directory"), wxICON_INFORMATION);
1784                        else
1785                                wxBell();
1786                }
1787        }
1788}
Note: See TracBrowser for help on using the repository browser.