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

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

Update new version: 3.15.02

File size: 27.2 KB
Line 
1#include <filezilla.h>
2
3#define FILELISTCTRL_INCLUDE_TEMPLATE_DEFINITION
4
5#include "search.h"
6#include "commandqueue.h"
7#include "filelistctrl.h"
8#include "ipcmutex.h"
9#include "Options.h"
10#include "queue.h"
11#include "recursive_operation.h"
12#include "sizeformatting.h"
13#include "timeformatting.h"
14#include "window_state_manager.h"
15#include "xrc_helper.h"
16
17class CSearchFileData : public CGenericFileData, public CDirentry
18{
19public:
20        CServerPath path;
21};
22
23template<>
24inline int DoCmpName(CSearchFileData const& data1, CSearchFileData const& data2, CFileListCtrlSortBase::NameSortMode const nameSortMode)
25{
26        int res;
27        switch (nameSortMode)
28        {
29        case CFileListCtrlSortBase::namesort_casesensitive:
30                res = CFileListCtrlSortBase::CmpCase(data1.name, data2.name);
31                break;
32        default:
33        case CFileListCtrlSortBase::namesort_caseinsensitive:
34                res = CFileListCtrlSortBase::CmpNoCase(data1.name, data2.name);
35                break;
36        case CFileListCtrlSortBase::namesort_natural:
37                res = CFileListCtrlSortBase::CmpNatural(data1.name, data2.name);
38                break;
39        }
40
41        if (!res) {
42                if (data1.path < data2.path)
43                        res = -1;
44                else if (data2.path < data1.path)
45                        res = 1;
46        }
47
48        return res;
49}
50
51class CSearchDialogFileList : public CFileListCtrl<CSearchFileData>
52{
53        friend class CSearchDialog;
54        friend class CSearchSortType;
55public:
56        CSearchDialogFileList(CSearchDialog* pParent, CState* pState, CQueueView* pQueue);
57
58protected:
59        virtual bool ItemIsDir(int index) const;
60
61        virtual int64_t ItemGetSize(int index) const;
62
63        CFileListCtrl<CSearchFileData>::CSortComparisonObject GetSortComparisonObject();
64
65        CSearchDialog *m_searchDialog;
66
67        virtual wxString GetItemText(int item, unsigned int column);
68        virtual int OnGetItemImage(long item) const;
69
70#ifdef __WXMSW__
71        virtual int GetOverlayIndex(int item);
72#endif
73
74private:
75        virtual bool CanStartComparison() { return false; }
76        virtual void StartComparison() {}
77        virtual bool get_next_file(wxString&, bool &, int64_t&, fz::datetime&) { return false; }
78        virtual void CompareAddFile(CComparableListing::t_fileEntryFlags) {}
79        virtual void FinishComparison() {}
80        virtual void ScrollTopItem(int) {}
81        virtual void OnExitComparisonMode() {}
82
83        int m_dirIcon;
84};
85
86// Search dialog file list
87// -----------------------
88
89// Defined in RemoteListView.cpp
90extern wxString StripVMSRevision(const wxString& name);
91
92CSearchDialogFileList::CSearchDialogFileList(CSearchDialog* pParent, CState* pState, CQueueView* pQueue)
93        : CFileListCtrl<CSearchFileData>(pParent, pState, pQueue, true),
94        m_searchDialog(pParent)
95{
96        m_hasParent = false;
97
98        SetImageList(GetSystemImageList(), wxIMAGE_LIST_SMALL);
99
100        m_dirIcon = GetIconIndex(iconType::dir);
101
102        InitSort(OPTION_SEARCH_SORTORDER);
103
104        InitHeaderSortImageList();
105
106        const unsigned long widths[7] = { 130, 130, 75, 80, 120, 80, 80 };
107
108        AddColumn(_("Filename"), wxLIST_FORMAT_LEFT, widths[0]);
109        AddColumn(_("Path"), wxLIST_FORMAT_LEFT, widths[1]);
110        AddColumn(_("Filesize"), wxLIST_FORMAT_RIGHT, widths[2]);
111        AddColumn(_("Filetype"), wxLIST_FORMAT_LEFT, widths[3]);
112        AddColumn(_("Last modified"), wxLIST_FORMAT_LEFT, widths[4]);
113        AddColumn(_("Permissions"), wxLIST_FORMAT_LEFT, widths[5]);
114        AddColumn(_("Owner/Group"), wxLIST_FORMAT_LEFT, widths[6]);
115        LoadColumnSettings(OPTION_SEARCH_COLUMN_WIDTHS, OPTION_SEARCH_COLUMN_SHOWN, OPTION_SEARCH_COLUMN_ORDER);
116}
117
118bool CSearchDialogFileList::ItemIsDir(int index) const
119{
120        return m_fileData[index].is_dir();
121}
122
123int64_t CSearchDialogFileList::ItemGetSize(int index) const
124{
125        return m_fileData[index].size;
126}
127
128CFileListCtrl<CSearchFileData>::CSortComparisonObject CSearchDialogFileList::GetSortComparisonObject()
129{
130        CFileListCtrlSortBase::DirSortMode dirSortMode = GetDirSortMode();
131        CFileListCtrlSortBase::NameSortMode nameSortMode = GetNameSortMode();
132
133        if (!m_sortDirection) {
134                if (m_sortColumn == 1)
135                        return CFileListCtrl<CSearchFileData>::CSortComparisonObject(new CFileListCtrlSortPath<std::vector<CSearchFileData>, CSearchFileData>(m_fileData, m_fileData, dirSortMode, nameSortMode, this));
136                else if (m_sortColumn == 2)
137                        return CFileListCtrl<CSearchFileData>::CSortComparisonObject(new CFileListCtrlSortSize<std::vector<CSearchFileData>, CSearchFileData>(m_fileData, m_fileData, dirSortMode, nameSortMode, this));
138                else if (m_sortColumn == 3)
139                        return CFileListCtrl<CSearchFileData>::CSortComparisonObject(new CFileListCtrlSortType<std::vector<CSearchFileData>, CSearchFileData>(m_fileData, m_fileData, dirSortMode, nameSortMode, this));
140                else if (m_sortColumn == 4)
141                        return CFileListCtrl<CSearchFileData>::CSortComparisonObject(new CFileListCtrlSortTime<std::vector<CSearchFileData>, CSearchFileData>(m_fileData, m_fileData, dirSortMode, nameSortMode, this));
142                else if (m_sortColumn == 5)
143                        return CFileListCtrl<CSearchFileData>::CSortComparisonObject(new CFileListCtrlSortPermissions<std::vector<CSearchFileData>, CSearchFileData>(m_fileData, m_fileData, dirSortMode, nameSortMode, this));
144                else if (m_sortColumn == 6)
145                        return CFileListCtrl<CSearchFileData>::CSortComparisonObject(new CFileListCtrlSortOwnerGroup<std::vector<CSearchFileData>, CSearchFileData>(m_fileData, m_fileData, dirSortMode, nameSortMode, this));
146                else
147                        return CFileListCtrl<CSearchFileData>::CSortComparisonObject(new CFileListCtrlSortNamePath<std::vector<CSearchFileData>, CSearchFileData>(m_fileData, m_fileData, dirSortMode, nameSortMode, this));
148        }
149        else {
150                if (m_sortColumn == 1)
151                        return CFileListCtrl<CSearchFileData>::CSortComparisonObject(new CReverseSort<CFileListCtrlSortPath<std::vector<CSearchFileData>, CSearchFileData>, CSearchFileData>(m_fileData, m_fileData, dirSortMode, nameSortMode, this));
152                else if (m_sortColumn == 2)
153                        return CFileListCtrl<CSearchFileData>::CSortComparisonObject(new CReverseSort<CFileListCtrlSortSize<std::vector<CSearchFileData>, CSearchFileData>, CSearchFileData>(m_fileData, m_fileData, dirSortMode, nameSortMode, this));
154                else if (m_sortColumn == 3)
155                        return CFileListCtrl<CSearchFileData>::CSortComparisonObject(new CReverseSort<CFileListCtrlSortType<std::vector<CSearchFileData>, CSearchFileData>, CSearchFileData>(m_fileData, m_fileData, dirSortMode, nameSortMode, this));
156                else if (m_sortColumn == 4)
157                        return CFileListCtrl<CSearchFileData>::CSortComparisonObject(new CReverseSort<CFileListCtrlSortTime<std::vector<CSearchFileData>, CSearchFileData>, CSearchFileData>(m_fileData, m_fileData, dirSortMode, nameSortMode, this));
158                else if (m_sortColumn == 5)
159                        return CFileListCtrl<CSearchFileData>::CSortComparisonObject(new CReverseSort<CFileListCtrlSortPermissions<std::vector<CSearchFileData>, CSearchFileData>, CSearchFileData>(m_fileData, m_fileData, dirSortMode, nameSortMode, this));
160                else if (m_sortColumn == 6)
161                        return CFileListCtrl<CSearchFileData>::CSortComparisonObject(new CReverseSort<CFileListCtrlSortOwnerGroup<std::vector<CSearchFileData>, CSearchFileData>, CSearchFileData>(m_fileData, m_fileData, dirSortMode, nameSortMode, this));
162                else
163                        return CFileListCtrl<CSearchFileData>::CSortComparisonObject(new CReverseSort<CFileListCtrlSortNamePath<std::vector<CSearchFileData>, CSearchFileData>, CSearchFileData>(m_fileData, m_fileData, dirSortMode, nameSortMode, this));
164        }
165}
166
167wxString CSearchDialogFileList::GetItemText(int item, unsigned int column)
168{
169        if (item < 0 || item >= (int)m_indexMapping.size())
170                return wxString();
171        int index = m_indexMapping[item];
172
173        const CDirentry& entry = m_fileData[index];
174        if (!column)
175                return entry.name;
176        else if (column == 1)
177                return m_fileData[index].path.GetPath();
178        else if (column == 2) {
179                if (entry.is_dir() || entry.size < 0)
180                        return wxString();
181                else
182                        return CSizeFormat::Format(entry.size);
183        }
184        else if (column == 3) {
185                CSearchFileData& data = m_fileData[index];
186                if (data.fileType.empty()) {
187                        if (data.path.GetType() == VMS)
188                                data.fileType = GetType(StripVMSRevision(entry.name), entry.is_dir());
189                        else
190                                data.fileType = GetType(entry.name, entry.is_dir());
191                }
192
193                return data.fileType;
194        }
195        else if (column == 4)
196                return CTimeFormat::Format(entry.time);
197        else if (column == 5)
198                return *entry.permissions;
199        else if (column == 6)
200                return *entry.ownerGroup;
201        return wxString();
202}
203
204int CSearchDialogFileList::OnGetItemImage(long item) const
205{
206        CSearchDialogFileList *pThis = const_cast<CSearchDialogFileList *>(this);
207        if (item < 0 || item >= (int)m_indexMapping.size())
208                return -1;
209        int index = m_indexMapping[item];
210
211        int &icon = pThis->m_fileData[index].icon;
212
213        if (icon != -2)
214                return icon;
215
216        icon = pThis->GetIconIndex(iconType::file, pThis->m_fileData[index].name, false);
217        return icon;
218}
219
220// Search dialog
221// -------------
222
223BEGIN_EVENT_TABLE(CSearchDialog, CFilterConditionsDialog)
224EVT_BUTTON(XRCID("ID_START"), CSearchDialog::OnSearch)
225EVT_BUTTON(XRCID("ID_STOP"), CSearchDialog::OnStop)
226EVT_CONTEXT_MENU(CSearchDialog::OnContextMenu)
227EVT_MENU(XRCID("ID_MENU_SEARCH_DOWNLOAD"), CSearchDialog::OnDownload)
228EVT_MENU(XRCID("ID_MENU_SEARCH_EDIT"), CSearchDialog::OnEdit)
229EVT_MENU(XRCID("ID_MENU_SEARCH_DELETE"), CSearchDialog::OnDelete)
230EVT_CHAR_HOOK(CSearchDialog::OnCharHook)
231END_EVENT_TABLE()
232
233CSearchDialog::CSearchDialog(wxWindow* parent, CState* pState, CQueueView* pQueue)
234        : CStateEventHandler(pState)
235        , m_parent(parent)
236        , m_pQueue(pQueue)
237{
238}
239
240CSearchDialog::~CSearchDialog()
241{
242        if (m_pWindowStateManager) {
243                m_pWindowStateManager->Remember(OPTION_SEARCH_SIZE);
244                delete m_pWindowStateManager;
245        }
246}
247
248bool CSearchDialog::Load()
249{
250        if (!wxDialogEx::Load(m_parent, _T("ID_SEARCH")))
251                return false;
252
253        /* XRCed complains if adding a status bar to a dialog, so do it here instead */
254        CFilelistStatusBar* pStatusBar = new CFilelistStatusBar(this);
255        pStatusBar->SetEmptyString(_("No search results"));
256        pStatusBar->SetConnected(true);
257
258        GetSizer()->Add(pStatusBar, 0, wxGROW);
259
260        if (!CreateListControl(filter_name | filter_size | filter_path | filter_date))
261                return false;
262
263        m_results = new CSearchDialogFileList(this, m_pState, 0);
264        ReplaceControl(XRCCTRL(*this, "ID_RESULTS", wxWindow), m_results);
265
266        m_results->SetFilelistStatusBar(pStatusBar);
267
268        const CServerPath path = m_pState->GetRemotePath();
269        if (!path.empty())
270                xrc_call(*this, "ID_PATH", &wxTextCtrl::ChangeValue, path.GetPath());
271
272        SetCtrlState();
273
274        m_pWindowStateManager = new CWindowStateManager(this);
275        m_pWindowStateManager->Restore(OPTION_SEARCH_SIZE, wxSize(750, 500));
276
277        Layout();
278
279        LoadConditions();
280        EditFilter(m_search_filter);
281
282        xrc_call(*this, "ID_CASE", &wxCheckBox::SetValue, m_search_filter.matchCase);
283        xrc_call(*this, "ID_FIND_FILES", &wxCheckBox::SetValue, m_search_filter.filterFiles);
284        xrc_call(*this, "ID_FIND_DIRS", &wxCheckBox::SetValue, m_search_filter.filterDirs);
285
286        return true;
287}
288
289void CSearchDialog::Run()
290{
291        m_original_dir = m_pState->GetRemotePath();
292        m_local_target = m_pState->GetLocalDir();
293
294        m_pState->RegisterHandler(this, STATECHANGE_REMOTE_DIR_OTHER, m_pState->GetRecursiveOperationHandler());
295        m_pState->RegisterHandler(this, STATECHANGE_REMOTE_IDLE, m_pState->GetRecursiveOperationHandler());
296
297        ShowModal();
298
299        SaveConditions();
300
301        m_pState->UnregisterHandler(this, STATECHANGE_REMOTE_IDLE);
302        m_pState->UnregisterHandler(this, STATECHANGE_REMOTE_DIR_OTHER);
303
304        if (m_searching) {
305                if (!m_pState->IsRemoteIdle()) {
306                        m_pState->m_pCommandQueue->Cancel();
307                        m_pState->GetRecursiveOperationHandler()->StopRecursiveOperation();
308                }
309                if (!m_original_dir.empty())
310                        m_pState->ChangeRemoteDir(m_original_dir);
311        }
312        else {
313                if (m_pState->IsRemoteIdle() && !m_original_dir.empty())
314                        m_pState->ChangeRemoteDir(m_original_dir);
315        }
316}
317
318void CSearchDialog::OnStateChange(CState* pState, enum t_statechange_notifications notification, const wxString&, const void* data2)
319{
320        if (notification == STATECHANGE_REMOTE_DIR_OTHER && data2) {
321                auto recursiveOperation = m_pState->GetRecursiveOperationHandler();
322                if (recursiveOperation && recursiveOperation->GetOperationMode() == CRecursiveOperation::recursive_list) {
323                        std::shared_ptr<CDirectoryListing> const& listing = *reinterpret_cast<std::shared_ptr<CDirectoryListing> const*>(data2);
324                        ProcessDirectoryListing(listing);
325                }
326        }
327        else if (notification == STATECHANGE_REMOTE_IDLE) {
328                if (pState->IsRemoteIdle())
329                        m_searching = false;
330                SetCtrlState();
331        }
332}
333
334void CSearchDialog::ProcessDirectoryListing(std::shared_ptr<CDirectoryListing> const& listing)
335{
336        if (!listing || listing->failed())
337                return;
338
339        // Do not process same directory multiple times
340        if (!m_visited.insert(listing->path).second)
341                return;
342
343        int old_count = m_results->m_fileData.size();
344        int added = 0;
345
346        m_results->m_fileData.reserve(m_results->m_fileData.size() + listing->GetCount());
347        m_results->m_indexMapping.reserve(m_results->m_indexMapping.size() + listing->GetCount());
348
349        for (unsigned int i = 0; i < listing->GetCount(); ++i) {
350                const CDirentry& entry = (*listing)[i];
351
352                if (!CFilterManager::FilenameFilteredByFilter(m_search_filter, entry.name, listing->path.GetPath(), entry.is_dir(), entry.size, 0, entry.time))
353                        continue;
354
355                CSearchFileData data;
356                static_cast<CDirentry&>(data) = entry;
357                data.path = listing->path;
358                data.icon = entry.is_dir() ? m_results->m_dirIcon : -2;
359                m_results->m_fileData.push_back(data);
360                m_results->m_indexMapping.push_back(old_count + added++);
361
362                if (entry.is_dir())
363                        m_results->GetFilelistStatusBar()->AddDirectory();
364                else
365                        m_results->GetFilelistStatusBar()->AddFile(entry.size);
366        }
367
368        if (added) {
369                m_results->SetItemCount(old_count + added);
370                m_results->SortList(-1, -1, true);
371                m_results->RefreshListOnly(false);
372        }
373}
374
375void CSearchDialog::OnSearch(wxCommandEvent&)
376{
377        if (!m_pState->IsRemoteIdle()) {
378                wxBell();
379                return;
380        }
381
382        CServerPath path;
383
384        const CServer* pServer = m_pState->GetServer();
385        if (!pServer) {
386                wxMessageBoxEx(_("Connection to server lost."), _("Remote file search"), wxICON_EXCLAMATION);
387                return;
388        }
389        path.SetType(pServer->GetType());
390        if (!path.SetPath(XRCCTRL(*this, "ID_PATH", wxTextCtrl)->GetValue()) || path.empty()) {
391                wxMessageBoxEx(_("Need to enter valid remote path"), _("Remote file search"), wxICON_EXCLAMATION);
392                return;
393        }
394
395        m_search_root = path;
396
397        // Prepare filter
398        wxString error;
399        if (!ValidateFilter(error, true)) {
400                wxMessageBoxEx(wxString::Format(_("Invalid search conditions: %s"), error), _("Remote file search"), wxICON_EXCLAMATION);
401                return;
402        }
403        m_search_filter = GetFilter();
404        if (!CFilterManager::CompileRegexes(m_search_filter)) {
405                wxMessageBoxEx(_("Invalid regular expression in search conditions."), _("Remote file search"), wxICON_EXCLAMATION);
406                return;
407        }
408        m_search_filter.matchCase = xrc_call(*this, "ID_CASE", &wxCheckBox::GetValue);
409        m_search_filter.filterFiles = xrc_call(*this, "ID_FIND_FILES", &wxCheckBox::GetValue);
410        m_search_filter.filterDirs = xrc_call(*this, "ID_FIND_DIRS", &wxCheckBox::GetValue);
411
412        // Delete old results
413        m_results->ClearSelection();
414        m_results->m_indexMapping.clear();
415        m_results->m_fileData.clear();
416        m_results->SetItemCount(0);
417        m_visited.clear();
418        m_results->RefreshListOnly(true);
419
420        m_results->GetFilelistStatusBar()->Clear();
421
422        // Start
423        m_searching = true;
424        recursion_root root(path, true);
425        root.add_dir_to_visit_restricted(path, _T(""), true);
426        m_pState->GetRecursiveOperationHandler()->AddRecursionRoot(std::move(root));
427        std::vector<CFilter> const filters; // Empty, recurse into everything
428        m_pState->GetRecursiveOperationHandler()->StartRecursiveOperation(CRecursiveOperation::recursive_list, filters, path);
429}
430
431void CSearchDialog::OnStop(wxCommandEvent&)
432{
433        if (!m_pState->IsRemoteIdle()) {
434                m_pState->m_pCommandQueue->Cancel();
435                m_pState->GetRecursiveOperationHandler()->StopRecursiveOperation();
436        }
437}
438
439void CSearchDialog::SetCtrlState()
440{
441        bool idle = m_pState->IsRemoteIdle();
442        XRCCTRL(*this, "ID_START", wxButton)->Enable(idle);
443        XRCCTRL(*this, "ID_STOP", wxButton)->Enable(!idle);
444}
445
446void CSearchDialog::OnContextMenu(wxContextMenuEvent& event)
447{
448        if (event.GetEventObject() != m_results && event.GetEventObject() != m_results->GetMainWindow()) {
449                event.Skip();
450                return;
451        }
452
453        wxMenu* pMenu = wxXmlResource::Get()->LoadMenu(_T("ID_MENU_SEARCH"));
454        if (!pMenu)
455                return;
456
457        if (!m_pState->IsRemoteIdle()) {
458                pMenu->Enable(XRCID("ID_MENU_SEARCH_DOWNLOAD"), false);
459                pMenu->Enable(XRCID("ID_MENU_SEARCH_DELETE"), false);
460                pMenu->Enable(XRCID("ID_MENU_SEARCH_EDIT"), false);
461        }
462
463        PopupMenu(pMenu);
464        delete pMenu;
465}
466
467
468
469class CSearchDownloadDialog : public wxDialogEx
470{
471public:
472        bool Run(wxWindow* parent, const wxString& m_local_dir, int count_files, int count_dirs)
473        {
474                if (!Load(parent, _T("ID_SEARCH_DOWNLOAD")))
475                        return false;
476
477                wxString desc;
478                if (!count_dirs)
479                        desc.Printf(wxPLURAL("Selected %d file for transfer.", "Selected %d files for transfer.", count_files), count_files);
480                else if (!count_files)
481                        desc.Printf(wxPLURAL("Selected %d directory with its contents for transfer.", "Selected %d directories with their contents for transfer.", count_dirs), count_dirs);
482                else {
483                        wxString files = wxString::Format(wxPLURAL("%d file", "%d files", count_files), count_files);
484                        wxString dirs = wxString::Format(wxPLURAL("%d directory with its contents", "%d directories with their contents", count_dirs), count_dirs);
485                        desc.Printf(_("Selected %s and %s for transfer."), files, dirs);
486                }
487                XRCCTRL(*this, "ID_DESC", wxStaticText)->SetLabel(desc);
488
489                XRCCTRL(*this, "ID_LOCALPATH", wxTextCtrl)->ChangeValue(m_local_dir);
490
491                if (ShowModal() != wxID_OK)
492                        return false;
493
494                return true;
495        }
496
497protected:
498
499        DECLARE_EVENT_TABLE()
500        void OnBrowse(wxCommandEvent& event);
501        void OnOK(wxCommandEvent& event);
502};
503
504BEGIN_EVENT_TABLE(CSearchDownloadDialog, wxDialogEx)
505EVT_BUTTON(XRCID("ID_BROWSE"), CSearchDownloadDialog::OnBrowse)
506EVT_BUTTON(XRCID("wxID_OK"), CSearchDownloadDialog::OnOK)
507END_EVENT_TABLE()
508
509void CSearchDownloadDialog::OnBrowse(wxCommandEvent&)
510{
511        wxTextCtrl *pText = XRCCTRL(*this, "ID_LOCALPATH", wxTextCtrl);
512
513        wxDirDialog dlg(this, _("Select target download directory"), pText->GetValue(), wxDD_NEW_DIR_BUTTON);
514        if (dlg.ShowModal() == wxID_OK)
515                pText->ChangeValue(dlg.GetPath());
516}
517
518void CSearchDownloadDialog::OnOK(wxCommandEvent&)
519{
520        wxTextCtrl *pText = XRCCTRL(*this, "ID_LOCALPATH", wxTextCtrl);
521
522        CLocalPath path(pText->GetValue());
523        if (path.empty()) {
524                wxMessageBoxEx(_("You have to enter a local directory."), _("Download search results"), wxICON_EXCLAMATION);
525                return;
526        }
527
528        if (!path.IsWriteable()) {
529                wxMessageBoxEx(_("You have to enter a writable local directory."), _("Download search results"), wxICON_EXCLAMATION);
530                return;
531        }
532
533        EndDialog(wxID_OK);
534}
535
536void CSearchDialog::ProcessSelection(std::list<int> &selected_files, std::deque<CServerPath> &selected_dirs)
537{
538        std::deque<CServerPath> dirs;
539
540        int sel = -1;
541        while ((sel = m_results->GetNextItem(sel, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED)) != -1) {
542                if (sel > (int)m_results->m_indexMapping.size())
543                        continue;
544                int index = m_results->m_indexMapping[sel];
545
546                if (m_results->m_fileData[index].is_dir()) {
547                        CServerPath path = m_results->m_fileData[index].path;
548                        path.ChangePath(m_results->m_fileData[index].name);
549                        if (path.empty())
550                                continue;
551
552                        dirs.push_back(path);
553                }
554                else
555                        selected_files.push_back(index);
556        }
557
558        // Make sure that selected_dirs does not contain
559        // any directories that are in a parent-child relationship
560        // Resolve by only keeping topmost parents
561        std::sort(dirs.begin(), dirs.end());
562        for (CServerPath const& path : dirs) {
563                if (!selected_dirs.empty() && (path.IsSubdirOf(selected_dirs.back(), false) || path == selected_dirs.back())) {
564                        continue;
565                }
566                selected_dirs.push_back(path);
567        }
568
569        // Now in a second phase filter out all files that are also in a directory
570        std::list<int> selected_files_new;
571        for (auto const& sel_file : selected_files) {
572                CServerPath const& path = m_results->m_fileData[sel_file].path;
573                std::deque<CServerPath>::iterator path_iter;
574                for (path_iter = selected_dirs.begin(); path_iter != selected_dirs.end(); ++path_iter) {
575                        if (*path_iter == path || path_iter->IsParentOf(path, false))
576                                break;
577                }
578                if (path_iter == selected_dirs.end())
579                        selected_files_new.push_back(sel_file);
580        }
581        selected_files.swap(selected_files_new);
582
583        // At this point selected_dirs contains uncomparable
584        // paths and selected_files contains only files not
585        // covered by any of those directories.
586}
587
588void CSearchDialog::OnDownload(wxCommandEvent&)
589{
590        if (!m_pState->IsRemoteIdle())
591                return;
592
593        // Find all selected files and directories
594        std::deque<CServerPath> selected_dirs;
595        std::list<int> selected_files;
596        ProcessSelection(selected_files, selected_dirs);
597
598        if (selected_files.empty() && selected_dirs.empty())
599                return;
600
601        CSearchDownloadDialog dlg;
602        if (!dlg.Run(this, m_local_target.GetPath(), selected_files.size(), selected_dirs.size()))
603                return;
604
605        wxTextCtrl *pText = XRCCTRL(dlg, "ID_LOCALPATH", wxTextCtrl);
606
607        CLocalPath path(pText->GetValue());
608        if (path.empty() || !path.IsWriteable()) {
609                wxBell();
610                return;
611        }
612        m_local_target = path;
613
614        CServer const* pServer = m_pState->GetServer();
615        if (!pServer) {
616                wxBell();
617                return;
618        }
619
620        bool start = XRCCTRL(dlg, "ID_QUEUE_START", wxRadioButton)->GetValue();
621        bool flatten = XRCCTRL(dlg, "ID_PATHS_FLATTEN", wxRadioButton)->GetValue();
622
623        for (auto const& sel : selected_files) {
624                const CDirentry& entry = m_results->m_fileData[sel];
625
626                CLocalPath target_path = path;
627                if (!flatten) {
628                        // Append relative path to search root to local target path
629                        CServerPath remote_path = m_results->m_fileData[sel].path;
630                        std::list<wxString> segments;
631                        while (m_search_root.IsParentOf(remote_path, false) && remote_path.HasParent()) {
632                                segments.push_front(remote_path.GetLastSegment());
633                                remote_path = remote_path.GetParent();
634                        }
635                        for (auto const& segment : segments) {
636                                target_path.AddSegment(segment);
637                        }
638                }
639
640                CServerPath remote_path = m_results->m_fileData[sel].path;
641                wxString localName = CQueueView::ReplaceInvalidCharacters(entry.name);
642                if (!entry.is_dir() && remote_path.GetType() == VMS && COptions::Get()->GetOptionVal(OPTION_STRIP_VMS_REVISION))
643                        localName = StripVMSRevision(localName);
644
645                m_pQueue->QueueFile(!start, true,
646                        entry.name, (localName != entry.name) ? localName : wxString(),
647                        target_path, remote_path, *pServer, entry.size);
648        }
649        m_pQueue->QueueFile_Finish(start);
650
651        enum CRecursiveOperation::OperationMode mode;
652        if (flatten)
653                mode = start ? CRecursiveOperation::recursive_download_flatten : CRecursiveOperation::recursive_addtoqueue_flatten;
654        else
655                mode = start ? CRecursiveOperation::recursive_download : CRecursiveOperation::recursive_addtoqueue;
656
657        for (auto const& dir : selected_dirs) {
658                CLocalPath target_path = path;
659                if (!flatten && dir.HasParent())
660                        target_path.AddSegment(dir.GetLastSegment());
661
662                recursion_root root(dir, true);
663                root.add_dir_to_visit(dir, _T(""), target_path, false);
664                m_pState->GetRecursiveOperationHandler()->AddRecursionRoot(std::move(root));
665        }
666        std::vector<CFilter> const filters; // Empty, recurse into everything
667        m_pState->GetRecursiveOperationHandler()->StartRecursiveOperation(mode, filters, m_original_dir);
668}
669
670void CSearchDialog::OnEdit(wxCommandEvent&)
671{
672        if (!m_pState->IsRemoteIdle())
673                return;
674
675        // Find all selected files and directories
676        std::deque<CServerPath> selected_dirs;
677        std::list<int> selected_files;
678        ProcessSelection(selected_files, selected_dirs);
679
680        if (selected_files.empty() && selected_dirs.empty())
681                return;
682
683        if (!selected_dirs.empty()) {
684                wxMessageBoxEx(_("Editing directories is not supported"), _("Editing search results"), wxICON_EXCLAMATION);
685                return;
686        }
687
688        CEditHandler* pEditHandler = CEditHandler::Get();
689        if (!pEditHandler) {
690                wxBell();
691                return;
692        }
693
694        const wxString& localDir = pEditHandler->GetLocalDirectory();
695        if (localDir.empty()) {
696                wxMessageBoxEx(_("Could not get temporary directory to download file into."), _("Cannot edit file"), wxICON_STOP);
697                return;
698        }
699
700        const CServer* pServer = m_pState->GetServer();
701        if (!pServer) {
702                wxBell();
703                return;
704        }
705
706        if (selected_files.size() > 10) {
707                CConditionalDialog dlg(this, CConditionalDialog::many_selected_for_edit, CConditionalDialog::yesno);
708                dlg.SetTitle(_("Confirmation needed"));
709                dlg.AddText(_("You have selected more than 10 files for editing, do you really want to continue?"));
710
711                if (!dlg.Run())
712                        return;
713        }
714
715        for (auto const& item : selected_files) {
716                const CDirentry& entry = m_results->m_fileData[item];
717                const CServerPath path = m_results->m_fileData[item].path;
718
719                pEditHandler->Edit(CEditHandler::remote, entry.name, path, *pServer, entry.size, this);
720        }
721}
722
723void CSearchDialog::OnDelete(wxCommandEvent&)
724{
725        if (!m_pState->IsRemoteIdle())
726                return;
727
728        // Find all selected files and directories
729        std::deque<CServerPath> selected_dirs;
730        std::list<int> selected_files;
731        ProcessSelection(selected_files, selected_dirs);
732
733        if (selected_files.empty() && selected_dirs.empty())
734                return;
735
736        wxString question;
737        if (selected_dirs.empty())
738                question.Printf(wxPLURAL("Really delete %d file from the server?", "Really delete %d files from the server?", selected_files.size()), selected_files.size());
739        else if (selected_files.empty())
740                question.Printf(wxPLURAL("Really delete %d directory with its contents from the server?", "Really delete %d directories with their contents from the server?", selected_dirs.size()), selected_dirs.size());
741        else {
742                wxString files = wxString::Format(wxPLURAL("%d file", "%d files", selected_files.size()), selected_files.size());
743                wxString dirs = wxString::Format(wxPLURAL("%d directory with its contents", "%d directories with their contents", selected_dirs.size()), selected_dirs.size());
744                question.Printf(_("Really delete %s and %s from the server?"), files, dirs);
745        }
746
747        if (wxMessageBoxEx(question, _("Confirm deletion"), wxICON_QUESTION | wxYES_NO) != wxYES)
748                return;
749
750        for (auto const& file : selected_files) {
751                CDirentry const& entry = m_results->m_fileData[file];
752                std::deque<wxString> files_to_delete;
753                files_to_delete.push_back(entry.name);
754                m_pState->m_pCommandQueue->ProcessCommand(new CDeleteCommand(m_results->m_fileData[file].path, std::move(files_to_delete)));
755        }
756
757        for (auto path : selected_dirs) {
758                wxString segment;
759                if (path.HasParent()) {
760                        segment = path.GetLastSegment();
761                        path = path.GetParent();
762                }
763                recursion_root root(path, !path.HasParent());
764                root.add_dir_to_visit(path, segment);
765                m_pState->GetRecursiveOperationHandler()->AddRecursionRoot(std::move(root));
766        }
767        std::vector<CFilter> const filters; // Empty, recurse into everything
768        m_pState->GetRecursiveOperationHandler()->StartRecursiveOperation(CRecursiveOperation::recursive_delete, filters, m_original_dir);
769}
770
771void CSearchDialog::OnCharHook(wxKeyEvent& event)
772{
773        if (IsEscapeKey(event)) {
774                EndDialog(wxID_CANCEL);
775                return;
776        }
777
778        event.Skip();
779}
780
781#ifdef __WXMSW__
782int CSearchDialogFileList::GetOverlayIndex(int item)
783{
784        if (item < 0 || item >= (int)m_indexMapping.size())
785                return -1;
786        int index = m_indexMapping[item];
787
788        if (m_fileData[index].is_link())
789                return GetLinkOverlayIndex();
790
791        return 0;
792}
793#endif
794
795void CSearchDialog::LoadConditions()
796{
797        CInterProcessMutex mutex(MUTEX_SEARCHCONDITIONS);
798
799        CXmlFile file(wxGetApp().GetSettingsFile(_T("search")));
800        auto document = file.Load();
801        if (!document) {
802                wxMessageBoxEx(file.GetError(), _("Error loading xml file"), wxICON_ERROR);
803                return;
804        }
805
806        auto filter = document.child("Filter");
807        if (!filter)
808                return;
809
810        if (!CFilterManager::LoadFilter(filter, m_search_filter))
811                m_search_filter = CFilter();
812}
813
814void CSearchDialog::SaveConditions()
815{
816        CInterProcessMutex mutex(MUTEX_SEARCHCONDITIONS);
817
818        CXmlFile file(wxGetApp().GetSettingsFile(_T("search")));
819        auto document = file.Load();
820        if (!document) {
821                wxMessageBoxEx(file.GetError(), _("Error loading xml file"), wxICON_ERROR);
822                return;
823        }
824
825        pugi::xml_node filter;
826        while ((filter = document.child("Filter")))
827                document.remove_child(filter);
828        filter = document.append_child("Filter");
829
830        CFilterDialog::SaveFilter(filter, m_search_filter);
831
832        file.Save(true);
833}
Note: See TracBrowser for help on using the repository browser.