1 ///////////////////////////////////////////////////////////////////////////// 
   3 // Purpose:     wxProgressDialog class 
   4 // Author:      Karsten Ballüder 
   8 // Copyright:   (c) Karsten Ballüder 
   9 // Licence:     wxWindows licence 
  10 ///////////////////////////////////////////////////////////////////////////// 
  12 // ============================================================================ 
  14 // ============================================================================ 
  16 // ---------------------------------------------------------------------------- 
  18 // ---------------------------------------------------------------------------- 
  20 // For compilers that support precompilation, includes "wx.h". 
  21 #include "wx/wxprec.h" 
  32     #include "wx/button.h" 
  33     #include "wx/stattext.h" 
  38     #include "wx/dcclient.h" 
  42 #include "wx/generic/progdlgg.h" 
  43 #include "wx/settings.h" 
  45 // --------------------------------------------------------------------------- 
  47 // --------------------------------------------------------------------------- 
  49 /* Macro for avoiding #ifdefs when value have to be different depending on size of 
  50    device we display on - take it from something like wxDesktopPolicy in the future 
  53 #if defined(__SMARTPHONE__) 
  54     #define wxLARGESMALL(large,small) small 
  56     #define wxLARGESMALL(large,small) large 
  59 // ---------------------------------------------------------------------------- 
  61 // ---------------------------------------------------------------------------- 
  63 #define LAYOUT_MARGIN wxLARGESMALL(8,2) 
  65 static const int wxID_SKIP 
= 32000;  // whatever 
  67 // ---------------------------------------------------------------------------- 
  69 // ---------------------------------------------------------------------------- 
  71 // update the label to show the given time (in seconds) 
  72 static void SetTimeLabel(unsigned long val
, wxStaticText 
*label
); 
  74 // ---------------------------------------------------------------------------- 
  76 // ---------------------------------------------------------------------------- 
  78 BEGIN_EVENT_TABLE(wxProgressDialog
, wxDialog
) 
  79     EVT_BUTTON(wxID_CANCEL
, wxProgressDialog::OnCancel
) 
  80     EVT_BUTTON(wxID_SKIP
, wxProgressDialog::OnSkip
) 
  82     EVT_CLOSE(wxProgressDialog::OnClose
) 
  85 IMPLEMENT_CLASS(wxProgressDialog
, wxDialog
) 
  87 // ============================================================================ 
  88 // wxProgressDialog implementation 
  89 // ============================================================================ 
  91 // ---------------------------------------------------------------------------- 
  92 // wxProgressDialog creation 
  93 // ---------------------------------------------------------------------------- 
  95 wxProgressDialog::wxProgressDialog(wxString 
const &title
, 
  96                                    wxString 
const &message
, 
 100                 : wxDialog(parent
, wxID_ANY
, title
), 
 103                   m_hasAbortButton(false), 
 104                   m_hasSkipButton(false) 
 106     // we may disappear at any moment, let the others know about it 
 107     SetExtraStyle(GetExtraStyle() | wxWS_EX_TRANSIENT
); 
 108     m_windowStyle 
|= style
; 
 110     m_hasAbortButton 
= (style 
& wxPD_CAN_ABORT
) != 0; 
 111     m_hasSkipButton 
= (style 
& wxPD_CAN_SKIP
) != 0; 
 113     bool isPda 
= (wxSystemSettings::GetScreenType() <= wxSYS_SCREEN_PDA
); 
 115 #if defined(__WXMSW__) && !defined(__WXUNIVERSAL__) 
 116     // we have to remove the "Close" button from the title bar then as it is 
 117     // confusing to have it - it doesn't work anyhow 
 119     // FIXME: should probably have a (extended?) window style for this 
 120     if ( !m_hasAbortButton 
) 
 122         EnableCloseButton(false); 
 126 #if defined(__SMARTPHONE__) 
 130     m_state 
= m_hasAbortButton 
? Continue 
: Uncancelable
; 
 133 #if defined(__WXMSW__) || defined(__WXPM__) 
 134     // we can't have values > 65,536 in the progress control under Windows, so 
 135     // scale everything down 
 136     m_factor 
= m_maximum 
/ 65536 + 1; 
 137     m_maximum 
/= m_factor
; 
 140     m_parentTop 
= wxGetTopLevelParent(parent
); 
 143     dc
.SetFont(wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT
)); 
 145     dc
.GetTextExtent(message
, &widthText
, NULL
, NULL
, NULL
, NULL
); 
 147     wxBoxSizer 
*sizer 
= new wxBoxSizer(wxVERTICAL
); 
 149     m_msg 
= new wxStaticText(this, wxID_ANY
, message
); 
 150     sizer
->Add(m_msg
, 0, wxLEFT 
| wxTOP
, 2*LAYOUT_MARGIN
); 
 153            sizeLabel 
= m_msg
->GetSize(); 
 154     sizeDlg
.y 
= 2*LAYOUT_MARGIN 
+ sizeLabel
.y
; 
 158         int gauge_style 
= wxGA_HORIZONTAL
; 
 159         if ( ( style 
& wxPD_SMOOTH 
) == wxPD_SMOOTH 
) 
 160             gauge_style 
|= wxGA_SMOOTH
; 
 161         m_gauge 
= new wxGauge(this, wxID_ANY
, m_maximum
, 
 162                               wxDefaultPosition
, wxDefaultSize
, 
 165         sizer
->Add(m_gauge
, 0, wxLEFT 
| wxRIGHT 
| wxTOP 
| wxEXPAND
, 2*LAYOUT_MARGIN
); 
 166         m_gauge
->SetValue(0); 
 168         wxSize sizeGauge 
= m_gauge
->GetSize(); 
 169         sizeDlg
.y 
+= 2*LAYOUT_MARGIN 
+ sizeGauge
.y
; 
 172         m_gauge 
= (wxGauge 
*)NULL
; 
 174     // create the estimated/remaining/total time zones if requested 
 175     m_elapsed 
= m_estimated 
= m_remaining 
= (wxStaticText
*)NULL
; 
 176     m_display_estimated 
= m_last_timeupdate 
= m_break 
= 0; 
 179     // if we are going to have at least one label, remmeber it in this var 
 180     wxStaticText 
*label 
= NULL
; 
 182     // also count how many labels we really have 
 183     size_t nTimeLabels 
= 0; 
 185     if ( style 
& wxPD_ELAPSED_TIME 
) 
 190         m_elapsed 
= CreateLabel(_("Elapsed time : "), sizer
); 
 193     if ( style 
& wxPD_ESTIMATED_TIME 
) 
 198         m_estimated 
= CreateLabel(_("Estimated time : "), sizer
); 
 201     if ( style 
& wxPD_REMAINING_TIME 
) 
 206         m_remaining 
= CreateLabel(_("Remaining time : "), sizer
); 
 209     if ( nTimeLabels 
> 0 ) 
 211         // set it to the current time 
 212         m_timeStart 
= wxGetCurrentTime(); 
 213         sizeDlg
.y 
+= nTimeLabels 
* (label
->GetSize().y 
+ LAYOUT_MARGIN
); 
 216 #if defined(__SMARTPHONE__) 
 217     if ( m_hasSkipButton 
) 
 218         SetRightMenu(wxID_SKIP
, _("Skip")); 
 219     if ( m_hasAbortButton 
) 
 220         SetLeftMenu(wxID_CANCEL
); 
 222     m_btnAbort 
= m_btnSkip 
= (wxButton 
*)NULL
; 
 223     bool sizeDlgModified 
= false; 
 224     wxBoxSizer 
*buttonSizer 
= new wxBoxSizer(wxHORIZONTAL
); 
 226     const int sizerFlags 
= 
 227 #if defined(__WXMSW__) || defined(__WXPM__) 
 228                            wxALIGN_RIGHT 
| wxALL
 
 230                            wxALIGN_CENTER_HORIZONTAL 
| wxBOTTOM 
| wxTOP
 
 234     if ( m_hasSkipButton 
) 
 236         m_btnSkip 
= new wxButton(this, wxID_SKIP
, _("Skip")); 
 238         // Windows dialogs usually have buttons in the lower right corner 
 239         buttonSizer
->Add(m_btnSkip
, 0, sizerFlags
, LAYOUT_MARGIN
); 
 240         sizeDlg
.y 
+= 2*LAYOUT_MARGIN 
+ wxButton::GetDefaultSize().y
; 
 241         sizeDlgModified 
= true; 
 244     if ( m_hasAbortButton 
) 
 246         m_btnAbort 
= new wxButton(this, wxID_CANCEL
); 
 248         // Windows dialogs usually have buttons in the lower right corner 
 249         buttonSizer
->Add(m_btnAbort
, 0, sizerFlags
, LAYOUT_MARGIN
); 
 251             sizeDlg
.y 
+= 2*LAYOUT_MARGIN 
+ wxButton::GetDefaultSize().y
; 
 254     sizer
->Add(buttonSizer
, 0, sizerFlags
, LAYOUT_MARGIN 
); 
 255 #endif // __SMARTPHONE__/!__SMARTPHONE__ 
 257     SetSizerAndFit(sizer
); 
 261         sizeDlg
.y 
+= 2*LAYOUT_MARGIN
; 
 263         // try to make the dialog not square but rectangular of reasonable width 
 264         sizeDlg
.x 
= (wxCoord
)wxMax(widthText
, 4*sizeDlg
.y
/3); 
 267         SetClientSize(sizeDlg
); 
 270     Centre(wxCENTER_FRAME 
| wxBOTH
); 
 272     if ( style 
& wxPD_APP_MODAL 
) 
 274         m_winDisabler 
= new wxWindowDisabler(this); 
 279             m_parentTop
->Disable(); 
 280         m_winDisabler 
= NULL
; 
 286     // this one can be initialized even if the others are unknown for now 
 288     // NB: do it after calling Layout() to keep the labels correctly aligned 
 291         SetTimeLabel(0, m_elapsed
); 
 297 wxStaticText 
*wxProgressDialog::CreateLabel(const wxString
& text
, 
 300     wxBoxSizer 
*locsizer 
= new wxBoxSizer(wxLARGESMALL(wxHORIZONTAL
,wxVERTICAL
)); 
 302     wxStaticText 
*dummy 
= new wxStaticText(this, wxID_ANY
, text
); 
 303     wxStaticText 
*label 
= new wxStaticText(this, wxID_ANY
, _("unknown")); 
 305     // select placement most native or nice on target GUI 
 306 #if defined(__SMARTPHONE__) 
 307     // label and time to the left in two rows 
 308     locsizer
->Add(dummy
, 1, wxALIGN_LEFT
); 
 309     locsizer
->Add(label
, 1, wxALIGN_LEFT
); 
 310     sizer
->Add(locsizer
, 0, wxALIGN_LEFT 
| wxTOP 
| wxLEFT
, LAYOUT_MARGIN
); 
 311 #elif defined(__WXMSW__) || defined(__WXPM__) || defined(__WXMAC__) 
 312     // label and time centered in one row 
 313     locsizer
->Add(dummy
, 1, wxLARGESMALL(wxALIGN_RIGHT
,wxALIGN_LEFT
)); 
 314     locsizer
->Add(label
, 1, wxALIGN_LEFT 
| wxLEFT
, LAYOUT_MARGIN
); 
 315     sizer
->Add(locsizer
, 0, wxALIGN_CENTER_HORIZONTAL 
| wxTOP
, LAYOUT_MARGIN
); 
 317     // label and time to the right in one row 
 318     sizer
->Add(locsizer
, 0, wxALIGN_RIGHT 
| wxRIGHT 
| wxTOP
, LAYOUT_MARGIN
); 
 319     locsizer
->Add(dummy
); 
 320     locsizer
->Add(label
, 0, wxLEFT
, LAYOUT_MARGIN
); 
 326 // ---------------------------------------------------------------------------- 
 327 // wxProgressDialog operations 
 328 // ---------------------------------------------------------------------------- 
 331 wxProgressDialog::Update(int value
, const wxString
& newmsg
, bool *skip
) 
 333     wxASSERT_MSG( value 
== -1 || m_gauge
, wxT("cannot update non existent dialog") ); 
 339     wxASSERT_MSG( value 
<= m_maximum
, wxT("invalid progress value") ); 
 341     // fill up the gauge if value == maximum because this means that the dialog 
 342     // is going to close and the gauge shouldn't be partly empty in this case 
 343     if ( m_gauge 
&& value 
<= m_maximum 
) 
 345         m_gauge
->SetValue(value 
== m_maximum 
? value 
: value 
+ 1); 
 348     if ( !newmsg
.empty() && newmsg 
!= m_msg
->GetLabel() ) 
 350         m_msg
->SetLabel(newmsg
); 
 355     if ( (m_elapsed 
|| m_remaining 
|| m_estimated
) && (value 
!= 0) ) 
 357         unsigned long elapsed 
= wxGetCurrentTime() - m_timeStart
; 
 358         if (    m_last_timeupdate 
< elapsed
 
 359              || value 
== m_maximum
 
 362             m_last_timeupdate 
= elapsed
; 
 363             unsigned long estimated 
= m_break 
+ 
 364                   (unsigned long)(( (double) (elapsed
-m_break
) * m_maximum 
) / ((double)value
)) ; 
 365             if (    estimated 
> m_display_estimated
 
 371             else if (    estimated 
< m_display_estimated
 
 381             if (    m_ctdelay 
>= m_delay          
// enough confirmations for a higher value 
 382                  || m_ctdelay 
<= (m_delay
*-1)     // enough confirmations for a lower value 
 383                  || value 
== m_maximum            
// to stay consistent 
 384                  || elapsed 
> m_display_estimated 
// to stay consistent 
 385                  || ( elapsed 
> 0 && elapsed 
< 4 ) // additional updates in the beginning 
 388                 m_display_estimated 
= estimated
; 
 393         long display_remaining 
= m_display_estimated 
- elapsed
; 
 394         if ( display_remaining 
< 0 ) 
 396             display_remaining 
= 0; 
 399         SetTimeLabel(elapsed
, m_elapsed
); 
 400         SetTimeLabel(m_display_estimated
, m_estimated
); 
 401         SetTimeLabel(display_remaining
, m_remaining
); 
 404     if ( value 
== m_maximum 
) 
 406         if ( m_state 
== Finished 
) 
 408             // ignore multiple calls to Update(m_maximum): it may sometimes be 
 409             // troublesome to ensure that Update() is not called twice with the 
 410             // same value (e.g. because of the rounding errors) and if we don't 
 411             // return now we're going to generate asserts below 
 415         // so that we return true below and that out [Cancel] handler knew what 
 418         if( !(GetWindowStyle() & wxPD_AUTO_HIDE
) ) 
 422 #if defined(__WXMSW__) && !defined(__WXUNIVERSAL__) 
 426             if ( newmsg
.empty() ) 
 428                 // also provide the finishing message if the application didn't 
 429                 m_msg
->SetLabel(_("Done.")); 
 438             // reenable other windows before hiding this one because otherwise 
 439             // Windows wouldn't give the focus back to the window which had 
 440             // been previously focused because it would still be disabled 
 441             ReenableOtherWindows(); 
 448         // we have to yield because not only we want to update the display but 
 449         // also to process the clicks on the cancel and skip buttons 
 452         if ( (m_skip
) && (skip 
!= NULL
) && (*skip 
== false) ) 
 460     // update the display in case yielding above didn't do it 
 463     return m_state 
!= Canceled
; 
 466 void wxProgressDialog::Resume() 
 469     m_ctdelay 
= m_delay
; // force an update of the elapsed/estimated/remaining time 
 470     m_break 
+= wxGetCurrentTime()-m_timeStop
; 
 477 bool wxProgressDialog::Show( bool show 
) 
 479     // reenable other windows before hiding this one because otherwise 
 480     // Windows wouldn't give the focus back to the window which had 
 481     // been previously focused because it would still be disabled 
 483         ReenableOtherWindows(); 
 485     return wxDialog::Show(show
); 
 488 // ---------------------------------------------------------------------------- 
 490 // ---------------------------------------------------------------------------- 
 492 void wxProgressDialog::OnCancel(wxCommandEvent
& event
) 
 494     if ( m_state 
== Finished 
) 
 496         // this means that the count down is already finished and we're being 
 497         // shown as a modal dialog - so just let the default handler do the job 
 502         // request to cancel was received, the next time Update() is called we 
 506         // update the buttons state immediately so that the user knows that the 
 507         // request has been noticed 
 511         // save the time when the dialog was stopped 
 512         m_timeStop 
= wxGetCurrentTime(); 
 516 void wxProgressDialog::OnSkip(wxCommandEvent
& WXUNUSED(event
)) 
 522 void wxProgressDialog::OnClose(wxCloseEvent
& event
) 
 524     if ( m_state 
== Uncancelable 
) 
 526         // can't close this dialog 
 529     else if ( m_state 
== Finished 
) 
 531         // let the default handler close the window as we already terminated 
 536         // next Update() will notice it 
 541         m_timeStop 
= wxGetCurrentTime(); 
 545 // ---------------------------------------------------------------------------- 
 547 // ---------------------------------------------------------------------------- 
 549 wxProgressDialog::~wxProgressDialog() 
 551     // normally this should have been already done, but just in case 
 552     ReenableOtherWindows(); 
 555 void wxProgressDialog::ReenableOtherWindows() 
 557     if ( GetWindowStyle() & wxPD_APP_MODAL 
) 
 559         delete m_winDisabler
; 
 560         m_winDisabler 
= (wxWindowDisabler 
*)NULL
; 
 565             m_parentTop
->Enable(); 
 569 // ---------------------------------------------------------------------------- 
 571 // ---------------------------------------------------------------------------- 
 573 static void SetTimeLabel(unsigned long val
, wxStaticText 
*label
) 
 578         unsigned long hours 
= val 
/ 3600; 
 579         unsigned long minutes 
= (val 
% 3600) / 60; 
 580         unsigned long seconds 
= val 
% 60; 
 581         s
.Printf(wxT("%lu:%02lu:%02lu"), hours
, minutes
, seconds
); 
 583         if ( s 
!= label
->GetLabel() ) 
 588 void wxProgressDialog::EnableSkip(bool enable
) 
 592 #ifdef __SMARTPHONE__ 
 594             SetRightMenu(wxID_SKIP
, _("Skip")); 
 599             m_btnSkip
->Enable(enable
); 
 604 void wxProgressDialog::EnableAbort(bool enable
) 
 608 #ifdef __SMARTPHONE__ 
 610             SetLeftMenu(wxID_CANCEL
); // stock buttons makes Cancel label 
 615             m_btnAbort
->Enable(enable
); 
 620 void wxProgressDialog::EnableClose() 
 624 #ifdef __SMARTPHONE__ 
 625         SetLeftMenu(wxID_CANCEL
, _("Close")); 
 629             m_btnAbort
->Enable(); 
 630             m_btnAbort
->SetLabel(_("Close")); 
 636 #endif // wxUSE_PROGRESSDLG