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