1 #include "filezilla.h"
2 #include "dialogex.h"
3 #include "msgbox.h"
4 #include "xrc_helper.h"
5 
6 #include <wx/statline.h>
7 #include <wx/gbsizer.h>
8 
9 #ifdef __WXMAC__
10 #include "filezillaapp.h"
11 #endif
12 
13 BEGIN_EVENT_TABLE(wxDialogEx, wxDialog)
14 EVT_CHAR_HOOK(wxDialogEx::OnChar)
15 END_EVENT_TABLE()
16 
17 std::vector<wxDialogEx*> wxDialogEx::shown_dialogs_;
18 
19 #ifdef __WXMAC__
20 std::vector<void*> wxDialogEx::shown_dialogs_creation_events_;
21 
22 static int const pasteId = wxNewId();
23 static int const copyId = wxNewId();
24 static int const cutId = wxNewId();
25 static int const selectAllId = wxNewId();
26 
27 extern wxTextEntry* GetSpecialTextEntry(wxWindow*, wxChar);
28 
ProcessEvent(wxEvent & event)29 bool wxDialogEx::ProcessEvent(wxEvent& event)
30 {
31 	if (event.GetEventType() != wxEVT_MENU) {
32 		return wxDialog::ProcessEvent(event);
33 	}
34 
35 	wxTextEntry* e = GetSpecialTextEntry(FindFocus(), 'V');
36 	if (e && event.GetId() == pasteId) {
37 		e->Paste();
38 		return true;
39 	}
40 	else if (e && event.GetId() == copyId) {
41 		e->Copy();
42 		return true;
43 	}
44 	else if (e && event.GetId() == cutId) {
45 		e->Cut();
46 		return true;
47 	}
48 	else if (e && event.GetId() == selectAllId) {
49 		e->SelectAll();
50 		return true;
51 	}
52 	else {
53 		return wxDialog::ProcessEvent(event);
54 	}
55 }
56 #endif
57 
Create(wxWindow * parent,int id,wxString const & title,wxPoint const & pos,wxSize const & size,long style)58 bool wxDialogEx::Create(wxWindow* parent, int id, wxString const& title, wxPoint const& pos, wxSize const& size, long style)
59 {
60 	bool ret = wxDialog::Create(parent, id, title, pos, size, style);
61 #ifdef __WXMAC__
62 	if (ret) {
63 		FixPasswordPaste(acceleratorTable_);
64 	}
65 #endif
66 	return ret;
67 }
68 
OnChar(wxKeyEvent & event)69 void wxDialogEx::OnChar(wxKeyEvent& event)
70 {
71 	if (event.GetKeyCode() == WXK_ESCAPE) {
72 		wxCommandEvent cmdEvent(wxEVT_COMMAND_BUTTON_CLICKED, wxID_CANCEL);
73 		ProcessEvent(cmdEvent);
74 	}
75 	else {
76 		event.Skip();
77 	}
78 }
79 
Load(wxWindow * pParent,wxString const & name,std::wstring const & file)80 bool wxDialogEx::Load(wxWindow* pParent, wxString const& name, std::wstring const& file)
81 {
82 	SetParent(pParent);
83 
84 	InitXrc(file);
85 	if (!wxXmlResource::Get()->LoadDialog(this, pParent, name)) {
86 		return false;
87 	}
88 
89 #ifdef __WCMAC__
90 	FixPasswordPaste(acceleratorTable_);
91 #endif
92 
93 	return true;
94 }
95 
SetChildLabel(int id,const wxString & label,unsigned long maxLength)96 bool wxDialogEx::SetChildLabel(int id, const wxString& label, unsigned long maxLength)
97 {
98 	wxWindow* pText = FindWindow(id);
99 	if (!pText) {
100 		return false;
101 	}
102 
103 	if (!maxLength) {
104 		pText->SetLabel(label);
105 	}
106 	else {
107 		wxString wrapped = label;
108 		WrapText(this, wrapped, maxLength);
109 		pText->SetLabel(wrapped);
110 	}
111 
112 	return true;
113 }
114 
SetChildLabel(char const * id,const wxString & label,unsigned long maxLength)115 bool wxDialogEx::SetChildLabel(char const* id, const wxString& label, unsigned long maxLength)
116 {
117 	return SetChildLabel(XRCID(id), label, maxLength);
118 }
119 
GetChildLabel(int id)120 wxString wxDialogEx::GetChildLabel(int id)
121 {
122 	auto pText = dynamic_cast<wxStaticText*>(FindWindow(id));
123 	if (!pText) {
124 		return wxString();
125 	}
126 
127 	return pText->GetLabel();
128 }
129 
ShowModal()130 int wxDialogEx::ShowModal()
131 {
132 	CenterOnParent();
133 
134 #ifdef __WXMSW__
135 	// All open menus need to be closed or app will become unresponsive.
136 	::EndMenu();
137 
138 	// For same reason release mouse capture.
139 	// Could happen during drag&drop with notification dialogs.
140 	::ReleaseCapture();
141 #endif
142 
143 	shown_dialogs_.push_back(this);
144 #ifdef __WXMAC__
145 	shown_dialogs_creation_events_.push_back(wxGetApp().MacGetCurrentEvent());
146 #endif
147 
148 	if (acceleratorTable_.empty()) {
149 		SetAcceleratorTable(wxNullAcceleratorTable);
150 	}
151 	else {
152 		SetAcceleratorTable(wxAcceleratorTable(acceleratorTable_.size(), acceleratorTable_.data()));
153 	}
154 
155 	int ret = wxDialog::ShowModal();
156 
157 #ifdef __WXMAC__
158 	shown_dialogs_creation_events_.pop_back();
159 #endif
160 	shown_dialogs_.pop_back();
161 
162 	return ret;
163 }
164 
ReplaceControl(wxWindow * old,wxWindow * wnd)165 bool wxDialogEx::ReplaceControl(wxWindow* old, wxWindow* wnd)
166 {
167 	if (!GetSizer()->Replace(old, wnd, true)) {
168 		return false;
169 	}
170 	old->Destroy();
171 
172 	return true;
173 }
174 
CanShowPopupDialog(wxTopLevelWindow * parent)175 bool wxDialogEx::CanShowPopupDialog(wxTopLevelWindow * parent)
176 {
177 	if (IsShowingMessageBox()) {
178 		// There already a message box showing
179 		return false;
180 	}
181 
182 	if (!shown_dialogs_.empty() && shown_dialogs_.back() != parent) {
183 		// There is an open dialog which isn't the expected parent
184 		return false;
185 	}
186 
187 	wxMouseState mouseState = wxGetMouseState();
188 	if (mouseState.LeftIsDown() || mouseState.MiddleIsDown() || mouseState.RightIsDown()) {
189 		// Displaying a dialog while the user is clicking is extremely confusing, don't do it.
190 		return false;
191 	}
192 #ifdef __WXMSW__
193 	// During a drag & drop we cannot show a dialog. Doing so can render the program unresponsive
194 	if (GetCapture()) {
195 		return false;
196 	}
197 #endif
198 
199 #ifdef __WXMAC__
200 	void* ev = wxGetApp().MacGetCurrentEvent();
201 	if (ev && (shown_dialogs_creation_events_.empty() || ev != shown_dialogs_creation_events_.back())) {
202 		// We're inside an event handler for a native mac event, such as a popup menu
203 		return false;
204 	}
205 #endif
206 
207 	return true;
208 }
209 
InitDialog()210 void wxDialogEx::InitDialog()
211 {
212 #ifdef __WXGTK__
213 	// Some controls only report proper size after the call to Show(), e.g.
214 	// wxStaticBox::GetBordersForSizer is affected by this.
215 	// Re-fit window to compensate.
216 	wxSizer* s = GetSizer();
217 	if (s) {
218 		wxSize min = GetMinClientSize();
219 		wxSize smin = s->GetMinSize();
220 		if ( min.x < smin.x || min.y < smin.y ) {
221 			s->Fit(this);
222 			SetMinSize(GetSize());
223 		}
224 	}
225 #endif
226 
227 	wxDialog::InitDialog();
228 }
229 
230 
layout()231 DialogLayout const& wxDialogEx::layout()
232 {
233 	if (!layout_) {
234 		layout_ = std::make_unique<DialogLayout>(this);
235 	}
236 
237 	return *layout_;
238 }
239 
240 wxSizerFlags const DialogLayout::grow(wxSizerFlags().Expand());
241 wxSizerFlags const DialogLayout::valign(wxSizerFlags().Align(wxALIGN_CENTER_VERTICAL));
242 wxSizerFlags const DialogLayout::halign(wxSizerFlags().Align(wxALIGN_CENTER_HORIZONTAL));
243 wxSizerFlags const DialogLayout::valigng(wxSizerFlags().Align(wxALIGN_CENTER_VERTICAL).Expand());
244 wxSizerFlags const DialogLayout::ralign(wxSizerFlags().Align(wxALIGN_RIGHT));
245 
DialogLayout(wxTopLevelWindow * parent)246 DialogLayout::DialogLayout(wxTopLevelWindow * parent)
247 	: parent_(parent)
248 {
249 	gap = dlgUnits(3);
250 	border = dlgUnits(3);
251 	indent = dlgUnits(10);
252 }
253 
dlgUnits(int num) const254 int DialogLayout::dlgUnits(int num) const
255 {
256 	return wxDLG_UNIT(parent_, wxPoint(0, num)).y;
257 }
258 
createMain(wxWindow * parent,int cols,int rows) const259 wxFlexGridSizer* DialogLayout::createMain(wxWindow* parent, int cols, int rows) const
260 {
261 	auto outer = new wxBoxSizer(wxVERTICAL);
262 	parent->SetSizer(outer);
263 
264 	auto main = createFlex(cols, rows);
265 	outer->Add(main, 1, wxALL|wxGROW, border);
266 
267 	return main;
268 }
269 
createFlex(int cols,int rows) const270 wxFlexGridSizer* DialogLayout::createFlex(int cols, int rows) const
271 {
272 	int const g = gap;
273 	return new wxFlexGridSizer(rows, cols, g, g);
274 }
275 
createGrid(int cols,int rows) const276 wxGridSizer* DialogLayout::createGrid(int cols, int rows) const
277 {
278 	int const g = gap;
279 	return new wxGridSizer(rows, cols, g, g);
280 }
281 
createGridBag(int cols,int rows) const282 wxGridBagSizer* DialogLayout::createGridBag(int cols, int rows) const
283 {
284 	int const g = gap;
285 	auto bag = new wxGridBagSizer(g, g);
286 	bag->SetCols(cols);
287 	bag->SetRows(rows);
288 	return bag;
289 }
290 
createButtonSizer(wxWindow * parent,wxSizer * sizer,bool hline) const291 wxStdDialogButtonSizer * DialogLayout::createButtonSizer(wxWindow* parent, wxSizer * sizer, bool hline) const
292 {
293 	if (hline) {
294 		sizer->Add(new wxStaticLine(parent), grow);
295 	}
296 	auto btns = new wxStdDialogButtonSizer();
297 	sizer->Add(btns, grow);
298 
299 	return btns;
300 }
301 
gbAddRow(wxGridBagSizer * gb,wxWindow * wnd,wxSizerFlags const & flags) const302 wxSizerItem* DialogLayout::gbAddRow(wxGridBagSizer * gb, wxWindow* wnd, wxSizerFlags const& flags) const
303 {
304 	int row = gb->GetRows();
305 	gb->SetRows(row + 1);
306 
307 	return gb->Add(wnd, wxGBPosition(row, 0), wxGBSpan(1, gb->GetCols()), flags.GetFlags(), flags.GetBorderInPixels());
308 }
309 
gbNewRow(wxGridBagSizer * gb) const310 void DialogLayout::gbNewRow(wxGridBagSizer * gb) const
311 {
312 	gb->SetRows(gb->GetRows() + 1);
313 }
314 
gbAdd(wxGridBagSizer * gb,wxWindow * wnd,wxSizerFlags const & flags) const315 wxSizerItem* DialogLayout::gbAdd(wxGridBagSizer* gb, wxWindow* wnd, wxSizerFlags const& flags) const
316 {
317 	int const row = gb->GetRows() - 1;
318 	int col;
319 	for (col = 0; col < gb->GetCols(); ++col) {
320 		if (!gb->FindItemAtPosition(wxGBPosition(row, col))) {
321 			break;
322 		}
323 	}
324 
325 	auto item = gb->Add(wnd, wxGBPosition(row, col), wxGBSpan(), flags.GetFlags(), flags.GetBorderInPixels());
326 	return item;
327 }
328 
gbAdd(wxGridBagSizer * gb,wxSizer * sizer,wxSizerFlags const & flags) const329 wxSizerItem* DialogLayout::gbAdd(wxGridBagSizer* gb, wxSizer* sizer, wxSizerFlags const& flags) const
330 {
331 	int const row = gb->GetRows() - 1;
332 	int col;
333 	for (col = 0; col < gb->GetCols(); ++col) {
334 		if (!gb->FindItemAtPosition(wxGBPosition(row, col))) {
335 			break;
336 		}
337 	}
338 
339 	auto item = gb->Add(sizer, wxGBPosition(row, col), wxGBSpan(), flags.GetFlags(), flags.GetBorderInPixels());
340 	return item;
341 }
342 
createStatBox(wxSizer * parent,wxString const & title,int cols,int rows) const343 std::tuple<wxStaticBox*, wxFlexGridSizer*> DialogLayout::createStatBox(wxSizer* parent, wxString const& title, int cols, int rows) const
344 {
345 	auto* boxSizer = new wxStaticBoxSizer(wxHORIZONTAL, parent->GetContainingWindow(), title);
346 	auto* box = boxSizer->GetStaticBox();
347 	parent->Add(boxSizer, nullID, wxGROW);
348 
349 	auto* flex = createFlex(cols, rows);
350 #ifdef __WXMSW__
351 	int style = wxGROW | wxLEFT | wxBOTTOM | wxRIGHT;
352 	int const b = dlgUnits(2);
353 #else
354 	int style = wxGROW | wxALL;
355 	int const b = border;
356 #endif
357 	boxSizer->Add(flex, 1, style, b);
358 
359 	return std::make_tuple(box, flex);
360 }
361 
LabelEscape(std::wstring const & label)362 std::wstring LabelEscape(std::wstring const& label)
363 {
364 	return fz::replaced_substrings(label, L"&", L"&&");
365 }
366 
367 #ifdef __WXMAC__
FixPasswordPaste(std::vector<wxAcceleratorEntry> & entries)368 void FixPasswordPaste(std::vector<wxAcceleratorEntry> & entries)
369 {
370 	entries.emplace_back(wxACCEL_CMD, 'V', pasteId);
371 	entries.emplace_back(wxACCEL_CMD, 'C', copyId);
372 	entries.emplace_back(wxACCEL_CMD, 'X', cutId);
373 	entries.emplace_back(wxACCEL_CMD, 'A', selectAllId);
374 }
375 #endif
376