avoiding nesting dcs on the same window concurrently
[wxWidgets.git] / src / generic / statusbr.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/generic/statusbr.cpp
3 // Purpose: wxStatusBarGeneric class implementation
4 // Author: Julian Smart
5 // Modified by:
6 // Created: 01/02/97
7 // RCS-ID: $Id$
8 // Copyright: (c) Julian Smart
9 // Licence: wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
11
12 // For compilers that support precompilation, includes "wx.h".
13 #include "wx/wxprec.h"
14
15 #ifdef __BORLANDC__
16 #pragma hdrstop
17 #endif
18
19 #if wxUSE_STATUSBAR
20
21 #include "wx/statusbr.h"
22
23 #ifndef WX_PRECOMP
24 #include "wx/settings.h"
25 #include "wx/dcclient.h"
26 #endif
27
28 #ifdef __WXGTK20__
29 #include <gtk/gtk.h>
30 #include "wx/gtk/win_gtk.h"
31 #endif
32
33 // we only have to do it here when we use wxStatusBarGeneric in addition to the
34 // standard wxStatusBar class, if wxStatusBarGeneric is the same as
35 // wxStatusBar, then the corresponding IMPLEMENT_DYNAMIC_CLASS is already in
36 // common/statbar.cpp
37 #if defined(__WXMAC__) || \
38 (defined(wxUSE_NATIVE_STATUSBAR) && wxUSE_NATIVE_STATUSBAR)
39 #include "wx/generic/statusbr.h"
40
41 IMPLEMENT_DYNAMIC_CLASS(wxStatusBarGeneric, wxWindow)
42 #endif // wxUSE_NATIVE_STATUSBAR
43
44 BEGIN_EVENT_TABLE(wxStatusBarGeneric, wxWindow)
45 EVT_PAINT(wxStatusBarGeneric::OnPaint)
46 EVT_LEFT_DOWN(wxStatusBarGeneric::OnLeftDown)
47 EVT_RIGHT_DOWN(wxStatusBarGeneric::OnRightDown)
48 EVT_SYS_COLOUR_CHANGED(wxStatusBarGeneric::OnSysColourChanged)
49 END_EVENT_TABLE()
50
51 // Default status border dimensions
52 #define wxTHICK_LINE_BORDER 2
53
54 void wxStatusBarGeneric::Init()
55 {
56 m_borderX = wxTHICK_LINE_BORDER;
57 m_borderY = wxTHICK_LINE_BORDER;
58 }
59
60 wxStatusBarGeneric::~wxStatusBarGeneric()
61 {
62 }
63
64 bool wxStatusBarGeneric::Create(wxWindow *parent,
65 wxWindowID id,
66 long style,
67 const wxString& name)
68 {
69 style |= wxTAB_TRAVERSAL | wxFULL_REPAINT_ON_RESIZE;
70 if ( !wxWindow::Create(parent, id,
71 wxDefaultPosition, wxDefaultSize,
72 style, name) )
73 return false;
74
75 // The status bar should have a themed background
76 SetThemeEnabled( true );
77
78 InitColours();
79
80 #ifdef __WXPM__
81 SetFont(*wxSMALL_FONT);
82 #endif
83
84 wxCoord y;
85 {
86 // Set the height according to the font and the border size
87 wxClientDC dc(this);
88 dc.SetFont(GetFont());
89
90 dc.GetTextExtent(_T("X"), NULL, &y );
91 }
92 int height = (int)( (11*y)/10 + 2*GetBorderY());
93
94 SetSize(wxDefaultCoord, wxDefaultCoord, wxDefaultCoord, height);
95
96 SetFieldsCount(1);
97
98 return true;
99 }
100
101
102 wxSize wxStatusBarGeneric::DoGetBestSize() const
103 {
104 int width, height;
105
106 // best width is the width of the parent
107 GetParent()->GetClientSize(&width, NULL);
108
109 // best height is as calculated above in Create
110 wxClientDC dc((wxWindow*)this);
111 dc.SetFont(GetFont());
112 wxCoord y;
113 dc.GetTextExtent(_T("X"), NULL, &y );
114 height = (int)( (11*y)/10 + 2*GetBorderY());
115
116 return wxSize(width, height);
117 }
118
119 void wxStatusBarGeneric::SetFieldsCount(int number, const int *widths)
120 {
121 wxASSERT_MSG( number >= 0, _T("negative number of fields in wxStatusBar?") );
122
123 int i;
124 for(i = m_nFields; i < number; ++i)
125 m_statusStrings.Add( wxEmptyString );
126
127 for (i = m_nFields - 1; i >= number; --i)
128 m_statusStrings.RemoveAt(i);
129
130 // forget the old cached pixel widths
131 m_widthsAbs.Empty();
132
133 wxStatusBarBase::SetFieldsCount(number, widths);
134
135 wxASSERT_MSG( m_nFields == (int)m_statusStrings.GetCount(),
136 _T("This really should never happen, can we do away with m_nFields here?") );
137 }
138
139 void wxStatusBarGeneric::SetStatusText(const wxString& text, int number)
140 {
141 wxCHECK_RET( (number >= 0) && (number < m_nFields),
142 _T("invalid status bar field index") );
143
144 wxString oldText = m_statusStrings[number];
145 if (oldText != text)
146 {
147 m_statusStrings[number] = text;
148
149 wxRect rect;
150 GetFieldRect(number, rect);
151
152 Refresh(true, &rect);
153
154 // it's common to show some text in the status bar before starting a
155 // relatively lengthy operation, ensure that the text is shown to the
156 // user immediately and not after the lengthy operation end
157 Update();
158 }
159 }
160
161 wxString wxStatusBarGeneric::GetStatusText(int n) const
162 {
163 wxCHECK_MSG( (n >= 0) && (n < m_nFields), wxEmptyString,
164 _T("invalid status bar field index") );
165
166 return m_statusStrings[n];
167 }
168
169 void wxStatusBarGeneric::SetStatusWidths(int n, const int widths_field[])
170 {
171 // only set status widths, when n == number of statuswindows
172 wxCHECK_RET( n == m_nFields, _T("status bar field count mismatch") );
173
174 // delete the old widths in any case - this function may be used to reset
175 // the widths to the default (all equal)
176 // MBN: this is incompatible with at least wxMSW and wxMAC and not
177 // documented, but let's keep it for now
178 ReinitWidths();
179
180 // forget the old cached pixel widths
181 m_widthsAbs.Empty();
182
183 if ( !widths_field )
184 {
185 // not an error, see the comment above
186 Refresh();
187 return;
188 }
189
190 wxStatusBarBase::SetStatusWidths(n, widths_field);
191 }
192
193 void wxStatusBarGeneric::OnPaint(wxPaintEvent& WXUNUSED(event) )
194 {
195 wxPaintDC dc(this);
196
197 #ifdef __WXGTK20__
198 // Draw grip first
199 if (HasFlag( wxST_SIZEGRIP ))
200 {
201 int width, height;
202 GetClientSize(&width, &height);
203
204 if (GetLayoutDirection() == wxLayout_RightToLeft)
205 {
206 gtk_paint_resize_grip( m_widget->style,
207 GTK_PIZZA(m_wxwindow)->bin_window,
208 (GtkStateType) GTK_WIDGET_STATE (m_widget),
209 NULL,
210 m_widget,
211 "statusbar",
212 GDK_WINDOW_EDGE_SOUTH_WEST,
213 2, 2, height-2, height-4 );
214 }
215 else
216 {
217 gtk_paint_resize_grip( m_widget->style,
218 GTK_PIZZA(m_wxwindow)->bin_window,
219 (GtkStateType) GTK_WIDGET_STATE (m_widget),
220 NULL,
221 m_widget,
222 "statusbar",
223 GDK_WINDOW_EDGE_SOUTH_EAST,
224 width-height-2, 2, height-2, height-4 );
225 }
226 }
227 #endif
228
229 if (GetFont().Ok())
230 dc.SetFont(GetFont());
231
232 dc.SetBackgroundMode(wxTRANSPARENT);
233
234 #ifdef __WXPM__
235 wxColour vColor;
236
237 vColor = wxSystemSettings::GetColour(wxSYS_COLOUR_MENUBAR);
238 ::WinFillRect(dc.m_hPS, &dc.m_vRclPaint, vColor.GetPixel());
239 #endif
240
241 for (int i = 0; i < m_nFields; i ++)
242 DrawField(dc, i);
243 }
244
245 void wxStatusBarGeneric::DrawFieldText(wxDC& dc, int i)
246 {
247 int leftMargin = 2;
248
249 wxRect rect;
250 GetFieldRect(i, rect);
251
252 wxString text(GetStatusText(i));
253
254 long x = 0, y = 0;
255
256 dc.GetTextExtent(text, &x, &y);
257
258 int xpos = rect.x + leftMargin;
259 int ypos = (int) (((rect.height - y) / 2 ) + rect.y + 0.5) ;
260
261 #if defined( __WXGTK__ ) || defined(__WXMAC__)
262 xpos++;
263 ypos++;
264 #endif
265
266 dc.SetClippingRegion(rect.x, rect.y, rect.width, rect.height);
267
268 dc.DrawText(text, xpos, ypos);
269
270 dc.DestroyClippingRegion();
271 }
272
273 void wxStatusBarGeneric::DrawField(wxDC& dc, int i)
274 {
275 wxRect rect;
276 GetFieldRect(i, rect);
277
278 int style = wxSB_NORMAL;
279 if (m_statusStyles)
280 style = m_statusStyles[i];
281
282 if (style != wxSB_FLAT)
283 {
284 // Draw border
285 // For wxSB_NORMAL:
286 // Have grey background, plus 3-d border -
287 // One black rectangle.
288 // Inside this, left and top sides - dark grey. Bottom and right -
289 // white.
290 // Reverse it for wxSB_RAISED
291
292 dc.SetPen((style == wxSB_RAISED) ? m_mediumShadowPen : m_hilightPen);
293
294 #ifndef __WXPM__
295
296 // Right and bottom lines
297 dc.DrawLine(rect.x + rect.width, rect.y,
298 rect.x + rect.width, rect.y + rect.height);
299 dc.DrawLine(rect.x + rect.width, rect.y + rect.height,
300 rect.x, rect.y + rect.height);
301
302 dc.SetPen((style == wxSB_RAISED) ? m_hilightPen : m_mediumShadowPen);
303
304 // Left and top lines
305 dc.DrawLine(rect.x, rect.y + rect.height,
306 rect.x, rect.y);
307 dc.DrawLine(rect.x, rect.y,
308 rect.x + rect.width, rect.y);
309 #else
310
311 dc.DrawLine(rect.x + rect.width, rect.height + 2,
312 rect.x, rect.height + 2);
313 dc.DrawLine(rect.x + rect.width, rect.y,
314 rect.x + rect.width, rect.y + rect.height);
315
316 dc.SetPen((style == wxSB_RAISED) ? m_hilightPen : m_mediumShadowPen);
317 dc.DrawLine(rect.x, rect.y,
318 rect.x + rect.width, rect.y);
319 dc.DrawLine(rect.x, rect.y + rect.height,
320 rect.x, rect.y);
321
322 #endif
323 }
324
325 DrawFieldText(dc, i);
326 }
327
328 // Get the position and size of the field's internal bounding rectangle
329 bool wxStatusBarGeneric::GetFieldRect(int n, wxRect& rect) const
330 {
331 wxCHECK_MSG( (n >= 0) && (n < m_nFields), false,
332 _T("invalid status bar field index") );
333
334 // FIXME: workarounds for OS/2 bugs have nothing to do here (VZ)
335 int width, height;
336 #ifdef __WXPM__
337 GetSize(&width, &height);
338 #else
339 GetClientSize(&width, &height);
340 #endif
341
342 // we cache m_widthsAbs between calls and recompute it if client
343 // width has changed (or when it is initially empty)
344 if ( m_widthsAbs.IsEmpty() || (m_lastClientWidth != width) )
345 {
346 wxConstCast(this, wxStatusBarGeneric)->
347 m_widthsAbs = CalculateAbsWidths(width);
348 // remember last width for which we have recomputed the widths in pixels
349 wxConstCast(this, wxStatusBarGeneric)->
350 m_lastClientWidth = width;
351 }
352
353 rect.x = 0;
354 for ( int i = 0; i < n; i++ )
355 {
356 rect.x += m_widthsAbs[i];
357 }
358
359 rect.x += m_borderX;
360 rect.y = m_borderY;
361
362 rect.width = m_widthsAbs[n] - 2*m_borderX;
363 rect.height = height - 2*m_borderY;
364
365 return true;
366 }
367
368 // Initialize colours
369 void wxStatusBarGeneric::InitColours()
370 {
371 // Shadow colours
372 #if defined(__WXMSW__) || defined(__WXMAC__)
373 wxColour mediumShadowColour(wxSystemSettings::GetColour(wxSYS_COLOUR_3DSHADOW));
374 m_mediumShadowPen = wxPen(mediumShadowColour, 1, wxSOLID);
375
376 wxColour hilightColour(wxSystemSettings::GetColour(wxSYS_COLOUR_3DHILIGHT));
377 m_hilightPen = wxPen(hilightColour, 1, wxSOLID);
378 #elif defined(__WXPM__)
379 m_mediumShadowPen = wxPen(wxColour(127, 127, 127), 1, wxSOLID);
380 m_hilightPen = *wxWHITE_PEN;
381
382 SetBackgroundColour(*wxLIGHT_GREY);
383 SetForegroundColour(*wxBLACK);
384 #else
385 m_mediumShadowPen = *wxGREY_PEN;
386 m_hilightPen = *wxWHITE_PEN;
387 #endif
388 }
389
390 // Responds to colour changes, and passes event on to children.
391 void wxStatusBarGeneric::OnSysColourChanged(wxSysColourChangedEvent& event)
392 {
393 InitColours();
394
395 // Propagate the event to the non-top-level children
396 wxWindow::OnSysColourChanged(event);
397 }
398
399 void wxStatusBarGeneric::SetMinHeight(int height)
400 {
401 // check that this min height is not less than minimal height for the
402 // current font
403 wxClientDC dc(this);
404 wxCoord y;
405 dc.GetTextExtent( wxT("X"), NULL, &y );
406
407 if ( height > (11*y)/10 )
408 {
409 SetSize(wxDefaultCoord, wxDefaultCoord, wxDefaultCoord, height + 2*m_borderY);
410 }
411 }
412
413 void wxStatusBarGeneric::OnLeftDown(wxMouseEvent& event)
414 {
415 #ifdef __WXGTK20__
416 int width, height;
417 GetClientSize(&width, &height);
418
419 if (HasFlag( wxST_SIZEGRIP ) && (event.GetX() > width-height))
420 {
421 GtkWidget *ancestor = gtk_widget_get_toplevel( m_widget );
422
423 if (!GTK_IS_WINDOW (ancestor))
424 return;
425
426 GdkWindow *source = GTK_PIZZA(m_wxwindow)->bin_window;
427
428 int org_x = 0;
429 int org_y = 0;
430 gdk_window_get_origin( source, &org_x, &org_y );
431
432 if (GetLayoutDirection() == wxLayout_RightToLeft)
433 {
434 gtk_window_begin_resize_drag (GTK_WINDOW (ancestor),
435 GDK_WINDOW_EDGE_SOUTH_WEST,
436 1,
437 org_x - event.GetX() + GetSize().x ,
438 org_y + event.GetY(),
439 0);
440 }
441 else
442 {
443 gtk_window_begin_resize_drag (GTK_WINDOW (ancestor),
444 GDK_WINDOW_EDGE_SOUTH_EAST,
445 1,
446 org_x + event.GetX(),
447 org_y + event.GetY(),
448 0);
449 }
450 }
451 else
452 {
453 event.Skip( true );
454 }
455 #else
456 event.Skip( true );
457 #endif
458 }
459
460 void wxStatusBarGeneric::OnRightDown(wxMouseEvent& event)
461 {
462 #ifdef __WXGTK20__
463 int width, height;
464 GetClientSize(&width, &height);
465
466 if (HasFlag( wxST_SIZEGRIP ) && (event.GetX() > width-height))
467 {
468 GtkWidget *ancestor = gtk_widget_get_toplevel( m_widget );
469
470 if (!GTK_IS_WINDOW (ancestor))
471 return;
472
473 GdkWindow *source = GTK_PIZZA(m_wxwindow)->bin_window;
474
475 int org_x = 0;
476 int org_y = 0;
477 gdk_window_get_origin( source, &org_x, &org_y );
478
479 gtk_window_begin_move_drag (GTK_WINDOW (ancestor),
480 2,
481 org_x + event.GetX(),
482 org_y + event.GetY(),
483 0);
484 }
485 else
486 {
487 event.Skip( true );
488 }
489 #else
490 event.Skip( true );
491 #endif
492 }
493
494 #endif // wxUSE_STATUSBAR