fixed compile error
[wxWidgets.git] / src / generic / progdlgg.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: progdlgg.h
3 // Purpose: wxProgressDialog class
4 // Author: Karsten Ballüder
5 // Modified by:
6 // Created: 09.05.1999
7 // RCS-ID: $Id$
8 // Copyright: (c) Karsten Ballüder
9 // Licence: wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
11
12 // ============================================================================
13 // declarations
14 // ============================================================================
15
16 // ----------------------------------------------------------------------------
17 // headers
18 // ----------------------------------------------------------------------------
19
20 #if defined(__GNUG__) && !defined(NO_GCC_PRAGMA)
21 #pragma implementation "progdlgg.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 #if wxUSE_PROGRESSDLG
32
33 #ifndef WX_PRECOMP
34 #include "wx/utils.h"
35 #include "wx/frame.h"
36 #include "wx/button.h"
37 #include "wx/stattext.h"
38 #include "wx/layout.h"
39 #include "wx/event.h"
40 #include "wx/gauge.h"
41 #include "wx/intl.h"
42 #include "wx/settings.h"
43 #include "wx/dcclient.h"
44 #include "wx/timer.h"
45 #endif
46
47 #include "wx/generic/progdlgg.h"
48
49 // ----------------------------------------------------------------------------
50 // constants
51 // ----------------------------------------------------------------------------
52
53 #define LAYOUT_X_MARGIN 8
54 #define LAYOUT_Y_MARGIN 8
55
56 // ----------------------------------------------------------------------------
57 // private functions
58 // ----------------------------------------------------------------------------
59
60 // update the label to show the given time (in seconds)
61 static void SetTimeLabel(unsigned long val, wxStaticText *label);
62
63 // ----------------------------------------------------------------------------
64 // event tables
65 // ----------------------------------------------------------------------------
66
67 BEGIN_EVENT_TABLE(wxProgressDialog, wxDialog)
68 EVT_BUTTON(wxID_CANCEL, wxProgressDialog::OnCancel)
69
70 EVT_CLOSE(wxProgressDialog::OnClose)
71 END_EVENT_TABLE()
72
73 IMPLEMENT_CLASS(wxProgressDialog, wxDialog)
74
75 // ============================================================================
76 // wxProgressDialog implementation
77 // ============================================================================
78
79 // ----------------------------------------------------------------------------
80 // wxProgressDialog creation
81 // ----------------------------------------------------------------------------
82
83 wxProgressDialog::wxProgressDialog(wxString const &title,
84 wxString const &message,
85 int maximum,
86 wxWindow *parent,
87 int style)
88 : wxDialog(parent, wxID_ANY, title)
89 {
90 // we may disappear at any moment, let the others know about it
91 SetExtraStyle(GetExtraStyle() | wxWS_EX_TRANSIENT);
92
93 m_windowStyle |= style;
94
95 bool hasAbortButton = (style & wxPD_CAN_ABORT) != 0;
96
97 #if defined(__WXMSW__) && !defined(__WXUNIVERSAL__)
98 // we have to remove the "Close" button from the title bar then as it is
99 // confusing to have it - it doesn't work anyhow
100 //
101 // FIXME: should probably have a (extended?) window style for this
102 if ( !hasAbortButton )
103 {
104 EnableCloseButton(false);
105 }
106 #endif // wxMSW
107
108 m_state = hasAbortButton ? Continue : Uncancelable;
109 m_maximum = maximum;
110
111 #if defined(__WXMSW__) || defined(__WXPM__)
112 // we can't have values > 65,536 in the progress control under Windows, so
113 // scale everything down
114 m_factor = m_maximum / 65536 + 1;
115 m_maximum /= m_factor;
116 #endif // __WXMSW__
117
118 m_parentTop = wxGetTopLevelParent(parent);
119
120 wxLayoutConstraints *c;
121
122 wxClientDC dc(this);
123 dc.SetFont(wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT));
124 long widthText;
125 dc.GetTextExtent(message, &widthText, NULL, NULL, NULL, NULL);
126
127 m_msg = new wxStaticText(this, wxID_ANY, message);
128 c = new wxLayoutConstraints;
129 c->left.SameAs(this, wxLeft, 2*LAYOUT_X_MARGIN);
130 c->top.SameAs(this, wxTop, 2*LAYOUT_Y_MARGIN);
131 c->width.AsIs();
132 c->height.AsIs();
133 m_msg->SetConstraints(c);
134
135 wxSize sizeDlg,
136 sizeLabel = m_msg->GetSize();
137 sizeDlg.y = 2*LAYOUT_Y_MARGIN + sizeLabel.y;
138
139 wxWindow *lastWindow = m_msg;
140
141 if ( maximum > 0 )
142 {
143 // note that we can't use wxGA_SMOOTH because it happens to
144 // cause the dialog to be modal. Have an extra
145 // style argument to wxProgressDialog, perhaps.
146 m_gauge = new wxGauge(this, wxID_ANY, m_maximum,
147 wxDefaultPosition, wxDefaultSize,
148 wxGA_HORIZONTAL);
149
150 c = new wxLayoutConstraints;
151 c->left.SameAs(this, wxLeft, 2*LAYOUT_X_MARGIN);
152 c->top.Below(m_msg, 2*LAYOUT_Y_MARGIN);
153 c->right.SameAs(this, wxRight, 2*LAYOUT_X_MARGIN);
154 c->height.AsIs();
155 m_gauge->SetConstraints(c);
156 m_gauge->SetValue(0);
157 lastWindow = m_gauge;
158
159 wxSize sizeGauge = m_gauge->GetSize();
160 sizeDlg.y += 2*LAYOUT_Y_MARGIN + sizeGauge.y;
161 }
162 else
163 m_gauge = (wxGauge *)NULL;
164
165 // create the estimated/remaining/total time zones if requested
166 m_elapsed = m_estimated = m_remaining = (wxStaticText*)NULL;
167
168 // if we are going to have at least one label, remmeber it in this var
169 wxStaticText *label = NULL;
170
171 // also count how many labels we really have
172 size_t nTimeLabels = 0;
173
174 if ( style & wxPD_ELAPSED_TIME )
175 {
176 nTimeLabels++;
177
178 label =
179 m_elapsed = CreateLabel(_("Elapsed time : "), &lastWindow);
180 }
181
182 if ( style & wxPD_ESTIMATED_TIME )
183 {
184 nTimeLabels++;
185
186 label =
187 m_estimated = CreateLabel(_("Estimated time : "), &lastWindow);
188 }
189
190 if ( style & wxPD_REMAINING_TIME )
191 {
192 nTimeLabels++;
193
194 label =
195 m_remaining = CreateLabel(_("Remaining time : "), &lastWindow);
196 }
197
198 if ( nTimeLabels > 0 )
199 {
200 // set it to the current time
201 m_timeStart = wxGetCurrentTime();
202 sizeDlg.y += nTimeLabels * (label->GetSize().y + LAYOUT_Y_MARGIN);
203 }
204
205 if ( hasAbortButton )
206 {
207 m_btnAbort = new wxButton(this, wxID_CANCEL, _("Cancel"));
208 c = new wxLayoutConstraints;
209
210 // Windows dialogs usually have buttons in the lower right corner
211 #if defined(__WXMSW__) || defined(__WXPM__)
212 c->right.SameAs(this, wxRight, 2*LAYOUT_X_MARGIN);
213 #else // !MSW
214 c->centreX.SameAs(this, wxCentreX);
215 #endif // MSW/!MSW
216 c->bottom.SameAs(this, wxBottom, 2*LAYOUT_Y_MARGIN);
217
218 c->width.AsIs();
219 c->height.AsIs();
220
221 m_btnAbort->SetConstraints(c);
222
223 sizeDlg.y += 2*LAYOUT_Y_MARGIN + wxButton::GetDefaultSize().y;
224 }
225 else // no "Cancel" button
226 {
227 m_btnAbort = (wxButton *)NULL;
228 }
229
230 SetAutoLayout(true);
231 Layout();
232
233 sizeDlg.y += 2*LAYOUT_Y_MARGIN;
234
235 // try to make the dialog not square but rectangular of reasonabel width
236 sizeDlg.x = (wxCoord)wxMax(widthText, 4*sizeDlg.y/3);
237 sizeDlg.x *= 3;
238 sizeDlg.x /= 2;
239 SetClientSize(sizeDlg);
240
241 Centre(wxCENTER_FRAME | wxBOTH);
242
243 if ( style & wxPD_APP_MODAL )
244 {
245 m_winDisabler = new wxWindowDisabler(this);
246 }
247 else
248 {
249 if ( m_parentTop )
250 m_parentTop->Disable();
251 m_winDisabler = NULL;
252 }
253
254 Show();
255 Enable();
256
257 // this one can be initialized even if the others are unknown for now
258 //
259 // NB: do it after calling Layout() to keep the labels correctly aligned
260 if ( m_elapsed )
261 {
262 SetTimeLabel(0, m_elapsed);
263 }
264
265 Update();
266 }
267
268 wxStaticText *wxProgressDialog::CreateLabel(const wxString& text,
269 wxWindow **lastWindow)
270 {
271 wxLayoutConstraints *c;
272
273 wxStaticText *label = new wxStaticText(this, wxID_ANY, _("unknown"));
274 c = new wxLayoutConstraints;
275
276 // VZ: I like the labels be centered - if the others don't mind, you may
277 // remove "#ifdef __WXMSW__" and use it for all ports
278 #if defined(__WXMSW__) || defined(__WXPM__) || defined(__WXMAC__)
279 c->left.SameAs(this, wxCentreX, LAYOUT_X_MARGIN);
280 #else // !MSW
281 c->right.SameAs(this, wxRight, 2*LAYOUT_X_MARGIN);
282 #endif // MSW/!MSW
283 c->top.Below(*lastWindow, LAYOUT_Y_MARGIN);
284 c->width.AsIs();
285 c->height.AsIs();
286 label->SetConstraints(c);
287
288 wxStaticText *dummy = new wxStaticText(this, wxID_ANY, text);
289 c = new wxLayoutConstraints;
290 c->right.LeftOf(label);
291 c->top.SameAs(label, wxTop, 0);
292 c->width.AsIs();
293 c->height.AsIs();
294 dummy->SetConstraints(c);
295
296 *lastWindow = label;
297
298 return label;
299 }
300
301 // ----------------------------------------------------------------------------
302 // wxProgressDialog operations
303 // ----------------------------------------------------------------------------
304
305 bool
306 wxProgressDialog::Update(int value, const wxString& newmsg)
307 {
308 wxASSERT_MSG( value == -1 || m_gauge, wxT("cannot update non existent dialog") );
309
310 #ifdef __WXMSW__
311 value /= m_factor;
312 #endif // __WXMSW__
313
314 wxASSERT_MSG( value <= m_maximum, wxT("invalid progress value") );
315
316 // fill up the gauge if value == maximum because this means that the dialog
317 // is going to close and the gauge shouldn't be partly empty in this case
318 if ( m_gauge && value <= m_maximum )
319 {
320 m_gauge->SetValue(value == m_maximum ? value : value + 1);
321 }
322
323 if ( !newmsg.IsEmpty() )
324 {
325 m_msg->SetLabel(newmsg);
326
327 wxYieldIfNeeded() ;
328 }
329
330 if ( (m_elapsed || m_remaining || m_estimated) && (value != 0) )
331 {
332 unsigned long elapsed = wxGetCurrentTime() - m_timeStart;
333 unsigned long estimated = (unsigned long)(( (double) elapsed * m_maximum ) / ((double)value)) ;
334 unsigned long remaining = estimated - elapsed;
335
336 SetTimeLabel(elapsed, m_elapsed);
337 SetTimeLabel(estimated, m_estimated);
338 SetTimeLabel(remaining, m_remaining);
339 }
340
341 if ( value == m_maximum )
342 {
343 // so that we return true below and that out [Cancel] handler knew what
344 // to do
345 m_state = Finished;
346 if( !(GetWindowStyle() & wxPD_AUTO_HIDE) )
347 {
348 if ( m_btnAbort )
349 {
350 // tell the user what he should do...
351 m_btnAbort->SetLabel(_("Close"));
352 }
353 #if defined(__WXMSW__) && !defined(__WXUNIVERSAL__)
354 else // enable the button to give the user a way to close the dlg
355 {
356 EnableCloseButton();
357 }
358 #endif // __WXMSW__
359
360 if ( !newmsg )
361 {
362 // also provide the finishing message if the application didn't
363 m_msg->SetLabel(_("Done."));
364 }
365
366 wxYieldIfNeeded() ;
367
368 (void)ShowModal();
369 }
370 else // auto hide
371 {
372 // reenable other windows before hiding this one because otherwise
373 // Windows wouldn't give the focus back to the window which had
374 // been previously focused because it would still be disabled
375 ReenableOtherWindows();
376
377 Hide();
378 }
379 }
380 else
381 {
382 // we have to yield because not only we want to update the display but
383 // also to process the clicks on the cancel button
384 wxYieldIfNeeded() ;
385 }
386
387 // update the display in case yielding above didn't do it
388 Update();
389
390 return m_state != Canceled;
391 }
392
393 void wxProgressDialog::Resume()
394 {
395 m_state = Continue;
396
397 // it may have been disabled by OnCancel(), so enable it back to let the
398 // user interrupt us again if needed
399 m_btnAbort->Enable();
400 }
401
402 bool wxProgressDialog::Show( bool show )
403 {
404 // reenable other windows before hiding this one because otherwise
405 // Windows wouldn't give the focus back to the window which had
406 // been previously focused because it would still be disabled
407 if(!show)
408 ReenableOtherWindows();
409
410 return wxDialog::Show(show);
411 }
412
413 // ----------------------------------------------------------------------------
414 // event handlers
415 // ----------------------------------------------------------------------------
416
417 void wxProgressDialog::OnCancel(wxCommandEvent& event)
418 {
419 if ( m_state == Finished )
420 {
421 // this means that the count down is already finished and we're being
422 // shown as a modal dialog - so just let the default handler do the job
423 event.Skip();
424 }
425 else
426 {
427 // request to cancel was received, the next time Update() is called we
428 // will handle it
429 m_state = Canceled;
430
431 // update the button state immediately so that the user knows that the
432 // request has been noticed
433 m_btnAbort->Disable();
434 }
435 }
436
437 void wxProgressDialog::OnClose(wxCloseEvent& event)
438 {
439 if ( m_state == Uncancelable )
440 {
441 // can't close this dialog
442 event.Veto();
443 }
444 else if ( m_state == Finished )
445 {
446 // let the default handler close the window as we already terminated
447 event.Skip();
448 }
449 else
450 {
451 // next Update() will notice it
452 m_state = Canceled;
453 }
454 }
455
456 // ----------------------------------------------------------------------------
457 // destruction
458 // ----------------------------------------------------------------------------
459
460 wxProgressDialog::~wxProgressDialog()
461 {
462 // normally this should have been already done, but just in case
463 ReenableOtherWindows();
464 }
465
466 void wxProgressDialog::ReenableOtherWindows()
467 {
468 if ( GetWindowStyle() & wxPD_APP_MODAL )
469 {
470 delete m_winDisabler;
471 m_winDisabler = (wxWindowDisabler *)NULL;
472 }
473 else
474 {
475 if ( m_parentTop )
476 m_parentTop->Enable();
477 }
478 }
479
480 // ----------------------------------------------------------------------------
481 // private functions
482 // ----------------------------------------------------------------------------
483
484 static void SetTimeLabel(unsigned long val, wxStaticText *label)
485 {
486 if ( label )
487 {
488 wxString s;
489 unsigned long hours = val / 3600;
490 unsigned long minutes = (val % 3600) / 60;
491 unsigned long seconds = val % 60;
492 s.Printf(wxT("%lu:%02lu:%02lu"), hours, minutes, seconds);
493
494 if ( s != label->GetLabel() )
495 label->SetLabel(s);
496 }
497 }
498
499 #endif // wxUSE_PROGRESSDLG