non-pch build fix
[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 // enlarge the m_statusStrings array if needed:
123 for (size_t i = m_panes.GetCount(); i < (size_t)number; ++i)
124 m_statusStrings.Add( wxEmptyString );
125
126 // shrink the m_statusStrings array if needed:
127 for (int j = (int)m_panes.GetCount() - 1; j >= number; --j)
128 m_statusStrings.RemoveAt(j);
129
130 // forget the old cached pixel widths
131 m_widthsAbs.Empty();
132
133 wxStatusBarBase::SetFieldsCount(number, widths);
134
135 wxASSERT_MSG( m_panes.GetCount() == m_statusStrings.GetCount(),
136 _T("This really should never happen, can we do away with m_panes.GetCount() here?") );
137 }
138
139 void wxStatusBarGeneric::SetStatusText(const wxString& text, int number)
140 {
141 wxCHECK_RET( (number >= 0) && ((size_t)number < m_panes.GetCount()),
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) && ((size_t)n < m_panes.GetCount()), 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( (size_t)n == m_panes.GetCount(), _T("status bar field count mismatch") );
173
174 // forget the old cached pixel widths
175 m_widthsAbs.Empty();
176
177 wxStatusBarBase::SetStatusWidths(n, widths_field);
178 }
179
180 bool wxStatusBarGeneric::ShowsSizeGrip() const
181 {
182 if ( !HasFlag(wxST_SIZEGRIP) )
183 return false;
184
185 wxTopLevelWindow * const
186 tlw = wxDynamicCast(wxGetTopLevelParent(GetParent()), wxTopLevelWindow);
187 return tlw && !tlw->IsMaximized() && tlw->HasFlag(wxRESIZE_BORDER);
188 }
189
190 void wxStatusBarGeneric::DrawFieldText(wxDC& dc, const wxRect& rect, int i, int textHeight)
191 {
192 wxString text(GetStatusText(i));
193 if (text.empty())
194 return; // optimization
195
196 int xpos = rect.x + wxFIELD_TEXT_MARGIN,
197 maxWidth = rect.width - 2*wxFIELD_TEXT_MARGIN,
198 ypos = (int) (((rect.height - textHeight) / 2) + rect.y + 0.5);
199
200 if (ShowsSizeGrip())
201 {
202 // don't write text over the size grip:
203 // NOTE: overloading DoGetClientSize() and GetClientAreaOrigin() wouldn't
204 // work because the adjustment needs to be done only when drawing
205 // the field text and not also when drawing the background, the
206 // size grip itself, etc
207 if ((GetLayoutDirection() == wxLayout_RightToLeft && i == 0) ||
208 (GetLayoutDirection() != wxLayout_RightToLeft &&
209 i == (int)m_panes.GetCount()-1))
210 {
211 const wxRect& gripRc = GetSizeGripRect();
212
213 // NOTE: we don't need any special treatment wrt to the layout direction
214 // since DrawText() will automatically adjust the origin of the
215 // text accordingly to the layout in use
216
217 maxWidth -= gripRc.width;
218 }
219 }
220
221 // eventually ellipsize the text so that it fits the field width
222 text = wxControl::Ellipsize(
223 text, dc,
224 GetLayoutDirection() == wxLayout_RightToLeft ? wxELLIPSIZE_START : wxELLIPSIZE_END,
225 maxWidth,
226 wxELLIPSIZE_EXPAND_TAB);
227 // Ellipsize() will do something only if necessary
228
229 #if defined( __WXGTK__ ) || defined(__WXMAC__)
230 xpos++;
231 ypos++;
232 #endif
233
234 // draw the text
235 dc.DrawText(text, xpos, ypos);
236 }
237
238 void wxStatusBarGeneric::DrawField(wxDC& dc, int i, int textHeight)
239 {
240 wxRect rect;
241 GetFieldRect(i, rect);
242
243 if (rect.GetWidth() <= 0)
244 return; // happens when the status bar is shrinked in a very small area!
245
246 int style = m_panes[i].nStyle;
247 if (style != wxSB_FLAT)
248 {
249 // Draw border
250 // For wxSB_NORMAL:
251 // Have grey background, plus 3-d border -
252 // One black rectangle.
253 // Inside this, left and top sides - dark grey. Bottom and right -
254 // white.
255 // Reverse it for wxSB_RAISED
256
257 dc.SetPen((style == wxSB_RAISED) ? m_mediumShadowPen : m_hilightPen);
258
259 #ifndef __WXPM__
260
261 // Right and bottom lines
262 dc.DrawLine(rect.x + rect.width, rect.y,
263 rect.x + rect.width, rect.y + rect.height);
264 dc.DrawLine(rect.x + rect.width, rect.y + rect.height,
265 rect.x, rect.y + rect.height);
266
267 dc.SetPen((style == wxSB_RAISED) ? m_hilightPen : m_mediumShadowPen);
268
269 // Left and top lines
270 dc.DrawLine(rect.x, rect.y + rect.height,
271 rect.x, rect.y);
272 dc.DrawLine(rect.x, rect.y,
273 rect.x + rect.width, rect.y);
274 #else
275
276 dc.DrawLine(rect.x + rect.width, rect.height + 2,
277 rect.x, rect.height + 2);
278 dc.DrawLine(rect.x + rect.width, rect.y,
279 rect.x + rect.width, rect.y + rect.height);
280
281 dc.SetPen((style == wxSB_RAISED) ? m_hilightPen : m_mediumShadowPen);
282 dc.DrawLine(rect.x, rect.y,
283 rect.x + rect.width, rect.y);
284 dc.DrawLine(rect.x, rect.y + rect.height,
285 rect.x, rect.y);
286 #endif
287 }
288
289 DrawFieldText(dc, rect, i, textHeight);
290 }
291
292 // Get the position and size of the field's internal bounding rectangle
293 bool wxStatusBarGeneric::GetFieldRect(int n, wxRect& rect) const
294 {
295 wxCHECK_MSG( (n >= 0) && ((size_t)n < m_panes.GetCount()), false,
296 _T("invalid status bar field index") );
297
298 if (m_widthsAbs.IsEmpty())
299 return false;
300
301 rect.x = 0;
302 for ( int i = 0; i < n; i++ )
303 rect.x += m_widthsAbs[i];
304 rect.x += m_borderX;
305
306 rect.y = m_borderY;
307 rect.width = m_widthsAbs[n] - 2*m_borderX;
308 rect.height = m_lastClientHeight - 2*m_borderY;
309
310 return true;
311 }
312
313 // Initialize colours
314 void wxStatusBarGeneric::InitColours()
315 {
316 #if defined(__WXPM__)
317 m_mediumShadowPen = wxPen(wxColour(127, 127, 127));
318 m_hilightPen = *wxWHITE_PEN;
319
320 SetBackgroundColour(*wxLIGHT_GREY);
321 SetForegroundColour(*wxBLACK);
322 #else // !__WXPM__
323 m_mediumShadowPen = wxPen(wxSystemSettings::GetColour(wxSYS_COLOUR_3DSHADOW));
324 m_hilightPen = wxPen(wxSystemSettings::GetColour(wxSYS_COLOUR_3DHILIGHT));
325 #endif // __WXPM__/!__WXPM__
326 }
327
328 void wxStatusBarGeneric::SetMinHeight(int height)
329 {
330 // check that this min height is not less than minimal height for the
331 // current font (min height is as calculated above in Create() except for border)
332 int minHeight = (int)((11*GetCharHeight())/10);
333
334 if ( height > minHeight )
335 SetSize(wxDefaultCoord, wxDefaultCoord, wxDefaultCoord, height + 2*m_borderY);
336 }
337
338 wxRect wxStatusBarGeneric::GetSizeGripRect() const
339 {
340 int width, height;
341 wxWindow::DoGetClientSize(&width, &height);
342
343 if (GetLayoutDirection() == wxLayout_RightToLeft)
344 return wxRect(2, 2, height-2, height-4);
345 else
346 return wxRect(width-height-2, 2, height-2, height-4);
347 }
348
349 // ----------------------------------------------------------------------------
350 // wxStatusBarGeneric - event handlers
351 // ----------------------------------------------------------------------------
352
353 void wxStatusBarGeneric::OnPaint(wxPaintEvent& WXUNUSED(event) )
354 {
355 wxPaintDC dc(this);
356
357 #ifdef __WXGTK20__
358 // Draw grip first
359 if ( ShowsSizeGrip() )
360 {
361 const wxRect& rc = GetSizeGripRect();
362 GdkWindowEdge edge =
363 GetLayoutDirection() == wxLayout_RightToLeft ? GDK_WINDOW_EDGE_SOUTH_WEST :
364 GDK_WINDOW_EDGE_SOUTH_EAST;
365 gtk_paint_resize_grip( m_widget->style,
366 GTKGetDrawingWindow(),
367 (GtkStateType) GTK_WIDGET_STATE (m_widget),
368 NULL,
369 m_widget,
370 "statusbar",
371 edge,
372 rc.x, rc.y, rc.width, rc.height );
373 }
374 #endif // __WXGTK20__
375
376 if (GetFont().IsOk())
377 dc.SetFont(GetFont());
378
379 // compute char height only once for all panes:
380 int textHeight = dc.GetCharHeight();
381
382 dc.SetBackgroundMode(wxBRUSHSTYLE_TRANSPARENT);
383 for (size_t i = 0; i < m_panes.GetCount(); i ++)
384 DrawField(dc, i, textHeight);
385 }
386
387 // Responds to colour changes, and passes event on to children.
388 void wxStatusBarGeneric::OnSysColourChanged(wxSysColourChangedEvent& event)
389 {
390 InitColours();
391
392 // Propagate the event to the non-top-level children
393 wxWindow::OnSysColourChanged(event);
394 }
395
396 void wxStatusBarGeneric::OnLeftDown(wxMouseEvent& event)
397 {
398 #ifdef __WXGTK20__
399 int width, height;
400 GetClientSize(&width, &height);
401
402 if ( ShowsSizeGrip() && (event.GetX() > width-height) )
403 {
404 GtkWidget *ancestor = gtk_widget_get_toplevel( m_widget );
405
406 if (!GTK_IS_WINDOW (ancestor))
407 return;
408
409 GdkWindow *source = GTKGetDrawingWindow();
410
411 int org_x = 0;
412 int org_y = 0;
413 gdk_window_get_origin( source, &org_x, &org_y );
414
415 if (GetLayoutDirection() == wxLayout_RightToLeft)
416 {
417 gtk_window_begin_resize_drag (GTK_WINDOW (ancestor),
418 GDK_WINDOW_EDGE_SOUTH_WEST,
419 1,
420 org_x - event.GetX() + GetSize().x ,
421 org_y + event.GetY(),
422 0);
423 }
424 else
425 {
426 gtk_window_begin_resize_drag (GTK_WINDOW (ancestor),
427 GDK_WINDOW_EDGE_SOUTH_EAST,
428 1,
429 org_x + event.GetX(),
430 org_y + event.GetY(),
431 0);
432 }
433 }
434 else
435 {
436 event.Skip( true );
437 }
438 #else
439 event.Skip( true );
440 #endif
441 }
442
443 void wxStatusBarGeneric::OnRightDown(wxMouseEvent& event)
444 {
445 #ifdef __WXGTK20__
446 int width, height;
447 GetClientSize(&width, &height);
448
449 if ( ShowsSizeGrip() && (event.GetX() > width-height) )
450 {
451 GtkWidget *ancestor = gtk_widget_get_toplevel( m_widget );
452
453 if (!GTK_IS_WINDOW (ancestor))
454 return;
455
456 GdkWindow *source = GTKGetDrawingWindow();
457
458 int org_x = 0;
459 int org_y = 0;
460 gdk_window_get_origin( source, &org_x, &org_y );
461
462 gtk_window_begin_move_drag (GTK_WINDOW (ancestor),
463 2,
464 org_x + event.GetX(),
465 org_y + event.GetY(),
466 0);
467 }
468 else
469 {
470 event.Skip( true );
471 }
472 #else
473 event.Skip( true );
474 #endif
475 }
476
477 void wxStatusBarGeneric::OnSize(wxSizeEvent& WXUNUSED(event))
478 {
479 // FIXME: workarounds for OS/2 bugs have nothing to do here (VZ)
480 int width;
481 #ifdef __WXPM__
482 GetSize(&width, &m_lastClientHeight);
483 #else
484 GetClientSize(&width, &m_lastClientHeight);
485 #endif
486
487 // recompute the cache of the field widths if the status bar width has changed
488 m_widthsAbs = CalculateAbsWidths(width);
489 }
490
491 #endif // wxUSE_STATUSBAR