]> git.saurik.com Git - wxWidgets.git/blob - src/generic/wizard.cpp
give frame a reasonable default size if none specified
[wxWidgets.git] / src / generic / wizard.cpp
1 ///////////////////////////////////////////////////////////////////////////////
2 // Name: generic/wizard.cpp
3 // Purpose: generic implementation of wxWizard class
4 // Author: Vadim Zeitlin
5 // Modified by:
6 // Created: 15.08.99
7 // RCS-ID: $Id$
8 // Copyright: (c) 1999 Vadim Zeitlin <zeitlin@dptmaths.ens-cachan.fr>
9 // Licence: wxWindows license
10 ///////////////////////////////////////////////////////////////////////////////
11
12 // ============================================================================
13 // declarations
14 // ============================================================================
15
16 // ----------------------------------------------------------------------------
17 // headers
18 // ----------------------------------------------------------------------------
19
20 #ifdef __GNUG__
21 #pragma implementation ".h"
22 #endif
23
24 // For compilers that support precompilation, includes "wx.h".
25 #include "wx/wxprec.h"
26
27 #ifdef __BORLANDC__
28 #pragma hdrstop
29 #endif
30
31 #ifndef WX_PRECOMP
32 #include "wx/dynarray.h"
33 #include "wx/intl.h"
34 #include "wx/statbmp.h"
35 #include "wx/button.h"
36 #endif //WX_PRECOMP
37
38 #include "wx/statline.h"
39
40 #include "wx/wizard.h"
41
42 // ----------------------------------------------------------------------------
43 // simple types
44 // ----------------------------------------------------------------------------
45
46 WX_DEFINE_ARRAY(wxPanel *, wxArrayPages);
47
48 // ----------------------------------------------------------------------------
49 // event tables and such
50 // ----------------------------------------------------------------------------
51
52 BEGIN_EVENT_TABLE(wxWizard, wxDialog)
53 EVT_BUTTON(wxID_CANCEL, wxWizard::OnCancel)
54 EVT_BUTTON(-1, wxWizard::OnBackOrNext)
55 END_EVENT_TABLE()
56
57 IMPLEMENT_DYNAMIC_CLASS(wxWizard, wxDialog)
58 IMPLEMENT_ABSTRACT_CLASS(wxWizardPage, wxPanel)
59 IMPLEMENT_DYNAMIC_CLASS(wxWizardPageSimple, wxWizardPage)
60 IMPLEMENT_DYNAMIC_CLASS(wxWizardEvent, wxNotifyEvent)
61
62 // ============================================================================
63 // implementation
64 // ============================================================================
65
66 // ----------------------------------------------------------------------------
67 // wxWizardPage
68 // ----------------------------------------------------------------------------
69
70 wxWizardPage::wxWizardPage(wxWizard *parent, const wxBitmap& bitmap)
71 : wxPanel(parent), m_bitmap(bitmap)
72 {
73 // initially the page is hidden, it's shown only when it becomes current
74 Hide();
75 }
76
77 // ----------------------------------------------------------------------------
78 // wxWizardPageSimple
79 // ----------------------------------------------------------------------------
80
81 wxWizardPage *wxWizardPageSimple::GetPrev() const
82 {
83 return m_prev;
84 }
85
86 wxWizardPage *wxWizardPageSimple::GetNext() const
87 {
88 return m_next;
89 }
90 // ----------------------------------------------------------------------------
91 // generic wxWizard implementation
92 // ----------------------------------------------------------------------------
93
94 wxWizard::wxWizard(wxWindow *parent,
95 int id,
96 const wxString& title,
97 const wxBitmap& bitmap,
98 const wxPoint& pos)
99 : m_posWizard(pos), m_bitmap(bitmap)
100 {
101 // just create the dialog itself here, the controls will be created in
102 // DoCreateControls() called later when we know our final size
103 m_page = (wxWizardPage *)NULL;
104 m_btnPrev = m_btnNext = NULL;
105 m_statbmp = NULL;
106
107 (void)wxDialog::Create(parent, id, title, pos);
108 }
109
110 void wxWizard::DoCreateControls()
111 {
112 // do nothing if the controls were already created
113 if ( WasCreated() )
114 return;
115
116 // constants defining the dialog layout
117 // ------------------------------------
118
119 // these constants define the position of the upper left corner of the
120 // bitmap or the page in the wizard
121 static const int X_MARGIN = 10;
122 static const int Y_MARGIN = 10;
123
124 // margin between the bitmap and the panel
125 static const int BITMAP_X_MARGIN = 15;
126
127 // margin between the bitmap and the static line
128 static const int BITMAP_Y_MARGIN = 15;
129
130 // margin between the static line and the buttons
131 static const int SEPARATOR_LINE_MARGIN = 15;
132
133 // margin between "Next >" and "Cancel" buttons
134 static const int BUTTON_MARGIN = 10;
135
136 // default width and height of the page
137 static const int DEFAULT_PAGE_WIDTH = 270;
138 static const int DEFAULT_PAGE_HEIGHT = 290;
139
140 // create controls
141 // ---------------
142
143 wxSize sizeBtn = wxButton::GetDefaultSize();
144
145 // the global dialog layout is: a row of buttons at the bottom (aligned to
146 // the right), the static line above them, the bitmap (if any) on the left
147 // of the upper part of the dialog and the panel in the remaining space
148 m_x = X_MARGIN;
149 m_y = Y_MARGIN;
150
151 int defaultHeight;
152 if ( m_bitmap.Ok() )
153 {
154 m_statbmp = new wxStaticBitmap(this, -1, m_bitmap, wxPoint(m_x, m_y));
155
156 m_x += m_bitmap.GetWidth() + BITMAP_X_MARGIN;
157
158 defaultHeight = m_bitmap.GetHeight();
159 }
160 else
161 {
162 m_statbmp = (wxStaticBitmap *)NULL;
163
164 defaultHeight = DEFAULT_PAGE_HEIGHT;
165 }
166
167 // use default size if none given and also make sure that the dialog is
168 // not less than the default size
169 m_height = m_sizePage.y == -1 ? defaultHeight : m_sizePage.y;
170 m_width = m_sizePage.x == -1 ? DEFAULT_PAGE_WIDTH : m_sizePage.x;
171 if ( m_height < defaultHeight )
172 m_height = defaultHeight;
173 if ( m_width < DEFAULT_PAGE_WIDTH )
174 m_width = DEFAULT_PAGE_WIDTH;
175
176 int x = X_MARGIN;
177 int y = m_y + m_height + BITMAP_Y_MARGIN;
178
179 #if wxUSE_STATLINE
180 (void)new wxStaticLine(this, -1, wxPoint(x, y),
181 wxSize(m_x + m_width - x, 2));
182 #endif // wxUSE_STATLINE
183
184 x = m_x + m_width - 3*sizeBtn.x - BUTTON_MARGIN;
185 y += SEPARATOR_LINE_MARGIN;
186 m_btnPrev = new wxButton(this, wxID_BACKWARD, _("< &Back"), wxPoint(x, y), sizeBtn);
187
188 x += sizeBtn.x;
189 m_btnNext = new wxButton(this, wxID_FORWARD, _("&Next >"), wxPoint(x, y), sizeBtn);
190
191 x += sizeBtn.x + BUTTON_MARGIN;
192 (void)new wxButton(this, wxID_CANCEL, _("Cancel"), wxPoint(x, y), sizeBtn);
193
194 // position and size the dialog
195 // ----------------------------
196
197 SetClientSize(m_x + m_width + X_MARGIN,
198 m_y + m_height + BITMAP_Y_MARGIN +
199 SEPARATOR_LINE_MARGIN + sizeBtn.y + Y_MARGIN);
200
201 if ( m_posWizard == wxDefaultPosition )
202 {
203 CentreOnScreen();
204 }
205 }
206
207 void wxWizard::SetPageSize(const wxSize& size)
208 {
209 // otherwise it will have no effect now as it's too late...
210 wxASSERT_MSG( !WasCreated(), _T("should be called before RunWizard()!") );
211
212 m_sizePage = size;
213 }
214
215 bool wxWizard::ShowPage(wxWizardPage *page, bool goingForward)
216 {
217 wxASSERT_MSG( page != m_page, wxT("this is useless") );
218
219 // we'll use this to decide whether we have to change the label of this
220 // button or not (initially the label is "Next")
221 bool btnLabelWasNext = TRUE;
222
223 // and this tells us whether we already had the default bitmap before
224 bool bmpWasDefault = TRUE;
225
226 if ( m_page )
227 {
228 // send the event to the old page
229 wxWizardEvent event(wxEVT_WIZARD_PAGE_CHANGING, GetId(), goingForward);
230 if ( m_page->GetEventHandler()->ProcessEvent(event) &&
231 !event.IsAllowed() )
232 {
233 // vetoed by the page
234 return FALSE;
235 }
236
237 m_page->Hide();
238
239 btnLabelWasNext = m_page->GetNext() != (wxWizardPage *)NULL;
240 bmpWasDefault = !m_page->GetBitmap().Ok();
241 }
242
243 // set the new one
244 m_page = page;
245
246 // is this the end?
247 if ( !m_page )
248 {
249 // terminate successfully
250 EndModal(wxID_OK);
251
252 return TRUE;
253 }
254
255 // send the event to the new page now
256 wxWizardEvent event(wxEVT_WIZARD_PAGE_CHANGED, GetId(), goingForward);
257 (void)m_page->GetEventHandler()->ProcessEvent(event);
258
259 // position and show the new page
260 (void)m_page->TransferDataToWindow();
261 m_page->SetSize(m_x, m_y, m_width, m_height);
262 m_page->Show();
263
264 // change the bitmap if necessary (and if we have it at all)
265 bool bmpIsDefault = !m_page->GetBitmap().Ok();
266 if ( m_statbmp && (bmpIsDefault != bmpWasDefault) )
267 {
268 wxBitmap bmp;
269 if ( bmpIsDefault )
270 bmp = m_bitmap;
271 else
272 bmp = m_page->GetBitmap();
273 m_statbmp->SetBitmap(bmp);
274 }
275
276 // and update the buttons state
277 m_btnPrev->Enable(m_page->GetPrev() != (wxWizardPage *)NULL);
278
279 bool hasNext = m_page->GetNext() != (wxWizardPage *)NULL;
280 if ( btnLabelWasNext != hasNext )
281 {
282 // need to update
283 if (btnLabelWasNext)
284 m_btnNext->SetLabel(_("&Finish"));
285 else
286 m_btnNext->SetLabel(_("&Next >"));
287 }
288 // nothing to do: the label was already correct
289
290 return TRUE;
291 }
292
293 bool wxWizard::RunWizard(wxWizardPage *firstPage)
294 {
295 wxCHECK_MSG( firstPage, FALSE, wxT("can't run empty wizard") );
296
297 DoCreateControls();
298
299 // can't return FALSE here because there is no old page
300 (void)ShowPage(firstPage, TRUE /* forward */);
301
302 return ShowModal() == wxID_OK;
303 }
304
305 wxWizardPage *wxWizard::GetCurrentPage() const
306 {
307 return m_page;
308 }
309
310 wxSize wxWizard::GetPageSize() const
311 {
312 // make sure that the controls are created because otherwise m_width and
313 // m_height would be both still -1
314 wxConstCast(this, wxWizard)->DoCreateControls();
315
316 return wxSize(m_width, m_height);
317 }
318
319 void wxWizard::OnCancel(wxCommandEvent& WXUNUSED(event))
320 {
321 // this function probably can never be called when we don't have an active
322 // page, but a small extra check won't hurt
323 wxWindow *win = m_page ? (wxWindow *)m_page : (wxWindow *)this;
324
325 wxWizardEvent event(wxEVT_WIZARD_CANCEL, GetId());
326 if ( !win->GetEventHandler()->ProcessEvent(event) || event.IsAllowed() )
327 {
328 // no objections - close the dialog
329 EndModal(wxID_CANCEL);
330 }
331 //else: request to Cancel ignored
332 }
333
334 void wxWizard::OnBackOrNext(wxCommandEvent& event)
335 {
336 wxASSERT_MSG( (event.GetEventObject() == m_btnNext) ||
337 (event.GetEventObject() == m_btnPrev),
338 wxT("unknown button") );
339
340 // ask the current page first: notice that we do it before calling
341 // GetNext/Prev() because the data transfered from the controls of the page
342 // may change the value returned by these methods
343 if ( m_page && !m_page->TransferDataFromWindow() )
344 {
345 // the page data is incorrect, don't do anything
346 return;
347 }
348
349 bool forward = event.GetEventObject() == m_btnNext;
350
351 wxWizardPage *page;
352 if ( forward )
353 {
354 page = m_page->GetNext();
355 }
356 else // back
357 {
358 page = m_page->GetPrev();
359
360 wxASSERT_MSG( page, wxT("\"<Back\" button should have been disabled") );
361 }
362
363 // just pass to the new page (or may be not - but we don't care here)
364 (void)ShowPage(page, forward);
365 }
366
367 // ----------------------------------------------------------------------------
368 // our public interface
369 // ----------------------------------------------------------------------------
370
371 /* static */
372 wxWizard *wxWizardBase::Create(wxWindow *parent,
373 int id,
374 const wxString& title,
375 const wxBitmap& bitmap,
376 const wxPoint& pos,
377 const wxSize& WXUNUSED(size))
378 {
379 return new wxWizard(parent, id, title, bitmap, pos);
380 }
381
382 // ----------------------------------------------------------------------------
383 // wxWizardEvent
384 // ----------------------------------------------------------------------------
385
386 wxWizardEvent::wxWizardEvent(wxEventType type, int id, bool direction)
387 : wxNotifyEvent(type, id)
388 {
389 m_direction = direction;
390 }
391