make wxStatusBarPane a 'full class' with getters and protected data; document it...
[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 #include "wx/control.h"
28 #endif
29
30 #ifdef __WXGTK20__
31 #include <gtk/gtk.h>
32 #endif
33
34 // we only have to do it here when we use wxStatusBarGeneric in addition to the
35 // standard wxStatusBar class, if wxStatusBarGeneric is the same as
36 // wxStatusBar, then the corresponding IMPLEMENT_DYNAMIC_CLASS is already in
37 // common/statbar.cpp
38 #if defined(__WXMAC__) || \
39 (defined(wxUSE_NATIVE_STATUSBAR) && wxUSE_NATIVE_STATUSBAR)
40 #include "wx/generic/statusbr.h"
41
42 IMPLEMENT_DYNAMIC_CLASS(wxStatusBarGeneric, wxWindow)
43 #endif // wxUSE_NATIVE_STATUSBAR
44
45 // Default status border dimensions
46 #define wxTHICK_LINE_BORDER 2
47
48 // Margin between the field text and the field rect
49 #define wxFIELD_TEXT_MARGIN 2
50
51
52 // ----------------------------------------------------------------------------
53 // wxStatusBarGeneric
54 // ----------------------------------------------------------------------------
55
56 BEGIN_EVENT_TABLE(wxStatusBarGeneric, wxWindow)
57 EVT_PAINT(wxStatusBarGeneric::OnPaint)
58 EVT_SIZE(wxStatusBarGeneric::OnSize)
59 EVT_LEFT_DOWN(wxStatusBarGeneric::OnLeftDown)
60 EVT_RIGHT_DOWN(wxStatusBarGeneric::OnRightDown)
61 EVT_SYS_COLOUR_CHANGED(wxStatusBarGeneric::OnSysColourChanged)
62 END_EVENT_TABLE()
63
64 void wxStatusBarGeneric::Init()
65 {
66 m_borderX = wxTHICK_LINE_BORDER;
67 m_borderY = wxTHICK_LINE_BORDER;
68 }
69
70 wxStatusBarGeneric::~wxStatusBarGeneric()
71 {
72 }
73
74 bool wxStatusBarGeneric::Create(wxWindow *parent,
75 wxWindowID id,
76 long style,
77 const wxString& name)
78 {
79 style |= wxTAB_TRAVERSAL | wxFULL_REPAINT_ON_RESIZE;
80 if ( !wxWindow::Create(parent, id,
81 wxDefaultPosition, wxDefaultSize,
82 style, name) )
83 return false;
84
85 // The status bar should have a themed background
86 SetThemeEnabled( true );
87
88 InitColours();
89
90 #ifdef __WXPM__
91 SetFont(*wxSMALL_FONT);
92 #endif
93
94 int height = (int)((11*GetCharHeight())/10 + 2*GetBorderY());
95 SetSize(wxDefaultCoord, wxDefaultCoord, wxDefaultCoord, height);
96
97 SetFieldsCount(1);
98
99 return true;
100 }
101
102 wxSize wxStatusBarGeneric::DoGetBestSize() const
103 {
104 int width, height;
105
106 // best width is the width of the parent
107 if (GetParent())
108 GetParent()->GetClientSize(&width, NULL);
109 else
110 width = 80; // a dummy value
111
112 // best height is as calculated above in Create()
113 height = (int)((11*GetCharHeight())/10 + 2*GetBorderY());
114
115 return wxSize(width, height);
116 }
117
118 void wxStatusBarGeneric::SetFieldsCount(int number, const int *widths)
119 {
120 wxASSERT_MSG( number >= 0, _T("negative number of fields in wxStatusBar?") );
121
122 // this will result in a call to SetStatusWidths() and thus an update to our
123 // m_widthsAbs cache
124 wxStatusBarBase::SetFieldsCount(number, widths);
125 }
126
127 void wxStatusBarGeneric::SetStatusText(const wxString& text, int number)
128 {
129 wxCHECK_RET( (number >= 0) && ((size_t)number < m_panes.GetCount()),
130 _T("invalid status bar field index") );
131
132 wxString oldText = GetStatusText(number);
133 if (oldText != text)
134 {
135 wxStatusBarBase::SetStatusText(text, number);
136
137 wxRect rect;
138 GetFieldRect(number, rect);
139
140 Refresh(true, &rect);
141
142 // it's common to show some text in the status bar before starting a
143 // relatively lengthy operation, ensure that the text is shown to the
144 // user immediately and not after the lengthy operation end
145 Update();
146 }
147 }
148
149 void wxStatusBarGeneric::SetStatusWidths(int n, const int widths_field[])
150 {
151 // only set status widths when n == number of statuswindows
152 wxCHECK_RET( (size_t)n == m_panes.GetCount(), _T("status bar field count mismatch") );
153
154 wxStatusBarBase::SetStatusWidths(n, widths_field);
155
156 // update cache
157 int width;
158 GetClientSize(&width, &m_lastClientHeight);
159 m_widthsAbs = CalculateAbsWidths(width);
160 }
161
162 bool wxStatusBarGeneric::ShowsSizeGrip() const
163 {
164 if ( !HasFlag(wxST_SIZEGRIP) )
165 return false;
166
167 wxTopLevelWindow * const
168 tlw = wxDynamicCast(wxGetTopLevelParent(GetParent()), wxTopLevelWindow);
169 return tlw && !tlw->IsMaximized() && tlw->HasFlag(wxRESIZE_BORDER);
170 }
171
172 void wxStatusBarGeneric::DrawFieldText(wxDC& dc, const wxRect& rect, int i, int textHeight)
173 {
174 wxString text(GetStatusText(i));
175 if (text.empty())
176 return; // optimization
177
178 int xpos = rect.x + wxFIELD_TEXT_MARGIN,
179 maxWidth = rect.width - 2*wxFIELD_TEXT_MARGIN,
180 ypos = (int) (((rect.height - textHeight) / 2) + rect.y + 0.5);
181
182 if (ShowsSizeGrip())
183 {
184 // don't write text over the size grip:
185 // NOTE: overloading DoGetClientSize() and GetClientAreaOrigin() wouldn't
186 // work because the adjustment needs to be done only when drawing
187 // the field text and not also when drawing the background, the
188 // size grip itself, etc
189 if ((GetLayoutDirection() == wxLayout_RightToLeft && i == 0) ||
190 (GetLayoutDirection() != wxLayout_RightToLeft &&
191 i == (int)m_panes.GetCount()-1))
192 {
193 const wxRect& gripRc = GetSizeGripRect();
194
195 // NOTE: we don't need any special treatment wrt to the layout direction
196 // since DrawText() will automatically adjust the origin of the
197 // text accordingly to the layout in use
198
199 maxWidth -= gripRc.width;
200 }
201 }
202
203 // eventually ellipsize the text so that it fits the field width
204 text = wxControl::Ellipsize(
205 text, dc,
206 GetLayoutDirection() == wxLayout_RightToLeft ? wxELLIPSIZE_START : wxELLIPSIZE_END,
207 maxWidth,
208 wxELLIPSIZE_EXPAND_TAB);
209 // Ellipsize() will do something only if necessary
210
211 #if defined( __WXGTK__ ) || defined(__WXMAC__)
212 xpos++;
213 ypos++;
214 #endif
215
216 // draw the text
217 dc.DrawText(text, xpos, ypos);
218 }
219
220 void wxStatusBarGeneric::DrawField(wxDC& dc, int i, int textHeight)
221 {
222 wxRect rect;
223 GetFieldRect(i, rect);
224
225 if (rect.GetWidth() <= 0)
226 return; // happens when the status bar is shrinked in a very small area!
227
228 int style = m_panes[i].GetStyle();
229 if (style != wxSB_FLAT)
230 {
231 // Draw border
232 // For wxSB_NORMAL:
233 // Have grey background, plus 3-d border -
234 // One black rectangle.
235 // Inside this, left and top sides - dark grey. Bottom and right -
236 // white.
237 // Reverse it for wxSB_RAISED
238
239 dc.SetPen((style == wxSB_RAISED) ? m_mediumShadowPen : m_hilightPen);
240
241 #ifndef __WXPM__
242
243 // Right and bottom lines
244 dc.DrawLine(rect.x + rect.width, rect.y,
245 rect.x + rect.width, rect.y + rect.height);
246 dc.DrawLine(rect.x + rect.width, rect.y + rect.height,
247 rect.x, rect.y + rect.height);
248
249 dc.SetPen((style == wxSB_RAISED) ? m_hilightPen : m_mediumShadowPen);
250
251 // Left and top lines
252 dc.DrawLine(rect.x, rect.y + rect.height,
253 rect.x, rect.y);
254 dc.DrawLine(rect.x, rect.y,
255 rect.x + rect.width, rect.y);
256 #else
257
258 dc.DrawLine(rect.x + rect.width, rect.height + 2,
259 rect.x, rect.height + 2);
260 dc.DrawLine(rect.x + rect.width, rect.y,
261 rect.x + rect.width, rect.y + rect.height);
262
263 dc.SetPen((style == wxSB_RAISED) ? m_hilightPen : m_mediumShadowPen);
264 dc.DrawLine(rect.x, rect.y,
265 rect.x + rect.width, rect.y);
266 dc.DrawLine(rect.x, rect.y + rect.height,
267 rect.x, rect.y);
268 #endif
269 }
270
271 DrawFieldText(dc, rect, i, textHeight);
272 }
273
274 // Get the position and size of the field's internal bounding rectangle
275 bool wxStatusBarGeneric::GetFieldRect(int n, wxRect& rect) const
276 {
277 wxCHECK_MSG( (n >= 0) && ((size_t)n < m_panes.GetCount()), false,
278 _T("invalid status bar field index") );
279
280 if (m_widthsAbs.IsEmpty())
281 return false;
282
283 rect.x = 0;
284 for ( int i = 0; i < n; i++ )
285 rect.x += m_widthsAbs[i];
286 rect.x += m_borderX;
287
288 rect.y = m_borderY;
289 rect.width = m_widthsAbs[n] - 2*m_borderX;
290 rect.height = m_lastClientHeight - 2*m_borderY;
291
292 return true;
293 }
294
295 // Initialize colours
296 void wxStatusBarGeneric::InitColours()
297 {
298 #if defined(__WXPM__)
299 m_mediumShadowPen = wxPen(wxColour(127, 127, 127));
300 m_hilightPen = *wxWHITE_PEN;
301
302 SetBackgroundColour(*wxLIGHT_GREY);
303 SetForegroundColour(*wxBLACK);
304 #else // !__WXPM__
305 m_mediumShadowPen = wxPen(wxSystemSettings::GetColour(wxSYS_COLOUR_3DSHADOW));
306 m_hilightPen = wxPen(wxSystemSettings::GetColour(wxSYS_COLOUR_3DHILIGHT));
307 #endif // __WXPM__/!__WXPM__
308 }
309
310 void wxStatusBarGeneric::SetMinHeight(int height)
311 {
312 // check that this min height is not less than minimal height for the
313 // current font (min height is as calculated above in Create() except for border)
314 int minHeight = (int)((11*GetCharHeight())/10);
315
316 if ( height > minHeight )
317 SetSize(wxDefaultCoord, wxDefaultCoord, wxDefaultCoord, height + 2*m_borderY);
318 }
319
320 wxRect wxStatusBarGeneric::GetSizeGripRect() const
321 {
322 int width, height;
323 wxWindow::DoGetClientSize(&width, &height);
324
325 if (GetLayoutDirection() == wxLayout_RightToLeft)
326 return wxRect(2, 2, height-2, height-4);
327 else
328 return wxRect(width-height-2, 2, height-2, height-4);
329 }
330
331 // ----------------------------------------------------------------------------
332 // wxStatusBarGeneric - event handlers
333 // ----------------------------------------------------------------------------
334
335 void wxStatusBarGeneric::OnPaint(wxPaintEvent& WXUNUSED(event) )
336 {
337 wxPaintDC dc(this);
338
339 #ifdef __WXGTK20__
340 // Draw grip first
341 if ( ShowsSizeGrip() )
342 {
343 const wxRect& rc = GetSizeGripRect();
344 GdkWindowEdge edge =
345 GetLayoutDirection() == wxLayout_RightToLeft ? GDK_WINDOW_EDGE_SOUTH_WEST :
346 GDK_WINDOW_EDGE_SOUTH_EAST;
347 gtk_paint_resize_grip( m_widget->style,
348 GTKGetDrawingWindow(),
349 (GtkStateType) GTK_WIDGET_STATE (m_widget),
350 NULL,
351 m_widget,
352 "statusbar",
353 edge,
354 rc.x, rc.y, rc.width, rc.height );
355 }
356 #endif // __WXGTK20__
357
358 if (GetFont().IsOk())
359 dc.SetFont(GetFont());
360
361 // compute char height only once for all panes:
362 int textHeight = dc.GetCharHeight();
363
364 dc.SetBackgroundMode(wxBRUSHSTYLE_TRANSPARENT);
365 for (size_t i = 0; i < m_panes.GetCount(); i ++)
366 DrawField(dc, i, textHeight);
367 }
368
369 // Responds to colour changes, and passes event on to children.
370 void wxStatusBarGeneric::OnSysColourChanged(wxSysColourChangedEvent& event)
371 {
372 InitColours();
373
374 // Propagate the event to the non-top-level children
375 wxWindow::OnSysColourChanged(event);
376 }
377
378 void wxStatusBarGeneric::OnLeftDown(wxMouseEvent& event)
379 {
380 #ifdef __WXGTK20__
381 int width, height;
382 GetClientSize(&width, &height);
383
384 if ( ShowsSizeGrip() && (event.GetX() > width-height) )
385 {
386 GtkWidget *ancestor = gtk_widget_get_toplevel( m_widget );
387
388 if (!GTK_IS_WINDOW (ancestor))
389 return;
390
391 GdkWindow *source = GTKGetDrawingWindow();
392
393 int org_x = 0;
394 int org_y = 0;
395 gdk_window_get_origin( source, &org_x, &org_y );
396
397 if (GetLayoutDirection() == wxLayout_RightToLeft)
398 {
399 gtk_window_begin_resize_drag (GTK_WINDOW (ancestor),
400 GDK_WINDOW_EDGE_SOUTH_WEST,
401 1,
402 org_x - event.GetX() + GetSize().x ,
403 org_y + event.GetY(),
404 0);
405 }
406 else
407 {
408 gtk_window_begin_resize_drag (GTK_WINDOW (ancestor),
409 GDK_WINDOW_EDGE_SOUTH_EAST,
410 1,
411 org_x + event.GetX(),
412 org_y + event.GetY(),
413 0);
414 }
415 }
416 else
417 {
418 event.Skip( true );
419 }
420 #else
421 event.Skip( true );
422 #endif
423 }
424
425 void wxStatusBarGeneric::OnRightDown(wxMouseEvent& event)
426 {
427 #ifdef __WXGTK20__
428 int width, height;
429 GetClientSize(&width, &height);
430
431 if ( ShowsSizeGrip() && (event.GetX() > width-height) )
432 {
433 GtkWidget *ancestor = gtk_widget_get_toplevel( m_widget );
434
435 if (!GTK_IS_WINDOW (ancestor))
436 return;
437
438 GdkWindow *source = GTKGetDrawingWindow();
439
440 int org_x = 0;
441 int org_y = 0;
442 gdk_window_get_origin( source, &org_x, &org_y );
443
444 gtk_window_begin_move_drag (GTK_WINDOW (ancestor),
445 2,
446 org_x + event.GetX(),
447 org_y + event.GetY(),
448 0);
449 }
450 else
451 {
452 event.Skip( true );
453 }
454 #else
455 event.Skip( true );
456 #endif
457 }
458
459 void wxStatusBarGeneric::OnSize(wxSizeEvent& WXUNUSED(event))
460 {
461 // FIXME: workarounds for OS/2 bugs have nothing to do here (VZ)
462 int width;
463 #ifdef __WXPM__
464 GetSize(&width, &m_lastClientHeight);
465 #else
466 GetClientSize(&width, &m_lastClientHeight);
467 #endif
468
469 // recompute the cache of the field widths if the status bar width has changed
470 m_widthsAbs = CalculateAbsWidths(width);
471 }
472
473 #endif // wxUSE_STATUSBAR