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