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

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

First release to xenial

File size: 27.4 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 GetNextFile(wxString&, bool &, int64_t&, CDateTime&) { 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);
295        m_pState->RegisterHandler(this, STATECHANGE_REMOTE_IDLE);
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& data, const void* data2)
319{
320        if (notification == STATECHANGE_REMOTE_DIR_OTHER && data2) {
321                std::shared_ptr<CDirectoryListing> const& listing = *reinterpret_cast<std::shared_ptr<CDirectoryListing> const*>(data2);
322                ProcessDirectoryListing(listing);
323        }
324        else if (notification == STATECHANGE_REMOTE_IDLE) {
325                if (pState->IsRemoteIdle())
326                        m_searching = false;
327                SetCtrlState();
328        }
329}
330
331void CSearchDialog::ProcessDirectoryListing(std::shared_ptr<CDirectoryListing> const& listing)
332{
333        if (!listing || listing->failed())
334                return;
335
336        // Do not process same directory multiple times
337        if (!m_visited.insert(listing->path).second)
338                return;
339
340        int old_count = m_results->m_fileData.size();
341        int added = 0;
342
343        m_results->m_fileData.reserve(m_results->m_fileData.size() + listing->GetCount());
344        m_results->m_indexMapping.reserve(m_results->m_indexMapping.size() + listing->GetCount());
345
346        for (unsigned int i = 0; i < listing->GetCount(); ++i) {
347                const CDirentry& entry = (*listing)[i];
348
349                if (!CFilterManager::FilenameFilteredByFilter(m_search_filter, entry.name, listing->path.GetPath(), entry.is_dir(), entry.size, 0, entry.time))
350                        continue;
351
352                CSearchFileData data;
353                static_cast<CDirentry&>(data) = entry;
354                data.path = listing->path;
355                data.icon = entry.is_dir() ? m_results->m_dirIcon : -2;
356                m_results->m_fileData.push_back(data);
357                m_results->m_indexMapping.push_back(old_count + added++);
358
359                if (entry.is_dir())
360                        m_results->GetFilelistStatusBar()->AddDirectory();
361                else
362                        m_results->GetFilelistStatusBar()->AddFile(entry.size);
363        }
364
365        if (added) {
366                m_results->SetItemCount(old_count + added);
367                m_results->SortList(-1, -1, true);
368                m_results->RefreshListOnly(false);
369        }
370}
371
372void CSearchDialog::OnSearch(wxCommandEvent& event)
373{
374        if (!m_pState->IsRemoteIdle()) {
375                wxBell();
376                return;
377        }
378
379        CServerPath path;
380
381        const CServer* pServer = m_pState->GetServer();
382        if (!pServer) {
383                wxMessageBoxEx(_("Connection to server lost."), _("Remote file search"), wxICON_EXCLAMATION);
384                return;
385        }
386        path.SetType(pServer->GetType());
387        if (!path.SetPath(XRCCTRL(*this, "ID_PATH", wxTextCtrl)->GetValue()) || path.empty()) {
388                wxMessageBoxEx(_("Need to enter valid remote path"), _("Remote file search"), wxICON_EXCLAMATION);
389                return;
390        }
391
392        m_search_root = path;
393
394        // Prepare filter
395        wxString error;
396        if (!ValidateFilter(error, true)) {
397                wxMessageBoxEx(wxString::Format(_("Invalid search conditions: %s"), error), _("Remote file search"), wxICON_EXCLAMATION);
398                return;
399        }
400        m_search_filter = GetFilter();
401        if (!CFilterManager::CompileRegexes(m_search_filter)) {
402                wxMessageBoxEx(_("Invalid regular expression in search conditions."), _("Remote file search"), wxICON_EXCLAMATION);
403                return;
404        }
405        m_search_filter.matchCase = xrc_call(*this, "ID_CASE", &wxCheckBox::GetValue);
406        m_search_filter.filterFiles = xrc_call(*this, "ID_FIND_FILES", &wxCheckBox::GetValue);
407        m_search_filter.filterDirs = xrc_call(*this, "ID_FIND_DIRS", &wxCheckBox::GetValue);
408
409        // Delete old results
410        m_results->ClearSelection();
411        m_results->m_indexMapping.clear();
412        m_results->m_fileData.clear();
413        m_results->SetItemCount(0);
414        m_visited.clear();
415        m_results->RefreshListOnly(true);
416
417        m_results->GetFilelistStatusBar()->Clear();
418
419        // Start
420        m_searching = true;
421        m_pState->GetRecursiveOperationHandler()->AddDirectoryToVisitRestricted(path, _T(""), true);
422        std::vector<CFilter> const filters; // Empty, recurse into everything
423        m_pState->GetRecursiveOperationHandler()->StartRecursiveOperation(CRecursiveOperation::recursive_list, path, filters, true);
424}
425
426void CSearchDialog::OnStop(wxCommandEvent& event)
427{
428        if (!m_pState->IsRemoteIdle()) {
429                m_pState->m_pCommandQueue->Cancel();
430                m_pState->GetRecursiveOperationHandler()->StopRecursiveOperation();
431        }
432}
433
434void CSearchDialog::SetCtrlState()
435{
436        bool idle = m_pState->IsRemoteIdle();
437        XRCCTRL(*this, "ID_START", wxButton)->Enable(idle);
438        XRCCTRL(*this, "ID_STOP", wxButton)->Enable(!idle);
439}
440
441void CSearchDialog::OnContextMenu(wxContextMenuEvent& event)
442{
443        if (event.GetEventObject() != m_results && event.GetEventObject() != m_results->GetMainWindow()) {
444                event.Skip();
445                return;
446        }
447
448        wxMenu* pMenu = wxXmlResource::Get()->LoadMenu(_T("ID_MENU_SEARCH"));
449        if (!pMenu)
450                return;
451
452        if (!m_pState->IsRemoteIdle()) {
453                pMenu->Enable(XRCID("ID_MENU_SEARCH_DOWNLOAD"), false);
454                pMenu->Enable(XRCID("ID_MENU_SEARCH_DELETE"), false);
455        }
456
457        PopupMenu(pMenu);
458        delete pMenu;
459}
460
461
462
463class CSearchDownloadDialog : public wxDialogEx
464{
465public:
466        bool Run(wxWindow* parent, const wxString& m_local_dir, int count_files, int count_dirs)
467        {
468                if (!Load(parent, _T("ID_SEARCH_DOWNLOAD")))
469                        return false;
470
471                wxString desc;
472                if (!count_dirs)
473                        desc.Printf(wxPLURAL("Selected %d file for transfer.", "Selected %d files for transfer.", count_files), count_files);
474                else if (!count_files)
475                        desc.Printf(wxPLURAL("Selected %d directory with its contents for transfer.", "Selected %d directories with their contents for transfer.", count_dirs), count_dirs);
476                else {
477                        wxString files = wxString::Format(wxPLURAL("%d file", "%d files", count_files), count_files);
478                        wxString dirs = wxString::Format(wxPLURAL("%d directory with its contents", "%d directories with their contents", count_dirs), count_dirs);
479                        desc.Printf(_("Selected %s and %s for transfer."), files, dirs);
480                }
481                XRCCTRL(*this, "ID_DESC", wxStaticText)->SetLabel(desc);
482
483                XRCCTRL(*this, "ID_LOCALPATH", wxTextCtrl)->ChangeValue(m_local_dir);
484
485                if (ShowModal() != wxID_OK)
486                        return false;
487
488                return true;
489        }
490
491protected:
492
493        DECLARE_EVENT_TABLE()
494        void OnBrowse(wxCommandEvent& event);
495        void OnOK(wxCommandEvent& event);
496};
497
498BEGIN_EVENT_TABLE(CSearchDownloadDialog, wxDialogEx)
499EVT_BUTTON(XRCID("ID_BROWSE"), CSearchDownloadDialog::OnBrowse)
500EVT_BUTTON(XRCID("wxID_OK"), CSearchDownloadDialog::OnOK)
501END_EVENT_TABLE()
502
503void CSearchDownloadDialog::OnBrowse(wxCommandEvent& event)
504{
505        wxTextCtrl *pText = XRCCTRL(*this, "ID_LOCALPATH", wxTextCtrl);
506
507        wxDirDialog dlg(this, _("Select target download directory"), pText->GetValue(), wxDD_NEW_DIR_BUTTON);
508        if (dlg.ShowModal() == wxID_OK)
509                pText->ChangeValue(dlg.GetPath());
510}
511
512void CSearchDownloadDialog::OnOK(wxCommandEvent& event)
513{
514        wxTextCtrl *pText = XRCCTRL(*this, "ID_LOCALPATH", wxTextCtrl);
515
516        CLocalPath path(pText->GetValue());
517        if (path.empty()) {
518                wxMessageBoxEx(_("You have to enter a local directory."), _("Download search results"), wxICON_EXCLAMATION);
519                return;
520        }
521
522        if (!path.IsWriteable()) {
523                wxMessageBoxEx(_("You have to enter a writable local directory."), _("Download search results"), wxICON_EXCLAMATION);
524                return;
525        }
526
527        EndDialog(wxID_OK);
528}
529
530void CSearchDialog::ProcessSelection(std::list<int> &selected_files, std::list<CServerPath> &selected_dirs)
531{
532        int sel = -1;
533        while ((sel = m_results->GetNextItem(sel, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED)) != -1) {
534                if (sel > (int)m_results->m_indexMapping.size())
535                        continue;
536                int index = m_results->m_indexMapping[sel];
537
538                if (m_results->m_fileData[index].is_dir()) {
539                        CServerPath path = m_results->m_fileData[index].path;
540                        path.ChangePath(m_results->m_fileData[index].name);
541                        if (path.empty())
542                                continue;
543
544                        bool replaced = false;
545                        std::list<CServerPath>::iterator iter = selected_dirs.begin();
546                        std::list<CServerPath>::iterator prev;
547
548                        // Make sure that selected_dirs does not contain
549                        // any directories that are in a parent-child relationship
550                        // Resolve by only keeping topmost parents
551                        while (iter != selected_dirs.end()) {
552                                if (*iter == path) {
553                                        replaced = true;
554                                        break;
555                                }
556
557                                if (iter->IsParentOf(path, false)) {
558                                        replaced = true;
559                                        break;
560                                }
561
562                                if (iter->IsSubdirOf(path, false)) {
563                                        if (!replaced) {
564                                                *iter = path;
565                                                replaced = true;
566                                        }
567                                        else {
568                                                prev = iter++;
569                                                selected_dirs.erase(prev);
570                                                continue;
571                                        }
572                                }
573                                ++iter;
574                        }
575                        if (!replaced)
576                                selected_dirs.push_back(path);
577                }
578                else
579                        selected_files.push_back(index);
580        }
581
582        // Now in a second phase filter out all files that are also in a directory
583        std::list<int> selected_files_new;
584        for (auto const& sel_file : selected_files) {
585                CServerPath path = m_results->m_fileData[sel_file].path;
586                std::list<CServerPath>::const_iterator path_iter;
587                for (path_iter = selected_dirs.begin(); path_iter != selected_dirs.end(); ++path_iter) {
588                        if (*path_iter == path || path_iter->IsParentOf(path, false))
589                                break;
590                }
591                if (path_iter == selected_dirs.end())
592                        selected_files_new.push_back(sel_file);
593        }
594        selected_files.swap(selected_files_new);
595
596        // At this point selected_dirs contains uncomparable
597        // paths and selected_files contains only files not
598        // covered by any of those directories.
599}
600
601void CSearchDialog::OnDownload(wxCommandEvent&)
602{
603        if (!m_pState->IsRemoteIdle())
604                return;
605
606        // Find all selected files and directories
607        std::list<CServerPath> selected_dirs;
608        std::list<int> selected_files;
609        ProcessSelection(selected_files, selected_dirs);
610
611        if (selected_files.empty() && selected_dirs.empty())
612                return;
613
614        if (selected_dirs.size() > 1) {
615                wxMessageBoxEx(_("Downloading multiple unrelated directories is not yet supported"), _("Downloading search results"), wxICON_EXCLAMATION);
616                return;
617        }
618
619        CSearchDownloadDialog dlg;
620        if (!dlg.Run(this, m_local_target.GetPath(), selected_files.size(), selected_dirs.size()))
621                return;
622
623        wxTextCtrl *pText = XRCCTRL(dlg, "ID_LOCALPATH", wxTextCtrl);
624
625        CLocalPath path(pText->GetValue());
626        if (path.empty() || !path.IsWriteable()) {
627                wxBell();
628                return;
629        }
630        m_local_target = path;
631
632        CServer const* pServer = m_pState->GetServer();
633        if (!pServer) {
634                wxBell();
635                return;
636        }
637
638        bool start = XRCCTRL(dlg, "ID_QUEUE_START", wxRadioButton)->GetValue();
639        bool flatten = XRCCTRL(dlg, "ID_PATHS_FLATTEN", wxRadioButton)->GetValue();
640
641        for (auto const& sel : selected_files) {
642                const CDirentry& entry = m_results->m_fileData[sel];
643
644                CLocalPath target_path = path;
645                if (!flatten) {
646                        // Append relative path to search root to local target path
647                        CServerPath remote_path = m_results->m_fileData[sel].path;
648                        std::list<wxString> segments;
649                        while (m_search_root.IsParentOf(remote_path, false) && remote_path.HasParent()) {
650                                segments.push_front(remote_path.GetLastSegment());
651                                remote_path = remote_path.GetParent();
652                        }
653                        for (auto const& segment : segments) {
654                                target_path.AddSegment(segment);
655                        }
656                }
657
658                CServerPath remote_path = m_results->m_fileData[sel].path;
659                wxString localName = CQueueView::ReplaceInvalidCharacters(entry.name);
660                if (!entry.is_dir() && remote_path.GetType() == VMS && COptions::Get()->GetOptionVal(OPTION_STRIP_VMS_REVISION))
661                        localName = StripVMSRevision(localName);
662
663                m_pQueue->QueueFile(!start, true,
664                        entry.name, (localName != entry.name) ? localName : wxString(),
665                        target_path, remote_path, *pServer, entry.size);
666        }
667        m_pQueue->QueueFile_Finish(start);
668
669        enum CRecursiveOperation::OperationMode mode;
670        if (flatten)
671                mode = start ? CRecursiveOperation::recursive_download_flatten : CRecursiveOperation::recursive_addtoqueue_flatten;
672        else
673                mode = start ? CRecursiveOperation::recursive_download : CRecursiveOperation::recursive_addtoqueue;
674
675        for (auto const& dir : selected_dirs) {
676                CLocalPath target_path = path;
677                if (!flatten && dir.HasParent())
678                        target_path.AddSegment(dir.GetLastSegment());
679
680                m_pState->GetRecursiveOperationHandler()->AddDirectoryToVisit(dir, _T(""), target_path, false);
681                std::vector<CFilter> const filters; // Empty, recurse into everything
682                m_pState->GetRecursiveOperationHandler()->StartRecursiveOperation(mode, dir, filters, true, m_original_dir);
683        }
684}
685
686void CSearchDialog::OnEdit(wxCommandEvent&)
687{
688        if (!m_pState->IsRemoteIdle())
689                return;
690
691        // Find all selected files and directories
692        std::list<CServerPath> selected_dirs;
693        std::list<int> selected_files;
694        ProcessSelection(selected_files, selected_dirs);
695
696        if (selected_files.empty() && selected_dirs.empty())
697                return;
698
699        if (!selected_dirs.empty()) {
700                wxMessageBoxEx(_("Editing directories is not supported"), _("Editing search results"), wxICON_EXCLAMATION);
701                return;
702        }
703
704        CEditHandler* pEditHandler = CEditHandler::Get();
705        if (!pEditHandler) {
706                wxBell();
707                return;
708        }
709
710        const wxString& localDir = pEditHandler->GetLocalDirectory();
711        if (localDir.empty()) {
712                wxMessageBoxEx(_("Could not get temporary directory to download file into."), _("Cannot edit file"), wxICON_STOP);
713                return;
714        }
715
716        const CServer* pServer = m_pState->GetServer();
717        if (!pServer) {
718                wxBell();
719                return;
720        }
721
722        if (selected_files.size() > 10) {
723                CConditionalDialog dlg(this, CConditionalDialog::many_selected_for_edit, CConditionalDialog::yesno);
724                dlg.SetTitle(_("Confirmation needed"));
725                dlg.AddText(_("You have selected more than 10 files for editing, do you really want to continue?"));
726
727                if (!dlg.Run())
728                        return;
729        }
730
731        for (auto const& item : selected_files) {
732                const CDirentry& entry = m_results->m_fileData[item];
733                const CServerPath path = m_results->m_fileData[item].path;
734
735                pEditHandler->Edit(CEditHandler::remote, entry.name, path, *pServer, entry.size, this);
736        }
737}
738
739void CSearchDialog::OnDelete(wxCommandEvent&)
740{
741        if (!m_pState->IsRemoteIdle())
742                return;
743
744        // Find all selected files and directories
745        std::list<CServerPath> selected_dirs;
746        std::list<int> selected_files;
747        ProcessSelection(selected_files, selected_dirs);
748
749        if (selected_files.empty() && selected_dirs.empty())
750                return;
751
752        if (selected_dirs.size() > 1) {
753                wxMessageBoxEx(_("Deleting multiple unrelated directories is not yet supported"), _("Deleting directories"), wxICON_EXCLAMATION);
754                return;
755        }
756
757        wxString question;
758        if (selected_dirs.empty())
759                question.Printf(wxPLURAL("Really delete %d file from the server?", "Really delete %d files from the server?", selected_files.size()), selected_files.size());
760        else if (selected_files.empty())
761                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());
762        else {
763                wxString files = wxString::Format(wxPLURAL("%d file", "%d files", selected_files.size()), selected_files.size());
764                wxString dirs = wxString::Format(wxPLURAL("%d directory with its contents", "%d directories with their contents", selected_dirs.size()), selected_dirs.size());
765                question.Printf(_("Really delete %s and %s from the server?"), files, dirs);
766        }
767
768        if (wxMessageBoxEx(question, _("Confirm deletion"), wxICON_QUESTION | wxYES_NO) != wxYES)
769                return;
770
771        for (auto const& file : selected_files) {
772                CDirentry const& entry = m_results->m_fileData[file];
773                std::deque<wxString> files_to_delete;
774                files_to_delete.push_back(entry.name);
775                m_pState->m_pCommandQueue->ProcessCommand(new CDeleteCommand(m_results->m_fileData[file].path, std::move(files_to_delete)));
776        }
777
778        for (auto path : selected_dirs) {
779                if (!path.HasParent())
780                        m_pState->GetRecursiveOperationHandler()->AddDirectoryToVisit(path, _T(""));
781                else {
782                        m_pState->GetRecursiveOperationHandler()->AddDirectoryToVisit(path.GetParent(), path.GetLastSegment());
783                        path = path.GetParent();
784                }
785
786                std::vector<CFilter> const filters; // Empty, recurse into everything
787                m_pState->GetRecursiveOperationHandler()->StartRecursiveOperation(CRecursiveOperation::recursive_delete, path, filters, !path.HasParent(), m_original_dir);
788        }
789}
790
791void CSearchDialog::OnCharHook(wxKeyEvent& event)
792{
793        if (IsEscapeKey(event)) {
794                EndDialog(wxID_CANCEL);
795                return;
796        }
797
798        event.Skip();
799}
800
801#ifdef __WXMSW__
802int CSearchDialogFileList::GetOverlayIndex(int item)
803{
804        if (item < 0 || item >= (int)m_indexMapping.size())
805                return -1;
806        int index = m_indexMapping[item];
807
808        if (m_fileData[index].is_link())
809                return GetLinkOverlayIndex();
810
811        return 0;
812}
813#endif
814
815void CSearchDialog::LoadConditions()
816{
817        CInterProcessMutex mutex(MUTEX_SEARCHCONDITIONS);
818
819        CXmlFile file(wxGetApp().GetSettingsFile(_T("search")));
820        auto document = file.Load();
821        if (!document) {
822                wxMessageBoxEx(file.GetError(), _("Error loading xml file"), wxICON_ERROR);
823                return;
824        }
825
826        auto filter = document.child("Filter");
827        if (!filter)
828                return;
829
830        if (!CFilterManager::LoadFilter(filter, m_search_filter))
831                m_search_filter = CFilter();
832}
833
834void CSearchDialog::SaveConditions()
835{
836        CInterProcessMutex mutex(MUTEX_SEARCHCONDITIONS);
837
838        CXmlFile file(wxGetApp().GetSettingsFile(_T("search")));
839        auto document = file.Load();
840        if (!document) {
841                wxMessageBoxEx(file.GetError(), _("Error loading xml file"), wxICON_ERROR);
842                return;
843        }
844
845        pugi::xml_node filter;
846        while ((filter = document.child("Filter")))
847                document.remove_child(filter);
848        filter = document.append_child("Filter");
849
850        CFilterDialog::SaveFilter(filter, m_search_filter);
851
852        file.Save(true);
853}
Note: See TracBrowser for help on using the repository browser.