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