Fit modeless preferences dialog to its contents too.
[wxWidgets.git] / src / generic / preferencesg.cpp
1 ///////////////////////////////////////////////////////////////////////////////
2 // Name: src/generic/preferencesg.cpp
3 // Purpose: Implementation of wxPreferencesEditor.
4 // Author: Vaclav Slavik
5 // Created: 2013-02-19
6 // RCS-ID: $Id$
7 // Copyright: (c) 2013 Vaclav Slavik <vslavik@fastmail.fm>
8 // Licence: wxWindows licence
9 ///////////////////////////////////////////////////////////////////////////////
10
11 // ============================================================================
12 // declarations
13 // ============================================================================
14
15 // ----------------------------------------------------------------------------
16 // headers
17 // ----------------------------------------------------------------------------
18
19 // for compilers that support precompilation, includes "wx.h".
20 #include "wx/wxprec.h"
21
22 #ifdef __BORLANDC__
23 #pragma hdrstop
24 #endif
25
26 #include "wx/private/preferences.h"
27
28 #ifndef wxHAS_PREF_EDITOR_NATIVE
29
30 #include "wx/app.h"
31 #include "wx/dialog.h"
32 #include "wx/notebook.h"
33 #include "wx/sizer.h"
34 #include "wx/sharedptr.h"
35 #include "wx/scopedptr.h"
36 #include "wx/scopeguard.h"
37 #include "wx/vector.h"
38
39 namespace
40 {
41
42 class wxGenericPrefsDialog : public wxDialog
43 {
44 public:
45 wxGenericPrefsDialog(wxWindow *parent, const wxString& title)
46 : wxDialog(parent, wxID_ANY, title,
47 wxDefaultPosition, wxDefaultSize,
48 wxDEFAULT_FRAME_STYLE & ~(wxRESIZE_BORDER | wxMAXIMIZE_BOX | wxMINIMIZE_BOX))
49 {
50 SetExtraStyle(wxWS_EX_VALIDATE_RECURSIVELY);
51
52 wxSizer *sizer = new wxBoxSizer(wxVERTICAL);
53
54 m_notebook = new wxNotebook(this, wxID_ANY);
55 sizer->Add(m_notebook, wxSizerFlags(1).Expand().DoubleBorder());
56
57 #ifdef __WXGTK__
58 SetEscapeId(wxID_CLOSE);
59 sizer->Add(CreateButtonSizer(wxCLOSE), wxSizerFlags().Expand().DoubleBorder(wxBOTTOM));
60 #else
61 sizer->Add(CreateButtonSizer(wxOK | wxCANCEL),
62 wxSizerFlags().Expand().DoubleBorder(wxLEFT|wxRIGHT|wxBOTTOM));
63 #endif
64 SetSizer(sizer);
65 }
66
67 void AddPage(wxPreferencesPage *page)
68 {
69 wxWindow *win = page->CreateWindow(m_notebook);
70 m_notebook->AddPage(win, page->GetName());
71 }
72
73 int GetSelectedPage() const
74 {
75 return m_notebook->GetSelection();
76 }
77
78 void SelectPage(int page)
79 {
80 m_notebook->ChangeSelection(page);
81 }
82
83 private:
84 wxNotebook *m_notebook;
85 };
86
87
88 class wxGenericPreferencesEditorImplBase : public wxPreferencesEditorImpl
89 {
90 public:
91 void SetTitle(const wxString& title)
92 {
93 m_title = title;
94 }
95
96 virtual void AddPage(wxPreferencesPage* page)
97 {
98 m_pages.push_back(wxSharedPtr<wxPreferencesPage>(page));
99 }
100
101 protected:
102 wxGenericPrefsDialog *CreateDialog(wxWindow *parent)
103 {
104 if ( m_title.empty() )
105 {
106 // Use the default title, which should include the application name
107 // under both MSW and GTK (and OSX uses its own native
108 // implementation anyhow).
109 m_title.Printf(_("%s Preferences"), wxTheApp->GetAppDisplayName());
110 }
111
112 wxGenericPrefsDialog *dlg = new wxGenericPrefsDialog(parent, m_title);
113
114 // TODO: Don't create all pages immediately like this, do it on demand
115 // when a page is selected in the notebook (as is done on OS X).
116 //
117 // Currently, creating all pages is necessary so that the notebook
118 // can determine its best size. We'll need to extend
119 // wxPreferencesPage with a GetBestSize() virtual method to make
120 // it possible to defer the creation.
121 for ( Pages::const_iterator i = m_pages.begin();
122 i != m_pages.end();
123 ++i )
124 {
125 dlg->AddPage(i->get());
126 }
127
128 dlg->Fit();
129
130 return dlg;
131 }
132
133 typedef wxVector< wxSharedPtr<wxPreferencesPage> > Pages;
134 Pages m_pages;
135
136 private:
137 wxString m_title;
138 };
139
140
141 #ifdef wxHAS_PREF_EDITOR_MODELESS
142
143 class wxModelessPreferencesEditorImpl : public wxGenericPreferencesEditorImplBase
144 {
145 public:
146 virtual ~wxModelessPreferencesEditorImpl()
147 {
148 // m_win may already be destroyed if this destructor is called from
149 // wxApp's destructor. In that case, all windows -- including this
150 // one -- would already be destroyed by now.
151 if ( m_win )
152 m_win->Destroy();
153 }
154
155 virtual void Show(wxWindow* parent)
156 {
157 if ( !m_win )
158 {
159 wxWindow *win = CreateDialog(parent);
160 win->Show();
161 m_win = win;
162 }
163 else
164 {
165 // Ideally, we'd reparent the dialog under 'parent', but it's
166 // probably not worth the hassle. We know the old parent is still
167 // valid, because otherwise Dismiss() would have been called and
168 // m_win cleared.
169 m_win->Raise();
170 }
171 }
172
173 virtual void Dismiss()
174 {
175 if ( m_win )
176 {
177 m_win->Close(/*force=*/true);
178 m_win = NULL;
179 }
180 }
181
182 private:
183 wxWeakRef<wxWindow> m_win;
184 };
185
186 inline
187 wxGenericPreferencesEditorImplBase* NewGenericImpl()
188 {
189 return new wxModelessPreferencesEditorImpl;
190 }
191
192 #else // !wxHAS_PREF_EDITOR_MODELESS
193
194 class wxModalPreferencesEditorImpl : public wxGenericPreferencesEditorImplBase
195 {
196 public:
197 wxModalPreferencesEditorImpl()
198 {
199 m_dlg = NULL;
200 m_currentPage = -1;
201 }
202
203 virtual void Show(wxWindow* parent)
204 {
205 wxScopedPtr<wxGenericPrefsDialog> dlg(CreateDialog(parent));
206
207 // Store it for Dismiss() but ensure that the pointer is reset to NULL
208 // when the dialog is destroyed on leaving this function.
209 m_dlg = dlg.get();
210 wxON_BLOCK_EXIT_NULL(m_dlg);
211
212 // Restore the previously selected page, if any.
213 if ( m_currentPage != -1 )
214 dlg->SelectPage(m_currentPage);
215
216 // Don't remember the last selected page if the dialog was cancelled.
217 if ( dlg->ShowModal() != wxID_CANCEL )
218 m_currentPage = dlg->GetSelectedPage();
219 }
220
221 virtual void Dismiss()
222 {
223 if ( m_dlg )
224 {
225 m_dlg->EndModal(wxID_CANCEL);
226 m_dlg = NULL;
227 }
228 }
229
230 private:
231 wxGenericPrefsDialog* m_dlg;
232 int m_currentPage;
233
234 wxDECLARE_NO_COPY_CLASS(wxModalPreferencesEditorImpl);
235 };
236
237 inline
238 wxGenericPreferencesEditorImplBase* NewGenericImpl()
239 {
240 return new wxModalPreferencesEditorImpl;
241 }
242
243 #endif // !wxHAS_PREF_EDITOR_MODELESS
244
245 } // anonymous namespace
246
247 /*static*/
248 wxPreferencesEditorImpl* wxPreferencesEditorImpl::Create(const wxString& title)
249 {
250 wxGenericPreferencesEditorImplBase* const impl = NewGenericImpl();
251
252 impl->SetTitle(title);
253
254 return impl;
255 }
256
257 #endif // !wxHAS_PREF_EDITOR_NATIVE