]> git.saurik.com Git - wxWidgets.git/blob - src/common/dlgcmn.cpp
31c5aa60ec4ad4111ca0c76e45466646ee4c2fd0
[wxWidgets.git] / src / common / dlgcmn.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/common/dlgcmn.cpp
3 // Purpose: common (to all ports) wxDialog functions
4 // Author: Vadim Zeitlin
5 // Modified by:
6 // Created: 28.06.99
7 // RCS-ID: $Id$
8 // Copyright: (c) Vadim Zeitlin
9 // Licence: wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
11
12 // ============================================================================
13 // declarations
14 // ============================================================================
15
16 // ----------------------------------------------------------------------------
17 // headers
18 // ----------------------------------------------------------------------------
19
20 // For compilers that support precompilation, includes "wx.h".
21 #include "wx/wxprec.h"
22
23 #ifdef __BORLANDC__
24 #pragma hdrstop
25 #endif
26
27 #include "wx/dialog.h"
28
29 #ifndef WX_PRECOMP
30 #include "wx/app.h"
31 #include "wx/button.h"
32 #include "wx/dcclient.h"
33 #include "wx/intl.h"
34 #include "wx/settings.h"
35 #include "wx/stattext.h"
36 #include "wx/sizer.h"
37 #include "wx/containr.h"
38 #endif
39
40 #include "wx/statline.h"
41 #include "wx/sysopt.h"
42 #include "wx/module.h"
43 #include "wx/private/stattext.h"
44 #include "wx/bookctrl.h"
45
46 #if wxUSE_DISPLAY
47 #include "wx/display.h"
48 #endif
49
50 // ----------------------------------------------------------------------------
51 // wxDialogBase
52 // ----------------------------------------------------------------------------
53
54 BEGIN_EVENT_TABLE(wxDialogBase, wxTopLevelWindow)
55 EVT_BUTTON(wxID_ANY, wxDialogBase::OnButton)
56
57 EVT_CLOSE(wxDialogBase::OnCloseWindow)
58
59 EVT_CHAR_HOOK(wxDialogBase::OnCharHook)
60 END_EVENT_TABLE()
61
62 wxDialogLayoutAdapter* wxDialogBase::sm_layoutAdapter = NULL;
63 bool wxDialogBase::sm_layoutAdaptation = false;
64
65 void wxDialogBase::Init()
66 {
67 m_returnCode = 0;
68 m_affirmativeId = wxID_OK;
69 m_escapeId = wxID_ANY;
70 m_layoutAdaptationLevel = 3;
71 m_layoutAdaptationDone = FALSE;
72 m_layoutAdaptationMode = wxDIALOG_ADAPTATION_MODE_DEFAULT;
73
74 // the dialogs have this flag on by default to prevent the events from the
75 // dialog controls from reaching the parent frame which is usually
76 // undesirable and can lead to unexpected and hard to find bugs
77 SetExtraStyle(GetExtraStyle() | wxWS_EX_BLOCK_EVENTS);
78 }
79
80 // helper of GetParentForModalDialog()
81 static bool CanBeUsedAsParent(wxWindow *parent)
82 {
83 extern WXDLLIMPEXP_DATA_CORE(wxList) wxPendingDelete;
84
85 return !parent->HasExtraStyle(wxWS_EX_TRANSIENT) &&
86 parent->IsShownOnScreen() &&
87 !wxPendingDelete.Member(parent) &&
88 !parent->IsBeingDeleted();
89 }
90
91 wxWindow *wxDialogBase::GetParentForModalDialog(wxWindow *parent) const
92 {
93 // creating a parent-less modal dialog will result (under e.g. wxGTK2)
94 // in an unfocused dialog, so try to find a valid parent for it:
95 if ( parent )
96 parent = wxGetTopLevelParent(parent);
97
98 if ( !parent || !CanBeUsedAsParent(parent) )
99 parent = wxTheApp->GetTopWindow();
100
101 if ( parent && !CanBeUsedAsParent(parent) )
102 {
103 // can't use this one, it's going to disappear
104 parent = NULL;
105 }
106
107 return parent;
108 }
109
110 #if wxUSE_STATTEXT
111
112 class wxTextSizerWrapper : public wxTextWrapper
113 {
114 public:
115 wxTextSizerWrapper(wxWindow *win)
116 {
117 m_win = win;
118 m_hLine = 0;
119 }
120
121 wxSizer *CreateSizer(const wxString& text, int widthMax)
122 {
123 m_sizer = new wxBoxSizer(wxVERTICAL);
124 Wrap(m_win, text, widthMax);
125 return m_sizer;
126 }
127
128 protected:
129 virtual void OnOutputLine(const wxString& line)
130 {
131 if ( !line.empty() )
132 {
133 m_sizer->Add(new wxStaticText(m_win, wxID_ANY, line));
134 }
135 else // empty line, no need to create a control for it
136 {
137 if ( !m_hLine )
138 m_hLine = m_win->GetCharHeight();
139
140 m_sizer->Add(5, m_hLine);
141 }
142 }
143
144 private:
145 wxWindow *m_win;
146 wxSizer *m_sizer;
147 int m_hLine;
148 };
149
150 wxSizer *wxDialogBase::CreateTextSizer(const wxString& message)
151 {
152 // I admit that this is complete bogus, but it makes
153 // message boxes work for pda screens temporarily..
154 int widthMax = -1;
155 const bool is_pda = wxSystemSettings::GetScreenType() <= wxSYS_SCREEN_PDA;
156 if (is_pda)
157 {
158 widthMax = wxSystemSettings::GetMetric( wxSYS_SCREEN_X ) - 25;
159 }
160
161 // '&' is used as accel mnemonic prefix in the wxWidgets controls but in
162 // the static messages created by CreateTextSizer() (used by wxMessageBox,
163 // for example), we don't want this special meaning, so we need to quote it
164 wxString text(message);
165 text.Replace(_T("&"), _T("&&"));
166
167 wxTextSizerWrapper wrapper(this);
168
169 return wrapper.CreateSizer(text, widthMax);
170 }
171
172 #endif // wxUSE_STATTEXT
173
174 wxSizer *wxDialogBase::CreateButtonSizer(long flags)
175 {
176 wxSizer *sizer = NULL;
177
178 #ifdef __SMARTPHONE__
179 wxDialog* dialog = (wxDialog*) this;
180 if ( flags & wxOK )
181 dialog->SetLeftMenu(wxID_OK);
182
183 if ( flags & wxCANCEL )
184 dialog->SetRightMenu(wxID_CANCEL);
185
186 if ( flags & wxYES )
187 dialog->SetLeftMenu(wxID_YES);
188
189 if ( flags & wxNO )
190 dialog->SetRightMenu(wxID_NO);
191 #else // !__SMARTPHONE__
192
193 #if wxUSE_BUTTON
194
195 #ifdef __POCKETPC__
196 // PocketPC guidelines recommend for Ok/Cancel dialogs to use OK button
197 // located inside caption bar and implement Cancel functionality through
198 // Undo outside dialog. As native behaviour this will be default here but
199 // can be replaced with real wxButtons by setting the option below to 1
200 if ( (flags & ~(wxCANCEL|wxNO_DEFAULT)) != wxOK ||
201 wxSystemOptions::GetOptionInt(wxT("wince.dialog.real-ok-cancel")) )
202 #endif // __POCKETPC__
203 {
204 sizer = CreateStdDialogButtonSizer(flags);
205 }
206 #else // !wxUSE_BUTTON
207 wxUnusedVar(flags);
208 #endif // wxUSE_BUTTON/!wxUSE_BUTTON
209
210 #endif // __SMARTPHONE__/!__SMARTPHONE__
211
212 return sizer;
213 }
214
215 wxSizer *wxDialogBase::CreateSeparatedButtonSizer(long flags)
216 {
217 wxSizer *sizer = CreateButtonSizer(flags);
218 if ( !sizer )
219 return NULL;
220
221 // Mac Human Interface Guidelines recommend not to use static lines as
222 // grouping elements
223 #if wxUSE_STATLINE && !defined(__WXMAC__)
224 wxBoxSizer *topsizer = new wxBoxSizer(wxVERTICAL);
225 topsizer->Add(new wxStaticLine(this),
226 wxSizerFlags().Expand().DoubleBorder(wxBOTTOM));
227 topsizer->Add(sizer, wxSizerFlags().Expand());
228 sizer = topsizer;
229 #endif // wxUSE_STATLINE
230
231 return sizer;
232 }
233
234 #if wxUSE_BUTTON
235
236 wxStdDialogButtonSizer *wxDialogBase::CreateStdDialogButtonSizer( long flags )
237 {
238 wxStdDialogButtonSizer *sizer = new wxStdDialogButtonSizer();
239
240 wxButton *ok = NULL;
241 wxButton *yes = NULL;
242 wxButton *no = NULL;
243
244 if (flags & wxOK)
245 {
246 ok = new wxButton(this, wxID_OK);
247 sizer->AddButton(ok);
248 }
249
250 if (flags & wxCANCEL)
251 {
252 wxButton *cancel = new wxButton(this, wxID_CANCEL);
253 sizer->AddButton(cancel);
254 }
255
256 if (flags & wxYES)
257 {
258 yes = new wxButton(this, wxID_YES);
259 sizer->AddButton(yes);
260 }
261
262 if (flags & wxNO)
263 {
264 no = new wxButton(this, wxID_NO);
265 sizer->AddButton(no);
266 }
267
268 if (flags & wxAPPLY)
269 {
270 wxButton *apply = new wxButton(this, wxID_APPLY);
271 sizer->AddButton(apply);
272 }
273
274 if (flags & wxCLOSE)
275 {
276 wxButton *close = new wxButton(this, wxID_CLOSE);
277 sizer->AddButton(close);
278 }
279
280 if (flags & wxHELP)
281 {
282 wxButton *help = new wxButton(this, wxID_HELP);
283 sizer->AddButton(help);
284 }
285
286 if (flags & wxNO_DEFAULT)
287 {
288 if (no)
289 {
290 no->SetDefault();
291 no->SetFocus();
292 }
293 }
294 else
295 {
296 if (ok)
297 {
298 ok->SetDefault();
299 ok->SetFocus();
300 }
301 else if (yes)
302 {
303 yes->SetDefault();
304 yes->SetFocus();
305 }
306 }
307
308 if (flags & wxOK)
309 SetAffirmativeId(wxID_OK);
310 else if (flags & wxYES)
311 SetAffirmativeId(wxID_YES);
312
313 sizer->Realize();
314
315 return sizer;
316 }
317
318 #endif // wxUSE_BUTTON
319
320 // ----------------------------------------------------------------------------
321 // standard buttons handling
322 // ----------------------------------------------------------------------------
323
324 void wxDialogBase::EndDialog(int rc)
325 {
326 if ( IsModal() )
327 EndModal(rc);
328 else
329 Hide();
330 }
331
332 void wxDialogBase::AcceptAndClose()
333 {
334 if ( Validate() && TransferDataFromWindow() )
335 {
336 EndDialog(m_affirmativeId);
337 }
338 }
339
340 void wxDialogBase::SetAffirmativeId(int affirmativeId)
341 {
342 m_affirmativeId = affirmativeId;
343 }
344
345 void wxDialogBase::SetEscapeId(int escapeId)
346 {
347 m_escapeId = escapeId;
348 }
349
350 bool wxDialogBase::EmulateButtonClickIfPresent(int id)
351 {
352 #if wxUSE_BUTTON
353 wxButton *btn = wxDynamicCast(FindWindow(id), wxButton);
354
355 if ( !btn || !btn->IsEnabled() || !btn->IsShown() )
356 return false;
357
358 wxCommandEvent event(wxEVT_COMMAND_BUTTON_CLICKED, id);
359 event.SetEventObject(btn);
360 btn->GetEventHandler()->ProcessEvent(event);
361
362 return true;
363 #else // !wxUSE_BUTTON
364 wxUnusedVar(id);
365 return false;
366 #endif // wxUSE_BUTTON/!wxUSE_BUTTON
367 }
368
369 bool wxDialogBase::IsEscapeKey(const wxKeyEvent& event)
370 {
371 // for most platforms, Esc key is used to close the dialogs
372 return event.GetKeyCode() == WXK_ESCAPE &&
373 event.GetModifiers() == wxMOD_NONE;
374 }
375
376 void wxDialogBase::OnCharHook(wxKeyEvent& event)
377 {
378 if ( event.GetKeyCode() == WXK_ESCAPE )
379 {
380 int idCancel = GetEscapeId();
381 switch ( idCancel )
382 {
383 case wxID_NONE:
384 // don't handle Esc specially at all
385 break;
386
387 case wxID_ANY:
388 // this value is special: it means translate Esc to wxID_CANCEL
389 // but if there is no such button, then fall back to wxID_OK
390 if ( EmulateButtonClickIfPresent(wxID_CANCEL) )
391 return;
392 idCancel = GetAffirmativeId();
393 // fall through
394
395 default:
396 // translate Esc to button press for the button with given id
397 if ( EmulateButtonClickIfPresent(idCancel) )
398 return;
399 }
400 }
401
402 event.Skip();
403 }
404
405 void wxDialogBase::OnButton(wxCommandEvent& event)
406 {
407 const int id = event.GetId();
408 if ( id == GetAffirmativeId() )
409 {
410 AcceptAndClose();
411 }
412 else if ( id == wxID_APPLY )
413 {
414 if ( Validate() )
415 TransferDataFromWindow();
416
417 // TODO: disable the Apply button until things change again
418 }
419 else if ( id == GetEscapeId() ||
420 (id == wxID_CANCEL && GetEscapeId() == wxID_ANY) )
421 {
422 EndDialog(wxID_CANCEL);
423 }
424 else // not a standard button
425 {
426 event.Skip();
427 }
428 }
429
430 // ----------------------------------------------------------------------------
431 // other event handlers
432 // ----------------------------------------------------------------------------
433
434 void wxDialogBase::OnCloseWindow(wxCloseEvent& WXUNUSED(event))
435 {
436 // We'll send a Cancel message by default, which may close the dialog.
437 // Check for looping if the Cancel event handler calls Close().
438
439 // Note that if a cancel button and handler aren't present in the dialog,
440 // nothing will happen when you close the dialog via the window manager, or
441 // via Close(). We wouldn't want to destroy the dialog by default, since
442 // the dialog may have been created on the stack. However, this does mean
443 // that calling dialog->Close() won't delete the dialog unless the handler
444 // for wxID_CANCEL does so. So use Destroy() if you want to be sure to
445 // destroy the dialog. The default OnCancel (above) simply ends a modal
446 // dialog, and hides a modeless dialog.
447
448 // VZ: this is horrible and MT-unsafe. Can't we reuse some of these global
449 // lists here? don't dare to change it now, but should be done later!
450 static wxList closing;
451
452 if ( closing.Member(this) )
453 return;
454
455 closing.Append(this);
456
457 wxCommandEvent cancelEvent(wxEVT_COMMAND_BUTTON_CLICKED, wxID_CANCEL);
458 cancelEvent.SetEventObject( this );
459 GetEventHandler()->ProcessEvent(cancelEvent); // This may close the dialog
460
461 closing.DeleteObject(this);
462 }
463
464 void wxDialogBase::OnSysColourChanged(wxSysColourChangedEvent& event)
465 {
466 #ifndef __WXGTK__
467 SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_3DFACE));
468 Refresh();
469 #endif
470
471 event.Skip();
472 }
473
474 /// Do the adaptation
475 bool wxDialogBase::DoLayoutAdaptation()
476 {
477 if (GetLayoutAdapter())
478 return GetLayoutAdapter()->DoLayoutAdaptation((wxDialog*) this);
479 else
480 return false;
481 }
482
483 /// Can we do the adaptation?
484 bool wxDialogBase::CanDoLayoutAdaptation()
485 {
486 // Check if local setting overrides the global setting
487 bool layoutEnabled = (GetLayoutAdaptationMode() == wxDIALOG_ADAPTATION_MODE_ENABLED) || (IsLayoutAdaptationEnabled() && (GetLayoutAdaptationMode() != wxDIALOG_ADAPTATION_MODE_DISABLED));
488
489 return (layoutEnabled && !m_layoutAdaptationDone && GetLayoutAdaptationLevel() != 0 && GetLayoutAdapter() != NULL && GetLayoutAdapter()->CanDoLayoutAdaptation((wxDialog*) this));
490 }
491
492 /// Set scrolling adapter class, returning old adapter
493 wxDialogLayoutAdapter* wxDialogBase::SetLayoutAdapter(wxDialogLayoutAdapter* adapter)
494 {
495 wxDialogLayoutAdapter* oldLayoutAdapter = sm_layoutAdapter;
496 sm_layoutAdapter = adapter;
497 return oldLayoutAdapter;
498 }
499
500 /*!
501 * Standard adapter
502 */
503
504 IMPLEMENT_CLASS(wxDialogLayoutAdapter, wxObject)
505
506 IMPLEMENT_CLASS(wxStandardDialogLayoutAdapter, wxDialogLayoutAdapter)
507
508 // Allow for caption size on wxWidgets < 2.9
509 #if defined(__WXGTK__) && !wxCHECK_VERSION(2,9,0)
510 #define wxEXTRA_DIALOG_HEIGHT 30
511 #else
512 #define wxEXTRA_DIALOG_HEIGHT 0
513 #endif
514
515 /// Indicate that adaptation should be done
516 bool wxStandardDialogLayoutAdapter::CanDoLayoutAdaptation(wxDialog* dialog)
517 {
518 if (dialog->GetSizer())
519 {
520 wxSize windowSize, displaySize;
521 return MustScroll(dialog, windowSize, displaySize) != 0;
522 }
523 else
524 return false;
525 }
526
527 bool wxStandardDialogLayoutAdapter::DoLayoutAdaptation(wxDialog* dialog)
528 {
529 if (dialog->GetSizer())
530 {
531 wxBookCtrlBase* bookContentWindow = wxDynamicCast(dialog->GetContentWindow(), wxBookCtrlBase);
532
533 if (bookContentWindow)
534 {
535 // If we have a book control, make all the pages (that use sizers) scrollable
536 wxWindowList windows;
537 for (size_t i = 0; i < bookContentWindow->GetPageCount(); i++)
538 {
539 wxWindow* page = bookContentWindow->GetPage(i);
540
541 wxScrolledWindow* scrolledWindow = wxDynamicCast(page, wxScrolledWindow);
542 if (scrolledWindow)
543 windows.Append(scrolledWindow);
544 else if (!scrolledWindow && page->GetSizer())
545 {
546 // Create a scrolled window and reparent
547 scrolledWindow = CreateScrolledWindow(page);
548 wxSizer* oldSizer = page->GetSizer();
549
550 wxSizer* newSizer = new wxBoxSizer(wxVERTICAL);
551 newSizer->Add(scrolledWindow,1, wxEXPAND, 0);
552
553 page->SetSizer(newSizer, false /* don't delete the old sizer */);
554
555 scrolledWindow->SetSizer(oldSizer);
556
557 ReparentControls(page, scrolledWindow);
558
559 windows.Append(scrolledWindow);
560 }
561 }
562
563 FitWithScrolling(dialog, windows);
564 }
565 else
566 {
567 // If we have an arbitrary dialog, create a scrolling area for the main content, and a button sizer
568 // for the main buttons.
569 wxScrolledWindow* scrolledWindow = CreateScrolledWindow(dialog);
570
571 int buttonSizerBorder = 0;
572
573 // First try to find a wxStdDialogButtonSizer
574 wxSizer* buttonSizer = FindButtonSizer(true /* find std button sizer */, dialog, dialog->GetSizer(), buttonSizerBorder);
575
576 // Next try to find a wxBoxSizer containing the controls
577 if (!buttonSizer && dialog->GetLayoutAdaptationLevel() > wxDIALOG_ADAPTATION_STANDARD_SIZER)
578 buttonSizer = FindButtonSizer(false /* find ordinary sizer */, dialog, dialog->GetSizer(), buttonSizerBorder);
579
580 // If we still don't have a button sizer, collect any 'loose' buttons in the layout
581 if (!buttonSizer && dialog->GetLayoutAdaptationLevel() > wxDIALOG_ADAPTATION_ANY_SIZER)
582 {
583 int count = 0;
584 wxStdDialogButtonSizer* stdButtonSizer = new wxStdDialogButtonSizer;
585 buttonSizer = stdButtonSizer;
586
587 FindLooseButtons(dialog, stdButtonSizer, dialog->GetSizer(), count);
588 if (count > 0)
589 stdButtonSizer->Realize();
590 else
591 {
592 delete buttonSizer;
593 buttonSizer = NULL;
594 }
595 }
596
597 if (buttonSizerBorder == 0)
598 buttonSizerBorder = 5;
599
600 ReparentControls(dialog, scrolledWindow, buttonSizer);
601
602 wxBoxSizer* newTopSizer = new wxBoxSizer(wxVERTICAL);
603 wxSizer* oldSizer = dialog->GetSizer();
604
605 dialog->SetSizer(newTopSizer, false /* don't delete old sizer */);
606
607 newTopSizer->Add(scrolledWindow, 1, wxEXPAND|wxALL, 0);
608 if (buttonSizer)
609 newTopSizer->Add(buttonSizer, 0, wxEXPAND|wxALL, buttonSizerBorder);
610
611 scrolledWindow->SetSizer(oldSizer);
612
613 FitWithScrolling(dialog, scrolledWindow);
614 }
615 }
616
617 dialog->SetLayoutAdaptationDone(true);
618 return true;
619 }
620
621 // Create the scrolled window
622 wxScrolledWindow* wxStandardDialogLayoutAdapter::CreateScrolledWindow(wxWindow* parent)
623 {
624 wxScrolledWindow* scrolledWindow = new wxScrolledWindow(parent, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL|wxVSCROLL|wxHSCROLL|wxBORDER_NONE);
625 return scrolledWindow;
626 }
627
628 /// Find and remove the button sizer, if any
629 wxSizer* wxStandardDialogLayoutAdapter::FindButtonSizer(bool stdButtonSizer, wxDialog* dialog, wxSizer* sizer, int& retBorder, int accumlatedBorder)
630 {
631 for ( wxSizerItemList::compatibility_iterator node = sizer->GetChildren().GetFirst();
632 node; node = node->GetNext() )
633 {
634 wxSizerItem *item = node->GetData();
635 wxSizer *childSizer = item->GetSizer();
636
637 if ( childSizer )
638 {
639 int newBorder = accumlatedBorder;
640 if (item->GetFlag() & wxALL)
641 newBorder += item->GetBorder();
642
643 if (stdButtonSizer) // find wxStdDialogButtonSizer
644 {
645 wxStdDialogButtonSizer* buttonSizer = wxDynamicCast(childSizer, wxStdDialogButtonSizer);
646 if (buttonSizer)
647 {
648 sizer->Detach(childSizer);
649 retBorder = newBorder;
650 return buttonSizer;
651 }
652 }
653 else // find a horizontal box sizer containing standard buttons
654 {
655 wxBoxSizer* buttonSizer = wxDynamicCast(childSizer, wxBoxSizer);
656 if (buttonSizer && IsOrdinaryButtonSizer(dialog, buttonSizer))
657 {
658 sizer->Detach(childSizer);
659 retBorder = newBorder;
660 return buttonSizer;
661 }
662 }
663
664 wxSizer* s = FindButtonSizer(stdButtonSizer, dialog, childSizer, retBorder, newBorder);
665 if (s)
666 return s;
667 }
668 }
669 return NULL;
670 }
671
672 /// Check if this sizer contains standard buttons, and so can be repositioned in the dialog
673 bool wxStandardDialogLayoutAdapter::IsOrdinaryButtonSizer(wxDialog* dialog, wxBoxSizer* sizer)
674 {
675 if (sizer->GetOrientation() != wxHORIZONTAL)
676 return false;
677
678 for ( wxSizerItemList::compatibility_iterator node = sizer->GetChildren().GetFirst();
679 node; node = node->GetNext() )
680 {
681 wxSizerItem *item = node->GetData();
682 wxButton *childButton = wxDynamicCast(item->GetWindow(), wxButton);
683
684 if (childButton && IsStandardButton(dialog, childButton))
685 return true;
686 }
687 return false;
688 }
689
690 /// Check if this is a standard button
691 bool wxStandardDialogLayoutAdapter::IsStandardButton(wxDialog* dialog, wxButton* button)
692 {
693 wxWindowID id = button->GetId();
694
695 return (id == wxID_OK || id == wxID_CANCEL || id == wxID_YES || id == wxID_NO || id == wxID_SAVE ||
696 id == wxID_APPLY || id == wxID_HELP || id == wxID_CONTEXT_HELP || dialog->IsMainButtonId(id));
697 }
698
699 /// Find 'loose' main buttons in the existing layout and add them to the standard dialog sizer
700 bool wxStandardDialogLayoutAdapter::FindLooseButtons(wxDialog* dialog, wxStdDialogButtonSizer* buttonSizer, wxSizer* sizer, int& count)
701 {
702 wxSizerItemList::compatibility_iterator node = sizer->GetChildren().GetFirst();
703 while (node)
704 {
705 wxSizerItemList::compatibility_iterator next = node->GetNext();
706 wxSizerItem *item = node->GetData();
707 wxSizer *childSizer = item->GetSizer();
708 wxButton *childButton = wxDynamicCast(item->GetWindow(), wxButton);
709
710 if (childButton && IsStandardButton(dialog, childButton))
711 {
712 sizer->Detach(childButton);
713 buttonSizer->AddButton(childButton);
714 count ++;
715 }
716
717 if (childSizer)
718 FindLooseButtons(dialog, buttonSizer, childSizer, count);
719
720 node = next;
721 }
722 return true;
723 }
724
725 /// Reparent the controls to the scrolled window
726 void wxStandardDialogLayoutAdapter::ReparentControls(wxWindow* parent, wxWindow* reparentTo, wxSizer* buttonSizer)
727 {
728 DoReparentControls(parent, reparentTo, buttonSizer);
729 }
730
731 void wxStandardDialogLayoutAdapter::DoReparentControls(wxWindow* parent, wxWindow* reparentTo, wxSizer* buttonSizer)
732 {
733 wxWindowList::compatibility_iterator node = parent->GetChildren().GetFirst();
734 while (node)
735 {
736 wxWindowList::compatibility_iterator next = node->GetNext();
737
738 wxWindow *win = node->GetData();
739
740 // Don't reparent the scrolled window or buttons in the button sizer
741 if (win != reparentTo && (!buttonSizer || !buttonSizer->GetItem(win)))
742 {
743 win->Reparent(reparentTo);
744 #ifdef __WXMSW__
745 // Restore correct tab order
746 ::SetWindowPos((HWND) win->GetHWND(), HWND_BOTTOM, -1, -1, -1, -1, SWP_NOMOVE|SWP_NOSIZE);
747 #endif
748 }
749
750 node = next;
751 }
752 }
753
754 /// Find whether scrolling will be necessary for the dialog, returning wxVERTICAL, wxHORIZONTAL or both
755 int wxStandardDialogLayoutAdapter::MustScroll(wxDialog* dialog, wxSize& windowSize, wxSize& displaySize)
756 {
757 return DoMustScroll(dialog, windowSize, displaySize);
758 }
759
760 /// Find whether scrolling will be necessary for the dialog, returning wxVERTICAL, wxHORIZONTAL or both
761 int wxStandardDialogLayoutAdapter::DoMustScroll(wxDialog* dialog, wxSize& windowSize, wxSize& displaySize)
762 {
763 wxSize minWindowSize = dialog->GetSizer()->GetMinSize();
764 windowSize = dialog->GetSize();
765 windowSize = wxSize(wxMax(windowSize.x, minWindowSize.x), wxMax(windowSize.y, minWindowSize.y));
766 #if wxUSE_DISPLAY
767 displaySize = wxDisplay(wxDisplay::GetFromWindow(dialog)).GetClientArea().GetSize();
768 #else
769 displaySize = wxGetClientDisplayRect();
770 #endif
771
772 int flags = 0;
773
774 if (windowSize.y >= (displaySize.y - wxEXTRA_DIALOG_HEIGHT))
775 flags |= wxVERTICAL;
776 if (windowSize.x >= displaySize.x)
777 flags |= wxHORIZONTAL;
778
779 return flags;
780 }
781
782 // A function to fit the dialog around its contents, and then adjust for screen size.
783 // If scrolled windows are passed, scrolling is enabled in the required orientation(s).
784 bool wxStandardDialogLayoutAdapter::FitWithScrolling(wxDialog* dialog, wxWindowList& windows)
785 {
786 return DoFitWithScrolling(dialog, windows);
787 }
788
789 // A function to fit the dialog around its contents, and then adjust for screen size.
790 // If a scrolled window is passed, scrolling is enabled in the required orientation(s).
791 bool wxStandardDialogLayoutAdapter::FitWithScrolling(wxDialog* dialog, wxScrolledWindow* scrolledWindow)
792 {
793 return DoFitWithScrolling(dialog, scrolledWindow);
794 }
795
796 // A function to fit the dialog around its contents, and then adjust for screen size.
797 // If a scrolled window is passed, scrolling is enabled in the required orientation(s).
798 bool wxStandardDialogLayoutAdapter::DoFitWithScrolling(wxDialog* dialog, wxScrolledWindow* scrolledWindow)
799 {
800 wxWindowList windows;
801 windows.Append(scrolledWindow);
802 return DoFitWithScrolling(dialog, windows);
803 }
804
805 bool wxStandardDialogLayoutAdapter::DoFitWithScrolling(wxDialog* dialog, wxWindowList& windows)
806 {
807 wxSizer* sizer = dialog->GetSizer();
808 if (!sizer)
809 return false;
810
811 sizer->SetSizeHints(dialog);
812
813 wxSize windowSize, displaySize;
814 int scrollFlags = DoMustScroll(dialog, windowSize, displaySize);
815 int scrollBarSize = 20;
816
817 if (scrollFlags)
818 {
819 int scrollBarExtraX = 0, scrollBarExtraY = 0;
820 bool resizeHorizontally = (scrollFlags & wxHORIZONTAL) != 0;
821 bool resizeVertically = (scrollFlags & wxVERTICAL) != 0;
822
823 if (windows.GetCount() != 0)
824 {
825 // Allow extra for a scrollbar, assuming we resizing in one direction only.
826 if ((resizeVertically && !resizeHorizontally) && (windowSize.x < (displaySize.x - scrollBarSize)))
827 scrollBarExtraX = scrollBarSize;
828 if ((resizeHorizontally && !resizeVertically) && (windowSize.y < (displaySize.y - scrollBarSize)))
829 scrollBarExtraY = scrollBarSize;
830 }
831
832 wxWindowList::compatibility_iterator node = windows.GetFirst();
833 while (node)
834 {
835 wxWindow *win = node->GetData();
836 wxScrolledWindow* scrolledWindow = wxDynamicCast(win, wxScrolledWindow);
837 if (scrolledWindow)
838 {
839 scrolledWindow->SetScrollRate(resizeHorizontally ? 10 : 0, resizeVertically ? 10 : 0);
840
841 if (scrolledWindow->GetSizer())
842 scrolledWindow->GetSizer()->Fit(scrolledWindow);
843 }
844
845 node = node->GetNext();
846 }
847
848 wxSize limitTo = windowSize + wxSize(scrollBarExtraX, scrollBarExtraY);
849 if (resizeVertically)
850 limitTo.y = displaySize.y - wxEXTRA_DIALOG_HEIGHT;
851 if (resizeHorizontally)
852 limitTo.x = displaySize.x;
853
854 dialog->SetMinSize(limitTo);
855 dialog->SetSize(limitTo);
856
857 dialog->SetSizeHints( limitTo.x, limitTo.y, dialog->GetMaxWidth(), dialog->GetMaxHeight() );
858 }
859
860 return true;
861 }
862
863 /*!
864 * Module to initialise standard adapter
865 */
866
867 class wxDialogLayoutAdapterModule: public wxModule
868 {
869 DECLARE_DYNAMIC_CLASS(wxDialogLayoutAdapterModule)
870 public:
871 wxDialogLayoutAdapterModule() {}
872 virtual void OnExit() { delete wxDialogBase::SetLayoutAdapter(NULL); }
873 virtual bool OnInit() { wxDialogBase::SetLayoutAdapter(new wxStandardDialogLayoutAdapter); return true; }
874 };
875
876 IMPLEMENT_DYNAMIC_CLASS(wxDialogLayoutAdapterModule, wxModule)