1 #include "filezilla.h"
2 #include "filteredit.h"
3 #include "customheightlistctrl.h"
4 #include "window_state_manager.h"
5 #include "Options.h"
6 #include "textctrlex.h"
7 
8 #include <libfilezilla/translate.hpp>
9 
BEGIN_EVENT_TABLE(CFilterEditDialog,CFilterConditionsDialog)10 BEGIN_EVENT_TABLE(CFilterEditDialog, CFilterConditionsDialog)
11 EVT_BUTTON(XRCID("wxID_OK"), CFilterEditDialog::OnOK)
12 EVT_BUTTON(XRCID("wxID_CANCEL"), CFilterEditDialog::OnCancel)
13 EVT_BUTTON(XRCID("ID_NEW"), CFilterEditDialog::OnNew)
14 EVT_BUTTON(XRCID("ID_DELETE"), CFilterEditDialog::OnDelete)
15 EVT_BUTTON(XRCID("ID_RENAME"), CFilterEditDialog::OnRename)
16 EVT_BUTTON(XRCID("ID_COPY"), CFilterEditDialog::OnCopy)
17 EVT_LISTBOX(XRCID("ID_FILTERS"), CFilterEditDialog::OnFilterSelect)
18 END_EVENT_TABLE()
19 
20 CFilterEditDialog::CFilterEditDialog()
21 {
22 }
23 
~CFilterEditDialog()24 CFilterEditDialog::~CFilterEditDialog()
25 {
26 	if (m_pWindowStateManager) {
27 		m_pWindowStateManager->Remember(OPTION_FILTEREDIT_SIZE);
28 		delete m_pWindowStateManager;
29 	}
30 }
31 
OnOK(wxCommandEvent &)32 void CFilterEditDialog::OnOK(wxCommandEvent&)
33 {
34 	if (!Validate()) {
35 		return;
36 	}
37 
38 	if (m_currentSelection != -1) {
39 		wxASSERT((unsigned int)m_currentSelection < m_filters.size());
40 		SaveFilter(m_filters[m_currentSelection]);
41 	}
42 	for (unsigned int i = 0; i < m_filters.size(); ++i) {
43 		if (!m_filters[i].HasConditionOfType(filter_permissions) && !m_filters[i].HasConditionOfType(filter_attributes)) {
44 			continue;
45 		}
46 
47 		for (unsigned int j = 0; j < m_filterSets.size(); ++j) {
48 			m_filterSets[j].remote[i] = false;
49 		}
50 	}
51 
52 	EndModal(wxID_OK);
53 }
54 
OnCancel(wxCommandEvent &)55 void CFilterEditDialog::OnCancel(wxCommandEvent&)
56 {
57 	EndModal(wxID_CANCEL);
58 }
59 
Create(wxWindow * parent,const std::vector<CFilter> & filters,const std::vector<CFilterSet> & filterSets)60 bool CFilterEditDialog::Create(wxWindow* parent, const std::vector<CFilter>& filters, const std::vector<CFilterSet>& filterSets)
61 {
62 	bool has_foreign_type = false;
63 	for (std::vector<CFilter>::const_iterator iter = filters.begin(); iter != filters.end(); ++iter) {
64 		const CFilter& filter = *iter;
65 		if (!filter.HasConditionOfType(filter_foreign)) {
66 			continue;
67 		}
68 
69 		has_foreign_type = true;
70 		break;
71 	}
72 
73 	wxDialogEx::Create(parent, -1, _("Edit filters"), wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER);
74 
75 	auto& lay = layout();
76 
77 	auto main = lay.createMain(this, 1);
78 	main->AddGrowableCol(0);
79 	main->AddGrowableRow(0);
80 
81 	auto sides = lay.createFlex(2);
82 	main->Add(sides, lay.grow);
83 	sides->AddGrowableRow(0);
84 	sides->AddGrowableCol(1);
85 
86 	{
87 		auto left = lay.createFlex(1);
88 		sides->Add(left, lay.grow);
89 		left->AddGrowableRow(1);
90 
91 		left->Add(new wxStaticText(this, -1, _("&Filters:")));
92 		filterList_ = new wxListBox(this, XRCID("ID_FILTERS"));
93 		filterList_->SetFocus();
94 		left->Add(filterList_, lay.grow);
95 
96 		auto grid = lay.createGrid(2, 2);
97 		left->Add(grid, 0, wxALIGN_CENTER_HORIZONTAL);
98 
99 		grid->Add(new wxButton(this, XRCID("ID_NEW"), _("&New")));
100 		grid->Add(new wxButton(this, XRCID("ID_DELETE"), _("&Delete")));
101 		grid->Add(new wxButton(this, XRCID("ID_RENAME"), _("&Rename")));
102 		grid->Add(new wxButton(this, XRCID("ID_COPY"), _("&Duplicate")));
103 	}
104 	{
105 		auto right = lay.createFlex(1);
106 		sides->Add(right, lay.grow);
107 		right->AddGrowableCol(0);
108 		right->AddGrowableRow(2);
109 
110 		auto row = lay.createFlex(2);
111 		right->Add(row, lay.grow);
112 		row->AddGrowableCol(1);
113 		row->Add(new wxStaticText(this, -1, _("F&ilter name:")), lay.valign);
114 		row->Add(new wxTextCtrlEx(this, XRCID("ID_NAME"), wxString()), lay.valigng);
115 
116 		row = lay.createFlex(2);
117 		right->Add(row, lay.grow);
118 		row->AddGrowableCol(1);
119 		row->Add(new wxStaticText(this, -1, _("&Filter conditions:")), lay.valign);
120 		auto choice = new wxChoice(this, XRCID("ID_MATCHTYPE"));
121 		row->Add(choice, lay.valigng);
122 
123 		choice->AppendString(_("Filter out items matching all of the following"));
124 		choice->AppendString(_("Filter out items matching any of the following"));
125 		choice->AppendString(_("Filter out items matching none of the following"));
126 		choice->AppendString(_("Filter out items matching not all of the following"));
127 
128 		auto conditions = new wxCustomHeightListCtrl(this, XRCID("ID_CONDITIONS"), wxDefaultPosition, wxDefaultSize, wxVSCROLL | wxSUNKEN_BORDER | wxTAB_TRAVERSAL);
129 		conditions->SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW));
130 		right->Add(conditions, lay.grow)->SetMinSize(wxSize(lay.dlgUnits(200), lay.dlgUnits(100)));
131 
132 		right->Add(new wxCheckBox(this, XRCID("ID_CASE"), _("Conditions are c&ase sensitive")));
133 		row = lay.createFlex(0, 1);
134 		right->Add(row);
135 		row->Add(new wxStaticText(this, -1, _("Filter applies to:")));
136 		row->Add(new wxCheckBox(this, XRCID("ID_FILES"), _("Fil&es")));
137 		row->Add(new wxCheckBox(this, XRCID("ID_DIRS"), _("Dire&ctories")));
138 	}
139 	auto buttons = lay.createButtonSizer(this, main, false);
140 	auto ok = new wxButton(this, wxID_OK, _("OK"));
141 	ok->SetDefault();
142 	buttons->AddButton(ok);
143 	auto cancel = new wxButton(this, wxID_CANCEL, _("Cancel"));
144 	buttons->AddButton(cancel);
145 	buttons->Realize();
146 
147 	Layout();
148 	GetSizer()->Fit(this);
149 	SetMinSize(GetSize());
150 
151 	int conditions = filter_name | filter_size | filter_path | filter_meta | filter_date;
152 	if (has_foreign_type) {
153 		conditions |= filter_foreign;
154 	}
155 	if (!CreateListControl(conditions)) {
156 		return false;
157 	}
158 
159 	m_filters = filters;
160 	m_filterSets = filterSets;
161 	for (auto const& filter: filters) {
162 		filterList_->Append(filter.name);
163 	}
164 
165 	m_pWindowStateManager = new CWindowStateManager(this);
166 	m_pWindowStateManager->Restore(OPTION_FILTEREDIT_SIZE, wxSize(750, 500));
167 
168 	Layout();
169 
170 	SetCtrlState(false);
171 
172 	return true;
173 }
174 
SaveFilter(CFilter & filter)175 void CFilterEditDialog::SaveFilter(CFilter& filter)
176 {
177 	bool const matchCase = XRCCTRL(*this, "ID_CASE", wxCheckBox)->GetValue();
178 	filter = GetFilter(matchCase);
179 	filter.matchCase = matchCase;
180 
181 	filter.filterFiles = XRCCTRL(*this, "ID_FILES", wxCheckBox)->GetValue();
182 	filter.filterDirs = XRCCTRL(*this, "ID_DIRS", wxCheckBox)->GetValue();
183 
184 	filter.name = XRCCTRL(*this, "ID_NAME", wxTextCtrl)->GetValue().ToStdWstring();
185 	if (filter.name != filterList_->GetString(m_currentSelection)) {
186 		int oldSelection = m_currentSelection;
187 		filterList_->Delete(oldSelection);
188 		filterList_->Insert(filter.name, oldSelection);
189 		filterList_->SetSelection(oldSelection);
190 	}
191 }
192 
OnNew(wxCommandEvent &)193 void CFilterEditDialog::OnNew(wxCommandEvent&)
194 {
195 	if (m_currentSelection != -1) {
196 		if (!Validate()) {
197 			return;
198 		}
199 		SaveFilter(m_filters[m_currentSelection]);
200 	}
201 
202 	int index = 1;
203 	std::wstring name = fztranslate("New filter");
204 	std::wstring newName = name;
205 	while (filterList_->FindString(newName) != wxNOT_FOUND) {
206 		newName = fz::sprintf(L"%s (%d)", name, ++index);
207 	}
208 
209 	wxTextEntryDialog dlg(this, _("Please enter a name for the new filter."), _("Enter filter name"), newName);
210 	if (dlg.ShowModal() != wxID_OK) {
211 		return;
212 	}
213 	newName = dlg.GetValue().ToStdWstring();
214 
215 	if (newName.empty()) {
216 		wxMessageBoxEx(_("No filter name given"), _("Cannot create new filter"), wxICON_INFORMATION);
217 		return;
218 	}
219 
220 	if (filterList_->FindString(newName) != wxNOT_FOUND) {
221 		wxMessageBoxEx(_("The entered filter name already exists, please choose a different name."), _("Filter name already exists"), wxICON_ERROR, this);
222 		return;
223 	}
224 
225 	CFilter filter;
226 	filter.name = newName;
227 
228 	m_filters.push_back(filter);
229 
230 	for (auto iter = m_filterSets.begin(); iter != m_filterSets.end(); ++iter) {
231 		CFilterSet& set = *iter;
232 		set.local.push_back(false);
233 		set.remote.push_back(false);
234 	}
235 
236 	int item = filterList_->Append(newName);
237 	filterList_->Select(item);
238 	wxCommandEvent evt;
239 	OnFilterSelect(evt);
240 }
241 
OnDelete(wxCommandEvent &)242 void CFilterEditDialog::OnDelete(wxCommandEvent&)
243 {
244 	int item = filterList_->GetSelection();
245 	if (item == -1) {
246 		return;
247 	}
248 
249 	m_currentSelection = -1;
250 	filterList_->Delete(item);
251 	m_filters.erase(m_filters.begin() + item);
252 
253 	// Remote filter from all filter sets
254 	for (auto & set : m_filterSets) {
255 		set.local.erase(set.local.begin() + item);
256 		set.remote.erase(set.remote.begin() + item);
257 	}
258 
259 	XRCCTRL(*this, "ID_NAME", wxTextCtrl)->ChangeValue(wxString());
260 	ClearFilter();
261 	SetCtrlState(false);
262 }
263 
OnRename(wxCommandEvent &)264 void CFilterEditDialog::OnRename(wxCommandEvent&)
265 {
266 	if (m_currentSelection == -1) {
267 		wxBell();
268 		return;
269 	}
270 
271 	const wxString& oldName = XRCCTRL(*this, "ID_NAME", wxTextCtrl)->GetValue();
272 	wxTextEntryDialog *pDlg = new wxTextEntryDialog(this, _("Please enter a new name for the filter."), _("Enter filter name"), oldName);
273 	pDlg->SetMaxLength(255);
274 	if (pDlg->ShowModal() != wxID_OK) {
275 		delete pDlg;
276 		return;
277 	}
278 
279 	const wxString& newName = pDlg->GetValue();
280 	delete pDlg;
281 
282 	if (newName.empty()) {
283 		wxMessageBoxEx(_("Empty filter names are not allowed."), _("Empty name"), wxICON_ERROR, this);
284 		return;
285 	}
286 
287 	if (newName == oldName) {
288 		return;
289 	}
290 
291 	if (filterList_->FindString(newName) != wxNOT_FOUND) {
292 		wxMessageBoxEx(_("The entered filter name already exists, please choose a different name."), _("Filter name already exists"), wxICON_ERROR, this);
293 		return;
294 	}
295 
296 	filterList_->Delete(m_currentSelection);
297 	filterList_->Insert(newName, m_currentSelection);
298 	filterList_->Select(m_currentSelection);
299 }
300 
OnCopy(wxCommandEvent &)301 void CFilterEditDialog::OnCopy(wxCommandEvent&)
302 {
303 	if (m_currentSelection == -1) {
304 		return;
305 	}
306 
307 	if (!Validate()) {
308 		return;
309 	}
310 	SaveFilter(m_filters[m_currentSelection]);
311 
312 	CFilter filter = m_filters[m_currentSelection];
313 
314 	int index = 1;
315 	std::wstring const& name = filter.name;
316 	std::wstring newName = name;
317 	while (filterList_->FindString(newName) != wxNOT_FOUND) {
318 		newName = fz::sprintf(L"%s (%d)", name, ++index);
319 	}
320 
321 	wxTextEntryDialog dlg(this, _("Please enter a new name for the copied filter."), _("Enter filter name"), newName);
322 	if (dlg.ShowModal() != wxID_OK) {
323 		return;
324 	}
325 
326 	newName = dlg.GetValue().ToStdWstring();
327 	if (newName.empty()) {
328 		wxMessageBoxEx(_("Empty filter names are not allowed."), _("Empty name"), wxICON_ERROR, this);
329 		return;
330 	}
331 
332 	if (filterList_->FindString(newName) != wxNOT_FOUND) {
333 		wxMessageBoxEx(_("The entered filter name already exists, please choose a different name."), _("Filter name already exists"), wxICON_ERROR, this);
334 		return;
335 	}
336 
337 	filter.name = newName;
338 
339 	m_filters.push_back(filter);
340 
341 	for (auto iter = m_filterSets.begin(); iter != m_filterSets.end(); ++iter) {
342 		CFilterSet& set = *iter;
343 		set.local.push_back(false);
344 		set.remote.push_back(false);
345 	}
346 
347 	int item = filterList_->Append(newName);
348 	filterList_->Select(item);
349 	wxCommandEvent evt;
350 	OnFilterSelect(evt);
351 }
352 
OnFilterSelect(wxCommandEvent &)353 void CFilterEditDialog::OnFilterSelect(wxCommandEvent&)
354 {
355 	int item = filterList_->GetSelection();
356 	if (item == -1) {
357 		m_currentSelection = -1;
358 		SetCtrlState(false);
359 		return;
360 	}
361 	else {
362 		SetCtrlState(true);
363 	}
364 
365 	if (item == m_currentSelection) {
366 		return;
367 	}
368 
369 	if (m_currentSelection != -1) {
370 		wxASSERT((unsigned int)m_currentSelection < m_filters.size());
371 
372 		if (!Validate()) {
373 			return;
374 		}
375 
376 		SaveFilter(m_filters[m_currentSelection]);
377 	}
378 
379 	m_currentSelection = item;
380 	filterList_->SetSelection(item); // In case SaveFilter has renamed an item
381 	CFilter filter = m_filters[item];
382 	EditFilter(filter);
383 
384 	XRCCTRL(*this, "ID_CASE", wxCheckBox)->SetValue(filter.matchCase);
385 
386 	XRCCTRL(*this, "ID_FILES", wxCheckBox)->SetValue(filter.filterFiles);
387 	XRCCTRL(*this, "ID_DIRS", wxCheckBox)->SetValue(filter.filterDirs);
388 
389 	XRCCTRL(*this, "ID_NAME", wxTextCtrl)->SetValue(filter.name);
390 }
391 
SetCtrlState(bool enabled)392 void CFilterEditDialog::SetCtrlState(bool enabled)
393 {
394 	XRCCTRL(*this, "ID_CASE", wxCheckBox)->Enable(enabled);
395 	XRCCTRL(*this, "ID_FILES", wxCheckBox)->Enable(enabled);
396 	XRCCTRL(*this, "ID_DIRS", wxCheckBox)->Enable(enabled);
397 }
398 
GetFilters() const399 const std::vector<CFilter>& CFilterEditDialog::GetFilters() const
400 {
401 	return m_filters;
402 }
403 
GetFilterSets() const404 const std::vector<CFilterSet>& CFilterEditDialog::GetFilterSets() const
405 {
406 	return m_filterSets;
407 }
408 
Validate()409 bool CFilterEditDialog::Validate()
410 {
411 	if (m_currentSelection == -1) {
412 		return true;
413 	}
414 
415 	wxString error;
416 	if (!ValidateFilter(error)) {
417 		filterList_->SetSelection(m_currentSelection);
418 		wxMessageBoxEx(error, _("Filter validation failed"), wxICON_ERROR, this);
419 		return false;
420 	}
421 
422 	wxString name = XRCCTRL(*this, "ID_NAME", wxTextCtrl)->GetValue();
423 	if (name.empty()) {
424 		filterList_->SetSelection(m_currentSelection);
425 		XRCCTRL(*this, "ID_NAME", wxTextCtrl)->SetFocus();
426 		wxMessageBoxEx(_("Need to enter filter name"), _("Filter validation failed"), wxICON_ERROR, this);
427 		return false;
428 	}
429 
430 	int pos = filterList_->FindString(name);
431 	if (pos != wxNOT_FOUND && pos != m_currentSelection) {
432 		filterList_->SetSelection(m_currentSelection);
433 		XRCCTRL(*this, "ID_NAME", wxTextCtrl)->SetFocus();
434 		wxMessageBoxEx(_("Filter name already exists"), _("Filter validation failed"), wxICON_ERROR, this);
435 		return false;
436 	}
437 
438 	return true;
439 }
440