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