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