get rid of the wxListString class in wxStatusBar code; introduce a wxStatusBarPane...
[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 #include "wx/toplevel.h"
27 #endif
28
29 #ifdef __WXGTK20__
30 #include <gtk/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 // Default status border dimensions
45 #define wxTHICK_LINE_BORDER 2
46
47
48 // ----------------------------------------------------------------------------
49 // wxStatusBarGeneric
50 // ----------------------------------------------------------------------------
51
52 BEGIN_EVENT_TABLE(wxStatusBarGeneric, wxWindow)
53 EVT_PAINT(wxStatusBarGeneric::OnPaint)
54 EVT_LEFT_DOWN(wxStatusBarGeneric::OnLeftDown)
55 EVT_RIGHT_DOWN(wxStatusBarGeneric::OnRightDown)
56 EVT_SYS_COLOUR_CHANGED(wxStatusBarGeneric::OnSysColourChanged)
57 END_EVENT_TABLE()
58
59 void wxStatusBarGeneric::Init()
60 {
61 m_borderX = wxTHICK_LINE_BORDER;
62 m_borderY = wxTHICK_LINE_BORDER;
63 }
64
65 wxStatusBarGeneric::~wxStatusBarGeneric()
66 {
67 }
68
69 bool wxStatusBarGeneric::Create(wxWindow *parent,
70 wxWindowID id,
71 long style,
72 const wxString& name)
73 {
74 style |= wxTAB_TRAVERSAL | wxFULL_REPAINT_ON_RESIZE;
75 if ( !wxWindow::Create(parent, id,
76 wxDefaultPosition, wxDefaultSize,
77 style, name) )
78 return false;
79
80 // The status bar should have a themed background
81 SetThemeEnabled( true );
82
83 InitColours();
84
85 #ifdef __WXPM__
86 SetFont(*wxSMALL_FONT);
87 #endif
88
89 wxCoord y;
90 {
91 // Set the height according to the font and the border size
92 wxClientDC dc(this);
93 dc.SetFont(GetFont());
94
95 dc.GetTextExtent(_T("X"), NULL, &y );
96 }
97 int height = (int)( (11*y)/10 + 2*GetBorderY());
98
99 SetSize(wxDefaultCoord, wxDefaultCoord, wxDefaultCoord, height);
100
101 SetFieldsCount(1);
102
103 return true;
104 }
105
106 wxSize wxStatusBarGeneric::DoGetBestSize() const
107 {
108 int width, height;
109
110 // best width is the width of the parent
111 GetParent()->GetClientSize(&width, NULL);
112
113 // best height is as calculated above in Create
114 wxClientDC dc((wxWindow*)this);
115 dc.SetFont(GetFont());
116 wxCoord y;
117 dc.GetTextExtent(_T("X"), NULL, &y );
118 height = (int)( (11*y)/10 + 2*GetBorderY());
119
120 return wxSize(width, height);
121 }
122
123 void wxStatusBarGeneric::SetFieldsCount(int number, const int *widths)
124 {
125 wxASSERT_MSG( number >= 0, _T("negative number of fields in wxStatusBar?") );
126
127 // enlarge the m_statusStrings array if needed:
128 for (size_t i = m_panes.GetCount(); i < (size_t)number; ++i)
129 m_statusStrings.Add( wxEmptyString );
130
131 // shrink the m_statusStrings array if needed:
132 for (int j = (int)m_panes.GetCount() - 1; j >= number; --j)
133 m_statusStrings.RemoveAt(j);
134
135 // forget the old cached pixel widths
136 m_widthsAbs.Empty();
137
138 wxStatusBarBase::SetFieldsCount(number, widths);
139
140 wxASSERT_MSG( m_panes.GetCount() == m_statusStrings.GetCount(),
141 _T("This really should never happen, can we do away with m_panes.GetCount() here?") );
142 }
143
144 void wxStatusBarGeneric::SetStatusText(const wxString& text, int number)
145 {
146 wxCHECK_RET( (number >= 0) && ((size_t)number < m_panes.GetCount()),
147 _T("invalid status bar field index") );
148
149 wxString oldText = m_statusStrings[number];
150 if (oldText != text)
151 {
152 m_statusStrings[number] = text;
153
154 wxRect rect;
155 GetFieldRect(number, rect);
156
157 Refresh(true, &rect);
158
159 // it's common to show some text in the status bar before starting a
160 // relatively lengthy operation, ensure that the text is shown to the
161 // user immediately and not after the lengthy operation end
162 Update();
163 }
164 }
165
166 wxString wxStatusBarGeneric::GetStatusText(int n) const
167 {
168 wxCHECK_MSG( (n >= 0) && ((size_t)n < m_panes.GetCount()), wxEmptyString,
169 _T("invalid status bar field index") );
170
171 return m_statusStrings[n];
172 }
173
174 void wxStatusBarGeneric::SetStatusWidths(int n, const int widths_field[])
175 {
176 // only set status widths when n == number of statuswindows
177 wxCHECK_RET( (size_t)n == m_panes.GetCount(), _T("status bar field count mismatch") );
178
179 // delete the old widths in any case - this function may be used to reset
180 // the widths to the default (all equal)
181 // MBN: this is incompatible with at least wxMSW and wxMAC and not
182 // documented, but let's keep it for now
183 m_bSameWidthForAllPanes = true;
184 // FM: what MBN's comment is saying is that allowing widths_field = NULL
185 // only for wxStatusBarGeneric is not documented...
186
187 // forget the old cached pixel widths
188 m_widthsAbs.Empty();
189
190 if ( !widths_field )
191 {
192 // not an error, see the comment above
193 Refresh();
194 return;
195 }
196
197 wxStatusBarBase::SetStatusWidths(n, widths_field);
198 }
199
200 bool wxStatusBarGeneric::ShowsSizeGrip() const
201 {
202 if ( !HasFlag(wxST_SIZEGRIP) )
203 return false;
204
205 wxTopLevelWindow * const
206 tlw = wxDynamicCast(wxGetTopLevelParent(GetParent()), wxTopLevelWindow);
207 return tlw && !tlw->IsMaximized() && tlw->HasFlag(wxRESIZE_BORDER);
208 }
209
210 void wxStatusBarGeneric::OnPaint(wxPaintEvent& WXUNUSED(event) )
211 {
212 wxPaintDC dc(this);
213
214 #ifdef __WXGTK20__
215 // Draw grip first
216 if ( ShowsSizeGrip() )
217 {
218 int width, height;
219 GetClientSize(&width, &height);
220
221 if (GetLayoutDirection() == wxLayout_RightToLeft)
222 {
223 gtk_paint_resize_grip( m_widget->style,
224 GTKGetDrawingWindow(),
225 (GtkStateType) GTK_WIDGET_STATE (m_widget),
226 NULL,
227 m_widget,
228 "statusbar",
229 GDK_WINDOW_EDGE_SOUTH_WEST,
230 2, 2, height-2, height-4 );
231 }
232 else
233 {
234 gtk_paint_resize_grip( m_widget->style,
235 GTKGetDrawingWindow(),
236 (GtkStateType) GTK_WIDGET_STATE (m_widget),
237 NULL,
238 m_widget,
239 "statusbar",
240 GDK_WINDOW_EDGE_SOUTH_EAST,
241 width-height-2, 2, height-2, height-4 );
242 }
243 }
244 #endif // __WXGTK20__
245
246 if (GetFont().Ok())
247 dc.SetFont(GetFont());
248
249 dc.SetBackgroundMode(wxBRUSHSTYLE_TRANSPARENT);
250
251 for (size_t i = 0; i < m_panes.GetCount(); i ++)
252 DrawField(dc, i);
253 }
254
255 void wxStatusBarGeneric::DrawFieldText(wxDC& dc, int i)
256 {
257 int leftMargin = 2;
258
259 wxRect rect;
260 GetFieldRect(i, rect);
261
262 wxString text(GetStatusText(i));
263
264 wxCoord x = 0, y = 0;
265
266 dc.GetTextExtent(text, &x, &y);
267
268 int xpos = rect.x + leftMargin;
269 int ypos = (int) (((rect.height - y) / 2 ) + rect.y + 0.5) ;
270
271 #if defined( __WXGTK__ ) || defined(__WXMAC__)
272 xpos++;
273 ypos++;
274 #endif
275
276 dc.SetClippingRegion(rect.x, rect.y, rect.width, rect.height);
277
278 dc.DrawText(text, xpos, ypos);
279
280 dc.DestroyClippingRegion();
281 }
282
283 void wxStatusBarGeneric::DrawField(wxDC& dc, int i)
284 {
285 wxRect rect;
286 GetFieldRect(i, rect);
287
288 int style = m_panes[i].nStyle;
289 if (style != wxSB_FLAT)
290 {
291 // Draw border
292 // For wxSB_NORMAL:
293 // Have grey background, plus 3-d border -
294 // One black rectangle.
295 // Inside this, left and top sides - dark grey. Bottom and right -
296 // white.
297 // Reverse it for wxSB_RAISED
298
299 dc.SetPen((style == wxSB_RAISED) ? m_mediumShadowPen : m_hilightPen);
300
301 #ifndef __WXPM__
302
303 // Right and bottom lines
304 dc.DrawLine(rect.x + rect.width, rect.y,
305 rect.x + rect.width, rect.y + rect.height);
306 dc.DrawLine(rect.x + rect.width, rect.y + rect.height,
307 rect.x, rect.y + rect.height);
308
309 dc.SetPen((style == wxSB_RAISED) ? m_hilightPen : m_mediumShadowPen);
310
311 // Left and top lines
312 dc.DrawLine(rect.x, rect.y + rect.height,
313 rect.x, rect.y);
314 dc.DrawLine(rect.x, rect.y,
315 rect.x + rect.width, rect.y);
316 #else
317
318 dc.DrawLine(rect.x + rect.width, rect.height + 2,
319 rect.x, rect.height + 2);
320 dc.DrawLine(rect.x + rect.width, rect.y,
321 rect.x + rect.width, rect.y + rect.height);
322
323 dc.SetPen((style == wxSB_RAISED) ? m_hilightPen : m_mediumShadowPen);
324 dc.DrawLine(rect.x, rect.y,
325 rect.x + rect.width, rect.y);
326 dc.DrawLine(rect.x, rect.y + rect.height,
327 rect.x, rect.y);
328
329 #endif
330 }
331
332 DrawFieldText(dc, i);
333 }
334
335 // Get the position and size of the field's internal bounding rectangle
336 bool wxStatusBarGeneric::GetFieldRect(int n, wxRect& rect) const
337 {
338 wxCHECK_MSG( (n >= 0) && ((size_t)n < m_panes.GetCount()), false,
339 _T("invalid status bar field index") );
340
341 // FIXME: workarounds for OS/2 bugs have nothing to do here (VZ)
342 int width, height;
343 #ifdef __WXPM__
344 GetSize(&width, &height);
345 #else
346 GetClientSize(&width, &height);
347 #endif
348
349 // we cache m_widthsAbs between calls and recompute it if client
350 // width has changed (or when it is initially empty)
351 if ( m_widthsAbs.IsEmpty() || (m_lastClientWidth != width) )
352 {
353 wxConstCast(this, wxStatusBarGeneric)->
354 m_widthsAbs = CalculateAbsWidths(width);
355 // remember last width for which we have recomputed the widths in pixels
356 wxConstCast(this, wxStatusBarGeneric)->
357 m_lastClientWidth = width;
358 }
359
360 rect.x = 0;
361 for ( int i = 0; i < n; i++ )
362 {
363 rect.x += m_widthsAbs[i];
364 }
365
366 rect.x += m_borderX;
367 rect.y = m_borderY;
368
369 rect.width = m_widthsAbs[n] - 2*m_borderX;
370 rect.height = height - 2*m_borderY;
371
372 return true;
373 }
374
375 // Initialize colours
376 void wxStatusBarGeneric::InitColours()
377 {
378 #if 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 // !__WXPM__
385 m_mediumShadowPen = wxPen(wxSystemSettings::GetColour(wxSYS_COLOUR_3DSHADOW));
386 m_hilightPen = wxPen(wxSystemSettings::GetColour(wxSYS_COLOUR_3DHILIGHT));
387 #endif // __WXPM__/!__WXPM__
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 ( ShowsSizeGrip() && (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 = GTKGetDrawingWindow();
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 ( ShowsSizeGrip() && (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 = GTKGetDrawingWindow();
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