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