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

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

First release to xenial

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