]> git.saurik.com Git - wxWidgets.git/blob - src/generic/progdlgg.cpp
Need to reset count when all items are deleted
[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/sizer.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 // macros
51 // ---------------------------------------------------------------------------
52
53 /* Macro for avoiding #ifdefs when value have to be different depending on size of
54 device we display on - take it from something like wxDesktopPolicy in the future
55 */
56
57 #if defined(__SMARTPHONE__)
58 #define wxLARGESMALL(large,small) small
59 #else
60 #define wxLARGESMALL(large,small) large
61 #endif
62
63 // ----------------------------------------------------------------------------
64 // constants
65 // ----------------------------------------------------------------------------
66
67 #define LAYOUT_MARGIN wxLARGESMALL(8,2)
68
69 // ----------------------------------------------------------------------------
70 // private functions
71 // ----------------------------------------------------------------------------
72
73 // update the label to show the given time (in seconds)
74 static void SetTimeLabel(unsigned long val, wxStaticText *label);
75
76 // ----------------------------------------------------------------------------
77 // event tables
78 // ----------------------------------------------------------------------------
79
80 BEGIN_EVENT_TABLE(wxProgressDialog, wxDialog)
81 EVT_BUTTON(wxID_CANCEL, wxProgressDialog::OnCancel)
82
83 EVT_CLOSE(wxProgressDialog::OnClose)
84 END_EVENT_TABLE()
85
86 IMPLEMENT_CLASS(wxProgressDialog, wxDialog)
87
88 // ============================================================================
89 // wxProgressDialog implementation
90 // ============================================================================
91
92 // ----------------------------------------------------------------------------
93 // wxProgressDialog creation
94 // ----------------------------------------------------------------------------
95
96 wxProgressDialog::wxProgressDialog(wxString const &title,
97 wxString const &message,
98 int maximum,
99 wxWindow *parent,
100 int style)
101 : wxDialog(parent, wxID_ANY, title),
102 m_delay(3)
103 {
104 // we may disappear at any moment, let the others know about it
105 SetExtraStyle(GetExtraStyle() | wxWS_EX_TRANSIENT);
106
107 m_windowStyle |= style;
108
109 bool hasAbortButton = (style & wxPD_CAN_ABORT) != 0;
110
111 #if defined(__WXMSW__) && !defined(__WXUNIVERSAL__)
112 // we have to remove the "Close" button from the title bar then as it is
113 // confusing to have it - it doesn't work anyhow
114 //
115 // FIXME: should probably have a (extended?) window style for this
116 if ( !hasAbortButton )
117 {
118 EnableCloseButton(false);
119 }
120 #endif // wxMSW
121
122 #if defined(__SMARTPHONE__)
123 SetLeftMenu();
124 #endif
125
126 m_state = hasAbortButton ? Continue : Uncancelable;
127 m_maximum = maximum;
128
129 #if defined(__WXMSW__) || defined(__WXPM__)
130 // we can't have values > 65,536 in the progress control under Windows, so
131 // scale everything down
132 m_factor = m_maximum / 65536 + 1;
133 m_maximum /= m_factor;
134 #endif // __WXMSW__
135
136 m_parentTop = wxGetTopLevelParent(parent);
137
138 wxClientDC dc(this);
139 dc.SetFont(wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT));
140 long widthText;
141 dc.GetTextExtent(message, &widthText, NULL, NULL, NULL, NULL);
142
143 wxBoxSizer *sizer = new wxBoxSizer(wxVERTICAL);
144
145 m_msg = new wxStaticText(this, wxID_ANY, message);
146 sizer->Add(m_msg, 0, wxLEFT | wxTOP, 2*LAYOUT_MARGIN);
147
148 wxSize sizeDlg,
149 sizeLabel = m_msg->GetSize();
150 sizeDlg.y = 2*LAYOUT_MARGIN + sizeLabel.y;
151
152 if ( maximum > 0 )
153 {
154 // note that we can't use wxGA_SMOOTH because it happens to
155 // cause the dialog to be modal. Have an extra
156 // style argument to wxProgressDialog, perhaps.
157 m_gauge = new wxGauge(this, wxID_ANY, m_maximum,
158 wxDefaultPosition, wxDefaultSize,
159 wxGA_HORIZONTAL);
160
161 sizer->Add(m_gauge, 0, wxLEFT | wxRIGHT | wxTOP | wxEXPAND, 2*LAYOUT_MARGIN);
162 m_gauge->SetValue(0);
163
164 wxSize sizeGauge = m_gauge->GetSize();
165 sizeDlg.y += 2*LAYOUT_MARGIN + sizeGauge.y;
166 }
167 else
168 m_gauge = (wxGauge *)NULL;
169
170 // create the estimated/remaining/total time zones if requested
171 m_elapsed = m_estimated = m_remaining = (wxStaticText*)NULL;
172 m_display_estimated = m_last_timeupdate = m_break = 0;
173 m_ctdelay = 0;
174
175 // if we are going to have at least one label, remmeber it in this var
176 wxStaticText *label = NULL;
177
178 // also count how many labels we really have
179 size_t nTimeLabels = 0;
180
181 if ( style & wxPD_ELAPSED_TIME )
182 {
183 nTimeLabels++;
184
185 label =
186 m_elapsed = CreateLabel(_("Elapsed time : "), sizer);
187 }
188
189 if ( style & wxPD_ESTIMATED_TIME )
190 {
191 nTimeLabels++;
192
193 label =
194 m_estimated = CreateLabel(_("Estimated time : "), sizer);
195 }
196
197 if ( style & wxPD_REMAINING_TIME )
198 {
199 nTimeLabels++;
200
201 label =
202 m_remaining = CreateLabel(_("Remaining time : "), sizer);
203 }
204
205 if ( nTimeLabels > 0 )
206 {
207 // set it to the current time
208 m_timeStart = wxGetCurrentTime();
209 sizeDlg.y += nTimeLabels * (label->GetSize().y + LAYOUT_MARGIN);
210 }
211
212 if ( hasAbortButton )
213 {
214 #if defined(__SMARTPHONE__)
215 SetLeftMenu(wxID_CANCEL, _("Cancel"));
216 }
217 #else
218 m_btnAbort = new wxButton(this, wxID_CANCEL);
219
220 // Windows dialogs usually have buttons in the lower right corner
221 #if defined(__WXMSW__) || defined(__WXPM__)
222 sizer->Add(m_btnAbort, 0, wxALIGN_RIGHT | wxALL, 2*LAYOUT_MARGIN);
223 #else // !MSW
224 sizer->Add(m_btnAbort, 0, wxALIGN_CENTER_HORIZONTAL | wxBOTTOM | wxTOP, 2*LAYOUT_MARGIN);
225 #endif // MSW/!MSW
226 sizeDlg.y += 2*LAYOUT_MARGIN + wxButton::GetDefaultSize().y;
227 }
228 else // no "Cancel" button
229 #endif // __SMARTPHONE__/!__SMARTPHONE__
230 {
231 m_btnAbort = (wxButton *)NULL;
232 }
233
234 SetSizerAndFit(sizer);
235
236 sizeDlg.y += 2*LAYOUT_MARGIN;
237
238 // try to make the dialog not square but rectangular of reasonabel width
239 sizeDlg.x = (wxCoord)wxMax(widthText, 4*sizeDlg.y/3);
240 sizeDlg.x *= 3;
241 sizeDlg.x /= 2;
242 SetClientSize(sizeDlg);
243
244 Centre(wxCENTER_FRAME | wxBOTH);
245
246 if ( style & wxPD_APP_MODAL )
247 {
248 m_winDisabler = new wxWindowDisabler(this);
249 }
250 else
251 {
252 if ( m_parentTop )
253 m_parentTop->Disable();
254 m_winDisabler = NULL;
255 }
256
257 Show();
258 Enable();
259
260 // this one can be initialized even if the others are unknown for now
261 //
262 // NB: do it after calling Layout() to keep the labels correctly aligned
263 if ( m_elapsed )
264 {
265 SetTimeLabel(0, m_elapsed);
266 }
267
268 Update();
269 }
270
271 wxStaticText *wxProgressDialog::CreateLabel(const wxString& text,
272 wxSizer *sizer)
273 {
274 wxBoxSizer *locsizer = new wxBoxSizer(wxLARGESMALL(wxHORIZONTAL,wxVERTICAL));
275
276 wxStaticText *dummy = new wxStaticText(this, wxID_ANY, text);
277 wxStaticText *label = new wxStaticText(this, wxID_ANY, _("unknown"));
278
279 // select placement most native or nice on target GUI
280 #if defined(__SMARTPHONE__)
281 // label and time to the left in two rows
282 locsizer->Add(dummy, 1, wxALIGN_LEFT);
283 locsizer->Add(label, 1, wxALIGN_LEFT);
284 sizer->Add(locsizer, 0, wxALIGN_LEFT | wxTOP | wxLEFT, LAYOUT_MARGIN);
285 #elif defined(__WXMSW__) || defined(__WXPM__) || defined(__WXMAC__)
286 // label and time centered in one row
287 locsizer->Add(dummy, 1, wxLARGESMALL(wxALIGN_RIGHT,wxALIGN_LEFT));
288 locsizer->Add(label, 1, wxALIGN_LEFT | wxLEFT, LAYOUT_MARGIN);
289 sizer->Add(locsizer, 0, wxALIGN_CENTER_HORIZONTAL | wxTOP, LAYOUT_MARGIN);
290 #else
291 // label and time to the right in one row
292 sizer->Add(locsizer, 0, wxALIGN_RIGHT | wxRIGHT | wxTOP, LAYOUT_MARGIN);
293 locsizer->Add(dummy);
294 locsizer->Add(label, 0, wxLEFT, LAYOUT_MARGIN);
295 #endif
296
297 return label;
298 }
299
300 // ----------------------------------------------------------------------------
301 // wxProgressDialog operations
302 // ----------------------------------------------------------------------------
303
304 bool
305 wxProgressDialog::Update(int value, const wxString& newmsg)
306 {
307 wxASSERT_MSG( value == -1 || m_gauge, wxT("cannot update non existent dialog") );
308
309 #ifdef __WXMSW__
310 value /= m_factor;
311 #endif // __WXMSW__
312
313 wxASSERT_MSG( value <= m_maximum, wxT("invalid progress value") );
314
315 // fill up the gauge if value == maximum because this means that the dialog
316 // is going to close and the gauge shouldn't be partly empty in this case
317 if ( m_gauge && value <= m_maximum )
318 {
319 m_gauge->SetValue(value == m_maximum ? value : value + 1);
320 }
321
322 if ( !newmsg.IsEmpty() )
323 {
324 m_msg->SetLabel(newmsg);
325
326 wxYieldIfNeeded() ;
327 }
328
329 if ( (m_elapsed || m_remaining || m_estimated) && (value != 0) )
330 {
331 unsigned long elapsed = wxGetCurrentTime() - m_timeStart;
332 if ( m_last_timeupdate < elapsed
333 || value == m_maximum
334 )
335 {
336 m_last_timeupdate = elapsed;
337 unsigned long estimated = m_break +
338 (unsigned long)(( (double) (elapsed-m_break) * m_maximum ) / ((double)value)) ;
339 if ( estimated > m_display_estimated
340 && m_ctdelay >= 0
341 )
342 {
343 ++m_ctdelay;
344 }
345 else if ( estimated < m_display_estimated
346 && m_ctdelay <= 0
347 )
348 {
349 --m_ctdelay;
350 }
351 else
352 {
353 m_ctdelay = 0;
354 }
355 if ( m_ctdelay >= m_delay // enough confirmations for a higher value
356 || m_ctdelay <= (m_delay*-1) // enough confirmations for a lower value
357 || value == m_maximum // to stay consistent
358 || elapsed > m_display_estimated // to stay consistent
359 || ( elapsed > 0 && elapsed < 4 ) // additional updates in the beginning
360 )
361 {
362 m_display_estimated = estimated;
363 m_ctdelay = 0;
364 }
365 }
366
367 long display_remaining = m_display_estimated - elapsed;
368 if ( display_remaining < 0 )
369 {
370 display_remaining = 0;
371 }
372
373 SetTimeLabel(elapsed, m_elapsed);
374 SetTimeLabel(m_display_estimated, m_estimated);
375 SetTimeLabel(display_remaining, m_remaining);
376 }
377
378 if ( value == m_maximum )
379 {
380 if ( m_state == Finished )
381 {
382 // ignore multiple calls to Update(m_maximum): it may sometimes be
383 // troublesome to ensure that Update() is not called twice with the
384 // same value (e.g. because of the rounding errors) and if we don't
385 // return now we're going to generate asserts below
386 return true;
387 }
388
389 // so that we return true below and that out [Cancel] handler knew what
390 // to do
391 m_state = Finished;
392 if( !(GetWindowStyle() & wxPD_AUTO_HIDE) )
393 {
394 #if defined(__SMARTPHONE__)
395 SetLeftMenu(wxID_CANCEL, _("Close"));
396 #endif
397 if ( m_btnAbort )
398 {
399 // tell the user what he should do...
400 m_btnAbort->SetLabel(_("Close"));
401 }
402 #if defined(__WXMSW__) && !defined(__WXUNIVERSAL__)
403 else // enable the button to give the user a way to close the dlg
404 {
405 EnableCloseButton();
406 }
407 #endif // __WXMSW__
408
409 if ( !newmsg )
410 {
411 // also provide the finishing message if the application didn't
412 m_msg->SetLabel(_("Done."));
413 }
414
415 wxYieldIfNeeded() ;
416
417 (void)ShowModal();
418 }
419 else // auto hide
420 {
421 // reenable other windows before hiding this one because otherwise
422 // Windows wouldn't give the focus back to the window which had
423 // been previously focused because it would still be disabled
424 ReenableOtherWindows();
425
426 Hide();
427 }
428 }
429 else
430 {
431 // we have to yield because not only we want to update the display but
432 // also to process the clicks on the cancel button
433 wxYieldIfNeeded() ;
434 }
435
436 // update the display in case yielding above didn't do it
437 Update();
438
439 return m_state != Canceled;
440 }
441
442 void wxProgressDialog::Resume()
443 {
444 m_state = Continue;
445 m_ctdelay = m_delay; // force an update of the elapsed/estimated/remaining time
446 m_break += wxGetCurrentTime()-m_timeStop;
447
448 // it may have been disabled by OnCancel(), so enable it back to let the
449 // user interrupt us again if needed
450 if(m_btnAbort)
451 m_btnAbort->Enable();
452 #if defined(__SMARTPHONE__)
453 SetLeftMenu(wxID_CANCEL, _("Cancel"));
454 #endif
455 }
456
457 bool wxProgressDialog::Show( bool show )
458 {
459 // reenable other windows before hiding this one because otherwise
460 // Windows wouldn't give the focus back to the window which had
461 // been previously focused because it would still be disabled
462 if(!show)
463 ReenableOtherWindows();
464
465 return wxDialog::Show(show);
466 }
467
468 // ----------------------------------------------------------------------------
469 // event handlers
470 // ----------------------------------------------------------------------------
471
472 void wxProgressDialog::OnCancel(wxCommandEvent& event)
473 {
474 if ( m_state == Finished )
475 {
476 // this means that the count down is already finished and we're being
477 // shown as a modal dialog - so just let the default handler do the job
478 event.Skip();
479 }
480 else
481 {
482 // request to cancel was received, the next time Update() is called we
483 // will handle it
484 m_state = Canceled;
485
486 // update the button state immediately so that the user knows that the
487 // request has been noticed
488 if(m_btnAbort)
489 m_btnAbort->Disable();
490
491 #if defined(__SMARTPHONE__)
492 SetLeftMenu();
493 #endif
494
495 // save the time when the dialog was stopped
496 m_timeStop = wxGetCurrentTime();
497 }
498 }
499
500 void wxProgressDialog::OnClose(wxCloseEvent& event)
501 {
502 if ( m_state == Uncancelable )
503 {
504 // can't close this dialog
505 event.Veto();
506 }
507 else if ( m_state == Finished )
508 {
509 // let the default handler close the window as we already terminated
510 event.Skip();
511 }
512 else
513 {
514 // next Update() will notice it
515 m_state = Canceled;
516 if(m_btnAbort)
517 m_btnAbort->Disable();
518 #if defined(__SMARTPHONE__)
519 SetLeftMenu();
520 #endif
521 m_timeStop = wxGetCurrentTime();
522 }
523 }
524
525 // ----------------------------------------------------------------------------
526 // destruction
527 // ----------------------------------------------------------------------------
528
529 wxProgressDialog::~wxProgressDialog()
530 {
531 // normally this should have been already done, but just in case
532 ReenableOtherWindows();
533 }
534
535 void wxProgressDialog::ReenableOtherWindows()
536 {
537 if ( GetWindowStyle() & wxPD_APP_MODAL )
538 {
539 delete m_winDisabler;
540 m_winDisabler = (wxWindowDisabler *)NULL;
541 }
542 else
543 {
544 if ( m_parentTop )
545 m_parentTop->Enable();
546 }
547 }
548
549 // ----------------------------------------------------------------------------
550 // private functions
551 // ----------------------------------------------------------------------------
552
553 static void SetTimeLabel(unsigned long val, wxStaticText *label)
554 {
555 if ( label )
556 {
557 wxString s;
558 unsigned long hours = val / 3600;
559 unsigned long minutes = (val % 3600) / 60;
560 unsigned long seconds = val % 60;
561 s.Printf(wxT("%lu:%02lu:%02lu"), hours, minutes, seconds);
562
563 if ( s != label->GetLabel() )
564 label->SetLabel(s);
565 }
566 }
567
568 #endif // wxUSE_PROGRESSDLG